Merge branch 'dev' into dev-cjw

This commit is contained in:
chen_junior
2025-04-21 11:36:26 +08:00
22 changed files with 885 additions and 142 deletions

View File

@ -34,6 +34,7 @@ enum Api {
loginConfig = '/system/loginConfig',
mobileLoginCode = '/system/captcha',
mobileLoginImg = '/system/captchaImg',
checkAccountCaptcha = '/system/checkAccountCaptcha',
}
/**
@ -262,3 +263,14 @@ export function sendMobileLoginCode(params, mode: ErrorMessageMode = 'modal') {
},
);
}
export function checkAccountCaptchaApi(params, mode: ErrorMessageMode = 'modal') {
return defHttp.get(
{
url: Api.checkAccountCaptcha,
params: params,
},
{
errorMessageMode: mode,
},
);
}

View File

@ -8,6 +8,7 @@ export interface LoginParams {
password: string;
tenantCode: string;
deviceType?: number;
captchaCode: string;
}
export interface RoleInfo {

View File

@ -22,12 +22,13 @@
</template>
<script setup lang="ts">
import { ref, onMounted, computed, defineAsyncComponent, reactive } from 'vue';
import { FromPageType } from '/@/enums/workflowEnum';
import { ref, onMounted, computed, defineAsyncComponent, reactive,inject } from 'vue';
import {ApproveCode, FromPageType} from '/@/enums/workflowEnum';
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
import { createFormEvent, loadFormEvent } from '/@/hooks/web/useFormEvent';
import { changeFormJson } from '/@/hooks/web/useWorkFlowForm';
import {message} from "ant-design-vue";
const props = defineProps({
systemComponent: {
@ -63,6 +64,7 @@
default: false,
},
});
const approvalData = ref(null);
const SystemFormRef = ref();
const visible = ref(false);
const formProps = ref({});
@ -129,6 +131,7 @@
onMounted(() => {
visible.value = true;
approvalData.value = inject("approvalData");
});
//
@ -168,11 +171,30 @@
}
async function setFieldsValue(record) {
flowConfig.draftsFormData = record;
SystemFormRef.value?.setFieldsValue(record);
}
async function getFieldsValue(){
return SystemFormRef.value.getFieldsValue();
}
async function getValue(){
let values = null;
if(approvalData.value?.approvedResult === ApproveCode.FINISH){
//终止的单据不走校验逻辑直接更新
try{
//有些旧表单可能没有生成getFieldsValue
values = await SystemFormRef.value.getFieldsValue();
}catch (e){}
}
if(values === null){
values = await SystemFormRef.value.validate();
}
//添加一个flowAction标志用于合同单号回收
values.flowAction = approvalData.value?.approvedResult;
return values;
}
async function workflowSubmit(saveRowKey) {
let values = {};
try {
values = await SystemFormRef.value.validate();
let values = await getValue();
// 提交表单
if (visible.value) {
let id = await submit(saveRowKey);
@ -189,6 +211,7 @@
return values;
} catch (error) {}
}
async function submit(saveRowKey) {
let saveValId = '';
let values = await SystemFormRef.value.validate();
@ -215,23 +238,29 @@
return SystemFormRef.value.setDisabledForm(isDisabled);
}
async function handleDelete(id) {
let ret;
try {
ret = await SystemFormRef.value.handleDelete(id);
} catch (e) {
message.error('表单未配置删除');
return null;
async function resetFields(){
return SystemFormRef.value.resetFields();
}
async function handleDelete(id) {
let ret;
try {
ret = await SystemFormRef.value.handleDelete(id);
} catch (e) {
message.error('表单未配置删除');
return null;
}
return ret;
}
return ret;
}
defineExpose({
workflowSubmit,
getRowKey,
validate,
getUploadComponentIds,
resetFields,
setFieldsValue,
getFieldsValue,
getIsOldSystem,
setDisabledForm,
handleDelete

View File

@ -467,16 +467,16 @@ function getTableItemConfig(tableName: string, element) {
}
// 辅助设置表单Disabled
export function changeSchemaDisabled(schemas) {
export function changeSchemaDisabled(schemas,isDisabled=true) {
const layoutComponents = ['tab', 'grid', 'card'];
schemas?.map((info) => {
if (layoutComponents.includes(info.type!)) {
info.children?.map((childInfo) => {
childInfo.list.map((com) => {
if (layoutComponents.includes(com.type)) {
changeSchemaDisabled(childInfo.list);
changeSchemaDisabled(childInfo.list,isDisabled);
} else {
com.dynamicDisabled = true;
com.dynamicDisabled = isDisabled;
}
});
});
@ -485,17 +485,24 @@ export function changeSchemaDisabled(schemas) {
childInfo.list.map((com) => {
com.children.map((el) => {
if (layoutComponents.includes(el.type) || el.type == 'table-layout') {
changeSchemaDisabled(com.children);
changeSchemaDisabled(com.children,isDisabled);
} else {
el.dynamicDisabled = true;
el.dynamicDisabled = isDisabled;
}
});
});
});
} else if (info.type == 'one-for-one') {
changeSchemaDisabled(info.componentProps.childSchemas);
changeSchemaDisabled(info.componentProps.childSchemas,isDisabled);
} else if (info.type == 'form') {
info.dynamicDisabled = isDisabled;
info.componentProps.disabled = isDisabled;
info.componentProps.columns?.forEach((column) => {
column.dynamicDisabled = isDisabled;
if(column?.componentProps) column.componentProps.disabled = isDisabled
})
} else {
info.dynamicDisabled = true;
info.dynamicDisabled = isDisabled;
}
});
return schemas;

View File

@ -109,11 +109,12 @@ export interface ProcessConfig {
globalEndEventConfigs: NodeEventConfig[];//全局 用户节点 结束事件
globalPrequalifyBeforeEventConfigs: NodeEventConfig[];//预审前
globalPrequalifyAfterEventConfigs: NodeEventConfig[];//预审后
globalFinishEventConfigs: NodeEventConfig[];//终止事件
globalRejectEventConfigs: NodeEventConfig[];//全局退回事件
globalAgreeEventConfigs: NodeEventConfig[];//全局同意事件
globalSuspendedEventConfigs: NodeEventConfig[];//全局 挂起/暂停事件
globalRestoreEventConfigs: NodeEventConfig[];//全局 恢复事件
globalFinishBeforeEventConfigs: NodeEventConfig[];//终止事件
globalRejectAfterEventConfigs: NodeEventConfig[];//全局退回事件
globalAgreeAfterEventConfigs: NodeEventConfig[];//全局同意事件
globalSuspendedBeforeEventConfigs: NodeEventConfig[];//全局 挂起/暂停事件
globalRestoreAfterEventConfigs: NodeEventConfig[];//全局 恢复事件
globalSetSignAfterEventConfigs: NodeEventConfig[];//全局 会签事件
xmlContent: String; //xml
}
@ -287,6 +288,11 @@ export interface BasicNodeConfig {
remark: string; //节点描述
startEventConfigs: NodeEventConfig[];
endEventConfigs: NodeEventConfig[];
prequalifyBeforeEventConfigs: [],//预审前
prequalifyAfterEventConfigs: [],//预审后
rejectAfterEventConfigs: [],//退回后事件
agreeAfterEventConfigs: [],//同意后事件
setSignAfterEventConfigs: [],//会签后事件
}
export interface NodeEventConfig {

View File

@ -1745,8 +1745,8 @@ export function buildSimpleFormCode(model: GeneratorConfig, _tableInfo: TableInf
await systemFormRef.value.resetFields();
}
// 设置表单数据全部为Disabled 【查看】
async function setDisabledForm() {
data.formDataProps.schemas = changeSchemaDisabled(cloneDeep(data.formDataProps.schemas));
async function setDisabledForm(isDisabled) {
data.formDataProps.schemas = changeSchemaDisabled(cloneDeep(data.formDataProps.schemas),isDisabled);
}
// 获取行键值
function getRowKey() {

View File

@ -3,7 +3,6 @@
<BasicTable @register="registerTable" ref="tableRef" @row-dbClick="dbClickRow">
<template #toolbar>
<template v-for="button in tableButtonConfig" :key="button.code">
<a-button v-if="button.isDefault" :type="button.type" @click="buttonClick(button.code)">
@ -394,4 +393,4 @@ function getActions(record: Recordable): ActionItem[] {
.hide {
display: none !important;
}
</style>
</style>

View File

@ -279,3 +279,8 @@
}
}
</style>
<style scoped>
:deep(.center-box) {
width: 540px;
}
</style>

View File

@ -3,7 +3,7 @@
<div>
<FormItem class="enter-x" name="account" v-if="loginType =='pw'">
<label class="form-title"> {{ t('账号') }}</label>
<Input v-model:value="formData.account" :placeholder="t('账号')" class="fix-auto-fill" size="large" style="height: 58px;">
<Input v-model:value="formData.account" :placeholder="t('账号')" class="fix-auto-fill" size="large" style="height: 58px;" @blur="handleBlur">
<template #prefix>
<IconFontSymbol class="user-icon" icon="yonghu-xianxing" />
</template>
@ -17,6 +17,22 @@
</template>
</InputPassword>
</FormItem>
<FormItem class="enter-x" name="captchaCode" v-if="loginType =='pw' && loginUseType == 'captcha'">
<label class="form-title"> {{ t('图形验证码') }}</label>
<Input v-model:value="formData.captchaCode" :placeholder="t('图形验证码')" class="fix-auto-fill" size="large" style="height: 58px;" visibilityToggle>
<template #prefix>
<PictureOutlined :style="{fontSize: '25px', color: '#717c91'}"/>
</template>
<template #suffix>
<a-image
:src="captchaImage.imgBase64 || ''"
alt="验证码"
class="captcha-image"
/>
<ReloadOutlined :style="{fontSize: '25px', color: '#717c91'}" @click="refreshCaptcha"/>
</template>
</Input>
</FormItem>
<FormItem v-if="getAppEnvConfig().VITE_TENANT_ENABLED && loginType =='pw'" name="tenantCode" class="enter-x">
<label class="form-title"> {{ t('租户码') }}</label>
@ -43,8 +59,10 @@
<FormItem class="enter-x" name="code" v-if="loginType =='mobile'">
<label class="form-title"> {{ t('验证码') }}</label>
<Input v-model:value="formData.code" :placeholder="t('验证码')" class="fix-auto-fill" size="large" style="height: 58px;">
<template #prefix>
<PhoneOutlined :style="{fontSize: '25px', color: '#717c91'}"/>
</template>
<template #suffix>
<!-- <span>111</span> -->
<Button type="link" class="f-16" @click="getLoginCode" size="small" :disabled="codeButtonDisabled">
{{ getCodeButtonName }}
</Button>
@ -64,7 +82,7 @@
<FormItem>
<!-- No logic, you need to deal with it yourself -->
<Button type="link" class="f-16" @click="changeLoginType" size="small">
{{ loginType == 'mobile' ? t('账号密码登录') : t('验证码登录') }}
{{ loginType == 'mobile' ? t('账号密码登录') : t('手机登录') }}
</Button>
</FormItem>
</ACol>
@ -118,7 +136,7 @@
import { useUserStore } from '/@/store/modules/user';
import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from '/@/views/sys/login/useLogin';
import { getMobileLoginCode, getMobileLoginImg, sendMobileLoginCode } from '/@/api/system/login';
import { getMobileLoginCode, getMobileLoginImg, sendMobileLoginCode, checkAccountCaptchaApi, } from '/@/api/system/login';
import { useDesign } from '/@/hooks/web/useDesign';
import { Base64 } from 'js-base64';
@ -126,6 +144,8 @@
import { useRouter } from 'vue-router';
import { getAppEnvConfig } from '/@/utils/env';
import Icon from '/@/components/Icon/index';
import { PhoneOutlined, PictureOutlined, ReloadOutlined } from '@ant-design/icons-vue';
const ACol = Col;
const ARow = Row;
@ -138,7 +158,7 @@
const { currentRoute } = useRouter();
const { getLoginState } = useLoginState();
const { getLoginState, setLoginState } = useLoginState();
const { getFormRules } = useFormRules();
const formRef = ref();
@ -146,6 +166,7 @@
const loading = ref(false);
const rememberMe = ref(false);
const loginType = ref('mobile')
const loginUseType = ref();
const countdown = ref(60)
const visible = ref(false);
@ -153,6 +174,9 @@
const imgObj = ref({
imgBase64: ''
})
const captchaImage = ref({
imgBase64: ''
})
const imgCode = ref('')
const formData = reactive({
@ -160,7 +184,8 @@
password: '',
mobile: '',
code: '',
tenantCode: 'system'
tenantCode: 'system',
captchaCode: ''
});
const getCodeButtonName = ref('获取验证码')
const codeButtonDisabled = ref(false)
@ -228,6 +253,24 @@
}
}
/**
* 账号失焦 请求后台获取业务状态
*/
async function handleBlur() {
if (!formData.account) {
return;
}
let checkAccountCaptcha = await checkAccountCaptchaApi({username: formData.account})
if(checkAccountCaptcha == true) {
loginUseType.value = 'captcha';
// setLoginState(LoginStateEnum.LOGIN_WITH_CAPTCHA)
refreshCaptcha();
} else {
loginUseType.value = '';
// setLoginState(LoginStateEnum.LOGIN)
}
}
function refreshTodo() {
refreshLoading.value = true
onMobileLoginImg()
@ -236,7 +279,12 @@
// 图形验证
async function onMobileLoginImg() {
imgObj.value = await getMobileLoginImg({mobile: formData.mobile})
imgObj.value = await getMobileLoginImg({account: formData.mobile})
}
// 图形账号验证
async function refreshCaptcha() {
captchaImage.value = await getMobileLoginImg({account: formData.account})
}
async function handleOk() {
@ -283,11 +331,22 @@
if (!data) return;
if (loginType.value == 'pw') {
try {
// 校验有没带图形验证码提交
if (loginUseType.value == 'captcha' && !data.captchaCode) {
createErrorModal({
title: t('错误提示'),
content: '请输入图形验证码',
getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body
});
return;
}
loading.value = true;
const userInfo = await userStore.login({
password: data.password,
userName: data.account,
tenantCode: data.tenantCode,
captchaCode: data.captchaCode,
deviceType: 0, //pc-0,app-1
mode: 'none' //不要默认的错误提示
});
@ -309,6 +368,11 @@
}
}
} catch (error) {
if ((error as unknown as Error).message.includes('验证码')) {
loginUseType.value = 'captcha';
// setLoginState(LoginStateEnum.LOGIN_WITH_CAPTCHA);
refreshCaptcha();
}
createErrorModal({
title: t('错误提示'),
content: (error as unknown as Error).message || t('网络异常,请检查您的网络连接是否正常!'),
@ -316,6 +380,7 @@
});
} finally {
loading.value = false;
handleBlur();
}
} else {
try {
@ -400,4 +465,14 @@
cursor: pointer;
}
}
:deep(.captcha-image) {
cursor: pointer;
border: 1px solid #ddd;
border-radius: 4px;
height: 48px;
}
// :deep(.phone-outlined-class) {
// height: 25.99px;
// width: 25.99px;
// }
</style>

