Browse Source

chore: compose workflow api

master
wangxiang 2 years ago
parent
commit
ed34c84809
No known key found for this signature in database
GPG Key ID: 1BA7946AB6B232E4
  1. 2
      src/api/platform/workflow/controller/process.ts
  2. 2
      src/views/system/menu/menu.data.ts
  3. 24
      src/views/workflow/process/index.vue
  4. 373
      src/views/workflow/task/TaskForm.vue
  5. 112
      src/views/workflow/task/WorkflowPreviewForm.vue

2
src/api/platform/workflow/controller/process.ts

@ -56,6 +56,6 @@ export const queryProcessStatus = (processInsId: string) => defHttp.get({ url: `
export const selfProcessInstanceList = (params: Recordable) => defHttp.get({ url: Api.selfProcessInstanceList, params }); export const selfProcessInstanceList = (params: Recordable) => defHttp.get({ url: Api.selfProcessInstanceList, params });
export const startProcessDefinition = (workflow: Workflow) => defHttp.post({ url: Api.startProcessDefinition, data: workflow }); export const startProcessDefinition = (workflow: Partial<Workflow>) => defHttp.post({ url: Api.startProcessDefinition, data: workflow });
export const removeHistoryProcessIns = (ids: string) => defHttp.delete({ url: `${Api.removeHistoryProcessIns}${ids}` }); export const removeHistoryProcessIns = (ids: string) => defHttp.delete({ url: `${Api.removeHistoryProcessIns}${ids}` });

2
src/views/system/menu/menu.data.ts

@ -222,7 +222,7 @@ export const formSchema: FormSchema[] = [
} }
}, },
{ {
field: 'keepalive', field: 'keepAlive',
label: '是否缓存', label: '是否缓存',
component: 'RadioButtonGroup', component: 'RadioButtonGroup',
defaultValue: '0', defaultValue: '0',

24
src/views/workflow/process/index.vue

@ -48,6 +48,10 @@
import { BasicTree, TreeItem } from '/@/components/Tree'; import { BasicTree, TreeItem } from '/@/components/Tree';
import { listToTree } from '/@/utils/helper/treeHelper'; import { listToTree } from '/@/utils/helper/treeHelper';
import { listProcessCategory } from '/@/api/platform/workflow/extension/controller/processCategory'; import { listProcessCategory } from '/@/api/platform/workflow/extension/controller/processCategory';
import { getTaskDefinition } from '/@/api/platform/workflow/controller/task';
import { useUserStore } from '/@/store/modules/user';
import { formatToDateTime } from '/@/utils/dateUtil';
import { useRouter } from 'vue-router';
/** 类型规范统一声明定义区域 */ /** 类型规范统一声明定义区域 */
interface TableState { interface TableState {
@ -67,6 +71,8 @@
const state = reactive<TableState>({ const state = reactive<TableState>({
treeData: [] treeData: []
}); });
const userStore = useUserStore();
const { push } = useRouter();
const [registerTable, { reload, getForm }] = useTable({ const [registerTable, { reload, getForm }] = useTable({
title: '流程定义列表', title: '流程定义列表',
api: listProcessDef, api: listProcessDef,
@ -97,7 +103,23 @@
state.treeData = listToTree(await listProcessCategory()); state.treeData = listToTree(await listProcessCategory());
}); });
function handleProcessDefinitionStart(record?: Recordable) { async function handleProcessDefinitionStart(record?: Recordable) {
const tabTitle = `启动流程【${record?.name}`;
const formTitle = `${userStore.getUserInfo.nickName}${formatToDateTime()} 发起了 [${record?.name}]`;
const data = await getTaskDefinition( { processDefId: record?.id });
await push({
path: '/workflow/task/taskForm',
query: {
_meta: 'y',
processDefId: record?.id,
processDefKey: record?.key,
status: 'start',
title: tabTitle,
formTitle: formTitle,
formType: data.formType,
formKey: data.formKey
}
});
} }
function handleSelect(selectedKeys: string[]) { function handleSelect(selectedKeys: string[]) {

373
src/views/workflow/task/TaskForm.vue

@ -1,8 +1,26 @@
<template> <template>
<LayoutContent style="min-height: 100vh"> <ALayoutContent style="min-height: 100vh">
<h4 style="text-align:center">{{ state.formTitle }}</h4> <h4 style="text-align:center">{{ state.formTitle }}</h4>
<ATable v-model:activeKey="state.taskSelectedTab">
</LayoutContent> <ATabPane key="formInfo" tab="表单信息">
<!-- 动态表单:内置使用枇杷表单设计器 -->
<workflow-preview-form v-if="state.formType !== '2'"
ref="form"
:taskFormData="state.taskFormData"
/>
<!-- 外置表单:内置使用用户自定义的vue页面,手动填写页面路径即可 -->
<component :is="state.formPath"
v-if="state.formType === '2'"
ref="form"
:formReadOnly="state.formReadOnly"
:businessId="state.businessId"
/>
</ATabPane>
<ATabPane key="processChart" tab="流程图">
流程图
</ATabPane>
</ATable>
</ALayoutContent>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
/** /**
@ -11,11 +29,22 @@
* Copyright © 2023-2023 <a href="https://godolphinx.org">海豚生态开源社区</a> All rights reserved. * Copyright © 2023-2023 <a href="https://godolphinx.org">海豚生态开源社区</a> All rights reserved.
* author wangxiang4 * author wangxiang4
*/ */
import { reactive, onActivated } from 'vue'; import { reactive, onActivated, unref, nextTick, ref } from 'vue';
import { LayoutContent } from 'ant-design-vue'; import { LayoutContent, Tabs } from 'ant-design-vue';
import type { FormProperty } from '/@/api/platform/workflow/entity/formProperty'; import type { FormProperty } from '/@/api/platform/workflow/entity/formProperty';
import type { Workflow } from '/@/api/platform/workflow/entity/workflow'; import type { Workflow } from '/@/api/platform/workflow/entity/workflow';
import type { WorkflowButton } from '/@/api/platform/workflow/extension/entity/workflowButton'; import type { WorkflowButton } from '/@/api/platform/workflow/extension/entity/workflowButton';
import { useRouter } from 'vue-router';
import { saveWorkflowCopy } from '/@/api/platform/workflow/extension/controller/workflowCopy';
import { useMessage } from '/@/hooks/web/useMessage';
import { startProcessDefinition, stopProcessInstance, getFlowChart as getProcessDefFlowChart } from '/@/api/platform/workflow/controller/process';
import { useTabs } from '/@/hooks/web/useTabs';
import { auditTask, rollBackTaskList, rejectTask, transferTask, delegateTask, getFlowChart, listHistoryFlowChange } from '/@/api/platform/workflow/controller/task';
import { useModal } from '/@/components/Modal';
import type { KiccUser } from '/@/api/common/base/entity';
import WorkflowPreviewForm from './WorkflowPreviewForm.vue';
import { getProcessStartEventFormData, getTaskFormData } from '/@/api/platform/workflow/controller/form';
import { findByDefIdAndTaskId } from '/@/api/platform/workflow/extension/controller/activityExtensionData';
/** 类型规范统一声明定义区域 */ /** 类型规范统一声明定义区域 */
interface WindowState { interface WindowState {
@ -28,7 +57,7 @@
// //
formPath: string; formPath: string;
// //
formReadOnly: false, formReadOnly: boolean,
// ID // ID
businessId: string, businessId: string,
// //
@ -53,7 +82,7 @@
// //
historyFlowChangeList: Workflow[], historyFlowChangeList: Workflow[],
// //
buttons: WorkflowButton[], buttons: Partial<WorkflowButton>[],
// //
isCC: boolean, isCC: boolean,
// //
@ -71,6 +100,10 @@
} }
/** 通用变量统一声明区域 */ /** 通用变量统一声明区域 */
const ALayoutContent = LayoutContent;
const ATable = Tabs;
const ATabPane = Tabs.TabPane;
const state = reactive<WindowState>({ const state = reactive<WindowState>({
formTitle: '', formTitle: '',
formKey: '', formKey: '',
@ -102,5 +135,331 @@
assignee: '' assignee: ''
} }
}); });
const { currentRoute, push } = useRouter();
const { params, query } = unref(currentRoute);
const { notification, createMessage, createConfirm } = useMessage();
const { close } = useTabs();
const form = ref();
const [registerUser , { openModal: openUser }] = useModal();
const [registerRollBackTask , { openModal: openRollBackTask }] = useModal();
onActivated(() => {
debugger
handleInit();
nextTick(async () => {
//
if (state.processInsId) {
const data = await getFlowChart(state.processInsId);
//this.$refs.flowableChart.setHighlightImportDiagram(response)
} else {
const data = getProcessDefFlowChart(state.processDefId);
//this.$refs.flowableChart.setHighlightImportDiagram({ bpmnXml: data })
}
//
if (state.formType === '2') {
if (state.formKey === '/404') {
state.formPath = '';
createMessage.info('没有关联流程表单!');
} else {
state.formPath = require('@/views' + state.formKey + '.vue').default;
}
//
} else {
if (state.formKey === '/404') {
form.value.init('');
} else {
form.value.init(state.formKey);
}
//
if (state.status === 'start' || state.status === 'reStart') {
state.taskFormData = await getProcessStartEventFormData(state.processDefId);
//
} else {
state.taskFormData = await getTaskFormData(state.taskId);
}
}
//
if (state.status === 'start' || state.status === 'reStart') {
state.buttons = [{ code: '_workflow_activity_start', name: '启动', isHide: '0' }];
// bpmn
} else if (state.processDefKey && state.taskDefKey) {
const data = await findByDefIdAndTaskId({ processDefId: state.processDefKey, activityDefId: state.taskDefKey });
state.buttons = data.workflowButtonList;
}
//
if (state.processInsId) {
state.historyFlowChangeList = await listHistoryFlowChange(state.processInsId);
}
});
});
function handleInit() {
state.taskSelectedTab = 'formInfo';
state.processDefId = query.processDefId as string;
state.processInsId = query.processInsId as string;
state.processDefKey = query.processDefKey as string;
state.formType = query.formType as string;
state.formKey = query.formKey as string;
state.taskId = query.taskId as string;
state.taskDefKey = query.taskDefKey as string;
state.status = query.status as string;
state.formTitle = query.formTitle as string;
state.businessId = query.businessId as string;
state.formReadOnly = query.formReadOnly === 'true';
state.isCC = false;
state.isAssign = false;
state.auditForm.assignee = '';
state.auditForm.userIds = '';
state.auditForm.message = '';
}
/** 流程抄送 */
function cc(data: Recordable) {
if (state.isCC && state.auditForm.userIds) {
//this.$refs['auditForm'].validate((valid) => {
//if (valid) {
saveWorkflowCopy(state.auditForm.userIds, {
processDefId: state.processDefId,
processInsId: data.processInsId,
processDefName: '',
processInsName: state.formTitle,
taskName: ''
});
//}
//});
}
}
/** 暂存草稿 */
function save() {
notification.warn({
message: '提示',
description: '功能正在开发中...'
});
}
/** 启动流程 */
function start(vars: Recordable) {
//
if (state.formType === '2') {
form.value.startProcessDefinition(async (businessTable: string, businessId: string) => {
const processInsId = await startProcessDefinition({
processDefKey: this.processDefKey,
businessTable: businessTable,
businessId: businessId,
...vars
});
await close();
await push({path: '/workflow/task/index'});
cc({ processInsId });
});
//
} else {
form.value.startFormProcessDefinition({ processDefId: this.processDefId, ...vars }, (processInsId: string) => {
close();
push({ path: '/workflow/task/index' });
cc({ processInsId });
});
}
}
/** 同意任务 */
function agree(vars?: Recordable) {
commit(vars);
}
/** 驳回任务 */
function reject () {
createConfirm({
iconType: 'warning',
title: '警告',
content: '确定驳回任务吗?',
onOk: async () => {
const nodes = await rollBackTaskList(state.taskId);
if (nodes.length > 0) {
const backTaskDefKey = nodes[nodes.length - 1].taskDefKey;
await handleRollBackTask(backTaskDefKey);
}
}
});
}
/** 转办 */
function transfer() {
openUser(true,{ _tag: 'transfer' });
}
/** 委托 */
function delegate () {
openUser(true,{ _tag: 'delegate' });
}
/** 终止 */
function stop() {
createConfirm({
iconType: 'warning',
title: '警告',
content: '确定终止流程吗?',
onOk: async () => {
await stopProcessInstance(this.processInsId, this.auditForm.message);
await close();
await push({path: '/workflow/task/index'});
cc({processInsId: state.processInsId});
}
});
}
/** 打印 */
function print() {
console.warn('---工作流表单打印成功,此处可以做一些打印回调逻辑处理!---');
}
/** 驳回到任意节点 */
function rollBack() {
openRollBackTask(true,{ taskId: this.taskId });
}
/** 加签 */
function addMultiInstance() {
// flowable,:http://www.pangubpm.com/doc58.html
notification.warn({
message: '提示',
description: '功能正在开发中...'
});
}
/** 减签 */
function delMultiInstance () {
notification.warn({
message: '提示',
description: '功能正在开发中...'
});
}
/** 自定义按钮提交 */
function commit(vars?: Recordable) {
//
if (state.formType === '2') {
form.value.auditTask(async () => {
await auditTask({
taskId: this.taskId,
taskDefKey: this.taskDefKey,
processInsId: this.processInsId,
processDefId: this.processDefId,
activityCommentInfo: this.auditForm,
assignee: this.auditForm.assignee,
vars,
});
await close();
await push({ path: '/workflow/task/index' });
cc({ processInsId: state.processInsId });
});
//
} else {
form.value.auditFormTask({
taskId: this.taskId,
processInsId: this.processInsId,
activityCommentInfo: this.auditForm,
assignee: this.auditForm.assignee,
vars,
}, () => {
close();
push({ path: '/workflow/task/index' });
cc({ processInsId: state.processInsId });
});
}
}
/** 处理回退到任意节点 */
async function handleRollBackTask(backTaskDefKey: string) {
await rejectTask({
currentTaskId: this.taskId,
rollBackTaskDefKey: backTaskDefKey,
comment: this.auditForm
});
await close();
await push({ path: '/workflow/task/index' });
cc({ processInsId: state.processInsId });
}
/** 处理转派任务 */
async function handleTransferTask(userList: KiccUser[]) {
await transferTask( this.taskId, userList[0].id);
await close();
await push({ path: '/workflow/task/index' });
cc({ processInsId: state.processInsId });
}
/** 处理任务委派 */
async function handleDelegateTask(userList: KiccUser[]) {
await delegateTask(this.taskId, userList[0].id);
await close();
await push({ path: '/workflow/task/index' });
cc({ processInsId: state.processInsId });
}
function submit(button: WorkflowButton) {
//
const vars: Recordable = {};
//
vars.title = state.formTitle;
//
vars.assignee = state.auditForm.assignee;
//
state.auditForm.mesCode = button.code;
//
state.auditForm.mesName = button.name;
switch (button.code) {
//
case '_workflow_activity_start':
start(vars);
break;
// 稿
case '_workflow_activity_save':
save();
break;
//
case '_workflow_activity_agree':
agree();
break;
//
case '_workflow_activity_reject':
reject();
break;
//
case '_workflow_activity_roll_back':
rollBack();
break;
//
case '_workflow_activity_add_multi_instance':
addMultiInstance();
break;
//
case '_workflow_activity_del_multi_instance':
delMultiInstance();
break;
//
case '_workflow_activity_transfer':
transfer();
break;
//
case '_workflow_activity_delegate':
delegate();
break;
//
case '_workflow_activity_stop':
stop();
break;
//
case '_workflow_activity_print':
print();
break;
//
default:
commit(vars);
}
}
</script> </script>

112
src/views/workflow/task/WorkflowPreviewForm.vue

@ -0,0 +1,112 @@
<template>
<div id="formPreview" v-loading="state.loading"/>
</template>
<script lang="ts" setup>
import { PropType, reactive } from 'vue';
import { loadMicroApp, MicroApp } from 'qiankun';
import { getSubDefineProps } from '/@/qiankun/state';
import { GlStateEnum } from '/@/enums/microAppEnum';
import { useMicroAppStore } from '/@/store/modules/microApp';
import { apps } from '/@/qiankun/apps';
import { getFormDefinitionJson } from '/@/api/platform/workflow/extension/controller/formDefinitionJson';
import { FORM_DESIGN_APP_COMPONENTS } from '/@/enums/microAppEnum';
import { useMessage } from '/@/hooks/web/useMessage';
import { startProcessDefinition, auditTask } from '/@/api/platform/workflow/controller/form';
import type { Workflow } from '/@/api/platform/workflow/entity/workflow';
const props = defineProps({
taskFormData: {
type: Array as PropType<Recordable[]>,
default: () => []
},
disabled: {
type: Boolean as PropType<boolean>,
default: false
}
});
interface WindowState {
formDesignApp: MicroApp;
formData: Recordable;
options: Recordable;
loading: boolean;
}
const state = reactive<WindowState>({
formDesignApp: undefined!,
loading: false,
formData: {},
options: {}
});
const formDesignProps = {
style: { height: 'calc(100vh - 160px)' },
options: {},
disabled: false,
readonly: false,
};
const emit = defineEmits(['success', 'register']);
const microAppStore = useMicroAppStore();
const { createMessage } = useMessage();
async function init(formJsonId: string) {
if (formJsonId) {
state.loading = true;
state.formData = {};
state.formDesignApp = loadMicroApp(Object.assign({} , apps.find(item => item.name == 'form-design'), {
container: '#formPreview',
props: {
...getSubDefineProps(),
// props
[GlStateEnum.FORM_DESIGN_APP_PROPS_KEY]: formDesignProps
}
}), { sandbox: { experimentalStyleIsolation: true }});
state.formDesignApp.mountPromise.then(() => {
const disableIds:string[] = [];
const showIds:string[] = [];
props.taskFormData.forEach((item) => {
if (item.value !== undefined) {
state.formData[item.id] = item.value;
}
if (item.readable === true) {
showIds.push(item.id);
}
if (item.writable === false) {
disableIds.push(item.id);
}
});
const formPreviewApp: Recordable = microAppStore.getFormDesignApp(FORM_DESIGN_APP_COMPONENTS.PREVIEW),
formRef: Recordable = formPreviewApp.getRef().$refs['form-preview'];
formRef.setShowIds(showIds);
formRef.setDisableIds(disableIds);
state.loading = false;
});
// json
try {
const data = await getFormDefinitionJson(formJsonId);
if (!data.json) throw Error('当前数据没有json结构体');
formDesignProps.options = eval('(' + data.json + ')');
} catch(e: any) {
createMessage.error(e);
}
} else createMessage.info('没有关联流程表单!');
}
async function startFormProcessDefinition(data: Partial<Workflow>, callback: Fn) {
const processVarsity = Object.assign(data.vars || {}, state.formData);
const processInsId = await startProcessDefinition({ ...data, vars: processVarsity });
callback(processInsId);
}
async function auditFormTask(data: Partial<Workflow>, callback: Fn) {
const processVarsity = Object.assign(data.vars || {}, state.formData);
await auditTask({...data, vars: processVarsity});
callback();
}
defineExpose({
init,
startFormProcessDefinition,
auditFormTask
});
</script>
Loading…
Cancel
Save