@veltra/sheet 2.3.4 → 2.5.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 (129) hide show
  1. package/dist/index.d.ts +2 -17
  2. package/dist/index.js +2 -13
  3. package/dist/types/index.d.ts +1 -3
  4. package/package.json +5 -6
  5. package/src/index.ts +1 -27
  6. package/src/types/index.ts +0 -2
  7. package/dist/components/report/binding-badge.js +0 -112
  8. package/dist/components/report/designer/cell-coords.js +0 -46
  9. package/dist/components/report/designer/col-widths.js +0 -29
  10. package/dist/components/report/designer/conditional-rules/helpers.js +0 -139
  11. package/dist/components/report/designer/drop-highlight-overlay.js +0 -76
  12. package/dist/components/report/designer/float-panel.js +0 -422
  13. package/dist/components/report/designer/role.js +0 -17
  14. package/dist/components/report/designer/rule-preview.js +0 -33
  15. package/dist/components/report/designer/rule-row.js +0 -253
  16. package/dist/components/report/designer/rules-dialog.js +0 -118
  17. package/dist/components/report/designer/topology-overlay.js +0 -122
  18. package/dist/components/report/designer/topology.js +0 -64
  19. package/dist/components/report/designer/use-grid-overlay.js +0 -75
  20. package/dist/components/report/designer-hub.js +0 -177
  21. package/dist/components/report/field-panel-helpers.js +0 -33
  22. package/dist/components/report/field-panel.js +0 -132
  23. package/dist/components/report/filter-bar.js +0 -74
  24. package/dist/components/report/hub-connection-form.js +0 -145
  25. package/dist/components/report/hub-dataset-editor.js +0 -409
  26. package/dist/components/report/preview-grid-size.js +0 -32
  27. package/dist/components/report/report-designer.js +0 -393
  28. package/dist/components/report/report-viewer.js +0 -124
  29. package/dist/components/report/style.d.ts +0 -17
  30. package/dist/components/report/style.js +0 -18
  31. package/dist/components/report/style2.css +0 -874
  32. package/dist/components/report/use-report-designer.js +0 -386
  33. package/dist/components/report/use-report-viewer.js +0 -55
  34. package/dist/report/binding.d.ts +0 -83
  35. package/dist/report/binding.js +0 -334
  36. package/dist/report/connector.d.ts +0 -66
  37. package/dist/report/connector.js +0 -159
  38. package/dist/report/export-xlsx.d.ts +0 -13
  39. package/dist/report/export-xlsx.js +0 -13
  40. package/dist/report/filter-bar.d.ts +0 -10
  41. package/dist/report/filter-bar.js +0 -31
  42. package/dist/report/index.d.ts +0 -12
  43. package/dist/report/params.d.ts +0 -13
  44. package/dist/report/params.js +0 -61
  45. package/dist/report/render/aggregate.js +0 -169
  46. package/dist/report/render/builder.d.ts +0 -12
  47. package/dist/report/render/builder.js +0 -139
  48. package/dist/report/render/coordinate.d.ts +0 -25
  49. package/dist/report/render/coordinate.js +0 -733
  50. package/dist/report/render/index.d.ts +0 -14
  51. package/dist/report/render/index.js +0 -21
  52. package/dist/report/render/style-resolver.js +0 -50
  53. package/dist/report/render/template-index.d.ts +0 -24
  54. package/dist/report/render/template-index.js +0 -137
  55. package/dist/report/rules/evaluate.d.ts +0 -25
  56. package/dist/report/rules/evaluate.js +0 -82
  57. package/dist/report/rules/index.d.ts +0 -2
  58. package/dist/report/template.d.ts +0 -63
  59. package/dist/report/template.js +0 -83
  60. package/dist/report/types.d.ts +0 -85
  61. package/dist/types/report-designer.d.ts +0 -38
  62. package/dist/types/report-viewer.d.ts +0 -38
  63. package/src/components/report/__test__/binding-badge.test.ts +0 -92
  64. package/src/components/report/__test__/conditional-rules-helpers.test.ts +0 -88
  65. package/src/components/report/__test__/field-panel-helpers.test.ts +0 -65
  66. package/src/components/report/__test__/float-panel-position.test.ts +0 -45
  67. package/src/components/report/__test__/preview-grid-size.test.ts +0 -63
  68. package/src/components/report/__test__/report-designer.test.ts +0 -464
  69. package/src/components/report/__test__/report-viewer.test.ts +0 -357
  70. package/src/components/report/__test__/topology.test.ts +0 -143
  71. package/src/components/report/__test__/use-report-designer.test.ts +0 -697
  72. package/src/components/report/binding-badge.ts +0 -117
  73. package/src/components/report/designer/cell-coords.ts +0 -95
  74. package/src/components/report/designer/col-widths.ts +0 -56
  75. package/src/components/report/designer/conditional-rules/helpers.ts +0 -189
  76. package/src/components/report/designer/drop-highlight-overlay.vue +0 -75
  77. package/src/components/report/designer/float-panel.vue +0 -432
  78. package/src/components/report/designer/role.ts +0 -32
  79. package/src/components/report/designer/rule-preview.vue +0 -40
  80. package/src/components/report/designer/rule-row.vue +0 -252
  81. package/src/components/report/designer/rules-dialog.vue +0 -110
  82. package/src/components/report/designer/topology-overlay.vue +0 -128
  83. package/src/components/report/designer/topology.ts +0 -124
  84. package/src/components/report/designer/use-grid-overlay.ts +0 -114
  85. package/src/components/report/designer-hub.vue +0 -198
  86. package/src/components/report/field-panel-helpers.ts +0 -44
  87. package/src/components/report/field-panel.vue +0 -136
  88. package/src/components/report/filter-bar.vue +0 -77
  89. package/src/components/report/hub-connection-form.vue +0 -124
  90. package/src/components/report/hub-dataset-editor.vue +0 -387
  91. package/src/components/report/index.ts +0 -2
  92. package/src/components/report/preview-grid-size.ts +0 -33
  93. package/src/components/report/report-designer.vue +0 -421
  94. package/src/components/report/report-viewer.vue +0 -136
  95. package/src/components/report/style-designer.scss +0 -587
  96. package/src/components/report/style-hub.scss +0 -292
  97. package/src/components/report/style-viewer.scss +0 -104
  98. package/src/components/report/style.scss +0 -3
  99. package/src/components/report/style.ts +0 -20
  100. package/src/components/report/use-report-designer.ts +0 -602
  101. package/src/components/report/use-report-viewer.ts +0 -88
  102. package/src/report/__test__/binding.test.ts +0 -320
  103. package/src/report/__test__/connector.test.ts +0 -239
  104. package/src/report/__test__/export-xlsx.test.ts +0 -96
  105. package/src/report/__test__/filter-bar.test.ts +0 -58
  106. package/src/report/__test__/params.test.ts +0 -156
  107. package/src/report/__test__/render.test.ts +0 -856
  108. package/src/report/__test__/rules.test.ts +0 -131
  109. package/src/report/__test__/template.test.ts +0 -366
  110. package/src/report/binding.ts +0 -474
  111. package/src/report/connector.ts +0 -206
  112. package/src/report/export-xlsx.ts +0 -15
  113. package/src/report/filter-bar.ts +0 -36
  114. package/src/report/index.ts +0 -13
  115. package/src/report/params.ts +0 -96
  116. package/src/report/render/__test__/aggregate.test.ts +0 -16
  117. package/src/report/render/__test__/coordinate.test.ts +0 -405
  118. package/src/report/render/aggregate.ts +0 -261
  119. package/src/report/render/builder.ts +0 -211
  120. package/src/report/render/coordinate.ts +0 -1239
  121. package/src/report/render/index.ts +0 -30
  122. package/src/report/render/style-resolver.ts +0 -68
  123. package/src/report/render/template-index.ts +0 -200
  124. package/src/report/rules/evaluate.ts +0 -113
  125. package/src/report/rules/index.ts +0 -7
  126. package/src/report/template.ts +0 -145
  127. package/src/report/types.ts +0 -96
  128. package/src/types/report-designer.ts +0 -38
  129. package/src/types/report-viewer.ts +0 -37
@@ -1,421 +0,0 @@
1
- <template>
2
- <div :class="cls.b">
3
- <div :class="cls.e('toolbar')">
4
- <u-button size="small" plain @click="hubVisible = true">数据中枢</u-button>
5
-
6
- <div :class="cls.e('mode-toggle')" role="group" aria-label="视图模式">
7
- <u-button
8
- size="small"
9
- :type="!isPreview ? 'primary' : undefined"
10
- :plain="isPreview"
11
- @click="setViewMode('design')"
12
- >
13
- 设计模式
14
- </u-button>
15
- <u-button
16
- size="small"
17
- :type="isPreview ? 'primary' : undefined"
18
- :plain="!isPreview"
19
- @click="setViewMode('preview')"
20
- >
21
- 预览模式
22
- </u-button>
23
- </div>
24
-
25
- <u-button
26
- v-if="isPreview"
27
- size="small"
28
- type="primary"
29
- plain
30
- :loading="exporting"
31
- @click="exportPreviewXlsx"
32
- >
33
- 导出 XLSX
34
- </u-button>
35
-
36
- <span :class="cls.e('hint')">
37
- 在数据中枢配置连接与 SQL
38
- 数据集,再将左侧字段点击或拖拽到单元格完成绑定;选中已绑定格可通过悬浮卡片调整预设与条件样式。
39
- </span>
40
- </div>
41
-
42
- <div v-if="!isPreview" :class="cls.e('body')">
43
- <u-report-field-panel
44
- :class="cls.e('field-panel')"
45
- :datasets="catalog"
46
- :selection-label="selectionLabel"
47
- :bound-keys="boundKeys"
48
- @bind="bindField"
49
- />
50
-
51
- <div
52
- ref="gridHostRef"
53
- :class="cls.e('grid')"
54
- @dragover="onGridDragOver"
55
- @dragleave="onGridDragLeave"
56
- @drop="onGridDrop"
57
- >
58
- <u-sheet
59
- ref="sheetRef"
60
- :class="cls.e('sheet')"
61
- :workbook="workbook"
62
- :rows="gridRows"
63
- :cols="gridCols"
64
- :show-tabs="false"
65
- :resolve-cell-style="resolveCellStyle"
66
- :resolve-cell-renderer="resolveCellRenderer"
67
- />
68
-
69
- <u-report-topology-overlay
70
- :cell="activeCell"
71
- :binding="activeBinding ?? null"
72
- :entries="bindingEntries"
73
- :meta-tick="metaTick"
74
- :host-el="gridHostEl"
75
- :get-grid="getDesignGrid"
76
- :get-binding-at="getBindingAt"
77
- />
78
-
79
- <u-report-drop-highlight-overlay
80
- :cell="dropTargetAddr"
81
- :host-el="gridHostEl"
82
- :get-grid="getDesignGrid"
83
- :dragging="isFieldDragging"
84
- :fallback-label="selectionLabel"
85
- />
86
-
87
- <u-report-drop-highlight-overlay
88
- v-for="addr in pickCandidateCells"
89
- :key="`pick-${addr.row}-${addr.col}`"
90
- :cell="addr"
91
- :host-el="gridHostEl"
92
- :get-grid="getDesignGrid"
93
- :dragging="false"
94
- fallback-label=""
95
- />
96
-
97
- <div v-if="parentPick" :class="cls.e('pick-hint')" role="status">
98
- {{ parentPick.mode === 'row' ? '点击分组头作为行方向父格' : '点击分组头作为列方向父格' }}
99
- </div>
100
-
101
- <u-report-float-panel
102
- :cell="activeCell"
103
- :binding="activeBinding ?? null"
104
- :resolved-row-parent-label="resolvedRowParentLabel"
105
- :resolved-col-parent-label="resolvedColParentLabel"
106
- :parent-pick-mode="parentPick?.mode ?? null"
107
- :row-parent-candidates="rowParentCandidates"
108
- :col-parent-candidates="colParentCandidates"
109
- :resolve-field-label="resolveFieldLabel"
110
- :get-binding-at="getBindingAt"
111
- :host-el="gridHostEl"
112
- :get-grid="getDesignGrid"
113
- @patch="patchActiveBinding"
114
- @remove="removeActiveBinding"
115
- @open-rules="rulesDialogVisible = true"
116
- @start-parent-pick="startParentPick"
117
- @cancel-parent-pick="cancelParentPick"
118
- @clear-parent="clearParent"
119
- />
120
- </div>
121
- </div>
122
-
123
- <div v-else-if="previewTemplate" :class="cls.e('preview')">
124
- <u-report-viewer
125
- ref="previewViewerRef"
126
- :connector="props.connector"
127
- :template="previewTemplate"
128
- :workbook="previewWorkbook"
129
- />
130
- </div>
131
-
132
- <u-drawer v-model="hubVisible" :class="cls.e('hub-drawer')" show-close>
133
- <u-report-dataset-hub :hub="designer" @close="onHubClose" />
134
- </u-drawer>
135
-
136
- <u-report-rules-dialog
137
- v-model="rulesDialogVisible"
138
- :rules="activeBinding?.conditionalRules ?? EMPTY_CONDITIONAL_RULES"
139
- :binding-field="activeBinding?.field ?? ''"
140
- :dataset-fields="activeDatasetFields"
141
- :field-type="activeFieldType"
142
- @save="saveConditionalRules"
143
- />
144
- </div>
145
- </template>
146
-
147
- <script lang="ts" setup>
148
- import { UButton, UDrawer } from '@veltra/desktop'
149
- import type { CellAddress } from '@veltra/sheet-core'
150
- import { Workbook } from '@veltra/sheet-core'
151
- import type { SheetGrid } from '@veltra/sheet-core/grid'
152
- import { bem } from '@veltra/utils'
153
- import { computed, nextTick, onScopeDispose, ref, shallowRef, useTemplateRef, watch } from 'vue'
154
-
155
- import type { ReportTemplate } from '../../report/template'
156
- import type { ConditionalRule } from '../../report/types'
157
- import type {
158
- ReportDesignerEmits,
159
- ReportDesignerProps,
160
- ReportViewerExposed,
161
- SheetExposed,
162
- _ReportDesignerExposed
163
- } from '../../types'
164
- import { USheet } from '../sheet'
165
- import UReportDatasetHub from './designer-hub.vue'
166
- import { resolveGridDropAddress } from './designer/cell-coords'
167
- import { applyGridColWidths, applySheetColWidths, readGridColWidths } from './designer/col-widths'
168
- import UReportDropHighlightOverlay from './designer/drop-highlight-overlay.vue'
169
- import UReportFloatPanel from './designer/float-panel.vue'
170
- import UReportRulesDialog from './designer/rules-dialog.vue'
171
- import UReportTopologyOverlay from './designer/topology-overlay.vue'
172
- import { FIELD_DRAG_MIME, parseFieldDragPayload } from './field-panel-helpers'
173
- import UReportFieldPanel from './field-panel.vue'
174
- import UReportViewer from './report-viewer.vue'
175
- import { useReportDesigner } from './use-report-designer'
176
-
177
- defineOptions({ name: 'UReportDesigner' })
178
-
179
- const props = withDefaults(defineProps<ReportDesignerProps>(), { connections: () => [] })
180
- const emit = defineEmits<ReportDesignerEmits>()
181
-
182
- const cls = bem('report-designer')
183
-
184
- /** 设计态网格默认尺寸(新建模板;已声明尺寸后以模型为准,避免删行后被 props 撑回) */
185
- const DESIGN_ROWS = 24
186
- const DESIGN_COLS = 10
187
-
188
- /** 条件样式弹窗在无绑定格时的空规则占位 */
189
- const EMPTY_CONDITIONAL_RULES: ConditionalRule[] = []
190
-
191
- /** `v-model:connections` 可写代理(连接为纯序列化对象,仅驻留内存) */
192
- const connections = computed({
193
- get: () => props.connections,
194
- set: (value) => emit('update:connections', value)
195
- })
196
-
197
- const designer = useReportDesigner({ props, connections })
198
- const {
199
- workbook,
200
- catalog,
201
- boundKeys,
202
- activeCell,
203
- selectionLabel,
204
- activeBinding,
205
- activeFieldType,
206
- activeDatasetFields,
207
- resolvedRowParentLabel,
208
- resolvedColParentLabel,
209
- parentPick,
210
- rowParentCandidates,
211
- colParentCandidates,
212
- bindingEntries,
213
- metaTick,
214
- getBindingAt,
215
- resolveFieldLabel,
216
- bindField,
217
- patchActiveBinding,
218
- startParentPick,
219
- cancelParentPick,
220
- pickParentAt,
221
- clearParent,
222
- removeActiveBinding,
223
- resolveCellStyle,
224
- resolveCellRenderer,
225
- getTemplate
226
- } = designer
227
-
228
- /** 渲染行列数跟随模型(删行后不再被固定 props 经 ensureTableSize 撑回) */
229
- const gridRows = computed(() => {
230
- const rows = workbook.value.activeSheet.rows
231
- return rows > 0 ? rows : DESIGN_ROWS
232
- })
233
- const gridCols = computed(() => {
234
- const cols = workbook.value.activeSheet.cols
235
- return cols > 0 ? cols : DESIGN_COLS
236
- })
237
-
238
- const hubVisible = ref(false)
239
- const rulesDialogVisible = ref(false)
240
-
241
- const pickCandidateCells = computed((): CellAddress[] => {
242
- if (!parentPick.value) return []
243
- return parentPick.value.mode === 'row' ? rowParentCandidates.value : colParentCandidates.value
244
- })
245
-
246
- function onHubClose(): void {
247
- hubVisible.value = false
248
- }
249
-
250
- watch(hubVisible, (open) => {
251
- if (!open) emit('datasets-change')
252
- })
253
-
254
- watch(
255
- () => designer.datasets.value,
256
- () => emit('datasets-change'),
257
- { deep: true }
258
- )
259
-
260
- const sheetRef = useTemplateRef<SheetExposed>('sheetRef')
261
- const previewViewerRef = useTemplateRef<ReportViewerExposed>('previewViewerRef')
262
- const gridHostRef = useTemplateRef<HTMLElement>('gridHostRef')
263
- const gridHostEl = computed(() => gridHostRef.value ?? null)
264
-
265
- /** 字段拖拽落点高亮(dragover 时更新;落空时 overlay 提示回退当前选区) */
266
- const isFieldDragging = ref(false)
267
- const dropTargetAddr = ref<CellAddress | null>(null)
268
-
269
- function getDesignGrid(): SheetGrid | undefined {
270
- return sheetRef.value?.getGrid()
271
- }
272
-
273
- // ---- 设计 / 预览模式切换(预览态内嵌查看器路径,issue 05) ----
274
-
275
- type ViewMode = 'design' | 'preview'
276
-
277
- const viewMode = ref<ViewMode>('design')
278
- const isPreview = computed(() => viewMode.value === 'preview')
279
-
280
- /** 进入预览时的模板快照(getTemplate 吐出,设计态工作簿不受预览取数影响,切回绑定不丢) */
281
- const previewTemplate = shallowRef<ReportTemplate | null>(null)
282
- /** 预览工作簿自持(XLSX 导出需要拿到填充后的 sheet,05 票 workbook? prop 先例) */
283
- const previewWorkbook = new Workbook()
284
- /** 当前设计态列宽(与 Sheet 模型同步;getTemplate 经 snapshot.colWidths 吐出) */
285
- const designColWidths = ref<Array<[number, number]>>([])
286
- const exporting = ref(false)
287
-
288
- /** 从当前设计网格捕获列宽并写回 Sheet 模型(网格未就绪则保留旧值) */
289
- function captureDesignColWidths(): void {
290
- const widths = readGridColWidths(
291
- getDesignGrid(),
292
- Array.from({ length: gridCols.value }, (_, col) => col)
293
- )
294
- if (!widths) return
295
- // 仅保留相对默认列宽有变化的条目,避免快照膨胀
296
- const custom = widths.filter(([, width]) => width > 0)
297
- designColWidths.value = custom
298
- applySheetColWidths(workbook.value.activeSheet, custom)
299
- }
300
-
301
- /** 设计网格重建后补刷新与列宽(restore 不发 content-reset 的场景由 grid 自行订阅,这里只管列宽) */
302
- function syncDesignGridView(): void {
303
- const grid = getDesignGrid()
304
- grid?.flushPending()
305
- grid?.refresh()
306
- applyGridColWidths(grid, designColWidths.value)
307
- }
308
-
309
- function setViewMode(mode: ViewMode): void {
310
- if (viewMode.value === mode) return
311
- if (mode === 'preview') {
312
- captureDesignColWidths()
313
- previewTemplate.value = getTemplate()
314
- }
315
- viewMode.value = mode
316
- if (mode === 'design') void nextTick(syncDesignGridView)
317
- }
318
-
319
- // 模板更换:组合式函数内完成载入;组件层切回设计态并恢复列宽
320
- watch(
321
- () => props.template,
322
- (template) => {
323
- viewMode.value = 'design'
324
- designColWidths.value = template?.colWidths?.length
325
- ? template.colWidths.map(([col, width]) => [col, width] as [number, number])
326
- : []
327
- if (designColWidths.value.length) {
328
- applySheetColWidths(workbook.value.activeSheet, designColWidths.value)
329
- }
330
- void nextTick(syncDesignGridView)
331
- }
332
- )
333
-
334
- // 父格点选:网格选区变更时写入父格并回到源格
335
- watch(activeCell, (cell) => {
336
- if (cell && parentPick.value) pickParentAt(cell)
337
- })
338
-
339
- // 徽章文案依赖 catalog(describe 完成 / label 覆盖变更后重绘绑定格;meta 变更由 grid 自行订阅)
340
- watch(catalog, () => {
341
- void nextTick(() => getDesignGrid()?.refresh())
342
- })
343
-
344
- const offStructureChange = workbook.value.activeSheet.on('structure-change', () => {
345
- void nextTick(syncDesignGridView)
346
- })
347
- onScopeDispose(offStructureChange)
348
-
349
- async function exportPreviewXlsx(): Promise<void> {
350
- if (!isPreview.value || exporting.value) return
351
- const viewer = previewViewerRef.value
352
- if (!viewer) {
353
- throw new Error('预览查看器尚未就绪,请稍后再导出')
354
- }
355
- exporting.value = true
356
- try {
357
- await viewer.exportXlsx()
358
- } finally {
359
- exporting.value = false
360
- }
361
- }
362
-
363
- function saveConditionalRules(rules: ConditionalRule[]): void {
364
- patchActiveBinding({ conditionalRules: rules })
365
- }
366
-
367
- function clearDropState(): void {
368
- isFieldDragging.value = false
369
- dropTargetAddr.value = null
370
- }
371
-
372
- function resolveDropAddress(event: DragEvent): CellAddress | null {
373
- const grid = getDesignGrid()
374
- const gridEl = gridHostRef.value?.querySelector('.u-sheet__grid-instance') as HTMLElement | null
375
- if (!grid || !gridEl) return null
376
- return resolveGridDropAddress(grid, gridEl, event.clientX, event.clientY)
377
- }
378
-
379
- function onGridDragOver(event: DragEvent): void {
380
- if (!event.dataTransfer?.types.includes(FIELD_DRAG_MIME)) return
381
- event.preventDefault()
382
- event.dataTransfer.dropEffect = 'copy'
383
- isFieldDragging.value = true
384
- dropTargetAddr.value = resolveDropAddress(event)
385
- }
386
-
387
- function onGridDragLeave(event: DragEvent): void {
388
- const host = gridHostRef.value
389
- const related = event.relatedTarget
390
- if (host && related instanceof Node && host.contains(related)) return
391
- clearDropState()
392
- }
393
-
394
- function onGridDrop(event: DragEvent): void {
395
- event.preventDefault()
396
- clearDropState()
397
- const raw = event.dataTransfer?.getData(FIELD_DRAG_MIME)
398
- const payload = raw ? parseFieldDragPayload(raw) : null
399
- if (!payload) return
400
-
401
- // 与旧设计器一致:网格未就绪直接放弃;仅 hit-test 落空时回退当前选区
402
- const grid = getDesignGrid()
403
- const gridEl = gridHostRef.value?.querySelector('.u-sheet__grid-instance') as HTMLElement | null
404
- if (!grid || !gridEl) return
405
-
406
- const dropAddr = resolveDropAddress(event)
407
- if (dropAddr) {
408
- bindField(payload.datasetId, payload.fieldName, dropAddr)
409
- sheetRef.value?.getContext().selectCell(dropAddr)
410
- } else {
411
- bindField(payload.datasetId, payload.fieldName)
412
- }
413
- }
414
-
415
- defineExpose<_ReportDesignerExposed>({
416
- getTemplate: () => {
417
- captureDesignColWidths()
418
- return getTemplate()
419
- }
420
- })
421
- </script>
@@ -1,136 +0,0 @@
1
- <template>
2
- <div :class="cls.b">
3
- <u-report-filter-bar
4
- v-if="params.length"
5
- :query-params="params"
6
- :values="values"
7
- @update:values="setValues"
8
- />
9
-
10
- <div :class="cls.e('body')">
11
- <div v-if="loading" :class="cls.e('loading')" aria-live="polite">
12
- <div :class="cls.e('loading-spinner')" />
13
- <span>正在加载报表数据…</span>
14
- </div>
15
- <div v-else-if="error" :class="cls.e('error')" role="alert">
16
- {{ error.message }}
17
- </div>
18
-
19
- <u-sheet
20
- ref="sheetRef"
21
- :class="cls.e('sheet')"
22
- :workbook="workbook"
23
- :rows="renderSize.rows"
24
- :cols="renderSize.cols"
25
- :show-toolbar="false"
26
- :show-formula-bar="false"
27
- :show-tabs="false"
28
- :show-row-header="false"
29
- :show-col-header="false"
30
- readonly
31
- />
32
- </div>
33
- </div>
34
- </template>
35
-
36
- <script lang="ts" setup>
37
- import { saveBlob } from '@cat-kit/fe'
38
- import type { SheetSnapshot } from '@veltra/sheet-core'
39
- import { Workbook } from '@veltra/sheet-core'
40
- import { bem } from '@veltra/utils'
41
- import { computed, nextTick, useTemplateRef, watch } from 'vue'
42
-
43
- import { exportFilledReportXlsx } from '../../report/export-xlsx'
44
- import type { ReportViewerProps, _ReportViewerExposed } from '../../types'
45
- import type { SheetExposed } from '../../types'
46
- import { USheet } from '../sheet'
47
- import { applyGridColWidths, applySheetColWidths } from './designer/col-widths'
48
- import UReportFilterBar from './filter-bar.vue'
49
- import { previewGridSize, type PreviewGridSizeMode } from './preview-grid-size'
50
- import { useReportViewer } from './use-report-viewer'
51
-
52
- defineOptions({ name: 'UReportViewer' })
53
-
54
- const props = defineProps<ReportViewerProps>()
55
-
56
- const cls = bem('report-viewer')
57
-
58
- /** 取数未完成时拒绝导出的可读错误 */
59
- const EXPORT_NOT_READY_MESSAGE = '报表数据尚未就绪,请等待取数完成后再导出'
60
- const EXPORT_LOADING_MESSAGE = '报表数据加载中,请稍后再导出'
61
-
62
- /** 查看器工作簿:宿主可注入(同 USheet `workbook?` 先例),缺省内部自建;模板 / 填充结果都 restore 进活动 sheet 只读展示 */
63
- const internalWorkbook = new Workbook()
64
- const workbook = computed(() => props.workbook ?? internalWorkbook)
65
-
66
- const sheetRef = useTemplateRef<SheetExposed>('sheetRef')
67
-
68
- const { params, values, loading, error, filledSnapshot, filledColWidths, refresh, setValues } =
69
- useReportViewer(props)
70
-
71
- const renderSize = computed(() => {
72
- const filled = filledSnapshot.value
73
- if (filled) return previewGridSize(filled, 'filled')
74
- return previewGridSize(props.template, 'template')
75
- })
76
-
77
- /** 宿主显式传入优先;否则取数完成后用展开映射列宽;再回落模板设计态列宽 */
78
- const effectiveColWidths = computed(() => {
79
- if (props.colWidths?.length) return props.colWidths
80
- if (filledColWidths.value?.length) return filledColWidths.value
81
- return props.template.colWidths
82
- })
83
-
84
- /** 将运行时列宽写入模型与 VTable(模型为单一事实源;grid 构造/content-reset 也会回放) */
85
- function applyRuntimeColWidths(): void {
86
- if (!effectiveColWidths.value?.length) return
87
- const sheet = workbook.value.activeSheet
88
- applySheetColWidths(sheet, effectiveColWidths.value)
89
- applyGridColWidths(sheetRef.value?.getGrid(), effectiveColWidths.value)
90
- }
91
-
92
- /**
93
- * restore 负责尺寸/冻结/行高/列宽/选区(静默),restoreContent 发 content-reset
94
- * 触发网格全量刷新(grid 层订阅直刷,无需手动 flush);history.clear 使填充不进 undo
95
- */
96
- function applySnapshot(snapshot: SheetSnapshot, mode: PreviewGridSizeMode): void {
97
- const size = previewGridSize(snapshot, mode)
98
- const trimmed: SheetSnapshot = { ...snapshot, rows: size.rows, cols: size.cols }
99
- const sheet = workbook.value.activeSheet
100
- sheet.restore(trimmed)
101
- sheet.restoreContent(trimmed)
102
- sheet.history.clear()
103
- void nextTick(applyRuntimeColWidths)
104
- }
105
-
106
- // 先铺模板静态结构(取数期间 / 取数失败时可见),取数成功后替换为 Filled Report
107
- watch(
108
- () => props.template,
109
- (template) => applySnapshot(template, 'template'),
110
- { immediate: true }
111
- )
112
- watch(filledSnapshot, (filled) => {
113
- if (filled) applySnapshot(filled, 'filled')
114
- })
115
- watch(effectiveColWidths, applyRuntimeColWidths)
116
-
117
- async function exportXlsx(): Promise<void> {
118
- if (loading.value) {
119
- throw new Error(EXPORT_LOADING_MESSAGE)
120
- }
121
- if (!filledSnapshot.value) {
122
- throw new Error(EXPORT_NOT_READY_MESSAGE)
123
- }
124
-
125
- const sheet = workbook.value.activeSheet
126
- const buffer = await exportFilledReportXlsx(sheet)
127
- saveBlob(
128
- new Blob([buffer as unknown as BlobPart], {
129
- type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
130
- }),
131
- `${sheet.name || '报表'}.xlsx`
132
- )
133
- }
134
-
135
- defineExpose<_ReportViewerExposed>({ refresh, exportXlsx })
136
- </script>