@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.
- 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/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 +2 -2
- 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 +6 -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/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 +2 -2
- 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
|
@@ -6,11 +6,15 @@ import {
|
|
|
6
6
|
createReportBinding,
|
|
7
7
|
formatBindingPlaceholder,
|
|
8
8
|
formatCellAddress,
|
|
9
|
+
inferColParentCandidate,
|
|
9
10
|
inferDropPreset,
|
|
11
|
+
inferParentsForPreset,
|
|
10
12
|
inferReportPreset,
|
|
13
|
+
inferRowParentCandidate,
|
|
11
14
|
isExpansionBandRow,
|
|
12
15
|
parseCellAddress,
|
|
13
16
|
presetBindingPatch,
|
|
17
|
+
presetStillApplies,
|
|
14
18
|
setBindingCatalog
|
|
15
19
|
} from '../binding'
|
|
16
20
|
import type { DatasetCatalogItem, ReportBinding } from '../types'
|
|
@@ -180,7 +184,32 @@ describe('report binding', () => {
|
|
|
180
184
|
expand: 'none',
|
|
181
185
|
rowParent: { row: 1, col: 0 }
|
|
182
186
|
})
|
|
183
|
-
).
|
|
187
|
+
).toBe('subtotal')
|
|
188
|
+
|
|
189
|
+
expect(
|
|
190
|
+
inferReportPreset({
|
|
191
|
+
dataset: 'orders',
|
|
192
|
+
field: 'amount',
|
|
193
|
+
aggregate: 'sum',
|
|
194
|
+
expand: 'none',
|
|
195
|
+
colParent: { row: 0, col: 1 }
|
|
196
|
+
})
|
|
197
|
+
).toBe('subtotal')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('presetStillApplies 同族内改聚合仍成立', () => {
|
|
201
|
+
const subtotal: ReportBinding = {
|
|
202
|
+
dataset: 'orders',
|
|
203
|
+
field: 'amount',
|
|
204
|
+
preset: 'subtotal',
|
|
205
|
+
aggregate: 'avg',
|
|
206
|
+
expand: 'none',
|
|
207
|
+
rowParent: { row: 1, col: 0 }
|
|
208
|
+
}
|
|
209
|
+
expect(presetStillApplies(subtotal)).toBe(true)
|
|
210
|
+
|
|
211
|
+
const broken = { ...subtotal, aggregate: 'list' as const, expand: 'down' as const }
|
|
212
|
+
expect(presetStillApplies(broken)).toBe(false)
|
|
184
213
|
})
|
|
185
214
|
|
|
186
215
|
it('inferDropPreset / isExpansionBandRow 落格推断', () => {
|
|
@@ -200,6 +229,79 @@ describe('report binding', () => {
|
|
|
200
229
|
expect(inferDropPreset({ row: 2, col: 0 }, 'string', getBindingAt)).toBe('detail')
|
|
201
230
|
})
|
|
202
231
|
|
|
232
|
+
it('inferDropPreset:数值字段落在横向展开带正右相邻列也推断为小计', () => {
|
|
233
|
+
const colGroup = createReportBinding(ORDERS_DATASET, 'region')
|
|
234
|
+
colGroup.aggregate = 'group'
|
|
235
|
+
colGroup.expand = 'right'
|
|
236
|
+
|
|
237
|
+
const getBindingAt = (addr: CellAddress) => {
|
|
238
|
+
if (addr.row === 0 && addr.col === 1) return colGroup
|
|
239
|
+
return undefined
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
expect(inferDropPreset({ row: 0, col: 2 }, 'number', getBindingAt)).toBe('subtotal')
|
|
243
|
+
expect(inferDropPreset({ row: 0, col: 2 }, 'string', getBindingAt)).toBe('detail')
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('inferRowParentCandidate:同行向左优先分组头', () => {
|
|
247
|
+
const group = createReportBinding(ORDERS_DATASET, 'customer')
|
|
248
|
+
group.aggregate = 'group'
|
|
249
|
+
group.expand = 'down'
|
|
250
|
+
const detail = createReportBinding(ORDERS_DATASET, 'orderNo')
|
|
251
|
+
|
|
252
|
+
const getBindingAt = (addr: CellAddress) => {
|
|
253
|
+
if (addr.row === 1 && addr.col === 0) return group
|
|
254
|
+
if (addr.row === 1 && addr.col === 1) return detail
|
|
255
|
+
return undefined
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
expect(inferRowParentCandidate({ row: 1, col: 2 }, getBindingAt)).toEqual({ row: 1, col: 0 })
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('inferRowParentCandidate:下方跨列小计推到行分组头而不是同列 list', () => {
|
|
262
|
+
const group = createReportBinding(ORDERS_DATASET, 'customer')
|
|
263
|
+
group.aggregate = 'group'
|
|
264
|
+
group.expand = 'down'
|
|
265
|
+
const detail = createReportBinding(ORDERS_DATASET, 'amount')
|
|
266
|
+
|
|
267
|
+
const getBindingAt = (addr: CellAddress) => {
|
|
268
|
+
if (addr.row === 1 && addr.col === 0) return group
|
|
269
|
+
if (addr.row === 1 && addr.col === 2) return detail
|
|
270
|
+
return undefined
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
expect(
|
|
274
|
+
inferRowParentCandidate({ row: 2, col: 2 }, getBindingAt, { preferGroup: true })
|
|
275
|
+
).toEqual({ row: 1, col: 0 })
|
|
276
|
+
expect(inferParentsForPreset({ row: 2, col: 2 }, 'subtotal', getBindingAt)).toEqual({
|
|
277
|
+
rowParent: { row: 1, col: 0 }
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('inferColParentCandidate:右侧列小计同列向上推到列分组头', () => {
|
|
282
|
+
const colGroup = createReportBinding(ORDERS_DATASET, 'region')
|
|
283
|
+
colGroup.aggregate = 'group'
|
|
284
|
+
colGroup.expand = 'right'
|
|
285
|
+
const cross = createReportBinding(ORDERS_DATASET, 'amount')
|
|
286
|
+
cross.preset = 'cross'
|
|
287
|
+
cross.aggregate = 'sum'
|
|
288
|
+
cross.expand = 'none'
|
|
289
|
+
cross.colParent = { row: 0, col: 1 }
|
|
290
|
+
|
|
291
|
+
const getBindingAt = (addr: CellAddress) => {
|
|
292
|
+
if (addr.row === 0 && addr.col === 1) return colGroup
|
|
293
|
+
if (addr.row === 1 && addr.col === 1) return cross
|
|
294
|
+
return undefined
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
expect(
|
|
298
|
+
inferColParentCandidate({ row: 2, col: 1 }, getBindingAt, { preferGroup: true })
|
|
299
|
+
).toEqual({ row: 0, col: 1 })
|
|
300
|
+
expect(inferParentsForPreset({ row: 2, col: 1 }, 'subtotal', getBindingAt)).toEqual({
|
|
301
|
+
colParent: { row: 0, col: 1 }
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
|
|
203
305
|
it('formatCellAddress / parseCellAddress 支持多字母列互逆', () => {
|
|
204
306
|
const single = { row: 2, col: 1 }
|
|
205
307
|
expect(formatCellAddress(single)).toBe('B3')
|
|
@@ -84,6 +84,27 @@ describe('createHttpConnector', () => {
|
|
|
84
84
|
expect(calls[0]!.body).toEqual({ connection: CONNECTION, sql: SQL })
|
|
85
85
|
})
|
|
86
86
|
|
|
87
|
+
it('describe:服务端返回 label 时保留中文显示名', async () => {
|
|
88
|
+
stubFetch(async () => ({
|
|
89
|
+
ok: true,
|
|
90
|
+
fields: [
|
|
91
|
+
{ name: 'customer', label: '客户名称', type: 'string' },
|
|
92
|
+
{ name: 'amount', label: '金额', type: 'number' }
|
|
93
|
+
]
|
|
94
|
+
}))
|
|
95
|
+
const connector = createHttpConnector({ endpoint: ENDPOINT })
|
|
96
|
+
|
|
97
|
+
const result = await connector.describe(CONNECTION, SQL)
|
|
98
|
+
|
|
99
|
+
expect(result).toEqual({
|
|
100
|
+
ok: true,
|
|
101
|
+
data: [
|
|
102
|
+
{ name: 'customer', label: '客户名称', type: 'string' },
|
|
103
|
+
{ name: 'amount', label: '金额', type: 'number' }
|
|
104
|
+
]
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
87
108
|
it('query:POST {endpoint}/query,请求体 { connection, sql, values },返回 fields + rows', async () => {
|
|
88
109
|
const calls = stubFetch(async () => ({
|
|
89
110
|
ok: true,
|
|
@@ -60,10 +60,10 @@ function buildFilledSheet(): Sheet {
|
|
|
60
60
|
|
|
61
61
|
describe('exportFilledReportXlsx', () => {
|
|
62
62
|
it('导出保真 XLSX:值 / 表头样式 / 条件样式打平后随快照导出', async () => {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const filled = buildFilledSheet()
|
|
64
|
+
filled.setColWidth(0, 120)
|
|
65
|
+
filled.setColWidth(1, 140)
|
|
66
|
+
const buffer = await exportFilledReportXlsx(filled)
|
|
67
67
|
|
|
68
68
|
// 合法 ZIP(xlsx)字节
|
|
69
69
|
expect(buffer[0]).toBe(0x50)
|
|
@@ -725,4 +725,106 @@ describe('renderReport', () => {
|
|
|
725
725
|
])
|
|
726
726
|
)
|
|
727
727
|
})
|
|
728
|
+
|
|
729
|
+
it('跨列行小计按组求和,不是全表合计', () => {
|
|
730
|
+
const sheet = new Sheet()
|
|
731
|
+
sheet.setCells([{ addr: { row: 0, col: 0 }, data: { v: '客户' } }])
|
|
732
|
+
const groupParent = { row: 1, col: 0 }
|
|
733
|
+
|
|
734
|
+
const customerGroup = createReportBinding(ORDERS_DATASET, 'customer')
|
|
735
|
+
customerGroup.preset = 'groupHeader'
|
|
736
|
+
customerGroup.aggregate = 'group'
|
|
737
|
+
customerGroup.expand = 'down'
|
|
738
|
+
sheet.setCellMeta(groupParent, REPORT_META_NAMESPACE, customerGroup)
|
|
739
|
+
|
|
740
|
+
const orderNo = createReportBinding(ORDERS_DATASET, 'orderNo')
|
|
741
|
+
orderNo.rowParent = groupParent
|
|
742
|
+
sheet.setCellMeta({ row: 1, col: 1 }, REPORT_META_NAMESPACE, orderNo)
|
|
743
|
+
|
|
744
|
+
const amount = createReportBinding(ORDERS_DATASET, 'amount')
|
|
745
|
+
amount.rowParent = groupParent
|
|
746
|
+
sheet.setCellMeta({ row: 1, col: 2 }, REPORT_META_NAMESPACE, amount)
|
|
747
|
+
|
|
748
|
+
const subtotal = createReportBinding(ORDERS_DATASET, 'amount')
|
|
749
|
+
subtotal.preset = 'subtotal'
|
|
750
|
+
subtotal.aggregate = 'sum'
|
|
751
|
+
subtotal.expand = 'none'
|
|
752
|
+
subtotal.rowParent = groupParent
|
|
753
|
+
sheet.setCellMeta({ row: 2, col: 2 }, REPORT_META_NAMESPACE, subtotal)
|
|
754
|
+
|
|
755
|
+
const filled = renderReport(sheet.snapshot(), MOCK_DATA_RECORDS)
|
|
756
|
+
expect(cellValue(filled, 5, 2)).toBe(630)
|
|
757
|
+
expect(cellValue(filled, 10, 2)).toBe(1050)
|
|
758
|
+
expect(cellValue(filled, 5, 2)).not.toBe(4180)
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
it('跨列行小计误挂明细时仍按分组祖先过滤', () => {
|
|
762
|
+
const sheet = new Sheet()
|
|
763
|
+
sheet.setCells([{ addr: { row: 0, col: 0 }, data: { v: '客户' } }])
|
|
764
|
+
const groupParent = { row: 1, col: 0 }
|
|
765
|
+
const listAddr = { row: 1, col: 2 }
|
|
766
|
+
|
|
767
|
+
const customerGroup = createReportBinding(ORDERS_DATASET, 'customer')
|
|
768
|
+
customerGroup.preset = 'groupHeader'
|
|
769
|
+
customerGroup.aggregate = 'group'
|
|
770
|
+
customerGroup.expand = 'down'
|
|
771
|
+
sheet.setCellMeta(groupParent, REPORT_META_NAMESPACE, customerGroup)
|
|
772
|
+
|
|
773
|
+
const amount = createReportBinding(ORDERS_DATASET, 'amount')
|
|
774
|
+
amount.rowParent = groupParent
|
|
775
|
+
sheet.setCellMeta(listAddr, REPORT_META_NAMESPACE, amount)
|
|
776
|
+
|
|
777
|
+
const subtotal = createReportBinding(ORDERS_DATASET, 'amount')
|
|
778
|
+
subtotal.preset = 'subtotal'
|
|
779
|
+
subtotal.aggregate = 'sum'
|
|
780
|
+
subtotal.expand = 'none'
|
|
781
|
+
subtotal.rowParent = listAddr
|
|
782
|
+
sheet.setCellMeta({ row: 2, col: 2 }, REPORT_META_NAMESPACE, subtotal)
|
|
783
|
+
|
|
784
|
+
const filled = renderReport(sheet.snapshot(), MOCK_DATA_RECORDS)
|
|
785
|
+
expect(cellValue(filled, 5, 2)).toBe(630)
|
|
786
|
+
expect(cellValue(filled, 10, 2)).toBe(1050)
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
it('交叉列小计仅 colParent 时按列分组求和,不是全表合计', () => {
|
|
790
|
+
const sheet = new Sheet()
|
|
791
|
+
const colGroupAddr = { row: 0, col: 1 }
|
|
792
|
+
const rowGroupAddr = { row: 1, col: 0 }
|
|
793
|
+
|
|
794
|
+
const categoryGroup = createReportBinding(SALES_MATRIX_DATASET, 'category')
|
|
795
|
+
categoryGroup.preset = 'groupHeader'
|
|
796
|
+
categoryGroup.aggregate = 'group'
|
|
797
|
+
categoryGroup.expand = 'right'
|
|
798
|
+
sheet.setCellMeta(colGroupAddr, REPORT_META_NAMESPACE, categoryGroup)
|
|
799
|
+
|
|
800
|
+
const regionGroup = createReportBinding(SALES_MATRIX_DATASET, 'region')
|
|
801
|
+
regionGroup.preset = 'groupHeader'
|
|
802
|
+
regionGroup.aggregate = 'group'
|
|
803
|
+
regionGroup.expand = 'down'
|
|
804
|
+
sheet.setCellMeta(rowGroupAddr, REPORT_META_NAMESPACE, regionGroup)
|
|
805
|
+
|
|
806
|
+
const cross = createReportBinding(SALES_MATRIX_DATASET, 'amount')
|
|
807
|
+
cross.preset = 'cross'
|
|
808
|
+
cross.aggregate = 'sum'
|
|
809
|
+
cross.expand = 'none'
|
|
810
|
+
cross.rowParent = rowGroupAddr
|
|
811
|
+
cross.colParent = colGroupAddr
|
|
812
|
+
sheet.setCellMeta({ row: 1, col: 1 }, REPORT_META_NAMESPACE, cross)
|
|
813
|
+
|
|
814
|
+
const colSubtotal = createReportBinding(SALES_MATRIX_DATASET, 'amount')
|
|
815
|
+
colSubtotal.preset = 'subtotal'
|
|
816
|
+
colSubtotal.aggregate = 'sum'
|
|
817
|
+
colSubtotal.expand = 'none'
|
|
818
|
+
colSubtotal.colParent = colGroupAddr
|
|
819
|
+
sheet.setCellMeta({ row: 2, col: 1 }, REPORT_META_NAMESPACE, colSubtotal)
|
|
820
|
+
|
|
821
|
+
const filled = renderReport(sheet.snapshot(), { 'sales-matrix': SALES_MATRIX_ROWS })
|
|
822
|
+
const officeTotal = SALES_MATRIX_ROWS.filter((row) => row.category === '办公设备').reduce(
|
|
823
|
+
(sum, row) => sum + (row.amount as number),
|
|
824
|
+
0
|
|
825
|
+
)
|
|
826
|
+
const grandTotal = SALES_MATRIX_ROWS.reduce((sum, row) => sum + (row.amount as number), 0)
|
|
827
|
+
expect(cellValue(filled, 5, 1)).toBe(officeTotal)
|
|
828
|
+
expect(cellValue(filled, 5, 1)).not.toBe(grandTotal)
|
|
829
|
+
})
|
|
728
830
|
})
|
|
@@ -105,6 +105,17 @@ describe('ReportTemplate version', () => {
|
|
|
105
105
|
expect(template.version).toBe(REPORT_TEMPLATE_VERSION)
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
+
it('createReportTemplate 继承 snapshot.colWidths', () => {
|
|
109
|
+
const sheet = new Sheet()
|
|
110
|
+
sheet.setColWidth(0, 120)
|
|
111
|
+
sheet.setColWidth(2, 90)
|
|
112
|
+
const template = createReportTemplate(sheet.snapshot(), [ORDERS_DATASET])
|
|
113
|
+
expect(template.colWidths).toEqual([
|
|
114
|
+
[0, 120],
|
|
115
|
+
[2, 90]
|
|
116
|
+
])
|
|
117
|
+
})
|
|
118
|
+
|
|
108
119
|
it('version 缺失时 assertCompatibleTemplateVersion 抛可读错误', () => {
|
|
109
120
|
const sheet = new Sheet()
|
|
110
121
|
const template = sheet.snapshot() as ReportTemplate
|