2026.3.9 一级口门

master
xufei 2026-03-09 11:28:49 +08:00
parent 192d24cea5
commit f4f2b3c566
7 changed files with 1662 additions and 79 deletions

View File

@ -247,13 +247,17 @@ class _DoorareaCarAddPageState extends State<DoorareaCarAddPage> {
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择时间:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '选择时间:',
isEditable: false,
text: pd['departmentName'] ?? '',
isRequired: true,
onTap: () async {},
),
),
const Divider(),
ItemListWidget.multiLineTitleTextField(
label: '申请原因',

View File

@ -0,0 +1,528 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:intl/intl.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/single_image_viewer.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/modules/basic_info_api.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart';
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
import 'package:qhd_prevention/pages/mine/webViewPage.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/services/SessionService.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:flutter/gestures.dart';
class FirstlevelCarAddPage extends StatefulWidget {
const FirstlevelCarAddPage({super.key});
@override
State<FirstlevelCarAddPage> createState() => _FirstlevelCarAddPageState();
}
class _FirstlevelCarAddPageState extends State<FirstlevelCarAddPage> {
Map<String, dynamic> pd = {};
bool _agreed = false;
//
List<dynamic> _deptList = [];
List<Map> _personList = [];
List<String> signImages = [];
late bool _isMyCompanyArea = false;
late bool _isSelectCar = false;
List<String> _vehicleImages = [];
List<String> _vehicleLicenseImages = [];
@override
void initState() {
super.initState();
_getDept();
}
//
Future<void> _getDept() async {
// try {
// final data = {
// 'eqCorpinfoId': widget.scanData['id'],
// // 'eqParentId': widget.scanData['corpinfoId'],
// };
// final result = await BasicInfoApi.getDeptTree(data);
// if (result['success'] == true) {
// final list = result['data'] ?? [];
// if (list.length > 0) {
// setState(() {
// _deptList = list[0]['childrenList'] ?? [];
// });
// }
// }
// } catch (e) {}
}
//
Future<void> _saveSuccess() async {
if (!FormUtils.hasValue(pd, 'corpinfoId')) {
ToastUtil.showNormal(context, '请选择部门');
return;
}
if (!FormUtils.hasValue(pd, 'postName')) {
ToastUtil.showNormal(context, '请输入岗位');
return;
}
// try {
// final result = await BasicInfoApi.userFirmEntry(pd);
// LoadingDialogHelper.hide();
// if (result['success'] == true) {
// ToastUtil.showNormal(context, '申请成功');
// _relogin();
// } else {
// ToastUtil.showNormal(context, result['errMessage']);
// }
// } catch (e) {
// LoadingDialogHelper.hide();
// ToastUtil.showNormal(context, '操作失败,请重试');
// }
}
Widget _addPersonWight(Map personData) {
return Stack(
children: [
Padding(padding: const EdgeInsets.only(top: 5),child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
// padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
//
border: Border.all(
color: Colors.grey.shade300,
width: 1.0,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.circular(5),
),
child: Column(
children: [
const SizedBox(height: 10),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择部门:',
isEditable: true,
text: personData['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择人员:',
isEditable: true,
text: personData['postName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.itemContainer(RepairedPhotoSection(
isRequired: true,
title: "人员照片",
maxCount: 1,
horizontalPadding: 0,
mediaType: MediaType.image,
isShowAI: false,
onChanged: (List<File> files) {},
onAiIdentify: () {},
),),
const SizedBox(height: 10)
],
)
),),
Positioned(
top: 0,
right: 2,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
),
child: GestureDetector(
child: const Icon(Icons.close, size: 14, color: Colors.white,),
onTap: () {
setState(() {
_personList.remove(personData);
});
},
),
),
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '车辆申请'),
body: SafeArea(
child: ItemListWidget.itemContainer(
horizontal: 5,
vertical: 12,
ListView(
children: [
ListItemFactory.createBuildSimpleSection('申请信息'),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择项目名称:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '审核人员:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '选择时间:',
isEditable: false,
text: pd['departmentName'] ?? '',
isRequired: true,
onTap: () async {},
),
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '访问港区:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择范围:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
// ItemListWidget.multiLineTitleTextField(
// label: '申请原因',
// isEditable: true,
// ),
///
ListItemFactory.createBuildSimpleSection('车辆信息'),
const Divider(),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '驾驶人部门',
isEditable: !_isSelectCar,
onTap: () {
},
text: '',
),
),
const Divider(height: 1,),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '驾驶人',
isEditable: !_isSelectCar,
onTap: () {
},
text: '',
),
),
const Divider(height: 1,),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '车牌类型',
isEditable: !_isSelectCar,
onTap: () {
},
text: '',
),
),
const Divider(height: 1,),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '车辆类型',
isEditable: !_isSelectCar,
onTap: () {
},
text: '',
),
),
const Divider(height: 1,),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '车牌号',
isEditable: !_isSelectCar,
onTap: () {
},
text: '',
),
),
// ItemListWidget.singleLineTitleText(
// label: '车牌号',
// isEditable: !_isSelectCar,
// text: '',
// onChanged: (value) {
//
// },
// ),
const Divider(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: RepairedPhotoSection(
isRequired:true,
title: "车辆照片",
maxCount: 4,
mediaType: MediaType.image,
isShowAI: false,
onChanged: (List<File> files) {
// files
_vehicleImages.clear();
for(int i=0;i<files.length;i++){
_vehicleImages.add(files[i].path);
}
},
onAiIdentify: () {
},
),),
const Divider(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: RepairedPhotoSection(
isRequired:true,
title: "行驶证照片",
maxCount: 2,
mediaType: MediaType.image,
isShowAI: false,
onChanged: (List<File> files) {
// files
_vehicleLicenseImages.clear();
for(int i=0;i<files.length;i++){
_vehicleLicenseImages.add(files[i].path);
}
},
onAiIdentify: () {
},
),),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Checkbox(
value: _agreed,
activeColor: Colors.blue,
checkColor: Colors.white,
side: const BorderSide(color: Colors.grey),
onChanged: (value) {
setState(() {
_agreed = value ?? false;
});
},
),
Flexible(
child: RichText(
text: TextSpan(
children: [
const TextSpan(
text: '我已阅读并同意',
style: TextStyle(color: Colors.black, fontSize: 12),
),
TextSpan(
text: '《安全进港须知》',
style: const TextStyle(
color: Colors.blue,
fontSize: 12,
),
recognizer:
TapGestureRecognizer()
..onTap = () async {
final path = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SignInstructionsWebview(
name: "安全进港须知",
url:
'http://47.92.102.56:7811/file/xieyi/zsyhxy.htm',
)),
);
await NativeOrientation.setPortrait();
if (path != null) {
setState(() {
setState(() {
_agreed = true;
signImages = [];
signImages.add(path);
});
});
}
// pushPage(
// const SignInstructionsWebview(
// name: "安全进港须知",
// url:
// 'http://47.92.102.56:7811/file/xieyi/zsyhxy.htm',
// ),
// context,
// );
},
),
],
),
),
),
],
),
// Container(
// padding: EdgeInsets.symmetric(horizontal: 12),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// ListItemFactory.headerTitle('签字:', isRequired: true),
// CustomButton(
// text: signImages.isNotEmpty ? '重新签字' : '手写签字',
// height: 36,
// backgroundColor: Colors.blue,
// onPressed: _sign,
// ),
// ],
// ),
// ),
if (signImages.isNotEmpty) _signListWidget(),
SizedBox(height: 10,),
CustomButton(
text: '提交申请',
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
height: 40,
backgroundColor: Colors.blue,
onPressed: () {
_saveSuccess();
},
),
],
),
),
),
);
}
Widget _signListWidget() {
return Column(
children:
signImages.map((path) {
return Column(
children: [
const SizedBox(height: 15),
// const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
child: // ConstrainedBox BoxFit.contain
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 200,
maxHeight: 150,
),
child: Image.file(
File(path),
//
fit: BoxFit.contain,
),
),
onTap: () {
presentOpaque(
SingleImageViewer(imageUrl: path),
context,
);
},
),
Column(
children: [
Container(
padding: const EdgeInsets.only(right: 5),
child: CustomButton(
text: 'X',
height: 30,
padding: const EdgeInsets.symmetric(horizontal: 10),
backgroundColor: Colors.red,
onPressed: () {
setState(() {
signImages.remove(path);
});
},
),
),
const SizedBox(height: 80),
],
),
],
),
],
);
}).toList(),
);
}
}

