import 'dart:convert'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.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/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/photo_picker_row.dart'; import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/main_tab.dart'; import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/services/SessionService.dart'; import 'package:qhd_prevention/tools/id_cart_util.dart'; import 'package:qhd_prevention/tools/tools.dart'; class FullUserinfoPage extends StatefulWidget { const FullUserinfoPage({super.key, required this.isEidt, required this.isChooseFirm}); final bool isEidt; final bool isChooseFirm; // 是否选择过企业 @override State createState() => _FullUserinfoPageState(); } class _FullUserinfoPageState extends State { Map pd = { "phone": "", "name": "", "userIdCard": "", "birthday": "", "gender": "", "nationName": "", "locationAddress": "", "currentAddress": "", "culturalLevel": "", "politicalAffiliation": "", "maritalStatus": "", }; Map rule = { "name": "请输入姓名", "userIdCard": "请输入身份证号码", "nationName": "请选择民族", "maritalStatus": "请选择婚姻状况", "culturalLevel": "请选择文化程度", "politicalAffiliation": "请选择政治面貌", "currentAddress": "请输入现居住地址", "locationAddress": "请输入户口所在地", }; late bool _isEdit; /// 标记修改 late bool _isChange = false; String _genderText = ''; String _birthText = ''; String _idValue = ''; List _idCardImgList = []; List _idCartImgIds = []; /// 是否修改了身份证 bool _isChangeIdCard = false; List _idCardImgRemoveList = []; List _wenhuachengduList = []; List _zhengzhimianmaoList = []; List _hunyinList = [ {"name": "已婚", "value": 1}, {"name": "未婚", "value": 0}, ]; List idPhotos = []; @override void initState() { super.initState(); _isEdit = widget.isEidt; if (!_isEdit) { _getUserDetail(); } else { pd['username'] = SessionService.instance.userName; pd['id'] = SessionService.instance.accountId; pd['flowFlag'] = 0; } _getKeyValues(); } Future _getUserDetail() async { final res = await BasicInfoApi.getUserMessage( '${SessionService.instance.accountId}', ); if (res['success']) { final data = res['data']; _genderText = data['sex'] ?? ''; _birthText = data['birthday'] ?? ''; final eqForeignKey = data['userId']; await FileApi.getImagePathWithType( eqForeignKey, '', UploadFileType.idCardPhoto, ).then((result) { if (result['success']) { List files = result['data'] ?? []; _idCardImgList = files.map((item) => item['filePath'].toString()).toList(); _idCartImgIds = files.map((item) => item['id'].toString()).toList(); // final filePath = fileData.first['filePath'] ?? ''; } }); setState(() { pd = data; try{ final idCardBase64 = utf8.decode(base64.decode(pd['userIdCard'])); if (idCardBase64.isNotEmpty) { pd['userIdCard'] =idCardBase64; } }catch(e){ print(e); } }); } } Future _getKeyValues() async { await BasicInfoApi.getDictValues('wenhuachengdu').then((res) { _wenhuachengduList = res['data']; }); await BasicInfoApi.getDictValues('zhengzhimianmao').then((res) { _zhengzhimianmaoList = res['data']; }); } Future _saveSuccess() async { if (!FormUtils.hasValue(pd, 'faceFiles') && !FormUtils.hasValue(pd, 'userAvatarUrl')) { ToastUtil.showNormal(context, '请上传人脸图片'); return; } for (String key in rule.keys) { if (!FormUtils.hasValue(pd, key)) { ToastUtil.showNormal(context, rule[key]); return; } } if (!FormUtils.hasValue(pd, 'userIdCard') || !FormUtils.hasValue(pd, 'birthday')) { ToastUtil.showNormal(context, '请输入正确身份证号'); return ; } if (idPhotos.length < 2 && !_isChange) { ToastUtil.showNormal(context, '请上传2张身份证照片'); return; } LoadingDialogHelper.show(); // 签字上传 final signResult = await _checkFaceImage(); // 证件上传图片 final situationResult = await _checkIDCartImages(); // 删除服务器图片 final deleteResult = await _checkDeleteImage(); if (signResult && situationResult && deleteResult) { pd['userIdCard'] = base64.encode(utf8.encode(pd['userIdCard'])); await BasicInfoApi.updateUserInfo(pd).then((res) { LoadingDialogHelper.hide(); if (res['success']) { ToastUtil.showNormal(context, '保存成功'); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const MainPage(isChooseFirm: false)), ); } else { ToastUtil.showNormal(context, '保存失败'); } }); }else{ ToastUtil.showNormal(context, '保存失败'); LoadingDialogHelper.hide(); } } Future _checkDeleteImage() async { late bool isSuccess = true; if (_idCardImgRemoveList.isNotEmpty) { final delIds = _idCardImgRemoveList; await FileApi.deleteImages(delIds).then((result) { if (result['success']) { isSuccess = true; } else { isSuccess = false; } }); }else{ isSuccess = true; } return isSuccess; } /// 校验人脸照片上传 Future _checkFaceImage() async { final faceImgPath = pd['faceFiles'] ?? ''; UploadFileType fileType = UploadFileType.userAvatar; late bool isSuccess = true; if (faceImgPath.isNotEmpty) { try { await FileApi.uploadFile(faceImgPath, fileType, '').then((result) { if (result['success']) { pd['userAvatarUrl'] = result['data']['filePath'] ?? ''; isSuccess = true; } else { LoadingDialogHelper.hide(); ToastUtil.showNormal(context, '人脸照片上传失败'); isSuccess = false; } }); } catch (e) { LoadingDialogHelper.hide(); ToastUtil.showNormal(context, '签名上传失败'); isSuccess = false; } } return isSuccess; } /// 校验是否需要上传身份证图片 Future _checkIDCartImages() async { late bool isSuccess = true; // 身份证上传图片 if (idPhotos.isEmpty) { return isSuccess; } try { await FileApi.uploadFiles( idPhotos, UploadFileType.idCardPhoto, pd['userId'] ?? '', ).then((result) { if (result['success']) { pd['userId'] = result['data']['foreignKey'] ?? ''; } else { LoadingDialogHelper.hide(); ToastUtil.showNormal(context, '图片上传失败'); isSuccess = false; } }); } catch (e) { ToastUtil.showNormal(context, '图片上传失败'); isSuccess = false; } return isSuccess; } /// 当身份证输入发生变化时调用(value 为输入框实时值) void _onIdChanged(String value) { _idValue = value ?? ''; _isChangeIdCard = true; final raw = _idValue.trim().toUpperCase(); // 当长度为 15 或 18 时触发解析 if (raw.length == 15 || raw.length == 18) { try { final info = parseChineseIDCard(raw); if (info.isValid /**&& info.checksumValid*/) { setState(() { // 使用 info.id18(标准 18 位)存储身份证号 pd['userIdCard'] = info.id18 ?? raw; pd['birthday'] = info.birth; // 格式 YYYY-MM-DD pd['age'] = info.age; pd['gender'] = info.gender; // "男"/"女" pd['provinceCode'] = info.provinceCode; pd['province'] = info.province; _genderText = info.gender ?? '未知'; _birthText = info.birth ?? '未知'; }); } else { // 解析失败或校验位不正确 — 清除解析字段但保留输入 ToastUtil.showNormal(context, '请输入正确格式身份证号'); setState(() { pd.remove('birthday'); pd.remove('age'); pd.remove('gender'); pd.remove('provinceCode'); pd.remove('province'); _genderText = '输入身份证获取'; _birthText = '输入身份证获取'; }); } } catch (e) { ToastUtil.showNormal(context, '请输入正确格式身份证号'); // 出现异常则清除解析字段 setState(() { pd.remove('birthday'); pd.remove('age'); pd.remove('gender'); pd.remove('provinceCode'); pd.remove('province'); _genderText = '输入身份证获取'; _birthText = '输入身份证获取'; }); } } else { // 长度不足时清除解析结果(可按需注释掉保留旧解析) if (_genderText != '请选择' || _birthText != '请选择') { setState(() { pd.remove('birthday'); pd.remove('age'); pd.remove('gender'); pd.remove('provinceCode'); pd.remove('province'); _genderText = '输入身份证获取'; _birthText = '输入身份证获取'; }); } } } @override Widget build(BuildContext context) { bool isShow = _isEdit; if (!_isEdit && FormUtils.hasValue(pd, 'id')) { isShow = true; } return Scaffold( appBar: MyAppbar( title: '信息补充', isBack: (!_isEdit || _isChange), onBackPressed: () async { if (_isChange) { await CustomAlertDialog.showConfirm( context, title: '温馨提示', content: '是否放弃修改?', cancelText: '取消', ).then( (isSure) => { if (isSure) {Navigator.pop(context)}, }, ); } else { Navigator.pop(context); } }, actions: [ if (!_isEdit) TextButton( onPressed: () { setState(() { _isChange = true; _isEdit = true; }); }, child: Text( '修改', style: TextStyle(color: Colors.white, fontSize: 17), ), ), ], ), body: SafeArea( child: ItemListWidget.itemContainer( horizontal: 5, isShow ? ListView( children: [ RepairedPhotoSection( title: '人脸照片', inlineSingle: true, isRequired: _isEdit, initialMediaPaths: FormUtils.hasValue(pd, 'userAvatarUrl') ? [ '${ApiService.baseImgPath}${pd['userAvatarUrl'] ?? ''}', ] : [], horizontalPadding: _isEdit ? 12 : 0, inlineImageWidth: 60, isFaceImage: true, isEdit: _isEdit, onChanged: (files) { if (files.isEmpty) { return; } pd['faceFiles'] = files.first.path; }, onAiIdentify: () {}, // onMediaRemovedForIndex: (index) async { // final deleFile = pd['userAvatarUrl'] ?? ''; // if (deleFile.contains(UploadFileType.idCardPhoto.path)) { // _idCardImgRemoveList.add(deleFile); // } // }, ), if (_isEdit) ItemListWidget.itemContainer( const Text( '温馨提示:该照片为进入项目施工场所口门人脸识别使用', style: TextStyle(color: Colors.red, fontSize: 10), ), ), const Divider(), ItemListWidget.singleLineTitleText( label: '姓名:', isRequired: true, hintText: '请输入姓名', text: pd['name'] ?? '', isEditable: _isEdit, onChanged: (value) { pd['name'] = value; }, ), const Divider(), ItemListWidget.singleLineTitleText( label: '手机号:', isRequired: true, text: pd['username'] ?? '', isNumericInput: true, hintText: '请输入手机号', strongRequired: _isEdit, isEditable: false, ), const Divider(), // 身份证输入:只使用 onChanged 回调(value 是实时输入框的值) ItemListWidget.singleLineTitleText( label: '身份证:', isRequired: true, hintText: '请输入身份证号', text: pd['userIdCard'] ?? '', isEditable: _isEdit, onChanged: (value) { // value 是实时输入框值 _onIdChanged(value ?? ''); }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '民族:', isEditable: _isEdit, text: pd['nationName'] ?? '请选择', isRequired: _isEdit, onTap: () async { final found = await BottomPicker.show( context, items: nationMapList, itemBuilder: (i) => Text(i['name']!, textAlign: TextAlign.center), initialIndex: 0, ); //FocusHelper.clearFocus(context); if (found != null) { setState(() { pd['nationName'] = found['name']; pd['nation'] = found['code']; }); } }, ), const Divider(), // 性别:不可编辑,显示解析结果(也允许手动覆盖) ItemListWidget.selectableLineTitleTextRightButton( label: '性别:', isEditable: false, text: _genderText, strongRequired: _isEdit, isRequired: true, onTap: () { // 允许手动选择覆盖解析结果 showModalBottomSheet( context: context, builder: (_) { return Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( title: const Text('男'), onTap: () { setState(() { pd['gender'] = '男'; _genderText = '男'; }); Navigator.pop(context); }, ), ListTile( title: const Text('女'), onTap: () { setState(() { pd['gender'] = '女'; _genderText = '女'; }); Navigator.pop(context); }, ), ], ); }, ); }, ), const Divider(), // 出生年月:显示解析结果,但仍然可以手动修改 ItemListWidget.selectableLineTitleTextRightButton( label: '出生年月:', isEditable: false, text: _birthText, strongRequired: _isEdit, isRequired: true, onTap: () async { }, ), const Divider(), ItemListWidget.singleLineTitleText( label: '户口所在地:', isRequired: _isEdit, hintText: '请输入户口所在地', text: pd['locationAddress'] ?? '', isEditable: _isEdit, onChanged: (value) { pd['locationAddress'] = value; }, ), const Divider(), ItemListWidget.singleLineTitleText( label: '现住址:', isRequired: true, text: pd['currentAddress'] ?? '', hintText: '请输入现住址', isEditable: _isEdit, onChanged: (value) { pd['currentAddress'] = value; }, ), const Divider(), if (_isEdit || _idCardImgList.isNotEmpty) RepairedPhotoSection( title: '身份证照片', isRequired: _isEdit, maxCount: 2, initialMediaPaths: _idCardImgList .map( (item) => ApiService.baseImgPath + item, ) .toList(), isEdit: _isEdit, horizontalPadding: _isEdit ? 12 : 0, inlineImageWidth: 60, onChanged: (files) { idPhotos = files.map((file) => file.path).toList(); }, onMediaRemovedForIndex: (index) async { final deleFile = _idCardImgList[index]; final deleId = _idCartImgIds[index]; if (deleFile.contains(UploadFileType.idCardPhoto.path)) { _idCardImgList.removeAt(index); _idCartImgIds.removeAt(index); _idCardImgRemoveList.add(deleId); } }, onAiIdentify: () { /* ... */ }, ), if (_isEdit) ItemListWidget.itemContainer( const Text( '温馨提示:用户要上传身份证正反面(身份证照片数量是2张才能进行人员培训)', style: TextStyle(color: Colors.red, fontSize: 10), ), ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '文化程度:', isEditable: _isEdit, text: pd['culturalLevelName'] ?? '请选择', isRequired: _isEdit, onTap: () async { final found = await BottomPicker.show( context, items: _wenhuachengduList, itemBuilder: (i) => Text( i['dictLabel']!, textAlign: TextAlign.center, ), initialIndex: 0, ); //FocusHelper.clearFocus(context); if (found != null) { setState(() { pd['culturalLevelName'] = found['dictLabel']; pd['culturalLevel'] = found['dictValue']; }); } }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '政治面貌:', isEditable: _isEdit, text: pd['politicalAffiliationName'] ?? '请选择', isRequired: _isEdit, onTap: () async { final found = await BottomPicker.show( context, items: _zhengzhimianmaoList, itemBuilder: (i) => Text( i['dictLabel']!, textAlign: TextAlign.center, ), initialIndex: 0, ); //FocusHelper.clearFocus(context); if (found != null) { setState(() { pd['politicalAffiliationName'] = found['dictLabel']; pd['politicalAffiliation'] = found['dictValue']; }); } }, ), const Divider(), ItemListWidget.selectableLineTitleTextRightButton( label: '婚姻状态:', isEditable: _isEdit, text: pd['maritalStatusName'] ?? '请选择', isRequired: _isEdit, onTap: () async { final found = await BottomPicker.show( context, items: _hunyinList, itemBuilder: (i) => Text(i['name']!, textAlign: TextAlign.center), initialIndex: 0, ); //FocusHelper.clearFocus(context); if (found != null) { setState(() { pd['maritalStatusName'] = found['name']; pd['maritalStatus'] = found['value']; }); } }, ), const Divider(), ListItemFactory.createYesNoSection( title: "是否流动人员:", horizontalPadding: 2, verticalPadding: 0, yesLabel: "是", noLabel: "否", text: pd['flowFlag'] == 1 ? '是' : '否', isRequired: true, isEdit: _isEdit, groupValue: (pd['flowFlag'] ?? 0) == 1, onChanged: (val) { setState(() { pd['flowFlag'] = val ? 1 : 0; }); }, ), const Divider(), ItemListWidget.singleLineTitleText( label: '电子邮箱:', isRequired: false, isNumericInput: false, hintText: '请输入电子邮箱', text: pd['email'] ?? '', isEditable: _isEdit, onChanged: (value) { pd['email'] = value; }, ), const Divider(), const SizedBox(height: 20), if (_isEdit) CustomButton( text: '保存', backgroundColor: Colors.blue, onPressed: () { _saveSuccess(); }, ), ], ) : const SizedBox(), ), ), ); } }