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 createState() => _ApplyRepairPageState(); } class _ApplyRepairPageState extends State { final ThemeController themeController = Get.find(); DeviceTypeController deviceTypeController = Get.find(); RepairController repairController = Get.find(); BodyDeviceController bodyDeviceController = Get.find(); final List 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 _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(); } 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( mainAxisSize: MainAxisSize.max, children: [ Obx(() { return SingleChildScrollView( scrollDirection: Axis.horizontal, // 横向滚动 child: Row( mainAxisSize: MainAxisSize.min, // 设置为 min,避免撑满父组件宽度 children: deviceTypeController.deviceTypeList.value .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: 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, ); }); } }