<script lang="ts" setup>
|
import { computed, defineAsyncComponent, markRaw, nextTick, reactive, ref, toRefs } from 'vue';
|
|
import { useGlobSetting, useMessage } from '@jnpf/hooks';
|
import { useModal } from '@jnpf/ui/modal';
|
import { BasicPopup, usePopup, usePopupInner } from '@jnpf/ui/popup';
|
import { formatToDate, formatToDateTime, getScriptFunc } from '@jnpf/utils';
|
|
import { useUserStore } from '@vben/stores';
|
|
import { Drawer, Tooltip } from 'ant-design-vue';
|
import { cloneDeep } from 'lodash-es';
|
|
import { launchFlow } from '#/api/onlineDev/visualDev';
|
import { getDataInterfaceRes } from '#/api/systemData/dataInterface';
|
import { getDelegateUser } from '#/api/workFlow/flowDelegate';
|
import { assign, cancel, getPauseType, pause, reboot, resurgence } from '#/api/workFlow/flowMonitor';
|
import {
|
assist,
|
audit,
|
auditRecall,
|
back,
|
cancelSign,
|
create,
|
freeApprover,
|
getBackNodeCodeList,
|
getCandidates,
|
getFlowTaskInfo,
|
handle,
|
launchRecall,
|
press,
|
retryOutside,
|
revoke,
|
saveAssist,
|
saveAudit,
|
sign,
|
transfer,
|
update,
|
} from '#/api/workFlow/task';
|
import nodeConfig from '#/components/FlowProcess/src/helper/config';
|
import FlowProcessMain from '#/components/FlowProcess/src/Main.vue';
|
import PrintBrowse from '#/components/PrintDesign/printBrowse/index.vue';
|
import PrintSelect from '#/components/PrintDesign/printSelect/index.vue';
|
import { useFlowState } from '#/hooks/flow/useFlowStatus';
|
import { $t } from '#/locales';
|
import { useBaseStore } from '#/store';
|
import { getLaunchFlowParamList, getParamList, importViewsFile, onlineUtils } from '#/utils/jnpf';
|
import CustomForm from '#/views/common/dynamicModel/list/CustomForm.vue';
|
import FlowParser from '#/views/workFlow/components/FlowParser.vue';
|
|
import ExtraPanel from './ExtraPanel.vue';
|
import FlowChart from './FlowChart.vue';
|
import FlowFile from './FlowFile.vue';
|
import ActionModal from './modal/ActionModal.vue';
|
import ApprovalModal from './modal/ApprovalModal.vue';
|
import CandidateModal from './modal/CandidateModal.vue';
|
import ErrorModal from './modal/ErrorModal.vue';
|
import FreeModal from './modal/FreeModal.vue';
|
import ReduceApproverModal from './modal/ReduceApproverModal.vue';
|
import UserListModal from './modal/UserListModal.vue';
|
import NodeFormParser from './NodeFormParser.vue';
|
import OpinionTextarea from './OpinionTextarea.vue';
|
import RecordList from './RecordList.vue';
|
import Sign from './Sign.vue';
|
import StartFormParser from './StartFormParser.vue';
|
import SubFlowParser from './SubFlowParser.vue';
|
|
interface State {
|
config: any;
|
properties: any;
|
loading: boolean;
|
btnLoading: boolean;
|
allBtnDisabled: boolean;
|
flowUrgent: any;
|
leftBtnList: any[];
|
rightBtnList: any[];
|
monitorBtnList: any[];
|
todoBtnList: any[];
|
flowDetailCustomBtns: any[];
|
activeKey: any;
|
taskInfo: any;
|
formInfo: any;
|
flowInfo: any;
|
btnInfo: any;
|
nodeList: any;
|
recordList: any[];
|
recordTimeList: any[];
|
assignNodeList: any;
|
thisStep: string;
|
endTime: any;
|
currentView: any;
|
formData: any;
|
eventType: string;
|
approvalData: any;
|
resurgenceData: any;
|
flowKey: number;
|
formKey: number;
|
hasComment: boolean;
|
showDataLog: boolean;
|
isFullscreen: boolean;
|
approvalForm: any;
|
showApprovalForm: boolean;
|
isFlow: number;
|
isApprovalFile: boolean;
|
showFlowParser: boolean;
|
approverText: {
|
approver: string;
|
approverLabel: string;
|
approverTime: string;
|
};
|
lineKeyList: any[];
|
freeFlowConfig: any;
|
isFreeType: boolean;
|
customValidatePassKey: string;
|
}
|
|
defineOptions({ name: 'FlowParser' });
|
const props = defineProps({
|
defaultFullscreen: { type: Boolean, default: false },
|
showFullscreen: { type: Boolean, default: true },
|
});
|
const emit = defineEmits(['register', 'reload', 'close']);
|
const baseStore = useBaseStore();
|
const { createMessage, createConfirm } = useMessage();
|
const [registerPopup, { closePopup, setPopupProps }] = usePopupInner(init);
|
const [registerError, { openModal: openErrorModal, closeModal: closeErrorModal, setModalProps: setErrorModalProps }] = useModal();
|
const [registerCandidate, { openModal: openCandidateModal, closeModal: closeCandidateModal, setModalProps: setCandidateModalProps }] = useModal();
|
const [registerAction, { openModal: openActionModal, closeModal: closeActionModal, setModalProps: setActionModalProps }] = useModal();
|
const [registerApproval, { openModal: openApprovalModal, closeModal: closeApprovalModal, setModalProps: setApprovalModalProps }] = useModal();
|
const [registerNodeFormParser, { openPopup: openNodeFormParserPopup }] = usePopup();
|
const [registerSubFlowParser, { openPopup: openSubFlowParserPopup }] = usePopup();
|
const [registerStartFormParser, { openPopup: openStartFormParserPopup }] = usePopup();
|
const [registerFlowChart, { openPopup: openFlowChartPopup }] = usePopup();
|
const [registerPrintSelect, { openModal: openPrintSelect }] = useModal();
|
const [registerPrintBrowse, { openModal: openPrintBrowse }] = useModal();
|
const [registerReduceApprover, { openModal: openReduceApprover }] = useModal();
|
const [registerFlowParser, { openPopup: openFlowParser }] = usePopup();
|
const [registerUserList, { openModal: openUserListModal }] = useModal();
|
const { getFlowStatusContent, getFlowStatusColor } = useFlowState();
|
const [registerFree, { openModal: openFreeModal, closeModal: closeFreeModal, setModalProps: setFreeModalProps }] = useModal();
|
const flowUrgentList = [
|
{ id: 1, fullName: '普通' },
|
{ id: 2, fullName: '重要' },
|
{ id: 3, fullName: '紧急' },
|
];
|
const formRef = ref<any>(null);
|
const formElRef = ref<any>(null);
|
const flowFileRef = ref<any>(null);
|
const customFormRef = ref<any>(null);
|
const globSetting = useGlobSetting();
|
const apiUrl = ref(globSetting.apiURL);
|
const userStore = useUserStore();
|
const userInfo = userStore.getUserInfo;
|
const state = reactive<State>({
|
config: {},
|
properties: {},
|
loading: false,
|
btnLoading: false,
|
allBtnDisabled: false,
|
flowUrgent: 1,
|
leftBtnList: [],
|
rightBtnList: [],
|
monitorBtnList: [],
|
todoBtnList: [],
|
flowDetailCustomBtns: [],
|
activeKey: ' 1',
|
taskInfo: {},
|
formInfo: {},
|
flowInfo: {},
|
btnInfo: {},
|
nodeList: [],
|
recordList: [],
|
recordTimeList: [],
|
assignNodeList: [],
|
thisStep: '',
|
endTime: 0,
|
currentView: null,
|
formData: {},
|
eventType: '',
|
approvalData: {},
|
resurgenceData: {},
|
flowKey: 0,
|
formKey: 0,
|
hasComment: false,
|
showDataLog: false,
|
isFullscreen: false,
|
approvalForm: {
|
handleOpinion: '',
|
fileList: [],
|
signImg: '',
|
useSignNext: false,
|
},
|
showApprovalForm: false,
|
isFlow: 0,
|
isApprovalFile: false,
|
showFlowParser: false,
|
approverText: {
|
approverLabel: '审批意见',
|
approver: '审批人',
|
approverTime: '审批时间',
|
},
|
lineKeyList: [],
|
freeFlowConfig: {},
|
isFreeType: false,
|
customValidatePassKey: '',
|
});
|
const customValidateEventMap: Record<string, string> = {
|
submit: 'initFuncConfig',
|
delegateSubmit: 'initFuncConfig',
|
audit: 'approveFuncConfig',
|
reject: 'rejectFuncConfig',
|
back: 'backFuncConfig',
|
auditRecall: 'recallFuncConfig',
|
launchRecall: 'flowRecallFuncConfig',
|
};
|
const {
|
config,
|
loading,
|
btnLoading,
|
allBtnDisabled,
|
flowKey,
|
formKey,
|
taskInfo,
|
monitorBtnList,
|
leftBtnList,
|
rightBtnList,
|
todoBtnList,
|
eventType,
|
isFullscreen,
|
approvalForm,
|
showApprovalForm,
|
properties,
|
btnInfo,
|
showFlowParser,
|
} = toRefs(state);
|
|
const getTitle = computed(() => {
|
if (state.flowInfo?.type === 2) return state.flowInfo.fullName || '';
|
const fullName = state.config.fullName || '';
|
if ([4, 5, 6].includes(state.config.opType)) return fullName;
|
return state.thisStep ? `${fullName}/${state.thisStep}` : fullName;
|
});
|
const getUrgentName = computed(() => {
|
const list: any = flowUrgentList.filter((o) => o.id === state.flowUrgent);
|
if (!list.length) return '普通';
|
return list[0].fullName || '普通';
|
});
|
const getFlowStatusClass = computed(() => {
|
const status = state.taskInfo.status;
|
if (status == 1) return 'doing'; // 进行中
|
if (status == 2) return 'adopt'; // 已通过
|
if (status == 3) return 'reject'; // 已拒绝
|
if (status == 4) return 'cancel'; // 已终止
|
if (status == 5) return 'pause'; // 已暂停
|
if (status == 6) return 'revoking'; // 撤销中
|
if (status == 7) return 'revoke'; // 已撤销
|
if (status == 8) return 'back'; // 已退回
|
if (status == 9) return 'recall'; // 已撤回
|
return '';
|
});
|
const getFlowStatusIcon = computed(() => state.config.opType == 6 && state.activeKey === '2' && state.flowInfo?.type !== 2);
|
const getShowExtraPanel = computed(() => state.config.opType != '-1' && (state.hasComment || state.config.opType != '6') && !state.loading);
|
const getBindExtraPanelValue = computed(() => ({
|
taskId: state.config.id,
|
recordTimeList: state.recordTimeList,
|
nodeList: state.nodeList,
|
flowInfo: state.flowInfo,
|
showComment: state.hasComment,
|
showRecord: state.config.opType != '6',
|
showDataLog: state.showDataLog,
|
modelId: state.formInfo?.id,
|
formDataId: state.config?.formData?.id,
|
auxiliaryInfo: state.properties.auxiliaryInfo || [],
|
formData: state.config?.formData,
|
}));
|
const getFullscreenTitle = computed(() => (state.isFullscreen ? $t('layout.header.tooltipExitFull') : $t('layout.header.tooltipEntryFull')));
|
const getHeaderCancelVisible = computed(() => state.config.showHeaderCancelBtn || (state.config.opType == '-1' && !state.config.hideCancelBtn));
|
const getShowFlowDetailCustomBtns = computed(() => state.config.disabled && state.config.opType != 6 && !!state.flowDetailCustomBtns.length);
|
|
function init(data) {
|
state.hasComment = false;
|
state.showDataLog = false;
|
state.isApprovalFile = false;
|
state.showApprovalForm = false;
|
state.approvalForm = {
|
handleOpinion: '',
|
fileList: [],
|
signImg: '',
|
useSignNext: false,
|
};
|
state.isFullscreen = props.defaultFullscreen || false;
|
state.loading = true;
|
state.btnLoading = false;
|
state.config = data;
|
state.config.disabled = data.opType != 3 && data.opType != '-1';
|
state.formData = {};
|
state.flowDetailCustomBtns = [];
|
state.flowUrgent = 1;
|
state.activeKey = data.opType == 6 ? '2' : '1';
|
state.isFlow = data.isFlow || 0;
|
state.freeFlowConfig = {};
|
/**
|
* opType
|
* -1 - 我发起的新建/编辑
|
* 0 - 我发起的详情
|
* 1 - 待签事宜
|
* 2 - 待办事宜
|
* 3 - 在办事宜
|
* 4 - 已办事宜
|
* 5 - 抄送事宜
|
* 6 - 流程监控
|
*/
|
getBeforeInfo(state.config);
|
}
|
function getBeforeInfo(config) {
|
const query: any = {
|
taskNodeId: config.taskNodeId,
|
operatorId: config.operatorId,
|
flowId: config.flowId, // 流程大Id
|
opType: config.opType == 2 ? 3 : config.opType, // 特殊:待办点击开始办理需要跳到在办页面
|
isFlow: state.isFlow,
|
};
|
if (config.type) query.type = config.type;
|
getFlowTaskInfo(config.id || '0', query)
|
.then((res) => {
|
state.formInfo = res.data.formInfo || {};
|
state.taskInfo = res.data.taskInfo || {};
|
state.flowInfo = res.data.flowInfo || {};
|
state.btnInfo = res.data.btnInfo || {};
|
state.lineKeyList = res.data.lineKeyList || [];
|
const fullName = config.opType == '-1' ? state.flowInfo.fullName : state.taskInfo.fullName;
|
config.fullName = fullName;
|
state.thisStep = state.taskInfo.currentNodeName || '';
|
state.flowUrgent = state.taskInfo.flowUrgent || 1;
|
config.status = state.taskInfo.status;
|
config.type = state.flowInfo.type;
|
config.draftData = res.data.draftData || null;
|
config.formData = res.data.formData || {};
|
config.formId = state.formInfo.id;
|
config.formEnCode = state.formInfo.enCode;
|
state.nodeList = res.data.nodeList || [];
|
state.properties = { ...nodeConfig.defaultNodeProperties, ...res.data.nodeProperties };
|
state.approverText = {
|
approverLabel: res.data.nodeProperties?.type === 'processing' ? '办理意见' : '审批意见',
|
approver: res.data.nodeProperties?.type === 'processing' ? '办理人' : '审批人',
|
approverTime: res.data.nodeProperties?.type === 'processing' ? '办理时间' : '审批时间',
|
};
|
state.recordList = (res.data.recordList || []).reverse();
|
state.recordTimeList = (res.data.progressList || []).reverse();
|
// 设置默认审批data
|
state.approvalData = {
|
fileList: [],
|
handleOpinion: '',
|
signImg: '',
|
copyIds: '',
|
backNodeCode: '',
|
backType: state.properties.backType || 1,
|
};
|
config.formConf = state.formInfo.formData;
|
config.formOperates = res.data.formOperates || [];
|
state.hasComment = state.flowInfo?.flowNodes?.global?.hasComment || false;
|
const formConf = getFlowFormConf();
|
state.formData = getFlowDetailButtonData();
|
state.flowDetailCustomBtns = getEnabledFlowDetailCustomBtns(formConf?.customBtns || []);
|
state.showDataLog = state.formInfo.type === 1 ? !!formConf?.dataLog : false;
|
state.isFreeType = res.data.flowInfo.type === 3;
|
if (config.opType == 0) {
|
for (let i = 0; i < config.formOperates.length; i++) {
|
config.formOperates[i].write = false;
|
}
|
}
|
state.assignNodeList = state.nodeList.filter((o) => config.opType == 6 && o.type == 1 && (o.nodeType === 'approver' || o.nodeType === 'processing'));
|
initBtnList();
|
initApprovalField();
|
if (config.opType == 6) {
|
state.activeKey = '2';
|
setPageLoad();
|
state.flowKey = Date.now();
|
return;
|
}
|
const formUrl =
|
state.formInfo.type == 1
|
? 'workFlow/workFlowForm/dynamicForm'
|
: state.formInfo.urlAddress
|
? state.formInfo.urlAddress.replaceAll(/\s*/g, '')
|
: `workFlow/workFlowForm/${state.formInfo.enCode}`;
|
state.currentView = markRaw(defineAsyncComponent(() => importViewsFile(formUrl)));
|
state.flowKey = Date.now();
|
})
|
.catch(() => {
|
state.loading = false;
|
});
|
}
|
function initBtnList() {
|
const properties = state.properties;
|
const btnInfo = state.btnInfo;
|
state.monitorBtnList = [];
|
state.leftBtnList = [];
|
state.rightBtnList = [];
|
state.todoBtnList = [];
|
// 流程监控
|
if (state.config.opType == 6) {
|
if (btnInfo?.hasCancelBtn) state.monitorBtnList.push({ id: 'cancel', fullName: '终止', type: 'error' });
|
if (btnInfo?.hasActivateBtn) state.monitorBtnList.push({ id: 'resurgence', fullName: '复活', type: 'primary' });
|
if (btnInfo?.hasPauseBtn) state.monitorBtnList.push({ id: 'pause', fullName: '暂停', type: 'info' });
|
if (btnInfo?.hasRebootBtn) state.monitorBtnList.push({ id: 'reboot', fullName: '恢复' });
|
if (btnInfo?.hasAssignBtn) state.monitorBtnList.push({ id: 'assign', fullName: '指派', type: 'warning' });
|
if (btnInfo?.hasFileBtn) state.monitorBtnList.push({ id: 'file', fullName: '归档', type: 'primary' });
|
} else {
|
// 右侧按钮
|
// 提交
|
if (btnInfo?.hasSaveBtn && !state.config.hideSaveBtn) state.rightBtnList.push({ id: 'save', fullName: properties.saveBtnText || '暂存', type: '' });
|
if (btnInfo?.hasSubmitBtn) state.rightBtnList.push({ id: 'submit', fullName: properties.submitBtnText || '提交', type: 'primary' });
|
if (btnInfo?.hasDelegateSubmitBtn) state.rightBtnList.push({ id: 'delegateSubmit', fullName: '委托发起', type: 'primary' });
|
// 待签
|
if (btnInfo?.hasSignBtn) state.rightBtnList.push({ id: 'sign', fullName: btnInfo?.proxyMark ? '代签' : '签收', type: 'primary' });
|
// 特殊:待办点击开始办理需要跳到在办页面
|
if (state.config.opType == 2) {
|
// 待办
|
const hasCancelSignBtn = baseStore?.getSysConfig?.flowSign && state.flowInfo?.flowNodes?.global?.hasSignFor;
|
if (hasCancelSignBtn) state.todoBtnList.push({ id: 'cancelSign', fullName: '退签', type: 'warning' });
|
state.todoBtnList.push({ id: 'startHandle', fullName: btnInfo?.proxyMark ? '代办' : '开始办理', type: 'primary' });
|
}
|
// 在办
|
if (btnInfo?.hasRecallLaunchBtn) state.rightBtnList.push({ id: 'launchRecall', fullName: '撤回', type: 'warning' });
|
if (btnInfo?.hasRecallAuditBtn) state.rightBtnList.push({ id: 'auditRecall', fullName: '撤回', type: 'warning' });
|
if (btnInfo?.hasAssistSaveBtn) state.rightBtnList.push({ id: 'saveAssist', fullName: '保存' });
|
if (btnInfo?.hasRejectBtn && state.properties.type != 'processing')
|
state.rightBtnList.push({ id: 'reject', fullName: properties.rejectBtnText || '拒绝', type: 'error' });
|
if (btnInfo?.hasAuditBtn)
|
state.rightBtnList.push({
|
id: 'audit',
|
fullName: properties.auditBtnText || (state.properties.type === 'processing' ? '确认办理' : '同意'),
|
type: 'primary',
|
});
|
if (btnInfo?.hasReduceApproverBtn) state.rightBtnList.push({ id: 'reduceApprover', fullName: properties.reduceApproverBtnText || '减签', type: 'primary' });
|
// 左侧按钮
|
if (btnInfo?.hasRevokeBtn) state.leftBtnList.push({ id: 'revoke', fullName: '撤销' });
|
if (btnInfo?.hasPressBtn) state.leftBtnList.push({ id: 'press', fullName: properties.pressBtnText || '催办' });
|
if (btnInfo?.hasAssistBtn) state.leftBtnList.push({ id: 'assist', fullName: properties.assistBtnText || '协办' });
|
if (btnInfo?.hasTransferBtn) state.leftBtnList.push({ id: 'transfer', fullName: properties.transferBtnText || '转审' });
|
if (btnInfo?.hasFreeApproverBtn) state.leftBtnList.push({ id: 'freeApprover', fullName: properties.freeApproverBtnText || '加签' });
|
if (btnInfo?.hasBackBtn) state.leftBtnList.push({ id: 'back', fullName: properties.backBtnText || '退回' });
|
if (btnInfo?.hasSaveAuditBtn) state.leftBtnList.push({ id: 'saveAudit', fullName: '暂存' });
|
// 流程打印
|
if (btnInfo?.hasPrintBtn) state.leftBtnList.push({ id: 'print', fullName: '打印' });
|
// 自定义按钮
|
if (properties?.customBtns?.length) {
|
properties?.customBtns.map((item: any) => {
|
state.leftBtnList.push({ id: item.value, fullName: item.label, jsJson: item.jsJson });
|
return item;
|
});
|
}
|
}
|
}
|
function eventLauncher(eventType) {
|
formRef.value?.dataFormSubmit(eventType, state.flowUrgent);
|
}
|
function eventReceiver(formData, eventType) {
|
state.formData = formData;
|
state.formData.flowId = state.flowInfo.flowId;
|
state.formData.id = state.config.id;
|
state.eventType = eventType;
|
state.customValidatePassKey = '';
|
// 发起暂存/提交
|
if (eventType === 'save' || eventType === 'submit') return submitOrSave();
|
// 委托发起
|
if (eventType === 'delegateSubmit') return handleDelegateSubmit();
|
// 审批暂存/协办保存
|
if (eventType === 'saveAudit' || eventType === 'saveAssist') return handleSaveAudit();
|
// 同意/拒绝
|
if (eventType === 'audit' || eventType === 'reject') return handleAuditOrReject();
|
// 加签
|
if (eventType === 'freeApprover') return handleFreeApprover();
|
}
|
async function runCustomValidate(eventType, data: any = {}) {
|
const eventConfigKey = customValidateEventMap[eventType];
|
if (!eventConfigKey) return true;
|
const validateFuncConfig = getCustomValidateFuncConfig(eventConfigKey);
|
if (!validateFuncConfig?.on) return true;
|
if (!validateFuncConfig.interfaceId) {
|
createMessage.error('请配置自定义验证接口');
|
return false;
|
}
|
const validatePassKey = `${eventType}_${validateFuncConfig.interfaceId}`;
|
if (state.customValidatePassKey === validatePassKey) return true;
|
const flowId = state.flowInfo?.flowId || state.config?.flowId || state.config?.flow_id || state.formData?.flowId || state.formData?.flow_id;
|
const taskId = state.config?.id || state.config?.taskId || state.config?.task_id || state.formData?.taskId || state.formData?.task_id;
|
const formData = {
|
...state.config?.formData,
|
...state.formData,
|
...data,
|
id: state.config?.formData?.id || state.formData?.id || state.config?.id,
|
taskId,
|
task_id: taskId,
|
flowId,
|
flow_id: flowId,
|
operatorId: state.config?.operatorId,
|
};
|
const templateJson = (validateFuncConfig.templateJson || []).map((o) => ({ ...o }));
|
const paramList = ensureFlowValidateParamList(getParamList(templateJson, formData) || [], flowId, taskId);
|
const query = { paramList };
|
try {
|
const res: any = await getDataInterfaceRes(validateFuncConfig.interfaceId, query);
|
if (res?.code && res.code !== 200) {
|
createMessage.error(res.msg || '验证失败');
|
return false;
|
}
|
const bizRes = res?.data;
|
if (bizRes && typeof bizRes === 'object' && (bizRes.success === false || (bizRes.code && bizRes.code !== 200))) {
|
createMessage.error(bizRes.msg || '验证失败');
|
return false;
|
}
|
state.customValidatePassKey = validatePassKey;
|
return true;
|
} catch (error: any) {
|
createMessage.error(error?.msg || error?.message || '验证失败');
|
return false;
|
}
|
}
|
function ensureFlowValidateParamList(paramList: any[], flowId: any, taskId: any) {
|
const applyValue = (field: string, value: any) => {
|
const item = paramList.find((o) => o?.field === field);
|
if (item) {
|
if (item.defaultValue === undefined || item.defaultValue === null || item.defaultValue === '') item.defaultValue = value || '';
|
return;
|
}
|
paramList.push({ field, defaultValue: value || '', sourceType: 3 });
|
};
|
applyValue('flow_id', flowId);
|
applyValue('task_id', taskId);
|
return paramList;
|
}
|
function getCustomValidateFuncConfig(eventConfigKey) {
|
const currentConfig = state.properties?.[eventConfigKey]?.validateFuncConfig;
|
const startEventKeys = ['initFuncConfig', 'endFuncConfig', 'flowRecallFuncConfig'];
|
if (!startEventKeys.includes(eventConfigKey)) return currentConfig;
|
if (currentConfig?.on || currentConfig?.interfaceId) return currentConfig;
|
const flowNodes = state.flowInfo?.flowNodes || {};
|
const startNode = flowNodes.start || Object.values(flowNodes).find((o: any) => o?.type === 'start' || o?.nodeType === 'start');
|
return (startNode as any)?.[eventConfigKey]?.validateFuncConfig || currentConfig;
|
}
|
// 提交或者暂存
|
async function submitOrSave() {
|
state.formData.status = state.eventType === 'save' ? 0 : 1;
|
state.formData.flowUrgent = state.flowUrgent;
|
// 受委托人不为空的时候走委托创建流程
|
delete state.formData.delegateUser;
|
if (state.eventType === 'delegateSubmit') state.formData.delegateUser = state.config.delegateUser;
|
if (state.eventType === 'save') return handleRequest();
|
state.btnLoading = true;
|
if (!(await runCustomValidate(state.eventType, state.formData))) {
|
state.btnLoading = false;
|
return;
|
}
|
getCandidates('0', state.formData)
|
.then((res) => {
|
state.btnLoading = false;
|
const candidateList = res.data.list.filter((o) => !o.isBranchFlow && o.isCandidates);
|
const branchList = res.data.list.filter((o) => o.isBranchFlow);
|
if (!candidateList.length && res.data.type == 3) {
|
handleRequest();
|
return;
|
}
|
// 自由流
|
if (res.data.type === 4) {
|
openFreeModal(true, {
|
isCustomCopy: state.properties.isCustomCopy,
|
formData: state.formData,
|
oneSelfEndApproval: false,
|
});
|
return;
|
}
|
openCandidateModal(true, {
|
branchList,
|
candidateList,
|
isCustomCopy: state.properties.isCustomCopy,
|
formData: state.formData,
|
eventType: state.eventType,
|
});
|
})
|
.catch(() => {
|
state.btnLoading = false;
|
});
|
}
|
// 提交或者暂存请求接口
|
async function handleRequest(candidateData: any = null) {
|
if (candidateData) state.formData = { ...state.formData, ...candidateData };
|
if (!state.formData.id) delete state.formData.id;
|
state.formData.isFlow = state.isFlow;
|
if (state.eventType !== 'save' && !(await runCustomValidate(state.eventType, state.formData))) {
|
setCandidateModalProps({ confirmLoading: false });
|
setFreeModalProps({ confirmLoading: false });
|
setErrorModalProps({ confirmLoading: false });
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
return;
|
}
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
const formMethod = state.formData.id ? update : create;
|
formMethod(state.formData)
|
.then((res) => {
|
handleError(res);
|
})
|
.catch(() => {
|
setCandidateModalProps({ confirmLoading: false });
|
setFreeModalProps({ confirmLoading: false });
|
setErrorModalProps({ confirmLoading: false });
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
});
|
}
|
// 异常处理
|
function handleError(res) {
|
const errorData = res.data?.errorCodeList;
|
if (errorData && Array.isArray(errorData) && errorData.length) {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
openErrorModal(true, { errorData });
|
} else {
|
createMessage.success(res.msg).then(() => {
|
closeErrorModal();
|
closeCandidateModal();
|
closeApprovalModal();
|
closeActionModal();
|
closeFreeModal();
|
if (res.data?.isEnd) {
|
state.isApprovalFile = true;
|
handleFile(res.data.taskId);
|
return;
|
}
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
handleClose(true);
|
});
|
}
|
}
|
// 异常处理弹窗提交
|
function errorReceiver(data) {
|
state.formData.errorRuleUserList = data;
|
if (state.eventType === 'submit') return handleRequest();
|
if (state.eventType === 'audit' || state.eventType === 'reject') return handleApproval();
|
}
|
// 关闭异常处理弹窗
|
function onErrorModalClose() {
|
setCandidateModalProps({ confirmLoading: false });
|
setApprovalModalProps({ confirmLoading: false });
|
setFreeModalProps({ confirmLoading: false });
|
setActionModalProps({ confirmLoading: false });
|
setErrorModalProps({ confirmLoading: false });
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
}
|
// 审批通过/审批退回/加签
|
async function handleApproval() {
|
const query = {
|
...state.approvalData,
|
...state.formData,
|
...state.freeFlowConfig,
|
};
|
if (state.eventType === 'audit') query.handleStatus = 1;
|
if (state.eventType === 'reject') query.handleStatus = 0;
|
const approvalMethod = state.eventType === 'freeApprover' ? freeApprover : state.eventType === 'back' ? back : audit;
|
if (!(await runCustomValidate(state.eventType, query))) {
|
setCandidateModalProps({ confirmLoading: false });
|
setApprovalModalProps({ confirmLoading: false });
|
setFreeModalProps({ confirmLoading: false });
|
setErrorModalProps({ confirmLoading: false });
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
return;
|
}
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
approvalMethod(state.config.operatorId, query)
|
.then((res) => {
|
handleError(res);
|
if (query.useSignNext) userStore.setUserInfo({ signImg: query.signImg, signId: query.signId });
|
})
|
.catch(() => {
|
onErrorModalClose();
|
});
|
}
|
// 审批通过/审批退回/加签弹窗提交
|
function approvalReceiver(data) {
|
state.approvalData = data;
|
handleApproval();
|
}
|
// 审批暂存
|
function handleSaveAudit() {
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
const save = state.eventType == 'saveAssist' ? saveAssist : saveAudit;
|
save(state.config.operatorId, state.formData)
|
.then((res) => {
|
createMessage.success(res.msg).then(() => {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
handleClose(true);
|
});
|
})
|
.catch(() => {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
});
|
}
|
// 流程催办
|
function handlePress() {
|
createConfirm({
|
iconType: 'warning',
|
title: '提示',
|
content: '此操作将提示该节点尽快处理,是否继续?',
|
onOk: () => {
|
press(state.config.id).then((res) => {
|
createMessage.success(res.msg);
|
});
|
},
|
});
|
}
|
// 撤回、转审、终止、驳回等操作弹窗
|
function actionLauncher(eventType) {
|
state.eventType = eventType;
|
state.customValidatePassKey = '';
|
const list = ['sign', 'cancelSign', 'startHandle'];
|
if (list.includes(eventType) || (eventType == 'cancel' && state.flowInfo?.type == 2)) {
|
let content = '';
|
if (eventType == 'sign') content = '确定签收,签收后进入待办。';
|
if (eventType == 'cancelSign') content = '确定退签,确定后进入我的待签。';
|
if (eventType == 'startHandle') content = '确定开始办理流程';
|
if (eventType == 'cancel') content = '终止后流程不可恢复,是否继续?';
|
createConfirm({
|
iconType: 'warning',
|
title: '提示',
|
content,
|
onOk: () => {
|
actionReceiver();
|
},
|
});
|
return;
|
}
|
openActionModal(true, { properties: state.properties, assignNodeList: state.assignNodeList, eventType });
|
}
|
// 签收、办理、撤回、转审、终止、驳回等操作请求
|
async function actionReceiver(data: any = null) {
|
if (!data) data = { handleOpinion: '', signImg: '', fileList: [] };
|
const list = ['resurgence', 'assign', 'pause', 'revoke', 'cancel', 'launchRecall'];
|
const id = list.includes(state.eventType) ? state.config.id : state.config.operatorId;
|
const actionMethod = getActionMethod();
|
if (!(await runCustomValidate(state.eventType, data))) {
|
setActionModalProps({ confirmLoading: false });
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
return;
|
}
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
actionMethod(id, data)
|
.then((res) => {
|
state.eventType == 'startHandle' ? handleStartHandle(res) : handleError(res);
|
})
|
.catch(() => {
|
setActionModalProps({ confirmLoading: false });
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
});
|
}
|
function getActionMethod() {
|
if (state.eventType === 'sign') return sign;
|
if (state.eventType === 'cancelSign') return cancelSign;
|
if (state.eventType === 'startHandle') return handle;
|
if (state.eventType === 'transfer') return transfer;
|
if (state.eventType === 'assist') return assist;
|
if (state.eventType === 'assign') return assign;
|
if (state.eventType === 'revoke') return revoke;
|
if (state.eventType === 'launchRecall') return launchRecall;
|
if (state.eventType === 'auditRecall') return auditRecall;
|
if (state.eventType === 'cancel') return cancel;
|
if (state.eventType === 'pause') return pause;
|
if (state.eventType === 'resurgence') return resurgence;
|
return cancel;
|
}
|
function handleUrgentClick({ key }) {
|
state.flowUrgent = key;
|
}
|
// 更多按钮事件
|
function handleBtnClick(key) {
|
state.eventType = key;
|
const list = ['save', 'submit', 'delegateSubmit', 'audit', 'reject', 'saveAudit', 'saveAssist', 'freeApprover']; // 发起暂存,委托发起,流程提交,同意,拒绝,审批暂存,协办暂存,
|
if (list.includes(key)) return eventLauncher(key);
|
if (key == 'reduceApprover') return handleReduceApprover(); // 减签
|
if (key == 'back') return handleBack(); // 退回
|
if (key == 'press') return handlePress(); // 催办
|
if (key == 'resurgence') return handleOpenResurgenceModal(key); // 流程监控复活
|
if (key == 'pause') return handleOpenPauseModal(key); // 流程监控暂停
|
if (key == 'reboot') return handleReboot(); // 流程监控恢复
|
if (key == 'print') return handlePrint(); // 流程打印
|
if (key == 'file') return handleFile(); // 流程归档
|
if (key.includes('btn_')) return handleCustomBtns(key);
|
actionLauncher(key);
|
}
|
// 委托发起
|
function handleDelegateSubmit() {
|
getDelegateUser(state.flowInfo.id).then((res) => {
|
const userList = res.data.list || [];
|
if (userList.length == 1) return selectUser(userList[0].id);
|
if (userList.length) return openUserListModal(true, { userList });
|
});
|
}
|
function selectUser(id) {
|
state.config.delegateUser = id;
|
submitOrSave();
|
}
|
// 审批、拒绝
|
async function handleAuditOrReject() {
|
try {
|
const values = await formElRef.value?.validate();
|
if (
|
!values &&
|
((state.properties.hasOpinion && state.properties.opinionRequired) ||
|
state.properties.hasSign ||
|
state.properties.hasFile ||
|
state.properties.hasApprovalField)
|
)
|
return (state.showApprovalForm = true);
|
state.formData.handleStatus = state.eventType === 'audit' ? 1 : 0;
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
if (!(await runCustomValidate(state.eventType, { ...state.formData, ...state.approvalForm }))) {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
return;
|
}
|
getCandidates(state.config.operatorId, state.formData)
|
.then((res) => {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
const candidateList = res.data.list.filter((o) => !o.isBranchFlow && o.isCandidates);
|
const branchList = res.data.list.filter((o) => o.isBranchFlow);
|
if (!candidateList.length && !state.properties.isCustomCopy && res.data.type == 3) {
|
state.approvalData = state.approvalForm;
|
handleApproval();
|
return;
|
}
|
// 自由流
|
if (res.data.type === 4) {
|
state.approvalData = state.approvalForm;
|
openFreeModal(true, {
|
branchList,
|
candidateList,
|
isCustomCopy: state.properties.isCustomCopy,
|
formData: state.formData,
|
eventType: state.eventType,
|
operatorId: state.config.operatorId,
|
oneSelfEndApproval: res.data.oneSelfEndApproval,
|
});
|
return;
|
}
|
openCandidateModal(true, {
|
branchList,
|
candidateList,
|
isCustomCopy: state.properties.isCustomCopy,
|
formData: state.formData,
|
eventType: state.eventType,
|
operatorId: state.config.operatorId,
|
});
|
})
|
.catch(() => {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
});
|
} catch {
|
state.showApprovalForm = true;
|
}
|
}
|
// 加签
|
function handleFreeApprover() {
|
state.btnLoading = true;
|
getCandidates(state.config.operatorId, state.formData)
|
.then((res) => {
|
state.btnLoading = false;
|
const candidateList = res.data.list.filter((o) => !o.isBranchFlow && o.isCandidates);
|
const branchList = res.data.list.filter((o) => o.isBranchFlow);
|
openApprovalModal(true, {
|
branchList,
|
candidateList,
|
backNodeCodeList: [],
|
operatorId: state.config.operatorId,
|
formData: state.formData,
|
eventType: state.eventType,
|
properties: state.properties,
|
});
|
})
|
.catch(() => {
|
state.btnLoading = false;
|
});
|
}
|
// 退回
|
function handleBack() {
|
state.btnLoading = true;
|
getBackNodeCodeList(state.config.operatorId)
|
.then((res) => {
|
state.btnLoading = false;
|
const data = {
|
backNodeCodeList: res.data.list || [],
|
branchList: [],
|
candidateList: [],
|
operatorId: state.config.operatorId,
|
formData: state.formData,
|
eventType: state.eventType,
|
properties: state.properties,
|
};
|
openApprovalModal(true, data);
|
})
|
.catch(() => {
|
state.btnLoading = false;
|
});
|
}
|
// 流程复活
|
function handleOpenResurgenceModal(key) {
|
openActionModal(true, { properties: state.properties, eventType: key });
|
}
|
// 流程暂停
|
function handleOpenPauseModal(key) {
|
getPauseType(state.config.id).then((res) => {
|
openActionModal(true, { properties: state.properties, eventType: key, pauseType: res.data || false });
|
});
|
}
|
// 流程恢复
|
function handleReboot() {
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
reboot(state.config.id).then((res) => {
|
createMessage.success(res.msg).then(() => {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
handleClose(true);
|
});
|
});
|
}
|
// 流程打印
|
function handlePrint() {
|
if (!state.properties.printConfig.printIds?.length) return createMessage.error('未配置打印模板');
|
if (state.properties.printConfig.printIds?.length === 1) return handleShowBrowse(state.properties.printConfig.printIds[0]);
|
openPrintSelect(true, state.properties.printConfig.printIds);
|
}
|
function handleShowBrowse(id) {
|
openPrintBrowse(true, { id, formInfo: [{ formId: state.config.formData.id, flowTaskId: state.config.id }] });
|
}
|
function getFlowFormConf() {
|
if (state.formInfo.type !== 1 || !state.config.formConf) return {};
|
try {
|
return JSON.parse(state.config.formConf);
|
} catch (error) {
|
console.error('[FlowParser] parse form config error:', error);
|
return {};
|
}
|
}
|
function getFlowDetailButtonData() {
|
const data = { ...state.config.formData };
|
const formDataId = state.config.formData?.id || data.id;
|
return {
|
...data,
|
id: formDataId || state.config.id,
|
flowId: state.flowInfo?.flowId || state.config.flowId,
|
flowTaskId: state.config.id,
|
operatorId: state.config.operatorId,
|
taskId: state.config.id,
|
};
|
}
|
function getEnabledFlowDetailCustomBtns(customBtns: any[] = []) {
|
return [...customBtns].reverse().filter((item) => getFlowDetailCustomBtnEnable(item));
|
}
|
function getFlowDetailCustomBtnEnable(item) {
|
const enableFunc = item?.actionConfig?.enableFunc;
|
if (!enableFunc) return true;
|
const func: any = getScriptFunc(enableFunc);
|
if (!func) return true;
|
try {
|
return !!func({ data: getFlowDetailButtonData(), onlineUtils: getFlowDetailOnlineUtils() });
|
} catch (error) {
|
console.error('[FlowParser] form detail custom button enable rule error:', error);
|
return false;
|
}
|
}
|
function handleFlowDetailCustomBtn(item) {
|
if (item.actionConfig.btnType == 1) handleFlowDetailPopup(item.actionConfig);
|
if (item.actionConfig.btnType == 2) handleFlowDetailScriptFunc(item.actionConfig);
|
if (item.actionConfig.btnType == 3) handleFlowDetailInterface(item.actionConfig);
|
if (item.actionConfig.btnType == 4) handleFlowDetailLaunchFlow(item);
|
}
|
function handleFlowDetailPopup(item) {
|
const data = {
|
...item,
|
recordModelId: state.formInfo.id,
|
record: getFlowDetailButtonData(),
|
menuId: item.modelId,
|
};
|
customFormRef.value?.init(data);
|
}
|
function handleFlowDetailScriptFunc(item) {
|
const func: any = getScriptFunc(item.func);
|
if (!func) return;
|
func({ data: getFlowDetailButtonData(), onlineUtils: getFlowDetailOnlineUtils() });
|
}
|
function handleFlowDetailInterface(item) {
|
const handlerInterface = () => {
|
const data = getFlowDetailButtonData();
|
const query = { paramList: getParamList(item.templateJson, data) || [] };
|
getDataInterfaceRes(item.interfaceId, query).then((res) => {
|
createMessage.success(res.msg);
|
});
|
};
|
if (!item.useConfirm) return handlerInterface();
|
createConfirm({
|
iconType: 'warning',
|
title: $t('common.tipTitle'),
|
content: item.confirmTitle || '确认执行此操作?',
|
onOk: handlerInterface,
|
});
|
}
|
function handleFlowDetailLaunchFlow(item) {
|
const launchFlowCfg = cloneDeep(item.actionConfig.launchFlow);
|
const query = {
|
template: launchFlowCfg.flowId,
|
btnCode: item.value,
|
currentUser: launchFlowCfg.currentUser,
|
customUser: launchFlowCfg.customUser,
|
initiator: launchFlowCfg.initiator,
|
hasPermission: launchFlowCfg.hasPermission,
|
dataList: [getLaunchFlowParamList(launchFlowCfg.transferList, getFlowDetailButtonData())],
|
};
|
launchFlow(state.formInfo.id, query).then((res) => {
|
createMessage.success(res.msg);
|
});
|
}
|
function getFlowDetailOnlineUtils() {
|
return {
|
...onlineUtils,
|
refresh: () => {
|
state.loading = true;
|
getBeforeInfo(state.config);
|
},
|
requestFormData: (configOrId?: any, idArg?: number | string) => {
|
const data = getFlowDetailButtonData();
|
const config = typeof configOrId === 'object' && !Array.isArray(configOrId) ? { ...configOrId } : { id: idArg ?? configOrId ?? data.id };
|
return onlineUtils.requestFormData({
|
id: data.id,
|
modelId: state.formInfo.id,
|
flowId: state.flowInfo?.flowId || state.config.flowId,
|
...config,
|
});
|
},
|
};
|
}
|
// 流程监控查看各节点表单详情
|
function viewNodeDetail(data) {
|
openNodeFormParserPopup(true, { ...data, flowId: state.flowInfo.flowId });
|
}
|
// 查看子流程
|
function viewSubFlow(nodeCode) {
|
const data = {
|
opType: state.config.opType,
|
nodeCode,
|
taskId: state.config.id,
|
};
|
openSubFlowParserPopup(true, data);
|
}
|
function viewStartForm() {
|
openStartFormParserPopup(true, { taskId: state.config.id, id: state.config.id, title: state.config.fullName });
|
}
|
function setPageLoad(val: any = false) {
|
state.loading = !!val;
|
}
|
function handleClose(reload = false) {
|
emit('close');
|
state.currentView = null;
|
closePopup();
|
if (reload) {
|
try {
|
state.config.onSuccess?.();
|
state.config.success?.();
|
} catch (error) {
|
console.error('[onlineUtils.openFlowForm] onSuccess error:', error);
|
}
|
}
|
if (reload) emit('reload');
|
}
|
function onClose() {
|
emit('close');
|
state.currentView = null;
|
return true;
|
}
|
// 显示减签弹窗
|
function handleReduceApprover() {
|
openReduceApprover(true, { id: state.config.operatorId });
|
}
|
function handleShowApprovalForm() {
|
state.showApprovalForm = !state.showApprovalForm;
|
}
|
// 打开流程图
|
function handleOpenFlowInfo() {
|
openFlowChartPopup(true, {
|
flowInfo: state.flowInfo,
|
nodeList: state.nodeList,
|
opType: state.config.opType,
|
taskId: state.config.id,
|
isRevokeTask: state.taskInfo.isRevokeTask,
|
lineKeyList: state.lineKeyList,
|
isFreeType: state.isFreeType,
|
});
|
}
|
// 流程归档
|
function handleFile(task?) {
|
state.allBtnDisabled = true;
|
state.btnLoading = true;
|
nextTick(() => {
|
flowFileRef.value?.init({
|
id: state.flowInfo?.flowNodes?.global?.fileConfig?.templateId,
|
formInfo: [{ formId: state.config.formData.id, flowTaskId: task || state.config.id }],
|
opType: state.config.opType,
|
});
|
});
|
}
|
// 自定义按钮
|
function handleCustomBtns(key) {
|
state.leftBtnList.length &&
|
state.leftBtnList.map((item) => {
|
if (item.id === key) {
|
const parameter = { flowInfo: state.flowInfo, formData: state.formData, taskInfo: state.taskInfo, onlineUtils };
|
const func: any = getScriptFunc(item.jsJson);
|
func && func(parameter);
|
}
|
return item;
|
});
|
return null;
|
}
|
function handleFileEnd() {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
if (state.config.opType == '6') state.monitorBtnList = state.monitorBtnList.filter((o) => o.id != 'file');
|
if (state.isApprovalFile) {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
handleClose(true);
|
}
|
}
|
// 切换全屏
|
function handleToggleFullscreen() {
|
state.isFullscreen = !state.isFullscreen;
|
setPopupProps({ defaultFullscreen: state.isFullscreen });
|
}
|
function onUseSignNextChange(value) {
|
state.approvalForm.useSignNext = value;
|
}
|
function onSignChange(_value, signId) {
|
state.approvalForm.signId = signId;
|
}
|
function candidateReceiver(data) {
|
if (state.eventType == 'audit' || state.eventType == 'reject') {
|
state.approvalData = { ...state.approvalForm, ...data };
|
handleApproval();
|
return;
|
}
|
handleRequest(data);
|
}
|
function submitFree(data) {
|
if (state.eventType == 'audit' || state.eventType == 'reject') {
|
state.freeFlowConfig = { ...state.freeFlowConfig, ...data };
|
handleApproval();
|
return;
|
}
|
handleRequest(data);
|
}
|
function handleStartHandle(res) {
|
createMessage.success(res.msg).then(() => {
|
state.allBtnDisabled = false;
|
state.btnLoading = false;
|
emit('reload', false);
|
state.config.opType = 3;
|
state.config.disabled = false;
|
initApprovalField();
|
state.formKey = Date.now();
|
});
|
}
|
function initApprovalField() {
|
state.approvalForm.approvalField = [];
|
if (state.config.opType == 3 && state.properties.hasApprovalField) {
|
const approvalFieldList = state.flowInfo?.flowNodes?.global?.approvalFieldList || [];
|
state.properties.approvalField.map((o) => {
|
const list = approvalFieldList.filter((item) => item.id == o);
|
list?.length && state.approvalForm.approvalField.push({ ...list[0], value: null });
|
return o;
|
});
|
}
|
}
|
function openRevokeFlow(id) {
|
state.showFlowParser = true;
|
nextTick(() => {
|
openFlowParser(true, { id, opType: 5, showFullscreen: false, isFullscreen: state.isFullscreen });
|
});
|
}
|
function onRetry(nodeId) {
|
retryOutside(nodeId).then((res) => {
|
createMessage.success(res.msg);
|
emit('reload');
|
});
|
}
|
</script>
|
<template>
|
<BasicPopup
|
v-bind="$attrs"
|
@register="registerPopup"
|
:show-back-icon="!config.hideCancelBtn"
|
:show-cancel-btn="false"
|
destroy-on-close
|
:close-func="onClose"
|
:default-fullscreen="defaultFullscreen"
|
class="full-popup basic-flow-parser">
|
<template #title>
|
<a-space :size="10">
|
<div class="header-title" :title="getTitle">{{ getTitle }}</div>
|
<template v-if="(!loading || getTitle) && state.flowInfo?.type != 2">
|
<a-dropdown :disabled="config.opType != '-1'">
|
<template #overlay>
|
<a-menu @click="handleUrgentClick">
|
<a-menu-item :key="item.id" v-for="item in flowUrgentList">
|
<div class="flow-urgent-value" :class="`urgent${item.id}`">{{ item.fullName }}</div>
|
</a-menu-item>
|
</a-menu>
|
</template>
|
<div class="flow-urgent-value" :class="`urgent${state.flowUrgent}`">{{ getUrgentName }}</div>
|
</a-dropdown>
|
</template>
|
</a-space>
|
</template>
|
<template #insertToolbar>
|
<div class="header-options" v-if="config.opType == 6">
|
<a-button
|
:color="item.type"
|
@click="handleBtnClick(item.id)"
|
:loading="btnLoading && eventType == item.id"
|
:disabled="loading || allBtnDisabled || (btnLoading && eventType !== item.id)"
|
:key="item.id"
|
v-for="item in monitorBtnList">
|
{{ item.fullName }}
|
</a-button>
|
</div>
|
<div class="header-btn" v-else>
|
<a-button class="header-cancel-btn" @click="handleClose()" v-if="getHeaderCancelVisible">取消</a-button>
|
<Tooltip title="查看发起表单" placement="bottom" v-if="btnInfo?.hasViewStartFormBtn">
|
<a-button type="text" @click="viewStartForm">
|
<i class="icon-ym icon-ym-report-icon-preview"></i>
|
</a-button>
|
</Tooltip>
|
<template v-if="getShowFlowDetailCustomBtns">
|
<a-button
|
class="flow-detail-custom-btn ml-[10px]"
|
type="primary"
|
:pre-icon="item.actionConfig?.btnIcon"
|
:disabled="loading"
|
@click="handleFlowDetailCustomBtn(item)"
|
:key="item.value"
|
v-for="item in state.flowDetailCustomBtns">
|
{{ item.labelI18nCode ? $t(item.labelI18nCode, item.label) : item.label }}
|
</a-button>
|
</template>
|
<Tooltip :title="getFullscreenTitle" placement="bottom" v-if="showFullscreen">
|
<a-button type="text" @click="handleToggleFullscreen">
|
<i class="icon-ym icon-ym-header-fullscreen" v-if="!isFullscreen"></i>
|
<i class="icon-ym icon-ym-header-fullscreen-exit" v-else></i>
|
</a-button>
|
</Tooltip>
|
<Tooltip title="流程图" :placement="isFullscreen ? 'bottomRight' : 'bottom'" @click="handleOpenFlowInfo">
|
<a-button type="text" :disabled="loading">
|
<i class="icon-ym icon-ym-flow-node-condition"></i>
|
</a-button>
|
</Tooltip>
|
</div>
|
</template>
|
<div class="jnpf-common-form-wrapper" v-loading="loading">
|
<div class="jnpf-common-form-wrapper__main">
|
<div class="approve-result" v-if="getFlowStatusIcon">
|
<div class="approve-result-img" :class="getFlowStatusClass"></div>
|
</div>
|
<a-tabs v-model:active-key="state.activeKey" class="flow-parser-tabs" :class="{ 'no-head-margin': state.activeKey == '3' }" v-if="config.opType == 6">
|
<a-tab-pane key="2" tab="流程信息" class="!overflow-hidden">
|
<FlowProcessMain
|
:flow-info="state.flowInfo"
|
:node-list="state.nodeList"
|
:is-preview="true"
|
:is-free-type="state.isFreeType"
|
:line-key-list="state.lineKeyList"
|
v-if="state.flowInfo.id"
|
:key="flowKey"
|
@view-sub-flow="viewSubFlow" />
|
</a-tab-pane>
|
<a-tab-pane key="3" tab="流转记录" class="!p-0" v-if="config.opType != '-1' || (config.opType == '-1' && config.status == 7)">
|
<RecordList
|
:list="state.recordList"
|
:op-type="config.opType"
|
:type="state.flowInfo.type"
|
:status="state.taskInfo?.status"
|
@view-detail="viewNodeDetail"
|
v-if="state.activeKey == '3'" />
|
</a-tab-pane>
|
</a-tabs>
|
<div class="flow-parser-container" v-else v-show="!loading">
|
<div class="flow-top-container" v-if="config.opType != '-1'">
|
<div class="item">
|
流程类型:<span class="content" :title="taskInfo.flowCategory">{{ taskInfo.flowCategory }}</span>
|
</div>
|
<div class="item">
|
流程状态:
|
<span class="content" :title="getFlowStatusContent(taskInfo.status)">
|
<JnpfTextTag :content="getFlowStatusContent(taskInfo.status)" :color="getFlowStatusColor(taskInfo.status)" />
|
</span>
|
</div>
|
<div class="item">
|
发起人:
|
<span class="content" :title="taskInfo.creatorUser">
|
<a-avatar :size="24" :src="apiUrl + taskInfo.headIcon" />
|
{{ taskInfo.creatorUser }}
|
</span>
|
</div>
|
<div class="item">
|
发起时间:
|
<span class="content" :title="formatToDateTime(taskInfo.creatorTime, 'YYYY-MM-DD HH:mm')">
|
{{ formatToDateTime(taskInfo.creatorTime, 'YYYY-MM-DD HH:mm') }}
|
</span>
|
</div>
|
</div>
|
<div class="flow-form-container">
|
<component
|
:is="state.currentView"
|
ref="formRef"
|
:config="config"
|
:key="formKey"
|
@event-receiver="eventReceiver"
|
@set-page-load="setPageLoad"
|
@open-revoke-flow="openRevokeFlow" />
|
</div>
|
<div class="flow-approval-form" v-if="config.opType == '3' && (btnInfo?.hasRejectBtn || btnInfo?.hasAuditBtn)">
|
<a-form
|
:colon="false"
|
:label-col="{ style: { width: '100px' } }"
|
class="opinion-btn"
|
@click="handleShowApprovalForm"
|
v-if="!!properties.hasOpinion">
|
<a-form-item :label="state.approverText.approverLabel" name="handleOpinion" :required="!!properties.opinionRequired">
|
<jnpf-input disabled :placeholder="state.approverText.approverLabel" v-model:value="approvalForm.handleOpinion" />
|
</a-form-item>
|
<div class="cover-btn"></div>
|
</a-form>
|
<Drawer placement="bottom" :closable="false" :open="showApprovalForm" :get-container="false" :style="{ position: 'absolute' }" destroy-on-close>
|
<div class="approval-form">
|
<a-form :colon="false" :label-col="{ style: { width: '100px' } }" :model="approvalForm" ref="formElRef">
|
<a-row :gutter="16">
|
<a-col :span="12">
|
<a-form-item :label="state.approverText.approver"> {{ userInfo?.userName }} </a-form-item>
|
</a-col>
|
<a-col :span="12">
|
<a-form-item :label="state.approverText.approverTime">{{ formatToDate(+new Date()) }} </a-form-item>
|
</a-col>
|
</a-row>
|
<a-form-item
|
:label="state.approverText.approverLabel"
|
name="handleOpinion"
|
:required="!!properties.opinionRequired"
|
v-if="!!properties.hasOpinion">
|
<OpinionTextarea
|
v-model:value="approvalForm.handleOpinion"
|
:message-text="state.properties?.type === 'processing' ? '请先填写办理意见' : '请先填写审批意见'" />
|
</a-form-item>
|
<a-form-item label="签名" name="signImg" :rules="[{ required: true, message: `请签名`, trigger: 'change' }]" v-if="properties.hasSign">
|
<Sign v-model:value="approvalForm.signImg" @change="onSignChange" @use-sign-next-change="onUseSignNextChange" />
|
</a-form-item>
|
<a-form-item label="附件" name="fileList" v-if="properties.hasFile">
|
<jnpf-upload-file v-model:value="approvalForm.fileList" type="workFlow" :limit="3" />
|
</a-form-item>
|
<a-form-item v-for="item in approvalForm.approvalField" :label="item.fieldName" :key="item.id">
|
<jnpf-input placeholder="请输入" v-model:value="item.value" v-if="item.jnpfKey == 'input'" />
|
<jnpf-textarea placeholder="请输入" v-model:value="item.value" v-if="item.jnpfKey == 'textarea'" />
|
<jnpf-input-number placeholder="请输入" v-model:value="item.value" v-if="item.jnpfKey == 'inputNumber'" />
|
</a-form-item>
|
</a-form>
|
<Tooltip title="收起" placement="bottom">
|
<i class="icon-ym icon-ym-report-icon-arrow-down arrow-icon" @click="handleShowApprovalForm"></i>
|
</Tooltip>
|
</div>
|
</Drawer>
|
</div>
|
<div class="flow-btn-container" v-if="leftBtnList.length || rightBtnList.length">
|
<template v-if="config.opType != 2">
|
<div class="flow-btn">
|
<a-button
|
:type="item.type"
|
@click="handleBtnClick(item.id)"
|
:loading="btnLoading && eventType == item.id"
|
:disabled="loading || allBtnDisabled || (btnLoading && eventType !== item.id)"
|
:key="item.id"
|
v-for="item in leftBtnList">
|
{{ item.fullName }}
|
</a-button>
|
</div>
|
<div class="flow-btn">
|
<a-button
|
:type="item.type"
|
@click="handleBtnClick(item.id)"
|
:loading="btnLoading && eventType == item.id"
|
:disabled="loading || allBtnDisabled || (btnLoading && eventType !== item.id)"
|
:key="item.id"
|
v-for="item in rightBtnList">
|
{{ item.fullName }}
|
</a-button>
|
</div>
|
</template>
|
<template v-else>
|
<div class="flow-btn"></div>
|
<div class="flow-btn">
|
<a-button
|
:type="item.type"
|
@click="handleBtnClick(item.id)"
|
:loading="btnLoading && eventType == item.id"
|
:disabled="loading || allBtnDisabled || (btnLoading && eventType !== item.id)"
|
:key="item.id"
|
v-for="item in todoBtnList">
|
{{ item.fullName }}
|
</a-button>
|
</div>
|
</template>
|
</div>
|
</div>
|
</div>
|
<ExtraPanel v-bind="getBindExtraPanelValue" @on-retry="onRetry" @view-sub-flow="viewSubFlow" v-if="getShowExtraPanel" />
|
</div>
|
<FlowFile ref="flowFileRef" @file-end="handleFileEnd" />
|
</BasicPopup>
|
<ErrorModal @register="registerError" @confirm="errorReceiver" @close="onErrorModalClose" />
|
<CandidateModal @register="registerCandidate" @confirm="candidateReceiver" />
|
<ActionModal @register="registerAction" @confirm="actionReceiver" />
|
<ApprovalModal @register="registerApproval" @confirm="approvalReceiver" />
|
<NodeFormParser @register="registerNodeFormParser" />
|
<PrintSelect @register="registerPrintSelect" @change="handleShowBrowse" />
|
<PrintBrowse @register="registerPrintBrowse" />
|
<ReduceApproverModal @register="registerReduceApprover" />
|
<SubFlowParser :default-fullscreen="isFullscreen" @register="registerSubFlowParser" />
|
<StartFormParser :default-fullscreen="isFullscreen" @register="registerStartFormParser" />
|
<FlowChart :default-fullscreen="isFullscreen" @register="registerFlowChart" />
|
<FlowParser :default-fullscreen="isFullscreen" :show-fullscreen="false" @register="registerFlowParser" v-if="showFlowParser" />
|
<UserListModal @register="registerUserList" @confirm="selectUser" />
|
<FreeModal @register="registerFree" @confirm="submitFree" />
|
<CustomForm ref="customFormRef" />
|
</template>
|
<style lang="scss">
|
@use './style/index.scss' as *;
|
</style>
|