Vue3 encapsulates a table component imitating ant-design-vue

Recently, it is necessary to encapsulate a simple table without using a UI framework. Since I am familiar with antd-vue, I wrote a simplified version of the Table component in the same way as antd-vue.

Technology used: VUE3, SCSS

Anyone who has used antd-vue should know that the main parameters of the table component are two: columns and dataSource, which correspond to the header information and the data information of the table respectively.

1. header data columns

The parameter data structure of the table header is as follows:

[
  {
    
     label: '姓名', key: 'name' },
  {
    
     label: '年龄', key: 'age' }
]
  • key: required, used to mark the current line
  • label: not required, the column header of the current column displays the content

Subcomponent - traversal of the header:

image.png

2. Table data dataSource

The data structure of the form data transfer parameter is as follows:

[
  {name: '张三', age: 24},
  {name: '李四', age: 45}   
]

You can see that the keys in the dataSource are the values ​​corresponding to the key fields in the columns

The corresponding source code is as follows:

image.png

The main idea is to traverse the data of each row first, and then traverse the data of the current column according to the columns in the data of each row

If you do not add 66 lines of code here, the entire table display function has been realized.

3. Add operation function

In fact, the whole function is relatively simple to implement. In retrospect, I am still not familiar with the use of vue3 slots.

See the official website for the basic use of slots

The main thing that can realize this function here is the part of the scope slot

The code still looks at lines 65 and 66 of the above picture.

But the parameter passing of columns needs to be changed:

[
  { label: '姓名', key: 'name' },
  { label: '年龄', key: 'age' },
  { label: '操作', key: 'operate', slots: 'operate'}   
]

The slots parameter indicates that the current column needs to use slots

The parent component uses the table component:

<template #operate="record">
     <a @click="handleOperate(record.rowData)">详情</a>
</template>

Notice:

  • #operate is a simplified way of writing v-slot
  • record is the parameter passed by the slot slot in the subcomponent
  • rowData corresponds to the :rowData of the Table component, that is, the content of the current tr

Next paste the usage code:

You can see that there is not much difference between using basic and antd-vue

4. Other functions

At present, the basic functions such as fixed table header, scrollable and width have been realized. If you need to add other functions, you can add various parameters on this basis.

5. All source code:

<template>
  <div class="gw-table-content">
    <!-- 需要表头固定,且表格可以滚动时的表头 -->
    <table class="gw-fixed-table" v-if="tableHeaderFixed">
      <thead
        :style="{
          backgroundColor: headerBackground
            ? 'hsla(200, 79%, 49%, 0.2)'
            : 'transparent',
          fontSize: headerFontSize || '18px'
        }"
      >
        <th style="width:70px;" v-if="serialNumber">序号</th>
        <th
          :class="{ ellipsis: headerEllipsis }"
          :width="h.width"
          v-for="(h, i) in columns"
          :key="i"
        >
          {
   
   { h.label }}
        </th>
      </thead>
    </table>
    <div class="un-fixed-table-box">
      <table class="gw-table">
        <!-- 正常的表头,无固定 -->
        <thead
          v-if="!tableHeaderFixed"
          :style="{
            backgroundColor: headerBackground
              ? 'hsla(200, 79%, 49%, 0.2)'
              : 'transparent',
            fontSize: headerFontSize || '18px'
          }"
        >
          <th style="width:70px;" v-if="serialNumber">序号</th>
          <th
            :class="{ ellipsis: headerEllipsis }"
            :width="h.width"
            v-for="(h, i) in columns"
            :key="i"
          >
            {
   
   { h.label }}
          </th>
        </thead>
        <tbody>
          <tr
            :class="{ dash: trDash }"
            v-for="(tr, ri) in dataSource"
            :key="ri"
          >
            <!-- 是否需要序号 -->
            <td style="width:70px;" v-if="serialNumber">{
   
   { ri + 1 }}</td>
            <td
              :style="{
                ...td.style,
                padding: tdPadding,
                width: td.width
              }"
              v-for="(td, key) in columns"
              :key="key"
            >
              <span :class="{ ellipsis: columnEllipsis }" :title="tr[td.key]">
                <!-- 当前列的插槽, -->
                <slot v-if="!td.slots">{
   
   { tr[td.key] }}</slot>
                <slot v-else :name="td.slots" :rowData="tr"></slot>
                <!-- tr[td.key] 这个东西为什么能获取到当前这个key值的数据没太懂。。 -->
              </span>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script lang="js">
import { defineComponent } from 'vue'

export default defineComponent({
  props: {
    columns: {
      // 表头
      required: true,
      default: []
    },
    dataSource: {
      // 表数据
      required: true,
      default: []
    },
    serialNumber: {
      // 是否需要第一列的序列号
      required: false,
      default: false
    },
    headerBackground: {
      // 是否需要表头的背景色
      required: false,
      default: false
    },
    headerFontSize: {
      // 表头字体大小
      required: false,
      default: ''
    },
    tdPadding: {
      // td的padding值
      required: false,
      default: ''
    },
    headerEllipsis: {
      // thead是否超过宽度省略
      required: false,
      default: true
    },
    columnEllipsis: {
      // td是否超过宽度省略
      required: false,
      default: true
    },
    trDash: {
      // 每行加下边框
      required: false,
      default: false
    },
    tableHeaderFixed: {
      // 表头是否固定
      required: false,
      default: false
    }
  }
})
</script>

<style lang="scss" scoped>
.gw-table-content {
  padding: 12px;
  height: calc(100% - 21px);
  .un-fixed-table-box {
    max-height: calc(100% - 44px);
    overflow-y: scroll;
  }
  .gw-table,
  .gw-fixed-table {
    width: 100%;
    max-height: 100%;
    position: relative;
    table-layout: fixed;
    thead {
      background-color: #1ba0e1;
      color: #00aaff;
      font-family: 'Adobe Heiti Std R';
      font-size: 17px;
      th {
        padding: 10px 12px;
        font-family: Adobe Heiti Std;
        font-weight: normal;
        color: #00aaff;
      }
      th.ellipsis {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }
    }
    tbody {
      tr:nth-child(odd) {
        background-color: rgba(27, 160, 225, 0.05);
      }
      tr {
        overflow: hidden;
        color: #00aaff;
        td {
          padding: 11px 12px;
          font-size: 14px;
          border-right: 2px solid transparent;

          > span {
            max-width: 100%;
            display: inline-block;
          }
          > span.ellipsis {
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
          }
        }
      }
      tr.dash {
        border-bottom: 1px dashed rgba(#1ba0e1, 0.3);
      }
    }
  }
}
</style>

Guess you like

Origin blog.csdn.net/vet_then_what/article/details/125515560