View File

@ -9,6 +9,7 @@ export enum LoginStateEnum {
RESET_PASSWORD,
MOBILE,
QR_CODE,
LOGIN_WITH_CAPTCHA,
}
const currentState = ref(LoginStateEnum.LOGIN);
@ -43,6 +44,7 @@ export function useFormRules(formData?: Recordable) {
const getAccountFormRule = computed(() => createRule(t('请输入账号')));
const getPasswordFormRule = computed(() => createRule(t('请输入密码')));
const getCaptchaCodeFormRule = computed(() => createRule(t('请输入图形验证码')));
const getSmsFormRule = computed(() => createRule(t('请输入验证码')));
const getMobileFormRule = computed(() => createRule(t('请输入手机号码')));
@ -65,6 +67,7 @@ export function useFormRules(formData?: Recordable) {
const getFormRules = computed((): { [k: string]: ValidationRule | ValidationRule[] } => {
const accountFormRule = unref(getAccountFormRule);
const passwordFormRule = unref(getPasswordFormRule);
const captchaCodeFormRule = unref(getCaptchaCodeFormRule);
const smsFormRule = unref(getSmsFormRule);
const mobileFormRule = unref(getMobileFormRule);
@ -96,6 +99,14 @@ export function useFormRules(formData?: Recordable) {
case LoginStateEnum.MOBILE:
return mobileRule;
// 枚举没实现,暂不适用
case LoginStateEnum.LOGIN_WITH_CAPTCHA:
return {
account: accountFormRule,
password: passwordFormRule,
captchaCode: captchaCodeFormRule,
};
// login form rules
default:
return {

View File

@ -110,8 +110,8 @@
await systemFormRef.value.resetFields();
}
// 设置表单数据全部为Disabled 【查看】
async function setDisabledForm() {
data.formDataProps.schemas = changeSchemaDisabled(cloneDeep(data.formDataProps.schemas));
async function setDisabledForm(isDisabled) {
data.formDataProps.schemas = changeSchemaDisabled(cloneDeep(data.formDataProps.schemas), isDisabled);
}
// 获取行键值
function getRowKey() {

View File

@ -117,6 +117,100 @@
/>
</a-form-item>
· <a-form-item name="checkErrorLoginCaptcha">
<div class="flex">
<div
class="gouBox"
:class="formState.checkErrorLoginCaptcha ? 'active' : ''"
@click="handleCaptchaClick()"
>{{ t('密码错误超过最大次数需要填写验证码') }}
<div class="triangle" v-if="formState.checkErrorLoginCaptcha"
><Icon color="#fff" icon="uil:check"
/></div>
</div>
</div>
<template #label>
<div>验证码策略</div>
<a-tooltip placement="bottomLeft" class="left-reset">
<template #title>
<div style="max-width: 336px">
<p>1.登录验证码策略是指在登录过程中出现密码错误情况时的系统处理方式</p>
<p>2.默认密码错误超过最大次需要验证码登录的策略状态为启用</p>
<p>3.如果设置验证码最大次数为0 即每次登录都需要验证码</p>
</div>
</template>
<QuestionCircleFilled
style="
font-size: 16px;
color: rgb(204 204 204);
position: absolute;
left: -90px;
top: 8px;
"
/>
</a-tooltip>
</template>
</a-form-item>
<a-form-item :label="t('最大次数')" name="checkErrorLoginCaptchaCount">
<a-input-number
v-model:value="formState.checkErrorLoginCaptchaCount"
:placeholder="t('请输入最大次数')"
style="width: 50%"
:min="0"
/>
</a-form-item>
<a-form-item :label="t('锁ip策略')" name="checkErrorLoginBlockIp">
<div class="flex">
<div
class="gouBox"
:class="formState.checkErrorLoginBlockIp ? 'active' : ''"
@click="handleBlockIpClick()"
>{{ t('密码错误超过最大次数锁定') }}
<div class="triangle" v-if="formState.checkErrorLoginBlockIp"
><Icon color="#fff" icon="uil:check"
/></div>
</div>
</div>
<a-tooltip placement="bottomLeft">
<template #title>
<div style="max-width: 336px">
<p>1.锁定ip策略是指在登录过程中出现密码错误情况时的系统处理方式</p>
<p>2.默认密码错误超过最大次数锁定ip的策略状态为启用</p>
<p
>3.当用户的ip被锁定后需要等待配置时间恢复后才能重新登录或者请联系管理员</p
>
</div>
</template>
<QuestionCircleFilled
style="
font-size: 16px;
color: rgb(204 204 204);
position: absolute;
left: -90px;
top: 8px;
"
/>
</a-tooltip>
</a-form-item>
<a-form-item :label="t('最大次数')" name="checkErrorLoginBlockIpCount">
<a-input-number
v-model:value="formState.checkErrorLoginBlockIpCount"
:placeholder="t('请输入最大次数')"
style="width: 50%"
:min="1"
/>
</a-form-item>
<a-form-item :label="t('恢复时间(小时)')" name="checkErrorLoginBlockIpRestoreTime">
<a-input-number
v-model:value="formState.checkErrorLoginBlockIpRestoreTime"
:placeholder="t('请输入恢复时间(小时)')"
style="width: 50%"
:min="1"
/>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 6, span: 24 }">
<a-button type="primary" html-type="submit" class="ml-4">{{ t('提交') }}</a-button>
</a-form-item>
@ -145,6 +239,11 @@
withoutLogin?: number;
strategyMaxNumber?: number | null;
passwordStrategy?: number;
checkErrorLoginCaptcha?: boolean;
checkErrorLoginCaptchaCount?: number | null;
checkErrorLoginBlockIp?: boolean;
checkErrorLoginBlockIpCount?: number | null;
checkErrorLoginBlockIpRestoreTime?: number | null;
}
let formState = ref<FormState>({});
@ -162,6 +261,11 @@
withoutLogin: res.withoutLogin,
strategyMaxNumber: res.strategyMaxNumber,
passwordStrategy: res.passwordStrategy,
checkErrorLoginCaptcha: res.checkErrorLoginCaptcha,
checkErrorLoginCaptchaCount: res.checkErrorLoginCaptchaCount,
checkErrorLoginBlockIp: res.checkErrorLoginBlockIp,
checkErrorLoginBlockIpCount: res.checkErrorLoginBlockIpCount,
checkErrorLoginBlockIpRestoreTime: res.checkErrorLoginBlockIpRestoreTime,
};
id.value = res.id;
});
@ -198,6 +302,22 @@
}
}
function handleCaptchaClick() {
if (formState.value.checkErrorLoginCaptcha) {
formState.value.checkErrorLoginCaptcha = false;
} else {
formState.value.checkErrorLoginCaptcha = true;
}
}
function handleBlockIpClick() {
if (formState.value.checkErrorLoginBlockIp) {
formState.value.checkErrorLoginBlockIp = false;
} else {
formState.value.checkErrorLoginBlockIp = true;
}
}
function handleClick(val) {
if (proDisabled.value) return;
if (formState.value.mulLogin?.includes(val)) {

View File

@ -186,7 +186,7 @@
});
findMenuTree(buttonSelectData.value, treeData.value, btnCheckedKey, 'button');
}
btnKeys.value = btnCheckedKey;
btnKeys.value = Array.from(new Set([...btnKeys.value, ...btnCheckedKey]));
let lostColKey = [];
colKeys.value.forEach((o) => {
@ -205,7 +205,7 @@
});
findMenuTree(columnSelectData.value, treeData.value, colCheckedKey, 'column');
}
colKeys.value = colCheckedKey;
colKeys.value = Array.from(new Set([...colKeys.value, ...colCheckedKey]));
let lostFieldKey = [];
fieldKeys.value.forEach((o) => {
@ -224,7 +224,7 @@
});
findMenuTree(fieldSelectData.value, treeData.value, fieldCheckedKey, 'field');
}
fieldKeys.value = fieldCheckedKey;
fieldKeys.value = Array.from(new Set([...fieldKeys.value, ...fieldCheckedKey]));
nextTick(() => {
getTree(unref(ButtonRef))?.setCheckedKeys(authList.buttonIds);
@ -289,6 +289,11 @@
// colKeys.value = getTree(unref(ColumnRef)).getCheckedKeys();
// fieldKeys.value = getTree(unref(FieldRef)).getCheckedKeys();
// 过滤无效的权限 ID
btnKeys.value = btnKeys.value.filter((o) => btnFilterKeys.value.includes(o));
colKeys.value = colKeys.value.filter((o) => colFilterKeys.value.includes(o));
fieldKeys.value = fieldKeys.value.filter((o) => fieldFilterKeys.value.includes(o));
// btnKeys.value = btnKeys.value.filter((o) => {
// return btnFilterKeys.value.includes(o);
// });
@ -299,6 +304,7 @@
// return fieldFilterKeys.value.includes(o);
// });
// 提交更新后的权限数据
await RoleSetAuth({
id: rowId.value,
type: typeKey.value == 4?NaN:typeKey.value,
@ -307,6 +313,7 @@
columnIds: colKeys.value,
formIds: fieldKeys.value,
});
notification.success({
message: t('提示'),
description: t('功能授权更新成功'),
@ -362,14 +369,23 @@
colFilterKeys.value = [];
fieldFilterKeys.value = [];
findMenuTree(buttonSelectData.value, treeData.value, addMenuSelect, 'button');
findMenuTree(columnSelectData.value, treeData.value, addMenuSelect, 'column');
findMenuTree(fieldSelectData.value, treeData.value, addMenuSelect, 'field');
// 使用 Set 合并已选中和新增选中的菜单,避免重复
const allMenuKeys = new Set([...menuKeys.value, ...addMenuSelect]);
let newBtnSelect = [...new Set([...btnKeys.value, ...addMenuSelect])];
getTree(unref(ButtonRef))?.setExpandedKeys(newBtnSelect);
getTree(unref(ColumnRef))?.setExpandedKeys([...new Set([...colKeys.value, ...addMenuSelect])]);
getTree(unref(FieldRef))?.setExpandedKeys([...new Set([...fieldKeys.value, ...addMenuSelect])]);
// 确保未选中菜单的子节点不会丢失
findMenuTree(buttonSelectData.value, treeData.value, Array.from(allMenuKeys), 'button');
findMenuTree(columnSelectData.value, treeData.value, Array.from(allMenuKeys), 'column');
findMenuTree(fieldSelectData.value, treeData.value, Array.from(allMenuKeys), 'field');
// 更新按钮、字段、表单的选中状态
btnKeys.value = Array.from(new Set([...btnKeys.value, ...addMenuSelect]));
colKeys.value = Array.from(new Set([...colKeys.value, ...addMenuSelect]));
fieldKeys.value = Array.from(new Set([...fieldKeys.value, ...addMenuSelect]));
// 扩展树的展开状态
getTree(unref(ButtonRef))?.setExpandedKeys(Array.from(allMenuKeys));
getTree(unref(ColumnRef))?.setExpandedKeys(Array.from(allMenuKeys));
getTree(unref(FieldRef))?.setExpandedKeys(Array.from(allMenuKeys));
}
function getParentKeys(checkedKey, arr, keys) {
for (let i = 0; i < arr.length; i++) {

View File

@ -51,6 +51,7 @@
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
@ -76,6 +77,11 @@
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteStartEvent(index)" />
@ -105,6 +111,7 @@
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
@ -130,6 +137,11 @@
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteEndEvent(index)" />
@ -137,6 +149,306 @@
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="3" :tab="t('会签后事件')" v-if="formInfo.setSignAfterEventConfigs || formInfo.type==BpmnNodeKey.USER">
<div class="process-top">
<a-button type="primary" @click="addSetSignAfterEvent"> {{ t('添加会签后事件') }} </a-button>
</div>
<a-table :columns="columns" :data-source="formInfo.setSignAfterEventConfigs" :pagination="false">
<template #headerCell="{ column }">
<template v-if="column.key === 'sort'">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fangxiang1" />
</svg>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'sort'">
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
<use xlink:href="#icon-paixu" />
</svg>
</template>
<template v-if="column.key === 'type'">
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
<ScriptApiSelect
v-if="record.type === NodeEventExType.API"
style="width: 100%"
v-model="record['apiConfig']"
:need-hide-components="true"
/>
<!-- <a-input
v-if="record.type === NodeEventExType.API"
v-model:value="record['apiConfig'].path"
@click="showConfig(NodeEventType.END, index)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input> -->
<a-select
style="width: 100%"
v-else-if="record.type === NodeEventExType.LITEFLOW"
v-model:value="record['liteflowId']"
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteSetSignAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="4" :tab="t('预审前事件')" v-if="formInfo.prequalifyBeforeEventConfigs || formInfo.type==BpmnNodeKey.USER">
<div class="process-top">
<a-button type="primary" @click="addPrequalifyBeforeEvent"> {{ t('添加预审前事件') }} </a-button>
</div>
<a-table :columns="columns" :data-source="formInfo.prequalifyBeforeEventConfigs" :pagination="false">
<template #headerCell="{ column }">
<template v-if="column.key === 'sort'">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fangxiang1" />
</svg>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'sort'">
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
<use xlink:href="#icon-paixu" />
</svg>
</template>
<template v-if="column.key === 'type'">
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
<ScriptApiSelect
v-if="record.type === NodeEventExType.API"
style="width: 100%"
v-model="record['apiConfig']"
:need-hide-components="true"
/>
<!-- <a-input
v-if="record.type === NodeEventExType.API"
v-model:value="record['apiConfig'].path"
@click="showConfig(NodeEventType.END, index)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input> -->
<a-select
style="width: 100%"
v-else-if="record.type === NodeEventExType.LITEFLOW"
v-model:value="record['liteflowId']"
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deletePrequalifyBeforeEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="5" :tab="t('预审后事件')" v-if="formInfo.prequalifyAfterEventConfigs || formInfo.type==BpmnNodeKey.USER">
<div class="process-top">
<a-button type="primary" @click="addPrequalifyAfterEvent"> {{ t('添加预审后事件') }} </a-button>
</div>
<a-table :columns="columns" :data-source="formInfo.prequalifyAfterEventConfigs" :pagination="false">
<template #headerCell="{ column }">
<template v-if="column.key === 'sort'">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fangxiang1" />
</svg>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'sort'">
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
<use xlink:href="#icon-paixu" />
</svg>
</template>
<template v-if="column.key === 'type'">
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
<ScriptApiSelect
v-if="record.type === NodeEventExType.API"
style="width: 100%"
v-model="record['apiConfig']"
:need-hide-components="true"
/>
<!-- <a-input
v-if="record.type === NodeEventExType.API"
v-model:value="record['apiConfig'].path"
@click="showConfig(NodeEventType.END, index)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input> -->
<a-select
style="width: 100%"
v-else-if="record.type === NodeEventExType.LITEFLOW"
v-model:value="record['liteflowId']"
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deletePrequalifyAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="6" :tab="t('同意后事件')" v-if="formInfo.agreeAfterEventConfigs || formInfo.type==BpmnNodeKey.USER">
<div class="process-top">
<a-button type="primary" @click="addAgreeAfterEvent"> {{ t('添加同意后事件') }} </a-button>
</div>
<a-table :columns="columns" :data-source="formInfo.agreeAfterEventConfigs" :pagination="false">
<template #headerCell="{ column }">
<template v-if="column.key === 'sort'">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fangxiang1" />
</svg>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'sort'">
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
<use xlink:href="#icon-paixu" />
</svg>
</template>
<template v-if="column.key === 'type'">
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
<ScriptApiSelect
v-if="record.type === NodeEventExType.API"
style="width: 100%"
v-model="record['apiConfig']"
:need-hide-components="true"
/>
<!-- <a-input
v-if="record.type === NodeEventExType.API"
v-model:value="record['apiConfig'].path"
@click="showConfig(NodeEventType.END, index)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input> -->
<a-select
style="width: 100%"
v-else-if="record.type === NodeEventExType.LITEFLOW"
v-model:value="record['liteflowId']"
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteAgreeAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="7" :tab="t('退回后事件')" v-if="formInfo.rejectAfterEventConfigs || formInfo.type==BpmnNodeKey.USER">
<div class="process-top">
<a-button type="primary" @click="addRejectAfterEvent"> {{ t('添加退回后事件') }} </a-button>
</div>
<a-table :columns="columns" :data-source="formInfo.rejectAfterEventConfigs" :pagination="false">
<template #headerCell="{ column }">
<template v-if="column.key === 'sort'">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fangxiang1" />
</svg>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'sort'">
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
<use xlink:href="#icon-paixu" />
</svg>
</template>
<template v-if="column.key === 'type'">
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
<ScriptApiSelect
v-if="record.type === NodeEventExType.API"
style="width: 100%"
v-model="record['apiConfig']"
:need-hide-components="true"
/>
<!-- <a-input
v-if="record.type === NodeEventExType.API"
v-model:value="record['apiConfig'].path"
@click="showConfig(NodeEventType.END, index)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input> -->
<a-select
style="width: 100%"
v-else-if="record.type === NodeEventExType.LITEFLOW"
v-model:value="record['liteflowId']"
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteRejectAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
</a-tabs>
</a-tab-pane>
</a-tabs>
@ -167,6 +479,9 @@
import Sortable from 'sortablejs';
import { NodeEventExType } from '/@/enums/workflowEnum';
import { NodeEventConfig } from '/@/model/workflow/workflowConfig';
import {
BpmnNodeKey,
} from '/@/enums/workflowEnum';
const { t } = useI18n();
const { showPanel, formInfo, nodeName } = useStateFormInfo();
const updateElementName = inject('updateElementName') as any;
@ -222,7 +537,12 @@
() => {
if (
formInfo.value.endEventConfigs?.length > 0 ||
formInfo.value.startEventConfigs?.length > 0
formInfo.value.startEventConfigs?.length > 0 ||
formInfo.value.setSignAfterEventConfigs?.length > 0 ||
formInfo.value.prequalifyBeforeEventConfigs?.length > 0 ||
formInfo.value.prequalifyAfterEventConfigs?.length > 0 ||
formInfo.value.agreeAfterEventConfigs?.length > 0 ||
formInfo.value.rejectAfterEventConfigs?.length > 0
) {
nextTick(() => {
const tbody: any = document.querySelector('.ant-table-tbody');
@ -246,6 +566,66 @@
const getList = async () => {
liteFlowOptions.value = (await getLiteflowList()) || [];
};
//新增会签 因为旧值中不包含所以html 判断node节点这里对add 可以新增空数组
const addSetSignAfterEvent = () => {
formInfo.value.setSignAfterEventConfigs = formInfo.value.setSignAfterEventConfigs==null?[]:formInfo.value.setSignAfterEventConfigs;
formInfo.value.setSignAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteSetSignAfterEvent = (index) => {
formInfo.value.setSignAfterEventConfigs.splice(index, 1);
};
// 预审前 因为旧值中不包含所以html 判断node节点这里对add 可以新增空数组
const addPrequalifyBeforeEvent = () => {
formInfo.value.prequalifyBeforeEventConfigs = formInfo.value.prequalifyBeforeEventConfigs==null?[]:formInfo.value.prequalifyBeforeEventConfigs;
formInfo.value.prequalifyBeforeEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deletePrequalifyBeforeEvent = (index) => {
formInfo.value.prequalifyBeforeEventConfigs.splice(index, 1);
};
// 预审后 因为旧值中不包含所以html 判断node节点这里对add 可以新增空数组
const addPrequalifyAfterEvent = () => {
formInfo.value.prequalifyAfterEventConfigs = formInfo.value.prequalifyAfterEventConfigs==null?[]:formInfo.value.prequalifyAfterEventConfigs;
formInfo.value.prequalifyAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deletePrequalifyAfterEvent = (index) => {
formInfo.value.prequalifyAfterEventConfigs.splice(index, 1);
};
// 同意 因为旧值中不包含所以html 判断node节点这里对add 可以新增空数组
const addAgreeAfterEvent = () => {
formInfo.value.agreeAfterEventConfigs = formInfo.value.agreeAfterEventConfigs==null?[]:formInfo.value.agreeAfterEventConfigs;
formInfo.value.agreeAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteAgreeAfterEvent = (index) => {
formInfo.value.agreeAfterEventConfigs.splice(index, 1);
};
// 退回 因为旧值中不包含所以html 判断node节点这里对add 可以新增空数组
const addRejectAfterEvent = () => {
formInfo.value.rejectAfterEventConfigs = formInfo.value.rejectAfterEventConfigs==null?[]:formInfo.value.rejectAfterEventConfigs;
formInfo.value.rejectAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteRejectAfterEvent = (index) => {
formInfo.value.rejectAfterEventConfigs.splice(index, 1);
};
</script>
<style lang="less" scoped>

View File

@ -89,11 +89,12 @@ export const processConfig: ProcessConfig = {
globalEndEventConfigs: [],//全局 用户节点 结束事件
globalPrequalifyBeforeEventConfigs: [],//预审前
globalPrequalifyAfterEventConfigs: [],//预审后
globalFinishEventConfigs: [],//终止事件
globalRejectEventConfigs: [],//全局退回事件
globalAgreeEventConfigs: [],//全局同意事件
globalSuspendedEventConfigs: [],//全局 挂起/暂停事件
globalRestoreEventConfigs: [],//全局 恢复事件
globalFinishBeforeEventConfigs: [],//终止事件
globalRejectAfterEventConfigs: [],//全局退回事件
globalAgreeAfterEventConfigs: [],//全局同意事件
globalSuspendedBeforeEventConfigs: [],//全局 挂起/暂停事件
globalRestoreAfterEventConfigs: [],//全局 恢复事件
globalSetSignAfterEventConfigs: [],//全局 会签事件
xmlContent: '',
};
// 默认属性
@ -204,6 +205,11 @@ const UserProperties: UserTaskConfig = {
}, //超时处理
startEventConfigs: [],
endEventConfigs: [],
prequalifyBeforeEventConfigs: [],//预审前
prequalifyAfterEventConfigs: [],//预审后
rejectAfterEventConfigs: [],//退回事件
agreeAfterEventConfigs: [],//同意事件
setSignAfterEventConfigs: [],//会签事件
};
// 脚本节点默认属性
const ScriptProperties: ScriptTaskConfig = {

View File

@ -132,7 +132,7 @@
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="3" :tab="t('全局预审前事件')">
<a-tab-pane key="3" :tab="t('预审前事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalPrequalifyBeforeEvent">
{{ t('添加全局预审前事件') }}
@ -198,7 +198,7 @@
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="4" :tab="t('全局预审后事件')">
<a-tab-pane key="4" :tab="t('预审后事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalPrequalifyAfterEvent">
{{ t('添加全局预审后事件') }}
@ -264,15 +264,15 @@
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="5" :tab="t('终止事件')">
<a-tab-pane key="5" :tab="t('终止事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalFinishEvent">
{{ t('添加终止事件') }}
<a-button type="primary" @click="addGlobalFinishBeforeEvent">
{{ t('添加终止事件') }}
</a-button>
</div>
<a-table
:columns="columns"
:dataSource="processInfo.globalFinishEventConfigs"
:dataSource="processInfo.globalFinishBeforeEventConfigs"
:pagination="false"
>
<template #headerCell="{ column }">
@ -325,20 +325,20 @@
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteFinishEvent(index)" />
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteFinishBeforeEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="6" :tab="t('全局退回事件')">
<a-tab-pane key="6" :tab="t('退回事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalRejectEvent">
{{ t('添加全局退回事件') }}
<a-button type="primary" @click="addGlobalRejectAfterEvent">
{{ t('添加全局退回事件') }}
</a-button>
</div>
<a-table
:columns="columns"
:dataSource="processInfo.globalRejectEventConfigs"
:dataSource="processInfo.globalRejectAfterEventConfigs"
:pagination="false"
>
<template #headerCell="{ column }">
@ -391,20 +391,20 @@
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteRejectEvent(index)" />
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteRejectAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="7" :tab="t('全局同意事件')">
<a-tab-pane key="7" :tab="t('同意事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalAgreeEvent">
{{ t('添加全局同意事件') }}
<a-button type="primary" @click="addGlobalAgreeAfterEvent">
{{ t('添加全局同意事件') }}
</a-button>
</div>
<a-table
:columns="columns"
:dataSource="processInfo.globalAgreeEventConfigs"
:dataSource="processInfo.globalAgreeAfterEventConfigs"
:pagination="false"
>
<template #headerCell="{ column }">
@ -457,20 +457,20 @@
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteAgreeEvent(index)" />
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteAgreeAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="8" :tab="t('挂起事件')">
<a-tab-pane key="8" :tab="t('挂起事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalSuspendedEvent">
{{ t('添加挂起事件') }}
<a-button type="primary" @click="addGlobalSuspendedBeforeEvent">
{{ t('添加挂起事件') }}
</a-button>
</div>
<a-table
:columns="columns"
:dataSource="processInfo.globalSuspendedEventConfigs"
:dataSource="processInfo.globalSuspendedBeforeEventConfigs"
:pagination="false"
>
<template #headerCell="{ column }">
@ -523,20 +523,20 @@
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteSuspendedEvent(index)" />
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteSuspendedBeforeEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="9" :tab="t('恢复事件')">
<a-tab-pane key="9" :tab="t('恢复事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalRestoreEvent">
{{ t('添加恢复事件') }}
<a-button type="primary" @click="addGlobalRestoreAfterEvent">
{{ t('添加恢复事件') }}
</a-button>
</div>
<a-table
:columns="columns"
:dataSource="processInfo.globalRestoreEventConfigs"
:dataSource="processInfo.globalRestoreAfterEventConfigs"
:pagination="false"
>
<template #headerCell="{ column }">
@ -589,7 +589,73 @@
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteRestoreEvent(index)" />
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteRestoreAfterEvent(index)" />
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="10" :tab="t('会签后事件')">
<div class="process-top">
<a-button type="primary" @click="addGlobalSetSignAfterEvent">
{{ t('添加会签后事件') }}
</a-button>
</div>
<a-table
:columns="columns"
:dataSource="processInfo.globalSetSignAfterEventConfigs"
:pagination="false"
>
<template #headerCell="{ column }">
<template v-if="column.key === 'sort'">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fangxiang1" />
</svg>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'sort'">
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
<use xlink:href="#icon-paixu" />
</svg>
</template>
<template v-if="column.key === 'type'">
<a-select v-model:value="record[column.dataIndex]">
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
<a-select-option :value="2">{{ t('类注入') }}</a-select-option>
</a-select>
</template>
<template v-if="column.key === 'operateConfig'">
<!-- <a-input
v-if="record.type === NodeEventExType.API"
v-model:value="record['apiConfig'].path"
@click="showConfig(NodeEventType.START, index)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input> -->
<ScriptApiSelect
v-if="record.type === NodeEventExType.API"
style="width: 100%"
v-model="record['apiConfig']"
:need-hide-components="true"
/>
<a-select
style="width: 100%"
v-else-if="record.type === NodeEventExType.LITEFLOW"
v-model:value="record['liteflowId']"
:options="liteFlowOptions"
:field-names="{ label: 'chainName', value: 'id' }"
/>
<a-input
style="width: 100%"
v-else-if="record.type === NodeEventExType.SERVICE"
v-model:value="record['serviceName']"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteSetSignAfterEvent(index)" />
</template>
</template>
</a-table>
@ -683,61 +749,71 @@ const deletePrequalifyAfterEvent = (index) => {
processInfo.value.globalPrequalifyAfterEventConfigs.splice(index, 1);
};
const addGlobalFinishEvent = () => {
processInfo.value.globalFinishEventConfigs.push({
const addGlobalFinishBeforeEvent = () => {
processInfo.value.globalFinishBeforeEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteFinishEvent = (index) => {
processInfo.value.globalFinishEventConfigs.splice(index, 1);
const deleteFinishBeforeEvent = (index) => {
processInfo.value.globalFinishBeforeEventConfigs.splice(index, 1);
};
const addGlobalRejectEvent = () => {
processInfo.value.globalRejectEventConfigs.push({
const addGlobalRejectAfterEvent = () => {
processInfo.value.globalRejectAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteRejectEvent = (index) => {
processInfo.value.globalRejectEventConfigs.splice(index, 1);
const deleteRejectAfterEvent = (index) => {
processInfo.value.globalRejectAfterEventConfigs.splice(index, 1);
};
const addGlobalAgreeEvent = () => {
processInfo.value.globalAgreeEventConfigs.push({
const addGlobalAgreeAfterEvent = () => {
processInfo.value.globalAgreeAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteAgreeEvent = (index) => {
processInfo.value.globalAgreeEventConfigs.splice(index, 1);
const deleteAgreeAfterEvent = (index) => {
processInfo.value.globalAgreeAfterEventConfigs.splice(index, 1);
};
const addGlobalSuspendedEvent = () => {
processInfo.value.globalSuspendedEventConfigs.push({
const addGlobalSuspendedBeforeEvent = () => {
processInfo.value.globalSuspendedBeforeEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteSuspendedEvent = (index) => {
processInfo.value.globalSuspendedEventConfigs.splice(index, 1);
const deleteSuspendedBeforeEvent = (index) => {
processInfo.value.globalSuspendedBeforeEventConfigs.splice(index, 1);
};
const addGlobalRestoreEvent = () => {
processInfo.value.globalRestoreEventConfigs.push({
const addGlobalRestoreAfterEvent = () => {
processInfo.value.globalRestoreAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteRestoreEvent = (index) => {
processInfo.value.globalRestoreEventConfigs.splice(index, 1);
const deleteRestoreAfterEvent = (index) => {
processInfo.value.globalRestoreAfterEventConfigs.splice(index, 1);
};
// 全局会签事件添加
const addGlobalSetSignAfterEvent = () => {
processInfo.value.globalSetSignAfterEventConfigs.push({
type: NodeEventExType.API,
apiConfig: {},
} as NodeEventConfig);
};
const deleteSetSignAfterEvent = (index) => {
processInfo.value.globalSetSignAfterEventConfigs.splice(index, 1);
};
</script>
<style lang="less" scoped>

View File

@ -57,7 +57,11 @@
</BasicTable>
<!-- 查看 -->
<LookProcess ref="lookProcess" :taskId="taskId" :processId="processId" @close="reload"/>
<LookProcess ref="lookProcess"
:taskId="taskId"
:processId="processId"
:schemaId="schemaId"
@close="reload"/>
<!-- 指派审核人 -->
<ApproveProcessMonitorUser
v-if="data.approvedUserVisible"
@ -407,6 +411,7 @@
async function dbClickRow(record) {
processId.value = record.processId;
taskId.value = record.taskId;//改版 一个流程中可能有多个taskId 已不返回
schemaId.value = record.schemaId;
await nextTick()
await lookProcess.value.look();
}

View File

@ -13,6 +13,7 @@
:taskRecords="data.taskRecords"
:predecessorTasks="selectedPredecessorTasks"
:processId="props.processId"
:schemaId="props.schemaId"
position="top"
>
<FormInformation

View File

@ -8,7 +8,7 @@
<a-button type="primary" class="clean-icon" @click.stop="close">{{ t('关闭') }}</a-button>
</template>
<template #full>
<LookTask v-if="visible" :taskId="props.taskId" :processId="props.processId" />
<LookTask v-if="visible" :taskId="props.taskId" :processId="props.processId" :schemaId="props.schemaId"/>
</template>
</ProcessLayout>
</span>
@ -22,20 +22,20 @@
import { onActivated, onMounted, ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
processId: {
type: String,
default: ''
},
taskId: {
type: String,
default: ''
},
visible: {
type: Boolean,
default: false
},
})
const props = withDefaults(
defineProps<{
processId: string;
taskId: string;
schemaId: string;
visible?: boolean;
}>(),
{
processId: '',
taskId: '',
schemaId: '',
visible: false,
},
);
let emits = defineEmits(['close']);
let visible = ref(false);
let showLoading = ref(false);

View File

@ -10,6 +10,7 @@
:taskRecords="data.taskRecords"
:predecessorTasks="selectedPredecessorTasks"
:processId="props.processId"
:schemaId="props.schemaId"
position="top"
>
<FormInformation

View File

@ -19,8 +19,9 @@
:class="activeIndex == index ? 'form-name actived' : 'form-name'"
>
<span :class="item.validate ? 'dot' : 'dot validate'"></span>
<div class="icon-box"> <IconFontSymbol icon="formItem" /> </div
><span @click="changeActiveIndex(index)" v-show="showPanel">{{ item.formName }}</span>
<div class="icon-box">
<IconFontSymbol icon="formItem" />
</div><span @click="changeActiveIndex(index)" v-show="showPanel">{{ item.formName }}</span>
</div>
</div>
</div>
@ -38,25 +39,11 @@
<div id="approveExtendButton"></div>
<div id="approveRightButton"></div>
</div>
<div class="top-toolbar">
<SystemForm
class="form-box"
v-if="item.formType == FormType.SYSTEM"
:systemComponent="item.systemComponent"
:isViewProcess="props.disabled"
:formModel="item.formModel"
:workflowConfig="item"
:ref="setItemRef"
/>
<SimpleForm
v-else-if="item.formType == FormType.CUSTOM"
class="form-box"
:ref="setItemRef"
:formProps="item.formProps"
:formModel="item.formModel"
:isWorkFlow="true"
/>
</div>
<SystemForm class="form-box" v-if="item.formType == FormType.SYSTEM"
:systemComponent="item.systemComponent" :isViewProcess="props.disabled" :formModel="item.formModel"
:workflowConfig="item" :ref="setItemRef" />
<SimpleForm v-else-if="item.formType == FormType.CUSTOM" class="form-box testClass" :ref="setItemRef"
:formProps="item.formProps" :formModel="item.formModel" :isWorkFlow="true" />
</div>
</div>
</div>
@ -383,7 +370,7 @@
};
}
function handleCancel() {
function handleCancel() {
itemRefs.value[activeIndex.value].setDisabledForm(true);
forms.modes[activeIndex.value] = 'view';
itemRefs.value[activeIndex.value].setFieldsValue(forms.formModels[activeIndex.value]);

View File

@ -1,5 +1,5 @@
<template>
<FlowPanel
<FlowPanel
v-if="visible"
:tab-position="position ? position : 'top'"
:xml="data.xml"
@ -7,7 +7,12 @@
:predecessorTasks="[]"
:processId="props.processId"
position="top"
>
:currentTaskInfo="data.currentTaskInfo"
:currentTaskAssigneeNames="data.currentTaskAssigneeNames"
:currentTaskAssignees="data.currentTaskAssignees"
:schemaId="props.schemaId"
:formDataId="data.formInfos[0].formData.id"
:formInfos="data.formInfos">
<FormInformation
:opinionsComponents="data.opinionsComponents"
:opinions="data.opinions"
@ -24,7 +29,7 @@
import { getApprovalProcess } from '/@/api/workflow/task';
import { onMounted, ref } from 'vue';
import userTaskItem from './../../hooks/userTaskItem';
let props = defineProps(['position', 'processId', 'taskId']);
let props = defineProps(['position', 'processId', 'taskId', 'schemaId']);
let visible = ref(false);
const { data, initProcessData } = userTaskItem();
onMounted(async () => {
@ -32,6 +37,7 @@
let res = await getApprovalProcess(props.taskId, props.processId);
initProcessData(res);
visible.value = true;
console.error('555555', data)
} catch (error) {}
});
</script>