<t-antd-form
:ref-obj.sync="formOpts.ref"
:formOpts="formOpts"
:widthSize="1"
:labelCol="{ span:2}"
:wrapperCol="{ span:22}"
@handleEvent="handleEvent"
/>
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
refObj | form 表单校验规则方法 (可以参考 antd FormModel 表单方法中的 validate) | obj | - |
className | 自定义类名 | String | - |
layout | 改变表单项 label 与输入框的布局方式(默认:horizontal) /vertical | String | ‘horizontal’ |
widthSize | 每行显示几个输入项(默认两项) 最大值 4 | Number | 2 |
isTrim | 全局是否开启清除前后空格(comp 为 a-input 且 type 不等于’password’) | Boolean | true |
formOpts | 表单配置项 | Object | {} |
—listTypeInfo | 下拉选择数据源(type:'select’有效) | Object | {} |
—fieldList | form 表单每项 list | Array | [] |
------isHideItem | 某一项不显示 | Boolean | false |
------slotName | 自定义表单某一项输入框 | slot | - |
------childSlotName | 自定义表单某一下拉选择项子组件插槽(a-select-option) | slot | - |
------comp | form 表单每一项组件是输入框还是下拉选择等(可使用第三方 UI 如 a-select/a-input 也可以使用自定义组件) | String | - |
------formItemBind | 表单每一项属性(继承FormModelItem的 Attributes) | Object | {} |
------bind | 表单每一项属性(继承第三方 UI 的 Attributes,如 a-input 中的 allowClear 清空功能)默认清空及下拉过滤 | Object | {} |
------isTrim | 是否不清除前后空格(comp 为 a-input 且 type 不等于’password’) | Boolean | false |
------type | form 表单每一项类型 | String | - |
------widthSize | form 表单某一项所占比例(如果占一整行则设置 1) | Number | 2 |
------width | form 表单某一项所占实际宽度 | String | 100% |
------arrLabel | type=select-arr 时,每个下拉显示的中文 | String | ‘label’ |
------arrKey | type=select-arr 时,每个下拉显示的中文传后台的数字 | String | ‘value’ |
------label | form 表单每一项 title | String | - |
------labelRender | 自定义某一项 title | function | - |
------value | form 表单每一项传给后台的参数 | String | - |
------rules | 每一项输入框的表单校验规则 | Object/Array | - |
------list | 下拉选择数据源(仅仅对 type:'select’有效) | String | - |
------event | 表单每一项事件标志(handleEvent 事件) | String | - |
------eventHandle | 继承 comp 组件的事件(返回两个参数,第一个自己自带,第二个 formOpts) | Object | - |
------isSelfCom | 是否使用自己封装的组件(TAntdSelect等—含有下拉框) | Boolean | false |
—formData | 表单提交数据(对应 fieldList 每一项的 value 值) | Object | - |
—labelCol | label 宽度({ span:2}) | Object | {span:2} |
—wrapperCol | 输入框 宽度 | Object | {span:22} |
—rules | 规则(可依据 AntdUI FormModel 配置————对应 formData 的值) | Object/Array | - |
—operatorList | 操作按钮 list | Array | - |
事件名 | 说明 | 返回值 |
---|---|---|
handleEvent | 单个查询条件触发事件 | fieldList 中 type/查询条件输入的值/fieldList 中 event 值 |
事件名 | 说明 | 参数 |
---|---|---|
resetFields | 重置表单 | - |
clearValidate | 清空校验 | - |
<template>
<FormModel
ref="form"
class="t_antd_form"
:class="className"
:model="formOpts.formData"
:rules="formOpts.rules"
:layout="formOpts.layout||'horizontal'"
v-bind="formAttr"
v-on="$listeners"
>
<template v-for="(item, index) in fieldList">
<FormModelItem
v-if="!item.isHideItem"
:key="index"
:prop="item.value"
:label="item.label"
:class="[item.className]"
:rules="item.rules"
:style="getChildWidth(item)"
v-bind="{...item.formItemBind}"
>
<template #label v-if="item.labelRender">
<render-comp :createElementFunc="item.labelRender" />
template>
<template v-if="item.slotName">
<slot :name="item.slotName">slot>
template>
<template v-if="item.textShow">
<span>{{item.textValue||formOpts.formData[item.value]}}span>
template>
<template v-if="item.isSelfCom">
<component
:is="item.comp"
v-model="formOpts.formData[item.value]"
:placeholder="item.placeholder||getPlaceholder(item)"
v-bind="{allowClear:true,showSearch:true,...item.bind}"
:style="{width: item.width||'100%'}"
v-on="cEvent(item)"
/>
template>
<component
v-if="!item.slotName&&!item.textShow&&!item.isSelfCom"
:is="item.comp"
v-model="formOpts.formData[item.value]"
:type="item.type||item.bind.type"
:mode="item.comp.includes('picker')?(item.type||item.bind.type):''"
:placeholder="item.placeholder||getPlaceholder(item)"
@change="handleEvent(item.event, formOpts.formData[item.value],item)"
v-bind="{allowClear:true,showSearch:true,...item.bind}"
:style="{width: item.width||'100%'}"
v-on="cEvent(item)"
>
<template #addonBefore v-if="item.addonBefore">{{ item.addonBefore }}template>
<template #addonAfter v-if="item.addonAfter">{{ item.addonAfter }}template>
<template v-if="item.childSlotName">
<slot :name="item.childSlotName">slot>
template>
<component
v-else
:is="compChildName(item)"
v-for="(value, key, index) in selectListType(item)"
:key="index"
:disabled="value.disabled"
:label="compChildLabel(item,value)"
:value="compChildValue(item,value,key)"
>{{compChildShowLabel(item,value)}}component>
component>
FormModelItem>
template>
<div class="footer_btn">
<template v-if="formOpts.btnSlotName">
<slot :name="formOpts.btnSlotName">slot>
template>
<template v-if="!formOpts.btnSlotName&&formOpts.operatorList&&formOpts.operatorList.length>0">
<Button
v-for="(val,index) in formOpts.operatorList"
:key="index"
@click="val.fun(val)"
:type="val.type||'primary'"
:icon="val.icon"
:size="val.size || 'default'"
:disabled="val.disabled"
>{{ val.label }}Button>
template>
div>
FormModel>
template>
<script>
import RenderComp from './render-comp.vue'
import { FormModel, Button } from 'ant-design-vue'
export default {
name: 'TAntdForm',
components: {
RenderComp,
FormModel,
FormModelItem: FormModel.Item,
Button
},
props: {
/** 表单配置项说明
* formData object 表单提交数据
* rules object 验证规则
* fieldList Array 表单渲染数据
* operatorList Array 操作按钮list
* listTypeInfo object 下拉选项数据
*/
formOpts: {
type: Object,
default: () => ({})
},
// 自定义类名
className: {
type: String
},
// 一行显示几个输入项;最大值4
widthSize: {
type: Number,
default: 2,
validator: (value) => {
return value <= 4
}
},
// 全局是否开启清除前后空格
isTrim: {
type: Boolean,
default: true
},
// ref
refObj: {
type: Object
}
},
data() {
return {
colSize: this.widthSize,
fieldList: this.formOpts.fieldList
}
},
computed: {
formAttr() {
let attr = {}
this.formOpts.layout === 'vertical'
? attr = {
...this.$attrs
} : attr = {
labelCol: { span: 2 },
wrapperCol: { span: 22 },
...this.$attrs
}
return attr
},
cEvent() {
return ({ eventHandle }, type) => {
let event = { ...eventHandle }
let changeEvent = {}
Object.keys(event).forEach(v => {
changeEvent[v] = (e, ids) => {
if (type === 't-antd-select-table') {
event[v] && event[v](e, ids, arguments)
} else {
if ((typeof e === 'number' && e === 0) || e) {
event[v] && event[v](e, this.formOpts, arguments)
} else {
event[v] && event[v](this.formOpts, arguments)
}
}
}
})
return { ...changeEvent }
}
},
selectListType() {
return ({ list }) => {
if (this.formOpts.listTypeInfo) {
return this.formOpts.listTypeInfo[list]
} else {
return []
}
}
},
// 子组件名称
compChildName() {
return ({ type }) => {
switch (type) {
case 'checkbox':
return 'a-checkbox'
case 'radio':
return 'a-radio'
case 'select-arr':
case 'select-obj':
return 'a-select-option'
}
}
},
// 子子组件label
compChildLabel() {
return ({ type, arrLabel }, value) => {
switch (type) {
case 'radio':
case 'checkbox':
return value.value
case 'select-arr':
return value[arrLabel || 'label']
case 'select-obj':
return value
}
}
},
// 子子组件value
compChildValue() {
return ({ type, arrKey }, value, key) => {
switch (type) {
case 'radio':
case 'checkbox':
return value.value
case 'select-arr':
return value[arrKey || 'value']
case 'select-obj':
return key
}
}
},
// 子子组件文字展示
compChildShowLabel() {
return ({ type, arrLabel }, value) => {
switch (type) {
case 'radio':
case 'checkbox':
return value.label
case 'select-arr':
return value[arrLabel || 'label']
case 'select-obj':
return value
}
}
}
},
watch: {
'formOpts.formData': {
handler(val) {
// 将form实例返回到父级
this.$emit('update:refObj', this.$refs.form)
},
deep: true // 深度监听
},
widthSize(val) {
if (val > 4) {
this.$message.warning('widthSize值不能大于4!')
this.colSize = 4
} else {
this.colSize = val
}
}
},
mounted() {
// 将form实例返回到父级
this.$emit('update:refObj', this.$refs.form)
},
methods: {
// label与输入框的布局方式
getChildWidth(item) {
if (this.formOpts.layout === 'vertical') {
return `flex: 0 1 calc((${100 / (item.widthSize || this.colSize)}% - 10px));margin-right:10px;`
} else {
return `flex: 0 1 ${100 / (item.widthSize || this.colSize)}%;`
}
},
// 得到placeholder的显示
getPlaceholder(row) {
let placeholder
if (typeof row.comp === 'string' && row.comp) {
if (row.comp.includes('input')) {
placeholder = row.label ? `请输入${row.label}` : `请输入`
} else if (row.comp.includes('select') || row.comp.includes('cascader')) {
placeholder = row.label ? `请选择${row.label}` : `请选择`
} else if (!row.comp.includes('t-antd-date-picker')) {
placeholder = row.label
}
} else {
placeholder = row.label
}
return placeholder
},
// 绑定的相关事件
handleEvent(type, val, item) {
// console.log('组件', type, val, item)
// 去除前后空格
if (this.isTrim && !item.isTrim && item.comp.includes('input') && item.type !== 'password' && item.type !== 'inputNumber') {
this.formOpts.formData[item.value] = this.formOpts.formData[item.value].trim()
}
this.$emit('handleEvent', type, val)
},
validate() {
// selfValidate() {
return new Promise((resolve, reject) => {
this.$refs.form.validate(valid => {
if (valid) {
resolve({
valid,
formData: this.formOpts.formData
})
} else {
// eslint-disable-next-line prefer-promise-reject-errors
reject({
valid,
formData: null
})
}
})
})
},
// 重置表单
resetFields() {
return this.$refs.form.resetFields()
},
// 清空校验
clearValidate() {
return this.$refs.form.clearValidate()
}
}
}
script>
<style lang="scss">
.t_antd_form {
display: flex;
flex-wrap: wrap;
.ant-select,
.ant-calendar-picker {
width: 100%;
}
.footer_btn {
display: flex;
align-items: center;
justify-content: center;
margin-top: 5px;
width: 100%;
.ant-btn {
margin-left: 10px;
}
.ant-btn:first-child {
margin-left: 0;
}
}
}
style>
gitHub组件地址
gitee码云组件地址
基于ElementUi再次封装基础组件文档
基于ant-design-vue再次封装基础组件文档
vue3+ts基于Element-plus再次封装基础组件文档