@veltra/desktop 1.1.15 → 1.1.19

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 (282) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action-group.vue_vue_type_script_setup_true_lang.js.map +1 -1
  3. package/dist/components/action/action.js.map +1 -1
  4. package/dist/components/action/action.vue.d.ts +1 -1
  5. package/dist/components/action/style2.css +0 -4
  6. package/dist/components/auto-complete/auto-complete.js.map +1 -1
  7. package/dist/components/auto-complete/style.js +1 -1
  8. package/dist/components/auto-complete/use-keyboard.js.map +1 -1
  9. package/dist/components/auto-complete/use-suggestions.js.map +1 -1
  10. package/dist/components/badge/badge.js +1 -1
  11. package/dist/components/badge/badge.js.map +1 -1
  12. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  13. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  14. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  15. package/dist/components/batch-edit/helper.js.map +1 -1
  16. package/dist/components/batch-edit/style.js +1 -1
  17. package/dist/components/batch-edit/use-edit.js.map +1 -1
  18. package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
  19. package/dist/components/button/button-group.js.map +1 -1
  20. package/dist/components/button/button.js.map +1 -1
  21. package/dist/components/calendar/calendar.js.map +1 -1
  22. package/dist/components/calendar/utils.js.map +1 -1
  23. package/dist/components/card/card-action.js.map +1 -1
  24. package/dist/components/card/card-content.js.map +1 -1
  25. package/dist/components/card/card-cover.js.map +1 -1
  26. package/dist/components/card/card-header.js.map +1 -1
  27. package/dist/components/card/card.js.map +1 -1
  28. package/dist/components/cascade/cascade-panel-item.js.map +1 -1
  29. package/dist/components/cascade/cascade.js.map +1 -1
  30. package/dist/components/cascade/cascade.vue.d.ts +3 -3
  31. package/dist/components/cascade/node.js.map +1 -1
  32. package/dist/components/cascade/use-check.js.map +1 -1
  33. package/dist/components/cascade/use-data-map.js.map +1 -1
  34. package/dist/components/cascade/use-select.js +1 -1
  35. package/dist/components/cascade/use-select.js.map +1 -1
  36. package/dist/components/check-tag/check-tag.js.map +1 -1
  37. package/dist/components/checkbox/checkbox-button.js.map +1 -1
  38. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  39. package/dist/components/checkbox/checkbox.js.map +1 -1
  40. package/dist/components/checkbox/style.js +1 -1
  41. package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
  42. package/dist/components/code-editor/code-editor.js.map +1 -1
  43. package/dist/components/collapse/collapse-item.js.map +1 -1
  44. package/dist/components/collapse/collapse.js.map +1 -1
  45. package/dist/components/condition-editor/components/condition-group.js +138 -103
  46. package/dist/components/condition-editor/components/condition-group.js.map +1 -1
  47. package/dist/components/condition-editor/components/condition-row.js +146 -95
  48. package/dist/components/condition-editor/components/condition-row.js.map +1 -1
  49. package/dist/components/condition-editor/condition-editor.js +18 -40
  50. package/dist/components/condition-editor/condition-editor.js.map +1 -1
  51. package/dist/components/condition-editor/condition-editor.vue.d.ts +0 -2
  52. package/dist/components/condition-editor/core/evaluator.d.ts +22 -0
  53. package/dist/components/condition-editor/core/evaluator.js +111 -50
  54. package/dist/components/condition-editor/core/evaluator.js.map +1 -1
  55. package/dist/components/condition-editor/core/operators.js.map +1 -1
  56. package/dist/components/condition-editor/index.d.ts +2 -1
  57. package/dist/components/condition-editor/style2.css +159 -82
  58. package/dist/components/context-menu/api.js.map +1 -1
  59. package/dist/components/context-menu/context-menu-item.js.map +1 -1
  60. package/dist/components/context-menu/context-menu.js.map +1 -1
  61. package/dist/components/context-menu/style.js +1 -1
  62. package/dist/components/date-panel/date-panel.js.map +1 -1
  63. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  64. package/dist/components/date-panel/headers/day.js.map +1 -1
  65. package/dist/components/date-panel/headers/month.js.map +1 -1
  66. package/dist/components/date-panel/headers/year.js.map +1 -1
  67. package/dist/components/date-panel/panels/day.js.map +1 -1
  68. package/dist/components/date-panel/panels/month.js.map +1 -1
  69. package/dist/components/date-panel/panels/year.js.map +1 -1
  70. package/dist/components/date-panel/use-date-select.js.map +1 -1
  71. package/dist/components/date-panel/use-panel-date.js.map +1 -1
  72. package/dist/components/date-panel/use-panel.js.map +1 -1
  73. package/dist/components/date-picker/date-picker.js.map +1 -1
  74. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  75. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  76. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  77. package/dist/components/dialog/dialog.js.map +1 -1
  78. package/dist/components/dialog/style.js +2 -2
  79. package/dist/components/dialog/use-maximum.js.map +1 -1
  80. package/dist/components/drawer/drawer.js.map +1 -1
  81. package/dist/components/drawer/style.js +1 -1
  82. package/dist/components/dropdown/dropdown.js +1 -0
  83. package/dist/components/dropdown/dropdown.js.map +1 -1
  84. package/dist/components/dropdown/style.js +1 -1
  85. package/dist/components/empty/empty.js.map +1 -1
  86. package/dist/components/expression-editor/components/path-preview.js.map +1 -1
  87. package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
  88. package/dist/components/expression-editor/core/chip.js.map +1 -1
  89. package/dist/components/expression-editor/core/editor.js.map +1 -1
  90. package/dist/components/expression-editor/core/mention.js.map +1 -1
  91. package/dist/components/expression-editor/core/model.js.map +1 -1
  92. package/dist/components/expression-editor/di.js.map +1 -1
  93. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  94. package/dist/components/file-picker/file-picker.js.map +1 -1
  95. package/dist/components/file-picker/helper.js.map +1 -1
  96. package/dist/components/file-viewer/file-viewer.js.map +1 -1
  97. package/dist/components/file-viewer/helper.js.map +1 -1
  98. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -1
  99. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -1
  100. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -1
  101. package/dist/components/file-viewer/previewers/pdf-viewport-scroll.js.map +1 -1
  102. package/dist/components/file-viewer/previewers/pdf-zoom-bridge.js.map +1 -1
  103. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -1
  104. package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -1
  105. package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -1
  106. package/dist/components/float-button/float-button.js.map +1 -1
  107. package/dist/components/form/dynamic-form-model.js.map +1 -1
  108. package/dist/components/form/form-model.js.map +1 -1
  109. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
  110. package/dist/components/form/helper.js.map +1 -1
  111. package/dist/components/form/use-node-interceptor.js.map +1 -1
  112. package/dist/components/form-item/form-item.js.map +1 -1
  113. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +2 -1
  114. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  115. package/dist/components/gantt-chart/gantt-chart.js.map +1 -1
  116. package/dist/components/grid/breakpoint.js.map +1 -1
  117. package/dist/components/grid/grid-item.js.map +1 -1
  118. package/dist/components/grid/grid.js.map +1 -1
  119. package/dist/components/grid/use-responsive.js.map +1 -1
  120. package/dist/components/grid-input/grid-input.js.map +1 -1
  121. package/dist/components/group-input/group-input.js.map +1 -1
  122. package/dist/components/group-input/use-group-items.js.map +1 -1
  123. package/dist/components/icon/icon.js.map +1 -1
  124. package/dist/components/index.d.ts +1 -0
  125. package/dist/components/index.js +4 -0
  126. package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js.map +1 -1
  127. package/dist/components/input/style.js +1 -1
  128. package/dist/components/kbd/kbd.js.map +1 -1
  129. package/dist/components/layout/layout-resizer.js.map +1 -1
  130. package/dist/components/layout/layout.js.map +1 -1
  131. package/dist/components/list/list-item.js.map +1 -1
  132. package/dist/components/list/list.js.map +1 -1
  133. package/dist/components/loading/directive.js.map +1 -1
  134. package/dist/components/loading/loading.js.map +1 -1
  135. package/dist/components/loading/style.js +1 -1
  136. package/dist/components/menu/helper.js.map +1 -1
  137. package/dist/components/menu/menu-item-collapsed.js.map +1 -1
  138. package/dist/components/menu/menu-item.js.map +1 -1
  139. package/dist/components/menu/menu-sub-collapsed.js.map +1 -1
  140. package/dist/components/menu/menu-sub.js.map +1 -1
  141. package/dist/components/menu/menu.js.map +1 -1
  142. package/dist/components/menu/use-menu-item.js.map +1 -1
  143. package/dist/components/menu/walk-menu-path.js.map +1 -1
  144. package/dist/components/message/helper.js.map +1 -1
  145. package/dist/components/message/message-box.js.map +1 -1
  146. package/dist/components/message/message.js.map +1 -1
  147. package/dist/components/message/message2.js.map +1 -1
  148. package/dist/components/message-confirm/message-confirm.js.map +1 -1
  149. package/dist/components/message-confirm/message-confirm2.js.map +1 -1
  150. package/dist/components/multi-select/multi-select-option.js.map +1 -1
  151. package/dist/components/multi-select/multi-select.js.map +1 -1
  152. package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
  153. package/dist/components/multi-select/style.js +1 -1
  154. package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
  155. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +2 -2
  156. package/dist/components/multi-tree-select/style.js +1 -1
  157. package/dist/components/node-render/node-render.js.map +1 -1
  158. package/dist/components/notification/index.js.map +1 -1
  159. package/dist/components/notification/notification.js.map +1 -1
  160. package/dist/components/number/number.js.map +1 -1
  161. package/dist/components/number-input/number-input.js.map +1 -1
  162. package/dist/components/number-range-input/number-range-input.js.map +1 -1
  163. package/dist/components/paginator/paginator.js.map +1 -1
  164. package/dist/components/palette/color-transform.js.map +1 -1
  165. package/dist/components/palette/palette-alpha.js.map +1 -1
  166. package/dist/components/palette/palette-color-switch.js.map +1 -1
  167. package/dist/components/palette/palette-hue.js.map +1 -1
  168. package/dist/components/palette/palette-sv.js.map +1 -1
  169. package/dist/components/palette/palette.js.map +1 -1
  170. package/dist/components/palette/use-hsv.js.map +1 -1
  171. package/dist/components/password-input/password-input.js.map +1 -1
  172. package/dist/components/pop-confirm/pop-confirm.js.map +1 -1
  173. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  174. package/dist/components/progress/progress.js.map +1 -1
  175. package/dist/components/progress-nodes/progress-nodes.js.map +1 -1
  176. package/dist/components/radio/radio.js.map +1 -1
  177. package/dist/components/radio/style.js +1 -1
  178. package/dist/components/radio-group/radio-group.js.map +1 -1
  179. package/dist/components/rich-text-editor/rich-text-editor.js.map +1 -1
  180. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  181. package/dist/components/scroll/scroll-bar.js +2 -0
  182. package/dist/components/scroll/scroll-bar.js.map +1 -1
  183. package/dist/components/scroll/scroll.js.map +1 -1
  184. package/dist/components/select/select.js.map +1 -1
  185. package/dist/components/select/select.vue.d.ts +2 -2
  186. package/dist/components/select/use-keyboard.js.map +1 -1
  187. package/dist/components/select/use-options.js.map +1 -1
  188. package/dist/components/slider/slider-thumb.js.map +1 -1
  189. package/dist/components/slider/slider.js +1 -1
  190. package/dist/components/slider/slider.js.map +1 -1
  191. package/dist/components/slider/use-slider.js.map +1 -1
  192. package/dist/components/steps/steps.js.map +1 -1
  193. package/dist/components/steps/steps.vue.d.ts +3 -3
  194. package/dist/components/switch/switch.js.map +1 -1
  195. package/dist/components/table/di.js.map +1 -1
  196. package/dist/components/table/node/col.js.map +1 -1
  197. package/dist/components/table/node/row.js.map +1 -1
  198. package/dist/components/table/table-body.js.map +1 -1
  199. package/dist/components/table/table-cell.js.map +1 -1
  200. package/dist/components/table/table-foot.js.map +1 -1
  201. package/dist/components/table/table-head.js.map +1 -1
  202. package/dist/components/table/table-row.js.map +1 -1
  203. package/dist/components/table/table.js.map +1 -1
  204. package/dist/components/table/use-check.js.map +1 -1
  205. package/dist/components/table/use-col-resize.js.map +1 -1
  206. package/dist/components/table/use-columns.js.map +1 -1
  207. package/dist/components/table/use-fixed-columns.js.map +1 -1
  208. package/dist/components/table/use-rows.js.map +1 -1
  209. package/dist/components/table/use-table.js.map +1 -1
  210. package/dist/components/table-editor/table-editor.js.map +1 -1
  211. package/dist/components/tabs/style.js +1 -1
  212. package/dist/components/tabs/tabs-horizontal.js.map +1 -1
  213. package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
  214. package/dist/components/tabs/tabs-vertical.js.map +1 -1
  215. package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
  216. package/dist/components/tabs/tabs.js.map +1 -1
  217. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  218. package/dist/components/tabs/use-tabs-bar.js.map +1 -1
  219. package/dist/components/tag/tag.js.map +1 -1
  220. package/dist/components/text/text.js.map +1 -1
  221. package/dist/components/textarea/style.js +1 -1
  222. package/dist/components/textarea/textarea.js.map +1 -1
  223. package/dist/components/textarea/textarea.vue.d.ts +1 -1
  224. package/dist/components/textarea/utils.js.map +1 -1
  225. package/dist/components/theme/schema.js.map +1 -1
  226. package/dist/components/theme/theme.js.map +1 -1
  227. package/dist/components/tip/tip.js.map +1 -1
  228. package/dist/components/tip/tip.vue.d.ts +1 -1
  229. package/dist/components/tip/use-nest.js.map +1 -1
  230. package/dist/components/tree/tree-node.js.map +1 -1
  231. package/dist/components/tree/tree-node2.js.map +1 -1
  232. package/dist/components/tree/tree.js.map +1 -1
  233. package/dist/components/tree/tree.vue.d.ts +1 -1
  234. package/dist/components/tree/use-check.js.map +1 -1
  235. package/dist/components/tree/use-filter.js.map +1 -1
  236. package/dist/components/tree/use-select.js.map +1 -1
  237. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  238. package/dist/components/tree-select/tree-select.js.map +1 -1
  239. package/dist/components/tree-select/tree-select.vue.d.ts +2 -2
  240. package/dist/components/watermark/watermark.js.map +1 -1
  241. package/dist/index.d.ts +4 -3
  242. package/dist/index.js +2 -1
  243. package/dist/install.js.map +1 -1
  244. package/dist/types/batch-edit.d.ts +2 -2
  245. package/dist/types/condition-editor.d.ts +21 -9
  246. package/dist/types/index.d.ts +2 -2
  247. package/package.json +13 -16
  248. package/src/components/action/style.scss +0 -6
  249. package/src/components/auto-complete/style.scss +0 -1
  250. package/src/components/auto-complete/style.ts +1 -1
  251. package/src/components/badge/badge.vue +1 -1
  252. package/src/components/batch-edit/style.ts +1 -1
  253. package/src/components/button/style.scss +1 -1
  254. package/src/components/cascade/use-select.ts +8 -6
  255. package/src/components/checkbox/style.ts +1 -1
  256. package/src/components/collapse/__test__/collapse.test.ts +3 -3
  257. package/src/components/condition-editor/components/condition-group.vue +118 -87
  258. package/src/components/condition-editor/components/condition-row.vue +137 -89
  259. package/src/components/condition-editor/condition-editor.vue +19 -48
  260. package/src/components/condition-editor/core/evaluator.ts +143 -57
  261. package/src/components/condition-editor/index.ts +6 -0
  262. package/src/components/condition-editor/style.scss +189 -102
  263. package/src/components/context-menu/style.ts +1 -1
  264. package/src/components/dialog/style.ts +2 -2
  265. package/src/components/drawer/style.ts +1 -1
  266. package/src/components/dropdown/style.ts +1 -1
  267. package/src/components/expression-editor/style.scss +1 -1
  268. package/src/components/form-item/form-item.vue +4 -9
  269. package/src/components/input/style.ts +1 -1
  270. package/src/components/loading/style.ts +1 -1
  271. package/src/components/menu/style.scss +1 -1
  272. package/src/components/message/style.scss +1 -1
  273. package/src/components/multi-select/style.ts +1 -1
  274. package/src/components/multi-tree-select/style.ts +1 -1
  275. package/src/components/number-input/__test__/number-input.test.ts +1 -1
  276. package/src/components/radio/style.ts +1 -1
  277. package/src/components/slider/slider.vue +1 -1
  278. package/src/components/table/di.ts +1 -1
  279. package/src/components/tabs/style.ts +1 -1
  280. package/src/components/textarea/style.ts +1 -1
  281. package/src/types/batch-edit.ts +2 -2
  282. package/src/types/condition-editor.ts +22 -8
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","names":[],"sources":["../../../src/components/file-viewer/helper.ts"],"sourcesContent":["import type { FileViewerItem, FileViewerKind } from '../../types/file-viewer'\n\nconst IMAGE_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico'])\n\nconst VIDEO_EXTS = new Set(['mp4', 'webm', 'mov', 'm4v', 'ogv', 'mkv'])\n\nconst SHEET_EXTS = new Set(['xlsx', 'xlsm', 'xlsb', 'csv'])\n\nconst TEXT_EXTS = new Set([\n 'txt',\n 'log',\n 'md',\n 'markdown',\n 'json',\n 'yml',\n 'yaml',\n 'xml',\n 'js',\n 'ts',\n 'tsx',\n 'jsx',\n 'css',\n 'scss',\n 'sass',\n 'less',\n 'html',\n 'htm',\n 'ini',\n 'toml',\n 'sh',\n 'bash',\n 'zsh',\n 'env',\n 'sql'\n])\n\nexport function getExtension(name: string): string {\n const idx = name.lastIndexOf('.')\n return idx === -1 ? '' : name.slice(idx + 1).toLowerCase()\n}\n\nexport function inferKind(name: string, explicit?: FileViewerKind): FileViewerKind {\n if (explicit) return explicit\n const ext = getExtension(name)\n if (IMAGE_EXTS.has(ext)) return 'image'\n if (VIDEO_EXTS.has(ext)) return 'video'\n if (ext === 'pdf') return 'pdf'\n if (SHEET_EXTS.has(ext)) return 'sheet'\n if (ext === 'docx') return 'docx'\n if (TEXT_EXTS.has(ext)) return 'text'\n return 'text'\n}\n\nexport const FILE_VIEWER_KIND_LABEL: Record<FileViewerKind, string> = {\n image: 'IMG',\n video: 'MP4',\n pdf: 'PDF',\n sheet: 'XLS',\n docx: 'DOC',\n text: 'TXT'\n}\n\n/** 规范化数据源为 ArrayBuffer,可被 AbortController 中断(仅 URL fetch 场景生效) */\nexport async function toArrayBuffer(\n src: FileViewerItem['src'],\n signal?: AbortSignal\n): Promise<ArrayBuffer> {\n if (typeof src === 'string') {\n const res = await fetch(src, { signal })\n if (!res.ok) {\n throw new Error(`Fetch failed: ${res.status} ${res.statusText}`)\n }\n return await res.arrayBuffer()\n }\n if (src instanceof ArrayBuffer) return src\n if (src instanceof Uint8Array) {\n // 复制底层以切断共享引用,防止外部修改影响预览\n return src.slice().buffer as ArrayBuffer\n }\n // File/Blob\n return await src.arrayBuffer()\n}\n\n/** 返回用于 <img> / <video> 等标签 src 的 URL;二进制会创建 ObjectURL 并返回 revoke 钩子 */\nexport function toBlobUrl(\n src: FileViewerItem['src'],\n mime?: string\n): { url: string; revoke: () => void } {\n if (typeof src === 'string') {\n return { url: src, revoke: () => {} }\n }\n let blob: Blob\n if (src instanceof Blob) {\n blob = src\n } else if (src instanceof ArrayBuffer) {\n blob = new Blob([src], { type: mime ?? 'application/octet-stream' })\n } else {\n // Uint8Array: 复制到新的 ArrayBuffer,避免与外部共享底层\n const copy = new Uint8Array(src.byteLength)\n copy.set(src)\n blob = new Blob([copy.buffer], { type: mime ?? 'application/octet-stream' })\n }\n const url = URL.createObjectURL(blob)\n return { url, revoke: () => URL.revokeObjectURL(url) }\n}\n\n/**\n * RFC-4180 子集 CSV 解析:支持 \"\" 转义、\\r\\n 与 \\n 换行、, 分隔。\n * 为性能起见采用状态机而非 split。\n */\nexport function parseCsv(text: string): string[][] {\n const rows: string[][] = []\n let row: string[] = []\n let field = ''\n let inQuotes = false\n const len = text.length\n\n for (let i = 0; i < len; i++) {\n const ch = text[i]\n\n if (inQuotes) {\n if (ch === '\"') {\n if (text[i + 1] === '\"') {\n field += '\"'\n i++\n } else {\n inQuotes = false\n }\n } else {\n field += ch\n }\n continue\n }\n\n if (ch === '\"') {\n inQuotes = true\n } else if (ch === ',') {\n row.push(field)\n field = ''\n } else if (ch === '\\n') {\n row.push(field)\n rows.push(row)\n row = []\n field = ''\n } else if (ch === '\\r') {\n // swallow CR; LF will commit the row\n } else {\n field += ch\n }\n }\n\n // 最后一行\n if (field.length > 0 || row.length > 0) {\n row.push(field)\n rows.push(row)\n }\n return rows\n}\n\nexport function formatBytes(bytes?: number): string {\n if (bytes === undefined || bytes === null || Number.isNaN(bytes)) return '-'\n if (bytes < 1024) return `${bytes} B`\n const units = ['KB', 'MB', 'GB', 'TB']\n let v = bytes / 1024\n let i = 0\n while (v >= 1024 && i < units.length - 1) {\n v /= 1024\n i++\n }\n return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`\n}\n\n/** 触发浏览器下载 */\nexport async function downloadFile(file: FileViewerItem): Promise<void> {\n let href: string\n let revoke: (() => void) | undefined\n if (typeof file.src === 'string') {\n href = file.src\n } else {\n const url = toBlobUrl(file.src, file.mime)\n href = url.url\n revoke = url.revoke\n }\n const a = document.createElement('a')\n a.href = href\n a.download = file.name\n a.rel = 'noopener'\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n if (revoke) setTimeout(revoke, 4000)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"helper.js","names":[],"sources":["../../../src/components/file-viewer/helper.ts"],"sourcesContent":["import type { FileViewerItem, FileViewerKind } from '../../types/file-viewer'\n\nconst IMAGE_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico'])\n\nconst VIDEO_EXTS = new Set(['mp4', 'webm', 'mov', 'm4v', 'ogv', 'mkv'])\n\nconst SHEET_EXTS = new Set(['xlsx', 'xlsm', 'xlsb', 'csv'])\n\nconst TEXT_EXTS = new Set([\n 'txt',\n 'log',\n 'md',\n 'markdown',\n 'json',\n 'yml',\n 'yaml',\n 'xml',\n 'js',\n 'ts',\n 'tsx',\n 'jsx',\n 'css',\n 'scss',\n 'sass',\n 'less',\n 'html',\n 'htm',\n 'ini',\n 'toml',\n 'sh',\n 'bash',\n 'zsh',\n 'env',\n 'sql'\n])\n\nexport function getExtension(name: string): string {\n const idx = name.lastIndexOf('.')\n return idx === -1 ? '' : name.slice(idx + 1).toLowerCase()\n}\n\nexport function inferKind(name: string, explicit?: FileViewerKind): FileViewerKind {\n if (explicit) return explicit\n const ext = getExtension(name)\n if (IMAGE_EXTS.has(ext)) return 'image'\n if (VIDEO_EXTS.has(ext)) return 'video'\n if (ext === 'pdf') return 'pdf'\n if (SHEET_EXTS.has(ext)) return 'sheet'\n if (ext === 'docx') return 'docx'\n if (TEXT_EXTS.has(ext)) return 'text'\n return 'text'\n}\n\nexport const FILE_VIEWER_KIND_LABEL: Record<FileViewerKind, string> = {\n image: 'IMG',\n video: 'MP4',\n pdf: 'PDF',\n sheet: 'XLS',\n docx: 'DOC',\n text: 'TXT'\n}\n\n/** 规范化数据源为 ArrayBuffer,可被 AbortController 中断(仅 URL fetch 场景生效) */\nexport async function toArrayBuffer(\n src: FileViewerItem['src'],\n signal?: AbortSignal\n): Promise<ArrayBuffer> {\n if (typeof src === 'string') {\n const res = await fetch(src, { signal })\n if (!res.ok) {\n throw new Error(`Fetch failed: ${res.status} ${res.statusText}`)\n }\n return await res.arrayBuffer()\n }\n if (src instanceof ArrayBuffer) return src\n if (src instanceof Uint8Array) {\n // 复制底层以切断共享引用,防止外部修改影响预览\n return src.slice().buffer as ArrayBuffer\n }\n // File/Blob\n return await src.arrayBuffer()\n}\n\n/** 返回用于 <img> / <video> 等标签 src 的 URL;二进制会创建 ObjectURL 并返回 revoke 钩子 */\nexport function toBlobUrl(\n src: FileViewerItem['src'],\n mime?: string\n): { url: string; revoke: () => void } {\n if (typeof src === 'string') {\n return { url: src, revoke: () => {} }\n }\n let blob: Blob\n if (src instanceof Blob) {\n blob = src\n } else if (src instanceof ArrayBuffer) {\n blob = new Blob([src], { type: mime ?? 'application/octet-stream' })\n } else {\n // Uint8Array: 复制到新的 ArrayBuffer,避免与外部共享底层\n const copy = new Uint8Array(src.byteLength)\n copy.set(src)\n blob = new Blob([copy.buffer], { type: mime ?? 'application/octet-stream' })\n }\n const url = URL.createObjectURL(blob)\n return { url, revoke: () => URL.revokeObjectURL(url) }\n}\n\n/**\n * RFC-4180 子集 CSV 解析:支持 \"\" 转义、\\r\\n 与 \\n 换行、, 分隔。\n * 为性能起见采用状态机而非 split。\n */\nexport function parseCsv(text: string): string[][] {\n const rows: string[][] = []\n let row: string[] = []\n let field = ''\n let inQuotes = false\n const len = text.length\n\n for (let i = 0; i < len; i++) {\n const ch = text[i]\n\n if (inQuotes) {\n if (ch === '\"') {\n if (text[i + 1] === '\"') {\n field += '\"'\n i++\n } else {\n inQuotes = false\n }\n } else {\n field += ch\n }\n continue\n }\n\n if (ch === '\"') {\n inQuotes = true\n } else if (ch === ',') {\n row.push(field)\n field = ''\n } else if (ch === '\\n') {\n row.push(field)\n rows.push(row)\n row = []\n field = ''\n } else if (ch === '\\r') {\n // swallow CR; LF will commit the row\n } else {\n field += ch\n }\n }\n\n // 最后一行\n if (field.length > 0 || row.length > 0) {\n row.push(field)\n rows.push(row)\n }\n return rows\n}\n\nexport function formatBytes(bytes?: number): string {\n if (bytes === undefined || bytes === null || Number.isNaN(bytes)) return '-'\n if (bytes < 1024) return `${bytes} B`\n const units = ['KB', 'MB', 'GB', 'TB']\n let v = bytes / 1024\n let i = 0\n while (v >= 1024 && i < units.length - 1) {\n v /= 1024\n i++\n }\n return `${v.toFixed(v >= 10 ? 0 : 1)} ${units[i]}`\n}\n\n/** 触发浏览器下载 */\nexport async function downloadFile(file: FileViewerItem): Promise<void> {\n let href: string\n let revoke: (() => void) | undefined\n if (typeof file.src === 'string') {\n href = file.src\n } else {\n const url = toBlobUrl(file.src, file.mime)\n href = url.url\n revoke = url.revoke\n }\n const a = document.createElement('a')\n a.href = href\n a.download = file.name\n a.rel = 'noopener'\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n if (revoke) setTimeout(revoke, 4000)\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"docx-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/docx-previewer.vue"],"sourcesContent":["<template>\n <u-scroll tag=\"div\" :class=\"cls.e('docx')\" v-bind=\"attrs\">\n <div ref=\"container\" :class=\"cls.e('docx-body')\" />\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, useAttrs, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { UScroll } from '../../scroll'\n\ndefineOptions({ name: 'FileViewerDocxPreviewer', inheritAttrs: false })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\nconst attrs = useAttrs()\n\nconst container = useTemplateRef<HTMLDivElement>('container')\nconst loading = ref(true)\n\nlet controller: AbortController | undefined\n\nasync function load() {\n controller?.abort()\n controller = new AbortController()\n const signal = controller.signal\n loading.value = true\n\n try {\n const [{ toArrayBuffer }, docx] = await Promise.all([\n import('../helper'),\n import('docx-preview')\n ])\n\n const buf = await toArrayBuffer(props.file.src, signal)\n if (signal.aborted) return\n const el = container.value\n if (!el) return\n el.innerHTML = ''\n\n await docx.renderAsync(buf, el, undefined, {\n className: 'u-file-viewer__docx-doc',\n inWrapper: true,\n ignoreWidth: false,\n ignoreHeight: false,\n ignoreFonts: false,\n breakPages: true,\n experimental: false,\n useBase64URL: false,\n renderChanges: false,\n renderHeaders: true,\n renderFooters: true,\n renderFootnotes: true\n })\n } catch (err) {\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n controller?.abort()\n if (container.value) container.value.innerHTML = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;EAgBA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAC7B,MAAM,QAAQ,UAAS;EAEvB,MAAM,YAAY,eAA+B,YAAW;EAC5D,MAAM,UAAU,IAAI,KAAI;EAExB,IAAI;EAEJ,eAAe,OAAO;AACpB,eAAY,OAAM;AAClB,gBAAa,IAAI,iBAAgB;GACjC,MAAM,SAAS,WAAW;AAC1B,WAAQ,QAAQ;AAEhB,OAAI;IACF,MAAM,CAAC,EAAE,iBAAiB,QAAQ,MAAM,QAAQ,IAAI,CAClD,OAAO,iBACP,OAAO,gBACR,CAAA;IAED,MAAM,MAAM,MAAM,cAAc,MAAM,KAAK,KAAK,OAAM;AACtD,QAAI,OAAO,QAAS;IACpB,MAAM,KAAK,UAAU;AACrB,QAAI,CAAC,GAAI;AACT,OAAG,YAAY;AAEf,UAAM,KAAK,YAAY,KAAK,IAAI,KAAA,GAAW;KACzC,WAAW;KACX,WAAW;KACX,aAAa;KACb,cAAc;KACd,aAAa;KACb,YAAY;KACZ,cAAc;KACd,cAAc;KACd,eAAe;KACf,eAAe;KACf,eAAe;KACf,iBAAiB;KAClB,CAAA;YACM,KAAK;AACZ,QAAK,KAA2B,SAAS,aAAc;AACvD,SAAK,SAAS,IAAG;aACT;AACR,YAAQ,QAAQ;;;AAIpB,cACQ,MAAM,YACN,MAAM,EACZ,EAAE,WAAW,MAAK,CACpB;AAEA,wBAAsB;AACpB,eAAY,OAAM;AAClB,OAAI,UAAU,MAAO,WAAU,MAAM,YAAY;IAClD;;uBA5EC,YAGW,MAAA,YAAA,EAHX,WAGW;IAHD,KAAI;IAAO,OAAO,MAAA,IAAG,CAAC,EAAC,OAAA;MAAkB,MAAA,MAAK,CAAA,EAAA;2BACH,CAAnD,mBAAmD,OAAA;cAA1C;KAAJ,KAAI;KAAa,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,YAAA,CAAA;iBACvB,QAAA,SAAA,WAAA,EAAX,mBAAwD,OAAA;;KAAnC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,UAAA,CAAA;OAAa,QAAI,EAAA,IAAA,mBAAA,QAAA,KAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"docx-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/docx-previewer.vue"],"sourcesContent":["<template>\n <u-scroll tag=\"div\" :class=\"cls.e('docx')\" v-bind=\"attrs\">\n <div ref=\"container\" :class=\"cls.e('docx-body')\" />\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, useAttrs, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { UScroll } from '../../scroll'\n\ndefineOptions({ name: 'FileViewerDocxPreviewer', inheritAttrs: false })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\nconst attrs = useAttrs()\n\nconst container = useTemplateRef<HTMLDivElement>('container')\nconst loading = ref(true)\n\nlet controller: AbortController | undefined\n\nasync function load() {\n controller?.abort()\n controller = new AbortController()\n const signal = controller.signal\n loading.value = true\n\n try {\n const [{ toArrayBuffer }, docx] = await Promise.all([\n import('../helper'),\n import('docx-preview')\n ])\n\n const buf = await toArrayBuffer(props.file.src, signal)\n if (signal.aborted) return\n const el = container.value\n if (!el) return\n el.innerHTML = ''\n\n await docx.renderAsync(buf, el, undefined, {\n className: 'u-file-viewer__docx-doc',\n inWrapper: true,\n ignoreWidth: false,\n ignoreHeight: false,\n ignoreFonts: false,\n breakPages: true,\n experimental: false,\n useBase64URL: false,\n renderChanges: false,\n renderHeaders: true,\n renderFooters: true,\n renderFootnotes: true\n })\n } catch (err) {\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n controller?.abort()\n if (container.value) container.value.innerHTML = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;EAgBA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAC7B,MAAM,QAAQ,UAAS;EAEvB,MAAM,YAAY,eAA+B,YAAW;EAC5D,MAAM,UAAU,IAAI,KAAI;EAExB,IAAI;EAEJ,eAAe,OAAO;GACpB,YAAY,OAAM;GAClB,aAAa,IAAI,iBAAgB;GACjC,MAAM,SAAS,WAAW;GAC1B,QAAQ,QAAQ;GAEhB,IAAI;IACF,MAAM,CAAC,EAAE,iBAAiB,QAAQ,MAAM,QAAQ,IAAI,CAClD,OAAO,iBACP,OAAO,gBACR,CAAA;IAED,MAAM,MAAM,MAAM,cAAc,MAAM,KAAK,KAAK,OAAM;IACtD,IAAI,OAAO,SAAS;IACpB,MAAM,KAAK,UAAU;IACrB,IAAI,CAAC,IAAI;IACT,GAAG,YAAY;IAEf,MAAM,KAAK,YAAY,KAAK,IAAI,KAAA,GAAW;KACzC,WAAW;KACX,WAAW;KACX,aAAa;KACb,cAAc;KACd,aAAa;KACb,YAAY;KACZ,cAAc;KACd,cAAc;KACd,eAAe;KACf,eAAe;KACf,eAAe;KACf,iBAAiB;KAClB,CAAA;YACM,KAAK;IACZ,IAAK,KAA2B,SAAS,cAAc;IACvD,KAAK,SAAS,IAAG;aACT;IACR,QAAQ,QAAQ;;;EAIpB,YACQ,MAAM,YACN,MAAK,EACX,EAAE,WAAW,MACf,CAAA;EAEA,sBAAsB;GACpB,YAAY,OAAM;GAClB,IAAI,UAAU,OAAO,UAAU,MAAM,YAAY;IAClD;;uBA5EC,YAGW,MAAA,YAAA,EAHX,WAGW;IAHD,KAAI;IAAO,OAAO,MAAA,IAAA,CAAI,EAAC,OAAA;MAAkB,MAAA,MAAK,CAAA,EAAA;2BACH,CAAnD,mBAAmD,OAAA;cAA1C;KAAJ,KAAI;KAAa,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,YAAA,CAAA;iBACvB,QAAA,SAAA,WAAA,EAAX,mBAAwD,OAAA;;KAAnC,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,UAAA,CAAA;OAAa,QAAI,EAAA,IAAA,mBAAA,QAAA,KAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"image-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/image-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('image')\" v-bind=\"attrs\">\n <img\n v-if=\"url\"\n :src=\"url\"\n :alt=\"file.name\"\n draggable=\"false\"\n @dragstart.prevent\n @load=\"loading = false\"\n @error=\"onImgError\"\n />\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n <div v-if=\"failed\" :class=\"cls.e('loading')\">图片加载失败</div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, shallowRef, useAttrs, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { toBlobUrl } from '../helper'\n\ndefineOptions({ name: 'FileViewerImagePreviewer', inheritAttrs: false })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\nconst attrs = useAttrs()\n\nconst url = shallowRef<string>('')\nconst loading = ref(true)\nconst failed = ref(false)\n\nlet revoke: (() => void) | undefined\n\nfunction load() {\n loading.value = true\n failed.value = false\n revoke?.()\n const r = toBlobUrl(props.file.src, props.file.mime)\n url.value = r.url\n revoke = r.revoke\n}\n\nfunction onImgError(e: Event) {\n loading.value = false\n failed.value = true\n emit('error', e)\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n revoke?.()\n url.value = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;;EAyBA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAC7B,MAAM,QAAQ,UAAS;EAEvB,MAAM,MAAM,WAAmB,GAAE;EACjC,MAAM,UAAU,IAAI,KAAI;EACxB,MAAM,SAAS,IAAI,MAAK;EAExB,IAAI;EAEJ,SAAS,OAAO;AACd,WAAQ,QAAQ;AAChB,UAAO,QAAQ;AACf,aAAS;GACT,MAAM,IAAI,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,KAAI;AACnD,OAAI,QAAQ,EAAE;AACd,YAAS,EAAE;;EAGb,SAAS,WAAW,GAAU;AAC5B,WAAQ,QAAQ;AAChB,UAAO,QAAQ;AACf,QAAK,SAAS,EAAC;;AAGjB,cACQ,MAAM,YACN,MAAM,EACZ,EAAE,WAAW,MAAK,CACpB;AAEA,wBAAsB;AACpB,aAAS;AACT,OAAI,QAAQ;IACb;;uBA7DC,mBAYM,OAZN,WAYM,EAZA,OAAO,MAAA,IAAG,CAAC,EAAC,QAAA,EAAA,EAAmB,MAAA,MAAK,CAAA,EAAA;IAEhC,IAAA,SAAA,WAAA,EADR,mBAQE,OAAA;;KANC,KAAK,IAAA;KACL,KAAK,QAAA,KAAK;KACX,WAAU;KACT,aAAS,OAAA,OAAA,OAAA,KAAA,oBAAV,IAAkB,CAAA,UAAA,CAAA;KACjB,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,QAAA,QAAO;KACb,SAAO;;IAEC,QAAA,SAAA,WAAA,EAAX,mBAAwD,OAAA;;KAAnC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,UAAA,CAAA;OAAa,QAAI,EAAA,IAAA,mBAAA,QAAA,KAAA;IACvC,OAAA,SAAA,WAAA,EAAX,mBAAyD,OAAA;;KAArC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,UAAA,CAAA;OAAa,UAAM,EAAA,IAAA,mBAAA,QAAA,KAAA"}
1
+ {"version":3,"file":"image-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/image-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('image')\" v-bind=\"attrs\">\n <img\n v-if=\"url\"\n :src=\"url\"\n :alt=\"file.name\"\n draggable=\"false\"\n @dragstart.prevent\n @load=\"loading = false\"\n @error=\"onImgError\"\n />\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n <div v-if=\"failed\" :class=\"cls.e('loading')\">图片加载失败</div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, shallowRef, useAttrs, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { toBlobUrl } from '../helper'\n\ndefineOptions({ name: 'FileViewerImagePreviewer', inheritAttrs: false })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\nconst attrs = useAttrs()\n\nconst url = shallowRef<string>('')\nconst loading = ref(true)\nconst failed = ref(false)\n\nlet revoke: (() => void) | undefined\n\nfunction load() {\n loading.value = true\n failed.value = false\n revoke?.()\n const r = toBlobUrl(props.file.src, props.file.mime)\n url.value = r.url\n revoke = r.revoke\n}\n\nfunction onImgError(e: Event) {\n loading.value = false\n failed.value = true\n emit('error', e)\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n revoke?.()\n url.value = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;;EAyBA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAC7B,MAAM,QAAQ,UAAS;EAEvB,MAAM,MAAM,WAAmB,GAAE;EACjC,MAAM,UAAU,IAAI,KAAI;EACxB,MAAM,SAAS,IAAI,MAAK;EAExB,IAAI;EAEJ,SAAS,OAAO;GACd,QAAQ,QAAQ;GAChB,OAAO,QAAQ;GACf,UAAS;GACT,MAAM,IAAI,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,KAAI;GACnD,IAAI,QAAQ,EAAE;GACd,SAAS,EAAE;;EAGb,SAAS,WAAW,GAAU;GAC5B,QAAQ,QAAQ;GAChB,OAAO,QAAQ;GACf,KAAK,SAAS,EAAC;;EAGjB,YACQ,MAAM,YACN,MAAK,EACX,EAAE,WAAW,MACf,CAAA;EAEA,sBAAsB;GACpB,UAAS;GACT,IAAI,QAAQ;IACb;;uBA7DC,mBAYM,OAZN,WAYM,EAZA,OAAO,MAAA,IAAA,CAAI,EAAC,QAAA,EAAA,EAAmB,MAAA,MAAK,CAAA,EAAA;IAEhC,IAAA,SAAA,WAAA,EADR,mBAQE,OAAA;;KANC,KAAK,IAAA;KACL,KAAK,QAAA,KAAK;KACX,WAAU;KACT,aAAS,OAAA,OAAA,OAAA,KAAA,oBAAV,IAAkB,CAAA,UAAA,CAAA;KACjB,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,QAAA,QAAO;KACb,SAAO;;IAEC,QAAA,SAAA,WAAA,EAAX,mBAAwD,OAAA;;KAAnC,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,UAAA,CAAA;OAAa,QAAI,EAAA,IAAA,mBAAA,QAAA,KAAA;IACvC,OAAA,SAAA,WAAA,EAAX,mBAAyD,OAAA;;KAArC,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,UAAA,CAAA;OAAa,UAAM,EAAA,IAAA,mBAAA,QAAA,KAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pdf-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/pdf-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('pdf')\" v-bind=\"attrs\">\n <div v-if=\"!engine || isLoading\" :class=\"cls.e('loading')\">正在加载 PDF 引擎…</div>\n <EmbedPDF v-else-if=\"pdfUrl\" :engine=\"engine\" :plugins=\"plugins\" v-slot=\"{ activeDocumentId }\">\n <PdfZoomBridge\n v-if=\"activeDocumentId\"\n :document-id=\"activeDocumentId\"\n @zoom-change=\"onZoomChange\"\n />\n <DocumentContent\n v-if=\"activeDocumentId\"\n :document-id=\"activeDocumentId\"\n v-slot=\"{ isLoaded }\"\n >\n <Viewport v-if=\"isLoaded\" :document-id=\"activeDocumentId\" :class=\"cls.e('pdf-viewport')\">\n <PdfViewportScroll>\n <ZoomGestureWrapper :document-id=\"activeDocumentId\" :enable-wheel=\"true\">\n <Scroller :document-id=\"activeDocumentId\">\n <template #default=\"{ page }\">\n <div\n :class=\"cls.e('pdf-page')\"\n :style=\"{ width: page.width + 'px', height: page.height + 'px' }\"\n >\n <RenderLayer :document-id=\"activeDocumentId\" :page-index=\"page.pageIndex\" />\n </div>\n </template>\n </Scroller>\n </ZoomGestureWrapper>\n </PdfViewportScroll>\n </Viewport>\n </DocumentContent>\n </EmbedPDF>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { createPluginRegistration } from '@embedpdf/core'\nimport { EmbedPDF } from '@embedpdf/core/vue'\nimport { usePdfiumEngine } from '@embedpdf/engines/vue'\nimport {\n DocumentContent,\n DocumentManagerPluginPackage\n} from '@embedpdf/plugin-document-manager/vue'\nimport { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/vue'\nimport { ScrollPluginPackage, Scroller } from '@embedpdf/plugin-scroll/vue'\nimport { ViewportPluginPackage, Viewport } from '@embedpdf/plugin-viewport/vue'\nimport {\n ZoomGestureWrapper,\n ZoomMode,\n ZoomPluginPackage,\n type ZoomScope\n} from '@embedpdf/plugin-zoom/vue'\nimport { bem } from '@veltra/utils'\nimport { computed, onBeforeUnmount, shallowRef, useAttrs, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { toBlobUrl } from '../helper'\nimport PdfViewportScroll from './pdf-viewport-scroll.vue'\nimport PdfZoomBridge from './pdf-zoom-bridge.vue'\n\ndefineOptions({ name: 'FileViewerPdfPreviewer', inheritAttrs: false })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{\n (e: 'error', err: unknown): void\n (e: 'zoom-change', level: number): void\n}>()\n\nconst cls = bem('file-viewer')\nconst attrs = useAttrs()\n\nconst { engine, isLoading, error } = usePdfiumEngine()\n\nconst pdfUrl = shallowRef<string>('')\nlet revoke: (() => void) | undefined\n\n/** 当前文档的 zoom API(由 PdfZoomBridge 注入) */\nlet zoomScope: ZoomScope | null = null\n\nfunction load() {\n revoke?.()\n const r = toBlobUrl(props.file.src, props.file.mime ?? 'application/pdf')\n pdfUrl.value = r.url\n revoke = r.revoke\n}\n\nconst plugins = computed(() => {\n if (!pdfUrl.value) return []\n return [\n createPluginRegistration(DocumentManagerPluginPackage, {\n initialDocuments: [{ url: pdfUrl.value }]\n }),\n createPluginRegistration(ViewportPluginPackage, { viewportGap: 0 }),\n createPluginRegistration(ScrollPluginPackage, { defaultBufferSize: 2 }),\n createPluginRegistration(RenderPluginPackage),\n createPluginRegistration(ZoomPluginPackage, {\n defaultZoomLevel: ZoomMode.FitPage,\n minZoom: 0.5,\n maxZoom: 3,\n zoomStep: 0.1\n })\n ]\n})\n\nfunction onZoomChange(payload: { level: number; scope: ZoomScope | null }) {\n zoomScope = payload.scope\n emit('zoom-change', payload.level)\n}\n\nfunction zoomIn() {\n zoomScope?.zoomIn()\n}\n\nfunction zoomOut() {\n zoomScope?.zoomOut()\n}\n\nfunction resetZoom() {\n zoomScope?.requestZoom(ZoomMode.FitPage)\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nwatch(error, (err) => {\n if (err) emit('error', err)\n})\n\nonBeforeUnmount(() => {\n revoke?.()\n pdfUrl.value = ''\n zoomScope = null\n})\n\ndefineExpose({\n zoomIn,\n zoomOut,\n resetZoom\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EA8DA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAKb,MAAM,MAAM,IAAI,cAAa;EAC7B,MAAM,QAAQ,UAAS;EAEvB,MAAM,EAAE,QAAQ,WAAW,UAAU,iBAAgB;EAErD,MAAM,SAAS,WAAmB,GAAE;EACpC,IAAI;;EAGJ,IAAI,YAA8B;EAElC,SAAS,OAAO;AACd,aAAS;GACT,MAAM,IAAI,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,QAAQ,kBAAiB;AACxE,UAAO,QAAQ,EAAE;AACjB,YAAS,EAAE;;EAGb,MAAM,UAAU,eAAe;AAC7B,OAAI,CAAC,OAAO,MAAO,QAAO,EAAC;AAC3B,UAAO;IACL,yBAAyB,8BAA8B,EACrD,kBAAkB,CAAC,EAAE,KAAK,OAAO,OAAO,CAAA,EACzC,CAAC;IACF,yBAAyB,uBAAuB,EAAE,aAAa,GAAG,CAAC;IACnE,yBAAyB,qBAAqB,EAAE,mBAAmB,GAAG,CAAC;IACvE,yBAAyB,oBAAoB;IAC7C,yBAAyB,mBAAmB;KAC1C,kBAAkB,SAAS;KAC3B,SAAS;KACT,SAAS;KACT,UAAU;KACX,CAAA;IACH;IACD;EAED,SAAS,aAAa,SAAqD;AACzE,eAAY,QAAQ;AACpB,QAAK,eAAe,QAAQ,MAAK;;EAGnC,SAAS,SAAS;AAChB,cAAW,QAAO;;EAGpB,SAAS,UAAU;AACjB,cAAW,SAAQ;;EAGrB,SAAS,YAAY;AACnB,cAAW,YAAY,SAAS,QAAO;;AAGzC,cACQ,MAAM,YACN,MAAM,EACZ,EAAE,WAAW,MAAK,CACpB;AAEA,QAAM,QAAQ,QAAQ;AACpB,OAAI,IAAK,MAAK,SAAS,IAAG;IAC3B;AAED,wBAAsB;AACpB,aAAS;AACT,UAAO,QAAQ;AACf,eAAY;IACb;AAED,WAAa;GACX;GACA;GACA;GACD,CAAA;;uBA7IC,mBA+BM,OA/BN,WA+BM,EA/BA,OAAO,MAAA,IAAG,CAAC,EAAC,MAAA,EAAA,EAAiB,MAAA,MAAK,CAAA,EAAA,CAAA,CAC1B,MAAA,OAAM,IAAI,MAAA,UAAS,IAAA,WAAA,EAA/B,mBAA6E,OAAA;;IAA3C,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,UAAA,CAAA;MAAa,gBAAY,EAAA,IAClD,OAAA,SAAA,WAAA,EAArB,YA4BW,MAAA,SAAA,EAAA;;IA5BmB,QAAQ,MAAA,OAAM;IAAG,SAAS,QAAA;;sBAKpD,EALuE,uBAAgB,CAEjF,oBAAA,WAAA,EADR,YAIE,aAAA;;KAFC,eAAa;KACA;sEAGR,oBAAA,WAAA,EADR,YAqBkB,MAAA,gBAAA,EAAA;;KAnBf,eAAa;;uBAkBH,EAjBD,eAAQ,CAEF,YAAA,WAAA,EAAhB,YAeW,MAAA,SAAA,EAAA;;MAfgB,eAAa;MAAmB,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,eAAA,CAAA;;6BAcjD,CAbpB,YAaoB,aAAA,MAAA;8BADG,CAXrB,YAWqB,MAAA,mBAAA,EAAA;QAXA,eAAa;QAAmB,gBAAc;;+BAUtD,CATX,YASW,MAAA,SAAA,EAAA,EATA,eAAa,kBAAgB,EAAA;SAC3B,SAAO,SAMV,EANc,WAAI,CACxB,mBAKM,OAAA;UAJH,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,WAAA,CAAA;UACZ,OAAK,eAAA;WAAA,OAAW,KAAK,QAAK;WAAA,QAAiB,KAAK,SAAM;WAAA,CAAA;aAEvD,YAA4E,MAAA,YAAA,EAAA;UAA9D,eAAa;UAAmB,cAAY,KAAK"}
1
+ {"version":3,"file":"pdf-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/pdf-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('pdf')\" v-bind=\"attrs\">\n <div v-if=\"!engine || isLoading\" :class=\"cls.e('loading')\">正在加载 PDF 引擎…</div>\n <EmbedPDF v-else-if=\"pdfUrl\" :engine=\"engine\" :plugins=\"plugins\" v-slot=\"{ activeDocumentId }\">\n <PdfZoomBridge\n v-if=\"activeDocumentId\"\n :document-id=\"activeDocumentId\"\n @zoom-change=\"onZoomChange\"\n />\n <DocumentContent\n v-if=\"activeDocumentId\"\n :document-id=\"activeDocumentId\"\n v-slot=\"{ isLoaded }\"\n >\n <Viewport v-if=\"isLoaded\" :document-id=\"activeDocumentId\" :class=\"cls.e('pdf-viewport')\">\n <PdfViewportScroll>\n <ZoomGestureWrapper :document-id=\"activeDocumentId\" :enable-wheel=\"true\">\n <Scroller :document-id=\"activeDocumentId\">\n <template #default=\"{ page }\">\n <div\n :class=\"cls.e('pdf-page')\"\n :style=\"{ width: page.width + 'px', height: page.height + 'px' }\"\n >\n <RenderLayer :document-id=\"activeDocumentId\" :page-index=\"page.pageIndex\" />\n </div>\n </template>\n </Scroller>\n </ZoomGestureWrapper>\n </PdfViewportScroll>\n </Viewport>\n </DocumentContent>\n </EmbedPDF>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { createPluginRegistration } from '@embedpdf/core'\nimport { EmbedPDF } from '@embedpdf/core/vue'\nimport { usePdfiumEngine } from '@embedpdf/engines/vue'\nimport {\n DocumentContent,\n DocumentManagerPluginPackage\n} from '@embedpdf/plugin-document-manager/vue'\nimport { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/vue'\nimport { ScrollPluginPackage, Scroller } from '@embedpdf/plugin-scroll/vue'\nimport { ViewportPluginPackage, Viewport } from '@embedpdf/plugin-viewport/vue'\nimport {\n ZoomGestureWrapper,\n ZoomMode,\n ZoomPluginPackage,\n type ZoomScope\n} from '@embedpdf/plugin-zoom/vue'\nimport { bem } from '@veltra/utils'\nimport { computed, onBeforeUnmount, shallowRef, useAttrs, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { toBlobUrl } from '../helper'\nimport PdfViewportScroll from './pdf-viewport-scroll.vue'\nimport PdfZoomBridge from './pdf-zoom-bridge.vue'\n\ndefineOptions({ name: 'FileViewerPdfPreviewer', inheritAttrs: false })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{\n (e: 'error', err: unknown): void\n (e: 'zoom-change', level: number): void\n}>()\n\nconst cls = bem('file-viewer')\nconst attrs = useAttrs()\n\nconst { engine, isLoading, error } = usePdfiumEngine()\n\nconst pdfUrl = shallowRef<string>('')\nlet revoke: (() => void) | undefined\n\n/** 当前文档的 zoom API(由 PdfZoomBridge 注入) */\nlet zoomScope: ZoomScope | null = null\n\nfunction load() {\n revoke?.()\n const r = toBlobUrl(props.file.src, props.file.mime ?? 'application/pdf')\n pdfUrl.value = r.url\n revoke = r.revoke\n}\n\nconst plugins = computed(() => {\n if (!pdfUrl.value) return []\n return [\n createPluginRegistration(DocumentManagerPluginPackage, {\n initialDocuments: [{ url: pdfUrl.value }]\n }),\n createPluginRegistration(ViewportPluginPackage, { viewportGap: 0 }),\n createPluginRegistration(ScrollPluginPackage, { defaultBufferSize: 2 }),\n createPluginRegistration(RenderPluginPackage),\n createPluginRegistration(ZoomPluginPackage, {\n defaultZoomLevel: ZoomMode.FitPage,\n minZoom: 0.5,\n maxZoom: 3,\n zoomStep: 0.1\n })\n ]\n})\n\nfunction onZoomChange(payload: { level: number; scope: ZoomScope | null }) {\n zoomScope = payload.scope\n emit('zoom-change', payload.level)\n}\n\nfunction zoomIn() {\n zoomScope?.zoomIn()\n}\n\nfunction zoomOut() {\n zoomScope?.zoomOut()\n}\n\nfunction resetZoom() {\n zoomScope?.requestZoom(ZoomMode.FitPage)\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nwatch(error, (err) => {\n if (err) emit('error', err)\n})\n\nonBeforeUnmount(() => {\n revoke?.()\n pdfUrl.value = ''\n zoomScope = null\n})\n\ndefineExpose({\n zoomIn,\n zoomOut,\n resetZoom\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EA8DA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAKb,MAAM,MAAM,IAAI,cAAa;EAC7B,MAAM,QAAQ,UAAS;EAEvB,MAAM,EAAE,QAAQ,WAAW,UAAU,iBAAgB;EAErD,MAAM,SAAS,WAAmB,GAAE;EACpC,IAAI;;EAGJ,IAAI,YAA8B;EAElC,SAAS,OAAO;GACd,UAAS;GACT,MAAM,IAAI,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,QAAQ,kBAAiB;GACxE,OAAO,QAAQ,EAAE;GACjB,SAAS,EAAE;;EAGb,MAAM,UAAU,eAAe;GAC7B,IAAI,CAAC,OAAO,OAAO,OAAO,EAAC;GAC3B,OAAO;IACL,yBAAyB,8BAA8B,EACrD,kBAAkB,CAAC,EAAE,KAAK,OAAO,OAAO,CAAA,EACzC,CAAA;IACD,yBAAyB,uBAAuB,EAAE,aAAa,GAAG,CAAA;IAClE,yBAAyB,qBAAqB,EAAE,mBAAmB,GAAG,CAAA;IACtE,yBAAyB,oBAAmB;IAC5C,yBAAyB,mBAAmB;KAC1C,kBAAkB,SAAS;KAC3B,SAAS;KACT,SAAS;KACT,UAAU;KACX,CAAA;IACH;IACD;EAED,SAAS,aAAa,SAAqD;GACzE,YAAY,QAAQ;GACpB,KAAK,eAAe,QAAQ,MAAK;;EAGnC,SAAS,SAAS;GAChB,WAAW,QAAO;;EAGpB,SAAS,UAAU;GACjB,WAAW,SAAQ;;EAGrB,SAAS,YAAY;GACnB,WAAW,YAAY,SAAS,QAAO;;EAGzC,YACQ,MAAM,YACN,MAAK,EACX,EAAE,WAAW,MACf,CAAA;EAEA,MAAM,QAAQ,QAAQ;GACpB,IAAI,KAAK,KAAK,SAAS,IAAG;IAC3B;EAED,sBAAsB;GACpB,UAAS;GACT,OAAO,QAAQ;GACf,YAAY;IACb;EAED,SAAa;GACX;GACA;GACA;GACD,CAAA;;uBA7IC,mBA+BM,OA/BN,WA+BM,EA/BA,OAAO,MAAA,IAAA,CAAI,EAAC,MAAA,EAAA,EAAiB,MAAA,MAAK,CAAA,EAAA,CAAA,CAC1B,MAAA,OAAA,IAAU,MAAA,UAAA,IAAA,WAAA,EAAtB,mBAA6E,OAAA;;IAA3C,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,UAAA,CAAA;MAAa,gBAAY,EAAA,IAClD,OAAA,SAAA,WAAA,EAArB,YA4BW,MAAA,SAAA,EAAA;;IA5BmB,QAAQ,MAAA,OAAA;IAAS,SAAS,QAAA;;sBAKpD,EALuE,uBAAgB,CAEjF,oBAAA,WAAA,EADR,YAIE,aAAA;;KAFC,eAAa;KACA;sEAGR,oBAAA,WAAA,EADR,YAqBkB,MAAA,gBAAA,EAAA;;KAnBf,eAAa;;uBAkBH,EAjBD,eAAQ,CAEF,YAAA,WAAA,EAAhB,YAeW,MAAA,SAAA,EAAA;;MAfgB,eAAa;MAAmB,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,eAAA,CAAA;;6BAcjD,CAbpB,YAaoB,aAAA,MAAA;8BADG,CAXrB,YAWqB,MAAA,mBAAA,EAAA;QAXA,eAAa;QAAmB,gBAAc;;+BAUtD,CATX,YASW,MAAA,SAAA,EAAA,EATA,eAAa,kBAAgB,EAAA;SAC3B,SAAO,SAMV,EANc,WAAI,CACxB,mBAKM,OAAA;UAJH,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,WAAA,CAAA;UACZ,OAAK,eAAA;WAAA,OAAW,KAAK,QAAK;WAAA,QAAiB,KAAK,SAAM;WAAA,CAAA;aAEvD,YAA4E,MAAA,YAAA,EAAA;UAA9D,eAAa;UAAmB,cAAY,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"pdf-viewport-scroll.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/pdf-viewport-scroll.vue"],"sourcesContent":["<template>\n <!-- EmbedPDF Viewport 自带滚动;u-scroll 仅同步自定义滚动条 -->\n <u-scroll :target=\"viewportEl\">\n <slot />\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, inject, onBeforeUnmount, type Ref, watch } from 'vue'\n\nimport { UScroll } from '../../scroll'\n\ndefineOptions({ name: 'FileViewerPdfViewportScroll' })\n\n/** EmbedPDF Viewport 向子树注入的滚动容器 ref */\nconst VIEWPORT_ELEMENT_KEY = 'viewport-element'\n\nconst viewportRef = inject<Ref<HTMLDivElement | null>>(VIEWPORT_ELEMENT_KEY)\nconst viewportEl = computed(() => viewportRef?.value ?? null)\n\nlet panState:\n | {\n pointerId: number\n startX: number\n startY: number\n scrollLeft: number\n scrollTop: number\n }\n | undefined\n\nfunction isInteractiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n return !!target.closest(\n 'button, a, input, textarea, select, video, [contenteditable=\"true\"], .u-scroll__bar-x, .u-scroll__bar-y'\n )\n}\n\nfunction handlePointerDown(e: PointerEvent) {\n if (e.button !== 0 || isInteractiveTarget(e.target)) return\n\n const el = viewportEl.value\n if (!el) return\n\n panState = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startY: e.clientY,\n scrollLeft: el.scrollLeft,\n scrollTop: el.scrollTop\n }\n el.classList.add('is-panning')\n el.setPointerCapture(e.pointerId)\n e.preventDefault()\n}\n\nfunction handlePointerMove(e: PointerEvent) {\n if (!panState || panState.pointerId !== e.pointerId) return\n\n const el = viewportEl.value\n if (!el) return\n\n el.scrollLeft = panState.scrollLeft - (e.clientX - panState.startX)\n el.scrollTop = panState.scrollTop - (e.clientY - panState.startY)\n}\n\nfunction endPan(e: PointerEvent) {\n if (!panState || panState.pointerId !== e.pointerId) return\n\n const el = viewportEl.value\n if (el?.hasPointerCapture(e.pointerId)) {\n el.releasePointerCapture(e.pointerId)\n }\n el?.classList.remove('is-panning')\n panState = undefined\n}\n\nlet detachPan: (() => void) | undefined\n\nwatch(\n viewportEl,\n (el, _, onCleanup) => {\n detachPan?.()\n detachPan = undefined\n if (!el) return\n\n el.addEventListener('pointerdown', handlePointerDown, { capture: true })\n el.addEventListener('pointermove', handlePointerMove, { capture: true })\n el.addEventListener('pointerup', endPan, { capture: true })\n el.addEventListener('pointercancel', endPan, { capture: true })\n\n detachPan = () => {\n el.removeEventListener('pointerdown', handlePointerDown, { capture: true })\n el.removeEventListener('pointermove', handlePointerMove, { capture: true })\n el.removeEventListener('pointerup', endPan, { capture: true })\n el.removeEventListener('pointercancel', endPan, { capture: true })\n el.classList.remove('is-panning')\n panState = undefined\n }\n\n onCleanup(() => {\n detachPan?.()\n detachPan = undefined\n })\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n detachPan?.()\n detachPan = undefined\n})\n</script>\n"],"mappings":";;;AAeA,MAAM,uBAAuB;;;;;;EAE7B,MAAM,cAAc,OAAmC,qBAAoB;EAC3E,MAAM,aAAa,eAAe,aAAa,SAAS,KAAI;EAE5D,IAAI;EAUJ,SAAS,oBAAoB,QAAqC;AAChE,OAAI,EAAE,kBAAkB,aAAc,QAAO;AAC7C,UAAO,CAAC,CAAC,OAAO,QACd,4GACF;;EAGF,SAAS,kBAAkB,GAAiB;AAC1C,OAAI,EAAE,WAAW,KAAK,oBAAoB,EAAE,OAAO,CAAE;GAErD,MAAM,KAAK,WAAW;AACtB,OAAI,CAAC,GAAI;AAET,cAAW;IACT,WAAW,EAAE;IACb,QAAQ,EAAE;IACV,QAAQ,EAAE;IACV,YAAY,GAAG;IACf,WAAW,GAAG;IAChB;AACA,MAAG,UAAU,IAAI,aAAY;AAC7B,MAAG,kBAAkB,EAAE,UAAS;AAChC,KAAE,gBAAe;;EAGnB,SAAS,kBAAkB,GAAiB;AAC1C,OAAI,CAAC,YAAY,SAAS,cAAc,EAAE,UAAW;GAErD,MAAM,KAAK,WAAW;AACtB,OAAI,CAAC,GAAI;AAET,MAAG,aAAa,SAAS,cAAc,EAAE,UAAU,SAAS;AAC5D,MAAG,YAAY,SAAS,aAAa,EAAE,UAAU,SAAS;;EAG5D,SAAS,OAAO,GAAiB;AAC/B,OAAI,CAAC,YAAY,SAAS,cAAc,EAAE,UAAW;GAErD,MAAM,KAAK,WAAW;AACtB,OAAI,IAAI,kBAAkB,EAAE,UAAU,CACpC,IAAG,sBAAsB,EAAE,UAAS;AAEtC,OAAI,UAAU,OAAO,aAAY;AACjC,cAAW,KAAA;;EAGb,IAAI;AAEJ,QACE,aACC,IAAI,GAAG,cAAc;AACpB,gBAAY;AACZ,eAAY,KAAA;AACZ,OAAI,CAAC,GAAI;AAET,MAAG,iBAAiB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;AACvE,MAAG,iBAAiB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;AACvE,MAAG,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAA;AAC1D,MAAG,iBAAiB,iBAAiB,QAAQ,EAAE,SAAS,MAAM,CAAA;AAE9D,qBAAkB;AAChB,OAAG,oBAAoB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;AAC1E,OAAG,oBAAoB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;AAC1E,OAAG,oBAAoB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAA;AAC7D,OAAG,oBAAoB,iBAAiB,QAAQ,EAAE,SAAS,MAAM,CAAA;AACjE,OAAG,UAAU,OAAO,aAAY;AAChC,eAAW,KAAA;;AAGb,mBAAgB;AACd,iBAAY;AACZ,gBAAY,KAAA;KACb;KAEH,EAAE,WAAW,MAAK,CACpB;AAEA,wBAAsB;AACpB,gBAAY;AACZ,eAAY,KAAA;IACb;;2DA7GC,mBAAA,8CAAkD,EAClD,YAEW,MAAA,YAAA,EAAA,EAFA,QAAQ,WAAA,OAAU,EAAA;2BACnB,CAAR,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"pdf-viewport-scroll.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/pdf-viewport-scroll.vue"],"sourcesContent":["<template>\n <!-- EmbedPDF Viewport 自带滚动;u-scroll 仅同步自定义滚动条 -->\n <u-scroll :target=\"viewportEl\">\n <slot />\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, inject, onBeforeUnmount, type Ref, watch } from 'vue'\n\nimport { UScroll } from '../../scroll'\n\ndefineOptions({ name: 'FileViewerPdfViewportScroll' })\n\n/** EmbedPDF Viewport 向子树注入的滚动容器 ref */\nconst VIEWPORT_ELEMENT_KEY = 'viewport-element'\n\nconst viewportRef = inject<Ref<HTMLDivElement | null>>(VIEWPORT_ELEMENT_KEY)\nconst viewportEl = computed(() => viewportRef?.value ?? null)\n\nlet panState:\n | {\n pointerId: number\n startX: number\n startY: number\n scrollLeft: number\n scrollTop: number\n }\n | undefined\n\nfunction isInteractiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n return !!target.closest(\n 'button, a, input, textarea, select, video, [contenteditable=\"true\"], .u-scroll__bar-x, .u-scroll__bar-y'\n )\n}\n\nfunction handlePointerDown(e: PointerEvent) {\n if (e.button !== 0 || isInteractiveTarget(e.target)) return\n\n const el = viewportEl.value\n if (!el) return\n\n panState = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startY: e.clientY,\n scrollLeft: el.scrollLeft,\n scrollTop: el.scrollTop\n }\n el.classList.add('is-panning')\n el.setPointerCapture(e.pointerId)\n e.preventDefault()\n}\n\nfunction handlePointerMove(e: PointerEvent) {\n if (!panState || panState.pointerId !== e.pointerId) return\n\n const el = viewportEl.value\n if (!el) return\n\n el.scrollLeft = panState.scrollLeft - (e.clientX - panState.startX)\n el.scrollTop = panState.scrollTop - (e.clientY - panState.startY)\n}\n\nfunction endPan(e: PointerEvent) {\n if (!panState || panState.pointerId !== e.pointerId) return\n\n const el = viewportEl.value\n if (el?.hasPointerCapture(e.pointerId)) {\n el.releasePointerCapture(e.pointerId)\n }\n el?.classList.remove('is-panning')\n panState = undefined\n}\n\nlet detachPan: (() => void) | undefined\n\nwatch(\n viewportEl,\n (el, _, onCleanup) => {\n detachPan?.()\n detachPan = undefined\n if (!el) return\n\n el.addEventListener('pointerdown', handlePointerDown, { capture: true })\n el.addEventListener('pointermove', handlePointerMove, { capture: true })\n el.addEventListener('pointerup', endPan, { capture: true })\n el.addEventListener('pointercancel', endPan, { capture: true })\n\n detachPan = () => {\n el.removeEventListener('pointerdown', handlePointerDown, { capture: true })\n el.removeEventListener('pointermove', handlePointerMove, { capture: true })\n el.removeEventListener('pointerup', endPan, { capture: true })\n el.removeEventListener('pointercancel', endPan, { capture: true })\n el.classList.remove('is-panning')\n panState = undefined\n }\n\n onCleanup(() => {\n detachPan?.()\n detachPan = undefined\n })\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n detachPan?.()\n detachPan = undefined\n})\n</script>\n"],"mappings":";;;AAeA,MAAM,uBAAuB;;;;;;EAE7B,MAAM,cAAc,OAAmC,qBAAoB;EAC3E,MAAM,aAAa,eAAe,aAAa,SAAS,KAAI;EAE5D,IAAI;EAUJ,SAAS,oBAAoB,QAAqC;GAChE,IAAI,EAAE,kBAAkB,cAAc,OAAO;GAC7C,OAAO,CAAC,CAAC,OAAO,QACd,4GACF;;EAGF,SAAS,kBAAkB,GAAiB;GAC1C,IAAI,EAAE,WAAW,KAAK,oBAAoB,EAAE,OAAM,EAAG;GAErD,MAAM,KAAK,WAAW;GACtB,IAAI,CAAC,IAAI;GAET,WAAW;IACT,WAAW,EAAE;IACb,QAAQ,EAAE;IACV,QAAQ,EAAE;IACV,YAAY,GAAG;IACf,WAAW,GAAG;IAChB;GACA,GAAG,UAAU,IAAI,aAAY;GAC7B,GAAG,kBAAkB,EAAE,UAAS;GAChC,EAAE,gBAAe;;EAGnB,SAAS,kBAAkB,GAAiB;GAC1C,IAAI,CAAC,YAAY,SAAS,cAAc,EAAE,WAAW;GAErD,MAAM,KAAK,WAAW;GACtB,IAAI,CAAC,IAAI;GAET,GAAG,aAAa,SAAS,cAAc,EAAE,UAAU,SAAS;GAC5D,GAAG,YAAY,SAAS,aAAa,EAAE,UAAU,SAAS;;EAG5D,SAAS,OAAO,GAAiB;GAC/B,IAAI,CAAC,YAAY,SAAS,cAAc,EAAE,WAAW;GAErD,MAAM,KAAK,WAAW;GACtB,IAAI,IAAI,kBAAkB,EAAE,UAAS,EACnC,GAAG,sBAAsB,EAAE,UAAS;GAEtC,IAAI,UAAU,OAAO,aAAY;GACjC,WAAW,KAAA;;EAGb,IAAI;EAEJ,MACE,aACC,IAAI,GAAG,cAAc;GACpB,aAAY;GACZ,YAAY,KAAA;GACZ,IAAI,CAAC,IAAI;GAET,GAAG,iBAAiB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;GACvE,GAAG,iBAAiB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;GACvE,GAAG,iBAAiB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAA;GAC1D,GAAG,iBAAiB,iBAAiB,QAAQ,EAAE,SAAS,MAAM,CAAA;GAE9D,kBAAkB;IAChB,GAAG,oBAAoB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;IAC1E,GAAG,oBAAoB,eAAe,mBAAmB,EAAE,SAAS,MAAM,CAAA;IAC1E,GAAG,oBAAoB,aAAa,QAAQ,EAAE,SAAS,MAAM,CAAA;IAC7D,GAAG,oBAAoB,iBAAiB,QAAQ,EAAE,SAAS,MAAM,CAAA;IACjE,GAAG,UAAU,OAAO,aAAY;IAChC,WAAW,KAAA;;GAGb,gBAAgB;IACd,aAAY;IACZ,YAAY,KAAA;KACb;KAEH,EAAE,WAAW,MACf,CAAA;EAEA,sBAAsB;GACpB,aAAY;GACZ,YAAY,KAAA;IACb;;2DA7GC,mBAAA,8CAAA,EACA,YAEW,MAAA,YAAA,EAAA,EAFA,QAAQ,WAAA,OAAU,EAAA;2BACnB,CAAR,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pdf-zoom-bridge.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/pdf-zoom-bridge.vue"],"sourcesContent":["<template>\n <span hidden aria-hidden=\"true\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { useZoom, ZoomMode, type ZoomScope } from '@embedpdf/plugin-zoom/vue'\nimport { watch } from 'vue'\n\ndefineOptions({ name: 'FileViewerPdfZoomBridge' })\n\nconst props = defineProps<{ documentId: string }>()\n\nconst emit = defineEmits<{\n (e: 'zoom-change', payload: { level: number; scope: ZoomScope | null }): void\n}>()\n\nconst { provides: zoom, state } = useZoom(() => props.documentId)\n\nwatch(\n [zoom, () => state.value.currentZoomLevel],\n () => {\n emit('zoom-change', {\n level: state.value.currentZoomLevel,\n scope: zoom.value\n })\n },\n { immediate: true }\n)\n\ndefineExpose({\n resetZoom: () => zoom.value?.requestZoom(ZoomMode.FitPage)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;EAUA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAIb,MAAM,EAAE,UAAU,MAAM,UAAU,cAAc,MAAM,WAAU;AAEhE,QACE,CAAC,YAAY,MAAM,MAAM,iBAAiB,QACpC;AACJ,QAAK,eAAe;IAClB,OAAO,MAAM,MAAM;IACnB,OAAO,KAAK;IACb,CAAA;KAEH,EAAE,WAAW,MAAK,CACpB;AAEA,WAAa,EACX,iBAAiB,KAAK,OAAO,YAAY,SAAS,QAAO,EAC1D,CAAA;;uBA9BC,mBAAkC,QAAlC,WAAkC"}
1
+ {"version":3,"file":"pdf-zoom-bridge.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/pdf-zoom-bridge.vue"],"sourcesContent":["<template>\n <span hidden aria-hidden=\"true\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { useZoom, ZoomMode, type ZoomScope } from '@embedpdf/plugin-zoom/vue'\nimport { watch } from 'vue'\n\ndefineOptions({ name: 'FileViewerPdfZoomBridge' })\n\nconst props = defineProps<{ documentId: string }>()\n\nconst emit = defineEmits<{\n (e: 'zoom-change', payload: { level: number; scope: ZoomScope | null }): void\n}>()\n\nconst { provides: zoom, state } = useZoom(() => props.documentId)\n\nwatch(\n [zoom, () => state.value.currentZoomLevel],\n () => {\n emit('zoom-change', {\n level: state.value.currentZoomLevel,\n scope: zoom.value\n })\n },\n { immediate: true }\n)\n\ndefineExpose({\n resetZoom: () => zoom.value?.requestZoom(ZoomMode.FitPage)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;EAUA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAIb,MAAM,EAAE,UAAU,MAAM,UAAU,cAAc,MAAM,WAAU;EAEhE,MACE,CAAC,YAAY,MAAM,MAAM,iBAAgB,QACnC;GACJ,KAAK,eAAe;IAClB,OAAO,MAAM,MAAM;IACnB,OAAO,KAAK;IACb,CAAA;KAEH,EAAE,WAAW,MACf,CAAA;EAEA,SAAa,EACX,iBAAiB,KAAK,OAAO,YAAY,SAAS,QAAO,EAC1D,CAAA;;uBA9BC,mBAAkC,QAAlC,WAAkC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/sheet-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('sheet')\">\n <div v-if=\"truncated\" :class=\"cls.e('sheet-note')\">\n 仅展示前 {{ renderedRows.toLocaleString() }} 行(共 {{ totalRows.toLocaleString() }} 行)\n </div>\n <div v-if=\"sheets.length > 1\" :class=\"cls.e('sheet-tabs')\">\n <button\n v-for=\"(s, i) in sheets\"\n :key=\"s.name\"\n :class=\"[cls.e('sheet-tab'), bem.is('active', i === activeSheetIndex)]\"\n @click=\"activeSheetIndex = i\"\n >\n {{ s.name }}\n </button>\n </div>\n <div ref=\"container\" :class=\"cls.e('sheet-stage')\" />\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\n\ndefineOptions({ name: 'FileViewerSheetPreviewer' })\n\nconst props = withDefaults(\n defineProps<{\n file: FileViewerItem\n maxRows?: number\n }>(),\n { maxRows: 50_000 }\n)\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\n\nconst container = useTemplateRef<HTMLDivElement>('container')\nconst loading = ref(true)\nconst truncated = ref(false)\nconst totalRows = ref(0)\nconst renderedRows = ref(0)\n\ninterface SheetData {\n name: string\n headers: string[]\n rows: string[][]\n totalCount: number\n}\n\nconst sheets = shallowRef<SheetData[]>([])\nconst activeSheetIndex = ref(0)\n\nlet controller: AbortController | undefined\nlet instance: { release?: () => void; resize?: () => void } | undefined\nlet resizeObserver: ResizeObserver | undefined\n\nasync function load() {\n controller?.abort()\n controller = new AbortController()\n const signal = controller.signal\n loading.value = true\n sheets.value = []\n activeSheetIndex.value = 0\n\n try {\n const ext = props.file.name.slice(props.file.name.lastIndexOf('.') + 1).toLowerCase()\n const { toArrayBuffer, parseCsv } = await import('../helper')\n const buf = await toArrayBuffer(props.file.src, signal)\n if (signal.aborted) return\n\n if (ext === 'csv') {\n const text = new TextDecoder('utf-8', { fatal: false }).decode(buf)\n const parsed = parseCsv(text).filter((row) => row.some((c) => c.length > 0))\n const headers = parsed[0] ?? []\n const rows = parsed.slice(1)\n sheets.value = [\n {\n name: props.file.name,\n headers: headers.map((h, i) => String(h) || `Column ${i + 1}`),\n rows,\n totalCount: rows.length\n }\n ]\n } else {\n const { readWorkbook } = await import('@cat-kit/excel')\n const workbook = await readWorkbook(new Uint8Array(buf))\n if (signal.aborted) return\n sheets.value = workbook.worksheets.map((ws) => {\n const wsRows = ws.getRows()\n const firstRow = wsRows[0]\n const headers = firstRow ? firstRow.toValues().map((v) => cellToString(v)) : []\n const rows = wsRows.slice(1).map((r) => r.toValues().map(cellToString))\n return {\n name: ws.name,\n headers: headers.map((h, i) => h || `Column ${i + 1}`),\n rows,\n totalCount: rows.length\n }\n })\n }\n\n await renderActive()\n } catch (err) {\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n loading.value = false\n }\n}\n\nfunction cellToString(v: unknown): string {\n if (v === null || v === undefined) return ''\n if (typeof v === 'string') return v\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n if (v instanceof Date) return v.toLocaleString()\n if (typeof v === 'object' && v !== null && 'formula' in v) {\n const f = v as { formula: string; result?: unknown }\n if (f.result !== undefined && f.result !== null) return cellToString(f.result)\n return `=${f.formula}`\n }\n return String(v)\n}\n\nasync function renderActive() {\n const sheet = sheets.value[activeSheetIndex.value]\n if (!sheet || !container.value) return\n\n releaseInstance()\n\n const limit = props.maxRows > 0 ? Math.min(props.maxRows, sheet.rows.length) : sheet.rows.length\n totalRows.value = sheet.totalCount\n renderedRows.value = limit\n truncated.value = props.maxRows > 0 && sheet.rows.length > props.maxRows\n\n const columns = sheet.headers.map((h, i) => ({\n field: 'c' + i,\n title: h,\n width: 'auto' as const\n }))\n\n const records: Record<string, string>[] = []\n for (let i = 0; i < limit; i++) {\n const row = sheet.rows[i]\n if (!row) continue\n const record: Record<string, string> = {}\n for (let j = 0; j < columns.length; j++) {\n record['c' + j] = row[j] ?? ''\n }\n records.push(record)\n }\n\n const VTable = await import('@visactor/vtable')\n if (!container.value) return\n\n instance = new VTable.ListTable(container.value, {\n records,\n columns,\n widthMode: 'standard',\n heightMode: 'standard',\n defaultRowHeight: 32,\n defaultHeaderRowHeight: 36,\n autoFillWidth: true,\n frozenColCount: 0,\n theme: (VTable.themes.ARCO ?? VTable.themes.DEFAULT).extends({\n defaultStyle: {\n borderLineWidth: 1\n }\n })\n })\n}\n\nfunction releaseInstance() {\n if (instance && typeof instance.release === 'function') {\n try {\n instance.release()\n } catch {\n /* noop */\n }\n }\n instance = undefined\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nwatch(activeSheetIndex, () => renderActive())\n\nwatch(container, (el) => {\n resizeObserver?.disconnect()\n if (!el) return\n resizeObserver = new ResizeObserver(() => {\n instance?.resize?.()\n })\n resizeObserver.observe(el)\n})\n\nonBeforeUnmount(() => {\n controller?.abort()\n resizeObserver?.disconnect()\n releaseInstance()\n sheets.value = []\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"sheet-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/sheet-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('sheet')\">\n <div v-if=\"truncated\" :class=\"cls.e('sheet-note')\">\n 仅展示前 {{ renderedRows.toLocaleString() }} 行(共 {{ totalRows.toLocaleString() }} 行)\n </div>\n <div v-if=\"sheets.length > 1\" :class=\"cls.e('sheet-tabs')\">\n <button\n v-for=\"(s, i) in sheets\"\n :key=\"s.name\"\n :class=\"[cls.e('sheet-tab'), bem.is('active', i === activeSheetIndex)]\"\n @click=\"activeSheetIndex = i\"\n >\n {{ s.name }}\n </button>\n </div>\n <div ref=\"container\" :class=\"cls.e('sheet-stage')\" />\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\n\ndefineOptions({ name: 'FileViewerSheetPreviewer' })\n\nconst props = withDefaults(\n defineProps<{\n file: FileViewerItem\n maxRows?: number\n }>(),\n { maxRows: 50_000 }\n)\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\n\nconst container = useTemplateRef<HTMLDivElement>('container')\nconst loading = ref(true)\nconst truncated = ref(false)\nconst totalRows = ref(0)\nconst renderedRows = ref(0)\n\ninterface SheetData {\n name: string\n headers: string[]\n rows: string[][]\n totalCount: number\n}\n\nconst sheets = shallowRef<SheetData[]>([])\nconst activeSheetIndex = ref(0)\n\nlet controller: AbortController | undefined\nlet instance: { release?: () => void; resize?: () => void } | undefined\nlet resizeObserver: ResizeObserver | undefined\n\nasync function load() {\n controller?.abort()\n controller = new AbortController()\n const signal = controller.signal\n loading.value = true\n sheets.value = []\n activeSheetIndex.value = 0\n\n try {\n const ext = props.file.name.slice(props.file.name.lastIndexOf('.') + 1).toLowerCase()\n const { toArrayBuffer, parseCsv } = await import('../helper')\n const buf = await toArrayBuffer(props.file.src, signal)\n if (signal.aborted) return\n\n if (ext === 'csv') {\n const text = new TextDecoder('utf-8', { fatal: false }).decode(buf)\n const parsed = parseCsv(text).filter((row) => row.some((c) => c.length > 0))\n const headers = parsed[0] ?? []\n const rows = parsed.slice(1)\n sheets.value = [\n {\n name: props.file.name,\n headers: headers.map((h, i) => String(h) || `Column ${i + 1}`),\n rows,\n totalCount: rows.length\n }\n ]\n } else {\n const { readWorkbook } = await import('@cat-kit/excel')\n const workbook = await readWorkbook(new Uint8Array(buf))\n if (signal.aborted) return\n sheets.value = workbook.worksheets.map((ws) => {\n const wsRows = ws.getRows()\n const firstRow = wsRows[0]\n const headers = firstRow ? firstRow.toValues().map((v) => cellToString(v)) : []\n const rows = wsRows.slice(1).map((r) => r.toValues().map(cellToString))\n return {\n name: ws.name,\n headers: headers.map((h, i) => h || `Column ${i + 1}`),\n rows,\n totalCount: rows.length\n }\n })\n }\n\n await renderActive()\n } catch (err) {\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n loading.value = false\n }\n}\n\nfunction cellToString(v: unknown): string {\n if (v === null || v === undefined) return ''\n if (typeof v === 'string') return v\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n if (v instanceof Date) return v.toLocaleString()\n if (typeof v === 'object' && v !== null && 'formula' in v) {\n const f = v as { formula: string; result?: unknown }\n if (f.result !== undefined && f.result !== null) return cellToString(f.result)\n return `=${f.formula}`\n }\n return String(v)\n}\n\nasync function renderActive() {\n const sheet = sheets.value[activeSheetIndex.value]\n if (!sheet || !container.value) return\n\n releaseInstance()\n\n const limit = props.maxRows > 0 ? Math.min(props.maxRows, sheet.rows.length) : sheet.rows.length\n totalRows.value = sheet.totalCount\n renderedRows.value = limit\n truncated.value = props.maxRows > 0 && sheet.rows.length > props.maxRows\n\n const columns = sheet.headers.map((h, i) => ({\n field: 'c' + i,\n title: h,\n width: 'auto' as const\n }))\n\n const records: Record<string, string>[] = []\n for (let i = 0; i < limit; i++) {\n const row = sheet.rows[i]\n if (!row) continue\n const record: Record<string, string> = {}\n for (let j = 0; j < columns.length; j++) {\n record['c' + j] = row[j] ?? ''\n }\n records.push(record)\n }\n\n const VTable = await import('@visactor/vtable')\n if (!container.value) return\n\n instance = new VTable.ListTable(container.value, {\n records,\n columns,\n widthMode: 'standard',\n heightMode: 'standard',\n defaultRowHeight: 32,\n defaultHeaderRowHeight: 36,\n autoFillWidth: true,\n frozenColCount: 0,\n theme: (VTable.themes.ARCO ?? VTable.themes.DEFAULT).extends({\n defaultStyle: {\n borderLineWidth: 1\n }\n })\n })\n}\n\nfunction releaseInstance() {\n if (instance && typeof instance.release === 'function') {\n try {\n instance.release()\n } catch {\n /* noop */\n }\n }\n instance = undefined\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nwatch(activeSheetIndex, () => renderActive())\n\nwatch(container, (el) => {\n resizeObserver?.disconnect()\n if (!el) return\n resizeObserver = new ResizeObserver(() => {\n instance?.resize?.()\n })\n resizeObserver.observe(el)\n})\n\nonBeforeUnmount(() => {\n controller?.abort()\n resizeObserver?.disconnect()\n releaseInstance()\n sheets.value = []\n})\n</script>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/text-previewer.vue"],"sourcesContent":["<template>\n <u-scroll tag=\"div\" :class=\"cls.e('text')\">\n <div v-if=\"truncated\" :class=\"cls.e('sheet-note')\">\n ⚠ 文件过大,仅展示前 {{ formatKb(maxBytes) }}\n </div>\n <pre :class=\"cls.e('text-pre')\"><code>{{ text }}</code></pre>\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, shallowRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { UScroll } from '../../scroll'\n\ndefineOptions({ name: 'FileViewerTextPreviewer' })\n\nconst props = withDefaults(\n defineProps<{\n file: FileViewerItem\n /** 单文件最大读取字节数(超出截断),默认 2MB */\n maxBytes?: number\n }>(),\n { maxBytes: 2 * 1024 * 1024 }\n)\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\n\nconst text = shallowRef('')\nconst truncated = ref(false)\nconst loading = ref(true)\n\nlet controller: AbortController | undefined\n\nfunction formatKb(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`\n return `${(bytes / 1024).toFixed(0)} KB`\n}\n\nasync function load() {\n controller?.abort()\n controller = new AbortController()\n loading.value = true\n truncated.value = false\n text.value = ''\n\n try {\n const { toArrayBuffer } = await import('../helper')\n const buf = await toArrayBuffer(props.file.src, controller.signal)\n const max = props.maxBytes\n const view = max > 0 && buf.byteLength > max ? new Uint8Array(buf, 0, max) : new Uint8Array(buf)\n truncated.value = max > 0 && buf.byteLength > max\n text.value = new TextDecoder('utf-8', { fatal: false }).decode(view)\n } catch (err) {\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n controller?.abort()\n text.value = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;EAmBA,MAAM,QAAQ;EASd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAE7B,MAAM,OAAO,WAAW,GAAE;EAC1B,MAAM,YAAY,IAAI,MAAK;EAC3B,MAAM,UAAU,IAAI,KAAI;EAExB,IAAI;EAEJ,SAAS,SAAS,OAAuB;AACvC,OAAI,SAAS,OAAO,KAAM,QAAO,IAAI,QAAQ,OAAO,MAAM,QAAQ,EAAE,CAAC;AACrE,UAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;;EAGtC,eAAe,OAAO;AACpB,eAAY,OAAM;AAClB,gBAAa,IAAI,iBAAgB;AACjC,WAAQ,QAAQ;AAChB,aAAU,QAAQ;AAClB,QAAK,QAAQ;AAEb,OAAI;IACF,MAAM,EAAE,kBAAkB,MAAM,OAAO;IACvC,MAAM,MAAM,MAAM,cAAc,MAAM,KAAK,KAAK,WAAW,OAAM;IACjE,MAAM,MAAM,MAAM;IAClB,MAAM,OAAO,MAAM,KAAK,IAAI,aAAa,MAAM,IAAI,WAAW,KAAK,GAAG,IAAI,GAAG,IAAI,WAAW,IAAG;AAC/F,cAAU,QAAQ,MAAM,KAAK,IAAI,aAAa;AAC9C,SAAK,QAAQ,IAAI,YAAY,SAAS,EAAE,OAAO,OAAO,CAAC,CAAC,OAAO,KAAI;YAC5D,KAAK;AACZ,QAAK,KAA2B,SAAS,aAAc;AACvD,SAAK,SAAS,IAAG;aACT;AACR,YAAQ,QAAQ;;;AAIpB,cACQ,MAAM,YACN,MAAM,EACZ,EAAE,WAAW,MAAK,CACpB;AAEA,wBAAsB;AACpB,eAAY,OAAM;AAClB,QAAK,QAAQ;IACd;;uBAzEC,YAMW,MAAA,YAAA,EAAA;IAND,KAAI;IAAO,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,OAAA,CAAA;;2BAGzB;KAFK,UAAA,SAAA,WAAA,EAAX,mBAEM,OAAA;;MAFiB,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,aAAA,CAAA;QAAgB,kBACrC,gBAAG,SAAS,QAAA,SAAQ,CAAA,EAAA,EAAA,IAAA,mBAAA,QAAA,KAAA;KAElC,mBAA6D,OAAA,EAAvD,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,WAAA,CAAA,EAAA,EAAA,CAAc,mBAAuB,QAAA,MAAA,gBAAd,KAAA,MAAI,EAAA,EAAA,CAAA,EAAA,EAAA;KAClC,QAAA,SAAA,WAAA,EAAX,mBAAwD,OAAA;;MAAnC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,UAAA,CAAA;QAAa,QAAI,EAAA,IAAA,mBAAA,QAAA,KAAA"}
1
+ {"version":3,"file":"text-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/text-previewer.vue"],"sourcesContent":["<template>\n <u-scroll tag=\"div\" :class=\"cls.e('text')\">\n <div v-if=\"truncated\" :class=\"cls.e('sheet-note')\">\n ⚠ 文件过大,仅展示前 {{ formatKb(maxBytes) }}\n </div>\n <pre :class=\"cls.e('text-pre')\"><code>{{ text }}</code></pre>\n <div v-if=\"loading\" :class=\"cls.e('loading')\">加载中…</div>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, ref, shallowRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { UScroll } from '../../scroll'\n\ndefineOptions({ name: 'FileViewerTextPreviewer' })\n\nconst props = withDefaults(\n defineProps<{\n file: FileViewerItem\n /** 单文件最大读取字节数(超出截断),默认 2MB */\n maxBytes?: number\n }>(),\n { maxBytes: 2 * 1024 * 1024 }\n)\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\n\nconst text = shallowRef('')\nconst truncated = ref(false)\nconst loading = ref(true)\n\nlet controller: AbortController | undefined\n\nfunction formatKb(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`\n return `${(bytes / 1024).toFixed(0)} KB`\n}\n\nasync function load() {\n controller?.abort()\n controller = new AbortController()\n loading.value = true\n truncated.value = false\n text.value = ''\n\n try {\n const { toArrayBuffer } = await import('../helper')\n const buf = await toArrayBuffer(props.file.src, controller.signal)\n const max = props.maxBytes\n const view = max > 0 && buf.byteLength > max ? new Uint8Array(buf, 0, max) : new Uint8Array(buf)\n truncated.value = max > 0 && buf.byteLength > max\n text.value = new TextDecoder('utf-8', { fatal: false }).decode(view)\n } catch (err) {\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n controller?.abort()\n text.value = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;EAmBA,MAAM,QAAQ;EASd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAE7B,MAAM,OAAO,WAAW,GAAE;EAC1B,MAAM,YAAY,IAAI,MAAK;EAC3B,MAAM,UAAU,IAAI,KAAI;EAExB,IAAI;EAEJ,SAAS,SAAS,OAAuB;GACvC,IAAI,SAAS,OAAO,MAAM,OAAO,IAAI,QAAQ,OAAO,MAAM,QAAQ,EAAC,CAAE;GACrE,OAAO,IAAI,QAAQ,MAAM,QAAQ,EAAC,CAAE;;EAGtC,eAAe,OAAO;GACpB,YAAY,OAAM;GAClB,aAAa,IAAI,iBAAgB;GACjC,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAClB,KAAK,QAAQ;GAEb,IAAI;IACF,MAAM,EAAE,kBAAkB,MAAM,OAAO;IACvC,MAAM,MAAM,MAAM,cAAc,MAAM,KAAK,KAAK,WAAW,OAAM;IACjE,MAAM,MAAM,MAAM;IAClB,MAAM,OAAO,MAAM,KAAK,IAAI,aAAa,MAAM,IAAI,WAAW,KAAK,GAAG,IAAG,GAAI,IAAI,WAAW,IAAG;IAC/F,UAAU,QAAQ,MAAM,KAAK,IAAI,aAAa;IAC9C,KAAK,QAAQ,IAAI,YAAY,SAAS,EAAE,OAAO,OAAO,CAAA,CAAE,OAAO,KAAI;YAC5D,KAAK;IACZ,IAAK,KAA2B,SAAS,cAAc;IACvD,KAAK,SAAS,IAAG;aACT;IACR,QAAQ,QAAQ;;;EAIpB,YACQ,MAAM,YACN,MAAK,EACX,EAAE,WAAW,MACf,CAAA;EAEA,sBAAsB;GACpB,YAAY,OAAM;GAClB,KAAK,QAAQ;IACd;;uBAzEC,YAMW,MAAA,YAAA,EAAA;IAND,KAAI;IAAO,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,OAAA,CAAA;;2BAGzB;KAFK,UAAA,SAAA,WAAA,EAAX,mBAEM,OAAA;;MAFiB,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,aAAA,CAAA;QAAgB,kBACrC,gBAAG,SAAS,QAAA,SAAQ,CAAA,EAAA,EAAA,IAAA,mBAAA,QAAA,KAAA;KAElC,mBAA6D,OAAA,EAAvD,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,WAAA,CAAA,EAAA,EAAA,CAAc,mBAAuB,QAAA,MAAA,gBAAd,KAAA,MAAI,EAAA,EAAA,CAAA,EAAA,EAAA;KAClC,QAAA,SAAA,WAAA,EAAX,mBAAwD,OAAA;;MAAnC,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,UAAA,CAAA;QAAa,QAAI,EAAA,IAAA,mBAAA,QAAA,KAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"video-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/video-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('video')\">\n <video\n v-if=\"url\"\n ref=\"videoRef\"\n :src=\"url\"\n :type=\"file.mime\"\n controls\n preload=\"metadata\"\n playsinline\n @error=\"onVideoError\"\n />\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { toBlobUrl } from '../helper'\n\ndefineOptions({ name: 'FileViewerVideoPreviewer' })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\n\nconst url = shallowRef<string>('')\nconst videoRef = useTemplateRef<HTMLVideoElement>('videoRef')\n\nlet revoke: (() => void) | undefined\nlet unmounting = false\n\nfunction load() {\n revoke?.()\n const r = toBlobUrl(props.file.src, props.file.mime)\n url.value = r.url\n revoke = r.revoke\n}\n\nfunction onVideoError(e: Event) {\n if (unmounting) return\n const v = e.target as HTMLVideoElement | null\n if (!v?.src) return\n emit('error', e)\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n unmounting = true\n const v = videoRef.value\n if (v) {\n v.pause()\n v.removeAttribute('src')\n try {\n v.load()\n } catch {\n /* noop */\n }\n }\n revoke?.()\n url.value = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;EAwBA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAE7B,MAAM,MAAM,WAAmB,GAAE;EACjC,MAAM,WAAW,eAAiC,WAAU;EAE5D,IAAI;EACJ,IAAI,aAAa;EAEjB,SAAS,OAAO;AACd,aAAS;GACT,MAAM,IAAI,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,KAAI;AACnD,OAAI,QAAQ,EAAE;AACd,YAAS,EAAE;;EAGb,SAAS,aAAa,GAAU;AAC9B,OAAI,WAAY;AAEhB,OAAI,CADM,EAAE,QACJ,IAAK;AACb,QAAK,SAAS,EAAC;;AAGjB,cACQ,MAAM,YACN,MAAM,EACZ,EAAE,WAAW,MAAK,CACpB;AAEA,wBAAsB;AACpB,gBAAa;GACb,MAAM,IAAI,SAAS;AACnB,OAAI,GAAG;AACL,MAAE,OAAM;AACR,MAAE,gBAAgB,MAAK;AACvB,QAAI;AACF,OAAE,MAAK;YACD;;AAIV,aAAS;AACT,OAAI,QAAQ;IACb;;uBArEC,mBAWM,OAAA,EAXA,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,QAAA,CAAA,EAAA,EAAA,CAER,IAAA,SAAA,WAAA,EADR,mBASE,SAAA;;aAPI;IAAJ,KAAI;IACH,KAAK,IAAA;IACL,MAAM,QAAA,KAAK;IACZ,UAAA;IACA,SAAQ;IACR,aAAA;IACC,SAAO"}
1
+ {"version":3,"file":"video-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/video-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('video')\">\n <video\n v-if=\"url\"\n ref=\"videoRef\"\n :src=\"url\"\n :type=\"file.mime\"\n controls\n preload=\"metadata\"\n playsinline\n @error=\"onVideoError\"\n />\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { toBlobUrl } from '../helper'\n\ndefineOptions({ name: 'FileViewerVideoPreviewer' })\n\nconst props = defineProps<{ file: FileViewerItem }>()\n\nconst emit = defineEmits<{ (e: 'error', err: unknown): void }>()\n\nconst cls = bem('file-viewer')\n\nconst url = shallowRef<string>('')\nconst videoRef = useTemplateRef<HTMLVideoElement>('videoRef')\n\nlet revoke: (() => void) | undefined\nlet unmounting = false\n\nfunction load() {\n revoke?.()\n const r = toBlobUrl(props.file.src, props.file.mime)\n url.value = r.url\n revoke = r.revoke\n}\n\nfunction onVideoError(e: Event) {\n if (unmounting) return\n const v = e.target as HTMLVideoElement | null\n if (!v?.src) return\n emit('error', e)\n}\n\nwatch(\n () => props.file,\n () => load(),\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n unmounting = true\n const v = videoRef.value\n if (v) {\n v.pause()\n v.removeAttribute('src')\n try {\n v.load()\n } catch {\n /* noop */\n }\n }\n revoke?.()\n url.value = ''\n})\n</script>\n"],"mappings":";;;;;;;;;;;EAwBA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAa;EAE7B,MAAM,MAAM,WAAmB,GAAE;EACjC,MAAM,WAAW,eAAiC,WAAU;EAE5D,IAAI;EACJ,IAAI,aAAa;EAEjB,SAAS,OAAO;GACd,UAAS;GACT,MAAM,IAAI,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,KAAI;GACnD,IAAI,QAAQ,EAAE;GACd,SAAS,EAAE;;EAGb,SAAS,aAAa,GAAU;GAC9B,IAAI,YAAY;GAEhB,IAAI,CADM,EAAE,QACJ,KAAK;GACb,KAAK,SAAS,EAAC;;EAGjB,YACQ,MAAM,YACN,MAAK,EACX,EAAE,WAAW,MACf,CAAA;EAEA,sBAAsB;GACpB,aAAa;GACb,MAAM,IAAI,SAAS;GACnB,IAAI,GAAG;IACL,EAAE,OAAM;IACR,EAAE,gBAAgB,MAAK;IACvB,IAAI;KACF,EAAE,MAAK;YACD;;GAIV,UAAS;GACT,IAAI,QAAQ;IACb;;uBArEC,mBAWM,OAAA,EAXA,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,QAAA,CAAA,EAAA,EAAA,CAER,IAAA,SAAA,WAAA,EADR,mBASE,SAAA;;aAPI;IAAJ,KAAI;IACH,KAAK,IAAA;IACL,MAAM,QAAA,KAAK;IACZ,UAAA;IACA,SAAQ;IACR,aAAA;IACC,SAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"float-button.js","names":[],"sources":["../../../src/components/float-button/float-button.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\">\n <div\n :class=\"cls.b\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n ref=\"floatButtonRef\"\n >\n <u-button\n v-for=\"(item, index) of items\"\n :key=\"item.key\"\n :class=\"[cls.e('item'), bem.is('first', index === 0)]\"\n circle\n :size=\"size\"\n :type=\"item.type ?? 'primary'\"\n ref=\"itemsRef\"\n :style=\"{ transitionDelay: index * 0.1 + 's' }\"\n @transitionend=\"handleTransitionEnd(index, $event)\"\n @click=\"emit('click', item.key)\"\n :title=\"item.name ?? item.key\"\n >\n <u-icon v-if=\"item.icon\"> <component :is=\"item.icon\" /> </u-icon>\n <span v-else>{{ item.name?.[0] ?? item.key[0] }}</span>\n </u-button>\n </div>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem, removeStyles, setStyles } from '@veltra/utils'\nimport { shallowRef } from 'vue'\n\nimport type { FloatButtonProps, FloatButtonEmits, ButtonExposed } from '../../types'\nimport { UButton } from '../button'\nimport { UIcon } from '../icon'\n\ndefineOptions({\n name: 'FloatButton'\n})\n\ndefineProps<FloatButtonProps>()\n\nconst emit = defineEmits<FloatButtonEmits>()\n\nconst cls = bem('float-button')\nconst hoveredCls = bem.is('hovered')\n\nconst floatButtonRef = shallowRef<HTMLElement>()\nconst itemsRef = shallowRef<ButtonExposed[]>()\n\nconst hovered = shallowRef(false)\n\nconst handleMouseEnter = () => {\n hovered.value = true\n\n itemsRef.value?.forEach((item) => {\n setStyles(item.el!, { display: 'flex' })\n })\n\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n itemsRef.value?.forEach((item) => {\n item.el!.classList.add(hoveredCls)\n })\n })\n })\n}\nconst handleMouseLeave = () => {\n hovered.value = false\n itemsRef.value?.forEach((item) => {\n item.el!.classList.remove(hoveredCls)\n })\n}\n\n/**\n * 动画结束回调\n * @param index 发生的元素索引\n * @param e 事件\n */\nconst handleTransitionEnd = (index: number, e: TransitionEvent) => {\n if (index !== 1 || hovered.value) return\n\n itemsRef.value?.forEach((item) => {\n removeStyles(item.el!, ['display'])\n })\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;EA0CA,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,eAAc;EAC9B,MAAM,aAAa,IAAI,GAAG,UAAS;EAEnC,MAAM,iBAAiB,YAAwB;EAC/C,MAAM,WAAW,YAA4B;EAE7C,MAAM,UAAU,WAAW,MAAK;EAEhC,MAAM,yBAAyB;AAC7B,WAAQ,QAAQ;AAEhB,YAAS,OAAO,SAAS,SAAS;AAChC,cAAU,KAAK,IAAK,EAAE,SAAS,QAAQ,CAAA;KACxC;AAED,+BAA4B;AAC1B,gCAA4B;AAC1B,cAAS,OAAO,SAAS,SAAS;AAChC,WAAK,GAAI,UAAU,IAAI,WAAU;OAClC;MACF;KACF;;EAEH,MAAM,yBAAyB;AAC7B,WAAQ,QAAQ;AAChB,YAAS,OAAO,SAAS,SAAS;AAChC,SAAK,GAAI,UAAU,OAAO,WAAU;KACrC;;;;;;;EAQH,MAAM,uBAAuB,OAAe,MAAuB;AACjE,OAAI,UAAU,KAAK,QAAQ,MAAO;AAElC,YAAS,OAAO,SAAS,SAAS;AAChC,iBAAa,KAAK,IAAK,CAAC,UAAU,CAAA;KACnC;;;uBAnFD,YAwBW,UAAA,EAxBD,IAAG,QAAM,EAAA,CACjB,mBAsBM,OAAA;IArBH,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC;IACZ,cAAY;IACZ,cAAY;aACT;IAAJ,KAAI;yBAEJ,mBAeW,UAAA,MAAA,WAde,QAAA,QAAhB,MAAM,UAAK;wBADrB,YAeW,MAAA,YAAA,EAAA;KAbR,KAAK,KAAK;KACV,OAAK,eAAA,CAAG,MAAA,IAAG,CAAC,EAAC,OAAA,EAAU,MAAA,IAAG,CAAC,GAAE,SAAU,UAAK,EAAA,CAAA,CAAA;KAC7C,QAAA;KACC,MAAM,QAAA;KACN,MAAM,KAAK,QAAI;;cACZ;KAAJ,KAAI;KACH,OAAK,eAAA,EAAA,iBAAqB,QAAK,KAAA,KAAA,CAAA;KAC/B,kBAAa,WAAE,oBAAoB,OAAO,OAAM;KAChD,UAAK,WAAE,KAAI,SAAU,KAAK,IAAG;KAC7B,OAAO,KAAK,QAAQ,KAAK;;4BAEuC,CAAnD,KAAK,QAAA,WAAA,EAAnB,YAAiE,MAAA,YAAA,EAAA,EAAA,KAAA,GAAA,EAAA;6BAAV,EAAA,WAAA,EAA7B,YAA6B,wBAAb,KAAK,KAAI,CAAA,EAAA,CAAA;;+BACnD,mBAAuD,QAAA,YAAA,gBAAvC,KAAK,OAAI,MAAS,KAAK,IAAG,GAAA,EAAA,EAAA,EAAA,CAAA"}
1
+ {"version":3,"file":"float-button.js","names":[],"sources":["../../../src/components/float-button/float-button.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\">\n <div\n :class=\"cls.b\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n ref=\"floatButtonRef\"\n >\n <u-button\n v-for=\"(item, index) of items\"\n :key=\"item.key\"\n :class=\"[cls.e('item'), bem.is('first', index === 0)]\"\n circle\n :size=\"size\"\n :type=\"item.type ?? 'primary'\"\n ref=\"itemsRef\"\n :style=\"{ transitionDelay: index * 0.1 + 's' }\"\n @transitionend=\"handleTransitionEnd(index, $event)\"\n @click=\"emit('click', item.key)\"\n :title=\"item.name ?? item.key\"\n >\n <u-icon v-if=\"item.icon\"> <component :is=\"item.icon\" /> </u-icon>\n <span v-else>{{ item.name?.[0] ?? item.key[0] }}</span>\n </u-button>\n </div>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem, removeStyles, setStyles } from '@veltra/utils'\nimport { shallowRef } from 'vue'\n\nimport type { FloatButtonProps, FloatButtonEmits, ButtonExposed } from '../../types'\nimport { UButton } from '../button'\nimport { UIcon } from '../icon'\n\ndefineOptions({\n name: 'FloatButton'\n})\n\ndefineProps<FloatButtonProps>()\n\nconst emit = defineEmits<FloatButtonEmits>()\n\nconst cls = bem('float-button')\nconst hoveredCls = bem.is('hovered')\n\nconst floatButtonRef = shallowRef<HTMLElement>()\nconst itemsRef = shallowRef<ButtonExposed[]>()\n\nconst hovered = shallowRef(false)\n\nconst handleMouseEnter = () => {\n hovered.value = true\n\n itemsRef.value?.forEach((item) => {\n setStyles(item.el!, { display: 'flex' })\n })\n\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n itemsRef.value?.forEach((item) => {\n item.el!.classList.add(hoveredCls)\n })\n })\n })\n}\nconst handleMouseLeave = () => {\n hovered.value = false\n itemsRef.value?.forEach((item) => {\n item.el!.classList.remove(hoveredCls)\n })\n}\n\n/**\n * 动画结束回调\n * @param index 发生的元素索引\n * @param e 事件\n */\nconst handleTransitionEnd = (index: number, e: TransitionEvent) => {\n if (index !== 1 || hovered.value) return\n\n itemsRef.value?.forEach((item) => {\n removeStyles(item.el!, ['display'])\n })\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;EA0CA,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,eAAc;EAC9B,MAAM,aAAa,IAAI,GAAG,UAAS;EAEnC,MAAM,iBAAiB,YAAwB;EAC/C,MAAM,WAAW,YAA4B;EAE7C,MAAM,UAAU,WAAW,MAAK;EAEhC,MAAM,yBAAyB;GAC7B,QAAQ,QAAQ;GAEhB,SAAS,OAAO,SAAS,SAAS;IAChC,UAAU,KAAK,IAAK,EAAE,SAAS,QAAQ,CAAA;KACxC;GAED,4BAA4B;IAC1B,4BAA4B;KAC1B,SAAS,OAAO,SAAS,SAAS;MAChC,KAAK,GAAI,UAAU,IAAI,WAAU;OAClC;MACF;KACF;;EAEH,MAAM,yBAAyB;GAC7B,QAAQ,QAAQ;GAChB,SAAS,OAAO,SAAS,SAAS;IAChC,KAAK,GAAI,UAAU,OAAO,WAAU;KACrC;;;;;;;EAQH,MAAM,uBAAuB,OAAe,MAAuB;GACjE,IAAI,UAAU,KAAK,QAAQ,OAAO;GAElC,SAAS,OAAO,SAAS,SAAS;IAChC,aAAa,KAAK,IAAK,CAAC,UAAU,CAAA;KACnC;;;uBAnFD,YAwBW,UAAA,EAxBD,IAAG,QAAM,EAAA,CACjB,mBAsBM,OAAA;IArBH,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC;IACZ,cAAY;IACZ,cAAY;aACT;IAAJ,KAAI;yBAEJ,mBAeW,UAAA,MAAA,WAde,QAAA,QAAhB,MAAM,UAAK;wBADrB,YAeW,MAAA,YAAA,EAAA;KAbR,KAAK,KAAK;KACV,OAAK,eAAA,CAAG,MAAA,IAAA,CAAI,EAAC,OAAA,EAAU,MAAA,IAAA,CAAI,GAAE,SAAU,UAAK,EAAA,CAAA,CAAA;KAC7C,QAAA;KACC,MAAM,QAAA;KACN,MAAM,KAAK,QAAI;;cACZ;KAAJ,KAAI;KACH,OAAK,eAAA,EAAA,iBAAqB,QAAK,KAAA,KAAA,CAAA;KAC/B,kBAAa,WAAE,oBAAoB,OAAO,OAAM;KAChD,UAAK,WAAE,KAAI,SAAU,KAAK,IAAG;KAC7B,OAAO,KAAK,QAAQ,KAAK;;4BAEuC,CAAnD,KAAK,QAAA,WAAA,EAAnB,YAAiE,MAAA,YAAA,EAAA,EAAA,KAAA,GAAA,EAAA;6BAAV,EAAA,WAAA,EAA7B,YAA6B,wBAAb,KAAK,KAAI,CAAA,EAAA,CAAA;;+BACnD,mBAAuD,QAAA,YAAA,gBAAvC,KAAK,OAAI,MAAS,KAAK,IAAG,GAAA,EAAA,EAAA,EAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-form-model.js","names":[],"sources":["../../../src/components/form/dynamic-form-model.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { Validator } from '@veltra/utils'\nimport { isReactive, reactive, shallowReactive, watch } from 'vue'\n\nimport type { FormModelItem, DataSettingConfig } from '../../types'\n\n/**\n * 动态表单模型\n */\nexport class DynamicFormModel {\n /** 表单数据 */\n private _data: Record<string, any> = reactive({})\n\n set data(v: Record<string, any>) {\n if (!isReactive(v)) {\n console.error(`data不是一个响应式对象`)\n return\n }\n this._data = v\n this.watchData()\n }\n\n get data() {\n return this._data\n }\n\n /** 表单规则 */\n readonly fields: Record<string, FormModelItem> = shallowReactive(\n {} as Record<string, FormModelItem>\n )\n\n private validator: Validator\n\n /**\n * 不同表单对应的key\n */\n formKeys: Map<number, string[]> = new Map()\n\n readonly errors: Map<string, string[] | undefined> = shallowReactive(new Map())\n\n /**\n * 是否在表单值更新时校验\n */\n private validateOnFieldChange = true\n\n private modelChangeCallback: Set<(fields: string, val: any) => void> = new Set()\n\n private _allKeys: string[] = []\n\n /** 所有的键 */\n get allKeys(): string[] {\n return this._allKeys\n }\n\n private getAllKeys(): void {\n this._allKeys = Object.keys(this.fields)\n }\n\n /** 初始数据 */\n readonly initialData: Record<string, any> = {}\n\n /**\n * 变更前的值\n */\n private readonly oldData: Record<string, any> = {}\n\n constructor(fields?: Record<string, FormModelItem>) {\n this.append(fields ?? {})\n this.validator = new Validator(this.fields)\n this.getAllKeys()\n this.watchData()\n }\n\n private watchData(): void {\n const { oldData } = this\n watch(this.data, (data) => {\n let changedKeys: string[] = []\n\n this.allKeys.forEach((key) => {\n const oldVal = o(oldData).get(key)\n const newVal = o(data).get(key)\n if (oldVal !== newVal) {\n this.modelChangeCallback.forEach((cb) => cb(key, newVal))\n changedKeys.push(key)\n o(oldData).set(key, newVal)\n }\n })\n\n if (this.validateOnFieldChange) {\n this.validate(changedKeys)\n } else {\n this.validateOnFieldChange = true\n }\n })\n }\n\n append(fields: Record<string, FormModelItem>): void {\n for (const key in fields) {\n this.add(key, fields[key] ?? {})\n }\n }\n\n /**\n * 添加字段\n * @param field 字段\n * @param item 字段配置项\n */\n add(field: string, item: FormModelItem): void {\n this.fields[field] = item\n const { value } = item\n const v = typeof value === 'function' ? value() : value\n /** 这是有bug的 */\n if (v !== undefined) {\n this.initialData[field] = JSON.parse(JSON.stringify(v))\n }\n o(this.data).set(field, v)\n o(this.oldData).set(field, v)\n this.getAllKeys()\n }\n\n /**\n * 移除字段\n * @param field 字段\n */\n delete(field: string): void {\n delete this.fields[field]\n this.getAllKeys()\n }\n\n /**\n * 监听值变更\n * @param cb 回调\n */\n onChange(cb: (field: string, val: any) => void): void {\n this.modelChangeCallback.add(cb)\n }\n\n offChange(cb: (field: string, val: any) => void): void {\n this.modelChangeCallback.delete(cb)\n }\n\n async validate(fields?: string | string[]): Promise<boolean> {\n const { errors, validator, data } = this\n\n const results = await validator.validate(data, this.getValidateFields(fields))\n\n // 设置错误提示\n if (!fields) {\n errors.clear()\n for (const field in results) {\n errors.set(field, results[field])\n }\n } else {\n ~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {\n const errs = results[field]\n if (errs?.length) {\n errors.set(field, errs)\n } else {\n errors.delete(field)\n }\n })\n }\n\n return errors.size === 0\n }\n\n private getValidateFields(fields?: string | string[]) {\n if (!fields) {\n if (this.formKeys.size) {\n let _fields: string[] = []\n this.formKeys.forEach((fields) => {\n _fields = _fields.concat(fields)\n })\n return _fields\n } else {\n return this.allKeys\n }\n }\n\n if (!Array.isArray(fields)) {\n return [fields]\n }\n return fields\n }\n\n /**\n * 重置数据\n * @param fields 需要重置的字段\n */\n resetData(keys?: string | string[]): void {\n if (typeof keys === 'string') {\n keys = [keys]\n } else if (Array.isArray(keys)) {\n } else {\n keys = this.allKeys\n }\n\n this.clearValidate()\n\n this.validateOnFieldChange = false\n\n keys.forEach((field) => {\n o(this.data).set(field as string, o(this.initialData).get(field as string))\n })\n }\n\n /**\n * 设置值\n * @param formData 表单值\n * @param options 配置\n */\n setData(formData: Record<string, any>, config?: DataSettingConfig): void {\n const { validate = true } = config || {}\n\n if (!validate) {\n this.validateOnFieldChange = false\n }\n\n this.allKeys.forEach((key) => {\n const value = o(formData).get(key)\n if (value !== undefined) {\n o(this.data).set(key, value)\n }\n })\n }\n\n clearValidate(): void {\n this.errors.clear()\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-form-model.js","names":[],"sources":["../../../src/components/form/dynamic-form-model.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { Validator } from '@veltra/utils'\nimport { isReactive, reactive, shallowReactive, watch } from 'vue'\n\nimport type { FormModelItem, DataSettingConfig } from '../../types'\n\n/**\n * 动态表单模型\n */\nexport class DynamicFormModel {\n /** 表单数据 */\n private _data: Record<string, any> = reactive({})\n\n set data(v: Record<string, any>) {\n if (!isReactive(v)) {\n console.error(`data不是一个响应式对象`)\n return\n }\n this._data = v\n this.watchData()\n }\n\n get data() {\n return this._data\n }\n\n /** 表单规则 */\n readonly fields: Record<string, FormModelItem> = shallowReactive(\n {} as Record<string, FormModelItem>\n )\n\n private validator: Validator\n\n /**\n * 不同表单对应的key\n */\n formKeys: Map<number, string[]> = new Map()\n\n readonly errors: Map<string, string[] | undefined> = shallowReactive(new Map())\n\n /**\n * 是否在表单值更新时校验\n */\n private validateOnFieldChange = true\n\n private modelChangeCallback: Set<(fields: string, val: any) => void> = new Set()\n\n private _allKeys: string[] = []\n\n /** 所有的键 */\n get allKeys(): string[] {\n return this._allKeys\n }\n\n private getAllKeys(): void {\n this._allKeys = Object.keys(this.fields)\n }\n\n /** 初始数据 */\n readonly initialData: Record<string, any> = {}\n\n /**\n * 变更前的值\n */\n private readonly oldData: Record<string, any> = {}\n\n constructor(fields?: Record<string, FormModelItem>) {\n this.append(fields ?? {})\n this.validator = new Validator(this.fields)\n this.getAllKeys()\n this.watchData()\n }\n\n private watchData(): void {\n const { oldData } = this\n watch(this.data, (data) => {\n let changedKeys: string[] = []\n\n this.allKeys.forEach((key) => {\n const oldVal = o(oldData).get(key)\n const newVal = o(data).get(key)\n if (oldVal !== newVal) {\n this.modelChangeCallback.forEach((cb) => cb(key, newVal))\n changedKeys.push(key)\n o(oldData).set(key, newVal)\n }\n })\n\n if (this.validateOnFieldChange) {\n this.validate(changedKeys)\n } else {\n this.validateOnFieldChange = true\n }\n })\n }\n\n append(fields: Record<string, FormModelItem>): void {\n for (const key in fields) {\n this.add(key, fields[key] ?? {})\n }\n }\n\n /**\n * 添加字段\n * @param field 字段\n * @param item 字段配置项\n */\n add(field: string, item: FormModelItem): void {\n this.fields[field] = item\n const { value } = item\n const v = typeof value === 'function' ? value() : value\n /** 这是有bug的 */\n if (v !== undefined) {\n this.initialData[field] = JSON.parse(JSON.stringify(v))\n }\n o(this.data).set(field, v)\n o(this.oldData).set(field, v)\n this.getAllKeys()\n }\n\n /**\n * 移除字段\n * @param field 字段\n */\n delete(field: string): void {\n delete this.fields[field]\n this.getAllKeys()\n }\n\n /**\n * 监听值变更\n * @param cb 回调\n */\n onChange(cb: (field: string, val: any) => void): void {\n this.modelChangeCallback.add(cb)\n }\n\n offChange(cb: (field: string, val: any) => void): void {\n this.modelChangeCallback.delete(cb)\n }\n\n async validate(fields?: string | string[]): Promise<boolean> {\n const { errors, validator, data } = this\n\n const results = await validator.validate(data, this.getValidateFields(fields))\n\n // 设置错误提示\n if (!fields) {\n errors.clear()\n for (const field in results) {\n errors.set(field, results[field])\n }\n } else {\n ~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {\n const errs = results[field]\n if (errs?.length) {\n errors.set(field, errs)\n } else {\n errors.delete(field)\n }\n })\n }\n\n return errors.size === 0\n }\n\n private getValidateFields(fields?: string | string[]) {\n if (!fields) {\n if (this.formKeys.size) {\n let _fields: string[] = []\n this.formKeys.forEach((fields) => {\n _fields = _fields.concat(fields)\n })\n return _fields\n } else {\n return this.allKeys\n }\n }\n\n if (!Array.isArray(fields)) {\n return [fields]\n }\n return fields\n }\n\n /**\n * 重置数据\n * @param fields 需要重置的字段\n */\n resetData(keys?: string | string[]): void {\n if (typeof keys === 'string') {\n keys = [keys]\n } else if (Array.isArray(keys)) {\n } else {\n keys = this.allKeys\n }\n\n this.clearValidate()\n\n this.validateOnFieldChange = false\n\n keys.forEach((field) => {\n o(this.data).set(field as string, o(this.initialData).get(field as string))\n })\n }\n\n /**\n * 设置值\n * @param formData 表单值\n * @param options 配置\n */\n setData(formData: Record<string, any>, config?: DataSettingConfig): void {\n const { validate = true } = config || {}\n\n if (!validate) {\n this.validateOnFieldChange = false\n }\n\n this.allKeys.forEach((key) => {\n const value = o(formData).get(key)\n if (value !== undefined) {\n o(this.data).set(key, value)\n }\n })\n }\n\n clearValidate(): void {\n this.errors.clear()\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"form-model.js","names":[],"sources":["../../../src/components/form/form-model.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { middleProxy, Validator } from '@veltra/utils'\nimport { nextTick, reactive, shallowReactive, watch, type Reactive } from 'vue'\n\nimport type {\n FormModelItem,\n ModelData,\n ModelRules,\n DataSettingConfig,\n IFormModel\n} from '../../types'\n\n/**\n * 表单模型\n */\nexport class FormModel<\n Fields extends Record<string, FormModelItem> = Record<string, FormModelItem>\n> implements IFormModel<Fields> {\n /** 表单数据 */\n data!: ModelData<Fields>\n\n /** 表单规则 */\n readonly fields: Fields\n\n /** 所有的键 */\n readonly allKeys: string[]\n\n /**\n * 不同表单所需要校验的字段\n * @description\n * 这个值会在表单组件渲染时由表单设置,因为只有真正渲染的组件才应该被校验\n */\n formKeys: Map<number, (keyof Fields)[]> = new Map()\n\n /** 初始数据 */\n readonly initialData: ModelData<Fields>\n\n readonly errors: Map<keyof Fields, string[] | undefined> = shallowReactive(new Map())\n\n private validator: Validator<ModelRules<Fields>>\n\n /**\n * 是否在表单值更新时校验\n */\n private validateOnFieldChange = true\n\n private modelChangeCallback: Set<(fields: string, val: any) => void> = new Set()\n\n constructor(fields: Fields) {\n this.fields = fields\n const rawData = {} as ModelData<Fields>\n const allKeys: string[] = []\n\n for (const key in fields) {\n const fieldItem = fields[key]!\n allKeys.push(key)\n const { value } = fieldItem\n let v = value\n\n if (typeof value === 'function') {\n watch(value, (v) => {\n o(this.initialData).set(key, v)\n })\n v = value()\n }\n\n o(rawData).set(key, v)\n }\n\n this.allKeys = allKeys\n this.initialData = JSON.parse(JSON.stringify(rawData))\n\n this.setProxyData(reactive(rawData))\n\n this.validator = new Validator(this.fields)\n\n return shallowReactive(this)\n }\n\n /**\n * 设置响应式值, 替换掉原有的数据\n * @description 设置响应式值时,会自动监听值的变化,并进行校验\n * @param proxyData 响应式的值\n */\n setProxyData(proxyData: ModelData<Fields> | Reactive<ModelData<Fields>>): void {\n const data = middleProxy(proxyData, {\n set: (field, val) => {\n this.modelChangeCallback.forEach((cb) => cb(field, val))\n },\n\n changed: (fields) => {\n if (!this.validateOnFieldChange) {\n this.validateOnFieldChange = true\n return\n }\n this.validate(fields)\n }\n })\n\n this.data = data as ModelData<Fields>\n }\n\n private getValidateFields(fields?: keyof Fields | (keyof Fields)[]) {\n if (!fields) {\n if (this.formKeys.size) {\n let _fields: (keyof Fields)[] = []\n this.formKeys.forEach((fields) => {\n _fields = _fields.concat(fields)\n })\n return _fields\n } else {\n return this.allKeys\n }\n }\n\n if (!Array.isArray(fields)) {\n return [fields]\n }\n return fields\n }\n\n /**\n * 模型校验\n * @param fields 需要校验的字段, 不传则校验所有字段\n * @returns\n */\n async validate(fields?: keyof Fields | (keyof Fields)[]): Promise<boolean> {\n const { errors, validator, data } = this\n\n const results = await validator.validate(data, this.getValidateFields(fields))\n\n // 全量校验\n if (!fields) {\n errors.clear()\n\n for (const field in results) {\n errors.set(field, results[field])\n }\n }\n // 局部校验\n else {\n ~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {\n const errs = results[field]\n if (errs?.length) {\n errors.set(field, errs)\n } else {\n errors.delete(field)\n }\n })\n }\n\n if (errors.size > 0) {\n if (!fields) {\n nextTick(() => {\n document.querySelector('.u-form-item.is-error')?.scrollIntoView({ block: 'nearest' })\n })\n }\n return Promise.reject(false)\n }\n\n return true\n }\n\n /**\n * 重置数据\n * @param fields 需要重置的字段\n */\n resetData(keys?: keyof Fields | (keyof Fields)[]): void {\n if (typeof keys === 'string') {\n keys = [keys]\n } else if (Array.isArray(keys)) {\n } else {\n keys = this.allKeys\n }\n\n this.clearValidate()\n\n this.validateOnFieldChange = false\n\n keys.forEach((field) => {\n o(this.data).set(field as string, o(this.initialData).get(field as string))\n })\n }\n\n /**\n * 设置值\n * @param formData 表单值\n * @param options 配置\n */\n setData(\n formData: Partial<ModelData<Fields> & Record<string, any>>,\n config?: DataSettingConfig\n ): FormModel<Fields> {\n const { validate = true } = config || {}\n\n if (!validate) {\n this.validateOnFieldChange = false\n }\n\n this.allKeys.forEach((key) => {\n const value = o(formData).get(key)\n if (value !== undefined) {\n o(this.data).set(key, value)\n }\n })\n\n return this\n }\n\n /**\n * 设置初始值\n * @description 初始值在重置数据和查看原始数据时会用到\n * @param data 初始值\n */\n setInitialData(data: Partial<ModelData<Fields>>): FormModel<Fields> {\n this.allKeys.forEach((key) => {\n o(this.initialData).set(key, o(data).get(key))\n })\n\n return this\n }\n\n /** 清除校验 */\n clearValidate(): void {\n this.errors.clear()\n }\n\n /**\n * 监听值变更\n * @param cb 回调\n */\n onChange(cb: (field: keyof Fields, val: any) => void): void {\n this.modelChangeCallback.add(cb)\n }\n\n /** 关闭监听值变更 */\n offChange(cb: (field: keyof Fields, val: any) => void): void {\n this.modelChangeCallback.delete(cb)\n }\n}\n"],"mappings":";;;;;;;AAeA,IAAa,YAAb,MAEgC;;CAE9B;;CAGA;;CAGA;;;;;;CAOA,2BAA0C,IAAI,KAAK;;CAGnD;CAEA,SAA2D,gCAAgB,IAAI,KAAK,CAAC;CAErF;;;;CAKA,wBAAgC;CAEhC,sCAAuE,IAAI,KAAK;CAEhF,YAAY,QAAgB;AAC1B,OAAK,SAAS;EACd,MAAM,UAAU,EAAE;EAClB,MAAM,UAAoB,EAAE;AAE5B,OAAK,MAAM,OAAO,QAAQ;GACxB,MAAM,YAAY,OAAO;AACzB,WAAQ,KAAK,IAAI;GACjB,MAAM,EAAE,UAAU;GAClB,IAAI,IAAI;AAER,OAAI,OAAO,UAAU,YAAY;AAC/B,UAAM,QAAQ,MAAM;AAClB,OAAE,KAAK,YAAY,CAAC,IAAI,KAAK,EAAE;MAC/B;AACF,QAAI,OAAO;;AAGb,KAAE,QAAQ,CAAC,IAAI,KAAK,EAAE;;AAGxB,OAAK,UAAU;AACf,OAAK,cAAc,KAAK,MAAM,KAAK,UAAU,QAAQ,CAAC;AAEtD,OAAK,aAAa,SAAS,QAAQ,CAAC;AAEpC,OAAK,YAAY,IAAI,UAAU,KAAK,OAAO;AAE3C,SAAO,gBAAgB,KAAK;;;;;;;CAQ9B,aAAa,WAAkE;EAC7E,MAAM,OAAO,YAAY,WAAW;GAClC,MAAM,OAAO,QAAQ;AACnB,SAAK,oBAAoB,SAAS,OAAO,GAAG,OAAO,IAAI,CAAC;;GAG1D,UAAU,WAAW;AACnB,QAAI,CAAC,KAAK,uBAAuB;AAC/B,UAAK,wBAAwB;AAC7B;;AAEF,SAAK,SAAS,OAAO;;GAExB,CAAC;AAEF,OAAK,OAAO;;CAGd,kBAA0B,QAA0C;AAClE,MAAI,CAAC,OACH,KAAI,KAAK,SAAS,MAAM;GACtB,IAAI,UAA4B,EAAE;AAClC,QAAK,SAAS,SAAS,WAAW;AAChC,cAAU,QAAQ,OAAO,OAAO;KAChC;AACF,UAAO;QAEP,QAAO,KAAK;AAIhB,MAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,QAAO,CAAC,OAAO;AAEjB,SAAO;;;;;;;CAQT,MAAM,SAAS,QAA4D;EACzE,MAAM,EAAE,QAAQ,WAAW,SAAS;EAEpC,MAAM,UAAU,MAAM,UAAU,SAAS,MAAM,KAAK,kBAAkB,OAAO,CAAC;AAG9E,MAAI,CAAC,QAAQ;AACX,UAAO,OAAO;AAEd,QAAK,MAAM,SAAS,QAClB,QAAO,IAAI,OAAO,QAAQ,OAAO;QAKnC,GAAE,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,SAAS,UAAU;GAC9D,MAAM,OAAO,QAAQ;AACrB,OAAI,MAAM,OACR,QAAO,IAAI,OAAO,KAAK;OAEvB,QAAO,OAAO,MAAM;IAEtB;AAGJ,MAAI,OAAO,OAAO,GAAG;AACnB,OAAI,CAAC,OACH,gBAAe;AACb,aAAS,cAAc,wBAAwB,EAAE,eAAe,EAAE,OAAO,WAAW,CAAC;KACrF;AAEJ,UAAO,QAAQ,OAAO,MAAM;;AAG9B,SAAO;;;;;;CAOT,UAAU,MAA8C;AACtD,MAAI,OAAO,SAAS,SAClB,QAAO,CAAC,KAAK;WACJ,MAAM,QAAQ,KAAK,EAAE,OAE9B,QAAO,KAAK;AAGd,OAAK,eAAe;AAEpB,OAAK,wBAAwB;AAE7B,OAAK,SAAS,UAAU;AACtB,KAAE,KAAK,KAAK,CAAC,IAAI,OAAiB,EAAE,KAAK,YAAY,CAAC,IAAI,MAAgB,CAAC;IAC3E;;;;;;;CAQJ,QACE,UACA,QACmB;EACnB,MAAM,EAAE,WAAW,SAAS,UAAU,EAAE;AAExC,MAAI,CAAC,SACH,MAAK,wBAAwB;AAG/B,OAAK,QAAQ,SAAS,QAAQ;GAC5B,MAAM,QAAQ,EAAE,SAAS,CAAC,IAAI,IAAI;AAClC,OAAI,UAAU,KAAA,EACZ,GAAE,KAAK,KAAK,CAAC,IAAI,KAAK,MAAM;IAE9B;AAEF,SAAO;;;;;;;CAQT,eAAe,MAAqD;AAClE,OAAK,QAAQ,SAAS,QAAQ;AAC5B,KAAE,KAAK,YAAY,CAAC,IAAI,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;IAC9C;AAEF,SAAO;;;CAIT,gBAAsB;AACpB,OAAK,OAAO,OAAO;;;;;;CAOrB,SAAS,IAAmD;AAC1D,OAAK,oBAAoB,IAAI,GAAG;;;CAIlC,UAAU,IAAmD;AAC3D,OAAK,oBAAoB,OAAO,GAAG"}
1
+ {"version":3,"file":"form-model.js","names":[],"sources":["../../../src/components/form/form-model.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { middleProxy, Validator } from '@veltra/utils'\nimport { nextTick, reactive, shallowReactive, watch, type Reactive } from 'vue'\n\nimport type {\n FormModelItem,\n ModelData,\n ModelRules,\n DataSettingConfig,\n IFormModel\n} from '../../types'\n\n/**\n * 表单模型\n */\nexport class FormModel<\n Fields extends Record<string, FormModelItem> = Record<string, FormModelItem>\n> implements IFormModel<Fields> {\n /** 表单数据 */\n data!: ModelData<Fields>\n\n /** 表单规则 */\n readonly fields: Fields\n\n /** 所有的键 */\n readonly allKeys: string[]\n\n /**\n * 不同表单所需要校验的字段\n * @description\n * 这个值会在表单组件渲染时由表单设置,因为只有真正渲染的组件才应该被校验\n */\n formKeys: Map<number, (keyof Fields)[]> = new Map()\n\n /** 初始数据 */\n readonly initialData: ModelData<Fields>\n\n readonly errors: Map<keyof Fields, string[] | undefined> = shallowReactive(new Map())\n\n private validator: Validator<ModelRules<Fields>>\n\n /**\n * 是否在表单值更新时校验\n */\n private validateOnFieldChange = true\n\n private modelChangeCallback: Set<(fields: string, val: any) => void> = new Set()\n\n constructor(fields: Fields) {\n this.fields = fields\n const rawData = {} as ModelData<Fields>\n const allKeys: string[] = []\n\n for (const key in fields) {\n const fieldItem = fields[key]!\n allKeys.push(key)\n const { value } = fieldItem\n let v = value\n\n if (typeof value === 'function') {\n watch(value, (v) => {\n o(this.initialData).set(key, v)\n })\n v = value()\n }\n\n o(rawData).set(key, v)\n }\n\n this.allKeys = allKeys\n this.initialData = JSON.parse(JSON.stringify(rawData))\n\n this.setProxyData(reactive(rawData))\n\n this.validator = new Validator(this.fields)\n\n return shallowReactive(this)\n }\n\n /**\n * 设置响应式值, 替换掉原有的数据\n * @description 设置响应式值时,会自动监听值的变化,并进行校验\n * @param proxyData 响应式的值\n */\n setProxyData(proxyData: ModelData<Fields> | Reactive<ModelData<Fields>>): void {\n const data = middleProxy(proxyData, {\n set: (field, val) => {\n this.modelChangeCallback.forEach((cb) => cb(field, val))\n },\n\n changed: (fields) => {\n if (!this.validateOnFieldChange) {\n this.validateOnFieldChange = true\n return\n }\n this.validate(fields)\n }\n })\n\n this.data = data as ModelData<Fields>\n }\n\n private getValidateFields(fields?: keyof Fields | (keyof Fields)[]) {\n if (!fields) {\n if (this.formKeys.size) {\n let _fields: (keyof Fields)[] = []\n this.formKeys.forEach((fields) => {\n _fields = _fields.concat(fields)\n })\n return _fields\n } else {\n return this.allKeys\n }\n }\n\n if (!Array.isArray(fields)) {\n return [fields]\n }\n return fields\n }\n\n /**\n * 模型校验\n * @param fields 需要校验的字段, 不传则校验所有字段\n * @returns\n */\n async validate(fields?: keyof Fields | (keyof Fields)[]): Promise<boolean> {\n const { errors, validator, data } = this\n\n const results = await validator.validate(data, this.getValidateFields(fields))\n\n // 全量校验\n if (!fields) {\n errors.clear()\n\n for (const field in results) {\n errors.set(field, results[field])\n }\n }\n // 局部校验\n else {\n ~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {\n const errs = results[field]\n if (errs?.length) {\n errors.set(field, errs)\n } else {\n errors.delete(field)\n }\n })\n }\n\n if (errors.size > 0) {\n if (!fields) {\n nextTick(() => {\n document.querySelector('.u-form-item.is-error')?.scrollIntoView({ block: 'nearest' })\n })\n }\n return Promise.reject(false)\n }\n\n return true\n }\n\n /**\n * 重置数据\n * @param fields 需要重置的字段\n */\n resetData(keys?: keyof Fields | (keyof Fields)[]): void {\n if (typeof keys === 'string') {\n keys = [keys]\n } else if (Array.isArray(keys)) {\n } else {\n keys = this.allKeys\n }\n\n this.clearValidate()\n\n this.validateOnFieldChange = false\n\n keys.forEach((field) => {\n o(this.data).set(field as string, o(this.initialData).get(field as string))\n })\n }\n\n /**\n * 设置值\n * @param formData 表单值\n * @param options 配置\n */\n setData(\n formData: Partial<ModelData<Fields> & Record<string, any>>,\n config?: DataSettingConfig\n ): FormModel<Fields> {\n const { validate = true } = config || {}\n\n if (!validate) {\n this.validateOnFieldChange = false\n }\n\n this.allKeys.forEach((key) => {\n const value = o(formData).get(key)\n if (value !== undefined) {\n o(this.data).set(key, value)\n }\n })\n\n return this\n }\n\n /**\n * 设置初始值\n * @description 初始值在重置数据和查看原始数据时会用到\n * @param data 初始值\n */\n setInitialData(data: Partial<ModelData<Fields>>): FormModel<Fields> {\n this.allKeys.forEach((key) => {\n o(this.initialData).set(key, o(data).get(key))\n })\n\n return this\n }\n\n /** 清除校验 */\n clearValidate(): void {\n this.errors.clear()\n }\n\n /**\n * 监听值变更\n * @param cb 回调\n */\n onChange(cb: (field: keyof Fields, val: any) => void): void {\n this.modelChangeCallback.add(cb)\n }\n\n /** 关闭监听值变更 */\n offChange(cb: (field: keyof Fields, val: any) => void): void {\n this.modelChangeCallback.delete(cb)\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"form.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/form.vue"],"sourcesContent":["<template>\n <u-grid\n tag=\"form\"\n ref=\"gridRef\"\n @submit.prevent\n :cols=\"cols || breakpointCols\"\n :class=\"[cls.b, bem.is('readonly', readonly)]\"\n >\n <template\n v-for=\"{ node, isFormItem, formItemProps, field, modelValue } of getSlotsNodes()\"\n :key=\"node.key\"\n >\n <component v-if=\"isFormItem || !field\" :is=\"node\" />\n\n <u-form-item v-else v-bind=\"formItemProps\">\n <component\n :is=\"node\"\n :model-value=\"modelValue ?? o(model?.data ?? {}).get(field)\"\n @update:model-value=\"handleUpdateValue(field, $event)\"\n />\n\n <div :class=\"cls.e('data-before')\" v-if=\"showInitialNode(field)\">\n <i :class=\"cls.e('changed-tag')\">变更前:</i>\n\n <component :is=\"node\" readonly :model-value=\"o(model?.initialData ?? {}).get(field)\" />\n </div>\n </u-form-item>\n </template>\n </u-grid>\n</template>\n\n<script lang=\"tsx\" setup generic=\"Model extends FormModel | DynamicFormModel\">\nimport { o } from '@cat-kit/core'\nimport { bem } from '@veltra/utils'\nimport { provideFormContext } from '@veltra/utils'\nimport { shallowRef, toRef } from 'vue'\n\nimport type { BreakCols, GridExposed, FormProps, _FormExposed } from '../../types'\nimport { UFormItem } from '../form-item'\nimport { UGrid } from '../grid'\nimport type { DynamicFormModel } from './dynamic-form-model'\nimport type { FormModel } from './form-model'\nimport { useNodeInterceptor } from './use-node-interceptor'\n\ndefineOptions({\n name: 'Form'\n})\n\nconst props = defineProps<FormProps<Model>>()\n\ndefineSlots<{\n default(props: {\n /** 表单数据 */\n data: Model['data']\n /** 表单模型 */\n model: Model\n }): any\n}>()\n\nconst cls = bem('form')\n\nconst breakpointCols: BreakCols = {\n xs: 1,\n md: 2,\n lg: 3,\n xl: 4,\n default: 4\n}\n\nprovideFormContext(props)\n\nconst { getSlotsNodes } = useNodeInterceptor({ props })\n\nfunction handleUpdateValue(field: string, value: any) {\n const { model } = props\n if (!model) return\n o(model.data).set(field, value)\n}\n\nfunction showInitialNode(field: string) {\n const { data, initialData } = props.model || {}\n\n const currentValue = o(data ?? {}).get(field)\n const initialValue = o(initialData ?? {}).get(field)\n const notEqual = !(initialValue === currentValue || (!initialValue && !currentValue))\n\n return props.showInitialData && notEqual\n}\n\nconst gridRef = shallowRef<GridExposed>()\n\ndefineExpose<_FormExposed>({\n el: toRef(() => gridRef.value?.el)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAgDA,MAAM,QAAQ;EAWd,MAAM,MAAM,IAAI,OAAM;EAEtB,MAAM,iBAA4B;GAChC,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACX;AAEA,qBAAmB,MAAK;EAExB,MAAM,EAAE,kBAAkB,mBAAmB,EAAE,OAAO,CAAA;EAEtD,SAAS,kBAAkB,OAAe,OAAY;GACpD,MAAM,EAAE,UAAU;AAClB,OAAI,CAAC,MAAO;AACZ,KAAE,MAAM,KAAK,CAAC,IAAI,OAAO,MAAK;;EAGhC,SAAS,gBAAgB,OAAe;GACtC,MAAM,EAAE,MAAM,gBAAgB,MAAM,SAAS,EAAC;GAE9C,MAAM,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,MAAK;GAC5C,MAAM,eAAe,EAAE,eAAe,EAAE,CAAC,CAAC,IAAI,MAAK;GACnD,MAAM,WAAW,EAAE,iBAAiB,gBAAiB,CAAC,gBAAgB,CAAC;AAEvE,UAAO,MAAM,mBAAmB;;EAGlC,MAAM,UAAU,YAAwB;AAExC,WAA2B,EACzB,IAAI,YAAY,QAAQ,OAAO,GAAE,EAClC,CAAA;;uBA5FC,YA2BS,MAAA,UAAA,EAAA;IA1BP,KAAI;aACA;IAAJ,KAAI;IACH,UAAM,OAAA,OAAA,OAAA,KAAA,oBAAP,IAAe,CAAA,UAAA,CAAA;IACd,MAAM,QAAA,QAAQ;IACd,OAAK,eAAA,CAAG,MAAA,IAAG,CAAC,GAAG,MAAA,IAAG,CAAC,GAAE,YAAa,QAAA,SAAQ,CAAA,CAAA;;2BAGwC,EAAA,UAAA,KAAA,EADnF,mBAmBW,UAAA,MAAA,WAlBwD,MAAA,cAAa,EAAA,GAAA,EAArE,MAAM,YAAY,eAAe,OAAO,iBAAU;6DACrD,KAAK,KAAA,EAAA,CAEM,cAAU,CAAK,SAAA,WAAA,EAAhC,YAAoD,wBAAR,KAAI,EAAA,EAAA,KAAA,GAAA,CAAA,KAAA,WAAA,EAEhD,YAYc,MAAA,kBAAA,EAZd,WAYc;;;QAZc,cAAa,EAAA;6BAKrC,EAAA,WAAA,EAJF,YAIE,wBAHK,KAAI,EAAA;OACR,eAAa,cAAc,MAAA,EAAC,CAAC,QAAA,OAAO,QAAI,EAAA,CAAA,CAAQ,IAAI,MAAK;OACzD,wBAAkB,WAAE,kBAAkB,OAAO,OAAM;4DAGb,gBAAgB,MAAK,IAAA,WAAA,EAA9D,mBAIM,OAAA;;OAJA,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,cAAA,CAAA;UAChB,mBAAyC,KAAA,EAArC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,cAAA,CAAA,EAAA,EAAiB,QAAI,EAAA,GAAA,WAAA,EAErC,YAAuF,wBAAvE,KAAI,EAAA;OAAE,UAAA;OAAU,eAAa,MAAA,EAAC,CAAC,QAAA,OAAO,eAAW,EAAA,CAAA,CAAQ,IAAI,MAAK"}
1
+ {"version":3,"file":"form.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/form.vue"],"sourcesContent":["<template>\n <u-grid\n tag=\"form\"\n ref=\"gridRef\"\n @submit.prevent\n :cols=\"cols || breakpointCols\"\n :class=\"[cls.b, bem.is('readonly', readonly)]\"\n >\n <template\n v-for=\"{ node, isFormItem, formItemProps, field, modelValue } of getSlotsNodes()\"\n :key=\"node.key\"\n >\n <component v-if=\"isFormItem || !field\" :is=\"node\" />\n\n <u-form-item v-else v-bind=\"formItemProps\">\n <component\n :is=\"node\"\n :model-value=\"modelValue ?? o(model?.data ?? {}).get(field)\"\n @update:model-value=\"handleUpdateValue(field, $event)\"\n />\n\n <div :class=\"cls.e('data-before')\" v-if=\"showInitialNode(field)\">\n <i :class=\"cls.e('changed-tag')\">变更前:</i>\n\n <component :is=\"node\" readonly :model-value=\"o(model?.initialData ?? {}).get(field)\" />\n </div>\n </u-form-item>\n </template>\n </u-grid>\n</template>\n\n<script lang=\"tsx\" setup generic=\"Model extends FormModel | DynamicFormModel\">\nimport { o } from '@cat-kit/core'\nimport { bem } from '@veltra/utils'\nimport { provideFormContext } from '@veltra/utils'\nimport { shallowRef, toRef } from 'vue'\n\nimport type { BreakCols, GridExposed, FormProps, _FormExposed } from '../../types'\nimport { UFormItem } from '../form-item'\nimport { UGrid } from '../grid'\nimport type { DynamicFormModel } from './dynamic-form-model'\nimport type { FormModel } from './form-model'\nimport { useNodeInterceptor } from './use-node-interceptor'\n\ndefineOptions({\n name: 'Form'\n})\n\nconst props = defineProps<FormProps<Model>>()\n\ndefineSlots<{\n default(props: {\n /** 表单数据 */\n data: Model['data']\n /** 表单模型 */\n model: Model\n }): any\n}>()\n\nconst cls = bem('form')\n\nconst breakpointCols: BreakCols = {\n xs: 1,\n md: 2,\n lg: 3,\n xl: 4,\n default: 4\n}\n\nprovideFormContext(props)\n\nconst { getSlotsNodes } = useNodeInterceptor({ props })\n\nfunction handleUpdateValue(field: string, value: any) {\n const { model } = props\n if (!model) return\n o(model.data).set(field, value)\n}\n\nfunction showInitialNode(field: string) {\n const { data, initialData } = props.model || {}\n\n const currentValue = o(data ?? {}).get(field)\n const initialValue = o(initialData ?? {}).get(field)\n const notEqual = !(initialValue === currentValue || (!initialValue && !currentValue))\n\n return props.showInitialData && notEqual\n}\n\nconst gridRef = shallowRef<GridExposed>()\n\ndefineExpose<_FormExposed>({\n el: toRef(() => gridRef.value?.el)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAgDA,MAAM,QAAQ;EAWd,MAAM,MAAM,IAAI,OAAM;EAEtB,MAAM,iBAA4B;GAChC,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,SAAS;GACX;EAEA,mBAAmB,MAAK;EAExB,MAAM,EAAE,kBAAkB,mBAAmB,EAAE,OAAO,CAAA;EAEtD,SAAS,kBAAkB,OAAe,OAAY;GACpD,MAAM,EAAE,UAAU;GAClB,IAAI,CAAC,OAAO;GACZ,EAAE,MAAM,KAAK,CAAC,IAAI,OAAO,MAAK;;EAGhC,SAAS,gBAAgB,OAAe;GACtC,MAAM,EAAE,MAAM,gBAAgB,MAAM,SAAS,EAAC;GAE9C,MAAM,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,MAAK;GAC5C,MAAM,eAAe,EAAE,eAAe,EAAE,CAAC,CAAC,IAAI,MAAK;GACnD,MAAM,WAAW,EAAE,iBAAiB,gBAAiB,CAAC,gBAAgB,CAAC;GAEvE,OAAO,MAAM,mBAAmB;;EAGlC,MAAM,UAAU,YAAwB;EAExC,SAA2B,EACzB,IAAI,YAAY,QAAQ,OAAO,GAAE,EAClC,CAAA;;uBA5FC,YA2BS,MAAA,UAAA,EAAA;IA1BP,KAAI;aACA;IAAJ,KAAI;IACH,UAAM,OAAA,OAAA,OAAA,KAAA,oBAAP,IAAe,CAAA,UAAA,CAAA;IACd,MAAM,QAAA,QAAQ;IACd,OAAK,eAAA,CAAG,MAAA,IAAG,CAAC,GAAG,MAAA,IAAG,CAAC,GAAE,YAAa,QAAA,SAAQ,CAAA,CAAA;;2BAGwC,EAAA,UAAA,KAAA,EADnF,mBAmBW,UAAA,MAAA,WAlBwD,MAAA,cAAa,EAAA,GAAA,EAArE,MAAM,YAAY,eAAe,OAAO,iBAAU;6DACrD,KAAK,KAAA,EAAA,CAEM,cAAU,CAAK,SAAA,WAAA,EAAhC,YAAoD,wBAAR,KAAI,EAAA,EAAA,KAAA,GAAA,CAAA,KAAA,WAAA,EAEhD,YAYc,MAAA,kBAAA,EAZd,WAYc;;;QAZc,cAAa,EAAA;6BAKrC,EAAA,WAAA,EAJF,YAIE,wBAHK,KAAI,EAAA;OACR,eAAa,cAAc,MAAA,EAAC,CAAC,QAAA,OAAO,QAAI,EAAA,CAAA,CAAQ,IAAI,MAAK;OACzD,wBAAkB,WAAE,kBAAkB,OAAO,OAAM;4DAGb,gBAAgB,MAAK,IAAA,WAAA,EAA9D,mBAIM,OAAA;;OAJA,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,cAAA,CAAA;UAChB,mBAAyC,KAAA,EAArC,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,cAAA,CAAA,EAAA,EAAiB,QAAI,EAAA,GAAA,WAAA,EAErC,YAAuF,wBAAvE,KAAI,EAAA;OAAE,UAAA;OAAU,eAAa,MAAA,EAAC,CAAC,QAAA,OAAO,eAAW,EAAA,CAAA,CAAQ,IAAI,MAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","names":[],"sources":["../../../src/components/form/helper.ts"],"sourcesContent":["import type { FormModelItem } from '../../types'\n\n/**\n * 定义表单项\n * @param item 表单项\n * @returns\n */\nexport function formField<Val = unknown>(item?: FormModelItem<Val>): FormModelItem<Val> {\n if (!item) {\n return {}\n }\n\n return item\n}\n"],"mappings":";;;;;;AAOA,SAAgB,UAAyB,MAA+C;AACtF,KAAI,CAAC,KACH,QAAO,EAAE;AAGX,QAAO"}
1
+ {"version":3,"file":"helper.js","names":[],"sources":["../../../src/components/form/helper.ts"],"sourcesContent":["import type { FormModelItem } from '../../types'\n\n/**\n * 定义表单项\n * @param item 表单项\n * @returns\n */\nexport function formField<Val = unknown>(item?: FormModelItem<Val>): FormModelItem<Val> {\n if (!item) {\n return {}\n }\n\n return item\n}\n"],"mappings":";;;;;;AAOA,SAAgB,UAAyB,MAA+C;CACtF,IAAI,CAAC,MACH,OAAO,EAAE;CAGX,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-node-interceptor.js","names":[],"sources":["../../../src/components/form/use-node-interceptor.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { createIncrease, extractNormalVNodes } from '@veltra/utils'\nimport { onBeforeUnmount, useSlots, type VNode } from 'vue'\n\nimport type { FormProps } from '../../types'\n\ninterface Options {\n props: FormProps\n}\n\nexport type SlotRenderItem =\n | {\n isFormItem: true\n formItemProps: Record<string, any>\n node: VNode\n field: undefined\n modelValue?: any\n }\n | {\n isFormItem: false\n formItemProps: Record<string, any>\n node: VNode\n field: string\n modelValue?: any\n }\n\nconst id = createIncrease(1)\n\n/**\n * 虚拟node拦截\n * @param options 选项\n * @returns\n */\nexport function useNodeInterceptor(options: Options): {\n getSlotsNodes: () => SlotRenderItem[] | null\n} {\n const { props } = options\n const slots = useSlots()\n\n const formId = id()\n\n function getSlotsNodes() {\n const { model } = props\n const nodes = slots.default?.({ model, data: model?.data })\n if (!nodes?.length) return null\n\n const flattedNodes = extractNormalVNodes(nodes)\n\n const results: SlotRenderItem[] = []\n\n // 渲染表单后得到的应当校验的字段\n const fields: string[] = []\n\n let i = 0\n while (i < flattedNodes.length) {\n const node = flattedNodes[i]!\n i++\n\n const { props, type } = node\n const item = {\n isFormItem: (type as any)?.name === 'FormItem',\n formItemProps: o((props ?? {}) as Record<string, any>).pick([\n 'label',\n 'field',\n 'span',\n 'tips',\n 'readonly'\n ]),\n node,\n field: props?.field,\n modelValue: props?.['model-value'] ?? props?.modelValue\n }\n\n item.field && fields.push(item.field)\n results.push(item)\n }\n\n model.formKeys.set(formId, fields)\n return results\n }\n\n onBeforeUnmount(() => {\n props.model?.formKeys.delete(formId)\n })\n\n return { getSlotsNodes }\n}\n"],"mappings":";;;;AA0BA,MAAM,KAAK,eAAe,EAAE;;;;;;AAO5B,SAAgB,mBAAmB,SAEjC;CACA,MAAM,EAAE,UAAU;CAClB,MAAM,QAAQ,UAAU;CAExB,MAAM,SAAS,IAAI;CAEnB,SAAS,gBAAgB;EACvB,MAAM,EAAE,UAAU;EAClB,MAAM,QAAQ,MAAM,UAAU;GAAE;GAAO,MAAM,OAAO;GAAM,CAAC;AAC3D,MAAI,CAAC,OAAO,OAAQ,QAAO;EAE3B,MAAM,eAAe,oBAAoB,MAAM;EAE/C,MAAM,UAA4B,EAAE;EAGpC,MAAM,SAAmB,EAAE;EAE3B,IAAI,IAAI;AACR,SAAO,IAAI,aAAa,QAAQ;GAC9B,MAAM,OAAO,aAAa;AAC1B;GAEA,MAAM,EAAE,OAAO,SAAS;GACxB,MAAM,OAAO;IACX,YAAa,MAAc,SAAS;IACpC,eAAe,EAAG,SAAS,EAAE,CAAyB,CAAC,KAAK;KAC1D;KACA;KACA;KACA;KACA;KACD,CAAC;IACF;IACA,OAAO,OAAO;IACd,YAAY,QAAQ,kBAAkB,OAAO;IAC9C;AAED,QAAK,SAAS,OAAO,KAAK,KAAK,MAAM;AACrC,WAAQ,KAAK,KAAK;;AAGpB,QAAM,SAAS,IAAI,QAAQ,OAAO;AAClC,SAAO;;AAGT,uBAAsB;AACpB,QAAM,OAAO,SAAS,OAAO,OAAO;GACpC;AAEF,QAAO,EAAE,eAAe"}
1
+ {"version":3,"file":"use-node-interceptor.js","names":[],"sources":["../../../src/components/form/use-node-interceptor.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { createIncrease, extractNormalVNodes } from '@veltra/utils'\nimport { onBeforeUnmount, useSlots, type VNode } from 'vue'\n\nimport type { FormProps } from '../../types'\n\ninterface Options {\n props: FormProps\n}\n\nexport type SlotRenderItem =\n | {\n isFormItem: true\n formItemProps: Record<string, any>\n node: VNode\n field: undefined\n modelValue?: any\n }\n | {\n isFormItem: false\n formItemProps: Record<string, any>\n node: VNode\n field: string\n modelValue?: any\n }\n\nconst id = createIncrease(1)\n\n/**\n * 虚拟node拦截\n * @param options 选项\n * @returns\n */\nexport function useNodeInterceptor(options: Options): {\n getSlotsNodes: () => SlotRenderItem[] | null\n} {\n const { props } = options\n const slots = useSlots()\n\n const formId = id()\n\n function getSlotsNodes() {\n const { model } = props\n const nodes = slots.default?.({ model, data: model?.data })\n if (!nodes?.length) return null\n\n const flattedNodes = extractNormalVNodes(nodes)\n\n const results: SlotRenderItem[] = []\n\n // 渲染表单后得到的应当校验的字段\n const fields: string[] = []\n\n let i = 0\n while (i < flattedNodes.length) {\n const node = flattedNodes[i]!\n i++\n\n const { props, type } = node\n const item = {\n isFormItem: (type as any)?.name === 'FormItem',\n formItemProps: o((props ?? {}) as Record<string, any>).pick([\n 'label',\n 'field',\n 'span',\n 'tips',\n 'readonly'\n ]),\n node,\n field: props?.field,\n modelValue: props?.['model-value'] ?? props?.modelValue\n }\n\n item.field && fields.push(item.field)\n results.push(item)\n }\n\n model.formKeys.set(formId, fields)\n return results\n }\n\n onBeforeUnmount(() => {\n props.model?.formKeys.delete(formId)\n })\n\n return { getSlotsNodes }\n}\n"],"mappings":";;;;AA0BA,MAAM,KAAK,eAAe,EAAE;;;;;;AAO5B,SAAgB,mBAAmB,SAEjC;CACA,MAAM,EAAE,UAAU;CAClB,MAAM,QAAQ,UAAU;CAExB,MAAM,SAAS,IAAI;CAEnB,SAAS,gBAAgB;EACvB,MAAM,EAAE,UAAU;EAClB,MAAM,QAAQ,MAAM,UAAU;GAAE;GAAO,MAAM,OAAO;GAAM,CAAC;EAC3D,IAAI,CAAC,OAAO,QAAQ,OAAO;EAE3B,MAAM,eAAe,oBAAoB,MAAM;EAE/C,MAAM,UAA4B,EAAE;EAGpC,MAAM,SAAmB,EAAE;EAE3B,IAAI,IAAI;EACR,OAAO,IAAI,aAAa,QAAQ;GAC9B,MAAM,OAAO,aAAa;GAC1B;GAEA,MAAM,EAAE,OAAO,SAAS;GACxB,MAAM,OAAO;IACX,YAAa,MAAc,SAAS;IACpC,eAAe,EAAG,SAAS,EAAE,CAAyB,CAAC,KAAK;KAC1D;KACA;KACA;KACA;KACA;KACD,CAAC;IACF;IACA,OAAO,OAAO;IACd,YAAY,QAAQ,kBAAkB,OAAO;IAC9C;GAED,KAAK,SAAS,OAAO,KAAK,KAAK,MAAM;GACrC,QAAQ,KAAK,KAAK;;EAGpB,MAAM,SAAS,IAAI,QAAQ,OAAO;EAClC,OAAO;;CAGT,sBAAsB;EACpB,MAAM,OAAO,SAAS,OAAO,OAAO;GACpC;CAEF,OAAO,EAAE,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"form-item.js","names":[],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', fieldRequired)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}:</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}:</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <slot></slot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!formProps?.readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTips\">\n {{ errorTips }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { type CSSProperties, computed } from 'vue'\n\nimport type { FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls } from './helper'\n\ndefineOptions({\n name: 'FormItem'\n})\n\nconst props = withDefaults(defineProps<FormItemProps>(), {\n readonly: undefined\n})\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst { formProps } = injectFormContext()\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value), bem.is('error', !!errorTips.value)].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\n/** 错误提示 */\nconst errorTips = computed<string | undefined>(() => {\n if (props.field) {\n return formProps?.model?.errors.get(props.field)?.[0]\n }\n})\n\n/** 字段是否必须 */\nconst fieldRequired = computed<boolean>(() => {\n const { field } = props\n if (!field || readonly.value) return false\n const required = formProps?.model?.fields[field]?.required\n return Boolean(required)\n})\n</script>\n"],"mappings":""}
1
+ {"version":3,"file":"form-item.js","names":[],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', fieldRequired)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}:</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}:</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <slot></slot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!formProps?.readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTips\">\n {{ errorTips }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { type CSSProperties, computed } from 'vue'\n\nimport type { FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls } from './helper'\n\ndefineOptions({ name: 'FormItem' })\n\nconst props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined })\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst { formProps } = injectFormContext()\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value), bem.is('error', !!errorTips.value)].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\n/** 错误提示 */\nconst errorTips = computed<string | undefined>(() => {\n if (!props.field) return undefined\n return formProps?.model?.errors.get(props.field)?.[0]\n})\n\n/** 字段是否必须 */\nconst fieldRequired = computed<boolean>(() => {\n const { field } = props\n if (!field || readonly.value) return false\n const required = formProps?.model?.fields[field]?.required\n return Boolean(required)\n})\n</script>\n"],"mappings":""}
@@ -43,7 +43,8 @@ var form_item_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
43
43
  });
44
44
  /** 错误提示 */
45
45
  const errorTips = computed(() => {
46
- if (props.field) return formProps?.model?.errors.get(props.field)?.[0];
46
+ if (!props.field) return void 0;
47
+ return formProps?.model?.errors.get(props.field)?.[0];
47
48
  });
48
49
  /** 字段是否必须 */
49
50
  const fieldRequired = computed(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"form-item.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', fieldRequired)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}:</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}:</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <slot></slot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!formProps?.readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTips\">\n {{ errorTips }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { type CSSProperties, computed } from 'vue'\n\nimport type { FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls } from './helper'\n\ndefineOptions({\n name: 'FormItem'\n})\n\nconst props = withDefaults(defineProps<FormItemProps>(), {\n readonly: undefined\n})\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst { formProps } = injectFormContext()\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value), bem.is('error', !!errorTips.value)].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\n/** 错误提示 */\nconst errorTips = computed<string | undefined>(() => {\n if (props.field) {\n return formProps?.model?.errors.get(props.field)?.[0]\n }\n})\n\n/** 字段是否必须 */\nconst fieldRequired = computed<boolean>(() => {\n const { field } = props\n if (!field || readonly.value) return false\n const required = formProps?.model?.fields[field]?.required\n return Boolean(required)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;EAiDA,MAAM,QAAQ;;EAWd,MAAM,EAAE,cAAc,mBAAkB;EAExC,MAAM,EAAE,WAAW,WAAU;EAE7B,MAAM,EAAE,MAAM,aAAa,iBAAiB,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE;GACpE,MAAM;GACN,UAAU;GACX,CAAA;EAED,MAAM,YAAY,eAAe;AAC/B,UAAO;IAAC,YAAI;IAAG,YAAI,EAAE,KAAK,MAAM;IAAE,IAAI,GAAG,SAAS,CAAC,CAAC,UAAU,MAAM;IAAC,CAAC,KAAK,IAAG;IAC/E;;EAGD,MAAM,cAAc,eAA8B;AAChD,UAAO,EACL,OAAO,SAAS,MAAM,cAAc,WAAW,cAAc,OAAO,KAAK,YAAY,KAAI,EAC3F;IACD;;EAGD,MAAM,YAAY,eAAmC;AACnD,OAAI,MAAM,MACR,QAAO,WAAW,OAAO,OAAO,IAAI,MAAM,MAAM,GAAG;IAEtD;;EAGD,MAAM,gBAAgB,eAAwB;GAC5C,MAAM,EAAE,UAAU;AAClB,OAAI,CAAC,SAAS,SAAS,MAAO,QAAO;GACrC,MAAM,WAAW,WAAW,OAAO,OAAO,QAAQ;AAClD,UAAO,QAAQ,SAAQ;IACxB;;uBA5FC,YA8Bc,MAAA,YAAA,EAAA;IA9BA,MAAM,QAAA;IAAO,OAAK,eAAE,UAAA,MAAS;;2BAcjC,CAZA,MAAM,SAASA,KAAAA,OAAO,SAAA,WAAA,EAD9B,mBAaQ,SAAA;;KAXL,OAAK,eAAA,CAAG,MAAA,YAAG,CAAC,EAAC,QAAA,EAAW,MAAA,IAAG,CAAC,GAAE,YAAa,cAAA,MAAa,CAAA,CAAA;KACxD,OAAK,eAAE,YAAA,MAAW;QAEN,QAAA,QAAA,WAAA,EAAb,YAIQ,MAAA,UAAA,EAAA;;KAJY,SAAS,QAAA;KAAO,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,OAAA,CAAA;;4BAGvC,CAFP,mBAEO,QAAA,EAFA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,YAAA,CAAA,EAAA,EAAA,CACjB,WAAsC,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBAAhB,QAAA,MAAK,GAAG,KAAC,EAAA,CAAA,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA;;mCAIjC,WAAsC,KAAA,QAAA,SAAA,EAAA,KAAA,GAAA,QAAA,CAAA,gBAAA,gBAAhB,QAAA,MAAK,GAAG,KAAC,EAAA,CAAA,CAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,QAAA,KAAA,EAInC,mBAaU,WAAA,EAbA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,UAAA,CAAA,EAAA,EAAA;KACpB,mBAEM,OAAA,EAFA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,UAAA,CAAA,EAAA,EAAA,CAChB,WAAa,KAAA,QAAA,UAAA,CAAA,EAAA,EAAA;KAGf,mBAAA,2BAA+B;MACS,MAAA,UAAS,EAAE,YAAQ,CAAK,MAAA,UAAS,EAAE,UAAA,WAAA,EAA3E,mBAMU,WAAA;;MANA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,QAAA,CAAA;SACpB,YAIa,YAAA;MAJD,MAAK;MAAiB,MAAK;;6BAG9B,CAAA,CAAA,CAFoC,UAAA,SAAA,WAAA,EAA3C,mBAEO,QAAA;;OAFA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,aAAA,CAAA;yBACd,UAAA,MAAS,EAAA,EAAA,IAAA,mBAAA,QAAA,KAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"form-item.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', fieldRequired)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}:</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}:</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <slot></slot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!formProps?.readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTips\">\n {{ errorTips }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { type CSSProperties, computed } from 'vue'\n\nimport type { FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls } from './helper'\n\ndefineOptions({ name: 'FormItem' })\n\nconst props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined })\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst { formProps } = injectFormContext()\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value), bem.is('error', !!errorTips.value)].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\n/** 错误提示 */\nconst errorTips = computed<string | undefined>(() => {\n if (!props.field) return undefined\n return formProps?.model?.errors.get(props.field)?.[0]\n})\n\n/** 字段是否必须 */\nconst fieldRequired = computed<boolean>(() => {\n const { field } = props\n if (!field || readonly.value) return false\n const required = formProps?.model?.fields[field]?.required\n return Boolean(required)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;EA+CA,MAAM,QAAQ;;EASd,MAAM,EAAE,cAAc,mBAAkB;EAExC,MAAM,EAAE,WAAW,WAAU;EAE7B,MAAM,EAAE,MAAM,aAAa,iBAAiB,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE;GACpE,MAAM;GACN,UAAU;GACX,CAAA;EAED,MAAM,YAAY,eAAe;GAC/B,OAAO;IAAC,YAAI;IAAG,YAAI,EAAE,KAAK,MAAM;IAAE,IAAI,GAAG,SAAS,CAAC,CAAC,UAAU,MAAM;IAAC,CAAC,KAAK,IAAG;IAC/E;;EAGD,MAAM,cAAc,eAA8B;GAChD,OAAO,EACL,OAAO,SAAS,MAAM,cAAc,WAAW,cAAc,OAAO,KAAK,YAAY,KAAI,EAC3F;IACD;;EAGD,MAAM,YAAY,eAAmC;GACnD,IAAI,CAAC,MAAM,OAAO,OAAO,KAAA;GACzB,OAAO,WAAW,OAAO,OAAO,IAAI,MAAM,MAAM,GAAG;IACpD;;EAGD,MAAM,gBAAgB,eAAwB;GAC5C,MAAM,EAAE,UAAU;GAClB,IAAI,CAAC,SAAS,SAAS,OAAO,OAAO;GACrC,MAAM,WAAW,WAAW,OAAO,OAAO,QAAQ;GAClD,OAAO,QAAQ,SAAQ;IACxB;;uBAvFC,YA8Bc,MAAA,YAAA,EAAA;IA9BA,MAAM,QAAA;IAAO,OAAK,eAAE,UAAA,MAAS;;2BAcjC,CAZA,MAAM,SAASA,KAAAA,OAAO,SAAA,WAAA,EAD9B,mBAaQ,SAAA;;KAXL,OAAK,eAAA,CAAG,MAAA,YAAG,CAAC,EAAC,QAAA,EAAW,MAAA,IAAG,CAAC,GAAE,YAAa,cAAA,MAAa,CAAA,CAAA;KACxD,OAAK,eAAE,YAAA,MAAW;QAEN,QAAA,QAAA,WAAA,EAAb,YAIQ,MAAA,UAAA,EAAA;;KAJY,SAAS,QAAA;KAAO,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,OAAA,CAAA;;4BAGvC,CAFP,mBAEO,QAAA,EAFA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,YAAA,CAAA,EAAA,EAAA,CACjB,WAAsC,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBAAhB,QAAA,MAAK,GAAG,KAAC,EAAA,CAAA,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA;;mCAIjC,WAAsC,KAAA,QAAA,SAAA,EAAA,KAAA,GAAA,QAAA,CAAA,gBAAA,gBAAhB,QAAA,MAAK,GAAG,KAAC,EAAA,CAAA,CAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,QAAA,KAAA,EAInC,mBAaU,WAAA,EAbA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,UAAA,CAAA,EAAA,EAAA;KACpB,mBAEM,OAAA,EAFA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,UAAA,CAAA,EAAA,EAAA,CAChB,WAAa,KAAA,QAAA,UAAA,CAAA,EAAA,EAAA;KAGf,mBAAA,2BAA+B;MACS,MAAA,UAAS,EAAE,YAAQ,CAAK,MAAA,UAAS,EAAE,UAAA,WAAA,EAA3E,mBAMU,WAAA;;MANA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,QAAA,CAAA;SACpB,YAIa,YAAA;MAJD,MAAK;MAAiB,MAAK;;6BAG9B,CAAA,CAAA,CAFoC,UAAA,SAAA,WAAA,EAA3C,mBAEO,QAAA;;OAFA,OAAK,eAAE,MAAA,YAAG,CAAC,EAAC,aAAA,CAAA;yBACd,UAAA,MAAS,EAAA,EAAA,IAAA,mBAAA,QAAA,KAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"gantt-chart.js","names":[],"sources":["../../../src/components/gantt-chart/gantt-chart.vue"],"sourcesContent":["<template>\n <div :class=\"cls.b\"></div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\n\nimport type { GanttChartProps } from '../../types'\n\ndefineOptions({\n name: 'GanttChart'\n})\n\ndefineProps<GanttChartProps>()\n\nconst cls = bem('gantt-chart')\n</script>\n"],"mappings":";;;;;;;;EAeA,MAAM,MAAM,IAAI,cAAa;;uBAd3B,mBAA0B,OAAA,EAApB,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC,EAAA,EAAA,MAAA,EAAA"}
1
+ {"version":3,"file":"gantt-chart.js","names":[],"sources":["../../../src/components/gantt-chart/gantt-chart.vue"],"sourcesContent":["<template>\n <div :class=\"cls.b\"></div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\n\nimport type { GanttChartProps } from '../../types'\n\ndefineOptions({\n name: 'GanttChart'\n})\n\ndefineProps<GanttChartProps>()\n\nconst cls = bem('gantt-chart')\n</script>\n"],"mappings":";;;;;;;;EAeA,MAAM,MAAM,IAAI,cAAa;;uBAd3B,mBAA0B,OAAA,EAApB,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC,EAAA,EAAA,MAAA,EAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"breakpoint.js","names":[],"sources":["../../../src/components/grid/breakpoint.ts"],"sourcesContent":["import type { BreakpointName, BreakCols, Breakpoint } from '../../types'\n\nexport const DEFAULT_COLS = 24\n\nconst points: [number, BreakpointName][] = [\n [578, 'xs'],\n [960, 'sm'],\n [1366, 'md'],\n [1920, 'lg'],\n [Infinity, 'xl']\n]\n\n/**\n * 获取容器断点\n * @param width\n */\nexport function getContainerBreakpoint(width: number): Breakpoint {\n for (let i = 0; i < points.length; i++) {\n const point = points[i]!\n if (width < point[0]) {\n return { name: point[1], level: i + 1 }\n }\n }\n return { name: 'xl', level: 5 }\n}\n\n/**\n * 获取断点对应的列数\n * @param colConf 断点配置\n * @param breakpoint 断点\n */\nexport function getBreakpointCols(colConf: BreakCols, breakpoint?: Breakpoint): number {\n if (!breakpoint) return DEFAULT_COLS\n const { name, level } = breakpoint\n const cols = colConf[name]\n if (cols !== undefined) return cols\n\n if (level === 5) return colConf.default ?? DEFAULT_COLS\n\n // 尝试寻找高级断点\n const highLevelPoints = points.slice(level - 1)\n for (let i = 0; i < highLevelPoints.length; i++) {\n const pointName = highLevelPoints[i]![1]\n if (colConf[pointName]) {\n return colConf[pointName]\n }\n }\n return DEFAULT_COLS\n}\n"],"mappings":"AAIA,MAAM,SAAqC;CACzC,CAAC,KAAK,KAAK;CACX,CAAC,KAAK,KAAK;CACX,CAAC,MAAM,KAAK;CACZ,CAAC,MAAM,KAAK;CACZ,CAAC,UAAU,KAAK;CACjB;;;;;AAMD,SAAgB,uBAAuB,OAA2B;AAChE,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,QAAQ,OAAO;AACrB,MAAI,QAAQ,MAAM,GAChB,QAAO;GAAE,MAAM,MAAM;GAAI,OAAO,IAAI;GAAG;;AAG3C,QAAO;EAAE,MAAM;EAAM,OAAO;EAAG;;;;;;;AAQjC,SAAgB,kBAAkB,SAAoB,YAAiC;AACrF,KAAI,CAAC,WAAY,QAAA;CACjB,MAAM,EAAE,MAAM,UAAU;CACxB,MAAM,OAAO,QAAQ;AACrB,KAAI,SAAS,KAAA,EAAW,QAAO;AAE/B,KAAI,UAAU,EAAG,QAAO,QAAQ,WAAA;CAGhC,MAAM,kBAAkB,OAAO,MAAM,QAAQ,EAAE;AAC/C,MAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;EAC/C,MAAM,YAAY,gBAAgB,GAAI;AACtC,MAAI,QAAQ,WACV,QAAO,QAAQ;;AAGnB,QAAA"}
1
+ {"version":3,"file":"breakpoint.js","names":[],"sources":["../../../src/components/grid/breakpoint.ts"],"sourcesContent":["import type { BreakpointName, BreakCols, Breakpoint } from '../../types'\n\nexport const DEFAULT_COLS = 24\n\nconst points: [number, BreakpointName][] = [\n [578, 'xs'],\n [960, 'sm'],\n [1366, 'md'],\n [1920, 'lg'],\n [Infinity, 'xl']\n]\n\n/**\n * 获取容器断点\n * @param width\n */\nexport function getContainerBreakpoint(width: number): Breakpoint {\n for (let i = 0; i < points.length; i++) {\n const point = points[i]!\n if (width < point[0]) {\n return { name: point[1], level: i + 1 }\n }\n }\n return { name: 'xl', level: 5 }\n}\n\n/**\n * 获取断点对应的列数\n * @param colConf 断点配置\n * @param breakpoint 断点\n */\nexport function getBreakpointCols(colConf: BreakCols, breakpoint?: Breakpoint): number {\n if (!breakpoint) return DEFAULT_COLS\n const { name, level } = breakpoint\n const cols = colConf[name]\n if (cols !== undefined) return cols\n\n if (level === 5) return colConf.default ?? DEFAULT_COLS\n\n // 尝试寻找高级断点\n const highLevelPoints = points.slice(level - 1)\n for (let i = 0; i < highLevelPoints.length; i++) {\n const pointName = highLevelPoints[i]![1]\n if (colConf[pointName]) {\n return colConf[pointName]\n }\n }\n return DEFAULT_COLS\n}\n"],"mappings":"AAIA,MAAM,SAAqC;CACzC,CAAC,KAAK,KAAK;CACX,CAAC,KAAK,KAAK;CACX,CAAC,MAAM,KAAK;CACZ,CAAC,MAAM,KAAK;CACZ,CAAC,UAAU,KAAK;CACjB;;;;;AAMD,SAAgB,uBAAuB,OAA2B;CAChE,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,QAAQ,OAAO;EACrB,IAAI,QAAQ,MAAM,IAChB,OAAO;GAAE,MAAM,MAAM;GAAI,OAAO,IAAI;GAAG;;CAG3C,OAAO;EAAE,MAAM;EAAM,OAAO;EAAG;;;;;;;AAQjC,SAAgB,kBAAkB,SAAoB,YAAiC;CACrF,IAAI,CAAC,YAAY,OAAA;CACjB,MAAM,EAAE,MAAM,UAAU;CACxB,MAAM,OAAO,QAAQ;CACrB,IAAI,SAAS,KAAA,GAAW,OAAO;CAE/B,IAAI,UAAU,GAAG,OAAO,QAAQ,WAAA;CAGhC,MAAM,kBAAkB,OAAO,MAAM,QAAQ,EAAE;CAC/C,KAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;EAC/C,MAAM,YAAY,gBAAgB,GAAI;EACtC,IAAI,QAAQ,YACV,OAAO,QAAQ;;CAGnB,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"grid-item.js","names":[],"sources":["../../../src/components/grid/grid-item.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" :style=\"style\" :class=\"cls.b\" v-if=\"span !== 0\">\n <slot />\n </component>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { computed, inject, type CSSProperties, watch } from 'vue'\n\nimport type { GridItemProps } from '../../types'\nimport { GridDIKey } from './di'\n\ndefineOptions({\n name: 'GridItem'\n})\n\nconst props = withDefaults(defineProps<GridItemProps>(), {\n tag: 'div'\n})\n\nconst cls = bem('grid-item')\n\nconst { currentBreakpoint, gridItemsProps } = inject(GridDIKey) ?? {}\n\nif (!currentBreakpoint) {\n console.error('GridItem组件仅能在Grid组件中使用')\n}\n\nconst span = computed<number | 'full'>(() => {\n let { span } = props\n if (span === undefined) return 1\n if (span === 'full') return 'full'\n\n if (typeof span === 'number') return span\n\n const breakpoint = currentBreakpoint?.value\n\n if (!breakpoint) return span.default\n return span[breakpoint.name] ?? span.default\n})\n\nconst style = computed<CSSProperties>(() => {\n return {\n gridColumn: span.value === 'full' ? '1 / -1' : `span ${span.value}`\n }\n})\n\nwatch(\n () => props.span,\n (span) => {\n if (typeof span === 'object') {\n gridItemsProps?.add(props)\n } else {\n gridItemsProps?.delete(props)\n }\n },\n { immediate: true }\n)\n</script>\n"],"mappings":";;;;;;;;;;;;EAiBA,MAAM,QAAQ;EAId,MAAM,MAAM,IAAI,YAAW;EAE3B,MAAM,EAAE,mBAAmB,mBAAmB,OAAO,UAAU,IAAI,EAAC;AAEpE,MAAI,CAAC,kBACH,SAAQ,MAAM,yBAAwB;EAGxC,MAAM,OAAO,eAAgC;GAC3C,IAAI,EAAE,SAAS;AACf,OAAI,SAAS,KAAA,EAAW,QAAO;AAC/B,OAAI,SAAS,OAAQ,QAAO;AAE5B,OAAI,OAAO,SAAS,SAAU,QAAO;GAErC,MAAM,aAAa,mBAAmB;AAEtC,OAAI,CAAC,WAAY,QAAO,KAAK;AAC7B,UAAO,KAAK,WAAW,SAAS,KAAK;IACtC;EAED,MAAM,QAAQ,eAA8B;AAC1C,UAAO,EACL,YAAY,KAAK,UAAU,SAAS,WAAW,QAAQ,KAAK,SAC9D;IACD;AAED,cACQ,MAAM,OACX,SAAS;AACR,OAAI,OAAO,SAAS,SAClB,iBAAgB,IAAI,MAAK;OAEzB,iBAAgB,OAAO,MAAK;KAGhC,EAAE,WAAW,MAAK,CACpB;;UAzD2D,KAAA,UAAI,KAAA,WAAA,EAA7D,YAEY,wBAFI,QAAA,IAAG,EAAA;;IAAG,OAAK,eAAE,MAAA,MAAK;IAAG,OAAK,eAAE,MAAA,IAAG,CAAC,EAAC;;2BACvC,CAAR,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"grid-item.js","names":[],"sources":["../../../src/components/grid/grid-item.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" :style=\"style\" :class=\"cls.b\" v-if=\"span !== 0\">\n <slot />\n </component>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { computed, inject, type CSSProperties, watch } from 'vue'\n\nimport type { GridItemProps } from '../../types'\nimport { GridDIKey } from './di'\n\ndefineOptions({\n name: 'GridItem'\n})\n\nconst props = withDefaults(defineProps<GridItemProps>(), {\n tag: 'div'\n})\n\nconst cls = bem('grid-item')\n\nconst { currentBreakpoint, gridItemsProps } = inject(GridDIKey) ?? {}\n\nif (!currentBreakpoint) {\n console.error('GridItem组件仅能在Grid组件中使用')\n}\n\nconst span = computed<number | 'full'>(() => {\n let { span } = props\n if (span === undefined) return 1\n if (span === 'full') return 'full'\n\n if (typeof span === 'number') return span\n\n const breakpoint = currentBreakpoint?.value\n\n if (!breakpoint) return span.default\n return span[breakpoint.name] ?? span.default\n})\n\nconst style = computed<CSSProperties>(() => {\n return {\n gridColumn: span.value === 'full' ? '1 / -1' : `span ${span.value}`\n }\n})\n\nwatch(\n () => props.span,\n (span) => {\n if (typeof span === 'object') {\n gridItemsProps?.add(props)\n } else {\n gridItemsProps?.delete(props)\n }\n },\n { immediate: true }\n)\n</script>\n"],"mappings":";;;;;;;;;;;;EAiBA,MAAM,QAAQ;EAId,MAAM,MAAM,IAAI,YAAW;EAE3B,MAAM,EAAE,mBAAmB,mBAAmB,OAAO,UAAS,IAAK,EAAC;EAEpE,IAAI,CAAC,mBACH,QAAQ,MAAM,yBAAwB;EAGxC,MAAM,OAAO,eAAgC;GAC3C,IAAI,EAAE,SAAS;GACf,IAAI,SAAS,KAAA,GAAW,OAAO;GAC/B,IAAI,SAAS,QAAQ,OAAO;GAE5B,IAAI,OAAO,SAAS,UAAU,OAAO;GAErC,MAAM,aAAa,mBAAmB;GAEtC,IAAI,CAAC,YAAY,OAAO,KAAK;GAC7B,OAAO,KAAK,WAAW,SAAS,KAAK;IACtC;EAED,MAAM,QAAQ,eAA8B;GAC1C,OAAO,EACL,YAAY,KAAK,UAAU,SAAS,WAAW,QAAQ,KAAK,SAC9D;IACD;EAED,YACQ,MAAM,OACX,SAAS;GACR,IAAI,OAAO,SAAS,UAClB,gBAAgB,IAAI,MAAK;QAEzB,gBAAgB,OAAO,MAAK;KAGhC,EAAE,WAAW,MACf,CAAA;;UAzD2D,KAAA,UAAI,KAAA,WAAA,EAA7D,YAEY,wBAFI,QAAA,IAAG,EAAA;;IAAG,OAAK,eAAE,MAAA,MAAK;IAAG,OAAK,eAAE,MAAA,IAAA,CAAI,EAAC;;2BACvC,CAAR,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA"}