@fesjs/fes-design
Version:
fes-design for PC
222 lines (216 loc) • 7.61 kB
JavaScript
import { ref, computed, onDeactivated, onActivated, watch, nextTick } from 'vue';
import { isUndefined, isEqual } from 'lodash-es';
import useResize from '../_util/use/useResize';
/**
* 更新列的宽度
* 列最小宽度为80,如果设定了width则使用设定的width作为宽度,没有设定宽度的列平分剩余的宽度(容器的宽度减去已使用的宽度)
*/
function useTableLayout(_ref) {
let {
props,
wrapperRef,
headerWrapperRef,
bodyWrapperRef,
bodyTableRef,
columns,
showData
} = _ref;
const bodyWidth = ref(0);
const widthMap = ref({});
const isScrollX = ref(false);
const isScrollY = ref(false);
const bodyHeight = ref(0);
// 兼容 windows 浏览器滚动条导致高度有小数的场景
const propHeight = computed(() => Math.floor(props.height));
const isWidthAuto = computed(() => {
return isUndefined(props.height) && props.layout === 'auto';
});
const isWatchX = ref(true);
const min = 100;
const computeY = () => {
// 第一次渲染时会出现 bodyWrapperHeight = 0,必须再nextTick
nextTick(() => {
// 需要在宽度分配完,重新渲染后,此时table已经按照期望正常渲染,此时的高度才是最终高度
const $wrapper = wrapperRef.value;
const $bodyWrapper = bodyWrapperRef.value;
if ($wrapper && $bodyWrapper) {
if (propHeight.value) {
const $headerWrapper = props.showHeader ? headerWrapperRef.value : {
offsetHeight: 0
};
const headerWrapperHeight = $headerWrapper.offsetHeight;
let remainBodyHeight = propHeight.value - headerWrapperHeight;
if (props.bordered) {
remainBodyHeight -= 2;
}
const bodyWrapperHeight = $bodyWrapper.offsetHeight;
bodyHeight.value = remainBodyHeight;
// 渲染后重新执行,会出现 remainBodyHeight === bodyWrapperHeight 的情况
if (remainBodyHeight <= bodyWrapperHeight) {
isScrollY.value = true;
} else {
isScrollY.value = false;
}
} else {
isScrollY.value = false;
}
}
});
};
const computeX = () => {
if (!isWatchX.value) {
return;
}
const $wrapper = wrapperRef.value;
if (!$wrapper) {
return;
}
if (isWidthAuto.value) {
columns.value.forEach(column => {
const widthObj = {
id: column.id
};
const width = column.props.width;
const minWidth = column.props.minWidth;
if (width) {
widthObj.width = width;
} else if (minWidth) {
widthObj.minWidth = minWidth;
} else if (column.props.type === 'selection' || column.props.type === 'expand') {
widthObj.minWidth = min;
}
if (!isEqual(widthMap.value[column.id], widthObj)) {
widthMap.value[column.id] = widthObj;
}
});
const $bodyTable = bodyTableRef.value;
if (!$bodyTable) {
return;
}
isScrollX.value = $bodyTable.offsetWidth > (props.bordered ? $wrapper.offsetWidth - 2 : $wrapper.offsetWidth);
} else {
const _wrapperWidth = $wrapper.offsetWidth;
const wrapperWidth = props.bordered ? _wrapperWidth - 2 : _wrapperWidth;
let bodyMinWidth = 0;
const newWidthList = {};
columns.value.forEach(column => {
const widthObj = {
id: column.id,
origin: {}
};
const width = column.props.width;
const minWidth = column.props.minWidth;
if (width || minWidth) {
// 用户设置的宽度优先级最高
widthObj.origin = {
minWidth,
width
};
} else if (column.props.type === 'selection' || column.props.type === 'expand') {
widthObj.origin = {
width: min
};
} else {
widthObj.origin = {
minWidth: min
};
}
newWidthList[column.id] = widthObj;
});
Object.values(newWidthList).forEach(widthObj => {
var _ref2, _widthObj$origin$widt;
bodyMinWidth += (_ref2 = (_widthObj$origin$widt = widthObj.origin.width) !== null && _widthObj$origin$widt !== void 0 ? _widthObj$origin$widt : widthObj.origin.minWidth) !== null && _ref2 !== void 0 ? _ref2 : min;
});
if (bodyMinWidth < wrapperWidth) {
isScrollX.value = false;
bodyWidth.value = wrapperWidth;
const additionalWidth = wrapperWidth - bodyMinWidth;
const hasMinItems = Object.values(newWidthList).filter(item => !item.origin.width);
const hasWidthItems = Object.values(newWidthList).filter(item => item.origin.width);
let addedWidth = 0;
hasMinItems.forEach((item, index) => {
const origin = newWidthList[item.id].origin;
if (index !== hasMinItems.length - 1) {
const widthObj = {
id: item.id,
width: origin.minWidth + Math.ceil(additionalWidth / hasMinItems.length)
};
if (!isEqual(widthObj, widthMap.value[item.id])) {
widthMap.value[item.id] = widthObj;
}
addedWidth += Math.ceil(additionalWidth / hasMinItems.length);
} else {
const widthObj = {
id: item.id,
width: origin.minWidth + additionalWidth - addedWidth
};
if (!isEqual(widthObj, widthMap.value[item.id])) {
widthMap.value[item.id] = widthObj;
}
}
});
hasWidthItems.forEach(item => {
const origin = newWidthList[item.id].origin;
const widthObj = {
id: item.id,
width: origin.width
};
if (!isEqual(widthObj, widthMap.value[item.id])) {
widthMap.value[item.id] = widthObj;
}
});
} else {
isScrollX.value = true;
bodyWidth.value = bodyMinWidth;
columns.value.forEach(column => {
var _origin$width;
const origin = newWidthList[column.id].origin;
const widthObj = {
id: column.id,
width: (_origin$width = origin.width) !== null && _origin$width !== void 0 ? _origin$width : origin.minWidth
};
if (!isEqual(widthObj, widthMap.value[column.id])) {
widthMap.value[column.id] = widthObj;
}
});
}
}
};
const watchResizeDisableRef = ref(false);
onDeactivated(() => {
watchResizeDisableRef.value = true;
});
onActivated(() => {
watchResizeDisableRef.value = false;
});
// 检测Table宽度和高度变化,计算内容宽度和高度
useResize(wrapperRef, () => {
computeX();
computeY();
}, watchResizeDisableRef);
useResize(bodyTableRef, () => {
computeX();
}, computed(() => {
return watchResizeDisableRef.value || !isWidthAuto.value;
}));
watch([columns, wrapperRef, () => props.bordered, isWidthAuto], () => {
computeX();
});
watch([widthMap, propHeight, () => props.showHeader, () => props.bordered, wrapperRef, bodyWrapperRef, headerWrapperRef], () => {
nextTick(computeY);
});
// 数据变化的时候,要重置滚动的变量,不然数据不足的时候还是会用之前旧的 bodyWrapperRef
watch(() => showData.value.length, () => {
isScrollY.value = false;
nextTick(computeY);
});
return {
widthMap,
bodyWidth,
bodyHeight,
isScrollX,
isScrollY,
isWatchX
};
}
export { useTableLayout as default };