增强办公,新增审批,缺少电子签章功能
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<view class="page-nav">
|
||||
<view class="nav-tabs nav-tabs--top">
|
||||
<view class="nav-tab" :class="{ active: activeTopTab === 'approval' }" @click="switchTopTab('approval')">我的审批</view>
|
||||
<view class="nav-tab" :class="{ active: activeTopTab === 'myApply' }" @click="switchTopTab('myApply')">我的申请</view>
|
||||
<view class="nav-tab" :class="{ active: activeTopTab === 'apply' }" @click="switchTopTab('apply')">发起申请</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -60,6 +61,52 @@
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view v-else-if="activeTopTab === 'myApply'" class="tab-panel lower-panel my-apply-panel">
|
||||
<view class="sub-tabs sub-tabs--underline">
|
||||
<view class="sub-tab" :class="{ active: myApplyStatusTab === 'all' }" @click="switchMyApplyStatusTab('all')">全部</view>
|
||||
<view class="sub-tab" :class="{ active: myApplyStatusTab === 'running' }" @click="switchMyApplyStatusTab('running')">审批中</view>
|
||||
<view class="sub-tab" :class="{ active: myApplyStatusTab === 'approved' }" @click="switchMyApplyStatusTab('approved')">已通过</view>
|
||||
<view class="sub-tab" :class="{ active: myApplyStatusTab === 'rejected' }" @click="switchMyApplyStatusTab('rejected')">已驳回</view>
|
||||
</view>
|
||||
|
||||
<view class="type-row">
|
||||
<scroll-view class="type-scroll" scroll-x show-scrollbar="false">
|
||||
<view class="type-tabs">
|
||||
<view class="type-tab" :class="{ active: myApplyBizTypeIndex === 0 }" @click="setMyApplyBizType(0)">全部</view>
|
||||
<view class="type-tab" v-for="(item, index) in bizTypeList.slice(1)" :key="item.value" :class="{ active: myApplyBizTypeIndex === index + 1 }" @click="setMyApplyBizType(index + 1)">{{ item.label }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="refresh-row" @click="refreshCurrentList">
|
||||
<uni-icons type="refreshempty" size="18" color="#1677ff"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="approval-list" scroll-y>
|
||||
<view v-if="loading" class="loading-container">
|
||||
<uni-load-more type="loading" color="#409EFF"></uni-load-more>
|
||||
</view>
|
||||
<view v-else-if="myApplyList.length === 0" class="empty-container">
|
||||
<uni-icons type="empty" size="60" color="#909399"></uni-icons>
|
||||
<view class="empty-text">暂无我的申请</view>
|
||||
</view>
|
||||
<view v-else class="list-item" v-for="(item, index) in myApplyList" :key="item.instId || item.id || index" @click="goDetail(item)">
|
||||
<view class="item-tag" :class="getBizTypeTagType(item.bizType)">{{ getBizTypeText(item.bizType) }}</view>
|
||||
<view class="item-main">
|
||||
<view class="applicant">
|
||||
<uni-icons type="paperplane" size="14" color="#8a8f99"></uni-icons>
|
||||
{{ formatMyApplyTitle(item) }}
|
||||
</view>
|
||||
<view class="request-info">{{ formatMyApplyInfo(item) }}</view>
|
||||
<view class="time-info">{{ formatDate(item.createTime || item.applyTime || item.startTime) }}</view>
|
||||
</view>
|
||||
<view class="right-section">
|
||||
<view class="status-tag" :class="statusType(item.status)">{{ myApplyStatusText(item) }}</view>
|
||||
<view v-if="item.bizType === 'travel' && (item.status === 'approved' || item.status === 'finished') && !item.actualEndTime && !item.endTime && !item.realEndTime" class="early-end-btn" @click.stop="goTravelEarlyEnd(item)">提前结束</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view v-else class="tab-panel apply-panel lower-panel">
|
||||
|
||||
<view class="apply-list">
|
||||
@@ -78,6 +125,7 @@
|
||||
<script>
|
||||
import { getUserProfile } from '@/api/oa/user.js';
|
||||
import { approveFlowTask, listMyFlowInstance, listTodoFlowTask, listDoneFlowTask, rejectFlowTask } from '@/api/hrm/flow';
|
||||
import { earlyEndTravelReq } from '@/api/hrm/travel';
|
||||
|
||||
export default {
|
||||
name: 'HrmApproval',
|
||||
@@ -89,14 +137,19 @@ export default {
|
||||
todoList: [],
|
||||
doneList: [],
|
||||
ccList: [],
|
||||
myApplyList: [],
|
||||
summary: {
|
||||
todo: 0,
|
||||
done: 0,
|
||||
cc: 0
|
||||
cc: 0,
|
||||
myApply: 0
|
||||
},
|
||||
query: {
|
||||
bizType: ''
|
||||
},
|
||||
myApplyQuery: {
|
||||
bizType: ''
|
||||
},
|
||||
bizTypeList: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '请假', value: 'leave' },
|
||||
@@ -106,6 +159,8 @@ export default {
|
||||
{ label: '拨款', value: 'appropriation' }
|
||||
],
|
||||
bizTypeIndex: 0,
|
||||
myApplyBizTypeIndex: 0,
|
||||
myApplyStatusTab: 'all',
|
||||
currentOaUserId: '',
|
||||
roleGroup: [],
|
||||
applyTypes: [
|
||||
@@ -124,11 +179,13 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
currentList() {
|
||||
if (this.activeTopTab === 'myApply') return this.myApplyList;
|
||||
if (this.activeApprovalTab === 'todo') return this.todoList;
|
||||
if (this.activeApprovalTab === 'done') return this.doneList;
|
||||
return this.ccList;
|
||||
},
|
||||
emptyText() {
|
||||
if (this.activeTopTab === 'myApply') return '暂无我的申请';
|
||||
const map = {
|
||||
todo: '暂无待处理审批',
|
||||
done: '暂无已处理记录',
|
||||
@@ -149,6 +206,7 @@ export default {
|
||||
this.$store.commit('oa/SET_STATE', user);
|
||||
this.currentOaUserId = user.userId || '';
|
||||
this.roleGroup = roles;
|
||||
this.myApplyBizTypeIndex = 0;
|
||||
return user;
|
||||
} catch (error) {
|
||||
console.error('获取用户个人信息失败:', error);
|
||||
@@ -160,6 +218,10 @@ export default {
|
||||
this.activeTopTab = tab;
|
||||
if (tab === 'approval') {
|
||||
this.refreshCurrentList();
|
||||
return;
|
||||
}
|
||||
if (tab === 'myApply') {
|
||||
this.refreshCurrentList();
|
||||
}
|
||||
},
|
||||
switchApprovalTab(tab) {
|
||||
@@ -167,6 +229,10 @@ export default {
|
||||
this.refreshCurrentList();
|
||||
},
|
||||
refreshCurrentList() {
|
||||
if (this.activeTopTab === 'myApply') {
|
||||
this.loadMyApplyList();
|
||||
return;
|
||||
}
|
||||
if (this.activeTopTab !== 'approval') return;
|
||||
if (this.activeApprovalTab === 'todo') {
|
||||
this.loadTodoList();
|
||||
@@ -234,6 +300,35 @@ export default {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async loadMyApplyList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await listMyFlowInstance({ pageNum: 1, pageSize: 200 });
|
||||
let list = res.rows || res.data || [];
|
||||
if (this.myApplyQuery.bizType) list = list.filter(item => item.bizType === this.myApplyQuery.bizType);
|
||||
if (this.myApplyStatusTab !== 'all') {
|
||||
list = list.filter(item => this.matchMyApplyStatus(item.status));
|
||||
}
|
||||
this.myApplyList = list;
|
||||
this.summary.myApply = list.length;
|
||||
} catch (err) {
|
||||
console.error('加载我的申请失败:', err);
|
||||
this.myApplyList = [];
|
||||
this.summary.myApply = 0;
|
||||
uni.showToast({ title: '加载我的申请失败', icon: 'none' });
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
switchMyApplyStatusTab(tab) {
|
||||
this.myApplyStatusTab = tab;
|
||||
this.refreshCurrentList();
|
||||
},
|
||||
setMyApplyBizType(index) {
|
||||
this.myApplyQuery.bizType = this.bizTypeList[index].value;
|
||||
this.myApplyBizTypeIndex = index;
|
||||
this.refreshCurrentList();
|
||||
},
|
||||
getBizTypeText(type) {
|
||||
const map = { leave: '请假', travel: '出差', seal: '用印', reimburse: '报销', appropriation: '拨款' };
|
||||
return map[type] || type || '-';
|
||||
@@ -242,10 +337,36 @@ export default {
|
||||
const map = { leave: 'primary', travel: 'success', seal: 'warning', reimburse: 'danger', appropriation: 'danger' };
|
||||
return map[type] || 'info';
|
||||
},
|
||||
formatMyApplyTitle(item) {
|
||||
return item?.bizTitle || item?.title || this.getBizTypeText(item?.bizType) || '我的申请';
|
||||
},
|
||||
formatMyApplyInfo(item) {
|
||||
if (!item) return '-';
|
||||
if (item.bizType === 'leave') return item.subTitle || item.leaveType || '请假申请';
|
||||
if (item.bizType === 'travel') return item.subTitle || item.destination || '出差申请';
|
||||
if (item.bizType === 'seal') return item.subTitle || '用印申请';
|
||||
if (item.bizType === 'reimburse') return item.subTitle || '报销申请';
|
||||
if (item.bizType === 'appropriation') return item.subTitle || '拨款申请';
|
||||
return item.subTitle || item.bizTitle || '-';
|
||||
},
|
||||
matchMyApplyStatus(status) {
|
||||
const s = String(status || '').toLowerCase();
|
||||
if (this.myApplyStatusTab === 'running') return ['pending', 'running'].includes(s);
|
||||
if (this.myApplyStatusTab === 'approved') return ['approved', 'finished'].includes(s);
|
||||
if (this.myApplyStatusTab === 'rejected') return ['rejected', 'reject'].includes(s);
|
||||
return true;
|
||||
},
|
||||
statusText(status) {
|
||||
const map = { pending: '待审批', draft: '草稿', approved: '已通过', rejected: '已驳回', running: '审批中', finished: '已完成', revoked: '已撤销' };
|
||||
return map[status] || status || '-';
|
||||
},
|
||||
myApplyStatusText(item) {
|
||||
if (item.bizType === 'travel' && (item.status === 'approved' || item.status === 'finished')) {
|
||||
const endTime = item.actualEndTime || item.endTime || item.realEndTime;
|
||||
return endTime ? '已结束' : '出差中';
|
||||
}
|
||||
return this.statusText(item.status);
|
||||
},
|
||||
statusType(status) {
|
||||
const map = { pending: 'warning', running: 'warning', draft: 'info', approved: 'success', rejected: 'danger', finished: 'success', revoked: 'danger' };
|
||||
return map[status] || 'info';
|
||||
@@ -332,11 +453,40 @@ export default {
|
||||
this.refreshCurrentList();
|
||||
});
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this.goTravelEarlyEnd(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
goTravelEarlyEnd(task) {
|
||||
if (!task || task.bizType !== 'travel') {
|
||||
uni.showToast({ title: '仅出差申请支持提前结束', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const bizId = task.bizId || task.instId;
|
||||
if (!bizId) {
|
||||
uni.showToast({ title: '缺少出差单编号', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
uni.showModal({
|
||||
title: '提前结束出差',
|
||||
content: '确定要提前结束该出差吗?',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return;
|
||||
earlyEndTravelReq(bizId)
|
||||
.then(() => {
|
||||
uni.showToast({ title: '提前结束成功', icon: 'none' });
|
||||
this.refreshCurrentList();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('提前结束失败:', err);
|
||||
uni.showToast({ title: '提前结束失败', icon: 'none' });
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -582,6 +732,25 @@ export default {
|
||||
.request-info { font-size: 26rpx; color: #6b7280; }
|
||||
.time-info { font-size: 24rpx; color: #9ca3af; }
|
||||
|
||||
.early-end-btn {
|
||||
flex: 0 0 auto;
|
||||
padding: 6rpx 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: #1677ff;
|
||||
background: #eef4ff;
|
||||
border-radius: 6rpx;
|
||||
border: 1rpx solid #1677ff;
|
||||
}
|
||||
|
||||
.right-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
padding-right: 8rpx;
|
||||
}
|
||||
|
||||
.apply-panel {
|
||||
padding-top: 16rpx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user