@veltra/desktop 1.7.13 → 1.7.15

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 (84) hide show
  1. package/dist/components/batch-edit/batch-edit-form.js +9 -3
  2. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  3. package/dist/components/batch-edit/batch-edit-list.js +2 -1
  4. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit.js +57 -30
  6. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  7. package/dist/components/batch-edit/style.d.ts +1 -0
  8. package/dist/components/batch-edit/style.js +1 -0
  9. package/dist/components/batch-edit/style2.css +14 -0
  10. package/dist/components/batch-edit/use-dialog-effect.js +20 -0
  11. package/dist/components/batch-edit/use-dialog-effect.js.map +1 -0
  12. package/dist/components/batch-edit/use-edit-state.js +22 -3
  13. package/dist/components/batch-edit/use-edit-state.js.map +1 -1
  14. package/dist/components/batch-edit/use-handlers.js +5 -0
  15. package/dist/components/batch-edit/use-handlers.js.map +1 -1
  16. package/dist/components/batch-edit/use-shortcut-key.js +5 -2
  17. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
  18. package/dist/components/file-viewer/file-viewer.js +21 -14
  19. package/dist/components/file-viewer/file-viewer.js.map +1 -1
  20. package/dist/components/file-viewer/helper.js +2 -0
  21. package/dist/components/file-viewer/helper.js.map +1 -1
  22. package/dist/components/file-viewer/previewers/ofd-previewer.js +196 -0
  23. package/dist/components/file-viewer/previewers/ofd-previewer.js.map +1 -0
  24. package/dist/components/file-viewer/style2.css +27 -0
  25. package/dist/components/table-editor/style2.css +23 -3
  26. package/dist/components/table-editor/table-editor.js +213 -63
  27. package/dist/components/table-editor/table-editor.js.map +1 -1
  28. package/dist/components/tip/style2.css +63 -0
  29. package/dist/components/tip/tip.js +6 -1
  30. package/dist/components/tip/tip.js.map +1 -1
  31. package/dist/index.d.ts +3 -3
  32. package/dist/packages/ofd-core/dist/color.js +69 -0
  33. package/dist/packages/ofd-core/dist/color.js.map +1 -0
  34. package/dist/packages/ofd-core/dist/ctm.js +55 -0
  35. package/dist/packages/ofd-core/dist/ctm.js.map +1 -0
  36. package/dist/packages/ofd-core/dist/error.js +14 -0
  37. package/dist/packages/ofd-core/dist/error.js.map +1 -0
  38. package/dist/packages/ofd-core/dist/font.js +384 -0
  39. package/dist/packages/ofd-core/dist/font.js.map +1 -0
  40. package/dist/packages/ofd-core/dist/index.js +6 -0
  41. package/dist/packages/ofd-core/dist/number.js +13 -0
  42. package/dist/packages/ofd-core/dist/number.js.map +1 -0
  43. package/dist/packages/ofd-core/dist/page-objects.js +181 -0
  44. package/dist/packages/ofd-core/dist/page-objects.js.map +1 -0
  45. package/dist/packages/ofd-core/dist/parse.js +65 -0
  46. package/dist/packages/ofd-core/dist/parse.js.map +1 -0
  47. package/dist/packages/ofd-core/dist/render.js +361 -0
  48. package/dist/packages/ofd-core/dist/render.js.map +1 -0
  49. package/dist/packages/ofd-core/dist/xml.js +199 -0
  50. package/dist/packages/ofd-core/dist/xml.js.map +1 -0
  51. package/dist/packages/ofd-core/dist/zip.js +93 -0
  52. package/dist/packages/ofd-core/dist/zip.js.map +1 -0
  53. package/dist/style.d.ts +1 -1
  54. package/dist/style.js +1 -1
  55. package/dist/types/batch-edit.d.ts +6 -0
  56. package/dist/types/file-viewer.d.ts +1 -1
  57. package/dist/types/index.d.ts +3 -3
  58. package/dist/types/table-editor.d.ts +28 -7
  59. package/dist/types/tip.d.ts +5 -1
  60. package/package.json +8 -7
  61. package/src/components/batch-edit/__test__/batch-edit.test.ts +191 -4
  62. package/src/components/batch-edit/batch-edit-form.vue +17 -3
  63. package/src/components/batch-edit/batch-edit-list.vue +2 -1
  64. package/src/components/batch-edit/batch-edit.vue +22 -4
  65. package/src/components/batch-edit/style.scss +20 -0
  66. package/src/components/batch-edit/style.ts +1 -0
  67. package/src/components/batch-edit/use-dialog-effect.ts +9 -3
  68. package/src/components/batch-edit/use-edit-state.ts +32 -4
  69. package/src/components/batch-edit/use-handlers.ts +10 -0
  70. package/src/components/batch-edit/use-shortcut-key.ts +6 -2
  71. package/src/components/file-viewer/__test__/helper.test.ts +13 -0
  72. package/src/components/file-viewer/file-viewer.vue +23 -16
  73. package/src/components/file-viewer/helper.ts +2 -0
  74. package/src/components/file-viewer/previewers/ofd-previewer.vue +216 -0
  75. package/src/components/file-viewer/style.scss +33 -0
  76. package/src/components/table-editor/__test__/table-editor.test.ts +471 -0
  77. package/src/components/table-editor/style.scss +42 -4
  78. package/src/components/table-editor/table-editor.vue +265 -38
  79. package/src/components/tip/style.scss +15 -0
  80. package/src/components/tip/tip.vue +1 -1
  81. package/src/types/batch-edit.ts +6 -0
  82. package/src/types/file-viewer.ts +1 -1
  83. package/src/types/table-editor.ts +29 -7
  84. package/src/types/tip.ts +6 -1
@@ -73,13 +73,19 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
73
73
  const cls = bem("file-viewer");
74
74
  const TRANSFORMABLE_KINDS = /* @__PURE__ */ new Set(["image"]);
75
75
  /** 工具栏显示缩放控件的类型 */
76
- const ZOOMABLE_KINDS = /* @__PURE__ */ new Set(["image", "pdf"]);
76
+ const ZOOMABLE_KINDS = /* @__PURE__ */ new Set([
77
+ "image",
78
+ "pdf",
79
+ "ofd"
80
+ ]);
81
+ /** 缩放由 previewer 内部实现、组件只透传命令并回显级别的类型 */
82
+ const DELEGATED_ZOOM_KINDS = /* @__PURE__ */ new Set(["pdf", "ofd"]);
77
83
  const activeId = useModel(__props, "modelValue");
78
84
  const openModel = useModel(__props, "open");
79
85
  const rootRef = shallowRef();
80
86
  const previewerRef = shallowRef();
81
87
  const scale = ref(1);
82
- const pdfZoomLevel = ref(1);
88
+ const previewerZoomLevel = ref(1);
83
89
  const offsetX = ref(0);
84
90
  const offsetY = ref(0);
85
91
  const isDragging = ref(false);
@@ -90,6 +96,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
90
96
  pdf: defineAsyncComponent(() => import("./previewers/pdf-previewer.js")),
91
97
  sheet: defineAsyncComponent(() => import("./previewers/sheet-previewer.js")),
92
98
  docx: defineAsyncComponent(() => import("./previewers/docx-previewer.js")),
99
+ ofd: defineAsyncComponent(() => import("./previewers/ofd-previewer.js")),
93
100
  text: defineAsyncComponent(() => import("./previewers/text-previewer.js"))
94
101
  };
