@veltra/desktop 1.0.9 → 1.0.11

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 (109) hide show
  1. package/dist/components/action/action-group.vue.d.ts +1 -1
  2. package/dist/components/action/action.vue.d.ts +3 -3
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +1 -1
  4. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  5. package/dist/components/batch-edit/style.js +2 -2
  6. package/dist/components/batch-edit/use-edit.js.map +1 -1
  7. package/dist/components/button/button.js.map +1 -1
  8. package/dist/components/button/button.vue.d.ts +1 -1
  9. package/dist/components/cascade/cascade.vue.d.ts +6 -6
  10. package/dist/components/cascade/use-select.js.map +1 -1
  11. package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
  12. package/dist/components/code-editor/code-editor.js +2 -0
  13. package/dist/components/code-editor/code-editor.js.map +1 -1
  14. package/dist/components/date-panel/date-panel.js.map +1 -1
  15. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  16. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  17. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  18. package/dist/components/drawer/drawer.js.map +1 -1
  19. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  20. package/dist/components/expression-editor/internal/editor-runtime/create-runtime.js.map +1 -1
  21. package/dist/components/expression-editor/internal/features/commands/register-command-packs.js.map +1 -1
  22. package/dist/components/expression-editor/internal/features/insertion/insertion-service.js.map +1 -1
  23. package/dist/components/expression-editor/use-decorators.js.map +1 -1
  24. package/dist/components/expression-editor/use-expression-drag-drop.js.map +1 -1
  25. package/dist/components/file-picker/file-picker.js.map +1 -1
  26. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  27. package/dist/components/file-viewer/file-viewer.js +195 -30
  28. package/dist/components/file-viewer/file-viewer.js.map +1 -1
  29. package/dist/components/file-viewer/previewers/docx-previewer.js +7 -5
  30. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -1
  31. package/dist/components/file-viewer/previewers/image-previewer.js +6 -6
  32. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -1
  33. package/dist/components/file-viewer/previewers/pdf-previewer.js +5 -3
  34. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -1
  35. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -1
  36. package/dist/components/file-viewer/style2.css +387 -136
  37. package/dist/components/form/form-model.js +2 -1
  38. package/dist/components/form/form-model.js.map +1 -1
  39. package/dist/components/grid/grid.js.map +1 -1
  40. package/dist/components/grid/use-responsive.js.map +1 -1
  41. package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
  42. package/dist/components/input/input.vue.d.ts +3 -3
  43. package/dist/components/list/list.js.map +1 -1
  44. package/dist/components/loading/directive.js.map +1 -1
  45. package/dist/components/menu/menu-item.js.map +1 -1
  46. package/dist/components/message/message2.js.map +1 -1
  47. package/dist/components/multi-select/multi-select.js.map +1 -1
  48. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  49. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +6 -6
  50. package/dist/components/notification/index.js.map +1 -1
  51. package/dist/components/notification/notification.vue.d.ts +1 -1
  52. package/dist/components/number-input/number-input.js.map +1 -1
  53. package/dist/components/number-input/number-input.vue.d.ts +1 -1
  54. package/dist/components/number-range-input/number-range-input.vue.d.ts +2 -2
  55. package/dist/components/palette/color-transform.js.map +1 -1
  56. package/dist/components/palette/palette-sv.js.map +1 -1
  57. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  58. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  59. package/dist/components/progress/progress.js +1 -1
  60. package/dist/components/progress/progress.js.map +1 -1
  61. package/dist/components/progress-nodes/progress-nodes.js.map +1 -1
  62. package/dist/components/progress-nodes/progress-nodes.vue.d.ts +3 -3
  63. package/dist/components/rich-text-editor/rich-text-editor.js.map +1 -1
  64. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +2 -2
  65. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  66. package/dist/components/scroll/scroll.js.map +1 -1
  67. package/dist/components/select/select.vue.d.ts +2 -2
  68. package/dist/components/slider/slider-thumb.js.map +1 -1
  69. package/dist/components/table/di.js.map +1 -1
  70. package/dist/components/table/style.js +1 -1
  71. package/dist/components/table/table-row.js +1 -2
  72. package/dist/components/table/table-row.js.map +1 -1
  73. package/dist/components/table/table.js +1 -2
  74. package/dist/components/table/table.js.map +1 -1
  75. package/dist/components/table/use-columns.js +4 -0
  76. package/dist/components/table/use-columns.js.map +1 -1
  77. package/dist/components/table/use-table.js +25 -0
  78. package/dist/components/table/use-table.js.map +1 -1
  79. package/dist/components/tabs/style2.css +3 -2
  80. package/dist/components/tabs/tabs-horizontal.js +2 -2
  81. package/dist/components/tabs/tabs-horizontal.js.map +1 -1
  82. package/dist/components/tabs/tabs-horizontal.vue.d.ts +14 -3
  83. package/dist/components/tabs/tabs-vertical.js +2 -2
  84. package/dist/components/tabs/tabs-vertical.js.map +1 -1
  85. package/dist/components/tabs/tabs-vertical.vue.d.ts +13 -3
  86. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  87. package/dist/components/text/text.js.map +1 -1
  88. package/dist/components/textarea/textarea.js.map +1 -1
  89. package/dist/components/textarea/textarea.vue.d.ts +3 -3
  90. package/dist/components/theme/theme.js.map +1 -1
  91. package/dist/components/tip/tip.vue.d.ts +2 -2
  92. package/dist/components/tree/tree-node2.js.map +1 -1
  93. package/dist/components/tree/tree.vue.d.ts +2 -2
  94. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  95. package/dist/components/tree-select/tree-select.vue.d.ts +4 -4
  96. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  97. package/package.json +13 -12
  98. package/src/components/code-editor/code-editor.vue +2 -0
  99. package/src/components/file-viewer/file-viewer.vue +250 -24
  100. package/src/components/file-viewer/previewers/docx-previewer.vue +4 -3
  101. package/src/components/file-viewer/previewers/image-previewer.vue +4 -5
  102. package/src/components/file-viewer/previewers/pdf-previewer.vue +4 -3
  103. package/src/components/file-viewer/style.scss +503 -161
  104. package/src/components/progress/progress.vue +1 -1
  105. package/src/components/table/di.ts +51 -53
  106. package/src/components/table/table.vue +0 -1
  107. package/src/components/tabs/style.scss +7 -4
  108. package/src/components/tabs/tabs-horizontal.vue +10 -7
  109. package/src/components/tabs/tabs-vertical.vue +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-tree-nodes.js","names":["TreeNode"],"sources":["../../../src/components/tree/use-tree-nodes.ts"],"sourcesContent":["import { Forest } from '@cat-kit/core'\nimport { computed, shallowRef, triggerRef, watch, type ShallowRef, type ComputedRef } from 'vue'\n\nimport type { TreeProps } from '../../types'\nimport { TreeNode } from './tree-node'\n\ninterface Options {\n props: TreeProps\n}\n\ninterface UseTreeNodesReturned {\n nodes: ShallowRef<TreeNode[]>\n forest: ComputedRef<Forest<Record<string, unknown>, any>>\n getFlattedNodes: () => void\n nodeDict: ComputedRef<Map<string | number, TreeNode>>\n /** 强制刷新 `nodes` 的订阅者(供字段级别变化但列表引用不变时手动触发)。 */\n triggerNodes: () => void\n}\n\nexport function useTreeNodes(options: Options): UseTreeNodesReturned {\n const { props } = options\n\n // forest / nodeDict 保持为 shallowRef 而非 computed:\n // - computed 会因为任一 prop 变化(尤其是内联 disabledNode 函数)而整棵重建,\n // 丢失 expanded/checked/visible 等运行时状态;\n // - 显式 watch 仅在真正影响结构的输入变化时才重建,行为可控、可测量。\n const forestRef = shallowRef<Forest<Record<string, unknown>, any>>(\n new Forest<Record<string, unknown>, any>({\n data: [],\n childrenKey: props.childrenKey ?? 'children'\n })\n )\n\n const nodeDictRef = shallowRef<Map<string | number, TreeNode>>(new Map())\n\n const nodes = shallowRef<TreeNode[]>([])\n\n function buildForest(): Forest<Record<string, unknown>, any> {\n const { disabledNode, expandAll = false, valueKey, labelKey } = props\n const childrenKey = props.childrenKey ?? 'children'\n\n function createNode(\n data: Record<string, any>,\n index: number,\n depth: number,\n parent?: TreeNode\n ): TreeNode {\n const node = new TreeNode({\n data,\n index,\n depth,\n parent,\n valueKey: valueKey!,\n labelKey: labelKey!\n })\n node.expanded = expandAll\n return node\n }\n\n return new Forest<Record<string, unknown>, any>({\n data: (props.data ?? []) as Record<string, unknown>[],\n childrenKey,\n createNode: disabledNode\n ? (data, index, depth, _f, parent) => {\n const node = createNode(data as Record<string, any>, index, depth, parent as TreeNode)\n if (data) {\n node.disabled = disabledNode(data as Record<string, any>, node) ?? false\n }\n return node\n }\n : (data, index, depth, _f, parent) =>\n createNode(data as Record<string, any>, index, depth, parent as TreeNode)\n })\n }\n\n function rebuildForest(): void {\n const nextForest = buildForest()\n const nextDict = new Map<string | number, TreeNode>()\n\n // 构建森林的同时一次 DFS 同步填充字典,\n // 避免旧版 “forest computed → nodeDict computed” 两次完整遍历。\n nextForest.dfs((node) => {\n nextDict.set(node.key, node)\n })\n\n forestRef.value = nextForest\n nodeDictRef.value = nextDict\n\n getFlattedNodes()\n }\n\n /** 按展开与可见性扁平化:仅一次 DFS。 */\n function getFlattedNodes(): void {\n const result: TreeNode[] = []\n const roots = forestRef.value.roots as TreeNode[]\n\n const stack: TreeNode[] = []\n for (let i = roots.length - 1; i >= 0; i--) {\n stack.push(roots[i]!)\n }\n\n while (stack.length) {\n const node = stack.pop()!\n if (!node.visible) continue\n result.push(node)\n if (node.expanded && node.children && node.children.length) {\n for (let i = node.children.length - 1; i >= 0; i--) {\n stack.push(node.children[i]!)\n }\n }\n }\n\n nodes.value = result\n }\n\n function triggerNodes(): void {\n triggerRef(nodes)\n }\n\n watch(\n [\n () => props.data,\n () => props.disabledNode,\n () => props.childrenKey,\n () => props.valueKey,\n () => props.labelKey,\n () => props.expandAll\n ],\n () => {\n rebuildForest()\n },\n { immediate: true }\n )\n\n const forest = computed(() => forestRef.value)\n const nodeDict = computed(() => nodeDictRef.value)\n\n return { nodes, forest, getFlattedNodes, nodeDict, triggerNodes }\n}\n"],"mappings":";;;;AAmBA,SAAgB,aAAa,SAAwC;CACnE,MAAM,EAAE,UAAU;CAMlB,MAAM,YAAY,WAChB,IAAI,OAAqC;EACvC,MAAM,EAAE;EACR,aAAa,MAAM,eAAe;EACnC,CAAC,CACH;CAED,MAAM,cAAc,2BAA2C,IAAI,KAAK,CAAC;CAEzE,MAAM,QAAQ,WAAuB,EAAE,CAAC;CAExC,SAAS,cAAoD;EAC3D,MAAM,EAAE,cAAc,YAAY,OAAO,UAAU,aAAa;EAChE,MAAM,cAAc,MAAM,eAAe;EAEzC,SAAS,WACP,MACA,OACA,OACA,QACU;GACV,MAAM,OAAO,IAAIA,WAAS;IACxB;IACA;IACA;IACA;IACU;IACA;IACX,CAAC;AACF,QAAK,WAAW;AAChB,UAAO;;AAGT,SAAO,IAAI,OAAqC;GAC9C,MAAO,MAAM,QAAQ,EAAE;GACvB;GACA,YAAY,gBACP,MAAM,OAAO,OAAO,IAAI,WAAW;IAClC,MAAM,OAAO,WAAW,MAA6B,OAAO,OAAO,OAAmB;AACtF,QAAI,KACF,MAAK,WAAW,aAAa,MAA6B,KAAK,IAAI;AAErE,WAAO;QAER,MAAM,OAAO,OAAO,IAAI,WACvB,WAAW,MAA6B,OAAO,OAAO,OAAmB;GAChF,CAAC;;CAGJ,SAAS,gBAAsB;EAC7B,MAAM,aAAa,aAAa;EAChC,MAAM,2BAAW,IAAI,KAAgC;AAIrD,aAAW,KAAK,SAAS;AACvB,YAAS,IAAI,KAAK,KAAK,KAAK;IAC5B;AAEF,YAAU,QAAQ;AAClB,cAAY,QAAQ;AAEpB,mBAAiB;;;CAInB,SAAS,kBAAwB;EAC/B,MAAM,SAAqB,EAAE;EAC7B,MAAM,QAAQ,UAAU,MAAM;EAE9B,MAAM,QAAoB,EAAE;AAC5B,OAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,IACrC,OAAM,KAAK,MAAM,GAAI;AAGvB,SAAO,MAAM,QAAQ;GACnB,MAAM,OAAO,MAAM,KAAK;AACxB,OAAI,CAAC,KAAK,QAAS;AACnB,UAAO,KAAK,KAAK;AACjB,OAAI,KAAK,YAAY,KAAK,YAAY,KAAK,SAAS,OAClD,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,IAC7C,OAAM,KAAK,KAAK,SAAS,GAAI;;AAKnC,QAAM,QAAQ;;CAGhB,SAAS,eAAqB;AAC5B,aAAW,MAAM;;AAGnB,OACE;QACQ,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;EACb,QACK;AACJ,iBAAe;IAEjB,EAAE,WAAW,MAAM,CACpB;AAKD,QAAO;EAAE;EAAO,QAHD,eAAe,UAAU,MAAM;EAGtB;EAAiB,UAFxB,eAAe,YAAY,MAAM;EAEC;EAAc"}
1
+ {"version":3,"file":"use-tree-nodes.js","names":["TreeNode"],"sources":["../../../src/components/tree/use-tree-nodes.ts"],"sourcesContent":["import { Forest } from '@cat-kit/core'\nimport { computed, shallowRef, triggerRef, watch, type ShallowRef, type ComputedRef } from 'vue'\n\nimport type { TreeProps } from '../../types'\nimport { TreeNode } from './tree-node'\n\ninterface Options {\n props: TreeProps\n}\n\ninterface UseTreeNodesReturned {\n nodes: ShallowRef<TreeNode[]>\n forest: ComputedRef<Forest<Record<string, unknown>, any>>\n getFlattedNodes: () => void\n nodeDict: ComputedRef<Map<string | number, TreeNode>>\n /** 强制刷新 `nodes` 的订阅者(供字段级别变化但列表引用不变时手动触发)。 */\n triggerNodes: () => void\n}\n\nexport function useTreeNodes(options: Options): UseTreeNodesReturned {\n const { props } = options\n\n // forest / nodeDict 保持为 shallowRef 而非 computed:\n // - computed 会因为任一 prop 变化(尤其是内联 disabledNode 函数)而整棵重建,\n // 丢失 expanded/checked/visible 等运行时状态;\n // - 显式 watch 仅在真正影响结构的输入变化时才重建,行为可控、可测量。\n const forestRef = shallowRef<Forest<Record<string, unknown>, any>>(\n new Forest<Record<string, unknown>, any>({\n data: [],\n childrenKey: props.childrenKey ?? 'children'\n })\n )\n\n const nodeDictRef = shallowRef<Map<string | number, TreeNode>>(new Map())\n\n const nodes = shallowRef<TreeNode[]>([])\n\n function buildForest(): Forest<Record<string, unknown>, any> {\n const { disabledNode, expandAll = false, valueKey, labelKey } = props\n const childrenKey = props.childrenKey ?? 'children'\n\n function createNode(\n data: Record<string, any>,\n index: number,\n depth: number,\n parent?: TreeNode\n ): TreeNode {\n const node = new TreeNode({\n data,\n index,\n depth,\n parent,\n valueKey: valueKey!,\n labelKey: labelKey!\n })\n node.expanded = expandAll\n return node\n }\n\n return new Forest<Record<string, unknown>, any>({\n data: (props.data ?? []) as Record<string, unknown>[],\n childrenKey,\n createNode: disabledNode\n ? (data, index, depth, _f, parent) => {\n const node = createNode(data as Record<string, any>, index, depth, parent as TreeNode)\n if (data) {\n node.disabled = disabledNode(data as Record<string, any>, node) ?? false\n }\n return node\n }\n : (data, index, depth, _f, parent) =>\n createNode(data as Record<string, any>, index, depth, parent as TreeNode)\n })\n }\n\n function rebuildForest(): void {\n const nextForest = buildForest()\n const nextDict = new Map<string | number, TreeNode>()\n\n // 构建森林的同时一次 DFS 同步填充字典,\n // 避免旧版 “forest computed → nodeDict computed” 两次完整遍历。\n nextForest.dfs((node) => {\n nextDict.set(node.key, node)\n })\n\n forestRef.value = nextForest\n nodeDictRef.value = nextDict\n\n getFlattedNodes()\n }\n\n /** 按展开与可见性扁平化:仅一次 DFS。 */\n function getFlattedNodes(): void {\n const result: TreeNode[] = []\n const roots = forestRef.value.roots as TreeNode[]\n\n const stack: TreeNode[] = []\n for (let i = roots.length - 1; i >= 0; i--) {\n stack.push(roots[i]!)\n }\n\n while (stack.length) {\n const node = stack.pop()!\n if (!node.visible) continue\n result.push(node)\n if (node.expanded && node.children && node.children.length) {\n for (let i = node.children.length - 1; i >= 0; i--) {\n stack.push(node.children[i]!)\n }\n }\n }\n\n nodes.value = result\n }\n\n function triggerNodes(): void {\n triggerRef(nodes)\n }\n\n watch(\n [\n () => props.data,\n () => props.disabledNode,\n () => props.childrenKey,\n () => props.valueKey,\n () => props.labelKey,\n () => props.expandAll\n ],\n () => {\n rebuildForest()\n },\n { immediate: true }\n )\n\n const forest = computed(() => forestRef.value)\n const nodeDict = computed(() => nodeDictRef.value)\n\n return { nodes, forest, getFlattedNodes, nodeDict, triggerNodes }\n}\n"],"mappings":";;;;AAmBA,SAAgB,aAAa,SAAwC;CACnE,MAAM,EAAE,UAAU;CAMlB,MAAM,YAAY,WAChB,IAAI,OAAqC;EACvC,MAAM,EAAE;EACR,aAAa,MAAM,eAAe;EACnC,CAAC,CACH;CAED,MAAM,cAAc,2BAA2C,IAAI,KAAK,CAAC;CAEzE,MAAM,QAAQ,WAAuB,EAAE,CAAC;CAExC,SAAS,cAAoD;EAC3D,MAAM,EAAE,cAAc,YAAY,OAAO,UAAU,aAAa;EAChE,MAAM,cAAc,MAAM,eAAe;EAEzC,SAAS,WACP,MACA,OACA,OACA,QACU;GACV,MAAM,OAAO,IAAIA,WAAS;IACxB;IACA;IACA;IACA;IACU;IACA;IACX,CAAC;AACF,QAAK,WAAW;AAChB,UAAO;;AAGT,SAAO,IAAI,OAAqC;GAC9C,MAAO,MAAM,QAAQ,EAAE;GACvB;GACA,YAAY,gBACP,MAAM,OAAO,OAAO,IAAI,WAAW;IAClC,MAAM,OAAO,WAAW,MAA6B,OAAO,OAAO,OAAmB;AACtF,QAAI,KACF,MAAK,WAAW,aAAa,MAA6B,KAAK,IAAI;AAErE,WAAO;QAER,MAAM,OAAO,OAAO,IAAI,WACvB,WAAW,MAA6B,OAAO,OAAO,OAAmB;GAChF,CAAC;;CAGJ,SAAS,gBAAsB;EAC7B,MAAM,aAAa,aAAa;EAChC,MAAM,2BAAW,IAAI,KAAgC;AAIrD,aAAW,KAAK,SAAS;AACvB,YAAS,IAAI,KAAK,KAAK,KAAK;IAC5B;AAEF,YAAU,QAAQ;AAClB,cAAY,QAAQ;AAEpB,mBAAiB;;;CAInB,SAAS,kBAAwB;EAC/B,MAAM,SAAqB,EAAE;EAC7B,MAAM,QAAQ,UAAU,MAAM;EAE9B,MAAM,QAAoB,EAAE;AAC5B,OAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,IACrC,OAAM,KAAK,MAAM,GAAI;AAGvB,SAAO,MAAM,QAAQ;GACnB,MAAM,OAAO,MAAM,KAAK;AACxB,OAAI,CAAC,KAAK,QAAS;AACnB,UAAO,KAAK,KAAK;AACjB,OAAI,KAAK,YAAY,KAAK,YAAY,KAAK,SAAS,OAClD,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,IAC7C,OAAM,KAAK,KAAK,SAAS,GAAI;;AAKnC,QAAM,QAAQ;;CAGhB,SAAS,eAAqB;AAC5B,aAAW,MAAM;;AAGnB,OACE;QACQ,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;EACb,QACK;AACJ,iBAAe;IAEjB,EAAE,WAAW,MAAM,CACpB;AAKD,QAAO;EAAE;EAAO,QAHD,eAAe,UAAU,MAGlB;EAAE;EAAiB,UAFxB,eAAe,YAAY,MAEK;EAAE;EAAc"}
@@ -19,20 +19,20 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
19
19
  change: (value?: string | number | undefined, selectedData?: Record<string, any> | undefined) => any;
20
20
  "update:text": (text?: string | undefined) => any;
21
21
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
22
- "onUpdate:modelValue"?: ((value: string | number | undefined) => any) | undefined;
23
22
  onClear?: (() => any) | undefined;
23
+ "onUpdate:modelValue"?: ((value: string | number | undefined) => any) | undefined;
24
24
  onChange?: ((value?: string | number | undefined, selectedData?: Record<string, any> | undefined) => any) | undefined;
25
25
  "onUpdate:text"?: ((text?: string | undefined) => any) | undefined;
26
26
  }>, {
27
27
  disabled: boolean;
28
+ placeholder: string;
29
+ clearable: boolean;
28
30
  readonly: boolean;
29
31
  labelKey: string;
30
32
  valueKey: string;
31
- placeholder: string;
32
- clearable: boolean;
33
+ filterable: boolean;
33
34
  minWidth: string;
34
35
  expandAll: boolean;
35
- filterable: boolean;
36
36
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
37
37
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
38
  declare const _default: typeof __VLS_export;
@@ -7,8 +7,8 @@ type __VLS_Slots = {} & {
7
7
  default?: (props: typeof __VLS_7) => any;
8
8
  };
9
9
  declare const __VLS_base: _$vue.DefineComponent<WatermarkProps, {}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<WatermarkProps> & Readonly<{}>, {
10
- route: number;
11
10
  fontSize: number;
11
+ route: number;
12
12
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
13
13
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
14
14
  declare const _default: typeof __VLS_export;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -25,13 +25,14 @@
25
25
  "check-types": "tsc -p tsconfig.json --noEmit"
26
26
  },
27
27
  "dependencies": {
28
- "@cat-kit/core": "^1.1.1",
28
+ "@cat-kit/core": "^1.1.3",
29
29
  "@cat-kit/excel": "^1.0.1",
30
30
  "@codemirror/lang-java": "^6.0.2",
31
31
  "@codemirror/lang-javascript": "^6.2.5",
32
32
  "@codemirror/lang-json": "^6.0.2",
33
33
  "@codemirror/lang-sql": "^6.10.0",
34
34
  "@codemirror/state": "^6.6.0",
35
+ "@codemirror/theme-one-dark": "^6.1.3",
35
36
  "@codemirror/view": "^6.41.1",
36
37
  "@embedpdf/core": "^2.14.1",
37
38
  "@embedpdf/engines": "^2.14.1",
@@ -47,22 +48,22 @@
47
48
  "@lexical/rich-text": "^0.43.0",
48
49
  "@lexical/selection": "^0.43.0",
49
50
  "@lexical/utils": "^0.43.0",
50
- "@veltra/compositions": "1.0.9",
51
- "@veltra/directives": "1.0.9",
52
- "@veltra/icons": "1.0.9",
53
- "@veltra/styles": "1.0.9",
54
- "@veltra/utils": "1.0.9",
51
+ "@veltra/compositions": "1.0.11",
52
+ "@veltra/directives": "1.0.11",
53
+ "@veltra/icons": "1.0.11",
54
+ "@veltra/styles": "1.0.11",
55
+ "@veltra/utils": "1.0.11",
55
56
  "@visactor/vtable": "^1.26.0",
56
57
  "codemirror": "^6.0.2",
57
58
  "docx-preview": "^0.3.7",
58
59
  "lexical": "^0.43.0"
59
60
  },
60
61
  "peerDependencies": {
61
- "@veltra/compositions": "1.0.9",
62
- "@veltra/directives": "1.0.9",
63
- "@veltra/icons": "1.0.9",
64
- "@veltra/styles": "1.0.9",
65
- "@veltra/utils": "1.0.9",
62
+ "@veltra/compositions": "1.0.11",
63
+ "@veltra/directives": "1.0.11",
64
+ "@veltra/icons": "1.0.11",
65
+ "@veltra/styles": "1.0.11",
66
+ "@veltra/utils": "1.0.11",
66
67
  "vue": "^3.5.33"
67
68
  }
68
69
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  <script lang="ts" setup>
8
8
  import { EditorState } from '@codemirror/state'
9
+ import { oneDark } from '@codemirror/theme-one-dark'
9
10
  import { tooltips } from '@codemirror/view'
10
11
  import { useFormComponent, useFormFallbackProps } from '@veltra/compositions'
11
12
  import { bem, zIndex } from '@veltra/utils'
@@ -56,6 +57,7 @@ async function renderEditor() {
56
57
 
57
58
  const extensions = [
58
59
  basicSetup,
60
+ oneDark,
59
61
  tooltips({
60
62
  parent: document.body
61
63
  }),
@@ -20,15 +20,14 @@
20
20
  v-for="f in normalizedFiles"
21
21
  :key="f.id"
22
22
  :class="[cls.e('item'), bem.is('active', f.id === activeId)]"
23
+ :title="f.name"
23
24
  @click="activate(f.id)"
24
25
  >
25
- <span :class="[cls.e('badge'), cls.em('badge', f.kind)]">
26
- {{ label(f.kind) }}
27
- </span>
28
26
  <span :class="cls.e('item-meta')">
29
27
  <span :class="cls.e('item-name')" :title="f.name">{{ f.name }}</span>
30
28
  <span :class="cls.e('item-size')">{{ formatBytes(f.size) }}</span>
31
29
  </span>
30
+ <span :class="cls.e('item-kind')">{{ label(f.kind) }}</span>
32
31
  </li>
33
32
  <li v-if="!normalizedFiles.length" :class="cls.e('list-empty')">
34
33
  <u-empty text="暂无文件" :size="32" />
@@ -42,27 +41,93 @@
42
41
  <span v-if="activeFile" :class="[cls.e('badge'), cls.em('badge', activeFile.kind)]">
43
42
  {{ label(activeFile.kind) }}
44
43
  </span>
45
- <span :class="cls.e('stage-name')">
46
- {{ activeFile?.name ?? '—' }}
47
- </span>
48
- <span v-if="activeFile?.size" :class="cls.e('stage-sub')">
49
- {{ formatBytes(activeFile.size) }}
44
+ <span :class="cls.e('stage-copy')">
45
+ <span :class="cls.e('stage-name')" :title="activeFile?.name">
46
+ {{ activeFile?.name ?? '—' }}
47
+ </span>
48
+ <span v-if="activeFile" :class="cls.e('stage-sub')">
49
+ <span>{{ activeIndexLabel }}</span>
50
+ <span v-if="activeFile.size">{{ formatBytes(activeFile.size) }}</span>
51
+ </span>
50
52
  </span>
51
53
  </div>
52
54
  <div :class="cls.e('stage-actions')">
53
- <button :class="cls.e('action')" :disabled="!hasPrev" type="button" @click="prev">
54
- 上一个
55
- </button>
56
- <button :class="cls.e('action')" :disabled="!hasNext" type="button" @click="next">
57
- 下一个
58
- </button>
55
+ <span :class="cls.e('action-group')">
56
+ <button
57
+ :class="[cls.e('action'), cls.em('action', 'icon')]"
58
+ :disabled="!hasPrev"
59
+ type="button"
60
+ aria-label="上一个"
61
+ title="上一个"
62
+ @click="prev"
63
+ >
64
+ <u-icon :size="15">
65
+ <ArrowLeft />
66
+ </u-icon>
67
+ </button>
68
+ <button
69
+ :class="[cls.e('action'), cls.em('action', 'icon')]"
70
+ :disabled="!hasNext"
71
+ type="button"
72
+ aria-label="下一个"
73
+ title="下一个"
74
+ @click="next"
75
+ >
76
+ <u-icon :size="15">
77
+ <ArrowRight />
78
+ </u-icon>
79
+ </button>
80
+ </span>
81
+ <span v-if="isTransformable" :class="cls.e('action-group')">
82
+ <button
83
+ :class="[cls.e('action'), cls.em('action', 'icon')]"
84
+ :disabled="zoomOutDisabled"
85
+ type="button"
86
+ aria-label="缩小"
87
+ title="缩小"
88
+ @click="zoomOut"
89
+ >
90
+ <u-icon :size="15">
91
+ <ZoomOut />
92
+ </u-icon>
93
+ </button>
94
+ <span :class="cls.e('zoom-value')">{{ zoomPercent }}</span>
95
+ <button
96
+ :class="[cls.e('action'), cls.em('action', 'icon')]"
97
+ :disabled="zoomInDisabled"
98
+ type="button"
99
+ aria-label="放大"
100
+ title="放大"
101
+ @click="zoomIn"
102
+ >
103
+ <u-icon :size="15">
104
+ <ZoomIn />
105
+ </u-icon>
106
+ </button>
107
+ <button
108
+ :class="[cls.e('action'), cls.em('action', 'icon')]"
109
+ :disabled="isTransformReset"
110
+ type="button"
111
+ aria-label="重置视图"
112
+ title="重置视图"
113
+ @click="resetTransform"
114
+ >
115
+ <u-icon :size="15">
116
+ <Refresh />
117
+ </u-icon>
118
+ </button>
119
+ </span>
59
120
  <button
60
121
  v-if="downloadable && activeFile"
61
- :class="[cls.e('action'), cls.em('action', 'primary')]"
122
+ :class="[cls.e('action'), cls.em('action', 'primary'), cls.em('action', 'icon')]"
62
123
  type="button"
124
+ aria-label="下载"
125
+ title="下载"
63
126
  @click="download"
64
127
  >
65
- 下载
128
+ <u-icon :size="15">
129
+ <Download />
130
+ </u-icon>
66
131
  </button>
67
132
  <button
68
133
  v-if="isModal"
@@ -80,14 +145,30 @@
80
145
  </header>
81
146
  <div :class="cls.e('body')">
82
147
  <transition name="u-file-viewer-fade" mode="out-in">
83
- <component
84
- :is="PreviewerMap[activeFile.kind]"
148
+ <div
85
149
  v-if="activeFile"
86
150
  :key="activeFile.id"
87
- :file="activeFile"
88
- :max-rows="sheetMaxRows"
89
- @error="handleChildError"
90
- />
151
+ :class="[
152
+ cls.e('viewport'),
153
+ bem.is('transformable', isTransformable),
154
+ bem.is('pannable', canPan),
155
+ bem.is('dragging', isDragging)
156
+ ]"
157
+ @pointerdown="handleViewportPointerDown"
158
+ @pointermove="handleViewportPointerMove"
159
+ @pointerup="handleViewportPointerEnd"
160
+ @pointercancel="handleViewportPointerEnd"
161
+ @dblclick="handleViewportDblclick"
162
+ >
163
+ <component
164
+ :is="PreviewerMap[activeFile.kind]"
165
+ :file="activeFile"
166
+ :max-rows="sheetMaxRows"
167
+ :class="isTransformable ? cls.e('previewer') : undefined"
168
+ :style="previewerStyle"
169
+ @error="handleChildError"
170
+ />
171
+ </div>
91
172
  <div v-else :class="cls.e('empty')">
92
173
  <u-empty text="暂无可预览文件" />
93
174
  </div>
@@ -101,9 +182,26 @@
101
182
  </template>
102
183
 
103
184
  <script lang="ts" setup>
104
- import { Close } from '@veltra/icons/normal'
185
+ import {
186
+ ArrowLeft,
187
+ ArrowRight,
188
+ Close,
189
+ Download,
190
+ Refresh,
191
+ ZoomIn,
192
+ ZoomOut
193
+ } from '@veltra/icons/normal'
105
194
  import { bem, withUnit } from '@veltra/utils'
106
- import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, shallowRef, watch } from 'vue'
195
+ import {
196
+ computed,
197
+ defineAsyncComponent,
198
+ nextTick,
199
+ onBeforeUnmount,
200
+ ref,
201
+ shallowRef,
202
+ watch
203
+ } from 'vue'
204
+ import type { CSSProperties } from 'vue'
107
205
 
108
206
  import type {
109
207
  _FileViewerExposed,
@@ -136,10 +234,29 @@ const emit = defineEmits<FileViewerEmits>()
136
234
 
137
235
  const cls = bem('file-viewer')
138
236
 
237
+ const MIN_SCALE = 0.5
238
+ const MAX_SCALE = 3
239
+ const SCALE_STEP = 0.25
240
+ const TRANSFORMABLE_KINDS = new Set<FileViewerKind>(['image', 'pdf', 'docx'])
241
+
139
242
  const activeId = defineModel<string | undefined>('modelValue', { default: undefined })
140
243
  const openModel = defineModel<boolean | undefined>('open', { default: undefined })
141
244
 
142
245
  const rootRef = shallowRef<HTMLDivElement>()
246
+ const scale = ref(1)
247
+ const offsetX = ref(0)
248
+ const offsetY = ref(0)
249
+ const isDragging = ref(false)
250
+
251
+ let dragState:
252
+ | {
253
+ pointerId: number
254
+ startX: number
255
+ startY: number
256
+ originX: number
257
+ originY: number
258
+ }
259
+ | undefined
143
260
 
144
261
  const PreviewerMap: Record<FileViewerKind, ReturnType<typeof defineAsyncComponent>> = {
145
262
  image: defineAsyncComponent(() => import('./previewers/image-previewer.vue')),
@@ -185,6 +302,33 @@ const sidebarWidthCss = computed(() => {
185
302
  /** 是否启用模态模式:只要父组件显式传入 open(含 v-model:open),即进入模态 */
186
303
  const isModal = computed(() => openModel.value !== undefined)
187
304
 
305
+ const activeIndexLabel = computed(() =>
306
+ activeIndex.value >= 0 ? `${activeIndex.value + 1} / ${normalizedFiles.value.length}` : ''
307
+ )
308
+
309
+ const isTransformable = computed(
310
+ () => !!activeFile.value && TRANSFORMABLE_KINDS.has(activeFile.value.kind)
311
+ )
312
+
313
+ const canPan = computed(() => isTransformable.value && scale.value > 1)
314
+
315
+ const zoomPercent = computed(() => `${Math.round(scale.value * 100)}%`)
316
+
317
+ const zoomInDisabled = computed(() => !isTransformable.value || scale.value >= MAX_SCALE)
318
+
319
+ const zoomOutDisabled = computed(() => !isTransformable.value || scale.value <= MIN_SCALE)
320
+
321
+ const isTransformReset = computed(
322
+ () => scale.value === 1 && offsetX.value === 0 && offsetY.value === 0
323
+ )
324
+
325
+ const previewerStyle = computed<CSSProperties | undefined>(() => {
326
+ if (!isTransformable.value) return undefined
327
+ return {
328
+ transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value})`
329
+ }
330
+ })
331
+
188
332
  function label(kind: FileViewerKind): string {
189
333
  return FILE_VIEWER_KIND_LABEL[kind]
190
334
  }
@@ -210,6 +354,83 @@ function next() {
210
354
  if (target) activate(target.id)
211
355
  }
212
356
 
357
+ function normalizeScale(value: number): number {
358
+ const clamped = Math.min(MAX_SCALE, Math.max(MIN_SCALE, value))
359
+ return Math.round(clamped * 100) / 100
360
+ }
361
+
362
+ function setScale(value: number) {
363
+ scale.value = normalizeScale(value)
364
+ if (scale.value <= 1) {
365
+ offsetX.value = 0
366
+ offsetY.value = 0
367
+ }
368
+ }
369
+
370
+ function zoomIn() {
371
+ if (!isTransformable.value) return
372
+ setScale(scale.value + SCALE_STEP)
373
+ }
374
+
375
+ function zoomOut() {
376
+ if (!isTransformable.value) return
377
+ setScale(scale.value - SCALE_STEP)
378
+ }
379
+
380
+ function resetTransform() {
381
+ scale.value = 1
382
+ offsetX.value = 0
383
+ offsetY.value = 0
384
+ dragState = undefined
385
+ isDragging.value = false
386
+ }
387
+
388
+ function isInteractiveTarget(target: EventTarget | null): boolean {
389
+ if (!(target instanceof HTMLElement)) return false
390
+ return !!target.closest('button, a, input, textarea, select, video, [contenteditable="true"]')
391
+ }
392
+
393
+ function handleViewportPointerDown(e: PointerEvent) {
394
+ if (!canPan.value || e.button !== 0 || isInteractiveTarget(e.target)) return
395
+
396
+ dragState = {
397
+ pointerId: e.pointerId,
398
+ startX: e.clientX,
399
+ startY: e.clientY,
400
+ originX: offsetX.value,
401
+ originY: offsetY.value
402
+ }
403
+ isDragging.value = true
404
+ const el = e.currentTarget as HTMLElement
405
+ el.setPointerCapture(e.pointerId)
406
+ e.preventDefault()
407
+ }
408
+
409
+ function handleViewportPointerMove(e: PointerEvent) {
410
+ if (!dragState || dragState.pointerId !== e.pointerId) return
411
+ offsetX.value = dragState.originX + e.clientX - dragState.startX
412
+ offsetY.value = dragState.originY + e.clientY - dragState.startY
413
+ }
414
+
415
+ function handleViewportPointerEnd(e: PointerEvent) {
416
+ if (!dragState || dragState.pointerId !== e.pointerId) return
417
+ const el = e.currentTarget as HTMLElement
418
+ if (el.hasPointerCapture(e.pointerId)) {
419
+ el.releasePointerCapture(e.pointerId)
420
+ }
421
+ dragState = undefined
422
+ isDragging.value = false
423
+ }
424
+
425
+ function handleViewportDblclick() {
426
+ if (!isTransformable.value) return
427
+ if (scale.value === 1) {
428
+ setScale(2)
429
+ } else {
430
+ resetTransform()
431
+ }
432
+ }
433
+
213
434
  async function download() {
214
435
  if (!activeFile.value) return
215
436
  try {
@@ -257,6 +478,11 @@ watch(
257
478
  { immediate: true }
258
479
  )
259
480
 
481
+ watch(
482
+ () => activeFile.value?.id,
483
+ () => resetTransform()
484
+ )
485
+
260
486
  // 模态打开时:锁定 body 滚动 + 让容器获得焦点以便接收 ESC
261
487
  let previousBodyOverflow = ''
262
488
  let isBodyLocked = false
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <u-scroll tag="div" :class="cls.e('docx')">
2
+ <u-scroll tag="div" :class="cls.e('docx')" v-bind="attrs">
3
3
  <div ref="container" :class="cls.e('docx-body')" />
4
4
  <div v-if="loading" :class="cls.e('loading')">加载中…</div>
5
5
  </u-scroll>
@@ -7,18 +7,19 @@
7
7
 
8
8
  <script lang="ts" setup>
9
9
  import { bem } from '@veltra/utils'
10
- import { onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'
10
+ import { onBeforeUnmount, ref, useAttrs, useTemplateRef, watch } from 'vue'
11
11
 
12
12
  import type { FileViewerItem } from '../../../types/file-viewer'
13
13
  import { UScroll } from '../../scroll'
14
14
 
15
- defineOptions({ name: 'FileViewerDocxPreviewer' })
15
+ defineOptions({ name: 'FileViewerDocxPreviewer', inheritAttrs: false })
16
16
 
17
17
  const props = defineProps<{ file: FileViewerItem }>()
18
18
 
19
19
  const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
20
20
 
21
21
  const cls = bem('file-viewer')
22
+ const attrs = useAttrs()
22
23
 
23
24
  const container = useTemplateRef<HTMLDivElement>('container')
24
25
  const loading = ref(true)
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="[cls.e('image'), bem.is('actual', actual)]">
2
+ <div :class="cls.e('image')" v-bind="attrs">
3
3
  <img
4
4
  v-if="url"
5
5
  :src="url"
@@ -7,7 +7,6 @@
7
7
  draggable="false"
8
8
  @load="loading = false"
9
9
  @error="onImgError"
10
- @dblclick="actual = !actual"
11
10
  />
12
11
  <div v-if="loading" :class="cls.e('loading')">加载中…</div>
13
12
  <div v-if="failed" :class="cls.e('loading')">图片加载失败</div>
@@ -16,22 +15,22 @@
16
15
 
17
16
  <script lang="ts" setup>
18
17
  import { bem } from '@veltra/utils'
19
- import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
18
+ import { onBeforeUnmount, ref, shallowRef, useAttrs, watch } from 'vue'
20
19
 
21
20
  import type { FileViewerItem } from '../../../types/file-viewer'
22
21
 
23
- defineOptions({ name: 'FileViewerImagePreviewer' })
22
+ defineOptions({ name: 'FileViewerImagePreviewer', inheritAttrs: false })
24
23
 
25
24
  const props = defineProps<{ file: FileViewerItem }>()
26
25
 
27
26
  const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
28
27
 
29
28
  const cls = bem('file-viewer')
29
+ const attrs = useAttrs()
30
30
 
31
31
  const url = shallowRef<string>('')
32
32
  const loading = ref(true)
33
33
  const failed = ref(false)
34
- const actual = ref(false)
35
34
 
36
35
  let revoke: (() => void) | undefined
37
36
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="cls.e('pdf')">
2
+ <div :class="cls.e('pdf')" v-bind="attrs">
3
3
  <div v-if="!engine || isLoading" :class="cls.e('loading')">正在加载 PDF 引擎…</div>
4
4
  <EmbedPDF v-else-if="pdfUrl" :engine="engine" :plugins="plugins" v-slot="{ activeDocumentId }">
5
5
  <DocumentContent
@@ -36,17 +36,18 @@ import { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/vue'
36
36
  import { ScrollPluginPackage, Scroller } from '@embedpdf/plugin-scroll/vue'
37
37
  import { ViewportPluginPackage, Viewport } from '@embedpdf/plugin-viewport/vue'
38
38
  import { bem } from '@veltra/utils'
39
- import { computed, onBeforeUnmount, shallowRef, watch } from 'vue'
39
+ import { computed, onBeforeUnmount, shallowRef, useAttrs, watch } from 'vue'
40
40
 
41
41
  import type { FileViewerItem } from '../../../types/file-viewer'
42
42
 
43
- defineOptions({ name: 'FileViewerPdfPreviewer' })
43
+ defineOptions({ name: 'FileViewerPdfPreviewer', inheritAttrs: false })
44
44
 
45
45
  const props = defineProps<{ file: FileViewerItem }>()
46
46
 
47
47
  const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
48
48
 
49
49
  const cls = bem('file-viewer')
50
+ const attrs = useAttrs()
50
51
 
51
52
  const { engine, isLoading, error } = usePdfiumEngine()
52
53