Explorar el Código

1、用户反馈完成

tags/0.0.2+7^2
PH2 hace 4 años
padre
commit
2d8884402b
Se han modificado 16 ficheros con 836 adiciones y 73 borrados
  1. +19
    -0
      lib/pages/feedback_page/bloc/feedback_bloc.dart
  2. +5
    -1
      lib/pages/feedback_page/bloc/feedback_event.dart
  3. +73
    -0
      lib/pages/feedback_page/bloc/feedback_record_bloc.dart
  4. +13
    -0
      lib/pages/feedback_page/bloc/feedback_record_event.dart
  5. +117
    -0
      lib/pages/feedback_page/bloc/feedback_record_repository.dart
  6. +26
    -0
      lib/pages/feedback_page/bloc/feedback_record_state.dart
  7. +90
    -4
      lib/pages/feedback_page/bloc/feedback_repository.dart
  8. +6
    -0
      lib/pages/feedback_page/bloc/feedback_state.dart
  9. +67
    -27
      lib/pages/feedback_page/feedback_page.dart
  10. +146
    -15
      lib/pages/feedback_page/feedback_record_page.dart
  11. +5
    -2
      lib/pages/feedback_page/model/feedback_model.dart
  12. +60
    -0
      lib/pages/feedback_page/model/feedback_record_data_model.dart
  13. +140
    -0
      lib/pages/feedback_page/model/feedback_record_style_model.dart
  14. +9
    -0
      lib/pages/feedback_page/model/feedback_save_model.dart
  15. +56
    -23
      lib/pages/feedback_page/widgets/feedback_record_item.dart
  16. +4
    -1
      lib/register.dart

+ 19
- 0
lib/pages/feedback_page/bloc/feedback_bloc.dart Ver fichero

@@ -22,10 +22,29 @@ class FeedbackBloc extends Bloc<FeedbackEvent, FeedbackState> {
if (event is FeedbackInitEvent) {
yield* _mapInitToState(event);
}

/// 提交反馈
if (event is FeedbackSubmitEvent) {
yield* _mapSaveToState(event);
}
}

/// 提交反馈
Stream<FeedbackState> _mapSaveToState(FeedbackSubmitEvent event) async* {
var result = await repository.saveFeedback(event?.model);
if (!EmptyUtil.isEmpty(result)) {
yield FeedbackSaveSuccessState();
} else {
yield FeedbackSaveErrorState();
}
}

/// 初始化
Stream<FeedbackState> _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);


+ 5
- 1
lib/pages/feedback_page/bloc/feedback_event.dart Ver fichero

@@ -10,4 +10,8 @@ class FeedbackInitEvent extends FeedbackEvent{
}

/// 提交反馈
class FeedbackSubmitEvent extends FeedbackEvent{}
class FeedbackSubmitEvent extends FeedbackEvent{
final Map<String, dynamic> model;

FeedbackSubmitEvent({this.model});
}

+ 73
- 0
lib/pages/feedback_page/bloc/feedback_record_bloc.dart Ver fichero

@@ -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<FeedbackRecordEvent, FeedbackRecordState> {
// FeedbackRecordBloc() : super(FeedbackRecordInitial());
@override
FeedbackRecordState get initialState => FeedbackRecordInitial();

FeedbackRecordRepository repository;

FeedbackRecordBloc(this.repository);

@override
Stream<FeedbackRecordState> 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<FeedbackRecordState> _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<FeedbackRecordState> _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<FeedbackRecordState> _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();
}
}
}

+ 13
- 0
lib/pages/feedback_page/bloc/feedback_record_event.dart Ver fichero

@@ -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 {}

+ 117
- 0
lib/pages/feedback_page/bloc/feedback_record_repository.dart Ver fichero

@@ -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<String, dynamic> data;

FeedbackRecordRepository(this.data) {
_dataModel = FeedbackRecordDataModel();
_dataModel.data = [];
}

/// 样式数据
FeedbackRecordStyleModel _styleModel;

/// 数据
FeedbackRecordDataModel _dataModel;

FeedbackRecordStyleModel get styleModel => _styleModel;

FeedbackRecordDataModel get dataModel => _dataModel;

/// 初始化数据
Future<dynamic> fetchInitData() async {
_hasMore = true;
_currentPage = 1;
return _fetchBaseRequest();
}

/// 样式初始化
Future<dynamic> 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<dynamic> 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<String, dynamic> 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<dynamic> fetchRefreshData() async {
_hasMore = true;
_currentPage = 1;
return _fetchBaseRequest();
}

/// 加载更多
Future<dynamic> fetchLoadData() async {
if (_hasMore) {
return _fetchBaseRequest();
}
return null;
}

/// 基础加载方法
Future<dynamic> _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;
}
}

+ 26
- 0
lib/pages/feedback_page/bloc/feedback_record_state.dart Ver fichero

@@ -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{}

+ 90
- 4
lib/pages/feedback_page/bloc/feedback_repository.dart Ver fichero

