基于antd-vue Table组件的二次封装
封装缘由:1.减少重复工作量2.统一风格3.方便一键式更改(如果样式变更,减少工作量)封装方式:本次封装没有封装接口联调部分,只是基于日常使用的规范,进行常用功能的封装。封装后支持的功能或方便之处:1.新增了斑马线的功能。2.新增了省略时鼠标移入的ToolTip组件提示。3.通过配置可以选择隐藏某些列。4.数据为空时,官网的滚动条在表头下面,改为表的最下面。5.通过自定义列配置,实现是/否的判断。
-
封装缘由:
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>
看到这里,可能会有童鞋说,为什么不将所需的属性存在一个变量中传入,然后动态赋值,这样做能解开上述的很多问题。我也想过,也试验过,最终的感觉就是不好用。封装起来可能比较简单,但是对于使用者来说,体验是最重要的,如果采用类似的官网的方式就行使用,对于开发着来说是最优的。
更多推荐
所有评论(0)