@veltra/sheet 2.2.0 → 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.
Files changed (84) hide show
  1. package/dist/components/report/binding-badge.js +45 -21
  2. package/dist/components/report/designer/col-widths.js +18 -4
  3. package/dist/components/report/designer/conditional-rules/helpers.js +3 -3
  4. package/dist/components/report/designer/float-panel.js +73 -36
  5. package/dist/components/report/designer/rule-row.js +111 -127
  6. package/dist/components/report/designer/rules-dialog.js +5 -22
  7. package/dist/components/report/preview-grid-size.js +32 -0
  8. package/dist/components/report/report-designer.js +75 -25
  9. package/dist/components/report/report-viewer.js +38 -25
  10. package/dist/components/report/style2.css +44 -39
  11. package/dist/components/report/use-report-designer.js +34 -12
  12. package/dist/components/report/use-report-viewer.js +6 -1
  13. package/dist/components/sheet/popups/border-popup.js +30 -2
  14. package/dist/components/sheet/popups/fill-color-popup.js +2 -2
  15. package/dist/components/sheet/popups/font-color-popup.js +2 -2
  16. package/dist/components/sheet/popups/font-size-popup.js +2 -2
  17. package/dist/components/sheet/sheet-context-menu.js +6 -3
  18. package/dist/components/sheet/sheet.js +8 -0
  19. package/dist/components/sheet/use-sheet-grid.js +14 -4
  20. package/dist/components/sheet/use-sheet-state.js +3 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +2 -2
  23. package/dist/report/binding.d.ts +49 -11
  24. package/dist/report/binding.js +186 -29
  25. package/dist/report/connector.js +1 -1
  26. package/dist/report/export-xlsx.d.ts +3 -5
  27. package/dist/report/export-xlsx.js +7 -10
  28. package/dist/report/index.d.ts +2 -2
  29. package/dist/report/render/aggregate.js +20 -8
  30. package/dist/report/template.d.ts +8 -4
  31. package/dist/report/template.js +5 -2
  32. package/dist/tools/apply-style.js +29 -0
  33. package/dist/tools/builtin.js +17 -17
  34. package/dist/tools/context.d.ts +38 -5
  35. package/dist/tools/context.js +38 -1
  36. package/dist/types/report-designer.d.ts +2 -0
  37. package/dist/types/report-viewer.d.ts +2 -2
  38. package/dist/types/sheet.d.ts +4 -0
  39. package/package.json +6 -6
  40. package/src/components/report/__test__/conditional-rules-helpers.test.ts +1 -0
  41. package/src/components/report/__test__/preview-grid-size.test.ts +63 -0
  42. package/src/components/report/__test__/report-designer.test.ts +76 -0
  43. package/src/components/report/__test__/report-viewer.test.ts +24 -1
  44. package/src/components/report/__test__/use-report-designer.test.ts +142 -10
  45. package/src/components/report/binding-badge.ts +39 -20
  46. package/src/components/report/designer/col-widths.ts +34 -11
  47. package/src/components/report/designer/conditional-rules/helpers.ts +6 -3
  48. package/src/components/report/designer/float-panel.vue +71 -27
  49. package/src/components/report/designer/rule-row.vue +67 -67
  50. package/src/components/report/designer/rules-dialog.vue +3 -19
  51. package/src/components/report/preview-grid-size.ts +33 -0
  52. package/src/components/report/report-designer.vue +79 -15
  53. package/src/components/report/report-viewer.vue +37 -29
  54. package/src/components/report/style-designer.scss +45 -41
  55. package/src/components/report/use-report-designer.ts +63 -14
  56. package/src/components/report/use-report-viewer.ts +11 -2
  57. package/src/components/sheet/__test__/sheet-component.test.ts +37 -0
  58. package/src/components/sheet/__test__/sheet-context-menu.test.ts +3 -2
  59. package/src/components/sheet/popups/border-popup.vue +31 -3
  60. package/src/components/sheet/popups/fill-color-popup.vue +2 -2
  61. package/src/components/sheet/popups/font-color-popup.vue +2 -2
  62. package/src/components/sheet/popups/font-size-popup.vue +2 -2
  63. package/src/components/sheet/sheet-context-menu.ts +8 -2
  64. package/src/components/sheet/sheet.vue +2 -0
  65. package/src/components/sheet/use-sheet-grid.ts +17 -3
  66. package/src/components/sheet/use-sheet-state.ts +8 -3
  67. package/src/report/__test__/binding.test.ts +103 -1
  68. package/src/report/__test__/connector.test.ts +21 -0
  69. package/src/report/__test__/export-xlsx.test.ts +4 -4
  70. package/src/report/__test__/render.test.ts +102 -0
  71. package/src/report/__test__/template.test.ts +11 -0
  72. package/src/report/binding.ts +278 -47
  73. package/src/report/connector.ts +3 -2
  74. package/src/report/export-xlsx.ts +9 -14
  75. package/src/report/render/aggregate.ts +29 -8
  76. package/src/report/template.ts +8 -4
  77. package/src/tools/__test__/apply-style.test.ts +129 -0
  78. package/src/tools/apply-style.ts +41 -0
  79. package/src/tools/builtin.ts +18 -18
  80. package/src/tools/context.ts +67 -4
  81. package/src/tools/index.ts +1 -0
  82. package/src/types/report-designer.ts +2 -0
  83. package/src/types/report-viewer.ts +2 -2
  84. package/src/types/sheet.ts +4 -0
@@ -1,25 +1,20 @@
1
1
  import { RULE_SCOPE_OPTIONS, coerceValueForOperator, isNumericFieldType, operatorsForFieldType, readBetweenValue, resolveEvalFieldType, ruleEvalFieldOptions, writeBetweenValue } from "./conditional-rules/helpers.js";
2
2
  import _sfc_main$1 from "./rule-preview.js";
3
- import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, unref, withCtx } from "vue";
4
- import { UButton, UInput, UNumberInput, UPalette, USelect } from "@veltra/desktop";
3
+ import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, renderList, toDisplayString, unref, withCtx } from "vue";
4
+ import { UButton, UButtonGroup, UInput, UNumberInput, UPalette, USelect } from "@veltra/desktop";
5
5
  import { bem } from "@veltra/utils";
6
6
  //#region src/components/report/designer/rule-row.vue
7
+ const _hoisted_1 = { title: "背景" };
8
+ const _hoisted_2 = { title: "文字" };
7
9
  const _sfc_main = /*@__PURE__*/ defineComponent({
8
10
  name: "UReportRuleRow",
9
11
  __name: "rule-row",
10
12
  props: {
11
13
  rule: {},
12
14
  bindingField: {},
13
- datasetFields: {},
14
- index: {},
15
- total: {}
15
+ datasetFields: {}
16
16
  },
17
- emits: [
18
- "update:rule",
19
- "remove",
20
- "move-up",
21
- "move-down"
22
- ],
17
+ emits: ["update:rule", "remove"],
23
18
  setup(__props, { emit: __emit }) {
24
19
  const props = __props;
25
20
  const emit = __emit;
@@ -28,10 +23,6 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
28
23
  const operatorOptions = computed(() => operatorsForFieldType(evalFieldType.value));
29
24
  const numericField = computed(() => isNumericFieldType(evalFieldType.value));
30
25
  const fieldOptions = computed(() => ruleEvalFieldOptions(props.bindingField, props.datasetFields));
31
- const scopeOptions = computed(() => RULE_SCOPE_OPTIONS.map((option) => ({
32
- value: option.value,
33
- label: option.label
34
- })));
35
26
  const betweenStart = computed(() => readBetweenValue(props.rule.value, 0, evalFieldType.value));
36
27
  const betweenEnd = computed(() => readBetweenValue(props.rule.value, 1, evalFieldType.value));
37
28
  const scalarNumber = computed(() => {
@@ -48,7 +39,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
48
39
  }
49
40
  function onEvalField(field) {
50
41
  const next = {};
51
- if (field === void 0) next.field = void 0;
42
+ if (!field) next.field = void 0;
52
43
  else next.field = field;
53
44
  const nextType = resolveEvalFieldType({
54
45
  ...props.rule,
@@ -114,153 +105,146 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
114
105
  createElementVNode("span", {
115
106
  class: normalizeClass(unref(cls).e("handle")),
116
107
  title: "拖拽排序"
117
- }, "≡", 2),
118
- createElementVNode("div", { class: normalizeClass(unref(cls).e("main")) }, [
119
- createElementVNode("div", { class: normalizeClass(unref(cls).e("meta")) }, [createVNode(unref(USelect), {
108
+ }, "⋮⋮", 2),
109
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("main")) }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("when")) }, [
110
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("lead")) }, "当", 2),
111
+ createVNode(unref(USelect), {
120
112
  size: "small",
121
113
  class: normalizeClass(unref(cls).e("field-select")),
122
- "model-value": __props.rule.field,
114
+ "model-value": __props.rule.field ?? unref(""),
123
115
  options: fieldOptions.value,
124
- placeholder: "求值字段",
125
116
  "onUpdate:modelValue": onEvalField
126
117
  }, null, 8, [
127
118
  "class",
128
119
  "model-value",
129
120
  "options"
130
- ]), createVNode(unref(USelect), {
121
+ ]),
122
+ createVNode(unref(USelect), {
131
123
  size: "small",
132
- class: normalizeClass(unref(cls).e("scope-select")),
133
- "model-value": __props.rule.scope ?? "cell",
134
- options: scopeOptions.value,
135
- "onUpdate:modelValue": onScope
124
+ class: normalizeClass(unref(cls).e("operator")),
125
+ "model-value": __props.rule.operator,
126
+ options: operatorOptions.value,
127
+ "onUpdate:modelValue": onOperator
136
128
  }, null, 8, [
137
129
  "class",
138
130
  "model-value",
139
131
  "options"
140
- ])], 2),
141
- createElementVNode("div", { class: normalizeClass(unref(cls).e("condition")) }, [
142
- createVNode(unref(USelect), {
132
+ ]),
133
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("value")) }, [__props.rule.operator === "between" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
134
+ numericField.value ? (openBlock(), createBlock(unref(UNumberInput), {
135
+ key: 0,
143
136
  size: "small",
144
- class: normalizeClass(unref(cls).e("operator")),
145
- "model-value": __props.rule.operator,
146
- options: operatorOptions.value,
147
- "onUpdate:modelValue": onOperator
148
- }, null, 8, [
149
- "class",
150
- "model-value",
151
- "options"
152
- ]),
153
- createElementVNode("div", { class: normalizeClass(unref(cls).e("value")) }, [__props.rule.operator === "between" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
154
- numericField.value ? (openBlock(), createBlock(unref(UNumberInput), {
155
- key: 0,
156
- size: "small",
157
- class: normalizeClass(unref(cls).e("between-input")),
158
- "model-value": betweenStart.value,
159
- "onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => patchBetween(0, v ?? 0))
160
- }, null, 8, ["class", "model-value"])) : (openBlock(), createBlock(unref(UInput), {
161
- key: 1,
162
- size: "small",
163
- class: normalizeClass(unref(cls).e("between-input")),
164
- "model-value": String(betweenStart.value),
165
- "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => patchBetween(0, String(v ?? "")))
166
- }, null, 8, ["class", "model-value"])),
167
- createElementVNode("span", { class: normalizeClass(unref(cls).e("between-sep")) }, "至", 2),
168
- numericField.value ? (openBlock(), createBlock(unref(UNumberInput), {
169
- key: 2,
170
- size: "small",
171
- class: normalizeClass(unref(cls).e("between-input")),
172
- "model-value": betweenEnd.value,
173
- "onUpdate:modelValue": _cache[2] || (_cache[2] = (v) => patchBetween(1, v ?? 0))
174
- }, null, 8, ["class", "model-value"])) : (openBlock(), createBlock(unref(UInput), {
175
- key: 3,
176
- size: "small",
177
- class: normalizeClass(unref(cls).e("between-input")),
178
- "model-value": String(betweenEnd.value),
179
- "onUpdate:modelValue": _cache[3] || (_cache[3] = (v) => patchBetween(1, String(v ?? "")))
180
- }, null, 8, ["class", "model-value"]))
181
- ], 64)) : __props.rule.operator === "contains" || !numericField.value && __props.rule.operator === "eq" ? (openBlock(), createBlock(unref(UInput), {
137
+ class: normalizeClass(unref(cls).e("between-input")),
138
+ "model-value": betweenStart.value,
139
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => patchBetween(0, v ?? 0))
140
+ }, null, 8, ["class", "model-value"])) : (openBlock(), createBlock(unref(UInput), {
182
141
  key: 1,
183
142
  size: "small",
184
- class: normalizeClass(unref(cls).e("scalar-input")),
185
- "model-value": String(__props.rule.value ?? ""),
186
- placeholder: "比较值",
187
- "onUpdate:modelValue": _cache[4] || (_cache[4] = (v) => patchValue(String(v ?? "")))
188
- }, null, 8, ["class", "model-value"])) : (openBlock(), createBlock(unref(UNumberInput), {
143
+ class: normalizeClass(unref(cls).e("between-input")),
144
+ "model-value": String(betweenStart.value),
145
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => patchBetween(0, String(v ?? "")))
146
+ }, null, 8, ["class", "model-value"])),
147
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("between-sep")) }, "至", 2),
148
+ numericField.value ? (openBlock(), createBlock(unref(UNumberInput), {
189
149
  key: 2,
190
150
  size: "small",
191
- class: normalizeClass(unref(cls).e("scalar-input")),
192
- "model-value": scalarNumber.value,
193
- "onUpdate:modelValue": _cache[5] || (_cache[5] = (v) => patchValue(v ?? 0))
194
- }, null, 8, ["class", "model-value"]))], 2),
195
- createVNode(_sfc_main$1, {
196
- class: normalizeClass(unref(cls).e("preview")),
197
- rule: __props.rule
198
- }, null, 8, ["class", "rule"])
199
- ], 2),
200
- createElementVNode("div", { class: normalizeClass(unref(cls).e("style")) }, [
201
- createElementVNode("span", { class: normalizeClass(unref(cls).e("style-label")) }, "背景", 2),
202
- createVNode(unref(UPalette), {
151
+ class: normalizeClass(unref(cls).e("between-input")),
152
+ "model-value": betweenEnd.value,
153
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = (v) => patchBetween(1, v ?? 0))
154
+ }, null, 8, ["class", "model-value"])) : (openBlock(), createBlock(unref(UInput), {
155
+ key: 3,
203
156
  size: "small",
204
- "model-value": __props.rule.style.fill?.color ?? "",
205
- "onUpdate:modelValue": patchFillColor
206
- }, null, 8, ["model-value"]),
207
- createElementVNode("span", { class: normalizeClass(unref(cls).e("style-label")) }, "字体", 2),
208
- createVNode(unref(UPalette), {
209
- size: "small",
210
- "model-value": __props.rule.style.font?.color ?? "",
211
- "onUpdate:modelValue": patchFontColor
212
- }, null, 8, ["model-value"]),
213
- createVNode(unref(UButton), {
157
+ class: normalizeClass(unref(cls).e("between-input")),
158
+ "model-value": String(betweenEnd.value),
159
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = (v) => patchBetween(1, String(v ?? "")))
160
+ }, null, 8, ["class", "model-value"]))
161
+ ], 64)) : __props.rule.operator === "contains" || !numericField.value && __props.rule.operator === "eq" ? (openBlock(), createBlock(unref(UInput), {
162
+ key: 1,
163
+ size: "small",
164
+ class: normalizeClass(unref(cls).e("scalar-input")),
165
+ "model-value": String(__props.rule.value ?? ""),
166
+ placeholder: "比较值",
167
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = (v) => patchValue(String(v ?? "")))
168
+ }, null, 8, ["class", "model-value"])) : (openBlock(), createBlock(unref(UNumberInput), {
169
+ key: 2,
170
+ size: "small",
171
+ class: normalizeClass(unref(cls).e("scalar-input")),
172
+ "model-value": scalarNumber.value,
173
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = (v) => patchValue(v ?? 0))
174
+ }, null, 8, ["class", "model-value"]))], 2),
175
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("lead")) }, "时", 2)
176
+ ], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("then")) }, [
177
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("lead")) }, "样式", 2),
178
+ createElementVNode("span", _hoisted_1, [createVNode(unref(UPalette), {
179
+ size: "small",
180
+ "model-value": __props.rule.style.fill?.color ?? "",
181
+ "onUpdate:modelValue": patchFillColor
182
+ }, null, 8, ["model-value"])]),
183
+ createElementVNode("span", _hoisted_2, [createVNode(unref(UPalette), {
184
+ size: "small",
185
+ "model-value": __props.rule.style.font?.color ?? "",
186
+ "onUpdate:modelValue": patchFontColor
187
+ }, null, 8, ["model-value"])]),
188
+ createVNode(unref(UButtonGroup), null, {
189
+ default: withCtx(() => [createVNode(unref(UButton), {
214
190
  size: "small",
215
191
  class: normalizeClass(unref(cls).e("toggle")),
216
192
  type: __props.rule.style.font?.bold ? "primary" : void 0,
217
193
  plain: "",
194
+ title: "加粗",
218
195
  onClick: toggleBold
219
196
  }, {
220
- default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode(" B ", -1)])]),
197
+ default: withCtx(() => [..._cache[7] || (_cache[7] = [createTextVNode(" B ", -1)])]),
221
198
  _: 1
222
- }, 8, ["class", "type"]),
223
- createVNode(unref(UButton), {
199
+ }, 8, ["class", "type"]), createVNode(unref(UButton), {
224
200
  size: "small",
225
201
  class: normalizeClass(unref(cls).e("toggle")),
226
202
  type: __props.rule.style.font?.italic ? "primary" : void 0,
227
203
  plain: "",
204
+ title: "斜体",
228
205
  onClick: toggleItalic
229
206
  }, {
230
- default: withCtx(() => [..._cache[10] || (_cache[10] = [createTextVNode(" I ", -1)])]),
207
+ default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" I ", -1)])]),
231
208
  _: 1
232
- }, 8, ["class", "type"])
233
- ], 2)
234
- ], 2),
235
- createElementVNode("div", { class: normalizeClass(unref(cls).e("actions")) }, [
236
- createVNode(unref(UButton), {
237
- size: "small",
238
- text: "",
239
- disabled: __props.index === 0,
240
- onClick: _cache[6] || (_cache[6] = ($event) => emit("move-up"))
241
- }, {
242
- default: withCtx(() => [..._cache[11] || (_cache[11] = [createTextVNode("↑", -1)])]),
209
+ }, 8, ["class", "type"])]),
243
210
  _: 1
244
- }, 8, ["disabled"]),
245
- createVNode(unref(UButton), {
246
- size: "small",
247
- text: "",
248
- disabled: __props.index >= __props.total - 1,
249
- onClick: _cache[7] || (_cache[7] = ($event) => emit("move-down"))
250
- }, {
251
- default: withCtx(() => [..._cache[12] || (_cache[12] = [createTextVNode(" ↓ ", -1)])]),
252
- _: 1
253
- }, 8, ["disabled"]),
254
- createVNode(unref(UButton), {
255
- size: "small",
256
- text: "",
257
- type: "danger",
258
- onClick: _cache[8] || (_cache[8] = ($event) => emit("remove"))
259
- }, {
260
- default: withCtx(() => [..._cache[13] || (_cache[13] = [createTextVNode("删除", -1)])]),
211
+ }),
212
+ createVNode(_sfc_main$1, {
213
+ class: normalizeClass(unref(cls).e("preview")),
214
+ rule: __props.rule
215
+ }, null, 8, ["class", "rule"]),
216
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("lead")) }, "应用到", 2),
217
+ createVNode(unref(UButtonGroup), null, {
218
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(RULE_SCOPE_OPTIONS), (option) => {
219
+ return openBlock(), createBlock(unref(UButton), {
220
+ key: option.value,
221
+ size: "small",
222
+ plain: "",
223
+ type: (__props.rule.scope ?? "cell") === option.value ? "primary" : void 0,
224
+ title: option.value === "row" ? "染满物理输出行(交叉表会盖住同行所有列)" : "只改本格",
225
+ onClick: ($event) => onScope(option.value)
226
+ }, {
227
+ default: withCtx(() => [createTextVNode(toDisplayString(option.label), 1)]),
228
+ _: 2
229
+ }, 1032, [
230
+ "type",
231
+ "title",
232
+ "onClick"
233
+ ]);
234
+ }), 128))]),
261
235
  _: 1
262
236
  })
263
- ], 2)
237
+ ], 2)], 2),
238
+ createVNode(unref(UButton), {
239
+ size: "small",
240
+ text: "",
241
+ type: "danger",
242
+ class: normalizeClass(unref(cls).e("remove")),
243
+ onClick: _cache[6] || (_cache[6] = ($event) => emit("remove"))
244
+ }, {
245
+ default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode(" 删除 ", -1)])]),
246
+ _: 1
247
+ }, 8, ["class"])
264
248
  ], 2);
265
249
  };
266
250
  }
@@ -40,15 +40,6 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
40
40
  function removeRule(index) {
41
41
  draftItems.value = draftItems.value.filter((_, i) => i !== index);
42
42
  }
43
- function moveRule(index, delta) {
44
- const target = index + delta;
45
- if (target < 0 || target >= draftItems.value.length) return;
46
- const next = [...draftItems.value];
47
- const [item] = next.splice(index, 1);
48
- if (!item) return;
49
- next.splice(target, 0, item);
50
- draftItems.value = next;
51
- }
52
43
  function confirm() {
53
44
  emit("save", cloneRulesFromDraft(draftItems.value));
54
45
  visible.value = false;
@@ -62,7 +53,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
62
53
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => visible.value = $event),
63
54
  title: "条件样式",
64
55
  style: {
65
- "width": "min(960px, 92vw)",
56
+ "width": "min(600px, 92vw)",
66
57
  "max-height": "85vh"
67
58
  },
68
59
  onClose
@@ -82,7 +73,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
82
73
  _: 1
83
74
  })]),
84
75
  default: withCtx(() => [createElementVNode("div", { class: normalizeClass(unref(cls).b) }, [
85
- createElementVNode("p", { class: normalizeClass(unref(cls).e("hint")) }, " 按列表顺序依次求值并合并样式。可指定求值字段(默认同绑定格字段);作用范围为「整行」时染满物理输出行(含横向展开列与静态格)。交叉表下整行高亮会覆盖同行所有列,明细行报表更合适。 ", 2),
76
+ createElementVNode("p", { class: normalizeClass(unref(cls).e("hint")) }, "规则按顺序叠加。整行高亮适合明细表,交叉表会染满整行。", 2),
86
77
  draftItems.value.length ? (openBlock(), createElementBlock("ul", {
87
78
  key: 0,
88
79
  ref_key: "parentRef",
@@ -96,27 +87,19 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
96
87
  rule: item.rule,
97
88
  "binding-field": __props.bindingField,
98
89
  "dataset-fields": __props.datasetFields,
99
- index,
100
- total: draftItems.value.length,
101
90
  "onUpdate:rule": ($event) => item.rule = $event,
102
- onRemove: ($event) => removeRule(index),
103
- onMoveUp: ($event) => moveRule(index, -1),
104
- onMoveDown: ($event) => moveRule(index, 1)
91
+ onRemove: ($event) => removeRule(index)
105
92
  }, null, 8, [
106
93
  "rule",
107
94
  "binding-field",
108
95
  "dataset-fields",
109
- "index",
110
- "total",
111
96
  "onUpdate:rule",
112
- "onRemove",
113
- "onMoveUp",
114
- "onMoveDown"
97
+ "onRemove"
115
98
  ])], 2);
116
99
  }), 128))], 2)) : (openBlock(), createElementBlock("p", {
117
100
  key: 1,
118
101
  class: normalizeClass(unref(cls).e("empty"))
119
- }, "暂无规则,点击下方按钮添加。", 2)),
102
+ }, "还没有规则。", 2)),
120
103
  createVNode(unref(UButton), {
121
104
  size: "small",
122
105
  plain: "",
@@ -0,0 +1,32 @@
1
+ //#region src/components/report/preview-grid-size.ts
2
+ /**
3
+ * 报表预览网格尺寸:只铺到实际内容,不用设计态 canvas / 50×10 下限。
4
+ *
5
+ * - `template`:取数前的静态结构,按 cells/merges/meta 包围盒(忽略 snapshot.rows/cols,
6
+ * 那是设计网格声明尺寸,会带出空白格)
7
+ * - `filled`:行保留展开布局的 `snapshot.rows`(可能含无存值的空扩展行);列按包围盒收敛
8
+ * (Filled Report 的 `cols` 常被 `max(layout, template.cols)` 撑到设计态列数)
9
+ */
10
+ function previewGridSize(snapshot, mode) {
11
+ let maxRow = -1;
12
+ let maxCol = -1;
13
+ const grow = (row, col) => {
14
+ if (row > maxRow) maxRow = row;
15
+ if (col > maxCol) maxCol = col;
16
+ };
17
+ for (const cell of snapshot.cells) grow(cell.row, cell.col);
18
+ for (const merge of snapshot.merges) grow(merge.end.row, merge.end.col);
19
+ for (const item of snapshot.meta ?? []) grow(item.row, item.col);
20
+ const boundRows = maxRow + 1;
21
+ const boundCols = maxCol + 1;
22
+ if (mode === "filled") return {
23
+ rows: Math.max(boundRows, snapshot.rows, 1),
24
+ cols: Math.max(boundCols, 1)
25
+ };
26
+ return {
27
+ rows: Math.max(boundRows, 1),
28
+ cols: Math.max(boundCols, 1)
29
+ };
30
+ }
31
+ //#endregion
32
+ export { previewGridSize };
@@ -1,8 +1,8 @@
1
1
  import _sfc_main$1 from "../sheet/sheet.js";
2
- import { resolveGridDropAddress } from "./designer/cell-coords.js";
3
- import { applyGridColWidths, readGridColWidths } from "./designer/col-widths.js";
2
+ import { applyGridColWidths, applySheetColWidths, readGridColWidths } from "./designer/col-widths.js";
4
3
  import _sfc_main$2 from "./report-viewer.js";
5
4
  import _sfc_main$3 from "./designer-hub.js";
5
+ import { resolveGridDropAddress } from "./designer/cell-coords.js";
6
6
  import _sfc_main$4 from "./designer/drop-highlight-overlay.js";
7
7
  import _sfc_main$5 from "./designer/float-panel.js";
8
8
  import _sfc_main$6 from "./designer/rules-dialog.js";
@@ -10,7 +10,7 @@ import _sfc_main$7 from "./designer/topology-overlay.js";
10
10
  import { FIELD_DRAG_MIME, parseFieldDragPayload } from "./field-panel-helpers.js";
11
11
  import _sfc_main$8 from "./field-panel.js";
12
12
  import { useReportDesigner } from "./use-report-designer.js";
13
- import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, openBlock, ref, shallowRef, unref, useTemplateRef, watch, withCtx } from "vue";
13
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, onScopeDispose, openBlock, ref, renderList, shallowRef, toDisplayString, unref, useTemplateRef, watch, withCtx } from "vue";
14
14
  import { UButton, UDrawer } from "@veltra/desktop";
15
15
  import { bem } from "@veltra/utils";
16
16
  import { Workbook } from "@veltra/sheet-core";
@@ -26,12 +26,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
26
26
  template: {},
27
27
  workbook: {}
28
28
  },
29
- emits: ["update:connections"],
29
+ emits: ["update:connections", "datasets-change"],
30
30
  setup(__props, { expose: __expose, emit: __emit }) {
31
31
  const props = __props;
32
32
  const emit = __emit;
33
33
  const cls = bem("report-designer");
34
- /** 设计态网格尺寸(列宽捕获范围与之一致) */
34
+ /** 设计态网格默认尺寸(新建模板;已声明尺寸后以模型为准,避免删行后被 props 撑回) */
35
35
  const EMPTY_CONDITIONAL_RULES = [];
36
36
  /** `v-model:connections` 可写代理(连接为纯序列化对象,仅驻留内存) */
37
37
  const connections = computed({
@@ -43,8 +43,28 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
43
43
  connections
44
44
  });
45
45
  const { workbook, catalog, boundKeys, activeCell, selectionLabel, activeBinding, activeFieldType, activeDatasetFields, resolvedRowParentLabel, resolvedColParentLabel, parentPick, rowParentCandidates, colParentCandidates, bindingEntries, metaTick, getBindingAt, resolveFieldLabel, bindField, patchActiveBinding, startParentPick, cancelParentPick, pickParentAt, clearParent, removeActiveBinding, resolveCellStyle, resolveCellRenderer, getTemplate } = designer;
46
+ /** 渲染行列数跟随模型(删行后不再被固定 props 经 ensureTableSize 撑回) */
47
+ const gridRows = computed(() => {
48
+ const rows = workbook.value.activeSheet.rows;
49
+ return rows > 0 ? rows : DESIGN_ROWS;
50
+ });
51
+ const gridCols = computed(() => {
52
+ const cols = workbook.value.activeSheet.cols;
53
+ return cols > 0 ? cols : DESIGN_COLS;
54
+ });
46
55
  const hubVisible = ref(false);
47
56
  const rulesDialogVisible = ref(false);
57
+ const pickCandidateCells = computed(() => {
58
+ if (!parentPick.value) return [];
59
+ return parentPick.value.mode === "row" ? rowParentCandidates.value : colParentCandidates.value;
60
+ });
61
+ function onHubClose() {
62
+ hubVisible.value = false;
63
+ }
64
+ watch(hubVisible, (open) => {
65
+ if (!open) emit("datasets-change");
66
+ });
67
+ watch(() => designer.datasets.value, () => emit("datasets-change"), { deep: true });
48
68
  const sheetRef = useTemplateRef("sheetRef");
49
69
  const previewViewerRef = useTemplateRef("previewViewerRef");
50
70
  const gridHostRef = useTemplateRef("gridHostRef");
@@ -61,13 +81,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
61
81
  const previewTemplate = shallowRef(null);
62
82
  /** 预览工作簿自持(XLSX 导出需要拿到填充后的 sheet,05 票 workbook? prop 先例) */
63
83
  const previewWorkbook = new Workbook();
64
- /** 当前设计态列宽(sheet-core 列宽未进快照,经 VTable 运行时捕获;导出随快照写宽) */
84
+ /** 当前设计态列宽(与 Sheet 模型同步;getTemplate 经 snapshot.colWidths 吐出) */
65
85
  const designColWidths = ref([]);
66
86
  const exporting = ref(false);
67
- /** 从当前设计网格捕获列宽(网格未就绪则保留旧值) */
87
+ /** 从当前设计网格捕获列宽并写回 Sheet 模型(网格未就绪则保留旧值) */
68
88
  function captureDesignColWidths() {
69
- const widths = readGridColWidths(getDesignGrid(), Array.from({ length: DESIGN_COLS }, (_, col) => col));
70
- if (widths) designColWidths.value = widths;
89
+ const widths = readGridColWidths(getDesignGrid(), Array.from({ length: gridCols.value }, (_, col) => col));
90
+ if (!widths) return;
91
+ const custom = widths.filter(([, width]) => width > 0);
92
+ designColWidths.value = custom;
93
+ applySheetColWidths(workbook.value.activeSheet, custom);
71
94
  }
72
95
  /** 设计网格重建后补刷新与列宽(restore 不发 content-reset 的场景由 grid 自行订阅,这里只管列宽) */
73
96
  function syncDesignGridView() {
@@ -85,8 +108,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
85
108
  viewMode.value = mode;
86
109
  if (mode === "design") nextTick(syncDesignGridView);
87
110
  }
88
- watch(() => props.template, () => {
111
+ watch(() => props.template, (template) => {
89
112
  viewMode.value = "design";
113
+ designColWidths.value = template?.colWidths?.length ? template.colWidths.map(([col, width]) => [col, width]) : [];
114
+ if (designColWidths.value.length) applySheetColWidths(workbook.value.activeSheet, designColWidths.value);
115
+ nextTick(syncDesignGridView);
90
116
  });
91
117
  watch(activeCell, (cell) => {
92
118
  if (cell && parentPick.value) pickParentAt(cell);
@@ -94,6 +120,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
94
120
  watch(catalog, () => {
95
121
  nextTick(() => getDesignGrid()?.refresh());
96
122
  });
123
+ const offStructureChange = workbook.value.activeSheet.on("structure-change", () => {
124
+ nextTick(syncDesignGridView);
125
+ });
126
+ onScopeDispose(offStructureChange);
97
127
  async function exportPreviewXlsx() {
98
128
  if (!isPreview.value || exporting.value) return;
99
129
  const viewer = previewViewerRef.value;
@@ -146,7 +176,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
146
176
  sheetRef.value?.getContext().selectCell(dropAddr);
147
177
  } else bindField(payload.datasetId, payload.fieldName);
148
178
  }
149
- __expose({ getTemplate });
179
+ __expose({ getTemplate: () => {
180
+ captureDesignColWidths();
181
+ return getTemplate();
182
+ } });
150
183
  return (_ctx, _cache) => {
151
184
  return openBlock(), createElementBlock("div", { class: normalizeClass(unref(cls).b) }, [
152
185
  createElementVNode("div", { class: normalizeClass(unref(cls).e("toolbar")) }, [
@@ -155,7 +188,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
155
188
  plain: "",
156
189
  onClick: _cache[0] || (_cache[0] = ($event) => hubVisible.value = true)
157
190
  }, {
158
- default: withCtx(() => [..._cache[7] || (_cache[7] = [createTextVNode("数据中枢", -1)])]),
191
+ default: withCtx(() => [..._cache[6] || (_cache[6] = [createTextVNode("数据中枢", -1)])]),
159
192
  _: 1
160
193
  }),
161
194
  createElementVNode("div", {
@@ -168,7 +201,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
168
201
  plain: isPreview.value,
169
202
  onClick: _cache[1] || (_cache[1] = ($event) => setViewMode("design"))
170
203
  }, {
171
- default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" 设计模式 ", -1)])]),
204
+ default: withCtx(() => [..._cache[7] || (_cache[7] = [createTextVNode(" 设计模式 ", -1)])]),
172
205
  _: 1
173
206
  }, 8, ["type", "plain"]), createVNode(unref(UButton), {
174
207
  size: "small",
@@ -176,7 +209,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
176
209
  plain: !isPreview.value,
177
210
  onClick: _cache[2] || (_cache[2] = ($event) => setViewMode("preview"))
178
211
  }, {
179
- default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode(" 预览模式 ", -1)])]),
212
+ default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" 预览模式 ", -1)])]),
180
213
  _: 1
181
214
  }, 8, ["type", "plain"])], 2),
182
215
  isPreview.value ? (openBlock(), createBlock(unref(UButton), {
@@ -187,10 +220,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
187
220
  loading: exporting.value,
188
221
  onClick: exportPreviewXlsx
189
222
  }, {
190
- default: withCtx(() => [..._cache[10] || (_cache[10] = [createTextVNode(" 导出 XLSX ", -1)])]),
223
+ default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode(" 导出 XLSX ", -1)])]),
191
224
  _: 1
192
225
  }, 8, ["loading"])) : createCommentVNode("v-if", true),
193
- createElementVNode("span", { class: normalizeClass(unref(cls).e("hint")) }, " 在数据中枢配置连接与 SQL 数据集,再将左侧字段点击或拖拽到单元格完成绑定;选中已绑定格可通过悬浮卡片调整角色、聚合与条件样式。 ", 2)
226
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("hint")) }, " 在数据中枢配置连接与 SQL 数据集,再将左侧字段点击或拖拽到单元格完成绑定;选中已绑定格可通过悬浮卡片调整预设与条件样式。 ", 2)
194
227
  ], 2),
195
228
  !isPreview.value ? (openBlock(), createElementBlock("div", {
196
229
  key: 0,
@@ -220,14 +253,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
220
253
  ref: sheetRef,
221
254
  class: normalizeClass(unref(cls).e("sheet")),
222
255
  workbook: unref(workbook),
223
- rows: DESIGN_ROWS,
224
- cols: DESIGN_COLS,
256
+ rows: gridRows.value,
257
+ cols: gridCols.value,
225
258
  "show-tabs": false,
226
259
  "resolve-cell-style": unref(resolveCellStyle),
227
260
  "resolve-cell-renderer": unref(resolveCellRenderer)
228
261
  }, null, 8, [
229
262
  "class",
230
263
  "workbook",
264
+ "rows",
265
+ "cols",
231
266
  "resolve-cell-style",
232
267
  "resolve-cell-renderer"
233
268
  ]),
@@ -259,6 +294,21 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
259
294
  "dragging",
260
295
  "fallback-label"
261
296
  ]),
297
+ (openBlock(true), createElementBlock(Fragment, null, renderList(pickCandidateCells.value, (addr) => {
298
+ return openBlock(), createBlock(_sfc_main$4, {
299
+ key: `pick-${addr.row}-${addr.col}`,
300
+ cell: addr,
301
+ "host-el": gridHostEl.value,
302
+ "get-grid": getDesignGrid,
303
+ dragging: false,
304
+ "fallback-label": ""
305
+ }, null, 8, ["cell", "host-el"]);
306
+ }), 128)),
307
+ unref(parentPick) ? (openBlock(), createElementBlock("div", {
308
+ key: 0,
309
+ class: normalizeClass(unref(cls).e("pick-hint")),
310
+ role: "status"
311
+ }, toDisplayString(unref(parentPick).mode === "row" ? "点击分组头作为行方向父格" : "点击分组头作为列方向父格"), 3)) : createCommentVNode("v-if", true),
262
312
  createVNode(_sfc_main$5, {
263
313
  cell: unref(activeCell),
264
314
  binding: unref(activeBinding) ?? null,
@@ -268,6 +318,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
268
318
  "row-parent-candidates": unref(rowParentCandidates),
269
319
  "col-parent-candidates": unref(colParentCandidates),
270
320
  "resolve-field-label": unref(resolveFieldLabel),
321
+ "get-binding-at": unref(getBindingAt),
271
322
  "host-el": gridHostEl.value,
272
323
  "get-grid": getDesignGrid,
273
324
  onPatch: unref(patchActiveBinding),
@@ -285,6 +336,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
285
336
  "row-parent-candidates",
286
337
  "col-parent-candidates",
287
338
  "resolve-field-label",
339
+ "get-binding-at",
288
340
  "host-el",
289
341
  "onPatch",
290
342
  "onRemove",
@@ -300,29 +352,27 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
300
352
  ref: previewViewerRef,
301
353
  connector: props.connector,
302
354
  template: previewTemplate.value,
303
- workbook: unref(previewWorkbook),
304
- "col-widths": designColWidths.value
355
+ workbook: unref(previewWorkbook)
305
356
  }, null, 8, [
306
357
  "connector",
307
358
  "template",
308
- "workbook",
309
- "col-widths"
359
+ "workbook"
310
360
  ])], 2)) : createCommentVNode("v-if", true),
311
361
  createVNode(unref(UDrawer), {
312
362
  modelValue: hubVisible.value,
313
- "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => hubVisible.value = $event),
363
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => hubVisible.value = $event),
314
364
  class: normalizeClass(unref(cls).e("hub-drawer")),
315
365
  "show-close": ""
316
366
  }, {
317
367
  default: withCtx(() => [createVNode(_sfc_main$3, {
318
368
  hub: unref(designer),
319
- onClose: _cache[4] || (_cache[4] = ($event) => hubVisible.value = false)
369
+ onClose: onHubClose
320
370
  }, null, 8, ["hub"])]),
321
371
  _: 1
322
372
  }, 8, ["modelValue", "class"]),
323
373
  createVNode(_sfc_main$6, {
324
374
  modelValue: rulesDialogVisible.value,
325
- "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => rulesDialogVisible.value = $event),
375
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => rulesDialogVisible.value = $event),
326
376
  rules: unref(activeBinding)?.conditionalRules ?? EMPTY_CONDITIONAL_RULES,
327
377
  "binding-field": unref(activeBinding)?.field ?? "",
328
378
  "dataset-fields": unref(activeDatasetFields),