hs 2025-08-16 14:10:57 +08:00
parent b2b2298929
commit 045c18fbf4
15 changed files with 500 additions and 4125 deletions

View File

@ -12,7 +12,7 @@ class CustomAlertDialog extends StatefulWidget {
final VoidCallback? onCancel;
final VoidCallback? onConfirm; //
final ValueChanged<String>? onInputConfirm; //
final DialogMode mode; //
final DialogMode mode; //
const CustomAlertDialog({
Key? key,
@ -24,87 +24,11 @@ class CustomAlertDialog extends StatefulWidget {
this.onCancel,
this.onConfirm,
this.onInputConfirm,
this.mode = DialogMode.text,
this.mode = DialogMode.text, //
}) : super(key: key);
@override
_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> {
@ -113,7 +37,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
@override
void initState() {
super.initState();
// TextField
// TextField
_controller = TextEditingController();
}
@ -130,32 +54,28 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
constraints: const BoxConstraints(minWidth: 280),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(5),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
widget.title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
Text(
widget.title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
const SizedBox(height: 20),
// mode
if (widget.mode == DialogMode.text)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Text(
widget.content,
style: const TextStyle(fontSize: 16, color: Colors.black54),
style: const TextStyle(fontSize: 16, color: Colors.black45),
textAlign: TextAlign.center,
),
)
@ -164,7 +84,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
padding: const EdgeInsets.symmetric(horizontal: 20),
child: TextField(
controller: _controller,
autofocus: true,
decoration: InputDecoration(
hintText: widget.hintText,
border: const OutlineInputBorder(),
@ -184,7 +104,9 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
const SizedBox(height: 20),
const Divider(height: 1),
hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context),
hasCancel
? _buildDoubleButtons(context)
: _buildSingleButton(context),
],
),
),
@ -198,11 +120,8 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
Expanded(
child: InkWell(
onTap: () {
// false null
final ret = widget.mode == DialogMode.text ? false : null;
//
Navigator.of(context).pop();
widget.onCancel?.call();
Navigator.of(context).pop(ret);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
@ -211,7 +130,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
widget.cancelText,
style: const TextStyle(
fontWeight: FontWeight.w500,
color: Colors.black87,
color: Colors.black,
fontSize: 18,
),
),
@ -225,13 +144,11 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
if (widget.mode == DialogMode.text) {
widget.onConfirm?.call();
Navigator.of(context).pop(true);
} else {
final value = _controller.text.trim();
widget.onInputConfirm?.call(value);
Navigator.of(context).pop(value);
widget.onInputConfirm?.call(_controller.text.trim());
}
},
child: Container(
@ -255,13 +172,11 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
Widget _buildSingleButton(BuildContext context) {
return InkWell(
onTap: () {
Navigator.of(context).pop();
if (widget.mode == DialogMode.text) {
widget.onConfirm?.call();
Navigator.of(context).pop(true);
} else {
final value = _controller.text.trim();
widget.onInputConfirm?.call(value);
Navigator.of(context).pop(value);
widget.onInputConfirm?.call(_controller.text.trim());
}
},
child: Container(

View File

@ -16,31 +16,23 @@ class Person {
}
}
///
/// USER_ID NAME
typedef PersonSelectCallback = void Function(String userId, String name);
/// indexint 0
typedef PersonSelectCallbackWithIndex = void Function(String userId, String name, int index);
/// 使
class DepartmentPersonPicker {
///
///
/// [personsData]: Map
/// [onSelected]: USER_ID NAME
/// [onSelectedWithIndex]: indexindex personsData/_all 0
/// [onSelected]: USER_ID NAME
static Future<void> show(
BuildContext context, {
required List<Map<String, dynamic>> personsData,
PersonSelectCallback? onSelected,
PersonSelectCallbackWithIndex? onSelectedWithIndex,
required PersonSelectCallback onSelected,
}) async {
//
assert(onSelected != null || onSelectedWithIndex != null,
'请至少传入 onSelected 或 onSelectedWithIndex');
//
final List<Person> _all = personsData.map((e) => Person.fromJson(e)).toList();
final List<Person> _all =
personsData.map((e) => Person.fromJson(e)).toList();
List<Person> _filtered = List.from(_all);
String _selectedName = '';
String _selectedId = '';
@ -62,7 +54,9 @@ class DepartmentPersonPicker {
setState(() {
_filtered = q.isEmpty
? List.from(_all)
: _all.where((p) => p.name.toLowerCase().contains(q)).toList();
: _all
.where((p) => p.name.toLowerCase().contains(q))
.toList();
});
}
@ -73,24 +67,24 @@ class DepartmentPersonPicker {
children: [
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
child: Row(
children: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text(
'取消',
style: TextStyle(fontSize: 16),
),
child: const Text('取消',style: TextStyle(fontSize: 16),),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
padding: const EdgeInsets.symmetric(
horizontal: 8),
child: SearchBarWidget(
controller: _searchController,
onTextChanged: _onSearch,
isShowSearchButton: false,
onSearch: (keyboard) {},
onSearch: (keyboard) {
},
),
),
),
@ -99,22 +93,9 @@ class DepartmentPersonPicker {
? null
: () {
Navigator.of(ctx).pop();
// 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);
}
onSelected(_selectedId, _selectedName);
},
child: const Text(
'确定',
style: TextStyle(color: Colors.green, fontSize: 16),
),
child: const Text('确定', style: TextStyle(color: Colors.green, fontSize: 16),),
),
],
),
@ -131,7 +112,8 @@ class DepartmentPersonPicker {
return ListTile(
titleAlignment: ListTileTitleAlignment.center,
title: Text(person.name),
trailing: selected ? const Icon(Icons.check, color: Colors.green) : null,
trailing:
selected ? const Icon(Icons.check, color: Colors.green) : null,
onTap: () => setState(() {
_selectedId = person.userId;
_selectedName = person.name;
@ -149,4 +131,4 @@ class DepartmentPersonPicker {
},
);
}
}
}

View File

@ -19,7 +19,7 @@ class MultiTextFieldWithTitle extends StatefulWidget {
required this.hintText,
required this.onTextsChanged,
this.fontSize = 15,
this.texts = const [],
this.texts = const [],
this.isRequired = true,
});
@ -35,97 +35,14 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
@override
void initState() {
super.initState();
// texts controllers
if (widget.texts.isNotEmpty) {
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);
}
//
// setState
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) widget.onTextsChanged(_getAllTexts());
if (mounted) {
_addTextField();
}
});
}
@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
void dispose() {
for (var controller in _controllers) {
@ -137,10 +54,9 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
super.dispose();
}
// _addTextField
void _addTextField([String initialText = '']) {
void _addTextField() {
setState(() {
final newController = TextEditingController(text: initialText);
final newController = TextEditingController();
final newFocusNode = FocusNode();
newController.addListener(() {
@ -150,35 +66,32 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
_controllers.add(newController);
_focusNodes.add(newFocusNode);
widget.onTextsChanged(_getAllTexts());
//
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
final idx = _controllers.length - 1;
_focusNodes[idx].requestFocus();
}
});
});
}
void _removeTextField(int index) async {
if (_controllers.length <= 1) return;
final confirmed = await CustomAlertDialog.showConfirm(
context,
title: '提示',
content: '确定删除检查情况吗?',
cancelText: '取消',
confirmText: '确定',
await showDialog<String>(
context: context,
builder:
(_) => CustomAlertDialog(
title: '提示',
mode: DialogMode.text,
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() {
@ -226,7 +139,7 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
horizontal: 5,
),
backgroundColor: Colors.blue,
onPressed: () => _addTextField(),
onPressed: _addTextField,
),
],
),
@ -248,17 +161,20 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
...widget.texts.map((c) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: SizedBox(
width: double.maxFinite,
child: DottedBorderBox(
child: Text(
c,
style: TextStyle(
fontSize: widget.fontSize,
color: Colors.grey[600],
),
width: double.maxFinite,
child: DottedBorderBox(
child:
Text(
c,
style: TextStyle(
fontSize: widget.fontSize,
color: Colors.grey[600],
),
)),
),
),
)
);
}).toList(),
],
@ -277,18 +193,19 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
Padding(
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
child: SizedBox(
width: double.maxFinite,
child: DottedBorderBox(
child: TextField(
controller: _controllers[index],
decoration: InputDecoration(hintText: widget.hintText),
focusNode: _focusNodes[index],
keyboardType: TextInputType.multiline,
maxLines: 3,
minLines: 3,
style: TextStyle(fontSize: widget.fontSize),
),
)),
width: double.maxFinite,
child: DottedBorderBox(
child: TextField(
controller: _controllers[index],
decoration: InputDecoration(hintText: widget.hintText),
focusNode: _focusNodes[index],
keyboardType: TextInputType.multiline,
maxLines: 3,
minLines: 3,
style: TextStyle(fontSize: widget.fontSize),
),
)
),
),
//

