zoukankan      html  css  js  c++  java
  • 用slot和component实现表单共用

    业务需求

    在oa开发中,有许多流程,每个流程里都会有很多字段,比如流程标题、拉下选择,附件等等,有些是每个流程都会有的,有些是特有的,按常规的方法开发,就为为一个流程写一个表单,校验,提交。如果新来流程,就复制一个表达,修改需要变更的地方。这样开发会导致很多重复的代码,而且比较凌乱

    简化实现

    • 将每一个输入框写成共用的,将必填校验判断也一并写入,比如:流程标题组件: iProcessTitle,使用详情看下方注释
    
    <template>
      <div>
        <div v-if="!isShow">
          <Row>
            <Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}:</span></Col>
            <Col :xs="18" :md='mdModelRight'>
              <FormItem :prop="config.key" :rules="rules">
                <Input
                  v-model="postData[config.key]"
                  :placeholder="placeholder"
                  :maxlength="config.maxLength"
                  :disabled="config.disabled || false"
                ></Input>
              </FormItem>
            </Col>
          </Row>
        </div>
        <div v-else>
          <div class="cont-i" v-if="config.title">
            <span class="gray gray-f">{{ config.title }}</span>
            <div class="attachment-i">{{ postData[config.key] }}</div>
          </div>
        </div>
      </div>
    </template>
    
    <script>
      import { mapState } from 'vuex'
      var validateData = {}
        export default {
            name: "i-process-title",
        computed: {
          ...mapState([
            'postData'
          ]),
          placeholder: function () {
            // 更具传入标题显示placeholder 
            let placeholder = '请选择输入' + this.config.title
            if (this.config.maxLength) {
              placeholder += '(' + this.config.maxLength +'个字以内)'
            }
            return placeholder
          },
          rules: function () {
            return {
              validator: validateData,
              trigger: 'blur'
            }
          },
          isShow: function () {
            return this.config.isShow
          }
        },
        props: {
          // 当前输入框配置
          config: {
            default(){
              return {
                title: '流程标题', // 输入框标题
                key: 'processTitle', // 要提交的字段
                required: false, // 是否必填
                disabled: false, // 是否禁止编辑
                isShow: true, // 是否是流程发起状态 true:流程发起,展示输入框; false: 审批过程/打印,展示结果
              }
            },
            type: Object
          }
        },
        data() {
          // 输入校验
          validateData = (rule, value, callback) => {
            let reg = /^[0-9]*$/;
            // 是否必填
            if (this.config.required) {
              if (value === '' || value === undefined) {
                callback(new Error(this.config.title + '必填'));
                return
              }
            }
            // 纯数字校验
            if (this.config.type && this.config.type === 'Number') {
              if (!reg.test(value) && value !== '' && value !== undefined) {
                callback(new Error('格式不符合'));
                return
              }
            }
            callback();
          }
                return {
    
          }
        },
        methods: {
        },
        mounted(){
          this.postData.department = this.$store.state.department
        }
        }
    </script>
    
    <style scoped>
    
    </style>
    
    
    • 选择框组件: iSelectType
    
    <template>
      <Row>
        <Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}:</span></Col>
        <Col :xs="18" :md='mdModelRight'>
          <FormItem :prop="config.key" :rules="rules">
            <Select v-model="postData[config.key]">
              <Option v-for="(item, key) in config.list" :value="item" :key="item">{{ key }}</Option>
            </Select>
          </FormItem>
        </Col>
      </Row>
    </template>
    <script>
      import UImodel from '../../assets/js/UIModel'
      import {mapState, mapMutations} from 'vuex'
      export default {
        name: 'i-select-type',
        props: {
          config: {
            default(){
              return {
                title: '是否超标', // 默认标题
                key: 'excessive', // 默认字段
                list: {           // 默认列表
                  '是': 'true',
                  '否': 'false'
                }
              }
            },
            type: Object
          }
        },
        computed: {
          ...mapState([
            'postData'
          ]),
          rules: function () {
            // 必填校验
            if (this.config.required) {
              return {
                required: true,
                message: '选择' + this.config.title,
                trigger: 'change'
              }
            }
          }
        },
        data () {
          return {
            mdModelLeft: UImodel.mdModelLeft,
            mdModelRight: UImodel.mdModelRight
          }
        }
      }
    </script>
    
    • 时间选择组件:iDate
    
    <template>
      <div>
        <Row>
          <Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}</span></Col>
          <Col :xs="18" :md='mdModelRight'>
            <FormItem prop="startTimeFlag" :rules="startRules">
              <DatePicker
                :type="type"
                v-model="postData.startTimeFlag"
                :format="format"
                placeholder="请选择时间"
                @on-change="sdateChange"
                style=" 200px">
              </DatePicker>
            </FormItem>
          </Col>
        </Row>
        <Row v-if="config.endate">
          <Col :xs="6" :md='mdModelLeft'><span class="t-span">结束时间:</span></Col>
          <Col :xs="18" :md='mdModelRight'>
            <FormItem prop="endTimeFlag" :rules="endRules">
              <DatePicker
                :type="type"
                v-model="postData.endTimeFlag"
                :format="format"
                :options="endDateOptions"
                placeholder="请选择时间"
                @on-change="edateChange"
                style=" 200px">
              </DatePicker>
            </FormItem>
          </Col>
        </Row>
      </div>
    </template>
    <script>
      import UImodel from '../../assets/js/UIModel'
      import {mapState, mapMutations} from 'vuex'
      var datePassCheck = {}
      export default {
        name: 'i-date',
        props: {
          config: {
            default () {
              return {
                title: '开始时间'
              }
            },
            type: Object
          }
        },
        computed: {
          ...mapState([
            'postData'
          ]),
          // 开始时间校验
          startRules: function () {
            //是否必填
            if (this.config.required) {
              return {
                type: 'date',
                required: true,
                message: this.config.title + '不能为空',
                trigger: 'change'
              }
            }
          },
          // 结束时间校验
          endRules: function () {
            // 是否必填
            if (this.config.endate && this.config.endrequired) {
              return {
                validator: datePassCheck, trigger: 'change'
              }
            }
          },
          // 时间显示格式
          format: function () {
            if (this.config.type === 'datetime') {
              this.type = 'datetime'
              return 'yyyy-MM-dd HH:mm'
            }
            return 'yyyy-MM-dd'
          }
        },
        methods: {
          ...mapMutations([
            'SET_POSTDATAKEY'
          ]),
          sdateChange: function (val) {
            this.$set(this.postData, this.config.key, val)
            this.$set(this.postData, 'startTime', val)
          },
          edateChange: function (val) {
            this.postData.endTime = val
          }
        },
        watch: {
          // 开始时间改变,需清空结束时间
          'postData.startTime': function (val) {
            let _this = this
            let v = this.postData.startTimeFlag
            let date = new Date(v)
            let time = date.getFullYear() + '-' +
              (date.getMonth() + 1) + '-' +
              date.getDate()
            this.endDateOptions.disabledDate = function (date) {
              return _this.config.isYesterday ? date.valueOf() < (new Date(time) - 23 * 60 * 60 * 1000) : date.valueOf() < new Date(time)
              // return date.valueOf() < new Date(time)
            }
            // 清空后面的日期
            this.postData.endTimeFlag = ''
            this.postData.endTime = ''
            this.showError = true
          }
        },
        data () {
          // 结束时间校验
          datePassCheck = (rule, value, callback) => {
            if (value === '') {
              callback(new Error('结束时间不能为空'))
            } else if (this.postData.endTime < this.postData.startTime) {
              callback(new Error('结束时间需大于开始时间'))
            } else {
              callback()
            }
          }
          return {
            mdModelLeft: UImodel.mdModelLeft,
            mdModelRight: UImodel.mdModelRight,
            // 结束日期的 起点规则
            endDateOptions: {
              disabledDate (date) { }
            },
            type: 'date'
          }
        },
        mounted () {
        }
      }
    </script>
    <style></style>
    
    
    • 如果还需要其他组件,按照上述方法添加即可,下面写申请界面的公共部分:apply
    
    <template>
      <Form ref="formValidate" :model="postData" :rules="ruleValidate" class="leave">
        <div class="disabledBox">
         <!-- 这里是每个流程的表单部分 -->
          <slot></slot>
          <!-- 附件组件 -->
          <uploadAttachments
            :processKey="processKey"
            :fileData="fileData"
            :fileAry="temporary.file"
            @deleteFileAry="deleteFileAry">
          </uploadAttachments>
          <div class="disabled" ref="disabled" v-if="submitAfret"></div>
        </div>
        <Row v-if="!submitAfret">
          <Col :span="6" :offset="18">
            <Button type="info" @click="submitData('formValidate')">转下一步</Button>
          </Col>
        </Row>
      </Form>
    </template>
    <script>
      import {mapState, mapMutations} from 'vuex'
      import uploadAttachments from './../process/common/uploadAttachments.vue'
      import tools from 'static/js/tools.js'
      export default {
        components: {
          uploadAttachments
        },
        props: {
          ruleValidate: {
            default(){
              return {}
            },
            type: Object
          },
          processKey: {
            type: String
          },
          candidate: {
            type: Array
          }
        },
        data () {
          return {
            processStart: true,
            // 提交之后显示推荐人
            submitAfret: false,
            // 转下一步数据
            nextStep: {},
            temporary: {
            },
            fileData: []
          }
        },
        computed: {
          ...mapState([
            'postData', 'processData'
          ])
        },
        methods: {
          ...mapMutations([
          'SET_POSTDATA'
          ]),
          submitData: function () {
            // console.log(this.postData)
            console.log(this.processStart)
            // 验证
            this.$refs.formValidate.validate(res => {
              //验证通过,则提交
              if (res) {
              // 这里执行提交操作
              }
              this.$Message.error("请根据页面提示填写内容!");
            })
          }
        }
      }
    </script>
    
    

    如上:<slot></slot>是每个流程的表单部分,其他则为每个流程共有的,比如附件、提交操作等。

    • 用上面的资源写一个休假流程:leave
    
    &lt;template&gt;
        &lt;apply :processKey="processKey" :candidate="candidate"&gt;
            &lt;!-- apply的slot部分,即为每个流程的表单部分 --&gt;
            &lt;component :is="item.component" v-for="(item, index) in items" :config="item" :key="index"&gt;
            &lt;/component&gt;
        &lt;/apply&gt;
    &lt;/template&gt;
    &lt;script&gt;
      import apply from './../comm/apply.vue'
      import {mapState, mapMutations} from 'vuex'
    
      const getComponent = name =&gt; {
        return resolve =&gt; require([`./../comm/${name}.vue`], resolve)
      }
      export default {
        components: {
          apply
        },
        props: {
          candidate: {
            type: Array
          },
          processKey: {
            type: String
          }
        },
        data () {
          return {
            //表单配置
            items: [
              {
                component: getComponent('iProcessTitle'),
                title: '流程标题',
                key: 'processTitle',
                required: true
              },
              {
                component: getComponent('iSelectType'),
                title: '休假类别',
                key: 'leave',
                required: true,
                list: {
                  '事假': 'busy',
                  '病假': 'sick',
                  '婚假': 'marriage',
                  '产假': 'maternity',
                  '丧假': 'funeral',
                  '陪产假': 'paternity',
                  '姨妈假': 'menstruation',
                  '年假': 'annual'
                }
              },
              /**
               * @author Liangyuhong
               * @date 2018/9/21 10:33
               * @Description: 精确到分钟
               */
              {
                component: getComponent('iDate'),
                title: '开始时间',
                type: 'datetime',
                required: true,
                endate: true, // 需要显示结束时间
                endrequired: true, // 结束时间必填
                isYesterday: true // 是否可以选择当天
              },
              {
                component: getComponent('iDays'),
                title: '休假天数',
                key: 'day',
                required: true
              },
              {
                component: getComponent('iRemarks'),
                title: '请假理由',
                key: 'state',
                required: true
              }
            ]
          }
        },
        methods: {
          ...mapMutations([
            'SET_POSTDATA'
          ]),
          init: function (data) {
            this.SET_POSTDATA(data)
            this.$root.Bus.$emit('initPostData', data)
            this.postData = data
            this.postData.processInstanceId = data.processInstanceId
          }
        },
        mounted () {
          this.SET_POSTDATA({})
        }
      }
    &lt;/script&gt;
    &lt;style lang="less" scoped&gt;
        @import './../../../static/css/process/process.less';
    &lt;/style&gt;
    

    这样再开发新流程的过程中就不用去重写template部分了,只需要配置好data里的items,这里指明了当前流程所需要的字段,每个字段的各种属性,其他的都基本不用动

    注:以上为不完全代码,依赖于ivew,提交的数据为postData 。存的全局变量

    总结

    • 将每一个输入框都写成单独的,可配置的组件,借助ivew,把校验都放在单个表单组件内部完成
    • 写一个公共组件apply,利用slot提供可变部分传入,把不变的,比如附件,提交这些写入这个组件,提供的便利在于不用在每一个流程去关注提交,校验等等一系列每个流程都需要的操作
    • 具体到某一个流程时只需关注data的items,也就是开发一个流程,只写items就可以完成。

    原文地址:https://segmentfault.com/a/1190000017306664

  • 相关阅读:
    [FlareOn4]greek_to_me
    [FlareOn1]Sploitastic
    [FlareOn1]Creation
    [FlareOn1]5get_it
    esxi6.7中,显卡设置为直通步骤
    esxi6.7安装步骤
    nmcli命令详解
    查看指定进程的IO/CPU/MEM/带宽/显卡
    使用WSGIServer修改静态文件
    k8s配置多端口ingress
  • 原文地址:https://www.cnblogs.com/datiangou/p/10121663.html
Copyright © 2011-2022 走看看