当前位置 : 主页 > 网络编程 > JavaScript >

Vue业务组件封装Table表格示例详解

来源:互联网 收集:自由互联 发布时间:2023-02-08
目录 前言 Table组件介绍 Table组件封装思路 了解element Table组件代码 Table组件如何去封装 新建LTable组件 配置文件 配置插槽 动态组件 解决插槽存在的问题 代码实现 总结 前言 这个系列主
目录
  • 前言
  • Table组件介绍
  • Table组件封装思路
    • 了解element Table组件代码
  • Table组件如何去封装
    • 新建LTable组件
    • 配置文件
    • 配置插槽
  • 动态组件
    • 解决插槽存在的问题
    • 代码实现
  • 总结

    前言

    这个系列主要是分享自己在工作中常用到的业务组件,以及如何对这些组件进行有效的封装和封装的思路。注:都是基于element ui进行二次封装。

    封装组件的基本方法就是通过props和emit进行父子组件的传值和通信。利用插槽、组件等去增加组件的可扩展性和复用性。

    Table组件介绍

    用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作。table组件常用于后台管理系统,基本上是后台管理系统中使用频率最高的组件了,一个table组件具有最基本的功能就是crud。

    基本的table

    Table组件封装思路

    了解element Table组件代码

    这里以最基本的Table代码为例进行分析:

     <template>
      <el-table
        :data="tableData"
        style="width: 100%">
        <el-table-column
          prop="date"
          label="日期"
          width="180">
        </el-table-column>
        <el-table-column
          prop="name"
          label="姓名"
          width="180">
        </el-table-column>
        <el-table-column
          prop="address"
          label="地址">
        </el-table-column>
      </el-table>
    </template>
    <script>
      export default {
        data() {
          return {
            tableData: [{
              date: '2016-05-02',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1518 弄'
            }, {
              date: '2016-05-04',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1517 弄'
            }, {
              date: '2016-05-01',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1519 弄'
            }, {
              date: '2016-05-03',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1516 弄'
            }]
          }
        }
      }
    </script>
    

    table样式

    根据基本的Table代码,我们可以知道:

    • Table渲染的数据是一个数组,数组的每一项包含了每一列的信息,然后绑定到el-table的data属性里面。
    • el-table-column为Table一行中每列的内容,一行有多少列,就写多少个el-table-column。
    • 每个el-table-column中绑定了prop、label属性,他们分别对应data数据里所渲染出来的内容和每列的表头文字,注意prop的值一定要与tableData里的对象属性相同。

    Table组件如何去封装

    通过分析Table代码我们可以把el-table-column里面绑定的属性抽离出一个配置文件,通过对配置文件的遍历得到所有el-table-column。

    配置文件代码实现

    新建LTable组件

    我们在components文件夹下新建一个LTable表示我们封装的Table组件。基于Table组件的基本代码,我们写下LTable下代码内容:

    <template>
      <div>
        <el-table :data="tableData" border style="width: 100%">
          <el-table-column type="index" align="center" width="50" v-if="showIndexColumn">
          </el-table-column>
          <el-table-column type="selection" align="center" width="55" v-if="showSelectColumn">
          </el-table-column>
          <template v-for="item in columnList">
            <el-table-column :key="item.prop" align="center" v-bind="item">
            </el-table-column>
          </template>
        </el-table>
      </div>
    </template>
    <script>
    export default {
      props: {
        columnList: {
          type: Array,
          default: () => []
        },
        showIndexColumn: {
          type: Boolean,
          default: false
        },
        showSelectColumn: {
          type: Boolean,
          default: false
        },
        requestUrl: {
          type: String,
          require: true
        }
      },
      created() {
        this.getData();
      },
      data() {
        return {
          tableData: [],
          // defaultSlotList: ["action"]
        };
      },
      computed: {
        isDefaultSlot() {
          return function(slotName) {
            return this.defaultSlotList.includes(slotName);
          };
        }
      },
      methods: {
        getData() {
          this.api.get(this.requestUrl).then(res => {
            this.tableData = res.dataList
          })
        },
        // handleEdit(row) {
        //   console.log(row);
        // },
        // handleDelete(row) {
        //   console.log(row);
        // }
      }
    };
    </script>
    

    在组件中我们需要父组件传入一个columnList,也就是前面我们说的配置文件,table的数据我们在LTable里获取,不用父组件传递过来。这样做的好处是:将通过接口获取table数据统一在LTable管理,减少父组件获取接口数据然后传递给LTable的重复代码逻辑。相应我们需要传递请求数据的接口地址。

    配置文件

    新建一个配置文件tableConfig.js,导出文件内容,然后再需要用到的页面引入。

    export const tableConfig = {
      columnList: [
        {
          label: "日期",
          prop: "date",
          sortable: true //对表格进行排序
        },
        {
          label: "图片", //文字
          prop: "imgSrc", //渲染数据对应的属性
          width: "180" //每列对应宽度
        },
        {
          label: "姓名",
          prop: "name"
        },
        {
          label: "地址",
          prop: "address"
        }
      ],
      showIndexColumn: true, //是否显示table索引
      showSelectColumn: true, //是否显示选择多行
      requestUrl: "api/getData" //接口请求地址
    };
    

    页面内容:

    <template>
      <div class="app-container">
        <l-table v-bind="tableConfig"></l-table>
      </div>
    </template>
    <script>
    import { tableConfig } from "./config/tableConfig.js";
    export default {
      components: { LTable: () => import("@/components/LTable") },
      data() {
        return {
          tableConfig
        };
      },
      mounted() {},
      methods: {}
    };
    </script>
    

    table效果

    这样,一个基本的Table封装就完成了,可以看到我们在页面中的代码是非常少的,只有引入了组件,然后将配置文件绑定到组件上就可以了。

    不过目前写的配置都是非常简单的,如果遇到复杂的表格内容,我们怎么办?

    配置插槽

    刚刚完成的小案例我们发现图片是直接显示了地址,那我们想展示图片怎么办,一般表格还会有操作列,我们怎么展示出来呢?

    这种我们不能直接渲染数据,而是需要转换,比较灵活得到我们想要的的内容就需要用到插槽了。

    首先我们需要在配置文件增加插槽的选项,我们这里取名slotName:

    columnList: [
      {
        label: "日期",
        prop: "date",
        Sortable: true
      },
      {
        label: "图片",
        prop: "imgSrc",
        width: "180",
        slotName: "img"
      },
      {
        label: "姓名",
        prop: "name"
      },
      {
        label: "地址",
        prop: "address"
      },
      {
        label: "操作",
        prop: "action",
        slotName: "action"
      }
    ],
    

    对应的LTable组件里面的渲染逻辑我们也需要调整,改为有插槽和没有插槽两种情况去渲染。表格里的插槽有些在所有表格里面都会用到比如操作,这种我们就当做默认插槽,写在LTabl里面。

    不是每个表格都需要用到插槽就是动态插槽了,放在对应的页面里。

    LTable增加插槽的代码:

    <template>
      <div>
        <el-table :data="tableData" border style="width: 100%">
          // 带有索引列
          <el-table-column type="index" align="center" width="50" v-if="showIndexColumn">
          </el-table-column>
          // 多选列
          <el-table-column type="selection" align="center" width="55" v-if="showSelectColumn">
          </el-table-column>
          <template v-for="item in columnList">
            <el-table-column :key="item.prop" v-if="item.slotName" v-bind='item' align="center">
              <template slot-scope="scope">
                <!-- 动态插槽 -->
                <slot v-if="!isDefaultSlot(item.slotName)" :name="item.slotName"
                  :row="scope.row">
                </slot>
                <!-- 默认插槽 -->
                <slot v-else name="action">
                  <el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
                  <el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
                </slot>
              </template>
            </el-table-column>
            // 直接渲染列
            <el-table-column v-else :key="item.prop" align="center" v-bind='item'>
            </el-table-column>
          </template>
        </el-table>
      </div>
    </template>
    

    页面里面动态插槽内容:

    <template>
      <div class="app-container">
        <l-table v-bind="tableConfig">
          <template #img="scope">
            <el-image
              style="width: 100px; height: 100px"
              :src="scope.row.imgSrc"
              :preview-src-list="[scope.row.imgSrc]"
              fit="cover"
              preview-teleported
            ></el-image>
          </template>
        </l-table>
      </div>
    </template>
    

    渲染出来的表格:

    带有插槽表格

    这样,我们就通过配置文件搭配插槽完成了Table组件的封装,我们可以自由灵活的显示我们表格里的内容。但同时,我们也发现,当我们LTable组件里涉及到的插槽越来越多的时候,里面的代码也会越来越多。随着我们的项目越来越复杂,需要的插槽可能有几十个甚至上百个,使用插槽的方式进行封装开始显示出了很多问题。我们该如何对这种情况进行优化呢?

    动态组件

    解决插槽存在的问题

    针对上面提出的问题,我从自动化的角度进行了思考,放弃了大量使用插槽,改用组件代替插槽的形式进行优化。将插槽的类型进行汇总,分别定义不同类型的组件去替换插槽,大大减少了由插槽产生的代码。

    代码实现

    首先在配置文件里面添加了type属性,表示它对应哪个动态组件。还添加了cb回调函数,主要处理对数据的格式进行转化。

    export const tableConfig = {
      columnList: [
        {
          type: "text",
          label: "日期",
          prop: "date",
          width: "180"
        },
        {
          type: "image",
          label: "图片",
          prop: "imgSrc",
          width: "180"
        },
        {
          type: "text",
          label: "姓名",
          prop: "name"
        },
        {
          type: "function",
          label: "性别",
          prop: "sex",
          sortable: true,
          cb: data => {
            return data == 1 ? "男" : "女";
          }
        },
        {
          type: "input",
          label: "地址",
          prop: "address"
        },
        {
          type: "slot",
          label: "操作",
          prop: "action",
          slotName: "actions"
        }
      ],
      showIndexColumn: true,
      showSelectColumn: true,
      requestUrl: "api/getData"
    };
    

    然后在LTable组件里面做自动化处理,这里需要用到Node里面的require.context方法,他会递归获取相应文件夹下的对应类型文件。

    所以我们还需要在相应文件夹下面创建相应的组件,这里我是在components文件夹下创建了control文件夹,control文件夹下放我们对应需要渲染的组件,这里我创建了四个,function、image、input、text,分别对应内容需要转化、图片展示、最基本文字、输入框。根据具体的业务创建不同的组件即可。

    control文件夹

    我们需要用require.context方法在LTable组件里拿到control文件夹下的所有组件。

    const modules = {};
    const files = require.context("../control", true, /\index.vue$/);
    files.keys().forEach(item => {
      const name = item.split("/")[1];
      modules[`com-${name}`] = files(item).default;
    });
    console.log(modules, 'modules');
    

    打印modules

    注册组件:

    components: {
      ...modules
    },
    

    我们通过动态组件的方式,根据我们要渲染的表格内容去加载不同的组件:

    <el-table-column
      v-for="item in columnList"
      :key="item.prop"
      v-bind="item"
      align="center"
    >
      <template slot-scope="scope">
        <slot
          v-if="item.type === 'slot'"
          :name="item.slotName"
          :row="scope.row"
        ></slot>
        <components
          v-else
          :row="scope.row"
          :prop="item.prop"
          :config="item"
          :is="`com-${item.type}`"
        ></components>
      </template>
    </el-table-column>
    

    页面里面的内容:

    <template>
      <div class="app-container">
        <automation-table v-bind="tableConfig">
          <template #actions="scope">
            <el-button @click="handleClick(scope.row)" type="text" size="small"
              >查看</el-button
            >
            <el-button type="text" size="small">编辑</el-button>
          </template>
        </automation-table>
      </div>
    </template>
    

    最后,我们在control文件夹下,给定义的组件写上渲染逻辑:

    function组件:

    <template>
      <div>
        {{ config.cb && config.cb(row[prop]) }}
      </div>
    </template>
    <script>
    export default {
      props: {
        row: {
          type: Object,
          require: true
        },
        prop: {
          type: String,
          require: true
        },
        config: {
          type: Object,
          require: true
        }
      },
      data() {
        return {};
      }
    };
    </script>
    <style lang="scss" scoped></style>
    

    主要调用配置文件里的cb,传入表格内容作为参数,进行我们想要的内容转化。

    image组件:

    <template>
      <div>
        <el-image
          style="width: 70px; height: 70px"
          :src="row[prop]"
          :preview-src-list="[row[prop]]"
          fit="cover"
          preview-teleported
        ></el-image>
      </div>
    </template>
    <script>
    export default {
      props: {
        row: {
          type: Object,
          require: true
        },
        prop: {
          type: String,
          require: true
        },
        config: {
          type: Object,
          require: true
        }
      },
      data() {
        return {};
      }
    };
    </script>
    <style lang="scss" scoped></style>
    

    主要将我们传入的文件地址,转化为图片展示出来。

    input组件:

    <template>
      <div>
        <el-input v-model="row[prop]" clearable></el-input>
      </div>
    </template>
    <script>
    export default {
      props: {
        row: {
          type: Object,
          require: true
        },
        prop: {
          type: String,
          require: true
        },
        config: {
          type: Object,
          require: true
        }
      },
      data() {
        return {};
      }
    };
    </script>
    <style lang="scss" scoped></style>
    

    主要将我们传入的内容绑定到input输入框里面,一般进行编辑操作。

    text组件:

    <template>
      <div>
        {{ row[prop] }}
      </div>
    </template>
    <script>
    export default {
      props: {
        row: {
          type: Object,
          require: true
        },
        prop: {
          type: String,
          require: true
        },
        config: {
          type: Object,
          require: true
        }
      },
      data() {
        return {};
      }
    };
    </script>
    <style lang="scss" scoped></style>
    

    直接展示表格内容。

    最终得到的表格效果:

    表格图片

    总结

    以上就是介绍的两种封装Table组件的方式。核心都是基于配置文件去进行渲染Table,渲染具体的内容可以使用插槽或者动态组件。

    如果项目中需要用到的插槽比较多,推荐使用动态组件的方式。

    更多关于Vue组件封装Table的资料请关注易盾网络其它相关文章!

    上一篇:vue中实现可编辑table及其中加入下拉选项
    下一篇:没有了
    网友评论