Skip to the content.

返回

StepsTab 字典分步表单组件

组件路径: StepsTab.vue

组件说明:

修改历史:

分步表单+动态表单项示例模块:

StepsTab-1703492046079.png

1)执行SQL脚本

2)路由配置

{
  "name": "分步表单和动态表单",
  "path": "/stepsTab",
  "module": "modules/ztb/ZtbXedjList"
}

使用

<!--当前状态和分步按钮选项两个数据分离情况,具体使用请参考组件:ZtbXedjForm.vue-->
<a-form-item class="step-form-item">
  <steps-tab
      ref="stepsTab"
      :debug="false"
      style="margin-bottom: 2vh"
      :dict="stateDict"
      v-decorator="['currentState', validatorRules.currentState]"
      :current-tab-dict-text.sync="currentTabName"
      :current-tab-dict-value.sync="currentTab"
      :current-finish="currentFinish"
      @update:hasPrev="e => $emit('update:hasPrev', e)"
      @update:hasNext="e => $emit('update:hasNext', e)"
      @stateChange="switchRequired"
  ></steps-tab>
</a-form-item>
<!--当前状态和分步按钮选项两个数据分离情况,使用自定义字典,具体使用请参考组件:ZtbXedjForm.vue-->
<a-form-item class="step-form-item">
  <steps-tab
      ref="stepsTab"
      :debug="false"
      :dict="true"
      :dict-text="['基本信息', '预警等级', '短信']"
      :dict-value="['1', '2', '3']"
      v-decorator="['warningStatus']"
      :current-finish="currentFinish"
      :current-tab-dict-value.sync="currentTab"
      @update:hasPrev="e => $emit('update:hasPrev', e)"
      @update:hasNext="e => $emit('update:hasNext', e)"
      @stateChange="switchRequired"
  ></steps-tab>
</a-form-item>

归档

使用方式@20240824旧版本归档

<!--当前状态和分步按钮选项两个数据分离情况-->
<steps-tab
  ref="stepsTab"
  :debug="false"
  style="margin-bottom: 2vh"
  :dict="stateDict"
  v-model="currentTab"
  :current-tab-dict-text.sync="currentTabName"
  :current-state.sync="validatorRules.currentState.modelValue"
  :current-finish="validatorRules.currentState.currentFinish"
  @update:hasPrev="e => $emit('update:hasPrev', e)"
  @update:hasNext="e => $emit('update:hasNext', e)"
  @stateChange="switchRequired"
></steps-tab>
<!--当前状态和选项表现一致的情况-->
<steps-tab
  :debug="true"
  dict="excavation_type_object_state"
  v-decorator="['currentState']"
  :current-finish="true"
  :current-tab-dict-value.sync="model.currentState"
  @update:currentTabDictValue="e => form.setFieldsValue({'currentState': e})"
></steps-tab>

旧版本-使用dict的选中值作为v-model值

代码:

<template>
  <a-steps v-if="dictLoad" v-model="currentTabDictValueIndex" type="navigation" size="small">
    <a-step v-for="step in stepItems" :key="step.title" :status="step.status" :title="step.title" :disabled="step.disabled" />
  </a-steps>
</template>

<script>
/**
 * 动态的分布表单展示组件封装
 * @author yoko
 * 可根据jeecg系统字典自动渲染步骤
 * 支持v-model双向绑定选中tab项
 * dict传值为Boolean时,启用字典且必须传入:dictText、dictValue、dict
 * dict传值为String时,将作为jeecg的系统内字典自动加载处理
 */
import { initDictOptions } from '@comp/dict/JDictSelectUtil';

export default {
  name: 'StepsTab',
  model: {
    prop: 'currentTabDictValue',
    event: 'change'
  },
  props: {
    /**
     * 当前节点(.sync同步|@update:currentState)
     * 这个值的类型和currentTabDictValue相同,即需要传入dict字典的value值
     * 作用:在非debug模式下,用户无法点击这个节点后的步骤
     */
    currentState: {
      type: String,
      default: '测试2'
    },
    currentFinish: {
      type: [Boolean, String],
      default: false
    },
    /**
     * 当前选中tab(v-model)
     * 这里的值是传入dict字典的value值
     */
    currentTabDictValue: {
      type: String,
      default: '测试2'
    },
    /**
     * 当前选中tab的字典翻译(.sync同步|@update:currentTabDictText)
     */
    currentTabDictText: {
      type: String,
      default: ''
    },
    /**
     * 静态字典文本数组,同tab的title
     */
    dictText: {
      type: Array,
      default: () => ['测试1', '测试2', '测试3']
    },
    /**
     * 静态字典值数组
     */
    dictValue: {
      type: Array,
      default: () => ['1', '2', '3']
    },
    /**
     * 字典模式
     * Boolean - false 时需传入 dictText dictValue
     * String - '字典code' 时默认去加载系统字典
     */
    dict: {
      type: [Boolean, String],
      default: false
    },
    /**
     * tab改变前的钩子,一定需要触发cb()
     */
    beforeChange: {
      type: Function,
      default: (before, after, cb) => cb()
    },
    /**
     * 调试模式(控制是否可以点击非当前步骤的后续step)
     */
    debug: {
      type: [Boolean, String],
      default: false
    },
    /**
     * 是否有下一步(.sync同步|@update:hasNext)
     */
    hasNext: {
      type: [Boolean, String],
      default: true
    },
    /**
     * 是否有上一步(.sync同步|@update:hasPrev)
     */
    hasPrev: {
      type: [Boolean, String],
      default: true
    }
  },
  data() {
    return {
      // 内部的是实际dict字典值对应的数组index
      currentTabDictValueIndex: 0,
      dictOptions: null,
      dictValueInner: [],
      dictTextInner: [],
      dictLoad: false
    }
  },
  computed: {
    transferCurrentTabDictValue() {
      if (!this.dictLoad) {
        return ''
      }
      return this.dict ? this.dictTextInner[this.dictValueInner.indexOf(this.currentTabDictValue)] : this.currentTabDictValue
    },
    transferCurrentState() {
      if (!this.dictLoad) {
        return ''
      }
      return this.dict ? this.dictTextInner[this.dictValueInner.indexOf(this.currentState)] : this.currentState
    },
    stepItems() {
      if (!this.dictLoad) {
        return []
      }
      const curIdx = this.dictTextInner.indexOf(this.transferCurrentState) || 0;
      return this.dictTextInner.map((text, idx) => {
        return {
          title: text,
          status: curIdx === idx ? this.currentFinish ? 'finish' : 'process' : curIdx < idx ? 'wait' : 'finish',
          disabled: this.debug ? false : (curIdx < idx)
        }
      })
    }
  },
  watch: {
    dict(v) {
      console.log('字典改变了', v, this.currentTabDictValue, this.currentTabDictValueIndex)
      this.dictLoad = false
      this.$nextTick(() => this.initialDict())
    },
    /**
     * 需要监听转换后的字典值(当前默认tab)
     * 可以做到 切换字典时自动切换默认选中tab
     */
    transferCurrentTabDictValue: {
      immediate: true,
      handler(val) {
        if (!val) {
          return
        }
        this.currentTabDictValueIndex = this.dictTextInner.indexOf(val || this.transferCurrentState) || 0;
        console.log('transferCurrentTabDictValue change', this.transferCurrentTabDictValue)
      }
    },
    /**
     * 这个方式做不到 切换字典时自动切换默认选中tab
     */
    // currentTabDictValue: {
    //   immediate: true,
    //   handler() {
    //     if (!this.dictLoad) {
    //       return
    //     }
    //     this.currentTabDictValueIndex = this.dictTextInner.indexOf(this.transferCurrentTabDictValue || this.transferCurrentState) || 0;
    //     console.log('currentTabDictValue change', this.transferCurrentTabDictValue, this.transferCurrentState, this.currentTabDictValueIndex)
    //   }
    // },
    /**
     * 双向绑定内部steps组件和外部值
     */
    currentTabDictValueIndex(idx, old) {
      const cb = (err) => {
        if (err) {
          console.error('错误触发了 ', err)
          return
        }
        this.$emit('change', this.dict ? this.dictValueInner[idx] : this.dictTextInner[idx]);
        this.$emit('update:currentTabDictText', this.dictTextInner[idx])
        console.log('currentTabDictValueIndex change', this.dictTextInner[idx], this.dictValueInner[idx])
        this.updateHasNextPrev()
      };
      this.beforeChange(this.dictValueInner[old], this.dictValueInner[idx], cb)
    },
    /**
     * 监听state值变化
     */
    transferCurrentState(val) {
      if (!val) {
        return
      }
      const args = {
        dictCode: this.dict,
        dictOptions: this.dictOptions,
        dictValue: this.dictValueInner,
        dictText: this.dictTextInner,
        text: this.transferCurrentState || this.dictTextInner[0],
        value: this.currentState || this.dictValueInner[0],
        index: this.dictValueInner.indexOf(this.currentState || this.dictValueInner[0])
      };
      console.log('transferCurrentState change', val, args)
      this.$emit('stateChange', args)
    }
  },
  async created() {
    await this.initialDict()
  },
  methods: {
    updateHasNextPrev() {
      console.log('updateHasNextPrev', this.currentTabDictValueIndex + 1 < this.dictValueInner.length, this.currentTabDictValueIndex > 0)
      this.$emit('update:hasNext', this.currentTabDictValueIndex + 1 < this.dictValueInner.length)
      this.$emit('update:hasPrev', this.currentTabDictValueIndex > 0)
    },
    /**
     * 上一步
     */
    prev() {
      if (!this.hasPrev) {
        return
      }
      this.currentTabDictValueIndex--
      this.$emit('update:currentState', this.dictValueInner[this.currentTabDictValueIndex])
    },
    /**
     * 下一步
     */
    next() {
      if (!this.hasNext) {
        return
      }
      this.currentTabDictValueIndex++
      this.$emit('update:currentState', this.dictValueInner[this.currentTabDictValueIndex])
    },
    /**
     * 初始化字典
     * @returns {Promise<void>}
     */
    async initialDict() {
      if (typeof this.dict === 'string') {
        const { result } = await initDictOptions(this.dict)
        this.dictOptions = result
        this.dictValueInner = result.map(e => e.value)
        this.dictTextInner = result.map(e => e.text)
      } else {
        if (this.dict && this.dictValue.length !== this.dictText.length) {
          throw new Error('字典模式下,请确保dictValue、dictText数量一致!')
        }
        this.dictValueInner = [...this.dictValue]
        this.dictTextInner = [...this.dictText]
      }
      // 触发computed缓存刷新
      this.dictLoad = true
      console.log('字典初始化完成', this.dictValueInner, this.dictTextInner, this.dictOptions)
    }
  }
}
</script>

END