niuma-ui 1.0.1 → 1.0.2
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/CHANGELOG.md +38 -1
- package/package.json +10 -2
- package/src/__tests__/RsButton.spec.ts +47 -0
- package/src/__tests__/RsCard.spec.ts +160 -1
- package/src/__tests__/RsConfirmDialog.spec.ts +127 -0
- package/src/__tests__/RsContextMenu.spec.ts +12 -0
- package/src/__tests__/RsDateTimePicker.spec.ts +11 -0
- package/src/__tests__/RsDescriptions.spec.ts +50 -0
- package/src/__tests__/RsDialog.spec.ts +117 -0
- package/src/__tests__/RsDrawer.spec.ts +96 -3
- package/src/__tests__/RsDynamicTags.spec.ts +52 -0
- package/src/__tests__/RsForm.spec.ts +245 -1
- package/src/__tests__/RsInput.spec.ts +15 -1
- package/src/__tests__/RsMenu.spec.ts +406 -14
- package/src/__tests__/RsPagination.spec.ts +67 -2
- package/src/__tests__/RsSelect.spec.ts +42 -1
- package/src/__tests__/RsSwitch.spec.ts +44 -0
- package/src/__tests__/RsTable.a11y.spec.ts +83 -0
- package/src/__tests__/RsTable.dom-baseline.spec.ts +87 -0
- package/src/__tests__/RsTable.perf.spec.ts +23 -4
- package/src/__tests__/RsTable.spec.ts +63 -8
- package/src/__tests__/RsTable.ssr.spec.ts +71 -0
- package/src/__tests__/RsTable.summary.spec.ts +45 -0
- package/src/__tests__/RsTabs.spec.ts +102 -4
- package/src/__tests__/RsTimePicker.spec.ts +11 -0
- package/src/__tests__/RsUpload.spec.ts +62 -6
- package/src/__tests__/createRsDialog.spec.ts +145 -0
- package/src/__tests__/form-rules.spec.ts +76 -0
- package/src/__tests__/perf-budgets.ts +32 -0
- package/src/__tests__/rs-table-api.spec.ts +206 -0
- package/src/__tests__/rs-table-compat-matrix.spec.ts +108 -0
- package/src/__tests__/rs-table-echarts-adapter.spec.ts +43 -0
- package/src/__tests__/rs-table-grid-nav.spec.ts +63 -0
- package/src/__tests__/rs-table-isolation.spec.ts +158 -0
- package/src/__tests__/split-pane-utils.spec.ts +21 -0
- package/src/__tests__/table-chart-utils.spec.ts +88 -0
- package/src/__tests__/table-summary-utils.spec.ts +62 -0
- package/src/__tests__/table-utils.spec.ts +124 -0
- package/src/__tests__/theme.spec.ts +25 -0
- package/src/__tests__/useRsTableColumnResize.spec.ts +25 -0
- package/src/__tests__/useRsTableCore.perf.spec.ts +170 -0
- package/src/components/RsAlert.vue +212 -0
- package/src/components/RsButton.vue +145 -21
- package/src/components/RsCard.vue +200 -16
- package/src/components/RsConfirmDialog.vue +215 -28
- package/src/components/RsContextMenu.vue +51 -13
- package/src/components/RsDatePicker.vue +267 -34
- package/src/components/RsDateTimePicker.vue +19 -2
- package/src/components/RsDescriptions.vue +186 -0
- package/src/components/RsDescriptionsItem.vue +32 -0
- package/src/components/RsDialog.vue +363 -51
- package/src/components/RsDivider.vue +89 -0
- package/src/components/RsDrawer.vue +558 -33
- package/src/components/RsDropdown.vue +33 -2
- package/src/components/RsDynamicTags.vue +517 -0
- package/src/components/RsForm.vue +97 -8
- package/src/components/RsInput.vue +80 -82
- package/src/components/RsInputNumber.vue +64 -23
- package/src/components/RsLabel.vue +20 -5
- package/src/components/RsLoadingBar.vue +151 -0
- package/src/components/RsMenu.vue +73 -15
- package/src/components/RsMenuItems.vue +147 -9
- package/src/components/RsPagination.vue +93 -14
- package/src/components/RsRadio.vue +81 -0
- package/src/components/RsRadioItem.vue +156 -0
- package/src/components/RsSelect.vue +67 -58
- package/src/components/RsSplitPane.vue +69 -1
- package/src/components/RsSwitch.vue +179 -0
- package/src/components/RsTable.vue +653 -2555
- package/src/components/RsTabs.vue +415 -83
- package/src/components/RsTag.vue +191 -0
- package/src/components/RsTimePicker.vue +44 -5
- package/src/components/RsTimePickerColumns.vue +136 -77
- package/src/components/RsToaster.vue +58 -18
- package/src/components/RsTree.vue +39 -21
- package/src/components/RsUpload.vue +145 -12
- package/src/components/RsVNodeHost.vue +20 -0
- package/src/components/button-utils.ts +39 -0
- package/src/components/context-menu-utils.ts +6 -0
- package/src/components/date-picker-utils.ts +84 -4
- package/src/components/descriptions-utils.ts +24 -0
- package/src/components/dialog-utils.ts +147 -0
- package/src/components/dialog-window.ts +74 -9
- package/src/components/drawer-utils.ts +73 -0
- package/src/components/form-rules.ts +257 -0
- package/src/components/form-utils.ts +52 -2
- package/src/components/menu-utils.ts +11 -0
- package/src/components/overlay-utils.ts +3 -0
- package/src/components/radio-utils.ts +12 -0
- package/src/components/reka.ts +5 -0
- package/src/components/select-utils.ts +21 -2
- package/src/components/split-pane-utils.ts +65 -12
- package/src/components/table/RsTableBody.vue +217 -0
- package/src/components/table/RsTableBodyRow.vue +90 -1
- package/src/components/table/RsTableColGroup.vue +41 -0
- package/src/components/table/RsTableHeader.vue +158 -0
- package/src/components/table/RsTableSummaryRow.vue +52 -0
- package/src/components/table/rs-table-api.ts +99 -0
- package/src/components/table/rs-table-compat-matrix.ts +71 -0
- package/src/components/table/rs-table-echarts-adapter.ts +81 -0
- package/src/components/table/rs-table-feature-host.ts +178 -0
- package/src/components/table/rs-table-grid-nav.ts +122 -0
- package/src/components/table/rs-table-header-types.ts +47 -0
- package/src/components/table/rs-table-module-registry.ts +72 -0
- package/src/components/table/rs-table-props.ts +274 -0
- package/src/components/table/rs-table-view-context.ts +258 -0
- package/src/components/table/rs-table.css +84 -6
- package/src/components/table/table-chart-utils.ts +165 -0
- package/src/components/table/table-features.ts +284 -0
- package/src/components/table/table-summary-utils.ts +194 -0
- package/src/components/table-utils.ts +452 -2
- package/src/components/tabs-utils.ts +142 -2
- package/src/components/time-picker-utils.ts +76 -3
- package/src/components/upload-utils.ts +122 -0
- package/src/components/virtual-list-utils.ts +15 -2
- package/src/composables/assembleRsTableApi.ts +79 -0
- package/src/composables/bindRsTableViewContext.ts +269 -0
- package/src/composables/createRsDialog.ts +357 -0
- package/src/composables/createRsTableEditEmitBridge.ts +38 -0
- package/src/composables/useRsLoadingBar.ts +36 -0
- package/src/composables/useRsTable.ts +134 -0
- package/src/composables/useRsTableA11y.ts +52 -0
- package/src/composables/useRsTableChartBridge.ts +79 -0
- package/src/composables/useRsTableColumnLayout.ts +320 -0
- package/src/composables/useRsTableColumnResize.ts +244 -0
- package/src/composables/useRsTableColumnVirtual.ts +180 -0
- package/src/composables/useRsTableColumns.ts +200 -0
- package/src/composables/useRsTableContextMenu.ts +257 -0
- package/src/composables/useRsTableCore.ts +256 -0
- package/src/composables/useRsTableEditActions.ts +606 -0
- package/src/composables/useRsTableEditLayer.ts +251 -0
- package/src/composables/useRsTableEngine.ts +357 -0
- package/src/composables/useRsTableGridKeyboard.ts +123 -0
- package/src/composables/useRsTableHeadless.ts +137 -0
- package/src/composables/useRsTableInteraction.ts +433 -0
- package/src/composables/useRsTableScrollHost.ts +75 -0
- package/src/composables/useRsTableScrollLayout.ts +255 -0
- package/src/composables/useRsTableSelectionSource.ts +89 -0
- package/src/composables/useRsTableShell.ts +416 -0
- package/src/composables/useRsTableShellChrome.ts +170 -0
- package/src/composables/useRsTableSummary.ts +113 -0
- package/src/composables/useRsTableViewProvide.ts +263 -0
- package/src/composables/useRsTableVirtual.ts +154 -0
- package/src/composables/useRsTabsNav.ts +4 -0
- package/src/composables/useTableEdit.ts +1 -1
- package/src/index.ts +309 -9
- package/src/locale/messages.ts +30 -0
- package/src/styles.css +305 -52
- package/src/theme/brand.example.css +21 -2
- package/src/theme/presets.ts +4 -0
- package/src/theme/types.ts +67 -0
- package/src/components/RsTableColgroup.vue +0 -17
- package/src/components/RsTableHeaderRow.vue +0 -30
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { createApp, h, ref, type App } from 'vue'
|
|
2
|
+
import RsConfigProvider from '../components/RsConfigProvider.vue'
|
|
3
|
+
import RsConfirmDialog from '../components/RsConfirmDialog.vue'
|
|
4
|
+
import RsDialog from '../components/RsDialog.vue'
|
|
5
|
+
import type { RsConfirmOptions } from '../components/dialog-utils'
|
|
6
|
+
import type { RsDialogBeforeClose, RsDialogCloseReason, RsDialogWidth } from '../components/dialog-utils'
|
|
7
|
+
import type { RsFeedbackTone } from '../components/overlay-utils'
|
|
8
|
+
import { defaultLocale, type RsLocale } from '../locale/types'
|
|
9
|
+
import type { RsThemeMode } from '../theme/types'
|
|
10
|
+
|
|
11
|
+
export type RsConfirmResult = boolean
|
|
12
|
+
|
|
13
|
+
export type RsConfirmInput = RsConfirmOptions | string
|
|
14
|
+
|
|
15
|
+
export interface RsDialogOpenOptions {
|
|
16
|
+
title?: string
|
|
17
|
+
subtitle?: string
|
|
18
|
+
description?: string
|
|
19
|
+
width?: RsDialogWidth
|
|
20
|
+
tone?: RsFeedbackTone
|
|
21
|
+
/**
|
|
22
|
+
* 布局。默认 window(工作窗)。
|
|
23
|
+
* confirm 仅作历史兼容;新代码的确认/提示请用 rsConfirm / RsConfirmDialog。
|
|
24
|
+
*/
|
|
25
|
+
layout?: 'window' | 'confirm'
|
|
26
|
+
showOverlay?: boolean
|
|
27
|
+
/** 遮罩不透明度 0–1 */
|
|
28
|
+
overlayOpacity?: number
|
|
29
|
+
/** 遮罩模糊;number 为 px */
|
|
30
|
+
overlayBlur?: number | string
|
|
31
|
+
showClose?: boolean
|
|
32
|
+
closeOnOverlayClick?: boolean
|
|
33
|
+
closeOnEsc?: boolean
|
|
34
|
+
showFooter?: boolean
|
|
35
|
+
showCancel?: boolean
|
|
36
|
+
showConfirm?: boolean
|
|
37
|
+
cancelText?: string
|
|
38
|
+
confirmText?: string
|
|
39
|
+
confirmVariant?: 'primary' | 'danger'
|
|
40
|
+
autoCloseOnConfirm?: boolean
|
|
41
|
+
beforeClose?: RsDialogBeforeClose
|
|
42
|
+
teleportTo?: string | HTMLElement
|
|
43
|
+
/** 默认插槽对应 #body 的渲染函数 */
|
|
44
|
+
body?: () => unknown
|
|
45
|
+
footer?: () => unknown
|
|
46
|
+
onConfirm?: () => void | Promise<void>
|
|
47
|
+
onCancel?: () => void
|
|
48
|
+
onAfterClose?: (reason: RsDialogCloseReason) => void
|
|
49
|
+
/** 命令式挂载时覆盖主题;默认读 document data-rs-theme */
|
|
50
|
+
theme?: RsThemeMode
|
|
51
|
+
/** 命令式挂载时覆盖语言;默认读 document data-rs-locale */
|
|
52
|
+
locale?: RsLocale
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface RsDialogHandle {
|
|
56
|
+
close: (reason?: RsDialogCloseReason) => Promise<boolean>
|
|
57
|
+
destroy: () => void
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** tone 快捷方法:可传完整选项或纯文案(作为 description) */
|
|
61
|
+
export interface RsConfirmApi {
|
|
62
|
+
(options?: RsConfirmOptions): Promise<RsConfirmResult>
|
|
63
|
+
warning: (options?: RsConfirmInput) => Promise<RsConfirmResult>
|
|
64
|
+
danger: (options?: RsConfirmInput) => Promise<RsConfirmResult>
|
|
65
|
+
info: (options?: RsConfirmInput) => Promise<RsConfirmResult>
|
|
66
|
+
success: (options?: RsConfirmInput) => Promise<RsConfirmResult>
|
|
67
|
+
error: (options?: RsConfirmInput) => Promise<RsConfirmResult>
|
|
68
|
+
confirm: (options?: RsConfirmInput) => Promise<RsConfirmResult>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readDomTheme(): RsThemeMode {
|
|
72
|
+
if (typeof document === 'undefined') return 'light'
|
|
73
|
+
return document.documentElement.getAttribute('data-rs-theme') === 'dark' ? 'dark' : 'light'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readDomLocale(): RsLocale {
|
|
77
|
+
if (typeof document === 'undefined') return defaultLocale
|
|
78
|
+
const value = document.documentElement.getAttribute('data-rs-locale')
|
|
79
|
+
return value === 'en-US' || value === 'zh-CN' ? value : defaultLocale
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function mountHost(
|
|
83
|
+
render: () => unknown,
|
|
84
|
+
config?: { theme?: RsThemeMode; locale?: RsLocale },
|
|
85
|
+
): { app: App; container: HTMLDivElement; destroy: () => void } {
|
|
86
|
+
const container = document.createElement('div')
|
|
87
|
+
document.body.appendChild(container)
|
|
88
|
+
|
|
89
|
+
// themeScope=local:不覆盖主应用的 document 主题;Portal 仍靠页面已有 data-rs-theme。
|
|
90
|
+
// ConfigProvider 负责 provide locale,使命令式实例的 i18n 默认文案正确。
|
|
91
|
+
const theme = config?.theme ?? readDomTheme()
|
|
92
|
+
const locale = config?.locale ?? readDomLocale()
|
|
93
|
+
|
|
94
|
+
const app = createApp({
|
|
95
|
+
setup() {
|
|
96
|
+
return () =>
|
|
97
|
+
h(
|
|
98
|
+
RsConfigProvider,
|
|
99
|
+
{ theme, locale, themeScope: 'local' },
|
|
100
|
+
{ default: () => render() },
|
|
101
|
+
)
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
app.mount(container)
|
|
105
|
+
const destroy = () => {
|
|
106
|
+
app.unmount()
|
|
107
|
+
container.remove()
|
|
108
|
+
}
|
|
109
|
+
return { app, container, destroy }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function normalizeConfirmInput(
|
|
113
|
+
options: RsConfirmInput | undefined,
|
|
114
|
+
defaults: Partial<RsConfirmOptions>,
|
|
115
|
+
): RsConfirmOptions {
|
|
116
|
+
if (typeof options === 'string') {
|
|
117
|
+
return { ...defaults, description: options }
|
|
118
|
+
}
|
|
119
|
+
return { ...defaults, ...(options ?? {}) }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 命令式确认框(基于 RsConfirmDialog)。
|
|
124
|
+
* resolve(true) 为确认,resolve(false) 为取消/关闭。
|
|
125
|
+
* 若传入 onConfirm 且返回 Promise,确认期间自动 loading,成功后关闭。
|
|
126
|
+
*/
|
|
127
|
+
function rsConfirmCore(options: RsConfirmOptions = {}): Promise<RsConfirmResult> {
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const open = ref(true)
|
|
130
|
+
const loading = ref(options.confirmLoading ?? false)
|
|
131
|
+
let settled = false
|
|
132
|
+
let confirmed = false
|
|
133
|
+
const hasAsyncConfirm = typeof options.onConfirm === 'function'
|
|
134
|
+
|
|
135
|
+
const settle = (value: boolean) => {
|
|
136
|
+
if (settled) return
|
|
137
|
+
settled = true
|
|
138
|
+
open.value = false
|
|
139
|
+
if (!value) options.onCancel?.()
|
|
140
|
+
// 等关闭帧后再销毁,避免动画被硬切
|
|
141
|
+
window.setTimeout(() => destroy(), 200)
|
|
142
|
+
resolve(value)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const { destroy } = mountHost(
|
|
146
|
+
() =>
|
|
147
|
+
h(
|
|
148
|
+
RsConfirmDialog,
|
|
149
|
+
{
|
|
150
|
+
open: open.value,
|
|
151
|
+
'onUpdate:open': (value: boolean) => {
|
|
152
|
+
open.value = value
|
|
153
|
+
if (!value) {
|
|
154
|
+
// 等同 tick 内的 confirm/cancel 事件先落定,避免 Action 关闭抢先 resolve(false)
|
|
155
|
+
queueMicrotask(() => settle(confirmed))
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
title: options.title,
|
|
159
|
+
subtitle: options.subtitle,
|
|
160
|
+
description: options.description,
|
|
161
|
+
tone: options.tone,
|
|
162
|
+
icon: options.icon,
|
|
163
|
+
width: options.width,
|
|
164
|
+
confirmText: options.confirmText,
|
|
165
|
+
cancelText: options.cancelText,
|
|
166
|
+
confirmVariant: options.confirmVariant,
|
|
167
|
+
showCancel: options.showCancel,
|
|
168
|
+
confirmLoading: loading.value,
|
|
169
|
+
beforeClose: options.beforeClose,
|
|
170
|
+
// 有异步 onConfirm 时由我们在结束后关闭,避免提前关掉
|
|
171
|
+
autoCloseOnConfirm: !hasAsyncConfirm,
|
|
172
|
+
showOverlay: options.showOverlay ?? true,
|
|
173
|
+
overlayOpacity: options.overlayOpacity,
|
|
174
|
+
overlayBlur: options.overlayBlur,
|
|
175
|
+
teleportTo: options.teleportTo === false ? false : options.teleportTo,
|
|
176
|
+
onConfirm: () => {
|
|
177
|
+
if (!hasAsyncConfirm) {
|
|
178
|
+
confirmed = true
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
loading.value = true
|
|
182
|
+
void Promise.resolve()
|
|
183
|
+
.then(() => options.onConfirm?.())
|
|
184
|
+
.then(() => {
|
|
185
|
+
confirmed = true
|
|
186
|
+
settle(true)
|
|
187
|
+
})
|
|
188
|
+
.catch(() => {
|
|
189
|
+
loading.value = false
|
|
190
|
+
})
|
|
191
|
+
},
|
|
192
|
+
onCancel: () => {
|
|
193
|
+
confirmed = false
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
options.extra
|
|
197
|
+
? {
|
|
198
|
+
extra: () => options.extra?.(),
|
|
199
|
+
}
|
|
200
|
+
: undefined,
|
|
201
|
+
),
|
|
202
|
+
{ theme: options.theme, locale: options.locale },
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 命令式确认 / 提示 API。
|
|
209
|
+
* - rsConfirm(options) 基础调用
|
|
210
|
+
* - rsConfirm.warning / .danger / .confirm 双按钮确认
|
|
211
|
+
* - rsConfirm.info / .success / .error 默认单按钮提示(showCancel: false)
|
|
212
|
+
*/
|
|
213
|
+
export const rsConfirm: RsConfirmApi = Object.assign(rsConfirmCore, {
|
|
214
|
+
warning: (options?: RsConfirmInput) =>
|
|
215
|
+
rsConfirmCore(
|
|
216
|
+
normalizeConfirmInput(options, {
|
|
217
|
+
tone: 'danger',
|
|
218
|
+
confirmVariant: 'danger',
|
|
219
|
+
showOverlay: true,
|
|
220
|
+
}),
|
|
221
|
+
),
|
|
222
|
+
danger: (options?: RsConfirmInput) =>
|
|
223
|
+
rsConfirmCore(
|
|
224
|
+
normalizeConfirmInput(options, {
|
|
225
|
+
tone: 'danger',
|
|
226
|
+
confirmVariant: 'danger',
|
|
227
|
+
showOverlay: true,
|
|
228
|
+
}),
|
|
229
|
+
),
|
|
230
|
+
info: (options?: RsConfirmInput) =>
|
|
231
|
+
rsConfirmCore(
|
|
232
|
+
normalizeConfirmInput(options, {
|
|
233
|
+
tone: 'info',
|
|
234
|
+
confirmVariant: 'primary',
|
|
235
|
+
showCancel: false,
|
|
236
|
+
showOverlay: true,
|
|
237
|
+
}),
|
|
238
|
+
),
|
|
239
|
+
success: (options?: RsConfirmInput) =>
|
|
240
|
+
rsConfirmCore(
|
|
241
|
+
normalizeConfirmInput(options, {
|
|
242
|
+
tone: 'success',
|
|
243
|
+
confirmVariant: 'primary',
|
|
244
|
+
showCancel: false,
|
|
245
|
+
showOverlay: true,
|
|
246
|
+
}),
|
|
247
|
+
),
|
|
248
|
+
error: (options?: RsConfirmInput) =>
|
|
249
|
+
rsConfirmCore(
|
|
250
|
+
normalizeConfirmInput(options, {
|
|
251
|
+
tone: 'danger',
|
|
252
|
+
confirmVariant: 'danger',
|
|
253
|
+
showCancel: false,
|
|
254
|
+
showOverlay: true,
|
|
255
|
+
}),
|
|
256
|
+
),
|
|
257
|
+
confirm: (options?: RsConfirmInput) =>
|
|
258
|
+
rsConfirmCore(
|
|
259
|
+
normalizeConfirmInput(options, {
|
|
260
|
+
tone: 'warning',
|
|
261
|
+
confirmVariant: 'primary',
|
|
262
|
+
showOverlay: true,
|
|
263
|
+
}),
|
|
264
|
+
),
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 命令式打开 RsDialog;适合临时表单/说明层。
|
|
269
|
+
* 确认/危险提示请用 rsConfirm,不要传 layout: 'confirm'。
|
|
270
|
+
* 复杂长期状态仍建议声明式使用组件。
|
|
271
|
+
*/
|
|
272
|
+
export function openRsDialog(options: RsDialogOpenOptions = {}): RsDialogHandle {
|
|
273
|
+
const open = ref(true)
|
|
274
|
+
let destroyed = false
|
|
275
|
+
let dialogExpose: { close?: (reason?: RsDialogCloseReason) => Promise<boolean> } | null = null
|
|
276
|
+
const layout = options.layout ?? 'window'
|
|
277
|
+
|
|
278
|
+
if (import.meta.env.DEV && layout === 'confirm') {
|
|
279
|
+
console.warn(
|
|
280
|
+
'[openRsDialog] layout:"confirm" 已弃用。确认/提示请使用 rsConfirm / RsConfirmDialog;本 API 仅用于工作窗/表单。',
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const { destroy: unmount } = mountHost(
|
|
285
|
+
() =>
|
|
286
|
+
h(
|
|
287
|
+
RsDialog,
|
|
288
|
+
{
|
|
289
|
+
ref: (instance: unknown) => {
|
|
290
|
+
dialogExpose = instance as typeof dialogExpose
|
|
291
|
+
},
|
|
292
|
+
open: open.value,
|
|
293
|
+
'onUpdate:open': (value: boolean) => {
|
|
294
|
+
open.value = value
|
|
295
|
+
if (!value && !destroyed) {
|
|
296
|
+
window.setTimeout(() => destroy(), 200)
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
title: options.title,
|
|
300
|
+
description:
|
|
301
|
+
[options.subtitle, options.description].filter(Boolean).join('\n\n') ||
|
|
302
|
+
options.description,
|
|
303
|
+
width: options.width ?? 'md',
|
|
304
|
+
tone: options.tone,
|
|
305
|
+
layout,
|
|
306
|
+
resizable: layout === 'window',
|
|
307
|
+
fullscreenable: layout === 'window',
|
|
308
|
+
showOverlay: options.showOverlay ?? true,
|
|
309
|
+
overlayOpacity: options.overlayOpacity,
|
|
310
|
+
overlayBlur: options.overlayBlur,
|
|
311
|
+
showClose: options.showClose,
|
|
312
|
+
closeOnOverlayClick: options.closeOnOverlayClick,
|
|
313
|
+
closeOnEsc: options.closeOnEsc,
|
|
314
|
+
showFooter: options.showFooter ?? true,
|
|
315
|
+
showCancel: options.showCancel,
|
|
316
|
+
showConfirm: options.showConfirm,
|
|
317
|
+
cancelText: options.cancelText,
|
|
318
|
+
confirmText: options.confirmText,
|
|
319
|
+
confirmVariant: options.confirmVariant,
|
|
320
|
+
autoCloseOnConfirm: options.autoCloseOnConfirm ?? true,
|
|
321
|
+
beforeClose: options.beforeClose,
|
|
322
|
+
teleportTo: options.teleportTo,
|
|
323
|
+
deferBodyMount: false,
|
|
324
|
+
onConfirm: () => {
|
|
325
|
+
void options.onConfirm?.()
|
|
326
|
+
},
|
|
327
|
+
onCancel: () => {
|
|
328
|
+
options.onCancel?.()
|
|
329
|
+
},
|
|
330
|
+
onAfterClose: (reason: RsDialogCloseReason) => {
|
|
331
|
+
options.onAfterClose?.(reason)
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
body: options.body,
|
|
336
|
+
footer: options.footer,
|
|
337
|
+
},
|
|
338
|
+
),
|
|
339
|
+
{ theme: options.theme, locale: options.locale },
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
function destroy(): void {
|
|
343
|
+
if (destroyed) return
|
|
344
|
+
destroyed = true
|
|
345
|
+
unmount()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
async close(reason: RsDialogCloseReason = 'programmatic') {
|
|
350
|
+
if (destroyed) return false
|
|
351
|
+
if (dialogExpose?.close) return dialogExpose.close(reason)
|
|
352
|
+
open.value = false
|
|
353
|
+
return true
|
|
354
|
+
},
|
|
355
|
+
destroy,
|
|
356
|
+
}
|
|
357
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将 Vue emit 适配为 useRsTableEditActions 所需的 edit emit 袋。
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EmitFn } from 'vue'
|
|
6
|
+
import type { RsTableEmits } from '../components/table/rs-table-props'
|
|
7
|
+
import type { RsTableRowData } from '../components/table-utils'
|
|
8
|
+
import type { UseRsTableEditActionsOptions } from './useRsTableEditActions'
|
|
9
|
+
|
|
10
|
+
type EditEmitBag<T extends RsTableRowData> = UseRsTableEditActionsOptions<T>['emit']
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 创建编辑 emit 桥(避免 RsTable 内联一长串箭头函数)。
|
|
14
|
+
*
|
|
15
|
+
* 入参用 Vue `EmitFn<RsTableEmits>`,与 `defineEmits` 产物对齐;
|
|
16
|
+
* 勿用手写 overload 接口承接(交叉重载在参数位逆变,会报 cellView ̸∈ update:columnOrder)。
|
|
17
|
+
*/
|
|
18
|
+
export function createRsTableEditEmitBridge<T extends RsTableRowData>(
|
|
19
|
+
emit: EmitFn<RsTableEmits<T>>,
|
|
20
|
+
): EditEmitBag<T> {
|
|
21
|
+
return {
|
|
22
|
+
cellView: (row, column, index) => emit('cellView', row, column, index),
|
|
23
|
+
cellEditStart: (row, column, index) => emit('cellEditStart', row, column, index),
|
|
24
|
+
cellEditDialog: (row, column, index, draft) =>
|
|
25
|
+
emit('cellEditDialog', row, column, index, draft),
|
|
26
|
+
cellEditCommit: (row, column, index, value, previous) =>
|
|
27
|
+
emit('cellEditCommit', row, column, index, value, previous),
|
|
28
|
+
cellEditCancel: (row, column, index) => emit('cellEditCancel', row, column, index),
|
|
29
|
+
cellEditInvalid: (row, column, index, message, value) =>
|
|
30
|
+
emit('cellEditInvalid', row, column, index, message, value),
|
|
31
|
+
cellEditBatchCommit: (column, changes) => emit('cellEditBatchCommit', column, changes),
|
|
32
|
+
cellEditUndo: (entry) => emit('cellEditUndo', entry),
|
|
33
|
+
cellEditRedo: (entry) => emit('cellEditRedo', entry),
|
|
34
|
+
cellEditReject: (row, index, reason) => emit('cellEditReject', row, index, reason),
|
|
35
|
+
rowEditCommit: (row, index, changes) => emit('rowEditCommit', row, index, changes),
|
|
36
|
+
rowEditRollback: (row, index) => emit('rowEditRollback', row, index),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { inject, type InjectionKey, type ShallowRef } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type RsLoadingBarApi = {
|
|
4
|
+
start: () => void
|
|
5
|
+
finish: () => void
|
|
6
|
+
error: () => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const RS_LOADING_BAR_KEY: InjectionKey<ShallowRef<RsLoadingBarApi | null>> =
|
|
10
|
+
Symbol('rs-loading-bar')
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* useRsLoadingBar 读取最近的 RsLoadingBar 实例 API。
|
|
14
|
+
* 调用方须位于 RsLoadingBar 默认插槽的后代树中;
|
|
15
|
+
* 未挂载或未包裹时返回空操作实现,避免调用方崩溃。
|
|
16
|
+
*/
|
|
17
|
+
export function useRsLoadingBar(): RsLoadingBarApi {
|
|
18
|
+
const injected = inject(RS_LOADING_BAR_KEY, null)
|
|
19
|
+
const noop: RsLoadingBarApi = {
|
|
20
|
+
start() {},
|
|
21
|
+
finish() {},
|
|
22
|
+
error() {},
|
|
23
|
+
}
|
|
24
|
+
if (!injected) return noop
|
|
25
|
+
return {
|
|
26
|
+
start() {
|
|
27
|
+
injected.value?.start()
|
|
28
|
+
},
|
|
29
|
+
finish() {
|
|
30
|
+
injected.value?.finish()
|
|
31
|
+
},
|
|
32
|
+
error() {
|
|
33
|
+
injected.value?.error()
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTable 公开面编排(对标 AG Grid api / TanStack table instance)。
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - FeatureHost:全局模块 + 实例 features 生命周期与贡献袋
|
|
6
|
+
* - RsTableApi:统一 create / provide,避免 expose 与 inject 漂移
|
|
7
|
+
*
|
|
8
|
+
* 内核装配(Columns / Engine / Virtual)见 useRsTableCore;本文件保持公开面轻量。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { inject, provide, type InjectionKey } from 'vue'
|
|
12
|
+
import type { RsContextMenuItem } from '../components/context-menu-utils'
|
|
13
|
+
import type { RsTableRowData } from '../components/table-utils'
|
|
14
|
+
import {
|
|
15
|
+
createRsTableApi,
|
|
16
|
+
RS_TABLE_API_KEY,
|
|
17
|
+
type RsTableApi,
|
|
18
|
+
} from '../components/table/rs-table-api'
|
|
19
|
+
import {
|
|
20
|
+
createRsTableFeatureHost,
|
|
21
|
+
type RsTableFeatureHost,
|
|
22
|
+
type RsTableOverlayContribution,
|
|
23
|
+
type RsTableToolbarItem,
|
|
24
|
+
} from '../components/table/rs-table-feature-host'
|
|
25
|
+
import { resolveInstanceFeatures } from '../components/table/rs-table-module-registry'
|
|
26
|
+
import {
|
|
27
|
+
createBuiltinTableFeatures,
|
|
28
|
+
type RsTableBuiltinFeatureId,
|
|
29
|
+
type RsTableFeature,
|
|
30
|
+
} from '../components/table/table-features'
|
|
31
|
+
import type { RsTableAnalyticsSnapshot } from './useRsTableSelectionSource'
|
|
32
|
+
|
|
33
|
+
export type { RsTableApi }
|
|
34
|
+
export { RS_TABLE_API_KEY, RS_TABLE_API_VERSION, isRsTableApi } from '../components/table/rs-table-api'
|
|
35
|
+
export { RsTableModuleRegistry, resolveInstanceFeatures } from '../components/table/rs-table-module-registry'
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* useRsTable 返回的公开面:Feature 宿主 + API 工厂。
|
|
39
|
+
*/
|
|
40
|
+
export interface UseRsTableSurface<T extends RsTableRowData = RsTableRowData> {
|
|
41
|
+
featureHost: RsTableFeatureHost<T>
|
|
42
|
+
/**
|
|
43
|
+
* 绑定内置 id + 实例/全局 features;返回 dispose。
|
|
44
|
+
* 应在 watch(immediate) 中调用,替换旧 dispose。
|
|
45
|
+
*/
|
|
46
|
+
bindFeatures: (options: {
|
|
47
|
+
builtinIds: RsTableBuiltinFeatureId[]
|
|
48
|
+
instanceFeatures?: RsTableFeature<T>[] | null
|
|
49
|
+
getViewRows: () => T[]
|
|
50
|
+
getSelectedRows: () => T[]
|
|
51
|
+
getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
|
|
52
|
+
subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
|
|
53
|
+
}) => () => void
|
|
54
|
+
/** 合并 props 自定义菜单与 feature 贡献 */
|
|
55
|
+
mergeContextMenuItems: (
|
|
56
|
+
baseItems: RsContextMenuItem[],
|
|
57
|
+
menuCtx: { row: T | null; selectedRows: T[] },
|
|
58
|
+
) => RsContextMenuItem[]
|
|
59
|
+
/** 由内部实现装配冻结 API,并可选 provide */
|
|
60
|
+
createApi: (
|
|
61
|
+
impl: Omit<RsTableApi<T>, 'apiVersion' | 'getActiveFeatureIds'> & {
|
|
62
|
+
getActiveFeatureIds?: () => string[]
|
|
63
|
+
},
|
|
64
|
+
options?: { provide?: boolean },
|
|
65
|
+
) => RsTableApi<T>
|
|
66
|
+
getActiveFeatureIds: () => string[]
|
|
67
|
+
/** 实例级 toolbar 贡献(多表互不共享) */
|
|
68
|
+
getToolbarItems: () => RsTableToolbarItem[]
|
|
69
|
+
/** 实例级 overlay 贡献 */
|
|
70
|
+
getOverlayContributions: () => RsTableOverlayContribution[]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 创建表格公开面(每个 RsTable 实例调用一次)。
|
|
75
|
+
*/
|
|
76
|
+
export function useRsTable<T extends RsTableRowData = RsTableRowData>(): UseRsTableSurface<T> {
|
|
77
|
+
const featureHost = createRsTableFeatureHost<T>()
|
|
78
|
+
|
|
79
|
+
function bindFeatures(options: {
|
|
80
|
+
builtinIds: RsTableBuiltinFeatureId[]
|
|
81
|
+
instanceFeatures?: RsTableFeature<T>[] | null
|
|
82
|
+
getViewRows: () => T[]
|
|
83
|
+
getSelectedRows: () => T[]
|
|
84
|
+
getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
|
|
85
|
+
subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
|
|
86
|
+
}): () => void {
|
|
87
|
+
// 合并顺序:全局模块 → 内置 → 实例 features(同 id 后者覆盖)
|
|
88
|
+
const features = resolveInstanceFeatures<T>([
|
|
89
|
+
...createBuiltinTableFeatures<T>(options.builtinIds),
|
|
90
|
+
...(options.instanceFeatures ?? []),
|
|
91
|
+
])
|
|
92
|
+
const ctx = featureHost.createContext({
|
|
93
|
+
getViewRows: options.getViewRows,
|
|
94
|
+
getSelectedRows: options.getSelectedRows,
|
|
95
|
+
getAnalyticsSnapshot: options.getAnalyticsSnapshot,
|
|
96
|
+
subscribeAnalytics: options.subscribeAnalytics,
|
|
97
|
+
})
|
|
98
|
+
return featureHost.setup(features, ctx)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function createApi(
|
|
102
|
+
impl: Omit<RsTableApi<T>, 'apiVersion' | 'getActiveFeatureIds'> & {
|
|
103
|
+
getActiveFeatureIds?: () => string[]
|
|
104
|
+
},
|
|
105
|
+
options?: { provide?: boolean },
|
|
106
|
+
): RsTableApi<T> {
|
|
107
|
+
const api = createRsTableApi<T>({
|
|
108
|
+
...impl,
|
|
109
|
+
getActiveFeatureIds: impl.getActiveFeatureIds ?? (() => featureHost.getActiveFeatureIds()),
|
|
110
|
+
})
|
|
111
|
+
if (options?.provide !== false) {
|
|
112
|
+
provide(RS_TABLE_API_KEY as InjectionKey<RsTableApi<T>>, api)
|
|
113
|
+
}
|
|
114
|
+
return api
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
featureHost,
|
|
119
|
+
bindFeatures,
|
|
120
|
+
mergeContextMenuItems: (base, menuCtx) => featureHost.mergeContextMenuItems(base, menuCtx),
|
|
121
|
+
createApi,
|
|
122
|
+
getActiveFeatureIds: () => featureHost.getActiveFeatureIds(),
|
|
123
|
+
getToolbarItems: () => featureHost.getToolbarItems(),
|
|
124
|
+
getOverlayContributions: () => featureHost.getOverlayContributions(),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 子树内注入表格 API(插槽/子组件用)。
|
|
130
|
+
* 未 provide 时返回 undefined。
|
|
131
|
+
*/
|
|
132
|
+
export function useRsTableApi<T extends RsTableRowData = RsTableRowData>(): RsTableApi<T> | undefined {
|
|
133
|
+
return inject(RS_TABLE_API_KEY as InjectionKey<RsTableApi<T>>, undefined)
|
|
134
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTable 无障碍基线:region / grid 语义与 loading busy。
|
|
3
|
+
*
|
|
4
|
+
* 与 useRsTableGridKeyboard + 单元格 role=gridcell 配合,形成可漫游网格。
|
|
5
|
+
* tabindex 放在 role=grid 的 table 上(复合控件可聚焦),勿放在 region 壳层。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { computed, type ComputedRef, type Ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
export interface UseRsTableA11yOptions {
|
|
11
|
+
/** 显式无障碍名称;空则回退 defaultLabel */
|
|
12
|
+
ariaLabel: () => string | undefined
|
|
13
|
+
defaultLabel: () => string
|
|
14
|
+
loading: () => boolean
|
|
15
|
+
/** 业务行数(view / dataRows) */
|
|
16
|
+
rowCount: Ref<number> | ComputedRef<number> | (() => number)
|
|
17
|
+
/** 可见数据列数 */
|
|
18
|
+
colCount: Ref<number> | ComputedRef<number> | (() => number)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function read(source: Ref<number> | ComputedRef<number> | (() => number)): number {
|
|
22
|
+
return typeof source === 'function' ? source() : source.value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 壳层 / table 元素无障碍属性。
|
|
27
|
+
*/
|
|
28
|
+
export function useRsTableA11y(options: UseRsTableA11yOptions) {
|
|
29
|
+
const resolvedLabel = computed(
|
|
30
|
+
() => options.ariaLabel()?.trim() || options.defaultLabel(),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const shellA11y = computed(() => ({
|
|
34
|
+
role: 'region' as const,
|
|
35
|
+
'aria-label': resolvedLabel.value,
|
|
36
|
+
'aria-busy': options.loading() ? true : undefined,
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
const tableA11y = computed(() => ({
|
|
40
|
+
role: 'grid' as const,
|
|
41
|
+
tabindex: 0,
|
|
42
|
+
'aria-rowcount': Math.max(0, read(options.rowCount)),
|
|
43
|
+
'aria-colcount': Math.max(0, read(options.colCount)),
|
|
44
|
+
'aria-label': resolvedLabel.value,
|
|
45
|
+
}))
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
resolvedLabel,
|
|
49
|
+
shellA11y,
|
|
50
|
+
tableA11y,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 表格图表桥:订阅 SelectionSource,产出库无关 chart series。
|
|
3
|
+
*
|
|
4
|
+
* 业务页可:
|
|
5
|
+
* - 把 series 丢给 ECharts option 组装
|
|
6
|
+
* - 或在图表点击时用 point.key 反查行做联动筛选
|
|
7
|
+
*
|
|
8
|
+
* 性能:series 随 sourceRows 浅引用变化重算;defs 建议业务侧稳定引用。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { computed, type ComputedRef, type Ref } from 'vue'
|
|
12
|
+
import type { RsTableRowData } from '../components/table-utils'
|
|
13
|
+
import {
|
|
14
|
+
buildTableChartSeriesList,
|
|
15
|
+
type RsTableChartSeries,
|
|
16
|
+
type RsTableChartSeriesDef,
|
|
17
|
+
} from '../components/table/table-chart-utils'
|
|
18
|
+
import {
|
|
19
|
+
useRsTableSelectionSource,
|
|
20
|
+
type RsTableAnalyticsSnapshot,
|
|
21
|
+
} from './useRsTableSelectionSource'
|
|
22
|
+
|
|
23
|
+
export interface UseRsTableChartBridgeOptions<T extends RsTableRowData> {
|
|
24
|
+
viewRows: Ref<T[]> | ComputedRef<T[]>
|
|
25
|
+
selectedRows?: Ref<T[]> | ComputedRef<T[]>
|
|
26
|
+
/** 系列定义列表;变化会触发重算 */
|
|
27
|
+
seriesDefs: Ref<RsTableChartSeriesDef[]> | ComputedRef<RsTableChartSeriesDef[]> | RsTableChartSeriesDef[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readDefs(
|
|
31
|
+
defs:
|
|
32
|
+
| Ref<RsTableChartSeriesDef[]>
|
|
33
|
+
| ComputedRef<RsTableChartSeriesDef[]>
|
|
34
|
+
| RsTableChartSeriesDef[],
|
|
35
|
+
): RsTableChartSeriesDef[] {
|
|
36
|
+
if (Array.isArray(defs)) return defs
|
|
37
|
+
return defs.value ?? []
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 选中联动图表桥。
|
|
42
|
+
*
|
|
43
|
+
* @returns selection 源 + series 列表 + getSnapshot
|
|
44
|
+
*/
|
|
45
|
+
export function useRsTableChartBridge<T extends RsTableRowData>(
|
|
46
|
+
options: UseRsTableChartBridgeOptions<T>,
|
|
47
|
+
) {
|
|
48
|
+
const selection = useRsTableSelectionSource<T>({
|
|
49
|
+
viewRows: options.viewRows,
|
|
50
|
+
selectedRows: options.selectedRows,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const series = computed<RsTableChartSeries[]>(() =>
|
|
54
|
+
buildTableChartSeriesList(selection.sourceRows.value, readDefs(options.seriesDefs)),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
/** 按 id 取单系列,便于模板绑定 */
|
|
58
|
+
function getSeriesById(id: string): RsTableChartSeries | undefined {
|
|
59
|
+
return series.value.find((item) => item.id === id)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getSnapshot(): RsTableAnalyticsSnapshot<T> & { series: RsTableChartSeries[] } {
|
|
63
|
+
return {
|
|
64
|
+
...selection.getSnapshot(),
|
|
65
|
+
series: series.value,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
...selection,
|
|
71
|
+
series,
|
|
72
|
+
getSeriesById,
|
|
73
|
+
getSnapshot,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type RsTableChartBridgeApi<T extends RsTableRowData> = ReturnType<
|
|
78
|
+
typeof useRsTableChartBridge<T>
|
|
79
|
+
>
|