iview表单组件配置化

253 阅读2分钟

BaseInput

<template>
  <component
    v-bind="$attrs"
    :is="inputType"
    v-model="inputValue"
    :style="defaultStyle"
    :type="componentType"
    v-on="$listeners"
    @input="(val)=>$emit('changeValue',val)"
  >
    <template v-if="Array.isArray(childrenOptions)">
      <component
        :is="getChildrenType"
        v-for="item in childrenOptions"
        :key="item.value"
        :value="item.value"
        :label="item.value"
      >
        {{ item.label }}
      </component>
    </template>
    <template v-else>
      <component
        :is="getChildrenType"
        v-for="(item,key) in childrenOptions"
        :key="key"
        :value="key"
        :label="item"
      >
        {{ item }}
      </component>
    </template>
  </component>
</template>
<script>
export default {
  name: 'BaseInput',
  model: {
    prop: 'value',
    event: 'changeValue'
  },
  props: {
    value: {
      type: [String, Array, Number, Date, Boolean],
      default: null
    },
    inputType: {
      type: String,
      default: 'Input'
    },
    childrenOptions: {
      type: [Array, Object],
      default: () => []
    },
    componentType: {
      type: String,
      default: undefined
    }
  },
  data () {
    return {
      inputValue: undefined
    }
  },
  computed: {
    getChildrenType () {
      const typeMap = {
        RadioGroup: 'Radio',
        Select: 'Option',
        CheckboxGroup: 'Checkbox'
      }
      return typeMap[this.inputType] || ''
    },
    defaultStyle () {
      const widthMap = {
        Input: '150px',
        Select: '150px',
        DatePicker: '150px',
        Cascader: '150px'
      }
      return {
        width: widthMap[this.inputType] || ''
      }
    }
  },
  watch: {
    value (val) {
      this.inputValue = val
    }
  }
}
</script>

XForm

<template>
  <Form
    ref="form"
    :model="formValue"
    v-bind="$attrs"
    v-on="$listeners"
  >
    <slot name="preItem" />
    <FormItem
      v-for="item in options"
      :key="item.key"
      :prop="item.key"
      :label="item.itemLabel"
      :label-width="item.itemLabelWidth"
      v-bind="item.formItemOptions"
    >
      <BaseInput
        v-model="formValue[item.key]"
        v-bind="getAttr(item)"
        v-on="getListener(item)"
        @on-change="setFormData"
      />
    </FormItem>
    <slot />
  </Form>
</template>
<script>
import BaseInput from './BaseInput.vue'
export default {
  name: 'XForm',
  components: {
    BaseInput
  },
  model: {
    prop: 'formData',
    event: 'formChange'
  },
  props: {
    formData: {
      type: Object,
      required: true
    },
    formOptions: {
      type: [Array, Function],
      default: () => []
    }
  },
  data () {
    return {
      formValue: {}
    }
  },
  computed: {
    options () {
      if (typeof this.formOptions === 'function') {
        return this.formOptions()
      } else {
        return this.formOptions
      }
    }
  },
  mounted () {
    this.formValue = this.formData
  },
  methods: {
    setFormData () {
      this.$emit('formChange', this.formValue)
    },
    isListener (key) {
      //判断传入参数是否为事件
      const preStr = key.substring(0, 2)
      return preStr.toLowerCase() === 'on'
    },
    formatListenerName (name) {
      const str = name.charAt(2)
      if (str === '-') {
        return name
      } else {
        return 'on-' + name.substring(2).toLowerCase()
      }
    },
    getAttr (item) {
      const attrs = {}
      for (const i in item) {
        if (typeof item[i] !== 'function') {
          attrs[i] = item[i]
        } else {
          if (!this.isListener(i)) {
            attrs[i] = item[i]
          }
        }
      }
      delete attrs.itemLabel
      delete attrs.itemLabelWidth
      delete attrs.itemoptions
      delete attrs.key
      return attrs
    },
    getListener (item) {
      const listeners = {}
      for (const i in item) {
        if (typeof item[i] === 'function') {
          if (this.isListener(i)) {
            listeners[this.formatListenerName(i)] = item[i]
          }
        }
      }
      return listeners
    },
    async validate () {
      const flag = await this.$refs.form.validate()
      return flag
    },
    async validateField (prop) {
      const flag = await this.$refs.form.validateField(prop)
      return flag
    },
    resetFields () {
      this.$refs.form.resetFields()
      this.$emit('formChange', this.formValue)
    }
  }
}
</script>

BaseInput组件说明

使用配置化参数生成iview基础表单组件


参数

paramstyperemark
inputTypeString对应iview组件类型,可传入Input,Select,DatePicker,等对应iview表单基础组件名称
childrenOptionsObject或Array用于含有子组件的表单组件配置,比如Select,RadioGroup,CheckboxGroup等
componentTypeString含有type属性值的表单组件使用,等于传入组件的type属性值,比如DatePicker
others---iview组件文档中定义属性均可作为参数传入

使用

Input

<BaseInput v-model="name" inputType="Input" placeholder="请输入姓名">

Select

<BaseInput
    v-model="status"
    input-type="Select"
    :children-options="childrenOptions"
/>
childrenOptions: [
    {
        label: '标签1',
        value: '1'
    },
    {
        label: '标签2',
        value: '2'
    }
]

XForm组件说明

使用配置化参数生成Form表单


参数

paramstyperemark
formOptionsArray组件配置文件,使用BaseInput生成对应组件
others---iview组件文档中定义Form属性均可作为参数传入

formOptions参数

paramstyperemark
keystringv-model绑定的属性名
itemLabelstringformItem的label
itemLabelWidthnumberformItem的label宽度
itemOptionsobjectformItem的属性值对象
others---BaseInput属性均可传入

当传入属性为方法时,on开头函数名的函数会被标识为事件,函数支持驼峰命名或横杠命名

使用

<XForm
  v-model="formData"
  :form-options="formOptions"
/>
formOptions = [
  {
    key: 'name',
    itemLabel: '姓名',
    itemLabelWidth: 120,
    style: { width: '200px' },
    inputType: 'Input',
    placeholder: '请输入姓名'
  },
  {
    key: 'textarea',
    itemLabel: '姓名',
    inputType: 'Input',
    componentType: 'textarea',
    placeholder: '请输入'
  },
  {
    key: 'status',
    itemLabel: '状态',
    inputType: 'Select',
    placeholder: '请选择状态',
    childrenOptions: [
      {
        label: '状态1',
        value: '1'
      },
      {
        label: '状态2',
        value: '2'
      }
    ]
  },
  {
    key: 'i-switch',
    itemLabel: '开关',
    inputType: 'i-switch'
  }
]