设置安全措施确认人完成

main
hs 2025-07-30 10:51:07 +08:00
parent d672f1d944
commit 217310e511
19 changed files with 2197 additions and 745 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
assets/images/xj_finish.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/images/xj_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
assets/images/xj_wait.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/images/xjjd_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -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,
);
}

View File

@ -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()
],
)
],
),
);
}
}

View File

@ -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),
],
),
),
],
);
}
}

View File

@ -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,
},
];
});

View File

@ -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,
),
],
),
);
}
}

View File

@ -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,
),
],
],
);
}
}

View File

@ -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,
};
}
}

View File

@ -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(),
),
),
],
),

View File

@ -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: [

View File

@ -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: [

View File

@ -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';

View File

@ -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,
),
);
}
}

View File

@ -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,