最近在做后台管理系统,遇到一个需求,就是根据下拉选择框多选,选中的内容在表格中展示,每一行都可以添加多个分类标签。

element下拉框出不来 element下拉表格_数组


效果图如上:

分析需求:

1.select组件多选功能实现

<el-select v-model="checks" multiple placeholder="请选择物料分类" @change="checkChange" size="mini" @remove-tag="removeTag">
   <el-option v-for="(cate) in productCategorymenu" :key="cate.id" :label="cate.name" :value="cate.id">
   </el-option>
</el-select>

分析上面的代码:

1.checks是v-model双向数据绑定的结果,多选时,checks是一个数组,数组中的每一项就是选中的option中的value值
2.@change是监听select组件的选中与取消选中操作
3.@remove-tag是监听select组件,不触发下拉弹窗,直接删除tag时触发的函数
4.option是循环渲染的,v-for循环的数据中,key是唯一值,label是展示的文本,value就是v-model中的每一项值了

2.监听select组件的选中与取消选中等方法

change监听组件选中与取消

checkChange(val) {
  //这个val就是v-model绑定的数组格式
  this.checks = val;
  //判断右侧的表格中是否有数据,如果没有或者没有此字段,则给此字段赋值为一个空数组
  if (!this.supplierObj.supplierChecks) {
      this.supplierObj.supplierChecks = [];
  }
  var arr = this.supplierObj.supplierChecks;
  if (val.length > arr.length) {
      this.checks.forEach(check => {
          var list = arr.filter(a => a.topCategoryId == check);
          if (list.length) {
              //console.log('找到数据');
          } else {
              //console.log('没有找到数据');
              var nameArr = this.productCategorymenu.filter(cate => cate.id == check);
              arr.push({
                  "topCategoryId": check,
                  "topCateogryName": nameArr && nameArr[0] && nameArr[0].name,
                  "cateogryName": []
              })
          }
      })
  } else {
      var list = [];
      this.checks.forEach(check => {
          var lis = arr.filter(a => a.topCategoryId == check);
          lis && lis.forEach(l => {
              list.push(l);
          })                     
      })
      this.supplierObj.supplierChecks = list;
  }
},

分析上面的代码:

如果此次操作是选中一项,则左侧的数组长度要多于右侧的数组长度,因此可以循环遍历,找到多出来的一项,也就是本次添加的一项,给右侧数组添加。

如果找到两个数组中的不同项,这个问题我遇到过两次了。。。

思路如下:
对第一个数组进行循环遍历,针对数组的每一项,都跟第二个数组filter筛选,如果有筛选值,则表示有数据,否则表示第二个数组中没有此项。

this.checks.forEach(check=>{
	var list = arr.filter(a=> a.topCategoryId == check);
	//此处的List就是筛选出来的数组,如果有长度,则表示第二个数组含有此项,否则表示第二个数组没有此项
	if (list.length) {
       //console.log('找到数据');找到数据后一般不做处理,
    } else {
        //console.log('没有找到数据');
        //没有找到此项,则根据此项的value找到对应的名称,可以通过for循环,也可以通过filter筛选
        var nameArr = this.productCategorymenu.filter(cate => cate.id == check); 
        arr.push({
            "topCategoryId": check,
            "topCateogryName": nameArr && nameArr[0] && nameArr[0].name,
            "cateogryName": []
        })
    }
})

如果此次操作是取消选中一项,则左侧的数组长度要小于右侧的数组长度,因此可以循环遍历,找到少出来的一项,也就是本次去掉的一项,给右侧数组删除。

最后的关键还是归结到:找两个数组的不同之项。还是可以通过筛选的方式

//定义一个空数组,遍历左侧数组,如果右侧数据有某一项,则直接push到空数组中,这样遍历完成后,拿到的跟左侧数据保持一致的右侧数据了。
var list = [];
this.checks.forEach(check => {
    var lis = arr.filter(a => a.topCategoryId == check);
    lis && lis.forEach(l => {
        list.push(l);
    })                     
})
this.supplierObj.supplierChecks = list;//将list数组赋值给右侧数组即可完成

3.直接删除tag形式的多选项,触发removeTag方法

removeTag(val) {
	//此时的参数val就是移除的value值
   var arr = this.supplierObj.supplierChecks;
   var list = arr.filter(a => a.topCategoryId != val);
   this.supplierObj.supplierChecks = list;
},

4.右侧表格的添加多项tag标签问题

<el-table :data="supplierObj.supplierChecks" border stripe style="width:800px">
    <el-table-column prop="topCateogryName"
                     label="物料名称">
    </el-table-column>
    <el-table-column prop="cateogryName"
                     label="分类">
        <template slot-scope="scope">
            <el-tag :key="tag" size="mini" :closable="!disabled"
                    v-for="tag in scope.row.cateogryName"
                    :disable-transitions="false"
                    @close="handleClose(tag,scope.row.cateogryName)">
                {{tag}}
            </el-tag>
            <el-input type="text" style="display:inline-block;width:80px;"
                      v-if="scope.row.inputVisible"
                      v-bind:value="scope.row.inputValue"
                      :ref="scope.$index+'saveTagInput'"
                      size="small"
                      @input="handleTagChange($event,scope)" @blur="handleInputConfirm(scope)" @keyup.enter.native="$event.target.blur">
            </el-input>
            <el-button v-else size="mini" class="button-new-tag" @click="showInput(scope)" :disabled="disabled">+ 添加分类</el-button>
        </template>
    </el-table-column>
</el-table>

注意:官网上给出的动态tag标签是这样的:

element下拉框出不来 element下拉表格_vue.js_02


官网上的的输入框,有两个触发保存的操作:回车事件失去焦点事件,但是在实际操作过程中,单纯的失去焦点事件并无问题,但是回车事件本身也会触发失去焦点事件,因此会导致页面出现问题。

回车事件与失去焦点事件冲突的解决方法

@keyup.enter.native="$event.target.blur" 触发回车后,通过$event.target.blur用来触发失去焦点事件即可。

使用v-model无法给input组件添加双向绑定时,则需要拆解为value+input的方式来处理

可能是因为我这边的需求是表格渲染的input动态添加tag,在实际操作中,并不能用v-model对组件进行双向绑定,因此改为以下的处理方式:

<el-input type="text" style="display:inline-block;width:80px;"
         v-if="scope.row.inputVisible"
          v-bind:value="scope.row.inputValue"
          :ref="scope.$index+'saveTagInput'"
          size="small"
          @input="handleTagChange($event,scope)" @blur="handleInputConfirm(scope)" @keyup.enter.native="$event.target.blur">
</el-input>

监听input组件的input输入方法,用于实现双向数据绑定

handleTagChange(val,scope) {
    var row = scope.row;
    row.inputValue = val;
    var index = scope.$index;
    this.supplierObj.supplierChecks[index].inputValue = row.inputValue;
    this.$set(this.supplierObj.supplierChecks,index,row);
},

监听添加标签按钮,用于展示input组件,并获取焦点

showInput(scope) {
    var index = scope.$index;
    var row = this.supplierObj.supplierChecks[index];
    row.inputVisible = true;
    row.inputValue = null;
    this.$forceUpdate();
    this.$set(this.supplierObj.supplierChecks,index,row);
    this.$nextTick(_ => {
        this.$refs[`${scope.$index}saveTagInput`].$refs.input.focus();
    });
},

移除tag标签的操作,触发的是close方法

handleClose(tag, arr) {
   arr.splice(arr.indexOf(tag), 1);
},

input输入框组件失去焦点事件

handleInputConfirm(scope) {
    var index = scope.$index;
    var value = scope.row.inputValue;
    if (value && !this.supplierObj.supplierChecks[index].cateogryName.includes(value)) {
        this.supplierObj.supplierChecks[index].cateogryName.push(value);
    } else {
        this.$message.error('请输入名称且名称不能重复');
        return;
    }
    scope.row.inputValue = "";
    scope.row.inputVisible = false;
    this.$set(this.supplierObj.supplierChecks,index,scope.row);
    this.$forceUpdate();
},

完成!!!