hs 2025-07-30 17:08:46 +08:00
parent e7fbd5e12c
commit 3a00844a57
11 changed files with 879 additions and 406 deletions

View File

@ -1,7 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:qhd_prevention/tools/h_colors.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import 'package:photo_manager/photo_manager.dart';
@ -10,25 +9,32 @@ import 'ItemWidgetFactory.dart';
///
enum MediaType { image, video }
///
///
/// 使
/// MediaPickerRow(
/// maxCount: 4,
/// mediaType: MediaType.video,
/// onChanged: (List<File> medias) {
/// // medias
/// },
/// initialMediaPaths: ['https://...', '/local/path.png'],
/// onChanged: (List<File> medias) {},
/// onMediaAdded: (String path) {},
/// onMediaRemoved: (String path) {},
/// ),
class MediaPickerRow extends StatefulWidget {
final int maxCount;
final MediaType mediaType;
final List<String>? initialMediaPaths;
final ValueChanged<List<File>> onChanged;
final ValueChanged<String>? onMediaAdded;
final ValueChanged<String>? onMediaRemoved;
const MediaPickerRow({
Key? key,
this.maxCount = 4,
this.mediaType = MediaType.image,
this.initialMediaPaths,
required this.onChanged,
this.onMediaAdded,
this.onMediaRemoved,
}) : super(key: key);
@override
@ -37,13 +43,24 @@ class MediaPickerRow extends StatefulWidget {
class _MediaPickerRowState extends State<MediaPickerRow> {
final ImagePicker _picker = ImagePicker();
final List<File> _files = [];
late List<String> _mediaPaths;
@override
void initState() {
super.initState();
_mediaPaths = widget.initialMediaPaths != null
? widget.initialMediaPaths!.take(widget.maxCount).toList()
: [];
WidgetsBinding.instance.addPostFrameCallback((_) {
// File
widget.onChanged(_mediaPaths.map((p) => p.startsWith('http') ? File('') : File(p)).toList());
});
}
Future<void> _showPickerOptions() async {
showModalBottomSheet(
context: context,
builder:
(_) => SafeArea(
builder: (_) => SafeArea(
child: Wrap(
children: [
ListTile(
@ -67,7 +84,9 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
: Icons.video_library,
),
title: Text(
widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频',
widget.mediaType == MediaType.image
? '从相册选择'
: '从相册选择视频',
),
onTap: () {
Navigator.of(context).pop();
@ -86,7 +105,7 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
}
Future<void> _pickCamera() async {
if (_files.length >= widget.maxCount) return;
if (_mediaPaths.length >= widget.maxCount) return;
try {
XFile? picked;
if (widget.mediaType == MediaType.image) {
@ -95,10 +114,10 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
picked = await _picker.pickVideo(source: ImageSource.camera);
}
if (picked != null) {
setState(() {
_files.add(File(picked!.path));
});
widget.onChanged(_files);
final path = picked.path;
setState(() => _mediaPaths.add(path));
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
widget.onMediaAdded?.call(path);
}
} catch (e) {
debugPrint('拍摄失败: $e');
@ -106,22 +125,21 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
}
Future<void> _pickGallery() async {
if (_files.length >= widget.maxCount) return;
if (_mediaPaths.length >= widget.maxCount) return;
final permission = await PhotoManager.requestPermissionExtend();
if (permission != PermissionState.authorized &&
permission != PermissionState.limited) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('请到设置中开启相册访问权限')));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请到设置中开启相册访问权限')),
);
return;
}
try {
final remaining = widget.maxCount - _files.length;
final remaining = widget.maxCount - _mediaPaths.length;
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
requestType:
widget.mediaType == MediaType.image
requestType: widget.mediaType == MediaType.image
? RequestType.image
: RequestType.video,
maxAssets: remaining,
@ -130,25 +148,27 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
);
if (assets != null) {
for (final asset in assets) {
if (_files.length >= widget.maxCount) break;
if (_mediaPaths.length >= widget.maxCount) break;
final file = await asset.file;
if (file != null) {
_files.add(file);
final path = file.path;
_mediaPaths.add(path);
widget.onMediaAdded?.call(path);
}
}
setState(() {});
widget.onChanged(_files);
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
}
} catch (e) {
debugPrint('相册选择失败: $e');
}
}
void _removeFile(int index) {
setState(() {
_files.removeAt(index);
});
widget.onChanged(_files);
void _removeMedia(int index) {
final removed = _mediaPaths[index];
setState(() => _mediaPaths.removeAt(index));
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
widget.onMediaRemoved?.call(removed);
}
@override
@ -157,25 +177,24 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
height: 80,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount:
_files.length < widget.maxCount
? _files.length + 1
itemCount: _mediaPaths.length < widget.maxCount
? _mediaPaths.length + 1
: widget.maxCount,
separatorBuilder: (_, __) => const SizedBox(width: 8),
itemBuilder: (context, index) {
if (index < _files.length) {
if (index < _mediaPaths.length) {
final path = _mediaPaths[index];
final isNetwork = path.startsWith('http');
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child:
widget.mediaType == MediaType.image
? Image.file(
_files[index],
width: 80,
height: 80,
fit: BoxFit.cover,
)
child: widget.mediaType == MediaType.image
? (isNetwork
? Image.network(path,
width: 80, height: 80, fit: BoxFit.cover)
: Image.file(File(path),
width: 80, height: 80, fit: BoxFit.cover))
: Container(
width: 80,
height: 80,
@ -189,11 +208,11 @@ class _MediaPickerRowState extends State<MediaPickerRow> {
),
),
Positioned(
top: -6,
right: -6,
top: -15,
right: -15,
child: IconButton(
icon: const Icon(Icons.cancel, size: 20, color: Colors.red),
onPressed: () => _removeFile(index),
onPressed: () => _removeMedia(index),
),
),
],
@ -230,7 +249,10 @@ class RepairedPhotoSection extends StatelessWidget {
final int maxCount;
final MediaType mediaType;
final String title;
final List<String>? initialMediaPaths;
final ValueChanged<List<File>> onChanged;
final ValueChanged<String>? onMediaAdded;
final ValueChanged<String>? onMediaRemoved;
final VoidCallback onAiIdentify;
final bool isShowAI;
final double horizontalPadding;
@ -240,10 +262,13 @@ class RepairedPhotoSection extends StatelessWidget {
this.maxCount = 4,
this.mediaType = MediaType.image,
required this.title,
this.initialMediaPaths,
this.isShowAI = false,
required this.onChanged,
required this.onAiIdentify,
this.horizontalPadding = 10,
this.onMediaAdded,
this.onMediaRemoved,
}) : super(key: key);
@override
@ -257,7 +282,7 @@ class RepairedPhotoSection extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
child: ListItemFactory.createRowSpaceBetweenItem(
leftText: title,
rightText: '0/$maxCount',
rightText: '${initialMediaPaths?.length ?? 0}/$maxCount',
),
),
Padding(
@ -265,7 +290,10 @@ class RepairedPhotoSection extends StatelessWidget {
child: MediaPickerRow(
maxCount: maxCount,
mediaType: mediaType,
initialMediaPaths: initialMediaPaths,
onChanged: onChanged,
onMediaAdded: onMediaAdded,
onMediaRemoved: onMediaRemoved,
),
),
const SizedBox(height: 20),

View File

@ -714,7 +714,7 @@ U6Hzm1ninpWeE+awIDAQAB
data: {
"CORPINFO_ID":SessionService.instance.corpinfoId,
"HOTWORK_ID": homeWorkId,
"USER_ID":SessionService.instance.loginUserId,
"CONFIRM_ID":SessionService.instance.loginUserId,
},
);
}
@ -801,6 +801,60 @@ U6Hzm1ninpWeE+awIDAQAB
);
}
///
static Future<Map<String, dynamic>> uploadSaveFile (
String filePath,
) async {
final Map<String, dynamic> data = {
"CORPINFO_ID":SessionService.instance.corpinfoId,
};
// MultipartFile
final path = filePath;
data['file'] = await MultipartFile.fromFile(
path,
filename: path.split(Platform.pathSeparator).last,
);
return HttpManager().uploadFaceImage(
baseUrl: basePath,
path: '/app/eightwork/saveFile',
fromData: data,
);
}
///
static Future<Map<String, dynamic>> deleteSaveFile(String FILE_PATH) {
return HttpManager().request(
basePath,
'/app/eightwork/deleteFile',
method: Method.post,
data: {
"FILE_PATH": FILE_PATH,
},
);
}
///
static Future<Map<String, dynamic>> saveDangerousOptionsFile (
List<String> filePaths,
) async {
// formData
final Map<String, dynamic> data = {
"CORPINFO_ID":SessionService.instance.corpinfoId,
};
// 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/eightwork/saveDangerousOptionsFile',
fromData: data,
);
}
///TODO --------------------------------- ---------------------------------

View File

@ -87,7 +87,7 @@ class MyApp extends StatelessWidget {
),
primarySwatch: Colors.blue,
//
scaffoldBackgroundColor: const Color(0xFFF5F7FA), //
scaffoldBackgroundColor: const Color(0xFFF1F1F1), //
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
inputDecorationTheme: const InputDecorationTheme(
border: InputBorder.none,

View File

@ -1,13 +1,17 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import '../../../customWidget/single_image_viewer.dart';
import '../../../tools/tools.dart';
class ItemListWidget {
static const Color detailtextColor = Colors.black54;
///
/// - + TextField
/// - +
static const Color detailtextColor = Colors.black54;
static Widget singleLineTitleText({
required String label, //
required bool isEditable, //

View File

@ -35,7 +35,13 @@ class WorkDetailFormWidget extends StatelessWidget {
this.relatedController,
this.riskController,
}) : assert(
!isEditable || (contentController != null && locationController != null && methodController != null && hotworkPersonController != null && relatedController != null && riskController != null),
!isEditable ||
(contentController != null &&
locationController != null &&
methodController != null &&
hotworkPersonController != null &&
relatedController != null &&
riskController != null),
'Editable mode requires all TextEditingController parameters',
),
super(key: key);
@ -43,7 +49,14 @@ class WorkDetailFormWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final pd = this.pd;
return Column(
return
Container(
padding: EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ItemListWidget.singleLineTitleText(
@ -116,7 +129,8 @@ class WorkDetailFormWidget extends StatelessWidget {
onTap: () async {
final val = await showDialog<String>(
context: context,
builder: (_) => SelectionPopup(
builder:
(_) => SelectionPopup(
type: 'assignments',
initialValue: pd['SPECIAL_WORK'] ?? '',
onConfirm: (v) => Navigator.of(context).pop(v),
@ -136,7 +150,8 @@ class WorkDetailFormWidget extends StatelessWidget {
onTap: () async {
final val = await showDialog<String>(
context: context,
builder: (_) => SelectionPopup(
builder:
(_) => SelectionPopup(
type: 'identification',
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
onConfirm: (v) => Navigator.of(context).pop(v),
@ -158,6 +173,9 @@ class WorkDetailFormWidget extends StatelessWidget {
),
],
],
),
);
}
}

View File

@ -0,0 +1,264 @@
import 'dart:io';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/HttpManager.dart';
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import '../../../../../../customWidget/custom_button.dart';
import '../../../../../../customWidget/photo_picker_row.dart';
import '../../../../../../customWidget/single_image_viewer.dart';
import '../../../../../../tools/tools.dart';
import '../../../../../mine/mine_sign_page.dart';
/// + 线
class ImageData {
String localPath;
String? serverPath;
ImageData({required this.localPath, this.serverPath});
}
class DangerousOptionsPage extends StatefulWidget {
final int index;
final int status;
final String measures;
final List<ImageData> imgList;
final List<String> signImgList;
const DangerousOptionsPage({
Key? key,
required this.index,
this.status = 1,
this.measures = '',
this.imgList = const [],
this.signImgList = const [],
}) : super(key: key);
@override
_DangerousOptionsPageState createState() => _DangerousOptionsPageState();
}
class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
late int index;
late int status;
late String measures;
late List<ImageData> imgList;
late List<String> signImgList;
List<String> signTimes = [];
bool buttonLoading = false;
final Dio _dio = Dio();
final _picker = ImagePicker();
@override
void initState() {
super.initState();
index = widget.index;
status = widget.status;
measures = widget.measures;
imgList = List<ImageData>.from(widget.imgList);
signImgList = List<String>.from(widget.signImgList);
}
///
Future<void> _onImageAdded(String localPath) async {
//
final res = await ApiService.uploadSaveFile(localPath);
final url = res['FILE_PATH'] as String;
setState(() {
imgList.add(ImageData(localPath: localPath, serverPath: url));
});
}
///
Future<void> _onImageRemoved(ImageData item) async {
if (item.serverPath != null) {
await ApiService.deleteSaveFile(item.serverPath!);
}
setState(() {
imgList.remove(item);
});
}
Future<void> _submit() async {
if (signImgList.isEmpty) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('请签字')));
return;
}
await ApiService.saveDangerousOptionsFile(signImgList);
setState(() => buttonLoading = true);
Navigator.pop(context, {
'imgList': imgList.map((e) => {'local': e.localPath, 'remote': e.serverPath}).toList(),
'signImgList': signImgList,
'index': index,
'status': status,
});
}
Future<void> _sign() async {
final path = await Navigator.push(
context,
MaterialPageRoute(builder: (c) => MineSignPage()),
);
if (path != null) {
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
setState(() {
signImgList.add(path);
signTimes.add(now);
});
FocusHelper.clearFocus(context);
}
}
Widget _signListWidget() {
return Column(
children: signImgList.map((path) {
final idx = signImgList.indexOf(path);
return Column(
children: [
const SizedBox(height: 10),
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200, maxHeight: 150),
child: Image.file(File(path), fit: BoxFit.contain),
),
onTap: () => presentOpaque(SingleImageViewer(imageUrl: path), context),
),
Column(
children: [
CustomButton(
text: '删除',
height: 30,
padding: const EdgeInsets.symmetric(horizontal: 10),
backgroundColor: Colors.red,
onPressed: () {
setState(() => signImgList.removeAt(idx));
},
),
const SizedBox(height: 80),
],
),
],
),
],
);
}).toList(),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: '安全措施'),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
color: Colors.white,
child: ListView(
children: [
Table(
border: TableBorder.all(color: Colors.grey.shade300),
columnWidths: {0: FlexColumnWidth(3), 1: FlexColumnWidth(2)},
children: [
TableRow(
decoration: BoxDecoration(color: Colors.grey.shade200),
children: [
Padding(
padding: EdgeInsets.all(10),
child: Center(
child: Text('主要安全措施', style: TextStyle(fontWeight: FontWeight.bold)),
),
),
Padding(
padding: EdgeInsets.all(10),
child: Center(
child: Text('操作', style: TextStyle(fontWeight: FontWeight.bold)),
),
),
],
),
TableRow(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 5),
child: Text(measures),
),
Padding(
padding: const EdgeInsets.all(0),
child: Column(
children: [
RadioListTile<int>(
value: -1,
groupValue: status,
title: Text('不涉及'),
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 8.0),
visualDensity: VisualDensity(vertical: -4, horizontal: 0),
onChanged: (v) => setState(() => status = v!),
),
RadioListTile<int>(
value: 1,
groupValue: status,
title: Text('涉及'),
contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
visualDensity: VisualDensity(vertical: -4, horizontal: 0),
onChanged: (v) => setState(() => status = v!),
),
],
),
),
],
),
],
),
const SizedBox(height: 20),
RepairedPhotoSection(
title: '上传图片',
maxCount: 2,
mediaType: MediaType.image,
initialMediaPaths: imgList.map((e) => e.localPath).toList(),
onChanged: (paths) {},
onMediaAdded: _onImageAdded,
onMediaRemoved: (path) {
final item = imgList.firstWhere((e) => e.localPath == path);
_onImageRemoved(item);
},
onAiIdentify: () {},
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('签字:', style: TextStyle(fontSize: 16)),
CustomButton(
text: '新增手写签字',
height: 36,
backgroundColor: Colors.green,
onPressed: _sign,
),
],
),
if (signImgList.isNotEmpty) _signListWidget(),
const SizedBox(height: 30),
CustomButton(
text: '保存',
backgroundColor: Colors.blue,
onPressed: buttonLoading ? null : _submit,
),
],
),
),
),
);
}
}

