merge: t24 待办派单/转办
This commit is contained in:
@@ -173,3 +173,15 @@ export interface ApprovalOrganization {
|
||||
organizationCode?: string;
|
||||
leaderId?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批目录用户(派单候选池)
|
||||
*/
|
||||
export interface ApprovalUser {
|
||||
userId: number;
|
||||
username?: string;
|
||||
nickname?: string;
|
||||
realName?: string;
|
||||
organizationId?: number;
|
||||
organizationName?: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request';
|
||||
import type { ApiResult } from '@/api';
|
||||
import type { ApprovalInstance } from '../model';
|
||||
import type { ApprovalInstance, ApprovalUser } from '../model';
|
||||
import { THINK_API_URL } from '@/config/setting';
|
||||
|
||||
/**
|
||||
@@ -70,6 +70,33 @@ export async function archiveTask(id: number) {
|
||||
return Promise.reject(new Error(res.data.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 派单候选池(仅派单节点、本人待办)
|
||||
*/
|
||||
export async function listDispatchCandidates(id: number) {
|
||||
const res = await request.get<ApiResult<ApprovalUser[]>>(
|
||||
THINK_API_URL + '/approval/task/' + id + '/candidates'
|
||||
);
|
||||
if (res.data.code === 0 && res.data.data) {
|
||||
return res.data.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 派单(选定 1 人即完成派单节点)
|
||||
*/
|
||||
export async function dispatchTask(id: number, targetUserId: number) {
|
||||
const res = await request.post<ApiResult<ApprovalInstance>>(
|
||||
THINK_API_URL + '/approval/task/' + id + '/dispatch',
|
||||
{ targetUserId }
|
||||
);
|
||||
if (res.data.code === 0 && res.data.data) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回
|
||||
*/
|
||||
|
||||
@@ -146,6 +146,13 @@
|
||||
>
|
||||
通过
|
||||
</button>
|
||||
<button
|
||||
v-if="detail.myTask && detail.permissions.canDispatch"
|
||||
class="action-button primary"
|
||||
@click="openDispatch"
|
||||
>
|
||||
派单
|
||||
</button>
|
||||
<button
|
||||
v-if="detail.myTask && detail.permissions.canReject"
|
||||
class="action-button danger"
|
||||
@@ -220,7 +227,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { getInstance, submitInstance } from '@/api/approval/instance';
|
||||
import {
|
||||
approveTask,
|
||||
@@ -247,6 +254,7 @@ const hasActions = computed(() => {
|
||||
return (
|
||||
permissions.canApprove ||
|
||||
permissions.canReject ||
|
||||
permissions.canDispatch ||
|
||||
permissions.canConfirm ||
|
||||
permissions.canArchive ||
|
||||
permissions.canWithdraw ||
|
||||
@@ -261,11 +269,22 @@ const editLabel = computed(() =>
|
||||
: '编辑'
|
||||
);
|
||||
|
||||
// 首次进入由 onLoad 加载;从派单/编辑页返回时重新拉取,避免展示过期节点
|
||||
let entered = false;
|
||||
|
||||
onLoad(options => {
|
||||
instanceId.value = Number(options?.id) || 0;
|
||||
load();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
if (!entered) {
|
||||
entered = true;
|
||||
return;
|
||||
}
|
||||
load();
|
||||
});
|
||||
|
||||
async function load() {
|
||||
if (!instanceId.value) {
|
||||
return;
|
||||
@@ -302,6 +321,17 @@ function openApprove() {
|
||||
actionDialog.value = 'approve';
|
||||
}
|
||||
|
||||
function openDispatch() {
|
||||
const taskId = detail.value?.myTask?.id;
|
||||
if (!taskId) {
|
||||
showError('任务已处理,请刷新后重试', null);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/packageApproval/pages/dispatch?taskId=' + taskId
|
||||
});
|
||||
}
|
||||
|
||||
function openReject() {
|
||||
opinion.value = '';
|
||||
actionDialog.value = 'reject';
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<view class="dispatch-page">
|
||||
<uni-card :border="false" :padding="'16px'" title="选择派单对象">
|
||||
<text class="hint">
|
||||
派单后由报送人与被派单人分别存档,请从受理部门启用成员中选择 1 人。
|
||||
</text>
|
||||
|
||||
<view
|
||||
v-for="item in candidates"
|
||||
:key="item.userId"
|
||||
class="candidate"
|
||||
:class="{ active: selectedId === item.userId }"
|
||||
@click="select(item)"
|
||||
>
|
||||
<view class="candidate-main">
|
||||
<text class="candidate-name">{{ displayName(item) }}</text>
|
||||
<text class="candidate-meta">
|
||||
{{ item.username }}
|
||||
<text v-if="item.organizationName"> · {{ item.organizationName }}</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="candidate-radio" :class="{ checked: selectedId === item.userId }">
|
||||
<view v-if="selectedId === item.userId" class="candidate-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="candidates.length === 0" class="empty">
|
||||
暂无可派单人员,请联系管理员维护受理部门成员
|
||||
</view>
|
||||
</uni-card>
|
||||
|
||||
<view class="footer">
|
||||
<button
|
||||
class="footer-button primary"
|
||||
:loading="submitting"
|
||||
:disabled="!selectedId || submitting"
|
||||
@click="confirmDispatch"
|
||||
>
|
||||
确认派单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { dispatchTask, listDispatchCandidates } from '@/api/approval/task';
|
||||
import type { ApprovalUser } from '@/api/approval/model';
|
||||
import { showError } from '@/utils/app';
|
||||
|
||||
const taskId = ref(0);
|
||||
const candidates = ref<ApprovalUser[]>([]);
|
||||
const selectedId = ref(0);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
|
||||
onLoad(options => {
|
||||
taskId.value = Number(options?.taskId) || 0;
|
||||
load();
|
||||
});
|
||||
|
||||
async function load() {
|
||||
if (!taskId.value) {
|
||||
showError('任务不存在,请返回重试', null);
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
candidates.value = await listDispatchCandidates(taskId.value);
|
||||
} catch (error: any) {
|
||||
candidates.value = [];
|
||||
showError(error?.message || '候选人员加载失败', null);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function displayName(item: ApprovalUser) {
|
||||
return item.realName || item.nickname || item.username || '—';
|
||||
}
|
||||
|
||||
function select(item: ApprovalUser) {
|
||||
if (submitting.value) {
|
||||
return;
|
||||
}
|
||||
selectedId.value = item.userId;
|
||||
}
|
||||
|
||||
function confirmDispatch() {
|
||||
const target = candidates.value.find(item => item.userId === selectedId.value);
|
||||
if (!target) {
|
||||
showError('请选择派单对象', null);
|
||||
return;
|
||||
}
|
||||
if (!taskId.value) {
|
||||
showError('任务不存在,请返回重试', null);
|
||||
return;
|
||||
}
|
||||
uni.showModal({
|
||||
title: '派单确认',
|
||||
content: '确定将单据派给「' + displayName(target) + '」?派单后不能更改。',
|
||||
success: async result => {
|
||||
if (!result.confirm) {
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
const response = await dispatchTask(taskId.value, target.userId);
|
||||
uni.showToast({ title: response.message || '派单成功', icon: 'none' });
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({ delta: 1 });
|
||||
}, 600);
|
||||
} catch (error: any) {
|
||||
showError(error?.message || '派单失败,请稍后重试', null);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dispatch-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f6f8;
|
||||
padding-bottom: 180rpx;
|
||||
}
|
||||
|
||||
.hint {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
line-height: 36rpx;
|
||||
color: #909399;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.candidate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 20rpx;
|
||||
margin-top: 12rpx;
|
||||
border: 2rpx solid #ebeef5;
|
||||
border-radius: 12rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.candidate.active {
|
||||
border-color: #1677ff;
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
.candidate-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.candidate-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.candidate-meta {
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.candidate-radio {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border: 2rpx solid #c0c4cc;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.candidate-radio.checked {
|
||||
border-color: #1677ff;
|
||||
}
|
||||
|
||||
.candidate-dot {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #1677ff;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
padding: 20rpx 24rpx;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.footer-button {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.footer-button.primary {
|
||||
background-color: #1677ff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.footer-button.primary[disabled] {
|
||||
background-color: #a0c4ff;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
@@ -63,6 +63,13 @@
|
||||
"navigationBarTitleText": "内部协调单",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/dispatch",
|
||||
"style": {
|
||||
"navigationBarTitleText": "派单",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user