@veltra/desktop 1.7.12 → 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.
- package/dist/components/batch-edit/batch-edit-form.js +9 -3
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/batch-edit-list.js +2 -1
- package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
- package/dist/components/batch-edit/batch-edit.js +57 -30
- package/dist/components/batch-edit/batch-edit.js.map +1 -1
- package/dist/components/batch-edit/helper.d.ts +1 -2
- package/dist/components/batch-edit/style.d.ts +1 -0
- package/dist/components/batch-edit/style.js +1 -0
- package/dist/components/batch-edit/style2.css +14 -0
- package/dist/components/batch-edit/use-dialog-effect.js +20 -0
- package/dist/components/batch-edit/use-dialog-effect.js.map +1 -0
- package/dist/components/batch-edit/use-edit-state.js +22 -3
- package/dist/components/batch-edit/use-edit-state.js.map +1 -1
- package/dist/components/batch-edit/use-handlers.js +5 -0
- package/dist/components/batch-edit/use-handlers.js.map +1 -1
- package/dist/components/batch-edit/use-shortcut-key.js +5 -2
- package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
- package/dist/components/condition-editor/core/evaluator.d.ts +4 -5
- package/dist/components/contextmenu/api.d.ts +1 -2
- package/dist/components/contextmenu/di.d.ts +2 -3
- package/dist/components/file-viewer/file-viewer.js +21 -14
- package/dist/components/file-viewer/file-viewer.js.map +1 -1
- package/dist/components/file-viewer/helper.js +2 -0
- package/dist/components/file-viewer/helper.js.map +1 -1
- package/dist/components/file-viewer/previewers/ofd-previewer.js +196 -0
- package/dist/components/file-viewer/previewers/ofd-previewer.js.map +1 -0
- package/dist/components/file-viewer/style2.css +27 -0
- package/dist/components/loading/directive.d.ts +1 -2
- package/dist/components/message/message.d.ts +1 -2
- package/dist/components/message-confirm/message-confirm.d.ts +1 -2
- package/dist/components/notification/notification.d.ts +1 -2
- package/dist/components/table/node/col.js +19 -0
- package/dist/components/table/node/col.js.map +1 -1
- package/dist/components/table/table-cell.js +2 -0
- package/dist/components/table/table-cell.js.map +1 -1
- package/dist/components/table/table-foot.js +2 -0
- package/dist/components/table/table-foot.js.map +1 -1
- package/dist/components/table/table-head.js +1 -0
- package/dist/components/table/table-head.js.map +1 -1
- package/dist/components/table/table-row.js +2 -0
- package/dist/components/table/table-row.js.map +1 -1
- package/dist/components/table/use-columns.d.ts +1 -2
- package/dist/components/table-editor/style2.css +23 -3
- package/dist/components/table-editor/table-editor.js +213 -63
- package/dist/components/table-editor/table-editor.js.map +1 -1
- package/dist/components/tip/style2.css +63 -0
- package/dist/components/tip/tip.js +6 -1
- package/dist/components/tip/tip.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/install.d.ts +3 -3
- package/dist/packages/ofd-core/dist/color.js +69 -0
- package/dist/packages/ofd-core/dist/color.js.map +1 -0
- package/dist/packages/ofd-core/dist/ctm.js +55 -0
- package/dist/packages/ofd-core/dist/ctm.js.map +1 -0
- package/dist/packages/ofd-core/dist/error.js +14 -0
- package/dist/packages/ofd-core/dist/error.js.map +1 -0
- package/dist/packages/ofd-core/dist/font.js +384 -0
- package/dist/packages/ofd-core/dist/font.js.map +1 -0
- package/dist/packages/ofd-core/dist/index.js +6 -0
- package/dist/packages/ofd-core/dist/number.js +13 -0
- package/dist/packages/ofd-core/dist/number.js.map +1 -0
- package/dist/packages/ofd-core/dist/page-objects.js +181 -0
- package/dist/packages/ofd-core/dist/page-objects.js.map +1 -0
- package/dist/packages/ofd-core/dist/parse.js +65 -0
- package/dist/packages/ofd-core/dist/parse.js.map +1 -0
- package/dist/packages/ofd-core/dist/render.js +361 -0
- package/dist/packages/ofd-core/dist/render.js.map +1 -0
- package/dist/packages/ofd-core/dist/xml.js +199 -0
- package/dist/packages/ofd-core/dist/xml.js.map +1 -0
- package/dist/packages/ofd-core/dist/zip.js +93 -0
- package/dist/packages/ofd-core/dist/zip.js.map +1 -0
- package/dist/style.d.ts +1 -1
- package/dist/style.js +1 -1
- package/dist/types/action.d.ts +7 -8
- package/dist/types/animation.d.ts +4 -5
- package/dist/types/auto-complete.d.ts +4 -5
- package/dist/types/badge.d.ts +4 -5
- package/dist/types/batch-edit.d.ts +15 -10
- package/dist/types/breadcrumb.d.ts +6 -7
- package/dist/types/button.d.ts +5 -6
- package/dist/types/calendar.d.ts +7 -8
- package/dist/types/card.d.ts +7 -8
- package/dist/types/cascade.d.ts +6 -7
- package/dist/types/check-tag.d.ts +4 -5
- package/dist/types/checkbox-group.d.ts +3 -4
- package/dist/types/checkbox.d.ts +5 -6
- package/dist/types/code-editor.d.ts +5 -6
- package/dist/types/collapse.d.ts +6 -7
- package/dist/types/condition-editor.d.ts +11 -12
- package/dist/types/contextmenu.d.ts +5 -6
- package/dist/types/css-transition.d.ts +4 -5
- package/dist/types/date-panel.d.ts +3 -4
- package/dist/types/date-picker.d.ts +5 -6
- package/dist/types/date-range-picker.d.ts +6 -7
- package/dist/types/dialog.d.ts +5 -6
- package/dist/types/drawer.d.ts +6 -7
- package/dist/types/dropdown.d.ts +4 -5
- package/dist/types/dual-nav.d.ts +6 -7
- package/dist/types/empty.d.ts +4 -5
- package/dist/types/expression-editor.d.ts +6 -7
- package/dist/types/file-picker.d.ts +4 -5
- package/dist/types/file-viewer.d.ts +6 -7
- package/dist/types/float-button.d.ts +5 -6
- package/dist/types/form-item.d.ts +3 -4
- package/dist/types/form.d.ts +4 -5
- package/dist/types/gantt-chart.d.ts +4 -5
- package/dist/types/grid-input.d.ts +4 -5
- package/dist/types/grid.d.ts +7 -8
- package/dist/types/group-input.d.ts +4 -5
- package/dist/types/group-nav.d.ts +3 -4
- package/dist/types/icon.d.ts +3 -4
- package/dist/types/image-cropper.d.ts +9 -10
- package/dist/types/index.d.ts +4 -4
- package/dist/types/input.d.ts +4 -5
- package/dist/types/layout.d.ts +4 -5
- package/dist/types/list.d.ts +4 -5
- package/dist/types/loading.d.ts +5 -6
- package/dist/types/message-confirm.d.ts +8 -9
- package/dist/types/message.d.ts +8 -9
- package/dist/types/multi-auto-complete.d.ts +4 -5
- package/dist/types/multi-select.d.ts +4 -5
- package/dist/types/multi-tree-select.d.ts +4 -5
- package/dist/types/nav.d.ts +5 -6
- package/dist/types/node-render.d.ts +2 -3
- package/dist/types/notification.d.ts +8 -9
- package/dist/types/number-input.d.ts +3 -4
- package/dist/types/number-range-input.d.ts +4 -5
- package/dist/types/number.d.ts +3 -4
- package/dist/types/paginator.d.ts +4 -5
- package/dist/types/palette.d.ts +7 -8
- package/dist/types/password-input.d.ts +4 -5
- package/dist/types/pop-confirm.d.ts +4 -5
- package/dist/types/pop.d.ts +4 -5
- package/dist/types/progress-nodes.d.ts +4 -5
- package/dist/types/progress.d.ts +4 -5
- package/dist/types/quick-batch-edit.d.ts +4 -5
- package/dist/types/radio-group.d.ts +4 -5
- package/dist/types/radio.d.ts +4 -5
- package/dist/types/rich-text-editor.d.ts +6 -7
- package/dist/types/scroll.d.ts +5 -6
- package/dist/types/segment.d.ts +5 -6
- package/dist/types/select.d.ts +4 -5
- package/dist/types/slider.d.ts +4 -5
- package/dist/types/steps.d.ts +5 -6
- package/dist/types/switch.d.ts +4 -5
- package/dist/types/table-editor.d.ts +30 -10
- package/dist/types/table.d.ts +23 -13
- package/dist/types/tabs.d.ts +13 -14
- package/dist/types/tag.d.ts +3 -4
- package/dist/types/text-editor.d.ts +5 -6
- package/dist/types/text.d.ts +4 -5
- package/dist/types/textarea.d.ts +4 -5
- package/dist/types/theme.d.ts +3 -4
- package/dist/types/tip.d.ts +11 -8
- package/dist/types/tree-select.d.ts +4 -5
- package/dist/types/tree.d.ts +6 -6
- package/dist/types/watermark.d.ts +4 -5
- package/package.json +8 -7
- package/src/components/batch-edit/__test__/batch-edit.test.ts +191 -4
- package/src/components/batch-edit/batch-edit-form.vue +17 -3
- package/src/components/batch-edit/batch-edit-list.vue +2 -1
- package/src/components/batch-edit/batch-edit.vue +22 -4
- package/src/components/batch-edit/style.scss +20 -0
- package/src/components/batch-edit/style.ts +1 -0
- package/src/components/batch-edit/use-dialog-effect.ts +9 -3
- package/src/components/batch-edit/use-edit-state.ts +32 -4
- package/src/components/batch-edit/use-handlers.ts +10 -0
- package/src/components/batch-edit/use-shortcut-key.ts +6 -2
- package/src/components/file-viewer/__test__/helper.test.ts +13 -0
- package/src/components/file-viewer/file-viewer.vue +23 -16
- package/src/components/file-viewer/helper.ts +2 -0
- package/src/components/file-viewer/previewers/ofd-previewer.vue +216 -0
- package/src/components/file-viewer/style.scss +33 -0
- package/src/components/table/__test__/column-style.test.ts +67 -0
- package/src/components/table/node/col.ts +30 -1
- package/src/components/table/table-cell.vue +7 -2
- package/src/components/table/table-foot.vue +2 -0
- package/src/components/table/table-head.tsx +1 -0
- package/src/components/table/table-row.tsx +2 -0
- package/src/components/table-editor/__test__/table-editor.test.ts +471 -0
- package/src/components/table-editor/style.scss +42 -4
- package/src/components/table-editor/table-editor.vue +265 -38
- package/src/components/tip/style.scss +15 -0
- package/src/components/tip/tip.vue +1 -1
- package/src/types/batch-edit.ts +6 -0
- package/src/types/file-viewer.ts +1 -1
- package/src/types/table-editor.ts +29 -7
- package/src/types/table.ts +12 -0
- 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([
|
|
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
|
|
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
|
|
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(() =>
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
184
|
+
if (isDelegatedZoom.value) {
|
|
178
185
|
previewerRef.value?.resetZoom?.();
|
|
179
|
-
|
|
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
|
|
189
|
-
|
|
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 (
|
|
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
|
-
|
|
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:
|
|
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":"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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 (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;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ObjectDirective } from "vue";
|
|
2
2
|
//#region src/components/loading/directive.d.ts
|
|
3
|
-
declare const vLoading: ObjectDirective<HTMLElement>;
|
|
3
|
+
export declare const vLoading: ObjectDirective<HTMLElement>;
|
|
4
4
|
//#endregion
|
|
5
|
-
export { vLoading };
|
|
6
5
|
//# sourceMappingURL=directive.d.ts.map
|
|
@@ -2,7 +2,6 @@ import { Message } from "../../types/message.js";
|
|
|
2
2
|
import "../../types/index.js";
|
|
3
3
|
//#region src/components/message/message.d.ts
|
|
4
4
|
/** --- 导出 API --- */
|
|
5
|
-
declare const message: Message;
|
|
5
|
+
export declare const message: Message;
|
|
6
6
|
//#endregion
|
|
7
|
-
export { message };
|
|
8
7
|
//# sourceMappingURL=message.d.ts.map
|
|
@@ -2,7 +2,6 @@ import { MessageConfirm } from "../../types/message-confirm.js";
|
|
|
2
2
|
import "../../types/index.js";
|
|
3
3
|
//#region src/components/message-confirm/message-confirm.d.ts
|
|
4
4
|
/** --- 导出 API --- */
|
|
5
|
-
declare const messageConfirm: MessageConfirm;
|
|
5
|
+
export declare const messageConfirm: MessageConfirm;
|
|
6
6
|
//#endregion
|
|
7
|
-
export { messageConfirm };
|
|
8
7
|
//# sourceMappingURL=message-confirm.d.ts.map
|
|
@@ -2,7 +2,6 @@ import { Notification } from "../../types/notification.js";
|
|
|
2
2
|
import "../../types/index.js";
|
|
3
3
|
//#region src/components/notification/notification.d.ts
|
|
4
4
|
/** --- 导出 API --- */
|
|
5
|
-
declare const notification: Notification;
|
|
5
|
+
export declare const notification: Notification;
|
|
6
6
|
//#endregion
|
|
7
|
-
export { notification };
|
|
8
7
|
//# sourceMappingURL=notification.d.ts.map
|