背景:用户需要实现可变列宽功能,但是项目当中UI框架绑定过深,无法通过升级UI框架的方式实现功能,需要在Ant Design Vue2.x上扩展功能。
一,依赖环境
Vue3、TypeScript、interactjs。
二、实现思路
通过interactjs结合table组件components接口使得DOM元素可以拖动,然后获取每次拖动的值进行设置表格宽度即可。
三、具体代码
import { Ref, h, onMounted, onUpdated, ref } from 'vue';
import { Table } from 'ant-design-vue';
import interact from 'interactjs';
import _ from 'lodash-es';
import { userProfile } from '/@/store/modules/user-profile';
import { useRoute } from 'vue-router';
/**
* 修改表格列宽,并且上传表格
* @param tableId 表格id 当前业务需求需要保存配置,可根据需求调整
* @param columns 表格列
* @param disable 是否能够调整表格宽度
* @returns 绑定表格的ref变量
*/
export function useChangeColumnWidth(tableId: string, columns: Ref<any[]>, disable?: boolean) {
let headTr: HTMLElement | null = null;
let colgroup: HTMLElement | null = null;
let HeaderColgroup: HTMLElement | null = null;
const tableRef = ref<InstanceType<typeof Table>>();
const route = useRoute();
const id = tableId + route.path.replaceAll('/', '-');
// ant-design 只定义表头渲染函数
const ResizeAbleTitle = (props: any, children: any) => {
const { key, ...restProps } = props;
if (disable) return h('th', { ...restProps }, children);
return h(
'th',
{
...restProps,
key,
onVnodeMounted(e: any) {
interact(e.el).resizable({
edges: { top: false, left: false, bottom: false, right: true },
listeners: {
move: function (event) {
const { width } = event.rect;
if (width <= 100) return;
// 查询当前节点索引
const i = findIndex(headTr?.children!, event.target);
if (!colgroup) initElement();
const handCol = colgroup?.children[i] as HTMLElement;
const bodyCol = HeaderColgroup?.children[i] as HTMLElement;
const params = {
width,
minWidth: 100,
};
setElementWidth(event.target, params);
handCol && setElementWidth(handCol, params);
bodyCol && setElementWidth(bodyCol, params);
// 关闭元素的点击事件
event.target.style.pointerEvents = 'none';
changeColumnsWidth(
columns?.value.find((item) => item.dataIndex === key),
width,
id,
() => {
event.target.style.pointerEvents = 'auto';
}
);
},
},
});
},
},
children
);
};
// 初始化元素
const initElement = () => {
const table = tableRef.value?.$el as HTMLDivElement;
// 可根据实际DOM结构调整,这里是需要调整宽度的元素集合
headTr = table.querySelector('.ant-table-thead > tr');
colgroup = table.querySelector('.ant-table-body > table > colgroup')!;
HeaderColgroup = table.querySelector('.ant-table-header > table > colgroup')!;
};
onMounted(() => {
initElement();
});
onUpdated(() => {
initElement();
});
return {
tableRef,
ResizeAbleTitle,
};
}
// 设置元素宽度
function setElementWidth(el: HTMLElement, { width, minWidth }: any) {
Object.assign(el.style, {
width: `${width}px`,
minWidth: `${minWidth}px`,
// height: `${event.rect.height}px`,
// transform: `translate(${x}px, ${y}px)`,
});
}
//查找元素所在位置
function findIndex(listNodes: HTMLCollection, el: HTMLElement) {
for (let i = 0; i < listNodes.length; i++) {
if (listNodes[i] === el) {
return i;
}
}
return -1;
}
// 收集用户习惯,防抖处理
const changeColumnsWidth = _.debounce(
(col: any, width: number, id: string, callbackFn?: () => void) => {
col.width = width;
// 根据业务需求自行调整,这里可以上传到服务器保存用户习惯
// userProfile.setUserTableConfig({
// resourceId: id,
// tableConfig: JSON.stringify({ [col.dataIndex]: { width } }),
// });
callbackFn && callbackFn();
},
500
);
四、使用方法
<template>
<Table ref="tableRef" :components="components"
:columns="newColumns.length ? newColumns : undefined">
.....
</Table>
</template>
<script lang="ts">
import { PropType, computed, ref, watch, defineComponent } from 'vue';
import { Table } from 'ant-design-vue'
import { useChangeColumnWidth } from './hook/useChangeColumnWidth';
export default defineComponent({
components: { Table },
props: {
columns: {
type: Array as PropType<any[]>,
default() {
return [] as PropType<any[]>
}
},
tableId: { // 表格的uuid唯一标识
type: String as PropType<string>,
default: ""
},
noChangeWidth: { // 禁止修改列宽
type: Boolean,
default: false
},
scroll: {
type: Object as PropType<{ x: number, y: number }>,
}
},
setup(props) {
const filterList = ['operation', 'index']
const newColumns = ref<any[]>([...formatColumn(props.columns)])
watch(() => props.columns, (value: any[]) => {
newColumns.value = formatColumn(value)
})
const { tableRef, ResizeAbleTitle } = useChangeColumnWidth(props.tableId, newColumns, props.noChangeWidth);
const components = {
header: {
cell: ResizeAbleTitle,
},
};
return {
newColumns,
tableRef,
components,
}
}
})
</script>
<style scoped>
</style>
见谅,时间关系实现过程没有写的很详细,后续有时间再补