merge: t24 待办派单/转办
This commit is contained in:
@@ -173,3 +173,15 @@ export interface ApprovalOrganization {
|
|||||||
organizationCode?: string;
|
organizationCode?: string;
|
||||||
leaderId?: number;
|
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 request from '@/utils/request';
|
||||||
import type { ApiResult } from '@/api';
|
import type { ApiResult } from '@/api';
|
||||||
import type { ApprovalInstance } from '../model';
|
import type { ApprovalInstance, ApprovalUser } from '../model';
|
||||||
import { THINK_API_URL } from '@/config/setting';
|
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));
|
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>
|
||||||
|
<button
|
||||||
|
v-if="detail.myTask && detail.permissions.canDispatch"
|
||||||
|
class="action-button primary"
|
||||||
|
@click="openDispatch"
|
||||||
|
>
|
||||||
|
派单
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="detail.myTask && detail.permissions.canReject"
|
v-if="detail.myTask && detail.permissions.canReject"
|
||||||
class="action-button danger"
|
class="action-button danger"
|
||||||
@@ -220,7 +227,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
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 { getInstance, submitInstance } from '@/api/approval/instance';
|
||||||
import {
|
import {
|
||||||
approveTask,
|
approveTask,
|
||||||
@@ -247,6 +254,7 @@ const hasActions = computed(() => {
|
|||||||
return (
|
return (
|
||||||
permissions.canApprove ||
|
permissions.canApprove ||
|
||||||
permissions.canReject ||
|
permissions.canReject ||
|
||||||
|
permissions.canDispatch ||
|
||||||
permissions.canConfirm ||
|
permissions.canConfirm ||
|
||||||
permissions.canArchive ||
|
permissions.canArchive ||
|
||||||
permissions.canWithdraw ||
|
permissions.canWithdraw ||
|
||||||
@@ -261,11 +269,22 @@ const editLabel = computed(() =>
|
|||||||
: '编辑'
|
: '编辑'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 首次进入由 onLoad 加载;从派单/编辑页返回时重新拉取,避免展示过期节点
|
||||||
|
let entered = false;
|
||||||
|
|
||||||
onLoad(options => {
|
onLoad(options => {
|
||||||
instanceId.value = Number(options?.id) || 0;
|
instanceId.value = Number(options?.id) || 0;
|
||||||
load();
|
load();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
if (!entered) {
|
||||||
|
entered = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
if (!instanceId.value) {
|
if (!instanceId.value) {
|
||||||
return;
|
return;
|
||||||
@@ -302,6 +321,17 @@ function openApprove() {
|
|||||||
actionDialog.value = 'approve';
|
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() {
|
function openReject() {
|
||||||
opinion.value = '';
|
opinion.value = '';
|
||||||
actionDialog.value = 'reject';
|
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": "内部协调单",
|
"navigationBarTitleText": "内部协调单",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/dispatch",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "派单",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user