康来智慧冷链系统 - 前端
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.
 
 
 
 
 

617 lines
20 KiB

<template>
<div :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' }"
@tabClick="handleTabsClick"
>
<ATabPane key="formInfo">
<template #tab>
<span>
<Icon icon="fa6-solid:file-lines"/>表单信息
</span>
</template>
<!-- 动态表单:内置使用枇杷表单设计器(待更新为海豚表单设计器) -->
<workflow-preview-form v-if="state.formType !== '2'"
ref="formPreview"
:taskFormData="state.taskFormData"
/>
<!-- 外置表单:内置使用用户自定义的vue页面,手动填写页面路径即可 -->
<component :is="state.formPath"
v-if="state.formType === '2'"
ref="form"
:formReadOnly="state.formReadOnly"
:businessId="state.businessId"
/>
</ATabPane>
<ATabPane key="processInfo">
<template #tab>
<span>
<Icon icon="fa6-solid:money-bill-wheat"/>流程信息
</span>
</template>
<WorkflowTimeLine/>
</ATabPane>
<ATabPane key="processChart">
<template #tab>
<span>
<Icon icon="fa-solid:image"/>流程图
</span>
</template>
<WorkflowChartModel ref="workflowChart"/>
</ATabPane>
<ATabPane key="flowRecord">
<template #tab>
<span>
<Icon icon="fa6-solid:bars-progress"/>流转记录
</span>
</template>
<WorkflowStep/>
</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"
: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">
<ATextarea
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"
:rules="[{required: true, message: '用户不能为空', validateTrigger: 'blur'}]"
>
<a-input-group compact>
<a-input v-model:value="state.auditForm.userIds" style="width: calc(100% - 48px)"/>
<a-button type="primary"
style="width: 48px"
@click="handleCCUserPicker"
>
<Icon icon="fa6-solid:users-viewfinder"/>
</a-button>
</a-input-group>
</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"
:rules="[{required: true, message: '用户不能为空', validateTrigger: 'blur'}]"
>
<a-select v-model:value="state.auditForm.userIds" allowClear/>
</AFormItem>
</ACol>
</AForm>
</a-card>
</ALayoutContent>
<footer class="workflow-form__footer" :style="getWrapFormFooterStyle">
<a-button size="large" type="primary">审批</a-button>
<a-button size="large" type="primary" danger>驳回</a-button>
</footer>
<UserPicker @register="registerModal" @success="handleTransferTask"/>
</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, 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 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';
/** 类型规范统一声明定义区域 */
interface WindowState {
// 流程表单标题
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: {
message: string,
mesCode: string,
mesName: string,
// 抄送用户ID
userIds: string,
// 任务代理人ID
assignee: string,
},
}
/** 通用变量统一声明区域 */
const ALayoutContent = LayoutContent;
const ATables = Tabs;
const ATabPane = Tabs.TabPane;
const AForm = Form;
const AFormItem = Form.Item;
const ACol = Col;
const ATextarea = Input.TextArea;
const { prefixCls } = useDesign('task-form');
const { getIsMobile } = useAppInject();
const { getCalcContentWidth } = useMenuSetting();
const state = reactive<WindowState>({
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: '',
userIds: '',
assignee: ''
}
});
const { currentRoute, push } = useRouter();
const { params, query } = unref(currentRoute);
const { notification, createMessage, createConfirm } = useMessage();
const { close } = useTabs();
const formPreview = ref();
const workflowChart = 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.formKey === '/404') {
formPreview.value.init('');
} else {
formPreview.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') {
formPreview.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 {
formPreview.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 handleCCUserPicker() {
openModal(true,{ _tag: 'transfer' });
}
/** 转办 */
function handleTransferUserPicker() {
openModal(true,{ _tag: 'transfer' });
}
/** 委托 */
function handleDelegateUserPicker() {
openModal(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') {
formPreview.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 {
formPreview.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':
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)
};
});
function handleTabsClick(activeKey: string, event: MouseEvent) {
// fixme: tabs组件首次加载不能初始化未激活的tab中的组件
if (activeKey === 'processChart' && !workflowChart.value) {
setTimeout(() => {
// 获取流程图数据
workflowChart.value.init(state.processInsId, state.processDefId);
}, 100);
}
}
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-task-form';
.@{prefix-cls} {
.workflow-form {
&__footer{
height: 66px;
background: #fff;
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: @page-footer-z-index;
}
}
}
.ant-form {
.ant-form-item {
margin-bottom: 15px;
}
}
</style>