Compare commits
5 Commits
8ef21d401b
...
3fd86e7ca2
| Author | SHA1 | Date |
|---|---|---|
|
|
3fd86e7ca2 | |
|
|
137ae571e6 | |
|
|
045c18fbf4 | |
|
|
b2b2298929 | |
|
|
8f5b0dc708 |
|
|
@ -12,7 +12,7 @@ class CustomAlertDialog extends StatefulWidget {
|
||||||
final VoidCallback? onCancel;
|
final VoidCallback? onCancel;
|
||||||
final VoidCallback? onConfirm; // 文字模式回调
|
final VoidCallback? onConfirm; // 文字模式回调
|
||||||
final ValueChanged<String>? onInputConfirm; // 输入模式回调
|
final ValueChanged<String>? onInputConfirm; // 输入模式回调
|
||||||
final DialogMode mode; // 新增:对话框模式
|
final DialogMode mode; // 对话框模式
|
||||||
|
|
||||||
const CustomAlertDialog({
|
const CustomAlertDialog({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
|
@ -24,11 +24,87 @@ class CustomAlertDialog extends StatefulWidget {
|
||||||
this.onCancel,
|
this.onCancel,
|
||||||
this.onConfirm,
|
this.onConfirm,
|
||||||
this.onInputConfirm,
|
this.onInputConfirm,
|
||||||
this.mode = DialogMode.text, // 默认文字模式
|
this.mode = DialogMode.text,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CustomAlertDialogState createState() => _CustomAlertDialogState();
|
_CustomAlertDialogState createState() => _CustomAlertDialogState();
|
||||||
|
|
||||||
|
// ------------------ 快捷静态方法 ------------------
|
||||||
|
|
||||||
|
/// 带“取消/确定”的确认弹窗
|
||||||
|
/// 返回 true = 确定,false = 取消或关闭
|
||||||
|
static Future<bool> showConfirm(
|
||||||
|
BuildContext context, {
|
||||||
|
required String title,
|
||||||
|
String content = '',
|
||||||
|
String cancelText = '取消',
|
||||||
|
String confirmText = '确定',
|
||||||
|
bool barrierDismissible = true,
|
||||||
|
}) async {
|
||||||
|
final result = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: barrierDismissible,
|
||||||
|
builder: (_) {
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
cancelText: cancelText,
|
||||||
|
confirmText: confirmText,
|
||||||
|
mode: DialogMode.text,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return result == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 只有“确定”按钮的文字提示弹窗(适合提示信息)
|
||||||
|
static Future<void> showAlert(
|
||||||
|
BuildContext context, {
|
||||||
|
required String title,
|
||||||
|
String content = '',
|
||||||
|
String confirmText = '确定',
|
||||||
|
bool barrierDismissible = true,
|
||||||
|
}) async {
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: barrierDismissible,
|
||||||
|
builder: (_) {
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
cancelText: '', // 隐藏取消按钮使其成为单按钮
|
||||||
|
confirmText: confirmText,
|
||||||
|
mode: DialogMode.text,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 输入对话框(带输入框),返回用户输入的字符串;取消或关闭返回 null
|
||||||
|
static Future<String?> showInput(
|
||||||
|
BuildContext context, {
|
||||||
|
required String title,
|
||||||
|
String hintText = '',
|
||||||
|
String cancelText = '取消',
|
||||||
|
String confirmText = '确定',
|
||||||
|
bool barrierDismissible = true,
|
||||||
|
}) async {
|
||||||
|
final result = await showDialog<String?>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: barrierDismissible,
|
||||||
|
builder: (_) {
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: title,
|
||||||
|
hintText: hintText,
|
||||||
|
cancelText: cancelText,
|
||||||
|
confirmText: confirmText,
|
||||||
|
mode: DialogMode.input,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
|
|
@ -37,7 +113,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// 输入模式下初始化 TextField
|
// 输入模式下初始化 TextField 控制器
|
||||||
_controller = TextEditingController();
|
_controller = TextEditingController();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,28 +130,32 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
return Dialog(
|
return Dialog(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(minWidth: 280),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Text(
|
Padding(
|
||||||
widget.title,
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
child: Text(
|
||||||
textAlign: TextAlign.center,
|
widget.title,
|
||||||
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// ★ 根据 mode 决定展示文字还是输入框 ★
|
// ★ 根据 mode 决定展示文字还是输入框 ★
|
||||||
if (widget.mode == DialogMode.text)
|
if (widget.mode == DialogMode.text)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.content,
|
widget.content,
|
||||||
style: const TextStyle(fontSize: 16, color: Colors.black45),
|
style: const TextStyle(fontSize: 16, color: Colors.black54),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -84,7 +164,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
|
autofocus: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
|
|
@ -104,9 +184,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
|
|
||||||
hasCancel
|
hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context),
|
||||||
? _buildDoubleButtons(context)
|
|
||||||
: _buildSingleButton(context),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -120,8 +198,11 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
// 根据模式返回不同值:文本模式返回 false,输入模式返回 null
|
||||||
|
final ret = widget.mode == DialogMode.text ? false : null;
|
||||||
|
// 先触发回调(如果开发者传了),再关闭并把结果返回给调用者
|
||||||
widget.onCancel?.call();
|
widget.onCancel?.call();
|
||||||
|
Navigator.of(context).pop(ret);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
|
@ -130,7 +211,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
widget.cancelText,
|
widget.cancelText,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black87,
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -144,11 +225,13 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
|
||||||
if (widget.mode == DialogMode.text) {
|
if (widget.mode == DialogMode.text) {
|
||||||
widget.onConfirm?.call();
|
widget.onConfirm?.call();
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
} else {
|
} else {
|
||||||
widget.onInputConfirm?.call(_controller.text.trim());
|
final value = _controller.text.trim();
|
||||||
|
widget.onInputConfirm?.call(value);
|
||||||
|
Navigator.of(context).pop(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
@ -172,11 +255,13 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
||||||
Widget _buildSingleButton(BuildContext context) {
|
Widget _buildSingleButton(BuildContext context) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
|
||||||
if (widget.mode == DialogMode.text) {
|
if (widget.mode == DialogMode.text) {
|
||||||
widget.onConfirm?.call();
|
widget.onConfirm?.call();
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
} else {
|
} else {
|
||||||
widget.onInputConfirm?.call(_controller.text.trim());
|
final value = _controller.text.trim();
|
||||||
|
widget.onInputConfirm?.call(value);
|
||||||
|
Navigator.of(context).pop(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
||||||
|
|
@ -16,23 +16,31 @@ class Person {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 回调签名,返回选中用户的 USER_ID 和 NAME
|
/// 原回调签名(向后兼容)
|
||||||
typedef PersonSelectCallback = void Function(String userId, String name);
|
typedef PersonSelectCallback = void Function(String userId, String name);
|
||||||
|
|
||||||
|
/// 新回调签名,增加可选 index(int,默认 0)
|
||||||
|
typedef PersonSelectCallbackWithIndex = void Function(String userId, String name, int index);
|
||||||
|
|
||||||
/// 底部弹窗人员选择器(使用预先传入的原始数据列表,不做接口请求)
|
/// 底部弹窗人员选择器(使用预先传入的原始数据列表,不做接口请求)
|
||||||
class DepartmentPersonPicker {
|
class DepartmentPersonPicker {
|
||||||
/// 显示人员选择弹窗
|
/// 显示人员选择弹窗
|
||||||
///
|
///
|
||||||
/// [personsData]: 已拉取并缓存的原始 Map 列表
|
/// [personsData]: 已拉取并缓存的原始 Map 列表
|
||||||
/// [onSelected]: 选中后回调 USER_ID 和 NAME
|
/// [onSelected]: 选中后回调 USER_ID 和 NAME(向后兼容旧代码)
|
||||||
|
/// [onSelectedWithIndex]: 可选的新回调,额外返回 index(index 为在原始 personsData/_all 中的下标,找不到则为 0)
|
||||||
static Future<void> show(
|
static Future<void> show(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required List<Map<String, dynamic>> personsData,
|
required List<Map<String, dynamic>> personsData,
|
||||||
required PersonSelectCallback onSelected,
|
PersonSelectCallback? onSelected,
|
||||||
|
PersonSelectCallbackWithIndex? onSelectedWithIndex,
|
||||||
}) async {
|
}) async {
|
||||||
|
// 至少传入一个回调(保持对旧调用的兼容)
|
||||||
|
assert(onSelected != null || onSelectedWithIndex != null,
|
||||||
|
'请至少传入 onSelected 或 onSelectedWithIndex');
|
||||||
|
|
||||||
// 转换为模型
|
// 转换为模型
|
||||||
final List<Person> _all =
|
final List<Person> _all = personsData.map((e) => Person.fromJson(e)).toList();
|
||||||
personsData.map((e) => Person.fromJson(e)).toList();
|
|
||||||
List<Person> _filtered = List.from(_all);
|
List<Person> _filtered = List.from(_all);
|
||||||
String _selectedName = '';
|
String _selectedName = '';
|
||||||
String _selectedId = '';
|
String _selectedId = '';
|
||||||
|
|
@ -54,9 +62,7 @@ class DepartmentPersonPicker {
|
||||||
setState(() {
|
setState(() {
|
||||||
_filtered = q.isEmpty
|
_filtered = q.isEmpty
|
||||||
? List.from(_all)
|
? List.from(_all)
|
||||||
: _all
|
: _all.where((p) => p.name.toLowerCase().contains(q)).toList();
|
||||||
.where((p) => p.name.toLowerCase().contains(q))
|
|
||||||
.toList();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,24 +73,24 @@ class DepartmentPersonPicker {
|
||||||
children: [
|
children: [
|
||||||
// 顶部:取消、搜索、确定
|
// 顶部:取消、搜索、确定
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
horizontal: 16, vertical: 8),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(ctx).pop(),
|
onPressed: () => Navigator.of(ctx).pop(),
|
||||||
child: const Text('取消',style: TextStyle(fontSize: 16),),
|
child: const Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
horizontal: 8),
|
|
||||||
child: SearchBarWidget(
|
child: SearchBarWidget(
|
||||||
controller: _searchController,
|
controller: _searchController,
|
||||||
onTextChanged: _onSearch,
|
onTextChanged: _onSearch,
|
||||||
isShowSearchButton: false,
|
isShowSearchButton: false,
|
||||||
onSearch: (keyboard) {
|
onSearch: (keyboard) {},
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -93,9 +99,22 @@ class DepartmentPersonPicker {
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
onSelected(_selectedId, _selectedName);
|
|
||||||
|
// 计算 index(在原始 _all 列表中的下标)
|
||||||
|
final idx = _all.indexWhere((p) => p.userId == _selectedId);
|
||||||
|
final validIndex = idx >= 0 ? idx : 0;
|
||||||
|
|
||||||
|
// 优先调用带 index 的回调(新),否则调用旧回调
|
||||||
|
if (onSelectedWithIndex != null) {
|
||||||
|
onSelectedWithIndex(_selectedId, _selectedName, validIndex);
|
||||||
|
} else if (onSelected != null) {
|
||||||
|
onSelected(_selectedId, _selectedName);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: const Text('确定', style: TextStyle(color: Colors.green, fontSize: 16),),
|
child: const Text(
|
||||||
|
'确定',
|
||||||
|
style: TextStyle(color: Colors.green, fontSize: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -112,8 +131,7 @@ class DepartmentPersonPicker {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
titleAlignment: ListTileTitleAlignment.center,
|
titleAlignment: ListTileTitleAlignment.center,
|
||||||
title: Text(person.name),
|
title: Text(person.name),
|
||||||
trailing:
|
trailing: selected ? const Icon(Icons.check, color: Colors.green) : null,
|
||||||
selected ? const Icon(Icons.check, color: Colors.green) : null,
|
|
||||||
onTap: () => setState(() {
|
onTap: () => setState(() {
|
||||||
_selectedId = person.userId;
|
_selectedId = person.userId;
|
||||||
_selectedName = person.name;
|
_selectedName = person.name;
|
||||||
|
|
@ -131,4 +149,4 @@ class DepartmentPersonPicker {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1228,6 +1228,110 @@ U6Hzm1ninpWeE+awIDAQAB
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// 安全检查确认列表
|
||||||
|
static Future<Map<String, dynamic>> getSafeCheckSureList(String INSPECTION_ID) {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
'/app/safetyenvironmental/goShow',
|
||||||
|
method: Method.post,
|
||||||
|
data: {
|
||||||
|
"INSPECTION_ID":INSPECTION_ID,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 安全检查操作
|
||||||
|
static Future<Map<String, dynamic>> SafeCheckStartGoEditMsg(
|
||||||
|
String imagePath,
|
||||||
|
String msg,
|
||||||
|
Map data
|
||||||
|
) async {
|
||||||
|
|
||||||
|
Map<String, dynamic> formData = {
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
if (imagePath.isNotEmpty) {
|
||||||
|
final file = File(imagePath);
|
||||||
|
if (!await file.exists()) {
|
||||||
|
throw ApiException('file_not_found', '图片不存在:$imagePath');
|
||||||
|
}
|
||||||
|
final fileName = file.path.split(Platform.pathSeparator).last;
|
||||||
|
formData['FFILE'] =
|
||||||
|
await MultipartFile.fromFile(file.path, filename: fileName);
|
||||||
|
return HttpManager().uploadFaceImage(
|
||||||
|
baseUrl: basePath,
|
||||||
|
path: '/app/safetyenvironmental/$msg',
|
||||||
|
fromData: formData,
|
||||||
|
);
|
||||||
|
}else {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
'/app/safetyenvironmental/$msg',
|
||||||
|
data: formData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// 安全检查核实
|
||||||
|
static Future<Map<String, dynamic>> getSafePersonCheck(Map formData, String imagePath) async {
|
||||||
|
final file = File(imagePath);
|
||||||
|
if (!await file.exists()) {
|
||||||
|
throw ApiException('file_not_found', '图片不存在:$imagePath');
|
||||||
|
}
|
||||||
|
final fileName = file.path
|
||||||
|
.split(Platform.pathSeparator)
|
||||||
|
.last;
|
||||||
|
|
||||||
|
return HttpManager().uploadFaceImage(
|
||||||
|
baseUrl: basePath,
|
||||||
|
path: '/app/safetyenvironmentalinspector/verify',
|
||||||
|
fromData: {
|
||||||
|
...formData,
|
||||||
|
'FFILE': await MultipartFile.fromFile(
|
||||||
|
file.path,
|
||||||
|
filename: fileName
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 安全检查确认
|
||||||
|
static Future<Map<String, dynamic>> getSafePersonSignSure(Map formData, String imagePath) async {
|
||||||
|
final file = File(imagePath);
|
||||||
|
if (!await file.exists()) {
|
||||||
|
throw ApiException('file_not_found', '图片不存在:$imagePath');
|
||||||
|
}
|
||||||
|
final fileName = file.path
|
||||||
|
.split(Platform.pathSeparator)
|
||||||
|
.last;
|
||||||
|
|
||||||
|
return HttpManager().uploadFaceImage(
|
||||||
|
baseUrl: basePath,
|
||||||
|
path: '/app/safetyenvironmentalexplain/add',
|
||||||
|
fromData: {
|
||||||
|
...formData,
|
||||||
|
'FFILE': await MultipartFile.fromFile(
|
||||||
|
file.path,
|
||||||
|
filename: fileName
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 安全检查隐患指派列表
|
||||||
|
static Future<Map<String, dynamic>> getSafeCheckDangerList(String INSPECTION_ID) {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
'/app/hidden/listForSafetyEnvironmental',
|
||||||
|
method: Method.post,
|
||||||
|
data: {
|
||||||
|
"INSPECTION_ID":INSPECTION_ID,
|
||||||
|
'INSPECTION_STATUS': '3-7',
|
||||||
|
'tm': DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
|
'KEYWORDS' : ''
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class MultiTextFieldWithTitle extends StatefulWidget {
|
||||||
required this.hintText,
|
required this.hintText,
|
||||||
required this.onTextsChanged,
|
required this.onTextsChanged,
|
||||||
this.fontSize = 15,
|
this.fontSize = 15,
|
||||||
this.texts = const [],
|
this.texts = const [],
|
||||||
this.isRequired = true,
|
this.isRequired = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -35,14 +35,97 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// 延迟初始化,避免构建过程中调用 setState
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
// 根据传入的初始 texts 初始化 controllers(可编辑时也赋值)
|
||||||
if (mounted) {
|
if (widget.texts.isNotEmpty) {
|
||||||
_addTextField();
|
for (final t in widget.texts) {
|
||||||
|
final controller = TextEditingController(text: t);
|
||||||
|
final node = FocusNode();
|
||||||
|
controller.addListener(() {
|
||||||
|
widget.onTextsChanged(_getAllTexts());
|
||||||
|
});
|
||||||
|
_controllers.add(controller);
|
||||||
|
_focusNodes.add(node);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有初始值,至少创建一个空的控制器(可编辑场景需要输入框)
|
||||||
|
final controller = TextEditingController();
|
||||||
|
final node = FocusNode();
|
||||||
|
controller.addListener(() {
|
||||||
|
widget.onTextsChanged(_getAllTexts());
|
||||||
|
});
|
||||||
|
_controllers.add(controller);
|
||||||
|
_focusNodes.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发一次回调,确保父组件拿到初始值
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) widget.onTextsChanged(_getAllTexts());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant MultiTextFieldWithTitle oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
// 当外部传入的 texts 发生变化(例如父组件替换了初始列表),
|
||||||
|
// 且当前 controllers 与之不一致时,进行同步更新。
|
||||||
|
// 为避免覆盖用户正在编辑的数据,这里只在长度或内容明显不同时才同步。
|
||||||
|
final newTexts = widget.texts;
|
||||||
|
bool needSync = false;
|
||||||
|
|
||||||
|
if (newTexts.length != _controllers.length) {
|
||||||
|
needSync = true;
|
||||||
|
} else {
|
||||||
|
// 长度相等时比较内容
|
||||||
|
for (var i = 0; i < newTexts.length; i++) {
|
||||||
|
final controllerText = _controllers[i].text;
|
||||||
|
if (controllerText != newTexts[i]) {
|
||||||
|
needSync = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needSync) {
|
||||||
|
// 清理旧的 controllers / nodes
|
||||||
|
for (var c in _controllers) {
|
||||||
|
c.dispose();
|
||||||
|
}
|
||||||
|
for (var n in _focusNodes) {
|
||||||
|
n.dispose();
|
||||||
|
}
|
||||||
|
_controllers.clear();
|
||||||
|
_focusNodes.clear();
|
||||||
|
|
||||||
|
// 重新创建
|
||||||
|
if (newTexts.isNotEmpty) {
|
||||||
|
for (final t in newTexts) {
|
||||||
|
final controller = TextEditingController(text: t);
|
||||||
|
final node = FocusNode();
|
||||||
|
controller.addListener(() {
|
||||||
|
widget.onTextsChanged(_getAllTexts());
|
||||||
|
});
|
||||||
|
_controllers.add(controller);
|
||||||
|
_focusNodes.add(node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final controller = TextEditingController();
|
||||||
|
final node = FocusNode();
|
||||||
|
controller.addListener(() {
|
||||||
|
widget.onTextsChanged(_getAllTexts());
|
||||||
|
});
|
||||||
|
_controllers.add(controller);
|
||||||
|
_focusNodes.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知父组件
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) widget.onTextsChanged(_getAllTexts());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (var controller in _controllers) {
|
for (var controller in _controllers) {
|
||||||
|
|
@ -54,9 +137,10 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addTextField() {
|
// _addTextField 现在支持传入初始文本
|
||||||
|
void _addTextField([String initialText = '']) {
|
||||||
setState(() {
|
setState(() {
|
||||||
final newController = TextEditingController();
|
final newController = TextEditingController(text: initialText);
|
||||||
final newFocusNode = FocusNode();
|
final newFocusNode = FocusNode();
|
||||||
|
|
||||||
newController.addListener(() {
|
newController.addListener(() {
|
||||||
|
|
@ -66,32 +150,35 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
_controllers.add(newController);
|
_controllers.add(newController);
|
||||||
_focusNodes.add(newFocusNode);
|
_focusNodes.add(newFocusNode);
|
||||||
widget.onTextsChanged(_getAllTexts());
|
widget.onTextsChanged(_getAllTexts());
|
||||||
|
// 自动聚焦到新创建的输入框(延迟到下一帧)
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) {
|
||||||
|
final idx = _controllers.length - 1;
|
||||||
|
_focusNodes[idx].requestFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _removeTextField(int index) async {
|
void _removeTextField(int index) async {
|
||||||
if (_controllers.length <= 1) return;
|
if (_controllers.length <= 1) return;
|
||||||
await showDialog<String>(
|
final confirmed = await CustomAlertDialog.showConfirm(
|
||||||
context: context,
|
context,
|
||||||
builder:
|
title: '提示',
|
||||||
(_) => CustomAlertDialog(
|
content: '确定删除检查情况吗?',
|
||||||
title: '提示',
|
cancelText: '取消',
|
||||||
mode: DialogMode.text,
|
confirmText: '确定',
|
||||||
content: '确定删除检查情况吗?',
|
|
||||||
cancelText: '取消',
|
|
||||||
confirmText: '确定',
|
|
||||||
onConfirm: () {
|
|
||||||
setState(() {
|
|
||||||
_controllers[index].dispose();
|
|
||||||
_focusNodes[index].dispose();
|
|
||||||
|
|
||||||
_controllers.removeAt(index);
|
|
||||||
_focusNodes.removeAt(index);
|
|
||||||
widget.onTextsChanged(_getAllTexts());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_controllers[index].dispose();
|
||||||
|
_focusNodes[index].dispose();
|
||||||
|
|
||||||
|
_controllers.removeAt(index);
|
||||||
|
_focusNodes.removeAt(index);
|
||||||
|
widget.onTextsChanged(_getAllTexts());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> _getAllTexts() {
|
List<String> _getAllTexts() {
|
||||||
|
|
@ -139,7 +226,7 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
horizontal: 5,
|
horizontal: 5,
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
onPressed: _addTextField,
|
onPressed: () => _addTextField(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -161,20 +248,17 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
...widget.texts.map((c) {
|
...widget.texts.map((c) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: DottedBorderBox(
|
child: DottedBorderBox(
|
||||||
child:
|
child: Text(
|
||||||
Text(
|
c,
|
||||||
c,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: widget.fontSize,
|
||||||
fontSize: widget.fontSize,
|
color: Colors.grey[600],
|
||||||
color: Colors.grey[600],
|
),
|
||||||
),
|
),
|
||||||
),
|
)),
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
],
|
],
|
||||||
|
|
@ -193,19 +277,18 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
|
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: DottedBorderBox(
|
child: DottedBorderBox(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _controllers[index],
|
controller: _controllers[index],
|
||||||
decoration: InputDecoration(hintText: widget.hintText),
|
decoration: InputDecoration(hintText: widget.hintText),
|
||||||
focusNode: _focusNodes[index],
|
focusNode: _focusNodes[index],
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
minLines: 3,
|
// minLines: 3,
|
||||||
style: TextStyle(fontSize: widget.fontSize),
|
style: TextStyle(fontSize: widget.fontSize),
|
||||||
),
|
),
|
||||||
)
|
)),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 删除按钮(叠加在左上角)
|
// 删除按钮(叠加在左上角)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,440 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSign/safecheck_sign_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
|
||||||
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
|
|
||||||
|
class SafecheckSignListPage extends StatefulWidget {
|
||||||
|
final String flow;
|
||||||
|
|
||||||
|
const SafecheckSignListPage({Key? key, required this.flow})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SafecheckSignListPageState createState() => _SafecheckSignListPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckSignListPageState extends State<SafecheckSignListPage> {
|
||||||
|
// Data and state variables
|
||||||
|
List<dynamic> list = [];
|
||||||
|
int currentPage = 1;
|
||||||
|
int rows = 10;
|
||||||
|
int totalPage = 1;
|
||||||
|
bool isLoading = false;
|
||||||
|
List stepList = [
|
||||||
|
{'id': '', 'name': '请选择'},
|
||||||
|
{'id': '0', 'name': '待检查人核实'},
|
||||||
|
{'id': '1', 'name': '检查人核实中'},
|
||||||
|
{'id': '2', 'name': '待被检查人确认'},
|
||||||
|
{'id': '3', 'name': '待指派'},
|
||||||
|
{'id': '4', 'name': '指派中'},
|
||||||
|
{'id': '5', 'name': '指派完成'},
|
||||||
|
{'id': '6', 'name': '检查待验收'},
|
||||||
|
{'id': '7', 'name': '检查已验收'},
|
||||||
|
{'id': '8', 'name': '已归档'},
|
||||||
|
{'id': '-1', 'name': '检查人核实打回'},
|
||||||
|
{'id': '-2', 'name': '被检查人申辩'},
|
||||||
|
];
|
||||||
|
final TextEditingController _searchController = TextEditingController();
|
||||||
|
|
||||||
|
int sindex = 0;
|
||||||
|
String searchKeywords = '';
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> flowList = [];
|
||||||
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchData();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >=
|
||||||
|
_scrollController.position.maxScrollExtent &&
|
||||||
|
!isLoading) {
|
||||||
|
if (currentPage < totalPage) {
|
||||||
|
currentPage++;
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchData() async {
|
||||||
|
if (isLoading) return;
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final data = {
|
||||||
|
'INSPECTION_STATUS': sindex > 0 ? stepList[sindex]['id'] : '',
|
||||||
|
'INSPECTED_SITEUSER_ID': SessionService.instance.loginUserId,
|
||||||
|
'KEYWORDS': searchKeywords,
|
||||||
|
};
|
||||||
|
final url =
|
||||||
|
'/app/safetyenvironmentalexplain/list?showCount=-1¤tPage=$currentPage';
|
||||||
|
final response = await ApiService.getSafeCheckSearchList(data, url);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list = response['varList'];
|
||||||
|
} else {
|
||||||
|
list.addAll(response['varList']);
|
||||||
|
}
|
||||||
|
Map<String, dynamic> page = response['page'];
|
||||||
|
totalPage = page['totalPage'] ?? 1;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching data: $e');
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _search() {
|
||||||
|
searchKeywords = _searchController.text.trim();
|
||||||
|
currentPage = 1;
|
||||||
|
list.clear();
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _goToDetail(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
await pushPage(SafecheckSignDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'] ?? '',isEdit: false), context);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_fetchData();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlowStepItem({
|
||||||
|
required Map<String, dynamic> item,
|
||||||
|
required bool isFirst,
|
||||||
|
required bool isLast,
|
||||||
|
}) {
|
||||||
|
bool status = item['active'] == item['order'];
|
||||||
|
// 依据状态设色
|
||||||
|
final Color dotColor = status ? Colors.blue :Colors.grey;
|
||||||
|
final Color textColor = status ? Colors.blue :Colors.black54;
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
visualDensity: VisualDensity(vertical: -4),
|
||||||
|
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
leading: Container(
|
||||||
|
width: 24,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
// 上方线段或占位
|
||||||
|
isFirst
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
// 圆点
|
||||||
|
CircleAvatar(radius: 6, backgroundColor: dotColor),
|
||||||
|
// 下方线段或占位
|
||||||
|
isLast
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item['title'] ?? '',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 15),
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (item['desc'] != null)
|
||||||
|
Text(
|
||||||
|
item['desc'],
|
||||||
|
style: TextStyle(color: textColor, fontSize: 13),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _checkStatusWithId(String id) {
|
||||||
|
for (Map item in stepList) {
|
||||||
|
if (item['id'] == id) {
|
||||||
|
return item['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListItem(Map<String, dynamic> item) {
|
||||||
|
return Card(
|
||||||
|
color: Colors.white,
|
||||||
|
margin: const EdgeInsets.all(8.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => _goToDetail(item),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"安全检查记录",
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查状态: ${_checkStatusWithId(item['INSPECTION_STATUS'].toString())}",
|
||||||
|
),
|
||||||
|
Text("检查类型: ${item['INSPECTION_TYPE_NAME'] ?? ''}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("检查人: ${item['INSPECTION_USER_NAME'] ?? ''}", maxLines: 5, overflow: TextOverflow.ellipsis),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查发起人: ${item['INSPECTION_ORIGINATOR_NAME'] ?? ''}",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("被检查人: ${item['INSPECTED_SITEUSER_NAME'] ?? ''}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查时间: ${item['INSPECTION_TIME_START'] ?? ''}至${item['INSPECTION_TIME_END'] ?? ''}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
if (item['INSPECTION_STATUS'] == '2')
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
SizedBox(),
|
||||||
|
CustomButton(
|
||||||
|
text: '确认',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () => _goToDetail(item),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示底部选择器
|
||||||
|
Future<void> _showStepPicker() async {
|
||||||
|
if (stepList.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('正在加载步骤数据,请稍后...')));
|
||||||
|
if (stepList.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('无法加载步骤数据')));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建选项列表
|
||||||
|
final options = stepList.map((e) => e['name'] as String).toList();
|
||||||
|
|
||||||
|
// 显示底部选择器
|
||||||
|
final choice = await BottomPicker.show<String>(
|
||||||
|
context,
|
||||||
|
items: options,
|
||||||
|
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||||
|
initialIndex: sindex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (choice != null) {
|
||||||
|
// 找到选择的索引
|
||||||
|
final newIndex = options.indexOf(choice);
|
||||||
|
if (newIndex != -1) {
|
||||||
|
setState(() {
|
||||||
|
sindex = newIndex;
|
||||||
|
});
|
||||||
|
_search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListContent() {
|
||||||
|
if (isLoading && list.isEmpty) {
|
||||||
|
// 初始加载时显示居中的加载指示器
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
} else if (list.isEmpty) {
|
||||||
|
// 没有数据
|
||||||
|
return NoDataWidget.show();
|
||||||
|
} else {
|
||||||
|
// 有数据或加载更多
|
||||||
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
|
||||||
|
controller: _scrollController,
|
||||||
|
itemCount: list.length + (isLoading ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index >= list.length) {
|
||||||
|
// 加载更多时在列表底部显示加载指示器
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildListItem(list[index]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final int lastDoneIndex = flowList.lastIndexWhere((e) => e['STATUS'] == 1);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
key: _scaffoldKey,
|
||||||
|
appBar: MyAppbar(
|
||||||
|
title: '${widget.flow}',
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
endDrawer: Drawer(
|
||||||
|
child: SafeArea(
|
||||||
|
child:
|
||||||
|
flowList.isEmpty
|
||||||
|
? Center(child: Text('暂无流程图数据'))
|
||||||
|
: ListView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
itemCount: flowList.length + 1, // +1 用来放标题
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
if (i == 0) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'查看流程图',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final idx = i - 1;
|
||||||
|
final item = flowList[idx];
|
||||||
|
final bool isFirst = idx == 0;
|
||||||
|
final bool isLast = idx == flowList.length - 1;
|
||||||
|
|
||||||
|
return _buildFlowStepItem(
|
||||||
|
item: item,
|
||||||
|
isFirst: isFirst,
|
||||||
|
isLast: isLast,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
body: SafeArea(child: Column(
|
||||||
|
children: [
|
||||||
|
// Filter bar
|
||||||
|
Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// 底部弹窗选择器
|
||||||
|
SizedBox(
|
||||||
|
width: 65,
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: _showStepPicker,
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 12,
|
||||||
|
horizontal: 5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'筛选',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 16),
|
||||||
|
),
|
||||||
|
Icon(Icons.arrow_drop_down, color: Colors.grey),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SearchBarWidget(
|
||||||
|
showResetButton: false,
|
||||||
|
hintText: "请输入关键字",
|
||||||
|
// isClickableOnly: true,
|
||||||
|
onSearch: (text) {
|
||||||
|
_search();
|
||||||
|
},
|
||||||
|
controller: _searchController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
// List
|
||||||
|
Expanded(child: _buildListContent()),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,471 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
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/department_person_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/dotted_border_box.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.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/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart';
|
||||||
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safeCheck_table.dart';
|
||||||
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safe_drawer_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/app/Danger_paicha/quick_report_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/SafeCheckFormView.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_drawer_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
|
||||||
|
class SafecheckSignDetail extends StatefulWidget {
|
||||||
|
const SafecheckSignDetail({
|
||||||
|
super.key,
|
||||||
|
required this.INSPECTION_ID,
|
||||||
|
required this.INSPECTION_INSPECTOR_ID,
|
||||||
|
required this.isEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String INSPECTION_ID;
|
||||||
|
final String INSPECTION_INSPECTOR_ID;
|
||||||
|
final bool isEdit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SafecheckSignDetail> createState() => _SafecheckSignDetailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckSignDetailState extends State<SafecheckSignDetail> {
|
||||||
|
String msg = 'add';
|
||||||
|
bool _isEdit = false;
|
||||||
|
bool forbidEdit = false;
|
||||||
|
List<String> signImages = [];
|
||||||
|
List<String> signTimes = []; // 签字时间列表
|
||||||
|
Map<String, dynamic> inspectedForm = {};
|
||||||
|
List<Map<String, dynamic>> inspectorRules = [
|
||||||
|
{'name': 'INSPECTION_STATUS', 'message': '核实结果不能为空'},
|
||||||
|
{'name': 'INSPECTION_ID', 'message': '安全检查ID不能为空'},
|
||||||
|
{'name': 'INSPECTION_USER_ID', 'message': '检查人不能为空'},
|
||||||
|
];
|
||||||
|
Map<String, dynamic> form = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_isEdit = widget.isEdit;
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_getData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getData() async {
|
||||||
|
try {
|
||||||
|
final result = await ApiService.getSafeCheckSureList(
|
||||||
|
widget.INSPECTION_ID,
|
||||||
|
);
|
||||||
|
// 在 await 之后检查 mounted,避免页面已经被 pop 导致 setState 报错
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
form = result['pd'] ?? {};
|
||||||
|
inspectedForm = {
|
||||||
|
'INSPECTION_EXPLAIN_ID': '', // 安全检查人员主键ID
|
||||||
|
'INSPECTION_STATUS': '3',
|
||||||
|
'INSPECTION_ID': widget.INSPECTION_ID, // 安全检查ID
|
||||||
|
'INSPECTED_EXPLAIN': '', // 申辩内容
|
||||||
|
'INSPECTED_EXPLAIN_FILENAME': '', // 申辩附件名称
|
||||||
|
'INSPECTED_SITEUSER_SIGN_IMG': '', // 被检查单位现场负责人签字
|
||||||
|
'INSPECTED_SITEUSER_SIGN_TIME': '',
|
||||||
|
};
|
||||||
|
List inspectorVerifyList = form['inspectorVerifyList'] ?? [];
|
||||||
|
for (int i = 0; i < inspectorVerifyList.length; i++) {
|
||||||
|
dynamic item = inspectorVerifyList[i];
|
||||||
|
if (SessionService.instance.loginUserId ==
|
||||||
|
(item['INSPECTION_USER_ID'] ?? '')) {
|
||||||
|
forbidEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e, st) {
|
||||||
|
print('加载单条数据失败: $e\n$st');
|
||||||
|
if (mounted) {
|
||||||
|
ToastUtil.showNormal(context, '加载数据失败:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openDrawer(Map<String, dynamic> hiddenForm, int index) async {
|
||||||
|
try {
|
||||||
|
final result = await openCustomDrawer<Map>(
|
||||||
|
context,
|
||||||
|
SafeCheckDrawerPage(
|
||||||
|
initialHidden: hiddenForm,
|
||||||
|
editType:
|
||||||
|
_isEdit
|
||||||
|
? (index < 0 ? SafeCheckEditType.add : SafeCheckEditType.edit)
|
||||||
|
: SafeCheckEditType.see,
|
||||||
|
toCheckUnitList: [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && mounted) {
|
||||||
|
setState(() {
|
||||||
|
if (index < 0) {
|
||||||
|
// 新增
|
||||||
|
form['hiddenList'].add(result);
|
||||||
|
print(form);
|
||||||
|
} else {
|
||||||
|
// 修改
|
||||||
|
form['hiddenList'][index] = result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("打开抽屉失败: $e");
|
||||||
|
ToastUtil.showNormal(context, "打开抽屉失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T?> openCustomDrawer<T>(BuildContext context, Widget child) {
|
||||||
|
return Navigator.of(context).push<T>(
|
||||||
|
PageRouteBuilder(
|
||||||
|
opaque: false,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: Colors.black54,
|
||||||
|
pageBuilder: (_, __, ___) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FractionallySizedBox(
|
||||||
|
widthFactor: 4 / 5,
|
||||||
|
child: Material(color: Colors.white, child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionsBuilder: (_, anim, __, child) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween(
|
||||||
|
begin: const Offset(1, 0),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将 form['situationList'](若存在)转换为 List<String>
|
||||||
|
List<String> _situationListToStrings() {
|
||||||
|
final List<dynamic> cur = List<dynamic>.from(form['situationList'] ?? []);
|
||||||
|
if (cur.isEmpty) return ['']; // 保持至少一行,和 uni-app 行为一致
|
||||||
|
return cur.map((e) {
|
||||||
|
if (e is Map && e['SITUATION'] != null) return e['SITUATION'].toString();
|
||||||
|
return '';
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------ 提交入口 ------------
|
||||||
|
Future<void> _submit() async {
|
||||||
|
if (forbidEdit) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var rule in inspectorRules) {
|
||||||
|
if ((rule['name'] as String).isEmpty &&
|
||||||
|
!FormUtils.hasValue(inspectedForm, rule['name'])) {
|
||||||
|
ToastUtil.showNormal(context, rule['message']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inspectedForm['INSPECTION_STATUS']?.toString() == '-2' &&
|
||||||
|
(inspectedForm['INSPECTED_EXPLAIN']?.toString().trim().isEmpty ??
|
||||||
|
true)) {
|
||||||
|
ToastUtil.showNormal(context, '请填写申辩说明');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 签字必填
|
||||||
|
if (signImages.isEmpty) {
|
||||||
|
ToastUtil.showNormal(context, '请签字');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示加载
|
||||||
|
LoadingDialogHelper.show();
|
||||||
|
|
||||||
|
inspectedForm['CREATOR'] = SessionService.instance.loginUserId;
|
||||||
|
inspectedForm['ACTION_USER'] = SessionService.instance.username;
|
||||||
|
inspectedForm['CORPINFO_ID'] = SessionService.instance.corpinfoId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 准备上传文件路径(兼容多种 signImgList 存法)
|
||||||
|
String filePath = signImages.first;
|
||||||
|
final resp = await ApiService.getSafePersonSignSure(
|
||||||
|
inspectedForm,
|
||||||
|
filePath,
|
||||||
|
);
|
||||||
|
if (resp['result'] == 'success') {
|
||||||
|
Navigator.of(context).pop(); // 关闭 loading
|
||||||
|
} else {
|
||||||
|
ToastUtil.showNormal(context, '提交失败');
|
||||||
|
}
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
} catch (e) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
|
||||||
|
ToastUtil.showNormal(context, '请求失败:${e.toString()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 罚单提交方法
|
||||||
|
Future<bool> fnSubmit(Map<String, dynamic>? ordForm) async {
|
||||||
|
if (ordForm == null) return false;
|
||||||
|
|
||||||
|
final Map<String, String> punishRules = {
|
||||||
|
'REASON': '请填写处罚原因',
|
||||||
|
'AMOUT': '请填写处罚金额',
|
||||||
|
'DATE': '请选择下发处罚时间',
|
||||||
|
};
|
||||||
|
// 校验
|
||||||
|
for (final entry in punishRules.entries) {
|
||||||
|
final key = entry.key;
|
||||||
|
final msg = entry.value;
|
||||||
|
final val = ordForm[key];
|
||||||
|
if (val == null || val.toString().trim().isEmpty) {
|
||||||
|
ToastUtil.showNormal(context, msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final requestData = Map<String, dynamic>.from(ordForm);
|
||||||
|
requestData['CORPINFO_ID'] = SessionService.instance.corpinfoId ?? '';
|
||||||
|
requestData['CREATOR'] = SessionService.instance.loginUserId ?? '';
|
||||||
|
requestData['OPERATOR'] = SessionService.instance.loginUserId ?? '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoadingDialogHelper.show();
|
||||||
|
final res = await ApiService.safeCheckPunishSubmit(requestData);
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
if (FormUtils.hasValue(res, 'result') && res['result'] == 'success') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
final msg =
|
||||||
|
res != null
|
||||||
|
? (res['msg'] ?? res['msaesge'] ?? res['message'] ?? '提交失败')
|
||||||
|
: '提交失败';
|
||||||
|
ToastUtil.showNormal(context, msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '罚单提交异常:$e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 签字
|
||||||
|
Future<void> _sign() async {
|
||||||
|
final path = await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||||
|
);
|
||||||
|
if (path != null) {
|
||||||
|
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
signImages = [];
|
||||||
|
signTimes = [];
|
||||||
|
signImages.add(path);
|
||||||
|
signTimes.add(now);
|
||||||
|
inspectedForm['INSPECTED_SITEUSER_SIGN_TIME'] = now;
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _signListWidget() {
|
||||||
|
return Column(
|
||||||
|
children:
|
||||||
|
signImages.map((path) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _mainWidget() {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SafeCheckFormView(
|
||||||
|
form: form,
|
||||||
|
isEdit: false,
|
||||||
|
onHiddenShow: (item, index) {
|
||||||
|
_openDrawer(item, index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
if (!forbidEdit)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 10),
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ListItemFactory.createYesNoSection(
|
||||||
|
title: '被检查单位现场负责人意见:',
|
||||||
|
groupValue:
|
||||||
|
inspectedForm['INSPECTION_STATUS'] == '3'
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
|
yesLabel: '同意',
|
||||||
|
noLabel: '申辩',
|
||||||
|
isEdit: true,
|
||||||
|
isRequired: false,
|
||||||
|
horizontalPadding: 3,
|
||||||
|
verticalPadding: 0,
|
||||||
|
onChanged: (val) {
|
||||||
|
// 如果需要把选择写回 form,可以在这里 setState 更新 widget.form
|
||||||
|
setState(() {
|
||||||
|
inspectedForm['INSPECTION_STATUS'] =
|
||||||
|
val ? '3' : '-2';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
if (inspectedForm['INSPECTION_STATUS'] == '-2')
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.multiLineTitleTextField(
|
||||||
|
label: '申辩说明',
|
||||||
|
isEditable: true,
|
||||||
|
isRequired: false,
|
||||||
|
hintText: '检查人意见(有异议时必填)',
|
||||||
|
onChanged: (val) {
|
||||||
|
inspectedForm['INSPECTED_EXPLAIN'] = val;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
ListItemFactory.headerTitle('签字:'),
|
||||||
|
CustomButton(
|
||||||
|
text: '手写签字',
|
||||||
|
height: 36,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: _sign,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (signImages.isNotEmpty)
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
if (signImages.isNotEmpty) _signListWidget(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 150,
|
||||||
|
child: CustomButton(
|
||||||
|
text: !forbidEdit ? '提交' : '返回',
|
||||||
|
textStyle: TextStyle(color: Colors.white, fontSize: 17),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: _submit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: MyAppbar(title: "安全检查确认", actions: []),
|
||||||
|
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
|
child: form.isNotEmpty ? _mainWidget() : SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,445 @@
|
||||||
|
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/custom_button.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/pages/home/SafeCheck/SafeCheckFormView.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_drawer_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
|
||||||
|
class CheckPersonDetail extends StatefulWidget {
|
||||||
|
const CheckPersonDetail({
|
||||||
|
super.key,
|
||||||
|
required this.INSPECTION_ID,
|
||||||
|
required this.INSPECTION_INSPECTOR_ID,
|
||||||
|
required this.isEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String INSPECTION_ID;
|
||||||
|
final String INSPECTION_INSPECTOR_ID;
|
||||||
|
final bool isEdit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CheckPersonDetail> createState() => _CheckPersonDetailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CheckPersonDetailState extends State<CheckPersonDetail> {
|
||||||
|
String msg = 'add';
|
||||||
|
bool _isEdit = false;
|
||||||
|
bool forbidEdit = false;
|
||||||
|
List<String> signImages = [];
|
||||||
|
List<String> signTimes = []; // 签字时间列表
|
||||||
|
Map<String, dynamic> inspectorForm = {};
|
||||||
|
List<Map<String, dynamic>> inspectorRules = [
|
||||||
|
{'name': 'INSPECTION_STATUS', 'message': '核实结果不能为空'},
|
||||||
|
{'name': 'INSPECTION_ID', 'message': '安全检查ID不能为空'},
|
||||||
|
{'name': 'INSPECTION_USER_ID', 'message': '检查人不能为空'},
|
||||||
|
];
|
||||||
|
Map<String, dynamic> form = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_isEdit = widget.isEdit;
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_getData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getData() async {
|
||||||
|
try {
|
||||||
|
final result = await ApiService.getSafeCheckStartGoEdit(
|
||||||
|
widget.INSPECTION_ID,
|
||||||
|
);
|
||||||
|
// 在 await 之后检查 mounted,避免页面已经被 pop 导致 setState 报错
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
form = result['pd'] ?? {};
|
||||||
|
inspectorForm = {
|
||||||
|
'INSPECTION_STATUS': '-1',
|
||||||
|
'INSPECTION_INSPECTOR_ID':widget.INSPECTION_INSPECTOR_ID, // 安全检查人员主键ID
|
||||||
|
'INSPECTION_ID': widget.INSPECTION_ID, // 安全检查ID
|
||||||
|
'INSPECTION_USER_ID': SessionService.instance.loginUserId, // 检查人
|
||||||
|
'INSPECTION_USER_OPINION': '', // 检查人意见(有异议时必填)
|
||||||
|
'INSPECTION_USER_SIGN_IMG': '', // 检查人签字
|
||||||
|
'INSPECTION_USER_SIGN_TIME': '', // 检查人签字时间
|
||||||
|
};
|
||||||
|
List inspectorVerifyList = form['inspectorVerifyList'] ?? [];
|
||||||
|
for (int i = 0; i < inspectorVerifyList.length; i++) {
|
||||||
|
dynamic item = inspectorVerifyList[i];
|
||||||
|
if (SessionService.instance.loginUserId ==
|
||||||
|
(item['INSPECTION_USER_ID'] ?? '')) {
|
||||||
|
forbidEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e, st) {
|
||||||
|
print('加载单条数据失败: $e\n$st');
|
||||||
|
if (mounted) {
|
||||||
|
ToastUtil.showNormal(context, '加载数据失败:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openDrawer(Map<String, dynamic> hiddenForm, int index) async {
|
||||||
|
try {
|
||||||
|
final result = await openCustomDrawer<Map>(
|
||||||
|
context,
|
||||||
|
SafeCheckDrawerPage(
|
||||||
|
initialHidden: hiddenForm,
|
||||||
|
editType:
|
||||||
|
_isEdit
|
||||||
|
? (index < 0 ? SafeCheckEditType.add : SafeCheckEditType.edit)
|
||||||
|
: SafeCheckEditType.see,
|
||||||
|
toCheckUnitList: [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && mounted) {
|
||||||
|
setState(() {
|
||||||
|
if (index < 0) {
|
||||||
|
// 新增
|
||||||
|
form['hiddenList'].add(result);
|
||||||
|
print(form);
|
||||||
|
} else {
|
||||||
|
// 修改
|
||||||
|
form['hiddenList'][index] = result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("打开抽屉失败: $e");
|
||||||
|
ToastUtil.showNormal(context, "打开抽屉失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T?> openCustomDrawer<T>(BuildContext context, Widget child) {
|
||||||
|
return Navigator.of(context).push<T>(
|
||||||
|
PageRouteBuilder(
|
||||||
|
opaque: false,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: Colors.black54,
|
||||||
|
pageBuilder: (_, __, ___) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FractionallySizedBox(
|
||||||
|
widthFactor: 4 / 5,
|
||||||
|
child: Material(color: Colors.white, child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionsBuilder: (_, anim, __, child) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween(
|
||||||
|
begin: const Offset(1, 0),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将 form['situationList'](若存在)转换为 List<String>
|
||||||
|
List<String> _situationListToStrings() {
|
||||||
|
final List<dynamic> cur = List<dynamic>.from(form['situationList'] ?? []);
|
||||||
|
if (cur.isEmpty) return ['']; // 保持至少一行,和 uni-app 行为一致
|
||||||
|
return cur.map((e) {
|
||||||
|
if (e is Map && e['SITUATION'] != null) return e['SITUATION'].toString();
|
||||||
|
return '';
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------ 提交入口 ------------
|
||||||
|
Future<void> _submit() async {
|
||||||
|
if (forbidEdit) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var rule in inspectorRules) {
|
||||||
|
if ((rule['name'] as String).isEmpty && !FormUtils.hasValue(inspectorForm, rule['name'])) {
|
||||||
|
ToastUtil.showNormal(context, rule['message']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inspectorForm['INSPECTION_STATUS']?.toString() == '-1' &&
|
||||||
|
(inspectorForm['INSPECTION_USER_OPINION']?.toString().trim().isEmpty ?? true)) {
|
||||||
|
ToastUtil.showNormal(context, '核实结果有异议时必填');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 签字必填
|
||||||
|
if (signImages.isEmpty) {
|
||||||
|
ToastUtil.showNormal(context, '请签字');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示加载
|
||||||
|
LoadingDialogHelper.show();
|
||||||
|
|
||||||
|
inspectorForm['OPERATOR'] = SessionService.instance.loginUserId;
|
||||||
|
inspectorForm['ACTION_USER'] = SessionService.instance.username;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 准备上传文件路径(兼容多种 signImgList 存法)
|
||||||
|
String filePath = signImages.first;
|
||||||
|
final resp = await ApiService.getSafePersonCheck(inspectorForm, filePath);
|
||||||
|
if (resp['result'] == 'success') {
|
||||||
|
Navigator.of(context).pop(); // 关闭 loading
|
||||||
|
}else {
|
||||||
|
ToastUtil.showNormal(context, '提交失败');
|
||||||
|
}
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
|
||||||
|
ToastUtil.showNormal(context, '请求失败:${e.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 罚单提交方法
|
||||||
|
Future<bool> fnSubmit(Map<String, dynamic>? ordForm) async {
|
||||||
|
if (ordForm == null) return false;
|
||||||
|
|
||||||
|
final Map<String, String> punishRules = {
|
||||||
|
'REASON': '请填写处罚原因',
|
||||||
|
'AMOUT': '请填写处罚金额',
|
||||||
|
'DATE': '请选择下发处罚时间',
|
||||||
|
};
|
||||||
|
// 校验
|
||||||
|
for (final entry in punishRules.entries) {
|
||||||
|
final key = entry.key;
|
||||||
|
final msg = entry.value;
|
||||||
|
final val = ordForm[key];
|
||||||
|
if (val == null || val.toString().trim().isEmpty) {
|
||||||
|
ToastUtil.showNormal(context, msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final requestData = Map<String, dynamic>.from(ordForm);
|
||||||
|
requestData['CORPINFO_ID'] = SessionService.instance.corpinfoId ?? '';
|
||||||
|
requestData['CREATOR'] = SessionService.instance.loginUserId ?? '';
|
||||||
|
requestData['OPERATOR'] = SessionService.instance.loginUserId ?? '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoadingDialogHelper.show();
|
||||||
|
final res = await ApiService.safeCheckPunishSubmit(requestData);
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
if (FormUtils.hasValue(res, 'result') && res['result'] == 'success') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
final msg =
|
||||||
|
res != null
|
||||||
|
? (res['msg'] ?? res['msaesge'] ?? res['message'] ?? '提交失败')
|
||||||
|
: '提交失败';
|
||||||
|
ToastUtil.showNormal(context, msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '罚单提交异常:$e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 签字
|
||||||
|
Future<void> _sign() async {
|
||||||
|
final path = await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||||
|
);
|
||||||
|
if (path != null) {
|
||||||
|
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
signImages = [];
|
||||||
|
signTimes = [];
|
||||||
|
signImages.add(path);
|
||||||
|
signTimes.add(now);
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _signListWidget() {
|
||||||
|
return Column(
|
||||||
|
children:
|
||||||
|
signImages.map((path) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _mainWidget() {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SafeCheckFormView(
|
||||||
|
form: form,
|
||||||
|
isEdit: false,
|
||||||
|
onHiddenShow: (item, index) {
|
||||||
|
_openDrawer(item, index);
|
||||||
|
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
if (!forbidEdit)
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ListItemFactory.createYesNoSection(
|
||||||
|
title: '核实结果:',
|
||||||
|
groupValue: inspectorForm['INSPECTION_STATUS'] == '1' ? true : false,
|
||||||
|
yesLabel: '同意',
|
||||||
|
noLabel: '不同意',
|
||||||
|
isEdit: true,
|
||||||
|
isRequired: false,
|
||||||
|
horizontalPadding: 5,
|
||||||
|
verticalPadding: 0,
|
||||||
|
onChanged: (val) {
|
||||||
|
// 如果需要把选择写回 form,可以在这里 setState 更新 widget.form
|
||||||
|
setState(() {
|
||||||
|
inspectorForm['INSPECTION_STATUS'] = val ? '1' : '-1';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (inspectorForm['INSPECTION_STATUS'] == '-1')
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.multiLineTitleTextField(
|
||||||
|
label: '核实意见',
|
||||||
|
isEditable: true,
|
||||||
|
isRequired: false,
|
||||||
|
hintText: '检查人意见(有异议时必填)',
|
||||||
|
onChanged: (val) {
|
||||||
|
inspectorForm['INSPECTION_USER_OPINION'] = val;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
ListItemFactory.headerTitle('签字:'),
|
||||||
|
CustomButton(
|
||||||
|
text: '手写签字',
|
||||||
|
height: 36,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: _sign,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (signImages.isNotEmpty)
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(children: [if (signImages.isNotEmpty) _signListWidget()]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 150,
|
||||||
|
child: CustomButton(
|
||||||
|
text: !forbidEdit ? '提交' : '返回',
|
||||||
|
textStyle: TextStyle(color: Colors.white, fontSize: 17),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: _submit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: MyAppbar(title: "安全检查确认", actions: []),
|
||||||
|
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
|
child: form.isNotEmpty ? _mainWidget() : SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,438 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
|
||||||
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
|
|
||||||
|
class CheckPersonListPage extends StatefulWidget {
|
||||||
|
final String flow;
|
||||||
|
|
||||||
|
const CheckPersonListPage({Key? key, required this.flow})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CheckPersonListPageState createState() => _CheckPersonListPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CheckPersonListPageState extends State<CheckPersonListPage> {
|
||||||
|
// Data and state variables
|
||||||
|
List<dynamic> list = [];
|
||||||
|
int currentPage = 1;
|
||||||
|
int rows = 10;
|
||||||
|
int totalPage = 1;
|
||||||
|
bool isLoading = false;
|
||||||
|
List stepList = [
|
||||||
|
{'id': '', 'name': '请选择'},
|
||||||
|
{'id': '0', 'name': '待检查人核实'},
|
||||||
|
{'id': '1', 'name': '检查人核实中'},
|
||||||
|
{'id': '2', 'name': '待被检查人确认'},
|
||||||
|
{'id': '3', 'name': '待指派'},
|
||||||
|
{'id': '4', 'name': '指派中'},
|
||||||
|
{'id': '5', 'name': '指派完成'},
|
||||||
|
{'id': '6', 'name': '检查待验收'},
|
||||||
|
{'id': '7', 'name': '检查已验收'},
|
||||||
|
{'id': '8', 'name': '已归档'},
|
||||||
|
{'id': '-1', 'name': '检查人核实打回'},
|
||||||
|
{'id': '-2', 'name': '被检查人申辩'},
|
||||||
|
];
|
||||||
|
final TextEditingController _searchController = TextEditingController();
|
||||||
|
|
||||||
|
int sindex = 0;
|
||||||
|
String searchKeywords = '';
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> flowList = [];
|
||||||
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchData();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >=
|
||||||
|
_scrollController.position.maxScrollExtent &&
|
||||||
|
!isLoading) {
|
||||||
|
if (currentPage < totalPage) {
|
||||||
|
currentPage++;
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchData() async {
|
||||||
|
if (isLoading) return;
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final data = {
|
||||||
|
'INSPECTION_STATUS': sindex > 0 ? stepList[sindex]['id'] : '',
|
||||||
|
'INSPECTION_USER_ID': SessionService.instance.loginUserId,
|
||||||
|
'KEYWORDS': searchKeywords,
|
||||||
|
};
|
||||||
|
final url =
|
||||||
|
'/app/safetyenvironmentalinspector/list?showCount=-1¤tPage=$currentPage';
|
||||||
|
final response = await ApiService.getSafeCheckSearchList(data, url);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list = response['varList'];
|
||||||
|
} else {
|
||||||
|
list.addAll(response['varList']);
|
||||||
|
}
|
||||||
|
Map<String, dynamic> page = response['page'];
|
||||||
|
totalPage = page['totalPage'] ?? 1;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching data: $e');
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _search() {
|
||||||
|
searchKeywords = _searchController.text.trim();
|
||||||
|
currentPage = 1;
|
||||||
|
list.clear();
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _goToDetail(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
await pushPage(CheckPersonDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'],isEdit: false), context);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_fetchData();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlowStepItem({
|
||||||
|
required Map<String, dynamic> item,
|
||||||
|
required bool isFirst,
|
||||||
|
required bool isLast,
|
||||||
|
}) {
|
||||||
|
bool status = item['active'] == item['order'];
|
||||||
|
// 依据状态设色
|
||||||
|
final Color dotColor = status ? Colors.blue :Colors.grey;
|
||||||
|
final Color textColor = status ? Colors.blue :Colors.black54;
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
visualDensity: VisualDensity(vertical: -4),
|
||||||
|
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
leading: Container(
|
||||||
|
width: 24,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
// 上方线段或占位
|
||||||
|
isFirst
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
// 圆点
|
||||||
|
CircleAvatar(radius: 6, backgroundColor: dotColor),
|
||||||
|
// 下方线段或占位
|
||||||
|
isLast
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item['title'] ?? '',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 15),
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (item['desc'] != null)
|
||||||
|
Text(
|
||||||
|
item['desc'],
|
||||||
|
style: TextStyle(color: textColor, fontSize: 13),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _checkStatusWithId(String id) {
|
||||||
|
for (Map item in stepList) {
|
||||||
|
if (item['id'] == id) {
|
||||||
|
return item['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListItem(Map<String, dynamic> item) {
|
||||||
|
return Card(
|
||||||
|
color: Colors.white,
|
||||||
|
margin: const EdgeInsets.all(8.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => _goToDetail(item),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"安全检查记录",
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查状态: ${_checkStatusWithId(item['INSPECTION_STATUS'].toString())}",
|
||||||
|
),
|
||||||
|
Text("检查类型: ${item['INSPECTION_TYPE_NAME'] ?? ''}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("检查人: ${item['INSPECTION_USER_NAME'] ?? ''}", maxLines: 5, overflow: TextOverflow.ellipsis),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查发起人: ${item['INSPECTION_ORIGINATOR_NAME'] ?? ''}",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("被检查人: ${item['INSPECTED_SITEUSER_NAME'] ?? ''}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查时间: ${item['INSPECTION_TIME_START'] ?? ''}至${item['INSPECTION_TIME_END'] ?? ''}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
if (item['INSPECTION_STATUS'] == '0' || item['INSPECTION_STATUS'] == '1')
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
SizedBox(),
|
||||||
|
CustomButton(
|
||||||
|
text: '核实',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () => _goToDetail(item),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示底部选择器
|
||||||
|
Future<void> _showStepPicker() async {
|
||||||
|
if (stepList.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('正在加载步骤数据,请稍后...')));
|
||||||
|
if (stepList.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('无法加载步骤数据')));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建选项列表
|
||||||
|
final options = stepList.map((e) => e['name'] as String).toList();
|
||||||
|
|
||||||
|
// 显示底部选择器
|
||||||
|
final choice = await BottomPicker.show<String>(
|
||||||
|
context,
|
||||||
|
items: options,
|
||||||
|
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||||
|
initialIndex: sindex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (choice != null) {
|
||||||
|
// 找到选择的索引
|
||||||
|
final newIndex = options.indexOf(choice);
|
||||||
|
if (newIndex != -1) {
|
||||||
|
setState(() {
|
||||||
|
sindex = newIndex;
|
||||||
|
});
|
||||||
|
_search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListContent() {
|
||||||
|
if (isLoading && list.isEmpty) {
|
||||||
|
// 初始加载时显示居中的加载指示器
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
} else if (list.isEmpty) {
|
||||||
|
// 没有数据
|
||||||
|
return NoDataWidget.show();
|
||||||
|
} else {
|
||||||
|
// 有数据或加载更多
|
||||||
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
|
||||||
|
controller: _scrollController,
|
||||||
|
itemCount: list.length + (isLoading ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index >= list.length) {
|
||||||
|
// 加载更多时在列表底部显示加载指示器
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildListItem(list[index]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final int lastDoneIndex = flowList.lastIndexWhere((e) => e['STATUS'] == 1);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
key: _scaffoldKey,
|
||||||
|
appBar: MyAppbar(
|
||||||
|
title: '${widget.flow}',
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
endDrawer: Drawer(
|
||||||
|
child: SafeArea(
|
||||||
|
child:
|
||||||
|
flowList.isEmpty
|
||||||
|
? Center(child: Text('暂无流程图数据'))
|
||||||
|
: ListView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
itemCount: flowList.length + 1, // +1 用来放标题
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
if (i == 0) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'查看流程图',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final idx = i - 1;
|
||||||
|
final item = flowList[idx];
|
||||||
|
final bool isFirst = idx == 0;
|
||||||
|
final bool isLast = idx == flowList.length - 1;
|
||||||
|
|
||||||
|
return _buildFlowStepItem(
|
||||||
|
item: item,
|
||||||
|
isFirst: isFirst,
|
||||||
|
isLast: isLast,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
body: SafeArea(child: Column(
|
||||||
|
children: [
|
||||||
|
// Filter bar
|
||||||
|
Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// 底部弹窗选择器
|
||||||
|
SizedBox(
|
||||||
|
width: 65,
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: _showStepPicker,
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 12,
|
||||||
|
horizontal: 5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'筛选',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 16),
|
||||||
|
),
|
||||||
|
Icon(Icons.arrow_drop_down, color: Colors.grey),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SearchBarWidget(
|
||||||
|
showResetButton: false,
|
||||||
|
hintText: "请输入关键字",
|
||||||
|
// isClickableOnly: true,
|
||||||
|
onSearch: (text) {
|
||||||
|
_search();
|
||||||
|
},
|
||||||
|
controller: _searchController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
// List
|
||||||
|
Expanded(child: _buildListContent()),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
|
||||||
|
class SafecheckAssignmentDetailPage extends StatefulWidget {
|
||||||
|
const SafecheckAssignmentDetailPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SafecheckAssignmentDetailPage> createState() => _SafecheckAssignmentDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckAssignmentDetailPageState extends State<SafecheckAssignmentDetailPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: MyAppbar(title: '安全检查指派'),
|
||||||
|
body: SafeArea(child: SizedBox()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,357 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
|
||||||
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
|
|
||||||
|
class SafecheckAssignmentList extends StatefulWidget {
|
||||||
|
final String INSPECTION_ID;
|
||||||
|
|
||||||
|
const SafecheckAssignmentList({Key? key, required this.INSPECTION_ID})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SafecheckAssignmentListState createState() => _SafecheckAssignmentListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckAssignmentListState extends State<SafecheckAssignmentList> {
|
||||||
|
// Data and state variables
|
||||||
|
List<dynamic> list = [];
|
||||||
|
int currentPage = 1;
|
||||||
|
int rows = 10;
|
||||||
|
int totalPage = 1;
|
||||||
|
bool isLoading = false;
|
||||||
|
final List<Map<String, String>> hiddenStatusList = [
|
||||||
|
{'ID': '', 'NAME': '请选择'},
|
||||||
|
{'ID': '1', 'NAME': '待整改'},
|
||||||
|
{'ID': '2', 'NAME': '待验收'},
|
||||||
|
{'ID': '3', 'NAME': '已整改'},
|
||||||
|
{'ID': '4', 'NAME': '已验收'},
|
||||||
|
{'ID': '5', 'NAME': '忽略隐患'},
|
||||||
|
{'ID': '6', 'NAME': '重大隐患'},
|
||||||
|
{'ID': '7', 'NAME': '待处理的特殊申请隐患'},
|
||||||
|
{'ID': '8', 'NAME': '已处理的特殊隐患'},
|
||||||
|
{'ID': '10', 'NAME': '验收打回'},
|
||||||
|
{'ID': '11', 'NAME': '分公司安委会办公室副主任核定'},
|
||||||
|
{'ID': '12', 'NAME': '分公司安委会办公室副主任核定'},
|
||||||
|
{'ID': '13', 'NAME': '港股分公司安委会办公室副主任核定、重大隐患待整改'},
|
||||||
|
{'ID': '13', 'NAME': '重大隐患待验收'},
|
||||||
|
{'ID': '15', 'NAME': '重大隐患已归档'},
|
||||||
|
{'ID': '16', 'NAME': '确认打回'},
|
||||||
|
{'ID': '100','NAME': '安全检查暂存的隐患'},
|
||||||
|
{'ID': '100','NAME': '检查已归档,待指派'},
|
||||||
|
{'ID': '101','NAME': '待指派整改人'},
|
||||||
|
{'ID': '-1', 'NAME': '已过期'},
|
||||||
|
{'ID': '-2', 'NAME': '待确认'},
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> flowList = [];
|
||||||
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchData();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >=
|
||||||
|
_scrollController.position.maxScrollExtent &&
|
||||||
|
!isLoading) {
|
||||||
|
if (currentPage < totalPage) {
|
||||||
|
currentPage++;
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String? translateHidden(String id) {
|
||||||
|
final match = hiddenStatusList.firstWhere(
|
||||||
|
(item) => item['ID'] == id,
|
||||||
|
orElse: () => {},
|
||||||
|
);
|
||||||
|
return match['NAME']; // 如果 orElse 返回 {},这里会是 null(安全)
|
||||||
|
}
|
||||||
|
Future<void> _fetchData() async {
|
||||||
|
if (isLoading) return;
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final response = await ApiService.getSafeCheckDangerList(widget.INSPECTION_ID);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list = response['varList'];
|
||||||
|
} else {
|
||||||
|
list.addAll(response['varList']);
|
||||||
|
}
|
||||||
|
Map<String, dynamic> page = response['page'];
|
||||||
|
totalPage = page['totalPage'] ?? 1;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching data: $e');
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//查看
|
||||||
|
void _goToDetail(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
// await pushPage(CheckPersonDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'],isEdit: false), context);
|
||||||
|
//
|
||||||
|
// setState(() {
|
||||||
|
// _fetchData();
|
||||||
|
// });
|
||||||
|
|
||||||
|
}
|
||||||
|
/// 指派
|
||||||
|
void _goToAssign(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
// await pushPage(SafecheckSignDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'] ?? '',isEdit: false), context);
|
||||||
|
_fetchData();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// 验收
|
||||||
|
void _goAccept(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
// await pushPage(SafecheckSignDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'] ?? '',isEdit: false), context);
|
||||||
|
_fetchData();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlowStepItem({
|
||||||
|
required Map<String, dynamic> item,
|
||||||
|
required bool isFirst,
|
||||||
|
required bool isLast,
|
||||||
|
}) {
|
||||||
|
bool status = item['active'] == item['order'];
|
||||||
|
// 依据状态设色
|
||||||
|
final Color dotColor = status ? Colors.blue :Colors.grey;
|
||||||
|
final Color textColor = status ? Colors.blue :Colors.black54;
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
visualDensity: VisualDensity(vertical: -4),
|
||||||
|
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
leading: Container(
|
||||||
|
width: 24,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
// 上方线段或占位
|
||||||
|
isFirst
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
// 圆点
|
||||||
|
CircleAvatar(radius: 6, backgroundColor: dotColor),
|
||||||
|
// 下方线段或占位
|
||||||
|
isLast
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item['title'] ?? '',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 15),
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (item['desc'] != null)
|
||||||
|
Text(
|
||||||
|
item['desc'],
|
||||||
|
style: TextStyle(color: textColor, fontSize: 13),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Widget _buildListItem(Map<String, dynamic> item) {
|
||||||
|
return Card(
|
||||||
|
color: Colors.white,
|
||||||
|
margin: const EdgeInsets.all(8.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => _goToDetail(item),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item['HIDDENDESCR'] ?? '',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("隐患状态: ${translateHidden(item['HIDDEN_STATUS'] ?? '')}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("隐患描述: ${item['HIDDENDESCR'] ?? ''}", maxLines: 5, overflow: TextOverflow.ellipsis),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
SizedBox(),
|
||||||
|
Row(
|
||||||
|
spacing: 5,
|
||||||
|
children: [
|
||||||
|
CustomButton(
|
||||||
|
text: '查看',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: () => _goToDetail(item),
|
||||||
|
),
|
||||||
|
if (item['INSPECTED_SITEUSER_ID'] == SessionService.instance.loginUserId && item['HIDDEN_STATUS'] == '101')
|
||||||
|
CustomButton(
|
||||||
|
text: '指派',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () => _goToAssign(item),
|
||||||
|
),
|
||||||
|
if (item['CREATOR'] == SessionService.instance.loginUserId &&
|
||||||
|
(item['HIDDEN_STATUS'] == '4' || item['HIDDEN_STATUS'] == '8') &&
|
||||||
|
(item['HIDDEN_STATUS'] == '4' || item['HIDDEN_STATUS'] == '8') &&
|
||||||
|
FormUtils.hasValue(item, 'FINAL_CHECK'))
|
||||||
|
CustomButton(
|
||||||
|
text: '验收',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () => _goAccept(item),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListContent() {
|
||||||
|
if (isLoading && list.isEmpty) {
|
||||||
|
// 初始加载时显示居中的加载指示器
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
} else if (list.isEmpty) {
|
||||||
|
// 没有数据
|
||||||
|
return NoDataWidget.show();
|
||||||
|
} else {
|
||||||
|
// 有数据或加载更多
|
||||||
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
|
||||||
|
controller: _scrollController,
|
||||||
|
itemCount: list.length + (isLoading ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index >= list.length) {
|
||||||
|
// 加载更多时在列表底部显示加载指示器
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildListItem(list[index]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final int lastDoneIndex = flowList.lastIndexWhere((e) => e['STATUS'] == 1);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
key: _scaffoldKey,
|
||||||
|
appBar: MyAppbar(
|
||||||
|
title: '发现问题',
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
endDrawer: Drawer(
|
||||||
|
child: SafeArea(
|
||||||
|
child:
|
||||||
|
flowList.isEmpty
|
||||||
|
? Center(child: Text('暂无流程图数据'))
|
||||||
|
: ListView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
itemCount: flowList.length + 1, // +1 用来放标题
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
if (i == 0) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'查看流程图',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final idx = i - 1;
|
||||||
|
final item = flowList[idx];
|
||||||
|
final bool isFirst = idx == 0;
|
||||||
|
final bool isLast = idx == flowList.length - 1;
|
||||||
|
|
||||||
|
return _buildFlowStepItem(
|
||||||
|
item: item,
|
||||||
|
isFirst: isFirst,
|
||||||
|
isLast: isLast,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
body: SafeArea(child: Expanded(child: _buildListContent()),),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,473 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSign/safecheck_sign_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/DangeCheck/safeCheck_assignment_list.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/DangeCheck/safecheck_danger_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
|
||||||
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
|
|
||||||
|
class SafecheckDangerListPage extends StatefulWidget {
|
||||||
|
final String flow;
|
||||||
|
|
||||||
|
const SafecheckDangerListPage({Key? key, required this.flow})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SafecheckDangerListPageState createState() => _SafecheckDangerListPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckDangerListPageState extends State<SafecheckDangerListPage> {
|
||||||
|
// Data and state variables
|
||||||
|
List<dynamic> list = [];
|
||||||
|
int currentPage = 1;
|
||||||
|
int rows = 10;
|
||||||
|
int totalPage = 1;
|
||||||
|
bool isLoading = false;
|
||||||
|
List stepList = [
|
||||||
|
{'id': '3-7', 'name': '请选择'},
|
||||||
|
{'id': '3', 'name': '待指派'},
|
||||||
|
{'id': '4', 'name': '指派中'},
|
||||||
|
{'id': '5', 'name': '指派完成'},
|
||||||
|
{'id': '6', 'name': '检查待验收'},
|
||||||
|
{'id': '7', 'name': '检查已验收'},
|
||||||
|
{'id': '8', 'name': '已归档'},
|
||||||
|
|
||||||
|
];
|
||||||
|
final TextEditingController _searchController = TextEditingController();
|
||||||
|
|
||||||
|
int sindex = 0;
|
||||||
|
String searchKeywords = '';
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> flowList = [];
|
||||||
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchData();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >=
|
||||||
|
_scrollController.position.maxScrollExtent &&
|
||||||
|
!isLoading) {
|
||||||
|
if (currentPage < totalPage) {
|
||||||
|
currentPage++;
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchData() async {
|
||||||
|
if (isLoading) return;
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final data = {
|
||||||
|
'INSPECTION_STATUS': sindex > 0 ? stepList[sindex]['id'] : '',
|
||||||
|
'ARCHIVE_USER_ID': SessionService.instance.loginUserId,
|
||||||
|
'KEYWORDS': searchKeywords,
|
||||||
|
};
|
||||||
|
final url =
|
||||||
|
'/app/safetyenvironmental/checkList?showCount=-1¤tPage=$currentPage';
|
||||||
|
final response = await ApiService.getSafeCheckSearchList(data, url);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list = response['varList'];
|
||||||
|
} else {
|
||||||
|
list.addAll(response['varList']);
|
||||||
|
}
|
||||||
|
Map<String, dynamic> page = response['page'];
|
||||||
|
totalPage = page['totalPage'] ?? 1;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching data: $e');
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _search() {
|
||||||
|
searchKeywords = _searchController.text.trim();
|
||||||
|
currentPage = 1;
|
||||||
|
list.clear();
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
/// 查看
|
||||||
|
void _goToDetail(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
await pushPage(SafecheckDangerDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'] ?? '',isEdit: false), context);
|
||||||
|
_fetchData();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// 指派
|
||||||
|
void _goToAssign(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
await pushPage(SafecheckAssignmentList(INSPECTION_ID: item['INSPECTION_ID'] ?? ''), context);
|
||||||
|
_fetchData();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// 验收
|
||||||
|
void _goAccept(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
// await pushPage(SafecheckSignDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', INSPECTION_INSPECTOR_ID: item['INSPECTION_INSPECTOR_ID'] ?? '',isEdit: false), context);
|
||||||
|
_fetchData();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlowStepItem({
|
||||||
|
required Map<String, dynamic> item,
|
||||||
|
required bool isFirst,
|
||||||
|
required bool isLast,
|
||||||
|
}) {
|
||||||
|
bool status = item['active'] == item['order'];
|
||||||
|
// 依据状态设色
|
||||||
|
final Color dotColor = status ? Colors.blue :Colors.grey;
|
||||||
|
final Color textColor = status ? Colors.blue :Colors.black54;
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
visualDensity: VisualDensity(vertical: -4),
|
||||||
|
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
leading: Container(
|
||||||
|
width: 24,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
// 上方线段或占位
|
||||||
|
isFirst
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
// 圆点
|
||||||
|
CircleAvatar(radius: 6, backgroundColor: dotColor),
|
||||||
|
// 下方线段或占位
|
||||||
|
isLast
|
||||||
|
? SizedBox(height: 6 + 5)
|
||||||
|
: Expanded(child: Container(width: 1, color: Colors.grey[300])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item['title'] ?? '',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 15),
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (item['desc'] != null)
|
||||||
|
Text(
|
||||||
|
item['desc'],
|
||||||
|
style: TextStyle(color: textColor, fontSize: 13),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _checkStatusWithId(String id) {
|
||||||
|
for (Map item in stepList) {
|
||||||
|
if (item['id'] == id) {
|
||||||
|
return item['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListItem(Map<String, dynamic> item) {
|
||||||
|
return Card(
|
||||||
|
color: Colors.white,
|
||||||
|
margin: const EdgeInsets.all(8.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => _goToDetail(item),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"安全检查记录",
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查状态: ${_checkStatusWithId(item['INSPECTION_STATUS'].toString())}",
|
||||||
|
),
|
||||||
|
Text("检查类型: ${item['INSPECTION_TYPE_NAME'] ?? ''}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("检查人: ${item['INSPECTION_USER_NAME'] ?? ''}", maxLines: 5, overflow: TextOverflow.ellipsis),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查发起人: ${item['INSPECTION_ORIGINATOR_NAME'] ?? ''}",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("被检查人: ${item['INSPECTED_SITEUSER_NAME'] ?? ''}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查时间: ${item['INSPECTION_TIME_START'] ?? ''}至${item['INSPECTION_TIME_END'] ?? ''}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
SizedBox(),
|
||||||
|
Row(
|
||||||
|
spacing: 5,
|
||||||
|
children: [
|
||||||
|
CustomButton(
|
||||||
|
text: '查看',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: () => _goToDetail(item),
|
||||||
|
),
|
||||||
|
if ((item['INSPECTION_STATUS'] == '3' || item['INSPECTION_STATUS'] == '4') && item['INSPECTED_SITEUSER_ID'] == SessionService.instance.loginUserId)
|
||||||
|
CustomButton(
|
||||||
|
text: '指派',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () => _goToAssign(item),
|
||||||
|
),
|
||||||
|
if ((item['INSPECTION_STATUS'] == '5' || item['INSPECTION_STATUS'] == '6' || item['INSPECTION_STATUS'] == '7') && item['checkout'].toString() == '1')
|
||||||
|
CustomButton(
|
||||||
|
text: '验收',
|
||||||
|
height: 32,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () => _goAccept(item),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示底部选择器
|
||||||
|
Future<void> _showStepPicker() async {
|
||||||
|
if (stepList.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('正在加载步骤数据,请稍后...')));
|
||||||
|
if (stepList.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('无法加载步骤数据')));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建选项列表
|
||||||
|
final options = stepList.map((e) => e['name'] as String).toList();
|
||||||
|
|
||||||
|
// 显示底部选择器
|
||||||
|
final choice = await BottomPicker.show<String>(
|
||||||
|
context,
|
||||||
|
items: options,
|
||||||
|
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||||
|
initialIndex: sindex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (choice != null) {
|
||||||
|
// 找到选择的索引
|
||||||
|
final newIndex = options.indexOf(choice);
|
||||||
|
if (newIndex != -1) {
|
||||||
|
setState(() {
|
||||||
|
sindex = newIndex;
|
||||||
|
});
|
||||||
|
_search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListContent() {
|
||||||
|
if (isLoading && list.isEmpty) {
|
||||||
|
// 初始加载时显示居中的加载指示器
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
} else if (list.isEmpty) {
|
||||||
|
// 没有数据
|
||||||
|
return NoDataWidget.show();
|
||||||
|
} else {
|
||||||
|
// 有数据或加载更多
|
||||||
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
|
||||||
|
controller: _scrollController,
|
||||||
|
itemCount: list.length + (isLoading ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index >= list.length) {
|
||||||
|
// 加载更多时在列表底部显示加载指示器
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildListItem(list[index]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final int lastDoneIndex = flowList.lastIndexWhere((e) => e['STATUS'] == 1);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
key: _scaffoldKey,
|
||||||
|
appBar: MyAppbar(
|
||||||
|
title: '${widget.flow}',
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
endDrawer: Drawer(
|
||||||
|
child: SafeArea(
|
||||||
|
child:
|
||||||
|
flowList.isEmpty
|
||||||
|
? Center(child: Text('暂无流程图数据'))
|
||||||
|
: ListView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
itemCount: flowList.length + 1, // +1 用来放标题
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
if (i == 0) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'查看流程图',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final idx = i - 1;
|
||||||
|
final item = flowList[idx];
|
||||||
|
final bool isFirst = idx == 0;
|
||||||
|
final bool isLast = idx == flowList.length - 1;
|
||||||
|
|
||||||
|
return _buildFlowStepItem(
|
||||||
|
item: item,
|
||||||
|
isFirst: isFirst,
|
||||||
|
isLast: isLast,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
body: SafeArea(child: Column(
|
||||||
|
children: [
|
||||||
|
// Filter bar
|
||||||
|
Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// 底部弹窗选择器
|
||||||
|
SizedBox(
|
||||||
|
width: 65,
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: _showStepPicker,
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 12,
|
||||||
|
horizontal: 5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'筛选',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 16),
|
||||||
|
),
|
||||||
|
Icon(Icons.arrow_drop_down, color: Colors.grey),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: SearchBarWidget(
|
||||||
|
showResetButton: false,
|
||||||
|
hintText: "请输入关键字",
|
||||||
|
// isClickableOnly: true,
|
||||||
|
onSearch: (text) {
|
||||||
|
_search();
|
||||||
|
},
|
||||||
|
controller: _searchController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
// List
|
||||||
|
Expanded(child: _buildListContent()),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,311 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
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/department_person_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/dotted_border_box.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.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/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart';
|
||||||
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safeCheck_table.dart';
|
||||||
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safe_drawer_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/app/Danger_paicha/quick_report_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/SafeCheckFormView.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_drawer_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
|
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
|
||||||
|
class SafecheckDangerDetail extends StatefulWidget {
|
||||||
|
const SafecheckDangerDetail({
|
||||||
|
super.key,
|
||||||
|
required this.INSPECTION_ID,
|
||||||
|
required this.INSPECTION_INSPECTOR_ID,
|
||||||
|
required this.isEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String INSPECTION_ID;
|
||||||
|
final String INSPECTION_INSPECTOR_ID;
|
||||||
|
final bool isEdit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SafecheckDangerDetail> createState() => _SafecheckDangerDetailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckDangerDetailState extends State<SafecheckDangerDetail> {
|
||||||
|
String msg = 'add';
|
||||||
|
bool _isEdit = false;
|
||||||
|
List<String> signImages = [];
|
||||||
|
List<String> signTimes = []; // 签字时间列表
|
||||||
|
Map<String, dynamic> inspectedForm = {};
|
||||||
|
List<Map<String, dynamic>> inspectorRules = [
|
||||||
|
{'name': 'INSPECTION_STATUS', 'message': '核实结果不能为空'},
|
||||||
|
{'name': 'INSPECTION_ID', 'message': '安全检查ID不能为空'},
|
||||||
|
{'name': 'INSPECTION_USER_ID', 'message': '检查人不能为空'},
|
||||||
|
];
|
||||||
|
Map<String, dynamic> form = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_isEdit = widget.isEdit;
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_getData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getData() async {
|
||||||
|
try {
|
||||||
|
final result = await ApiService.getSafeCheckSureList(
|
||||||
|
widget.INSPECTION_ID,
|
||||||
|
);
|
||||||
|
// 在 await 之后检查 mounted,避免页面已经被 pop 导致 setState 报错
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
form = result['pd'] ?? {};
|
||||||
|
inspectedForm = {
|
||||||
|
'INSPECTION_EXPLAIN_ID': '', // 安全检查人员主键ID
|
||||||
|
'INSPECTION_STATUS': '3',
|
||||||
|
'INSPECTION_ID': widget.INSPECTION_ID, // 安全检查ID
|
||||||
|
'INSPECTED_EXPLAIN': '', // 申辩内容
|
||||||
|
'INSPECTED_EXPLAIN_FILENAME': '', // 申辩附件名称
|
||||||
|
'INSPECTED_SITEUSER_SIGN_IMG': '', // 被检查单位现场负责人签字
|
||||||
|
'INSPECTED_SITEUSER_SIGN_TIME': '',
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (e, st) {
|
||||||
|
print('加载单条数据失败: $e\n$st');
|
||||||
|
if (mounted) {
|
||||||
|
ToastUtil.showNormal(context, '加载数据失败:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openDrawer(Map<String, dynamic> hiddenForm, int index) async {
|
||||||
|
try {
|
||||||
|
final result = await openCustomDrawer<Map>(
|
||||||
|
context,
|
||||||
|
SafeCheckDrawerPage(
|
||||||
|
initialHidden: hiddenForm,
|
||||||
|
editType:
|
||||||
|
_isEdit
|
||||||
|
? (index < 0 ? SafeCheckEditType.add : SafeCheckEditType.edit)
|
||||||
|
: SafeCheckEditType.see,
|
||||||
|
toCheckUnitList: [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && mounted) {
|
||||||
|
setState(() {
|
||||||
|
if (index < 0) {
|
||||||
|
// 新增
|
||||||
|
form['hiddenList'].add(result);
|
||||||
|
print(form);
|
||||||
|
} else {
|
||||||
|
// 修改
|
||||||
|
form['hiddenList'][index] = result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("打开抽屉失败: $e");
|
||||||
|
ToastUtil.showNormal(context, "打开抽屉失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T?> openCustomDrawer<T>(BuildContext context, Widget child) {
|
||||||
|
return Navigator.of(context).push<T>(
|
||||||
|
PageRouteBuilder(
|
||||||
|
opaque: false,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: Colors.black54,
|
||||||
|
pageBuilder: (_, __, ___) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FractionallySizedBox(
|
||||||
|
widthFactor: 4 / 5,
|
||||||
|
child: Material(color: Colors.white, child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionsBuilder: (_, anim, __, child) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween(
|
||||||
|
begin: const Offset(1, 0),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将 form['situationList'](若存在)转换为 List<String>
|
||||||
|
List<String> _situationListToStrings() {
|
||||||
|
final List<dynamic> cur = List<dynamic>.from(form['situationList'] ?? []);
|
||||||
|
if (cur.isEmpty) return ['']; // 保持至少一行,和 uni-app 行为一致
|
||||||
|
return cur.map((e) {
|
||||||
|
if (e is Map && e['SITUATION'] != null) return e['SITUATION'].toString();
|
||||||
|
return '';
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _signListWidget() {
|
||||||
|
return Column(
|
||||||
|
children:
|
||||||
|
signImages.map((path) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _mainWidget() {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SafeCheckFormView(
|
||||||
|
form: form,
|
||||||
|
isEdit: false,
|
||||||
|
onHiddenShow: (item, index) {
|
||||||
|
_openDrawer(item, index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 10),
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '被检查单位现场负责人意见:',
|
||||||
|
isEditable: false,
|
||||||
|
text: FormUtils.hasValue(form, 'INSPECTED_EXPLAIN') ? '申辩' : '同意',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
if (FormUtils.hasValue(form, 'INSPECTED_EXPLAIN'))
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.multiLineTitleTextField(
|
||||||
|
label: '申辩说明',
|
||||||
|
isEditable: false,
|
||||||
|
text: form['INSPECTED_EXPLAIN'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ItemListWidget.OneRowImageTitle(
|
||||||
|
label: '被检查单位现场负责人签字',
|
||||||
|
text: form['INSPECTED_SITEUSER_SIGN_TIME'],
|
||||||
|
onTapCallBack: (path) {
|
||||||
|
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||||
|
},
|
||||||
|
imgPath: form['INSPECTED_SITEUSER_SIGN_IMG'],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 150,
|
||||||
|
child: CustomButton(
|
||||||
|
text: '返回',
|
||||||
|
textStyle: TextStyle(color: Colors.white, fontSize: 17),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: MyAppbar(title: "安全检查详情", actions: []),
|
||||||
|
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
|
child: form.isNotEmpty ? _mainWidget() : SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
// safe_check_form_view.dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/dotted_border_box.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||||
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart';
|
||||||
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safeCheck_table.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
|
||||||
|
class SafeCheckFormView extends StatefulWidget {
|
||||||
|
const SafeCheckFormView({
|
||||||
|
Key? key,
|
||||||
|
required this.form,
|
||||||
|
required this.isEdit,
|
||||||
|
required this.onHiddenShow,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
// === 数据 ===
|
||||||
|
final Map<String, dynamic> form;
|
||||||
|
final bool isEdit;
|
||||||
|
|
||||||
|
/// 显示隐患详情(点击)
|
||||||
|
final void Function(Map<String, dynamic> item, int idx) onHiddenShow;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SafeCheckFormViewState createState() => _SafeCheckFormViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafeCheckFormViewState extends State<SafeCheckFormView> {
|
||||||
|
/// 将 widget.form['situationList'] 转为 MultiTextFieldWithTitle 需要的 List<String>
|
||||||
|
List<String> _situationListToStrings() {
|
||||||
|
final List<dynamic> cur = List<dynamic>.from(
|
||||||
|
widget.form['situationList'] ?? [],
|
||||||
|
);
|
||||||
|
if (cur.isEmpty) return ['']; // 保持至少一行,和 uni-app 行为一致
|
||||||
|
return cur.map((e) {
|
||||||
|
if (e is Map && e['SITUATION'] != null) return e['SITUATION'].toString();
|
||||||
|
if (e is String) return e;
|
||||||
|
return '';
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _personUnitItem(Map item, int index) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 5),
|
||||||
|
child: DottedBorderBox(
|
||||||
|
child: SizedBox(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
isRequired: false,
|
||||||
|
label: '${index + 1}.检查人员单位:',
|
||||||
|
isEditable: false,
|
||||||
|
onTapClean: () {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
text: item['INSPECTION_DEPARTMENT_NAME'] ?? '请选择',
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
isRequired: false,
|
||||||
|
label: '${index + 1}.检查人员:',
|
||||||
|
isEditable: false,
|
||||||
|
text: item['INSPECTION_USER_NAME'] ?? '请选择',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
// 删除按钮(叠加在左上角)
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _checkItem(Map item, int index) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '检查人员核查结果:',
|
||||||
|
isEditable: false,
|
||||||
|
text: '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.multiLineTitleTextField(
|
||||||
|
label: '检查人意见',
|
||||||
|
isEditable: false,
|
||||||
|
text: item['INSPECTION_USER_OPINION'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.OneRowImageTitle(
|
||||||
|
label: '检查人签字',
|
||||||
|
text: item['INSPECTION_USER_SIGN_TIME'],
|
||||||
|
onTapCallBack: (path) {
|
||||||
|
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||||
|
},
|
||||||
|
imgPath: item['INSPECTION_USER_SIGN_IMG'],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final form = widget.form;
|
||||||
|
final isEdit = widget.isEdit;
|
||||||
|
List<dynamic> inspectorList = widget.form['inspectorList'] ?? [];
|
||||||
|
List<dynamic> inspectorVerifyList =
|
||||||
|
widget.form['inspectorVerifyList'] ?? widget.form['inspectorList'];
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// 顶部表单区容器
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
// 检查题目选择(示例复用你项目的 ListItemFactory)
|
||||||
|
ListItemFactory.createYesNoSection(
|
||||||
|
title: '检查题目:',
|
||||||
|
groupValue:
|
||||||
|
(form['INSPECTION_CATEGORY']?.toString() ?? '')
|
||||||
|
.toLowerCase() ==
|
||||||
|
'安全'
|
||||||
|
? true
|
||||||
|
: (form['INSPECTION_CATEGORY']?.toString() ?? '')
|
||||||
|
.toLowerCase() ==
|
||||||
|
'综合'
|
||||||
|
? false
|
||||||
|
: null,
|
||||||
|
yesLabel: '安全',
|
||||||
|
noLabel: '综合',
|
||||||
|
isEdit: isEdit,
|
||||||
|
isRequired: true,
|
||||||
|
horizontalPadding: 5,
|
||||||
|
verticalPadding: 0,
|
||||||
|
text: '${form['INSPECTION_SUBJECT'] ?? ''}现场检查记录',
|
||||||
|
|
||||||
|
onChanged: (val) {
|
||||||
|
// 如果需要把选择写回 form,可以在这里 setState 更新 widget.form
|
||||||
|
// setState(() { widget.form['INSPECTION_CATEGORY'] = val ? '安全' : '综合'; });
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '被检查单位:',
|
||||||
|
isEditable: false,
|
||||||
|
text: form['INSPECTED_DEPARTMENT_NAMES'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '被检查单位现场负责人:',
|
||||||
|
isEditable: isEdit,
|
||||||
|
text: form['INSPECTED_SITEUSER_NAME'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '检查场所:',
|
||||||
|
isEditable: isEdit,
|
||||||
|
text: form['INSPECTION_PLACE'],
|
||||||
|
hintText: '请输入检查场所',
|
||||||
|
onChanged: (val) {
|
||||||
|
// 可选择写回 form
|
||||||
|
// setState(() { widget.form['INSPECTION_PLACE'] = val; });
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '检查类型:',
|
||||||
|
isEditable: isEdit,
|
||||||
|
text: form['INSPECTION_TYPE_NAME'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '检查开始时间:',
|
||||||
|
isEditable: isEdit,
|
||||||
|
text: form['INSPECTION_TIME_START'] ?? '',
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '检查结束时间:',
|
||||||
|
isEditable: isEdit,
|
||||||
|
text: form['INSPECTION_TIME_END'] ?? '',
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
// MultiTextFieldWithTitle(检查情况),外部通过 onMultiTextsChanged 更新 form['situationList']
|
||||||
|
MultiTextFieldWithTitle(
|
||||||
|
label: "检查情况",
|
||||||
|
isEditable: isEdit,
|
||||||
|
hintText: "请输入检查情况...",
|
||||||
|
texts: _situationListToStrings(),
|
||||||
|
onTextsChanged: (val) {},
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ListItemFactory.headerTitle('检查人员'),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: inspectorList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _personUnitItem(inspectorList[index], index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 12,
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [ListItemFactory.headerTitle('发现问题')],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// HiddenListTable:隐患列表展示(外部通过 onHiddenShow/onHiddenRemove 处理具体交互)
|
||||||
|
HiddenListTable(
|
||||||
|
hiddenList: (form['hiddenList'] as List<dynamic>?) ?? [],
|
||||||
|
forbidEdit: false,
|
||||||
|
baseImgPath: ApiService.baseImgPath,
|
||||||
|
personSignImg: '',
|
||||||
|
personSignTime: '',
|
||||||
|
showHidden: widget.onHiddenShow,
|
||||||
|
removeHidden: (item, index) {},
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10,),
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
inspectorVerifyList.isNotEmpty
|
||||||
|
? ListView.builder(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 0),
|
||||||
|
itemCount: inspectorVerifyList.length,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _checkItem(inspectorVerifyList[index], index);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: SizedBox(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
|
||||||
|
class SafecheckDefendSetPage extends StatefulWidget {
|
||||||
|
const SafecheckDefendSetPage({super.key, required this.INSPECTION_ID});
|
||||||
|
final String INSPECTION_ID;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SafecheckDefendSetPage> createState() => _SafecheckDefendSetPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckDefendSetPageState extends State<SafecheckDefendSetPage> {
|
||||||
|
|
||||||
|
late Map<String, dynamic> form = {};
|
||||||
|
|
||||||
|
Future<void> _getData() async {
|
||||||
|
try {
|
||||||
|
final result = await ApiService.getSafeCheckStartGoEdit(
|
||||||
|
widget.INSPECTION_ID,
|
||||||
|
);
|
||||||
|
// 在 await 之后检查 mounted,避免页面已经被 pop 导致 setState 报错
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
form = result['pd'] ?? {};
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (e, st) {
|
||||||
|
print('加载单条数据失败: $e\n$st');
|
||||||
|
if (mounted) {
|
||||||
|
ToastUtil.showNormal(context, '加载数据失败:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: MyAppbar(title: '申辩处理'),
|
||||||
|
body: SafeArea(child: Padding(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
child: ItemListWidget.itemContainer(Column(children: [
|
||||||
|
|
||||||
|
],)))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,565 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/bottom_picker_two.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/department_picker_hidden_type.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/full_screen_video_page.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
import '../../../../customWidget/photo_picker_row.dart';
|
||||||
|
import '../../../../http/ApiService.dart';
|
||||||
|
|
||||||
|
/// 添加 修改 查看
|
||||||
|
enum SafeCheckEditType { add, edit, see }
|
||||||
|
|
||||||
|
class SafeCheckDrawerPage extends StatefulWidget {
|
||||||
|
const SafeCheckDrawerPage({
|
||||||
|
super.key,
|
||||||
|
required this.editType,
|
||||||
|
this.initialHidden,
|
||||||
|
required this.toCheckUnitList,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SafeCheckEditType editType;
|
||||||
|
final List<dynamic> toCheckUnitList;
|
||||||
|
|
||||||
|
/// 可选:传入已有的 hiddenForm(用于 edit / see)
|
||||||
|
final Map<String, dynamic>? initialHidden;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SafeCheckDrawerPage> createState() => _SafeCheckDrawerPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafeCheckDrawerPageState extends State<SafeCheckDrawerPage> {
|
||||||
|
// Controllers
|
||||||
|
final TextEditingController _descCtl = TextEditingController();
|
||||||
|
final TextEditingController _partCtl = TextEditingController();
|
||||||
|
final TextEditingController _rectifyCtl = TextEditingController();
|
||||||
|
|
||||||
|
bool _isEdit = false;
|
||||||
|
|
||||||
|
// Data lists
|
||||||
|
List<dynamic> _hazardLevels = [];
|
||||||
|
|
||||||
|
// Selected / transient state
|
||||||
|
Map<String, dynamic> hiddenForm = {};
|
||||||
|
|
||||||
|
bool _rectifyClickable = true; // 是否可切换整改方式
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_isEdit = widget.editType != SafeCheckEditType.see;
|
||||||
|
_initHiddenForm();
|
||||||
|
_loadHazardLevels();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initHiddenForm() {
|
||||||
|
hiddenForm = {
|
||||||
|
'ISRELEVANT': '2',
|
||||||
|
'SOURCE': '5',
|
||||||
|
'hiddenImgs': <Map<String, dynamic>>[],
|
||||||
|
'zgImgs': <Map<String, dynamic>>[],
|
||||||
|
'hiddenVideos': <Map<String, dynamic>>[],
|
||||||
|
'RECTIFICATIONTYPE': '2',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果传入了初始数据(编辑/查看),覆盖默认
|
||||||
|
if (widget.initialHidden != null) {
|
||||||
|
final Map<String, dynamic> m = Map<String, dynamic>.from(
|
||||||
|
widget.initialHidden!,
|
||||||
|
);
|
||||||
|
// normalize lists to List<Map<String,dynamic>>
|
||||||
|
m['hiddenImgs'] =
|
||||||
|
(m['hiddenImgs'] as List<dynamic>?)
|
||||||
|
?.map(
|
||||||
|
(e) =>
|
||||||
|
e is Map
|
||||||
|
? Map<String, dynamic>.from(e)
|
||||||
|
: {'FILEPATH': e.toString()},
|
||||||
|
)
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
m['zgImgs'] =
|
||||||
|
(m['zgImgs'] as List<dynamic>?)
|
||||||
|
?.map(
|
||||||
|
(e) =>
|
||||||
|
e is Map
|
||||||
|
? Map<String, dynamic>.from(e)
|
||||||
|
: {'FILEPATH': e.toString()},
|
||||||
|
)
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
m['hiddenVideos'] =
|
||||||
|
(m['hiddenVideos'] as List<dynamic>?)
|
||||||
|
?.map(
|
||||||
|
(e) =>
|
||||||
|
e is Map
|
||||||
|
? Map<String, dynamic>.from(e)
|
||||||
|
: {'FILEPATH': e.toString()},
|
||||||
|
)
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
hiddenForm.addAll(m);
|
||||||
|
}
|
||||||
|
// init controllers and flags
|
||||||
|
_descCtl.text = hiddenForm['HIDDENDESCR'] ?? '';
|
||||||
|
_partCtl.text = hiddenForm['HIDDENPART'] ?? '';
|
||||||
|
_rectifyCtl.text = hiddenForm['RECTIFYDESCR'] ?? '';
|
||||||
|
|
||||||
|
// hidden level selected map is not stored directly; we can set HIDDENLEVEL_NAME/HIDDENLEVEL
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_descCtl.dispose();
|
||||||
|
_partCtl.dispose();
|
||||||
|
_rectifyCtl.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadHazardLevels() async {
|
||||||
|
try {
|
||||||
|
final res = await ApiService.getHazardLevel();
|
||||||
|
if (res['result'] == 'success') {
|
||||||
|
setState(() {
|
||||||
|
final list = List<dynamic>.from(res['list'] ?? []);
|
||||||
|
List rList = [];
|
||||||
|
for (Map item in list) {
|
||||||
|
if (item['NAME'] != '重大隐患' && item['NAME'] != '轻微隐患') {
|
||||||
|
rList.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_hazardLevels = rList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _section(Widget child) => Container(
|
||||||
|
margin: const EdgeInsets.only(top: 10),
|
||||||
|
color: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
|
||||||
|
List<String> _getSelectedImages() {
|
||||||
|
return (hiddenForm['hiddenImgs'] as List<Map<String, dynamic>>).map((e) {
|
||||||
|
final p = '${e['FILEPATH']}';
|
||||||
|
return p.contains('uploadFiles') ? '${ApiService.baseImgPath}$p' : p;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _getSelectedVideos() {
|
||||||
|
return (hiddenForm['hiddenVideos'] as List<Map<String, dynamic>>)
|
||||||
|
.map((e) => '${e['FILEPATH']}')
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
///隐患发现时间
|
||||||
|
Future<void> _chooseDatePicker() async {
|
||||||
|
DateTime? picked = await BottomDateTimePicker.showDate(context);
|
||||||
|
if (picked != null) {
|
||||||
|
setState(() {
|
||||||
|
hiddenForm['CREATTIME'] = DateFormat('yyyy-MM-dd HH:mm').format(picked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 隐患发现人
|
||||||
|
Future<void> _pickDangerPerson() async {
|
||||||
|
final choice = await BottomPicker.show<String>(
|
||||||
|
context,
|
||||||
|
items: [SessionService.instance.username as String],
|
||||||
|
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||||
|
initialIndex: 0,
|
||||||
|
);
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
|
||||||
|
if (choice != null) {
|
||||||
|
setState(() {
|
||||||
|
hiddenForm['CREATOR_INDEX'] = 0;
|
||||||
|
hiddenForm['CREATOR'] = SessionService.instance.loginUserId;
|
||||||
|
hiddenForm['CREATOR_NAME'] = SessionService.instance.username;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey[100],
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// scroll content
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.only(bottom: 20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
// photos
|
||||||
|
_section(
|
||||||
|
RepairedPhotoSection(
|
||||||
|
title: '隐患照片',
|
||||||
|
maxCount: 4,
|
||||||
|
mediaType: MediaType.image,
|
||||||
|
isShowAI: true,
|
||||||
|
isEdit: _isEdit,
|
||||||
|
isRequired: _isEdit,
|
||||||
|
initialMediaPaths: _getSelectedImages(),
|
||||||
|
onMediaTapped: (p) {
|
||||||
|
presentOpaque(
|
||||||
|
SingleImageViewer(imageUrl: p),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onChanged:
|
||||||
|
(files) => setState(() {
|
||||||
|
hiddenForm['hiddenImgs'] =
|
||||||
|
files
|
||||||
|
.map((f) => {'FILEPATH': f.path})
|
||||||
|
.toList();
|
||||||
|
}),
|
||||||
|
onAiIdentify: () {
|
||||||
|
final imgs = List<Map<String, dynamic>>.from(
|
||||||
|
hiddenForm['hiddenImgs'] ?? [],
|
||||||
|
);
|
||||||
|
if (imgs.isEmpty)
|
||||||
|
return ToastUtil.showNormal(context, '请先上传一张图片');
|
||||||
|
if (imgs.length > 1)
|
||||||
|
return ToastUtil.showNormal(
|
||||||
|
context,
|
||||||
|
'识别暂时只能上传一张图片',
|
||||||
|
);
|
||||||
|
final path = imgs.first['FILEPATH']?.toString() ?? '';
|
||||||
|
if (path.isNotEmpty) _identifyImage(path);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// videos
|
||||||
|
_section(
|
||||||
|
RepairedPhotoSection(
|
||||||
|
title: '隐患视频',
|
||||||
|
maxCount: 1,
|
||||||
|
onMediaTapped: (p) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierColor: Colors.black54,
|
||||||
|
builder: (_) => VideoPlayerPopup(videoUrl: p),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isEdit: _isEdit,
|
||||||
|
mediaType: MediaType.video,
|
||||||
|
initialMediaPaths: _getSelectedVideos(),
|
||||||
|
onChanged:
|
||||||
|
(files) => setState(() {
|
||||||
|
hiddenForm['hiddenVideos'] =
|
||||||
|
files
|
||||||
|
.map((f) => {'FILEPATH': f.path})
|
||||||
|
.toList();
|
||||||
|
}),
|
||||||
|
onAiIdentify: () {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// description
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
ItemListWidget.multiLineTitleTextField(
|
||||||
|
label: '隐患描述',
|
||||||
|
isEditable: _isEdit,
|
||||||
|
text: hiddenForm['HIDDENDESCR'] ?? '',
|
||||||
|
controller: _descCtl,
|
||||||
|
),
|
||||||
|
horizontal: 5,
|
||||||
|
),
|
||||||
|
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
ItemListWidget.multiLineTitleTextField(
|
||||||
|
label: '隐患部位',
|
||||||
|
isEditable: _isEdit,
|
||||||
|
text: hiddenForm['HIDDENPART'] ?? '',
|
||||||
|
controller: _partCtl,
|
||||||
|
),
|
||||||
|
horizontal: 5,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 3),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '隐患级别:',
|
||||||
|
isEditable: _isEdit,
|
||||||
|
text: hiddenForm['HIDDENLEVEL_NAME'] ?? '',
|
||||||
|
onTap: () {
|
||||||
|
_pickHazardLevel();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '隐患类型:',
|
||||||
|
isEditable: _isEdit,
|
||||||
|
text: hiddenForm['HIDDENTYPE_NAME'] ?? '',
|
||||||
|
onTap: () {
|
||||||
|
_pickHazardType();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '隐患发现时间:',
|
||||||
|
isEditable: _isEdit,
|
||||||
|
text: hiddenForm['CREATTIME'] ?? '',
|
||||||
|
onTap: () {
|
||||||
|
_chooseDatePicker();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '隐患发现人:',
|
||||||
|
isEditable: _isEdit,
|
||||||
|
text: hiddenForm['CREATOR_NAME'] ?? '',
|
||||||
|
onTap: () {
|
||||||
|
_pickDangerPerson();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
// buttons
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: CustomButton(
|
||||||
|
onPressed: cancelHidden,
|
||||||
|
text: '返回',
|
||||||
|
textStyle: TextStyle(color: Colors.black87),
|
||||||
|
backgroundColor: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
if (_isEdit)
|
||||||
|
Expanded(
|
||||||
|
child: CustomButton(
|
||||||
|
onPressed: saveHidden,
|
||||||
|
text: '保存',
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------- Helpers -----------------
|
||||||
|
Future<void> _pickHazardLevel() async {
|
||||||
|
if (_hazardLevels.isEmpty) return ToastUtil.showNormal(context, '隐患级别数据为空');
|
||||||
|
final choice = await BottomPickerTwo.show<String>(
|
||||||
|
context,
|
||||||
|
items: _hazardLevels,
|
||||||
|
itemBuilder: (i) => Text(i['NAME'], textAlign: TextAlign.center),
|
||||||
|
initialIndex: 0,
|
||||||
|
);
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
if (choice == null) return;
|
||||||
|
final found = _hazardLevels.firstWhere(
|
||||||
|
(e) => e['NAME'] == choice,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
if (found != null) {
|
||||||
|
setState(() {
|
||||||
|
hiddenForm['HIDDENLEVEL'] = found['BIANMA'] ?? found['id'] ?? '';
|
||||||
|
hiddenForm['HIDDENLEVEL_NAME'] = found['NAME'] ?? '';
|
||||||
|
hiddenForm['HIDDENLEVEL_INDEX'] = found['id'] ?? '0';
|
||||||
|
_rectifyClickable =
|
||||||
|
(found['DICTIONARIES_ID']?.toString() ?? '') !=
|
||||||
|
'5ff9daf78e9a4fb1b40d77980656799d';
|
||||||
|
if (!_rectifyClickable) {
|
||||||
|
hiddenForm['RECTIFICATIONTYPE'] = '2';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _pickHazardType() async {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
barrierColor: Colors.black54,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder:
|
||||||
|
(_) => DepartmentPickerHiddenType(
|
||||||
|
onSelected: (result) {
|
||||||
|
try {
|
||||||
|
final Map m = Map.from(result);
|
||||||
|
final ids = List<String>.from(m['id'] ?? []);
|
||||||
|
final names = List<String>.from(m['name'] ?? []);
|
||||||
|
setState(() {
|
||||||
|
hiddenForm['HIDDENTYPE'] = ids;
|
||||||
|
hiddenForm['HIDDENTYPE_NAME'] = names.join('/');
|
||||||
|
|
||||||
|
});
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 保存隐患(这里只返回 hiddenForm 给上层调用者)
|
||||||
|
void saveHidden() {
|
||||||
|
// update hiddenForm from controllers before returning
|
||||||
|
hiddenForm['HIDDENDESCR'] = _descCtl.text.trim();
|
||||||
|
hiddenForm['HIDDENPART'] = _partCtl.text.trim();
|
||||||
|
hiddenForm['RECTIFYDESCR'] = _rectifyCtl.text.trim();
|
||||||
|
if ((hiddenForm['hiddenImgs'] as List).isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '请上传隐患图片');
|
||||||
|
}
|
||||||
|
// basic validation example
|
||||||
|
if ((hiddenForm['HIDDENDESCR'] ?? '').toString().trim().isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '输入隐患描述');
|
||||||
|
}
|
||||||
|
if ((hiddenForm['HIDDENPART']?? '').toString().trim().isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '输入隐患部位');
|
||||||
|
}
|
||||||
|
if ((hiddenForm['HIDDENLEVEL'] ?? '').toString().trim().isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '请选择隐患级别');
|
||||||
|
}
|
||||||
|
if ((hiddenForm['HIDDENTYPE'] ?? '').toString().trim().isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '请选择隐患类型');
|
||||||
|
}
|
||||||
|
if ((hiddenForm['CREATTIME'] ?? '').toString().trim().isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '请选择隐患发现时间');
|
||||||
|
}
|
||||||
|
if ((hiddenForm['CREATOR'] ?? '').toString().trim().isEmpty) {
|
||||||
|
return ToastUtil.showNormal(context, '请选择隐患发现人');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 返回给调用方(抽屉关闭并返回数据)
|
||||||
|
Navigator.of(context).pop(hiddenForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancelHidden() {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片/视频查看与删除(简单实现)
|
||||||
|
void viewImage(int index) {
|
||||||
|
final imgs = List<Map<String, dynamic>>.from(
|
||||||
|
hiddenForm['hiddenImgs'] ?? [],
|
||||||
|
);
|
||||||
|
if (index < 0 || index >= imgs.length) return;
|
||||||
|
final path = imgs[index]['FILEPATH']?.toString() ?? '';
|
||||||
|
|
||||||
|
presentOpaque(
|
||||||
|
SingleImageViewer(imageUrl: ApiService.baseImgPath + path),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delImage(int index) {
|
||||||
|
setState(() {
|
||||||
|
final imgs = List<Map<String, dynamic>>.from(
|
||||||
|
hiddenForm['hiddenImgs'] ?? [],
|
||||||
|
);
|
||||||
|
if (index >= 0 && index < imgs.length) {
|
||||||
|
imgs.removeAt(index);
|
||||||
|
hiddenForm['hiddenImgs'] = imgs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void viewVideo(int index) {
|
||||||
|
final vids = List<Map<String, dynamic>>.from(
|
||||||
|
hiddenForm['hiddenVideos'] ?? [],
|
||||||
|
);
|
||||||
|
if (index < 0 || index >= vids.length) return;
|
||||||
|
final path = vids[index]['FILEPATH']?.toString() ?? '';
|
||||||
|
ToastUtil.showNormal(context, '查看视频:$path');
|
||||||
|
}
|
||||||
|
|
||||||
|
void delVideo(int index) {
|
||||||
|
setState(() {
|
||||||
|
final vids = List<Map<String, dynamic>>.from(
|
||||||
|
hiddenForm['hiddenVideos'] ?? [],
|
||||||
|
);
|
||||||
|
if (index >= 0 && index < vids.length) {
|
||||||
|
vids.removeAt(index);
|
||||||
|
hiddenForm['hiddenVideos'] = vids;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _identifyImage(String path) async {
|
||||||
|
try {
|
||||||
|
LoadingDialogHelper.show();
|
||||||
|
final raw = await ApiService.identifyImg(path);
|
||||||
|
if (raw['result'] == 'success') {
|
||||||
|
final aiList = raw['aiHiddens'] ?? [];
|
||||||
|
String desc = '';
|
||||||
|
String rectify = '';
|
||||||
|
for (final item in aiList) {
|
||||||
|
try {
|
||||||
|
final m = jsonDecode(item);
|
||||||
|
desc =
|
||||||
|
desc.isEmpty
|
||||||
|
? (m['hiddenDescr'] ?? '')
|
||||||
|
: '$desc;${m['hiddenDescr'] ?? ''}';
|
||||||
|
rectify =
|
||||||
|
rectify.isEmpty
|
||||||
|
? (m['rectificationSuggestions'] ?? '')
|
||||||
|
: '$rectify;${m['rectificationSuggestions'] ?? ''}';
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_descCtl.text = desc;
|
||||||
|
_rectifyCtl.text = rectify;
|
||||||
|
hiddenForm['HIDDENDESCR'] = desc;
|
||||||
|
hiddenForm['RECTIFYDESCR'] = rectify;
|
||||||
|
hiddenForm['RECTIFICATIONTYPE'] = '1';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ToastUtil.showNormal(context, '识别失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ToastUtil.showNormal(context, '识别异常:$e');
|
||||||
|
} finally {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_defend_set_page.dart';
|
||||||
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
||||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
|
@ -113,7 +114,7 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
/// 申请
|
/// 申请
|
||||||
void _handleApply() {
|
void _handleApply() {
|
||||||
// 处理申请按钮点击逻辑
|
// 处理申请按钮点击逻辑
|
||||||
pushPage(SafecheckStartDetail(INSPECTION_ID: '', isEdit: true), context);
|
pushPage(SafecheckStartDetail(INSPECTION_ID: '', type: 'add',), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 打开流程图
|
/// 打开流程图
|
||||||
|
|
@ -140,9 +141,9 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToDetail(Map<String, dynamic> item) async {
|
void _goToDetail(Map<String, dynamic> item, String type) async {
|
||||||
|
|
||||||
pushPage(SafecheckStartDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', isEdit: false), context);
|
pushPage(SafecheckStartDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', type: type), context);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_fetchData();
|
_fetchData();
|
||||||
|
|
@ -212,13 +213,19 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
void _goToDefend(Map<String, dynamic> item) async {
|
||||||
|
|
||||||
|
await pushPage(SafecheckDefendSetPage(INSPECTION_ID: item['INSPECTION_ID'] ?? ''), context);
|
||||||
|
_fetchData();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Widget _buildListItem(Map<String, dynamic> item) {
|
Widget _buildListItem(Map<String, dynamic> item) {
|
||||||
return Card(
|
return Card(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
margin: const EdgeInsets.all(8.0),
|
margin: const EdgeInsets.all(8.0),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => _goToDetail(item),
|
onTap: () => _goToDetail(item, 'detail'),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -279,6 +286,8 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
|
spacing: 5,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
CustomButton(
|
CustomButton(
|
||||||
text: '流程图',
|
text: '流程图',
|
||||||
|
|
@ -288,7 +297,41 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
onPressed: () => _openFlowDrawer(item['INSPECTION_ID']),
|
onPressed: () => _openFlowDrawer(item['INSPECTION_ID']),
|
||||||
),
|
),
|
||||||
SizedBox(width: 1),
|
if (item['INSPECTION_STATUS'] == '0' ||
|
||||||
|
item['INSPECTION_STATUS'] == '-1'||
|
||||||
|
item['INSPECTION_STATUS'] == '-2')
|
||||||
|
Row(
|
||||||
|
spacing: 5,
|
||||||
|
children: [
|
||||||
|
if (item['INSPECTION_STATUS'] == '-1')
|
||||||
|
CustomButton(
|
||||||
|
text: '编辑',
|
||||||
|
height: 35,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: () => _goToDetail(item, 'edit'),
|
||||||
|
),
|
||||||
|
if (item['INSPECTION_STATUS'] == '-1')
|
||||||
|
CustomButton(
|
||||||
|
text: '查看',
|
||||||
|
height: 35,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: () => _goToDetail(item, 'look'),
|
||||||
|
),
|
||||||
|
if (item['INSPECTION_STATUS'] == '-2' && item['INSPECTION_ORIGINATOR_ID'] == SessionService.instance.loginUserId)
|
||||||
|
CustomButton(
|
||||||
|
text: '申辩处理',
|
||||||
|
height: 35,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
margin: EdgeInsets.only(left: 0),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
onPressed: () => _goToDefend(item),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -425,7 +468,7 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
body: Column(
|
body: SafeArea(child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Filter bar
|
// Filter bar
|
||||||
Container(
|
Container(
|
||||||
|
|
@ -475,7 +518,7 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
||||||
// List
|
// List
|
||||||
Expanded(child: _buildListContent()),
|
Expanded(child: _buildListContent()),
|
||||||
],
|
],
|
||||||
),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
import 'package:qhd_prevention/pages/KeyProjects/Danger/danger_list_page.dart';
|
import 'package:qhd_prevention/pages/KeyProjects/Danger/danger_list_page.dart';
|
||||||
import 'package:qhd_prevention/pages/KeyProjects/KeyProject/keyProject_list_page.dart';
|
import 'package:qhd_prevention/pages/KeyProjects/KeyProject/keyProject_list_page.dart';
|
||||||
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/safeCheck_list_page.dart';
|
import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/safeCheck_list_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSign/safeCheck_sign_list_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_list_page.dart';
|
||||||
|
import 'package:qhd_prevention/pages/home/SafeCheck/DangeCheck/safeCheck_danger_list_page.dart';
|
||||||
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_list_page.dart';
|
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_list_page.dart';
|
||||||
import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_icon_grid.dart';
|
import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_icon_grid.dart';
|
||||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
|
@ -100,15 +103,19 @@ class _SafecheckTabListState extends State<SafecheckTabList> {
|
||||||
} break;
|
} break;
|
||||||
case 1: {
|
case 1: {
|
||||||
title = '安全检查核实';
|
title = '安全检查核实';
|
||||||
// await pushPage(SafecheckListPage(flow: title), context);
|
await pushPage(CheckPersonListPage(flow: title), context);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case 2: {
|
case 2: {
|
||||||
title = '安全检查确认';
|
title = '安全检查确认';
|
||||||
// await pushPage(DangerListPage(flow: title), context);
|
await pushPage(SafecheckSignListPage(flow: title), context);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
case 3: {
|
||||||
|
title = '隐患指派及验收';
|
||||||
|
await pushPage(SafecheckDangerListPage(flow: title), context);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case 3: title = '隐患指派及验收'; break;
|
|
||||||
case 4: title = '申辩记录'; break;
|
case 4: title = '申辩记录'; break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ class ItemListWidget {
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
style: TextStyle(fontSize: fontSize),
|
style: TextStyle(fontSize: fontSize),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
hintText: hintText,
|
hintText: hintText,
|
||||||
|
|
@ -65,11 +66,13 @@ class ItemListWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Text(
|
: Expanded(child: Text(
|
||||||
text ?? '',
|
text ?? '',
|
||||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
maxLines: 5,
|
||||||
overflow: TextOverflow.ellipsis, // 超出省略
|
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||||
),
|
textAlign: TextAlign.right,
|
||||||
|
overflow: TextOverflow.ellipsis, // 超出省略
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -207,7 +210,7 @@ class ItemListWidget {
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
text.isNotEmpty ? text : '请选择',
|
text.isNotEmpty ? text : '请选择',
|
||||||
maxLines: 1,
|
maxLines: 5,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
|
|
@ -597,6 +600,8 @@ class ItemListWidget {
|
||||||
double fontSize = 15, // 字体大小
|
double fontSize = 15, // 字体大小
|
||||||
Color btnColor = Colors.blue,
|
Color btnColor = Colors.blue,
|
||||||
bool isRequired = false,
|
bool isRequired = false,
|
||||||
|
String text = '',
|
||||||
|
void Function(String)? onTapCallBack,
|
||||||
}) {
|
}) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||||
|
|
@ -614,23 +619,25 @@ class ItemListWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
GestureDetector(
|
Column(
|
||||||
onTap: () {
|
children: [
|
||||||
if (imgPath.isNotEmpty) {
|
GestureDetector(
|
||||||
SingleImageViewer(
|
onTap: () {
|
||||||
imageUrl: '${ApiService.baseImgPath}${imgPath}',
|
if (onTapCallBack != null) onTapCallBack('${ApiService.baseImgPath}$imgPath');
|
||||||
);
|
},
|
||||||
}
|
child:
|
||||||
},
|
|
||||||
child:
|
|
||||||
imgPath.isNotEmpty
|
imgPath.isNotEmpty
|
||||||
? Image.network(
|
? Image.network(
|
||||||
'${ApiService.baseImgPath}${imgPath}',
|
'${ApiService.baseImgPath}${imgPath}',
|
||||||
width: 40,
|
width: 50,
|
||||||
height: 40,
|
height: 50,
|
||||||
)
|
)
|
||||||
: SizedBox(),
|
: SizedBox(),
|
||||||
),
|
),
|
||||||
|
if (text.isNotEmpty)
|
||||||
|
Text(text)
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qhd_prevention/customWidget/remote_file_page.dart';
|
import 'package:qhd_prevention/customWidget/remote_file_page.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||||
import 'package:qhd_prevention/http/ApiService.dart';
|
import 'package:qhd_prevention/http/ApiService.dart';
|
||||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||||
|
|
@ -86,6 +87,9 @@ class _SxkjTzglDetailState extends State<SxkjTzglDetail> {
|
||||||
ItemListWidget.OneRowImageTitle(
|
ItemListWidget.OneRowImageTitle(
|
||||||
label: '警示标志附件',
|
label: '警示标志附件',
|
||||||
imgPath: pd['WARNINGSIGNS_PATH'] ?? '',
|
imgPath: pd['WARNINGSIGNS_PATH'] ?? '',
|
||||||
|
onTapCallBack: (path) {
|
||||||
|
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||||
|
}
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
ItemListWidget.singleLineTitleText(
|
ItemListWidget.singleLineTitleText(
|
||||||
|
|
|
||||||
308
pubspec.lock
308
pubspec.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue