2025.7.17 离岗完成

main
xufei 2025-07-17 17:58:04 +08:00
parent 9e64c43dd0
commit af81885441
8 changed files with 1046 additions and 313 deletions

View File

@ -17,12 +17,31 @@ class ApiService {
// static const String adminPath = "https://qaaqwh.qhdsafety.com/integrated_whb/";
// static const String projectManagerUrl = 'https://pm.qhdsafety.com/zy-projectManage/';
// static const String publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3pM3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OKU6Hzm1ninpWeE+awIDAQAB'
// ///
// static const String baseFacePath =
// "https://qaaqwh.qhdsafety.com/whb_stu_face/";
//
// ///
// static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
//
// ///
// static const String baseImgPath = "https://file.zcloudchina.com/YTHFile";
//
// ///
// static const String adminPath =
// "https://qaaqwh.qhdsafety.com/integrated_whb/";
//
// ///
// static const String projectManagerUrl =
// 'https://pm.qhdsafety.com/zy-projectManage';
///
static const String baseFacePath =
"https://qaaqwh.qhdsafety.com/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 baseImgPath = "https://file.zcloudchina.com/YTHFile";
@ -35,6 +54,7 @@ class ApiService {
static const String projectManagerUrl =
'https://pm.qhdsafety.com/zy-projectManage';
/// RSA
static const publicKey = '''
-----BEGIN PUBLIC KEY-----
@ -45,6 +65,9 @@ U6Hzm1ninpWeE+awIDAQAB
-----END PUBLIC KEY-----
''';
///
static Future<Map<String, dynamic>> loginCheck(String keydataVal) {
return HttpManager().request(
@ -403,6 +426,29 @@ U6Hzm1ninpWeE+awIDAQAB
);
}
///
static Future<Map<String, dynamic>> reloadFeedBack(String imagePath) async {
final file = File(imagePath);
if (!await file.exists()) {
throw ApiException('file_not_found', '图片不存在:$imagePath');
}
final fileName = file.path.split(Platform.pathSeparator).last;
return HttpManager().uploadFaceImage(
baseUrl: basePath,
path: '/app/feedback/upload',
fromData: {
"corpUserId":"",
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
'FFILE': await MultipartFile.fromFile(
file.path,
filename: fileName
),
}
);
}
///
static Future<Map<String, dynamic>> changePassWord(String oldPwd,String confirmPwd) {
return HttpManager().request(
@ -423,7 +469,7 @@ U6Hzm1ninpWeE+awIDAQAB
static Future<Map<String, dynamic>> getDutyManagement(int showCount, int currentPage) {
return HttpManager().request(
basePath,
'/app/user/editUserPwd?showCount=$showCount&currentPage=$currentPage',
'/app/offduty/list?showCount=$showCount&currentPage=$currentPage',
method: Method.post,
data: {
"CORPINFO_ID":SessionService.instance.corpinfoId,
@ -538,7 +584,7 @@ U6Hzm1ninpWeE+awIDAQAB
);
}
///
/// AI
static Future<Map<String, dynamic>> getAiAlarmList(String showCount, String currentPage,String keyWord) {
return HttpManager().request(
@ -555,5 +601,75 @@ U6Hzm1ninpWeE+awIDAQAB
}
///
static Future<Map<String, dynamic>> submitApplicationLeaving(String startTime, String endTime,String text) {
return HttpManager().request(
basePath,
'/app/offduty/add',
method: Method.post,
data: {
"OFFDUTY_ID":"",
"STARTTIME": startTime,
"ENDTIME": endTime,
"DESCR": text,
"REVIEW_USER_ID": "",
"CREATOR": SessionService.instance.loginUserId,
"OPERATOR": SessionService.instance.loginUserId,
"CORPINFO_ID": SessionService.instance.corpinfoId,
"USER_ID": SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> dutyApproval(String type, String text,String itemNum) {
return HttpManager().request(
basePath,
'/app/offduty/review',
method: Method.post,
data: {
"OFFDUTY_ID":itemNum,
"REVIEW_STATUS":type,
"REVIEW_DESC":text,
"OPERATOR":SessionService.instance.loginUserId
},
);
}
///
static Future<Map<String, dynamic>> dutyReturned(String type, String text,String itemNum) {
return HttpManager().request(
basePath,
'/app/offduty/cancel',
method: Method.post,
data: {
"OFFDUTY_ID":itemNum,
"REVIEW_STATUS":type,
"REVIEW_DESC":text,
"OPERATOR":SessionService.instance.loginUserId
},
);
}
///
static Future<Map<String, dynamic>> getDepartureRecordList(int showCount, int currentPage) {
return HttpManager().request(
basePath,
'/app/offduty/list?showCount=$showCount&currentPage=$currentPage',
method: Method.post,
data: {
"CORPINFO_ID": SessionService.instance.corpinfoId,
"USER_ID":SessionService.instance.loginUserId,
"ISMAIN":0,
"ISSUPERVISE":0,
"DEPARTMENT_ID": SessionService.instance.corpinfoId,
"TYPE": 'show'
},
);
}
}

View File

@ -86,7 +86,7 @@ class _FaceRecognitionPageState extends State<FaceRecognitionPage> {
setState(() => _message = '上传中...');
try {
final pic = await _cameraController!.takePicture();
final res = await ApiService.reloadMyFace(pic.path,);
final res = await ApiService.reloadMyFace(pic.path);
if (res['result'] == 'success') {
_onSuccess();
} else {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import '../../http/ApiService.dart';
import '../../tools/tools.dart';
import 'mine_duty_application.dart';
import 'mine_duty_detail.dart';
@ -35,58 +36,37 @@ class MineDepartureRecordPage extends StatefulWidget {
class _MineDepartureRecordPage extends State<MineDepartureRecordPage> {
int showCount=-1;
int currentPage=1;
late List<dynamic> _list = [];
Future<void> _onRefresh() async {
//
await Future.delayed(const Duration(seconds: 2));
// fetchData()
setState(() {
// TODO:
refreshData();
});
}
@override
void initState() {
_getListData();
}
void refreshData(){
currentPage=1;
_list.clear();
_getListData();
}
@override
Widget build(BuildContext context) {
//
final leaveRecords = [
LeaveRecord(
applicant: "王轩",
department: "测试部",
position: "测试员",
startDate: DateTime(2025, 7, 18),
endDate: DateTime(2025, 7, 18),
status: "无需审批",
statusColor: const Color(0xFF4CAF50),
),
LeaveRecord(
applicant: "王轩",
department: "测试部",
position: "测试员",
startDate: DateTime(2025, 7, 16),
endDate: DateTime(2025, 7, 16),
status: "无需审批",
statusColor: const Color(0xFF4CAF50),
),
LeaveRecord(
applicant: "李思",
department: "开发部",
position: "高级工程师",
startDate: DateTime(2025, 7, 20),
endDate: DateTime(2025, 7, 22),
status: "待审批",
statusColor: const Color(0xFFF57C00),
),
LeaveRecord(
applicant: "张伟",
department: "产品部",
position: "产品经理",
startDate: DateTime(2025, 7, 15),
endDate: DateTime(2025, 7, 17),
status: "已拒绝",
statusColor: const Color(0xFFF44336),
),
];
return Scaffold(
backgroundColor: const Color(0xFFF5F7FA),
@ -95,13 +75,17 @@ class _MineDepartureRecordPage extends State<MineDepartureRecordPage> {
RefreshIndicator(
onRefresh: _onRefresh,
child:
//
Expanded(
child: ListView.builder(
child:
_list.isEmpty
? Center(child: Text('暂无数据'))
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: leaveRecords.length,
itemCount: _list.length,
itemBuilder: (context, index) {
final record = leaveRecords[index];
final record = _list[index];
return _buildRecordCard(record,context);
},
),
@ -112,138 +96,399 @@ class _MineDepartureRecordPage extends State<MineDepartureRecordPage> {
);
}
Widget _buildRecordCard(LeaveRecord record, 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)}";
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']}";
return
// GestureDetector(
// onTap: () {
// pushPage(MineDutyDetailPage(), context);
// },
// child:
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 6,
offset: const Offset(0, 2),
)
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
children: [
// Container(
// width: 40,
// height: 40,
// decoration: BoxDecoration(
// color: const Color(0xFFE3F2FD),
// shape: BoxShape.circle,
// ),
// child: const Icon(Icons.person, color: Color(0xFF1976D2)),
// ),
// const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"申请人:${record.applicant}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
"部门:${record.department} \n岗位:${record.position}",
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
),
),
],
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: record.statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
),
child: Text(
record.status,
style: TextStyle(
color: record.statusColor,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 16),
//
Row(
children: [
const Icon(Icons.calendar_today, size: 18, color: Colors.grey),
const SizedBox(width: 8),
Text(
"离岗时间: $dateRange",
style: const TextStyle(color: Colors.grey),
),
],
),
const SizedBox(height: 8),
//
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
pushPage(MineDutyDetailPage(), context);
},
style: TextButton.styleFrom(
foregroundColor: const Color(0xFF1976D2),
),
child: const Text("查看详情"),
),
// const SizedBox(width: 16),
// if (record.status == "待审批")
// ElevatedButton(
// onPressed: () {},
// style: ElevatedButton.styleFrom(
// backgroundColor: const Color(0xFF1976D2),
// padding: const EdgeInsets.symmetric(horizontal: 20),
// ),
// child: const Text("审批", style: TextStyle(color: Colors.white)),
// )
],
),
GestureDetector(
onTap: () {
pushPage(MineDutyDetailPage(item), context);
},
child:
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 6,
offset: const Offset(0, 2),
)
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
children: [
// Container(
// width: 40,
// height: 40,
// decoration: BoxDecoration(
// color: const Color(0xFFE3F2FD),
// shape: BoxShape.circle,
// ),
// child: const Icon(Icons.person, color: Color(0xFF1976D2)),
// ),
// const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"申请人:${item['USER_NAME']}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
"部门:${item['DEPARTMENTNAME']} \n岗位:${item['POSTNAME']}",
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
),
),
],
),
// const Spacer(),
],
),
const SizedBox(height: 8),
//
Row(
children: [
const Icon(Icons.calendar_today, size: 18, color: Colors.grey),
const SizedBox(width: 8),
Expanded(
child: Text(
"离岗时间: $dateRange",
style: const TextStyle(color: Colors.grey),
),
),
],
),
const SizedBox(height: 4),
// Container(
// padding: const EdgeInsets.symmetric( vertical: 6),
// // decoration: BoxDecoration(
// // color: record.statusColor.withOpacity(0.1),
// // borderRadius: BorderRadius.circular(20),
// // ),
// child:
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),
),
child: const Text("关 闭", style: TextStyle(color: Colors.white,fontSize: 12)),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () {
showDialog(
context: context,
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)),
)
],
),
],
),
),
),
// ),
);
}
Future<void> _getListData() async {
try {
final result = await ApiService.getDepartureRecordList(showCount,currentPage);
if (result['result'] == 'success') {
final List<dynamic> newList = result['varList'] ?? [];
setState(() {
_list.addAll(newList);
});
}
} catch (e) {
print('加载出错: $e');
}
}
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 "审批错误";
}
}
}
enum FeedbackType {
tongGuo,
Dahui,
}
class DutyDialog extends StatefulWidget {
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;
//
String _getTypeName(FeedbackType type) {
switch (type) {
case FeedbackType.tongGuo:
return '通过';
case FeedbackType.Dahui:
return '打回';
}
}
Future<void> _dutyApproval() async {
try {
String typeString;
if(FeedbackType.tongGuo==_selectedType){
typeString="1";
}else{
typeString="-1";
}
final result = await ApiService.dutyApproval(typeString,_reasonController.text,widget.item["OFFDUTY_ID"]);
if (result['result'] == 'success') {
widget.onClose('关闭提交'); //
}
} catch (e) {
print('加载出错: $e');
}
}
Future<void> _dutyReturned() async {
try {
final result = await ApiService.dutyReturned("-1",_reasonController.text,widget.item["OFFDUTY_ID"]);
if (result['result'] == 'success') {
widget.onClose('关闭提交'); //
}
} catch (e) {
print('加载出错: $e');
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
const Center(
child: Text(
'离岗审批',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 20),
//
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// _buildActionButton('通过', Colors.green),
// _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(),
),
),
const SizedBox(height: 20),
//
TextField(
controller: _reasonController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: widget.type==1?'请输入审批意见':'请输入原因',
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
),
maxLines: 4,
),
const SizedBox(height: 20),
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: ElevatedButton(
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);
} ,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('提交',style: TextStyle(color: Colors.white),),
),
),
const SizedBox(width: 15),
Expanded(
child: ElevatedButton(
onPressed: (){
Navigator.pop(context);
} ,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('关闭',style: TextStyle(color: Colors.white),),
),
),
],
),
],
),
),
);
}
}

