康来智慧冷链系统 - 前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

762 lines
24 KiB

<template>
<div v-loading="state.loading" :class="prefixCls">
<ALayoutContent class="m-10">
<h4 class="text-center text-lg pb-2">{{ state.formTitle }}</h4>
<ATables v-model:activeKey="state.taskSelectedTab"
class="bg-white"
:tab-bar-style="{ 'padding-left': '13px' }"
>
<ATabPane key="formInfo">
<template #tab>
<span>
<Icon icon="fa6-solid:file-lines"/>表单信息
</span>
</template>
<!-- 动态表单:内置使用枇杷表单设计器(待更新为VUE3海豚表单设计器) -->
<workflow-preview-form v-if="state.formType !== '2'"
ref="formPreview"
:task-form-data="state.taskFormData"
/>
<!-- 外置表单:内置使用用户自定义的vue页面,手动填写页面路径即可 -->
<component :is="state.formPath"
v-if="state.formType === '2'"
ref="formPreview"
:form-read-only="state.formReadOnly"
:businessId="state.businessId"
/>
</ATabPane>
<ATabPane v-if="state.processInsId" key="processInfo">
<template #tab>
<span>
<Icon icon="fa6-solid:money-bill-wheat"/>流程信息
</span>
</template>
<WorkflowTimeLine :history-flow-change-list="state.historyFlowChangeList"/>
</ATabPane>
<ATabPane key="processChart" forceRender>
<template #tab>
<span>
<Icon icon="fa-solid:image"/>流程图
</span>
</template>
<WorkflowChartModel ref="workflowChart"/>
</ATabPane>
<ATabPane v-if="state.processInsId" key="flowRecord">
<template #tab>
<span>
<Icon icon="fa6-solid:bars-progress"/>流转记录
</span>
</template>
<WorkflowStep :history-flow-change-list="state.historyFlowChangeList"/>
</ATabPane>
</ATables>
<a-card v-if="!state.processInsId || state.taskId || state.status === 'reStart'" style="margin-top:10px;margin-bottom:66px;">
<AForm
ref="auditForm"
:labelCol="{ style: { width: '90px' } }"
:wrapperCol="{ style: { width: '100%' } }"
:model="state.auditForm"
:rules="state.rulesRef"
:scrollToFirstError="true"
>
<ACol :span="16">
<AFormItem v-if="!state.processInsId || state.status === 'reStart'" label="流程标题" name="formTitle">
<a-input v-model:value="state.formTitle" placeholder="请输入流程标题"/>
</AFormItem>
</ACol>
<ACol :span="16">
<AFormItem v-if="state.taskId" label="审批信息" name="message">
<AInputTextarea
v-model:value="state.auditForm.message"
placeholder="请输入审批意见"
:rows="3"
allowClear
/>
</AFormItem>
</ACol>
<ACol :span="16">
<AFormItem label=" " :colon="false" name="isCC">
<a-checkbox v-model:checked="state.isCC">是否抄送</a-checkbox>
</AFormItem>
</ACol>
<ACol :span="16">
<AFormItem v-if="state.isCC"
label="抄送给"
name="userIds"
>
<AInputGroup compact>
<ApiSelect v-model:value="state.auditForm.userIds"
disabled
mode="tags"
style="width: calc(100% - 48px)"
:api="listUser"
:params="{ size: 999 }"
resultField="data"
labelField="username"
valueField="id"
/>
<a-button type="primary"
style="width: 48px"
@click="handleCCUserPicker"
>
<Icon icon="fa6-solid:users-viewfinder"/>
</a-button>
</AInputGroup>
</AFormItem>
</ACol>
<ACol :span="16">
<AFormItem label=" " :colon="false" name="isAssign">
<a-checkbox v-model:checked="state.isAssign">指定下一步处理者(不设置就使用默认处理人)</a-checkbox>
</AFormItem>
</ACol>
<ACol :span="16">
<AFormItem v-if="state.isAssign"
label="指定"
name="assignee"
>
<AInputGroup compact>
<ApiSelect v-model:value="state.auditForm.assignee"
disabled
style="width: calc(100% - 48px)"
:api="listUser"
:showArrow="false"
:params="{ size: 999 }"
resultField="data"
labelField="username"
valueField="id"
/>
<a-button type="primary"
style="width: 48px"
@click="handleAssignUserPicker"
>
<Icon icon="fa6-solid:clipboard-user"/>
</a-button>
</AInputGroup>
</AFormItem>
</ACol>
</AForm>
</a-card>
</ALayoutContent>
<footer class="workflow-form__footer" :style="getWrapFormFooterStyle">
<template v-for="(button, index) in state.buttons" :key="index">
<!-- 渲染配置按钮 -->
<a-button v-show="button.isHide === '0'"
size="large"
type="primary"
@click="submit(button)"
>{{ button.name }}</a-button>
</template>
</footer>
<UserPicker :title="state.userPickerProps.title"
:limit="state.userPickerProps.limit"
@register="registerModal"
@ccEmit="handleCCEmit"
@assignEmit="handleAssignEmit"
@transferTask="handleTransferTask"
@delegateTask="handleDelegateTask"
/>
<RollBackTaskModal @register="registerRollBackTask" @success="handleRollBackTask"/>
</div>
</template>
<script lang="ts" setup>
/**
* 提供模板规范代码参考,请尽量保证编写代码风格跟模板规范代码一致
* 采用vben-动态表格表单封装组件编写,采用 setup 写法
* Copyright © 2023-2023 <a href="https://godolphinx.org">海豚生态开源社区</a> All rights reserved.
* author wangxiang4
*/
import { reactive, onActivated, unref, nextTick, ref, computed, CSSProperties } from 'vue';
import { Input, LayoutContent, Tabs, Form, Col } from 'ant-design-vue';
import type { FormProperty } from '/@/api/platform/workflow/entity/formProperty';
import type { Workflow } from '/@/api/platform/workflow/entity/workflow';
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 } from '/@/api/platform/workflow/controller/process';
import { useTabs } from '/@/hooks/web/useTabs';
import { auditTask, rollBackTaskList, rejectTask, transferTask, delegateTask, 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 WorkflowChartModel from './WorkflowChartModel.vue';
import WorkflowTimeLine from './WorkflowTimeLine.vue';
import WorkflowStep from './workflowStep/index.vue';
import { getProcessStartEventFormData, getTaskFormData } from '/@/api/platform/workflow/controller/form';
import { findByDefIdAndTaskId } from '/@/api/platform/workflow/extension/controller/activityExtensionData';
import { useDesign } from '/@/hooks/web/useDesign';
import { useAppInject } from '/@/hooks/web/useAppInject';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import UserPicker from './popups/UserPicker/index.vue';
import { ApiSelect } from '/@/components/Form';
import { listUser } from '/@/api/platform/system/controller/user';
import { ActivityCommentInfo } from '/@/api/platform/workflow/entity/activityCommentInfo';
import RollBackTaskModal from './popups/RollBackTaskModal.vue';
import { PageEnum } from '/@/enums/workflowEnum';
import { useAppStore } from '/@/store/modules/app';
/** 类型规范统一声明定义区域 */
interface WindowState {
// 加载
loading: boolean;
// 流程表单标题
formTitle: string;
// 流程表单key
formKey: string;
// 表单类型(1:动态表单,2:外置表单)
formType: string;
// 外置表单路径
formPath: string;
// 外置表单是否只读
formReadOnly: boolean;
// 外置表单业务数据绑定ID
businessId: string;
// 动态表单字段配置数据
taskFormData: FormProperty[];
// 设置选择任务选项卡
taskSelectedTab: string;
// 流程任务ID
taskId: string;
// 流程任务定义key
taskDefKey: string;
// 流程定义ID
processDefId: string;
// 流程实例ID
processInsId: string;
// 流程定义Key
processDefKey: string;
// 当前任务表单操作状态
status: string;
// 历史流转信息
historyFlowChangeList: Workflow[];
// 当前操作按钮配置信息
buttons: Partial<WorkflowButton>[];
// 是否抄送
isCC: boolean;
// 是否指定代理人
isAssign: boolean;
// 审批表单信息
auditForm: ActivityCommentInfo & {
// 抄送用户ID
userIds: string[];
// 任务代理人ID
assignee: string;
};
// 审批表单验证规则
rulesRef: Recordable;
// 用户选取器prop
userPickerProps: {
title: string;
limit: number;
};
}
/** 通用变量统一声明区域 */
const ALayoutContent = LayoutContent;
const ATables = Tabs;
const ATabPane = Tabs.TabPane;
const AForm = Form;
const AFormItem = Form.Item;
const ACol = Col;
const AInputTextarea = Input.TextArea;
const AInputGroup = Input.Group;
const appStore = useAppStore();
const { prefixCls } = useDesign('task-form');
const { getIsMobile } = useAppInject();
const { getCalcContentWidth } = useMenuSetting();
const state = reactive<WindowState>({
loading: false,
formTitle: '',
formKey: '',
formType: '',
formPath: '',
formReadOnly: false,
businessId: '',
taskFormData: [],
taskSelectedTab: 'formInfo',
taskId: '',
taskDefKey: '',
processDefId: '',
processInsId: '',
processDefKey: '',
status: '',
historyFlowChangeList: [],
buttons: [],
isCC: false,
isAssign: false,
auditForm: {
message: '',
mesCode: '',
mesName: '',
mesLevel: '',
userIds: [],
assignee: ''
},
rulesRef: {
userIds: [{ required: true, type: 'array', message: '抄送用户不能为空', validateTrigger: 'blur' }],
assignee: [{ required: true, whitespace: true, message: '处理用户不能为空', validateTrigger: 'blur' }]
},
userPickerProps: {
title: '',
limit: 0,
}
});
const { currentRoute, push } = useRouter();
const { params, query } = unref(currentRoute);
const { notification, createMessage, createConfirm } = useMessage();
const { close } = useTabs();
const formPreview = ref();
const workflowChart = ref();
const auditForm = ref();
const [registerModal, { openModal }] = useModal();
const [registerRollBackTask , { openModal: openRollBackTask }] = useModal();
onActivated(() => {
handleInit();
nextTick(async () => {
// 初始化外置表单
if (state.formType === '2') {
if (state.formKey === '/404') {
state.formPath = '';
createMessage.info('没有关联流程表单!');
} else {
state.formPath = require('@/views' + state.formKey + '.vue').default;
}
// 初始化动态表单
} else {
// 获取启动事件表单数据
if (state.status === 'start' || state.status === 'reStart') {
state.taskFormData = await getProcessStartEventFormData(state.processDefId);
// 获取任务表单数据
} else {
state.taskId && (state.taskFormData = await getTaskFormData(state.taskId));
}
if (state.formKey === '/404') {
formPreview.value.init('');
} else {
formPreview.value.init(state.formKey);
}
}
// 设置启动按钮配置
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);
}
workflowChart.value.init(state.processInsId, state.processDefId);
});
});
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 = '';
state.userPickerProps.title = '';
state.userPickerProps.limit = 0;
}
/** 流程抄送 */
async function cc(data: Recordable) {
state.loading = true;
try {
if (state.isCC && state.auditForm.userIds) {
await auditForm.value.validate();
await saveWorkflowCopy(state.auditForm.userIds.join(','), {
processDefId: state.processDefId,
processInsId: data.processInsId,
processDefName: '',
processInsName: state.formTitle,
taskName: ''
});
}
} finally {
state.loading = false;
}
}
/** 暂存草稿 */
function save() {
state.loading = true;
try {
notification.warn({
message: '提示',
description: '功能正在开发中...'
});
} finally {
state.loading = false;
}
}
/** 启动流程 */
async function start(vars: Recordable) {
state.loading = true;
try {
// 外置表单
if (state.formType === '2') {
await formPreview.value.startProcessDefinition(async (businessTable: string, businessId: string) => {
const processInsId = await startProcessDefinition({
processDefKey: state.processDefKey,
businessTable: businessTable,
businessId: businessId,
...vars
});
await cc({ processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
});
// 动态表单
} else {
await formPreview.value.startFormProcessDefinition({ processDefId: state.processDefId, ...vars }, async (processInsId: string) => {
await cc({ processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
});
}
} finally {
state.loading = false;
}
}
/** 同意任务 */
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 handleCCUserPicker() {
state.userPickerProps.title = '抄送用户选择';
state.userPickerProps.limit = 0;
openModal(true,{
record: {
ids: state.auditForm.userIds,
emitEventName: 'ccEmit'
}
});
}
function handleCCEmit(rows: KiccUser[]) {
state.auditForm.userIds = rows.map(item => item.id);
}
function handleAssignUserPicker() {
state.userPickerProps.title = '代理用户选择';
state.userPickerProps.limit = 1;
openModal(true,{
record: {
ids: [ state.auditForm.assignee ],
emitEventName: 'assignEmit'
}
});
}
function handleAssignEmit(rows: KiccUser[]) {
state.auditForm.assignee = rows[0].id;
}
/** 转办 */
function handleTransferUserPicker() {
openModal(true,{ _tag: 'transferTask' });
}
/** 委托 */
function handleDelegateUserPicker() {
openModal(true,{ _tag: 'delegateTask' });
}
/** 终止 */
function stop() {
createConfirm({
iconType: 'warning',
title: '警告',
content: '确定终止流程吗?',
onOk: async () => {
await stopProcessInstance(state.processInsId, state.auditForm.message);
await cc({processInsId: state.processInsId});
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
}
});
}
/** 打印 */
function print() {
state.loading = true;
try {
console.warn('---工作流表单打印成功,此处可以做一些打印回调逻辑处理!---');
} finally {
state.loading = false;
}
}
/** 驳回到任意节点 */
function rollBack() {
openRollBackTask(true,{ taskId: state.taskId });
}
/** 加签 */
function addMultiInstance() {
state.loading = true;
try {
// flowable开源版加签会导致模板数量急剧扩大,需要经过内部讨论是否有比较好的解决方案:http://www.pangubpm.com/doc58.html
notification.warn({
message: '提示',
description: '功能正在开发中...'
});
} finally {
state.loading = false;
}
}
/** 减签 */
function delMultiInstance () {
state.loading = true;
try {
notification.warn({
message: '提示',
description: '功能正在开发中...'
});
} finally {
state.loading = false;
}
}
/** 自定义按钮提交 */
async function commit(vars?: Recordable) {
state.loading = true;
try {
// 外置表单审批
if (state.formType === '2') {
await formPreview.value.auditTask(async () => {
await auditTask({
taskId: state.taskId,
taskDefKey: state.taskDefKey,
processInsId: state.processInsId,
processDefId: state.processDefId,
activityCommentInfo: state.auditForm,
assignee: state.auditForm.assignee,
vars,
});
await cc({ processInsId: state.processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
});
// 动态表单审批
} else {
await formPreview.value.auditFormTask({
formType: '',
taskId: state.taskId,
processInsId: state.processInsId,
activityCommentInfo: state.auditForm,
assignee: state.auditForm.assignee,
vars
}, async () => {
await cc({ processInsId: state.processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
});
}
} finally {
state.loading = false;
}
}
/** 处理回退到任意节点 */
async function handleRollBackTask(backTaskDefKey: string) {
state.loading = true;
try {
await rejectTask({
currentTaskId: state.taskId,
rollBackTaskDefKey: backTaskDefKey,
comment: state.auditForm
});
await cc({ processInsId: state.processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
} finally {
state.loading = false;
}
}
/** 处理转派任务 */
async function handleTransferTask(userList: KiccUser[]) {
state.loading = true;
try {
await transferTask( state.taskId, userList[0].id);
await cc({ processInsId: state.processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
} finally {
state.loading = false;
}
}
/** 处理任务委派 */
async function handleDelegateTask(userList: KiccUser[]) {
state.loading = true;
try {
await delegateTask(state.taskId, userList[0].id);
await cc({ processInsId: state.processInsId });
await close();
await push({ path: PageEnum.TODO_TASK_PAGE });
} finally {
state.loading = false;
}
}
function submit(button: Partial<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':
handleTransferUserPicker();
break;
// 外派
case '_workflow_activity_delegate':
handleDelegateUserPicker();
break;
// 终止
case '_workflow_activity_stop':
stop();
break;
// 打印
case '_workflow_activity_print':
print();
break;
// 自定义按钮提交
default:
commit(vars);
}
}
const getWrapFormFooterStyle = computed((): CSSProperties | Recordable => {
return {
width: unref(getIsMobile) ? '100%' : unref(getCalcContentWidth)
};
});
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-task-form';
.@{prefix-cls} {
.workflow-form {
&__footer{
height: 66px;
background: @component-background;
display: flex;
gap: 11px;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
-webkit-box-shadow: 0 -3px 5px 0 rgba(0,0,0,.12);
box-shadow: 0 -3px 5px 0 rgba(0,0,0,.12);
-webkit-transition: inline-block 0.3s, left 0.3s, width 0.3s, margin-left 0.3s, font-size 0.3s;
transition: inline-block 0.3s, left 0.3s, width 0.3s, margin-left 0.3s, font-size 0.3s;
position: fixed;
bottom: 0;
z-index: 9;
}
}
}
.ant-form {
.ant-form-item {
margin-bottom: 15px;
}
}
:deep(.ant-select) {
&-disabled {
&.ant-select:not(.ant-select-customize-input) {
.ant-select-selector {
background: transparent;
}
}
}
}
:deep(.ant-tabs) {
.ant-tabs-content {
min-height: 200px;
}
}
</style>