hs 2025-09-05 11:27:45 +08:00
parent 019fff8329
commit 4055de6723
11 changed files with 456 additions and 312 deletions

View File

@ -24,6 +24,7 @@ class ApiService {
"http://192.168.20.240:8500/whb_stu_face/";
///
// static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
// static const String basePath = "http://192.168.20.240:8500/integrated_whb";
static const String basePath = "http://192.168.20.240:8500/integrated_whb";
// static const String basePath = "http://192.168.0.37:8099/api";
@ -1581,8 +1582,8 @@ U6Hzm1ninpWeE+awIDAQAB
"CORPINFO_ID": SessionService.instance.corpinfoId,
"USER_ID": SessionService.instance.loginUserId,
"OPERATOR": SessionService.instance.loginUserId,
"NAME": SessionService.instance.loginUser?["USERNAME"] ?? "",
"USERNAME": SessionService.instance.loginUser?["USERNAME"] ?? "",
"NAME": SessionService.instance.username,
"USERNAME": SessionService.instance.username,
},
);
}
@ -2481,7 +2482,7 @@ U6Hzm1ninpWeE+awIDAQAB
String id,
) {
return HttpManager().request(
basePath,
basePath,
'/app/hidden/riskListCheckInspection',
method: Method.post,
data: {

View File

@ -59,6 +59,8 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
@override
void initState() {
super.initState();
// webview controller使
_initLocation();
// UI 使 setState
_fetchAll();
}
@ -112,12 +114,114 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
centerLng = lng;
_loadingMap = false;
});
// controller
if (_controller == null) {
// controller
await _initLocation();
} else {
// controller onPageFinished
await _injectLocationParams();
}
} catch (e, st) {
debugPrint('getMapData error: $e\n$st');
setState(() => _loadingMap = false);
}
}
Future<void> _initLocation() async {
try {
// controller
if (_controller != null) return;
// controller setState 使 BaiduMapWebView controller
setState(() {
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('JS', onMessageReceived: (JavaScriptMessage message) {
debugPrint('JS LOG: ${message.message}');
})
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (String url) async {
debugPrint('网页加载完成: $url');
//
await _injectLocationParams();
},
onWebResourceError: (err) {
debugPrint('Web resource error: ${err.description}');
},
),
);
});
// setState
await Future.delayed(const Duration(milliseconds: 50));
try {
await _controller!.loadRequest(Uri.parse('http://47.92.102.56:7811/file/fluteightmap/index.html'));
} catch (e, st) {
debugPrint('loadRequest 错误: $e\n$st');
}
} catch (e, st) {
debugPrint('_initLocation error: $e\n$st');
}
}
Future<void> _injectLocationParams() async {
if (_controller == null) {
debugPrint('_injectLocationParams: controller 为空,跳过注入');
return;
}
// 使 centerLat/centerLng
final params = {
'longitude': centerLng,
'latitude': centerLat,
'GSON': covers, // covers
't': DateTime.now().millisecondsSinceEpoch,
};
final jsonParams = jsonEncode(params);
try {
await _controller!.runJavaScript('''
(function(){
try {
if (typeof window.initWithData === 'function') {
window.initWithData($jsonParams);
} else if (typeof window.initMap === 'function') {
window.initMap($jsonParams);
} else {
console.error('initWithData / initMap function not found');
}
} catch(e) {
console.error('call initWithData error', e);
}
})();
''');
debugPrint('已注入地图初始化参数: $jsonParams');
} catch (e, st) {
debugPrint('注入位置参数失败: $e\n$st');
// race
await Future.delayed(const Duration(milliseconds: 200));
try {
if (_controller != null) {
await _controller!.runJavaScript('''
(function(){
try {
if (typeof window.initWithData === 'function') {
window.initWithData($jsonParams);
} else if (typeof window.initMap === 'function') {
window.initMap($jsonParams);
}
} catch(e) {}
})();
''');
}
} catch (_) {}
}
}
List<Map<String, dynamic>> buildCoversFromRes(Map res) {
final list = <Map<String, dynamic>>[];
@ -378,72 +482,6 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
),
);
}
/// WebView
Future<void> _initLocation() async {
//
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('JS', onMessageReceived: (JavaScriptMessage message) {
debugPrint('JS LOG: ${message.message}');
})
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (String url) async {
debugPrint('网页加载完成: $url');
await _injectLocationParams();
},
onWebResourceError: (err) {
debugPrint('Web resource error: ${err.description}');
},
),
);
// loadRequest
try {
await _controller!.loadRequest(Uri.parse('http://47.92.102.56:7811/file/fluteightmap/index.html'));
} catch (e, st) {
debugPrint('loadRequest 错误: $e\n$st');
}
}
Future<void> _injectLocationParams() async {
if (_controller == null) {
debugPrint('_injectLocationParams: controller 为空,跳过注入');
return;
}
final params = {
'longitude': centerLng,
'latitude': centerLat,
'GSON': [],
't': DateTime.now().millisecondsSinceEpoch,
};
final jsonParams = jsonEncode(params);
try {
await _controller!.runJavaScript('''
(function(){
try {
if (typeof window.initWithData === 'function') {
window.initWithData($jsonParams);
} else if (typeof window.initMap === 'function') {
window.initMap($jsonParams);
} else {
console.error('initWithData / initMap function not found');
}
} catch(e) {
console.error('call initWithData error', e);
}
})();
''');
debugPrint('已注入地图初始化参数');
} catch (e) {
debugPrint('注入位置参数失败: $e');
}
}
Widget _buildTableHeaderCell(String text) {
return Padding(padding: EdgeInsets.all(8), child: Center(child: Text(text, style: TextStyle(fontWeight: FontWeight.bold))));

View File

@ -68,9 +68,11 @@ class _SafecheckSignDetailState extends State<SafecheckSignDetail> {
Future<void> _getData() async {
try {
LoadingDialogHelper.show();
final result = await ApiService.getSafeCheckSureList(
widget.INSPECTION_ID,
);
LoadingDialogHelper.hide();
// await mounted pop setState
if (!mounted) return;
setState(() {

View File

@ -78,7 +78,6 @@ class _SafecheckAssignmentDetailPageState
.map((item) => '${ApiService.baseImgPath}${item['FILEPATH']}')
.toList();
files = data['hImgs'];
});
}
}
@ -164,9 +163,8 @@ class _SafecheckAssignmentDetailPageState
if (result['result'] == 'success') {
ToastUtil.showNormal(context, '保存成功');
Navigator.of(context).pop();
}else{
} else {
ToastUtil.showNormal(context, '保存失败');
}
LoadingDialogHelper.hide();
} catch (e) {
@ -213,6 +211,12 @@ class _SafecheckAssignmentDetailPageState
ItemListWidget.singleLineTitleText(
label: '隐患类型',
isEditable: false,
text: form['HIDDENTYPE_NAME'] ?? '',
),
const Divider(),
ItemListWidget.singleLineTitleText(
label: '整改类型',
isEditable: false,
text:
_rect_des[form['RECTIFICATIONTYPE'].toString()] ?? '',
),
@ -241,7 +245,6 @@ class _SafecheckAssignmentDetailPageState
initialMediaPaths: hiddenVideo,
onChanged: (files) {},
onAiIdentify: () {},
),
),
],
@ -322,13 +325,16 @@ class _SafecheckAssignmentDetailPageState
),
),
SizedBox(height: 15),
Padding(padding: EdgeInsets.symmetric(horizontal: 50, vertical: 10), child: CustomButton(
text: _isEdit ? '保存' : '返回',
backgroundColor: Colors.blue,
onPressed: () {
_submit();
},
),)
Padding(
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 10),
child: CustomButton(
text: _isEdit ? '保存' : '返回',
backgroundColor: Colors.blue,
onPressed: () {
_submit();
},
),
),
],
),
),