95
102
  const normalizedFiles = computed(() => props.files.map((f, i) => ({
@@ -112,14 +119,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
112
119
  const activeIndexLabel = computed(() => activeIndex.value >= 0 ? `${activeIndex.value + 1} / ${normalizedFiles.value.length}` : "");
113
120
  const isTransformable = computed(() => !!activeFile.value && TRANSFORMABLE_KINDS.has(activeFile.value.kind));
114
121
  const isZoomable = computed(() => !!activeFile.value && ZOOMABLE_KINDS.has(activeFile.value.kind));
115
- const isPdfActive = computed(() => activeFile.value?.kind === "pdf");
122
+ const isDelegatedZoom = computed(() => !!activeFile.value && DELEGATED_ZOOM_KINDS.has(activeFile.value.kind));
116
123
  const canPan = computed(() => isTransformable.value && scale.value > 1);
117
- const displayZoomLevel = computed(() => isPdfActive.value ? pdfZoomLevel.value : scale.value);
124
+ const displayZoomLevel = computed(() => isDelegatedZoom.value ? previewerZoomLevel.value : scale.value);
118
125
  const zoomPercent = computed(() => `${Math.round(displayZoomLevel.value * 100)}%`);
119
126
  const zoomInDisabled = computed(() => !isZoomable.value || displayZoomLevel.value >= MAX_SCALE);
120
127
  const zoomOutDisabled = computed(() => !isZoomable.value || displayZoomLevel.value <= MIN_SCALE);
121
128
  const isTransformReset = computed(() => {
122
- if (isPdfActive.value) return Math.abs(pdfZoomLevel.value - 1) < .02;
129
+ if (isDelegatedZoom.value) return Math.abs(previewerZoomLevel.value - 1) < .02;
123
130
  return scale.value === 1 && offsetX.value === 0 && offsetY.value === 0;
124
131
  });
125
132
  const previewerStyle = computed(() => {
@@ -159,7 +166,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
159
166
  }
160
167
  function zoomIn() {
161
168
  if (!isZoomable.value) return;
162
- if (isPdfActive.value) {
169
+ if (isDelegatedZoom.value) {
163
170
  previewerRef.value?.zoomIn?.();
164
171
  return;
165
172
  }
@@ -167,16 +174,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
167
174
  }
168
175
  function zoomOut() {
169
176
  if (!isZoomable.value) return;
170
- if (isPdfActive.value) {
177
+ if (isDelegatedZoom.value) {
171
178
  previewerRef.value?.zoomOut?.();
172
179
  return;
173
180
  }
174
181
  setScale(scale.value - SCALE_STEP);
175
182
  }
176
183
  function resetTransform() {
177
- if (isPdfActive.value) {
184
+ if (isDelegatedZoom.value) {
178
185
  previewerRef.value?.resetZoom?.();
179
- pdfZoomLevel.value = 1;
186
+ previewerZoomLevel.value = 1;
180
187
  return;
181
188
  }
182
189
  scale.value = 1;
@@ -185,8 +192,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
185
192
  dragState = void 0;
186
193
  isDragging.value = false;
187
194
  }
188
- function handlePdfZoomChange(level) {
189
- pdfZoomLevel.value = level;
195
+ function handlePreviewerZoomChange(level) {
196
+ previewerZoomLevel.value = level;
190
197
  }
191
198
  function isZoomWheel(e) {
192
199
  return e.ctrlKey || e.metaKey;
@@ -229,7 +236,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
229
236
  isDragging.value = false;
230
237
  }
231
238
  function handleViewportDblclick() {
232
- if (isPdfActive.value) return;
239
+ if (isDelegatedZoom.value) return;
233
240
  if (!isTransformable.value) return;
234
241
  if (scale.value === 1) setScale(2);
235
242
  else resetTransform();
@@ -276,7 +283,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
276
283
  if (!activeId.value || !files.some((f) => f.id === activeId.value)) activeId.value = files[0].id;
277
284
  }, { immediate: true });
278
285
  watch(() => activeFile.value?.id, async () => {
279
- pdfZoomLevel.value = 1;
286
+ previewerZoomLevel.value = 1;
280
287
  await nextTick();
281
288
  resetTransform();
282
289
  });
@@ -479,7 +486,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
479
486
  class: normalizeClass(isTransformable.value ? unref(cls).e("previewer") : void 0),
480
487
  style: normalizeStyle(previewerStyle.value),
481
488
  onError: handleChildError,
482
- onZoomChange: handlePdfZoomChange
489
+ onZoomChange: handlePreviewerZoomChange
483
490
  }, null, 40, [
484
491
  "file",
485
492
  "max-rows",
@@ -1 +1 @@
1
- {"version":3,"file":"file-viewer.js","names":[],"sources":["../../../src/components/file-viewer/file-viewer.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\" :disabled=\"!isModal\">\n <transition :name=\"isModal ? 'u-file-viewer-modal' : ''\">\n <div\n v-if=\"!isModal || openModel\"\n :class=\"[cls.b, bem.is('modal', isModal)]\"\n :tabindex=\"isModal ? -1 : undefined\"\n ref=\"rootRef\"\n @mousedown.self=\"handleBackdropMousedown\"\n >\n <div v-if=\"isModal\" :class=\"cls.e('backdrop')\" @mousedown.self=\"handleBackdropMousedown\" />\n <div :class=\"cls.e('inner')\">\n <aside v-if=\"showSidebar\" :class=\"cls.e('sidebar')\" :style=\"{ width: sidebarWidthCss }\">\n <header :class=\"cls.e('sidebar-head')\">\n <span :class=\"cls.e('sidebar-title')\">文件</span>\n <span :class=\"cls.e('sidebar-count')\">{{ normalizedFiles.length }}</span>\n </header>\n <u-scroll tag=\"ul\" :class=\"cls.e('list')\">\n <li\n v-for=\"f in normalizedFiles\"\n :key=\"f.id\"\n :class=\"[cls.e('item'), bem.is('active', f.id === activeId)]\"\n :title=\"f.name\"\n @click=\"activate(f.id)\"\n >\n <span :class=\"cls.e('item-meta')\">\n <span :class=\"cls.e('item-name')\" :title=\"f.name\">{{ f.name }}</span>\n <span :class=\"cls.e('item-size')\">{{ formatBytes(f.size) }}</span>\n </span>\n <span :class=\"cls.e('item-kind')\">{{ label(f.kind) }}</span>\n </li>\n <li v-if=\"!normalizedFiles.length\" :class=\"cls.e('list-empty')\">\n <u-empty text=\"暂无文件\" :size=\"32\" />\n </li>\n </u-scroll>\n </aside>\n\n <section :class=\"cls.e('stage')\">\n <header :class=\"cls.e('stage-head')\">\n <div :class=\"cls.e('stage-title')\">\n <span v-if=\"activeFile\" :class=\"[cls.e('badge'), cls.em('badge', activeFile.kind)]\">\n {{ label(activeFile.kind) }}\n </span>\n <span :class=\"cls.e('stage-copy')\">\n <span :class=\"cls.e('stage-name')\" :title=\"activeFile?.name\">\n {{ activeFile?.name ?? '—' }}\n </span>\n <span v-if=\"activeFile\" :class=\"cls.e('stage-sub')\">\n <span>{{ activeIndexLabel }}</span>\n <span v-if=\"activeFile.size\">{{ formatBytes(activeFile.size) }}</span>\n </span>\n </span>\n </div>\n <div :class=\"cls.e('stage-actions')\">\n <span :class=\"cls.e('action-group')\">\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"!hasPrev\"\n type=\"button\"\n aria-label=\"上一个\"\n title=\"上一个\"\n @click=\"prev\"\n >\n <u-icon :size=\"15\">\n <ArrowLeft />\n </u-icon>\n </button>\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"!hasNext\"\n type=\"button\"\n aria-label=\"下一个\"\n title=\"下一个\"\n @click=\"next\"\n >\n <u-icon :size=\"15\">\n <ArrowRight />\n </u-icon>\n </button>\n </span>\n <span v-if=\"isZoomable\" :class=\"cls.e('action-group')\">\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"zoomOutDisabled\"\n type=\"button\"\n aria-label=\"缩小\"\n title=\"缩小\"\n @click=\"zoomOut\"\n >\n <u-icon :size=\"15\">\n <ZoomOut />\n </u-icon>\n </button>\n <span :class=\"cls.e('zoom-value')\">{{ zoomPercent }}</span>\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"zoomInDisabled\"\n type=\"button\"\n aria-label=\"放大\"\n title=\"放大\"\n @click=\"zoomIn\"\n >\n <u-icon :size=\"15\">\n <ZoomIn />\n </u-icon>\n </button>\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"isTransformReset\"\n type=\"button\"\n aria-label=\"重置视图\"\n title=\"重置视图\"\n @click=\"resetTransform\"\n >\n <u-icon :size=\"15\">\n <Refresh />\n </u-icon>\n </button>\n </span>\n <button\n v-if=\"downloadable && activeFile\"\n :class=\"[cls.e('action'), cls.em('action', 'primary'), cls.em('action', 'icon')]\"\n type=\"button\"\n aria-label=\"下载\"\n title=\"下载\"\n @click=\"download\"\n >\n <u-icon :size=\"15\">\n <Download />\n </u-icon>\n </button>\n <button\n v-if=\"isModal\"\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n type=\"button\"\n aria-label=\"关闭预览\"\n title=\"关闭\"\n @click=\"handleClose\"\n >\n <u-icon :size=\"16\">\n <Close />\n </u-icon>\n </button>\n </div>\n </header>\n <div :class=\"cls.e('body')\">\n <transition name=\"u-file-viewer-fade\" mode=\"out-in\">\n <div\n v-if=\"activeFile\"\n :key=\"activeFile.id\"\n :class=\"[\n cls.e('viewport'),\n bem.is('transformable', isTransformable),\n bem.is('pannable', canPan),\n bem.is('dragging', isDragging)\n ]\"\n @pointerdown.capture=\"handleViewportPointerDown\"\n @pointermove.capture=\"handleViewportPointerMove\"\n @pointerup.capture=\"handleViewportPointerEnd\"\n @pointercancel.capture=\"handleViewportPointerEnd\"\n @wheel=\"handleViewportWheel\"\n @dblclick=\"handleViewportDblclick\"\n >\n <component\n :is=\"PreviewerMap[activeFile.kind]\"\n ref=\"previewerRef\"\n :file=\"activeFile\"\n :max-rows=\"sheetMaxRows\"\n :class=\"isTransformable ? cls.e('previewer') : undefined\"\n :style=\"previewerStyle\"\n @error=\"handleChildError\"\n @zoom-change=\"handlePdfZoomChange\"\n />\n </div>\n <div v-else :class=\"cls.e('empty')\">\n <u-empty text=\"暂无可预览文件\" />\n </div>\n </transition>\n </div>\n </section>\n </div>\n </div>\n </transition>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport {\n ArrowLeft,\n ArrowRight,\n Close,\n Download,\n Refresh,\n ZoomIn,\n ZoomOut\n} from '@veltra/icons/normal'\nimport { bem, withUnit } from '@veltra/utils'\nimport {\n computed,\n defineAsyncComponent,\n nextTick,\n onBeforeUnmount,\n ref,\n shallowRef,\n watch\n} from 'vue'\nimport type { CSSProperties } from 'vue'\n\nimport type {\n _FileViewerExposed,\n FileViewerEmits,\n FileViewerItem,\n FileViewerKind,\n FileViewerProps\n} from '../../types/file-viewer'\nimport { UEmpty } from '../empty'\nimport { UIcon } from '../icon'\nimport { UScroll } from '../scroll'\nimport { FILE_VIEWER_KIND_LABEL, downloadFile, formatBytes, inferKind } from './helper'\n\ndefineOptions({ name: 'UFileViewer', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<FileViewerProps>(), {\n modelValue: undefined,\n sidebarWidth: '280px',\n sheetMaxRows: 50_000,\n downloadable: true,\n open: undefined,\n closeOnClickBackdrop: true,\n closeOnEsc: true\n})\n\nconst emit = defineEmits<FileViewerEmits>()\n\nconst cls = bem('file-viewer')\n\nconst MIN_SCALE = 0.5\nconst MAX_SCALE = 3\n/** 每次缩放固定增减 10% */\nconst SCALE_STEP = 0.1\n/** 使用 CSS transform 缩放的类型(PDF 由 EmbedPDF zoom 插件处理) */\nconst TRANSFORMABLE_KINDS = new Set<FileViewerKind>(['image'])\n/** 工具栏显示缩放控件的类型 */\nconst ZOOMABLE_KINDS = new Set<FileViewerKind>(['image', 'pdf'])\n\nconst activeId = defineModel<string | undefined>('modelValue', { default: undefined })\nconst openModel = defineModel<boolean | undefined>('open', { default: undefined })\n\nconst rootRef = shallowRef<HTMLDivElement>()\nconst previewerRef = shallowRef<{\n zoomIn?: () => void\n zoomOut?: () => void\n resetZoom?: () => void\n}>()\nconst scale = ref(1)\nconst pdfZoomLevel = ref(1)\nconst offsetX = ref(0)\nconst offsetY = ref(0)\nconst isDragging = ref(false)\n\nlet dragState:\n | { pointerId: number; startX: number; startY: number; originX: number; originY: number }\n | undefined\n\nconst PreviewerMap: Record<FileViewerKind, ReturnType<typeof defineAsyncComponent>> = {\n image: defineAsyncComponent(() => import('./previewers/image-previewer.vue')),\n video: defineAsyncComponent(() => import('./previewers/video-previewer.vue')),\n pdf: defineAsyncComponent(() => import('./previewers/pdf-previewer.vue')),\n sheet: defineAsyncComponent(() => import('./previewers/sheet-previewer.vue')),\n docx: defineAsyncComponent(() => import('./previewers/docx-previewer.vue')),\n text: defineAsyncComponent(() => import('./previewers/text-previewer.vue'))\n}\n\ninterface NormalizedFile extends FileViewerItem {\n id: string\n kind: FileViewerKind\n}\n\nconst normalizedFiles = computed<NormalizedFile[]>(() =>\n props.files.map((f, i) => ({ ...f, id: f.id ?? `file-${i}`, kind: inferKind(f.name, f.kind) }))\n)\n\nconst activeFile = computed<NormalizedFile | undefined>(() =>\n normalizedFiles.value.find((f) => f.id === activeId.value)\n)\n\nconst activeIndex = computed(() => normalizedFiles.value.findIndex((f) => f.id === activeId.value))\n\nconst hasPrev = computed(() => activeIndex.value > 0)\nconst hasNext = computed(\n () => activeIndex.value >= 0 && activeIndex.value < normalizedFiles.value.length - 1\n)\n\nconst showSidebar = computed(() => props.sidebarWidth !== false && props.sidebarWidth !== 0)\n\nconst sidebarWidthCss = computed(() => {\n const w = props.sidebarWidth\n if (w === false || w === 0) return undefined\n return withUnit(w ?? '280px', 'px')\n})\n\n/** 是否启用模态模式:只要父组件显式传入 open(含 v-model:open),即进入模态 */\nconst isModal = computed(() => openModel.value !== undefined)\n\nconst activeIndexLabel = computed(() =>\n activeIndex.value >= 0 ? `${activeIndex.value + 1} / ${normalizedFiles.value.length}` : ''\n)\n\nconst isTransformable = computed(\n () => !!activeFile.value && TRANSFORMABLE_KINDS.has(activeFile.value.kind)\n)\n\nconst isZoomable = computed(() => !!activeFile.value && ZOOMABLE_KINDS.has(activeFile.value.kind))\n\nconst isPdfActive = computed(() => activeFile.value?.kind === 'pdf')\n\nconst canPan = computed(() => isTransformable.value && scale.value > 1)\n\nconst displayZoomLevel = computed(() => (isPdfActive.value ? pdfZoomLevel.value : scale.value))\n\nconst zoomPercent = computed(() => `${Math.round(displayZoomLevel.value * 100)}%`)\n\nconst zoomInDisabled = computed(() => !isZoomable.value || displayZoomLevel.value >= MAX_SCALE)\n\nconst zoomOutDisabled = computed(() => !isZoomable.value || displayZoomLevel.value <= MIN_SCALE)\n\nconst isTransformReset = computed(() => {\n if (isPdfActive.value) {\n return Math.abs(pdfZoomLevel.value - 1) < 0.02\n }\n return scale.value === 1 && offsetX.value === 0 && offsetY.value === 0\n})\n\nconst previewerStyle = computed<CSSProperties | undefined>(() => {\n if (!isTransformable.value) return undefined\n return {\n transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value})`\n }\n})\n\nfunction label(kind: FileViewerKind): string {\n return FILE_VIEWER_KIND_LABEL[kind]\n}\n\nfunction activate(id: string) {\n if (activeId.value === id) return\n activeId.value = id\n const f = normalizedFiles.value.find((x) => x.id === id)\n if (f) emit('change', f)\n}\n\nfunction prev() {\n const i = activeIndex.value\n if (i <= 0) return\n const target = normalizedFiles.value[i - 1]\n if (target) activate(target.id)\n}\n\nfunction next() {\n const i = activeIndex.value\n if (i < 0 || i >= normalizedFiles.value.length - 1) return\n const target = normalizedFiles.value[i + 1]\n if (target) activate(target.id)\n}\n\nfunction normalizeScale(value: number): number {\n const clamped = Math.min(MAX_SCALE, Math.max(MIN_SCALE, value))\n return Math.round(clamped * 100) / 100\n}\n\nfunction setScale(value: number) {\n scale.value = normalizeScale(value)\n if (scale.value <= 1) {\n offsetX.value = 0\n offsetY.value = 0\n }\n}\n\nfunction zoomIn() {\n if (!isZoomable.value) return\n if (isPdfActive.value) {\n previewerRef.value?.zoomIn?.()\n return\n }\n setScale(scale.value + SCALE_STEP)\n}\n\nfunction zoomOut() {\n if (!isZoomable.value) return\n if (isPdfActive.value) {\n previewerRef.value?.zoomOut?.()\n return\n }\n setScale(scale.value - SCALE_STEP)\n}\n\nfunction resetTransform() {\n if (isPdfActive.value) {\n previewerRef.value?.resetZoom?.()\n pdfZoomLevel.value = 1\n return\n }\n scale.value = 1\n offsetX.value = 0\n offsetY.value = 0\n dragState = undefined\n isDragging.value = false\n}\n\nfunction handlePdfZoomChange(level: number) {\n pdfZoomLevel.value = level\n}\n\nfunction isZoomWheel(e: WheelEvent): boolean {\n return e.ctrlKey || e.metaKey\n}\n\nfunction handleViewportWheel(e: WheelEvent) {\n if (!activeFile.value || activeFile.value.kind !== 'image') return\n if (!isZoomWheel(e)) return\n if (isInteractiveTarget(e.target)) return\n e.preventDefault()\n const delta = e.deltaY > 0 ? -SCALE_STEP : SCALE_STEP\n setScale(scale.value + delta)\n}\n\nfunction isInteractiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n return !!target.closest('button, a, input, textarea, select, video, [contenteditable=\"true\"]')\n}\n\nfunction handleViewportPointerDown(e: PointerEvent) {\n if (!canPan.value || e.button !== 0 || isInteractiveTarget(e.target)) return\n\n dragState = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startY: e.clientY,\n originX: offsetX.value,\n originY: offsetY.value\n }\n isDragging.value = true\n const el = e.currentTarget as HTMLElement\n el.setPointerCapture(e.pointerId)\n e.preventDefault()\n}\n\nfunction handleViewportPointerMove(e: PointerEvent) {\n if (!dragState || dragState.pointerId !== e.pointerId) return\n offsetX.value = dragState.originX + e.clientX - dragState.startX\n offsetY.value = dragState.originY + e.clientY - dragState.startY\n}\n\nfunction handleViewportPointerEnd(e: PointerEvent) {\n if (!dragState || dragState.pointerId !== e.pointerId) return\n const el = e.currentTarget as HTMLElement\n if (el.hasPointerCapture(e.pointerId)) {\n el.releasePointerCapture(e.pointerId)\n }\n dragState = undefined\n isDragging.value = false\n}\n\nfunction handleViewportDblclick() {\n if (isPdfActive.value) return\n if (!isTransformable.value) return\n if (scale.value === 1) {\n setScale(2)\n } else {\n resetTransform()\n }\n}\n\nasync function download() {\n if (!activeFile.value) return\n try {\n await downloadFile(activeFile.value)\n } catch (err) {\n emit('error', { file: activeFile.value, error: err })\n }\n}\n\nfunction handleChildError(err: unknown) {\n if (!activeFile.value) return\n emit('error', { file: activeFile.value, error: err })\n}\n\nfunction handleClose() {\n if (!isModal.value) return\n openModel.value = false\n}\n\nfunction handleWindowKeydown(e: KeyboardEvent) {\n if (e.key !== 'Escape') return\n if (!isModal.value || !openModel.value) return\n if (props.closeOnEsc === false) return\n e.stopPropagation()\n handleClose()\n}\n\nfunction handleBackdropMousedown() {\n if (!isModal.value) return\n if (props.closeOnClickBackdrop === false) return\n handleClose()\n}\n\nwatch(\n normalizedFiles,\n (files) => {\n if (!files.length) {\n activeId.value = undefined\n return\n }\n if (!activeId.value || !files.some((f) => f.id === activeId.value)) {\n activeId.value = files[0]!.id\n }\n },\n { immediate: true }\n)\n\nwatch(\n () => activeFile.value?.id,\n async () => {\n pdfZoomLevel.value = 1\n await nextTick()\n resetTransform()\n }\n)\n\n// 模态打开时:锁定 body 滚动 + 让容器获得焦点以便接收 ESC\nlet previousBodyOverflow = ''\nlet isBodyLocked = false\n\nfunction lockBody() {\n if (isBodyLocked || typeof document === 'undefined') return\n previousBodyOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n isBodyLocked = true\n}\n\nfunction unlockBody() {\n if (!isBodyLocked || typeof document === 'undefined') return\n document.body.style.overflow = previousBodyOverflow\n isBodyLocked = false\n}\n\nwatch(\n [isModal, openModel],\n ([modal, open]) => {\n if (modal && open) {\n lockBody()\n nextTick(() => {\n rootRef.value?.focus()\n })\n if (typeof window !== 'undefined') {\n window.addEventListener('keydown', handleWindowKeydown, true)\n }\n } else {\n unlockBody()\n if (typeof window !== 'undefined') {\n window.removeEventListener('keydown', handleWindowKeydown, true)\n }\n }\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n unlockBody()\n if (typeof window !== 'undefined') {\n window.removeEventListener('keydown', handleWindowKeydown, true)\n }\n})\n\ndefineExpose<_FileViewerExposed>({ activeId, activate, next, prev })\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-viewer.js","names":[],"sources":["../../../src/components/file-viewer/file-viewer.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\" :disabled=\"!isModal\">\n <transition :name=\"isModal ? 'u-file-viewer-modal' : ''\">\n <div\n v-if=\"!isModal || openModel\"\n :class=\"[cls.b, bem.is('modal', isModal)]\"\n :tabindex=\"isModal ? -1 : undefined\"\n ref=\"rootRef\"\n @mousedown.self=\"handleBackdropMousedown\"\n >\n <div v-if=\"isModal\" :class=\"cls.e('backdrop')\" @mousedown.self=\"handleBackdropMousedown\" />\n <div :class=\"cls.e('inner')\">\n <aside v-if=\"showSidebar\" :class=\"cls.e('sidebar')\" :style=\"{ width: sidebarWidthCss }\">\n <header :class=\"cls.e('sidebar-head')\">\n <span :class=\"cls.e('sidebar-title')\">文件</span>\n <span :class=\"cls.e('sidebar-count')\">{{ normalizedFiles.length }}</span>\n </header>\n <u-scroll tag=\"ul\" :class=\"cls.e('list')\">\n <li\n v-for=\"f in normalizedFiles\"\n :key=\"f.id\"\n :class=\"[cls.e('item'), bem.is('active', f.id === activeId)]\"\n :title=\"f.name\"\n @click=\"activate(f.id)\"\n >\n <span :class=\"cls.e('item-meta')\">\n <span :class=\"cls.e('item-name')\" :title=\"f.name\">{{ f.name }}</span>\n <span :class=\"cls.e('item-size')\">{{ formatBytes(f.size) }}</span>\n </span>\n <span :class=\"cls.e('item-kind')\">{{ label(f.kind) }}</span>\n </li>\n <li v-if=\"!normalizedFiles.length\" :class=\"cls.e('list-empty')\">\n <u-empty text=\"暂无文件\" :size=\"32\" />\n </li>\n </u-scroll>\n </aside>\n\n <section :class=\"cls.e('stage')\">\n <header :class=\"cls.e('stage-head')\">\n <div :class=\"cls.e('stage-title')\">\n <span v-if=\"activeFile\" :class=\"[cls.e('badge'), cls.em('badge', activeFile.kind)]\">\n {{ label(activeFile.kind) }}\n </span>\n <span :class=\"cls.e('stage-copy')\">\n <span :class=\"cls.e('stage-name')\" :title=\"activeFile?.name\">\n {{ activeFile?.name ?? '—' }}\n </span>\n <span v-if=\"activeFile\" :class=\"cls.e('stage-sub')\">\n <span>{{ activeIndexLabel }}</span>\n <span v-if=\"activeFile.size\">{{ formatBytes(activeFile.size) }}</span>\n </span>\n </span>\n </div>\n <div :class=\"cls.e('stage-actions')\">\n <span :class=\"cls.e('action-group')\">\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"!hasPrev\"\n type=\"button\"\n aria-label=\"上一个\"\n title=\"上一个\"\n @click=\"prev\"\n >\n <u-icon :size=\"15\">\n <ArrowLeft />\n </u-icon>\n </button>\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"!hasNext\"\n type=\"button\"\n aria-label=\"下一个\"\n title=\"下一个\"\n @click=\"next\"\n >\n <u-icon :size=\"15\">\n <ArrowRight />\n </u-icon>\n </button>\n </span>\n <span v-if=\"isZoomable\" :class=\"cls.e('action-group')\">\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"zoomOutDisabled\"\n type=\"button\"\n aria-label=\"缩小\"\n title=\"缩小\"\n @click=\"zoomOut\"\n >\n <u-icon :size=\"15\">\n <ZoomOut />\n </u-icon>\n </button>\n <span :class=\"cls.e('zoom-value')\">{{ zoomPercent }}</span>\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"zoomInDisabled\"\n type=\"button\"\n aria-label=\"放大\"\n title=\"放大\"\n @click=\"zoomIn\"\n >\n <u-icon :size=\"15\">\n <ZoomIn />\n </u-icon>\n </button>\n <button\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n :disabled=\"isTransformReset\"\n type=\"button\"\n aria-label=\"重置视图\"\n title=\"重置视图\"\n @click=\"resetTransform\"\n >\n <u-icon :size=\"15\">\n <Refresh />\n </u-icon>\n </button>\n </span>\n <button\n v-if=\"downloadable && activeFile\"\n :class=\"[cls.e('action'), cls.em('action', 'primary'), cls.em('action', 'icon')]\"\n type=\"button\"\n aria-label=\"下载\"\n title=\"下载\"\n @click=\"download\"\n >\n <u-icon :size=\"15\">\n <Download />\n </u-icon>\n </button>\n <button\n v-if=\"isModal\"\n :class=\"[cls.e('action'), cls.em('action', 'icon')]\"\n type=\"button\"\n aria-label=\"关闭预览\"\n title=\"关闭\"\n @click=\"handleClose\"\n >\n <u-icon :size=\"16\">\n <Close />\n </u-icon>\n </button>\n </div>\n </header>\n <div :class=\"cls.e('body')\">\n <transition name=\"u-file-viewer-fade\" mode=\"out-in\">\n <div\n v-if=\"activeFile\"\n :key=\"activeFile.id\"\n :class=\"[\n cls.e('viewport'),\n bem.is('transformable', isTransformable),\n bem.is('pannable', canPan),\n bem.is('dragging', isDragging)\n ]\"\n @pointerdown.capture=\"handleViewportPointerDown\"\n @pointermove.capture=\"handleViewportPointerMove\"\n @pointerup.capture=\"handleViewportPointerEnd\"\n @pointercancel.capture=\"handleViewportPointerEnd\"\n @wheel=\"handleViewportWheel\"\n @dblclick=\"handleViewportDblclick\"\n >\n <component\n :is=\"PreviewerMap[activeFile.kind]\"\n ref=\"previewerRef\"\n :file=\"activeFile\"\n :max-rows=\"sheetMaxRows\"\n :class=\"isTransformable ? cls.e('previewer') : undefined\"\n :style=\"previewerStyle\"\n @error=\"handleChildError\"\n @zoom-change=\"handlePreviewerZoomChange\"\n />\n </div>\n <div v-else :class=\"cls.e('empty')\">\n <u-empty text=\"暂无可预览文件\" />\n </div>\n </transition>\n </div>\n </section>\n </div>\n </div>\n </transition>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport {\n ArrowLeft,\n ArrowRight,\n Close,\n Download,\n Refresh,\n ZoomIn,\n ZoomOut\n} from '@veltra/icons/normal'\nimport { bem, withUnit } from '@veltra/utils'\nimport {\n computed,\n defineAsyncComponent,\n nextTick,\n onBeforeUnmount,\n ref,\n shallowRef,\n watch\n} from 'vue'\nimport type { CSSProperties } from 'vue'\n\nimport type {\n _FileViewerExposed,\n FileViewerEmits,\n FileViewerItem,\n FileViewerKind,\n FileViewerProps\n} from '../../types/file-viewer'\nimport { UEmpty } from '../empty'\nimport { UIcon } from '../icon'\nimport { UScroll } from '../scroll'\nimport { FILE_VIEWER_KIND_LABEL, downloadFile, formatBytes, inferKind } from './helper'\n\ndefineOptions({ name: 'UFileViewer', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<FileViewerProps>(), {\n modelValue: undefined,\n sidebarWidth: '280px',\n sheetMaxRows: 50_000,\n downloadable: true,\n open: undefined,\n closeOnClickBackdrop: true,\n closeOnEsc: true\n})\n\nconst emit = defineEmits<FileViewerEmits>()\n\nconst cls = bem('file-viewer')\n\nconst MIN_SCALE = 0.5\nconst MAX_SCALE = 3\n/** 每次缩放固定增减 10% */\nconst SCALE_STEP = 0.1\n/** 使用 CSS transform 缩放的类型(PDF / OFD 由各自 previewer 内部缩放) */\nconst TRANSFORMABLE_KINDS = new Set<FileViewerKind>(['image'])\n/** 工具栏显示缩放控件的类型 */\nconst ZOOMABLE_KINDS = new Set<FileViewerKind>(['image', 'pdf', 'ofd'])\n/** 缩放由 previewer 内部实现、组件只透传命令并回显级别的类型 */\nconst DELEGATED_ZOOM_KINDS = new Set<FileViewerKind>(['pdf', 'ofd'])\n\nconst activeId = defineModel<string | undefined>('modelValue', { default: undefined })\nconst openModel = defineModel<boolean | undefined>('open', { default: undefined })\n\nconst rootRef = shallowRef<HTMLDivElement>()\nconst previewerRef = shallowRef<{\n zoomIn?: () => void\n zoomOut?: () => void\n resetZoom?: () => void\n}>()\nconst scale = ref(1)\nconst previewerZoomLevel = ref(1)\nconst offsetX = ref(0)\nconst offsetY = ref(0)\nconst isDragging = ref(false)\n\nlet dragState:\n | { pointerId: number; startX: number; startY: number; originX: number; originY: number }\n | undefined\n\nconst PreviewerMap: Record<FileViewerKind, ReturnType<typeof defineAsyncComponent>> = {\n image: defineAsyncComponent(() => import('./previewers/image-previewer.vue')),\n video: defineAsyncComponent(() => import('./previewers/video-previewer.vue')),\n pdf: defineAsyncComponent(() => import('./previewers/pdf-previewer.vue')),\n sheet: defineAsyncComponent(() => import('./previewers/sheet-previewer.vue')),\n docx: defineAsyncComponent(() => import('./previewers/docx-previewer.vue')),\n ofd: defineAsyncComponent(() => import('./previewers/ofd-previewer.vue')),\n text: defineAsyncComponent(() => import('./previewers/text-previewer.vue'))\n}\n\ninterface NormalizedFile extends FileViewerItem {\n id: string\n kind: FileViewerKind\n}\n\nconst normalizedFiles = computed<NormalizedFile[]>(() =>\n props.files.map((f, i) => ({ ...f, id: f.id ?? `file-${i}`, kind: inferKind(f.name, f.kind) }))\n)\n\nconst activeFile = computed<NormalizedFile | undefined>(() =>\n normalizedFiles.value.find((f) => f.id === activeId.value)\n)\n\nconst activeIndex = computed(() => normalizedFiles.value.findIndex((f) => f.id === activeId.value))\n\nconst hasPrev = computed(() => activeIndex.value > 0)\nconst hasNext = computed(\n () => activeIndex.value >= 0 && activeIndex.value < normalizedFiles.value.length - 1\n)\n\nconst showSidebar = computed(() => props.sidebarWidth !== false && props.sidebarWidth !== 0)\n\nconst sidebarWidthCss = computed(() => {\n const w = props.sidebarWidth\n if (w === false || w === 0) return undefined\n return withUnit(w ?? '280px', 'px')\n})\n\n/** 是否启用模态模式:只要父组件显式传入 open(含 v-model:open),即进入模态 */\nconst isModal = computed(() => openModel.value !== undefined)\n\nconst activeIndexLabel = computed(() =>\n activeIndex.value >= 0 ? `${activeIndex.value + 1} / ${normalizedFiles.value.length}` : ''\n)\n\nconst isTransformable = computed(\n () => !!activeFile.value && TRANSFORMABLE_KINDS.has(activeFile.value.kind)\n)\n\nconst isZoomable = computed(() => !!activeFile.value && ZOOMABLE_KINDS.has(activeFile.value.kind))\n\nconst isDelegatedZoom = computed(\n () => !!activeFile.value && DELEGATED_ZOOM_KINDS.has(activeFile.value.kind)\n)\n\nconst canPan = computed(() => isTransformable.value && scale.value > 1)\n\nconst displayZoomLevel = computed(() =>\n isDelegatedZoom.value ? previewerZoomLevel.value : scale.value\n)\n\nconst zoomPercent = computed(() => `${Math.round(displayZoomLevel.value * 100)}%`)\n\nconst zoomInDisabled = computed(() => !isZoomable.value || displayZoomLevel.value >= MAX_SCALE)\n\nconst zoomOutDisabled = computed(() => !isZoomable.value || displayZoomLevel.value <= MIN_SCALE)\n\nconst isTransformReset = computed(() => {\n if (isDelegatedZoom.value) {\n return Math.abs(previewerZoomLevel.value - 1) < 0.02\n }\n return scale.value === 1 && offsetX.value === 0 && offsetY.value === 0\n})\n\nconst previewerStyle = computed<CSSProperties | undefined>(() => {\n if (!isTransformable.value) return undefined\n return {\n transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value})`\n }\n})\n\nfunction label(kind: FileViewerKind): string {\n return FILE_VIEWER_KIND_LABEL[kind]\n}\n\nfunction activate(id: string) {\n if (activeId.value === id) return\n activeId.value = id\n const f = normalizedFiles.value.find((x) => x.id === id)\n if (f) emit('change', f)\n}\n\nfunction prev() {\n const i = activeIndex.value\n if (i <= 0) return\n const target = normalizedFiles.value[i - 1]\n if (target) activate(target.id)\n}\n\nfunction next() {\n const i = activeIndex.value\n if (i < 0 || i >= normalizedFiles.value.length - 1) return\n const target = normalizedFiles.value[i + 1]\n if (target) activate(target.id)\n}\n\nfunction normalizeScale(value: number): number {\n const clamped = Math.min(MAX_SCALE, Math.max(MIN_SCALE, value))\n return Math.round(clamped * 100) / 100\n}\n\nfunction setScale(value: number) {\n scale.value = normalizeScale(value)\n if (scale.value <= 1) {\n offsetX.value = 0\n offsetY.value = 0\n }\n}\n\nfunction zoomIn() {\n if (!isZoomable.value) return\n if (isDelegatedZoom.value) {\n previewerRef.value?.zoomIn?.()\n return\n }\n setScale(scale.value + SCALE_STEP)\n}\n\nfunction zoomOut() {\n if (!isZoomable.value) return\n if (isDelegatedZoom.value) {\n previewerRef.value?.zoomOut?.()\n return\n }\n setScale(scale.value - SCALE_STEP)\n}\n\nfunction resetTransform() {\n if (isDelegatedZoom.value) {\n previewerRef.value?.resetZoom?.()\n previewerZoomLevel.value = 1\n return\n }\n scale.value = 1\n offsetX.value = 0\n offsetY.value = 0\n dragState = undefined\n isDragging.value = false\n}\n\nfunction handlePreviewerZoomChange(level: number) {\n previewerZoomLevel.value = level\n}\n\nfunction isZoomWheel(e: WheelEvent): boolean {\n return e.ctrlKey || e.metaKey\n}\n\nfunction handleViewportWheel(e: WheelEvent) {\n if (!activeFile.value || activeFile.value.kind !== 'image') return\n if (!isZoomWheel(e)) return\n if (isInteractiveTarget(e.target)) return\n e.preventDefault()\n const delta = e.deltaY > 0 ? -SCALE_STEP : SCALE_STEP\n setScale(scale.value + delta)\n}\n\nfunction isInteractiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n return !!target.closest('button, a, input, textarea, select, video, [contenteditable=\"true\"]')\n}\n\nfunction handleViewportPointerDown(e: PointerEvent) {\n if (!canPan.value || e.button !== 0 || isInteractiveTarget(e.target)) return\n\n dragState = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startY: e.clientY,\n originX: offsetX.value,\n originY: offsetY.value\n }\n isDragging.value = true\n const el = e.currentTarget as HTMLElement\n el.setPointerCapture(e.pointerId)\n e.preventDefault()\n}\n\nfunction handleViewportPointerMove(e: PointerEvent) {\n if (!dragState || dragState.pointerId !== e.pointerId) return\n offsetX.value = dragState.originX + e.clientX - dragState.startX\n offsetY.value = dragState.originY + e.clientY - dragState.startY\n}\n\nfunction handleViewportPointerEnd(e: PointerEvent) {\n if (!dragState || dragState.pointerId !== e.pointerId) return\n const el = e.currentTarget as HTMLElement\n if (el.hasPointerCapture(e.pointerId)) {\n el.releasePointerCapture(e.pointerId)\n }\n dragState = undefined\n isDragging.value = false\n}\n\nfunction handleViewportDblclick() {\n if (isDelegatedZoom.value) return\n if (!isTransformable.value) return\n if (scale.value === 1) {\n setScale(2)\n } else {\n resetTransform()\n }\n}\n\nasync function download() {\n if (!activeFile.value) return\n try {\n await downloadFile(activeFile.value)\n } catch (err) {\n emit('error', { file: activeFile.value, error: err })\n }\n}\n\nfunction handleChildError(err: unknown) {\n if (!activeFile.value) return\n emit('error', { file: activeFile.value, error: err })\n}\n\nfunction handleClose() {\n if (!isModal.value) return\n openModel.value = false\n}\n\nfunction handleWindowKeydown(e: KeyboardEvent) {\n if (e.key !== 'Escape') return\n if (!isModal.value || !openModel.value) return\n if (props.closeOnEsc === false) return\n e.stopPropagation()\n handleClose()\n}\n\nfunction handleBackdropMousedown() {\n if (!isModal.value) return\n if (props.closeOnClickBackdrop === false) return\n handleClose()\n}\n\nwatch(\n normalizedFiles,\n (files) => {\n if (!files.length) {\n activeId.value = undefined\n return\n }\n if (!activeId.value || !files.some((f) => f.id === activeId.value)) {\n activeId.value = files[0]!.id\n }\n },\n { immediate: true }\n)\n\nwatch(\n () => activeFile.value?.id,\n async () => {\n previewerZoomLevel.value = 1\n await nextTick()\n resetTransform()\n }\n)\n\n// 模态打开时:锁定 body 滚动 + 让容器获得焦点以便接收 ESC\nlet previousBodyOverflow = ''\nlet isBodyLocked = false\n\nfunction lockBody() {\n if (isBodyLocked || typeof document === 'undefined') return\n previousBodyOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n isBodyLocked = true\n}\n\nfunction unlockBody() {\n if (!isBodyLocked || typeof document === 'undefined') return\n document.body.style.overflow = previousBodyOverflow\n isBodyLocked = false\n}\n\nwatch(\n [isModal, openModel],\n ([modal, open]) => {\n if (modal && open) {\n lockBody()\n nextTick(() => {\n rootRef.value?.focus()\n })\n if (typeof window !== 'undefined') {\n window.addEventListener('keydown', handleWindowKeydown, true)\n }\n } else {\n unlockBody()\n if (typeof window !== 'undefined') {\n window.removeEventListener('keydown', handleWindowKeydown, true)\n }\n }\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n unlockBody()\n if (typeof window !== 'undefined') {\n window.removeEventListener('keydown', handleWindowKeydown, true)\n }\n})\n\ndefineExpose<_FileViewerExposed>({ activeId, activate, next, prev })\n</script>\n"],"mappings":"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"}
@@ -63,6 +63,7 @@ function inferKind(name, explicit) {
63
63
  if (ext === "pdf") return "pdf";
64
64
  if (SHEET_EXTS.has(ext)) return "sheet";
65
65
  if (ext === "docx") return "docx";
66
+ if (ext === "ofd") return "ofd";
66
67
  if (TEXT_EXTS.has(ext)) return "text";
67
68
  return "text";
68
69
  }
@@ -72,6 +73,7 @@ const FILE_VIEWER_KIND_LABEL = {
72
73
  pdf: "PDF",
73
74
  sheet: "XLS",
74
75
  docx: "DOC",
76
+ ofd: "OFD",
75
77
  text: "TXT"
76
78
  };
77
79
  /** 规范化数据源为 ArrayBuffer,可被 AbortController 中断(仅 URL fetch 场景生效) */
@@ -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\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":";AAEA,MAAM,6BAAa,IAAI,IAAI;CAAC;CAAO;CAAO;CAAQ;CAAO;CAAQ;CAAO;CAAO;CAAQ;AAAK,CAAC;AAE7F,MAAM,6BAAa,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAO;CAAO;CAAO;AAAK,CAAC;AAEtE,MAAM,6BAAa,IAAI,IAAI;CAAC;CAAQ;CAAQ;CAAQ;AAAK,CAAC;AAE1D,MAAM,4BAAY,IAAI,IAAI;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,aAAa,MAAsB;CACjD,MAAM,MAAM,KAAK,YAAY,GAAG;CAChC,OAAO,QAAQ,KAAK,KAAK,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC,YAAY;AAC3D;AAEA,SAAgB,UAAU,MAAc,UAA2C;CACjF,IAAI,UAAU,OAAO;CACrB,MAAM,MAAM,aAAa,IAAI;CAC7B,IAAI,WAAW,IAAI,GAAG,GAAG,OAAO;CAChC,IAAI,WAAW,IAAI,GAAG,GAAG,OAAO;CAChC,IAAI,QAAQ,OAAO,OAAO;CAC1B,IAAI,WAAW,IAAI,GAAG,GAAG,OAAO;CAChC,IAAI,QAAQ,QAAQ,OAAO;CAC3B,IAAI,UAAU,IAAI,GAAG,GAAG,OAAO;CAC/B,OAAO;AACT;AAEA,MAAa,yBAAyD;CACpE,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,MAAM;CACN,MAAM;AACR;;AAGA,eAAsB,cACpB,KACA,QACsB;CACtB,IAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,OAAO,CAAC;EACvC,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,iBAAiB,IAAI,OAAO,GAAG,IAAI,YAAY;EAEjE,OAAO,MAAM,IAAI,YAAY;CAC/B;CACA,IAAI,eAAe,aAAa,OAAO;CACvC,IAAI,eAAe,YAEjB,OAAO,IAAI,MAAM,CAAC,CAAC;CAGrB,OAAO,MAAM,IAAI,YAAY;AAC/B;;AAGA,SAAgB,UACd,KACA,MACqC;CACrC,IAAI,OAAO,QAAQ,UACjB,OAAO;EAAE,KAAK;EAAK,cAAc,CAAC;CAAE;CAEtC,IAAI;CACJ,IAAI,eAAe,MACjB,OAAO;MACF,IAAI,eAAe,aACxB,OAAO,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,QAAQ,2BAA2B,CAAC;MAC9D;EAEL,MAAM,OAAO,IAAI,WAAW,IAAI,UAAU;EAC1C,KAAK,IAAI,GAAG;EACZ,OAAO,IAAI,KAAK,CAAC,KAAK,MAAM,GAAG,EAAE,MAAM,QAAQ,2BAA2B,CAAC;CAC7E;CACA,MAAM,MAAM,IAAI,gBAAgB,IAAI;CACpC,OAAO;EAAE;EAAK,cAAc,IAAI,gBAAgB,GAAG;CAAE;AACvD;AAEA,SAAgB,YAAY,OAAwB;CAClD,IAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,OAAO,MAAM,KAAK,GAAG,OAAO;CACzE,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAM;CAClC,MAAM,QAAQ;EAAC;EAAM;EAAM;EAAM;CAAI;CACrC,IAAI,IAAI,QAAQ;CAChB,IAAI,IAAI;CACR,OAAO,KAAK,QAAQ,IAAI,MAAM,SAAS,GAAG;EACxC,KAAK;EACL;CACF;CACA,OAAO,GAAG,EAAE,QAAQ,KAAK,KAAK,IAAI,CAAC,EAAE,GAAG,MAAM;AAChD;;AAGA,eAAsB,aAAa,MAAqC;CACtE,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,KAAK,QAAQ,UACtB,OAAO,KAAK;MACP;EACL,MAAM,MAAM,UAAU,KAAK,KAAK,KAAK,IAAI;EACzC,OAAO,IAAI;EACX,SAAS,IAAI;CACf;CACA,MAAM,IAAI,SAAS,cAAc,GAAG;CACpC,EAAE,OAAO;CACT,EAAE,WAAW,KAAK;CAClB,EAAE,MAAM;CACR,EAAE,MAAM,UAAU;CAClB,SAAS,KAAK,YAAY,CAAC;CAC3B,EAAE,MAAM;CACR,SAAS,KAAK,YAAY,CAAC;CAC3B,IAAI,QAAQ,WAAW,QAAQ,GAAI;AACrC"}
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 (ext === 'ofd') return 'ofd'\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 ofd: 'OFD',\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\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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"}
@@ -0,0 +1,196 @@
1
+ import _sfc_main$1 from "../../empty/empty.js";
2
+ import { toArrayBuffer } from "../helper.js";
3
+ import { Fragment, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, renderList, unref, useTemplateRef, watch } from "vue";
4
+ import { bem } from "@veltra/utils";
5
+ //#region src/components/file-viewer/previewers/ofd-previewer.vue
6
+ const _hoisted_1 = ["data-index"];
7
+ const _hoisted_2 = ["innerHTML"];
8
+ /** OFD 毫米 → CSS 像素(96dpi),与内核 SVG 输出的换算一致 */
9
+ const PX_PER_MM = 96 / 25.4;
10
+ const MIN_ZOOM = .5;
11
+ const MAX_ZOOM = 3;
12
+ /** 每次缩放固定增减 10%,对齐 pdf 缩放配置 */
13
+ const ZOOM_STEP = .1;
14
+ /** 可视区上下各预渲染一个视口高度的页 */
15
+ const RENDER_MARGIN = "100% 0px";
16
+ const _sfc_main = /*@__PURE__*/ defineComponent({
17
+ name: "UFileViewerOfdPreviewer",
18
+ __name: "ofd-previewer",
19
+ props: { file: {} },
20
+ emits: ["error", "zoom-change"],
21
+ setup(__props, { expose: __expose, emit: __emit }) {
22
+ /** 仅类型查询;运行时动态 import 按需加载,构建时随 alwaysBundle 内联进 dist */
23
+ const props = __props;
24
+ const emit = __emit;
25
+ const cls = bem("file-viewer");
26
+ const scrollEl = useTemplateRef("scroll");
27
+ const loading = ref(true);
28
+ const pages = ref([]);
29
+ const zoomLevel = ref(1);
30
+ let controller;
31
+ /** 递增 token:防止切文件后旧解析/渲染结果落地 */
32
+ let loadToken = 0;
33
+ let ofdCore;
34
+ let observer;
35
+ let activeZip;
36
+ let activeContainer;
37
+ /** 已进入渲染流程(进行中或已完成)的页,防止滚动重复触发 */
38
+ const scheduledPages = /* @__PURE__ */ new Set();
39
+ function clampZoom(value) {
40
+ return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, Math.round(value * 100) / 100));
41
+ }
42
+ function setZoom(value) {
43
+ const next = clampZoom(value);
44
+ if (zoomLevel.value === next) return;
45
+ zoomLevel.value = next;
46
+ emit("zoom-change", next);
47
+ }
48
+ function zoomIn() {
49
+ setZoom(zoomLevel.value + ZOOM_STEP);
50
+ }
51
+ function zoomOut() {
52
+ setZoom(zoomLevel.value - ZOOM_STEP);
53
+ }
54
+ function resetZoom() {
55
+ setZoom(1);
56
+ }
57
+ __expose({
58
+ zoomIn,
59
+ zoomOut,
60
+ resetZoom
61
+ });
62
+ /** 动态加载 ofd-core chunk;首次加载后缓存模块复用 */
63
+ async function resolveOfdCore() {
64
+ const mod = ofdCore ?? await import("../../../packages/ofd-core/dist/index.js");
65
+ ofdCore = mod;
66
+ return mod;
67
+ }
68
+ function teardownRender() {
69
+ observer?.disconnect();
70
+ observer = void 0;
71
+ activeZip = void 0;
72
+ activeContainer = void 0;
73
+ scheduledPages.clear();
74
+ }
75
+ function observePages() {
76
+ const root = scrollEl.value;
77
+ if (!root || typeof IntersectionObserver === "undefined") return;
78
+ observer = new IntersectionObserver((entries) => {
79
+ const token = loadToken;
80
+ for (const entry of entries) {
81
+ if (!entry.isIntersecting) continue;
82
+ observer?.unobserve(entry.target);
83
+ const index = Number(entry.target.dataset.index);
84
+ if (Number.isNaN(index) || scheduledPages.has(index)) continue;
85
+ scheduledPages.add(index);
86
+ renderPage(index, token);
87
+ }
88
+ }, {
89
+ root,
90
+ rootMargin: RENDER_MARGIN
91
+ });
92
+ for (const el of root.querySelectorAll("[data-index]")) observer.observe(el);
93
+ }
94
+ /** 渲染单页 SVG;失败置空串避免滚动时反复重试同一页 */
95
+ async function renderPage(index, token) {
96
+ const page = pages.value[index];
97
+ const core = ofdCore;
98
+ if (!page || page.svg !== null || !core || !activeZip || !activeContainer) return;
99
+ try {
100
+ const svg = await core.pageToSvg(activeZip, activeContainer, page.docIndex, page.pageIndex);
101
+ if (token !== loadToken) return;
102
+ pages.value[index] = {
103
+ ...page,
104
+ svg
105
+ };
106
+ } catch (err) {
107
+ if (token !== loadToken) return;
108
+ pages.value[index] = {
109
+ ...page,
110
+ svg: ""
111
+ };
112
+ emit("error", err);
113
+ }
114
+ }
115
+ async function load() {
116
+ const token = ++loadToken;
117
+ controller?.abort();
118
+ controller = new AbortController();
119
+ teardownRender();
120
+ loading.value = true;
121
+ pages.value = [];
122
+ try {
123
+ const core = await resolveOfdCore();
124
+ if (token !== loadToken) return;
125
+ const buf = await toArrayBuffer(props.file.src, controller.signal);
126
+ if (token !== loadToken) return;
127
+ const zip = core.openOfdZip(new Uint8Array(buf));
128
+ const container = await core.parseOfdContainer(zip);
129
+ if (token !== loadToken) return;
130
+ activeZip = zip;
131
+ activeContainer = container;
132
+ pages.value = container.docs.flatMap((doc, docIndex) => doc.pages.map((page) => {
133
+ const size = page.size ?? doc.pageSize ?? {
134
+ width: 210,
135
+ height: 297
136
+ };
137
+ return {
138
+ docIndex,
139
+ pageIndex: page.index,
140
+ widthPx: Math.round(size.width * PX_PER_MM),
141
+ heightPx: Math.round(size.height * PX_PER_MM),
142
+ svg: null
143
+ };
144
+ }));
145
+ await nextTick();
146
+ if (token !== loadToken) return;
147
+ observePages();
148
+ } catch (err) {
149
+ if (token !== loadToken) return;
150
+ if (err?.name === "AbortError") return;
151
+ emit("error", err);
152
+ } finally {
153
+ if (token === loadToken) loading.value = false;
154
+ }
155
+ }
156
+ watch(() => props.file, load, { immediate: true });
157
+ onBeforeUnmount(() => {
158
+ controller?.abort();
159
+ teardownRender();
160
+ pages.value = [];
161
+ });
162
+ return (_ctx, _cache) => {
163
+ return openBlock(), createElementBlock("div", { class: normalizeClass(unref(cls).e("ofd")) }, [createElementVNode("div", {
164
+ ref: "scroll",
165
+ class: normalizeClass(unref(cls).e("ofd-scroll"))
166
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(pages.value, (page, i) => {
167
+ return openBlock(), createElementBlock("div", {
168
+ key: i,
169
+ "data-index": i,
170
+ class: normalizeClass(unref(cls).e("ofd-page")),
171
+ style: normalizeStyle({
172
+ width: page.widthPx * zoomLevel.value + "px",
173
+ height: page.heightPx * zoomLevel.value + "px"
174
+ })
175
+ }, [page.svg ? (openBlock(), createElementBlock("div", {
176
+ key: 0,
177
+ class: normalizeClass(unref(cls).e("ofd-page-body")),
178
+ innerHTML: page.svg
179
+ }, null, 10, _hoisted_2)) : createCommentVNode("v-if", true)], 14, _hoisted_1);
180
+ }), 128)), !pages.value.length && !loading.value ? (openBlock(), createElementBlock("div", {
181
+ key: 0,
182
+ class: normalizeClass(unref(cls).e("empty"))
183
+ }, [createVNode(unref(_sfc_main$1), {
184
+ text: "该文件没有可预览的页面",
185
+ size: 32
186
+ })], 2)) : createCommentVNode("v-if", true)], 2), loading.value ? (openBlock(), createElementBlock("div", {
187
+ key: 0,
188
+ class: normalizeClass(unref(cls).e("loading"))
189
+ }, "正在解析 OFD…", 2)) : createCommentVNode("v-if", true)], 2);
190
+ };
191
+ }
192
+ });
193
+ //#endregion
194
+ export { _sfc_main as default };
195
+
196
+ //# sourceMappingURL=ofd-previewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ofd-previewer.js","names":[],"sources":["../../../../src/components/file-viewer/previewers/ofd-previewer.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('ofd')\">\n <div ref=\"scroll\" :class=\"cls.e('ofd-scroll')\">\n <div\n v-for=\"(page, i) in pages\"\n :key=\"i\"\n :data-index=\"i\"\n :class=\"cls.e('ofd-page')\"\n :style=\"{\n width: page.widthPx * zoomLevel + 'px',\n height: page.heightPx * zoomLevel + 'px'\n }\"\n >\n <div v-if=\"page.svg\" :class=\"cls.e('ofd-page-body')\" v-html=\"page.svg\" />\n </div>\n <div v-if=\"!pages.length && !loading\" :class=\"cls.e('empty')\">\n <u-empty text=\"该文件没有可预览的页面\" :size=\"32\" />\n </div>\n </div>\n <div v-if=\"loading\" :class=\"cls.e('loading')\">正在解析 OFD…</div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { nextTick, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\n\nimport type { FileViewerItem } from '../../../types/file-viewer'\nimport { UEmpty } from '../../empty'\nimport { toArrayBuffer } from '../helper'\n\ndefineOptions({ name: 'UFileViewerOfdPreviewer' })\n\n/** 仅类型查询;运行时动态 import 按需加载,构建时随 alwaysBundle 内联进 dist */\ntype OfdCoreModule = typeof import('@veltra/ofd-core')\ntype OfdContainer = import('@veltra/ofd-core').OfdContainer\ntype OfdZip = import('@veltra/ofd-core').OfdZip\n\n/** OFD 毫米 → CSS 像素(96dpi),与内核 SVG 输出的换算一致 */\nconst PX_PER_MM = 96 / 25.4\n\nconst MIN_ZOOM = 0.5\nconst MAX_ZOOM = 3\n/** 每次缩放固定增减 10%,对齐 pdf 缩放配置 */\nconst ZOOM_STEP = 0.1\n/** 可视区上下各预渲染一个视口高度的页 */\nconst RENDER_MARGIN = '100% 0px'\n\ninterface OfdPageView {\n docIndex: number\n pageIndex: number\n /** 页面基准尺寸(缩放 1 时),毫米按 96dpi 换算为像素 */\n widthPx: number\n heightPx: number\n /** null = 未渲染;'' = 已尝试但渲染失败,不再重试 */\n svg: string | null\n}\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 scrollEl = useTemplateRef<HTMLDivElement>('scroll')\nconst loading = ref(true)\nconst pages = ref<OfdPageView[]>([])\nconst zoomLevel = ref(1)\n\nlet controller: AbortController | undefined\n/** 递增 token:防止切文件后旧解析/渲染结果落地 */\nlet loadToken = 0\nlet ofdCore: OfdCoreModule | undefined\nlet observer: IntersectionObserver | undefined\nlet activeZip: OfdZip | undefined\nlet activeContainer: OfdContainer | undefined\n/** 已进入渲染流程(进行中或已完成)的页,防止滚动重复触发 */\nconst scheduledPages = new Set<number>()\n\n// ---- 缩放:作用于页面容器尺寸,滚动高度随之自适应 ----\n\nfunction clampZoom(value: number): number {\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, Math.round(value * 100) / 100))\n}\n\nfunction setZoom(value: number) {\n const next = clampZoom(value)\n if (zoomLevel.value === next) return\n zoomLevel.value = next\n emit('zoom-change', next)\n}\n\nfunction zoomIn() {\n setZoom(zoomLevel.value + ZOOM_STEP)\n}\n\nfunction zoomOut() {\n setZoom(zoomLevel.value - ZOOM_STEP)\n}\n\nfunction resetZoom() {\n setZoom(1)\n}\n\ndefineExpose({ zoomIn, zoomOut, resetZoom })\n\n// ---- 解析与懒渲染 ----\n\n/** 动态加载 ofd-core chunk;首次加载后缓存模块复用 */\nasync function resolveOfdCore(): Promise<OfdCoreModule> {\n const mod = ofdCore ?? (await import('@veltra/ofd-core'))\n ofdCore = mod\n return mod\n}\n\nfunction teardownRender() {\n observer?.disconnect()\n observer = undefined\n activeZip = undefined\n activeContainer = undefined\n scheduledPages.clear()\n}\n\nfunction observePages() {\n const root = scrollEl.value\n if (!root || typeof IntersectionObserver === 'undefined') return\n observer = new IntersectionObserver(\n (entries) => {\n const token = loadToken\n for (const entry of entries) {\n if (!entry.isIntersecting) continue\n observer?.unobserve(entry.target)\n const index = Number((entry.target as HTMLElement).dataset.index)\n if (Number.isNaN(index) || scheduledPages.has(index)) continue\n scheduledPages.add(index)\n void renderPage(index, token)\n }\n },\n { root, rootMargin: RENDER_MARGIN }\n )\n for (const el of root.querySelectorAll<HTMLElement>('[data-index]')) {\n observer.observe(el)\n }\n}\n\n/** 渲染单页 SVG;失败置空串避免滚动时反复重试同一页 */\nasync function renderPage(index: number, token: number) {\n const page = pages.value[index]\n const core = ofdCore\n if (!page || page.svg !== null || !core || !activeZip || !activeContainer) return\n try {\n const svg = await core.pageToSvg(activeZip, activeContainer, page.docIndex, page.pageIndex)\n if (token !== loadToken) return\n pages.value[index] = { ...page, svg }\n } catch (err) {\n if (token !== loadToken) return\n pages.value[index] = { ...page, svg: '' }\n emit('error', err)\n }\n}\n\nasync function load() {\n const token = ++loadToken\n controller?.abort()\n controller = new AbortController()\n teardownRender()\n loading.value = true\n pages.value = []\n\n try {\n const core = await resolveOfdCore()\n if (token !== loadToken) return\n\n const buf = await toArrayBuffer(props.file.src, controller.signal)\n if (token !== loadToken) return\n const zip = core.openOfdZip(new Uint8Array(buf))\n const container = await core.parseOfdContainer(zip)\n if (token !== loadToken) return\n\n activeZip = zip\n activeContainer = container\n // 未声明尺寸时按 A4 兜底,与内核 pageToSvg 的兜底一致\n pages.value = container.docs.flatMap((doc, docIndex) =>\n doc.pages.map((page) => {\n const size = page.size ?? doc.pageSize ?? { width: 210, height: 297 }\n return {\n docIndex,\n pageIndex: page.index,\n widthPx: Math.round(size.width * PX_PER_MM),\n heightPx: Math.round(size.height * PX_PER_MM),\n svg: null\n }\n })\n )\n await nextTick()\n if (token !== loadToken) return\n observePages()\n } catch (err) {\n if (token !== loadToken) return\n if ((err as { name?: string })?.name === 'AbortError') return\n emit('error', err)\n } finally {\n if (token === loadToken) loading.value = false\n }\n}\n\nwatch(() => props.file, load, { immediate: true })\n\nonBeforeUnmount(() => {\n controller?.abort()\n teardownRender()\n pages.value = []\n})\n</script>\n"],"mappings":"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"}
@@ -257,6 +257,11 @@
257
257
  --u-file-viewer-kind-border: rgba(100, 116, 139, 0.28);
258
258
  --u-file-viewer-kind-bg: rgba(100, 116, 139, 0.1);
259
259
  }
260
+ .u-file-viewer__badge--ofd {
261
+ --u-file-viewer-kind: #7c3aed;
262
+ --u-file-viewer-kind-border: rgba(124, 58, 237, 0.28);
263
+ --u-file-viewer-kind-bg: rgba(124, 58, 237, 0.1);
264
+ }
260
265
  .u-file-viewer__stage {
261
266
  display: flex;
262
267
  flex: 1;
@@ -566,6 +571,28 @@
566
571
  margin: clamp(24px, 3vw, 44px) auto;
567
572
  box-shadow: 0 38px 110px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.04);
568
573
  }
574
+ .u-file-viewer__ofd {
575
+ position: absolute;
576
+ inset: 0;
577
+ }
578
+ .u-file-viewer__ofd-scroll {
579
+ width: 100%;
580
+ height: 100%;
581
+ overflow: auto;
582
+ }
583
+ .u-file-viewer__ofd-page {
584
+ margin: 24px auto;
585
+ background: #fff;
586
+ box-shadow: var(--u-file-viewer-page-shadow);
587
+ }
588
+ .u-file-viewer__ofd-page svg {
589
+ display: block;
590
+ width: 100%;
591
+ height: 100%;
592
+ }
593
+ .u-file-viewer.is-modal .u-file-viewer__ofd-page {
594
+ margin: clamp(24px, 3vw, 44px) auto;
595
+ }
569
596
  .u-file-viewer__docx {
570
597
  position: absolute;
571
598
  inset: 0;
@@ -17,10 +17,30 @@
17
17
  /** 暗色:`UITheme.render()` 按主题系列写入 html[data-theme] */
18
18
  .u-table-editor .u-table__body .u-table__cell {
19
19
  padding: 2px;
20
+ height: calc(var(--u-form-component-height-default) + 4px + 1px);
20
21
  }
22
+ .u-table-editor .u-table__body .u-table__cell > [class^=u-],
21
23
  .u-table-editor .u-table__body .u-table__cell [class^=u-input] {
22
- box-shadow: none;
24
+ vertical-align: top;
23
25
  }
24
- .u-table-editor .u-table__body .u-table__cell > [class^=u-] {
25
- box-shadow: none;
26
+ .u-table-editor .u-table-editor__operations {
27
+ display: flex;
28
+ justify-content: center;
29
+ gap: 4px;
30
+ }
31
+ .u-table-editor .u-table-editor__operations .u-button {
32
+ padding: 0 0.4em;
33
+ }
34
+ .u-table-editor__required-mark {
35
+ padding-right: 2px;
36
+ color: var(--u-color-danger);
37
+ }
38
+ .u-table-editor__header-text.is-error {
39
+ color: var(--u-color-danger);
40
+ }
41
+ .u-table-editor__header-icon {
42
+ color: var(--u-color-danger);
43
+ margin-left: 2px;
44
+ vertical-align: -2px;
45
+ cursor: help;
26
46
  }