| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import { defineComponent, h, ref, onMounted, onUnmounted, PropType, watch } from 'vue'
- import ResizeObserver from 'resize-observer-polyfill'
- import { useDrag } from './drag'
- import './index.less'
- interface CatTableColumn {
- prop: string;
- label: string;
- width: number;
- }
- /**
- * 通用表格组件(开发中)
- */
- export default defineComponent({
- props: {
- columns: {
- type: Array as PropType<CatTableColumn[]>,
- default: () => ([])
- },
- rows: {
- default: () => ([])
- }
- },
- setup(props) {
- const { dragStart, drag, dragEnd } = useDrag();
- const tableElement = ref<HTMLDivElement>();
- const headerElement = ref<HTMLTableElement>();
- const bodyElement = ref<HTMLTableElement>();
- const headerColumns = ref<CatTableColumn[]>([]);
- const scrollbarWidth = ref(0); // 滚动条宽度
- const tableStyle = () => ({
- width: headerColumns.value.reduce((pre, cur) => pre += cur.width, 0) + 'px'
- })
- const renderColGroup = () => h('colgroup', headerColumns.value.map((column) => h('col', { width: column.width })));
- const renderHeader = () => h('div', {
- class: 'cat-table__wrapper'
- }, [
- h('table', {
- ref: headerElement,
- class: 'cat-table__header',
- cellspacing: 0,
- cellpadding: 0,
- style: tableStyle(),
- }, [
- renderColGroup(),
- h('thead', h('tr', headerColumns.value.map((column) => h('th', h('div', { class: 'cell' }, column.label)))))
- ])
- ])
- const renderBody = () => h('div', {
- class: 'cat-table__wrapper'
- }, [
- h('table', {
- ref: bodyElement,
- class: 'cat-table__body',
- cellspacing: 0,
- cellpadding: 0,
- style: tableStyle(),
- }, [
- renderColGroup(),
- h('tbody', props.rows.map((row) => {
- return h('tr', headerColumns.value.map((column) => h('td', h('div', { class: 'cell' }, row[column.prop]))))
- }))
- ]),
- ...scrollbarWidth.value > 0 ? [renderScrollbar()] : [],
- ])
- const renderScrollbar = () => h('div', {
- class: 'cat-scrollbar',
- }, h('div', {
- class: 'cat-scrollbar__thumb',
- draggable: true,
- ondragstart: dragStart,
- ondrag: drag,
- ondragend: dragEnd,
- style: {
- width: scrollbarWidth.value + 'px'
- },
- }))
- const columnResize = (() => {
- let timer = 0;
- let prevWidth = 0; // 记录上次宽度
- return () => {
- const table = tableElement.value;
- if (table) {
- const miniWidth = 48; // 限制列宽最小宽度
- headerColumns.value = [];
- // 计算出列宽的组合数据
- const colgroup = props.columns.reduce((pre, cur) => {
- if (cur.width > miniWidth) {
- pre.width -= cur.width;
- pre.length -= 1;
- }
- return pre;
- }, {
- width: table.clientWidth,
- length: props.columns.length,
- })
- // 列宽平均分配
- props.columns.forEach((column) => {
- const item = { ...column };
- if (!item.width) {
- const width = Math.floor(colgroup.width / colgroup.length);
- item.width = width > miniWidth ? width : miniWidth;
- }
- headerColumns.value.push(item)
- })
- // 判断是否显示滚动条
- if (colgroup.width !== prevWidth) {
- clearTimeout(timer);
- scrollbarWidth.value = 0;
- timer = window.setTimeout(() => {
- prevWidth = colgroup.width;
- const body = bodyElement.value;
- if (body) {
- const ratio = table.clientWidth / body.clientWidth;
- if (ratio < 1) {
- scrollbarWidth.value = table.clientWidth * ratio;
- }
- }
- }, 200)
- }
- }
- }
- })()
- watch(() => props.columns, () => columnResize());
- onMounted(() => {
- const el = tableElement.value;
- if (el) {
- // 监听元素变化
- const resizeObserver = new ResizeObserver(columnResize);
- resizeObserver.observe(el);
- onUnmounted(() => {
- resizeObserver.unobserve(el);
- })
- }
- })
- return () => h('div', {
- ref: tableElement,
- class: 'cat-table'
- }, [
- ...props.columns.length ? [renderHeader()] : [],
- ...props.rows.length ? [renderBody()] : [],
- ])
- }
- })
|