diff --git a/packages/components/calendar/__test__/__snapshots__/index.test.js.snap b/packages/components/calendar/__test__/__snapshots__/index.test.js.snap index 5d458ac3a2..7e96062302 100644 --- a/packages/components/calendar/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/calendar/__test__/__snapshots__/index.test.js.snap @@ -816,7 +816,7 @@ exports[`calendar :base 1`] = ` diff --git a/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap b/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap index 1f620bf4fb..918cc34b9e 100644 --- a/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap @@ -521,7 +521,7 @@ exports[`date-time-picker :base 1`] = ` diff --git a/packages/components/dialog/__test__/__snapshots__/index.test.js.snap b/packages/components/dialog/__test__/__snapshots__/index.test.js.snap index 00ae9e2586..fbcc291f25 100644 --- a/packages/components/dialog/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/dialog/__test__/__snapshots__/index.test.js.snap @@ -48,7 +48,7 @@ exports[`dialog :base 1`] = ` /> diff --git a/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap b/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap index 7983eb5617..51e3e1621d 100644 --- a/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap @@ -675,7 +675,7 @@ exports[`dropdown-menu :base 1`] = ` class="t-dropdown-item__footer t-class-footer" /> diff --git a/packages/components/mixins/using-custom-navbar.ts b/packages/components/mixins/using-custom-navbar.ts index eb25c9f964..d3d3968e6f 100644 --- a/packages/components/mixins/using-custom-navbar.ts +++ b/packages/components/mixins/using-custom-navbar.ts @@ -15,10 +15,12 @@ const useCustomNavbarBehavior = Behavior({ distanceTop: 0, }, - lifetimes: { - attached() { - if (this.properties.usingCustomNavbar) { + observers: { + usingCustomNavbar(value) { + if (value) { this.calculateCustomNavbarDistanceTop(); + } else { + this.setData({ distanceTop: 0 }); } }, }, diff --git a/packages/components/picker/__test__/__snapshots__/index.test.js.snap b/packages/components/picker/__test__/__snapshots__/index.test.js.snap index 776f95b7f1..60a3fb50be 100644 --- a/packages/components/picker/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/picker/__test__/__snapshots__/index.test.js.snap @@ -200,7 +200,7 @@ exports[`picker :base 1`] = ` diff --git a/packages/components/popup/popup.less b/packages/components/popup/popup.less index 5f9f4281df..d8bd3c5579 100644 --- a/packages/components/popup/popup.less +++ b/packages/components/popup/popup.less @@ -33,6 +33,7 @@ padding: 20rpx; line-height: 1; color: @popup-close-btn-color; + font-size: 48rpx; } &--top { diff --git a/packages/components/popup/popup.ts b/packages/components/popup/popup.ts index a2f2f95fb9..73ebbfe66c 100644 --- a/packages/components/popup/popup.ts +++ b/packages/components/popup/popup.ts @@ -14,7 +14,7 @@ const name = `${prefix}-popup`; @wxComponent() export default class Popup extends SuperComponent { - externalClasses = [`${prefix}-class`, `${prefix}-class-content`]; + externalClasses = [`${prefix}-class`, `${prefix}-class-content`, `${prefix}-class-close`]; behaviors = [transition(), useCustomNavbar]; diff --git a/packages/components/popup/popup.wxml b/packages/components/popup/popup.wxml index 673e377744..6fa11a0812 100644 --- a/packages/components/popup/popup.wxml +++ b/packages/components/popup/popup.wxml @@ -29,8 +29,8 @@ > - - + + diff --git a/packages/components/table/README.en-US.md b/packages/components/table/README.en-US.md index d7c5d1448f..5f591630e0 100644 --- a/packages/components/table/README.en-US.md +++ b/packages/components/table/README.en-US.md @@ -59,6 +59,8 @@ align | String | left | align type。options: left/right/center | N cell | String / Function | - | use cell to render table cell。Typescript: `string \| ((params: BaseTableCellParams) => string)` `interface BaseTableCellParams { row: T; rowIndex: number; col: BaseTableCol; colIndex: number }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N class-name | String / Object / Array / Function | - | cell classnames。Typescript: `TableColumnClassName \| TableColumnClassName[]` `type TableColumnClassName = ClassName \| ((context: CellData) => ClassName)` `interface CellData extends BaseTableCellParams { type: 'th' \| 'td' }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N col-key | String | - | unique key for column | N +ellipsis | Boolean / Object / Function | false | ellipsis cell content。Typescript: `boolean \| PopupProps \| { props: PopupProps; content: string }`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N +ellipsis-title | Boolean / Object / Function | undefinde | ellipsis title content。Typescript: `boolean \| PopupProps \| { props: PopupProps; content: string }` `interface BaseTableColParams { col: BaseTableCol; colIndex: number }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N fixed | String | left | fixed current column to left or right。options: left/right | N min-width | String / Number | - | add CSS property `min-width` to HTML Element ``,Browsers with [TablesNG](https://docs.google.com/document/d/16PFD1GtMI9Zgwu0jtPaKZJ75Q2wyZ9EZnVbBacOfiNA/preview) support `minWidth` | N width | String / Number | - | column width | N diff --git a/packages/components/table/README.md b/packages/components/table/README.md index 5884ab5dbd..4e4aeeb980 100644 --- a/packages/components/table/README.md +++ b/packages/components/table/README.md @@ -45,7 +45,7 @@ toc: false {{ stripe }} -#### 带边框表格样式 +#### 带边框表格样式(内容超出省略与浮层预览) {{ bordered }} @@ -113,6 +113,8 @@ align | String | left | 列横向对齐方式。可选项:left/right/center | cell | String / Function | - | 自定义单元格渲染。默认使用 `colKey` 的值作为自定义当前列的插槽名称。
如果 `cell` 值类型为 Function 表示以函数形式渲染单元格。优先级高于 `render`。泛型 T 指表格数据类型。TS 类型:`string \| ((params: BaseTableCellParams) => string)` `interface BaseTableCellParams { row: T; rowIndex: number; col: BaseTableCol; colIndex: number }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N class-name | String / Object / Array / Function | - | 列类名,值类型是 Function 使用返回值作为列类名;值类型不为 Function 时,值用于整列类名(含表头)。泛型 T 指表格数据类型。TS 类型:`TableColumnClassName \| TableColumnClassName[]` `type TableColumnClassName = ClassName \| ((context: CellData) => ClassName)` `interface CellData extends BaseTableCellParams { type: 'th' \| 'td' }`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N col-key | String | - | 渲染列所需字段,值为 `serial-number` 表示当前列为「序号」列 | N +ellipsis | Boolean / Object / Function | false | 单元格和表头内容超出时,是否显示省略号。如果仅希望单元格超出省略,可设置 `ellipsisTitle = false`。
值为 `true`,则超出省略浮层默认显示单元格内容;
值类型为 `Function` 则自定义超出省略浮中层显示的内容。
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }`。
请注意单元格超出省略的两个基本点:1. 内容元素是内联元素或样式(自定义单元格内容时需特别注意);2. 内容超出父元素。TS 类型:`boolean \| PopupProps \| { props: PopupProps; content: string }`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N +ellipsis-title | Boolean / Object / Function | undefinde | 表头内容超出时,是否显示省略号。优先级高于 `ellipsis`。
值为 `true`,则超出省略的浮层默认显示表头全部内容;
值类型为 `Function` 用于自定义超出省略浮层显示的表头内容;
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }`。TS 类型:`boolean \| PopupProps \| { props: PopupProps; content: string }` `interface BaseTableColParams { col: BaseTableCol; colIndex: number }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/table/type.ts) | N fixed | String | left | 固定列显示位置。可选项:left/right | N min-width | String / Number | - | 透传 CSS 属性 `min-width` 到 `` 元素。⚠️ 仅少部分浏览器支持,如:使用 [TablesNG](https://docs.google.com/document/d/16PFD1GtMI9Zgwu0jtPaKZJ75Q2wyZ9EZnVbBacOfiNA/preview) 渲染的 Chrome 浏览器支持 `minWidth` | N width | String / Number | - | 列宽,可以作为最小宽度使用。当列宽总和小于 `table` 元素时,浏览器根据宽度设置情况自动分配宽度;当列宽总和大于 `table` 元素,表现为定宽。可以同时调整 `table` 元素的宽度来达到自己想要的效果 | N diff --git a/packages/components/table/__test__/__snapshots__/demo.test.js.snap b/packages/components/table/__test__/__snapshots__/demo.test.js.snap index a48ba3cbe8..ad06c098a0 100644 --- a/packages/components/table/__test__/__snapshots__/demo.test.js.snap +++ b/packages/components/table/__test__/__snapshots__/demo.test.js.snap @@ -281,6 +281,99 @@ exports[`Table Table bordered demo works fine 1`] = ` `; +exports[`Table Table ellipsis demo works fine 1`] = ` + + + +`; + exports[`Table Table rowspan-colspan demo works fine 1`] = ` { mapper.forEach((demoName) => { diff --git a/packages/components/table/_example/base/index.wxss b/packages/components/table/_example/base/index.wxss deleted file mode 100644 index bc0171b967..0000000000 --- a/packages/components/table/_example/base/index.wxss +++ /dev/null @@ -1 +0,0 @@ -/* base demo 无额外样式 */ diff --git a/packages/components/table/_example/bordered/index.wxss b/packages/components/table/_example/bordered/index.wxss deleted file mode 100644 index 0a74168579..0000000000 --- a/packages/components/table/_example/bordered/index.wxss +++ /dev/null @@ -1 +0,0 @@ -/* bordered demo 无额外样式 */ diff --git a/packages/components/table/_example/ellipsis/index.js b/packages/components/table/_example/ellipsis/index.js new file mode 100644 index 0000000000..885201d7df --- /dev/null +++ b/packages/components/table/_example/ellipsis/index.js @@ -0,0 +1,89 @@ +import SkylineBehavior from '@behaviors/skyline.js'; + +Component({ + behaviors: [SkylineBehavior], + data: { + // 组合 ellipsis + ellipsisTitle 的不同形式与取值 + // 约束:弹出层方向仅 bottom / center;弹出内容为单元格内容;弹出标题为该列表头 + columns: [ + // 1. ellipsis 为 Object:整体透传给 Popup(center 弹出 + 列表头作为标题);content 缺省时回退为单元格内容 + { + colKey: 'applicant', + title: '申请人', + ellipsis: { placement: 'center', title: '申请人' }, + }, + // 2. ellipsis 为 Object + ellipsisTitle 为 true:表头内容也超出省略(优先级高于 ellipsis);bottom 弹出 + { + colKey: 'status', + title: '审批状态与处理结果', + ellipsis: { placement: 'bottom', title: '审批状态' }, + ellipsisTitle: true, + }, + // 3. ellipsis 为 Object + ellipsisTitle 为 false:仅单元格超出省略,表头不省略 + { + colKey: 'channel', + title: '签署方式', + ellipsis: { placement: 'bottom', title: '签署方式' }, + ellipsisTitle: false, + }, + // 4. ellipsis 与 ellipsisTitle 均为 Object:单元格浮层与表头浮层均带列表头标题;bottom 弹出 + { + colKey: 'email', + title: '邮箱地址', + ellipsis: { placement: 'bottom', title: '邮箱地址' }, + ellipsisTitle: { placement: 'bottom', title: '邮箱地址' }, + }, + ], + data: [], + }, + lifetimes: { + attached() { + const data = [ + { + id: 1, + applicant: '贾明(kyrieJia)', + status: '审批通过,流程已全部走完', + channel: '电子签署(在线完成)', + email: 'jiaming.kyrie@example.com', + }, + { + id: 2, + applicant: '张三(threeZhang)', + status: '审批失败,请联系管理员处理', + channel: '纸质签署(线下邮寄)', + email: 'zhangsan.three@example.com', + }, + { + id: 3, + applicant: '王芳(fangWang)', + status: '审批已过期,需重新发起', + channel: '电子签署(在线完成)', + email: 'wangfang.fw@example.com', + }, + { + id: 4, + applicant: '李雷(leiLi)', + status: '审批中,等待上级确认', + channel: '纸质签署(线下邮寄)', + email: 'lilei.ll@example.com', + }, + { + id: 5, + applicant: '韩梅梅(meiHan)', + status: '审批通过,流程已全部走完', + channel: '电子签署(在线完成)', + email: 'hanmeimei.mm@example.com', + }, + ]; + this.setData({ data }); + }, + }, + methods: { + handleRowClick(e) { + console.log('[row-click]', e.detail); + }, + handleCellClick(e) { + console.log('[cell-click]', e.detail); + }, + }, +}); diff --git a/packages/components/table/_example/ellipsis/index.json b/packages/components/table/_example/ellipsis/index.json new file mode 100644 index 0000000000..e3c621db08 --- /dev/null +++ b/packages/components/table/_example/ellipsis/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-table": "tdesign-miniprogram/table/table" + } +} diff --git a/packages/components/table/_example/ellipsis/index.wxml b/packages/components/table/_example/ellipsis/index.wxml new file mode 100644 index 0000000000..dcd1ef5bdf --- /dev/null +++ b/packages/components/table/_example/ellipsis/index.wxml @@ -0,0 +1,9 @@ + diff --git a/packages/components/table/_example/rowspan-colspan/index.wxss b/packages/components/table/_example/rowspan-colspan/index.wxss deleted file mode 100644 index 8fae2cf7c7..0000000000 --- a/packages/components/table/_example/rowspan-colspan/index.wxss +++ /dev/null @@ -1 +0,0 @@ -/* rowspan-colspan demo 无额外样式 */ diff --git a/packages/components/table/_example/scroll/index.wxss b/packages/components/table/_example/scroll/index.wxss deleted file mode 100644 index 6c14f83f70..0000000000 --- a/packages/components/table/_example/scroll/index.wxss +++ /dev/null @@ -1 +0,0 @@ -/* scroll demo 无额外样式 */ diff --git a/packages/components/table/_example/stripe/index.wxss b/packages/components/table/_example/stripe/index.wxss deleted file mode 100644 index dacf7e23a1..0000000000 --- a/packages/components/table/_example/stripe/index.wxss +++ /dev/null @@ -1 +0,0 @@ -/* stripe demo 无额外样式 */ diff --git a/packages/components/table/_example/table.json b/packages/components/table/_example/table.json index 0dd71e440d..f85f125f51 100644 --- a/packages/components/table/_example/table.json +++ b/packages/components/table/_example/table.json @@ -6,6 +6,7 @@ "bordered": "./bordered", "stripe": "./stripe", "scroll": "./scroll", + "ellipsis": "./ellipsis", "rowspan-colspan": "./rowspan-colspan" } } diff --git a/packages/components/table/_example/table.wxml b/packages/components/table/_example/table.wxml index 234f169c11..5ea3e8cd81 100644 --- a/packages/components/table/_example/table.wxml +++ b/packages/components/table/_example/table.wxml @@ -17,6 +17,9 @@ + + + diff --git a/packages/components/table/table.json b/packages/components/table/table.json index f20e16478f..0d40836af5 100644 --- a/packages/components/table/table.json +++ b/packages/components/table/table.json @@ -2,6 +2,7 @@ "component": true, "styleIsolation": "apply-shared", "usingComponents": { - "t-loading": "../loading/loading" + "t-loading": "../loading/loading", + "t-popup": "../popup/popup" } } diff --git a/packages/components/table/table.less b/packages/components/table/table.less index aa60ecd112..51906a4b89 100644 --- a/packages/components/table/table.less +++ b/packages/components/table/table.less @@ -7,6 +7,8 @@ @table-header-bg-color: var(--td-table-header-bg-color, @bg-color-container); @table-header-text-color: var(--td-table-header-text-color, @text-color-placeholder); @table-body-text-color: var(--td-table-body-text-color, @text-color-primary); +@table-popup-title-color: @text-color-primary; +@table-popup-title-font: @font-title-medium; @table-stripe-bg-color: var(--td-table-stripe-bg-color, @bg-color-secondarycontainer); @table-td-padding: var(--td-table-td-padding, @spacer @spacer-1); @table-th-padding: var(--td-table-th-padding, @spacer @spacer-1); @@ -102,10 +104,80 @@ &__th-content, &__td-content { overflow: hidden; + } + + // 开启 ellipsis / ellipsisTitle 后才出现超出省略 + &__th-content--ellipsis, + &__td-content--ellipsis { text-overflow: ellipsis; white-space: nowrap; } + &__popup { + &--center { + width: 480rpx; + height: 480rpx; + } + + &--bottom, + &--top { + width: 100%; + height: 436rpx; + } + + &--left, + &--right { + width: 65%; + height: 100%; + } + + &-inner { + display: flex; + flex-direction: column; + height: 100%; + box-sizing: border-box; + } + + &-title { + flex-shrink: 0; + padding: @spacer-2; + overflow: hidden; + color: @table-popup-title-color; + font: @table-popup-title-font; + font-weight: 600; + text-align: center; + white-space: nowrap; + text-overflow: ellipsis; + + &:empty { + display: none; + } + } + + &-close { + padding: @spacer-2; + } + + &-content { + flex: 1; + overflow: auto; + word-break: break-all; + color: @table-body-text-color; + box-sizing: border-box; + padding: calc(48rpx + @spacer-2 * 2) @spacer-2 @spacer-2; + + &--center { + padding-right: @spacer-3; + padding-bottom: @spacer-3; + padding-left: @spacer-3; + } + } + + &-title:not(:empty) + .@{table-prefix}__popup-content { + padding-top: 0; + } + } + // 空数据 &__empty-row { display: flex; diff --git a/packages/components/table/table.ts b/packages/components/table/table.ts index 1c2b28cb4a..8c86a093c2 100644 --- a/packages/components/table/table.ts +++ b/packages/components/table/table.ts @@ -10,8 +10,11 @@ import { getCellKey, getSkipSpansMap, handleCellSpan, + hasEllipsisColumn, isFirstColumnInSpan, isLastRowInSpan, + isTextEllipsis, + resolveEllipsis, } from './utils'; const { prefix } = config; @@ -48,6 +51,12 @@ export default class Table extends SuperComponent { scrollableToLeft: false, scrollableToRight: false, contentClasses: '', + // 超出省略(ellipsis / ellipsisTitle) + hasEllipsis: false, + titleClassNames: [] as string[], + titleEllipsisFlags: [] as boolean[], + popupProps: { placement: 'center' } as Record, + popupVisible: false, }; observers = { @@ -85,6 +94,9 @@ export default class Table extends SuperComponent { } = this.properties; const { classPrefix } = this.data; + // 开启后才渲染预览弹窗,并为单元格/表头附加超出省略测量用的类名 + const hasEllipsis = hasEllipsisColumn((columns || []) as BaseTableCol[]); + // 解析 fixedRows const fixedTopRows = (fixedRows && (fixedRows as number[])[0]) || 0; const fixedBottomRows = (fixedRows && (fixedRows as number[])[1]) || 0; @@ -207,6 +219,19 @@ export default class Table extends SuperComponent { return classes.filter(Boolean).join(' '); }); + // 表头内容类名与省略标记:ellipsisTitle 优先级高于 ellipsis + const titleEllipsisList = (columns || []).map((col: BaseTableCol, colIndex: number) => + resolveEllipsis(col.ellipsisTitle !== undefined ? col.ellipsisTitle : col.ellipsis, { col, colIndex }), + ); + const titleClassNames = titleEllipsisList.map((item: any, colIndex: number) => { + const classes: string[] = [`${classPrefix}__th-content`]; + if (item.enabled) { + classes.push(`${classPrefix}__th-content--ellipsis`, `${classPrefix}__th-content-${colIndex}`); + } + return classes.filter(Boolean).join(' '); + }); + const titleEllipsisFlags = titleEllipsisList.map((item: any) => !!item.enabled); + // 计算合并单元格 const hasSpan = !!rowspanAndColspan && !!data?.length && !!columns?.length; const skipSpansMap = getSkipSpansMap(data, columns, rowKey, rowspanAndColspan as Function); @@ -245,11 +270,21 @@ export default class Table extends SuperComponent { } } + const cellEllipsis = resolveEllipsis(col.ellipsis, { row, col, rowIndex, colIndex }); + // 合并场景用 grid 定位实现跨行跨列,非合并场景沿用列宽样式 const cellStyle = hasSpan ? `grid-column: ${colIndex + 1} / span ${colspan || 1}; grid-row: ${rowIndex + 1} / span ${rowspan || 1}` : colStyles[colIndex]; + const contentClasses: string[] = [`${classPrefix}__td-content`]; + if (cellEllipsis.enabled) { + contentClasses.push( + `${classPrefix}__td-content--ellipsis`, + `${classPrefix}__td-content-${rowIndex}-${colIndex}`, + ); + } + return { colKey: col.colKey || String(colIndex), colIndex, @@ -257,6 +292,8 @@ export default class Table extends SuperComponent { tdClass: tdClasses.filter(Boolean).join(' '), cellStyle, skipped: skipped || false, + contentClass: contentClasses.filter(Boolean).join(' '), + ellipsis: !!cellEllipsis.enabled, }; }); @@ -338,6 +375,9 @@ export default class Table extends SuperComponent { hasSpan, tbodyStyles, contentClasses, + hasEllipsis, + titleClassNames, + titleEllipsisFlags, }, () => { // 动态测量行高,修正固定行的 top/bottom 值 @@ -403,9 +443,77 @@ export default class Table extends SuperComponent { } }, + /** 展示超出省略内容的预览弹窗(仅当内容实际溢出时才弹出) */ + showEllipsisPopup( + this: any, + type: 'td' | 'th', + rowIndex: number, + colIndex: number, + content: string, + props: Record, + ) { + if (!content) return; + + const { classPrefix } = this.data; + const suffix = type === 'th' ? `${colIndex}` : `${rowIndex}-${colIndex}`; + + const query = this.createSelectorQuery(); + query.select(`.${classPrefix}__${type}-content-${suffix}`).boundingClientRect(); + query.select(`.${classPrefix}__${type}-text-${suffix}`).boundingClientRect(); + query.exec((res: any) => { + const [contentRect, textRect] = res || []; + // 内容实际宽度大于可视宽度才视为溢出(对齐桌面端 scrollWidth > clientWidth) + if (!isTextEllipsis(contentRect, textRect)) return; + + this.setData({ + popupProps: { + placement: props.placement || 'center', + showOverlay: props.showOverlay !== false, + closeOnOverlayClick: props.closeOnOverlayClick !== false, + closeBtn: props.closeBtn !== false, + usingCustomNavbar: !!props.usingCustomNavbar, + title: props.title || '', + content, + }, + popupVisible: true, + }); + }); + }, + + /** 点击表头:表头内容超出省略时,弹出浮层预览完整标题 */ + onTitleClick(this: any, e: any) { + const { colIndex } = e.currentTarget.dataset; + const { columns } = this.properties; + const col = columns && columns[colIndex]; + if (!col) return; + + const resolved = resolveEllipsis(col.ellipsisTitle !== undefined ? col.ellipsisTitle : col.ellipsis, { + col, + colIndex, + }); + if (!resolved.enabled) return; + + this.showEllipsisPopup('th', 0, colIndex, resolved.content || col.title || '', resolved.props); + }, + + onPopupVisibleChange(this: any, e: any) { + const { visible } = e.detail || {}; + if (!!visible !== this.data.popupVisible) { + this.setData({ popupVisible: !!visible }); + } + }, + onCellClick(this: any, e: any) { const { rowIndex, colIndex } = e.currentTarget.dataset; const { data, columns } = this.properties; + const cell = this.data.renderData?.[rowIndex]?.cells?.[colIndex]; + const col = columns && columns[colIndex]; + if (cell && col) { + const resolved = resolveEllipsis(col.ellipsis, { row: data[rowIndex], col, rowIndex, colIndex }); + if (resolved.enabled) { + this.showEllipsisPopup('td', rowIndex, colIndex, resolved.content || cell.content, resolved.props); + } + } if (data && data[rowIndex] && columns && columns[colIndex]) { this.triggerEvent('cell-click', { row: data[rowIndex], diff --git a/packages/components/table/table.wxml b/packages/components/table/table.wxml index 2fa0f239c9..8087681215 100644 --- a/packages/components/table/table.wxml +++ b/packages/components/table/table.wxml @@ -9,7 +9,12 @@ data-col-index="{{cell.colIndex}}" bind:tap="onCellClick" > - {{cell.content}} + + + {{cell.content}} + + {{cell.content}} + @@ -32,8 +37,15 @@ wx:key="colKey" class="{{classPrefix}}__th {{thClassNames[colIndex]}}" style="{{colStyles[colIndex]}}" + data-col-index="{{colIndex}}" + bind:tap="onTitleClick" > - {{col.title}} + + + {{col.title}} + + {{col.title}} + @@ -93,4 +105,25 @@ {{footerSummary}} + + + + + {{popupProps.title}} + {{popupProps.content}} + + diff --git a/packages/components/table/type.ts b/packages/components/table/type.ts index e639e93115..c55faca4ed 100644 --- a/packages/components/table/type.ts +++ b/packages/components/table/type.ts @@ -5,6 +5,7 @@ * */ import { LoadingProps } from '../loading/index'; +import { PopupProps } from '../popup/index'; import type { ClassName } from '../common/common'; export interface TdBaseTableProps { @@ -166,6 +167,16 @@ export interface BaseTableCol { * @default '' */ colKey?: string; + /** + * 单元格和表头内容超出时,是否显示省略号。如果仅希望单元格超出省略,可设置 `ellipsisTitle = false`。
值为 `true`,则超出省略浮层默认显示单元格内容;
值类型为 `Function` 则自定义超出省略浮中层显示的内容。
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }`。
请注意单元格超出省略的两个基本点:1. 内容元素是内联元素或样式(自定义单元格内容时需特别注意);2. 内容超出父元素 + * @default false + */ + ellipsis?: boolean | PopupProps | { props: PopupProps; content: string }; + /** + * 表头内容超出时,是否显示省略号。优先级高于 `ellipsis`。
值为 `true`,则超出省略的浮层默认显示表头全部内容;
值类型为 `Function` 用于自定义超出省略浮层显示的表头内容;
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }` + * @default undefinde + */ + ellipsisTitle?: boolean | PopupProps | { props: PopupProps; content: string }; /** * 固定列显示位置 * @default left @@ -209,4 +220,9 @@ export interface CellData extends BaseTab type: 'th' | 'td'; } +export interface BaseTableColParams { + col: BaseTableCol; + colIndex: number; +} + export type DataType = TableRowData; diff --git a/packages/components/table/utils.ts b/packages/components/table/utils.ts index c3cba4aae3..3048d125f6 100644 --- a/packages/components/table/utils.ts +++ b/packages/components/table/utils.ts @@ -117,3 +117,64 @@ export function isLastRowInSpan(rowIndex: number, rowspan?: number, totalDataLen export function isFirstColumnInSpan(colIndex: number): boolean { return colIndex === 0; } + +// ------- 超出省略(ellipsis / ellipsisTitle) ------- + +export interface EllipsisResult { + /** 是否开启超出省略 */ + enabled: boolean; + /** 浮层展示内容,缺省时回退为单元格/表头完整内容 */ + content?: string; + /** 透传给 Popup 组件的属性 */ + props: Record; +} + +/** + * 解析 `ellipsis` / `ellipsisTitle` 配置。 + * 支持 `boolean` / `Object`(透传 Popup)/ `{ props, content }` / `Function`(按单元格动态返回)。 + * `ellipsisTitle` 优先级高于 `ellipsis`,由调用方决定取值。 + */ +export function resolveEllipsis(value: any, params: Record): EllipsisResult { + const disabled: EllipsisResult = { enabled: false, props: {} }; + let result = value; + + if (typeof value === 'function') { + try { + result = value(params); + } catch (err) { + // 函数可能依赖行数据(表头场景没有 row),异常时按未开启超出省略处理 + return disabled; + } + } + + if (result === false || result === null || result === undefined) return disabled; + if (typeof result === 'string') return { enabled: true, content: result, props: {} }; + if (typeof result !== 'object') return { enabled: !!result, props: {} }; + + // `{ props, content }` 中 props 透传 Popup;其余形式整体作为 Popup 属性透传 + const props: Record = result.props && typeof result.props === 'object' ? { ...result.props } : {}; + if (!result.props) { + Object.keys(result).forEach((key) => { + if (key !== 'content') props[key] = result[key]; + }); + } + + const content = typeof result.content === 'function' ? result.content() : result.content; + return { enabled: true, content: typeof content === 'string' ? content : undefined, props }; +} + +/** 是否存在列开启了超出省略 */ +export function hasEllipsisColumn(columns: BaseTableCol[]): boolean { + return (columns || []).some((col) => !!col.ellipsis || !!col.ellipsisTitle); +} + +/** + * 判断单元格/表头内容是否溢出(对齐桌面端 `isTextEllipsis`:`scrollWidth > clientWidth`)。 + * 小程序无法直接取节点 `scrollWidth`,故用内层文本节点的实际宽度(≈ scrollWidth) + * 与内容容器的可视宽度(≈ clientWidth)比较,内容宽度更大即为溢出。 + * `tolerance` 用于消除 `boundingClientRect` 的亚像素误差。 + */ +export function isTextEllipsis(contentRect?: { width: number }, textRect?: { width: number }, tolerance = 1): boolean { + if (!contentRect || !textRect) return false; + return textRect.width - contentRect.width > tolerance; +} diff --git a/packages/uniapp-components/table/README.en-US.md b/packages/uniapp-components/table/README.en-US.md index efab84369f..73b53a5231 100644 --- a/packages/uniapp-components/table/README.en-US.md +++ b/packages/uniapp-components/table/README.en-US.md @@ -50,6 +50,8 @@ align | String | left | align type。options: left/right/center | N cell | String / Function | - | use cell to render table cell。Typescript: `string \| ((params: BaseTableCellParams) => string)` `interface BaseTableCellParams { row: T; rowIndex: number; col: BaseTableCol; colIndex: number }`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N class-name | String / Object / Array / Function | - | cell classnames。Typescript: `TableColumnClassName \| TableColumnClassName[]` `type TableColumnClassName = ClassName \| ((context: CellData) => ClassName)` `interface CellData extends BaseTableCellParams { type: 'th' \| 'td' }`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/common/common.ts)。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N col-key | String | - | unique key for column | N +ellipsis | Boolean / Object / Function | false | ellipsis cell content。Typescript: `boolean \| PopupProps \| { props: PopupProps; content: string }`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N +ellipsis-title | Boolean / Object / Function | undefinde | ellipsis title content。Typescript: `boolean \| PopupProps \| { props: PopupProps; content: string }` `interface BaseTableColParams { col: BaseTableCol; colIndex: number }`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N fixed | String | left | fixed current column to left or right。options: left/right | N min-width | String / Number | - | add CSS property `min-width` to HTML Element ``,Browsers with [TablesNG](https://docs.google.com/document/d/16PFD1GtMI9Zgwu0jtPaKZJ75Q2wyZ9EZnVbBacOfiNA/preview) support `minWidth` | N width | String / Number | - | column width | N diff --git a/packages/uniapp-components/table/README.md b/packages/uniapp-components/table/README.md index 53bb6deba3..43bf7988a6 100644 --- a/packages/uniapp-components/table/README.md +++ b/packages/uniapp-components/table/README.md @@ -95,6 +95,8 @@ align | String | left | 列横向对齐方式。可选项:left/right/center | cell | String / Function | - | 自定义单元格渲染。默认使用 `colKey` 的值作为自定义当前列的插槽名称。
如果 `cell` 值类型为 Function 表示以函数形式渲染单元格。优先级高于 `render`。泛型 T 指表格数据类型。TS 类型:`string \| ((params: BaseTableCellParams) => string)` `interface BaseTableCellParams { row: T; rowIndex: number; col: BaseTableCol; colIndex: number }`。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N class-name | String / Object / Array / Function | - | 列类名,值类型是 Function 使用返回值作为列类名;值类型不为 Function 时,值用于整列类名(含表头)。泛型 T 指表格数据类型。TS 类型:`TableColumnClassName \| TableColumnClassName[]` `type TableColumnClassName = ClassName \| ((context: CellData) => ClassName)` `interface CellData extends BaseTableCellParams { type: 'th' \| 'td' }`。[通用类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/common/common.ts)。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N col-key | String | - | 渲染列所需字段,值为 `serial-number` 表示当前列为「序号」列 | N +ellipsis | Boolean / Object / Function | false | 单元格和表头内容超出时,是否显示省略号。如果仅希望单元格超出省略,可设置 `ellipsisTitle = false`。
值为 `true`,则超出省略浮层默认显示单元格内容;
值类型为 `Function` 则自定义超出省略浮中层显示的内容。
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }`。
请注意单元格超出省略的两个基本点:1. 内容元素是内联元素或样式(自定义单元格内容时需特别注意);2. 内容超出父元素。TS 类型:`boolean \| PopupProps \| { props: PopupProps; content: string }`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N +ellipsis-title | Boolean / Object / Function | undefinde | 表头内容超出时,是否显示省略号。优先级高于 `ellipsis`。
值为 `true`,则超出省略的浮层默认显示表头全部内容;
值类型为 `Function` 用于自定义超出省略浮层显示的表头内容;
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }`。TS 类型:`boolean \| PopupProps \| { props: PopupProps; content: string }` `interface BaseTableColParams { col: BaseTableCol; colIndex: number }`。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/table/type.ts) | N fixed | String | left | 固定列显示位置。可选项:left/right | N min-width | String / Number | - | 透传 CSS 属性 `min-width` 到 `` 元素。⚠️ 仅少部分浏览器支持,如:使用 [TablesNG](https://docs.google.com/document/d/16PFD1GtMI9Zgwu0jtPaKZJ75Q2wyZ9EZnVbBacOfiNA/preview) 渲染的 Chrome 浏览器支持 `minWidth` | N width | String / Number | - | 列宽,可以作为最小宽度使用。当列宽总和小于 `table` 元素时,浏览器根据宽度设置情况自动分配宽度;当列宽总和大于 `table` 元素,表现为定宽。可以同时调整 `table` 元素的宽度来达到自己想要的效果 | N diff --git a/packages/uniapp-components/table/type.ts b/packages/uniapp-components/table/type.ts index 65a884ac26..5ba5934b51 100644 --- a/packages/uniapp-components/table/type.ts +++ b/packages/uniapp-components/table/type.ts @@ -5,6 +5,7 @@ * */ import type { TdLoadingProps as LoadingProps } from '../loading/type'; +import type { TdPopupProps as PopupProps } from '../popup/type'; import type { ClassName } from '../common/common'; export interface TdBaseTableProps { @@ -119,6 +120,16 @@ export interface BaseTableCol { * @default '' */ colKey?: string; + /** + * 单元格和表头内容超出时,是否显示省略号。如果仅希望单元格超出省略,可设置 `ellipsisTitle = false`。
值为 `true`,则超出省略浮层默认显示单元格内容;
值类型为 `Function` 则自定义超出省略浮中层显示的内容。
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }`。
请注意单元格超出省略的两个基本点:1. 内容元素是内联元素或样式(自定义单元格内容时需特别注意);2. 内容超出父元素 + * @default false + */ + ellipsis?: boolean | PopupProps | { props: PopupProps; content: string }; + /** + * 表头内容超出时,是否显示省略号。优先级高于 `ellipsis`。
值为 `true`,则超出省略的浮层默认显示表头全部内容;
值类型为 `Function` 用于自定义超出省略浮层显示的表头内容;
值类型为 `Object`,则自动透传属性到 Popup 组件,可用于调整弹窗方向等特性。
同时透传 Popup 属性和自定义浮层内容,请使用 `{ props: { showOverlay: false }, content: () => 'something' }` + * @default undefinde + */ + ellipsisTitle?: boolean | PopupProps | { props: PopupProps; content: string }; /** * 固定列显示位置 * @default left @@ -170,11 +181,15 @@ export interface BaseTableCellParams { } export type TableColumnClassName = - | ClassName - | ((context: CellData) => ClassName); + ClassName | ((context: CellData) => ClassName); export interface CellData extends BaseTableCellParams { type: 'th' | 'td'; } +export interface BaseTableColParams { + col: BaseTableCol; + colIndex: number; +} + export type DataType = TableRowData;