View File

@ -1,473 +0,0 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/toast_util.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 SafecheckStartListPage extends StatefulWidget {
final String flow;
const SafecheckStartListPage({Key? key, required this.flow})
: super(key: key);
@override
_SafecheckStartListPageState createState() => _SafecheckStartListPageState();
}
class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
// 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'] : '',
'KEYWORDS': searchKeywords,
};
final url =
'/app/safetyenvironmental/list?showCount=-1&currentPage=$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 _handleApply() {
//
pushPage(SafecheckStartDetail(INSPECTION_ID: '', isEdit: true), context);
}
///
Future<void> _openFlowDrawer(String ID) async {
try {
final response = await ApiService.safeCheckFlowList(ID);
final List<dynamic>? newFlow = response['varList'];
if (newFlow == null || newFlow.isEmpty) {
ToastUtil.showNormal(context, '暂无流程图数据');
return;
}
setState(() {
flowList = List<Map<String, dynamic>>.from(newFlow);
});
Future.microtask(() {
_scaffoldKey.currentState?.openEndDrawer();
});
} catch (e) {
print('Error fetching flow data: $e');
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('获取流程图失败: $e')));
}
}
void _goToDetail(Map<String, dynamic> item) async {
pushPage(SafecheckStartDetail(INSPECTION_ID: item['INSPECTION_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'] ?? ''}"),
],
),
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(
children: [
CustomButton(
text: '流程图',
height: 35,
padding: EdgeInsets.symmetric(horizontal: 12),
margin: EdgeInsets.only(left: 0),
backgroundColor: Colors.blue,
onPressed: () => _openFlowDrawer(item['INSPECTION_ID']),
),
SizedBox(width: 1),
],
),
],
),
),
),
);
}
//
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: [
TextButton(
onPressed: _handleApply,
child: const Text(
'发起',
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
],
),
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()),
],
)),
);
}
}

