From 2d8884402b5e19bf075802b61d875308b71f2a25 Mon Sep 17 00:00:00 2001 From: PH2 <1293456824@qq.com> Date: Tue, 17 Nov 2020 15:45:02 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E7=94=A8=E6=88=B7=E5=8F=8D=E9=A6=88?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feedback_page/bloc/feedback_bloc.dart | 19 +++ .../feedback_page/bloc/feedback_event.dart | 6 +- .../bloc/feedback_record_bloc.dart | 73 ++++++++ .../bloc/feedback_record_event.dart | 13 ++ .../bloc/feedback_record_repository.dart | 117 +++++++++++++ .../bloc/feedback_record_state.dart | 26 +++ .../bloc/feedback_repository.dart | 94 +++++++++- .../feedback_page/bloc/feedback_state.dart | 6 + lib/pages/feedback_page/feedback_page.dart | 94 +++++++--- .../feedback_page/feedback_record_page.dart | 161 ++++++++++++++++-- .../feedback_page/model/feedback_model.dart | 7 +- .../model/feedback_record_data_model.dart | 60 +++++++ .../model/feedback_record_style_model.dart | 140 +++++++++++++++ .../model/feedback_save_model.dart | 9 + .../widgets/feedback_record_item.dart | 79 ++++++--- lib/register.dart | 5 +- 16 files changed, 836 insertions(+), 73 deletions(-) create mode 100644 lib/pages/feedback_page/bloc/feedback_record_bloc.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_record_event.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_record_repository.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_record_state.dart create mode 100644 lib/pages/feedback_page/model/feedback_record_data_model.dart create mode 100644 lib/pages/feedback_page/model/feedback_record_style_model.dart create mode 100644 lib/pages/feedback_page/model/feedback_save_model.dart diff --git a/lib/pages/feedback_page/bloc/feedback_bloc.dart b/lib/pages/feedback_page/bloc/feedback_bloc.dart index 8a28a04..eb80386 100644 --- a/lib/pages/feedback_page/bloc/feedback_bloc.dart +++ b/lib/pages/feedback_page/bloc/feedback_bloc.dart @@ -22,10 +22,29 @@ class FeedbackBloc extends Bloc { if (event is FeedbackInitEvent) { yield* _mapInitToState(event); } + + /// 提交反馈 + if (event is FeedbackSubmitEvent) { + yield* _mapSaveToState(event); + } + } + + /// 提交反馈 + Stream _mapSaveToState(FeedbackSubmitEvent event) async* { + var result = await repository.saveFeedback(event?.model); + if (!EmptyUtil.isEmpty(result)) { + yield FeedbackSaveSuccessState(); + } else { + yield FeedbackSaveErrorState(); + } } /// 初始化 Stream _mapInitToState(FeedbackInitEvent event) async* { + var cache = await repository.fetchCachedData(event.model); + if (!EmptyUtil.isEmpty(cache)) { + yield FeedbackLoadedState(model: cache); + } var result = await repository.fetchData(event.model); if (!EmptyUtil.isEmpty(result)) { yield FeedbackLoadedState(model: result); diff --git a/lib/pages/feedback_page/bloc/feedback_event.dart b/lib/pages/feedback_page/bloc/feedback_event.dart index c5df86b..63419ea 100644 --- a/lib/pages/feedback_page/bloc/feedback_event.dart +++ b/lib/pages/feedback_page/bloc/feedback_event.dart @@ -10,4 +10,8 @@ class FeedbackInitEvent extends FeedbackEvent{ } /// 提交反馈 -class FeedbackSubmitEvent extends FeedbackEvent{} \ No newline at end of file +class FeedbackSubmitEvent extends FeedbackEvent{ + final Map model; + + FeedbackSubmitEvent({this.model}); +} \ No newline at end of file diff --git a/lib/pages/feedback_page/bloc/feedback_record_bloc.dart b/lib/pages/feedback_page/bloc/feedback_record_bloc.dart new file mode 100644 index 0000000..f5dd414 --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_record_bloc.dart @@ -0,0 +1,73 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_record_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'feedback_record_event.dart'; +import 'feedback_record_state.dart'; + +class FeedbackRecordBloc extends Bloc { + // FeedbackRecordBloc() : super(FeedbackRecordInitial()); + @override + FeedbackRecordState get initialState => FeedbackRecordInitial(); + + FeedbackRecordRepository repository; + + FeedbackRecordBloc(this.repository); + + @override + Stream mapEventToState( + FeedbackRecordEvent event, + ) async* { + /// 初始化 + if (event is FeedbackRecordInitEvent) { + yield* _mapInitEventToState(event); + } + + /// 刷新 + if (event is FeedbackRecordRefreshEvent) { + yield* _mapRefreshEventToState(event); + } + + /// 加载更多 + if (event is FeedbackRecordLoadEvent) { + yield* _mapLoadEventToState(event); + } + } + + /// 初始化 + Stream _mapInitEventToState(FeedbackRecordInitEvent event) async* { + var styleCache = await repository.fetchCacheStyle(); + var styleNet = await repository.fetchInitStyle(); + var data = await repository.fetchInitData(); + if (!EmptyUtil.isEmpty(data) && (!EmptyUtil.isEmpty(styleCache) || !EmptyUtil.isEmpty(styleNet))) { + yield FeedbackRecordLoadedState(dataModel: data, styleModel: !EmptyUtil.isEmpty(styleNet) ? styleNet : styleCache); + } else { + yield FeedbackRecordInitErrorState(); + } + } + + /// 刷新 + Stream _mapRefreshEventToState(FeedbackRecordRefreshEvent event) async* { + var style = repository.styleModel; + var result = await repository.fetchRefreshData(); + if (!EmptyUtil.isEmpty(result) && !EmptyUtil.isEmpty(style)) { + yield FeedbackRecordRefreshSuccessState(); + yield FeedbackRecordLoadedState(dataModel: result, styleModel: style); + } else { + yield FeedbackRecordRefreshErrorState(); + } + } + + /// 加载更多 + Stream _mapLoadEventToState(FeedbackRecordLoadEvent event) async* { + var style = repository.styleModel; + var result = await repository.fetchLoadData(); + if (!EmptyUtil.isEmpty(result) && !EmptyUtil.isEmpty(style)) { + yield FeedbackRecordLoadSuccessState(); + yield FeedbackRecordLoadedState(dataModel: result, styleModel: style); + } else { + yield FeedbackRecordLoadErrorState(); + } + } +} diff --git a/lib/pages/feedback_page/bloc/feedback_record_event.dart b/lib/pages/feedback_page/bloc/feedback_record_event.dart new file mode 100644 index 0000000..319a5dc --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_record_event.dart @@ -0,0 +1,13 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class FeedbackRecordEvent {} + +/// 初始化 +class FeedbackRecordInitEvent extends FeedbackRecordEvent {} + +/// 下拉刷新 +class FeedbackRecordRefreshEvent extends FeedbackRecordEvent {} + +/// 加载更多 +class FeedbackRecordLoadEvent extends FeedbackRecordEvent {} diff --git a/lib/pages/feedback_page/bloc/feedback_record_repository.dart b/lib/pages/feedback_page/bloc/feedback_record_repository.dart new file mode 100644 index 0000000..1f428d0 --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_record_repository.dart @@ -0,0 +1,117 @@ +import 'dart:convert'; + +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_data_model.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_style_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class FeedbackRecordRepository { + // final int max = 5; + int _currentPage = 1; + bool _hasMore = true; + + final Map data; + + FeedbackRecordRepository(this.data) { + _dataModel = FeedbackRecordDataModel(); + _dataModel.data = []; + } + + /// 样式数据 + FeedbackRecordStyleModel _styleModel; + + /// 数据 + FeedbackRecordDataModel _dataModel; + + FeedbackRecordStyleModel get styleModel => _styleModel; + + FeedbackRecordDataModel get dataModel => _dataModel; + + /// 初始化数据 + Future fetchInitData() async { + _hasMore = true; + _currentPage = 1; + return _fetchBaseRequest(); + } + + /// 样式初始化 + Future fetchInitStyle() async { + try { + String skipIdentifier = + !EmptyUtil.isEmpty(data) && data.containsKey('skip_identifier') && !EmptyUtil.isEmpty(data['skip_identifier']) ? data['skip_identifier'] : 'pub.flutter.feedback_list'; + var result = await NetUtil.post('/api/v1/mod/$skipIdentifier', method: NetMethod.GET, cache: true); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + return _handleStyle(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } + + /// 获取缓存样式 + Future fetchCacheStyle() async { + try { + String skipIdentifier = + !EmptyUtil.isEmpty(data) && data.containsKey('skip_identifier') && !EmptyUtil.isEmpty(data['skip_identifier']) ? data['skip_identifier'] : 'pub.flutter.feedback_list'; + var result = await NetUtil.getRequestCachedData('/api/v1/mod/$skipIdentifier'); + if (!EmptyUtil.isEmpty(result)) { + return _handleStyle(result); + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } + + /// 处理样式 + dynamic _handleStyle(Map data) { + try { + var modListData = data['mod_list'][0]['data']; + if (!EmptyUtil.isEmpty(modListData)) { + FeedbackRecordStyleModel model = FeedbackRecordStyleModel.fromJson(modListData is String ? jsonDecode(modListData) : modListData); + if (!EmptyUtil.isEmpty(model)) { + _styleModel = model; + return _styleModel; + } + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } + + /// 刷新 + Future fetchRefreshData() async { + _hasMore = true; + _currentPage = 1; + return _fetchBaseRequest(); + } + + /// 加载更多 + Future fetchLoadData() async { + if (_hasMore) { + return _fetchBaseRequest(); + } + return null; + } + + /// 基础加载方法 + Future _fetchBaseRequest() async { + try { + var result = await NetUtil.post('/api/v1/feedback/list', method: NetMethod.GET, queryParameters: {'page': _currentPage.toString()}); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + FeedbackRecordDataModel model = FeedbackRecordDataModel.fromJson(result); + if (!EmptyUtil.isEmpty(model) && !EmptyUtil.isEmpty(model?.data)) { + _dataModel.data.addAll(model.data); + _hasMore = true; + ++_currentPage; + return _dataModel; + } + } + _hasMore = false; + } catch (e, s) { + Logger.error(e, s); + } + return null; + } +} diff --git a/lib/pages/feedback_page/bloc/feedback_record_state.dart b/lib/pages/feedback_page/bloc/feedback_record_state.dart new file mode 100644 index 0000000..2305ba0 --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_record_state.dart @@ -0,0 +1,26 @@ +import 'package:meta/meta.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_data_model.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_style_model.dart'; + +@immutable +abstract class FeedbackRecordState {} + +class FeedbackRecordInitial extends FeedbackRecordState {} + +/// 数据加载成功 +class FeedbackRecordLoadedState extends FeedbackRecordState { + FeedbackRecordDataModel dataModel; + FeedbackRecordStyleModel styleModel; + + FeedbackRecordLoadedState({this.dataModel, this.styleModel}); +} + +/// 初始化失败 +class FeedbackRecordInitErrorState extends FeedbackRecordState{} + +/// 刷新成功与失败 +class FeedbackRecordRefreshSuccessState extends FeedbackRecordState{} +class FeedbackRecordRefreshErrorState extends FeedbackRecordState{} +/// 加载更多成功与失败 +class FeedbackRecordLoadSuccessState extends FeedbackRecordState{} +class FeedbackRecordLoadErrorState extends FeedbackRecordState{} diff --git a/lib/pages/feedback_page/bloc/feedback_repository.dart b/lib/pages/feedback_page/bloc/feedback_repository.dart index e5578ad..94077a6 100644 --- a/lib/pages/feedback_page/bloc/feedback_repository.dart +++ b/lib/pages/feedback_page/bloc/feedback_repository.dart @@ -1,21 +1,42 @@ import 'dart:convert'; - +import 'dart:io'; +import 'package:path/path.dart'; import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_model.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; class FeedBackRepository { /// 获取缓存样式数据 + Future fetchCachedData(final Map data) async { + try { + String skipIdentifier = + !EmptyUtil.isEmpty(data) && data.containsKey('skip_identifier') && !EmptyUtil.isEmpty(data['skip_identifier']) ? data['skip_identifier'] : 'pub.flutter.feedback'; + var result = await NetUtil.getRequestCachedData('/api/v1/mod/$skipIdentifier'); + if (!EmptyUtil.isEmpty(result)) { + var modListData = result['mod_list'][0]['data']; + if (!EmptyUtil.isEmpty(modListData)) { + FeedbackModel model = FeedbackModel.fromJson(jsonDecode(modListData)); + model.feedbackTypes.last.isSelect = true; + return model; + } + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } /// 获取网络样式数据 Future fetchData(final Map data) async { try { - String skip_identifier = !EmptyUtil.isEmpty(data) && data.containsKey('skip_identifier') && !EmptyUtil.isEmpty(data['skip_identifier']) ? data['skip_identifier'] : 'pub.flutter.feedback'; + String skipIdentifier = + !EmptyUtil.isEmpty(data) && data.containsKey('skip_identifier') && !EmptyUtil.isEmpty(data['skip_identifier']) ? data['skip_identifier'] : 'pub.flutter.feedback'; - var result = await NetUtil.post('/api/v1/mod/$skip_identifier', method: NetMethod.GET, cache: true); + var result = await NetUtil.post('/api/v1/mod/$skipIdentifier', method: NetMethod.GET, cache: true); if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { var modListData = result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list'][0]['data']; if (!EmptyUtil.isEmpty(modListData)) { FeedbackModel model = FeedbackModel.fromJson(jsonDecode(modListData)); + model.feedbackTypes.last.isSelect = true; return model; } } @@ -25,6 +46,71 @@ class FeedBackRepository { return null; } - /// 获取网络数据 + /// 提交反馈 + Future saveFeedback(final Map data) async { + try { + List files = data['files']; + List imageList = []; + if(!EmptyUtil.isEmpty(files)) { + // files.forEach((element) async { + // String fileName = await updateFile(element); + // if (!EmptyUtil.isEmpty(fileName)) { + // imageList.add(fileName); + // } + // }); + + for(int i = 0 ; i < files.length; i++){ + String imgName = await updateFile(files[i]); + if(!EmptyUtil.isEmpty(imgName)){ + imageList.add(imgName); + } + } + + if (imageList.length != files.length) { + return null; + } + } + var result = await NetUtil.post('/api/v1/feedback/save', method: NetMethod.POST, params: { + 'type': data['type']?.toString(), + 'title': data['title']?.toString(), + 'content': data['content']?.toString(), + 'image_list': imageList, + }); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + return result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]; + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } + + /// 上传图片 + Future updateFile(final File imageFile) async { + try { + List originBytes = await imageFile.readAsBytes(); + var result1 = await NetUtil.post('/api/v1/img/upload', params: {'dir': 'feedback', 'file_size': originBytes.length, 'file_name': basename(imageFile.path)}, method: NetMethod.PUT); + if (NetUtil.isSuccess(result1) && !EmptyUtil.isEmpty(result1[GlobalConfig.HTTP_RESPONSE_KEY_DATA]) && !EmptyUtil.isEmpty(result1[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['token'])) { + String token = result1[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['token']; + String host = result1[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['host']; + String key = result1[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['key']; + String method = result1[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['method']; + + var result2 = await NetUtil.uploadFile(host, imageFile, params: {'key': key, 'token': token}, method: method); + Map json = jsonDecode(result2.toString()); + if (NetUtil.isSuccess(json) && !EmptyUtil.isEmpty(json[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + String filename = json['data']['fname'] ?? ''; + print(filename); + // filename += '?${DateTime.now().millisecondsSinceEpoch.toString()}'; + // print(filename); + // ⚠️ 这里返回key是因为fname是带有域名的,所以直接用key即可 + return !EmptyUtil.isEmpty(filename) ? key : null; + } + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } } diff --git a/lib/pages/feedback_page/bloc/feedback_state.dart b/lib/pages/feedback_page/bloc/feedback_state.dart index 5948746..c582a39 100644 --- a/lib/pages/feedback_page/bloc/feedback_state.dart +++ b/lib/pages/feedback_page/bloc/feedback_state.dart @@ -13,3 +13,9 @@ class FeedbackLoadedState extends FeedbackState{ } class FeedbackErrorState extends FeedbackState{} + +/// 保存反馈成功 +class FeedbackSaveSuccessState extends FeedbackState{} + +/// 保存反馈失败 +class FeedbackSaveErrorState extends FeedbackState{} \ No newline at end of file diff --git a/lib/pages/feedback_page/feedback_page.dart b/lib/pages/feedback_page/feedback_page.dart index ddda224..d86948f 100644 --- a/lib/pages/feedback_page/feedback_page.dart +++ b/lib/pages/feedback_page/feedback_page.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -55,6 +56,9 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { TextEditingController _title; + // 是否上传中 + bool isUpLoading = false; + @override void initState() { _feedback = TextEditingController(); @@ -69,7 +73,6 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { super.dispose(); } - /// 选择图片 void _onAddImage() async { if (_images.length >= 4) { @@ -107,9 +110,13 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { if (file == null) return; - setState(() { - _images.add(File(file.path)); - }); + if (_images.length <= 4) { + setState(() { + _images.add(File(file.path)); + }); + } else { + Fluttertoast.showToast(msg: '最多只能选择4张'); + } // File resultFile = await EncodeUtil.compressImage(file, 800); } @@ -120,6 +127,19 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { return BlocConsumer( listener: (context, state) {}, buildWhen: (prev, current) { + /// 保存成功 + if (current is FeedbackSaveSuccessState) { + Fluttertoast.showToast(msg: '反馈成功~'); + Navigator.pop(context); + return false; + } + if (current is FeedbackSaveErrorState) { + Fluttertoast.showToast(msg: '反馈失败~'); + setState(() { + isUpLoading = false; + }); + return false; + } return true; }, builder: (context, state) { @@ -169,14 +189,7 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { Center( child: GestureDetector( behavior: HitTestBehavior.opaque, - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (_) => FeedbackRecordPage(), - ), - ); - }, + onTap: () => RouterUtil.route(model, model.toJson(), context), child: Container( width: 120, height: 30, @@ -273,6 +286,7 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { ), ), const SizedBox(height: 2), + /// 异常选项 Padding( padding: EdgeInsets.only(top: 4, bottom: 4), @@ -281,16 +295,15 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { child: Row( children: model.feedbackTypes .map((e) => Expanded( - child: GestureDetector( + child: GestureDetector( behavior: HitTestBehavior.opaque, - onTap: (){ + onTap: () { setState(() { model.feedbackTypes.forEach((element) { element.isSelect = false; }); e.isSelect = true; }); - }, child: Container( margin: EdgeInsets.only(left: 2, right: 2), @@ -311,9 +324,8 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { e.name ?? '', style: TextStyle( fontSize: 12, - color: HexColor.fromHex(e.isSelect - ? model?.feedbackTypeSelectedTextColor - : model?.feedbackTypeNoSelectedTextColor) //e.isSelect ? Colors.redAccent : Color(0xff999999), + color: HexColor.fromHex( + e.isSelect ? model?.feedbackTypeSelectedTextColor : model?.feedbackTypeNoSelectedTextColor) //e.isSelect ? Colors.redAccent : Color(0xff999999), ), ), ), @@ -401,8 +413,9 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { child: FeedbackImageWidget( file: file, onDelete: () { - _images.remove(file); - setState(() {}); + setState(() { + _images.remove(file); + }); }, ), )); @@ -425,6 +438,8 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { } return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ @@ -448,9 +463,18 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { ), ], ), - Row( + // Row( + // children: images, + // ) + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + textDirection: TextDirection.ltr, + spacing: 0, + runSpacing: 0, + runAlignment: WrapAlignment.center, + alignment: WrapAlignment.start, children: images, - ) + ), ], ); } @@ -461,9 +485,27 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { if (!_submitable) { return; } + + if(isUpLoading){ + // Fluttertoast.showToast(msg: '提交中...'); + return; + } + + setState(() { + isUpLoading = true; + }); + Logger.debug('提交:${_feedback.text.toString()}'); - Fluttertoast.showToast(msg: '提交成功~'); - Navigator.pop(context); + String selectId; + model.feedbackTypes.forEach((element) { + if (element.isSelect) { + selectId = element.typeId; + } + }); + BlocProvider.of(context) + .add(FeedbackSubmitEvent(model: {'title': _title?.text?.toString(), 'content': _feedback?.text?.toString(), 'type': selectId, 'files': _images})); + // Fluttertoast.showToast(msg: '提交成功~'); + // Navigator.pop(context); }, child: Container( margin: EdgeInsets.only(top: 24, bottom: 4), @@ -474,7 +516,7 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { ), child: Center( child: Text( - model?.feedbackPostBtnText ?? '提交意见', + isUpLoading ? '提交意见...' : model?.feedbackPostBtnText ?? '提交意见', style: TextStyle( fontSize: 14, color: HexColor.fromHex(model?.feedbackPostBtnTextColor ?? '#FFFFFF'), @@ -484,6 +526,4 @@ class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { ), ); } - - } diff --git a/lib/pages/feedback_page/feedback_record_page.dart b/lib/pages/feedback_page/feedback_record_page.dart index 562665c..26bf527 100644 --- a/lib/pages/feedback_page/feedback_record_page.dart +++ b/lib/pages/feedback_page/feedback_record_page.dart @@ -1,36 +1,167 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_record_bloc.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_record_repository.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_data_model.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_style_model.dart'; import 'package:zhiying_base_widget/pages/feedback_page/widgets/feedback_record_item.dart'; import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'bloc/feedback_record_event.dart'; +import 'bloc/feedback_record_state.dart'; + +class FeedbackRecordPage extends StatelessWidget { + final Map data; + + FeedbackRecordPage(this.data); -class FeedbackRecordPage extends StatefulWidget { @override - _FeedbackRecordPageState createState() => _FeedbackRecordPageState(); + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => FeedbackRecordBloc(FeedbackRecordRepository(data))..add(FeedbackRecordInitEvent()), + child: _FeedbackRecordPage(), + ); + } } -class _FeedbackRecordPageState extends State { +class _FeedbackRecordPage extends StatefulWidget { + @override + __FeedbackRecordPageState createState() => __FeedbackRecordPageState(); +} + +class __FeedbackRecordPageState extends State<_FeedbackRecordPage> { + RefreshController _controller; + + /// 点击详情 + + /// 刷新 + void _refresh() { + BlocProvider.of(context).add(FeedbackRecordRefreshEvent()); + } + + /// 加载更多 + void _load() { + BlocProvider.of(context).add(FeedbackRecordLoadEvent()); + } + + @override + void initState() { + _controller = RefreshController(); + super.initState(); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) {}, + buildWhen: (prev, current) { + if (current is FeedbackRecordRefreshSuccessState) { + _controller?.refreshCompleted(resetFooterState: true); + return false; + } + if (current is FeedbackRecordRefreshErrorState) { + _controller?.refreshFailed(); + return false; + } + + if (current is FeedbackRecordLoadSuccessState) { + _controller?.loadComplete(); + return false; + } + + if (current is FeedbackRecordLoadErrorState) { + _controller?.loadNoData(); + return false; + } + + return true; + }, + builder: (context, state) { + /// 数据视图 + if (state is FeedbackRecordLoadedState) { + return _createDataWidget(state?.styleModel, state?.dataModel); + } + + /// 初始化失败,空视图 + if (state is FeedbackRecordInitErrorState) { + return _createEmptyWidget(); + } + + /// 加载中,骨架图 + return _createSkeletonWidget(); + }, + ); + } + + /// 有数据 + Widget _createDataWidget(FeedbackRecordStyleModel styleModel, FeedbackRecordDataModel dataModel) { + int length = dataModel?.data?.length ?? 0; + if(length == 0 ) return _createEmptyWidget(); + + return Scaffold( + appBar: _createNav(styleModel), + body: GestureDetector( + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: SafeArea( + child: SmartRefresher( + controller: _controller, + enablePullUp: true, + enablePullDown: true, + onRefresh: _refresh, + onLoading: _load, + child: ListView.builder( + itemCount: length, + itemBuilder: (context, index) { + + return FeedbackRecordItem(styleModel, dataModel.data[index]); + }), + ), + ), + ), + ); + } + + /// 空数据 + Widget _createEmptyWidget() { return Scaffold( - appBar: _createNav(), + appBar: _createNav(null), body: GestureDetector( onTap: () { FocusScope.of(context).requestFocus(FocusNode()); }, child: SafeArea( - child: Center(child: EmptyWidget()), - // child: ListView.builder( - // itemCount: 10, - // itemBuilder: (context, index) { - // return FeedbackRecordItem(); - // }), + child: SmartRefresher( + controller: _controller, + enablePullDown: true, + enablePullUp: false, + onRefresh: _refresh, + child: Center(child: EmptyWidget()), + ), ), ), ); } - // 导航栏 - Widget _createNav() { + /// 骨架图 + Widget _createSkeletonWidget() { + return Scaffold( + appBar: _createNav(null), + body: Container(), + ); + } + + /// 导航栏 + Widget _createNav(FeedbackRecordStyleModel styleModel) { return CupertinoNavigationBar( border: Border( bottom: BorderSide( @@ -38,7 +169,7 @@ class _FeedbackRecordPageState extends State { style: BorderStyle.none, ), ), - backgroundColor: Colors.white, + backgroundColor: HexColor.fromHex(styleModel?.appBarBgColor ?? '#FFFFFF'), leading: Navigator.canPop(context) ? GestureDetector( child: Container( @@ -56,10 +187,10 @@ class _FeedbackRecordPageState extends State { ) : Container(), middle: Text( - '反馈列表', + styleModel?.appBarName ?? '反馈列表', style: TextStyle( fontSize: 15, - color: Color(0xff333333), + color: HexColor.fromHex(styleModel?.appBarNameColor ?? '#FF333333'), ), ), ); diff --git a/lib/pages/feedback_page/model/feedback_model.dart b/lib/pages/feedback_page/model/feedback_model.dart index ccf25be..cb2dc19 100644 --- a/lib/pages/feedback_page/model/feedback_model.dart +++ b/lib/pages/feedback_page/model/feedback_model.dart @@ -1,4 +1,6 @@ -class FeedbackModel { +import 'package:zhiying_comm/zhiying_comm.dart'; + +class FeedbackModel extends SkipModel{ String appBarName; String appBarNameColor; String appBarBgColor; @@ -73,6 +75,7 @@ class FeedbackModel { this.skipIdentifier}); FeedbackModel.fromJson(Map json) { + super.fromJson(json); appBarName = json['app_bar_name']; appBarNameColor = json['app_bar_name_color']; appBarBgColor = json['app_bar_bg_color']; @@ -121,7 +124,7 @@ class FeedbackModel { } Map toJson() { - final Map data = new Map(); + final Map data = super.toJson(); data['app_bar_name'] = this.appBarName; data['app_bar_name_color'] = this.appBarNameColor; data['app_bar_bg_color'] = this.appBarBgColor; diff --git a/lib/pages/feedback_page/model/feedback_record_data_model.dart b/lib/pages/feedback_page/model/feedback_record_data_model.dart new file mode 100644 index 0000000..f1cd131 --- /dev/null +++ b/lib/pages/feedback_page/model/feedback_record_data_model.dart @@ -0,0 +1,60 @@ +/// 反馈列表数据 +class FeedbackRecordDataModel { + List data; + + FeedbackRecordDataModel({this.data}); + + FeedbackRecordDataModel.fromJson(Map json) { + if (json['data'] != null) { + data = new List(); + json['data'].forEach((v) { + data.add(new FeedbackRecordDataItemModel.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.data != null) { + data['data'] = this.data.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class FeedbackRecordDataItemModel { + String id; + String type; + String typeText; + String state; + String title; + String content; + List imageList; + String createTime; + + FeedbackRecordDataItemModel({this.id, this.type, this.typeText, this.state, this.title, this.content, this.imageList, this.createTime}); + + FeedbackRecordDataItemModel.fromJson(Map json) { + id = json['id']; + type = json['type']; + typeText = json['type_text']; + state = json['state']; + title = json['title']; + content = json['content']; + imageList = json['image_list'].cast(); + createTime = json['create_time']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['type'] = this.type; + data['type_text'] = this.typeText; + data['state'] = this.state; + data['title'] = this.title; + data['content'] = this.content; + data['image_list'] = this.imageList; + data['create_time'] = this.createTime; + return data; + } +} diff --git a/lib/pages/feedback_page/model/feedback_record_style_model.dart b/lib/pages/feedback_page/model/feedback_record_style_model.dart new file mode 100644 index 0000000..cc4781e --- /dev/null +++ b/lib/pages/feedback_page/model/feedback_record_style_model.dart @@ -0,0 +1,140 @@ +import 'package:zhiying_comm/zhiying_comm.dart'; +/// 反馈列表样式 +class FeedbackRecordStyleModel extends SkipModel{ + String appBarName; + String appBarNameColor; + String appBarBgColor; + String bgColor; + ItemStyle itemStyle; + List feedbackTypes; + List stateImages; + String requiredLogin; + String requiredTaobaoAuth; + String skipIdentifier; + + FeedbackRecordStyleModel( + {this.appBarName, + this.appBarNameColor, + this.appBarBgColor, + this.bgColor, + this.itemStyle, + this.feedbackTypes, + this.stateImages, + this.requiredLogin, + this.requiredTaobaoAuth, + this.skipIdentifier}); + + FeedbackRecordStyleModel.fromJson(Map json) { + super.fromJson(json); + appBarName = json['app_bar_name']; + appBarNameColor = json['app_bar_name_color']; + appBarBgColor = json['app_bar_bg_color']; + bgColor = json['bg_color']; + itemStyle = json['item_style'] != null ? new ItemStyle.fromJson(json['item_style']) : null; + if (json['feedback_types'] != null) { + feedbackTypes = new List(); + json['feedback_types'].forEach((v) { + feedbackTypes.add(new FeedbackTypes.fromJson(v)); + }); + } + if (json['state_images'] != null) { + stateImages = new List(); + json['state_images'].forEach((v) { + stateImages.add(new StateImages.fromJson(v)); + }); + } + requiredLogin = json['required_login']; + requiredTaobaoAuth = json['required_taobao_auth']; + skipIdentifier = json['skip_identifier']; + } + + Map toJson() { + final Map data = super.toJson(); + data['app_bar_name'] = this.appBarName; + data['app_bar_name_color'] = this.appBarNameColor; + data['app_bar_bg_color'] = this.appBarBgColor; + data['bg_color'] = this.bgColor; + if (this.itemStyle != null) { + data['item_style'] = this.itemStyle.toJson(); + } + if (this.feedbackTypes != null) { + data['feedback_types'] = this.feedbackTypes.map((v) => v.toJson()).toList(); + } + if (this.stateImages != null) { + data['state_images'] = this.stateImages.map((v) => v.toJson()).toList(); + } + data['required_login'] = this.requiredLogin; + data['required_taobao_auth'] = this.requiredTaobaoAuth; + data['skip_identifier'] = this.skipIdentifier; + return data; + } +} + +class ItemStyle { + String bgColor; + String typeTextColor; + String typeBgColor; + String titleTextColor; + String contentTextColor; + String timeTextColor; + + ItemStyle({this.bgColor, this.typeTextColor, this.typeBgColor, this.titleTextColor, this.contentTextColor, this.timeTextColor}); + + ItemStyle.fromJson(Map json) { + bgColor = json['bg_color']; + typeTextColor = json['type_text_color']; + typeBgColor = json['type_bg_color']; + titleTextColor = json['title_text_color']; + contentTextColor = json['content_text_color']; + timeTextColor = json['time_text_color']; + } + + Map toJson() { + final Map data = new Map(); + data['bg_color'] = this.bgColor; + data['type_text_color'] = this.typeTextColor; + data['type_bg_color'] = this.typeBgColor; + data['title_text_color'] = this.titleTextColor; + data['content_text_color'] = this.contentTextColor; + data['time_text_color'] = this.timeTextColor; + return data; + } +} + +class FeedbackTypes { + String typeId; + String name; + + FeedbackTypes({this.typeId, this.name}); + + FeedbackTypes.fromJson(Map json) { + typeId = json['type_id']; + name = json['name']; + } + + Map toJson() { + final Map data = new Map(); + data['type_id'] = this.typeId; + data['name'] = this.name; + return data; + } +} + +class StateImages { + String stateId; + String img; + + StateImages({this.stateId, this.img}); + + StateImages.fromJson(Map json) { + stateId = json['state_id']; + img = json['img']; + } + + Map toJson() { + final Map data = new Map(); + data['state_id'] = this.stateId; + data['img'] = this.img; + return data; + } +} diff --git a/lib/pages/feedback_page/model/feedback_save_model.dart b/lib/pages/feedback_page/model/feedback_save_model.dart new file mode 100644 index 0000000..cb72c20 --- /dev/null +++ b/lib/pages/feedback_page/model/feedback_save_model.dart @@ -0,0 +1,9 @@ +import 'dart:io'; + +class FeedbackSaveModel { + String type; + String title; + String content; + List files; + FeedbackSaveModel({this.type, this.title, this.content, this.files}); +} diff --git a/lib/pages/feedback_page/widgets/feedback_record_item.dart b/lib/pages/feedback_page/widgets/feedback_record_item.dart index 6e08064..571ab4f 100644 --- a/lib/pages/feedback_page/widgets/feedback_record_item.dart +++ b/lib/pages/feedback_page/widgets/feedback_record_item.dart @@ -1,6 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_data_model.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_record_style_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; class FeedbackRecordItem extends StatelessWidget { + FeedbackRecordStyleModel styleModel; + FeedbackRecordDataItemModel dataModel; + + FeedbackRecordItem(this.styleModel, this.dataModel); + @override Widget build(BuildContext context) { return Container( @@ -8,7 +16,7 @@ class FeedbackRecordItem extends StatelessWidget { padding: EdgeInsets.all(15), width: double.infinity, decoration: BoxDecoration( - color: Colors.white, + color: HexColor.fromHex(styleModel?.itemStyle?.bgColor ?? '#FFFFFF'), borderRadius: BorderRadius.circular(7.5), ), child: Row( @@ -31,40 +39,53 @@ class FeedbackRecordItem extends StatelessWidget { width: 60, height: 60, decoration: BoxDecoration( - color: Colors.green, + // color: Colors.green, borderRadius: BorderRadius.circular(30), ), + child: CachedNetworkImage( + imageUrl: getStateImageUrl(), + ), ) ], ), ); } + String getStateImageUrl(){ + String result = ''; + int length = styleModel?.stateImages?.length ?? 0; + if(length > 0){ + styleModel.stateImages.forEach((element) { + if(element.stateId == dataModel.state){ + result = element.img; + } + }); + } + return result; + } + Widget _createTitle() { List list = List(); list.add(WidgetSpan( child: Container( - padding: EdgeInsets.only(left: 2, right: 2, top: 3, bottom: 3), + padding: EdgeInsets.only(left: 4.5, right: 4.5, top: 3, bottom: 3), margin: EdgeInsets.only(right: 4), child: Text( - '功能异常', + dataModel?.typeText ?? '其它', style: TextStyle( fontSize: 9, height: 1, - // color: HexColor.fromHex(style.providerNameColor), - color: Colors.white, + color: HexColor.fromHex(styleModel?.itemStyle?.typeTextColor ?? '#FFFFFF'), ), ), - decoration: BoxDecoration( - color: Colors.redAccent, borderRadius: BorderRadius.circular(2.5)), + decoration: BoxDecoration(color: HexColor.fromHex(styleModel?.itemStyle?.typeBgColor ?? '#FF4242'), borderRadius: BorderRadius.circular(2.5)), ), )); list.add(TextSpan( - text: '关于关闭提现功能的反馈', - style: TextStyle( - fontSize: 14, color: Color(0xff333333), fontWeight: FontWeight.bold), + text: dataModel?.title ?? '', + style: TextStyle(fontSize: 14, color: HexColor.fromHex(styleModel?.itemStyle?.titleTextColor ?? '#333333'), fontWeight: FontWeight.bold), )); return RichText( maxLines: 2, @@ -76,32 +97,44 @@ class FeedbackRecordItem extends StatelessWidget { Widget _createDesc() { return Container( child: Text( - '我今天想使用我的提现功能的时候,发现官方已经把这个功能给暂时关闭了,所以我很伤心,不开心不开心不开心,所以我很伤心,不开心不开心不开心', - style: TextStyle(fontSize: 11, color: Color(0xff999999)), + dataModel?.content ?? '', + style: TextStyle(fontSize: 11, color: HexColor.fromHex(styleModel?.itemStyle?.contentTextColor ?? '#999999')), ), ); } Widget _createImage() { - return Container( - margin: EdgeInsets.only(top: 4, bottom: 4), - child: Wrap( - children: [ + List images = []; + int length = dataModel?.imageList?.length ?? 0; + if (length > 0) { + for (int i = 0; i < length; i++) { + images.add( Container( width: 60, height: 60, - color: Colors.redAccent, + child: CachedNetworkImage( + imageUrl: dataModel?.imageList[i] ?? '', + ), ), - ], - ), - ); + ); + } + + return Container( + margin: EdgeInsets.only(top: 4, bottom: 4), + child: Wrap( + children: images, + ), + ); + } else { + return Container(); + } } Widget _createTime() { return Container( child: Text( - '2020-06-23 01:00:06', - style: TextStyle(fontSize: 11, color: Color(0xffd8d8d8)), + dataModel?.createTime ?? '', + style: TextStyle(fontSize: 11, color: HexColor.fromHex(styleModel?.itemStyle?.timeTextColor ?? '#D8D8D8')), ), ); } diff --git a/lib/register.dart b/lib/register.dart index 51f3d37..c4a4c7d 100644 --- a/lib/register.dart +++ b/lib/register.dart @@ -6,6 +6,7 @@ import 'package:sharesdk_plugin/sharesdk_register.dart'; import 'package:zhiying_base_widget/pages/about_us_page/about_us_page.dart'; import 'package:zhiying_base_widget/pages/bil_detail_page/bil_detail_page.dart'; import 'package:zhiying_base_widget/pages/feedback_page/feedback_page.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/feedback_record_page.dart'; import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; import 'package:zhiying_base_widget/pages/home_page/home_page.dart'; import 'package:zhiying_base_widget/pages/hot_ranking_page/hot_ranking_page.dart'; @@ -58,6 +59,7 @@ import 'package:zhiying_base_widget/widgets/wallet_bil_detail/wallet_bil_detail. import 'package:zhiying_comm/util/defalut_widget_creater.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; +import 'pages/custom_page/custom_page.dart'; import 'pages/favorites_page/favorites_page.dart'; import 'pages/message_notice_page/message_notice_page.dart'; import 'pages/privacy_settings_page/privacy_settings_page.dart'; @@ -139,6 +141,7 @@ class BaseWidgetRegister { PageFactory.regist( 'search_result_item', (model) => SearchResultItemPage(model)); PageFactory.regist('pub.flutter.feedback', (model) => FeedbackPage(model)); + PageFactory.regist('pub.flutter.feedback_list', (model) => FeedbackRecordPage(model)); PageFactory.regist( 'pub.flutter.wechat_teacher', (model) => WechatTeacherPage()); PageFactory.regist('pub.flutter.cash_out', (model) => WithdrawPage(model)); @@ -199,7 +202,7 @@ class BaseWidgetRegister { 'pub.flutter.my_wallet_detail', (model) => BilDetailPage(model)); /// 通用模块 - PageFactory.regist('pub.flutter.custom', (model) => EmptyPage()); + PageFactory.regist('pub.flutter.custom', (model) => CustomPage(model)); } // 注册控件