// 这里保持你的其他 import 不变 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/pages/user/firm_list_page.dart'; import 'package:qhd_prevention/services/SessionService.dart'; import 'package:qhd_prevention/tools/id_cart_util.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:shared_preferences/shared_preferences.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": "", "culturalLevel": "", "politicalAffiliation": "", }; Map rule = { "name": "请输入姓名", "userIdCard": "请输入身份证号码", "nationName": "请选择民族", "culturalLevel": "请选择文化程度", "politicalAffiliation": "请选择政治面貌", }; late bool _isEdit; late bool _isChange = false; String _genderText = ''; String _birthText = ''; String _idValue = ''; String? _userId = SessionService.instance.userId; 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['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']; pd = 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(); } }); if ('${pd['userAvatarUrl']}'.isEmpty) { await FileApi.getImagePathWithType( eqForeignKey, '', UploadFileType.userAvatar, ).then((result) { if (result['success']) { List files = result['data'] ?? []; pd['userAvatarUrl'] = files.map((item) => item['filePath'].toString()).toList().first; } }); } setState(() { try { final idCardBase64 = utf8.decode(base64.decode(pd['userIdCard'])); if (idCardBase64.isNotEmpty) { pd['userIdCard'] = idCardBase64; _idValue = idCardBase64; } } catch (e) { print(e); } }); } } Future _getKeyValues() async { final prefs = await SharedPreferences.getInstance(); final phone = prefs.getString("savePhone"); pd['username'] = phone; await BasicInfoApi.getDictValues('wenhuachengdu').then((res) { _wenhuachengduList = res['data']; }); await BasicInfoApi.getDictValues('zhengzhimianmao').then((res) { _zhengzhimianmaoList = res['data']; }); setState(() {}); } void _clearParsedIdCardFields({bool keepInput = true}) { setState(() { if (!keepInput) { pd['userIdCard'] = ''; } pd.remove('birthday'); pd.remove('age'); pd.remove('gender'); pd.remove('provinceCode'); pd.remove('province'); _genderText = '输入身份证获取'; _birthText = '输入身份证获取'; }); } /// 身份证输入变化 void _onIdChanged(String value) { final raw = value.trim().toUpperCase(); _idValue = raw; _isChangeIdCard = true; setState(() { pd['userIdCard'] = raw; }); // 只在18位时开始校验 if (raw.length != 18) { _clearParsedIdCardFields(keepInput: true); return; } final info = parseChineseIDCard(raw); if (info.isValid) { setState(() { pd['userIdCard'] = info.id18 ?? raw; pd['birthday'] = info.birth; pd['age'] = info.age; pd['gender'] = info.gender; pd['provinceCode'] = info.provinceCode; pd['province'] = info.province; _genderText = info.gender ?? '未知'; _birthText = info.birth ?? '未知'; }); } else { _clearParsedIdCardFields(keepInput: true); ToastUtil.showNormal(context, info.error ?? '请输入正确身份证号'); } } 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; } } // 保存前再兜底校验一次身份证 final idRaw = (pd['userIdCard'] ?? '').toString().trim().toUpperCase(); final idInfo = parseChineseIDCard(idRaw); if (!idInfo.isValid) { ToastUtil.showNormal(context, idInfo.error ?? '请输入正确身份证号'); return; } // 保存时统一使用标准 18 位 pd['userIdCard'] = idInfo.id18 ?? idRaw; pd['birthday'] = idInfo.birth; pd['age'] = idInfo.age; pd['gender'] = pd['gender'] ?? idInfo.gender; pd['provinceCode'] = idInfo.provinceCode; pd['province'] = idInfo.province; if (_genderText.isEmpty) { _genderText = idInfo.gender ?? ''; } if (_birthText.isEmpty) { _birthText = idInfo.birth ?? ''; } 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) { final userIdCard = base64.encode(utf8.encode(pd['userIdCard'])); await BasicInfoApi.updateUserInfo(pd, userIdCard).then((res) { LoadingDialogHelper.hide(); if (res['success']) { SessionService.instance.name = pd['name']; ToastUtil.showNormal(context, '保存成功'); if (widget.isChooseFirm || _isChange) { Navigator.pop(context); } else { Navigator.pushReplacement( context, MaterialPageRoute( builder: (_) => FirmListPage(isBack: false), ), ); } } else { ToastUtil.showNormal(context, res['errMessage'] ?? '保存失败'); } }); } else { ToastUtil.showNormal(context, '保存失败'); LoadingDialogHelper.hide(); } } Future _checkDeleteImage() async { late bool isSuccess = true; if (_idCardImgRemoveList.isNotEmpty) { final delIds = _idCardImgRemoveList; try { await FileApi.deleteImages(delIds).then((result) { if (result['success']) { isSuccess = true; } else { isSuccess = false; } }); } catch (e) { LoadingDialogHelper.hide(); } } 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, _userId ?? '') .then((result) { if (result['success']) { pd['userAvatarUrl'] = result['data']['filePath'] ?? ''; pd['userId'] = result['data']['foreignKey'] ?? ''; 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, _userId ?? '', ).then((result) { if (result['success']) { } else { LoadingDialogHelper.hide(); ToastUtil.showNormal(context, '图片上传失败'); isSuccess = false; } }); } catch (e) { ToastUtil.showNormal(context, '图片上传失败'); isSuccess = false; } return isSuccess; } @override Widget build(BuildContext context) { bool isShow = _isEdit; if (!_isEdit && FormUtils.hasValue(pd, 'userAvatarUrl')) { isShow = true; } String token = SessionService.instance.token ?? ''; 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: const 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: () {}, ), 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(), ItemListWidget.singleLineTitleText( label: '身份证:', isRequired: true, hintText: '请输入身份证号', text: pd['userIdCard'] ?? '', isEditable: _isEdit, onChanged: (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, ); if (found != null) { setState(() { pd['nationName'] = found['name']; pd['nation'] = found['bianma']; }); } }, ), 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: () {}, ), 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, ); 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, ); if (found != null) { setState(() { pd['politicalAffiliationName'] = found['dictLabel']; pd['politicalAffiliation'] = found['dictValue']; }); } }, ), const Divider(), ListItemFactory.createYesNoSection( title: "是否流动人员:", horizontalPadding: 2, verticalPadding: 0, yesLabel: "是", noLabel: "否", text: pd['flowFlag'] == 1 ? '是' : '否', isRequired: true, isEdit: _isEdit ? (token.isEmpty ? true : false) : false, 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(), ), ), ); } }