View File

@ -1,367 +0,0 @@
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 CheckPersonDetail extends StatefulWidget {
const CheckPersonDetail({
super.key,
required this.INSPECTION_ID,
required this.isEdit,
});
final String INSPECTION_ID;
final bool isEdit;
@override
State<CheckPersonDetail> createState() => _CheckPersonDetailState();
}
class _CheckPersonDetailState extends State<CheckPersonDetail> {
String msg = 'add';
bool _isEdit = false;
List<String> signImages = [];
List<String> signTimes = []; //
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'] ?? {};
});
} 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 { 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.add(path);
signTimes.add(now);
FocusHelper.clearFocus(context);
});
}
}
}
// ==========
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.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) {
}),
ItemListWidget.itemContainer(
horizontal: 0,
Column(
children: [
if (signImages.isNotEmpty) _signListWidget(),
],
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_isEdit)
SizedBox(
width: 150,
child: CustomButton(
text: '返回',
textStyle: TextStyle(
color: Colors.white,
fontSize: 17,
),
backgroundColor: Colors.black38,
onPressed: () => Navigator.pop(context),
),
),
SizedBox(
width: 150,
child: CustomButton(
text: _isEdit ? '提交' : '返回',
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(),
),
),
);
}
}

View File

@ -1,476 +0,0 @@
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'] : '',
'KEYWORDS': searchKeywords,
};
final url =
'/app/safetyenvironmentalinspector/list?showCount=-1&currentPage=$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 _handleApply() {
//
pushPage(SafecheckStartDetail(INSPECTION_ID: '', isEdit: true), context);
}
///
Future<void> _openFlowDrawer(String ID) async {
try {
final response = await ApiService.safeCheckFlowList(ID);
final List<dynamic>? newFlow = response['varList'];
if (newFlow == null || newFlow.isEmpty) {
ToastUtil.showNormal(context, '暂无流程图数据');
return;
}
setState(() {
flowList = List<Map<String, dynamic>>.from(newFlow);
});
Future.microtask(() {
_scaffoldKey.currentState?.openEndDrawer();
});
} catch (e) {
print('Error fetching flow data: $e');
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('获取流程图失败: $e')));
}
}
void _goToDetail(Map<String, dynamic> item) async {
pushPage(CheckPersonDetail(INSPECTION_ID: item['INSPECTION_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'] ?? ''}"),
],
),
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: [
TextButton(
onPressed: _handleApply,
child: const Text(
'发起',
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
],
),
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()),
],
)),
);
}
}

