QinGang_interested/lib/pages/user/full_userinfo_page.dart

672 lines
21 KiB
Dart
Raw Normal View History

2026-04-13 08:59:45 +08:00
// 这里保持你的其他 import 不变
2025-12-12 09:11:30 +08:00
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';
2025-12-24 16:07:53 +08:00
import 'package:qhd_prevention/pages/user/firm_list_page.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/services/SessionService.dart';
import 'package:qhd_prevention/tools/id_cart_util.dart';
import 'package:qhd_prevention/tools/tools.dart';
2025-12-24 16:07:53 +08:00
import 'package:shared_preferences/shared_preferences.dart';
2025-12-12 09:11:30 +08:00
class FullUserinfoPage extends StatefulWidget {
2026-04-13 08:59:45 +08:00
const FullUserinfoPage({
super.key,
required this.isEidt,
required this.isChooseFirm,
});
2025-12-12 09:11:30 +08:00
final bool isEidt;
2026-04-13 08:59:45 +08:00
final bool isChooseFirm;
2025-12-12 09:11:30 +08:00
@override
State<FullUserinfoPage> createState() => _FullUserinfoPageState();
}
class _FullUserinfoPageState extends State<FullUserinfoPage> {
Map<String, dynamic> pd = {
"phone": "",
"name": "",
"userIdCard": "",
"birthday": "",
"gender": "",
"nationName": "",
"culturalLevel": "",
"politicalAffiliation": "",
};
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
Map rule = {
"name": "请输入姓名",
"userIdCard": "请输入身份证号码",
"nationName": "请选择民族",
"culturalLevel": "请选择文化程度",
"politicalAffiliation": "请选择政治面貌",
};
2026-04-13 08:59:45 +08:00
late bool _isEdit;
2025-12-12 09:11:30 +08:00
late bool _isChange = false;
String _genderText = '';
String _birthText = '';
String _idValue = '';
2025-12-24 16:07:53 +08:00
String? _userId = SessionService.instance.userId;
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
List<String> _idCardImgList = [];
List<String> _idCartImgIds = [];
bool _isChangeIdCard = false;
List<String> _idCardImgRemoveList = [];
List<dynamic> _wenhuachengduList = [];
List<dynamic> _zhengzhimianmaoList = [];
List<dynamic> _hunyinList = [
{"name": "已婚", "value": 1},
{"name": "未婚", "value": 0},
];
List<String> idPhotos = [];
@override
void initState() {
super.initState();
_isEdit = widget.isEidt;
if (!_isEdit) {
_getUserDetail();
} else {
pd['id'] = SessionService.instance.accountId;
pd['flowFlag'] = 0;
}
_getKeyValues();
}
Future<void> _getUserDetail() async {
final res = await BasicInfoApi.getUserMessage(
'${SessionService.instance.accountId}',
);
if (res['success']) {
final data = res['data'];
2026-04-14 16:24:31 +08:00
pd = data;
2025-12-12 09:11:30 +08:00
_genderText = data['sex'] ?? '';
_birthText = data['birthday'] ?? '';
final eqForeignKey = data['userId'];
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
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();
}
});
2026-04-14 16:24:31 +08:00
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;
}
});
}
2025-12-12 09:11:30 +08:00
setState(() {
2026-04-13 08:59:45 +08:00
try {
2025-12-12 09:11:30 +08:00
final idCardBase64 = utf8.decode(base64.decode(pd['userIdCard']));
if (idCardBase64.isNotEmpty) {
2026-04-13 08:59:45 +08:00
pd['userIdCard'] = idCardBase64;
_idValue = idCardBase64;
2025-12-12 09:11:30 +08:00
}
2026-04-13 08:59:45 +08:00
} catch (e) {
2025-12-12 09:11:30 +08:00
print(e);
}
});
}
}
Future<void> _getKeyValues() async {
2025-12-24 16:07:53 +08:00
final prefs = await SharedPreferences.getInstance();
2026-04-13 08:59:45 +08:00
final phone = prefs.getString("savePhone");
2025-12-24 16:07:53 +08:00
pd['username'] = phone;
2025-12-12 09:11:30 +08:00
await BasicInfoApi.getDictValues('wenhuachengdu').then((res) {
_wenhuachengduList = res['data'];
});
await BasicInfoApi.getDictValues('zhengzhimianmao').then((res) {
_zhengzhimianmaoList = res['data'];
});
2026-04-13 08:59:45 +08:00
setState(() {});
}
void _clearParsedIdCardFields({bool keepInput = true}) {
2025-12-24 16:07:53 +08:00
setState(() {
2026-04-13 08:59:45 +08:00
if (!keepInput) {
pd['userIdCard'] = '';
}
pd.remove('birthday');
pd.remove('age');
pd.remove('gender');
pd.remove('provinceCode');
pd.remove('province');
_genderText = '输入身份证获取';
_birthText = '输入身份证获取';
});
}
2025-12-24 16:07:53 +08:00
2025-12-12 09:11:30 +08:00
2026-04-13 08:59:45 +08:00
/// 身份证输入变化
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 ?? '请输入正确身份证号');
}
}
2025-12-12 09:11:30 +08:00
Future<void> _saveSuccess() async {
if (!FormUtils.hasValue(pd, 'faceFiles') &&
!FormUtils.hasValue(pd, 'userAvatarUrl')) {
ToastUtil.showNormal(context, '请上传人脸图片');
return;
}
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
for (String key in rule.keys) {
if (!FormUtils.hasValue(pd, key)) {
ToastUtil.showNormal(context, rule[key]);
return;
}
}
2026-04-13 08:59:45 +08:00
// 保存前再兜底校验一次身份证
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 ?? '';
2025-12-12 09:11:30 +08:00
}
if (idPhotos.length < 2 && !_isChange) {
ToastUtil.showNormal(context, '请上传2张身份证照片');
return;
}
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
LoadingDialogHelper.show();
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
final signResult = await _checkFaceImage();
final situationResult = await _checkIDCartImages();
final deleteResult = await _checkDeleteImage();
if (signResult && situationResult && deleteResult) {
2026-01-14 09:55:23 +08:00
final userIdCard = base64.encode(utf8.encode(pd['userIdCard']));
await BasicInfoApi.updateUserInfo(pd, userIdCard).then((res) {
2025-12-12 09:11:30 +08:00
LoadingDialogHelper.hide();
if (res['success']) {
2026-02-28 14:38:07 +08:00
SessionService.instance.name = pd['name'];
2025-12-12 09:11:30 +08:00
ToastUtil.showNormal(context, '保存成功');
2026-01-14 09:55:23 +08:00
if (widget.isChooseFirm || _isChange) {
Navigator.pop(context);
2026-04-13 08:59:45 +08:00
} else {
2026-01-14 09:55:23 +08:00
Navigator.pushReplacement(
context,
2026-04-13 08:59:45 +08:00
MaterialPageRoute(
builder: (_) => FirmListPage(isBack: false),
),
2026-01-14 09:55:23 +08:00
);
}
2025-12-12 09:11:30 +08:00
} else {
2026-01-14 09:55:23 +08:00
ToastUtil.showNormal(context, res['errMessage'] ?? '保存失败');
2025-12-12 09:11:30 +08:00
}
});
2026-04-13 08:59:45 +08:00
} else {
2025-12-12 09:11:30 +08:00
ToastUtil.showNormal(context, '保存失败');
LoadingDialogHelper.hide();
}
}
2026-04-13 08:59:45 +08:00
2025-12-12 09:11:30 +08:00
Future<bool> _checkDeleteImage() async {
late bool isSuccess = true;
if (_idCardImgRemoveList.isNotEmpty) {
final delIds = _idCardImgRemoveList;
2026-04-13 08:59:45 +08:00
try {
2026-01-14 09:55:23 +08:00
await FileApi.deleteImages(delIds).then((result) {
if (result['success']) {
isSuccess = true;
} else {
isSuccess = false;
}
});
2026-04-13 08:59:45 +08:00
} catch (e) {
2026-01-14 09:55:23 +08:00
LoadingDialogHelper.hide();
}
2026-04-13 08:59:45 +08:00
} else {
2025-12-12 09:11:30 +08:00
isSuccess = true;
}
return isSuccess;
}
Future<bool> _checkFaceImage() async {
final faceImgPath = pd['faceFiles'] ?? '';
UploadFileType fileType = UploadFileType.userAvatar;
late bool isSuccess = true;
if (faceImgPath.isNotEmpty) {
try {
2026-04-13 08:59:45 +08:00
await FileApi.uploadFile(faceImgPath, fileType, _userId ?? '')
.then((result) {
2025-12-12 09:11:30 +08:00
if (result['success']) {
pd['userAvatarUrl'] = result['data']['filePath'] ?? '';
2026-04-14 16:24:31 +08:00
pd['userId'] = result['data']['foreignKey'] ?? '';
isSuccess = true;
2025-12-12 09:11:30 +08:00
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, '人脸照片上传失败');
isSuccess = false;
}
});
} catch (e) {
LoadingDialogHelper.hide();
2025-12-24 16:07:53 +08:00
ToastUtil.showNormal(context, '人脸照片上传失败');
2025-12-12 09:11:30 +08:00
isSuccess = false;
}
}
return isSuccess;
}
Future<bool> _checkIDCartImages() async {
late bool isSuccess = true;
if (idPhotos.isEmpty) {
return isSuccess;
}
try {
await FileApi.uploadFiles(
idPhotos,
UploadFileType.idCardPhoto,
2025-12-24 16:07:53 +08:00
_userId ?? '',
2025-12-12 09:11:30 +08:00
).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;
2026-04-14 16:24:31 +08:00
if (!_isEdit && FormUtils.hasValue(pd, 'userAvatarUrl')) {
2025-12-12 09:11:30 +08:00
isShow = true;
}
2026-01-14 09:55:23 +08:00
String token = SessionService.instance.token ?? '';
2025-12-12 09:11:30 +08:00
return Scaffold(
appBar: MyAppbar(
title: '信息补充',
isBack: (!_isEdit || _isChange),
onBackPressed: () async {
if (_isChange) {
await CustomAlertDialog.showConfirm(
context,
title: '温馨提示',
content: '是否放弃修改?',
cancelText: '取消',
).then(
2026-04-13 08:59:45 +08:00
(isSure) => {
2025-12-12 09:11:30 +08:00
if (isSure) {Navigator.pop(context)},
},
);
} else {
Navigator.pop(context);
}
},
actions: [
if (!_isEdit)
TextButton(
onPressed: () {
setState(() {
_isChange = true;
_isEdit = true;
});
},
2026-04-13 08:59:45 +08:00
child: const Text(
2025-12-12 09:11:30 +08:00
'修改',
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
],
),
body: SafeArea(
child: ItemListWidget.itemContainer(
horizontal: 5,
isShow
? ListView(
2026-04-13 08:59:45 +08:00
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),
2025-12-12 09:11:30 +08:00
),
2026-04-13 08:59:45 +08:00
),
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);
},
),
],
2025-12-12 09:11:30 +08:00
);
},
2026-04-13 08:59:45 +08:00
);
},
),
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),
2025-12-12 09:11:30 +08:00
),
2026-04-13 08:59:45 +08:00
),
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,
2025-12-12 09:11:30 +08:00
),
2026-04-13 08:59:45 +08:00
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,
2025-12-12 09:11:30 +08:00
),
2026-04-13 08:59:45 +08:00
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();
},
),
],
)
2025-12-12 09:11:30 +08:00
: const SizedBox(),
),
),
);
}
2026-04-13 08:59:45 +08:00
}