<script lang="ts" setup>
|
import { reactive, ref, toRefs, unref } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { BasicForm, useForm } from '@jnpf/ui/form';
|
import { BasicModal, useModal, useModalInner } from '@jnpf/ui/modal';
|
import { formValidate } from '@jnpf/utils';
|
|
import { create, getInfo, update } from '#/api/onlineDev/visualDev';
|
import { getDataSourceSelector } from '#/api/systemData/dataSource';
|
import logoImg from '#/assets/images/jnpf.png';
|
import { $t } from '#/locales';
|
|
import TableModal from '../formDesign/components/TableModal.vue';
|
import BtnForm from './BtnForm.vue';
|
import FieldForm from './FieldForm.vue';
|
import ListForm from './ListForm.vue';
|
|
interface State {
|
activeStep: number;
|
loading: boolean;
|
btnLoading: boolean;
|
dataForm: Recordable;
|
formData: any[];
|
buttonData: any[];
|
appButtonData: any[];
|
columnData: any[];
|
appColumnData: any[];
|
isReload: boolean;
|
dbOptions: any[];
|
tables: any[];
|
}
|
interface ComType {
|
getData: () => any;
|
}
|
|
const emit = defineEmits(['register', 'reload']);
|
const [registerForm, { setFieldsValue, resetFields, validate, updateSchema, getFieldsValue }] = useForm({
|
labelWidth: 122,
|
schemas: [
|
{
|
field: 'fullName',
|
label: '表单名称',
|
component: 'Input',
|
componentProps: { placeholder: '请输入', maxlength: 50 },
|
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
},
|
{
|
field: 'enCode',
|
label: '表单编码',
|
component: 'Input',
|
componentProps: { placeholder: '请输入', maxlength: 50 },
|
rules: [{ validator: formValidate('enCode'), trigger: 'blur' }],
|
},
|
{
|
field: 'category',
|
label: '表单分类',
|
component: 'Select',
|
componentProps: { placeholder: '请选择', showSearch: true },
|
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
},
|
{
|
field: 'webAddress',
|
label: 'Web页面地址',
|
helpMessage: 'Web页面地址配置为前端物理地址',
|
component: 'Input',
|
componentProps: { placeholder: '请输入', addonBefore: '#/views/' },
|
},
|
{
|
field: 'appAddress',
|
label: 'App页面地址',
|
helpMessage: 'App页面地址配置为前端物理地址,需与代码同步更新',
|
component: 'Input',
|
componentProps: { placeholder: '请输入' },
|
},
|
{
|
field: 'enableFlow',
|
label: '流程表单',
|
component: 'Switch',
|
defaultValue: 0,
|
},
|
{
|
ifShow: ({ values }) => !!values.enableFlow,
|
field: 'urlAddress',
|
label: 'Web表单地址',
|
helpMessage: 'Web表单地址配置为前端物理地址',
|
component: 'Input',
|
componentProps: { placeholder: '请输入', addonBefore: '#/views/' },
|
},
|
{
|
ifShow: ({ values }) => !!values.enableFlow,
|
field: 'appUrlAddress',
|
label: 'App表单地址',
|
helpMessage: 'App表单地址配置为前端物理地址,需与代码同步更新',
|
component: 'Input',
|
componentProps: { placeholder: '请输入' },
|
},
|
{
|
ifShow: ({ values }) => !!values.enableFlow,
|
field: 'interfaceUrl',
|
label: '接口地址',
|
helpMessage: ['后端接口请求地址', ' 系统将会请求地址中的saveData(post方法), getData(get方法)', '接口例:/api/example/UserController'],
|
component: 'Input',
|
componentProps: { placeholder: '请输入' },
|
},
|
{
|
field: 'sortCode',
|
label: '表单排序',
|
defaultValue: 0,
|
component: 'InputNumber',
|
componentProps: { min: 0, max: 999999 },
|
},
|
{
|
field: 'description',
|
label: '表单说明',
|
component: 'Textarea',
|
componentProps: { placeholder: '请输入' },
|
},
|
{
|
field: 'dbLinkId',
|
label: '数据连接',
|
defaultValue: '0',
|
component: 'Select',
|
componentProps: { placeholder: '请选择', allowClear: false, showSearch: true, fieldNames: { options: 'children' }, onChange: onDbChange },
|
},
|
],
|
});
|
const [registerTableModal, { openModal: openTableModal }] = useModal();
|
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
const { createMessage, createConfirm } = useMessage();
|
const columns = [
|
{ title: '类别', dataIndex: 'typeId', key: 'typeId', width: 65 },
|
{ title: '表名', dataIndex: 'table', key: 'table' },
|
{ title: '操作', dataIndex: 'action', key: 'action', width: 50, fixed: 'right' },
|
];
|
const state = reactive<State>({
|
activeStep: 0,
|
loading: false,
|
btnLoading: false,
|
dataForm: {
|
id: '',
|
fullName: '',
|
enCode: '',
|
type: 2,
|
webType: 1,
|
dbLinkId: '0',
|
sortCode: 0,
|
state: 1,
|
category: '',
|
description: '',
|
tables: '',
|
webAddress: '',
|
appAddress: '',
|
urlAddress: '',
|
appUrlAddress: '',
|
interfaceUrl: '',
|
},
|
formData: [],
|
buttonData: [],
|
appButtonData: [],
|
columnData: [],
|
appColumnData: [],
|
dbOptions: [],
|
tables: [],
|
isReload: false,
|
});
|
const btnFormRef = ref<Nullable<ComType>>(null);
|
const fieldFormRef = ref<Nullable<ComType>>(null);
|
const listFormRef = ref<Nullable<ComType>>(null);
|
const { activeStep, loading, btnLoading, formData, buttonData, appButtonData, columnData, appColumnData, tables } = toRefs(state);
|
|
function init(data) {
|
state.isReload = false;
|
state.activeStep = 0;
|
state.loading = true;
|
state.formData = [];
|
state.buttonData = [];
|
state.appButtonData = [];
|
state.columnData = [];
|
state.appColumnData = [];
|
state.tables = [];
|
updateSchema([{ field: 'category', componentProps: { options: data.categoryList } }]);
|
getDbOptions();
|
changeLoading(true);
|
resetFields();
|
state.dataForm.id = data.id || '';
|
if (state.dataForm.id) {
|
getInfo(state.dataForm.id).then((res) => {
|
state.dataForm = res.data;
|
setFieldsValue(state.dataForm);
|
state.tables = state.dataForm.tables ? JSON.parse(state.dataForm.tables) : [];
|
state.formData = state.dataForm.formData ? JSON.parse(state.dataForm.formData) : [];
|
state.buttonData = state.dataForm.buttonData ? JSON.parse(state.dataForm.buttonData) : [];
|
state.appButtonData = state.dataForm.appButtonData ? JSON.parse(state.dataForm.appButtonData) : [];
|
state.columnData = state.dataForm.columnData ? JSON.parse(state.dataForm.columnData) : [];
|
state.appColumnData = state.dataForm.appColumnData ? JSON.parse(state.dataForm.appColumnData) : [];
|
state.loading = false;
|
changeLoading(false);
|
});
|
} else {
|
setFieldsValue({ type: 2 });
|
state.loading = false;
|
changeLoading(false);
|
}
|
}
|
async function handleSubmit() {
|
const values = await validate();
|
if (!values) return;
|
state.dataForm = { ...state.dataForm, ...values };
|
const btnRes = await (unref(btnFormRef) as ComType).getData();
|
if (!btnRes) return;
|
const fieldRes = await (unref(fieldFormRef) as ComType).getData();
|
if (!fieldRes) return;
|
const listRes = await (unref(listFormRef) as ComType).getData();
|
if (!listRes) return;
|
handleRequest();
|
}
|
function handleRequest() {
|
state.btnLoading = true;
|
const query: any = {
|
...state.dataForm,
|
tables: state.tables ? JSON.stringify(state.tables) : null,
|
formData: state.formData ? JSON.stringify(state.formData) : null,
|
buttonData: state.buttonData ? JSON.stringify(state.buttonData) : null,
|
appButtonData: state.appButtonData ? JSON.stringify(state.appButtonData) : null,
|
columnData: state.columnData ? JSON.stringify(state.columnData) : null,
|
appColumnData: state.appColumnData ? JSON.stringify(state.appColumnData) : null,
|
};
|
const formMethod = state.dataForm.id ? update : create;
|
formMethod(query)
|
.then((res) => {
|
createMessage.success(res.msg);
|
state.btnLoading = false;
|
state.isReload = true;
|
if (!state.dataForm.id) state.dataForm.id = res.data?.id;
|
})
|
.catch(() => {
|
state.btnLoading = false;
|
});
|
}
|
function handleCancel() {
|
closeModal();
|
if (state.isReload) emit('reload');
|
}
|
function onDbChange() {
|
state.tables = [];
|
}
|
function getDbOptions() {
|
getDataSourceSelector().then((res) => {
|
let list = res.data.list || [];
|
list = list.filter((o) => o.children && o.children.length);
|
if (list[0] && list[0].children && list[0].children.length) list[0] = list[0].children[0];
|
delete list[0].children;
|
state.dbOptions = list;
|
updateSchema([{ field: 'dbLinkId', componentProps: { options: state.dbOptions } }]);
|
});
|
}
|
function openTableBox() {
|
const values = getFieldsValue();
|
if (!values.dbLinkId) return createMessage.error('请先选择数据库');
|
openTableModal(true, { dbLinkId: values.dbLinkId });
|
}
|
async function onTableSelect(data) {
|
const checkList: any[] = [];
|
if (state.tables.length) {
|
for (const e of data) {
|
const boo = state.tables.some((o) => o.table == e.table);
|
if (!boo) {
|
const item = {
|
table: e.table,
|
tableName: e.tableName,
|
typeId: '0',
|
};
|
checkList.push(item);
|
}
|
}
|
state.tables = [...state.tables, ...checkList];
|
} else {
|
for (const [i, e] of data.entries()) {
|
const typeId = i == 0 ? '1' : '0';
|
const item = {
|
table: e.table,
|
tableName: e.tableName,
|
typeId,
|
};
|
checkList.push(item);
|
}
|
state.tables = checkList;
|
}
|
state.loading = false;
|
}
|
function changeTable(record) {
|
for (let i = 0; i < state.tables.length; i++) {
|
state.tables[i].typeId = state.tables[i].table === record.table ? '1' : '0';
|
}
|
}
|
function handleDelItem(record, index) {
|
createConfirm({
|
iconType: 'warning',
|
title: $t('common.tipTitle'),
|
content: '确定要删除当前行?',
|
onOk: () => {
|
state.tables.splice(index, 1);
|
if (record.typeId == '1' && state.tables.length) {
|
state.tables[0].typeId = '1';
|
}
|
},
|
});
|
}
|
</script>
|
<template>
|
<BasicModal
|
v-bind="$attrs"
|
@register="registerModal"
|
default-fullscreen
|
:footer="null"
|
:closable="false"
|
:keyboard="false"
|
class="jnpf-full-modal full-modal designer-modal"
|
destroy-on-close>
|
<template #title>
|
<div class="jnpf-full-modal-header">
|
<div class="header-title">
|
<img :src="logoImg" class="header-logo" />
|
<span class="header-dot"></span>
|
<p class="header-txt">表单回传</p>
|
</div>
|
<a-steps v-model:current="activeStep" type="navigation" size="small" class="header-steps tab-steps">
|
<a-step title="基础设置" />
|
<a-step title="按钮设置" />
|
<a-step title="表单字段" />
|
<a-step title="列表字段" />
|
</a-steps>
|
<a-space class="options" :size="10">
|
<a-button shape="round" type="primary" @click="handleSubmit()" :disabled="loading" :loading="btnLoading">{{ $t('common.saveText') }}</a-button>
|
<a-button shape="round" @click="handleCancel()">{{ $t('common.closeText') }}</a-button>
|
</a-space>
|
</div>
|
</template>
|
<a-row type="flex" justify="center" align="middle" class="basic-content">
|
<a-col :span="12" :xxl="10" class="basic-form" v-show="!activeStep">
|
<BasicForm @register="registerForm" />
|
<a-table :data-source="tables" :columns="columns" size="small" :pagination="false" :scroll="{ x: 'max-content' }">
|
<template #bodyCell="{ column, record, index }">
|
<template v-if="column.key === 'typeId'">
|
<a-tag color="processing" v-if="record.typeId == '1'">主表</a-tag>
|
<a-tag color="warning" @click="changeTable(record)" v-else style="cursor: pointer" title="点击设置成主表">从表</a-tag>
|
</template>
|
<template v-if="column.key === 'table'">
|
<span :title="record.tableName || record.table">{{ record.table }}</span>
|
</template>
|
<template v-if="column.key === 'action'">
|
<a-button class="action-btn" type="link" color="error" @click="handleDelItem(record, index)" size="small">删除</a-button>
|
</template>
|
</template>
|
<template #emptyText>
|
<p class="ant-table__empty-text">点击“新增”可选择1条(单表)或2条以上(多表)</p>
|
</template>
|
</a-table>
|
<div class="table-add-action" @click="openTableBox">
|
<a-button type="link" pre-icon="icon-ym icon-ym-btn-add">新增一行</a-button>
|
</div>
|
</a-col>
|
<a-col :span="12" :xxl="10" class="basic-form !pt-0" v-show="activeStep == 1">
|
<BtnForm ref="btnFormRef" :conf="buttonData" :app-conf="appButtonData" />
|
</a-col>
|
<a-col :span="18" :xxl="16" class="basic-form !pt-0" v-show="activeStep == 2">
|
<FieldForm ref="fieldFormRef" :conf="formData" :table-list="tables" />
|
</a-col>
|
<a-col :span="12" :xxl="10" class="basic-form !pt-0" v-show="activeStep == 3">
|
<ListForm ref="listFormRef" :conf="columnData" :app-conf="appColumnData" :table-list="tables" />
|
</a-col>
|
</a-row>
|
<TableModal @register="registerTableModal" @select="onTableSelect" />
|
</BasicModal>
|
</template>
|
<style lang="scss">
|
.sys-form-title-content {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
|
.action-bar {
|
padding: unset !important;
|
border-bottom: unset !important;
|
}
|
}
|
</style>
|