八项作业动火作业流程完成
parent
3a00844a57
commit
ff39ac4737
|
|
@ -7,6 +7,7 @@ import UIKit
|
|||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class ListItemFactory {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
_truncateText(rightText,14),
|
||||
_truncateText(rightText,100),
|
||||
// rightText,
|
||||
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||
),
|
||||
|
|
@ -233,7 +233,7 @@ class ListItemFactory {
|
|||
);
|
||||
}
|
||||
|
||||
/// 分类头部
|
||||
/// YesNo
|
||||
static Widget createYesNoSection({
|
||||
required String title,
|
||||
required String yesLabel,
|
||||
|
|
@ -317,7 +317,23 @@ class ListItemFactory {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 单纯标题
|
||||
static Widget headerTitle(String title) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
/// 扩展项(根据需求自定义)
|
||||
static Widget createCustomItem({
|
||||
required Widget child,
|
||||
|
|
@ -344,8 +360,10 @@ class ListItemFactory {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题
|
||||
HhTextStyleUtils.mainTitle(label, fontSize: 15),
|
||||
const SizedBox(height: 8),
|
||||
// 文本输入框,清除默认内边距以与标题左对齐
|
||||
Expanded(
|
||||
child: TextField(
|
||||
autofocus: false,
|
||||
|
|
@ -357,6 +375,7 @@ class ListItemFactory {
|
|||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero, // 去除默认内边距
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -365,7 +384,6 @@ class ListItemFactory {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/// 分类头部
|
||||
static Widget createYesNoSectionTwo({
|
||||
required String title,
|
||||
|
|
|
|||
|
|
@ -244,8 +244,8 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 整改后照片上传区域组件
|
||||
class RepairedPhotoSection extends StatelessWidget {
|
||||
/// 照片上传区域组件
|
||||
class RepairedPhotoSection extends StatefulWidget {
|
||||
final int maxCount;
|
||||
final MediaType mediaType;
|
||||
final String title;
|
||||
|
|
@ -271,54 +271,85 @@ class RepairedPhotoSection extends StatelessWidget {
|
|||
this.onMediaRemoved,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_RepairedPhotoSectionState createState() => _RepairedPhotoSectionState();
|
||||
}
|
||||
|
||||
class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
|
||||
late List<String> _mediaPaths;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 初始化已有地址
|
||||
_mediaPaths = widget.initialMediaPaths?.take(widget.maxCount).toList() ?? [];
|
||||
// 初始回调
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onChanged(
|
||||
_mediaPaths.map((p) => File(p)).toList(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.only(left: 5, right: 10),
|
||||
padding: const EdgeInsets.only(left: 0, right: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
|
||||
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
|
||||
child: ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: title,
|
||||
rightText: '${initialMediaPaths?.length ?? 0}/$maxCount',
|
||||
leftText: widget.title,
|
||||
// 动态展示已选数量
|
||||
rightText: '${_mediaPaths.length}/${widget.maxCount}',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
|
||||
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
|
||||
child: MediaPickerRow(
|
||||
maxCount: maxCount,
|
||||
mediaType: mediaType,
|
||||
initialMediaPaths: initialMediaPaths,
|
||||
onChanged: onChanged,
|
||||
onMediaAdded: onMediaAdded,
|
||||
onMediaRemoved: onMediaRemoved,
|
||||
maxCount: widget.maxCount,
|
||||
mediaType: widget.mediaType,
|
||||
initialMediaPaths: _mediaPaths,
|
||||
// 统一在 onChanged 中维护 mediaPaths,避免重复添加
|
||||
onChanged: (files) {
|
||||
final newPaths = files.map((f) => f.path).toList();
|
||||
setState(() {
|
||||
_mediaPaths = newPaths;
|
||||
});
|
||||
widget.onChanged(files);
|
||||
},
|
||||
// 仅回调外部,不再修改本地 _mediaPaths
|
||||
onMediaAdded: widget.onMediaAdded,
|
||||
onMediaRemoved: widget.onMediaRemoved,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (isShowAI)
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onAiIdentify,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFDFEAFF),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset('assets/images/ai_img.png', width: 20),
|
||||
const SizedBox(width: 5),
|
||||
const Text('AI隐患识别与处理'),
|
||||
],
|
||||
),
|
||||
if (widget.isShowAI)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
|
||||
child: GestureDetector(
|
||||
onTap: widget.onAiIdentify,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFDFEAFF),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset('assets/images/ai_img.png', width: 20),
|
||||
const SizedBox(width: 5),
|
||||
const Text('AI隐患识别与处理'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -801,7 +801,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
);
|
||||
}
|
||||
|
||||
/// 上传图片
|
||||
/// 上传图片或视频
|
||||
static Future<Map<String, dynamic>> uploadSaveFile (
|
||||
String filePath,
|
||||
) async {
|
||||
|
|
@ -842,6 +842,9 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
// 把文件路径填成 MultipartFile
|
||||
for (var i = 0; i < filePaths.length; i++) {
|
||||
final path = filePaths[i];
|
||||
if (path.contains('uploadFiles')) {
|
||||
continue;
|
||||
}
|
||||
data['file$i'] = await MultipartFile.fromFile(
|
||||
path,
|
||||
filename: path.split(Platform.pathSeparator).last,
|
||||
|
|
|
|||
|
|
@ -430,4 +430,14 @@ class ItemListWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
static Widget itemContainer(Widget child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import '../../../../../customWidget/date_picker_dialog.dart';
|
||||
import '../../../../../http/ApiService.dart';
|
||||
import '../../../../../tools/tools.dart';
|
||||
import 'dh_work_detai/hotwork_apply_detail.dart';
|
||||
|
||||
import '../../../../../../customWidget/date_picker_dialog.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import 'hotwork_apply_detail.dart';
|
||||
|
||||
/// 表格组件,用于展示“安全防护措施”列表
|
||||
import 'package:flutter/material.dart';
|
||||
/// 八项作业先关组件,用于展示“安全防护措施”列表
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// 主安全措施列表组件,支持编辑与只读模式
|
||||
/// 主安全措施表格组件,支持编辑与只读模式
|
||||
class MeasuresListWidget extends StatelessWidget {
|
||||
const MeasuresListWidget({
|
||||
super.key,
|
||||
|
|
@ -22,6 +19,8 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
required this.baseImgPath,
|
||||
required this.isAllowEdit,
|
||||
this.onSign,
|
||||
this.isShowSign = false,
|
||||
|
||||
});
|
||||
/// 接口返回的原始 Map 列表
|
||||
final List<Map<String, dynamic>> measuresList;
|
||||
|
|
@ -35,6 +34,8 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
/// 签字按钮回调,回调当前行 item
|
||||
final void Function(Map<String, dynamic> item)? onSign;
|
||||
|
||||
/// 操作那里是否显示签名图片
|
||||
final bool isShowSign;
|
||||
|
||||
|
||||
@override
|
||||
|
|
@ -128,17 +129,15 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
isAllowEdit
|
||||
? TextButton(
|
||||
onPressed: () {
|
||||
if ((item['STATUS'] as String?) != '-1') {
|
||||
onSign?.call(item);
|
||||
}
|
||||
onSign?.call(item);
|
||||
},
|
||||
child: Text(
|
||||
(item['SIGN_ITEM'] ?? '').toString().isNotEmpty
|
||||
? '已签字'
|
||||
: '签字',
|
||||
style: TextStyle(
|
||||
color: (item['STATUS'] as String?) == '-1'
|
||||
? Colors.grey
|
||||
color: (item['SIGN_ITEM'] ?? '').toString().isNotEmpty
|
||||
? Colors.grey.shade600
|
||||
: Colors.blue,
|
||||
),
|
||||
),
|
||||
|
|
@ -156,7 +155,7 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
|
||||
// 操作图片
|
||||
if (item.containsKey('IMG_PATH') &&
|
||||
(item['IMG_PATH'] as String).isNotEmpty)
|
||||
(item['IMG_PATH'] as String).isNotEmpty && isShowSign)
|
||||
..._buildImageRows(
|
||||
context,
|
||||
(item['IMG_PATH'] as String).split(','),
|
||||
|
|
@ -258,15 +257,10 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
PageRouteBuilder(
|
||||
opaque: false,
|
||||
pageBuilder: (_, __, ___) =>
|
||||
SingleImageViewer(imageUrl: '$baseImgPath\$p'),
|
||||
),
|
||||
);
|
||||
presentOpaque(SingleImageViewer(imageUrl: '$baseImgPath$p'), context);
|
||||
|
||||
},
|
||||
child: Image.network('$baseImgPath\$p', width: 80, height: 80),
|
||||
child: Image.network('$baseImgPath$p', width: 80, height: 80),
|
||||
),
|
||||
if (time.isNotEmpty) ...[const SizedBox(width: 8), Text(time)],
|
||||
],
|
||||
|
|
@ -276,7 +270,6 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// 其他安全防护措施表格组件
|
||||
class OtherMeasuresWidget extends StatelessWidget {
|
||||
/// 其他安全防护措施数据列表
|
||||
|
|
@ -363,36 +356,71 @@ class OtherMeasuresWidget extends StatelessWidget {
|
|||
|
||||
/// 处理 SIGN_PATH 可能是 List 或 String
|
||||
Widget _buildSignImages(BuildContext context, Map<String, dynamic> item) {
|
||||
final dynamic raw = item['SIGN_PATH'];
|
||||
final rawPath = item['SIGN_PATH'];
|
||||
List<String> paths = [];
|
||||
if (raw is String && raw.isNotEmpty) {
|
||||
paths = [raw];
|
||||
} else if (raw is List) {
|
||||
paths = raw.cast<String>();
|
||||
if (rawPath is String && rawPath.isNotEmpty) {
|
||||
paths = [rawPath];
|
||||
} else if (rawPath is List) {
|
||||
paths = rawPath.whereType<String>().toList();
|
||||
}
|
||||
|
||||
// 健壮判断:SIGN_TIME 可能不存在或类型非 String/List
|
||||
List<String> times = [];
|
||||
if (item.containsKey('SIGN_TIME')) {
|
||||
final rawTime = item['SIGN_TIME'];
|
||||
if (rawTime is String && rawTime.isNotEmpty) {
|
||||
times = [rawTime];
|
||||
} else if (rawTime is List) {
|
||||
times = rawTime.whereType<String>().toList();
|
||||
}
|
||||
}
|
||||
|
||||
// 没有图片,直接返回空
|
||||
if (paths.isEmpty) return const SizedBox.shrink();
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children:
|
||||
paths.map((p) {
|
||||
return GestureDetector(
|
||||
|
||||
// 构造每一对“图片 + 时间”
|
||||
final children = <Widget>[];
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
final path = paths[i];
|
||||
// 时间对应同下标,越界则使用空字符串
|
||||
final timeLabel = (i < times.length) ? times[i] : '';
|
||||
|
||||
children.add(
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => SingleImageViewer(imageUrl: '$baseImgPath$p'),
|
||||
),
|
||||
);
|
||||
presentOpaque(SingleImageViewer(imageUrl: '$baseImgPath$path'), context);
|
||||
},
|
||||
child: Image.network(
|
||||
'$baseImgPath$p',
|
||||
'$baseImgPath$path',
|
||||
width: 60,
|
||||
height: 60,
|
||||
errorBuilder: (c, o, s) => const Icon(Icons.broken_image),
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => const Icon(Icons.broken_image),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
timeLabel,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black54),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (i != paths.length - 1) {
|
||||
children.add(const SizedBox(height: 8));
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -568,14 +596,9 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => SingleImageViewer(
|
||||
imageUrl: '$baseImgPath${signPaths[i]}',
|
||||
),
|
||||
),
|
||||
),
|
||||
() => presentOpaque(SingleImageViewer(
|
||||
imageUrl: '$baseImgPath${signPaths[i]}',
|
||||
), context),
|
||||
child: Image.network(
|
||||
'$baseImgPath${signPaths[i]}',
|
||||
width: 100,
|
||||
|
|
@ -664,14 +687,9 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
() => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => SingleImageViewer(
|
||||
imageUrl: '$baseImgPath${signPaths[i]}',
|
||||
),
|
||||
),
|
||||
),
|
||||
() => presentOpaque(SingleImageViewer(
|
||||
imageUrl: '$baseImgPath${signPaths[i]}',
|
||||
), context),
|
||||
child: Image.network(
|
||||
'$baseImgPath${signPaths[i]}',
|
||||
width: 100,
|
||||
|
|
@ -714,7 +732,6 @@ class SelectionPopup extends StatefulWidget {
|
|||
@override
|
||||
_SelectionPopupState createState() => _SelectionPopupState();
|
||||
}
|
||||
|
||||
class _SelectionPopupState extends State<SelectionPopup> {
|
||||
late List<Map<String, String>> workList;
|
||||
late String selectedWorkType;
|
||||
|
|
@ -986,3 +1003,5 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,399 @@
|
|||
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 'WorkDetailFormWidget.dart';
|
||||
|
||||
class SpecialWorkFormBaseWork extends StatelessWidget {
|
||||
/// 基础数据
|
||||
final Map<String, dynamic> pd;
|
||||
|
||||
/// 安全防护措施列表
|
||||
final List<Map<String, dynamic>> measuresList;
|
||||
|
||||
/// 其他签字数据
|
||||
final Map<String, dynamic> signs;
|
||||
|
||||
/// 图片前缀
|
||||
final String baseImgPath;
|
||||
|
||||
/// 是否可编辑
|
||||
final bool isEditable;
|
||||
|
||||
/// 回调:选择级别
|
||||
final VoidCallback onChooseLevel;
|
||||
|
||||
/// 回调:选择动火作业人员
|
||||
final VoidCallback onChooseHotworkUser;
|
||||
|
||||
/// 回调:分析按钮
|
||||
final VoidCallback onAnalyzeTap;
|
||||
|
||||
const SpecialWorkFormBaseWork({
|
||||
Key? key,
|
||||
required this.pd,
|
||||
required this.measuresList,
|
||||
required this.signs,
|
||||
required this.baseImgPath,
|
||||
this.isEditable = false,
|
||||
required this.onChooseLevel,
|
||||
required this.onChooseHotworkUser,
|
||||
required this.onAnalyzeTap,
|
||||
}) : super(key: key);
|
||||
|
||||
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,
|
||||
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 Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: Image.network(
|
||||
fullUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
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.cover,
|
||||
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(
|
||||
spacing: 12,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 1. 作业详情表单
|
||||
WorkDetailFormWidget(
|
||||
pd: pd,
|
||||
isEditable: isEditable,
|
||||
onChooseLevel: onChooseLevel,
|
||||
onChooseHotworkUser: onChooseHotworkUser,
|
||||
onAnalyzeTap: onAnalyzeTap,
|
||||
),
|
||||
|
||||
// 2. 安全防护措施
|
||||
if (measuresList.isNotEmpty)
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListItemFactory.createBuildSimpleSection('安全防护措施'),
|
||||
MeasuresListWidget(
|
||||
measuresList: measuresList,
|
||||
baseImgPath: baseImgPath,
|
||||
isAllowEdit: isEditable,
|
||||
isShowSign: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 3. 其他安全防护措施
|
||||
if (FormUtils.hasValue(signs, 'MEASURES_CONFIRM'))
|
||||
_itemContainer(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListItemFactory.createBuildSimpleSection('其他安全防护措施'),
|
||||
OtherMeasuresWidget(
|
||||
otherMeasures: signs['MEASURES_CONFIRM'],
|
||||
baseImgPath: baseImgPath,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 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),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
// 作业开始负责人签字
|
||||
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, '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),
|
||||
],
|
||||
)
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import '../../../../../tools/tools.dart';
|
||||
import '../../item_list_widget.dart';
|
||||
import '../special_Wrok/dh_work_detai/MeasuresListWidget.dart';
|
||||
import '../special_Wrok/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用明细表单组件
|
||||
/// 编辑模式时需传入对应的 TextEditingController;非编辑模式可不传
|
||||
|
|
@ -12,7 +12,6 @@ class WorkDetailFormWidget extends StatelessWidget {
|
|||
final VoidCallback onChooseLevel;
|
||||
final VoidCallback onChooseHotworkUser;
|
||||
final VoidCallback onAnalyzeTap;
|
||||
|
||||
/// 编辑模式下需提供以下控制器,非编辑可不传
|
||||
final TextEditingController? contentController;
|
||||
final TextEditingController? locationController;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/http/HttpManager.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
|
|
@ -19,8 +20,38 @@ import '../../../../../mine/mine_sign_page.dart';
|
|||
/// 本地路径 + 线上路径模型
|
||||
class ImageData {
|
||||
String localPath;
|
||||
String? serverPath;
|
||||
ImageData({required this.localPath, this.serverPath});
|
||||
String serverPath;
|
||||
|
||||
ImageData({required this.localPath, required this.serverPath});
|
||||
}
|
||||
|
||||
class SignImageData {
|
||||
String SIGNER_TIME;
|
||||
String? filePath;
|
||||
int? key = 0;
|
||||
|
||||
SignImageData({
|
||||
required this.SIGNER_TIME,
|
||||
required this.filePath,
|
||||
required this.key,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'SIGNER_TIME': SIGNER_TIME,
|
||||
'filePath': filePath,
|
||||
'key': key,
|
||||
};
|
||||
|
||||
factory SignImageData.fromJson(Map<String, dynamic> json) {
|
||||
return SignImageData(
|
||||
SIGNER_TIME: json['SIGNER_TIME'] as String,
|
||||
filePath: json['filePath'] as String?,
|
||||
key:
|
||||
json['key'] is int
|
||||
? json['key'] as int
|
||||
: int.tryParse(json['key']?.toString() ?? ''),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DangerousOptionsPage extends StatefulWidget {
|
||||
|
|
@ -28,7 +59,7 @@ class DangerousOptionsPage extends StatefulWidget {
|
|||
final int status;
|
||||
final String measures;
|
||||
final List<ImageData> imgList;
|
||||
final List<String> signImgList;
|
||||
final List<Map<String, dynamic>> signImgList;
|
||||
|
||||
const DangerousOptionsPage({
|
||||
Key? key,
|
||||
|
|
@ -48,12 +79,10 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
late int status;
|
||||
late String measures;
|
||||
late List<ImageData> imgList;
|
||||
late List<String> signImgList;
|
||||
late List<SignImageData> signImgList;
|
||||
List<String> signTimes = [];
|
||||
|
||||
bool buttonLoading = false;
|
||||
final Dio _dio = Dio();
|
||||
final _picker = ImagePicker();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -62,7 +91,8 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
status = widget.status;
|
||||
measures = widget.measures;
|
||||
imgList = List<ImageData>.from(widget.imgList);
|
||||
signImgList = List<String>.from(widget.signImgList);
|
||||
signImgList =
|
||||
widget.signImgList.map((map) => SignImageData.fromJson(map)).toList();
|
||||
}
|
||||
|
||||
/// 拍照或选图后的回调
|
||||
|
|
@ -87,29 +117,61 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
|
||||
Future<void> _submit() async {
|
||||
if (signImgList.isEmpty) {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('请签字')));
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
await ApiService.saveDangerousOptionsFile(signImgList);
|
||||
List<String> filePaths =
|
||||
signImgList.map((img) => img.filePath ?? '').toList();
|
||||
final result = await ApiService.saveDangerousOptionsFile(filePaths);
|
||||
final List<dynamic> signList = result['FILE_PATH_LIST'];
|
||||
List<Map<String, dynamic>> sineImageList = [];
|
||||
for (SignImageData data in signImgList) {
|
||||
for (Map<String, dynamic> img in signList) {
|
||||
String imgName = 'file${data.key}';
|
||||
if (data.filePath!.contains('uploadFiles')) {
|
||||
final idata = {
|
||||
'filePath': data.filePath,
|
||||
'SIGNER_TIME': data.SIGNER_TIME,
|
||||
'key': data.key,
|
||||
};
|
||||
sineImageList.add(idata);
|
||||
}
|
||||
if (imgName == img['key']) {
|
||||
final idata = {
|
||||
'filePath': img['filePath'] ?? '',
|
||||
'SIGNER_TIME': data.SIGNER_TIME,
|
||||
'key': data.key,
|
||||
};
|
||||
sineImageList.add(idata);
|
||||
}
|
||||
}
|
||||
}
|
||||
setState(() => buttonLoading = true);
|
||||
Navigator.pop(context, {
|
||||
'imgList': imgList.map((e) => {'local': e.localPath, 'remote': e.serverPath}).toList(),
|
||||
'signImgList': signImgList,
|
||||
'imgList':
|
||||
imgList
|
||||
.map((e) => {'local': e.localPath, 'remote': e.serverPath})
|
||||
.toList(),
|
||||
'signImgList': sineImageList,
|
||||
'index': index,
|
||||
'status': status,
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
final String path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (c) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
signImgList.add(path);
|
||||
final imageData = SignImageData(
|
||||
SIGNER_TIME: now,
|
||||
filePath: path,
|
||||
key: signImgList.length,
|
||||
);
|
||||
signImgList.add(imageData);
|
||||
signTimes.add(now);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -118,41 +180,57 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children: signImgList.map((path) {
|
||||
final idx = signImgList.indexOf(path);
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children:
|
||||
signImgList.map((imgData) {
|
||||
final idx = signImgList.indexOf(imgData);
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 200, maxHeight: 150),
|
||||
child: Image.file(File(path), fit: BoxFit.contain),
|
||||
),
|
||||
onTap: () => presentOpaque(SingleImageViewer(imageUrl: path), context),
|
||||
),
|
||||
Column(
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomButton(
|
||||
text: '删除',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() => signImgList.removeAt(idx));
|
||||
},
|
||||
GestureDetector(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 230,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child:
|
||||
(imgData.filePath ?? '').contains('uploadFiles')
|
||||
? Image.network(
|
||||
'${ApiService.baseImgPath}${imgData.filePath}',
|
||||
)
|
||||
: Image.file(
|
||||
File(imgData.filePath ?? ''),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap:
|
||||
() => presentOpaque(
|
||||
SingleImageViewer(imageUrl: imgData.filePath ?? ''),
|
||||
context,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
CustomButton(
|
||||
text: '删除',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() => signImgList.removeAt(idx));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -177,13 +255,19 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Center(
|
||||
child: Text('主要安全措施', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
'主要安全措施',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Center(
|
||||
child: Text('操作', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
'操作',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -191,7 +275,10 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
TableRow(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 5),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 5,
|
||||
),
|
||||
child: Text(measures),
|
||||
),
|
||||
Padding(
|
||||
|
|
@ -202,16 +289,28 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
value: -1,
|
||||
groupValue: status,
|
||||
title: Text('不涉及'),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 8.0),
|
||||
visualDensity: VisualDensity(vertical: -4, horizontal: 0),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 0,
|
||||
horizontal: 8.0,
|
||||
),
|
||||
visualDensity: VisualDensity(
|
||||
vertical: -4,
|
||||
horizontal: 0,
|
||||
),
|
||||
onChanged: (v) => setState(() => status = v!),
|
||||
),
|
||||
RadioListTile<int>(
|
||||
value: 1,
|
||||
groupValue: status,
|
||||
title: Text('涉及'),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
|
||||
visualDensity: VisualDensity(vertical: -4, horizontal: 0),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0,
|
||||
horizontal: 8.0,
|
||||
),
|
||||
visualDensity: VisualDensity(
|
||||
vertical: -4,
|
||||
horizontal: 0,
|
||||
),
|
||||
onChanged: (v) => setState(() => status = v!),
|
||||
),
|
||||
],
|
||||
|
|
@ -226,7 +325,10 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
title: '上传图片',
|
||||
maxCount: 2,
|
||||
mediaType: MediaType.image,
|
||||
initialMediaPaths: imgList.map((e) => e.localPath).toList(),
|
||||
initialMediaPaths:
|
||||
imgList
|
||||
.map((e) => '${ApiService.baseImgPath}${e.serverPath}')
|
||||
.toList(),
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaRemoved: (path) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import '../../../../../../customWidget/single_image_viewer.dart';
|
|||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
|
||||
|
|
@ -52,7 +52,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
late List<dynamic> workUserList = [];
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<MeasureItem> measuresListCopy = [];
|
||||
List<String> imagePaths = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
|
|
@ -60,13 +59,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
super.initState();
|
||||
_getData();
|
||||
_getHotWorkNameList();
|
||||
addMeasuresListCopy();
|
||||
}
|
||||
|
||||
String measuresListToJson() {
|
||||
final List<Map<String, dynamic>> jsonList =
|
||||
measuresListCopy.map((item) => item.toJson()).toList();
|
||||
return jsonEncode(jsonList);
|
||||
}
|
||||
|
||||
/// 弹出单位选择
|
||||
|
|
@ -100,42 +92,11 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
});
|
||||
}
|
||||
|
||||
/// 弹出人员选择,需先选择单位
|
||||
void choosePersonHandle(MeasureItem item) async {
|
||||
String unitId = item.DEPARTMENT_ID;
|
||||
final personList = item.userList;
|
||||
if (!unitId.isNotEmpty) {
|
||||
ToastUtil.showNormal(context, '请先选择确认单位');
|
||||
return;
|
||||
}
|
||||
if (personList.isEmpty) {
|
||||
// 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口
|
||||
await _getPersonListForUnitId(item);
|
||||
final list = item.userList;
|
||||
|
||||
if (list.isEmpty) {
|
||||
// 如果还是没数据,说明该部门没有可选的人
|
||||
ToastUtil.showNormal(context, '暂无数据,请选择其他单位');
|
||||
} else {
|
||||
choosePersonHandle(item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
DepartmentPersonPicker.show(
|
||||
context,
|
||||
personsData: personList,
|
||||
onSelected: (userId, name) {
|
||||
setState(() {
|
||||
item.USER_NAME = name;
|
||||
item.USER_ID = userId;
|
||||
print(json.encode(measuresListCopy));
|
||||
});
|
||||
},
|
||||
).then((_) {});
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
FocusHelper.clearFocus(context);
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
|
|
@ -208,49 +169,47 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
}).toList(),
|
||||
);
|
||||
}
|
||||
bool _validateAndProceed(BuildContext context) {
|
||||
for (var i = 0; i < measuresList.length; i++) {
|
||||
final m = measuresList[i];
|
||||
|
||||
/// 提交 1 提交 0暂存
|
||||
// 1. 未签字
|
||||
if (!FormUtils.hasValue(m, 'SIGN_ITEM')) {
|
||||
ToastUtil.showNormal(context, '第${i + 1}项未签字');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 只有 STATUS == '1' 时才检查后续 ANSWER
|
||||
if (m['STATUS'] == '1') {
|
||||
for (var j = 1; j <= 4; j++) {
|
||||
if (FormUtils.hasValue(m, 'QUESTION${j}')
|
||||
) {
|
||||
if (!FormUtils.hasValue(m, 'ANSWER${j}')
|
||||
|| (m['ANSWER${j}'] as String?)!.length == 0) {
|
||||
ToastUtil.showNormal(context, '第${i + 1}项未填写第${j}项');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// 提交 1 提交 0暂存
|
||||
Future<void> _submit(String status) async {
|
||||
if (_otherController.text.trim().isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入其他安全措施,没有请填“无”');
|
||||
return;
|
||||
}
|
||||
if (imagePaths.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
List<Map<String, dynamic>> signers = [];
|
||||
String reasonText = '';
|
||||
|
||||
if (status == '1') {
|
||||
int index = 0;
|
||||
for (var item in measuresListCopy) {
|
||||
if (item.USER_ID.isEmpty) {
|
||||
ToastUtil.showNormal(
|
||||
context,
|
||||
'第${index + 1}项未设置确认人',
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (item.selectMeasures.isEmpty) {
|
||||
ToastUtil.showNormal(
|
||||
context,
|
||||
'第${index + 1}项未选择安全措施',
|
||||
);
|
||||
return;
|
||||
}
|
||||
final userId = item.USER_ID;
|
||||
final selectMeasures = item.selectMeasures as List<dynamic>? ?? [];
|
||||
|
||||
for (var item in selectMeasures) {
|
||||
signers.add({
|
||||
'BUS_HOTWORK_MEASURES_ID': item['BUS_HOTWORK_MEASURES_ID'],
|
||||
'USER_ID': userId,
|
||||
});
|
||||
}
|
||||
// 检查长度是否一致
|
||||
if (signers.length != measuresList.length) {
|
||||
// 使用 ScaffoldMessenger 弹出提示
|
||||
ToastUtil.showNormal(context, '请为每个安全措施选择确认人');
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
if (!_validateAndProceed(context)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
|
|
@ -273,15 +232,15 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['DESCR'] = _otherController.text.trim().isEmpty ? _otherController.text.trim() : "无";
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['PREPARERS'] = json.encode(signers);
|
||||
formData['MEASURES'] = json.encode(measuresList);
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
|
|
@ -327,8 +286,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
final result = await ApiService.getHotWorkNameList();
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
List<String> names =
|
||||
workUserList.map((item) => item['NAME'] as String).toList();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -350,42 +307,20 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
});
|
||||
}
|
||||
|
||||
void removeMeasuresListCopy(int index) {
|
||||
setState(() {
|
||||
measuresListCopy.removeAt(index);
|
||||
});
|
||||
}
|
||||
|
||||
void addMeasuresListCopy() {
|
||||
setState(() {
|
||||
measuresListCopy.add(
|
||||
MeasureItem(
|
||||
id: Random().nextDouble(),
|
||||
DEPARTMENT_ID: '',
|
||||
DEPARTMENT_NAME: '',
|
||||
USER_ID: '',
|
||||
USER_NAME: '',
|
||||
userList: [],
|
||||
userIndex: -1,
|
||||
selectMeasures: [],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _itemToSign(Map<String, dynamic> measures, int index) async {
|
||||
// 签名图片列表
|
||||
final List<String> signImgList = measures['SIGN_ITEM'] ?? [];
|
||||
final List<Map<String, dynamic>> signImgList = measures['SIGN_ITEM'] ?? [];
|
||||
|
||||
// 操作图片路径列表
|
||||
final List<ImageData> imgList = [];
|
||||
if ((measures['IMG_PATH'] as String?)?.isNotEmpty ?? false) {
|
||||
for (var path in (measures['IMG_PATH'] as String).split(',')) {
|
||||
|
||||
imgList.add(ImageData(localPath: path));
|
||||
imgList.add(ImageData(localPath:path, serverPath: path));
|
||||
}
|
||||
}
|
||||
final result = await pushPage(DangerousOptionsPage(index: index,status: measures['STATUS'] ?? 1,
|
||||
//measures['STATUS']
|
||||
final result = await pushPage(DangerousOptionsPage(index: index,status: int.parse(FormUtils.hasValue(measures, 'STATUS') ? measures['STATUS'] : '1'),
|
||||
measures: measures['PROTECTIVE_MEASURES'] ?? '',
|
||||
imgList: imgList,
|
||||
signImgList: signImgList), context);
|
||||
|
|
@ -393,18 +328,28 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
setState(() {
|
||||
// 更新返回的图片列表(local + remote)
|
||||
final returned = result['imgList'] as List<dynamic>;
|
||||
measures['IMG_PATH'] = returned.map((m) {
|
||||
// 先映射成 ImageData 列表
|
||||
final imgDataList = returned.map((m) {
|
||||
return ImageData(
|
||||
localPath: m['local'] as String,
|
||||
serverPath: m['remote'] as String?,
|
||||
serverPath: m['remote'] as String,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// 更新签字列表
|
||||
measures['SIGN_ITEM'] = List<String>.from(result['signImgList'] as List);
|
||||
// 抽取所有非空的 serverPath 并拼成逗号分隔的字符串
|
||||
final serverPathString = imgDataList
|
||||
.map((e) => e.serverPath)
|
||||
.where((s) => s.isNotEmpty)
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.join(',');
|
||||
|
||||
// 存回 measures
|
||||
measures['IMG_PATH'] = serverPathString;
|
||||
|
||||
// 更新签字列表
|
||||
measures['SIGN_ITEM'] = List<Map<String,dynamic>>.from(result['signImgList'] as List);
|
||||
// 更新状态、序号等
|
||||
measures['STATUS'] = result['status'];
|
||||
measures['STATUS'] = result['status'].toString();
|
||||
index = result['index'] as int;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,335 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 安全管理部门意见
|
||||
class HotworkAqglDetail extends StatefulWidget {
|
||||
const HotworkAqglDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkAqglDetail> createState() => _HotworkAqglDetailState();
|
||||
}
|
||||
|
||||
class _HotworkAqglDetailState extends State<HotworkAqglDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
String DESCR = _contentController.text.trim();
|
||||
|
||||
if (DESCR.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入安全管理部门意见');
|
||||
return;
|
||||
}
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['DESCR'] = DESCR;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '安全管理部门意见'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'安全管理部门意见',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
),
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('安全管理部门负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 本地路径 + 线上路径模型
|
||||
class ImageData {
|
||||
String localPath;
|
||||
String serverPath;
|
||||
|
||||
ImageData({required this.localPath, required this.serverPath});
|
||||
}
|
||||
|
||||
/// 安全措施确认人确认
|
||||
class HotworkAqjdDetail extends StatefulWidget {
|
||||
const HotworkAqjdDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkAqjdDetail> createState() => _HotworkAqjdDetailState();
|
||||
}
|
||||
|
||||
class _HotworkAqjdDetailState extends State<HotworkAqjdDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
late List<ImageData> imgList = [];
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 拍照或选图后的回调
|
||||
Future<void> _onImageAdded(String localPath) async {
|
||||
// 上传到服务器
|
||||
final res = await ApiService.uploadSaveFile(localPath);
|
||||
final url = res['FILE_PATH'] as String;
|
||||
setState(() {
|
||||
imgList.add(ImageData(localPath: localPath, serverPath: url));
|
||||
});
|
||||
}
|
||||
|
||||
/// 删除图片处理:调用删除接口并更新列表
|
||||
Future<void> _onImageRemoved(ImageData item) async {
|
||||
if (item.serverPath != null) {
|
||||
await ApiService.deleteSaveFile(item.serverPath!);
|
||||
}
|
||||
setState(() {
|
||||
imgList.remove(item);
|
||||
});
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 抽取所有非空的 serverPath 并拼成逗号分隔的字符串
|
||||
final serverPathString = imgList
|
||||
.map((e) => e.serverPath)
|
||||
.where((s) => s.isNotEmpty)
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.join(',');
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['IMG_PATH'] = serverPathString;
|
||||
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '安全交底人签字确认'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(Column(
|
||||
children: [RepairedPhotoSection(
|
||||
title: '上传图片',
|
||||
maxCount: 2,
|
||||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
},
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('安全交底人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
),],
|
||||
),),
|
||||
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 班长验票
|
||||
class HotworkDbbzDetail extends StatefulWidget {
|
||||
const HotworkDbbzDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkDbbzDetail> createState() => _HotworkDbbzDetailState();
|
||||
}
|
||||
|
||||
class _HotworkDbbzDetailState extends State<HotworkDbbzDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
String DESCR = _contentController.text.trim();
|
||||
|
||||
if (DESCR.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入安全管理部门意见');
|
||||
return;
|
||||
}
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['DESCR'] = DESCR;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '班长验票'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'动火前在岗班长意见',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
),
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('动火前在岗班长'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ import '../../../../../../customWidget/bottom_picker.dart';
|
|||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import 'MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
|
||||
enum EditUserType {
|
||||
ANALYZE('分析单位', '分析单位负责人'),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,335 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 审批部门意见
|
||||
class HotworkDhspDetail extends StatefulWidget {
|
||||
const HotworkDhspDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkDhspDetail> createState() => _HotworkDhspDetailState();
|
||||
}
|
||||
|
||||
class _HotworkDhspDetailState extends State<HotworkDhspDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
String DESCR = _contentController.text.trim();
|
||||
|
||||
if (DESCR.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入安全管理部门意见');
|
||||
return;
|
||||
}
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['DESCR'] = DESCR;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '审批部门意见'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'动火审批部门意见',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
),
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('动火审批人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
/// 监护人签字
|
||||
class HotworkJhrDetail extends StatefulWidget {
|
||||
const HotworkJhrDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkJhrDetail> createState() => _HotworkJhrDetailState();
|
||||
}
|
||||
|
||||
class _HotworkJhrDetailState extends State<HotworkJhrDetail> {
|
||||
late bool isEditable = false;
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
|
||||
List<String> imagePaths = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
imagePaths.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(() {
|
||||
imagePaths.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (imagePaths.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
imagePaths,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(
|
||||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 其他安全防护措施
|
||||
Widget _setSafeDetailWidget() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
child: Column(
|
||||
children: [
|
||||
if (measuresList.isNotEmpty)
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 5),
|
||||
ListItemFactory.createBuildSimpleSection('安全防护措施'),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: MeasuresListWidget(
|
||||
measuresList:
|
||||
measuresList, // List<Map<String, dynamic>>
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isAllowEdit: false,
|
||||
isShowSign:true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (FormUtils.hasValue(signs, 'MEASURES_CONFIRM'))
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
ListItemFactory.createBuildSimpleSection('其他安全防护措施'),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: OtherMeasuresWidget(
|
||||
otherMeasures: signs['MEASURES_CONFIRM'],
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.createBuildSimpleSection('监护人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
if (imagePaths.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '监护人签字确认'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
// _card(_defaultDetail()),
|
||||
WorkDetailFormWidget(
|
||||
pd: pd,
|
||||
isEditable: false,
|
||||
onChooseLevel: (){},
|
||||
onChooseHotworkUser: (){},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
_setSafeDetailWidget(),
|
||||
SizedBox(height: 20),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
/// 接受交底人签字
|
||||
class HotworkJsjdDetail extends StatefulWidget {
|
||||
const HotworkJsjdDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkJsjdDetail> createState() => _HotworkJsjdDetailState();
|
||||
}
|
||||
|
||||
class _HotworkJsjdDetailState extends State<HotworkJsjdDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '接受交底人签字确认'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('接受交底人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,378 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 作业结束时间选择
|
||||
class HotworkJszyDetail extends StatefulWidget {
|
||||
const HotworkJszyDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkJszyDetail> createState() => _HotworkJszyDetailState();
|
||||
}
|
||||
|
||||
class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
/// 选择的时间
|
||||
String endTime = '';
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
bool checkWorkTime(Map<String, dynamic> pd, BuildContext context) {
|
||||
// 解析开始和结束时间
|
||||
final start = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
final end = DateTime.parse(endTime);
|
||||
|
||||
// 校验:结束时间必须晚于开始时间
|
||||
if (end.isAtSameMomentAs(start) || end.isBefore(start)) {
|
||||
ToastUtil.showNormal(context, '作业开始时间不能晚于或等于结束时间,请重新选择');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max8h = 8 * 60 * 60 * 1000;
|
||||
if (diffMs >= max8h) {
|
||||
ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '二级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max72h = 72 * 60 * 60 * 1000;
|
||||
if (diffMs >= max72h) {
|
||||
ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (endTime.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请选择作业结束时间');
|
||||
return;
|
||||
}
|
||||
if (!checkWorkTime(pd, context)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['WORK_END_DATE'] = endTime;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '作业结束时间选择'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: ListItemFactory.createRowSpaceBetweenItem(leftText: '作业结束时间', rightText: endTime.isEmpty?'请选择':endTime, isRight: true),
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(context);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
endTime = DateFormat('yyyy-MM-dd HH:mm').format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业结束时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 作业开始时间选择
|
||||
class HotworkKszyDetail extends StatefulWidget {
|
||||
const HotworkKszyDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkKszyDetail> createState() => _HotworkKszyDetailState();
|
||||
}
|
||||
|
||||
class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
/// 选择的时间
|
||||
String startTime = '';
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -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;
|
||||
}
|
||||
final workStart = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
final analyzeTime = DateTime.parse(pd['ANALYZE_TIME'] as String);
|
||||
final intervalMs = workStart.difference(analyzeTime).inMilliseconds;
|
||||
const thresholdMs = 30 * 60 * 1000;
|
||||
|
||||
if (intervalMs >= thresholdMs) {
|
||||
ToastUtil.showNormal(context, '气体分析时间与开始时间间隔超过30分钟,请重新上传气体检测结果');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['WORK_START_DATE'] = startTime;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '作业开始时间选择'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: ListItemFactory.createRowSpaceBetweenItem(leftText: '作业开始时间', rightText: startTime.isEmpty?'请选择':startTime, isRight: true),
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(context);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat('yyyy-MM-dd HH:mm').format(picked);
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业开始时间负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,17 @@ import 'package:flutter/material.dart';
|
|||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/dh_work_detai/hotwork_apply_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/aqcs_work_detail/hotwork_safe_func_sure.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/aqgl_work_detail/hotwork_aqgl_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/aqjd_work_detail/hotwork_aqjd_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dbbz_work_detail/hotwork_dbbz_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dhsp_work_detail/hotwork_dhsp_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/jhr_work_detail/hotwork_jhr_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/jszy_work_detail/hotwork_jszy_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/kszy_work_detail/hotwork_kszy_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/szaq_work_detail/hotwork_set_safe_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/szdw_work_detail/hotwork_szdw_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/ysgd/hotwork_ysgd_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/zyfz_work_detail/hotwork_zyfz_detail.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../customWidget/bottom_picker.dart';
|
||||
|
|
@ -10,6 +20,7 @@ import '../../../../../customWidget/custom_button.dart';
|
|||
import '../../../../../customWidget/search_bar_widget.dart';
|
||||
import '../../../../../http/ApiService.dart';
|
||||
import '../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import 'jsjd_work_detail/hotwork_jsjd_detail.dart';
|
||||
|
||||
class SpecialWorkListPage extends StatefulWidget {
|
||||
final String flow;
|
||||
|
|
@ -191,7 +202,7 @@ class _SpecialWorkListPageState extends State<SpecialWorkListPage> {
|
|||
_fetchData();
|
||||
break;
|
||||
case '气体检测':
|
||||
pushPage(HotworkGasList(HOTWORK_ID: item['HOTWORK_ID'], addFlag:true), context);
|
||||
await pushPage(HotworkGasList(HOTWORK_ID: item['HOTWORK_ID'], addFlag:true), context);
|
||||
break;
|
||||
case '设置安全措施确认人':
|
||||
await pushPage(HotworkSetSafeDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
|
@ -201,46 +212,70 @@ class _SpecialWorkListPageState extends State<SpecialWorkListPage> {
|
|||
break;
|
||||
case '监护人签字':
|
||||
routeName = '/hotwork-guardian-detail';
|
||||
await pushPage(HotworkJhrDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '安全交底人签字':
|
||||
routeName = '/hotwork-confess-detail';
|
||||
await pushPage(HotworkAqjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '接受交底人签字':
|
||||
routeName = '/hotwork-acceptconfess-detail';
|
||||
await pushPage(HotworkJsjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '作业负责人签字':
|
||||
routeName = '/hotwork-confirm-detail';
|
||||
await pushPage(HotworkZyfzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '所在单位签字':
|
||||
routeName = '/hotwork-leader-detail';
|
||||
await pushPage(HotworkSzdwDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '安全管理部门签字':
|
||||
routeName = '/hotwork-audit-detail';
|
||||
await pushPage(HotworkAqglDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '审批人签字':
|
||||
routeName = '/hotwork-approve-detail';
|
||||
await pushPage(HotworkDhspDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '当班班长验票':
|
||||
routeName = '/hotwork-monitor-detail';
|
||||
await pushPage(HotworkDbbzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '开始作业':
|
||||
routeName = '/hotwork-startwork-detail';
|
||||
await pushPage(HotworkKszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '结束作业':
|
||||
routeName = '/hotwork-endwork-detail';
|
||||
await pushPage(HotworkJszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '验收签字':
|
||||
routeName = '/hotwork-accept-detail';
|
||||
await pushPage(HotworkYsgdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
default:
|
||||
routeName = '/hotwork-detail';
|
||||
}
|
||||
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
routeName,
|
||||
arguments: {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow},
|
||||
);
|
||||
setState(() {
|
||||
_fetchSteps();
|
||||
_fetchData();
|
||||
});
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// routeName,
|
||||
// arguments: {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow},
|
||||
// );
|
||||
}
|
||||
|
||||
Widget _buildFlowStepItem({
|
||||
|
|
|
|||
|
|
@ -11,13 +11,11 @@ import 'package:qhd_prevention/customWidget/department_picker.dart';
|
|||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import 'SafeFunctionDialog.dart';
|
||||
|
|
@ -38,18 +36,11 @@ class HotworkSetSafeDetail extends StatefulWidget {
|
|||
|
||||
class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
||||
late bool isEditable = false;
|
||||
final levelList = ["特级", "一级", "二级"];
|
||||
|
||||
/// 编辑还是新增
|
||||
late String msg = 'add';
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<MeasureItem> measuresListCopy = [];
|
||||
List<String> imagePaths = [];
|
||||
|
|
@ -58,7 +49,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
_getHotWorkNameList();
|
||||
addMeasuresListCopy();
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +329,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(
|
||||
context,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
@ -359,15 +349,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _getHotWorkNameList() async {
|
||||
final result = await ApiService.getHotWorkNameList();
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
List<String> names =
|
||||
workUserList.map((item) => item['NAME'] as String).toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,335 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 所在单位意见
|
||||
class HotworkSzdwDetail extends StatefulWidget {
|
||||
const HotworkSzdwDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkSzdwDetail> createState() => _HotworkSzdwDetailState();
|
||||
}
|
||||
|
||||
class _HotworkSzdwDetailState extends State<HotworkSzdwDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
String DESCR = _contentController.text.trim();
|
||||
|
||||
if (DESCR.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入所在单位负责人意见');
|
||||
return;
|
||||
}
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['DESCR'] = DESCR;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '所在单位意见'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'所在单位负责人意见',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
),
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('所在单位负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,418 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 完工验收
|
||||
class ImageData {
|
||||
String localPath;
|
||||
String serverPath;
|
||||
|
||||
ImageData({required this.localPath, required this.serverPath});
|
||||
}
|
||||
|
||||
class HotworkYsgdDetail extends StatefulWidget {
|
||||
const HotworkYsgdDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkYsgdDetail> createState() => _HotworkYsgdDetailState();
|
||||
}
|
||||
|
||||
class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
/// 选择的时间
|
||||
String startTime = '';
|
||||
/// 验收意见
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
late List<ImageData> imgList = [];
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
/// 拍照或选图后的回调
|
||||
Future<void> _onImageAdded(String localPath) async {
|
||||
// 上传到服务器
|
||||
final res = await ApiService.uploadSaveFile(localPath);
|
||||
final url = res['FILE_PATH'] as String;
|
||||
setState(() {
|
||||
imgList.add(ImageData(localPath: localPath, serverPath: url));
|
||||
});
|
||||
}
|
||||
|
||||
/// 删除图片处理:调用删除接口并更新列表
|
||||
Future<void> _onImageRemoved(ImageData item) async {
|
||||
if (item.serverPath != null) {
|
||||
await ApiService.deleteSaveFile(item.serverPath!);
|
||||
}
|
||||
setState(() {
|
||||
imgList.remove(item);
|
||||
});
|
||||
}
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String DESCR = _contentController.text.trim();
|
||||
|
||||
if (DESCR.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入负责人意见');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
if (status == '1') {
|
||||
if (startTime.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请选择验收时间');
|
||||
return;
|
||||
}
|
||||
// final workStart = DateTime.parse(pd['WORK_START_DATE'] as String);
|
||||
final endTime = DateTime.parse(pd['WORK_END_DATE'] as String);
|
||||
final acceptTime = DateTime.parse(startTime);
|
||||
|
||||
final intervalMs = endTime.difference(acceptTime).inMilliseconds;
|
||||
const thresholdMs = 30 * 60 * 1000;
|
||||
|
||||
if (intervalMs >= thresholdMs) {
|
||||
ToastUtil.showNormal(context, '请在作业结束30分钟内完成验收');
|
||||
return;
|
||||
}
|
||||
if (imgList.isNotEmpty) {
|
||||
ToastUtil.showNormal(context, '请上传验收视频');
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
final serverPathString = imgList
|
||||
.map((e) => e.serverPath)
|
||||
.where((s) => s.isNotEmpty)
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.join(',');
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['ACCEPT_TIME'] = startTime;
|
||||
formData['DESCR'] = DESCR;
|
||||
formData['VIDEO_PATH'] = serverPathString;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '完工验收'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
// _setSafeDetailWidget(),
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'完工验收',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
),
|
||||
Divider(),
|
||||
GestureDetector(
|
||||
child: ListItemFactory.createRowSpaceBetweenItem(leftText: '验收时间', rightText: startTime.isEmpty?'请选择':startTime, isRight: true),
|
||||
onTap: () async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(context);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat('yyyy-MM-dd HH:mm').format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
Divider(),
|
||||
RepairedPhotoSection(
|
||||
title: '验收视频',
|
||||
maxCount: 1,
|
||||
mediaType: MediaType.video,
|
||||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaRemoved: (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(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart: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/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../../../../customWidget/single_image_viewer.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
import '../../../../../mine/mine_sign_page.dart';
|
||||
import '../../../../../my_appbar.dart';
|
||||
import '../../special_Wrok/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
import '../szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 作业负责人意见
|
||||
class HotworkZyfzDetail extends StatefulWidget {
|
||||
const HotworkZyfzDetail({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkZyfzDetail> createState() => _HotworkZyfzDetailState();
|
||||
}
|
||||
|
||||
class _HotworkZyfzDetailState extends State<HotworkZyfzDetail> {
|
||||
late bool isEditable = false;
|
||||
|
||||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
|
||||
/// 安全防护措施列表
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
List<String> signImages = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
signImages.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
signImages.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 作废 -1 通过 1
|
||||
Future<void> _submit(String status) async {
|
||||
if (signImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
String reasonText = '';
|
||||
String DESCR = _contentController.text.trim();
|
||||
|
||||
if (DESCR.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入负责人意见');
|
||||
return;
|
||||
}
|
||||
if (status == '1') {
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onInputConfirm: (text) {
|
||||
reasonText = text;
|
||||
},
|
||||
),
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 存回 measures
|
||||
final Map<String, dynamic> formData = {};
|
||||
// 提交参数
|
||||
formData['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
formData['SIGNTIME'] = signTimes.join(',');
|
||||
formData['USER_ID'] = SessionService.instance.loginUserId;
|
||||
formData['APPLY_STATUS'] = status;
|
||||
formData['STEP_REASON'] = reasonText;
|
||||
formData['DESCR'] = DESCR;
|
||||
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show(context);
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
formData,
|
||||
signImages,
|
||||
);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
|
||||
_getSigns(pd['HOTWORK_ID'] ?? '');
|
||||
_getMeasures(pd['HOTWORK_ID'] ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished(
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget _bottomButtons() {
|
||||
return Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '作业负责人意见'),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
SpecialWorkFormBaseWork(
|
||||
pd: pd,
|
||||
measuresList: measuresList,
|
||||
signs: signs,
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isEditable: false,
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
ItemListWidget.itemContainer(
|
||||
Column(
|
||||
children: [
|
||||
ListItemFactory.createBuildMultilineInput(
|
||||
'作业负责人意见',
|
||||
'请输入意见',
|
||||
_contentController,
|
||||
),
|
||||
Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ListItemFactory.headerTitle('作业负责人'),
|
||||
CustomButton(
|
||||
text: '新增手写签字',
|
||||
height: 36,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_sign();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (signImages.isNotEmpty) _signListWidget(),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue