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

  • 相关阅读:
    【HDOJ】2267 How Many People Can Survive
    【HDOJ】2268 How To Use The Car
    【HDOJ】2266 How Many Equations Can You Find
    【POJ】2278 DNA Sequence
    【ZOJ】3430 Detect the Virus
    【HDOJ】2896 病毒侵袭
    求奇数的乘积
    平方和与立方和
    求数列的和
    水仙花数
  • 原文地址:https://www.cnblogs.com/datiangou/p/10121663.html
Copyright © 2011-2022 走看看