气体检测,无数据通用组件

main
hs 2025-07-28 14:22:07 +08:00
parent 4d3fb2b6e6
commit fd4cbf2dff
48 changed files with 3798 additions and 670 deletions

View File

@ -339,6 +339,7 @@ class ListItemFactory {
const SizedBox(height: 8),
Expanded(
child: TextField(
autofocus: false,
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
///
/// Example:
@ -30,8 +31,12 @@ class BottomPicker {
double itemExtent = 40.0,
double height = 250,
}) {
if (items.isEmpty) return Future.value(null);
//
final safeIndex = initialIndex.clamp(0, items.length - 1);
//
T selected = items[initialIndex];
T selected = items[safeIndex];
return showModalBottomSheet<T>(
context: context,
@ -51,11 +56,11 @@ class BottomPicker {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
onPressed: () {FocusScope.of(context).unfocus();Navigator.of(ctx).pop();},
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(selected),
onPressed: () {FocusScope.of(context).unfocus(); Navigator.of(ctx).pop(selected);},
child: const Text('确定'),
),
],

View File

@ -1,27 +1,56 @@
import 'package:flutter/material.dart';
class CustomAlertDialog extends StatelessWidget {
///
enum DialogMode { text, input }
class CustomAlertDialog extends StatefulWidget {
final String title;
final String content;
final String content; //
final String hintText; //
final String cancelText;
final String confirmText;
final VoidCallback? onCancel;
final VoidCallback? onConfirm;
final VoidCallback? onConfirm; //
final ValueChanged<String>? onInputConfirm; //
final DialogMode mode; //
const CustomAlertDialog({
Key? key,
required this.title,
required this.content,
this.cancelText = "取消",
this.confirmText = "确定",
this.content = '',
this.hintText = '',
this.cancelText = '取消',
this.confirmText = '确定',
this.onCancel,
this.onConfirm,
this.onInputConfirm,
this.mode = DialogMode.text, //
}) : super(key: key);
@override
Widget build(BuildContext context) {
final bool hasCancel = cancelText.trim().isNotEmpty;
_CustomAlertDialogState createState() => _CustomAlertDialogState();
}
class _CustomAlertDialogState extends State<CustomAlertDialog> {
late TextEditingController _controller;
@override
void initState() {
super.initState();
// TextField
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
bool get hasCancel => widget.cancelText.trim().isNotEmpty;
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
@ -29,34 +58,55 @@ class CustomAlertDialog extends StatelessWidget {
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
padding: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 20),
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
widget.title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Text(
content,
style: const TextStyle(
fontSize: 16,
color: Colors.black45,
// mode
if (widget.mode == DialogMode.text)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Text(
widget.content,
style: const TextStyle(fontSize: 16, color: Colors.black45),
textAlign: TextAlign.center,
),
)
else
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: widget.hintText,
border: const OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 1),
borderRadius: BorderRadius.circular(4),
),
isDense: true,
contentPadding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
),
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 20),
const Divider(height: 1),
hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context),
hasCancel
? _buildDoubleButtons(context)
: _buildSingleButton(context),
],
),
),
@ -66,17 +116,18 @@ class CustomAlertDialog extends StatelessWidget {
Widget _buildDoubleButtons(BuildContext context) {
return Row(
children: [
//
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
onCancel?.call();
widget.onCancel?.call();
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
child: Text(
cancelText,
widget.cancelText,
style: const TextStyle(
fontWeight: FontWeight.w500,
color: Colors.black,
@ -86,20 +137,27 @@ class CustomAlertDialog extends StatelessWidget {
),
),
),
Container(width: 1, height: 48, color: Colors.grey[300]),
//
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
onConfirm?.call();
if (widget.mode == DialogMode.text) {
widget.onConfirm?.call();
} else {
widget.onInputConfirm?.call(_controller.text.trim());
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
child: Text(
confirmText,
widget.confirmText,
style: const TextStyle(
color: Color(0xFF3874F6), //
color: Color(0xFF3874F6),
fontWeight: FontWeight.w500,
fontSize: 18,
),
@ -115,16 +173,20 @@ class CustomAlertDialog extends StatelessWidget {
return InkWell(
onTap: () {
Navigator.of(context).pop();
onConfirm?.call();
if (widget.mode == DialogMode.text) {
widget.onConfirm?.call();
} else {
widget.onInputConfirm?.call(_controller.text.trim());
}
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 14),
alignment: Alignment.center,
child: Text(
confirmText,
widget.confirmText,
style: const TextStyle(
color: Color(0xFF3874F6), //
color: Color(0xFF3874F6),
fontWeight: FontWeight.w500,
fontSize: 18,
),

View File

@ -28,7 +28,7 @@ class CustomButton extends StatelessWidget {
return GestureDetector(
onTap: onPressed,
child: Container(
height: height ?? 50, // 50
height: height ?? 45, // 45
padding: padding ?? const EdgeInsets.all(8), //
margin: margin ?? const EdgeInsets.symmetric(horizontal: 5), //
decoration: BoxDecoration(
@ -40,6 +40,7 @@ class CustomButton extends StatelessWidget {
text,
style: textStyle ?? const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold,
),
),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
class HDatePickerDialog extends StatefulWidget {
final DateTime initialDate;
@ -125,7 +126,7 @@ class _HDatePickerDialogState extends State<HDatePickerDialog> {
},
child: Container(
decoration: BoxDecoration(
color: isSelected ? Theme.of(context).primaryColor : null,
color: isSelected ? Colors.blue : null,
shape: BoxShape.circle,
),
child: Center(
@ -147,14 +148,13 @@ class _HDatePickerDialogState extends State<HDatePickerDialog> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: widget.onCancel,
child: const Text('取消'),
),
ElevatedButton(
onPressed: () => widget.onConfirm(_selectedDate),
child: const Text('确定'),
),
Expanded(child: CustomButton(text: '取消',height: 40,backgroundColor: Colors.grey.shade500,
onPressed: widget.onCancel),),
SizedBox(width: 30,),
Expanded(child: CustomButton(text: '确定',height: 40,backgroundColor: Colors.blue,
onPressed: () => widget.onConfirm(_selectedDate)),)
],
),
],

View File

@ -1,7 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
///
class Person {
final String userId;

View File

@ -101,7 +101,7 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
widget.onChanged(_files);
}
} catch (e) {
debugPrint('拍摄失败: \$e');
debugPrint('拍摄失败: $e');
}
}
@ -140,7 +140,7 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
widget.onChanged(_files);
}
} catch (e) {
debugPrint('相册选择失败: \$e');
debugPrint('相册选择失败: $e');
}
}

View File

@ -0,0 +1,220 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
///
/// DateTime? picked = await BottomDateTimePicker.show(context);
/// if (picked != null) {
/// print('用户选择的时间:$picked');
/// }
class BottomDateTimePicker {
static Future<DateTime?> show(BuildContext context) {
return showModalBottomSheet<DateTime>(
context: context,
backgroundColor: Colors.white,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
builder: (_) => _InlineDateTimePickerContent(),
);
}
}
class _InlineDateTimePickerContent extends StatefulWidget {
@override
State<_InlineDateTimePickerContent> createState() => _InlineDateTimePickerContentState();
}
class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerContent> {
//
final List<int> years = List.generate(101, (i) => 1970 + i);
final List<int> months = List.generate(12, (i) => i + 1);
final List<int> days = List.generate(31, (i) => i + 1);
final List<int> hours = List.generate(24, (i) => i);
final List<int> minutes = List.generate(60, (i) => i);
// Controllers
late FixedExtentScrollController yearCtrl;
late FixedExtentScrollController monthCtrl;
late FixedExtentScrollController dayCtrl;
late FixedExtentScrollController hourCtrl;
late FixedExtentScrollController minuteCtrl;
//
late int selectedYear;
late int selectedMonth;
late int selectedDay;
late int selectedHour;
late int selectedMinute;
@override
void initState() {
super.initState();
final now = DateTime.now();
selectedYear = now.year;
selectedMonth = now.month;
selectedDay = now.day;
selectedHour = now.hour;
selectedMinute = now.minute;
yearCtrl = FixedExtentScrollController(initialItem: years.indexOf(selectedYear));
monthCtrl = FixedExtentScrollController(initialItem: selectedMonth - 1);
dayCtrl = FixedExtentScrollController(initialItem: selectedDay - 1);
hourCtrl = FixedExtentScrollController(initialItem: selectedHour);
minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute);
}
@override
void dispose() {
yearCtrl.dispose();
monthCtrl.dispose();
dayCtrl.dispose();
hourCtrl.dispose();
minuteCtrl.dispose();
super.dispose();
}
void _checkAndClampToNow() {
final picked = DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute);
final now = DateTime.now();
if (picked.isAfter(now)) {
//
selectedYear = now.year;
selectedMonth = now.month;
selectedDay = now.day;
selectedHour = now.hour;
selectedMinute = now.minute;
//
yearCtrl.jumpToItem(years.indexOf(selectedYear));
monthCtrl.jumpToItem(selectedMonth - 1);
dayCtrl.jumpToItem(selectedDay - 1);
hourCtrl.jumpToItem(selectedHour);
minuteCtrl.jumpToItem(selectedMinute);
}
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
child: Column(
children: [
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("取消", style: TextStyle(color: Colors.grey)),
),
TextButton(
onPressed: () {
final result = DateTime(
selectedYear,
selectedMonth,
selectedDay,
selectedHour,
selectedMinute,
);
Navigator.of(context).pop(result);
},
child: const Text("确定", style: TextStyle(color: Colors.blue)),
),
],
),
),
const Divider(height: 1),
//
Expanded(
child: Row(
children: [
//
_buildPicker(
controller: yearCtrl,
items: years.map((e) => e.toString()).toList(),
onSelected: (idx) {
setState(() {
selectedYear = years[idx];
_checkAndClampToNow();
});
},
),
//
_buildPicker(
controller: monthCtrl,
items: months.map((e) => e.toString().padLeft(2, '0')).toList(),
onSelected: (idx) {
setState(() {
selectedMonth = months[idx];
_checkAndClampToNow();
});
},
),
//
_buildPicker(
controller: dayCtrl,
items: days.map((e) => e.toString().padLeft(2, '0')).toList(),
onSelected: (idx) {
setState(() {
selectedDay = days[idx];
_checkAndClampToNow();
});
},
),
//
_buildPicker(
controller: hourCtrl,
items: hours.map((e) => e.toString().padLeft(2, '0')).toList(),
onSelected: (idx) {
setState(() {
selectedHour = hours[idx];
_checkAndClampToNow();
});
},
),
//
_buildPicker(
controller: minuteCtrl,
items: minutes.map((e) => e.toString().padLeft(2, '0')).toList(),
onSelected: (idx) {
setState(() {
selectedMinute = minutes[idx];
_checkAndClampToNow();
});
},
),
],
),
),
],
),
);
}
Widget _buildPicker({
required FixedExtentScrollController controller,
required List<String> items,
required ValueChanged<int> onSelected,
}) {
return Expanded(
child: CupertinoPicker.builder(
scrollController: controller,
itemExtent: 32,
childCount: items.length,
onSelectedItemChanged: onSelected,
itemBuilder: (context, index) {
return Center(child: Text(items[index]));
},
),
);
}
}

