import { Form, FormItem } from 'element-ui' import FormItemWrap from '@/components/FormItemWrap' import { iterator } from './shared' const ISchema = { type: Object, default: () => ({}) } const IConfig = { type: Object, default: () => ({}) } const IValue = { type: Object, default: () => ({}) } // el-form组件props的key const EL_FORM_PROP_KEYS = Object.keys(Form.props) // el-form-item组件props的key const EL_FORM_ITEM_PROP_KEYS = Object.keys(FormItem.props) export default { components: { FormItemWrap }, props: { schema: ISchema, config: IConfig, value: IValue, disabled: { type: Boolean, default: false } }, data() { return { formData: { ...(this.value || {}) }, formType: {}, formOptions: {}, formConfig: { size: 'small', queryAfterReset: true, ...(this.config || {}) }, lastLinkageValue: {} } }, mounted() { // 先初始化default的值 this.initDefault() // 初始化类型 this.initType() // 再初始化options的值 this.initOptions() }, watch: { /** * 监听value变化,如果value发生变化,引用地址发生变化,则覆盖formData */ value: { handler() { // 取出当前值 const { value, formData } = this // 如果值发生变化 if (value === formData) { return } // 遍历数据 Object.keys(value).forEach(key => { // 如果当前项的值和传入的值不一致,则使用传入的值 if (formData[key] !== value[key]) { formData[key] = value[key] } }) }, deep: true } }, methods: { /** * 先将default的值初始化至formData中 */ initDefault() { iterator(this.schema, (schema, key) => { this.$set(this.formData, key, (this.value[key] === undefined || this.value[key] === null) ? schema.default : this.value[key]) }) // 数据开始双向绑定 this.$emit('input', this.formData) // 触发change方法 this.$emit('change', this.formData) }, /** * 初始化formType类型 */ initType() { iterator(this.schema, (schema, key) => { // 重新生成的类型 const schemaType = schema.type instanceof Function ? schema.type(this.formData) : schema.type // 如果当前和上一次的类型不一致,则重新赋值 if (this.formType[key] !== schemaType) { this.$set(this.formType, key, schemaType) } }) }, /** * 初始化select等的下拉选项 */ initOptions() { iterator(this.schema, async(schema, key) => { // 如果当前是数组,则直接赋值 if (schema.options instanceof Array) { this.$set(this.formOptions, key, schema.options) } // 如果当前options是promise,则直接在then中获取结果 if (schema.options instanceof Promise) { const options = await schema.options this.$set(this.formOptions, key, options) } // 如果当前是方法,则根据是否有联动项,判断是否立即执行 if (schema.options instanceof Function) { const options = await schema.options(this.formData) this.$set(this.formOptions, key, options) } }) }, /** * 初始化绑定的方法 * @param schemaOn 当前组件上需要绑定的方法 */ initBindFunction(schemaOn) { // 如果没有需要绑定的方法 if (!schemaOn) { return {} } // 将值绑定上formData return Object.keys(schemaOn).reduce((memo, key) => { memo[key] = schemaOn[key].bind(this, this.formData) return memo }, {}) }, /** * 绑定属性 * @param {Object} attrs 当前属性值 * @param {String} type 当前组件类型 */ bindAttrs(attrs, type) { // 组件有的属性 let componentAttrs = [] // 判断类型 switch (type) { case 'el-form': componentAttrs = EL_FORM_PROP_KEYS break case 'el-form-item': componentAttrs = EL_FORM_ITEM_PROP_KEYS break } return Object.keys(attrs).reduce((memo, current) => { // 如果选择的属性,是组件的属性 if (componentAttrs.includes(current)) { memo[current] = attrs[current] } return memo }, {}) }, /** * 过滤掉不需要显示的schema */ filterUnShowSchema() { // 如果当前没有数据 if (!this.schema) { return {} } // 当前需要显示的formSchema const formSchema = {} // 便利原始数据 iterator(this.schema, (value, key) => { // 如果vif是方法 if (value.vif instanceof Function) { // 如果当前vif返回结果是true,则需要直接展示 if (value.vif(this.formData)) { formSchema[key] = value } } else { formSchema[key] = value } }) return formSchema }, /** * 占位文案 * @param schema 当前schema对象 */ placeholder(schema) { // 如果当前设置了占位文案 if (schema.attrs && schema.attrs.placeholder) { return schema.attrs.placeholder } // 如果当前时下拉或者级联组件,则展示选择,其他展示输入 return `请${['el-select', 'el-cascader'].includes(schema.type) ? '选择' : '输入'}${schema.label || ''}` }, /** * 表单数据发生变化:触发一次options方法 * @param value 当前发生值变化的表单的值 * @param key 当前发生值变化的表单的key */ handleFormDataChange(value, key) { const { lastLinkageValue } = this // 便利数据 iterator(this.schema, async(schema, schemaKey) => { // 如果当前是方法,且配置了联动项,则当联动项发生变化才会触发options if (schema.options instanceof Function && schema.linkage) { // 如果当前改变的值是联动项,则和上一次改变的值不一样,则执行options方法 if (schema.linkage.includes(key) && value !== lastLinkageValue[key]) { // 重新赋值,防止保留了上一次的记过 this.formData[schemaKey] = schema.default // 将options的值初始化为空 this.$set(this.formOptions, schemaKey, []) // 开始执行options方法 const options = await schema.options(this.formData) // 开始绑定值,渲染页面 this.$set(this.formOptions, schemaKey, options) } } }) // 记录下当前的联动项的值 lastLinkageValue[key] = value // 开始初始化type this.initType() // 开始双向绑定数据 this.$emit('input', this.formData) // 通过change方法,向上级暴露当前表单输入的值 this.$emit('change', this.formData) // 双向绑定数据 this.$emit('input', this.formData) }, /** * 点击提交方法 */ handleSubmit() { this.$emit('submit', this.formData) }, /** * 点击重置方法 */ handleReset() { // 去掉输入内容以及校验结果 this.$refs.formRender.resetFields() // 通知父组件 this.$emit('reset') // 如果当前需要点击重置后发起一起请求 this.formConfig.queryAfterReset && this.handleSubmit() }, /** * 拿到对应key的options * @param fieldKey 字段key */ getOptions(fieldKey) { return this.formOptions[fieldKey] }, /** * 拿到指定field的,具体值得option * @param fieldKey 字段key * @param optionValue 字段的值 */ getOption(fieldKey, optionValue) { return this.formOptions[fieldKey].find(option => option.value === optionValue) }, /** * 重新加载options方法 */ async reloadOptions(key, data) { // 生成options的方法 const { options } = this.schema[key] // 如果当前是方法 if (options instanceof Function) { // 开始调用方法 const option = await options(this.formData, data) // 开始赋值为默认值 this.formData[key] = this.schema[key].default // 开始绑定值,渲染页面 this.$set(this.formOptions, key, option) } } } }