Element Table实现用户自定义列

需求:文件列表用户自定义

基础:之前已经实现了文件列表的功能,但是是固定表头的列表

期初,我们是根据用户提出的要求展示相应的列,没有做过多的考虑,因此,我们当时直接固定了列名。这样做其实大大限制了程序的可扩展性,如果用户使用过程中发现我需要的没有这么多列或者我想要更多的列的时候怎么办?只能改代码,严重违反了软件设计的开放封闭的原则,因此,在功能设计的时候要考虑的全面一点,不能不听用户的,但是也不能全听用户的,要有全局观。

基础代码实现:

当el-table元素中注入data对象数组后,在el-table-column中用prop属性来对应对象中的键名即可填入数据,用label属性来定义表格的列名。可以使用width属性来定义列宽。

    <el-table
        //绑定对象数组
        :data="tableData"
        //带斑马纹的表格
        stripe
        //带有纵向边框
        border
        class="table"
        //当某一行被点击时会触发该事件
        @row-click="openDetails"
      >
        <el-table-column
          //对应列内容的字段名,
          prop="projectname"
          //列宽
          width="100"
          //显示的标题
          label="项目名"
          show-overflow-tooltip
        ></el-table-column>
        <el-table-column
          prop="name"
          width="100"
          label="文件名"
          show-overflow-tooltip
        ></el-table-column>
        <el-table-column
          prop="type"
          width="100"
          label="文件类型"
          show-overflow-tooltip
        ></el-table-column>
        <el-table-column
          prop="url"
          width="350"
          label="文件地址"
          show-overflow-tooltip
        ></el-table-column>
        <el-table-column
          label="操作"
          width="200"
          align="center"
          show-overflow-tooltip
        >
          <template slot-scope="scope">
            <div
              v-if="
                ['video', 'application', 'audio', 'pdf'].includes(
                  scope.row.type
                )
              "
            >
              <el-button
                type="text"
                icon="el-icon-plus"
                @click="addQuestion(scope.row)"
                >添加问题</el-button
              >
              <el-button
                type="text"
                icon="el-icon-delete"
                @click="deleteFile(scope.$index, scope.row)"
                >删除文件</el-button
              >
            </div>
            <div v-else>
              <el-button type="text" icon="el-icon-plus" disabled
                >添加问题</el-button
              >
              <el-button type="text" icon="el-icon-delete" disabled
                >删除文件</el-button
              >
            </div>
          </template>
        </el-table-column>
      </el-table>

js:

里面包含一些分页逻辑,之前的总结中已经写过element Table+Pagination实现分页_灵活的小胖子-CSDN博客

    handleSearch(pageNum) {
      if (pageNum) {
        this.query.pageNum = 1;
      }
      this.tableData = [];
      Search(this.query).then((res) => {
        var result = res.result;
        this.dataList = result.informationData;
        this.total = result.informationCount;
        for (let i = 0; i < this.dataList.length; i++) {
          var o = new Object();
          o.name = this.dataList[i].name;
          o.type = this.dataList[i].type;
          o.length = this.dataList[i].length;
          o.projectname = this.dataList[i].projectname;
          o.url = this.dataList[i].url;
          this.tableData.push(o);
        }
      });
    },

实现效果:

优化:有了前车之鉴,我们直接将文件列表设计成用户自定义的岂不是更好,让用户决定显示什么不显示什么,做到让软件的使用者成为软件的设计者

首先,我们列出供用户选择的全部的列,我是将整个表的列和列注释(给用户看的一定不能是英文)都查出来

//sql
select COLUMN_NAME "columns",COLUMN_COMMENT "remark" from information_schema.COLUMNS where table_name = 't_information'
    //调用后端方法
    handleSet(){
       SelectAllColumns().then((res) => {
         this.columnsList=res.result
      });
    },

渲染到前端:

关于用户自定义列的选择框:

样式:小编这里用的是一个弹出框里面套一个卡片的形式实现的(主要是偷懒,嘻嘻),大家可以根据自己的需要来

数据存储形式:由于数据库是不会轻易改变的,因此数据库表里的列查出来之后可以存储在redis里或存储在前端缓存里,提高程序的性能;如果列数不是很多的话,每次去查也不会影响性能,根据实际项目情况来即可

     <el-popover
          class="box"
          title="自定义表单项"
          placement="top-start"
          width="290"
          trigger="click"
        >
          <el-card class="box-card">
            <el-checkbox-group v-model="checkedList">
              <el-checkbox
                //循环我们查询到的列
                v-for="column in columnsList"
                :key="column.index"
                :label="column.remark"
                >{
   
   { column.remark }}</el-checkbox
              >
            </el-checkbox-group>
            <el-row :gutter="60" style="margin-top: 10px; margin-left: -12%">
              <el-col :span="10"
                ><el-button type="primary" @click="save()"
                  >保存</el-button
                ></el-col>
              <el-col :span="13"><el-button @click="restoreDefault">恢复默认</el-button>                    </el-col>
            </el-row>
          </el-card>
          <el-button
            slot="reference"
            type="text"
            icon="el-icon-s-tools"
            @click="handleSet"
          ></el-button>
        </el-popover>

效果:

初始化时默认勾选下面的三个,后期可以根据用户的选择进行优化,记录用户行为数据,比如大部分用户都想看某某列,初始化的时候就勾选相应的列

 我们还要做一个转换,用户选择的是中文的列,但是我们去库中查询的是数据库对应的英文名的列,也为了更方便和Table组件的prop做对应,因此需要我们拿到我们选择的中文的列名,然后遍历之前查到的所有的列后端返回的列和列的注释,最后得到英文的列名

    for(let a=0;a<this.checkedList.length;a++){
          for(let b=0;b<this.columnsList.length;b++){
            var cols = new Object();
            if(this.checkedList[a]==this.columnsList[b].remark){
              cols.columns=this.columnsList[b].columns;
              cols.remark=this.columnsList[b].remark;
              this.colsList.push(cols);
            }
          }
        }
        this.checkedListNew=[]
        this.checkedListNew= this.colsList

下面就是根据用户选择的列展示数据:

 <el-table
        :data="tableData"
        stripe
        border
        class="table"
        ref="multipleTable"
        @row-click="openDetails"
        header-cell-class-name="table-header"
        resizable=true
      >
        <template v-for="(item, index) in checkedListNew">
          //这是基础样式项
          <el-table-column
            :prop="item.columns"
            :label="item.remark"
            :key="index"
            align="center"
            show-overflow-tooltip
            width="120"
          >
          </el-table-column>
        </template>
        <el-table-column
          label="操作"
          width="200"
          align="center"
          show-overflow-tooltip
        >
          <template slot-scope="scope">
            <div
              v-if="
                ['video', 'application', 'audio', 'pdf'].includes(
                  scope.row.type
                )
              "
            >
              <el-button
                type="text"
                icon="el-icon-plus"
                class="blue"
                @click="addQuestion(scope.row)"
                >添加问题</el-button
              >
              <el-button
                type="text"
                icon="el-icon-delete"
                @click="deleteFile(scope.$index, scope.row)"
                >删除文件</el-button
              >
            </div>
            <div v-else>
              <el-button type="text" icon="el-icon-plus" disabled
                >添加问题</el-button
              >
              <el-button type="text" icon="el-icon-delete" disabled
                >删除文件</el-button
              >
            </div>
          </template>
        </el-table-column>
      </el-table>
    handleSearch(pageNum) {
      if (pageNum) {
        this.query.pageNum = 1;
      }
      this.tableData = [];
      Search(this.query).then((res) => {
        var result = res.result;
        this.dataList = result.informationData;
        this.total = result.informationCount;
        this.tableData=this.dataList
      });
    },

有没有觉得熟悉,跟没有用户自定义代码很类似。

html:

之前的列是固定的,列名可以直接写好;现在列是不固定的,是根据用户选择的,因此要根据用户选择循环出所有的列

js:

之前我们对后端返回数据进行处理,只拿需要现实的列并且赋值存到一个新定义的list里,我们做了一下优化,其实可以不用这么麻烦的,只要定义好prop(对应列内容的字段名)和我们后端返回的字段能对应上既可以展示相应的列,让它自己挑选能对应的数据,不用我们重新赋值,减少了赋值过程中可能产生的错误也避免了代码的冗余

前面只说了用户选择的列,那如果不是用户选择的呢,是固定的呢?就拿我们的操作一列来说。通过设置 Scoped slot 来自定义表头:

        <template slot-scope="scope">
            <div
              v-if="
                ['video', 'application', 'audio', 'pdf'].includes(
                  scope.row.type
                )
              "
            >
              <el-button
                type="text"
                icon="el-icon-plus"
                class="blue"
                @click="addQuestion(scope.row)"
                >添加问题</el-button
              >
              <el-button
                type="text"
                icon="el-icon-delete"
                @click="deleteFile(scope.$index, scope.row)"
                >删除文件</el-button
              >
            </div>
            <div v-else>
              <el-button type="text" icon="el-icon-plus" disabled
                >添加问题</el-button
              >
              <el-button type="text" icon="el-icon-delete" disabled
                >删除文件</el-button
              >
            </div>
          </template>

我们这里的操作还做了限制,如果列表中type的类型满足条件即操作里的按钮可用,否则不可用,那么问题来了---

思考:如果用户自定义列的时候不选择type类型,操作列的数据会怎样展示?为什么?

还有一个问题:用户自定义列,用户选择的列可能很多,可能很少,这个时候前端页面应该怎样控制列的宽度呢?

总结:小编作为一个初学者,功能可能只是实现了效果,待优化的地方还有很多,希望大家多提宝贵意见~~~

猜你喜欢

转载自blog.csdn.net/hejingfang123/article/details/120364250