Files
tuiche/lib/pages/repair/apply_repair_page.dart
2025-11-14 12:01:07 +08:00

583 lines
24 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/device/body_device_controller.dart';
import 'package:vbvs_app/controller/device/device_type_controller.dart';
import 'package:vbvs_app/controller/repair/repair_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/repair/component/RepairModelWidget.dart';
class ApplyRepairPage extends StatefulWidget {
const ApplyRepairPage({super.key});
@override
State<ApplyRepairPage> createState() => _ApplyRepairPageState();
}
class _ApplyRepairPageState extends State<ApplyRepairPage> {
final ThemeController themeController = Get.find();
DeviceTypeController deviceTypeController = Get.find();
RepairController repairController = Get.find();
BodyDeviceController bodyDeviceController = Get.find();
final List<GlobalKey> repairItemKeys = [];
final GlobalKey contactKey = GlobalKey();
final GlobalKey phoneKey = GlobalKey();
@override
void initState() {
super.initState();
_loadData();
repairItemKeys.addAll(List.generate(
repairController.repairList.length, (index) => GlobalKey()));
}
void _updateRepairItemKeys() {
try {
final int currentLength = repairController.repairList.length;
if (repairItemKeys.length != currentLength) {
repairItemKeys.clear();
repairItemKeys.addAll(
List.generate(currentLength, (index) => GlobalKey()),
);
}
} catch (e) {
print(e);
}
}
Future<void> _loadData() async {
repairController.repairList = [{}].obs;
repairController.name.value = "";
repairController.phone.value = "";
await deviceTypeController.getDeviceType(); // 等待数据加载
if (deviceTypeController.deviceTypeList.isNotEmpty) {
repairController.device_type.value =
deviceTypeController.deviceTypeList.first['type'];
}
await bodyDeviceController.getDeviceList();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateRepairItemKeys();
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
if (deviceTypeController.deviceTypeList.isNotEmpty) {
repairController.device_type.value =
deviceTypeController.deviceTypeList.first['type'];
}
if (bodyDeviceController.deviceList.isNotEmpty) {
repairController.deviceListId = bodyDeviceController.deviceList
.map((e) => (e['_id']) as String)
.toList();
repairController.deviceListIdLabel = bodyDeviceController.deviceList
.map((e) => (e['code'] ?? e['mac']) as String)
.toList();
}
return LayoutBuilder(
builder: (context, bodysize) => GestureDetector(
// onTap: () => FocusScope.of(context).unfocus(),,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: themeController.currentColor.sc3,
),
titleSpacing: 0,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
/// 居中标题
Text(
'设备报修'.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
Positioned(
left: 0,
// child: returnIconButtom,
child: returnIconButtomAddCallback(() {}),
),
Positioned(
right: 20.rpx,
child: ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc16,
padding: EdgeInsets.all(8.rpx),
onTap: () {
Get.toNamed("/repairListPage");
},
child: SvgPicture.asset(
'assets/img/icon/history.svg',
width: 39.rpx,
height: 39.rpx,
color: themeController.currentColor.sc16,
),
),
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(30.rpx, 29.rpx, 30.rpx, 0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Obx(() {
var devicetype = deviceTypeController
.deviceTypeList.value
.where((device) => device['type'] == 1)
.toList();
return SingleChildScrollView(
scrollDirection: Axis.horizontal, // 横向滚动
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min, // 设置为 min避免撑满父组件宽度
children: devicetype
.map(
(deviceType) => CustomCard(
borderRadius:
AppConstants().button_container_radius,
onTap: () async {
repairController.device_type.value =
deviceType['type'];
repairController.repairList
.clear(); // 清空旧数据
repairController.repairList
.add({}); // 添加新数据
await bodyDeviceController
.getDeviceList(); // 等待数据加载
_updateRepairItemKeys(); // 清空后更新 keys
repairController.updateAll(); // 手动触发更新
},
colors: deviceType['type'] ==
repairController.device_type.value
? [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
]
: [themeController.currentColor.sc5],
child: Padding(
padding: EdgeInsets.fromLTRB(
32.rpx, 0, 32.rpx, 0),
child: Container(
// width: (MediaQuery.sizeOf(context).width *
// 0.284)
// .rpx,
constraints: BoxConstraints(
// minWidth: 213.rpx,
minHeight: 91.rpx,
),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(20.rpx),
),
child: Align(
alignment: AlignmentDirectional(0, 0),
child: Text(
deviceType['name'],
style: TextStyle(
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
fontSize: AppConstants()
.normal_text_fontSize,
),
),
),
),
),
),
)
.toList()
.divide(SizedBox(width: 25.rpx)),
),
);
}),
Obx(() {
return Column(
children: repairController.repairList
.map((item) {
final int index =
repairController.repairList.indexOf(item);
return RepairModelWidget(
widgetKey:
repairItemKeys[index], // 确保 index 有效
model: item,
onTap: () {
repairController.repairList.remove(item);
_updateRepairItemKeys(); // 删除后更新 keys
},
length: repairController.repairList.length,
);
})
.toList()
.divide(SizedBox(
height: 25.rpx,
)),
);
}),
ClickableContainer(
backgroundColor: themeController.currentColor.sc5,
highlightColor: themeController.currentColor.sc21,
borderRadius: 20.rpx,
padding: EdgeInsets.zero,
onTap: () {
repairController.repairList.add({});
_updateRepairItemKeys(); // 初始化 keys
repairController.updateAll();
},
child: SizedBox(
width: double.infinity,
height: 90.rpx,
child: Center(
child: Image.asset(
"assets/img/addItem.png",
width: 39.rpx,
height: 39.rpx,
),
),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(
color: themeController.currentColor.sc5,
borderRadius: BorderRadius.circular(
AppConstants().normal_container_radius),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 30.rpx, 30.rpx, 30.rpx),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
_buildParamRow(
context,
"联系人".tr,
"名称输入提示".tr,
(value) {
repairController.name.value = value;
},
key: contactKey,
),
_buildParamRow(
context,
"手机号".tr,
"手机号输入提示".tr,
(value) {
repairController.phone.value = value;
},
key: phoneKey,
),
].divide(SizedBox(height: 30.rpx)),
),
),
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
100.rpx, 0.rpx, 100.rpx, 60.rpx),
child: CustomCard(
borderRadius:
AppConstants().button_container_radius, // 圆角半径
onTap: () async {
// TopSlideNotification.show(context,
// text: "提交成功",
// textColor: themeController.currentColor.sc1);
// Future.delayed(const Duration(seconds: 1), () {
// Get.offAllNamed("/mianPageBottomChange");
// });
String msg = checkRepairParam();
if (msg.isNotEmpty) {
TopSlideNotification.show(context,
text: msg,
textColor: themeController.currentColor.sc9);
} else {
String serviceAddress =
ServiceConstant.service_address;
String serviceName =
ServiceConstant.server_service;
String serviceApi = ServiceConstant.submit_repair;
String queryUrl =
"$serviceAddress$serviceName$serviceApi";
var data = {
"device": repairController.repairList.value,
"type": repairController.device_type.value,
"contacts": {
"name": repairController.name.value,
"phone": repairController.phone.value
},
};
ApiResponse apiResponse = await requestWithLog(
logTitle: "提交报修信息",
method: MyHttpMethod.post,
queryUrl: queryUrl,
data: data,
onSuccess: (res) {
TopSlideNotification.show(context,
text: res.msg!);
// Get.back();
Get.toNamed("/applyRepairSuccess");
},
onFailure: (res) {
TopSlideNotification.show(context,
text: res.msg!,
textColor:
themeController.currentColor.sc9);
},
);
}
},
colors: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
child: Container(
color: Colors.transparent,
width: MediaQuery.sizeOf(context).width * 0.66,
height: MediaQuery.sizeOf(context).height * 0.055,
constraints: BoxConstraints(
minWidth: 500.rpx,
minHeight: 90.rpx,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'提交'.tr,
style: TextStyle(
color: themeController.currentColor.sc3,
fontFamily: 'Inter',
fontSize:
AppConstants().normal_text_fontSize,
letterSpacing: 0.0,
),
),
].divide(SizedBox(
width: 17.rpx,
)),
),
),
),
),
]
.divide(SizedBox(
height: 25.rpx,
))
.addToEnd(SizedBox(
height: 25.rpx,
)),
),
),
),
),
),
),
),
);
}
Widget _buildParamRow(BuildContext context, String text, String hinttext,
void Function(String) onChanged,
{Key? key} // 新增可选参数
) {
return Row(
key: key, // 使用传入的 key
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 110.rpx,
child: Text(
text,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
),
overflow: TextOverflow.ellipsis,
softWrap: false,
maxLines: 1,
),
),
Expanded(
child: Container(
width: 200.rpx,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.rpx),
color: Colors.transparent,
),
child: TextFormField(
autofocus: false,
obscureText: false,
decoration: InputDecoration(
isDense: true,
labelStyle: TextStyle(
letterSpacing: 0.0,
// fontWeight:
// FlutterFlowTheme.of(context).labelMedium.fontWeight,
// fontStyle: FlutterFlowTheme.of(context).labelMedium.fontStyle,
),
hintText: hinttext,
hintStyle: TextStyle(
letterSpacing: 0.0,
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc4,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1.rpx,
),
borderRadius: BorderRadius.circular(
AppConstants().normal_container_radius),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
width: 1.rpx,
),
borderRadius: BorderRadius.circular(8.rpx),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
//
width: 1.rpx,
),
borderRadius: BorderRadius.circular(8.rpx),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
//
width: 1.rpx,
),
borderRadius: BorderRadius.circular(8.rpx),
),
filled: true,
fillColor: themeController.currentColor.sc15,
),
style: TextStyle(
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
fontSize: AppConstants().normal_text_fontSize,
),
cursorColor: themeController.currentColor.sc3,
onChanged: onChanged,
),
),
),
].divide(SizedBox(width: 24.rpx)),
);
}
String checkRepairParam() {
String errormsg = "";
// 检查报修项
for (var i = 0; i < repairController.repairList.length; i++) {
final item = repairController.repairList[i];
if (item["path"] == null || item["path"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项图片未上传";
return "图片未上传".tr;
}
if (item["id"] == null || item["id"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项设备未选择";
return "设备未选择".tr;
}
if (item["param"] == null || item["param"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项参数未填写";
return "参数未填写".tr;
}
if (item["issue"] == null || item["issue"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项问题描述未填写";
return "问题描述未填写".tr;
}
if (item["issue"] != null && item["issue"].toString().trim().isNotEmpty) {
final issueText = item["issue"].toString().trim();
final charCount = issueText.runes.length;
if (charCount > 60) {
_scrollToKey(repairItemKeys[i]);
return "问题描述最多输入60个字".tr;
}
} else {
_scrollToKey(repairItemKeys[i]);
return "问题描述不能为空".tr;
}
}
// 检查联系人
if (repairController.name.trim().isEmpty) {
_scrollToKey(contactKey);
return "联系人不能为空".tr;
}
if (repairController.phone.trim().isEmpty) {
_scrollToKey(phoneKey);
return "联系电话不能为空".tr;
}
if (!MyUtils.isValidPhoneNumber(repairController.phone.trim())) {
_scrollToKey(phoneKey);
return "无效的手机号码".tr;
}
return errormsg;
}
// 新增滚动方法
void _scrollToKey(GlobalKey key) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
}
}