View File

@ -222,7 +222,7 @@ class _DoorcarTabPageState extends State<DoorcarTabPage> {
builder: (context, constraints) {
const spacing = 20.0;
final totalWidth = constraints.maxWidth;
final itemWidth = (totalWidth - spacing * 2) / 3;
final itemWidth = (totalWidth - spacing * 2) / 2;
return Wrap(
spacing: spacing,
runSpacing: spacing,
@ -240,6 +240,13 @@ class _DoorcarTabPageState extends State<DoorcarTabPage> {
},
),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 5),
child: Text('*申请封闭区域时,系统已自动包含进港口门权限,您无需再次申请进港口门权限。',
style: const TextStyle(
fontSize: 12, fontWeight: FontWeight.bold,color: Colors.red)),
),
],
),
),

View File

@ -12,6 +12,7 @@ import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/modules/basic_info_api.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/person_selection_page.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart';
@ -36,7 +37,7 @@ class _DoorareaPersonApplyPageState extends State<DoorareaPersonApplyPage> {
//
List<dynamic> _deptList = [];
List<Map> _personList = [];
List<Person> _personList = [];
List<String> signImages = [];
@override
@ -89,55 +90,69 @@ class _DoorareaPersonApplyPageState extends State<DoorareaPersonApplyPage> {
// }
}
Widget _addPersonWight(Map personData) {
Widget _addPersonWight(Person personData) {
return Stack(
children: [
Padding(padding: const EdgeInsets.only(top: 5),child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
// padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
//
border: Border.all(
color: Colors.grey.shade300,
width: 1.0,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.circular(5),
),
Card(
margin: EdgeInsets.only(bottom: 12),
color: Colors.white,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
const SizedBox(height: 10),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择部门:',
isEditable: true,
text: personData['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
//
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
'姓名: ${personData.name??''}',
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
),
// _buildInfoItem('姓名:', person['姓名'] ?? '',),
SizedBox(width: 24),
Expanded(
child: Text(
'部门: ${personData.group??''}',
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
),
// _buildInfoItem('部门:', person['部门'] ?? ''),
],
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择人员:',
isEditable: true,
text: personData['postName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.itemContainer(RepairedPhotoSection(
isRequired: true,
title: "人员照片",
maxCount: 1,
horizontalPadding: 0,
mediaType: MediaType.image,
isShowAI: false,
onChanged: (List<File> files) {},
onAiIdentify: () {},
),),
const SizedBox(height: 10)
// SizedBox(height: 8),
//
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// CustomButton(
// height: 35,
// onPressed: () async {
//
// },
// backgroundColor: Colors.red,
// textStyle: const TextStyle(color: Colors.black),
// buttonStyle:ButtonStyleType.primary,
// text: '删除')
// ],
// ),
],
)
),),
),
),
),
Positioned(
top: 0,
right: 2,
@ -236,12 +251,15 @@ class _DoorareaPersonApplyPageState extends State<DoorareaPersonApplyPage> {
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择时间:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '选择时间:',
isEditable: false,
text: pd['departmentName'] ?? '',
isRequired: true,
onTap: () async {},
),
),
const Divider(),
ItemListWidget.multiLineTitleTextField(
@ -266,11 +284,18 @@ class _DoorareaPersonApplyPageState extends State<DoorareaPersonApplyPage> {
padding: const EdgeInsets.symmetric(horizontal: 20),
height: 35,
backgroundColor: Colors.blue,
onPressed: () {
setState(() {
_personList.add({});
_addPersonWight({});
});
onPressed: () async {
final result = await Navigator.push<SelectionPersonResult>(
context,
MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList)),
);
if (result != null) {
//
_showSelectedResult(context, result);
}
},
),
const SizedBox(width: 10),
@ -453,6 +478,22 @@ class _DoorareaPersonApplyPageState extends State<DoorareaPersonApplyPage> {
// }
// }
void _showSelectedResult(BuildContext context, SelectionPersonResult result) {
// String message = '选中了 ${result.selectedPersons.length} 人:\n';
// result.groupedSelected.forEach((group, persons) {
// message += '${group}: ${persons.map((p) => p.name).join(', ')}\n';
// });
setState(() {
_personList.clear();
for(int i=0;i<result.selectedPersons.length;i++){
_personList.add(result.selectedPersons[i]);
_addPersonWight(result.selectedPersons[i]);
}
});
}
}

