niuma-ui 1.0.1 → 1.1.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.
Files changed (190) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/package.json +10 -2
  3. package/src/__tests__/RsButton.spec.ts +47 -0
  4. package/src/__tests__/RsCard.spec.ts +160 -1
  5. package/src/__tests__/RsConfirmDialog.spec.ts +127 -0
  6. package/src/__tests__/RsContextMenu.spec.ts +12 -0
  7. package/src/__tests__/RsDatePicker.spec.ts +21 -0
  8. package/src/__tests__/RsDateTimePicker.spec.ts +11 -0
  9. package/src/__tests__/RsDescriptions.spec.ts +50 -0
  10. package/src/__tests__/RsDialog.spec.ts +178 -0
  11. package/src/__tests__/RsDrawer.spec.ts +96 -3
  12. package/src/__tests__/RsDynamicTags.spec.ts +52 -0
  13. package/src/__tests__/RsForm.spec.ts +250 -6
  14. package/src/__tests__/RsFormItem.spec.ts +478 -0
  15. package/src/__tests__/RsFormList.spec.ts +109 -0
  16. package/src/__tests__/RsInput.spec.ts +108 -1
  17. package/src/__tests__/RsInputNumber.spec.ts +33 -0
  18. package/src/__tests__/RsMenu.spec.ts +406 -14
  19. package/src/__tests__/RsPagination.spec.ts +67 -2
  20. package/src/__tests__/RsSelect.spec.ts +465 -2
  21. package/src/__tests__/RsSwitch.spec.ts +74 -0
  22. package/src/__tests__/RsTable.a11y.spec.ts +83 -0
  23. package/src/__tests__/RsTable.dom-baseline.spec.ts +87 -0
  24. package/src/__tests__/RsTable.perf.spec.ts +23 -4
  25. package/src/__tests__/RsTable.spec.ts +63 -8
  26. package/src/__tests__/RsTable.ssr.spec.ts +71 -0
  27. package/src/__tests__/RsTable.summary.spec.ts +45 -0
  28. package/src/__tests__/RsTabs.spec.ts +102 -4
  29. package/src/__tests__/RsTimePicker.spec.ts +20 -0
  30. package/src/__tests__/RsTooltip.spec.ts +43 -0
  31. package/src/__tests__/RsUpload.spec.ts +62 -6
  32. package/src/__tests__/createRsDialog.spec.ts +145 -0
  33. package/src/__tests__/css-token.spec.ts +51 -0
  34. package/src/__tests__/date-picker-utils.spec.ts +49 -0
  35. package/src/__tests__/dialog-utils.spec.ts +43 -0
  36. package/src/__tests__/form-path.spec.ts +46 -0
  37. package/src/__tests__/form-rules.spec.ts +139 -0
  38. package/src/__tests__/perf-budgets.ts +32 -0
  39. package/src/__tests__/rs-table-api.spec.ts +206 -0
  40. package/src/__tests__/rs-table-compat-matrix.spec.ts +108 -0
  41. package/src/__tests__/rs-table-echarts-adapter.spec.ts +43 -0
  42. package/src/__tests__/rs-table-grid-nav.spec.ts +63 -0
  43. package/src/__tests__/rs-table-isolation.spec.ts +158 -0
  44. package/src/__tests__/split-pane-utils.spec.ts +21 -0
  45. package/src/__tests__/table-chart-utils.spec.ts +88 -0
  46. package/src/__tests__/table-summary-utils.spec.ts +62 -0
  47. package/src/__tests__/table-utils.spec.ts +124 -0
  48. package/src/__tests__/theme.spec.ts +27 -0
  49. package/src/__tests__/useRsTableColumnResize.spec.ts +25 -0
  50. package/src/__tests__/useRsTableCore.perf.spec.ts +170 -0
  51. package/src/components/RsAlert.vue +212 -0
  52. package/src/components/RsAvatar.vue +1 -1
  53. package/src/components/RsBadge.vue +1 -1
  54. package/src/components/RsBreadcrumb.vue +1 -1
  55. package/src/components/RsButton.vue +147 -23
  56. package/src/components/RsCalendarGrid.vue +3 -3
  57. package/src/components/RsCard.vue +204 -20
  58. package/src/components/RsCodeBlock.vue +4 -4
  59. package/src/components/RsCodeEditor.vue +8 -8
  60. package/src/components/RsConfirmDialog.vue +217 -30
  61. package/src/components/RsContextMenu.vue +57 -17
  62. package/src/components/RsDatePicker.vue +316 -39
  63. package/src/components/RsDateTimePicker.vue +27 -2
  64. package/src/components/RsDescriptions.vue +186 -0
  65. package/src/components/RsDescriptionsItem.vue +32 -0
  66. package/src/components/RsDialog.vue +374 -52
  67. package/src/components/RsDivider.vue +89 -0
  68. package/src/components/RsDrawer.vue +559 -34
  69. package/src/components/RsDropdown.vue +34 -3
  70. package/src/components/RsDynamicTags.vue +531 -0
  71. package/src/components/RsEmpty.vue +1 -1
  72. package/src/components/RsForm.vue +164 -8
  73. package/src/components/RsFormItem.vue +423 -0
  74. package/src/components/RsFormItemControl.vue +21 -0
  75. package/src/components/RsFormList.vue +117 -0
  76. package/src/components/RsInput.vue +491 -100
  77. package/src/components/RsInputNumber.vue +77 -27
  78. package/src/components/RsLabel.vue +20 -5
  79. package/src/components/RsLink.vue +1 -1
  80. package/src/components/RsLoadingBar.vue +151 -0
  81. package/src/components/RsMarkdown.vue +5 -5
  82. package/src/components/RsMenu.vue +74 -16
  83. package/src/components/RsMenuItems.vue +147 -9
  84. package/src/components/RsMonacoEditor.vue +9 -3
  85. package/src/components/RsPagination.vue +93 -14
  86. package/src/components/RsProseEditor.vue +3 -3
  87. package/src/components/RsRadio.vue +81 -0
  88. package/src/components/RsRadioItem.vue +156 -0
  89. package/src/components/RsSelect.vue +372 -642
  90. package/src/components/RsSidebarGroup.vue +1 -1
  91. package/src/components/RsSplitPane.vue +90 -1
  92. package/src/components/RsStatCard.vue +2 -2
  93. package/src/components/RsSteps.vue +2 -2
  94. package/src/components/RsSwitch.vue +192 -0
  95. package/src/components/RsTable.vue +653 -2555
  96. package/src/components/RsTabs.vue +422 -90
  97. package/src/components/RsTag.vue +191 -0
  98. package/src/components/RsTerminal.vue +58 -19
  99. package/src/components/RsTimePicker.vue +72 -9
  100. package/src/components/RsTimePickerColumns.vue +137 -78
  101. package/src/components/RsToaster.vue +64 -24
  102. package/src/components/RsTooltip.vue +10 -1
  103. package/src/components/RsTooltipProvider.vue +11 -1
  104. package/src/components/RsTree.vue +45 -27
  105. package/src/components/RsUpload.vue +147 -14
  106. package/src/components/RsVNodeHost.vue +20 -0
  107. package/src/components/button-utils.ts +39 -0
  108. package/src/components/context-menu-utils.ts +6 -0
  109. package/src/components/date-picker-utils.ts +195 -4
  110. package/src/components/descriptions-utils.ts +24 -0
  111. package/src/components/dialog-utils.ts +182 -0
  112. package/src/components/dialog-window.ts +74 -9
  113. package/src/components/drawer-utils.ts +73 -0
  114. package/src/components/form-item-control.ts +55 -0
  115. package/src/components/form-path.ts +92 -0
  116. package/src/components/form-rules.ts +311 -0
  117. package/src/components/form-utils.ts +145 -3
  118. package/src/components/menu-utils.ts +11 -0
  119. package/src/components/overlay-utils.ts +3 -0
  120. package/src/components/radio-utils.ts +12 -0
  121. package/src/components/reka.ts +5 -0
  122. package/src/components/select-utils.ts +285 -6
  123. package/src/components/split-pane-utils.ts +65 -12
  124. package/src/components/table/RsTableBody.vue +217 -0
  125. package/src/components/table/RsTableBodyRow.vue +90 -1
  126. package/src/components/table/RsTableColGroup.vue +41 -0
  127. package/src/components/table/RsTableHeader.vue +158 -0
  128. package/src/components/table/RsTableHeaderFilter.vue +1 -1
  129. package/src/components/table/RsTableSummaryRow.vue +52 -0
  130. package/src/components/table/rs-table-api.ts +99 -0
  131. package/src/components/table/rs-table-compat-matrix.ts +71 -0
  132. package/src/components/table/rs-table-echarts-adapter.ts +81 -0
  133. package/src/components/table/rs-table-feature-host.ts +178 -0
  134. package/src/components/table/rs-table-grid-nav.ts +122 -0
  135. package/src/components/table/rs-table-header-types.ts +47 -0
  136. package/src/components/table/rs-table-module-registry.ts +72 -0
  137. package/src/components/table/rs-table-props.ts +274 -0
  138. package/src/components/table/rs-table-view-context.ts +258 -0
  139. package/src/components/table/rs-table.css +85 -7
  140. package/src/components/table/table-chart-utils.ts +165 -0
  141. package/src/components/table/table-features.ts +284 -0
  142. package/src/components/table/table-summary-utils.ts +194 -0
  143. package/src/components/table-utils.ts +452 -2
  144. package/src/components/tabs-utils.ts +142 -2
  145. package/src/components/time-picker-utils.ts +76 -3
  146. package/src/components/upload-utils.ts +122 -0
  147. package/src/components/use-rs-select.ts +436 -0
  148. package/src/components/virtual-list-utils.ts +15 -2
  149. package/src/composables/assembleRsTableApi.ts +79 -0
  150. package/src/composables/bindRsTableViewContext.ts +269 -0
  151. package/src/composables/createRsDialog.ts +357 -0
  152. package/src/composables/createRsTableEditEmitBridge.ts +38 -0
  153. package/src/composables/useRsConfig.ts +10 -3
  154. package/src/composables/useRsI18n.ts +16 -5
  155. package/src/composables/useRsLoadingBar.ts +36 -0
  156. package/src/composables/useRsTable.ts +134 -0
  157. package/src/composables/useRsTableA11y.ts +52 -0
  158. package/src/composables/useRsTableChartBridge.ts +79 -0
  159. package/src/composables/useRsTableColumnLayout.ts +320 -0
  160. package/src/composables/useRsTableColumnResize.ts +244 -0
  161. package/src/composables/useRsTableColumnVirtual.ts +180 -0
  162. package/src/composables/useRsTableColumns.ts +200 -0
  163. package/src/composables/useRsTableContextMenu.ts +257 -0
  164. package/src/composables/useRsTableCore.ts +256 -0
  165. package/src/composables/useRsTableEditActions.ts +606 -0
  166. package/src/composables/useRsTableEditLayer.ts +251 -0
  167. package/src/composables/useRsTableEngine.ts +357 -0
  168. package/src/composables/useRsTableGridKeyboard.ts +123 -0
  169. package/src/composables/useRsTableHeadless.ts +137 -0
  170. package/src/composables/useRsTableInteraction.ts +433 -0
  171. package/src/composables/useRsTableScrollHost.ts +75 -0
  172. package/src/composables/useRsTableScrollLayout.ts +255 -0
  173. package/src/composables/useRsTableSelectionSource.ts +89 -0
  174. package/src/composables/useRsTableShell.ts +416 -0
  175. package/src/composables/useRsTableShellChrome.ts +170 -0
  176. package/src/composables/useRsTableSummary.ts +113 -0
  177. package/src/composables/useRsTableViewProvide.ts +263 -0
  178. package/src/composables/useRsTableVirtual.ts +154 -0
  179. package/src/composables/useRsTabsNav.ts +4 -0
  180. package/src/composables/useTableEdit.ts +1 -1
  181. package/src/index.ts +378 -10
  182. package/src/locale/interpolate.ts +32 -0
  183. package/src/locale/messages.ts +40 -0
  184. package/src/styles.css +365 -56
  185. package/src/theme/brand.example.css +24 -2
  186. package/src/theme/css-token.ts +107 -0
  187. package/src/theme/presets.ts +4 -0
  188. package/src/theme/types.ts +68 -0
  189. package/src/components/RsTableColgroup.vue +0 -17
  190. package/src/components/RsTableHeaderRow.vue +0 -30
@@ -18,6 +18,13 @@ defineProps<{
18
18
  columnStyleMap: Map<string, Record<string, string> | undefined>
19
19
  showRowDragHandle: boolean
20
20
  expandable: boolean
21
+ /** 树表模式:在指定数据列内缩进展开 */
22
+ treeMode?: boolean
23
+ treeDepth?: number
24
+ treeHasChildren?: boolean
25
+ treeIndent?: number
26
+ treeColumnKey?: string | null
27
+ treeLoading?: boolean
21
28
  selectable: boolean
22
29
  showIndex: boolean
23
30
  showEditGutter: boolean
@@ -34,6 +41,8 @@ defineProps<{
34
41
  selected: boolean
35
42
  highlighted: boolean
36
43
  expanded: boolean
44
+ /** 树级联半选 */
45
+ indeterminate?: boolean
37
46
  disabled: boolean
38
47
  rowDragByRow: boolean
39
48
  dragging: boolean
@@ -99,7 +108,9 @@ const emit = defineEmits<{
99
108
 
100
109
  <template>
101
110
  <tr
111
+ role="row"
102
112
  class="rs-table__row"
113
+ :data-row-key="rowKey"
103
114
  :class="{
104
115
  'rs-table__row--selected': selected,
105
116
  'rs-table__row--highlighted': highlighted && !selected,
@@ -157,12 +168,19 @@ const emit = defineEmits<{
157
168
  :style="selectLeadStyle"
158
169
  @click.stop
159
170
  >
160
- <label class="rs-table__checkbox" :class="{ 'rs-table__checkbox--checked': selected }">
171
+ <label
172
+ class="rs-table__checkbox"
173
+ :class="{
174
+ 'rs-table__checkbox--checked': selected,
175
+ 'rs-table__checkbox--indeterminate': indeterminate && !selected,
176
+ }"
177
+ >
161
178
  <input
162
179
  :type="selectionType"
163
180
  class="rs-table__checkbox-input"
164
181
  :name="isRadioSelection ? 'rs-table-radio' : undefined"
165
182
  :checked="selected"
183
+ :indeterminate="Boolean(indeterminate && !selected)"
166
184
  :disabled="!canSelect"
167
185
  :aria-label="selectRowLabel"
168
186
  @change="emit('toggleSelect')"
@@ -218,7 +236,9 @@ const emit = defineEmits<{
218
236
  <td
219
237
  v-for="column in columns"
220
238
  :key="column.key"
239
+ role="gridcell"
221
240
  class="rs-table__td rs-table__td--data"
241
+ :data-col-key="column.key"
222
242
  :class="[
223
243
  columnTdClassMap.get(column.key),
224
244
  {
@@ -226,10 +246,12 @@ const emit = defineEmits<{
226
246
  'rs-table__td--editing': editingColKey === column.key,
227
247
  'rs-table__td--focused': focusColKey === column.key && editingColKey !== column.key,
228
248
  'rs-table__td--invalid': !!getCellError(rowKey, column.key),
249
+ 'rs-table__td--tree': treeMode && treeColumnKey === column.key,
229
250
  },
230
251
  ]"
231
252
  :style="columnStyleMap.get(column.key)"
232
253
  :draggable="rowDragByRow"
254
+ :tabindex="focusColKey === column.key && editingColKey !== column.key ? 0 : -1"
233
255
  :aria-selected="focusColKey === column.key || editingColKey === column.key ? 'true' : undefined"
234
256
  @click.stop="emit('cellClick', column.key, $event)"
235
257
  @dblclick.stop="emit('cellDblclick', column.key, $event)"
@@ -237,7 +259,74 @@ const emit = defineEmits<{
237
259
  @dragstart="rowDragByRow ? emit('rowDragStart', $event) : undefined"
238
260
  @dragend="rowDragByRow ? emit('rowDragEnd') : undefined"
239
261
  >
262
+ <div
263
+ v-if="treeMode && treeColumnKey === column.key"
264
+ class="rs-table__tree-cell"
265
+ :style="{ paddingInlineStart: `${(treeDepth ?? 0) * (treeIndent ?? 20)}px` }"
266
+ >
267
+ <button
268
+ v-if="treeHasChildren"
269
+ type="button"
270
+ class="rs-table__expand-btn rs-table__tree-toggle"
271
+ :class="{
272
+ 'rs-table__expand-btn--expanded': expanded,
273
+ 'rs-table__tree-toggle--loading': treeLoading,
274
+ }"
275
+ :disabled="treeLoading"
276
+ :aria-label="expanded ? collapseRowLabel : expandRowLabel"
277
+ @click.stop="emit('toggleExpand')"
278
+ >
279
+ ›
280
+ </button>
281
+ <span
282
+ v-else
283
+ class="rs-table__tree-leaf"
284
+ aria-hidden="true"
285
+ />
286
+ <span
287
+ :class="[
288
+ 'rs-table__tree-content',
289
+ { 'rs-table__cell-tip': cellTooltipEnabled(column, rowIndex) },
290
+ { 'rs-table__cell-tip--editing': editingColKey === column.key },
291
+ ]"
292
+ :data-rs-table-tip-mode="cellTooltipEnabled(column, rowIndex) ? cellTooltipMode(column, rowIndex) : undefined"
293
+ :data-rs-table-tip-text="cellTooltipText(column, row, rowIndex)"
294
+ :title="getCellError(rowKey, column.key) || cellTooltipFallbackTitle(column, row, rowIndex)"
295
+ >
296
+ <RsTableCell
297
+ :column="column"
298
+ :row="row"
299
+ :row-index="rowIndex"
300
+ :row-key="rowKey"
301
+ :table-editable="tableEditable"
302
+ :editing="editingColKey === column.key"
303
+ :dirty="isCellDirty(rowKey, column.key)"
304
+ :focused="focusColKey === column.key"
305
+ :draft="getCellDraft(rowKey, column.key)"
306
+ :has-custom-slot="hasColumnSlot(column.key)"
307
+ :has-edit-slot="hasEditSlot(column.key)"
308
+ :edit-trigger="editTrigger"
309
+ :row-commit="rowCommit"
310
+ :allow-null="allowNull"
311
+ :focus-mode="focusMode"
312
+ :null-label="nullLabel"
313
+ :error-message="getCellError(rowKey, column.key) ?? null"
314
+ :validating="isCellValidating(rowKey, column.key)"
315
+ @start-edit="emit('cellStartEdit', column.key)"
316
+ @commit="emit('cellCommit', column.key, $event)"
317
+ @cancel="emit('cellCancel', column.key)"
318
+ @update-draft="emit('cellUpdateDraft', column.key, $event)"
319
+ @navigate="emit('cellNavigate', column.key, $event)"
320
+ >
321
+ <slot :name="column.key" :row="row" :column="column" :index="rowIndex" />
322
+ <template v-if="hasEditSlot(column.key)" #editor="scope">
323
+ <slot :name="`edit-${column.key}`" v-bind="scope" />
324
+ </template>
325
+ </RsTableCell>
326
+ </span>
327
+ </div>
240
328
  <span
329
+ v-else
241
330
  :class="[
242
331
  { 'rs-table__cell-tip': cellTooltipEnabled(column, rowIndex) },
243
332
  { 'rs-table__cell-tip--editing': editingColKey === column.key },
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts" generic="T extends import('../table-utils').RsTableRowData">
2
+ import { useRsTableView } from './rs-table-view-context'
3
+
4
+ const view = useRsTableView<T>()
5
+ </script>
6
+
7
+ <template>
8
+ <colgroup v-if="view.useFixedColumnLayout">
9
+ <col v-if="view.showRowDragHandle" :style="{ width: `${view.prefixWidths.drag}px` }">
10
+ <col v-if="view.detailExpandable" :style="{ width: `${view.prefixWidths.expand}px` }">
11
+ <col v-if="view.showSelectColumn" :style="{ width: `${view.prefixWidths.select}px` }">
12
+ <col
13
+ v-if="view.showEditGutterColumn"
14
+ data-col-key="gutter"
15
+ :style="{ width: `${view.resolvedGutterWidth}px` }"
16
+ >
17
+ <col
18
+ v-else-if="view.showIndexColumn"
19
+ :style="{ width: `${view.resolvedIndexWidth}px` }"
20
+ >
21
+ <col v-if="view.showRowStatusColumn" :style="{ width: `${view.prefixWidths.status}px` }">
22
+ <col
23
+ v-if="view.columnPadLeft > 0"
24
+ class="rs-table__col-pad"
25
+ :style="{ width: `${view.columnPadLeft}px` }"
26
+ >
27
+ <col
28
+ v-for="column in view.visibleDataColumns"
29
+ :key="column.key"
30
+ :data-col-key="column.key"
31
+ :style="{
32
+ width: `${view.resolvedDataColumnWidth(column.key, column.width ?? column.minWidth)}px`,
33
+ }"
34
+ >
35
+ <col
36
+ v-if="view.columnPadRight > 0"
37
+ class="rs-table__col-pad"
38
+ :style="{ width: `${view.columnPadRight}px` }"
39
+ >
40
+ </colgroup>
41
+ </template>
@@ -0,0 +1,158 @@
1
+ <script setup lang="ts" generic="T extends import('../table-utils').RsTableRowData">
2
+ import RsIcon from '../RsIcon.vue'
3
+ import RsTableHeaderFilter from './RsTableHeaderFilter.vue'
4
+ import { useRsTableView } from './rs-table-view-context'
5
+
6
+ const view = useRsTableView<T>()
7
+ </script>
8
+
9
+ <template>
10
+ <thead
11
+ class="rs-table__head"
12
+ @pointerover.passive="view.onHeaderPointerOver($event)"
13
+ @pointerout.passive="view.onHeaderPointerOut($event)"
14
+ >
15
+ <tr role="row">
16
+ <th
17
+ v-if="view.showRowDragHandle"
18
+ role="columnheader"
19
+ class="rs-table__th rs-table__th--drag"
20
+ :style="view.dragLeadHeaderStyle"
21
+ />
22
+ <th
23
+ v-if="view.detailExpandable"
24
+ role="columnheader"
25
+ class="rs-table__th rs-table__th--expand"
26
+ :style="view.expandLeadHeaderStyle"
27
+ />
28
+ <th
29
+ v-if="view.showSelectColumn"
30
+ role="columnheader"
31
+ class="rs-table__th rs-table__th--selection"
32
+ :style="view.selectLeadHeaderStyle"
33
+ >
34
+ <label
35
+ v-if="!view.isRadioSelection"
36
+ class="rs-table__checkbox"
37
+ :class="{
38
+ 'rs-table__checkbox--checked': view.selectAllState === 'checked',
39
+ 'rs-table__checkbox--indeterminate': view.selectAllState === 'indeterminate',
40
+ }"
41
+ >
42
+ <input
43
+ type="checkbox"
44
+ class="rs-table__checkbox-input"
45
+ :checked="view.selectAllState === 'checked'"
46
+ :aria-label="view.headerLabels.selectAll"
47
+ @change="view.onToggleSelectAll()"
48
+ >
49
+ <span class="rs-table__checkbox-box" aria-hidden="true" />
50
+ </label>
51
+ </th>
52
+ <th
53
+ v-if="view.showEditGutterColumn"
54
+ role="columnheader"
55
+ class="rs-table__th rs-table__th--gutter rs-table__cell--center"
56
+ :style="view.gutterLeadHeaderStyle"
57
+ />
58
+ <th
59
+ v-else-if="view.showIndexColumn"
60
+ role="columnheader"
61
+ class="rs-table__th rs-table__th--index rs-table__cell--center"
62
+ :style="view.indexLeadHeaderStyle"
63
+ >
64
+ {{ view.headerLabels.index }}
65
+ </th>
66
+ <th
67
+ v-if="view.showRowStatusColumn"
68
+ role="columnheader"
69
+ class="rs-table__th rs-table__th--status rs-table__cell--center"
70
+ >
71
+ {{ view.headerLabels.rowStatus }}
72
+ </th>
73
+ <th
74
+ v-if="view.columnPadLeft > 0"
75
+ role="presentation"
76
+ class="rs-table__th rs-table__col-pad"
77
+ :style="{ width: `${view.columnPadLeft}px`, minWidth: `${view.columnPadLeft}px` }"
78
+ />
79
+ <th
80
+ v-for="column in view.visibleDataColumns"
81
+ :key="column.key"
82
+ role="columnheader"
83
+ :data-col-key="column.key"
84
+ class="rs-table__th"
85
+ :class="[
86
+ `rs-table__cell--${column.align ?? 'left'}`,
87
+ { 'rs-table__th--sortable': column.sortable },
88
+ { 'rs-table__th--filterable': column.filterable },
89
+ { 'rs-table__cell--fixed': column.fixed },
90
+ { 'rs-table__th--dragging': view.dragColumnKey === column.key },
91
+ { 'rs-table__th--drop-target': view.dropColumnKey === column.key },
92
+ ]"
93
+ :style="view.columnHeaderStyleMap.get(column.key)"
94
+ @dragover="view.onColumnDragOver(column.key, $event)"
95
+ @drop="view.onColumnDrop(column.key, $event)"
96
+ >
97
+ <span
98
+ v-if="view.columnDraggable"
99
+ class="rs-table__column-drag-handle"
100
+ draggable="true"
101
+ :aria-label="view.headerLabels.dragColumn"
102
+ @click.stop
103
+ @dragstart.stop="view.onColumnDragStart(column.key, $event)"
104
+ @dragend.stop="view.onColumnDragEnd()"
105
+ />
106
+ <slot :name="`header-${column.key}`" :column="column">
107
+ <span
108
+ v-if="column.headerTip"
109
+ class="rs-table__th-label rs-table__th-label--tip"
110
+ :data-rs-table-header-tip="column.headerTip"
111
+ >{{ column.title }}</span>
112
+ <span v-else class="rs-table__th-label">{{ column.title }}</span>
113
+ </slot>
114
+ <span v-if="column.filterable || column.sortable" class="rs-table__header-actions">
115
+ <RsTableHeaderFilter
116
+ v-if="column.filterable"
117
+ :model-value="view.columnFilterValue(column.key)"
118
+ :column-title="column.title"
119
+ :filter-label="view.headerLabels.filterColumn"
120
+ :placeholder="view.headerLabels.filterPlaceholder"
121
+ :clear-label="view.headerLabels.filterClear"
122
+ :apply-label="view.headerLabels.filterApply"
123
+ :active="view.isColumnFilterActive(view.columnFiltersState, column.key)"
124
+ @update:model-value="view.updateColumnFilter(column.key, $event)"
125
+ />
126
+ <button
127
+ v-if="column.sortable"
128
+ type="button"
129
+ class="rs-table__sort"
130
+ :class="{ 'rs-table__sort--active': !!view.sortOrderFor(column.key) }"
131
+ :title="column.title"
132
+ :aria-label="column.title"
133
+ @click.stop="view.onHeaderClick(column)"
134
+ >
135
+ <RsIcon :name="view.sortIconName(column.key)" size="sm" />
136
+ <span
137
+ v-if="view.multiSort && view.sortPriorityFor(column.key) > 1"
138
+ class="rs-table__sort-priority"
139
+ >
140
+ {{ view.sortPriorityFor(column.key) }}
141
+ </span>
142
+ </button>
143
+ </span>
144
+ <span
145
+ v-if="view.resizable"
146
+ class="rs-table__resize-handle"
147
+ @mousedown.stop="view.onResizeStart(column.key, $event)"
148
+ @click.stop
149
+ />
150
+ </th>
151
+ <th
152
+ v-if="view.columnPadRight > 0"
153
+ class="rs-table__th rs-table__col-pad"
154
+ :style="{ width: `${view.columnPadRight}px`, minWidth: `${view.columnPadRight}px` }"
155
+ />
156
+ </tr>
157
+ </thead>
158
+ </template>
@@ -87,7 +87,7 @@ function onEditorEnter(): void {
87
87
  .rs-table-header-filter__title {
88
88
  margin: 0 0 0.5rem;
89
89
  font-size: var(--rs-font-size-sm);
90
- font-weight: 600;
90
+ font-weight: var(--rs-font-weight-semibold);
91
91
  color: var(--rs-fg);
92
92
  }
93
93
  .rs-table-header-filter__field {
@@ -0,0 +1,52 @@
1
+ <script setup lang="ts" generic="T extends import('../table-utils').RsTableRowData">
2
+ /**
3
+ * 表格 footer 汇总行:按列渲染 summaryCells.text。
4
+ *
5
+ * 前缀列(拖拽/展开/选择/序号等)用 colspan 占位,与表体列对齐;
6
+ * padLeft / padRight 对齐列虚拟滚动的左右垫片。
7
+ */
8
+ import type { RsTableColumn } from '../table-utils'
9
+ import type { RsTableSummaryCell } from './table-summary-utils'
10
+
11
+ defineProps<{
12
+ /** 与表体一致的展示列(通常为 displayColumns) */
13
+ columns: RsTableColumn<T>[]
14
+ /** 与 columns 按 key 对应的汇总单元格 */
15
+ cells: RsTableSummaryCell[]
16
+ /** 前缀列占位 colspan(拖拽/展开/选择/序号/状态等) */
17
+ prefixColspan?: number
18
+ /** 列虚拟左侧垫片宽度(px) */
19
+ padLeft?: number
20
+ /** 列虚拟右侧垫片宽度(px) */
21
+ padRight?: number
22
+ }>()
23
+ </script>
24
+
25
+ <template>
26
+ <tr class="rs-table__summary-row">
27
+ <td
28
+ v-if="(prefixColspan ?? 0) > 0"
29
+ class="rs-table__summary-cell rs-table__summary-cell--prefix"
30
+ :colspan="prefixColspan"
31
+ />
32
+ <td
33
+ v-if="(padLeft ?? 0) > 0"
34
+ class="rs-table__summary-cell rs-table__summary-cell--pad"
35
+ :style="{ width: `${padLeft}px`, minWidth: `${padLeft}px` }"
36
+ />
37
+ <td
38
+ v-for="column in columns"
39
+ :key="column.key"
40
+ class="rs-table__summary-cell"
41
+ :class="column.align ? `rs-table__summary-cell--${column.align}` : undefined"
42
+ :data-col-key="column.key"
43
+ >
44
+ {{ cells.find((c) => c.key === column.key)?.text ?? '' }}
45
+ </td>
46
+ <td
47
+ v-if="(padRight ?? 0) > 0"
48
+ class="rs-table__summary-cell rs-table__summary-cell--pad"
49
+ :style="{ width: `${padRight}px`, minWidth: `${padRight}px` }"
50
+ />
51
+ </tr>
52
+ </template>
@@ -0,0 +1,99 @@
1
+ /**
2
+ * RsTable 公开 API 契约(对标 AG Grid GridApi / TanStack table 实例)。
3
+ *
4
+ * 设计原则(大厂共性):
5
+ * - 业务与第三方只依赖本接口,不依赖 SFC 内部变量
6
+ * - 加性演进:新增方法用可选字段或 bump RS_TABLE_API_VERSION
7
+ * - 查询与命令分离:getXxx 无副作用;setXxx / commitXxx 为命令
8
+ *
9
+ * 兼容承诺:同主版本内不删除、不改语义已有方法签名。
10
+ */
11
+
12
+ import type { RsTableStagedCell } from '../../composables/useTableEdit'
13
+ import type { RsTableAnalyticsSnapshot } from '../../composables/useRsTableSelectionSource'
14
+ import type { RsTableColumn, RsTableRowData } from '../table-utils'
15
+
16
+ /** 公开 API 语义版本(与 package 版本解耦,专指 TableApi 契约) */
17
+ export const RS_TABLE_API_VERSION = '1.0.0' as const
18
+
19
+ /**
20
+ * 表格实例 API:ref / expose / provide / inject 共用同一形状。
21
+ */
22
+ export interface RsTableApi<T extends RsTableRowData = RsTableRowData> {
23
+ readonly apiVersion: typeof RS_TABLE_API_VERSION
24
+
25
+ // -- 行查询 --
26
+ getRowByKey: (rowKey: string) => T | undefined
27
+ getRowByIndex: (index: number) => T | undefined
28
+ getColumnValues: (colKey: string) => unknown[]
29
+ getCellValue: (rowKey: string, colKey: string) => unknown
30
+
31
+ // -- 视图 / 选中(分析与图表真源)--
32
+ /** 筛选/排序后的业务行(不含 group/expand 伪行) */
33
+ getViewRows: () => T[]
34
+ getSelectedRows: () => T[]
35
+ getSelectedRowKeys: () => string[]
36
+ getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
37
+ subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
38
+
39
+ // -- 高亮 --
40
+ getHighlightedRowKey: () => string | undefined
41
+ setHighlightedRowKey: (key: string | undefined) => void
42
+
43
+ // -- 编辑 --
44
+ cancelCellEdit: () => void
45
+ cancelAllEdits: () => void
46
+ getDirtyCellKeys: () => string[]
47
+ getCellError: (rowKey: string, colKey: string) => string | undefined
48
+ setCellError: (rowKey: string, colKey: string, message: string | null) => void
49
+ stageCell: (cell: RsTableStagedCell) => void
50
+ rejectRowEdit: (rowKey: string, reason?: string) => void
51
+ undoEdit: () => void
52
+ redoEdit: () => void
53
+ commitRowEdits: (rowKey: string) => void
54
+ rollbackRowEdits: (rowKey: string) => void
55
+
56
+ // -- 扩展元数据(可选,便于遥测)--
57
+ getActiveFeatureIds?: () => string[]
58
+ /** 实例 Feature 贡献的工具条项 */
59
+ getToolbarItems?: () => import('./rs-table-feature-host').RsTableToolbarItem[]
60
+ /** 实例 Feature 贡献的 overlay 描述 */
61
+ getOverlayContributions?: () => import('./rs-table-feature-host').RsTableOverlayContribution[]
62
+ }
63
+
64
+ /** provide/inject 键:子组件或插槽内拿表格 API */
65
+ export const RS_TABLE_API_KEY: unique symbol = Symbol('rsTableApi')
66
+
67
+ /**
68
+ * 由 RsTable 内部装配公开 API(单一出口,避免 expose 与 inject 漂移)。
69
+ */
70
+ export function createRsTableApi<T extends RsTableRowData>(
71
+ impl: Omit<RsTableApi<T>, 'apiVersion'> & { apiVersion?: typeof RS_TABLE_API_VERSION },
72
+ ): RsTableApi<T> {
73
+ return {
74
+ apiVersion: impl.apiVersion ?? RS_TABLE_API_VERSION,
75
+ ...impl,
76
+ }
77
+ }
78
+
79
+ /** 类型守卫:判断未知对象是否像 RsTableApi */
80
+ export function isRsTableApi<T extends RsTableRowData = RsTableRowData>(
81
+ value: unknown,
82
+ ): value is RsTableApi<T> {
83
+ if (!value || typeof value !== 'object') return false
84
+ const api = value as Partial<RsTableApi<T>>
85
+ return (
86
+ typeof api.getViewRows === 'function' &&
87
+ typeof api.getSelectedRows === 'function' &&
88
+ typeof api.getAnalyticsSnapshot === 'function'
89
+ )
90
+ }
91
+
92
+ /** 列定义辅助:标记某列参与图表分类(文档/约定,不强制运行时) */
93
+ export type RsTableColumnChartMeta = {
94
+ chartCategory?: boolean
95
+ chartValue?: boolean
96
+ }
97
+
98
+ export type RsTableColumnWithChartMeta<T extends RsTableRowData = RsTableRowData> = RsTableColumn<T> &
99
+ RsTableColumnChartMeta
@@ -0,0 +1,71 @@
1
+ /**
2
+ * RsTable 公开契约兼容矩阵(单一真源)。
3
+ *
4
+ * - 测试锁定这些键:删除/改名 = 失败,须 bump RS_TABLE_API_VERSION
5
+ * - 文档与 CI 共用,避免矩阵与实现漂移
6
+ */
7
+
8
+ import { RS_TABLE_API_VERSION } from './rs-table-api'
9
+ import { RS_TABLE_PROP_DEFAULTS } from './rs-table-props'
10
+
11
+ /** 当前锁定的 API 契约版本(与 createRsTableApi 一致) */
12
+ export const RS_TABLE_COMPAT_API_VERSION = RS_TABLE_API_VERSION
13
+
14
+ /**
15
+ * 1.0.x 必选 Api 方法(加性可选方法不在此列)。
16
+ * 删减任一项视为 breaking。
17
+ */
18
+ export const RS_TABLE_API_REQUIRED_METHODS = [
19
+ 'getRowByKey',
20
+ 'getRowByIndex',
21
+ 'getColumnValues',
22
+ 'getCellValue',
23
+ 'getViewRows',
24
+ 'getSelectedRows',
25
+ 'getSelectedRowKeys',
26
+ 'getAnalyticsSnapshot',
27
+ 'subscribeAnalytics',
28
+ 'getHighlightedRowKey',
29
+ 'setHighlightedRowKey',
30
+ 'cancelCellEdit',
31
+ 'cancelAllEdits',
32
+ 'getDirtyCellKeys',
33
+ 'getCellError',
34
+ 'setCellError',
35
+ 'stageCell',
36
+ 'rejectRowEdit',
37
+ 'undoEdit',
38
+ 'redoEdit',
39
+ 'commitRowEdits',
40
+ 'rollbackRowEdits',
41
+ ] as const
42
+
43
+ /** 1.0.x 加性可选 Api(可缺省,但若存在须为函数) */
44
+ export const RS_TABLE_API_OPTIONAL_METHODS = [
45
+ 'getActiveFeatureIds',
46
+ 'getToolbarItems',
47
+ 'getOverlayContributions',
48
+ ] as const
49
+
50
+ /**
51
+ * 稳定 emit 名(同主版本不删)。
52
+ * 新增 emit 可追加到测试期望的超集校验。
53
+ */
54
+ export const RS_TABLE_STABLE_EMITS = [
55
+ 'rowClick',
56
+ 'rowDblclick',
57
+ 'cellView',
58
+ 'update:selectedRowKeys',
59
+ 'selectionChange',
60
+ 'loadMore',
61
+ 'cellEditCommit',
62
+ 'cellEditCancel',
63
+ 'rowEditCommit',
64
+ 'rowEditRollback',
65
+ ] as const
66
+
67
+ /** 带默认值的 props 键(与 RS_TABLE_PROP_DEFAULTS 同步校验) */
68
+ export const RS_TABLE_DEFAULTED_PROP_KEYS = Object.keys(RS_TABLE_PROP_DEFAULTS).sort()
69
+
70
+ export type RsTableApiRequiredMethod = (typeof RS_TABLE_API_REQUIRED_METHODS)[number]
71
+ export type RsTableApiOptionalMethod = (typeof RS_TABLE_API_OPTIONAL_METHODS)[number]
@@ -0,0 +1,81 @@
1
+ /**
2
+ * RsTable 系列 → ECharts option 纯映射(不依赖 echarts 包)。
3
+ *
4
+ * 业务侧:`import('echarts').then(e => e.init(el).setOption(option))`
5
+ * 内核保持库无关;本文件只产出普通 JSON 可序列化结构。
6
+ */
7
+
8
+ import type { RsTableChartSeries } from './table-chart-utils'
9
+
10
+ /** 最小 ECharts option 面(刻意放宽,避免绑定具体 echarts 类型版本) */
11
+ export type RsTableEChartsOption = {
12
+ tooltip?: { trigger?: string }
13
+ legend?: { data?: string[] }
14
+ xAxis?: { type?: string; data?: string[] } | Array<{ type?: string; data?: string[] }>
15
+ yAxis?: { type?: string } | Array<{ type?: string }>
16
+ series: Array<{
17
+ id?: string
18
+ name?: string
19
+ type: 'bar' | 'line' | 'pie'
20
+ data: Array<number | { name: string; value: number }>
21
+ }>
22
+ }
23
+
24
+ export interface MapRsTableSeriesToEChartsOptions {
25
+ /** 多系列时是否共用类目轴(默认 true;饼图忽略) */
26
+ sharedCategoryAxis?: boolean
27
+ /** tooltip.trigger,默认按首系列推断 */
28
+ tooltipTrigger?: 'item' | 'axis'
29
+ }
30
+
31
+ function mapKind(kind: RsTableChartSeries['kind']): 'bar' | 'line' | 'pie' {
32
+ if (kind === 'pie') return 'pie'
33
+ if (kind === 'line') return 'line'
34
+ return 'bar'
35
+ }
36
+
37
+ /**
38
+ * 将库无关 series 映射为可直接 setOption 的 ECharts option。
39
+ */
40
+ export function mapRsTableSeriesToEChartsOption(
41
+ seriesList: readonly RsTableChartSeries[],
42
+ options: MapRsTableSeriesToEChartsOptions = {},
43
+ ): RsTableEChartsOption {
44
+ if (!seriesList.length) {
45
+ return { series: [] }
46
+ }
47
+
48
+ const allPie = seriesList.every((s) => s.kind === 'pie')
49
+ const tooltipTrigger =
50
+ options.tooltipTrigger ?? (allPie ? 'item' : 'axis')
51
+
52
+ if (allPie) {
53
+ return {
54
+ tooltip: { trigger: tooltipTrigger },
55
+ legend: { data: seriesList.flatMap((s) => s.categories) },
56
+ series: seriesList.map((s) => ({
57
+ id: s.id,
58
+ name: s.id,
59
+ type: 'pie' as const,
60
+ data: s.points.map((p) => ({ name: p.name, value: p.value })),
61
+ })),
62
+ }
63
+ }
64
+
65
+ const shared = options.sharedCategoryAxis !== false
66
+ const primary = seriesList[0]!
67
+ return {
68
+ tooltip: { trigger: tooltipTrigger },
69
+ legend: { data: seriesList.map((s) => s.id) },
70
+ xAxis: shared
71
+ ? { type: 'category', data: primary.categories }
72
+ : seriesList.map((s) => ({ type: 'category', data: s.categories })),
73
+ yAxis: { type: 'value' },
74
+ series: seriesList.map((s) => ({
75
+ id: s.id,
76
+ name: s.id,
77
+ type: mapKind(s.kind),
78
+ data: s.values,
79
+ })),
80
+ }
81
+ }