diff --git a/lib/constants/api.dart b/lib/constants/api.dart index a8607af7..fcf0259c 100644 --- a/lib/constants/api.dart +++ b/lib/constants/api.dart @@ -99,6 +99,12 @@ class _Manager { ///建议继续提问 String get adviceQuestion => '/user/advice/reQuestion'; + + ///建议评价 + String get adviceEvaluate => '/user/advice/evaluate'; + + ///删除建议 + String get deleteAdvice => '/user/advice/falseDelete'; } class _Upload { diff --git a/lib/main.dart b/lib/main.dart index 66148ab7..cebb54c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,6 @@ import 'package:akuCommunity/utils/developer_util.dart'; import 'package:bot_toast/bot_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/services.dart'; import 'package:fluwx/fluwx.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; diff --git a/lib/pages/activities_page/activities_page.dart b/lib/pages/activities_page/activities_page.dart index c71bff98..3c83556f 100644 --- a/lib/pages/activities_page/activities_page.dart +++ b/lib/pages/activities_page/activities_page.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; -import 'package:akuCommunity/routers/page_routers.dart'; import 'package:akuCommunity/widget/activity_card.dart'; class ActivitiesPage extends StatefulWidget { diff --git a/lib/ui/manager/advice/advice_card.dart b/lib/ui/manager/advice/advice_card.dart index dbdb1214..2eee5a63 100644 --- a/lib/ui/manager/advice/advice_card.dart +++ b/lib/ui/manager/advice/advice_card.dart @@ -1,9 +1,11 @@ +import 'package:akuCommunity/base/base_style.dart'; import 'package:akuCommunity/model/manager/suggestion_or_complain_model.dart'; import 'package:akuCommunity/ui/manager/advice/advice_detail_page.dart'; import 'package:akuCommunity/utils/headers.dart'; import 'package:akuCommunity/widget/views/horizontal_image_view.dart'; import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:velocity_x/velocity_x.dart'; class AdviceCard extends StatefulWidget { @@ -21,6 +23,28 @@ class _AdviceCardState extends State { 2: '反馈中', 3: '已反馈', }[widget.model.status]; + + Widget _buildRating() { + if (widget.model.score == null) + return SizedBox(); + else + return [ + 120.hb, + 24.wb, + '评测得分'.text.size(32.sp).color(ktextSubColor).make(), + Spacer(), + RatingBarIndicator( + itemBuilder: (context, index) => Icon( + Icons.star_rounded, + color: kPrimaryColor, + ), + rating: widget.model.score / 2, + itemSize: 40.w, + ), + 24.wb, + ].row(); + } + @override Widget build(BuildContext context) { return MaterialButton( @@ -54,6 +78,13 @@ class _AdviceCardState extends State { ), widget.model.content.text.size(28.w).black.make().pSymmetric(h: 28.w), HorizontalImageView(widget.model.imgUrls.map((e) => e.url).toList()), + widget.model.score == null + ? SizedBox() + : Divider( + indent: 32.w, + endIndent: 32.w, + ), + _buildRating(), ], ), ); diff --git a/lib/ui/manager/advice/advice_detail_page.dart b/lib/ui/manager/advice/advice_detail_page.dart index 2094988b..5160fa8d 100644 --- a/lib/ui/manager/advice/advice_detail_page.dart +++ b/lib/ui/manager/advice/advice_detail_page.dart @@ -3,6 +3,7 @@ import 'package:akuCommunity/constants/api.dart'; import 'package:akuCommunity/model/manager/advice_detail_model.dart'; import 'package:akuCommunity/model/manager/suggestion_or_complain_model.dart'; import 'package:akuCommunity/ui/manager/advice/advice_add_comment_page.dart'; +import 'package:akuCommunity/ui/manager/advice/advice_evaluate_page.dart'; import 'package:akuCommunity/utils/network/net_util.dart'; import 'package:akuCommunity/widget/bee_scaffold.dart'; import 'package:akuCommunity/utils/headers.dart'; @@ -160,7 +161,7 @@ class _AdviceDetailPageState extends State { title: '查看详情', actions: [ FlatButton( - onPressed: () {}, + onPressed: AdviceEvaluatePage(id: widget.model.id).to, child: '评价'.text.make(), ), ], diff --git a/lib/ui/manager/advice/advice_evaluate_page.dart b/lib/ui/manager/advice/advice_evaluate_page.dart new file mode 100644 index 00000000..b9fa9a2e --- /dev/null +++ b/lib/ui/manager/advice/advice_evaluate_page.dart @@ -0,0 +1,84 @@ +import 'package:akuCommunity/base/base_style.dart'; +import 'package:akuCommunity/constants/api.dart'; +import 'package:akuCommunity/utils/headers.dart'; +import 'package:akuCommunity/utils/network/base_model.dart'; +import 'package:akuCommunity/utils/network/net_util.dart'; +import 'package:akuCommunity/widget/bee_scaffold.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_rating_bar/flutter_rating_bar.dart'; +import 'package:get/get.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class AdviceEvaluatePage extends StatefulWidget { + final int id; + AdviceEvaluatePage({Key key, @required this.id}) : super(key: key); + + @override + _AdviceEvaluatePageState createState() => _AdviceEvaluatePageState(); +} + +class _AdviceEvaluatePageState extends State { + int _rating = 10; + @override + Widget build(BuildContext context) { + return BeeScaffold( + title: '评价', + body: ListView( + padding: EdgeInsets.all(32.w), + children: [ + VxBox( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + '请您对本次服务进行评价'.text.size(28.sp).color(Color(0xFF999999)).make(), + 50.hb, + Row( + children: [ + '综合评价'.text.size(28.sp).color(Color(0xFF999999)).make(), + 50.wb, + RatingBar.builder( + initialRating: _rating / 2, + minRating: 0.5, + direction: Axis.horizontal, + allowHalfRating: true, + itemCount: 5, + // itemPadding: EdgeInsets.symmetric(horizontal: 16.w), + itemBuilder: (context, _) => Icon( + Icons.star_border_rounded, + color: kPrimaryColor, + ), + itemSize: 64.w, + onRatingUpdate: (rating) { + _rating = (rating * 2).floor(); + }, + glow: false, + ) + ], + ), + ], + ), + ).padding(EdgeInsets.all(32.w)).white.make(), + 42.hb, + MaterialButton( + height: 96.w, + shape: StadiumBorder(), + color: kPrimaryColor, + elevation: 0, + onPressed: () async { + BaseModel baseModel = await NetUtil().post( + API.manager.adviceEvaluate, + params: {'id': widget.id, 'score': _rating}, + showMessage: true, + ); + if (baseModel.status) { + Get.back(); + Get.back(); + } + }, + child: '确认'.text.bold.size(32.sp).make(), + ), + ], + ), + ); + } +} diff --git a/lib/ui/manager/advice/advice_page.dart b/lib/ui/manager/advice/advice_page.dart index a9c0b07f..3f3d80a8 100644 --- a/lib/ui/manager/advice/advice_page.dart +++ b/lib/ui/manager/advice/advice_page.dart @@ -3,9 +3,12 @@ import 'package:akuCommunity/model/manager/suggestion_or_complain_model.dart'; import 'package:akuCommunity/pages/things_page/widget/bee_list_view.dart'; import 'package:akuCommunity/ui/manager/advice/advice_card.dart'; import 'package:akuCommunity/ui/manager/advice/new_advice_page.dart'; +import 'package:akuCommunity/utils/network/net_util.dart'; +import 'package:akuCommunity/widget/animated/animated_transition.dart'; import 'package:akuCommunity/widget/bee_scaffold.dart'; import 'package:akuCommunity/utils/headers.dart'; import 'package:akuCommunity/widget/buttons/bottom_button.dart'; +import 'package:akuCommunity/widget/buttons/radio_button.dart'; import 'package:akuCommunity/widget/tab_bar/bee_tab_bar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -29,6 +32,9 @@ class AdvicePage extends StatefulWidget { class _AdvicePageState extends State with TickerProviderStateMixin { EasyRefreshController _refreshController = EasyRefreshController(); TabController _tabController; + bool _selectedMode = false; + + List _selectedItems = []; String get title { switch (widget.type) { @@ -82,9 +88,18 @@ class _AdvicePageState extends State with TickerProviderStateMixin { Widget build(BuildContext context) { return BeeScaffold( title: title, + actions: [ + FlatButton( + onPressed: () => setState(() => _selectedMode = !_selectedMode), + child: (_selectedMode ? '完成' : '编辑').text.make(), + ), + ], appBarBottom: BeeTabBar( controller: _tabController, tabs: tabs, + onTap: (index) { + _selectedItems.clear(); + }, ), body: TabBarView( controller: _tabController, @@ -100,7 +115,37 @@ class _AdvicePageState extends State with TickerProviderStateMixin { return ListView.separated( padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 20.w), itemBuilder: (context, index) { - return AdviceCard(model: items[index]); + return Stack( + children: [ + Positioned( + left: 0, + top: 0, + bottom: 0, + child: GestureDetector( + onTap: () { + setState(() { + if (_selectedItems.contains(items[index].id)) + _selectedItems.remove(items[index].id); + else + _selectedItems.add(items[index].id); + }); + }, + child: Container( + color: Colors.transparent, + alignment: Alignment.topLeft, + child: BeeRadio( + value: items[index].id, + groupValues: _selectedItems, + ), + ), + ), + ), + AnimatedTranslate( + offset: _selectedMode ? Offset(60.w, 0) : Offset.zero, + child: AdviceCard(model: items[index]), + ), + ], + ); }, separatorBuilder: (_, __) => 20.hb, itemCount: items.length, @@ -109,23 +154,42 @@ class _AdvicePageState extends State with TickerProviderStateMixin { ); }).toList(), ), - bottomNavi: BottomButton( - onPressed: () async { - bool needRefresh = await Get.to(NewAdvicePage(type: widget.type)); - if (needRefresh == true) { + bottomNavi: AnimatedCrossFade( + crossFadeState: _selectedMode + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + duration: Duration(milliseconds: 300), + firstChild: BottomButton( + onPressed: () async { + await NetUtil().post( + API.manager.deleteAdvice, + params: {'ids': _selectedItems}, + showMessage: true, + ); _refreshController.callRefresh(); - Get.dialog(CupertinoAlertDialog( - title: '您的信息已提交,我们会尽快回复您,祝您生活愉快'.text.isIntrinsic.make(), - actions: [ - CupertinoDialogAction( - child: '确定'.text.color(Color(0xFFFF8200)).isIntrinsic.make(), - onPressed: Get.back, - ), - ], - )); - } - }, - child: Text('新增'), + setState(() => _selectedMode = false); + }, + child: '删除订单'.text.make(), + ), + secondChild: BottomButton( + onPressed: () async { + bool needRefresh = await Get.to(NewAdvicePage(type: widget.type)); + if (needRefresh == true) { + _refreshController.callRefresh(); + Get.dialog(CupertinoAlertDialog( + title: '您的信息已提交,我们会尽快回复您,祝您生活愉快'.text.isIntrinsic.make(), + actions: [ + CupertinoDialogAction( + child: + '确定'.text.color(Color(0xFFFF8200)).isIntrinsic.make(), + onPressed: Get.back, + ), + ], + )); + } + }, + child: Text('新增'), + ), ), ); } diff --git a/lib/widget/animated/animated_scale.dart b/lib/widget/animated/animated_scale.dart new file mode 100644 index 00000000..323c29e5 --- /dev/null +++ b/lib/widget/animated/animated_scale.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class AnimatedScale extends ImplicitlyAnimatedWidget { + final Widget child; + final double scale; + + AnimatedScale({this.child, this.scale}) + : super( + duration: Duration(milliseconds: 300), + ); + @override + ImplicitlyAnimatedWidgetState createState() => + _AnimatedScaleState(); +} + +class _AnimatedScaleState extends AnimatedWidgetBaseState { + Tween scaleTween; + @override + Widget build(BuildContext context) { + return Transform.scale( + scale: scaleTween.evaluate(animation), + child: widget.child, + ); + } + + @override + void forEachTween(visitor) { + scaleTween = visitor( + scaleTween, + widget.scale, + (value) => Tween(begin: value), + ); + } +} diff --git a/lib/widget/animated/animated_transition.dart b/lib/widget/animated/animated_transition.dart new file mode 100644 index 00000000..c9d8a73d --- /dev/null +++ b/lib/widget/animated/animated_transition.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class AnimatedTranslate extends ImplicitlyAnimatedWidget { + final Widget child; + final Offset offset; + + AnimatedTranslate({this.child, this.offset}) + : super( + duration: Duration(milliseconds: 300), + ); + @override + ImplicitlyAnimatedWidgetState createState() => + _AnimatedTranslateState(); +} + +class _AnimatedTranslateState + extends AnimatedWidgetBaseState { + Tween _offsetTween; + @override + Widget build(BuildContext context) { + return Transform.translate( + offset: _offsetTween.evaluate(animation), + child: widget.child, + ); + } + + @override + void forEachTween(visitor) { + _offsetTween = visitor( + _offsetTween, + widget.offset, + (value) => Tween(begin: value), + ); + } +} diff --git a/lib/widget/buttons/bottom_button.dart b/lib/widget/buttons/bottom_button.dart index be102fc3..82197994 100644 --- a/lib/widget/buttons/bottom_button.dart +++ b/lib/widget/buttons/bottom_button.dart @@ -30,6 +30,7 @@ class BottomButton extends StatelessWidget { onPressed: onPressed, color: kPrimaryColor, height: 98.w, + minWidth: double.infinity, ), ); } diff --git a/lib/widget/buttons/radio_button.dart b/lib/widget/buttons/radio_button.dart new file mode 100644 index 00000000..29ac8f7d --- /dev/null +++ b/lib/widget/buttons/radio_button.dart @@ -0,0 +1,55 @@ +import 'package:akuCommunity/base/base_style.dart'; +import 'package:akuCommunity/utils/headers.dart'; +import 'package:akuCommunity/widget/animated/animated_scale.dart'; +import 'package:flutter/material.dart'; + +class BeeRadio extends StatefulWidget { + final T value; + final List groupValues; + BeeRadio({Key key, @required this.value, @required this.groupValues}) + : super(key: key); + + @override + _BeeRadioState createState() => _BeeRadioState(); +} + +class _BeeRadioState extends State { + bool get _selected { + if (widget.groupValues.contains(widget.value)) return true; + return false; + } + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + height: 40.w, + width: 40.w, + decoration: BoxDecoration( + border: Border.all( + color: _selected ? kPrimaryColor : Color(0xFF979797), + width: 3.w, + ), + borderRadius: BorderRadius.circular(20.w), + ), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutCubic, + alignment: Alignment.center, + child: AnimatedOpacity( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutCubic, + opacity: _selected ? 1 : 0, + child: AnimatedScale( + scale: _selected ? 1 : 0, + child: Container( + height: 24.w, + width: 24.w, + decoration: BoxDecoration( + color: kPrimaryColor, + borderRadius: BorderRadius.circular(12.w), + ), + ), + ), + ), + ); + } +} diff --git a/lib/widget/picker/bee_image_preview.dart b/lib/widget/picker/bee_image_preview.dart index d5473f51..6f9c67be 100644 --- a/lib/widget/picker/bee_image_preview.dart +++ b/lib/widget/picker/bee_image_preview.dart @@ -46,11 +46,11 @@ class _BeeImagePreviewState extends State { backgroundColor: Colors.black54, body: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), - child: Center( - child: InteractiveViewer( - minScale: 0.2, - child: image, - ), + child: InteractiveViewer( + boundaryMargin: EdgeInsets.all(48), + minScale: 0.2, + maxScale: 10, + child: Center(child: image), ), ), ), diff --git a/lib/widget/tab_bar/bee_tab_bar.dart b/lib/widget/tab_bar/bee_tab_bar.dart index 1f5574f7..9997b9d5 100644 --- a/lib/widget/tab_bar/bee_tab_bar.dart +++ b/lib/widget/tab_bar/bee_tab_bar.dart @@ -5,7 +5,9 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; class BeeTabBar extends StatefulWidget with PreferredSizeWidget { final TabController controller; final List tabs; - BeeTabBar({Key key, @required this.controller, @required this.tabs}) + final Function(int index) onTap; + BeeTabBar( + {Key key, @required this.controller, @required this.tabs, this.onTap}) : super(key: key); @override @@ -16,6 +18,22 @@ class BeeTabBar extends StatefulWidget with PreferredSizeWidget { } class _BeeTabBarState extends State { + update() { + if (widget.onTap != null) widget.onTap(widget.controller.index); + } + + @override + void initState() { + super.initState(); + widget.controller.addListener(update); + } + + @override + void dispose() { + widget.controller.removeListener(update); + super.dispose(); + } + @override Widget build(BuildContext context) { return TabBar( @@ -30,6 +48,7 @@ class _BeeTabBarState extends State { indicatorColor: Color(0xffffc40c), indicatorSize: TabBarIndicatorSize.label, tabs: widget.tabs.map((e) => Tab(text: e)).toList(), + onTap: widget.onTap, ); } } diff --git a/lib/widget/views/bee_grid_image_view.dart b/lib/widget/views/bee_grid_image_view.dart index 4d64afbf..235ec9a1 100644 --- a/lib/widget/views/bee_grid_image_view.dart +++ b/lib/widget/views/bee_grid_image_view.dart @@ -16,6 +16,7 @@ class BeeGridImageView extends StatelessWidget { @override Widget build(BuildContext context) { + if (urls.isEmpty) return SizedBox(); return GridView.builder( padding: padding, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( diff --git a/pubspec.lock b/pubspec.lock index 05d4110e..1f914f0b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -410,6 +410,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.11" + flutter_rating_bar: + dependency: "direct main" + description: + name: flutter_rating_bar + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.0+1" flutter_redux: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3ca07b1c..0b4e37d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,7 +102,7 @@ dependencies: power_logger: git: url: http://192.168.2.201:8099/aku_fe/power_logger.git - + flutter_rating_bar: ^3.2.0+1 dev_dependencies: