feat:【antd】【bpm】processInstance/detail 代码评审
@@ -11,6 +11,12 @@ import {
|
|||||||
BpmTaskStatusEnum,
|
BpmTaskStatusEnum,
|
||||||
DICT_TYPE,
|
DICT_TYPE,
|
||||||
} from '@vben/constants';
|
} from '@vben/constants';
|
||||||
|
import {
|
||||||
|
SvgBpmApproveIcon,
|
||||||
|
SvgBpmCancelIcon,
|
||||||
|
SvgBpmRejectIcon,
|
||||||
|
SvgBpmRunningIcon,
|
||||||
|
} from '@vben/icons';
|
||||||
import { formatDateTime } from '@vben/utils';
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
import { Avatar, Card, Col, message, Row, TabPane, Tabs } from 'ant-design-vue';
|
import { Avatar, Card, Col, message, Row, TabPane, Tabs } from 'ant-design-vue';
|
||||||
@@ -23,12 +29,6 @@ import { getSimpleUserList } from '#/api/system/user';
|
|||||||
import DictTag from '#/components/dict-tag/dict-tag.vue';
|
import DictTag from '#/components/dict-tag/dict-tag.vue';
|
||||||
import { setConfAndFields2 } from '#/components/form-create';
|
import { setConfAndFields2 } from '#/components/form-create';
|
||||||
import { registerComponent } from '#/utils';
|
import { registerComponent } from '#/utils';
|
||||||
import {
|
|
||||||
SvgBpmApproveIcon,
|
|
||||||
SvgBpmCancelIcon,
|
|
||||||
SvgBpmRejectIcon,
|
|
||||||
SvgBpmRunningIcon,
|
|
||||||
} from '#/views/bpm/processInstance/detail/modules/icons';
|
|
||||||
|
|
||||||
import ProcessInstanceBpmnViewer from './modules/bpm-viewer.vue';
|
import ProcessInstanceBpmnViewer from './modules/bpm-viewer.vue';
|
||||||
import ProcessInstanceOperationButton from './modules/operation-button.vue';
|
import ProcessInstanceOperationButton from './modules/operation-button.vue';
|
||||||
@@ -44,22 +44,11 @@ const props = defineProps<{
|
|||||||
taskId?: string; // 任务编号
|
taskId?: string; // 任务编号
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
// TODO @jason:是不是使用全局的 FieldPermissionType?export enum FieldPermissionType {
|
||||||
enum FieldPermissionType {
|
enum FieldPermissionType {
|
||||||
/**
|
NONE = '3', // 隐藏
|
||||||
* 隐藏
|
READ = '1', // 只读
|
||||||
*/
|
WRITE = '2', // 编辑
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
NONE = '3',
|
|
||||||
/**
|
|
||||||
* 只读
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
READ = '1',
|
|
||||||
/**
|
|
||||||
* 编辑
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
WRITE = '2',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const processInstanceLoading = ref(false); // 流程实例的加载中
|
const processInstanceLoading = ref(false); // 流程实例的加载中
|
||||||
@@ -67,6 +56,8 @@ const processInstance = ref<BpmProcessInstanceApi.ProcessInstance>(); // 流程
|
|||||||
const processDefinition = ref<any>({}); // 流程定义
|
const processDefinition = ref<any>({}); // 流程定义
|
||||||
const processModelView = ref<any>({}); // 流程模型视图
|
const processModelView = ref<any>({}); // 流程模型视图
|
||||||
const operationButtonRef = ref(); // 操作按钮组件 ref
|
const operationButtonRef = ref(); // 操作按钮组件 ref
|
||||||
|
const activeTab = ref('form');
|
||||||
|
const taskListRef = ref();
|
||||||
const auditIconsMap: {
|
const auditIconsMap: {
|
||||||
[key: string]:
|
[key: string]:
|
||||||
| typeof SvgBpmApproveIcon
|
| typeof SvgBpmApproveIcon
|
||||||
@@ -82,29 +73,28 @@ const auditIconsMap: {
|
|||||||
[BpmTaskStatusEnum.RETURN]: SvgBpmRejectIcon,
|
[BpmTaskStatusEnum.RETURN]: SvgBpmRejectIcon,
|
||||||
[BpmTaskStatusEnum.WAIT]: SvgBpmRunningIcon,
|
[BpmTaskStatusEnum.WAIT]: SvgBpmRunningIcon,
|
||||||
};
|
};
|
||||||
|
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); // 审批节点信息
|
||||||
|
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
|
||||||
|
|
||||||
// ========== 申请信息 ==========
|
const fApi = ref<any>();
|
||||||
const fApi = ref<any>(); //
|
|
||||||
const detailForm = ref({
|
const detailForm = ref({
|
||||||
rule: [],
|
rule: [],
|
||||||
option: {},
|
option: {},
|
||||||
value: {},
|
value: {},
|
||||||
}); // 流程实例的表单详情
|
}); // 流程实例的表单详情
|
||||||
|
|
||||||
const writableFields: Array<string> = []; // 表单可以编辑的字段
|
const writableFields: Array<string> = []; // 表单可以编辑的字段
|
||||||
|
|
||||||
/** 加载流程实例 */
|
const BusinessFormComponent = shallowRef<any>(null); // 异步组件(业务表单)
|
||||||
const BusinessFormComponent = shallowRef<any>(null); // 异步组件
|
|
||||||
|
|
||||||
/** 获取详情 */
|
/** 获取详情 */
|
||||||
async function getDetail() {
|
async function getDetail() {
|
||||||
// 获得审批详情
|
// 获得审批详情
|
||||||
getApprovalDetail();
|
await getApprovalDetail();
|
||||||
|
|
||||||
// 获得流程模型视图
|
// 获得流程模型视图
|
||||||
getProcessModelView();
|
await getProcessModelView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获得审批详情 */
|
||||||
async function getApprovalDetail() {
|
async function getApprovalDetail() {
|
||||||
processInstanceLoading.value = true;
|
processInstanceLoading.value = true;
|
||||||
try {
|
try {
|
||||||
@@ -114,11 +104,9 @@ async function getApprovalDetail() {
|
|||||||
taskId: props.taskId,
|
taskId: props.taskId,
|
||||||
};
|
};
|
||||||
const data = await getApprovalDetailApi(param);
|
const data = await getApprovalDetailApi(param);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
message.error('查询不到审批详情信息!');
|
message.error('查询不到审批详情信息!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.processDefinition || !data.processInstance) {
|
if (!data.processDefinition || !data.processInstance) {
|
||||||
message.error('查询不到流程信息!');
|
message.error('查询不到流程信息!');
|
||||||
}
|
}
|
||||||
@@ -143,6 +131,7 @@ async function getApprovalDetail() {
|
|||||||
processInstance.value.formVariables,
|
processInstance.value.formVariables,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// TODO @jason:这里 await 来搞?
|
||||||
nextTick().then(() => {
|
nextTick().then(() => {
|
||||||
fApi.value?.btn.show(false);
|
fApi.value?.btn.show(false);
|
||||||
fApi.value?.resetBtn.show(false);
|
fApi.value?.resetBtn.show(false);
|
||||||
@@ -187,11 +176,7 @@ async function getProcessModelView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审批节点信息
|
/** 设置表单权限 */
|
||||||
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]);
|
|
||||||
/**
|
|
||||||
* 设置表单权限
|
|
||||||
*/
|
|
||||||
function setFieldPermission(field: string, permission: string) {
|
function setFieldPermission(field: string, permission: string) {
|
||||||
if (permission === FieldPermissionType.READ) {
|
if (permission === FieldPermissionType.READ) {
|
||||||
fApi.value?.disabled(true, field);
|
fApi.value?.disabled(true, field);
|
||||||
@@ -206,6 +191,7 @@ function setFieldPermission(field: string, permission: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @jason:这个还要么?
|
||||||
/**
|
/**
|
||||||
* 操作成功后刷新
|
* 操作成功后刷新
|
||||||
*/
|
*/
|
||||||
@@ -214,16 +200,13 @@ function setFieldPermission(field: string, permission: string) {
|
|||||||
// getDetail();
|
// getDetail();
|
||||||
// };
|
// };
|
||||||
|
|
||||||
/** 当前的Tab */
|
|
||||||
const activeTab = ref('form');
|
|
||||||
const taskListRef = ref();
|
|
||||||
|
|
||||||
/** 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表 */
|
/** 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表 */
|
||||||
watch(
|
watch(
|
||||||
() => activeTab.value,
|
() => activeTab.value,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal === 'record') {
|
if (newVal === 'record') {
|
||||||
// 如果切换到流转记录标签,刷新任务列表
|
// 如果切换到流转记录标签,刷新任务列表
|
||||||
|
// TODO @jason:await nextTick 要不?
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
taskListRef.value?.refresh();
|
taskListRef.value?.refresh();
|
||||||
});
|
});
|
||||||
@@ -232,7 +215,6 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getDetail();
|
await getDetail();
|
||||||
// 获得用户列表
|
// 获得用户列表
|
||||||
@@ -281,9 +263,9 @@ onMounted(async () => {
|
|||||||
>
|
>
|
||||||
{{ processInstance?.startUser?.nickname.substring(0, 1) }}
|
{{ processInstance?.startUser?.nickname.substring(0, 1) }}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span class="text-sm">{{
|
<span class="text-sm">
|
||||||
processInstance?.startUser?.nickname
|
{{ processInstance?.startUser?.nickname }}
|
||||||
}}</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-gray-500">
|
<div class="text-gray-500">
|
||||||
{{ formatDateTime(processInstance?.startTime) }} 提交
|
{{ formatDateTime(processInstance?.startTime) }} 提交
|
||||||
@@ -324,9 +306,8 @@ onMounted(async () => {
|
|||||||
:rule="detailForm.rule"
|
:rule="detailForm.rule"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-else-if="
|
||||||
processDefinition?.formType === BpmModelFormType.CUSTOM
|
processDefinition?.formType === BpmModelFormType.CUSTOM
|
||||||
"
|
"
|
||||||
class="h-full"
|
class="h-full"
|
||||||
@@ -341,7 +322,6 @@ onMounted(async () => {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|
||||||
<TabPane
|
<TabPane
|
||||||
tab="流程图"
|
tab="流程图"
|
||||||
key="diagram"
|
key="diagram"
|
||||||
@@ -367,7 +347,6 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|
||||||
<TabPane tab="流转记录" key="record" class="tab-pane-content">
|
<TabPane tab="流转记录" key="record" class="tab-pane-content">
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
<BpmProcessInstanceTaskList
|
<BpmProcessInstanceTaskList
|
||||||
@@ -377,7 +356,6 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|
||||||
<!-- TODO 待开发 -->
|
<!-- TODO 待开发 -->
|
||||||
<TabPane
|
<TabPane
|
||||||
tab="流转评论"
|
tab="流转评论"
|
||||||
@@ -410,6 +388,7 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
// @jason:看看能不能通过 tailwindcss 简化下
|
||||||
.ant-tabs-content {
|
.ant-tabs-content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ defineOptions({ name: 'ProcessInstanceBpmnViewer' });
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- TODO @ziye:可以后续,找下 antd 有没可以直接用的组件哈 -->
|
<!-- TODO @jason:这里 BPMN 图的接入 -->
|
||||||
<div>
|
<div>
|
||||||
<h1>BPMN Viewer</h1>
|
<h1>BPMN Viewer</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { createIconifyIcon } from '@vben/icons';
|
|
||||||
|
|
||||||
// bpm 图标
|
|
||||||
// TODO @siye:可以新建出一个 bpm 目录哇?icons/bpm;
|
|
||||||
const SvgBpmRunningIcon = createIconifyIcon('svg:bpm-running');
|
|
||||||
const SvgBpmApproveIcon = createIconifyIcon('svg:bpm-approve');
|
|
||||||
const SvgBpmRejectIcon = createIconifyIcon('svg:bpm-reject');
|
|
||||||
const SvgBpmCancelIcon = createIconifyIcon('svg:bpm-cancel');
|
|
||||||
|
|
||||||
export {
|
|
||||||
SvgBpmApproveIcon,
|
|
||||||
SvgBpmCancelIcon,
|
|
||||||
SvgBpmRejectIcon,
|
|
||||||
SvgBpmRunningIcon,
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
// TODO @jason:你感觉要拆分下,按照表单么?
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import type { Rule } from 'ant-design-vue/es/form';
|
import type { Rule } from 'ant-design-vue/es/form';
|
||||||
|
|
||||||
@@ -51,8 +52,6 @@ import ProcessInstanceTimeline from './time-line.vue';
|
|||||||
|
|
||||||
defineOptions({ name: 'ProcessInstanceBtnContainer' });
|
defineOptions({ name: 'ProcessInstanceBtnContainer' });
|
||||||
|
|
||||||
// 定义 success 事件,用于操作成功后的回调
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
normalForm: any; // 流程表单 formCreate
|
normalForm: any; // 流程表单 formCreate
|
||||||
normalFormApi: any; // 流程表单 formCreate Api
|
normalFormApi: any; // 流程表单 formCreate Api
|
||||||
@@ -68,11 +67,6 @@ const [SignatureModal, signatureModalApi] = useVbenModal({
|
|||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 创建流程表达式 */
|
|
||||||
function openSignatureModal() {
|
|
||||||
signatureModalApi.setData(null).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const userId = userStore.userInfo?.id;
|
const userId = userStore.userInfo?.id;
|
||||||
@@ -90,15 +84,19 @@ const popOverVisible: any = ref({
|
|||||||
}); // 气泡卡是否展示
|
}); // 气泡卡是否展示
|
||||||
const returnList = ref([] as any); // 退回节点
|
const returnList = ref([] as any); // 退回节点
|
||||||
|
|
||||||
|
/** 创建流程表达式 */
|
||||||
|
function openSignatureModal() {
|
||||||
|
signatureModalApi.setData(null).open();
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 审批信息 ==========
|
// ========== 审批信息 ==========
|
||||||
const runningTask = ref<any>(); // 运行中的任务
|
const runningTask = ref<any>(); // 运行中的任务
|
||||||
const approveForm = ref<any>({}); // 审批通过时,额外的补充信息
|
const approveForm = ref<any>({}); // 审批通过时,额外的补充信息
|
||||||
const approveFormFApi = ref<any>({}); // approveForms 的 fAPi
|
const approveFormFApi = ref<any>({}); // approveForms 的 fAPi
|
||||||
const nodeTypeName = ref('审批'); // 节点类型名称
|
const nodeTypeName = ref('审批'); // 节点类型名称
|
||||||
|
|
||||||
// 审批通过意见表单
|
|
||||||
const reasonRequire = ref();
|
const reasonRequire = ref();
|
||||||
const approveFormRef = ref<FormInstance>();
|
const approveFormRef = ref<FormInstance>(); // 审批通过意见表单
|
||||||
const approveSignFormRef = ref();
|
const approveSignFormRef = ref();
|
||||||
const nextAssigneesActivityNode = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>(
|
const nextAssigneesActivityNode = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>(
|
||||||
[],
|
[],
|
||||||
@@ -127,11 +125,10 @@ const approveReasonRule: Record<string, any> = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 拒绝表单
|
|
||||||
const rejectFormRef = ref<FormInstance>();
|
const rejectFormRef = ref<FormInstance>();
|
||||||
const rejectReasonForm = reactive({
|
const rejectReasonForm = reactive({
|
||||||
reason: '',
|
reason: '',
|
||||||
});
|
}); // 拒绝表单
|
||||||
const rejectReasonRule: any = computed(() => {
|
const rejectReasonRule: any = computed(() => {
|
||||||
return {
|
return {
|
||||||
reason: [
|
reason: [
|
||||||
@@ -144,8 +141,7 @@ const rejectReasonRule: any = computed(() => {
|
|||||||
} as Record<string, Rule[]>;
|
} as Record<string, Rule[]>;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 抄送表单
|
const copyFormRef = ref<FormInstance>(); // 抄送表单
|
||||||
const copyFormRef = ref<FormInstance>();
|
|
||||||
const copyForm = reactive({
|
const copyForm = reactive({
|
||||||
copyUserIds: [],
|
copyUserIds: [],
|
||||||
copyReason: '',
|
copyReason: '',
|
||||||
@@ -156,8 +152,7 @@ const copyFormRule: Record<string, Rule[]> = reactive({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 转办表单
|
const transferFormRef = ref<FormInstance>(); // 转办表单
|
||||||
const transferFormRef = ref<FormInstance>();
|
|
||||||
const transferForm = reactive({
|
const transferForm = reactive({
|
||||||
assigneeUserId: undefined,
|
assigneeUserId: undefined,
|
||||||
reason: '',
|
reason: '',
|
||||||
@@ -169,8 +164,7 @@ const transferFormRule: Record<string, Rule[]> = reactive({
|
|||||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 委派表单
|
const delegateFormRef = ref<FormInstance>(); // 委派表单
|
||||||
const delegateFormRef = ref<FormInstance>();
|
|
||||||
const delegateForm = reactive({
|
const delegateForm = reactive({
|
||||||
delegateUserId: undefined,
|
delegateUserId: undefined,
|
||||||
reason: '',
|
reason: '',
|
||||||
@@ -182,8 +176,7 @@ const delegateFormRule: Record<string, Rule[]> = reactive({
|
|||||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 加签表单
|
const addSignFormRef = ref<FormInstance>(); // 加签表单
|
||||||
const addSignFormRef = ref<FormInstance>();
|
|
||||||
const addSignForm = reactive({
|
const addSignForm = reactive({
|
||||||
addSignUserIds: undefined,
|
addSignUserIds: undefined,
|
||||||
reason: '',
|
reason: '',
|
||||||
@@ -195,8 +188,7 @@ const addSignFormRule: Record<string, Rule[]> = reactive({
|
|||||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 减签表单
|
const deleteSignFormRef = ref<FormInstance>(); // 减签表单
|
||||||
const deleteSignFormRef = ref<FormInstance>();
|
|
||||||
const deleteSignForm = reactive({
|
const deleteSignForm = reactive({
|
||||||
deleteSignTaskId: undefined,
|
deleteSignTaskId: undefined,
|
||||||
reason: '',
|
reason: '',
|
||||||
@@ -208,8 +200,7 @@ const deleteSignFormRule: Record<string, Rule[]> = reactive({
|
|||||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 退回表单
|
const returnFormRef = ref<FormInstance>(); // 退回表单
|
||||||
const returnFormRef = ref<FormInstance>();
|
|
||||||
const returnForm = reactive({
|
const returnForm = reactive({
|
||||||
targetTaskDefinitionKey: undefined,
|
targetTaskDefinitionKey: undefined,
|
||||||
returnReason: '',
|
returnReason: '',
|
||||||
@@ -223,9 +214,7 @@ const returnFormRule: Record<string, Rule[]> = reactive({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 取消表单
|
const cancelFormRef = ref<FormInstance>(); // 取消表单
|
||||||
const cancelFormRef = ref<FormInstance>();
|
|
||||||
|
|
||||||
const cancelForm = reactive({
|
const cancelForm = reactive({
|
||||||
cancelReason: '',
|
cancelReason: '',
|
||||||
});
|
});
|
||||||
@@ -256,7 +245,7 @@ async function openPopover(type: string) {
|
|||||||
message.warning('表单校验不通过,请先完善表单!!');
|
message.warning('表单校验不通过,请先完善表单!!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
initNextAssigneesFormField();
|
await initNextAssigneesFormField();
|
||||||
}
|
}
|
||||||
if (type === 'return') {
|
if (type === 'return') {
|
||||||
// 获取退回节点
|
// 获取退回节点
|
||||||
@@ -269,6 +258,7 @@ async function openPopover(type: string) {
|
|||||||
Object.keys(popOverVisible.value).forEach((item) => {
|
Object.keys(popOverVisible.value).forEach((item) => {
|
||||||
if (popOverVisible.value[item]) popOverVisible.value[item] = item === type;
|
if (popOverVisible.value[item]) popOverVisible.value[item] = item === type;
|
||||||
});
|
});
|
||||||
|
// TODO @jason:下面这 2 行,要删除么?
|
||||||
// await nextTick()
|
// await nextTick()
|
||||||
// formRef.value.resetFields()
|
// formRef.value.resetFields()
|
||||||
}
|
}
|
||||||
@@ -333,6 +323,7 @@ async function initNextAssigneesFormField() {
|
|||||||
function selectNextAssigneesConfirm(id: string, userList: any[]) {
|
function selectNextAssigneesConfirm(id: string, userList: any[]) {
|
||||||
approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id);
|
approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 审批通过时,校验每个自选审批人的节点是否都已配置了审批人 */
|
/** 审批通过时,校验每个自选审批人的节点是否都已配置了审批人 */
|
||||||
function validateNextAssignees() {
|
function validateNextAssignees() {
|
||||||
if (Object.keys(nextAssigneesActivityNode.value).length === 0) {
|
if (Object.keys(nextAssigneesActivityNode.value).length === 0) {
|
||||||
@@ -438,7 +429,9 @@ async function handleTransfer() {
|
|||||||
formLoading.value = true;
|
formLoading.value = true;
|
||||||
try {
|
try {
|
||||||
// 1.1 校验表单
|
// 1.1 校验表单
|
||||||
if (!transferFormRef.value) return;
|
if (!transferFormRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await transferFormRef.value.validate();
|
await transferFormRef.value.validate();
|
||||||
// 1.2 提交转交
|
// 1.2 提交转交
|
||||||
const data = {
|
const data = {
|
||||||
@@ -470,7 +463,6 @@ async function handleDelegate() {
|
|||||||
reason: delegateForm.reason,
|
reason: delegateForm.reason,
|
||||||
delegateUserId: delegateForm.delegateUserId,
|
delegateUserId: delegateForm.delegateUserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
await TaskApi.delegateTask(data);
|
await TaskApi.delegateTask(data);
|
||||||
popOverVisible.value.delegate = false;
|
popOverVisible.value.delegate = false;
|
||||||
delegateFormRef.value.resetFields();
|
delegateFormRef.value.resetFields();
|
||||||
@@ -520,7 +512,6 @@ async function handleReturn() {
|
|||||||
reason: returnForm.returnReason,
|
reason: returnForm.returnReason,
|
||||||
targetTaskDefinitionKey: returnForm.targetTaskDefinitionKey,
|
targetTaskDefinitionKey: returnForm.targetTaskDefinitionKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
await TaskApi.returnTask(data);
|
await TaskApi.returnTask(data);
|
||||||
popOverVisible.value.return = false;
|
popOverVisible.value.return = false;
|
||||||
returnFormRef.value.resetFields();
|
returnFormRef.value.resetFields();
|
||||||
@@ -557,11 +548,11 @@ async function handleCancel() {
|
|||||||
/** 处理再次提交 */
|
/** 处理再次提交 */
|
||||||
async function handleReCreate() {
|
async function handleReCreate() {
|
||||||
// 跳转发起流程界面
|
// 跳转发起流程界面
|
||||||
|
// TODO @jason:这个要优化成 push 到 name 么?这样后续 path 可以按需调整;
|
||||||
await router.push({
|
await router.push({
|
||||||
path: '/bpm/task/create',
|
path: '/bpm/task/create',
|
||||||
query: { processInstanceId: props.processInstance?.id },
|
query: { processInstanceId: props.processInstance?.id },
|
||||||
});
|
});
|
||||||
// router.push('/bpm/task/my');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取减签人员标签 */
|
/** 获取减签人员标签 */
|
||||||
@@ -570,6 +561,7 @@ function getDeleteSignUserLabel(task: any): string {
|
|||||||
const nickname = task?.assigneeUser?.nickname || task?.ownerUser?.nickname;
|
const nickname = task?.assigneeUser?.nickname || task?.ownerUser?.nickname;
|
||||||
return `${nickname} ( 所属部门:${deptName} )`;
|
return `${nickname} ( 所属部门:${deptName} )`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理减签 */
|
/** 处理减签 */
|
||||||
async function handlerDeleteSign() {
|
async function handlerDeleteSign() {
|
||||||
formLoading.value = true;
|
formLoading.value = true;
|
||||||
@@ -592,6 +584,7 @@ async function handlerDeleteSign() {
|
|||||||
formLoading.value = false;
|
formLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 重新加载数据 */
|
/** 重新加载数据 */
|
||||||
function reload() {
|
function reload() {
|
||||||
emit('success');
|
emit('success');
|
||||||
@@ -643,6 +636,7 @@ function getButtonDisplayName(btnType: BpmTaskOperationButtonTypeEnum) {
|
|||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 加载待办任务 */
|
||||||
function loadTodoTask(task: any) {
|
function loadTodoTask(task: any) {
|
||||||
approveForm.value = {};
|
approveForm.value = {};
|
||||||
runningTask.value = task;
|
runningTask.value = task;
|
||||||
@@ -650,7 +644,7 @@ function loadTodoTask(task: any) {
|
|||||||
reasonRequire.value = task?.reasonRequire ?? false;
|
reasonRequire.value = task?.reasonRequire ?? false;
|
||||||
nodeTypeName.value =
|
nodeTypeName.value =
|
||||||
task?.nodeType === BpmNodeTypeEnum.TRANSACTOR_NODE ? '办理' : '审批';
|
task?.nodeType === BpmNodeTypeEnum.TRANSACTOR_NODE ? '办理' : '审批';
|
||||||
// 处理 approve 表单.
|
// 处理 approve 表单
|
||||||
if (task && task.formId && task.formConf) {
|
if (task && task.formId && task.formConf) {
|
||||||
const tempApproveForm = {};
|
const tempApproveForm = {};
|
||||||
setConfAndFields2(
|
setConfAndFields2(
|
||||||
@@ -707,6 +701,7 @@ defineExpose({ loadTodoTask });
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
<!-- TODO @jason:这里要删除么? -->
|
||||||
<!-- <div>是否处理中 {{ !!isHandleTaskStatus() }}</div> -->
|
<!-- <div>是否处理中 {{ !!isHandleTaskStatus() }}</div> -->
|
||||||
|
|
||||||
<!-- 【通过】按钮 -->
|
<!-- 【通过】按钮 -->
|
||||||
@@ -777,7 +772,6 @@ defineExpose({ loadTodoTask });
|
|||||||
<Button @click="openSignatureModal" type="primary">
|
<Button @click="openSignatureModal" type="primary">
|
||||||
{{ approveReasonForm.signPicUrl ? '重新签名' : '点击签名' }}
|
{{ approveReasonForm.signPicUrl ? '重新签名' : '点击签名' }}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<Image
|
<Image
|
||||||
class="float-left h-40 w-80"
|
class="float-left h-40 w-80"
|
||||||
@@ -786,7 +780,6 @@ defineExpose({ loadTodoTask });
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem :label="`${nodeTypeName}意见`" name="reason">
|
<FormItem :label="`${nodeTypeName}意见`" name="reason">
|
||||||
<Textarea
|
<Textarea
|
||||||
v-model:value="approveReasonForm.reason"
|
v-model:value="approveReasonForm.reason"
|
||||||
@@ -794,7 +787,6 @@ defineExpose({ loadTodoTask });
|
|||||||
:rows="4"
|
:rows="4"
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
@@ -902,6 +894,7 @@ defineExpose({ loadTodoTask });
|
|||||||
label-width="100px"
|
label-width="100px"
|
||||||
>
|
>
|
||||||
<FormItem label="抄送人" name="copyUserIds">
|
<FormItem label="抄送人" name="copyUserIds">
|
||||||
|
<!-- TODO @jason:看看是不是用 看看能不能通过 tailwindcss 简化下 style -->
|
||||||
<Select
|
<Select
|
||||||
v-model:value="copyForm.copyUserIds"
|
v-model:value="copyForm.copyUserIds"
|
||||||
:allow-clear="true"
|
:allow-clear="true"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { IconifyIcon } from '@vben/icons';
|
|||||||
import { base64ToFile } from '@vben/utils';
|
import { base64ToFile } from '@vben/utils';
|
||||||
|
|
||||||
import { Button, message, Space, Tooltip } from 'ant-design-vue';
|
import { Button, message, Space, Tooltip } from 'ant-design-vue';
|
||||||
// TODO @ziye:这个可能,适合放到全局?!因为 element-plus 也用这个;
|
|
||||||
import Vue3Signature from 'vue3-signature';
|
import Vue3Signature from 'vue3-signature';
|
||||||
|
|
||||||
import { uploadFile } from '#/api/infra/file';
|
import { uploadFile } from '#/api/infra/file';
|
||||||
@@ -20,28 +19,29 @@ const emits = defineEmits(['success']);
|
|||||||
const signature = ref<InstanceType<typeof Vue3Signature>>();
|
const signature = ref<InstanceType<typeof Vue3Signature>>();
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
title: '流程签名',
|
|
||||||
onOpenChange(visible) {
|
|
||||||
if (!visible) {
|
|
||||||
modalApi.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
|
// TODO @jason:这里需要使用类似 modalApi.lock() 么?类似别的模块
|
||||||
message.success({
|
message.success({
|
||||||
content: '签名上传中请稍等。。。',
|
content: '签名上传中,请稍等...',
|
||||||
});
|
});
|
||||||
const signFileUrl = await uploadFile({
|
const signFileUrl = await uploadFile({
|
||||||
file: base64ToFile(signature?.value?.save('image/jpeg') || '', '签名'),
|
file: base64ToFile(signature?.value?.save('image/jpeg') || '', '签名'),
|
||||||
});
|
});
|
||||||
emits('success', signFileUrl);
|
emits('success', signFileUrl);
|
||||||
// TODO @ziye:下面有个告警哈;ps:所有告警,皆是错误,可以关注 ide 给的提示哈;
|
// TODO @jason:是不是不用主动 close?
|
||||||
|
await modalApi.close();
|
||||||
|
},
|
||||||
|
// TODO @jason:这个是不是下面方法,可以删除;
|
||||||
|
onOpenChange(visible) {
|
||||||
|
if (!visible) {
|
||||||
modalApi.close();
|
modalApi.close();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="h-2/5 w-3/5">
|
<Modal title="流程签名" class="h-2/5 w-3/5">
|
||||||
<div class="mb-2 flex justify-end">
|
<div class="mb-2 flex justify-end">
|
||||||
<Space>
|
<Space>
|
||||||
<Tooltip title="撤销上一步操作">
|
<Tooltip title="撤销上一步操作">
|
||||||
@@ -52,7 +52,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
撤销
|
撤销
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip title="清空画布">
|
<Tooltip title="清空画布">
|
||||||
<Button @click="signature?.clear()">
|
<Button @click="signature?.clear()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ const props = withDefaults(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const simpleModel = ref<any>({});
|
const simpleModel = ref<any>({});
|
||||||
// 用户任务
|
const tasks = ref([]); // 用户任务
|
||||||
const tasks = ref([]);
|
const processInstance = ref(); // 流程实例
|
||||||
// 流程实例
|
|
||||||
const processInstance = ref();
|
|
||||||
|
|
||||||
/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */
|
/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */
|
||||||
watch(
|
watch(
|
||||||
@@ -69,6 +67,7 @@ watch(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSimpleModelNodeTaskStatus = (
|
const setSimpleModelNodeTaskStatus = (
|
||||||
simpleModel: SimpleFlowNode | undefined,
|
simpleModel: SimpleFlowNode | undefined,
|
||||||
processStatus: number,
|
processStatus: number,
|
||||||
@@ -102,7 +101,7 @@ const setSimpleModelNodeTaskStatus = (
|
|||||||
} else if (finishedActivityIds.includes(simpleModel.id)) {
|
} else if (finishedActivityIds.includes(simpleModel.id)) {
|
||||||
simpleModel.activityStatus = BpmTaskStatusEnum.APPROVE;
|
simpleModel.activityStatus = BpmTaskStatusEnum.APPROVE;
|
||||||
}
|
}
|
||||||
// TODO 是不是还缺一个 cancel 的状态
|
// TODO 是不是还缺一个 cancel 的状态 @jason:
|
||||||
}
|
}
|
||||||
// 抄送节点
|
// 抄送节点
|
||||||
if (simpleModel.type === BpmNodeTypeEnum.COPY_TASK_NODE) {
|
if (simpleModel.type === BpmNodeTypeEnum.COPY_TASK_NODE) {
|
||||||
@@ -177,5 +176,3 @@ const setSimpleModelNodeTaskStatus = (
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import type { formCreate } from '@form-create/antd-designer';
|
import type { formCreate } from '@form-create/antd-designer';
|
||||||
|
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { BpmTaskApi } from '#/api/bpm/task';
|
import type { BpmTaskApi } from '#/api/bpm/task';
|
||||||
|
|
||||||
import { nextTick, onMounted, ref, shallowRef } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
@@ -25,8 +25,16 @@ const props = defineProps<{
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 使用 shallowRef 减少不必要的深度响应
|
/** 表单类型定义 */
|
||||||
const columns = shallowRef([
|
interface TaskForm {
|
||||||
|
rule: any[];
|
||||||
|
option: Record<string, any>;
|
||||||
|
value: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取表格列配置 */
|
||||||
|
function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '审批节点',
|
title: '审批节点',
|
||||||
@@ -77,12 +85,59 @@ const columns = shallowRef([
|
|||||||
minWidth: 180,
|
minWidth: 180,
|
||||||
formatter: 'formatPast2',
|
formatter: 'formatPast2',
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRef = ref<formCreate>();
|
||||||
|
const taskForm = ref<TaskForm>({
|
||||||
|
rule: [],
|
||||||
|
option: {},
|
||||||
|
value: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
title: '查看表单',
|
||||||
|
footer: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function handleRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 显示表单详情 */
|
||||||
|
async function handleShowFormDetail(row: BpmTaskApi.TaskManager) {
|
||||||
|
// 设置表单配置和表单字段
|
||||||
|
taskForm.value = {
|
||||||
|
rule: [],
|
||||||
|
option: {},
|
||||||
|
value: row,
|
||||||
|
};
|
||||||
|
setConfAndFields2(
|
||||||
|
taskForm,
|
||||||
|
row.formConf,
|
||||||
|
row.formFields || [],
|
||||||
|
row.formVariables || {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
modalApi.open();
|
||||||
|
// 等待表单渲染
|
||||||
|
await nextTick();
|
||||||
|
// 获取表单 API 实例
|
||||||
|
const formApi = formRef.value?.fapi;
|
||||||
|
if (!formApi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置表单不可编辑
|
||||||
|
formApi.btn.show(false);
|
||||||
|
formApi.resetBtn.show(false);
|
||||||
|
formApi.disabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Grid配置和API
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: columns.value,
|
columns: useGridColumns(),
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
showFooter: true,
|
showFooter: true,
|
||||||
border: true,
|
border: true,
|
||||||
@@ -96,6 +151,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
pagerConfig: {
|
pagerConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -103,84 +159,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
cellConfig: {
|
} as VxeTableGridOptions<BpmTaskApi.TaskManager>,
|
||||||
height: 60,
|
|
||||||
},
|
|
||||||
} as VxeTableGridOptions<BpmTaskApi.Task>,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新表格数据
|
|
||||||
*/
|
|
||||||
function refresh() {
|
|
||||||
gridApi.query();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单相关
|
|
||||||
interface TaskForm {
|
|
||||||
rule: any[];
|
|
||||||
option: Record<string, any>;
|
|
||||||
value: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义表单组件引用类型
|
|
||||||
|
|
||||||
// 使用明确的类型定义
|
|
||||||
const formRef = ref<formCreate>();
|
|
||||||
const taskForm = ref<TaskForm>({
|
|
||||||
rule: [],
|
|
||||||
option: {},
|
|
||||||
value: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示表单详情
|
|
||||||
* @param row 任务数据
|
|
||||||
*/
|
|
||||||
async function showFormDetail(row: BpmTaskApi.TaskManager): Promise<void> {
|
|
||||||
// 设置表单配置和表单字段
|
|
||||||
taskForm.value = {
|
|
||||||
rule: [],
|
|
||||||
option: {},
|
|
||||||
value: row,
|
|
||||||
};
|
|
||||||
|
|
||||||
setConfAndFields2(
|
|
||||||
taskForm,
|
|
||||||
row.formConf,
|
|
||||||
row.formFields || [],
|
|
||||||
row.formVariables || {},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 打开弹窗
|
|
||||||
modalApi.open();
|
|
||||||
|
|
||||||
// 等待表单渲染
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
// 获取表单API实例
|
|
||||||
const formApi = formRef.value?.fapi;
|
|
||||||
if (!formApi) return;
|
|
||||||
|
|
||||||
// 设置表单不可编辑
|
|
||||||
formApi.btn.show(false);
|
|
||||||
formApi.resetBtn.show(false);
|
|
||||||
formApi.disabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单查看模态框
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
|
||||||
title: '查看表单',
|
|
||||||
footer: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
refresh();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 暴露刷新方法给父组件
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
refresh,
|
refresh: handleRefresh,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -191,14 +174,13 @@ defineExpose({
|
|||||||
<div class="flex flex-wrap items-center justify-center">
|
<div class="flex flex-wrap items-center justify-center">
|
||||||
<span v-if="row.reason">{{ row.reason }}</span>
|
<span v-if="row.reason">{{ row.reason }}</span>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
v-if="row.formId > 0"
|
v-if="row.formId > 0"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="showFormDetail(row)"
|
|
||||||
size="small"
|
size="small"
|
||||||
ghost
|
ghost
|
||||||
class="ml-1"
|
class="ml-1"
|
||||||
|
@click="handleShowFormDetail(row)"
|
||||||
>
|
>
|
||||||
<IconifyIcon icon="lucide:file-text" />
|
<IconifyIcon icon="lucide:file-text" />
|
||||||
<span class="!ml-0.5 text-xs">查看表单</span>
|
<span class="!ml-0.5 text-xs">查看表单</span>
|
||||||
|
|||||||
@@ -38,34 +38,21 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
// 状态图标映射
|
|
||||||
const statusIconMap: Record<
|
const statusIconMap: Record<
|
||||||
string,
|
string,
|
||||||
{ animation?: string; color: string; icon: string }
|
{ animation?: string; color: string; icon: string }
|
||||||
> = {
|
> = {
|
||||||
// 跳过
|
'-2': { color: '#909398', icon: 'mdi:skip-forward-outline' }, // 跳过
|
||||||
'-2': { color: '#909398', icon: 'mdi:skip-forward-outline' },
|
'-1': { color: '#909398', icon: 'mdi:clock-outline' }, // 审批未开始
|
||||||
// 审批未开始
|
'0': { color: '#ff943e', icon: 'mdi:loading', animation: 'animate-spin' }, // 待审批
|
||||||
'-1': { color: '#909398', icon: 'mdi:clock-outline' },
|
'1': { color: '#448ef7', icon: 'mdi:loading', animation: 'animate-spin' }, // 审批中
|
||||||
// 待审批
|
'2': { color: '#00b32a', icon: 'mdi:check' }, // 审批通过
|
||||||
'0': { color: '#ff943e', icon: 'mdi:loading', animation: 'animate-spin' },
|
'3': { color: '#f46b6c', icon: 'mdi:close' }, // 审批不通过
|
||||||
// 审批中
|
'4': { color: '#cccccc', icon: 'mdi:trash-can-outline' }, // 已取消
|
||||||
'1': { color: '#448ef7', icon: 'mdi:loading', animation: 'animate-spin' },
|
'5': { color: '#f46b6c', icon: 'mdi:arrow-left' }, // 退回
|
||||||
// 审批通过
|
'6': { color: '#448ef7', icon: 'mdi:clock-outline' }, // 委派中
|
||||||
'2': { color: '#00b32a', icon: 'mdi:check' },
|
'7': { color: '#00b32a', icon: 'mdi:check' }, // 审批通过中
|
||||||
// 审批不通过
|
}; // 状态图标映射
|
||||||
'3': { color: '#f46b6c', icon: 'mdi:close' },
|
|
||||||
// 已取消
|
|
||||||
'4': { color: '#cccccc', icon: 'mdi:trash-can-outline' },
|
|
||||||
// 退回
|
|
||||||
'5': { color: '#f46b6c', icon: 'mdi:arrow-left' },
|
|
||||||
// 委派中
|
|
||||||
'6': { color: '#448ef7', icon: 'mdi:clock-outline' },
|
|
||||||
// 审批通过中
|
|
||||||
'7': { color: '#00b32a', icon: 'mdi:check' },
|
|
||||||
};
|
|
||||||
|
|
||||||
// 节点类型图标映射
|
|
||||||
const nodeTypeSvgMap = {
|
const nodeTypeSvgMap = {
|
||||||
// 结束节点
|
// 结束节点
|
||||||
[BpmNodeTypeEnum.END_EVENT_NODE]: {
|
[BpmNodeTypeEnum.END_EVENT_NODE]: {
|
||||||
@@ -107,40 +94,39 @@ const nodeTypeSvgMap = {
|
|||||||
color: '#14bb83',
|
color: '#14bb83',
|
||||||
icon: 'icon-park-outline:tree-diagram',
|
icon: 'icon-park-outline:tree-diagram',
|
||||||
},
|
},
|
||||||
} as Record<BpmNodeTypeEnum, { color: string; icon: string }>;
|
} as Record<BpmNodeTypeEnum, { color: string; icon: string }>; // 节点类型图标映射
|
||||||
|
const onlyStatusIconShow = [-1, 0, 1]; // 只有状态是 -1、0、1 才展示头像右小角状态小 icon
|
||||||
|
|
||||||
// 只有状态是 -1、0、1 才展示头像右小角状态小icon
|
/** 获取审批节点类型图标 */
|
||||||
const onlyStatusIconShow = [-1, 0, 1];
|
|
||||||
|
|
||||||
// 获取审批节点类型图标
|
|
||||||
function getApprovalNodeTypeIcon(nodeType: BpmNodeTypeEnum) {
|
function getApprovalNodeTypeIcon(nodeType: BpmNodeTypeEnum) {
|
||||||
return nodeTypeSvgMap[nodeType]?.icon;
|
return nodeTypeSvgMap[nodeType]?.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取审批节点图标
|
/** 获取审批节点图标 */
|
||||||
function getApprovalNodeIcon(taskStatus: number, nodeType: BpmNodeTypeEnum) {
|
function getApprovalNodeIcon(taskStatus: number, nodeType: BpmNodeTypeEnum) {
|
||||||
if (taskStatus === BpmTaskStatusEnum.NOT_START) {
|
if (taskStatus === BpmTaskStatusEnum.NOT_START) {
|
||||||
return statusIconMap[taskStatus]?.icon || 'mdi:clock-outline';
|
return statusIconMap[taskStatus]?.icon || 'mdi:clock-outline';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
nodeType === BpmNodeTypeEnum.START_USER_NODE ||
|
[
|
||||||
nodeType === BpmNodeTypeEnum.USER_TASK_NODE ||
|
BpmNodeTypeEnum.START_USER_NODE,
|
||||||
nodeType === BpmNodeTypeEnum.TRANSACTOR_NODE ||
|
BpmNodeTypeEnum.USER_TASK_NODE,
|
||||||
nodeType === BpmNodeTypeEnum.CHILD_PROCESS_NODE ||
|
BpmNodeTypeEnum.TRANSACTOR_NODE,
|
||||||
nodeType === BpmNodeTypeEnum.END_EVENT_NODE
|
BpmNodeTypeEnum.CHILD_PROCESS_NODE,
|
||||||
|
BpmNodeTypeEnum.END_EVENT_NODE,
|
||||||
|
].includes(nodeType)
|
||||||
) {
|
) {
|
||||||
return statusIconMap[taskStatus]?.icon || 'mdi:clock-outline';
|
return statusIconMap[taskStatus]?.icon || 'mdi:clock-outline';
|
||||||
}
|
}
|
||||||
return 'mdi:clock-outline';
|
return 'mdi:clock-outline';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取审批节点颜色
|
/** 获取审批节点颜色 */
|
||||||
function getApprovalNodeColor(taskStatus: number) {
|
function getApprovalNodeColor(taskStatus: number) {
|
||||||
return statusIconMap[taskStatus]?.color;
|
return statusIconMap[taskStatus]?.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取审批节点时间
|
/** 获取审批节点时间 */
|
||||||
function getApprovalNodeTime(node: BpmProcessInstanceApi.ApprovalNodeInfo) {
|
function getApprovalNodeTime(node: BpmProcessInstanceApi.ApprovalNodeInfo) {
|
||||||
if (node.nodeType === BpmNodeTypeEnum.START_USER_NODE && node.startTime) {
|
if (node.nodeType === BpmNodeTypeEnum.START_USER_NODE && node.startTime) {
|
||||||
return formatDateTime(node.startTime);
|
return formatDateTime(node.startTime);
|
||||||
@@ -154,7 +140,6 @@ function getApprovalNodeTime(node: BpmProcessInstanceApi.ApprovalNodeInfo) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择自定义审批人
|
|
||||||
const [UserSelectModalComp, userSelectModalApi] = useVbenModal({
|
const [UserSelectModalComp, userSelectModalApi] = useVbenModal({
|
||||||
connectedComponent: UserSelectModal,
|
connectedComponent: UserSelectModal,
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
@@ -162,7 +147,7 @@ const [UserSelectModalComp, userSelectModalApi] = useVbenModal({
|
|||||||
const selectedActivityNodeId = ref<string>();
|
const selectedActivityNodeId = ref<string>();
|
||||||
const customApproveUsers = ref<Record<string, any[]>>({}); // key:activityId,value:用户列表
|
const customApproveUsers = ref<Record<string, any[]>>({}); // key:activityId,value:用户列表
|
||||||
|
|
||||||
// 打开选择用户弹窗
|
/** 打开选择用户弹窗 */
|
||||||
const handleSelectUser = (activityId: string, selectedList: any[]) => {
|
const handleSelectUser = (activityId: string, selectedList: any[]) => {
|
||||||
selectedActivityNodeId.value = activityId;
|
selectedActivityNodeId.value = activityId;
|
||||||
userSelectModalApi
|
userSelectModalApi
|
||||||
@@ -170,7 +155,7 @@ const handleSelectUser = (activityId: string, selectedList: any[]) => {
|
|||||||
.open();
|
.open();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 选择用户完成
|
/** 选择用户完成 */
|
||||||
const selectedUsers = ref<number[]>([]);
|
const selectedUsers = ref<number[]>([]);
|
||||||
function handleUserSelectConfirm(userList: any[]) {
|
function handleUserSelectConfirm(userList: any[]) {
|
||||||
if (!selectedActivityNodeId.value) {
|
if (!selectedActivityNodeId.value) {
|
||||||
@@ -194,7 +179,7 @@ function handleChildProcess(activity: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否需要显示自定义选择审批人
|
/** 判断是否需要显示自定义选择审批人 */
|
||||||
function shouldShowCustomUserSelect(
|
function shouldShowCustomUserSelect(
|
||||||
activity: BpmProcessInstanceApi.ApprovalNodeInfo,
|
activity: BpmProcessInstanceApi.ApprovalNodeInfo,
|
||||||
) {
|
) {
|
||||||
@@ -209,7 +194,7 @@ function shouldShowCustomUserSelect(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否需要显示审批意见
|
/** 判断是否需要显示审批意见 */
|
||||||
function shouldShowApprovalReason(task: any, nodeType: BpmNodeTypeEnum) {
|
function shouldShowApprovalReason(task: any, nodeType: BpmNodeTypeEnum) {
|
||||||
return (
|
return (
|
||||||
task.reason &&
|
task.reason &&
|
||||||
@@ -219,12 +204,12 @@ function shouldShowApprovalReason(task: any, nodeType: BpmNodeTypeEnum) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户选择弹窗关闭
|
/** 用户选择弹窗关闭 */
|
||||||
function handleUserSelectClosed() {
|
function handleUserSelectClosed() {
|
||||||
selectedUsers.value = [];
|
selectedUsers.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户选择弹窗取消
|
/** 用户选择弹窗取消 */
|
||||||
function handleUserSelectCancel() {
|
function handleUserSelectCancel() {
|
||||||
selectedUsers.value = [];
|
selectedUsers.value = [];
|
||||||
}
|
}
|
||||||
@@ -241,7 +226,6 @@ const batchSetCustomApproveUsers = (data: Record<string, any[]>) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 暴露方法给父组件
|
|
||||||
defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
|
defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -264,10 +248,9 @@ defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
|
|||||||
class="size-6 text-white"
|
class="size-6 text-white"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="showStatusIcon"
|
v-if="showStatusIcon"
|
||||||
class="absolute right--2.5 top-4 flex size-5 items-center rounded-full border-2 border-solid border-white p-0.5"
|
class="absolute left-4 top-4 flex size-4 items-center rounded-full border-2 border-solid border-white p-0.5"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundColor: getApprovalNodeColor(activity.status),
|
backgroundColor: getApprovalNodeColor(activity.status),
|
||||||
}"
|
}"
|
||||||
@@ -409,14 +392,14 @@ defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
|
|||||||
v-if="
|
v-if="
|
||||||
showStatusIcon && onlyStatusIconShow.includes(task.status)
|
showStatusIcon && onlyStatusIconShow.includes(task.status)
|
||||||
"
|
"
|
||||||
class="absolute left-6 top-5 flex items-center rounded-full border-2 border-solid border-white p-1"
|
class="absolute left-5 top-5 flex items-center rounded-full border-2 border-solid border-white p-1"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundColor: statusIconMap[task.status]?.color,
|
backgroundColor: statusIconMap[task.status]?.color,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
:icon="statusIconMap[task.status]?.icon || 'lucide:clock'"
|
:icon="statusIconMap[task.status]?.icon || 'lucide:clock'"
|
||||||
class="size-2 text-white"
|
class="size-1.5 text-white"
|
||||||
:class="[statusIconMap[task.status]?.animation]"
|
:class="[statusIconMap[task.status]?.animation]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 939 B After Width: | Height: | Size: 939 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -34,6 +34,12 @@ const SvgWxBarIcon = createIconifyIcon('svg:wx-bar');
|
|||||||
const SvgWalletIcon = createIconifyIcon('svg:wallet');
|
const SvgWalletIcon = createIconifyIcon('svg:wallet');
|
||||||
const SvgMockIcon = createIconifyIcon('svg:mock');
|
const SvgMockIcon = createIconifyIcon('svg:mock');
|
||||||
|
|
||||||
|
/** BPM */
|
||||||
|
const SvgBpmRunningIcon = createIconifyIcon('svg:bpm-running');
|
||||||
|
const SvgBpmApproveIcon = createIconifyIcon('svg:bpm-approve');
|
||||||
|
const SvgBpmRejectIcon = createIconifyIcon('svg:bpm-reject');
|
||||||
|
const SvgBpmCancelIcon = createIconifyIcon('svg:bpm-cancel');
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SvgAlipayAppIcon,
|
SvgAlipayAppIcon,
|
||||||
SvgAlipayBarIcon,
|
SvgAlipayBarIcon,
|
||||||
@@ -46,6 +52,10 @@ export {
|
|||||||
SvgAvatar3Icon,
|
SvgAvatar3Icon,
|
||||||
SvgAvatar4Icon,
|
SvgAvatar4Icon,
|
||||||
SvgBellIcon,
|
SvgBellIcon,
|
||||||
|
SvgBpmApproveIcon,
|
||||||
|
SvgBpmCancelIcon,
|
||||||
|
SvgBpmRejectIcon,
|
||||||
|
SvgBpmRunningIcon,
|
||||||
SvgCakeIcon,
|
SvgCakeIcon,
|
||||||
SvgCardIcon,
|
SvgCardIcon,
|
||||||
SvgDingDingIcon,
|
SvgDingDingIcon,
|
||||||
|
|||||||