View File

@ -2,10 +2,14 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import '../../http/ApiService.dart';
import '../../tools/tools.dart';
class MineDutyApplicationPage extends StatefulWidget {
const MineDutyApplicationPage({super.key});
const MineDutyApplicationPage({super.key,required this.onClose});
final Function(String) onClose; //
@override
State<MineDutyApplicationPage> createState() => _MineDutyApplicationPage();
@ -15,7 +19,27 @@ class _MineDutyApplicationPage extends State<MineDutyApplicationPage> {
DateTime? _startDate;
DateTime? _endDate;
final TextEditingController _reasonController = TextEditingController();
final String _applicant = "王轩";
Future<void> _submitApplicationLeaving() async {
try {
var formatter = DateFormat('yyyy-MM-dd'); // 使 'dd-MM-yyyy'
String startTime= formatter.format(_startDate!);
String endTime= formatter.format(_endDate!);
final result = await ApiService.submitApplicationLeaving(startTime,endTime,_reasonController.text);
if (result['result'] == 'success') {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('提交成功')));
widget.onClose('关闭提交'); //
Navigator.pop(context); //
}
} catch (e) {
print('加载出错: $e');
}
}
Future<void> _selectDate(BuildContext context, bool isStartDate) async {
final DateTime? picked = await showDatePicker(
@ -42,19 +66,30 @@ class _MineDutyApplicationPage extends State<MineDutyApplicationPage> {
if (picked != null) {
setState(() {
if (isStartDate) {
_startDate = picked;
//
if (_endDate == null || _endDate!.isBefore(picked)) {
_endDate = picked;
if(picked.isBefore(DateTime.now().subtract(Duration(days: 1)))){
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('开始日期不能早于当前日期')));
}else {
_startDate = picked;
//
if (_endDate == null || _endDate!.isBefore(picked)) {
_endDate = picked;
}
}
} else {
//
if (_startDate != null && picked.isBefore(_startDate!)) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('结束日期不能早于开始日期'))
);
} else {
_endDate = picked;
if(picked.isBefore(DateTime.now().subtract(Duration(days: 1)))){
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('结束日期不能早于当前日期')));
}else {
//
if (_startDate != null && picked.isBefore(_startDate!)) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('结束日期不能早于开始日期'))
);
} else {
_endDate = picked;
}
}
}
});
@ -80,16 +115,24 @@ class _MineDutyApplicationPage extends State<MineDutyApplicationPage> {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('申请提交成功'),
content: const Text('的离岗申请已成功提交,请等待审批'),
title: const Text('申请提交'),
content: const Text('确定提交离岗申请吗?'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context); //
Navigator.pop(context); //
},
child: const Text("取消"),
),
TextButton(
onPressed: () {
Navigator.pop(context); //
// Navigator.pop(context); //
_submitApplicationLeaving();
},
child: const Text("确定"),
)
),
],
),
);
@ -161,7 +204,8 @@ class _MineDutyApplicationPage extends State<MineDutyApplicationPage> {
const SizedBox(height: 24),
//
_buildInfoRow("申请人", _applicant),
// _buildInfoRow("申请人", _applicant),
_buildInfoRow("申请人", SessionService.instance.username.toString()),
const SizedBox(height: 40),

View File

@ -3,19 +3,19 @@ import 'package:intl/intl.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
///
class MineDutyDetailPage extends StatefulWidget {
const MineDutyDetailPage({super.key});
const MineDutyDetailPage(this.item, {super.key});
final item;
@override
State<MineDutyDetailPage> createState() => _MineDutyDetailPage();
}
class _MineDutyDetailPage extends State<MineDutyDetailPage> {
DateTime? _startDate;
DateTime? _endDate;
final TextEditingController _reasonController = TextEditingController();
final String _applicant = "王轩";
@ -32,13 +32,13 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
//
_buildInfoRow("申请人", _applicant),
_buildInfoRow("申请人", widget.item["USER_NAME"]),
const SizedBox(height: 24),
//
_buildDateField(
label: "离岗开始时间",
date: _startDate,
date: widget.item["STARTTIME"],
onTap: () {
}
@ -50,7 +50,7 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
//
_buildDateField(
label: "离岗结束时间",
date: _endDate,
date: widget.item["ENDTIME"],
onTap: () {
},
@ -83,7 +83,8 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
],
),
child: Text(
"原因",style: TextStyle(color: Colors.black),
widget.item["DESCR"],
style: TextStyle(color: Colors.black),
// controller: _reasonController,
// maxLines: 5,
// decoration: const InputDecoration(
@ -96,8 +97,10 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
const SizedBox(height: 24),
//
_buildInfoRow("审批状态", "无需审批"),
_buildInfoRow("审批状态", _getTypeReturn(widget.item)),
const SizedBox(height: 40),
@ -131,7 +134,7 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
Widget _buildDateField({
required String label,
required DateTime? date,
required String? date,
required VoidCallback onTap,
}) {
return GestureDetector(
@ -158,10 +161,11 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
style: const TextStyle(fontSize: 16),
),
Text(
date != null ? DateFormat('yyyy-MM-dd').format(date) : "请选择日期",
// date != null ? DateFormat('yyyy-MM-dd').format(date) : "请选择日期",
date! ,
style: TextStyle(
fontSize: 16,
color: date != null ? Colors.black : const Color(0xFF9E9E9E),
color: Colors.black ,
),
),
],
@ -200,4 +204,29 @@ class _MineDutyDetailPage extends State<MineDutyDetailPage> {
),
);
}
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 "审批错误";
}
}
}