View File

@ -70,7 +70,7 @@ class _RemoteFilePageState extends State<RemoteFilePage> {
_isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('文件加载失败: \$e')),
SnackBar(content: Text('文件加载失败: $e')),
);
}
}

View File

@ -9,14 +9,20 @@ class SingleImageViewer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
backgroundColor: Colors.black.withValues(alpha: 0.7),
appBar: MyAppbar(
backgroundColor: Colors.transparent, title: '',
isBack: false,
actions: [
IconButton(onPressed: () {
Navigator.of(context).pop();
}, icon: Icon(Icons.close, color: Colors.white, size: 40,),)
],
backgroundColor: Colors.black.withValues(alpha:0.7), title: '',
),
body: Center(
child: PhotoView(
imageProvider: NetworkImage(imageUrl),
backgroundDecoration: BoxDecoration(color: Colors.black),
backgroundDecoration: BoxDecoration(color: Colors.black.withValues(alpha:00.5)),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
onTapUp: (context, details, controllerValue) {

View File

@ -607,6 +607,7 @@ U6Hzm1ninpWeE+awIDAQAB
},
);
}
///
static Future<Map<String, dynamic>> dhGetFlowList(String hotworkId) {
return HttpManager().request(
basePath,
@ -617,6 +618,138 @@ U6Hzm1ninpWeE+awIDAQAB
},
);
}
///
static Future<Map<String, dynamic>> getHomeworkFindById(String hotworkId) {
return HttpManager().request(
basePath,
'/app/hotwork/findById',
method: Method.post,
data: {
"HOTWORK_ID":hotworkId,
"CORPINFO_ID":SessionService.instance.corpinfoId,
"USER_ID":SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> submitHotwork(String url, Map data) {
return HttpManager().request(
basePath,
url,
method: Method.post,
data: {
...data
},
);
}
///
static Future<Map<String, dynamic>> hotworkGasList(String hotworkId) {
return HttpManager().request(
basePath,
'/app/hotwork/gas/list',
method: Method.post,
data: {
"HOTWORK_ID":hotworkId,
"tm": DateTime.now().millisecondsSinceEpoch,
"CORPINFO_ID":SessionService.instance.corpinfoId,
"USER_ID":SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> hotworkGasDelete(String hotworkId) {
return HttpManager().request(
basePath,
'/app/hotwork/gas/delete',
method: Method.post,
data: {
"HOTWORK_ID":hotworkId,
"tm": DateTime.now().millisecondsSinceEpoch,
"CORPINFO_ID":SessionService.instance.corpinfoId,
"USER_ID":SessionService.instance.loginUserId,
},
);
}
static Future<Map<String, dynamic>> listSignFinished(String hotworkId) {
return HttpManager().request(
basePath,
'/app/hotwork/listSignFinished',
method: Method.post,
data: {
"HOTWORK_ID":hotworkId,
},
);
}
///
static Future<Map<String, dynamic>> listSignFinishMeasures(String hotworkId) {
return HttpManager().request(
basePath,
'/app/hotwork/listSignFinishMeasures',
method: Method.post,
data: {
"HOTWORK_ID":hotworkId,
},
);
}
///
static Future<Map<String, dynamic>> getEightWorkStartList(Map data) {
return HttpManager().request(
basePath,
'/app/eightwork/startingList',
method: Method.post,
data: {
...data
},
);
}
///
static Future<Map<String, dynamic>> getEightWorkInfo(Map data) {
return HttpManager().request(
basePath,
'/app/eightwork/getInfo',
method: Method.post,
data: {
...data
},
);
}
///
static Future<Map<String, dynamic>> getHotWorkNameList() {
return HttpManager().request(
basePath,
'/app/hotwork/namelist',
method: Method.post,
data: {
"tm": DateTime.now().millisecondsSinceEpoch,
"CORPINFO_ID":SessionService.instance.corpinfoId,
"USER_ID":SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> saveGasTest(
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/gas/save',
fromData: data,
);
}

View File

@ -60,7 +60,8 @@ void main() async {
class MyApp extends StatelessWidget {
final bool isLoggedIn;
const MyApp({super.key, required this.isLoggedIn});
MyApp({super.key, required this.isLoggedIn});
final FocusNode _blankFocusNode = FocusNode(); //
@override
Widget build(BuildContext context) {
@ -72,7 +73,7 @@ class MyApp extends StatelessWidget {
behavior: HitTestBehavior.translucent, //
onTap: () {
//
FocusScope.of(context).unfocus();
FocusScope.of(context).requestFocus(_blankFocusNode);
},
child: child,
);
@ -107,7 +108,6 @@ class MyApp extends StatelessWidget {
debugShowCheckedModeBanner: false,
routes: {
'/login': (_) => const LoginPage(),
//---------
// ...
},

View File

@ -1,8 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../http/ApiService.dart';
import '../../tools/tools.dart';
import '../../../http/ApiService.dart';
import '../../../tools/tools.dart';
class HiddenRollWidget extends StatefulWidget {
///

View File

@ -12,7 +12,7 @@ import 'package:qhd_prevention/pages/home/work/danger_wait_list_page.dart';
import 'package:qhd_prevention/pages/home/work/laws_regulations_page.dart';
import 'package:qhd_prevention/pages/home/workSet_page.dart';
import '../../customWidget/hidden_roll_widget.dart';
import 'hidden_roll_widget.dart';
import '../../http/ApiService.dart';
import '../../tools/tools.dart';
@ -359,7 +359,7 @@ class _HomePageState extends State<HomePage> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(icon, width: 35, height: 35),
const SizedBox(width: 15),
const SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,

View File

@ -128,7 +128,7 @@ class _RiskControlPageState extends State<RiskControlPage> {
),
Expanded(
child: _list.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: ListView.separated(
separatorBuilder: (_, __) => const Divider(height: 1),
itemCount: _list.length,

View File

@ -49,16 +49,7 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
appBar: MyAppbar(title: "课程列表"),
body: SafeArea(
child: _list.isEmpty
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/images/null.png', width: 150),
SizedBox(height: 12),
Text('暂无数据', style: TextStyle(color: Colors.grey)),
],
),
) : ListView.builder(
? NoDataWidget.show() : ListView.builder(
itemCount: _list.length,
itemBuilder: (context, index) {
return _buildItem(_list[index]);

View File

@ -383,16 +383,7 @@ class _StudyMyTaskPageState extends State<StudyMyTaskPage> {
onNotification: _onScroll,
child:
_list.isEmpty
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/images/null.png', width: 150),
SizedBox(height: 12),
Text('暂无数据', style: TextStyle(color: Colors.grey)),
],
),
)
? NoDataWidget.show()
: ListView.builder(
itemCount: _list.length,
itemBuilder: (_, i) => _buildItem(_list[i]),

View File

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import '../../../http/ApiService.dart'; //
import '../../../http/ApiService.dart';
import '../../../tools/tools.dart'; //
class StudyPractisePage extends StatefulWidget {
final String videoCoursewareId;
@ -253,7 +254,7 @@ class _PracticePageState extends State<StudyPractisePage> {
loading
? Center(child: CircularProgressIndicator())
: options.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: Padding(
padding: EdgeInsets.all(16),
child: Column(

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/h_colors.dart';
import '../../../http/ApiService.dart';
import '../../../tools/tools.dart';
class StudyScorePage extends StatefulWidget {
const StudyScorePage({Key? key}) : super(key: key);
@ -229,9 +230,7 @@ class _StudyScorePageState extends State<StudyScorePage> {
Expanded(
child:
list.isEmpty
? Center(
child: Text('暂无数据', style: TextStyle(color: Colors.grey)),
)
? NoDataWidget.show()
: ListView.builder(
controller: _scrollController,
itemCount: list.length + 1,

View File

@ -3,6 +3,8 @@ import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import '../../../tools/tools.dart';
class VideoStudyDetailPage extends StatefulWidget {
final String studentId;
final String classId;
@ -163,7 +165,7 @@ class _VideoStudyDetailPageState extends State<VideoStudyDetailPage> {
body: loading
? Center(child: CircularProgressIndicator())
: questions.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: Padding(
padding: EdgeInsets.all(16),
child: Column(

View File

@ -5,15 +5,19 @@ class ItemListWidget {
///
/// - + TextField
/// - +
static const Color detailtextColor = Colors.black54;
static Widget singleLineTitleText({
required String label, //
required bool isEditable, //
TextEditingController? controller, // 使
String? text, //
String hintText = '请输入',
double fontSize = 15, //
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
child: Row(
mainAxisAlignment: isEditable
? MainAxisAlignment.start
@ -27,19 +31,20 @@ class ItemListWidget {
isEditable
? Expanded(
child: TextField(
autofocus: false,
controller: controller,
style: TextStyle(fontSize: fontSize),
maxLines: 1,
decoration: const InputDecoration(
decoration: InputDecoration(
isDense: true,
hintText: '请输入',
hintText: hintText,
contentPadding: EdgeInsets.symmetric(vertical: 8),
),
),
)
: Text(
text ?? '',
style: TextStyle(fontSize: fontSize, color: Colors.grey[500]),
style: TextStyle(fontSize: fontSize, color: detailtextColor),
overflow: TextOverflow.ellipsis, //
),
],
@ -59,7 +64,8 @@ class ItemListWidget {
double height = 110, //
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
// padding线
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
height: height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -67,28 +73,32 @@ class ItemListWidget {
Text(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
), //
),
const SizedBox(height: 8),
Expanded(
child: isEditable
? TextField(
autofocus: false,
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
expands: true, //
expands: true,
//
textAlignVertical: TextAlignVertical.top,
style: TextStyle(fontSize: fontSize),
decoration: const InputDecoration(
hintText: '请输入'
decoration: InputDecoration(
hintText: '请输入',
// TextField
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
),
)
: Container(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
child: Text(
text ?? '',
style: TextStyle(fontSize: fontSize, color: Colors.grey),
),
: SingleChildScrollView(
// padding
padding: EdgeInsets.zero,
child: Text(
text ?? '',
style: TextStyle(fontSize: fontSize, color: detailtextColor),
),
),
),
@ -97,6 +107,7 @@ class ItemListWidget {
);
}
///
/// - + +
/// - +
@ -110,7 +121,7 @@ class ItemListWidget {
return InkWell(
onTap: isEditable ? onTap : null,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
child: Row(
children: [
// 1.
@ -135,7 +146,7 @@ class ItemListWidget {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: fontSize,
color: isEditable ? Colors.black : Colors.grey,
color: isEditable ? Colors.black : detailtextColor,
),
),
),
@ -171,7 +182,7 @@ class ItemListWidget {
double row2Height = 80, //
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -188,10 +199,10 @@ class ItemListWidget {
Row(
children: [
Text(
isEditable ? (text.isNotEmpty ? text : '请选择') : '',
isEditable ? '请选择' : '',
style: TextStyle(
fontSize: fontSize,
color: isEditable ? Colors.black : Colors.grey,
color: isEditable ? Colors.black : detailtextColor,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@ -208,6 +219,7 @@ class ItemListWidget {
padding: const EdgeInsets.symmetric(vertical: 8),
child: isEditable
? TextField(
autofocus: false,
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
@ -223,7 +235,7 @@ class ItemListWidget {
padding: EdgeInsets.zero,
child: Text(
text,
style: TextStyle(fontSize: fontSize, color: Colors.grey),
style: TextStyle(fontSize: fontSize, color: detailtextColor),
),
),
),
@ -246,27 +258,31 @@ class ItemListWidget {
double row2Height = 80, //
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// +
InkWell(
onTap: isEditable ? onTap : null,
child: Row(
children: [
Text(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
Flexible(
child: Text(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
CustomButton(
text: "选择其他",
height: 30,
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
backgroundColor: Colors.green,
onPressed: onTap,
),
if (isEditable)
CustomButton(
text: "选择其他",
height: 30,
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
backgroundColor: Colors.green,
onPressed: onTap,
),
],
),
),
@ -277,6 +293,7 @@ class ItemListWidget {
padding: const EdgeInsets.symmetric(vertical: 8),
child: isEditable
? TextField(
autofocus: false,
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
@ -292,7 +309,7 @@ class ItemListWidget {
padding: EdgeInsets.zero,
child: Text(
text,
style: TextStyle(fontSize: fontSize, color: Colors.grey),
style: TextStyle(fontSize: fontSize, color: detailtextColor),
),
),
),
@ -300,5 +317,46 @@ class ItemListWidget {
),
);
}
///
/// + +
static Widget OneRowButtonTitleText({
required String label, //
required String text, //
required VoidCallback? onTap, //
double fontSize = 15, //
}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
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),
),
),
],
),),
CustomButton(
text: "分析详情",
height: 30,
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
backgroundColor: Colors.green,
onPressed: onTap,
),
],
)
);
}
}

View File

@ -1,336 +0,0 @@
import 'package:flutter/material.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 '../../../../../../customWidget/bottom_picker.dart';
import '../../../../../../http/ApiService.dart';
import '../../../../../my_appbar.dart';
enum EditUserType {
analyze('分析单位'),
confirm('作业负责人单位'),
guardian('监护人单位'),
confess('安全交底人单位'),
acceptconfess('接受交底人单位'),
workstart('作业开始负责人单位'),
workend('作业结束负责人单位'),
leader('安全管理部门'),
audit('审核部门'),
approve('动火审批单位'),
monitor('动火前在岗部门'),
accept('验收部门');
///
final String displayName;
const EditUserType(this.displayName);
}
class HotworkApplyDetail extends StatefulWidget {
const HotworkApplyDetail({
super.key,
required this.HOTWORK_ID,
required this.flow,
});
final String HOTWORK_ID;
final String flow;
@override
State<HotworkApplyDetail> createState() => _HotworkApplyDetailState();
}
class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
final bool isEditable = true;
///
late String msg = 'add';
//
final Map<EditUserType, String> _selectedUnitId = {};
final Map<EditUserType, String> _selectedUnitName = {};
final Map<EditUserType, String> _selectedPersonId = {};
final Map<EditUserType, String> _selectedPersonName = {};
//
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
Widget _defaultDetail() {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ItemListWidget.singleLineTitleText(
label: '申请单位:',
isEditable: false,
text: '1111',
),
Divider(),
ItemListWidget.singleLineTitleText(
label: '申请人:',
isEditable: false,
text: '1111',
),
Divider(),
ItemListWidget.multiLineTitleTextField(
label: '作业内容:',
isEditable: isEditable,
text: '1111',
),
Divider(),
ItemListWidget.singleLineTitleText(
label: '动火地点及动火部位:',
isEditable: isEditable,
text: '1111',
),
Divider(),
ItemListWidget.selectableLineTitleTextField(
label: '动火作业级别',
isEditable: isEditable,
onTap: () {},
text: '',
),
Divider(),
ItemListWidget.singleLineTitleText(
label: '动火方式:',
isEditable: isEditable,
text: '1111',
),
Divider(),
ItemListWidget.twoRowSelectableTitleText(
label: '动火人及证书编号:',
isEditable: isEditable,
text: '1111',
),
Divider(),
ItemListWidget.twoRowButtonTitleText(
label: '关联的其他特殊作业及安全作业票编号',
isEditable: isEditable,
text: '1111',
hintText: '请输入关联的其他特殊作业及安全作业票编号',
onTap: () {},
),
Divider(),
ItemListWidget.twoRowButtonTitleText(
label: '风险辨识结果',
isEditable: isEditable,
text: '1111',
hintText: '请输入风险辨识结果',
onTap: () {},
),
],
);
}
Widget _card(Widget child) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: child,
);
}
Widget _chooseItem(String unitLabel, String personLabel, EditUserType type) {
return Column(
children: [
ItemListWidget.selectableLineTitleTextField(
label: unitLabel,
isEditable: isEditable,
text: _selectedUnitName[type] ?? '请选择',
onTap: () => chooseUnitHandle(type),
),
Divider(),
ItemListWidget.selectableLineTitleTextField(
label: personLabel,
isEditable: isEditable,
text: _selectedPersonName[type] ?? '请选择',
onTap: () => choosePersonHandle(type),
),
],
);
}
///
void chooseUnitHandle(EditUserType type) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
barrierColor: Colors.black54,
backgroundColor: Colors.transparent,
builder:
(_) => DepartmentPicker(
onSelected: (id, name) async {
setState(() {
_selectedUnitId[type] = id;
_selectedUnitName[type] = name;
//
_selectedPersonId.remove(type);
_selectedPersonName.remove(type);
});
//
final result = await ApiService.getListTreePersonList(id);
_personCache[type] = List<Map<String, dynamic>>.from(
result['userList'] as List,
);
},
),
);
}
///
void choosePersonHandle(EditUserType type) {
final unitId = _selectedUnitId[type];
final personList = _personCache[type] ?? [];
if (unitId == null || personList.isEmpty) {
final unitName = type.displayName;
ToastUtil.showNormal(context, '请先选择$unitName');
return;
}
DepartmentPersonPicker.show(
context,
personsData: personList,
onSelected: (userId, name) {
setState(() {
_selectedPersonId[type] = userId;
_selectedPersonName[type] = name;
});
},
);
}
Future<void> _submit(String STATUS) async {
// '1' 0
}
///
Future<void> _getData() async {
// '1' 0
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '动火安全作业申请'),
body: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.all(12),
child: Column(
children: [
_card(_defaultDetail()),
SizedBox(height: 15),
_card(_chooseItem('分析单位', '分析单位负责人', EditUserType.analyze)),
SizedBox(height: 15),
_card(_chooseItem('监护人单位', '监护人', EditUserType.guardian)),
SizedBox(height: 15),
_card(_chooseItem('安全交底人单位', '安全交底人', EditUserType.confess)),
SizedBox(height: 15),
_card(
_chooseItem('接受交底人单位', '接受交底人', EditUserType.acceptconfess),
),
SizedBox(height: 15),
_card(_chooseItem('作业负责人单位', '作业负责人', EditUserType.confirm)),
SizedBox(height: 15),
_card(_chooseItem('安全管理部门', '所在单位负责人', EditUserType.leader)),
SizedBox(height: 15),
_card(_chooseItem('动火审批单位', '动火审批负责人', EditUserType.approve)),
SizedBox(height: 15),
_card(_chooseItem('动火前在岗部门', '动火前在岗班长', EditUserType.monitor)),
SizedBox(height: 15),
_card(
_chooseItem('作业开始负责人单位', '作业开始负责人', EditUserType.workstart),
),
SizedBox(height: 15),
_card(
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_chooseItem('作业开始负责人单位', '作业开始负责人', EditUserType.workend),
Divider(),
Row(
children: [
SizedBox(width: 12),
Text(
'友情提示:负责填写作业实际开始时间',
style: TextStyle(color: Colors.red),
),
],
),
SizedBox(height: 5),
],
),
),
SizedBox(height: 15),
_card(
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_chooseItem('作业结束负责人单位', '作业结束负责人', EditUserType.workend),
Divider(),
Row(
children: [
SizedBox(width: 12),
Text(
'友情提示:负责填写作业实际结束时间',
style: TextStyle(color: Colors.red),
),
],
),
SizedBox(height: 5),
],
),
),
SizedBox(height: 15),
_card(_chooseItem('验收部门', '验收部门负责人', EditUserType.accept)),
SizedBox(height: 15),
Row(
spacing: 10,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: CustomButton(
height: 45,
textStyle: TextStyle(fontSize: 16, color: Colors.white),
text: '提交',
backgroundColor: Colors.blue,
onPressed: () {
_submit('1');
},
),
),
Expanded(
child: CustomButton(
textStyle: TextStyle(fontSize: 16, color: Colors.white),
text: '暂存',
backgroundColor: Colors.green,
onPressed: () {
_submit('1');
},
),
),
],
),
],
),
),
),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
if (widget.HOTWORK_ID.length > 0) {
msg = 'edit';
_getData();
}
}
}

View File

@ -0,0 +1,714 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import '../../../../../../customWidget/date_picker_dialog.dart';
import '../../../../../../http/ApiService.dart';
import '../../../../../../tools/tools.dart';
import 'hotwork_apply_detail.dart';
///
class MeasuresListWidget extends StatelessWidget {
/// Map
final List<Map<String, dynamic>> measuresList;
///
final String baseImgPath;
const MeasuresListWidget({
Key? key,
required this.measuresList,
required this.baseImgPath,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (measuresList.isEmpty) {
return const Center(child: Text('暂无更多数据'));
}
return SingleChildScrollView(
padding: const EdgeInsets.only(top: 10, bottom: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Container(
margin: const EdgeInsets.symmetric(horizontal: 5),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(4),
),
child: Table(
columnWidths: const {
0: FlexColumnWidth(3),
1: FixedColumnWidth(100),
},
border: TableBorder(
horizontalInside: BorderSide(color: Colors.grey.shade300),
verticalInside: BorderSide(color: Colors.grey.shade300),
),
children: [
//
TableRow(
decoration: BoxDecoration(color: Colors.grey.shade100),
children: const [
Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Text(
'主要安全措施',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Text(
'操作',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
],
),
//
for (var item in measuresList)
TableRow(
children: [
// + +
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Text(item['PROTECTIVE_MEASURES'] as String? ?? ''),
// +
if (item.containsKey('SIGN_PATH') &&
(item['SIGN_PATH'] as String).isNotEmpty)
..._buildImageRows(
context,
(item['SIGN_PATH'] as String).split(','),
item['SIGN_TIME'] as String? ?? '',
),
// 14 +
for (var i = 1; i <= 4; i++)
if (item.containsKey('QUESTION$i'))
_buildQnA(
item['QUESTION$i'] as String? ?? '',
item['ANSWER$i'] as String? ?? '0',
),
],
),
),
// +
Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
//
Text(
(item['STATUS'] as String?) == '-1'
? '不涉及'
: '涉及',
style: TextStyle(
color:
(item['STATUS'] as String?) == '-1'
? Colors.grey
: Colors.black,
),
),
//
if (item.containsKey('IMG_PATH') &&
(item['IMG_PATH'] as String).isNotEmpty)
..._buildImageRows(
context,
(item['IMG_PATH'] as String).split(','),
'',
),
],
),
),
],
),
],
),
),
],
),
);
}
/// +
Widget _buildQnA(String question, String answer) {
return Padding(
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()
],
)
);
}
/// +
List<Widget> _buildImageRows(BuildContext context, List<String> paths, String time) {
return paths.map((p) {
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
children: [
GestureDetector(
onTap: () {
Navigator.of(context).push(PageRouteBuilder(
opaque: false,
pageBuilder: (_, __, ___) => SingleImageViewer(imageUrl: '$baseImgPath$p'),
));
},
child: Image.network(
'$baseImgPath$p',
width: 80,
height: 80,
),
),
if (time.isNotEmpty) ...[const SizedBox(width: 8), Text(time)],
],
),
);
}).toList();
}
}
///
class OtherMeasuresWidget extends StatelessWidget {
///
final List<dynamic>? otherMeasures;
final String baseImgPath;
const OtherMeasuresWidget({
Key? key,
required this.otherMeasures,
required this.baseImgPath,
}) : super(key: key);
@override
Widget build(BuildContext context) {
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(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(4),
),
child: Table(
border: TableBorder(
horizontalInside: BorderSide(color: Colors.grey.shade300),
verticalInside: BorderSide(color: Colors.grey.shade300),
),
children: [
//
TableRow(
decoration: BoxDecoration(color: Colors.grey.shade100),
children: const [
Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Text('其他安全措施', style: TextStyle(fontWeight: FontWeight.bold)),
),
),
Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Text('签字', style: TextStyle(fontWeight: FontWeight.bold)),
),
),
],
),
//
for (var item in list)
TableRow(
children: [
//
Padding(
padding: const EdgeInsets.all(8),
child: Text(item['DESCR'] as String? ?? ''),
),
//
Padding(
padding: const EdgeInsets.all(8),
child: _buildSignImages(context, item),
),
],
),
],
),
),
],
);
}
/// SIGN_PATH List String
Widget _buildSignImages(BuildContext context, Map<String, dynamic> item) {
final dynamic raw = item['SIGN_PATH'];
List<String> paths = [];
if (raw is String && raw.isNotEmpty) {
paths = [raw];
} else if (raw is List) {
paths = raw.cast<String>();
}
if (paths.isEmpty) return const SizedBox.shrink();
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(),
);
}
}
///
class SignaturesListWidget extends StatelessWidget {
final Map<String, dynamic>? signs;
final Map<String, dynamic>? pd;
final String baseImgPath;
const SignaturesListWidget({
Key? key,
this.signs,
this.pd,
required this.baseImgPath,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final safeSigns = signs ?? {};
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'),
],
);
}
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;
final name = userName is String ? userName : '';
//
final signPaths = <String>[];
final signTimes = <String>[];
if (first['SIGN_PATH'] != null) {
final rawPath = first['SIGN_PATH'];
if (rawPath is String && rawPath.isNotEmpty) signPaths.add(rawPath);
if (rawPath is List) signPaths.addAll(rawPath.cast<String>());
}
if (first['SIGN_TIME'] != null) {
final rawTime = first['SIGN_TIME'];
if (rawTime is String && rawTime.isNotEmpty) signTimes.add(rawTime);
if (rawTime is List) signTimes.addAll(rawTime.cast<String>());
}
return Container(
margin: const EdgeInsets.only(top: 20),
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)),
),
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(),
),
),
for (var i = 0; i < signPaths.length; i++)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
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)),
),
const SizedBox(width: 16),
Expanded(child: Text(i < signTimes.length ? signTimes[i] : '')),
],
),
),
],
),
);
}
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) {
final rawPath = first['SIGN_PATH'];
if (rawPath is String && rawPath.isNotEmpty) signPaths.add(rawPath);
if (rawPath is List) signPaths.addAll(rawPath.cast<String>());
}
if (first['SIGN_TIME'] != null) {
final rawTime = first['SIGN_TIME'];
if (rawTime is String && rawTime.isNotEmpty) signTimes.add(rawTime);
if (rawTime is List) signTimes.addAll(rawTime.cast<String>());
}
return Container(
margin: const EdgeInsets.only(top: 20),
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: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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)),
],
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text('$personDes: $name'),
),
for (var i = 0; i < signPaths.length; i++)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
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)),
),
const SizedBox(width: 16),
Expanded(child: Text(i < signTimes.length ? signTimes[i] : '')),
],
),
),
],
),
);
}
}
///
class SelectionPopup extends StatefulWidget {
/// : 'assignments' 'identification'
final String type;
///
final String initialValue;
///
final void Function(String) onConfirm;
const SelectionPopup({
Key? key,
required this.type,
required this.initialValue,
required this.onConfirm,
}) : super(key: key);
@override
_SelectionPopupState createState() => _SelectionPopupState();
}
class _SelectionPopupState extends State<SelectionPopup> {
late List<Map<String, String>> workList;
late String selectedWorkType;
late String selectedWorkName;
DateTime? selectedDate;
List<String> selectValue = [];
List<Map<String, dynamic>> list = [];
@override
void initState() {
super.initState();
//
workList = [
{'WORK_TYPE': '', 'WORK_NAME': '作业选择'},
{'WORK_TYPE': 'HOTWORK', 'WORK_NAME': '动火作业'},
{'WORK_TYPE': 'CONFINEDSPACE', 'WORK_NAME': '受限作业'},
{'WORK_TYPE': 'HIGHWORK', 'WORK_NAME': '高处作业'},
{'WORK_TYPE': 'ELECTRICITY', 'WORK_NAME': '临时用电'},
{'WORK_TYPE': 'BREAKGROUND', 'WORK_NAME': '动土作业'},
{'WORK_TYPE': 'HOISTING', 'WORK_NAME': '吊装作业'},
{'WORK_TYPE': 'CUTROAD', 'WORK_NAME': '断路作业'},
{'WORK_TYPE': 'BLINDBOARD', 'WORK_NAME': '盲板作业'},
];
selectedWorkType = workList[0]['WORK_TYPE']!;
selectedWorkName = workList[0]['WORK_NAME']!;
//
if (widget.initialValue.isNotEmpty) {
selectValue = widget.initialValue.split(',');
}
//
_getData();
}
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;
});
},
),
);
}
Future<void> _getData() async {
//
Map<String, dynamic> params;
if (widget.type == 'assignments') {
params = {
'WORK_TYPE': selectedWorkType,
'KEYWORDS': selectedDate == null ? '' : selectedDate!.toString().split(' ')[0],
'CORPINFO_ID': SessionService.instance.corpinfoId,
};
} else {
params = {
'vectors': jsonEncode(['accidentType']),
};
}
try {
if (widget.type == 'assignments') {
final result = await ApiService.getEightWorkStartList(params);
setState(() {
list = (result['varList'] as List).cast<Map<String, dynamic>>();
for (var item in list) {
final type = item['WORK_TYPE'] as String? ?? '';
final no = item['CHECK_NO'] as String? ?? '';
final prefixMap = {
'HOTWORK': '动火作业',
'CONFINEDSPACE': '受限作业',
'HIGHWORK': '高处作业',
'ELECTRICITY': '临时用电',
'BREAKGROUND': '动土作业',
'HOISTING': '吊装作业',
'CUTROAD': '断路作业',
'BLINDBOARD': '盲板作业',
};
item['CHECK_NO'] = (prefixMap[type] ?? '') + ' ' + no;
}
});
}else{
final result = await ApiService.getEightWorkInfo(params);
setState(() {
list = (result['accidentType'] as List).cast<Map<String, dynamic>>();
});
}
} catch (e) {
ToastUtil.showError(context, '$e');
}
}
void _reset() {
setState(() {
selectedWorkType = '';
selectedWorkName = '请选择';
selectedDate = null;
list.clear();
selectValue.clear();
});
_getData();
}
void _determine() {
//
final result = selectValue.join(',');
widget.onConfirm(result);
Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
return Dialog(
insetPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 40),
child: SizedBox(
width: double.infinity,
height: 800,
child: Column(
children: [
//
if (widget.type == 'assignments')
Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
//
Expanded(
child: DropdownButton<String>(
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),
TextButton(onPressed: _pickDate, child: Row(
children: [
Text(selectedDate == null
? '选择作业申请时间'
: selectedDate!.toString().split(' ')[0]),
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,)
],
),
),
const Divider(),
//
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 0),
itemCount: list.length,
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 checked = value.contains(key);
return CheckboxListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(key),
if (widget.type == 'assignments') ...[
Text('作业内容: ${item['WORK_CONTENT'] ?? ''}'),
Text('作业负责人: ${item['CONFIRM_USER_NAME'] ?? ''}'),
Text('作业申请时间: ${item['CREATTIME'] ?? ''}'),
],
],
),
value: checked,
onChanged: (v) {
setState(() {
if (v == true)
selectValue.add(key);
else
selectValue.remove(key);
});
},
);
},
),
),
// /
Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Expanded(
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(),)
),
],
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
class HotSafeWorkChoosePage extends StatefulWidget {
const HotSafeWorkChoosePage({super.key});
@override
State<HotSafeWorkChoosePage> createState() => _HotSafeWorkChoosePageState();
}
class _HotSafeWorkChoosePageState extends State<HotSafeWorkChoosePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: 'dong'),
);
}
}

