<script lang="ts" setup>
|
import type { FormInstance } from 'ant-design-vue';
|
|
import { reactive, ref, toRefs } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { MonacoEditor } from '@jnpf/ui';
|
import { BasicModal, useModalInner } from '@jnpf/ui/modal';
|
|
import { cloneDeep } from 'lodash-es';
|
|
import { getConfigDataByMenuId } from '#/api/onlineDev/visualDev';
|
import { getMenuListFormType } from '#/api/system/menu';
|
import { getDataInterfaceInfo } from '#/api/systemData/dataInterface';
|
import { InterfaceModal } from '#/components/CommonModal';
|
import ScriptDemo from '#/components/FormGenerator/src/components/ScriptDemo.vue';
|
import { noAllowSelectList } from '#/components/FormGenerator/src/helper/config';
|
import { $t } from '#/locales';
|
import { interfaceSystemOptions, sourceTypeOptions, templateJsonColumns } from '#/utils/define';
|
|
import LaunchFlowCfg from './LaunchFlowCfg.vue';
|
|
interface State {
|
dataForm: any;
|
formRules: any;
|
item: any;
|
}
|
|
const emit = defineEmits(['register', 'confirm']);
|
const { createMessage } = useMessage();
|
const [registerModal, { closeModal }] = useModalInner(init);
|
const popupTypeOptions = [
|
{ id: 'dialog', fullName: '居中弹窗' },
|
{ id: 'drawer', fullName: '右侧弹窗' },
|
];
|
const popupWidthOptions = ['600px', '800px', '1000px', '40%', '50%', '60%', '70%', '80%'];
|
const formOptionsColumns = [
|
{ width: 50, title: '序号', align: 'center', customRender: ({ index }) => index + 1 },
|
{ title: '当前表单值', dataIndex: 'currentField', key: 'currentField', width: 250 },
|
{ title: '赋值给', dataIndex: 'type', key: 'type', align: 'center' },
|
{ title: '弹窗表单值', dataIndex: 'field', key: 'field', width: 250 },
|
{ title: '操作', dataIndex: 'action', key: 'action', width: 50 },
|
];
|
const defaultData = {
|
btnType: 1,
|
modelId: '',
|
popupTitle: '自定义操作',
|
popupType: 'dialog',
|
popupWidth: '800px',
|
formOptions: [],
|
customBtn: false,
|
func: '({ data, onlineUtils }) => {\r\n \r\n}',
|
interfaceId: '',
|
interfaceName: '',
|
templateJson: [],
|
useConfirm: false,
|
confirmTitle: '此操作将通过接口处理',
|
label: '',
|
labelI18nCode: '',
|
launchFlow: {
|
flowId: '',
|
flowName: '',
|
currentUser: 1,
|
customUser: 0,
|
initiator: [],
|
transferList: [],
|
formFieldList: [],
|
hasPermission: false, // 校验发起权限
|
},
|
};
|
const showType = ref('pc');
|
const state = reactive<State>({
|
dataForm: cloneDeep(defaultData),
|
formRules: {
|
label: [{ required: true, message: '必填', trigger: 'change' }],
|
btnType: [{ required: true, message: '必填', trigger: 'change' }],
|
modelId: [{ required: true, message: '必填', trigger: 'change' }],
|
popupTitle: [{ required: true, message: '必填', trigger: 'change' }],
|
interfaceId: [{ required: true, message: '必填', trigger: 'change' }],
|
confirmTitle: [{ required: true, message: '必填', trigger: 'change' }],
|
},
|
item: {},
|
});
|
const { dataForm, formRules } = toRefs(state);
|
const treeData = ref([]);
|
const fieldOptions = ref<any[]>([]);
|
const formFieldsOptions = ref<any[]>([]);
|
const formFieldsOptions1 = ref<any[]>([]);
|
const allFormFieldsOptions = ref<any[]>([]);
|
const formElRef = ref<FormInstance>();
|
const noAllowFormFieldsList = new Set(noAllowSelectList.filter((o) => o != 'billRule'));
|
|
function init(data) {
|
showType.value = data.showType || 'pc';
|
state.item = data.data;
|
state.dataForm = {
|
...cloneDeep(defaultData),
|
...cloneDeep(data.data.actionConfig),
|
label: data.data.label,
|
labelI18nCode: data.data.labelI18nCode,
|
};
|
getFormFieldsOptions(data.drawingList || []);
|
getAllFormFieldsOptions(data.drawingList || []);
|
getFeatureList();
|
getFieldOptions();
|
}
|
function getFormFieldsOptions(drawingList) {
|
const list: any[] = [];
|
const loop = (data, parent?) => {
|
if (!data) return;
|
if (data.__config__ && data.__config__?.jnpfKey !== 'table' && data.__config__.children && Array.isArray(data.__config__.children)) {
|
loop(data.__config__.children, data);
|
}
|
if (Array.isArray(data)) data.forEach((d) => loop(d, parent));
|
if (data.__vModel__ && !noAllowFormFieldsList.has(data.__config__.jnpfKey)) {
|
list.push({
|
id: data.__vModel__,
|
fullName: data.__config__.label,
|
});
|
}
|
};
|
loop(drawingList);
|
formFieldsOptions.value = list;
|
formFieldsOptions1.value = [{ fullName: '@表单ID', id: '@formId' }, ...formFieldsOptions.value];
|
}
|
function getAllFormFieldsOptions(drawingList) {
|
const list: any[] = [];
|
const loop = (data, parent?) => {
|
if (!data) return;
|
if (data.__config__ && data.__config__.children && Array.isArray(data.__config__.children)) {
|
loop(data.__config__.children, data);
|
}
|
if (Array.isArray(data)) data.forEach((d) => loop(d, parent));
|
if (data.__vModel__ && data.__config__.jnpfKey != 'table') {
|
const isTableChild = parent && parent.__config__ && parent.__config__.jnpfKey === 'table';
|
list.push({
|
id: isTableChild ? `${parent.__vModel__}-${data.__vModel__}` : data.__vModel__,
|
fullName: isTableChild ? `${parent.__config__.label}-${data.__config__.label}` : data.__config__.label,
|
__config__: {
|
isSubTable: data.__config__.isSubTable,
|
},
|
});
|
}
|
};
|
loop(drawingList);
|
allFormFieldsOptions.value = [{ fullName: '@表单ID', id: '@formId' }, ...list];
|
}
|
function getFeatureList() {
|
getMenuListFormType({ category: showType.value == 'pc' ? 'Web' : 'App', type: 3 }).then((res) => {
|
treeData.value = res.data.list;
|
});
|
}
|
function onBtnTypeChange() {
|
const data: any = {
|
modelId: '',
|
popupTitle: '自定义操作',
|
popupType: 'dialog',
|
popupWidth: '800px',
|
formOptions: [],
|
customBtn: false,
|
func: '({ data, onlineUtils }) => {\r\n \r\n}',
|
interfaceId: '',
|
interfaceName: '',
|
templateJson: [],
|
useConfirm: false,
|
confirmTitle: '此操作将通过接口处理',
|
isRefresh: false,
|
};
|
state.dataForm = { ...state.dataForm, ...data };
|
state.dataForm.formOptions = [];
|
state.dataForm.templateJson = [];
|
fieldOptions.value = [];
|
formElRef.value?.clearValidate();
|
}
|
function onModeIdChange(val) {
|
clearField();
|
if (!val) {
|
fieldOptions.value = [];
|
return;
|
}
|
getFieldOptions();
|
}
|
function clearField() {
|
state.dataForm.formOptions = (state.dataForm.formOptions as any[]).map((o) => ({ ...o, field: '' })) as any;
|
state.dataForm.templateJson = (state.dataForm.templateJson as any[]).map((o) => ({ ...o, relationField: o.sourceType === 1 ? '' : o.relationField })) as any;
|
}
|
function getFieldOptions() {
|
if (!state.dataForm.modelId) return;
|
getConfigDataByMenuId({ menuId: state.dataForm.modelId }).then((res) => {
|
const { formData, webType, interfaceId } = res.data;
|
state.dataForm.webType = webType;
|
if (webType == 4) {
|
getDataInterfaceInfo(interfaceId).then((res) => {
|
const data = res.data;
|
if (data.hasPage === 1) {
|
const fieldJson = data.parameterJson ? JSON.parse(data.parameterJson) : [];
|
fieldOptions.value = fieldJson.map((o) => ({ id: o.field, fullName: o.fieldName || o.field }));
|
} else {
|
const fieldJson = data.fieldJson ? JSON.parse(data.fieldJson) : [];
|
fieldOptions.value = fieldJson.map((o) => ({ id: o.defaultValue, fullName: o.field }));
|
}
|
});
|
} else {
|
let fieldList: any = [];
|
let formJson: any = {};
|
if (formData) formJson = JSON.parse(formData);
|
fieldList = formJson.fields || [];
|
fieldOptions.value = transformFieldList(fieldList);
|
}
|
});
|
}
|
function transformFieldList(formFieldList) {
|
const list: any[] = [];
|
const loop = (data, parent?) => {
|
if (!data) return;
|
if (data.__vModel__) {
|
const isTableChild = parent?.__config__?.jnpfKey === 'table';
|
if (!isTableChild && !noAllowFormFieldsList.has(data?.__config__?.jnpfKey)) {
|
const item: any = {
|
id: data.__vModel__,
|
fullName: data.__config__.label,
|
};
|
list.push(item);
|
}
|
}
|
if (Array.isArray(data)) data.forEach((d) => loop(d, parent));
|
if (data.__config__ && data.__config__.children && Array.isArray(data.__config__.children)) {
|
loop(data.__config__.children, data);
|
}
|
};
|
loop(formFieldList);
|
return list;
|
}
|
function handleAddFormOptions() {
|
(state.dataForm.formOptions as any).push({
|
currentField: '',
|
field: '',
|
});
|
}
|
function handleDelItem(index) {
|
state.dataForm.formOptions.splice(index, 1);
|
}
|
function visibleChange(val) {
|
if (!val) return;
|
if (!state.dataForm.modelId) createMessage.warning('请先选择关联功能');
|
}
|
function onInterfaceChange(id, row) {
|
if (!id) {
|
state.dataForm.interfaceId = '';
|
state.dataForm.interfaceName = '';
|
state.dataForm.templateJson = [];
|
return;
|
}
|
if (state.dataForm.interfaceId === id) return;
|
state.dataForm.interfaceId = id;
|
state.dataForm.interfaceName = row.fullName;
|
state.dataForm.templateJson = row.templateJson ? row.templateJson.map((o) => ({ ...o, relationField: '', sourceType: 1 })) : [];
|
}
|
async function handleSubmit() {
|
try {
|
const values = await formElRef.value?.validate();
|
if (!values) return;
|
if (state.dataForm.btnType == 1) {
|
if (!state.dataForm.formOptions.length) return createMessage.warning('赋值规则不能为空');
|
for (let i = 0; i < state.dataForm.formOptions.length; i++) {
|
const e: any = state.dataForm.formOptions[i];
|
if (!e.currentField) return createMessage.warning(`赋值规则第${i + 1}行当前表单值不能为空`);
|
if (!e.field) return createMessage.warning(`赋值规则第${i + 1}行弹窗表单值不能为空`);
|
}
|
}
|
if (state.dataForm.btnType == 4) {
|
const launchFlow = state.dataForm.launchFlow;
|
if (!launchFlow.transferList.length) return createMessage.warning('请至少设置一个字段');
|
for (let i = 0; i < launchFlow.transferList.length; i++) {
|
const e: any = launchFlow.transferList[i];
|
if (!e.targetField) return createMessage.warning(`第${i + 1}行目标表单值不能为空`);
|
if (!e.sourceValue) return createMessage.warning(`第${i + 1}行值不能为空`);
|
}
|
if (launchFlow.customUser && !launchFlow.initiator?.length) return createMessage.warning('请添加发起人');
|
}
|
const actionConfig = cloneDeep(state.dataForm);
|
state.item.label = actionConfig.label;
|
state.item.labelI18nCode = actionConfig.labelI18nCode;
|
delete actionConfig.label;
|
delete actionConfig.labelI18nCode;
|
state.item.actionConfig = actionConfig;
|
emit('confirm', state.item);
|
closeModal();
|
} catch {}
|
}
|
function onSourceTypeChange(record) {
|
record.relationField = record.sourceType == 4 ? interfaceSystemOptions[0]?.id : '';
|
}
|
function getSourceTypeOptions(isRequired) {
|
return isRequired ? sourceTypeOptions.filter((o) => o.id != 3) : sourceTypeOptions;
|
}
|
</script>
|
<template>
|
<BasicModal
|
v-bind="$attrs"
|
@register="registerModal"
|
title="按钮事件配置"
|
help-message="小程序不支持在线JS脚本"
|
:width="800"
|
@ok="handleSubmit"
|
destroy-on-close
|
class="btn-event-modal">
|
<a-form :colon="false" label-align="left" :label-col="{ style: { width: '90px' } }" :model="dataForm" :rules="formRules" ref="formElRef" hide-required-mark>
|
<a-form-item label="按钮名称" name="label">
|
<jnpf-i18n-input v-model:value="dataForm.label" v-model:i18n="dataForm.labelI18nCode" placeholder="请输入" :maxlength="10" />
|
</a-form-item>
|
<jnpf-group-title content="动作设置" :bordered="false" />
|
<a-form-item label="类型选择" name="btnType">
|
<a-radio-group v-model:value="dataForm.btnType" @change="onBtnTypeChange">
|
<a-radio :value="1">弹窗配置</a-radio>
|
<a-radio :value="2">JS脚本</a-radio>
|
<a-radio :value="3">数据接口</a-radio>
|
<a-radio :value="4">发起审批</a-radio>
|
</a-radio-group>
|
</a-form-item>
|
<template v-if="dataForm.btnType == 1">
|
<a-form-item label="选择表单" name="modelId">
|
<jnpf-tree-select v-model:value="dataForm.modelId" :options="treeData" placeholder="请选择" last-level allow-clear @change="onModeIdChange" />
|
</a-form-item>
|
<a-form-item label="弹窗标题" name="popupTitle">
|
<a-input v-model:value="dataForm.popupTitle" placeholder="请输入" allow-clear />
|
</a-form-item>
|
<a-form-item label="弹窗类型" name="popupType" v-if="showType === 'pc'">
|
<jnpf-select v-model:value="dataForm.popupType" placeholder="请选择" :options="popupTypeOptions" />
|
</a-form-item>
|
<a-form-item label="弹窗宽度" name="popupWidth" v-if="showType === 'pc'">
|
<a-select v-model:value="dataForm.popupWidth">
|
<a-select-option v-for="item in popupWidthOptions" :key="item" :value="item">{{ item }}</a-select-option>
|
</a-select>
|
</a-form-item>
|
<a-form-item label="赋值规则" style="margin-bottom: 0" />
|
<a-table :data-source="dataForm.formOptions" :columns="formOptionsColumns" size="small" :pagination="false">
|
<template #bodyCell="{ column, record, index }">
|
<template v-if="column.key === 'currentField'">
|
<jnpf-select
|
v-model:value="record.currentField"
|
placeholder="请选择"
|
:options="formFieldsOptions1"
|
:field-names="{ options: 'options1' }"
|
allow-clear
|
show-search />
|
</template>
|
<template v-if="column.key === 'type'">赋值给</template>
|
<template v-if="column.key === 'field'">
|
<jnpf-select v-model:value="record.field" placeholder="请选择" :options="fieldOptions" show-search @dropdown-visible-change="visibleChange" />
|
</template>
|
<template v-if="column.key === 'action'">
|
<a-button class="action-btn" type="link" color="error" @click="handleDelItem(index)" size="small">删除</a-button>
|
</template>
|
</template>
|
<template #emptyText>
|
<p class="leading-[60px]">暂无数据</p>
|
</template>
|
</a-table>
|
<div class="table-add-action mb-[20px]" @click="handleAddFormOptions()">
|
<a-button type="link" pre-icon="icon-ym icon-ym-btn-add">{{ $t('common.add2Text') }}</a-button>
|
</div>
|
<a-form-item label="自定义按钮事件" :label-col="{ style: { width: '110px' } }">
|
<a-switch v-model:checked="dataForm.customBtn" />
|
<span class="tip">(开启后,弹窗中按钮事件失效,调用接口事件。)</span>
|
</a-form-item>
|
<template v-if="dataForm.customBtn">
|
<a-form-item label="数据接口" name="interfaceId">
|
<InterfaceModal :value="dataForm.interfaceId" :title="dataForm.interfaceName" :source-type="3" @change="onInterfaceChange" />
|
</a-form-item>
|
<a-form-item label="参数设置" style="margin-bottom: 0" />
|
<a-table :data-source="dataForm.templateJson" :columns="templateJsonColumns" size="small" :pagination="false" class="mb-[20px]">
|
<template #bodyCell="{ column, record }">
|
<template v-if="column.key === 'field'">
|
<span class="required-sign">{{ record.required ? '*' : '' }}</span>
|
{{ record.field }}{{ record.fieldName ? `(${record.fieldName})` : '' }}
|
</template>
|
<template v-if="column.key === 'sourceType'">
|
<jnpf-select
|
v-model:value="record.sourceType"
|
placeholder="请选择"
|
:options="getSourceTypeOptions(record.required)"
|
class="!w-[100px]"
|
@change="onSourceTypeChange(record)" />
|
</template>
|
<template v-if="column.key === 'relationField'">
|
<jnpf-select
|
v-model:value="record.relationField"
|
placeholder="请选择"
|
:options="fieldOptions"
|
allow-clear
|
show-search
|
@dropdown-visible-change="visibleChange"
|
v-if="record.sourceType === 1" />
|
<template v-else-if="record.sourceType == 2">
|
<jnpf-input-number
|
v-if="['int', 'decimal'].includes(record.dataType)"
|
v-model:value="record.relationField"
|
placeholder="请输入"
|
allow-clear />
|
<jnpf-date-picker
|
v-else-if="record.dataType == 'datetime'"
|
class="!w-full"
|
v-model:value="record.relationField"
|
placeholder="请选择"
|
format="YYYY-MM-DD HH:mm:ss"
|
allow-clear />
|
<a-input v-else v-model:value="record.relationField" placeholder="请输入" allow-clear />
|
</template>
|
<jnpf-select
|
v-model:value="record.relationField"
|
placeholder="请选择"
|
:options="interfaceSystemOptions"
|
:field-names="{ options: 'options1' }"
|
allow-clear
|
class="!w-[204px]"
|
:disabled="record.disabled"
|
v-else-if="record.sourceType === 4" />
|
</template>
|
</template>
|
<template #emptyText>
|
<p class="leading-[60px]">暂无数据</p>
|
</template>
|
</a-table>
|
</template>
|
</template>
|
<template v-if="dataForm.btnType == 2">
|
<div class="form-script-editor">
|
<MonacoEditor v-model="dataForm.func" />
|
</div>
|
<div class="form-script-tips">
|
<p>支持JavaScript的脚本,<ScriptDemo type="childTableBtn" /></p>
|
</div>
|
</template>
|
<template v-if="dataForm.btnType == 3">
|
<a-form-item label="数据接口" name="interfaceId">
|
<InterfaceModal :value="dataForm.interfaceId" :title="dataForm.interfaceName" :source-type="3" @change="onInterfaceChange" />
|
</a-form-item>
|
<a-form-item label="参数设置" style="margin-bottom: 0" />
|
<a-table :data-source="dataForm.templateJson" :columns="templateJsonColumns" size="small" :pagination="false" class="mb-[20px]">
|
<template #bodyCell="{ column, record }">
|
<template v-if="column.key === 'field'">
|
<span class="required-sign">{{ record.required ? '*' : '' }}</span>
|
{{ record.field }}{{ record.fieldName ? `(${record.fieldName})` : '' }}
|
</template>
|
<template v-if="column.key === 'sourceType'">
|
<jnpf-select
|
v-model:value="record.sourceType"
|
placeholder="请选择"
|
:options="getSourceTypeOptions(record.required)"
|
class="!w-[100px]"
|
@change="onSourceTypeChange(record)" />
|
</template>
|
<template v-if="column.key === 'relationField'">
|
<jnpf-select
|
v-model:value="record.relationField"
|
placeholder="请选择"
|
:options="formFieldsOptions"
|
:field-names="{ options: 'options1' }"
|
allow-clear
|
show-search
|
v-if="record.sourceType === 1" />
|
<template v-else-if="record.sourceType == 2">
|
<jnpf-input-number v-if="['int', 'decimal'].includes(record.dataType)" v-model:value="record.relationField" placeholder="请输入" allow-clear />
|
<jnpf-date-picker
|
v-else-if="record.dataType == 'datetime'"
|
class="!w-full"
|
v-model:value="record.relationField"
|
placeholder="请选择"
|
format="YYYY-MM-DD HH:mm:ss"
|
allow-clear />
|
<a-input v-else v-model:value="record.relationField" placeholder="请输入" allow-clear />
|
</template>
|
<jnpf-select
|
v-model:value="record.relationField"
|
placeholder="请选择"
|
:options="interfaceSystemOptions"
|
:field-names="{ options: 'options1' }"
|
allow-clear
|
class="!w-[204px]"
|
v-else-if="record.sourceType === 4" />
|
</template>
|
</template>
|
<template #emptyText>
|
<p class="leading-[60px]">暂无数据</p>
|
</template>
|
</a-table>
|
<a-form-item label="启用确认框">
|
<a-switch v-model:checked="dataForm.useConfirm" />
|
</a-form-item>
|
<a-form-item name="confirmTitle" v-if="dataForm.useConfirm">
|
<a-input v-model:value="dataForm.confirmTitle" placeholder="请输入" allow-clear />
|
</a-form-item>
|
</template>
|
<template v-if="dataForm.btnType == 4">
|
<LaunchFlowCfg :data-form="dataForm" :all-form-fields-options="allFormFieldsOptions" />
|
</template>
|
</a-form>
|
</BasicModal>
|
</template>
|