vue项目中一个开发思路记录:可复用的select选项框中的options数据,根据用户选择后可添加子级拓展

本文探讨了如何提高前端表单组件的复用性,特别是针对时间维度选择功能。通过将选项options抽取到独立的JS文件中,实现各页面按需调用,减少了重复代码。此外,文章介绍了如何设计一个灵活的form组件,能够根据不同的数据类型(如input、select等)动态展示,并提供了具体的代码示例,包括select组件的动态加载和国际化支持。这种开发思路适用于后台管理系统,能有效提高代码维护性和开发效率。
摘要由CSDN通过智能技术生成

今天在工作中发现了一种复用性很广的开发思路,例如在后台管理系统中经常会有提供给用户进行选择的表单:在一些数据记录的功能页面中,常常要通过选择不同时间维度来显示每个时段的数据(如“日”、“月”、“年”),而选完时间维度后,起始日期和结束日期的可选项和格式也不一样(如“日”维度下的时间范围是当天的某几个小时,“月”维度下是当月的某几天,“年”维度下就是当年的某几个月)。
若每次需要运用到此种时间维度查询功能时,都重新写一遍select逻辑就太麻烦了,直接ctrl + cv又会让代码过于坑长。
直接将select做成组件的方法固然也可以,但我们若想将select组件乃至form组件开发得复用性更广,就不该把options的内容写死成时间维度选项,毕竟上面说的只是举个例,在一个后台管理项目中需要复用的选项框还多着呢。
那我们的思路是否可以从选项options的复用上开展呢?将options抽取出来,把要用到的选项做成一个个对象放在js文件中,哪个页面要用到哪个选项时直接调用该js文件中相应的options对象就可以了


在此以图文方式为大家举例:
在这里插入图片描述
某个功能页面中运用到了项目中的form组件,该页面需要一个时间维度的选项来查询不同时间维度下的数据
在这里插入图片描述
form的数据记录在formList里,而formList应为一个计算属性
在这里插入图片描述
图中的红框就是今天的重点了。这个this.$dimension是什么呢?看过我上一篇文章的小伙伴肯定都知道了:浅谈vue中的 含义及其用法( 含义及其用法( 含义及其用法(xxx引用的位置)

那我们就顺藤摸瓜找到它:
在这里插入图片描述
main.js:
在这里插入图片描述
dimension.js:
在这里插入图片描述
而最初的options选项里的年维度this.$dimension.year就是图中的year,绑定的value值为’year’,显示的选项label为langt['year'](这里涉及到国际化的i18n知识,若页面语言选择中文,则取zh.js中的值’年’,若选择英文则取en.js中的值’year’)
当选择完“年”维度后,则显示“年”维度下的时间范围选项“选择年份”
同路径下的dates.js:
在这里插入图片描述
图中的pickerOptions即为判断选择时间是否大于当前时间,若大于,则触发该页面的对应逻辑(如该页面若允许选择的时间大于当前时间,则啥也不干;反之则可以提示类似“所选时间不可大于当前时间”的错误弹窗)。

而form组件的设计思路可以参考如下:

        <el-form ref="formValidate"
            :model="formObj" 
            :rules="rules"
            :inline="inline" 
            :label-width="labelWidth"
            v-bind="$attrs">
            <el-form-item v-for="(item) in formListNew" 
                style="padding-left: 10px"
                :key="item.prop" 
                :prop="item.prop"  
                :label="item.label">

                <!-- input -->
                <el-input v-if="item.type === 'input'" v-model="formObj[item.prop]"
                  :placeholder="item.placeholder"
                  :clearable="item.clearable"
                  :disabled="item.disabled">
                  <el-select v-if="item.appendSelect" v-model="item.selectValue"
                    slot="prepend"
                    style="width: 120px;"
                    :disabled="item.selectDisabled">
                    <el-option v-for="option in item.options" 
                      :key="option.value" 
                      :value="option.value"
                      :label="option.label">
                    </el-option>
                  </el-select>
                  <el-button v-if="item.appendButton" 
                    slot="append" 
                    :disabled="item.buttonDisabled"
                    @click="inputButtonClick(item.prop, item.selectValue)">
                    {{lang.Taketherecommendedvalue}}
                  </el-button>
                </el-input>

                <!-- textarea -->
                <el-input v-if="item.type === 'textarea'" v-model="formObj[item.prop]" 
                  type="textarea"
                  :placeholder="item.placeholder"
                  :disabled="item.disabled">
                </el-input>

                <!-- number -->
                <el-input-number v-if="item.type ==='number'" v-model="formObj[item.prop]" 
                  controls-position="right"
                  :style="`width:100%`"
                  :placeholder="item.placeholder"
                  :min="item.min"
                  :max="item.max"
                  :disabled="item.disabled">                  
                </el-input-number>

                <!-- checkbox -->
                <el-checkbox v-if="item.type === 'checkbox'" v-model="formObj[item.prop]" 
                    :true-label="item.trueValue"
                    :false-label="item.falseValue"
                    :disabled="item.disabled">
                </el-checkbox>
                <!-- checkboxGroup -->
                <el-checkbox-group v-if="item.type==='checkboxGroup'" v-model="formObj[item.prop]" >
                    <el-checkbox v-for="option in item.options" 
                        :key="option.value" 
                        :label="option.value"
                        :disabled="option.disabled"
                        >
                        {{option.label}}
                    </el-checkbox>
                </el-checkbox-group>
                <!-- shiftCheckboxGroup -->
                <el-checkbox-group v-if="item.type==='shifts'" v-model="formObj[item.prop]">
                    <el-checkbox v-for="option in shiftOptions" 
                        :key="option.value" 
                        :label="option.value">
                        {{option.label}}
                    </el-checkbox>
                </el-checkbox-group>
                <!-- shiftRradioGroup -->
                <el-radio-group v-if="item.type==='shift'" v-model="formObj[item.prop]">>
                  <el-radio v-for="option in shiftOptions" 
                    :key="option.value" 
                    :label="option.value">
                    {{option.label}}
                  </el-radio>
                </el-radio-group>
                <el-radio-group v-if="item.type==='radio'" v-model="formObj[item.prop]">>
                  <el-radio v-for="option in item.options" 
                    :key="option.value" 
                    :label="option.value">
                    {{option.label}}
                  </el-radio>
                </el-radio-group>
                
                <!-- select -->
                <el-select v-if="item.type === 'select'" v-model="formObj[item.prop]" 
                  :placeholder="item.placeholder" @change="selectChange(item,formObj[item.prop])"
                  :disabled="item.disabled"
                  :clearable="item.clearable"
                  :style="`width:${item.width ? item.width : '100%'}`">
                  <el-option v-for="option in item.options" 
                      :key="option.value" 
                      :value="option.value"
                      :label="option.label">
                  </el-option>
                </el-select>
                <!-- selectMultiple -->
                <el-select v-if="item.type==='selectMultiple'" v-model="formObj[item.prop]" @change="selectChange(item,formObj[item.prop])" :collapse-tags='item.collapseTags'
                    multiple>
                    <el-option v-for="option in item.options" 
                    :value="option.value" 
                    :key="option.value" 
                    :label="option.label" multiple>
                    </el-option>
                </el-select>
                <!-- alarmMethod -->
                <el-select v-if="item.type === 'alarmMethod'" v-model="formObj[item.prop]" 
                  clearable
                  :placeholder="lang.Selectalarmscheme" 
                  :disabled="item.disabled"
                  :style="`width:100%`">
                  <el-option v-for="option in alarmMethods" 
                      :key="option.value" 
                      :value="option.value"
                      :label="option.label">
                  </el-option>
                </el-select>
                <!-- product -->
                <el-select v-if="item.type === 'product'" v-model="formObj[item.prop]" 
                  remote
                  filterable
                  reserve-keyword
                  :placeholder="lang.pleaseInputKeywords" 
                  :remote-method="getProducts"
                  :disabled="item.disabled"
                  :style="`width:100%`">
                  <el-option v-for="option in products" 
                      :key="option.value" 
                      :value="option.value"
                      :label="option.label">
                  </el-option>
                </el-select>

                <!-- userSelect -->
                <vueDynamicLoader v-if="item.type === 'userSelect'" v-model="formObj[item.prop]"
                  url="xxxx/index.js"
                  :c-lang="locale"
                  recombine-symbol=";"
                  :data="item.data"
                  :loginId="true" 
                  @change="userChange($event, item.prop)">
                </vueDynamicLoader>
                <!-- treeSelect -->
                <cmpTreeSelect v-if="item.type === 'treeSelect'" v-model="formObj[item.prop]"
                  :style="`width:${item.width ? item.width : '100%'}`"
                  :disabled="item.disabled"
                  :placeholder="item.placeholder"
                  :isSingleCheck="item.isSingleCheck"
                  :disabledKeys="item.disabledKeys"
                  :url="item.url"
                  :httpParams="item.httpParams"
                  @change="cmpTreeSelectChange"
                >
                </cmpTreeSelect>
                <!-- tableSelect -->
                <cmpTableSelect v-if="item.type === 'tableSelect'" v-model="formObj[item.prop]"
                  :disabled="item.disabled"
                  :isSingleCheck="item.isSingleCheck"
                  :url="item.url"
                  :httpParams="item.httpParams"
                  @change="$refs['formValidate'].validateField(item.prop)">
                </cmpTableSelect>

                <!-- dateTimePicker -->
                <el-date-picker v-if="item.type === 'dateTimePicker'" v-model="formObj[item.prop]" 
                    :clearable="false"
                    :disabled="item.disabled"
                    :popper-class="item.format == 'yyyy-MM-dd HH' ? 'el-date-picker__hour' : ''" 
                    :value-format="item.valueFormat"
                    :type="item.pickerType"
                    :format="item.format"
                    :placeholder="lang.Pleaseselectatime" size="mini"
                    :picker-options="item.pickerOptions">
                </el-date-picker>
                <!-- timeSelect -->
                <el-time-select v-if="item.type==='timeSelect'" v-model="formObj[item.prop]"
                    :picker-options="{
                        start: '00:00',
                        step: '00:30',
                        end: '23:30'
                    }"
                    :placeholder="lang.Selecttime">
                </el-time-select>
                <!-- timePicker -->
                <TimePicker v-if="item.type === 'timePicker'" v-model="formObj[item.prop]" 
                    confirm
                    hide-disabled-options 
                    :key="item.step"
                    :clearable="false"
                    :style="`width:${item.width ? item.width : ''}`"
                    type="time"
                    format="HH:mm"
                    :steps="[1, item.step ? item.step : 5, 0]"
                    :disabled-hours="item.disabledHours||[]"
                    :disabled-minutes="item.disabledMinutes||[]"
                    :placeholder="lang.Pleaseselectatime">
                </TimePicker>
                <cmpTime v-if="item.type === 'cmpTime'" :disabled="item.disabled||false"  v-model="formObj[item.prop]"></cmpTime>
                <!-- <div  v-if="item.type === 'cmpdaytime'">{{formObj[item.prop]}}</div> -->
                <cmpMinutetime v-if="item.type === 'cmpMinutetime'" v-model="formObj[item.prop]" :pickerOptions="item.pickerOptions" :step="item.step"></cmpMinutetime>
                <cmpdaytime v-if="item.type === 'cmpdaytime'" v-model="formObj[item.prop]" :pickerOptions="item.pickerOptions"></cmpdaytime>
                <!-- <cmpdaytime -->
                  <div v-if="item.type=='section'">
                     <el-col :span="11">
                      <el-input type='text' v-model="formObj[item.model1]" clearable></el-input>
                     </el-col>
                     <el-col class="line" :span="2">-</el-col>
                     <el-col :span="11">
                        <el-input type='text' v-model="formObj[item.model2]" clearable></el-input>
                     </el-col>
                  </div>
                  <div v-if="item.type=='btns'">
                    <el-button v-for='(li,i) in item.btns' :key="i" :type="li.primary||''" :size="li.size||'mini'" @click="cfmButtonClick(li)">{{li.text}}</el-button>
                  </div>
            </el-form-item>

            <el-form-item v-for="(item,index) in dateOptions" 
              :key="item.key"
              :label="item.label">

              <el-time-select  v-if="item.type === 'time'" v-model="dates[index]"
                style="width: 200px"
                :picker-options="{
                    start: '00:00',
                    step: '01:00',
                    end: '23:00'
                }"
                @change="changDate($event,item)">
              </el-time-select>

              <el-date-picker v-else v-model="dates[index]"
                :clearable="false"
                :placeholder="lang.Pleaseselectatime"
                :popper-class="item.format == 'yyyy-MM-dd HH' ? 'el-date-picker__hour' : ''" 
                :type="item.type"
                :format="item.format"
                :picker-options="item.pickerOptions"
                @change="changDate($event,item)">
              </el-date-picker>

            </el-form-item>

            <slot></slot>
        </el-form>

组件的计算属性formList下对于children的递归算法:

    formListNew() {
      let list = this.formList
        .filter(item => !item.isHidden)
        .map(item => {
          if(item.type === "checkboxGroup" && item.parentProp && this.formObj[item.parentProp]) {
            let parentForm = this.formList.find(form => form.prop === item.parentProp);
            if(!parentForm) return item;
            let parentOption = parentForm.options.find(option => option.value === this.formObj[item.parentProp]);
            if(!parentOption || !parentOption.children || parentOption.children.length == 0) {
              item.options = [];
            }
            else {
              item.options = parentOption.children;
            }            
            let values = item.options.map(option => option.value);
            let checkedValues = this.formObj[item.prop].filter(value => values.includes(value));
            if(checkedValues.length < this.formObj[item.prop].length) {
              this.formObj[item.prop].splice(0, this.formObj[item.prop].length, ...checkedValues);
            }
            return item;
          }
          if(item.type === "select" && item.parentProp && this.formObj[item.parentProp]) {
            let parentForm = this.formList.find(form => form.prop === item.parentProp);
            if(!parentForm) return item;
            let parentOption = parentForm.options.find(option => option.value === this.formObj[item.parentProp]);
            item.options = parentOption.children;
            if(item.options.length > 0 && this.formObj[item.prop] && !item.options.map(option => option.value).includes(this.formObj[item.prop])){
              this.formObj[item.prop] = item.options[0].value;
            }
          }
          if(item.type === "select" 
                  && item.options.length > 0 
                  && this.formObj[item.prop] 
                  && !item.options.map(option => option.value).includes(this.formObj[item.prop])
          ) {
            this.formObj[item.prop] = item.options[0].value;
            return item;
          }
          if(item.type === "dates" && item.parentProp && this.formObj[item.parentProp]) {
            let parentForm = this.formList.find(form => form.prop === item.parentProp);
            if(!parentForm) return item;
            let parentOption = parentForm.options.find(option => option.value === this.formObj[item.parentProp]);
            this.dateOptions = parentOption.children;
            this.formObj[item.prop] = this.dates;
            return item;
          }
          if(item.type === "shift" || item.type === "shifts") {
            this.getShifts(item);
          }
          if(item.type === "alarmMethod") {
            this.getAlarmMethodList(item);
          }
          if(item.type === "product") {
            this.products = [];
          }
          if(item.type === "timePicker" && this.formObj[item.prop] && typeof this.formObj[item.prop] === "string") {
            this.formObj[item.prop] = moment(`2020-01-01 ${this.formObj[item.prop]}`).toDate();
          }
          return item;
        });
      return list;
    },

等有时间了再回过头来补充注释吧


以后这个专栏就用来记录在工作中遇到的一些项目上有意思、实用性强、复用性广的开发思路

感谢您的支持,THX

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值