2025-08-21 16:44:24 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
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/pages/main_tab.dart';
|
|
|
|
|
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
|
|
|
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
|
|
|
|
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
|
|
|
|
import 'package:qhd_prevention/http/ApiService.dart';
|
|
|
|
|
import 'package:qhd_prevention/tools/tools.dart';
|
|
|
|
|
|
|
|
|
|
class PromisePage extends StatefulWidget {
|
|
|
|
|
const PromisePage({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<PromisePage> createState() => _PromisePageState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _PromisePageState extends State<PromisePage> {
|
|
|
|
|
bool _loading = true;
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> info = {};
|
|
|
|
|
String? baseImgPath;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
_loadData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _loadData() async {
|
|
|
|
|
setState(() {
|
|
|
|
|
_loading = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final Map<String, dynamic> payload =
|
|
|
|
|
await ApiService.safeCorppromiseDetail();
|
|
|
|
|
|
|
|
|
|
final coll = (payload['COLLATERAL'] as List?) ?? [];
|
|
|
|
|
final DETAIL =
|
|
|
|
|
coll.map((e) {
|
|
|
|
|
return {
|
|
|
|
|
'value': e?['COLLATERAL']?.toString() ?? '',
|
|
|
|
|
'id': e?['PROMISEDETAIL_ID']?.toString() ?? '',
|
|
|
|
|
};
|
|
|
|
|
}).toList();
|
|
|
|
|
|
|
|
|
|
final Map<String, dynamic> resolved = {};
|
|
|
|
|
if (payload['TEXT'] is Map) {
|
|
|
|
|
resolved.addAll(payload['TEXT'] as Map<String, dynamic>);
|
|
|
|
|
} else {
|
|
|
|
|
resolved['TEXT'] = payload['TEXT']?.toString() ?? '';
|
|
|
|
|
}
|
|
|
|
|
resolved['DETAIL'] = DETAIL;
|
|
|
|
|
resolved['SIGNTIME'] =
|
|
|
|
|
DateTime.now().millisecondsSinceEpoch.toString();
|
|
|
|
|
resolved['COVERPEOPLE'] =
|
|
|
|
|
(payload['COVERPEOPLE'] is List && payload['COVERPEOPLE'].isNotEmpty)
|
|
|
|
|
? payload['COVERPEOPLE'][0]['USERNAME']?.toString() ?? ''
|
|
|
|
|
: '';
|
|
|
|
|
// 合并其他顶层字段(保留现有字段)
|
|
|
|
|
if (payload is Map<String, dynamic>) {
|
|
|
|
|
payload.forEach((k, v) {
|
|
|
|
|
if (!resolved.containsKey(k)) resolved[k] = v;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
resolved['PROMISEPEOPLE_ID'] = payload['PROMISEPEOPLE_ID'] ?? '';
|
|
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
|
info = resolved;
|
|
|
|
|
_loading = false;
|
|
|
|
|
});
|
|
|
|
|
} catch (e, st) {
|
|
|
|
|
debugPrint('safeCorppromiseDetail error: $e\n$st');
|
|
|
|
|
setState(() {
|
|
|
|
|
_loading = false;
|
|
|
|
|
});
|
|
|
|
|
ToastUtil.showNormal(context, '加载失败,请稍后重试');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _formatDateShort(String? s) {
|
|
|
|
|
if (s == null) return '';
|
|
|
|
|
if (s.length >= 10) return s.substring(0, 10);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 点击签字:导航到你自己实现的签字页面,等待返回签字路径(String)
|
|
|
|
|
Future<void> _sign() async {
|
|
|
|
|
await NativeOrientation.setLandscape();
|
|
|
|
|
final String path = await Navigator.push(
|
|
|
|
|
context,
|
|
|
|
|
MaterialPageRoute(builder: (c) => MineSignPage()),
|
|
|
|
|
);
|
2025-08-29 11:05:17 +08:00
|
|
|
|
await NativeOrientation.setPortrait();
|
|
|
|
|
|
2025-08-21 16:44:24 +08:00
|
|
|
|
if (path != null) {
|
|
|
|
|
setState(() {
|
|
|
|
|
info['FILEPATH'] = path;
|
|
|
|
|
});
|
|
|
|
|
FocusHelper.clearFocus(context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提交:占位实现 —— 请把下面的逻辑替换为你项目的上传/提交 API(例如 ApiService.editPeopleII 或类似)
|
|
|
|
|
Future<void> submitSignedPromise() async {
|
|
|
|
|
LoadingDialogHelper.show();
|
|
|
|
|
final filePath = (info['FILEPATH'] ?? '').toString();
|
|
|
|
|
if (filePath.isEmpty) {
|
|
|
|
|
ToastUtil.showNormal(context, '请签字');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
final result = await ApiService.submitCorppromiseSign(filePath,info);
|
|
|
|
|
LoadingDialogHelper.hide();
|
|
|
|
|
if (result['result'] == 'success') {
|
|
|
|
|
ToastUtil.showSuccess(context, '提交成功');
|
|
|
|
|
Navigator.pushReplacement(
|
|
|
|
|
context,
|
|
|
|
|
MaterialPageRoute(builder: (_) => const MainPage()),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildTitle() {
|
|
|
|
|
final type = (info['TYPE'] ?? '').toString();
|
|
|
|
|
final title = type == '0' ? '安全生产承诺书' : '安全生产责任状';
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
|
|
|
|
child: Text(
|
|
|
|
|
title,
|
|
|
|
|
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildHeaderName() {
|
|
|
|
|
final type = (info['TYPE'] ?? '').toString();
|
|
|
|
|
if (type == '0') {
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
|
|
|
child: Text(
|
|
|
|
|
'${info['COVERPEOPLE'] ?? ''}:',
|
|
|
|
|
style: const TextStyle(fontSize: 16),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildParagraph(String text) {
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
|
|
|
|
|
child: RichText(
|
|
|
|
|
textAlign: TextAlign.justify,
|
|
|
|
|
text: TextSpan(
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
height: 1.6,
|
|
|
|
|
letterSpacing: 0.5,
|
|
|
|
|
color: Colors.black,
|
|
|
|
|
),
|
|
|
|
|
children: [
|
|
|
|
|
const WidgetSpan(
|
|
|
|
|
child: SizedBox(width: 28), // 缩进2个汉字宽度
|
|
|
|
|
),
|
|
|
|
|
TextSpan(text: text),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildCollateralList() {
|
|
|
|
|
final detail = (info['DETAIL'] as List?) ?? [];
|
|
|
|
|
if (detail.isEmpty) return const SizedBox.shrink();
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.only(left: 0, right: 8.0, top: 6.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children:
|
|
|
|
|
detail.map((e) {
|
|
|
|
|
return _buildParagraph(e['value']?.toString() ?? '');
|
|
|
|
|
}).toList(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildFooter() {
|
|
|
|
|
final type = (info['TYPE'] ?? '').toString();
|
|
|
|
|
final signImagePath = (info['FILEPATH'] ?? '').toString();
|
|
|
|
|
final signTime = (info['SIGNTIME'] ?? '').toString();
|
|
|
|
|
final creatTime = (info['CREATTIME'] ?? '').toString();
|
|
|
|
|
|
|
|
|
|
Widget signPreview;
|
|
|
|
|
if (signImagePath.isNotEmpty && signImagePath.startsWith('http')) {
|
|
|
|
|
signPreview = Image.network(
|
|
|
|
|
(baseImgPath ?? '') + signImagePath,
|
|
|
|
|
width: 100,
|
|
|
|
|
height: 50,
|
2025-08-29 11:05:17 +08:00
|
|
|
|
fit: BoxFit.fill,
|
2025-08-21 16:44:24 +08:00
|
|
|
|
);
|
|
|
|
|
} else if (signImagePath.isNotEmpty && File(signImagePath).existsSync()) {
|
|
|
|
|
signPreview = Image.file(
|
|
|
|
|
File(signImagePath),
|
|
|
|
|
width: 100,
|
|
|
|
|
height: 50,
|
2025-08-29 11:05:17 +08:00
|
|
|
|
fit: BoxFit.fill,
|
2025-08-21 16:44:24 +08:00
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
signPreview = const SizedBox.shrink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
|
top: 12.0,
|
|
|
|
|
left: 8.0,
|
|
|
|
|
right: 8.0,
|
|
|
|
|
bottom: 20.0,
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
|
children: [
|
|
|
|
|
if (type == '0')
|
|
|
|
|
const Align(
|
|
|
|
|
alignment: Alignment.centerRight,
|
|
|
|
|
child: Text('承诺单位(盖章):'),
|
|
|
|
|
),
|
|
|
|
|
if (type == '1')
|
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
|
|
children: [
|
|
|
|
|
Text('发状人:${info['COVERPEOPLE'] ?? ''}'),
|
|
|
|
|
const SizedBox(height: 6),
|
|
|
|
|
Text(creatTime.isNotEmpty ? _formatDateShort(creatTime) : ''),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Row(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
|
|
|
children: [
|
|
|
|
|
Text(type == '0' ? '主要负责人签字:' : '受状人:'),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
presentOpaque(
|
|
|
|
|
SingleImageViewer(imageUrl: info['FILEPATH'] ?? ''),
|
|
|
|
|
context,
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
child: signPreview,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
CustomButton(
|
|
|
|
|
text:
|
|
|
|
|
(info['FILEPATH'] ?? '').toString().isNotEmpty
|
|
|
|
|
? '重签'
|
|
|
|
|
: '手写签字',
|
|
|
|
|
backgroundColor: Colors.blue,
|
|
|
|
|
padding: EdgeInsets.symmetric(
|
|
|
|
|
vertical: 0,
|
|
|
|
|
horizontal: 20,
|
|
|
|
|
),
|
|
|
|
|
height: 35,
|
|
|
|
|
onPressed: _sign,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
const SizedBox(),
|
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
|
|
children: [Text(signTime)],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Scaffold(
|
|
|
|
|
appBar: const MyAppbar(title: '安全承诺', isBack: false),
|
|
|
|
|
body:
|
|
|
|
|
_loading
|
|
|
|
|
? const Center(child: CircularProgressIndicator())
|
|
|
|
|
: SingleChildScrollView(
|
|
|
|
|
child: Container(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
|
horizontal: 12,
|
|
|
|
|
vertical: 5,
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
children: [_buildTitle()],
|
|
|
|
|
),
|
|
|
|
|
_buildHeaderName(),
|
|
|
|
|
_buildParagraph(info['TEXT']?.toString() ?? ''),
|
|
|
|
|
_buildCollateralList(),
|
|
|
|
|
if ((info['TYPE'] ?? '').toString() == '0') ...[
|
|
|
|
|
_buildParagraph(
|
|
|
|
|
'若违反上述承诺和未履行安全生产职责,或发生责任事故的,接受政府或公司事故调查组做出的处罚决定。',
|
|
|
|
|
),
|
|
|
|
|
_buildParagraph(
|
|
|
|
|
'承诺期限自${_formatDateShort(info['PROMISE_TERM_START']?.toString())}至${_formatDateShort(info['PROMISE_TERM_END']?.toString())}。',
|
|
|
|
|
),
|
|
|
|
|
] else ...[
|
|
|
|
|
_buildParagraph(
|
|
|
|
|
'若未履行安全生产职责,或发生生产安全事故的,接受公司或政府事故调查组做出的处罚。',
|
|
|
|
|
),
|
|
|
|
|
_buildParagraph(
|
|
|
|
|
'责任期限自${_formatDateShort(info['PROMISE_TERM_START']?.toString())}至${_formatDateShort(info['PROMISE_TERM_END']?.toString())}。',
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
_buildFooter(),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
SizedBox(
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
child: CustomButton(
|
|
|
|
|
text: '提 交',
|
|
|
|
|
backgroundColor: Colors.blue,
|
|
|
|
|
onPressed: submitSignedPromise,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|