JxlForm 表单
满足开发者采用配置的方式,快速搭建表单的需求。
基于 ElementUI 的 el-form、el-row 和 el-col 标签封装。
基于自封组件的 jxl-form-item 标签封装。
<template>
<div>
<el-form :id="formId" ref="form" :model="form" :rules="rules" v-bind="$attrs" v-on="$listeners" @submit.native.prevent>
<template v-if="group">
<div v-for="(groupItem, groupKey) in items" :key="groupKey" :class="['el-form-group', indent ? '' : 'no-indent']">
<template v-if="isVisible(groupItem)">
<div class="el-form-group-title">{{ groupItem.title }}</div>
<div class="el-form-group-items">
<el-row v-bind="layout.row">
<template v-for="(item, key) in groupItem.items">
<template v-if="isVisible(item)">
<el-col :key="key" v-bind="[getLayout(item), item.colAttrs]">
<jxl-form-item :item="item" :colon="colon" :is-query="isQuery" :input-space="inputSpace" :form="form" @query="queryHandle">
<template v-if="$scopedSlots[getLabelExtraSlotName(item)]" v-slot:labelExtra>
<slot :name="getLabelExtraSlotName(item)" :row="item" />
</template>
<template v-if="$scopedSlots[getTipSlotName(item)]" v-slot:tipContent>
<slot :name="getTipSlotName(item)" :row="item" />
</template>
<template v-if="$scopedSlots[getItemSlotName(item, key)]" v-slot:content>
<slot :name="getItemSlotName(item, key)" :row="item" />
</template>
</jxl-form-item>
</el-col>
</template>
</template>
</el-row>
</div>
</template>
</div>
</template>
<template v-else>
<el-row v-bind="layout.row">
<template v-for="(item, key) in items">
<template v-if="isVisible(item)">
<el-col :key="key" v-bind="[getLayout(item), item.colAttrs]">
<jxl-form-item :item="item" :colon="colon" :is-query="isQuery" :input-space="inputSpace" :form="form" @query="queryHandle">
<template v-if="$scopedSlots[getLabelExtraSlotName(item)]" v-slot:labelExtra>
<slot :name="getLabelExtraSlotName(item)" :row="item" />
</template>
<template v-if="$scopedSlots[getTipSlotName(item)]" v-slot:tipContent>
<slot :name="getTipSlotName(item)" :row="item" />
</template>
<template v-if="$scopedSlots[getItemSlotName(item, key)]" v-slot:content>
<slot :name="getItemSlotName(item, key)" :row="item" />
</template>
</jxl-form-item>
</el-col>
</template>
</template>
</el-row>
</template>
<slot />
</el-form>
<slot name="form-footer" />
</div>
</template>
<script>
import JxlFormItem from '@/components/libs/Form/JxlFormItem'
import mixin from '@/components/libs/mixin'
export default {
name: 'JxlForm',
components: { JxlFormItem },
mixins: [mixin],
props: {
/**
* 表单ID
*/
formId: {
type: String,
default: undefined
},
/**
* 表单数据
*/
form: {
type: Object,
require: true,
default: null
},
/**
* 表单是否分组
*/
group: {
type: Boolean,
default: false
},
/**
* 是否显示冒号
*/
colon: {
type: Boolean,
default: true
},
/**
* 分组模式下标题与子项缩进
*/
indent: {
type: Boolean,
default: true
},
/**
* 是否为表单查询模式
* 监听 change 事件,回调为 query 事件
*/
isQuery: {
type: Boolean,
default: false
},
/**
* 输入空格
* 可选值:none,auto, valid
* none: 不能输入空格
* auto: 自由输入空格, 默认值
* valid: 不能连续输入空格
*/
inputSpace: {
type: String,
default: 'auto'
},
/**
* 表单配置
*/
items: {
type: [Object, Array],
require: true,
default: null
},
/**
* 校验规则
*/
rules: {
type: Object,
require: true,
default: null
},
/**
* 表单布局
*/
layout: {
type: Object,
default: () => {
return {
row: {},
col: {}
}
}
},
/**
* 查询表单方法
*/
query: {
type: Function,
default: null
}
},
methods: {
/**
* 提交表单
*/
validate(callback) {
this.$refs.form.validate(callback)
},
/**
* 重置表单
*/
resetFields() {
// checkbox全选效果需要手动去除标记
this.checkAllClear() // 清除全选
// 等待去除标记后数据刷新再重置
this.$nextTick(() => {
this.$refs.form.resetFields()
})
},
/** 验证某个字段 */
validateField(filed, callback) {
this.$refs.form.validateField(filed, callback)
},
/**
* 移除表单项的校验结果。
* 传入待移除的表单项的 prop 属性或者 prop 组成的数组,
* 如不传则移除整个表单校验结果
* @param props array | string | undefined
*/
clearValidate(props) {
this.$refs.form.clearValidate(props)
},
/**
* 清除全选
*/
checkAllClear() {
Object.keys(this.items).map(key => {
const checkAll = this.items[key].checkAll
if (this._typeof(checkAll) === Object) {
checkAll.value = false
checkAll.indeterminate = false
}
})
},
/**
* 获取列布局
* @param item
* @returns {object}
*/
getLayout(item) {
if (item.layout && item.layout.col && this._typeof(item.layout.col) === Object) return item.layout.col
if (this.layout.col && this._typeof(this.layout.col) === Object) return this.layout.col
return {}
},
/**
* 获取条目插槽名称
* @param item
* @returns {*}
*/
getLabelExtraSlotName(item) {
return this._get(item, 'labelExtraSlotName')
},
/**
* 获取提示组件插槽名称
* @param item
* @returns {*}
*/
getTipSlotName(item) {
return this._get(item, 'tip.prompt.content.slotName')
},
/**
* 获取表单条目组件插槽名称
* @param item
* @param key
* @returns {*}
*/
getItemSlotName(item, key) {
if (this._typeof(item.slotName) === String && this.isValid(item.slotName)) return item.slotName
if (item.slot === true) {
if (this._typeof(item.prop) === String && this.isValid(item.prop)) return item.prop
if (this._typeof(key) === String && this.isValid(key)) return key
}
return undefined
},
/**
* 查询表单处理
*/
queryHandle() {
this.$emit('query')
}
}
}
</script>
<style lang="less">
.el-form {
position: relative;
&.el-form--label-top {
.el-form-item {
.el-form-item__label {
display: block;
position: relative;
width: 100%;
&:after {
display: none;
}
}
}
}
.el-form-item {
.el-form-item__label {
font-weight: 400;
}
.el-input__inner {
border-color: #d0d7dd;
&:focus {
border-color: #1890ff;
}
}
}
}
</style>
<style lang="less" scoped>
.el-form-group {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
&.no-indent {
.el-form-group-title {
padding-left: 0;
&:before {
display: none;
}
}
.el-form-group-items {
padding-left: 0;
}
}
.el-form-group-title {
position: relative;
white-space: nowrap;
text-overflow: ellipsis;
color: #303233;
font-weight: 600;
font-size: 16px;
line-height: 1.5;
padding-left: 12px;
margin-bottom: 20px;
&:before {
display: block;
content: '';
position: absolute;
left: 0;
top: 3px;
width: 2px;
height: 13px;
background-color: #1890ff;
}
}
.el-form-group-items {
padding-left: 12px;
&.no-indent {
padding-left: 0;
}
}
}
/deep/ .el-col:last-child {
.description-item {
margin-bottom: 0;
}
}
</style>
JxlFormItem 表单项
满足开发者采用配置的方式,快速搭建表单的需求。
基于 ElementUI 的 el-form-item 标签封装。
<template>
<el-form-item :label="item.label" :prop="item.prop" :rules="rules" :required="item.required" :class="[colon ? 'has-colon' : '']" v-bind="item.itemAttrs">
<template v-if="item.label" slot="label">
<!--条目名称-->
<span>{{ item.label }}</span>
<!--条目额外的内容-->
<slot v-if="$scopedSlots['labelExtra']" name="labelExtra" :row="item" />
<!--条目提示符-->
<jxl-tip v-if="item.tip" :options="item.tip">
<template v-if="_get(item, 'tip.prompt.content.slotName')" v-slot:content>
<slot v-if="$scopedSlots['tipContent']" name="tipContent" :row="item" />
</template>
</jxl-tip>
</template>
<!--自定义组件-->
<slot v-if="$scopedSlots['content']" name="content" :row="item" />
<!--对象组件-->
<template v-else-if="_typeof(item.component) === Object">
<component :is="item.component" v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--输入框-->
<template v-else-if="item.component === 'el-input'">
<el-input v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--计数器-->
<template v-else-if="item.component === 'el-input-number'">
<el-input-number v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--选择器-->
<template v-else-if="item.component === 'el-select'">
<el-select v-model="form[item.prop]" :name="item.prop" value="" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events">
<el-option v-if="item['optionAll']" :label="item['optionAll'].label || '全部'" :value="item['optionAll'].value || ''" />
<template v-for="(option, index) in item.options">
<el-option v-if="isVisible(option)" :key="index" :label="getOptionLabel(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs" :value="getOptionValue(option)" />
</template>
</el-select>
</template>
<!--开关-->
<template v-else-if="item.component === 'el-switch'">
<el-switch v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--多选框-->
<template v-else-if="item.component === 'el-checkbox'">
<template v-if="_typeof(item.options) === Array">
<el-checkbox v-if="_typeof(item.checkAll) === Object" v-model="item.checkAll.value" :name="item.prop + 'All'" :indeterminate="item.checkAll.indeterminate" v-bind="item.checkAll.attrs" @change="checkAllChange(item)">{{ item.checkAll.label || item.checkAll.name }}</el-checkbox>
<el-checkbox-group v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" @change="checkedChange(item)">
<template v-for="(option, index) in item.options">
<el-checkbox v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-checkbox>
</template>
</el-checkbox-group>
</template>
<template v-else>
<el-checkbox v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" v-on="item.events">{{ item.optionName }}</el-checkbox>
</template>
</template>
<!--单选框-->
<template v-else-if="item.component === 'el-radio'">
<el-radio-group v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" v-on="item.events">
<template v-if="item['optionButton']">
<template v-for="(option, index) in item.options">
<el-radio-button v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-radio-button>
</template>
</template>
<template v-else>
<template v-for="(option, index) in item.options">
<el-radio v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-radio>
</template>
</template>
</el-radio-group>
</template>
<!--滑块-->
<template v-else-if="item.component === 'el-slider'">
<el-slider v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--时间选择器-固定时间点-->
<template v-else-if="item.component === 'el-time-select'">
<el-time-select v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--时间选择器-任意时间点-->
<template v-else-if="item.component === 'el-time-picker'">
<el-time-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--日期选择器-->
<template v-else-if="item.component === 'el-date-picker'">
<el-date-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--评分-->
<template v-else-if="item.component === 'el-rate'">
<el-rate v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--颜色选择器-->
<template v-else-if="item.component === 'el-color-picker'">
<el-color-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--穿梭框-->
<template v-else-if="item.component === 'el-transfer'">
<el-transfer v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--级联选择器-->
<template v-else-if="item.component === 'el-cascader'">
<el-cascader v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" v-on="item.events" />
</template>
<!--上传-->
<template v-else-if="item.component === 'el-upload'">
<el-upload v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="Object.assign(item.attrs, item.events)" :action="item.attrs.action">
<template v-if="$scopedSlots[item.uploadSlot]">
<slot :name="item.uploadSlot" :row="item" />
</template>
<template v-else>
<img v-if="form.avatar" :src="form.avatar" class="avatar" alt="">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</template>
</el-upload>
</template>
<!--具名组件-->
<template v-else>
<component :is="item.component" v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<div v-if="item.extra" class="el-form-item-extra">{{ item.extra }}</div>
</el-form-item>
</template>
<script>
import mixin from '@/components/libs/mixin'
import JxlTip from '@/components/libs/Tip/JxlTip'
import { _typeof } from '@/utils/libs/support'
import { preventVoidInput } from '@/components/libs/util'
export default {
name: 'JxlFormItem',
components: { JxlTip },
mixins: [mixin],
props: {
/**
* 表单数据
*/
form: {
type: Object,
require: true,
default: null
},
/**
* 表单项配置
*/
item: {
type: Object,
require: true,
default: null
},
/**
* 校验规则
*/
rules: {
type: Object,
require: true,
default: null
},
/**
* 是否显示冒号
*/
colon: {
type: Boolean,
default: true
},
/**
* 是否为表单查询模式
* 监听 change 事件,回调为 query 事件
*/
isQuery: {
type: Boolean,
default: false
},
/**
* 输入空格
* 可选值:none,auto, valid
* none: 不能输入空格
* auto: 自由输入空格, 默认值
* valid: 不能连续输入空格
*/
inputSpace: {
type: String,
default: 'auto'
},
/**
* 查询表单方法
*/
query: {
type: Function,
default: null
}
},
data() {
return {
inputPlaceholder: ['el-input', 'el-input-number'], // 输入提示
selectPlaceholder: ['el-select', 'el-time-select', 'el-time-picker', 'el-date-picker', 'el-cascader'] // 选择提示
}
},
methods: {
/**
* 处理多选
* @param item 绑定的表单对象
*/
checkedChange(item) {
const checkedCount = this.form[item.prop].length
if (this._typeof(item.checkAll) === Object) {
item.checkAll.value = checkedCount === item.options.length
item.checkAll.indeterminate = checkedCount > 0 && checkedCount < item.options.length
}
},
/**
* 处理全选
* @param item 绑定的表单对象
*/
checkAllChange(item) {
this.form[item.prop] = item.checkAll.value ? this.getValues(item.options) : []
item.checkAll.indeterminate = false
},
/**
* 获取对象列表的值,返回一个值得列表
* @param array
* @returns {array}
*/
getValues(array) {
return array.map(option => {
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['value']]
if ('value' in option) return option['value']
return option['id']
})
},
/**
* 获取选项数值
* @param option
*/
getOptionValue(option) {
if (this._typeof(option) === String) return option
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['value']]
if ('value' in option) return option['value']
return option['id']
},
/**
* 获取选项标签
* @param option
* @returns {*}
*/
getOptionLabel(option) {
if (this._typeof(option) === String) return option
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['label']]
if ('label' in option) return option['label']
return option['name']
},
/**
* 插入属性
* @param item
*/
insertAttrs(item) {
const attrs = _typeof(item.attrs) === Object ? item.attrs : {}
if ('placeholder' in attrs) return attrs
if (this.inputPlaceholder.includes(item.component)) return Object.assign(attrs, { placeholder: '请输入' + item.label })
if (this.selectPlaceholder.includes(item.component)) return Object.assign(attrs, { placeholder: '请选择' + item.label })
return attrs
},
/**
* 输入处理
* @param val
*/
inputHandle(val) {
if (!['valid', 'none'].includes(this.inputSpace)) return
this.form[this.item.prop] = preventVoidInput({ text: val, trimAll: this.inputSpace === 'none' })
},
/**
* 改变处理
* @param val
*/
changeHandle(val) {
if (!this.isQuery) return
this.$emit('query')
}
}
}
</script>
<style lang="less">
.el-form-item {
&.has-colon {
.el-form-item__label:after {
content: ":";
position: relative;
left: 4px;
top: -1px;
padding-right: 4px;
font-weight: 600;
}
}
}
</style>
<style lang="less" scoped>
/deep/ .avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
/deep/ .avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
/deep/ .avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
/deep/ .avatar {
width: 178px;
height: 178px;
display: block;
}
.el-form-item-extra {
margin-top: 4px;
line-height: 1.5;
min-height: 24px;
clear: both;
color: #00000073;
font-size: 12px;
transition: color .3s cubic-bezier(.215,.61,.355,1);
}
</style>
JxlFormItem 表单项
满足开发者采用配置的方式,快速搭建表单的需求。
基于 ElementUI 的 el-form-item 标签封装。
<template>
<el-form-item :label="item.label" :prop="item.prop" :rules="rules" :required="item.required" :class="[colon ? 'has-colon' : '']" v-bind="item.itemAttrs">
<template v-if="item.label" slot="label">
<!--条目名称-->
<span>{{ item.label }}</span>
<!--条目额外的内容-->
<slot v-if="$scopedSlots['labelExtra']" name="labelExtra" :row="item" />
<!--条目提示符-->
<jxl-tip v-if="item.tip" :options="item.tip">
<template v-if="_get(item, 'tip.prompt.content.slotName')" v-slot:content>
<slot v-if="$scopedSlots['tipContent']" name="tipContent" :row="item" />
</template>
</jxl-tip>
</template>
<!--自定义组件-->
<slot v-if="$scopedSlots['content']" name="content" :row="item" />
<!--对象组件-->
<template v-else-if="_typeof(item.component) === Object">
<component :is="item.component" v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--输入框-->
<template v-else-if="item.component === 'el-input'">
<el-input v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--计数器-->
<template v-else-if="item.component === 'el-input-number'">
<el-input-number v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--选择器-->
<template v-else-if="item.component === 'el-select'">
<el-select v-model="form[item.prop]" :name="item.prop" value="" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events">
<el-option v-if="item['optionAll']" :label="item['optionAll'].label || '全部'" :value="item['optionAll'].value || ''" />
<template v-for="(option, index) in item.options">
<el-option v-if="isVisible(option)" :key="index" :label="getOptionLabel(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs" :value="getOptionValue(option)" />
</template>
</el-select>
</template>
<!--开关-->
<template v-else-if="item.component === 'el-switch'">
<el-switch v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--多选框-->
<template v-else-if="item.component === 'el-checkbox'">
<template v-if="_typeof(item.options) === Array">
<el-checkbox v-if="_typeof(item.checkAll) === Object" v-model="item.checkAll.value" :name="item.prop + 'All'" :indeterminate="item.checkAll.indeterminate" v-bind="item.checkAll.attrs" @change="checkAllChange(item)">{{ item.checkAll.label || item.checkAll.name }}</el-checkbox>
<el-checkbox-group v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" @change="checkedChange(item)">
<template v-for="(option, index) in item.options">
<el-checkbox v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-checkbox>
</template>
</el-checkbox-group>
</template>
<template v-else>
<el-checkbox v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" v-on="item.events">{{ item.optionName }}</el-checkbox>
</template>
</template>
<!--单选框-->
<template v-else-if="item.component === 'el-radio'">
<el-radio-group v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" v-on="item.events">
<template v-if="item['optionButton']">
<template v-for="(option, index) in item.options">
<el-radio-button v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-radio-button>
</template>
</template>
<template v-else>
<template v-for="(option, index) in item.options">
<el-radio v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-radio>
</template>
</template>
</el-radio-group>
</template>
<!--滑块-->
<template v-else-if="item.component === 'el-slider'">
<el-slider v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--时间选择器-固定时间点-->
<template v-else-if="item.component === 'el-time-select'">
<el-time-select v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--时间选择器-任意时间点-->
<template v-else-if="item.component === 'el-time-picker'">
<el-time-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--日期选择器-->
<template v-else-if="item.component === 'el-date-picker'">
<el-date-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--评分-->
<template v-else-if="item.component === 'el-rate'">
<el-rate v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--颜色选择器-->
<template v-else-if="item.component === 'el-color-picker'">
<el-color-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--穿梭框-->
<template v-else-if="item.component === 'el-transfer'">
<el-transfer v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--级联选择器-->
<template v-else-if="item.component === 'el-cascader'">
<el-cascader v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" v-on="item.events" />
</template>
<!--上传-->
<template v-else-if="item.component === 'el-upload'">
<el-upload v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="Object.assign(item.attrs, item.events)" :action="item.attrs.action">
<template v-if="$scopedSlots[item.uploadSlot]">
<slot :name="item.uploadSlot" :row="item" />
</template>
<template v-else>
<img v-if="form.avatar" :src="form.avatar" class="avatar" alt="">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</template>
</el-upload>
</template>
<!--具名组件-->
<template v-else>
<component :is="item.component" v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<div v-if="item.extra" class="el-form-item-extra">{{ item.extra }}</div>
</el-form-item>
</template>
<script>
import mixin from '@/components/libs/mixin'
import JxlTip from '@/components/libs/Tip/JxlTip'
import { _typeof } from '@/utils/libs/support'
import { preventVoidInput } from '@/components/libs/util'
export default {
name: 'JxlFormItem',
components: { JxlTip },
mixins: [mixin],
props: {
/**
* 表单数据
*/
form: {
type: Object,
require: true,
default: null
},
/**
* 表单项配置
*/
item: {
type: Object,
require: true,
default: null
},
/**
* 校验规则
*/
rules: {
type: Object,
require: true,
default: null
},
/**
* 是否显示冒号
*/
colon: {
type: Boolean,
default: true
},
/**
* 是否为表单查询模式
* 监听 change 事件,回调为 query 事件
*/
isQuery: {
type: Boolean,
default: false
},
/**
* 输入空格
* 可选值:none,auto, valid
* none: 不能输入空格
* auto: 自由输入空格, 默认值
* valid: 不能连续输入空格
*/
inputSpace: {
type: String,
default: 'auto'
},
/**
* 查询表单方法
*/
query: {
type: Function,
default: null
}
},
data() {
return {
inputPlaceholder: ['el-input', 'el-input-number'], // 输入提示
selectPlaceholder: ['el-select', 'el-time-select', 'el-time-picker', 'el-date-picker', 'el-cascader'] // 选择提示
}
},
methods: {
/**
* 处理多选
* @param item 绑定的表单对象
*/
checkedChange(item) {
const checkedCount = this.form[item.prop].length
if (this._typeof(item.checkAll) === Object) {
item.checkAll.value = checkedCount === item.options.length
item.checkAll.indeterminate = checkedCount > 0 && checkedCount < item.options.length
}
},
/**
* 处理全选
* @param item 绑定的表单对象
*/
checkAllChange(item) {
this.form[item.prop] = item.checkAll.value ? this.getValues(item.options) : []
item.checkAll.indeterminate = false
},
/**
* 获取对象列表的值,返回一个值得列表
* @param array
* @returns {array}
*/
getValues(array) {
return array.map(option => {
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['value']]
if ('value' in option) return option['value']
return option['id']
})
},
/**
* 获取选项数值
* @param option
*/
getOptionValue(option) {
if (this._typeof(option) === String) return option
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['value']]
if ('value' in option) return option['value']
return option['id']
},
/**
* 获取选项标签
* @param option
* @returns {*}
*/
getOptionLabel(option) {
if (this._typeof(option) === String) return option
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['label']]
if ('label' in option) return option['label']
return option['name']
},
/**
* 插入属性
* @param item
*/
insertAttrs(item) {
const attrs = _typeof(item.attrs) === Object ? item.attrs : {}
if ('placeholder' in attrs) return attrs
if (this.inputPlaceholder.includes(item.component)) return Object.assign(attrs, { placeholder: '请输入' + item.label })
if (this.selectPlaceholder.includes(item.component)) return Object.assign(attrs, { placeholder: '请选择' + item.label })
return attrs
},
/**
* 输入处理
* @param val
*/
inputHandle(val) {
if (!['valid', 'none'].includes(this.inputSpace)) return
this.form[this.item.prop] = preventVoidInput({ text: val, trimAll: this.inputSpace === 'none' })
},
/**
* 改变处理
* @param val
*/
changeHandle(val) {
if (!this.isQuery) return
this.$emit('query')
}
}
}
</script>
<style lang="less">
.el-form-item {
&.has-colon {
.el-form-item__label:after {
content: ":";
position: relative;
left: 4px;
top: -1px;
padding-right: 4px;
font-weight: 600;
}
}
}
</style>
<style lang="less" scoped>
/deep/ .avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
/deep/ .avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
/deep/ .avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
/deep/ .avatar {
width: 178px;
height: 178px;
display: block;
}
.el-form-item-extra {
margin-top: 4px;
line-height: 1.5;
min-height: 24px;
clear: both;
color: #00000073;
font-size: 12px;
transition: color .3s cubic-bezier(.215,.61,.355,1);
}
</style>
JxlFormItem 表单项
满足开发者采用配置的方式,快速搭建表单的需求。
基于 ElementUI 的 el-form-item 标签封装。
<template>
<el-form-item :label="item.label" :prop="item.prop" :rules="rules" :required="item.required" :class="[colon ? 'has-colon' : '']" v-bind="item.itemAttrs">
<template v-if="item.label" slot="label">
<!--条目名称-->
<span>{{ item.label }}</span>
<!--条目额外的内容-->
<slot v-if="$scopedSlots['labelExtra']" name="labelExtra" :row="item" />
<!--条目提示符-->
<jxl-tip v-if="item.tip" :options="item.tip">
<template v-if="_get(item, 'tip.prompt.content.slotName')" v-slot:content>
<slot v-if="$scopedSlots['tipContent']" name="tipContent" :row="item" />
</template>
</jxl-tip>
</template>
<!--自定义组件-->
<slot v-if="$scopedSlots['content']" name="content" :row="item" />
<!--对象组件-->
<template v-else-if="_typeof(item.component) === Object">
<component :is="item.component" v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--输入框-->
<template v-else-if="item.component === 'el-input'">
<el-input v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--计数器-->
<template v-else-if="item.component === 'el-input-number'">
<el-input-number v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--选择器-->
<template v-else-if="item.component === 'el-select'">
<el-select v-model="form[item.prop]" :name="item.prop" value="" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" @input="inputHandle" v-on="item.events">
<el-option v-if="item['optionAll']" :label="item['optionAll'].label || '全部'" :value="item['optionAll'].value || ''" />
<template v-for="(option, index) in item.options">
<el-option v-if="isVisible(option)" :key="index" :label="getOptionLabel(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs" :value="getOptionValue(option)" />
</template>
</el-select>
</template>
<!--开关-->
<template v-else-if="item.component === 'el-switch'">
<el-switch v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" @input="inputHandle" v-on="item.events" />
</template>
<!--多选框-->
<template v-else-if="item.component === 'el-checkbox'">
<template v-if="_typeof(item.options) === Array">
<el-checkbox v-if="_typeof(item.checkAll) === Object" v-model="item.checkAll.value" :name="item.prop + 'All'" :indeterminate="item.checkAll.indeterminate" v-bind="item.checkAll.attrs" @change="checkAllChange(item)">{{ item.checkAll.label || item.checkAll.name }}</el-checkbox>
<el-checkbox-group v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" @change="checkedChange(item)">
<template v-for="(option, index) in item.options">
<el-checkbox v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-checkbox>
</template>
</el-checkbox-group>
</template>
<template v-else>
<el-checkbox v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" v-on="item.events">{{ item.optionName }}</el-checkbox>
</template>
</template>
<!--单选框-->
<template v-else-if="item.component === 'el-radio'">
<el-radio-group v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" @change="changeHandle" v-on="item.events">
<template v-if="item['optionButton']">
<template v-for="(option, index) in item.options">
<el-radio-button v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-radio-button>
</template>
</template>
<template v-else>
<template v-for="(option, index) in item.options">
<el-radio v-if="isVisible(option)" :key="index" :label="getOptionValue(option)" :disabled="isDisabled(option.attrs)" v-bind="option.attrs">{{ getOptionLabel(option) }}</el-radio>
</template>
</template>
</el-radio-group>
</template>
<!--滑块-->
<template v-else-if="item.component === 'el-slider'">
<el-slider v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--时间选择器-固定时间点-->
<template v-else-if="item.component === 'el-time-select'">
<el-time-select v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--时间选择器-任意时间点-->
<template v-else-if="item.component === 'el-time-picker'">
<el-time-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--日期选择器-->
<template v-else-if="item.component === 'el-date-picker'">
<el-date-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" @change="changeHandle" v-on="item.events" />
</template>
<!--评分-->
<template v-else-if="item.component === 'el-rate'">
<el-rate v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--颜色选择器-->
<template v-else-if="item.component === 'el-color-picker'">
<el-color-picker v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--穿梭框-->
<template v-else-if="item.component === 'el-transfer'">
<el-transfer v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<!--级联选择器-->
<template v-else-if="item.component === 'el-cascader'">
<el-cascader v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="insertAttrs(item)" v-on="item.events" />
</template>
<!--上传-->
<template v-else-if="item.component === 'el-upload'">
<el-upload v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="Object.assign(item.attrs, item.events)" :action="item.attrs.action">
<template v-if="$scopedSlots[item.uploadSlot]">
<slot :name="item.uploadSlot" :row="item" />
</template>
<template v-else>
<img v-if="form.avatar" :src="form.avatar" class="avatar" alt="">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</template>
</el-upload>
</template>
<!--具名组件-->
<template v-else>
<component :is="item.component" v-model="form[item.prop]" :name="item.prop" :disabled="isDisabled(item.attrs)" v-bind="item.attrs" v-on="item.events" />
</template>
<div v-if="item.extra" class="el-form-item-extra">{{ item.extra }}</div>
</el-form-item>
</template>
<script>
import mixin from '@/components/libs/mixin'
import JxlTip from '@/components/libs/Tip/JxlTip'
import { _typeof } from '@/utils/libs/support'
import { preventVoidInput } from '@/components/libs/util'
export default {
name: 'JxlFormItem',
components: { JxlTip },
mixins: [mixin],
props: {
/**
* 表单数据
*/
form: {
type: Object,
require: true,
default: null
},
/**
* 表单项配置
*/
item: {
type: Object,
require: true,
default: null
},
/**
* 校验规则
*/
rules: {
type: Object,
require: true,
default: null
},
/**
* 是否显示冒号
*/
colon: {
type: Boolean,
default: true
},
/**
* 是否为表单查询模式
* 监听 change 事件,回调为 query 事件
*/
isQuery: {
type: Boolean,
default: false
},
/**
* 输入空格
* 可选值:none,auto, valid
* none: 不能输入空格
* auto: 自由输入空格, 默认值
* valid: 不能连续输入空格
*/
inputSpace: {
type: String,
default: 'auto'
},
/**
* 查询表单方法
*/
query: {
type: Function,
default: null
}
},
data() {
return {
inputPlaceholder: ['el-input', 'el-input-number'], // 输入提示
selectPlaceholder: ['el-select', 'el-time-select', 'el-time-picker', 'el-date-picker', 'el-cascader'] // 选择提示
}
},
methods: {
/**
* 处理多选
* @param item 绑定的表单对象
*/
checkedChange(item) {
const checkedCount = this.form[item.prop].length
if (this._typeof(item.checkAll) === Object) {
item.checkAll.value = checkedCount === item.options.length
item.checkAll.indeterminate = checkedCount > 0 && checkedCount < item.options.length
}
},
/**
* 处理全选
* @param item 绑定的表单对象
*/
checkAllChange(item) {
this.form[item.prop] = item.checkAll.value ? this.getValues(item.options) : []
item.checkAll.indeterminate = false
},
/**
* 获取对象列表的值,返回一个值得列表
* @param array
* @returns {array}
*/
getValues(array) {
return array.map(option => {
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['value']]
if ('value' in option) return option['value']
return option['id']
})
},
/**
* 获取选项数值
* @param option
*/
getOptionValue(option) {
if (this._typeof(option) === String) return option
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['value']]
if ('value' in option) return option['value']
return option['id']
},
/**
* 获取选项标签
* @param option
* @returns {*}
*/
getOptionLabel(option) {
if (this._typeof(option) === String) return option
if (this._typeof(this.item.optionSource) === Object) return option[this.item.optionSource['label']]
if ('label' in option) return option['label']
return option['name']
},
/**
* 插入属性
* @param item
*/
insertAttrs(item) {
const attrs = _typeof(item.attrs) === Object ? item.attrs : {}
if ('placeholder' in attrs) return attrs
if (this.inputPlaceholder.includes(item.component)) return Object.assign(attrs, { placeholder: '请输入' + item.label })
if (this.selectPlaceholder.includes(item.component)) return Object.assign(attrs, { placeholder: '请选择' + item.label })
return attrs
},
/**
* 输入处理
* @param val
*/
inputHandle(val) {
if (!['valid', 'none'].includes(this.inputSpace)) return
this.form[this.item.prop] = preventVoidInput({ text: val, trimAll: this.inputSpace === 'none' })
},
/**
* 改变处理
* @param val
*/
changeHandle(val) {
if (!this.isQuery) return
this.$emit('query')
}
}
}
</script>
<style lang="less">
.el-form-item {
&.has-colon {
.el-form-item__label:after {
content: ":";
position: relative;
left: 4px;
top: -1px;
padding-right: 4px;
font-weight: 600;
}
}
}
</style>
<style lang="less" scoped>
/deep/ .avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
/deep/ .avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
/deep/ .avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
/deep/ .avatar {
width: 178px;
height: 178px;
display: block;
}
.el-form-item-extra {
margin-top: 4px;
line-height: 1.5;
min-height: 24px;
clear: both;
color: #00000073;
font-size: 12px;
transition: color .3s cubic-bezier(.215,.61,.355,1);
}
</style>