Merge branch 'main' of http://47.92.113.182:3000/hanshuang/flutter_integrated_whb
commit
e7d38f12aa
|
|
@ -407,8 +407,10 @@ class ListItemFactory {
|
|||
}
|
||||
|
||||
/// 单纯标题
|
||||
static Widget headerTitle(String title, {bool isRequired = false}) {
|
||||
static Widget headerTitle(String title, {bool isRequired = false, double verticalPadding = 0,
|
||||
double horizontalPadding = 0,}) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: verticalPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,263 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
|
||||
/// 居中多选弹窗(Dialog)
|
||||
/// 返回 Future<List<T>?>:用户点击确定返回所选项列表;取消或关闭返回 null。
|
||||
class CenterMultiPicker {
|
||||
static Future<List<T>?> show<T>(
|
||||
BuildContext context, {
|
||||
required List<T> items,
|
||||
required Widget Function(T item) itemBuilder,
|
||||
List<int>? initialSelectedIndices,
|
||||
int? maxSelection,
|
||||
bool allowEmpty = false,
|
||||
double itemHeight = 52,
|
||||
double maxHeightFactor = 0.75, // 屏幕高度的最大占比
|
||||
String? title,
|
||||
}) {
|
||||
if (items.isEmpty) return Future.value(null);
|
||||
|
||||
// 安全化初始索引
|
||||
final initialSet = <int>{};
|
||||
if (initialSelectedIndices != null) {
|
||||
for (final i in initialSelectedIndices) {
|
||||
// if (i >= 0 && i < items.length) initialSet.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return showDialog<List<T>?>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (ctx) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
insetPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 24,
|
||||
),
|
||||
child: _CenterMultiPickerBody<T>(
|
||||
items: items,
|
||||
itemBuilder: itemBuilder,
|
||||
initialSelected: initialSet,
|
||||
maxSelection: maxSelection,
|
||||
allowEmpty: allowEmpty,
|
||||
itemHeight: itemHeight,
|
||||
maxHeightFactor: maxHeightFactor,
|
||||
title: title,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CenterMultiPickerBody<T> extends StatefulWidget {
|
||||
const _CenterMultiPickerBody({
|
||||
Key? key,
|
||||
required this.items,
|
||||
required this.itemBuilder,
|
||||
required this.initialSelected,
|
||||
required this.maxSelection,
|
||||
required this.allowEmpty,
|
||||
required this.itemHeight,
|
||||
required this.maxHeightFactor,
|
||||
this.title,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<T> items;
|
||||
final Widget Function(T item) itemBuilder;
|
||||
final Set<int> initialSelected;
|
||||
final int? maxSelection;
|
||||
final bool allowEmpty;
|
||||
final double itemHeight;
|
||||
final double maxHeightFactor;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
State<_CenterMultiPickerBody<T>> createState() =>
|
||||
_CenterMultiPickerBodyState<T>();
|
||||
}
|
||||
|
||||
class _CenterMultiPickerBodyState<T> extends State<_CenterMultiPickerBody<T>> {
|
||||
late Set<int> _selected;
|
||||
|
||||
// 固定的 header / footer 高度估算
|
||||
static const double _headerHeight = 56;
|
||||
static const double _footerHeight = 58;
|
||||
static const double _verticalPadding = 16; // Dialog 内上下 padding
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selected = Set<int>.from(widget.initialSelected);
|
||||
}
|
||||
|
||||
void _toggle(int idx) {
|
||||
setState(() {
|
||||
if (_selected.contains(idx)) {
|
||||
_selected.remove(idx);
|
||||
} else {
|
||||
if (widget.maxSelection != null &&
|
||||
_selected.length >= widget.maxSelection!) {
|
||||
ToastUtil.showNormal(context, '最多可选择 ${widget.maxSelection} 项');
|
||||
return;
|
||||
}
|
||||
_selected.add(idx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final items = widget.items;
|
||||
final screenH = MediaQuery.of(context).size.height;
|
||||
final contentHeight =
|
||||
items.length * widget.itemHeight +
|
||||
_headerHeight +
|
||||
_footerHeight +
|
||||
_verticalPadding * 2;
|
||||
final maxAllowed = screenH * widget.maxHeightFactor;
|
||||
final dialogHeight =
|
||||
contentHeight <= maxAllowed ? contentHeight : maxAllowed;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10)
|
||||
),
|
||||
width: double.infinity,
|
||||
height: dialogHeight,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// header
|
||||
Container(
|
||||
height: _headerHeight,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.title != null)
|
||||
Expanded(
|
||||
child: Text(
|
||||
widget.title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: Text(
|
||||
'已选 ${_selected.length}${widget.maxSelection != null ? '/${widget.maxSelection}' : ''}',
|
||||
style: const TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
// 可将一些快捷按钮放在右侧(如全选/反选),这里暂不显示
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
// 列表区域(可滚动)
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
child: ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (ctx, idx) {
|
||||
final isSelected = _selected.contains(idx);
|
||||
return InkWell(
|
||||
onTap: () => _toggle(idx),
|
||||
child: Container(
|
||||
height: widget.itemHeight,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 22,
|
||||
height: 22,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isSelected
|
||||
? Colors.blue
|
||||
: Colors.transparent,
|
||||
border: Border.all(
|
||||
color:
|
||||
isSelected ? Colors.blue : Colors.black26,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child:
|
||||
isSelected
|
||||
? const Icon(
|
||||
Icons.check,
|
||||
size: 18,
|
||||
color: Colors.white,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: widget.itemBuilder(items[idx])),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
// footer: 取消 / 确定(固定在底部)
|
||||
Container(
|
||||
height: _footerHeight,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
text: '取消',
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
textStyle: TextStyle(fontSize: 14, color: Colors.black),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(null);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
text: '确定',
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: () {
|
||||
if (!widget.allowEmpty && _selected.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请至少选择一项');
|
||||
return;
|
||||
}
|
||||
final result = _selected
|
||||
.map((i) => items[i])
|
||||
.toList(growable: false);
|
||||
Navigator.of(context).pop(result);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,12 +20,12 @@ class ApiService {
|
|||
// static const String publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3pM3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OKU6Hzm1ninpWeE+awIDAQAB'
|
||||
/// 人脸识别服务
|
||||
// static const String baseFacePath = "https://qaaqwh.qhdsafety.com/whb_stu_face";
|
||||
static const String baseFacePath =
|
||||
"http://192.168.20.240:8500/whb_stu_face/";
|
||||
static const String baseFacePath = "http://192.168.20.240:8500/whb_stu_face/";
|
||||
/// 登录及其他管理后台接口
|
||||
// static const String basePath = "http://192.168.20.240:8500/integrated_whb";//测试服务器
|
||||
// static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
|
||||
static const String basePath = "http://192.168.20.240:8500/integrated_whb";
|
||||
// static const String basePath = "http://192.168.0.25:28199";//王轩服务器
|
||||
// static const String basePath = "http://192.168.0.45:28199";//长久服务器
|
||||
|
||||
/// 图片文件服务
|
||||
|
|
@ -171,6 +171,19 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
/// 安全检查隐患验收提交
|
||||
static Future<Map<String, dynamic>> safeCheckDangerAcceptanceSubmit(Map data) {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/hidden/finalcheck',
|
||||
method: Method.post,
|
||||
data: {
|
||||
...data,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Future<Map<String, dynamic>> getUpdateInfo() {
|
||||
return HttpManager().request(
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
child: _section(
|
||||
ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: '隐患级别',
|
||||
isRequired: true,
|
||||
rightText:
|
||||
hiddenForm['HIDDENLEVEL_NAME']?.toString() ??
|
||||
'请选择',
|
||||
|
|
@ -270,6 +271,7 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
child: _section(
|
||||
ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: '隐患类型',
|
||||
isRequired: true,
|
||||
rightText:
|
||||
hiddenForm['HIDDENTYPE_NAME']?.toString() ??
|
||||
'请选择',
|
||||
|
|
@ -325,27 +327,11 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
),
|
||||
),
|
||||
] else ...[
|
||||
// limited rectify: department, person, date
|
||||
// GestureDetector(
|
||||
// onTap: _isEdit ? _pickDept : null,
|
||||
// child: _section(
|
||||
// ListItemFactory.createRowSpaceBetweenItem(
|
||||
// leftText: '整改部门',
|
||||
// rightText:
|
||||
// _isEdit
|
||||
// ? (hiddenForm['RECTIFICATIONDEPT_NAME']
|
||||
// ?.toString() ??
|
||||
// '请选择')
|
||||
// : hiddenForm['RECTIFICATIONDEPT_NAME']
|
||||
// ?.toString() ??
|
||||
// '',
|
||||
// isRight: _isEdit,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
ItemListWidget.itemContainer(horizontal: 15,ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: '整改部门',
|
||||
isRequired: true,
|
||||
rightText:hiddenForm['RECTIFICATIONDEPT_NAME'] ?? '',
|
||||
isRight: false,
|
||||
),),
|
||||
|
|
@ -360,6 +346,7 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
),
|
||||
child: ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: '整改人',
|
||||
isRequired: true,
|
||||
rightText:
|
||||
hiddenForm['RECTIFICATIONOR_NAME']
|
||||
?.toString() ??
|
||||
|
|
@ -373,6 +360,7 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
child: _section(
|
||||
ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: '整改期限',
|
||||
isRequired: true,
|
||||
rightText:
|
||||
hiddenForm['RECTIFICATIONDEADLINE']
|
||||
?.toString() ??
|
||||
|
|
@ -510,8 +498,10 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
'1',
|
||||
);
|
||||
setState(
|
||||
() =>
|
||||
_personCache = List<Map<String, dynamic>>.from(res['varList'] ?? []),
|
||||
() {
|
||||
_personCache = List<Map<String, dynamic>>.from(res['varList'] ?? []);
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -528,11 +518,12 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
// 用户点击确定并选择了 choice
|
||||
setState(() {
|
||||
hiddenForm['RECTIFICATIONOR_NAME'] = choice;
|
||||
Map<String, dynamic>? target = widget.toCheckUnitList.firstWhere(
|
||||
(item) => item['RECTIFICATIONOR_NAME'] == choice,
|
||||
Map<String, dynamic> target = _personCache.firstWhere(
|
||||
(item) => item['NAME'] == choice,
|
||||
orElse: () => {},
|
||||
);
|
||||
hiddenForm['RECTIFICATIONOR'] = target?['PERSONNELMANAGEMENT_ID'];
|
||||
hiddenForm['RECTIFICATIONOR'] = target['PERSONNELMANAGEMENT_ID'] ?? '';
|
||||
hiddenForm['RECTIFICATIONOR_INDEX'] = _personCache.indexOf(target);
|
||||
//FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,91 +1,179 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_new_badger/flutter_new_badger.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 单例角标管理,多接口分模块更新及全局原生角标同步
|
||||
/// 优化版 BadgeManager:每个接口超时保护、增量更新、notify 合并、原生角标 debounce
|
||||
class BadgeManager extends ChangeNotifier {
|
||||
BadgeManager._internal();
|
||||
|
||||
static final BadgeManager _instance = BadgeManager._internal();
|
||||
|
||||
factory BadgeManager() => _instance;
|
||||
|
||||
// 各模块未读
|
||||
int _appCount = 0;
|
||||
int _appDysCount = 0; // 新增:待验收
|
||||
int _appDzgCount = 0; // 新增:待整改
|
||||
int _appDysCount = 0;
|
||||
int _appDzgCount = 0;
|
||||
int _notifCount = 0;
|
||||
int _envInspectCount = 0;
|
||||
int _eightWorkCount = 0;
|
||||
|
||||
/// 总未读角标数
|
||||
// 读取接口值的公开 getter
|
||||
int get count => _appCount + _notifCount + _envInspectCount + _eightWorkCount;
|
||||
|
||||
/// MainPage 整体“应用”角标
|
||||
int get appCount => _appCount;
|
||||
|
||||
/// MainPage “待验收”子项
|
||||
int get appDysCount => _appDysCount;
|
||||
|
||||
/// MainPage “待整改”子项
|
||||
int get appDzgCount => _appDzgCount;
|
||||
|
||||
int get notifCount => _notifCount;
|
||||
|
||||
int get envInspectCount => _envInspectCount;
|
||||
|
||||
int get eightWorkCount => _eightWorkCount;
|
||||
|
||||
/// 登录后或有用户上下文时,初始化所有接口汇总数,并同步原生角标
|
||||
Future<void> initAllModules() async {
|
||||
// 通知合并控制(短时间内多次更新只触发一次 notify)
|
||||
Timer? _notifyTimer;
|
||||
Duration _notifyDelay = const Duration(milliseconds: 180);
|
||||
void _scheduleNotify() {
|
||||
// 如果已有计划,不需要再立即计划(合并)
|
||||
_notifyTimer?.cancel();
|
||||
_notifyTimer = Timer(_notifyDelay, () {
|
||||
try {
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('BadgeManager.notifyListeners error: $e');
|
||||
}
|
||||
_notifyTimer = null;
|
||||
});
|
||||
}
|
||||
|
||||
// 原生角标同步 debounce(防止短时间内频繁调用)
|
||||
Timer? _syncTimer;
|
||||
Duration _syncDelay = const Duration(milliseconds: 250);
|
||||
void _syncNativeDebounced() {
|
||||
_syncTimer?.cancel();
|
||||
_syncTimer = Timer(_syncDelay, () {
|
||||
try {
|
||||
final total = count;
|
||||
if (total > 0) {
|
||||
FlutterNewBadger.setBadge(total);
|
||||
} else {
|
||||
FlutterNewBadger.removeBadge();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('BadgeManager._syncNativeDebounced error: $e');
|
||||
}
|
||||
_syncTimer = null;
|
||||
});
|
||||
}
|
||||
|
||||
// safe wrapper: 给单个 future 加超时与兜底返回
|
||||
Future<T> _safe<T>(Future<T> future, T fallback, {Duration timeout = const Duration(seconds: 5)}) async {
|
||||
try {
|
||||
final results = await Future.wait([
|
||||
ApiService.getWork(), // 应用中心
|
||||
ApiService.getNotifRedPoint(), // 通知公告
|
||||
ApiService.getSafetyEnvironmentalInspectionCount(), // 安全巡检
|
||||
ApiService.getRedPoint(), // 八项作业
|
||||
]);
|
||||
|
||||
// 应用中心部分
|
||||
final workJson = results[0];
|
||||
final hid = workJson['hidCount'] as Map<String, dynamic>? ?? {};
|
||||
_appDysCount = (hid['dys'] ?? 0) as int;
|
||||
_appDzgCount = (hid['dzg'] ?? 0) as int;
|
||||
_appCount = _appDysCount + _appDzgCount;
|
||||
|
||||
// 通知公告部分
|
||||
final notifJson = results[1];
|
||||
_notifCount = notifJson['count'] as int? ?? 0;
|
||||
|
||||
// 安全巡检部分
|
||||
final checkJson = results[2];
|
||||
_envInspectCount =
|
||||
checkJson['repulseCount']?['repulseCount'] ?? 0;
|
||||
|
||||
// 八项作业部分
|
||||
final redPointJson = results[3];
|
||||
_eightWorkCount = 0;
|
||||
(redPointJson['count'] as Map<String, dynamic>? ?? {}).values.forEach((
|
||||
v,
|
||||
) {
|
||||
_eightWorkCount += (v ?? 0) as int;
|
||||
});
|
||||
|
||||
_syncNative();
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('BadgeManager.initAllModules error: $e');
|
||||
return await future.timeout(timeout);
|
||||
} catch (e, st) {
|
||||
// 报错不抛出到外层,记录并返回 fallback
|
||||
debugPrint('BadgeManager._safe error: $e\n$st');
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新 MainPage 的应用中心角标
|
||||
/// 初始化所有模块(并行发起,但每个接口独立 timeout & 提交结果)
|
||||
Future<void> initAllModules() async {
|
||||
// 不 await 整个 Future.wait,使调用方不会因为单个慢接口阻塞
|
||||
try {
|
||||
// 每个请求都通过 _safe 包裹,设置超时与兜底
|
||||
final fWork = _safe<Map<String, dynamic>>(
|
||||
ApiService.getWork().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 6),
|
||||
);
|
||||
final fNotif = _safe<Map<String, dynamic>>(
|
||||
ApiService.getNotifRedPoint().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 4),
|
||||
);
|
||||
final fCheck = _safe<Map<String, dynamic>>(
|
||||
ApiService.getSafetyEnvironmentalInspectionCount().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
final fRed = _safe<Map<String, dynamic>>(
|
||||
ApiService.getRedPoint().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
// 按接口独立处理:完成即更新并合并通知(progressive update)
|
||||
fWork.then((workJson) {
|
||||
try {
|
||||
final hid = (workJson['hidCount'] as Map<String, dynamic>?) ?? {};
|
||||
_appDysCount = (hid['dys'] ?? 0) as int? ?? 0;
|
||||
_appDzgCount = (hid['dzg'] ?? 0) as int? ?? 0;
|
||||
_appCount = _appDysCount + _appDzgCount;
|
||||
} catch (e, st) {
|
||||
debugPrint('BadgeManager.parse workJson error: $e\n$st');
|
||||
}
|
||||
_scheduleNotify();
|
||||
_syncNativeDebounced();
|
||||
});
|
||||
|
||||
fNotif.then((notifJson) {
|
||||
try {
|
||||
_notifCount = (notifJson['count'] as int?) ?? 0;
|
||||
} catch (e, st) {
|
||||
debugPrint('BadgeManager.parse notifJson error: $e\n$st');
|
||||
}
|
||||
_scheduleNotify();
|
||||
_syncNativeDebounced();
|
||||
});
|
||||
|
||||
fCheck.then((checkJson) {
|
||||
try {
|
||||
// 兼容不同返回结构,防止空指针
|
||||
int checkedCount = 0, repulseAndCheckCount = 0, confirmCount = 0, repulseCount = 0;
|
||||
if (checkJson['checkedCount'] is Map) checkedCount = (checkJson['checkedCount']['checkedCount'] ?? 0) as int? ?? 0;
|
||||
if (checkJson['repulseAndCheckCount'] is Map) repulseAndCheckCount = (checkJson['repulseAndCheckCount']['repulseAndCheckCount'] ?? 0) as int? ?? 0;
|
||||
if (checkJson['confirmCount'] is Map) confirmCount = (checkJson['confirmCount']['confirmCount'] ?? 0) as int? ?? 0;
|
||||
if (checkJson['repulseCount'] is Map) repulseCount = (checkJson['repulseCount']['repulseCount'] ?? 0) as int? ?? 0;
|
||||
_envInspectCount = checkedCount + repulseAndCheckCount + confirmCount + repulseCount;
|
||||
} catch (e, st) {
|
||||
debugPrint('BadgeManager.parse checkJson error: $e\n$st');
|
||||
}
|
||||
_scheduleNotify();
|
||||
_syncNativeDebounced();
|
||||
});
|
||||
|
||||
fRed.then((redPointJson) {
|
||||
try {
|
||||
_eightWorkCount = 0;
|
||||
final m = (redPointJson['count'] as Map<String, dynamic>?) ?? {};
|
||||
for (final v in m.values) {
|
||||
_eightWorkCount += (v ?? 0) as int? ?? 0;
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint('BadgeManager.parse redPointJson error: $e\n$st');
|
||||
}
|
||||
_scheduleNotify();
|
||||
_syncNativeDebounced();
|
||||
});
|
||||
|
||||
// 可选:等待所有结束(不阻塞调用方),用于 debug 或需要最终一致性的场景
|
||||
unawaited(Future.wait([fWork, fNotif, fCheck, fRed]).then((_) {
|
||||
debugPrint('BadgeManager.initAllModules: all done at ${DateTime.now()}');
|
||||
}));
|
||||
} catch (e, st) {
|
||||
debugPrint('BadgeManager.initAllModules unexpected error: $e\n$st');
|
||||
}
|
||||
}
|
||||
|
||||
// 下面的 updateX 方法也做了超时保护并使用 _onModuleChanged 合并通知
|
||||
void updateAppCount() async {
|
||||
try {
|
||||
final workJson = await ApiService.getWork();
|
||||
final hid = workJson['hidCount'] as Map<String, dynamic>? ?? {};
|
||||
_appDysCount = (hid['dys'] ?? 0) as int;
|
||||
_appDzgCount = (hid['dzg'] ?? 0) as int;
|
||||
final workJson = await _safe<Map<String, dynamic>>(
|
||||
ApiService.getWork().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
final hid = (workJson['hidCount'] as Map<String, dynamic>?) ?? {};
|
||||
_appDysCount = (hid['dys'] ?? 0) as int? ?? 0;
|
||||
_appDzgCount = (hid['dzg'] ?? 0) as int? ?? 0;
|
||||
_appCount = _appDysCount + _appDzgCount;
|
||||
_onModuleChanged();
|
||||
} catch (e) {
|
||||
|
|
@ -93,43 +181,49 @@ class BadgeManager extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
/// 更新 MainPage 的通知公告角标
|
||||
void updateNotifCount() async {
|
||||
try {
|
||||
final notifJson = await ApiService.getNotifRedPoint();
|
||||
_notifCount = notifJson['count'] as int? ?? 0;
|
||||
final notifJson = await _safe<Map<String, dynamic>>(
|
||||
ApiService.getNotifRedPoint().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 4),
|
||||
);
|
||||
_notifCount = (notifJson['count'] as int?) ?? 0;
|
||||
_onModuleChanged();
|
||||
} catch (e) {
|
||||
debugPrint('updateNotifCount error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新 HomePage 安全巡检角标
|
||||
void updateEnvInspectCount() async {
|
||||
try {
|
||||
final checkJson =
|
||||
await ApiService.getSafetyEnvironmentalInspectionCount();
|
||||
_envInspectCount =
|
||||
(checkJson['confirmCount']?['confirmCount'] ?? 0)(
|
||||
checkJson['repulseCount']?['repulseCount'] ?? 0,
|
||||
)(checkJson['repulseAndCheckCount']?['repulseAndCheckCount'] ?? 0)
|
||||
as int;
|
||||
final checkJson = await _safe<Map<String, dynamic>>(
|
||||
ApiService.getSafetyEnvironmentalInspectionCount().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
int checkedCount = 0, repulseAndCheckCount = 0, confirmCount = 0, repulseCount = 0;
|
||||
if (checkJson['checkedCount'] is Map) checkedCount = (checkJson['checkedCount']['checkedCount'] ?? 0) as int? ?? 0;
|
||||
if (checkJson['repulseAndCheckCount'] is Map) repulseAndCheckCount = (checkJson['repulseAndCheckCount']['repulseAndCheckCount'] ?? 0) as int? ?? 0;
|
||||
if (checkJson['confirmCount'] is Map) confirmCount = (checkJson['confirmCount']['confirmCount'] ?? 0) as int? ?? 0;
|
||||
if (checkJson['repulseCount'] is Map) repulseCount = (checkJson['repulseCount']['repulseCount'] ?? 0) as int? ?? 0;
|
||||
_envInspectCount = checkedCount + repulseAndCheckCount + confirmCount + repulseCount;
|
||||
_onModuleChanged();
|
||||
} catch (e) {
|
||||
debugPrint('updateEnvInspectCount error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新 HomePage 八项作业角标
|
||||
void updateEightWorkCount() async {
|
||||
try {
|
||||
final redPointJson = await ApiService.getRedPoint();
|
||||
final redPointJson = await _safe<Map<String, dynamic>>(
|
||||
ApiService.getRedPoint().then((r) => r as Map<String, dynamic>),
|
||||
<String, dynamic>{},
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
int sum = 0;
|
||||
(redPointJson['count'] as Map<String, dynamic>? ?? {}).values.forEach((
|
||||
v,
|
||||
) {
|
||||
sum += (v ?? 0) as int;
|
||||
});
|
||||
final m = (redPointJson['count'] as Map<String, dynamic>?) ?? {};
|
||||
for (final v in m.values) sum += (v ?? 0) as int? ?? 0;
|
||||
_eightWorkCount = sum;
|
||||
_onModuleChanged();
|
||||
} catch (e) {
|
||||
|
|
@ -137,25 +231,15 @@ class BadgeManager extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
/// 清空所有角标
|
||||
void clearAll() {
|
||||
_appCount = _notifCount = _envInspectCount = _eightWorkCount = 0;
|
||||
_appDysCount = _appDzgCount = 0;
|
||||
_syncNative();
|
||||
notifyListeners();
|
||||
_syncNativeDebounced();
|
||||
_scheduleNotify();
|
||||
}
|
||||
|
||||
void _onModuleChanged() {
|
||||
_syncNative();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _syncNative() {
|
||||
final total = count;
|
||||
if (total > 0) {
|
||||
FlutterNewBadger.setBadge(total);
|
||||
} else {
|
||||
FlutterNewBadger.removeBadge();
|
||||
}
|
||||
_syncNativeDebounced();
|
||||
_scheduleNotify();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -334,9 +334,9 @@ class HomePageState extends State<HomePage> {
|
|||
final data = await ApiService.getWork();
|
||||
final hidCount = data['hidCount'] as Map<String, dynamic>? ?? {};
|
||||
|
||||
// 告知BadgeManager去更新“安全巡检”和“八项作业”角标
|
||||
BadgeManager().updateEnvInspectCount();
|
||||
BadgeManager().updateEightWorkCount();
|
||||
// // 告知BadgeManager去更新“安全巡检”和“八项作业”角标
|
||||
// BadgeManager().updateEnvInspectCount();
|
||||
// BadgeManager().updateEightWorkCount();
|
||||
|
||||
// 拉取其他数据
|
||||
final results = await Future.wait([
|
||||
|
|
|
|||
|
|
@ -709,6 +709,7 @@ class ItemListWidget {
|
|||
|
||||
children: [
|
||||
// 标题部分
|
||||
if (title.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Text(
|
||||
|
|
@ -862,15 +863,95 @@ class ItemListWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
/// 多行垂直布局:
|
||||
/// 标题、图片、说明、签字信息
|
||||
static Widget mulColumnRowTitleAndImages({
|
||||
required String title, // 第一行标题
|
||||
required List<dynamic>? imageUrls,
|
||||
required String text, // 描述
|
||||
required List<dynamic>? signUrls, /// 签字
|
||||
required List<dynamic>? signTimes, /// 签字时间
|
||||
|
||||
static Widget itemContainer(Widget child, {double horizontal = horizontal_inset}) {
|
||||
double row2Height = 80, // 第二行高度
|
||||
double fontSize = 14, // 字体大小
|
||||
void Function(String)? onTapCallBack,
|
||||
bool isRequired = true,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
// 标题部分
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 图片横向滚动区域
|
||||
SizedBox(
|
||||
height: 80, // 图片区域固定高度
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: imageUrls?.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(right: 8), // 图片间距
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (onTapCallBack != null) onTapCallBack('${ApiService.baseImgPath}${imageUrls![index] ?? ''}');
|
||||
},
|
||||
child: Image.network(
|
||||
'${ApiService.baseImgPath}${imageUrls![index] ?? ''}',
|
||||
width: 80, // 图片宽度
|
||||
height: 80, // 图片高度
|
||||
fit: BoxFit.fill,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
color: Colors.grey[200],
|
||||
child: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
color: Colors.transparent,
|
||||
child: SizedBox(),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Row()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static Widget itemContainer(Widget child, {double horizontal = horizontal_inset, double vertical = vertical_inset}) {
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical_inset),
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/customWidget/date_picker_dialog.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||
|
||||
|
|
@ -25,7 +29,7 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
});
|
||||
|
||||
/// 接口返回的原始 Map 列表
|
||||
final List<Map<String, dynamic>> measuresList;
|
||||
final List<dynamic> measuresList;
|
||||
|
||||
/// 图片的基础路径
|
||||
final String baseImgPath;
|
||||
|
|
@ -61,9 +65,11 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
|
||||
// 或 .top / .bottom
|
||||
columnWidths: const {
|
||||
0: FlexColumnWidth(3),
|
||||
1: FixedColumnWidth(100),
|
||||
columnWidths: {
|
||||
0: FlexColumnWidth(10),
|
||||
1: FixedColumnWidth(210),
|
||||
2: FlexColumnWidth(20),
|
||||
if (!isAllowEdit) 3: FixedColumnWidth(60),
|
||||
},
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(color: Colors.grey.shade300),
|
||||
|
|
@ -73,32 +79,57 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
// 表头
|
||||
TableRow(
|
||||
decoration: BoxDecoration(color: Colors.grey.shade100),
|
||||
children: const [
|
||||
Padding(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(2),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'序号',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'主要安全措施',
|
||||
'安全措施',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'操作',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
isAllowEdit ? '操作' : '是否\n涉及',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isAllowEdit)
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'确认人',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 数据行
|
||||
for (var item in measuresList)
|
||||
TableRow(
|
||||
children: [
|
||||
// 第一列:措施 + 签名 + 问题答案
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Center(
|
||||
child: Text('${measuresList.indexOf(item) + 1}'),
|
||||
),
|
||||
),
|
||||
// 第二列:措施 + 签名 + 问题答案
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
|
|
@ -106,21 +137,34 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
children: [
|
||||
// 主要安全措施
|
||||
Text(item['PROTECTIVE_MEASURES'] as String? ?? ''),
|
||||
|
||||
// 签名图片 + 时间
|
||||
if (item.containsKey('SIGN_PATH') &&
|
||||
(item['SIGN_PATH'] as String).isNotEmpty)
|
||||
..._buildImageRows(
|
||||
context,
|
||||
(item['SIGN_PATH'] as String).split(','),
|
||||
item['SIGN_TIME'] as String? ?? '',
|
||||
),
|
||||
// // 签名图片
|
||||
// if (item.containsKey('SIGN_PATH') &&
|
||||
// (item['SIGN_PATH'] as String).isNotEmpty)
|
||||
// ..._buildImageRows(
|
||||
// context,
|
||||
// (item['SIGN_PATH'] as String).split(','),
|
||||
// item['SIGN_TIME'] as String? ?? '',
|
||||
// ),
|
||||
|
||||
// 问题1~4 + 答案(可编辑或只读)
|
||||
for (var i = 1; i <= 4; i++)
|
||||
if ((item['QUESTION$i'] as String?)?.isNotEmpty ??
|
||||
false)
|
||||
_buildQnA(item, i),
|
||||
// 操作图片
|
||||
if (item.containsKey('IMG_PATH') &&
|
||||
(item['IMG_PATH'] as String).isNotEmpty &&
|
||||
isShowSign)
|
||||
Row(
|
||||
children: [
|
||||
..._buildImageRows(
|
||||
context,
|
||||
(item['IMG_PATH'] as String).split(','),
|
||||
'',
|
||||
8,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -163,19 +207,55 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
: Colors.black,
|
||||
),
|
||||
),
|
||||
|
||||
// 操作图片
|
||||
if (item.containsKey('IMG_PATH') &&
|
||||
(item['IMG_PATH'] as String).isNotEmpty &&
|
||||
isShowSign)
|
||||
..._buildImageRows(
|
||||
context,
|
||||
(item['IMG_PATH'] as String).split(','),
|
||||
'',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!isAllowEdit)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (item.containsKey('SIGN_PATH') &&
|
||||
(item['SIGN_PATH'] as String).isNotEmpty)
|
||||
// 把 SIGN_PATH 切分为单张图片 path 列表,并去掉可能的空格
|
||||
...((item['SIGN_PATH'] as String)
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty)).map((path) {
|
||||
final imageUrl = '$baseImgPath$path';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 5),
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: imageUrl),
|
||||
context,
|
||||
);
|
||||
},
|
||||
child: Image.network(
|
||||
imageUrl,
|
||||
width: 40,
|
||||
height: 40,
|
||||
fit: BoxFit.cover,
|
||||
// 防止图片加载失败导致布局异常
|
||||
errorBuilder:
|
||||
(_, __, ___) => Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: Colors.grey.shade200,
|
||||
child: const Icon(
|
||||
Icons.broken_image,
|
||||
size: 18,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
@ -265,24 +345,56 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
BuildContext context,
|
||||
List<String> paths,
|
||||
String time,
|
||||
double right,
|
||||
) {
|
||||
return paths.map((p) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center, // ← 这一行
|
||||
if (paths.isEmpty) return [];
|
||||
|
||||
const int imagesPerRow = 4;
|
||||
const double imageSize = 40.0;
|
||||
const double spacing = 8.0;
|
||||
|
||||
// 把 paths 切成若干行(chunk)
|
||||
final rows = <List<String>>[];
|
||||
for (var i = 0; i < paths.length; i += imagesPerRow) {
|
||||
rows.add(paths.sublist(i, min(i + imagesPerRow, paths.length)));
|
||||
}
|
||||
|
||||
return rows.map((rowPaths) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(right: right, top: 5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: '$baseImgPath$p'),
|
||||
context,
|
||||
);
|
||||
},
|
||||
child: Image.network('$baseImgPath$p', width: 60, height: 60),
|
||||
),
|
||||
if (time.isNotEmpty) ...[const SizedBox(width: 8), Text(time)],
|
||||
for (var i = 0; i < rowPaths.length; i++) ...[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: '$baseImgPath${rowPaths[i]}'),
|
||||
context,
|
||||
);
|
||||
},
|
||||
child: Image.network(
|
||||
'$baseImgPath${rowPaths[i]}',
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder:
|
||||
(_, __, ___) => Container(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
color: Colors.grey.shade200,
|
||||
child: const Icon(
|
||||
Icons.broken_image,
|
||||
size: 18,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (i != rowPaths.length - 1) SizedBox(width: spacing),
|
||||
],
|
||||
// 如果希望每行右侧也填充占位使宽度平均,可加 Expanded 占位
|
||||
// Expanded(child: SizedBox()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -599,28 +711,31 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
children: (first['IMG_PATH'] as List)
|
||||
.cast<String>()
|
||||
.map((img) {
|
||||
final fullUrl = '$baseImgPath$img';
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
presentOpaque(SingleImageViewer(imageUrl: fullUrl), context);
|
||||
},
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (ctx, err, st) => Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
color: Colors.grey[200],
|
||||
child: const Icon(Icons.broken_image, size: 20),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
children:
|
||||
(first['IMG_PATH'] as List).cast<String>().map((img) {
|
||||
final fullUrl = '$baseImgPath$img';
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
);
|
||||
},
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder:
|
||||
(ctx, err, st) => Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
color: Colors.grey[200],
|
||||
child: const Icon(Icons.broken_image, size: 20),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
|
||||
|
|
@ -897,7 +1012,6 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5), // 圆角半径
|
||||
|
|
@ -1010,7 +1124,7 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
onPressed: _getData,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10,),
|
||||
const SizedBox(width: 10),
|
||||
// 清空
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
|
|
@ -1042,17 +1156,28 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
: item['NAME'] as String? ?? '';
|
||||
final checked = value.contains(key);
|
||||
return CheckboxListTile(
|
||||
controlAffinity: ListTileControlAffinity.leading, // <-- 把图标放左边
|
||||
contentPadding: EdgeInsets.zero, // 可选:移除左右默认 padding,使图标贴近左侧
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
// <-- 把图标放左边
|
||||
contentPadding: EdgeInsets.zero,
|
||||
// 可选:移除左右默认 padding,使图标贴近左侧
|
||||
activeColor: Colors.blue,
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(key, style: TextStyle(fontSize: 15)),
|
||||
if (widget.type == 'assignments') ...[
|
||||
Text('作业内容: ${item['WORK_CONTENT'] ?? ''}', style: TextStyle(fontSize: 15),),
|
||||
Text('作业负责人: ${item['CONFIRM_USER_NAME'] ?? ''}', style: TextStyle(fontSize: 15)),
|
||||
Text('作业申请时间: ${item['CREATTIME'] ?? ''}', style: TextStyle(fontSize: 15)),
|
||||
Text(
|
||||
'作业内容: ${item['WORK_CONTENT'] ?? ''}',
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
Text(
|
||||
'作业负责人: ${item['CONFIRM_USER_NAME'] ?? ''}',
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
Text(
|
||||
'作业申请时间: ${item['CREATTIME'] ?? ''}',
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
|
@ -1066,7 +1191,6 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
});
|
||||
},
|
||||
);
|
||||
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -1102,3 +1226,606 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 签字意见
|
||||
/// 显示签名/图片/时间等信息的项
|
||||
class SignItemWidget extends StatelessWidget {
|
||||
const SignItemWidget({
|
||||
Key? key,
|
||||
required this.signKey,
|
||||
required this.nameKey,
|
||||
required this.title,
|
||||
required this.pd,
|
||||
required this.signs,
|
||||
required this.baseImgPath,
|
||||
this.smallThumbSize = 50.0,
|
||||
this.signImageWidth = 200.0,
|
||||
this.signImageHeight = 100.0,
|
||||
}) : super(key: key);
|
||||
|
||||
/// signs map 中用于查找签名列表的 key
|
||||
final String signKey;
|
||||
|
||||
/// pd map 中用于显示右侧字段的 key
|
||||
final String nameKey;
|
||||
|
||||
/// 标题文本(左侧)
|
||||
final String title;
|
||||
|
||||
/// pd 数据(用来显示右侧信息)
|
||||
final Map<String, dynamic> pd;
|
||||
|
||||
/// signs 数据(包含 SIGN_PATH / SIGN_TIME / IMG_PATH 等)
|
||||
final Map<String, dynamic> signs;
|
||||
|
||||
/// 图片基准路径(会拼接到每个 path 之前)
|
||||
final String baseImgPath;
|
||||
|
||||
/// 小图(IMG_PATH)缩略图尺寸
|
||||
final double smallThumbSize;
|
||||
|
||||
/// 签名图片展示尺寸(每张一行)
|
||||
final double signImageWidth;
|
||||
final double signImageHeight;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// signs 中的各项
|
||||
if (FormUtils.hasValue(signs, signKey)) ..._buildSignItems(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildSignItems(BuildContext context) {
|
||||
final list = <Widget>[];
|
||||
|
||||
final rawList = signs[signKey];
|
||||
if (rawList is! List) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 聚合所有条目的签字路径和签名时间(避免为每条记录都生成一个签字行)
|
||||
final List<String> aggregatedSignPaths = [];
|
||||
final List<String> aggregatedSignTimes = [];
|
||||
|
||||
// 保留原来每条记录的 IMG_PATH 行(如果你也想合并这些小图行到一行,可以改这里)
|
||||
final List<Widget> smallThumbRows = [];
|
||||
|
||||
for (final raw in rawList) {
|
||||
if (raw is! Map) continue;
|
||||
final item = Map<String, dynamic>.from(raw);
|
||||
|
||||
// 解析 SIGN_PATH 并聚合
|
||||
List<String> signPaths = [];
|
||||
final rawSP =
|
||||
FormUtils.hasValue(item, 'SIGN_PATH') ? item['SIGN_PATH'] : null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
signPaths = rawSP
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList();
|
||||
} else if (rawSP is List) {
|
||||
signPaths = rawSP
|
||||
.cast<String>()
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
aggregatedSignPaths.addAll(signPaths);
|
||||
|
||||
// 解析 SIGN_TIME 并聚合(保留原有行为:split 不会 trim 时间内部空格)
|
||||
List<String> signTimes = [];
|
||||
final rawST =
|
||||
FormUtils.hasValue(item, 'SIGN_TIME') ? item['SIGN_TIME'] : null;
|
||||
if (rawST is String && rawST.isNotEmpty) {
|
||||
signTimes = rawST.split(',');
|
||||
} else if (rawST is List) {
|
||||
signTimes = rawST.cast<String>();
|
||||
}
|
||||
aggregatedSignTimes.addAll(signTimes);
|
||||
|
||||
// 解析 IMG_PATH(取最多 2 张缩略图)——保持原来行为,但改成先收集 widget 再统一加入 list
|
||||
final List<String> aggregatedImgPaths = [];
|
||||
|
||||
for (final raw in rawList) {
|
||||
if (raw is! Map) continue;
|
||||
final item = Map<String, dynamic>.from(raw);
|
||||
|
||||
final rawIP =
|
||||
FormUtils.hasValue(item, 'IMG_PATH') ? item['IMG_PATH'] : null;
|
||||
if (rawIP is String && rawIP.isNotEmpty) {
|
||||
aggregatedImgPaths.addAll(
|
||||
rawIP.split(',')
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty),
|
||||
);
|
||||
} else if (rawIP is List) {
|
||||
aggregatedImgPaths.addAll(
|
||||
rawIP.cast<String>()
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 最终只取 2 张
|
||||
final displayImgPaths = aggregatedImgPaths.take(2).toList();
|
||||
|
||||
if (displayImgPaths.isNotEmpty) {
|
||||
smallThumbRows.add(
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
children: displayImgPaths.map((p) {
|
||||
final fullUrl = '$baseImgPath$p';
|
||||
return GestureDetector(
|
||||
onTap: () => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: smallThumbSize,
|
||||
height: smallThumbSize,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Container(
|
||||
width: smallThumbSize,
|
||||
height: smallThumbSize,
|
||||
color: Colors.grey.shade200,
|
||||
child: const Icon(Icons.broken_image, size: 24),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
smallThumbRows.add(const SizedBox(height: 8));
|
||||
}
|
||||
} // end for
|
||||
|
||||
// 先把所有小缩略图行加入(保持原来每条记录的小图显示行为)
|
||||
if (smallThumbRows.isNotEmpty) {
|
||||
list.addAll(smallThumbRows);
|
||||
}
|
||||
|
||||
// 聚合后的最后一个签名时间(如果没有则为空串)
|
||||
final lastTime = aggregatedSignTimes.isNotEmpty
|
||||
? aggregatedSignTimes.last.trim()
|
||||
: '';
|
||||
|
||||
// 每张图片的尺寸与间距(保持你原来的数值)
|
||||
const double thumbSize = 60.0;
|
||||
const double gapBetweenThumbs = 8.0;
|
||||
const double timeBoxWidth = 120.0; // 时间文本固定宽度(可调整)
|
||||
|
||||
// 单独渲染一行:签字: [多个签字图片水平滚动] 时间(只会渲染一次)
|
||||
// 单独渲染一行:签字: [多个签字图片多行显示(Wrap)] 时间(只会渲染一次,超过宽度自动换行)
|
||||
list.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: LayoutBuilder(
|
||||
builder: (ctx, constraints) {
|
||||
// 图片区域最大宽度 = 可用宽度 - 时间宽度 - 间隔
|
||||
final double maxImageAreaWidth =
|
||||
(constraints.maxWidth - timeBoxWidth - 8)
|
||||
.clamp(0.0, constraints.maxWidth);
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end, // 底部对齐
|
||||
children: [
|
||||
const Text('签字:', style: TextStyle(fontSize: 13)),
|
||||
|
||||
// 图片区域(限制最大宽度,使用 Wrap 自动换行,不滚动)
|
||||
if (aggregatedSignPaths.isNotEmpty)
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxImageAreaWidth),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: Wrap(
|
||||
spacing: gapBetweenThumbs,
|
||||
runSpacing: 6.0,
|
||||
alignment: WrapAlignment.start,
|
||||
crossAxisAlignment: WrapCrossAlignment.end,
|
||||
children: aggregatedSignPaths.map((imgPath) {
|
||||
final fullUrl = '$baseImgPath$imgPath';
|
||||
return GestureDetector(
|
||||
onTap: () => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: thumbSize,
|
||||
height: thumbSize,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Container(
|
||||
width: thumbSize,
|
||||
height: thumbSize,
|
||||
color: Colors.grey.shade200,
|
||||
child: const Icon(
|
||||
Icons.broken_image,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 图片与时间之间的小间隔(仅在有图片时展示)
|
||||
if (aggregatedSignPaths.isNotEmpty) const SizedBox(width: 8),
|
||||
|
||||
// 时间文本(始终固定在最右侧并底部对齐)
|
||||
SizedBox(
|
||||
width: timeBoxWidth,
|
||||
child: Text(
|
||||
lastTime,
|
||||
textAlign: TextAlign.right,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
/// 签名意见回显
|
||||
class ConfirmWithSignWidget extends StatelessWidget {
|
||||
const ConfirmWithSignWidget({
|
||||
Key? key,
|
||||
required this.signs,
|
||||
required this.pd,
|
||||
required this.baseImgPath,
|
||||
// 可定制的 key 与标题,保持与你原来的调用一致为默认值
|
||||
required this.sectionKey, // 'CONFIRM',
|
||||
this.descrField = 'DESCR',
|
||||
this.imgsKey = '',
|
||||
this.contentKey = '',
|
||||
required this.nameKey, // 'CONFIRM_USER_NAME',
|
||||
required this.headerTitle, // '作业负责人意见',
|
||||
required this.roleTitle, // '作业负责人',
|
||||
}) : super(key: key);
|
||||
|
||||
// 允许外部传入 null(但构建内部会降级为 {},避免空异常)
|
||||
final Map<String, dynamic>? signs;
|
||||
final Map<String, dynamic>? pd;
|
||||
final String baseImgPath;
|
||||
|
||||
/// signs map 中用来查找意见与签名的 key(例如 'CONFIRM')
|
||||
final String sectionKey;
|
||||
|
||||
/// 意见文本字段名(通常 'DESCR')
|
||||
final String descrField;
|
||||
|
||||
/// 取pd中指定图片信息(可选)
|
||||
final String imgsKey;
|
||||
|
||||
/// 取pd中指定描述信息(可选)
|
||||
final String contentKey;
|
||||
|
||||
/// pd map 中显示在 header 右侧的字段
|
||||
final String nameKey;
|
||||
|
||||
/// 顶部卡片左侧标题文字(例如 '作业负责人意见')
|
||||
final String headerTitle;
|
||||
|
||||
/// 传给 SignItemWidget 的 title(例如 '作业负责人')
|
||||
final String roleTitle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 容错:如果传入为 null,降级成空 Map,后续访问安全
|
||||
final safeSigns = signs ?? <String, dynamic>{};
|
||||
final safePd = pd ?? <String, dynamic>{};
|
||||
|
||||
// 如果没有该 section 则不渲染
|
||||
if (!FormUtils.hasValue(safeSigns, sectionKey)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// 从 signs 中取第一个意见(保持与你原来逻辑一致)
|
||||
final sectionList = safeSigns[sectionKey];
|
||||
String descrText = '';
|
||||
if (sectionList is List && sectionList.isNotEmpty && sectionList[0] is Map) {
|
||||
final first = sectionList[0] as Map;
|
||||
final dynamic ds = first[descrField];
|
||||
if (ds != null) {
|
||||
descrText = ds.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// 意见卡片(复用你之前的 _itemContainer 风格)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题行
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 10,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: ListItemFactory.headerTitle('$headerTitle:')),
|
||||
const SizedBox(),
|
||||
// 仅当 pd 中存在 nameKey 且不为空时显示
|
||||
// if (FormUtils.hasValue(safePd, nameKey))
|
||||
// Text(
|
||||
// (safePd[nameKey] ?? '').toString(),
|
||||
// style: const TextStyle(fontSize: 14),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 描述内容(当 descrText 有意义才显示)
|
||||
if (descrText.isNotEmpty && descrText != '无')
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
child: Text(
|
||||
descrText,
|
||||
maxLines: 100,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
),
|
||||
|
||||
// 如果 imgsKey 有值并且 safePd 中有对应图片数据则显示(ItemListWidget.twoRowTitleAndImages 期望的 imageUrls)
|
||||
if (imgsKey.isNotEmpty && FormUtils.hasValue(safePd, imgsKey))
|
||||
Builder(builder: (ctx) {
|
||||
final dynamic rawImgs = safePd[imgsKey];
|
||||
// 传给 ItemListWidget 的 imageUrls 需要类型兼容,这里仅在 rawImgs 非空时才调用
|
||||
if (rawImgs != null) {
|
||||
return ItemListWidget.twoRowTitleAndImages(
|
||||
onTapCallBack: (val) {
|
||||
// 使用传入的 context 打开预览
|
||||
presentOpaque(SingleImageViewer(imageUrl: val), context);
|
||||
},
|
||||
title: '',
|
||||
imageUrls: rawImgs,
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}),
|
||||
|
||||
// 如果 contentKey 有值并且 pd 中存在则显示(类型转换为字符串以防出错)
|
||||
if (contentKey.isNotEmpty && FormUtils.hasValue(safePd, contentKey))
|
||||
ItemListWidget.itemContainer(
|
||||
Text(
|
||||
(safePd[contentKey] ?? '').toString(),
|
||||
style: const TextStyle(fontSize: 14, color: Colors.black54),
|
||||
),
|
||||
),
|
||||
|
||||
// SignItemWidget 需要非空 Map 参数,传入 safeSigns / safePd(即使为空 Map 也不会崩溃)
|
||||
SignItemWidget(
|
||||
signKey: sectionKey,
|
||||
nameKey: nameKey,
|
||||
title: roleTitle,
|
||||
pd: safePd,
|
||||
signs: safeSigns,
|
||||
baseImgPath: baseImgPath,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 单行显示标题+签字/电子姓名
|
||||
class SignRowImageTitle extends StatelessWidget {
|
||||
const SignRowImageTitle({
|
||||
Key? key,
|
||||
required this.label,
|
||||
required this.signKey,
|
||||
required this.signs, // 可以为 null,内部会容错
|
||||
this.fontSize = 14,
|
||||
this.text = '',
|
||||
this.imageSize = 40,
|
||||
this.gap = 8.0,
|
||||
}) : super(key: key);
|
||||
|
||||
final String label;
|
||||
final String signKey;
|
||||
final Map<String, dynamic>? signs; // 允许为 null
|
||||
final double fontSize;
|
||||
final String text;
|
||||
final double imageSize;
|
||||
final double gap;
|
||||
|
||||
List<String> _extractSignPaths() {
|
||||
final List<String> imagePaths = [];
|
||||
final safeSigns = signs ?? <String, dynamic>{};
|
||||
|
||||
final rawList = FormUtils.hasValue(safeSigns, signKey) ? safeSigns[signKey] : null;
|
||||
|
||||
if (rawList == null) return imagePaths;
|
||||
|
||||
// rawList 可能是 List、String 等 —— 以 List 为主处理,兼容其他类型
|
||||
if (rawList is List) {
|
||||
for (final raw in rawList) {
|
||||
if (raw is! Map) continue;
|
||||
final item = Map<String, dynamic>.from(raw);
|
||||
|
||||
final rawSP = FormUtils.hasValue(item, 'SIGN_PATH') ? item['SIGN_PATH'] : null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
imagePaths.addAll(
|
||||
rawSP.split(',').map((s) => s.trim()).where((s) => s.isNotEmpty),
|
||||
);
|
||||
} else if (rawSP is List) {
|
||||
imagePaths.addAll(
|
||||
rawSP.cast<dynamic>()
|
||||
.where((e) => e != null)
|
||||
.map((e) => e.toString().trim())
|
||||
.where((s) => s.isNotEmpty),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (rawList is String) {
|
||||
// 有些后端直接把路径以逗号连接成字符串返回
|
||||
imagePaths.addAll(
|
||||
rawList.split(',').map((s) => s.trim()).where((s) => s.isNotEmpty),
|
||||
);
|
||||
} else if (rawList is Map) {
|
||||
// 若误传了单条 Map,也尝试从中抽取 SIGN_PATH
|
||||
final rawSP = FormUtils.hasValue(rawList, 'SIGN_PATH') ? rawList['SIGN_PATH'] : null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
imagePaths.addAll(
|
||||
rawSP.split(',').map((s) => s.trim()).where((s) => s.isNotEmpty),
|
||||
);
|
||||
} else if (rawSP is List) {
|
||||
imagePaths.addAll(
|
||||
rawSP.cast<dynamic>()
|
||||
.where((e) => e != null)
|
||||
.map((e) => e.toString().trim())
|
||||
.where((s) => s.isNotEmpty),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 可选:去重并保持顺序
|
||||
final seen = <String>{};
|
||||
final deduped = <String>[];
|
||||
for (final p in imagePaths) {
|
||||
if (!seen.contains(p)) {
|
||||
seen.add(p);
|
||||
deduped.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
return deduped;
|
||||
}
|
||||
|
||||
void _openPreview(BuildContext context, String fullUrl) {
|
||||
try {
|
||||
presentOpaque(SingleImageViewer(imageUrl: fullUrl), context);
|
||||
return;
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imagePaths = _extractSignPaths();
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 12,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// 左侧标题
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
// const SizedBox(width: 12),
|
||||
// 右侧:优先显示图片(若无图片则显示 text;无 text 则不显示)
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (imagePaths.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Wrap(
|
||||
spacing: gap,
|
||||
runSpacing: gap,
|
||||
alignment: WrapAlignment.end,
|
||||
crossAxisAlignment: WrapCrossAlignment.end,
|
||||
children: imagePaths.map((p) {
|
||||
final fullUrl = '${ApiService.baseImgPath}$p';
|
||||
return GestureDetector(
|
||||
onTap: () => _openPreview(context, fullUrl),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Container(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
color: Colors.grey.shade200,
|
||||
child: const Icon(Icons.broken_image, size: 24),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
)
|
||||
else if (text.isNotEmpty)
|
||||
Text(
|
||||
text,
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(fontSize: 14, color: Colors.black54),
|
||||
)
|
||||
else
|
||||
const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
|||
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用明细表单组件(支持编辑/只读)
|
||||
class HotWorkDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -15,17 +15,21 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
final VoidCallback onChooseHotworkUser;
|
||||
final VoidCallback onAnalyzeTap;
|
||||
|
||||
|
||||
|
||||
// 新增
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onChooseVideoManager;/// 承包商
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
|
||||
/// 其他签字数据
|
||||
final signs;
|
||||
|
||||
/// 编辑模式下需提供以下控制器,非编辑可不传
|
||||
final TextEditingController? contentController;
|
||||
|
|
@ -42,6 +46,7 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
required this.onChooseLevel,
|
||||
required this.onChooseHotworkUser,
|
||||
required this.onAnalyzeTap,
|
||||
|
||||
/// 新增
|
||||
this.onChooseVideoManager,
|
||||
this.onContractorHandle,
|
||||
|
|
@ -54,7 +59,7 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
this.hotworkPersonController,
|
||||
this.relatedController,
|
||||
this.riskController,
|
||||
|
||||
this.signs = null,
|
||||
}) : assert(
|
||||
!isEditable ||
|
||||
(contentController != null &&
|
||||
|
|
@ -74,10 +79,12 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
|
||||
class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
||||
bool IS_CONTRACTOR_WORK = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
widget.pd['LATITUDE_LONGITUDE'] =
|
||||
'${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
|
|
@ -90,8 +97,16 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (FormUtils.hasValue(pd, 'CHECK_NO') && !widget.isEditable) ...[
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '编号:',
|
||||
isEditable: false,
|
||||
text: pd['CHECK_NO'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请单位:',
|
||||
label: '作业申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
|
|
@ -101,12 +116,12 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
isEditable: false,
|
||||
text: pd['APPLY_USER_NAME'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CHECK_NO')) ...[
|
||||
if (FormUtils.hasValue(pd, 'CREATTIME') && !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '编号:',
|
||||
label: '作业申请时间:',
|
||||
isEditable: false,
|
||||
text: pd['CHECK_NO'] ?? '',
|
||||
text: pd['CREATTIME'] ?? '',
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
|
|
@ -139,22 +154,37 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
controller: widget.methodController,
|
||||
text: pd['WORK_FUNCTION'] ?? '',
|
||||
),
|
||||
if (pd['WORK_START_DATE']?.toString().isNotEmpty == true) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火作业实施时间:',
|
||||
isEditable: false,
|
||||
text: '${pd['WORK_START_DATE']} 至 ${pd['WORK_END_DATE'] ?? '--'}',
|
||||
),
|
||||
],
|
||||
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
buttonText: '选择',
|
||||
isRequired: widget.isEditable,
|
||||
label: '动火人及证书编号:',
|
||||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
text:pd['WORK_USER'] ?? '',
|
||||
text: pd['WORK_USER'] ?? '请选择动火人及证书编号',
|
||||
hintText: '请选择动火人及证书编号',
|
||||
onTap: widget.onChooseHotworkUser,
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_DEPARTMENT_NAME') &&
|
||||
!widget.isEditable) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '作业单位:',
|
||||
isEditable: false,
|
||||
text: pd['CONFIRM_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_USER_NAME') &&
|
||||
!widget.isEditable) ...[
|
||||
const Divider(),
|
||||
SignRowImageTitle(
|
||||
label: '作业负责人:',
|
||||
signKey: 'CONFIRM',
|
||||
signs: widget.signs,
|
||||
text: pd['CONFIRM_USER_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号',
|
||||
|
|
@ -206,75 +236,98 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
controller: widget.riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业视频监控:',
|
||||
isClean: widget.isEditable && (pd['WORK_LEVEL'] == '一级' || pd['WORK_LEVEL'] == '二级'),
|
||||
cleanText: '清除监控',
|
||||
isRequired: pd['WORK_LEVEL'] == '特级',
|
||||
isEditable: widget.isEditable,
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
mode: BottomPickerMode.dateTimeWithSeconds,
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
if (FormUtils.hasValue(pd, 'WORK_START_DATE') &&
|
||||
!widget.isEditable) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '动火作业实施时间:',
|
||||
isEditable: widget.isEditable,
|
||||
text:
|
||||
FormUtils.hasValue(pd, 'WORK_END_DATE')
|
||||
? '${pd['WORK_START_DATE'] ?? ''}\n-${pd['WORK_END_DATE'] ?? ''}'
|
||||
: pd['WORK_START_DATE'] ?? '',
|
||||
|
||||
),
|
||||
if (!FormUtils.hasValue(pd, 'WORK_END_DATE'))
|
||||
...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
],
|
||||
] else ...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
mode: BottomPickerMode.dateTimeWithSeconds,
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
@ -313,10 +366,28 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
onTap: widget.onWorkAreaLocationHandle ?? () {},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? (widget.isEditable ? '' : '无'),
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业视频监控:',
|
||||
isClean:
|
||||
widget.isEditable &&
|
||||
(pd['WORK_LEVEL'] == '一级' || pd['WORK_LEVEL'] == '二级'),
|
||||
cleanText: '清除监控',
|
||||
isRequired: pd['WORK_LEVEL'] == '特级',
|
||||
isEditable: widget.isEditable,
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
|
||||
if (FormUtils.hasValue(pd, 'ANALYZE_TIME') && !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
import 'HotWorkDetailFormWidget.dart';
|
||||
|
||||
class SpecialWorkFormBaseWork extends StatelessWidget {
|
||||
|
|
@ -55,168 +56,6 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget signItemWidget(
|
||||
String signKey,
|
||||
String nameKey,
|
||||
String name,
|
||||
BuildContext context,
|
||||
) {
|
||||
return _itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题行
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListItemFactory.headerTitle(name),
|
||||
),
|
||||
if (FormUtils.hasValue(pd, nameKey))
|
||||
Text(
|
||||
pd[nameKey]?.toString() ?? '',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
if (FormUtils.hasValue(signs, signKey))
|
||||
...((signs[signKey] as List<dynamic>)
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((item) {
|
||||
// 解析 SIGN_PATH
|
||||
List<String> signPaths = [];
|
||||
final rawSP = FormUtils.hasValue(item, 'SIGN_PATH') ? item['SIGN_PATH'] : null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
signPaths = rawSP.split(',').map((s) => s.trim()).toList();
|
||||
} else if (rawSP is List) {
|
||||
signPaths = rawSP.cast<String>();
|
||||
}
|
||||
|
||||
// 解析 SIGN_TIME 保留空格
|
||||
List<String> signTimes = [];
|
||||
final rawST = FormUtils.hasValue(item, 'SIGN_TIME') ? item['SIGN_TIME'] : null;
|
||||
if (rawST is String && rawST.isNotEmpty) {
|
||||
signTimes = rawST.split(',');
|
||||
} else if (rawST is List) {
|
||||
signTimes = rawST.cast<String>();
|
||||
}
|
||||
|
||||
final pairCount = math.min(signPaths.length, signTimes.length);
|
||||
|
||||
// 解析 IMG_PATH, 最多 2 张
|
||||
List<String> imgPaths = [];
|
||||
final rawIP = FormUtils.hasValue(item, 'IMG_PATH') ? item['IMG_PATH'] : null;
|
||||
if (rawIP is String && rawIP.isNotEmpty) {
|
||||
imgPaths = rawIP.split(',').map((s) => s.trim()).take(2).toList();
|
||||
} else if (rawIP is List) {
|
||||
imgPaths = rawIP.cast<String>().take(2).toList();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (imgPaths.isNotEmpty) ...[
|
||||
Row(
|
||||
children: imgPaths.map((p) {
|
||||
final fullUrl = '$baseImgPath$p';
|
||||
return GestureDetector(
|
||||
onTap: () => SingleImageViewer(imageUrl: fullUrl),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder: (_, __, ___) => const Icon(
|
||||
Icons.broken_image,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
|
||||
// 签名及时间
|
||||
...List.generate(pairCount, (index) {
|
||||
final imgPath = signPaths[index];
|
||||
final timeLabel = signTimes[index];
|
||||
final fullUrl = '$baseImgPath$imgPath';
|
||||
const imageWidth = 200.0;
|
||||
const imageHeight = 100.0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder: (_, __, ___) =>
|
||||
const Icon(Icons.broken_image, size: 60),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: imageHeight,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Text(
|
||||
timeLabel,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (index < pairCount - 1)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Divider(height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
|
|
@ -230,6 +69,7 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
onChooseLevel: onChooseLevel,
|
||||
onChooseHotworkUser: onChooseHotworkUser,
|
||||
onAnalyzeTap: onAnalyzeTap,
|
||||
signs: signs,
|
||||
),
|
||||
|
||||
// 2. 安全防护措施
|
||||
|
|
@ -257,6 +97,7 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
children: [
|
||||
ListItemFactory.createBuildSimpleSection('其他安全防护措施'),
|
||||
OtherMeasuresWidget(
|
||||
|
||||
otherMeasures: signs['MEASURES_CONFIRM'],
|
||||
baseImgPath: baseImgPath,
|
||||
),
|
||||
|
|
@ -264,138 +105,97 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
|
||||
// 4. 各环节签字及意见
|
||||
if (FormUtils.hasValue(signs, 'GUARDIAN'))
|
||||
signItemWidget('GUARDIAN', 'GUARDIAN_USER_NAME', '监护人', context),
|
||||
|
||||
if (FormUtils.hasValue(signs, 'CONFESS'))
|
||||
signItemWidget('CONFESS', 'CONFESS_USER_NAME', '安全交底人', context),
|
||||
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT_CONFESS'))
|
||||
signItemWidget('ACCEPT_CONFESS', 'ACCEPT_CONFESS_USER_NAME', '接受交底人', context),
|
||||
|
||||
// 作业负责人意见
|
||||
if (FormUtils.hasValue(signs, 'CONFIRM')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('作业负责人意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['CONFIRM'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('CONFIRM', 'CONFIRM_USER_NAME', '作业负责人', context),
|
||||
],
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
// 所在单位负责人意见
|
||||
if (FormUtils.hasValue(signs, 'LEADER')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('所在单位意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['LEADER'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('LEADER', 'LEADER_USER_NAME', '所在单位负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 安全管理部门负责人意见
|
||||
if (FormUtils.hasValue(signs, 'AUDIT')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('安全管理部门意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['AUDIT'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('AUDIT', 'AUDIT_USER_NAME', '安全管理部门负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 动火审批人意见
|
||||
if (FormUtils.hasValue(signs, 'APPROVE')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('动火审批人意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['APPROVE'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('APPROVE', 'APPROVE_USER_NAME', '动火审批人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'CONFESS',
|
||||
nameKey: 'CONFESS_USER_NAME',
|
||||
headerTitle: '安全交底人意见',
|
||||
roleTitle: '安全交底人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'ACCEPT_CONFESS',
|
||||
nameKey: 'ACCEPT_CONFESS_USER_NAME',
|
||||
headerTitle: '接受交底人',
|
||||
roleTitle: '',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'GUARDIAN',
|
||||
nameKey: 'GUARDIAN_USER_NAME',
|
||||
headerTitle: '监护人意见',
|
||||
roleTitle: '监护人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'CONFIRM',
|
||||
nameKey: 'CONFIRM_USER_NAME',
|
||||
headerTitle: '作业负责人意见',
|
||||
roleTitle: '作业负责人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'LEADER',
|
||||
nameKey: 'LEADER_USER_NAME',
|
||||
headerTitle: '所在单位意见',
|
||||
roleTitle: '所在单位负责人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'AUDIT',
|
||||
nameKey: 'AUDIT_USER_NAME',
|
||||
headerTitle: '安全管理部门意见',
|
||||
roleTitle: '安全管理部门',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'APPROVE',
|
||||
nameKey: 'APPROVE_USER_NAME',
|
||||
headerTitle: '动火审批人意见',
|
||||
roleTitle: '动火审批人',
|
||||
),
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'MONITOR'))
|
||||
signItemWidget('MONITOR', 'MONITOR_USER_NAME', '动火前在岗班长', context),
|
||||
// if (FormUtils.hasValue(signs, 'MONITOR'))
|
||||
// signItemWidget('MONITOR', 'MONITOR_USER_NAME', '动火前在岗班长', context),
|
||||
//
|
||||
// // 作业开始负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
// signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'MONITOR',
|
||||
nameKey: 'MONITOR_USER_NAME',
|
||||
headerTitle: '动火前,岗位当班班长验票情况',
|
||||
roleTitle: '动火前,岗位当班班长验票情况',
|
||||
),
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'ACCEPT',
|
||||
nameKey: 'ACCEPT_USER_NAME',
|
||||
headerTitle: '验收部门负责人意见',
|
||||
roleTitle: '验收部门负责人',
|
||||
),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('完工验收意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['ACCEPT'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('ACCEPT', 'ACCEPT_USER_NAME', '验收部门负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/center_multi_picker.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';
|
||||
|
|
@ -25,7 +26,6 @@ enum EditUserType {
|
|||
GUARDIAN('监护人单位', '监护人', true),
|
||||
CONFESS('安全交底人单位', '安全交底人', true),
|
||||
ACCEPT_CONFESS('接受交底人单位', '接受交底人', true),
|
||||
WORK_USER('作业人单位', '作业人', true),
|
||||
CONFIRM('作业负责人单位', '作业负责人', true),
|
||||
LEADER('所在单位', '所在单位负责人', true),
|
||||
AUDIT('安全管理部门', '安全管理部门负责人', true),
|
||||
|
|
@ -67,7 +67,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
late Map<String, dynamic> signs = {};
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late List<dynamic> measuresList = [];
|
||||
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
final TextEditingController _locationController = TextEditingController();
|
||||
|
|
@ -83,10 +83,13 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
/// ------------------- 新增 -------------------
|
||||
/// 视频监控摄像
|
||||
late List<dynamic> videoMonitoringList = [];
|
||||
|
||||
/// 承包商列表
|
||||
late List<dynamic> unitAllList = [];
|
||||
|
||||
/// 作业区域列表
|
||||
late List<Map<String, dynamic>> workAreaList = [];
|
||||
late List<dynamic> workAreaList = [];
|
||||
|
||||
/// --------------------------------------
|
||||
|
||||
// 存储各单位的人员列表
|
||||
|
|
@ -107,7 +110,6 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
pd['APPLY_USER_ID'] = SessionService.instance.loginUserId;
|
||||
pd['APPLY_USER_NAME'] = SessionService.instance.username;
|
||||
pd['IS_CONTRACTOR_WORK'] = '0';
|
||||
|
||||
}
|
||||
_getHotWorkNameList();
|
||||
_getVideoList();
|
||||
|
|
@ -125,9 +127,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
_methodController.addListener(() {
|
||||
pd['WORK_FUNCTION'] = _methodController.text.trim();
|
||||
});
|
||||
_hotworkPersonController.addListener(() {
|
||||
pd['WORK_USER'] = _hotworkPersonController.text.trim();
|
||||
});
|
||||
|
||||
_relatedController.addListener(() {
|
||||
pd['SPECIAL_WORK'] = _relatedController.text.trim();
|
||||
});
|
||||
|
|
@ -145,11 +145,9 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
void set_pd_USER_ID(EditUserType type, String id) {
|
||||
if (type == EditUserType.WORK_USER) {
|
||||
pd['${type.name}_ID'] = id;
|
||||
}else{
|
||||
|
||||
pd['${type.name}_USER_ID'] = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void set_pd_USER_Name(EditUserType type, String name) {
|
||||
|
|
@ -165,11 +163,8 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
String get_pd_USER_ID(EditUserType type) {
|
||||
if (type == EditUserType.WORK_USER) {
|
||||
return pd['${type.name}_ID'] ?? '';
|
||||
} else {
|
||||
return pd['${type.name}_USER_ID'] ?? '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String get_pd_USER_Name(EditUserType type) {
|
||||
|
|
@ -193,26 +188,39 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
Future<void> _chooseHorkUser() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
final res = await CenterMultiPicker.show<String>(
|
||||
context,
|
||||
items: workUserList.map((item) => item['NAME'] as String).toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
itemBuilder: (it) => Text(it),
|
||||
initialSelectedIndices: [1, 3],
|
||||
maxSelection: 100,
|
||||
allowEmpty: false,
|
||||
title: '动火人及证书编号',
|
||||
);
|
||||
if (choice != null) {
|
||||
|
||||
if (res != null) {
|
||||
// 点击确定,res 为 List<String>
|
||||
setState(() {
|
||||
pd['WORK_USER'] = choice;
|
||||
_hotworkPersonController.text = choice;
|
||||
Map<String, dynamic> result = workUserList.firstWhere(
|
||||
(item) => item['NAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'USER_ID')) {
|
||||
pd['WORK_USER_ID'] = result['USER_ID'];
|
||||
List<String> names = [];
|
||||
List<String> ids = [];
|
||||
|
||||
for (String name in res) {
|
||||
for (final item in workUserList) {
|
||||
if (item['NAME'] == name) {
|
||||
names.add(item['UNAME']);
|
||||
ids.add(item['PERSONNELMANAGEMENT_ID']);
|
||||
}
|
||||
}
|
||||
}
|
||||
//FocusHelper.clearFocus(context);
|
||||
pd['WORK_USER_NAME'] = names.join(',');
|
||||
pd['WORK_USER_ID'] = ids.join(',');
|
||||
pd['WORK_USER'] = res.join(',');
|
||||
print('${pd['WORK_USER_NAME']}\n${pd['WORK_USER_ID']}\n${pd['WORK_USER']}');
|
||||
});
|
||||
} else {
|
||||
// 取消或关闭对话框
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// ---------------------------- 新增 --------------------------------
|
||||
|
|
@ -242,6 +250,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 选择承包商
|
||||
Future<void> _chooseUnitManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
|
|
@ -267,7 +276,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
/// 选择经纬度
|
||||
Future<void> _showLocationHandle() async{
|
||||
Future<void> _showLocationHandle() async {
|
||||
if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择作业区域');
|
||||
return;
|
||||
|
|
@ -276,11 +285,11 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
setState(() {
|
||||
pd['LONGITUDE'] = mapData['longitue'] ?? '';
|
||||
pd['LATITUDE'] = mapData['latitude'] ?? '';
|
||||
pd['LATITUDE_LONGITUDE'] = '${mapData['longitue'] ?? ''},${mapData['latitude'] ?? ''}';
|
||||
pd['LATITUDE_LONGITUDE'] =
|
||||
'${mapData['longitue'] ?? ''},${mapData['latitude'] ?? ''}';
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// 作业区域
|
||||
Future<void> _getWorkArea() async {
|
||||
//FocusHelper.clearFocus(context);
|
||||
|
|
@ -291,16 +300,17 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) => WorkAreaPicker(
|
||||
onSelected: (String id, String POSITIONS, String name) {
|
||||
pd['ELECTRONIC_FENCE_AREA_ID'] = id;
|
||||
pd['POSITIONS'] = POSITIONS;
|
||||
pd['PLS_NAME'] = name;
|
||||
},
|
||||
),
|
||||
onSelected: (String id, String POSITIONS, String name) {
|
||||
pd['ELECTRONIC_FENCE_AREA_ID'] = id;
|
||||
pd['POSITIONS'] = POSITIONS;
|
||||
pd['PLS_NAME'] = name;
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
//FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
|
||||
/// 获取摄像头列表
|
||||
Future<void> _getVideoList() async {
|
||||
final result = await ApiService.getVideomanagerList();
|
||||
|
|
@ -308,6 +318,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
videoMonitoringList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
|
||||
/// 获承包商列表
|
||||
Future<void> _getUnitListAll() async {
|
||||
final result = await ApiService.getUnitListAll();
|
||||
|
|
@ -315,6 +326,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
unitAllList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
|
||||
/// 作业区域列表
|
||||
Future<void> _getPlsList() async {
|
||||
final result = await ApiService.getWorkAreaList();
|
||||
|
|
@ -323,8 +335,8 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
workAreaList = jsonDecode(zTreeNodes);
|
||||
});
|
||||
}
|
||||
/// ------------------------------------------------------------
|
||||
|
||||
/// ------------------------------------------------------------
|
||||
|
||||
Widget _card(Widget child) {
|
||||
return Container(
|
||||
|
|
@ -476,11 +488,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
{'value': _contentController.text.trim(), 'message': '请填写作业内容'},
|
||||
{'value': _locationController.text.trim(), 'message': '请填写动火地点及部位'},
|
||||
{'value': _methodController.text.trim(), 'message': '请填写动火方式'},
|
||||
{'value': _hotworkPersonController.text.trim(), 'message': '请填写动火人及证书编号'},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': pd['WORK_USER'], 'message': '请选择动火人及证书编号'},
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
];
|
||||
final level = pd['WORK_LEVEL'] ?? '';
|
||||
|
|
@ -492,7 +500,6 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
EditUserType.GUARDIAN,
|
||||
EditUserType.CONFESS,
|
||||
EditUserType.ACCEPT_CONFESS,
|
||||
EditUserType.WORK_USER,
|
||||
EditUserType.CONFIRM,
|
||||
EditUserType.LEADER,
|
||||
// 如果不是二级,或者SAFETY_USER_ID有值就加上 AUDIT
|
||||
|
|
@ -534,7 +541,8 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) {
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1' &&
|
||||
!FormUtils.hasValue(pd, 'UNITS_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择承包商');
|
||||
return;
|
||||
}
|
||||
|
|
@ -595,7 +603,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
ToastUtil.showError(context, '提交失败');
|
||||
ToastUtil.showError(context, result['msg'] ?? '提交失败');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -610,9 +618,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// ---------------------------- 接口请求 --------------------------------
|
||||
/// ---------------------------- 接口请求 --------------------------------
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
|
|
@ -632,12 +638,11 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
_contentController.text = pd['WORK_CONTENT'] ?? '';
|
||||
_locationController.text = pd['WORK_PLACE'] ?? '';
|
||||
_methodController.text = pd['WORK_FUNCTION'] ?? '';
|
||||
_hotworkPersonController.text = pd['WORK_USER'] ?? '';
|
||||
_relatedController.text = pd['SPECIAL_WORK'] ?? '';
|
||||
_riskController.text = pd['RISK_IDENTIFICATION'] ?? '';
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
'hotwork',
|
||||
|
|
@ -647,13 +652,14 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
'hotwork',
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
measuresList = List<dynamic>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
|
|
@ -706,8 +712,6 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.ACCEPT_CONFESS)),
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.WORK_USER)),
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.CONFIRM)),
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.LEADER)),
|
||||
|
|
|
|||
|
|
@ -51,81 +51,6 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -163,10 +88,7 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -305,7 +227,6 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -349,29 +270,11 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -52,87 +52,8 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -277,7 +198,6 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -320,29 +240,10 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'package:qhd_prevention/http/ApiService.dart';
|
|||
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用断路作业明细表单组件(支持编辑/只读)
|
||||
class CutroadDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -18,19 +18,24 @@ class CutroadDetailFormWidget extends StatefulWidget {
|
|||
final VoidCallback onChooseLevel;
|
||||
|
||||
/// 编辑模式下需提供以下控制器,非编辑模式可不传
|
||||
final TextEditingController? unitController; // 涉及部门
|
||||
final TextEditingController? contentController; // 原因
|
||||
final TextEditingController? unitController; // 涉及部门
|
||||
final TextEditingController? contentController; // 原因
|
||||
final TextEditingController? relatedController;
|
||||
final TextEditingController? riskController;
|
||||
|
||||
// 新增
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onChooseVideoManager;/// 承包商
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
|
||||
/// 其他签字数据
|
||||
final signs;
|
||||
|
||||
|
|
@ -51,19 +56,19 @@ class CutroadDetailFormWidget extends StatefulWidget {
|
|||
this.relatedController,
|
||||
this.riskController,
|
||||
this.signs = null,
|
||||
|
||||
}) : assert(
|
||||
!isEditable ||
|
||||
(unitController != null &&
|
||||
contentController != null &&
|
||||
relatedController != null &&
|
||||
riskController != null),
|
||||
'Editable mode requires all TextEditingController parameters',
|
||||
),
|
||||
super(key: key);
|
||||
!isEditable ||
|
||||
(unitController != null &&
|
||||
contentController != null &&
|
||||
relatedController != null &&
|
||||
riskController != null),
|
||||
'Editable mode requires all TextEditingController parameters',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
State<CutroadDetailFormWidget> createState() => _CutroadDetailFormWidgetState();
|
||||
State<CutroadDetailFormWidget> createState() =>
|
||||
_CutroadDetailFormWidgetState();
|
||||
}
|
||||
|
||||
class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
||||
|
|
@ -77,174 +82,184 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget signItemWidget(
|
||||
String signKey,
|
||||
String nameKey,
|
||||
String name,
|
||||
BuildContext context,
|
||||
) {
|
||||
String signKey,
|
||||
BuildContext context,
|
||||
) {
|
||||
return _itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题行
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListItemFactory.headerTitle(name),
|
||||
),
|
||||
if (FormUtils.hasValue(widget.pd, nameKey))
|
||||
Text(
|
||||
widget.pd[nameKey]?.toString() ?? '',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
if (FormUtils.hasValue(widget.signs, signKey))
|
||||
...((widget.signs[signKey] as List<dynamic>)
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((item) {
|
||||
// 解析 SIGN_PATH
|
||||
List<String> signPaths = [];
|
||||
final rawSP = FormUtils.hasValue(item, 'SIGN_PATH') ? item['SIGN_PATH'] : null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
signPaths = rawSP.split(',').map((s) => s.trim()).toList();
|
||||
} else if (rawSP is List) {
|
||||
signPaths = rawSP.cast<String>();
|
||||
}
|
||||
// 解析 SIGN_PATH
|
||||
List<String> signPaths = [];
|
||||
final rawSP =
|
||||
FormUtils.hasValue(item, 'SIGN_PATH')
|
||||
? item['SIGN_PATH']
|
||||
: null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
signPaths = rawSP.split(',').map((s) => s.trim()).toList();
|
||||
} else if (rawSP is List) {
|
||||
signPaths = rawSP.cast<String>();
|
||||
}
|
||||
|
||||
// 解析 SIGN_TIME 保留空格
|
||||
List<String> signTimes = [];
|
||||
final rawST = FormUtils.hasValue(item, 'SIGN_TIME') ? item['SIGN_TIME'] : null;
|
||||
if (rawST is String && rawST.isNotEmpty) {
|
||||
signTimes = rawST.split(',');
|
||||
} else if (rawST is List) {
|
||||
signTimes = rawST.cast<String>();
|
||||
}
|
||||
// 解析 SIGN_TIME 保留空格
|
||||
List<String> signTimes = [];
|
||||
final rawST =
|
||||
FormUtils.hasValue(item, 'SIGN_TIME')
|
||||
? item['SIGN_TIME']
|
||||
: null;
|
||||
if (rawST is String && rawST.isNotEmpty) {
|
||||
signTimes = rawST.split(',');
|
||||
} else if (rawST is List) {
|
||||
signTimes = rawST.cast<String>();
|
||||
}
|
||||
|
||||
final pairCount = math.min(signPaths.length, signTimes.length);
|
||||
final pairCount = math.min(
|
||||
signPaths.length,
|
||||
signTimes.length,
|
||||
);
|
||||
|
||||
// 解析 IMG_PATH, 最多 2 张
|
||||
List<String> imgPaths = [];
|
||||
final rawIP = FormUtils.hasValue(item, 'IMG_PATH') ? item['IMG_PATH'] : null;
|
||||
if (rawIP is String && rawIP.isNotEmpty) {
|
||||
imgPaths = rawIP.split(',').map((s) => s.trim()).take(2).toList();
|
||||
} else if (rawIP is List) {
|
||||
imgPaths = rawIP.cast<String>().take(2).toList();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (imgPaths.isNotEmpty) ...[
|
||||
Row(
|
||||
children: imgPaths.map((p) {
|
||||
final fullUrl = '${ApiService.baseImgPath}$p';
|
||||
return GestureDetector(
|
||||
onTap: () => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder: (_, __, ___) => const Icon(
|
||||
Icons.broken_image,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
|
||||
// 签名及时间
|
||||
...List.generate(pairCount, (index) {
|
||||
final imgPath = signPaths[index];
|
||||
final timeLabel = signTimes[index];
|
||||
final fullUrl = '${ApiService.baseImgPath}$imgPath';
|
||||
const imageWidth = 200.0;
|
||||
const imageHeight = 100.0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder: (_, __, ___) =>
|
||||
const Icon(Icons.broken_image, size: 60),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: imageHeight,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Text(
|
||||
timeLabel,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black87,
|
||||
// 解析 IMG_PATH, 最多 2 张
|
||||
List<String> imgPaths = [];
|
||||
final rawIP =
|
||||
FormUtils.hasValue(item, 'IMG_PATH')
|
||||
? item['IMG_PATH']
|
||||
: null;
|
||||
if (rawIP is String && rawIP.isNotEmpty) {
|
||||
imgPaths =
|
||||
rawIP.split(',').map((s) => s.trim()).take(2).toList();
|
||||
} else if (rawIP is List) {
|
||||
imgPaths = rawIP.cast<String>().take(2).toList();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8.0,
|
||||
horizontal: 10,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (imgPaths.isNotEmpty) ...[
|
||||
Row(
|
||||
children:
|
||||
imgPaths.map((p) {
|
||||
final fullUrl = '${ApiService.baseImgPath}$p';
|
||||
return GestureDetector(
|
||||
onTap:
|
||||
() => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 8.0,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(
|
||||
Icons.broken_image,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
|
||||
// 签名及时间
|
||||
...List.generate(pairCount, (index) {
|
||||
final imgPath = signPaths[index];
|
||||
final timeLabel = signTimes[index];
|
||||
final fullUrl = '${ApiService.baseImgPath}$imgPath';
|
||||
const imageWidth = 200.0;
|
||||
const imageHeight = 100.0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => presentOpaque(
|
||||
SingleImageViewer(
|
||||
imageUrl: fullUrl,
|
||||
),
|
||||
context,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(
|
||||
Icons.broken_image,
|
||||
size: 60,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: imageHeight,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Text(
|
||||
timeLabel,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (index < pairCount - 1)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Divider(height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (index < pairCount - 1)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Divider(height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
.toList()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
widget.pd['LATITUDE_LONGITUDE'] =
|
||||
'${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
|
|
@ -276,8 +291,20 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
isEditable: false,
|
||||
text: pd['APPLY_USER_NAME'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_DEPARTMENT_NAME')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '作业单位:',
|
||||
isEditable: false,
|
||||
text: pd['CONFIRM_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_USER_NAME')&& !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
// ItemListWidget.OneRowImageTitle(label: '作业负责人', imgPatlabel: '作业负责人', imgPath: imgPathh: imgPath)
|
||||
SignRowImageTitle(label: '作业负责人:', signKey: 'CONFIRM', signs: widget.signs, text: pd['CONFIRM_USER_NAME']??'',),
|
||||
],
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '涉及相关单位(部门):',
|
||||
isEditable: widget.isEditable,
|
||||
|
|
@ -285,6 +312,11 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
controller: widget.unitController,
|
||||
text: pd['OTHER_DEPT'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_USER_NAME')&& !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
// ItemListWidget.OneRowImageTitle(label: '作业负责人', imgPath: imgPath)
|
||||
SignRowImageTitle(label: '监护人:', signKey: 'GUARDIAN', signs: widget.signs, text: pd['GUARDIAN_USER_NAME']??'',),
|
||||
],
|
||||
const Divider(),
|
||||
ItemListWidget.multiLineTitleTextField(
|
||||
label: '断路原因:',
|
||||
|
|
@ -301,16 +333,17 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'assignments',
|
||||
initialValue: pd['SPECIAL_WORK'] ?? '',
|
||||
onConfirm: (v) {
|
||||
setState(() {
|
||||
pd['SPECIAL_WORK'] = v;
|
||||
widget.relatedController?.text = v;
|
||||
});
|
||||
},
|
||||
),
|
||||
builder:
|
||||
(_) => SelectionPopup(
|
||||
type: 'assignments',
|
||||
initialValue: pd['SPECIAL_WORK'] ?? '',
|
||||
onConfirm: (v) {
|
||||
setState(() {
|
||||
pd['SPECIAL_WORK'] = v;
|
||||
widget.relatedController?.text = v;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
//FocusHelper.clearFocus(context);
|
||||
},
|
||||
|
|
@ -319,41 +352,35 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
text: pd['SPECIAL_WORK'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ConfirmWithSignWidget(
|
||||
signs: widget.signs,
|
||||
pd: pd,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
sectionKey: 'GUARDIAN',
|
||||
nameKey: 'WORK_CONTENT',
|
||||
headerTitle: '断路地段示意图(可另附图)及相关说明',
|
||||
imgsKey: 'CONTENT_IMG_PATH',
|
||||
contentKey: 'WORK_CONTENT',
|
||||
roleTitle: '',
|
||||
),
|
||||
|
||||
if (!widget.isEditable && FormUtils.hasValue(pd, 'WORK_CONTENT')) ...[
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '断路地段示意图(可另附图)及相关说明:',
|
||||
isEditable: false,
|
||||
text: pd['WORK_CONTENT'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
if (!widget.isEditable && FormUtils.hasValue(pd, 'CONTENT_IMG_PATH')) ...[
|
||||
ItemListWidget.twoRowTitleAndImages(
|
||||
onTapCallBack: (val) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: val), context);
|
||||
},
|
||||
title: '断路地段示意图:',
|
||||
imageUrls: pd['CONTENT_IMG_PATH'],
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'identification',
|
||||
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
onConfirm: (v) {
|
||||
setState(() {
|
||||
pd['RISK_IDENTIFICATION'] = v;
|
||||
widget.riskController?.text = v;
|
||||
});
|
||||
},
|
||||
),
|
||||
builder:
|
||||
(_) => SelectionPopup(
|
||||
type: 'identification',
|
||||
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
onConfirm: (v) {
|
||||
setState(() {
|
||||
pd['RISK_IDENTIFICATION'] = v;
|
||||
widget.riskController?.text = v;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
//FocusHelper.clearFocus(context);
|
||||
},
|
||||
|
|
@ -361,59 +388,69 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
controller: widget.riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
mode: BottomPickerMode.dateTimeWithSeconds,
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
if (FormUtils.hasValue(pd, 'WORK_START_DATE')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '断路作业实施时间:',
|
||||
isEditable: widget.isEditable,
|
||||
text: pd['WORK_START_DATE'] ?? '',
|
||||
),
|
||||
] else ...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
mode: BottomPickerMode.dateTimeWithSeconds,
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
@ -452,7 +489,7 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
onTap: widget.onWorkAreaLocationHandle ?? () {},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? (widget.isEditable ? '' : '无'),
|
||||
),
|
||||
|
|
@ -473,14 +510,6 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
|
||||
if (widget.signs != null && FormUtils.hasValue(widget.signs, 'PROJECT_MANAGER'))
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
signItemWidget('PROJECT_MANAGER', 'PROJECT_MANAGER_USER_NAME', '断路示意图负责人签字', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
import 'CutroadDetailFormWidget.dart';
|
||||
|
||||
class CutroadFormBaseWork extends StatelessWidget {
|
||||
|
|
@ -67,7 +67,7 @@ class CutroadFormBaseWork extends StatelessWidget {
|
|||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListItemFactory.headerTitle(name),
|
||||
child: ListItemFactory.headerTitle('$name'),
|
||||
),
|
||||
if (FormUtils.hasValue(pd, nameKey))
|
||||
Text(
|
||||
|
|
@ -261,135 +261,71 @@ class CutroadFormBaseWork extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'CONFESS',
|
||||
nameKey: 'CONFESS_USER_NAME',
|
||||
headerTitle: '安全交底人',
|
||||
roleTitle: '安全交底人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'ACCEPT_CONFESS',
|
||||
nameKey: 'ACCEPT_CONFESS_USER_NAME',
|
||||
headerTitle: '接受交底人',
|
||||
roleTitle: '',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'CONFIRM',
|
||||
nameKey: 'CONFIRM_USER_NAME',
|
||||
headerTitle: '作业负责人意见',
|
||||
roleTitle: '作业负责人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'LEADER',
|
||||
nameKey: 'LEADER_USER_NAME',
|
||||
headerTitle: '所在单位负责人意见',
|
||||
roleTitle: '所在单位负责人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'AUDIT',
|
||||
nameKey: 'AUDIT_USER_NAME',
|
||||
headerTitle: '消防、安全管理部门负责人意见',
|
||||
roleTitle: '消防、安全管理部门负责人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'APPROVE',
|
||||
nameKey: 'APPROVE_USER_NAME',
|
||||
headerTitle: '审批部门负责人意见',
|
||||
roleTitle: '审批部门负责人',
|
||||
),
|
||||
|
||||
// 4. 各环节签字及意见
|
||||
if (FormUtils.hasValue(signs, 'GUARDIAN'))
|
||||
signItemWidget('GUARDIAN', 'GUARDIAN_USER_NAME', '监护人', context),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'ACCEPT',
|
||||
nameKey: 'ACCEPT_USER_NAME',
|
||||
headerTitle: '验收部门负责人意见',
|
||||
roleTitle: '验收部门负责人',
|
||||
),
|
||||
|
||||
if (FormUtils.hasValue(signs, 'CONFESS'))
|
||||
signItemWidget('CONFESS', 'CONFESS_USER_NAME', '安全交底人', context),
|
||||
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT_CONFESS'))
|
||||
signItemWidget('ACCEPT_CONFESS', 'ACCEPT_CONFESS_USER_NAME', '接受交底人', context),
|
||||
|
||||
// 作业负责人意见
|
||||
if (FormUtils.hasValue(signs, 'CONFIRM')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('作业负责人意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['CONFIRM'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('CONFIRM', 'CONFIRM_USER_NAME', '作业负责人', context),
|
||||
],
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
// 所在单位负责人意见
|
||||
if (FormUtils.hasValue(signs, 'LEADER')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('所在单位意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['LEADER'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('LEADER', 'LEADER_USER_NAME', '所在单位负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 安全管理部门负责人意见
|
||||
if (FormUtils.hasValue(signs, 'AUDIT')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('消防、安全管理部门意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['AUDIT'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('AUDIT', 'AUDIT_USER_NAME', '消防、安全管理部门负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 审批人意见
|
||||
if (FormUtils.hasValue(signs, 'APPROVE')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('审批人意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['APPROVE'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('APPROVE', 'APPROVE_USER_NAME', '审批负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('完工验收意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['ACCEPT'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('ACCEPT', 'ACCEPT_USER_NAME', '验收部门负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
onAnalyzeTap: () {},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
horizontal: 5,
|
||||
Column(
|
||||
children: [
|
||||
RepairedPhotoSection(
|
||||
|
|
@ -327,6 +328,7 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
onAiIdentify: () {},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
horizontal: 5,
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ class _CutroadListPageState extends State<CutroadListPage> {
|
|||
int code = 0;
|
||||
for (Map item in stepList) {
|
||||
if (item['STEP_NAME'] == widget.flow) {
|
||||
code = item['STEP_ID'] ?? 0;
|
||||
code = item['STEP_ID'] ?? '';
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
|
@ -112,7 +112,7 @@ class _CutroadListPageState extends State<CutroadListPage> {
|
|||
widget.flow == '气体检测' ? SessionService.instance.loginUserId : '',
|
||||
'STEP_NAME':
|
||||
(widget.flow == '提交申请' || widget.flow == '气体检测') ? '' : widget.flow,
|
||||
'STEP_CODE': code,
|
||||
'STEP_CODE': (widget.flow == '提交申请' || widget.flow == '气体检测') ? '' : code,
|
||||
'STEP_ID': sindex > 0 ? stepList[sindex]['STEP_ID'] : '',
|
||||
'KEYWORDS': searchKeywords,
|
||||
'WORK_STATE': '0',
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ enum EditUserType {
|
|||
ACCEPT_CONFESS('接受交底人单位', '接受交底人', true),
|
||||
CONFIRM('作业负责人单位', '作业负责人', true),
|
||||
LEADER('所在单位', '所在单位负责人', true),
|
||||
AUDIT('消防、安全管理部门', '安全管理部门负责人', true),
|
||||
AUDIT('消防、安全管理部门', '消防、安全管理部门负责人', true),
|
||||
APPROVE('审批部门', '审批部门负责人', true),
|
||||
WORK_START('作业开始负责人单位', '作业开始负责人', true),
|
||||
WORK_END('作业结束负责人单位', '作业结束负责人', true),
|
||||
|
|
|
|||
|
|
@ -51,80 +51,80 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
// /// 签字
|
||||
// Future<void> _sign() async {
|
||||
// await NativeOrientation.setLandscape();
|
||||
// final path = await Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
// );
|
||||
// await NativeOrientation.setPortrait();
|
||||
// if (path != null) {
|
||||
// final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
// setState(() {
|
||||
// signImages.add(path);
|
||||
// signTimes.add(now);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 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(),
|
||||
// );
|
||||
// }
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
|
|
@ -143,10 +143,10 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
// if (signImages.isEmpty) {
|
||||
// ToastUtil.showNormal(context, '请签字');
|
||||
// return;
|
||||
// }
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -283,7 +283,6 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
CutroadFormBaseWork(
|
||||
|
|
@ -322,29 +321,30 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
// Divider(),
|
||||
// Column(
|
||||
// children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
// CustomButton(
|
||||
// text: '新增手写签字',
|
||||
// height: 36,
|
||||
// backgroundColor: Colors.green,
|
||||
// onPressed: () {
|
||||
// _sign();
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// if (signImages.isNotEmpty) _signListWidget(),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -52,101 +52,22 @@ class _CutroadKszyDetailState extends State<CutroadKszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请选择作业开始时间');
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
reasonText = await CustomAlertDialog.showInput(
|
||||
context,
|
||||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
|
@ -173,23 +94,22 @@ setState(() {
|
|||
barrierDismissible: false,
|
||||
);
|
||||
if (confirmed) {
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'cutroad',
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'cutroad',
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +188,6 @@ setState(() {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
CutroadFormBaseWork(
|
||||
|
|
@ -290,7 +209,8 @@ setState(() {
|
|||
child: ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: '作业开始时间',
|
||||
rightText: startTime.isEmpty ? '请选择' : startTime,
|
||||
isRight: true, isRequired: true
|
||||
isRight: true,
|
||||
isRequired: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -310,34 +230,35 @@ setState(() {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('* ', style: TextStyle(color: Colors.red)),
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
// Divider(),
|
||||
// Column(
|
||||
// children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Row(
|
||||
// children: [
|
||||
// Text('* ', style: TextStyle(color: Colors.red)),
|
||||
// ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
// ],
|
||||
// ),
|
||||
// CustomButton(
|
||||
// text: '新增手写签字',
|
||||
// height: 36,
|
||||
// backgroundColor: Colors.green,
|
||||
// onPressed: () {
|
||||
// _sign();
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// if (signImages.isNotEmpty) _signListWidget(),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ class _CutroadYsgdDetailState extends State<CutroadYsgdDetail> {
|
|||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'完工验收',
|
||||
'完工验收意见',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
isRequired: true,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import 'package:qhd_prevention/tools/h_colors.dart';
|
|||
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用动土作业明细表单组件(支持编辑/只读)
|
||||
class BreakgroundDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -472,7 +472,7 @@ class _BreakgroundDetailFormWidgetState
|
|||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/Break
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
class SpecialWorkFormBaseWork extends StatelessWidget {
|
||||
/// 基础数据
|
||||
|
|
@ -425,18 +425,18 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget(
|
||||
'WORK_START',
|
||||
'WORK_START_USER_NAME',
|
||||
'作业开始负责人',
|
||||
context,
|
||||
),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
// // 作业开始负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
// signItemWidget(
|
||||
// 'WORK_START',
|
||||
// 'WORK_START_USER_NAME',
|
||||
// '作业开始负责人',
|
||||
// context,
|
||||
// ),
|
||||
//
|
||||
// // 作业结束负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
// signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
|
|
|
|||
|
|
@ -51,81 +51,6 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -143,10 +68,7 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -285,7 +207,6 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -323,29 +244,10 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -52,87 +52,9 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -268,7 +190,6 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -311,34 +232,11 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('* ', style: TextStyle(color: Colors.red)),
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用高处作业明细表单组件(支持编辑/只读)
|
||||
class HoistWorkDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -453,7 +453,7 @@ class _HoistworkDetailFormWidgetState extends State<HoistWorkDetailFormWidget> {
|
|||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
import 'HoistWorkDetailFormWidget.dart';
|
||||
|
||||
class SpecialWorkFormBaseWork extends StatelessWidget {
|
||||
|
|
@ -363,12 +363,12 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
// if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
// signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
//
|
||||
// // 作业结束负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
// signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
|
|
|
|||
|
|
@ -51,81 +51,6 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -143,10 +68,7 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -285,7 +207,6 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -322,30 +243,10 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
}
|
||||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,88 +51,9 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -268,7 +189,6 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -311,34 +231,11 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('* ', style: TextStyle(color: Colors.red)),
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用高处作业明细表单组件(支持编辑/只读)
|
||||
class HighWorkDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -242,7 +242,7 @@ class _HighWorkDetailFormWidgetState extends State<HighWorkDetailFormWidget> {
|
|||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
import 'HighWorkDetailFormWidget.dart';
|
||||
|
||||
class HighWorkFormBaseWork extends StatelessWidget {
|
||||
|
|
@ -360,13 +360,13 @@ class HighWorkFormBaseWork extends StatelessWidget {
|
|||
)
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
// // 作业开始负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
// signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
//
|
||||
// // 作业结束负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
// signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
|
|
|
|||
|
|
@ -51,81 +51,6 @@ class _HighworkJszyDetailState extends State<HighworkJszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -143,10 +68,7 @@ class _HighworkJszyDetailState extends State<HighworkJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -285,7 +207,6 @@ class _HighworkJszyDetailState extends State<HighworkJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
HighWorkFormBaseWork(
|
||||
|
|
@ -322,30 +243,10 @@ class _HighworkJszyDetailState extends State<HighworkJszyDetail> {
|
|||
}
|
||||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -52,87 +52,9 @@ class _HighworkKszyDetailState extends State<HighworkKszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -268,7 +190,6 @@ class _HighworkKszyDetailState extends State<HighworkKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
HighWorkFormBaseWork(
|
||||
|
|
@ -311,34 +232,11 @@ class _HighworkKszyDetailState extends State<HighworkKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('* ', style: TextStyle(color: Colors.red)),
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用明细表单组件(支持编辑/只读)
|
||||
class ElectricityDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -87,7 +87,7 @@ class _ElectricityDetailFormWidgetState extends State<ElectricityDetailFormWidge
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请单位:',
|
||||
label: '作业申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
|
|
@ -310,7 +310,7 @@ class _ElectricityDetailFormWidgetState extends State<ElectricityDetailFormWidge
|
|||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
import 'ElectricityDetailFormWidget.dart';
|
||||
|
||||
class SpecialWorkFormBaseWork extends StatelessWidget {
|
||||
|
|
@ -363,17 +363,16 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
)
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'MONITOR'))
|
||||
signItemWidget('MONITOR', 'MONITOR_USER_NAME', '动火前在岗班长', context),
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
// if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
// signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
//
|
||||
// // 作业结束负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
// signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
|
|
|
|||
|
|
@ -51,81 +51,6 @@ class _ElectricityJszyDetailState extends State<ElectricityJszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -143,10 +68,7 @@ class _ElectricityJszyDetailState extends State<ElectricityJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -285,7 +207,6 @@ class _ElectricityJszyDetailState extends State<ElectricityJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -329,29 +250,10 @@ class _ElectricityJszyDetailState extends State<ElectricityJszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,88 +51,9 @@ class _ElectricityKszyDetailState extends State<ElectricityKszyDetail> {
|
|||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -280,7 +201,6 @@ class _ElectricityKszyDetailState extends State<ElectricityKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -323,29 +243,10 @@ class _ElectricityKszyDetailState extends State<ElectricityKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
|
|
@ -6,9 +7,10 @@ import 'package:qhd_prevention/customWidget/custom_button.dart';
|
|||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
class MBCD_data_model {
|
||||
String BOARD_MATERIAL;
|
||||
|
|
@ -46,12 +48,17 @@ class BlindboardDetailFormWidget extends StatefulWidget {
|
|||
|
||||
final bool isEditable;
|
||||
final VoidCallback onChooseLevel;
|
||||
// 新增
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onChooseVideoManager;/// 承包商
|
||||
|
||||
// 新增
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
|
||||
|
|
@ -64,12 +71,16 @@ class BlindboardDetailFormWidget extends StatefulWidget {
|
|||
final TextEditingController? relatedController;
|
||||
final TextEditingController? riskController;
|
||||
|
||||
/// 其他签字数据
|
||||
final signs;
|
||||
|
||||
const BlindboardDetailFormWidget({
|
||||
Key? key,
|
||||
required this.pd,
|
||||
required this.isEditable,
|
||||
required this.onChooseLevel,
|
||||
required this.boardList,
|
||||
|
||||
/// 新增
|
||||
this.onChooseVideoManager,
|
||||
this.onContractorHandle,
|
||||
|
|
@ -82,6 +93,7 @@ class BlindboardDetailFormWidget extends StatefulWidget {
|
|||
this.pressureController,
|
||||
this.relatedController,
|
||||
this.riskController,
|
||||
this.signs = null,
|
||||
}) : assert(
|
||||
!isEditable ||
|
||||
(nameController != null &&
|
||||
|
|
@ -115,15 +127,196 @@ class _BlindboardDetailFormWidgetState
|
|||
}
|
||||
}
|
||||
|
||||
Widget _itemContainer(Widget child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget signItemWidget(String signKey, BuildContext context) {
|
||||
return _itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (FormUtils.hasValue(widget.signs, signKey))
|
||||
...((widget.signs[signKey] as List<dynamic>)
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((item) {
|
||||
// 解析 SIGN_PATH
|
||||
List<String> signPaths = [];
|
||||
final rawSP =
|
||||
FormUtils.hasValue(item, 'SIGN_PATH')
|
||||
? item['SIGN_PATH']
|
||||
: null;
|
||||
if (rawSP is String && rawSP.isNotEmpty) {
|
||||
signPaths = rawSP.split(',').map((s) => s.trim()).toList();
|
||||
} else if (rawSP is List) {
|
||||
signPaths = rawSP.cast<String>();
|
||||
}
|
||||
|
||||
// 解析 SIGN_TIME 保留空格
|
||||
List<String> signTimes = [];
|
||||
final rawST =
|
||||
FormUtils.hasValue(item, 'SIGN_TIME')
|
||||
? item['SIGN_TIME']
|
||||
: null;
|
||||
if (rawST is String && rawST.isNotEmpty) {
|
||||
signTimes = rawST.split(',');
|
||||
} else if (rawST is List) {
|
||||
signTimes = rawST.cast<String>();
|
||||
}
|
||||
|
||||
final pairCount = math.min(
|
||||
signPaths.length,
|
||||
signTimes.length,
|
||||
);
|
||||
|
||||
// 解析 IMG_PATH, 最多 2 张
|
||||
List<String> imgPaths = [];
|
||||
final rawIP =
|
||||
FormUtils.hasValue(item, 'IMG_PATH')
|
||||
? item['IMG_PATH']
|
||||
: null;
|
||||
if (rawIP is String && rawIP.isNotEmpty) {
|
||||
imgPaths =
|
||||
rawIP.split(',').map((s) => s.trim()).take(2).toList();
|
||||
} else if (rawIP is List) {
|
||||
imgPaths = rawIP.cast<String>().take(2).toList();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8.0,
|
||||
horizontal: 10,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (imgPaths.isNotEmpty) ...[
|
||||
Row(
|
||||
children:
|
||||
imgPaths.map((p) {
|
||||
final fullUrl = '${ApiService.baseImgPath}$p';
|
||||
return GestureDetector(
|
||||
onTap:
|
||||
() => presentOpaque(
|
||||
SingleImageViewer(imageUrl: fullUrl),
|
||||
context,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 8.0,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(
|
||||
Icons.broken_image,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
|
||||
// 签名及时间
|
||||
...List.generate(pairCount, (index) {
|
||||
final imgPath = signPaths[index];
|
||||
final timeLabel = signTimes[index];
|
||||
final fullUrl = '${ApiService.baseImgPath}$imgPath';
|
||||
const imageWidth = 200.0;
|
||||
const imageHeight = 100.0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => presentOpaque(
|
||||
SingleImageViewer(
|
||||
imageUrl: fullUrl,
|
||||
),
|
||||
context,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(
|
||||
Icons.broken_image,
|
||||
size: 60,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: imageHeight,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Text(
|
||||
timeLabel,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (index < pairCount - 1)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: Divider(height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
.toList()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final boardList = widget.boardList;
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
widget.pd['LATITUDE_LONGITUDE'] =
|
||||
'${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
|
|
@ -142,7 +335,7 @@ class _BlindboardDetailFormWidgetState
|
|||
const Divider(),
|
||||
],
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请部门:',
|
||||
label: '作业申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
|
|
@ -152,16 +345,24 @@ class _BlindboardDetailFormWidgetState
|
|||
isEditable: false,
|
||||
text: pd['APPLY_USER_NAME'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_DEPARTMENT_NAME')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '作业单位:',
|
||||
isEditable: false,
|
||||
text: pd['CONFIRM_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业方式:',
|
||||
label: '作业类别:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseLevel,
|
||||
text: _getWorkLevelText(pd['WORK_TYPE']),
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '设备管道名称:',
|
||||
label: '设备、管道名称:',
|
||||
isEditable: widget.isEditable,
|
||||
controller: widget.nameController,
|
||||
text: pd['NAME'] ?? '',
|
||||
|
|
@ -205,7 +406,10 @@ class _BlindboardDetailFormWidgetState
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.createBuildSimpleSection('盲板抽堵参数', horPadding: 0),
|
||||
ListItemFactory.createBuildSimpleSection(
|
||||
'盲板抽堵参数',
|
||||
horPadding: 0,
|
||||
),
|
||||
if (widget.isEditable)
|
||||
CustomButton(
|
||||
onPressed: () {
|
||||
|
|
@ -237,6 +441,7 @@ class _BlindboardDetailFormWidgetState
|
|||
),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '材质:',
|
||||
isRequired: widget.isEditable,
|
||||
isEditable: widget.isEditable,
|
||||
text: board['BOARD_MATERIAL'],
|
||||
hintText: '请输入材质',
|
||||
|
|
@ -245,12 +450,12 @@ class _BlindboardDetailFormWidgetState
|
|||
board['BOARD_MATERIAL'] = val;
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '规格:',
|
||||
isRequired: widget.isEditable,
|
||||
isEditable: widget.isEditable,
|
||||
text: board['BOARD_SPECIFICATION'],
|
||||
onChanged: (val) {
|
||||
|
|
@ -259,13 +464,13 @@ class _BlindboardDetailFormWidgetState
|
|||
});
|
||||
},
|
||||
hintText: '请输入规格',
|
||||
isRequired: false,
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '编号:',
|
||||
isEditable: widget.isEditable,
|
||||
isRequired: widget.isEditable,
|
||||
text: board['BOARD_NO'],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
|
|
@ -273,35 +478,99 @@ class _BlindboardDetailFormWidgetState
|
|||
});
|
||||
},
|
||||
hintText: '请输入编号',
|
||||
isRequired: false,
|
||||
),
|
||||
if (boardList.indexOf(board) != boardList.length-1)
|
||||
if (boardList.indexOf(board) != boardList.length - 1)
|
||||
const Divider(),
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
const Divider(),
|
||||
if (!widget.isEditable && FormUtils.hasValue(pd, 'BOARD_PATH')) ...[
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '盲板抽堵位置及安全措施:',
|
||||
isEditable: false,
|
||||
text:'',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'WORK_START_DATE')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '盲板作业实施时间:',
|
||||
isEditable: widget.isEditable,
|
||||
text: pd['WORK_START_DATE'] ?? '',
|
||||
),
|
||||
] else ...[
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
mode: BottomPickerMode.dateTimeWithSeconds,
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
],
|
||||
if (!widget.isEditable && FormUtils.hasValue(pd, 'BOARD_PATH')) ...[
|
||||
ItemListWidget.twoRowTitleAndImages(
|
||||
onTapCallBack: (val) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: val), context);
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
title: '断路地段示意图:',
|
||||
imageUrls: pd['BOARD_PATH'],
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
],
|
||||
if (!widget.isEditable &&
|
||||
FormUtils.hasValue(pd, 'BOARD_PATH_NO'))
|
||||
ConfirmWithSignWidget(
|
||||
signs: widget.signs,
|
||||
pd: pd,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
sectionKey: 'WORKSHOP',
|
||||
nameKey: '',
|
||||
headerTitle: '盲板位置图(可另附图)及编号',
|
||||
imgsKey: 'BOARD_PATH',
|
||||
contentKey: 'BOARD_PATH_NO',
|
||||
roleTitle: '',
|
||||
),
|
||||
|
||||
if (FormUtils.hasValue(pd, 'CONFIRM_USER_NAME') && !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
SignRowImageTitle(label: '作业负责人:', signKey: 'CONFIRM', signs: widget.signs, text: pd['CONFIRM_USER_NAME']??'',),
|
||||
],
|
||||
if (FormUtils.hasValue(pd, 'WORK_USER_USER_NAME') && !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
SignRowImageTitle(label: '作业人:', signKey: 'WORK_USER', signs: widget.signs, text: pd['WORK_USER_USER_NAME']??'',),
|
||||
],
|
||||
if (FormUtils.hasValue(pd, 'GUARDIAN_USER_NAME') && !widget.isEditable) ...[
|
||||
const Divider(),
|
||||
SignRowImageTitle(label: '监护人:', signKey: 'GUARDIAN', signs: widget.signs, text: pd['GUARDIAN_USER_NAME']??'',),
|
||||
],
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号',
|
||||
isRequired: false,
|
||||
|
|
@ -352,75 +621,11 @@ class _BlindboardDetailFormWidgetState
|
|||
controller: widget.riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
mode: BottomPickerMode.dateTimeWithSeconds,
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
@ -459,10 +664,26 @@ class _BlindboardDetailFormWidgetState
|
|||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
onTap: widget.onWorkAreaLocationHandle ?? () {},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? (widget.isEditable ? '' : '无'),
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/mbcd_work/Bli
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
class BlindboardFormBaseWork extends StatelessWidget {
|
||||
/// 基础数据
|
||||
|
|
@ -231,6 +231,7 @@ class BlindboardFormBaseWork extends StatelessWidget {
|
|||
pd: pd,
|
||||
isEditable: isEditable,
|
||||
onChooseLevel: onChooseLevel,
|
||||
signs: signs,
|
||||
),
|
||||
|
||||
// 2. 安全防护措施
|
||||
|
|
@ -265,134 +266,44 @@ class BlindboardFormBaseWork extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
|
||||
// 4. 各环节签字及意见
|
||||
if (FormUtils.hasValue(signs, 'GUARDIAN'))
|
||||
signItemWidget('GUARDIAN', 'GUARDIAN_USER_NAME', '监护人', context),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'CONFESS',
|
||||
nameKey: 'CONFESS_USER_NAME',
|
||||
headerTitle: '安全交底人',
|
||||
roleTitle: '安全交底人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'ACCEPT_CONFESS',
|
||||
nameKey: 'ACCEPT_CONFESS_USER_NAME',
|
||||
headerTitle: '接受交底人',
|
||||
roleTitle: '',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'CONFIRM',
|
||||
nameKey: 'CONFIRM_USER_NAME',
|
||||
headerTitle: '作业负责人意见',
|
||||
roleTitle: '作业负责人',
|
||||
),
|
||||
ConfirmWithSignWidget(
|
||||
signs: signs,
|
||||
pd: pd,
|
||||
baseImgPath: baseImgPath,
|
||||
sectionKey: 'LEADER',
|
||||
nameKey: 'LEADER_USER_NAME',
|
||||
headerTitle: '所在单位负责人意见',
|
||||
roleTitle: '所在单位负责人',
|
||||
),
|
||||
|
||||
if (FormUtils.hasValue(signs, 'CONFESS'))
|
||||
signItemWidget('CONFESS', 'CONFESS_USER_NAME', '安全交底人', context),
|
||||
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT_CONFESS'))
|
||||
signItemWidget('ACCEPT_CONFESS', 'ACCEPT_CONFESS_USER_NAME', '接受交底人', context),
|
||||
|
||||
// 作业负责人意见
|
||||
if (FormUtils.hasValue(signs, 'CONFIRM')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('作业负责人意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['CONFIRM'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('CONFIRM', 'CONFIRM_USER_NAME', '作业负责人', context),
|
||||
],
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
// 所在单位负责人意见
|
||||
if (FormUtils.hasValue(signs, 'LEADER')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('所在单位意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['LEADER'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('LEADER', 'LEADER_USER_NAME', '所在单位负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 安全管理部门负责人意见
|
||||
if (FormUtils.hasValue(signs, 'AUDIT')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('安全管理部门意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['AUDIT'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('AUDIT', 'AUDIT_USER_NAME', '安全管理部门负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 审批人意见
|
||||
if (FormUtils.hasValue(signs, 'APPROVE')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('审批人意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['APPROVE'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('APPROVE', 'APPROVE_USER_NAME', '审批负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
Column(
|
||||
children: [
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('完工验收意见'),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(signs['ACCEPT'][0]['DESCR'] ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1,),
|
||||
signItemWidget('ACCEPT', 'ACCEPT_USER_NAME', '验收部门负责人', context),
|
||||
],
|
||||
)
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ class _BlindboardListPageState extends State<BlindboardListPage> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
_fetchSteps();
|
||||
_fetchData();
|
||||
_scrollController.addListener(_onScroll);
|
||||
}
|
||||
|
||||
|
|
@ -83,12 +82,12 @@ class _BlindboardListPageState extends State<BlindboardListPage> {
|
|||
Future<void> _fetchSteps() async {
|
||||
try {
|
||||
final response = await ApiService.searchGetTaskSetps(13);
|
||||
setState(() {
|
||||
stepList = [
|
||||
{'STEP_NAME': '全部', 'STEP_ID': ''},
|
||||
...response['list'] ?? [],
|
||||
];
|
||||
});
|
||||
stepList = [
|
||||
{'STEP_NAME': '全部', 'STEP_ID': ''},
|
||||
...response['list'] ?? [],
|
||||
];
|
||||
_fetchData();
|
||||
|
||||
} catch (e) {
|
||||
print('Error fetching steps: $e');
|
||||
}
|
||||
|
|
@ -97,7 +96,12 @@ class _BlindboardListPageState extends State<BlindboardListPage> {
|
|||
Future<void> _fetchData() async {
|
||||
if (isLoading) return;
|
||||
setState(() => isLoading = true);
|
||||
|
||||
int code = 0;
|
||||
for (Map item in stepList) {
|
||||
if (item['STEP_NAME'] == widget.flow) {
|
||||
code = item['STEP_ID'] ?? '';
|
||||
}
|
||||
}
|
||||
try {
|
||||
final data = {
|
||||
'CREATOR':
|
||||
|
|
@ -106,6 +110,7 @@ class _BlindboardListPageState extends State<BlindboardListPage> {
|
|||
widget.flow == '气体检测' ? SessionService.instance.loginUserId : '',
|
||||
'STEP_NAME':
|
||||
(widget.flow == '提交申请' || widget.flow == '气体检测') ? '' : widget.flow,
|
||||
'STEP_CODE': (widget.flow == '提交申请' || widget.flow == '气体检测') ? '' : code,
|
||||
'STEP_ID': sindex > 0 ? stepList[sindex]['STEP_ID'] : '',
|
||||
'KEYWORDS': searchKeywords,
|
||||
'WORK_STATE': '0',
|
||||
|
|
@ -177,7 +182,7 @@ class _BlindboardListPageState extends State<BlindboardListPage> {
|
|||
case '提交申请':
|
||||
await pushPage(BlindboardApplyDetail(BLINDBOARD_ID: item['BLINDBOARD_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '车间人员签字':
|
||||
case '盲板抽堵位置图负责人':
|
||||
await pushPage(BlindboardCjryDetail(BLINDBOARD_ID: item['BLINDBOARD_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '设置安全措施确认人':
|
||||
|
|
@ -225,7 +230,6 @@ class _BlindboardListPageState extends State<BlindboardListPage> {
|
|||
}
|
||||
setState(() {
|
||||
_fetchSteps();
|
||||
_fetchData();
|
||||
});
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
|||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/CutroadFormBaseWork.dart';
|
||||
|
||||
/// 本地路径 + 线上路径模型
|
||||
class ImageData {
|
||||
String localPath;
|
||||
|
|
@ -23,7 +24,8 @@ class ImageData {
|
|||
|
||||
ImageData({required this.localPath, required this.serverPath});
|
||||
}
|
||||
///车间人员签字
|
||||
|
||||
///盲板抽堵位置图负责人
|
||||
class BlindboardCjryDetail extends StatefulWidget {
|
||||
const BlindboardCjryDetail({
|
||||
super.key,
|
||||
|
|
@ -48,6 +50,7 @@ class _BlindboardCjryDetailState extends State<BlindboardCjryDetail> {
|
|||
// final TextEditingController _contentController = TextEditingController();
|
||||
|
||||
late List<ImageData> imgList = [];
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
|
@ -70,7 +73,7 @@ class _BlindboardCjryDetailState extends State<BlindboardCjryDetail> {
|
|||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
|
|
@ -144,21 +147,22 @@ setState(() {
|
|||
String reasonText = '';
|
||||
|
||||
if (status == '1') {
|
||||
// if (_contentController.text.trim().isEmpty) {
|
||||
// ToastUtil.showNormal(context, '盲板抽堵位置及安全措施');
|
||||
// return;
|
||||
// }
|
||||
if (imgList.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请上传盲板抽堵位置图');
|
||||
return;
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'BOARD_PATH_NO')) {
|
||||
ToastUtil.showNormal(context, '请输入盲板抽堵位置图编号');
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
reasonText = await CustomAlertDialog.showInput(
|
||||
context,
|
||||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
|
@ -168,7 +172,7 @@ setState(() {
|
|||
final serverPathString = imgList
|
||||
.map((e) => e.serverPath)
|
||||
.where((s) => s.isNotEmpty)
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.join(',');
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
|
|
@ -176,12 +180,13 @@ setState(() {
|
|||
// formData['WORK_CONTENT'] = _contentController.text.trim();
|
||||
formData['CONIMG_PATH'] = serverPathString;
|
||||
|
||||
formData['DESCR'] = FormUtils.hasValue(pd, 'DESCR') ?pd['DESCR']: '无';
|
||||
formData['DESCR'] = FormUtils.hasValue(pd, 'DESCR') ? pd['DESCR'] : '无';
|
||||
formData['BLINDBOARD_ID'] = pd['BLINDBOARD_ID'] ?? widget.BLINDBOARD_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['BOARD_PATH_NO'] = pd['BOARD_PATH_NO'];
|
||||
|
||||
final confirmed = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
|
|
@ -192,30 +197,32 @@ setState(() {
|
|||
barrierDismissible: false,
|
||||
);
|
||||
if (confirmed) {
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'blindboard',
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'blindboard',
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
LoadingDialogHelper.show();
|
||||
final data = await ApiService.getHomeworkFindById('blindboard', widget.BLINDBOARD_ID);
|
||||
final data = await ApiService.getHomeworkFindById(
|
||||
'blindboard',
|
||||
widget.BLINDBOARD_ID,
|
||||
);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
boardList = data['boardList'];
|
||||
|
|
@ -224,10 +231,11 @@ setState(() {
|
|||
_getMeasures(pd['BLINDBOARD_ID'] ?? '');
|
||||
});
|
||||
LoadingDialogHelper.hide();
|
||||
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures('blindboard',
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
'blindboard',
|
||||
homework_id.length > 0 ? homework_id : widget.BLINDBOARD_ID,
|
||||
);
|
||||
setState(() {
|
||||
|
|
@ -238,13 +246,15 @@ setState(() {
|
|||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished('blindboard',
|
||||
final data = await ApiService.listSignFinished(
|
||||
'blindboard',
|
||||
homework_id.length > 0 ? homework_id : widget.BLINDBOARD_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 拍照或选图后的回调
|
||||
Future<void> _onImageAdded(String localPath) async {
|
||||
// 上传到服务器
|
||||
|
|
@ -266,6 +276,7 @@ setState(() {
|
|||
imgList.remove(item);
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
|
|
@ -300,7 +311,7 @@ setState(() {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '车间人员签字'),
|
||||
appBar: MyAppbar(title: '盲板抽堵位置图负责人'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
|
|
@ -311,46 +322,63 @@ setState(() {
|
|||
pd: pd,
|
||||
boardList: boardList,
|
||||
isEditable: false,
|
||||
onChooseLevel: (){},
|
||||
onChooseLevel: () {},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
horizontal: 0,
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.headerTitle('盲板抽堵位置及安全措施'),
|
||||
Divider(),
|
||||
RepairedPhotoSection(
|
||||
title: '断路地段示意图:',
|
||||
title: '盲板抽堵位置图:',
|
||||
maxCount: 100,
|
||||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
isShowNum: false,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 12,
|
||||
isRequired: true,
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
);
|
||||
_onImageRemoved(item);
|
||||
},
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('车间人员'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '盲板抽堵位置图编号:',
|
||||
isEditable: true,
|
||||
isRequired: true,
|
||||
text: pd['BOARD_PATH_NO'],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
pd['BOARD_PATH_NO'] = val;
|
||||
});
|
||||
},
|
||||
hintText: '请输入盲板抽堵位置图编号',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('盲板抽堵位置图负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -51,82 +51,6 @@ class _BlindboardJszyDetailState extends State<BlindboardJszyDetail> {
|
|||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -144,10 +68,7 @@ class _BlindboardJszyDetailState extends State<BlindboardJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -287,7 +208,6 @@ class _BlindboardJszyDetailState extends State<BlindboardJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
BlindboardFormBaseWork(
|
||||
|
|
@ -325,30 +245,10 @@ class _BlindboardJszyDetailState extends State<BlindboardJszyDetail> {
|
|||
}
|
||||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -53,87 +53,9 @@ class _BlindboardKszyDetailState extends State<BlindboardKszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -270,7 +192,6 @@ class _BlindboardKszyDetailState extends State<BlindboardKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
BlindboardFormBaseWork(
|
||||
|
|
@ -314,34 +235,10 @@ class _BlindboardKszyDetailState extends State<BlindboardKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('* ', style: TextStyle(color: Colors.red)),
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart';
|
|||
import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart';
|
||||
|
||||
enum EditUserType {
|
||||
WORKSHOP('车间人员单位', '车间人员', true),
|
||||
WORKSHOP('盲板抽堵位置图负责单位', '盲板抽堵位置图负责人', true),
|
||||
GUARDIAN('监护人单位', '监护人', true),
|
||||
CONFESS('安全交底人单位', '安全交底人', true),
|
||||
ACCEPT_CONFESS('接受交底人单位', '接受交底人', true),
|
||||
|
|
@ -28,7 +28,7 @@ enum EditUserType {
|
|||
CONFIRM('作业负责人单位', '作业负责人', true),
|
||||
LEADER('所在单位', '所在单位负责人', true),
|
||||
WORK_START('实际作业开始负责人单位', '实际作业开始负责人', true),
|
||||
WORK_END('作业结束负责人单位', '作业结束负责人', true),
|
||||
WORK_END('实际作业结束负责人单位', '实际作业结束负责人', true),
|
||||
ACCEPT('验收部门', '验收部门负责人', true);
|
||||
|
||||
/// 对应的单位显示名
|
||||
|
|
@ -71,7 +71,7 @@ class _BlindboardApplyDetailState extends State<BlindboardApplyDetail> {
|
|||
/// 承包商列表
|
||||
late List<dynamic> unitAllList = [];
|
||||
/// 作业区域列表
|
||||
late List<Map<String, dynamic>> workAreaList = [];
|
||||
late List<dynamic> workAreaList = [];
|
||||
/// --------------------------------------
|
||||
late Map<String, dynamic> signs = {};
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
|
|
|
|||
|
|
@ -47,13 +47,23 @@ class _BlindboardSetSafeDetailState extends State<BlindboardSetSafeDetail> {
|
|||
late List<MeasureItem> measuresListCopy = [];
|
||||
List<String> imagePaths = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
addMeasuresListCopy();
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
'blindboard',
|
||||
homework_id.length > 0 ? homework_id : widget.BLINDBOARD_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
String measuresListToJson() {
|
||||
final List<Map<String, dynamic>> jsonList =
|
||||
measuresListCopy.map((item) => item.toJson()).toList();
|
||||
|
|
@ -345,8 +355,10 @@ class _BlindboardSetSafeDetailState extends State<BlindboardSetSafeDetail> {
|
|||
setState(() {
|
||||
pd = data['pd'];
|
||||
boardList = data['boardList'];
|
||||
_getSigns(widget.BLINDBOARD_ID);
|
||||
_getMeasures();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Future<void> _getMeasures() async {
|
||||
|
|
@ -564,6 +576,7 @@ class _BlindboardSetSafeDetailState extends State<BlindboardSetSafeDetail> {
|
|||
pd: pd,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
signs: signs,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
_setSafeDetailWidget(),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用明细表单组件(支持编辑/只读)
|
||||
class SpaceWorkDetailFormWidget extends StatefulWidget {
|
||||
|
|
@ -86,7 +86,7 @@ class _SpaceWorkDetailFormWidgetState extends State<SpaceWorkDetailFormWidget> {
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请单位:',
|
||||
label: '作业申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
|
|
@ -297,7 +297,7 @@ class _SpaceWorkDetailFormWidgetState extends State<SpaceWorkDetailFormWidget> {
|
|||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import '../../../../../../customWidget/ItemWidgetFactory.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart';
|
||||
import 'SpaceWorkDetailFormWidget.dart';
|
||||
|
||||
class SpecialWorkFormBaseWork extends StatelessWidget {
|
||||
|
|
@ -363,17 +363,16 @@ class SpecialWorkFormBaseWork extends StatelessWidget {
|
|||
)
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'MONITOR'))
|
||||
signItemWidget('MONITOR', 'MONITOR_USER_NAME', '动火前在岗班长', context),
|
||||
|
||||
// 作业开始负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
|
||||
// 作业结束负责人签字
|
||||
if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
// if (FormUtils.hasValue(signs, 'WORK_START'))
|
||||
// signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context),
|
||||
//
|
||||
// // 作业结束负责人签字
|
||||
// if (FormUtils.hasValue(signs, 'WORK_END'))
|
||||
// signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context),
|
||||
|
||||
// 完工验收意见和签字
|
||||
if (FormUtils.hasValue(signs, 'ACCEPT')) ...[
|
||||
|
|
|
|||
|
|
@ -51,82 +51,6 @@ class _SpaceworkJszyDetailState extends State<SpaceworkJszyDetail> {
|
|||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
|
|
@ -144,10 +68,6 @@ class _SpaceworkJszyDetailState extends State<SpaceworkJszyDetail> {
|
|||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
|
|
@ -286,7 +206,6 @@ class _SpaceworkJszyDetailState extends State<SpaceworkJszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -332,30 +251,10 @@ class _SpaceworkJszyDetailState extends State<SpaceworkJszyDetail> {
|
|||
}
|
||||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -54,87 +54,9 @@ class _SpaceworkKszyDetailState extends State<SpaceworkKszyDetail> {
|
|||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
|
|
@ -279,7 +201,6 @@ class _SpaceworkKszyDetailState extends State<SpaceworkKszyDetail> {
|
|||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
|
|
@ -325,29 +246,10 @@ class _SpaceworkKszyDetailState extends State<SpaceworkKszyDetail> {
|
|||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12,),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class _WorkTabMbcdListState extends State<WorkTabMbcdList> {
|
|||
},
|
||||
{
|
||||
"icon": "assets/icon-apps/icon-yh-1.png",
|
||||
"title": "车间人员\n签字",
|
||||
"title": "盲板抽堵位置图\n负责人意见",
|
||||
"unreadCount": eight_work_count['WORKSHOP'] ?? '0',
|
||||
},
|
||||
{
|
||||
|
|
@ -105,7 +105,7 @@ class _WorkTabMbcdListState extends State<WorkTabMbcdList> {
|
|||
String title = '';
|
||||
switch (index) {
|
||||
case 0: title = '提交申请'; break;
|
||||
case 1: title = '车间人员签字'; break;
|
||||
case 1: title = '盲板抽堵位置图负责人'; break;
|
||||
case 2: title = '设置安全措施确认人'; break;
|
||||
case 3: title = '安全措施确认'; break;
|
||||
case 4: title = '监护人签字'; break;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import 'dart:ffi';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
|
||||
|
||||
Color h_backGroundColor() => Color(0xFFF1F1F1);
|
||||
|
||||
List<Color> riskLevelTextColors() {
|
||||
return [Color(0xFFE54D42),Color(0xFFF37B1D),Color(0xFFF9BD08),Color(0xFF3281FF)];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ class FormUtils {
|
|||
/// - String:去掉首尾空白后非空 -> true
|
||||
/// - Iterable / Map:非空 -> true
|
||||
/// - 其它类型(int、double、bool 等)只要不为 null 就算有值 -> true
|
||||
static bool hasValue(Map<String, dynamic> data, String key) {
|
||||
static bool hasValue(Map<dynamic, dynamic> data, String key) {
|
||||
if (!data.containsKey(key)) return false;
|
||||
final val = data[key];
|
||||
if (val == null) return false;
|
||||
|
|
|
|||
Loading…
Reference in New Issue