• 封装缘由:
    1.减少重复工作量
    2.统一风格
    3.方便一键式更改(如果样式变更,减少工作量)

  • 封装方式:
    本次封装没有封装接口联调部分,只是基于日常使用的规范,进行常用功能的封装。

  • 封装后支持的功能或方便之处:
    1.新增了斑马线的功能。
    2.新增了省略时鼠标移入的ToolTip组件提示。
    3.通过配置可以选择隐藏某些列。
    4.数据为空时,官网的滚动条在表头下面,改为表的最下面。
    5.通过自定义列配置,实现是/否的判断。

  • 封装时遇到的问题及解决方式
    1.问题:插槽的二次封装,如果想要正常使用,必须在封装组件的时候重新定义一遍
    解决方式:封装时通过遍历传入的column,去动态生成外层的插槽。
    2.问题:封装省略时鼠标移入的ToolTip组件提示时,判断是否省略
    解决方式:给容器一个最大宽度,然后判断容器的scrollWidth是否大于设置好的最大宽度。
    3.问题:如果需要判断是否溢出的是一个插槽,该怎么去判断
    解决方式:通过columns自定义一个变量代表渲染方式,判断是插槽类型还是文字类型。如果是插槽类型,那么就外包裹;如果是文件类型,那么就内包裹。具体请详看代码。
    4.问题:初版斑马线使用计算设置的,但是使用fixed时,出现错位情况
    解决方式:使用css方式添加斑马线。
    5.问题:scroll绑定的时,如果没有赋值会报错
    解决方式:动态判断是否有没有传入scroll属性,如果没有传入,不会绑定此属性。
    6.问题:因为antd-vue层级结构已定,该如果更改滚动条位置
    解决方式:隐藏之前的dom,通过定位和更改样式,去实现一个类似的效果。

  • 完整代码


<template>
  <div :class="`my-table ${dataSource.length ? '' : 'no-data'}`">
    <a-table
      :class="hideStripe ? '' : 'stripe'"
      :rowKey="rowKey || 'id'"
      :columns="newColumns"
      :data-source="dataSource"
      :loading="loading"
      :pagination="newPagination"
      :showHeader="showHeader"
      :row-selection="rowSelection"
      :childrenColumnName="childrenColumnName"
      :expandIcon="null"
      v-on="$listeners"
      v-bind="
        $props.scroll
          ? {
              scroll: {
                ...scroll,
                x:
                  scroll.x <= 1500
                    ? dataSource.length
                      ? scroll.x
                      : null
                    : scroll.x,
              },
            }
          : {}
      "
      @expand="expand"
      :rowClassName="rowClassName"
    >

      <template slot="customTitle">
        <slot name="customTitle"></slot>
      </template>

      <template
        v-for="(column, columnIndex) in columns"
        :slot="column.scopedSlots ? column.scopedSlots.customRender : ''"
        slot-scope="text, record, index"
      >
        <div :key="columnIndex">
          <!-- 支持传入插槽的tooltip格式 -->
          <EllipsisContent
            v-if="
              column.scopedSlots
                ? column.scopedSlots.ellipsisSlot
                  ? true
                  : false
                : false
            "
            :width="getEllipsisContentWidth(column)"
            :title="
              record[column.scopedSlots ? column.scopedSlots.customRender : '']
            "
          >
            <slot
              :name="column.scopedSlots ? column.scopedSlots.customRender : ''"
              v-bind="record"
              v-bind:index="index"
            >
            </slot>
          </EllipsisContent>

          <!-- 支持传入插槽的普通渲染格式 -->
          <!-- 不支持传入插槽的tooltip格式 -->
          <!-- 通用xxxx判断(0:否,1:是) -->
          <slot
            v-else
            :name="column.scopedSlots ? column.scopedSlots.customRender : ''"
            v-bind="record"
            v-bind:index="index"
          >

            <!-- 支持传入插槽的普通渲染格式 -->
            <div
              v-if="
                column.scopedSlots
                  ? column.scopedSlots.ellipsisName
                    ? true
                    : false
                  : false
              "
              :slot="column.scopedSlots.ellipsisName"
              :key="index"
            >
              <EllipsisContent
                :width="getEllipsisContentWidth(column)"
                :title="record[column.scopedSlots.ellipsisName]"
              >
                {{ record[column.scopedSlots.ellipsisName] }}
              </EllipsisContent>
            </div>

            <!-- 通用xxxx判断(0:否,1:是) -->
            <div
              v-if="
                column.scopedSlots
                  ? column.scopedSlots.customRender === 'is'
                    ? true
                    : false
                  : false
              "
              slot="is"
            >
              {{ record[column.dataIndex] == 1 ? "是" : record[column.dataIndex] == 0 ? "否" : "" }}
            </div>
          </slot>
        </div>
      </template>
      <template
        :slot="expandedRowRender ? 'expandedRowRender' : 'undefind'"
        slot-scope="record"
      >
        <slot name="expandedRowRender" v-bind="record"></slot>
      </template>
    </a-table>

    <!-- 数据为空时显示自定义 empty -->
    <a-empty v-show="!dataSource.length" class="empty">
      <img slot="image" src="@/assets/svg/empty.svg" />
    </a-empty>
  </div>
</template>

<script>
const defaultPagination = {
  pageSize: 10,
  current: 1,
  pageSizeOptions: ["10", "20", "30", "50", "100"],
  "show-size-changer": true,
  "show-quick-jumper": true,
  showTotal(total) {
    return `共${total}条`;
  },
};

// antd 表格默认的左右padding,会影响省略时父容器的计算
const ANT_TABLE_LR_PADDING = 32;

import { Table } from "ant-design-vue";
export default {
  name: "my-table",
  data() {
    return {
      paginationData: defaultPagination,
      variableColumns: [],
      isload: true,
    };
  },
  props: {
    ...Table.props,
    expandedRowRender: {
      type: Boolean,
      default: false,
    },
    pagination: {
      default() {
        return defaultPagination;
      },
    },
    // 隐藏斑马线
    hideStripe: {
      type: Boolean,
      default: false,
    },
    // 隐藏配置过hidden为true的列
    hideConfigColumns: {
      type: Boolean,
      default: false,
    },
  },
  methods: {
    expand(expanded, record) {
      this.$emit("expand", expanded, record);
    },

    // 获取省略容器真实宽度
    getEllipsisContentWidth(column) {
      let columnWidth = parseInt(column.width || 200 + ANT_TABLE_LR_PADDING);
      return columnWidth - ANT_TABLE_LR_PADDING;
    },
  },
  computed: {
    newPagination() {
      if (!this.pagination) {
        return false;
      }

      return {
        ...defaultPagination,
        ...this.pagination,
      };
    },

    // 通过hideConfigColumns控制columns中配置过hidden为true的列隐藏
    newColumns() {
      let columns = this.columns;
      let newColumns = [];
      if (this.hideConfigColumns) {
        columns.map((column) => {
          if (!column.hidden) {
            newColumns.push(column);
          }
        });
      }
      return newColumns?.length ? newColumns : columns;
    },
  },
};
</script>

<style lang="less" scoped>
@import "@/style/antd.less";
.my-table {
  position: relative;

  // 斑马线css版样式
  .stripe {
    /deep/ .ant-table-tbody > tr:nth-child(even) > td {
      background: #f5f7fa;
    }

    /deep/ .ant-table-tbody > tr:nth-child(even) {
      &:hover td {
        background: #e6f7ff;
      }
    }
  }

  // 修改每列的样式
  /deep/ .ant-table-thead > tr > th,
  /deep/ .ant-table-tbody > tr > td {
    padding: 12px 16px;
  }

  /deep/ tr.ant-table-expanded-row,
  tr.ant-table-expanded-row:hover {
    background-color: transparent;
  }

  // 删除拓展表格无用的样式
  /deep/ tr.ant-table-expanded-row.ant-table-expanded-row-level-1 > td {
    padding: 0;
  }
  /deep/ tr.ant-table-expanded-row.ant-table-expanded-row-level-1 {
    .ant-table-tbody > tr:last-child > td {
      border-bottom: 0;
    }
  }

  /deep/ .ant-table-scroll {
    .ant-table-body {
      overflow-x: auto !important;
    }
  }
}

.no-data {
  /deep/ .ant-table-placeholder {
    display: none;
  }
  /deep/ .ant-table-body {
    height: 168px;
  }
}

.empty {
  top: 46px;
  width: 100%;
  height: 100px;
  position: absolute;
  padding-right: 14px;

  /deep/ .ant-empty-image {
    height: 40px;
    margin-top: 20px;
    margin-bottom: 8px;
  }
  /deep/ .ant-empty-description {
    color: rgba(0, 0, 0, 0.2);
  }
}
</style>

省略组件相关代码:

<template>
  <div class="">
    <a-tooltip v-if="visible" :title="title">
      <div :style="`max-width: ${width}px;`"  class="ellipsis-content" ref="content">
        <slot></slot>
      </div>
    </a-tooltip>
    <div :style="`max-width: ${width}px;`" v-else class="ellipsis-content" ref="content">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  name: "ellipsis-content",
  data() {
    return {
      visible: false,
    };
  },
  props: {
    width: {
      default: 200,
    },
    title: {
      default: "",
    },
  },
  mounted() {
    this.$nextTick(() => {
      let scrollWidth = this.$refs.content.scrollWidth;
      if (scrollWidth > this.width) {
        this.visible = true;
      }
    });
  },
};
</script>

<style lang="less" scoped>
.ellipsis-content {
  max-width: 200px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
</style>

使用代码:

<Table
      rowKey="id"
      :columns="columns"
      :data-source="dataSource"
      :pagination="pagination"
      :loading="loading"
      :row-selection="{
        selectedRowKeys: selectedRowKeys,
        onChange: handleSelectChange,
      }"
      :scroll="{ x: 3180 }"
      @change="change"
   / >

使用起来和antd官网基本相似,可能存在部分属性的差异。

在这里插入图片描述
如图,如果要配置省略时有tooltip的效果,ellipsisName代表渲染对应的字段,ellipsisSlot代表采用插槽类型。定义插槽类型时,需要写好插槽,并且插槽中渲染的内容必须是文字且不能被标签包裹。如下:

<template slot="name" slot-scope="record">
 {{ record.name }}
</template>

看到这里,可能会有童鞋说,为什么不将所需的属性存在一个变量中传入,然后动态赋值,这样做能解开上述的很多问题。我也想过,也试验过,最终的感觉就是不好用。封装起来可能比较简单,但是对于使用者来说,体验是最重要的,如果采用类似的官网的方式就行使用,对于开发着来说是最优的。

Logo

前往低代码交流专区

更多推荐