@oneflowui/ui 0.8.4 → 0.8.6

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 (36) hide show
  1. package/README.en.md +296 -0
  2. package/README.md +296 -0
  3. package/dist/components/ai/AiMessageList.vue.js +1 -1
  4. package/dist/components/ai/AiMessageList.vue2.js +28 -27
  5. package/dist/components/common/ThemeScope.vue.d.ts +24 -0
  6. package/dist/components/common/ThemeScope.vue.js +24 -0
  7. package/dist/components/common/ThemeScope.vue2.js +4 -0
  8. package/dist/components/common/ThemeScopeScene.vue.d.ts +31 -0
  9. package/dist/components/common/ThemeScopeScene.vue.js +7 -0
  10. package/dist/components/common/ThemeScopeScene.vue2.js +56 -0
  11. package/dist/components/common/index.d.ts +2 -0
  12. package/dist/components/database/DatabaseView.vue.d.ts +4 -4
  13. package/dist/components/database/DatabaseView.vue.js +4 -4
  14. package/dist/components/database/DatabaseView.vue2.js +135 -134
  15. package/dist/components/database/index.d.ts +1 -1
  16. package/dist/components/kanban/KanbanColumn.vue.js +3 -3
  17. package/dist/components/kanban/KanbanColumn.vue2.js +31 -30
  18. package/dist/components/table/DataTable.vue.js +2 -2
  19. package/dist/components/table/DataTable.vue2.js +251 -249
  20. package/dist/composables/index.d.ts +5 -3
  21. package/dist/composables/useDataTableLayout.d.ts +1 -0
  22. package/dist/composables/useDataTableLayout.js +29 -26
  23. package/dist/composables/useDatabaseView.d.ts +1 -1
  24. package/dist/composables/useDatabaseView.js +311 -284
  25. package/dist/composables/useDatabaseViewMiddleware.d.ts +56 -0
  26. package/dist/composables/useDatabaseViewMiddleware.js +158 -0
  27. package/dist/composables/useVirtualList.d.ts +11 -0
  28. package/dist/composables/useVirtualList.js +146 -104
  29. package/dist/composables.js +74 -0
  30. package/dist/contracts/database.d.ts +23 -0
  31. package/dist/index.d.ts +6 -3
  32. package/dist/index.js +267 -253
  33. package/dist/style.css +1 -1
  34. package/dist/theme.d.ts +1 -0
  35. package/dist/theme.js +4 -0
  36. package/package.json +16 -2
package/README.en.md CHANGED
@@ -110,6 +110,19 @@ import '@oneflowui/ui/styles'
110
110
 
111
111
  Note: starting from `0.5.4`, the plugin entry is separated from the root entry. Use `@oneflowui/ui/plugin` for `app.use(...)`, and keep named imports on `@oneflowui/ui`.
112
112
 
113
+ If you want a stricter import boundary, stable subpath exports are also available:
114
+
115
+ ```ts
116
+ import { useVirtualListStateCache } from '@oneflowui/ui/composables'
117
+ import type { DataRecord } from '@oneflowui/ui/types'
118
+ ```
119
+
120
+ Recommended convention:
121
+ - Import components and common capabilities from the root `@oneflowui/ui` entry.
122
+ - Import composables and pure types from `@oneflowui/ui/composables` and `@oneflowui/ui/types` when you want a clearer dependency boundary.
123
+ - If you only want tokens and theme layers without plugin registration, use `@oneflowui/ui/theme`.
124
+ - Keep `@oneflowui/ui/styles` as the full legacy-compatible style entry.
125
+
113
126
  ### Theme Layers
114
127
 
115
128
  OneUI now ships with a neutral default theme and an optional product skin without changing component logic.
@@ -127,8 +140,103 @@ document.documentElement.dataset.ofTheme = 'neutral'
127
140
  document.documentElement.dataset.ofTheme = 'ops-console'
128
141
  ```
129
142
 
143
+ If you want to decouple “style injection” from “plugin registration”, prefer the dedicated theme entry:
144
+
145
+ ```ts
146
+ import '@oneflowui/ui/theme'
147
+ ```
148
+
130
149
  This structure is meant to keep the component layer reusable while letting product-specific styling live above it.
131
150
 
151
+ ### ThemeScope Wrapper
152
+
153
+ When a single page needs two visual contexts at once, prefer the `ThemeScope` component. It automatically writes `data-of-theme` and `data-of-theme-scope` on the wrapper, so consumers do not need to hand-write attributes.
154
+
155
+ ```vue
156
+ <script setup lang="ts">
157
+ import { DataTable, ThemeScope } from '@oneflowui/ui'
158
+ </script>
159
+
160
+ <template>
161
+ <ThemeScope theme="ops-console" tag="section" class="ops-preview">
162
+ <div class="ops-preview__panel">
163
+ <h3>Scoped ops-console preview</h3>
164
+ <p>This subtree inherits ops-console tokens while the outer page stays neutral.</p>
165
+ </div>
166
+ </ThemeScope>
167
+ </template>
168
+ ```
169
+
170
+ Supported values currently match the global theme list:
171
+
172
+ - `neutral`
173
+ - `ops-console`
174
+
175
+ Direct `data-of-theme-scope` usage is still compatible for older code, but new consumers should use `ThemeScope`.
176
+
177
+ ### ThemeScope Scene Component
178
+
179
+ If you want to reuse "local theme + title + description + meta/footer slots" together, use `ThemeScopeScene`. It is a lightweight scene shell component that fits enterprise back offices, ops consoles, and task centers.
180
+
181
+ ```vue
182
+ <script setup lang="ts">
183
+ import { DataTable, ThemeScopeScene } from '@oneflowui/ui'
184
+ </script>
185
+
186
+ <template>
187
+ <ThemeScopeScene
188
+ theme="ops-console"
189
+ tag="section"
190
+ eyebrow="Enterprise Scene"
191
+ title="Task Overview"
192
+ description="This region automatically receives ops-console tokens."
193
+ >
194
+ <template #meta>
195
+ <span class="scene-chip">ThemeScopeScene</span>
196
+ </template>
197
+
198
+ <DataTable :rows="rows" :columns="columns" />
199
+
200
+ <template #footer>
201
+ <small>footer / notes / actions</small>
202
+ </template>
203
+ </ThemeScopeScene>
204
+ </template>
205
+ ```
206
+
207
+ `ThemeScopeScene` still reuses `ThemeScope` under the hood, so this is non-breaking. It just packages the common business shell into a reusable component.
208
+
209
+ ### Virtual List State Cache
210
+
211
+ If a virtual list remounts often and you want to keep `scrollTop`, `containerHeight`, and `invalidateVersion`, reuse the same state by key.
212
+ `createVirtualListState()` is still available, while `useVirtualListStateCache()` is the helper for sharing one state across remounts.
213
+
214
+ ```ts
215
+ import { useVirtualList, useVirtualListStateCache } from '@oneflowui/ui'
216
+
217
+ const virtualListState = useVirtualListStateCache('ai-message-list')
218
+
219
+ const { visibleItems, totalHeight, offsetY } = useVirtualList({
220
+ items: messages,
221
+ itemHeight: 60,
222
+ containerRef,
223
+ state: virtualListState,
224
+ })
225
+ ```
226
+
227
+ ## Release & Verification
228
+
229
+ If you need to review the current traceable release materials, start with these docs:
230
+
231
+ - Current evidence index: [`docs/plans/2026-03-22-release-0.8.6-evidence-index.md`](docs/plans/2026-03-22-release-0.8.6-evidence-index.md)
232
+ - Release proof: [`docs/plans/2026-03-22-release-0.8.6-proof.md`](docs/plans/2026-03-22-release-0.8.6-proof.md)
233
+ - Verification result: [`docs/plans/2026-03-22-release-0.8.6-verification.md`](docs/plans/2026-03-22-release-0.8.6-verification.md)
234
+ - Pre-release smoke: [`docs/plans/2026-03-22-oneui-theme-scope-middleware-composer-pre-release-verification.md`](docs/plans/2026-03-22-oneui-theme-scope-middleware-composer-pre-release-verification.md)
235
+ - Package entrypoints verification: [`docs/plans/2026-03-22-oneui-package-entrypoints-verification.md`](docs/plans/2026-03-22-oneui-package-entrypoints-verification.md)
236
+ - Changelog: [`docs/CHANGELOG-v0.8.6.md`](docs/CHANGELOG-v0.8.6.md)
237
+
238
+ The current public npm version should be checked against the latest release proof, and publish, pack, dry-run, and dual-host smoke checks all have separate evidence.
239
+
132
240
  ---
133
241
 
134
242
  ## Usage Examples
@@ -204,6 +312,194 @@ toast.success('Saved successfully')
204
312
  toast.error('Operation failed')
205
313
  ```
206
314
 
315
+ ### DatabaseView middleware presets
316
+
317
+ You can compose reusable `DatabaseView` middleware presets for toast, analytics, and optimistic updates:
318
+
319
+ ```ts
320
+ import {
321
+ composeDatabaseViewMiddlewares,
322
+ createDatabaseViewAnalyticsMiddleware,
323
+ createDatabaseViewPresetBundle,
324
+ createDatabaseViewPresetMiddleware,
325
+ createDatabaseViewOptimisticMiddleware,
326
+ createDatabaseViewToastMiddleware,
327
+ useDatabaseView,
328
+ } from '@oneflowui/ui'
329
+
330
+ const middleware = composeDatabaseViewMiddlewares(
331
+ createDatabaseViewToastMiddleware({
332
+ onSuccess: (message) => toast.success(message),
333
+ onError: (message) => toast.error(message),
334
+ }),
335
+ createDatabaseViewAnalyticsMiddleware({
336
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
337
+ }),
338
+ createDatabaseViewOptimisticMiddleware({
339
+ apply: ({ payload }) => updateLocalRecord(payload),
340
+ revert: ({ payload }) => revertLocalRecord(payload),
341
+ }),
342
+ )
343
+
344
+ const view = useDatabaseView({
345
+ tableId: 'tbl-1',
346
+ actions: {
347
+ middleware,
348
+ onCellEdit: saveCellEdit,
349
+ },
350
+ })
351
+ ```
352
+
353
+ If you prefer a single factory that returns a ready-to-use middleware, use the official preset bundle helper:
354
+
355
+ ```ts
356
+ const presetBundle = createDatabaseViewPresetBundle({
357
+ toast: {
358
+ onSuccess: (message) => toast.success(message),
359
+ onError: (message) => toast.error(message),
360
+ },
361
+ analytics: {
362
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
363
+ },
364
+ optimistic: {
365
+ apply: ({ payload }) => updateLocalRecord(payload),
366
+ revert: ({ payload }) => revertLocalRecord(payload),
367
+ },
368
+ })
369
+
370
+ const view = useDatabaseView({
371
+ tableId: 'tbl-1',
372
+ actions: {
373
+ middleware: presetBundle.middleware,
374
+ onCellEdit: saveCellEdit,
375
+ },
376
+ })
377
+ ```
378
+
379
+ `createDatabaseViewPresetMiddleware` is the one-liner version when you only need the final middleware object:
380
+
381
+ ```ts
382
+ const middleware = createDatabaseViewPresetMiddleware({
383
+ toast: {
384
+ onSuccess: (message) => toast.success(message),
385
+ onError: (message) => toast.error(message),
386
+ },
387
+ analytics: {
388
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
389
+ },
390
+ optimistic: {
391
+ apply: ({ payload }) => updateLocalRecord(payload),
392
+ revert: ({ payload }) => revertLocalRecord(payload),
393
+ },
394
+ })
395
+ ```
396
+
397
+ ### ThemeScope Scene Template
398
+
399
+ If you want to evolve `ThemeScope` from a local wrapper into a business scene template, use it as the page shell that organizes the region structure. This fits enterprise dashboards, ops consoles, and task boards: the outer shell fixes the visual context while the inner area hosts cards, tables, metrics, and side notes.
400
+
401
+ ```vue
402
+ <script setup lang="ts">
403
+ import { ThemeScope } from '@oneflowui/ui'
404
+ </script>
405
+
406
+ <template>
407
+ <ThemeScope theme="ops-console" tag="section" class="enterprise-scene">
408
+ <header class="enterprise-scene__header">
409
+ <h3>Enterprise Scene</h3>
410
+ <p>Theme, layout, and business content are reused together as one template.</p>
411
+ </header>
412
+
413
+ <div class="enterprise-scene__body">
414
+ <div class="enterprise-scene__summary">...</div>
415
+ <DataTable :rows="rows" :columns="columns" />
416
+ </div>
417
+ </ThemeScope>
418
+ </template>
419
+ ```
420
+
421
+ The repository's `src/dev/examples/database/DatabaseEnterpriseDemo.vue` is the dev/examples-level reference for this kind of scene template. It shows the "ThemeScopeScene + DatabaseView + middleware" composition pattern that you can copy into your own project and turn into a real enterprise page.
422
+
423
+ ### Dev Examples / Enterprise Demo
424
+
425
+ `src/dev/examples/database/DatabaseEnterpriseDemo.vue` is a dev/examples-level consumption pattern used to show a more complete enterprise-style page composition. It is documentation and development sample material, not an npm-exported component, and it does not change the public export surface of `@oneflowui/ui`.
426
+ The repository also ships `src/dev/examples/database/DatabasePresetDemo.vue` as a shorter official preset-bundle example, focused on direct `createDatabaseViewPresetBundle` / `actions.middleware` consumption.
427
+
428
+ If your business app needs something similar, copy the composition idea from the example and wire it to your own data source and action contract instead of depending on a separate production export.
429
+
430
+ ### Combined Consumption Example
431
+
432
+ If you want to use theme scoping, database action middleware, and virtual list state caching in the same business surface, keep them inside one wrapper component. This is the closest pattern to a real app page and the easiest one to copy.
433
+
434
+ ```vue
435
+ <script setup lang="ts">
436
+ import { ref } from 'vue'
437
+ import {
438
+ ThemeScope,
439
+ composeDatabaseViewMiddlewares,
440
+ createDatabaseViewAnalyticsMiddleware,
441
+ createDatabaseViewOptimisticMiddleware,
442
+ createDatabaseViewToastMiddleware,
443
+ useDatabaseView,
444
+ useVirtualList,
445
+ useVirtualListStateCache,
446
+ } from '@oneflowui/ui'
447
+
448
+ const containerRef = ref<HTMLElement | null>(null)
449
+ const virtualListState = useVirtualListStateCache('task-feed')
450
+
451
+ const middleware = composeDatabaseViewMiddlewares(
452
+ createDatabaseViewToastMiddleware({
453
+ onSuccess: (message) => toast.success(message),
454
+ onError: (message) => toast.error(message),
455
+ }),
456
+ createDatabaseViewAnalyticsMiddleware({
457
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
458
+ }),
459
+ createDatabaseViewOptimisticMiddleware({
460
+ apply: ({ payload }) => updateLocalRecord(payload),
461
+ revert: ({ payload }) => revertLocalRecord(payload),
462
+ }),
463
+ )
464
+
465
+ const view = useDatabaseView({
466
+ tableId: 'tbl-1',
467
+ actions: {
468
+ middleware,
469
+ onCellEdit: saveCellEdit,
470
+ },
471
+ })
472
+
473
+ const { visibleItems } = useVirtualList({
474
+ items: view.records,
475
+ itemHeight: 60,
476
+ containerRef,
477
+ state: virtualListState,
478
+ })
479
+
480
+ const columns = [
481
+ { key: 'title', label: 'Task', width: 'fill' },
482
+ { key: 'status', label: 'Status', width: 120 },
483
+ { key: 'priority', label: 'Priority', width: 100 },
484
+ ]
485
+ </script>
486
+
487
+ <template>
488
+ <ThemeScope theme="ops-console" tag="section" class="task-surface">
489
+ <header class="task-surface__header">
490
+ <h3>Task Overview</h3>
491
+ <p>The outer shell uses scoped ops-console tokens while actions, list rendering, and cached state share the same component entry points.</p>
492
+ </header>
493
+
494
+ <div ref="containerRef" class="task-surface__list">
495
+ <DataTable :rows="visibleItems" :columns="columns" />
496
+ </div>
497
+ </ThemeScope>
498
+ </template>
499
+ ```
500
+
501
+ These capabilities can be used independently or combined like above; all of them are non-breaking additions and do not require consumer API changes.
502
+
207
503
  ---
208
504
 
209
505
  ## Local Development
package/README.md CHANGED
@@ -88,6 +88,19 @@ app.use(OneflowUI)
88
88
  app.mount('#app')
89
89
  ```
90
90
 
91
+ 如果需要更细粒度的导入边界,可以直接使用稳定子路径入口:
92
+
93
+ ```ts
94
+ import { useVirtualListStateCache } from '@oneflowui/ui/composables'
95
+ import type { DataRecord } from '@oneflowui/ui/types'
96
+ ```
97
+
98
+ 推荐约定:
99
+ - 组件与公共能力优先从 `@oneflowui/ui` 根入口导入。
100
+ - composables 和纯类型如果希望导入意图更清晰,可分别从 `@oneflowui/ui/composables` 与 `@oneflowui/ui/types` 导入。
101
+ - 如果只想注入 token 和主题层,不想顺带注册插件,可改用 `@oneflowui/ui/theme`。
102
+ - 样式全量入口仍保留 `@oneflowui/ui/styles`,兼容现有消费方式。
103
+
91
104
  ### 按需引入
92
105
 
93
106
  ```ts
@@ -114,12 +127,107 @@ document.documentElement.dataset.ofTheme = 'neutral'
114
127
  document.documentElement.dataset.ofTheme = 'ops-console'
115
128
  ```
116
129
 
130
+ 如果你希望把“样式注入”和“组件插件注册”拆开,推荐改用更明确的主题入口:
131
+
132
+ ```ts
133
+ import '@oneflowui/ui/theme'
134
+ ```
135
+
117
136
  这套结构的目标是:
118
137
 
119
138
  1. 组件默认保持中性、可复用
120
139
  2. 业务系统通过主题皮肤注入品牌感或中控台气质
121
140
  3. 后续可继续扩展更多主题,而不需要修改组件 API
122
141
 
142
+ ### ThemeScope 包装组件
143
+
144
+ 如果同一页面里需要并存两种视觉语境,优先使用 `ThemeScope`。这个组件会自动给 wrapper 注入 `data-of-theme` 和 `data-of-theme-scope`,业务方不需要手写属性。
145
+
146
+ ```vue
147
+ <script setup lang="ts">
148
+ import { DataTable, ThemeScope } from '@oneflowui/ui'
149
+ </script>
150
+
151
+ <template>
152
+ <ThemeScope theme="ops-console" tag="section" class="ops-preview">
153
+ <div class="ops-preview__panel">
154
+ <h3>局部 ops-console 预览</h3>
155
+ <p>这里会继承 ops-console 的 token,而外层仍然保持全局 neutral。</p>
156
+ </div>
157
+ </ThemeScope>
158
+ </template>
159
+ ```
160
+
161
+ `ThemeScope` 当前支持的主题值与全局主题一致:
162
+
163
+ - `neutral`
164
+ - `ops-console`
165
+
166
+ 老代码里直接手写的 `data-of-theme-scope` 仍然兼容,但新代码优先使用组件入口。
167
+
168
+ ### ThemeScope 场景组件
169
+
170
+ 如果你想把“局部主题 + 标题 + 说明 + meta/footer 插槽”一起复用,直接用 `ThemeScopeScene`。它是一个轻量的场景壳层组件,适合企业后台、运营控制台、任务中心这类页面。
171
+
172
+ ```vue
173
+ <script setup lang="ts">
174
+ import { DataTable, ThemeScopeScene } from '@oneflowui/ui'
175
+ </script>
176
+
177
+ <template>
178
+ <ThemeScopeScene
179
+ theme="ops-console"
180
+ tag="section"
181
+ eyebrow="Enterprise Scene"
182
+ title="任务总览"
183
+ description="这个区域会自动获得 ops-console token。"
184
+ >
185
+ <template #meta>
186
+ <span class="scene-chip">ThemeScopeScene</span>
187
+ </template>
188
+
189
+ <DataTable :rows="rows" :columns="columns" />
190
+
191
+ <template #footer>
192
+ <small>footer / notes / actions</small>
193
+ </template>
194
+ </ThemeScopeScene>
195
+ </template>
196
+ ```
197
+
198
+ `ThemeScopeScene` 内部继续复用 `ThemeScope` 的 token 作用域,所以它仍然是 non-breaking 的增强,只是把常见业务壳层收成了一个可复用组件。
199
+
200
+ ### 虚拟列表状态缓存
201
+
202
+ 如果虚拟列表会频繁 remount,但你希望保留 `scrollTop`、`containerHeight` 和 `invalidateVersion`,可以按 key 复用同一份状态。
203
+ `createVirtualListState()` 仍然可用,而 `useVirtualListStateCache()` 适合把同一份状态挂在多个 remount 之间。
204
+
205
+ ```ts
206
+ import { useVirtualList, useVirtualListStateCache } from '@oneflowui/ui'
207
+
208
+ const virtualListState = useVirtualListStateCache('ai-message-list')
209
+
210
+ const { visibleItems, totalHeight, offsetY } = useVirtualList({
211
+ items: messages,
212
+ itemHeight: 60,
213
+ containerRef,
214
+ state: virtualListState,
215
+ })
216
+ ```
217
+
218
+ ## 发布与验收
219
+
220
+ 如果你需要核对当前可追溯的发布材料,优先看这几份文档:
221
+
222
+ - 当前版本证据索引:[`docs/plans/2026-03-22-release-0.8.6-evidence-index.md`](docs/plans/2026-03-22-release-0.8.6-evidence-index.md)
223
+ - 发布 proof:[`docs/plans/2026-03-22-release-0.8.6-proof.md`](docs/plans/2026-03-22-release-0.8.6-proof.md)
224
+ - 验收结果:[`docs/plans/2026-03-22-release-0.8.6-verification.md`](docs/plans/2026-03-22-release-0.8.6-verification.md)
225
+ - 预发布验证:[`docs/plans/2026-03-22-oneui-theme-scope-middleware-composer-pre-release-verification.md`](docs/plans/2026-03-22-oneui-theme-scope-middleware-composer-pre-release-verification.md)
226
+ - 子路径入口治理:[`docs/plans/2026-03-22-oneui-package-entrypoints-verification.md`](docs/plans/2026-03-22-oneui-package-entrypoints-verification.md)
227
+ - 版本日志:[`docs/CHANGELOG-v0.8.6.md`](docs/CHANGELOG-v0.8.6.md)
228
+
229
+ 当前 npm 公开版本以最新 release proof 为准,对应的发布、pack、dry-run 和双宿主 smoke 都有独立留痕。
230
+
123
231
  ---
124
232
 
125
233
  ## 页面级方案
@@ -175,6 +283,194 @@ type DatabaseViewActions = {
175
283
 
176
284
  如果接入的是 `provider` 模式,建议把 `onFetch` / `onRefresh` 作为必配项;如果接入的是 `local` 模式,则重点只需要保证 `onUpdateRecord`、`onCreateRecord`、`onDeleteRecord` 和 `onSaveView` 这几类页面动作可回传。
177
285
 
286
+ ### Middleware presets
287
+
288
+ 如果页面想复用 toast、分析埋点或乐观更新逻辑,可以直接组合 `useDatabaseView` 提供的 middleware presets:
289
+
290
+ ```ts
291
+ import {
292
+ composeDatabaseViewMiddlewares,
293
+ createDatabaseViewAnalyticsMiddleware,
294
+ createDatabaseViewPresetBundle,
295
+ createDatabaseViewPresetMiddleware,
296
+ createDatabaseViewOptimisticMiddleware,
297
+ createDatabaseViewToastMiddleware,
298
+ useDatabaseView,
299
+ } from '@oneflowui/ui'
300
+
301
+ const middleware = composeDatabaseViewMiddlewares(
302
+ createDatabaseViewToastMiddleware({
303
+ onSuccess: (message) => toast.success(message),
304
+ onError: (message) => toast.error(message),
305
+ }),
306
+ createDatabaseViewAnalyticsMiddleware({
307
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
308
+ }),
309
+ createDatabaseViewOptimisticMiddleware({
310
+ apply: ({ payload }) => updateLocalRecord(payload),
311
+ revert: ({ payload }) => revertLocalRecord(payload),
312
+ }),
313
+ )
314
+
315
+ const view = useDatabaseView({
316
+ tableId: 'tbl-1',
317
+ actions: {
318
+ middleware,
319
+ onCellEdit: saveCellEdit,
320
+ },
321
+ })
322
+ ```
323
+
324
+ 如果你更想要一个“一键拿到可直接消费的 middleware”的入口,可以优先用官方 preset 工厂:
325
+
326
+ ```ts
327
+ const presetBundle = createDatabaseViewPresetBundle({
328
+ toast: {
329
+ onSuccess: (message) => toast.success(message),
330
+ onError: (message) => toast.error(message),
331
+ },
332
+ analytics: {
333
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
334
+ },
335
+ optimistic: {
336
+ apply: ({ payload }) => updateLocalRecord(payload),
337
+ revert: ({ payload }) => revertLocalRecord(payload),
338
+ },
339
+ })
340
+
341
+ const view = useDatabaseView({
342
+ tableId: 'tbl-1',
343
+ actions: {
344
+ middleware: presetBundle.middleware,
345
+ onCellEdit: saveCellEdit,
346
+ },
347
+ })
348
+ ```
349
+
350
+ `createDatabaseViewPresetBundle` 会把常用 preset 先组装好,再暴露给业务方按需复用;`createDatabaseViewPresetMiddleware` 则适合直接塞进 `actions.middleware`,用于最小接入,例如:
351
+
352
+ ```ts
353
+ const middleware = createDatabaseViewPresetMiddleware({
354
+ toast: {
355
+ onSuccess: (message) => toast.success(message),
356
+ onError: (message) => toast.error(message),
357
+ },
358
+ analytics: {
359
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
360
+ },
361
+ optimistic: {
362
+ apply: ({ payload }) => updateLocalRecord(payload),
363
+ revert: ({ payload }) => revertLocalRecord(payload),
364
+ },
365
+ })
366
+ ```
367
+
368
+ ### ThemeScope 场景模板
369
+
370
+ 如果你想把 `ThemeScope` 从“局部 wrapper”升级成“业务场景模板”,可以直接把它当成页面壳层来组织区域结构。这个模板适合企业后台、运营控制台、任务看板这类页面:外层固定视觉语境,内层再放业务卡片、数据表、统计块和侧边说明。
371
+
372
+ ```vue
373
+ <script setup lang="ts">
374
+ import { ThemeScope } from '@oneflowui/ui'
375
+ </script>
376
+
377
+ <template>
378
+ <ThemeScope theme="ops-console" tag="section" class="enterprise-scene">
379
+ <header class="enterprise-scene__header">
380
+ <h3>Enterprise Scene</h3>
381
+ <p>主题、布局和业务内容一起作为模板复用。</p>
382
+ </header>
383
+
384
+ <div class="enterprise-scene__body">
385
+ <div class="enterprise-scene__summary">...</div>
386
+ <DataTable :rows="rows" :columns="columns" />
387
+ </div>
388
+ </ThemeScope>
389
+ </template>
390
+ ```
391
+
392
+ 仓库里的 `src/dev/examples/database/DatabaseEnterpriseDemo.vue` 就是这一类场景组件的 dev/examples 级参考实现。它展示的是“ThemeScopeScene + DatabaseView + middleware”的组合方式,便于复制到自家项目里改造成真正的企业页。
393
+
394
+ ### Dev Examples / Enterprise Demo
395
+
396
+ `src/dev/examples/database/DatabaseEnterpriseDemo.vue` 是 dev/examples 级的消费范式,用来展示更完整的企业版页面组合方式。它属于开发示例和文档参考,不是 npm 包对外导出的组件,不会改变 `@oneflowui/ui` 的公开导出面。
397
+ 仓库同时提供 `src/dev/examples/database/DatabasePresetDemo.vue` 作为更短的 preset bundle 官方示例,重点展示 `createDatabaseViewPresetBundle` / `actions.middleware` 的直接消费方式。
398
+
399
+ 如果你在业务工程里需要类似的页面,建议直接复制示例里的组合思路,再按自己的数据源和动作契约接入,而不是依赖一个额外的生产级导出入口。
400
+
401
+ ### 组合消费示例
402
+
403
+ 如果你要在同一块业务区域里同时使用主题作用域、数据库动作中间件和虚拟列表状态缓存,可以把三者放进同一个组件壳层。这个写法更接近真实业务页,也最适合作为复制模板。
404
+
405
+ ```vue
406
+ <script setup lang="ts">
407
+ import { ref } from 'vue'
408
+ import {
409
+ ThemeScope,
410
+ composeDatabaseViewMiddlewares,
411
+ createDatabaseViewAnalyticsMiddleware,
412
+ createDatabaseViewOptimisticMiddleware,
413
+ createDatabaseViewToastMiddleware,
414
+ useDatabaseView,
415
+ useVirtualList,
416
+ useVirtualListStateCache,
417
+ } from '@oneflowui/ui'
418
+
419
+ const containerRef = ref<HTMLElement | null>(null)
420
+ const virtualListState = useVirtualListStateCache('task-feed')
421
+
422
+ const middleware = composeDatabaseViewMiddlewares(
423
+ createDatabaseViewToastMiddleware({
424
+ onSuccess: (message) => toast.success(message),
425
+ onError: (message) => toast.error(message),
426
+ }),
427
+ createDatabaseViewAnalyticsMiddleware({
428
+ onEvent: (event) => console.log('[db-view]', event.phase, event.action),
429
+ }),
430
+ createDatabaseViewOptimisticMiddleware({
431
+ apply: ({ payload }) => updateLocalRecord(payload),
432
+ revert: ({ payload }) => revertLocalRecord(payload),
433
+ }),
434
+ )
435
+
436
+ const view = useDatabaseView({
437
+ tableId: 'tbl-1',
438
+ actions: {
439
+ middleware,
440
+ onCellEdit: saveCellEdit,
441
+ },
442
+ })
443
+
444
+ const { visibleItems } = useVirtualList({
445
+ items: view.records,
446
+ itemHeight: 60,
447
+ containerRef,
448
+ state: virtualListState,
449
+ })
450
+
451
+ const columns = [
452
+ { key: 'title', label: '任务', width: 'fill' },
453
+ { key: 'status', label: '状态', width: 120 },
454
+ { key: 'priority', label: '优先级', width: 100 },
455
+ ]
456
+ </script>
457
+
458
+ <template>
459
+ <ThemeScope theme="ops-console" tag="section" class="task-surface">
460
+ <header class="task-surface__header">
461
+ <h3>任务总览</h3>
462
+ <p>外层是局部 ops-console token,内部动作、列表和状态缓存都复用同一套组件入口。</p>
463
+ </header>
464
+
465
+ <div ref="containerRef" class="task-surface__list">
466
+ <DataTable :rows="visibleItems" :columns="columns" />
467
+ </div>
468
+ </ThemeScope>
469
+ </template>
470
+ ```
471
+
472
+ 这三个能力可以独立使用,也可以像上面这样组合使用;它们都是非 breaking 增强,不要求业务方修改现有组件 API。
473
+
178
474
  ### Selected record / detail workspace
179
475
 
180
476
  当前 dev app 已经把“选中记录 -> detail workspace”这条链路接起来了:点击 table / kanban / gallery / timeline 中的条目,会把当前记录送入详情工作区,再由 `DetailLayout`、`PropPanel`、`CommentItem` 这组组件展示主内容、属性和活动记录。
@@ -1,7 +1,7 @@
1
1
  import o from "./AiMessageList.vue2.js";
2
2
  /* empty css */
3
3
  import t from "../../_virtual/_plugin-vue_export-helper.js";
4
- const a = /* @__PURE__ */ t(o, [["__scopeId", "data-v-d42e9b32"]]);
4
+ const a = /* @__PURE__ */ t(o, [["__scopeId", "data-v-76bed64f"]]);
5
5
  export {
6
6
  a as default
7
7
  };