View File

@ -0,0 +1,792 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/department_person_picker.dart';
import 'package:qhd_prevention/customWidget/department_picker.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/dh_work_detai/hotwork_gas_list.dart';
import 'package:qhd_prevention/tools/tools.dart';
import '../../../../../../customWidget/bottom_picker.dart';
import '../../../../../../http/ApiService.dart';
import '../../../../../my_appbar.dart';
import 'MeasuresListWidget.dart';
enum EditUserType {
ANALYZE('分析单位', '分析单位负责人'),
GUARDIAN('监护人单位', '监护人'),
CONFESS('安全交底人单位', '安全交底人'),
ACCEPT_CONFESS('接受交底人单位', '接受交底人'),
CONFIRM('作业负责人单位', '作业负责人'),
LEADER('所在单位', '所在单位负责人'),
AUDIT('安全管理部门', '安全管理部门负责人'),
APPROVE('动火审批单位', '动火审批负责人'),
MONITOR('动火前在岗部门', '动火前在岗班长'),
WORK_START('作业开始负责人单位', '作业开始负责人'),
WORK_END('作业结束负责人单位', '作业结束负责人'),
ACCEPT('验收部门', '验收部门负责人');
///
final String displayName;
final String personName;
const EditUserType(this.displayName, this.personName);
}
class HotworkApplyDetail extends StatefulWidget {
const HotworkApplyDetail({
super.key,
required this.HOTWORK_ID,
required this.flow,
});
final String HOTWORK_ID;
final String flow;
@override
State<HotworkApplyDetail> createState() => _HotworkApplyDetailState();
}
class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
late bool isEditable = false;
final levelList = ["特级", "一级", "二级"];
///
late String msg = 'add';
///
late Map<String, dynamic> pd = {};
late Map<String, dynamic> signs = {};
late List<Map<String, dynamic>> measuresList = [];
final TextEditingController _contentController = TextEditingController();
final TextEditingController _locationController = TextEditingController();
final TextEditingController _methodController = TextEditingController();
final TextEditingController _hotworkPersonController =
TextEditingController();
final TextEditingController _relatedController = TextEditingController();
final TextEditingController _riskController = TextEditingController();
///
late List<dynamic> workUserList = [];
//
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
@override
void initState() {
super.initState();
if (widget.HOTWORK_ID.length > 0) {
msg = 'edit';
_getData();
} else {
isEditable = true;
pd['ANALYZE_TIME'] = 1;
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;
}
_getHotWorkNameList();
_contentController.addListener(() {
setState(() {
pd['WORK_CONTENT'] = _contentController.text.trim();
});
});
_locationController.addListener(() {
pd['WORK_PLACE'] = _locationController.text.trim();
});
_methodController.addListener(() {
pd['WORK_FUNCTION'] = _methodController.text.trim();
});
_hotworkPersonController.addListener(() {
pd['WORK_USER'] = _hotworkPersonController.text.trim();
});
_relatedController.addListener(() {
pd['SPECIAL_WORK'] = _relatedController.text.trim();
});
_riskController.addListener(() {
pd['RISK_IDENTIFICATION'] = _riskController.text.trim();
});
}
void set_pd_DEPARTMENT_ID(EditUserType type, String id) {
pd['${type.name}_DEPARTMENT_ID'] = id;
}
void set_pd_DEPARTMENT_NAME(EditUserType type, String name) {
pd['${type.name}_DEPARTMENT_NAME'] = name;
}
void set_pd_USER_ID(EditUserType type, String id) {
pd['${type.name}_USER_ID'] = id;
}
void set_pd_USER_Name(EditUserType type, String name) {
pd['${type.name}_USER_NAME'] = name;
}
String get_pd_DEPARTMENT_ID(EditUserType type) {
return pd['${type.name}_DEPARTMENT_ID'] ?? '';
}
String get_pd_DEPARTMENT_NAME(EditUserType type) {
return pd['${type.name}_DEPARTMENT_NAME'] ?? '';
}
String get_pd_USER_ID(EditUserType type) {
return pd['${type.name}_USER_ID'] ?? '';
}
String get_pd_USER_Name(EditUserType type) {
return pd['${type.name}_USER_NAME'] ?? '';
}
Future<void> _chooseLevel() async {
final choice = await BottomPicker.show<String>(
context,
items: levelList,
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
initialIndex: 0,
);
if (choice != null) {
// choice
setState(() {
pd['WORK_LEVEL'] = choice;
FocusHelper.clearFocus(context);
});
}
}
Future<void> _chooseHorkUser() async{
final choice = await BottomPicker.show<String>(
context,
items: workUserList.map((item) => item['NAME'] as String).toList(),
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
initialIndex: 0,
);
if (choice != null) {
setState(() {
pd['WORK_USER'] = choice;
_hotworkPersonController.text = choice;
Map<String, dynamic> result = workUserList.firstWhere(
(item) => item['NAME'] == choice,
orElse: () => {}, //
);
if (FormUtils.hasValue(result, 'USER_ID')) {
pd['WORK_USER_ID'] = result['USER_ID'];
}
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(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: child,
);
}
Widget _chooseItem(EditUserType type) {
return Column(
children: [
ItemListWidget.selectableLineTitleTextField(
label: type.displayName,
isEditable: isEditable,
text: pd['${type.name}_DEPARTMENT_NAME'] ?? '请选择',
onTap: () => chooseUnitHandle(type),
),
Divider(),
ItemListWidget.selectableLineTitleTextField(
label: type.personName,
isEditable: isEditable,
text: pd['${type.name}_USER_NAME'] ?? '请选择',
onTap: () => choosePersonHandle(type),
),
],
);
}
///
void chooseUnitHandle(EditUserType type) {
FocusHelper.clearFocus(context);
showModalBottomSheet(
context: context,
isScrollControlled: true,
barrierColor: Colors.black54,
backgroundColor: Colors.transparent,
builder:
(_) => DepartmentPicker(
onSelected: (id, name) async {
setState(() {
set_pd_DEPARTMENT_ID(type, id);
set_pd_DEPARTMENT_NAME(type, name);
set_pd_USER_ID(type, '');
set_pd_USER_Name(type, '');
});
_getPersonListForUnitId(id, type);
},
),
).then((_) {
FocusHelper.clearFocus(context);
});
}
Future<void> _getPersonListForUnitId(String id, EditUserType type) async {
//
final result = await ApiService.getListTreePersonList(id);
setState(() {
_personCache[type] = List<Map<String, dynamic>>.from(
result['userList'] as List,
);
});
}
///
void choosePersonHandle(EditUserType type) async{
FocusHelper.clearFocus(context);
String unitId = get_pd_DEPARTMENT_ID(type);
final personList = _personCache[type] ?? [];
if (!unitId.isNotEmpty) {
final unitName = type.displayName;
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);
}
return;
}
DepartmentPersonPicker.show(
context,
personsData: personList,
onSelected: (userId, name) {
setState(() {
set_pd_USER_ID(type, userId);
set_pd_USER_Name(type, name);
});
},
).then((_) {
FocusHelper.clearFocus(context);
});
}
/// 1 0
Future<void> _submit(String status) async {
//
final textRules = <Map<String, dynamic>>[
{'value': _contentController.text.trim(), 'message': '请填写作业内容'},
{'value': _locationController.text.trim(), 'message': '请填写动火地点及部位'},
{'value': _methodController.text.trim(), 'message': '请填写动火方式'},
{'value': _hotworkPersonController.text.trim(), 'message': '请填写动火人及证书编号'},
{
'value': _relatedController.text.trim(),
'message': '请输入关联的其他特殊作业及安全作业票编号',
},
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
];
final level = pd['WORK_LEVEL'] ?? '';
print('---level-$level');
///
final unitRules = <EditUserType>[
EditUserType.ANALYZE,
EditUserType.GUARDIAN,
EditUserType.CONFESS,
EditUserType.ACCEPT_CONFESS,
EditUserType.CONFIRM,
EditUserType.LEADER,
EditUserType.AUDIT,
EditUserType.APPROVE,
EditUserType.MONITOR,
EditUserType.WORK_START,
EditUserType.WORK_END,
EditUserType.ACCEPT,
];
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 (var type in unitRules) {
if (get_pd_DEPARTMENT_ID(type).length == 0) {
ToastUtil.showNormal(context, '请选择${type.displayName}');
return;
}
if (get_pd_USER_ID(type).length == 0) {
ToastUtil.showNormal(context, '请选择${type.displayName}负责人');
return;
}
}
}
// LoadingDialogHelper.show(context);
String taskId = '0';
if (level == '特级') {
taskId = '1';
} else if (level == '一级') {
taskId = '2';
} else if (level == '二级') {
taskId = '3';
}
//
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['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_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);
}
} 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'];
if (pd['STEP_ID'] == 0) {
isEditable = true;
} else {
_getSigns(pd['HOTWORK_ID'] ?? '');
_getMeasures(pd['HOTWORK_ID'] ?? '');
}
//
_contentController.text = pd['WORK_CONTENT'] ?? '';
_locationController.text = pd['WORK_PLACE'] ?? '';
_methodController.text = pd['WORK_FUNCTION'] ?? '';
_hotworkPersonController.text = pd['WORK_USER'] ?? '';
_relatedController.text = pd['SPECIAL_WORK'] ?? '';
_riskController.text = pd['RISK_IDENTIFICATION'] ?? '';
});
// final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
// setState(() {
// pd = data['pd'];
// });
// LoadingDialogHelper.hide(context);
}
Future<void> _getSigns(String homework_id) async {
final data = await ApiService.listSignFinished(
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
);
setState(() {
signs = data['signs'] ?? {};
});
}
Future<void> _getMeasures(String homework_id) async {
final data = await ApiService.listSignFinishMeasures(
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
);
setState(() {
measuresList = List<Map<String, dynamic>>.from(
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '动火安全作业申请'),
body: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.all(12),
child: Column(
children: [
_card(_defaultDetail()),
if (isEditable)
Column(
children: [
SizedBox(height: 15),
_card(_chooseItem(EditUserType.ANALYZE)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.GUARDIAN)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.CONFESS)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.ACCEPT_CONFESS)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.CONFIRM)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.LEADER)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.AUDIT)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.APPROVE)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.MONITOR)),
SizedBox(height: 15),
_card(
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_chooseItem(EditUserType.WORK_START),
Divider(),
Row(
children: [
SizedBox(width: 12),
Text(
'友情提示:负责填写作业实际开始时间',
style: TextStyle(color: Colors.red),
),
],
),
SizedBox(height: 5),
],
),
),
SizedBox(height: 15),
_card(
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_chooseItem(EditUserType.WORK_END),
Divider(),
Row(
children: [
SizedBox(width: 12),
Text(
'友情提示:负责填写作业实际结束时间',
style: TextStyle(color: Colors.red),
),
],
),
SizedBox(height: 5),
],
),
),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.ACCEPT)),
SizedBox(height: 15),
],
),
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,
),
),
],
),
if (FormUtils.hasValue(signs, 'MEASURES_CONFIRM'))
Column(
children: [
SizedBox(height: 20),
ListItemFactory.createBuildSimpleSection('其他安全防护措施'),
Container(
color: Colors.white,
child: OtherMeasuresWidget(
otherMeasures: signs['MEASURES_CONFIRM'],
baseImgPath: ApiService.baseImgPath,
),
),
],
),
SignaturesListWidget(
signs: signs,
pd: pd,
baseImgPath: ApiService.baseImgPath,
),
isEditable
? Row(
spacing: 10,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: CustomButton(
height: 45,
textStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
text: '提交',
backgroundColor: Colors.blue,
onPressed: () {
_submit('1');
},
),
),
Expanded(
child: CustomButton(
textStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
text: '暂存',
backgroundColor: Colors.green,
onPressed: () {
_submit('0');
},
),
),
],
)
: Column(
children: [
SizedBox(height: 20),
Row(
children: [
SizedBox(width: 50),
Expanded(
child: CustomButton(
height: 45,
textStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
text: '返回',
backgroundColor: Colors.green,
onPressed: () {
Navigator.pop(context);
},
),
),
SizedBox(width: 50),
],
),
],
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,188 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import '../../../../../../customWidget/single_image_viewer.dart';
import '../../../../../../tools/tools.dart';
///
class HotworkGasList extends StatefulWidget {
const HotworkGasList({
super.key,
required this.HOTWORK_ID,
this.addFlag = false,
});
final String HOTWORK_ID;
final bool addFlag;
@override
State<HotworkGasList> createState() => _HotworkGasListState();
}
class _HotworkGasListState extends State<HotworkGasList> {
List list = [];
bool isLoading = true;
Future<void> _getListData() async {
final data = await ApiService.hotworkGasList(widget.HOTWORK_ID);
setState(() {
list = data['varList'] ?? [];
isLoading = false;
});
}
void _deleteItem(String id) async {
showDialog<void>(
context: context,
builder:
(_) => CustomAlertDialog(
title: '温馨提示',
content: '确定要删除这条记录?',
cancelText: '取消',
onCancel: () {},
onConfirm: () async {
final result = await ApiService.hotworkGasDelete(id);
if (result['result']) {
ToastUtil.showNormal(context, '删除成功');
_getListData();
}
},
),
);
}
Widget _buildListItem(Map item) {
final images = (item['SIGN_PATH'] as String?)?.split(',') ?? [];
final baseImgPath = ApiService.baseImgPath;
return Container(
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('分析时间: ${item['ANALYZE_TIME'] ?? ''}'),
Text('代表性气体: ${item['ANALYZE_GAS'] ?? ''}'),
],
),
const SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('分析数据: ${item['ANALYZE_RESULT'] ?? ''}'),
if ((item['ANALYZE_PLACE'] ?? '').toString().isNotEmpty)
Text('分析地点: ${item['ANALYZE_PLACE']}'),
],
),
const SizedBox(height: 6),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('分析人:'),
const SizedBox(width: 8),
Expanded(
child: Wrap(
spacing: 8,
runSpacing: 4,
children: List.generate(images.length, (i) {
final img = baseImgPath + images[i];
return GestureDetector(
onTap: () {
present(
SingleImageViewer(imageUrl: img),
context,
);
},
child: Image.network(
img,
width: 50,
height: 30,
fit: BoxFit.cover,
),
);
}),
),
),
if (widget.addFlag)
TextButton(
onPressed: () =>
_deleteItem(item['HOTWORKGAS_ID'].toString()),
style: TextButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(horizontal: 12),
minimumSize: const Size(0, 32),
),
child: const Text(
'删除',
style: TextStyle(color: Colors.white),
),
),
],
)
],
),
),
);
}
Widget _buildListContent() {
if (isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (list.isEmpty) {
return NoDataWidget.show();
}
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) {
final item = list[index];
return _buildListItem(item);
},
);
}
@override
void initState() {
super.initState();
_getListData();
}
void _pyshAddGas() async {
await pushPage(HomeGasTestPage(HOTWORK_ID: widget.HOTWORK_ID,), context);
_getListData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(
title: '气体分析详情',
actions: [
if (widget.addFlag)
TextButton(
onPressed: () {
_pyshAddGas();
},
child: const Text('添加', style: TextStyle(color: Colors.white, fontSize: 16)),
)
],
),
body: _buildListContent(),
);
}
}