View File

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/car/doorArea_car_add_page.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/car/firstlevel_car_add_page.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/car/onlyLook_car_application.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/car/onlyLook_doorarea_car.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/person/doorArea_person_add_page.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/person/firstlevel_person_add_page.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/person/onlyLook_doorarea_person.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/person/onlyLook_person_application.dart';
@ -309,13 +311,13 @@ class _DoorareaPersonRecordPageState extends State<DoorareaPersonRecordPage> {
await pushPage(DoorareaPersonApplyPage(), context);
break;
case 3:
await pushPage(FirstlevelPersonAddPage(), context);
break;
case 5:
await pushPage(DoorareaCarAddPage(), context);
break;
case 7:
await pushPage(FirstlevelCarAddPage(), context);
break;
}
_fetchData();
@ -326,26 +328,62 @@ class _DoorareaPersonRecordPageState extends State<DoorareaPersonRecordPage> {
],),
body: Column(
children: [
// Filter bar
//
Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
child: SearchBarWidget(
showResetButton: true,
hintText: '请输入关键字',
resetButtonText: '重置',
onReset: () {
FocusScope.of(context).unfocus();
_searchController.text = '';
_search();
},
onSearch: (text) {
FocusScope.of(context).unfocus();
_search();
},
controller: _searchController,
child: Row(
children: [
//
GestureDetector(
onTap: () {
//
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
Text(
'筛选',
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
const SizedBox(width: 2),
Icon(
Icons.expand_more,
size: 20,
color: Colors.black,
),
],
),
),
),
const SizedBox(width: 8),
//
Expanded(
child: SearchBarWidget(
showResetButton: true,
hintText: '请输入关键字',
resetButtonText: '重置',
onReset: () {
FocusScope.of(context).unfocus();
_searchController.text = '';
_search();
},
onSearch: (text) {
FocusScope.of(context).unfocus();
_search();
},
controller: _searchController,
),
),
],
),
),
const Divider(height: 1),
// List
Expanded(child: _buildListContent()),

View File

@ -0,0 +1,469 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:intl/intl.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/single_image_viewer.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/modules/basic_info_api.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/person_selection_page.dart';
import 'package:qhd_prevention/pages/home/doorAndCar/sign_instructions_webView.dart';
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
import 'package:qhd_prevention/pages/mine/webViewPage.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/services/SessionService.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:flutter/gestures.dart';
class FirstlevelPersonAddPage extends StatefulWidget {
const FirstlevelPersonAddPage({super.key});
@override
State<FirstlevelPersonAddPage> createState() =>
_FirstlevelPersonAddPageState();
}
class _FirstlevelPersonAddPageState extends State<FirstlevelPersonAddPage> {
Map<String, dynamic> pd = {};
bool _agreed = false;
//
List<dynamic> _deptList = [];
List<Person> _personList = [];
List<String> signImages = [];
@override
void initState() {
super.initState();
_getDept();
}
//
Future<void> _getDept() async {
// try {
// final data = {
// 'eqCorpinfoId': widget.scanData['id'],
// // 'eqParentId': widget.scanData['corpinfoId'],
// };
// final result = await BasicInfoApi.getDeptTree(data);
// if (result['success'] == true) {
// final list = result['data'] ?? [];
// if (list.length > 0) {
// setState(() {
// _deptList = list[0]['childrenList'] ?? [];
// });
// }
// }
// } catch (e) {}
}
//
Future<void> _saveSuccess() async {
if (!FormUtils.hasValue(pd, 'corpinfoId')) {
ToastUtil.showNormal(context, '请选择部门');
return;
}
if (!FormUtils.hasValue(pd, 'postName')) {
ToastUtil.showNormal(context, '请输入岗位');
return;
}
// try {
// final result = await BasicInfoApi.userFirmEntry(pd);
// LoadingDialogHelper.hide();
// if (result['success'] == true) {
// ToastUtil.showNormal(context, '申请成功');
// _relogin();
// } else {
// ToastUtil.showNormal(context, result['errMessage']);
// }
// } catch (e) {
// LoadingDialogHelper.hide();
// ToastUtil.showNormal(context, '操作失败,请重试');
// }
}
Widget _addPersonWight(Person personData) {
return Stack(
children: [
Card(
margin: EdgeInsets.only(bottom: 12),
color: Colors.white,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
//
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
'姓名: ${personData.name??''}',
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
),
// _buildInfoItem('姓名:', person['姓名'] ?? '',),
SizedBox(width: 24),
Expanded(
child: Text(
'部门: ${personData.group??''}',
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
),
// _buildInfoItem('部门:', person['部门'] ?? ''),
],
),
// SizedBox(height: 8),
//
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// CustomButton(
// height: 35,
// onPressed: () async {
//
// },
// backgroundColor: Colors.red,
// textStyle: const TextStyle(color: Colors.black),
// buttonStyle:ButtonStyleType.primary,
// text: '删除')
// ],
// ),
],
),
),
),
Positioned(
top: 0,
right: 2,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
),
child: GestureDetector(
child: const Icon(Icons.close, size: 14, color: Colors.white,),
onTap: () {
setState(() {
_personList.remove(personData);
});
},
),
),
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '人员申请'),
body: SafeArea(
child: ItemListWidget.itemContainer(
horizontal: 5,
vertical: 12,
ListView(
children: [
ListItemFactory.createBuildSimpleSection('申请信息'),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择项目名称:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '审核人员:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ItemListWidget.selectableLineTitleTextRightButton(
label: '选择时间:',
isEditable: false,
text: pd['departmentName'] ?? '',
isRequired: true,
onTap: () async {},
),
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '访问港区:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
const Divider(),
ItemListWidget.selectableLineTitleTextRightButton(
label: '选择范围:',
isEditable: true,
text: pd['departmentName'] ?? '请选择',
isRequired: true,
onTap: () async {},
),
// const Divider(),
// ItemListWidget.multiLineTitleTextField(
// label: '申请原因',
// isEditable: true,
// ),
const Divider(),
if (_personList.isNotEmpty)
...[
ListItemFactory.createBuildSimpleSection('人员信息'),
// const Divider(),
for (var i = 0; i < _personList.length; i++)
_addPersonWight(_personList[i]),
],
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: '添加人员',
padding: const EdgeInsets.symmetric(horizontal: 20),
height: 35,
backgroundColor: Colors.blue,
onPressed: () async {
final result = await Navigator.push<SelectionPersonResult>(
context,
MaterialPageRoute(builder: (context) => PersonSelectionPage(_personList)),
);
if (result != null) {
//
_showSelectedResult(context, result);
}
},
),
const SizedBox(width: 10),
],
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Checkbox(
value: _agreed,
activeColor: Colors.blue,
checkColor: Colors.white,
side: const BorderSide(color: Colors.grey),
onChanged: (value) {
setState(() {
_agreed = value ?? false;
});
},
),
Flexible(
child: RichText(
text: TextSpan(
children: [
const TextSpan(
text: '我已阅读并同意',
style: TextStyle(color: Colors.black, fontSize: 12),
),
TextSpan(
text: '《安全进港须知》',
style: const TextStyle(
color: Colors.blue,
fontSize: 12,
),
recognizer:
TapGestureRecognizer()
..onTap = () async {
final path = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SignInstructionsWebview(
name: "安全进港须知",
url:
'http://47.92.102.56:7811/file/xieyi/zsyhxy.htm',
)),
);
await NativeOrientation.setPortrait();
if (path != null) {
setState(() {
setState(() {
_agreed = true;
signImages = [];
signImages.add(path);
});
});
}
},
),
],
),
),
),
],
),
// Container(
// padding: EdgeInsets.symmetric(horizontal: 12),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// ListItemFactory.headerTitle('签字:', isRequired: true),
// CustomButton(
// text: signImages.isNotEmpty ? '重新签字' : '手写签字',
// height: 36,
// backgroundColor: Colors.blue,
// onPressed: _sign,
// ),
// ],
// ),
// ),
if (signImages.isNotEmpty) _signListWidget(),
SizedBox(height: 10,),
CustomButton(
text: '提交申请',
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
height: 40,
backgroundColor: Colors.blue,
onPressed: () {
_saveSuccess();
},
),
],
),
),
),
);
}
Widget _signListWidget() {
return Column(
children:
signImages.map((path) {
return Column(
children: [
const SizedBox(height: 15),
// const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
child: // ConstrainedBox BoxFit.contain
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 200,
maxHeight: 150,
),
child: Image.file(
File(path),
//
fit: BoxFit.contain,
),
),
onTap: () {
presentOpaque(
SingleImageViewer(imageUrl: path),
context,
);
},
),
Column(
children: [
Container(
padding: const EdgeInsets.only(right: 5),
child: CustomButton(
text: 'X',
height: 30,
padding: const EdgeInsets.symmetric(horizontal: 10),
backgroundColor: Colors.red,
onPressed: () {
setState(() {
signImages.remove(path);
});
},
),
),
const SizedBox(height: 80),
],
),
],
),
],
);
}).toList(),
);
}
///
// Future<void> _sign() async {
// final name = SessionService.instance.name ?? '';
// await NativeOrientation.setLandscape();
// final path = await Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => MineSignPage(personName: name,)),
// );
// await NativeOrientation.setPortrait();
// if (path != null) {
// setState(() {
// signImages = [];
// signImages.add(path);
// });
// }
// }
void _showSelectedResult(BuildContext context, SelectionPersonResult result) {
// String message = '选中了 ${result.selectedPersons.length} 人:\n';
// result.groupedSelected.forEach((group, persons) {
// message += '${group}: ${persons.map((p) => p.name).join(', ')}\n';
// });
setState(() {
_personList.clear();
for(int i=0;i<result.selectedPersons.length;i++){
_personList.add(result.selectedPersons[i]);
_addPersonWight(result.selectedPersons[i]);
}
});
}
}