View File

@ -27,6 +27,7 @@ class LeaveRecord {
});
}
///
class MineDutyManagementPage extends StatefulWidget {
const MineDutyManagementPage({super.key});
@ -38,6 +39,8 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
int showCount=-1;
int currentPage=1;
late List<dynamic> _list = [];
Future<void> _onRefresh() async {
//
@ -45,6 +48,7 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
// fetchData()
setState(() {
// TODO:
refreshData();
});
}
@ -54,56 +58,74 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
}
void refreshData(){
currentPage=1;
_list.clear();
_getListData();
}
@override
Widget build(BuildContext context) {
//
final leaveRecords = [
LeaveRecord(
applicant: "王轩",
department: "测试部",
position: "测试员",
startDate: DateTime(2025, 7, 18),
endDate: DateTime(2025, 7, 18),
status: "无需审批",
statusColor: const Color(0xFF4CAF50),
),
LeaveRecord(
applicant: "王轩",
department: "测试部",
position: "测试员",
startDate: DateTime(2025, 7, 16),
endDate: DateTime(2025, 7, 16),
status: "无需审批",
statusColor: const Color(0xFF4CAF50),
),
LeaveRecord(
applicant: "李思",
department: "开发部",
position: "高级工程师",
startDate: DateTime(2025, 7, 20),
endDate: DateTime(2025, 7, 22),
status: "待审批",
statusColor: const Color(0xFFF57C00),
),
LeaveRecord(
applicant: "张伟",
department: "产品部",
position: "产品经理",
startDate: DateTime(2025, 7, 15),
endDate: DateTime(2025, 7, 17),
status: "已拒绝",
statusColor: const Color(0xFFF44336),
),
];
// final leaveRecords = [
// LeaveRecord(
// applicant: "王轩",
// department: "测试部",
// position: "测试员",
// startDate: DateTime(2025, 7, 18),
// endDate: DateTime(2025, 7, 18),
// status: "无需审批",
// statusColor: const Color(0xFF4CAF50),
// ),
// LeaveRecord(
// applicant: "王轩",
// department: "测试部",
// position: "测试员",
// startDate: DateTime(2025, 7, 16),
// endDate: DateTime(2025, 7, 16),
// status: "无需审批",
// statusColor: const Color(0xFF4CAF50),
// ),
// LeaveRecord(
// applicant: "李思",
// department: "开发部",
// position: "高级工程师",
// startDate: DateTime(2025, 7, 20),
// endDate: DateTime(2025, 7, 22),
// status: "待审批",
// statusColor: const Color(0xFFF57C00),
// ),
// LeaveRecord(
// applicant: "张伟",
// department: "产品部",
// position: "产品经理",
// startDate: DateTime(2025, 7, 15),
// endDate: DateTime(2025, 7, 17),
// status: "已拒绝",
// statusColor: const Color(0xFFF44336),
// ),
// ];
return Scaffold(
backgroundColor: const Color(0xFFF5F7FA),
appBar: MyAppbar(title: "离岗管理",actions: [
TextButton(
onPressed: () {
pushPage(MineDutyApplicationPage(), context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MineDutyApplicationPage(
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
),
);
// pushPage(MineDutyApplicationPage(), context);
},
child: Text("申请",style: TextStyle(color: Colors.white,fontSize: 16,fontWeight:FontWeight.bold),))
],),
@ -131,11 +153,14 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
//
Expanded(
child: ListView.builder(
child:
_list.isEmpty
? Center(child: Text('暂无数据'))
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: leaveRecords.length,
itemCount: _list.length,
itemBuilder: (context, index) {
final record = leaveRecords[index];
final record = _list[index];
return _buildRecordCard(record,context);
},
),
@ -146,19 +171,20 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
);
}
Widget _buildRecordCard(LeaveRecord record, 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)}";
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']}";
return
// GestureDetector(
// onTap: () {
// pushPage(MineDutyDetailPage(), context);
// },
// child:
GestureDetector(
onTap: () {
pushPage(MineDutyDetailPage(item), context);
},
child:
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
@ -195,7 +221,7 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"申请人:${record.applicant}",
"申请人:${item['USER_NAME']}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
@ -203,77 +229,114 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
),
const SizedBox(height: 4),
Text(
"部门:${record.department} \n岗位:${record.position}",
"部门:${item['DEPARTMENTNAME']} \n岗位:${item['POSTNAME']}",
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
),
),
],
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: record.statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
),
child: Text(
record.status,
style: TextStyle(
color: record.statusColor,
fontWeight: FontWeight.w500,
),
),
),
// const Spacer(),
],
),
const SizedBox(height: 16),
const SizedBox(height: 8),
//
Row(
children: [
const Icon(Icons.calendar_today, size: 18, color: Colors.grey),
const SizedBox(width: 8),
Text(
"离岗时间: $dateRange",
style: const TextStyle(color: Colors.grey),
Expanded(
child: Text(
"离岗时间: $dateRange",
style: const TextStyle(color: Colors.grey),
),
),
],
),
const SizedBox(height: 8),
const SizedBox(height: 4),
// Container(
// padding: const EdgeInsets.symmetric( vertical: 6),
// // decoration: BoxDecoration(
// // color: record.statusColor.withOpacity(0.1),
// // borderRadius: BorderRadius.circular(20),
// // ),
// child:
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: [
TextButton(
ElevatedButton(
onPressed: () {
pushPage(MineDutyDetailPage(), context);
showDialog(
context: context,
builder: (context) => DutyDialog(
item,1,
onClose: (result) {
// print('详情页面已关闭,返回结果: $result');
refreshData();
},
),
);
},
style: TextButton.styleFrom(
foregroundColor: const Color(0xFF1976D2),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1976D2),
// padding: const EdgeInsets.symmetric(horizontal: 10),
),
child: const Text("查看详情"),
child: const Text("审 批", style: TextStyle(color: Colors.white,fontSize: 12)),
),
// const SizedBox(width: 16),
// if (record.status == "待审批")
// ElevatedButton(
// onPressed: () {},
// style: ElevatedButton.styleFrom(
// backgroundColor: const Color(0xFF1976D2),
// padding: const EdgeInsets.symmetric(horizontal: 20),
// ),
// child: const Text("审批", style: TextStyle(color: Colors.white)),
// )
const SizedBox(width: 16),
ElevatedButton(
onPressed: () {
showDialog(
context: context,
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)),
)
],
),
],
),
),
// ),
),
);
}
@ -281,16 +344,229 @@ class _MineDutyManagementPageState extends State<MineDutyManagementPage> {
Future<void> _getListData() async {
try {
final raw = await ApiService.getDutyManagement(showCount,currentPage);
final result = await ApiService.getDutyManagement(showCount,currentPage);
if (result['result'] == 'success') {
final List<dynamic> newList = result['varList'] ?? [];
setState(() {
_list.addAll(newList);
});
}
} catch (e) {
// Toast
print('加载首页数据失败:$e');
print('加载出错: $e');
}
}
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 "审批错误";
}
}
}
enum FeedbackType {
tongGuo,
Dahui,
}
class DutyDialog extends StatefulWidget {
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;
//
String _getTypeName(FeedbackType type) {
switch (type) {
case FeedbackType.tongGuo:
return '通过';
case FeedbackType.Dahui:
return '打回';
}
}
Future<void> _dutyApproval() async {
try {
String typeString;
if(FeedbackType.tongGuo==_selectedType){
typeString="1";
}else{
typeString="-1";
}
final result = await ApiService.dutyApproval(typeString,_reasonController.text,widget.item["OFFDUTY_ID"]);
if (result['result'] == 'success') {
widget.onClose('关闭提交'); //
}
} catch (e) {
print('加载出错: $e');
}
}
Future<void> _dutyReturned() async {
try {
final result = await ApiService.dutyReturned("-1",_reasonController.text,widget.item["OFFDUTY_ID"]);
if (result['result'] == 'success') {
widget.onClose('关闭提交'); //
}
} catch (e) {
print('加载出错: $e');
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
const Center(
child: Text(
'离岗审批',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 20),
//
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// _buildActionButton('通过', Colors.green),
// _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(),
),
),
const SizedBox(height: 20),
//
TextField(
controller: _reasonController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: widget.type==1?'请输入审批意见':'请输入原因',
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
),
maxLines: 4,
),
const SizedBox(height: 20),
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: ElevatedButton(
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);
} ,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('提交',style: TextStyle(color: Colors.white),),
),
),
const SizedBox(width: 15),
Expanded(
child: ElevatedButton(
onPressed: (){
Navigator.pop(context);
} ,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('关闭',style: TextStyle(color: Colors.white),),
),
),
],
),
],
),
),
);
}
}

