feat: 响应式布局支持字段换行
docs: 增加表单二开说明
This commit is contained in:
391
dev_tools/formprops.js
Normal file
391
dev_tools/formprops.js
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
module.exports = {
|
||||||
|
labelCol: { span: 3, offset: 0 },
|
||||||
|
labelAlign: 'right',
|
||||||
|
layout: 'horizontal',
|
||||||
|
size: 'default',
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
key: '95b6f464a9604b238ef8722db5d4878c',
|
||||||
|
field: 'ziDuan16563',
|
||||||
|
label: '字段1',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: true,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入字段1',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'd7f295f12d2a4ec7863099d875b95515',
|
||||||
|
field: 'ziDuan28273',
|
||||||
|
label: '字段2',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: true,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入字段2',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '37d86747f1e64aae874f13f29a5697f3',
|
||||||
|
field: 'ziDuan35193',
|
||||||
|
label: '字段3',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: true,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入字段3',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
respBreakLine: true,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fa04ee3d955a4659be18331be1819601',
|
||||||
|
field: 'ziDuan45516',
|
||||||
|
label: '字段4',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: true,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入字段4',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '9dcd8c7ac1e54acd8cee285274b82b0d',
|
||||||
|
field: 'ziDuan55031',
|
||||||
|
label: '字段5',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: false,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入字段5',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'a4b8975a9b2f4fda84dbc88cdd2bcfb7',
|
||||||
|
field: 'jiLianBenTi7352',
|
||||||
|
label: '级联本体',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: true,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入级联本体',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'a2e8519113f844c3a0ae489b2c8a09d0',
|
||||||
|
field: 'jiLianWenZi5194',
|
||||||
|
label: '级联文字',
|
||||||
|
type: 'input',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: false,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入级联文字',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '506f9008790c4e72843910da7c36b2d8',
|
||||||
|
field: '',
|
||||||
|
label: '标题',
|
||||||
|
type: 'title',
|
||||||
|
component: 'Title',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
defaultValue: '明细表',
|
||||||
|
componentProps: {
|
||||||
|
defaultValue: '明细表',
|
||||||
|
color: '',
|
||||||
|
align: 'left',
|
||||||
|
fontSize: 18,
|
||||||
|
isShow: true,
|
||||||
|
style: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '25a93adbfba647b2a5398221d2a35952',
|
||||||
|
label: '表格组件',
|
||||||
|
field: 'cascadeDemoChild5561List',
|
||||||
|
type: 'form',
|
||||||
|
component: 'SubForm',
|
||||||
|
required: true,
|
||||||
|
colProps: { span: 24 },
|
||||||
|
componentProps: {
|
||||||
|
mainKey: 'cascadeDemoChild5561List',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'af2057f5fa774ec29f91b2114c4af4f7',
|
||||||
|
title: '列1',
|
||||||
|
dataIndex: 'lie19747',
|
||||||
|
componentType: 'Input',
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: false,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入列1',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'd02e0e1df55249e6a42964143f2d2b48',
|
||||||
|
title: '级联本体',
|
||||||
|
dataIndex: 'jiLianBenTi9627',
|
||||||
|
componentType: 'Input',
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: false,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入级联本体',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '07ea250f2eb84680a01692b078e1d1cc',
|
||||||
|
title: '级联文字',
|
||||||
|
dataIndex: 'jiLianWenZi9294',
|
||||||
|
componentType: 'Input',
|
||||||
|
defaultValue: '',
|
||||||
|
componentProps: {
|
||||||
|
width: '100%',
|
||||||
|
span: '',
|
||||||
|
defaultValue: '',
|
||||||
|
labelWidthMode: 'fix',
|
||||||
|
labelFixWidth: 120,
|
||||||
|
responsive: false,
|
||||||
|
respNewRow: false,
|
||||||
|
placeholder: '请输入级联文字',
|
||||||
|
maxlength: null,
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
addonBefore: '',
|
||||||
|
addonAfter: '',
|
||||||
|
disabled: false,
|
||||||
|
allowClear: false,
|
||||||
|
showLabel: true,
|
||||||
|
required: false,
|
||||||
|
rules: [],
|
||||||
|
events: {},
|
||||||
|
isSave: false,
|
||||||
|
isShow: true,
|
||||||
|
scan: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '操作', key: 'action', fixed: 'right', width: '50px' },
|
||||||
|
],
|
||||||
|
span: '24',
|
||||||
|
preloadType: 'api',
|
||||||
|
apiConfig: {},
|
||||||
|
itemId: '',
|
||||||
|
dicOptions: [],
|
||||||
|
useSelectButton: false,
|
||||||
|
buttonName: '选择数据',
|
||||||
|
showLabel: false,
|
||||||
|
showComponentBorder: true,
|
||||||
|
showFormBorder: true,
|
||||||
|
showIndex: false,
|
||||||
|
isShow: true,
|
||||||
|
multipleHeads: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
buttonLocation: 'center',
|
||||||
|
actionColOptions: { span: 24 },
|
||||||
|
showResetButton: false,
|
||||||
|
showSubmitButton: false,
|
||||||
|
hiddenComponent: [],
|
||||||
|
};
|
||||||
24
dev_tools/template_extend.js
Normal file
24
dev_tools/template_extend.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const formProps = require('./formProps');
|
||||||
|
|
||||||
|
let tmpl = '';
|
||||||
|
|
||||||
|
// 用于将config的表单格式展开成字段,以便二开
|
||||||
|
formProps.schemas.forEach((prop) => {
|
||||||
|
let schema = `schemaMap['${prop.key}']`;
|
||||||
|
tmpl += `
|
||||||
|
<!-- ${prop.label || prop.field || prop.component} -->
|
||||||
|
<Col v-if="getIfShow2('${prop.key}')" v-show="getIsShow2('${prop.key}')" :span="getColWidth(${schema})">
|
||||||
|
<template v-if="showComponent(${schema})">
|
||||||
|
<SimpleFormItem v-model:value="formModel[${schema}.field]" :form-api="formApi" :isWorkFlow="isWorkFlow" :refreshFieldObj="refreshFieldObj" :schema="${schema}" />
|
||||||
|
</template>
|
||||||
|
</Col>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
//write to file
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
fs.writeFile('./template.txt', tmpl, (err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
console.log('The file has been saved!');
|
||||||
|
});
|
||||||
@ -76,83 +76,6 @@ onMounted(async () => {
|
|||||||
emits('form-mounted', formProps); // 补上这一句
|
emits('form-mounted', formProps); // 补上这一句
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
## 如何自定义表单字段
|
|
||||||
一般情况下,字段会和权限绑定,假设你已经解决了字段的权限问题(对于简单的数据结构,可以建一个纯文本字段,然后在前端换成自定义渲染的)。
|
|
||||||
|
|
||||||
首先我们定义一个组件,这里为了简单起见只用了一个文本框,一般情况下,你要实现v-model,同时接收disabled参数以处理只读模式。注意vue<3.4的版本并不支持defineModel。
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div class="custom-input">
|
|
||||||
<input style="width: 100%" v-if="!disabled" :value="props.modelValue" @change="onChange($event)" />
|
|
||||||
<span v-else style="color: #cc0000">{{ props.modelValue }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #cc0000;
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script setup>
|
|
||||||
import {defineProps} from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps(['modelValue', 'disabled'])
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
|
|
||||||
function onChange(event) {
|
|
||||||
emit('update:modelValue', event.target.value);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
然后在表单页面Form.vue中引入该字段
|
|
||||||
```vue
|
|
||||||
<SimpleForm>
|
|
||||||
<template v-slot:addr="scope">
|
|
||||||
<FormItem :label-col="{style: {width: '120px'}}" label="出差地点">
|
|
||||||
<custom-field :disabled="scope.schema.dynamicDisabled" v-model="scope.formModel.chuChaDiDian3876"></custom-field>
|
|
||||||
</FormItem>
|
|
||||||
</template>
|
|
||||||
</SimpleForm>
|
|
||||||
```
|
|
||||||
这里需要注意的是,自定义字段中需要自行实现label部分,所以外层用了一个FormItem,如果你需要在表单中间独立成行,也可以去掉FormItem这层。
|
|
||||||
|
|
||||||
最后在config.ts中换掉字段的类型:
|
|
||||||
```javascript
|
|
||||||
export const formProps: FormProps = {
|
|
||||||
labelCol: { span: 3, offset: 0 },
|
|
||||||
labelAlign: 'right',
|
|
||||||
layout: 'horizontal',
|
|
||||||
size: 'default',
|
|
||||||
schemas: [
|
|
||||||
{
|
|
||||||
key: '783e504378024a2c9db35b9e741eb5c3',
|
|
||||||
field: 'chuChaDiDian3876',
|
|
||||||
label: '出差地点',
|
|
||||||
type: 'slot', // type和slotName需要换掉
|
|
||||||
slotName: 'addr' // 和template中的插槽名字对应
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
```
|
|
||||||
## 如何自定义列表页字段
|
|
||||||
列表页的字段定义比较简单,首先在columns中补充需要的列(或者替换已有的列),然后再bodyCell插槽里替换所需的部分接即可。
|
|
||||||
```vue
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.dataIndex === 'action'">
|
|
||||||
<TableAction :actions="getActions(record)" />
|
|
||||||
</template>
|
|
||||||
<!-- 下面是要定制的列 --->
|
|
||||||
<template v-if="column.dataIndex === 'chuChaDiDian3876'">
|
|
||||||
<span style="color: #f00">{{record.chuChaDiDian3876}}</span>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
注意,根据antd-vue的文档,bodyCell里除了要修改的列,不能写其他内容,否则会将内容覆盖到所有列上。
|
|
||||||
## 如何在按钮栏中刚增加按钮
|
|
||||||
|
|
||||||
|
|
||||||
## 如何修改选项卡标题
|
## 如何修改选项卡标题
|
||||||
```javascript
|
```javascript
|
||||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||||
@ -167,3 +90,179 @@ tabStore.changeTitle(fullPath, `选项卡标题`);
|
|||||||
// 顺便tabStore也支持关闭选项卡
|
// 顺便tabStore也支持关闭选项卡
|
||||||
tabStore.closeTab(currentRoute, router);
|
tabStore.closeTab(currentRoute, router);
|
||||||
```
|
```
|
||||||
|
## 如何进行表单二开
|
||||||
|
首先,打开项目dev_tools/formprops.js,将其中的内容替换为模块config,注意nodejs不加参数必须用module.exports不能用export default写法。
|
||||||
|
|
||||||
|
然后执行template_extend.js,会在同目录生成template.txt,这个文件就是将config文件在第一层展开的模版内容。
|
||||||
|
|
||||||
|
接下来找到要二开的表单,在components目录里定义新建一个vue文件,然后用下面的内容初始化,这个文件实际上就是SimpleForm的继承,我们要用自己的模版覆盖掉原来的循环。由于不是每个字段都有dataIndex(如布局类、标题),使用数组展开在二次修改后会比较混乱,此处定义了映射表,可以通过key找到组件,同时简化了原来的两个函数。
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div ref="formWrap">
|
||||||
|
<Form ref="formRef" :label-col="getProps?.labelCol" :labelAlign="getProps?.labelAlign" :layout="getProps?.layout" :model="formModel" :wrapper-col="getProps?.wrapperCol" @keypress.enter="handleEnterPress">
|
||||||
|
<Row>
|
||||||
|
<!-- 把刚才template.txt的内容放到这里 -->
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div :style="{ textAlign: getProps.buttonLocation }">
|
||||||
|
<slot name="buttonBefore"></slot>
|
||||||
|
<a-button v-if="getProps.showSubmitButton" type="primary" @click="handleSubmit">
|
||||||
|
{{ t('提交') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button v-if="getProps.showResetButton" style="margin-left: 10px" @click="handleReset">
|
||||||
|
{{ t('重置') }}
|
||||||
|
</a-button>
|
||||||
|
<slot name="buttonAfter"></slot>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
|
||||||
|
import { Col, Form, Row } from 'ant-design-vue';
|
||||||
|
import SimpleFormItem from '/@/components/SimpleForm/src/components/SimpleFormItem.vue';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Form, Col, SimpleFormItem, Row, FormItem },
|
||||||
|
mixins: [SimpleForm],
|
||||||
|
setup(props, ctx) {
|
||||||
|
const ret = SimpleForm.setup(props, ctx);
|
||||||
|
const schemaMap = {};
|
||||||
|
ret.getSchemas._value.forEach((schema) => {
|
||||||
|
schemaMap[schema.key] = schema;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
schemaMap,
|
||||||
|
...ret
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getIfShow2: function (key) {
|
||||||
|
return this.getIfShow(this.schemaMap[key], this.formModel[this.schemaMap[key].field]);
|
||||||
|
},
|
||||||
|
|
||||||
|
getIsShow2: function (key) {
|
||||||
|
return this.getIsShow(this.schemaMap[key], this.formModel[this.schemaMap[key].field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
找到components目录下的Form.vue,用我们自己的表单替换掉原来的SimpleForm(假设我们的文件为CascadeDemoForm.vue):
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<CascadedemoForm
|
||||||
|
ref="systemFormRef"
|
||||||
|
:formProps="data.formDataProps"
|
||||||
|
:formModel="{}"
|
||||||
|
:isWorkFlow="props.fromPage!=FromPageType.MENU"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
展开后的文件和原有表单一致,每个字段都已经拆开,因此可以自由使用v-if/v-show等进行显隐控制,以及在字段之间插入内容、做自动计算等。
|
||||||
|
|
||||||
|
**注:该展开方法不能用Grid、Tab等布局嵌套,如果需要,建议自行写代码套容器,不要用编辑器自带的布局组件。**
|
||||||
|
|
||||||
|
## 如何在表单二开中自定义布局
|
||||||
|
```vue
|
||||||
|
<Col v-if="getIfShow2('d7fxx515')" v-show="getIsShow2('d7f2xx515')" :span="getColWidth(schemaMap['d7fxx515'])">
|
||||||
|
</Col>
|
||||||
|
```
|
||||||
|
这段代码上的span就是控制字段占位的,满宽度位24,1/3为8,1/4为6,以此类推,我们强烈大家使用响应式布局,以免在屏幕过窄时影响显示效果。同样,响应式布局在编辑器里支持换行、独立成行。
|
||||||
|
|
||||||
|
如果在二开里要换行,可以用div包裹,也可以加一个空div强制换行
|
||||||
|
```html
|
||||||
|
<div style="width: 100%"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 如何添加自定义校验
|
||||||
|
可以在config.ts中定义,componentProps里默认有空的rules数组,可以自己添加,antd vue所有支持的校验都可以在这里使用。
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
min: 10,
|
||||||
|
max: 30,
|
||||||
|
message: '这个长度在10-30之间'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 如何设置字段联动
|
||||||
|
编辑器中选择Api即可配置字段联动,只要在magic-api配置参数即可,参数值可以直接选择表单或者明细表字段。
|
||||||
|
|
||||||
|
选择字典没有效果。
|
||||||
|
|
||||||
|
## 如何在表单二开中定义字段
|
||||||
|
下面的代码为一个支持多层加载的级联选择器,同时值和文字描述分开存储,不会在表单打开时加载数据:
|
||||||
|
```vue
|
||||||
|
<FormItem :labelCol="{ style: { width: '120px' } }" label="级联本体" name="jiLianBenTi7352">
|
||||||
|
<a-input v-model:value="formModel.jiLianBenTi7352" style="display: none"/>
|
||||||
|
<!-- 当一个formitem里有多个表单字段时,不需要取值的用form-item-rest包起来 -->
|
||||||
|
<a-form-item-rest>
|
||||||
|
<a-cascader v-model:value="cascadeValue" :load-data="onCascadeLoad" :options="cascadeData" placeholder="请选择" @change="onCascadeChange">
|
||||||
|
<a href="#">{{ formModel.jiLianWenZi5194 || '请选择' }}</a>
|
||||||
|
</a-cascader>
|
||||||
|
</a-form-item-rest>
|
||||||
|
</FormItem>
|
||||||
|
```
|
||||||
|
这里特别注意下,因为cascade的值为数组,而数组在数据库是不能存储的,所以这里cascadeValue为数组值,a-input里存储的为逗号分开的id。
|
||||||
|
|
||||||
|
特别注意,FormItem在本框架里获取值依靠form的validate方法,因此FormItem里必须有**antd vue的表单元素**,如果为纯手动开发的组件,建议用一个a-input储存表单值。
|
||||||
|
|
||||||
|
脚本部分
|
||||||
|
```vue
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
setup(props, ctx) {
|
||||||
|
const ret = SimpleForm.setup(props, ctx);
|
||||||
|
const schemaMap = {};
|
||||||
|
const cascadeData = ref([]);
|
||||||
|
const cascadeValue = ref([]);
|
||||||
|
ret.getSchemas._value.forEach((schema) => {
|
||||||
|
schemaMap[schema.key] = schema;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
schemaMap,
|
||||||
|
cascadeData,
|
||||||
|
cascadeValue,
|
||||||
|
...ret
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getCascadeData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCascadeData: function (parent = {}) {
|
||||||
|
defHttp
|
||||||
|
.get({
|
||||||
|
url: '/system/dev-demo/cascade-data',
|
||||||
|
params: {
|
||||||
|
level: parent.level
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (!parent.level) {
|
||||||
|
this.cascadeData = data;
|
||||||
|
} else {
|
||||||
|
parent.children = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onCascadeChange: function (value, selectedOptions) {
|
||||||
|
this.formModel.jiLianWenZi5194 = selectedOptions.map((item) => item.label).join(' > ');
|
||||||
|
this.formModel.jiLianBenTi7352 = selectedOptions.map((item) => item.value).join(',');
|
||||||
|
},
|
||||||
|
|
||||||
|
onCascadeLoad: function (selectedOptions) {
|
||||||
|
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||||
|
this.getCascadeData(targetOption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|||||||
@ -59,6 +59,10 @@
|
|||||||
<a-switch v-model:checked="data.options.showPagination" />
|
<a-switch v-model:checked="data.options.showPagination" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-if="data.type === 'form' && data.options.showPagination" label="每页大小">
|
||||||
|
<a-input v-model:value="data.options.pageSize" placeholder="默认每页10条" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<template v-if="data.options.useSelectButton">
|
<template v-if="data.options.useSelectButton">
|
||||||
<a-form-item :label="t('按钮名称')">
|
<a-form-item :label="t('按钮名称')">
|
||||||
<a-input v-model:value="data.options.buttonName" :placeholder="t('请填写按钮名称')" />
|
<a-input v-model:value="data.options.buttonName" :placeholder="t('请填写按钮名称')" />
|
||||||
@ -439,6 +443,10 @@
|
|||||||
<a-switch v-model:checked="data.options.respNewRow" />
|
<a-switch v-model:checked="data.options.respNewRow" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-if="data.options.responsive" label="此组件换行">
|
||||||
|
<a-switch v-model:checked="data.options.respBreakLine" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item v-if="hasKey('labelWidthMode')" label="宽度模式">
|
<a-form-item v-if="hasKey('labelWidthMode')" label="宽度模式">
|
||||||
<a-radio-group v-model:value="data.options.labelWidthMode" button-style="solid" size="small">
|
<a-radio-group v-model:value="data.options.labelWidthMode" button-style="solid" size="small">
|
||||||
<a-radio-button value="flex">{{ t('百分比') }}</a-radio-button>
|
<a-radio-button value="flex">{{ t('百分比') }}</a-radio-button>
|
||||||
|
|||||||
@ -4,11 +4,12 @@
|
|||||||
<Row v-bind="getRow">
|
<Row v-bind="getRow">
|
||||||
<template v-for="schema in getSchemas" :key="schema.field">
|
<template v-for="schema in getSchemas" :key="schema.field">
|
||||||
<Col v-if="getIfShow(schema, formModel[schema.field])" v-show="getIsShow(schema, formModel[schema.field])" :span="getColWidth(schema)">
|
<Col v-if="getIfShow(schema, formModel[schema.field])" v-show="getIsShow(schema, formModel[schema.field])" :span="getColWidth(schema)">
|
||||||
|
<div v-if="schema?.componentProps.respBreakLine" style="width: 100%; height: 1px"></div>
|
||||||
<template v-if="showComponent(schema) && schema.type !== 'slot'">
|
<template v-if="showComponent(schema) && schema.type !== 'slot'">
|
||||||
<SimpleFormItem v-model:value="formModel[schema.field]" :form-api="formApi" :isWorkFlow="isWorkFlow" :refreshFieldObj="refreshFieldObj" :schema="schema" />
|
<SimpleFormItem v-model:value="formModel[schema.field]" :form-api="formApi" :isWorkFlow="isWorkFlow" :refreshFieldObj="refreshFieldObj" :schema="schema" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="schema.type === 'slot'">
|
<template v-if="schema.type === 'slot'">
|
||||||
<slot :name="schema.slotName" :schema="schema" :formModel="formModel"></slot>
|
<slot :formModel="formModel" :name="schema.slotName" :schema="schema"></slot>
|
||||||
</template>
|
</template>
|
||||||
</Col>
|
</Col>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user