设置安全措施确认人完成
parent
d672f1d944
commit
217310e511
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 143 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -704,6 +704,21 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
/// 安全措施确认
|
||||
static Future<Map<String, dynamic>> listSignSureAllMeasures(String homeWorkId) {
|
||||
final String tm = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/hotwork/listAllMeasuresForSign?tm=$tm',
|
||||
method: Method.post,
|
||||
data: {
|
||||
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||
"HOTWORK_ID": homeWorkId,
|
||||
"USER_ID":SessionService.instance.loginUserId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// 关联的特殊作业列表
|
||||
static Future<Map<String, dynamic>> getEightWorkStartList(Map data) {
|
||||
|
|
@ -763,7 +778,28 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
fromData: data,
|
||||
);
|
||||
}
|
||||
/// 保存安全措施确认
|
||||
static Future<Map<String, dynamic>> saveSafeFunctionSure (
|
||||
Map<String, dynamic> formData,
|
||||
List<String> filePaths,
|
||||
) async {
|
||||
// 复制一份 formData
|
||||
final data = Map<String, dynamic>.from(formData);
|
||||
|
||||
// 把文件路径填成 MultipartFile
|
||||
for (var i = 0; i < filePaths.length; i++) {
|
||||
final path = filePaths[i];
|
||||
data['file$i'] = await MultipartFile.fromFile(
|
||||
path,
|
||||
filename: path.split(Platform.pathSeparator).last,
|
||||
);
|
||||
}
|
||||
return HttpManager().uploadFaceImage(
|
||||
baseUrl: basePath,
|
||||
path: '/app/hotwork/nextStep',
|
||||
fromData: data,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
|
||||
class HomeNfcDetailPage extends StatefulWidget {
|
||||
const HomeNfcDetailPage({super.key});
|
||||
|
||||
@override
|
||||
State<HomeNfcDetailPage> createState() => _HomeNfcDetailPageState();
|
||||
}
|
||||
|
||||
class _HomeNfcDetailPageState extends State<HomeNfcDetailPage> {
|
||||
|
||||
Map<String, String> info = {
|
||||
'title': '设备巡检 A',
|
||||
'status': '专项巡检',
|
||||
'department': '安全部',
|
||||
'owner': '张三',
|
||||
'unType': 'UN1001',
|
||||
'cycle': '7天',
|
||||
'points': '3/5',
|
||||
};
|
||||
final List<ProgressItem> demoData = const [
|
||||
ProgressItem(status: '未查', location: '到期哦i维护经费欺废气阀我废费欺废气阀我废费欺废气阀我废气阀', code: 'XJ1001'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
ProgressItem(status: '已查', location: 'B区-配电室', code: 'XJ1002', checkTime: '2025-07-28 15:32'),
|
||||
|
||||
];
|
||||
Widget _pendingTopCard(Map<String, String> item) {
|
||||
return SizedBox(
|
||||
height: 180,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.asset(
|
||||
'assets/images/xj_top.png',
|
||||
height: 70,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
// 标题 & 状态标签
|
||||
Positioned(
|
||||
top: 12,
|
||||
left: 12,
|
||||
child: Text(
|
||||
item['title']!,
|
||||
style: const TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 12,
|
||||
right: 12,
|
||||
child: Container(
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
item['status']!,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 白色信息区域(盖住图片部分)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 50, // 盖住图片底部
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 0),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _buildInfoGrid(item),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
/// 构建信息网格
|
||||
Widget _buildInfoGrid(Map<String, String> item) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('负责部门:${item['department']}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('负责人:${item['owner']}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('UN件类型:${item['unType']}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text('巡检周期:${item['cycle']}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('已巡点位:${item['points']}'),
|
||||
Text('涉及管道区域:${item['department']}')
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '任务详情'),
|
||||
body: SafeArea(child: Padding(padding: EdgeInsets.all(16), child: Column(
|
||||
children: [
|
||||
_pendingTopCard(info),
|
||||
Expanded(child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
child: SingleChildScrollView(
|
||||
child: ProgressList(
|
||||
items: demoData,
|
||||
onStartCheck: (idx) {
|
||||
print('开始检查第 $idx 项');
|
||||
},
|
||||
),
|
||||
)
|
||||
))
|
||||
],
|
||||
),)),
|
||||
);
|
||||
}
|
||||
}
|
||||
/// 单条进度数据模型
|
||||
class ProgressItem {
|
||||
final String status; // “未查” 或 “已查”
|
||||
final String location; // 地点
|
||||
final String code; // 编码
|
||||
final String? checkTime; // 检查时间(已查时有值)
|
||||
|
||||
const ProgressItem({
|
||||
required this.status,
|
||||
required this.location,
|
||||
required this.code,
|
||||
this.checkTime,
|
||||
});
|
||||
}
|
||||
|
||||
/// 进度列表组件
|
||||
class ProgressList extends StatelessWidget {
|
||||
/// 数据源
|
||||
final List<ProgressItem> items;
|
||||
|
||||
/// 点击“开始检查”后的回调,index 对应 items 的下标
|
||||
final void Function(int index) onStartCheck;
|
||||
|
||||
const ProgressList({
|
||||
Key? key,
|
||||
required this.items,
|
||||
required this.onStartCheck,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 整体用 Column 包裹标题和列表
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'已查点位1/5',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
|
||||
// 列表
|
||||
ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(), // 让父级滚动
|
||||
shrinkWrap: true,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (ctx, idx) {
|
||||
return _ProgressListItem(
|
||||
item: items[idx],
|
||||
onStart: () => onStartCheck(idx),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 单行进度条目
|
||||
class _ProgressListItem extends StatelessWidget {
|
||||
final ProgressItem item;
|
||||
final VoidCallback onStart;
|
||||
|
||||
const _ProgressListItem({
|
||||
Key? key,
|
||||
required this.item,
|
||||
required this.onStart,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool unchecked = item.status == '未查';
|
||||
|
||||
return Container(
|
||||
height: 100,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// 左侧状态区
|
||||
Column(children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
unchecked
|
||||
? 'assets/images/xj_wait.png'
|
||||
: 'assets/images/xj_finish.png',
|
||||
width: 50,
|
||||
height: 30,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
top: 2,
|
||||
child: Text(
|
||||
item.status,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15,),
|
||||
Positioned(
|
||||
// bottom: 0,
|
||||
// right: 17,
|
||||
child: Image.asset(
|
||||
'assets/images/xjjd_top.png',
|
||||
width: 15,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
],),
|
||||
|
||||
|
||||
const SizedBox(width: 20),
|
||||
|
||||
// 中间详情
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
item.location,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1, // 最多一行
|
||||
overflow: TextOverflow.ellipsis, // 超出省略号
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text('NFC编码:${item.code}'),
|
||||
|
||||
unchecked
|
||||
? InkWell(
|
||||
onTap: onStart,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 5,),
|
||||
Container(
|
||||
height: 35,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFFFFA726), Color(0xFFFF7043)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: const Text(
|
||||
'开始检查',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
: Text(
|
||||
'检查时间:' + (item.checkTime ?? ''),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 操作区或时间
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Icon(Icons.chevron_right, color: Colors.grey),
|
||||
SizedBox()
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/pages/home/NFC/home_nfc_detail_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
||||
class HomeNfcListPage extends StatefulWidget {
|
||||
const HomeNfcListPage({super.key});
|
||||
|
||||
@override
|
||||
State<HomeNfcListPage> createState() => _HomeNfcListPageState();
|
||||
}
|
||||
|
||||
class _HomeNfcListPageState extends State<HomeNfcListPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
|
||||
// 测试数据
|
||||
final List<Map<String, String>> _pendingList = [
|
||||
{
|
||||
'title': '设备巡检 A',
|
||||
'status': '待巡检',
|
||||
'department': '安全部',
|
||||
'owner': '张三',
|
||||
'unType': 'UN1001',
|
||||
'cycle': '7天',
|
||||
'points': '3/5',
|
||||
},
|
||||
{
|
||||
'title': '设备巡检 B',
|
||||
'status': '待巡检',
|
||||
'department': '维护部',
|
||||
'owner': '李四',
|
||||
'unType': 'UN1002',
|
||||
'cycle': '30天',
|
||||
'points': '1/2',
|
||||
},
|
||||
{
|
||||
'title': '设备巡检 C',
|
||||
'status': '待巡检',
|
||||
'department': '维护部',
|
||||
'owner': '李四',
|
||||
'unType': 'UN1002',
|
||||
'cycle': '30天',
|
||||
'points': '1/2',
|
||||
},
|
||||
];
|
||||
|
||||
final List<Map<String, String>> _recordList = [
|
||||
{
|
||||
'title': '设备巡检 A',
|
||||
'status': '待巡检',
|
||||
'department': '安全部',
|
||||
'owner': '张三',
|
||||
'unType': 'UN1001',
|
||||
'cycle': '7天',
|
||||
'points': '3/5',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '巡检列表'),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// Tab bar
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
labelStyle: const TextStyle(fontSize: 16),
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(width: 3.0, color: Colors.blue),
|
||||
insets: EdgeInsets.symmetric(horizontal: 50.0),
|
||||
),
|
||||
labelColor: Colors.blue,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: const [Tab(text: '待巡检'), Tab(text: '巡检记录')],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// Tab 内容
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [_buildPendingList(), _buildRecordList()],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPendingList() {
|
||||
if (_pendingList.isEmpty) {
|
||||
return NoDataWidget.show(); // 无数据提示
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
|
||||
itemCount: _pendingList.length,
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 0),
|
||||
itemBuilder: (context, index) {
|
||||
final item = _pendingList[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 0),
|
||||
child: GestureDetector(
|
||||
onTap: (){
|
||||
pushPage(HomeNfcDetailPage(), context);
|
||||
},
|
||||
child: _pendingCard(item, false),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecordList() {
|
||||
if (_recordList.isEmpty) {
|
||||
return NoDataWidget.show(); // 无数据提示
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: _recordList.length,
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 0),
|
||||
itemBuilder: (context, index) {
|
||||
final item = _recordList[index];
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 0),
|
||||
child: _pendingCard(item, true),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建待巡检卡片
|
||||
Widget _pendingCard(Map<String, String> item, bool isFinish) {
|
||||
return SizedBox(
|
||||
height: 180,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
// 背景卡片阴影 & 图片
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.asset(
|
||||
'assets/images/xj_top.png',
|
||||
height: 70,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
// 标题 & 状态标签
|
||||
Positioned(
|
||||
top: 12,
|
||||
left: 12,
|
||||
child: Text(
|
||||
item['title']!,
|
||||
style: const TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isFinish)
|
||||
Positioned(
|
||||
top: 12,
|
||||
right: 12,
|
||||
child: Container(
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
item['status']!,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 白色信息区域(盖住图片部分)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 50, // 盖住图片底部
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 0),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _buildInfoGrid(item, isFinish),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建信息网格
|
||||
Widget _buildInfoGrid(Map<String, String> item, bool isFinish) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('负责部门:${item['department']}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('负责人:${item['owner']}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('UN件类型:${item['unType']}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text('巡检周期:${item['cycle']}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('已巡点位:${item['points']}'),
|
||||
isFinish
|
||||
? Text('涉及管道区域:${item['department']}')
|
||||
: const SizedBox(height: 25),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/pages/home/NFC/home_nfc_list_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/low_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/risk/riskControl_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/study/study_garden_page.dart';
|
||||
|
|
@ -196,6 +197,8 @@ class _HomePageState extends State<HomePage> {
|
|||
pushPage(WorkTabListPage(), context);
|
||||
} else if (index == 7) {
|
||||
pushPage(StudyGardenPage(), context);
|
||||
} else if (index == 11) {
|
||||
pushPage(HomeNfcListPage(), context);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -513,6 +516,11 @@ class _HomePageState extends State<HomePage> {
|
|||
"title": "安全例会",
|
||||
"unreadCount": 0,
|
||||
},
|
||||
{
|
||||
"icon": "assets/icon-apps/home-xj.png",
|
||||
"title": "燃气巡检",
|
||||
"unreadCount": 0,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ class ItemListWidget {
|
|||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: isEditable
|
||||
? MainAxisAlignment.start
|
||||
: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment:
|
||||
isEditable
|
||||
? MainAxisAlignment.start
|
||||
: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
|
|
@ -30,23 +31,23 @@ class ItemListWidget {
|
|||
const SizedBox(width: 8),
|
||||
isEditable
|
||||
? Expanded(
|
||||
child: TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
maxLines: 1,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: hintText,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
),
|
||||
)
|
||||
child: TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
maxLines: 1,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: hintText,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
text ?? '',
|
||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||
overflow: TextOverflow.ellipsis, // 超出省略
|
||||
),
|
||||
text ?? '',
|
||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||
overflow: TextOverflow.ellipsis, // 超出省略
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -76,47 +77,53 @@ class ItemListWidget {
|
|||
),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: isEditable
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
// 垂直顶部对齐
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入',
|
||||
// 去掉 TextField 默认内边距
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
// 去掉多余的 padding
|
||||
padding: EdgeInsets.zero,
|
||||
child: Text(
|
||||
text ?? '',
|
||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||
),
|
||||
),
|
||||
child:
|
||||
isEditable
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
// 垂直顶部对齐
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入',
|
||||
// 去掉 TextField 默认内边距
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
// 去掉多余的 padding
|
||||
padding: EdgeInsets.zero,
|
||||
child: Text(
|
||||
text ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: detailtextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// 单行可点击选择:
|
||||
/// - 可编辑时:标题 + “请选择”提示 + 右箭头
|
||||
/// - 不可编辑时:标题 + 文本内容
|
||||
static Widget selectableLineTitleTextField({
|
||||
required String label, // 标题文本
|
||||
required bool isEditable, // 是否可点击
|
||||
required String text, // 显示内容或提示
|
||||
VoidCallback? onTap, // 点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
required String label, // 标题文本
|
||||
required bool isEditable, // 是否可点击
|
||||
required String text, // 显示内容或提示
|
||||
VoidCallback? onTap, // 点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
bool isClean = false,
|
||||
VoidCallback? onTapClean, // 清除回调
|
||||
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: isEditable ? onTap : null,
|
||||
|
|
@ -127,14 +134,24 @@ class ItemListWidget {
|
|||
// 1. 标题
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
|
||||
),
|
||||
|
||||
if (isClean)
|
||||
Column(
|
||||
children: [
|
||||
CustomButton(
|
||||
text: '清除',
|
||||
height: 20,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||
textStyle: TextStyle(fontSize: 11, color: Colors.white),
|
||||
borderRadius: 10,
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: onTapClean,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
|
|
@ -153,10 +170,7 @@ class ItemListWidget {
|
|||
if (isEditable)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 4),
|
||||
child: Icon(
|
||||
Icons.chevron_right,
|
||||
size: 20,
|
||||
),
|
||||
child: Icon(Icons.chevron_right, size: 20),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -167,8 +181,6 @@ class ItemListWidget {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 两行垂直布局:
|
||||
/// 第一行:可点击选择(带箭头)或仅显示标题
|
||||
/// 第二行:多行输入框或多行文本展示
|
||||
|
|
@ -194,7 +206,10 @@ class ItemListWidget {
|
|||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
|
|
@ -209,7 +224,7 @@ class ItemListWidget {
|
|||
),
|
||||
if (isEditable) const Icon(Icons.chevron_right),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -217,27 +232,31 @@ class ItemListWidget {
|
|||
Container(
|
||||
height: row2Height,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: isEditable
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入',
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||
),
|
||||
),
|
||||
child:
|
||||
isEditable
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入',
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: detailtextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -269,7 +288,10 @@ class ItemListWidget {
|
|||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
|
@ -279,7 +301,10 @@ class ItemListWidget {
|
|||
CustomButton(
|
||||
text: "选择其他",
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 5,
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: onTap,
|
||||
),
|
||||
|
|
@ -291,32 +316,37 @@ class ItemListWidget {
|
|||
Container(
|
||||
height: row2Height,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: isEditable
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||
),
|
||||
),
|
||||
child:
|
||||
isEditable
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: detailtextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 单行布局:
|
||||
/// 标题 + 文字 + 按钮
|
||||
static Widget OneRowButtonTitleText({
|
||||
|
|
@ -330,23 +360,31 @@ class ItemListWidget {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Row(
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(width: 15,),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: fontSize, color: detailtextColor),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),),
|
||||
SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: detailtextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
CustomButton(
|
||||
text: "分析详情",
|
||||
height: 30,
|
||||
|
|
@ -355,9 +393,10 @@ class ItemListWidget {
|
|||
onPressed: onTap,
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 单行布局:
|
||||
/// 标题 + 按钮
|
||||
static Widget OneRowButtonTitle({
|
||||
|
|
@ -366,27 +405,25 @@ class ItemListWidget {
|
|||
required VoidCallback? onTap, // 第一行点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
Color btnColor = Colors.blue,
|
||||
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
|
||||
),
|
||||
CustomButton(
|
||||
text: buttonText,
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
backgroundColor: btnColor,
|
||||
onPressed: onTap,
|
||||
),
|
||||
],
|
||||
)
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
|
||||
),
|
||||
CustomButton(
|
||||
text: buttonText,
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
backgroundColor: btnColor,
|
||||
onPressed: onTap,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../tools/tools.dart';
|
||||
import '../../item_list_widget.dart';
|
||||
import '../special_Wrok/dh_work_detai/MeasuresListWidget.dart';
|
||||
|
||||
/// 通用明细表单组件
|
||||
/// 编辑模式时需传入对应的 TextEditingController;非编辑模式可不传
|
||||
class WorkDetailFormWidget extends StatelessWidget {
|
||||
final Map<String, dynamic> pd;
|
||||
final bool isEditable;
|
||||
final VoidCallback onChooseLevel;
|
||||
final VoidCallback onChooseHotworkUser;
|
||||
final VoidCallback onAnalyzeTap;
|
||||
|
||||
/// 编辑模式下需提供以下控制器,非编辑可不传
|
||||
final TextEditingController? contentController;
|
||||
final TextEditingController? locationController;
|
||||
final TextEditingController? methodController;
|
||||
final TextEditingController? hotworkPersonController;
|
||||
final TextEditingController? relatedController;
|
||||
final TextEditingController? riskController;
|
||||
|
||||
const WorkDetailFormWidget({
|
||||
Key? key,
|
||||
required this.pd,
|
||||
required this.isEditable,
|
||||
required this.onChooseLevel,
|
||||
required this.onChooseHotworkUser,
|
||||
required this.onAnalyzeTap,
|
||||
this.contentController,
|
||||
this.locationController,
|
||||
this.methodController,
|
||||
this.hotworkPersonController,
|
||||
this.relatedController,
|
||||
this.riskController,
|
||||
}) : assert(
|
||||
!isEditable || (contentController != null && locationController != null && methodController != null && hotworkPersonController != null && relatedController != null && riskController != null),
|
||||
'Editable mode requires all TextEditingController parameters',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pd = this.pd;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请人:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_USER_NAME'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CHECK_NO')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '编号:',
|
||||
isEditable: false,
|
||||
text: pd['CHECK_NO'] ?? '',
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
ItemListWidget.multiLineTitleTextField(
|
||||
label: '作业内容:',
|
||||
isEditable: isEditable,
|
||||
controller: contentController,
|
||||
text: pd['WORK_CONTENT'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火地点及动火部位:',
|
||||
isEditable: isEditable,
|
||||
controller: locationController,
|
||||
text: pd['WORK_PLACE'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextField(
|
||||
label: '动火作业级别:',
|
||||
isEditable: isEditable,
|
||||
onTap: onChooseLevel,
|
||||
text: pd['WORK_LEVEL'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火方式:',
|
||||
isEditable: isEditable,
|
||||
controller: methodController,
|
||||
text: pd['WORK_FUNCTION'] ?? '',
|
||||
),
|
||||
if (pd['WORK_START_DATE']?.toString().isNotEmpty == true) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火作业实施时间:',
|
||||
isEditable: isEditable,
|
||||
text: '${pd['WORK_START_DATE']} 至 ${pd['WORK_END_DATE'] ?? '--'}',
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowSelectableTitleText(
|
||||
label: '动火人及证书编号:',
|
||||
isEditable: isEditable,
|
||||
onTap: onChooseHotworkUser,
|
||||
text: pd['WORK_USER'] ?? '',
|
||||
controller: hotworkPersonController,
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票:',
|
||||
isEditable: isEditable,
|
||||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'assignments',
|
||||
initialValue: pd['SPECIAL_WORK'] ?? '',
|
||||
onConfirm: (v) => Navigator.of(context).pop(v),
|
||||
),
|
||||
);
|
||||
if (val != null) pd['SPECIAL_WORK'] = val;
|
||||
FocusHelper.clearFocus(context);
|
||||
},
|
||||
hintText: '请输入关联的其他特殊作业及安全作业票编号',
|
||||
controller: relatedController,
|
||||
text: pd['SPECIAL_WORK'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果:',
|
||||
isEditable: isEditable,
|
||||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'identification',
|
||||
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
onConfirm: (v) => Navigator.of(context).pop(v),
|
||||
),
|
||||
);
|
||||
if (val != null) pd['RISK_IDENTIFICATION'] = val;
|
||||
FocusHelper.clearFocus(context);
|
||||
},
|
||||
hintText: '请输入风险辨识结果',
|
||||
controller: riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'ANALYZE_TIME')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.OneRowButtonTitleText(
|
||||
label: '分析人:',
|
||||
text: pd['ANALYZE_USER_NAME'] ?? '',
|
||||
onTap: onAnalyzeTap,
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,532 @@
|
|||
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/dh_work_detai/MeasuresListWidget.dart';
|
||||
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
|
||||
import '../WorkDetailFormWidget.dart';
|
||||
/// 安全措施确认
|
||||
class HotworkSafeFuncSure extends StatefulWidget {
|
||||
const HotworkSafeFuncSure({
|
||||
super.key,
|
||||
required this.HOTWORK_ID,
|
||||
required this.flow,
|
||||
});
|
||||
|
||||
final String HOTWORK_ID;
|
||||
final String flow;
|
||||
|
||||
@override
|
||||
State<HotworkSafeFuncSure> createState() => _HotworkSafeFuncSureState();
|
||||
}
|
||||
|
||||
class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
||||
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 = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
_getHotWorkNameList();
|
||||
addMeasuresListCopy();
|
||||
}
|
||||
|
||||
String measuresListToJson() {
|
||||
final List<Map<String, dynamic>> jsonList =
|
||||
measuresListCopy.map((item) => item.toJson()).toList();
|
||||
return jsonEncode(jsonList);
|
||||
}
|
||||
|
||||
Widget _card(Widget child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 弹出单位选择
|
||||
void chooseUnitHandle(MeasureItem item) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: Colors.black54,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) => DepartmentPicker(
|
||||
onSelected: (id, name) async {
|
||||
setState(() {
|
||||
item.DEPARTMENT_ID = id;
|
||||
item.DEPARTMENT_NAME = name;
|
||||
});
|
||||
_getPersonListForUnitId(item);
|
||||
},
|
||||
),
|
||||
).then((_) {});
|
||||
}
|
||||
|
||||
Future<void> _getPersonListForUnitId(MeasureItem item) async {
|
||||
// 拉取该单位的人员列表并缓存
|
||||
final result = await ApiService.getListTreePersonList(item.DEPARTMENT_ID);
|
||||
setState(() {
|
||||
item.userList = List<Map<String, dynamic>>.from(
|
||||
result['userList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 弹出人员选择,需先选择单位
|
||||
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 {
|
||||
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 提交 0暂存
|
||||
Future<void> _submit(String status) async {
|
||||
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++;
|
||||
}
|
||||
} 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;
|
||||
formData['PREPARERS'] = json.encode(signers);
|
||||
|
||||
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,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void printLongString(String text, {int chunkSize = 800}) {
|
||||
final pattern = RegExp('.{1,$chunkSize}'); // 每 chunkSize 个字符一组
|
||||
for (final match in pattern.allMatches(text)) {
|
||||
print(match.group(0));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
_getMeasures();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures() async {
|
||||
final data = await ApiService.listSignSureAllMeasures(widget.HOTWORK_ID);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['measuresForSignList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
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: [],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 安全防护措施
|
||||
Widget _setSafeDetailWidget() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
child: Column(
|
||||
children: [
|
||||
if (measuresList.length > 0)
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
ListItemFactory.createBuildSimpleSection('安全防护措施'),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: MeasuresListWidget(
|
||||
measuresList:
|
||||
measuresList, // List<Map<String, dynamic>>
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(),
|
||||
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()),
|
||||
_card(
|
||||
WorkDetailFormWidget(
|
||||
pd: pd,
|
||||
isEditable: false,
|
||||
onChooseLevel: (){},
|
||||
onChooseHotworkUser: (){},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
_setSafeDetailWidget(),
|
||||
SizedBox(height: 20),
|
||||
_bottomButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MeasureItem {
|
||||
final double id;
|
||||
String DEPARTMENT_ID;
|
||||
String DEPARTMENT_NAME;
|
||||
String USER_ID;
|
||||
String USER_NAME;
|
||||
List<Map<String, dynamic>> userList;
|
||||
int userIndex;
|
||||
List<Map<String, dynamic>> selectMeasures;
|
||||
|
||||
MeasureItem({
|
||||
required this.id,
|
||||
this.DEPARTMENT_ID = '',
|
||||
this.DEPARTMENT_NAME = '',
|
||||
this.USER_ID = '',
|
||||
this.USER_NAME = '',
|
||||
List<Map<String, dynamic>>? userList,
|
||||
this.userIndex = -1,
|
||||
List<Map<String, dynamic>>? selectMeasures,
|
||||
}) : userList = userList ?? [],
|
||||
selectMeasures = selectMeasures ?? [];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'DEPARTMENT_ID': DEPARTMENT_ID,
|
||||
'DEPARTMENT_NAME': DEPARTMENT_NAME,
|
||||
'USER_ID': USER_ID,
|
||||
'USER_NAME': USER_NAME,
|
||||
'userList': userList,
|
||||
'userIndex': userIndex,
|
||||
'selectMeasures': selectMeasures,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +50,6 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
border: TableBorder(
|
||||
horizontalInside: BorderSide(color: Colors.grey.shade300),
|
||||
verticalInside: BorderSide(color: Colors.grey.shade300),
|
||||
|
||||
),
|
||||
children: [
|
||||
// 表头
|
||||
|
|
@ -155,26 +154,32 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 12), child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'$question: ',
|
||||
style: const TextStyle(fontWeight: FontWeight.w800),
|
||||
),
|
||||
Text(answer.isNotEmpty ? answer : '0')
|
||||
],
|
||||
),),
|
||||
Divider()
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'$question: ',
|
||||
style: const TextStyle(fontWeight: FontWeight.w800),
|
||||
),
|
||||
Text(answer.isNotEmpty ? answer : '0'),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构造一组图片 + 可选时间文本行
|
||||
List<Widget> _buildImageRows(BuildContext context, List<String> paths, String time) {
|
||||
|
||||
List<Widget> _buildImageRows(
|
||||
BuildContext context,
|
||||
List<String> paths,
|
||||
String time,
|
||||
) {
|
||||
return paths.map((p) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
|
|
@ -182,16 +187,16 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(PageRouteBuilder(
|
||||
opaque: false,
|
||||
pageBuilder: (_, __, ___) => SingleImageViewer(imageUrl: '$baseImgPath$p'),
|
||||
));
|
||||
Navigator.of(context).push(
|
||||
PageRouteBuilder(
|
||||
opaque: false,
|
||||
pageBuilder:
|
||||
(_, __, ___) =>
|
||||
SingleImageViewer(imageUrl: '$baseImgPath$p'),
|
||||
),
|
||||
);
|
||||
},
|
||||
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)],
|
||||
],
|
||||
|
|
@ -200,6 +205,7 @@ class MeasuresListWidget extends StatelessWidget {
|
|||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/// 其他安全防护措施表格组件
|
||||
class OtherMeasuresWidget extends StatelessWidget {
|
||||
/// 其他安全防护措施数据列表
|
||||
|
|
@ -214,17 +220,17 @@ class OtherMeasuresWidget extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final list = (otherMeasures ?? [])
|
||||
.where((e) => e is Map<String, dynamic>)
|
||||
.map((e) => e as Map<String, dynamic>)
|
||||
.toList();
|
||||
final list =
|
||||
(otherMeasures ?? [])
|
||||
.where((e) => e is Map<String, dynamic>)
|
||||
.map((e) => e as Map<String, dynamic>)
|
||||
.toList();
|
||||
if (list.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -244,13 +250,19 @@ class OtherMeasuresWidget extends StatelessWidget {
|
|||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Center(
|
||||
child: Text('其他安全措施', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
'其他安全措施',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Center(
|
||||
child: Text('签字', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
'签字',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -291,29 +303,35 @@ class OtherMeasuresWidget extends StatelessWidget {
|
|||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: paths.map((p) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => SingleImageViewer(imageUrl: '$baseImgPath$p'),
|
||||
));
|
||||
},
|
||||
child: Image.network(
|
||||
'$baseImgPath$p',
|
||||
width: 60,
|
||||
height: 60,
|
||||
errorBuilder: (c, o, s) => const Icon(Icons.broken_image),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
children:
|
||||
paths.map((p) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => SingleImageViewer(imageUrl: '$baseImgPath$p'),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Image.network(
|
||||
'$baseImgPath$p',
|
||||
width: 60,
|
||||
height: 60,
|
||||
errorBuilder: (c, o, s) => const Icon(Icons.broken_image),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 各角色签名展示组件
|
||||
class SignaturesListWidget extends StatelessWidget {
|
||||
final Map<String, dynamic>? signs;
|
||||
final Map<String, dynamic>? pd;
|
||||
final String baseImgPath;
|
||||
|
||||
const SignaturesListWidget({
|
||||
Key? key,
|
||||
this.signs,
|
||||
|
|
@ -327,22 +345,96 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
final safePd = pd ?? {};
|
||||
return Column(
|
||||
children: [
|
||||
_buildSection(context, '监护人', safeSigns['GUARDIAN'], safePd['GUARDIAN_USER_NAME'], EditUserType.GUARDIAN),
|
||||
_buildSection(context, '安全交底人', safeSigns['CONFESS'], safePd['CONFESS_USER_NAME'], EditUserType.CONFESS),
|
||||
_buildSection(context, '接受交底人', safeSigns['ACCEPT_CONFESS'], safePd['ACCEPT_CONFESS_USER_NAME'], EditUserType.ACCEPT_CONFESS),
|
||||
_buildTextareaWithSigns(context, '作业负责人意见', safeSigns['CONFIRM'], safePd['CONFIRM_USER_NAME'], EditUserType.CONFIRM),
|
||||
_buildTextareaWithSigns(context, '所在单位意见', safeSigns['LEADER'], safePd['LEADER_USER_NAME'], EditUserType.LEADER),
|
||||
_buildTextareaWithSigns(context, '安全管理部门意见', safeSigns['AUDIT'], safePd['AUDIT_USER_NAME'], EditUserType.AUDIT),
|
||||
_buildTextareaWithSigns(context, '动火审批人意见', safeSigns['APPROVE'], safePd['APPROVE_USER_NAME'], EditUserType.APPROVE),
|
||||
_buildTextareaWithSigns(context, '动火前在岗班长意见', safeSigns['MONITOR'], safePd['MONITOR_USER_NAME'], EditUserType.MONITOR),
|
||||
_buildSection(context, '作业开始负责人', safeSigns['WORK_START'], safePd['WORK_START_USER_NAME'], EditUserType.WORK_START),
|
||||
_buildSection(context, '作业结束负责人', safeSigns['WORK_END'], safePd['WORK_END_USER_NAME'], EditUserType.WORK_END),
|
||||
_buildTextareaWithSigns(context, '完工验收', safeSigns['ACCEPT'], safePd['ACCEPT_USER_NAME'], EditUserType.ACCEPT, timeKey: 'ACCEPT_TIME'),
|
||||
_buildSection(
|
||||
context,
|
||||
'监护人',
|
||||
safeSigns['GUARDIAN'],
|
||||
safePd['GUARDIAN_USER_NAME'],
|
||||
EditUserType.GUARDIAN,
|
||||
),
|
||||
_buildSection(
|
||||
context,
|
||||
'安全交底人',
|
||||
safeSigns['CONFESS'],
|
||||
safePd['CONFESS_USER_NAME'],
|
||||
EditUserType.CONFESS,
|
||||
),
|
||||
_buildSection(
|
||||
context,
|
||||
'接受交底人',
|
||||
safeSigns['ACCEPT_CONFESS'],
|
||||
safePd['ACCEPT_CONFESS_USER_NAME'],
|
||||
EditUserType.ACCEPT_CONFESS,
|
||||
),
|
||||
_buildTextareaWithSigns(
|
||||
context,
|
||||
'作业负责人意见',
|
||||
safeSigns['CONFIRM'],
|
||||
safePd['CONFIRM_USER_NAME'],
|
||||
EditUserType.CONFIRM,
|
||||
),
|
||||
_buildTextareaWithSigns(
|
||||
context,
|
||||
'所在单位意见',
|
||||
safeSigns['LEADER'],
|
||||
safePd['LEADER_USER_NAME'],
|
||||
EditUserType.LEADER,
|
||||
),
|
||||
_buildTextareaWithSigns(
|
||||
context,
|
||||
'安全管理部门意见',
|
||||
safeSigns['AUDIT'],
|
||||
safePd['AUDIT_USER_NAME'],
|
||||
EditUserType.AUDIT,
|
||||
),
|
||||
_buildTextareaWithSigns(
|
||||
context,
|
||||
'动火审批人意见',
|
||||
safeSigns['APPROVE'],
|
||||
safePd['APPROVE_USER_NAME'],
|
||||
EditUserType.APPROVE,
|
||||
),
|
||||
_buildTextareaWithSigns(
|
||||
context,
|
||||
'动火前在岗班长意见',
|
||||
safeSigns['MONITOR'],
|
||||
safePd['MONITOR_USER_NAME'],
|
||||
EditUserType.MONITOR,
|
||||
),
|
||||
_buildSection(
|
||||
context,
|
||||
'作业开始负责人',
|
||||
safeSigns['WORK_START'],
|
||||
safePd['WORK_START_USER_NAME'],
|
||||
EditUserType.WORK_START,
|
||||
),
|
||||
_buildSection(
|
||||
context,
|
||||
'作业结束负责人',
|
||||
safeSigns['WORK_END'],
|
||||
safePd['WORK_END_USER_NAME'],
|
||||
EditUserType.WORK_END,
|
||||
),
|
||||
_buildTextareaWithSigns(
|
||||
context,
|
||||
'完工验收',
|
||||
safeSigns['ACCEPT'],
|
||||
safePd['ACCEPT_USER_NAME'],
|
||||
EditUserType.ACCEPT,
|
||||
timeKey: 'ACCEPT_TIME',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSection(BuildContext context, String title, dynamic rawList, dynamic userName, EditUserType type, {bool showImages = false}) {
|
||||
Widget _buildSection(
|
||||
BuildContext context,
|
||||
String title,
|
||||
dynamic rawList,
|
||||
dynamic userName,
|
||||
EditUserType type, {
|
||||
bool showImages = false,
|
||||
}) {
|
||||
if (rawList is! List || rawList.isEmpty) return const SizedBox.shrink();
|
||||
final list = rawList.cast<Map<String, dynamic>>();
|
||||
final first = list.first;
|
||||
|
|
@ -364,20 +456,38 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
decoration: BoxDecoration(color: Colors.white,border: Border.symmetric(vertical: BorderSide(color: Colors.grey.shade300))),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text('$title: $name', style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
'$title: $name',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
if (showImages && first['IMG_PATH'] is List)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
children: (first['IMG_PATH'] as List).cast<String>().map((img) => Image.network('$baseImgPath$img', width: 50, height: 50)).toList(),
|
||||
children:
|
||||
(first['IMG_PATH'] as List)
|
||||
.cast<String>()
|
||||
.map(
|
||||
(img) => Image.network(
|
||||
'$baseImgPath$img',
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
for (var i = 0; i < signPaths.length; i++)
|
||||
|
|
@ -386,11 +496,27 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => SingleImageViewer(imageUrl: '$baseImgPath${signPaths[i]}'))),
|
||||
child: Image.network('$baseImgPath${signPaths[i]}', width: 100, height: 100, errorBuilder: (_, __, ___) => const Icon(Icons.broken_image)),
|
||||
onTap:
|
||||
() => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => SingleImageViewer(
|
||||
imageUrl: '$baseImgPath${signPaths[i]}',
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Image.network(
|
||||
'$baseImgPath${signPaths[i]}',
|
||||
width: 100,
|
||||
height: 100,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(Icons.broken_image),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(child: Text(i < signTimes.length ? signTimes[i] : '')),
|
||||
Expanded(
|
||||
child: Text(i < signTimes.length ? signTimes[i] : ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -399,14 +525,20 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildTextareaWithSigns(BuildContext context, String label, dynamic rawList, dynamic userName, EditUserType type, {String? timeKey}) {
|
||||
Widget _buildTextareaWithSigns(
|
||||
BuildContext context,
|
||||
String label,
|
||||
dynamic rawList,
|
||||
dynamic userName,
|
||||
EditUserType type, {
|
||||
String? timeKey,
|
||||
}) {
|
||||
if (rawList is! List || rawList.isEmpty) return const SizedBox.shrink();
|
||||
final first = (rawList as List).cast<Map<String, dynamic>>().first;
|
||||
final descr = first['DESCR'] is String ? first['DESCR'] as String : '';
|
||||
final name = pd?['${type.name}_USER_NAME'];
|
||||
final personDes = type.personName;
|
||||
|
||||
|
||||
final signPaths = <String>[];
|
||||
final signTimes = <String>[];
|
||||
if (first['SIGN_PATH'] != null) {
|
||||
|
|
@ -422,7 +554,12 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
decoration: BoxDecoration(color: Colors.white,border: Border.symmetric(vertical: BorderSide(color: Colors.grey.shade300))),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -431,9 +568,17 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
TextField(controller: TextEditingController(text: descr), maxLines: null, readOnly: true, decoration: const InputDecoration(border: InputBorder.none)),
|
||||
TextField(
|
||||
controller: TextEditingController(text: descr),
|
||||
maxLines: null,
|
||||
readOnly: true,
|
||||
decoration: const InputDecoration(border: InputBorder.none),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -447,11 +592,27 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => SingleImageViewer(imageUrl: '$baseImgPath${signPaths[i]}'))),
|
||||
child: Image.network('$baseImgPath${signPaths[i]}', width: 100, height: 100, errorBuilder: (_, __, ___) => const Icon(Icons.broken_image)),
|
||||
onTap:
|
||||
() => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => SingleImageViewer(
|
||||
imageUrl: '$baseImgPath${signPaths[i]}',
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Image.network(
|
||||
'$baseImgPath${signPaths[i]}',
|
||||
width: 100,
|
||||
height: 100,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(Icons.broken_image),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(child: Text(i < signTimes.length ? signTimes[i] : '')),
|
||||
Expanded(
|
||||
child: Text(i < signTimes.length ? signTimes[i] : ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -460,12 +621,15 @@ class SignaturesListWidget extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
///动火安全申请里的’选择其他‘弹窗
|
||||
class SelectionPopup extends StatefulWidget {
|
||||
/// 类型: 'assignments' 或 'identification'
|
||||
final String type;
|
||||
|
||||
/// 初始选中值,以逗号分隔
|
||||
final String initialValue;
|
||||
|
||||
/// 确认回调
|
||||
final void Function(String) onConfirm;
|
||||
|
||||
|
|
@ -514,22 +678,20 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
}
|
||||
|
||||
Future<void> _pickDate() async {
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => HDatePickerDialog(
|
||||
initialDate: DateTime.now(),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onConfirm: (selected) {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
selectedDate = selected;
|
||||
});
|
||||
},
|
||||
),
|
||||
initialDate: DateTime.now(),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onConfirm: (selected) {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
selectedDate = selected;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
Future<void> _getData() async {
|
||||
|
|
@ -538,7 +700,8 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
if (widget.type == 'assignments') {
|
||||
params = {
|
||||
'WORK_TYPE': selectedWorkType,
|
||||
'KEYWORDS': selectedDate == null ? '' : selectedDate!.toString().split(' ')[0],
|
||||
'KEYWORDS':
|
||||
selectedDate == null ? '' : selectedDate!.toString().split(' ')[0],
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
};
|
||||
} else {
|
||||
|
|
@ -567,14 +730,12 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
item['CHECK_NO'] = (prefixMap[type] ?? '') + ' ' + no;
|
||||
}
|
||||
});
|
||||
|
||||
}else{
|
||||
} else {
|
||||
final result = await ApiService.getEightWorkInfo(params);
|
||||
setState(() {
|
||||
list = (result['accidentType'] as List).cast<Map<String, dynamic>>();
|
||||
list = (result['accidentType'] as List).cast<Map<String, dynamic>>();
|
||||
});
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
ToastUtil.showError(context, '$e');
|
||||
}
|
||||
|
|
@ -582,15 +743,16 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
|
||||
void _reset() {
|
||||
setState(() {
|
||||
selectedWorkType = '';
|
||||
selectedWorkName = '请选择';
|
||||
selectedDate = null;
|
||||
// 回到下拉列表的初始选项(第 0 项)
|
||||
selectedWorkType = workList[0]['WORK_TYPE']!;
|
||||
selectedWorkName = workList[0]['WORK_NAME']!;
|
||||
|
||||
selectedDate = null;
|
||||
list.clear();
|
||||
selectValue.clear();
|
||||
});
|
||||
_getData();
|
||||
}
|
||||
|
||||
void _determine() {
|
||||
// 合并选中
|
||||
final result = selectValue.join(',');
|
||||
|
|
@ -610,51 +772,76 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
children: [
|
||||
// 筛选栏
|
||||
if (widget.type == 'assignments')
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
// 作业类型下拉
|
||||
Expanded(
|
||||
child: DropdownButton<String>(
|
||||
dropdownColor: Colors.white,
|
||||
style: TextStyle(),
|
||||
isExpanded: true,
|
||||
value: selectedWorkName,
|
||||
items: workList
|
||||
.map((e) => DropdownMenuItem(
|
||||
value: e['WORK_NAME'],
|
||||
child: Text(e['WORK_NAME']!, style: TextStyle(color: Colors.black87),),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (v) {
|
||||
final idx = workList.indexWhere((e) => e['WORK_NAME'] == v);
|
||||
if (idx >= 0) {
|
||||
setState(() {
|
||||
selectedWorkType = workList[idx]['WORK_TYPE']!;
|
||||
selectedWorkName = v!;
|
||||
});
|
||||
_getData();
|
||||
}
|
||||
},
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
// 作业类型下拉
|
||||
Expanded(
|
||||
child: DropdownButton<String>(
|
||||
dropdownColor: Colors.white,
|
||||
style: TextStyle(),
|
||||
isExpanded: true,
|
||||
value: selectedWorkName,
|
||||
items:
|
||||
workList
|
||||
.map(
|
||||
(e) => DropdownMenuItem(
|
||||
value: e['WORK_NAME'],
|
||||
child: Text(
|
||||
e['WORK_NAME']!,
|
||||
style: TextStyle(color: Colors.black87),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (v) {
|
||||
final idx = workList.indexWhere(
|
||||
(e) => e['WORK_NAME'] == v,
|
||||
);
|
||||
if (idx >= 0) {
|
||||
setState(() {
|
||||
selectedWorkType = workList[idx]['WORK_TYPE']!;
|
||||
selectedWorkName = v!;
|
||||
});
|
||||
_getData();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
TextButton(onPressed: _pickDate, child: Row(
|
||||
children: [
|
||||
Text(selectedDate == null
|
||||
? '选择作业申请时间'
|
||||
: selectedDate!.toString().split(' ')[0],style: TextStyle(color: Colors.blue),),
|
||||
SizedBox(width: 5,),
|
||||
Icon(Icons.arrow_drop_down, color: Colors.grey, size: 20,), ],
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: _pickDate,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
selectedDate == null
|
||||
? '选择作业申请时间'
|
||||
: selectedDate!.toString().split(' ')[0],
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Icon(
|
||||
Icons.arrow_drop_down,
|
||||
color: Colors.grey,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 清空
|
||||
CustomButton(text: '清空',padding: EdgeInsets.symmetric(horizontal: 15),height: 35, backgroundColor: Colors.blue, onPressed: _reset,)
|
||||
],
|
||||
// 清空
|
||||
CustomButton(
|
||||
text: '清空',
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
height: 35,
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: _reset,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
// 列表多选
|
||||
Expanded(
|
||||
|
|
@ -664,9 +851,10 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
itemBuilder: (c, i) {
|
||||
final item = list[i];
|
||||
final value = selectValue;
|
||||
final key = widget.type == 'assignments'
|
||||
? item['CHECK_NO'] as String? ?? ''
|
||||
: item['NAME'] as String? ?? '';
|
||||
final key =
|
||||
widget.type == 'assignments'
|
||||
? item['CHECK_NO'] as String? ?? ''
|
||||
: item['NAME'] as String? ?? '';
|
||||
final checked = value.contains(key);
|
||||
return CheckboxListTile(
|
||||
activeColor: Colors.blue,
|
||||
|
|
@ -700,11 +888,22 @@ class _SelectionPopupState extends State<SelectionPopup> {
|
|||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child:CustomButton(text: '确定', height: 40, backgroundColor: Colors.blue, onPressed: _determine,)
|
||||
child: CustomButton(
|
||||
text: '确定',
|
||||
height: 40,
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: _determine,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: CustomButton(text: '关闭', height: 40, backgroundColor: Colors.grey.shade300, textStyle: TextStyle(color: Colors.grey.shade600), onPressed: () => Navigator.of(context).pop(),)
|
||||
child: CustomButton(
|
||||
text: '关闭',
|
||||
height: 40,
|
||||
backgroundColor: Colors.grey.shade300,
|
||||
textStyle: TextStyle(color: Colors.grey.shade600),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ 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/WorkDetailFormWidget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import '../../../../../../customWidget/bottom_picker.dart';
|
||||
import '../../../../../../http/ApiService.dart';
|
||||
|
|
@ -159,12 +160,10 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
pd['WORK_LEVEL'] = choice;
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _chooseHorkUser() async{
|
||||
|
||||
Future<void> _chooseHorkUser() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items: workUserList.map((item) => item['NAME'] as String).toList(),
|
||||
|
|
@ -176,7 +175,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
pd['WORK_USER'] = choice;
|
||||
_hotworkPersonController.text = choice;
|
||||
Map<String, dynamic> result = workUserList.firstWhere(
|
||||
(item) => item['NAME'] == choice,
|
||||
(item) => item['NAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'USER_ID')) {
|
||||
|
|
@ -184,172 +183,9 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Widget _defaultDetail() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请人:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_USER_NAME'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CHECK_NO'))
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '编号:',
|
||||
isEditable: false,
|
||||
text: pd['CHECK_NO'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Divider(),
|
||||
ItemListWidget.multiLineTitleTextField(
|
||||
label: '作业内容:',
|
||||
isEditable: isEditable,
|
||||
controller: _contentController,
|
||||
text: pd['WORK_CONTENT'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火地点及动火部位:',
|
||||
isEditable: isEditable,
|
||||
controller: _locationController,
|
||||
text: pd['WORK_PLACE'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.selectableLineTitleTextField(
|
||||
label: '动火作业级别',
|
||||
isEditable: isEditable,
|
||||
onTap: () {
|
||||
_chooseLevel();
|
||||
},
|
||||
text: pd['WORK_LEVEL'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火方式:',
|
||||
isEditable: isEditable,
|
||||
controller: _methodController,
|
||||
text: pd['WORK_FUNCTION'] ?? '',
|
||||
),
|
||||
if (pd['WORK_START_DATE'] != null &&
|
||||
pd['WORK_START_DATE'].toString().isNotEmpty)
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火作业\n实施时间:',
|
||||
isEditable: isEditable,
|
||||
controller: _methodController,
|
||||
text:
|
||||
pd['WORK_START_DATE'] ??
|
||||
'' +
|
||||
'至' +
|
||||
(pd['WORK_END_DATE']
|
||||
? pd['WORK_END_DATE'] ?? ''
|
||||
: '--') ??
|
||||
'',
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.twoRowSelectableTitleText(
|
||||
label: '动火人及证书编号:',
|
||||
isEditable: isEditable,
|
||||
onTap: () {
|
||||
_chooseHorkUser();
|
||||
},
|
||||
controller: _hotworkPersonController,
|
||||
text: pd['WORK_USER'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联的其他特殊作业及安全作业票编号',
|
||||
isEditable: isEditable,
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'assignments',
|
||||
initialValue: pd['SPECIAL_WORK'] ?? '',
|
||||
onConfirm: (val) {
|
||||
// val 为逗号分隔的选中值
|
||||
setState(() {
|
||||
pd['SPECIAL_WORK'] = val;
|
||||
_relatedController.text = val;
|
||||
});
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
// identification
|
||||
},
|
||||
hintText: '请输入关联的其他特殊作业及安全作业票编号',
|
||||
controller: _relatedController,
|
||||
text: pd['SPECIAL_WORK'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果',
|
||||
isEditable: isEditable,
|
||||
onTap: () {
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'identification',
|
||||
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
onConfirm: (val) {
|
||||
// val 为逗号分隔的选中值
|
||||
setState(() {
|
||||
pd['RISK_IDENTIFICATION'] = val;
|
||||
_riskController.text = val;
|
||||
});
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
FocusHelper.clearFocus(context);
|
||||
|
||||
});
|
||||
},
|
||||
hintText: '请输入风险辨识结果',
|
||||
controller: _riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'ANALYZE_TIME'))
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
ItemListWidget.OneRowButtonTitleText(
|
||||
label: '分析人',
|
||||
text: pd['ANALYZE_USER_NAME'] ?? '',
|
||||
onTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _card(Widget child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -361,11 +197,32 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
Widget _chooseItem(EditUserType type) {
|
||||
bool isClean = false;
|
||||
|
||||
if (isEditable) {
|
||||
if (type == EditUserType.AUDIT && (pd['WORK_LEVEL'] ?? '') == '二级') {
|
||||
isClean = true;
|
||||
}
|
||||
if (type == EditUserType.APPROVE &&
|
||||
((pd['WORK_LEVEL'] ?? '') == '二级' ||
|
||||
(pd['WORK_LEVEL'] ?? '') == '一级')) {
|
||||
isClean = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
ItemListWidget.selectableLineTitleTextField(
|
||||
label: type.displayName,
|
||||
isEditable: isEditable,
|
||||
isClean: isClean,
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['${type.name}_DEPARTMENT_NAME'] = '';
|
||||
pd['${type.name}_USER_NAME'] = '';
|
||||
_personCache.remove(type);
|
||||
});
|
||||
},
|
||||
text: pd['${type.name}_DEPARTMENT_NAME'] ?? '请选择',
|
||||
onTap: () => chooseUnitHandle(type),
|
||||
),
|
||||
|
|
@ -416,7 +273,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
/// 弹出人员选择,需先选择单位
|
||||
void choosePersonHandle(EditUserType type) async{
|
||||
void choosePersonHandle(EditUserType type) async {
|
||||
FocusHelper.clearFocus(context);
|
||||
|
||||
String unitId = get_pd_DEPARTMENT_ID(type);
|
||||
|
|
@ -426,17 +283,22 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
ToastUtil.showNormal(context, '请先选择$unitName');
|
||||
return;
|
||||
}
|
||||
if (personList.isEmpty) { // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口
|
||||
await _getPersonListForUnitId(unitId, type);
|
||||
final list = _personCache[type] ?? [];
|
||||
|
||||
if (list.isEmpty) { // 如果还是没数据,说明该部门没有可选的人
|
||||
ToastUtil.showNormal(context, '暂无数据,请选择其他单位');
|
||||
}else{
|
||||
choosePersonHandle(type);
|
||||
}
|
||||
if (personList.isEmpty) {
|
||||
// 如果还是没数据,说明该部门没有可选的人
|
||||
ToastUtil.showNormal(context, '请先选择' + type.displayName);
|
||||
return;
|
||||
}
|
||||
// if (personList.isEmpty) { // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口
|
||||
// await _getPersonListForUnitId(unitId, type);
|
||||
// final list = _personCache[type] ?? [];
|
||||
//
|
||||
// if (list.isEmpty) { // 如果还是没数据,说明该部门没有可选的人
|
||||
// ToastUtil.showNormal(context, '暂无数据,请选择其他单位');
|
||||
// }else{
|
||||
// choosePersonHandle(type);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
DepartmentPersonPicker.show(
|
||||
context,
|
||||
personsData: personList,
|
||||
|
|
@ -467,6 +329,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
];
|
||||
final level = pd['WORK_LEVEL'] ?? '';
|
||||
print('---level-$level');
|
||||
|
||||
/// 各项负责人校验
|
||||
final unitRules = <EditUserType>[
|
||||
EditUserType.ANALYZE,
|
||||
|
|
@ -529,11 +392,11 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
pd['TASK_ID'] = taskId;
|
||||
pd['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
pd['APPLY_DEPARTMENT_ID'] = SessionService.instance.deptId;
|
||||
pd['APPLY_DEPARTMENT_NAME'] = SessionService.instance.loginUser?['DEPARTMENT_NAME'] ?? '';
|
||||
pd['APPLY_DEPARTMENT_NAME'] =
|
||||
SessionService.instance.loginUser?['DEPARTMENT_NAME'] ?? '';
|
||||
pd['APPLY_USER_ID'] = SessionService.instance.loginUserId;
|
||||
pd['APPLY_USER_NAME'] = SessionService.instance.username;
|
||||
pd['USER_ID'] = SessionService.instance.loginUserId;
|
||||
|
||||
}
|
||||
|
||||
LoadingDialogHelper.show(context);
|
||||
|
|
@ -552,20 +415,23 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
}
|
||||
|
||||
void printLongString(String text, {int chunkSize = 800}) {
|
||||
final pattern = RegExp('.{1,$chunkSize}'); // 每 chunkSize 个字符一组
|
||||
for (final match in pattern.allMatches(text)) {
|
||||
print(match.group(0));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _getHotWorkNameList() async {
|
||||
final result = await ApiService.getHotWorkNameList();
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
List<String> names = workUserList.map((item) => item['NAME'] as String).toList();
|
||||
|
||||
List<String> names =
|
||||
workUserList.map((item) => item['NAME'] as String).toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
|
|
@ -584,7 +450,6 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
_hotworkPersonController.text = pd['WORK_USER'] ?? '';
|
||||
_relatedController.text = pd['SPECIAL_WORK'] ?? '';
|
||||
_riskController.text = pd['RISK_IDENTIFICATION'] ?? '';
|
||||
|
||||
});
|
||||
// final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
// setState(() {
|
||||
|
|
@ -622,7 +487,26 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
_card(_defaultDetail()),
|
||||
_card(
|
||||
WorkDetailFormWidget(
|
||||
pd: pd,
|
||||
isEditable: isEditable,
|
||||
contentController: _contentController,
|
||||
locationController: _locationController,
|
||||
methodController: _methodController,
|
||||
hotworkPersonController: _hotworkPersonController,
|
||||
relatedController: _relatedController,
|
||||
riskController: _riskController,
|
||||
onChooseLevel: _chooseLevel,
|
||||
onChooseHotworkUser: _chooseHorkUser,
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (isEditable)
|
||||
Column(
|
||||
children: [
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
|||
|
||||
import '../../../../../customWidget/custom_alert_dialog.dart';
|
||||
import '../../../../../customWidget/picker/CupertinoDatePicker.dart';
|
||||
import '../../../../../customWidget/single_image_viewer.dart';
|
||||
|
||||
class HomeGasTestPage extends StatefulWidget {
|
||||
const HomeGasTestPage({Key? key, required this.HOTWORK_ID}) : super(key: key);
|
||||
|
|
@ -68,21 +69,29 @@ class _HomeGasTestPageState extends State<HomeGasTestPage> {
|
|||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
// const DottedLine(
|
||||
// dashLength: 6.0,
|
||||
// dashGapLength: 4.0,
|
||||
// lineThickness: 0.5,
|
||||
// dashColor: Colors.grey,
|
||||
// ),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Image.file(
|
||||
File(path),
|
||||
width: 200,
|
||||
height: 150,
|
||||
fit: BoxFit.cover,
|
||||
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: [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
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/szaq_work_detail/hotwork_set_safe_detail.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
|
@ -196,7 +197,7 @@ class _SpecialWorkListPageState extends State<SpecialWorkListPage> {
|
|||
await pushPage(HotworkSetSafeDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '安全措施确认':
|
||||
routeName = '/hotwork-measures-confirm-detail';
|
||||
await pushPage(HotworkSafeFuncSure(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '监护人签字':
|
||||
routeName = '/hotwork-guardian-detail';
|
||||
|
|
|
|||
|
|
@ -3,15 +3,20 @@ import 'package:qhd_prevention/customWidget/custom_button.dart';
|
|||
|
||||
/// 安全措施弹窗
|
||||
class SafeFunctionDialog extends StatefulWidget {
|
||||
/// 原始数据列表,每个元素为包含 "PROTECTIVE_MEASURES" 键的 Map
|
||||
final List<Map<String, dynamic>> data;
|
||||
|
||||
/// 点击确认回调,返回选中的措施列表
|
||||
final void Function(List<String> selectedMeasures) onConfirm;
|
||||
/// 已选中的初始数据列表
|
||||
final List<Map<String, dynamic>> initialSelectedItems;
|
||||
|
||||
/// 构造函数
|
||||
/// 点击确认回调,返回选中的 Map 列表
|
||||
final void Function(List<Map<String, dynamic>> selectedItems) onConfirm;
|
||||
|
||||
/// 构造函数,支持传入已选中的数据
|
||||
const SafeFunctionDialog({
|
||||
Key? key,
|
||||
required this.data,
|
||||
this.initialSelectedItems = const [],
|
||||
required this.onConfirm,
|
||||
}) : super(key: key);
|
||||
|
||||
|
|
@ -20,19 +25,19 @@ class SafeFunctionDialog extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _SafeFunctionDialogState extends State<SafeFunctionDialog> {
|
||||
/// 存放所有可选项的文字
|
||||
late final List<String> _allMeasures;
|
||||
/// 存放所有可选项的 Map 列表
|
||||
late final List<Map<String, dynamic>> _allItems;
|
||||
|
||||
/// 存放当前被选中的文字
|
||||
final List<String> _selectedMeasures = [];
|
||||
/// 存放当前被选中的 Map 列表
|
||||
late final List<Map<String, dynamic>> _selectedItems;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_allMeasures =
|
||||
widget.data
|
||||
.map((e) => e['PROTECTIVE_MEASURES'] as String? ?? '')
|
||||
.toList();
|
||||
// 初始化所有选项
|
||||
_allItems = widget.data;
|
||||
// 初始化已选中项,克隆一份避免修改原数据
|
||||
_selectedItems = List<Map<String, dynamic>>.from(widget.initialSelectedItems);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -41,32 +46,30 @@ class _SafeFunctionDialogState extends State<SafeFunctionDialog> {
|
|||
insetPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 24),
|
||||
content: SizedBox(
|
||||
width: MediaQuery.of(context).size.width - 24,
|
||||
height:
|
||||
MediaQuery.of(context).size.height -
|
||||
height: MediaQuery.of(context).size.height -
|
||||
MediaQuery.of(context).padding.top -
|
||||
MediaQuery.of(context).padding.bottom,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: _allMeasures.length,
|
||||
itemCount: _allItems.length,
|
||||
itemBuilder: (ctx, index) {
|
||||
final measure = _allMeasures[index];
|
||||
final checked = _selectedMeasures.contains(measure);
|
||||
final item = _allItems[index];
|
||||
final label = item['PROTECTIVE_MEASURES'] as String? ?? '';
|
||||
final checked = _selectedItems.contains(item);
|
||||
return CheckboxListTile(
|
||||
// 将复选框移到左侧
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
// 调整左右间隙
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
activeColor: Colors.blue,
|
||||
title: Text(measure, style: const TextStyle(fontSize: 14)),
|
||||
title: Text(label, style: const TextStyle(fontSize: 14)),
|
||||
value: checked,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
if (value == true) {
|
||||
_selectedMeasures.add(measure);
|
||||
_selectedItems.add(item);
|
||||
} else {
|
||||
_selectedMeasures.remove(measure);
|
||||
_selectedItems.remove(item);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
@ -83,7 +86,7 @@ class _SafeFunctionDialogState extends State<SafeFunctionDialog> {
|
|||
height: 40,
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: () {
|
||||
widget.onConfirm(_selectedMeasures);
|
||||
widget.onConfirm(_selectedItems);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
|
|
@ -107,14 +110,21 @@ class _SafeFunctionDialogState extends State<SafeFunctionDialog> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 显示安全措施弹窗
|
||||
/// [initialSelected] 可选,指定弹窗打开时的已选项
|
||||
Future<void> showSafeFunctionDialog(
|
||||
BuildContext context,
|
||||
List<Map<String, dynamic>> data,
|
||||
void Function(List<String>) onConfirm,
|
||||
) {
|
||||
BuildContext context,
|
||||
List<Map<String, dynamic>> data,
|
||||
void Function(List<Map<String, dynamic>>) onConfirm, {
|
||||
List<Map<String, dynamic>> initialSelected = const [],
|
||||
}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => SafeFunctionDialog(data: data, onConfirm: onConfirm),
|
||||
builder: (_) => SafeFunctionDialog(
|
||||
data: data,
|
||||
initialSelectedItems: initialSelected,
|
||||
onConfirm: onConfirm,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,15 +12,16 @@ 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';
|
||||
|
||||
|
||||
/// 设置安全措施确认人
|
||||
class HotworkSetSafeDetail extends StatefulWidget {
|
||||
const HotworkSetSafeDetail({
|
||||
super.key,
|
||||
|
|
@ -46,7 +47,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
late Map<String, dynamic> pd = {};
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
|
|
@ -60,128 +60,14 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
_getData();
|
||||
_getHotWorkNameList();
|
||||
addMeasuresListCopy();
|
||||
|
||||
}
|
||||
|
||||
String measuresListToJson() {
|
||||
final List<Map<String, dynamic>> jsonList =
|
||||
measuresListCopy.map((item) => item.toJson()).toList();
|
||||
measuresListCopy.map((item) => item.toJson()).toList();
|
||||
return jsonEncode(jsonList);
|
||||
}
|
||||
|
||||
|
||||
Widget _defaultDetail() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请单位:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_DEPARTMENT_NAME'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '申请人:',
|
||||
isEditable: false,
|
||||
text: pd['APPLY_USER_NAME'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'CHECK_NO'))
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '编号:',
|
||||
isEditable: false,
|
||||
text: pd['CHECK_NO'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Divider(),
|
||||
ItemListWidget.multiLineTitleTextField(
|
||||
label: '作业内容:',
|
||||
isEditable: isEditable,
|
||||
text: pd['WORK_CONTENT'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火地点及动火部位:',
|
||||
isEditable: isEditable,
|
||||
text: pd['WORK_PLACE'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.selectableLineTitleTextField(
|
||||
label: '动火作业级别',
|
||||
isEditable: isEditable,
|
||||
text: pd['WORK_LEVEL'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火方式:',
|
||||
isEditable: isEditable,
|
||||
text: pd['WORK_FUNCTION'] ?? '',
|
||||
),
|
||||
if (pd['WORK_START_DATE'] != null &&
|
||||
pd['WORK_START_DATE'].toString().isNotEmpty)
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火作业\n实施时间:',
|
||||
isEditable: isEditable,
|
||||
text:
|
||||
pd['WORK_START_DATE'] ??
|
||||
'' +
|
||||
'至' +
|
||||
(pd['WORK_END_DATE']
|
||||
? pd['WORK_END_DATE'] ?? ''
|
||||
: '--') ??
|
||||
'',
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.twoRowSelectableTitleText(
|
||||
label: '动火人及证书编号:',
|
||||
isEditable: isEditable,
|
||||
text: pd['WORK_USER'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联的其他特殊作业及安全作业票编号',
|
||||
isEditable: isEditable,
|
||||
onTap: ()=>{},
|
||||
hintText: '请输入关联的其他特殊作业及安全作业票编号',
|
||||
text: pd['SPECIAL_WORK'] ?? '',
|
||||
),
|
||||
Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果',
|
||||
isEditable: isEditable,
|
||||
onTap: () {},
|
||||
hintText: '请输入风险辨识结果',
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(pd, 'ANALYZE_TIME'))
|
||||
Column(
|
||||
children: [
|
||||
Divider(),
|
||||
ItemListWidget.OneRowButtonTitleText(
|
||||
label: '分析人',
|
||||
text: pd['ANALYZE_USER_NAME'] ?? '',
|
||||
onTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _card(Widget child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -214,10 +100,16 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
label: '安全措施:',
|
||||
buttonText: '选择安全措施',
|
||||
onTap: () {
|
||||
showSafeFunctionDialog(context, measuresList, (selected) {
|
||||
// 在这里处理用户的选择结果
|
||||
debugPrint('用户选择了:' + json.encode(selected));
|
||||
});
|
||||
showSafeFunctionDialog(
|
||||
context,
|
||||
measuresList,
|
||||
initialSelected: item.selectMeasures,
|
||||
(selected) {
|
||||
setState(() {
|
||||
item.selectMeasures = selected;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
@ -226,7 +118,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
|
||||
/// 弹出单位选择
|
||||
void chooseUnitHandle(MeasureItem item) {
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
|
|
@ -242,16 +133,13 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
_getPersonListForUnitId(item);
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
|
||||
});
|
||||
).then((_) {});
|
||||
}
|
||||
|
||||
Future<void> _getPersonListForUnitId(MeasureItem item) async {
|
||||
// 拉取该单位的人员列表并缓存
|
||||
final result = await ApiService.getListTreePersonList(item.DEPARTMENT_ID);
|
||||
setState(() {
|
||||
|
||||
item.userList = List<Map<String, dynamic>>.from(
|
||||
result['userList'] ?? <Map<String, dynamic>>[],
|
||||
);
|
||||
|
|
@ -260,12 +148,10 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
|
||||
/// 弹出人员选择,需先选择单位
|
||||
void choosePersonHandle(MeasureItem item) async {
|
||||
|
||||
String unitId = item.DEPARTMENT_ID;
|
||||
final personList = item.userList;
|
||||
if (!unitId.isNotEmpty) {
|
||||
final unitName = item.DEPARTMENT_NAME;
|
||||
ToastUtil.showNormal(context, '请先选择$unitName');
|
||||
ToastUtil.showNormal(context, '请先选择确认单位');
|
||||
return;
|
||||
}
|
||||
if (personList.isEmpty) {
|
||||
|
|
@ -291,10 +177,9 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
print(json.encode(measuresListCopy));
|
||||
});
|
||||
},
|
||||
).then((_) {
|
||||
|
||||
});
|
||||
).then((_) {});
|
||||
}
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
final path = await Navigator.push(
|
||||
|
|
@ -311,115 +196,168 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children: imagePaths.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children:
|
||||
imagePaths.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: Image.file(
|
||||
File(path),
|
||||
width: 200,
|
||||
height: 150,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl:path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
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);
|
||||
});
|
||||
},
|
||||
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),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 提交 1 提交 0暂存
|
||||
Future<void> _submit(String status) async {
|
||||
// 通用文本字段校验规则
|
||||
if (imagePaths.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
List<Map<String, dynamic>> signers = [];
|
||||
String reasonText = '';
|
||||
|
||||
if (status == '1') {
|
||||
// // 文本校验
|
||||
// for (var rule in textRules) {
|
||||
// if ((rule['value'] as String).isEmpty) {
|
||||
// ToastUtil.showNormal(context, rule['message']);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// // 级别校验
|
||||
// if (level.length == 0) {
|
||||
// ToastUtil.showNormal(context, '请选择动火级别');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// for (MeasureItem item in measuresListCopy) {
|
||||
// if (item.USER_ID.isNotEmpty) {
|
||||
// ToastUtil.showNormal(context, '请选择确认人');
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// LoadingDialogHelper.show(context);
|
||||
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>? ?? [];
|
||||
|
||||
// 提交参数
|
||||
if (msg == 'add') {
|
||||
pd['CORPINFO_ID'] = SessionService.instance.corpinfoId;
|
||||
pd['CREATOR'] = SessionService.instance.loginUserId;
|
||||
pd['OPERATOR'] = SessionService.instance.loginUserId;
|
||||
pd['ACTION_USER'] = SessionService.instance.username;
|
||||
pd['APPLY_STATUS'] = status;
|
||||
pd['STEP_ID'] = status;
|
||||
pd['HOTWORK_ID'] = widget.HOTWORK_ID;
|
||||
pd['APPLY_DEPARTMENT_ID'] = SessionService.instance.deptId;
|
||||
pd['APPLY_DEPARTMENT_NAME'] =
|
||||
SessionService.instance.loginUser?['DEPARTMENT_NAME'] ?? '';
|
||||
pd['APPLY_USER_ID'] = SessionService.instance.loginUserId;
|
||||
pd['APPLY_USER_NAME'] = SessionService.instance.username;
|
||||
pd['USER_ID'] = SessionService.instance.loginUserId;
|
||||
}
|
||||
|
||||
LoadingDialogHelper.show(context);
|
||||
String jsonStr = jsonEncode(pd);
|
||||
printLongString(jsonStr);
|
||||
try {
|
||||
String url = "/app/hotwork/" + msg;
|
||||
final result = await ApiService.submitHotwork(url, pd);
|
||||
LoadingDialogHelper.hide(context);
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.pop(context);
|
||||
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++;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
|
||||
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['PREPARERS'] = json.encode(signers);
|
||||
|
||||
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,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide(context);
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void printLongString(String text, {int chunkSize = 800}) {
|
||||
|
|
@ -444,13 +382,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
setState(() {
|
||||
pd = data['pd'];
|
||||
_getMeasures();
|
||||
|
||||
});
|
||||
// final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
|
||||
// setState(() {
|
||||
// pd = data['pd'];
|
||||
// });
|
||||
// LoadingDialogHelper.hide(context);
|
||||
}
|
||||
|
||||
Future<void> _getMeasures() async {
|
||||
|
|
@ -535,9 +467,14 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
|
||||
...item.selectMeasures.asMap().entries.map((e) {
|
||||
int idx = e.key;
|
||||
String txt = e.value;
|
||||
String txt = e.value['PROTECTIVE_MEASURES'];
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 4),
|
||||
padding: EdgeInsets.only(
|
||||
top: 4,
|
||||
left: 12,
|
||||
right: 12,
|
||||
bottom: 12,
|
||||
),
|
||||
child: Text('${idx + 1}. $txt'),
|
||||
);
|
||||
}),
|
||||
|
|
@ -589,7 +526,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
),
|
||||
SizedBox(height: 10),
|
||||
if (imagePaths.isNotEmpty) _signListWidget(),
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -608,7 +544,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
text: '作废',
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
_submit('-1');
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -618,7 +554,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
text: '通过',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('0');
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -635,8 +571,21 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
_card(_defaultDetail()),
|
||||
|
||||
// _card(_defaultDetail()),
|
||||
_card(
|
||||
WorkDetailFormWidget(
|
||||
pd: pd,
|
||||
isEditable: false,
|
||||
onChooseLevel: (){},
|
||||
onChooseHotworkUser: (){},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
_setSafeDetailWidget(),
|
||||
SizedBox(height: 20),
|
||||
|
|
@ -657,7 +606,7 @@ class MeasureItem {
|
|||
String USER_NAME;
|
||||
List<Map<String, dynamic>> userList;
|
||||
int userIndex;
|
||||
List<String> selectMeasures;
|
||||
List<Map<String, dynamic>> selectMeasures;
|
||||
|
||||
MeasureItem({
|
||||
required this.id,
|
||||
|
|
@ -667,9 +616,10 @@ class MeasureItem {
|
|||
this.USER_NAME = '',
|
||||
List<Map<String, dynamic>>? userList,
|
||||
this.userIndex = -1,
|
||||
List<String>? selectMeasures,
|
||||
List<Map<String, dynamic>>? selectMeasures,
|
||||
}) : userList = userList ?? [],
|
||||
selectMeasures = selectMeasures ?? [];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
|
|
|
|||
Loading…
Reference in New Issue