ivue-material-plus
Version:
A high quality UI components Library with Vue.js
491 lines (397 loc) • 11.1 kB
text/typescript
import { computed, ref, watch, unref, watchEffect } from 'vue';
import { useResizeObserver, useEventListener } from '@vueuse/core';
// ts
import type { Table, TableProps, RenderExpanded } from './defaults';
import type TableLayout from '../table-layout';
import type { Store } from '../store';
function useStyle(
props: TableProps,
layout: TableLayout,
store: Store,
table: Table
) {
// 滚动组件样式
const scrollbarContentStyle = {
display: 'inline-block',
verticalAlign: 'middle',
};
// data
// 拖动中
const dragging = ref<boolean>(false);
// 是否可以展开
const renderExpanded = ref<RenderExpanded | null>(null);
// 表格可滚动高度
const tableScrollHeight = ref<number>(0);
// 中间内容的高度
const bodyScrollHeight = ref<number>(0);
// 表格头部滚动高度
const headerScrollHeight = ref<number>(0);
// 表格底部滚动高度
const footerScrollHeight = ref<number>(0);
// 表格缩放
const resizeState = ref<{
width: null | number;
height: null | number;
headerHeight: null | number;
}>({
width: null,
height: null,
headerHeight: null,
});
// 表格宽度
const tableWidth = ref<number>(0);
// 是否拥有多级表头
const isGroup = ref<boolean>(false);
// computed
// table-layout布局方式
const tableLayout = computed(() => {
// 有最大高度
if (props.maxHeight) {
return 'fixed';
}
return props.tableLayout;
});
// 内容样式
const contentStyles = computed(() => {
let obj = {};
// 有固定高度
if (props.height) {
obj = {
height: !Number.isNaN(Number(props.height))
? `${props.height}px`
: props.height,
};
}
// 有最大高度
if (props.maxHeight) {
obj = {
maxHeight: !Number.isNaN(Number(props.maxHeight))
? `${props.maxHeight}px`
: props.maxHeight,
};
}
return obj;
});
// 表格内容宽度
const tableStyles = computed(() => {
const { scrollY, gutterWidth } = layout;
return {
width: layout.bodyWidth.value
? `${
(layout.bodyWidth.value as number) -
(scrollY.value ? gutterWidth : 0)
}px`
: '',
};
});
// 高度是否有变化
const shouldUpdateHeight = computed(() => {
return (
props.height ||
props.maxHeight ||
store.states.fixedColumns.value.length > 0 ||
store.states.rightFixedColumns.value.length > 0
);
});
// 提示样式
const placeholderStyle = computed(() => {
if (props.data && props.data.length) {
return null;
}
let height = '100%';
// 中间内容的高度
if (props.height && bodyScrollHeight.value) {
height = `${bodyScrollHeight.value}px`;
}
const width = tableWidth.value;
return {
width: width ? `${width}px` : '',
height,
};
});
// 内容宽度
const tableBodyStyle = computed(() => {
const { bodyWidth } = layout;
const obj: any = {
tableLayout: tableLayout.value,
};
if (bodyWidth.value) {
obj.width = `${bodyWidth.value as number}px`;
}
return obj;
});
// 滚动组件样式
const scrollbarWrapperStyle = computed(() => {
// 有设置高度
if (props.height) {
return {
height: '100%',
};
}
// 有最大高度
if (props.maxHeight) {
// number
if (!Number.isNaN(Number(props.maxHeight))) {
const headerHeight = table.refs.header?.scrollHeight || 0;
const maxHeight = props.maxHeight;
// 超过最大高度
const reachMaxHeight = tableScrollHeight.value >= Number(maxHeight);
// 表格内容高度
if (reachMaxHeight) {
return {
maxHeight: `${tableScrollHeight.value - headerHeight}px`,
};
}
}
// string
else {
return {
maxHeight: `calc(${props.maxHeight} - ${headerScrollHeight.value}px)`,
};
}
}
return {};
});
// methods
// 绑定事件
const handleBindEvents = () => {
const scrollbar = table.refs.scrollbar;
// 没有滚动条
if (!scrollbar) {
return;
}
// 有滚动条
if (scrollbar.scrollbarWrapper) {
useEventListener(
scrollbar.scrollbarWrapper,
'scroll',
handleScrollbarScroll,
{
passive: true,
}
);
}
// 列的宽度自撑开
if (props.fit) {
// 绑定resize
useResizeObserver(table.vnode.el as HTMLElement, handleResizeListener);
}
// 表格大小改变
else {
useEventListener(window, 'resize', handleResizeListener);
}
};
// 鼠标离开
const handleMouseLeave = () => {
// 清除列hover
table.store.commit('setHoverRow', null);
// 清除列hover
if (table.hoverState) {
table.hoverState = null;
}
};
// 鼠标滚轮
const handleMousewheel = (event, data) => {
const { pixelX, pixelY } = data;
// x 大于 y table横向滚动
if (Math.abs(pixelX) >= Math.abs(pixelY)) {
table.refs.bodyWrapper.scrollLeft += data.pixelX / 5;
}
};
// 拖拽显示
const handleDragVisible = (visible: boolean) => {
dragging.value = visible;
};
// 更新布局
const updateLayout = () => {
// 高度是否有变化更新高度
if (shouldUpdateHeight.value) {
layout.updateTableContentHeight();
}
// 更新列的宽度
layout.updateColumnsWidth();
// 滚动条滚动
requestAnimationFrame(handleScrollbarScroll);
};
// 表格大小改变
const handleResizeListener = () => {
// 表格是否渲染完成
if (!table.$ready) {
return;
}
// 更新布局
let shouldUpdateLayout = false;
// 当前el
const el = table.vnode.el!;
// 当前改变的值
const {
width: oldWidth,
height: oldHeight,
headerHeight: oldHeaderHeight,
} = resizeState.value;
// 当前表格宽度
const width = (tableWidth.value = el.offsetWidth);
// 有改变表格宽度
if (oldWidth !== width) {
shouldUpdateLayout = true;
}
// 监听高度变化
const height = el.offsetHeight;
if ((props.height || shouldUpdateHeight.value) && oldHeight !== height) {
shouldUpdateLayout = true;
}
// 监听表格头部是否显示
const tableHeader: HTMLElement = table.refs.header || {};
if (
tableHeader &&
props.showHeader &&
tableHeader.offsetHeight !== oldHeaderHeight
) {
shouldUpdateLayout = true;
}
// 表格可滚动高度
tableScrollHeight.value = table.refs.tableWrapper.scrollHeight;
// 表格头部滚动高度
headerScrollHeight.value = table.refs.header?.scrollHeight || 0;
// 表格底部滚动高度
footerScrollHeight.value = table.refs.footer?.scrollHeight || 0;
// 中间内容的高度 = 表格可滚动高度 - 表格头部滚动高度 - 表格底部滚动高度
bodyScrollHeight.value =
tableScrollHeight.value -
headerScrollHeight.value -
footerScrollHeight.value;
if (shouldUpdateLayout) {
resizeState.value = {
width,
height,
headerHeight: props.showHeader ? tableHeader.offsetHeight : null,
};
// 更新布局
updateLayout();
}
};
// 滚动条滚动
const handleScrollbarScroll = () => {
const scrollbar = table.refs.scrollbar;
// 没有滚动
if (!scrollbar) {
return;
}
// 是否可以滚动
if (!layout.scrollX.value) {
const scrollingNoneClass = 'is-scrolling-none';
if (!hasClass(scrollingNoneClass)) {
setTableClass(scrollingNoneClass);
}
}
const scrollContainer = scrollbar.scrollbarWrapper;
// 没有滚动条
if (!scrollContainer) {
return;
}
const { scrollLeft, offsetWidth, scrollWidth } = scrollContainer;
const { header, footer } = table.refs;
// 设置头部滚动位置
if (header) {
header.scrollLeft = scrollLeft;
}
// 设置底部滚动位置
if (footer) {
footer.scrollLeft = scrollLeft;
}
// 最大滚动位置
const maxScrollLeftPosition = scrollWidth - offsetWidth - 1;
// 最右边
if (scrollLeft >= maxScrollLeftPosition) {
setTableClass('is-scrolling-right');
}
// 滚动到最左边
else if (scrollLeft === 0) {
setTableClass('is-scrolling-left');
}
// 滚动到中间
else {
setTableClass('is-scrolling-middle');
}
};
// 是否有当前样式
const hasClass = (className: string) => {
const { tableWrapper } = table.refs;
return !!(tableWrapper && tableWrapper.classList.contains(className));
};
// 设置表格样式
const setTableClass = (className: string) => {
const { tableWrapper } = table.refs;
if (!tableWrapper) {
return;
}
const classList = Array.from(tableWrapper.classList).filter(
(item) => !item.startsWith('is-scrolling-')
);
classList.push(layout.scrollX.value ? className : 'is-scrolling-none');
tableWrapper.className = classList.join(' ');
};
// 监听数据变化
watch(
() => props.data,
(data) => {
table.store.commit('setData', data);
},
{
immediate: true,
deep: true,
}
);
// 监听行key
watch(
() => [store.states.rowKey],
([currentRowKey, rowKey]) => {
if (!unref(rowKey)) {
return;
}
// 设置当前行的key
store.setCurrentRowKey(`${currentRowKey}`);
},
{
immediate: true,
}
);
// watchEffect
// 监听高度变化设置表格高度
watchEffect(() => {
props.height && layout.setHeight(props.height);
});
// 监听高度变化设置表格最大高度
watchEffect(() => {
props.maxHeight && layout.setMaxHeight(props.maxHeight);
});
// 以通过该属性设置 Table 目前的展开行
watchEffect(() => {
if (props.expandRowKeys) {
store.setExpandRowKeysAdapter(props.expandRowKeys);
}
});
return {
// data
dragging,
renderExpanded,
resizeState,
tableWidth,
isGroup,
// computed
tableLayout,
contentStyles,
tableStyles,
placeholderStyle,
tableBodyStyle,
scrollbarWrapperStyle,
scrollbarContentStyle,
// methods
handleMouseLeave,
handleMousewheel,
handleDragVisible,
updateLayout,
handleBindEvents,
};
}
export default useStyle;