@@ -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<FeedbackModel> fetchCachedData(final Map<String, dynamic> 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<FeedbackModel> fetchData(final Map<String, dynamic> 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<dynamic> saveFeedback(final Map<String, dynamic> data) async {
try {
List<File> files = data['files'];
List<String> 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<String> updateFile(final File imageFile) async {
try {
List<int> 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<String, dynamic> 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;
}
}

+ 6
- 0
lib/pages/feedback_page/bloc/feedback_state.dart Ver fichero

@@ -13,3 +13,9 @@ class FeedbackLoadedState extends FeedbackState{
}

class FeedbackErrorState extends FeedbackState{}

/// 保存反馈成功
class FeedbackSaveSuccessState extends FeedbackState{}

/// 保存反馈失败
class FeedbackSaveErrorState extends FeedbackState{}

+ 67
- 27
lib/pages/feedback_page/feedback_page.dart Ver fichero

@@ -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<FeedbackBloc, FeedbackState>(
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: <Widget>[
Row(
children: <Widget>[
@@ -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<FeedbackBloc>(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> {
),
);
}


}

+ 146
- 15
lib/pages/feedback_page/feedback_record_page.dart Ver fichero

@@ -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<String, dynamic> data;

FeedbackRecordPage(this.data);

class FeedbackRecordPage extends StatefulWidget {
@override
_FeedbackRecordPageState createState() => _FeedbackRecordPageState();
Widget build(BuildContext context) {
return BlocProvider<FeedbackRecordBloc>(
create: (_) => FeedbackRecordBloc(FeedbackRecordRepository(data))..add(FeedbackRecordInitEvent()),
child: _FeedbackRecordPage(),
);
}
}

class _FeedbackRecordPageState extends State<FeedbackRecordPage> {
class _FeedbackRecordPage extends StatefulWidget {
@override
__FeedbackRecordPageState createState() => __FeedbackRecordPageState();
}

class __FeedbackRecordPageState extends State<_FeedbackRecordPage> {
RefreshController _controller;

/// 点击详情

/// 刷新
void _refresh() {
BlocProvider.of<FeedbackRecordBloc>(context).add(FeedbackRecordRefreshEvent());
}

/// 加载更多
void _load() {
BlocProvider.of<FeedbackRecordBloc>(context).add(FeedbackRecordLoadEvent());
}

@override
void initState() {
_controller = RefreshController();
super.initState();
}

@override
void dispose() {
_controller?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return BlocConsumer<FeedbackRecordBloc, FeedbackRecordState>(
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<FeedbackRecordPage> {
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<FeedbackRecordPage> {
)
: Container(),
middle: Text(
'反馈列表',
styleModel?.appBarName ?? '反馈列表',
style: TextStyle(
fontSize: 15,
color: Color(0xff333333),
color: HexColor.fromHex(styleModel?.appBarNameColor ?? '#FF333333'),
),
),
);


+ 5
- 2
lib/pages/feedback_page/model/feedback_model.dart Ver fichero

@@ -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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
final Map<String, dynamic> data = super.toJson();
data['app_bar_name'] = this.appBarName;
data['app_bar_name_color'] = this.appBarNameColor;
data['app_bar_bg_color'] = this.appBarBgColor;


+ 60
- 0
lib/pages/feedback_page/model/feedback_record_data_model.dart Ver fichero

@@ -0,0 +1,60 @@
/// 反馈列表数据
class FeedbackRecordDataModel {
List<FeedbackRecordDataItemModel> data;

FeedbackRecordDataModel({this.data});

FeedbackRecordDataModel.fromJson(Map<String, dynamic> json) {
if (json['data'] != null) {
data = new List<FeedbackRecordDataItemModel>();
json['data'].forEach((v) {
data.add(new FeedbackRecordDataItemModel.fromJson(v));
});
}
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<String> imageList;
String createTime;

FeedbackRecordDataItemModel({this.id, this.type, this.typeText, this.state, this.title, this.content, this.imageList, this.createTime});

FeedbackRecordDataItemModel.fromJson(Map<String, dynamic> 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<String>();
createTime = json['create_time'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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;
}
}

+ 140
- 0
lib/pages/feedback_page/model/feedback_record_style_model.dart Ver fichero

@@ -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> feedbackTypes;
List<StateImages> 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<String, dynamic> 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<FeedbackTypes>();
json['feedback_types'].forEach((v) {
feedbackTypes.add(new FeedbackTypes.fromJson(v));
});
}
if (json['state_images'] != null) {
stateImages = new List<StateImages>();
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<String, dynamic> toJson() {
final Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<String, dynamic> json) {
typeId = json['type_id'];
name = json['name'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<String, dynamic> json) {
stateId = json['state_id'];
img = json['img'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['state_id'] = this.stateId;
data['img'] = this.img;
return data;
}
}

+ 9
- 0
lib/pages/feedback_page/model/feedback_save_model.dart Ver fichero

@@ -0,0 +1,9 @@
import 'dart:io';

class FeedbackSaveModel {
String type;
String title;
String content;
List<File> files;
FeedbackSaveModel({this.type, this.title, this.content, this.files});
}

+ 56
- 23
lib/pages/feedback_page/widgets/feedback_record_item.dart Ver fichero

@@ -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<InlineSpan> 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: <Widget>[
List<Widget> 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')),
),
);
}


+ 4
- 1
lib/register.dart Ver fichero

@@ -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));
}

// 注册控件


Cargando…
Cancelar
Guardar