View File

@ -0,0 +1,280 @@
import 'dart:io';
import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
import '../../../../../customWidget/custom_alert_dialog.dart';
import '../../../../../customWidget/picker/CupertinoDatePicker.dart';
class HomeGasTestPage extends StatefulWidget {
const HomeGasTestPage({Key? key, required this.HOTWORK_ID}) : super(key: key);
final String HOTWORK_ID;
@override
_HomeGasTestPageState createState() => _HomeGasTestPageState();
}
class _HomeGasTestPageState extends State<HomeGasTestPage> {
String _selectData = '';
List<String> imagePaths = [];
List<String> signTimes = []; //
final TextEditingController _gasController = TextEditingController();
final TextEditingController _resultController = TextEditingController();
final TextEditingController _addressController = TextEditingController();
bool _loading = false;
@override
void initState() {
super.initState();
}
Future<void> _chooseDatePicker() async {
DateTime? picked = await BottomDateTimePicker.show(context);
if (picked != null) {
setState(() {
_selectData = DateFormat('yyyy-MM-dd HH:mm').format(picked);
});
}
}
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);
});
}
}
Widget _signListWidget() {
return Column(
children: imagePaths.map((path) {
return Column(
children: [
const SizedBox(height: 10),
const DottedLine(
dashLength: 6.0,
dashGapLength: 4.0,
lineThickness: 0.5,
dashColor: Colors.grey,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.file(
File(path),
width: 200,
height: 150,
fit: BoxFit.cover,
),
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(),
);
}
Future<void> _submit(int status) async {
if (imagePaths.isEmpty) {
ToastUtil.showNormal(context, '请签字');
return;
}
if (status == 1) {
if (_selectData.isEmpty || _gasController.text.isEmpty || _resultController.text.isEmpty || _addressController.text.isEmpty) {
ToastUtil.showNormal(context, '请完善所有字段');
return;
}
}
String reasonText = '';
if (status != 1) {
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;
}
}
bool isConfirm = false;
await showDialog<bool>(
context: context,
builder: (_) => CustomAlertDialog(
title: '请确认',
content: status == 1 ? '通过本作业票?' : '作废本作业票?',
onConfirm: () {
isConfirm = true;
},
),
);
if (isConfirm != true) return;
setState(() => _loading = true);
final formData = {
'HOTWORK_ID': widget.HOTWORK_ID,
'ANALYZE_TIME': _selectData,
'ANALYZE_GAS': _gasController.text,
'ANALYZE_RESULT': _resultController.text,
'ANALYZE_PLACE': _addressController.text,
'ANALYZE_USER': SessionService.instance.username,
'APPLY_STATUS': status,
'STEP_REASON': reasonText ?? '',
'SIGNTIME':signTimes.join(','),
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
};
try {
await ApiService.saveGasTest(formData, imagePaths);
ToastUtil.showNormal(context, status == 1 ? '保存成功' : '作废成功');
Navigator.of(context).pop(status == 1);
} catch (e) {
ToastUtil.showNormal(context, e.toString());
} finally {
setState(() => _loading = false);
}
}
Future<void> setRequest() async {
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '气体检测'),
body: _loading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
ItemListWidget.selectableLineTitleTextField(
label: '分析时间',
isEditable: true,
text: _selectData,
onTap: _chooseDatePicker,
),
const Divider(),
ItemListWidget.singleLineTitleText(
label: '代表性气体:',
hintText: '请输入代表性气体',
isEditable: true,
controller: _gasController,
),
const Divider(),
ItemListWidget.singleLineTitleText(
label: '分析结果:',
isEditable: true,
controller: _resultController,
),
const Divider(),
ItemListWidget.singleLineTitleText(
label: '分析地点:',
isEditable: true,
controller: _addressController,
),
const Divider(),
ItemListWidget.singleLineTitleText(
label: '分析人:',
isEditable: false,
text: SessionService.instance.username,
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
CustomButton(
text: '新增手写签字',
height: 40,
backgroundColor: Colors.blue,
onPressed: _sign,
),
],
),
if (imagePaths.isNotEmpty) _signListWidget(),
],
),
),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: CustomButton(
text: '作废',
backgroundColor: Colors.red,
onPressed: () => _submit(-1),
),
),
const SizedBox(width: 20),
Expanded(
child: CustomButton(
text: '保存',
backgroundColor: Colors.blue,
onPressed: () => _submit(1),
),
),
],
),
],
),
),
);
}
}

