ant-design-vue Table封装
ant-design-vue Table封装ant-design-vue Table封装表格主要功能:1、表格加载(数据支持数据数组和接口访问方法)2、表格分页3、表格伸缩列4、支持单击选中行5、表格支持列显示和隐藏(同时也可以查看AVue,具有相同的功能,AVue组件已经封装,可直接使用;此处仅供需要情景使用)import { Table } from 'ant-design-vue'impor
·
ant-design-vue Table
封装表格主要功能:
1、表格加载(源数据支持数组和接口方法传递)
2、表格分页
3、表格伸缩列
4、支持单击选中行
5、表格支持列显示和隐藏(同时也可以查看AVue,具有相同的功能,AVue 组件已经封装,可直接使用;此处仅供需要情景使用)
第一步
安装支持vue的可拖动控件
npm install vue-draggable-resizable -S
第二步
利用ant和vue-draggable-resizeable封装自己的表格
Table.js如下
import { Table } from 'ant-design-vue'
import Vue from 'vue'
// 引入vue-draggable-resizable,用于表格列伸缩
import VueDraggableResizable from 'vue-draggable-resizable'
// TableOption用于表格列显示或隐藏
import TableOption from './TableOption'
// 注册组件
Vue.component('vue-draggable-resizable', VueDraggableResizable)
Vue.component('table-option', TableOption)
const componentName = 'drag-table'
const DragTable = {
name: componentName,
props: Object.assign({}, Table.props, {
// 返回 Promise<{ currPage, totalCount, list: any[] }> 的获取数据的函数,用于内部管理数据加载
data: { type: Function },
// 是否开启:单击行则选中行
selectOnClick: { type: Boolean, default: true },
// 默认翻到第 1 页
pageNum: { type: Number, default: 1 },
// 默认分页大小 10 行
pageSize: { type: Number, default: 10 },
// 是否显示分页大小切换下拉框
showSizeChanger: { type: Boolean, default: true },
// 是否显示分页器
showPagination: { type: [String, Boolean], default: 'auto' },
// 指定表格当前页数的url 例如:/users/2
pageURI: { type: Boolean, default: false },
// 是否展示序号列
showIndex: { type: Boolean, default: true },
customCell: { type: Function }
}),
data() {
return {
localLoading: false, // 加载标识
localDataSource: [], // 表格源数据
localPagination: Object.assign({}, this.pagination), // 分页对象,合并ant默认分页数据
localScroll: {},
// 表格列显隐
filterValue: [],
originColumns: []
}
},
computed: {
localKeys() {
return [...Object.keys(this.$data), ...Object.keys(this._computedWatchers), ...Object.keys(this).filter(k => k.startsWith('local'))]
},
// 处理最大显示长度后的列
localColumns(){
return this.originColumns.filter(col => !this.filterValue.includes(col.dataIndex || col.key || col.title))
},
// 表格伸缩列(该属性是ant表格中覆盖默认的 table 元素的属性components)
localComponents(){
const headerComponent = {}
headerComponent.header ={}
headerComponent.header.cell = (h, props, children) => {
const { key, ...restProps } = props
const col = this.columns.find(col => {
const k = col.dataIndex || col.key
return k === key
})
if (!col) {
return h('th', { ...restProps }, [...children])
}
const dragProps = {
key: col.dataIndex || col.key,
class: 'table-draggable-handle',
attrs: {
w: 8,
x: parseFloat(col.width),
z: 1,
axis: 'x',
draggable: true,
resizable: false,
onDragStart: (e) => {
e.stopPropagation()
}
},
on: {
dragging: (x) => {
col.width = Math.max(x, 35)
this.computeWidth()
}
}
}
const drag = h('vue-draggable-resizable', { ...dragProps })
return <th {...restProps} title={col.title} width={col.width} class="resize-table-th">
{children}
{ drag }
</th>
}
return headerComponent
}
},
watch: {
loading(val) {
this.localLoading = val
},
// 表格源数据
dataSource: {
handler(val) {
this.localDataSource = val
},
immediate: true
},
'localPagination.current'(val) {
this.pageURI && this.$router.push({
...this.$route,
params: Object.assign({}, this.$route.params, { pageNo: val })
})
},
pageNum(val) {
Object.assign(this.localPagination, { current: val })
},
pageSize(val) {
Object.assign(this.localPagination, { pageSize: val })
},
showSizeChanger(val) {
Object.assign(this.localPagination, { showSizeChanger: val })
},
scroll() {
this.calcLocalScroll()
},
columns: {
handler(val) {
const data = []
// 表格添加序号列
if (this.showIndex) {
data.push({
title: '序号',
dataIndex: 'sort',
width: 50,
customRender:
(text, record, index) => record.total
? record.total
: `${(this.localPagination.current - 1) * (this.localPagination.pageSize) + (index + 1) || (index + 1)}`
})
}
this.originColumns = data.concat(val)
// 超出后显示省略号 不支持操作列、和排序一并使用
this.originColumns.forEach((col)=>{
if(col.dataIndex || col.key) {
col.ellipsis = true
}
})
},
immediate: true
}
},
created() {
// 判断格是传进数据源还是远程数据接口方法
if (this.data) {
// 合并分页数据
const { pageNo } = this.$route.params
const localPageNum = this.pageURI ? (pageNo && parseInt(pageNo)) : this.pageNum
this.localPagination = ['auto', true].includes(this.showPagination)
? Object.assign({}, this.localPagination, {
showQuickJumper: true,
current: localPageNum,
pageSize: this.pageSize,
showSizeChanger: this.showSizeChanger,
pageSizeOptions: ['10', '20', '40', '80', '120']
})
: false
// 调用接口获得数据
this.loadData()
} else {
// 源数据传入不支持分页
this.localPagination = false
}
window.addEventListener('resize', this.calcLocalScroll)
},
mounted() {
setTimeout(() => {
this.calcLocalScroll()
this.resetColumns()
})
},
destroyed() {
window.removeEventListener('resize', this.calcLocalScroll)
},
methods: {
/**
* 表格限制最大宽度/高度计算,用于滾动显示
*/
calcLocalScroll() {
const localScroll = { ...(this.scroll || {}) }
// 根据自己的页面计算除表格外其他组件占据的高度,从而得出表格最大高度,也可自适应显示表格
const extraDis = (this.$store.getters.multiTab ? 40 : 0 ) + 56 + 104 + (this.pagination ? 6 : 0) + (this.$scopedSlots.footer ? 33 : 0)
localScroll.x = localScroll.x || this.$el.offsetWidth - 20
localScroll.y = localScroll.y || document.body.clientHeight - ((this.$el || {}).offsetTop || 128) - extraDis
this.localScroll = localScroll
// 计算表格列宽度
this.computeWidth()
},
/**
* 表格重新加载方法
*@param {object} option 对象属性: {boolean} isBackToFirstPage 如果参数为 true, 则强制刷新到第一页
* 对象属性: {boolean} isResetOption 如果参数为 true, 则重置显隐配置项
* 对象属性: {boolean} layoutTag 如果参数为 true, 则重新计算表格限制最大宽度/高度
*/
refresh({ isBackToFirstPage = false, isResetOption = false, layoutTag = false } = { isBackToFirstPage: false, isResetOption: false, layoutTag: false }) {
if(layoutTag) {
this.calcLocalScroll()
} else {
isResetOption && this.resetColumns()
isBackToFirstPage && (this.localPagination = Object.assign({}, {
current: 1,
pageSize: this.pageSize
}))
this.loadData()
}
},
/**
* 加载数据方法
* @param {{ page: number, limit: number }} pagination 分页选项器
* @param {{ [field: string]: string }} filters 过滤条件
* @param {{ field: string, order: 'asc' | 'desc' }} sorter 排序条件
*/
loadData(pagination, filters, sorter = {}) {
this.localLoading = true
const result = this.data({
page: (pagination && pagination.current) ||
this.showPagination && this.localPagination.current || this.pageNum,
limit: (pagination && pagination.pageSize) ||
this.showPagination && this.localPagination.pageSize || this.pageSize,
sidx: sorter.field,
order: sorter.order && sorter.order.slice(0, sorter.order.length - 3),
...filters
})
// 对接自己的通用数据接口需要修改下方代码中的 r.currPage, r.totalCount, r.list
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then(r => {
r = r || { currPage: 1, totalCount: 0, list: [] }
this.localPagination = this.showPagination
? Object.assign({}, this.localPagination, {
showQuickJumper: true,
current: r.currPage, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) || this.localPagination.pageSize
})
: false
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
if (r.list.length === 0 && this.showPagination && this.localPagination.current > 1) {
this.localPagination.current--
this.loadData()
return
}
// 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) {
this.localPagination.hideOnSinglePage = true
}
} catch (e) {
this.localPagination = false
}
this.localDataSource = r.list // 返回结果中的数组数据
this.localLoading = false
})
}
},
/**
* 自定义行。可以配置表格行的相关事件,此处主要定义表格单击选中行,没有复选框或者单选框得表格可以屏蔽该功能
* @param {*} record
*/
localCustomRow(record) {
const rowCustomer = this.customRow ? this.customRow(record) : {}
if (!this.selectOnClick || !this.rowSelection) {
return rowCustomer
}
if (!rowCustomer.on) {
rowCustomer.on = {}
}
// 单击选中行需要判断是单选框还是多选框,表格多选或单选框得使用会在后续发文章补充。
const selectOnClickHandler = () => {
const { type, selectedRowKeys } = this.rowSelection
if (selectedRowKeys.includes(record[this.rowKey]) && !type) {
this.rowSelection.selections.splice(this.rowSelection.selections.findIndex(r => r === record), 1)
selectedRowKeys.splice(selectedRowKeys.findIndex(r => r === record[this.rowKey]), 1)
} else if(!type) {
this.rowSelection.selections.push(record)
selectedRowKeys.push(record[this.rowKey])
} else {
this.rowSelection.selectedRow = record
selectedRowKeys.splice(0, 1, record[this.rowKey])
}
}
if (rowCustomer.on.click) {
const originalClickHandler = rowCustomer.on.click
rowCustomer.on.click = e => {
originalClickHandler(e)
selectOnClickHandler(e, record)
}
} else {
rowCustomer.on.click = selectOnClickHandler
}
return rowCustomer
},
/**
* 对表格设置width(来避免表头和内容的错位)
*/
computeWidth() {
const fullWidth = (this.localScroll.x || this.$el?.offsetWidth) - (this.rowSelection ? 60 : 0)
if(!isNaN(fullWidth) && fullWidth >= 0) {
const remain = this.originColumns.reduce((obj, col) => {
if(!this.filterValue.includes(col.dataIndex || col.key)) {
if(col.width) {
obj.colWidthSum =obj.colWidthSum - (typeof width === 'string' && col.width.endsWith('%')
? parseFloat(col.width) * fullWidth / 100
: parseFloat(col.width))
} else {
obj.noWidthColCount += 1
}
}
return obj
}, {colWidthSum: fullWidth, noWidthColCount: 0})
// 平均宽度
const averageWidth = remain.colWidthSum / remain.noWidthColCount
const lastIndex = this.originColumns.length - ( this.originColumns[this.originColumns.length-1].fixed ? 2 : 1)
// 设置默认列宽,最少显示为7个字符
// 最后一列默认不设置宽度,避免列宽改变时影响其他列
this.originColumns.forEach((col,index) => {
if(index !== lastIndex && averageWidth !== Infinity && !col.width) {
Vue.set(col, 'width', averageWidth > 150 ? averageWidth : 150)
remain.colWidthSum = remain.colWidthSum - (averageWidth > 150 ? averageWidth : 150)
} else if(index === lastIndex) {
const minWidth = col.width || 150
remain.colWidthSum = (remain.colWidthSum + (col.width || 0)).toFixed()
Vue.set(col, 'width', remain.colWidthSum < minWidth ? minWidth : undefined)
}
})
}
},
/**
* 表格列重置,主要使用在数据使用数据数组的表格
*/
resetColumns() {
this.filterValue = []
this.filterShow = !this.filterShow
}
},
// 渲染表格方法
render(h) {
const props = {}
// 表格属性
Object.keys(Table.props).forEach(k => {
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
// if(k === 'columns'){}
if (this.localKeys.includes(localKey)) {
props[k] = this[localKey]
} else if (this[k] != null) {
props[k] = this[k]
}
})
const on = { ...this.$listeners }
this.data && (on.change = this.loadData)
return (
<div class={`${componentName}-wrapper`} style="position: relative;">{[
props.showHeader && h('table-option', {
ref: 'tableOption',
style: {
float: 'right',
marginTop:'-25px',
marginRight: '5px'
},
props: {
columns: this.originColumns,
noCheckedValues: this.filterValue
},
on: {
filter: (noCheckedValues) => this.filterValue = noCheckedValues
}
}),
h('a-table', {
props,
on,
scopedSlots: { ...this.$scopedSlots }
},
Object.keys(this.$slots).map(name => (
<template slot={name}>{this.$slots[name]}</template>
))
)
]}
</div>
)
}
}
export default DragTable
TableOption.vue组件
<template>
<div>
<a-tooltip placement="leftTop" title="表格列显示配置">
<a-button @click="handleClick" class="optionBtn"><a-icon type="table" /></a-button>
</a-tooltip>
<div v-if="visible" class="table-select">
<a-checkbox
:checked="options.length === checkedValues.length"
@change="onCheckAllChange"
>
全选/反选
</a-checkbox>
<a-checkbox-group
:options="options"
v-model="checkedValues"
@change="selectChange"
/>
</div>
</div>
</template>
<script>
const componentName = 'ebig-table-option'
const TableOption= {
name: componentName,
props: {
columns: { type: Array, default: ()=>([])},
noCheckedValues: { type:Array, default: ()=>([]) }
},
data() {
return {
checkedValues: [],
visible: false
}
},
watch: {
noCheckedValues: {
handler(val) {
this.checkedValues = this.options.filter(col => !val.includes(col.value)).map(c => c.value)
},
immediate: true
}
},
computed: {
options() {
return this.columns.map(col => {
const key = col.dataIndex || col.key
return { label: col.title || col.slots.title, value: key + '' }
})
},
allKeys: vm => vm.options.map(o => o.value)
},
methods: {
handleClick() {
this.visible = !this.visible
},
onCheckAllChange(e) {
this.checkedValues = e.target.checked ? this.allKeys : []
const noCheckedValues = e.target.checked ? [] : this.allKeys
this.$emit('filter', noCheckedValues)
},
selectChange(checkedValues) {
const noCheckedValues = this.allKeys.filter(key => !checkedValues.includes(key))
this.$emit('filter', noCheckedValues)
}
}
}
export default TableOption
</script>
<style lang="less">
.optionBtn {
padding: 0 4px !important;
height: auto !important;
opacity: 0.4;
&:hover {
opacity: 1;
}
}
.ant-checkbox-group-item + .ant-checkbox-group-item {
display: block
}
.table-select {
position: absolute;
background:#fff;
border:1px solid #ecedef;
top: 5px;
right: 0;
z-index: 100000;
padding: 10px 0 10px 10px;
width: 180px;
max-height: 100%;
overflow: auto;
&::-webkit-scrollbar {
width: 5px;
}
&::-webkit-scrollbar-thumb {
border-radius: 8px;
background-color: rgb(177, 175, 175);
}
&::-webkit-scrollbar-thumb:hover {
border-radius: 10px;
background-color: #22212177;
}
}
</style>
使用
表格使用案例
<template>
<a-card style="paddingTop: 50px">
<drag-table
ref="table"
size="small"
row-key="id"
:columns="columns"
:data="loadData"
/>
</a-card>
</template>
<script>
import DragTable from './Table'
export default {
name: 'drag-table-example',
components: { DragTable },
data() {
return {
columns: [
{ dataIndex: 'name', title: '姓名' },
{ dataIndex: 'sex', title: '性别' },
{ dataIndex: 'age', title: '年龄' },
{ dataIndex: 'school', title: '学校' }
],
dataSource: []
}
},
methods: {
loadData() {
return Promise.resolve({
currPage: 1,
pageSize: 10,
totalCount: 9,
totalPage: 1,
list: [
{ id: 1, name: '张三', sex: '男', age: 18, school: '测试高级学校1'},
{ id: 2, name: '李四', sex: '女', age: 16, school: '测试高级学校2'},
{ id: 3, name: '王五', sex: '男', age: 15, school: '测试高级学校3'},
{ id: 4, name: '张红', sex: '女', age: 17, school: '测试高级学校4'},
{ id: 5, name: '陈平', sex: '男', age: 20, school: '测试高级学校5'},
]
})
}
}
}
</script>
antdv-基础表格拖拽
更多推荐
已为社区贡献2条内容
所有评论(0)