From c8039784a834b91a78313e05bf35d45c3232e665 Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Wed, 20 May 2026 14:56:57 +0800 Subject: [PATCH 01/37] feat(data-table): add DataTable component with customizable features - Implemented a new DataTable component with built-in column configuration and pagination. - Added styles for the DataTable in dataTable.less. - Created documentation in both English and Chinese for usage examples and API details. - Introduced props for row and cell customization, including rendering options. - Added support for local and remote pagination modes. - Implemented loading and empty states for better user experience. - Created example usage in Vue with various configurations. - Added locale support for English and Chinese languages. - Defined TypeScript types for props and context to enhance type safety. --- .../varlet-ui/src/data-table/DataTable.vue | 310 +++++++++++++++++ .../src/data-table/__tests__/index.spec.js | 181 ++++++++++ .../varlet-ui/src/data-table/dataTable.less | 140 ++++++++ .../varlet-ui/src/data-table/docs/en-US.md | 316 ++++++++++++++++++ .../varlet-ui/src/data-table/docs/zh-CN.md | 316 ++++++++++++++++++ .../src/data-table/example/index.vue | 162 +++++++++ .../src/data-table/example/locale/en-US.ts | 15 + .../src/data-table/example/locale/index.ts | 11 + .../src/data-table/example/locale/zh-CN.ts | 15 + packages/varlet-ui/src/data-table/index.ts | 12 + packages/varlet-ui/src/data-table/props.ts | 100 ++++++ .../varlet-ui/src/pagination/docs/en-US.md | 2 +- packages/varlet-ui/src/pagination/props.ts | 1 - .../__snapshots__/index.spec.js.snap | 54 +++ .../varlet-ui/src/themes/dark/dataTable.ts | 20 ++ packages/varlet-ui/src/themes/dark/index.ts | 2 + .../src/themes/md3-dark/dataTable.ts | 20 ++ .../varlet-ui/src/themes/md3-dark/index.ts | 2 + .../src/themes/md3-light/dataTable.ts | 20 ++ .../varlet-ui/src/themes/md3-light/index.ts | 2 + packages/varlet-ui/types/dataTable.d.ts | 89 +++++ packages/varlet-ui/types/index.d.ts | 2 + packages/varlet-ui/types/styleVars.d.ts | 20 +- packages/varlet-ui/varlet.config.mjs | 8 + 24 files changed, 1817 insertions(+), 3 deletions(-) create mode 100644 packages/varlet-ui/src/data-table/DataTable.vue create mode 100644 packages/varlet-ui/src/data-table/__tests__/index.spec.js create mode 100644 packages/varlet-ui/src/data-table/dataTable.less create mode 100644 packages/varlet-ui/src/data-table/docs/en-US.md create mode 100644 packages/varlet-ui/src/data-table/docs/zh-CN.md create mode 100644 packages/varlet-ui/src/data-table/example/index.vue create mode 100644 packages/varlet-ui/src/data-table/example/locale/en-US.ts create mode 100644 packages/varlet-ui/src/data-table/example/locale/index.ts create mode 100644 packages/varlet-ui/src/data-table/example/locale/zh-CN.ts create mode 100644 packages/varlet-ui/src/data-table/index.ts create mode 100644 packages/varlet-ui/src/data-table/props.ts create mode 100644 packages/varlet-ui/src/themes/dark/dataTable.ts create mode 100644 packages/varlet-ui/src/themes/md3-dark/dataTable.ts create mode 100644 packages/varlet-ui/src/themes/md3-light/dataTable.ts create mode 100644 packages/varlet-ui/types/dataTable.d.ts diff --git a/packages/varlet-ui/src/data-table/DataTable.vue b/packages/varlet-ui/src/data-table/DataTable.vue new file mode 100644 index 00000000000..3dcd0919f6f --- /dev/null +++ b/packages/varlet-ui/src/data-table/DataTable.vue @@ -0,0 +1,310 @@ + + + + + diff --git a/packages/varlet-ui/src/data-table/__tests__/index.spec.js b/packages/varlet-ui/src/data-table/__tests__/index.spec.js new file mode 100644 index 00000000000..51474ef6257 --- /dev/null +++ b/packages/varlet-ui/src/data-table/__tests__/index.spec.js @@ -0,0 +1,181 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, test } from 'vite-plus/test' +import { createApp, h } from 'vue' +import DataTable from '..' +import VarDataTable from '../DataTable' + +test('data-table use', () => { + const app = createApp({}).use(DataTable) + expect(app.component(DataTable.name)).toBeTruthy() +}) + +const columns = [ + { key: 'name', title: 'Name' }, + { key: 'role', title: 'Role' }, +] + +const data = [ + { id: 1, name: 'Ada', role: 'Admin' }, + { id: 2, name: 'Linus', role: 'Maintainer' }, + { id: 3, name: 'Taylor', role: 'Designer' }, +] + +describe('test data-table component props', () => { + test('should render basic table content', () => { + const wrapper = mount(VarDataTable, { + props: { + columns, + data, + pagination: false, + }, + }) + + expect(wrapper.findAll('thead th')).toHaveLength(2) + expect(wrapper.findAll('tbody tr')).toHaveLength(3) + expect(wrapper.text()).toContain('Ada') + wrapper.unmount() + }) + + test('should slice data in local pagination mode', () => { + const wrapper = mount(VarDataTable, { + props: { + columns, + data, + page: 2, + pageSize: 2, + }, + }) + + expect(wrapper.text()).not.toContain('Ada') + expect(wrapper.text()).toContain('Taylor') + wrapper.unmount() + }) + + test('should not slice data in remote pagination mode', () => { + const wrapper = mount(VarDataTable, { + props: { + columns, + data: [data[2]], + page: 2, + pageSize: 2, + total: 3, + remote: true, + }, + }) + + expect(wrapper.findAll('tbody tr')).toHaveLength(1) + expect(wrapper.text()).toContain('Taylor') + wrapper.unmount() + }) + + test('should support render function', () => { + const wrapper = mount(VarDataTable, { + props: { + columns: [ + { + key: 'name', + title: 'Name', + render: ({ row }) => h('strong', row.name), + }, + ], + data: [data[0]], + pagination: false, + }, + }) + + expect(wrapper.find('strong').text()).toBe('Ada') + wrapper.unmount() + }) + + test('should support rowProps and cellProps', () => { + const wrapper = mount(VarDataTable, { + props: { + columns: [ + { + key: 'name', + title: 'Name', + cellProps: ({ row, rowIndex }) => ({ + class: 'custom-cell', + 'data-name': row.name, + 'data-row-index': rowIndex, + }), + }, + ], + data: [data[0]], + pagination: false, + rowProps: ({ row, rowIndex }) => ({ + class: 'custom-row', + 'data-id': row.id, + 'data-row-index': rowIndex, + }), + }, + }) + + expect(wrapper.find('tbody tr').classes()).toContain('custom-row') + expect(wrapper.find('tbody tr').attributes('data-id')).toBe('1') + expect(wrapper.find('tbody td').classes()).toContain('custom-cell') + expect(wrapper.find('tbody td').attributes('data-name')).toBe('Ada') + wrapper.unmount() + }) + + test('should render empty text', () => { + const wrapper = mount(VarDataTable, { + props: { + columns, + data: [], + pagination: false, + emptyText: 'Nothing here', + }, + }) + + expect(wrapper.find('.var-data-table__empty').text()).toBe('Nothing here') + wrapper.unmount() + }) + + test('should render loading state', () => { + const wrapper = mount(VarDataTable, { + props: { + columns, + data: [], + loading: true, + }, + }) + + expect(wrapper.find('.var-loading__body').exists()).toBe(true) + wrapper.unmount() + }) + + test('should keep footer inside loading content', () => { + const wrapper = mount(VarDataTable, { + props: { + columns, + data, + loading: true, + }, + }) + + expect(wrapper.find('.var-loading__content .var-data-table__footer').exists()).toBe(true) + wrapper.unmount() + }) + + test('should emit pagination updates', async () => { + const onUpdatePage = vi.fn() + const onUpdatePageSize = vi.fn() + + const wrapper = mount(VarDataTable, { + props: { + columns, + data, + 'onUpdate:page': onUpdatePage, + 'onUpdate:pageSize': onUpdatePageSize, + }, + }) + + wrapper.findComponent({ name: 'var-pagination' }).vm.$emit('change', 2, 20) + await wrapper.vm.$nextTick() + + expect(onUpdatePage).toHaveBeenCalledWith(2) + expect(onUpdatePageSize).toHaveBeenCalledWith(20) + wrapper.unmount() + }) +}) diff --git a/packages/varlet-ui/src/data-table/dataTable.less b/packages/varlet-ui/src/data-table/dataTable.less new file mode 100644 index 00000000000..f764c627d8f --- /dev/null +++ b/packages/varlet-ui/src/data-table/dataTable.less @@ -0,0 +1,140 @@ +:root { + --data-table-background: #fff; + --data-table-header-background: #fff; + --data-table-header-color: rgba(0, 0, 0, 0.6); + --data-table-row-color: #555; + --data-table-border-color: var(--color-outline); + --data-table-hover-background: #eee; + --data-table-striped-background: #fff; + --data-table-empty-color: var(--color-text-disabled); + --data-table-border-radius: 2px; + --data-table-cell-padding: 0 16px; + --data-table-cell-min-width: 120px; + --data-table-cell-font-size: 16px; + --data-table-header-font-size: 14px; + --data-table-row-height: 46px; + --data-table-row-height-small: 40px; + --data-table-row-height-large: 52px; + --data-table-footer-padding: 12px 16px; + --data-table-empty-padding: 48px 16px; +} + +.var-data-table { + width: 100%; + border-radius: var(--data-table-border-radius); + background: var(--data-table-background); + + * { + box-sizing: border-box; + } + + &--cell-bordered { + border: 1px solid var(--data-table-border-color); + } + + &__main { + position: relative; + width: 100%; + overflow: auto; + } + + &__table { + min-width: 100%; + border-spacing: 0; + border-collapse: collapse; + line-height: normal; + table-layout: auto; + } + + &__header-row { + background: var(--data-table-header-background); + border-bottom: 1px solid var(--data-table-border-color); + } + + &__row { + background: var(--data-table-background); + border-bottom: 1px solid var(--data-table-border-color); + transition: background-color 0.25s; + + &:hover { + background: var(--data-table-hover-background); + } + } + + &--with-footer { + .var-data-table__row:last-child { + border-bottom: 0; + } + } + + &__row--striped:nth-child(even) { + background: var(--data-table-striped-background); + } + + &__cell { + height: var(--data-table-row-height); + padding: var(--data-table-cell-padding); + min-width: var(--data-table-cell-min-width); + font-size: var(--data-table-cell-font-size); + vertical-align: middle; + + .var-data-table--cell-bordered & { + &:not(:last-child) { + border-right: 1px solid var(--data-table-border-color); + } + } + } + + &__header-cell { + color: var(--data-table-header-color); + font-size: var(--data-table-header-font-size); + font-weight: 500; + background: var(--data-table-header-background); + } + + &__body-cell { + color: var(--data-table-row-color); + } + + &__empty { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + } + + &__empty { + color: var(--data-table-empty-color); + padding: var(--data-table-empty-padding); + } + + &__footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: var(--data-table-footer-padding); + border-top: 1px solid var(--data-table-border-color); + background: var(--data-table-background); + overflow: auto; + --pagination-item-background: transparent; + --pagination-hover-bg-color: hsla(var(--hsl-text), 0.08); + --pagination-item-margin: 0 4px; + + .var-pagination { + margin-left: auto; + } + } + + &--small { + .var-data-table__cell { + height: var(--data-table-row-height-small); + } + } + + &--large { + .var-data-table__cell { + height: var(--data-table-row-height-large); + } + } +} diff --git a/packages/varlet-ui/src/data-table/docs/en-US.md b/packages/varlet-ui/src/data-table/docs/en-US.md new file mode 100644 index 00000000000..e77ff15651b --- /dev/null +++ b/packages/varlet-ui/src/data-table/docs/en-US.md @@ -0,0 +1,316 @@ +# DataTable + +### Intro + +A higher-level data table component based on `Table`, with built-in column configuration and pagination. + +### Basic Usage + +```html + + + +``` + +### Cell Bordered + +```html + +``` + +### Striped + +```html + +``` + +### Sizes + +```html + +``` + +### Column Options + +Use `width`, `minWidth`, `align`, and `titleAlign` to control column width and alignment. + +```html + + + +``` + +### Custom Props + +Use `row-props` and `column.cellProps` to pass native attributes to rows and cells. + +```html + + + +``` + +### Custom Render + +Use `column.render` to customize the cell content. + +```html + + + +``` + +### Pager Pagination + +Use `pagination` to configure built-in pager pagination. + +```html + +``` + +### Local Pagination + +In local pagination mode, pass the full data set and let the component slice it internally. + +```html + +``` + +### Remote Pagination + +Set `remote` to stop internal slicing. In this mode, `data` should be the current page data and `total` should be controlled externally. + +```html + + + +``` + +### Empty Text + +```html + +``` + +### Loading + +```html + +``` + +## API + +### Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `data` | Data source. Full data in local mode, current page data in remote mode | _any[]_ | `[]` | +| `columns` | Column definitions | _DataTableColumn[]_ | `[]` | +| `row-key` | Row key field or getter | _string \| ((row, rowIndex) => string \| number)_ | `'id'` | +| `row-props` | Native row props, supports object or function | _DataTableRowProps_ | `-` | +| `loading` | Whether to show loading overlay | _boolean_ | `false` | +| `pagination` | Built-in pagination config | _boolean \| DataTablePagination_ | `true` | +| `remote` | Whether to enable remote pagination mode | _boolean_ | `false` | +| `v-model:page` | Current page | _number \| string_ | `1` | +| `v-model:page-size` | Current page size | _number \| string_ | `10` | +| `total` | Total item count in remote mode | _number \| string_ | `-` | +| `elevation` | Elevation level | _boolean \| number \| string_ | `true` | +| `striped` | Whether to show striped rows | _boolean_ | `false` | +| `cell-bordered` | Whether to show outer border and cell dividers | _boolean_ | `false` | +| `size` | Table size | _'small' \| 'normal' \| 'large'_ | `'normal'` | +| `empty-text` | Empty text | _string_ | `Locale.selectEmptyText` | + +### DataTableColumn + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `key` | Unique column key | _string_ | `-` | +| `title` | Column title | _string_ | `-` | +| `width` | Column width | _number \| string_ | `-` | +| `minWidth` | Column min width | _number \| string_ | `-` | +| `align` | Body cell align | _'left' \| 'center' \| 'right'_ | `'left'` | +| `titleAlign` | Header title align | _'left' \| 'center' \| 'right'_ | `align` | +| `cellProps` | Native cell props, supports object or function | _DataTableCellProps_ | `-` | +| `render` | Custom cell render | _`(context) => VNodeChild`_ | `-` | + +### DataTableRowProps + +`DataTableRowProps = HTMLAttributes | ((context: { row, rowIndex, pageRowIndex }) => HTMLAttributes | undefined)` + +### DataTableCellProps + +`DataTableCellProps = HTMLAttributes | ((context: { row, rowIndex, pageRowIndex, column }) => HTMLAttributes | undefined)` + +### DataTablePagination + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `simple` | Whether to use simple pagination | _boolean_ | `false` | +| `disabled` | Whether to disable pagination | _boolean_ | `false` | +| `showSizeChanger` | Whether to show page size changer | _boolean_ | `true` | +| `showQuickJumper` | Whether to show quick jumper | _boolean_ | `false` | +| `maxPagerCount` | Max pager count | _number_ | `5` | +| `sizeOption` | Page size options | _number[]_ | `[10, 20, 50, 100]` | +| `showTotal` | Total text renderer | _`(total: number, range: [number, number]) => string`_ | `-` | + +### Slots + +| Name | Description | Parameters | +| --- | --- | --- | +| `loading` | Custom loading content | `-` | +| `footer-prefix` | Content before pagination | `-` | + +### Style Variables + +| Variable | Default | +| --- | --- | +| `--data-table-background` | `#fff` | +| `--data-table-header-background` | `#fff` | +| `--data-table-header-color` | `rgba(0, 0, 0, 0.6)` | +| `--data-table-row-color` | `#555` | +| `--data-table-border-color` | `var(--color-outline)` | +| `--data-table-hover-background` | `#eee` | +| `--data-table-striped-background` | `#fff` | +| `--data-table-empty-color` | `var(--color-text-disabled)` | +| `--data-table-border-radius` | `2px` | +| `--data-table-cell-padding` | `0 16px` | +| `--data-table-cell-min-width` | `120px` | +| `--data-table-cell-font-size` | `16px` | +| `--data-table-header-font-size` | `14px` | +| `--data-table-row-height` | `46px` | +| `--data-table-row-height-small` | `40px` | +| `--data-table-row-height-large` | `52px` | +| `--data-table-footer-padding` | `12px 16px` | +| `--data-table-empty-padding` | `48px 16px` | diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md new file mode 100644 index 00000000000..ea0b77dd004 --- /dev/null +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -0,0 +1,316 @@ +# DataTable + +### 介绍 + +基于 `Table` 的更高阶数据表格组件,内置列配置与分页能力。 + +### 基本使用 + +```html + + + +``` + +### 单元格分割线 + +```html + +``` + +### 斑马纹 + +```html + +``` + +### 尺寸 + +```html + +``` + +### 列配置 + +通过 `width`、`minWidth`、`align` 和 `titleAlign` 调整列宽和对齐方式。 + +```html + + + +``` + +### 属性透传 + +通过 `row-props` 和 `column.cellProps` 向行和单元格透传原生属性。 + +```html + + + +``` + +### 自定义渲染 + +通过 `column.render` 自定义单元格内容。 + +```html + + + +``` + +### 页码分页 + +通过 `pagination` 配置内置页码分页。 + +```html + +``` + +### 本地分页 + +本地分页模式下传入全量数据,由组件内部切片。 + +```html + +``` + +### 远程分页 + +设置 `remote` 后组件不再切片数据。此时 `data` 应为当前页数据,`total` 由外部控制。 + +```html + + + +``` + +### 空态文案 + +```html + +``` + +### 加载状态 + +```html + +``` + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `data` | 数据源。本地分页下传全量数据,远程分页下传当前页数据 | _any[]_ | `[]` | +| `columns` | 列配置 | _DataTableColumn[]_ | `[]` | +| `row-key` | 行 key 字段或获取函数 | _string \| ((row, rowIndex) => string \| number)_ | `'id'` | +| `row-props` | 行属性透传,支持对象或函数 | _DataTableRowProps_ | `-` | +| `loading` | 是否显示加载遮罩 | _boolean_ | `false` | +| `pagination` | 内置分页配置 | _boolean \| DataTablePagination_ | `true` | +| `remote` | 是否启用远程分页模式 | _boolean_ | `false` | +| `v-model:page` | 当前页码 | _number \| string_ | `1` | +| `v-model:page-size` | 当前每页条数 | _number \| string_ | `10` | +| `total` | 远程分页总条数 | _number \| string_ | `-` | +| `elevation` | 海拔层级 | _boolean \| number \| string_ | `true` | +| `striped` | 是否显示斑马纹 | _boolean_ | `false` | +| `cell-bordered` | 是否显示外边框和单元格分割线 | _boolean_ | `false` | +| `size` | 表格尺寸 | _'small' \| 'normal' \| 'large'_ | `'normal'` | +| `empty-text` | 空数据文案 | _string_ | `Locale.selectEmptyText` | + +### DataTableColumn + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `key` | 列唯一 key | _string_ | `-` | +| `title` | 列标题 | _string_ | `-` | +| `width` | 列宽 | _number \| string_ | `-` | +| `minWidth` | 列最小宽度 | _number \| string_ | `-` | +| `align` | 内容对齐方式 | _'left' \| 'center' \| 'right'_ | `'left'` | +| `titleAlign` | 表头标题对齐方式 | _'left' \| 'center' \| 'right'_ | `align` | +| `cellProps` | 单元格属性透传,支持对象或函数 | _DataTableCellProps_ | `-` | +| `render` | 自定义单元格渲染 | _`(context) => VNodeChild`_ | `-` | + +### DataTableRowProps + +`DataTableRowProps = HTMLAttributes \| ((context: { row, rowIndex, pageRowIndex }) => HTMLAttributes \| undefined)` + +### DataTableCellProps + +`DataTableCellProps = HTMLAttributes \| ((context: { row, rowIndex, pageRowIndex, column }) => HTMLAttributes \| undefined)` + +### DataTablePagination + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `simple` | 是否使用简洁分页 | _boolean_ | `false` | +| `disabled` | 是否禁用分页 | _boolean_ | `false` | +| `showSizeChanger` | 是否显示每页条数切换器 | _boolean_ | `true` | +| `showQuickJumper` | 是否显示快速跳转 | _boolean_ | `false` | +| `maxPagerCount` | 最多显示的页码数量 | _number_ | `5` | +| `sizeOption` | 每页条数选项 | _number[]_ | `[10, 20, 50, 100]` | +| `showTotal` | 总数文案渲染函数 | _`(total: number, range: [number, number]) => string`_ | `-` | + +### Slots + +| 名称 | 说明 | 参数 | +| --- | --- | --- | +| `loading` | 自定义加载内容 | `-` | +| `footer-prefix` | 分页前置内容 | `-` | + +### 样式变量 + +| 变量名 | 默认值 | +| --- | --- | +| `--data-table-background` | `#fff` | +| `--data-table-header-background` | `#fff` | +| `--data-table-header-color` | `rgba(0, 0, 0, 0.6)` | +| `--data-table-row-color` | `#555` | +| `--data-table-border-color` | `var(--color-outline)` | +| `--data-table-hover-background` | `#eee` | +| `--data-table-striped-background` | `#fff` | +| `--data-table-empty-color` | `var(--color-text-disabled)` | +| `--data-table-border-radius` | `2px` | +| `--data-table-cell-padding` | `0 16px` | +| `--data-table-cell-min-width` | `120px` | +| `--data-table-cell-font-size` | `16px` | +| `--data-table-header-font-size` | `14px` | +| `--data-table-row-height` | `46px` | +| `--data-table-row-height-small` | `40px` | +| `--data-table-row-height-large` | `52px` | +| `--data-table-footer-padding` | `12px 16px` | +| `--data-table-empty-padding` | `48px 16px` | diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue new file mode 100644 index 00000000000..3b295975576 --- /dev/null +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -0,0 +1,162 @@ + + + diff --git a/packages/varlet-ui/src/data-table/example/locale/en-US.ts b/packages/varlet-ui/src/data-table/example/locale/en-US.ts new file mode 100644 index 00000000000..10ea327d498 --- /dev/null +++ b/packages/varlet-ui/src/data-table/example/locale/en-US.ts @@ -0,0 +1,15 @@ +export default { + basicUsage: 'Basic Usage', + cellBordered: 'Cell Bordered', + striped: 'Striped', + sizes: 'Sizes', + columnOptions: 'Column Options', + customProps: 'Custom Props', + customRender: 'Custom Render', + compactPagination: 'Pager Pagination', + localPagination: 'Local Pagination', + remotePagination: 'Remote Pagination', + emptyText: 'Empty Text', + emptyTip: 'No Data', + loading: 'Loading', +} diff --git a/packages/varlet-ui/src/data-table/example/locale/index.ts b/packages/varlet-ui/src/data-table/example/locale/index.ts new file mode 100644 index 00000000000..89fca66084c --- /dev/null +++ b/packages/varlet-ui/src/data-table/example/locale/index.ts @@ -0,0 +1,11 @@ +import { useLocale } from '../../../locale' +import enUS from './en-US' +import zhCN from './zh-CN' + +const { add, use, t } = useLocale() + +add('zh-CN', zhCN) +add('en-US', enUS) +use('zh-CN') + +export { use, t } diff --git a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts new file mode 100644 index 00000000000..358a1017de4 --- /dev/null +++ b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts @@ -0,0 +1,15 @@ +export default { + basicUsage: '基本使用', + cellBordered: '单元格分割线', + striped: '斑马纹', + sizes: '尺寸', + columnOptions: '列配置', + customProps: '属性透传', + customRender: '自定义渲染', + compactPagination: '页码分页', + localPagination: '本地分页', + remotePagination: '远程分页', + emptyText: '空态文案', + emptyTip: '暂无数据', + loading: '加载状态', +} diff --git a/packages/varlet-ui/src/data-table/index.ts b/packages/varlet-ui/src/data-table/index.ts new file mode 100644 index 00000000000..d507fa2edf3 --- /dev/null +++ b/packages/varlet-ui/src/data-table/index.ts @@ -0,0 +1,12 @@ +import { withInstall, withPropsDefaultsSetter } from '../utils/components' +import DataTable from './DataTable.vue' +import { props as dataTableProps } from './props' + +withInstall(DataTable) +withPropsDefaultsSetter(DataTable, dataTableProps) + +export { dataTableProps } + +export const _DataTableComponent = DataTable + +export default DataTable diff --git a/packages/varlet-ui/src/data-table/props.ts b/packages/varlet-ui/src/data-table/props.ts new file mode 100644 index 00000000000..b4e8e6733eb --- /dev/null +++ b/packages/varlet-ui/src/data-table/props.ts @@ -0,0 +1,100 @@ +import { type HTMLAttributes, type PropType, type VNodeChild } from 'vue' +import { defineListenerProp } from '../utils/components' + +export type DataTableKey = string | number + +export type DataTableAlign = 'left' | 'center' | 'right' + +export type DataTableRowKey = keyof Row | string | ((row: Row, rowIndex: number) => DataTableKey) + +export interface DataTableColumnRenderContext { + row: Row + rowIndex: number + pageRowIndex: number + column: DataTableColumn +} + +export interface DataTableRowPropsContext { + row: Row + rowIndex: number + pageRowIndex: number +} + +export interface DataTableCellPropsContext extends DataTableRowPropsContext { + column: DataTableColumn +} + +export type DataTableRowProps = + | HTMLAttributes + | ((context: DataTableRowPropsContext) => HTMLAttributes | undefined) + +export type DataTableCellProps = + | HTMLAttributes + | ((context: DataTableCellPropsContext) => HTMLAttributes | undefined) + +export interface DataTableColumn { + key: string + title: string + width?: string | number + minWidth?: string | number + align?: DataTableAlign + titleAlign?: DataTableAlign + cellProps?: DataTableCellProps + render?: (context: DataTableColumnRenderContext) => VNodeChild +} + +export interface DataTablePagination { + simple?: boolean + disabled?: boolean + showSizeChanger?: boolean + showQuickJumper?: boolean + maxPagerCount?: number + sizeOption?: number[] + showTotal?: (total: number, range: [number, number]) => string +} + +export const props = { + data: { + type: Array as PropType, + default: () => [], + }, + columns: { + type: Array as PropType, + default: () => [], + }, + rowKey: { + type: [String, Function] as PropType, + default: 'id', + }, + rowProps: { + type: [Object, Function] as PropType, + }, + loading: Boolean, + pagination: { + type: [Boolean, Object] as PropType, + default: true, + }, + remote: Boolean, + page: { + type: [Number, String], + default: 1, + }, + pageSize: { + type: [Number, String], + default: 10, + }, + total: [Number, String], + elevation: { + type: [Boolean, Number, String], + default: true, + }, + striped: Boolean, + cellBordered: Boolean, + size: { + type: String as PropType<'small' | 'normal' | 'large'>, + default: 'normal', + }, + emptyText: String, + 'onUpdate:page': defineListenerProp<(page: number) => void>(), + 'onUpdate:pageSize': defineListenerProp<(pageSize: number) => void>(), +} diff --git a/packages/varlet-ui/src/pagination/docs/en-US.md b/packages/varlet-ui/src/pagination/docs/en-US.md index 9dd9ede38a1..0d90f96c57c 100644 --- a/packages/varlet-ui/src/pagination/docs/en-US.md +++ b/packages/varlet-ui/src/pagination/docs/en-US.md @@ -171,4 +171,4 @@ Here are the CSS variables used by the component. Styles can be customized using | `--pagination-bg-disabled-color` | `var(--color-disabled)` | | `--pagination-size-line-height` | `24px` | | `--pagination-size-padding` | `0 4px` | -| `--pagination-quick-jumper-margin` | `0 10px` | \ No newline at end of file +| `--pagination-quick-jumper-margin` | `0 10px` | diff --git a/packages/varlet-ui/src/pagination/props.ts b/packages/varlet-ui/src/pagination/props.ts index 6b763e7a2fa..f92c93e67b4 100644 --- a/packages/varlet-ui/src/pagination/props.ts +++ b/packages/varlet-ui/src/pagination/props.ts @@ -2,7 +2,6 @@ import { type PropType } from 'vue' import { defineListenerProp } from '../utils/components' export type Range = [number, number] - export const props = { current: [Number, String], size: { diff --git a/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap index 22c85b91463..8d8d08ad2c3 100644 --- a/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap +++ b/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap @@ -302,6 +302,24 @@ exports[`dark theme 1`] = ` "--counter-input-width": "28px", "--counter-padding": "0 4px", "--cubic-bezier": "cubic-bezier(0.25, 0.8, 0.5, 1)", + "--data-table-background": "#303030", + "--data-table-border-color": "var(--color-outline)", + "--data-table-border-radius": "2px", + "--data-table-cell-font-size": "16px", + "--data-table-cell-min-width": "120px", + "--data-table-cell-padding": "0 16px", + "--data-table-empty-color": "var(--color-text-disabled)", + "--data-table-empty-padding": "48px 16px", + "--data-table-footer-padding": "12px 16px", + "--data-table-header-background": "#303030", + "--data-table-header-color": "rgba(255, 255, 255, 0.6)", + "--data-table-header-font-size": "14px", + "--data-table-hover-background": "#4c4b4b", + "--data-table-row-color": "#fff", + "--data-table-row-height": "46px", + "--data-table-row-height-large": "52px", + "--data-table-row-height-small": "40px", + "--data-table-striped-background": "#303030", "--date-picker-actions-padding": "0 8px 12px 8px", "--date-picker-body-background-color": "#303030", "--date-picker-body-height": "280px", @@ -1258,6 +1276,24 @@ exports[`md3Dark theme 1`] = ` "--counter-input-width": "28px", "--counter-padding": "0 4px", "--cubic-bezier": "cubic-bezier(0.25, 0.8, 0.5, 1)", + "--data-table-background": "var(--color-surface-container-highest)", + "--data-table-border-color": "var(--color-outline)", + "--data-table-border-radius": "2px", + "--data-table-cell-font-size": "16px", + "--data-table-cell-min-width": "120px", + "--data-table-cell-padding": "0 16px", + "--data-table-empty-color": "var(--color-text-disabled)", + "--data-table-empty-padding": "48px 16px", + "--data-table-footer-padding": "12px 16px", + "--data-table-header-background": "var(--color-surface-container-highest)", + "--data-table-header-color": "rgba(255, 255, 255, 0.6)", + "--data-table-header-font-size": "14px", + "--data-table-hover-background": "var(--color-surface-container-highest)", + "--data-table-row-color": "#fff", + "--data-table-row-height": "46px", + "--data-table-row-height-large": "52px", + "--data-table-row-height-small": "40px", + "--data-table-striped-background": "var(--color-surface-container-highest)", "--date-picker-actions-padding": "20px", "--date-picker-body-background-color": "var(--color-surface-container-high)", "--date-picker-body-height": "300px", @@ -2199,6 +2235,24 @@ exports[`md3Light theme 1`] = ` "--counter-input-width": "28px", "--counter-padding": "0 4px", "--cubic-bezier": "cubic-bezier(0.25, 0.8, 0.5, 1)", + "--data-table-background": "var(--color-surface-container-low)", + "--data-table-border-color": "var(--color-outline)", + "--data-table-border-radius": "2px", + "--data-table-cell-font-size": "16px", + "--data-table-cell-min-width": "120px", + "--data-table-cell-padding": "0 16px", + "--data-table-empty-color": "var(--color-text-disabled)", + "--data-table-empty-padding": "48px 16px", + "--data-table-footer-padding": "12px 16px", + "--data-table-header-background": "var(--color-surface-container-low)", + "--data-table-header-color": "rgba(0, 0, 0, 0.6)", + "--data-table-header-font-size": "14px", + "--data-table-hover-background": "var(--color-surface-container-low)", + "--data-table-row-color": "#555", + "--data-table-row-height": "46px", + "--data-table-row-height-large": "52px", + "--data-table-row-height-small": "40px", + "--data-table-striped-background": "var(--color-surface-container-low)", "--date-picker-actions-padding": "20px", "--date-picker-body-background-color": "var(--color-surface-container-high)", "--date-picker-body-height": "300px", diff --git a/packages/varlet-ui/src/themes/dark/dataTable.ts b/packages/varlet-ui/src/themes/dark/dataTable.ts new file mode 100644 index 00000000000..5e88204a497 --- /dev/null +++ b/packages/varlet-ui/src/themes/dark/dataTable.ts @@ -0,0 +1,20 @@ +export default { + '--data-table-background': '#303030', + '--data-table-header-background': '#303030', + '--data-table-header-color': 'rgba(255, 255, 255, 0.6)', + '--data-table-row-color': '#fff', + '--data-table-border-color': 'var(--color-outline)', + '--data-table-hover-background': '#4c4b4b', + '--data-table-striped-background': '#303030', + '--data-table-empty-color': 'var(--color-text-disabled)', + '--data-table-border-radius': '2px', + '--data-table-cell-padding': '0 16px', + '--data-table-cell-min-width': '120px', + '--data-table-cell-font-size': '16px', + '--data-table-header-font-size': '14px', + '--data-table-row-height': '46px', + '--data-table-row-height-small': '40px', + '--data-table-row-height-large': '52px', + '--data-table-footer-padding': '12px 16px', + '--data-table-empty-padding': '48px 16px', +} diff --git a/packages/varlet-ui/src/themes/dark/index.ts b/packages/varlet-ui/src/themes/dark/index.ts index 80945b38a4d..c6f0a53ce3a 100644 --- a/packages/varlet-ui/src/themes/dark/index.ts +++ b/packages/varlet-ui/src/themes/dark/index.ts @@ -18,6 +18,7 @@ import code from './code' import collapse from './collapse' import countdown from './countdown' import counter from './counter' +import dataTable from './dataTable' import datePicker from './datePicker' import dialog from './dialog' import divider from './divider' @@ -193,6 +194,7 @@ export default { ...bottomNavigation, ...countdown, ...counter, + ...dataTable, ...fab, ...floatingPanel, ...formDetails, diff --git a/packages/varlet-ui/src/themes/md3-dark/dataTable.ts b/packages/varlet-ui/src/themes/md3-dark/dataTable.ts new file mode 100644 index 00000000000..3457d227347 --- /dev/null +++ b/packages/varlet-ui/src/themes/md3-dark/dataTable.ts @@ -0,0 +1,20 @@ +export default { + '--data-table-background': 'var(--color-surface-container-highest)', + '--data-table-header-background': 'var(--color-surface-container-highest)', + '--data-table-header-color': 'rgba(255, 255, 255, 0.6)', + '--data-table-row-color': '#fff', + '--data-table-border-color': 'var(--color-outline)', + '--data-table-hover-background': 'var(--color-surface-container-highest)', + '--data-table-striped-background': 'var(--color-surface-container-highest)', + '--data-table-empty-color': 'var(--color-text-disabled)', + '--data-table-border-radius': '2px', + '--data-table-cell-padding': '0 16px', + '--data-table-cell-min-width': '120px', + '--data-table-cell-font-size': '16px', + '--data-table-header-font-size': '14px', + '--data-table-row-height': '46px', + '--data-table-row-height-small': '40px', + '--data-table-row-height-large': '52px', + '--data-table-footer-padding': '12px 16px', + '--data-table-empty-padding': '48px 16px', +} diff --git a/packages/varlet-ui/src/themes/md3-dark/index.ts b/packages/varlet-ui/src/themes/md3-dark/index.ts index ac1b561cecf..cec6d033c2e 100644 --- a/packages/varlet-ui/src/themes/md3-dark/index.ts +++ b/packages/varlet-ui/src/themes/md3-dark/index.ts @@ -18,6 +18,7 @@ import code from './code' import collapse from './collapse' import countdown from './countdown' import counter from './counter' +import dataTable from './dataTable' import datePicker from './datePicker' import dialog from './dialog' import divider from './divider' @@ -193,6 +194,7 @@ export default { ...select, ...option, ...counter, + ...dataTable, ...switchThemes, ...slider, ...uploader, diff --git a/packages/varlet-ui/src/themes/md3-light/dataTable.ts b/packages/varlet-ui/src/themes/md3-light/dataTable.ts new file mode 100644 index 00000000000..1ac414a2cac --- /dev/null +++ b/packages/varlet-ui/src/themes/md3-light/dataTable.ts @@ -0,0 +1,20 @@ +export default { + '--data-table-background': 'var(--color-surface-container-low)', + '--data-table-header-background': 'var(--color-surface-container-low)', + '--data-table-header-color': 'rgba(0, 0, 0, 0.6)', + '--data-table-row-color': '#555', + '--data-table-border-color': 'var(--color-outline)', + '--data-table-hover-background': 'var(--color-surface-container-low)', + '--data-table-striped-background': 'var(--color-surface-container-low)', + '--data-table-empty-color': 'var(--color-text-disabled)', + '--data-table-border-radius': '2px', + '--data-table-cell-padding': '0 16px', + '--data-table-cell-min-width': '120px', + '--data-table-cell-font-size': '16px', + '--data-table-header-font-size': '14px', + '--data-table-row-height': '46px', + '--data-table-row-height-small': '40px', + '--data-table-row-height-large': '52px', + '--data-table-footer-padding': '12px 16px', + '--data-table-empty-padding': '48px 16px', +} diff --git a/packages/varlet-ui/src/themes/md3-light/index.ts b/packages/varlet-ui/src/themes/md3-light/index.ts index a3842212abf..1a6489d3930 100644 --- a/packages/varlet-ui/src/themes/md3-light/index.ts +++ b/packages/varlet-ui/src/themes/md3-light/index.ts @@ -18,6 +18,7 @@ import code from './code' import collapse from './collapse' import countdown from './countdown' import counter from './counter' +import dataTable from './dataTable' import datePicker from './datePicker' import dialog from './dialog' import divider from './divider' @@ -195,6 +196,7 @@ export default { ...badge, ...countdown, ...counter, + ...dataTable, ...divider, ...formDetails, ...icon, diff --git a/packages/varlet-ui/types/dataTable.d.ts b/packages/varlet-ui/types/dataTable.d.ts new file mode 100644 index 00000000000..ac25113b741 --- /dev/null +++ b/packages/varlet-ui/types/dataTable.d.ts @@ -0,0 +1,89 @@ +import { HTMLAttributes, VNode, VNodeChild } from 'vue' +import { BasicAttributes, ListenerProp, SetPropsDefaults, VarComponent } from './varComponent' + +export declare const dataTableProps: Record + +export type DataTableKey = string | number + +export type DataTableAlign = 'left' | 'center' | 'right' + +export type DataTableRowKey = keyof Row | string | ((row: Row, rowIndex: number) => DataTableKey) + +export interface DataTableColumnRenderContext { + row: Row + rowIndex: number + pageRowIndex: number + column: DataTableColumn +} + +export interface DataTableRowPropsContext { + row: Row + rowIndex: number + pageRowIndex: number +} + +export interface DataTableCellPropsContext extends DataTableRowPropsContext { + column: DataTableColumn +} + +export type DataTableRowProps = + | HTMLAttributes + | ((context: DataTableRowPropsContext) => HTMLAttributes | undefined) + +export type DataTableCellProps = + | HTMLAttributes + | ((context: DataTableCellPropsContext) => HTMLAttributes | undefined) + +export interface DataTableColumn { + key: string + title: string + width?: string | number + minWidth?: string | number + align?: DataTableAlign + titleAlign?: DataTableAlign + cellProps?: DataTableCellProps + render?: (context: DataTableColumnRenderContext) => VNodeChild +} + +export interface DataTablePagination { + simple?: boolean + disabled?: boolean + showSizeChanger?: boolean + showQuickJumper?: boolean + maxPagerCount?: number + sizeOption?: number[] + showTotal?: (total: number, range: [number, number]) => string +} + +export interface DataTableProps extends BasicAttributes { + data?: any[] + columns?: DataTableColumn[] + rowKey?: DataTableRowKey + rowProps?: DataTableRowProps + loading?: boolean + pagination?: boolean | DataTablePagination + remote?: boolean + page?: number | string + pageSize?: number | string + total?: number | string + elevation?: boolean | string | number + striped?: boolean + cellBordered?: boolean + size?: 'small' | 'normal' | 'large' + emptyText?: string + 'onUpdate:page'?: ListenerProp<(page: number) => void> + 'onUpdate:pageSize'?: ListenerProp<(pageSize: number) => void> +} + +export class DataTable extends VarComponent { + static setPropsDefaults: SetPropsDefaults + + $props: DataTableProps + + $slots: { + loading(): VNode[] + footerPrefix(): VNode[] + } +} + +export class _DataTableComponent extends DataTable {} diff --git a/packages/varlet-ui/types/index.d.ts b/packages/varlet-ui/types/index.d.ts index f69e458d926..360be390c5e 100644 --- a/packages/varlet-ui/types/index.d.ts +++ b/packages/varlet-ui/types/index.d.ts @@ -31,6 +31,7 @@ export * from './context' export * from './countTo' export * from './countdown' export * from './counter' +export * from './dataTable' export * from './datePicker' export * from './dialog' export * from './divider' @@ -133,6 +134,7 @@ declare module 'vue' { VarCountTo: typeof import('@varlet/ui')['_CountToComponent'] VarCountdown: typeof import('@varlet/ui')['_CountdownComponent'] VarCounter: typeof import('@varlet/ui')['_CounterComponent'] + VarDataTable: typeof import('@varlet/ui')['_DataTableComponent'] VarDatePicker: typeof import('@varlet/ui')['_DatePickerComponent'] VarDialog: typeof import('@varlet/ui')['_DialogComponent'] VarDivider: typeof import('@varlet/ui')['_DividerComponent'] diff --git a/packages/varlet-ui/types/styleVars.d.ts b/packages/varlet-ui/types/styleVars.d.ts index 20416091fb6..f71c22b396b 100644 --- a/packages/varlet-ui/types/styleVars.d.ts +++ b/packages/varlet-ui/types/styleVars.d.ts @@ -268,6 +268,24 @@ interface BaseStyleVars { '--count-to-text-font-size'?: string '--countdown-text-color'?: string '--countdown-text-font-size'?: string + '--data-table-background'?: string + '--data-table-header-background'?: string + '--data-table-header-color'?: string + '--data-table-row-color'?: string + '--data-table-border-color'?: string + '--data-table-hover-background'?: string + '--data-table-striped-background'?: string + '--data-table-empty-color'?: string + '--data-table-border-radius'?: string + '--data-table-cell-padding'?: string + '--data-table-cell-min-width'?: string + '--data-table-cell-font-size'?: string + '--data-table-header-font-size'?: string + '--data-table-row-height'?: string + '--data-table-row-height-small'?: string + '--data-table-row-height-large'?: string + '--data-table-footer-padding'?: string + '--data-table-empty-padding'?: string '--counter-padding'?: string '--counter-font-color'?: string '--counter-background'?: string @@ -962,4 +980,4 @@ interface BaseStyleVars { [key: PropertyKey]: string } -export interface StyleVars extends FormatStyleVars {} \ No newline at end of file +export interface StyleVars extends FormatStyleVars {} diff --git a/packages/varlet-ui/varlet.config.mjs b/packages/varlet-ui/varlet.config.mjs index 4598d059b93..4c02684bddb 100644 --- a/packages/varlet-ui/varlet.config.mjs +++ b/packages/varlet-ui/varlet.config.mjs @@ -384,6 +384,14 @@ export default defineConfig({ doc: 'table', type: 2, }, + { + text: { + 'zh-CN': 'DataTable 数据表格', + 'en-US': 'DataTable', + }, + doc: 'data-table', + type: 2, + }, { text: { 'zh-CN': 'Watermark 水印', From a60fd067093247aed6448512f448a5bbff7b1c6c Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Wed, 20 May 2026 23:22:32 +0800 Subject: [PATCH 02/37] feat(data-table): add selection column support with checkboxes - Implemented a selection column in the DataTable component allowing users to select multiple rows. - Added `checkedRowKeys` prop to manage selected rows. - Updated rendering logic to include checkboxes for selection. - Enhanced tests to cover selection functionality. - Modified documentation to include examples for selection usage. --- .../varlet-ui/src/data-table/DataTable.vue | 217 ++++++++++++++---- .../src/data-table/__tests__/index.spec.js | 45 +++- .../varlet-ui/src/data-table/dataTable.less | 26 ++- .../varlet-ui/src/data-table/docs/en-US.md | 78 ++++--- .../varlet-ui/src/data-table/docs/zh-CN.md | 78 ++++--- .../src/data-table/example/index.vue | 138 +++++------ .../src/data-table/example/locale/en-US.ts | 5 +- .../src/data-table/example/locale/zh-CN.ts | 5 +- packages/varlet-ui/src/data-table/props.ts | 36 ++- 9 files changed, 431 insertions(+), 197 deletions(-) diff --git a/packages/varlet-ui/src/data-table/DataTable.vue b/packages/varlet-ui/src/data-table/DataTable.vue index 3dcd0919f6f..7b2e38de5ba 100644 --- a/packages/varlet-ui/src/data-table/DataTable.vue +++ b/packages/varlet-ui/src/data-table/DataTable.vue @@ -14,15 +14,31 @@
+ + + + @@ -31,24 +47,33 @@
- {{ column.title }} + +
- + +
- {{ resolvedEmptyText }} + + {{ resolvedEmptyText }} +
@@ -78,14 +103,23 @@ ``` @@ -141,6 +131,28 @@ const data = [ ``` +### Selection + +Use `type: 'selection'` to render a checkbox column. Bind `v-model:checked-row-keys` to control the selected rows. + +```html + + + +``` + ### Pager Pagination Use `pagination` to configure built-in pager pagination. @@ -170,7 +182,6 @@ In local pagination mode, pass the full data set and let the component slice it :columns="columns" :data="data" :pagination="{ - simple: true, showSizeChanger: false, showQuickJumper: false, }" @@ -209,6 +220,10 @@ const columns = [{ key: 'name', title: 'Name' }] :columns="columns" :data="currentPageData" :total="allData.length" + :pagination="{ + showSizeChanger: false, + showQuickJumper: false, + }" remote /> @@ -218,7 +233,9 @@ const columns = [{ key: 'name', title: 'Name' }] ```html ``` @@ -239,40 +256,33 @@ const columns = [{ key: 'name', title: 'Name' }] | `data` | Data source. Full data in local mode, current page data in remote mode | _any[]_ | `[]` | | `columns` | Column definitions | _DataTableColumn[]_ | `[]` | | `row-key` | Row key field or getter | _string \| ((row, rowIndex) => string \| number)_ | `'id'` | -| `row-props` | Native row props, supports object or function | _DataTableRowProps_ | `-` | +| `row-props` | Native row props, supports object or function | _object \| (context) => object_ | `-` | | `loading` | Whether to show loading overlay | _boolean_ | `false` | | `pagination` | Built-in pagination config | _boolean \| DataTablePagination_ | `true` | | `remote` | Whether to enable remote pagination mode | _boolean_ | `false` | | `v-model:page` | Current page | _number \| string_ | `1` | | `v-model:page-size` | Current page size | _number \| string_ | `10` | +| `v-model:checked-row-keys` | Selected row keys | _Array_ | `[]` | | `total` | Total item count in remote mode | _number \| string_ | `-` | | `elevation` | Elevation level | _boolean \| number \| string_ | `true` | -| `striped` | Whether to show striped rows | _boolean_ | `false` | -| `cell-bordered` | Whether to show outer border and cell dividers | _boolean_ | `false` | +| `cell-bordered` | Whether to show cell dividers | _boolean_ | `false` | | `size` | Table size | _'small' \| 'normal' \| 'large'_ | `'normal'` | -| `empty-text` | Empty text | _string_ | `Locale.selectEmptyText` | ### DataTableColumn | Prop | Description | Type | Default | | --- | --- | --- | --- | +| `type` | Column type. Set to `selection` to render checkboxes | _'selection'_ | `-` | | `key` | Unique column key | _string_ | `-` | | `title` | Column title | _string_ | `-` | | `width` | Column width | _number \| string_ | `-` | | `minWidth` | Column min width | _number \| string_ | `-` | | `align` | Body cell align | _'left' \| 'center' \| 'right'_ | `'left'` | | `titleAlign` | Header title align | _'left' \| 'center' \| 'right'_ | `align` | -| `cellProps` | Native cell props, supports object or function | _DataTableCellProps_ | `-` | +| `selectable` | Whether the row can be selected. Only works on selection columns | _`(context) => boolean`_ | `-` | +| `cellProps` | Native cell props, supports object or function | _object \| (context) => object_ | `-` | | `render` | Custom cell render | _`(context) => VNodeChild`_ | `-` | -### DataTableRowProps - -`DataTableRowProps = HTMLAttributes | ((context: { row, rowIndex, pageRowIndex }) => HTMLAttributes | undefined)` - -### DataTableCellProps - -`DataTableCellProps = HTMLAttributes | ((context: { row, rowIndex, pageRowIndex, column }) => HTMLAttributes | undefined)` - ### DataTablePagination | Prop | Description | Type | Default | @@ -289,6 +299,7 @@ const columns = [{ key: 'name', title: 'Name' }] | Name | Description | Parameters | | --- | --- | --- | +| `empty` | Custom empty content | `-` | | `loading` | Custom loading content | `-` | | `footer-prefix` | Content before pagination | `-` | @@ -302,7 +313,6 @@ const columns = [{ key: 'name', title: 'Name' }] | `--data-table-row-color` | `#555` | | `--data-table-border-color` | `var(--color-outline)` | | `--data-table-hover-background` | `#eee` | -| `--data-table-striped-background` | `#fff` | | `--data-table-empty-color` | `var(--color-text-disabled)` | | `--data-table-border-radius` | `2px` | | `--data-table-cell-padding` | `0 16px` | diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index ea0b77dd004..95032b0d88f 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -33,14 +33,6 @@ const data = [ ``` -### 斑马纹 - -```html - -``` - ### 尺寸 ```html @@ -88,19 +80,17 @@ const columns = [ }), }, ] + +const customRowProps = ({ row, rowIndex }) => ({ + style: { + backgroundColor: rowIndex === 0 ? 'hsla(var(--hsl-primary), 0.08)' : undefined, + }, + title: row.name, +}) ``` @@ -141,6 +131,28 @@ const data = [ ``` +### 选择列 + +通过 `type: 'selection'` 渲染复选框列,并使用 `v-model:checked-row-keys` 控制选中行。 + +```html + + + +``` + ### 页码分页 通过 `pagination` 配置内置页码分页。 @@ -170,7 +182,6 @@ const data = [ :columns="columns" :data="data" :pagination="{ - simple: true, showSizeChanger: false, showQuickJumper: false, }" @@ -209,6 +220,10 @@ const columns = [{ key: 'name', title: '姓名' }] :columns="columns" :data="currentPageData" :total="allData.length" + :pagination="{ + showSizeChanger: false, + showQuickJumper: false, + }" remote /> @@ -218,7 +233,9 @@ const columns = [{ key: 'name', title: '姓名' }] ```html ``` @@ -239,40 +256,33 @@ const columns = [{ key: 'name', title: '姓名' }] | `data` | 数据源。本地分页下传全量数据,远程分页下传当前页数据 | _any[]_ | `[]` | | `columns` | 列配置 | _DataTableColumn[]_ | `[]` | | `row-key` | 行 key 字段或获取函数 | _string \| ((row, rowIndex) => string \| number)_ | `'id'` | -| `row-props` | 行属性透传,支持对象或函数 | _DataTableRowProps_ | `-` | +| `row-props` | 行属性透传,支持对象或函数 | _object \| (context) => object_ | `-` | | `loading` | 是否显示加载遮罩 | _boolean_ | `false` | | `pagination` | 内置分页配置 | _boolean \| DataTablePagination_ | `true` | | `remote` | 是否启用远程分页模式 | _boolean_ | `false` | | `v-model:page` | 当前页码 | _number \| string_ | `1` | | `v-model:page-size` | 当前每页条数 | _number \| string_ | `10` | +| `v-model:checked-row-keys` | 选中行的 key 集合 | _Array_ | `[]` | | `total` | 远程分页总条数 | _number \| string_ | `-` | | `elevation` | 海拔层级 | _boolean \| number \| string_ | `true` | -| `striped` | 是否显示斑马纹 | _boolean_ | `false` | -| `cell-bordered` | 是否显示外边框和单元格分割线 | _boolean_ | `false` | +| `cell-bordered` | 是否显示单元格分割线 | _boolean_ | `false` | | `size` | 表格尺寸 | _'small' \| 'normal' \| 'large'_ | `'normal'` | -| `empty-text` | 空数据文案 | _string_ | `Locale.selectEmptyText` | ### DataTableColumn | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | +| `type` | 列类型。设为 `selection` 时渲染勾选列 | _'selection'_ | `-` | | `key` | 列唯一 key | _string_ | `-` | | `title` | 列标题 | _string_ | `-` | | `width` | 列宽 | _number \| string_ | `-` | | `minWidth` | 列最小宽度 | _number \| string_ | `-` | | `align` | 内容对齐方式 | _'left' \| 'center' \| 'right'_ | `'left'` | | `titleAlign` | 表头标题对齐方式 | _'left' \| 'center' \| 'right'_ | `align` | -| `cellProps` | 单元格属性透传,支持对象或函数 | _DataTableCellProps_ | `-` | +| `selectable` | 是否允许选中该行,仅对选择列生效 | _`(context) => boolean`_ | `-` | +| `cellProps` | 单元格属性透传,支持对象或函数 | _object \| (context) => object_ | `-` | | `render` | 自定义单元格渲染 | _`(context) => VNodeChild`_ | `-` | -### DataTableRowProps - -`DataTableRowProps = HTMLAttributes \| ((context: { row, rowIndex, pageRowIndex }) => HTMLAttributes \| undefined)` - -### DataTableCellProps - -`DataTableCellProps = HTMLAttributes \| ((context: { row, rowIndex, pageRowIndex, column }) => HTMLAttributes \| undefined)` - ### DataTablePagination | 参数 | 说明 | 类型 | 默认值 | @@ -289,6 +299,7 @@ const columns = [{ key: 'name', title: '姓名' }] | 名称 | 说明 | 参数 | | --- | --- | --- | +| `empty` | 自定义空态内容 | `-` | | `loading` | 自定义加载内容 | `-` | | `footer-prefix` | 分页前置内容 | `-` | @@ -302,7 +313,6 @@ const columns = [{ key: 'name', title: '姓名' }] | `--data-table-row-color` | `#555` | | `--data-table-border-color` | `var(--color-outline)` | | `--data-table-hover-background` | `#eee` | -| `--data-table-striped-background` | `#fff` | | `--data-table-empty-color` | `var(--color-text-disabled)` | | `--data-table-border-radius` | `2px` | | `--data-table-cell-padding` | `0 16px` | diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index 3b295975576..5f8a4d8299c 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -1,9 +1,10 @@ - + + +``` + ### Selection -Use `type: 'selection'` to render a checkbox column. Bind `v-model:checked-row-keys` to control the selected rows. +Use `type: 'selection'` to render a selection column. Bind `v-model:checked-row-keys` to control the selected rows. Set `multiple: false` for single selection and `disabled` to disable the whole selection column or specific rows. ```html + + +``` + ### Pager Pagination Use `pagination` to configure built-in pager pagination. @@ -229,6 +285,16 @@ const columns = [{ key: 'name', title: 'Name' }] ``` +### Sticky Header + +Set `max-height` to make the table body scroll internally while keeping the header fixed at the top of the scroll container. + +```html + +``` + ### Empty Text ```html @@ -264,6 +330,7 @@ const columns = [{ key: 'name', title: 'Name' }] | `v-model:page-size` | Current page size | _number \| string_ | `10` | | `v-model:checked-row-keys` | Selected row keys | _Array_ | `[]` | | `total` | Total item count in remote mode | _number \| string_ | `-` | +| `max-height` | Max height of the table body. When set, the header stays fixed and the body scrolls internally | _number \| string_ | `-` | | `elevation` | Elevation level | _boolean \| number \| string_ | `true` | | `cell-bordered` | Whether to show cell dividers | _boolean_ | `false` | | `size` | Table size | _'small' \| 'normal' \| 'large'_ | `'normal'` | @@ -272,14 +339,20 @@ const columns = [{ key: 'name', title: 'Name' }] | Prop | Description | Type | Default | | --- | --- | --- | --- | -| `type` | Column type. Set to `selection` to render checkboxes | _'selection'_ | `-` | +| `type` | Column type. Supports `selection` and `expand` | _'selection' \| 'expand'_ | `-` | | `key` | Unique column key | _string_ | `-` | | `title` | Column title | _string_ | `-` | +| `multiple` | Whether the selection column allows multiple rows | _boolean_ | `true` | +| `disabled` | Whether selection is disabled. Supports `boolean` or `(context) => boolean` | _boolean \| `(context) => boolean`_ | `false` | +| `expandable` | Whether the row can be expanded. Only works on expand columns | _`(context) => boolean`_ | `-` | +| `renderExpand` | Custom expanded content. Only works on expand columns | _`(context) => VNodeChild`_ | `-` | | `width` | Column width | _number \| string_ | `-` | | `minWidth` | Column min width | _number \| string_ | `-` | | `align` | Body cell align | _'left' \| 'center' \| 'right'_ | `'left'` | | `titleAlign` | Header title align | _'left' \| 'center' \| 'right'_ | `align` | -| `selectable` | Whether the row can be selected. Only works on selection columns | _`(context) => boolean`_ | `-` | +| `titleColSpan` | Header col span. Set to `0` to hide the current header cell | _number_ | `1` | +| `colSpan` | Body cell col span. Supports a number or function. Return `0` to hide the current cell | _number \| `(context) => number`_ | `1` | +| `rowSpan` | Body cell row span. Supports a number or function. Return `0` to hide the current cell | _number \| `(context) => number`_ | `1` | | `cellProps` | Native cell props, supports object or function | _object \| (context) => object_ | `-` | | `render` | Custom cell render | _`(context) => VNodeChild`_ | `-` | diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index 1cab86ee376..51486b9cc30 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -131,9 +131,41 @@ const data = [ ``` +### 单元格合并 + +通过 `rowSpan`、`colSpan` 和 `titleColSpan` 控制表体与表头合并。返回 `0` 时当前单元格不渲染,通常配合前一个单元格的跨行或跨列使用。 + +```html + + + +``` + ### 选择列 -通过 `type: 'selection'` 渲染复选框列,并使用 `v-model:checked-row-keys` 控制选中行。 +通过 `type: 'selection'` 渲染选择列,并使用 `v-model:checked-row-keys` 控制选中行。设置 `multiple: false` 可切换为单选,设置 `disabled` 可禁用整列选择或按行禁用。 ```html + + +``` + ### 页码分页 通过 `pagination` 配置内置页码分页。 @@ -229,6 +285,16 @@ const columns = [{ key: 'name', title: '姓名' }] ``` +### 固定表头 + +设置 `max-height` 后,表格主体会在内部滚动,表头会固定在滚动容器顶部。 + +```html + +``` + ### 空态文案 ```html @@ -264,6 +330,7 @@ const columns = [{ key: 'name', title: '姓名' }] | `v-model:page-size` | 当前每页条数 | _number \| string_ | `10` | | `v-model:checked-row-keys` | 选中行的 key 集合 | _Array_ | `[]` | | `total` | 远程分页总条数 | _number \| string_ | `-` | +| `max-height` | 表格主体最大高度。设置后表头固定,内容区域内部滚动 | _number \| string_ | `-` | | `elevation` | 海拔层级 | _boolean \| number \| string_ | `true` | | `cell-bordered` | 是否显示单元格分割线 | _boolean_ | `false` | | `size` | 表格尺寸 | _'small' \| 'normal' \| 'large'_ | `'normal'` | @@ -272,14 +339,20 @@ const columns = [{ key: 'name', title: '姓名' }] | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| `type` | 列类型。设为 `selection` 时渲染勾选列 | _'selection'_ | `-` | +| `type` | 列类型。支持 `selection` 和 `expand` | _'selection' \| 'expand'_ | `-` | | `key` | 列唯一 key | _string_ | `-` | | `title` | 列标题 | _string_ | `-` | +| `multiple` | 选择列是否允许多选,仅对选择列生效 | _boolean_ | `true` | +| `disabled` | 是否禁用选择。支持 `boolean` 或 `(context) => boolean`,仅对选择列生效 | _boolean \| `(context) => boolean`_ | `false` | +| `expandable` | 是否允许展开该行,仅对展开列生效 | _`(context) => boolean`_ | `-` | +| `renderExpand` | 自定义展开内容,仅对展开列生效 | _`(context) => VNodeChild`_ | `-` | | `width` | 列宽 | _number \| string_ | `-` | | `minWidth` | 列最小宽度 | _number \| string_ | `-` | | `align` | 内容对齐方式 | _'left' \| 'center' \| 'right'_ | `'left'` | | `titleAlign` | 表头标题对齐方式 | _'left' \| 'center' \| 'right'_ | `align` | -| `selectable` | 是否允许选中该行,仅对选择列生效 | _`(context) => boolean`_ | `-` | +| `titleColSpan` | 表头列合并数量,设为 `0` 时当前表头不渲染 | _number_ | `1` | +| `colSpan` | 表体单元格列合并数量,支持数字或函数,返回 `0` 时当前单元格不渲染 | _number \| `(context) => number`_ | `1` | +| `rowSpan` | 表体单元格行合并数量,支持数字或函数,返回 `0` 时当前单元格不渲染 | _number \| `(context) => number`_ | `1` | | `cellProps` | 单元格属性透传,支持对象或函数 | _object \| (context) => object_ | `-` | | `render` | 自定义单元格渲染 | _`(context) => VNodeChild`_ | `-` | diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index 5f8a4d8299c..db382c7049e 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -35,6 +35,26 @@ const renderColumns = [ { key: 'status', title: 'Status', render: renderStatus }, ] +const spanColumns = [ + { + key: 'name', + title: 'Identity', + titleColSpan: 2, + rowSpan: ({ rowIndex }) => (rowIndex === 0 ? 2 : 1), + }, + { + key: 'role', + title: 'Role', + colSpan: ({ rowIndex }) => (rowIndex === 0 ? 2 : 1), + }, + { key: 'status', title: 'Status' }, +] + +const spanData = [ + { id: 1, name: 'Ada', role: 'Admin', status: 'Online' }, + { id: 2, name: 'Linus', role: 'Maintainer', status: 'Offline' }, +] + const checkedRowKeys = ref([1, 3]) const selectionColumns = [ { type: 'selection' }, @@ -42,6 +62,28 @@ const selectionColumns = [ { key: 'role', title: 'Role' }, { key: 'status', title: 'Status' }, ] + +const expandColumns = [ + { + type: 'expand', + expandable: ({ row }) => row.status !== 'Busy', + renderExpand: ({ row }) => + h( + 'div', + { + style: { + display: 'grid', + gap: '4px', + }, + }, + [h('strong', `${row.name}`), h('span', `Role: ${row.role}`), h('span', `Status: ${row.status}`)], + ), + }, + { key: 'name', title: 'Name' }, + { key: 'role', title: 'Role' }, + { key: 'status', title: 'Status' }, +] + const selectedRowNames = computed(() => data .filter((row) => checkedRowKeys.value.includes(row.id)) @@ -131,12 +173,18 @@ const remoteData = computed(() => { {{ t('customRender') }} + {{ t('spans') }} + + {{ t('selection') }}
{{ t('selectedRows') }}: {{ selectedRowNames || '-' }}
+ {{ t('expand') }} + + {{ t('pagerPagination') }} @@ -154,6 +202,9 @@ const remoteData = computed(() => { remote /> + {{ t('stickyHeader') }} + + {{ t('emptyText') }} diff --git a/packages/varlet-ui/src/data-table/example/locale/en-US.ts b/packages/varlet-ui/src/data-table/example/locale/en-US.ts index baa23fff6d7..a8fab3e289d 100644 --- a/packages/varlet-ui/src/data-table/example/locale/en-US.ts +++ b/packages/varlet-ui/src/data-table/example/locale/en-US.ts @@ -5,11 +5,14 @@ export default { columnOptions: 'Column Options', customProps: 'Custom Props', customRender: 'Custom Render', + spans: 'Cell Spans', selection: 'Selection', + expand: 'Expand', selectedRows: 'Selected Rows', pagerPagination: 'Pager Pagination', localPagination: 'Local Pagination', remotePagination: 'Remote Pagination', + stickyHeader: 'Sticky Header', emptyText: 'Empty Text', emptyTip: 'No Data', loading: 'Loading', diff --git a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts index 548d7ebc4ae..25779689afd 100644 --- a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts @@ -5,11 +5,14 @@ export default { columnOptions: '列配置', customProps: '属性透传', customRender: '自定义渲染', + spans: '单元格合并', selection: '选择列', + expand: '展开列', selectedRows: '已选行', pagerPagination: '页码分页', localPagination: '本地分页', remotePagination: '远程分页', + stickyHeader: '固定表头', emptyText: '空态文案', emptyTip: '暂无数据', loading: '加载状态', diff --git a/packages/varlet-ui/src/data-table/props.ts b/packages/varlet-ui/src/data-table/props.ts index 61b90c65d83..d6c4ff2bbeb 100644 --- a/packages/varlet-ui/src/data-table/props.ts +++ b/packages/varlet-ui/src/data-table/props.ts @@ -4,6 +4,7 @@ import { defineListenerProp } from '../utils/components' export type DataTableKey = string | number export type DataTableAlign = 'left' | 'center' | 'right' +export type DataTableFixed = 'left' | 'right' export type DataTableRowKey = | Extract @@ -27,10 +28,17 @@ export interface DataTableCellPropsContext extends DataTableRowPropsC column: DataTableColumn } +export type DataTableCellSpan = number | ((context: DataTableCellPropsContext) => number) +export type DataTableSelectionDisabled = boolean | ((context: DataTableRowPropsContext) => boolean) + export interface DataTableSelectionColumnContext extends DataTableRowPropsContext { checked: boolean } +export interface DataTableExpandColumnContext extends DataTableRowPropsContext { + expanded: boolean +} + export type DataTableRowProps = | HTMLAttributes | ((context: DataTableRowPropsContext) => HTMLAttributes | undefined) @@ -40,10 +48,14 @@ export type DataTableCellProps = | ((context: DataTableCellPropsContext) => HTMLAttributes | undefined) export interface DataTableBaseColumn { + fixed?: DataTableFixed width?: string | number minWidth?: string | number align?: DataTableAlign titleAlign?: DataTableAlign + titleColSpan?: number + colSpan?: DataTableCellSpan + rowSpan?: DataTableCellSpan cellProps?: DataTableCellProps } @@ -58,11 +70,24 @@ export interface DataTableSelectionColumn extends DataTableBaseColumn type: 'selection' key?: string title?: string + multiple?: boolean + disabled?: DataTableSelectionDisabled + render?: never +} + +export interface DataTableExpandColumn extends DataTableBaseColumn { + type: 'expand' + key?: string + title?: string render?: never - selectable?: (context: DataTableRowPropsContext) => boolean + expandable?: (context: DataTableRowPropsContext) => boolean + renderExpand: (context: DataTableExpandColumnContext) => VNodeChild } -export type DataTableColumn = DataTableFieldColumn | DataTableSelectionColumn +export type DataTableColumn = + | DataTableFieldColumn + | DataTableSelectionColumn + | DataTableExpandColumn export interface DataTablePagination { simple?: boolean @@ -105,6 +130,7 @@ export const props = { default: 10, }, total: [Number, String], + maxHeight: [Number, String], checkedRowKeys: { type: Array as PropType, default: () => [], diff --git a/packages/varlet-ui/src/menu-select/MenuSelect.vue b/packages/varlet-ui/src/menu-select/MenuSelect.vue index ad7d3aececc..f986a443458 100644 --- a/packages/varlet-ui/src/menu-select/MenuSelect.vue +++ b/packages/varlet-ui/src/menu-select/MenuSelect.vue @@ -29,7 +29,7 @@ ``` -### Flat Table +### Plain Table -Set `elevation` to `0` to remove the card-like shadow and present the table as a flat surface. +Set `plain` to remove the card-like shadow, background, and radius, and present the table as a pure table surface. ```html ``` @@ -345,25 +345,6 @@ const columns = [ ``` -### Pager Pagination - -Use `pagination` to configure built-in pager pagination. - -```html - -``` - ### Local Pagination In local pagination mode, pass the full data set, bind `v-model:page` and `v-model:page-size`, and let the component slice it internally. @@ -531,6 +512,7 @@ const resizableColumns = [ | `total` | Total item count in remote mode | _number_ | `-` | | `max-height` | Max height of the table body. When set, the header stays fixed and the body scrolls internally | _number \| string_ | `-` | | `scroll-x` | Table width used to enable horizontal scrolling. Usually paired with fixed columns | _number \| string_ | `-` | +| `plain` | Whether to render as a plain table without card shadow, background, or radius | _boolean_ | `false` | | `table-layout` | Native `table-layout` value | _'auto' \| 'fixed'_ | `'auto'` | | `tree` | Whether to explicitly enable tree data mode | _boolean_ | `false` | | `cascade` | Whether tree selection should cascade | _boolean_ | `true` | @@ -593,7 +575,9 @@ const resizableColumns = [ | `--data-table-header-cell-text-color` | `rgba(0, 0, 0, 0.6)` | | `--data-table-body-cell-text-color` | `#555` | | `--data-table-border-color` | `var(--color-outline)` | -| `--data-table-row-hover-background` | `#eee` | +| `--data-table-row-hover-background` | `#f5f5f5` | +| `--data-table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | +| `--data-table-plain-row-hover-background` | `hsla(var(--hsl-on-surface), 0.04)` | | `--data-table-empty-text-color` | `var(--color-text-disabled)` | | `--data-table-border-radius` | `2px` | | `--data-table-cell-padding` | `0 16px` | diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index 3b66d31b3e4..701d2347146 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -141,13 +141,13 @@ const data = [ ``` -### 无卡片表格 +### 纯表格 -设置 `elevation` 为 `0` 可以去掉卡片阴影,展示更纯粹的表格形态。 +设置 `plain` 可以一并去掉卡片阴影、背景色和圆角,展示更纯粹的表格形态。 ```html ``` @@ -345,25 +345,6 @@ const columns = [ ``` -### 页码分页 - -通过 `pagination` 配置内置页码分页。 - -```html - -``` - ### 本地分页 本地分页模式下传入全量数据,并绑定 `v-model:page` / `v-model:page-size`,由组件内部切片。 @@ -531,6 +512,7 @@ const resizableColumns = [ | `total` | 远程分页总条数 | _number_ | `-` | | `max-height` | 表格主体最大高度。设置后表头固定,内容区域内部滚动 | _number \| string_ | `-` | | `scroll-x` | 用于开启横向滚动的表格宽度,通常和固定列一起使用 | _number \| string_ | `-` | +| `plain` | 是否以纯表格形态渲染,不带卡片阴影、背景色和圆角 | _boolean_ | `false` | | `table-layout` | 原生 `table-layout` 布局方式 | _'auto' \| 'fixed'_ | `'auto'` | | `tree` | 是否显式开启树形数据 | _boolean_ | `false` | | `cascade` | 树形选择是否开启级联 | _boolean_ | `true` | @@ -593,7 +575,9 @@ const resizableColumns = [ | `--data-table-header-cell-text-color` | `rgba(0, 0, 0, 0.6)` | | `--data-table-body-cell-text-color` | `#555` | | `--data-table-border-color` | `var(--color-outline)` | -| `--data-table-row-hover-background` | `#eee` | +| `--data-table-row-hover-background` | `#f5f5f5` | +| `--data-table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | +| `--data-table-plain-row-hover-background` | `hsla(var(--hsl-on-surface), 0.04)` | | `--data-table-empty-text-color` | `var(--color-text-disabled)` | | `--data-table-border-radius` | `2px` | | `--data-table-cell-padding` | `0 16px` | diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index e5c7675caff..6c29c93a0fc 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -210,13 +210,6 @@ const customRowProps = ({ row, rowIndex }) => ({ title: row.name, }) -const pagerPagination = { - simple: false, - showSizeChanger: false, - showQuickJumper: false, - maxPagerCount: 2, -} - const defaultPagination = { showSizeChanger: false, showQuickJumper: false, @@ -232,13 +225,6 @@ const manyRows = times(48, (index) => ({ status: index % 3 === 0 ? 'Online' : 'Offline', })) -const compactPagedRows = times(12, (index) => ({ - id: index + 1, - name: `Member ${index + 1}`, - role: index % 2 === 0 ? 'Engineer' : 'Operator', - status: index % 3 === 0 ? 'Online' : 'Offline', -})) - const remotePage = ref(1) const remotePageSize = ref(10) const remoteLoading = ref(false) @@ -288,8 +274,8 @@ watch( {{ t('surfaceLow') }} - {{ t('flatTable') }} - + {{ t('plainTable') }} + {{ t('spans') }} @@ -337,9 +323,6 @@ watch( {{ t('expand') }} - {{ t('pagerPagination') }} - - {{ t('localPagination') }} >, default: () => [], diff --git a/packages/varlet-ui/src/table/docs/en-US.md b/packages/varlet-ui/src/table/docs/en-US.md index 409ae1151c1..ad582ed1844 100644 --- a/packages/varlet-ui/src/table/docs/en-US.md +++ b/packages/varlet-ui/src/table/docs/en-US.md @@ -192,13 +192,14 @@ Here are the CSS variables used by the component. Styles can be customized using | Variable | Default | | --- | --- | | `--table-background` | `#fff` | +| `--table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | | `--table-border-radius` | `2px` | | `--table-thead-border-bottom` | `thin solid var(--color-outline)` | | `--table-thead-th-text-color` | `rgba(0, 0, 0, 0.6)` | | `--table-thead-th-text-align` | `left` | | `--table-thead-th-font-size` | `14px` | | `--table-thead-tr-border-bottom` | `thin solid var(--color-outline)` | -| `--table-tbody-tr-hover-background` | `#eee` | +| `--table-tbody-tr-hover-background` | `#f5f5f5` | | `--table-tbody-tr-border-bottom` | `thin solid var(--color-outline)` | | `--table-tbody-td-text-color` | `#555` | | `--table-tbody-td-font-size` | `16px` | diff --git a/packages/varlet-ui/src/table/docs/zh-CN.md b/packages/varlet-ui/src/table/docs/zh-CN.md index 0f20bb73f6e..734f822a90f 100644 --- a/packages/varlet-ui/src/table/docs/zh-CN.md +++ b/packages/varlet-ui/src/table/docs/zh-CN.md @@ -192,13 +192,14 @@ function get(current, size) { | 变量名 | 默认值 | | --- | --- | | `--table-background` | `#fff` | +| `--table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | | `--table-border-radius` | `2px` | | `--table-thead-border-bottom` | `thin solid var(--color-outline)` | | `--table-thead-th-text-color` | `rgba(0, 0, 0, 0.6)` | | `--table-thead-th-text-align` | `left` | | `--table-thead-th-font-size` | `14px` | | `--table-thead-tr-border-bottom` | `thin solid var(--color-outline)` | -| `--table-tbody-tr-hover-background` | `#eee` | +| `--table-tbody-tr-hover-background` | `#f5f5f5` | | `--table-tbody-tr-border-bottom` | `thin solid var(--color-outline)` | | `--table-tbody-td-text-color` | `#555` | | `--table-tbody-td-font-size` | `16px` | diff --git a/packages/varlet-ui/src/table/table.less b/packages/varlet-ui/src/table/table.less index e16fd074990..e69d536d81e 100644 --- a/packages/varlet-ui/src/table/table.less +++ b/packages/varlet-ui/src/table/table.less @@ -1,13 +1,14 @@ :root { --table-background: #fff; --table-surface-low-background: var(--color-surface-container-highest); + --table-surface-low-row-hover-background: var(--color-surface-container-highest); --table-border-radius: 2px; --table-thead-border-bottom: thin solid var(--color-outline); --table-thead-th-text-color: rgba(0, 0, 0, 0.6); --table-thead-th-text-align: left; --table-thead-th-font-size: 14px; --table-thead-tr-border-bottom: thin solid var(--color-outline); - --table-tbody-tr-hover-background: #eee; + --table-tbody-tr-hover-background: #f5f5f5; --table-tbody-tr-border-bottom: thin solid var(--color-outline); --table-tbody-td-text-color: #555; --table-tbody-td-font-size: 16px; @@ -99,6 +100,7 @@ &--surface-low { --table-background: var(--table-surface-low-background); + --table-tbody-tr-hover-background: var(--table-surface-low-row-hover-background); } &__footer { diff --git a/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap index 6278fb00fb4..232905ac2a3 100644 --- a/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap +++ b/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap @@ -303,6 +303,7 @@ exports[`dark theme 1`] = ` "--counter-padding": "0 4px", "--cubic-bezier": "cubic-bezier(0.25, 0.8, 0.5, 1)", "--data-table-background": "#303030", + "--data-table-body-cell-text-color": "#fff", "--data-table-border-color": "var(--color-outline)", "--data-table-border-radius": "2px", "--data-table-cell-font-size": "16px", @@ -310,15 +311,13 @@ exports[`dark theme 1`] = ` "--data-table-empty-padding": "48px 16px", "--data-table-empty-text-color": "var(--color-text-disabled)", "--data-table-footer-padding": "12px 16px", - "--data-table-header-background": "#303030", + "--data-table-header-cell-background": "#303030", + "--data-table-header-cell-text-color": "rgba(255, 255, 255, 0.6)", "--data-table-header-font-size": "14px", - "--data-table-header-text-color": "rgba(255, 255, 255, 0.6)", - "--data-table-hover-background": "#4c4b4b", "--data-table-row-height": "46px", + "--data-table-row-hover-background": "#4c4b4b", "--data-table-row-large-height": "52px", "--data-table-row-small-height": "40px", - "--data-table-row-text-color": "#fff", - "--data-table-striped-background": "#303030", "--date-picker-actions-padding": "0 8px 12px 8px", "--date-picker-body-background-color": "#303030", "--date-picker-body-height": "280px", @@ -848,7 +847,7 @@ exports[`dark theme 1`] = ` "--table-tbody-td-text-align": "left", "--table-tbody-td-text-color": "#fff", "--table-tbody-tr-border-bottom": "thin solid var(--color-outline)", - "--table-tbody-tr-hover-background": "#4c4b4b", + "--table-tbody-tr-hover-background": "#3a3a3a", "--table-thead-border-bottom": "thin solid var(--color-outline)", "--table-thead-th-font-size": "14px", "--table-thead-th-text-align": "left", @@ -1276,6 +1275,7 @@ exports[`md3Dark theme 1`] = ` "--counter-padding": "0 4px", "--cubic-bezier": "cubic-bezier(0.25, 0.8, 0.5, 1)", "--data-table-background": "var(--color-surface-container-highest)", + "--data-table-body-cell-text-color": "#fff", "--data-table-border-color": "var(--color-outline)", "--data-table-border-radius": "2px", "--data-table-cell-font-size": "16px", @@ -1283,15 +1283,13 @@ exports[`md3Dark theme 1`] = ` "--data-table-empty-padding": "48px 16px", "--data-table-empty-text-color": "var(--color-text-disabled)", "--data-table-footer-padding": "12px 16px", - "--data-table-header-background": "var(--color-surface-container-highest)", + "--data-table-header-cell-background": "var(--color-surface-container-highest)", + "--data-table-header-cell-text-color": "rgba(255, 255, 255, 0.6)", "--data-table-header-font-size": "14px", - "--data-table-header-text-color": "rgba(255, 255, 255, 0.6)", - "--data-table-hover-background": "var(--color-surface-container-highest)", "--data-table-row-height": "46px", + "--data-table-row-hover-background": "var(--color-surface-container-highest)", "--data-table-row-large-height": "52px", "--data-table-row-small-height": "40px", - "--data-table-row-text-color": "#fff", - "--data-table-striped-background": "var(--color-surface-container-highest)", "--data-table-surface-low-background": "#1c1b1d", "--date-picker-actions-padding": "20px", "--date-picker-body-background-color": "var(--color-surface-container-high)", @@ -1823,7 +1821,7 @@ exports[`md3Dark theme 1`] = ` "--table-tbody-td-text-align": "left", "--table-tbody-td-text-color": "#fff", "--table-tbody-tr-border-bottom": "thin solid var(--color-outline)", - "--table-tbody-tr-hover-background": "var(--color-surface-container-highest)", + "--table-tbody-tr-hover-background": "var(--color-surface-container-high)", "--table-thead-border-bottom": "thin solid var(--color-outline)", "--table-thead-th-font-size": "14px", "--table-thead-th-text-align": "left", @@ -2235,6 +2233,7 @@ exports[`md3Light theme 1`] = ` "--counter-padding": "0 4px", "--cubic-bezier": "cubic-bezier(0.25, 0.8, 0.5, 1)", "--data-table-background": "var(--color-surface-container-low)", + "--data-table-body-cell-text-color": "#555", "--data-table-border-color": "var(--color-outline)", "--data-table-border-radius": "2px", "--data-table-cell-font-size": "16px", @@ -2242,15 +2241,13 @@ exports[`md3Light theme 1`] = ` "--data-table-empty-padding": "48px 16px", "--data-table-empty-text-color": "var(--color-text-disabled)", "--data-table-footer-padding": "12px 16px", - "--data-table-header-background": "var(--color-surface-container-low)", + "--data-table-header-cell-background": "var(--color-surface-container-low)", + "--data-table-header-cell-text-color": "rgba(0, 0, 0, 0.6)", "--data-table-header-font-size": "14px", - "--data-table-header-text-color": "rgba(0, 0, 0, 0.6)", - "--data-table-hover-background": "var(--color-surface-container-low)", "--data-table-row-height": "46px", + "--data-table-row-hover-background": "var(--color-surface-container-low)", "--data-table-row-large-height": "52px", "--data-table-row-small-height": "40px", - "--data-table-row-text-color": "#555", - "--data-table-striped-background": "var(--color-surface-container-low)", "--data-table-surface-low-background": "var(--color-surface-container-low)", "--date-picker-actions-padding": "20px", "--date-picker-body-background-color": "var(--color-surface-container-high)", @@ -2780,7 +2777,7 @@ exports[`md3Light theme 1`] = ` "--table-tbody-td-text-align": "left", "--table-tbody-td-text-color": "#555", "--table-tbody-tr-border-bottom": "thin solid var(--color-outline)", - "--table-tbody-tr-hover-background": "var(--color-surface-container-low)", + "--table-tbody-tr-hover-background": "var(--color-surface-container-high)", "--table-thead-border-bottom": "thin solid var(--color-outline)", "--table-thead-th-font-size": "14px", "--table-thead-th-text-align": "left", diff --git a/packages/varlet-ui/src/themes/dark/dataTable.ts b/packages/varlet-ui/src/themes/dark/dataTable.ts index 143e753d0fa..6b448d3763b 100644 --- a/packages/varlet-ui/src/themes/dark/dataTable.ts +++ b/packages/varlet-ui/src/themes/dark/dataTable.ts @@ -4,8 +4,9 @@ export default { '--data-table-header-cell-text-color': 'rgba(255, 255, 255, 0.6)', '--data-table-body-cell-text-color': '#fff', '--data-table-border-color': 'var(--color-outline)', - '--data-table-row-hover-background': '#4c4b4b', - '--data-table-striped-background': '#303030', + '--data-table-row-hover-background': '#3a3a3a', + '--data-table-surface-low-row-hover-background': '#2a2a2a', + '--data-table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.08)', '--data-table-empty-text-color': 'var(--color-text-disabled)', '--data-table-border-radius': '2px', '--data-table-cell-padding': '0 16px', diff --git a/packages/varlet-ui/src/themes/dark/table.ts b/packages/varlet-ui/src/themes/dark/table.ts index efaa1db8f6c..b0c5594a8d0 100644 --- a/packages/varlet-ui/src/themes/dark/table.ts +++ b/packages/varlet-ui/src/themes/dark/table.ts @@ -1,9 +1,10 @@ export default { '--table-background': '#303030', + '--table-surface-low-row-hover-background': '#2a2a2a', '--table-thead-th-text-color': 'rgba(255, 255, 255, 0.6)', '--table-thead-th-text-align': 'left', '--table-tbody-td-text-color': '#fff', - '--table-tbody-tr-hover-background': '#4c4b4b', + '--table-tbody-tr-hover-background': '#3a3a3a', '--table-border-radius': '2px', '--table-thead-border-bottom': 'thin solid var(--color-outline)', '--table-thead-th-font-size': '14px', diff --git a/packages/varlet-ui/src/themes/md3-dark/dataTable.ts b/packages/varlet-ui/src/themes/md3-dark/dataTable.ts index f7c06ebf853..5bb8b9d7e9d 100644 --- a/packages/varlet-ui/src/themes/md3-dark/dataTable.ts +++ b/packages/varlet-ui/src/themes/md3-dark/dataTable.ts @@ -5,8 +5,9 @@ export default { '--data-table-header-cell-text-color': 'rgba(255, 255, 255, 0.6)', '--data-table-body-cell-text-color': '#fff', '--data-table-border-color': 'var(--color-outline)', - '--data-table-row-hover-background': 'var(--color-surface-container-highest)', - '--data-table-striped-background': 'var(--color-surface-container-highest)', + '--data-table-row-hover-background': 'var(--color-surface-container-high)', + '--data-table-surface-low-row-hover-background': 'var(--color-surface-container-highest)', + '--data-table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.08)', '--data-table-empty-text-color': 'var(--color-text-disabled)', '--data-table-border-radius': '2px', '--data-table-cell-padding': '0 16px', diff --git a/packages/varlet-ui/src/themes/md3-dark/table.ts b/packages/varlet-ui/src/themes/md3-dark/table.ts index 40d01b53303..6fac5478547 100644 --- a/packages/varlet-ui/src/themes/md3-dark/table.ts +++ b/packages/varlet-ui/src/themes/md3-dark/table.ts @@ -1,9 +1,10 @@ export default { '--table-background': 'var(--color-surface-container-highest)', + '--table-surface-low-row-hover-background': '#1c1b1d', '--table-thead-th-text-color': 'rgba(255, 255, 255, 0.6)', '--table-thead-th-text-align': 'left', '--table-tbody-td-text-color': '#fff', - '--table-tbody-tr-hover-background': 'var(--color-surface-container-highest)', + '--table-tbody-tr-hover-background': 'var(--color-surface-container-high)', '--table-border-radius': '2px', '--table-thead-border-bottom': 'thin solid var(--color-outline)', '--table-thead-th-font-size': '14px', diff --git a/packages/varlet-ui/src/themes/md3-light/dataTable.ts b/packages/varlet-ui/src/themes/md3-light/dataTable.ts index 2128ba89103..7fa59bc1013 100644 --- a/packages/varlet-ui/src/themes/md3-light/dataTable.ts +++ b/packages/varlet-ui/src/themes/md3-light/dataTable.ts @@ -5,8 +5,9 @@ export default { '--data-table-header-cell-text-color': 'rgba(0, 0, 0, 0.6)', '--data-table-body-cell-text-color': '#555', '--data-table-border-color': 'var(--color-outline)', - '--data-table-row-hover-background': 'var(--color-surface-container-low)', - '--data-table-striped-background': 'var(--color-surface-container-low)', + '--data-table-row-hover-background': 'var(--color-surface-container-high)', + '--data-table-surface-low-row-hover-background': 'var(--color-surface-container-highest)', + '--data-table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.04)', '--data-table-empty-text-color': 'var(--color-text-disabled)', '--data-table-border-radius': '2px', '--data-table-cell-padding': '0 16px', diff --git a/packages/varlet-ui/src/themes/md3-light/table.ts b/packages/varlet-ui/src/themes/md3-light/table.ts index 701ddaeba74..73c6bb72c60 100644 --- a/packages/varlet-ui/src/themes/md3-light/table.ts +++ b/packages/varlet-ui/src/themes/md3-light/table.ts @@ -1,6 +1,7 @@ export default { '--table-background': 'var(--color-surface-container-low)', - '--table-tbody-tr-hover-background': 'var(--color-surface-container-low)', + '--table-surface-low-row-hover-background': 'var(--color-surface-container-highest)', + '--table-tbody-tr-hover-background': 'var(--color-surface-container-high)', '--table-border-radius': '2px', '--table-thead-border-bottom': 'thin solid var(--color-outline)', '--table-thead-th-text-color': 'rgba(0, 0, 0, 0.6)', diff --git a/packages/varlet-ui/types/dataTable.d.ts b/packages/varlet-ui/types/dataTable.d.ts index edd1e3a0c88..7f50b90713d 100644 --- a/packages/varlet-ui/types/dataTable.d.ts +++ b/packages/varlet-ui/types/dataTable.d.ts @@ -107,6 +107,7 @@ export interface DataTableProps extends BasicAttributes { surface?: DataTableSurface cascade?: boolean childrenKey?: string + plain?: boolean elevation?: boolean | string | number cellBordered?: boolean tableLayout?: DataTableTableLayout diff --git a/packages/varlet-ui/types/styleVars.d.ts b/packages/varlet-ui/types/styleVars.d.ts index b44ed1c35cc..abc8d87cca4 100644 --- a/packages/varlet-ui/types/styleVars.d.ts +++ b/packages/varlet-ui/types/styleVars.d.ts @@ -274,15 +274,16 @@ interface BaseStyleVars { '--data-table-body-cell-text-color'?: string '--data-table-border-color'?: string '--data-table-row-hover-background'?: string - '--data-table-striped-background'?: string + '--data-table-surface-low-row-hover-background'?: string + '--data-table-plain-row-hover-background'?: string '--data-table-empty-text-color'?: string '--data-table-border-radius'?: string '--data-table-cell-padding'?: string '--data-table-cell-font-size'?: string '--data-table-header-font-size'?: string '--data-table-row-height'?: string - '--data-table-row-height-small'?: string - '--data-table-row-height-large'?: string + '--data-table-row-small-height'?: string + '--data-table-row-large-height'?: string '--data-table-footer-padding'?: string '--data-table-empty-padding'?: string '--counter-padding'?: string @@ -780,6 +781,7 @@ interface BaseStyleVars { '--table-row-height'?: string '--table-row-padding'?: string '--table-footer-border-top'?: string + '--table-surface-low-row-hover-background'?: string '--tabs-item-horizontal-height'?: string '--tabs-item-vertical-height'?: string '--tabs-radius'?: string From 7bb0d32231f60483d16ce08a8f6d1ec5fdac6c87 Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Sat, 23 May 2026 21:48:41 +0800 Subject: [PATCH 17/37] feat(data-table): add plain table mode with related styles, documentation, and examples --- .../varlet-ui/src/data-table/dataTable.less | 2 ++ .../varlet-ui/src/data-table/docs/en-US.md | 20 +++++------ .../varlet-ui/src/data-table/docs/zh-CN.md | 20 +++++------ .../src/data-table/example/index.vue | 6 ++-- packages/varlet-ui/src/table/Table.vue | 13 +++++++- .../src/table/__tests__/index.spec.js | 13 ++++++++ packages/varlet-ui/src/table/docs/en-US.md | 33 +++++++++++++++++++ packages/varlet-ui/src/table/docs/zh-CN.md | 33 +++++++++++++++++++ .../varlet-ui/src/table/example/index.vue | 24 ++++++++++++++ .../src/table/example/locale/en-US.ts | 1 + .../src/table/example/locale/zh-CN.ts | 1 + packages/varlet-ui/src/table/props.ts | 1 + packages/varlet-ui/src/table/table.less | 9 +++++ packages/varlet-ui/src/themes/dark/table.ts | 1 + .../varlet-ui/src/themes/md3-dark/table.ts | 1 + .../varlet-ui/src/themes/md3-light/table.ts | 1 + packages/varlet-ui/types/styleVars.d.ts | 2 ++ packages/varlet-ui/types/table.d.ts | 2 ++ 18 files changed, 159 insertions(+), 24 deletions(-) diff --git a/packages/varlet-ui/src/data-table/dataTable.less b/packages/varlet-ui/src/data-table/dataTable.less index e514dcf4f22..c9ccdfe6bc4 100644 --- a/packages/varlet-ui/src/data-table/dataTable.less +++ b/packages/varlet-ui/src/data-table/dataTable.less @@ -130,6 +130,8 @@ &__body-cell { color: var(--data-table-body-cell-text-color); + background: inherit; + background-clip: padding-box; } &__fixed-cell { diff --git a/packages/varlet-ui/src/data-table/docs/en-US.md b/packages/varlet-ui/src/data-table/docs/en-US.md index c9603efe23e..96ff1d7e112 100644 --- a/packages/varlet-ui/src/data-table/docs/en-US.md +++ b/packages/varlet-ui/src/data-table/docs/en-US.md @@ -25,6 +25,16 @@ const data = [ ``` +### Plain Table + +Set `plain` to remove the card-like shadow, background, and radius, and present the table as a pure table surface. + +```html + +``` + ### Cell Bordered ```html @@ -141,16 +151,6 @@ Use `surface="low"` for a subtler MD3-style surface layer. ``` -### Plain Table - -Set `plain` to remove the card-like shadow, background, and radius, and present the table as a pure table surface. - -```html - -``` - ### Cell Spans Use `rowSpan`, `colSpan`, and `titleColSpan` to merge body and header cells. Returning `0` hides the current cell, which is typically used together with a previous spanning cell. diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index 701d2347146..8af28f09132 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -25,6 +25,16 @@ const data = [ ``` +### 纯表格 + +设置 `plain` 可以一并去掉卡片阴影、背景色和圆角,展示更纯粹的表格形态。 + +```html + +``` + ### 单元格分割线 ```html @@ -141,16 +151,6 @@ const data = [ ``` -### 纯表格 - -设置 `plain` 可以一并去掉卡片阴影、背景色和圆角,展示更纯粹的表格形态。 - -```html - -``` - ### 单元格合并 通过 `rowSpan`、`colSpan` 和 `titleColSpan` 控制表体与表头合并。返回 `0` 时当前单元格不渲染,通常配合前一个单元格的跨行或跨列使用。 diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index 6c29c93a0fc..e6ae7635c74 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -254,6 +254,9 @@ watch( {{ t('basicUsage') }} + {{ t('plainTable') }} + + {{ t('cellBordered') }} @@ -274,9 +277,6 @@ watch( {{ t('surfaceLow') }} - {{ t('plainTable') }} - - {{ t('spans') }} diff --git a/packages/varlet-ui/src/table/Table.vue b/packages/varlet-ui/src/table/Table.vue index d4aeef76a05..e90d8854862 100644 --- a/packages/varlet-ui/src/table/Table.vue +++ b/packages/varlet-ui/src/table/Table.vue @@ -1,5 +1,15 @@ ``` +### Plain Table + +Set `plain` to remove the card-like shadow, background, and radius, and present the table as a pure table surface. + +```html + +``` + ### Footer Slots You can insert something in the tail slot, the most common is to insert a `Pagination`. @@ -176,6 +206,7 @@ function get(current, size) { |--------------| -------------- | -------- | ---------- | | `full-width` | The width of the `table` (including the scrollable part) | _string \| number_ | `100%` | | `elevation` | Elevation level, options `true` `false` and level of `0-24` | _string \| number \| boolean_| `true` | +| `plain` | Whether to render as a plain table without card shadow, background, or radius | _boolean_ | `false` | | `scroller-height` ***3.2.0*** | The height of the scroll container, which can be used to implement functions such as longitudinal partial scrolling and fixed table headers. | _string \| number_ | `-` | ### Slots @@ -192,7 +223,9 @@ Here are the CSS variables used by the component. Styles can be customized using | Variable | Default | | --- | --- | | `--table-background` | `#fff` | +| `--table-surface-low-background` | `var(--color-surface-container-highest)` | | `--table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | +| `--table-plain-row-hover-background` | `hsla(var(--hsl-on-surface), 0.04)` | | `--table-border-radius` | `2px` | | `--table-thead-border-bottom` | `thin solid var(--color-outline)` | | `--table-thead-th-text-color` | `rgba(0, 0, 0, 0.6)` | diff --git a/packages/varlet-ui/src/table/docs/zh-CN.md b/packages/varlet-ui/src/table/docs/zh-CN.md index 734f822a90f..6b7a0238cf7 100644 --- a/packages/varlet-ui/src/table/docs/zh-CN.md +++ b/packages/varlet-ui/src/table/docs/zh-CN.md @@ -32,6 +32,36 @@ ``` +### 纯表格 + +设置 `plain` 可以一并去掉卡片阴影、背景色和圆角,展示更纯粹的表格形态。 + +```html + +``` + ### 尾部插槽 可以在尾部插槽中插入一些东西,最常见的是插入分页组件。 @@ -176,6 +206,7 @@ function get(current, size) { |--------------| -------------- | -------- | ---------- | | `full-width` | `table` 的宽度(包含可滚动部分) | _string \| number_ | `100%` | | `elevation` | 海拔高度,可选值为 `true` `false` 和 `0-24` 的等级 | _string \| number \| boolean_| `true` | +| `plain` | 是否以纯表格形态渲染,不带卡片阴影、背景色和圆角 | _boolean_ | `false` | | `scroller-height` ***3.2.0*** | 滚动容器高度,可用于实现纵向局部滚动,固定表头等功能 | _string \| number_ | `-` | ### 插槽 @@ -192,7 +223,9 @@ function get(current, size) { | 变量名 | 默认值 | | --- | --- | | `--table-background` | `#fff` | +| `--table-surface-low-background` | `var(--color-surface-container-highest)` | | `--table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | +| `--table-plain-row-hover-background` | `hsla(var(--hsl-on-surface), 0.04)` | | `--table-border-radius` | `2px` | | `--table-thead-border-bottom` | `thin solid var(--color-outline)` | | `--table-thead-th-text-color` | `rgba(0, 0, 0, 0.6)` | diff --git a/packages/varlet-ui/src/table/example/index.vue b/packages/varlet-ui/src/table/example/index.vue index 62fd2163789..d9c43f378a7 100644 --- a/packages/varlet-ui/src/table/example/index.vue +++ b/packages/varlet-ui/src/table/example/index.vue @@ -55,6 +55,30 @@ onThemeChange() + {{ t('plainTable') }} + + + + {{ t('name') }} + {{ t('math') }} + {{ t('english') }} + + + + + + {{ t('jerry') }} + 124 + 38 + + + {{ t('tom') }} + 100 + 135 + + + + {{ t('slot') }} diff --git a/packages/varlet-ui/src/table/example/locale/en-US.ts b/packages/varlet-ui/src/table/example/locale/en-US.ts index fa3cc403825..553f51616a1 100644 --- a/packages/varlet-ui/src/table/example/locale/en-US.ts +++ b/packages/varlet-ui/src/table/example/locale/en-US.ts @@ -1,5 +1,6 @@ export default { basicUsage: 'Basic Usage', + plainTable: 'Plain Table', slot: 'Footer Slots', fixedHeader: 'Fixed Table Header', math: 'Math', diff --git a/packages/varlet-ui/src/table/example/locale/zh-CN.ts b/packages/varlet-ui/src/table/example/locale/zh-CN.ts index ce8c7096cd3..7e81c24cacd 100644 --- a/packages/varlet-ui/src/table/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/table/example/locale/zh-CN.ts @@ -1,5 +1,6 @@ export default { basicUsage: '基本使用', + plainTable: '纯表格', slot: '尾部插槽', fixedHeader: '固定表头', math: '数学', diff --git a/packages/varlet-ui/src/table/props.ts b/packages/varlet-ui/src/table/props.ts index fa260316249..037a33eb25e 100644 --- a/packages/varlet-ui/src/table/props.ts +++ b/packages/varlet-ui/src/table/props.ts @@ -15,4 +15,5 @@ export const props = { default: true, }, surface: String as PropType, + plain: Boolean, } diff --git a/packages/varlet-ui/src/table/table.less b/packages/varlet-ui/src/table/table.less index e69d536d81e..9dcddc66f1c 100644 --- a/packages/varlet-ui/src/table/table.less +++ b/packages/varlet-ui/src/table/table.less @@ -2,6 +2,7 @@ --table-background: #fff; --table-surface-low-background: var(--color-surface-container-highest); --table-surface-low-row-hover-background: var(--color-surface-container-highest); + --table-plain-row-hover-background: hsla(var(--hsl-on-surface), 0.04); --table-border-radius: 2px; --table-thead-border-bottom: thin solid var(--color-outline); --table-thead-th-text-color: rgba(0, 0, 0, 0.6); @@ -78,6 +79,8 @@ td { color: var(--table-tbody-td-text-color); font-size: var(--table-tbody-td-font-size); + background: inherit; + background-clip: padding-box; } } @@ -103,6 +106,12 @@ --table-tbody-tr-hover-background: var(--table-surface-low-row-hover-background); } + &--plain { + --table-background: transparent; + --table-tbody-tr-hover-background: var(--table-plain-row-hover-background); + border-radius: 0; + } + &__footer { width: 100%; min-height: var(--table-row-height); diff --git a/packages/varlet-ui/src/themes/dark/table.ts b/packages/varlet-ui/src/themes/dark/table.ts index b0c5594a8d0..1f064b6d7b0 100644 --- a/packages/varlet-ui/src/themes/dark/table.ts +++ b/packages/varlet-ui/src/themes/dark/table.ts @@ -1,5 +1,6 @@ export default { '--table-background': '#303030', + '--table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.08)', '--table-surface-low-row-hover-background': '#2a2a2a', '--table-thead-th-text-color': 'rgba(255, 255, 255, 0.6)', '--table-thead-th-text-align': 'left', diff --git a/packages/varlet-ui/src/themes/md3-dark/table.ts b/packages/varlet-ui/src/themes/md3-dark/table.ts index 6fac5478547..83214f6aaf3 100644 --- a/packages/varlet-ui/src/themes/md3-dark/table.ts +++ b/packages/varlet-ui/src/themes/md3-dark/table.ts @@ -1,5 +1,6 @@ export default { '--table-background': 'var(--color-surface-container-highest)', + '--table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.08)', '--table-surface-low-row-hover-background': '#1c1b1d', '--table-thead-th-text-color': 'rgba(255, 255, 255, 0.6)', '--table-thead-th-text-align': 'left', diff --git a/packages/varlet-ui/src/themes/md3-light/table.ts b/packages/varlet-ui/src/themes/md3-light/table.ts index 73c6bb72c60..01b25ae88ad 100644 --- a/packages/varlet-ui/src/themes/md3-light/table.ts +++ b/packages/varlet-ui/src/themes/md3-light/table.ts @@ -1,5 +1,6 @@ export default { '--table-background': 'var(--color-surface-container-low)', + '--table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.04)', '--table-surface-low-row-hover-background': 'var(--color-surface-container-highest)', '--table-tbody-tr-hover-background': 'var(--color-surface-container-high)', '--table-border-radius': '2px', diff --git a/packages/varlet-ui/types/styleVars.d.ts b/packages/varlet-ui/types/styleVars.d.ts index abc8d87cca4..c591785dbf2 100644 --- a/packages/varlet-ui/types/styleVars.d.ts +++ b/packages/varlet-ui/types/styleVars.d.ts @@ -774,6 +774,8 @@ interface BaseStyleVars { '--table-thead-th-font-size'?: string '--table-thead-tr-border-bottom'?: string '--table-tbody-tr-hover-background'?: string + '--table-surface-low-row-hover-background'?: string + '--table-plain-row-hover-background'?: string '--table-tbody-tr-border-bottom'?: string '--table-tbody-td-text-color'?: string '--table-tbody-td-font-size'?: string diff --git a/packages/varlet-ui/types/table.d.ts b/packages/varlet-ui/types/table.d.ts index e0ef0e27d38..4ec17529283 100644 --- a/packages/varlet-ui/types/table.d.ts +++ b/packages/varlet-ui/types/table.d.ts @@ -7,6 +7,8 @@ export interface TableProps extends BasicAttributes { fullWidth?: string | number scrollerHeight?: string | number elevation?: boolean | string | number + surface?: 'low' + plain?: boolean } export class Table extends VarComponent { From c542b5050f26caea266f0dea6f4821e54b68b359 Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Sat, 23 May 2026 23:12:59 +0800 Subject: [PATCH 18/37] feat(data-table): add single and multiple column sorting functionality - Implemented single column sorting with controlled state using v-model:sorters. - Added support for multiple column sorting by setting sort-mode to "multiple". - Enhanced documentation for sorting features in zh-CN and en-US. - Updated example files to demonstrate sorting capabilities. - Introduced new props for sorters and sort mode in DataTable component. - Created useSorter composable for managing sorting logic. - Added new style variables for sorting triggers and surface low background. --- .../varlet-ui/src/data-table/DataTable.vue | 18 ++- .../src/data-table/DataTableHeaderCell.vue | 97 +++++++++++- .../src/data-table/__tests__/index.spec.js | 94 +++++++++++ .../varlet-ui/src/data-table/dataTable.less | 85 +++++++++- .../varlet-ui/src/data-table/docs/en-US.md | 148 +++++++++++++++++- .../varlet-ui/src/data-table/docs/zh-CN.md | 148 +++++++++++++++++- .../src/data-table/example/index.vue | 134 ++++++++++++++++ .../src/data-table/example/locale/en-US.ts | 4 + .../src/data-table/example/locale/zh-CN.ts | 4 + packages/varlet-ui/src/data-table/props.ts | 17 ++ .../varlet-ui/src/data-table/usePagination.ts | 8 +- .../varlet-ui/src/data-table/useSorter.ts | 75 +++++++++ packages/varlet-ui/src/table/docs/en-US.md | 31 ++++ packages/varlet-ui/src/table/docs/zh-CN.md | 31 ++++ .../varlet-ui/src/table/example/index.vue | 24 +++ .../src/table/example/locale/en-US.ts | 1 + .../src/table/example/locale/zh-CN.ts | 1 + .../__snapshots__/index.spec.js.snap | 28 +++- .../varlet-ui/src/themes/dark/dataTable.ts | 4 + .../src/themes/md3-dark/dataTable.ts | 3 + .../src/themes/md3-light/dataTable.ts | 3 + packages/varlet-ui/types/dataTable.d.ts | 13 +- packages/varlet-ui/types/styleVars.d.ts | 4 + 23 files changed, 955 insertions(+), 20 deletions(-) create mode 100644 packages/varlet-ui/src/data-table/useSorter.ts diff --git a/packages/varlet-ui/src/data-table/DataTable.vue b/packages/varlet-ui/src/data-table/DataTable.vue index 6937d2414fd..8860b659c04 100644 --- a/packages/varlet-ui/src/data-table/DataTable.vue +++ b/packages/varlet-ui/src/data-table/DataTable.vue @@ -38,10 +38,13 @@ :is-expand-column="isExpandColumn" :is-multiple-selection-column="isMultipleSelectionColumn" :is-selection-column-selectable="isSelectionColumnSelectable" + :is-column-sortable="isColumnSortable" + :get-column-sorter-order="getColumnSorterOrder" :is-column-resizable="isColumnResizable" :is-last-header-column="isLastHeaderColumn" :is-last-left-fixed-column="isLastLeftFixedColumn" :is-first-right-fixed-column="isFirstRightFixedColumn" + :toggle-column-sorter="toggleColumnSorter" :toggle-current-selectable-rows="toggleCurrentSelectableRows" :start-column-resize="startColumnResize" /> @@ -112,8 +115,8 @@ /> - ``` +### Single Column Sorting + +Set `column.sorter` to enable sorter interaction on a field column. The component only manages sorter state. You should control `v-model:sorters` and decide how to sort the data yourself. + +```html + + + +``` + +### Multiple Column Sorting + +Set `sort-mode="multiple"` to keep multiple columns active at the same time. + +```html + +``` + ### Cell Bordered ```html @@ -110,7 +169,7 @@ Use `column.render` to customize the cell content. ```html + + +``` + +### Right Alignment + +Use right-aligned headers and cells together with `sorter` and `resizable` to verify the trigger layout in a right-aligned case. + +```html + + + +``` + ## API ### Props @@ -512,6 +639,8 @@ const resizableColumns = [ | `total` | Total item count in remote mode | _number_ | `-` | | `max-height` | Max height of the table body. When set, the header stays fixed and the body scrolls internally | _number \| string_ | `-` | | `scroll-x` | Table width used to enable horizontal scrolling. Usually paired with fixed columns | _number \| string_ | `-` | +| `v-model:sorters` | Controlled sorter states | _DataTableSorter[]_ | `[]` | +| `sort-mode` | Sorter mode | _'single' \| 'multiple'_ | `'single'` | | `plain` | Whether to render as a plain table without card shadow, background, or radius | _boolean_ | `false` | | `table-layout` | Native `table-layout` value | _'auto' \| 'fixed'_ | `'auto'` | | `tree` | Whether to explicitly enable tree data mode | _boolean_ | `false` | @@ -522,13 +651,14 @@ const resizableColumns = [ | `cell-bordered` | Whether to show cell dividers | _boolean_ | `false` | | `size` | Table size | _'small' \| 'normal' \| 'large'_ | `'normal'` | -### DataTableColumn +#### DataTableColumn | Prop | Description | Type | Default | | --- | --- | --- | --- | | `type` | Column type. Supports `selection` and `expand` | _'selection' \| 'expand'_ | `-` | | `key` | Unique column key | _string_ | `-` | | `title` | Column title | _string_ | `-` | +| `sorter` | Whether the field column shows sorter interaction | _boolean_ | `false` | | `multiple` | Whether the selection column allows multiple rows | _boolean_ | `true` | | `selectable` | Whether selection is enabled. Supports `boolean` or `(context) => boolean` | _boolean \| `(context) => boolean`_ | `true` | | `expandable` | Whether the row can be expanded. Only works on expand columns | _`(context) => boolean`_ | `-` | @@ -545,7 +675,7 @@ const resizableColumns = [ | `cellProps` | Native cell props, supports object or function | _object \| (context) => object_ | `-` | | `render` | Custom cell render | _`(context) => VNodeChild`_ | `-` | -### DataTablePagination +#### DataTablePagination | Prop | Description | Type | Default | | --- | --- | --- | --- | @@ -557,12 +687,19 @@ const resizableColumns = [ | `sizeOption` | Page size options | _number[]_ | `[10, 20, 50, 100]` | | `showTotal` | Total text renderer | _`(total: number, range: [number, number]) => string`_ | `-` | +#### DataTableSorter + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `key` | Target column key | _string_ | `-` | +| `order` | Sort order | _'asc' \| 'desc'_ | `-` | + ### Slots | Name | Description | Parameters | | --- | --- | --- | | `empty` | Custom empty content | `-` | -| `loading` | Custom loading content | `-` | +| `loading-description` | Custom loading description | `-` | | `footer-prefix` | Content before pagination | `-` | ### Style Variables @@ -578,6 +715,9 @@ const resizableColumns = [ | `--data-table-row-hover-background` | `#f5f5f5` | | `--data-table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | | `--data-table-plain-row-hover-background` | `hsla(var(--hsl-on-surface), 0.04)` | +| `--data-table-sort-trigger-color` | `hsla(var(--hsl-on-surface), 0.42)` | +| `--data-table-sort-trigger-active-color` | `var(--color-primary)` | +| `--data-table-sort-trigger-hover-background` | `hsla(var(--hsl-primary), 0.08)` | | `--data-table-empty-text-color` | `var(--color-text-disabled)` | | `--data-table-border-radius` | `2px` | | `--data-table-cell-padding` | `0 16px` | diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index 8af28f09132..8596fb37093 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -35,6 +35,65 @@ const data = [ ``` +### 单列排序 + +给字段列设置 `column.sorter` 后,可以开启排序交互。组件只管理排序状态,你需要通过 `v-model:sorters` 受控这些状态,并自行决定如何排序数据。 + +```html + + + +``` + +### 多列排序 + +设置 `sort-mode="multiple"` 后,可以让多个列同时处于非无排序态。 + +```html + +``` + ### 单元格分割线 ```html @@ -110,7 +169,7 @@ const customRowProps = ({ row, rowIndex }) => ({ ```html + + +``` + +### 右对齐 + +当你需要检查右对齐表头和单元格下排序按钮、列宽拖拽触发区的布局时,可以同时设置 `align`、`titleAlign` 为 `right`,并开启 `sorter` 和 `resizable`。 + +```html + + + +``` + ## API ### Props @@ -512,6 +639,8 @@ const resizableColumns = [ | `total` | 远程分页总条数 | _number_ | `-` | | `max-height` | 表格主体最大高度。设置后表头固定,内容区域内部滚动 | _number \| string_ | `-` | | `scroll-x` | 用于开启横向滚动的表格宽度,通常和固定列一起使用 | _number \| string_ | `-` | +| `v-model:sorters` | 受控排序状态集合 | _DataTableSorter[]_ | `[]` | +| `sort-mode` | 排序器模式 | _'single' \| 'multiple'_ | `'single'` | | `plain` | 是否以纯表格形态渲染,不带卡片阴影、背景色和圆角 | _boolean_ | `false` | | `table-layout` | 原生 `table-layout` 布局方式 | _'auto' \| 'fixed'_ | `'auto'` | | `tree` | 是否显式开启树形数据 | _boolean_ | `false` | @@ -522,13 +651,14 @@ const resizableColumns = [ | `cell-bordered` | 是否显示单元格分割线 | _boolean_ | `false` | | `size` | 表格尺寸 | _'small' \| 'normal' \| 'large'_ | `'normal'` | -### DataTableColumn +#### DataTableColumn | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | `type` | 列类型。支持 `selection` 和 `expand` | _'selection' \| 'expand'_ | `-` | | `key` | 列唯一 key | _string_ | `-` | | `title` | 列标题 | _string_ | `-` | +| `sorter` | 字段列是否显示排序交互 | _boolean_ | `false` | | `multiple` | 选择列是否允许多选,仅对选择列生效 | _boolean_ | `true` | | `selectable` | 是否允许选择。支持 `boolean` 或 `(context) => boolean`,仅对选择列生效 | _boolean \| `(context) => boolean`_ | `true` | | `expandable` | 是否允许展开该行,仅对展开列生效 | _`(context) => boolean`_ | `-` | @@ -545,7 +675,7 @@ const resizableColumns = [ | `cellProps` | 单元格属性透传,支持对象或函数 | _object \| (context) => object_ | `-` | | `render` | 自定义单元格渲染 | _`(context) => VNodeChild`_ | `-` | -### DataTablePagination +#### DataTablePagination | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | @@ -557,12 +687,19 @@ const resizableColumns = [ | `sizeOption` | 每页条数选项 | _number[]_ | `[10, 20, 50, 100]` | | `showTotal` | 总数文案渲染函数 | _`(total: number, range: [number, number]) => string`_ | `-` | +#### DataTableSorter + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `key` | 对应列 key | _string_ | `-` | +| `order` | 排序方向 | _'asc' \| 'desc'_ | `-` | + ### Slots | 名称 | 说明 | 参数 | | --- | --- | --- | | `empty` | 自定义空态内容 | `-` | -| `loading` | 自定义加载内容 | `-` | +| `loading-description` | 自定义加载描述 | `-` | | `footer-prefix` | 分页前置内容 | `-` | ### 样式变量 @@ -578,6 +715,9 @@ const resizableColumns = [ | `--data-table-row-hover-background` | `#f5f5f5` | | `--data-table-surface-low-row-hover-background` | `var(--color-surface-container-highest)` | | `--data-table-plain-row-hover-background` | `hsla(var(--hsl-on-surface), 0.04)` | +| `--data-table-sort-trigger-color` | `hsla(var(--hsl-on-surface), 0.42)` | +| `--data-table-sort-trigger-active-color` | `var(--color-primary)` | +| `--data-table-sort-trigger-hover-background` | `hsla(var(--hsl-primary), 0.08)` | | `--data-table-empty-text-color` | `var(--color-text-disabled)` | | `--data-table-border-radius` | `2px` | | `--data-table-cell-padding` | `0 16px` | diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index e6ae7635c74..e7cf8b5ed55 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -44,6 +44,11 @@ const surfaceColumns = [ { key: 'role', title: 'Role' }, { key: 'status', title: 'Status' }, ] +const sortableColumns = [ + { key: 'name', title: 'Name', sorter: true }, + { key: 'role', title: 'Role', sorter: true }, + { key: 'status', title: 'Status', sorter: true }, +] const spanColumns = [ { @@ -105,8 +110,86 @@ const resizableColumns = [ { key: 'role', title: 'Role', width: 220, minWidth: 160, resizable: true }, { key: 'status', title: 'Status', width: 140, maxWidth: 180, resizable: true }, ] +const centeredSortableResizableColumns = [ + { type: 'selection' }, + { + type: 'expand', + renderExpand: renderExpandContent, + }, + { + key: 'name', + title: 'Name', + width: 170, + minWidth: 120, + resizable: true, + sorter: true, + align: 'center', + titleAlign: 'center', + }, + { + key: 'role', + title: 'Role', + width: 190, + minWidth: 140, + resizable: true, + sorter: true, + align: 'center', + titleAlign: 'center', + }, + { + key: 'status', + title: 'Status', + width: 150, + minWidth: 120, + resizable: true, + sorter: true, + align: 'center', + titleAlign: 'center', + }, +] +const rightAlignedSortableResizableColumns = [ + { type: 'selection' }, + { + type: 'expand', + renderExpand: renderExpandContent, + }, + { + key: 'name', + title: 'Name', + width: 170, + minWidth: 120, + resizable: true, + sorter: true, + align: 'right', + titleAlign: 'right', + }, + { + key: 'role', + title: 'Role', + width: 190, + minWidth: 140, + resizable: true, + sorter: true, + align: 'right', + titleAlign: 'right', + }, + { + key: 'status', + title: 'Status', + width: 150, + minWidth: 120, + resizable: true, + sorter: true, + align: 'right', + titleAlign: 'right', + }, +] const checkedRowKeys = ref([1, 3]) +const sorters = ref([]) +const multipleSorters = ref([]) +const centeredSorters = ref([]) +const rightAlignedSorters = ref([]) const singleCheckedRowKeys = ref([2]) const treeCheckedRowKeys = ref([1, 11, 12]) const treeNonCascadeCheckedRowKeys = ref([12]) @@ -184,6 +267,11 @@ const selectedRowNames = computed(() => .join(', '), ) +const sortedData = computed(() => applySorters(data, sorters.value)) +const multipleSortedData = computed(() => applySorters(data, multipleSorters.value)) +const centeredSortedData = computed(() => applySorters(data, centeredSorters.value)) +const rightAlignedSortedData = computed(() => applySorters(data, rightAlignedSorters.value)) + const alignedColumns = [ { key: 'name', title: 'Name', minWidth: 140 }, { key: 'role', title: 'Role', titleAlign: 'center', align: 'center', width: 120 }, @@ -248,6 +336,22 @@ watch( }, { immediate: true }, ) + +function applySorters(rows, activeSorters) { + return activeSorters.reduceRight((currentRows, sorter) => { + return [...currentRows].sort((leftRow, rightRow) => { + const leftValue = leftRow[sorter.key] + const rightValue = rightRow[sorter.key] + + if (leftValue === rightValue) { + return 0 + } + + const comparisonResult = leftValue > rightValue ? 1 : -1 + return sorter.order === 'asc' ? comparisonResult : -comparisonResult + }) + }, rows) +} diff --git a/packages/varlet-ui/src/data-table/example/locale/en-US.ts b/packages/varlet-ui/src/data-table/example/locale/en-US.ts index af8d99bb5f1..2dddc045032 100644 --- a/packages/varlet-ui/src/data-table/example/locale/en-US.ts +++ b/packages/varlet-ui/src/data-table/example/locale/en-US.ts @@ -7,6 +7,8 @@ export default { customRender: 'Custom Render', surfaceLow: 'Subtle Background', plainTable: 'Plain Table', + sorter: 'Single Column Sorting', + multipleSorter: 'Multiple Column Sorting', spans: 'Cell Spans', selection: 'Selection', singleSelection: 'Single Selection', @@ -23,4 +25,6 @@ export default { emptyText: 'Empty Text', emptyTip: 'No Data', loading: 'Loading', + centeredSortingAndResizing: 'Centered Alignment', + rightAlignedSortingAndResizing: 'Right Alignment', } diff --git a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts index 2e1b6801bea..fd21d33262c 100644 --- a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts @@ -7,6 +7,8 @@ export default { customRender: '自定义渲染', surfaceLow: '弱背景色', plainTable: '纯表格', + sorter: '单列排序', + multipleSorter: '多列排序', spans: '单元格合并', selection: '选择列', singleSelection: '单选', @@ -23,4 +25,6 @@ export default { emptyText: '空态文案', emptyTip: '暂无数据', loading: '加载状态', + centeredSortingAndResizing: '居中对齐', + rightAlignedSortingAndResizing: '右对齐', } diff --git a/packages/varlet-ui/src/data-table/props.ts b/packages/varlet-ui/src/data-table/props.ts index 45646789ef6..fff712fd30e 100644 --- a/packages/varlet-ui/src/data-table/props.ts +++ b/packages/varlet-ui/src/data-table/props.ts @@ -4,6 +4,8 @@ import { defineListenerProp } from '../utils/components' export type DataTableSurface = 'low' export type DataTableTableLayout = 'auto' | 'fixed' +export type DataTableSortMode = 'single' | 'multiple' +export type DataTableSorterOrder = 'asc' | 'desc' export type DataTableColumnAlign = 'left' | 'center' | 'right' @@ -57,10 +59,16 @@ export interface DataTableBaseColumn { cellProps?: DataTableCellProps } +export interface DataTableSorter { + key: string + order: DataTableSorterOrder +} + export interface DataTableFieldColumn extends DataTableBaseColumn { type?: undefined key: string title: string + sorter?: boolean render?: (context: DataTableColumnRenderContext) => VNodeChild } @@ -130,6 +138,14 @@ export const props = { total: Number, maxHeight: [Number, String], scrollX: [Number, String], + sorters: { + type: Array as PropType, + default: () => [], + }, + sortMode: { + type: String as PropType, + default: 'single', + }, tree: Boolean, surface: String as PropType, cascade: { @@ -161,4 +177,5 @@ export const props = { 'onUpdate:checkedRowKeys': defineListenerProp<(checkedRowKeys: Array) => void>(), 'onUpdate:page': defineListenerProp<(page: number) => void>(), 'onUpdate:pageSize': defineListenerProp<(pageSize: number) => void>(), + 'onUpdate:sorters': defineListenerProp<(sorters: DataTableSorter[]) => void>(), } diff --git a/packages/varlet-ui/src/data-table/usePagination.ts b/packages/varlet-ui/src/data-table/usePagination.ts index 35800395db3..87428c27be2 100644 --- a/packages/varlet-ui/src/data-table/usePagination.ts +++ b/packages/varlet-ui/src/data-table/usePagination.ts @@ -22,6 +22,7 @@ const defaultPaginationOptions = { interface UsePaginationOptions> { pagination: () => boolean | DataTablePagination remote: () => boolean + loading: () => boolean page: () => number pageSize: () => number total: () => number | undefined @@ -32,6 +33,7 @@ interface UsePaginationOptions> { export function usePagination>({ pagination, remote, + loading, page, pageSize, total, @@ -42,12 +44,16 @@ export function usePagination>({ const resolvedPagination = pagination() if (isBoolean(resolvedPagination)) { - return defaultPaginationOptions + return { + ...defaultPaginationOptions, + disabled: loading(), + } } return { ...defaultPaginationOptions, ...resolvedPagination, + disabled: loading() || resolvedPagination.disabled === true, } }) diff --git a/packages/varlet-ui/src/data-table/useSorter.ts b/packages/varlet-ui/src/data-table/useSorter.ts new file mode 100644 index 00000000000..aade6d447c6 --- /dev/null +++ b/packages/varlet-ui/src/data-table/useSorter.ts @@ -0,0 +1,75 @@ +import { call } from '@varlet/shared' +import { computed } from 'vue' +import type { + DataTableColumn, + DataTableFieldColumn, + DataTableSortMode, + DataTableSorter, + DataTableSorterOrder, +} from './props' + +interface UseSorterOptions { + sorters: () => DataTableSorter[] + sortMode: () => DataTableSortMode + onUpdateSorters: () => ((sorters: DataTableSorter[]) => void) | ((sorters: DataTableSorter[]) => void)[] | undefined +} + +export function useSorter({ sorters, sortMode, onUpdateSorters }: UseSorterOptions) { + const activeSorters = computed(() => sorters()) + + function isColumnSortable(column: DataTableColumn): column is DataTableFieldColumn { + return column.type == null && column.sorter === true + } + + function getColumnSorterOrder(columnKey: string): DataTableSorterOrder | undefined { + return activeSorters.value.find((sorter) => sorter.key === columnKey)?.order + } + + function toggleColumnSorter(columnKey: string) { + const currentOrder = getColumnSorterOrder(columnKey) + + if (sortMode() === 'single') { + call( + onUpdateSorters(), + currentOrder == null + ? [{ key: columnKey, order: 'asc' as const }] + : currentOrder === 'asc' + ? [{ key: columnKey, order: 'desc' as const }] + : [], + ) + return + } + + if (currentOrder == null) { + call(onUpdateSorters(), [...activeSorters.value, { key: columnKey, order: 'asc' as const }]) + return + } + + if (currentOrder === 'asc') { + call( + onUpdateSorters(), + activeSorters.value.map((sorter) => + sorter.key !== columnKey + ? sorter + : { + ...sorter, + order: 'desc' as const, + }, + ), + ) + return + } + + call( + onUpdateSorters(), + activeSorters.value.filter((sorter) => sorter.key !== columnKey), + ) + } + + return { + activeSorters, + isColumnSortable, + getColumnSorterOrder, + toggleColumnSorter, + } +} diff --git a/packages/varlet-ui/src/table/docs/en-US.md b/packages/varlet-ui/src/table/docs/en-US.md index c526f6f879e..4cd910fb21b 100644 --- a/packages/varlet-ui/src/table/docs/en-US.md +++ b/packages/varlet-ui/src/table/docs/en-US.md @@ -62,6 +62,36 @@ Set `plain` to remove the card-like shadow, background, and radius, and present ``` +### Subtle Background + +Use `surface="low"` for a subtler MD3-style surface layer. + +```html + +``` + ### Footer Slots You can insert something in the tail slot, the most common is to insert a `Pagination`. @@ -207,6 +237,7 @@ function get(current, size) { | `full-width` | The width of the `table` (including the scrollable part) | _string \| number_ | `100%` | | `elevation` | Elevation level, options `true` `false` and level of `0-24` | _string \| number \| boolean_| `true` | | `plain` | Whether to render as a plain table without card shadow, background, or radius | _boolean_ | `false` | +| `surface` | Subtle background style | _'low'_ | `-` | | `scroller-height` ***3.2.0*** | The height of the scroll container, which can be used to implement functions such as longitudinal partial scrolling and fixed table headers. | _string \| number_ | `-` | ### Slots diff --git a/packages/varlet-ui/src/table/docs/zh-CN.md b/packages/varlet-ui/src/table/docs/zh-CN.md index 6b7a0238cf7..ac9a77507ad 100644 --- a/packages/varlet-ui/src/table/docs/zh-CN.md +++ b/packages/varlet-ui/src/table/docs/zh-CN.md @@ -62,6 +62,36 @@ ``` +### 弱背景色 + +通过 `surface="low"` 使用更接近 MD3 的弱背景层级。 + +```html + +``` + ### 尾部插槽 可以在尾部插槽中插入一些东西,最常见的是插入分页组件。 @@ -207,6 +237,7 @@ function get(current, size) { | `full-width` | `table` 的宽度(包含可滚动部分) | _string \| number_ | `100%` | | `elevation` | 海拔高度,可选值为 `true` `false` 和 `0-24` 的等级 | _string \| number \| boolean_| `true` | | `plain` | 是否以纯表格形态渲染,不带卡片阴影、背景色和圆角 | _boolean_ | `false` | +| `surface` | 弱背景色风格 | _'low'_ | `-` | | `scroller-height` ***3.2.0*** | 滚动容器高度,可用于实现纵向局部滚动,固定表头等功能 | _string \| number_ | `-` | ### 插槽 diff --git a/packages/varlet-ui/src/table/example/index.vue b/packages/varlet-ui/src/table/example/index.vue index d9c43f378a7..4397b8fd99e 100644 --- a/packages/varlet-ui/src/table/example/index.vue +++ b/packages/varlet-ui/src/table/example/index.vue @@ -79,6 +79,30 @@ onThemeChange() + {{ t('surfaceLow') }} + + + + {{ t('name') }} + {{ t('math') }} + {{ t('english') }} + + + + + + {{ t('jerry') }} + 124 + 38 + + + {{ t('tom') }} + 100 + 135 + + + + {{ t('slot') }} diff --git a/packages/varlet-ui/src/table/example/locale/en-US.ts b/packages/varlet-ui/src/table/example/locale/en-US.ts index 553f51616a1..418e949f06d 100644 --- a/packages/varlet-ui/src/table/example/locale/en-US.ts +++ b/packages/varlet-ui/src/table/example/locale/en-US.ts @@ -1,6 +1,7 @@ export default { basicUsage: 'Basic Usage', plainTable: 'Plain Table', + surfaceLow: 'Subtle Background', slot: 'Footer Slots', fixedHeader: 'Fixed Table Header', math: 'Math', diff --git a/packages/varlet-ui/src/table/example/locale/zh-CN.ts b/packages/varlet-ui/src/table/example/locale/zh-CN.ts index 7e81c24cacd..7e5879b96f9 100644 --- a/packages/varlet-ui/src/table/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/table/example/locale/zh-CN.ts @@ -1,6 +1,7 @@ export default { basicUsage: '基本使用', plainTable: '纯表格', + surfaceLow: '弱背景色', slot: '尾部插槽', fixedHeader: '固定表头', math: '数学', diff --git a/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap index 232905ac2a3..1fc238c27dc 100644 --- a/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap +++ b/packages/varlet-ui/src/themes/__tests__/__snapshots__/index.spec.js.snap @@ -314,10 +314,16 @@ exports[`dark theme 1`] = ` "--data-table-header-cell-background": "#303030", "--data-table-header-cell-text-color": "rgba(255, 255, 255, 0.6)", "--data-table-header-font-size": "14px", + "--data-table-plain-row-hover-background": "hsla(var(--hsl-on-surface), 0.08)", "--data-table-row-height": "46px", - "--data-table-row-hover-background": "#4c4b4b", + "--data-table-row-hover-background": "#3a3a3a", "--data-table-row-large-height": "52px", "--data-table-row-small-height": "40px", + "--data-table-sort-trigger-active-color": "var(--color-primary)", + "--data-table-sort-trigger-color": "hsla(var(--hsl-on-surface), 0.5)", + "--data-table-sort-trigger-hover-background": "hsla(var(--hsl-primary), 0.12)", + "--data-table-surface-low-background": "#2a2a2a", + "--data-table-surface-low-row-hover-background": "#2a2a2a", "--date-picker-actions-padding": "0 8px 12px 8px", "--date-picker-body-background-color": "#303030", "--date-picker-body-height": "280px", @@ -841,8 +847,10 @@ exports[`dark theme 1`] = ` "--table-background": "#303030", "--table-border-radius": "2px", "--table-footer-border-top": "thin solid var(--color-outline)", + "--table-plain-row-hover-background": "hsla(var(--hsl-on-surface), 0.08)", "--table-row-height": "46px", "--table-row-padding": "0 16px", + "--table-surface-low-row-hover-background": "#2a2a2a", "--table-tbody-td-font-size": "16px", "--table-tbody-td-text-align": "left", "--table-tbody-td-text-color": "#fff", @@ -1286,11 +1294,16 @@ exports[`md3Dark theme 1`] = ` "--data-table-header-cell-background": "var(--color-surface-container-highest)", "--data-table-header-cell-text-color": "rgba(255, 255, 255, 0.6)", "--data-table-header-font-size": "14px", + "--data-table-plain-row-hover-background": "hsla(var(--hsl-on-surface), 0.08)", "--data-table-row-height": "46px", - "--data-table-row-hover-background": "var(--color-surface-container-highest)", + "--data-table-row-hover-background": "var(--color-surface-container-high)", "--data-table-row-large-height": "52px", "--data-table-row-small-height": "40px", + "--data-table-sort-trigger-active-color": "var(--color-primary)", + "--data-table-sort-trigger-color": "hsla(var(--hsl-on-surface), 0.5)", + "--data-table-sort-trigger-hover-background": "hsla(var(--hsl-primary), 0.12)", "--data-table-surface-low-background": "#1c1b1d", + "--data-table-surface-low-row-hover-background": "var(--color-surface-container-highest)", "--date-picker-actions-padding": "20px", "--date-picker-body-background-color": "var(--color-surface-container-high)", "--date-picker-body-height": "300px", @@ -1815,8 +1828,10 @@ exports[`md3Dark theme 1`] = ` "--table-background": "var(--color-surface-container-highest)", "--table-border-radius": "2px", "--table-footer-border-top": "thin solid var(--color-outline)", + "--table-plain-row-hover-background": "hsla(var(--hsl-on-surface), 0.08)", "--table-row-height": "46px", "--table-row-padding": "0 16px", + "--table-surface-low-row-hover-background": "#1c1b1d", "--table-tbody-td-font-size": "16px", "--table-tbody-td-text-align": "left", "--table-tbody-td-text-color": "#fff", @@ -2244,11 +2259,16 @@ exports[`md3Light theme 1`] = ` "--data-table-header-cell-background": "var(--color-surface-container-low)", "--data-table-header-cell-text-color": "rgba(0, 0, 0, 0.6)", "--data-table-header-font-size": "14px", + "--data-table-plain-row-hover-background": "hsla(var(--hsl-on-surface), 0.04)", "--data-table-row-height": "46px", - "--data-table-row-hover-background": "var(--color-surface-container-low)", + "--data-table-row-hover-background": "var(--color-surface-container-high)", "--data-table-row-large-height": "52px", "--data-table-row-small-height": "40px", + "--data-table-sort-trigger-active-color": "var(--color-primary)", + "--data-table-sort-trigger-color": "hsla(var(--hsl-on-surface), 0.42)", + "--data-table-sort-trigger-hover-background": "hsla(var(--hsl-primary), 0.08)", "--data-table-surface-low-background": "var(--color-surface-container-low)", + "--data-table-surface-low-row-hover-background": "var(--color-surface-container-highest)", "--date-picker-actions-padding": "20px", "--date-picker-body-background-color": "var(--color-surface-container-high)", "--date-picker-body-height": "300px", @@ -2771,8 +2791,10 @@ exports[`md3Light theme 1`] = ` "--table-background": "var(--color-surface-container-low)", "--table-border-radius": "2px", "--table-footer-border-top": "thin solid var(--color-outline)", + "--table-plain-row-hover-background": "hsla(var(--hsl-on-surface), 0.04)", "--table-row-height": "46px", "--table-row-padding": "0 16px", + "--table-surface-low-row-hover-background": "var(--color-surface-container-highest)", "--table-tbody-td-font-size": "16px", "--table-tbody-td-text-align": "left", "--table-tbody-td-text-color": "#555", diff --git a/packages/varlet-ui/src/themes/dark/dataTable.ts b/packages/varlet-ui/src/themes/dark/dataTable.ts index 6b448d3763b..4aa3a7b3c35 100644 --- a/packages/varlet-ui/src/themes/dark/dataTable.ts +++ b/packages/varlet-ui/src/themes/dark/dataTable.ts @@ -1,5 +1,6 @@ export default { '--data-table-background': '#303030', + '--data-table-surface-low-background': '#2a2a2a', '--data-table-header-cell-background': '#303030', '--data-table-header-cell-text-color': 'rgba(255, 255, 255, 0.6)', '--data-table-body-cell-text-color': '#fff', @@ -7,6 +8,9 @@ export default { '--data-table-row-hover-background': '#3a3a3a', '--data-table-surface-low-row-hover-background': '#2a2a2a', '--data-table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.08)', + '--data-table-sort-trigger-color': 'hsla(var(--hsl-on-surface), 0.5)', + '--data-table-sort-trigger-active-color': 'var(--color-primary)', + '--data-table-sort-trigger-hover-background': 'hsla(var(--hsl-primary), 0.12)', '--data-table-empty-text-color': 'var(--color-text-disabled)', '--data-table-border-radius': '2px', '--data-table-cell-padding': '0 16px', diff --git a/packages/varlet-ui/src/themes/md3-dark/dataTable.ts b/packages/varlet-ui/src/themes/md3-dark/dataTable.ts index 5bb8b9d7e9d..fbd30d2a0d3 100644 --- a/packages/varlet-ui/src/themes/md3-dark/dataTable.ts +++ b/packages/varlet-ui/src/themes/md3-dark/dataTable.ts @@ -8,6 +8,9 @@ export default { '--data-table-row-hover-background': 'var(--color-surface-container-high)', '--data-table-surface-low-row-hover-background': 'var(--color-surface-container-highest)', '--data-table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.08)', + '--data-table-sort-trigger-color': 'hsla(var(--hsl-on-surface), 0.5)', + '--data-table-sort-trigger-active-color': 'var(--color-primary)', + '--data-table-sort-trigger-hover-background': 'hsla(var(--hsl-primary), 0.12)', '--data-table-empty-text-color': 'var(--color-text-disabled)', '--data-table-border-radius': '2px', '--data-table-cell-padding': '0 16px', diff --git a/packages/varlet-ui/src/themes/md3-light/dataTable.ts b/packages/varlet-ui/src/themes/md3-light/dataTable.ts index 7fa59bc1013..95c9c6f4124 100644 --- a/packages/varlet-ui/src/themes/md3-light/dataTable.ts +++ b/packages/varlet-ui/src/themes/md3-light/dataTable.ts @@ -8,6 +8,9 @@ export default { '--data-table-row-hover-background': 'var(--color-surface-container-high)', '--data-table-surface-low-row-hover-background': 'var(--color-surface-container-highest)', '--data-table-plain-row-hover-background': 'hsla(var(--hsl-on-surface), 0.04)', + '--data-table-sort-trigger-color': 'hsla(var(--hsl-on-surface), 0.42)', + '--data-table-sort-trigger-active-color': 'var(--color-primary)', + '--data-table-sort-trigger-hover-background': 'hsla(var(--hsl-primary), 0.08)', '--data-table-empty-text-color': 'var(--color-text-disabled)', '--data-table-border-radius': '2px', '--data-table-cell-padding': '0 16px', diff --git a/packages/varlet-ui/types/dataTable.d.ts b/packages/varlet-ui/types/dataTable.d.ts index 7f50b90713d..c3c62af4b63 100644 --- a/packages/varlet-ui/types/dataTable.d.ts +++ b/packages/varlet-ui/types/dataTable.d.ts @@ -7,6 +7,8 @@ export type DataTableColumnAlign = 'left' | 'center' | 'right' export type DataTableColumnFixed = 'left' | 'right' export type DataTableSurface = 'low' export type DataTableTableLayout = 'auto' | 'fixed' +export type DataTableSortMode = 'single' | 'multiple' +export type DataTableSorterOrder = 'asc' | 'desc' export type DataTableRowKey = keyof Row | string | ((row: Row, rowIndex: number) => string | number) @@ -50,10 +52,16 @@ export interface DataTableBaseColumn { cellProps?: DataTableCellProps } +export interface DataTableSorter { + key: string + order: DataTableSorterOrder +} + export interface DataTableFieldColumn extends DataTableBaseColumn { type?: undefined key: string title: string + sorter?: boolean render?: (context: DataTableColumnRenderContext) => VNodeChild } @@ -103,6 +111,8 @@ export interface DataTableProps extends BasicAttributes { total?: number maxHeight?: number | string scrollX?: number | string + sorters?: DataTableSorter[] + sortMode?: DataTableSortMode tree?: boolean surface?: DataTableSurface cascade?: boolean @@ -116,6 +126,7 @@ export interface DataTableProps extends BasicAttributes { 'onUpdate:checkedRowKeys'?: ListenerProp<(checkedRowKeys: Array) => void> 'onUpdate:page'?: ListenerProp<(page: number) => void> 'onUpdate:pageSize'?: ListenerProp<(pageSize: number) => void> + 'onUpdate:sorters'?: ListenerProp<(sorters: DataTableSorter[]) => void> } export class DataTable extends VarComponent { @@ -124,7 +135,7 @@ export class DataTable extends VarComponent { $props: DataTableProps $slots: { - loading(): VNode[] + loadingDescription(): VNode[] footerPrefix(): VNode[] } } diff --git a/packages/varlet-ui/types/styleVars.d.ts b/packages/varlet-ui/types/styleVars.d.ts index c591785dbf2..8cb135ed53f 100644 --- a/packages/varlet-ui/types/styleVars.d.ts +++ b/packages/varlet-ui/types/styleVars.d.ts @@ -269,6 +269,7 @@ interface BaseStyleVars { '--countdown-text-color'?: string '--countdown-text-font-size'?: string '--data-table-background'?: string + '--data-table-surface-low-background'?: string '--data-table-header-cell-background'?: string '--data-table-header-cell-text-color'?: string '--data-table-body-cell-text-color'?: string @@ -276,6 +277,9 @@ interface BaseStyleVars { '--data-table-row-hover-background'?: string '--data-table-surface-low-row-hover-background'?: string '--data-table-plain-row-hover-background'?: string + '--data-table-sort-trigger-color'?: string + '--data-table-sort-trigger-active-color'?: string + '--data-table-sort-trigger-hover-background'?: string '--data-table-empty-text-color'?: string '--data-table-border-radius'?: string '--data-table-cell-padding'?: string From f7d7ff7bda9d91c702b987bbe552c251004c806c Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Sat, 23 May 2026 23:36:14 +0800 Subject: [PATCH 19/37] feat(data-table): add support for grouped header columns with updated documentation and examples --- .../varlet-ui/src/data-table/DataTable.vue | 172 ++++++++++++++---- .../src/data-table/DataTableHeaderCell.vue | 20 +- .../src/data-table/__tests__/index.spec.js | 28 +++ .../varlet-ui/src/data-table/docs/en-US.md | 27 +++ .../varlet-ui/src/data-table/docs/zh-CN.md | 27 +++ .../src/data-table/example/index.vue | 16 ++ .../src/data-table/example/locale/en-US.ts | 1 + .../src/data-table/example/locale/zh-CN.ts | 1 + packages/varlet-ui/src/data-table/props.ts | 1 + .../varlet-ui/src/data-table/useSorter.ts | 2 +- packages/varlet-ui/types/dataTable.d.ts | 1 + 11 files changed, 252 insertions(+), 44 deletions(-) diff --git a/packages/varlet-ui/src/data-table/DataTable.vue b/packages/varlet-ui/src/data-table/DataTable.vue index 8860b659c04..da6eeb5ff5b 100644 --- a/packages/varlet-ui/src/data-table/DataTable.vue +++ b/packages/varlet-ui/src/data-table/DataTable.vue @@ -15,19 +15,19 @@ >
- +
- + -
+
@@ -138,7 +138,9 @@ import { props, type DataTableColumn, type DataTableColumnAlign, + type DataTableColumnFixed, type DataTableExpandColumn, + type DataTableFieldColumn, type DataTableSelectionColumn, } from './props' import { type DataTableBodyCell, type DataTableBodyRow, useBodyRows } from './useBodyRows' @@ -154,9 +156,13 @@ const { name, n, classes } = createNamespace('data-table') interface DataTableHeaderCell { key: string - columnIndex: number column: DataTableColumn + columnIndex: number + startLeafColumnIndex: number + endLeafColumnIndex: number colSpan?: number + rowSpan?: number + fixed?: DataTableColumnFixed } export default defineComponent({ @@ -208,14 +214,16 @@ export default defineComponent({ getTreeChildren, }) + const leafColumns = computed(() => flattenLeafColumns(props.columns)) + const { columnWidths, getColStyle, isColumnResizable, startColumnResize } = useColumnWidths({ - columns: () => props.columns, + columns: () => leafColumns.value, isSelectionColumn, isExpandColumn, }) const { getFixedStyle, isFirstRightFixedColumn, isLastLeftFixedColumn } = useColumnsFixedOffsets({ - columns: () => props.columns, + columns: () => leafColumns.value, columnWidths: () => columnWidths.value, }) @@ -237,11 +245,11 @@ export default defineComponent({ }) const firstTreeColumnIndex = computed(() => - props.columns.findIndex((column) => !isSelectionColumn(column) && !isExpandColumn(column)), + leafColumns.value.findIndex((column) => !isSelectionColumn(column) && !isExpandColumn(column)), ) const { expandedRowKeys, isRowExpandable, toggleRowExpanded, renderExpandedRow } = useExpandRow({ - columns: () => props.columns, + columns: () => leafColumns.value, isExpandColumn, }) @@ -251,7 +259,7 @@ export default defineComponent({ firstTreeColumnIndex, getRowKey, getTreeChildren, - columns: () => props.columns, + columns: () => leafColumns.value, sourceRows: () => pagedData.value, tree: () => props.tree, }) @@ -271,7 +279,7 @@ export default defineComponent({ checkedRowKeys, isSelectionColumn, getTreeChildren, - columns: () => props.columns, + columns: () => leafColumns.value, tree: () => props.tree, cascade: () => props.cascade, pagedData: () => pagedData.value, @@ -279,32 +287,85 @@ export default defineComponent({ treeRowMeta: () => treeRowMeta.value, }) - const headerCells = computed(() => { - const cells: DataTableHeaderCell[] = [] - let coveredUntil = -1 - - props.columns.forEach((column, columnIndex) => { - if (columnIndex <= coveredUntil) { - return - } - - const maxColSpan = props.columns.length - columnIndex - const colSpan = clamp(floor(column.titleColSpan ?? 1), 0, maxColSpan) + const headerRows = computed(() => { + if (!props.columns.some(isGroupColumn)) { + const cells: DataTableHeaderCell[] = [] + let coveredUntil = -1 + + props.columns.forEach((column, columnIndex) => { + if (columnIndex <= coveredUntil) { + return + } + + const maxColSpan = props.columns.length - columnIndex + const colSpan = clamp(floor(column.titleColSpan ?? 1), 0, maxColSpan) + + if (colSpan === 0) { + return + } + + coveredUntil = columnIndex + colSpan - 1 + cells.push({ + key: `${column.key ?? column.type ?? columnIndex}-header-${columnIndex}`, + column, + columnIndex, + startLeafColumnIndex: columnIndex, + endLeafColumnIndex: columnIndex + colSpan - 1, + colSpan: colSpan > 1 ? colSpan : undefined, + fixed: resolveHeaderCellFixed(leafColumns.value.slice(columnIndex, columnIndex + colSpan)), + }) + }) - if (colSpan === 0) { - return - } + return [cells] + } - coveredUntil = columnIndex + colSpan - 1 - cells.push({ - key: `${column.key ?? column.type ?? columnIndex}-header-${columnIndex}`, - columnIndex, - column, - colSpan: colSpan > 1 ? colSpan : undefined, + const rows: DataTableHeaderCell[][] = [] + const maxDepth = getColumnDepth(props.columns) + let leafColumnIndex = 0 + + function visit(columns: DataTableColumn[], depth: number) { + columns.forEach((column, columnIndex) => { + rows[depth] ??= [] + + if (isGroupColumn(column)) { + const childColumns = column.children + const startLeafColumnIndex = leafColumnIndex + const leafCount = countLeafColumns(childColumns) + leafColumnIndex += leafCount + const endLeafColumnIndex = leafColumnIndex - 1 + + rows[depth].push({ + key: `${column.key ?? column.title}-header-${depth}-${columnIndex}`, + column, + columnIndex: startLeafColumnIndex, + startLeafColumnIndex, + endLeafColumnIndex, + colSpan: leafCount, + fixed: resolveHeaderCellFixed(leafColumns.value.slice(startLeafColumnIndex, endLeafColumnIndex + 1)), + }) + + visit(childColumns, depth + 1) + return + } + + const startLeafColumnIndex = leafColumnIndex + leafColumnIndex += 1 + + rows[depth].push({ + key: `${column.key ?? column.type ?? columnIndex}-header-${depth}-${startLeafColumnIndex}`, + column, + columnIndex: startLeafColumnIndex, + startLeafColumnIndex, + endLeafColumnIndex: startLeafColumnIndex, + rowSpan: maxDepth - depth > 1 ? maxDepth - depth : undefined, + fixed: column.fixed, + }) }) - }) + } + + visit(props.columns, 0) - return cells + return rows }) function getRowKey(row: Record, rowIndex: number) { @@ -321,6 +382,40 @@ export default defineComponent({ return isArray(children) ? children : [] } + function isGroupColumn(column: DataTableColumn): column is DataTableFieldColumn & { children: DataTableColumn[] } { + return 'children' in column && isArray(column.children) && column.children.length > 0 + } + + function flattenLeafColumns(columns: DataTableColumn[]): DataTableColumn[] { + return columns.flatMap((column) => (isGroupColumn(column) ? flattenLeafColumns(column.children) : [column])) + } + + function countLeafColumns(columns: DataTableColumn[]) { + return flattenLeafColumns(columns).length + } + + function getColumnDepth(columns: DataTableColumn[]): number { + if (!columns.length) { + return 0 + } + + return Math.max(...columns.map((column) => (isGroupColumn(column) ? 1 + getColumnDepth(column.children) : 1))) + } + + function resolveHeaderCellFixed(columns: DataTableColumn[]): DataTableColumnFixed | undefined { + if (!columns.length) { + return + } + + if (columns.every((column) => column.fixed === 'left')) { + return 'left' + } + + if (columns.every((column) => column.fixed === 'right')) { + return 'right' + } + } + function isSelectionColumn(column: DataTableColumn): column is DataTableSelectionColumn { return column.type === 'selection' } @@ -371,14 +466,14 @@ export default defineComponent({ } function isLastHeaderColumn(columnIndex: number) { - return columnIndex === props.columns.length - 1 + return columnIndex === leafColumns.value.length - 1 } function getHeaderCellStyle(cell: DataTableHeaderCell): CSSProperties { return { textAlign: getAlign(cell.column.titleAlign ?? cell.column.align), - zIndex: cell.column.fixed ? 3 : undefined, - ...getFixedStyle(cell.column.fixed, cell.columnIndex), + zIndex: cell.fixed ? 3 : undefined, + ...getFixedStyle(cell.fixed, cell.startLeafColumnIndex), } } @@ -405,10 +500,11 @@ export default defineComponent({ paginationTotal, showPagination, tableStyle, + leafColumns, currentSelectableRows, allCurrentRowsSelected, someCurrentRowsSelected, - headerCells, + headerRows, bodyRows, isColumnResizable, getRowProps, diff --git a/packages/varlet-ui/src/data-table/DataTableHeaderCell.vue b/packages/varlet-ui/src/data-table/DataTableHeaderCell.vue index f71b9b0abf3..4feacd8684c 100644 --- a/packages/varlet-ui/src/data-table/DataTableHeaderCell.vue +++ b/packages/varlet-ui/src/data-table/DataTableHeaderCell.vue @@ -6,13 +6,14 @@ n('header-cell'), [isSelectionColumn(headerCell.column), n('selection-cell')], [isExpandColumn(headerCell.column), n('expand-cell')], - [headerCell.column.fixed, n('fixed-cell')], - [isLastLeftFixedColumn(headerCell.columnIndex), n('fixed-cell--shadow-right')], - [isFirstRightFixedColumn(headerCell.columnIndex), n('fixed-cell--shadow-left')], + [headerCell.fixed, n('fixed-cell')], + [isLastLeftFixedColumn(headerCell.endLeafColumnIndex), n('fixed-cell--shadow-right')], + [isFirstRightFixedColumn(headerCell.startLeafColumnIndex), n('fixed-cell--shadow-left')], ) " :style="style" :colspan="headerCell.colSpan" + :rowspan="headerCell.rowSpan" > (() => { + if (props.headerCell.colSpan != null && props.headerCell.colSpan > 1) { + return {} + } + return { position: 'absolute', top: 0, diff --git a/packages/varlet-ui/src/data-table/__tests__/index.spec.js b/packages/varlet-ui/src/data-table/__tests__/index.spec.js index 58cf77236d9..169f1cf0b24 100644 --- a/packages/varlet-ui/src/data-table/__tests__/index.spec.js +++ b/packages/varlet-ui/src/data-table/__tests__/index.spec.js @@ -133,6 +133,34 @@ describe('test data-table component props', () => { wrapper.unmount() }) + test('should support grouped header columns', () => { + const wrapper = mount(VarDataTable, { + props: { + columns: [ + { + title: 'Profile', + children: [ + { key: 'name', title: 'Name' }, + { key: 'role', title: 'Role' }, + ], + }, + { + title: 'State', + children: [{ key: 'status', title: 'Status' }], + }, + ], + data, + pagination: false, + }, + }) + + expect(wrapper.findAll('thead tr')).toHaveLength(2) + expect(wrapper.findAll('thead tr')[0].findAll('th')).toHaveLength(2) + expect(wrapper.findAll('thead tr')[1].findAll('th')).toHaveLength(3) + expect(wrapper.findAll('colgroup col')).toHaveLength(3) + wrapper.unmount() + }) + test('should support single sorter cycle', async () => { const onUpdateSorters = vi.fn() const wrapper = mount(VarDataTable, { diff --git a/packages/varlet-ui/src/data-table/docs/en-US.md b/packages/varlet-ui/src/data-table/docs/en-US.md index 3a3c4487fac..fcc11eec246 100644 --- a/packages/varlet-ui/src/data-table/docs/en-US.md +++ b/packages/varlet-ui/src/data-table/docs/en-US.md @@ -129,6 +129,32 @@ const columns = [ ``` +### Grouped Header + +Set `children` on a column to group leaf columns under a shared header cell. Only leaf columns render body cells. + +```html + + + +``` + ### Custom Props Use `row-props` and `column.cellProps` to pass native attributes to rows and cells. @@ -658,6 +684,7 @@ const data = computed(() => applySorters(rawData, sorters.value)) | `type` | Column type. Supports `selection` and `expand` | _'selection' \| 'expand'_ | `-` | | `key` | Unique column key | _string_ | `-` | | `title` | Column title | _string_ | `-` | +| `children` | Child columns used to render a grouped header | _DataTableColumn[]_ | `-` | | `sorter` | Whether the field column shows sorter interaction | _boolean_ | `false` | | `multiple` | Whether the selection column allows multiple rows | _boolean_ | `true` | | `selectable` | Whether selection is enabled. Supports `boolean` or `(context) => boolean` | _boolean \| `(context) => boolean`_ | `true` | diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index 8596fb37093..784ccfe8714 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -129,6 +129,32 @@ const columns = [ ``` +### 分组表头 + +给列配置设置 `children` 后,可以把多个叶子列归到同一个表头分组下。表体只会渲染叶子列对应的单元格。 + +```html + + + +``` + ### 属性透传 通过 `row-props` 和 `column.cellProps` 向行和单元格透传原生属性。 @@ -658,6 +684,7 @@ const data = computed(() => applySorters(rawData, sorters.value)) | `type` | 列类型。支持 `selection` 和 `expand` | _'selection' \| 'expand'_ | `-` | | `key` | 列唯一 key | _string_ | `-` | | `title` | 列标题 | _string_ | `-` | +| `children` | 子列配置,用于渲染分组表头 | _DataTableColumn[]_ | `-` | | `sorter` | 字段列是否显示排序交互 | _boolean_ | `false` | | `multiple` | 选择列是否允许多选,仅对选择列生效 | _boolean_ | `true` | | `selectable` | 是否允许选择。支持 `boolean` 或 `(context) => boolean`,仅对选择列生效 | _boolean \| `(context) => boolean`_ | `true` | diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index e7cf8b5ed55..d8920305a1a 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -277,6 +277,19 @@ const alignedColumns = [ { key: 'role', title: 'Role', titleAlign: 'center', align: 'center', width: 120 }, { key: 'status', title: 'Status', titleAlign: 'right', align: 'right', width: 100 }, ] +const groupedHeaderColumns = [ + { + title: 'Profile', + children: [ + { key: 'name', title: 'Name', width: 140 }, + { key: 'role', title: 'Role', width: 140 }, + ], + }, + { + title: 'State', + children: [{ key: 'status', title: 'Status', width: 120 }], + }, +] const getStatusCellProps = ({ row }) => ({ style: { @@ -384,6 +397,9 @@ function applySorters(rows, activeSorters) { {{ t('columnOptions') }} + {{ t('groupedHeader') }} + + {{ t('customProps') }} diff --git a/packages/varlet-ui/src/data-table/example/locale/en-US.ts b/packages/varlet-ui/src/data-table/example/locale/en-US.ts index 2dddc045032..00c77837428 100644 --- a/packages/varlet-ui/src/data-table/example/locale/en-US.ts +++ b/packages/varlet-ui/src/data-table/example/locale/en-US.ts @@ -3,6 +3,7 @@ export default { cellBordered: 'Cell Bordered', sizes: 'Sizes', columnOptions: 'Column Options', + groupedHeader: 'Grouped Header', customProps: 'Custom Props', customRender: 'Custom Render', surfaceLow: 'Subtle Background', diff --git a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts index fd21d33262c..a9fa5617620 100644 --- a/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/data-table/example/locale/zh-CN.ts @@ -3,6 +3,7 @@ export default { cellBordered: '单元格分割线', sizes: '尺寸', columnOptions: '列配置', + groupedHeader: '分组表头', customProps: '属性透传', customRender: '自定义渲染', surfaceLow: '弱背景色', diff --git a/packages/varlet-ui/src/data-table/props.ts b/packages/varlet-ui/src/data-table/props.ts index fff712fd30e..b73b8894a1b 100644 --- a/packages/varlet-ui/src/data-table/props.ts +++ b/packages/varlet-ui/src/data-table/props.ts @@ -68,6 +68,7 @@ export interface DataTableFieldColumn extends DataTableBaseColumn[] sorter?: boolean render?: (context: DataTableColumnRenderContext) => VNodeChild } diff --git a/packages/varlet-ui/src/data-table/useSorter.ts b/packages/varlet-ui/src/data-table/useSorter.ts index aade6d447c6..5ac8c69e697 100644 --- a/packages/varlet-ui/src/data-table/useSorter.ts +++ b/packages/varlet-ui/src/data-table/useSorter.ts @@ -18,7 +18,7 @@ export function useSorter({ sorters, sortMode, onUpdateSorters }: UseSorterOptio const activeSorters = computed(() => sorters()) function isColumnSortable(column: DataTableColumn): column is DataTableFieldColumn { - return column.type == null && column.sorter === true + return column.type == null && !column.children?.length && column.sorter === true } function getColumnSorterOrder(columnKey: string): DataTableSorterOrder | undefined { diff --git a/packages/varlet-ui/types/dataTable.d.ts b/packages/varlet-ui/types/dataTable.d.ts index c3c62af4b63..27d24c176fa 100644 --- a/packages/varlet-ui/types/dataTable.d.ts +++ b/packages/varlet-ui/types/dataTable.d.ts @@ -61,6 +61,7 @@ export interface DataTableFieldColumn extends DataTableBaseColumn[] sorter?: boolean render?: (context: DataTableColumnRenderContext) => VNodeChild } From 03ca9d71f1f94b442d65ee6539d83f49cef4d6dd Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Sat, 23 May 2026 23:48:50 +0800 Subject: [PATCH 20/37] feat(data-table): update column widths and add cell-bordered prop for improved layout --- packages/varlet-ui/src/data-table/dataTable.less | 2 +- packages/varlet-ui/src/data-table/docs/en-US.md | 8 ++++---- packages/varlet-ui/src/data-table/docs/zh-CN.md | 8 ++++---- packages/varlet-ui/src/data-table/example/index.vue | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/varlet-ui/src/data-table/dataTable.less b/packages/varlet-ui/src/data-table/dataTable.less index e1a8bbd4402..fa5b8a5881a 100644 --- a/packages/varlet-ui/src/data-table/dataTable.less +++ b/packages/varlet-ui/src/data-table/dataTable.less @@ -101,7 +101,7 @@ font-size: var(--data-table-header-font-size); font-weight: 500; background: var(--data-table-header-cell-background); - border-bottom: 1px solid var(--data-table-border-color); + box-shadow: inset 0 -1px 0 var(--data-table-border-color); position: sticky; top: 0; } diff --git a/packages/varlet-ui/src/data-table/docs/en-US.md b/packages/varlet-ui/src/data-table/docs/en-US.md index fcc11eec246..fc51506f059 100644 --- a/packages/varlet-ui/src/data-table/docs/en-US.md +++ b/packages/varlet-ui/src/data-table/docs/en-US.md @@ -139,19 +139,19 @@ const columns = [ { title: 'Profile', children: [ - { key: 'name', title: 'Name', width: 140 }, - { key: 'role', title: 'Role', width: 140 }, + { key: 'name', title: 'Name', width: 112 }, + { key: 'role', title: 'Role', width: 112 }, ], }, { title: 'State', - children: [{ key: 'status', title: 'Status', width: 120 }], + children: [{ key: 'status', title: 'Status', width: 96 }], }, ] ``` diff --git a/packages/varlet-ui/src/data-table/docs/zh-CN.md b/packages/varlet-ui/src/data-table/docs/zh-CN.md index 784ccfe8714..17c7e5e530b 100644 --- a/packages/varlet-ui/src/data-table/docs/zh-CN.md +++ b/packages/varlet-ui/src/data-table/docs/zh-CN.md @@ -139,19 +139,19 @@ const columns = [ { title: '资料', children: [ - { key: 'name', title: '姓名', width: 140 }, - { key: 'role', title: '角色', width: 140 }, + { key: 'name', title: '姓名', width: 112 }, + { key: 'role', title: '角色', width: 112 }, ], }, { title: '状态分组', - children: [{ key: 'status', title: '状态', width: 120 }], + children: [{ key: 'status', title: '状态', width: 96 }], }, ] ``` diff --git a/packages/varlet-ui/src/data-table/example/index.vue b/packages/varlet-ui/src/data-table/example/index.vue index d8920305a1a..96fb28e749b 100644 --- a/packages/varlet-ui/src/data-table/example/index.vue +++ b/packages/varlet-ui/src/data-table/example/index.vue @@ -281,13 +281,13 @@ const groupedHeaderColumns = [ { title: 'Profile', children: [ - { key: 'name', title: 'Name', width: 140 }, - { key: 'role', title: 'Role', width: 140 }, + { key: 'name', title: 'Name', width: 112 }, + { key: 'role', title: 'Role', width: 112 }, ], }, { title: 'State', - children: [{ key: 'status', title: 'Status', width: 120 }], + children: [{ key: 'status', title: 'Status', width: 96 }], }, ] @@ -398,7 +398,7 @@ function applySorters(rows, activeSorters) { {{ t('groupedHeader') }} - + {{ t('customProps') }} From ae70fb00b44ce60276409fe8f5bf364a8834b054 Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Sun, 24 May 2026 01:36:48 +0800 Subject: [PATCH 21/37] feat(data-table): implement useColumnWidths for dynamic column resizing functionality --- .../src/data-table/{useColumnWidths.ts => useColumnSizes.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/varlet-ui/src/data-table/{useColumnWidths.ts => useColumnSizes.ts} (100%) diff --git a/packages/varlet-ui/src/data-table/useColumnWidths.ts b/packages/varlet-ui/src/data-table/useColumnSizes.ts similarity index 100% rename from packages/varlet-ui/src/data-table/useColumnWidths.ts rename to packages/varlet-ui/src/data-table/useColumnSizes.ts From 0e930a649b2e02a9594163eed71d2b74e2aae893 Mon Sep 17 00:00:00 2001 From: haoziqaq <357229046@qq.com> Date: Sun, 24 May 2026 01:36:57 +0800 Subject: [PATCH 22/37] feat(data-table): refactor column handling and introduce useHeaderRows for improved header management --- .../varlet-ui/src/data-table/DataTable.vue | 159 ++---------------- .../src/data-table/DataTableHeaderCell.vue | 13 +- .../src/data-table/__tests__/index.spec.js | 146 ++++++++++++++++ .../src/data-table/useColumnSizes.ts | 4 +- .../varlet-ui/src/data-table/useHeaderRows.ts | 145 ++++++++++++++++ .../src/table/__tests__/index.spec.js | 60 +++++++ 6 files changed, 372 insertions(+), 155 deletions(-) create mode 100644 packages/varlet-ui/src/data-table/useHeaderRows.ts diff --git a/packages/varlet-ui/src/data-table/DataTable.vue b/packages/varlet-ui/src/data-table/DataTable.vue index da6eeb5ff5b..e2319167255 100644 --- a/packages/varlet-ui/src/data-table/DataTable.vue +++ b/packages/varlet-ui/src/data-table/DataTable.vue @@ -15,10 +15,10 @@ >
- +
@@ -80,7 +80,7 @@ -
+
@@ -123,7 +123,7 @@ ``` -### Plain Table -Set `plain` to remove the card-like shadow, background, and radius, and present the table as a pure table surface. +### Custom Column Render ```html + + ``` -### Single Column Sorting -Set `column.sorter` to enable sorter interaction on a field column. The component only manages sorter state. You should control `v-model:sorters` and decide how to sort the data yourself. +### Frontend Pagination ```html -const sorters = ref([]) + +``` -const columns = [ - { key: 'name', title: 'Name', sorter: true }, - { key: 'role', title: 'Role', sorter: true }, - { key: 'status', title: 'Status', sorter: true }, -] +### Remote Pagination -const data = computed(() => { - return sorters.value.reduceRight((rows, sorter) => { - return [...rows].sort((left, right) => { - const leftValue = left[sorter.key] - const rightValue = right[sorter.key] +```html + ``` -### Multiple Column Sorting - -Set `sort-mode="multiple"` to keep multiple columns active at the same time. +### Single Column Sorting ```html + + ``` -### Cell Bordered +### Multiple Column Sorting ```html - -``` + -```html ``` ### Column Options -Use `width`, `minWidth`, `align`, and `titleAlign` to control column width and alignment. - ```html