时间允许未来
parent
b756e4e809
commit
d14e56616e
|
@ -37,16 +37,17 @@ class ListItemFactory {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
if (isRight)
|
if (isRight)
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(child: Text(
|
||||||
_truncateText(rightText, 100),
|
rightText,
|
||||||
// rightText,
|
// rightText,
|
||||||
style: TextStyle(fontSize: 15, color: Colors.grey),
|
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||||
),
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),),
|
||||||
SizedBox(width: 2),
|
SizedBox(width: 2),
|
||||||
Icon(
|
Icon(
|
||||||
Icons.arrow_forward_ios_rounded,
|
Icons.arrow_forward_ios_rounded,
|
||||||
|
@ -56,7 +57,8 @@ class ListItemFactory {
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Text(rightText, style: TextStyle(fontSize: 15, color: Colors.grey)),
|
Expanded(child: Text(rightText, style: TextStyle(fontSize: 15, color: Colors.grey,),maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis, textAlign: TextAlign.right,)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:dotted_border/dotted_border.dart';
|
||||||
|
|
||||||
|
class DottedBorderBox extends StatelessWidget {
|
||||||
|
final Widget? child;
|
||||||
|
final Color color;
|
||||||
|
final double strokeWidth;
|
||||||
|
final List<double> dashPattern;
|
||||||
|
final BorderRadius borderRadius;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
final StrokeCap strokeCap;
|
||||||
|
|
||||||
|
const DottedBorderBox({
|
||||||
|
super.key,
|
||||||
|
this.child,
|
||||||
|
this.color = Colors.black26,
|
||||||
|
this.strokeWidth = 1.5,
|
||||||
|
this.dashPattern = const [6, 3],
|
||||||
|
this.borderRadius = const BorderRadius.all(Radius.circular(8)),
|
||||||
|
this.padding = const EdgeInsets.all(8),
|
||||||
|
this.strokeCap = StrokeCap.butt,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DottedBorder(
|
||||||
|
options: RoundedRectDottedBorderOptions(
|
||||||
|
// 控制内边距(虚线与外部的间隔)
|
||||||
|
borderPadding: EdgeInsets.zero,
|
||||||
|
// 控制虚线与 child 的间距(虚线内侧留白)
|
||||||
|
padding: padding,
|
||||||
|
color: color,
|
||||||
|
strokeWidth: strokeWidth,
|
||||||
|
dashPattern: dashPattern,
|
||||||
|
strokeCap: strokeCap, radius: Radius.circular(0),
|
||||||
|
// 如果需要,可以传 gradient 等
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
child: child ?? const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// 调用示例:
|
/// 调用示例:
|
||||||
/// DateTime? picked = await BottomDateTimePicker.show(context);
|
/// DateTime? picked = await BottomDateTimePicker.showDate(
|
||||||
|
/// context,
|
||||||
|
/// allowFuture: true, // 添加此参数允许选择未来时间
|
||||||
|
/// );
|
||||||
/// if (picked != null) {
|
/// if (picked != null) {
|
||||||
/// print('用户选择的时间:$picked');
|
/// print('用户选择的时间:$picked');
|
||||||
/// }
|
/// }
|
||||||
class BottomDateTimePicker {
|
class BottomDateTimePicker {
|
||||||
static Future<DateTime?> showDate(BuildContext context) {
|
static Future<DateTime?> showDate(BuildContext context, {bool allowFuture = false}) {
|
||||||
return showModalBottomSheet<DateTime>(
|
return showModalBottomSheet<DateTime>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
|
@ -16,12 +18,16 @@ class BottomDateTimePicker {
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||||
),
|
),
|
||||||
builder: (_) => _InlineDateTimePickerContent(),
|
builder: (_) => _InlineDateTimePickerContent(allowFuture: allowFuture),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InlineDateTimePickerContent extends StatefulWidget {
|
class _InlineDateTimePickerContent extends StatefulWidget {
|
||||||
|
final bool allowFuture; // 允许未来
|
||||||
|
|
||||||
|
const _InlineDateTimePickerContent({Key? key, this.allowFuture = false}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_InlineDateTimePickerContent> createState() => _InlineDateTimePickerContentState();
|
State<_InlineDateTimePickerContent> createState() => _InlineDateTimePickerContentState();
|
||||||
}
|
}
|
||||||
|
@ -30,10 +36,12 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
// 数据源
|
// 数据源
|
||||||
final List<int> years = List.generate(101, (i) => 1970 + i);
|
final List<int> years = List.generate(101, (i) => 1970 + i);
|
||||||
final List<int> months = List.generate(12, (i) => i + 1);
|
final List<int> months = List.generate(12, (i) => i + 1);
|
||||||
final List<int> days = List.generate(31, (i) => i + 1);
|
|
||||||
final List<int> hours = List.generate(24, (i) => i);
|
final List<int> hours = List.generate(24, (i) => i);
|
||||||
final List<int> minutes = List.generate(60, (i) => i);
|
final List<int> minutes = List.generate(60, (i) => i);
|
||||||
|
|
||||||
|
// 动态天数列表(根据年月变化)
|
||||||
|
late List<int> days;
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
late FixedExtentScrollController yearCtrl;
|
late FixedExtentScrollController yearCtrl;
|
||||||
late FixedExtentScrollController monthCtrl;
|
late FixedExtentScrollController monthCtrl;
|
||||||
|
@ -58,6 +66,9 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
selectedHour = now.hour;
|
selectedHour = now.hour;
|
||||||
selectedMinute = now.minute;
|
selectedMinute = now.minute;
|
||||||
|
|
||||||
|
// 初始化天数列表
|
||||||
|
days = _getDaysInMonth(selectedYear, selectedMonth);
|
||||||
|
|
||||||
yearCtrl = FixedExtentScrollController(initialItem: years.indexOf(selectedYear));
|
yearCtrl = FixedExtentScrollController(initialItem: years.indexOf(selectedYear));
|
||||||
monthCtrl = FixedExtentScrollController(initialItem: selectedMonth - 1);
|
monthCtrl = FixedExtentScrollController(initialItem: selectedMonth - 1);
|
||||||
dayCtrl = FixedExtentScrollController(initialItem: selectedDay - 1);
|
dayCtrl = FixedExtentScrollController(initialItem: selectedDay - 1);
|
||||||
|
@ -65,19 +76,33 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute);
|
minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
// 根据年月获取当月天数
|
||||||
void dispose() {
|
List<int> _getDaysInMonth(int year, int month) {
|
||||||
yearCtrl.dispose();
|
final lastDay = DateUtils.getDaysInMonth(year, month);
|
||||||
monthCtrl.dispose();
|
return List.generate(lastDay, (i) => i + 1);
|
||||||
dayCtrl.dispose();
|
|
||||||
hourCtrl.dispose();
|
|
||||||
minuteCtrl.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新天数列表并调整选中日期
|
||||||
|
void _updateDays() {
|
||||||
|
final newDays = _getDaysInMonth(selectedYear, selectedMonth);
|
||||||
|
final isDayValid = selectedDay <= newDays.length;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
days = newDays;
|
||||||
|
if (!isDayValid) {
|
||||||
|
selectedDay = newDays.last;
|
||||||
|
dayCtrl.jumpToItem(selectedDay - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查并限制时间(当不允许未来时间时)
|
||||||
void _checkAndClampToNow() {
|
void _checkAndClampToNow() {
|
||||||
|
if (widget.allowFuture) return; // 允许未来时间时跳过检查
|
||||||
|
|
||||||
final picked = DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute);
|
final picked = DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute);
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|
||||||
if (picked.isAfter(now)) {
|
if (picked.isAfter(now)) {
|
||||||
// 回滚到当前时间
|
// 回滚到当前时间
|
||||||
selectedYear = now.year;
|
selectedYear = now.year;
|
||||||
|
@ -92,9 +117,22 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
dayCtrl.jumpToItem(selectedDay - 1);
|
dayCtrl.jumpToItem(selectedDay - 1);
|
||||||
hourCtrl.jumpToItem(selectedHour);
|
hourCtrl.jumpToItem(selectedHour);
|
||||||
minuteCtrl.jumpToItem(selectedMinute);
|
minuteCtrl.jumpToItem(selectedMinute);
|
||||||
|
|
||||||
|
// 更新天数列表
|
||||||
|
_updateDays();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
yearCtrl.dispose();
|
||||||
|
monthCtrl.dispose();
|
||||||
|
dayCtrl.dispose();
|
||||||
|
hourCtrl.dispose();
|
||||||
|
minuteCtrl.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
|
@ -140,6 +178,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
onSelected: (idx) {
|
onSelected: (idx) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedYear = years[idx];
|
selectedYear = years[idx];
|
||||||
|
_updateDays(); // 年份变化时更新天数
|
||||||
_checkAndClampToNow();
|
_checkAndClampToNow();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -152,6 +191,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
onSelected: (idx) {
|
onSelected: (idx) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedMonth = months[idx];
|
selectedMonth = months[idx];
|
||||||
|
_updateDays(); // 月份变化时更新天数
|
||||||
_checkAndClampToNow();
|
_checkAndClampToNow();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -217,4 +257,4 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1174,6 +1174,61 @@ U6Hzm1ninpWeE+awIDAQAB
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///TODO -------------–-------------------- 安全检查 -------------–--------------------
|
||||||
|
static Future<Map<String, dynamic>> getSafeCheckCount() {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
'/app/safetyenvironmental/countCheck',
|
||||||
|
method: Method.post,
|
||||||
|
data: {
|
||||||
|
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||||
|
'INSPECTION_USER_ID': SessionService.instance.loginUserId,
|
||||||
|
'INSPECTED_SITEUSER_ID': SessionService.instance.loginUserId,
|
||||||
|
'INSPECTION_ORIGINATOR_ID': SessionService.instance.loginUserId,
|
||||||
|
"tm": DateTime.now().millisecondsSinceEpoch,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/// 安全检查流程图
|
||||||
|
static Future<Map<String, dynamic>> safeCheckFlowList(String ID) {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
'/app/safetyenvironmental/showFlowChart',
|
||||||
|
method: Method.post,
|
||||||
|
data: {
|
||||||
|
'ID' :ID
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/// 安全检查列表
|
||||||
|
static Future<Map<String, dynamic>> getSafeCheckSearchList(Map data, String url) {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
url,
|
||||||
|
method: Method.post,
|
||||||
|
data: {
|
||||||
|
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||||
|
"loginUserId":SessionService.instance.loginUserId,
|
||||||
|
'tm': DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
|
'roleLevel' : SessionService.instance.loginUser?['roleLevel'] ?? '',
|
||||||
|
'supDeparIds' : SessionService.instance.loginUser?['supDeparIds'] ?? '',
|
||||||
|
|
||||||
|
...data
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 安全检查列表详情
|
||||||
|
static Future<Map<String, dynamic>> getSafeCheckStartGoEdit(String INSPECTION_ID) {
|
||||||
|
return HttpManager().request(
|
||||||
|
basePath,
|
||||||
|
'/app/safetyenvironmental/goEdit',
|
||||||
|
method: Method.post,
|
||||||
|
data: {
|
||||||
|
"INSPECTION_ID":INSPECTION_ID,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
import 'package:qhd_prevention/customWidget/dotted_border_box.dart';
|
||||||
|
|
||||||
class MultiTextFieldWithTitle extends StatefulWidget {
|
class MultiTextFieldWithTitle extends StatefulWidget {
|
||||||
final String label;
|
final String label;
|
||||||
|
@ -160,21 +161,20 @@ 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: Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
child: SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
decoration: BoxDecoration(
|
child: DottedBorderBox(
|
||||||
border: Border.all(color: Colors.grey),
|
child:
|
||||||
borderRadius: BorderRadius.circular(4),
|
Text(
|
||||||
),
|
c,
|
||||||
child: Text(
|
style: TextStyle(
|
||||||
c,
|
fontSize: widget.fontSize,
|
||||||
style: TextStyle(
|
color: Colors.grey[600],
|
||||||
fontSize: widget.fontSize,
|
),
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
],
|
],
|
||||||
|
@ -192,21 +192,19 @@ class _MultiTextFieldWithTitleState extends State<MultiTextFieldWithTitle> {
|
||||||
// 输入框
|
// 输入框
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
|
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
|
||||||
child: Container(
|
child: SizedBox(
|
||||||
decoration: BoxDecoration(
|
width: double.maxFinite,
|
||||||
border: BoxBorder.all(color: Colors.grey.shade300, width: 1),
|
child: DottedBorderBox(
|
||||||
borderRadius: BorderRadius.circular(4),
|
child: TextField(
|
||||||
),
|
controller: _controllers[index],
|
||||||
padding: EdgeInsets.symmetric(vertical: 10),
|
decoration: InputDecoration(hintText: widget.hintText),
|
||||||
child: TextField(
|
focusNode: _focusNodes[index],
|
||||||
controller: _controllers[index],
|
keyboardType: TextInputType.multiline,
|
||||||
decoration: InputDecoration(hintText: widget.hintText),
|
maxLines: 3,
|
||||||
focusNode: _focusNodes[index],
|
minLines: 3,
|
||||||
keyboardType: TextInputType.multiline,
|
style: TextStyle(fontSize: widget.fontSize),
|
||||||
maxLines: 3,
|
),
|
||||||
minLines: 3,
|
)
|
||||||
style: TextStyle(fontSize: widget.fontSize),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,848 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
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/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/tap/item_list_widget.dart';
|
||||||
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||||
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
|
||||||
|
class SafecheckStartDetail extends StatefulWidget {
|
||||||
|
const SafecheckStartDetail({
|
||||||
|
super.key,
|
||||||
|
required this.INSPECTION_ID,
|
||||||
|
required this.isEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String INSPECTION_ID;
|
||||||
|
final bool isEdit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SafecheckStartDetail> createState() => _SafecheckStartDetailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
||||||
|
/// 被检查单位负责人
|
||||||
|
late List<dynamic> personList = [];
|
||||||
|
|
||||||
|
/// 检查类型
|
||||||
|
late List<dynamic> typeList = [];
|
||||||
|
|
||||||
|
/// 被检查单位
|
||||||
|
late List<dynamic> toCheckUnitList = [];
|
||||||
|
|
||||||
|
bool? chooseTitleType = null;
|
||||||
|
late List<dynamic> inspectorList = [];
|
||||||
|
|
||||||
|
// 存储多行输入的内容
|
||||||
|
List<String> multiTexts = [];
|
||||||
|
|
||||||
|
List<String> delInspectors = [];
|
||||||
|
List<String> delSituations = [];
|
||||||
|
List<String> delHiddens = [];
|
||||||
|
List<String> delHiddenFiles = [];
|
||||||
|
|
||||||
|
// rules 格式: [{ 'name': 'INSPECTION_CATEGORY', 'message': '请填写检查题目' }, ...]
|
||||||
|
List<Map<String, String>> rules = [
|
||||||
|
{'name': 'INSPECTION_CATEGORY', 'message': '请选择检查题目'},
|
||||||
|
{'name': 'UNITS_ID', 'message': '请选择被检查单位'},
|
||||||
|
{'name': 'PERSONNELMANAGEMENT_ID', 'message': '请选择被检查单位现场负责人'},
|
||||||
|
{'name': 'INSPECTION_TYPE', 'message': '请选择检查类型不能为空'},
|
||||||
|
{'name': 'INSPECTION_PLACE', 'message': '请输入检查场所'},
|
||||||
|
{'name': 'INSPECTION_TIME_START', 'message': '请选择检查开始时间'},
|
||||||
|
{'name': 'INSPECTION_TIME_END', 'message': '请选择作业结束时间'},
|
||||||
|
{'name': 'INSPECTION_USERS', 'message': '请输入检查人员'},
|
||||||
|
];
|
||||||
|
|
||||||
|
Map<String, dynamic> form = {
|
||||||
|
'INSPECTION_SOURCE': '5', // 检查来源(4-监管端 5-企业端)
|
||||||
|
'hiddenList': [
|
||||||
|
{
|
||||||
|
'ISRELEVANT': '2',
|
||||||
|
'SOURCE': '5', // 隐患来源
|
||||||
|
'RECTIFICATIONTYPE': '2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'INSPECTION_USER_SIGN_TIME': '',
|
||||||
|
'INSPECTION_USER_OPINION': '',
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
form['INSPECTION_ID'] = widget.INSPECTION_ID;
|
||||||
|
form['hiddenList'] = [];
|
||||||
|
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'] ?? {};
|
||||||
|
inspectorList =
|
||||||
|
FormUtils.hasValue(form, 'inspectorList')
|
||||||
|
? form['inspectorList']
|
||||||
|
: [];
|
||||||
|
_syncMultiTextsFromForm();
|
||||||
|
});
|
||||||
|
} catch (e, st) {
|
||||||
|
print('加载单条数据失败: $e\n$st');
|
||||||
|
if (mounted) {
|
||||||
|
ToastUtil.showNormal(context, '加载数据失败:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _choosePerson() async {
|
||||||
|
final choice = await BottomPicker.show<String>(
|
||||||
|
context,
|
||||||
|
items: personList.map((val) => val['NAME'] as String).toList(),
|
||||||
|
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||||
|
initialIndex: 0,
|
||||||
|
);
|
||||||
|
if (choice != null) {
|
||||||
|
// 用户点击确定并选择了 choice
|
||||||
|
setState(() {
|
||||||
|
form['INSPECTED_SITEUSER_INDEX'] = choice;
|
||||||
|
final data = FormUtils.findMapForKeyValue(personList, 'NAME', choice);
|
||||||
|
form['INSPECTED_SITEUSER_ID'] = data['USER_ID'];
|
||||||
|
form['INSPECTED_SITEUSER_NAME'] = data['NAME'];
|
||||||
|
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _chooseType() async {
|
||||||
|
final choice = await BottomPicker.show<String>(
|
||||||
|
context,
|
||||||
|
items: typeList.map((val) => val['name'] as String).toList(),
|
||||||
|
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||||
|
initialIndex: 0,
|
||||||
|
);
|
||||||
|
if (choice != null) {
|
||||||
|
// 用户点击确定并选择了 choice
|
||||||
|
setState(() {
|
||||||
|
form['INSPECTION_TYPE_NAME'] = choice;
|
||||||
|
final data = FormUtils.findMapForKeyValue(typeList, 'name', choice);
|
||||||
|
form['INSPECTION_TYPE'] = data['id'];
|
||||||
|
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openDrawer(Map<String, dynamic> hiddenForm, int index) async {
|
||||||
|
try {
|
||||||
|
final result = await openCustomDrawer<Map>(
|
||||||
|
context,
|
||||||
|
SafeDrawerPage(
|
||||||
|
initialHidden: hiddenForm,
|
||||||
|
editType:
|
||||||
|
widget.isEdit
|
||||||
|
? (index < 0 ? SafeEditType.add : SafeEditType.edit)
|
||||||
|
: SafeEditType.see,
|
||||||
|
toCheckUnitList: toCheckUnitList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && mounted) {
|
||||||
|
setState(() {
|
||||||
|
if (index < 0) {
|
||||||
|
// 新增
|
||||||
|
form['hiddenList'].add(result);
|
||||||
|
} 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将 List<String> 转换成 situationList(保留已存在的 INSPECTION_SITUATION_ID)
|
||||||
|
List<Map<String, dynamic>> _stringsToSituationList(List<String> texts) {
|
||||||
|
final List<dynamic> existing = List<dynamic>.from(
|
||||||
|
form['situationList'] ?? [],
|
||||||
|
);
|
||||||
|
final List<Map<String, dynamic>> out = [];
|
||||||
|
|
||||||
|
for (int i = 0; i < texts.length; i++) {
|
||||||
|
final s = texts[i] ?? '';
|
||||||
|
if (i < existing.length && existing[i] is Map) {
|
||||||
|
// 保留已有项的其他字段(如 INSPECTION_SITUATION_ID),只覆盖 SITUATION
|
||||||
|
final Map<String, dynamic> copy = Map<String, dynamic>.from(
|
||||||
|
existing[i],
|
||||||
|
);
|
||||||
|
copy['SITUATION'] = s;
|
||||||
|
// 若没有 INSPECTION_SITUATION_ID 字段,确保它存在(保持原 uni-app 结构)
|
||||||
|
copy['INSPECTION_SITUATION_ID'] = copy['INSPECTION_SITUATION_ID'] ?? '';
|
||||||
|
out.add(copy);
|
||||||
|
} else {
|
||||||
|
// 新增项
|
||||||
|
out.add({'INSPECTION_SITUATION_ID': '', 'SITUATION': s});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果用户删除了行,要把超出的 existing id 记录到 delSituations(可选)
|
||||||
|
// 下面为示例:把被删除的旧 ID 收集到 delSituations,便于编辑时提交删除列表
|
||||||
|
if (existing.isNotEmpty && existing.length > texts.length) {
|
||||||
|
for (int j = texts.length; j < existing.length; j++) {
|
||||||
|
final ex = existing[j];
|
||||||
|
if (ex is Map) {
|
||||||
|
final id = (ex['INSPECTION_SITUATION_ID'] ?? '').toString();
|
||||||
|
if (id.isNotEmpty) {
|
||||||
|
delSituations.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 在加载数据之后调用:把已存在的 form['situationList'] 同步到 multiTexts(用于 MultiTextFieldWithTitle)
|
||||||
|
void _syncMultiTextsFromForm() {
|
||||||
|
final List<String> arr = _situationListToStrings();
|
||||||
|
setState(() {
|
||||||
|
multiTexts = arr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------ 提交入口 ------------
|
||||||
|
Future<void> _submit() async {
|
||||||
|
if (!widget.isEdit) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool required = true;
|
||||||
|
// 基于 rules 验证
|
||||||
|
for (final r in rules) {
|
||||||
|
final name = r['name'] ?? '';
|
||||||
|
final message = r['message'] ?? '请完善表单';
|
||||||
|
final v = form[name];
|
||||||
|
if (v == null || v.toString().isEmpty || v.toString() == '请选择') {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, message);
|
||||||
|
required = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!required) return;
|
||||||
|
|
||||||
|
// situationList 每项 SITUATION 非空
|
||||||
|
final situations = (form['situationList'] as List<dynamic>?) ?? [];
|
||||||
|
for (var i = 0; i < situations.length; i++) {
|
||||||
|
final s = Map<String, dynamic>.from(situations[i]);
|
||||||
|
if ((s['SITUATION'] ?? '').toString().trim().isEmpty) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '请填写第${i + 1}项检查情况');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 inspectorList 中是否有重复 INSPECTION_USER_ID
|
||||||
|
final List<Map<String, String>> inspectors = form['inspectorList'] ?? [];
|
||||||
|
final seenIds = <String>{};
|
||||||
|
for (final it in inspectors) {
|
||||||
|
final id = (it as Map)['INSPECTION_USER_ID']?.toString() ?? '';
|
||||||
|
if (id.isNotEmpty) {
|
||||||
|
if (seenIds.contains(id)) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '检查人重复!请检查数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seenIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据 hiddenList 构建需要上传的文件数组
|
||||||
|
final origHiddenList = (form['hiddenList'] as List<dynamic>?) ?? [];
|
||||||
|
final List<List<Map<String, dynamic>>> hiddenFilesPerHidden = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < origHiddenList.length; i++) {
|
||||||
|
final hidden = Map<String, dynamic>.from(origHiddenList[i] as Map);
|
||||||
|
final List<Map<String, dynamic>> fileList = [];
|
||||||
|
|
||||||
|
// hiddenImgs (多张)
|
||||||
|
final hiddenImgs = (hidden['hiddenImgs'] as List<dynamic>?) ?? [];
|
||||||
|
for (var j = 0; j < hiddenImgs.length; j++) {
|
||||||
|
final img = hiddenImgs[j];
|
||||||
|
// 如果是字符串路径
|
||||||
|
if (img is String) {
|
||||||
|
fileList.add({'type': 3, 'FILEPATH': img});
|
||||||
|
} else if (img is Map) {
|
||||||
|
final hasId = (img['IMGFILES_ID'] ?? '').toString().isNotEmpty;
|
||||||
|
if (!hasId) {
|
||||||
|
fileList.add({
|
||||||
|
'type': 3,
|
||||||
|
'FILEPATH': img['FILEPATH'] ?? img['path'] ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// zgImgs (整改图)
|
||||||
|
final zgImgs = (hidden['zgImgs'] as List<dynamic>?) ?? [];
|
||||||
|
for (var j = 0; j < zgImgs.length; j++) {
|
||||||
|
final img = zgImgs[j];
|
||||||
|
if (img is String) {
|
||||||
|
fileList.add({'type': 4, 'FILEPATH': img});
|
||||||
|
} else if (img is Map) {
|
||||||
|
final hasId = (img['IMGFILES_ID'] ?? '').toString().isNotEmpty;
|
||||||
|
if (!hasId) {
|
||||||
|
fileList.add({
|
||||||
|
'type': 4,
|
||||||
|
'FILEPATH': img['FILEPATH'] ?? img['path'] ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hiddenVideos (只取第一个)
|
||||||
|
final hiddenVideos = (hidden['hiddenVideos'] as List<dynamic>?) ?? [];
|
||||||
|
if (hiddenVideos.isNotEmpty) {
|
||||||
|
final v = hiddenVideos[0];
|
||||||
|
if (v is String) {
|
||||||
|
fileList.add({'type': 102, 'FILEPATH': v});
|
||||||
|
} else if (v is Map) {
|
||||||
|
final hasId = (v['IMGFILES_ID'] ?? '').toString().isNotEmpty;
|
||||||
|
if (!hasId) {
|
||||||
|
fileList.add({
|
||||||
|
'type': 102,
|
||||||
|
'FILEPATH': v['FILEPATH'] ?? v['path'] ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hiddenFilesPerHidden.add(fileList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保当前登录用户在 inspectorList 中(依据 SessionService)
|
||||||
|
final loginUser = SessionService.instance.loginUser ?? {};
|
||||||
|
final loginUserId = SessionService.instance.loginUserId ?? '';
|
||||||
|
final idx = inspectors.indexWhere((item) {
|
||||||
|
final m = Map<String, dynamic>.from(item as Map);
|
||||||
|
return (m['INSPECTION_USER_ID'] ?? '') ==
|
||||||
|
(loginUser['USER_ID'] ?? loginUserId);
|
||||||
|
});
|
||||||
|
if (idx < 0) {
|
||||||
|
inspectors.add({
|
||||||
|
'INSPECTION_INSPECTOR_ID': '',
|
||||||
|
'INSPECTION_DEPARTMENT_ID': loginUser['DEPARTMENT_ID'] ?? '',
|
||||||
|
'INSPECTION_DEPARTMENT_NAME': loginUser['DEPARTMENT_NAME'] ?? '',
|
||||||
|
'INSPECTION_USER_ID': loginUser['USER_ID'] ?? loginUserId,
|
||||||
|
'INSPECTION_USER_INDEX': '',
|
||||||
|
'INSPECTION_USER_NAME': loginUser['NAME'] ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备 form 字段(JSON 字符串等)
|
||||||
|
form['INSPECTORJSON'] = jsonEncode(inspectors);
|
||||||
|
form['SITUATIONJSON'] = jsonEncode(situations);
|
||||||
|
form['HIDDENJSON'] = jsonEncode(origHiddenList);
|
||||||
|
form['delInspectors'] = delInspectors.join(',');
|
||||||
|
form['delSituations'] = delSituations.join(',');
|
||||||
|
form['delHiddens'] = delHiddens.join(',');
|
||||||
|
form['delHiddenFiles'] = delHiddenFiles.join(',');
|
||||||
|
form['CREATOR'] = loginUser['USER_ID'] ?? loginUserId;
|
||||||
|
form['CORPINFO_ID'] = SessionService.instance.corpinfoId ?? '';
|
||||||
|
form['ACTION_USER'] = loginUser['NAME'] ?? '';
|
||||||
|
LoadingDialogHelper.show(); // 显示 loading
|
||||||
|
|
||||||
|
// 提交主表
|
||||||
|
try {
|
||||||
|
final requestData = <String, dynamic>{
|
||||||
|
'CORPINFO_ID': form['CORPINFO_ID'],
|
||||||
|
...form,
|
||||||
|
};
|
||||||
|
final res = await ApiService.safeKeyprojectCheckSubmit(requestData);
|
||||||
|
// 如果你的 ApiService 返回结构不同,请按实际调整判断
|
||||||
|
if (res != null && res['result'] == 'success') {
|
||||||
|
final pd = res['pd'] ?? {};
|
||||||
|
final List<dynamic> returnedHiddenList = pd['hiddenList'] ?? [];
|
||||||
|
|
||||||
|
// 如果没有附件需要上传,直接完成
|
||||||
|
final hasFiles = hiddenFilesPerHidden.any((lst) => lst.isNotEmpty);
|
||||||
|
if (!hasFiles) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '提交成功');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 若每个 hidden 有 punishForm,需要先提交罚单
|
||||||
|
for (var i = 0; i < returnedHiddenList.length; i++) {
|
||||||
|
if (i < (form['hiddenList'] as List).length) {
|
||||||
|
final hidden = Map<String, dynamic>.from(
|
||||||
|
(form['hiddenList'] as List)[i],
|
||||||
|
);
|
||||||
|
final punishForm = hidden['punishForm'];
|
||||||
|
if (punishForm != null) {
|
||||||
|
final hid = (returnedHiddenList[i]['HIDDEN_ID'] ?? '').toString();
|
||||||
|
punishForm['HIDDEN_ID'] = hid;
|
||||||
|
// await 调用罚单提交(在下面实现)
|
||||||
|
await fnSubmit(Map<String, dynamic>.from(punishForm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传所有附件(按隐患对应的 hiddenId)
|
||||||
|
// 把返回的 hiddenList 转为 Map 列表,确保索引一致
|
||||||
|
final returnedHiddenMapList =
|
||||||
|
returnedHiddenList
|
||||||
|
.map((e) => Map<String, dynamic>.from(e))
|
||||||
|
.toList();
|
||||||
|
await uploadHiddenFiles(hiddenFilesPerHidden, returnedHiddenMapList);
|
||||||
|
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '提交成功');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} else {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
final msg =
|
||||||
|
res != null
|
||||||
|
? (res['msg'] ?? res['msaesge'] ?? res['message'] ?? '提交失败')
|
||||||
|
: '提交失败';
|
||||||
|
ToastUtil.showNormal(context, msg);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
LoadingDialogHelper.hide();
|
||||||
|
ToastUtil.showNormal(context, '提交异常:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 上传附件的方法 ==========
|
||||||
|
Future<void> uploadHiddenFiles(
|
||||||
|
List<List<Map<String, dynamic>>> hiddenFilesPerHidden,
|
||||||
|
List<Map<String, dynamic>> returnedHiddenList,
|
||||||
|
) async {
|
||||||
|
for (var i = 0; i < hiddenFilesPerHidden.length; i++) {
|
||||||
|
final filesForHidden = hiddenFilesPerHidden[i];
|
||||||
|
if (filesForHidden.isEmpty) continue;
|
||||||
|
final hiddenId =
|
||||||
|
i < returnedHiddenList.length
|
||||||
|
? (returnedHiddenList[i]['HIDDEN_ID']?.toString() ?? '')
|
||||||
|
: '';
|
||||||
|
if (hiddenId.isEmpty) continue;
|
||||||
|
|
||||||
|
for (final f in filesForHidden) {
|
||||||
|
final filePath = f['FILEPATH']?.toString() ?? '';
|
||||||
|
final type = f['type']?.toString() ?? '';
|
||||||
|
if (filePath.isEmpty) continue;
|
||||||
|
try {
|
||||||
|
await ApiService.addImgFiles(filePath, type, hiddenId);
|
||||||
|
} catch (e) {
|
||||||
|
// 你可以记录失败项或重试,这里先忽略单文件错误
|
||||||
|
print('上传文件失败: $e (path=$filePath)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 罚单提交方法(对应原 fnSubmit) ==========
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _personUnitItem(Map item, int index) {
|
||||||
|
return DottedBorderBox(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
isRequired: widget.isEdit,
|
||||||
|
label: '${index + 1}.检查人员单位:',
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
onTapClean: () {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
text: item['INSPECTION_DEPARTMENT_NAME'] ?? '请选择',
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
isRequired: widget.isEdit,
|
||||||
|
label: 'type.personName',
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
text: item['INSPECTION_USER_NAME'] ?? '请选择',
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
? ListView(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
horizontal: 0,
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ListItemFactory.createYesNoSection(
|
||||||
|
title: '检查题目:',
|
||||||
|
groupValue: chooseTitleType,
|
||||||
|
yesLabel: '安全',
|
||||||
|
noLabel: '综合',
|
||||||
|
isEdit: widget.isEdit,
|
||||||
|
isRequired: true,
|
||||||
|
horizontalPadding: 5,
|
||||||
|
verticalPadding: 0,
|
||||||
|
text:
|
||||||
|
'${form['INSPECTION_SUBJECT'] ?? ''}现场检查记录',
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
chooseTitleType = val;
|
||||||
|
form['INSPECTION_SUBJECT'] =
|
||||||
|
val == true ? '安全' : '综合';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '被检查单位:',
|
||||||
|
isEditable: false,
|
||||||
|
text: form['INSPECTED_DEPARTMENT_NAMES'],
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
form['INSPECTED_DEPARTMENT_NAME'] = val;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '被检查单位现场负责人:',
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
onTap: () {
|
||||||
|
_choosePerson();
|
||||||
|
},
|
||||||
|
text: form['INSPECTED_SITEUSER_INDEX'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '检查场所:',
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
text: form['INSPECTION_PLACE'],
|
||||||
|
hintText: '请输入检查场所',
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
form['INSPECTION_PLACE'] = val;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '检查类型:',
|
||||||
|
onTap: () {
|
||||||
|
_chooseType();
|
||||||
|
},
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
text: form['INSPECTION_TYPE_NAME'] ?? '',
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '检查开始时间:',
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
text: form['INSPECTION_TIME_START'] ?? '',
|
||||||
|
onTap: () async {
|
||||||
|
DateTime? picked =
|
||||||
|
await BottomDateTimePicker.showDate(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
if (picked != null) {
|
||||||
|
setState(() {
|
||||||
|
form['INSPECTION_TIME_START'] =
|
||||||
|
DateFormat(
|
||||||
|
'yyyy-MM-dd HH:mm',
|
||||||
|
).format(picked);
|
||||||
|
});
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
ItemListWidget.selectableLineTitleTextRightButton(
|
||||||
|
label: '检查结束时间:',
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
text: form['INSPECTION_TIME_END'] ?? '',
|
||||||
|
onTap: () async {
|
||||||
|
DateTime? picked =
|
||||||
|
await BottomDateTimePicker.showDate(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
if (picked != null) {
|
||||||
|
setState(() {
|
||||||
|
form['INSPECTION_TIME_END'] =
|
||||||
|
DateFormat(
|
||||||
|
'yyyy-MM-dd HH:mm',
|
||||||
|
).format(picked);
|
||||||
|
});
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
MultiTextFieldWithTitle(
|
||||||
|
label: "检查情况",
|
||||||
|
// 更合适的标题
|
||||||
|
isEditable: widget.isEdit,
|
||||||
|
// 使用父组件的编辑状态
|
||||||
|
hintText: "请输入检查情况...",
|
||||||
|
texts: multiTexts,
|
||||||
|
onTextsChanged: (texts) {
|
||||||
|
setState(() {
|
||||||
|
multiTexts = texts; // 保存到状态变量
|
||||||
|
form['situationList'] =
|
||||||
|
_stringsToSituationList(texts);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.itemContainer(
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ListItemFactory.headerTitle('检查人员'),
|
||||||
|
Expanded(child: 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('发现问题'),
|
||||||
|
if (widget.isEdit)
|
||||||
|
CustomButton(
|
||||||
|
text: " 添加 ",
|
||||||
|
height: 30,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 2,
|
||||||
|
horizontal: 5,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: () {
|
||||||
|
_openDrawer(
|
||||||
|
form,
|
||||||
|
-1,
|
||||||
|
); // 添加括号和 await
|
||||||
|
FocusHelper.clearFocus(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
HiddenListTable(
|
||||||
|
hiddenList: form['hiddenList'] ?? [],
|
||||||
|
forbidEdit: widget.isEdit,
|
||||||
|
baseImgPath: ApiService.baseImgPath,
|
||||||
|
personSignImg: form['PERSON_SIGN_IMG'] ?? '',
|
||||||
|
personSignTime:
|
||||||
|
form['PERSON_SIGN_TIME'] ?? '',
|
||||||
|
showHidden: (item, idx) {
|
||||||
|
_openDrawer(item, idx);
|
||||||
|
},
|
||||||
|
removeHidden: (item, idx) {
|
||||||
|
/* 删除逻辑 */
|
||||||
|
},
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
|
||||||
|
if (!widget.isEdit)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.twoRowTitleAndImages(
|
||||||
|
title: '签字',
|
||||||
|
onTapCallBack: (p) {
|
||||||
|
presentOpaque(
|
||||||
|
SingleImageViewer(imageUrl: p),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
imageUrls: [
|
||||||
|
'${form['PERSON_SIGN_IMG'] ?? ''}',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ItemListWidget.singleLineTitleText(
|
||||||
|
label: '签字时间',
|
||||||
|
isEditable: false,
|
||||||
|
text: form['PERSON_SIGN_TIME'] ?? '',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (widget.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: widget.isEdit ? '提交' : '返回',
|
||||||
|
textStyle: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 17,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
onPressed: _submit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,481 @@
|
||||||
|
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¤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 _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,
|
||||||
|
required int status, // 1 = 完成, 0 = 进行中, -1 = 未到达
|
||||||
|
}) {
|
||||||
|
// 依据状态设色
|
||||||
|
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),
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 根据 lastDoneIndex 自动计算“进行中”
|
||||||
|
final int status = item['active'] == '1' ? 1 : -1;
|
||||||
|
|
||||||
|
return _buildFlowStepItem(
|
||||||
|
item: item,
|
||||||
|
isFirst: isFirst,
|
||||||
|
isLast: isLast,
|
||||||
|
status: status,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
body: 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()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ 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/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';
|
||||||
import 'package:qhd_prevention/tools/tools.dart';
|
import 'package:qhd_prevention/tools/tools.dart';
|
||||||
|
@ -52,31 +53,38 @@ class _SafecheckTabListState extends State<SafecheckTabList> {
|
||||||
_getData();
|
_getData();
|
||||||
}
|
}
|
||||||
Future<void> _getData() async {
|
Future<void> _getData() async {
|
||||||
LoadingDialogHelper.show();
|
final data = await ApiService.getSafeCheckCount();
|
||||||
final data = await ApiService.getKeyProjectCount();
|
|
||||||
LoadingDialogHelper.hide();
|
|
||||||
setState(() {
|
setState(() {
|
||||||
final eight_work_count = data['pd'] ?? {};
|
final checkedCount = data['checkedCount']['checkedCount'] ?? '';
|
||||||
|
final repulseAndCheckCount = data['repulseAndCheckCount']['repulseAndCheckCount'] ?? '';
|
||||||
|
final confirmCount = data['confirmCount']['confirmCount'] ?? '';
|
||||||
|
final repulseCount = data['repulseCount']['repulseCount'] ?? '';
|
||||||
|
|
||||||
buttonInfos = [
|
buttonInfos = [
|
||||||
{
|
{
|
||||||
"icon": "assets/icon-apps/icon-yxkj-1.png",
|
"icon": "assets/icon-apps/icon-yxkj-1.png",
|
||||||
"title": "重点工程管理",
|
"title": "安全检查\n发起",
|
||||||
"unreadCount": eight_work_count['GC_COUNT'] ?? '0',
|
"unreadCount": repulseCount,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": "assets/icon-apps/icon-yxkj-1.png",
|
"icon": "assets/icon-apps/icon-yxkj-4.png",
|
||||||
"title": "安全检查管理",
|
"title": "检查人\n确认",
|
||||||
"unreadCount": '0',
|
"unreadCount": confirmCount,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": "assets/icon-apps/icon-yxkj-1.png",
|
"icon": "assets/icon-apps/icon-yxkj-2.png",
|
||||||
"title": "隐患管理",
|
"title": "被检查人\n签字/申辩",
|
||||||
"unreadCount": eight_work_count['CF_COUNT'] ?? '0',
|
"unreadCount":checkedCount,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": "assets/icon-apps/icon-yxkj-1.png",
|
"icon": "assets/icon-apps/icon-yxkj-2.png",
|
||||||
"title": "处罚管理",
|
"title": "隐患指派\n及验收",
|
||||||
"unreadCount": eight_work_count['HIDDEN_COUNT'] ?? '0',
|
"unreadCount":repulseAndCheckCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "assets/icon-apps/icon-yxkj-2.png",
|
||||||
|
"title": "申辩记录",
|
||||||
|
"unreadCount":'0',
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -88,7 +96,7 @@ class _SafecheckTabListState extends State<SafecheckTabList> {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0: {
|
case 0: {
|
||||||
title = '安全检查发起';
|
title = '安全检查发起';
|
||||||
// await pushPage(KeyprojectListPage(flow: title), context);
|
await pushPage(SafecheckStartListPage(flow: title), context);
|
||||||
} break;
|
} break;
|
||||||
case 1: {
|
case 1: {
|
||||||
title = '安全检查核实';
|
title = '安全检查核实';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:dotted_line/dotted_line.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:dotted_line/dotted_line.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_list.dart';
|
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_list.dart';
|
||||||
import 'package:dotted_line/dotted_line.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||||
|
|
|
@ -177,14 +177,14 @@ packages:
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
dotted_line:
|
dotted_border:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dotted_line
|
name: dotted_border
|
||||||
sha256: "41e3d655939559815daa1370fc1e07673a205fa628cf40ce3af45d90029a77b6"
|
sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c"
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.3"
|
version: "3.1.0"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -73,8 +73,8 @@ dependencies:
|
||||||
geolocator: ^10.0.0
|
geolocator: ^10.0.0
|
||||||
#打开外部预览app
|
#打开外部预览app
|
||||||
url_launcher: ^6.0.9
|
url_launcher: ^6.0.9
|
||||||
#虚线
|
#虚线框
|
||||||
dotted_line: ^3.2.3
|
dotted_border: ^3.1.0
|
||||||
#未读角标
|
#未读角标
|
||||||
flutter_new_badger: ^1.1.1
|
flutter_new_badger: ^1.1.1
|
||||||
#loading
|
#loading
|
||||||
|
|
Loading…
Reference in New Issue