View File

@ -1,473 +0,0 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/toast_util.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 SafecheckStartListPage extends StatefulWidget {
final String flow;
const SafecheckStartListPage({Key? key, required this.flow})
: super(key: key);
@override
_SafecheckStartListPageState createState() => _SafecheckStartListPageState();
}
class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
// 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'] : '',
'KEYWORDS': searchKeywords,
};
final url =
'/app/safetyenvironmental/list?showCount=-1&currentPage=$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 _handleApply() {
//
pushPage(SafecheckStartDetail(INSPECTION_ID: '', isEdit: true), context);
}
///
Future<void> _openFlowDrawer(String ID) async {
try {
final response = await ApiService.safeCheckFlowList(ID);
final List<dynamic>? newFlow = response['varList'];
if (newFlow == null || newFlow.isEmpty) {
ToastUtil.showNormal(context, '暂无流程图数据');
return;
}
setState(() {
flowList = List<Map<String, dynamic>>.from(newFlow);
});
Future.microtask(() {
_scaffoldKey.currentState?.openEndDrawer();
});
} catch (e) {
print('Error fetching flow data: $e');
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('获取流程图失败: $e')));
}
}
void _goToDetail(Map<String, dynamic> item) async {
pushPage(SafecheckStartDetail(INSPECTION_ID: item['INSPECTION_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'] ?? ''}"),
],
),
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(
children: [
CustomButton(
text: '流程图',
height: 35,
padding: EdgeInsets.symmetric(horizontal: 12),
margin: EdgeInsets.only(left: 0),
backgroundColor: Colors.blue,
onPressed: () => _openFlowDrawer(item['INSPECTION_ID']),
),
SizedBox(width: 1),
],
),
],
),
),
),
);
}
//
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: [
TextButton(
onPressed: _handleApply,
child: const Text(
'发起',
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
],
),
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()),
],
)),
);
}
}

View File

@ -1,473 +0,0 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/toast_util.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 SafecheckStartListPage extends StatefulWidget {
final String flow;
const SafecheckStartListPage({Key? key, required this.flow})
: super(key: key);
@override
_SafecheckStartListPageState createState() => _SafecheckStartListPageState();
}
class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
// 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'] : '',
'KEYWORDS': searchKeywords,
};
final url =
'/app/safetyenvironmental/list?showCount=-1&currentPage=$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 _handleApply() {
//
pushPage(SafecheckStartDetail(INSPECTION_ID: '', isEdit: true), context);
}
///
Future<void> _openFlowDrawer(String ID) async {
try {
final response = await ApiService.safeCheckFlowList(ID);
final List<dynamic>? newFlow = response['varList'];
if (newFlow == null || newFlow.isEmpty) {
ToastUtil.showNormal(context, '暂无流程图数据');
return;
}
setState(() {
flowList = List<Map<String, dynamic>>.from(newFlow);
});
Future.microtask(() {
_scaffoldKey.currentState?.openEndDrawer();
});
} catch (e) {
print('Error fetching flow data: $e');
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('获取流程图失败: $e')));
}
}
void _goToDetail(Map<String, dynamic> item) async {
pushPage(SafecheckStartDetail(INSPECTION_ID: item['INSPECTION_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'] ?? ''}"),
],
),
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(
children: [
CustomButton(
text: '流程图',
height: 35,
padding: EdgeInsets.symmetric(horizontal: 12),
margin: EdgeInsets.only(left: 0),
backgroundColor: Colors.blue,
onPressed: () => _openFlowDrawer(item['INSPECTION_ID']),
),
SizedBox(width: 1),
],
),
],
),
),
),
);
}
//
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: [
TextButton(
onPressed: _handleApply,
child: const Text(
'发起',
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
],
),
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()),
],
)),
);
}
}

