@veltra/desktop 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/action/action-group.vue.d.ts +1 -1
- package/dist/components/action/action.vue.d.ts +3 -3
- package/dist/components/auto-complete/auto-complete.vue.d.ts +1 -1
- package/dist/components/batch-edit/batch-edit.vue.d.ts +2 -2
- package/dist/components/button/button.vue.d.ts +1 -1
- package/dist/components/cascade/cascade.vue.d.ts +6 -6
- package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
- package/dist/components/code-editor/code-editor.js +2 -0
- package/dist/components/code-editor/code-editor.js.map +1 -1
- package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
- package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
- package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
- package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
- package/dist/components/file-viewer/file-viewer.js +195 -30
- package/dist/components/file-viewer/file-viewer.js.map +1 -1
- package/dist/components/file-viewer/previewers/docx-previewer.js +7 -5
- package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -1
- package/dist/components/file-viewer/previewers/image-previewer.js +6 -6
- package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -1
- package/dist/components/file-viewer/previewers/pdf-previewer.js +5 -3
- package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -1
- package/dist/components/file-viewer/style2.css +387 -136
- package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
- package/dist/components/input/input.vue.d.ts +3 -3
- package/dist/components/multi-select/multi-select.vue.d.ts +4 -4
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +7 -7
- package/dist/components/notification/notification.vue.d.ts +1 -1
- package/dist/components/number-input/number-input.vue.d.ts +1 -1
- package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
- package/dist/components/password-input/password-input.vue.d.ts +1 -1
- package/dist/components/pop-confirm/pop-confirm.vue.d.ts +2 -2
- package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
- package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
- package/dist/components/select/select.vue.d.ts +3 -3
- package/dist/components/table/table.vue.d.ts +2 -2
- package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
- package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
- package/dist/components/tabs/tabs.vue.d.ts +2 -2
- package/dist/components/textarea/textarea.vue.d.ts +4 -4
- package/dist/components/tip/tip.vue.d.ts +2 -2
- package/dist/components/tree/tree.vue.d.ts +6 -6
- package/dist/components/tree-select/tree-select.vue.d.ts +7 -7
- package/package.json +13 -12
- package/src/components/code-editor/code-editor.vue +2 -0
- package/src/components/file-viewer/file-viewer.vue +250 -24
- package/src/components/file-viewer/previewers/docx-previewer.vue +4 -3
- package/src/components/file-viewer/previewers/image-previewer.vue +4 -5
- package/src/components/file-viewer/previewers/pdf-previewer.vue +4 -3
- package/src/components/file-viewer/style.scss +503 -161
|
@@ -20,15 +20,14 @@
|
|
|
20
20
|
v-for="f in normalizedFiles"
|
|
21
21
|
:key="f.id"
|
|
22
22
|
:class="[cls.e('item'), bem.is('active', f.id === activeId)]"
|
|
23
|
+
:title="f.name"
|
|
23
24
|
@click="activate(f.id)"
|
|
24
25
|
>
|
|
25
|
-
<span :class="[cls.e('badge'), cls.em('badge', f.kind)]">
|
|
26
|
-
{{ label(f.kind) }}
|
|
27
|
-
</span>
|
|
28
26
|
<span :class="cls.e('item-meta')">
|
|
29
27
|
<span :class="cls.e('item-name')" :title="f.name">{{ f.name }}</span>
|
|
30
28
|
<span :class="cls.e('item-size')">{{ formatBytes(f.size) }}</span>
|
|
31
29
|
</span>
|
|
30
|
+
<span :class="cls.e('item-kind')">{{ label(f.kind) }}</span>
|
|
32
31
|
</li>
|
|
33
32
|
<li v-if="!normalizedFiles.length" :class="cls.e('list-empty')">
|
|
34
33
|
<u-empty text="暂无文件" :size="32" />
|
|
@@ -42,27 +41,93 @@
|
|
|
42
41
|
<span v-if="activeFile" :class="[cls.e('badge'), cls.em('badge', activeFile.kind)]">
|
|
43
42
|
{{ label(activeFile.kind) }}
|
|
44
43
|
</span>
|
|
45
|
-
<span :class="cls.e('stage-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
<span :class="cls.e('stage-copy')">
|
|
45
|
+
<span :class="cls.e('stage-name')" :title="activeFile?.name">
|
|
46
|
+
{{ activeFile?.name ?? '—' }}
|
|
47
|
+
</span>
|
|
48
|
+
<span v-if="activeFile" :class="cls.e('stage-sub')">
|
|
49
|
+
<span>{{ activeIndexLabel }}</span>
|
|
50
|
+
<span v-if="activeFile.size">{{ formatBytes(activeFile.size) }}</span>
|
|
51
|
+
</span>
|
|
50
52
|
</span>
|
|
51
53
|
</div>
|
|
52
54
|
<div :class="cls.e('stage-actions')">
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
<span :class="cls.e('action-group')">
|
|
56
|
+
<button
|
|
57
|
+
:class="[cls.e('action'), cls.em('action', 'icon')]"
|
|
58
|
+
:disabled="!hasPrev"
|
|
59
|
+
type="button"
|
|
60
|
+
aria-label="上一个"
|
|
61
|
+
title="上一个"
|
|
62
|
+
@click="prev"
|
|
63
|
+
>
|
|
64
|
+
<u-icon :size="15">
|
|
65
|
+
<ArrowLeft />
|
|
66
|
+
</u-icon>
|
|
67
|
+
</button>
|
|
68
|
+
<button
|
|
69
|
+
:class="[cls.e('action'), cls.em('action', 'icon')]"
|
|
70
|
+
:disabled="!hasNext"
|
|
71
|
+
type="button"
|
|
72
|
+
aria-label="下一个"
|
|
73
|
+
title="下一个"
|
|
74
|
+
@click="next"
|
|
75
|
+
>
|
|
76
|
+
<u-icon :size="15">
|
|
77
|
+
<ArrowRight />
|
|
78
|
+
</u-icon>
|
|
79
|
+
</button>
|
|
80
|
+
</span>
|
|
81
|
+
<span v-if="isTransformable" :class="cls.e('action-group')">
|
|
82
|
+
<button
|
|
83
|
+
:class="[cls.e('action'), cls.em('action', 'icon')]"
|
|
84
|
+
:disabled="zoomOutDisabled"
|
|
85
|
+
type="button"
|
|
86
|
+
aria-label="缩小"
|
|
87
|
+
title="缩小"
|
|
88
|
+
@click="zoomOut"
|
|
89
|
+
>
|
|
90
|
+
<u-icon :size="15">
|
|
91
|
+
<ZoomOut />
|
|
92
|
+
</u-icon>
|
|
93
|
+
</button>
|
|
94
|
+
<span :class="cls.e('zoom-value')">{{ zoomPercent }}</span>
|
|
95
|
+
<button
|
|
96
|
+
:class="[cls.e('action'), cls.em('action', 'icon')]"
|
|
97
|
+
:disabled="zoomInDisabled"
|
|
98
|
+
type="button"
|
|
99
|
+
aria-label="放大"
|
|
100
|
+
title="放大"
|
|
101
|
+
@click="zoomIn"
|
|
102
|
+
>
|
|
103
|
+
<u-icon :size="15">
|
|
104
|
+
<ZoomIn />
|
|
105
|
+
</u-icon>
|
|
106
|
+
</button>
|
|
107
|
+
<button
|
|
108
|
+
:class="[cls.e('action'), cls.em('action', 'icon')]"
|
|
109
|
+
:disabled="isTransformReset"
|
|
110
|
+
type="button"
|
|
111
|
+
aria-label="重置视图"
|
|
112
|
+
title="重置视图"
|
|
113
|
+
@click="resetTransform"
|
|
114
|
+
>
|
|
115
|
+
<u-icon :size="15">
|
|
116
|
+
<Refresh />
|
|
117
|
+
</u-icon>
|
|
118
|
+
</button>
|
|
119
|
+
</span>
|
|
59
120
|
<button
|
|
60
121
|
v-if="downloadable && activeFile"
|
|
61
|
-
:class="[cls.e('action'), cls.em('action', 'primary')]"
|
|
122
|
+
:class="[cls.e('action'), cls.em('action', 'primary'), cls.em('action', 'icon')]"
|
|
62
123
|
type="button"
|
|
124
|
+
aria-label="下载"
|
|
125
|
+
title="下载"
|
|
63
126
|
@click="download"
|
|
64
127
|
>
|
|
65
|
-
|
|
128
|
+
<u-icon :size="15">
|
|
129
|
+
<Download />
|
|
130
|
+
</u-icon>
|
|
66
131
|
</button>
|
|
67
132
|
<button
|
|
68
133
|
v-if="isModal"
|
|
@@ -80,14 +145,30 @@
|
|
|
80
145
|
</header>
|
|
81
146
|
<div :class="cls.e('body')">
|
|
82
147
|
<transition name="u-file-viewer-fade" mode="out-in">
|
|
83
|
-
<
|
|
84
|
-
:is="PreviewerMap[activeFile.kind]"
|
|
148
|
+
<div
|
|
85
149
|
v-if="activeFile"
|
|
86
150
|
:key="activeFile.id"
|
|
87
|
-
:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
151
|
+
:class="[
|
|
152
|
+
cls.e('viewport'),
|
|
153
|
+
bem.is('transformable', isTransformable),
|
|
154
|
+
bem.is('pannable', canPan),
|
|
155
|
+
bem.is('dragging', isDragging)
|
|
156
|
+
]"
|
|
157
|
+
@pointerdown="handleViewportPointerDown"
|
|
158
|
+
@pointermove="handleViewportPointerMove"
|
|
159
|
+
@pointerup="handleViewportPointerEnd"
|
|
160
|
+
@pointercancel="handleViewportPointerEnd"
|
|
161
|
+
@dblclick="handleViewportDblclick"
|
|
162
|
+
>
|
|
163
|
+
<component
|
|
164
|
+
:is="PreviewerMap[activeFile.kind]"
|
|
165
|
+
:file="activeFile"
|
|
166
|
+
:max-rows="sheetMaxRows"
|
|
167
|
+
:class="isTransformable ? cls.e('previewer') : undefined"
|
|
168
|
+
:style="previewerStyle"
|
|
169
|
+
@error="handleChildError"
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
91
172
|
<div v-else :class="cls.e('empty')">
|
|
92
173
|
<u-empty text="暂无可预览文件" />
|
|
93
174
|
</div>
|
|
@@ -101,9 +182,26 @@
|
|
|
101
182
|
</template>
|
|
102
183
|
|
|
103
184
|
<script lang="ts" setup>
|
|
104
|
-
import {
|
|
185
|
+
import {
|
|
186
|
+
ArrowLeft,
|
|
187
|
+
ArrowRight,
|
|
188
|
+
Close,
|
|
189
|
+
Download,
|
|
190
|
+
Refresh,
|
|
191
|
+
ZoomIn,
|
|
192
|
+
ZoomOut
|
|
193
|
+
} from '@veltra/icons/normal'
|
|
105
194
|
import { bem, withUnit } from '@veltra/utils'
|
|
106
|
-
import {
|
|
195
|
+
import {
|
|
196
|
+
computed,
|
|
197
|
+
defineAsyncComponent,
|
|
198
|
+
nextTick,
|
|
199
|
+
onBeforeUnmount,
|
|
200
|
+
ref,
|
|
201
|
+
shallowRef,
|
|
202
|
+
watch
|
|
203
|
+
} from 'vue'
|
|
204
|
+
import type { CSSProperties } from 'vue'
|
|
107
205
|
|
|
108
206
|
import type {
|
|
109
207
|
_FileViewerExposed,
|
|
@@ -136,10 +234,29 @@ const emit = defineEmits<FileViewerEmits>()
|
|
|
136
234
|
|
|
137
235
|
const cls = bem('file-viewer')
|
|
138
236
|
|
|
237
|
+
const MIN_SCALE = 0.5
|
|
238
|
+
const MAX_SCALE = 3
|
|
239
|
+
const SCALE_STEP = 0.25
|
|
240
|
+
const TRANSFORMABLE_KINDS = new Set<FileViewerKind>(['image', 'pdf', 'docx'])
|
|
241
|
+
|
|
139
242
|
const activeId = defineModel<string | undefined>('modelValue', { default: undefined })
|
|
140
243
|
const openModel = defineModel<boolean | undefined>('open', { default: undefined })
|
|
141
244
|
|
|
142
245
|
const rootRef = shallowRef<HTMLDivElement>()
|
|
246
|
+
const scale = ref(1)
|
|
247
|
+
const offsetX = ref(0)
|
|
248
|
+
const offsetY = ref(0)
|
|
249
|
+
const isDragging = ref(false)
|
|
250
|
+
|
|
251
|
+
let dragState:
|
|
252
|
+
| {
|
|
253
|
+
pointerId: number
|
|
254
|
+
startX: number
|
|
255
|
+
startY: number
|
|
256
|
+
originX: number
|
|
257
|
+
originY: number
|
|
258
|
+
}
|
|
259
|
+
| undefined
|
|
143
260
|
|
|
144
261
|
const PreviewerMap: Record<FileViewerKind, ReturnType<typeof defineAsyncComponent>> = {
|
|
145
262
|
image: defineAsyncComponent(() => import('./previewers/image-previewer.vue')),
|
|
@@ -185,6 +302,33 @@ const sidebarWidthCss = computed(() => {
|
|
|
185
302
|
/** 是否启用模态模式:只要父组件显式传入 open(含 v-model:open),即进入模态 */
|
|
186
303
|
const isModal = computed(() => openModel.value !== undefined)
|
|
187
304
|
|
|
305
|
+
const activeIndexLabel = computed(() =>
|
|
306
|
+
activeIndex.value >= 0 ? `${activeIndex.value + 1} / ${normalizedFiles.value.length}` : ''
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
const isTransformable = computed(
|
|
310
|
+
() => !!activeFile.value && TRANSFORMABLE_KINDS.has(activeFile.value.kind)
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
const canPan = computed(() => isTransformable.value && scale.value > 1)
|
|
314
|
+
|
|
315
|
+
const zoomPercent = computed(() => `${Math.round(scale.value * 100)}%`)
|
|
316
|
+
|
|
317
|
+
const zoomInDisabled = computed(() => !isTransformable.value || scale.value >= MAX_SCALE)
|
|
318
|
+
|
|
319
|
+
const zoomOutDisabled = computed(() => !isTransformable.value || scale.value <= MIN_SCALE)
|
|
320
|
+
|
|
321
|
+
const isTransformReset = computed(
|
|
322
|
+
() => scale.value === 1 && offsetX.value === 0 && offsetY.value === 0
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
const previewerStyle = computed<CSSProperties | undefined>(() => {
|
|
326
|
+
if (!isTransformable.value) return undefined
|
|
327
|
+
return {
|
|
328
|
+
transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value})`
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
|
|
188
332
|
function label(kind: FileViewerKind): string {
|
|
189
333
|
return FILE_VIEWER_KIND_LABEL[kind]
|
|
190
334
|
}
|
|
@@ -210,6 +354,83 @@ function next() {
|
|
|
210
354
|
if (target) activate(target.id)
|
|
211
355
|
}
|
|
212
356
|
|
|
357
|
+
function normalizeScale(value: number): number {
|
|
358
|
+
const clamped = Math.min(MAX_SCALE, Math.max(MIN_SCALE, value))
|
|
359
|
+
return Math.round(clamped * 100) / 100
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function setScale(value: number) {
|
|
363
|
+
scale.value = normalizeScale(value)
|
|
364
|
+
if (scale.value <= 1) {
|
|
365
|
+
offsetX.value = 0
|
|
366
|
+
offsetY.value = 0
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function zoomIn() {
|
|
371
|
+
if (!isTransformable.value) return
|
|
372
|
+
setScale(scale.value + SCALE_STEP)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function zoomOut() {
|
|
376
|
+
if (!isTransformable.value) return
|
|
377
|
+
setScale(scale.value - SCALE_STEP)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function resetTransform() {
|
|
381
|
+
scale.value = 1
|
|
382
|
+
offsetX.value = 0
|
|
383
|
+
offsetY.value = 0
|
|
384
|
+
dragState = undefined
|
|
385
|
+
isDragging.value = false
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function isInteractiveTarget(target: EventTarget | null): boolean {
|
|
389
|
+
if (!(target instanceof HTMLElement)) return false
|
|
390
|
+
return !!target.closest('button, a, input, textarea, select, video, [contenteditable="true"]')
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function handleViewportPointerDown(e: PointerEvent) {
|
|
394
|
+
if (!canPan.value || e.button !== 0 || isInteractiveTarget(e.target)) return
|
|
395
|
+
|
|
396
|
+
dragState = {
|
|
397
|
+
pointerId: e.pointerId,
|
|
398
|
+
startX: e.clientX,
|
|
399
|
+
startY: e.clientY,
|
|
400
|
+
originX: offsetX.value,
|
|
401
|
+
originY: offsetY.value
|
|
402
|
+
}
|
|
403
|
+
isDragging.value = true
|
|
404
|
+
const el = e.currentTarget as HTMLElement
|
|
405
|
+
el.setPointerCapture(e.pointerId)
|
|
406
|
+
e.preventDefault()
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function handleViewportPointerMove(e: PointerEvent) {
|
|
410
|
+
if (!dragState || dragState.pointerId !== e.pointerId) return
|
|
411
|
+
offsetX.value = dragState.originX + e.clientX - dragState.startX
|
|
412
|
+
offsetY.value = dragState.originY + e.clientY - dragState.startY
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function handleViewportPointerEnd(e: PointerEvent) {
|
|
416
|
+
if (!dragState || dragState.pointerId !== e.pointerId) return
|
|
417
|
+
const el = e.currentTarget as HTMLElement
|
|
418
|
+
if (el.hasPointerCapture(e.pointerId)) {
|
|
419
|
+
el.releasePointerCapture(e.pointerId)
|
|
420
|
+
}
|
|
421
|
+
dragState = undefined
|
|
422
|
+
isDragging.value = false
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function handleViewportDblclick() {
|
|
426
|
+
if (!isTransformable.value) return
|
|
427
|
+
if (scale.value === 1) {
|
|
428
|
+
setScale(2)
|
|
429
|
+
} else {
|
|
430
|
+
resetTransform()
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
213
434
|
async function download() {
|
|
214
435
|
if (!activeFile.value) return
|
|
215
436
|
try {
|
|
@@ -257,6 +478,11 @@ watch(
|
|
|
257
478
|
{ immediate: true }
|
|
258
479
|
)
|
|
259
480
|
|
|
481
|
+
watch(
|
|
482
|
+
() => activeFile.value?.id,
|
|
483
|
+
() => resetTransform()
|
|
484
|
+
)
|
|
485
|
+
|
|
260
486
|
// 模态打开时:锁定 body 滚动 + 让容器获得焦点以便接收 ESC
|
|
261
487
|
let previousBodyOverflow = ''
|
|
262
488
|
let isBodyLocked = false
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<u-scroll tag="div" :class="cls.e('docx')">
|
|
2
|
+
<u-scroll tag="div" :class="cls.e('docx')" v-bind="attrs">
|
|
3
3
|
<div ref="container" :class="cls.e('docx-body')" />
|
|
4
4
|
<div v-if="loading" :class="cls.e('loading')">加载中…</div>
|
|
5
5
|
</u-scroll>
|
|
@@ -7,18 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
<script lang="ts" setup>
|
|
9
9
|
import { bem } from '@veltra/utils'
|
|
10
|
-
import { onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'
|
|
10
|
+
import { onBeforeUnmount, ref, useAttrs, useTemplateRef, watch } from 'vue'
|
|
11
11
|
|
|
12
12
|
import type { FileViewerItem } from '../../../types/file-viewer'
|
|
13
13
|
import { UScroll } from '../../scroll'
|
|
14
14
|
|
|
15
|
-
defineOptions({ name: 'FileViewerDocxPreviewer' })
|
|
15
|
+
defineOptions({ name: 'FileViewerDocxPreviewer', inheritAttrs: false })
|
|
16
16
|
|
|
17
17
|
const props = defineProps<{ file: FileViewerItem }>()
|
|
18
18
|
|
|
19
19
|
const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
|
|
20
20
|
|
|
21
21
|
const cls = bem('file-viewer')
|
|
22
|
+
const attrs = useAttrs()
|
|
22
23
|
|
|
23
24
|
const container = useTemplateRef<HTMLDivElement>('container')
|
|
24
25
|
const loading = ref(true)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="
|
|
2
|
+
<div :class="cls.e('image')" v-bind="attrs">
|
|
3
3
|
<img
|
|
4
4
|
v-if="url"
|
|
5
5
|
:src="url"
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
draggable="false"
|
|
8
8
|
@load="loading = false"
|
|
9
9
|
@error="onImgError"
|
|
10
|
-
@dblclick="actual = !actual"
|
|
11
10
|
/>
|
|
12
11
|
<div v-if="loading" :class="cls.e('loading')">加载中…</div>
|
|
13
12
|
<div v-if="failed" :class="cls.e('loading')">图片加载失败</div>
|
|
@@ -16,22 +15,22 @@
|
|
|
16
15
|
|
|
17
16
|
<script lang="ts" setup>
|
|
18
17
|
import { bem } from '@veltra/utils'
|
|
19
|
-
import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
|
|
18
|
+
import { onBeforeUnmount, ref, shallowRef, useAttrs, watch } from 'vue'
|
|
20
19
|
|
|
21
20
|
import type { FileViewerItem } from '../../../types/file-viewer'
|
|
22
21
|
|
|
23
|
-
defineOptions({ name: 'FileViewerImagePreviewer' })
|
|
22
|
+
defineOptions({ name: 'FileViewerImagePreviewer', inheritAttrs: false })
|
|
24
23
|
|
|
25
24
|
const props = defineProps<{ file: FileViewerItem }>()
|
|
26
25
|
|
|
27
26
|
const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
|
|
28
27
|
|
|
29
28
|
const cls = bem('file-viewer')
|
|
29
|
+
const attrs = useAttrs()
|
|
30
30
|
|
|
31
31
|
const url = shallowRef<string>('')
|
|
32
32
|
const loading = ref(true)
|
|
33
33
|
const failed = ref(false)
|
|
34
|
-
const actual = ref(false)
|
|
35
34
|
|
|
36
35
|
let revoke: (() => void) | undefined
|
|
37
36
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="cls.e('pdf')">
|
|
2
|
+
<div :class="cls.e('pdf')" v-bind="attrs">
|
|
3
3
|
<div v-if="!engine || isLoading" :class="cls.e('loading')">正在加载 PDF 引擎…</div>
|
|
4
4
|
<EmbedPDF v-else-if="pdfUrl" :engine="engine" :plugins="plugins" v-slot="{ activeDocumentId }">
|
|
5
5
|
<DocumentContent
|
|
@@ -36,17 +36,18 @@ import { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/vue'
|
|
|
36
36
|
import { ScrollPluginPackage, Scroller } from '@embedpdf/plugin-scroll/vue'
|
|
37
37
|
import { ViewportPluginPackage, Viewport } from '@embedpdf/plugin-viewport/vue'
|
|
38
38
|
import { bem } from '@veltra/utils'
|
|
39
|
-
import { computed, onBeforeUnmount, shallowRef, watch } from 'vue'
|
|
39
|
+
import { computed, onBeforeUnmount, shallowRef, useAttrs, watch } from 'vue'
|
|
40
40
|
|
|
41
41
|
import type { FileViewerItem } from '../../../types/file-viewer'
|
|
42
42
|
|
|
43
|
-
defineOptions({ name: 'FileViewerPdfPreviewer' })
|
|
43
|
+
defineOptions({ name: 'FileViewerPdfPreviewer', inheritAttrs: false })
|
|
44
44
|
|
|
45
45
|
const props = defineProps<{ file: FileViewerItem }>()
|
|
46
46
|
|
|
47
47
|
const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
|
|
48
48
|
|
|
49
49
|
const cls = bem('file-viewer')
|
|
50
|
+
const attrs = useAttrs()
|
|
50
51
|
|
|
51
52
|
const { engine, isLoading, error } = usePdfiumEngine()
|
|
52
53
|
|