QinGang_interested/lib/pages/user/full_userinfo_page.dart

672 lines
21 KiB
Dart
Raw 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 不变
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<FullUserinfoPage> createState() => _FullUserinfoPageState();
}
class _FullUserinfoPageState extends State<FullUserinfoPage> {
Map<String, dynamic> 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<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'];
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<void> _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<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;
}
}
// 保存前再兜底校验一次身份证
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<bool> _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<bool> _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<bool> _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(),
),
),
);
}
}