View File

@ -1,7 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
@ -10,8 +9,8 @@ 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/aqcs_work_detail/dangerous_options_page.dart';
import 'package:qhd_prevention/tools/tools.dart';
import '../../../../../../customWidget/bottom_picker.dart';
import '../../../../../../customWidget/custom_alert_dialog.dart';
import '../../../../../../customWidget/single_image_viewer.dart';
import '../../../../../../http/ApiService.dart';
@ -20,6 +19,7 @@ import '../../../../../my_appbar.dart';
import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart';
import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart';
import '../WorkDetailFormWidget.dart';
///
class HotworkSafeFuncSure extends StatefulWidget {
const HotworkSafeFuncSure({
@ -45,6 +45,8 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
///
late Map<String, dynamic> pd = {};
late List<Map<String, dynamic>> measuresList = [];
///
final TextEditingController _otherController = TextEditingController();
///
late List<dynamic> workUserList = [];
@ -67,18 +69,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
return jsonEncode(jsonList);
}
Widget _card(Widget child) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: child,
);
}
///
void chooseUnitHandle(MeasureItem item) {
showModalBottomSheet(
@ -87,7 +77,8 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
barrierColor: Colors.black54,
backgroundColor: Colors.transparent,
builder:
(_) => DepartmentPicker(
(_) =>
DepartmentPicker(
onSelected: (id, name) async {
setState(() {
item.DEPARTMENT_ID = id;
@ -265,7 +256,8 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
await showDialog<String>(
context: context,
builder:
(_) => CustomAlertDialog(
(_) =>
CustomAlertDialog(
title: '作废原因',
mode: DialogMode.input,
hintText: '请输入作废原因',
@ -294,7 +286,8 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
await showDialog<String>(
context: context,
builder:
(_) => CustomAlertDialog(
(_) =>
CustomAlertDialog(
title: '提示',
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
cancelText: '取消',
@ -380,6 +373,44 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
});
}
Future<void> _itemToSign(Map<String, dynamic> measures, int index) async {
//
final List<String> signImgList = measures['SIGN_ITEM'] ?? [];
//
final List<ImageData> imgList = [];
if ((measures['IMG_PATH'] as String?)?.isNotEmpty ?? false) {
for (var path in (measures['IMG_PATH'] as String).split(',')) {
imgList.add(ImageData(localPath: path));
}
}
final result = await pushPage(DangerousOptionsPage(index: index,status: measures['STATUS'] ?? 1,
measures: measures['PROTECTIVE_MEASURES'] ?? '',
imgList: imgList,
signImgList: signImgList), context);
if (result != null) {
setState(() {
// local + remote
final returned = result['imgList'] as List<dynamic>;
measures['IMG_PATH'] = returned.map((m) {
return ImageData(
localPath: m['local'] as String,
serverPath: m['remote'] as String?,
);
}).toList();
//
measures['SIGN_ITEM'] = List<String>.from(result['signImgList'] as List);
//
measures['STATUS'] = result['status'];
index = result['index'] as int;
});
}
}
///
Widget _setSafeDetailWidget() {
return Container(
@ -390,10 +421,10 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
padding: EdgeInsets.symmetric(horizontal: 5),
child: Column(
children: [
if (measuresList.length > 0)
if (measuresList.isNotEmpty)
Column(
children: [
SizedBox(height: 20),
SizedBox(height: 5),
ListItemFactory.createBuildSimpleSection('安全防护措施'),
Container(
color: Colors.white,
@ -401,10 +432,19 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
measuresList:
measuresList, // List<Map<String, dynamic>>
baseImgPath: ApiService.baseImgPath,
isAllowEdit: true,
onSign: (item) {
_itemToSign(item, measuresList.indexOf(item));
},
),
),
],
),
ItemListWidget.singleLineTitleText(label: '其他安全措施:',
isEditable: true,
hintText: '请输入其他安全措施',
controller
:_otherController),
SizedBox(height: 20),
Row(
@ -468,13 +508,11 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
padding: EdgeInsets.all(12),
child: Column(
children: [
// _card(_defaultDetail()),
_card(
WorkDetailFormWidget(
pd: pd,
isEditable: false,
onChooseLevel: (){},
onChooseHotworkUser: (){},
onChooseLevel: () {},
onChooseHotworkUser: () {},
onAnalyzeTap: () {
pushPage(
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
@ -482,7 +520,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
);
},
),
),
SizedBox(height: 20),
_setSafeDetailWidget(),
SizedBox(height: 20),
@ -514,7 +551,8 @@ class MeasureItem {
List<Map<String, dynamic>>? userList,
this.userIndex = -1,
List<Map<String, dynamic>>? selectMeasures,
}) : userList = userList ?? [],
})
: userList = userList ?? [],
selectMeasures = selectMeasures ?? [];
Map<String, dynamic> toJson() {

View File

@ -11,18 +11,31 @@ import '../../../../../../tools/tools.dart';
import 'hotwork_apply_detail.dart';
///
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
///
class MeasuresListWidget extends StatelessWidget {
const MeasuresListWidget({
super.key,
required this.measuresList,
required this.baseImgPath,
required this.isAllowEdit,
this.onSign,
});
/// Map
final List<Map<String, dynamic>> measuresList;
///
final String baseImgPath;
const MeasuresListWidget({
Key? key,
required this.measuresList,
required this.baseImgPath,
}) : super(key: key);
/// &
final bool isAllowEdit;
/// item
final void Function(Map<String, dynamic> item)? onSign;
@override
Widget build(BuildContext context) {
@ -98,30 +111,44 @@ class MeasuresListWidget extends StatelessWidget {
item['SIGN_TIME'] as String? ?? '',
),
// 14 +
// 14 +
for (var i = 1; i <= 4; i++)
if (item.containsKey('QUESTION$i'))
_buildQnA(
item['QUESTION$i'] as String? ?? '',
item['ANSWER$i'] as String? ?? '0',
),
if ((item['QUESTION$i'] as String?)?.isNotEmpty ?? false)
_buildQnA(item, i),
],
),
),
// +
// / +
Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
//
Text(
//
isAllowEdit
? TextButton(
onPressed: () {
if ((item['STATUS'] as String?) != '-1') {
onSign?.call(item);
}
},
child: Text(
(item['SIGN_ITEM'] ?? '').toString().isNotEmpty
? '已签字'
: '签字',
style: TextStyle(
color: (item['STATUS'] as String?) == '-1'
? Colors.grey
: Colors.blue,
),
),
)
: Text(
(item['STATUS'] as String?) == '-1'
? '不涉及'
: '涉及',
style: TextStyle(
color:
(item['STATUS'] as String?) == '-1'
color: (item['STATUS'] as String?) == '-1'
? Colors.grey
: Colors.black,
),
@ -148,27 +175,71 @@ class MeasuresListWidget extends StatelessWidget {
);
}
/// +
Widget _buildQnA(String question, String answer) {
/// +
Widget _buildQnA(Map<String, dynamic> item, int index) {
final question = item['QUESTION$index'] as String? ?? '';
final key = 'ANSWER$index';
final answer = item[key]?.toString() ?? '';
if (isAllowEdit) {
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
children: [
Expanded(
flex: 2,
child: Text(
'$question:',
style: const TextStyle(fontWeight: FontWeight.w800),
),
),
Expanded(
flex: 1,
child: TextFormField(
initialValue: answer,
textAlign: TextAlign.center, //
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
isDense: true,
contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
//
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300),
),
),
onChanged: (val) {
item[key] = val;
},
),
),
],
),
);
}
//
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'$question: ',
'$question:',
style: const TextStyle(fontWeight: FontWeight.w800),
),
Text(answer.isNotEmpty ? answer : '0'),
],
),
),
Divider(),
const Divider(),
],
),
);
@ -190,13 +261,12 @@ class MeasuresListWidget extends StatelessWidget {
Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
pageBuilder:
(_, __, ___) =>
SingleImageViewer(imageUrl: '$baseImgPath$p'),
pageBuilder: (_, __, ___) =>
SingleImageViewer(imageUrl: '$baseImgPath\$p'),
),
);
},
child: Image.network('$baseImgPath$p', width: 80, height: 80),
child: Image.network('$baseImgPath\$p', width: 80, height: 80),
),
if (time.isNotEmpty) ...[const SizedBox(width: 8), Text(time)],
],
@ -206,6 +276,7 @@ class MeasuresListWidget extends StatelessWidget {
}
}
///
class OtherMeasuresWidget extends StatelessWidget {
///
@ -753,6 +824,7 @@ class _SelectionPopupState extends State<SelectionPopup> {
});
_getData();
}
void _determine() {
//
final result = selectValue.join(',');

View File

@ -8,6 +8,7 @@ import 'package:qhd_prevention/customWidget/department_picker.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/WorkDetailFormWidget.dart';
import 'package:qhd_prevention/tools/h_colors.dart';
import 'package:qhd_prevention/tools/tools.dart';
import '../../../../../../customWidget/bottom_picker.dart';
import '../../../../../../http/ApiService.dart';
@ -188,6 +189,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
Widget _card(Widget child) {
return Container(
padding: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
@ -283,22 +285,23 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
ToastUtil.showNormal(context, '请先选择$unitName');
return;
}
if (personList.isEmpty) { //
await _getPersonListForUnitId(unitId, type);
final list = _personCache[type] ?? [];
if (list.isEmpty) { //
ToastUtil.showNormal(context, '暂无数据,请选择其他单位');
}else{
choosePersonHandle(type);
}
return;
}
if (personList.isEmpty) {
//
ToastUtil.showNormal(context, '请先选择' + type.displayName);
return;
}
// if (personList.isEmpty) { //
// await _getPersonListForUnitId(unitId, type);
// final list = _personCache[type] ?? [];
//
// if (list.isEmpty) { //
// ToastUtil.showNormal(context, '暂无数据,请选择其他单位');
// }else{
// choosePersonHandle(type);
// }
// return;
// }
DepartmentPersonPicker.show(
context,
personsData: personList,
@ -481,6 +484,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: h_backGroundColor(),
appBar: MyAppbar(title: '动火安全作业申请'),
body: SafeArea(
child: SingleChildScrollView(
@ -584,6 +588,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
measuresList:
measuresList, // List<Map<String, dynamic>>
baseImgPath: ApiService.baseImgPath,
isAllowEdit: false,
),
),
],

View File

@ -68,15 +68,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
return jsonEncode(jsonList);
}
Widget _card(Widget child) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: child,
);
}
Widget _chooseItem(MeasureItem item) {
return Column(
@ -255,7 +247,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
);
}
/// 1 0
/// -1 1
Future<void> _submit(String status) async {
if (imagePaths.isEmpty) {
ToastUtil.showNormal(context, '请签字');
@ -572,7 +564,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
child: Column(
children: [
// _card(_defaultDetail()),
_card(
WorkDetailFormWidget(
pd: pd,
isEditable: false,
@ -585,7 +576,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
);
},
),
),
SizedBox(height: 20),
_setSafeDetailWidget(),
SizedBox(height: 20),

View File

@ -1,7 +1,7 @@
import 'dart:ffi';
import 'dart:ui';
Color h_backGroundColor() => Color(0xFFF5F7FA);
Color h_backGroundColor() => Color(0xFFF1F1F1);
List<Color> riskLevelTextColors() {
return [Color(0xFFE54D42),Color(0xFFF37B1D),Color(0xFFF9BD08),Color(0xFF3281FF)];
}