Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vben into dev
commit
3a740b5abd
|
@ -5,7 +5,10 @@ import type { SimpleFlowNode } from '../../consts';
|
||||||
|
|
||||||
import { inject, ref } from 'vue';
|
import { inject, ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
import { useTaskStatusClass, useWatchNode } from '../../helpers';
|
import { useTaskStatusClass, useWatchNode } from '../../helpers';
|
||||||
|
import ProcessInstanceModal from './modules/process-instance-modal.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'EndEventNode' });
|
defineOptions({ name: 'EndEventNode' });
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -20,15 +23,26 @@ const currentNode = useWatchNode(props);
|
||||||
const readonly = inject<Boolean>('readonly');
|
const readonly = inject<Boolean>('readonly');
|
||||||
const processInstance = inject<Ref<any>>('processInstance', ref({}));
|
const processInstance = inject<Ref<any>>('processInstance', ref({}));
|
||||||
|
|
||||||
const processInstanceInfos = ref<any[]>([]); // 流程的审批信息
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
connectedComponent: ProcessInstanceModal,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
function nodeClick() {
|
function nodeClick() {
|
||||||
if (readonly && processInstance && processInstance.value) {
|
if (readonly && processInstance && processInstance.value) {
|
||||||
console.warn(
|
const processInstanceInfo = [
|
||||||
'TODO 只读模式,弹窗显示审批信息',
|
{
|
||||||
processInstance.value,
|
startUser: processInstance.value.startUser,
|
||||||
processInstanceInfos.value,
|
createTime: processInstance.value.startTime,
|
||||||
);
|
endTime: processInstance.value.endTime,
|
||||||
|
status: processInstance.value.status,
|
||||||
|
durationInMillis: processInstance.value.durationInMillis,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
modalApi
|
||||||
|
.setData(processInstanceInfo)
|
||||||
|
.setState({ title: '流程信息' })
|
||||||
|
.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -42,5 +56,6 @@ function nodeClick() {
|
||||||
<span class="node-fixed-name" title="结束">结束</span>
|
<span class="node-fixed-name" title="结束">结束</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO 审批信息 -->
|
<!-- 流程信息弹窗 -->
|
||||||
|
<Modal />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||||
|
|
||||||
|
import { DICT_TYPE } from '#/utils';
|
||||||
|
|
||||||
|
/** 流程实例列表字段 */
|
||||||
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'startUser',
|
||||||
|
title: '发起人',
|
||||||
|
slots: {
|
||||||
|
default: ({ row }: { row: any }) => {
|
||||||
|
return row.startUser?.nickname;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'deptName',
|
||||||
|
title: '部门',
|
||||||
|
slots: {
|
||||||
|
default: ({ row }: { row: any }) => {
|
||||||
|
return row.startUser?.deptName;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '开始时间',
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
minWidth: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'endTime',
|
||||||
|
title: '结束时间',
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
minWidth: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '流程状态',
|
||||||
|
minWidth: 90,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'durationInMillis',
|
||||||
|
title: '耗时',
|
||||||
|
minWidth: 100,
|
||||||
|
formatter: 'formatPast2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||||
|
|
||||||
|
import { useGridColumns } from './process-instance-data';
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(),
|
||||||
|
border: true,
|
||||||
|
height: 'auto',
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
footer: false,
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
const data = modalApi.getData<any[]>();
|
||||||
|
// 填充列表数据
|
||||||
|
await gridApi.setGridOptions({ data });
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal class="w-3/4">
|
||||||
|
<Grid />
|
||||||
|
</Modal>
|
||||||
|
</template>
|
|
@ -0,0 +1,61 @@
|
||||||
|
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||||
|
|
||||||
|
import { DICT_TYPE } from '#/utils';
|
||||||
|
|
||||||
|
/** 审批记录列表字段 */
|
||||||
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'assigneeUser',
|
||||||
|
title: '审批人',
|
||||||
|
slots: {
|
||||||
|
default: ({ row }: { row: any }) => {
|
||||||
|
return row.assigneeUser?.nickname || row.ownerUser?.nickname;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'deptName',
|
||||||
|
title: '部门',
|
||||||
|
slots: {
|
||||||
|
default: ({ row }: { row: any }) => {
|
||||||
|
return row.assigneeUser?.deptName || row.ownerUser?.deptName;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '开始时间',
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
minWidth: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'endTime',
|
||||||
|
title: '结束时间',
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
minWidth: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '审批状态',
|
||||||
|
minWidth: 90,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.BPM_TASK_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'reason',
|
||||||
|
title: '审批建议',
|
||||||
|
minWidth: 160,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'durationInMillis',
|
||||||
|
title: '耗时',
|
||||||
|
minWidth: 100,
|
||||||
|
formatter: 'formatPast2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||||
|
|
||||||
|
import { useGridColumns } from './task-list-data';
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(),
|
||||||
|
border: true,
|
||||||
|
height: 'auto',
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
footer: false,
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
const data = modalApi.getData<any[]>();
|
||||||
|
// 填充列表数据
|
||||||
|
await gridApi.setGridOptions({ data });
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal class="w-3/4">
|
||||||
|
<Grid />
|
||||||
|
</Modal>
|
||||||
|
</template>
|
|
@ -6,6 +6,7 @@ import type { SimpleFlowNode } from '../../consts';
|
||||||
|
|
||||||
import { inject, ref } from 'vue';
|
import { inject, ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { Input } from 'ant-design-vue';
|
import { Input } from 'ant-design-vue';
|
||||||
|
@ -15,6 +16,7 @@ import { BpmNodeTypeEnum } from '#/utils';
|
||||||
import { NODE_DEFAULT_TEXT } from '../../consts';
|
import { NODE_DEFAULT_TEXT } from '../../consts';
|
||||||
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
|
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
|
||||||
import StartUserNodeConfig from '../nodes-config/start-user-node-config.vue';
|
import StartUserNodeConfig from '../nodes-config/start-user-node-config.vue';
|
||||||
|
import TaskListModal from './modules/task-list-modal.vue';
|
||||||
import NodeHandler from './node-handler.vue';
|
import NodeHandler from './node-handler.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'StartUserNode' });
|
defineOptions({ name: 'StartUserNode' });
|
||||||
|
@ -27,7 +29,6 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义事件,更新父组件。
|
// 定义事件,更新父组件。
|
||||||
// const emits = defineEmits<{
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
'update:modelValue': [node: SimpleFlowNode | undefined];
|
'update:modelValue': [node: SimpleFlowNode | undefined];
|
||||||
}>();
|
}>();
|
||||||
|
@ -44,24 +45,25 @@ const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||||
|
|
||||||
const nodeSetting = ref();
|
const nodeSetting = ref();
|
||||||
|
|
||||||
// 任务的弹窗显示,用于只读模式
|
const [Modal, modalApi] = useVbenModal({
|
||||||
const selectTasks = ref<any[] | undefined>([]); // 选中的任务数组
|
connectedComponent: TaskListModal,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
function nodeClick() {
|
function nodeClick() {
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
// 只读模式,弹窗显示任务信息
|
// 只读模式,弹窗显示任务信息
|
||||||
if (tasks && tasks.value) {
|
if (tasks && tasks.value) {
|
||||||
console.warn(
|
// 过滤出当前节点的任务
|
||||||
'TODO 只读模式,弹窗显示任务信息',
|
const nodeTasks = tasks.value.filter(
|
||||||
tasks.value,
|
(task) => task.taskDefinitionKey === currentNode.value.id,
|
||||||
selectTasks.value,
|
|
||||||
);
|
);
|
||||||
|
// 弹窗显示任务信息
|
||||||
|
modalApi
|
||||||
|
.setData(nodeTasks)
|
||||||
|
.setState({ title: currentNode.value.name })
|
||||||
|
.open();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
|
||||||
'TODO 编辑模式,打开节点配置、把当前节点传递给配置组件',
|
|
||||||
nodeSetting.value,
|
|
||||||
);
|
|
||||||
nodeSetting.value.showStartUserNodeConfig(currentNode.value);
|
nodeSetting.value.showStartUserNodeConfig(currentNode.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,5 +124,6 @@ function nodeClick() {
|
||||||
ref="nodeSetting"
|
ref="nodeSetting"
|
||||||
:flow-node="currentNode"
|
:flow-node="currentNode"
|
||||||
/>
|
/>
|
||||||
<!-- 审批记录 TODO -->
|
<!-- 审批记录弹窗 -->
|
||||||
|
<Modal />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import type { SimpleFlowNode } from '../../consts';
|
||||||
|
|
||||||
import { inject, ref } from 'vue';
|
import { inject, ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { Input } from 'ant-design-vue';
|
import { Input } from 'ant-design-vue';
|
||||||
|
@ -14,6 +15,26 @@ import { BpmNodeTypeEnum } from '#/utils';
|
||||||
import { NODE_DEFAULT_TEXT } from '../../consts';
|
import { NODE_DEFAULT_TEXT } from '../../consts';
|
||||||
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
|
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
|
||||||
import UserTaskNodeConfig from '../nodes-config/user-task-node-config.vue';
|
import UserTaskNodeConfig from '../nodes-config/user-task-node-config.vue';
|
||||||
|
import TaskListModal from './modules/task-list-modal.vue';
|
||||||
|
// // 使用useVbenVxeGrid
|
||||||
|
// const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
// gridOptions: {
|
||||||
|
// columns: columns.value,
|
||||||
|
// keepSource: true,
|
||||||
|
// border: true,
|
||||||
|
// height: 'auto',
|
||||||
|
// data: selectTasks.value,
|
||||||
|
// rowConfig: {
|
||||||
|
// keyField: 'id',
|
||||||
|
// },
|
||||||
|
// pagerConfig: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// toolbarConfig: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// } as VxeTableGridOptions<any>,
|
||||||
|
// });
|
||||||
import NodeHandler from './node-handler.vue';
|
import NodeHandler from './node-handler.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'UserTaskNode' });
|
defineOptions({ name: 'UserTaskNode' });
|
||||||
|
@ -42,11 +63,23 @@ const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||||
);
|
);
|
||||||
const nodeSetting = ref();
|
const nodeSetting = ref();
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
connectedComponent: TaskListModal,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
function nodeClick() {
|
function nodeClick() {
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
if (tasks && tasks.value) {
|
if (tasks && tasks.value) {
|
||||||
// 只读模式,弹窗显示任务信息 TODO 待实现
|
// 过滤出当前节点的任务
|
||||||
console.warn('只读模式,弹窗显示任务信息待实现');
|
const nodeTasks = tasks.value.filter(
|
||||||
|
(task) => task.taskDefinitionKey === currentNode.value.id,
|
||||||
|
);
|
||||||
|
// 弹窗显示任务信息
|
||||||
|
modalApi
|
||||||
|
.setData(nodeTasks)
|
||||||
|
.setState({ title: currentNode.value.name })
|
||||||
|
.open();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 编辑模式,打开节点配置、把当前节点传递给配置组件
|
// 编辑模式,打开节点配置、把当前节点传递给配置组件
|
||||||
|
@ -64,8 +97,6 @@ function findReturnTaskNodes(
|
||||||
// 从父节点查找
|
// 从父节点查找
|
||||||
emits('findParentNode', matchNodeList, BpmNodeTypeEnum.USER_TASK_NODE);
|
emits('findParentNode', matchNodeList, BpmNodeTypeEnum.USER_TASK_NODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const selectTasks = ref<any[] | undefined>([]); // 选中的任务数组
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="node-wrapper">
|
<div class="node-wrapper">
|
||||||
|
@ -138,5 +169,6 @@ function findReturnTaskNodes(
|
||||||
:flow-node="currentNode"
|
:flow-node="currentNode"
|
||||||
@find-return-task-nodes="findReturnTaskNodes"
|
@find-return-task-nodes="findReturnTaskNodes"
|
||||||
/>
|
/>
|
||||||
<!-- TODO 审批记录 -->
|
<!-- 审批记录弹窗 -->
|
||||||
|
<Modal />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -249,7 +249,7 @@ onMounted(() => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO 这个好像暂时没有用到。保存失败弹窗 -->
|
|
||||||
<Modal
|
<Modal
|
||||||
v-model:open="errorDialogVisible"
|
v-model:open="errorDialogVisible"
|
||||||
title="保存失败"
|
title="保存失败"
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { SimpleFlowNode } from '../consts';
|
||||||
|
|
||||||
|
import { provide, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { useWatchNode } from '../helpers';
|
||||||
|
import SimpleProcessModel from './simple-process-model.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'SimpleProcessViewer' });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
flowNode: SimpleFlowNode;
|
||||||
|
// 流程实例
|
||||||
|
processInstance?: any;
|
||||||
|
// 流程任务
|
||||||
|
tasks?: any[];
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
processInstance: undefined,
|
||||||
|
tasks: () => [] as any[],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const approveTasks = ref<any[]>(props.tasks);
|
||||||
|
const currentProcessInstance = ref(props.processInstance);
|
||||||
|
const simpleModel = useWatchNode(props);
|
||||||
|
watch(
|
||||||
|
() => props.tasks,
|
||||||
|
(newValue) => {
|
||||||
|
approveTasks.value = newValue;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => props.processInstance,
|
||||||
|
(newValue) => {
|
||||||
|
currentProcessInstance.value = newValue;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// 提供给后代组件使用
|
||||||
|
provide('tasks', approveTasks);
|
||||||
|
provide('processInstance', currentProcessInstance);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<SimpleProcessModel :flow-node="simpleModel" :readonly="true" />
|
||||||
|
</template>
|
|
@ -4,4 +4,8 @@ export { default as HttpRequestSetting } from './components/nodes-config/modules
|
||||||
|
|
||||||
export { default as SimpleProcessDesigner } from './components/simple-process-designer.vue';
|
export { default as SimpleProcessDesigner } from './components/simple-process-designer.vue';
|
||||||
|
|
||||||
|
export { default as SimpleProcessViewer } from './components/simple-process-viewer.vue';
|
||||||
|
|
||||||
|
export type { SimpleFlowNode } from './consts';
|
||||||
|
|
||||||
export { parseFormFields } from './helpers';
|
export { parseFormFields } from './helpers';
|
||||||
|
|
|
@ -46,6 +46,7 @@ const processedActions = ref<any[]>([]);
|
||||||
const processedDropdownActions = ref<any[]>([]);
|
const processedDropdownActions = ref<any[]>([]);
|
||||||
|
|
||||||
/** 用于比较的字符串化版本 */
|
/** 用于比较的字符串化版本 */
|
||||||
|
// TODO @xingyu:下面的拼写错误,需要修改
|
||||||
const actionsStringified = ref('');
|
const actionsStringified = ref('');
|
||||||
const dropdownActionsStringified = ref('');
|
const dropdownActionsStringified = ref('');
|
||||||
|
|
||||||
|
|
|
@ -346,7 +346,12 @@ onMounted(async () => {
|
||||||
</Row>
|
</Row>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|
||||||
<TabPane tab="流程图" key="diagram" class="tab-pane-content">
|
<TabPane
|
||||||
|
tab="流程图"
|
||||||
|
key="diagram"
|
||||||
|
class="tab-pane-content"
|
||||||
|
:force-render="true"
|
||||||
|
>
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
<ProcessInstanceSimpleViewer
|
<ProcessInstanceSimpleViewer
|
||||||
v-show="
|
v-show="
|
||||||
|
|
|
@ -1,9 +1,180 @@
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'ProcessInstanceSimpleViewer' });
|
import type { SimpleFlowNode } from '#/components/simple-process-design';
|
||||||
</script>
|
|
||||||
|
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { SimpleProcessViewer } from '#/components/simple-process-design';
|
||||||
|
import { BpmNodeTypeEnum, BpmTaskStatusEnum } from '#/utils';
|
||||||
|
|
||||||
|
defineOptions({ name: 'BpmProcessInstanceSimpleViewer' });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
loading?: boolean; // 是否加载中
|
||||||
|
modelView?: any;
|
||||||
|
simpleJson?: string; // Simple 模型结构数据 (json 格式)
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
loading: false,
|
||||||
|
modelView: () => ({}),
|
||||||
|
simpleJson: '',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const simpleModel = ref<any>({});
|
||||||
|
// 用户任务
|
||||||
|
const tasks = ref([]);
|
||||||
|
// 流程实例
|
||||||
|
const processInstance = ref();
|
||||||
|
|
||||||
|
/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */
|
||||||
|
watch(
|
||||||
|
() => props.modelView,
|
||||||
|
async (newModelView) => {
|
||||||
|
if (newModelView) {
|
||||||
|
tasks.value = newModelView.tasks;
|
||||||
|
processInstance.value = newModelView.processInstance;
|
||||||
|
// 已经拒绝的活动节点编号集合,只包括 UserTask
|
||||||
|
const rejectedTaskActivityIds: string[] =
|
||||||
|
newModelView.rejectedTaskActivityIds;
|
||||||
|
// 进行中的活动节点编号集合, 只包括 UserTask
|
||||||
|
const unfinishedTaskActivityIds: string[] =
|
||||||
|
newModelView.unfinishedTaskActivityIds;
|
||||||
|
// 已经完成的活动节点编号集合, 包括 UserTask、Gateway 等
|
||||||
|
const finishedActivityIds: string[] =
|
||||||
|
newModelView.finishedTaskActivityIds;
|
||||||
|
// 已经完成的连线节点编号集合,只包括 SequenceFlow
|
||||||
|
const finishedSequenceFlowActivityIds: string[] =
|
||||||
|
newModelView.finishedSequenceFlowActivityIds;
|
||||||
|
setSimpleModelNodeTaskStatus(
|
||||||
|
newModelView.simpleModel,
|
||||||
|
newModelView.processInstance?.status,
|
||||||
|
rejectedTaskActivityIds,
|
||||||
|
unfinishedTaskActivityIds,
|
||||||
|
finishedActivityIds,
|
||||||
|
finishedSequenceFlowActivityIds,
|
||||||
|
);
|
||||||
|
simpleModel.value = newModelView.simpleModel || {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 监控模型结构数据 */
|
||||||
|
watch(
|
||||||
|
() => props.simpleJson,
|
||||||
|
async (value) => {
|
||||||
|
if (value) {
|
||||||
|
simpleModel.value = JSON.parse(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const setSimpleModelNodeTaskStatus = (
|
||||||
|
simpleModel: SimpleFlowNode | undefined,
|
||||||
|
processStatus: number,
|
||||||
|
rejectedTaskActivityIds: string[],
|
||||||
|
unfinishedTaskActivityIds: string[],
|
||||||
|
finishedActivityIds: string[],
|
||||||
|
finishedSequenceFlowActivityIds: string[],
|
||||||
|
) => {
|
||||||
|
if (!simpleModel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 结束节点
|
||||||
|
if (simpleModel.type === BpmNodeTypeEnum.END_EVENT_NODE) {
|
||||||
|
simpleModel.activityStatus = finishedActivityIds.includes(simpleModel.id)
|
||||||
|
? processStatus
|
||||||
|
: BpmTaskStatusEnum.NOT_START;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 审批节点
|
||||||
|
if (
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.START_USER_NODE ||
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.USER_TASK_NODE ||
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.TRANSACTOR_NODE ||
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.CHILD_PROCESS_NODE
|
||||||
|
) {
|
||||||
|
simpleModel.activityStatus = BpmTaskStatusEnum.NOT_START;
|
||||||
|
if (rejectedTaskActivityIds.includes(simpleModel.id)) {
|
||||||
|
simpleModel.activityStatus = BpmTaskStatusEnum.REJECT;
|
||||||
|
} else if (unfinishedTaskActivityIds.includes(simpleModel.id)) {
|
||||||
|
simpleModel.activityStatus = BpmTaskStatusEnum.RUNNING;
|
||||||
|
} else if (finishedActivityIds.includes(simpleModel.id)) {
|
||||||
|
simpleModel.activityStatus = BpmTaskStatusEnum.APPROVE;
|
||||||
|
}
|
||||||
|
// TODO 是不是还缺一个 cancel 的状态
|
||||||
|
}
|
||||||
|
// 抄送节点
|
||||||
|
if (simpleModel.type === BpmNodeTypeEnum.COPY_TASK_NODE) {
|
||||||
|
// 抄送节点,只有通过和未执行状态
|
||||||
|
simpleModel.activityStatus = finishedActivityIds.includes(simpleModel.id)
|
||||||
|
? BpmTaskStatusEnum.APPROVE
|
||||||
|
: BpmTaskStatusEnum.NOT_START;
|
||||||
|
}
|
||||||
|
// 延迟器节点
|
||||||
|
if (simpleModel.type === BpmNodeTypeEnum.DELAY_TIMER_NODE) {
|
||||||
|
// 延迟器节点,只有通过和未执行状态
|
||||||
|
simpleModel.activityStatus = finishedActivityIds.includes(simpleModel.id)
|
||||||
|
? BpmTaskStatusEnum.APPROVE
|
||||||
|
: BpmTaskStatusEnum.NOT_START;
|
||||||
|
}
|
||||||
|
// 触发器节点
|
||||||
|
if (simpleModel.type === BpmNodeTypeEnum.TRIGGER_NODE) {
|
||||||
|
// 触发器节点,只有通过和未执行状态
|
||||||
|
simpleModel.activityStatus = finishedActivityIds.includes(simpleModel.id)
|
||||||
|
? BpmTaskStatusEnum.APPROVE
|
||||||
|
: BpmTaskStatusEnum.NOT_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 条件节点对应 SequenceFlow
|
||||||
|
if (simpleModel.type === BpmNodeTypeEnum.CONDITION_NODE) {
|
||||||
|
// 条件节点,只有通过和未执行状态
|
||||||
|
simpleModel.activityStatus = finishedSequenceFlowActivityIds.includes(
|
||||||
|
simpleModel.id,
|
||||||
|
)
|
||||||
|
? BpmTaskStatusEnum.APPROVE
|
||||||
|
: BpmTaskStatusEnum.NOT_START;
|
||||||
|
}
|
||||||
|
// 网关节点
|
||||||
|
if (
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.CONDITION_BRANCH_NODE ||
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.PARALLEL_BRANCH_NODE ||
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.INCLUSIVE_BRANCH_NODE ||
|
||||||
|
simpleModel.type === BpmNodeTypeEnum.ROUTER_BRANCH_NODE
|
||||||
|
) {
|
||||||
|
// 网关节点。只有通过和未执行状态
|
||||||
|
simpleModel.activityStatus = finishedActivityIds.includes(simpleModel.id)
|
||||||
|
? BpmTaskStatusEnum.APPROVE
|
||||||
|
: BpmTaskStatusEnum.NOT_START;
|
||||||
|
simpleModel.conditionNodes?.forEach((node) => {
|
||||||
|
setSimpleModelNodeTaskStatus(
|
||||||
|
node,
|
||||||
|
processStatus,
|
||||||
|
rejectedTaskActivityIds,
|
||||||
|
unfinishedTaskActivityIds,
|
||||||
|
finishedActivityIds,
|
||||||
|
finishedSequenceFlowActivityIds,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setSimpleModelNodeTaskStatus(
|
||||||
|
simpleModel.childNode,
|
||||||
|
processStatus,
|
||||||
|
rejectedTaskActivityIds,
|
||||||
|
unfinishedTaskActivityIds,
|
||||||
|
finishedActivityIds,
|
||||||
|
finishedSequenceFlowActivityIds,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div v-loading="loading">
|
||||||
<h1>Simple BPM Viewer</h1>
|
<SimpleProcessViewer
|
||||||
|
:flow-node="simpleModel"
|
||||||
|
:tasks="tasks"
|
||||||
|
:process-instance="processInstance"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
||||||
|
|
||||||
import { h, nextTick, onMounted, ref } from 'vue';
|
import { nextTick, onMounted, ref } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { confirm, Page } from '@vben/common-ui';
|
import { Page, prompt } from '@vben/common-ui';
|
||||||
|
|
||||||
import { Input, message } from 'ant-design-vue';
|
import { Input, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ const processDefinitionId = query.processDefinitionId as string;
|
||||||
const formFields = ref<any[]>([]);
|
const formFields = ref<any[]>([]);
|
||||||
const userList = ref<any[]>([]); // 用户列表
|
const userList = ref<any[]>([]); // 用户列表
|
||||||
const gridReady = ref(false); // 表格是否准备好
|
const gridReady = ref(false); // 表格是否准备好
|
||||||
const cancelReason = ref(''); // 取消原因
|
|
||||||
|
|
||||||
// 表格的列需要解析表单字段,这里定义成变量,解析表单字段后再渲染
|
// 表格的列需要解析表单字段,这里定义成变量,解析表单字段后再渲染
|
||||||
let Grid: any = null;
|
let Grid: any = null;
|
||||||
|
@ -81,26 +80,19 @@ const handleDetail = (row: BpmProcessInstanceApi.ProcessInstance) => {
|
||||||
|
|
||||||
/** 取消按钮操作 */
|
/** 取消按钮操作 */
|
||||||
const handleCancel = async (row: BpmProcessInstanceApi.ProcessInstance) => {
|
const handleCancel = async (row: BpmProcessInstanceApi.ProcessInstance) => {
|
||||||
cancelReason.value = ''; // 重置取消原因
|
prompt({
|
||||||
confirm({
|
content: '请输入取消原因:',
|
||||||
title: '取消流程',
|
title: '取消流程',
|
||||||
content: h('div', [
|
icon: 'question',
|
||||||
h('p', '请输入取消原因:'),
|
component: Input,
|
||||||
h(Input, {
|
modelPropName: 'value',
|
||||||
value: cancelReason.value,
|
async beforeClose(scope) {
|
||||||
'onUpdate:value': (val: string) => {
|
if (!scope.isConfirm) return;
|
||||||
cancelReason.value = val;
|
if (!scope.value) {
|
||||||
},
|
|
||||||
placeholder: '请输入取消原因',
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
beforeClose: async ({ isConfirm }) => {
|
|
||||||
if (!isConfirm) return;
|
|
||||||
if (!cancelReason.value.trim()) {
|
|
||||||
message.warning('请输入取消原因');
|
message.warning('请输入取消原因');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await cancelProcessInstanceByAdmin(row.id, cancelReason.value);
|
await cancelProcessInstanceByAdmin(row.id, scope.value);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
|
|
@ -29,6 +29,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
<Modal class="w-2/5" :title="$t('ui.widgets.qa')">
|
<Modal class="w-2/5" :title="$t('ui.widgets.qa')">
|
||||||
<div class="mt-2 flex flex-col">
|
<div class="mt-2 flex flex-col">
|
||||||
<div class="mt-2 flex flex-row">
|
<div class="mt-2 flex flex-row">
|
||||||
|
<!-- TODO @xingyu:要不要垂直?1. 项目地址;2. 问题反馈;3. 开发文档 -->
|
||||||
<VbenButtonGroup class="basis-1/3" :gap="2" border size="large">
|
<VbenButtonGroup class="basis-1/3" :gap="2" border size="large">
|
||||||
<p class="p-2">项目地址:</p>
|
<p class="p-2">项目地址:</p>
|
||||||
<VbenButton
|
<VbenButton
|
||||||
|
|
|
@ -46,6 +46,7 @@ async function handleChange(id: number | undefined) {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
<!-- TODO @xingyu:1)未选择的时候,空着一块,有点怪。是不是有个 placeholder 会好看点哈(之前有 page.tenant.placeholder)?2)是不是要支持个 clear 选择 -->
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<Button
|
<Button
|
||||||
|
|
Loading…
Reference in New Issue