diff --git a/apps/web-antd/src/api/bpm/model/index.ts b/apps/web-antd/src/api/bpm/model/index.ts index 4f9a51c10..713fedc47 100644 --- a/apps/web-antd/src/api/bpm/model/index.ts +++ b/apps/web-antd/src/api/bpm/model/index.ts @@ -18,6 +18,7 @@ export namespace BpmModelApi { deploymentTime: number; suspensionState: number; formType?: number; + formCustomViewPath?: string; } /** 流程模型 VO */ diff --git a/apps/web-antd/src/api/bpm/processInstance/index.ts b/apps/web-antd/src/api/bpm/processInstance/index.ts index cec0a1fdd..f287ee0be 100644 --- a/apps/web-antd/src/api/bpm/processInstance/index.ts +++ b/apps/web-antd/src/api/bpm/processInstance/index.ts @@ -1,7 +1,7 @@ import type { PageParam, PageResult } from '@vben/request'; import type { BpmModelApi } from '#/api/bpm/model'; -import type { CandidateStrategy, NodeType } from '#/utils'; +import type { CandidateStrategyEnum, NodeTypeEnum } from '#/utils'; import { requestClient } from '#/api/request'; @@ -29,12 +29,12 @@ export namespace BpmProcessInstanceApi { // 审批节点信息 export type ApprovalNodeInfo = { - candidateStrategy?: CandidateStrategy; + candidateStrategy?: CandidateStrategyEnum; candidateUsers?: User[]; endTime?: Date; id: number; name: string; - nodeType: NodeType; + nodeType: NodeTypeEnum; startTime?: Date; status: number; tasks: ApprovalTaskInfo[]; @@ -46,14 +46,25 @@ export namespace BpmProcessInstanceApi { createTime: string; endTime: string; fields: string[]; + formVariables: Record; id: number; name: string; processDefinition?: BpmModelApi.ProcessDefinitionVO; processDefinitionId: string; remark: string; result: number; + startTime?: Date; + startUser?: User; + status: number; + tasks?: BpmProcessInstanceApi.Task[]; + }; + + export type ApprovalDetail = { + activityNodes: BpmProcessInstanceApi.ApprovalNodeInfo[]; + formFieldsPermission: any; + processDefinition: BpmModelApi.ProcessDefinitionVO; + processInstance: BpmProcessInstanceApi.ProcessInstanceVO; status: number; - tasks: BpmProcessInstanceApi.Task[]; }; } @@ -133,7 +144,7 @@ export async function updateProcessInstance( /** 获取审批详情 */ export async function getApprovalDetail(params: any) { - return requestClient.get( + return requestClient.get( `/bpm/process-instance/get-approval-detail`, { params }, ); @@ -156,7 +167,7 @@ export async function getFormFieldsPermission(params: any) { } /** 获取流程实例 BPMN 模型视图 */ -export async function getProcessInstanceBpmnModelView(id: number) { +export async function getProcessInstanceBpmnModelView(id: string) { return requestClient.get( `/bpm/process-instance/get-bpmn-model-view?id=${id}`, ); diff --git a/apps/web-antd/src/components/user-select-modal/index.ts b/apps/web-antd/src/components/user-select-modal/index.ts new file mode 100644 index 000000000..9cb1c93fd --- /dev/null +++ b/apps/web-antd/src/components/user-select-modal/index.ts @@ -0,0 +1 @@ +export { default as UserSelectModal } from './user-select-modal.vue'; diff --git a/apps/web-antd/src/components/user-select-modal/user-select-modal.vue b/apps/web-antd/src/components/user-select-modal/user-select-modal.vue new file mode 100644 index 000000000..7437a8b28 --- /dev/null +++ b/apps/web-antd/src/components/user-select-modal/user-select-modal.vue @@ -0,0 +1,528 @@ + + + + + diff --git a/apps/web-antd/src/router/routes/modules/bpm.ts b/apps/web-antd/src/router/routes/modules/bpm.ts new file mode 100644 index 000000000..20688f3e3 --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/bpm.ts @@ -0,0 +1,35 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/bpm', + name: 'bpm', + meta: { + title: '工作流', + hideInMenu: true, + }, + children: [ + { + path: 'process-instance/detail', + component: () => import('#/views/bpm/processInstance/detail/index.vue'), + name: 'BpmProcessInstanceDetail', + meta: { + title: '流程详情', + activePath: '/bpm/task/my', + icon: 'ant-design:history-outlined', + keepAlive: false, + hideInMenu: true, + }, + props: (route) => { + return { + id: route.query.id, + taskId: route.query.taskId, + activityId: route.query.activityId, + }; + }, + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-antd/src/utils/constants.ts b/apps/web-antd/src/utils/constants.ts index ec58eec82..78da9f947 100644 --- a/apps/web-antd/src/utils/constants.ts +++ b/apps/web-antd/src/utils/constants.ts @@ -466,7 +466,7 @@ export const BpmAutoApproveType = { }; // 候选人策略枚举 ( 用于审批节点。抄送节点 ) -export enum CandidateStrategy { +export enum CandidateStrategyEnum { /** * 审批人自选 */ @@ -532,7 +532,7 @@ export enum CandidateStrategy { /** * 节点类型 */ -export enum NodeType { +export enum NodeTypeEnum { /** * 子流程节点 */ @@ -593,3 +593,44 @@ export enum NodeType { */ USER_TASK_NODE = 11, } + +/** + * 任务状态枚举 + */ +export enum TaskStatusEnum { + /** + * 审批通过 + */ + APPROVE = 2, + + /** + * 审批通过中 + */ + APPROVING = 7, + /** + * 已取消 + */ + CANCEL = 4, + /** + * 未开始 + */ + NOT_START = -1, + + /** + * 审批不通过 + */ + REJECT = 3, + + /** + * 已退回 + */ + RETURN = 5, + /** + * 审批中 + */ + RUNNING = 1, + /** + * 待审批 + */ + WAIT = 0, +} diff --git a/apps/web-antd/src/utils/formCreate.ts b/apps/web-antd/src/utils/formCreate.ts new file mode 100644 index 000000000..d7f944c0f --- /dev/null +++ b/apps/web-antd/src/utils/formCreate.ts @@ -0,0 +1,64 @@ +/** + * 针对 https://github.com/xaboy/form-create-designer 封装的工具类 + */ + +import { isRef } from 'vue'; + +// 编码表单 Conf +export const encodeConf = (designerRef: object) => { + // @ts-ignore designerRef.value is dynamically added by form-create-designer + return JSON.stringify(designerRef.value.getOption()); +}; + +// 编码表单 Fields +export const encodeFields = (designerRef: object) => { + // @ts-ignore designerRef.value is dynamically added by form-create-designer + const rule = JSON.parse(designerRef.value.getJson()); + const fields: string[] = []; + rule.forEach((item: unknown) => { + fields.push(JSON.stringify(item)); + }); + return fields; +}; + +// 解码表单 Fields +export const decodeFields = (fields: string[]) => { + const rule: object[] = []; + fields.forEach((item) => { + rule.push(JSON.parse(item)); + }); + return rule; +}; + +// 设置表单的 Conf 和 Fields,适用 FcDesigner 场景 +export const setConfAndFields = ( + designerRef: object, + conf: string, + fields: string, +) => { + // @ts-ignore designerRef.value is dynamically added by form-create-designer + designerRef.value.setOption(JSON.parse(conf)); + // @ts-ignore designerRef.value is dynamically added by form-create-designer + designerRef.value.setRule(decodeFields(fields)); +}; + +// 设置表单的 Conf 和 Fields,适用 form-create 场景 +export const setConfAndFields2 = ( + detailPreview: object, + conf: string, + fields: string[], + value?: object, +) => { + if (isRef(detailPreview)) { + // @ts-ignore detailPreview.value is dynamically added by form-create-designer + detailPreview = detailPreview.value; + } + // @ts-ignore detailPreview properties are dynamically added by form-create-designer + detailPreview.option = JSON.parse(conf); + // @ts-ignore detailPreview properties are dynamically added by form-create-designer + detailPreview.rule = decodeFields(fields); + if (value) { + // @ts-ignore detailPreview properties are dynamically added by form-create-designer + detailPreview.value = value; + } +}; diff --git a/apps/web-antd/src/utils/index.ts b/apps/web-antd/src/utils/index.ts index dfcc60466..022e6441d 100644 --- a/apps/web-antd/src/utils/index.ts +++ b/apps/web-antd/src/utils/index.ts @@ -1,5 +1,7 @@ export * from './constants'; export * from './dict'; export * from './formatTime'; +export * from './formCreate'; export * from './rangePickerProps'; +export * from './routerHelper'; export * from './validator'; diff --git a/apps/web-antd/src/utils/routerHelper.ts b/apps/web-antd/src/utils/routerHelper.ts new file mode 100644 index 000000000..d4c7ac5d0 --- /dev/null +++ b/apps/web-antd/src/utils/routerHelper.ts @@ -0,0 +1,15 @@ +import { defineAsyncComponent } from 'vue'; + +const modules = import.meta.glob('../views/**/*.{vue,tsx}'); +/** + * 注册一个异步组件 + * @param componentPath 例:/bpm/oa/leave/detail + */ +export const registerComponent = (componentPath: string) => { + for (const item in modules) { + if (item.includes(componentPath)) { + // 使用异步组件的方式来动态加载组件 + return defineAsyncComponent(modules[item] as any); + } + } +}; diff --git a/apps/web-antd/src/views/bpm/processInstance/detail/index.vue b/apps/web-antd/src/views/bpm/processInstance/detail/index.vue new file mode 100644 index 000000000..74ee5c766 --- /dev/null +++ b/apps/web-antd/src/views/bpm/processInstance/detail/index.vue @@ -0,0 +1,354 @@ + + + diff --git a/apps/web-antd/src/views/bpm/processInstance/detail/modules/time-line.vue b/apps/web-antd/src/views/bpm/processInstance/detail/modules/time-line.vue new file mode 100644 index 000000000..0a5a3d8fb --- /dev/null +++ b/apps/web-antd/src/views/bpm/processInstance/detail/modules/time-line.vue @@ -0,0 +1,451 @@ + + + + diff --git a/apps/web-antd/src/views/bpm/processInstance/manager/index.vue b/apps/web-antd/src/views/bpm/processInstance/manager/index.vue index 3b3ce81b9..576cc2ec0 100644 --- a/apps/web-antd/src/views/bpm/processInstance/manager/index.vue +++ b/apps/web-antd/src/views/bpm/processInstance/manager/index.vue @@ -17,6 +17,7 @@ import { getProcessInstanceManagerPage, } from '#/api/bpm/processInstance'; import { DocAlert } from '#/components/doc-alert'; +import { router } from '#/router'; import { useGridColumns, useGridFormSchema } from './data'; @@ -110,6 +111,10 @@ function onCancel(row: BpmProcessInstanceApi.ProcessInstanceVO) { /** 查看流程实例 */ function onDetail(row: BpmProcessInstanceApi.ProcessInstanceVO) { console.warn(row); + router.push({ + name: 'BpmProcessInstanceDetail', + query: { id: row.id }, + }); } /** 刷新表格 */ diff --git a/packages/icons/src/svg/icons/bpm-approve.svg b/packages/icons/src/svg/icons/bpm-approve.svg new file mode 100644 index 000000000..06aa09d6d --- /dev/null +++ b/packages/icons/src/svg/icons/bpm-approve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bpm-cancel.svg b/packages/icons/src/svg/icons/bpm-cancel.svg new file mode 100644 index 000000000..ab9b1553b --- /dev/null +++ b/packages/icons/src/svg/icons/bpm-cancel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bpm-reject.svg b/packages/icons/src/svg/icons/bpm-reject.svg new file mode 100644 index 000000000..21fd5f60e --- /dev/null +++ b/packages/icons/src/svg/icons/bpm-reject.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bpm-running.svg b/packages/icons/src/svg/icons/bpm-running.svg new file mode 100644 index 000000000..5908c13b2 --- /dev/null +++ b/packages/icons/src/svg/icons/bpm-running.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/index.ts b/packages/icons/src/svg/index.ts index 3b5cb9953..64d46df75 100644 --- a/packages/icons/src/svg/index.ts +++ b/packages/icons/src/svg/index.ts @@ -12,6 +12,12 @@ const SvgBellIcon = createIconifyIcon('svg:bell'); const SvgCakeIcon = createIconifyIcon('svg:cake'); const SvgAntdvLogoIcon = createIconifyIcon('svg:antdv-logo'); +// 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 { SvgAntdvLogoIcon, SvgAvatar1Icon, @@ -19,6 +25,10 @@ export { SvgAvatar3Icon, SvgAvatar4Icon, SvgBellIcon, + SvgBpmApproveIcon, + SvgBpmCancelIcon, + SvgBpmRejectIcon, + SvgBpmRunningIcon, SvgCakeIcon, SvgCardIcon, SvgDownloadIcon,