From 375cd0d5af63adc78e2eb155ed7c7be0bc8996a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?w=C5=AB=20y=C4=81ng?= Date: Thu, 3 Aug 2023 14:03:17 +0800 Subject: [PATCH] feat(1.5.2): weekly sync update to naruto (#2661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add naruto auto publish workflow (#2624) * Revert "chore: add naruto auto publish workflow (#2624)" (#2625) This reverts commit 7da9217cf9e4215cbba8f19b00d78d9594b0f8ca. * fix(table): filter table pass value improvement (#2623) * fix(table): filter table pass value * fix(table): empty * fix(tree-select): 自定义标签关闭异常 (#2631) * fix(tree-select): 自定义标签关闭异常 修复关闭自定义标签报错,并无法关闭 * fix: 删除无用代码 * feat(table): support TableColumnFilter.attrs .style .classNames (#2629) * feat(table): support TableColumnFilter.attrs * feat(table): more attributes * refactor(table): custom filter component * revert(group): group value * feat(table): remove value from filter component * fix(table): styles -> style * fix(table): support selectedRowKeys which not exist in data * fix(table): table doest not show empty elements (#2641) * fix(table): empty table * chore: lint error * fix: table empty data * fix(table): empty elements width * chore: fix build script process in Windows env (#2644) * fix(input): form disabled状态input显示clear (#2634) 修复form disabled状态input依然显示clear #2589 * fix(dialog): 修复dialog初始化时没有执行移动相关的初始化逻辑,导致image-viewer小窗口图片查看器无法移动问题 (#2622) * feat(table): scrollToElement By row key (#2643) * fix: dialog body class (#2645) * chore: release/1.5.1 (#2647) * fix: changelog intent * feat: release 1.5.1 * feat: update common * chore: fix indent in changelog * chore: changelog's changes * chore: fix indent --------- Co-authored-by: github-actions[bot] * chore: release 1.5.2 (#2657) * chore: release 1.5.2 * chore: update rollup config * chore: revert pull-request.yml change * chore: fix lint * chore: update common --------- Co-authored-by: sheepluo Co-authored-by: sinbadmaster <40019023+sinbadmaster@users.noreply.github.com> Co-authored-by: yusongh <45997005+yusongh@users.noreply.github.com> Co-authored-by: azhe Co-authored-by: github-actions[bot] --- .github/workflows/auto-release.yml | 2 +- CHANGELOG.md | 21 ++++++- package.json | 8 +-- script/utils/index.js | 2 +- src/_common | 2 +- src/checkbox/group.tsx | 2 +- src/common.ts | 6 +- src/dialog/dialog.tsx | 71 +++++++++++++----------- src/input/input.tsx | 2 +- src/menu/menu-item.tsx | 4 +- src/table/_example/filter-controlled.vue | 5 ++ src/table/_example/tree-select.vue | 25 +++++++-- src/table/_example/virtual-scroll.vue | 6 +- src/table/base-table.tsx | 22 +++++++- src/table/enhanced-table.tsx | 34 +++++++++++- src/table/filter-controller.tsx | 19 +++++-- src/table/hooks/useFixed.ts | 3 +- src/table/hooks/useTreeSelect.tsx | 1 + src/table/primary-table.tsx | 5 +- src/table/table.en-US.md | 5 +- src/table/table.md | 7 ++- src/table/tbody.tsx | 7 ++- src/table/type.ts | 18 +++++- src/tree-select/tree-select.tsx | 13 +++-- test/snap/__snapshots__/csr.test.js.snap | 24 ++++---- vitest.config.js | 2 +- 26 files changed, 230 insertions(+), 86 deletions(-) diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 37bcea997..66a19bb03 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -58,7 +58,7 @@ jobs: BODY: ${{ github.event.comment.body }} run: | txt=$(cat CHANGELOG.md) - echo "${txt%%##*} $BODY ${txt##*---}" > CHANGELOG.md + echo "${txt%%##*}$BODY${txt##*---}" > CHANGELOG.md git add . git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" diff --git a/CHANGELOG.md b/CHANGELOG.md index 859d8d6c2..4a377ef3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ toc: false docClass: timeline --- +## 🌈 1.5.2 `2023-08-01` +### 🚀 Features +- `Table`: + - 可筛选表格,支持透传 attrs/style/classNames 属性、样式、类名等信息到自定义组件,[issue#2627](https://github.com/Tencent/tdesign-vue/issues/2627) @chaishi ([#2629](https://github.com/Tencent/tdesign-vue/pull/2629)) + - 虚拟滚动场景,支持通过行唯一标识跳转到指定行(通过行下标跳转到指定行,以前的版本已支持) @chaishi ([#2643](https://github.com/Tencent/tdesign-vue/pull/2643)) +- `Upload`: 拖拽上传场景,支持 accept 限制可上传的文件类型 @chaishi ([common#1547](https://github.com/Tencent/tdesign-common/pull/1547)) +### 🐞 Bug Fixes +- `Checkbox`: 支持 `value` 传入 `undefined` @chaishi ([#2623](https://github.com/Tencent/tdesign-vue/pull/2623)) +- `Table`: + - 可筛选表格场景,filterValue 透传优化,没有显示写明 value 值的筛选项,不再透传 `undefined` 到子组件,因有些组件的默认值不允许为 undefined @chaishi ([#2623](https://github.com/Tencent/tdesign-vue/pull/2623)) + - 树形结构表格,修复选中行的值 `selectedRowKeys` 不在数据 `data` 中时,报错问题 @chaishi ([#2629](https://github.com/Tencent/tdesign-vue/pull/2629)) + - 修复 1.5.0 版本空表格没有显示占位元素问题 @chaishi ([#2641](https://github.com/Tencent/tdesign-vue/pull/2641)) + - 固定列空数据场景,元素显示错位问题 @chaishi ([#2641](https://github.com/Tencent/tdesign-vue/pull/2641)) +- `Input`: form表单disabled状态下input异常显示clear @sinbadmaster ([#2634](https://github.com/Tencent/tdesign-vue/pull/2634)) +- `Dialog`: + - 修复 dialog 初始化时没有执行移动相关的初始化逻辑,导致 image-viewer 小窗口图片查看器无法移动的问题 @yusongh ([#2622](https://github.com/Tencent/tdesign-vue/pull/2622)) + - 反馈类对话框补齐body class @uyarn ([#2645](https://github.com/Tencent/tdesign-vue/pull/2645)) +- `TreeSelect`: 修复自定义标签,点击关闭异常的问题 @sinbadmaster ([#2631](https://github.com/Tencent/tdesign-vue/pull/2631)) + ## 🌈 1.5.0 `2023-07-25` ### 🚀 Features - `Table`: 新增 `lazyLoad` 表格元素懒加载,当出现在可视区域时,再渲染表格第一屏数据 @chaishi ([#2605](https://github.com/Tencent/tdesign-vue/pull/2605)) @@ -36,7 +55,7 @@ docClass: timeline - 修复 `treeNodeModel` 实例未能同步 node 属性的问题 - 优化节点状态更新时的性能 - ## 🌈 1.4.8 `2023-07-18` +## 🌈 1.4.8 `2023-07-18` ### 🚀 Features - `DatePicker`: 优化关闭浮层后重置默认选中区域 @honkinglin ([#2585](https://github.com/Tencent/tdesign-vue/pull/2585)) - `Checkbox`: @chaishi ([#2583](https://github.com/Tencent/tdesign-vue/pull/2583) diff --git a/package.json b/package.json index 78cb13bcd..34680b998 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tdesign-vue", "purename": "tdesign", - "version": "1.5.0-naruto", + "version": "1.5.2-naruto", "description": "tdesign-vue", "title": "tdesign-vue", "keywords": [ @@ -52,7 +52,7 @@ "site:preview": "cd site && npm run build:preview", "site:intranet": "cd site && npm run build:intranet", "postsite:preview": "cp _site/index.html _site/404.html", - "prebuild": "rimraf es/* esm/* lib/* cjs/* dist/*", + "prebuild": "rimraf es/* esm/* lib/* cjs/* dist/* --glob", "build": "cross-env NODE_ENV=production rollup -c script/rollup.config.js && npm run build:tsc", "build:tsc": "run-p build:tsc-*", "build:tsc-es": "tsc --emitDeclarationOnly -d --outDir es/", @@ -63,7 +63,7 @@ "lint": "npm run lint:tsc && eslint --ext .vue,.js,.ts,.tsx ./ --max-warnings 0", "lint:tsc": "tsc --emitDeclarationOnly", "generate:usage": "node script/generate-usage/index.js", - "test": "npm run test:unit && npm run test:snap", + "test": "npm run test:unit && npm run test:snap", "test:unit": "vitest run --config vitest.config.js", "test:update": "vitest run -u && npm run test:snap-update", "test:unit-dev": "vitest", @@ -165,7 +165,7 @@ "postcss": "^8.3.11", "prettier": "^2.6.2", "prismjs": "^1.25.0", - "rimraf": "^3.0.2", + "rimraf": "^5.0.1", "rollup": "^2.53.2", "rollup-plugin-analyzer": "^3.3.0", "rollup-plugin-esbuild": "^4.8.2", diff --git a/script/utils/index.js b/script/utils/index.js index bbdd35b95..6abc4d363 100644 --- a/script/utils/index.js +++ b/script/utils/index.js @@ -1,6 +1,6 @@ +const fs = require('fs'); const path = require('path'); const clc = require('cli-color'); -const fs = require('fs'); function deleteFolderRecursive(path) { if (fs.existsSync(path)) { diff --git a/src/_common b/src/_common index dd930d965..05f588ae6 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit dd930d965239f756ae8444111d0a9c81ef28d0a4 +Subproject commit 05f588ae6c1d71255320a7d7f6ff61b7cd14318c diff --git a/src/checkbox/group.tsx b/src/checkbox/group.tsx index f4a540adf..03ee10da8 100644 --- a/src/checkbox/group.tsx +++ b/src/checkbox/group.tsx @@ -201,7 +201,7 @@ export default defineComponent({ props={option} index={index} data={option} - checked={this.innerValue.includes(option.value)} + checked={this.innerValue?.includes(option.value) || false} storeKey={this.storeKey} scopedSlots={this.$scopedSlots} > diff --git a/src/common.ts b/src/common.ts index 5db79faf4..e8f794152 100644 --- a/src/common.ts +++ b/src/common.ts @@ -72,7 +72,7 @@ export interface KeysType { } export interface HTMLElementAttributes { - [css: string]: string; + [attribute: string]: string; } export interface TScroll { @@ -115,3 +115,7 @@ export interface ScrollToElementParams { time?: number; behavior?: 'auto' | 'smooth'; } + +export interface ComponentScrollToElementParams extends ScrollToElementParams { + key: string | number; +} diff --git a/src/dialog/dialog.tsx b/src/dialog/dialog.tsx index 5a71e13d2..13798b00f 100644 --- a/src/dialog/dialog.tsx +++ b/src/dialog/dialog.tsx @@ -147,40 +147,47 @@ export default mixins(ActionMixin, getConfigReceiverMixins('d }, watch: { - visible(value) { - if (value) { - this.animationEnd = false; - if ((this.isModal && !this.showInAttachedElement) || this.isFullScreen) { - if (this.preventScrollThrough) { - document.head.appendChild(this.styleEl); - } + visible: { + handler(value) { + if (value) { + this.animationEnd = false; + if ((this.isModal && !this.showInAttachedElement) || this.isFullScreen) { + if (this.preventScrollThrough) { + this.$nextTick(() => { + document.head.appendChild(this.styleEl); + }); + } + this.$nextTick(() => { + const target = this.$refs.dialog as HTMLElement; + if (mousePosition && target) { + target.style.transformOrigin = `${mousePosition.x - target.offsetLeft}px ${ + mousePosition.y - target.offsetTop + }px`; + } + }); + } + // 清除鼠标焦点 避免entry事件多次触发(按钮弹出弹窗 不移除焦点 立即按Entry按键 会造成弹窗关闭再弹出) + (document.activeElement as HTMLElement).blur(); + } else { + this.clearStyleFunc(); + } + // 多个dialog同时存在时使用esc关闭异常 (#1209) + this.storeUid(value); + this.addKeyboardEvent(value); + if (this.isModeLess && this.draggable) { this.$nextTick(() => { - const target = this.$refs.dialog as HTMLElement; - if (mousePosition && target) { - target.style.transformOrigin = `${mousePosition.x - target.offsetLeft}px ${ - mousePosition.y - target.offsetTop - }px`; - } + this.initDragEvent(value); }); } - // 清除鼠标焦点 避免entry事件多次触发(按钮弹出弹窗 不移除焦点 立即按Entry按键 会造成弹窗关闭再弹出) - (document.activeElement as HTMLElement).blur(); - } else { - this.clearStyleFunc(); - } - // 多个dialog同时存在时使用esc关闭异常 (#1209) - this.storeUid(value); - this.addKeyboardEvent(value); - if (this.isModeLess && this.draggable) { - this.initDragEvent(value); - } - // 父元素为 display: none 时,需要更新子元素,避免 Dialog 前套 Table 组件时,固定列等特性失效 - if (value && !this.destroyOnClose && requestAnimationFrame) { - requestAnimationFrame(() => { - updateElement(this); - }); - } + // 父元素为 display: none 时,需要更新子元素,避免 Dialog 前套 Table 组件时,固定列等特性失效 + if (value && !this.destroyOnClose && requestAnimationFrame) { + requestAnimationFrame(() => { + updateElement(this); + }); + } + }, + immediate: true, }, }, mounted() { @@ -433,7 +440,9 @@ export default mixins(ActionMixin, getConfigReceiverMixins('d const closeClassName = this.isFullScreen ? [`${this.componentName}__close`, `${this.componentName}__close--fullscreen`] : `${this.componentName}__close`; - const bodyClassName = this.theme === 'default' ? [`${this.componentName}__body`] : [`${this.componentName}__body__icon`]; + const bodyClassName = this.theme === 'default' + ? [`${this.componentName}__body`] + : [`${this.componentName}__body`, `${this.componentName}__body__icon`]; const footerContent = renderTNodeJSX(this, 'footer', defaultFooter); diff --git a/src/input/input.tsx b/src/input/input.tsx index 02a43c72f..c0de85d17 100644 --- a/src/input/input.tsx +++ b/src/input/input.tsx @@ -81,7 +81,7 @@ export default mixins( }, showClear(): boolean { return ( - ((this.value && !this.disabled && this.clearable && !this.readonly) || this.showClearIconOnEmpty) + ((this.value && !this.tDisabled && this.clearable && !this.readonly) || this.showClearIconOnEmpty) && this.isHover ); }, diff --git a/src/menu/menu-item.tsx b/src/menu/menu-item.tsx index de202f6a4..fba558d03 100644 --- a/src/menu/menu-item.tsx +++ b/src/menu/menu-item.tsx @@ -24,6 +24,7 @@ export default defineComponent({ const active = computed(() => menu.activeValue.value === props.value); const collapsed = computed(() => menu.collapsed?.value); const classPrefix = usePrefixClass(); + const { proxy } = getCurrentInstance(); const classes = computed(() => [ `${classPrefix.value}-menu__item`, @@ -44,8 +45,7 @@ export default defineComponent({ if (props.href) { window.open(props.href, props.target); - } else if (props.to) { - const { proxy } = getCurrentInstance(); + } else if (props.to || props.routerLink) { const router = props.router || (proxy as Record).$router; const methods: string = props.replace ? 'replace' : 'push'; router[methods](props.to || props.href).catch((err: Error) => { diff --git a/src/table/_example/filter-controlled.vue b/src/table/_example/filter-controlled.vue index 67b1e38ac..026f8593f 100644 --- a/src/table/_example/filter-controlled.vue +++ b/src/table/_example/filter-controlled.vue @@ -173,6 +173,11 @@ export default { props: { firstDayOfWeek: 7, }, + attrs: { + 'data-id': 'attribute-id-value', + }, + classNames: 'custom-class-name', + styles: { fontSize: '14px' }, // 是否显示重置取消按钮,一般情况不需要显示 showConfirmAndReset: true, // 日期范围是一个组件,重置时需赋值为 [] diff --git a/src/table/_example/tree-select.vue b/src/table/_example/tree-select.vue index f3f70111e..0507cf8bd 100644 --- a/src/table/_example/tree-select.vue +++ b/src/table/_example/tree-select.vue @@ -170,19 +170,32 @@ export default { MessagePlugin.success('获取成功,请打开控制台查看'); }, + // 虚拟滚动场景:滚动到指定行 scrollToElement() { const { enhancedTableRef } = this.$refs; - const treeNodeData = enhancedTableRef.getData('first_level_150'); - console.log(treeNodeData); - // 因为可能会存在前面的元素节点展开,或行展开,故而下标跟序号不一定一样,不一定是 150 - enhancedTableRef.primaryTableRef.scrollToElement({ - // 跳转元素下标(第 151 个元素位置) - index: treeNodeData.rowIndex - this.selectedRowKeys.length, + + // 方式一:通过行唯一标识跳转到指定行 + enhancedTableRef.scrollToElement({ + // 滚动到指定元素 + key: 'first_level_150', + // 如果元素没有被展开,则跳转到父元素所在位置 + // key: 'second_level_1510', // 滚动元素距离顶部的距离(如表头高度) top: 47, // 高度动态变化场景下,即 isFixedRowHeight = false。延迟设置元素位置,一般用于依赖不同高度异步渲染等场景,单位:毫秒。(固定高度不需要这个) time: 60, }); + + // 方式二:通过行下标跳转到指定行(示例代码有效:勿删) + // const treeNodeData = enhancedTableRef.getData('first_level_150'); + // enhancedTableRef.primaryTableRef.scrollToElement({ + // // 跳转元素下标(第 151 个元素位置) + // index: treeNodeData.rowIndex - this.expandedRowKeys.length, + // // 滚动元素距离顶部的距离(如表头高度) + // top: 47, + // // 高度动态变化场景下,即 isFixedRowHeight = false。延迟设置元素位置,一般用于依赖不同高度异步渲染等场景,单位:毫秒。(固定高度不需要这个) + // time: 60, + // }); }, onRowClick(data) { diff --git a/src/table/_example/virtual-scroll.vue b/src/table/_example/virtual-scroll.vue index 4aec9d808..dd76f422d 100644 --- a/src/table/_example/virtual-scroll.vue +++ b/src/table/_example/virtual-scroll.vue @@ -93,8 +93,10 @@ export default { methods: { scrollToElement() { this.$refs.tableRef.scrollToElement({ - // 跳转元素下标(第 256 个元素位置) - index: 255, + // 方式一:使用下标跳转到指定行(第 256 个元素位置) + // index: 255, + // 方式二:使用行唯一标识跳转到指定行(id = 255) + key: 255, // 滚动元素距离顶部的距离(如表头高度) top: 47, // 行高度动态变化场景场景下,即 isFixedRowHeight = false。延迟设置元素位置,一般用于依赖不同高度异步渲染等场景,单位:毫秒。(固定高度不需要这个) diff --git a/src/table/base-table.tsx b/src/table/base-table.tsx index 90c5a93ab..84c59fca2 100644 --- a/src/table/base-table.tsx +++ b/src/table/base-table.tsx @@ -3,6 +3,7 @@ import { } from 'vue'; import pick from 'lodash/pick'; import isFunction from 'lodash/isFunction'; +import get from 'lodash/get'; import props from './base-table-props'; import useTableHeader from './hooks/useTableHeader'; import useColumnResize from './hooks/useColumnResize'; @@ -24,7 +25,7 @@ import TFoot from './tfoot'; import log from '../_common/js/log'; import { getIEVersion } from '../_common/js/utils/helper'; import { getAffixProps } from './utils'; -import { Styles } from '../common'; +import { ComponentScrollToElementParams, Styles } from '../common'; import { BaseTableCol, TableRowData } from './type'; export const BASE_TABLE_EVENTS = ['page-change', 'cell-click', 'scroll', 'scrollX', 'scrollY', 'column-resize-change']; @@ -266,9 +267,26 @@ export default defineComponent({ addTableResizeObserver(tableRef.value); }); + const tableData = computed(() => (isPaginateData.value ? dataSource.value : props.data)); + + const scrollToElement = (params: ComponentScrollToElementParams) => { + let { index } = params; + if (!index && index !== 0) { + if (!params.key) { + log.error('Table', 'scrollToElement: one of `index` or `key` must exist.'); + return; + } + index = tableData.value?.findIndex((item) => get(item, props.rowKey) === params.key); + if (index < 0) { + log.error('Table', `${params.key} does not exist in data, check \`rowKey\` or \`data\` please.`); + } + } + virtualConfig.scrollToElement({ ...params, index }); + }; + return { virtualConfig, - scrollToElement: virtualConfig.scrollToElement, + scrollToElement, columnResizable, thList, classPrefix, diff --git a/src/table/enhanced-table.tsx b/src/table/enhanced-table.tsx index a0ba2bf93..a141032ef 100644 --- a/src/table/enhanced-table.tsx +++ b/src/table/enhanced-table.tsx @@ -7,12 +7,19 @@ import primaryTableProps from './primary-table-props'; import enhancedTableProps from './enhanced-table-props'; import PrimaryTable, { BASE_TABLE_ALL_EVENTS } from './primary-table'; import { - TdEnhancedTableProps, PrimaryTableCol, TableRowData, DragSortContext, TdPrimaryTableProps, + TdEnhancedTableProps, + PrimaryTableCol, + TableRowData, + DragSortContext, + TdPrimaryTableProps, + TableRowState, } from './type'; import useTreeData from './hooks/useTreeData'; import useTreeSelect from './hooks/useTreeSelect'; import { TableListeners } from './base-table'; import { usePrefixClass } from '../hooks/useConfig'; +import { ComponentScrollToElementParams } from '..'; +import log from '../_common/js/log'; const PRIMARY_B_EVENTS = [ 'cell-click', @@ -106,6 +113,30 @@ export default defineComponent({ context.emit('row-click', p); }; + const getScrollRowIndex = (rowStateData: TableRowState, key: string | number): number => { + if (!rowStateData) return -1; + if (rowStateData.rowIndex >= 0) return rowStateData.rowIndex; + if (rowStateData.rowIndex < 0) { + return getScrollRowIndex(rowStateData.parent, key); + } + }; + + const scrollToElement = (params: ComponentScrollToElementParams) => { + let { index } = params; + if (!index && index !== 0) { + if (!params.key) { + log.error('Table', 'scrollToElement: one of `index` or `key` must exist.'); + return; + } + const rowStateData = treeDataMap.value.get(params.key); + index = getScrollRowIndex(rowStateData, params.key); + if (index < 0 || index === undefined) { + log.error('Table', `${params.key} does not exist in data, check \`rowKey\` or \`data\` please.`); + } + } + primaryTableRef.value.scrollToElement({ ...params, index }); + }; + return { store, classPrefix, @@ -119,6 +150,7 @@ export default defineComponent({ onInnerSelectChange, onEnhancedTableRowClick, ...treeInstanceFunctions, + scrollToElement, }; }, diff --git a/src/table/filter-controller.tsx b/src/table/filter-controller.tsx index cc29215ec..2d1337cd8 100644 --- a/src/table/filter-controller.tsx +++ b/src/table/filter-controller.tsx @@ -100,8 +100,14 @@ export default defineComponent({ const filterComponentProps: { [key: string]: any } = { options: ['single', 'multiple'].includes(column.filter.type) ? column.filter?.list : undefined, ...(column.filter?.props || {}), - value: this.innerFilterValue?.[column.colKey], }; + if ( + column.colKey + && this.innerFilterValue + && column.colKey in (this.innerFilterValue as PropType) + ) { + filterComponentProps.value = this.innerFilterValue[column.colKey]; + } // 这个代码必须放在这里,没事儿别改 if (column.filter.type === 'single') { filterComponentProps.onChange = (val: any) => { @@ -133,17 +139,22 @@ export default defineComponent({ if (!component) return null; const isVueComponent = component.install && component.component; if (typeof component === 'function' && !isVueComponent) { + // component() is going to be deprecated return component((v: FirstParams, b: SecondParams) => { - const tProps = typeof b === 'object' && 'attrs' in b ? b.attrs : {}; + const attributes = typeof b === 'object' && 'attrs' in b ? b.attrs : {}; return h(v, { - props: { ...filterComponentProps, ...tProps }, + props: { ...filterComponentProps }, + attrs: attributes, on, }); }); } + const filter = (this.column as TableFilterControllerProps['column']).filter || {}; return ( diff --git a/src/table/hooks/useFixed.ts b/src/table/hooks/useFixed.ts index 52cafcc8e..fbd3d16fc 100644 --- a/src/table/hooks/useFixed.ts +++ b/src/table/hooks/useFixed.ts @@ -416,9 +416,9 @@ export default function useFixed( }; const updateThWidthListHandler = () => { - if (notNeedThWidthList.value) return; const timer = setTimeout(() => { updateTableWidth(); + if (notNeedThWidthList.value) return; const thead = tableContentRef.value?.querySelector('thead'); if (!thead) return; updateThWidthList(thead.children); @@ -549,6 +549,7 @@ export default function useFixed( onMounted(() => { const scrollWidth = getScrollbarWidthWithCSS(); scrollbarWidth.value = scrollWidth; + updateThWidthListHandler(); const isWatchResize = isFixedColumn.value || isFixedHeader.value || !notNeedThWidthList.value || !data.value.length; // IE 11 以下使用 window resize;IE 11 以上使用 ResizeObserver if ((isWatchResize && getIEVersion() < 11) || typeof window.ResizeObserver === 'undefined') { diff --git a/src/table/hooks/useTreeSelect.tsx b/src/table/hooks/useTreeSelect.tsx index 2ea9cc2b3..5742144fa 100644 --- a/src/table/hooks/useTreeSelect.tsx +++ b/src/table/hooks/useTreeSelect.tsx @@ -139,6 +139,7 @@ export default function useTreeSelect(props: TdEnhancedTableProps, treeDataMap: for (let i = 0, len = tSelectedRowKeys.value.length; i < len; i++) { const rowValue = tSelectedRowKeys.value[i]; const state = treeDataMap.value.get(rowValue); + if (!state) continue; const children = get(state.row, rowDataKeys.value.childrenKey); // 根据选中的叶子结点计算父节点半选状态 if (!children || !children.length) { diff --git a/src/table/primary-table.tsx b/src/table/primary-table.tsx index 1900c1598..b470d106f 100644 --- a/src/table/primary-table.tsx +++ b/src/table/primary-table.tsx @@ -23,6 +23,7 @@ import useEditableRow from './hooks/useEditableRow'; import { EditableCellProps } from './editable-cell'; import useStyle from './hooks/useStyle'; import { CheckboxGroupValue } from '..'; +import { ComponentScrollToElementParams } from '../common'; export { BASE_TABLE_ALL_EVENTS } from './base-table'; @@ -300,8 +301,8 @@ export default defineComponent({ tRowAttributes, primaryTableClasses, errorListMap, - scrollToElement: (data: any) => { - primaryTableRef.value.virtualConfig.scrollToElement(data); + scrollToElement: (data: ComponentScrollToElementParams) => { + primaryTableRef.value.scrollToElement(data); }, scrollColumnIntoView: (colKey: string) => { primaryTableRef.value.scrollColumnIntoView(colKey); diff --git a/src/table/table.en-US.md b/src/table/table.en-US.md index fb6576298..40e9e7fbe 100644 --- a/src/table/table.en-US.md +++ b/src/table/table.en-US.md @@ -261,13 +261,16 @@ rowIndex | Number | - | required | Y name | type | default | description | required -- | -- | -- | -- | -- +attrs | Object | - | html attributes of component。Typescript:`HTMLElementAttributes`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +classNames | String | - | component class names。Typescript:`ClassName`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N component | Slot / Function | - | Typescript:`ComponentType`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N confirmEvents | Array | - | Typescript:`string[]` | N list | Array | - | Typescript:`Array`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N popupProps | Object | - | Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N -props | Array | - | Typescript:`FilterProps` `type FilterProps = RadioProps \| CheckboxProps \| InputProps \| { [key: string]: any }`,[Input API Documents](./input?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N +props | Object | - | Typescript:`FilterProps` `type FilterProps = RadioProps \| CheckboxProps \| InputProps \| { [key: string]: any }`,[Input API Documents](./input?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N resetValue | \- | - | Typescript:`any` | N showConfirmAndReset | Boolean | false | \- | N +style | Object | - | styles of component。Typescript:`Styles`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N type | String | - | Typescript:`FilterType` `type FilterType = 'input' \| 'single' \| 'multiple'`。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N ### TableColumnController diff --git a/src/table/table.md b/src/table/table.md index d9309da6e..ec6370e5a 100644 --- a/src/table/table.md +++ b/src/table/table.md @@ -261,14 +261,17 @@ rowIndex | Number | - | 必需。表格行下标,值为 `-1` 标识当前行 名称 | 类型 | 默认值 | 说明 | 必传 -- | -- | -- | -- | -- +attrs | Object | - | 用于透传筛选器属性到自定义组件 `component`,HTML 原生属性。TS 类型:`HTMLElementAttributes`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +classNames | String | - | 透传类名到自定义组件 `component`。TS 类型:`ClassName`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N component | Slot / Function | - | 用于自定义筛选器,只要保证自定义筛选器包含 value 属性 和 change 事件,即可像内置筛选器一样正常使用。示例:`component: DatePicker`。TS 类型:`ComponentType`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N confirmEvents | Array | - | 哪些事件触发后会进行过滤搜索(确认按钮无需配置,会默认触发搜索)。输入框组件示例:`confirmEvents: ['onEnter']`。TS 类型:`string[]` | N list | Array | - | 用于配置当前筛选器可选值有哪些,仅当 `filter.type` 等于 `single` 或 `multiple` 时有效。TS 类型:`Array`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N popupProps | Object | - | 透传 Popup 组件全部属性到筛选器浮层。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N -props | Array | - | 用于透传筛选器属性,可以对筛选器进行任何原组件支持的属性配置。TS 类型:`FilterProps` `type FilterProps = RadioProps \| CheckboxProps \| InputProps \| { [key: string]: any }`,[Input API Documents](./input?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N +props | Object | - | 用于透传筛选器属性到自定义组件 `component`,可以对筛选器进行任何原组件支持的属性配置。TS 类型:`FilterProps` `type FilterProps = RadioProps \| CheckboxProps \| InputProps \| { [key: string]: any }`,[Input API Documents](./input?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N resetValue | \- | - | 重置时设置的值,示例:'' 或 []。TS 类型:`any` | N showConfirmAndReset | Boolean | false | 是否显示重置和确认。值为真,过滤事件(filter-change)会在确定时触发;值为假,则数据变化时会立即触发过滤事件 | N -type | String | - | 用于设置筛选器类型:单选按钮筛选器、复选框筛选器、输入框筛选器。TS 类型:`FilterType` `type FilterType = 'input' \| 'single' \| 'multiple'`。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N +style | Object | - | 透传内联样式到自定义组件 `component`。TS 类型:`Styles`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +type | String | - | 用于设置筛选器类型:单选按钮筛选器、复选框筛选器、输入框筛选器。更多复杂组件,请更为使用 `component` 自定义任意组件。TS 类型:`FilterType` `type FilterType = 'input' \| 'single' \| 'multiple'`。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/table/type.ts) | N ### TableColumnController diff --git a/src/table/tbody.tsx b/src/table/tbody.tsx index fff175d51..17c5f3954 100644 --- a/src/table/tbody.tsx +++ b/src/table/tbody.tsx @@ -32,6 +32,7 @@ export interface TableBodyProps extends BaseTableProps { // table 到 body 的相同属性 export const extendTableProps = [ + 'bordered', 'rowKey', 'rowClassName', 'rowAttributes', @@ -130,12 +131,13 @@ export default defineComponent({ const renderEmpty = (h: CreateElement, columns: TableBodyProps['columns']) => { // 小于 100 属于异常宽度,不显示 const showEmptyText = Boolean(this.tableWidth && this.tableWidth > 100) || process.env.NODE_ENV === 'test'; + const tableWidth = this.bordered ? this.tableWidth - 2 : this.tableWidth; return (
{showEmptyText ? this.renderTNode('empty') || this.t(this.global.empty) : ''}
@@ -155,6 +157,7 @@ export default defineComponent({ if (['', null, undefined, false].includes(fullRowNode)) return null; // const isFixedToLeft = this.isWidthOverflow && this.columns.find((col) => col.fixed === 'left'); const classes = [this.tableFullRowClasses.base, this.tableFullRowClasses[tType]]; + const tableWidth = this.bordered ? this.tableWidth - 2 : this.tableWidth; /** innerFullRow 和 innerFullElement 同时存在,是为了保证 固定列时,当前行不随内容进行横向滚动 */ return ( @@ -163,7 +166,7 @@ export default defineComponent({ // @ts-ignore class={{ [this.tableFullRowClasses.innerFullRow]: this.isFixedToLeft }} // @ts-ignore - style={this.isFixedToLeft ? { width: `${this.tableWidth}px` } : {}} + style={this.isFixedToLeft ? { width: `${tableWidth}px` } : {}} >
{fullRowNode}
diff --git a/src/table/type.ts b/src/table/type.ts index b7d2e651c..2285fb0be 100644 --- a/src/table/type.ts +++ b/src/table/type.ts @@ -23,6 +23,7 @@ import { OptionData, SizeEnum, ClassName, + Styles, AttachNode, HTMLElementAttributes, ComponentType, @@ -772,6 +773,15 @@ export interface TableRowState { } export interface TableColumnFilter { + /** + * 用于透传筛选器属性到自定义组件 `component`,HTML 原生属性 + */ + attrs?: HTMLElementAttributes; + /** + * 透传类名到自定义组件 `component` + * @default '' + */ + classNames?: ClassName; /** * 用于自定义筛选器,只要保证自定义筛选器包含 value 属性 和 change 事件,即可像内置筛选器一样正常使用。示例:`component: DatePicker` */ @@ -789,7 +799,7 @@ export interface TableColumnFilter { */ popupProps?: PopupProps; /** - * 用于透传筛选器属性,可以对筛选器进行任何原组件支持的属性配置 + * 用于透传筛选器属性到自定义组件 `component`,可以对筛选器进行任何原组件支持的属性配置 */ props?: FilterProps; /** @@ -802,7 +812,11 @@ export interface TableColumnFilter { */ showConfirmAndReset?: boolean; /** - * 用于设置筛选器类型:单选按钮筛选器、复选框筛选器、输入框筛选器 + * 透传内联样式到自定义组件 `component` + */ + style?: Styles; + /** + * 用于设置筛选器类型:单选按钮筛选器、复选框筛选器、输入框筛选器。更多复杂组件,请更为使用 `component` 自定义任意组件 * @default '' */ type?: FilterType; diff --git a/src/tree-select/tree-select.tsx b/src/tree-select/tree-select.tsx index b5c3c9fad..f2fc4f69e 100644 --- a/src/tree-select/tree-select.tsx +++ b/src/tree-select/tree-select.tsx @@ -4,11 +4,11 @@ import isFunction from 'lodash/isFunction'; import Tree, { TreeProps } from '../tree'; import props from './props'; import SelectInput, { SelectInputProps } from '../select-input'; -import { TagInputChangeContext, TagInputValue } from '../tag-input'; import FakeArrow from '../common-components/fake-arrow'; import { TreeSelectValue, TdTreeSelectProps } from './type'; import { useTNodeJSX, useTNodeDefault } from '../hooks/tnode'; import useTreeSelect from './useTreeSelect'; +import { TreeOptionData } from '..'; export default defineComponent({ name: 'TTreeSelect', @@ -189,9 +189,14 @@ export default defineComponent({ valueDisplay: () => this.renderTNodeJSX('valueDisplay', { params: this.multiple ? { - value: this.nodeInfo, - onClose: (value: TagInputValue, context: TagInputChangeContext) => { - this.tagChange(value, context); + value: this.nodeInfo as TreeOptionData[], + onClose: (index: number) => { + const value = this.nodeInfo.map((node: TreeOptionData) => node.value); + this.tagChange(value, { + trigger: 'tag-remove', + index, + item: value[index], + }); }, } : { diff --git a/test/snap/__snapshots__/csr.test.js.snap b/test/snap/__snapshots__/csr.test.js.snap index 5de8532c8..5df67a7e5 100644 --- a/test/snap/__snapshots__/csr.test.js.snap +++ b/test/snap/__snapshots__/csr.test.js.snap @@ -35240,7 +35240,7 @@ exports[`csr snapshot test > csr test ./src/config-provider/_example/dialog.vue
Would you like to be my friends?
@@ -35335,7 +35335,7 @@ exports[`csr snapshot test > csr test ./src/config-provider/_example/dialog.vue
Would you like to be my friends?
@@ -35430,7 +35430,7 @@ exports[`csr snapshot test > csr test ./src/config-provider/_example/dialog.vue
Would you like to be my friends?
@@ -35525,7 +35525,7 @@ exports[`csr snapshot test > csr test ./src/config-provider/_example/dialog.vue
Would you like to be my friends?
@@ -44821,7 +44821,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/icon.vue 1`] = `
信息已全部保存,是否确认下单?
@@ -44913,7 +44913,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/icon.vue 1`] = `
系统重启后会短暂影响页面访问,确认重启吗?
@@ -45005,7 +45005,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/icon.vue 1`] = `
请检查推送数据是否符合要求
@@ -45097,7 +45097,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/icon.vue 1`] = `
是否前往查看订单列表
@@ -46305,7 +46305,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/warning.vue 1`] = `
对话框内容
@@ -46390,7 +46390,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/warning.vue 1`] = `
对话框内容
@@ -46475,7 +46475,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/warning.vue 1`] = `
对话框内容
@@ -46560,7 +46560,7 @@ exports[`csr snapshot test > csr test ./src/dialog/_example/warning.vue 1`] = `
对话框内容
diff --git a/vitest.config.js b/vitest.config.js index 6b7f8e4a5..2c64cfe63 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -1,5 +1,5 @@ -import { defineConfig } from 'vite'; import path from 'path'; +import { defineConfig } from 'vite'; import { createVuePlugin } from 'vite-plugin-vue2'; import vueJsx from '@vitejs/plugin-vue2-jsx';