@veltra/sheet 2.1.1 → 2.3.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.
- package/dist/components/report/binding-badge.js +100 -0
- package/dist/components/report/designer/cell-coords.js +46 -0
- package/dist/components/report/designer/col-widths.js +29 -0
- package/dist/components/report/designer/conditional-rules/helpers.js +139 -0
- package/dist/components/report/designer/drop-highlight-overlay.js +76 -0
- package/dist/components/report/designer/float-panel.js +422 -0
- package/dist/components/report/designer/role.js +17 -0
- package/dist/components/report/designer/rule-preview.js +33 -0
- package/dist/components/report/designer/rule-row.js +253 -0
- package/dist/components/report/designer/rules-dialog.js +118 -0
- package/dist/components/report/designer/topology-overlay.js +122 -0
- package/dist/components/report/designer/topology.js +64 -0
- package/dist/components/report/designer/use-grid-overlay.js +75 -0
- package/dist/components/report/designer-hub.js +177 -0
- package/dist/components/report/field-panel-helpers.js +33 -0
- package/dist/components/report/field-panel.js +132 -0
- package/dist/components/report/filter-bar.js +74 -0
- package/dist/components/report/hub-connection-form.js +145 -0
- package/dist/components/report/hub-dataset-editor.js +394 -0
- package/dist/components/report/preview-grid-size.js +32 -0
- package/dist/components/report/report-designer.js +393 -0
- package/dist/components/report/report-viewer.js +124 -0
- package/dist/components/report/style.d.ts +17 -0
- package/dist/components/report/style.js +18 -0
- package/dist/components/report/style2.css +874 -0
- package/dist/components/report/use-report-designer.js +382 -0
- package/dist/components/report/use-report-viewer.js +55 -0
- package/dist/{vue → components/sheet}/formula-bar.js +1 -1
- package/dist/{vue → components/sheet}/formula-suggest-list.js +1 -1
- package/dist/{vue → components/sheet}/import-file.js +1 -1
- package/dist/{vue → components/sheet}/insert-count-menu-item.js +1 -1
- package/dist/{vue → components/sheet}/insert-image.js +1 -1
- package/dist/{vue → components/sheet}/popup-helpers.js +1 -1
- package/dist/{vue → components/sheet}/popups/border-popup.js +31 -3
- package/dist/{vue → components/sheet}/popups/border-preset-glyph.js +1 -1
- package/dist/{vue → components/sheet}/popups/export-popup.js +2 -2
- package/dist/{vue → components/sheet}/popups/fill-color-popup.js +3 -3
- package/dist/{vue → components/sheet}/popups/find-popup.js +1 -1
- package/dist/{vue → components/sheet}/popups/font-color-popup.js +3 -3
- package/dist/{vue → components/sheet}/popups/font-size-popup.js +3 -3
- package/dist/{vue → components/sheet}/popups/import.worker.d.ts +1 -1
- package/dist/{vue → components/sheet}/popups/import.worker.js +1 -1
- package/dist/{vue → components/sheet}/popups/insert-image-popup.js +1 -1
- package/dist/{vue → components/sheet}/sheet-context-menu.js +7 -4
- package/dist/{vue → components/sheet}/sheet-tabs.js +1 -1
- package/dist/{vue → components/sheet}/sheet-toolbar.js +1 -1
- package/dist/{vue → components/sheet}/sheet.js +16 -1
- package/dist/{vue → components/sheet}/use-find-replace.js +1 -1
- package/dist/{vue → components/sheet}/use-formula-ref-select.js +1 -1
- package/dist/{vue → components/sheet}/use-sheet-grid.js +22 -4
- package/dist/{vue → components/sheet}/use-sheet-state.js +6 -5
- package/dist/{vue → components/sheet}/use-sheet-tabs-bar.js +1 -1
- package/dist/{vue → components/sheet}/use-tool-groups.js +2 -2
- package/dist/{vue → components/sheet}/use-tool-popup.js +2 -2
- package/dist/{vue → components/sheet}/use-toolbar-scroll.js +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.js +13 -3
- package/dist/report/binding.d.ts +83 -0
- package/dist/report/binding.js +332 -0
- package/dist/report/connector.d.ts +66 -0
- package/dist/report/connector.js +159 -0
- package/dist/report/export-xlsx.d.ts +17 -0
- package/dist/report/export-xlsx.js +92 -0
- package/dist/report/filter-bar.d.ts +10 -0
- package/dist/report/filter-bar.js +31 -0
- package/dist/report/index.d.ts +12 -0
- package/dist/report/params.d.ts +13 -0
- package/dist/report/params.js +61 -0
- package/dist/report/render/aggregate.js +169 -0
- package/dist/report/render/builder.d.ts +12 -0
- package/dist/report/render/builder.js +139 -0
- package/dist/report/render/coordinate.d.ts +25 -0
- package/dist/report/render/coordinate.js +733 -0
- package/dist/report/render/index.d.ts +14 -0
- package/dist/report/render/index.js +21 -0
- package/dist/report/render/style-resolver.js +50 -0
- package/dist/report/render/template-index.d.ts +24 -0
- package/dist/report/render/template-index.js +137 -0
- package/dist/report/rules/evaluate.d.ts +25 -0
- package/dist/report/rules/evaluate.js +82 -0
- package/dist/report/rules/index.d.ts +2 -0
- package/dist/report/template.d.ts +63 -0
- package/dist/report/template.js +83 -0
- package/dist/report/types.d.ts +85 -0
- package/dist/tools/apply-style.js +29 -0
- package/dist/tools/builtin.js +17 -17
- package/dist/tools/context.d.ts +39 -6
- package/dist/tools/context.js +38 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/report-designer.d.ts +38 -0
- package/dist/types/report-viewer.d.ts +38 -0
- package/dist/types/sheet.d.ts +21 -1
- package/package.json +6 -8
- package/src/components/report/__test__/conditional-rules-helpers.test.ts +88 -0
- package/src/components/report/__test__/field-panel-helpers.test.ts +65 -0
- package/src/components/report/__test__/float-panel-position.test.ts +45 -0
- package/src/components/report/__test__/preview-grid-size.test.ts +63 -0
- package/src/components/report/__test__/report-designer.test.ts +464 -0
- package/src/components/report/__test__/report-viewer.test.ts +357 -0
- package/src/components/report/__test__/topology.test.ts +143 -0
- package/src/components/report/__test__/use-report-designer.test.ts +697 -0
- package/src/components/report/binding-badge.ts +92 -0
- package/src/components/report/designer/cell-coords.ts +94 -0
- package/src/components/report/designer/col-widths.ts +56 -0
- package/src/components/report/designer/conditional-rules/helpers.ts +189 -0
- package/src/components/report/designer/drop-highlight-overlay.vue +74 -0
- package/src/components/report/designer/float-panel.vue +431 -0
- package/src/components/report/designer/role.ts +32 -0
- package/src/components/report/designer/rule-preview.vue +40 -0
- package/src/components/report/designer/rule-row.vue +252 -0
- package/src/components/report/designer/rules-dialog.vue +110 -0
- package/src/components/report/designer/topology-overlay.vue +127 -0
- package/src/components/report/designer/topology.ts +124 -0
- package/src/components/report/designer/use-grid-overlay.ts +113 -0
- package/src/components/report/designer-hub.vue +198 -0
- package/src/components/report/field-panel-helpers.ts +44 -0
- package/src/components/report/field-panel.vue +136 -0
- package/src/components/report/filter-bar.vue +77 -0
- package/src/components/report/hub-connection-form.vue +124 -0
- package/src/components/report/hub-dataset-editor.vue +369 -0
- package/src/components/report/index.ts +2 -0
- package/src/components/report/preview-grid-size.ts +33 -0
- package/src/components/report/report-designer.vue +420 -0
- package/src/components/report/report-viewer.vue +136 -0
- package/src/components/report/style-designer.scss +587 -0
- package/src/components/report/style-hub.scss +292 -0
- package/src/components/report/style-viewer.scss +104 -0
- package/src/components/report/style.scss +3 -0
- package/src/components/report/style.ts +20 -0
- package/src/components/report/use-report-designer.ts +596 -0
- package/src/components/report/use-report-viewer.ts +88 -0
- package/src/{vue → components/sheet}/__test__/formula-bar.test.ts +37 -18
- package/src/{vue → components/sheet}/__test__/insert-image-popup.test.ts +1 -1
- package/src/{vue → components/sheet}/__test__/insert-image.test.ts +1 -1
- package/src/{vue → components/sheet}/__test__/sheet-component.test.ts +77 -3
- package/src/{vue → components/sheet}/__test__/sheet-context-menu.test.ts +4 -3
- package/src/{vue → components/sheet}/formula-bar.vue +1 -1
- package/src/{vue → components/sheet}/insert-image.ts +1 -1
- package/src/{vue → components/sheet}/popup-helpers.ts +1 -1
- package/src/{vue → components/sheet}/popups/border-popup.vue +32 -4
- package/src/{vue → components/sheet}/popups/export-popup.vue +2 -2
- package/src/{vue → components/sheet}/popups/fill-color-popup.vue +3 -3
- package/src/{vue → components/sheet}/popups/find-popup.vue +1 -1
- package/src/{vue → components/sheet}/popups/font-color-popup.vue +3 -3
- package/src/{vue → components/sheet}/popups/font-size-popup.vue +3 -3
- package/src/{vue → components/sheet}/popups/insert-image-popup.vue +1 -1
- package/src/{vue → components/sheet}/sheet-context-menu.ts +10 -4
- package/src/{vue → components/sheet}/sheet-toolbar.vue +2 -2
- package/src/{vue → components/sheet}/sheet.vue +6 -3
- package/src/{vue → components/sheet}/use-find-replace.ts +1 -1
- package/src/{vue → components/sheet}/use-sheet-grid.ts +23 -5
- package/src/{vue → components/sheet}/use-sheet-state.ts +11 -6
- package/src/{vue → components/sheet}/use-tool-groups.ts +2 -2
- package/src/{vue → components/sheet}/use-tool-popup.ts +2 -2
- package/src/index.ts +24 -188
- package/src/report/__test__/binding.test.ts +320 -0
- package/src/report/__test__/connector.test.ts +239 -0
- package/src/report/__test__/export-xlsx.test.ts +118 -0
- package/src/report/__test__/filter-bar.test.ts +58 -0
- package/src/report/__test__/params.test.ts +156 -0
- package/src/report/__test__/render.test.ts +830 -0
- package/src/report/__test__/rules.test.ts +131 -0
- package/src/report/__test__/template.test.ts +366 -0
- package/src/report/binding.ts +474 -0
- package/src/report/connector.ts +206 -0
- package/src/report/export-xlsx.ts +113 -0
- package/src/report/filter-bar.ts +36 -0
- package/src/report/index.ts +13 -0
- package/src/report/params.ts +96 -0
- package/src/report/render/__test__/aggregate.test.ts +16 -0
- package/src/report/render/__test__/coordinate.test.ts +405 -0
- package/src/report/render/aggregate.ts +261 -0
- package/src/report/render/builder.ts +211 -0
- package/src/report/render/coordinate.ts +1239 -0
- package/src/report/render/index.ts +30 -0
- package/src/report/render/style-resolver.ts +68 -0
- package/src/report/render/template-index.ts +200 -0
- package/src/report/rules/evaluate.ts +113 -0
- package/src/report/rules/index.ts +7 -0
- package/src/report/template.ts +145 -0
- package/src/report/types.ts +96 -0
- package/src/tools/__test__/apply-style.test.ts +129 -0
- package/src/tools/apply-style.ts +41 -0
- package/src/tools/builtin.ts +20 -20
- package/src/tools/context.ts +67 -4
- package/src/tools/index.ts +1 -0
- package/src/types/index.ts +2 -6
- package/src/types/report-designer.ts +38 -0
- package/src/types/report-viewer.ts +37 -0
- package/src/types/sheet.ts +26 -1
- /package/dist/{vue → components/sheet}/style.d.ts +0 -0
- /package/dist/{vue → components/sheet}/style.js +0 -0
- /package/dist/{vue → components/sheet}/style2.css +0 -0
- /package/dist/{vue → components/sheet}/use-formula-suggest.js +0 -0
- /package/src/{vue → components/sheet}/__test__/import-file.test.ts +0 -0
- /package/src/{vue → components/sheet}/__test__/insert-count-menu-item.test.ts +0 -0
- /package/src/{vue → components/sheet}/__test__/use-formula-ref-select.test.ts +0 -0
- /package/src/{vue → components/sheet}/__test__/use-formula-suggest.test.ts +0 -0
- /package/src/{vue → components/sheet}/formula-suggest-list.vue +0 -0
- /package/src/{vue → components/sheet}/import-file.ts +0 -0
- /package/src/{vue → components/sheet}/index.ts +0 -0
- /package/src/{vue → components/sheet}/insert-count-menu-item.vue +0 -0
- /package/src/{vue → components/sheet}/parsing.ts +0 -0
- /package/src/{vue → components/sheet}/popups/border-preset-glyph.vue +0 -0
- /package/src/{vue → components/sheet}/popups/import.worker.ts +0 -0
- /package/src/{vue → components/sheet}/sheet-tabs.vue +0 -0
- /package/src/{vue → components/sheet}/style.scss +0 -0
- /package/src/{vue → components/sheet}/style.ts +0 -0
- /package/src/{vue → components/sheet}/use-formula-ref-select.ts +0 -0
- /package/src/{vue → components/sheet}/use-formula-suggest.ts +0 -0
- /package/src/{vue → components/sheet}/use-sheet-tabs-bar.ts +0 -0
- /package/src/{vue → components/sheet}/use-toolbar-scroll.ts +0 -0
package/dist/tools/builtin.js
CHANGED
|
@@ -17,15 +17,15 @@ function selectionRange(ctx) {
|
|
|
17
17
|
function toggleFontFlag(ctx, field) {
|
|
18
18
|
const sel = selectionRange(ctx);
|
|
19
19
|
if (!sel) return;
|
|
20
|
-
const on = ctx.
|
|
21
|
-
ctx.
|
|
20
|
+
const on = ctx.getEffectiveStyle(sel.activeCell)?.font?.[field] === true;
|
|
21
|
+
ctx.applyStyle(sel.range, { font: { [field]: on ? null : true } });
|
|
22
22
|
}
|
|
23
23
|
/** 对齐 toggle:点当前档 = 清除,点其它档 = 切换 */
|
|
24
24
|
function toggleAlign(ctx, axis, value) {
|
|
25
25
|
const sel = selectionRange(ctx);
|
|
26
26
|
if (!sel) return;
|
|
27
|
-
if (ctx.
|
|
28
|
-
else ctx.
|
|
27
|
+
if (ctx.getEffectiveStyle(sel.activeCell)?.align?.[axis] === value) ctx.applyStyle(sel.range, { align: { [axis]: null } });
|
|
28
|
+
else ctx.applyStyle(sel.range, { align: { [axis]: value } });
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* 内置工具(dogfood 扩展机制)。
|
|
@@ -129,7 +129,7 @@ registerTool({
|
|
|
129
129
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
130
130
|
active: (ctx) => {
|
|
131
131
|
const active = ctx.getSelection().activeCell;
|
|
132
|
-
return !!active && ctx.
|
|
132
|
+
return !!active && ctx.getEffectiveStyle(active)?.font?.bold === true;
|
|
133
133
|
},
|
|
134
134
|
onClick: (ctx) => toggleFontFlag(ctx, "bold")
|
|
135
135
|
});
|
|
@@ -143,7 +143,7 @@ registerTool({
|
|
|
143
143
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
144
144
|
active: (ctx) => {
|
|
145
145
|
const active = ctx.getSelection().activeCell;
|
|
146
|
-
return !!active && ctx.
|
|
146
|
+
return !!active && ctx.getEffectiveStyle(active)?.font?.italic === true;
|
|
147
147
|
},
|
|
148
148
|
onClick: (ctx) => toggleFontFlag(ctx, "italic")
|
|
149
149
|
});
|
|
@@ -157,7 +157,7 @@ registerTool({
|
|
|
157
157
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
158
158
|
active: (ctx) => {
|
|
159
159
|
const active = ctx.getSelection().activeCell;
|
|
160
|
-
return !!active && ctx.
|
|
160
|
+
return !!active && ctx.getEffectiveStyle(active)?.font?.underline === true;
|
|
161
161
|
},
|
|
162
162
|
onClick: (ctx) => toggleFontFlag(ctx, "underline")
|
|
163
163
|
});
|
|
@@ -171,7 +171,7 @@ registerTool({
|
|
|
171
171
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
172
172
|
active: (ctx) => {
|
|
173
173
|
const active = ctx.getSelection().activeCell;
|
|
174
|
-
return !!active && ctx.
|
|
174
|
+
return !!active && ctx.getEffectiveStyle(active)?.font?.strikethrough === true;
|
|
175
175
|
},
|
|
176
176
|
onClick: (ctx) => toggleFontFlag(ctx, "strikethrough")
|
|
177
177
|
});
|
|
@@ -207,7 +207,7 @@ registerTool({
|
|
|
207
207
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
208
208
|
active: (ctx) => {
|
|
209
209
|
const active = ctx.getSelection().activeCell;
|
|
210
|
-
return !!active && ctx.
|
|
210
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.horizontal === "left";
|
|
211
211
|
},
|
|
212
212
|
onClick: (ctx) => toggleAlign(ctx, "horizontal", "left")
|
|
213
213
|
});
|
|
@@ -221,7 +221,7 @@ registerTool({
|
|
|
221
221
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
222
222
|
active: (ctx) => {
|
|
223
223
|
const active = ctx.getSelection().activeCell;
|
|
224
|
-
return !!active && ctx.
|
|
224
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.horizontal === "center";
|
|
225
225
|
},
|
|
226
226
|
onClick: (ctx) => toggleAlign(ctx, "horizontal", "center")
|
|
227
227
|
});
|
|
@@ -235,7 +235,7 @@ registerTool({
|
|
|
235
235
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
236
236
|
active: (ctx) => {
|
|
237
237
|
const active = ctx.getSelection().activeCell;
|
|
238
|
-
return !!active && ctx.
|
|
238
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.horizontal === "right";
|
|
239
239
|
},
|
|
240
240
|
onClick: (ctx) => toggleAlign(ctx, "horizontal", "right")
|
|
241
241
|
});
|
|
@@ -249,7 +249,7 @@ registerTool({
|
|
|
249
249
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
250
250
|
active: (ctx) => {
|
|
251
251
|
const active = ctx.getSelection().activeCell;
|
|
252
|
-
return !!active && ctx.
|
|
252
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.vertical === "top";
|
|
253
253
|
},
|
|
254
254
|
onClick: (ctx) => toggleAlign(ctx, "vertical", "top")
|
|
255
255
|
});
|
|
@@ -263,7 +263,7 @@ registerTool({
|
|
|
263
263
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
264
264
|
active: (ctx) => {
|
|
265
265
|
const active = ctx.getSelection().activeCell;
|
|
266
|
-
return !!active && ctx.
|
|
266
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.vertical === "middle";
|
|
267
267
|
},
|
|
268
268
|
onClick: (ctx) => toggleAlign(ctx, "vertical", "middle")
|
|
269
269
|
});
|
|
@@ -277,7 +277,7 @@ registerTool({
|
|
|
277
277
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
278
278
|
active: (ctx) => {
|
|
279
279
|
const active = ctx.getSelection().activeCell;
|
|
280
|
-
return !!active && ctx.
|
|
280
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.vertical === "bottom";
|
|
281
281
|
},
|
|
282
282
|
onClick: (ctx) => toggleAlign(ctx, "vertical", "bottom")
|
|
283
283
|
});
|
|
@@ -291,13 +291,13 @@ registerTool({
|
|
|
291
291
|
disabled: (ctx) => !ctx.getSelection().activeCell,
|
|
292
292
|
active: (ctx) => {
|
|
293
293
|
const active = ctx.getSelection().activeCell;
|
|
294
|
-
return !!active && ctx.
|
|
294
|
+
return !!active && ctx.getEffectiveStyle(active)?.align?.wrap === true;
|
|
295
295
|
},
|
|
296
296
|
onClick: (ctx) => {
|
|
297
297
|
const sel = selectionRange(ctx);
|
|
298
298
|
if (!sel) return;
|
|
299
|
-
const on = ctx.
|
|
300
|
-
ctx.
|
|
299
|
+
const on = ctx.getEffectiveStyle(sel.activeCell)?.align?.wrap === true;
|
|
300
|
+
ctx.applyStyle(sel.range, { align: { wrap: on ? null : true } });
|
|
301
301
|
}
|
|
302
302
|
});
|
|
303
303
|
registerTool({
|
package/dist/tools/context.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { CellAddress, CellRange } from "@veltra/sheet-core/core/address";
|
|
|
2
2
|
import { CellStyle, CellStylePatch } from "@veltra/sheet-core/core/style/types";
|
|
3
3
|
import { CellData, CellValue } from "@veltra/sheet-core/core/cell-store";
|
|
4
4
|
import { Workbook } from "@veltra/sheet-core/core/workbook";
|
|
5
|
+
import { FrozenState, Sheet } from "@veltra/sheet-core/core/sheet";
|
|
5
6
|
import { HistoryState } from "@veltra/sheet-core/core/command/history";
|
|
6
7
|
import { ImageUpdateFields } from "@veltra/sheet-core/core/command/image";
|
|
7
8
|
import { SetCellValueItem } from "@veltra/sheet-core/core/command/set-cell-value";
|
|
8
9
|
import { ImageInput, SheetImage } from "@veltra/sheet-core/core/image";
|
|
9
10
|
import { CellInfo } from "@veltra/sheet-core/core/merge-manager";
|
|
10
11
|
import { SelectionState } from "@veltra/sheet-core/core/selection";
|
|
11
|
-
import { FrozenState, Sheet } from "@veltra/sheet-core/core/sheet";
|
|
12
12
|
//#region src/tools/context.d.ts
|
|
13
13
|
/**
|
|
14
14
|
* SheetContext:工具的唯一操作门面。
|
|
@@ -37,12 +37,33 @@ interface SheetContext {
|
|
|
37
37
|
getDisplayValue(addr: CellAddress): CellValue | undefined;
|
|
38
38
|
/** 合并语义下的单元格信息(普通格 / 合并锚点 / 被覆盖格) */
|
|
39
39
|
getCellInfo(addr: CellAddress): CellInfo;
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* 按选区形态写样式(Excel 语义):
|
|
42
|
+
* 整行选区 → rowStyles;整列选区 → colStyles;其余 → 逐格。
|
|
43
|
+
* 判定依赖 createSheetContext 注入的渲染尺寸;未注入时一律走 setCellStyle。
|
|
44
|
+
*/
|
|
45
|
+
applyStyle(range: CellRange, partial: CellStylePatch): void;
|
|
46
|
+
/**
|
|
47
|
+
* 选区样式目标(与 applyStyle 同一判定);未注入渲染尺寸时恒为 'cell'。
|
|
48
|
+
*/
|
|
49
|
+
resolveStyleTarget(range: CellRange): 'row' | 'col' | 'cell';
|
|
50
|
+
/**
|
|
51
|
+
* 按选区形态清除样式:整行/整列清对应默认样式;其余清单元格 s。
|
|
52
|
+
* 未注入渲染尺寸时一律走 clearCellStyle。
|
|
53
|
+
*/
|
|
54
|
+
clearStyle(range: CellRange): void;
|
|
55
|
+
/** 设置区域单元格样式(部分合并语义,见 CellStylePatch);空样式 = 删除 s 字段 */
|
|
41
56
|
setCellStyle(range: CellRange, partial: CellStylePatch): void;
|
|
42
|
-
/**
|
|
57
|
+
/** 清除区域单元格样式(保留值 / 公式) */
|
|
43
58
|
clearCellStyle(range: CellRange): void;
|
|
44
|
-
/**
|
|
59
|
+
/** 设置行默认样式(部分合并,进 undo) */
|
|
60
|
+
setRowStyle(row: number, partial: CellStylePatch): void;
|
|
61
|
+
/** 设置列默认样式(部分合并,进 undo) */
|
|
62
|
+
setColStyle(col: number, partial: CellStylePatch): void;
|
|
63
|
+
/** 读取单元格样式(原始存储语义:被覆盖格 → undefined;不含行列默认) */
|
|
45
64
|
getCellStyle(addr: CellAddress): CellStyle | undefined;
|
|
65
|
+
/** 有效样式:列 → 行 → 格字段级叠加(空格可继承行列默认) */
|
|
66
|
+
getEffectiveStyle(addr: CellAddress): CellStyle | undefined;
|
|
46
67
|
setCellValue(addr: CellAddress, value: CellValue): void;
|
|
47
68
|
setCellFormula(addr: CellAddress, formula: string): void;
|
|
48
69
|
/** 批量写入(一次调用 = 一个 undo 单元) */
|
|
@@ -86,6 +107,17 @@ interface SheetContext {
|
|
|
86
107
|
id?: string;
|
|
87
108
|
}) => void): () => void;
|
|
88
109
|
}
|
|
110
|
+
/** createSheetContext 可选配置 */
|
|
111
|
+
interface SheetContextOptions {
|
|
112
|
+
/**
|
|
113
|
+
* 当前渲染网格尺寸(与 SheetGrid / resolveRenderSize 一致)。
|
|
114
|
+
* applyStyle / clearStyle 用其判定整行/整列;缺省时不走行列默认样式。
|
|
115
|
+
*/
|
|
116
|
+
resolveGridSize?: () => {
|
|
117
|
+
rows: number;
|
|
118
|
+
cols: number;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
89
121
|
/**
|
|
90
122
|
* 创建工具上下文。
|
|
91
123
|
* @param resolveSheet 活动 sheet 解析器(USheet 传 `() => activeSheet`,
|
|
@@ -94,7 +126,8 @@ interface SheetContext {
|
|
|
94
126
|
* 支持传解析函数 `() => Workbook`——USheet 在 props.workbook 动态切换后,
|
|
95
127
|
* 上下文始终解析到**当前**工作簿(#2:旧实现取构造时值快照,宿主切换
|
|
96
128
|
* workbook prop 后导出工具仍导出旧工作簿)
|
|
129
|
+
* @param options.resolveGridSize 渲染尺寸解析器(整行/整列样式路由)
|
|
97
130
|
*/
|
|
98
|
-
declare function createSheetContext(resolveSheet: Sheet | (() => Sheet), workbook?: Workbook | (() => Workbook)): SheetContext;
|
|
131
|
+
declare function createSheetContext(resolveSheet: Sheet | (() => Sheet), workbook?: Workbook | (() => Workbook), options?: SheetContextOptions): SheetContext;
|
|
99
132
|
//#endregion
|
|
100
|
-
export { SheetContext, createSheetContext };
|
|
133
|
+
export { SheetContext, SheetContextOptions, createSheetContext };
|
package/dist/tools/context.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { axisStyleItemsForRange, classifySelectionStyleTarget } from "./apply-style.js";
|
|
1
2
|
//#region src/tools/context.ts
|
|
2
3
|
/**
|
|
3
4
|
* 创建工具上下文。
|
|
@@ -7,10 +8,16 @@
|
|
|
7
8
|
* 支持传解析函数 `() => Workbook`——USheet 在 props.workbook 动态切换后,
|
|
8
9
|
* 上下文始终解析到**当前**工作簿(#2:旧实现取构造时值快照,宿主切换
|
|
9
10
|
* workbook prop 后导出工具仍导出旧工作簿)
|
|
11
|
+
* @param options.resolveGridSize 渲染尺寸解析器(整行/整列样式路由)
|
|
10
12
|
*/
|
|
11
|
-
function createSheetContext(resolveSheet, workbook) {
|
|
13
|
+
function createSheetContext(resolveSheet, workbook, options) {
|
|
12
14
|
const sheet = typeof resolveSheet === "function" ? resolveSheet : () => resolveSheet;
|
|
13
15
|
const resolveWorkbook = typeof workbook === "function" ? workbook : workbook != null ? () => workbook : void 0;
|
|
16
|
+
const resolveGridSize = options?.resolveGridSize;
|
|
17
|
+
const resolveTarget = (range) => {
|
|
18
|
+
const size = resolveGridSize?.();
|
|
19
|
+
return size ? classifySelectionStyleTarget(range, size.rows, size.cols) : "cell";
|
|
20
|
+
};
|
|
14
21
|
return {
|
|
15
22
|
get workbook() {
|
|
16
23
|
return resolveWorkbook?.();
|
|
@@ -29,9 +36,39 @@ function createSheetContext(resolveSheet, workbook) {
|
|
|
29
36
|
setCells: (items) => sheet().setCells(items),
|
|
30
37
|
mergeCells: (range) => sheet().mergeCells(range),
|
|
31
38
|
unmergeCells: (range) => sheet().unmergeCells(range),
|
|
39
|
+
applyStyle: (range, partial) => {
|
|
40
|
+
const target = resolveTarget(range);
|
|
41
|
+
if (target === "row" || target === "col") {
|
|
42
|
+
const items = axisStyleItemsForRange(range, target, { partial });
|
|
43
|
+
if (items.length === 0) return;
|
|
44
|
+
sheet().executeCommand("sheet.command.set-axis-style", {
|
|
45
|
+
axis: target,
|
|
46
|
+
items
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
sheet().setCellStyle(range, partial);
|
|
51
|
+
},
|
|
52
|
+
resolveStyleTarget: (range) => resolveTarget(range),
|
|
53
|
+
clearStyle: (range) => {
|
|
54
|
+
const target = resolveTarget(range);
|
|
55
|
+
if (target === "row" || target === "col") {
|
|
56
|
+
const items = axisStyleItemsForRange(range, target, { clear: true });
|
|
57
|
+
if (items.length === 0) return;
|
|
58
|
+
sheet().executeCommand("sheet.command.set-axis-style", {
|
|
59
|
+
axis: target,
|
|
60
|
+
items
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
sheet().clearCellStyle(range);
|
|
65
|
+
},
|
|
32
66
|
setCellStyle: (range, partial) => sheet().setCellStyle(range, partial),
|
|
33
67
|
clearCellStyle: (range) => sheet().clearCellStyle(range),
|
|
68
|
+
setRowStyle: (row, partial) => sheet().setRowStyle(row, partial),
|
|
69
|
+
setColStyle: (col, partial) => sheet().setColStyle(col, partial),
|
|
34
70
|
getCellStyle: (addr) => sheet().getCellStyle(addr),
|
|
71
|
+
getEffectiveStyle: (addr) => sheet().getEffectiveStyle(addr),
|
|
35
72
|
executeCommand: (commandId, params) => sheet().executeCommand(commandId, params),
|
|
36
73
|
get frozen() {
|
|
37
74
|
return sheet().frozen;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { SheetEmits, SheetExposed, SheetProps, _SheetExposed } from "./sheet.js";
|
|
2
|
-
import "
|
|
3
|
-
|
|
2
|
+
import { ReportViewerExposed, ReportViewerProps, _ReportViewerExposed } from "./report-viewer.js";
|
|
3
|
+
import { ReportDesignerEmits, ReportDesignerExposed, ReportDesignerProps, _ReportDesignerExposed } from "./report-designer.js";
|
|
4
|
+
export { ReportDesignerEmits, ReportDesignerExposed, ReportDesignerProps, ReportViewerExposed, ReportViewerProps, SheetEmits, SheetExposed, SheetProps, _ReportDesignerExposed, _ReportViewerExposed, _SheetExposed };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { DataConnection, DataConnector } from "../report/connector.js";
|
|
2
|
+
import { ReportTemplate } from "../report/template.js";
|
|
3
|
+
import "../report/index.js";
|
|
4
|
+
import { DeconstructValue } from "@veltra/utils";
|
|
5
|
+
import { Workbook } from "@veltra/sheet-core/core/workbook";
|
|
6
|
+
//#region src/types/report-designer.d.ts
|
|
7
|
+
/** 报表设计器组件属性(ADR-0003 决策 2) */
|
|
8
|
+
interface ReportDesignerProps {
|
|
9
|
+
/** 数据连接器(如 `createHttpConnector({ endpoint })`):数据中枢的测试连接 / 字段解析 / 记录预览均经它 */
|
|
10
|
+
connector: DataConnector;
|
|
11
|
+
/**
|
|
12
|
+
* 数据连接列表(`v-model:connections`):纯序列化对象,仅驻留内存;
|
|
13
|
+
* 凭据的持久化与安全存储完全由宿主掌控(ADR-0003 决策 4)
|
|
14
|
+
*/
|
|
15
|
+
connections?: DataConnection[];
|
|
16
|
+
/**
|
|
17
|
+
* 载入既有 Report Template 继续设计:恢复网格绑定与设计态数据集;
|
|
18
|
+
* 内嵌连接按 id 合并进 `v-model:connections`(仅缺省时追加,宿主列表是单一事实源)
|
|
19
|
+
*/
|
|
20
|
+
template?: ReportTemplate;
|
|
21
|
+
/** 承载设计态的工作簿(USheet 先例:缺省内部自建单 sheet 工作簿) */
|
|
22
|
+
workbook?: Workbook;
|
|
23
|
+
}
|
|
24
|
+
/** 报表设计器组件定义的事件 */
|
|
25
|
+
interface ReportDesignerEmits {
|
|
26
|
+
(e: 'update:connections', value: DataConnection[]): void;
|
|
27
|
+
/** 数据集增删改或数据中枢关闭:宿主可立即持久化工作区 */
|
|
28
|
+
(e: 'datasets-change'): void;
|
|
29
|
+
}
|
|
30
|
+
/** 在组件内部引用 */
|
|
31
|
+
interface _ReportDesignerExposed {
|
|
32
|
+
/** 取回含 meta 绑定与内嵌数据集定义的可序列化 Report Template */
|
|
33
|
+
getTemplate: () => ReportTemplate;
|
|
34
|
+
}
|
|
35
|
+
/** 报表设计器组件暴露的方法 */
|
|
36
|
+
type ReportDesignerExposed = DeconstructValue<_ReportDesignerExposed>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { ReportDesignerEmits, ReportDesignerExposed, ReportDesignerProps, _ReportDesignerExposed };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ReportColWidthEntry } from "../report/export-xlsx.js";
|
|
2
|
+
import { DataConnector } from "../report/connector.js";
|
|
3
|
+
import { ReportTemplate } from "../report/template.js";
|
|
4
|
+
import "../report/index.js";
|
|
5
|
+
import { DeconstructValue } from "@veltra/utils";
|
|
6
|
+
import { Workbook } from "@veltra/sheet-core/core/workbook";
|
|
7
|
+
//#region src/types/report-viewer.d.ts
|
|
8
|
+
/** 报表查看器组件属性(ADR-0003 决策 2) */
|
|
9
|
+
interface ReportViewerProps {
|
|
10
|
+
/** 数据连接器(如 `createHttpConnector({ endpoint })`) */
|
|
11
|
+
connector: DataConnector;
|
|
12
|
+
/**
|
|
13
|
+
* 自包含 Report Template(SheetSnapshot + 内嵌数据集定义,见 `ReportTemplate`):
|
|
14
|
+
* 查看器据此提取实际绑定数据集的查询参数并集生成 Filter Bar,并经连接器取数展开渲染
|
|
15
|
+
*/
|
|
16
|
+
template: ReportTemplate;
|
|
17
|
+
/** 承载填充报表的工作簿(USheet 先例:缺省内部自建单 sheet 工作簿) */
|
|
18
|
+
workbook?: Workbook;
|
|
19
|
+
/**
|
|
20
|
+
* 列宽显式覆盖(可选):优先于模板 `colWidths` / 展开映射结果写入模型与网格。
|
|
21
|
+
* 列宽已进 `SheetSnapshot.colWidths`,通常无需再传。
|
|
22
|
+
*/
|
|
23
|
+
colWidths?: ReadonlyArray<ReportColWidthEntry>;
|
|
24
|
+
}
|
|
25
|
+
/** 在组件内部引用 */
|
|
26
|
+
interface _ReportViewerExposed {
|
|
27
|
+
/** 重新取数并展开渲染(外部状态变化后主动刷新报表) */
|
|
28
|
+
refresh: () => Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* 导出填充报表为 XLSX(取数完成前拒绝;内部读取填充 sheet 与运行时列宽)。
|
|
31
|
+
* 不内置导出按钮,工具栏由下游决定。
|
|
32
|
+
*/
|
|
33
|
+
exportXlsx: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/** 报表查看器组件暴露的方法 */
|
|
36
|
+
type ReportViewerExposed = DeconstructValue<_ReportViewerExposed>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { ReportViewerExposed, ReportViewerProps, _ReportViewerExposed };
|
package/dist/types/sheet.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SheetContext } from "../tools/context.js";
|
|
2
2
|
import { ComputedRef } from "vue";
|
|
3
3
|
import { DeconstructValue } from "@veltra/utils";
|
|
4
|
-
import { SheetGrid } from "@veltra/sheet-core/grid/sheet-grid";
|
|
4
|
+
import { ResolveCellRenderer, ResolveCellStyleHook, ResolveDisplayValue, SheetGrid } from "@veltra/sheet-core/grid/sheet-grid";
|
|
5
5
|
import { Workbook } from "@veltra/sheet-core/core/workbook";
|
|
6
6
|
import { Sheet } from "@veltra/sheet-core/core/sheet";
|
|
7
7
|
//#region src/types/sheet.d.ts
|
|
@@ -13,12 +13,32 @@ interface SheetProps {
|
|
|
13
13
|
rows?: number;
|
|
14
14
|
/** 渲染列数,默认 26(A..Z) */
|
|
15
15
|
cols?: number;
|
|
16
|
+
/**
|
|
17
|
+
* 显示值覆盖(设计态 Binding Placeholder 等):覆盖 VTable record,不写 CellData.v
|
|
18
|
+
*/
|
|
19
|
+
resolveDisplayValue?: ResolveDisplayValue;
|
|
20
|
+
/**
|
|
21
|
+
* 动态单元格样式:视口渲染时叠加条件样式补丁,不写 CellData.s / StylePool
|
|
22
|
+
*/
|
|
23
|
+
resolveCellStyle?: ResolveCellStyleHook;
|
|
24
|
+
/**
|
|
25
|
+
* 动态单元格渲染(ADR-0004):视口布局时按格自定义渲染形态(VTable
|
|
26
|
+
* customLayout,布局构建用 sheet-core 导出的 CustomLayout),返回 undefined
|
|
27
|
+
* 回落默认渲染;不写模型、不进快照
|
|
28
|
+
*/
|
|
29
|
+
resolveCellRenderer?: ResolveCellRenderer;
|
|
16
30
|
/** 是否显示工具栏,默认 true */
|
|
17
31
|
showToolbar?: boolean;
|
|
18
32
|
/** 是否显示顶部公式栏(名称框 + fx 输入栏),默认 true */
|
|
19
33
|
showFormulaBar?: boolean;
|
|
20
34
|
/** 是否显示底部 sheet 标签栏,默认 true */
|
|
21
35
|
showTabs?: boolean;
|
|
36
|
+
/** 是否显示行号列,默认 true */
|
|
37
|
+
showRowHeader?: boolean;
|
|
38
|
+
/** 是否显示列字母表头,默认 true */
|
|
39
|
+
showColHeader?: boolean;
|
|
40
|
+
/** 只读预览(关闭编辑回写、填充柄等写入口) */
|
|
41
|
+
readonly?: boolean;
|
|
22
42
|
}
|
|
23
43
|
interface SheetEmits {
|
|
24
44
|
/** 激活 sheet 切换(点击 tab 或宿主调用 workbook.activateSheet) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltra/sheet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist",
|
|
6
6
|
"src"
|
|
@@ -30,14 +30,12 @@
|
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@cat-kit/core": ">=1.1.8",
|
|
32
32
|
"@cat-kit/fe": ">=1.1.8",
|
|
33
|
-
"@veltra/
|
|
33
|
+
"@veltra/compositions": "^1.6.0",
|
|
34
|
+
"@veltra/desktop": "^1.6.0",
|
|
34
35
|
"@veltra/icons": "^1.5.0",
|
|
35
|
-
"@veltra/sheet-core": "^2.
|
|
36
|
-
"@veltra/styles": "^1.
|
|
37
|
-
"@veltra/utils": "^1.
|
|
36
|
+
"@veltra/sheet-core": "^2.3.0",
|
|
37
|
+
"@veltra/styles": "^1.6.0",
|
|
38
|
+
"@veltra/utils": "^1.6.0",
|
|
38
39
|
"vue": ">=3.5.41"
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@visactor/vtable": "^1.26.6"
|
|
42
40
|
}
|
|
43
41
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import type { ConditionalRule } from '../../../report/types'
|
|
4
|
+
import {
|
|
5
|
+
cloneRulesFromDraft,
|
|
6
|
+
coerceValueForOperator,
|
|
7
|
+
createDraftItem,
|
|
8
|
+
defaultRuleValue,
|
|
9
|
+
initDraftFromRules,
|
|
10
|
+
operatorsForFieldType,
|
|
11
|
+
readBetweenValue,
|
|
12
|
+
resolveEvalFieldType,
|
|
13
|
+
ruleEvalFieldOptions,
|
|
14
|
+
RULE_SCOPE_OPTIONS,
|
|
15
|
+
writeBetweenValue
|
|
16
|
+
} from '../designer/conditional-rules/helpers'
|
|
17
|
+
|
|
18
|
+
describe('conditional-rules helpers', () => {
|
|
19
|
+
it('按字段类型返回运算符列表', () => {
|
|
20
|
+
expect(operatorsForFieldType('number').map((o) => o.value)).toEqual([
|
|
21
|
+
'gt',
|
|
22
|
+
'gte',
|
|
23
|
+
'lt',
|
|
24
|
+
'lte',
|
|
25
|
+
'eq',
|
|
26
|
+
'between'
|
|
27
|
+
])
|
|
28
|
+
expect(operatorsForFieldType('string').map((o) => o.value)).toEqual(['eq', 'contains'])
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('运算符切换时重置比较值', () => {
|
|
32
|
+
expect(coerceValueForOperator('between', 'number', 42)).toEqual([0, 100])
|
|
33
|
+
expect(coerceValueForOperator('contains', 'string', 42)).toBe('')
|
|
34
|
+
expect(coerceValueForOperator('gt', 'number', '12')).toBe(12)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('between 读写辅助', () => {
|
|
38
|
+
const value = defaultRuleValue('between', 'number')
|
|
39
|
+
expect(readBetweenValue(value, 0, 'number')).toBe(0)
|
|
40
|
+
expect(readBetweenValue(value, 1, 'number')).toBe(100)
|
|
41
|
+
expect(writeBetweenValue(value, 1, 250, 'number')).toEqual([0, 250])
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('草稿与规则互转保持深拷贝', () => {
|
|
45
|
+
const rules: ConditionalRule[] = [
|
|
46
|
+
{ operator: 'gt', value: 10, style: { fill: { color: '#FFCCCC' }, font: { bold: true } } }
|
|
47
|
+
]
|
|
48
|
+
const draft = initDraftFromRules(rules, 'number')
|
|
49
|
+
expect(draft).toHaveLength(1)
|
|
50
|
+
|
|
51
|
+
const cloned = cloneRulesFromDraft(draft)
|
|
52
|
+
expect(cloned[0]).toEqual(rules[0])
|
|
53
|
+
expect(cloned[0]).not.toBe(rules[0])
|
|
54
|
+
|
|
55
|
+
draft[0]!.rule.style.fill!.color = '#000000'
|
|
56
|
+
expect(rules[0]!.style.fill!.color).toBe('#FFCCCC')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('新建草稿项带默认样式', () => {
|
|
60
|
+
const item = createDraftItem(undefined, 'string')
|
|
61
|
+
expect(item.rule.operator).toBe('eq')
|
|
62
|
+
expect(item.rule.value).toBe('')
|
|
63
|
+
expect(item.rule.style.fill?.color).toBeTruthy()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('求值字段与作用范围选项', () => {
|
|
67
|
+
const fields = [
|
|
68
|
+
{ name: 'amount', label: '金额', type: 'number' as const },
|
|
69
|
+
{ name: 'region', label: '地区', type: 'string' as const }
|
|
70
|
+
]
|
|
71
|
+
expect(ruleEvalFieldOptions('amount', fields).map((item) => item.label)).toEqual([
|
|
72
|
+
'本格字段',
|
|
73
|
+
'地区'
|
|
74
|
+
])
|
|
75
|
+
expect(ruleEvalFieldOptions('amount', fields).map((item) => item.value)).toEqual(['', 'region'])
|
|
76
|
+
expect(RULE_SCOPE_OPTIONS.map((item) => item.label)).toEqual(['本格', '整行'])
|
|
77
|
+
expect(resolveEvalFieldType({ operator: 'eq', value: '', style: {} }, 'amount', fields)).toBe(
|
|
78
|
+
'number'
|
|
79
|
+
)
|
|
80
|
+
expect(
|
|
81
|
+
resolveEvalFieldType(
|
|
82
|
+
{ operator: 'eq', value: '', style: {}, field: 'region' },
|
|
83
|
+
'amount',
|
|
84
|
+
fields
|
|
85
|
+
)
|
|
86
|
+
).toBe('string')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import type { DatasetCatalogItem } from '../../../report/types'
|
|
4
|
+
import {
|
|
5
|
+
fieldTypeGlyph,
|
|
6
|
+
filterDatasetsByQuery,
|
|
7
|
+
formatFieldDragPayload,
|
|
8
|
+
parseFieldDragPayload
|
|
9
|
+
} from '../field-panel-helpers'
|
|
10
|
+
|
|
11
|
+
// ---- 内联 fixtures ----
|
|
12
|
+
|
|
13
|
+
const ORDERS: DatasetCatalogItem = {
|
|
14
|
+
id: 'orders',
|
|
15
|
+
label: '销售明细',
|
|
16
|
+
fields: [
|
|
17
|
+
{ name: 'customer', label: '客户', type: 'string' },
|
|
18
|
+
{ name: 'amount', label: '金额', type: 'number' }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const INVENTORY: DatasetCatalogItem = {
|
|
23
|
+
id: 'inventory',
|
|
24
|
+
label: '库存',
|
|
25
|
+
fields: [
|
|
26
|
+
{ name: 'sku', label: 'SKU', type: 'string' },
|
|
27
|
+
{ name: 'qty', label: '数量', type: 'number' }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('field-panel helpers', () => {
|
|
32
|
+
it('fieldTypeGlyph 按字段类型返回短标识', () => {
|
|
33
|
+
expect(fieldTypeGlyph('string')).toBe('文')
|
|
34
|
+
expect(fieldTypeGlyph('number')).toBe('#')
|
|
35
|
+
expect(fieldTypeGlyph('date')).toBe('日')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('filterDatasetsByQuery 按中文名或英文字段名过滤', () => {
|
|
39
|
+
const datasets = [ORDERS, INVENTORY]
|
|
40
|
+
|
|
41
|
+
expect(filterDatasetsByQuery(datasets, '客户')).toEqual([
|
|
42
|
+
{ ...ORDERS, fields: [ORDERS.fields[0]!] }
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
expect(filterDatasetsByQuery(datasets, 'qty')).toEqual([
|
|
46
|
+
{ ...INVENTORY, fields: [INVENTORY.fields[1]!] }
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
expect(filterDatasetsByQuery(datasets, ' ')).toEqual(datasets)
|
|
50
|
+
expect(filterDatasetsByQuery(datasets, '不存在')).toEqual([])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('字段拖拽负载编解码往返;非法负载返回 null', () => {
|
|
54
|
+
const payload = formatFieldDragPayload('orders', 'customer')
|
|
55
|
+
expect(payload).toBe('orders:customer')
|
|
56
|
+
expect(parseFieldDragPayload(payload)).toEqual({ datasetId: 'orders', fieldName: 'customer' })
|
|
57
|
+
|
|
58
|
+
// 字段名含冒号时按首个分隔符切分
|
|
59
|
+
expect(parseFieldDragPayload('orders:a:b')).toEqual({ datasetId: 'orders', fieldName: 'a:b' })
|
|
60
|
+
|
|
61
|
+
expect(parseFieldDragPayload('')).toBeNull()
|
|
62
|
+
expect(parseFieldDragPayload(':customer')).toBeNull()
|
|
63
|
+
expect(parseFieldDragPayload('orders:')).toBeNull()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import type { CellOverlayRect, GridOverlayLayout } from '../designer/cell-coords'
|
|
4
|
+
import { resolveBindingFloatPanelPosition } from '../designer/use-grid-overlay'
|
|
5
|
+
|
|
6
|
+
function cellRect(top: number, height = 24): CellOverlayRect {
|
|
7
|
+
const bottom = top + height
|
|
8
|
+
return {
|
|
9
|
+
left: 100,
|
|
10
|
+
top,
|
|
11
|
+
right: 180,
|
|
12
|
+
bottom,
|
|
13
|
+
width: 80,
|
|
14
|
+
height,
|
|
15
|
+
centerX: 140,
|
|
16
|
+
centerY: top + height / 2
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const layout: GridOverlayLayout = { offsetX: 0, offsetY: 77, viewW: 640, viewH: 400 }
|
|
21
|
+
|
|
22
|
+
describe('resolveBindingFloatPanelPosition', () => {
|
|
23
|
+
it('顶行格空间不足时改在单元格下方', () => {
|
|
24
|
+
const rect = cellRect(101)
|
|
25
|
+
const position = resolveBindingFloatPanelPosition(rect, layout, 320, 68, 640)
|
|
26
|
+
|
|
27
|
+
expect(position.placement).toBe('below')
|
|
28
|
+
expect(position.top).toBe(rect.bottom + 8)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('中部格优先在单元格上方', () => {
|
|
32
|
+
const rect = cellRect(200)
|
|
33
|
+
const position = resolveBindingFloatPanelPosition(rect, layout, 320, 68, 640)
|
|
34
|
+
|
|
35
|
+
expect(position.placement).toBe('above')
|
|
36
|
+
expect(position.top).toBe(rect.top - 8)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('水平居中并限制在宿主宽度内', () => {
|
|
40
|
+
const rect = cellRect(200)
|
|
41
|
+
const position = resolveBindingFloatPanelPosition(rect, layout, 320, 68, 340)
|
|
42
|
+
|
|
43
|
+
expect(position.left).toBe(170)
|
|
44
|
+
})
|
|
45
|
+
})
|