View File

@ -0,0 +1,744 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/department_person_picker.dart';
import 'package:qhd_prevention/customWidget/department_picker.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/dh_work_detai/hotwork_gas_list.dart';
import 'package:qhd_prevention/tools/tools.dart';
import '../../../../../../customWidget/bottom_picker.dart';
import '../../../../../../http/ApiService.dart';
import '../../../../../my_appbar.dart';
import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart';
enum EditUserType {
ANALYZE('分析单位', '分析单位负责人'),
GUARDIAN('监护人单位', '监护人'),
CONFESS('安全交底人单位', '安全交底人'),
ACCEPT_CONFESS('接受交底人单位', '接受交底人'),
CONFIRM('作业负责人单位', '作业负责人'),
LEADER('所在单位', '所在单位负责人'),
AUDIT('安全管理部门', '安全管理部门负责人'),
APPROVE('动火审批单位', '动火审批负责人'),
MONITOR('动火前在岗部门', '动火前在岗班长'),
WORK_START('作业开始负责人单位', '作业开始负责人'),
WORK_END('作业结束负责人单位', '作业结束负责人'),
ACCEPT('验收部门', '验收部门负责人');
///
final String displayName;
final String personName;
const EditUserType(this.displayName, this.personName);
}
class GasWorkApplyDetail extends StatefulWidget {
const GasWorkApplyDetail({
super.key,
required this.HOTWORK_ID,
required this.flow,
});
final String HOTWORK_ID;
final String flow;
@override
State<GasWorkApplyDetail> createState() => _GasWorkApplyDetailState();
}
class _GasWorkApplyDetailState extends State<GasWorkApplyDetail> {
late bool isEditable = false;
final levelList = ["特级", "一级", "二级"];
///
late String msg = 'add';
///
late Map<String, dynamic> pd = {};
late Map<String, dynamic> signs = {};
late List<Map<String, dynamic>> measuresList = [];
final TextEditingController _contentController = TextEditingController();
final TextEditingController _locationController = TextEditingController();
final TextEditingController _methodController = TextEditingController();
final TextEditingController _hotworkPersonController =
TextEditingController();
final TextEditingController _relatedController = TextEditingController();
final TextEditingController _riskController = TextEditingController();
///
late List<dynamic> workUserList = [];
//
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
@override
void initState() {
super.initState();
if (widget.HOTWORK_ID.length > 0) {
msg = 'edit';
_getData();
_getHotWorkNameList();
} else {
isEditable = true;
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;
}
_contentController.addListener(() {
setState(() {
pd['WORK_CONTENT'] = _contentController.text.trim();
});
});
_locationController.addListener(() {
pd['WORK_PLACE'] = _locationController.text.trim();
});
_methodController.addListener(() {
pd['WORK_FUNCTION'] = _methodController.text.trim();
});
_hotworkPersonController.addListener(() {
pd['WORK_USER'] = _hotworkPersonController.text.trim();
});
_relatedController.addListener(() {
pd['SPECIAL_WORK'] = _relatedController.text.trim();
});
_riskController.addListener(() {
pd['RISK_IDENTIFICATION'] = _riskController.text.trim();
});
}
void set_pd_DEPARTMENT_ID(EditUserType type, String id) {
pd['${type.name}_DEPARTMENT_ID'] = id;
}
void set_pd_DEPARTMENT_NAME(EditUserType type, String name) {
pd['${type.name}_DEPARTMENT_NAME'] = name;
}
void set_pd_USER_ID(EditUserType type, String id) {
pd['${type.name}_USER_ID'] = id;
}
void set_pd_USER_Name(EditUserType type, String name) {
pd['${type.name}_USER_NAME'] = name;
}
String get_pd_DEPARTMENT_ID(EditUserType type) {
return pd['${type.name}_DEPARTMENT_ID'] ?? '';
}
String get_pd_DEPARTMENT_NAME(EditUserType type) {
return pd['${type.name}_DEPARTMENT_NAME'] ?? '';
}
String get_pd_USER_ID(EditUserType type) {
return pd['${type.name}_USER_ID'] ?? '';
}
String get_pd_USER_Name(EditUserType type) {
return pd['${type.name}_USER_NAME'] ?? '';
}
Future<void> _chooseLevel() async {
final choice = await BottomPicker.show<String>(
context,
items: levelList,
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
initialIndex: 0,
);
if (choice != null) {
// choice
setState(() {
pd['WORK_LEVEL'] = choice;
});
}
}
Future<void> _chooseHorkUser() async{
//
final choice = await BottomPicker.show<String>(
context,
items: workUserList.map((item) => item['NAME'] as String).toList(),
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
initialIndex: 0,
);
if (choice != null) {
pd['WORK_USER'] = choice;
_hotworkPersonController.text = choice;
Map<String, dynamic> result = workUserList.firstWhere(
(item) => item['NAME'] == choice,
orElse: () => {}, //
);
if (FormUtils.hasValue(result, 'USER_ID')) {
pd['WORK_USER_ID'] = result['USER_ID'];
}
}
}
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;
});
},
),
);
// 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;
});
},
),
);
},
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(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: child,
);
}
Widget _chooseItem(EditUserType type) {
return Column(
children: [
ItemListWidget.selectableLineTitleTextField(
label: type.displayName,
isEditable: isEditable,
text: pd['${type.name}_DEPARTMENT_NAME'] ?? '请选择',
onTap: () => chooseUnitHandle(type),
),
Divider(),
ItemListWidget.selectableLineTitleTextField(
label: type.personName,
isEditable: isEditable,
text: pd['${type.name}_USER_NAME'] ?? '请选择',
onTap: () => choosePersonHandle(type),
),
],
);
}
///
void chooseUnitHandle(EditUserType type) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
barrierColor: Colors.black54,
backgroundColor: Colors.transparent,
builder:
(_) => DepartmentPicker(
onSelected: (id, name) async {
setState(() {
set_pd_DEPARTMENT_ID(type, id);
set_pd_DEPARTMENT_NAME(type, name);
set_pd_USER_ID(type, '');
set_pd_USER_Name(type, '');
});
//
final result = await ApiService.getListTreePersonList(id);
_personCache[type] = List<Map<String, dynamic>>.from(
result['userList'] as List,
);
},
),
);
}
///
void choosePersonHandle(EditUserType type) {
final unitId = get_pd_DEPARTMENT_ID(type);
final personList = _personCache[type] ?? [];
if (unitId == null || personList.isEmpty) {
final unitName = type.displayName;
ToastUtil.showNormal(context, '请先选择$unitName');
return;
}
DepartmentPersonPicker.show(
context,
personsData: personList,
onSelected: (userId, name) {
setState(() {
set_pd_USER_ID(type, userId);
set_pd_USER_Name(type, name);
});
},
);
}
/// 1 0
Future<void> _submit(String status) async {
//
final textRules = <Map<String, dynamic>>[
{'value': _contentController.text.trim(), 'message': '请填写作业内容'},
{'value': _locationController.text.trim(), 'message': '请填写动火地点及部位'},
{'value': _methodController.text.trim(), 'message': '请填写动火方式'},
{'value': _hotworkPersonController.text.trim(), 'message': '请填写动火人及证书编号'},
{
'value': _relatedController.text.trim(),
'message': '请输入关联的其他特殊作业及安全作业票编号',
},
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
];
final level = pd['WORK_LEVEL'] as String? ?? '';
///
final unitRules = <EditUserType>[
EditUserType.ANALYZE,
EditUserType.GUARDIAN,
EditUserType.CONFESS,
EditUserType.ACCEPT_CONFESS,
EditUserType.CONFIRM,
EditUserType.LEADER,
EditUserType.AUDIT,
EditUserType.APPROVE,
EditUserType.MONITOR,
EditUserType.WORK_START,
EditUserType.WORK_END,
EditUserType.ACCEPT,
];
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 (var type in unitRules) {
if (get_pd_DEPARTMENT_ID(type).length == 0) {
ToastUtil.showNormal(context, '请选择${type.displayName}');
return;
}
if (get_pd_USER_ID(type).length == 0) {
ToastUtil.showNormal(context, '请选择${type.displayName}负责人');
return;
}
}
}
// LoadingDialogHelper.show(context);
String TASK_ID = '0';
if (level == '特级') {
TASK_ID = '1';
} else if (level == '一级') {
TASK_ID = '2';
} else if (level == '二级') {
TASK_ID = '3';
}
//
Map<String, dynamic> payload = {};
if (msg == 'add') {
payload = {
'HOTWORK_ID': widget.HOTWORK_ID,
'CREATOR': SessionService.instance.loginUserId,
'OPERATOR': SessionService.instance.loginUserId,
'ACTION_USER': SessionService.instance.username,
'APPLY_STATUS': status,
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
'STEP_ID': status,
'WORK_LEVEL': TASK_ID,
};
}
pd.forEach((key, value) {
payload[key] = value;
});
LoadingDialogHelper.show(context);
try {
String url = "/app/hotwork/" + msg;
final result = await ApiService.submitHotwork(url, payload);
if (result['result'] == 'success') {
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
}
} catch (e) {
ToastUtil.showNormal(context, '操作失败:$e');
}
LoadingDialogHelper.hide(context);
}
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'];
if (pd['STEP_ID'] == 0) {
isEditable = true;
} else {
_getSigns(pd['HOTWORK_ID'] ?? '');
_getMeasures(pd['HOTWORK_ID'] ?? '');
}
//
_contentController.text = pd['WORK_CONTENT'] ?? '';
_locationController.text = pd['WORK_PLACE'] ?? '';
_methodController.text = pd['WORK_FUNCTION'] ?? '';
_hotworkPersonController.text = pd['WORK_USER'] ?? '';
_relatedController.text = pd['SPECIAL_WORK'] ?? '';
_riskController.text = pd['RISK_IDENTIFICATION'] ?? '';
});
// final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID);
// setState(() {
// pd = data['pd'];
// });
// LoadingDialogHelper.hide(context);
}
Future<void> _getSigns(String homework_id) async {
final data = await ApiService.listSignFinished(
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
);
setState(() {
signs = data['signs'] ?? {};
});
}
Future<void> _getMeasures(String homework_id) async {
final data = await ApiService.listSignFinishMeasures(
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
);
setState(() {
measuresList = List<Map<String, dynamic>>.from(
data['finishMeasuresList'] ?? <Map<String, dynamic>>[],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '动火安全作业申请'),
body: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.all(12),
child: Column(
children: [
_card(_defaultDetail()),
if (isEditable)
Column(
children: [
SizedBox(height: 15),
_card(_chooseItem(EditUserType.ANALYZE)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.GUARDIAN)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.CONFESS)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.ACCEPT_CONFESS)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.CONFIRM)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.LEADER)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.AUDIT)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.APPROVE)),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.MONITOR)),
SizedBox(height: 15),
_card(
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_chooseItem(EditUserType.WORK_START),
Divider(),
Row(
children: [
SizedBox(width: 12),
Text(
'友情提示:负责填写作业实际开始时间',
style: TextStyle(color: Colors.red),
),
],
),
SizedBox(height: 5),
],
),
),
SizedBox(height: 15),
_card(
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_chooseItem(EditUserType.WORK_END),
Divider(),
Row(
children: [
SizedBox(width: 12),
Text(
'友情提示:负责填写作业实际结束时间',
style: TextStyle(color: Colors.red),
),
],
),
SizedBox(height: 5),
],
),
),
SizedBox(height: 15),
_card(_chooseItem(EditUserType.ACCEPT)),
SizedBox(height: 15),
],
),
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,
),
),
],
),
if (FormUtils.hasValue(signs, 'MEASURES_CONFIRM'))
Column(
children: [
SizedBox(height: 20),
ListItemFactory.createBuildSimpleSection('其他安全防护措施'),
Container(
color: Colors.white,
child: OtherMeasuresWidget(
otherMeasures: signs['MEASURES_CONFIRM'],
baseImgPath: ApiService.baseImgPath,
),
),
],
),
SignaturesListWidget(
signs: signs,
pd: pd,
baseImgPath: ApiService.baseImgPath,
),
isEditable
? Row(
spacing: 10,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: CustomButton(
height: 45,
textStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
text: '提交',
backgroundColor: Colors.blue,
onPressed: () {
_submit('1');
},
),
),
Expanded(
child: CustomButton(
textStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
text: '暂存',
backgroundColor: Colors.green,
onPressed: () {
_submit('0');
},
),
),
],
)
: Column(
children: [
SizedBox(height: 20),
Row(
children: [
SizedBox(width: 50),
Expanded(
child: CustomButton(
height: 45,
textStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
text: '返回',
backgroundColor: Colors.green,
onPressed: () {
Navigator.pop(context);
},
),
),
SizedBox(width: 50),
],
),
],
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,186 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import '../../../../../../customWidget/single_image_viewer.dart';
import '../../../../../../tools/tools.dart';
///
class HotworkGasList extends StatefulWidget {
const HotworkGasList({
super.key,
required this.HOTWORK_ID,
this.addFlag = false,
});
final String HOTWORK_ID;
final bool addFlag;
@override
State<HotworkGasList> createState() => _HotworkGasListState();
}
class _HotworkGasListState extends State<HotworkGasList> {
List list = [];
bool isLoading = true;
Future<void> _getListData() async {
final data = await ApiService.hotworkGasList(widget.HOTWORK_ID);
setState(() {
list = data['varList'] ?? [];
isLoading = false;
});
}
void _deleteItem(String id) async {
showDialog<void>(
context: context,
builder:
(_) => CustomAlertDialog(
title: '温馨提示',
content: '确定要删除这条记录?',
cancelText: '取消',
onCancel: () {},
onConfirm: () async {
final result = await ApiService.hotworkGasDelete(id);
if (result['result']) {
ToastUtil.showNormal(context, '删除成功');
_getListData();
}
},
),
);
}
Widget _buildListItem(Map item) {
final images = (item['SIGN_PATH'] as String?)?.split(',') ?? [];
final baseImgPath = ApiService.baseImgPath;
return Container(
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('分析时间: ${item['ANALYZE_TIME'] ?? ''}'),
Text('代表性气体: ${item['ANALYZE_GAS'] ?? ''}'),
],
),
const SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('分析数据: ${item['ANALYZE_RESULT'] ?? ''}'),
if ((item['ANALYZE_PLACE'] ?? '').toString().isNotEmpty)
Text('分析地点: ${item['ANALYZE_PLACE']}'),
],
),
const SizedBox(height: 6),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('分析人:'),
const SizedBox(width: 8),
Expanded(
child: Wrap(
spacing: 8,
runSpacing: 4,
children: List.generate(images.length, (i) {
final img = baseImgPath + images[i];
return GestureDetector(
onTap: () {
present(
SingleImageViewer(imageUrl: img),
context,
);
},
child: Image.network(
img,
width: 50,
height: 30,
fit: BoxFit.cover,
),
);
}),
),
),
if (widget.addFlag)
TextButton(
onPressed: () =>
_deleteItem(item['HOTWORKGAS_ID'].toString()),
style: TextButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(horizontal: 12),
minimumSize: const Size(0, 32),
),
child: const Text(
'删除',
style: TextStyle(color: Colors.white),
),
),
],
)
],
),
),
);
}
Widget _buildListContent() {
if (isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (list.isEmpty) {
return NoDataWidget.show();
}
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) {
final item = list[index];
return _buildListItem(item);
},
);
}
@override
void initState() {
super.initState();
_getListData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(
title: '气体分析详情',
actions: [
if (widget.addFlag)
TextButton(
onPressed: () {
Navigator.pushNamed(
context,
'/hotworkGasDetail',
arguments: {'HOTWORK_ID': widget.HOTWORK_ID},
).then((_) => _getListData());
},
child: const Text('添加', style: TextStyle(color: Colors.white)),
)
],
),
body: _buildListContent(),
);
}
}

