diff --git a/components/table/components/header.tsx b/components/table/components/header.tsx index 7b756ce2..056d5e82 100644 --- a/components/table/components/header.tsx +++ b/components/table/components/header.tsx @@ -1,11 +1,6 @@ -import { - type ComponentObjectPropsOptions, - Fragment, - type PropType, - computed, - defineComponent, - inject, -} from 'vue'; +import type { ComponentObjectPropsOptions, PropType } from 'vue'; +import { Fragment, computed, defineComponent, inject } from 'vue'; +import { isUndefined } from 'lodash-es'; import FCheckbox from '../../checkbox/checkbox.vue'; import { provideKey } from '../const'; import useResize from '../useResize'; @@ -33,12 +28,17 @@ export default defineComponent({ prefixCls, sortState, layout, + rootProps, } = inject(provideKey); const { current, onMousedown } = useResize( props.columns, layout.widthMap, handleHeaderResize, + layout.isWatchX, + computed(() => { + return isUndefined(rootProps.height) && rootProps.layout === 'auto'; + }), ); /** @@ -144,9 +144,9 @@ export default defineComponent({ diff --git a/components/table/table.tsx b/components/table/table.tsx index 9fda68c2..0161bd10 100644 --- a/components/table/table.tsx +++ b/components/table/table.tsx @@ -145,7 +145,6 @@ export default defineComponent({ clearSelect, wrapperRef, wrapperClass, - layout, columns, rootProps, toggleRowExpend, @@ -188,9 +187,6 @@ export default defineComponent({ ); const render = () => { - if (!layout.initRef.value) { - return; - } return ( <> {composed.value && rootProps.showHeader && ( @@ -198,13 +194,13 @@ export default defineComponent({ )} {rootProps.virtualScroll && showData.value.length ? ( - + ) : ( - + )} {showData.value.length === 0 && } diff --git a/components/table/useResize.ts b/components/table/useResize.ts index 50dd1cdf..095558a6 100644 --- a/components/table/useResize.ts +++ b/components/table/useResize.ts @@ -1,4 +1,5 @@ -import { type Ref, ref } from 'vue'; +import type { ComputedRef, Ref } from 'vue'; +import { ref } from 'vue'; import { cloneDeep } from 'lodash-es'; import { useEventListener } from '@vueuse/core'; import { depx } from '../_util/utils'; @@ -16,6 +17,8 @@ export default ( columns: ColumnInst[], widthMap: Ref>, handleHeaderResize: ReturnType['handleHeaderResize'], + isWatchX: Ref, + isWidthAuto: ComputedRef, ) => { const current = ref<{ id: number; @@ -24,6 +27,8 @@ export default ( width: number; }>(null); + let _widthMap: Record = null; + const onMousedown = ( column: ColumnInst, columnIndex: number, @@ -37,36 +42,33 @@ export default ( (event.target as HTMLElement).parentElement.offsetWidth, ), }; + _widthMap = cloneDeep(widthMap.value); + isWatchX.value = false; }; const onMousemove = (event: MouseEvent) => { if (!current.value) { return; } - const _widthMap = cloneDeep(widthMap.value); - const leftColumns = columns - .slice(0, current.value.columnIndex) - .filter((col) => { - return !_widthMap[col.id].width; - }); - const rightColumns = columns - .slice(current.value.columnIndex + 1) - .filter((col) => { - return !_widthMap[col.id].width; + if (!_widthMap) { + return; + } + const currentClientX = event.clientX; + const offset = currentClientX - current.value.clientX; + + if (!isWidthAuto.value) { + const rightColumns = columns.slice(current.value.columnIndex + 1); + widthMap.value[current.value.id].width = offset + _widthMap[current.value.id].width; + rightColumns.forEach((col) => { + widthMap.value[col.id].width = (-offset / rightColumns.length) + _widthMap[col.id].width; }); - const offsetX - = ((event.clientX - current.value.clientX) - * (leftColumns.length + rightColumns.length)) - / rightColumns.length; - const width = current.value.width + offsetX; - const currentColumn = columns[current.value.columnIndex]; - if ( - currentColumn.props.minWidth - && width >= currentColumn.props.minWidth - ) { - _widthMap[current.value.id].width = width; - _widthMap[current.value.id].minWidth = width; - widthMap.value = _widthMap; + } else { + widthMap.value[current.value.id] = { + ..._widthMap[current.value.id], + width: offset + current.value.width, + minWidth: offset + current.value.width, + maxWidth: offset + current.value.width, + }; } }; @@ -104,6 +106,8 @@ export default ( // reset current current.value = null; + _widthMap = null; + isWatchX.value = true; }; useEventListener(window.document, 'mousemove', onMousemove); diff --git a/components/table/useTableLayout.ts b/components/table/useTableLayout.ts index e30d0671..a9e88921 100644 --- a/components/table/useTableLayout.ts +++ b/components/table/useTableLayout.ts @@ -18,6 +18,11 @@ export interface WidthItem { width?: number; minWidth?: number; maxWidth?: number; + origin?: { + width?: number; + minWidth?: number; + maxWidth?: number; + }; } /** @@ -46,7 +51,6 @@ export default function useTableLayout({ const isScrollX = ref(false); const isScrollY = ref(false); const bodyHeight = ref(0); - const initRef = ref(false); // 兼容 windows 浏览器滚动条导致高度有小数的场景 const propHeight = computed(() => Math.floor(props.height)); @@ -55,7 +59,9 @@ export default function useTableLayout({ return isUndefined(props.height) && props.layout === 'auto'; }); - const min = 80; + const isWatchX = ref(true); + + const min = 100; const computeY = () => { // 第一次渲染时会出现 bodyWrapperHeight = 0,必须再nextTick @@ -89,11 +95,35 @@ export default function useTableLayout({ }; const computeX = () => { + if (!isWatchX.value) { + return; + } const $wrapper = wrapperRef.value; if (!$wrapper) { return; } + if (isWidthAuto.value) { + columns.value.forEach((column) => { + const widthObj: WidthItem = { + 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; @@ -106,50 +136,94 @@ export default function useTableLayout({ : _wrapperWidth; let bodyMinWidth = 0; - Object.keys(widthMap.value).forEach((id) => { - const widthObj = widthMap.value[id]; - bodyMinWidth += widthObj.width ?? widthObj.minWidth ?? min; + const newWidthList: Record = {}; + columns.value.forEach((column) => { + const widthObj: WidthItem = { + 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) => { + bodyMinWidth += widthObj.origin.width ?? widthObj.origin.minWidth ?? 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) => { + const origin = newWidthList[column.id].origin; + const widthObj = { + id: column.id, + width: origin.width ?? origin.minWidth, + }; + if (!isEqual(widthObj, widthMap.value[column.id])) { + widthMap.value[column.id] = widthObj; + } + }); } } }; - const computeColumnWidth = () => { - const newWidthList: Record = {}; - columns.value.forEach((column) => { - const widthObj: WidthItem = { - id: column.id, - }; - const width = column.props.width; - const minWidth = column.props.minWidth; - if (width || minWidth) { - // 用户设置的宽度优先级最高 - widthObj.width = width; - widthObj.minWidth = minWidth; - } else if ( - column.props.type === 'selection' - || column.props.type === 'expand' - ) { - widthObj.width = min; - } - newWidthList[column.id] = widthObj; - }); - // 如果值一样则没必要再次渲染,可减少一次多余渲染 - if (!isEqual(newWidthList, widthMap.value)) { - widthMap.value = newWidthList; - } - nextTick(() => { - initRef.value = true; - }); - }; - const watchResizeDisableRef = ref(false); onDeactivated(() => { @@ -176,10 +250,9 @@ export default function useTableLayout({ return watchResizeDisableRef.value || !isWidthAuto.value; })); - // 根据列数据,计算列宽度 - watch([columns, wrapperRef], computeColumnWidth); - - watch([widthMap, wrapperRef, () => props.bordered], computeX); + watch([columns, wrapperRef, () => props.bordered, isWidthAuto], () => { + computeX(); + }); watch( [ @@ -211,6 +284,6 @@ export default function useTableLayout({ bodyHeight, isScrollX, isScrollY, - initRef, + isWatchX, }; } diff --git a/components/table/useTableStyle.ts b/components/table/useTableStyle.ts index bb5d9953..200b0b73 100644 --- a/components/table/useTableStyle.ts +++ b/components/table/useTableStyle.ts @@ -239,12 +239,12 @@ export default ({ return [ typeof colClassName === 'function' ? colClassName({ - row, - column, - rowIndex, - columnIndex, - cellValue, - }) + row, + column, + rowIndex, + columnIndex, + cellValue, + }) : colClassName, ]; }; diff --git a/package.json b/package.json index dd2e8fc3..ae8fd6f1 100644 --- a/package.json +++ b/package.json @@ -1,171 +1,171 @@ { - "name": "@fesjs/fes-design", - "type": "module", - "version": "0.8.80", - "packageManager": "pnpm@9.15.2", - "description": "fes-design for PC", - "author": "winixt", - "license": "MIT", - "homepage": "https://github.com/WeBankFinTech/fes-design#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/WeBankFinTech/fes-design.git" - }, - "bugs": { - "url": "https://github.com/WeBankFinTech/fes-design/issues" - }, - "keywords": [ - "fes", - "fes-ui", - "fes-design" - ], - "sideEffects": [ - "./dist/*", - "**/style/*" - ], - "exports": { - ".": "./es/index.js", - "./es/*": "./es/*", - "./dist/*": "./dist/*", - "./icon": "./es/icon/index.js" - }, - "main": "dist/fes-design.js", - "module": "es/index.js", - "types": "./es/index.d.ts", - "files": [ - "dist", - "es", - "icon", - "scripts", - "types" - ], - "scripts": { - "prepare": "husky install", - "docs:dev": "node scripts/genComponentDoc.js && vitepress dev docs", - "docs:build": "NODE_ENV=production node scripts/genComponentDoc.js && vitepress build docs", - "docs:preview": "vitepress preview docs --port 8999", - "test": "jest", - "test:watch": "jest --watch", - "gen:component": "node scripts/createComponent.js", - "gen:icon": "node scripts/genIcons.js", - "build:version": "node scripts/genVersion.js", - "build:esm": "node scripts/build-esm.js", - "build:esm-browser": "node scripts/build-browser.js", - "build:umd": "node scripts/build-umd.js", - "build:style": "node scripts/build-style.js", - "build:type": "node scripts/build-types.js", - "build:icon": "node scripts/build-icon.js", - "build": "npm run build:version && npm run build:esm && npm run build:type && cp -rf es/icon . && npm run build:esm-browser && npm run build:umd && npm run build:style && npm run build:icon", - "release": "node scripts/release.js", - "lint-staged": "lint-staged --allow-empty", - "commitlint": "commitlint --config commitlint.config.cjs -e -V", - "lint:script": "eslint ./components --fix", - "lint:style": "stylelint 'components/**/*.less' --fix", - "lint:docs-script": "eslint ./docs/.vitepress/components --fix", - "commit": "git cz", - "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" - }, - "peerDependencies": { - "vue": "^3.2.24" - }, - "dependencies": { - "@babel/runtime": "^7.16.3", - "@floating-ui/dom": "^1.2.9", - "@juggle/resize-observer": "^3.3.1", - "@types/lodash-es": "^4.17.5", - "@vue/shared": "^3.2.24", - "@vueuse/core": "^9.6.0", - "async-validator": "^4.2.5", - "csstype": "^3.0.10", - "date-fns": "^2.28.0", - "lodash-es": "^4.17.21", - "stickybits": "^3.7.9", - "virtua": "^0.40.3" - }, - "devDependencies": { - "@antfu/eslint-config": "^2.15.0", - "@babel/core": "^7.16.0", - "@babel/plugin-syntax-jsx": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-typescript": "^7.16.7", - "@betit/rollup-plugin-rename-extensions": "^0.1.0", - "@commitlint/cli": "^13.2.1", - "@commitlint/config-conventional": "^13.2.0", - "@docsearch/css": "^3.0.0", - "@docsearch/js": "^3.0.0", - "@rollup/plugin-babel": "^5.3.0", - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^13.0.6", - "@rollup/plugin-replace": "^5.0.1", - "@types/jest": "^27.4.1", - "@vite-pwa/vitepress": "^0.3.0", - "@vitejs/plugin-vue-jsx": "^1.3.0", - "@vue/babel-plugin-jsx": "^1.1.1", - "@vue/repl": "^3.4.0", - "@vue/test-utils": "2.0.0-rc.17", - "@vue/vue3-jest": "27.0.0-alpha.4", - "@webank/eslint-config-ts": "^1.2.0", - "autoprefixer": "^10.4.0", - "babel-jest": "^27.5.1", - "body-scroll-lock": "4.0.0-beta.0", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cheap-watch": "^1.0.4", - "commitizen": "^4.2.4", - "conventional-changelog-cli": "^2.1.1", - "csso": "^4.2.0", - "cz-conventional-changelog": "^3.3.0", - "enquirer": "^2.3.6", - "eslint": "^9.1.1", - "eslint-import-resolver-custom-alias": "^1.3.2", - "execa": "^4.1.0", - "fast-glob": "^3.2.7", - "fs-extra": "^10.0.0", - "husky": "^7.0.4", - "jest": "^27.5.1", - "jest-canvas-mock": "^2.3.1", - "jest-transform-stub": "^2.0.0", - "less": "^4.1.2", - "less-loader": "^10.2.0", - "lint-staged": "^11.2.6", - "minimist": "^1.2.5", - "postcss": "^8.4.35", - "prettier": "^2.5.1", - "rollup": "^2.60.2", - "rollup-plugin-postcss": "^4.0.2", - "rollup-plugin-vue": "^6.0.0", - "semver": "^7.3.5", - "shiki": "^1.1.0", - "stylelint": "^14.1.0", - "stylelint-config-prettier": "^9.0.3", - "stylelint-config-rational-order": "^0.1.2", - "stylelint-config-standard": "^24.0.0", - "stylelint-declaration-block-no-ignored-properties": "^2.5.0", - "stylelint-order": "^5.0.0", - "svgo": "^2.8.0", - "terser": "^5.10.0", - "ts-jest": "^27.1.3", - "ts-morph": "^17.0.1", - "typescript": "^4.9.5", - "vitepress": "^1.3.4", - "vue": "^3.3.4" - }, - "lint-staged": { - "components/**/*.{js,ts,vue,jsx}": [ - "eslint --fix" + "name": "@fesjs/fes-design", + "type": "module", + "version": "0.8.80", + "packageManager": "pnpm@9.15.2", + "description": "fes-design for PC", + "author": "winixt", + "license": "MIT", + "homepage": "https://github.com/WeBankFinTech/fes-design#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/WeBankFinTech/fes-design.git" + }, + "bugs": { + "url": "https://github.com/WeBankFinTech/fes-design/issues" + }, + "keywords": [ + "fes", + "fes-ui", + "fes-design" ], - "components/**/*.{less,css}": [ - "stylelint --fix" - ] - }, - "config": { - "commitizen": { - "path": "./node_modules/cz-conventional-changelog" + "sideEffects": [ + "./dist/*", + "**/style/*" + ], + "exports": { + ".": "./es/index.js", + "./es/*": "./es/*", + "./dist/*": "./dist/*", + "./icon": "./es/icon/index.js" + }, + "main": "dist/fes-design.js", + "module": "es/index.js", + "types": "./es/index.d.ts", + "files": [ + "dist", + "es", + "icon", + "scripts", + "types" + ], + "scripts": { + "prepare": "husky install", + "docs:dev": "node scripts/genComponentDoc.js && vitepress dev docs", + "docs:build": "NODE_ENV=production node scripts/genComponentDoc.js && vitepress build docs", + "docs:preview": "vitepress preview docs --port 8999", + "test": "jest", + "test:watch": "jest --watch", + "gen:component": "node scripts/createComponent.js", + "gen:icon": "node scripts/genIcons.js", + "build:version": "node scripts/genVersion.js", + "build:esm": "node scripts/build-esm.js", + "build:esm-browser": "node scripts/build-browser.js", + "build:umd": "node scripts/build-umd.js", + "build:style": "node scripts/build-style.js", + "build:type": "node scripts/build-types.js", + "build:icon": "node scripts/build-icon.js", + "build": "npm run build:version && npm run build:esm && npm run build:type && cp -rf es/icon . && npm run build:esm-browser && npm run build:umd && npm run build:style && npm run build:icon", + "release": "node scripts/release.js", + "lint-staged": "lint-staged --allow-empty", + "commitlint": "commitlint --config commitlint.config.cjs -e -V", + "lint:script": "eslint ./components --fix", + "lint:style": "stylelint 'components/**/*.less' --fix", + "lint:docs-script": "eslint ./docs/.vitepress/components --fix", + "commit": "git cz", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" + }, + "peerDependencies": { + "vue": "^3.2.24" + }, + "dependencies": { + "@babel/runtime": "^7.16.3", + "@floating-ui/dom": "^1.2.9", + "@juggle/resize-observer": "^3.3.1", + "@vue/shared": "^3.2.24", + "@vueuse/core": "^9.6.0", + "async-validator": "^4.2.5", + "csstype": "^3.0.10", + "date-fns": "^2.28.0", + "lodash-es": "^4.17.21", + "stickybits": "^3.7.9", + "virtua": "^0.40.3" + }, + "devDependencies": { + "@antfu/eslint-config": "^2.15.0", + "@babel/core": "^7.16.0", + "@babel/plugin-syntax-jsx": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-typescript": "^7.16.7", + "@betit/rollup-plugin-rename-extensions": "^0.1.0", + "@commitlint/cli": "^13.2.1", + "@commitlint/config-conventional": "^13.2.0", + "@docsearch/css": "^3.0.0", + "@docsearch/js": "^3.0.0", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.6", + "@rollup/plugin-replace": "^5.0.1", + "@types/jest": "^27.4.1", + "@types/lodash-es": "^4.17.5", + "@vite-pwa/vitepress": "^0.3.0", + "@vitejs/plugin-vue-jsx": "^1.3.0", + "@vue/babel-plugin-jsx": "^1.1.1", + "@vue/repl": "^3.4.0", + "@vue/test-utils": "2.0.0-rc.17", + "@vue/vue3-jest": "27.0.0-alpha.4", + "@webank/eslint-config-ts": "^1.2.0", + "autoprefixer": "^10.4.0", + "babel-jest": "^27.5.1", + "body-scroll-lock": "4.0.0-beta.0", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cheap-watch": "^1.0.4", + "commitizen": "^4.2.4", + "conventional-changelog-cli": "^2.1.1", + "csso": "^4.2.0", + "cz-conventional-changelog": "^3.3.0", + "enquirer": "^2.3.6", + "eslint": "^9.1.1", + "eslint-import-resolver-custom-alias": "^1.3.2", + "execa": "^4.1.0", + "fast-glob": "^3.2.7", + "fs-extra": "^10.0.0", + "husky": "^7.0.4", + "jest": "^27.5.1", + "jest-canvas-mock": "^2.3.1", + "jest-transform-stub": "^2.0.0", + "less": "^4.1.2", + "less-loader": "^10.2.0", + "lint-staged": "^11.2.6", + "minimist": "^1.2.5", + "postcss": "^8.4.35", + "prettier": "^2.5.1", + "rollup": "^2.60.2", + "rollup-plugin-postcss": "^4.0.2", + "rollup-plugin-vue": "^6.0.0", + "semver": "^7.3.5", + "shiki": "^1.1.0", + "stylelint": "^14.1.0", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-rational-order": "^0.1.2", + "stylelint-config-standard": "^24.0.0", + "stylelint-declaration-block-no-ignored-properties": "^2.5.0", + "stylelint-order": "^5.0.0", + "svgo": "^2.8.0", + "terser": "^5.10.0", + "ts-jest": "^27.1.3", + "ts-morph": "^17.0.1", + "typescript": "^4.9.5", + "vitepress": "^1.3.4", + "vue": "^3.3.4" + }, + "lint-staged": { + "components/**/*.{js,ts,vue,jsx}": [ + "eslint --fix" + ], + "components/**/*.{less,css}": [ + "stylelint --fix" + ] + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "publishConfig": { + "access": "public" } - }, - "publishConfig": { - "access": "public" - } }