diff --git a/android/app/src/main/kotlin/com/qysz/qgxgf/MainActivity.kt b/android/app/src/main/kotlin/com/qysz/qgxgf/MainActivity.kt new file mode 100644 index 0000000..ad6b739 --- /dev/null +++ b/android/app/src/main/kotlin/com/qysz/qgxgf/MainActivity.kt @@ -0,0 +1,123 @@ +package com.qysz.qgxgf + +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.core.content.FileProvider +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import java.io.File + +class MainActivity: FlutterActivity() { + private val CHANNEL = "app.install" + private val REQ_INSTALL_UNKNOWN = 9999 + + // 暂存安装请求(仅在跳转设置并等待返回时使用) + private var pendingApkPath: String? = null + private var pendingResult: MethodChannel.Result? = null + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> + when (call.method) { + "installApk" -> { + val path = call.argument("path") + if (path == null) { + result.error("NO_PATH", "no path provided", null) + return@setMethodCallHandler + } + handleInstallRequest(path, result) + } + else -> result.notImplemented() + } + } + } + + private fun handleInstallRequest(path: String, result: MethodChannel.Result) { + val file = File(path) + if (!file.exists()) { + result.error("NO_FILE", "file not exist", null) + return + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // 8.0+ 需要 app 级别未知来源授权 + if (!packageManager.canRequestPackageInstalls()) { + // 存储请求信息以便用户返回后继续 + pendingApkPath = path + pendingResult = result + + val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, + Uri.parse("package:$packageName")) + // 使用 startActivityForResult 以便用户返回后可以继续安装 + startActivityForResult(intent, REQ_INSTALL_UNKNOWN) + return + } + } + // 已有授权 或 非 8.0+:直接安装 + installApkInternal(path, result) + } + + // 真正执行安装的函数(假定有权限) + private fun installApkInternal(path: String, result: MethodChannel.Result) { + val file = File(path) + if (!file.exists()) { + result.error("NO_FILE", "file not exist", null) + return + } + + try { + val apkUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", file) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(apkUri, "application/vnd.android.package-archive") + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + startActivity(intent) + result.success(true) + } catch (e: Exception) { + result.error("INSTALL_FAILED", e.message, null) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQ_INSTALL_UNKNOWN) { + // 用户从系统设置页返回后,检查是否已授权 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (packageManager.canRequestPackageInstalls()) { + // 授权已开:继续安装 + val path = pendingApkPath + val res = pendingResult + // 清理 pending 状态 + pendingApkPath = null + pendingResult = null + if (path != null && res != null) { + installApkInternal(path, res) + } else { + // 安全兜底:若没有 pending 数据,通知 caller 重新触发 + res?.error("NO_PENDING", "no pending install info", null) + } + } else { + // 用户仍未授权 + pendingApkPath = null + pendingResult?.error("NEED_INSTALL_PERMISSION", "user did not allow install unknown apps", null) + pendingResult = null + } + } else { + // API < 26:尝试直接安装一次作为尝试(某些 ROM 无法精准判断) + val path = pendingApkPath + val res = pendingResult + pendingApkPath = null + pendingResult = null + if (path != null && res != null) { + installApkInternal(path, res) + } else { + res?.error("NO_PENDING", "no pending install info", null) + } + } + } + } +} diff --git a/lib/constants/app_enums.dart b/lib/constants/app_enums.dart index d0fbf09..fd25d85 100644 --- a/lib/constants/app_enums.dart +++ b/lib/constants/app_enums.dart @@ -394,6 +394,8 @@ enum UploadFileType { '302', 'fire_safety_inspection_passed_images', ), + // /// 门口门禁签字照片 - 类型: '303', 路径: 'gate_access_vehicle_signature_photo' + // gateAccessVehicleSignaturePhoto('303', 'gate_access_vehicle_signature_photo'), /// 门口门禁车辆行驶证照片 - 类型: '601', 路径: 'gate_access_vehicle_license_photo' gateAccessVehicleLicensePhoto('601', 'gate_access_vehicle_license_photo'), @@ -404,14 +406,28 @@ enum UploadFileType { /// 门口门禁车辆附件 - 类型: '603', 路径: 'gate_access_vehicle_attachment' gateAccessVehicleAttachment('603', 'gate_access_vehicle_attachment'), - /// 排放标准证明 - 类型: '604', 路径: 'emission_standard_certificate' - emissionStandardCertificate('604', 'emission_standard_certificate'), + // /// 排放标准证明 - 类型: '604', 路径: 'emission_standard_certificate' + // emissionStandardCertificate('604', 'emission_standard_certificate'), /// 机动车登记证书(绿本) - 类型: '605', 路径: 'motor_vehicle_registration_certificate_green_book' motorVehicleRegistrationCertificateGreenBook( '605', 'motor_vehicle_registration_certificate_green_book', - ); + ), + + /// 门口门禁人员申请人签字 - 类型: '611', 路径: 'gate_access_personnel_applicant_signature' + gateAccessPersonnelApplicantSignature('611', 'gate_access_personnel_applicant_signature'), + /// 门口门禁车辆申请人签字 - 类型: '606', 路径: 'gate_access_vehicle_applicant_signature' + gateAccessVehicleApplicantSignature('606', 'gate_access_vehicle_applicant_signature'), + /// 封闭区域人员申请人签字 - 类型: '609', 路径: 'enclosed_area_personnel_applicant_signature' + enclosedAreaPersonnelApplicantSignature('609', 'enclosed_area_personnel_applicant_signature'), + /// 封闭区域车辆申请人签字 - 类型: '610', 路径: 'enclosed_area_vehicle_applicant_signature' + enclosedAreaVehicleApplicantSignature('610', 'enclosed_area_vehicle_applicant_signature'); + + + + + const UploadFileType(this.type, this.path); diff --git a/lib/customWidget/department_picker_enterprise.dart b/lib/customWidget/department_picker_enterprise.dart new file mode 100644 index 0000000..64ef987 --- /dev/null +++ b/lib/customWidget/department_picker_enterprise.dart @@ -0,0 +1,252 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; +import '../tools/tools.dart'; // 包含 SessionService + +// 数据模型 +class CategoryEnterpriseType { + final String id; + final String name; + final String pdId; + final List children; + + CategoryEnterpriseType({ + required this.id, + required this.name, + required this.pdId, + this.children = const [], + }); + + factory CategoryEnterpriseType.fromJson(Map json) { + return CategoryEnterpriseType( + id: json['jurisdictionalCorpId'] != null ? json['jurisdictionalCorpId'].toString() : "", + name: json['jurisdictionalCorpName'] != null ? json['jurisdictionalCorpName'].toString() : "", + pdId: json['parentId'] != null ? json['parentId'].toString() : "", + children: _safeParseChildren(json['childrenList']), + ); + } + + static List _safeParseChildren(dynamic childrenData) { + if (childrenData == null) return []; + if (childrenData is! List) return []; + + final List children = []; + for (var item in childrenData) { + if (item is Map) { + try { + children.add(CategoryEnterpriseType.fromJson(item)); + } catch (e) { + print('解析子项失败: $e'); + } + } + } + return children; + } +} + + + +/// 弹窗回调签名:返回选中项的 id 和 name +typedef DeptSelectCallback = void Function(String id, String name,String pdId); + +class DepartmentPickerEnterprise extends StatefulWidget { + /// 回调,返回选中部门 id 与 name + final DeptSelectCallback onSelected; + ///港区id + final String jurisdictionalAuthorityId; + + const DepartmentPickerEnterprise(this.jurisdictionalAuthorityId,{Key? key, required this.onSelected}) : super(key: key); + + @override + _DepartmentPickerEnterpriseState createState() => _DepartmentPickerEnterpriseState(); +} + +class _DepartmentPickerEnterpriseState extends State { + String selectedId = ''; + String selectedPDId = ''; + String selectedName = ''; + Set expandedSet = {}; + + List original = []; + List filtered = []; + bool loading = true; + + final TextEditingController _searchController = TextEditingController(); + + @override + void initState() { + super.initState(); + // 初始均为空 + selectedId = ''; + selectedName = ''; + selectedPDId = ''; + expandedSet = {}; + _searchController.addListener(_onSearchChanged); + _loadData(); + } + + @override + void dispose() { + _searchController.removeListener(_onSearchChanged); + _searchController.dispose(); + super.dispose(); + } + + Future _loadData() async { + try { + List raw; + // if (SessionService.instance.departmentJsonStr?.isNotEmpty ?? false) { + // raw = json.decode(SessionService.instance.departmentJsonStr!) as List; + // } else { + // final result = await HiddenDangerApi.getHiddenTreatmentListTree(); + // final String nodes = result['data'] as String; + // SessionService.instance.departmentJsonStr = nodes; + // raw = result['data']; + // } + + final result = await DoorAndCarApi.getJurisdictionalAuthorityListTree(widget.jurisdictionalAuthorityId); + raw = result['data']; + + setState(() { + original = raw.map((e) => CategoryEnterpriseType.fromJson(e as Map)).toList(); + filtered = original; + loading = false; + }); + } catch (e) { + setState(() => loading = false); + } + } + + void _onSearchChanged() { + final query = _searchController.text.toLowerCase().trim(); + setState(() { + filtered = query.isEmpty ? original : _filterCategories(original, query); + }); + } + + List _filterCategories(List list, String query) { + List result = []; + for (var cat in list) { + final children = _filterCategories(cat.children, query); + if (cat.name.toLowerCase().contains(query) || children.isNotEmpty) { + result.add(CategoryEnterpriseType(id: cat.id, name: cat.name,pdId:cat.pdId, children: children)); + } + } + return result; + } + + Widget _buildRow(CategoryEnterpriseType cat, int indent) { + final hasChildren = cat.children.isNotEmpty; + final isExpanded = expandedSet.contains(cat.id); + final isSelected = cat.id == selectedId; + return Column( + children: [ + InkWell( + onTap: () { + setState(() { + if (hasChildren) { + isExpanded ? expandedSet.remove(cat.id) : expandedSet.add(cat.id); + selectedPDId=cat.pdId; + }else{ + selectedPDId=cat.id; + } + selectedId = cat.id; + selectedName = cat.name; + + }); + }, + child: Container( + color: Colors.white, + child: Row( + children: [ + SizedBox(width: 16.0 * indent), + SizedBox( + width: 24, + child: hasChildren + ? Icon(isExpanded ? Icons.arrow_drop_down_rounded : Icons.arrow_right_rounded, + size: 35, color: Colors.grey[600]) + : const SizedBox.shrink(), + ), + const SizedBox(width: 5), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Text(cat.name), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Icon( + isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, + color: Colors.blue, + ), + ), + ], + ), + ), + ), + if (hasChildren && isExpanded) + ...cat.children.map((c) => _buildRow(c, indent + 1)), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.7, + color: Colors.white, + child: Column( + children: [ + Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: const Text('取消', style: TextStyle(fontSize: 16)), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: SearchBarWidget( + controller: _searchController, + isShowSearchButton: false, + onSearch: (keyboard) { + + }, + ), + ), + ), + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + widget.onSelected(selectedId, selectedName,selectedPDId); + }, + child: const Text('确定', style: TextStyle(fontSize: 16, color: Colors.blue)), + ), + ], + ), + ), + Divider(), + Expanded( + child: loading + ? const Center(child: CircularProgressIndicator()) + : Container( + color: Colors.white, + child: ListView.builder( + itemCount: filtered.length, + itemBuilder: (ctx, idx) => _buildRow(filtered[idx], 0), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/customWidget/department_picker_two.dart b/lib/customWidget/department_picker_two.dart index 6e69073..c9d7676 100644 --- a/lib/customWidget/department_picker_two.dart +++ b/lib/customWidget/department_picker_two.dart @@ -54,8 +54,10 @@ typedef DeptSelectCallback = void Function(String id, String name,String pdId); class DepartmentPickerTwo extends StatefulWidget { /// 回调,返回选中部门 id 与 name final DeptSelectCallback onSelected; + /// 预选企业ID(非必填) + final String id; - const DepartmentPickerTwo({Key? key, required this.onSelected}) : super(key: key); + const DepartmentPickerTwo({Key? key, required this.onSelected, this.id = '', }) : super(key: key); @override _DepartmentPickerTwoState createState() => _DepartmentPickerTwoState(); @@ -104,7 +106,7 @@ class _DepartmentPickerTwoState extends State { // raw = result['data']; // } - final result = await HiddenDangerApi.getHiddenTreatmentListTree(); + final result = await HiddenDangerApi.getHiddenTreatmentListTree(widget.id); raw = result['data']; setState(() { diff --git a/lib/customWidget/item_list_widget.dart b/lib/customWidget/item_list_widget.dart index 7f9154d..4cb7789 100644 --- a/lib/customWidget/item_list_widget.dart +++ b/lib/customWidget/item_list_widget.dart @@ -1523,4 +1523,120 @@ class ItemListWidget { }, ); } + + + /// 单行水平排列:(填充数据可根据TextEditingController改变) + /// - 可编辑时:标题 + TextField + /// - 不可编辑时:标题 + 带省略号的文本 + + static Widget singleLineTitleTextTwo({ + required String label, // 标题文本 + required bool isEditable, // 是否可编辑 + String? text, // 显示的初始文本(编辑/非编辑模式都会显示) + TextEditingController? controller, // 新增:可选的控制器 + String hintText = '请输入', + double fontSize = 14, // 字体大小 + bool isRequired = true, + bool strongRequired = false, + ValueChanged? onChanged, + ValueChanged? onFieldSubmitted, + int maxLines = 5, + bool showMaxLength = false, + + // 新增:数字输入控制 + bool isNumericInput = false, + int maxDecimalPlaces = 2, + TextInputType keyboardType = TextInputType.text, + // 新增:控制文字前后 + bool isTextFont = true, + }) { + // 数字输入键盘 + final actualKeyboardType = + isNumericInput ? const TextInputType.numberWithOptions(decimal: true) : keyboardType; + + // 数字输入格式化器 + final List? numericFormatters = isNumericInput + ? [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]')), + TextInputFormatter.withFunction((oldValue, newValue) { + final newText = newValue.text; + + if (newText.isEmpty) return newValue; + + if (newText.split('.').length > 2) return oldValue; + + final regex = RegExp(r'^\d*\.?\d{0,' + maxDecimalPlaces.toString() + r'}$'); + + if (regex.hasMatch(newText)) return newValue; + + return oldValue; + }), + ] + : null; + + return Container( + padding: const EdgeInsets.symmetric( + vertical: vertical_inset, + horizontal: horizontal_inset, + ), + child: Row( + mainAxisAlignment: isEditable ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if ((isRequired && isEditable) || strongRequired) + Text('* ', style: TextStyle(color: Colors.red)), + Text( + label, + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + const SizedBox(width: 8), + + /// 可编辑模式 + isEditable + ? Expanded( + child: TextFormField( + controller: controller, + textAlign: isTextFont?TextAlign.start:TextAlign.end, + initialValue: controller == null ? text : null, // 关键修改:如果有controller,就不传text + autofocus: false, + onChanged: onChanged, + onFieldSubmitted: onFieldSubmitted, + keyboardType: actualKeyboardType, + maxLength: showMaxLength ? 120 : null, + style: TextStyle(fontSize: fontSize), + maxLines: 1, + inputFormatters: numericFormatters, + decoration: InputDecoration( + isDense: true, + hintText: hintText, + contentPadding: EdgeInsets.symmetric(vertical: 8), + ), + ), + ) + + /// 只读模式 + : Expanded( + child: Text( + text ?? '', + maxLines: maxLines, + style: TextStyle(fontSize: fontSize, color: detailtextColor), + textAlign: TextAlign.right, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } + + + + + } diff --git a/lib/http/modules/basic_info_api.dart b/lib/http/modules/basic_info_api.dart index 36da5f5..6985658 100644 --- a/lib/http/modules/basic_info_api.dart +++ b/lib/http/modules/basic_info_api.dart @@ -221,3 +221,17 @@ class CertificateApi { } } + +// 待办事项 +class TodoApi { + static Future> getTodoList(Map data) { + return HttpManager().request( + ApiService.basePath + '/appmenu', + '/todoList/list', + method: Method.post, + data: { + ...data + }, + ); + } +} diff --git a/lib/http/modules/doorAndCar_api.dart b/lib/http/modules/doorAndCar_api.dart new file mode 100644 index 0000000..36ffb05 --- /dev/null +++ b/lib/http/modules/doorAndCar_api.dart @@ -0,0 +1,242 @@ +import 'package:dio/dio.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/http/HttpManager.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; + +class DoorAndCarApi { + + ///////=======一级口门人 + /// 人员-相关方人员入场申请列表 + static Future> getXgfPersonAuditList(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/personApply/xgfPersonApplyList', + method: Method.post, + data: { + ...data + }, + ); + } + + + /// 人员-相关方-人员申请-详情 + static Future> getXgfAuditInfoById(String id) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/personApply/xgfPersonApplyInfoById/$id', + method: Method.get, + data: { + // "id": id, + // ...data , + }, + ); + } + + /// 人员-获取本公司的项目列表 + static Future> getProjectNameList() { + return HttpManager().request( + '${ApiService.basePath}', + '/xgfManager/project/listAllPassedBySelfCorp', + method: Method.get, + data: { + // ...data , + }, + ); + } + + + /// 人员-获取项目里的人列表 + static Future> getPeopleinProject(String id) { + return HttpManager().request( + '${ApiService.basePath}', + '/xgfManager/projectUser/getPeopleinProject/$id', + method: Method.get, + data: { + // "id": id, + // ...data , + }, + ); + } + + + /// 审批人所有数据 + static Future> getApproverList(String corpId,String deptId, + String personnelPermissionFlag, //人员审核权限(1-无权限,2-有权限) + String vehiclePermissionFlag,//车辆审核权限(1-无权限,2-有权限) + String temporaryPermissionFlag,//临时审核权限(1-无权限,2-有权限) + ) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/mkmjApprovalUser/listAll?corpId=$corpId' + '&personnelPermissionFlag=$personnelPermissionFlag' + '&vehiclePermissionFlag=$vehiclePermissionFlag' + '&temporaryPermissionFlag=$temporaryPermissionFlag', + method: Method.get, + data: { + "zgdw-mjspr": '/primeport/container/stakeholder/firstLevelDoor/approverUser', + // ...data + }, + ); + } + + + /// 人员-相关方人员入场申请列表 + static Future> xgfPersonSave(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/personApply/xgfPersonSave', + method: Method.post, + data: { + ...data + }, + ); + } + + +///////=======一级口门车 + /// 车辆-相关方提上来的车辆申请 + static Future> getXgfCarAuditList(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/vehicleAudit/pendingApprovalList', + method: Method.post, + data: { + ...data + }, + ); + } + + /// 股份、相关方车辆审批-详情 + static Future> getLevelCarInfoById(String id) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/vehicleApply/$id', + method: Method.get, + data: { + // "id": id, + // ...data , + }, + ); + } + + /// 相关方车辆录入 + static Future> levelCarSave(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/vehicleApply/save', + method: Method.post, + data: { + ...data + }, + ); + } + + /////=========封闭区域人 + /// 人员-待审批-已审批|申请中-申请记录分页 + static Future> getEnclosedPersonList(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedAreaPersonApply/pendingApprovalRecordList', + method: Method.post, + data: { + ...data + }, + ); + } + + /// 人员-详情 + static Future> getEnclosedPersonById(String id) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedAreaPersonApply/$id', + method: Method.get, + data: { + // "id": id, + // ...data , + }, + ); + } + + /// 人员-新增 + static Future> enclosedPersonSave(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedAreaPersonApply/save', + method: Method.post, + data: { + ...data + }, + ); + } + + + /////=========封闭区域车 + /// 人员-待审批-已审批|申请中-申请记录分页 + static Future> getEnclosedCarList(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedAreaCarApply/list', + method: Method.post, + data: { + ...data + }, + ); + } + + /// 车辆-详情 + static Future> getEnclosedCarById(String id) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedAreaCarApply/$id', + method: Method.get, + data: { + // "id": id, + // ...data , + }, + ); + } + + /// 车辆-新增 + static Future> enclosedAreaCarSave(Map data) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedAreaCarApply/save', + method: Method.post, + data: { + ...data + }, + ); + } + + + + + /// 跟据管辖单位查询封闭区域 + static Future> getEnclosedAreaById(String id) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedArea/listAllByJurisdictionalCorpId/$id', + method: Method.get, + data: { + // "id": id, + // ...data , + }, + ); + } + + + /// 跟据港区查询管辖单位 + static Future> getJurisdictionalAuthorityListTree(String id) { + return HttpManager().request( + '${ApiService.basePath}/primeport', + '/closedArea/listAllByhgAuthArea?hgAuthArea=$id', + method: Method.get, + data: { + + }, + ); + } + + + +} \ No newline at end of file diff --git a/lib/http/modules/hidden_danger_api.dart b/lib/http/modules/hidden_danger_api.dart index 080aece..9815eab 100644 --- a/lib/http/modules/hidden_danger_api.dart +++ b/lib/http/modules/hidden_danger_api.dart @@ -40,14 +40,26 @@ class HiddenDangerApi { ); } + // /// 获取企业 + // static Future> getCorpInfoListTree() { + // return HttpManager().request( + // ApiService.basePath, + // '/basicInfo/corpInfo/listAll', + // method: Method.get, + // data: { + // "inType": '0,1', + // }, + // ); + // } + /// 获取部门 - static Future> getHiddenTreatmentListTree() { + static Future> getHiddenTreatmentListTree(String id) { return HttpManager().request( ApiService.basePath, '/basicInfo/department/listTree', method: Method.post, data: { - // "eqCorpinfoId": "1984137837376081921", + "eqCorpinfoId": id, }, ); } diff --git a/lib/http/modules/notif_api.dart b/lib/http/modules/notif_api.dart index 69193ce..cc15563 100644 --- a/lib/http/modules/notif_api.dart +++ b/lib/http/modules/notif_api.dart @@ -66,4 +66,42 @@ class NotifApi { } + + /// 公告列表 + static Future> getAnnouncementList(Map data) { + return HttpManager().request( + '${ApiService.basePath}/base', + '/messages/page?pageSize=9999&pageIndex=${data['pageIndex']}', + method: Method.get, + data: { + // ...data , + }, + ); + } + + /// 公告详情 + static Future> getAnnouncementDetail(String noticeId) { + return HttpManager().request( + '${ApiService.basePath}/message', + '/messages/$noticeId', + method: Method.get, + data: { + // ...data , + }, + ); + } + + /// 公告列表数量 + static Future> getAnnouncementRedPoint() { + return HttpManager().request( + '${ApiService.basePath}/base', + '/messages/page?pageSize=9999&pageIndex=1&statusEnum=UNREAD', + method: Method.get, + data: { + // ...data , + }, + ); + } + + } \ No newline at end of file diff --git a/lib/pages/badge_manager.dart b/lib/pages/badge_manager.dart index 3ba68d5..643f83e 100644 --- a/lib/pages/badge_manager.dart +++ b/lib/pages/badge_manager.dart @@ -17,11 +17,12 @@ class BadgeManager extends ChangeNotifier { Map get workData => _workData; // 各模块未读 int _notifCount = 0; + int _AnnouncementCount = 0; // 读取接口值的公开 getter int get count => _notifCount ; - int get notifCount => _notifCount; + int get notifCount => _notifCount+_AnnouncementCount; @@ -76,14 +77,13 @@ class BadgeManager extends ChangeNotifier { Future initAllModules() async { // 不 await 整个 Future.wait,使调用方不会因为单个慢接口阻塞 try { + // 每个请求都通过 _safe 包裹,设置超时与兜底 final fNotif = _safe>( NotifApi.getNotifRedPoint().then((r) => r as Map), {}, timeout: const Duration(seconds: 4), ); - - fNotif.then((notifJson) { try { _notifCount = ((notifJson['data'] as int?) ?? 0); @@ -94,6 +94,22 @@ class BadgeManager extends ChangeNotifier { _syncNativeDebounced(); }); + // 每个请求都通过 _safe 包裹,设置超时与兜底 + final aNotif = _safe>( + NotifApi.getAnnouncementRedPoint().then((r) => r as Map), + {}, + timeout: const Duration(seconds: 4), + ); + aNotif.then((notifJson) { + try { + _AnnouncementCount = ((notifJson['totalCount'] as int?) ?? 0); + } catch (e, st) { + debugPrint('BadgeManager.parse notifJson error: $e\n$st'); + } + _scheduleNotify(); + _syncNativeDebounced(); + }); + } catch (e, st) { debugPrint('BadgeManager.initAllModules unexpected error: $e\n$st'); @@ -110,6 +126,16 @@ class BadgeManager extends ChangeNotifier { timeout: const Duration(seconds: 4), ); _notifCount = (notifJson['data'] as int?) ?? 0; + + final aNotifJson = await _safe>( + NotifApi.getAnnouncementRedPoint().then((r) => r as Map), + {}, + timeout: const Duration(seconds: 4), + ); + _AnnouncementCount = (aNotifJson['totalCount'] as int?) ?? 0; + + + _onModuleChanged(); } catch (e) { debugPrint('updateNotifCount error: $e'); diff --git a/lib/pages/home/doorAndCar/car/doorArea_car_add_page.dart b/lib/pages/home/doorAndCar/car/doorArea_car_add_page.dart index ffb8730..05a2071 100644 --- a/lib/pages/home/doorAndCar/car/doorArea_car_add_page.dart +++ b/lib/pages/home/doorAndCar/car/doorArea_car_add_page.dart @@ -1,23 +1,32 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/MultiDictValuesPicker.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/center_multi_picker.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_picker_enterprise.dart'; +import 'package:qhd_prevention/customWidget/department_picker_three.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/photo_picker_row.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/modules/basic_info_api.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; +import 'package:qhd_prevention/pages/home/doorAndCar/person_selection_page.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart'; import 'package:qhd_prevention/pages/mine/mine_sign_page.dart'; import 'package:qhd_prevention/pages/mine/webViewPage.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/services/SessionService.dart'; +import 'package:qhd_prevention/tools/car_licence_type.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:flutter/gestures.dart'; @@ -29,12 +38,12 @@ class DoorareaCarAddPage extends StatefulWidget { } class _DoorareaCarAddPageState extends State { - Map pd = {}; + bool _agreed = false; // 部门列表 List _deptList = []; - List _personList = []; + List _personList = []; List signImages = []; late bool _isMyCompanyArea = false; @@ -44,128 +53,21 @@ class _DoorareaCarAddPageState extends State { List _vehicleImages = []; List _vehicleLicenseImages = []; + String corpinfoId = "";//公司 + String corpinfoName = ""; + String buMenId = "";//部门 + String buMenName = ""; + String responsibleId="";//人 + String responsibleName=""; + @override void initState() { super.initState(); - _getDept(); + _getUserData(); } - // 获取部门 - Future _getDept() async { - // try { - // final data = { - // 'eqCorpinfoId': widget.scanData['id'], - // // 'eqParentId': widget.scanData['corpinfoId'], - // }; - // final result = await BasicInfoApi.getDeptTree(data); - // if (result['success'] == true) { - // final list = result['data'] ?? []; - // if (list.length > 0) { - // setState(() { - // _deptList = list[0]['childrenList'] ?? []; - // }); - // } - // } - // } catch (e) {} - } - // 提交 - Future _saveSuccess() async { - if (!FormUtils.hasValue(pd, 'corpinfoId')) { - ToastUtil.showNormal(context, '请选择部门'); - return; - } - if (!FormUtils.hasValue(pd, 'postName')) { - ToastUtil.showNormal(context, '请输入岗位'); - return; - } - // try { - // final result = await BasicInfoApi.userFirmEntry(pd); - // LoadingDialogHelper.hide(); - // if (result['success'] == true) { - // ToastUtil.showNormal(context, '申请成功'); - // _relogin(); - // } else { - // ToastUtil.showNormal(context, result['errMessage']); - // } - // } catch (e) { - // LoadingDialogHelper.hide(); - // ToastUtil.showNormal(context, '操作失败,请重试'); - // } - } - Widget _addPersonWight(Map personData) { - return Stack( - children: [ - Padding(padding: const EdgeInsets.only(top: 5),child: Container( - margin: const EdgeInsets.symmetric(horizontal: 12), - // padding: const EdgeInsets.symmetric(horizontal: 10), - decoration: BoxDecoration( - color: Colors.white, - // 边框 - border: Border.all( - color: Colors.grey.shade300, - width: 1.0, - style: BorderStyle.solid, - ), - borderRadius: BorderRadius.circular(5), - ), - child: Column( - children: [ - const SizedBox(height: 10), - ItemListWidget.selectableLineTitleTextRightButton( - label: '选择部门:', - isEditable: true, - text: personData['departmentName'] ?? '请选择', - isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '选择人员:', - isEditable: true, - text: personData['postName'] ?? '请选择', - isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.itemContainer(RepairedPhotoSection( - isRequired: true, - title: "人员照片", - maxCount: 1, - horizontalPadding: 0, - mediaType: MediaType.image, - isShowAI: false, - onChanged: (List files) {}, - onAiIdentify: () {}, - ),), - const SizedBox(height: 10) - ], - ) - ),), - Positioned( - top: 0, - right: 2, - child: Container( - padding: const EdgeInsets.all(2), - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Colors.red, - ), - - child: GestureDetector( - child: const Icon(Icons.close, size: 14, color: Colors.white,), - onTap: () { - setState(() { - _personList.remove(personData); - }); - }, - ), - ), - ), - ], - ); - } @override Widget build(BuildContext context) { @@ -180,36 +82,52 @@ class _DoorareaCarAddPageState extends State { ListItemFactory.createBuildSimpleSection('申请信息'), - - const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '选择港区:', + isEditable: true, + text: addData['portAreaName'] ?? '请选择', + isRequired: true, + onTap: () async { + _getPortAreaType(); + }, + ), + const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '选择管辖单位:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['jurisdictionalCorpName'] ?? '请选择', isRequired: true, - onTap: () async { - if (_deptList.isEmpty) { - ToastUtil.showNormal(context, '暂无部门信息'); + onTap: () { + if( addData['portAreaId'].isEmpty){ + ToastUtil.showNormal(context, '请先选择港区'); return; } - final found = await BottomPicker.show( - context, - items: _deptList, - itemBuilder: - (i) => Text(i['name']!, textAlign: TextAlign.center), - initialIndex: 0, - ); - //FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (ctx) => DepartmentPickerEnterprise( + addData['portAreaId'], + onSelected: (id, name,pdId) async { + setState(() { + addData['jurisdictionalCorpId']= id; + addData['jurisdictionalCorpName']= name; + + addData['auditPersonCorpId']= id; + addData['auditPersonCorpName']= name; + addData['auditPersonDepartmentId']= ''; + addData['auditPersonDepartmentName']= ''; + addData['auditPersonUserId']=''; + addData['auditPersonUserName']=''; + + }); + }, + ), + ); - if (found != null) { - setState(() { - pd['departmentId'] = found['id']; - pd['departmentName'] = found['name']; - pd['corpinfoId'] = found['corpinfoId']; - pd['corpinfoName'] = found['corpinfoName']; - }); - } }, ), @@ -218,33 +136,48 @@ class _DoorareaCarAddPageState extends State { ItemListWidget.selectableLineTitleTextRightButton( label: '选择管辖区域:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['closedAreaName']?? "请选择", isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '审核人员部门:', - isEditable: true, - text: pd['departmentName'] ?? '请选择', - isRequired: true, - onTap: () async {}, + onTap: () { + if(addData['jurisdictionalCorpName'].isEmpty){ + ToastUtil.showNormal(context, '请选择管辖单位'); + return ; + } + _getVisitPortArea(); + }, ), + // const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '审核人员部门:', + // isEditable: true, + // text: addData['departmentName'] ?? '请选择', + // isRequired: true, + // onTap: () async {}, + // ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '审核人员:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['auditPersonUserName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + // if ( addData['auditPersonUserName'].isEmpty) { + // ToastUtil.showNormal(context, '请先选择部门'); + // return; + // } + + _getApproverList(); + }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '选择项目:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['projectName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + _getProjectNameList(); + }, ), const Divider(), Padding( @@ -252,7 +185,7 @@ class _DoorareaCarAddPageState extends State { child: ItemListWidget.selectableLineTitleTextRightButton( label: '选择时间:', isEditable: false, - text: pd['departmentName'] ?? '', + text: addData['auditAllTime'] ?? '', isRequired: true, onTap: () async {}, ), @@ -262,6 +195,9 @@ class _DoorareaCarAddPageState extends State { ItemListWidget.multiLineTitleTextField( label: '申请原因', isEditable: true, + onChanged: (value) { + addData['applyReason']=value; + }, ), @@ -272,28 +208,41 @@ class _DoorareaCarAddPageState extends State { const Divider(), - Padding( - padding: EdgeInsets.symmetric(vertical: 8), - child: ItemListWidget.selectableLineTitleTextRightButton( - label: '驾驶人部门', - isEditable: !_isSelectCar, - onTap: () { - - }, - text: '', - ), - ), - const Divider(height: 1,), + // Padding( + // padding: EdgeInsets.symmetric(vertical: 8), + // child: ItemListWidget.selectableLineTitleTextRightButton( + // label: '驾驶人部门', + // isEditable: !_isSelectCar, + // onTap: () { + // + // }, + // text: '', + // ), + // ), + // const Divider(height: 1,), Padding( padding: EdgeInsets.symmetric(vertical: 8), child: ItemListWidget.selectableLineTitleTextRightButton( label: '驾驶人', isEditable: !_isSelectCar, - onTap: () { + onTap: () async { + if(addData['projectId'].isEmpty){ + ToastUtil.showNormal(context, '请先选择项目'); + return; + } + final result = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList,addData['projectId'],isMoreSelect: false,)), + ); + + if (result != null) { + // 处理返回的选中结果 + _showSelectedResult(context, result); + } }, - text: '', + text: _personList.isNotEmpty?_personList[0].employeePersonUserName: '请选择', ), ), const Divider(height: 1,), @@ -305,9 +254,9 @@ class _DoorareaCarAddPageState extends State { label: '车牌类型', isEditable: !_isSelectCar, onTap: () { - + _getLicensePlateType(); }, - text: '', + text: addData['licenceTypeName'].isNotEmpty ? addData['licenceTypeName'] : "请选择", ), ), @@ -318,22 +267,23 @@ class _DoorareaCarAddPageState extends State { label: '车辆类型', isEditable: !_isSelectCar, onTap: () { - + _getVehicleType(); }, - text: '', + text: addData['vehicleTypeName'].isNotEmpty ? addData['vehicleTypeName'] : "请选择", ), ), const Divider(height: 1,), Padding( padding: EdgeInsets.symmetric(vertical: 8), - child: ItemListWidget.selectableLineTitleTextRightButton( + child:ItemListWidget.singleLineTitleText( label: '车牌号', - isEditable: !_isSelectCar, - onTap: () { - + isEditable: true, + text: addData['licenceNo']??'', + onChanged: (value) { + addData['licenceNo']=value; }, - text: '', + ), ), // ItemListWidget.singleLineTitleText( @@ -399,7 +349,7 @@ class _DoorareaCarAddPageState extends State { side: const BorderSide(color: Colors.grey), onChanged: (value) { setState(() { - _agreed = value ?? false; + // _agreed = value ?? false; }); }, ), @@ -557,6 +507,464 @@ class _DoorareaCarAddPageState extends State { } + Future _getVisitPortArea() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getEnclosedAreaById(addData['jurisdictionalCorpId']); + LoadingDialogHelper.hide(); + if (raw['success'] ) { + List newList = raw['data'] ?? []; + + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + addData['closedAreaId']=id;//封闭区域id + addData['closedAreaName']=name;//封闭区域名称 + + }); + }, + ), + ); + }else{ + ToastUtil.showNormal(context, "获取列表失败"); + LoadingDialogHelper.hide(); + // _showMessage('反馈提交失败'); + // return ""; + } + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + // return ""; + LoadingDialogHelper.hide(); + } + } + + + Future _getApproverList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getApproverList( addData['auditPersonCorpId'],'','','1','');//addData['auditDeptId'] + LoadingDialogHelper.hide(); + if (raw['success'] ) { + List newList = raw['data'] ?? []; + + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + for(int i=0;i _getProjectNameList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getProjectNameList( ); + LoadingDialogHelper.hide(); + printLongString(jsonEncode(raw)); + if (raw['success'] ) { + final newList = raw['data'] ?? []; + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + addData['projectId']=id; + addData['projectName']=name; + //时间 + for(int i=0;i _getPortAreaType() async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => MultiDictValuesPicker( + title: '港区', + dictType: 'HG_AUTH_AREA', + allowSelectParent: false, + onSelected: (id, name, extraData) { + setState(() { + addData['portAreaId'] = extraData?['dictValue'];//港区id + addData['portAreaName'] = name;//港区名称 + + }); + }, + ), + ).then((_) { + // 可选:FocusHelper.clearFocus(context); + }); + } + + Future _getLicensePlateType() async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => MultiDictValuesPicker( + title: '车牌类型', + dictType: 'LICENSE_PLATE_TYPE', + allowSelectParent: false, + onSelected: (id, name, extraData) { + setState(() { + addData['licenceType'] = extraData?['dictValue']; + addData['licenceTypeName'] = name; + + }); + }, + ), + ).then((_) { + // 可选:FocusHelper.clearFocus(context); + }); + } + + Future _getVehicleType() async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => MultiDictValuesPicker( + title: '车辆类型', + dictType: 'VEHICLE_TYPE', + allowSelectParent: false, + onSelected: (id, name, extraData) { + setState(() { + addData['vehicleType'] = extraData?['dictValue']; + addData['vehicleTypeName'] = name; + + }); + }, + ), + ).then((_) { + // 可选:FocusHelper.clearFocus(context); + }); + } + + + // 获取部门 + Future _getUserData() async { + try { + + final raw = await AuthApi.getUserData( ); + if (raw['success'] ) { + setState(() { + corpinfoId = raw['data']['corpinfoId'];//公司 + corpinfoName = raw['data']['corpinfoName']; + buMenId = raw['data']['departmentId']; + buMenName = raw['data']['departmentName']; + responsibleId=raw['data']['id']; + responsibleName=raw['data']['name']; + + addData['applyPersonCorpId']=corpinfoId;//所属企业 + addData['applyPersonCorpName']=corpinfoName; + + }); + + }else{ + ToastUtil.showNormal(context, "获取个人信息失败"); + + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + + } + } + + + + // 提交 + Future _saveSuccess() async { + try { + + if(addData['jurisdictionalCorpId'].isEmpty){ + ToastUtil.showNormal(context, '请选择选择管辖单位'); + return; + } + + if(addData['auditPersonUserName'].isEmpty){ + ToastUtil.showNormal(context, '请选择审核人员'); + return; + } + + + if(addData['projectName'].isEmpty){ + ToastUtil.showNormal(context, '请选择项目'); + return; + } + + if(addData['applyReason'].isEmpty){ + ToastUtil.showNormal(context, '请填写申请原因'); + return; + } + + if(_personList.isEmpty){ + ToastUtil.showNormal(context, '请选择驾驶人'); + return; + } + + addData['applyPersonCorpId']=_personList[0].personCorpId; + addData['applyPersonCorpName']=_personList[0].personCorpName; + addData['applyPersonDepartmentId']=_personList[0].personDepartmentId; + addData['applyPersonDepartmentName']=_personList[0].personDepartmentName; + addData['applyPersonUserId']=_personList[0].employeePersonUserId; + addData['applyPersonUserName']=_personList[0].employeePersonUserName; + + addData['userFaceUrl']=_personList[0].userFaceUrl; + addData['userPhone']=_personList[0].userPhone; + addData['userCard']=_personList[0].userCard; + + if(addData['licenceTypeName'].isEmpty){ + ToastUtil.showNormal(context, '请选择车牌类型'); + return; + } + + if(addData['vehicleTypeName'].isEmpty){ + ToastUtil.showNormal(context, '请选择车辆类型'); + return; + } + + if(addData['licenceNo'].isEmpty){ + ToastUtil.showNormal(context, '请输入车牌号'); + return; + } + + + bool isLicenseTrue = isValidChineseLicensePlate(addData['licenceNo']); + if(!isLicenseTrue){ + ToastUtil.showNormal(context, '车牌号错误,请注意字母大小写'); + return; + } + + if(_vehicleImages.isEmpty){ + ToastUtil.showNormal(context, '请上传车辆照片'); + return; + } + + if(_vehicleLicenseImages.isEmpty){ + ToastUtil.showNormal(context, '请上传行驶证照片'); + return; + }else if(_vehicleLicenseImages.length==1){ + ToastUtil.showNormal(context, '请上传行驶证正反面照片'); + return; + } + + + if(signImages.isEmpty){ + ToastUtil.showNormal(context, '请阅读《安全进港须知》并签字'); + return; + } + + LoadingDialogHelper.show(); + if(_vehicleLicenseImages.isNotEmpty){ + String licenseId= await _addImgFilesLicense(_vehicleLicenseImages, UploadFileType.gateAccessVehicleLicensePhoto,addData['drivingLicenseId']); + addData['drivingLicenseId']=licenseId; + } + + if(_vehicleImages.isNotEmpty){ + String carImageId= await _addImgFilesLicense(_vehicleImages, UploadFileType.gateAccessVehiclePhoto,'',); + addData['attachmentId']=carImageId; + } + + if(signImages.isNotEmpty){ + String signImagesId= await _addImgFilesLicense(signImages, UploadFileType.enclosedAreaVehicleApplicantSignature,''); + addData['informSignId']=signImagesId; + } + LoadingDialogHelper.hide(); + + + + LoadingDialogHelper.show(); + final result = await DoorAndCarApi.enclosedAreaCarSave(addData); + LoadingDialogHelper.hide(); + if (result['success'] ) { + ToastUtil.showNormal(context, '提交成功'); + Navigator.pop(context); + } else { + ToastUtil.showNormal(context, result['errMessage']); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '操作失败,请重试'); + } + } + + + + Future _addImgFilesLicense(List imagePaths,UploadFileType type,String idType) async { + try { + + final raw = await FileApi.uploadFiles( imagePaths, type,idType); + if (raw['success'] ) { + return raw['data']['foreignKey']; + }else{ + // _showMessage('反馈提交失败'); + return ""; + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + return ""; + } + } + + + Map addData={ + "carBelongType": "3", //申请归属1股份2分公司3相关方4临时人员5分公司内部车辆 + "licenceType": "", //车牌类型数据字典 + "licenceTypeName": "", //车牌类型名称0-白牌 1- 蓝牌 2-黄牌 3-绿牌 4-黑牌 + "licenceNo": "", //车牌号 + "vehicleType": "", //车辆类型 + "vehicleTypeName": "", //车辆类型-名字 + "levelOneMkmjId": "", //一级口门id + "levelTwoMkmjId": "", //二级口门id(封闭区域口门) + "closedAreaId": "", //封闭区域id + "closedAreaName": "", //封闭区域名称 + "visitStartTime": "", //访问起始时间 + "visitEndTime": "", //访问结束时间 + "jurisdictionalCorpId": "", //区域管辖单位名id + "jurisdictionalCorpName": "", //区域管辖单位名称 + "projectId": "", //项目id + "projectName": "", //项目名称 + "applyReason": "", //申请理由 + // "entourage": null, //随行人员 + "informSignId": "", //告知签字 + "drivingLicenseId": "", //行驶证照片 + "attachmentId": "", //车辆照片 + "auditPersonCorpId": "", //审批人员所属企业ID + "auditPersonCorpName": "", //审批人员所属企业名称 + "auditPersonDepartmentId": "", //审批人员所属部门id + "auditPersonDepartmentName": "", //审批人员部门名称 + "auditPersonUserId": "", //审批人员id + "auditPersonUserName": "", //审批人员姓名 + "applyPersonCorpId": "", //申请人员所属企业ID + "applyPersonCorpName": "", //申请人员所属企业名称 + "applyPersonDepartmentId": "", //待审批人员所属部门id + "applyPersonDepartmentName": "", //申请人员部门名称 + "applyPersonUserId": "", //申请人员id + "applyPersonUserName": "", //申请人员姓名 + "userFaceUrl": "", //申请人头像 + "userPhone": "", //申请人手机号 + "userCard": "", //申请人身份证号码 + + "portAreaId": '', //港区id + "portAreaName": '', //港区名称 + "auditAllTime": '', //时间反显 + + }; + + diff --git a/lib/pages/home/doorAndCar/car/firstlevel_car_add_page.dart b/lib/pages/home/doorAndCar/car/firstlevel_car_add_page.dart index ac46765..d2aff0c 100644 --- a/lib/pages/home/doorAndCar/car/firstlevel_car_add_page.dart +++ b/lib/pages/home/doorAndCar/car/firstlevel_car_add_page.dart @@ -1,23 +1,31 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/MultiDictValuesPicker.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/center_multi_picker.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_picker_three.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/photo_picker_row.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/modules/basic_info_api.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; +import 'package:qhd_prevention/pages/home/doorAndCar/person_selection_page.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart'; import 'package:qhd_prevention/pages/mine/mine_sign_page.dart'; import 'package:qhd_prevention/pages/mine/webViewPage.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/services/SessionService.dart'; +import 'package:qhd_prevention/tools/car_licence_type.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:flutter/gestures.dart'; @@ -29,12 +37,14 @@ class FirstlevelCarAddPage extends StatefulWidget { } class _FirstlevelCarAddPageState extends State { - Map pd = {}; + + final TextEditingController _licenseController = TextEditingController(); + bool _agreed = false; // 部门列表 List _deptList = []; - List _personList = []; + List _personList = []; List signImages = []; late bool _isMyCompanyArea = false; @@ -44,128 +54,23 @@ class _FirstlevelCarAddPageState extends State { List _vehicleImages = []; List _vehicleLicenseImages = []; + + String corpinfoId = "";//公司 + String corpinfoName = ""; + String buMenId = "";//部门 + String buMenName = ""; + String responsibleId="";//人 + String responsibleName=""; + + @override void initState() { super.initState(); - _getDept(); + _getUserData(); } - // 获取部门 - Future _getDept() async { - // try { - // final data = { - // 'eqCorpinfoId': widget.scanData['id'], - // // 'eqParentId': widget.scanData['corpinfoId'], - // }; - // final result = await BasicInfoApi.getDeptTree(data); - // if (result['success'] == true) { - // final list = result['data'] ?? []; - // if (list.length > 0) { - // setState(() { - // _deptList = list[0]['childrenList'] ?? []; - // }); - // } - // } - // } catch (e) {} - } - // 提交 - Future _saveSuccess() async { - if (!FormUtils.hasValue(pd, 'corpinfoId')) { - ToastUtil.showNormal(context, '请选择部门'); - return; - } - if (!FormUtils.hasValue(pd, 'postName')) { - ToastUtil.showNormal(context, '请输入岗位'); - return; - } - // try { - // final result = await BasicInfoApi.userFirmEntry(pd); - // LoadingDialogHelper.hide(); - // if (result['success'] == true) { - // ToastUtil.showNormal(context, '申请成功'); - // _relogin(); - // } else { - // ToastUtil.showNormal(context, result['errMessage']); - // } - // } catch (e) { - // LoadingDialogHelper.hide(); - // ToastUtil.showNormal(context, '操作失败,请重试'); - // } - } - Widget _addPersonWight(Map personData) { - return Stack( - children: [ - Padding(padding: const EdgeInsets.only(top: 5),child: Container( - margin: const EdgeInsets.symmetric(horizontal: 12), - // padding: const EdgeInsets.symmetric(horizontal: 10), - decoration: BoxDecoration( - color: Colors.white, - // 边框 - border: Border.all( - color: Colors.grey.shade300, - width: 1.0, - style: BorderStyle.solid, - ), - borderRadius: BorderRadius.circular(5), - ), - child: Column( - children: [ - const SizedBox(height: 10), - ItemListWidget.selectableLineTitleTextRightButton( - label: '选择部门:', - isEditable: true, - text: personData['departmentName'] ?? '请选择', - isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '选择人员:', - isEditable: true, - text: personData['postName'] ?? '请选择', - isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.itemContainer(RepairedPhotoSection( - isRequired: true, - title: "人员照片", - maxCount: 1, - horizontalPadding: 0, - mediaType: MediaType.image, - isShowAI: false, - onChanged: (List files) {}, - onAiIdentify: () {}, - ),), - const SizedBox(height: 10) - ], - ) - ),), - Positioned( - top: 0, - right: 2, - child: Container( - padding: const EdgeInsets.all(2), - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Colors.red, - ), - - child: GestureDetector( - child: const Icon(Icons.close, size: 14, color: Colors.white,), - onTap: () { - setState(() { - _personList.remove(personData); - }); - }, - ), - ), - ), - ], - ); - } @override Widget build(BuildContext context) { @@ -184,17 +89,21 @@ class _FirstlevelCarAddPageState extends State { ItemListWidget.selectableLineTitleTextRightButton( label: '选择项目名称:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['projectName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + _getProjectNameList(); + }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '审核人员:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['auditUserName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + _getApproverList(); + }, ), const Divider(), Padding( @@ -202,7 +111,7 @@ class _FirstlevelCarAddPageState extends State { child: ItemListWidget.selectableLineTitleTextRightButton( label: '选择时间:', isEditable: false, - text: pd['departmentName'] ?? '', + text: addData['auditAllTime'] ?? '', isRequired: true, onTap: () async {}, ), @@ -211,19 +120,21 @@ class _FirstlevelCarAddPageState extends State { ItemListWidget.selectableLineTitleTextRightButton( label: '访问港区:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['gateLevelAuthAreaName'] ?? '请选择', isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '选择范围:', - isEditable: true, - text: pd['departmentName'] ?? '请选择', - isRequired: true, - onTap: () async {}, + onTap: () { + _getVisitPortArea(); + }, ), const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '选择范围:', + // isEditable: true, + // text: pd['departmentName'] ?? '请选择', + // isRequired: true, + // onTap: () async {}, + // ), + // const Divider(), // ItemListWidget.multiLineTitleTextField( // label: '申请原因', // isEditable: true, @@ -237,28 +148,41 @@ class _FirstlevelCarAddPageState extends State { const Divider(), - Padding( - padding: EdgeInsets.symmetric(vertical: 8), - child: ItemListWidget.selectableLineTitleTextRightButton( - label: '驾驶人部门', - isEditable: !_isSelectCar, - onTap: () { - - }, - text: '', - ), - ), - const Divider(height: 1,), + // Padding( + // padding: EdgeInsets.symmetric(vertical: 8), + // child: ItemListWidget.selectableLineTitleTextRightButton( + // label: '驾驶人部门', + // isEditable: !_isSelectCar, + // onTap: () { + // + // }, + // text: '', + // ), + // ), + // const Divider(height: 1,), Padding( padding: EdgeInsets.symmetric(vertical: 8), child: ItemListWidget.selectableLineTitleTextRightButton( label: '驾驶人', isEditable: !_isSelectCar, - onTap: () { + onTap: () async { + if(addData['projectId'].isEmpty){ + ToastUtil.showNormal(context, '请先选择项目'); + return; + } + final result = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList,addData['projectId'],isMoreSelect: false,)), + ); + + if (result != null) { + // 处理返回的选中结果 + _showSelectedResult(context, result); + } }, - text: '', + text: _personList.isNotEmpty?_personList[0].employeePersonUserName: '请选择', ), ), const Divider(height: 1,), @@ -270,9 +194,9 @@ class _FirstlevelCarAddPageState extends State { label: '车牌类型', isEditable: !_isSelectCar, onTap: () { - + _getLicensePlateType(); }, - text: '', + text: addData['licenceTypeName'].isNotEmpty ? addData['licenceTypeName'] : "请选择", ), ), @@ -283,32 +207,26 @@ class _FirstlevelCarAddPageState extends State { label: '车辆类型', isEditable: !_isSelectCar, onTap: () { - + _getVehicleType(); }, - text: '', + text: addData['vehicleTypeName'].isNotEmpty ? addData['vehicleTypeName'] : "请选择", ), ), const Divider(height: 1,), Padding( padding: EdgeInsets.symmetric(vertical: 8), - child: ItemListWidget.selectableLineTitleTextRightButton( + child:ItemListWidget.singleLineTitleTextTwo( + controller: _licenseController, label: '车牌号', - isEditable: !_isSelectCar, - onTap: () { - + isEditable: true, + text: addData['licenceNo']??'', + onChanged: (value) { + addData['licenceNo']=value; }, - text: '', + ), ), - // ItemListWidget.singleLineTitleText( - // label: '车牌号', - // isEditable: !_isSelectCar, - // text: '', - // onChanged: (value) { - // - // }, - // ), const Divider(), @@ -364,7 +282,7 @@ class _FirstlevelCarAddPageState extends State { side: const BorderSide(color: Colors.grey), onChanged: (value) { setState(() { - _agreed = value ?? false; + // _agreed = value ?? false; }); }, ), @@ -522,6 +440,450 @@ class _FirstlevelCarAddPageState extends State { } + // 获取部门 + Future _getUserData() async { + try { + + final raw = await AuthApi.getUserData( ); + if (raw['success'] ) { + setState(() { + corpinfoId = raw['data']['corpinfoId'];//公司 + corpinfoName = raw['data']['corpinfoName']; + buMenId = raw['data']['departmentId']; + buMenName = raw['data']['departmentName']; + responsibleId=raw['data']['id']; + responsibleName=raw['data']['name']; + + addData['vehicleCorpId']=corpinfoId;//所属企业 + addData['vehicleCorpName']=corpinfoName; + + }); + + }else{ + ToastUtil.showNormal(context, "获取个人信息失败"); + + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + + } + } + + + Future _getProjectNameList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getProjectNameList( ); + LoadingDialogHelper.hide(); + printLongString(jsonEncode(raw)); + if (raw['success'] ) { + final newList = raw['data'] ?? []; + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) { + setState(() { + addData['projectId']=id; + addData['projectName']=name; + //时间 + for(int i=0;i _getApproverList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getApproverList( corpinfoId,'','','1',''); + LoadingDialogHelper.hide(); + if (raw['success'] ) { + List newList = raw['data'] ?? []; + + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + for(int i=0;i _getVisitPortArea() async { + + List result = []; + // 计算预选索引 + final List initialIndices = []; + + final resultList = await BasicInfoApi.getDictValues('HG_AUTH_AREA'); + List raw = resultList["data"] as List; + + result = raw.map((item) => item['dictLabel'].toString()).toList(); + + if(addData['gateLevelAuthArea'].isNotEmpty){ + // 解析 JSON + Map jsonData = json.decode(addData['gateLevelAuthArea']); + List areaList = jsonData['area']; + // 提取 value 值列表 + List targetValues = areaList.map((item) => item['value'].toString()).toList(); + + // 遍历目标值,在 raw 中查找匹配 + for (String targetValue in targetValues) { + for (int i = 0; i < raw.length; i++) { + var item = raw[i]; + if (item != null && item is Map && item.containsKey('dictLabel')) { + if (item['dictLabel'].toString() == targetValue) { + initialIndices.add(i);// 记录索引位置(从0开始) + break; + } + } + } + } + } + + + // 显示选择器 + final selectedItems = await CenterMultiPicker.show( + context, + items: result, + itemBuilder: (item) => Text( + item, + style: const TextStyle(fontSize: 16), + ), + initialSelectedIndices: initialIndices, // 设置预选索引 + maxSelection: null, // 不限制选择数量 + allowEmpty: true, + title: '访问港区范围', + ); + + // 处理选择结果 + if (selectedItems != null) { + setState(() { + addData['gateLevelAuthAreaName'] = selectedItems.join(','); + + // 构建新的 JSON 数据 + List> areaList = []; + + // 遍历选中的项目,在 raw 中查找对应的完整数据 + for (String selectedItem in selectedItems) { + for (var item in raw) { + if (item != null && item is Map && item.containsKey('dictLabel')) { + if (item['dictLabel'].toString() == selectedItem) { + // 找到匹配的数据,添加 value(dictLabel) 和 bianma(dictValue) + areaList.add({ + 'value': item['dictLabel'].toString(), + 'bianma': item['dictValue'].toString(), // 或者使用 toString() 如果需要字符串 + }); + break; + } + } + } + } + + // 构建最终的 JSON 字符串 + Map jsonData = { + 'area': areaList + }; + + // 将 Map 转换为 JSON 字符串并保存 + addData['gateLevelAuthArea'] = json.encode(jsonData); + + }); + } + + } + + void _showSelectedResult(BuildContext context, SelectionPersonResult result) { + setState(() { + _personList.clear(); + for(int i=0;i _getLicensePlateType() async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => MultiDictValuesPicker( + title: '车牌类型', + dictType: 'LICENSE_PLATE_TYPE', + allowSelectParent: false, + onSelected: (id, name, extraData) { + setState(() { + addData['licenceType'] = extraData?['dictValue']; + addData['licenceTypeName'] = name; + + }); + }, + ), + ).then((_) { + // 可选:FocusHelper.clearFocus(context); + }); + } + + Future _getVehicleType() async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => MultiDictValuesPicker( + title: '车辆类型', + dictType: 'VEHICLE_TYPE', + allowSelectParent: false, + onSelected: (id, name, extraData) { + setState(() { + addData['vehicleType'] = extraData?['dictValue']; + addData['vehicleTypeName'] = name; + + }); + }, + ), + ).then((_) { + // 可选:FocusHelper.clearFocus(context); + }); + } + + + + + + // 提交 + Future _saveSuccess() async { + try { + if(addData['projectName'].isEmpty){ + ToastUtil.showNormal(context, '请选择选择项目名称'); + return; + } + + if(addData['auditUserName'].isEmpty){ + ToastUtil.showNormal(context, '请选择审核人员'); + return; + } + + + if(addData['gateLevelAuthAreaName'].isEmpty){ + ToastUtil.showNormal(context, '请选择访问港区'); + return; + } + + if(_personList.isEmpty){ + ToastUtil.showNormal(context, '请选择驾驶人'); + return; + } + + addData['drivingUserId']=_personList[0].userPhone; + addData['drivingUserName']=_personList[0].userCard; + addData['lsUserPhone']=_personList[0].userPhone; + addData['lsUserIdcard']=_personList[0].userCard; + + if(addData['licenceTypeName'].isEmpty){ + ToastUtil.showNormal(context, '请选择车牌类型'); + return; + } + + if(addData['vehicleTypeName'].isEmpty){ + ToastUtil.showNormal(context, '请选择车辆类型'); + return; + } + + if(addData['licenceNo'].isEmpty){ + ToastUtil.showNormal(context, '请输入车牌号'); + return; + } + + bool isLicenseTrue = isValidChineseLicensePlate(addData['licenceNo']); + if(!isLicenseTrue){ + ToastUtil.showNormal(context, '车牌号错误,请注意字母大小写'); + return; + } + + if(_vehicleLicenseImages.isEmpty){ + ToastUtil.showNormal(context, '请上传行驶证照片'); + return; + }else if(_vehicleLicenseImages.length==1){ + ToastUtil.showNormal(context, '请上传行驶证正反面照片'); + return; + } + + if(_vehicleImages.isEmpty){ + ToastUtil.showNormal(context, '请上传车辆照片'); + return; + } + + if(signImages.isEmpty){ + ToastUtil.showNormal(context, '请阅读《安全进港须知》并签字'); + return; + } + + LoadingDialogHelper.show(); + if(_vehicleLicenseImages.isNotEmpty){ + String licenseId= await _addImgFilesLicense(_vehicleLicenseImages, UploadFileType.gateAccessVehicleLicensePhoto,addData['drivingLicenseId']); + addData['drivingLicenseId']=licenseId; + } + + if(_vehicleImages.isNotEmpty){ + String carImageId= await _addImgFilesLicense(_vehicleImages, UploadFileType.gateAccessVehiclePhoto,'',); + addData['attachmentId']=carImageId; + } + + if(signImages.isNotEmpty){ + String signImagesId= await _addImgFilesLicense(signImages, UploadFileType.gateAccessVehicleApplicantSignature,''); + addData['informSignId']=signImagesId; + } + LoadingDialogHelper.hide(); + + LoadingDialogHelper.show(); + final result = await DoorAndCarApi.levelCarSave(addData); + LoadingDialogHelper.hide(); + if (result['success'] == true) { + ToastUtil.showNormal(context, '提交成功'); + Navigator.pop(context); + } else { + ToastUtil.showNormal(context, result['errMessage']); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '操作失败,请重试'); + } + } + + + Future _addImgFilesLicense(List imagePaths,UploadFileType type,String idType) async { + try { + + final raw = await FileApi.uploadFiles( imagePaths, type,idType); + if (raw['success'] ) { + return raw['data']['foreignKey']; + }else{ + // _showMessage('反馈提交失败'); + return ""; + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + return ""; + } + } + + + Map addData={ + "licenceType": "", //车牌类型数据字典 + "licenceTypeName": "", //车牌类型名称0-白牌 1- 蓝牌 2-黄牌 3-绿牌 4-黑牌 + "licenceNo": "", //车牌号 + "vehicleType": "", //车辆类型 + "vehicleTypeName": "", //车辆类型-名字 + "vehicleBelongType": "5", //车辆所属类型 1-股份员工车辆,2-股份单位车辆,3-分公司员工车辆,4-分公司单位车辆 ,5-相关方车辆, 6:临时车辆,7检查部门车辆 + "gateLevelAuthArea": "", //授权范围港区与区域 + "visitStartTime": "", //访问起始时间 + "visitEndTime": "", //访问结束时间 + "emissionStandards": "", //排放标准名称 + "emissionStandardsName": "", //排放标准 + "drivingLicenseId": '', //行驶证照片 + "attachmentId": '', //车辆照片 + "informSignId": '', //告知签字 + "projectId": '', //项目id + "projectName": "", //项目名称 + "blockedFlag": '', //1被拉黑2未拉黑 + "mkmjId": '', //口门id + "lsUserPhone": "", //临时申请驾驶人手机号 + "lsUserIdcard": "", //临时申请驾驶人身份证 + "auditCorpId": '', //审核企业 + "auditCorpName": "", //审核企业名称 + "auditDeptId": '', //审核部门 + "auditDeptName": "", //审核部门名称 + "auditUserId": '', //审核用户 + "auditUserName": "", //审核用户名称 + + "auditAllTime": '', //时间反显 + "gateLevelAuthAreaName": "", //访问地区范围反显 + }; diff --git a/lib/pages/home/doorAndCar/car/onlyLook_car_application.dart b/lib/pages/home/doorAndCar/car/onlyLook_car_application.dart index 5d0efb8..fd62bfd 100644 --- a/lib/pages/home/doorAndCar/car/onlyLook_car_application.dart +++ b/lib/pages/home/doorAndCar/car/onlyLook_car_application.dart @@ -1,41 +1,51 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; import 'package:qhd_prevention/tools/tools.dart'; -class CarApplicationRecord extends StatefulWidget { - const CarApplicationRecord(this.type, {super.key}); +class OnlylookCarApplication extends StatefulWidget { + const OnlylookCarApplication(this.type, this.id, {super.key}); final int type;//1 一级口门人员审核 2 一级口门车辆审核 3 审批记录(人员) 4 审批记录(车辆) + final String id; @override - State createState() => _CarApplicationRecordState(); + State createState() => _OnlylookCarApplicationState(); } -class _CarApplicationRecordState extends State { - // 申请信息数据 - final Map applicationInfo = { - 'mingcheng': '项目名称', - 'bumen': '审核人员部门', - 'renyuan': '审核人员', - 'shijian': '2024-01-01 至 2024-12-31', - 'gangqu': '访问港区', - 'diqu': '访问地区', - }; +class _OnlylookCarApplicationState extends State { - // 车辆信息数据 - final Map vehicleInfo = { - 'name': '驾驶人姓名', - 'type': '车辆类型', - 'licenseType': '车牌号类型', - 'license': '车牌号',}; + // 模拟申请信息数据 + dynamic applicationInfo = {}; + // 模拟审核人员列表数据 + List personnelList = []; + + //签字图片 + List signList = []; + //行驶证图片 + List licenseList = []; + // 车辆图片 + List attachmentList = []; + + + + @override + void initState() { + super.initState(); + + _getXgfApplyInfoById(); + } @override @@ -63,7 +73,7 @@ class _CarApplicationRecordState extends State { SizedBox(height: 12), // 车辆信息卡片 - _buildCarCard(vehicleInfo), + _buildCarCard(applicationInfo), SizedBox(height: 16), @@ -113,7 +123,7 @@ class _CarApplicationRecordState extends State { label: '项目名称:', isEditable: false, horizontalnum: 0, - text: applicationInfo['mingcheng'] ?? '', + text: applicationInfo['projectName'] ?? '', onTap: () {}, ), const Divider(), @@ -121,52 +131,52 @@ class _CarApplicationRecordState extends State { label: '访问港区:', isEditable: false, horizontalnum: 0, - text: applicationInfo['gangqu'] ?? '', + text: _getAccessArea(applicationInfo), onTap: () {}, ), const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '区域范围:', - isEditable: false, - horizontalnum: 0, - text: applicationInfo['diqu'] ?? '', - onTap: () {}, - ), - const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '区域范围:', + // isEditable: false, + // horizontalnum: 0, + // text: applicationInfo['diqu'] ?? '', + // onTap: () {}, + // ), + // const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '审核人员部门:', - isEditable: false, - horizontalnum: 0, - text: applicationInfo['bumen'] ?? '', - onTap: () {}, - ), - const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '审核人员部门:', + // isEditable: false, + // horizontalnum: 0, + // text: applicationInfo['bumen'] ?? '', + // onTap: () {}, + // ), + // const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '审核人员:', isEditable: false, horizontalnum: 0, - text: applicationInfo['renyuan'] ?? '', + text: personnelList.isNotEmpty? personnelList[0]['auditUserName'] ?? '':'', onTap: () {}, ), const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '所属项目:', - isEditable: false, - horizontalnum: 0, - text: applicationInfo['mingcheng'] ?? '', - onTap: () {}, - ), - const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '所属项目:', + // isEditable: false, + // horizontalnum: 0, + // text: applicationInfo['mingcheng'] ?? '', + // onTap: () {}, + // ), + // const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '选择时间:', isEditable: false, horizontalnum: 0, - text: applicationInfo['shijian'] ?? '', + text: _changeTime(applicationInfo), onTap: () {}, ), const Divider(), @@ -187,7 +197,7 @@ class _CarApplicationRecordState extends State { label: '驾驶人姓名:', isEditable: false, horizontalnum: 0, - text: info['name'] ?? '', + text: info['employeeVehicleUserName'] ?? '', onTap: () {}, ), const Divider(), @@ -195,7 +205,7 @@ class _CarApplicationRecordState extends State { label: '车辆类型:', isEditable: false, horizontalnum: 0, - text: info['type'] ?? '', + text: info['vehicleTypeName'] ?? '', onTap: () {}, ), const Divider(), @@ -204,7 +214,7 @@ class _CarApplicationRecordState extends State { label: '车牌号类型:', isEditable: false, horizontalnum: 0, - text: info['licenseType'] ?? '', + text: info['licenceTypeName'] ?? '', onTap: () {}, ), const Divider(), @@ -213,22 +223,24 @@ class _CarApplicationRecordState extends State { label: '车牌号:', isEditable: false, horizontalnum: 0, - text: info['license'] ?? '', + text: info['licenceNo'] ?? '', onTap: () {}, ), const Divider(), // 照片信息 - _buildPhotoItem(1), + _buildPhotoItem(1,licenseList), const Divider(), - _buildPhotoItem(2), + _buildPhotoItem(2,attachmentList), const Divider(), // _buildPhotoItem(3), + + if(signList.isNotEmpty) Container( height: 150, padding: EdgeInsets.all(8), alignment: Alignment.center, - child: Image.network('${ApiService.baseImgPath}1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'), + child: Image.network('${ApiService.baseImgPath}${signList[0]}'), ), // const Divider(), @@ -238,7 +250,7 @@ class _CarApplicationRecordState extends State { ); } - Widget _buildPhotoItem(int witch) { + Widget _buildPhotoItem(int witch,List listData) { String labelName=''; switch(witch){ case 1: @@ -260,16 +272,9 @@ class _CarApplicationRecordState extends State { onTapCallBack: (path) { presentOpaque(SingleImageViewer(imageUrl: path), context); }, - imageUrls: ['1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg','1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'], + imageUrls: listData, ), - // ItemListWidget.OneRowImageTitle( - // label: labelName, - // text: '', - // onTapCallBack: (path) { - // presentOpaque(SingleImageViewer(imageUrl: path), context); - // }, - // imgPath: '1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg', - // ), + ); } @@ -317,4 +322,79 @@ class _CarApplicationRecordState extends State { ], ); } + + + String _getAccessArea(final item) { + //审核状态(1:审核中;2审核通过; 3:审核驳回,4无需审批(检查部门车辆)) + String type = item["gateLevelAuthArea"]??''; + if(type.isNotEmpty){ + // 解析 JSON + Map jsonData = json.decode(type); + List areaList = jsonData['area']; + // 提取 value 值列表 + List targetValues = areaList.map((item) => item['value'].toString()).toList(); + return targetValues.join(','); + }else{ + return ''; + } + } + + String _changeTime(item) { + String timeStart=item['visitStartTime']??''; + String timeEnd=item['visitEndTime']??''; + if(timeStart.isNotEmpty&&timeEnd.isNotEmpty){ + return '$timeStart 至 $timeEnd'; + }else{ + return ''; + } + + } + + + Future _getXgfApplyInfoById() async { + try { + LoadingDialogHelper.show(); + final Map result= await DoorAndCarApi.getLevelCarInfoById(widget.id); + LoadingDialogHelper.hide(); + if (result['success'] ) { + + // final dynamic newList = result['data'] ; + + setState(() async { + applicationInfo=result['data']; + personnelList=applicationInfo['vehicleAuditLogList']??[]; + + final imageResults = await Future.wait([ + FileApi.getImagePath(applicationInfo['informSignId'], UploadFileType.gateAccessVehicleApplicantSignature), + FileApi.getImagePath(applicationInfo['drivingLicenseId'], UploadFileType.gateAccessVehicleLicensePhoto), + FileApi.getImagePath(applicationInfo['attachmentId'], UploadFileType.gateAccessVehiclePhoto), + ]); + if (imageResults[0]['success']) { + setState(() { + // 签字图片 - 明确指定类型 + List signData = imageResults[0]['data'] as List; + signList = signData.map((item) => item['filePath'].toString()).toList(); + //行驶证图片 + List licenseData = imageResults[1]['data'] as List; + licenseList = licenseData.map((item) => item['filePath'].toString()).toList(); + // 车辆图片 + List attachmentData = imageResults[2]['data'] as List; + attachmentList = attachmentData.map((item) => item['filePath'].toString()).toList(); + + }); + } + }); + + }else{ + ToastUtil.showNormal(context, '加载数据失败'); + // _showMessage('加载数据失败'); + } + } catch (e) { + LoadingDialogHelper.hide(); + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载数据失败:$e'); + } + } + + } diff --git a/lib/pages/home/doorAndCar/car/onlyLook_doorarea_car.dart b/lib/pages/home/doorAndCar/car/onlyLook_doorarea_car.dart index 1b1232d..4f2ed7e 100644 --- a/lib/pages/home/doorAndCar/car/onlyLook_doorarea_car.dart +++ b/lib/pages/home/doorAndCar/car/onlyLook_doorarea_car.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; import 'package:qhd_prevention/tools/tools.dart'; @@ -12,9 +14,10 @@ import 'package:shared_preferences/shared_preferences.dart'; class OnlylookDoorareaCar extends StatefulWidget { - const OnlylookDoorareaCar(this.type, {super.key}); + const OnlylookDoorareaCar(this.type, this.id, {super.key}); final int type;//1 审核 2 查看 + final String id; @override State createState() => _OnlylookDoorareaCarState(); @@ -22,24 +25,18 @@ class OnlylookDoorareaCar extends StatefulWidget { class _OnlylookDoorareaCarState extends State { - // 模拟申请信息数据 - final Map applicationInfo = { - 'mingcheng': '项目名称', - 'bumen': '审核人员部门', - 'renyuan': '审核人员', - 'shijian': '2024-01-01 至 2024-12-31', - 'gangqu': '访问港区', - 'diqu': '访问地区', - }; + // 模拟申请信息数据 + dynamic applicationInfo = {}; - // 模拟人员列表数据 - final Map personnelList = - { - 'name': '张三', - 'bumen': '技术部', - 'isPei': '是', - 'wan': 'A区、B区', - }; + // 模拟审核人员列表数据 + List personnelList = []; + + //签字图片 + List signList = []; + //行驶证图片 + List licenseList = []; + // 车辆图片 + List attachmentList = []; /// 是否是监管端 bool isJGD = false; @@ -50,7 +47,7 @@ class _OnlylookDoorareaCarState extends State { super.initState(); _getTypeTitle(); - + _getXgfApplyInfoById(); } @@ -169,7 +166,7 @@ class _OnlylookDoorareaCarState extends State { label: '管辖单位:', isEditable: false, horizontalnum:0, - text: applicationInfo['mingcheng'] ?? '', + text: applicationInfo['jurisdictionalCorpName'] ?? '', onTap: () {}, ), const Divider(), @@ -178,7 +175,7 @@ class _OnlylookDoorareaCarState extends State { label: '管辖区域:', isEditable: false, horizontalnum:0, - text: applicationInfo['bumen'] ?? '', + text: applicationInfo['closedAreaName'] ?? '', onTap: () {}, ), const Divider(), @@ -187,7 +184,7 @@ class _OnlylookDoorareaCarState extends State { label: '审核人员部门:', isEditable: false, horizontalnum:0, - text: applicationInfo['renyuan'] ?? '', + text: applicationInfo['applyPersonDepartmentName'] ?? '', onTap: () {}, ), const Divider(), @@ -196,7 +193,7 @@ class _OnlylookDoorareaCarState extends State { label: '审核人员:', isEditable: false, horizontalnum:0, - text: applicationInfo['diqu'] ?? '', + text: applicationInfo['applyPersonUserName'] ?? '', onTap: () {}, ), @@ -207,7 +204,7 @@ class _OnlylookDoorareaCarState extends State { label: '所属项目:', isEditable: false, horizontalnum:0, - text: applicationInfo['gangqu'] ?? '', + text: applicationInfo['projectName'] ?? '', onTap: () {}, ), const Divider(), @@ -216,7 +213,7 @@ class _OnlylookDoorareaCarState extends State { label: '时间范围:', isEditable: false, horizontalnum:0, - text: applicationInfo['shijian'] ?? '', + text: _changeTime(applicationInfo), onTap: () {}, ), const Divider(), @@ -224,7 +221,7 @@ class _OnlylookDoorareaCarState extends State { ItemListWidget.twoRowTitleText( label: '申请原因:', - text: applicationInfo['shijian']??'', + text: applicationInfo['applyReason']??'', horizontalInset:0, ), const Divider(), @@ -259,7 +256,7 @@ class _OnlylookDoorareaCarState extends State { label: '驾驶人部门:', isEditable: false, horizontalnum:0, - text: personnelList['name'] ?? '', + text: applicationInfo['applyPersonDepartmentName'] ?? '', onTap: () {}, ), const Divider(), @@ -268,7 +265,7 @@ class _OnlylookDoorareaCarState extends State { label: '驾驶人:', isEditable: false, horizontalnum:0, - text: personnelList['bumen'] ?? '', + text: applicationInfo['applyPersonUserName'] ?? '', onTap: () {}, ), const Divider(), @@ -295,7 +292,7 @@ class _OnlylookDoorareaCarState extends State { label: '车辆类型:', isEditable: false, horizontalnum:0, - text: personnelList['wan'] ?? '', + text: applicationInfo['vehicleTypeName'] ?? '', onTap: () {}, ), const Divider(), @@ -304,7 +301,7 @@ class _OnlylookDoorareaCarState extends State { label: '车牌类型:', isEditable: false, horizontalnum:0, - text: personnelList['wan'] ?? '', + text: applicationInfo['licenceTypeName'] ?? '', onTap: () {}, ), const Divider(), @@ -313,28 +310,29 @@ class _OnlylookDoorareaCarState extends State { label: '车牌号:', isEditable: false, horizontalnum:0, - text: personnelList['wan'] ?? '', + text: applicationInfo['licenceNo'] ?? '', onTap: () {}, ), const Divider(), + ItemListWidget.twoRowTitleAndImages( + title: '车辆照片', + horizontalInset:0, + onTapCallBack: (path) { + presentOpaque(SingleImageViewer(imageUrl: path), context); + }, + imageUrls: attachmentList, + ), - ItemListWidget.twoRowTitleAndImages( - title: '车辆照片', - horizontalInset:0, - onTapCallBack: (path) { - presentOpaque(SingleImageViewer(imageUrl: path), context); - }, - imageUrls: ['1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg','1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'], - ), - // ItemListWidget.OneRowImageTitle( - // label: '车辆照片:', - // imgPath: '1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg', + // ItemListWidget.twoRowTitleAndImages( + // title: '车辆照片', // horizontalInset:0, - // onTapCallBack: (imgUrl) { - // presentOpaque(SingleImageViewer(imageUrl: imgUrl), context); + // onTapCallBack: (path) { + // presentOpaque(SingleImageViewer(imageUrl: path), context); // }, + // imageUrls: ['1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg','1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'], // ), + const Divider(), ItemListWidget.twoRowTitleAndImages( @@ -343,15 +341,25 @@ class _OnlylookDoorareaCarState extends State { onTapCallBack: (path) { presentOpaque(SingleImageViewer(imageUrl: path), context); }, - imageUrls: ['1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg','1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'], + imageUrls: licenseList, ), + + // ItemListWidget.twoRowTitleAndImages( + // title: '行驶证照片', + // horizontalInset:0, + // onTapCallBack: (path) { + // presentOpaque(SingleImageViewer(imageUrl: path), context); + // }, + // imageUrls: ['1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg','1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'], + // ), const Divider(), + if(signList.isNotEmpty) Container( height: 150, padding: EdgeInsets.all(8), alignment: Alignment.center, - child: Image.network('${ApiService.baseImgPath}1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'), + child: Image.network('${ApiService.baseImgPath}${signList[0]}'), ), @@ -380,4 +388,68 @@ class _OnlylookDoorareaCarState extends State { } + + String _changeTime(item) { + String timeStart=item['visitStartTime']??''; + String timeEnd=item['visitEndTime']??''; + if(timeStart.isNotEmpty&&timeEnd.isNotEmpty){ + return '$timeStart 至 $timeEnd'; + }else{ + return ''; + } + + } + + + Future _getXgfApplyInfoById() async { + try { + LoadingDialogHelper.show(); + final Map result= await DoorAndCarApi.getEnclosedCarById(widget.id); + LoadingDialogHelper.hide(); + if (result['success'] ) { + + // final dynamic newList = result['data'] ; + + setState(() async { + applicationInfo=result['data']; + personnelList=applicationInfo['vehicleAuditLogList']??[]; + + final imageResults = await Future.wait([ + FileApi.getImagePath(applicationInfo['informSignId'], UploadFileType.enclosedAreaVehicleApplicantSignature), + FileApi.getImagePath(applicationInfo['drivingLicenseId'], UploadFileType.gateAccessVehicleLicensePhoto), + FileApi.getImagePath(applicationInfo['attachmentId'], UploadFileType.gateAccessVehiclePhoto), + ]); + if (imageResults[0]['success']) { + setState(() { + // 签字图片 - 明确指定类型 + List signData = imageResults[0]['data'] as List; + signList = signData.map((item) => item['filePath'].toString()).toList(); + //行驶证图片 + List licenseData = imageResults[1]['data'] as List; + licenseList = licenseData.map((item) => item['filePath'].toString()).toList(); + // 车辆图片 + List attachmentData = imageResults[2]['data'] as List; + attachmentList = attachmentData.map((item) => item['filePath'].toString()).toList(); + + }); + } + }); + + }else{ + ToastUtil.showNormal(context, '加载数据失败'); + // _showMessage('加载数据失败'); + } + } catch (e) { + LoadingDialogHelper.hide(); + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载数据失败:$e'); + } + } + + + + + + + } diff --git a/lib/pages/home/doorAndCar/doorArea_type_page.dart b/lib/pages/home/doorAndCar/doorArea_type_page.dart index 2dfe43e..3534d96 100644 --- a/lib/pages/home/doorAndCar/doorArea_type_page.dart +++ b/lib/pages/home/doorAndCar/doorArea_type_page.dart @@ -70,7 +70,7 @@ class _DoorareaTypePageState extends State { } }, - text: '', + text: ' ', ), const Divider(), @@ -90,7 +90,7 @@ class _DoorareaTypePageState extends State { } }, - text: '', + text: ' ', ), const Divider(), diff --git a/lib/pages/home/doorAndCar/person/doorArea_person_add_page.dart b/lib/pages/home/doorAndCar/person/doorArea_person_add_page.dart index d221df7..291c9c5 100644 --- a/lib/pages/home/doorAndCar/person/doorArea_person_add_page.dart +++ b/lib/pages/home/doorAndCar/person/doorArea_person_add_page.dart @@ -1,17 +1,26 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/MultiDictValuesPicker.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/center_multi_picker.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart' hide Person; +import 'package:qhd_prevention/customWidget/department_picker_enterprise.dart'; +import 'package:qhd_prevention/customWidget/department_picker_three.dart'; +import 'package:qhd_prevention/customWidget/department_picker_two.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/photo_picker_row.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/modules/basic_info_api.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/person_selection_page.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart'; @@ -32,150 +41,33 @@ class DoorareaPersonApplyPage extends StatefulWidget { } class _DoorareaPersonApplyPageState extends State { - Map pd = {}; + bool _agreed = false; // 部门列表 List _deptList = []; List _personList = []; List signImages = []; + // 存储各单位的人员列表 + List> _personCache = []; + + String corpinfoId = "";//公司 + String corpinfoName = ""; + String buMenId = "";//部门 + String buMenName = ""; + String responsibleId="";//人 + String responsibleName=""; + @override void initState() { super.initState(); - _getDept(); + } - // 获取部门 - Future _getDept() async { - // try { - // final data = { - // 'eqCorpinfoId': widget.scanData['id'], - // // 'eqParentId': widget.scanData['corpinfoId'], - // }; - // final result = await BasicInfoApi.getDeptTree(data); - // if (result['success'] == true) { - // final list = result['data'] ?? []; - // if (list.length > 0) { - // setState(() { - // _deptList = list[0]['childrenList'] ?? []; - // }); - // } - // } - // } catch (e) {} - } - // 提交 - Future _saveSuccess() async { - if (!FormUtils.hasValue(pd, 'corpinfoId')) { - ToastUtil.showNormal(context, '请选择部门'); - return; - } - if (!FormUtils.hasValue(pd, 'postName')) { - ToastUtil.showNormal(context, '请输入岗位'); - return; - } - // try { - // final result = await BasicInfoApi.userFirmEntry(pd); - // LoadingDialogHelper.hide(); - // if (result['success'] == true) { - // ToastUtil.showNormal(context, '申请成功'); - // _relogin(); - // } else { - // ToastUtil.showNormal(context, result['errMessage']); - // } - // } catch (e) { - // LoadingDialogHelper.hide(); - // ToastUtil.showNormal(context, '操作失败,请重试'); - // } - } - Widget _addPersonWight(Person personData) { - return Stack( - children: [ - Card( - margin: EdgeInsets.only(bottom: 12), - color: Colors.white, - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - children: [ - // 第一行:姓名和部门 - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Text( - '姓名: ${personData.name??''}', - style: TextStyle( - fontSize: 14, - color: Colors.grey[700], - ), - ), - ), - // _buildInfoItem('姓名:', person['姓名'] ?? '',), - SizedBox(width: 24), - Expanded( - child: Text( - '部门: ${personData.group??''}', - style: TextStyle( - fontSize: 14, - color: Colors.grey[700], - ), - ), - ), - // _buildInfoItem('部门:', person['部门'] ?? ''), - ], - ), - // SizedBox(height: 8), - - // 第二行:删除 - // Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // CustomButton( - // height: 35, - // onPressed: () async { - // - // }, - // backgroundColor: Colors.red, - // textStyle: const TextStyle(color: Colors.black), - // buttonStyle:ButtonStyleType.primary, - // text: '删除') - // ], - // ), - ], - ), - ), - ), - Positioned( - top: 0, - right: 2, - child: Container( - padding: const EdgeInsets.all(2), - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Colors.red, - ), - - child: GestureDetector( - child: const Icon(Icons.close, size: 14, color: Colors.white,), - onTap: () { - setState(() { - _personList.remove(personData); - }); - }, - ), - ), - ), - ], - ); - } @override Widget build(BuildContext context) { @@ -190,65 +82,129 @@ class _DoorareaPersonApplyPageState extends State { ListItemFactory.createBuildSimpleSection('申请信息'), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( - label: '选择管辖单位:', + label: '选择港区:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['portAreaName'] ?? '请选择', isRequired: true, onTap: () async { - if (_deptList.isEmpty) { - ToastUtil.showNormal(context, '暂无部门信息'); + _getPortAreaType(); + }, + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '选择管辖单位:', + isEditable: true, + text: addData['jurisdictionalCorpName'] ?? '请选择', + isRequired: true, + onTap: () { + if( addData['portAreaId'].isEmpty){ + ToastUtil.showNormal(context, '请先选择港区'); return; } - final found = await BottomPicker.show( - context, - items: _deptList, - itemBuilder: - (i) => Text(i['name']!, textAlign: TextAlign.center), - initialIndex: 0, - ); - //FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (ctx) => DepartmentPickerEnterprise( + addData['portAreaId'], + onSelected: (id, name,pdId) async { + setState(() { + addData['jurisdictionalCorpId']= id; + addData['jurisdictionalCorpName']= name; + + addData['auditPersonCorpId']= id; + addData['auditPersonCorpName']= name; + addData['auditPersonDepartmentId']= ''; + addData['auditPersonDepartmentName']= ''; + addData['auditPersonUserId']=''; + addData['auditPersonUserName']=''; + + }); + }, + ), + ); - if (found != null) { - setState(() { - pd['departmentId'] = found['id']; - pd['departmentName'] = found['name']; - pd['corpinfoId'] = found['corpinfoId']; - pd['corpinfoName'] = found['corpinfoName']; - }); - } }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '选择管辖区域:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['closedAreaName']?? "请选择", isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '审核人员部门:', - isEditable: true, - text: pd['departmentName'] ?? '请选择', - isRequired: true, - onTap: () async {}, + onTap: () { + if(addData['jurisdictionalCorpName'].isEmpty){ + ToastUtil.showNormal(context, '请选择管辖单位'); + return ; + } + _getVisitPortArea(); + }, ), + // const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '审核人员部门:', + // isEditable: true, + // text: addData['auditDeptName'] ?? '请选择', + // isRequired: true, + // onTap: () { + // if ( addData['auditCorpName'].isEmpty) { + // ToastUtil.showNormal(context, '请先选择企业'); + // return; + // } + // showModalBottomSheet( + // context: context, + // isScrollControlled: true, + // barrierColor: Colors.black54, + // backgroundColor: Colors.transparent, + // builder: + // (ctx) => DepartmentPickerTwo( + // onSelected: (id, name,pdId) async { + // setState(() { + // addData['auditDeptId']= id; + // addData['auditDeptName']= name; + // + // addData['auditUserId']=''; + // addData['auditUserName']=''; + // + // }); + // // 拉取该单位的人员列表并缓存 + // final result = await HiddenDangerApi.getListTreePersonList(id); + // _personCache=List>.from( + // result['data'] as List, + // ); + // }, + // ), + // ); + // + // }, + // ), + // const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '审核人员:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['auditPersonUserName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + // if ( addData['auditPersonUserName'].isEmpty) { + // ToastUtil.showNormal(context, '请先选择部门'); + // return; + // } + + _getApproverList(); + }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '选择项目:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['projectName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + _getProjectNameList(); + }, ), const Divider(), Padding( @@ -256,7 +212,7 @@ class _DoorareaPersonApplyPageState extends State { child: ItemListWidget.selectableLineTitleTextRightButton( label: '选择时间:', isEditable: false, - text: pd['departmentName'] ?? '', + text: addData['auditAllTime'] ?? '', isRequired: true, onTap: () async {}, ), @@ -265,6 +221,9 @@ class _DoorareaPersonApplyPageState extends State { ItemListWidget.multiLineTitleTextField( label: '申请原因', isEditable: true, + onChanged: (value) { + addData['applyReason']=value; + }, ), const Divider(), @@ -285,9 +244,13 @@ class _DoorareaPersonApplyPageState extends State { height: 35, backgroundColor: Colors.blue, onPressed: () async { + if(addData['projectId'].isEmpty){ + ToastUtil.showNormal(context, '请先选择项目'); + return; + } final result = await Navigator.push( context, - MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList)), + MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList,addData['projectId']??'')), ); if (result != null) { @@ -313,7 +276,7 @@ class _DoorareaPersonApplyPageState extends State { side: const BorderSide(color: Colors.grey), onChanged: (value) { setState(() { - _agreed = value ?? false; + // _agreed = value ?? false; }); }, ), @@ -401,7 +364,92 @@ class _DoorareaPersonApplyPageState extends State { ); } + Widget _addPersonWight(Person personData) { + return Stack( + children: [ + Card( + margin: EdgeInsets.only(bottom: 12), + color: Colors.white, + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + children: [ + // 第一行:姓名和部门 + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + '姓名: ${personData.employeePersonUserName??''}', + style: TextStyle( + fontSize: 14, + color: Colors.grey[700], + ), + ), + ), + // _buildInfoItem('姓名:', person['姓名'] ?? '',), + SizedBox(width: 24), + Expanded( + child: Text( + '部门: ${personData.personDepartmentName??''}', + style: TextStyle( + fontSize: 14, + color: Colors.grey[700], + ), + ), + ), + // _buildInfoItem('部门:', person['部门'] ?? ''), + ], + ), + // SizedBox(height: 8), + + // 第二行:删除 + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // CustomButton( + // height: 35, + // onPressed: () async { + // + // }, + // backgroundColor: Colors.red, + // textStyle: const TextStyle(color: Colors.black), + // buttonStyle:ButtonStyleType.primary, + // text: '删除') + // ], + // ), + ], + ), + ), + ), + Positioned( + top: 0, + right: 2, + child: Container( + padding: const EdgeInsets.all(2), + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + + child: GestureDetector( + child: const Icon(Icons.close, size: 14, color: Colors.white,), + onTap: () { + setState(() { + _personList.remove(personData); + }); + }, + ), + ), + ), + ], + ); + } Widget _signListWidget() { return Column( @@ -495,5 +543,325 @@ class _DoorareaPersonApplyPageState extends State { } + Future _getPortAreaType() async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => MultiDictValuesPicker( + title: '港区', + dictType: 'HG_AUTH_AREA', + allowSelectParent: false, + onSelected: (id, name, extraData) { + setState(() { + addData['portAreaId'] = extraData?['dictValue'];//港区id + addData['portAreaName'] = name;//港区名称 + + }); + }, + ), + ).then((_) { + // 可选:FocusHelper.clearFocus(context); + }); + } + + + Future _getVisitPortArea() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getEnclosedAreaById(addData['jurisdictionalCorpId']); + LoadingDialogHelper.hide(); + if (raw['success'] ) { + List newList = raw['data'] ?? []; + + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + addData['closedAreaId']=id;//封闭区域id + addData['closedAreaName']=name;//封闭区域名称 + + }); + }, + ), + ); + }else{ + ToastUtil.showNormal(context, "获取列表失败"); + LoadingDialogHelper.hide(); + // _showMessage('反馈提交失败'); + // return ""; + } + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + // return ""; + LoadingDialogHelper.hide(); + } + } + + + Future _getApproverList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getApproverList( addData['auditPersonCorpId'],'','1','','');//addData['auditDeptId'] + LoadingDialogHelper.hide(); + if (raw['success'] ) { + List newList = raw['data'] ?? []; + + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + for(int i=0;i _getProjectNameList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getProjectNameList( ); + LoadingDialogHelper.hide(); + printLongString(jsonEncode(raw)); + if (raw['success'] ) { + final newList = raw['data'] ?? []; + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + addData['projectId']=id; + addData['projectName']=name; + //时间 + for(int i=0;i _saveSuccess() async { + try { + + if(addData['jurisdictionalCorpName'].isEmpty){ + ToastUtil.showNormal(context, '请选择管辖单位'); + return; + } + + if(addData['closedAreaName'].isEmpty){ + ToastUtil.showNormal(context, '请选择管辖区域'); + return; + } + + // if(addData['auditDeptName'].isEmpty){ + // ToastUtil.showNormal(context, '请选择审核人员部门'); + // return; + // } + + if(addData['auditPersonUserName'].isEmpty){ + ToastUtil.showNormal(context, '请选择审核人员'); + return; + } + + if(addData['projectName'].isEmpty){ + ToastUtil.showNormal(context, '请选择项目'); + return; + } + + if(addData['applyReason'].isEmpty){ + ToastUtil.showNormal(context, '请填写原因'); + return; + } + + if(_personList.isEmpty){ + ToastUtil.showNormal(context, '请选择人员'); + return; + } + + // 将 List 转换为 List> + List> jsonList = _personList.map((person) => person.toJson()).toList(); + // 再转换为 JSON 字符串 + addData['entourage'] = jsonEncode(jsonList); + + if(signImages.isEmpty){ + ToastUtil.showNormal(context, '请阅读《安全进港须知》并签字'); + return; + } + + LoadingDialogHelper.show(); + if(signImages.isNotEmpty){ + String licenseId= await _addImgFilesLicense(signImages, UploadFileType.enclosedAreaPersonnelApplicantSignature,''); + addData['informSignId']=licenseId; + } + + + final result = await DoorAndCarApi.enclosedPersonSave(addData); + LoadingDialogHelper.hide(); + if (result['success'] == true) { + ToastUtil.showNormal(context, '提交成功'); + Navigator.pop(context); + } else { + ToastUtil.showNormal(context, result['errMessage']); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '操作失败,请重试'); + } + } + + + Future _addImgFilesLicense(List imagePaths,UploadFileType type,String idType) async { + try { + + final raw = await FileApi.uploadFiles( imagePaths, type,idType); + if (raw['success'] ) { + return raw['data']['foreignKey']; + }else{ + // _showMessage('反馈提交失败'); + return ""; + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + return ""; + } + } + + + Map addData={ + "personBelongType": '3', //1股份2分公司3相关方4临时人员 + "levelOneMkmjId": '', //一级口门id + "levelTwoMkmjId": '', //二级口门id(封闭区域口门) + "closedAreaId": '', //封闭区域id + "closedAreaName": "", //封闭区域名称 + "visitStartTime": "", //访问起始时间 + "visitEndTime": "", //访问结束时间 + "jurisdictionalCorpId": '', //管辖单位名id + "jurisdictionalCorpName": "", //管辖单位名称 + "projectId": '', //项目id + "projectName": "", //项目名称 + "applyReason": "", //申请理由 + "entourage": "", //随行人员 + "informSignId": '', //告知签字 + "auditPersonCorpId": '', //审批人员所属企业ID + "auditPersonCorpName": "", //审批人员所属企业名称 + "auditPersonDepartmentId": '', //审批人员所属部门id + "auditPersonDepartmentName": "", //审批人员部门名称 + "auditPersonUserId": '', //审批人员id + "auditPersonUserName": "", //审批人员姓名 + "applyPersonUserName": "", //申请人员姓名 + "userFaceUrl": "", //申请人头像 + "userPhone": "", //申请人手机号 + "userCard": "", //申请人身份证号 + + "portAreaId": '', //港区id + "portAreaName": '', //港区名称 + "auditAllTime": '', //时间反显 + "personApplyList": [], + }; + + // Map addPersonData= { + // "personCorpId": '', //待审批人员所属企业ID + // "personCorpName": '', //待审批人员所属企业名称 + // "personDepartmentId": '', //待审批人员所属部门id + // "personDepartmentName": '', //待审批人员部门名称 + // "employeePersonUserId": '', //待审批人员id + // "employeePersonUserName": '', //待审批人员姓名 + // "userFaceUrl": '', //头像 + // "userPhone": '', //手机号 + // "userCard": '', //身份证号码 + // }; + + + } diff --git a/lib/pages/home/doorAndCar/person/doorArea_person_record_page.dart b/lib/pages/home/doorAndCar/person/doorArea_person_record_page.dart index e3386e0..523e153 100644 --- a/lib/pages/home/doorAndCar/person/doorArea_person_record_page.dart +++ b/lib/pages/home/doorAndCar/person/doorArea_person_record_page.dart @@ -1,4 +1,8 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/car/doorArea_car_add_page.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/car/firstlevel_car_add_page.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/car/onlyLook_car_application.dart'; @@ -28,43 +32,9 @@ class DoorareaPersonRecordPage extends StatefulWidget { class _DoorareaPersonRecordPageState extends State { // Data and state variables - // List list = []; - final List> list = [ - { - 'applicant': '封闭区域申请', - 'region': '申请人', - 'people': '人数', - 'startTime': '2025-10-29 00:00', - 'endTime': '2025-10-31 00:00', - 'license': '申请区域', - 'status': '待审核', - 'statusColor': Colors.orange, - }, - { - 'applicant': '封闭区域申请', - 'region': '申请人', - 'people': '人数', - 'startTime': '2025-10-29 00:00', - 'endTime': '2025-10-31 00:00', - 'license': '申请区域', - 'status': '待审核', - 'statusColor': Colors.orange, - }, - { - 'applicant': '封闭区域申请', - 'region': '申请人', - 'people': '人数', - 'startTime': '2025-10-29 00:00', - 'endTime': '2025-10-31 00:00', - 'license': '申请区域', - 'status': '待审核', - 'statusColor': Colors.orange, - }, + List list = []; - ]; - - - Map pd = {}; + // Map pd = {}; int currentPage = 1; int rows = 10; @@ -91,29 +61,75 @@ class _DoorareaPersonRecordPageState extends State { super.initState(); switch(widget.type){ - case 1: - case 3: - titleName='人员审核'; - buttonName='查看'; - isAdd=true; + case 1://1 进港口门申请(人) + titleName='人员审核'; + buttonName='查看'; + isAdd=true; + xgfDataApplyListData['processOrRecord']=1; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/firstLevelDoor/personnelApplication/list'; break; - case 2: - case 4: + case 2:// 2 进港口门申请记录(人) titleName='人员审核记录'; buttonName='查看'; isAdd=false; + xgfDataApplyListData['processOrRecord']=2; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/firstLevelDoor/personnelApplicationRecords/list'; break; - case 5: - case 7: + case 3://3 封闭区域口门申请(人) + titleName='人员审核'; + buttonName='查看'; + isAdd=true; + xgfDataApplyListData['processOrRecord']=1; + xgfDataApplyListData['personBelongType']=3; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/enclosedArea/apply/personnel/list'; + break; + case 4://4 封闭区域口门申请记录(人) + titleName='人员审核记录'; + buttonName='查看'; + isAdd=false; + xgfDataApplyListData['processOrRecord']=2; + xgfDataApplyListData['personBelongType']=3; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/enclosedArea/apply/personnelRecords/list'; + break; + case 5: //5 进港口门申请(车) titleName='车辆审核'; buttonName='查看'; isAdd=true; + xgfDataApplyListData['processOrRecord']=1; + xgfDataApplyListData['vehicleBelongType']='5'; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/firstLevelDoor/vehicleApplication/list'; break; - case 6: - case 8: + case 6: // 6 进港口门申请记录(车) titleName='车辆审核记录'; buttonName='查看'; isAdd=false; + xgfDataApplyListData['processOrRecord']=2; + xgfDataApplyListData['vehicleBelongType']='5'; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/firstLevelDoor/vehicleApplicationRecords/list'; + break; + case 7: // 7 封闭区域口门申请(车) + titleName='车辆审核'; + buttonName='查看'; + isAdd=true; + xgfDataApplyListData['processOrRecord']=1; + xgfDataApplyListData['personBelongType']=3; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/enclosedArea/apply/vehicle/list'; + break; + case 8: // 8 封闭区域口门申请记录(车) + titleName='车辆审核记录'; + buttonName='查看'; + isAdd=false; + xgfDataApplyListData['processOrRecord']=2; + xgfDataApplyListData['personBelongType']=3; + + xgfDataApplyListData['menuPath'] = '/primeport/container/stakeholder/enclosedArea/apply/vehicleRecords/list'; break; } @@ -142,31 +158,45 @@ class _DoorareaPersonRecordPageState extends State { Future _fetchData() async { - // if (isLoading) return; - // setState(() => isLoading = true); - // - // try { - // final data = { - // "likeCorpName": searchKeywords, - // "pageSize": rows, - // "pageIndex": currentPage, - // }; - // final response = await RiskwarningApi.getCorpRiskStatement(); - // final firmRes = await RiskwarningApi.getCorpRiskList(data); - // setState(() { - // pd = response['data']; - // if (currentPage == 1) { - // list = firmRes['data']; - // } else { - // list.addAll(firmRes['data']); - // } - // totalPage = firmRes['totalPages'] ?? 1; - // isLoading = false; - // }); - // } catch (e) { - // print('Error fetching data: $e'); - // setState(() => isLoading = false); - // } + if (isLoading) return; + setState(() => isLoading = true); + + try { + xgfDataApplyListData['projectName']=searchKeywords.isNotEmpty?searchKeywords:''; + xgfDataApplyListData['pageIndex']=currentPage; + + final Map result; + if(widget.type==1||widget.type==2) { + result = await DoorAndCarApi.getXgfPersonAuditList(xgfDataApplyListData); + }else if(widget.type==3||widget.type==4) { + result = await DoorAndCarApi.getEnclosedPersonList(xgfDataApplyListData); + }else if(widget.type==5||widget.type==6) { + result = await DoorAndCarApi.getXgfCarAuditList(xgfDataApplyListData); + }else if(widget.type==7||widget.type==8) { + result = await DoorAndCarApi.getEnclosedCarList(xgfDataApplyListData); + } else{ + result = await DoorAndCarApi.getXgfPersonAuditList(xgfDataApplyListData); + } + + if (result['success'] ) { + setState(() { + // pd = result['data']; + if (currentPage == 1) { + list = result['data']; + } else { + list.addAll(result['data']); + } + totalPage = result['totalCount'] ?? 1; + isLoading = false; + }); + }else{ + ToastUtil.showNormal(context, '加载数据失败'); + // _showMessage('加载数据失败'); + } + } catch (e) { + print('Error fetching data: $e'); + setState(() => isLoading = false); + } } void _search() { @@ -180,22 +210,22 @@ class _DoorareaPersonRecordPageState extends State { switch(widget.type){ case 1: case 2: - await pushPage(OnlylookDoorareaPerson(2), context); + await pushPage(OnlylookPersonApplication(3,item['id']), context); break; case 3: case 4: - await pushPage(OnlylookPersonApplication(3), context); + await pushPage(OnlylookDoorareaPerson(2,item['id']), context); break; case 5: case 6: - await pushPage(OnlylookDoorareaCar(2), context); + await pushPage(OnlylookCarApplication(4,item['vehicleApplyId']), context); break; case 7: case 8: - await pushPage(CarApplicationRecord(4), context); + await pushPage(OnlylookDoorareaCar(2,item['id']), context); break; } - _fetchData(); + // _fetchData(); } Widget _buildListItem(Map item,int index) { @@ -221,7 +251,7 @@ class _DoorareaPersonRecordPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - item['applicant'], + item['projectName']??'', // '${item['name']??''}', style: const TextStyle( fontSize: 16, @@ -231,20 +261,23 @@ class _DoorareaPersonRecordPageState extends State { const SizedBox(height: 8), - Text('申请人:${item['region']??""}',), + if(widget.type==1||widget.type==2||widget.type==5||widget.type==6) + Text('审核人:${item['auditUserName']??""}',), + if(widget.type==3||widget.type==4||widget.type==7||widget.type==8) + Text('审核人:${item['auditPersonUserName']??""}',), if(widget.type==5||widget.type==6||widget.type==7||widget.type==8)...[ const SizedBox(height: 8), - Text('车牌号:${item['region']??""}',), + Text('车牌号:${item['licenceNo']??""}',), ], const SizedBox(height: 8), - Text('申请区域:${item['license']??""}',), + Text('申请区域:${_getAccessArea(item)}',), const SizedBox(height: 8), - Text('时间范围:自${item['startTime']??""}至${item['endTime']??""}止'), + Text('时间范围:自${item['visitStartTime']??""}至${item['visitEndTime']??""}止'), const SizedBox(height: 8), - Text('审核状态:${item['status']??""}',), - if(widget.type==2||widget.type==4||widget.type==6||widget.type==8)...[ + Text('审核状态:${_getReviewStatus(item)}',), + if((widget.type==2||widget.type==4||widget.type==6||widget.type==8)&&(item['reasonsRefusal']??"").isNotEmpty)...[ const SizedBox(height: 8), - Text('驳回原因:${item['status']??""}',), + Text('驳回原因:${item['reasonsRefusal']??""}',), ], @@ -308,18 +341,19 @@ class _DoorareaPersonRecordPageState extends State { onPressed: () async { switch(widget.type){ case 1: - await pushPage(DoorareaPersonApplyPage(), context); - break; - case 3: await pushPage(FirstlevelPersonAddPage(), context); break; - case 5: - await pushPage(DoorareaCarAddPage(), context); + case 3: + await pushPage(DoorareaPersonApplyPage(), context); break; - case 7: + case 5: await pushPage(FirstlevelCarAddPage(), context); break; + case 7: + await pushPage(DoorareaCarAddPage(), context); + break; } + currentPage=1; _fetchData(); }, @@ -335,33 +369,33 @@ class _DoorareaPersonRecordPageState extends State { child: Row( children: [ // 可点击的筛选图标 - GestureDetector( - onTap: () { - // 筛选逻辑 - }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Row( - children: [ - Text( - '筛选', - style: TextStyle( - fontSize: 14, - color: Colors.black, - ), - ), - const SizedBox(width: 2), - Icon( - Icons.expand_more, - size: 20, - color: Colors.black, - ), - - ], - ), - ), - ), - const SizedBox(width: 8), + // GestureDetector( + // onTap: () { + // // 筛选逻辑 + // }, + // child: Container( + // padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + // child: Row( + // children: [ + // Text( + // '筛选', + // style: TextStyle( + // fontSize: 14, + // color: Colors.black, + // ), + // ), + // const SizedBox(width: 2), + // Icon( + // Icons.expand_more, + // size: 20, + // color: Colors.black, + // ), + // + // ], + // ), + // ), + // ), + // const SizedBox(width: 8), // 搜索框 Expanded( child: SearchBarWidget( @@ -391,4 +425,68 @@ class _DoorareaPersonRecordPageState extends State { ), ); } + + + String _getAccessArea(final item) { + //审核状态(1:审核中;2审核通过; 3:审核驳回,4无需审批(检查部门车辆)) + String type =''; + if(widget.type==1||widget.type==2||widget.type==5||widget.type==6){ + type = item["gateLevelAuthArea"]??''; + if(type.isNotEmpty){ + // 解析 JSON + Map jsonData = json.decode(type); + List areaList = jsonData['area']; + // 提取 value 值列表 + List targetValues = areaList.map((item) => item['value'].toString()).toList(); + return targetValues.join(','); + }else{ + return ''; + } + }else{ + return item["closedAreaName"]??''; + } + + } + + String _getReviewStatus(final item) { + //审核状态(1:审核中;2审核通过; 3:审核驳回,4无需审批(长期人员)) + // int type = item['auditFlag']??""; + + int type =0; + if(widget.type==1||widget.type==2||widget.type==3||widget.type==4){ + type = int.tryParse(item['auditFlag']?.toString() ?? '') ?? 0; + }else{ + type = int.tryParse(item['auditStatus']?.toString() ?? '') ?? 0; + } + if (1 == type) { + return "待审核"; + } else if (2 == type) { + return "审核通过"; + } else if (3 == type) { + return "审核驳回"; + } else if (4 == type) { + return "无需审批(长期人员)"; + } else { + return "无需审批"; + } + } + + Map xgfDataApplyListData={ + "processOrRecord": '', //1审批中数据,2审批记录 + "projectName": "", //项目名称 + "applyCorpName": "", //申请单位名称 + "userCard": "", //临时访客h5二维码用根据身份证号查询申请记录 + "pageSize": 10, + "pageIndex": 1, + + "licenceNo": "", //车牌号 + "vehicleBelongTypeArr": "", //车辆所属类型 1-股份员工车辆,2-股份单位车辆,3-分公司员工车辆,4-分公司单位车辆 ,5-相关方车辆, 6:临时车辆,7检查部门车辆 + "vehicleBelongType": "", //车辆所属类型 1-股份员工车辆,2-股份单位车辆,3-分公司员工车辆,4-分公司单位车辆 ,5-相关方车辆, 6:临时车辆,7检查部门车辆 + "personBelongType": '', //1股份2分公司3相关方4临时人员 + + }; + + + + } diff --git a/lib/pages/home/doorAndCar/person/firstlevel_person_add_page.dart b/lib/pages/home/doorAndCar/person/firstlevel_person_add_page.dart index bbd9fce..2569b5b 100644 --- a/lib/pages/home/doorAndCar/person/firstlevel_person_add_page.dart +++ b/lib/pages/home/doorAndCar/person/firstlevel_person_add_page.dart @@ -1,17 +1,22 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/center_multi_picker.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_picker_three.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/photo_picker_row.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/modules/basic_info_api.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/person_selection_page.dart'; import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart'; @@ -32,7 +37,7 @@ class FirstlevelPersonAddPage extends StatefulWidget { } class _FirstlevelPersonAddPageState extends State { - Map pd = {}; + bool _agreed = false; // 部门列表 @@ -40,142 +45,20 @@ class _FirstlevelPersonAddPageState extends State { List _personList = []; List signImages = []; + String corpinfoId = "";//公司 + String corpinfoName = ""; + String buMenId = "";//部门 + String buMenName = ""; + String responsibleId="";//人 + String responsibleName=""; + @override void initState() { super.initState(); - _getDept(); + _getUserData(); } - // 获取部门 - Future _getDept() async { - // try { - // final data = { - // 'eqCorpinfoId': widget.scanData['id'], - // // 'eqParentId': widget.scanData['corpinfoId'], - // }; - // final result = await BasicInfoApi.getDeptTree(data); - // if (result['success'] == true) { - // final list = result['data'] ?? []; - // if (list.length > 0) { - // setState(() { - // _deptList = list[0]['childrenList'] ?? []; - // }); - // } - // } - // } catch (e) {} - } - // 提交 - Future _saveSuccess() async { - if (!FormUtils.hasValue(pd, 'corpinfoId')) { - ToastUtil.showNormal(context, '请选择部门'); - return; - } - if (!FormUtils.hasValue(pd, 'postName')) { - ToastUtil.showNormal(context, '请输入岗位'); - return; - } - // try { - // final result = await BasicInfoApi.userFirmEntry(pd); - // LoadingDialogHelper.hide(); - // if (result['success'] == true) { - // ToastUtil.showNormal(context, '申请成功'); - // _relogin(); - // } else { - // ToastUtil.showNormal(context, result['errMessage']); - // } - // } catch (e) { - // LoadingDialogHelper.hide(); - // ToastUtil.showNormal(context, '操作失败,请重试'); - // } - } - - Widget _addPersonWight(Person personData) { - return Stack( - children: [ - Card( - margin: EdgeInsets.only(bottom: 12), - color: Colors.white, - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - children: [ - // 第一行:姓名和部门 - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Text( - '姓名: ${personData.name??''}', - style: TextStyle( - fontSize: 14, - color: Colors.grey[700], - ), - ), - ), - // _buildInfoItem('姓名:', person['姓名'] ?? '',), - SizedBox(width: 24), - Expanded( - child: Text( - '部门: ${personData.group??''}', - style: TextStyle( - fontSize: 14, - color: Colors.grey[700], - ), - ), - ), - - // _buildInfoItem('部门:', person['部门'] ?? ''), - ], - ), - // SizedBox(height: 8), - - // 第二行:删除 - // Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // CustomButton( - // height: 35, - // onPressed: () async { - // - // }, - // backgroundColor: Colors.red, - // textStyle: const TextStyle(color: Colors.black), - // buttonStyle:ButtonStyleType.primary, - // text: '删除') - // ], - // ), - ], - ), - ), - ), - Positioned( - top: 0, - right: 2, - child: Container( - padding: const EdgeInsets.all(2), - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Colors.red, - ), - - child: GestureDetector( - child: const Icon(Icons.close, size: 14, color: Colors.white,), - onTap: () { - setState(() { - _personList.remove(personData); - }); - }, - ), - ), - ), - ], - ); - } @override Widget build(BuildContext context) { @@ -192,17 +75,21 @@ class _FirstlevelPersonAddPageState extends State { ItemListWidget.selectableLineTitleTextRightButton( label: '选择项目名称:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['projectName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + _getProjectNameList(); + }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '审核人员:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['auditUserName'] ?? '请选择', isRequired: true, - onTap: () async {}, + onTap: () { + _getApproverList(); + }, ), const Divider(), Padding( @@ -210,7 +97,7 @@ class _FirstlevelPersonAddPageState extends State { child: ItemListWidget.selectableLineTitleTextRightButton( label: '选择时间:', isEditable: false, - text: pd['departmentName'] ?? '', + text: addData['auditAllTime'] ?? '', isRequired: true, onTap: () async {}, ), @@ -219,19 +106,23 @@ class _FirstlevelPersonAddPageState extends State { ItemListWidget.selectableLineTitleTextRightButton( label: '访问港区:', isEditable: true, - text: pd['departmentName'] ?? '请选择', + text: addData['gateLevelAuthAreaName'] ?? '请选择', isRequired: true, - onTap: () async {}, - ), - const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '选择范围:', - isEditable: true, - text: pd['departmentName'] ?? '请选择', - isRequired: true, - onTap: () async {}, + onTap: () { + _getVisitPortArea(); + }, ), // const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '选择范围:', + // isEditable: true, + // text: pd['departmentName'] ?? '请选择', + // isRequired: true, + // onTap: () { + // + // }, + // ), + // const Divider(), // ItemListWidget.multiLineTitleTextField( // label: '申请原因', // isEditable: true, @@ -255,9 +146,13 @@ class _FirstlevelPersonAddPageState extends State { height: 35, backgroundColor: Colors.blue, onPressed: () async { + if(addData['projectId'].isEmpty){ + ToastUtil.showNormal(context, '请先选择项目'); + return; + } final result = await Navigator.push( context, - MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList)), + MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList,addData['projectId'])), ); if (result != null) { @@ -283,7 +178,7 @@ class _FirstlevelPersonAddPageState extends State { side: const BorderSide(color: Colors.grey), onChanged: (value) { setState(() { - _agreed = value ?? false; + // _agreed = value ?? false; }); }, ), @@ -372,6 +267,93 @@ class _FirstlevelPersonAddPageState extends State { } + Widget _addPersonWight(Person personData) { + return Stack( + children: [ + Card( + margin: EdgeInsets.only(bottom: 12), + color: Colors.white, + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + children: [ + // 第一行:姓名和部门 + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + '姓名: ${personData.employeePersonUserName??''}', + style: TextStyle( + fontSize: 14, + color: Colors.grey[700], + ), + ), + ), + // _buildInfoItem('姓名:', person['姓名'] ?? '',), + SizedBox(width: 24), + Expanded( + child: Text( + '部门: ${personData.personDepartmentName??''}', + style: TextStyle( + fontSize: 14, + color: Colors.grey[700], + ), + ), + ), + + // _buildInfoItem('部门:', person['部门'] ?? ''), + ], + ), + // SizedBox(height: 8), + + // 第二行:删除 + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // CustomButton( + // height: 35, + // onPressed: () async { + // + // }, + // backgroundColor: Colors.red, + // textStyle: const TextStyle(color: Colors.black), + // buttonStyle:ButtonStyleType.primary, + // text: '删除') + // ], + // ), + ], + ), + ), + ), + Positioned( + top: 0, + right: 2, + child: Container( + padding: const EdgeInsets.all(2), + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + + child: GestureDetector( + child: const Icon(Icons.close, size: 14, color: Colors.white,), + onTap: () { + setState(() { + _personList.remove(personData); + }); + }, + ), + ), + ), + ], + ); + } + Widget _signListWidget() { return Column( @@ -465,5 +447,344 @@ class _FirstlevelPersonAddPageState extends State { } + Future _getUserData() async { + try { + + final raw = await AuthApi.getUserData( ); + if (raw['success'] ) { + setState(() { + corpinfoId = raw['data']['corpinfoId'];//公司 + corpinfoName = raw['data']['corpinfoName']; + buMenId = raw['data']['departmentId']; + buMenName = raw['data']['departmentName']; + responsibleId=raw['data']['id']; + responsibleName=raw['data']['name']; + + addData['vehicleCorpId']=corpinfoId;//所属企业 + addData['vehicleCorpName']=corpinfoName; + + }); + + }else{ + ToastUtil.showNormal(context, "获取个人信息失败"); + + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + + } + } + + + + Future _getProjectNameList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getProjectNameList( ); + LoadingDialogHelper.hide(); + printLongString(jsonEncode(raw)); + if (raw['success'] ) { + final newList = raw['data'] ?? []; + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) { + setState(() { + addData['projectId']=id; + addData['projectName']=name; + //时间 + for(int i=0;i _getApproverList() async { + try { + LoadingDialogHelper.show(); + final raw = await DoorAndCarApi.getApproverList( corpinfoId,'','1','',''); + LoadingDialogHelper.hide(); + if (raw['success'] ) { + List newList = raw['data'] ?? []; + + for(int i=0;i DepartmentPickerThree( + listdata: newList, + onSelected: (id, name,pdId) async { + setState(() { + for(int i=0;i _getVisitPortArea() async { + + List result = []; + // 计算预选索引 + final List initialIndices = []; + + final resultList = await BasicInfoApi.getDictValues('HG_AUTH_AREA'); + List raw = resultList["data"] as List; + + result = raw.map((item) => item['dictLabel'].toString()).toList(); + + if(addData['gateLevelAuthArea'].isNotEmpty){ + // 解析 JSON + Map jsonData = json.decode(addData['gateLevelAuthArea']); + List areaList = jsonData['area']; + // 提取 value 值列表 + List targetValues = areaList.map((item) => item['value'].toString()).toList(); + + // 遍历目标值,在 raw 中查找匹配 + for (String targetValue in targetValues) { + for (int i = 0; i < raw.length; i++) { + var item = raw[i]; + if (item != null && item is Map && item.containsKey('dictLabel')) { + if (item['dictLabel'].toString() == targetValue) { + initialIndices.add(i);// 记录索引位置(从0开始) + break; + } + } + } + } + } + + + // 显示选择器 + final selectedItems = await CenterMultiPicker.show( + context, + items: result, + itemBuilder: (item) => Text( + item, + style: const TextStyle(fontSize: 16), + ), + initialSelectedIndices: initialIndices, // 设置预选索引 + maxSelection: null, // 不限制选择数量 + allowEmpty: true, + title: '访问港区范围', + ); + + // 处理选择结果 + if (selectedItems != null) { + setState(() { + addData['gateLevelAuthAreaName'] = selectedItems.join(','); + + // 构建新的 JSON 数据 + List> areaList = []; + + // 遍历选中的项目,在 raw 中查找对应的完整数据 + for (String selectedItem in selectedItems) { + for (var item in raw) { + if (item != null && item is Map && item.containsKey('dictLabel')) { + if (item['dictLabel'].toString() == selectedItem) { + // 找到匹配的数据,添加 value(dictLabel) 和 bianma(dictValue) + areaList.add({ + 'value': item['dictLabel'].toString(), + 'bianma': item['dictValue'].toString(), // 或者使用 toString() 如果需要字符串 + }); + break; + } + } + } + } + + // 构建最终的 JSON 字符串 + Map jsonData = { + 'area': areaList + }; + + // 将 Map 转换为 JSON 字符串并保存 + addData['gateLevelAuthArea'] = json.encode(jsonData); + + }); + } + + } + + Future _addImgFilesLicense(List imagePaths,UploadFileType type,String idType) async { + try { + + final raw = await FileApi.uploadFiles( imagePaths, type,idType); + if (raw['success'] ) { + return raw['data']['foreignKey']; + }else{ + // _showMessage('反馈提交失败'); + return ""; + } + + } catch (e) { + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载首页数据失败:$e'); + return ""; + } + } + + + // 提交 + Future _saveSuccess() async { + try { + + if(addData['projectName'].isEmpty){ + ToastUtil.showNormal(context, '请选择选择项目名称'); + return; + } + + if(addData['auditUserName'].isEmpty){ + ToastUtil.showNormal(context, '请选择审核人员'); + return; + } + + + if(addData['gateLevelAuthAreaName'].isEmpty){ + ToastUtil.showNormal(context, '请选择访问港区'); + return; + } + + if(_personList.isEmpty){ + ToastUtil.showNormal(context, '请选择人员'); + return; + } + + // 将 List 转换为 List> + addData['personApplyList'] = _personList.map((person) => person.toJson()).toList(); + + if(signImages.isEmpty){ + ToastUtil.showNormal(context, '请阅读《安全进港须知》并签字'); + return; + } + + if(signImages.isNotEmpty){ + String licenseId= await _addImgFilesLicense(signImages, UploadFileType.gateAccessPersonnelApplicantSignature,''); + addData['informSignId']=licenseId; + } + + LoadingDialogHelper.show(); + final result = await DoorAndCarApi.xgfPersonSave(addData); + LoadingDialogHelper.hide(); + if (result['success'] == true) { + ToastUtil.showNormal(context, '提交成功'); + Navigator.pop(context); + } else { + ToastUtil.showNormal(context, result['errMessage']); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '操作失败,请重试'); + } + } + + + + Map addData={ + "personBelongType": '3', //1股份2分公司3相关方4临时人员 + "gateLevelAuthArea": '', //授权范围港区与区域 + "gateLevelAuthAreaName": "", //访问地区范围反显 + "visitStartTime": '', //访问起始时间 + "visitEndTime": '', //访问结束时间 + "informSignId": '', //告知签字 + "projectId": '', //项目id + "mkmjId": '', //口门id,临时申请用。 + "projectName": '', //项目名称 + "auditCorpId": '', //审核企业 + "auditCorpName": '', //审核企业名称 + "auditDeptId": '', //审核部门 + "auditDeptName": '', //审核部门名称 + "auditUserId": '', //审核用户 + "auditUserName": '', //审核用户名称 + "auditAllTime": '', //时间反显 + "personApplyList": [], + }; + + Map addPersonData= { + "personCorpId": '', //待审批人员所属企业ID + "personCorpName": '', //待审批人员所属企业名称 + "personDepartmentId": '', //待审批人员所属部门id + "personDepartmentName": '', //待审批人员部门名称 + "employeePersonUserId": '', //待审批人员id + "employeePersonUserName": '', //待审批人员姓名 + "userFaceUrl": '', //头像 + "userPhone": '', //手机号 + "userCard": '', //身份证号码 + }; + + + } diff --git a/lib/pages/home/doorAndCar/person/onlyLook_doorarea_person.dart b/lib/pages/home/doorAndCar/person/onlyLook_doorarea_person.dart index 8d1a88e..c772c3d 100644 --- a/lib/pages/home/doorAndCar/person/onlyLook_doorarea_person.dart +++ b/lib/pages/home/doorAndCar/person/onlyLook_doorarea_person.dart @@ -1,10 +1,14 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; import 'package:qhd_prevention/tools/tools.dart'; @@ -12,9 +16,10 @@ import 'package:shared_preferences/shared_preferences.dart'; class OnlylookDoorareaPerson extends StatefulWidget { - const OnlylookDoorareaPerson(this.type, {super.key}); + const OnlylookDoorareaPerson(this.type, this.id, {super.key}); final int type;//1 审核 2 查看 + final String id; @override State createState() => _OnlylookDoorareaPersonState(); @@ -22,50 +27,23 @@ class OnlylookDoorareaPerson extends StatefulWidget { class _OnlylookDoorareaPersonState extends State { - // 模拟申请信息数据 - final Map applicationInfo = { - 'mingcheng': '项目名称', - 'bumen': '审核人员部门', - 'renyuan': '审核人员', - 'shijian': '2024-01-01 至 2024-12-31', - 'gangqu': '访问港区', - 'diqu': '访问地区', - }; + // 模拟申请信息数据 + dynamic applicationInfo = {}; // 模拟人员列表数据 - final List> personnelList = [ - { - 'name': '张三', - 'bumen': '技术部', - 'isPei': '是', - 'wan': 'A区、B区', - }, - { - 'name': '李四', - 'bumen': '安全部', - 'isPei': '否', - 'wan': 'C区', - }, - { - 'name': '王五', - 'bumen': '运营部', - 'isPei': '是', - 'wan': 'A区、C区、D区', - }, - ]; + List personnelList = []; + //签字图片 + List signList = []; @override void initState() { super.initState(); - - + _getXgfApplyInfoById(); } - - @override Widget build(BuildContext context) { return Scaffold( @@ -179,7 +157,7 @@ class _OnlylookDoorareaPersonState extends State { label: '管辖单位:', isEditable: false, horizontalnum:0, - text: applicationInfo['mingcheng'] ?? '', + text: applicationInfo['jurisdictionalCorpName'] ?? '', onTap: () {}, ), const Divider(), @@ -188,7 +166,8 @@ class _OnlylookDoorareaPersonState extends State { label: '管辖区域:', isEditable: false, horizontalnum:0, - text: applicationInfo['bumen'] ?? '', + text: applicationInfo['closedAreaName'] ?? '', + // text: _getAccessArea(applicationInfo), onTap: () {}, ), const Divider(), @@ -197,7 +176,7 @@ class _OnlylookDoorareaPersonState extends State { label: '审核人员部门:', isEditable: false, horizontalnum:0, - text: applicationInfo['renyuan'] ?? '', + text: applicationInfo['auditPersonDepartmentName'] ?? '', onTap: () {}, ), const Divider(), @@ -206,7 +185,7 @@ class _OnlylookDoorareaPersonState extends State { label: '审核人员:', isEditable: false, horizontalnum:0, - text: applicationInfo['diqu'] ?? '', + text: applicationInfo['auditPersonUserName'] ?? '', onTap: () {}, ), @@ -217,7 +196,7 @@ class _OnlylookDoorareaPersonState extends State { label: '所属项目:', isEditable: false, horizontalnum:0, - text: applicationInfo['gangqu'] ?? '', + text: applicationInfo['projectName'] ?? '', onTap: () {}, ), const Divider(), @@ -226,7 +205,7 @@ class _OnlylookDoorareaPersonState extends State { label: '时间范围:', isEditable: false, horizontalnum:0, - text: applicationInfo['shijian'] ?? '', + text: _changeTime(applicationInfo), onTap: () {}, ), const Divider(), @@ -234,7 +213,7 @@ class _OnlylookDoorareaPersonState extends State { ItemListWidget.twoRowTitleText( label: '申请原因:', - text: applicationInfo['shijian']??'', + text: applicationInfo['applyReason']??'', horizontalInset:0, ), const Divider(), @@ -267,11 +246,12 @@ class _OnlylookDoorareaPersonState extends State { // 人员列表 ...personnelList.map((person) => _buildPersonCard(person)), + if(signList.isNotEmpty) Container( height: 150, padding: EdgeInsets.all(8), alignment: Alignment.center, - child: Image.network('${ApiService.baseImgPath}1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'), + child: Image.network('${ApiService.baseImgPath}${signList[0]}'), ), @@ -299,7 +279,7 @@ class _OnlylookDoorareaPersonState extends State { children: [ Expanded( child: Text( - '姓名: ${person['name']}', + '姓名: ${person['employeePersonUserName']}', style: TextStyle( fontSize: 14, color: Colors.grey[700], @@ -310,7 +290,7 @@ class _OnlylookDoorareaPersonState extends State { SizedBox(width: 24), Expanded( child: Text( - '部门: ${person['bumen']}', + '部门: ${person['personDepartmentName']}', style: TextStyle( fontSize: 14, color: Colors.grey[700], @@ -344,8 +324,69 @@ class _OnlylookDoorareaPersonState extends State { ); } + String _getAccessArea(final item) { + //审核状态(1:审核中;2审核通过; 3:审核驳回,4无需审批(检查部门车辆)) + String type = item["gateLevelAuthArea"]??''; + if(type.isNotEmpty){ + // 解析 JSON + Map jsonData = json.decode(type); + List areaList = jsonData['area']; + // 提取 value 值列表 + List targetValues = areaList.map((item) => item['value'].toString()).toList(); + return targetValues.join(','); + }else{ + return ''; + } + } + + String _changeTime(item) { + String timeStart=item['visitStartTime']??''; + String timeEnd=item['visitEndTime']??''; + if(timeStart.isNotEmpty&&timeEnd.isNotEmpty){ + return '$timeStart 至 $timeEnd'; + }else{ + return ''; + } + + } + Future _getXgfApplyInfoById() async { + try { + LoadingDialogHelper.show(); + final Map result= await DoorAndCarApi.getEnclosedPersonById(widget.id); + LoadingDialogHelper.hide(); + if (result['success'] ) { + + // final dynamic newList = result['data'] ; + + setState(() async { + applicationInfo=result['data']; + personnelList=applicationInfo['personApplyList']??[]; + + final imageResults = await Future.wait([ + FileApi.getImagePath(applicationInfo['informSignId'], UploadFileType.enclosedAreaPersonnelApplicantSignature), + ]); + if (imageResults[0]['success']) { + setState(() { + // 签字图片 - 明确指定类型 + List licenseData = imageResults[0]['data'] as List; + signList = licenseData.map((item) => item['filePath'].toString()).toList(); + + }); + } + }); + + }else{ + ToastUtil.showNormal(context, '加载数据失败'); + // _showMessage('加载数据失败'); + } + } catch (e) { + LoadingDialogHelper.hide(); + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载数据失败:$e'); + } + } } diff --git a/lib/pages/home/doorAndCar/person/onlyLook_person_application.dart b/lib/pages/home/doorAndCar/person/onlyLook_person_application.dart index 5b4ee0b..dcbc04d 100644 --- a/lib/pages/home/doorAndCar/person/onlyLook_person_application.dart +++ b/lib/pages/home/doorAndCar/person/onlyLook_person_application.dart @@ -1,17 +1,23 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; +import 'package:qhd_prevention/tools/tools.dart'; class OnlylookPersonApplication extends StatefulWidget { - const OnlylookPersonApplication(this.type, {super.key}); + const OnlylookPersonApplication(this.type, this.id,{super.key}); - final int type;//1 一级口门人员审核 2 一级口门车辆审核 3 审批记录(人员) 4 审批记录(车辆) + final int type;//1 审核 2 查看 + final String id; @override State createState() => _OnlylookPersonApplicationState(); @@ -19,37 +25,21 @@ class OnlylookPersonApplication extends StatefulWidget { class _OnlylookPersonApplicationState extends State { - // 模拟申请信息数据 - final Map applicationInfo = { - 'mingcheng': '项目名称', - 'bumen': '审核人员部门', - 'renyuan': '审核人员', - 'shijian': '2024-01-01 至 2024-12-31', - 'gangqu': '访问港区', - 'diqu': '访问地区', - }; + // 模拟申请信息数据 + dynamic applicationInfo = {}; // 模拟人员列表数据 - final List> personnelList = [ - { - 'name': '张三', - 'bumen': '技术部', - 'isPei': '是', - 'wan': 'A区、B区', - }, - { - 'name': '李四', - 'bumen': '安全部', - 'isPei': '否', - 'wan': 'C区', - }, - { - 'name': '王五', - 'bumen': '运营部', - 'isPei': '是', - 'wan': 'A区、C区、D区', - }, - ]; + List personnelList = []; + + //签字图片 + late List signList = []; + + @override + void initState() { + super.initState(); + + _getXgfApplyInfoById(); + } @override @@ -73,11 +63,12 @@ class _OnlylookPersonApplicationState extends State { // 人员列表 ...personnelList.map((person) => _buildPersonCard(person)), + if (signList.isNotEmpty) Container( height: 150, padding: EdgeInsets.all(8), alignment: Alignment.center, - child: Image.network('${ApiService.baseImgPath}1983773013086048256/202601/ai_recognition_images/a9c8e701f773470d8a8485ccb6fb35b7.jpg'), + child: Image.network('${ApiService.baseImgPath}${signList[0]}'), ), SizedBox(height: 10,), @@ -171,25 +162,25 @@ class _OnlylookPersonApplicationState extends State { label: '项目名称:', isEditable: false, horizontalnum:0, - text: applicationInfo['mingcheng'] ?? '', + text: applicationInfo['projectName'] ?? '', onTap: () {}, ), const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '审核人员部门:', - isEditable: false, - horizontalnum:0, - text: applicationInfo['bumen'] ?? '', - onTap: () {}, - ), - const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '审核人员部门:', + // isEditable: false, + // horizontalnum:0, + // text: applicationInfo['auditDeptName'] ?? '', + // onTap: () {}, + // ), + // const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '审核人员:', isEditable: false, horizontalnum:0, - text: applicationInfo['renyuan'] ?? '', + text: applicationInfo['auditUserName'] ?? '', onTap: () {}, ), const Divider(), @@ -198,7 +189,7 @@ class _OnlylookPersonApplicationState extends State { label: '时间范围:', isEditable: false, horizontalnum:0, - text: applicationInfo['shijian'] ?? '', + text: _changeTime(applicationInfo), onTap: () {}, ), const Divider(), @@ -207,26 +198,26 @@ class _OnlylookPersonApplicationState extends State { label: '访问港区:', isEditable: false, horizontalnum:0, - text: applicationInfo['gangqu'] ?? '', + text: _getAccessArea(applicationInfo), onTap: () {}, ), const Divider(), - ItemListWidget.selectableLineTitleTextRightButton( - label: '区域范围:', - isEditable: false, - horizontalnum:0, - text: applicationInfo['diqu'] ?? '', - onTap: () {}, - ), - const Divider(), + // ItemListWidget.selectableLineTitleTextRightButton( + // label: '区域范围:', + // isEditable: false, + // horizontalnum:0, + // text: applicationInfo['diqu'] ?? '', + // onTap: () {}, + // ), + // const Divider(), ], ), ); } - Widget _buildPersonCard(Map person) { + Widget _buildPersonCard(Map person) { return Card( margin: EdgeInsets.only(bottom: 12), color: Colors.white, @@ -244,7 +235,7 @@ class _OnlylookPersonApplicationState extends State { children: [ Expanded( child: Text( - '姓名: ${person['name']}', + '姓名: ${person['employeePersonUserName']}', style: TextStyle( fontSize: 14, color: Colors.grey[700], @@ -255,7 +246,7 @@ class _OnlylookPersonApplicationState extends State { SizedBox(width: 24), Expanded( child: Text( - '部门: ${person['bumen']}', + '部门: ${person['personDepartmentName']}', style: TextStyle( fontSize: 14, color: Colors.grey[700], @@ -266,23 +257,25 @@ class _OnlylookPersonApplicationState extends State { // _buildInfoItem('部门:', person['部门'] ?? ''), ], ), - SizedBox(height: 12), + + SizedBox(height: 12), // 第二行:是否培训和权限范围 Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildInfoItem('是否培训:', person['isPei'] ?? ''), // 改为 'isPei' + _buildInfoItem('是否培训:', '是'), // 改为 'isPei' SizedBox(width: 24), Expanded( child: _buildInfoItem( '现口门权限范围:', - person['wan'] ?? '', // 改为 'wan' + _getAccessArea(applicationInfo), // 改为 'wan' isExpanded: true, ), ), ], ), + ], ), ), @@ -326,4 +319,69 @@ class _OnlylookPersonApplicationState extends State { ), ); } + + String _getAccessArea(final item) { + //审核状态(1:审核中;2审核通过; 3:审核驳回,4无需审批(检查部门车辆)) + String type = item["gateLevelAuthArea"]??''; + if(type.isNotEmpty){ + // 解析 JSON + Map jsonData = json.decode(type); + List areaList = jsonData['area']; + // 提取 value 值列表 + List targetValues = areaList.map((item) => item['value'].toString()).toList(); + return targetValues.join(','); + }else{ + return ''; + } + } + + String _changeTime(item) { + String timeStart=item['visitStartTime']??''; + String timeEnd=item['visitEndTime']??''; + if(timeStart.isNotEmpty&&timeEnd.isNotEmpty){ + return '$timeStart 至 $timeEnd'; + }else{ + return ''; + } + + } + + Future _getXgfApplyInfoById() async { + try { + LoadingDialogHelper.show(); + final Map result= await DoorAndCarApi.getXgfAuditInfoById(widget.id); + LoadingDialogHelper.hide(); + if (result['success'] ) { + + // final dynamic newList = result['data'] ; + + setState(() async { + applicationInfo=result['data']; + personnelList=applicationInfo['personApplyList']??[]; + + final imageResults = await Future.wait([ + FileApi.getImagePath(applicationInfo['informSignId'], UploadFileType.gateAccessPersonnelApplicantSignature), + ]); + if (imageResults[0]['success']) { + setState(() { + // 签字图片 - 明确指定类型 + List licenseData = imageResults[0]['data'] as List; + signList = licenseData.map((item) => item['filePath'].toString()).toList(); + + }); + } + }); + + }else{ + ToastUtil.showNormal(context, '加载数据失败'); + // _showMessage('加载数据失败'); + } + } catch (e) { + LoadingDialogHelper.hide(); + // 出错时可以 Toast 或者在页面上显示错误状态 + print('加载数据失败:$e'); + } + } + + } diff --git a/lib/pages/home/doorAndCar/person_selection_page.dart b/lib/pages/home/doorAndCar/person_selection_page.dart index 5b3c03a..c22c8ce 100644 --- a/lib/pages/home/doorAndCar/person_selection_page.dart +++ b/lib/pages/home/doorAndCar/person_selection_page.dart @@ -1,17 +1,48 @@ import 'package:flutter/material.dart'; import 'package:lpinyin/lpinyin.dart'; import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/modules/doorAndCar_api.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; // 人员模型类 class Person { - final String id; - final String name; + final String employeePersonUserId; + final String employeePersonUserName; final String group; - Person({required this.id, required this.name, required this.group}); + final String personCorpId; + final String personCorpName; + final String personDepartmentId; + final String personDepartmentName; + final String userFaceUrl; + final String userPhone; + final String userCard; + + Person({required this.employeePersonUserId, required this.employeePersonUserName, required this.group, + required this.personCorpId, required this.personCorpName, required this.personDepartmentId, + required this.personDepartmentName, required this.userFaceUrl, required this.userPhone, + required this.userCard, }); + + // 添加 toJson 方法 + Map toJson() { + return { + 'employeePersonUserId': employeePersonUserId, + 'employeePersonUserName': employeePersonUserName, + 'group': group, + 'personCorpId': personCorpId, + 'personCorpName': personCorpName, + 'personDepartmentId': personDepartmentId, + 'personDepartmentName': personDepartmentName, + 'userFaceUrl': userFaceUrl, + 'userPhone': userPhone, + 'userCard': userCard, + }; + } + } + // 选择结果类 class SelectionPersonResult { final List selectedPersons; @@ -24,9 +55,11 @@ class SelectionPersonResult { } class PersonSelectionPage extends StatefulWidget { - const PersonSelectionPage(this.oldList, {super.key}); + const PersonSelectionPage(this.oldList,this.id, {super.key,this.isMoreSelect=true,}); final List oldList; + final String id; + final bool isMoreSelect;//是否多选 @override State createState() => _PersonSelectionPageState(); @@ -45,67 +78,10 @@ class _PersonSelectionPageState extends State { late final List sortedGroupKeys; // 存储每个分组的GlobalKey final Map groupKeys = {}; + List< dynamic> list =[]; - final List> list = [ - { - 'id': 'p1', - 'name': '赵', - }, - { - 'id': 'p2', - 'name': '钱', - }, - { - 'id': 'p3', - 'name': '算', - }, - { - 'id': 'p4', - 'name': '里', - }, - { - 'id': 'p5', - 'name': '周', - }, - { - 'id': 'p6', - 'name': '吴', - }, - { - 'id': 'p7', - 'name': '啊', - }, - { - 'id': 'p8', - 'name': '到', - }, - { - 'id': 'p9', - 'name': '噢', - }, - { - 'id': 'p10', - 'name': '饿', - }, - { - 'id': 'p11', - 'name': '不', - }, - { - 'id': 'p12', - 'name': '吃', - }, - { - 'id': 'p13', - 'name': '123', // 测试数字 - }, - { - 'id': 'p14', - 'name': '@@@', // 测试特殊字符 - }, - ]; + late Future _dataFuture; - // 选中状态 final Map selectedStates = {}; // 分组选中状态 @@ -125,28 +101,41 @@ class _PersonSelectionPageState extends State { void initState() { super.initState(); // 创建oldList的ID集合 - _oldSelectedIds = widget.oldList.map((person) => person.id).toSet(); - _initData(); + _oldSelectedIds = widget.oldList.map((person) => person.employeePersonUserId).toSet(); + _dataFuture = _initData(); + } - void _initData() { + Future _initData() async { + + + await _getProJectUserList(widget.id); // 根据姓名首字母分组 groupedPersons = {}; for (var item in list) { - String name = item['name']; - String id = item['id']; + String id = item['userId']??''; + String name = item['userName']??''; // 使用lpinyin获取姓名首字母 String firstLetter = _getFirstLetter(name); // 创建Person对象 Person person = Person( - id: id, - name: name, + employeePersonUserId: id, + employeePersonUserName: name, group: firstLetter, + + personCorpId: item['deptId']??'', + personCorpName: item['deptName']??'', + personDepartmentId: item['deptId']??'', + personDepartmentName: item['deptName']??'', + userFaceUrl: item['deptName']??'', + userPhone: item['phone']??'', + userCard: item['deptName']??'', ); + // 添加到对应分组 if (!groupedPersons.containsKey(firstLetter)) { groupedPersons[firstLetter] = []; @@ -156,7 +145,7 @@ class _PersonSelectionPageState extends State { // 对每个分组内的人员按姓名排序 groupedPersons.forEach((key, value) { - value.sort((a, b) => a.name.compareTo(b.name)); + value.sort((a, b) => a.employeePersonUserName.compareTo(b.employeePersonUserName)); }); // 对分组键进行排序(确保#在最后) @@ -178,7 +167,7 @@ class _PersonSelectionPageState extends State { for (var group in groupedPersons.keys) { for (var person in groupedPersons[group]!) { // 检查当前人员是否在oldList中,如果在则设置为true - selectedStates[person.id] = _oldSelectedIds.contains(person.id); + selectedStates[person.employeePersonUserId] = _oldSelectedIds.contains(person.employeePersonUserId); } // 初始化分组选中状态 groupSelectedStates[group] = false; @@ -191,6 +180,26 @@ class _PersonSelectionPageState extends State { } } + Future _getProJectUserList(String id) async { + try { + + final Map result = await DoorAndCarApi.getPeopleinProject(id); + + if (result['success'] ) { + setState(() { + list = result['data']; + }); + }else{ + ToastUtil.showNormal(context, '加载数据失败'); + // _showMessage('加载数据失败'); + } + } catch (e) { + print('Error fetching data: $e'); + } + } + + + // 使用lpinyin获取姓名的首字母 String _getFirstLetter(String name) { if (name.isEmpty) return '#'; @@ -276,8 +285,8 @@ class _PersonSelectionPageState extends State { // 更新分组的全选状态 void _updateGroupSelection(String group) { final groupPersons = groupedPersons[group]!; - final allSelected = groupPersons.every((person) => selectedStates[person.id] == true); - final anySelected = groupPersons.any((person) => selectedStates[person.id] == true); + final allSelected = groupPersons.every((person) => selectedStates[person.employeePersonUserId] == true); + final anySelected = groupPersons.any((person) => selectedStates[person.employeePersonUserId] == true); setState(() { groupSelectedStates[group] = allSelected; @@ -292,7 +301,7 @@ class _PersonSelectionPageState extends State { setState(() { groupSelectedStates[group] = newState; for (var person in groupPersons) { - selectedStates[person.id] = newState; + selectedStates[person.employeePersonUserId] = newState; } }); } @@ -305,7 +314,7 @@ class _PersonSelectionPageState extends State { for (var group in groupedPersons.keys) { final groupPersons = groupedPersons[group]!; final selectedInGroup = groupPersons - .where((person) => selectedStates[person.id] == true) + .where((person) => selectedStates[person.employeePersonUserId] == true) .toList(); if (selectedInGroup.isNotEmpty) { @@ -323,6 +332,13 @@ class _PersonSelectionPageState extends State { // 确认添加并返回 void _confirmSelection() { final result = _getSelectedResult(); + + // ,如果没有选择人员,提示用户 + if (result.selectedPersons.isEmpty) { + ToastUtil.showNormal(context, '请选择一个人'); + return; + } + Navigator.pop(context, result); } @@ -343,84 +359,95 @@ class _PersonSelectionPageState extends State { ), ],), - body: Column( - children: [ - Container( - color: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8), - child: SearchBarWidget( - showResetButton: true, - hintText: '请输入关键字', - resetButtonText: '重置', - onReset: () { - FocusScope.of(context).unfocus(); - _searchController.text = ''; - _search(); - }, - onSearch: (text) { - FocusScope.of(context).unfocus(); - _search(); - }, - controller: _searchController, - ), - ), + body: + FutureBuilder( + future: _dataFuture, + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Center(child: CircularProgressIndicator()); + } - - // 人员列表 - Expanded( - child: Row( - children: [ - // 左侧人员列表 - Expanded( - child: ListView.builder( - controller: _scrollController, - itemCount: sortedGroupKeys.length, - itemBuilder: (context, index) { - String group = sortedGroupKeys[index]; - List persons = groupedPersons[group]!; - - return _buildGroupSection(group, persons); - }, - ), + return Column( + children: [ + Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8), + child: SearchBarWidget( + showResetButton: true, + hintText: '请输入关键字', + resetButtonText: '重置', + onReset: () { + FocusScope.of(context).unfocus(); + _searchController.text = ''; + _search(); + }, + onSearch: (text) { + FocusScope.of(context).unfocus(); + _search(); + }, + controller: _searchController, ), + ), - // 右侧字母导航 - Container( - width: 30, - color: Colors.grey.shade50, - child: ListView.builder( - itemCount: alphabet.length, - itemBuilder: (context, index) { - final letter = alphabet[index]; - final hasGroup = groupedPersons.containsKey(letter); - return GestureDetector( - onTap: hasGroup ? () => _scrollToGroup(letter) : null, - child: Container( - height: 30, - alignment: Alignment.center, - child: Text( - letter, - style: TextStyle( - fontSize: 12, - color: hasGroup - ? Colors.blue - : Colors.grey.shade400, - fontWeight: hasGroup - ? FontWeight.bold - : FontWeight.normal, + // 人员列表 + Expanded( + child: Row( + children: [ + // 左侧人员列表 + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: sortedGroupKeys.length, + itemBuilder: (context, index) { + String group = sortedGroupKeys[index]; + List persons = groupedPersons[group]!; + + return _buildGroupSection(group, persons); + }, + ), + ), + + // 右侧字母导航 + Container( + width: 30, + color: Colors.grey.shade50, + child: ListView.builder( + itemCount: alphabet.length, + itemBuilder: (context, index) { + final letter = alphabet[index]; + final hasGroup = groupedPersons.containsKey(letter); + + return GestureDetector( + onTap: hasGroup ? () => _scrollToGroup(letter) : null, + child: Container( + height: 30, + alignment: Alignment.center, + child: Text( + letter, + style: TextStyle( + fontSize: 12, + color: hasGroup + ? Colors.blue + : Colors.grey.shade400, + fontWeight: hasGroup + ? FontWeight.bold + : FontWeight.normal, + ), + ), ), - ), - ), - ); - }, - ), + ); + }, + ), + ), + ], ), - ], - ), - ), - ], + ), + ], + ); + }, ), + ); } @@ -470,14 +497,29 @@ class _PersonSelectionPageState extends State { height: 56, // 固定高度便于计算 child: CheckboxListTile( title: Text( - person.name, + person.employeePersonUserName, style: const TextStyle(fontSize: 14), ), - value: selectedStates[person.id] ?? false, + value: selectedStates[person.employeePersonUserId] ?? false, onChanged: (bool? value) { setState(() { - selectedStates[person.id] = value ?? false; - _updateGroupSelection(person.group); + + if(widget.isMoreSelect){ + selectedStates[person.employeePersonUserId] = value ?? false; + _updateGroupSelection(person.group); + }else{ + // 清空所有选中状态 + for (var group in groupedPersons.keys) { + for (var p in groupedPersons[group]!) { + selectedStates[p.employeePersonUserId] = false; + } + } + // 设置当前选中 + selectedStates[person.employeePersonUserId] = true; + } + + + }); }, activeColor: Colors.blue, diff --git a/lib/pages/home/doorAndCar/sign_instructions_webView.dart b/lib/pages/home/doorAndCar/sign_instructions_webView.dart index fa4574c..25e6931 100644 --- a/lib/pages/home/doorAndCar/sign_instructions_webView.dart +++ b/lib/pages/home/doorAndCar/sign_instructions_webView.dart @@ -111,6 +111,7 @@ class _SignInstructionsWebviewState extends State { onPressed: () { if( signImages.isEmpty){ ToastUtil.showNormal(context, '请先签字'); + return; } Navigator.pop(context, signImages[0]); diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 772d0bc..552f6e0 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -10,6 +10,7 @@ import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/home/Study/study_tab_list_page.dart'; +import 'package:qhd_prevention/pages/home/doorAndCar/doorCar_tab_page.dart'; import 'package:qhd_prevention/pages/home/scan_page.dart'; import 'package:qhd_prevention/pages/home/unit/unit_tab_page.dart'; import 'package:qhd_prevention/pages/main_tab.dart'; @@ -60,28 +61,31 @@ class HomePageState extends RouteAwareState List totalList = []; - Map workStats = {'total': 36, 'processed': 30, 'pending': 6}; + // 工作统计(示例) + Map workStats = {'total': 0, 'processed': 0, 'pending': 0}; - List> checkLists = [ - { - "title": "电工班车间清单", - "type": "隐患排查", - "time": "2025-11-17", - "color": Color(0xFF4CAF50), - }, - { - "title": "工地区消防点检清单", - "type": "消防点检", - "time": "2025-11-17", - "color": Color(0xFF2196F3), - }, - { - "title": "消防专项检查清单", - "type": "重点作业", - "time": "2025-11-17", - "color": Color(0xFFFF9800), - }, - ]; + // 检查清单示例 + List checkLists = []; + // List> checkLists = [ + // { + // "title": "电工班车间清单", + // "type": "隐患排查", + // "time": "2025-11-17", + // "color": Color(0xFF4CAF50), + // }, + // { + // "title": "工地区消防点检清单", + // "type": "消防点检", + // "time": "2025-11-17", + // "color": Color(0xFF2196F3), + // }, + // { + // "title": "消防专项检查清单", + // "type": "安环检查", + // "time": "2025-11-17", + // "color": Color(0xFFFF9800), + // }, + // ]; final List _notifications = [ "系统通知:今晚20:00 将进行系统维护,请提前保存数据。", @@ -129,6 +133,8 @@ class HomePageState extends RouteAwareState "joinFirm": "dashboard-start-work", }; + int pcType=1; + @override void initState() { super.initState(); @@ -159,6 +165,10 @@ class HomePageState extends RouteAwareState Future.microtask(() { _updateModuleAndButtonVisibility(); }); + if(_isShowCheckLogin){ + _getToDoWorkList(pcType); + } + } @override @@ -623,6 +633,9 @@ class HomePageState extends RouteAwareState case "入港培训": pushPage(StudyTabListPage(), context); break; + case "口门门禁": + pushPage(DoorcarTabPage(), context); + break; default: ToastUtil.showNormal(context, '功能开发中...'); break; @@ -630,37 +643,79 @@ class HomePageState extends RouteAwareState } Widget _buildWorkStatsSection() { - double buttonHeight = 45.0; return Container( - padding: const EdgeInsets.all(1), + padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), - boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))], + boxShadow: const [ + BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2)), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 0), + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: "今日有工作项", + style: TextStyle(fontSize: 16, color: Colors.black87), + ), + TextSpan( + text: " ${workStats['total']}", + style: const TextStyle( + fontSize: 16, + color: Color(0xFF2A75F8), + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: "个", + style: TextStyle(fontSize: 16, color: Colors.black87), + ), + ], + ), + ), + ), + const SizedBox(height: 15), Container( width: screenWidth(context), - height: buttonHeight, + height: 36, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.grey[300]!, width: 1), + ), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Expanded( child: GestureDetector( - onTap: () => setState(() => _isMobileSelected = true), + onTap: () { + setState(() { + _isMobileSelected = true; + }); + pcType=1; + _getToDoWorkList(pcType); + }, child: Container( decoration: BoxDecoration( - color: _isMobileSelected ? const Color(0xFFE9F4FE) : Colors.white, - borderRadius: const BorderRadius.only(topLeft: Radius.circular(12)), + color: + _isMobileSelected ? const Color(0xFF2A75F8) : Colors.white, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + bottomLeft: Radius.circular(20), + ), ), child: Center( child: Text( "手机端", style: TextStyle( fontSize: 14, - color: _isMobileSelected ? Colors.black : Colors.blue, + color: _isMobileSelected ? Colors.white : Colors.black87, fontWeight: FontWeight.w500, ), ), @@ -668,26 +723,29 @@ class HomePageState extends RouteAwareState ), ), ), - Image.asset( - 'assets/images/${_isMobileSelected ? 'img2.png' : 'img1.png'}', - fit: BoxFit.cover, - height: buttonHeight, - width: 30, - ), Expanded( child: GestureDetector( - onTap: () => setState(() => _isMobileSelected = false), + onTap: () { + setState(() { + _isMobileSelected = false; + }); + pcType=2; + _getToDoWorkList(pcType); + }, child: Container( decoration: BoxDecoration( - color: !_isMobileSelected ? const Color(0xFFE9F4FE) : Colors.white, - borderRadius: const BorderRadius.only(topRight: Radius.circular(12)), + color: !_isMobileSelected ? const Color(0xFF2A75F8) : Colors.white, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(20), + bottomRight: Radius.circular(20), + ), ), child: Center( child: Text( "电脑端", style: TextStyle( - fontSize: 15, - color: !_isMobileSelected ? Colors.black : Colors.blue, + fontSize: 14, + color: !_isMobileSelected ? Colors.white : Colors.black87, fontWeight: FontWeight.w500, ), ), @@ -698,147 +756,192 @@ class HomePageState extends RouteAwareState ], ), ), - Padding( - padding: const EdgeInsets.all(15), - child: Column( - children: [ - Container( - width: double.infinity, - padding: const EdgeInsets.symmetric(vertical: 0), - child: RichText( - text: TextSpan( - children: [ - const TextSpan(text: "今日有工作项", style: TextStyle(fontSize: 16, color: Colors.black87)), - TextSpan( - text: " ${workStats['total']}", - style: const TextStyle(fontSize: 16, color: Color(0xFF2A75F8), fontWeight: FontWeight.bold), - ), - const TextSpan(text: "个", style: TextStyle(fontSize: 16, color: Colors.black87)), - ], - ), - ), - ), - const SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - RichText( - text: TextSpan( - children: [ - const TextSpan(text: "已处理工作项:", style: TextStyle(fontSize: 14, color: Colors.black87)), - TextSpan( - text: " ${workStats['processed']}", - style: const TextStyle(fontSize: 14, color: Colors.greenAccent, fontWeight: FontWeight.bold), - ), - const TextSpan(text: "个", style: TextStyle(fontSize: 14, color: Colors.black87)), - ], - ), - ), - RichText( - text: TextSpan( - children: [ - const TextSpan(text: "待处理工作项:", style: TextStyle(fontSize: 14, color: Colors.black87)), - TextSpan( - text: " ${workStats['pending']}", - style: const TextStyle(fontSize: 14, color: Colors.orange, fontWeight: FontWeight.bold), - ), - const TextSpan(text: "个", style: TextStyle(fontSize: 14, color: Colors.black87)), - ], - ), - ), - ], - ), - ], - ), - ), + // const SizedBox(height: 15), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // RichText( + // text: TextSpan( + // children: [ + // TextSpan( + // text: "已处理工作项:", + // style: TextStyle(fontSize: 14, color: Colors.black87), + // ), + // TextSpan( + // text: " ${workStats['processed']}", + // style: const TextStyle( + // fontSize: 14, + // color: Colors.greenAccent, + // fontWeight: FontWeight.bold, + // ), + // ), + // TextSpan( + // text: "个", + // style: TextStyle(fontSize: 14, color: Colors.black87), + // ), + // ], + // ), + // ), + // RichText( + // text: TextSpan( + // children: [ + // TextSpan( + // text: "待处理工作项:", + // style: TextStyle(fontSize: 14, color: Colors.black87), + // ), + // TextSpan( + // text: " ${workStats['processed']}", + // style: const TextStyle( + // fontSize: 14, + // color: Colors.orange, + // fontWeight: FontWeight.bold, + // ), + // ), + // TextSpan( + // text: "个", + // style: TextStyle(fontSize: 14, color: Colors.black87), + // ), + // ], + // ), + // ), + // ], + // ), ], ), ); } + // Widget _buildCheckListSection() { + // return Column( + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: const [ + // Text(' 待办清单', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + // SizedBox(), + // ], + // ), + // const SizedBox(height: 8), + // Container( + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(12), + // boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))], + // ), + // child: Column(children: [...checkLists.map((item) => _buildCheckListItem(item))]), + // ), + // ], + // ); + // } + Widget _buildCheckListSection() { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - Text(' 待办清单', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), - SizedBox(), - ], - ), - const SizedBox(height: 8), - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))], - ), - child: Column(children: [...checkLists.map((item) => _buildCheckListItem(item))]), - ), - ], + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2)), + ], + ), + child: Column( + children: checkLists.map((item) => _buildCheckListItem(item)).toList(), + ), ); } Widget _buildCheckListItem(Map item) { - return Container( - padding: const EdgeInsets.all(15), - decoration: BoxDecoration( - border: Border(bottom: BorderSide(color: Colors.grey[200]!, width: 1)), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( + return GestureDetector( + onTap: () async { + if(pcType==2){ + return; + } + switch(item['appName']){ + case "隐患治理": + case "隐患管理": + case "风险管控应用": + // await pushPage(ApplicationPageTwo(), context); + break; + case "消防检查": + // await pushPage(FireManagementTabPage(), context); + break; + case "一级口门管理": + await pushPage(DoorcarTabPage(), context); + break; + case "安全环保检查": + // await pushPage(SafecheckTabList(), context); + break; + + } + _getToDoWorkList(pcType); + // _getHiddenIssuesNum(); + + }, + child: Container( + padding: const EdgeInsets.all(15), + decoration: + BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey[200]!, width: 1))), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( item['title'], style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black87), - overflow: TextOverflow.ellipsis, - maxLines: 1, ), - ), - const SizedBox(width: 8), - Text(item['type'], style: TextStyle(fontSize: 12, color: Colors.grey[500])), - ], - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: Colors.grey[100], - borderRadius: BorderRadius.circular(4), - ), - child: Text( - "类型:${item['type']}", - style: const TextStyle(fontSize: 12, color: Colors.blue), - overflow: TextOverflow.ellipsis, - ), + Text(item['content'], style: TextStyle(fontSize: 12, color: Colors.grey[500])), + ], + ), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Image.asset('assets/images/mine1.png', width: 15, height: 15), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration(color: Colors.grey[100], borderRadius: BorderRadius.circular(4)), + child: Text("类型:${item['appName']}", style: const TextStyle(fontSize: 12, color: Colors.blue)), + ), + ], ), - ), - const SizedBox(width: 8), - Flexible( - child: Text( - "时间:${item['time']}", - style: const TextStyle(fontSize: 12, color: Colors.grey), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], + Text("时间:${item['createTime']}", style: const TextStyle(fontSize: 12, color: Colors.grey)), + ], + ), + ], + ), ), - ), - ], + ], + ), ), ); } + + + // 获取待办事项 + void _getToDoWorkList(int type) async { + Map data = { + "eqAppFlag": type==1?"1":"", + "eqPcFlag": type==1?"":"1", + "eqStatus": "1", + "pageIndex": '1', + "pageSize": '999', + }; + final result = await TodoApi.getTodoList(data); + if (result['success']) { + setState(() { + workStats['total'] =result['totalCount'] ; + checkLists=result['data']; + // checkLists = result['data']; + int m= checkLists.length; + }); + } + } + + } \ No newline at end of file diff --git a/lib/pages/notif/notif_detail_page.dart b/lib/pages/notif/notif_detail_page.dart index 4406a7a..a9653e5 100644 --- a/lib/pages/notif/notif_detail_page.dart +++ b/lib/pages/notif/notif_detail_page.dart @@ -50,9 +50,10 @@ class _NotifDetailPageState extends State { } Future _getNotifDetail() async { - // LoadingDialogHelper.show(); try { + LoadingDialogHelper.show(); final result = await NotifApi.getNoticeDetail(item['noticeId']); + LoadingDialogHelper.hide(); if (result['success']) { final dynamic newList = result['data'] ; setState(() { @@ -72,23 +73,29 @@ class _NotifDetailPageState extends State { } } catch (e) { print('加载出错: $e'); + LoadingDialogHelper.hide(); } } Future _getNotifEnterpriseDetail() async { - // LoadingDialogHelper.show(); try { - // final result = await HiddenDangerApi.getNotifEnterpriseDetail(item['NOTICECORPUSERID_ID']); - // if (result['result'] == 'success') { - // final dynamic newList = result['pd'] ; - // setState(() { - // title= newList['SYNOPSIS']; - // time= newList['CREATTIME']; - // text= newList['CONTENT']; - // }); - // } + LoadingDialogHelper.show(); + final result = await NotifApi.getAnnouncementDetail(item['id']); + LoadingDialogHelper.hide(); + if (result['success']) { + final dynamic newList = result['data'] ; + setState(() { + title= newList['title']??''; + time= newList['sendTime']??''; + text= newList['content']??''; + isShowReply=false; + // noticeReadId=newList['noticeReadId']??''; + // replyText=newList['replyContent']??''; + }); + } } catch (e) { print('加载出错: $e'); + LoadingDialogHelper.hide(); } } @@ -127,30 +134,30 @@ class _NotifDetailPageState extends State { ), SizedBox(height: 8), - // if(isShowReply&&replyText.isEmpty) - CustomButton( - height: 35, - margin: EdgeInsets.only(right: 10), - onPressed: () async { + if(isShowReply&&replyText.isEmpty) + CustomButton( + height: 35, + margin: EdgeInsets.only(right: 10), + onPressed: () async { - final confirmed = await CustomAlertDialogTwo.showInput( - context, - title: "回复", - hintText: '输入内容', - cancelText: '关闭', - confirmText: '确定', - ); - if (confirmed != null&&confirmed.isNotEmpty) { - //确定回复 - // ToastUtil.showNormal(context, confirmed); - _replyContent(confirmed); - } - }, - backgroundColor: h_AppBarColor(), - textStyle: const TextStyle(color: Colors.white), - buttonStyle: ButtonStyleType.primary, - text: '回复', - ), + final confirmed = await CustomAlertDialogTwo.showInput( + context, + title: "回复", + hintText: '输入内容', + cancelText: '关闭', + confirmText: '确定', + ); + if (confirmed != null&&confirmed.isNotEmpty) { + //确定回复 + // ToastUtil.showNormal(context, confirmed); + _replyContent(confirmed); + } + }, + backgroundColor: h_AppBarColor(), + textStyle: const TextStyle(color: Colors.white), + buttonStyle: ButtonStyleType.primary, + text: '回复', + ), ], ), ), diff --git a/lib/pages/notif/notif_page.dart b/lib/pages/notif/notif_page.dart index 03cd170..e44dc42 100644 --- a/lib/pages/notif/notif_page.dart +++ b/lib/pages/notif/notif_page.dart @@ -189,35 +189,35 @@ class NotifPageState extends RouteAwareState if(_selectedTab==0){ result = await NotifApi.getNoticeList(data); }else{ - result = await NotifApi.getNoticeList(data); + result = await NotifApi.getAnnouncementList(data); } BadgeManager().updateNotifCount(); LoadingDialogHelper.hide(); - if(_selectedTab==0){ - if (result['success']) { - _totalPage =result['totalPages'] ?? 1; - final List newList = result['data'] ?? []; - // setState(() { - // _list.addAll(newList); - // }); - setState(() { - if (loadMore) { - _list.addAll(newList); - } else { - _list = newList; - } - _hasMore = pageNum < _totalPage; - // if (_hasMore) _page++; - }); + if (result['success']) { + _totalPage =result['totalPages'] ?? 1; + final List newList = result['data'] ?? []; + // setState(() { + // _list.addAll(newList); + // }); - }else{ - ToastUtil.showNormal(context, "加载数据失败"); - // _showMessage('加载数据失败'); - } + setState(() { + if (loadMore) { + _list.addAll(newList); + } else { + _list = newList; + } + _hasMore = pageNum < _totalPage; + // if (_hasMore) _page++; + }); + + }else{ + ToastUtil.showNormal(context, "加载数据失败"); + // _showMessage('加载数据失败'); } + } catch (e) { LoadingDialogHelper.hide(); // 出错时可以 Toast 或者在页面上显示错误状态 @@ -371,7 +371,7 @@ class NotifPageState extends RouteAwareState SizedBox(height: 8.0), Text( - '发布时间:${item['publishTime']??''}', + '发布时间:${_selectedTab==0?item['publishTime']??'':item['sendTime']??''}', style: TextStyle(fontSize: 14.0, color: Colors.grey[500]), ), ], @@ -384,7 +384,7 @@ class NotifPageState extends RouteAwareState mainAxisSize: MainAxisSize.min, children: [ Text( - item['readStatus']=='1'?'已阅':'待阅', + _readStates(item), style: TextStyle( fontSize: 16.0, color: Colors.black, @@ -393,7 +393,8 @@ class NotifPageState extends RouteAwareState ), const SizedBox(width: 6.0), Image.asset( - item['readStatus']=='1'?'assets/icon-apps/read_message.png':'assets/icon-apps/unread_message.png', + _readStatesOne(item), + // item['readStatus']=='1'?'assets/icon-apps/read_message.png':'assets/icon-apps/unread_message.png', width: 25, height: 25, ), @@ -407,7 +408,21 @@ class NotifPageState extends RouteAwareState ); } + String _readStatesOne(item){ + if(_selectedTab==0){ + return item['readStatus']=='1'?'assets/icon-apps/read_message.png':'assets/icon-apps/unread_message.png'; + }else{ + return item['readTime']!=null?'assets/icon-apps/read_message.png':'assets/icon-apps/unread_message.png'; + } + } + String _readStates(item){ + if(_selectedTab==0){ + return item['readStatus']=='1'?'已阅':'待阅'; + }else{ + return item['readTime']!=null?'已阅':'待阅'; + } + } diff --git a/lib/tools/car_licence_type.dart b/lib/tools/car_licence_type.dart new file mode 100644 index 0000000..4ab328e --- /dev/null +++ b/lib/tools/car_licence_type.dart @@ -0,0 +1,53 @@ +/// 校验中国大陆车牌号(支持燃油车蓝牌和新能源绿牌) +/// 规则依据: +/// 1. 燃油车(92式): 省份简称(1位汉字) + 发牌机关代号(1位字母) + 序号(5位字母/数字组合,不含I/O) +/// 2. 新能源车: 省份简称(1位汉字) + 发牌机关代号(1位字母) + 序号(6位字符) +/// - 小型车: 字母D或F + 5位字母/数字 +/// - 大型车: 5位字母/数字 + 字母D或F +/// 注意:此处为常用民用牌照规则,不包含使领馆、警用、军用等特殊牌照 + + + +bool isValidChineseLicensePlate(String plate) { + if (plate.isEmpty) return false; + + // 去除可能的空格和特殊字符(如·) + plate = plate.replaceAll(RegExp(r'[\s·\-]'), ''); + + // 定义省份简称 + final provinces = '京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼'; + + // 更精确的正则表达式 + // 燃油车:省份 + 字母 + 5位字母数字(排除I和O) + final fuelRegex = RegExp('^[$provinces][A-HJ-NP-Z][A-HJ-NP-Z0-9]{5}\$'); + + // 新能源车:省份 + 字母 + 6位字母数字(必须包含D或F) + // 注意:排除I和O,但D和F是允许的 + final newEnergyRegex = RegExp('^[$provinces][A-HJ-NP-Z][A-HJ-NP-Z0-9]{6}\$'); + + // 燃油车校验(7位) + if (plate.length == 7) { + return fuelRegex.hasMatch(plate); + } + + // 新能源车校验(8位) + if (plate.length == 8) { + // 检查格式 + if (!newEnergyRegex.hasMatch(plate)) { + return false; + } + + // 检查是否包含D或F(不区分大小写) + // 获取后6位 + String suffix = plate.substring(2); + + // 新能源车牌必须包含D(纯电)或F(非纯电) + if (!suffix.contains(RegExp(r'[DF]'))) { + return false; + } + + return true; + } + + return false; +}