@veltra/sheet 2.4.0 → 2.5.1

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 (147) hide show
  1. package/dist/components/sheet/formula-bar.js +50 -7
  2. package/dist/components/sheet/style2.css +2 -2
  3. package/dist/index.d.ts +2 -19
  4. package/dist/index.js +2 -15
  5. package/dist/types/index.d.ts +1 -3
  6. package/package.json +5 -6
  7. package/src/components/sheet/__test__/formula-bar.test.ts +29 -0
  8. package/src/components/sheet/formula-bar.vue +67 -6
  9. package/src/components/sheet/style.scss +1 -1
  10. package/src/index.ts +1 -27
  11. package/src/types/index.ts +0 -2
  12. package/dist/components/report/binding-badge.js +0 -118
  13. package/dist/components/report/designer/cell-coords.js +0 -46
  14. package/dist/components/report/designer/col-widths.js +0 -29
  15. package/dist/components/report/designer/conditional-rules/helpers.js +0 -139
  16. package/dist/components/report/designer/drill-dialog.js +0 -258
  17. package/dist/components/report/designer/drop-highlight-overlay.js +0 -76
  18. package/dist/components/report/designer/float-panel.js +0 -434
  19. package/dist/components/report/designer/role.js +0 -17
  20. package/dist/components/report/designer/rule-preview.js +0 -33
  21. package/dist/components/report/designer/rule-row.js +0 -253
  22. package/dist/components/report/designer/rules-dialog.js +0 -118
  23. package/dist/components/report/designer/topology-overlay.js +0 -122
  24. package/dist/components/report/designer/topology.js +0 -64
  25. package/dist/components/report/designer/use-grid-overlay.js +0 -75
  26. package/dist/components/report/designer-hub.js +0 -177
  27. package/dist/components/report/field-panel-helpers.js +0 -33
  28. package/dist/components/report/field-panel.js +0 -132
  29. package/dist/components/report/filter-bar.js +0 -74
  30. package/dist/components/report/hub-connection-form.js +0 -145
  31. package/dist/components/report/hub-dataset-editor.js +0 -409
  32. package/dist/components/report/preview-grid-size.js +0 -32
  33. package/dist/components/report/print.js +0 -39
  34. package/dist/components/report/report-designer.js +0 -428
  35. package/dist/components/report/report-viewer.js +0 -253
  36. package/dist/components/report/style.d.ts +0 -17
  37. package/dist/components/report/style.js +0 -18
  38. package/dist/components/report/style2.css +0 -965
  39. package/dist/components/report/use-report-designer.js +0 -387
  40. package/dist/components/report/use-report-viewer.js +0 -140
  41. package/dist/report/binding.d.ts +0 -83
  42. package/dist/report/binding.js +0 -334
  43. package/dist/report/connector.d.ts +0 -66
  44. package/dist/report/connector.js +0 -159
  45. package/dist/report/drill.d.ts +0 -48
  46. package/dist/report/drill.js +0 -78
  47. package/dist/report/export-xlsx.d.ts +0 -13
  48. package/dist/report/export-xlsx.js +0 -13
  49. package/dist/report/filter-bar.d.ts +0 -10
  50. package/dist/report/filter-bar.js +0 -31
  51. package/dist/report/index.d.ts +0 -14
  52. package/dist/report/params.d.ts +0 -13
  53. package/dist/report/params.js +0 -61
  54. package/dist/report/print.d.ts +0 -23
  55. package/dist/report/print.js +0 -197
  56. package/dist/report/render/aggregate.js +0 -193
  57. package/dist/report/render/builder.d.ts +0 -12
  58. package/dist/report/render/builder.js +0 -139
  59. package/dist/report/render/coordinate.d.ts +0 -25
  60. package/dist/report/render/coordinate.js +0 -733
  61. package/dist/report/render/index.d.ts +0 -14
  62. package/dist/report/render/index.js +0 -21
  63. package/dist/report/render/style-resolver.js +0 -50
  64. package/dist/report/render/template-index.d.ts +0 -24
  65. package/dist/report/render/template-index.js +0 -137
  66. package/dist/report/rules/evaluate.d.ts +0 -25
  67. package/dist/report/rules/evaluate.js +0 -82
  68. package/dist/report/rules/index.d.ts +0 -2
  69. package/dist/report/template.d.ts +0 -63
  70. package/dist/report/template.js +0 -83
  71. package/dist/report/types.d.ts +0 -103
  72. package/dist/types/report-designer.d.ts +0 -51
  73. package/dist/types/report-viewer.d.ts +0 -57
  74. package/src/components/report/__test__/binding-badge.test.ts +0 -92
  75. package/src/components/report/__test__/conditional-rules-helpers.test.ts +0 -88
  76. package/src/components/report/__test__/field-panel-helpers.test.ts +0 -65
  77. package/src/components/report/__test__/float-panel-position.test.ts +0 -45
  78. package/src/components/report/__test__/preview-grid-size.test.ts +0 -63
  79. package/src/components/report/__test__/report-designer.test.ts +0 -559
  80. package/src/components/report/__test__/report-drill.test.ts +0 -692
  81. package/src/components/report/__test__/report-viewer.test.ts +0 -400
  82. package/src/components/report/__test__/topology.test.ts +0 -143
  83. package/src/components/report/__test__/use-report-designer.test.ts +0 -804
  84. package/src/components/report/binding-badge.ts +0 -122
  85. package/src/components/report/designer/cell-coords.ts +0 -95
  86. package/src/components/report/designer/col-widths.ts +0 -56
  87. package/src/components/report/designer/conditional-rules/helpers.ts +0 -189
  88. package/src/components/report/designer/drill-dialog.vue +0 -245
  89. package/src/components/report/designer/drop-highlight-overlay.vue +0 -75
  90. package/src/components/report/designer/float-panel.vue +0 -444
  91. package/src/components/report/designer/role.ts +0 -32
  92. package/src/components/report/designer/rule-preview.vue +0 -40
  93. package/src/components/report/designer/rule-row.vue +0 -252
  94. package/src/components/report/designer/rules-dialog.vue +0 -110
  95. package/src/components/report/designer/topology-overlay.vue +0 -128
  96. package/src/components/report/designer/topology.ts +0 -124
  97. package/src/components/report/designer/use-grid-overlay.ts +0 -114
  98. package/src/components/report/designer-hub.vue +0 -198
  99. package/src/components/report/field-panel-helpers.ts +0 -44
  100. package/src/components/report/field-panel.vue +0 -136
  101. package/src/components/report/filter-bar.vue +0 -77
  102. package/src/components/report/hub-connection-form.vue +0 -124
  103. package/src/components/report/hub-dataset-editor.vue +0 -387
  104. package/src/components/report/index.ts +0 -2
  105. package/src/components/report/preview-grid-size.ts +0 -33
  106. package/src/components/report/print.ts +0 -44
  107. package/src/components/report/report-designer.vue +0 -454
  108. package/src/components/report/report-viewer.vue +0 -289
  109. package/src/components/report/style-designer.scss +0 -653
  110. package/src/components/report/style-hub.scss +0 -292
  111. package/src/components/report/style-viewer.scss +0 -144
  112. package/src/components/report/style.scss +0 -3
  113. package/src/components/report/style.ts +0 -20
  114. package/src/components/report/use-report-designer.ts +0 -603
  115. package/src/components/report/use-report-viewer.ts +0 -227
  116. package/src/report/__test__/binding.test.ts +0 -320
  117. package/src/report/__test__/connector.test.ts +0 -239
  118. package/src/report/__test__/drill.test.ts +0 -194
  119. package/src/report/__test__/export-xlsx.test.ts +0 -96
  120. package/src/report/__test__/filter-bar.test.ts +0 -58
  121. package/src/report/__test__/params.test.ts +0 -156
  122. package/src/report/__test__/print.test.ts +0 -170
  123. package/src/report/__test__/render.test.ts +0 -856
  124. package/src/report/__test__/rules.test.ts +0 -131
  125. package/src/report/__test__/template.test.ts +0 -366
  126. package/src/report/binding.ts +0 -474
  127. package/src/report/connector.ts +0 -206
  128. package/src/report/drill.ts +0 -112
  129. package/src/report/export-xlsx.ts +0 -15
  130. package/src/report/filter-bar.ts +0 -36
  131. package/src/report/index.ts +0 -16
  132. package/src/report/params.ts +0 -96
  133. package/src/report/print.ts +0 -234
  134. package/src/report/render/__test__/aggregate.test.ts +0 -16
  135. package/src/report/render/__test__/coordinate.test.ts +0 -405
  136. package/src/report/render/aggregate.ts +0 -289
  137. package/src/report/render/builder.ts +0 -211
  138. package/src/report/render/coordinate.ts +0 -1239
  139. package/src/report/render/index.ts +0 -30
  140. package/src/report/render/style-resolver.ts +0 -68
  141. package/src/report/render/template-index.ts +0 -200
  142. package/src/report/rules/evaluate.ts +0 -113
  143. package/src/report/rules/index.ts +0 -7
  144. package/src/report/template.ts +0 -145
  145. package/src/report/types.ts +0 -116
  146. package/src/types/report-designer.ts +0 -55
  147. package/src/types/report-viewer.ts +0 -54
@@ -1,30 +0,0 @@
1
- import type { SheetSnapshot } from '@veltra/sheet-core'
2
-
3
- import type { ReportColWidthEntry } from '../export-xlsx'
4
- import type { DatasetRecords } from '../types'
5
- import { buildFilledReport, mapFilledColWidths } from './builder'
6
- import { computeExpansionLayout } from './coordinate'
7
- import { buildTemplateIndex } from './template-index'
8
-
9
- /**
10
- * 将 Report Template 按 Dataset 渲染为 Filled Report 快照。
11
- * 基于显式展开方向与父格关系展开,不做坐标推断。
12
- */
13
- export function renderReport(template: SheetSnapshot, data: DatasetRecords): SheetSnapshot {
14
- const index = buildTemplateIndex(template)
15
- const layout = computeExpansionLayout(index, data)
16
- return buildFilledReport(template, index, layout, data)
17
- }
18
-
19
- /** 将设计态模板列宽映射为填充报表物理列宽(横向展开列继承父格列宽) */
20
- export function resolveFilledColWidths(
21
- template: SheetSnapshot,
22
- data: DatasetRecords,
23
- templateColWidths: ReadonlyArray<ReportColWidthEntry>
24
- ): ReportColWidthEntry[] {
25
- const index = buildTemplateIndex(template)
26
- const layout = computeExpansionLayout(index, data)
27
- return mapFilledColWidths(templateColWidths, layout, index)
28
- }
29
-
30
- export { mapFilledColWidths } from './builder'
@@ -1,68 +0,0 @@
1
- import type { CellStyle, SheetSnapshot } from '@veltra/sheet-core'
2
- import { StylePool } from '@veltra/sheet-core'
3
- import { mergeCellStyle } from '@veltra/sheet-core/core/command/set-cell-style'
4
-
5
- import { evaluateConditionalStyle, type ConditionalEvalContext } from '../rules'
6
- import type { ConditionalRule } from '../types'
7
-
8
- /** 静态样式经 StylePool 复用 + 条件样式打平(含 `scope: 'row'` 两阶段叠加) */
9
- export class StyleResolver {
10
- private readonly pool = new StylePool()
11
- private readonly rowStyleByPhysRow = new Map<number, CellStyle>()
12
-
13
- constructor(private readonly template: SheetSnapshot) {
14
- if (template.styles.length > 0) this.pool.restore(template.styles)
15
- }
16
-
17
- /** 收集 `scope: 'row'` 命中样式,按物理行累加(须在输出静态格之前完成) */
18
- collectRowStyle(
19
- physicalRow: number,
20
- ctx: ConditionalEvalContext,
21
- rules: readonly ConditionalRule[] | undefined
22
- ): void {
23
- if (!rules?.some((rule) => rule.scope === 'row')) return
24
-
25
- const patch = evaluateConditionalStyle(ctx, undefined, rules, 'row')
26
- if (!patch) return
27
-
28
- const existing = this.rowStyleByPhysRow.get(physicalRow)
29
- this.rowStyleByPhysRow.set(physicalRow, mergeCellStyle(existing, patch) ?? patch)
30
- }
31
-
32
- /** 解析模板格基础样式 + `scope: 'cell'` 条件样式 */
33
- resolveCell(
34
- templateRow: number,
35
- templateCol: number,
36
- ctx: ConditionalEvalContext,
37
- rules: readonly ConditionalRule[] | undefined
38
- ): number | undefined {
39
- const tpl = this.template.cells.find((c) => c.row === templateRow && c.col === templateCol)
40
- const baseStyle = tpl?.s !== undefined ? this.pool.get(tpl.s) : undefined
41
- const merged = evaluateConditionalStyle(ctx, baseStyle, rules, 'cell')
42
- if (!merged) return tpl?.s
43
- if (merged === baseStyle) return tpl?.s
44
- return this.pool.intern(merged)
45
- }
46
-
47
- /** 无绑定的静态格:仅模板样式 */
48
- resolveStatic(templateRow: number, templateCol: number): number | undefined {
49
- const tpl = this.template.cells.find((c) => c.row === templateRow && c.col === templateCol)
50
- return tpl?.s
51
- }
52
-
53
- /** 将行级样式叠加到格级样式 id */
54
- mergeRowStyle(styleId: number | undefined, physicalRow: number): number | undefined {
55
- const rowPatch = this.rowStyleByPhysRow.get(physicalRow)
56
- if (!rowPatch) return styleId
57
-
58
- const base = styleId !== undefined ? this.pool.get(styleId) : undefined
59
- const merged = mergeCellStyle(base, rowPatch)
60
- if (!merged) return styleId
61
- if (merged === base) return styleId
62
- return this.pool.intern(merged)
63
- }
64
-
65
- snapshot(): CellStyle[] {
66
- return this.pool.snapshot()
67
- }
68
- }
@@ -1,200 +0,0 @@
1
- import type { CellAddress, CellRange, CellSnapshotItem, SheetSnapshot } from '@veltra/sheet-core'
2
- import { cellKey, formatAddress } from '@veltra/sheet-core'
3
-
4
- import { REPORT_META_NAMESPACE, formatCellAddress } from '../binding'
5
- import type { ReportBinding } from '../types'
6
-
7
- /** 模板结构约束违反时抛出(附 A1 标签) */
8
- export class TemplateStructureError extends Error {
9
- constructor(message: string) {
10
- super(message)
11
- this.name = 'TemplateStructureError'
12
- }
13
- }
14
-
15
- export interface IndexedBinding {
16
- addr: CellAddress
17
- binding: ReportBinding
18
- }
19
-
20
- /** 模板一次性索引:绑定直查、静态格直查、行/列父子树 */
21
- export interface TemplateIndex {
22
- readonly bindings: ReadonlyMap<number, IndexedBinding>
23
- readonly staticCells: ReadonlyMap<number, CellSnapshotItem>
24
- readonly rowChildren: ReadonlyMap<number, readonly CellAddress[]>
25
- readonly colChildren: ReadonlyMap<number, readonly CellAddress[]>
26
- readonly logicalRows: readonly number[]
27
- readonly logicalCols: readonly number[]
28
- readonly merges: readonly CellRange[]
29
- bindingAt(addr: CellAddress): ReportBinding | undefined
30
- cellAt(addr: CellAddress): CellSnapshotItem | undefined
31
- bindingsOnRow(row: number): readonly IndexedBinding[]
32
- bindingsOnCol(col: number): readonly IndexedBinding[]
33
- isBindingAddr(addr: CellAddress): boolean
34
- }
35
-
36
- function addressesEqual(a: CellAddress, b: CellAddress): boolean {
37
- return a.row === b.row && a.col === b.col
38
- }
39
-
40
- function collectLogicalRows(template: SheetSnapshot): number[] {
41
- const rows = new Set<number>()
42
- for (const cell of template.cells) rows.add(cell.row)
43
- for (const item of template.meta ?? []) rows.add(item.row)
44
- return [...rows].sort((a, b) => a - b)
45
- }
46
-
47
- function collectLogicalCols(template: SheetSnapshot): number[] {
48
- const cols = new Set<number>()
49
- for (const cell of template.cells) cols.add(cell.col)
50
- for (const item of template.meta ?? []) cols.add(item.col)
51
- return [...cols].sort((a, b) => a - b)
52
- }
53
-
54
- function validateParentExists(
55
- addr: CellAddress,
56
- parent: CellAddress | undefined,
57
- bindings: Map<number, IndexedBinding>,
58
- direction: 'row' | 'col'
59
- ): void {
60
- if (!parent) return
61
- const label = formatCellAddress(addr)
62
- const parentLabel = formatCellAddress(parent)
63
- if (!bindings.has(cellKey(parent))) {
64
- throw new TemplateStructureError(
65
- `单元格 ${label} 的${direction === 'row' ? '行' : '列'}方向父格 ${parentLabel} 不存在绑定`
66
- )
67
- }
68
- }
69
-
70
- function detectParentCycle(
71
- start: CellAddress,
72
- bindings: Map<number, IndexedBinding>,
73
- direction: 'row' | 'col'
74
- ): void {
75
- const label = formatCellAddress(start)
76
- const visited = new Set<number>()
77
- let current: CellAddress | undefined = start
78
-
79
- while (current) {
80
- const key = cellKey(current)
81
- if (visited.has(key)) {
82
- throw new TemplateStructureError(
83
- `单元格 ${label} 的${direction === 'row' ? '行' : '列'}方向父链存在环`
84
- )
85
- }
86
- visited.add(key)
87
- const entry = bindings.get(key)
88
- if (!entry) break
89
- current = direction === 'row' ? entry.binding.rowParent : entry.binding.colParent
90
- }
91
- }
92
-
93
- function appendChild(map: Map<number, CellAddress[]>, parentKey: number, child: CellAddress): void {
94
- const list = map.get(parentKey)
95
- if (list) {
96
- list.push(child)
97
- return
98
- }
99
- map.set(parentKey, [child])
100
- }
101
-
102
- /** 由模板快照构建索引并校验结构约束(ADR-0005 决策 3) */
103
- export function buildTemplateIndex(template: SheetSnapshot): TemplateIndex {
104
- const bindings = new Map<number, IndexedBinding>()
105
- const bindingAddrs = new Set<number>()
106
-
107
- for (const item of template.meta ?? []) {
108
- if (item.namespace !== REPORT_META_NAMESPACE) continue
109
- const addr = { row: item.row, col: item.col }
110
- const key = cellKey(addr)
111
- if (bindings.has(key)) {
112
- throw new TemplateStructureError(`单元格 ${formatCellAddress(addr)} 存在重复绑定`)
113
- }
114
- bindings.set(key, { addr, binding: item.payload as ReportBinding })
115
- bindingAddrs.add(key)
116
- }
117
-
118
- const staticCells = new Map<number, CellSnapshotItem>()
119
- for (const cell of template.cells) {
120
- const key = cellKey({ row: cell.row, col: cell.col })
121
- if (bindingAddrs.has(key)) continue
122
- staticCells.set(key, cell)
123
- }
124
-
125
- const rowChildren = new Map<number, CellAddress[]>()
126
- const colChildren = new Map<number, CellAddress[]>()
127
-
128
- for (const { addr, binding } of bindings.values()) {
129
- validateParentExists(addr, binding.rowParent, bindings, 'row')
130
- validateParentExists(addr, binding.colParent, bindings, 'col')
131
- detectParentCycle(addr, bindings, 'row')
132
- detectParentCycle(addr, bindings, 'col')
133
-
134
- if (binding.rowParent) {
135
- appendChild(rowChildren, cellKey(binding.rowParent), addr)
136
- }
137
- if (binding.colParent) {
138
- appendChild(colChildren, cellKey(binding.colParent), addr)
139
- }
140
- }
141
-
142
- const logicalRows = collectLogicalRows(template)
143
- const logicalCols = collectLogicalCols(template)
144
- const merges = template.merges ?? []
145
-
146
- const bindingsByRow = new Map<number, IndexedBinding[]>()
147
- for (const entry of bindings.values()) {
148
- const list = bindingsByRow.get(entry.addr.row)
149
- if (list) list.push(entry)
150
- else bindingsByRow.set(entry.addr.row, [entry])
151
- }
152
- for (const list of bindingsByRow.values()) {
153
- list.sort((a, b) => a.addr.col - b.addr.col)
154
- }
155
-
156
- const bindingsByCol = new Map<number, IndexedBinding[]>()
157
- for (const entry of bindings.values()) {
158
- const list = bindingsByCol.get(entry.addr.col)
159
- if (list) list.push(entry)
160
- else bindingsByCol.set(entry.addr.col, [entry])
161
- }
162
- for (const list of bindingsByCol.values()) {
163
- list.sort((a, b) => a.addr.row - b.addr.row)
164
- }
165
-
166
- return {
167
- bindings,
168
- staticCells,
169
- rowChildren,
170
- colChildren,
171
- logicalRows,
172
- logicalCols,
173
- merges,
174
- bindingAt(addr) {
175
- return bindings.get(cellKey(addr))?.binding
176
- },
177
- cellAt(addr) {
178
- return staticCells.get(cellKey(addr))
179
- },
180
- bindingsOnRow(row) {
181
- return bindingsByRow.get(row) ?? []
182
- },
183
- bindingsOnCol(col) {
184
- return bindingsByCol.get(col) ?? []
185
- },
186
- isBindingAddr(addr) {
187
- return bindings.has(cellKey(addr))
188
- }
189
- }
190
- }
191
-
192
- /** 判断两地址是否相同(供 coordinate 层使用) */
193
- export function sameAddress(a: CellAddress, b: CellAddress): boolean {
194
- return addressesEqual(a, b)
195
- }
196
-
197
- /** 地址键 → A1 标签 */
198
- export function addressLabel(addr: CellAddress): string {
199
- return formatAddress(addr)
200
- }
@@ -1,113 +0,0 @@
1
- import type { CellStyle } from '@veltra/sheet-core'
2
- import { mergeCellStyle } from '@veltra/sheet-core/core/command/set-cell-style'
3
-
4
- import type { ConditionalRule } from '../types'
5
-
6
- /** 条件样式求值上下文:当前展示值 + 可选同记录字段 */
7
- export interface ConditionalEvalContext {
8
- cellValue: unknown
9
- bindingField: string
10
- record?: Record<string, unknown>
11
- }
12
-
13
- function ruleScope(rule: ConditionalRule): 'cell' | 'row' {
14
- return rule.scope ?? 'cell'
15
- }
16
-
17
- /** 按规则配置的求值字段从上下文取比较值 */
18
- export function resolveRuleCompareValue(
19
- ctx: ConditionalEvalContext,
20
- rule: ConditionalRule
21
- ): unknown {
22
- if (rule.field !== undefined && ctx.record !== undefined) {
23
- return ctx.record[rule.field]
24
- }
25
- return ctx.cellValue
26
- }
27
-
28
- /** 将单元格值转为可比较的有限数值;无法转换时返回 null */
29
- export function toComparableNumber(value: unknown): number | null {
30
- if (typeof value === 'number' && Number.isFinite(value)) return value
31
- if (typeof value === 'string' && value.trim() !== '') {
32
- const parsed = Number(value)
33
- if (Number.isFinite(parsed)) return parsed
34
- }
35
- return null
36
- }
37
-
38
- function isBetweenValue(value: unknown): value is [unknown, unknown] {
39
- return Array.isArray(value) && value.length === 2
40
- }
41
-
42
- /**
43
- * 评估单条条件规则是否命中。
44
- * 数值比较运算符在双方均可解析为有限数时按数值比较;`eq` 在无法数值化时回退字符串相等。
45
- */
46
- export function evaluateCondition(cellValue: unknown, rule: ConditionalRule): boolean {
47
- const { operator, value: ruleValue } = rule
48
-
49
- switch (operator) {
50
- case 'gt':
51
- case 'gte':
52
- case 'lt':
53
- case 'lte':
54
- case 'eq': {
55
- const left = toComparableNumber(cellValue)
56
- const right = toComparableNumber(ruleValue)
57
- if (left !== null && right !== null) {
58
- if (operator === 'gt') return left > right
59
- if (operator === 'gte') return left >= right
60
- if (operator === 'lt') return left < right
61
- if (operator === 'lte') return left <= right
62
- return left === right
63
- }
64
- if (operator === 'eq') {
65
- if (typeof cellValue === 'string' && typeof ruleValue === 'string') {
66
- return cellValue === ruleValue
67
- }
68
- return Object.is(cellValue, ruleValue)
69
- }
70
- return false
71
- }
72
- case 'between': {
73
- if (!isBetweenValue(ruleValue)) return false
74
- const left = toComparableNumber(cellValue)
75
- const min = toComparableNumber(ruleValue[0])
76
- const max = toComparableNumber(ruleValue[1])
77
- if (left === null || min === null || max === null) return false
78
- return left >= min && left <= max
79
- }
80
- case 'contains': {
81
- if (typeof cellValue !== 'string') return false
82
- if (typeof ruleValue !== 'string') return false
83
- return cellValue.includes(ruleValue)
84
- }
85
- default:
86
- return false
87
- }
88
- }
89
-
90
- /**
91
- * 按规则数组顺序评估条件样式,将命中规则的 style 增量依次合并到 baseStyle。
92
- * 无规则或未命中时原样返回 baseStyle。
93
- */
94
- export function evaluateConditionalStyle(
95
- ctx: ConditionalEvalContext,
96
- baseStyle: CellStyle | undefined,
97
- rules: readonly ConditionalRule[] | undefined,
98
- scope: 'cell' | 'row' = 'cell'
99
- ): CellStyle | undefined {
100
- if (!rules || rules.length === 0) return baseStyle
101
-
102
- let merged = baseStyle
103
- let matched = false
104
-
105
- for (const rule of rules) {
106
- if (ruleScope(rule) !== scope) continue
107
- if (!evaluateCondition(resolveRuleCompareValue(ctx, rule), rule)) continue
108
- merged = mergeCellStyle(merged, rule.style)
109
- matched = true
110
- }
111
-
112
- return matched ? merged : baseStyle
113
- }
@@ -1,7 +0,0 @@
1
- export {
2
- evaluateCondition,
3
- evaluateConditionalStyle,
4
- resolveRuleCompareValue,
5
- toComparableNumber,
6
- type ConditionalEvalContext
7
- } from './evaluate'
@@ -1,145 +0,0 @@
1
- import type { SheetSnapshot } from '@veltra/sheet-core'
2
-
3
- import { REPORT_META_NAMESPACE } from './binding'
4
- import type { DataConnection, DataConnector, Result } from './connector'
5
- import { buildParamDefs } from './params'
6
- import type {
7
- DatasetField,
8
- DatasetRecords,
9
- ParamValues,
10
- QueryParamDef,
11
- ReportBinding
12
- } from './types'
13
-
14
- /**
15
- * 模板内嵌的数据集定义
16
- * 与 playground DatasetDef 同构,connectionId 替换为内嵌的完整连接对象。
17
- */
18
- export interface ReportDatasetDef {
19
- id: string
20
- label: string
21
- connection: DataConnection
22
- sql: string
23
- /** 参数元数据覆盖(label / 类型 / 默认值 / 选项) */
24
- paramOverrides?: Record<string, Partial<Omit<QueryParamDef, 'id'>>>
25
- /** 字段中文名覆盖(name → label);查看器不消费,随模板序列化往返供设计器还原 */
26
- fieldOverrides?: Record<string, Partial<Pick<DatasetField, 'label'>>>
27
- }
28
-
29
- /** 当前支持的报表模板版本 */
30
- export const REPORT_TEMPLATE_VERSION = 1
31
-
32
- /** 模板版本不兼容时抛出 */
33
- export class IncompatibleTemplateVersionError extends Error {
34
- constructor(message: string) {
35
- super(message)
36
- this.name = 'IncompatibleTemplateVersionError'
37
- }
38
- }
39
-
40
- /**
41
- * 自包含 Report Template:SheetSnapshot + 内嵌数据集定义 + 版本段。
42
- * 查看器仅凭 `template` + `connector` 即可完成「参数提取 → 取数 → 展开渲染」闭环;
43
- * 模板可 JSON 序列化流转。
44
- *
45
- * `colWidths` 继承自 `SheetSnapshot.colWidths`(模型列索引 → 像素)。
46
- * 注意:`Sheet.snapshot()` 不产生 `datasets` / `version` 字段,经 `restore()`/`snapshot()` 往返会丢失,
47
- * 由设计器 `getTemplate()` 在吐出快照时重新附加。
48
- */
49
- export interface ReportTemplate extends SheetSnapshot {
50
- /** 模板结构版本;当前值为 `REPORT_TEMPLATE_VERSION` */
51
- version?: number
52
- datasets?: ReportDatasetDef[]
53
- }
54
-
55
- /** 载入模板前校验版本:缺失或高于当前支持版本时抛可读错误 */
56
- export function assertCompatibleTemplateVersion(template: ReportTemplate): void {
57
- if (template.version === undefined) {
58
- throw new IncompatibleTemplateVersionError(
59
- '报表模板缺少 version 字段,请在设计器中重建模板后重试'
60
- )
61
- }
62
- if (template.version > REPORT_TEMPLATE_VERSION) {
63
- throw new IncompatibleTemplateVersionError(
64
- `报表模板版本 ${template.version} 高于当前支持的版本 ${REPORT_TEMPLATE_VERSION},请升级组件库后重试`
65
- )
66
- }
67
- }
68
-
69
- /**
70
- * 由工作簿快照构造带版本段的 Report Template。
71
- * `colWidths` 随 `snapshot` 展开(已在 `SheetSnapshot.colWidths`)。
72
- */
73
- export function createReportTemplate(
74
- snapshot: SheetSnapshot,
75
- datasets?: ReportDatasetDef[]
76
- ): ReportTemplate {
77
- return { ...snapshot, version: REPORT_TEMPLATE_VERSION, datasets }
78
- }
79
-
80
- /** 读取模板内嵌的数据集定义(旧模板未内嵌时回退空数组) */
81
- export function getTemplateDatasets(template: ReportTemplate): ReportDatasetDef[] {
82
- return Array.isArray(template.datasets) ? template.datasets : []
83
- }
84
-
85
- /** 模板实际绑定的数据集 id(按绑定在 meta 中的出现顺序去重;绑定即真相,ADR-0002 决策 4) */
86
- export function getBoundDatasetIds(template: SheetSnapshot): string[] {
87
- const ids: string[] = []
88
- for (const item of template.meta ?? []) {
89
- if (item.namespace !== REPORT_META_NAMESPACE) continue
90
- const dataset = (item.payload as ReportBinding).dataset
91
- if (typeof dataset === 'string' && dataset && !ids.includes(dataset)) ids.push(dataset)
92
- }
93
- return ids
94
- }
95
-
96
- /**
97
- * Filter Bar 参数来源(ADR-0002 决策 4):模板实际绑定的数据集的查询参数并集,
98
- * 同名参数合并、先见数据集为准;绑定引用了模板未定义的数据集时跳过。
99
- */
100
- export function resolveTemplateParams(template: ReportTemplate): QueryParamDef[] {
101
- const datasets = getTemplateDatasets(template)
102
- const map = new Map<string, QueryParamDef>()
103
- for (const datasetId of getBoundDatasetIds(template)) {
104
- const dataset = datasets.find((item) => item.id === datasetId)
105
- if (!dataset) continue
106
- for (const param of buildParamDefs(dataset.sql, dataset.paramOverrides)) {
107
- if (!map.has(param.id)) map.set(param.id, param)
108
- }
109
- }
110
- return Array.from(map.values())
111
- }
112
-
113
- /** 以参数定义的默认值生成 Filter Bar 初始运行时值 */
114
- export function resolveParamDefaults(params: readonly QueryParamDef[]): ParamValues {
115
- const values: ParamValues = {}
116
- for (const param of params) values[param.id] = param.defaultValue
117
- return values
118
- }
119
-
120
- /**
121
- * 经数据连接器为模板实际绑定的数据集取数(并行),按数据集 id 折叠为 DatasetRecords。
122
- * 任一数据集返回业务错误(`ok: false`)则整体透传该错误;绑定但未定义的数据集跳过。
123
- */
124
- export async function fetchTemplateRecords(
125
- connector: DataConnector,
126
- template: ReportTemplate,
127
- values: ParamValues
128
- ): Promise<Result<DatasetRecords>> {
129
- const datasets = getTemplateDatasets(template)
130
- const targets = getBoundDatasetIds(template)
131
- .map((datasetId) => datasets.find((item) => item.id === datasetId))
132
- .filter((dataset): dataset is ReportDatasetDef => dataset !== undefined)
133
-
134
- const results = await Promise.all(
135
- targets.map((dataset) => connector.query(dataset.connection, dataset.sql, values))
136
- )
137
-
138
- const records: DatasetRecords = {}
139
- for (let i = 0; i < targets.length; i++) {
140
- const result = results[i]!
141
- if (!result.ok) return result
142
- records[targets[i]!.id] = result.data.rows
143
- }
144
- return { ok: true, data: records }
145
- }
@@ -1,116 +0,0 @@
1
- import type { CellAddress, CellStylePatch } from '@veltra/sheet-core'
2
-
3
- /** 报表绑定扩展方向 */
4
- export type ReportExpand = 'down' | 'right' | 'none'
5
-
6
- /** 报表绑定聚合方式 */
7
- export type ReportAggregate = 'list' | 'group' | 'sum' | 'avg' | 'count' | 'max' | 'min'
8
-
9
- /** 报表绑定排序 */
10
- export type ReportSort = 'asc' | 'desc' | 'none'
11
-
12
- /**
13
- * 设计器预设标签:引擎不读,仅用于 UI 展示与一键切换。
14
- * 缺失时 Action Pill 显示「自定义」。
15
- */
16
- export type ReportPreset = 'groupHeader' | 'detail' | 'subtotal' | 'grandTotal' | 'cross'
17
-
18
- /** 条件样式比较运算符 */
19
- export type ConditionalOperator = 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'between' | 'contains'
20
-
21
- /** 条件样式规则:命中后叠加 CellStyle 增量 */
22
- export interface ConditionalRule {
23
- operator: ConditionalOperator
24
- /** 单值比较用标量;`between` 用 `[min, max]`(含端点) */
25
- value: unknown
26
- style: CellStylePatch
27
- /** 求值字段;缺省取绑定格自身字段 */
28
- field?: string
29
- /** 作用范围;缺省 `'cell'` */
30
- scope?: 'cell' | 'row'
31
- }
32
-
33
- /** 绑定格下钻配置(存于 Cell Meta namespace `report`,随绑定序列化;旧查看器忽略该字段) */
34
- export interface ReportDrillConfig {
35
- /** 目标模板引用(宿主模板列表项的 ref,不支持手填) */
36
- target: string
37
- /** 字段 → 详情报参数映射:源记录字段名 → 目标报表 Filter Bar 参数 id */
38
- mapping: Record<string, string>
39
- /** 打开方式:查看器内切换 | UDialog 弹框 */
40
- openMode: 'switch' | 'dialog'
41
- }
42
-
43
- /** 宿主提供的可下钻模板列表项(设计器下拉与查看器 resolveTemplate 契约共用) */
44
- export interface ReportTemplateListItem {
45
- /** 模板引用,作为下钻 target 并传给宿主 resolveTemplate */
46
- ref: string
47
- /** 可读名,用于设计器下拉展示 */
48
- label: string
49
- }
50
-
51
- /** 设计格上的报表绑定(存于 Cell Meta namespace `report`) */
52
- export interface ReportBinding {
53
- dataset: string
54
- field: string
55
- expand: ReportExpand
56
- aggregate: ReportAggregate
57
- /** 纵向从属父格:本格数据受该格当前行实例的值约束 */
58
- rowParent?: CellAddress
59
- /** 横向从属父格:本格数据受该格当前列实例的值约束 */
60
- colParent?: CellAddress
61
- /** 扩展实例是否合并为单个单元格;缺省 true */
62
- mergeSpan?: boolean
63
- /** 分组/明细排序;缺省视为 none */
64
- sort?: ReportSort
65
- /** 条件样式规则(按数组顺序优先级依次求值并合并样式) */
66
- conditionalRules?: ConditionalRule[]
67
- /** 设计器预设;引擎不读 */
68
- preset?: ReportPreset
69
- /** 下钻配置;缺省不可下钻 */
70
- drill?: ReportDrillConfig
71
- }
72
-
73
- /**
74
- * 字段 schema(来自数据集配置,非运行时推断)。
75
- * - `name`:绑定键(写入 Binding.field,英文标识)
76
- * - `label`:中文显示名(字段面板与占位可读名)
77
- */
78
- export interface DatasetField {
79
- name: string
80
- label: string
81
- type: 'string' | 'number' | 'date'
82
- }
83
-
84
- /**
85
- * 全局数据集目录项(catalog):系统里可用的数据集定义。
86
- * 与「本报表选用哪些」无关。
87
- */
88
- export interface DatasetCatalogItem {
89
- id: string
90
- label: string
91
- fields: DatasetField[]
92
- }
93
-
94
- /** Dataset id → 行记录(渲染 Filled Report 用) */
95
- export type DatasetRecords = Record<string, Record<string, unknown>[]>
96
-
97
- /** 查询参数控件类型 */
98
- export type QueryParamType = 'text' | 'number' | 'date' | 'date-range' | 'select'
99
-
100
- /** 下拉选项 */
101
- export interface QueryParamOption {
102
- label: string
103
- value: string
104
- }
105
-
106
- /** 查询参数定义(从 SQL `${param}` 提取,可在数据集编辑器覆盖元数据) */
107
- export interface QueryParamDef {
108
- id: string
109
- label: string
110
- type: QueryParamType
111
- defaultValue: unknown
112
- options?: QueryParamOption[]
113
- }
114
-
115
- /** 运行时参数值 */
116
- export type ParamValues = Record<string, unknown>