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/pages/hot_ranking_page/hot_ranking_page.dart b/lib/pages/hot_ranking_page/hot_ranking_page.dart index 85b17b2..01c68b5 100644 --- a/lib/pages/hot_ranking_page/hot_ranking_page.dart +++ b/lib/pages/hot_ranking_page/hot_ranking_page.dart @@ -22,7 +22,7 @@ class HotRankingPage extends StatefulWidget { _HotRankingPageState createState() => _HotRankingPageState(); } -class _HotRankingPageState extends State { +class _HotRankingPageState extends State with AutomaticKeepAliveClientMixin{ @override Widget build(BuildContext context) { print('hot_ranking_page build'); @@ -48,6 +48,10 @@ class _HotRankingPageState extends State { ), ); } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; } class _HotRankingPageContainer extends StatefulWidget { @@ -60,7 +64,7 @@ class _HotRankingPageContainer extends StatefulWidget { __HotRankingPageContainerState(); } -class __HotRankingPageContainerState extends State<_HotRankingPageContainer> { +class __HotRankingPageContainerState extends State<_HotRankingPageContainer> with AutomaticKeepAliveClientMixin{ HotRankingPageBloc _bloc; RefreshController _refreshController; String backgroundImage; @@ -136,6 +140,10 @@ class __HotRankingPageContainerState extends State<_HotRankingPageContainer> { } return list; } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; } class _SilverAppBarDelegate extends SliverPersistentHeaderDelegate { diff --git a/lib/pages/webview/base_webview.dart b/lib/pages/webview/base_webview.dart index 517251c..6bc77db 100644 --- a/lib/pages/webview/base_webview.dart +++ b/lib/pages/webview/base_webview.dart @@ -1,5 +1,11 @@ +import 'dart:io'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/launch_page/launch_page.dart'; +import 'package:zhiying_comm/util/log/let_log.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:webview_flutter/webview_flutter.dart'; class BaseWebview extends StatefulWidget { @@ -33,7 +39,18 @@ class _BaseWebviewState extends State { onWebViewCreated: (WebViewController webViewController) { _webViewController = webViewController; }, - navigationDelegate: (NavigationRequest request) { + navigationDelegate: (NavigationRequest request) async{ + // 解决Android的拼多多webview 转发的问题 + if(Platform.isAndroid){ + String url = request?.url?.toString(); + if(!EmptyUtil.isEmpty(url) && !url.startsWith('https://') && !url.startsWith('http://')){ + Logger.log('navigation url = $url'); + // if(await canLaunch(url)){ + // await launch(url); + // } + return NavigationDecision.prevent; + } + } return NavigationDecision.navigate; }, onPageStarted: (String url) { diff --git a/lib/pages/withdraw_page/withdraw_page.dart b/lib/pages/withdraw_page/withdraw_page.dart index 31293bc..9b7d480 100644 --- a/lib/pages/withdraw_page/withdraw_page.dart +++ b/lib/pages/withdraw_page/withdraw_page.dart @@ -363,10 +363,28 @@ class _WithdrawContainerState extends State<_WithdrawContainer> { } Widget _createSubmit(WithdrawModel model) { - - String value = model.cashOutDashbordItems[_currentIndex].value; + // String value = model.cashOutDashbordItems[_currentIndex].value; + // try { + // if (num.tryParse(value) != null && + // null != _bloc.withdrawDataModel && + // num.tryParse(value) < + // num.tryParse(_bloc.withdrawDataModel.finValue)) { + // _submitable = true; + // } else { + // _submitable = false; + // } + // } catch (e, s) { + // print(e); + // print(s); + // } try { - if (num.tryParse(value) != null && null != _bloc.withdrawDataModel && num.tryParse(value) < num.tryParse(_bloc.withdrawDataModel.finValue)) { + var currentWithdraw = num.tryParse(_controller.text); + var finValue = num.tryParse(_bloc.withdrawDataModel.finValue); + if (currentWithdraw != null && + currentWithdraw != 0 && + finValue != null && + finValue != 0 && + currentWithdraw <= finValue) { _submitable = true; } else { _submitable = false; @@ -434,18 +452,24 @@ class _WithdrawContainerState extends State<_WithdrawContainer> { ); } + ///检查提现按钮是否可用 void _checkSubmit(String value) { - try { - if (num.tryParse(value) != null && - num.tryParse(value) < num.tryParse(_bloc.withdrawDataModel.finValue)) { - _submitable = true; - } else { - _submitable = false; - } - } catch (e, s) { - print(e); - print(s); - } + // try { + // var currentValue = num.tryParse(value); + // var finValue = num.tryParse(_bloc.withdrawDataModel.finValue); + // + // if (currentValue != null && + // currentValue != 0 && + // currentValue <= finValue && + // finValue != 0) { + // _submitable = true; + // } else { + // _submitable = false; + // } + // } catch (e, s) { + // print(e); + // print(s); + // } setState(() {}); } } 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)); } // 注册控件 diff --git a/lib/widgets/goods_details/coupon/counpon_widget.dart b/lib/widgets/goods_details/coupon/counpon_widget.dart index cfd6107..441f055 100644 --- a/lib/widgets/goods_details/coupon/counpon_widget.dart +++ b/lib/widgets/goods_details/coupon/counpon_widget.dart @@ -58,7 +58,7 @@ class _CounponWidgetContainerState extends State { /// 点击领取 void _onJump(CounponModel model) async{ - TurnChainUtil.openReceiveCoupon(context, _user, model.provider, model.toJson()); + TurnChainUtil.openReceiveCoupon(context, _user, model.provider, model?.convertArgs?.toJson()); } @override @@ -73,7 +73,7 @@ class _CounponWidgetContainerState extends State { }, builder: (context, state) { if (state is CounponLoadedState) { - return _getMainWdiget(state.model); + return _getMainWidget(state.model); } return CounponSkeleton(); }, @@ -81,7 +81,7 @@ class _CounponWidgetContainerState extends State { } /// 主视图 - Widget _getMainWdiget(CounponModel model) { + Widget _getMainWidget(CounponModel model) { return Visibility( visible: !EmptyUtil.isEmpty(model?.coupon_price), child: GestureDetector( diff --git a/lib/widgets/goods_details/detail_img/goods_details_img.dart b/lib/widgets/goods_details/detail_img/goods_details_img.dart index 6fb550c..ed49ced 100644 --- a/lib/widgets/goods_details/detail_img/goods_details_img.dart +++ b/lib/widgets/goods_details/detail_img/goods_details_img.dart @@ -97,18 +97,19 @@ class _GoodsDetailsImgWidgetContainerState return Column( children: model.image_detail_list.map((item) { return GestureDetector( - onTap: () { - if(item!=null){ - PhotoPreview.showPhotoPreviewByimages( - context, [item], - currentIndex: 0); - } - }, - child: CachedNetworkImage( - imageUrl: item ?? '', - fit: BoxFit.fitWidth, - ), - ); + onTap: () { + if (item != null) { + PhotoPreview.showPhotoPreviewByimages(context, [item], + currentIndex: 0, heroTagSuffix: "bottom"); + } + }, + child: Hero( + tag: item + "bottom", + child: CachedNetworkImage( + imageUrl: item ?? '', + fit: BoxFit.fitWidth, + ), + )); }).toList(), ); } else { diff --git a/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart index 32df501..1c09104 100644 --- a/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart +++ b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart @@ -46,8 +46,9 @@ class _GoodsDetailsSlideBannerContainerState /// 子元素点击事件 void _itemOnClick(String model, List images, int index) { print('点击了 $model'); - if(images!=null){ - PhotoPreview.showPhotoPreviewByimages(context, images, currentIndex: index); + if (images != null) { + PhotoPreview.showPhotoPreviewByimages(context, images, + currentIndex: index, heroTagSuffix: "top"); } } @@ -108,7 +109,10 @@ class _GoodsDetailsSlideBannerContainerState String items = datas.image_list[index]; return Container( width: double.infinity, - child: CachedNetworkImage(imageUrl: items ?? '', fit: BoxFit.cover), + child: Hero( + tag: items + "top", + child: CachedNetworkImage( + imageUrl: items ?? '', fit: BoxFit.cover)), ); }, itemCount: datas?.image_list?.length ?? 0, diff --git a/lib/widgets/hot_ranking/hot_ranking_goods/hot_ranking_goods.dart b/lib/widgets/hot_ranking/hot_ranking_goods/hot_ranking_goods.dart index 0598715..da05d52 100644 --- a/lib/widgets/hot_ranking/hot_ranking_goods/hot_ranking_goods.dart +++ b/lib/widgets/hot_ranking/hot_ranking_goods/hot_ranking_goods.dart @@ -12,25 +12,31 @@ class HotRankingGoods extends StatelessWidget { HotRankingListModel styleModel; int index; - HotRankingGoods({Key key, this.good, this.styleModel, this.index}) : super(key: key); + HotRankingGoods({Key key, this.good, this.styleModel, this.index}) + : super(key: key); @override Widget build(BuildContext context) { var indexImage; - if (styleModel.hotRankIconList != null && styleModel.hotRankIconList.length > 0 && styleModel.hotRankIconList.length - 1 >= index) { + if (styleModel.hotRankIconList != null && + styleModel.hotRankIconList.length > 0 && + styleModel.hotRankIconList.length - 1 >= index) { indexImage = styleModel.hotRankIconList[index]; } Providers providers = getProvider(good.provider); return GestureDetector( onTap: () { - RouterUtil.route(SkipModel(skipIdentifier: "goods_details"), good?.toJson(), context); + RouterUtil.route(SkipModel(skipIdentifier: "goods_details"), + good?.toJson(), context); }, child: Stack( children: [ Container( padding: EdgeInsets.all(15.w), - margin: EdgeInsets.only(top: 8.w, bottom: 8.w, left: 25.w, right: 25.w), - decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.w)), + margin: + EdgeInsets.only(top: 8.w, bottom: 8.w, left: 25.w, right: 25.w), + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(15.w)), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -40,11 +46,15 @@ class HotRankingGoods extends StatelessWidget { height: 254.w, child: ClipRRect( borderRadius: BorderRadius.circular(6), - child: CachedNetworkImage( - imageUrl: good?.goodImage ?? '', + child: Hero( + tag: (good?.goodImage ?? "") + "top", + child: CachedNetworkImage( + imageUrl: good?.goodImage ?? '', + ), ), ), - decoration: BoxDecoration(borderRadius: BorderRadius.circular(6)), + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(6)), ), /// 商品图片右边视图 @@ -64,8 +74,12 @@ class HotRankingGoods extends StatelessWidget { WidgetSpan( alignment: ui.PlaceholderAlignment.middle, child: Container( - padding: EdgeInsets.only(left: 4.w, right: 4.w, top: 1, bottom: 1), - decoration: BoxDecoration(color: HexColor.fromHex(providers.providerBgColor), borderRadius: BorderRadius.circular(2.5)), + padding: EdgeInsets.only( + left: 4.w, right: 4.w, top: 1, bottom: 1), + decoration: BoxDecoration( + color: HexColor.fromHex( + providers.providerBgColor), + borderRadius: BorderRadius.circular(2.5)), child: Text( good.providerName ?? "", style: TextStyle( @@ -79,7 +93,12 @@ class HotRankingGoods extends StatelessWidget { child: SizedBox( width: 4.h, )), - TextSpan(text: good.goodTitle, style: TextStyle(color: HexColor.fromHex(styleModel.titleColor ?? ""), fontSize: 30.sp)) + TextSpan( + text: good.goodTitle, + style: TextStyle( + color: HexColor.fromHex( + styleModel.titleColor ?? ""), + fontSize: 30.sp)) ])), /// 优惠券 @@ -88,16 +107,32 @@ class HotRankingGoods extends StatelessWidget { good.coupon == "" ? Container() : Container( - margin: EdgeInsets.only(top: 4, bottom: 4, right: 15.w), + margin: EdgeInsets.only( + top: 4, bottom: 4, right: 15.w), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2.5), - color: HexColor.fromHex(styleModel.couponCommission.left.couponBgColor), - image: DecorationImage(image: CachedNetworkImageProvider(styleModel.couponCommission.left.couponBgImg))), + borderRadius: BorderRadius.circular(2.5), + color: HexColor.fromHex(styleModel + .couponCommission.left.couponBgColor), + image: DecorationImage( + image: CachedNetworkImageProvider( + styleModel.couponCommission.left + .couponBgImg))), child: Padding( - padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2), + padding: const EdgeInsets.only( + left: 8, right: 8, top: 2, bottom: 2), child: Text( - (good.coupon ?? "") + (styleModel.couponCommission.left.couonText ?? ""), - style: TextStyle(color: HexColor.fromHex(styleModel.couponCommission.left.couponFontColor), fontSize: 22.sp, fontFamily: 'Din', package: 'zhiying_base_widget'), + (good.coupon ?? "") + + (styleModel.couponCommission.left + .couonText ?? + ""), + style: TextStyle( + color: HexColor.fromHex(styleModel + .couponCommission + .left + .couponFontColor), + fontSize: 22.sp, + fontFamily: 'Din', + package: 'zhiying_base_widget'), ), ), ), @@ -106,14 +141,33 @@ class HotRankingGoods extends StatelessWidget { : Container( margin: EdgeInsets.only(top: 4, bottom: 4), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2.5), - color: HexColor.fromHex(styleModel.couponCommission.right.commissionBgColor ?? ""), - image: DecorationImage(image: CachedNetworkImageProvider(styleModel.couponCommission.right.commissionBgImg ?? ""))), + borderRadius: BorderRadius.circular(2.5), + color: HexColor.fromHex(styleModel + .couponCommission + .right + .commissionBgColor ?? + ""), + image: DecorationImage( + image: CachedNetworkImageProvider( + styleModel.couponCommission.right + .commissionBgImg ?? + ""))), child: Padding( - padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2), + padding: const EdgeInsets.only( + left: 8, right: 8, top: 2, bottom: 2), child: Text( - (styleModel.couponCommission.right.commissionText ?? "") + (good.commission ?? ""), - style: TextStyle(color: HexColor.fromHex(styleModel.couponCommission.right.commissionFontColor), fontSize: 22.sp,fontFamily: 'Din', package: 'zhiying_base_widget'), + (styleModel.couponCommission.right + .commissionText ?? + "") + + (good.commission ?? ""), + style: TextStyle( + color: HexColor.fromHex(styleModel + .couponCommission + .right + .commissionFontColor), + fontSize: 22.sp, + fontFamily: 'Din', + package: 'zhiying_base_widget'), ), ), ), @@ -130,12 +184,21 @@ class HotRankingGoods extends StatelessWidget { padding: EdgeInsets.only(bottom: 6.sp), child: Text( "¥", - style: TextStyle(color: HexColor.fromHex(styleModel.currentPriceColor ?? ""), fontSize: 20.sp), + style: TextStyle( + color: HexColor.fromHex( + styleModel.currentPriceColor ?? ""), + fontSize: 20.sp), ), ), Text( good.currentPrice ?? "", - style: TextStyle(color: HexColor.fromHex(styleModel.currentPriceColor ?? ""), fontSize: 40.sp,fontWeight: FontWeight.bold ,fontFamily: 'Din', package: 'zhiying_base_widget'), + style: TextStyle( + color: HexColor.fromHex( + styleModel.currentPriceColor ?? ""), + fontSize: 40.sp, + fontWeight: FontWeight.bold, + fontFamily: 'Din', + package: 'zhiying_base_widget'), ), SizedBox( width: 6, @@ -144,7 +207,13 @@ class HotRankingGoods extends StatelessWidget { padding: EdgeInsets.only(bottom: 4.sp), child: Text( "¥" + good.marketPrice ?? "", - style: TextStyle(color: HexColor.fromHex(styleModel.marketPriceColor ?? ""), fontSize: 22.sp, decoration: TextDecoration.lineThrough, fontFamily: 'Din', package: 'zhiying_base_widget'), + style: TextStyle( + color: HexColor.fromHex( + styleModel.marketPriceColor ?? ""), + fontSize: 22.sp, + decoration: TextDecoration.lineThrough, + fontFamily: 'Din', + package: 'zhiying_base_widget'), ), ), ], @@ -167,11 +236,17 @@ class HotRankingGoods extends StatelessWidget { padding: EdgeInsets.only( left: 40.w, ), - margin: EdgeInsets.only(right: 20, left: 20.w), - color: HexColor.fromHex(styleModel.hotRank.bgColor ?? ""), + margin: + EdgeInsets.only(right: 20, left: 20.w), + color: HexColor.fromHex( + styleModel.hotRank.bgColor ?? ""), child: Text( "热销" + good.inorderCount + "件", - style: TextStyle(color: Colors.white, fontSize: 22.sp, fontFamily: 'Din', package: 'zhiying_base_widget'), + style: TextStyle( + color: Colors.white, + fontSize: 22.sp, + fontFamily: 'Din', + package: 'zhiying_base_widget'), ), )) ], @@ -180,10 +255,12 @@ class HotRankingGoods extends StatelessWidget { width: 48.w, height: 48.w, child: CachedNetworkImage( - imageUrl: styleModel?.hotRank?.hotSaleImg ?? "", + imageUrl: + styleModel?.hotRank?.hotSaleImg ?? "", width: 48.w, height: 48.w, - placeholder: (context, _) => Container(color: Colors.yellow), + placeholder: (context, _) => + Container(color: Colors.yellow), fit: BoxFit.fill, ), ), @@ -193,8 +270,13 @@ class HotRankingGoods extends StatelessWidget { height: 48.h, width: 127.w, decoration: BoxDecoration( - image: DecorationImage(image: CachedNetworkImageProvider(styleModel.hotRank.buyNowImg ?? ""), fit: BoxFit.fitWidth), - borderRadius: BorderRadius.circular(20)), + image: DecorationImage( + image: CachedNetworkImageProvider( + styleModel.hotRank.buyNowImg ?? + ""), + fit: BoxFit.fitWidth), + borderRadius: + BorderRadius.circular(20)), margin: EdgeInsets.only(right: 0), )) ], @@ -210,7 +292,9 @@ class HotRankingGoods extends StatelessWidget { Align( alignment: Alignment.topLeft, child: Container( - decoration: BoxDecoration(image: DecorationImage(image: CachedNetworkImageProvider(indexImage ?? ""))), + decoration: BoxDecoration( + image: DecorationImage( + image: CachedNetworkImageProvider(indexImage ?? ""))), margin: EdgeInsets.only(left: 40.w, top: 8.h), height: 60.w, width: 60.w, diff --git a/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_bloc.dart b/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_bloc.dart index ccfefdc..709b423 100644 --- a/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_bloc.dart +++ b/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_bloc.dart @@ -1,6 +1,11 @@ import 'dart:async'; import 'dart:convert'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/dialog/loading/loading.dart'; +import 'package:zhiying_base_widget/dialog/loading/loading_dialog.dart'; import 'package:zhiying_base_widget/widgets/hot_ranking/hot_ranking_list/model/hot_ranking_list_data_model.dart'; import 'package:zhiying_comm/util/base_bloc.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; @@ -25,7 +30,7 @@ class HotRankingListBloc extends BlocBase { _dataController = null; } - void loadData(String typeId, int page,Function complete) { + void loadData(String typeId, int page, Function complete) { if (isLoading) { complete(); return; @@ -35,16 +40,18 @@ class HotRankingListBloc extends BlocBase { NetUtil.request( '/api/v1/rec/taobao?type_id=' + typeId + '&page=' + page.toString(), method: NetMethod.GET, onSuccess: (data) { - complete(); + complete(); isLoading = false; _loadData(data); + Loading.dismiss(); }, onError: (e) { - complete(); + Loading.dismiss(); + complete(); isLoading = false; }); } - void loadMoreData(String typeId,Function complete) { + void loadMoreData(String typeId, Function complete) { if (isLoading) { complete(); return; diff --git a/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_list.dart b/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_list.dart index db0cc27..3db5fdd 100644 --- a/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_list.dart +++ b/lib/widgets/hot_ranking/hot_ranking_list/hot_ranking_list.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/dialog/loading/loading.dart'; +import 'package:zhiying_base_widget/dialog/loading/loading_dialog.dart'; import 'package:zhiying_base_widget/pages/hot_ranking_page/hot_ranking_page_bloc.dart'; import 'package:zhiying_base_widget/widgets/hot_ranking/hot_ranking_goods/hot_ranking_goods.dart'; import 'package:zhiying_base_widget/widgets/hot_ranking/hot_ranking_list/model/hot_ranking_list_data_model.dart'; @@ -23,7 +25,7 @@ class HotRankingList extends StatefulWidget { } class _HotRankingState extends State - with TickerProviderStateMixin { + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { HotRankingListBloc _bloc; TabController _tabController; RefreshController _refreshController; @@ -33,6 +35,8 @@ class _HotRankingState extends State ScrollController _scrollcontroller; + bool isFirstTime=true; + @override void initState() { _bloc = HotRankingListBloc(); @@ -43,8 +47,14 @@ class _HotRankingState extends State if (_pageBloc != null) { _pageBloc.event.listen((event) { if (event.containsKey('type') && event['type'] == "loadData") { + if(!isFirstTime){ + Loading.show(context); + } + isFirstTime=false; _bloc.currentPage = 1; _bloc.loadData(event['type_id'], _bloc.currentPage, () {}); + _scrollcontroller.animateTo(0, + duration: Duration(milliseconds: 500), curve: Curves.bounceInOut); } }); } @@ -72,12 +82,7 @@ class _HotRankingState extends State return HotRankingSkeleton(); } else { goods = snapshot.data.good; - if (_bloc.currentPage == 1) { - Future.delayed(Duration(milliseconds: 100), () { - _scrollcontroller.animateTo(0, - duration: Duration(milliseconds: 100), curve: Curves.ease); - }); - } + return SmartRefresher( controller: _refreshController, enablePullDown: true, @@ -121,4 +126,8 @@ class _HotRankingState extends State index: index, ); } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; }