QinGang_interested/lib/pages/user/full_userinfo_page.dart

712 lines
25 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<FullUserinfoPage> createState() => _FullUserinfoPageState();
}
class _FullUserinfoPageState extends State<FullUserinfoPage> {
Map<String, dynamic> 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<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['username'] = SessionService.instance.userName;
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'];
_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<void> _getKeyValues() async {
await BasicInfoApi.getDictValues('wenhuachengdu').then((res) {
_wenhuachengduList = res['data'];
});
await BasicInfoApi.getDictValues('zhengzhimianmao').then((res) {
_zhengzhimianmaoList = res['data'];
});
}
Future<void> _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<bool> _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<bool> _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<bool> _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(),
),
),
);
}
}