View File

@ -1,22 +1,24 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_detai/hotwork_apply_detail.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/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
import '../../../../../customWidget/bottom_picker.dart';
import '../../../../../customWidget/custom_button.dart';
import '../../../../../customWidget/search_bar_widget.dart';
import '../../../../../http/ApiService.dart';
import '../special_Wrok/dh_work_detai/hotwork_gas_list.dart';
class DhWorkListPage extends StatefulWidget {
class SpecialWorkListPage extends StatefulWidget {
final String flow;
const DhWorkListPage({Key? key, required this.flow}) : super(key: key);
const SpecialWorkListPage({Key? key, required this.flow}) : super(key: key);
@override
_DhWorkListPageState createState() => _DhWorkListPageState();
_SpecialWorkListPageState createState() => _SpecialWorkListPageState();
}
class _DhWorkListPageState extends State<DhWorkListPage> {
class _SpecialWorkListPageState extends State<SpecialWorkListPage> {
// Data and state variables
List<dynamic> list = [];
int currentPage = 1;
@ -118,15 +120,14 @@ class _DhWorkListPageState extends State<DhWorkListPage> {
//
pushPage(HotworkApplyDetail(HOTWORK_ID: '', flow: widget.flow), context);
}
///
Future<void> _openFlowDrawer(String hotworkId) async {
try {
final response = await ApiService.dhGetFlowList(hotworkId);
final List<dynamic>? newFlow = response['flowList'];
if (newFlow == null || newFlow.isEmpty) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('暂无流程图数据')));
ToastUtil.showNormal(context, '暂无流程图数据');
return;
}
@ -179,15 +180,16 @@ class _DhWorkListPageState extends State<DhWorkListPage> {
}
}
void _goToDetail(Map<String, dynamic> item) {
void _goToDetail(Map<String, dynamic> item) async {
final Map<String, dynamic> data = {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow};
String routeName = '';
switch (widget.flow) {
case '提交申请':
pushPage(HotworkApplyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
await pushPage(HotworkApplyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
_fetchData();
break;
case '气体检测':
routeName = '/gas-list';
pushPage(HotworkGasList(HOTWORK_ID: item['HOTWORK_ID'], addFlag:true), context);
break;
case '设置安全措施确认人':
routeName = '/hotwork-measures-detail';
@ -323,53 +325,50 @@ class _DhWorkListPageState extends State<DhWorkListPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("编号: ${item['CHECK_NO'] ?? ''}", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),),
Text("作业级别: ${item['WORK_LEVEL']}", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),),
Text("作业级别: ${item['WORK_LEVEL'] ?? ''}", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("申请人: ${item['APPLY_USER_NAME']}"),
Text("分析人: ${item['ANALYZE_USER_NAME']}"),
Text("申请人: ${item['APPLY_USER_NAME'] ?? ''}"),
Text("分析人: ${item['ANALYZE_USER_NAME'] ?? ''}"),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("监护人: ${item['GUARDIAN_USER_NAME']}"),
Text("批准人: ${item['APPROVE_USER_NAME']}"),
Text("监护人: ${item['GUARDIAN_USER_NAME'] ?? ''}"),
Text("批准人: ${item['APPROVE_USER_NAME'] ?? ''}"),
],
),
const SizedBox(height: 8),
if (item['CONFESS_USER_NAME'] != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("安全交底人: ${item['CONFESS_USER_NAME']}"),
Text("接受交底人: ${item['ACCEPT_CONFESS_USER_NAME']}"),
Text("安全交底人: ${item['CONFESS_USER_NAME'] ?? ''}"),
Text("接受交底人: ${item['ACCEPT_CONFESS_USER_NAME'] ?? ''}"),
],
),
const SizedBox(height: 8),
if (item['CONFIRM_USER_NAME'] != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("作业负责人: ${item['CONFIRM_USER_NAME']}"),
Text("动火点负责人: ${item['LEADER_USER_NAME']}"),
Text("作业负责人: ${item['CONFIRM_USER_NAME'] ?? ''}"),
Text("动火点负责人: ${item['LEADER_USER_NAME'] ?? ''}"),
],
),
const SizedBox(height: 8),
if (item['AUDIT_USER_NAME'] != null)
Text("安全管理部门负责人: ${item['AUDIT_USER_NAME']}"),
Text("安全管理部门负责人: ${item['AUDIT_USER_NAME'] ?? ''}"),
const SizedBox(height: 8),
if (item['MONITOR_USER_NAME'] != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("动火前在岗班长: ${item['MONITOR_USER_NAME']}"),
Text("验收部门负责人: ${item['ACCEPT_USER_NAME']}"),
Text("动火前在岗班长: ${item['MONITOR_USER_NAME'] ?? ''}"),
Text("验收部门负责人: ${item['ACCEPT_USER_NAME'] ?? ''}"),
],
),
const SizedBox(height: 8),
@ -424,7 +423,7 @@ class _DhWorkListPageState extends State<DhWorkListPage> {
}
}
//
//
Future<void> _showStepPicker() async {
if (stepList.isEmpty) {
ScaffoldMessenger.of(
@ -462,14 +461,13 @@ class _DhWorkListPageState extends State<DhWorkListPage> {
}
}
//
Widget _buildListContent() {
if (isLoading && list.isEmpty) {
//
return Center(child: CircularProgressIndicator());
} else if (list.isEmpty) {
//
return Center(child: Text('暂无数据'));
return NoDataWidget.show();
} else {
//
return ListView.builder(
@ -595,7 +593,6 @@ class _DhWorkListPageState extends State<DhWorkListPage> {
flex: 2,
child: SearchBarWidget(
showResetButton: false,
hintText: "请输入关键字",
// isClickableOnly: true,
onSearch: (text) {

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_icon_grid.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -129,7 +130,7 @@ class _WorkTabDhListState extends State<WorkTabDhList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}
@override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_icon_grid.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -123,7 +124,7 @@ class _WorkTabDlListState extends State<WorkTabDlList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}
@override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_icon_grid.dart';
@ -124,7 +125,7 @@ class _WorkTabDtListState extends State<WorkTabDtList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}
@override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -136,7 +137,7 @@ class _WorkTabDzListState extends State<WorkTabDzList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}
@override

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -124,7 +124,7 @@ class _WorkTabGcListState extends State<WorkTabGcList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}
@override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -124,7 +125,7 @@ class _WorkTabLsydListState extends State<WorkTabLsydList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}
@override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -112,7 +113,7 @@ class _WorkTabMbcdListState extends State<WorkTabMbcdList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/special_work_list_page.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/special_work_list_page.dart' hide SpecialWorkListPage;
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
@ -157,7 +158,7 @@ class _WorkTabSxkjListState extends State<WorkTabSxkjList> {
default:
print("按钮 $index 被点击");
}
pushPage(DhWorkListPage(flow: title), context);
pushPage(SpecialWorkListPage(flow: title), context);
}

View File

@ -113,7 +113,7 @@ class _AiAlarmPageState extends State<AiAlarmPage>
// List
Expanded(
child: _list.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: ListView.separated(
itemCount: _notifications.length,
separatorBuilder: (_, __) => const SizedBox(),

View File

@ -167,7 +167,7 @@ class _DangerWaitListPageState extends State<DangerWaitListPage> {
),
Expanded(
child: _list.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: ListView.separated(
separatorBuilder: (_, __) => const Divider(height: 1),
itemCount: _list.length,

View File

@ -9,6 +9,7 @@ import 'package:qhd_prevention/pages/my_appbar.dart';
import '../../../customWidget/search_bar_widget.dart';
import '../../../http/ApiService.dart';
import '../../../tools/tools.dart';
import 'laws_list_picker.dart';
import 'package:url_launcher/url_launcher.dart';
@ -196,16 +197,7 @@ class _LawsRegulationsPage extends State<LawsRegulationsPage> {
),
);
} else {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.folder_open, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('暂无数据', style: TextStyle(fontSize: 18, color: Colors.grey)),
],
),
);
return NoDataWidget.show();
}
}

