diff --git a/lib/customWidget/ItemWidgetFactory.dart b/lib/customWidget/ItemWidgetFactory.dart index a715af3..110e556 100644 --- a/lib/customWidget/ItemWidgetFactory.dart +++ b/lib/customWidget/ItemWidgetFactory.dart @@ -339,6 +339,7 @@ class ListItemFactory { const SizedBox(height: 8), Expanded( child: TextField( + autofocus: false, controller: controller, keyboardType: TextInputType.multiline, maxLines: null, diff --git a/lib/customWidget/bottom_picker.dart b/lib/customWidget/bottom_picker.dart index 11841e0..6b4dd19 100644 --- a/lib/customWidget/bottom_picker.dart +++ b/lib/customWidget/bottom_picker.dart @@ -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( 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('确定'), ), ], diff --git a/lib/customWidget/custom_alert_dialog.dart b/lib/customWidget/custom_alert_dialog.dart index 6da2b87..6cc83b8 100644 --- a/lib/customWidget/custom_alert_dialog.dart +++ b/lib/customWidget/custom_alert_dialog.dart @@ -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? 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 { + 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, ), diff --git a/lib/customWidget/custom_button.dart b/lib/customWidget/custom_button.dart index 8fe6aa6..dc85dea 100644 --- a/lib/customWidget/custom_button.dart +++ b/lib/customWidget/custom_button.dart @@ -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, ), ), diff --git a/lib/customWidget/date_picker_dialog.dart b/lib/customWidget/date_picker_dialog.dart index f7f25d0..057d4bb 100644 --- a/lib/customWidget/date_picker_dialog.dart +++ b/lib/customWidget/date_picker_dialog.dart @@ -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 { }, 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 { 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)),) + + ], ), ], diff --git a/lib/customWidget/department_person_picker.dart b/lib/customWidget/department_person_picker.dart index 25e7461..27b56ff 100644 --- a/lib/customWidget/department_person_picker.dart +++ b/lib/customWidget/department_person_picker.dart @@ -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; diff --git a/lib/customWidget/photo_picker_row.dart b/lib/customWidget/photo_picker_row.dart index e2d94eb..62e62d6 100644 --- a/lib/customWidget/photo_picker_row.dart +++ b/lib/customWidget/photo_picker_row.dart @@ -101,7 +101,7 @@ class _MediaPickerRowState extends State { widget.onChanged(_files); } } catch (e) { - debugPrint('拍摄失败: \$e'); + debugPrint('拍摄失败: $e'); } } @@ -140,7 +140,7 @@ class _MediaPickerRowState extends State { widget.onChanged(_files); } } catch (e) { - debugPrint('相册选择失败: \$e'); + debugPrint('相册选择失败: $e'); } } diff --git a/lib/customWidget/picker/CupertinoDatePicker.dart b/lib/customWidget/picker/CupertinoDatePicker.dart new file mode 100644 index 0000000..c7ce1dc --- /dev/null +++ b/lib/customWidget/picker/CupertinoDatePicker.dart @@ -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 show(BuildContext context) { + return showModalBottomSheet( + 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 years = List.generate(101, (i) => 1970 + i); + final List months = List.generate(12, (i) => i + 1); + final List days = List.generate(31, (i) => i + 1); + final List hours = List.generate(24, (i) => i); + final List 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 items, + required ValueChanged onSelected, + }) { + return Expanded( + child: CupertinoPicker.builder( + scrollController: controller, + itemExtent: 32, + childCount: items.length, + onSelectedItemChanged: onSelected, + itemBuilder: (context, index) { + return Center(child: Text(items[index])); + }, + ), + ); + } +} diff --git a/lib/customWidget/remote_file_page.dart b/lib/customWidget/remote_file_page.dart index 9604fa6..1446ad5 100644 --- a/lib/customWidget/remote_file_page.dart +++ b/lib/customWidget/remote_file_page.dart @@ -70,7 +70,7 @@ class _RemoteFilePageState extends State { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('文件加载失败: \$e')), + SnackBar(content: Text('文件加载失败: $e')), ); } } diff --git a/lib/customWidget/single_image_viewer.dart b/lib/customWidget/single_image_viewer.dart index c13dfa1..d123767 100644 --- a/lib/customWidget/single_image_viewer.dart +++ b/lib/customWidget/single_image_viewer.dart @@ -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) { diff --git a/lib/http/ApiService.dart b/lib/http/ApiService.dart index 3151350..bf7c5fe 100644 --- a/lib/http/ApiService.dart +++ b/lib/http/ApiService.dart @@ -607,6 +607,7 @@ U6Hzm1ninpWeE+awIDAQAB }, ); } + /// 作业流程图 static Future> dhGetFlowList(String hotworkId) { return HttpManager().request( basePath, @@ -617,6 +618,138 @@ U6Hzm1ninpWeE+awIDAQAB }, ); } + /// 作业详情 + static Future> 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> submitHotwork(String url, Map data) { + return HttpManager().request( + basePath, + url, + method: Method.post, + data: { + ...data + }, + ); + } + /// 气体分析详情列表 + static Future> 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> 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> listSignFinished(String hotworkId) { + return HttpManager().request( + basePath, + '/app/hotwork/listSignFinished', + method: Method.post, + data: { + "HOTWORK_ID":hotworkId, + }, + ); + } + /// 安全防护措施 + static Future> listSignFinishMeasures(String hotworkId) { + return HttpManager().request( + basePath, + '/app/hotwork/listSignFinishMeasures', + method: Method.post, + data: { + "HOTWORK_ID":hotworkId, + }, + ); + } + /// 关联的特殊作业列表 + static Future> getEightWorkStartList(Map data) { + return HttpManager().request( + basePath, + '/app/eightwork/startingList', + method: Method.post, + data: { + ...data + }, + ); + } + /// 风险辨识结果 风险列表 + static Future> getEightWorkInfo(Map data) { + return HttpManager().request( + basePath, + '/app/eightwork/getInfo', + method: Method.post, + data: { + ...data + }, + ); + } + /// 动火人及证书编号 + static Future> 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> saveGasTest( + Map formData, + List filePaths, + ) async { + // 复制一份 formData + final data = Map.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, + ); + } diff --git a/lib/main.dart b/lib/main.dart index 88182c2..ff26a02 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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(), - //---------动火列表详情 // ... 其他路由 }, diff --git a/lib/customWidget/hidden_roll_widget.dart b/lib/pages/home/hidden_roll_widget.dart similarity index 98% rename from lib/customWidget/hidden_roll_widget.dart rename to lib/pages/home/hidden_roll_widget.dart index 0d7f4b8..6fcc752 100644 --- a/lib/customWidget/hidden_roll_widget.dart +++ b/lib/pages/home/hidden_roll_widget.dart @@ -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 { /// 隐患列表数据 diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 161284c..4fdb7fd 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -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 { 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, diff --git a/lib/pages/home/risk/riskControl_page.dart b/lib/pages/home/risk/riskControl_page.dart index 5514c81..5a6d2c6 100644 --- a/lib/pages/home/risk/riskControl_page.dart +++ b/lib/pages/home/risk/riskControl_page.dart @@ -128,7 +128,7 @@ class _RiskControlPageState extends State { ), Expanded( child: _list.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : ListView.separated( separatorBuilder: (_, __) => const Divider(height: 1), itemCount: _list.length, diff --git a/lib/pages/home/study/study_class_list_page.dart b/lib/pages/home/study/study_class_list_page.dart index 6b52fe5..d45d0a7 100644 --- a/lib/pages/home/study/study_class_list_page.dart +++ b/lib/pages/home/study/study_class_list_page.dart @@ -49,16 +49,7 @@ class _StudyClassListPageState extends State { 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]); diff --git a/lib/pages/home/study/study_my_task_page.dart b/lib/pages/home/study/study_my_task_page.dart index 42bab65..8da411e 100644 --- a/lib/pages/home/study/study_my_task_page.dart +++ b/lib/pages/home/study/study_my_task_page.dart @@ -383,16 +383,7 @@ class _StudyMyTaskPageState extends State { 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]), diff --git a/lib/pages/home/study/study_practise_page.dart b/lib/pages/home/study/study_practise_page.dart index e6412f3..1077580 100644 --- a/lib/pages/home/study/study_practise_page.dart +++ b/lib/pages/home/study/study_practise_page.dart @@ -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 { loading ? Center(child: CircularProgressIndicator()) : options.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : Padding( padding: EdgeInsets.all(16), child: Column( diff --git a/lib/pages/home/study/study_score_page.dart b/lib/pages/home/study/study_score_page.dart index e5e5434..61264c5 100644 --- a/lib/pages/home/study/study_score_page.dart +++ b/lib/pages/home/study/study_score_page.dart @@ -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 { Expanded( child: list.isEmpty - ? Center( - child: Text('暂无数据', style: TextStyle(color: Colors.grey)), - ) + ? NoDataWidget.show() : ListView.builder( controller: _scrollController, itemCount: list.length + 1, diff --git a/lib/pages/home/study/video_study_detail_page.dart b/lib/pages/home/study/video_study_detail_page.dart index a6f2102..2d115cd 100644 --- a/lib/pages/home/study/video_study_detail_page.dart +++ b/lib/pages/home/study/video_study_detail_page.dart @@ -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 { body: loading ? Center(child: CircularProgressIndicator()) : questions.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : Padding( padding: EdgeInsets.all(16), child: Column( diff --git a/lib/pages/home/tap/item_list_widget.dart b/lib/pages/home/tap/item_list_widget.dart index 882b809..01c9fb9 100644 --- a/lib/pages/home/tap/item_list_widget.dart +++ b/lib/pages/home/tap/item_list_widget.dart @@ -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, + ), + ], + ) + ); + } } diff --git a/lib/pages/home/tap/tabList/dh_Wrok/dh_work_detai/hotwork_apply_detail.dart b/lib/pages/home/tap/tabList/dh_Wrok/dh_work_detai/hotwork_apply_detail.dart deleted file mode 100644 index bfb9090..0000000 --- a/lib/pages/home/tap/tabList/dh_Wrok/dh_work_detai/hotwork_apply_detail.dart +++ /dev/null @@ -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 createState() => _HotworkApplyDetailState(); -} - -class _HotworkApplyDetailState extends State { - final bool isEditable = true; - /// 编辑还是新增 - late String msg = 'add'; - - // 保存不同环节的单位和负责人 - final Map _selectedUnitId = {}; - final Map _selectedUnitName = {}; - final Map _selectedPersonId = {}; - final Map _selectedPersonName = {}; - - // 存储各单位的人员列表 - final Map>> _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>.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 _submit(String STATUS) async { - // '1'提交 ‘0’暂存 - - - } - /// 初始化拉取数据 - Future _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(); - - } - } -} diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart new file mode 100644 index 0000000..84cfd17 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart @@ -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> 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? ?? '', + ), + + // 问题1~4 + 答案 + 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 _buildImageRows(BuildContext context, List 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? 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) + .map((e) => e as Map) + .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 item) { + final dynamic raw = item['SIGN_PATH']; + List paths = []; + if (raw is String && raw.isNotEmpty) { + paths = [raw]; + } else if (raw is List) { + paths = raw.cast(); + } + 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? signs; + final Map? 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>(); + final first = list.first; + final name = userName is String ? userName : ''; + + // 签字图片和时间 + final signPaths = []; + final signTimes = []; + 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()); + } + 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()); + } + + 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().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>().first; + final descr = first['DESCR'] is String ? first['DESCR'] as String : ''; + final name = pd?['${type.name}_USER_NAME']; + final personDes = type.personName; + + + final signPaths = []; + final signTimes = []; + 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()); + } + 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()); + } + + 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 { + late List> workList; + late String selectedWorkType; + late String selectedWorkName; + DateTime? selectedDate; + List selectValue = []; + List> 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 _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 _getData() async { + // 构造参数 + Map 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>(); + 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>(); + }); + } + + } 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( + 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(),) + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hot_safe_work_choose_page.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hot_safe_work_choose_page.dart new file mode 100644 index 0000000..b66f8cc --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hot_safe_work_choose_page.dart @@ -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 createState() => _HotSafeWorkChoosePageState(); +} + +class _HotSafeWorkChoosePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: 'dong'), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart new file mode 100644 index 0000000..0024482 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart @@ -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 createState() => _HotworkApplyDetailState(); +} + +class _HotworkApplyDetailState extends State { + late bool isEditable = false; + final levelList = ["特级", "一级", "二级"]; + + /// 编辑还是新增 + late String msg = 'add'; + + /// 详情 + late Map pd = {}; + late Map signs = {}; + late List> 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 workUserList = []; + + // 存储各单位的人员列表 + final Map>> _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 _chooseLevel() async { + final choice = await BottomPicker.show( + 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 _chooseHorkUser() async{ + + final choice = await BottomPicker.show( + 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 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 _getPersonListForUnitId(String id, EditUserType type) async { + // 拉取该单位的人员列表并缓存 + final result = await ApiService.getListTreePersonList(id); + setState(() { + _personCache[type] = List>.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 _submit(String status) async { + // 通用文本字段校验规则 + final textRules = >[ + {'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.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 _getHotWorkNameList() async { + final result = await ApiService.getHotWorkNameList(); + setState(() { + workUserList = result['varList'] ?? ''; + List names = workUserList.map((item) => item['NAME'] as String).toList(); + + }); + } + /// 初始化拉取数据 + Future _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 _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + @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> + 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), + ], + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_gas_list.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_gas_list.dart new file mode 100644 index 0000000..8fa7a15 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_gas_list.dart @@ -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 createState() => _HotworkGasListState(); +} + +class _HotworkGasListState extends State { + List list = []; + bool isLoading = true; + Future _getListData() async { + final data = await ApiService.hotworkGasList(widget.HOTWORK_ID); + setState(() { + list = data['varList'] ?? []; + isLoading = false; + }); + } + + void _deleteItem(String id) async { + showDialog( + 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(), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart new file mode 100644 index 0000000..212943c --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart @@ -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 { + String _selectData = ''; + List imagePaths = []; + List signTimes = []; // 签字时间列表 + + final TextEditingController _gasController = TextEditingController(); + final TextEditingController _resultController = TextEditingController(); + final TextEditingController _addressController = TextEditingController(); + bool _loading = false; + + @override + void initState() { + super.initState(); + } + + Future _chooseDatePicker() async { + DateTime? picked = await BottomDateTimePicker.show(context); + if (picked != null) { + setState(() { + _selectData = DateFormat('yyyy-MM-dd HH:mm').format(picked); + }); + } + } + + Future _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 _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( + 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( + 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 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), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/gasWork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/gasWork_apply_detail.dart new file mode 100644 index 0000000..1d7ee92 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/gasWork_apply_detail.dart @@ -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 createState() => _GasWorkApplyDetailState(); +} + +class _GasWorkApplyDetailState extends State { + late bool isEditable = false; + final levelList = ["特级", "一级", "二级"]; + + /// 编辑还是新增 + late String msg = 'add'; + + /// 详情 + late Map pd = {}; + late Map signs = {}; + late List> 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 workUserList = []; + + // 存储各单位的人员列表 + final Map>> _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 _chooseLevel() async { + final choice = await BottomPicker.show( + context, + items: levelList, + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + // 用户点击确定并选择了 choice + setState(() { + pd['WORK_LEVEL'] = choice; + }); + } + } + + Future _chooseHorkUser() async{ + // + final choice = await BottomPicker.show( + 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 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>.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 _submit(String status) async { + // 通用文本字段校验规则 + final textRules = >[ + {'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.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 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 _getHotWorkNameList() async { + final result = await ApiService.getHotWorkNameList(); + setState(() { + workUserList = result['varList'] ?? ''; + List names = workUserList.map((item) => item['NAME'] as String).toList(); + + }); + } + /// 初始化拉取数据 + Future _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 _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + @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> + 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), + ], + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/hotwork_gas_list.dart b/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/hotwork_gas_list.dart new file mode 100644 index 0000000..3c73312 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/hotwork_gas_list.dart @@ -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 createState() => _HotworkGasListState(); +} + +class _HotworkGasListState extends State { + List list = []; + bool isLoading = true; + Future _getListData() async { + final data = await ApiService.hotworkGasList(widget.HOTWORK_ID); + setState(() { + list = data['varList'] ?? []; + isLoading = false; + }); + } + + void _deleteItem(String id) async { + showDialog( + 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(), + ); + } +} diff --git a/lib/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart similarity index 92% rename from lib/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart rename to lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart index 9a8111e..184456d 100644 --- a/lib/pages/home/tap/tabList/dh_Wrok/dh_work_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart @@ -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 { +class _SpecialWorkListPageState extends State { // Data and state variables List list = []; int currentPage = 1; @@ -118,15 +120,14 @@ class _DhWorkListPageState extends State { // 处理申请按钮点击逻辑 pushPage(HotworkApplyDetail(HOTWORK_ID: '', flow: widget.flow), context); } + /// 打开流程图 Future _openFlowDrawer(String hotworkId) async { try { final response = await ApiService.dhGetFlowList(hotworkId); final List? 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 { } } - void _goToDetail(Map item) { + void _goToDetail(Map item) async { final Map 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 { 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 { } } - // 新添加的方法:显示底部选择器 + // 显示底部选择器 Future _showStepPicker() async { if (stepList.isEmpty) { ScaffoldMessenger.of( @@ -462,14 +461,13 @@ class _DhWorkListPageState extends State { } } - // 添加这个方法 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 { flex: 2, child: SearchBarWidget( showResetButton: false, - hintText: "请输入关键字", // isClickableOnly: true, onSearch: (text) { diff --git a/lib/pages/home/tap/tabList/work_tab_dh_list.dart b/lib/pages/home/tap/tabList/work_tab_dh_list.dart index 8554bf0..3f2e881 100644 --- a/lib/pages/home/tap/tabList/work_tab_dh_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_dh_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } @override diff --git a/lib/pages/home/tap/tabList/work_tab_dl_list.dart b/lib/pages/home/tap/tabList/work_tab_dl_list.dart index 6ab6c51..a2e7aed 100644 --- a/lib/pages/home/tap/tabList/work_tab_dl_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_dl_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } @override diff --git a/lib/pages/home/tap/tabList/work_tab_dt_list.dart b/lib/pages/home/tap/tabList/work_tab_dt_list.dart index 0ae1ed6..29cd3c6 100644 --- a/lib/pages/home/tap/tabList/work_tab_dt_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_dt_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } @override diff --git a/lib/pages/home/tap/tabList/work_tab_dz_list.dart b/lib/pages/home/tap/tabList/work_tab_dz_list.dart index 7c36629..0244746 100644 --- a/lib/pages/home/tap/tabList/work_tab_dz_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_dz_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } @override diff --git a/lib/pages/home/tap/tabList/work_tab_gc_list.dart b/lib/pages/home/tap/tabList/work_tab_gc_list.dart index 2eb7712..358fb2f 100644 --- a/lib/pages/home/tap/tabList/work_tab_gc_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_gc_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } @override diff --git a/lib/pages/home/tap/tabList/work_tab_lsyd_list.dart b/lib/pages/home/tap/tabList/work_tab_lsyd_list.dart index f348f87..5250dd0 100644 --- a/lib/pages/home/tap/tabList/work_tab_lsyd_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_lsyd_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } @override diff --git a/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart b/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart index 6acae07..2859af2 100644 --- a/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } diff --git a/lib/pages/home/tap/tabList/work_tab_sxkj_list.dart b/lib/pages/home/tap/tabList/work_tab_sxkj_list.dart index 895fc56..07ec366 100644 --- a/lib/pages/home/tap/tabList/work_tab_sxkj_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_sxkj_list.dart @@ -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 { default: print("按钮 $index 被点击"); } - pushPage(DhWorkListPage(flow: title), context); + pushPage(SpecialWorkListPage(flow: title), context); } diff --git a/lib/pages/home/work/ai_alarm_page.dart b/lib/pages/home/work/ai_alarm_page.dart index 8fc7206..f77e0d5 100644 --- a/lib/pages/home/work/ai_alarm_page.dart +++ b/lib/pages/home/work/ai_alarm_page.dart @@ -113,7 +113,7 @@ class _AiAlarmPageState extends State // List Expanded( child: _list.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : ListView.separated( itemCount: _notifications.length, separatorBuilder: (_, __) => const SizedBox(), diff --git a/lib/pages/home/work/danger_wait_list_page.dart b/lib/pages/home/work/danger_wait_list_page.dart index cbdfe31..4566221 100644 --- a/lib/pages/home/work/danger_wait_list_page.dart +++ b/lib/pages/home/work/danger_wait_list_page.dart @@ -167,7 +167,7 @@ class _DangerWaitListPageState extends State { ), Expanded( child: _list.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : ListView.separated( separatorBuilder: (_, __) => const Divider(height: 1), itemCount: _list.length, diff --git a/lib/pages/home/work/laws_regulations_page.dart b/lib/pages/home/work/laws_regulations_page.dart index cd59e53..ccd0730 100644 --- a/lib/pages/home/work/laws_regulations_page.dart +++ b/lib/pages/home/work/laws_regulations_page.dart @@ -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 { ), ); } 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(); } } diff --git a/lib/pages/mine/mine_departure_record.dart b/lib/pages/mine/mine_departure_record.dart index 235fecd..e26ec44 100644 --- a/lib/pages/mine/mine_departure_record.dart +++ b/lib/pages/mine/mine_departure_record.dart @@ -80,7 +80,7 @@ class _MineDepartureRecordPage extends State { 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 { 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 { ), const SizedBox(height: 4), Text( - "部门:${item['DEPARTMENTNAME']} \n岗位:${item['POSTNAME']}", + "部门:${item['DEPARTMENTNAME'] ?? ''} \n岗位:${item['POSTNAME'] ?? ''}", style: TextStyle( fontSize: 13, color: Colors.grey[600], diff --git a/lib/pages/mine/mine_duty_management.dart b/lib/pages/mine/mine_duty_management.dart index 322b88b..bacf214 100644 --- a/lib/pages/mine/mine_duty_management.dart +++ b/lib/pages/mine/mine_duty_management.dart @@ -155,7 +155,7 @@ class _MineDutyManagementPageState extends State { Expanded( child: _list.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : ListView.builder( padding: const EdgeInsets.symmetric(vertical: 16), itemCount: _list.length, diff --git a/lib/pages/mine/mine_sign_page.dart b/lib/pages/mine/mine_sign_page.dart index 6a38e5b..e034ed2 100644 --- a/lib/pages/mine/mine_sign_page.dart +++ b/lib/pages/mine/mine_sign_page.dart @@ -63,36 +63,35 @@ class _SignatureConfirmPageState extends State { } // 保存签名 - 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); }); }); - } } diff --git a/lib/pages/notif/notif_page.dart b/lib/pages/notif/notif_page.dart index 7848030..3e8175b 100644 --- a/lib/pages/notif/notif_page.dart +++ b/lib/pages/notif/notif_page.dart @@ -173,7 +173,7 @@ class _NotifPageState extends State Expanded( child: _list.isEmpty - ? Center(child: Text('暂无数据')) + ? NoDataWidget.show() : ListView.builder( itemCount: _list.length, itemBuilder: (context, index) { diff --git a/lib/tools/tools.dart b/lib/tools/tools.dart index 831b3bd..08741cf 100644 --- a/lib/tools/tools.dart +++ b/lib/tools/tools.dart @@ -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 + /// - 其它类型(int、double、bool 等)只要不为 null 就算有值 -> true + static bool hasValue(Map 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)), + ], + ), + ); + } + +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 9ae05fd..6c4fe48 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.7.0" asn1lib: @@ -14,7 +14,7 @@ packages: description: name: asn1lib sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.6.5" async: @@ -22,7 +22,7 @@ packages: description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.13.0" boolean_selector: @@ -30,7 +30,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.2" camera: @@ -38,7 +38,7 @@ packages: description: name: camera sha256: d6ec2cbdbe2fa8f5e0d07d8c06368fe4effa985a4a5ddade9cc58a8cd849557d - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.11.2" camera_android_camerax: @@ -46,23 +46,23 @@ packages: description: name: camera_android_camerax sha256: "4b6c1bef4270c39df96402c4d62f2348c3bb2bbaefd0883b9dbd58f426306ad0" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.6.19" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: b389be4a325742a3950e50475067d95a3de2fb32ba3f31bfcc62b0b6d19907a6 - url: "https://pub.flutter-io.cn" + sha256: cabc6cbdeadca9cbcac178110c9d5e87bde5c646c3b8c9c4c2747fc500a12432 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "0.9.20+4" + version: "0.9.20+5" camera_platform_interface: dependency: transitive description: name: camera_platform_interface sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.10.0" camera_web: @@ -70,7 +70,7 @@ packages: description: name: camera_web sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.3.5" characters: @@ -78,7 +78,7 @@ packages: description: name: characters sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.4.0" clock: @@ -86,7 +86,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.2" collection: @@ -94,7 +94,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.19.1" connectivity_plus: @@ -102,7 +102,7 @@ packages: description: name: connectivity_plus sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.1.4" connectivity_plus_platform_interface: @@ -110,7 +110,7 @@ packages: description: name: connectivity_plus_platform_interface sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.1" convert: @@ -118,7 +118,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.2" cross_file: @@ -126,7 +126,7 @@ packages: description: name: cross_file sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.3.4+2" crypto: @@ -134,7 +134,7 @@ packages: description: name: crypto sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.6" csslib: @@ -142,7 +142,7 @@ packages: description: name: csslib sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.2" cupertino_icons: @@ -150,7 +150,7 @@ packages: description: name: cupertino_icons sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.8" dbus: @@ -158,7 +158,7 @@ packages: description: name: dbus sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.7.11" dio: @@ -166,7 +166,7 @@ packages: description: name: dio sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.8.0+1" dio_web_adapter: @@ -174,15 +174,23 @@ packages: description: name: dio_web_adapter sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.1" + dotted_line: + dependency: "direct main" + description: + name: dotted_line + sha256: "41e3d655939559815daa1370fc1e07673a205fa628cf40ce3af45d90029a77b6" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + source: hosted + version: "3.2.3" encrypt: dependency: "direct main" description: name: encrypt sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.0.3" extended_image: @@ -190,7 +198,7 @@ packages: description: name: extended_image sha256: f6cbb1d798f51262ed1a3d93b4f1f2aa0d76128df39af18ecb77fa740f88b2e0 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "10.0.1" extended_image_library: @@ -198,7 +206,7 @@ packages: description: name: extended_image_library sha256: "1f9a24d3a00c2633891c6a7b5cab2807999eb2d5b597e5133b63f49d113811fe" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.0.1" extension: @@ -206,7 +214,7 @@ packages: description: name: extension sha256: be3a6b7f8adad2f6e2e8c63c895d19811fcf203e23466c6296267941d0ff4f24 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.6.0" fake_async: @@ -214,7 +222,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.3" ffi: @@ -222,7 +230,7 @@ packages: description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.4" file: @@ -230,7 +238,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "7.0.1" file_selector_linux: @@ -238,7 +246,7 @@ packages: description: name: file_selector_linux sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.9.3+2" file_selector_macos: @@ -246,7 +254,7 @@ packages: description: name: file_selector_macos sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.9.4+3" file_selector_platform_interface: @@ -254,7 +262,7 @@ packages: description: name: file_selector_platform_interface sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.6.2" file_selector_windows: @@ -262,7 +270,7 @@ packages: description: name: file_selector_windows sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.9.3+4" fixnum: @@ -270,7 +278,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.1" flutter: @@ -283,7 +291,7 @@ packages: description: name: flutter_html sha256: "38a2fd702ffdf3243fb7441ab58aa1bc7e6922d95a50db76534de8260638558d" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.0" flutter_lints: @@ -291,7 +299,7 @@ packages: description: name: flutter_lints sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.0.0" flutter_plugin_android_lifecycle: @@ -299,7 +307,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.28" flutter_test: @@ -317,7 +325,7 @@ packages: description: name: fluttertoast sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "8.2.12" geolocator: @@ -325,7 +333,7 @@ packages: description: name: geolocator sha256: f4efb8d3c4cdcad2e226af9661eb1a0dd38c71a9494b22526f9da80ab79520e5 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "10.1.1" geolocator_android: @@ -333,7 +341,7 @@ packages: description: name: geolocator_android sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.6.2" geolocator_apple: @@ -341,7 +349,7 @@ packages: description: name: geolocator_apple sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.3.13" geolocator_platform_interface: @@ -349,7 +357,7 @@ packages: description: name: geolocator_platform_interface sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.2.6" geolocator_web: @@ -357,7 +365,7 @@ packages: description: name: geolocator_web sha256: "102e7da05b48ca6bf0a5bda0010f886b171d1a08059f01bfe02addd0175ebece" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.1" geolocator_windows: @@ -365,7 +373,7 @@ packages: description: name: geolocator_windows sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.2.5" html: @@ -373,7 +381,7 @@ packages: description: name: html sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.15.6" http: @@ -381,7 +389,7 @@ packages: description: name: http sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.4.0" http_client_helper: @@ -389,7 +397,7 @@ packages: description: name: http_client_helper sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.0" http_parser: @@ -397,7 +405,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.1.2" image_picker: @@ -405,7 +413,7 @@ packages: description: name: image_picker sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.2" image_picker_android: @@ -413,7 +421,7 @@ packages: description: name: image_picker_android sha256: "6fae381e6af2bbe0365a5e4ce1db3959462fa0c4d234facf070746024bb80c8d" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.8.12+24" image_picker_for_web: @@ -421,7 +429,7 @@ packages: description: name: image_picker_for_web sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.6" image_picker_ios: @@ -429,7 +437,7 @@ packages: description: name: image_picker_ios sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.8.12+2" image_picker_linux: @@ -437,7 +445,7 @@ packages: description: name: image_picker_linux sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.2.1+2" image_picker_macos: @@ -445,7 +453,7 @@ packages: description: name: image_picker_macos sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.2.1+2" image_picker_platform_interface: @@ -453,7 +461,7 @@ packages: description: name: image_picker_platform_interface sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.10.1" image_picker_windows: @@ -461,7 +469,7 @@ packages: description: name: image_picker_windows sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.2.1+1" intl: @@ -469,7 +477,7 @@ packages: description: name: intl sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.20.2" js: @@ -477,7 +485,7 @@ packages: description: name: js sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.7.2" leak_tracker: @@ -485,7 +493,7 @@ packages: description: name: leak_tracker sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "10.0.9" leak_tracker_flutter_testing: @@ -493,7 +501,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.9" leak_tracker_testing: @@ -501,7 +509,7 @@ packages: description: name: leak_tracker_testing sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.1" lints: @@ -509,7 +517,7 @@ packages: description: name: lints sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.1.1" list_counter: @@ -517,7 +525,7 @@ packages: description: name: list_counter sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.2" matcher: @@ -525,7 +533,7 @@ packages: description: name: matcher sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.12.17" material_color_utilities: @@ -533,7 +541,7 @@ packages: description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.11.1" meta: @@ -541,7 +549,7 @@ packages: description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.16.0" mime: @@ -549,7 +557,7 @@ packages: description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.0" mobile_scanner: @@ -557,23 +565,47 @@ packages: description: name: mobile_scanner sha256: "54005bdea7052d792d35b4fef0f84ec5ddc3a844b250ecd48dc192fb9b4ebc95" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "7.0.1" + ndef_record: + dependency: transitive + description: + name: ndef_record + sha256: "0c72dfac0d5c16fc264846d103ee5d8249cd3858261a5a537b455a24c1bd5857" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + source: hosted + version: "1.2.1" nested: dependency: transitive description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.0" + nfc_manager: + dependency: "direct main" + description: + name: nfc_manager + sha256: "164cc0223dee528d4d05a542da921f0b3a31ca0312400701c93ebf4ce757f676" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + source: hosted + version: "4.0.2" + nfc_manager_ndef: + dependency: "direct main" + description: + name: nfc_manager_ndef + sha256: "676e741c42b63ab1fda5a981015cb706ab4fdb76e5d0eec6611993bb27d7e7bf" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + source: hosted + version: "1.0.1" nm: dependency: transitive description: name: nm sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.5.0" package_info_plus: @@ -581,7 +613,7 @@ packages: description: name: package_info_plus sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "8.3.0" package_info_plus_platform_interface: @@ -589,7 +621,7 @@ packages: description: name: package_info_plus_platform_interface sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.0" path: @@ -597,7 +629,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.9.1" path_provider: @@ -605,7 +637,7 @@ packages: description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.5" path_provider_android: @@ -613,7 +645,7 @@ packages: description: name: path_provider_android sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.17" path_provider_foundation: @@ -621,7 +653,7 @@ packages: description: name: path_provider_foundation sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.1" path_provider_linux: @@ -629,7 +661,7 @@ packages: description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -637,7 +669,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.2" path_provider_windows: @@ -645,7 +677,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.3.0" pdfx: @@ -653,7 +685,7 @@ packages: description: name: pdfx sha256: "29db9b71d46bf2335e001f91693f2c3fbbf0760e4c2eb596bf4bafab211471c1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.9.2" petitparser: @@ -661,7 +693,7 @@ packages: description: name: petitparser sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.1.0" photo_manager: @@ -669,7 +701,7 @@ packages: description: name: photo_manager sha256: a0d9a7a9bc35eda02d33766412bde6d883a8b0acb86bbe37dac5f691a0894e8a - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.7.1" photo_manager_image_provider: @@ -677,7 +709,7 @@ packages: description: name: photo_manager_image_provider sha256: b6015b67b32f345f57cf32c126f871bced2501236c405aafaefa885f7c821e4f - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.0" photo_view: @@ -685,7 +717,7 @@ packages: description: name: photo_view sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.15.0" platform: @@ -693,7 +725,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.6" plugin_platform_interface: @@ -701,7 +733,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.8" pointycastle: @@ -709,7 +741,7 @@ packages: description: name: pointycastle sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.9.1" provider: @@ -717,7 +749,7 @@ packages: description: name: provider sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.1.5" shared_preferences: @@ -725,7 +757,7 @@ packages: description: name: shared_preferences sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.5.3" shared_preferences_android: @@ -733,7 +765,7 @@ packages: description: name: shared_preferences_android sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.10" shared_preferences_foundation: @@ -741,7 +773,7 @@ packages: description: name: shared_preferences_foundation sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.5.4" shared_preferences_linux: @@ -749,7 +781,7 @@ packages: description: name: shared_preferences_linux sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.1" shared_preferences_platform_interface: @@ -757,7 +789,7 @@ packages: description: name: shared_preferences_platform_interface sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.1" shared_preferences_web: @@ -765,7 +797,7 @@ packages: description: name: shared_preferences_web sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.3" shared_preferences_windows: @@ -773,7 +805,7 @@ packages: description: name: shared_preferences_windows sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.1" simple_gesture_detector: @@ -781,7 +813,7 @@ packages: description: name: simple_gesture_detector sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.2.1" sky_engine: @@ -794,7 +826,7 @@ packages: description: name: source_span sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.10.1" sprintf: @@ -802,7 +834,7 @@ packages: description: name: sprintf sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "7.0.0" stack_trace: @@ -810,7 +842,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.12.1" stream_channel: @@ -818,7 +850,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.4" stream_transform: @@ -826,7 +858,7 @@ packages: description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.1" string_scanner: @@ -834,7 +866,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.4.1" synchronized: @@ -842,7 +874,7 @@ packages: description: name: synchronized sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.4.0" table_calendar: @@ -850,7 +882,7 @@ packages: description: name: table_calendar sha256: "0c0c6219878b363a2d5f40c7afb159d845f253d061dc3c822aa0d5fe0f721982" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.0" term_glyph: @@ -858,7 +890,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.2" test_api: @@ -866,7 +898,7 @@ packages: description: name: test_api sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.7.4" typed_data: @@ -874,7 +906,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.4.0" universal_platform: @@ -882,7 +914,7 @@ packages: description: name: universal_platform sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.0" url_launcher: @@ -890,7 +922,7 @@ packages: description: name: url_launcher sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.3.2" url_launcher_android: @@ -898,7 +930,7 @@ packages: description: name: url_launcher_android sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.3.16" url_launcher_ios: @@ -906,7 +938,7 @@ packages: description: name: url_launcher_ios sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.3.3" url_launcher_linux: @@ -914,7 +946,7 @@ packages: description: name: url_launcher_linux sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.1" url_launcher_macos: @@ -922,7 +954,7 @@ packages: description: name: url_launcher_macos sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.2" url_launcher_platform_interface: @@ -930,7 +962,7 @@ packages: description: name: url_launcher_platform_interface sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.3.2" url_launcher_web: @@ -938,7 +970,7 @@ packages: description: name: url_launcher_web sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.1" url_launcher_windows: @@ -946,7 +978,7 @@ packages: description: name: url_launcher_windows sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.4" uuid: @@ -954,7 +986,7 @@ packages: description: name: uuid sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.5.1" vector_math: @@ -962,7 +994,7 @@ packages: description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.4" video_player: @@ -970,7 +1002,7 @@ packages: description: name: video_player sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.10.0" video_player_android: @@ -978,7 +1010,7 @@ packages: description: name: video_player_android sha256: "4a5135754a62dbc827a64a42ef1f8ed72c962e191c97e2d48744225c2b9ebb73" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.8.7" video_player_avfoundation: @@ -986,7 +1018,7 @@ packages: description: name: video_player_avfoundation sha256: "9fedd55023249f3a02738c195c906b4e530956191febf0838e37d0dac912f953" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.8.0" video_player_platform_interface: @@ -994,7 +1026,7 @@ packages: description: name: video_player_platform_interface sha256: cf2a1d29a284db648fd66cbd18aacc157f9862d77d2cc790f6f9678a46c1db5a - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.4.0" video_player_web: @@ -1002,7 +1034,7 @@ packages: description: name: video_player_web sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.0" visibility_detector: @@ -1010,7 +1042,7 @@ packages: description: name: visibility_detector sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.4.0+2" vm_service: @@ -1018,7 +1050,7 @@ packages: description: name: vm_service sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "15.0.0" web: @@ -1026,7 +1058,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.1" webview_flutter: @@ -1034,7 +1066,7 @@ packages: description: name: webview_flutter sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.13.0" webview_flutter_android: @@ -1042,7 +1074,7 @@ packages: description: name: webview_flutter_android sha256: "9573ad97890d199ac3ab32399aa33a5412163b37feb573eb5b0a76b35e9ffe41" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.8.2" webview_flutter_platform_interface: @@ -1050,7 +1082,7 @@ packages: description: name: webview_flutter_platform_interface sha256: f0dc2dc3a2b1e3a6abdd6801b9355ebfeb3b8f6cde6b9dc7c9235909c4a1f147 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.13.1" webview_flutter_wkwebview: @@ -1058,7 +1090,7 @@ packages: description: name: webview_flutter_wkwebview sha256: "71523b9048cf510cfa1fd4e0a3fa5e476a66e0884d5df51d59d5023dba237107" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.22.1" wechat_assets_picker: @@ -1066,7 +1098,7 @@ packages: description: name: wechat_assets_picker sha256: cafe3d32564ed3cacf9822f251941f7b44fe9885c17c8de4fca7e939a459e1ef - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "9.5.1" wechat_picker_library: @@ -1074,7 +1106,7 @@ packages: description: name: wechat_picker_library sha256: a42e09cb85b15fc9410f6a69671371cc60aa99c4a1f7967f6593a7f665f6f47a - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.5" win32: @@ -1082,7 +1114,7 @@ packages: description: name: win32 sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.14.0" xdg_directories: @@ -1090,7 +1122,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.0" xml: @@ -1098,7 +1130,7 @@ packages: description: name: xml sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.5.0" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index bc68435..fb99184 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,7 +73,8 @@ dependencies: geolocator: ^10.0.0 #打开外部预览app url_launcher: ^6.0.9 - + #虚线 + dotted_line: ^3.2.3 dev_dependencies: flutter_test: