@veltra/sheet 2.2.0 → 2.3.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/report/binding-badge.js +45 -21
- package/dist/components/report/designer/col-widths.js +18 -4
- package/dist/components/report/designer/conditional-rules/helpers.js +3 -3
- package/dist/components/report/designer/float-panel.js +73 -36
- package/dist/components/report/designer/rule-row.js +111 -127
- package/dist/components/report/designer/rules-dialog.js +5 -22
- package/dist/components/report/hub-dataset-editor.js +16 -1
- package/dist/components/report/preview-grid-size.js +32 -0
- package/dist/components/report/report-designer.js +75 -25
- package/dist/components/report/report-viewer.js +38 -25
- package/dist/components/report/style2.css +44 -39
- package/dist/components/report/use-report-designer.js +34 -12
- package/dist/components/report/use-report-viewer.js +6 -1
- package/dist/components/sheet/popups/border-popup.js +30 -2
- package/dist/components/sheet/popups/fill-color-popup.js +4 -3
- package/dist/components/sheet/popups/font-color-popup.js +2 -2
- package/dist/components/sheet/popups/font-size-popup.js +2 -2
- package/dist/components/sheet/sheet-context-menu.js +6 -3
- package/dist/components/sheet/sheet.js +8 -0
- package/dist/components/sheet/use-sheet-grid.js +14 -4
- package/dist/components/sheet/use-sheet-state.js +3 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/report/binding.d.ts +49 -11
- package/dist/report/binding.js +186 -29
- package/dist/report/connector.js +1 -1
- package/dist/report/export-xlsx.d.ts +3 -5
- package/dist/report/export-xlsx.js +7 -10
- package/dist/report/index.d.ts +2 -2
- package/dist/report/render/aggregate.js +20 -8
- package/dist/report/template.d.ts +8 -4
- package/dist/report/template.js +5 -2
- package/dist/tools/apply-style.js +29 -0
- package/dist/tools/builtin.js +17 -17
- package/dist/tools/context.d.ts +38 -5
- package/dist/tools/context.js +38 -1
- package/dist/types/report-designer.d.ts +2 -0
- package/dist/types/report-viewer.d.ts +2 -2
- package/dist/types/sheet.d.ts +4 -0
- package/package.json +15 -6
- package/src/components/report/__test__/conditional-rules-helpers.test.ts +1 -0
- package/src/components/report/__test__/preview-grid-size.test.ts +63 -0
- package/src/components/report/__test__/report-designer.test.ts +76 -0
- package/src/components/report/__test__/report-viewer.test.ts +24 -1
- package/src/components/report/__test__/use-report-designer.test.ts +142 -10
- package/src/components/report/binding-badge.ts +39 -20
- package/src/components/report/designer/col-widths.ts +34 -11
- package/src/components/report/designer/conditional-rules/helpers.ts +6 -3
- package/src/components/report/designer/float-panel.vue +71 -27
- package/src/components/report/designer/rule-row.vue +67 -67
- package/src/components/report/designer/rules-dialog.vue +3 -19
- package/src/components/report/hub-dataset-editor.vue +19 -1
- package/src/components/report/preview-grid-size.ts +33 -0
- package/src/components/report/report-designer.vue +79 -15
- package/src/components/report/report-viewer.vue +37 -29
- package/src/components/report/style-designer.scss +45 -41
- package/src/components/report/use-report-designer.ts +63 -14
- package/src/components/report/use-report-viewer.ts +11 -2
- package/src/components/sheet/__test__/sheet-component.test.ts +37 -0
- package/src/components/sheet/__test__/sheet-context-menu.test.ts +3 -2
- package/src/components/sheet/popups/border-popup.vue +31 -3
- package/src/components/sheet/popups/fill-color-popup.vue +5 -4
- package/src/components/sheet/popups/font-color-popup.vue +2 -2
- package/src/components/sheet/popups/font-size-popup.vue +2 -2
- package/src/components/sheet/sheet-context-menu.ts +8 -2
- package/src/components/sheet/sheet.vue +2 -0
- package/src/components/sheet/use-sheet-grid.ts +17 -3
- package/src/components/sheet/use-sheet-state.ts +8 -3
- package/src/report/__test__/binding.test.ts +103 -1
- package/src/report/__test__/connector.test.ts +21 -0
- package/src/report/__test__/export-xlsx.test.ts +4 -4
- package/src/report/__test__/render.test.ts +102 -0
- package/src/report/__test__/template.test.ts +11 -0
- package/src/report/binding.ts +278 -47
- package/src/report/connector.ts +3 -2
- package/src/report/export-xlsx.ts +9 -14
- package/src/report/render/aggregate.ts +29 -8
- package/src/report/template.ts +8 -4
- package/src/tools/__test__/apply-style.test.ts +129 -0
- package/src/tools/apply-style.ts +41 -0
- package/src/tools/builtin.ts +18 -18
- package/src/tools/context.ts +67 -4
- package/src/tools/index.ts +1 -0
- package/src/types/report-designer.ts +2 -0
- package/src/types/report-viewer.ts +2 -2
- package/src/types/sheet.ts +4 -0
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
@update:model-value="onPreset"
|
|
22
22
|
/>
|
|
23
23
|
<u-select
|
|
24
|
+
v-if="showAggregateSelect"
|
|
24
25
|
size="small"
|
|
25
26
|
:class="[cls.e('select'), cls.em('select', 'aggregate')]"
|
|
26
27
|
:model-value="binding.aggregate"
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
@update:model-value="onAggregate"
|
|
29
30
|
/>
|
|
30
31
|
<u-select
|
|
32
|
+
v-if="showExpandSelect"
|
|
31
33
|
size="small"
|
|
32
34
|
:class="[cls.e('select'), cls.em('select', 'expand')]"
|
|
33
35
|
:model-value="binding.expand"
|
|
@@ -35,6 +37,7 @@
|
|
|
35
37
|
@update:model-value="onExpand"
|
|
36
38
|
/>
|
|
37
39
|
<u-button size="small" plain @click="emit('open-rules')">条件样式</u-button>
|
|
40
|
+
<u-button size="small" text type="danger" @click="emit('remove')">删除绑定</u-button>
|
|
38
41
|
<u-button
|
|
39
42
|
size="small"
|
|
40
43
|
text
|
|
@@ -138,11 +141,8 @@
|
|
|
138
141
|
</div>
|
|
139
142
|
</section>
|
|
140
143
|
|
|
141
|
-
<footer :class="cls.e('footer')">
|
|
142
|
-
<u-button
|
|
143
|
-
取消点选
|
|
144
|
-
</u-button>
|
|
145
|
-
<u-button size="small" type="danger" plain @click="emit('remove')">删除绑定</u-button>
|
|
144
|
+
<footer v-if="parentPickMode" :class="cls.e('footer')">
|
|
145
|
+
<u-button size="small" plain @click="emit('cancel-parent-pick')">取消点选</u-button>
|
|
146
146
|
</footer>
|
|
147
147
|
</div>
|
|
148
148
|
</div>
|
|
@@ -157,7 +157,9 @@ import { computed, nextTick, ref, toRef, watch } from 'vue'
|
|
|
157
157
|
import {
|
|
158
158
|
formatBindingPlaceholder,
|
|
159
159
|
formatCellAddress,
|
|
160
|
-
|
|
160
|
+
formatParentCandidateLabel,
|
|
161
|
+
inferReportPreset,
|
|
162
|
+
isReductionAggregate
|
|
161
163
|
} from '../../../report/binding'
|
|
162
164
|
import type {
|
|
163
165
|
ReportAggregate,
|
|
@@ -188,6 +190,7 @@ const props = defineProps<{
|
|
|
188
190
|
rowParentCandidates: CellAddress[]
|
|
189
191
|
colParentCandidates: CellAddress[]
|
|
190
192
|
resolveFieldLabel?: (datasetId: string, fieldName: string) => string
|
|
193
|
+
getBindingAt?: (addr: CellAddress) => ReportBinding | undefined
|
|
191
194
|
hostEl: HTMLElement | null
|
|
192
195
|
getGrid: () => SheetGrid | undefined
|
|
193
196
|
}>()
|
|
@@ -216,7 +219,7 @@ const presetOptions = computed(() => {
|
|
|
216
219
|
return options
|
|
217
220
|
})
|
|
218
221
|
|
|
219
|
-
const
|
|
222
|
+
const ALL_AGGREGATE_OPTIONS = [
|
|
220
223
|
{ value: 'list' as const, label: '明细' },
|
|
221
224
|
{ value: 'group' as const, label: '分组' },
|
|
222
225
|
{ value: 'sum' as const, label: '求和' },
|
|
@@ -226,6 +229,10 @@ const aggregateOptions = [
|
|
|
226
229
|
{ value: 'min' as const, label: '最小' }
|
|
227
230
|
]
|
|
228
231
|
|
|
232
|
+
const REDUCTION_AGGREGATE_OPTIONS = ALL_AGGREGATE_OPTIONS.filter((item) =>
|
|
233
|
+
isReductionAggregate(item.value)
|
|
234
|
+
)
|
|
235
|
+
|
|
229
236
|
const expandOptions = [
|
|
230
237
|
{ value: 'down' as const, label: '向下' },
|
|
231
238
|
{ value: 'right' as const, label: '向右' },
|
|
@@ -247,24 +254,59 @@ const currentPresetValue = computed(() => {
|
|
|
247
254
|
return inferReportPreset(props.binding) ?? CUSTOM_PRESET_VALUE
|
|
248
255
|
})
|
|
249
256
|
|
|
257
|
+
const showAggregateSelect = computed(() => {
|
|
258
|
+
const preset = currentPresetValue.value
|
|
259
|
+
return (
|
|
260
|
+
preset === CUSTOM_PRESET_VALUE ||
|
|
261
|
+
preset === 'subtotal' ||
|
|
262
|
+
preset === 'grandTotal' ||
|
|
263
|
+
preset === 'cross'
|
|
264
|
+
)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
const showExpandSelect = computed(() => {
|
|
268
|
+
const preset = currentPresetValue.value
|
|
269
|
+
return preset === CUSTOM_PRESET_VALUE || preset === 'groupHeader' || preset === 'detail'
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
const aggregateOptions = computed(() =>
|
|
273
|
+
currentPresetValue.value === CUSTOM_PRESET_VALUE
|
|
274
|
+
? ALL_AGGREGATE_OPTIONS
|
|
275
|
+
: REDUCTION_AGGREGATE_OPTIONS
|
|
276
|
+
)
|
|
277
|
+
|
|
250
278
|
const summaryText = computed(() => {
|
|
251
279
|
if (!props.binding) return ''
|
|
252
|
-
|
|
280
|
+
const parts = [formatBindingPlaceholder(props.binding, props.resolveFieldLabel)]
|
|
281
|
+
if (props.binding.rowParent && props.resolvedRowParentLabel !== '—') {
|
|
282
|
+
const parent = props.getBindingAt?.(props.binding.rowParent)
|
|
283
|
+
const name = parent
|
|
284
|
+
? (props.resolveFieldLabel?.(parent.dataset, parent.field) ?? parent.field)
|
|
285
|
+
: props.resolvedRowParentLabel
|
|
286
|
+
parts.push(`行父格 ${name} ${props.resolvedRowParentLabel}`)
|
|
287
|
+
}
|
|
288
|
+
if (props.binding.colParent && props.resolvedColParentLabel !== '—') {
|
|
289
|
+
const parent = props.getBindingAt?.(props.binding.colParent)
|
|
290
|
+
const name = parent
|
|
291
|
+
? (props.resolveFieldLabel?.(parent.dataset, parent.field) ?? parent.field)
|
|
292
|
+
: props.resolvedColParentLabel
|
|
293
|
+
parts.push(`列父格 ${name} ${props.resolvedColParentLabel}`)
|
|
294
|
+
}
|
|
295
|
+
return parts.join(' · ')
|
|
253
296
|
})
|
|
254
297
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
)
|
|
298
|
+
function candidateOption(addr: CellAddress): { value: string; label: string } {
|
|
299
|
+
const value = formatCellAddress(addr)
|
|
300
|
+
const binding = props.getBindingAt?.(addr)
|
|
301
|
+
return {
|
|
302
|
+
value,
|
|
303
|
+
label: binding ? formatParentCandidateLabel(addr, binding, props.resolveFieldLabel) : value
|
|
304
|
+
}
|
|
305
|
+
}
|
|
261
306
|
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
label: formatCellAddress(addr)
|
|
266
|
-
}))
|
|
267
|
-
)
|
|
307
|
+
const rowParentOptions = computed(() => props.rowParentCandidates.map(candidateOption))
|
|
308
|
+
|
|
309
|
+
const colParentOptions = computed(() => props.colParentCandidates.map(candidateOption))
|
|
268
310
|
|
|
269
311
|
const rowParentSelectValue = computed(() =>
|
|
270
312
|
props.binding?.rowParent ? formatCellAddress(props.binding.rowParent) : undefined
|
|
@@ -281,10 +323,18 @@ const panelStyle = computed(() => ({ left: `${panelLeft.value}px`, top: `${panel
|
|
|
281
323
|
watch(
|
|
282
324
|
() => [props.cell?.row, props.cell?.col] as const,
|
|
283
325
|
() => {
|
|
326
|
+
if (props.parentPickMode) return
|
|
284
327
|
expanded.value = false
|
|
285
328
|
}
|
|
286
329
|
)
|
|
287
330
|
|
|
331
|
+
watch(
|
|
332
|
+
() => props.parentPickMode,
|
|
333
|
+
(mode) => {
|
|
334
|
+
if (mode) expanded.value = true
|
|
335
|
+
}
|
|
336
|
+
)
|
|
337
|
+
|
|
288
338
|
watch(expanded, () => {
|
|
289
339
|
nextTick(() => updatePosition())
|
|
290
340
|
})
|
|
@@ -300,13 +350,7 @@ function onPreset(preset: ReportPreset | typeof CUSTOM_PRESET_VALUE): void {
|
|
|
300
350
|
}
|
|
301
351
|
|
|
302
352
|
function aggregateDefaultExpand(aggregate: ReportAggregate): ReportExpand {
|
|
303
|
-
return aggregate
|
|
304
|
-
aggregate === 'avg' ||
|
|
305
|
-
aggregate === 'count' ||
|
|
306
|
-
aggregate === 'max' ||
|
|
307
|
-
aggregate === 'min'
|
|
308
|
-
? 'none'
|
|
309
|
-
: 'down'
|
|
353
|
+
return isReductionAggregate(aggregate) ? 'none' : 'down'
|
|
310
354
|
}
|
|
311
355
|
|
|
312
356
|
function onAggregate(aggregate: ReportAggregate): void {
|
|
@@ -1,27 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="cls.b">
|
|
3
|
-
<span :class="cls.e('handle')" title="拖拽排序"
|
|
3
|
+
<span :class="cls.e('handle')" title="拖拽排序">⋮⋮</span>
|
|
4
4
|
|
|
5
5
|
<div :class="cls.e('main')">
|
|
6
|
-
<div :class="cls.e('
|
|
6
|
+
<div :class="cls.e('when')">
|
|
7
|
+
<span :class="cls.e('lead')">当</span>
|
|
7
8
|
<u-select
|
|
8
9
|
size="small"
|
|
9
10
|
:class="cls.e('field-select')"
|
|
10
|
-
:model-value="rule.field"
|
|
11
|
+
:model-value="rule.field ?? SELF_FIELD_VALUE"
|
|
11
12
|
:options="fieldOptions"
|
|
12
|
-
placeholder="求值字段"
|
|
13
13
|
@update:model-value="onEvalField"
|
|
14
14
|
/>
|
|
15
|
-
<u-select
|
|
16
|
-
size="small"
|
|
17
|
-
:class="cls.e('scope-select')"
|
|
18
|
-
:model-value="rule.scope ?? 'cell'"
|
|
19
|
-
:options="scopeOptions"
|
|
20
|
-
@update:model-value="onScope"
|
|
21
|
-
/>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<div :class="cls.e('condition')">
|
|
25
15
|
<u-select
|
|
26
16
|
size="small"
|
|
27
17
|
:class="cls.e('operator')"
|
|
@@ -29,7 +19,6 @@
|
|
|
29
19
|
:options="operatorOptions"
|
|
30
20
|
@update:model-value="onOperator"
|
|
31
21
|
/>
|
|
32
|
-
|
|
33
22
|
<div :class="cls.e('value')">
|
|
34
23
|
<template v-if="rule.operator === 'between'">
|
|
35
24
|
<u-number-input
|
|
@@ -80,56 +69,76 @@
|
|
|
80
69
|
@update:model-value="(v) => patchValue(v ?? 0)"
|
|
81
70
|
/>
|
|
82
71
|
</div>
|
|
72
|
+
<span :class="cls.e('lead')">时</span>
|
|
73
|
+
</div>
|
|
83
74
|
|
|
75
|
+
<div :class="cls.e('then')">
|
|
76
|
+
<span :class="cls.e('lead')">样式</span>
|
|
77
|
+
<span title="背景">
|
|
78
|
+
<u-palette
|
|
79
|
+
size="small"
|
|
80
|
+
:model-value="rule.style.fill?.color ?? ''"
|
|
81
|
+
@update:model-value="patchFillColor"
|
|
82
|
+
/>
|
|
83
|
+
</span>
|
|
84
|
+
<span title="文字">
|
|
85
|
+
<u-palette
|
|
86
|
+
size="small"
|
|
87
|
+
:model-value="rule.style.font?.color ?? ''"
|
|
88
|
+
@update:model-value="patchFontColor"
|
|
89
|
+
/>
|
|
90
|
+
</span>
|
|
91
|
+
<u-button-group>
|
|
92
|
+
<u-button
|
|
93
|
+
size="small"
|
|
94
|
+
:class="cls.e('toggle')"
|
|
95
|
+
:type="rule.style.font?.bold ? 'primary' : undefined"
|
|
96
|
+
plain
|
|
97
|
+
title="加粗"
|
|
98
|
+
@click="toggleBold"
|
|
99
|
+
>
|
|
100
|
+
B
|
|
101
|
+
</u-button>
|
|
102
|
+
<u-button
|
|
103
|
+
size="small"
|
|
104
|
+
:class="cls.e('toggle')"
|
|
105
|
+
:type="rule.style.font?.italic ? 'primary' : undefined"
|
|
106
|
+
plain
|
|
107
|
+
title="斜体"
|
|
108
|
+
@click="toggleItalic"
|
|
109
|
+
>
|
|
110
|
+
I
|
|
111
|
+
</u-button>
|
|
112
|
+
</u-button-group>
|
|
84
113
|
<u-report-rule-preview :class="cls.e('preview')" :rule="rule" />
|
|
85
|
-
</div>
|
|
86
114
|
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
:type="rule.style.font?.bold ? 'primary' : undefined"
|
|
104
|
-
plain
|
|
105
|
-
@click="toggleBold"
|
|
106
|
-
>
|
|
107
|
-
B
|
|
108
|
-
</u-button>
|
|
109
|
-
<u-button
|
|
110
|
-
size="small"
|
|
111
|
-
:class="cls.e('toggle')"
|
|
112
|
-
:type="rule.style.font?.italic ? 'primary' : undefined"
|
|
113
|
-
plain
|
|
114
|
-
@click="toggleItalic"
|
|
115
|
-
>
|
|
116
|
-
I
|
|
117
|
-
</u-button>
|
|
115
|
+
<span :class="cls.e('lead')">应用到</span>
|
|
116
|
+
<u-button-group>
|
|
117
|
+
<u-button
|
|
118
|
+
v-for="option in RULE_SCOPE_OPTIONS"
|
|
119
|
+
:key="option.value"
|
|
120
|
+
size="small"
|
|
121
|
+
plain
|
|
122
|
+
:type="(rule.scope ?? 'cell') === option.value ? 'primary' : undefined"
|
|
123
|
+
:title="
|
|
124
|
+
option.value === 'row' ? '染满物理输出行(交叉表会盖住同行所有列)' : '只改本格'
|
|
125
|
+
"
|
|
126
|
+
@click="onScope(option.value)"
|
|
127
|
+
>
|
|
128
|
+
{{ option.label }}
|
|
129
|
+
</u-button>
|
|
130
|
+
</u-button-group>
|
|
118
131
|
</div>
|
|
119
132
|
</div>
|
|
120
133
|
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
↓
|
|
125
|
-
</u-button>
|
|
126
|
-
<u-button size="small" text type="danger" @click="emit('remove')">删除</u-button>
|
|
127
|
-
</div>
|
|
134
|
+
<u-button size="small" text type="danger" :class="cls.e('remove')" @click="emit('remove')">
|
|
135
|
+
删除
|
|
136
|
+
</u-button>
|
|
128
137
|
</div>
|
|
129
138
|
</template>
|
|
130
139
|
|
|
131
140
|
<script lang="ts" setup>
|
|
132
|
-
import { UButton, UInput, UNumberInput, UPalette, USelect } from '@veltra/desktop'
|
|
141
|
+
import { UButton, UButtonGroup, UInput, UNumberInput, UPalette, USelect } from '@veltra/desktop'
|
|
133
142
|
import { bem } from '@veltra/utils'
|
|
134
143
|
import { computed } from 'vue'
|
|
135
144
|
|
|
@@ -142,6 +151,7 @@ import {
|
|
|
142
151
|
resolveEvalFieldType,
|
|
143
152
|
RULE_SCOPE_OPTIONS,
|
|
144
153
|
ruleEvalFieldOptions,
|
|
154
|
+
SELF_FIELD_VALUE,
|
|
145
155
|
writeBetweenValue
|
|
146
156
|
} from './conditional-rules/helpers'
|
|
147
157
|
import UReportRulePreview from './rule-preview.vue'
|
|
@@ -152,16 +162,9 @@ const props = defineProps<{
|
|
|
152
162
|
rule: ConditionalRule
|
|
153
163
|
bindingField: string
|
|
154
164
|
datasetFields: readonly DatasetField[]
|
|
155
|
-
index: number
|
|
156
|
-
total: number
|
|
157
165
|
}>()
|
|
158
166
|
|
|
159
|
-
const emit = defineEmits<{
|
|
160
|
-
'update:rule': [rule: ConditionalRule]
|
|
161
|
-
remove: []
|
|
162
|
-
'move-up': []
|
|
163
|
-
'move-down': []
|
|
164
|
-
}>()
|
|
167
|
+
const emit = defineEmits<{ 'update:rule': [rule: ConditionalRule]; remove: [] }>()
|
|
165
168
|
|
|
166
169
|
const cls = bem('report-rule-row')
|
|
167
170
|
|
|
@@ -171,9 +174,6 @@ const evalFieldType = computed(() =>
|
|
|
171
174
|
const operatorOptions = computed(() => operatorsForFieldType(evalFieldType.value))
|
|
172
175
|
const numericField = computed(() => isNumericFieldType(evalFieldType.value))
|
|
173
176
|
const fieldOptions = computed(() => ruleEvalFieldOptions(props.bindingField, props.datasetFields))
|
|
174
|
-
const scopeOptions = computed(() =>
|
|
175
|
-
RULE_SCOPE_OPTIONS.map((option) => ({ value: option.value, label: option.label }))
|
|
176
|
-
)
|
|
177
177
|
|
|
178
178
|
const betweenStart = computed(() => readBetweenValue(props.rule.value, 0, evalFieldType.value))
|
|
179
179
|
const betweenEnd = computed(() => readBetweenValue(props.rule.value, 1, evalFieldType.value))
|
|
@@ -191,7 +191,7 @@ function patchRule(patch: Partial<ConditionalRule>): void {
|
|
|
191
191
|
|
|
192
192
|
function onEvalField(field: string | undefined): void {
|
|
193
193
|
const next: Partial<ConditionalRule> = {}
|
|
194
|
-
if (field
|
|
194
|
+
if (!field) {
|
|
195
195
|
next.field = undefined
|
|
196
196
|
} else {
|
|
197
197
|
next.field = field
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
<u-dialog
|
|
3
3
|
v-model="visible"
|
|
4
4
|
title="条件样式"
|
|
5
|
-
style="width: min(
|
|
5
|
+
style="width: min(600px, 92vw); max-height: 85vh"
|
|
6
6
|
@close="onClose"
|
|
7
7
|
>
|
|
8
8
|
<div :class="cls.b">
|
|
9
|
-
<p :class="cls.e('hint')">
|
|
10
|
-
按列表顺序依次求值并合并样式。可指定求值字段(默认同绑定格字段);作用范围为「整行」时染满物理输出行(含横向展开列与静态格)。交叉表下整行高亮会覆盖同行所有列,明细行报表更合适。
|
|
11
|
-
</p>
|
|
9
|
+
<p :class="cls.e('hint')">规则按顺序叠加。整行高亮适合明细表,交叉表会染满整行。</p>
|
|
12
10
|
|
|
13
11
|
<ul v-if="draftItems.length" ref="parentRef" :class="cls.e('list')">
|
|
14
12
|
<li v-for="(item, index) in draftItems" :key="item.id" :class="cls.e('item')">
|
|
@@ -16,16 +14,12 @@
|
|
|
16
14
|
:rule="item.rule"
|
|
17
15
|
:binding-field="bindingField"
|
|
18
16
|
:dataset-fields="datasetFields"
|
|
19
|
-
:index="index"
|
|
20
|
-
:total="draftItems.length"
|
|
21
17
|
@update:rule="item.rule = $event"
|
|
22
18
|
@remove="removeRule(index)"
|
|
23
|
-
@move-up="moveRule(index, -1)"
|
|
24
|
-
@move-down="moveRule(index, 1)"
|
|
25
19
|
/>
|
|
26
20
|
</li>
|
|
27
21
|
</ul>
|
|
28
|
-
<p v-else :class="cls.e('empty')"
|
|
22
|
+
<p v-else :class="cls.e('empty')">还没有规则。</p>
|
|
29
23
|
|
|
30
24
|
<u-button size="small" plain @click="addRule">添加规则</u-button>
|
|
31
25
|
</div>
|
|
@@ -105,16 +99,6 @@ function removeRule(index: number): void {
|
|
|
105
99
|
draftItems.value = draftItems.value.filter((_, i) => i !== index)
|
|
106
100
|
}
|
|
107
101
|
|
|
108
|
-
function moveRule(index: number, delta: -1 | 1): void {
|
|
109
|
-
const target = index + delta
|
|
110
|
-
if (target < 0 || target >= draftItems.value.length) return
|
|
111
|
-
const next = [...draftItems.value]
|
|
112
|
-
const [item] = next.splice(index, 1)
|
|
113
|
-
if (!item) return
|
|
114
|
-
next.splice(target, 0, item)
|
|
115
|
-
draftItems.value = next
|
|
116
|
-
}
|
|
117
|
-
|
|
118
102
|
function confirm(): void {
|
|
119
103
|
emit('save', cloneRulesFromDraft(draftItems.value))
|
|
120
104
|
visible.value = false
|
|
@@ -219,11 +219,27 @@ watch(
|
|
|
219
219
|
{ immediate: true }
|
|
220
220
|
)
|
|
221
221
|
|
|
222
|
+
/** 立即把草稿 SQL 落入数据集(v-model 实时、change 失焦才发且提交有 debounce),供预览同步拿到最新 SQL */
|
|
223
|
+
function flushSqlDraft(datasetId: string): void {
|
|
224
|
+
if (sqlTimer) {
|
|
225
|
+
clearTimeout(sqlTimer)
|
|
226
|
+
sqlTimer = undefined
|
|
227
|
+
}
|
|
228
|
+
const current = dataset.value
|
|
229
|
+
if (!current || current.sql === sqlDraft.value) return
|
|
230
|
+
props.hub.updateDataset(datasetId, { sql: sqlDraft.value })
|
|
231
|
+
void runDescribe()
|
|
232
|
+
}
|
|
233
|
+
|
|
222
234
|
function onSqlChange(value: string): void {
|
|
223
235
|
sqlDraft.value = value
|
|
236
|
+
const datasetId = props.datasetId
|
|
224
237
|
if (sqlTimer) clearTimeout(sqlTimer)
|
|
225
238
|
sqlTimer = setTimeout(() => {
|
|
226
|
-
|
|
239
|
+
sqlTimer = undefined
|
|
240
|
+
// 防抖窗口内切换了数据集则丢弃,避免把旧草稿写进新数据集
|
|
241
|
+
if (props.datasetId !== datasetId) return
|
|
242
|
+
props.hub.updateDataset(datasetId, { sql: value })
|
|
227
243
|
void runDescribe()
|
|
228
244
|
}, 280)
|
|
229
245
|
}
|
|
@@ -271,6 +287,8 @@ const schemaRows = computed(() => {
|
|
|
271
287
|
// ---- 记录预览(按参数默认值经连接器取数) ----
|
|
272
288
|
|
|
273
289
|
async function runPreview(): Promise<void> {
|
|
290
|
+
// 编辑器 change 仅在失焦时触发且提交有 debounce,先同步落草稿 SQL,避免用旧 SQL 取数
|
|
291
|
+
flushSqlDraft(props.datasetId)
|
|
274
292
|
previewing.value = true
|
|
275
293
|
previewError.value = ''
|
|
276
294
|
const result = await props.hub.previewDataset(props.datasetId)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SheetSnapshot } from '@veltra/sheet-core'
|
|
2
|
+
|
|
3
|
+
export type PreviewGridSizeMode = 'template' | 'filled'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 报表预览网格尺寸:只铺到实际内容,不用设计态 canvas / 50×10 下限。
|
|
7
|
+
*
|
|
8
|
+
* - `template`:取数前的静态结构,按 cells/merges/meta 包围盒(忽略 snapshot.rows/cols,
|
|
9
|
+
* 那是设计网格声明尺寸,会带出空白格)
|
|
10
|
+
* - `filled`:行保留展开布局的 `snapshot.rows`(可能含无存值的空扩展行);列按包围盒收敛
|
|
11
|
+
* (Filled Report 的 `cols` 常被 `max(layout, template.cols)` 撑到设计态列数)
|
|
12
|
+
*/
|
|
13
|
+
export function previewGridSize(
|
|
14
|
+
snapshot: Pick<SheetSnapshot, 'cells' | 'merges' | 'meta' | 'rows'>,
|
|
15
|
+
mode: PreviewGridSizeMode
|
|
16
|
+
): { rows: number; cols: number } {
|
|
17
|
+
let maxRow = -1
|
|
18
|
+
let maxCol = -1
|
|
19
|
+
const grow = (row: number, col: number): void => {
|
|
20
|
+
if (row > maxRow) maxRow = row
|
|
21
|
+
if (col > maxCol) maxCol = col
|
|
22
|
+
}
|
|
23
|
+
for (const cell of snapshot.cells) grow(cell.row, cell.col)
|
|
24
|
+
for (const merge of snapshot.merges) grow(merge.end.row, merge.end.col)
|
|
25
|
+
for (const item of snapshot.meta ?? []) grow(item.row, item.col)
|
|
26
|
+
|
|
27
|
+
const boundRows = maxRow + 1
|
|
28
|
+
const boundCols = maxCol + 1
|
|
29
|
+
if (mode === 'filled') {
|
|
30
|
+
return { rows: Math.max(boundRows, snapshot.rows, 1), cols: Math.max(boundCols, 1) }
|
|
31
|
+
}
|
|
32
|
+
return { rows: Math.max(boundRows, 1), cols: Math.max(boundCols, 1) }
|
|
33
|
+
}
|