View File

@ -0,0 +1,496 @@
import 'package:flutter/material.dart';
import 'package:lpinyin/lpinyin.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
//
class Person {
final String id;
final String name;
final String group;
Person({required this.id, required this.name, required this.group});
}
//
class SelectionPersonResult {
final List<Person> selectedPersons;
final Map<String, List<Person>> groupedSelected;
SelectionPersonResult({
required this.selectedPersons,
required this.groupedSelected,
});
}
class PersonSelectionPage extends StatefulWidget {
const PersonSelectionPage(this.oldList, {super.key});
final List<Person> oldList;
@override
State<PersonSelectionPage> createState() => _PersonSelectionPageState();
}
class _PersonSelectionPageState extends State<PersonSelectionPage> {
// #
final List<String> alphabet = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#'
];
//
late final Map<String, List<Person>> groupedPersons;
//
late final List<String> sortedGroupKeys;
// GlobalKey
final Map<String, GlobalKey> groupKeys = {};
final List<Map<String, dynamic>> list = [
{
'id': 'p1',
'name': '',
},
{
'id': 'p2',
'name': '',
},
{
'id': 'p3',
'name': '',
},
{
'id': 'p4',
'name': '',
},
{
'id': 'p5',
'name': '',
},
{
'id': 'p6',
'name': '',
},
{
'id': 'p7',
'name': '',
},
{
'id': 'p8',
'name': '',
},
{
'id': 'p9',
'name': '',
},
{
'id': 'p10',
'name': '饿',
},
{
'id': 'p11',
'name': '',
},
{
'id': 'p12',
'name': '',
},
{
'id': 'p13',
'name': '123', //
},
{
'id': 'p14',
'name': '@@@', //
},
];
//
final Map<String, bool> selectedStates = {};
//
final Map<String, bool> groupSelectedStates = {};
//
final ScrollController _scrollController = ScrollController();
final TextEditingController _searchController = TextEditingController();
//
late final Map<String, int> _groupIndexMap = {};
// oldListID
late final Set<String> _oldSelectedIds;
@override
void initState() {
super.initState();
// oldListID
_oldSelectedIds = widget.oldList.map((person) => person.id).toSet();
_initData();
}
void _initData() {
//
groupedPersons = {};
for (var item in list) {
String name = item['name'];
String id = item['id'];
// 使lpinyin
String firstLetter = _getFirstLetter(name);
// Person
Person person = Person(
id: id,
name: name,
group: firstLetter,
);
//
if (!groupedPersons.containsKey(firstLetter)) {
groupedPersons[firstLetter] = [];
}
groupedPersons[firstLetter]!.add(person);
}
//
groupedPersons.forEach((key, value) {
value.sort((a, b) => a.name.compareTo(b.name));
});
// #
sortedGroupKeys = groupedPersons.keys.toList()..sort((a, b) {
// a#a
if (a == '#') return 1;
// b#b
if (b == '#') return -1;
//
return a.compareTo(b);
});
//
for (int i = 0; i < sortedGroupKeys.length; i++) {
_groupIndexMap[sortedGroupKeys[i]] = i;
}
// GlobalKey
for (var group in groupedPersons.keys) {
for (var person in groupedPersons[group]!) {
// oldListtrue
selectedStates[person.id] = _oldSelectedIds.contains(person.id);
}
//
groupSelectedStates[group] = false;
groupKeys[group] = GlobalKey();
}
//
for (var group in groupedPersons.keys) {
_updateGroupSelection(group);
}
}
// 使lpinyin
String _getFirstLetter(String name) {
if (name.isEmpty) return '#';
try {
//
String firstChar = name.substring(0, 1);
RegExp englishLetter = RegExp(r'^[A-Za-z]$');
if (englishLetter.hasMatch(firstChar)) {
return firstChar.toUpperCase();
}
//
RegExp nonChinese = RegExp(r'^[0-9!@#\$%^&*()_+\-=\[\]{};:"\\|,.<>\/?~`]');
if (nonChinese.hasMatch(firstChar)) {
return '#';
}
// - 使WITHOUT_TONE
String pinyin = PinyinHelper.getPinyin(name,
separator: ' ', format: PinyinFormat.WITHOUT_TONE);
if (pinyin.isNotEmpty) {
//
List<String> pinyinParts = pinyin.split(' ');
if (pinyinParts.isNotEmpty) {
String firstCharPinyin = pinyinParts[0];
if (firstCharPinyin.isNotEmpty) {
String firstLetter = firstCharPinyin.substring(0, 1).toUpperCase();
//
if (englishLetter.hasMatch(firstLetter)) {
return firstLetter;
}
}
}
}
} catch (e) {
// #
print('拼音转换错误: $e');
}
// #
return '#';
}
//
void _scrollToGroup(String letter) {
if (!groupedPersons.containsKey(letter)) return;
// 1使
int? index = _groupIndexMap[letter];
if (index != null && _scrollController.hasClients) {
// + item
double position = 0;
for (int i = 0; i < index; i++) {
String group = sortedGroupKeys[i];
// 56item56
position += 56; //
position += (groupedPersons[group]?.length ?? 0) * 56; //
}
_scrollController.animateTo(
position,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
return;
}
// 2使GlobalKey
final key = groupKeys[letter];
if (key?.currentContext != null) {
Scrollable.ensureVisible(
key!.currentContext!,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
alignment: 0, //
);
}
}
//
void _updateGroupSelection(String group) {
final groupPersons = groupedPersons[group]!;
final allSelected = groupPersons.every((person) => selectedStates[person.id] == true);
final anySelected = groupPersons.any((person) => selectedStates[person.id] == true);
setState(() {
groupSelectedStates[group] = allSelected;
});
}
// /
void _toggleGroupSelection(String group) {
final newState = !(groupSelectedStates[group] ?? false);
final groupPersons = groupedPersons[group]!;
setState(() {
groupSelectedStates[group] = newState;
for (var person in groupPersons) {
selectedStates[person.id] = newState;
}
});
}
//
SelectionPersonResult _getSelectedResult() {
List<Person> selectedPersons = [];
Map<String, List<Person>> groupedSelected = {};
for (var group in groupedPersons.keys) {
final groupPersons = groupedPersons[group]!;
final selectedInGroup = groupPersons
.where((person) => selectedStates[person.id] == true)
.toList();
if (selectedInGroup.isNotEmpty) {
groupedSelected[group] = selectedInGroup;
selectedPersons.addAll(selectedInGroup);
}
}
return SelectionPersonResult(
selectedPersons: selectedPersons,
groupedSelected: groupedSelected,
);
}
//
void _confirmSelection() {
final result = _getSelectedResult();
Navigator.pop(context, result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(title: '选择人员', actions: [
TextButton(
onPressed: () {
_confirmSelection();
},
child: Text(
"确认添加",
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
],),
body: Column(
children: [
Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
child: SearchBarWidget(
showResetButton: true,
hintText: '请输入关键字',
resetButtonText: '重置',
onReset: () {
FocusScope.of(context).unfocus();
_searchController.text = '';
_search();
},
onSearch: (text) {
FocusScope.of(context).unfocus();
_search();
},
controller: _searchController,
),
),
//
Expanded(
child: Row(
children: [
//
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: sortedGroupKeys.length,
itemBuilder: (context, index) {
String group = sortedGroupKeys[index];
List<Person> persons = groupedPersons[group]!;
return _buildGroupSection(group, persons);
},
),
),
//
Container(
width: 30,
color: Colors.grey.shade50,
child: ListView.builder(
itemCount: alphabet.length,
itemBuilder: (context, index) {
final letter = alphabet[index];
final hasGroup = groupedPersons.containsKey(letter);
return GestureDetector(
onTap: hasGroup ? () => _scrollToGroup(letter) : null,
child: Container(
height: 30,
alignment: Alignment.center,
child: Text(
letter,
style: TextStyle(
fontSize: 12,
color: hasGroup
? Colors.blue
: Colors.grey.shade400,
fontWeight: hasGroup
? FontWeight.bold
: FontWeight.normal,
),
),
),
);
},
),
),
],
),
),
],
),
);
}
//
Widget _buildGroupSection(String group, List<Person> persons) {
return Container(
key: groupKeys[group],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: Colors.grey.shade100,
height: 56, // 便
child: Row(
children: [
Text(
group,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
],
),
),
//
...persons.map((person) => _buildPersonItem(person)),
],
),
);
}
//
Widget _buildPersonItem(Person person) {
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
),
height: 56, // 便
child: CheckboxListTile(
title: Text(
person.name,
style: const TextStyle(fontSize: 14),
),
value: selectedStates[person.id] ?? false,
onChanged: (bool? value) {
setState(() {
selectedStates[person.id] = value ?? false;
_updateGroupSelection(person.group);
});
},
activeColor: Colors.blue,
controlAffinity: ListTileControlAffinity.leading,
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
),
);
}
void _search() {
// searchKeywords = _searchController.text.trim();
// currentPage = 1;
// list.clear();
// _fetchData();
}
}