View File

@ -67,9 +67,11 @@ class _SafecheckDangerDetailState extends State<SafecheckDangerDetail> {
Future<void> _getData() async {
try {
LoadingDialogHelper.show();
final result = await ApiService.getSafeCheckSureList(
widget.INSPECTION_ID,
);
LoadingDialogHelper.hide();
// await mounted pop setState
if (!mounted) return;
setState(() {

View File

@ -34,7 +34,10 @@ class _DefendRecordDetailPageState extends State<DefendRecordDetailPage> {
widget.INSPECTION_ID,
);
if (result['result'] == 'success') {
_list = result['list'];
setState(() {
_list = result['list'];
});
}
LoadingDialogHelper.hide();
} catch (e) {
@ -53,6 +56,7 @@ class _DefendRecordDetailPageState extends State<DefendRecordDetailPage> {
'申辩意见:${item['INSPECTED_EXPLAIN'] ?? ''}',
),
),
const Divider(),
ItemListWidget.singleLineTitleText(
label: '申辩时间',
isEditable: false,

View File

@ -150,7 +150,7 @@ class _MineDutyApplicationPage extends State<MineDutyApplicationPage> {
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
color: Colors.blue.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 6,
offset: const Offset(0, 2),
@ -160,6 +160,7 @@ class _MineDutyApplicationPage extends State<MineDutyApplicationPage> {
child: TextField(
controller: _reasonController,
maxLines: 5,
maxLength: 120, // 200
decoration: const InputDecoration(
hintText: "请输入离岗原因",
hintStyle: TextStyle(color: Color(0xFF9E9E9E)),

View File

@ -36,12 +36,10 @@ class MineDutyManagementPage extends StatefulWidget {
}
class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
int showCount=-1;
int currentPage=1;
int showCount = -1;
int currentPage = 1;
late List<dynamic> _list = [];
Future<void> _onRefresh() async {
//
await Future.delayed(const Duration(seconds: 2));
@ -55,19 +53,16 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
@override
void initState() {
_getListData();
}
void refreshData(){
currentPage=1;
void refreshData() {
currentPage = 1;
_list.clear();
_getListData();
}
@override
Widget build(BuildContext context) {
//
// final leaveRecords = [
// LeaveRecord(
@ -110,82 +105,89 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
return Scaffold(
backgroundColor: const Color(0xFFF5F7FA),
appBar: MyAppbar(title: "离岗管理",actions: [
TextButton(
appBar: MyAppbar(
title: "离岗管理",
actions: [
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MineDutyApplicationPage(
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
builder:
(context) => MineDutyApplicationPage(
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
),
);
// pushPage(MineDutyApplicationPage(), context);
},
child: Text("申请",style: TextStyle(color: Colors.white,fontSize: 16,fontWeight:FontWeight.bold),))
],),
body:
RefreshIndicator(
onRefresh: _onRefresh,
child:
// Column(
// children: [
//
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// color: const Color(0xFFE3F2FD),
// child: const Row(
// children: [
// Icon(Icons.info_outline, color: Color(0xFF1976D2)),
// SizedBox(width: 8),
// Text(
// "离岗记录显示最近30天内的申请记录",
// style: TextStyle(color: Color(0xFF1976D2), fontSize: 14),
// ),
// ],
// ),
// ),
//
Expanded(
child:
_list.isEmpty
? NoDataWidget.show()
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: _list.length,
itemBuilder: (context, index) {
final record = _list[index];
return _buildRecordCard(record,context);
},
},
child: Text(
"申请",
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
// ),
// ],
),
],
),
body: RefreshIndicator(
onRefresh: _onRefresh,
child:
// Column(
// children: [
//
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// color: const Color(0xFFE3F2FD),
// child: const Row(
// children: [
// Icon(Icons.info_outline, color: Color(0xFF1976D2)),
// SizedBox(width: 8),
// Text(
// "离岗记录显示最近30天内的申请记录",
// style: TextStyle(color: Color(0xFF1976D2), fontSize: 14),
// ),
// ],
// ),
// ),
//
Expanded(
child:
_list.isEmpty
? NoDataWidget.show()
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: _list.length,
itemBuilder: (context, index) {
final record = _list[index];
return _buildRecordCard(record, context);
},
),
// ),
// ],
),
),
),
);
}
Widget _buildRecordCard(final item , BuildContext context) {
Widget _buildRecordCard(final item, BuildContext context) {
// final dateFormat = DateFormat('yyyy-MM-dd');
// final isSameDay = record.startDate == record.endDate;
// final dateRange = isSameDay
// ? dateFormat.format(record.startDate)
// : "${dateFormat.format(record.startDate)}${dateFormat.format(record.endDate)}";
final dateRange ="${item['STARTTIME']}${item['ENDTIME']}";
final dateRange = "${item['STARTTIME']}${item['ENDTIME']}";
return
GestureDetector(
return GestureDetector(
onTap: () {
pushPage(MineDutyDetailPage(item), context);
},
child:
Container(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
@ -196,7 +198,7 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
spreadRadius: 1,
blurRadius: 6,
offset: const Offset(0, 2),
)
),
],
),
child: Padding(
@ -230,16 +232,12 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
const SizedBox(height: 4),
Text(
"部门:${item['DEPARTMENTNAME']} \n岗位:${item['POSTNAME']}",
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
),
style: TextStyle(fontSize: 13, color: Colors.grey[600]),
),
],
),
// const Spacer(),
// const Spacer(),
],
),
@ -248,15 +246,18 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
//
Row(
children: [
const Icon(Icons.calendar_today, size: 18, color: Colors.grey),
const Icon(
Icons.calendar_today,
size: 18,
color: Colors.grey,
),
const SizedBox(width: 8),
Expanded(
child: Text(
"离岗时间: $dateRange",
style: const TextStyle(color: Colors.grey),
),
child: Text(
"离岗时间: $dateRange",
style: const TextStyle(color: Colors.grey),
),
),
],
),
@ -268,83 +269,91 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
// // borderRadius: BorderRadius.circular(20),
// // ),
// child:
Text(
"审核状态:${_getTypeReturn(item)}",
// record.status,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
),
Text(
"审核状态:${_getTypeReturn(item)}",
// record.status,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
),
// ),
),
// ),
const SizedBox(height: 4),
//
if( item["REVIEW_STATUS"]=="0"&&DateTime.now().isBefore(item["ENDTIME"])&&
(item["REVIEW_USER_ID"]==SessionService.instance.loginUserId)&&
SessionService.instance.loginUser?["USERNAME"]=="1"&&item['REVIEW_STATUS']=="0")
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => DutyDialog(
item,1,
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1976D2),
// padding: const EdgeInsets.symmetric(horizontal: 10),
if (item["REVIEW_STATUS"] == "0" &&
isBeforeNow(item["ENDTIME"]) &&
(item["REVIEW_USER_ID"] ==
SessionService.instance.loginUserId) &&
SessionService.instance.loginUser?["USERNAME"] == "1" &&
item['REVIEW_STATUS'] == "0")
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder:
(context) => DutyDialog(
item,
1,
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1976D2),
// padding: const EdgeInsets.symmetric(horizontal: 10),
),
child: const Text(
"审 批",
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
child: const Text("审 批", style: TextStyle(color: Colors.white,fontSize: 12)),
),
const SizedBox(width: 16),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => DutyDialog(
item,2,
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
builder:
(context) => DutyDialog(
item,
2,
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
// padding: const EdgeInsets.symmetric(horizontal: 10),
),
child: const Text("取 消", style: TextStyle(color: Colors.white,fontSize: 12)),
)
],
),
child: const Text(
"取 消",
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
],
),
],
),
),
),
);
}
Future<void> _getListData() async {
try {
final result = await ApiService.getDutyManagement(showCount,currentPage);
final result = await ApiService.getDutyManagement(showCount, currentPage);
if (result['result'] == 'success') {
final List<dynamic> newList = result['varList'] ?? [];
setState(() {
@ -356,56 +365,47 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
}
}
String _getTypeReturn(final item) {
String type=item['REVIEW_STATUS'];
if("0"==type){
return "待审批";
}else if("1"==type){
return "审批通过";
}else if("2"==type){
return "无需审批";
}else if("-1"==type){
String type2=item['ISDELETE'];
if("1"==type2){
if(item['CREATOR']==item['OPERATOR']){
return "申请人取消";
}else{
return "审批人取消";
}
}else{
return "审批打回";
}
}else{
return "审批错误";
}
String type = item['REVIEW_STATUS'];
if ("0" == type) {
return "待审批";
} else if ("1" == type) {
return "审批通过";
} else if ("2" == type) {
return "无需审批";
} else if ("-1" == type) {
String type2 = item['ISDELETE'];
if ("1" == type2) {
if (item['CREATOR'] == item['OPERATOR']) {
return "申请人取消";
} else {
return "审批人取消";
}
} else {
return "审批打回";
}
} else {
return "审批错误";
}
}
}
enum FeedbackType {
tongGuo,
Dahui,
}
enum FeedbackType { tongGuo, Dahui }
class DutyDialog extends StatefulWidget {
const DutyDialog(this.item,this.type, {super.key,required this.onClose});
const DutyDialog(this.item, this.type, {super.key, required this.onClose});
final Function(String) onClose; //
final item;
final int type;
@override
State<DutyDialog> createState() => _DutyDialogState();
}
class _DutyDialogState extends State<DutyDialog> {
final TextEditingController _reasonController = TextEditingController();
//
FeedbackType? _selectedType = FeedbackType.tongGuo;
@ -419,18 +419,20 @@ class _DutyDialogState extends State<DutyDialog> {
}
}
Future<void> _dutyApproval() async {
try {
String typeString;
if(FeedbackType.tongGuo==_selectedType){
typeString="1";
}else{
typeString="-1";
if (FeedbackType.tongGuo == _selectedType) {
typeString = "1";
} else {
typeString = "-1";
}
final result = await ApiService.dutyApproval(typeString,_reasonController.text,widget.item["OFFDUTY_ID"]);
final result = await ApiService.dutyApproval(
typeString,
_reasonController.text,
widget.item["OFFDUTY_ID"],
);
if (result['result'] == 'success') {
widget.onClose('关闭提交'); //
}
@ -441,8 +443,11 @@ class _DutyDialogState extends State<DutyDialog> {
Future<void> _dutyReturned() async {
try {
final result = await ApiService.dutyReturned("-1",_reasonController.text,widget.item["OFFDUTY_ID"]);
final result = await ApiService.dutyReturned(
"-1",
_reasonController.text,
widget.item["OFFDUTY_ID"],
);
if (result['result'] == 'success') {
widget.onClose('关闭提交'); //
}
@ -479,37 +484,39 @@ class _DutyDialogState extends State<DutyDialog> {
// _buildActionButton('打印', Colors.blue),
// ],
// ),
if(1==widget.type)
Center(
child: Wrap(
spacing: 16,
children:
FeedbackType.values.map((type) {
return ChoiceChip(
label: Text(_getTypeName(type)),
selected: _selectedType == type,
onSelected: (selected) {
setState(() {
if (selected) {
_selectedType = type;
}
});
},
);
}).toList(),
if (1 == widget.type)
Center(
child: Wrap(
spacing: 16,
children:
FeedbackType.values.map((type) {
return ChoiceChip(
label: Text(_getTypeName(type)),
selected: _selectedType == type,
onSelected: (selected) {
setState(() {
if (selected) {
_selectedType = type;
}
});
},
);
}).toList(),
),
),
),
const SizedBox(height: 20),
//
TextField(
TextField(
controller: _reasonController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: widget.type==1?'请输入审批意见':'请输入原因',
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
hintText: widget.type == 1 ? '请输入审批意见' : '请输入原因',
contentPadding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
),
maxLines: 4,
),
@ -522,39 +529,49 @@ class _DutyDialogState extends State<DutyDialog> {
children: [
Expanded(
child: ElevatedButton(
onPressed: (){
if(_reasonController.text.isEmpty){
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(widget.type==1?'请输入审批意见':'请输入原因'))
);
return;
}
onPressed: () {
if (_reasonController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
widget.type == 1 ? '请输入审批意见' : '请输入原因',
),
),
);
return;
}
if(1==widget.type){
_dutyApproval();//
}else{
_dutyReturned();//
}
Navigator.pop(context);
} ,
if (1 == widget.type) {
_dutyApproval(); //
} else {
_dutyReturned(); //
}
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('提交',style: TextStyle(color: Colors.white),),
child: const Text(
'提交',
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(width: 15),
Expanded(
child: ElevatedButton(
onPressed: (){
onPressed: () {
Navigator.pop(context);
} ,
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('关闭',style: TextStyle(color: Colors.white),),
child: const Text(
'关闭',
style: TextStyle(color: Colors.white),
),
),
),
],
@ -564,9 +581,4 @@ class _DutyDialogState extends State<DutyDialog> {
),
);
}
}

View File

@ -101,6 +101,7 @@ class _FeedbackPageState extends State<FeedbackPage> {
const Text('标题', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
TextFormField(
maxLength: 50,
controller: _titleController,
decoration: const InputDecoration(
hintText: '请输入标题...',
@ -124,6 +125,7 @@ class _FeedbackPageState extends State<FeedbackPage> {
TextFormField(
controller: _descriptionController,
maxLines: 5,
maxLength: 120,
decoration:
const InputDecoration(
hintText: '请补充详细问题和意见...',

View File

@ -284,24 +284,29 @@ String formatDate(DateTime? date, String fmt) {
/// 'yyyy-MM-dd HH:mm' 'yyyy-MM-ddTHH:mm' DateTime null
DateTime? _parseYMdHm(String s) {
if (s.isEmpty) return null;
// 'yyyy-MM-dd HH:mm' 'yyyy-MM-ddTHH:mm'
// :00
String t = s.trim();
// null
if (!RegExp(r'^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}$').hasMatch(t)) {
// 'yyyy-MM-dd HH:mm:ss'
if (!RegExp(r'^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}$').hasMatch(t)) {
return null;
} else {
// parse
return DateTime.tryParse(t.replaceFirst(' ', 'T'));
}
// yyyy-MM-dd
if (RegExp(r'^\d{4}-\d{2}-\d{2}$').hasMatch(t)) {
return DateTime.tryParse(t); // 00:00:00
}
// 'yyyy-MM-dd HH:mm'使 ISO
final iso = t.replaceFirst(' ', 'T') + ':00';
return DateTime.tryParse(iso);
// yyyy-MM-dd HH:mm yyyy-MM-ddTHH:mm
if (RegExp(r'^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}$').hasMatch(t)) {
final iso = t.replaceFirst(' ', 'T') + ':00';
return DateTime.tryParse(iso);
}
// yyyy-MM-dd HH:mm:ss yyyy-MM-ddTHH:mm:ss
if (RegExp(r'^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}$').hasMatch(t)) {
return DateTime.tryParse(t.replaceFirst(' ', 'T'));
}
// null
return null;
}
/// 'yyyy-MM-dd HH:mm'
/// 1 (a>b), 0 (a==b), -1 (a<b)
int compareYMdHmStrings(String a, String b) {
@ -320,7 +325,14 @@ bool isAfterStr(String a, String b) => compareYMdHmStrings(a, b) == 1;
/// 便a b
bool isBeforeStr(String a, String b) => compareYMdHmStrings(a, b) == -1;
/// (yyyy-MM-dd HH:mm)
bool isBeforeNow(String timeStr) {
final dt = _parseYMdHm(timeStr);
if (dt == null) {
throw FormatException("时间格式错误,期望 'yyyy-MM-dd HH:mm' 或 'yyyy-MM-dd HH:mm:ss'");
}
return dt.isBefore(DateTime.now());
}
/// ------------------------------------------------------
///
/// ------------------------------------------------------

View File

@ -765,6 +765,70 @@ packages:
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "0.5.0"
open_file:
dependency: "direct main"
description:
name: open_file
sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "3.5.10"
open_file_android:
dependency: transitive
description:
name: open_file_android
sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.0.6"
open_file_ios:
dependency: transitive
description:
name: open_file_ios
sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.0.3"
open_file_linux:
dependency: transitive
description:
name: open_file_linux
sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "0.0.5"
open_file_mac:
dependency: transitive
description:
name: open_file_mac
sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.0.3"
open_file_platform_interface:
dependency: transitive
description:
name: open_file_platform_interface
sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.0.3"
open_file_web:
dependency: transitive
description:
name: open_file_web
sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "0.0.4"
open_file_windows:
dependency: transitive
description:
name: open_file_windows
sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "0.0.3"
package_info_plus:
dependency: "direct main"
description: