cube-ui 维护记录 —— form组件label扩展

在表单label栏目实现标题序号、红色标示语、扩展项。效果图如下:
扩展项与序号:

在这里插入图片描述
点击规范后:
在这里插入图片描述
红色标示语:
在这里插入图片描述

先贴上需要修改的文件form-item.vue的代码:

<template>
  <div class="cube-form-item border-bottom-1px" ref="formItem" :class="itemClass">
    <template v-if="!isBtnField">
    <!-- label块 -->
      <slot name="label">
        <div class="cube-form-label" v-show="fieldValue.label"><span>{{fieldValue.label}}</span></div>
      </slot>
      <!-- 验证模块 -->
      <cube-validator
        class="cube-form-field"
        v-if="hasRules"
        ref="validator"
        v-model="originValid"
        :disabled="validatorDisabled"
        :model="validatorModel"
        :model-key="validatorModelKey"
        :rules="fieldValue.rules"
        :messages="fieldValue.messages"
        @input="validatorChangeHandler"
        @validating="validatingHandler"
        @validated="validatedHandler"
        @msg-click="msgClick"
      >
     	<!-- 开启验证下的组件 -->
        <slot>
          <component :is="componentName" v-model="modelValue" v-bind="fieldValue.props" v-on="fieldValue.events"></component>
        </slot>
      </cube-validator>
      <!-- 不开启验证下的组件 -->
      <div class="cube-form-field" v-else>
        <slot>
          <component :is="componentName" v-model="modelValue" v-bind="fieldValue.props" v-on="fieldValue.events"></component>
        </slot>
      </div>
    </template>
    <!-- 按钮标题 -->
    <cube-button v-bind="fieldValue.props" v-on="fieldValue.events" v-else>{{fieldValue.label}}</cube-button>
  </div>
</template>

<script>
  import { processField } from './fields/index'
  import { resetTypeValue, cb2PromiseWithResolve, debounce } from '../../common/helpers/util'
  import CubeValidator from '../validator/validator.vue'
  import LAYOUTS from './layouts'
  import { getResetValueByType } from './fields/reset'
  import mixin from './mixin'
  import components from './components'
  components.CubeValidator = CubeValidator

  const COMPONENT_NAME = 'cube-form-item'
  const EVENT_FOCUSIN = 'focusin'
  const EVENT_FOCUSOUT = 'focusout'

  export default {
    name: COMPONENT_NAME,
    mixins: [mixin],
    props: {
      field: {
        type: Object,
        default() {
          /* istanbul ignore next */
          return {}
        }
      }
    },
    data() {
      const validatorModelKey = 'value'
      const modelKey = this.field.modelKey
      const modelValue = modelKey ? this.form.model[modelKey] : null
      return {
        validatorDisabled: false,
        validatorModelKey,
        modelValue: modelValue,
        validatorModel: {
          [validatorModelKey]: modelValue
        }
      }
    },
    computed: {
      fieldValue() {
        return processField(this.field)
      },
      hasRules() {
        return Object.keys(this.fieldValue.rules || {}).length > 0
      },
      isBtnField() {
        return this.fieldValue.type === 'button'
      },
      itemClass() {
        const rules = this.fieldValue.rules
        return {
          // only handle required rule for now
          'cube-form-item_required': rules && rules.required,
          'cube-form-item_btn': this.isBtnField,
          'cube-form-item_validating': this.validating,
          'cube-form-item_pending': this.pending,
          'cube-form-item_valid': this.valid,
          'cube-form-item_invalid': this.invalid
        }
      },
      modelVal() {
        return this.form.model[this.fieldValue.modelKey]
      },
      componentName() {
        const fieldValue = this.fieldValue
        const component = fieldValue.component
        if (component) {
          return component
        }
        const type = fieldValue.type
        const cubeType = `cube-${type}`
        if (components[cubeType]) {
          return cubeType
        }
        return type
      }
    },
    watch: {
      modelVal(newModel) {
        if (this.modelValue !== newModel) {
          this.modelValue = newModel
        }
      },
      modelValue: {
        handler(newModel) {
          // update form model
          this.form.model[this.fieldValue.modelKey] = newModel
          this.updateValidatorModel()
        },
        sync: true
      },
      originValid(newVal) {
        this.lastOriginValid = newVal
      }
    },
    beforeCreate() {
      this.form = this.$parent.form
    },
    created() {
      this.form.addField(this)
      this.getValidatorModel = (modelValue) => {
        this.pending = false
        return modelValue
      }
    },
    mounted() {
      this.initDebounce()
      this.initFocusEvents()
    },
    methods: {
      initDebounce() {
        let debounceTime = this.fieldValue.debounce
        if (debounceTime === true) {
          debounceTime = 200
        }
        if ((!debounceTime && debounceTime !== 0) || debounceTime < 0 || this.fieldValue.trigger === 'blur') return
        this.getValidatorModel = debounce((modelValue) => {
          this.syncValidatorValue()
          // this.validate()
          return modelValue
        }, debounceTime, false, this.validatorModel[this.validatorModelKey])
      },
      focusInHandler() {
        this.focused = true
      },
      focusOutHandler() {
        this.focused = false
        this.updateValidatorModel()
        // this.validate()
      },
      initFocusEvents() {
        if (this.fieldValue.trigger === 'blur') {
          const formItem = this.$refs.formItem
          formItem.addEventListener(EVENT_FOCUSIN, this.focusInHandler, false)
          formItem.addEventListener(EVENT_FOCUSOUT, this.focusOutHandler, false)
          this.getValidatorModel = (modelValue) => {
            if (this.focused) {
              return this.validatorModel[this.validatorModelKey]
            } else {
              this.pending = false
              this.form.updatePending()
              return modelValue
            }
          }
        }
      },
      removeFocusEvents() {
        const formItem = this.$refs.formItem
        formItem.removeEventListener(EVENT_FOCUSIN, this.focusInHandler, false)
        formItem.removeEventListener(EVENT_FOCUSOUT, this.focusOutHandler, false)
      },
      updateValidatorModel() {
        this.pending = true
        this.validatorModel[this.validatorModelKey] = this.getValidatorModel(this.modelValue)
        if (this.pending) {
          this.form.setPending(this.pending)
          this.originValid = undefined
        }
      },
      syncValidatorValue() {
        this.pending = false
        this.validatorModel[this.validatorModelKey] = this.modelValue
        this.form.updatePending()
      },
      validatorChangeHandler() {
        // disabled or true to true no update validity
        if (this.validatorDisabled || (this.originValid && this.lastOriginValid)) {
          return
        }
        this.updateValidity()
      },
      validatingHandler() {
        this.validating = true
        this.form.setValidating(true)
      },
      validatedHandler() {
        this.validating = false
        this.form.updateValidating()
      },
      updateValidity() {
        const validator = this.$refs.validator
        if (validator) {
          // sync update validaty
          this.form.updateValidity(this.fieldValue.modelKey, validator.valid, validator.result, validator.dirty)
        }
      },
      validate(cb) {
        const promise = cb2PromiseWithResolve(cb)
        if (promise) {
          cb = promise.resolve
        }
        const validator = this.$refs.validator
        if (validator) {
          validator.validate(() => {
            this.validatorDisabled = true
            this.updateValidity()
            cb && cb()
            this.$nextTick(() => {
              this.validatorDisabled = false
            })
          })
        } else {
          cb && cb()
        }
        return promise
      },
      reset() {
        const fieldValue = this.fieldValue
        if (fieldValue.modelKey) {
          const defValue = getResetValueByType(fieldValue.type)
          this.validatorDisabled = true
          resetTypeValue(this, 'modelValue', defValue)
          // need to sync validator value too, because of trigger blur or debounce
          this.syncValidatorValue()
          this.$refs.validator && this.$refs.validator.reset()
          this.$nextTick(() => {
            this.validatorDisabled = false
          })
        }
        this.validating = false
        this.pending = false
      },
      msgClick() {
        /* istanbul ignore if */
        if (this.form.layout !== LAYOUTS.STANDARD) {
          return
        }
        /* istanbul ignore next */
        this.$createToast && this.$createToast({
          type: 'warn',
          txt: this.$refs.validator.msg,
          time: 1000
        }).show()
      }
    },
    beforeDestroy() {
      this.removeFocusEvents()
      this.form.destroyField(this)
      this.form = null
    },
    components
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
  @require "../../common/stylus/variable.styl"
  @require "../../common/stylus/mixin.styl"

  .cube-form-item
    position: relative
    display: flex
    align-items: center
    padding: 0 15px
    &:last-child
      &::after
        display: none
    .cube-checkbox-group, .cube-radio-group
      background-color: transparent
    .cube-checkbox, .cube-radio
      padding-left: 0
      padding-right: 0
  .cube-form-item_btn
    margin: 15px 0
    &::after
      display: none
  .cube-form-label
    display: flex
    align-items: center
    word-wrap: break-word
    word-break: break-word
  .cube-form-item_required
    .cube-form-label
      &::before
        content: "*"
        display: block
        margin-top: 1px
        margin-right: .3em
        color: $form-label-required-color
</style>

1.先实现标题序号的功能,我们可以通过css来实现,在babel那里加一个伪类放在标题前面,然后通过在cube-form-item这个类名的css中添加counter-increment属性递增一个计数器值,得到标题的下标,然后在伪类上使用content将下标显示出来。为了控制是否显示,还需要传入一个属性来控制,我这边是定义了一个showNumber 类型为boolean,默认为false,不显示,这个属性需要在form.vue 和 form-group.vue 中一层层传递过来。

showNumber: {
  type: Boolean,
  default () {
     return false
  }
},

在标题部分修改代码,定义一个显示序号的class名,我是定为cube-form-title,当showNumber为true时,显示序号:

<div class="cube-form-label" v-show="fieldValue.label">
    <span :class="showNumber? 'cube-form-title': ''">{{fieldValue.label}}</span>
</div>

因为表单项是遍历出来的,所以可以在cube-form-item中添加一个counter-increment,定一个变量cube-form-index用来记录下标:

.cube-form-item
    counter-increment: cube-form-index
    position: relative
    display: flex
    align-items: center
    padding: 0 15px

然后添加一个css

.cube-form-title
   &::before
      content: counter(cube-form-index) '.'
      order: -1
      white-space: nowrap
      margin-right: 10px

添加序号的功能就实现了。
2.添加红色标语,因为有字体的颜色改动,所以为了方便,我们将标题从text改为html就可以直接传入标签。

<div class="cube-form-label" v-show="fieldValue.label">
    <span :class="showNumber? 'cube-form-title': ''" v-html="fieldValue.label"></span>
</div>

使用的时候就可以直接传入html格式了

{
    type: 'select',
    modelKey: 'baseHsjg',
    label: '核实结果 <span style="color:red">(此项为核实人的状态)</span>',
    props: {
       options: []
    },
    rules: {
       required: true
    }
},

3.扩展项,由于业务上是点击按钮后出弹窗显示一个提示页面,但是经过考虑,为了更灵活,决定不只使用button,而是使用component标签实现,由于在做红色标语的时候使用v-html 会导致标签里面的元素会被覆盖,所以要改动一下结构,在里面再加一个行内元素标签span用于装label,然后使用slot和component标签实现一个自定义组件。为了接收label的组件属性、事件、对象,所以要定义一些参数用于控制自定义组件,我定的是labelComponent、labelProps、labelEvents:

<slot name="label">
        <div class="cube-form-label" v-show="fieldValue.label">
          <span :class="showNumber? 'cube-form-title': ''">
            <span v-html="fieldValue.label"></span>
            <slot>
              <component :is="fieldLabelComponent" v-bind="fieldValue.labelProps" v-on="fieldValue.labelEvents"></component>
            </slot>
          </span>
        </div>
      </slot>

还需要判断这个组件是否有传入,所以我将他封装成一个函数,判断后return

/*
* 判断LabelComponent属性是否有效
*/
fieldLabelComponent () {
   const fieldValue = this.fieldValue
   const component = fieldValue.labelComponent
   if (component) {
      return component
   } else {
      return false
   }
}

使用的时候的schema:

{
    type: 'select',
    modelKey: `modelKey`,
    label: '核实结果',
    labelComponent: popUpButton,
    labelProps: {
        data: '暂无',
        value: '规范',
        title: '暂无',
        styles: 'color:red;padding-left:10px',
        type: 'text',
        click: this.showLawerBtn
    },
    props: {
    },
    rules: {
        required: true
    }
}

labelProps 是回传到自定义组件 popUpButton 上的属性,传的是什么由popUpButton自定义组件决定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值