@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.
Files changed (212) hide show
  1. package/dist/components/report/binding-badge.js +100 -0
  2. package/dist/components/report/designer/cell-coords.js +46 -0
  3. package/dist/components/report/designer/col-widths.js +29 -0
  4. package/dist/components/report/designer/conditional-rules/helpers.js +139 -0
  5. package/dist/components/report/designer/drop-highlight-overlay.js +76 -0
  6. package/dist/components/report/designer/float-panel.js +422 -0
  7. package/dist/components/report/designer/role.js +17 -0
  8. package/dist/components/report/designer/rule-preview.js +33 -0
  9. package/dist/components/report/designer/rule-row.js +253 -0
  10. package/dist/components/report/designer/rules-dialog.js +118 -0
  11. package/dist/components/report/designer/topology-overlay.js +122 -0
  12. package/dist/components/report/designer/topology.js +64 -0
  13. package/dist/components/report/designer/use-grid-overlay.js +75 -0
  14. package/dist/components/report/designer-hub.js +177 -0
  15. package/dist/components/report/field-panel-helpers.js +33 -0
  16. package/dist/components/report/field-panel.js +132 -0
  17. package/dist/components/report/filter-bar.js +74 -0
  18. package/dist/components/report/hub-connection-form.js +145 -0
  19. package/dist/components/report/hub-dataset-editor.js +394 -0
  20. package/dist/components/report/preview-grid-size.js +32 -0
  21. package/dist/components/report/report-designer.js +393 -0
  22. package/dist/components/report/report-viewer.js +124 -0
  23. package/dist/components/report/style.d.ts +17 -0
  24. package/dist/components/report/style.js +18 -0
  25. package/dist/components/report/style2.css +874 -0
  26. package/dist/components/report/use-report-designer.js +382 -0
  27. package/dist/components/report/use-report-viewer.js +55 -0
  28. package/dist/{vue → components/sheet}/formula-bar.js +1 -1
  29. package/dist/{vue → components/sheet}/formula-suggest-list.js +1 -1
  30. package/dist/{vue → components/sheet}/import-file.js +1 -1
  31. package/dist/{vue → components/sheet}/insert-count-menu-item.js +1 -1
  32. package/dist/{vue → components/sheet}/insert-image.js +1 -1
  33. package/dist/{vue → components/sheet}/popup-helpers.js +1 -1
  34. package/dist/{vue → components/sheet}/popups/border-popup.js +31 -3
  35. package/dist/{vue → components/sheet}/popups/border-preset-glyph.js +1 -1
  36. package/dist/{vue → components/sheet}/popups/export-popup.js +2 -2
  37. package/dist/{vue → components/sheet}/popups/fill-color-popup.js +3 -3
  38. package/dist/{vue → components/sheet}/popups/find-popup.js +1 -1
  39. package/dist/{vue → components/sheet}/popups/font-color-popup.js +3 -3
  40. package/dist/{vue → components/sheet}/popups/font-size-popup.js +3 -3
  41. package/dist/{vue → components/sheet}/popups/import.worker.d.ts +1 -1
  42. package/dist/{vue → components/sheet}/popups/import.worker.js +1 -1
  43. package/dist/{vue → components/sheet}/popups/insert-image-popup.js +1 -1
  44. package/dist/{vue → components/sheet}/sheet-context-menu.js +7 -4
  45. package/dist/{vue → components/sheet}/sheet-tabs.js +1 -1
  46. package/dist/{vue → components/sheet}/sheet-toolbar.js +1 -1
  47. package/dist/{vue → components/sheet}/sheet.js +16 -1
  48. package/dist/{vue → components/sheet}/use-find-replace.js +1 -1
  49. package/dist/{vue → components/sheet}/use-formula-ref-select.js +1 -1
  50. package/dist/{vue → components/sheet}/use-sheet-grid.js +22 -4
  51. package/dist/{vue → components/sheet}/use-sheet-state.js +6 -5
  52. package/dist/{vue → components/sheet}/use-sheet-tabs-bar.js +1 -1
  53. package/dist/{vue → components/sheet}/use-tool-groups.js +2 -2
  54. package/dist/{vue → components/sheet}/use-tool-popup.js +2 -2
  55. package/dist/{vue → components/sheet}/use-toolbar-scroll.js +1 -1
  56. package/dist/index.d.ts +17 -3
  57. package/dist/index.js +13 -3
  58. package/dist/report/binding.d.ts +83 -0
  59. package/dist/report/binding.js +332 -0
  60. package/dist/report/connector.d.ts +66 -0
  61. package/dist/report/connector.js +159 -0
  62. package/dist/report/export-xlsx.d.ts +17 -0
  63. package/dist/report/export-xlsx.js +92 -0
  64. package/dist/report/filter-bar.d.ts +10 -0
  65. package/dist/report/filter-bar.js +31 -0
  66. package/dist/report/index.d.ts +12 -0
  67. package/dist/report/params.d.ts +13 -0
  68. package/dist/report/params.js +61 -0
  69. package/dist/report/render/aggregate.js +169 -0
  70. package/dist/report/render/builder.d.ts +12 -0
  71. package/dist/report/render/builder.js +139 -0
  72. package/dist/report/render/coordinate.d.ts +25 -0
  73. package/dist/report/render/coordinate.js +733 -0
  74. package/dist/report/render/index.d.ts +14 -0
  75. package/dist/report/render/index.js +21 -0
  76. package/dist/report/render/style-resolver.js +50 -0
  77. package/dist/report/render/template-index.d.ts +24 -0
  78. package/dist/report/render/template-index.js +137 -0
  79. package/dist/report/rules/evaluate.d.ts +25 -0
  80. package/dist/report/rules/evaluate.js +82 -0
  81. package/dist/report/rules/index.d.ts +2 -0
  82. package/dist/report/template.d.ts +63 -0
  83. package/dist/report/template.js +83 -0
  84. package/dist/report/types.d.ts +85 -0
  85. package/dist/tools/apply-style.js +29 -0
  86. package/dist/tools/builtin.js +17 -17
  87. package/dist/tools/context.d.ts +39 -6
  88. package/dist/tools/context.js +38 -1
  89. package/dist/types/index.d.ts +3 -2
  90. package/dist/types/report-designer.d.ts +38 -0
  91. package/dist/types/report-viewer.d.ts +38 -0
  92. package/dist/types/sheet.d.ts +21 -1
  93. package/package.json +6 -8
  94. package/src/components/report/__test__/conditional-rules-helpers.test.ts +88 -0
  95. package/src/components/report/__test__/field-panel-helpers.test.ts +65 -0
  96. package/src/components/report/__test__/float-panel-position.test.ts +45 -0
  97. package/src/components/report/__test__/preview-grid-size.test.ts +63 -0
  98. package/src/components/report/__test__/report-designer.test.ts +464 -0
  99. package/src/components/report/__test__/report-viewer.test.ts +357 -0
  100. package/src/components/report/__test__/topology.test.ts +143 -0
  101. package/src/components/report/__test__/use-report-designer.test.ts +697 -0
  102. package/src/components/report/binding-badge.ts +92 -0
  103. package/src/components/report/designer/cell-coords.ts +94 -0
  104. package/src/components/report/designer/col-widths.ts +56 -0
  105. package/src/components/report/designer/conditional-rules/helpers.ts +189 -0
  106. package/src/components/report/designer/drop-highlight-overlay.vue +74 -0
  107. package/src/components/report/designer/float-panel.vue +431 -0
  108. package/src/components/report/designer/role.ts +32 -0
  109. package/src/components/report/designer/rule-preview.vue +40 -0
  110. package/src/components/report/designer/rule-row.vue +252 -0
  111. package/src/components/report/designer/rules-dialog.vue +110 -0
  112. package/src/components/report/designer/topology-overlay.vue +127 -0
  113. package/src/components/report/designer/topology.ts +124 -0
  114. package/src/components/report/designer/use-grid-overlay.ts +113 -0
  115. package/src/components/report/designer-hub.vue +198 -0
  116. package/src/components/report/field-panel-helpers.ts +44 -0
  117. package/src/components/report/field-panel.vue +136 -0
  118. package/src/components/report/filter-bar.vue +77 -0
  119. package/src/components/report/hub-connection-form.vue +124 -0
  120. package/src/components/report/hub-dataset-editor.vue +369 -0
  121. package/src/components/report/index.ts +2 -0
  122. package/src/components/report/preview-grid-size.ts +33 -0
  123. package/src/components/report/report-designer.vue +420 -0
  124. package/src/components/report/report-viewer.vue +136 -0
  125. package/src/components/report/style-designer.scss +587 -0
  126. package/src/components/report/style-hub.scss +292 -0
  127. package/src/components/report/style-viewer.scss +104 -0
  128. package/src/components/report/style.scss +3 -0
  129. package/src/components/report/style.ts +20 -0
  130. package/src/components/report/use-report-designer.ts +596 -0
  131. package/src/components/report/use-report-viewer.ts +88 -0
  132. package/src/{vue → components/sheet}/__test__/formula-bar.test.ts +37 -18
  133. package/src/{vue → components/sheet}/__test__/insert-image-popup.test.ts +1 -1
  134. package/src/{vue → components/sheet}/__test__/insert-image.test.ts +1 -1
  135. package/src/{vue → components/sheet}/__test__/sheet-component.test.ts +77 -3
  136. package/src/{vue → components/sheet}/__test__/sheet-context-menu.test.ts +4 -3
  137. package/src/{vue → components/sheet}/formula-bar.vue +1 -1
  138. package/src/{vue → components/sheet}/insert-image.ts +1 -1
  139. package/src/{vue → components/sheet}/popup-helpers.ts +1 -1
  140. package/src/{vue → components/sheet}/popups/border-popup.vue +32 -4
  141. package/src/{vue → components/sheet}/popups/export-popup.vue +2 -2
  142. package/src/{vue → components/sheet}/popups/fill-color-popup.vue +3 -3
  143. package/src/{vue → components/sheet}/popups/find-popup.vue +1 -1
  144. package/src/{vue → components/sheet}/popups/font-color-popup.vue +3 -3
  145. package/src/{vue → components/sheet}/popups/font-size-popup.vue +3 -3
  146. package/src/{vue → components/sheet}/popups/insert-image-popup.vue +1 -1
  147. package/src/{vue → components/sheet}/sheet-context-menu.ts +10 -4
  148. package/src/{vue → components/sheet}/sheet-toolbar.vue +2 -2
  149. package/src/{vue → components/sheet}/sheet.vue +6 -3
  150. package/src/{vue → components/sheet}/use-find-replace.ts +1 -1
  151. package/src/{vue → components/sheet}/use-sheet-grid.ts +23 -5
  152. package/src/{vue → components/sheet}/use-sheet-state.ts +11 -6
  153. package/src/{vue → components/sheet}/use-tool-groups.ts +2 -2
  154. package/src/{vue → components/sheet}/use-tool-popup.ts +2 -2
  155. package/src/index.ts +24 -188
  156. package/src/report/__test__/binding.test.ts +320 -0
  157. package/src/report/__test__/connector.test.ts +239 -0
  158. package/src/report/__test__/export-xlsx.test.ts +118 -0
  159. package/src/report/__test__/filter-bar.test.ts +58 -0
  160. package/src/report/__test__/params.test.ts +156 -0
  161. package/src/report/__test__/render.test.ts +830 -0
  162. package/src/report/__test__/rules.test.ts +131 -0
  163. package/src/report/__test__/template.test.ts +366 -0
  164. package/src/report/binding.ts +474 -0
  165. package/src/report/connector.ts +206 -0
  166. package/src/report/export-xlsx.ts +113 -0
  167. package/src/report/filter-bar.ts +36 -0
  168. package/src/report/index.ts +13 -0
  169. package/src/report/params.ts +96 -0
  170. package/src/report/render/__test__/aggregate.test.ts +16 -0
  171. package/src/report/render/__test__/coordinate.test.ts +405 -0
  172. package/src/report/render/aggregate.ts +261 -0
  173. package/src/report/render/builder.ts +211 -0
  174. package/src/report/render/coordinate.ts +1239 -0
  175. package/src/report/render/index.ts +30 -0
  176. package/src/report/render/style-resolver.ts +68 -0
  177. package/src/report/render/template-index.ts +200 -0
  178. package/src/report/rules/evaluate.ts +113 -0
  179. package/src/report/rules/index.ts +7 -0
  180. package/src/report/template.ts +145 -0
  181. package/src/report/types.ts +96 -0
  182. package/src/tools/__test__/apply-style.test.ts +129 -0
  183. package/src/tools/apply-style.ts +41 -0
  184. package/src/tools/builtin.ts +20 -20
  185. package/src/tools/context.ts +67 -4
  186. package/src/tools/index.ts +1 -0
  187. package/src/types/index.ts +2 -6
  188. package/src/types/report-designer.ts +38 -0
  189. package/src/types/report-viewer.ts +37 -0
  190. package/src/types/sheet.ts +26 -1
  191. /package/dist/{vue → components/sheet}/style.d.ts +0 -0
  192. /package/dist/{vue → components/sheet}/style.js +0 -0
  193. /package/dist/{vue → components/sheet}/style2.css +0 -0
  194. /package/dist/{vue → components/sheet}/use-formula-suggest.js +0 -0
  195. /package/src/{vue → components/sheet}/__test__/import-file.test.ts +0 -0
  196. /package/src/{vue → components/sheet}/__test__/insert-count-menu-item.test.ts +0 -0
  197. /package/src/{vue → components/sheet}/__test__/use-formula-ref-select.test.ts +0 -0
  198. /package/src/{vue → components/sheet}/__test__/use-formula-suggest.test.ts +0 -0
  199. /package/src/{vue → components/sheet}/formula-suggest-list.vue +0 -0
  200. /package/src/{vue → components/sheet}/import-file.ts +0 -0
  201. /package/src/{vue → components/sheet}/index.ts +0 -0
  202. /package/src/{vue → components/sheet}/insert-count-menu-item.vue +0 -0
  203. /package/src/{vue → components/sheet}/parsing.ts +0 -0
  204. /package/src/{vue → components/sheet}/popups/border-preset-glyph.vue +0 -0
  205. /package/src/{vue → components/sheet}/popups/import.worker.ts +0 -0
  206. /package/src/{vue → components/sheet}/sheet-tabs.vue +0 -0
  207. /package/src/{vue → components/sheet}/style.scss +0 -0
  208. /package/src/{vue → components/sheet}/style.ts +0 -0
  209. /package/src/{vue → components/sheet}/use-formula-ref-select.ts +0 -0
  210. /package/src/{vue → components/sheet}/use-formula-suggest.ts +0 -0
  211. /package/src/{vue → components/sheet}/use-sheet-tabs-bar.ts +0 -0
  212. /package/src/{vue → components/sheet}/use-toolbar-scroll.ts +0 -0
@@ -1,10 +1,12 @@
1
+ import { parseRange } from '@veltra/sheet-core/core/address'
1
2
  import { Workbook } from '@veltra/sheet-core/core/workbook'
2
- import { ListTable } from '@visactor/vtable'
3
3
  import { afterEach, describe, expect, it } from 'vitest'
4
4
  import { createApp, h, nextTick, type App } from 'vue'
5
5
 
6
- import { USheet } from '../../index'
7
- import type { SheetExposed } from '../../types'
6
+ import { USheet } from '../../../index'
7
+ import { createSheetContext } from '../../../tools/context'
8
+ import type { SheetExposed } from '../../../types'
9
+ import UFormulaBar from '../formula-bar.vue'
8
10
 
9
11
  const apps: App[] = []
10
12
  const containers: HTMLElement[] = []
@@ -32,6 +34,26 @@ function mount(
32
34
  return { app, el }
33
35
  }
34
36
 
37
+ function mountFormulaBar(sheet: Workbook['activeSheet']) {
38
+ const el = document.createElement('div')
39
+ document.body.appendChild(el)
40
+ containers.push(el)
41
+ const exposed: { value: InstanceType<typeof UFormulaBar> | undefined } = { value: undefined }
42
+ const app = createApp({
43
+ render: () =>
44
+ h(UFormulaBar, {
45
+ sheet,
46
+ context: createSheetContext(sheet),
47
+ ref: (value: unknown) => {
48
+ exposed.value = value as InstanceType<typeof UFormulaBar> | undefined
49
+ }
50
+ })
51
+ })
52
+ app.mount(el)
53
+ apps.push(app)
54
+ return { el, exposed }
55
+ }
56
+
35
57
  function createWorkbook() {
36
58
  const workbook = new Workbook()
37
59
  workbook.addSheet('Sheet2')
@@ -397,15 +419,16 @@ describe('USheet 公式栏:函数补全与引用选择', () => {
397
419
  expect(sheet.getCellData({ row: 2, col: 0 })).toBeUndefined()
398
420
  expect(input.value).toBe('=SUM(')
399
421
 
400
- // 与 grid 选区测试同路径:selectCells 设视觉选区 + fire SELECTED_CELL
401
- const table = exposed.value!.getGrid()!.getTable()
402
- table.selectCells([{ start: { col: 1, row: 1 }, end: { col: 2, row: 2 } }])
403
- table.fireListeners(ListTable.EVENT_TYPE.SELECTED_CELL, { col: 2, row: 2 })
422
+ // 网格选区拦截 → handleRefSelect 路径(VTable 时序见 sheet-core selection-debug)
423
+ const { el: barEl, exposed: barExposed } = mountFormulaBar(sheet)
404
424
  await nextTick()
405
- expect(input.value).toBe('=SUM(A1:B2')
406
- expect(input.selectionStart).toBe('=SUM(A1:B2'.length)
407
- // intercept 不写模型选区
408
- expect(sheet.getSelection().activeCell).toEqual({ row: 2, col: 0 })
425
+ setFxText(barEl, '=SUM(')
426
+ await nextTick()
427
+ barExposed.value!.handleRefSelect(parseRange('A1:B2'))
428
+ await nextTick()
429
+ const barInput = fxInput(barEl)
430
+ expect(barInput.value).toBe('=SUM(A1:B2')
431
+ expect(barInput.selectionStart).toBe('=SUM(A1:B2'.length)
409
432
 
410
433
  // 非引用选择场景失焦提交
411
434
  setFxText(el, 'plain')
@@ -436,19 +459,15 @@ describe('USheet 公式栏:函数补全与引用选择', () => {
436
459
  })
437
460
 
438
461
  it('连续引用选择:=A1+ 后再插入 B1', async () => {
439
- const workbook = new Workbook()
440
- const sheet = workbook.activeSheet
441
- const exposed: { value: SheetExposed | undefined } = { value: undefined }
442
- const { el } = mount(() => ({ workbook, rows: 20, cols: 8 }), exposed)
462
+ const sheet = new Workbook().activeSheet
463
+ const { el, exposed } = mountFormulaBar(sheet)
443
464
  await nextTick()
444
465
  sheet.selectCell({ row: 2, col: 0 })
445
466
  await nextTick()
446
467
 
447
468
  setFxText(el, '=A1+')
448
469
  await nextTick()
449
- const table = exposed.value!.getGrid()!.getTable()
450
- table.selectCells([{ start: { col: 2, row: 1 }, end: { col: 2, row: 1 } }])
451
- table.fireListeners(ListTable.EVENT_TYPE.SELECTED_CELL, { col: 2, row: 1 })
470
+ exposed.value!.handleRefSelect(parseRange('B1'))
452
471
  await nextTick()
453
472
  expect(fxInput(el).value).toBe('=A1+B1')
454
473
  })
@@ -2,7 +2,7 @@ import { Sheet } from '@veltra/sheet-core/core/sheet'
2
2
  import { afterEach, describe, expect, it, vi } from 'vitest'
3
3
  import { createApp, h, type App } from 'vue'
4
4
 
5
- import { createSheetContext } from '../../tools/context'
5
+ import { createSheetContext } from '../../../tools/context'
6
6
  import USheetInsertImagePopup from '../popups/insert-image-popup.vue'
7
7
 
8
8
  const mocks = vi.hoisted(() => {
@@ -1,7 +1,7 @@
1
1
  import { Sheet } from '@veltra/sheet-core/core/sheet'
2
2
  import { afterEach, describe, expect, it, vi } from 'vitest'
3
3
 
4
- import { createSheetContext } from '../../tools/context'
4
+ import { createSheetContext } from '../../../tools/context'
5
5
  import {
6
6
  INSERT_IMAGE_ACCEPT,
7
7
  insertImageFromFile,
@@ -2,9 +2,9 @@ import { Workbook } from '@veltra/sheet-core/core/workbook'
2
2
  import { afterEach, describe, expect, it, vi } from 'vitest'
3
3
  import { createApp, h, nextTick, ref, type App } from 'vue'
4
4
 
5
- import { USheet } from '../../index'
6
- import { registerTool, unregisterTool } from '../../tools/registry'
7
- import type { SheetExposed } from '../../types'
5
+ import { USheet } from '../../../index'
6
+ import { registerTool, unregisterTool } from '../../../tools/registry'
7
+ import type { SheetExposed } from '../../../types'
8
8
 
9
9
  const apps: App[] = []
10
10
  const containers: HTMLElement[] = []
@@ -258,6 +258,80 @@ describe('USheet 组件', () => {
258
258
  expect(gridAfter).not.toBe(gridBefore)
259
259
  })
260
260
 
261
+ it('showRowHeader/showColHeader 为 false 时隐藏行号与列头', async () => {
262
+ const workbook = createWorkbook()
263
+ const exposed: { value: SheetExposed | undefined } = { value: undefined }
264
+ mount(
265
+ () => ({ workbook, rows: 8, cols: 4, showRowHeader: false, showColHeader: false }),
266
+ exposed
267
+ )
268
+ await nextTick()
269
+
270
+ workbook.activeSheet.setCellValue({ row: 0, col: 0 }, 'A1')
271
+ await nextTick()
272
+ const table = exposed.value!.getGrid()!.getTable()
273
+ expect(table.options.showHeader).toBe(false)
274
+ expect(table.options.rowSeriesNumber).toBeUndefined()
275
+ expect(table.columnHeaderLevelCount).toBe(0)
276
+ expect(table.rowCount).toBe(8)
277
+ expect(table.getCellValue(0, 0)).toBe('A1')
278
+ })
279
+
280
+ it('删除行后渲染行数跟随模型收缩(不被 props.rows 撑回)', async () => {
281
+ const workbook = createWorkbook()
282
+ const exposed: { value: SheetExposed | undefined } = { value: undefined }
283
+ mount(() => ({ workbook, rows: 10, cols: 6 }), exposed)
284
+ await nextTick()
285
+
286
+ const sheet = exposed.value!.getActiveSheet()
287
+ expect(sheet.rows).toBe(10)
288
+ expect(exposed.value!.getGrid()!.getTable().rowCount).toBe(11) // 列头 + 10
289
+
290
+ sheet.deleteRows(0, 2)
291
+ await nextTick()
292
+ await nextTick()
293
+
294
+ expect(sheet.rows).toBe(8)
295
+ expect(exposed.value!.getGrid()!.getTable().rowCount).toBe(9) // 列头 + 8
296
+ })
297
+
298
+ it('resolveCellStyle prop 传入 SheetGrid 视口渲染', async () => {
299
+ const workbook = createWorkbook()
300
+ const sheet = workbook.activeSheet
301
+ const exposed: { value: SheetExposed | undefined } = { value: undefined }
302
+ mount(
303
+ () => ({
304
+ workbook,
305
+ rows: 10,
306
+ cols: 6,
307
+ resolveCellStyle: (
308
+ addr: { row: number; col: number },
309
+ baseStyle?: { fill?: { color: string } }
310
+ ) => {
311
+ if (addr.row === 1 && addr.col === 1) {
312
+ return { ...baseStyle, fill: { color: '#FFE0E0' } }
313
+ }
314
+ return baseStyle
315
+ }
316
+ }),
317
+ exposed
318
+ )
319
+ await nextTick()
320
+
321
+ const addr = { row: 1, col: 1 }
322
+ sheet.setCellStyle({ start: addr, end: addr }, { fill: { color: '#CCCCCC' } })
323
+ await nextTick()
324
+
325
+ const table = exposed.value?.getGrid()?.getTable()
326
+ expect(table).toBeDefined()
327
+ const column = table!.getBodyColumnDefine(2, 2) as {
328
+ style?: (arg: { row: number; col: number; table: unknown }) => Record<string, unknown>
329
+ }
330
+ const rendered = column.style!({ row: 2, col: 2, table })
331
+ expect(rendered.bgColor).toBe('#FFE0E0')
332
+ expect(sheet.getCellStyle(addr)?.fill?.color).toBe('#CCCCCC')
333
+ })
334
+
261
335
  it('workbook prop 变更:tabs 与 grid 切到新工作簿', async () => {
262
336
  const workbook1 = createWorkbook()
263
337
  const workbook2 = new Workbook()
@@ -2,7 +2,7 @@ import { createRange } from '@veltra/sheet-core/core/address'
2
2
  import { Sheet } from '@veltra/sheet-core/core/sheet'
3
3
  import { describe, expect, it } from 'vitest'
4
4
 
5
- import { createSheetContext } from '../../tools/context'
5
+ import { createSheetContext } from '../../../tools/context'
6
6
  import {
7
7
  buildBodyMenus,
8
8
  buildColHeaderMenus,
@@ -36,8 +36,9 @@ describe('sheet-context-menu helpers', () => {
36
36
  expect(isColInSelection(ranges, 6)).toBe(false)
37
37
  })
38
38
 
39
- it('resolveRenderSize = max(props, sheet)', () => {
40
- expect(resolveRenderSize(100, 26, 120, 10)).toEqual({ rows: 120, cols: 26 })
39
+ it('resolveRenderSize:模型已声明时以模型为准(可低于 props)', () => {
40
+ expect(resolveRenderSize(100, 26, 120, 10)).toEqual({ rows: 120, cols: 10 })
41
+ expect(resolveRenderSize(100, 26, 29, 26)).toEqual({ rows: 29, cols: 26 })
41
42
  expect(resolveRenderSize(undefined, undefined, 0, 0)).toEqual({ rows: 100, cols: 26 })
42
43
  })
43
44
  })
@@ -86,7 +86,7 @@ import {
86
86
  watch
87
87
  } from 'vue'
88
88
 
89
- import type { SheetContext } from '../tools/context'
89
+ import type { SheetContext } from '../../tools/context'
90
90
  import UFormulaSuggestList from './formula-suggest-list.vue'
91
91
  import { insertRefText, isRefSelectContext } from './use-formula-ref-select'
92
92
  import {
@@ -1,7 +1,7 @@
1
1
  import { message } from '@veltra/desktop'
2
2
  import type { SheetImageType } from '@veltra/sheet-core/core/image'
3
3
 
4
- import type { SheetContext } from '../tools/context'
4
+ import type { SheetContext } from '../../tools/context'
5
5
 
6
6
  /** 文件选择 accept(工具栏弹层与右键菜单共用;不用裸 image/*) */
7
7
  export const INSERT_IMAGE_ACCEPT = '.png,.jpg,.jpeg,.gif,.svg,.webp'
@@ -2,7 +2,7 @@ import type { CellRange } from '@veltra/sheet-core/core/address'
2
2
  import type { BorderPreset } from '@veltra/sheet-core/core/style/border-presets'
3
3
  import { BORDER_STYLE_WIDTH, type BorderLineStyle } from '@veltra/sheet-core/core/style/types'
4
4
 
5
- import type { SheetContext } from '../tools/context'
5
+ import type { SheetContext } from '../../tools/context'
6
6
 
7
7
  /** 当前选区(ranges[0] 优先;无区域选区时用活动格单格) */
8
8
  export function currentRange(context: SheetContext): CellRange | null {
@@ -40,12 +40,13 @@ import { buildBorderPresetItems } from '@veltra/sheet-core/core/style/border-pre
40
40
  import {
41
41
  BORDER_STYLE_WIDTH,
42
42
  type BorderEdge,
43
- type BorderLineStyle
43
+ type BorderLineStyle,
44
+ type CellStylePatch
44
45
  } from '@veltra/sheet-core/core/style/types'
45
46
  import { bem } from '@veltra/utils'
46
47
  import { ref } from 'vue'
47
48
 
48
- import type { SheetContext } from '../../tools/context'
49
+ import type { SheetContext } from '../../../tools/context'
49
50
  import {
50
51
  BORDER_LINE_STYLES,
51
52
  BORDER_LINE_TITLES,
@@ -63,6 +64,7 @@ defineOptions({ name: 'USheetBorderPopup' })
63
64
  * 预设补丁由 core/style/border-presets 生成(含邻居格共享边同步,对齐
64
65
  * Excel「写入时同步、最近设置生效」);面板打开期间的写入由
65
66
  * use-tool-popup 事务包裹为一个 undo 单元。
67
+ * 整行/整列选区下,均匀预设(all/none/单侧)走 applyStyle → 行列默认样式。
66
68
  */
67
69
  const props = defineProps<{
68
70
  /** 工具上下文(写入走命令系统,天然可撤销) */
@@ -75,7 +77,27 @@ const cls = bem('sheet')
75
77
  const borderLineStyle = ref<BorderLineStyle>('thin')
76
78
  const borderColor = ref('#000000')
77
79
 
78
- /** 应用边框预设:一次 executeCommand(items 批量,含邻居同步补丁)= 一个 undo 单元 */
80
+ /** 均匀边框补丁(可写进行/列默认样式);outer/inner 几何相关 → null */
81
+ function uniformBorderPatch(preset: BorderPresetId, edge: BorderEdge): CellStylePatch | null {
82
+ switch (preset) {
83
+ case 'all':
84
+ return { border: { top: edge, right: edge, bottom: edge, left: edge } }
85
+ case 'none':
86
+ return { border: { top: null, right: null, bottom: null, left: null } }
87
+ case 'top':
88
+ return { border: { top: edge } }
89
+ case 'bottom':
90
+ return { border: { bottom: edge } }
91
+ case 'left':
92
+ return { border: { left: edge } }
93
+ case 'right':
94
+ return { border: { right: edge } }
95
+ default:
96
+ return null
97
+ }
98
+ }
99
+
100
+ /** 应用边框预设:整行/整列均匀预设走 applyStyle;其余逐格批量(含邻居同步) */
79
101
  function applyBorderPreset(preset: BorderPresetId): void {
80
102
  const range = currentRange(props.context)
81
103
  if (!range) return
@@ -84,8 +106,14 @@ function applyBorderPreset(preset: BorderPresetId): void {
84
106
  width: BORDER_STYLE_WIDTH[borderLineStyle.value],
85
107
  color: borderColor.value
86
108
  }
109
+ const target = props.context.resolveStyleTarget(range)
110
+ const uniform = target !== 'cell' ? uniformBorderPatch(preset, edge) : null
111
+ if (uniform) {
112
+ props.context.applyStyle(range, uniform)
113
+ return
114
+ }
87
115
  const items: SetCellStyleItem[] = buildBorderPresetItems(range, preset, edge, (addr) =>
88
- props.context.getCellStyle(addr)
116
+ props.context.getEffectiveStyle(addr)
89
117
  ).map(({ addr, patch }) => ({ addr, partial: patch }))
90
118
  if (items.length === 0) return
91
119
  props.context.executeCommand('sheet.command.set-cell-style', { items })
@@ -13,8 +13,8 @@
13
13
  import { message } from '@veltra/desktop'
14
14
  import { bem } from '@veltra/utils'
15
15
 
16
- import type { SheetContext } from '../../tools/context'
17
- import { exportSheetCsvFile, exportWorkbookFile } from '../../tools/download'
16
+ import type { SheetContext } from '../../../tools/context'
17
+ import { exportSheetCsvFile, exportWorkbookFile } from '../../../tools/download'
18
18
 
19
19
  defineOptions({ name: 'USheetExportPopup' })
20
20
 
@@ -6,7 +6,7 @@
6
6
  import { UPalette } from '@veltra/desktop'
7
7
  import { onMounted, ref } from 'vue'
8
8
 
9
- import type { SheetContext } from '../../tools/context'
9
+ import type { SheetContext } from '../../../tools/context'
10
10
  import { currentRange } from '../popup-helpers'
11
11
 
12
12
  defineOptions({ name: 'USheetFillColorPopup' })
@@ -26,7 +26,7 @@ const fillColor = ref('')
26
26
  // 打开时初始化为活动格当前填充色
27
27
  onMounted(() => {
28
28
  const active = props.context.getSelection().activeCell
29
- const style = active ? props.context.getCellStyle(active) : undefined
29
+ const style = active ? props.context.getEffectiveStyle(active) : undefined
30
30
  fillColor.value = style?.fill?.color ?? ''
31
31
  })
32
32
 
@@ -34,6 +34,6 @@ onMounted(() => {
34
34
  function applyFillColor(color: string): void {
35
35
  const range = currentRange(props.context)
36
36
  if (!range) return
37
- props.context.setCellStyle(range, color ? { fill: { color } } : { fill: {} })
37
+ props.context.applyStyle(range, color ? { fill: { color } } : { fill: {} })
38
38
  }
39
39
  </script>
@@ -72,7 +72,7 @@ import { UInput } from '@veltra/desktop'
72
72
  import type { Sheet } from '@veltra/sheet-core/core/sheet'
73
73
  import { bem } from '@veltra/utils'
74
74
 
75
- import type { SheetContext } from '../../tools/context'
75
+ import type { SheetContext } from '../../../tools/context'
76
76
  import { useFindReplace } from '../use-find-replace'
77
77
 
78
78
  defineOptions({ name: 'USheetFindPopup' })
@@ -6,7 +6,7 @@
6
6
  import { UPalette } from '@veltra/desktop'
7
7
  import { onMounted, ref } from 'vue'
8
8
 
9
- import type { SheetContext } from '../../tools/context'
9
+ import type { SheetContext } from '../../../tools/context'
10
10
  import { currentRange } from '../popup-helpers'
11
11
 
12
12
  defineOptions({ name: 'USheetFontColorPopup' })
@@ -26,7 +26,7 @@ const fontColor = ref('')
26
26
  // 打开时初始化为活动格当前字体色
27
27
  onMounted(() => {
28
28
  const active = props.context.getSelection().activeCell
29
- const style = active ? props.context.getCellStyle(active) : undefined
29
+ const style = active ? props.context.getEffectiveStyle(active) : undefined
30
30
  fontColor.value = style?.font?.color ?? ''
31
31
  })
32
32
 
@@ -34,6 +34,6 @@ onMounted(() => {
34
34
  function applyFontColor(color: string): void {
35
35
  const range = currentRange(props.context)
36
36
  if (!range) return
37
- props.context.setCellStyle(range, color ? { font: { color } } : { font: { color: null } })
37
+ props.context.applyStyle(range, color ? { font: { color } } : { font: { color: null } })
38
38
  }
39
39
  </script>
@@ -20,7 +20,7 @@ import { UScroll } from '@veltra/desktop'
20
20
  import { bem } from '@veltra/utils'
21
21
  import { onMounted, ref } from 'vue'
22
22
 
23
- import type { SheetContext } from '../../tools/context'
23
+ import type { SheetContext } from '../../../tools/context'
24
24
  import { currentRange } from '../popup-helpers'
25
25
 
26
26
  defineOptions({ name: 'USheetFontSizePopup' })
@@ -39,7 +39,7 @@ const currentSize = ref<number | undefined>(undefined)
39
39
 
40
40
  onMounted(() => {
41
41
  const active = props.context.getSelection().activeCell
42
- const style = active ? props.context.getCellStyle(active) : undefined
42
+ const style = active ? props.context.getEffectiveStyle(active) : undefined
43
43
  currentSize.value = style?.font?.size
44
44
  })
45
45
 
@@ -47,6 +47,6 @@ function applyFontSize(size: number): void {
47
47
  const range = currentRange(props.context)
48
48
  if (!range) return
49
49
  currentSize.value = size
50
- props.context.setCellStyle(range, { font: { size } })
50
+ props.context.applyStyle(range, { font: { size } })
51
51
  }
52
52
  </script>
@@ -15,7 +15,7 @@
15
15
  import { UFilePicker } from '@veltra/desktop'
16
16
  import { bem } from '@veltra/utils'
17
17
 
18
- import type { SheetContext } from '../../tools/context'
18
+ import type { SheetContext } from '../../../tools/context'
19
19
  import { INSERT_IMAGE_ACCEPT, insertImageFromFile } from '../insert-image'
20
20
 
21
21
  defineOptions({ name: 'USheetInsertImagePopup' })
@@ -2,8 +2,8 @@ import type { ContextmenuItem } from '@veltra/desktop'
2
2
  import { createRange, type CellAddress, type CellRange } from '@veltra/sheet-core/core/address'
3
3
  import { defineComponent, h } from 'vue'
4
4
 
5
- import type { SheetContext } from '../tools/context'
6
- import { defaultToolRegistry } from '../tools/registry'
5
+ import type { SheetContext } from '../../tools/context'
6
+ import { defaultToolRegistry } from '../../tools/registry'
7
7
  import InsertCountMenuItem from './insert-count-menu-item.vue'
8
8
  import { pickAndInsertImage } from './insert-image'
9
9
 
@@ -39,14 +39,20 @@ export function wholeColRange(col: number, rowCount: number): CellRange {
39
39
  return createRange({ row: 0, col }, { row: Math.max(rowCount - 1, 0), col })
40
40
  }
41
41
 
42
- /** 渲染尺寸 = max(props, sheet.rows/cols),与 SheetGrid 一致 */
42
+ /**
43
+ * 渲染尺寸:模型已声明时以模型为准(删行可低于 props);未声明时回落 props。
44
+ * 与 SheetGrid / useSheetGrid 一致——避免 ensureTableSize(props) 把删行结果撑回。
45
+ */
43
46
  export function resolveRenderSize(
44
47
  propsRows: number | undefined,
45
48
  propsCols: number | undefined,
46
49
  sheetRows: number,
47
50
  sheetCols: number
48
51
  ): { rows: number; cols: number } {
49
- return { rows: Math.max(propsRows ?? 100, sheetRows), cols: Math.max(propsCols ?? 26, sheetCols) }
52
+ return {
53
+ rows: sheetRows > 0 ? sheetRows : (propsRows ?? 100),
54
+ cols: sheetCols > 0 ? sheetCols : (propsCols ?? 26)
55
+ }
50
56
  }
51
57
 
52
58
  function insertCountRender(options: {
@@ -61,8 +61,8 @@ import { ArrowLeft, ArrowRight } from '@veltra/icons/normal'
61
61
  import { bem } from '@veltra/utils'
62
62
  import { toRef, useTemplateRef } from 'vue'
63
63
 
64
- import type { SheetTool } from '../tools/registry'
65
- import type { ToolGroupView } from '../use-tool-groups'
64
+ import type { SheetTool } from '../../tools/registry'
65
+ import type { ToolGroupView } from './use-tool-groups'
66
66
  import { useToolbarScroll } from './use-toolbar-scroll'
67
67
 
68
68
  defineOptions({ name: 'USheetToolbar' })
@@ -77,8 +77,8 @@ import { UDropdown } from '@veltra/desktop'
77
77
  import { bem } from '@veltra/utils'
78
78
  import { computed, ref, useTemplateRef, watch } from 'vue'
79
79
 
80
- import type { SheetTool } from '../tools/registry'
81
- import type { SheetEmits, SheetProps, _SheetExposed } from '../types'
80
+ import type { SheetTool } from '../../tools/registry'
81
+ import type { SheetEmits, SheetProps, _SheetExposed } from '../../types'
82
82
  import UFormulaBar from './formula-bar.vue'
83
83
  import { pickAndImportFile } from './import-file'
84
84
  import USheetBorderPopup from './popups/border-popup.vue'
@@ -102,7 +102,10 @@ const props = withDefaults(defineProps<SheetProps>(), {
102
102
  cols: 26,
103
103
  showToolbar: true,
104
104
  showFormulaBar: true,
105
- showTabs: true
105
+ showTabs: true,
106
+ showRowHeader: true,
107
+ showColHeader: true,
108
+ readonly: false
106
109
  })
107
110
 
108
111
  const emit = defineEmits<SheetEmits>()
@@ -14,7 +14,7 @@ import {
14
14
  import type { Sheet } from '@veltra/sheet-core/core/sheet'
15
15
  import { computed, ref, shallowRef, watch } from 'vue'
16
16
 
17
- import type { SheetContext } from '../tools/context'
17
+ import type { SheetContext } from '../../tools/context'
18
18
 
19
19
  interface UseFindReplaceOptions {
20
20
  /** 目标 sheet(弹层存活期间固定;tab 切换会关闭并销毁弹层) */
@@ -4,8 +4,8 @@ import type { Sheet } from '@veltra/sheet-core/core/sheet'
4
4
  import { SheetGrid, type SheetGridContextMenuInfo } from '@veltra/sheet-core/grid/sheet-grid'
5
5
  import { onBeforeUnmount, onMounted, watch } from 'vue'
6
6
 
7
- import type { SheetContext } from '../tools/context'
8
- import type { SheetProps } from '../types'
7
+ import type { SheetContext } from '../../tools/context'
8
+ import type { SheetProps } from '../../types'
9
9
  import {
10
10
  buildBodyMenus,
11
11
  buildColHeaderMenus,
@@ -109,11 +109,19 @@ export function useSheetGrid(options: UseSheetGridOptions) {
109
109
  const el = document.createElement('div')
110
110
  el.className = 'u-sheet__grid-instance'
111
111
  host.appendChild(el)
112
+ // 模型已声明尺寸时传入模型值,避免删行后被 props 经 ensureTableSize 撑回
113
+ const { rows, cols } = resolveRenderSize(props.rows, props.cols, sheet.rows, sheet.cols)
112
114
  const grid = new SheetGrid({
113
115
  container: el,
114
116
  sheet,
115
- rows: props.rows,
116
- cols: props.cols,
117
+ rows,
118
+ cols,
119
+ readonly: props.readonly,
120
+ showRowHeader: props.showRowHeader,
121
+ showColHeader: props.showColHeader,
122
+ resolveDisplayValue: props.resolveDisplayValue,
123
+ resolveCellStyle: props.resolveCellStyle,
124
+ resolveCellRenderer: props.resolveCellRenderer,
117
125
  onContextMenu: handleContextMenu,
118
126
  onEditStart: (addr) => formulaBarRef.value?.mirrorGridEdit(addr),
119
127
  onEditEnd: (addr) => formulaBarRef.value?.exitMirror(addr),
@@ -233,7 +241,17 @@ export function useSheetGrid(options: UseSheetGridOptions) {
233
241
 
234
242
  onMounted(activateGrid)
235
243
 
236
- watch(() => [props.rows, props.cols], rebuildGrid)
244
+ // props 尺寸显式增大时先扩张模型,再重建(删行缩小不走这条路径)
245
+ watch(
246
+ () =>
247
+ [props.rows, props.cols, props.readonly, props.showRowHeader, props.showColHeader] as const,
248
+ ([nextRows, nextCols]) => {
249
+ const sheet = getActiveSheet()
250
+ if (nextRows != null) sheet.ensureTableSize(nextRows, 0)
251
+ if (nextCols != null) sheet.ensureTableSize(0, nextCols)
252
+ rebuildGrid()
253
+ }
254
+ )
237
255
 
238
256
  onBeforeUnmount(() => {
239
257
  invalidateAll()
@@ -2,9 +2,10 @@ import type { Sheet } from '@veltra/sheet-core/core/sheet'
2
2
  import { Workbook } from '@veltra/sheet-core/core/workbook'
3
3
  import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
4
4
 
5
- import { createSheetContext } from '../tools/context'
6
- import { defaultToolRegistry } from '../tools/registry'
7
- import type { SheetProps } from '../types'
5
+ import { createSheetContext } from '../../tools/context'
6
+ import { defaultToolRegistry } from '../../tools/registry'
7
+ import type { SheetProps } from '../../types'
8
+ import { resolveRenderSize } from './sheet-context-menu'
8
9
 
9
10
  /**
10
11
  * useSheetState 与宿主组件的协作钩子。
@@ -44,10 +45,14 @@ export function useSheetState(props: SheetProps, emit: Emit, hooks: SheetStateHo
44
45
  () => sheetList.value[activeIndex.value] ?? workbook.value.activeSheet
45
46
  )
46
47
 
47
- /** 工具上下文:动态解析活动 sheet 与当前工作簿,tab 切换 / workbook prop 切换后自动指向最新值 */
48
+ /** 工具上下文:动态解析活动 sheet / 工作簿 / 渲染尺寸,tab 切换后自动指向最新值 */
48
49
  const context = createSheetContext(
49
50
  () => activeSheet.value,
50
- () => workbook.value
51
+ () => workbook.value,
52
+ {
53
+ resolveGridSize: () =>
54
+ resolveRenderSize(props.rows, props.cols, activeSheet.value.rows, activeSheet.value.cols)
55
+ }
51
56
  )
52
57
 
53
58
  /** 工具栏状态版本号:visible/disabled/active 是 (ctx) => boolean 纯函数,状态源变化时 bump 触发重算 */
@@ -80,7 +85,7 @@ export function useSheetState(props: SheetProps, emit: Emit, hooks: SheetStateHo
80
85
  // 整表内容替换(导入 / undo/redo 回放):restore 静默不发 cell-change,
81
86
  // 状态源需 bump(grid 层自行订阅 content-reset 全量刷新,此处不重建)
82
87
  sheet.on('content-reset', scheduleBump),
83
- // 行列插入/删除 → 重建网格(渲染行列数 = max(props, sheet.rows/cols),
88
+ // 行列插入/删除 → 重建网格(渲染行列数跟随模型 sheet.rows/cols,
84
89
  // 数据/选区/冻结/行高随重建恢复;低频操作直接重建)
85
90
  sheet.on('structure-change', hooks.rebuildGrid)
86
91
  ]
@@ -1,7 +1,7 @@
1
1
  import { computed, type ComputedRef, type Ref } from 'vue'
2
2
 
3
- import type { SheetContext } from '../tools/context'
4
- import { defaultToolRegistry, type SheetTool } from '../tools/registry'
3
+ import type { SheetContext } from '../../tools/context'
4
+ import { defaultToolRegistry, type SheetTool } from '../../tools/registry'
5
5
 
6
6
  /** 工具栏按钮视图模型 */
7
7
  export interface ToolGroupItem {
@@ -1,7 +1,7 @@
1
1
  import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
2
2
 
3
- import type { SheetContext } from '../tools/context'
4
- import { defaultToolRegistry, type SheetTool } from '../tools/registry'
3
+ import type { SheetContext } from '../../tools/context'
4
+ import { defaultToolRegistry, type SheetTool } from '../../tools/registry'
5
5
 
6
6
  /** 模板 ref 只读形态 */
7
7
  type ElRef = { readonly value: HTMLElement | null | undefined }