From 9ab109e8f99fb1bd4e5b4e99b0b814bf34f0b4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A1=E8=89=B2?= Date: Thu, 20 Jul 2023 14:45:17 +0800 Subject: [PATCH] feat(abc:st): add `onCell`, support colSpan and rowSpan merging (#1613) --- packages/abc/st/demo/colspan-rowspan.md | 73 +++++++++++++++++++++ packages/abc/st/index.en-US.md | 1 + packages/abc/st/index.zh-CN.md | 1 + packages/abc/st/st-data-source.ts | 17 ++++- packages/abc/st/st.component.html | 32 +++++---- packages/abc/st/st.component.ts | 21 ++++-- packages/abc/st/st.interfaces.ts | 13 +++- packages/abc/st/st.types.ts | 3 +- packages/abc/st/test/st-data-source.spec.ts | 38 ++++++++++- 9 files changed, 172 insertions(+), 27 deletions(-) create mode 100644 packages/abc/st/demo/colspan-rowspan.md diff --git a/packages/abc/st/demo/colspan-rowspan.md b/packages/abc/st/demo/colspan-rowspan.md new file mode 100644 index 000000000..3051d52ef --- /dev/null +++ b/packages/abc/st/demo/colspan-rowspan.md @@ -0,0 +1,73 @@ +--- +order: 8 +title: + en-US: colSpan and rowSpan + zh-CN: 表格行/列合并 +--- + +## zh-CN + +表格支持行/列合并,使用 `onCell` 进行设置,若返回 `colSpan` 或者 `rowSpan` 设值为 0 时,设置的表格不会渲染。 + +## en-US + +The table supports row/column merging, use `onCell` to set, if return `colSpan` or `rowSpan` is set to 0, the set table will not be rendered. + +```ts +import { Component } from '@angular/core'; + +import { STColumn, STData, STOnCellResult } from '@delon/abc/st'; + +// In the fifth row, other columns are merged into first column +// by setting it's colSpan to be 0 +const sharedOnCell = (_: STData, index: number): STOnCellResult => { + if (index === 1) { + return { colSpan: 0 }; + } + + return {}; +}; + +@Component({ + selector: 'app-demo', + template: ` + ` +}) +export class DemoComponent { + url = `/users?total=2&field=list`; + params = { a: 1, b: 2 }; + + columns: STColumn[] = [ + { title: '编号', index: 'id', sort: true, width: 100 }, + { title: '头像', type: 'img', index: 'picture.thumbnail', width: 60 }, + { + title: '邮箱', + index: 'email', + onCell: (_, index) => ({ + colSpan: index === 1 ? 5 : 1 + }) + }, + { + title: 'first', + index: 'name.first', + sort: true, + className: 'text-center', + onCell: (_, index) => { + if (index === 3) { + return { rowSpan: 2 }; + } + // These two are merged into above cell + if (index === 4) { + return { rowSpan: 0 }; + } + if (index === 1) { + return { colSpan: 0 }; + } + + return {}; + } + }, + { title: 'last', index: 'name.last', onCell: sharedOnCell } + ]; +} +``` diff --git a/packages/abc/st/index.en-US.md b/packages/abc/st/index.en-US.md index 84cddca7c..27ee448e1 100644 --- a/packages/abc/st/index.en-US.md +++ b/packages/abc/st/index.en-US.md @@ -266,6 +266,7 @@ class TestComponent { | `[format]` | Format value of this column | `(item: STData, col: STColumn, index: number) => string` | - | | `[className]` | Class name of this column, e.g: `text-center`, `text-right`, `text-error`, pls refer to [Style Tools](/theme/tools) | `string` | - | | `[colSpan]` | Span of this column's title | `number` | - | +| `[onCell]` | Set props on per cell | `(item: T, index: number) => STOnCellResult;` | - | | `[sort]` | Sort config of this column, Remote Data Configuration**Priority** Rule:
`true` allow sorting, should be auto generate compose `compare: (a, b) => a[index] - b[index]` method when data is local
`string` corresponding `key` value | `true,string,STColumnSort` | - | | `[filter]` | Filter config of this column | `STColumnFilter` | - | | `[selections]` | Config of type is checkbox | `STColumnSelection[]` | - | diff --git a/packages/abc/st/index.zh-CN.md b/packages/abc/st/index.zh-CN.md index 1dcd1b460..576478668 100644 --- a/packages/abc/st/index.zh-CN.md +++ b/packages/abc/st/index.zh-CN.md @@ -266,6 +266,7 @@ class TestComponent { | `[format]` | 格式化列值 | `(item: STData, col: STColumn, index: number) => string` | - | | `[className]` | 列 `class` 属性值,例如:`text-center` 居中; `text-right` 居右; `text-error` 异常色,更多参考[样式工具类](/theme/tools) | `string` | - | | `[colSpan]` | 合并列 | `number` | - | +| `[onCell]` | 设置单元格属性 | `(item: T, index: number) => STOnCellResult;` | - | | `[sort]` | 排序配置项,远程数据配置**优先**规则:
`true` 表示允许排序,且若数据源为本地数据时会自动生成 `compare: (a, b) => a[index] - b[index]` 方法
`string` 表示远程数据排序相对应 `key` 值 | `true,string,STColumnSort` | - | | `[filter]` | 过滤配置项 | `STColumnFilter` | - | | `[selections]` | 选择功能配置 | `STColumnSelection[]` | - | diff --git a/packages/abc/st/st-data-source.ts b/packages/abc/st/st-data-source.ts index 181f6d87b..15027887b 100644 --- a/packages/abc/st/st-data-source.ts +++ b/packages/abc/st/st-data-source.ts @@ -19,6 +19,7 @@ import { STData, STMultiSort, STMultiSortResultType, + STOnCellResult, STPage, STReq, STReqReNameType, @@ -324,15 +325,27 @@ export class STDataSource { return this.http.request(method, url, reqOptions); } + getCell(c: STColumn, item: STData, idx: number): STOnCellResult { + const onCellResult = typeof c.onCell === 'function' ? c.onCell(item, idx) : null; + const mergedColSpan = onCellResult?.colSpan ?? 1; + const mergedRowSpan = onCellResult?.rowSpan ?? 1; + return { + colSpan: mergedColSpan <= 0 ? null : mergedColSpan, + rowSpan: mergedRowSpan <= 0 ? null : mergedRowSpan + } as STOnCellResult; + } + optimizeData(options: { columns: _STColumn[]; result: STData[]; rowClassName?: STRowClassName | null }): STData[] { const { result, columns, rowClassName } = options; for (let i = 0, len = result.length; i < len; i++) { result[i]._values = columns.map(c => { + const props = this.getCell(c, result[i], i); + if (Array.isArray(c.buttons) && c.buttons.length > 0) { - return { buttons: this.genButtons(c.buttons, result[i], c), _text: '' }; + return { buttons: this.genButtons(c.buttons, result[i], c), _text: '', props }; } - return this.get(result[i], c, i); + return { ...this.get(result[i], c, i), props }; }); result[i]._rowClassName = [rowClassName ? rowClassName(result[i], i) : null, result[i].className] .filter(w => !!w) diff --git a/packages/abc/st/st.component.html b/packages/abc/st/st.component.html index 68d931792..20993b762 100644 --- a/packages/abc/st/st.component.html +++ b/packages/abc/st/st.component.html @@ -167,19 +167,25 @@ (click)="_stopPropagation($event)" nzWidth="50px" > - - - - - - + + + + + + + + w.type === 'no') - .forEach(c => - this._data.forEach((i, idx) => { + this._columns.forEach(c => { + this._data.forEach((i, idx) => { + const values = i._values as _STDataValue[]; + if (c.type === 'no') { const text = `${this.dataSource.getNoIndex(i, c, idx)}`; - i._values![c.__point!] = { text, _text: text, org: idx, safeType: 'text' } as _STDataValue; - }) - ); + values[c.__point!] = { + text, + _text: text, + org: idx, + safeType: 'text' + } as _STDataValue; + } + values[c.__point!].props = this.dataSource.getCell(c, i, idx); + }); + }); return this.refreshData(); } diff --git a/packages/abc/st/st.interfaces.ts b/packages/abc/st/st.interfaces.ts index ed8e65c08..b90fb2836 100644 --- a/packages/abc/st/st.interfaces.ts +++ b/packages/abc/st/st.interfaces.ts @@ -357,9 +357,11 @@ export interface STColumn { */ className?: NgClassType; /** - * 合并列 + * Table cell supports `colSpan` and `rowSpan`. When each of them is set to 0, the cell will not be rendered. + * + * 表格支持行/列合并,若返回的 `colSpan` 或者 `rowSpan` 设值为 0 时表示不会渲染 */ - colSpan?: number; + onCell?: (item: T, index: number) => STOnCellResult; /** * 数字格式,`type=number` 有效 */ @@ -432,7 +434,7 @@ export interface STColumn { * 分组表头 */ children?: Array>; - + colSpan?: number; rowSpan?: number; /** @@ -1280,3 +1282,8 @@ export interface STCustomRequestOptions { url: string; options: STRequestOptions; } + +export interface STOnCellResult { + rowSpan?: number | null; + colSpan?: number | null; +} diff --git a/packages/abc/st/st.types.ts b/packages/abc/st/st.types.ts index 6d8016e6a..8837bed09 100644 --- a/packages/abc/st/st.types.ts +++ b/packages/abc/st/st.types.ts @@ -2,7 +2,7 @@ import { TemplateRef } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; -import { STColumn, STColumnButton, STColumnSafeType, STData, STSortMap } from './st.interfaces'; +import { STColumn, STColumnButton, STColumnSafeType, STData, STOnCellResult, STSortMap } from './st.interfaces'; /** * @inner @@ -74,4 +74,5 @@ export interface _STDataValue { color?: string; safeType: STColumnSafeType; buttons?: _STColumnButton[]; + props?: STOnCellResult | null; } diff --git a/packages/abc/st/test/st-data-source.spec.ts b/packages/abc/st/test/st-data-source.spec.ts index 482b67f7e..b0a60ff50 100644 --- a/packages/abc/st/test/st-data-source.spec.ts +++ b/packages/abc/st/test/st-data-source.spec.ts @@ -10,7 +10,7 @@ import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { STDataSource, STDataSourceOptions } from '../st-data-source'; import { ST_DEFAULT_CONFIG } from '../st.config'; import { STColumnButton, STColumnFilterMenu, STData } from '../st.interfaces'; -import { _STColumn } from '../st.types'; +import { _STColumn, _STDataValue } from '../st.types'; const DEFAULT = { pi: 1, @@ -908,6 +908,42 @@ describe('abc: table: data-souce', () => { done(); }); }); + describe('#onCell', () => { + it('should be working', done => { + const index = 1; + options.data = genData(); + options.columns = [ + { index: 'a', onCell: (_, idx) => ({ colSpan: idx === index ? 2 : 1 }) }, + { index: 'b', onCell: (_, idx) => ({ rowSpan: idx === index ? 2 : 1 }) } + ] as _STColumn[]; + srv.process(options).subscribe(res => { + const values = res.list[index]._values as _STDataValue[]; + expect(values[0].props?.colSpan).toBe(2); + expect(values[0].props?.rowSpan).toBe(1); + + expect(values[1].props?.colSpan).toBe(1); + expect(values[1].props?.rowSpan).toBe(2); + done(); + }); + }); + it('should be ignore when set 0', done => { + const index = 1; + options.data = genData(); + options.columns = [ + { index: 'a', onCell: (_, idx) => ({ colSpan: idx === index ? 0 : 1 }) }, + { index: 'b', onCell: (_, idx) => ({ rowSpan: idx === index ? 0 : 1 }) } + ] as _STColumn[]; + srv.process(options).subscribe(res => { + const values = res.list[index]._values as _STDataValue[]; + expect(values[0].props?.colSpan).toBeNull(); + expect(values[0].props?.rowSpan).toBe(1); + + expect(values[1].props?.colSpan).toBe(1); + expect(values[1].props?.rowSpan).toBeNull(); + done(); + }); + }); + }); }); describe('[buttons]', () => {