一、应用场景和意义
可以通过配置字段和校验规则,快速完成页面开发、提升开发效率
二、应用前提
形成ui/业务规范,最好是应用在问卷调查之类的业务
三、动态表单的功能
字段报错、快速滚动定位报错信息、支持字段值和字段规则拆分,便于实现复杂的表单交互
四、动态表单支持的组件类型
1、input-text 文本输入框类型
<template v-if="item.type === 'input-text'">
<view class="formItemBox">
<view class="formItem">
<view class="left">
<view class="num" v-if="num">{{ index + 1 }}.</view>
<view v-if="item.redTip" class="colorRed">{{ item.redTip ? '*' : ''
}}
</view>
<view class="name">{{ item.label }}</view>
</view>
<view class="right">
<u-form-item>
<view v-if="item.readonly || disabledFields.includes(item.id)"
@click="showNoEdit" class="readonlytext">{{ form[item.id] }}
</view>
<u--input v-else style="width: 100%;" v-model="form[item.id]"
input-align="right" :placeholder="item.placeholder"
:placeholderStyle="placeholderStyle"
border="none"></u--input>
</u-form-item>
</view>
</view>
<u-form-item labelWidth="0" :prop="item.id" class="formItemNoBody">
</u-form-item>
<view class="line" v-show="formItem.list.length - 1 > index"></view>
</view>
</template>
支持正常的输入校验 和 不可编辑的样式和提示
2、single-select 单选
<template v-if="item.type === 'single-select'">
<view class="formItemBox">
<view class="formItem">
<view class="left">
<view class="num" v-if="num">{{ index + 1 }}.</view>
<view v-if="item.redTip" class="colorRed">{{ item.redTip ? '*' : ''
}}
</view>
<view class="name">{{ item.label }}</view>
</view>
<view class="right optItemBox">
<view class="optItem" @click.stop="showNoEditOrSelect(item)"></view>
<u-form-item>
<view v-if="item.readonly || disabledFields.includes(item.id)"
class="readonlytext">{{ form[item.id] }}</view>
<view v-else class="select_right">
<view v-if="!form[item.id]" class="placeholderStyle">
{{ item.placeholder }}</view>
<view v-else class="select_right_text">
{{ item.listMap[form[item.id]] &&
item.listMap[form[item.id]].name || '未匹配到' }}
</view>
</view>
<view v-show="false">
<u--input style="width: 100%;" v-model="form[item.id]"
input-align="right" :placeholder="item.placeholder"
:placeholderStyle="placeholderStyle"
border="none"></u--input>
</view>
<view class="arrow_right_icon">
<img src="./icon/arrow.png" alt="">
</view>
</u-form-item>
</view>
</view>
<u-form-item labelWidth="0" :prop="item.id" class="formItemNoBody">
</u-form-item>
<view class="line"></view>
</view>
<!-- <u-action-sheet cancelText="取消" :safeAreaInsetBottom="true" :actions="item.list"
:title="item.selectTitle" safeAreaInsetBottom :show="item.showSelect"
@select="selectConfirm($event, item)" @close="selectClose(item)"></u-action-sheet> --> <u-picker :show="item.showSelect" ref="uPicker" :defaultIndex="defaultIndex" :confirmColor="env.themeColor" :columns="[item.list]" @cancel="selectClose(item)" @close="selectClose(item)" keyName="name" @confirm="selectConfirm($event, item)"></u-picker>
</template>
支持正常的单选和不可编辑以及未匹配到的提示
3、dependent-computing 依赖别的字段计算
<template v-if="item.type === 'dependent-computing'">
<view class="formItemBox">
<view class="formItem">
<view class="left">
<view class="num" v-if="num">{{ index + 1 }}.</view>
<view v-if="item.redTip" class="colorRed">{{ item.redTip ? '*' : ''
}}
</view>
<view class="name">{{ item.label }}</view>
</view>
<view class="right optItemBox">
<view class="optItem" @click.stop="showNoEditOrSelect(item)"></view>
<u-form-item>
<view v-if="item.readonly || disabledFields.includes(item.id)"
class="readonlytext">{{ form[item.id] }}</view>
<view v-else class="select_right">
<u--input style="width: 100%;" v-model="form[item.id]"
input-align="right" :placeholder="item.placeholder"
:placeholderStyle="placeholderStyle"
border="none"></u--input>
</view>
</u-form-item>
</view>
</view>
<u-form-item labelWidth="0" :prop="item.id" class="formItemNoBody">
</u-form-item>
<view class="line"></view>
</view>
</template>
依赖计算不可编辑、做显示
4、switch 开关
<template v-if="item.type === 'switch'">
<view class="formItemBox">
<view class="formItem formSwitchItem">
<view class="left">
<view class="num" v-if="num">{{ index + 1 }}.</view>
<view v-if="item.redTip" class="colorRed">{{ item.redTip ? '*' : ''
}}
</view>
<view class="name">{{ item.label }}</view>
</view>
<view class="right optItemBox">
<view v-if="item.readonly || disabledFields.includes(item.id)"
class="optItem" @click.stop="showNoEditOrSelect(item)"></view>
<u-form-item>
<view class="switchBox">
<u-switch v-model="form[item.id]"
inactiveColor="rgba(229, 230, 235, 1)"
:activeColor="env.themeColor"
@change="changeSwitch($event, item)"></u-switch>
</view>
</u-form-item>
</view>
</view>
<u-form-item labelWidth="0" :prop="item.id" class="formItemNoBody">
</u-form-item>
<view class="line"></view>
</view>
</template>
5、单选日期 single-calendar
<template v-if="item.type === 'single-calendar'">
<view class="formItemBox">
<view class="formItem">
<view class="left">
<view class="num" v-if="num">{{ index + 1 }}.</view>
<view v-if="item.redTip" class="colorRed">{{ item.redTip ? '*' : ''
}}
</view>
<view class="name">{{ item.label }}</view>
</view>
<view class="right optItemBox">
<view class="optItem" @click.stop="showNoEditOrCalendar(item)">
</view>
<u-form-item>
<view v-if="item.readonly || disabledFields.includes(item.id)"
class="readonlytext">{{ form[item.id] }}</view>
<view v-else class="select_right">
<view v-if="!form[item.id]" class="placeholderStyle">
{{ item.placeholder }}</view>
<view v-else class="select_right_text">
{{ form[item.id] }}</view>
</view>
<view v-show="false">
<u--input style="width: 100%;" v-model="form[item.id]"
input-align="right" :placeholder="item.placeholder"
:placeholderStyle="placeholderStyle"
border="none"></u--input>
</view>
<view class="arrow_right_icon">
<img src="./icon/arrow.png" alt="">
</view>
</u-form-item>
</view>
</view>
<u-form-item labelWidth="0" :prop="item.id" class="formItemNoBody">
</u-form-item>
<view class="line"></view>
</view>
<!-- <u-calendar :show="item.showSingleCalendar" :color="env.themeColor"
:defaultDate="defaultDateSingle" mode="single"
></u-calendar> -->
<u-datetime-picker :show="item.showSingleCalendar" v-model="defaultDateSingle"
mode="date" @confirm="singleCalendarConfirm($event, item)"
@cancel="closeCalendar(item)"
@close="closeCalendar(item)"></u-datetime-picker>
</template>
支持正常的单选和不可编辑
6、upload-images 上传图片
<template v-if="item.type === 'upload-images'">
<view class="formItemBox">
<view class="formItem">
<view class="left">
<view class="num" v-if="num">{{ index + 1 }}.</view>
<view v-if="item.redTip" class="colorRed">{{ item.redTip ? '*' : ''
}}
</view>
<view class="name">{{ item.label }}</view>
</view>
</view>
<view class='img-wrap'>
<view v-show="false">
<u--input style="width: 100%;" v-model="form[${item.id}filePath
]"
input-align="right" :placeholder="item.placeholder"
:placeholderStyle="placeholderStyle" border="none"></u--input>
</view>
<Upload :filesData="form[item.id] || []"
@onFilesChange="(items) => filesDataChange(items, ${item.id}
)"
:disableEdit="item.readonly || disabledFields.includes(item.id)"
:max='item.imgLen' uploadType='photo' />
</view>
<u-form-item labelWidth="0" :prop="${item.id}filePath
"
class="formItemNoBody">
</u-form-item>
<view class="line"></view>
</view>
</template>
这个图片上传耦合了接口上传 和禁止编辑
五、入参
formData 表单校验规则
valueForm 表单字段值
六、使用示例
form: {
ownerName: '',
idNumbe: '',
phoneNumber: "",
proAddres: "",
projectDetailedAddress: "",
},
allFormData: [
{
title: '客户信息',
showTitle: true,
list: [{
id: "ownerName",
placeholder: "请输入",
label: "姓名",
type: "input-text",
redTip: false,
readonly: true,
rules: [
{
type: 'string',
required: true,
message: '请输入',
trigger: ['change', 'blur']
}
]
},
{
id: "idNumbe",
placeholder: "请输入",
label: "证件号码",
type: "input-text",
redTip: false,
readonly: true,
rules: [
{
type: 'string',
required: true,
message: '请输入',
trigger: ['change', 'blur']
}
]
},
{
id: "phoneNumber",
placeholder: "请输入",
label: "联系电话",
type: "input-text",
redTip: false,
readonly: true,
rules: [
{
type: 'string',
required: true,
message: '请输入',
trigger: ['change', 'blur']
}
]
},
]
},
{
title: '项目地址',
showTitle: true,
list: [{
id: "proAddres",
placeholder: "请输入",
label: "项目地址",
type: "input-text",
redTip: false,
readonly: true,
rules: [
{
type: 'string',
required: true,
message: '请输入',
trigger: ['change', 'blur']
}
]
}, {
id: "projectDetailedAddress",
placeholder: "请输入",
label: "详细地址",
type: "input-text",
redTip: false,
readonly: true,
rules: [
{
type: 'string',
required: true,
message: '请输入',
trigger: ['change', 'blur']
}
]
}]
},
{
title: '电站基本情况',
showTitle: true,
list: [{
id: "zuKuai",
placeholder: "请输入",
label: "板块数",
type: "input-text",
redTip: false,
readonly: false,
rules: [
{
type: 'string',
required: true,
message: '请输入',
trigger: ['change', 'blur']
}, {
validator: (rule, value, callback) => {
const pattern = /^[1-9]\d{0,3}$|^[1-9]\d{0,3}$|^\d{1,4}$/
if (!pattern.test(value)) {
callback(new Error('请输入大于0小于10000的正整数'));
} else {
callback();
}
},
trigger: ['change', 'blur'],
}
]
},
{
id: "zuPower",
placeholder: "请选择",
label: "功率",
type: "single-select",
redTip: false,
readonly: false,
showSelect: false,
selectTitle: "请选择",
list: [],
listMap: {},
rules: [
{
type: 'string',
required: true,
message: '请选择',
trigger: ['change', 'blur']
}
]
},
{
id: "totalZuPower",
placeholder: "计算带出,不得修改",
label: "系统容量",
type: "dependent-computing",
redTip: false,
readonly: false,
rules: [{
type: 'string',
required: true,
message: '请完善版块数和功率',
trigger: ['change', 'blur']
}, {
validator: (rule, value, callback) => {
if (Number(value) > 120000) {
callback(new Error('系统容量不能大于120000'));
} else {
callback();
}
},
trigger: ['change', 'blur'],
}]
},]
},
{
title: '付款信息',
showTitle: true,
list: [
{
id: "priceType",
placeholder: "请选择",
label: "价格类型",
type: "single-select",
redTip: false,
readonly: false,
showSelect: false,
selectTitle: "请选择",
list: [],
listMap: {},
rules: [
{
type: 'string',
required: true,
message: '请选择',
trigger: ['change', 'blur']
}
],
beferConfirmFun: (confirmFun) => {
uni.showModal({
title: '提示',
content: '修改价格类型后需重新填写付款信息',
success: async (res) => {
if (res.confirm) {
confirmFun()
}
},
});
}
},
{
id: "assemblyAttribute",
placeholder: "请选择",
label: "组件属性",
type: "single-select",
redTip: false,
readonly: false,
showSelect: false,
selectTitle: "请选择",
list: [],
listMap: {},
rules: [
{
type: 'string',
required: true,
message: '请选择',
trigger: ['change', 'blur']
}
]
},
{
id: "applySituation",
placeholder: "请选择",
label: "应用场景",
type: "single-select",
redTip: false,
readonly: false,
showSelect: false,
selectTitle: "请选择",
list: [],
listMap: {},
rules: [
{
type: 'string',
required: true,
message: '请选择',
trigger: ['change', 'blur']
}
]
},
{
id: "singleDoubleSide",
placeholder: "请选择",
label: "单双面",
type: "single-select",
redTip: false,
readonly: false,
showSelect: false,
selectTitle: "请选择",
list: [],
listMap: {},
rules: [
{
type: 'string',
required: true,
message: '请选择',
trigger: ['change', 'blur']
}
]
},
]
},
],