x-next 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI_CODING.md +6 -5
- package/AI_USAGE/catalog.md +106 -105
- package/AI_USAGE/components/alert-api.md +48 -0
- package/AI_USAGE/components/alert.md +222 -3
- package/AI_USAGE/components/bar-chart-api.md +177 -0
- package/AI_USAGE/components/bar-chart.md +297 -3
- package/AI_USAGE/components/boxicons.md +147 -0
- package/AI_USAGE/components/button-api.md +144 -0
- package/AI_USAGE/components/button.md +447 -3
- package/AI_USAGE/components/charts.md +81 -0
- package/AI_USAGE/components/checkbox-api.md +145 -0
- package/AI_USAGE/components/checkbox.md +365 -3
- package/AI_USAGE/components/color-picker-api.md +52 -0
- package/AI_USAGE/components/color-picker.md +211 -3
- package/AI_USAGE/components/config-provider-api.md +38 -0
- package/AI_USAGE/components/config-provider.md +250 -3
- package/AI_USAGE/components/date-picker-api.md +1114 -0
- package/AI_USAGE/components/date-picker.md +378 -3
- package/AI_USAGE/components/dialog-api.md +142 -0
- package/AI_USAGE/components/dialog.md +284 -3
- package/AI_USAGE/components/draggable-api.md +146 -0
- package/AI_USAGE/components/draggable.md +259 -0
- package/AI_USAGE/components/drawer-api.md +78 -0
- package/AI_USAGE/components/drawer.md +227 -0
- package/AI_USAGE/components/dropdown-api.md +529 -0
- package/AI_USAGE/components/dropdown.md +392 -3
- package/AI_USAGE/components/empty-api.md +36 -0
- package/AI_USAGE/components/empty.md +227 -3
- package/AI_USAGE/components/finicon.md +88 -0
- package/AI_USAGE/components/form-api.md +524 -0
- package/AI_USAGE/components/form.md +454 -0
- package/AI_USAGE/components/grid-api.md +527 -0
- package/AI_USAGE/components/grid.md +224 -0
- package/AI_USAGE/components/heatmap-api.md +122 -0
- package/AI_USAGE/components/heatmap.md +220 -0
- package/AI_USAGE/components/icon-api.md +4707 -0
- package/AI_USAGE/components/icon.md +257 -0
- package/AI_USAGE/components/image-api.md +440 -0
- package/AI_USAGE/components/image.md +465 -0
- package/AI_USAGE/components/input-api.md +184 -0
- package/AI_USAGE/components/input-number-api.md +66 -0
- package/AI_USAGE/components/input-number.md +294 -0
- package/AI_USAGE/components/input-tag-api.md +114 -0
- package/AI_USAGE/components/input-tag.md +482 -0
- package/AI_USAGE/components/input.md +293 -3
- package/AI_USAGE/components/layout-api.md +147 -0
- package/AI_USAGE/components/layout.md +230 -0
- package/AI_USAGE/components/line-chart-api.md +197 -0
- package/AI_USAGE/components/line-chart.md +279 -3
- package/AI_USAGE/components/link-api.md +42 -0
- package/AI_USAGE/components/link.md +197 -0
- package/AI_USAGE/components/menu-api.md +516 -0
- package/AI_USAGE/components/menu.md +268 -0
- package/AI_USAGE/components/message-api.md +215 -0
- package/AI_USAGE/components/message-box-api.md +108 -0
- package/AI_USAGE/components/message-box.md +268 -3
- package/AI_USAGE/components/message.md +231 -3
- package/AI_USAGE/components/network-api.md +91 -0
- package/AI_USAGE/components/network.md +378 -0
- package/AI_USAGE/components/notification-api.md +236 -0
- package/AI_USAGE/components/notification.md +275 -3
- package/AI_USAGE/components/pagination-api.md +62 -0
- package/AI_USAGE/components/pagination.md +386 -5
- package/AI_USAGE/components/pie-chart-api.md +109 -0
- package/AI_USAGE/components/pie-chart.md +245 -3
- package/AI_USAGE/components/popconfirm-api.md +75 -0
- package/AI_USAGE/components/popconfirm.md +231 -3
- package/AI_USAGE/components/popover-api.md +75 -0
- package/AI_USAGE/components/popover.md +208 -3
- package/AI_USAGE/components/popup-api.md +75 -0
- package/AI_USAGE/components/popup.md +52 -0
- package/AI_USAGE/components/progress-api.md +44 -0
- package/AI_USAGE/components/progress.md +248 -3
- package/AI_USAGE/components/radio-api.md +165 -0
- package/AI_USAGE/components/radio.md +259 -3
- package/AI_USAGE/components/rate-api.md +54 -0
- package/AI_USAGE/components/rate.md +233 -3
- package/AI_USAGE/components/resize-box-api.md +56 -0
- package/AI_USAGE/components/resize-box.md +231 -0
- package/AI_USAGE/components/scrollbar-api.md +77 -0
- package/AI_USAGE/components/scrollbar.md +218 -0
- package/AI_USAGE/components/select-api.md +758 -0
- package/AI_USAGE/components/select.md +640 -3
- package/AI_USAGE/components/skeleton-api.md +85 -0
- package/AI_USAGE/components/skeleton.md +150 -0
- package/AI_USAGE/components/slider-api.md +51 -0
- package/AI_USAGE/components/slider.md +233 -3
- package/AI_USAGE/components/space-api.md +38 -0
- package/AI_USAGE/components/space.md +145 -0
- package/AI_USAGE/components/spin-api.md +43 -0
- package/AI_USAGE/components/spin.md +205 -0
- package/AI_USAGE/components/steps-api.md +111 -0
- package/AI_USAGE/components/steps.md +201 -3
- package/AI_USAGE/components/switch-api.md +58 -0
- package/AI_USAGE/components/switch.md +290 -3
- package/AI_USAGE/components/table-api.md +2919 -0
- package/AI_USAGE/components/table.md +1414 -0
- package/AI_USAGE/components/tabs-api.md +135 -0
- package/AI_USAGE/components/tabs.md +308 -3
- package/AI_USAGE/components/tag-api.md +124 -0
- package/AI_USAGE/components/tag.md +292 -0
- package/AI_USAGE/components/textarea-api.md +55 -0
- package/AI_USAGE/components/textarea.md +326 -3
- package/AI_USAGE/components/time-picker-api.md +137 -0
- package/AI_USAGE/components/time-picker.md +271 -3
- package/AI_USAGE/components/time-range-selector-api.md +44 -0
- package/AI_USAGE/components/time-range-selector.md +355 -0
- package/AI_USAGE/components/timeline-api.md +93 -0
- package/AI_USAGE/components/timeline.md +312 -0
- package/AI_USAGE/components/tooltip-api.md +77 -0
- package/AI_USAGE/components/tooltip.md +182 -0
- package/AI_USAGE/components/trend-chart-api.md +119 -0
- package/AI_USAGE/components/trend-chart.md +335 -0
- package/AI_USAGE/components/trigger-api.md +124 -0
- package/AI_USAGE/components/trigger.md +185 -0
- package/AI_USAGE/components/upload-api.md +200 -0
- package/AI_USAGE/components/upload.md +711 -3
- package/AI_USAGE/components/watermark-api.md +87 -0
- package/AI_USAGE/components/watermark.md +200 -0
- package/AI_USAGE/core.md +1 -1
- package/AI_USAGE/index.md +35 -7
- package/AI_USAGE/recipes/form-on-demand.md +1 -1
- package/AI_USAGE.md +33657 -7
- package/CHANGELOG.md +40 -0
- package/dist/_utils/inherit-component-type.d.ts +11 -0
- package/dist/components/date-picker/index.d.ts +420 -5
- package/dist/components/form-input/index.d.ts +617 -2
- package/dist/components/layout/index.d.ts +689 -1
- package/dist/components/menu/index.d.ts +786 -1
- package/dist/components/network/Network.vue.d.ts +7 -3
- package/dist/components/network/index.d.ts +10 -2
- package/dist/components/network/props.d.ts +5 -0
- package/dist/components/network/utils.d.ts +3 -3
- package/dist/components/pagination/page-item-ellipsis.vue.d.ts +4 -1
- package/dist/components/pagination/page-item-step.vue.d.ts +101 -130
- package/dist/components/pagination/page-item.vue.d.ts +1 -0
- package/dist/components/pagination/page-jumper.vue.d.ts +1 -1
- package/dist/components/pagination/page-options.vue.d.ts +6 -4
- package/dist/components/pagination/utils.d.ts +3 -0
- package/dist/components/time-range-selector/index.d.ts +6 -6
- package/dist/es/_container/resize-observer-v2.js +1 -1
- package/dist/es/_container/resize-observer.js +1 -1
- package/dist/es/_hooks/use-resize-observer.js +1 -1
- package/dist/es/_styles/palette-aliases.js +4 -4
- package/dist/es/_utils/inherit-component-type.js +6 -0
- package/dist/es/_virtual/advancedFormat.js +1 -1
- package/dist/es/_virtual/customParseFormat.js +1 -1
- package/dist/es/_virtual/dayjs.min.js +1 -1
- package/dist/es/_virtual/isBetween.js +1 -1
- package/dist/es/_virtual/quarterOfYear.js +1 -1
- package/dist/es/_virtual/weekOfYear.js +1 -1
- package/dist/es/_virtual/weekYear.js +1 -1
- package/dist/es/_virtual/zh-cn.js +1 -1
- package/dist/es/components/button/Button.js +53 -42
- package/dist/es/components/date-picker/index.js +18 -17
- package/dist/es/components/draggable/useDraggable.js +1 -1
- package/dist/es/components/form/Form.vue2.js +1 -1
- package/dist/es/components/form/FormItem.vue2.js +1 -1
- package/dist/es/components/form-input/index.js +12 -11
- package/dist/es/components/form-input-number/InputNumber.js +1 -1
- package/dist/es/components/form-slider/Slider.vue2.js +1 -1
- package/dist/es/components/form-slider/slider-ticks.vue2.js +1 -1
- package/dist/es/components/form-slider/utils.js +1 -1
- package/dist/es/components/layout/index.js +10 -9
- package/dist/es/components/menu/MenuItem.js +1 -1
- package/dist/es/components/menu/hooks/use-level.js +5 -5
- package/dist/es/components/menu/index.js +13 -10
- package/dist/es/components/menu/overflow-wrap.js +1 -1
- package/dist/es/components/network/Network.vue.js +17 -12
- package/dist/es/components/network/Network.vue2.js +78 -70
- package/dist/es/components/network/utils.js +112 -71
- package/dist/es/components/pagination/Pagination.js +132 -123
- package/dist/es/components/pagination/page-item-ellipsis.vue.js +10 -10
- package/dist/es/components/pagination/page-item-ellipsis.vue2.js +24 -19
- package/dist/es/components/pagination/page-item-step.vue.js +14 -11
- package/dist/es/components/pagination/page-item-step.vue2.js +20 -22
- package/dist/es/components/pagination/page-item.vue.js +10 -9
- package/dist/es/components/pagination/page-item.vue2.js +24 -19
- package/dist/es/components/pagination/page-jumper.vue2.js +23 -23
- package/dist/es/components/pagination/page-options.vue.js +15 -10
- package/dist/es/components/pagination/page-options.vue2.js +21 -17
- package/dist/es/components/pagination/utils.js +4 -2
- package/dist/es/components/progress/circle.vue2.js +1 -1
- package/dist/es/components/progress/line.vue2.js +1 -1
- package/dist/es/components/progress/steps.vue2.js +1 -1
- package/dist/es/components/rate/Rate.js +1 -1
- package/dist/es/components/virtual-list-v2/virtual-list-item.js +1 -1
- package/dist/es/locale/en-US.js +2 -0
- package/dist/es/locale/zh-CN.js +2 -0
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/dayjs.min.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/locale/zh-cn.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/plugin/advancedFormat.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/plugin/customParseFormat.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/plugin/isBetween.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/plugin/quarterOfYear.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/plugin/weekOfYear.js +1 -1
- package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/dayjs@1.11.21/node_modules/dayjs/plugin/weekYear.js +1 -1
- package/dist/es/style.css +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.umd.js +3 -3
- package/dist/locale/interface.d.ts +3 -0
- package/dist/style/base.css +1 -1
- package/dist/style/button.css +1 -1
- package/dist/style/form-select.css +1 -1
- package/dist/style/network.css +1 -1
- package/dist/style/pagination.css +1 -1
- package/dist/style/table.css +1 -1
- package/dist/style.css +1 -1
- package/package.json +10 -5
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/index.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/is.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/locale/en-US.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/array.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/base.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/boolean.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/custom.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/number.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/object.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/string.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/rules/type.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/b-validate@1.5.3/node_modules/b-validate/es/util.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/compute-scroll-into-view@3.1.1/node_modules/compute-scroll-into-view/dist/index.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/number-precision@1.6.0/node_modules/number-precision/build/index.es.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/resize-observer-polyfill@1.5.1/node_modules/resize-observer-polyfill/dist/ResizeObserver.es.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/scroll-into-view-if-needed@3.1.0/node_modules/scroll-into-view-if-needed/dist/index.js +0 -0
- /package/dist/es/{Users/xuyu/Code/x-next/node_modules → node_modules}/.pnpm/sortablejs@1.15.7/node_modules/sortablejs/modular/sortable.esm.js +0 -0
|
@@ -0,0 +1,1414 @@
|
|
|
1
|
+
<!-- Generated by pnpm generate:ai-usage. Edit script/ai-usage/content.md and script/ai-usage/. -->
|
|
2
|
+
# Table AI 使用
|
|
3
|
+
|
|
4
|
+
适用版本:x-next@1.14.0;来源指纹:`014984639d816a2c7e25eb22690c586b0b2e8efba6846bec8a4e424db41a09e9`。
|
|
5
|
+
|
|
6
|
+
先读 [通用接入](../core.md)。公开导出:`Table, TableColumn, Tbody, Td, Th, Thead, Tr`;主要模型:v-model:selected-keys、v-model:expanded-keys。完整 Props、Events、Slots、实例方法与服务签名见 [本地 API](./table-api.md),包含本组件族全部子组件和别名。
|
|
7
|
+
|
|
8
|
+
按需样式:`x-next/style/table`(包含 base.css、button.css、empty.css、form-checkbox.css、form-input.css、form-input-number.css、form-radio.css、form-select.css、pagination.css、scrollbar-v2.css、spin.css、table.css、trigger.css、tooltip.css)。代码块未单独引样式时在应用入口加载全量样式,或按实际所有组件加载 JS 入口;不要仅因复制一个组件页就漏掉其中的 Button 等组合依赖。
|
|
9
|
+
|
|
10
|
+
## 场景用法
|
|
11
|
+
|
|
12
|
+
以下示例从本地 `src/docs/components/table.md` 显式导入到独立 AI 源,补齐局部注册、状态和样式后进行严格模板检查。上方已核验行为优先;原始 prop 默认值、包装透传和示例业务适配分别查 [完整 API](./table-api.md) 与场景说明。类型通过不代表所有散文和交互已验证。
|
|
13
|
+
|
|
14
|
+
### 用途与选型
|
|
15
|
+
|
|
16
|
+
用于数据收集展示、分析整理和操作处理,适合中后台列表、配置项矩阵、数据明细和可批量处理的记录集合。
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### 何时使用
|
|
20
|
+
|
|
21
|
+
- 需要展示结构化数据,并支持排序、筛选、分页、滚动或固定列。
|
|
22
|
+
- 需要对数据行进行选择、展开、拖拽、合并或汇总。
|
|
23
|
+
- 需要通过插槽或渲染函数定制表头、单元格、空态、分页两侧内容或内部表格元素。
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### 基础用法
|
|
27
|
+
|
|
28
|
+
```vue
|
|
29
|
+
<script setup lang="ts">
|
|
30
|
+
import 'x-next/style.css';
|
|
31
|
+
import { Table as XTable } from 'x-next';
|
|
32
|
+
import type { TableColumnData } from 'x-next';
|
|
33
|
+
|
|
34
|
+
const columns: TableColumnData[] = [
|
|
35
|
+
{ title: '名称', dataIndex: 'name' },
|
|
36
|
+
{ title: '类型', dataIndex: 'type' },
|
|
37
|
+
{ title: '状态', dataIndex: 'status' },
|
|
38
|
+
];
|
|
39
|
+
const data = [
|
|
40
|
+
{ key: '1', name: 'Button', type: '通用', status: '稳定' },
|
|
41
|
+
{ key: '2', name: 'Select', type: '表单', status: '维护中' },
|
|
42
|
+
{ key: '3', name: 'Table', type: '数据展示', status: '改造中' },
|
|
43
|
+
];
|
|
44
|
+
</script>
|
|
45
|
+
<template>
|
|
46
|
+
<XTable :columns="columns" :data="data" />
|
|
47
|
+
</template>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 声明式列
|
|
51
|
+
|
|
52
|
+
```vue
|
|
53
|
+
<script setup lang="ts">
|
|
54
|
+
import 'x-next/style.css';
|
|
55
|
+
import { Table as XTable, TableColumn as XTableColumn } from 'x-next';
|
|
56
|
+
|
|
57
|
+
const data = [
|
|
58
|
+
{ key: '1', name: 'Button', status: '稳定' },
|
|
59
|
+
{ key: '2', name: 'Table', status: '维护中' },
|
|
60
|
+
];
|
|
61
|
+
</script>
|
|
62
|
+
<template>
|
|
63
|
+
<XTable :data="data" :pagination="false">
|
|
64
|
+
<XTableColumn title="名称" data-index="name" />
|
|
65
|
+
<XTableColumn title="状态" data-index="status" />
|
|
66
|
+
</XTable>
|
|
67
|
+
</template>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 尺寸和边框
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
import 'x-next/style.css';
|
|
75
|
+
import { Table as XTable } from 'x-next';
|
|
76
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
77
|
+
|
|
78
|
+
const sizes = ['mini', 'small', 'medium', 'large'] as const;
|
|
79
|
+
const columns: TableColumnData[] = [
|
|
80
|
+
{ title: '规格', dataIndex: 'name' },
|
|
81
|
+
{ title: '说明', dataIndex: 'desc' },
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const data: TableData[] = [
|
|
85
|
+
{ key: '1', name: 'large', desc: '默认尺寸' },
|
|
86
|
+
{ key: '2', name: 'small', desc: '紧凑展示' },
|
|
87
|
+
];
|
|
88
|
+
</script>
|
|
89
|
+
<template>
|
|
90
|
+
<XTable v-for="size in sizes" :key="size" :columns="columns" :data="data" :size="size" :pagination="false" />
|
|
91
|
+
<XTable
|
|
92
|
+
:columns="columns"
|
|
93
|
+
:data="data"
|
|
94
|
+
size="mini"
|
|
95
|
+
:bordered="{ wrapper: true, cell: true }"
|
|
96
|
+
:pagination="false"
|
|
97
|
+
/>
|
|
98
|
+
</template>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 行选择器
|
|
102
|
+
|
|
103
|
+
```vue
|
|
104
|
+
<script setup lang="ts">
|
|
105
|
+
import 'x-next/style.css';
|
|
106
|
+
import { ref } from 'vue';
|
|
107
|
+
import { Table as XTable } from 'x-next';
|
|
108
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
109
|
+
|
|
110
|
+
const selectedKeys = ref<(string | number)[]>(['1', '2']);
|
|
111
|
+
|
|
112
|
+
const columns: TableColumnData[] = [
|
|
113
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
114
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
115
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
116
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const data: TableData[] = [
|
|
120
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
121
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
122
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
123
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
124
|
+
];
|
|
125
|
+
</script>
|
|
126
|
+
<template>
|
|
127
|
+
<XTable
|
|
128
|
+
v-model:selected-keys="selectedKeys"
|
|
129
|
+
:columns="columns"
|
|
130
|
+
:data="data"
|
|
131
|
+
:row-selection="{ type: 'checkbox', showCheckedAll: true, onlyCurrent: false }"
|
|
132
|
+
:pagination="{ pageSize: 3 }"
|
|
133
|
+
/>
|
|
134
|
+
</template>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 单选
|
|
138
|
+
|
|
139
|
+
```vue
|
|
140
|
+
<script setup lang="ts">
|
|
141
|
+
import 'x-next/style.css';
|
|
142
|
+
import { ref } from 'vue';
|
|
143
|
+
import { Table as XTable } from 'x-next';
|
|
144
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
145
|
+
|
|
146
|
+
const columns: TableColumnData[] = [
|
|
147
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
148
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
149
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
150
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
const data: TableData[] = [
|
|
154
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
155
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
156
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
157
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
const selectedKeys = ref<(string | number)[]>(['2']);
|
|
161
|
+
</script>
|
|
162
|
+
<template>
|
|
163
|
+
<XTable
|
|
164
|
+
v-model:selected-keys="selectedKeys"
|
|
165
|
+
:columns="columns"
|
|
166
|
+
:data="data"
|
|
167
|
+
:row-selection="{ type: 'radio' }"
|
|
168
|
+
:pagination="false"
|
|
169
|
+
/>
|
|
170
|
+
</template>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 展开行
|
|
174
|
+
|
|
175
|
+
```vue
|
|
176
|
+
<script setup lang="ts">
|
|
177
|
+
import 'x-next/style.css';
|
|
178
|
+
const expandable = {
|
|
179
|
+
title: '展开',
|
|
180
|
+
width: 72,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
import { ref } from 'vue';
|
|
184
|
+
import { Table as XTable } from 'x-next';
|
|
185
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
186
|
+
|
|
187
|
+
const columns: TableColumnData[] = [
|
|
188
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
189
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
190
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
191
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
const data: TableData[] = [
|
|
195
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
196
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
197
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
198
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const expandedKeys = ref<(string | number)[]>(['1']);
|
|
202
|
+
</script>
|
|
203
|
+
<template>
|
|
204
|
+
<XTable
|
|
205
|
+
v-model:expanded-keys="expandedKeys"
|
|
206
|
+
:columns="columns"
|
|
207
|
+
:data="data"
|
|
208
|
+
:expandable="{ title: '展开', width: 72 }"
|
|
209
|
+
>
|
|
210
|
+
<template #expand-row="{ record }">
|
|
211
|
+
{{ record.expand || `${record.name} 暂无额外说明。` }}
|
|
212
|
+
</template>
|
|
213
|
+
</XTable>
|
|
214
|
+
</template>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 树形数据
|
|
218
|
+
|
|
219
|
+
```vue
|
|
220
|
+
<script setup lang="ts">
|
|
221
|
+
import 'x-next/style.css';
|
|
222
|
+
import { Table as XTable } from 'x-next';
|
|
223
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
224
|
+
|
|
225
|
+
const columns: TableColumnData[] = [
|
|
226
|
+
{ title: '名称', dataIndex: 'name' },
|
|
227
|
+
{ title: '类型', dataIndex: 'type' },
|
|
228
|
+
{ title: '状态', dataIndex: 'status' },
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
const data: TableData[] = [
|
|
232
|
+
{
|
|
233
|
+
key: '1',
|
|
234
|
+
name: '数据录入',
|
|
235
|
+
type: '表单',
|
|
236
|
+
status: '稳定',
|
|
237
|
+
children: [
|
|
238
|
+
{ key: '1-1', name: 'Input', type: '表单', status: '稳定' },
|
|
239
|
+
{ key: '1-2', name: 'Select', type: '表单', status: '维护中' },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
key: '2',
|
|
244
|
+
name: '数据展示',
|
|
245
|
+
type: '展示',
|
|
246
|
+
status: '改造中',
|
|
247
|
+
children: [
|
|
248
|
+
{ key: '2-1', name: 'Table', type: '展示', status: '改造中' },
|
|
249
|
+
{ key: '2-2', name: 'Timeline', type: '展示', status: '稳定' },
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
</script>
|
|
254
|
+
<template>
|
|
255
|
+
<XTable
|
|
256
|
+
:columns="columns"
|
|
257
|
+
:data="data"
|
|
258
|
+
:default-expanded-keys="['1']"
|
|
259
|
+
:pagination="false"
|
|
260
|
+
/>
|
|
261
|
+
</template>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 排序和筛选
|
|
265
|
+
|
|
266
|
+
可排序表头可通过 `Tab` 聚焦,并使用 `Enter` 或 `Space` 切换排序;读屏软件可从 `aria-sort` 获得当前顺序。筛选图标为原生按钮,会通过 `aria-expanded` 和 `aria-controls` 关联筛选弹层。
|
|
267
|
+
|
|
268
|
+
键盘打开筛选会聚焦首个选项;单选选项可用方向键切换。按 `Escape` 关闭弹层并返回筛选按钮,未确认的选择不会改变表格结果。
|
|
269
|
+
|
|
270
|
+
```vue
|
|
271
|
+
<script setup lang="ts">
|
|
272
|
+
import 'x-next/style.css';
|
|
273
|
+
import { ref } from 'vue';
|
|
274
|
+
import { Table as XTable } from 'x-next';
|
|
275
|
+
import type { TableColumnData, TableData, TableChangeExtra } from 'x-next';
|
|
276
|
+
|
|
277
|
+
const columns: TableColumnData[] = [
|
|
278
|
+
{
|
|
279
|
+
title: '姓名',
|
|
280
|
+
dataIndex: 'name',
|
|
281
|
+
sortable: { sortDirections: ['ascend', 'descend'] },
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
title: '薪资',
|
|
285
|
+
dataIndex: 'salary',
|
|
286
|
+
filterable: {
|
|
287
|
+
filters: [{ text: '20000 以上', value: '20000' }],
|
|
288
|
+
filter: (values, record) => values.some((value) => record.salary > Number(value)),
|
|
289
|
+
multiple: true,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
const data: TableData[] = [
|
|
295
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
296
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
297
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
298
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
const alignLeft = ref(false);
|
|
302
|
+
|
|
303
|
+
const log = ref('尚未排序或筛选');
|
|
304
|
+
const handleChange = (_rows: TableData[], extra: TableChangeExtra, currentData: TableData[]) => {
|
|
305
|
+
log.value = `${extra.type}:共 ${currentData.length} 条符合条件`;
|
|
306
|
+
};
|
|
307
|
+
</script>
|
|
308
|
+
<template>
|
|
309
|
+
<XTable
|
|
310
|
+
:columns="columns"
|
|
311
|
+
:data="data"
|
|
312
|
+
:filter-icon-align-left="alignLeft"
|
|
313
|
+
@change="handleChange"
|
|
314
|
+
/>
|
|
315
|
+
|
|
316
|
+
<p>{{ log }}</p>
|
|
317
|
+
</template>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 多列排序
|
|
321
|
+
|
|
322
|
+
为参与多列排序的列声明 `sortable.sortPriority`,数字越大越先比较,相同优先级按列顺序比较。点击这些表头只改变各自条件;点击没有优先级的列仍切换为单列排序。每列支持 `sortOrder` 受控、默认方向和自定义比较函数。
|
|
323
|
+
|
|
324
|
+
`change` 的 `extra.sorters` 给出全部有效条件,`extra.sorter` 保留第一个条件,兼容已有消费方。比较函数接收方向,需自行返回对应顺序;`sorter: true` 的列交给服务端排序。
|
|
325
|
+
|
|
326
|
+
```vue
|
|
327
|
+
<script setup lang="ts">
|
|
328
|
+
import 'x-next/style.css';
|
|
329
|
+
import { ref } from 'vue';
|
|
330
|
+
import { Table as XTable, Button as XButton } from 'x-next';
|
|
331
|
+
import type {
|
|
332
|
+
TableColumnData,
|
|
333
|
+
TableData,
|
|
334
|
+
TableChangeExtra,
|
|
335
|
+
} from 'x-next';
|
|
336
|
+
|
|
337
|
+
const tableRef = ref<InstanceType<typeof XTable>>();
|
|
338
|
+
const log = ref('默认先按团队升序,再按积分降序;点击两个表头分别调整方向。');
|
|
339
|
+
const columns: TableColumnData[] = [
|
|
340
|
+
{ title: '姓名', dataIndex: 'name' },
|
|
341
|
+
{
|
|
342
|
+
title: '团队',
|
|
343
|
+
dataIndex: 'team',
|
|
344
|
+
sortable: {
|
|
345
|
+
sortDirections: ['ascend', 'descend'],
|
|
346
|
+
sortPriority: 2,
|
|
347
|
+
defaultSortOrder: 'ascend',
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
title: '积分',
|
|
352
|
+
dataIndex: 'score',
|
|
353
|
+
sortable: {
|
|
354
|
+
sortDirections: ['ascend', 'descend'],
|
|
355
|
+
sortPriority: 1,
|
|
356
|
+
defaultSortOrder: 'descend',
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
const data: TableData[] = [
|
|
361
|
+
{ key: '1', name: 'Jane', team: 'B', score: 90 },
|
|
362
|
+
{ key: '2', name: 'Alisa', team: 'A', score: 80 },
|
|
363
|
+
{ key: '3', name: 'Kevin', team: 'A', score: 95 },
|
|
364
|
+
{ key: '4', name: 'Ed', team: 'B', score: 85 },
|
|
365
|
+
];
|
|
366
|
+
const handleChange = (_page: TableData[], extra: TableChangeExtra, rows: TableData[]) => {
|
|
367
|
+
log.value = `排序:${extra.sorters?.map((sorter) => `${sorter.field} ${sorter.direction}`).join(' → ') || '无'};顺序:${rows.map((row) => row.name).join('、')}`;
|
|
368
|
+
};
|
|
369
|
+
</script>
|
|
370
|
+
<template>
|
|
371
|
+
<div class="table-followup-demo">
|
|
372
|
+
<div class="table-followup-actions">
|
|
373
|
+
<XButton size="small" @click="tableRef?.resetSorters()">恢复默认排序</XButton>
|
|
374
|
+
<XButton size="small" @click="tableRef?.clearSorters()">清空排序</XButton>
|
|
375
|
+
</div>
|
|
376
|
+
<XTable
|
|
377
|
+
ref="tableRef"
|
|
378
|
+
:columns="columns"
|
|
379
|
+
:data="data"
|
|
380
|
+
:pagination="false"
|
|
381
|
+
@change="handleChange"
|
|
382
|
+
/>
|
|
383
|
+
<span class="demo-inline-state" aria-live="polite">{{ log }}</span>
|
|
384
|
+
</div>
|
|
385
|
+
</template>
|
|
386
|
+
<style scoped>
|
|
387
|
+
.table-followup-demo {
|
|
388
|
+
display: grid;
|
|
389
|
+
gap: 12px;
|
|
390
|
+
width: 100%;
|
|
391
|
+
}
|
|
392
|
+
.table-followup-actions {
|
|
393
|
+
display: flex;
|
|
394
|
+
flex-wrap: wrap;
|
|
395
|
+
gap: 8px;
|
|
396
|
+
}
|
|
397
|
+
</style>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 分组表头、滚动和固定列
|
|
401
|
+
|
|
402
|
+
```vue
|
|
403
|
+
<script setup lang="ts">
|
|
404
|
+
import 'x-next/style.css';
|
|
405
|
+
import { Table as XTable } from 'x-next';
|
|
406
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
407
|
+
|
|
408
|
+
const columns: TableColumnData[] = [
|
|
409
|
+
{
|
|
410
|
+
title: '用户',
|
|
411
|
+
children: [
|
|
412
|
+
{ title: '姓名', dataIndex: 'name', width: 140, fixed: 'left' as const },
|
|
413
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
414
|
+
],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
title: '工作信息',
|
|
418
|
+
children: [
|
|
419
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
420
|
+
{ title: '邮箱', dataIndex: 'email', width: 260 },
|
|
421
|
+
],
|
|
422
|
+
},
|
|
423
|
+
{ title: '状态', dataIndex: 'status', width: 120, fixed: 'right' as const },
|
|
424
|
+
];
|
|
425
|
+
|
|
426
|
+
const data: TableData[] = [
|
|
427
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
428
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
429
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
430
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
431
|
+
];
|
|
432
|
+
</script>
|
|
433
|
+
<template>
|
|
434
|
+
<XTable
|
|
435
|
+
:columns="columns"
|
|
436
|
+
:data="data"
|
|
437
|
+
:scroll="{ x: 900, y: 220 }"
|
|
438
|
+
:pagination="false"
|
|
439
|
+
/>
|
|
440
|
+
</template>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### 固定操作列与状态控制
|
|
444
|
+
|
|
445
|
+
同时展示手柄、展开、选择和左右固定列。调整列宽会保留当前排序与筛选;只清空某一列时,其他列筛选继续生效。选中行在条纹和悬浮下仍保持选中反馈。
|
|
446
|
+
|
|
447
|
+
```vue
|
|
448
|
+
<script setup lang="ts">
|
|
449
|
+
import 'x-next/style.css';
|
|
450
|
+
const expandable = {
|
|
451
|
+
title: '展开',
|
|
452
|
+
width: 72,
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
import { ref } from 'vue';
|
|
456
|
+
import { Table as XTable, Button as XButton } from 'x-next';
|
|
457
|
+
import type { TableChangeExtra, TableColumnData, TableData } from 'x-next';
|
|
458
|
+
|
|
459
|
+
const tableRef = ref<InstanceType<typeof XTable>>();
|
|
460
|
+
const selectedKeys = ref<(string | number)[]>(['1']);
|
|
461
|
+
const log = ref('可排序、筛选、调整列宽,并横向滚动检查固定列。');
|
|
462
|
+
const data = ref<TableData[]>([
|
|
463
|
+
{ key: '1', name: 'Jane', city: 'London', salary: 23000, desc: '基础组件维护' },
|
|
464
|
+
{ key: '2', name: 'Alisa', city: 'Paris', salary: 25000, desc: '业务组件维护' },
|
|
465
|
+
{ key: '3', name: 'Kevin', city: 'London', salary: 22000, desc: '文档维护', disabled: true },
|
|
466
|
+
{ key: '4', name: 'Ed', city: 'Tokyo', salary: 17000, desc: '测试维护' },
|
|
467
|
+
]);
|
|
468
|
+
const columns: TableColumnData[] = [
|
|
469
|
+
{
|
|
470
|
+
title: '人员', fixed: 'left',
|
|
471
|
+
children: [{
|
|
472
|
+
title: '基本信息',
|
|
473
|
+
children: [{ title: '姓名', dataIndex: 'name', width: 140, minWidth: 100 }],
|
|
474
|
+
}],
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
title: '城市', dataIndex: 'city', width: 180,
|
|
478
|
+
filterable: {
|
|
479
|
+
filters: [{ text: 'London', value: 'London' }, { text: 'Paris', value: 'Paris' }],
|
|
480
|
+
defaultFilteredValue: ['London'],
|
|
481
|
+
filter: (values, record) => values.includes(record.city),
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
title: '薪资', dataIndex: 'salary', width: 180,
|
|
486
|
+
sortable: { sortDirections: ['ascend', 'descend'] },
|
|
487
|
+
filterable: {
|
|
488
|
+
filters: [{ text: '22000 以上', value: '22000' }],
|
|
489
|
+
filter: (values, record) => values.some(value => record.salary >= Number(value)),
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
{ title: '说明', dataIndex: 'desc', width: 420, ellipsis: true, tooltip: true },
|
|
493
|
+
{ title: '操作', width: 100, fixed: 'right', render: ({ record }) => record.disabled ? '只读' : '可维护' },
|
|
494
|
+
];
|
|
495
|
+
const handleChange = (_pageRows: TableData[], extra: TableChangeExtra, currentData: TableData[]) => {
|
|
496
|
+
if (extra.type === 'drag') data.value = currentData;
|
|
497
|
+
log.value = `${extra.type}:${currentData.map(row => row.name).join(' / ') || '空'}`;
|
|
498
|
+
};
|
|
499
|
+
</script>
|
|
500
|
+
<template>
|
|
501
|
+
<div class="table-combination-demo">
|
|
502
|
+
<div class="table-combination-actions">
|
|
503
|
+
<XButton size="small" @click="tableRef?.clearFilters('salary')">仅清空薪资筛选</XButton>
|
|
504
|
+
<XButton size="small" @click="tableRef?.clearFilters()">清空全部筛选</XButton>
|
|
505
|
+
<XButton size="small" @click="tableRef?.resetFilters()">恢复默认筛选</XButton>
|
|
506
|
+
<XButton size="small" @click="tableRef?.clearSorters()">清空排序</XButton>
|
|
507
|
+
</div>
|
|
508
|
+
<XTable
|
|
509
|
+
ref="tableRef"
|
|
510
|
+
v-model:selected-keys="selectedKeys"
|
|
511
|
+
:columns="columns"
|
|
512
|
+
:data="data"
|
|
513
|
+
:scroll="{ x: 1140, y: 180 }"
|
|
514
|
+
:row-selection="{ showCheckedAll: true }"
|
|
515
|
+
:expandable="{ expandedRowRender: record => `${record.name}:${record.desc}` }"
|
|
516
|
+
:draggable="{ type: 'handle' }"
|
|
517
|
+
:bordered="{ wrapper: true, cell: true }"
|
|
518
|
+
:pagination="false"
|
|
519
|
+
stripe
|
|
520
|
+
column-resizable
|
|
521
|
+
@change="handleChange"
|
|
522
|
+
/>
|
|
523
|
+
<p aria-live="polite">{{ log }};选中:{{ selectedKeys.join('、') || '无' }}</p>
|
|
524
|
+
</div>
|
|
525
|
+
</template>
|
|
526
|
+
<style scoped>
|
|
527
|
+
.table-combination-demo { width: 100%; min-width: 0; }
|
|
528
|
+
.table-combination-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
|
|
529
|
+
.table-combination-demo p { margin: 12px 0 0; font-size: 13px; }
|
|
530
|
+
</style>
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### 单元格合并
|
|
534
|
+
|
|
535
|
+
```vue
|
|
536
|
+
<script setup lang="ts">
|
|
537
|
+
import 'x-next/style.css';
|
|
538
|
+
import { Table as XTable } from 'x-next';
|
|
539
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
540
|
+
|
|
541
|
+
const spanMethod = ({ rowIndex, columnIndex }: { rowIndex: number; columnIndex: number }) => {
|
|
542
|
+
if (rowIndex === 1 && columnIndex === 1) {
|
|
543
|
+
return { rowspan: 2, colspan: 2 };
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const columns: TableColumnData[] = [
|
|
548
|
+
{ title: '姓名', dataIndex: 'name' },
|
|
549
|
+
{ title: '薪资', dataIndex: 'salary' },
|
|
550
|
+
{ title: '城市', dataIndex: 'city' },
|
|
551
|
+
{ title: '邮箱', dataIndex: 'email' },
|
|
552
|
+
];
|
|
553
|
+
|
|
554
|
+
const data: TableData[] = [
|
|
555
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
556
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
557
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
558
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
559
|
+
];
|
|
560
|
+
</script>
|
|
561
|
+
<template>
|
|
562
|
+
<XTable
|
|
563
|
+
:columns="columns"
|
|
564
|
+
:data="data"
|
|
565
|
+
:span-method="spanMethod"
|
|
566
|
+
:bordered="{ wrapper: true, cell: true }"
|
|
567
|
+
/>
|
|
568
|
+
</template>
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### 总结行
|
|
572
|
+
|
|
573
|
+
```vue
|
|
574
|
+
<script setup lang="ts">
|
|
575
|
+
import 'x-next/style.css';
|
|
576
|
+
import { Table as XTable } from 'x-next';
|
|
577
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
578
|
+
|
|
579
|
+
const summary = ({ data }: { data: TableData[] }) => {
|
|
580
|
+
const total = data.reduce((acc, item) => {
|
|
581
|
+
acc.salary += item.salary;
|
|
582
|
+
return acc;
|
|
583
|
+
}, { name: '合计', salary: 0 });
|
|
584
|
+
return [total];
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const columns: TableColumnData[] = [
|
|
588
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
589
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
590
|
+
{ title: '项目数', dataIndex: 'projects', width: 120 },
|
|
591
|
+
{ title: '缺陷数', dataIndex: 'bugs', width: 120 },
|
|
592
|
+
];
|
|
593
|
+
|
|
594
|
+
const data: TableData[] = [
|
|
595
|
+
{ key: '1', name: 'Jane Doe', salary: 23000, projects: 4, bugs: 2 },
|
|
596
|
+
{ key: '2', name: 'Alisa Ross', salary: 25000, projects: 5, bugs: 1 },
|
|
597
|
+
{ key: '3', name: 'Kevin Sandra', salary: 22000, projects: 3, bugs: 4 },
|
|
598
|
+
{ key: '4', name: 'Ed Hellen', salary: 17000, projects: 2, bugs: 3 },
|
|
599
|
+
];
|
|
600
|
+
</script>
|
|
601
|
+
<template>
|
|
602
|
+
<XTable
|
|
603
|
+
:columns="columns"
|
|
604
|
+
:data="data"
|
|
605
|
+
:summary="summary"
|
|
606
|
+
summary-text="合计"
|
|
607
|
+
/>
|
|
608
|
+
</template>
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### 省略与文本提示
|
|
612
|
+
|
|
613
|
+
列配置 `ellipsis: true` 时超出宽度会截断。再开启 `tooltip` 后,悬浮会显示完整内容;`tooltip` 也可传入对象以配置 Tooltip 的属性。
|
|
614
|
+
|
|
615
|
+
```vue
|
|
616
|
+
<script setup lang="ts">
|
|
617
|
+
import 'x-next/style.css';
|
|
618
|
+
import { Table as XTable } from 'x-next';
|
|
619
|
+
import type { TableColumnData } from 'x-next';
|
|
620
|
+
|
|
621
|
+
const columns: TableColumnData[] = [
|
|
622
|
+
{ title: '名称', dataIndex: 'name', width: 160 },
|
|
623
|
+
// 截断并在悬浮时显示完整内容
|
|
624
|
+
{ title: '描述', dataIndex: 'desc', ellipsis: true, tooltip: true },
|
|
625
|
+
// 只截断,不显示提示
|
|
626
|
+
{ title: '备注', dataIndex: 'remark', width: 120, ellipsis: true },
|
|
627
|
+
];
|
|
628
|
+
const data = [{ key: '1', name: 'Button', desc: '很长的描述文本……', remark: '备注' }];
|
|
629
|
+
</script>
|
|
630
|
+
<template>
|
|
631
|
+
<XTable :columns="columns" :data="data" :pagination="false" />
|
|
632
|
+
</template>
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### 吸顶表头
|
|
636
|
+
|
|
637
|
+
`stickyHeader` 让表头在页面滚动时吸顶;传入数字可指定距离顶部的偏移量。需要纵向滚动时同时设置 `scroll.y`。
|
|
638
|
+
|
|
639
|
+
```vue
|
|
640
|
+
<script setup lang="ts">
|
|
641
|
+
import 'x-next/style.css';
|
|
642
|
+
import { Table as XTable } from 'x-next';
|
|
643
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
644
|
+
|
|
645
|
+
const columns: TableColumnData[] = [
|
|
646
|
+
{ title: '名称', dataIndex: 'name', width: 200 },
|
|
647
|
+
{ title: '负责人', dataIndex: 'owner', width: 160 },
|
|
648
|
+
{ title: '状态', dataIndex: 'status', width: 140 },
|
|
649
|
+
];
|
|
650
|
+
|
|
651
|
+
const data: TableData[] = Array.from({ length: 12 }, (_, index) => ({
|
|
652
|
+
key: String(index + 1),
|
|
653
|
+
name: `记录 ${index + 1}`,
|
|
654
|
+
owner: ['Jane', 'Alisa', 'Kevin', 'Ed'][index % 4],
|
|
655
|
+
status: index % 2 === 0 ? '已完成' : '处理中',
|
|
656
|
+
}));
|
|
657
|
+
</script>
|
|
658
|
+
<template>
|
|
659
|
+
<XTable
|
|
660
|
+
:columns="columns"
|
|
661
|
+
:data="data"
|
|
662
|
+
:sticky-header="48"
|
|
663
|
+
:scroll="{ y: 220 }"
|
|
664
|
+
:pagination="false"
|
|
665
|
+
/>
|
|
666
|
+
</template>
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### 懒加载子树
|
|
670
|
+
|
|
671
|
+
传入 `loadMore` 后展开箭头会在需要时异步拉取子节点。函数的第二个参数 `done` 用于回传子数据。
|
|
672
|
+
|
|
673
|
+
```vue
|
|
674
|
+
<script setup lang="ts">
|
|
675
|
+
import 'x-next/style.css';
|
|
676
|
+
import { ref } from 'vue';
|
|
677
|
+
import { Table as XTable } from 'x-next';
|
|
678
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
679
|
+
|
|
680
|
+
const data = ref([{ key: 'dept-1', name: '研发中心', type: '部门' }]);
|
|
681
|
+
|
|
682
|
+
// done 回传子节点;子节点标记 isLeaf 后不再显示展开箭头
|
|
683
|
+
const loadMore = (record: TableData, done: (children?: TableData[]) => void) => {
|
|
684
|
+
setTimeout(() => {
|
|
685
|
+
done([
|
|
686
|
+
{ key: `${record.key}-1`, name: `${record.name} · 小组 1`, type: '小组', isLeaf: true },
|
|
687
|
+
]);
|
|
688
|
+
}, 400);
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
const columns: TableColumnData[] = [
|
|
692
|
+
{ title: '名称', dataIndex: 'name', width: 220 },
|
|
693
|
+
{ title: '类型', dataIndex: 'type', width: 140 },
|
|
694
|
+
];
|
|
695
|
+
</script>
|
|
696
|
+
<template>
|
|
697
|
+
<XTable :columns="columns" :data="data" :load-more="loadMore" :pagination="false" />
|
|
698
|
+
</template>
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
### 调整列宽
|
|
702
|
+
|
|
703
|
+
拖动列边缘调整宽度,或用 `Tab` 聚焦列宽分隔符,再按左右方向键每次调整 10px。宽度不会小于列的 `minWidth`(至少 40px);最后一列保留自动分配宽度。
|
|
704
|
+
|
|
705
|
+
```vue
|
|
706
|
+
<script setup lang="ts">
|
|
707
|
+
import 'x-next/style.css';
|
|
708
|
+
import { Table as XTable } from 'x-next';
|
|
709
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
710
|
+
|
|
711
|
+
const columns: TableColumnData[] = [
|
|
712
|
+
{ title: '姓名', dataIndex: 'name', width: 140, minWidth: 100 },
|
|
713
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
714
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
715
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
716
|
+
];
|
|
717
|
+
|
|
718
|
+
const data: TableData[] = [
|
|
719
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
720
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
721
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
722
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
723
|
+
];
|
|
724
|
+
</script>
|
|
725
|
+
<template>
|
|
726
|
+
<XTable
|
|
727
|
+
:columns="columns"
|
|
728
|
+
:data="data"
|
|
729
|
+
column-resizable
|
|
730
|
+
:bordered="{ wrapper: true, cell: true }"
|
|
731
|
+
/>
|
|
732
|
+
</template>
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### 拖拽排序
|
|
736
|
+
|
|
737
|
+
默认手柄是可聚焦按钮,支持鼠标和触屏拖动;获得焦点后用 Alt + ↑ / ↓ 在当前页内移动同级行,`change` 回调与鼠标拖拽一致。
|
|
738
|
+
|
|
739
|
+
```vue
|
|
740
|
+
<script setup lang="ts">
|
|
741
|
+
import 'x-next/style.css';
|
|
742
|
+
import { ref } from 'vue';
|
|
743
|
+
import { Table as XTable } from 'x-next';
|
|
744
|
+
import type { TableColumnData, TableData, TableChangeExtra } from 'x-next';
|
|
745
|
+
|
|
746
|
+
const data = ref<TableData[]>([
|
|
747
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
748
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
749
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
750
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
751
|
+
]);
|
|
752
|
+
const handleChange = (_pageRows: TableData[], extra: TableChangeExtra, currentData: TableData[]) => {
|
|
753
|
+
if (extra.type === 'drag') data.value = currentData;
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
const columns: TableColumnData[] = [
|
|
757
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
758
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
759
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
760
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
761
|
+
];
|
|
762
|
+
</script>
|
|
763
|
+
<template>
|
|
764
|
+
<XTable
|
|
765
|
+
:columns="columns"
|
|
766
|
+
:data="data"
|
|
767
|
+
:draggable="{ type: 'handle', width: 40 }"
|
|
768
|
+
:pagination="false" @change="handleChange"
|
|
769
|
+
/>
|
|
770
|
+
</template>
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### 键盘导航与行移动
|
|
774
|
+
|
|
775
|
+
开启 `keyboardNavigation` 后,Tab 可进入表体,方向键移动到相邻可见单元格,Home / End 移至本行首尾,Ctrl + Home / End 移至全部可见数据首尾。树形展开行参与导航,虚拟窗口会挂载目标行;合并格导航跳过被跨行覆盖的单元格。
|
|
776
|
+
|
|
777
|
+
单元格内的输入控件保留自己的方向键行为,Tab 可进入或离开控件。表格保留原生 table 语义。拖拽手柄或整行获得焦点后,Alt + ↑ / ↓ 移动当前页内的同级行;与鼠标拖拽一样通过 `change` 的第三个参数写回完整数据。触屏可按住手柄上下拖动,取消手势不提交。
|
|
778
|
+
|
|
779
|
+
```vue
|
|
780
|
+
<script setup lang="ts">
|
|
781
|
+
import 'x-next/style.css';
|
|
782
|
+
import { h, ref } from 'vue';
|
|
783
|
+
import { Table as XTable, Input as XInput } from 'x-next';
|
|
784
|
+
import type {
|
|
785
|
+
TableColumnData,
|
|
786
|
+
TableData,
|
|
787
|
+
TableChangeExtra,
|
|
788
|
+
} from 'x-next';
|
|
789
|
+
|
|
790
|
+
const log = ref('Tab 进入单元格后用方向键导航;手柄获得焦点后用 Alt + ↑ / ↓ 移动行。');
|
|
791
|
+
const data = ref<TableData[]>(
|
|
792
|
+
Array.from({ length: 80 }, (_, index) => ({
|
|
793
|
+
key: index + 1,
|
|
794
|
+
name: `任务 ${index + 1}`,
|
|
795
|
+
note: `备注 ${index + 1}`,
|
|
796
|
+
})),
|
|
797
|
+
);
|
|
798
|
+
const columns: TableColumnData[] = [
|
|
799
|
+
{ title: '编号', dataIndex: 'key', width: 80 },
|
|
800
|
+
{ title: '任务', dataIndex: 'name', width: 180 },
|
|
801
|
+
{
|
|
802
|
+
title: '可编辑备注',
|
|
803
|
+
dataIndex: 'note',
|
|
804
|
+
width: 220,
|
|
805
|
+
render: ({ record }) =>
|
|
806
|
+
h(XInput, {
|
|
807
|
+
modelValue: record.note as string,
|
|
808
|
+
size: 'small',
|
|
809
|
+
'aria-label': `任务 ${record.key} 备注`,
|
|
810
|
+
'onUpdate:modelValue': (value: string) => {
|
|
811
|
+
record.note = value;
|
|
812
|
+
},
|
|
813
|
+
}),
|
|
814
|
+
},
|
|
815
|
+
];
|
|
816
|
+
const handleChange = (_page: TableData[], extra: TableChangeExtra, rows: TableData[]) => {
|
|
817
|
+
if (extra.type === 'drag') {
|
|
818
|
+
data.value = rows;
|
|
819
|
+
log.value = `已移动任务 ${extra.dragTarget?.key};前五行:${rows
|
|
820
|
+
.slice(0, 5)
|
|
821
|
+
.map((row) => row.key)
|
|
822
|
+
.join('、')}`;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
</script>
|
|
826
|
+
<template>
|
|
827
|
+
<div class="table-keyboard-demo">
|
|
828
|
+
<XTable
|
|
829
|
+
keyboard-navigation
|
|
830
|
+
:columns="columns"
|
|
831
|
+
:data="data"
|
|
832
|
+
:draggable="{ type: 'handle', width: 48 }"
|
|
833
|
+
:virtual-list-props="{ height: 240, buffer: 5, estimatedSize: 40 }"
|
|
834
|
+
:pagination="false"
|
|
835
|
+
@change="handleChange"
|
|
836
|
+
/>
|
|
837
|
+
<span class="demo-inline-state" aria-live="polite">{{ log }}</span>
|
|
838
|
+
</div>
|
|
839
|
+
</template>
|
|
840
|
+
<style scoped>
|
|
841
|
+
.table-keyboard-demo {
|
|
842
|
+
display: grid;
|
|
843
|
+
gap: 12px;
|
|
844
|
+
width: 100%;
|
|
845
|
+
}
|
|
846
|
+
</style>
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### 行拖拽
|
|
850
|
+
|
|
851
|
+
`draggable.type` 为 `'row'` 时整行可用鼠标拖动,也可聚焦整行后用 Alt + ↑ / ↓ 移动;为 `'handle'` 时通过手柄拖动,支持触屏(见上一节)。
|
|
852
|
+
|
|
853
|
+
```vue
|
|
854
|
+
<script setup lang="ts">
|
|
855
|
+
import 'x-next/style.css';
|
|
856
|
+
import { ref } from 'vue';
|
|
857
|
+
import { Table as XTable } from 'x-next';
|
|
858
|
+
import type { TableColumnData, TableData, TableChangeExtra } from 'x-next';
|
|
859
|
+
|
|
860
|
+
const data = ref<TableData[]>([
|
|
861
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
862
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
863
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
864
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
865
|
+
]);
|
|
866
|
+
|
|
867
|
+
const onChange = (_pageRows: TableData[], extra: TableChangeExtra, currentData: TableData[]) => {
|
|
868
|
+
if (extra.type === 'drag') data.value = currentData;
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
const columns: TableColumnData[] = [
|
|
872
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
873
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
874
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
875
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
876
|
+
];
|
|
877
|
+
</script>
|
|
878
|
+
<template>
|
|
879
|
+
<XTable :columns="columns" :data="data" :draggable="{ type: 'row' }" :pagination="false" @change="onChange" />
|
|
880
|
+
</template>
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### 可编辑单元格
|
|
884
|
+
|
|
885
|
+
Table 没有内置的编辑态,用列的 `cell` 插槽即可实现:双击切换到输入控件,失焦或回车后写回数据。
|
|
886
|
+
|
|
887
|
+
```vue
|
|
888
|
+
<script setup lang="ts">
|
|
889
|
+
import 'x-next/style.css';
|
|
890
|
+
import { ref } from 'vue';
|
|
891
|
+
import { Table as XTable, Select as XSelect } from 'x-next';
|
|
892
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
893
|
+
|
|
894
|
+
const columns: TableColumnData[] = [
|
|
895
|
+
{ title: '名称', dataIndex: 'name' },
|
|
896
|
+
// slotName 与 template 名一致
|
|
897
|
+
{ title: '状态', dataIndex: 'status', slotName: 'statusCell' },
|
|
898
|
+
];
|
|
899
|
+
const data = ref([{ key: '1', name: 'Button', status: '待处理', editing: false }]);
|
|
900
|
+
|
|
901
|
+
const startEdit = (record: TableData) => {
|
|
902
|
+
data.value = data.value.map((item) => ({ ...item, editing: item.key === record.key }));
|
|
903
|
+
};
|
|
904
|
+
const apply = (record: TableData, value: unknown) => {
|
|
905
|
+
data.value = data.value.map((item) =>
|
|
906
|
+
item.key === record.key ? { ...item, status: String(value ?? ''), editing: false } : item,
|
|
907
|
+
);
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
const options = ['待处理', '处理中', '已完成'];
|
|
911
|
+
</script>
|
|
912
|
+
<template>
|
|
913
|
+
<XTable :columns="columns" :data="data" :pagination="false">
|
|
914
|
+
<template #statusCell="{ record }">
|
|
915
|
+
<!-- 非编辑态:双击进入编辑 -->
|
|
916
|
+
<span v-if="!record.editing" @dblclick="startEdit(record)">{{ record.status }}</span>
|
|
917
|
+
<!-- 编辑态:写入后退出 -->
|
|
918
|
+
<XSelect
|
|
919
|
+
v-else
|
|
920
|
+
:model-value="record.status"
|
|
921
|
+
:options="options"
|
|
922
|
+
@change="(value) => apply(record, value)"
|
|
923
|
+
/>
|
|
924
|
+
</template>
|
|
925
|
+
</XTable>
|
|
926
|
+
</template>
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
### 虚拟列表
|
|
930
|
+
|
|
931
|
+
`estimatedSize` 用于估算未挂载行的高度;普通内容建议使用动态高度。`fixedSize: true` 只适合实际高度完全一致的行,估值必须与实际行高一致。包含树或展开详情时会使用动态高度;传入 `spanMethod` 时会回退普通滚动,以完整呈现跨行合并。
|
|
932
|
+
|
|
933
|
+
Table 管理虚拟行的数据、身份和原生表格结构,`virtualListProps` 中的 `data`、`itemKey` 和 `component` 不会覆盖它们。通过 Table 的 `data` 和 `rowKey` 指定业务数据与键字段,记录键支持 `string` 和 `number`。
|
|
934
|
+
|
|
935
|
+
```vue
|
|
936
|
+
<script setup lang="ts">
|
|
937
|
+
import 'x-next/style.css';
|
|
938
|
+
import { Table as XTable } from 'x-next';
|
|
939
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
940
|
+
|
|
941
|
+
const columns: TableColumnData[] = [
|
|
942
|
+
{ title: '编号', dataIndex: 'key', width: 100 },
|
|
943
|
+
{ title: '任务', dataIndex: 'name', width: 220 },
|
|
944
|
+
{ title: '状态', dataIndex: 'status', width: 140 },
|
|
945
|
+
{ title: '负责人', dataIndex: 'owner', width: 160 },
|
|
946
|
+
];
|
|
947
|
+
|
|
948
|
+
const data: TableData[] = Array.from({ length: 120 }, (_, index) => ({
|
|
949
|
+
key: String(index + 1),
|
|
950
|
+
name: `任务 ${index + 1}`,
|
|
951
|
+
status: index % 3 === 0 ? '待处理' : index % 3 === 1 ? '处理中' : '已完成',
|
|
952
|
+
owner: ['Jane', 'Alisa', 'Kevin', 'Ed'][index % 4],
|
|
953
|
+
}));
|
|
954
|
+
</script>
|
|
955
|
+
<template>
|
|
956
|
+
<XTable
|
|
957
|
+
:columns="columns"
|
|
958
|
+
:data="data"
|
|
959
|
+
:virtual-list-props="{ height: 260, fixedSize: false, estimatedSize: 39 }"
|
|
960
|
+
:pagination="false"
|
|
961
|
+
/>
|
|
962
|
+
</template>
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
### 虚拟行异步高度
|
|
966
|
+
|
|
967
|
+
动态模式持续测量已挂载行;文本、图片和展开详情异步变高后,更新虚拟占位和定位偏移。下面的按钮模拟异步加载,先展开第一行,再观察详情从一段变成八段。未挂载内容仍通过 `estimatedSize` 估算,异步媒体预留尺寸可减少滚动位置变化。
|
|
968
|
+
|
|
969
|
+
```vue
|
|
970
|
+
<script setup lang="ts">
|
|
971
|
+
import 'x-next/style.css';
|
|
972
|
+
const expandable = {
|
|
973
|
+
title: '展开',
|
|
974
|
+
width: 72,
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
import { onBeforeUnmount, ref } from 'vue';
|
|
978
|
+
import { Table as XTable, Button as XButton } from 'x-next';
|
|
979
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
980
|
+
|
|
981
|
+
const expandedKeys = ref<(string | number)[]>([1]);
|
|
982
|
+
const details = ref(['当前为短内容。点击按钮模拟异步请求。']);
|
|
983
|
+
const loading = ref(false);
|
|
984
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
985
|
+
const reset = () => {
|
|
986
|
+
clearTimeout(timer);
|
|
987
|
+
loading.value = false;
|
|
988
|
+
details.value = ['当前为短内容。点击按钮模拟异步请求。'];
|
|
989
|
+
};
|
|
990
|
+
const loadDetails = () => {
|
|
991
|
+
if (loading.value) return;
|
|
992
|
+
loading.value = true;
|
|
993
|
+
expandedKeys.value = [1];
|
|
994
|
+
timer = setTimeout(() => {
|
|
995
|
+
details.value = Array.from(
|
|
996
|
+
{ length: 8 },
|
|
997
|
+
(_, index) => `异步详情 ${index + 1}:动态内容加载完成后,虚拟列表持续测量实际行高。`,
|
|
998
|
+
);
|
|
999
|
+
loading.value = false;
|
|
1000
|
+
}, 600);
|
|
1001
|
+
};
|
|
1002
|
+
onBeforeUnmount(() => clearTimeout(timer));
|
|
1003
|
+
const columns: TableColumnData[] = [
|
|
1004
|
+
{ title: '编号', dataIndex: 'key', width: 80 },
|
|
1005
|
+
{ title: '任务', dataIndex: 'name', width: 260 },
|
|
1006
|
+
];
|
|
1007
|
+
const data: TableData[] = Array.from({ length: 80 }, (_, index) => ({
|
|
1008
|
+
key: index + 1,
|
|
1009
|
+
name: `异步任务 ${index + 1}`,
|
|
1010
|
+
}));
|
|
1011
|
+
</script>
|
|
1012
|
+
<template>
|
|
1013
|
+
<div class="table-height-demo">
|
|
1014
|
+
<div class="table-height-actions">
|
|
1015
|
+
<XButton size="small" :loading="loading" @click="loadDetails">异步加载详情</XButton>
|
|
1016
|
+
<XButton size="small" @click="reset">恢复短内容</XButton>
|
|
1017
|
+
</div>
|
|
1018
|
+
<XTable
|
|
1019
|
+
v-model:expanded-keys="expandedKeys"
|
|
1020
|
+
:columns="columns"
|
|
1021
|
+
:data="data"
|
|
1022
|
+
:expandable="{ width: 48 }"
|
|
1023
|
+
:virtual-list-props="{ height: 260, fixedSize: false, estimatedSize: 40, buffer: 3 }"
|
|
1024
|
+
:pagination="false"
|
|
1025
|
+
>
|
|
1026
|
+
<template #expand-row="{ record }">
|
|
1027
|
+
<div v-if="record.key === 1" class="table-height-details">
|
|
1028
|
+
<p v-for="(line, index) in details" :key="index">{{ line }}</p>
|
|
1029
|
+
</div>
|
|
1030
|
+
<div v-else class="table-height-details">任务 {{ record.key }} 的详情</div>
|
|
1031
|
+
</template>
|
|
1032
|
+
</XTable>
|
|
1033
|
+
<span class="demo-inline-state" aria-live="polite"
|
|
1034
|
+
>第一行详情:{{ details.length }} 段;展开后异步变高,滚动仍按实际高度定位。</span
|
|
1035
|
+
>
|
|
1036
|
+
</div>
|
|
1037
|
+
</template>
|
|
1038
|
+
<style scoped>
|
|
1039
|
+
.table-height-demo {
|
|
1040
|
+
display: grid;
|
|
1041
|
+
gap: 12px;
|
|
1042
|
+
width: 100%;
|
|
1043
|
+
}
|
|
1044
|
+
.table-height-actions {
|
|
1045
|
+
display: flex;
|
|
1046
|
+
flex-wrap: wrap;
|
|
1047
|
+
gap: 8px;
|
|
1048
|
+
}
|
|
1049
|
+
.table-height-details {
|
|
1050
|
+
padding: 12px;
|
|
1051
|
+
}
|
|
1052
|
+
.table-height-details p {
|
|
1053
|
+
margin: 0 0 8px;
|
|
1054
|
+
}
|
|
1055
|
+
.table-height-details p:last-child {
|
|
1056
|
+
margin-bottom: 0;
|
|
1057
|
+
}
|
|
1058
|
+
</style>
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
### 自定义渲染和插槽
|
|
1062
|
+
|
|
1063
|
+
```vue
|
|
1064
|
+
<script setup lang="ts">
|
|
1065
|
+
import 'x-next/style.css';
|
|
1066
|
+
import { Table as XTable } from 'x-next';
|
|
1067
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1068
|
+
|
|
1069
|
+
const columns: TableColumnData[] = [
|
|
1070
|
+
{ title: '名称', dataIndex: 'name', slotName: 'nameCell' },
|
|
1071
|
+
{ title: '状态', dataIndex: 'status' },
|
|
1072
|
+
];
|
|
1073
|
+
|
|
1074
|
+
const data: TableData[] = [
|
|
1075
|
+
{ key: '1', name: 'Button', type: '通用', status: '稳定' },
|
|
1076
|
+
{ key: '2', name: 'Select', type: '表单', status: '维护中' },
|
|
1077
|
+
{ key: '3', name: 'Table', type: '数据展示', status: '改造中' },
|
|
1078
|
+
];
|
|
1079
|
+
</script>
|
|
1080
|
+
<template>
|
|
1081
|
+
<XTable :columns="columns" :data="data">
|
|
1082
|
+
<template #nameCell="{ record }">
|
|
1083
|
+
<strong>{{ record.name }}</strong>
|
|
1084
|
+
</template>
|
|
1085
|
+
</XTable>
|
|
1086
|
+
</template>
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
### 空态和加载
|
|
1090
|
+
|
|
1091
|
+
`loading` 为 `true` 时使用标准主题色圆环,默认主题下为蓝色。需要提示文字、延迟或圆点动画时,可传入 Spin 配置,例如 `{ loading: true, tip: '加载中', delay: 200 }` 或 `{ loading: true, dot: 'color' }`。
|
|
1092
|
+
|
|
1093
|
+
```vue
|
|
1094
|
+
<script setup lang="ts">
|
|
1095
|
+
import 'x-next/style.css';
|
|
1096
|
+
import { ref } from 'vue';
|
|
1097
|
+
import { Button as XButton, Table as XTable } from 'x-next';
|
|
1098
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1099
|
+
|
|
1100
|
+
const loading = ref(true);
|
|
1101
|
+
|
|
1102
|
+
const columns: TableColumnData[] = [
|
|
1103
|
+
{ title: '名称', dataIndex: 'name' },
|
|
1104
|
+
{ title: '类型', dataIndex: 'type' },
|
|
1105
|
+
{ title: '状态', dataIndex: 'status' },
|
|
1106
|
+
];
|
|
1107
|
+
|
|
1108
|
+
const data: TableData[] = [
|
|
1109
|
+
{ key: '1', name: 'Button', type: '通用', status: '稳定' },
|
|
1110
|
+
{ key: '2', name: 'Select', type: '表单', status: '维护中' },
|
|
1111
|
+
{ key: '3', name: 'Table', type: '数据展示', status: '改造中' },
|
|
1112
|
+
];
|
|
1113
|
+
</script>
|
|
1114
|
+
<template>
|
|
1115
|
+
<XTable :columns="columns" :data="[]" :pagination="false">
|
|
1116
|
+
<template #empty>暂无匹配数据</template>
|
|
1117
|
+
</XTable>
|
|
1118
|
+
<XButton size="small" @click="loading = !loading">
|
|
1119
|
+
{{ loading ? '结束加载' : '开始加载' }}
|
|
1120
|
+
</XButton>
|
|
1121
|
+
<XTable :columns="columns" :data="data" :loading="loading" :pagination="false" />
|
|
1122
|
+
</template>
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
### 命令式方法
|
|
1126
|
+
|
|
1127
|
+
通过 ref 调用 `select` / `expand` / `resetFilters` / `clearSorters` 等方法操作表格状态。
|
|
1128
|
+
|
|
1129
|
+
```vue
|
|
1130
|
+
<script setup lang="ts">
|
|
1131
|
+
import 'x-next/style.css';
|
|
1132
|
+
import { ref } from 'vue';
|
|
1133
|
+
import { Table as XTable, Button as XButton } from 'x-next';
|
|
1134
|
+
import type { TableColumnData } from 'x-next';
|
|
1135
|
+
|
|
1136
|
+
const tableRef = ref<InstanceType<typeof XTable>>();
|
|
1137
|
+
const data = [{ key: '1', name: 'Button' }, { key: '2', name: 'Table' }];
|
|
1138
|
+
|
|
1139
|
+
const columns: TableColumnData[] = [{ title: '名称', dataIndex: 'name' }];
|
|
1140
|
+
</script>
|
|
1141
|
+
<template>
|
|
1142
|
+
<XButton @click="tableRef?.select('2', true)">选中第 2 行</XButton>
|
|
1143
|
+
<XButton @click="tableRef?.selectAll(true)">全选</XButton>
|
|
1144
|
+
<XTable ref="tableRef" :columns="columns" :data="data" :row-selection="{ type: 'checkbox' }" />
|
|
1145
|
+
</template>
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
### 自定义筛选弹层
|
|
1149
|
+
|
|
1150
|
+
`filterable.slotName` 指定弹层内容所用的具名插槽,插槽参数含 `filterValue`、`setFilterValue`、`handleFilterConfirm`、`handleFilterReset`。
|
|
1151
|
+
|
|
1152
|
+
```vue
|
|
1153
|
+
<script setup lang="ts">
|
|
1154
|
+
import 'x-next/style.css';
|
|
1155
|
+
import { ref } from 'vue';
|
|
1156
|
+
import { Table as XTable, Button as XButton, Checkbox as XCheckbox, CheckboxGroup as XCheckboxGroup } from 'x-next';
|
|
1157
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1158
|
+
|
|
1159
|
+
const columns: TableColumnData[] = [
|
|
1160
|
+
{ title: '名称', dataIndex: 'name' },
|
|
1161
|
+
{
|
|
1162
|
+
title: '状态',
|
|
1163
|
+
dataIndex: 'status',
|
|
1164
|
+
filterable: {
|
|
1165
|
+
filters: [{ text: '待处理', value: '待处理' }],
|
|
1166
|
+
filter: (values, record) => values.length === 0 || values.includes(record.status),
|
|
1167
|
+
multiple: true,
|
|
1168
|
+
// 与 template 名一致
|
|
1169
|
+
slotName: 'statusFilter',
|
|
1170
|
+
},
|
|
1171
|
+
},
|
|
1172
|
+
];
|
|
1173
|
+
|
|
1174
|
+
const data: TableData[] = [
|
|
1175
|
+
{ key: '1', name: 'Button', status: '待处理', owner: 'Jane' },
|
|
1176
|
+
{ key: '2', name: 'Table', status: '处理中', owner: 'Alisa' },
|
|
1177
|
+
{ key: '3', name: 'Select', status: '已完成', owner: 'Kevin' },
|
|
1178
|
+
{ key: '4', name: 'Input', status: '待处理', owner: 'Ed' },
|
|
1179
|
+
];
|
|
1180
|
+
|
|
1181
|
+
const log = ref('未筛选');
|
|
1182
|
+
const onFilterChange = (field: string, values: string[]) => {
|
|
1183
|
+
log.value = `${field}: ${values.join('、') || '清空'}`;
|
|
1184
|
+
};
|
|
1185
|
+
</script>
|
|
1186
|
+
<template>
|
|
1187
|
+
<XTable :columns="columns" :data="data" @filter-change="onFilterChange">
|
|
1188
|
+
<template #statusFilter="{ filterValue, setFilterValue, handleFilterConfirm, handleFilterReset }">
|
|
1189
|
+
<XCheckboxGroup :model-value="filterValue" @change="setFilterValue">
|
|
1190
|
+
<XCheckbox value="待处理">待处理</XCheckbox>
|
|
1191
|
+
<XCheckbox value="处理中">处理中</XCheckbox>
|
|
1192
|
+
</XCheckboxGroup>
|
|
1193
|
+
<XButton size="mini" @click="handleFilterReset">重置</XButton>
|
|
1194
|
+
<XButton size="mini" type="primary" @click="handleFilterConfirm">确认</XButton>
|
|
1195
|
+
</template>
|
|
1196
|
+
</XTable>
|
|
1197
|
+
|
|
1198
|
+
<p>{{ log }}</p>
|
|
1199
|
+
</template>
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
### 自定义展开图标
|
|
1203
|
+
|
|
1204
|
+
`expand-icon` 插槽可替换默认的展开箭头,参数为 `{ expanded, record }`。
|
|
1205
|
+
|
|
1206
|
+
```vue
|
|
1207
|
+
<script setup lang="ts">
|
|
1208
|
+
import 'x-next/style.css';
|
|
1209
|
+
const expandable = {
|
|
1210
|
+
title: '展开',
|
|
1211
|
+
width: 72,
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
import { Table as XTable } from 'x-next';
|
|
1215
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1216
|
+
|
|
1217
|
+
const columns: TableColumnData[] = [
|
|
1218
|
+
{ title: '名称', dataIndex: 'name', width: 180 },
|
|
1219
|
+
{ title: '说明', dataIndex: 'desc' },
|
|
1220
|
+
];
|
|
1221
|
+
|
|
1222
|
+
const data: TableData[] = [
|
|
1223
|
+
{ key: '1', name: 'Button', desc: '触发即时操作' },
|
|
1224
|
+
{ key: '2', name: 'Table', desc: '数据收集与展示' },
|
|
1225
|
+
];
|
|
1226
|
+
|
|
1227
|
+
const render = (record: TableData) => `详情:${record.desc}`;
|
|
1228
|
+
</script>
|
|
1229
|
+
<template>
|
|
1230
|
+
<XTable :columns="columns" :data="data" :expandable="{ expandedRowRender: render }">
|
|
1231
|
+
<template #expand-icon="{ expanded }">
|
|
1232
|
+
<span>{{ expanded ? '−' : '+' }}</span>
|
|
1233
|
+
</template>
|
|
1234
|
+
</XTable>
|
|
1235
|
+
</template>
|
|
1236
|
+
```
|
|
1237
|
+
|
|
1238
|
+
### 分页两侧与总结行插槽
|
|
1239
|
+
|
|
1240
|
+
`pagination-left` / `pagination-right` 用于在分页器两侧补充内容;`summary-cell` 用于自定义总结行的单元格。
|
|
1241
|
+
|
|
1242
|
+
```vue
|
|
1243
|
+
<script setup lang="ts">
|
|
1244
|
+
import 'x-next/style.css';
|
|
1245
|
+
import { ref } from 'vue';
|
|
1246
|
+
import { Table as XTable, Button as XButton } from 'x-next';
|
|
1247
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1248
|
+
|
|
1249
|
+
const columns: TableColumnData[] = [
|
|
1250
|
+
{ title: '名称', dataIndex: 'name', width: 180 },
|
|
1251
|
+
{ title: '状态', dataIndex: 'status', width: 140 },
|
|
1252
|
+
{ title: '负责人', dataIndex: 'owner' },
|
|
1253
|
+
];
|
|
1254
|
+
|
|
1255
|
+
const data = ref<TableData[]>([
|
|
1256
|
+
{ key: '1', name: 'Button', status: '待处理', owner: 'Jane' },
|
|
1257
|
+
{ key: '2', name: 'Table', status: '处理中', owner: 'Alisa' },
|
|
1258
|
+
{ key: '3', name: 'Select', status: '已完成', owner: 'Kevin' },
|
|
1259
|
+
]);
|
|
1260
|
+
|
|
1261
|
+
const log = ref('尚未刷新');
|
|
1262
|
+
</script>
|
|
1263
|
+
<template>
|
|
1264
|
+
<XTable :columns="columns" :data="data" :pagination="{ pageSize: 2 }" :summary="true">
|
|
1265
|
+
<template #pagination-left>
|
|
1266
|
+
<span>共 {{ data.length }} 条记录</span>
|
|
1267
|
+
</template>
|
|
1268
|
+
<template #pagination-right>
|
|
1269
|
+
<XButton size="mini" @click="log = '已刷新'">刷新</XButton>
|
|
1270
|
+
</template>
|
|
1271
|
+
</XTable>
|
|
1272
|
+
|
|
1273
|
+
<p>{{ log }}</p>
|
|
1274
|
+
</template>
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
```vue
|
|
1278
|
+
<script setup lang="ts">
|
|
1279
|
+
import 'x-next/style.css';
|
|
1280
|
+
import { Table as XTable } from 'x-next';
|
|
1281
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1282
|
+
|
|
1283
|
+
const columns: TableColumnData[] = [
|
|
1284
|
+
{ title: '姓名', dataIndex: 'name' },
|
|
1285
|
+
{ title: '薪资', dataIndex: 'salary' },
|
|
1286
|
+
{ title: '项目数', dataIndex: 'projects' },
|
|
1287
|
+
];
|
|
1288
|
+
|
|
1289
|
+
const data: TableData[] = [
|
|
1290
|
+
{ key: '1', name: 'Jane Doe', salary: 23000, projects: 4 },
|
|
1291
|
+
{ key: '2', name: 'Alisa Ross', salary: 25000, projects: 5 },
|
|
1292
|
+
{ key: '3', name: 'Kevin Sandra', salary: 22000, projects: 3 },
|
|
1293
|
+
];
|
|
1294
|
+
|
|
1295
|
+
const summaryText = (column: TableColumnData) => {
|
|
1296
|
+
if (column.dataIndex === 'name') return '合计';
|
|
1297
|
+
const field = column.dataIndex;
|
|
1298
|
+
return field ? data.reduce((total, row) => total + Number(row[field] || 0), 0) : '';
|
|
1299
|
+
};
|
|
1300
|
+
</script>
|
|
1301
|
+
<template>
|
|
1302
|
+
<XTable :columns="columns" :data="data" :summary="true">
|
|
1303
|
+
<!-- 参数为 { column, record, rowIndex } -->
|
|
1304
|
+
<template #summary-cell="{ column }">
|
|
1305
|
+
<strong>{{ summaryText(column) }}</strong>
|
|
1306
|
+
</template>
|
|
1307
|
+
</XTable>
|
|
1308
|
+
</template>
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
### 事件回调
|
|
1312
|
+
|
|
1313
|
+
```vue
|
|
1314
|
+
<script setup lang="ts">
|
|
1315
|
+
import 'x-next/style.css';
|
|
1316
|
+
import { ref } from 'vue';
|
|
1317
|
+
import { Table as XTable } from 'x-next';
|
|
1318
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1319
|
+
|
|
1320
|
+
const columns: TableColumnData[] = [
|
|
1321
|
+
{ title: '姓名', dataIndex: 'name', width: 140 },
|
|
1322
|
+
{ title: '薪资', dataIndex: 'salary', width: 120 },
|
|
1323
|
+
{ title: '城市', dataIndex: 'city', width: 120 },
|
|
1324
|
+
{ title: '邮箱', dataIndex: 'email', width: 220 },
|
|
1325
|
+
];
|
|
1326
|
+
|
|
1327
|
+
const data: TableData[] = [
|
|
1328
|
+
{ key: '1', name: 'Jane', salary: 23000, city: 'London', email: 'jane@example.com' },
|
|
1329
|
+
{ key: '2', name: 'Alisa', salary: 25000, city: 'Paris', email: 'alisa@example.com' },
|
|
1330
|
+
{ key: '3', name: 'Kevin', salary: 22000, city: 'London', email: 'kevin@example.com', disabled: true },
|
|
1331
|
+
{ key: '4', name: 'Ed', salary: 17000, city: 'Tokyo', email: 'ed@example.com' },
|
|
1332
|
+
];
|
|
1333
|
+
|
|
1334
|
+
const logs = ref<string[]>([]);
|
|
1335
|
+
const handleRowClick = (record: TableData) => logs.value.push(`行:${record.name}`);
|
|
1336
|
+
const handleCellClick = (record: TableData, column: TableColumnData) => logs.value.push(`单元格:${record.name}/${column.dataIndex}`);
|
|
1337
|
+
const handleHeaderClick = (column: TableColumnData) => logs.value.push(`表头:${column.title}`);
|
|
1338
|
+
</script>
|
|
1339
|
+
<template>
|
|
1340
|
+
<XTable
|
|
1341
|
+
:columns="columns"
|
|
1342
|
+
:data="data"
|
|
1343
|
+
@row-click="handleRowClick"
|
|
1344
|
+
@cell-click="handleCellClick"
|
|
1345
|
+
@header-click="handleHeaderClick"
|
|
1346
|
+
/>
|
|
1347
|
+
|
|
1348
|
+
<p v-for="(log, index) in logs" :key="index">{{ log }}</p>
|
|
1349
|
+
</template>
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
### 类型说明
|
|
1353
|
+
|
|
1354
|
+
| 类型 | 说明 |
|
|
1355
|
+
| --- | --- |
|
|
1356
|
+
| `TableData` | 行数据,默认用 `key` 作为唯一标识,可包含 `children`、`expand`、`disabled`、`isLeaf` |
|
|
1357
|
+
| `TableSortable` | 排序配置,包含 `sortDirections`、`sorter`、`sortOrder`、`defaultSortOrder`、`sortPriority` |
|
|
1358
|
+
| `TableFilterable` | 筛选配置,包含 `filters`、`filter`、`multiple`、`filteredValue`、`defaultFilteredValue`、`renderContent`、`icon`、`triggerProps`、`alignLeft`、`slotName` |
|
|
1359
|
+
| `TableRowSelection` | 行选择配置,包含 `type`、`selectedRowKeys`、`defaultSelectedRowKeys`、`showCheckedAll`、`title`、`width`、`fixed`、`checkStrictly`、`onlyCurrent` |
|
|
1360
|
+
| `TableExpandable` | 展开行配置,包含 `expandedRowKeys`、`defaultExpandedRowKeys`、`defaultExpandAllRows`、`expandedRowRender`、`icon`、`title`、`width`、`fixed` |
|
|
1361
|
+
| `TableDraggable` | 拖拽配置,包含 `type`、`title`、`width`、`fixed` |
|
|
1362
|
+
| `TableChangeExtra` | `change` 事件附加信息,包含 `type`、`page`、`pageSize`、`sorter`、`sorters`、`filters`、`dragTarget` |
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
### 元素插槽
|
|
1366
|
+
|
|
1367
|
+
`tbody`、`thead`、`tr`、`td`、`th` 是内部元素插槽,可替换表格的原生元素(例如给 `tbody` 加原生属性、给 `td` 挂自定义指令)。
|
|
1368
|
+
|
|
1369
|
+
**使用契约**:插槽内只提供**单一空元素**,让 Table 把原有的 children、class、style 和事件合并到该元素上。若自行写死内容,会覆盖表格原本的行、列和事件结构。
|
|
1370
|
+
|
|
1371
|
+
也可用于 `tr` / `td` / `th` / `thead`。`tr` / `td` / `th` 会传入行与列信息(见上方 Slots 表);`tbody` 与 `thead` 不传参数——children、attrs 与事件统一由 Table 合并到插槽返回的元素上。
|
|
1372
|
+
|
|
1373
|
+
```vue
|
|
1374
|
+
<script setup lang="ts">
|
|
1375
|
+
import 'x-next/style.css';
|
|
1376
|
+
import { Table as XTable } from 'x-next';
|
|
1377
|
+
import type { TableColumnData, TableData } from 'x-next';
|
|
1378
|
+
|
|
1379
|
+
const columns: TableColumnData[] = [
|
|
1380
|
+
{ title: '名称', dataIndex: 'name' },
|
|
1381
|
+
{ title: '类型', dataIndex: 'type' },
|
|
1382
|
+
{ title: '状态', dataIndex: 'status' },
|
|
1383
|
+
];
|
|
1384
|
+
|
|
1385
|
+
const data: TableData[] = [
|
|
1386
|
+
{ key: '1', name: 'Button', type: '通用', status: '稳定' },
|
|
1387
|
+
{ key: '2', name: 'Select', type: '表单', status: '维护中' },
|
|
1388
|
+
{ key: '3', name: 'Table', type: '数据展示', status: '改造中' },
|
|
1389
|
+
];
|
|
1390
|
+
</script>
|
|
1391
|
+
<template>
|
|
1392
|
+
<XTable :columns="columns" :data="data" :pagination="false">
|
|
1393
|
+
<!-- 只写单一空元素,Table 会把 children / class / 事件合并进来 -->
|
|
1394
|
+
<!-- tbody 插槽不接收参数:Table 会把 children/attrs 合并到返回的元素上 -->
|
|
1395
|
+
<template #tbody>
|
|
1396
|
+
<tbody v-bind="{ class: 'custom-table-body', 'data-custom-body': 'true' }" />
|
|
1397
|
+
</template>
|
|
1398
|
+
</XTable>
|
|
1399
|
+
</template>
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
### 已知限制
|
|
1403
|
+
|
|
1404
|
+
- 拖拽排序和键盘行移动都要求业务在 `change` 中写回第三个参数。键盘移动限定当前页内同级行;触屏拖动通过手柄进行,整行模式保留页面的触屏滚动。
|
|
1405
|
+
- 非受控分页下筛选、排序和每页数量变化会自动回到第一页;受控分页需要业务同步 `pagination.current`。
|
|
1406
|
+
- 单选 `select` 只取数组中的第一个 key,`select(key, false)` 可取消选中;`selectAll` 仅用于多选。
|
|
1407
|
+
- `checkStrictly: false` 时父子联动与全选只选可用叶子,禁用叶子不参与联动。
|
|
1408
|
+
- `change` 的第一个参数为当前页数据,第三个参数为筛选、排序或拖拽后的全量业务数据;维护完整列表时使用第三个参数。
|
|
1409
|
+
- `loadMore` 调用 `done(undefined)` 可结束本次加载并允许重试;`done([])` 表示已加载的空子树。替换原始记录后,同 key 不会复用旧记录的懒加载缓存。
|
|
1410
|
+
- 虚拟动态行持续测量已挂载内容,未挂载行仍使用估值;异步媒体建议预留尺寸以减少跳动。跨行合并使用普通滚动。单元格导航是可选能力,编辑、复制粘贴和校验规则仍由业务实现。
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
返回 [AI 索引](../index.md)。先查本页与 [本地 API](./table-api.md);需要更深类型时查 [公开组件导航](../catalog.md),不默认上网查找本包用法。
|