View File

@ -16,6 +16,7 @@ enum FeedbackType {
other,
}
///
class FeedbackApp extends StatelessWidget {
const FeedbackApp({super.key});
@ -185,8 +186,11 @@ class _FeedbackPageState extends State<FeedbackPage> {
mediaType: MediaType.image,
onChanged: (files) {
// files
},
onAiIdentify: () {
},
onAiIdentify: () {},
),
// GridView.builder(
@ -304,10 +308,12 @@ class _FeedbackPageState extends State<FeedbackPage> {
String imagePaths="";
for(int i=0;i<_images.length;i++){
String imagePath=_reloadFeedBack(_images[i]) as String;
if(0==i){
imagePaths=_images[i];
imagePaths=imagePath;
}else {
imagePaths = "$imagePaths,${_images[i]}";
imagePaths = "$imagePaths;$imagePath";
}
}
@ -339,6 +345,23 @@ class _FeedbackPageState extends State<FeedbackPage> {
// }
}
Future<String> _reloadFeedBack(String imagePath) async {
try {
final raw = await ApiService.reloadFeedBack(imagePath);
if (raw['result'] == 'success') {
return raw['imgPath'];
}else{
// _showMessage('反馈提交失败');
return "";
}
} catch (e) {
// Toast
print('加载首页数据失败:$e');
return "";
}
}
Future<void> _setFeedBack(String title, String text, String imagePaths, String num) async {
try {

View File

@ -14,7 +14,7 @@ packages:
description:
name: asn1lib
sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.6.5"
async:
@ -38,7 +38,7 @@ packages:
description:
name: camera
sha256: d6ec2cbdbe2fa8f5e0d07d8c06368fe4effa985a4a5ddade9cc58a8cd849557d
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.2"
camera_android_camerax:
@ -46,7 +46,7 @@ packages:
description:
name: camera_android_camerax
sha256: "4b6c1bef4270c39df96402c4d62f2348c3bb2bbaefd0883b9dbd58f426306ad0"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.19"
camera_avfoundation:
@ -54,7 +54,7 @@ packages:
description:
name: camera_avfoundation
sha256: "9e02b36c9c09a01edcb0f2bfc58a94ed38bbbf37907759d651707bb0f327a365"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.20+3"
camera_platform_interface:
@ -62,7 +62,7 @@ packages:
description:
name: camera_platform_interface
sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.10.0"
camera_web:
@ -70,7 +70,7 @@ packages:
description:
name: camera_web
sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.5"
characters:
@ -118,7 +118,7 @@ packages:
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.2"
cross_file:
@ -166,7 +166,7 @@ packages:
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.8.0+1"
dio_web_adapter:
@ -174,7 +174,7 @@ packages:
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
encrypt:
@ -182,7 +182,7 @@ packages:
description:
name: encrypt
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.3"
extended_image:
@ -301,7 +301,7 @@ packages:
description:
name: fluttertoast
sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.2.12"
html:
@ -637,7 +637,7 @@ packages:
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.9.1"
provider:
@ -746,7 +746,7 @@ packages:
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
string_scanner: