UNPKG

ivue-material-plus

Version:

A high quality UI components Library with Vue.js

335 lines (271 loc) • 8.6 kB
import { isRef, ref, nextTick } from 'vue'; import { hasOwn } from '@vue/shared'; // ts import type { Ref } from 'vue'; import type { Store } from './store'; import type { Table } from './table/defaults'; import type { TableColumnCtx } from './table-column/defaults'; import type { TableHeader } from './table-header/types'; class TableLayout { store!: Store; table!: Table; fit: boolean | undefined; showHeader: boolean | undefined; scrollX: Ref<boolean>; scrollY: Ref<boolean>; bodyWidth: Ref<null | number>; observers: TableHeader[]; height: Ref<null | number>; gutterWidth: number; constructor(options: Record<string, any>) { // 表格 scrollX this.scrollX = ref<boolean>(false); // 表格 scrollY this.scrollY = ref<boolean>(false); // 表格宽度 this.bodyWidth = ref<null | number>(null); // 表格高度 this.height = ref<null | number>(null); // 观察者列表 this.observers = []; // 排水沟宽度 this.gutterWidth = 0; // 设置内部函数 for (const name in options) { // 检测是否属性是否拥有 if (hasOwn(options, name)) { // 检查值是否为一个 ref 对象。 if (isRef(this[name])) { this[name as string].value = options[name]; } // 其他 else { this[name as string] = options[name]; } } } } // 扁平化列 getFlattenColumns(): TableColumnCtx[] { const flattenColumns = []; // 列 const columns = this.table.store.states.columns.value; columns.forEach((column) => { flattenColumns.push(column); }); return flattenColumns; } // 添加观察者 addObserver(observer: TableHeader) { this.observers.push(observer); } // 移除观察者 removeObserver(observer: TableHeader) { const index = this.observers.indexOf(observer); if (index !== -1) { this.observers.splice(index, 1); } } // 通知观察者 notifyObservers(event: string) { const observers = this.observers; observers.forEach((item) => { // 设置列的宽度 if (event === 'columns') { item.state?.handleColumnsChange(this); } // 设置滚动宽度 if (event === 'scrollable') { item.state?.handleScrollableWidthChange(this); } }); } // 更新列宽度 updateColumnsWidth() { // 列的宽度是否自撑开 const fit = this.fit; // 获取表宽度 const bodyWidth = this.table.vnode.el?.clientWidth; // 扁平化列 const flattenColumns = this.getFlattenColumns(); // 需要自动撑开的列 const flexColumns = flattenColumns.filter( (item) => typeof item.width !== 'number' ); // 最小宽度 let bodyMinWidth = 0; // 重置列的宽度 flattenColumns.forEach((item) => { // 初始化列宽度 if (typeof item.width === 'number' && item.columnWidth) item.columnWidth = 0; }); // 列的宽度是否自撑开 if (flexColumns.length > 0 && fit) { flattenColumns.forEach((item) => { // 获取所有列相加的宽度 bodyMinWidth += Number(item.width || item.minWidth || 80); }); // 不是滚动的表格 if (bodyMinWidth <= bodyWidth) { // 表格 scrollX this.scrollX.value = false; // 可以自动撑开的宽度 const totalFlexWidth = bodyWidth - bodyMinWidth; // 只有一个自动撑开的列沾满屏幕剩余宽度 if (flexColumns.length === 1) { flexColumns[0].columnWidth = Number(flexColumns[0].minWidth || 80) + totalFlexWidth; } // 多个自动撑开列 else { // 获取自动撑开列的最小宽度 数量 * 80 const allColumnWidth = flexColumns.reduce( (prev, item) => prev + Number(item.minWidth || 80), 0 ); // 可以自动撑开的宽度 / 所有列加起来的宽度 = 弹性像素 const flexWidthPerPixel = totalFlexWidth / allColumnWidth; // 不是第一个的宽度 let noneFirstWidth = 0; // 自动撑开的列 flexColumns.forEach((item, index) => { // 忽略第一个 if (index === 0) { return; } // 列的宽度 * 弹性像素 const flexWidth = Math.floor( Number(item.minWidth || 80) * flexWidthPerPixel ); // 剩余可撑开宽度 noneFirstWidth += flexWidth; // 列的宽度 + 弹性宽度 (最小80 + 弹性宽度) item.columnWidth = Number(item.minWidth || 80) + flexWidth; }); // 自动撑开的第一个宽度等于剩余的宽度 flexColumns[0].columnWidth = Number(flexColumns[0].minWidth || 80) + totalFlexWidth - noneFirstWidth; } } // 是滚动表格 else { // x轴滚动 this.scrollX.value = true; // 设置列宽度为最小宽度 flexColumns.forEach((item) => { item.columnWidth = Number(item.minWidth); }); } // 设置表格快读 this.bodyWidth.value = Math.max(bodyMinWidth, bodyWidth); // 调整表格大小 this.table.state.resizeState.value.width = this.bodyWidth.value; } // 不是自动撑开 else { flattenColumns.forEach((item) => { // 没有宽度 if (!item.width && !item.minWidth) { item.columnWidth = 80; } // 有宽度 else { item.columnWidth = Number(item.width || item.minWidth); } // 表格宽度 bodyMinWidth += item.columnWidth; }); // x轴是否可以滚动 内容的最小宽度 > 表格的宽度 this.scrollX.value = bodyMinWidth > bodyWidth; // 表格宽度 this.bodyWidth.value = bodyMinWidth; } // 通知观察者 this.notifyObservers('columns'); } // 设置高度 setHeight(value: string | number, prop = 'height') { const el = this.table.vnode.el!; // 高度转换类型 value = this.parseHeight(value); this.height.value = Number(value); // 没有节点下个线程执行 if (!el && (value || value === 0)) { return nextTick(() => { this.setHeight(value, prop); }); } // number if (typeof value === 'number') { el.style[prop] = `${value}px`; // 更新表格高度 this.updateTableContentHeight(); } // string else if (typeof value === 'string') { el.style[prop] = value; // 更新表格高度 this.updateTableContentHeight(); } } // 设置最大高度 setMaxHeight(value: string | number) { this.setHeight(value, 'max-height'); } // 设置高度 parseHeight(height: number | string) { // number if (typeof height === 'number') { return height; } // string if (typeof height === 'string') { // 是否有px if (/^\d+(?:px)?$/.test(height)) { return Number.parseInt(height, 10); } // 其他 else { return height; } } return 0; } // 更新表格高度 updateTableContentHeight() { // 更新y轴滚动高度 this.updateScrollY(); // 设置滚动高度 this.notifyObservers('scrollable'); } // 更新y轴滚动高度 updateScrollY() { const height = this.height.value; // 高度未初始化时为空 // 表格初始化后,未配置高度时,高度为0 if (!height) { return false; } // 滚动条dom const scrollbar = this.table.refs.scrollbar; // 是否渲染完成 if (this.table.vnode.el && scrollbar) { let scrollY = true; // 之前的值 const prevScrollY = this.scrollY.value; // 是否可以滚动 scrollY = scrollbar.scrollbarWrapper.scrollHeight > scrollbar.scrollbarWrapper.clientHeight; // 是否可以滚动 this.scrollY.value = scrollY; // 是否相等 是否有滚动过 return prevScrollY !== scrollY; } return false; } } export default TableLayout;