feat: 选人组件(未完成) 加签减签
This commit is contained in:
parent
8b58440e00
commit
2e3d385747
@ -2,6 +2,8 @@ import type {
|
||||
CompleteTaskReqData,
|
||||
StartWorkFlowReqData,
|
||||
TaskInfo,
|
||||
TaskOperationData,
|
||||
TaskOperationType,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
@ -88,7 +90,7 @@ export function pageByTaskCopy(params?: PageQuery) {
|
||||
* @returns info
|
||||
*/
|
||||
export function getTaskByTaskId(taskId: string) {
|
||||
return requestClient.get<TaskInfo>(`/workflow/task/${taskId}`);
|
||||
return requestClient.get<TaskInfo>(`/workflow/task/getTask/${taskId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,11 +109,14 @@ export function terminationTask(data: { taskId: string }) {
|
||||
* @param taskOperationData 参数
|
||||
* @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
*/
|
||||
export function taskOperation(taskOperationData: any, taskOperation: string) {
|
||||
return requestClient.postWithMsg<void>('/workflow/task/taskOperation', {
|
||||
...taskOperationData,
|
||||
taskOperation,
|
||||
});
|
||||
export function taskOperation(
|
||||
taskOperationData: TaskOperationData,
|
||||
taskOperation: TaskOperationType,
|
||||
) {
|
||||
return requestClient.postWithMsg<void>(
|
||||
`/workflow/task/taskOperation/${taskOperation}`,
|
||||
taskOperationData,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
20
apps/web-antd/src/api/workflow/task/model.d.ts
vendored
20
apps/web-antd/src/api/workflow/task/model.d.ts
vendored
@ -23,7 +23,7 @@ export interface TaskInfo {
|
||||
assigneeNames: string;
|
||||
processedBy: string;
|
||||
type: string;
|
||||
nodeRatio?: any;
|
||||
nodeRatio?: string;
|
||||
createBy: string;
|
||||
createByName: string;
|
||||
}
|
||||
@ -52,3 +52,21 @@ export interface StartWorkFlowReqData {
|
||||
*/
|
||||
variables: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface TaskOperationData {
|
||||
message?: string;
|
||||
taskId: ID;
|
||||
// 单个操作人
|
||||
userId?: ID;
|
||||
// 多个操作人
|
||||
userIds?: IDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
*/
|
||||
export type TaskOperationType =
|
||||
| 'addSignature'
|
||||
| 'delegateTask'
|
||||
| 'reductionSignature'
|
||||
| 'transferTask';
|
||||
|
@ -10,6 +10,8 @@ import { getDeptTree } from '#/api/system/user';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true });
|
||||
|
||||
const emit = defineEmits<{
|
||||
/**
|
||||
* 点击刷新按钮的事件
|
||||
@ -68,7 +70,10 @@ onMounted(loadTree);
|
||||
class="bg-background flex h-full flex-col overflow-y-auto rounded-lg"
|
||||
>
|
||||
<!-- 固定在顶部 必须加上bg-background背景色 否则会产生'穿透'效果 -->
|
||||
<div class="bg-background z-100 sticky left-0 top-0 p-[8px]">
|
||||
<div
|
||||
v-if="showSearch"
|
||||
class="bg-background z-100 sticky left-0 top-0 p-[8px]"
|
||||
>
|
||||
<InputSearch
|
||||
v-model:value="searchValue"
|
||||
:placeholder="$t('pages.common.search')"
|
||||
|
@ -118,7 +118,6 @@ async function handleSubmit() {
|
||||
return;
|
||||
}
|
||||
const data = cloneDeep(await formApi.getValues());
|
||||
console.log(data);
|
||||
const requestData = {
|
||||
...omit(data, ['attachment']),
|
||||
fileId: data.attachment.join(','),
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { User } from '#/api/core/user';
|
||||
import type { FlowInfoResponse } from '#/api/workflow/instance/model';
|
||||
import type { TaskInfo } from '#/api/workflow/task/model';
|
||||
|
||||
@ -6,11 +7,15 @@ import { computed, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { Fallback, useVbenModal, VbenAvatar } from '@vben/common-ui';
|
||||
import { DictEnum } from '@vben/constants';
|
||||
import { getPopupContainer } from '@vben/utils';
|
||||
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import {
|
||||
Card,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Skeleton,
|
||||
@ -20,10 +25,15 @@ import {
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { flowInfo } from '#/api/workflow/instance';
|
||||
import { terminationTask } from '#/api/workflow/task';
|
||||
import {
|
||||
getTaskByTaskId,
|
||||
taskOperation,
|
||||
terminationTask,
|
||||
} from '#/api/workflow/task';
|
||||
import { renderDict } from '#/utils/render';
|
||||
|
||||
import { approvalModal, approvalRejectionModal, ApprovalTimeline } from '.';
|
||||
import userSelectModal from './user-select-modal.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'ApprovalPanel',
|
||||
@ -33,6 +43,20 @@ defineOptions({
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
const props = defineProps<{ task?: TaskInfo; type: ApprovalType }>();
|
||||
|
||||
const currentTask = ref<TaskInfo>();
|
||||
/**
|
||||
* 是否显示 加签/减签操作
|
||||
*/
|
||||
const showMultiActions = computed(() => {
|
||||
if (!currentTask.value) {
|
||||
return false;
|
||||
}
|
||||
if (Number(currentTask.value.nodeRatio) > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* myself 我发起的
|
||||
* readonly 只读 只用于查看
|
||||
@ -75,6 +99,9 @@ async function handleLoadInfo(task: TaskInfo | undefined) {
|
||||
iframeLoaded.value = false;
|
||||
const resp = await flowInfo(task.businessId);
|
||||
currentFlowInfo.value = resp;
|
||||
|
||||
const taskResp = await getTaskByTaskId(props.task!.id);
|
||||
currentTask.value = taskResp;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
@ -129,6 +156,89 @@ function handleApproval() {
|
||||
approvalModalApi.setData({ taskId: props.task?.id });
|
||||
approvalModalApi.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: 1提取公共函数 2原版是可以填写意见的(message参数)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 委托
|
||||
*/
|
||||
const [DelegationModal, delegationModalApi] = useVbenModal({
|
||||
connectedComponent: userSelectModal,
|
||||
});
|
||||
function handleDelegation(userList: User[]) {
|
||||
if (userList.length === 0) return;
|
||||
const current = userList[0];
|
||||
Modal.confirm({
|
||||
title: '委托',
|
||||
content: `确定委托给${current?.nickName}吗?`,
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await taskOperation(
|
||||
{ taskId: props.task!.id, userId: current!.userId },
|
||||
'delegateTask',
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 转办
|
||||
*/
|
||||
const [TransferModal, transferModalApi] = useVbenModal({
|
||||
connectedComponent: userSelectModal,
|
||||
});
|
||||
function handleTransfer(userList: User[]) {
|
||||
if (userList.length === 0) return;
|
||||
const current = userList[0];
|
||||
Modal.confirm({
|
||||
title: '转办',
|
||||
content: `确定转办给${current?.nickName}吗?`,
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await taskOperation(
|
||||
{ taskId: props.task!.id, userId: current!.userId },
|
||||
'transferTask',
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const [AddSignatureModal, addSignatureModalApi] = useVbenModal({
|
||||
connectedComponent: userSelectModal,
|
||||
});
|
||||
function handleAddSignature(userList: User[]) {
|
||||
if (userList.length === 0) return;
|
||||
const userIds = userList.map((user) => user.userId);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确认加签吗?',
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await taskOperation({ taskId: props.task!.id, userIds }, 'addSignature');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const [ReductionSignatureModal, reductionSignatureModalApi] = useVbenModal({
|
||||
connectedComponent: userSelectModal,
|
||||
});
|
||||
function handleReductionSignature(userList: User[]) {
|
||||
if (userList.length === 0) return;
|
||||
const userIds = userList.map((user) => user.userId);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确认加签吗?',
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await taskOperation(
|
||||
{ taskId: props.task!.id, userIds },
|
||||
'reductionSignature',
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -217,9 +327,45 @@ function handleApproval() {
|
||||
<a-button danger type="primary" @click="handleRejection">
|
||||
驳回
|
||||
</a-button>
|
||||
<a-button>其他</a-button>
|
||||
<Dropdown
|
||||
:get-popup-container="getPopupContainer"
|
||||
placement="bottomRight"
|
||||
>
|
||||
<template #overlay>
|
||||
<Menu>
|
||||
<MenuItem key="1" @click="() => delegationModalApi.open()">
|
||||
委托
|
||||
</MenuItem>
|
||||
<MenuItem key="2" @click="() => transferModalApi.open()">
|
||||
转办
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
v-if="showMultiActions"
|
||||
key="3"
|
||||
@click="() => addSignatureModalApi.open()"
|
||||
>
|
||||
加签
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
v-if="showMultiActions"
|
||||
key="4"
|
||||
@click="() => reductionSignatureModalApi.open()"
|
||||
>
|
||||
减签
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</template>
|
||||
<a-button> 其他 </a-button>
|
||||
</Dropdown>
|
||||
<ApprovalModal />
|
||||
<RejectionModal />
|
||||
<DelegationModal mode="single" @finish="handleDelegation" />
|
||||
<TransferModal mode="single" @finish="handleTransfer" />
|
||||
<AddSignatureModal mode="multiple" @finish="handleAddSignature" />
|
||||
<ReductionSignatureModal
|
||||
mode="multiple"
|
||||
@finish="handleReductionSignature"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,4 @@
|
||||
<!--抄送组件-->
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>dsadsa</template>
|
@ -10,4 +10,7 @@ export { default as ApprovalPanel } from './approval-panel.vue';
|
||||
*/
|
||||
export { default as approvalRejectionModal } from './approval-rejection-modal.vue';
|
||||
export { default as ApprovalTimeline } from './approval-timeline.vue';
|
||||
/**
|
||||
* 选人 支持单选/多选
|
||||
*/
|
||||
export { default as UserSelectModal } from './user-select-modal.vue';
|
||||
|
@ -1,45 +1,287 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { User } from '#/api';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Transfer } from 'ant-design-vue';
|
||||
import { useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { userList } from '#/api/system/user';
|
||||
import DeptTree from '#/views/system/user/dept-tree.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserSelectModal',
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const [BasicModal] = useVbenModal({
|
||||
title: '选择',
|
||||
class: 'w-[800px]',
|
||||
const props = withDefaults(defineProps<{ mode?: 'multiple' | 'single' }>(), {
|
||||
mode: 'multiple',
|
||||
});
|
||||
|
||||
const targetKeys = ref<string[]>([]);
|
||||
const emit = defineEmits<{
|
||||
finish: [User[]];
|
||||
}>();
|
||||
|
||||
const dataSource = ref<Awaited<ReturnType<typeof userList>>['rows']>([]);
|
||||
onMounted(async () => {
|
||||
const resp = await userList({ pageNum: 1, pageSize: 10 });
|
||||
dataSource.value = resp.rows.map((item) => ({
|
||||
...item,
|
||||
userId: String(item.userId),
|
||||
}));
|
||||
const [BasicModal, modalApi] = useVbenModal({
|
||||
title: '选择人员',
|
||||
class: 'w-[1000px]',
|
||||
fullscreenButton: false,
|
||||
onConfirm: handleSubmit,
|
||||
});
|
||||
|
||||
// 左边部门用
|
||||
const selectDeptId = ref<string[]>([]);
|
||||
const formOptions: VbenFormProps = {
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'userName',
|
||||
label: '用户账号',
|
||||
hideLabel: true,
|
||||
componentProps: {
|
||||
placeholder: '请输入账号',
|
||||
},
|
||||
},
|
||||
],
|
||||
commonConfig: {
|
||||
labelWidth: 80,
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
wrapperClass: 'grid-cols-2',
|
||||
handleReset: async () => {
|
||||
selectDeptId.value = [];
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
const { formApi, reload } = tableApi;
|
||||
await formApi.resetForm();
|
||||
const formValues = formApi.form.values;
|
||||
formApi.setLatestSubmissionValues(formValues);
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
await rightTableApi.grid.loadData([]);
|
||||
await reload(formValues);
|
||||
},
|
||||
};
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
checkboxConfig: {
|
||||
// 翻页时保留选中状态
|
||||
reserve: true,
|
||||
// 点击行选中
|
||||
trigger: 'row',
|
||||
},
|
||||
radioConfig: {
|
||||
trigger: 'row',
|
||||
strict: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: props.mode === 'single' ? 'radio' : 'checkbox', width: 60 },
|
||||
{
|
||||
field: 'userName',
|
||||
title: '名称',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
field: 'nickName',
|
||||
title: '昵称',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
field: 'deptName',
|
||||
title: '部门',
|
||||
width: 120,
|
||||
},
|
||||
],
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues = {}) => {
|
||||
// 部门树选择处理
|
||||
if (selectDeptId.value.length === 1) {
|
||||
formValues.deptId = selectDeptId.value[0];
|
||||
} else {
|
||||
Reflect.deleteProperty(formValues, 'deptId');
|
||||
}
|
||||
|
||||
return await userList({
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'userId',
|
||||
},
|
||||
toolbarConfig: {
|
||||
// 自定义列
|
||||
custom: false,
|
||||
// 最大化
|
||||
zoom: false,
|
||||
// 刷新
|
||||
refresh: false,
|
||||
},
|
||||
};
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
// 需要控制不同的事件 radio也会触发checkbox事件
|
||||
checkboxChange: checkBoxEvent,
|
||||
checkboxAll: checkBoxEvent,
|
||||
radioChange: radioEvent,
|
||||
},
|
||||
});
|
||||
|
||||
function checkBoxEvent() {
|
||||
if (props.mode !== 'multiple') {
|
||||
return;
|
||||
}
|
||||
// 给右边表格赋值
|
||||
const records = tableApi.grid.getCheckboxRecords();
|
||||
console.log(records);
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
rightTableApi.grid.loadData(records);
|
||||
}
|
||||
|
||||
function radioEvent() {
|
||||
if (props.mode !== 'single') {
|
||||
return;
|
||||
}
|
||||
// 给右边表格赋值
|
||||
const records = tableApi.grid.getRadioRecord();
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
rightTableApi.grid.loadData([records]);
|
||||
}
|
||||
|
||||
const rightGridOptions: VxeGridProps = {
|
||||
checkboxConfig: {},
|
||||
columns: [
|
||||
{
|
||||
field: 'nickName',
|
||||
title: '昵称',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
title: '操作',
|
||||
width: 120,
|
||||
slots: { default: 'action' },
|
||||
},
|
||||
],
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'userId',
|
||||
},
|
||||
toolbarConfig: {
|
||||
// 自定义列
|
||||
custom: false,
|
||||
// 最大化
|
||||
zoom: false,
|
||||
// 刷新
|
||||
refresh: false,
|
||||
},
|
||||
};
|
||||
|
||||
const [RightBasicTable, rightTableApi] = useVbenVxeGrid({
|
||||
gridOptions: rightGridOptions,
|
||||
});
|
||||
|
||||
function handleRemoveItem(row: any) {
|
||||
if (props.mode === 'multiple') {
|
||||
tableApi.grid.setCheckboxRow(row, false);
|
||||
}
|
||||
if (props.mode === 'single') {
|
||||
tableApi.grid.clearRadioRow();
|
||||
}
|
||||
rightTableApi.grid.remove(row);
|
||||
}
|
||||
|
||||
function handleRemoveAll() {
|
||||
if (props.mode === 'multiple') {
|
||||
tableApi.grid.clearCheckboxRow();
|
||||
}
|
||||
if (props.mode === 'single') {
|
||||
tableApi.grid.clearRadioRow();
|
||||
}
|
||||
rightTableApi.grid.loadData([]);
|
||||
}
|
||||
|
||||
async function handleDeptQuery() {
|
||||
await tableApi.reload();
|
||||
// 重置后恢复 保存勾选的数据
|
||||
const records = rightTableApi.grid.getData();
|
||||
if (props.mode === 'multiple') {
|
||||
tableApi?.grid.setCheckboxRow(records, true);
|
||||
}
|
||||
if (props.mode === 'single' && records.length === 1) {
|
||||
tableApi.grid.setRadioRow(records[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
const records = rightTableApi.grid.getData();
|
||||
console.log(records);
|
||||
emit('finish', records);
|
||||
modalApi.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal>
|
||||
<div class="min-h-[350px]">
|
||||
<Transfer
|
||||
v-model:target-keys="targetKeys"
|
||||
:data-source="dataSource"
|
||||
:pagination="true"
|
||||
:render="(item) => item.nickName"
|
||||
:row-key="(record) => record.userId"
|
||||
class="h-full"
|
||||
<div class="flex min-h-[600px]">
|
||||
<DeptTree
|
||||
v-model:select-dept-id="selectDeptId"
|
||||
:show-search="false"
|
||||
class="w-[230px]"
|
||||
@reload="() => tableApi.reload()"
|
||||
@select="handleDeptQuery"
|
||||
/>
|
||||
<div class="h-[600px] w-[420px]">
|
||||
<BasicTable />
|
||||
</div>
|
||||
<div class="flex h-[600px] w-[360px] flex-col">
|
||||
<div class="flex w-full px-4">
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<div>已选中人员</div>
|
||||
<div>
|
||||
<a-button size="small" @click="handleRemoveAll">
|
||||
清空选中
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RightBasicTable>
|
||||
<template #action="{ row }">
|
||||
<a-button size="small" @click="handleRemoveItem(row)">
|
||||
移除
|
||||
</a-button>
|
||||
</template>
|
||||
</RightBasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(div.vben-link) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.vxe-body--row) {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -168,6 +168,6 @@ function handleTest() {
|
||||
</Space>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<UserSelectModal />
|
||||
<UserSelectModal mode="single" />
|
||||
</Page>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user