View File

@ -1,230 +0,0 @@
// 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/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';
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.all(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'] ?? '请选择',
),
],
),
),
),
),
],
);
//
}
@override
Widget build(BuildContext context) {
final form = widget.form;
final isEdit = widget.isEdit;
List<dynamic> inspectorList = widget.form['inspectorList'];
return SizedBox(
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
child:
Column(
children: [
//
ItemListWidget.itemContainer(
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(),
ListItemFactory.headerTitle('检查人员'),
SizedBox(height: 10),
ListView.builder(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
itemCount: inspectorList.length,
itemBuilder: (context, index) {
return _personUnitItem(
inspectorList[index],
index,
);
},
),
const Divider(),
ItemListWidget.itemContainer(
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,
),
],
),
),
const SizedBox(height: 20),
],
),
);
}
}

View File

@ -1,565 +0,0 @@
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

View File

@ -154,11 +154,15 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
required Map<String, dynamic> item,
required bool isFirst,
required bool isLast,
required int status, // 1 = , 0 = , -1 =
}) {
bool status = item['active'] == item['order'];
//
final Color dotColor = status ? Colors.blue :Colors.grey;
final Color textColor = status ? Colors.blue :Colors.black54;
final Color dotColor =
status == 1 ? Colors.blue : (status == 0 ? Colors.blue : Colors.grey);
final Color textColor =
status == 1
? Colors.blue
: (status == 0 ? Colors.blue : Colors.black54);
return ListTile(
visualDensity: VisualDensity(vertical: -4),
@ -407,17 +411,21 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
final bool isFirst = idx == 0;
final bool isLast = idx == flowList.length - 1;
// lastDoneIndex
final int status = item['active'] == '1' ? 1 : -1;
return _buildFlowStepItem(
item: item,
isFirst: isFirst,
isLast: isLast,
status: status,
);
},
),
),
),
body: SafeArea(child: Column(
body: Column(
children: [
// Filter bar
Container(
@ -467,7 +475,7 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
// List
Expanded(child: _buildListContent()),
],
)),
),
);
}
}

View File

@ -4,7 +4,6 @@ import 'package:qhd_prevention/http/ApiService.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/SafeCheck/safeCheck_list_page.dart';
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_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/my_appbar.dart';
@ -101,7 +100,7 @@ class _SafecheckTabListState extends State<SafecheckTabList> {
} break;
case 1: {
title = '安全检查核实';
await pushPage(CheckPersonListPage(flow: title), context);
// await pushPage(SafecheckListPage(flow: title), context);
} break;
case 2: {

View File

@ -207,7 +207,7 @@ class ItemListWidget {
Flexible(
child: Text(
text.isNotEmpty ? text : '请选择',
maxLines: 5,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: fontSize,
@ -597,8 +597,6 @@ class ItemListWidget {
double fontSize = 15, //
Color btnColor = Colors.blue,
bool isRequired = false,
String text = '',
void Function(String)? onTapCallBack,
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
@ -616,25 +614,23 @@ class ItemListWidget {
),
],
),
Column(
children: [
GestureDetector(
onTap: () {
if (onTapCallBack != null) onTapCallBack('${ApiService.baseImgPath}$imgPath');
},
child:
GestureDetector(
onTap: () {
if (imgPath.isNotEmpty) {
SingleImageViewer(
imageUrl: '${ApiService.baseImgPath}${imgPath}',
);
}
},
child:
imgPath.isNotEmpty
? Image.network(
'${ApiService.baseImgPath}${imgPath}',
width: 50,
height: 50,
)
'${ApiService.baseImgPath}${imgPath}',
width: 40,
height: 40,
)
: SizedBox(),
),
if (text.isNotEmpty)
Text(text)
],
)
),
],
),
);

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.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/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
@ -87,9 +86,6 @@ class _SxkjTzglDetailState extends State<SxkjTzglDetail> {
ItemListWidget.OneRowImageTitle(
label: '警示标志附件',
imgPath: pd['WARNINGSIGNS_PATH'] ?? '',
onTapCallBack: (path) {
presentOpaque(SingleImageViewer(imageUrl: path), context);
}
),
Divider(),
ItemListWidget.singleLineTitleText(