diff --git a/assets/icons/alarm_clock.png b/assets/icons/alarm_clock.png new file mode 100644 index 0000000..5eb641f Binary files /dev/null and b/assets/icons/alarm_clock.png differ diff --git a/assets/icons/camera.png b/assets/icons/camera.png new file mode 100644 index 0000000..65264d9 Binary files /dev/null and b/assets/icons/camera.png differ diff --git a/assets/icons/cancel_task.png b/assets/icons/cancel_task.png new file mode 100644 index 0000000..a29d816 Binary files /dev/null and b/assets/icons/cancel_task.png differ diff --git a/assets/icons/clock_circle.png b/assets/icons/clock_circle.png new file mode 100644 index 0000000..9d94ffc Binary files /dev/null and b/assets/icons/clock_circle.png differ diff --git a/assets/icons/connect.png b/assets/icons/connect.png new file mode 100644 index 0000000..e8ef8cb Binary files /dev/null and b/assets/icons/connect.png differ diff --git a/assets/icons/copy.png b/assets/icons/copy.png new file mode 100644 index 0000000..cd5cd7c Binary files /dev/null and b/assets/icons/copy.png differ diff --git a/assets/icons/dissatisfied.png b/assets/icons/dissatisfied.png new file mode 100644 index 0000000..c111236 Binary files /dev/null and b/assets/icons/dissatisfied.png differ diff --git a/assets/icons/edit.png b/assets/icons/edit.png new file mode 100644 index 0000000..1cf9ef6 Binary files /dev/null and b/assets/icons/edit.png differ diff --git a/assets/icons/environment.png b/assets/icons/environment.png new file mode 100644 index 0000000..83f00c7 Binary files /dev/null and b/assets/icons/environment.png differ diff --git a/assets/icons/examining.png b/assets/icons/examining.png new file mode 100644 index 0000000..b7fadbb Binary files /dev/null and b/assets/icons/examining.png differ diff --git a/assets/icons/finish_order.png b/assets/icons/finish_order.png new file mode 100644 index 0000000..c77f2f0 Binary files /dev/null and b/assets/icons/finish_order.png differ diff --git a/assets/icons/normal.png b/assets/icons/normal.png new file mode 100644 index 0000000..5c2fefb Binary files /dev/null and b/assets/icons/normal.png differ diff --git a/assets/icons/pass.png b/assets/icons/pass.png new file mode 100644 index 0000000..26ae537 Binary files /dev/null and b/assets/icons/pass.png differ diff --git a/assets/icons/phone.png b/assets/icons/phone.png new file mode 100644 index 0000000..fca1b3d Binary files /dev/null and b/assets/icons/phone.png differ diff --git a/assets/icons/phone_circle.png b/assets/icons/phone_circle.png new file mode 100644 index 0000000..462dae8 Binary files /dev/null and b/assets/icons/phone_circle.png differ diff --git a/assets/icons/record.png b/assets/icons/record.png new file mode 100644 index 0000000..194a276 Binary files /dev/null and b/assets/icons/record.png differ diff --git a/assets/icons/reject.png b/assets/icons/reject.png new file mode 100644 index 0000000..e1eac9b Binary files /dev/null and b/assets/icons/reject.png differ diff --git a/assets/icons/reward.png b/assets/icons/reward.png new file mode 100644 index 0000000..2683a0d Binary files /dev/null and b/assets/icons/reward.png differ diff --git a/assets/icons/satisfied.png b/assets/icons/satisfied.png new file mode 100644 index 0000000..eddabde Binary files /dev/null and b/assets/icons/satisfied.png differ diff --git a/assets/icons/tag.png b/assets/icons/tag.png new file mode 100644 index 0000000..a74bdef Binary files /dev/null and b/assets/icons/tag.png differ diff --git a/assets/icons/task_location.png b/assets/icons/task_location.png new file mode 100644 index 0000000..de56355 Binary files /dev/null and b/assets/icons/task_location.png differ diff --git a/assets/icons/watch.png b/assets/icons/watch.png new file mode 100644 index 0000000..cbac68e Binary files /dev/null and b/assets/icons/watch.png differ diff --git a/lib/const/saas_api.dart b/lib/const/saas_api.dart index c948589..14a114c 100644 --- a/lib/const/saas_api.dart +++ b/lib/const/saas_api.dart @@ -24,6 +24,7 @@ class SAASAPI { static _Inspection inspection = _Inspection(); static _User user = _User(); static _Message message = _Message(); + static _WorkOrder workOrder = _WorkOrder(); ///二级分类 static _OtherApi other = _OtherApi(); @@ -122,3 +123,51 @@ class _Message { ///查询所有的管家app信息 String get messageList => '/butlerApp/user/message/list'; } + +class _WorkOrder { + ///提醒支付 + String get reminderPay => '/butlerApp/user/workOrder/reminderPay'; + + ///开始服务 + String get startService => '/butlerApp/user/workOrder/startService'; + + ///完成工单 + String get finish => '/butlerApp/user/workOrder/finish'; + + ///汇报 + String get report => '/butlerApp/user/workOrder/report'; + + ///领取任务 + String get receiveTask => '/butlerApp/user/workOrder/receiveTask'; + + ///提醒用户确认 + String get reminderConfirm => '/butlerApp/user/workOrder/reminderConfirm'; + + ///提醒完成 + String get reminderDone => '/butlerApp/user/workOrder/reminderDone'; + + ///提醒处理 + String get reminderProcessing => '/butlerApp/user/workOrder/reminderDone'; + + ///移至待分配 + String get moveToAssignment => '/butlerApp/user/workOrder/moveToAssignment'; + + ///加入工单池 + String get joinTicketPool => '/butlerApp/user/workOrder/joinTicketPool'; + + ///取消工单 + String get cancel => '/butlerApp/user/workOrder/cancel'; + + ///查询服务团队名单 + String get findServiceTeamList => + '/butlerApp/user/workOrder/findServiceTeamList'; + + ///根据工单主键id查询工单信息 + String get findById => '/butlerApp/user/workOrder/findById'; + + ///添加管家app工单信息 + String get insert => '/butlerApp/user/workOrder/insert'; + + ///查询所有的管家app工单信息 + String get list => '/butlerApp/user/workOrder/list'; +} diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 84689c9..78082a0 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -146,6 +146,86 @@ class $AssetsHomeGen { AssetGenImage get sos => const AssetGenImage('assets/home/sos.png'); } +class $AssetsIconsGen { + const $AssetsIconsGen(); + + /// File path: assets/icons/alarm_clock.png + AssetGenImage get alarmClock => + const AssetGenImage('assets/icons/alarm_clock.png'); + + /// File path: assets/icons/camera.png + AssetGenImage get camera => const AssetGenImage('assets/icons/camera.png'); + + /// File path: assets/icons/cancel_task.png + AssetGenImage get cancelTask => + const AssetGenImage('assets/icons/cancel_task.png'); + + /// File path: assets/icons/clock_circle.png + AssetGenImage get clockCircle => + const AssetGenImage('assets/icons/clock_circle.png'); + + /// File path: assets/icons/connect.png + AssetGenImage get connect => const AssetGenImage('assets/icons/connect.png'); + + /// File path: assets/icons/copy.png + AssetGenImage get copy => const AssetGenImage('assets/icons/copy.png'); + + /// File path: assets/icons/dissatisfied.png + AssetGenImage get dissatisfied => + const AssetGenImage('assets/icons/dissatisfied.png'); + + /// File path: assets/icons/edit.png + AssetGenImage get edit => const AssetGenImage('assets/icons/edit.png'); + + /// File path: assets/icons/environment.png + AssetGenImage get environment => + const AssetGenImage('assets/icons/environment.png'); + + /// File path: assets/icons/examining.png + AssetGenImage get examining => + const AssetGenImage('assets/icons/examining.png'); + + /// File path: assets/icons/finish_order.png + AssetGenImage get finishOrder => + const AssetGenImage('assets/icons/finish_order.png'); + + /// File path: assets/icons/normal.png + AssetGenImage get normal => const AssetGenImage('assets/icons/normal.png'); + + /// File path: assets/icons/pass.png + AssetGenImage get pass => const AssetGenImage('assets/icons/pass.png'); + + /// File path: assets/icons/phone.png + AssetGenImage get phone => const AssetGenImage('assets/icons/phone.png'); + + /// File path: assets/icons/phone_circle.png + AssetGenImage get phoneCircle => + const AssetGenImage('assets/icons/phone_circle.png'); + + /// File path: assets/icons/record.png + AssetGenImage get record => const AssetGenImage('assets/icons/record.png'); + + /// File path: assets/icons/reject.png + AssetGenImage get reject => const AssetGenImage('assets/icons/reject.png'); + + /// File path: assets/icons/reward.png + AssetGenImage get reward => const AssetGenImage('assets/icons/reward.png'); + + /// File path: assets/icons/satisfied.png + AssetGenImage get satisfied => + const AssetGenImage('assets/icons/satisfied.png'); + + /// File path: assets/icons/tag.png + AssetGenImage get tag => const AssetGenImage('assets/icons/tag.png'); + + /// File path: assets/icons/task_location.png + AssetGenImage get taskLocation => + const AssetGenImage('assets/icons/task_location.png'); + + /// File path: assets/icons/watch.png + AssetGenImage get watch => const AssetGenImage('assets/icons/watch.png'); +} + class $AssetsInspectionGen { const $AssetsInspectionGen(); @@ -456,6 +536,7 @@ class Assets { Assets._(); static const $AssetsHomeGen home = $AssetsHomeGen(); + static const $AssetsIconsGen icons = $AssetsIconsGen(); static const $AssetsInspectionGen inspection = $AssetsInspectionGen(); static const $AssetsManageGen manage = $AssetsManageGen(); static const $AssetsMessageGen message = $AssetsMessageGen(); diff --git a/lib/new_ui/work_order/dialog/urge_dialog.dart b/lib/new_ui/work_order/dialog/urge_dialog.dart new file mode 100644 index 0000000..4c23bd2 --- /dev/null +++ b/lib/new_ui/work_order/dialog/urge_dialog.dart @@ -0,0 +1,35 @@ +import 'package:aku_new_community_manager/ui/widgets/common/bee_long_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class UrgeDialog extends StatelessWidget { + final VoidCallback onConfirm; + final String title; + final String content; + + const UrgeDialog({ + Key? key, + required this.onConfirm, + required this.title, + required this.content, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 32.w), + child: Column( + children: [ + 120.w.heightBox, + title.text.size(36.sp).black.bold.isIntrinsic.make(), + 40.w.heightBox, + content.text.size(28.sp).isIntrinsic.make(), + 150.w.heightBox, + BeeLongButton(onPressed: onConfirm, text: '确认提醒') + ], + ), + ); + } +} diff --git a/lib/new_ui/work_order/distributor/distributor_card.dart b/lib/new_ui/work_order/distributor/distributor_card.dart new file mode 100644 index 0000000..6456c41 --- /dev/null +++ b/lib/new_ui/work_order/distributor/distributor_card.dart @@ -0,0 +1,248 @@ +import 'package:aku_new_community_manager/gen/assets.gen.dart'; +import 'package:aku_new_community_manager/new_ui/work_order/distributor/distributor_detail_page.dart'; +import 'package:aku_new_community_manager/saas_models/work_order/work_order_list_model.dart'; +import 'package:aku_new_community_manager/style/app_style.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/bee_hor_image_view.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/car_bottom_button.dart'; +import 'package:aku_new_community_manager/utils/bee_date_util.dart'; +import 'package:common_utils/common_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:velocity_x/src/extensions/string_ext.dart'; +import 'package:velocity_x/velocity_x.dart'; + +import '../work_order_func.dart'; +import '../work_order_map.dart'; + +class DistributorCard extends StatelessWidget { + final WorkOrderListModel model; + final VoidCallback refresh; + + const DistributorCard({Key? key, required this.model, required this.refresh}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned( + top: 9.35.w, + right: 0, + child: Container( + width: 160.w, + height: 60.w, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomLeft, + end: Alignment.topRight, + colors: [ + Color(0xFFFAC058), + Color(0xFFFFD589), + ]), + color: kPrimaryColor, + borderRadius: + BorderRadius.only(topRight: Radius.circular(12.w)), + ), + alignment: Alignment.center, + child: Text( + '${WorkOrderMap.orderStatus[model.status]}', + style: TextStyle( + fontSize: 26.sp, + color: Colors.black, + ), + ), + )), + GestureDetector( + onTap: () { + Get.to(() => DistributorDetailPage( + id: model.id, + )); + }, + child: ClipPath( + clipper: WorkOrderCardClip(), + child: Container( + padding: EdgeInsets.all(24.w), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(12.w), + bottomLeft: Radius.circular(12.w), + bottomRight: Radius.circular(12.w)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: EdgeInsets.symmetric( + vertical: 8.w, horizontal: 16.w), + decoration: BoxDecoration( + color: Color(0xFFFFFBE6), + borderRadius: BorderRadius.circular(8.w), + ), + child: '${model.workOrderTypeName}' + .text + .size(24.sp) + .color(Color(0xFFD48806)) + .make(), + ), + ], + ), + 16.w.heightBox, + Row( + children: [ + Assets.icons.alarmClock.image(width: 40.w, height: 40.w), + 24.w.widthBox, + '${model.updateDate}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + ], + ), + 16.w.heightBox, + Row( + children: [ + Assets.icons.taskLocation + .image(width: 40.w, height: 40.w), + 24.w.widthBox, + '${model.reserveAddress}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + ], + ), + 60.w.heightBox, + '${model.content}' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.65)) + .make(), + 24.w.heightBox, + BeeHorImageView( + maxCount: 4, + imgs: model.imgList ?? [], + imgWidth: 146.w, + imgHeight: 146.w), + 24.w.heightBox, + Row( + children: [ + BeeDateUtil(DateUtil.getDateTime(model.createDate)) + .timeAgo + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + Spacer(), + ], + ), + ], + ), + ), + ), + ) + ], + ); + } + + Widget getCardBottomButton() { + switch (model.status) { + case 1: + return SizedBox.shrink(); + case 2: + return SizedBox.shrink(); + case 3: + return CardBottomButton.yellow( + text: '提醒处理', + onPressed: () async { + var re = await WorkOrderFuc.reminderProcessing(model.id); + if (re) { + refresh(); + } + }); + case 4: + return CardBottomButton.yellow( + text: '提醒完成', + onPressed: () async { + var re = await WorkOrderFuc.reminderDone(model.id); + if (re) { + refresh(); + } + }); + case 5: + return CardBottomButton.yellow( + text: '提醒确认', + onPressed: () async { + var re = await WorkOrderFuc.reminderConfirm(model.id); + if (re) { + refresh(); + } + }); + case 6: + return CardBottomButton.yellow( + text: '提醒支付', + onPressed: () async { + var re = await WorkOrderFuc.reminderPay(model.id); + if (re) { + refresh(); + } + }); + case 7: + return CardBottomButton.yellow(text: '去评价', onPressed: () {}); + case 8: + return CardBottomButton.yellow(text: '查看评价', onPressed: () {}); + case 9: + case 10: + default: + return SizedBox.shrink(); + } + } +} + +class WorkOrderCardClip extends CustomClipper { + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return false; + } + + @override + Path getClip(Size size) { + Path path = Path(); + //第一段圆弧起始位置的横坐标 + double asx = 531.w; + //第一段圆弧终点位置横坐标 + double aex = 542.46.w; + //第一段圆弧终点位置纵坐标 + double aey = 9.34.w; + + //第一段圆弧控制点位置横坐标 + double acx = 540.w; + + //第二段圆弧起始点位置横坐标 + double bsx = 558.45.w; + //第二段圆弧起始点位置纵坐标 + double bsy = 58.88.w; + //第二段圆弧终点点位置横坐标 + double bex = 569.91.w; + //第二段圆弧终点位纵横坐标 + double bey = 67.35.w; + //第二段圆弧控制点位置横坐标 + double bcx = 561.06.w; + //第二段圆弧控制点位置纵坐标 + double bcy = 67.35.w; + path.lineTo(asx, 0); + path.quadraticBezierTo(acx, 0, aex, aey); + path.lineTo(bsx, bsy); + path.quadraticBezierTo(bcx, bcy, bex, bey); + path.lineTo(size.width, bey); + path.lineTo(size.width, size.height); + path.lineTo(0, size.height); + path.lineTo(0, 0); + path.close(); + return path; + } +} diff --git a/lib/new_ui/work_order/distributor/distributor_detail_page.dart b/lib/new_ui/work_order/distributor/distributor_detail_page.dart new file mode 100644 index 0000000..4df286d --- /dev/null +++ b/lib/new_ui/work_order/distributor/distributor_detail_page.dart @@ -0,0 +1,529 @@ +import 'package:aku_new_community_manager/const/saas_api.dart'; +import 'package:aku_new_community_manager/gen/assets.gen.dart'; +import 'package:aku_new_community_manager/saas_models/work_order/work_order_detail_model.dart'; +import 'package:aku_new_community_manager/ui/widgets/app_widgets/bee_grid_image_view.dart'; +import 'package:aku_new_community_manager/ui/widgets/app_widgets/bee_image_network.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/aku_scaffold.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/bee_divider.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/bee_long_button.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/stack_avatar.dart'; +import 'package:aku_new_community_manager/utils/network/net_util.dart'; +import 'package:bot_toast/bot_toast.dart'; +import 'package:common_utils/common_utils.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:velocity_x/src/extensions/num_ext.dart'; +import 'package:velocity_x/src/extensions/string_ext.dart'; + +import '../team_list_page.dart'; +import '../work_order_func.dart'; +import '../work_order_map.dart'; + +class DistributorDetailPage extends StatefulWidget { + final int id; + + const DistributorDetailPage({ + Key? key, + required this.id, + }) : super(key: key); + + @override + _DistributorDetailPageState createState() => _DistributorDetailPageState(); +} + +class _DistributorDetailPageState extends State { + WorkOrderDetailModel? _model; + EasyRefreshController _refreshController = EasyRefreshController(); + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AkuScaffold( + title: '', + extendBody: true, + body: Stack( + children: [ + _model == null + ? Container() + : Container( + width: double.infinity, + height: 380.w, + decoration: BoxDecoration( + gradient: _getLiner, + ), + child: Column( + children: [ + 150.w.heightBox, + Row( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 32.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async {}, + child: Material( + color: Colors.transparent, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + '${WorkOrderMap.orderStatus[_model?.status] ?? ''}' + .text + .size(40.sp) + .color(Colors.black) + .bold + .make(), + 16.w.widthBox, + Icon( + CupertinoIcons.chevron_right, + size: 32.w, + color: Colors.black.withOpacity(0.65), + ) + ], + ), + ), + ), + '${WorkOrderMap.subStatusString[_model?.status] ?? ''}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + ], + ), + ), + Spacer(), + ], + ), + ], + ), + ), + SafeArea( + child: EasyRefresh( + firstRefresh: true, + header: MaterialHeader(), + onRefresh: () async { + var base = await NetUtil().get(SAASAPI.workOrder.findById, + params: {'workOrderId': widget.id}); + if (base.success) { + _model = WorkOrderDetailModel.fromJson(base.data); + setState(() {}); + } + }, + child: _model == null + ? Container() + : ListView( + padding: + EdgeInsets.only(top: 120.w, left: 32.w, right: 32.w), + children: [ + Offstage( + offstage: _model!.servicePersonnelImgList == null || + _model!.servicePersonnelImgList!.isEmpty, + child: Column( + children: [ + _servicePeople(), + 24.w.heightBox, + ], + )), + _head(), + 24.w.heightBox, + _content(), + 24.w.heightBox, + _taskInfo(), + ], + ), + )), + ], + ), + bottom: Padding( + padding: EdgeInsets.only( + left: 32.w, + right: 32.w, + bottom: MediaQuery.of(context).padding.bottom + 32.w), + child: _getBottomButton()), + ); + } + + LinearGradient get _getLiner { + switch (_model?.status) { + case 7: + case 8: + return LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0xFFA5A5A6).withOpacity(0), + Color(0xFFE0DDDE), + ]); + case 9: + return LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0xFFFF0000).withOpacity(0), + Color(0xFFFC5757), + ]); + default: + return LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0xFFFFD589).withOpacity(0), + Color(0xFFFAC058), + ]); + } + } + + Widget _getBottomButton() { + switch (_model?.status) { + case 1: + return BeeLongButton( + onPressed: () async { + var re = await WorkOrderFuc.joinOrderPool(widget.id); + if (re) { + _refreshController.callRefresh(); + } + }, + text: '加入工单池'); + case 2: + return BeeLongButton( + onPressed: () async { + var re = await WorkOrderFuc.moveToAssignment(widget.id); + if (re) { + _refreshController.callRefresh(); + } + }, + text: '移至待分配'); + + case 3: + return BeeLongButton( + onPressed: () async { + var re = await WorkOrderFuc.reminderProcessing(widget.id); + if (re) { + _refreshController.callRefresh(); + } + }, + text: '提醒处理'); + + case 4: + return BeeLongButton( + onPressed: () async { + var re = await WorkOrderFuc.reminderConfirm(widget.id); + if (re) { + _refreshController.callRefresh(); + } + }, + text: '提醒用户确认'); + case 5: + return BeeLongButton( + onPressed: () async { + var re = await WorkOrderFuc.reminderPay(widget.id); + if (re) { + _refreshController.callRefresh(); + } + }, + text: '提醒支付'); + case 6: + + case 7: + + case 8: + return SizedBox.shrink(); + case 9: + return SizedBox.shrink(); + default: + return SizedBox.shrink(); + } + } + + Widget _historyReport() { + return GestureDetector( + onTap: () {}, + child: Material( + color: Colors.transparent, + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(16.w)), + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 32.w), + child: Row( + children: [ + '历史报告' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.85)) + .make(), + Spacer(), + '点击查看' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + 24.w.widthBox, + Icon( + CupertinoIcons.chevron_right, + size: 24.w, + color: Colors.black.withOpacity(0.45), + ), + ], + ), + ), + ), + ); + } + + Widget _servicePeople() { + return Container( + padding: EdgeInsets.all(24.w), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12.w), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + '服务人员名单' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.85)) + .make(), + 24.w.heightBox, + BeeDivider.horizontal(), + 24.w.heightBox, + GestureDetector( + onTap: () { + Get.to(() => TeamListPage( + workOderId: _model!.id, + )); + }, + child: Material( + color: Colors.transparent, + child: Row( + children: [ + StackAvatar( + avatars: (_model!.servicePersonnelImgList ?? []) + .map((e) => e.url) + .toList()), + Spacer(), + '点击查看' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.65)) + .make(), + Icon( + CupertinoIcons.chevron_right, + size: 24.w, + ) + ], + ), + ), + ) + ], + ), + ); + } + + Widget _head() { + return Container( + padding: EdgeInsets.all(24.w), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12.w), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: EdgeInsets.symmetric(vertical: 8.w, horizontal: 16.w), + decoration: BoxDecoration( + color: Color(0xFFFFFBE6), + borderRadius: BorderRadius.circular(8.w), + ), + child: '${_model!.workOrderTypeName}' + .text + .size(24.sp) + .color(Color(0xFFD48806)) + .make(), + ), + ], + ), + 16.w.heightBox, + Row( + children: [ + Assets.icons.alarmClock.image(width: 40.w, height: 40.w), + 24.w.widthBox, + '${DateUtil.formatDateStr(_model!.reserveDate)}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + ], + ), + 16.w.heightBox, + Row( + children: [ + Assets.icons.taskLocation.image(width: 40.w, height: 40.w), + 24.w.widthBox, + '${_model!.reserveAddress}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + ], + ), + 24.w.heightBox, + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 24.w), + child: Column( + children: [ + '申请人' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.25)) + .make(), + 24.w.heightBox, + Row( + children: [ + ClipOval( + child: BeeImageNetwork( + width: 108.w, + height: 108.w, + imgs: _model!.applicantImgList, + ), + ), + 16.w.widthBox, + Expanded( + child: Column( + children: [ + _model!.applicantName.text + .size(28.sp) + .color(Colors.black.withOpacity(0.85)) + .make(), + 8.w.heightBox, + '${_model!.identity == 1 ? '住户' : '物业'}|${_model!.buildingName}${_model!.unitName}${_model!.estateName}' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + GestureDetector( + onTap: () { + launch('tel:${_model!.applicantTel}'); + }, + child: Assets.icons.phoneCircle + .image(width: 40.w, height: 40.w)), + ], + )), + ], + ) + ], + ), + ), + 24.w.heightBox, + ], + ), + ); + } + + Widget _content() { + return Container( + padding: EdgeInsets.all(24.w), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12.w), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + '${_model!.content}' + .text + .size(28.sp) + .color(Colors.black.withOpacity(0.65)) + .make(), + 24.w.heightBox, + BeeGridImageView( + urls: (_model!.imgList ?? []).map((e) => e.url).toList(), + ), + 24.w.heightBox, + ], + ), + ); + } + + Widget _taskInfo() { + return Container( + width: 686.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.w), + ), + padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 24.w), + child: Column( + children: [ + Row( + children: [ + '任务信息'.text.size(28.sp).color(Colors.black).bold.make(), + Spacer(), + ], + ), + 24.w.heightBox, + BeeDivider.horizontal(), + 24.w.heightBox, + Row( + children: [ + '创建时间' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + Spacer(), + '${DateUtil.formatDateStr(_model!.createDate)}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + 64.w.widthBox, + ], + ), + 24.w.heightBox, + Row( + children: [ + '工单编号' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + Spacer(), + '${_model!.code}' + .text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + 24.w.widthBox, + GestureDetector( + onTap: () async { + await Clipboard.setData(ClipboardData(text: _model!.code)); + BotToast.showText(text: '已复制到粘贴板'); + }, + child: Assets.icons.copy.image(width: 40.w, height: 40.w)), + ], + ) + ], + ), + ); + } +} diff --git a/lib/new_ui/work_order/distributor/work_order_distributor_page.dart b/lib/new_ui/work_order/distributor/work_order_distributor_page.dart new file mode 100644 index 0000000..deca129 --- /dev/null +++ b/lib/new_ui/work_order/distributor/work_order_distributor_page.dart @@ -0,0 +1,112 @@ +import 'package:aku_new_community_manager/const/saas_api.dart'; +import 'package:aku_new_community_manager/new_ui/work_order/distributor/distributor_card.dart'; +import 'package:aku_new_community_manager/saas_models/work_order/work_order_list_model.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/aku_scaffold.dart'; +import 'package:aku_new_community_manager/ui/widgets/inner/aku_tab_bar.dart'; +import 'package:aku_new_community_manager/utils/network/net_util.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class WorkOrderDistributorPage extends StatefulWidget { + const WorkOrderDistributorPage({Key? key}) : super(key: key); + + @override + _WorkOrderDistributorPageState createState() => + _WorkOrderDistributorPageState(); +} + +class _WorkOrderDistributorPageState extends State + with SingleTickerProviderStateMixin { + List _tabs = ['全部', '待分配', '工单池', '已接单', '处理中', '待确认', '已完成', '']; + late TabController _tabController; + EasyRefreshController _refreshController = EasyRefreshController(); + int _page = 1; + int _size = 10; + List _models = []; + + @override + void initState() { + _tabController = TabController(length: _tabs.length, vsync: this); + super.initState(); + } + + @override + void dispose() { + _refreshController.dispose(); + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AkuScaffold( + title: '工单管理', + actions: [ + IconButton( + onPressed: () {}, + icon: Icon( + CupertinoIcons.plus_circle, + size: 40.w, + )) + ], + appBarBottom: PreferredSize( + preferredSize: Size.fromHeight(88.w), + child: AkuTabBar( + tabs: _tabs, + controller: _tabController, + ), + ), + body: TabBarView( + controller: _tabController, + children: + _tabs.mapIndexed((e, index) => _getOrderView(index)).toList()), + ); + } + + Widget _getOrderView(int index) { + return EasyRefresh( + firstRefresh: true, + header: MaterialHeader(), + onRefresh: () async { + _page = 1; + var base = await NetUtil().getList(SAASAPI.workOrder.list, params: { + 'pageNum': _page, + 'size': _size, + 'status': index == 0 ? null : index, + }); + _models = + base.rows.map((e) => WorkOrderListModel.fromJson(e)).toList(); + setState(() {}); + }, + onLoad: () async { + _page++; + var base = await NetUtil().getList(SAASAPI.workOrder.list, params: { + 'pageNum': _page, + 'size': _size, + 'status': index == 0 ? null : index, + }); + if (_models.length < base.total) { + _models.addAll( + base.rows.map((e) => WorkOrderListModel.fromJson(e)).toList()); + setState(() {}); + } else { + _refreshController.finishLoad(); + } + }, + child: ListView.separated( + padding: EdgeInsets.all(24.w), + itemBuilder: (context, index) { + return DistributorCard( + model: _models[index], + refresh: _refreshController.callRefresh, + ); + }, + separatorBuilder: (context, index) { + return 24.w.heightBox; + }, + itemCount: _models.length)); + } +} diff --git a/lib/new_ui/work_order/team_list_page.dart b/lib/new_ui/work_order/team_list_page.dart new file mode 100644 index 0000000..d09b2b9 --- /dev/null +++ b/lib/new_ui/work_order/team_list_page.dart @@ -0,0 +1,111 @@ +import 'package:aku_new_community_manager/const/saas_api.dart'; +import 'package:aku_new_community_manager/gen/assets.gen.dart'; +import 'package:aku_new_community_manager/saas_models/work_order/team_list_model.dart'; +import 'package:aku_new_community_manager/ui/widgets/common/aku_scaffold.dart'; +import 'package:aku_new_community_manager/utils/network/net_util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class TeamListPage extends StatefulWidget { + final int workOderId; + + const TeamListPage({Key? key, required this.workOderId}) : super(key: key); + + @override + _TeamListPageState createState() => _TeamListPageState(); +} + +class _TeamListPageState extends State { + List _models = []; + + TeamListModel? get _mainModel { + for (var item in _models) { + if (item.userType == 1) { + return item; + } else { + return null; + } + } + } + + @override + Widget build(BuildContext context) { + return AkuScaffold( + title: '服务团队名单', + body: EasyRefresh( + onRefresh: () async { + var base = await NetUtil() + .get(SAASAPI.workOrder.findServiceTeamList, params: { + 'workOrderId': widget.workOderId, + }); + if (base.success) { + _models = (base.data as List) + .map((e) => TeamListModel.fromJson(e)) + .toList(); + setState(() {}); + } + }, + child: ListView( + children: [ + _positionTile('主负责人'), + if (_mainModel != null) _personTile(_mainModel!), + _positionTile('协同人'), + ..._models.map((e) => _personTile(e)).toList() + ], + ), + ), + ); + } + + Widget _personTile(TeamListModel model) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 16.w), + width: double.infinity, + child: Row( + children: [ + CircleAvatar( + backgroundColor: Colors.red, + ), + 16.w.heightBox, + Column( + children: [ + model.manageUserName.text + .size(28.sp) + .color(Colors.black.withOpacity(0.85)) + .make(), + 8.w.heightBox, + model.departmentName.text + .size(24.sp) + .color(Colors.black.withOpacity(0.45)) + .make(), + ], + ), + Spacer(), + GestureDetector( + onTap: () async { + await launch('tel:${model.manageUserTel}'); + }, + child: Assets.icons.phone.image(width: 40.w, height: 40.w), + ) + ], + ), + ); + } + + Widget _positionTile(String text) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 16.w), + width: double.infinity, + child: Text( + text, + style: TextStyle( + fontSize: 26.sp, + color: Colors.black.withOpacity(0.45), + ), + ), + ); + } +} diff --git a/lib/new_ui/work_order/work_order_func.dart b/lib/new_ui/work_order/work_order_func.dart new file mode 100644 index 0000000..f7bf648 --- /dev/null +++ b/lib/new_ui/work_order/work_order_func.dart @@ -0,0 +1,108 @@ +import 'package:aku_new_community_manager/const/saas_api.dart'; +import 'package:aku_new_community_manager/saas_models/net_model/base_model.dart'; +import 'package:aku_new_community_manager/saas_models/work_order/work_order_submit_model.dart'; +import 'package:aku_new_community_manager/utils/network/net_util.dart'; + +class WorkOrderFuc { + ///提醒用户确认 + static Future reminderConfirm(int id) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.reminderConfirm, + params: {'workOrderId': id}, showMessage: true); + return baseModel.success; + } + + ///提醒完成 + static Future reminderDone(int id) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.reminderDone, + params: {'workOrderId': id}, showMessage: true); + return baseModel.success; + } + + ///提醒处理 + static Future reminderProcessing(int id) async { + BaseModel baseModel = await NetUtil().get( + SAASAPI.workOrder.reminderProcessing, + params: {'workOrderId': id}, + showMessage: true); + return baseModel.success; + } + + ///提醒处理 + static Future reminderPay(int id) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.reminderPay, + params: {'workOrderId': id}, showMessage: true); + return baseModel.success; + } + + ///开始服务 + static Future startService(int id) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.startService, + params: {'workOrderId': id}, showMessage: true); + return baseModel.success; + } + + ///加入工单池 + static Future joinOrderPool(int id) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.joinTicketPool, + params: {'workOrderId': id}, showMessage: true); + return baseModel.success; + } + + ///移至待分配 + static Future moveToAssignment(int id) async { + BaseModel baseModel = await NetUtil().get( + SAASAPI.workOrder.moveToAssignment, + params: {'workOrderId': id}, + showMessage: true); + return baseModel.success; + } + + ///领取任务 + static Future receiveTask(int id) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.receiveTask, + params: {'workOrderId': id}, showMessage: true); + return baseModel.success; + } + + ///完成工单 + static Future confirmComplete(WorkOrderSubmitModel model) async { + BaseModel baseModel = await NetUtil().get(SAASAPI.workOrder.finish, + params: model.toJson(), showMessage: true); + return baseModel.success; + } + + ///取消 + static Future cancel({ + required int workOrderId, + }) async { + var base = await NetUtil().get(SAASAPI.workOrder.cancel, + params: { + 'workOrderId': workOrderId, + }, + showMessage: true); + return base.success; + } + + ///发布工单 + static Future publish({ + required int estateId, + required int workOrderTypeId, + required String reserveDate, + required String reserveAddress, + required String content, + required List imgUrls, + }) async { + var base = await NetUtil().post(SAASAPI.workOrder.insert, + params: { + 'estateId': estateId, + 'workOrderTypeId': workOrderTypeId, + 'reserveDate': reserveDate, + 'reserveAddress': reserveAddress, + 'content': content, + 'imgUrls': imgUrls, + }, + showMessage: true); + + return base.success; + } +} diff --git a/lib/new_ui/work_order/work_order_map.dart b/lib/new_ui/work_order/work_order_map.dart new file mode 100644 index 0000000..1b66468 --- /dev/null +++ b/lib/new_ui/work_order/work_order_map.dart @@ -0,0 +1,25 @@ +class WorkOrderMap { + static Map orderStatus = { + 1: '待分配', + 2: '工单池', + 3: '已接单', + 4: '处理中', + 5: '待确认', + 6: '待支付', + 7: '已完成', + 8: '已评价', + 9: '已取消', + }; + + static Map subStatusString = { + 1: '请及时前往现场与业主进行确认、检查', + 2: '请及时前往现场与业主进行确认、检查', + 3: '请及时前往现场与业主进行确认、检查', + 4: '进行过程中,记得提交报告哦', + 5: '如果申请人长时间未确认,请让物业尝试联系对方', + 6: '如果申请人长时间未支付,请让物业尝试联系对方', + 7: '用户已支付工单费用', + 8: '用户已对您的表现进行评价', + 9: '任务已拒绝,中止工单进行' + }; +} diff --git a/lib/saas_models/user/user_info_model.dart b/lib/saas_models/user/user_info_model.dart index 7a506ba..ddff59a 100644 --- a/lib/saas_models/user/user_info_model.dart +++ b/lib/saas_models/user/user_info_model.dart @@ -24,6 +24,9 @@ class UserInfoModel extends Equatable { final String entryDate; final String? remake; final List? imgList; + final String communityName; + final String positionName; + final int? workOrderJurisdiction; String get sexValue { if (sex == 1) return '男'; @@ -63,6 +66,9 @@ class UserInfoModel extends Equatable { entryDate, remake, imgList, + communityName, + positionName, + workOrderJurisdiction ]; const UserInfoModel({ @@ -83,5 +89,8 @@ class UserInfoModel extends Equatable { required this.entryDate, this.remake, this.imgList, + required this.communityName, + required this.positionName, + this.workOrderJurisdiction, }); } diff --git a/lib/saas_models/user/user_info_model.g.dart b/lib/saas_models/user/user_info_model.g.dart index 41fecea..2582ca9 100644 --- a/lib/saas_models/user/user_info_model.g.dart +++ b/lib/saas_models/user/user_info_model.g.dart @@ -27,4 +27,7 @@ UserInfoModel _$UserInfoModelFromJson(Map json) => imgList: (json['imgList'] as List?) ?.map((e) => ImgModel.fromJson(e as Map)) .toList(), + communityName: json['communityName'] as String, + positionName: json['positionName'] as String, + workOrderJurisdiction: json['workOrderJurisdiction'] as int?, ); diff --git a/lib/saas_models/work_order/team_list_model.dart b/lib/saas_models/work_order/team_list_model.dart new file mode 100644 index 0000000..565b8c4 --- /dev/null +++ b/lib/saas_models/work_order/team_list_model.dart @@ -0,0 +1,45 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'team_list_model.g.dart'; + +@JsonSerializable() +class TeamListModel extends Equatable { + final int id; + final int userType; + final int manageUserId; + final String manageUserName; + final String manageUserTel; + final int departmentId; + final String departmentName; + final int positionId; + final String positionName; + + factory TeamListModel.fromJson(Map json) => + _$TeamListModelFromJson(json); + + const TeamListModel({ + required this.id, + required this.userType, + required this.manageUserId, + required this.manageUserName, + required this.manageUserTel, + required this.departmentId, + required this.departmentName, + required this.positionId, + required this.positionName, + }); + + @override + List get props => [ + id, + userType, + manageUserId, + manageUserName, + manageUserTel, + departmentId, + departmentName, + positionId, + positionName, + ]; +} diff --git a/lib/saas_models/work_order/team_list_model.g.dart b/lib/saas_models/work_order/team_list_model.g.dart new file mode 100644 index 0000000..27c44de --- /dev/null +++ b/lib/saas_models/work_order/team_list_model.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'team_list_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TeamListModel _$TeamListModelFromJson(Map json) => + TeamListModel( + id: json['id'] as int, + userType: json['userType'] as int, + manageUserId: json['manageUserId'] as int, + manageUserName: json['manageUserName'] as String, + manageUserTel: json['manageUserTel'] as String, + departmentId: json['departmentId'] as int, + departmentName: json['departmentName'] as String, + positionId: json['positionId'] as int, + positionName: json['positionName'] as String, + ); diff --git a/lib/saas_models/work_order/work_order_detail_model.dart b/lib/saas_models/work_order/work_order_detail_model.dart new file mode 100644 index 0000000..e58539e --- /dev/null +++ b/lib/saas_models/work_order/work_order_detail_model.dart @@ -0,0 +1,87 @@ +import 'package:aku_new_community_manager/models/common/img_model.dart'; +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'work_order_detail_model.g.dart'; + +@JsonSerializable() +class WorkOrderDetailModel extends Equatable { + final int id; + final String code; + final int status; + final String workOrderTypeName; + final String reserveAddress; + final String reserveDate; + final int identity; + final int applicantId; + final String applicantName; + final String applicantTel; + final List? applicantImgList; + final String buildingName; + final String unitName; + final String estateName; + final String content; + final String createDate; + final List? imgList; + final List? servicePersonnelImgList; + final int newReportNum; + final int? evaluateLevel; + final String? evaluateContent; + final String? evaluateDate; + final double totalCost; + + factory WorkOrderDetailModel.fromJson(Map json) => + _$WorkOrderDetailModelFromJson(json); + + @override + List get props => [ + code, + status, + workOrderTypeName, + reserveAddress, + reserveDate, + identity, + applicantId, + applicantName, + applicantTel, + buildingName, + unitName, + estateName, + content, + createDate, + imgList, + servicePersonnelImgList, + newReportNum, + evaluateLevel, + evaluateContent, + evaluateDate, + totalCost, + applicantImgList, + ]; + + const WorkOrderDetailModel({ + required this.id, + required this.code, + required this.status, + required this.workOrderTypeName, + required this.reserveAddress, + required this.reserveDate, + required this.identity, + required this.applicantId, + required this.applicantName, + required this.applicantTel, + this.applicantImgList, + required this.buildingName, + required this.unitName, + required this.estateName, + required this.content, + required this.createDate, + this.imgList, + this.servicePersonnelImgList, + required this.newReportNum, + this.evaluateLevel, + this.evaluateContent, + this.evaluateDate, + required this.totalCost, + }); +} diff --git a/lib/saas_models/work_order/work_order_detail_model.g.dart b/lib/saas_models/work_order/work_order_detail_model.g.dart new file mode 100644 index 0000000..f78e8ec --- /dev/null +++ b/lib/saas_models/work_order/work_order_detail_model.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'work_order_detail_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +WorkOrderDetailModel _$WorkOrderDetailModelFromJson( + Map json) => + WorkOrderDetailModel( + id: json['id'] as int, + code: json['code'] as String, + status: json['status'] as int, + workOrderTypeName: json['workOrderTypeName'] as String, + reserveAddress: json['reserveAddress'] as String, + reserveDate: json['reserveDate'] as String, + identity: json['identity'] as int, + applicantId: json['applicantId'] as int, + applicantName: json['applicantName'] as String, + applicantTel: json['applicantTel'] as String, + applicantImgList: (json['applicantImgList'] as List?) + ?.map((e) => ImgModel.fromJson(e as Map)) + .toList(), + buildingName: json['buildingName'] as String, + unitName: json['unitName'] as String, + estateName: json['estateName'] as String, + content: json['content'] as String, + createDate: json['createDate'] as String, + imgList: (json['imgList'] as List?) + ?.map((e) => ImgModel.fromJson(e as Map)) + .toList(), + servicePersonnelImgList: + (json['servicePersonnelImgList'] as List?) + ?.map((e) => ImgModel.fromJson(e as Map)) + .toList(), + newReportNum: json['newReportNum'] as int, + evaluateLevel: json['evaluateLevel'] as int?, + evaluateContent: json['evaluateContent'] as String?, + evaluateDate: json['evaluateDate'] as String?, + totalCost: (json['totalCost'] as num).toDouble(), + ); diff --git a/lib/saas_models/work_order/work_order_list_model.dart b/lib/saas_models/work_order/work_order_list_model.dart new file mode 100644 index 0000000..e4a915c --- /dev/null +++ b/lib/saas_models/work_order/work_order_list_model.dart @@ -0,0 +1,54 @@ +import 'package:aku_new_community_manager/models/common/img_model.dart'; +import 'package:common_utils/common_utils.dart'; +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'work_order_list_model.g.dart'; + +@JsonSerializable() +class WorkOrderListModel extends Equatable { + final int id; + final String code; + final int status; + final String workOrderTypeName; + final String reserveAddress; + final String reserveDate; + final String content; + final String updateDate; + final String createDate; + final List? imgList; + + factory WorkOrderListModel.fromJson(Map json) => + _$WorkOrderListModelFromJson(json); + + DateTime? get updateDateDT => DateUtil.getDateTime(updateDate); + + DateTime? get createDateDT => DateUtil.getDateTime(createDate); + + @override + List get props => [ + id, + code, + status, + workOrderTypeName, + reserveAddress, + reserveDate, + content, + updateDate, + createDate, + imgList + ]; + + const WorkOrderListModel({ + required this.id, + required this.code, + required this.status, + required this.workOrderTypeName, + required this.reserveAddress, + required this.reserveDate, + required this.content, + required this.updateDate, + required this.createDate, + this.imgList, + }); +} diff --git a/lib/saas_models/work_order/work_order_list_model.g.dart b/lib/saas_models/work_order/work_order_list_model.g.dart new file mode 100644 index 0000000..3d1a849 --- /dev/null +++ b/lib/saas_models/work_order/work_order_list_model.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'work_order_list_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +WorkOrderListModel _$WorkOrderListModelFromJson(Map json) => + WorkOrderListModel( + id: json['id'] as int, + code: json['code'] as String, + status: json['status'] as int, + workOrderTypeName: json['workOrderTypeName'] as String, + reserveAddress: json['reserveAddress'] as String, + reserveDate: json['reserveDate'] as String, + content: json['content'] as String, + updateDate: json['updateDate'] as String, + createDate: json['createDate'] as String, + imgList: (json['imgList'] as List?) + ?.map((e) => ImgModel.fromJson(e as Map)) + .toList(), + ); diff --git a/lib/saas_models/work_order/work_order_submit_model.dart b/lib/saas_models/work_order/work_order_submit_model.dart new file mode 100644 index 0000000..1384384 --- /dev/null +++ b/lib/saas_models/work_order/work_order_submit_model.dart @@ -0,0 +1,51 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'work_order_submit_model.g.dart'; + +@JsonSerializable(createToJson: true) +class WorkOrderSubmitModel extends Equatable { + final int workOrderId; + final String content; + final List imgUrls; + final WorkOrderFinishCostDTOList workOrderFinishCostDTOList; + + factory WorkOrderSubmitModel.fromJson(Map json) => + _$WorkOrderSubmitModelFromJson(json); + + Map toJson() => _$WorkOrderSubmitModelToJson(this); + + WorkOrderSubmitModel({ + required this.workOrderId, + required this.content, + required this.imgUrls, + required this.workOrderFinishCostDTOList, + }); + + @override + List get props => + [workOrderId, content, imgUrls, workOrderFinishCostDTOList]; +} + +@JsonSerializable(createToJson: true) +class WorkOrderFinishCostDTOList extends Equatable { + final int costType; + final String name; + final int num; + final double price; + + factory WorkOrderFinishCostDTOList.fromJson(Map json) => + _$WorkOrderFinishCostDTOListFromJson(json); + + Map toJson() => _$WorkOrderFinishCostDTOListToJson(this); + + WorkOrderFinishCostDTOList({ + required this.costType, + required this.name, + required this.num, + required this.price, + }); + + @override + List get props => [costType, num, name, price]; +} diff --git a/lib/saas_models/work_order/work_order_submit_model.g.dart b/lib/saas_models/work_order/work_order_submit_model.g.dart new file mode 100644 index 0000000..7a4ce08 --- /dev/null +++ b/lib/saas_models/work_order/work_order_submit_model.g.dart @@ -0,0 +1,45 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'work_order_submit_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +WorkOrderSubmitModel _$WorkOrderSubmitModelFromJson( + Map json) => + WorkOrderSubmitModel( + workOrderId: json['workOrderId'] as int, + content: json['content'] as String, + imgUrls: + (json['imgUrls'] as List).map((e) => e as String).toList(), + workOrderFinishCostDTOList: WorkOrderFinishCostDTOList.fromJson( + json['workOrderFinishCostDTOList'] as Map), + ); + +Map _$WorkOrderSubmitModelToJson( + WorkOrderSubmitModel instance) => + { + 'workOrderId': instance.workOrderId, + 'content': instance.content, + 'imgUrls': instance.imgUrls, + 'workOrderFinishCostDTOList': instance.workOrderFinishCostDTOList, + }; + +WorkOrderFinishCostDTOList _$WorkOrderFinishCostDTOListFromJson( + Map json) => + WorkOrderFinishCostDTOList( + costType: json['costType'] as int, + name: json['name'] as String, + num: json['num'] as int, + price: (json['price'] as num).toDouble(), + ); + +Map _$WorkOrderFinishCostDTOListToJson( + WorkOrderFinishCostDTOList instance) => + { + 'costType': instance.costType, + 'name': instance.name, + 'num': instance.num, + 'price': instance.price, + }; diff --git a/lib/ui/widgets/app_widgets/bee_grid_image_view.dart b/lib/ui/widgets/app_widgets/bee_grid_image_view.dart index bce56e3..a5d0e6a 100644 --- a/lib/ui/widgets/app_widgets/bee_grid_image_view.dart +++ b/lib/ui/widgets/app_widgets/bee_grid_image_view.dart @@ -1,16 +1,18 @@ -import 'package:aku_new_community_manager/const/api.dart'; -import 'package:aku_new_community_manager/const/resource.dart'; +import 'package:aku_new_community_manager/const/saas_api.dart'; +import 'package:aku_new_community_manager/gen/assets.gen.dart'; import 'package:aku_new_community_manager/ui/widgets/inner/bee_image_preview.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class BeeGridImageView extends StatelessWidget { - final List urls; + final List urls; final EdgeInsetsGeometry padding; + final int? crossCount; const BeeGridImageView({ Key? key, required this.urls, this.padding = EdgeInsets.zero, + this.crossCount = 3, }) : super(key: key); @override @@ -19,7 +21,7 @@ class BeeGridImageView extends StatelessWidget { return GridView.builder( padding: padding, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, + crossAxisCount: crossCount!, crossAxisSpacing: 16.w, mainAxisSpacing: 16.w, ), @@ -29,19 +31,15 @@ class BeeGridImageView extends StatelessWidget { BeeImagePreview.toPath(path: urls[index], tag: urls[index]); }, child: Hero( - tag: urls[index], + tag: urls[index]!, child: ClipRRect( borderRadius: BorderRadius.circular(8.w), child: FadeInImage.assetNetwork( height: 184.w, width: 184.w, - placeholder: R.ASSETS_PLACEHOLDER_WEBP, - image: API.image(urls[index]), + placeholder: Assets.placeholder.path, + image: SAASAPI.image(urls[index]), fit: BoxFit.cover, - imageErrorBuilder: (context, error, stackTrace) { - return Image.asset(R.ASSETS_PLACEHOLDER_WEBP,height: 184.w, - width: 184.w,); - }, ), ), ), diff --git a/lib/ui/widgets/common/aku_scaffold.dart b/lib/ui/widgets/common/aku_scaffold.dart index bcf9201..47839fb 100644 --- a/lib/ui/widgets/common/aku_scaffold.dart +++ b/lib/ui/widgets/common/aku_scaffold.dart @@ -1,9 +1,9 @@ // Flutter imports: -import 'package:flutter/material.dart'; - // Project imports: import 'package:aku_new_community_manager/tools/screen_tool.dart'; import 'package:aku_new_community_manager/ui/widgets/common/aku_back_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; ///用法大致同Scaffold @@ -50,7 +50,10 @@ class AkuScaffold extends StatefulWidget { final List actions; + final bool extendBody; + final PreferredSizeWidget? appBarBottom; + AkuScaffold({ Key? key, this.appBar, @@ -64,6 +67,7 @@ class AkuScaffold extends StatefulWidget { this.appBarColor = Colors.white, this.appBarBottom, this.actions = const [], + this.extendBody = false, }) : super(key: key); @override @@ -75,11 +79,14 @@ class _AkuScaffoldState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: widget.backgroundColor, + extendBody: widget.extendBody, + extendBodyBehindAppBar: widget.extendBody, appBar: widget.appBar ?? AppBar( backgroundColor: widget.appBarColor, leading: widget.leading ?? AkuBackButton(), - brightness: widget.brightness, + systemOverlayStyle: + SystemUiOverlayStyle(statusBarBrightness: widget.brightness), elevation: 0, centerTitle: true, title: DefaultTextStyle( diff --git a/lib/ui/widgets/common/bee_hor_image_view.dart b/lib/ui/widgets/common/bee_hor_image_view.dart new file mode 100644 index 0000000..f140068 --- /dev/null +++ b/lib/ui/widgets/common/bee_hor_image_view.dart @@ -0,0 +1,118 @@ +import 'package:aku_new_community_manager/models/common/img_model.dart'; +import 'package:aku_new_community_manager/ui/widgets/app_widgets/bee_image_network.dart'; +import 'package:aku_new_community_manager/ui/widgets/inner/bee_image_preview.dart'; +import 'package:aku_new_community_manager/utils/extension/list_extension.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:velocity_x/src/extensions/string_ext.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class BeeHorImageView extends StatefulWidget { + final EdgeInsets? padding; + final int maxCount; + final List imgs; + final double imgWidth; + final double imgHeight; + final BorderRadiusGeometry? borderRadius; + final int? spacing; + final VoidCallback? onPressed; + + const BeeHorImageView( + {Key? key, + this.padding = EdgeInsets.zero, + required this.maxCount, + required this.imgs, + required this.imgWidth, + required this.imgHeight, + this.borderRadius, + this.spacing, + this.onPressed}) + : super(key: key); + + @override + _BeeHorImageViewState createState() => _BeeHorImageViewState(); +} + +class _BeeHorImageViewState extends State { + bool get overFlow => widget.imgs.length > widget.maxCount; + + int get overCount => overFlow ? (widget.imgs.length - widget.maxCount) : 0; + + @override + Widget build(BuildContext context) { + return Container( + padding: widget.padding, + child: Row( + children: List.generate(widget.imgs.length - overCount, (index) { + if (overFlow && (index == widget.maxCount - 1)) { + return _overFlowWidget(widget.imgs[index]); + } else { + return _imageView(widget.imgs[index]); + } + }).sepWidget(separate: (widget.spacing ?? 10).w.widthBox), + ), + ); + } + + Widget _overFlowWidget(ImgModel img) { + return Stack( + children: [ + Container( + clipBehavior: Clip.antiAliasWithSaveLayer, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: widget.borderRadius ?? BorderRadius.circular(16.w)), + child: BeeImageNetwork( + width: widget.imgWidth, + height: widget.imgHeight, + imgs: [img], + ), + ), + GestureDetector( + onTap: widget.onPressed, + child: Container( + width: widget.imgWidth, + height: widget.imgHeight, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: widget.borderRadius ?? BorderRadius.circular(16.w), + ), + alignment: Alignment.center, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.image, + size: 24.w, + color: Colors.white, + ), + 8.w.widthBox, + '${overCount + 1}'.text.size(28.sp).color(Colors.white).make(), + ], + ), + ), + ) + ], + ); + } + + Widget _imageView(ImgModel img) { + return GestureDetector( + onTap: () { + BeeImagePreview.toPath(path: img.url); + }, + child: Container( + clipBehavior: Clip.antiAliasWithSaveLayer, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: widget.borderRadius ?? BorderRadius.circular(16.w)), + child: BeeImageNetwork( + width: widget.imgWidth, + height: widget.imgHeight, + imgs: [img], + ), + ), + ); + } +} diff --git a/lib/ui/widgets/common/bee_long_button.dart b/lib/ui/widgets/common/bee_long_button.dart new file mode 100644 index 0000000..7a1a569 --- /dev/null +++ b/lib/ui/widgets/common/bee_long_button.dart @@ -0,0 +1,36 @@ +import 'package:aku_new_community_manager/style/app_style.dart'; +import 'package:flutter/material.dart'; +import 'package:velocity_x/src/extensions/string_ext.dart'; + +class BeeLongButton extends StatelessWidget { + final VoidCallback? onPressed; + final String text; + final Color backColor; + final BorderSide border; + + const BeeLongButton({Key? key, required this.onPressed, required this.text}) + : backColor = kPrimaryColor, + border = BorderSide.none, + super(key: key); + + BeeLongButton.white({Key? key, required this.onPressed, required this.text}) + : backColor = Colors.white, + border = BorderSide(color: Colors.black.withOpacity(0.45)), + super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialButton( + elevation: 0, + height: 93.w, + disabledColor: Colors.black.withOpacity(0.06), + disabledTextColor: Colors.black.withOpacity(0.25), + textColor: Colors.black.withOpacity(0.85), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(65.w), side: border), + color: backColor, + onPressed: onPressed, + child: text.text.size(32.sp).bold.make(), + ); + } +} diff --git a/lib/ui/widgets/common/stack_avatar.dart b/lib/ui/widgets/common/stack_avatar.dart new file mode 100644 index 0000000..b11b94f --- /dev/null +++ b/lib/ui/widgets/common/stack_avatar.dart @@ -0,0 +1,43 @@ +import 'package:aku_new_community_manager/ui/widgets/app_widgets/bee_image_network.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class StackAvatar extends StatelessWidget { + final List avatars; + + const StackAvatar({Key? key, required this.avatars}) : super(key: key); + + double get offset => 35.w; + + int get length => avatars.length; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + SizedBox( + width: 44.w * 2 + 26.w, + height: 44.w + 6.w, + ), + ...List.generate(length, (index) { + return Positioned( + left: index * offset, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(22.w + 2.w), + // border: Border.all(color: Color(0xFF999999)), + ), + clipBehavior: Clip.antiAlias, + child: BeeImageNetwork( + width: 44.w, + height: 44.w, + urls: [avatars[index] ?? ''], + ), + ), + ); + }), + ], + ); + } +} diff --git a/lib/ui/widgets/inner/bee_image_preview.dart b/lib/ui/widgets/inner/bee_image_preview.dart index f6aa794..b683b50 100644 --- a/lib/ui/widgets/inner/bee_image_preview.dart +++ b/lib/ui/widgets/inner/bee_image_preview.dart @@ -1,10 +1,11 @@ import 'dart:io'; -import 'package:aku_new_community_manager/const/api.dart'; -import 'package:aku_new_community_manager/const/resource.dart'; +import 'package:aku_new_community_manager/const/saas_api.dart'; +import 'package:aku_new_community_manager/gen/assets.gen.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +///图片点击放大预览 class BeeImagePreview extends StatefulWidget { static Future toFile({required File file, String? tag}) async { return await navigator!.push( @@ -45,6 +46,7 @@ class BeeImagePreview extends StatefulWidget { final File? file; final String? path; final String? tag; + BeeImagePreview.file({Key? key, required this.file, this.tag}) : path = null, super(key: key); @@ -63,8 +65,14 @@ class _BeeImagePreviewState extends State { return Hero( tag: widget.tag ?? widget.path!, child: FadeInImage.assetNetwork( - placeholder: R.ASSETS_PLACEHOLDER_WEBP, - image: API.image(widget.path!), + placeholder: Assets.placeholder.path, + image: SAASAPI.image(widget.path), + imageErrorBuilder: (context, obj, stackTrace) { + return Image.asset( + Assets.placeholder.path, + fit: BoxFit.fill, + ); + }, ), ); else @@ -89,4 +97,4 @@ class _BeeImagePreviewState extends State { ), ); } -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index ebe189f..e59dbc5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,7 @@ flutter: - assets/manage/ - assets/inspection/ - assets/outdoor/ + - assets/icons/ - assets/static_temp/ - assets/static_fix/