View File

@ -80,7 +80,7 @@ class _MineDepartureRecordPage extends State<MineDepartureRecordPage> {
Expanded(
child:
_list.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: _list.length,
@ -146,7 +146,7 @@ class _MineDepartureRecordPage extends State<MineDepartureRecordPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"申请人:${item['USER_NAME']}",
"申请人:${item['USER_NAME'] ?? ''}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
@ -154,7 +154,7 @@ class _MineDepartureRecordPage extends State<MineDepartureRecordPage> {
),
const SizedBox(height: 4),
Text(
"部门:${item['DEPARTMENTNAME']} \n岗位:${item['POSTNAME']}",
"部门:${item['DEPARTMENTNAME'] ?? ''} \n岗位:${item['POSTNAME'] ?? ''}",
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],

View File

@ -155,7 +155,7 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
Expanded(
child:
_list.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: _list.length,

View File

@ -63,36 +63,35 @@ class _SignatureConfirmPageState extends State<SignatureConfirmPage> {
}
//
void _saveSignPic() async{
void _saveSignPic() async {
RenderRepaintBoundary boundary = _signatureKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
var image = await boundary.toImage(pixelRatio: 1);
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
int timestamp = DateTime.now().millisecondsSinceEpoch;
Directory dir = await getTemporaryDirectory();
String path = dir.path +"/"+ 'sign.png';
//
String path = '${dir.path}/sign_$timestamp.png';
File file2 = File(path);
//
if (await file2.exists()) {
//
await file2.delete();
}
var file = await File(path).create(recursive: true);
if(byteData != null){
file.writeAsBytesSync(byteData.buffer.asInt8List(),flush: true);
if(byteData != null) {
file.writeAsBytesSync(byteData.buffer.asInt8List(), flush: true);
setState(() {
_postBytes = byteData.buffer.asUint8List();
fileN = file;
imagepath = file.path;
Future.delayed(const Duration(milliseconds: 500), () {
Navigator.pop(context,imagepath);
Navigator.pop(context, imagepath);
});
});
}
}

View File

@ -173,7 +173,7 @@ class _NotifPageState extends State<NotifPage>
Expanded(
child:
_list.isEmpty
? Center(child: Text('暂无数据'))
? NoDataWidget.show()
: ListView.builder(
itemCount: _list.length,
itemBuilder: (context, index) {

View File

@ -24,9 +24,16 @@ void present(Widget page, BuildContext context) {
MaterialPageRoute(fullscreenDialog: true, builder: (context) => page),
);
}
class FocusHelper {
static final FocusNode _emptyNode = FocusNode();
///
static void clearFocus(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
FocusScope.of(context).requestFocus(_emptyNode);
});
}
}
///
///
/// Text Widget
class HhTextStyleUtils {
/// Text
@ -302,3 +309,41 @@ String secondsCount(dynamic seconds) {
return '$hh:$mm:$ss';
}
///
class FormUtils {
/// [data] [key]
/// - key null -> false
/// - String -> true
/// - Iterable / Map -> true
/// - intdoublebool null -> true
static bool hasValue(Map<String, dynamic> data, String key) {
if (!data.containsKey(key)) return false;
final val = data[key];
if (val == null) return false;
if (val is String) {
return val.trim().isNotEmpty;
}
if (val is Iterable || val is Map) {
return val.isNotEmpty;
}
//
return true;
}
}
class NoDataWidget {
static Widget show() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/null.png', width: 200,),
Text('暂无数据', style: TextStyle(color: Colors.grey)),
],
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,8 @@ dependencies:
geolocator: ^10.0.0
#打开外部预览app
url_launcher: ^6.0.9
#虚线
dotted_line: ^3.2.3
dev_dependencies:
flutter_test: