@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.
- package/dist/components/sheet/formula-bar.js +50 -7
- package/dist/components/sheet/style2.css +2 -2
- package/dist/index.d.ts +2 -19
- package/dist/index.js +2 -15
- package/dist/types/index.d.ts +1 -3
- package/package.json +5 -6
- package/src/components/sheet/__test__/formula-bar.test.ts +29 -0
- package/src/components/sheet/formula-bar.vue +67 -6
- package/src/components/sheet/style.scss +1 -1
- package/src/index.ts +1 -27
- package/src/types/index.ts +0 -2
- package/dist/components/report/binding-badge.js +0 -118
- package/dist/components/report/designer/cell-coords.js +0 -46
- package/dist/components/report/designer/col-widths.js +0 -29
- package/dist/components/report/designer/conditional-rules/helpers.js +0 -139
- package/dist/components/report/designer/drill-dialog.js +0 -258
- package/dist/components/report/designer/drop-highlight-overlay.js +0 -76
- package/dist/components/report/designer/float-panel.js +0 -434
- package/dist/components/report/designer/role.js +0 -17
- package/dist/components/report/designer/rule-preview.js +0 -33
- package/dist/components/report/designer/rule-row.js +0 -253
- package/dist/components/report/designer/rules-dialog.js +0 -118
- package/dist/components/report/designer/topology-overlay.js +0 -122
- package/dist/components/report/designer/topology.js +0 -64
- package/dist/components/report/designer/use-grid-overlay.js +0 -75
- package/dist/components/report/designer-hub.js +0 -177
- package/dist/components/report/field-panel-helpers.js +0 -33
- package/dist/components/report/field-panel.js +0 -132
- package/dist/components/report/filter-bar.js +0 -74
- package/dist/components/report/hub-connection-form.js +0 -145
- package/dist/components/report/hub-dataset-editor.js +0 -409
- package/dist/components/report/preview-grid-size.js +0 -32
- package/dist/components/report/print.js +0 -39
- package/dist/components/report/report-designer.js +0 -428
- package/dist/components/report/report-viewer.js +0 -253
- package/dist/components/report/style.d.ts +0 -17
- package/dist/components/report/style.js +0 -18
- package/dist/components/report/style2.css +0 -965
- package/dist/components/report/use-report-designer.js +0 -387
- package/dist/components/report/use-report-viewer.js +0 -140
- package/dist/report/binding.d.ts +0 -83
- package/dist/report/binding.js +0 -334
- package/dist/report/connector.d.ts +0 -66
- package/dist/report/connector.js +0 -159
- package/dist/report/drill.d.ts +0 -48
- package/dist/report/drill.js +0 -78
- package/dist/report/export-xlsx.d.ts +0 -13
- package/dist/report/export-xlsx.js +0 -13
- package/dist/report/filter-bar.d.ts +0 -10
- package/dist/report/filter-bar.js +0 -31
- package/dist/report/index.d.ts +0 -14
- package/dist/report/params.d.ts +0 -13
- package/dist/report/params.js +0 -61
- package/dist/report/print.d.ts +0 -23
- package/dist/report/print.js +0 -197
- package/dist/report/render/aggregate.js +0 -193
- package/dist/report/render/builder.d.ts +0 -12
- package/dist/report/render/builder.js +0 -139
- package/dist/report/render/coordinate.d.ts +0 -25
- package/dist/report/render/coordinate.js +0 -733
- package/dist/report/render/index.d.ts +0 -14
- package/dist/report/render/index.js +0 -21
- package/dist/report/render/style-resolver.js +0 -50
- package/dist/report/render/template-index.d.ts +0 -24
- package/dist/report/render/template-index.js +0 -137
- package/dist/report/rules/evaluate.d.ts +0 -25
- package/dist/report/rules/evaluate.js +0 -82
- package/dist/report/rules/index.d.ts +0 -2
- package/dist/report/template.d.ts +0 -63
- package/dist/report/template.js +0 -83
- package/dist/report/types.d.ts +0 -103
- package/dist/types/report-designer.d.ts +0 -51
- package/dist/types/report-viewer.d.ts +0 -57
- package/src/components/report/__test__/binding-badge.test.ts +0 -92
- package/src/components/report/__test__/conditional-rules-helpers.test.ts +0 -88
- package/src/components/report/__test__/field-panel-helpers.test.ts +0 -65
- package/src/components/report/__test__/float-panel-position.test.ts +0 -45
- package/src/components/report/__test__/preview-grid-size.test.ts +0 -63
- package/src/components/report/__test__/report-designer.test.ts +0 -559
- package/src/components/report/__test__/report-drill.test.ts +0 -692
- package/src/components/report/__test__/report-viewer.test.ts +0 -400
- package/src/components/report/__test__/topology.test.ts +0 -143
- package/src/components/report/__test__/use-report-designer.test.ts +0 -804
- package/src/components/report/binding-badge.ts +0 -122
- package/src/components/report/designer/cell-coords.ts +0 -95
- package/src/components/report/designer/col-widths.ts +0 -56
- package/src/components/report/designer/conditional-rules/helpers.ts +0 -189
- package/src/components/report/designer/drill-dialog.vue +0 -245
- package/src/components/report/designer/drop-highlight-overlay.vue +0 -75
- package/src/components/report/designer/float-panel.vue +0 -444
- package/src/components/report/designer/role.ts +0 -32
- package/src/components/report/designer/rule-preview.vue +0 -40
- package/src/components/report/designer/rule-row.vue +0 -252
- package/src/components/report/designer/rules-dialog.vue +0 -110
- package/src/components/report/designer/topology-overlay.vue +0 -128
- package/src/components/report/designer/topology.ts +0 -124
- package/src/components/report/designer/use-grid-overlay.ts +0 -114
- package/src/components/report/designer-hub.vue +0 -198
- package/src/components/report/field-panel-helpers.ts +0 -44
- package/src/components/report/field-panel.vue +0 -136
- package/src/components/report/filter-bar.vue +0 -77
- package/src/components/report/hub-connection-form.vue +0 -124
- package/src/components/report/hub-dataset-editor.vue +0 -387
- package/src/components/report/index.ts +0 -2
- package/src/components/report/preview-grid-size.ts +0 -33
- package/src/components/report/print.ts +0 -44
- package/src/components/report/report-designer.vue +0 -454
- package/src/components/report/report-viewer.vue +0 -289
- package/src/components/report/style-designer.scss +0 -653
- package/src/components/report/style-hub.scss +0 -292
- package/src/components/report/style-viewer.scss +0 -144
- package/src/components/report/style.scss +0 -3
- package/src/components/report/style.ts +0 -20
- package/src/components/report/use-report-designer.ts +0 -603
- package/src/components/report/use-report-viewer.ts +0 -227
- package/src/report/__test__/binding.test.ts +0 -320
- package/src/report/__test__/connector.test.ts +0 -239
- package/src/report/__test__/drill.test.ts +0 -194
- package/src/report/__test__/export-xlsx.test.ts +0 -96
- package/src/report/__test__/filter-bar.test.ts +0 -58
- package/src/report/__test__/params.test.ts +0 -156
- package/src/report/__test__/print.test.ts +0 -170
- package/src/report/__test__/render.test.ts +0 -856
- package/src/report/__test__/rules.test.ts +0 -131
- package/src/report/__test__/template.test.ts +0 -366
- package/src/report/binding.ts +0 -474
- package/src/report/connector.ts +0 -206
- package/src/report/drill.ts +0 -112
- package/src/report/export-xlsx.ts +0 -15
- package/src/report/filter-bar.ts +0 -36
- package/src/report/index.ts +0 -16
- package/src/report/params.ts +0 -96
- package/src/report/print.ts +0 -234
- package/src/report/render/__test__/aggregate.test.ts +0 -16
- package/src/report/render/__test__/coordinate.test.ts +0 -405
- package/src/report/render/aggregate.ts +0 -289
- package/src/report/render/builder.ts +0 -211
- package/src/report/render/coordinate.ts +0 -1239
- package/src/report/render/index.ts +0 -30
- package/src/report/render/style-resolver.ts +0 -68
- package/src/report/render/template-index.ts +0 -200
- package/src/report/rules/evaluate.ts +0 -113
- package/src/report/rules/index.ts +0 -7
- package/src/report/template.ts +0 -145
- package/src/report/types.ts +0 -116
- package/src/types/report-designer.ts +0 -55
- package/src/types/report-viewer.ts +0 -54
package/dist/report/binding.js
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { formatAddress, parseAddress } from "@veltra/sheet-core";
|
|
2
|
-
//#region src/report/binding.ts
|
|
3
|
-
/** 字段 label 解析用的 catalog(由宿主注入) */
|
|
4
|
-
let bindingCatalog = [];
|
|
5
|
-
function setBindingCatalog(catalog) {
|
|
6
|
-
bindingCatalog = catalog;
|
|
7
|
-
}
|
|
8
|
-
const REPORT_META_NAMESPACE = "report";
|
|
9
|
-
const AGGREGATE_PLACEHOLDER_TAG = {
|
|
10
|
-
list: "明细",
|
|
11
|
-
group: "分组",
|
|
12
|
-
sum: "求和",
|
|
13
|
-
avg: "平均",
|
|
14
|
-
count: "计数",
|
|
15
|
-
max: "最大",
|
|
16
|
-
min: "最小"
|
|
17
|
-
};
|
|
18
|
-
/** 预设 → 绑定字段补丁(父格除 grandTotal 外均保留推断值) */
|
|
19
|
-
function presetBindingPatch(preset, options) {
|
|
20
|
-
const expand = options?.transpose ? "right" : "down";
|
|
21
|
-
switch (preset) {
|
|
22
|
-
case "groupHeader": return {
|
|
23
|
-
preset,
|
|
24
|
-
expand,
|
|
25
|
-
aggregate: "group"
|
|
26
|
-
};
|
|
27
|
-
case "detail": return {
|
|
28
|
-
preset,
|
|
29
|
-
expand,
|
|
30
|
-
aggregate: "list"
|
|
31
|
-
};
|
|
32
|
-
case "subtotal": return {
|
|
33
|
-
preset,
|
|
34
|
-
expand: "none",
|
|
35
|
-
aggregate: "sum"
|
|
36
|
-
};
|
|
37
|
-
case "grandTotal": return {
|
|
38
|
-
preset,
|
|
39
|
-
expand: "none",
|
|
40
|
-
aggregate: "sum",
|
|
41
|
-
rowParent: void 0,
|
|
42
|
-
colParent: void 0
|
|
43
|
-
};
|
|
44
|
-
case "cross": return {
|
|
45
|
-
preset,
|
|
46
|
-
expand: "none",
|
|
47
|
-
aggregate: "sum"
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/** 将预设写入绑定(grandTotal 清空双父格;其余保留既有父格) */
|
|
52
|
-
function applyReportPreset(binding, preset, options) {
|
|
53
|
-
const patch = presetBindingPatch(preset, options);
|
|
54
|
-
const next = {
|
|
55
|
-
...binding,
|
|
56
|
-
...patch
|
|
57
|
-
};
|
|
58
|
-
if (preset === "grandTotal") {
|
|
59
|
-
delete next.rowParent;
|
|
60
|
-
delete next.colParent;
|
|
61
|
-
}
|
|
62
|
-
return next;
|
|
63
|
-
}
|
|
64
|
-
function isVerticalExpandBinding(binding) {
|
|
65
|
-
return binding.expand === "down" && (binding.aggregate === "group" || binding.aggregate === "list");
|
|
66
|
-
}
|
|
67
|
-
function isHorizontalExpandBinding(binding) {
|
|
68
|
-
return binding.expand === "right" && (binding.aggregate === "group" || binding.aggregate === "list");
|
|
69
|
-
}
|
|
70
|
-
function isRowGroupBinding(binding) {
|
|
71
|
-
return binding.expand === "down" && binding.aggregate === "group";
|
|
72
|
-
}
|
|
73
|
-
function isColGroupBinding(binding) {
|
|
74
|
-
return binding.expand === "right" && binding.aggregate === "group";
|
|
75
|
-
}
|
|
76
|
-
function isReductionAggregate(aggregate) {
|
|
77
|
-
return aggregate === "sum" || aggregate === "avg" || aggregate === "count" || aggregate === "max" || aggregate === "min";
|
|
78
|
-
}
|
|
79
|
-
/** 小计 / 汇总 / 交叉格的父格候选只列分组头 */
|
|
80
|
-
function prefersGroupParent(binding) {
|
|
81
|
-
if (binding.preset === "subtotal" || binding.preset === "grandTotal" || binding.preset === "cross") return true;
|
|
82
|
-
return binding.expand === "none" && isReductionAggregate(binding.aggregate);
|
|
83
|
-
}
|
|
84
|
-
/** 当前 expand / aggregate 是否仍属于已写入的预设族(同族内改聚合不掉到「自定义」) */
|
|
85
|
-
function presetStillApplies(binding) {
|
|
86
|
-
const preset = binding.preset;
|
|
87
|
-
if (!preset) return false;
|
|
88
|
-
if (preset === "groupHeader") return binding.aggregate === "group" && (binding.expand === "down" || binding.expand === "right");
|
|
89
|
-
if (preset === "detail") return binding.aggregate === "list" && (binding.expand === "down" || binding.expand === "right");
|
|
90
|
-
return binding.expand === "none" && isReductionAggregate(binding.aggregate);
|
|
91
|
-
}
|
|
92
|
-
const DEFAULT_SCAN_COLS = 64;
|
|
93
|
-
const DEFAULT_SCAN_ROWS = 64;
|
|
94
|
-
/** 指定模板行是否含纵向扩展绑定(展开带行) */
|
|
95
|
-
function isExpansionBandRow(row, getBindingAt, scanCols = DEFAULT_SCAN_COLS) {
|
|
96
|
-
for (let col = 0; col < scanCols; col++) {
|
|
97
|
-
const binding = getBindingAt({
|
|
98
|
-
row,
|
|
99
|
-
col
|
|
100
|
-
});
|
|
101
|
-
if (binding && isVerticalExpandBinding(binding)) return true;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
/** 指定模板列是否含横向扩展绑定(展开带列) */
|
|
106
|
-
function isExpansionBandCol(col, getBindingAt, scanRows = DEFAULT_SCAN_ROWS) {
|
|
107
|
-
for (let row = 0; row < scanRows; row++) {
|
|
108
|
-
const binding = getBindingAt({
|
|
109
|
-
row,
|
|
110
|
-
col
|
|
111
|
-
});
|
|
112
|
-
if (binding && isHorizontalExpandBinding(binding)) return true;
|
|
113
|
-
}
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
/** 落格时推断预设:默认明细;数值字段且位于展开带正下方或正右相邻 → 小计 */
|
|
117
|
-
function inferDropPreset(addr, fieldType, getBindingAt) {
|
|
118
|
-
if (fieldType !== "number") return "detail";
|
|
119
|
-
if (addr.row > 0 && isExpansionBandRow(addr.row - 1, getBindingAt)) return "subtotal";
|
|
120
|
-
if (addr.col > 0 && isExpansionBandCol(addr.col - 1, getBindingAt)) return "subtotal";
|
|
121
|
-
return "detail";
|
|
122
|
-
}
|
|
123
|
-
/** 同数据集纵向扩展绑定格(行方向父格下拉候选) */
|
|
124
|
-
function listRowParentCandidates(binding, entries, options) {
|
|
125
|
-
const preferGroup = options?.preferGroup === true;
|
|
126
|
-
const candidates = [];
|
|
127
|
-
for (const entry of entries) {
|
|
128
|
-
if (entry.binding.dataset !== binding.dataset) continue;
|
|
129
|
-
if (preferGroup) {
|
|
130
|
-
if (!isRowGroupBinding(entry.binding)) continue;
|
|
131
|
-
} else if (!isVerticalExpandBinding(entry.binding)) continue;
|
|
132
|
-
candidates.push(entry.addr);
|
|
133
|
-
}
|
|
134
|
-
return candidates;
|
|
135
|
-
}
|
|
136
|
-
/** 同数据集横向扩展绑定格(列方向父格下拉候选) */
|
|
137
|
-
function listColParentCandidates(binding, entries, options) {
|
|
138
|
-
const preferGroup = options?.preferGroup === true;
|
|
139
|
-
const candidates = [];
|
|
140
|
-
for (const entry of entries) {
|
|
141
|
-
if (entry.binding.dataset !== binding.dataset) continue;
|
|
142
|
-
if (preferGroup) {
|
|
143
|
-
if (!isColGroupBinding(entry.binding)) continue;
|
|
144
|
-
} else if (!isHorizontalExpandBinding(entry.binding)) continue;
|
|
145
|
-
candidates.push(entry.addr);
|
|
146
|
-
}
|
|
147
|
-
return candidates;
|
|
148
|
-
}
|
|
149
|
-
function betterHit(current, next) {
|
|
150
|
-
if (!current) return true;
|
|
151
|
-
if (next.distance !== current.distance) return next.distance < current.distance;
|
|
152
|
-
return next.addr.col + next.addr.row < current.addr.col + current.addr.row;
|
|
153
|
-
}
|
|
154
|
-
function pickExpandHit(hits, preferGroup) {
|
|
155
|
-
let group = null;
|
|
156
|
-
let expand = null;
|
|
157
|
-
for (const hit of hits) {
|
|
158
|
-
if (hit.group && betterHit(group, hit)) group = hit;
|
|
159
|
-
if (betterHit(expand, hit)) expand = hit;
|
|
160
|
-
}
|
|
161
|
-
if (preferGroup) return group?.addr ?? null;
|
|
162
|
-
return group?.addr ?? expand?.addr ?? null;
|
|
163
|
-
}
|
|
164
|
-
function scanVerticalOnRow(row, originCol, colStart, colEnd, getBindingAt) {
|
|
165
|
-
const hits = [];
|
|
166
|
-
for (let col = colStart; col < colEnd; col++) {
|
|
167
|
-
const binding = getBindingAt({
|
|
168
|
-
row,
|
|
169
|
-
col
|
|
170
|
-
});
|
|
171
|
-
if (!binding || !isVerticalExpandBinding(binding)) continue;
|
|
172
|
-
hits.push({
|
|
173
|
-
addr: {
|
|
174
|
-
row,
|
|
175
|
-
col
|
|
176
|
-
},
|
|
177
|
-
group: isRowGroupBinding(binding),
|
|
178
|
-
distance: Math.abs(col - originCol)
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
return hits;
|
|
182
|
-
}
|
|
183
|
-
function scanHorizontalOnCol(col, originRow, rowStart, rowEnd, getBindingAt) {
|
|
184
|
-
const hits = [];
|
|
185
|
-
for (let row = rowStart; row < rowEnd; row++) {
|
|
186
|
-
const binding = getBindingAt({
|
|
187
|
-
row,
|
|
188
|
-
col
|
|
189
|
-
});
|
|
190
|
-
if (!binding || !isHorizontalExpandBinding(binding)) continue;
|
|
191
|
-
hits.push({
|
|
192
|
-
addr: {
|
|
193
|
-
row,
|
|
194
|
-
col
|
|
195
|
-
},
|
|
196
|
-
group: isColGroupBinding(binding),
|
|
197
|
-
distance: Math.abs(row - originRow)
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
return hits;
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* 行方向父格推断:
|
|
204
|
-
* 1. 同行向左:先找分组头,否则最近纵向扩展格
|
|
205
|
-
* 2. 上方各行整行扫描:每行先找分组头,再退回任意纵向扩展格
|
|
206
|
-
* 小计(preferGroup)禁止把明细当成父格
|
|
207
|
-
*/
|
|
208
|
-
function inferRowParentCandidate(addr, getBindingAt, options) {
|
|
209
|
-
const preferGroup = options?.preferGroup === true;
|
|
210
|
-
const sameRow = pickExpandHit(scanVerticalOnRow(addr.row, addr.col, 0, addr.col, getBindingAt), preferGroup);
|
|
211
|
-
if (sameRow) return sameRow;
|
|
212
|
-
for (let row = addr.row - 1; row >= 0; row--) {
|
|
213
|
-
const found = pickExpandHit(scanVerticalOnRow(row, addr.col, 0, DEFAULT_SCAN_COLS, getBindingAt), preferGroup);
|
|
214
|
-
if (found) return found;
|
|
215
|
-
}
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* 列方向父格推断(与行方向对称):
|
|
220
|
-
* 1. 同列向上:先找列分组头,否则最近横向扩展格
|
|
221
|
-
* 2. 左侧各列整列扫描:每列先找列分组头
|
|
222
|
-
* 列小计(preferGroup)禁止把交叉格/明细当成列父格
|
|
223
|
-
*/
|
|
224
|
-
function inferColParentCandidate(addr, getBindingAt, options) {
|
|
225
|
-
const preferGroup = options?.preferGroup === true;
|
|
226
|
-
const sameCol = pickExpandHit(scanHorizontalOnCol(addr.col, addr.row, 0, addr.row, getBindingAt), preferGroup);
|
|
227
|
-
if (sameCol) return sameCol;
|
|
228
|
-
for (let col = addr.col - 1; col >= 0; col--) {
|
|
229
|
-
const found = pickExpandHit(scanHorizontalOnCol(col, addr.row, 0, DEFAULT_SCAN_ROWS, getBindingAt), preferGroup);
|
|
230
|
-
if (found) return found;
|
|
231
|
-
}
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
function suggestsColumnSubtotal(above) {
|
|
235
|
-
if (!above) return false;
|
|
236
|
-
return isHorizontalExpandBinding(above) || !!above.colParent || above.preset === "cross";
|
|
237
|
-
}
|
|
238
|
-
function suggestsRowSubtotal(left) {
|
|
239
|
-
if (!left) return false;
|
|
240
|
-
return isVerticalExpandBinding(left) || !!left.rowParent || left.preset === "cross";
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* 按预设写入落格父格:明细/分组/交叉双方向推断;小计按「下方行小计 / 右侧列小计」只挂对应轴。
|
|
244
|
-
*/
|
|
245
|
-
function inferParentsForPreset(addr, preset, getBindingAt) {
|
|
246
|
-
if (preset === "grandTotal") return {};
|
|
247
|
-
const preferGroup = preset === "subtotal" || preset === "cross";
|
|
248
|
-
const rowParent = inferRowParentCandidate(addr, getBindingAt, { preferGroup }) ?? void 0;
|
|
249
|
-
const colParent = inferColParentCandidate(addr, getBindingAt, { preferGroup }) ?? void 0;
|
|
250
|
-
if (preset !== "subtotal") return {
|
|
251
|
-
...rowParent ? { rowParent } : {},
|
|
252
|
-
...colParent ? { colParent } : {}
|
|
253
|
-
};
|
|
254
|
-
const above = addr.row > 0 ? getBindingAt({
|
|
255
|
-
row: addr.row - 1,
|
|
256
|
-
col: addr.col
|
|
257
|
-
}) : void 0;
|
|
258
|
-
const left = addr.col > 0 ? getBindingAt({
|
|
259
|
-
row: addr.row,
|
|
260
|
-
col: addr.col - 1
|
|
261
|
-
}) : void 0;
|
|
262
|
-
const belowBand = addr.row > 0 && isExpansionBandRow(addr.row - 1, getBindingAt);
|
|
263
|
-
const rightOfBand = addr.col > 0 && isExpansionBandCol(addr.col - 1, getBindingAt);
|
|
264
|
-
const columnSubtotal = suggestsColumnSubtotal(above) || rightOfBand && !suggestsRowSubtotal(left);
|
|
265
|
-
const rowSubtotal = suggestsRowSubtotal(left) || belowBand && !columnSubtotal;
|
|
266
|
-
const result = {};
|
|
267
|
-
if (rowSubtotal && rowParent) result.rowParent = rowParent;
|
|
268
|
-
if (columnSubtotal && colParent) result.colParent = colParent;
|
|
269
|
-
if (!rowSubtotal && !columnSubtotal) {
|
|
270
|
-
if (belowBand && rowParent) result.rowParent = rowParent;
|
|
271
|
-
else if (rightOfBand && colParent) result.colParent = colParent;
|
|
272
|
-
else {
|
|
273
|
-
if (rowParent) result.rowParent = rowParent;
|
|
274
|
-
if (colParent) result.colParent = colParent;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return result;
|
|
278
|
-
}
|
|
279
|
-
/** 从绑定字段推断预设;不匹配任何预设时返回 null(自定义) */
|
|
280
|
-
function inferReportPreset(binding) {
|
|
281
|
-
if (binding.preset) return binding.preset;
|
|
282
|
-
if (binding.expand === "none" && isReductionAggregate(binding.aggregate)) {
|
|
283
|
-
if (binding.rowParent && binding.colParent) return "cross";
|
|
284
|
-
if (binding.rowParent || binding.colParent) return "subtotal";
|
|
285
|
-
return "grandTotal";
|
|
286
|
-
}
|
|
287
|
-
if (binding.aggregate === "group" && (binding.expand === "down" || binding.expand === "right")) return "groupHeader";
|
|
288
|
-
if (binding.aggregate === "list" && (binding.expand === "down" || binding.expand === "right")) return "detail";
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
/** 默认明细 + 纵向扩展 */
|
|
292
|
-
function createReportBinding(dataset, fieldName) {
|
|
293
|
-
return {
|
|
294
|
-
dataset: dataset.id,
|
|
295
|
-
field: fieldName,
|
|
296
|
-
aggregate: "list",
|
|
297
|
-
expand: "down",
|
|
298
|
-
preset: "detail",
|
|
299
|
-
sort: "none",
|
|
300
|
-
conditionalRules: []
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
/** 设计地址 → A1 标签(支持多字母列) */
|
|
304
|
-
function formatCellAddress(addr) {
|
|
305
|
-
return formatAddress(addr);
|
|
306
|
-
}
|
|
307
|
-
/** A1 标签 → 设计地址;非法输入返回 null */
|
|
308
|
-
function parseCellAddress(label) {
|
|
309
|
-
return parseAddress(label);
|
|
310
|
-
}
|
|
311
|
-
/** 解析字段中文标签(找不到时回退字段名);可传入报表配置中的 label */
|
|
312
|
-
function resolveFieldLabel(datasetId, fieldName, resolveLabel) {
|
|
313
|
-
const override = resolveLabel?.(datasetId, fieldName);
|
|
314
|
-
if (override) return override;
|
|
315
|
-
return (bindingCatalog.find((d) => d.id === datasetId)?.fields.find((f) => f.name === fieldName))?.label ?? fieldName;
|
|
316
|
-
}
|
|
317
|
-
/** Binding Placeholder 分段文案(聚合标签 + 字段标签),供徽章分色渲染等场景使用 */
|
|
318
|
-
function formatBindingPlaceholderParts(binding, resolveLabel) {
|
|
319
|
-
return {
|
|
320
|
-
tag: AGGREGATE_PLACEHOLDER_TAG[binding.aggregate],
|
|
321
|
-
label: resolveFieldLabel(binding.dataset, binding.field, resolveLabel)
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
/** Binding Placeholder 中文可读文案(如「分组 · 客户」) */
|
|
325
|
-
function formatBindingPlaceholder(binding, resolveLabel) {
|
|
326
|
-
const { tag, label } = formatBindingPlaceholderParts(binding, resolveLabel);
|
|
327
|
-
return `${tag} · ${label}`;
|
|
328
|
-
}
|
|
329
|
-
/** 父格下拉候选文案(如「分组 · 客户 (A2)」) */
|
|
330
|
-
function formatParentCandidateLabel(addr, binding, resolveLabel) {
|
|
331
|
-
return `${formatBindingPlaceholder(binding, resolveLabel)} (${formatCellAddress(addr)})`;
|
|
332
|
-
}
|
|
333
|
-
//#endregion
|
|
334
|
-
export { REPORT_META_NAMESPACE, applyReportPreset, createReportBinding, formatBindingPlaceholder, formatBindingPlaceholderParts, formatCellAddress, formatParentCandidateLabel, inferColParentCandidate, inferDropPreset, inferParentsForPreset, inferReportPreset, inferRowParentCandidate, isColGroupBinding, isExpansionBandCol, isExpansionBandRow, isHorizontalExpandBinding, isReductionAggregate, isRowGroupBinding, isVerticalExpandBinding, listColParentCandidates, listRowParentCandidates, parseCellAddress, prefersGroupParent, presetBindingPatch, presetStillApplies, setBindingCatalog };
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { DatasetField, ParamValues } from "./types.js";
|
|
2
|
-
//#region src/report/connector.d.ts
|
|
3
|
-
/** 数据连接类型(ADR-0003 决策 3:收敛为 MySQL / PostgreSQL) */
|
|
4
|
-
type ConnectionType = 'mysql' | 'postgresql';
|
|
5
|
-
/** 各连接类型的默认端口 */
|
|
6
|
-
declare const DEFAULT_CONNECTION_PORTS: Record<ConnectionType, number>;
|
|
7
|
-
/**
|
|
8
|
-
* 切换连接类型时解析端口:若当前端口仍是上一类型的默认值(或 0),则切到新类型默认端口;
|
|
9
|
-
* 用户已自定义的端口保持不变。
|
|
10
|
-
*/
|
|
11
|
-
declare function resolvePortOnTypeChange(prevType: ConnectionType, nextType: ConnectionType, currentPort: number): number;
|
|
12
|
-
/**
|
|
13
|
-
* 数据连接(纯序列化对象,仅驻留内存)。
|
|
14
|
-
* 凭据持久化与安全存储由下游负责(ADR-0003 决策 4)。
|
|
15
|
-
*/
|
|
16
|
-
interface DataConnection {
|
|
17
|
-
id: string;
|
|
18
|
-
label: string;
|
|
19
|
-
type: ConnectionType;
|
|
20
|
-
host: string;
|
|
21
|
-
port: number;
|
|
22
|
-
database: string;
|
|
23
|
-
username: string;
|
|
24
|
-
password: string;
|
|
25
|
-
}
|
|
26
|
-
/** 业务错误(`200 + { ok: false, error }`)或客户端传输层错误 */
|
|
27
|
-
interface ConnectorError {
|
|
28
|
-
code: string;
|
|
29
|
-
message: string;
|
|
30
|
-
}
|
|
31
|
-
/** 统一结果:成功携带 data;失败携带 error(连接器不抛异常,调用方只处理一条错误路径) */
|
|
32
|
-
type Result<T> = {
|
|
33
|
-
ok: true;
|
|
34
|
-
data: T;
|
|
35
|
-
} | {
|
|
36
|
-
ok: false;
|
|
37
|
-
error: ConnectorError;
|
|
38
|
-
};
|
|
39
|
-
/** query 成功结果:字段 schema + 行记录(契约 `{ fields, rows }` 归一化后) */
|
|
40
|
-
interface QueryResult {
|
|
41
|
-
fields: DatasetField[];
|
|
42
|
-
rows: Record<string, unknown>[];
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* 数据连接器(词汇表:Data Connector):test / describe / query 三方法镜像 HTTP 契约三端点。
|
|
46
|
-
* 真实数据库访问由下游后端以任意语言实现(BYO)。
|
|
47
|
-
*/
|
|
48
|
-
interface DataConnector {
|
|
49
|
-
test(connection: DataConnection): Promise<Result<void>>;
|
|
50
|
-
describe(connection: DataConnection, sql: string): Promise<Result<DatasetField[]>>;
|
|
51
|
-
query(connection: DataConnection, sql: string, values: ParamValues): Promise<Result<QueryResult>>;
|
|
52
|
-
}
|
|
53
|
-
interface CreateHttpConnectorOptions {
|
|
54
|
-
/** 契约端点前缀,如 `https://api.example.com/report`;三端点 = `{endpoint}/test|describe|query`(无版本段) */
|
|
55
|
-
endpoint: string;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* 创建 HTTP 数据连接器:三方法对应 `POST {endpoint}/test|describe|query`。
|
|
59
|
-
* - 请求体 JSON 序列化,`Content-Type: application/json`;
|
|
60
|
-
* - 网络异常与非 2xx(传输层错误)折叠为 `{ ok: false, error }`,不抛异常;
|
|
61
|
-
* - 业务错误(连接失败 / SQL 报错)由服务端以 `200 + { ok: false, error: { code, message } }` 返回并原样透传;
|
|
62
|
-
* - `describe` / `query` 的 fields 归一化为内核 `DatasetField`(label 回退 name,type 缺省 string)。
|
|
63
|
-
*/
|
|
64
|
-
declare function createHttpConnector({ endpoint }: CreateHttpConnectorOptions): DataConnector;
|
|
65
|
-
//#endregion
|
|
66
|
-
export { ConnectionType, ConnectorError, CreateHttpConnectorOptions, DEFAULT_CONNECTION_PORTS, DataConnection, DataConnector, QueryResult, Result, createHttpConnector, resolvePortOnTypeChange };
|
package/dist/report/connector.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
//#region src/report/connector.ts
|
|
2
|
-
/** 各连接类型的默认端口 */
|
|
3
|
-
const DEFAULT_CONNECTION_PORTS = {
|
|
4
|
-
mysql: 3306,
|
|
5
|
-
postgresql: 5432
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* 切换连接类型时解析端口:若当前端口仍是上一类型的默认值(或 0),则切到新类型默认端口;
|
|
9
|
-
* 用户已自定义的端口保持不变。
|
|
10
|
-
*/
|
|
11
|
-
function resolvePortOnTypeChange(prevType, nextType, currentPort) {
|
|
12
|
-
if (prevType === nextType) return currentPort;
|
|
13
|
-
if (currentPort === DEFAULT_CONNECTION_PORTS[prevType] || currentPort === 0) return DEFAULT_CONNECTION_PORTS[nextType];
|
|
14
|
-
return currentPort;
|
|
15
|
-
}
|
|
16
|
-
/** 客户端错误:网络不可达 / 非 2xx / 响应不可解析(业务错误由服务端 200 透传) */
|
|
17
|
-
function badResponse(message) {
|
|
18
|
-
return {
|
|
19
|
-
ok: false,
|
|
20
|
-
error: {
|
|
21
|
-
code: "BAD_RESPONSE",
|
|
22
|
-
message
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
function isRecord(value) {
|
|
27
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
-
}
|
|
29
|
-
function toConnectorError(value) {
|
|
30
|
-
if (isRecord(value)) {
|
|
31
|
-
const { code, message } = value;
|
|
32
|
-
if (typeof code === "string" && typeof message === "string") return {
|
|
33
|
-
code,
|
|
34
|
-
message
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
code: "SERVER_ERROR",
|
|
39
|
-
message: "服务端返回未知错误"
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
/** 契约字段 → 内核 DatasetField(label 缺省回退 name,type 缺省 string) */
|
|
43
|
-
function normalizeField(field) {
|
|
44
|
-
return {
|
|
45
|
-
name: field.name,
|
|
46
|
-
label: field.label ?? field.name,
|
|
47
|
-
type: field.type ?? "string"
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/** 校验并归一化契约 fields;形状不合法返回 null */
|
|
51
|
-
function normalizeFields(fields) {
|
|
52
|
-
if (!Array.isArray(fields)) return null;
|
|
53
|
-
const normalized = [];
|
|
54
|
-
for (const field of fields) {
|
|
55
|
-
if (!isRecord(field) || typeof field.name !== "string") return null;
|
|
56
|
-
normalized.push(normalizeField(field));
|
|
57
|
-
}
|
|
58
|
-
return normalized;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* 创建 HTTP 数据连接器:三方法对应 `POST {endpoint}/test|describe|query`。
|
|
62
|
-
* - 请求体 JSON 序列化,`Content-Type: application/json`;
|
|
63
|
-
* - 网络异常与非 2xx(传输层错误)折叠为 `{ ok: false, error }`,不抛异常;
|
|
64
|
-
* - 业务错误(连接失败 / SQL 报错)由服务端以 `200 + { ok: false, error: { code, message } }` 返回并原样透传;
|
|
65
|
-
* - `describe` / `query` 的 fields 归一化为内核 `DatasetField`(label 回退 name,type 缺省 string)。
|
|
66
|
-
*/
|
|
67
|
-
function createHttpConnector({ endpoint }) {
|
|
68
|
-
async function postJson(path, body) {
|
|
69
|
-
let response;
|
|
70
|
-
try {
|
|
71
|
-
response = await fetch(`${endpoint}${path}`, {
|
|
72
|
-
method: "POST",
|
|
73
|
-
headers: { "Content-Type": "application/json" },
|
|
74
|
-
body: JSON.stringify(body)
|
|
75
|
-
});
|
|
76
|
-
} catch (error) {
|
|
77
|
-
return {
|
|
78
|
-
ok: false,
|
|
79
|
-
error: {
|
|
80
|
-
code: "NETWORK_ERROR",
|
|
81
|
-
message: error instanceof Error ? error.message : String(error)
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
if (!response.ok) return {
|
|
86
|
-
ok: false,
|
|
87
|
-
error: {
|
|
88
|
-
code: `HTTP_${response.status}`,
|
|
89
|
-
message: `请求失败(HTTP ${response.status})`
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
let payload;
|
|
93
|
-
try {
|
|
94
|
-
payload = await response.json();
|
|
95
|
-
} catch {
|
|
96
|
-
return badResponse("响应不是合法 JSON");
|
|
97
|
-
}
|
|
98
|
-
if (!isRecord(payload)) return badResponse("响应不是 JSON 对象");
|
|
99
|
-
return {
|
|
100
|
-
ok: true,
|
|
101
|
-
data: payload
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
return {
|
|
105
|
-
async test(connection) {
|
|
106
|
-
const result = await postJson("/test", { connection });
|
|
107
|
-
if (!result.ok) return result;
|
|
108
|
-
if (result.data.ok === true) return {
|
|
109
|
-
ok: true,
|
|
110
|
-
data: void 0
|
|
111
|
-
};
|
|
112
|
-
return {
|
|
113
|
-
ok: false,
|
|
114
|
-
error: toConnectorError(result.data.error)
|
|
115
|
-
};
|
|
116
|
-
},
|
|
117
|
-
async describe(connection, sql) {
|
|
118
|
-
const result = await postJson("/describe", {
|
|
119
|
-
connection,
|
|
120
|
-
sql
|
|
121
|
-
});
|
|
122
|
-
if (!result.ok) return result;
|
|
123
|
-
if (result.data.ok !== true) return {
|
|
124
|
-
ok: false,
|
|
125
|
-
error: toConnectorError(result.data.error)
|
|
126
|
-
};
|
|
127
|
-
const fields = normalizeFields(result.data.fields);
|
|
128
|
-
if (!fields) return badResponse("describe 响应缺少合法的 fields 数组");
|
|
129
|
-
return {
|
|
130
|
-
ok: true,
|
|
131
|
-
data: fields
|
|
132
|
-
};
|
|
133
|
-
},
|
|
134
|
-
async query(connection, sql, values) {
|
|
135
|
-
const result = await postJson("/query", {
|
|
136
|
-
connection,
|
|
137
|
-
sql,
|
|
138
|
-
values
|
|
139
|
-
});
|
|
140
|
-
if (!result.ok) return result;
|
|
141
|
-
if (result.data.ok !== true) return {
|
|
142
|
-
ok: false,
|
|
143
|
-
error: toConnectorError(result.data.error)
|
|
144
|
-
};
|
|
145
|
-
const fields = normalizeFields(result.data.fields);
|
|
146
|
-
if (!fields) return badResponse("query 响应缺少合法的 fields 数组");
|
|
147
|
-
if (!Array.isArray(result.data.rows)) return badResponse("query 响应缺少 rows 数组");
|
|
148
|
-
return {
|
|
149
|
-
ok: true,
|
|
150
|
-
data: {
|
|
151
|
-
fields,
|
|
152
|
-
rows: result.data.rows
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
//#endregion
|
|
159
|
-
export { DEFAULT_CONNECTION_PORTS, createHttpConnector, resolvePortOnTypeChange };
|
package/dist/report/drill.d.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { DatasetRecords, ParamValues, ReportDrillConfig } from "./types.js";
|
|
2
|
-
import { ReportTemplate } from "./template.js";
|
|
3
|
-
import { SheetSnapshot } from "@veltra/sheet-core";
|
|
4
|
-
//#region src/report/drill.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* 宿主模板解析契约:按下钻 target 引用取回目标模板(允许同步/异步)。
|
|
7
|
-
* 失败抛错——查看器提示可读错误并停留当前报;设计器配置对话框据此解析目标模板查询参数。
|
|
8
|
-
*/
|
|
9
|
-
type ResolveReportTemplate = (ref: string) => ReportTemplate | Promise<ReportTemplate>;
|
|
10
|
-
/**
|
|
11
|
-
* 按下钻映射从该格对应记录取值,生成详情报 Filter Bar 参数值。
|
|
12
|
-
* 记录缺字段(值为 `undefined`)时跳过对应参数,由详情报回退默认值;空映射返回空参数。
|
|
13
|
-
*/
|
|
14
|
-
declare function resolveDrillParams(config: ReportDrillConfig, record: Record<string, unknown>): ParamValues;
|
|
15
|
-
/** 下钻栈层:一张报表模板 + 当时 Filter Bar 参数值(回退时据此恢复并重新取数) */
|
|
16
|
-
interface DrillStackLayer {
|
|
17
|
-
template: ReportTemplate;
|
|
18
|
-
params: ParamValues;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 下钻栈:自根(初始报表)到顶的层序列,至少一层。
|
|
22
|
-
* 只经下方纯函数做不可变更新(返回新栈),便于组件层直接挂响应式。
|
|
23
|
-
*/
|
|
24
|
-
type DrillStack = DrillStackLayer[];
|
|
25
|
-
/** 以初始报表为根层创建下钻栈 */
|
|
26
|
-
declare function createDrillStack(root: DrillStackLayer): DrillStack;
|
|
27
|
-
/**
|
|
28
|
-
* 压入详情报新层并使其成为当前层;`currentParams` 把当前层参数定格为离开时的值
|
|
29
|
-
* (缺省保留原值)。指向自身或成环不做检测,照常压栈,靠 pop 逐级退出。
|
|
30
|
-
*/
|
|
31
|
-
declare function pushDrillLayer(stack: DrillStack, next: DrillStackLayer, currentParams?: ParamValues): DrillStack;
|
|
32
|
-
/** 弹出当前层返回上一层;仅剩根层时原样返回(查看器始终保留一张报表) */
|
|
33
|
-
declare function popDrillLayer(stack: DrillStack): DrillStack;
|
|
34
|
-
/** 当前层(栈顶) */
|
|
35
|
-
declare function currentDrillLayer(stack: DrillStack): DrillStackLayer;
|
|
36
|
-
/** 下钻命中:物理格对应的下钻配置与该格记录 */
|
|
37
|
-
interface DrillHit {
|
|
38
|
-
config: ReportDrillConfig;
|
|
39
|
-
record: Record<string, unknown>;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* 构建填充报表的物理格 → 下钻命中索引(每次成功取数后重建)。
|
|
43
|
-
* 仅含配了下钻的绑定格落点(含分组/汇总格与展开后的实例格,合并跨度内每格都命中);
|
|
44
|
-
* 记录由 `resolvePlacementContext` 解析:list 明细取源数据行,分组/汇总取分组上下文。
|
|
45
|
-
*/
|
|
46
|
-
declare function buildDrillHitMap(template: SheetSnapshot, data: DatasetRecords): Map<number, DrillHit>;
|
|
47
|
-
//#endregion
|
|
48
|
-
export { DrillHit, DrillStack, DrillStackLayer, ResolveReportTemplate, buildDrillHitMap, createDrillStack, currentDrillLayer, popDrillLayer, pushDrillLayer, resolveDrillParams };
|
package/dist/report/drill.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { buildTemplateIndex } from "./render/template-index.js";
|
|
2
|
-
import { computeExpansionLayout } from "./render/coordinate.js";
|
|
3
|
-
import { AggregateIndex, resolvePlacementContext } from "./render/aggregate.js";
|
|
4
|
-
import { cellKey } from "@veltra/sheet-core";
|
|
5
|
-
//#region src/report/drill.ts
|
|
6
|
-
/**
|
|
7
|
-
* 按下钻映射从该格对应记录取值,生成详情报 Filter Bar 参数值。
|
|
8
|
-
* 记录缺字段(值为 `undefined`)时跳过对应参数,由详情报回退默认值;空映射返回空参数。
|
|
9
|
-
*/
|
|
10
|
-
function resolveDrillParams(config, record) {
|
|
11
|
-
const values = {};
|
|
12
|
-
for (const [field, paramId] of Object.entries(config.mapping)) {
|
|
13
|
-
const value = record[field];
|
|
14
|
-
if (value === void 0) continue;
|
|
15
|
-
values[paramId] = value;
|
|
16
|
-
}
|
|
17
|
-
return values;
|
|
18
|
-
}
|
|
19
|
-
/** 以初始报表为根层创建下钻栈 */
|
|
20
|
-
function createDrillStack(root) {
|
|
21
|
-
return [{
|
|
22
|
-
template: root.template,
|
|
23
|
-
params: { ...root.params }
|
|
24
|
-
}];
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* 压入详情报新层并使其成为当前层;`currentParams` 把当前层参数定格为离开时的值
|
|
28
|
-
* (缺省保留原值)。指向自身或成环不做检测,照常压栈,靠 pop 逐级退出。
|
|
29
|
-
*/
|
|
30
|
-
function pushDrillLayer(stack, next, currentParams) {
|
|
31
|
-
const top = stack[stack.length - 1];
|
|
32
|
-
const layers = currentParams ? [...stack.slice(0, -1), {
|
|
33
|
-
template: top.template,
|
|
34
|
-
params: { ...currentParams }
|
|
35
|
-
}] : [...stack];
|
|
36
|
-
layers.push({
|
|
37
|
-
template: next.template,
|
|
38
|
-
params: { ...next.params }
|
|
39
|
-
});
|
|
40
|
-
return layers;
|
|
41
|
-
}
|
|
42
|
-
/** 弹出当前层返回上一层;仅剩根层时原样返回(查看器始终保留一张报表) */
|
|
43
|
-
function popDrillLayer(stack) {
|
|
44
|
-
if (stack.length <= 1) return stack;
|
|
45
|
-
return stack.slice(0, -1);
|
|
46
|
-
}
|
|
47
|
-
/** 当前层(栈顶) */
|
|
48
|
-
function currentDrillLayer(stack) {
|
|
49
|
-
return stack[stack.length - 1];
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* 构建填充报表的物理格 → 下钻命中索引(每次成功取数后重建)。
|
|
53
|
-
* 仅含配了下钻的绑定格落点(含分组/汇总格与展开后的实例格,合并跨度内每格都命中);
|
|
54
|
-
* 记录由 `resolvePlacementContext` 解析:list 明细取源数据行,分组/汇总取分组上下文。
|
|
55
|
-
*/
|
|
56
|
-
function buildDrillHitMap(template, data) {
|
|
57
|
-
const hits = /* @__PURE__ */ new Map();
|
|
58
|
-
const index = buildTemplateIndex(template);
|
|
59
|
-
const layout = computeExpansionLayout(index, data);
|
|
60
|
-
const aggregateIndex = new AggregateIndex(data);
|
|
61
|
-
for (const placement of layout.placements) {
|
|
62
|
-
const config = placement.binding?.drill;
|
|
63
|
-
if (!config) continue;
|
|
64
|
-
const record = resolvePlacementContext(placement, index, data, aggregateIndex);
|
|
65
|
-
if (!record) continue;
|
|
66
|
-
const hit = {
|
|
67
|
-
config,
|
|
68
|
-
record
|
|
69
|
-
};
|
|
70
|
-
for (let row = placement.physical.start.row; row <= placement.physical.end.row; row++) for (let col = placement.physical.start.col; col <= placement.physical.end.col; col++) hits.set(cellKey({
|
|
71
|
-
row,
|
|
72
|
-
col
|
|
73
|
-
}), hit);
|
|
74
|
-
}
|
|
75
|
-
return hits;
|
|
76
|
-
}
|
|
77
|
-
//#endregion
|
|
78
|
-
export { buildDrillHitMap, createDrillStack, currentDrillLayer, popDrillLayer, pushDrillLayer, resolveDrillParams };
|