feat(approval): 员工端待办/已办与通过驳回撤回 (#20)
- 审批中心三个 tab 接入待办/已办接口,支持状态+关键词筛选与下拉刷新 - 详情页新增通过(意见可选)、驳回(意见必填)、撤回(二次确认) - 驳回/撤回后提供修改并重新提交入口,按钮按 permissions 显隐
This commit is contained in:
@@ -109,13 +109,35 @@
|
||||
<view v-if="detail.records.length === 0" class="empty">暂无记录</view>
|
||||
</uni-card>
|
||||
|
||||
<view v-if="detail.permissions.canEdit || detail.permissions.canSubmit" class="actions">
|
||||
<view v-if="hasActions" class="actions">
|
||||
<button
|
||||
v-if="detail.permissions.canEdit"
|
||||
class="action-button ghost"
|
||||
@click="editDraft"
|
||||
v-if="detail.myTask && detail.permissions.canApprove"
|
||||
class="action-button primary"
|
||||
@click="openApprove"
|
||||
>
|
||||
编辑
|
||||
通过
|
||||
</button>
|
||||
<button
|
||||
v-if="detail.myTask && detail.permissions.canReject"
|
||||
class="action-button danger"
|
||||
@click="openReject"
|
||||
>
|
||||
驳回
|
||||
</button>
|
||||
<button
|
||||
v-if="detail.permissions.canWithdraw"
|
||||
class="action-button ghost"
|
||||
:loading="submitting"
|
||||
@click="withdraw"
|
||||
>
|
||||
撤回
|
||||
</button>
|
||||
<button
|
||||
v-if="showEditButton"
|
||||
class="action-button ghost"
|
||||
@click="editAndResubmit"
|
||||
>
|
||||
{{ editLabel }}
|
||||
</button>
|
||||
<button
|
||||
v-if="detail.permissions.canSubmit"
|
||||
@@ -123,9 +145,27 @@
|
||||
:loading="submitting"
|
||||
@click="submitDraft"
|
||||
>
|
||||
提交
|
||||
{{ detail.status === 'DRAFT' ? '提交' : '重新提交' }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view v-if="actionDialog" class="dialog-mask">
|
||||
<view class="dialog">
|
||||
<text class="dialog-title">{{ actionDialog === 'approve' ? '审批意见' : '驳回意见' }}</text>
|
||||
<textarea
|
||||
v-model="opinion"
|
||||
class="dialog-input"
|
||||
:maxlength="500"
|
||||
:placeholder="actionDialog === 'approve' ? '请输入意见(可选)' : '请输入驳回意见(必填)'"
|
||||
/>
|
||||
<view class="dialog-actions">
|
||||
<button class="dialog-button ghost" @click="closeDialog">取消</button>
|
||||
<button class="dialog-button primary" :loading="submitting" @click="confirmAction">
|
||||
确定
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="loading">
|
||||
<text>{{ loading ? '加载中...' : '单据不存在' }}</text>
|
||||
@@ -134,9 +174,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { getInstance, submitInstance } from '@/api/approval/instance';
|
||||
import {
|
||||
approveTask,
|
||||
rejectTask,
|
||||
withdrawInstance
|
||||
} from '@/api/approval/task';
|
||||
import type { ApprovalInstance } from '@/api/approval/model';
|
||||
import { showError } from '@/utils/app';
|
||||
|
||||
@@ -144,6 +189,28 @@ const detail = ref<ApprovalInstance | null>(null);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const instanceId = ref(0);
|
||||
const actionDialog = ref<'' | 'approve' | 'reject'>('');
|
||||
const opinion = ref('');
|
||||
|
||||
const hasActions = computed(() => {
|
||||
const permissions = detail.value?.permissions;
|
||||
if (!permissions) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
permissions.canApprove ||
|
||||
permissions.canReject ||
|
||||
permissions.canWithdraw ||
|
||||
permissions.canSubmit ||
|
||||
permissions.canEdit
|
||||
);
|
||||
});
|
||||
const showEditButton = computed(() => !!detail.value?.permissions.canEdit);
|
||||
const editLabel = computed(() =>
|
||||
detail.value?.status === 'REJECTED' || detail.value?.status === 'WITHDRAWN'
|
||||
? '修改并重新提交'
|
||||
: '编辑'
|
||||
);
|
||||
|
||||
onLoad(options => {
|
||||
instanceId.value = Number(options?.id) || 0;
|
||||
@@ -164,13 +231,85 @@ async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
function editDraft() {
|
||||
function editAndResubmit() {
|
||||
if (detail.value?.docType !== 'completion') {
|
||||
showError('该单据类型暂不支持在线修改', null);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/packageApproval/pages/form-completion?mode=edit&id=' + instanceId.value
|
||||
});
|
||||
}
|
||||
|
||||
function openApprove() {
|
||||
opinion.value = '';
|
||||
actionDialog.value = 'approve';
|
||||
}
|
||||
|
||||
function openReject() {
|
||||
opinion.value = '';
|
||||
actionDialog.value = 'reject';
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
if (submitting.value) {
|
||||
return;
|
||||
}
|
||||
actionDialog.value = '';
|
||||
}
|
||||
|
||||
async function confirmAction() {
|
||||
const taskId = detail.value?.myTask?.id;
|
||||
if (!taskId) {
|
||||
showError('任务已处理,请刷新后重试', null);
|
||||
return;
|
||||
}
|
||||
const isApprove = actionDialog.value === 'approve';
|
||||
if (!isApprove && !opinion.value.trim()) {
|
||||
uni.showToast({ title: '请填写驳回意见', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
const response = isApprove
|
||||
? await approveTask(taskId, opinion.value.trim() || undefined)
|
||||
: await rejectTask(taskId, opinion.value.trim());
|
||||
actionDialog.value = '';
|
||||
uni.showToast({
|
||||
title: response.message || (isApprove ? '已通过' : '已驳回'),
|
||||
icon: 'none'
|
||||
});
|
||||
await load();
|
||||
} catch (error: any) {
|
||||
showError(error?.message || '操作失败', null);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function withdraw() {
|
||||
uni.showModal({
|
||||
title: '撤回确认',
|
||||
content: '撤回后单据回到报送人,可修改后重新提交。确认撤回?',
|
||||
success: async result => {
|
||||
if (!result.confirm) {
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
const response = await withdrawInstance(instanceId.value);
|
||||
uni.showToast({ title: response.message || '撤回成功', icon: 'none' });
|
||||
await load();
|
||||
} catch (error: any) {
|
||||
showError(error?.message || '撤回失败', null);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitDraft() {
|
||||
uni.showModal({
|
||||
title: '提交确认',
|
||||
@@ -411,12 +550,14 @@ function formatSize(value?: number) {
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx 32rpx 48rpx;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
flex: 1;
|
||||
min-width: 200rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
@@ -432,6 +573,74 @@ function formatSize(value?: number) {
|
||||
border: 1rpx solid #dcdfe6;
|
||||
}
|
||||
|
||||
.action-button.danger {
|
||||
background: #ffffff;
|
||||
color: #cf1322;
|
||||
border: 1rpx solid #cf1322;
|
||||
}
|
||||
|
||||
.dialog-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
width: 600rpx;
|
||||
padding: 32rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.dialog-input {
|
||||
width: 100%;
|
||||
height: 180rpx;
|
||||
margin-top: 20rpx;
|
||||
padding: 16rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 26rpx;
|
||||
background: #f5f6f8;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.dialog-button {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.dialog-button.primary {
|
||||
background: #d81e06;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.dialog-button.ghost {
|
||||
background: #ffffff;
|
||||
color: #303133;
|
||||
border: 1rpx solid #dcdfe6;
|
||||
}
|
||||
|
||||
.loading {
|
||||
padding: 160rpx 0;
|
||||
text-align: center;
|
||||
|
||||
Reference in New Issue
Block a user