sakura-preview-file 1.0.0

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.
@@ -0,0 +1,1075 @@
1
+ import { defineComponent, computed, openBlock, createElementBlock, normalizeStyle, createElementVNode, Fragment, renderList, unref, ref, watchEffect, watch, mergeModels, useModel, renderSlot, normalizeClass, withDirectives, vModelText, toDisplayString, createCommentVNode, reactive, onMounted, toRefs, createVNode, createBlock, isRef, withCtx, nextTick, useTemplateRef, Teleport, provide, resolveDynamicComponent, normalizeProps, guardReactiveProps, vShow } from "vue";
2
+ import { addUnit, isEmpty, FILE_TYPE, getFileType, base64ToFileUrl, blobToBase64, isBase64File, networkFileUrlToLocalFileUrl, numberToFixed, sleep, extend, base64ToFile, generateId, isPdfFile, isWordFile, isImageFile } from "sakura-utils";
3
+ import VuePdfEmbed from "vue-pdf-embed";
4
+ import "pdfjs-dist";
5
+ import { useThrottleFn, useWindowSize } from "@vueuse/core";
6
+ import { renderAsync } from "docx-preview";
7
+ const _hoisted_1$7 = ["stroke"];
8
+ const _sfc_main$b = /* @__PURE__ */ defineComponent({
9
+ __name: "Circle",
10
+ props: {
11
+ size: { default: 50 },
12
+ color: { default: "#409eff" },
13
+ duration: { default: 2 }
14
+ },
15
+ setup(__props) {
16
+ const props = __props;
17
+ const circleStyle = computed(() => {
18
+ const { size, duration } = props;
19
+ const style = {
20
+ "--circle-size": addUnit(size),
21
+ "--circle-duration": `${duration}s`
22
+ };
23
+ return style;
24
+ });
25
+ return (_ctx, _cache) => {
26
+ return openBlock(), createElementBlock("svg", {
27
+ class: "circular",
28
+ viewBox: "0 0 50 50",
29
+ style: normalizeStyle([circleStyle.value])
30
+ }, [
31
+ createElementVNode("circle", {
32
+ class: "circular__path-inner",
33
+ cx: "25",
34
+ cy: "25",
35
+ r: "20",
36
+ fill: "none",
37
+ stroke: __props.color,
38
+ "stroke-width": "4",
39
+ "stroke-miterlimit": "10"
40
+ }, null, 8, _hoisted_1$7)
41
+ ], 4);
42
+ };
43
+ }
44
+ });
45
+ const Circle_vue_vue_type_style_index_0_scoped_51d1fa15_lang = "";
46
+ const _export_sfc = (sfc, props) => {
47
+ const target = sfc.__vccOpts || sfc;
48
+ for (const [key, val] of props) {
49
+ target[key] = val;
50
+ }
51
+ return target;
52
+ };
53
+ const Circle = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-51d1fa15"]]);
54
+ const _sfc_main$a = /* @__PURE__ */ defineComponent({
55
+ __name: "Dot",
56
+ props: {
57
+ gap: { default: 6 },
58
+ color: { default: "var(--preview-file-primary-color)" },
59
+ size: { default: 10 },
60
+ duration: { default: 1.4 }
61
+ },
62
+ setup(__props) {
63
+ const props = __props;
64
+ const dotStyle = computed(() => {
65
+ const { size, gap, duration, color } = props;
66
+ const style = {
67
+ "--dot-size": addUnit(size),
68
+ "--dot-duration": `${duration}s`,
69
+ "--dot-gap": addUnit(gap),
70
+ "--dot-color": color
71
+ };
72
+ return style;
73
+ });
74
+ return (_ctx, _cache) => {
75
+ return openBlock(), createElementBlock("div", {
76
+ class: "dot",
77
+ style: normalizeStyle([dotStyle.value])
78
+ }, [..._cache[0] || (_cache[0] = [
79
+ createElementVNode("div", { class: "dot__item" }, null, -1),
80
+ createElementVNode("div", { class: "dot__item" }, null, -1),
81
+ createElementVNode("div", { class: "dot__item" }, null, -1)
82
+ ])], 4);
83
+ };
84
+ }
85
+ });
86
+ const Dot_vue_vue_type_style_index_0_scoped_2acd99b5_lang = "";
87
+ const Dot = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__scopeId", "data-v-2acd99b5"]]);
88
+ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
89
+ __name: "Progress",
90
+ props: {
91
+ width: { default: 200 },
92
+ height: { default: 4 },
93
+ background: { default: "#ebeef5" },
94
+ radius: {},
95
+ color: { default: "var(--preview-file-primary-color)" },
96
+ duration: { default: 2 }
97
+ },
98
+ setup(__props) {
99
+ const props = __props;
100
+ const progressStyle = computed(() => {
101
+ const { width, height, color, duration, radius, background } = props;
102
+ const style = {
103
+ "--progress-width": addUnit(width),
104
+ "--progress-height": addUnit(height),
105
+ "--progress-border-radius": addUnit(radius ? radius : Number(height) / 2),
106
+ "--progress-background": background,
107
+ "--progress-duration": `${duration}s`,
108
+ "--progress-color": color
109
+ };
110
+ return style;
111
+ });
112
+ return (_ctx, _cache) => {
113
+ return openBlock(), createElementBlock("div", {
114
+ class: "progress",
115
+ style: normalizeStyle([progressStyle.value])
116
+ }, [..._cache[0] || (_cache[0] = [
117
+ createElementVNode("div", { class: "progress__bar" }, null, -1)
118
+ ])], 4);
119
+ };
120
+ }
121
+ });
122
+ const Progress_vue_vue_type_style_index_0_scoped_3a710001_lang = "";
123
+ const Progress = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["__scopeId", "data-v-3a710001"]]);
124
+ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
125
+ __name: "Cube",
126
+ props: {
127
+ size: { default: 40 },
128
+ color: { default: "var(--preview-file-primary-color)" },
129
+ duration: { default: 1.3 }
130
+ },
131
+ setup(__props) {
132
+ const props = __props;
133
+ const cubeStyle = computed(() => {
134
+ const { size, duration, color } = props;
135
+ const style = {
136
+ "--cube-size": addUnit(size),
137
+ "--cube-color": color,
138
+ "--cube-duration": `${duration}s`
139
+ };
140
+ return style;
141
+ });
142
+ return (_ctx, _cache) => {
143
+ return openBlock(), createElementBlock("div", {
144
+ class: "cube",
145
+ style: normalizeStyle([cubeStyle.value])
146
+ }, [
147
+ (openBlock(), createElementBlock(Fragment, null, renderList(9, (v) => {
148
+ return createElementVNode("div", {
149
+ key: v,
150
+ class: "cube__item"
151
+ });
152
+ }), 64))
153
+ ], 4);
154
+ };
155
+ }
156
+ });
157
+ const Cube_vue_vue_type_style_index_0_scoped_0e6bb9d0_lang = "";
158
+ const Cube = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["__scopeId", "data-v-0e6bb9d0"]]);
159
+ const Loading = {
160
+ Circle,
161
+ Dot,
162
+ Progress,
163
+ Cube
164
+ };
165
+ const defaultNamespace = "sakura";
166
+ const statePrefix = "is-";
167
+ const _bem = (namespace, block, blockSuffix, element, modifier) => {
168
+ let className = namespace + `-${block}`;
169
+ if (blockSuffix) {
170
+ className += "-" + blockSuffix;
171
+ }
172
+ if (element) {
173
+ className += "__" + element;
174
+ }
175
+ if (modifier) {
176
+ className += "--" + modifier;
177
+ }
178
+ return className;
179
+ };
180
+ const useNamespace = (block, namespaceOverrides) => {
181
+ const namespace = unref(namespaceOverrides) ?? defaultNamespace ?? block;
182
+ const b = (blockSuffix = "") => {
183
+ return _bem(namespace, block, blockSuffix, "", "");
184
+ };
185
+ const e = (element = "") => {
186
+ return element ? _bem(namespace, block, "", element, "") : "";
187
+ };
188
+ const m = (modifier = "") => {
189
+ return modifier ? _bem(namespace, block, "", "", modifier) : "";
190
+ };
191
+ const be = (blockSuffix = "", element = "") => {
192
+ return blockSuffix && element ? _bem(namespace, block, blockSuffix, element, "") : "";
193
+ };
194
+ const em = (element = "", modifier = "") => {
195
+ return element && modifier ? _bem(namespace, block, "", element, modifier) : "";
196
+ };
197
+ const bm = (blockSuffix = "", modifier = "") => {
198
+ return blockSuffix && modifier ? _bem(namespace, block, blockSuffix, "", modifier) : "";
199
+ };
200
+ const bem = (blockSuffix = "", element = "", modifier = "") => {
201
+ return blockSuffix && element && modifier ? _bem(namespace, block, blockSuffix, element, modifier) : "";
202
+ };
203
+ const is = (name, ...args) => {
204
+ const state = args.length ? args[0] : true;
205
+ return state && name ? statePrefix + name : "";
206
+ };
207
+ const cssVar = (object) => {
208
+ const style = {};
209
+ for (const k in object) {
210
+ if (object[k]) {
211
+ style[`--${namespace}-${k} `] = object[k];
212
+ }
213
+ }
214
+ return style;
215
+ };
216
+ const cssBlockVar = (block2, object) => {
217
+ const style = {};
218
+ for (const k in object) {
219
+ if (object[k]) {
220
+ style[`--${namespace}-${block2}-${k}`] = object[k];
221
+ }
222
+ }
223
+ return style;
224
+ };
225
+ const cssVarName = (name) => `${namespace}-${name}`;
226
+ const cssBlockVarName = (block2, name) => `${namespace}-${block2}-${name}`;
227
+ const cssVarNameValue = (name) => `var(--${cssVarName(name)})`;
228
+ const cssBlockVarNameValue = (block2, name) => `var(--${cssBlockVarName(block2, name)})`;
229
+ return {
230
+ b,
231
+ e,
232
+ m,
233
+ be,
234
+ em,
235
+ bm,
236
+ bem,
237
+ is,
238
+ cssVar,
239
+ cssBlockVar,
240
+ cssVarName,
241
+ cssBlockVarName,
242
+ cssVarNameValue,
243
+ cssBlockVarNameValue
244
+ };
245
+ };
246
+ const isBlob = (value) => {
247
+ return value instanceof Blob;
248
+ };
249
+ const getConfigurePdfJs = () => {
250
+ return {
251
+ // 启用字体增强属性
252
+ fontExtraProperties: true,
253
+ // 标准字体数据URL
254
+ // standardFontDataUrl,
255
+ // CMap URL用于处理中文等非拉丁字符
256
+ cMapUrl: "./cmaps/",
257
+ cMapPacked: true,
258
+ // 禁用字体警告
259
+ disableFontFace: false,
260
+ // 启用文本选择
261
+ enableXfa: true,
262
+ // 设置字体加载超时时间
263
+ fontLoadTimeout: 3e4,
264
+ // 抑制控制台警告
265
+ verbosity: 0
266
+ };
267
+ };
268
+ const useFileState = (props) => {
269
+ const fileUrl = ref("");
270
+ const changeFileUrl = async () => {
271
+ const { src } = props;
272
+ fileUrl.value = "";
273
+ if (isBlob(src)) {
274
+ fileUrl.value = base64ToFileUrl(await blobToBase64(src));
275
+ } else if (isBase64File(src)) {
276
+ fileUrl.value = base64ToFileUrl(src);
277
+ } else {
278
+ fileUrl.value = props.convertToLocalFileUrl ? await networkFileUrlToLocalFileUrl(src) : src;
279
+ }
280
+ };
281
+ const fileType = computed(() => {
282
+ if (isEmpty(fileUrl.value)) {
283
+ return FILE_TYPE.IMAGE;
284
+ }
285
+ return getFileType(props.convertToLocalFileUrl ? props.src : fileUrl.value);
286
+ });
287
+ watchEffect(changeFileUrl);
288
+ return {
289
+ fileType,
290
+ fileUrl
291
+ };
292
+ };
293
+ const useMouseEvent = () => {
294
+ const isMouseDown = ref(false);
295
+ const startX = ref(0);
296
+ const startY = ref(0);
297
+ const translateX = ref(0);
298
+ const translateY = ref(0);
299
+ const onMouseStart = (e) => {
300
+ isMouseDown.value = true;
301
+ startX.value = e.clientX - translateX.value;
302
+ startY.value = e.clientY - translateY.value;
303
+ window.addEventListener("mousemove", onMouseMove);
304
+ window.addEventListener("mouseup", onMouseLeave);
305
+ };
306
+ const onMouseMove = (e) => {
307
+ if (!isMouseDown.value) {
308
+ return;
309
+ }
310
+ e.preventDefault();
311
+ const x = e.clientX;
312
+ const y = e.clientY;
313
+ translateX.value = x - startX.value;
314
+ translateY.value = y - startY.value;
315
+ };
316
+ const onMouseLeave = () => {
317
+ isMouseDown.value = false;
318
+ window.removeEventListener("mousemove", onMouseMove);
319
+ window.removeEventListener("mouseup", onMouseLeave);
320
+ };
321
+ const resetMouseState = () => {
322
+ startX.value = 0;
323
+ startY.value = 0;
324
+ translateX.value = 0;
325
+ translateY.value = 0;
326
+ isMouseDown.value = false;
327
+ };
328
+ return {
329
+ resetMouseState,
330
+ onMouseStart,
331
+ translateX,
332
+ translateY,
333
+ isMouseDown
334
+ };
335
+ };
336
+ const useWheelEvent = (state = {}) => {
337
+ const { maxScale = 3, minScale = 0.4, stepNum = 0.2, wheelNum = 0.3 } = state;
338
+ const scale = ref(1);
339
+ const scaleText = computed(() => {
340
+ return `${numberToFixed(scale.value * 100, 0)}%`;
341
+ });
342
+ const minusScale = (num = stepNum) => {
343
+ scale.value = Math.max(minScale, scale.value -= num);
344
+ };
345
+ const plusScale = (num = stepNum) => {
346
+ scale.value = (scale.value += num) > maxScale ? maxScale : scale.value;
347
+ };
348
+ const clickMinusScale = useThrottleFn(() => minusScale(), 300);
349
+ const clickPlusScale = useThrottleFn(() => plusScale(), 300);
350
+ const wheelMinusScale = () => {
351
+ minusScale(Math.random() * wheelNum);
352
+ };
353
+ const wheelPlusScale = () => {
354
+ plusScale(Math.random() * wheelNum);
355
+ };
356
+ const onWheel = (e) => {
357
+ if (!e.ctrlKey) {
358
+ return;
359
+ }
360
+ e.preventDefault();
361
+ e.deltaY > 0 ? wheelMinusScale() : wheelPlusScale();
362
+ };
363
+ const resetWheelState = () => {
364
+ scale.value = 1;
365
+ };
366
+ return {
367
+ clickMinusScale,
368
+ clickPlusScale,
369
+ scale,
370
+ scaleText,
371
+ onWheel,
372
+ resetWheelState
373
+ };
374
+ };
375
+ const usePreviewFileTools = (fileUrl, pageNum) => {
376
+ const { onWheel, clickMinusScale, clickPlusScale, resetWheelState, scale, scaleText } = useWheelEvent();
377
+ const { isMouseDown, onMouseStart, resetMouseState, translateX, translateY } = useMouseEvent();
378
+ const wrapperStyle = computed(() => {
379
+ return {
380
+ transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,
381
+ transformOrigin: "center center"
382
+ };
383
+ });
384
+ const resetPdfData = () => {
385
+ resetMouseState();
386
+ resetWheelState();
387
+ };
388
+ watch(() => pageNum == null ? void 0 : pageNum.value, resetPdfData);
389
+ const wrapperRef = ref(null);
390
+ watch(() => fileUrl.value, () => {
391
+ if (!wrapperRef.value) {
392
+ return;
393
+ }
394
+ wrapperRef.value.scrollTop = 0;
395
+ resetPdfData();
396
+ });
397
+ return {
398
+ wrapperRef,
399
+ onWheel,
400
+ clickMinusScale,
401
+ clickPlusScale,
402
+ resetWheelState,
403
+ scale,
404
+ scaleText,
405
+ isMouseDown,
406
+ onMouseStart,
407
+ resetMouseState,
408
+ translateX,
409
+ translateY,
410
+ wrapperStyle
411
+ };
412
+ };
413
+ var EVENT_NAME = /* @__PURE__ */ ((EVENT_NAME2) => {
414
+ EVENT_NAME2["完成"] = "loaded";
415
+ EVENT_NAME2["改变"] = "change";
416
+ EVENT_NAME2["下一页"] = "next";
417
+ EVENT_NAME2["上一页"] = "prev";
418
+ EVENT_NAME2["放大"] = "zoomIn";
419
+ EVENT_NAME2["缩小"] = "zoomOut";
420
+ return EVENT_NAME2;
421
+ })(EVENT_NAME || {});
422
+ const usePreviewPdf = (state) => {
423
+ const nextPageDisabled = computed(() => state.pageNum >= state.total);
424
+ const nextPage = useThrottleFn(() => {
425
+ if (nextPageDisabled.value) {
426
+ return;
427
+ }
428
+ state.pageNum++;
429
+ }, 500);
430
+ const prevPageDisabled = computed(() => state.pageNum <= 1);
431
+ const prevPage = useThrottleFn(() => {
432
+ if (prevPageDisabled.value) {
433
+ return;
434
+ }
435
+ state.pageNum--;
436
+ }, 500);
437
+ const _pageNum = ref(1);
438
+ const onPageNumChange = () => {
439
+ const { total } = state;
440
+ state.pageNum = Math.max(Math.min(_pageNum.value, total), 1);
441
+ };
442
+ watchEffect(() => {
443
+ _pageNum.value = state.pageNum;
444
+ });
445
+ return {
446
+ nextPage,
447
+ nextPageDisabled,
448
+ prevPage,
449
+ prevPageDisabled,
450
+ _pageNum,
451
+ onPageNumChange
452
+ };
453
+ };
454
+ const _hoisted_1$6 = { class: "file-data__tools" };
455
+ const _hoisted_2$1 = { class: "file-data__page" };
456
+ const _hoisted_3$1 = { class: "file-data__text" };
457
+ const _hoisted_4$1 = {
458
+ key: 1,
459
+ class: "file-data__scale"
460
+ };
461
+ const _hoisted_5$1 = { class: "file-data__scale-text" };
462
+ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
463
+ __name: "Tools",
464
+ props: /* @__PURE__ */ mergeModels({
465
+ showTools: { type: Boolean },
466
+ total: {},
467
+ scaleText: {},
468
+ nextPageDisabled: { type: Boolean },
469
+ prevPageDisabled: { type: Boolean },
470
+ enablePagination: { type: Boolean },
471
+ enableMove: { type: Boolean },
472
+ enableScale: { type: Boolean }
473
+ }, {
474
+ "modelValue": {
475
+ type: Number,
476
+ default: 1
477
+ },
478
+ "modelModifiers": {}
479
+ }),
480
+ emits: /* @__PURE__ */ mergeModels([EVENT_NAME.改变, EVENT_NAME.下一页, EVENT_NAME.上一页, EVENT_NAME.放大, EVENT_NAME.缩小], ["update:modelValue"]),
481
+ setup(__props, { emit: __emit }) {
482
+ const emits = __emit;
483
+ const _pageNum = useModel(__props, "modelValue");
484
+ return (_ctx, _cache) => {
485
+ return openBlock(), createElementBlock("div", _hoisted_1$6, [
486
+ renderSlot(_ctx.$slots, "default"),
487
+ __props.showTools ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
488
+ __props.enablePagination ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
489
+ createElementVNode("div", {
490
+ class: normalizeClass(["file-data__arrow file-data__arrow-prev", [__props.prevPageDisabled ? "is-disabled" : ""]]),
491
+ onClick: _cache[0] || (_cache[0] = ($event) => emits(unref(EVENT_NAME).上一页))
492
+ }, null, 2),
493
+ createElementVNode("div", _hoisted_2$1, [
494
+ withDirectives(createElementVNode("input", {
495
+ class: "file-data__input",
496
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => _pageNum.value = $event),
497
+ onBlur: _cache[2] || (_cache[2] = ($event) => emits(unref(EVENT_NAME).改变))
498
+ }, null, 544), [
499
+ [vModelText, _pageNum.value]
500
+ ]),
501
+ createElementVNode("span", _hoisted_3$1, " / " + toDisplayString(__props.total), 1)
502
+ ]),
503
+ createElementVNode("div", {
504
+ class: normalizeClass(["file-data__arrow file-data__arrow-next", [__props.nextPageDisabled ? "is-disabled" : ""]]),
505
+ onClick: _cache[3] || (_cache[3] = ($event) => emits(unref(EVENT_NAME).下一页))
506
+ }, null, 2)
507
+ ], 64)) : createCommentVNode("", true),
508
+ __props.enableScale ? (openBlock(), createElementBlock("div", _hoisted_4$1, [
509
+ createElementVNode("div", {
510
+ class: "file-data__icon file-data__minus",
511
+ onClick: _cache[4] || (_cache[4] = ($event) => emits(unref(EVENT_NAME).缩小))
512
+ }),
513
+ createElementVNode("span", _hoisted_5$1, toDisplayString(__props.scaleText), 1),
514
+ createElementVNode("div", {
515
+ class: "file-data__icon file-data__plus",
516
+ onClick: _cache[5] || (_cache[5] = ($event) => emits(unref(EVENT_NAME).放大))
517
+ })
518
+ ])) : createCommentVNode("", true)
519
+ ], 64)) : createCommentVNode("", true)
520
+ ]);
521
+ };
522
+ }
523
+ });
524
+ const _hoisted_1$5 = { class: "file-data" };
525
+ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
526
+ __name: "Pdf",
527
+ props: /* @__PURE__ */ mergeModels({
528
+ enablePagination: { type: Boolean, default: true },
529
+ enableScale: { type: Boolean, default: true },
530
+ enableMove: { type: Boolean, default: true },
531
+ fileUrl: {},
532
+ showTools: { type: Boolean, default: true }
533
+ }, {
534
+ "modelValue": {
535
+ type: Boolean,
536
+ default: false
537
+ },
538
+ "modelModifiers": {}
539
+ }),
540
+ emits: /* @__PURE__ */ mergeModels([EVENT_NAME.完成], ["update:modelValue"]),
541
+ setup(__props, { emit: __emit }) {
542
+ const props = __props;
543
+ const emits = __emit;
544
+ const pdfLoading = useModel(__props, "modelValue");
545
+ const pdfSource = computed(() => {
546
+ return {
547
+ url: props.fileUrl,
548
+ ...getConfigurePdfJs()
549
+ };
550
+ });
551
+ const getState = () => ({
552
+ pageNum: 1,
553
+ total: 0,
554
+ isError: false
555
+ });
556
+ const state = reactive(getState());
557
+ const pdfHeight = ref(0);
558
+ const onLoadFileSuccess = async (e) => {
559
+ var _a;
560
+ state.total = e._pdfInfo.numPages;
561
+ await nextTick();
562
+ await sleep(50);
563
+ pdfHeight.value = ((_a = document.querySelector(".vue-file-dataembed")) == null ? void 0 : _a.clientHeight) ?? 0;
564
+ };
565
+ const onPdfError = () => {
566
+ state.isError = true;
567
+ };
568
+ const onRendered = async () => {
569
+ await sleep(300);
570
+ pdfLoading.value = false;
571
+ emits(EVENT_NAME.完成);
572
+ };
573
+ const {
574
+ prevPageDisabled,
575
+ nextPageDisabled,
576
+ nextPage,
577
+ _pageNum,
578
+ onPageNumChange,
579
+ prevPage
580
+ } = usePreviewPdf(state);
581
+ const onFileUrlChange = () => {
582
+ pdfLoading.value = true;
583
+ _pageNum.value = 1;
584
+ extend(state, getState());
585
+ };
586
+ watch(() => props.fileUrl, onFileUrlChange);
587
+ onMounted(onFileUrlChange);
588
+ const { pageNum } = toRefs(state);
589
+ const { fileUrl } = toRefs(props);
590
+ const { onWheel, clickMinusScale, clickPlusScale, isMouseDown, scaleText, onMouseStart, wrapperStyle } = usePreviewFileTools(fileUrl, pageNum);
591
+ return (_ctx, _cache) => {
592
+ return openBlock(), createElementBlock("div", _hoisted_1$5, [
593
+ createElementVNode("div", {
594
+ ref: "wrapperRef",
595
+ class: normalizeClass(["file-data__wrapper", [unref(isMouseDown) ? "is-drag" : ""]]),
596
+ onMousedown: _cache[0] || (_cache[0] = ($event) => __props.enableMove && unref(onMouseStart)($event)),
597
+ onWheel: _cache[1] || (_cache[1] = ($event) => __props.enableScale && unref(onWheel)($event))
598
+ }, [
599
+ createElementVNode("div", {
600
+ style: normalizeStyle([unref(wrapperStyle)])
601
+ }, [
602
+ createVNode(unref(VuePdfEmbed), {
603
+ class: "pdf",
604
+ source: pdfSource.value,
605
+ page: __props.enablePagination ? state.pageNum : void 0,
606
+ scale: 1,
607
+ onLoaded: onLoadFileSuccess,
608
+ onRendered,
609
+ onError: onPdfError
610
+ }, null, 8, ["source", "page"])
611
+ ], 4)
612
+ ], 34),
613
+ __props.enablePagination || __props.enableScale || __props.showTools ? (openBlock(), createBlock(_sfc_main$7, {
614
+ key: 0,
615
+ modelValue: unref(_pageNum),
616
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => isRef(_pageNum) ? _pageNum.value = $event : null),
617
+ total: state.total,
618
+ "prev-page-disabled": unref(prevPageDisabled),
619
+ "next-page-disabled": unref(nextPageDisabled),
620
+ "scale-text": unref(scaleText),
621
+ "show-tools": __props.showTools,
622
+ "enable-pagination": __props.enablePagination,
623
+ "enable-scale": __props.enableScale,
624
+ "enable-move": __props.enableMove,
625
+ onChange: unref(onPageNumChange),
626
+ onNext: unref(nextPage),
627
+ onPrev: unref(prevPage),
628
+ onZoomIn: unref(clickPlusScale),
629
+ onZoomOut: unref(clickMinusScale)
630
+ }, {
631
+ default: withCtx(() => [
632
+ renderSlot(_ctx.$slots, "default", { fileDataState: state })
633
+ ]),
634
+ _: 3
635
+ }, 8, ["modelValue", "total", "prev-page-disabled", "next-page-disabled", "scale-text", "show-tools", "enable-pagination", "enable-scale", "enable-move", "onChange", "onNext", "onPrev", "onZoomIn", "onZoomOut"])) : createCommentVNode("", true)
636
+ ]);
637
+ };
638
+ }
639
+ });
640
+ const _hoisted_1$4 = { class: "file-data" };
641
+ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
642
+ __name: "Word",
643
+ props: /* @__PURE__ */ mergeModels({
644
+ enablePagination: { type: Boolean },
645
+ enableScale: { type: Boolean },
646
+ enableMove: { type: Boolean },
647
+ fileUrl: {},
648
+ showTools: { type: Boolean, default: true },
649
+ isLocalFile: { type: Boolean, default: false },
650
+ enableWrapper: { type: Boolean, default: true }
651
+ }, {
652
+ "modelValue": {
653
+ type: Boolean,
654
+ default: false
655
+ },
656
+ "modelModifiers": {}
657
+ }),
658
+ emits: /* @__PURE__ */ mergeModels([EVENT_NAME.完成], ["update:modelValue"]),
659
+ setup(__props, { emit: __emit }) {
660
+ const props = __props;
661
+ const wordLoading = useModel(__props, "modelValue");
662
+ const generateWordFile = async () => {
663
+ const response = await fetch(props.fileUrl);
664
+ const blob = await response.blob();
665
+ return base64ToFile(await blobToBase64(blob), `${generateId()}.docx`);
666
+ };
667
+ const wordFileRef = useTemplateRef("wordFileRef");
668
+ const loadWordFile = async () => {
669
+ try {
670
+ wordLoading.value = true;
671
+ if (wordFileRef.value) {
672
+ wordFileRef.value.innerHTML = "";
673
+ }
674
+ const wordFile = await generateWordFile();
675
+ await renderAsync(
676
+ wordFile,
677
+ wordFileRef.value,
678
+ void 0,
679
+ {
680
+ // 默认样式类
681
+ className: "docx",
682
+ // 启用包装器
683
+ inWrapper: props.enableWrapper,
684
+ // 不忽略宽度
685
+ ignoreWidth: true,
686
+ // 不忽略高度
687
+ ignoreHeight: false,
688
+ // 不忽略字体
689
+ ignoreFonts: true,
690
+ // ✅ 启用分页
691
+ breakPages: true,
692
+ // ✅ 不忽略最后的分页符
693
+ ignoreLastRenderedPageBreak: true,
694
+ // 启用实验性功能
695
+ experimental: false,
696
+ // 清理XML声明
697
+ trimXmlDeclaration: true
698
+ }
699
+ );
700
+ await nextTick();
701
+ } catch (e) {
702
+ console.log("fileUrl---", props.fileUrl);
703
+ console.log("加载word文件失败----", e);
704
+ } finally {
705
+ wordLoading.value = false;
706
+ }
707
+ };
708
+ onMounted(loadWordFile);
709
+ watch(() => props.fileUrl, loadWordFile);
710
+ const { fileUrl } = toRefs(props);
711
+ const { onWheel, clickMinusScale, clickPlusScale, isMouseDown, scaleText, onMouseStart, wrapperStyle } = usePreviewFileTools(fileUrl);
712
+ return (_ctx, _cache) => {
713
+ return openBlock(), createElementBlock("div", _hoisted_1$4, [
714
+ createElementVNode("div", {
715
+ ref: "wrapperRef",
716
+ class: normalizeClass(["file-data__wrapper", [unref(isMouseDown) ? "is-drag" : "", __props.enableWrapper ? "is-enable-wrapper" : ""]]),
717
+ onMousedown: _cache[0] || (_cache[0] = ($event) => __props.enableMove && unref(onMouseStart)($event)),
718
+ onWheel: _cache[1] || (_cache[1] = ($event) => __props.enableScale && unref(onWheel)($event))
719
+ }, [
720
+ createElementVNode("div", {
721
+ ref_key: "wordFileRef",
722
+ ref: wordFileRef,
723
+ style: normalizeStyle([unref(wrapperStyle)])
724
+ }, null, 4)
725
+ ], 34),
726
+ __props.enablePagination || __props.enableScale || __props.showTools ? (openBlock(), createBlock(_sfc_main$7, {
727
+ key: 0,
728
+ "scale-text": unref(scaleText),
729
+ "show-tools": __props.showTools,
730
+ "enable-scale": __props.enableScale,
731
+ "enable-move": __props.enableMove,
732
+ onZoomIn: unref(clickPlusScale),
733
+ onZoomOut: unref(clickMinusScale)
734
+ }, {
735
+ default: withCtx(() => [
736
+ renderSlot(_ctx.$slots, "default")
737
+ ]),
738
+ _: 3
739
+ }, 8, ["scale-text", "show-tools", "enable-scale", "enable-move", "onZoomIn", "onZoomOut"])) : createCommentVNode("", true)
740
+ ]);
741
+ };
742
+ }
743
+ });
744
+ const _sfc_main$4 = {};
745
+ const _hoisted_1$3 = {
746
+ width: "24",
747
+ height: "24",
748
+ fill: "none"
749
+ };
750
+ function _sfc_render$2(_ctx, _cache) {
751
+ return openBlock(), createElementBlock("svg", _hoisted_1$3, [..._cache[0] || (_cache[0] = [
752
+ createElementVNode("circle", {
753
+ cx: "10.5",
754
+ cy: "10.5",
755
+ r: "6.5",
756
+ stroke: "#fff",
757
+ "stroke-width": "2",
758
+ "stroke-linecap": "round",
759
+ "stroke-linejoin": "round"
760
+ }, null, -1),
761
+ createElementVNode("path", {
762
+ d: "M15.5 15.5L20 20",
763
+ stroke: "#fff",
764
+ "stroke-width": "2",
765
+ "stroke-linecap": "round",
766
+ "stroke-linejoin": "round"
767
+ }, null, -1)
768
+ ])]);
769
+ }
770
+ const ZoomIn = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$2]]);
771
+ const _sfc_main$3 = {};
772
+ const _hoisted_1$2 = {
773
+ width: "24",
774
+ height: "24",
775
+ viewBox: "0 0 24 24",
776
+ fill: "none",
777
+ xmlns: "http://www.w3.org/2000/svg"
778
+ };
779
+ function _sfc_render$1(_ctx, _cache) {
780
+ return openBlock(), createElementBlock("svg", _hoisted_1$2, [..._cache[0] || (_cache[0] = [
781
+ createElementVNode("circle", {
782
+ cx: "10.5",
783
+ cy: "10.5",
784
+ r: "6.5",
785
+ stroke: "#fff",
786
+ "stroke-width": "2",
787
+ "stroke-linecap": "round",
788
+ "stroke-linejoin": "round"
789
+ }, null, -1),
790
+ createElementVNode("path", {
791
+ d: "M7.5 10.5H13.5",
792
+ stroke: "#fff",
793
+ "stroke-width": "2",
794
+ "stroke-linecap": "round",
795
+ "stroke-linejoin": "round"
796
+ }, null, -1),
797
+ createElementVNode("path", {
798
+ d: "M15.5 15.5L20 20",
799
+ stroke: "#fff",
800
+ "stroke-width": "2",
801
+ "stroke-linecap": "round",
802
+ "stroke-linejoin": "round"
803
+ }, null, -1)
804
+ ])]);
805
+ }
806
+ const ZoomOut = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$1]]);
807
+ const _sfc_main$2 = {};
808
+ const _hoisted_1$1 = {
809
+ width: "24",
810
+ height: "24",
811
+ viewBox: "0 0 24 24",
812
+ fill: "none",
813
+ xmlns: "http://www.w3.org/2000/svg"
814
+ };
815
+ function _sfc_render(_ctx, _cache) {
816
+ return openBlock(), createElementBlock("svg", _hoisted_1$1, [..._cache[0] || (_cache[0] = [
817
+ createElementVNode("path", {
818
+ d: "M7 7L17 17",
819
+ stroke: "#fff",
820
+ "stroke-width": "2",
821
+ "stroke-linecap": "round",
822
+ "stroke-linejoin": "round"
823
+ }, null, -1),
824
+ createElementVNode("path", {
825
+ d: "M7 17L17 7",
826
+ stroke: "#fff",
827
+ "stroke-width": "2",
828
+ "stroke-linecap": "round",
829
+ "stroke-linejoin": "round"
830
+ }, null, -1)
831
+ ])]);
832
+ }
833
+ const Close = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render]]);
834
+ const _hoisted_1 = { class: "file-data" };
835
+ const _hoisted_2 = { class: "file-data__wrapper is-image" };
836
+ const _hoisted_3 = ["src"];
837
+ const _hoisted_4 = { class: "sakura-preview__image--data" };
838
+ const _hoisted_5 = ["src"];
839
+ const _hoisted_6 = {
840
+ key: 0,
841
+ class: "sakura-preview__image--tools"
842
+ };
843
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
844
+ __name: "Image",
845
+ props: /* @__PURE__ */ mergeModels({
846
+ enablePagination: { type: Boolean },
847
+ enableScale: { type: Boolean },
848
+ enableMove: { type: Boolean },
849
+ fileUrl: {},
850
+ showTools: { type: Boolean }
851
+ }, {
852
+ "modelValue": {
853
+ type: Boolean,
854
+ default: false
855
+ },
856
+ "modelModifiers": {}
857
+ }),
858
+ emits: ["update:modelValue"],
859
+ setup(__props) {
860
+ const props = __props;
861
+ const pdfLoading = useModel(__props, "modelValue");
862
+ const onImageLoad = () => {
863
+ pdfLoading.value = false;
864
+ };
865
+ const { width, height } = useWindowSize();
866
+ const imageWidth = ref(0);
867
+ const imageHeight = ref(0);
868
+ const loadImageSize = async () => {
869
+ const fileUrl2 = props.fileUrl;
870
+ imageWidth.value = 0;
871
+ imageHeight.value = 0;
872
+ const image = new Image();
873
+ image.src = fileUrl2;
874
+ image.onload = () => {
875
+ imageWidth.value = image.width;
876
+ imageHeight.value = image.height;
877
+ };
878
+ };
879
+ onMounted(loadImageSize);
880
+ const dialogImageStyle = computed(() => {
881
+ if (imageWidth.value > width.value) {
882
+ return {
883
+ width: "auto",
884
+ height: addUnit(height.value)
885
+ };
886
+ }
887
+ return {
888
+ width: addUnit(imageWidth.value),
889
+ height: addUnit(imageHeight.value)
890
+ };
891
+ });
892
+ const { fileUrl } = toRefs(props);
893
+ const {
894
+ onWheel,
895
+ onMouseStart,
896
+ isMouseDown,
897
+ clickMinusScale,
898
+ clickPlusScale,
899
+ wrapperStyle
900
+ } = usePreviewFileTools(fileUrl);
901
+ const visible = ref(false);
902
+ const handleVisible = () => {
903
+ visible.value = !visible.value;
904
+ };
905
+ return (_ctx, _cache) => {
906
+ return openBlock(), createElementBlock(Fragment, null, [
907
+ createElementVNode("div", _hoisted_1, [
908
+ createElementVNode("div", _hoisted_2, [
909
+ createElementVNode("img", {
910
+ class: "file-data__image",
911
+ src: unref(fileUrl),
912
+ alt: "",
913
+ onLoad: onImageLoad,
914
+ onClick: handleVisible
915
+ }, null, 40, _hoisted_3)
916
+ ])
917
+ ]),
918
+ (openBlock(), createBlock(Teleport, { to: "body" }, [
919
+ createElementVNode("div", {
920
+ class: normalizeClass(["sakura-preview-dialog", [visible.value ? "is-visible" : ""]])
921
+ }, [
922
+ _cache[2] || (_cache[2] = createElementVNode("div", { class: "sakura-preview__image--mark" }, null, -1)),
923
+ createElementVNode("div", {
924
+ ref: "wrapperRef",
925
+ class: normalizeClass(["sakura-preview__image", [unref(isMouseDown) ? "is-drag" : ""]]),
926
+ onMousedown: _cache[0] || (_cache[0] = ($event) => __props.enableMove && unref(onMouseStart)($event)),
927
+ onWheel: _cache[1] || (_cache[1] = ($event) => __props.enableScale && unref(onWheel)($event))
928
+ }, [
929
+ createElementVNode("div", _hoisted_4, [
930
+ createElementVNode("img", {
931
+ class: "dot-move",
932
+ src: unref(fileUrl),
933
+ style: normalizeStyle([dialogImageStyle.value, unref(wrapperStyle)]),
934
+ alt: ""
935
+ }, null, 12, _hoisted_5)
936
+ ]),
937
+ createElementVNode("div", {
938
+ class: "sakura-preview__image--close",
939
+ onClick: handleVisible
940
+ }, [
941
+ createVNode(Close)
942
+ ]),
943
+ __props.enableScale ? (openBlock(), createElementBlock("div", _hoisted_6, [
944
+ createVNode(ZoomIn, { onClick: unref(clickPlusScale) }, null, 8, ["onClick"]),
945
+ createVNode(ZoomOut, { onClick: unref(clickMinusScale) }, null, 8, ["onClick"])
946
+ ])) : createCommentVNode("", true)
947
+ ], 34)
948
+ ], 2)
949
+ ]))
950
+ ], 64);
951
+ };
952
+ }
953
+ });
954
+ const PreviewFileContextKey = Symbol("PreviewFile");
955
+ const _sfc_main = /* @__PURE__ */ defineComponent({
956
+ __name: "preview-file",
957
+ props: {
958
+ width: { default: "100%" },
959
+ height: { default: "100%" },
960
+ src: {},
961
+ enablePagination: { type: Boolean, default: true },
962
+ enableScale: { type: Boolean, default: true },
963
+ enableMove: { type: Boolean, default: true },
964
+ loadingType: { default: "circle" },
965
+ loadingState: { default: () => ({}) },
966
+ convertToLocalFileUrl: { type: Boolean, default: false },
967
+ themeColor: { default: "#409eff" }
968
+ },
969
+ setup(__props) {
970
+ const props = __props;
971
+ provide(PreviewFileContextKey, props);
972
+ const ns = useNamespace("preview-file");
973
+ const previewFileStyle = computed(() => {
974
+ const { width, height, themeColor } = props;
975
+ const style = {
976
+ "--preview-file-primary-color": themeColor
977
+ };
978
+ if (!isEmpty(width)) {
979
+ style.width = addUnit(width);
980
+ }
981
+ if (!isEmpty(height)) {
982
+ style.height = addUnit(height);
983
+ }
984
+ return style;
985
+ });
986
+ const { fileType, fileUrl } = useFileState(props);
987
+ const loading = ref(true);
988
+ const loadingCom = computed(() => {
989
+ const { loadingType } = props;
990
+ switch (loadingType) {
991
+ case "circle":
992
+ return Loading.Circle;
993
+ case "dot":
994
+ return Loading.Dot;
995
+ case "cube":
996
+ return Loading.Cube;
997
+ case "progress":
998
+ default:
999
+ return Loading.Progress;
1000
+ }
1001
+ });
1002
+ return (_ctx, _cache) => {
1003
+ return openBlock(), createElementBlock("div", {
1004
+ class: normalizeClass(unref(ns).b()),
1005
+ style: normalizeStyle([previewFileStyle.value])
1006
+ }, [
1007
+ !unref(isEmpty)(unref(fileUrl)) ? (openBlock(), createElementBlock("div", {
1008
+ key: 0,
1009
+ class: normalizeClass([unref(ns).e("wrapper")])
1010
+ }, [
1011
+ unref(isPdfFile)(unref(fileType)) ? (openBlock(), createBlock(_sfc_main$6, {
1012
+ key: 0,
1013
+ modelValue: loading.value,
1014
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => loading.value = $event),
1015
+ "file-url": unref(fileUrl),
1016
+ "show-tools": !_ctx.$slots.default,
1017
+ "enable-pagination": __props.enablePagination,
1018
+ "enable-scale": __props.enableScale,
1019
+ "enable-move": __props.enableMove
1020
+ }, {
1021
+ default: withCtx(({ fileDataState }) => [
1022
+ renderSlot(_ctx.$slots, "default", {
1023
+ fileState: fileDataState,
1024
+ data: props
1025
+ })
1026
+ ]),
1027
+ _: 3
1028
+ }, 8, ["modelValue", "file-url", "show-tools", "enable-pagination", "enable-scale", "enable-move"])) : createCommentVNode("", true),
1029
+ unref(isWordFile)(unref(fileType)) ? (openBlock(), createBlock(_sfc_main$5, {
1030
+ key: 1,
1031
+ modelValue: loading.value,
1032
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => loading.value = $event),
1033
+ "file-url": unref(fileUrl),
1034
+ "show-tools": !_ctx.$slots.default,
1035
+ "enable-pagination": __props.enablePagination,
1036
+ "enable-scale": __props.enableScale,
1037
+ "enable-move": __props.enableMove,
1038
+ "is-local-file": __props.convertToLocalFileUrl
1039
+ }, null, 8, ["modelValue", "file-url", "show-tools", "enable-pagination", "enable-scale", "enable-move", "is-local-file"])) : unref(isImageFile)(unref(fileType)) ? (openBlock(), createBlock(_sfc_main$1, {
1040
+ key: 2,
1041
+ modelValue: loading.value,
1042
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => loading.value = $event),
1043
+ "file-url": unref(fileUrl),
1044
+ "show-tools": !_ctx.$slots.default,
1045
+ "enable-scale": __props.enableScale,
1046
+ "enable-move": __props.enableMove
1047
+ }, null, 8, ["modelValue", "file-url", "show-tools", "enable-scale", "enable-move"])) : createCommentVNode("", true)
1048
+ ], 2)) : createCommentVNode("", true),
1049
+ withDirectives(createElementVNode("div", {
1050
+ class: normalizeClass([unref(ns).e("loading")])
1051
+ }, [
1052
+ createElementVNode("div", {
1053
+ class: normalizeClass([unref(ns).e("loading-spinner")])
1054
+ }, [
1055
+ (openBlock(), createBlock(resolveDynamicComponent(loadingCom.value), normalizeProps(guardReactiveProps({
1056
+ ...__props.loadingState,
1057
+ color: __props.themeColor
1058
+ })), null, 16))
1059
+ ], 2)
1060
+ ], 2), [
1061
+ [vShow, loading.value]
1062
+ ])
1063
+ ], 6);
1064
+ };
1065
+ }
1066
+ });
1067
+ const PreviewFileComponent = _sfc_main;
1068
+ PreviewFileComponent.install = (app) => {
1069
+ app.component("PreviewFile", _sfc_main);
1070
+ app.component("Loading", Loading);
1071
+ };
1072
+ export {
1073
+ Loading,
1074
+ PreviewFileComponent as default
1075
+ };