@eternalheart/vue-file-preview 1.3.13 → 1.4.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.
- package/lib/FilePreviewContent.vue.d.ts +3 -0
- package/lib/FilePreviewContent.vue.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.vue.d.ts +5 -0
- package/lib/FilePreviewEmbed.vue.d.ts.map +1 -1
- package/lib/FilePreviewModal.vue.d.ts.map +1 -1
- package/lib/chunks/{RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs → RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs} +9 -9
- package/lib/chunks/RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs.map +1 -0
- package/lib/chunks/index-8ETACZ6T.mjs +267 -0
- package/lib/chunks/index-8ETACZ6T.mjs.map +1 -0
- package/lib/chunks/{index-DyipSoKh.mjs → index-B-LW-GJO.mjs} +24 -21
- package/lib/chunks/index-B-LW-GJO.mjs.map +1 -0
- package/lib/chunks/{index-Cjnw238n.mjs → index-BT9hXA6r.mjs} +3 -3
- package/lib/chunks/{index-Cjnw238n.mjs.map → index-BT9hXA6r.mjs.map} +1 -1
- package/lib/chunks/{index-CyrFxdIJ.mjs → index-BcKI75RW.mjs} +4 -4
- package/lib/chunks/{index-CyrFxdIJ.mjs.map → index-BcKI75RW.mjs.map} +1 -1
- package/lib/chunks/{index-Ctd0JQ6K.mjs → index-By6vGdnK.mjs} +3 -3
- package/lib/chunks/{index-Ctd0JQ6K.mjs.map → index-By6vGdnK.mjs.map} +1 -1
- package/lib/chunks/{index-BHi1WRQZ.mjs → index-C-Mu0fvH.mjs} +3 -3
- package/lib/chunks/{index-BHi1WRQZ.mjs.map → index-C-Mu0fvH.mjs.map} +1 -1
- package/lib/chunks/{index-DT9dPz8I.mjs → index-CGSq7-Q-.mjs} +3 -3
- package/lib/chunks/{index-DT9dPz8I.mjs.map → index-CGSq7-Q-.mjs.map} +1 -1
- package/lib/chunks/{index-DTtHrdBl.mjs → index-CWpU8prZ.mjs} +11 -11
- package/lib/chunks/index-CWpU8prZ.mjs.map +1 -0
- package/lib/chunks/index-CYa_XneH.mjs +303 -0
- package/lib/chunks/index-CYa_XneH.mjs.map +1 -0
- package/lib/chunks/{index-CYt6tpQC.mjs → index-CbJkkdEB.mjs} +9 -9
- package/lib/chunks/index-CbJkkdEB.mjs.map +1 -0
- package/lib/chunks/{index-DEoR7aDN.mjs → index-CoITLoYL.mjs} +3 -3
- package/lib/chunks/{index-DEoR7aDN.mjs.map → index-CoITLoYL.mjs.map} +1 -1
- package/lib/chunks/{index-Oxwizt6-.mjs → index-D9QQL7U6.mjs} +605 -557
- package/lib/chunks/index-D9QQL7U6.mjs.map +1 -0
- package/lib/chunks/{index-BvegaxQF.mjs → index-DAdO2lkI.mjs} +32 -32
- package/lib/chunks/index-DAdO2lkI.mjs.map +1 -0
- package/lib/chunks/{index-GGGDPwxX.mjs → index-DGqDJMot.mjs} +3 -3
- package/lib/chunks/{index-GGGDPwxX.mjs.map → index-DGqDJMot.mjs.map} +1 -1
- package/lib/chunks/{index-DIe0vHlt.mjs → index-DTyG1Wzp.mjs} +8 -8
- package/lib/chunks/index-DTyG1Wzp.mjs.map +1 -0
- package/lib/chunks/{index-Dw1U4YqT.mjs → index-DglMeKJM.mjs} +36 -38
- package/lib/chunks/{index-Dw1U4YqT.mjs.map → index-DglMeKJM.mjs.map} +1 -1
- package/lib/chunks/{index-B2lqsdp7.mjs → index-OOoaYpkl.mjs} +3 -3
- package/lib/chunks/{index-B2lqsdp7.mjs.map → index-OOoaYpkl.mjs.map} +1 -1
- package/lib/chunks/{index-DBw8tvyb.mjs → index-jcgLzyDR.mjs} +4 -4
- package/lib/chunks/{index-DBw8tvyb.mjs.map → index-jcgLzyDR.mjs.map} +1 -1
- package/lib/chunks/{index-BTW1xwB9.mjs → index-nFIiu1lA.mjs} +3 -3
- package/lib/chunks/{index-BTW1xwB9.mjs.map → index-nFIiu1lA.mjs.map} +1 -1
- package/lib/index.cjs +40 -32
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +1 -1
- package/lib/renderers/Audio/index.vue.d.ts.map +1 -1
- package/lib/renderers/Docx/index.vue.d.ts.map +1 -1
- package/lib/renderers/Epub/index.vue.d.ts.map +1 -1
- package/lib/renderers/Markdown/index.vue.d.ts.map +1 -1
- package/lib/renderers/Pdf/index.vue.d.ts +3 -0
- package/lib/renderers/Pdf/index.vue.d.ts.map +1 -1
- package/lib/renderers/Pdf/toolbar.d.ts +5 -0
- package/lib/renderers/Pdf/toolbar.d.ts.map +1 -1
- package/lib/renderers/RendererError.vue.d.ts.map +1 -1
- package/lib/renderers/Subtitle/index.vue.d.ts.map +1 -1
- package/lib/renderers/Text/index.vue.d.ts.map +1 -1
- package/lib/renderers/Xml/index.vue.d.ts.map +1 -1
- package/package.json +1 -1
- package/lib/chunks/RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs.map +0 -1
- package/lib/chunks/index-BvegaxQF.mjs.map +0 -1
- package/lib/chunks/index-CYt6tpQC.mjs.map +0 -1
- package/lib/chunks/index-DIe0vHlt.mjs.map +0 -1
- package/lib/chunks/index-DTtHrdBl.mjs.map +0 -1
- package/lib/chunks/index-DX_NBPzS.mjs +0 -248
- package/lib/chunks/index-DX_NBPzS.mjs.map +0 -1
- package/lib/chunks/index-DyipSoKh.mjs.map +0 -1
- package/lib/chunks/index-Oxwizt6-.mjs.map +0 -1
- package/lib/chunks/index-Zwl5J-Uw.mjs +0 -166
- package/lib/chunks/index-Zwl5J-Uw.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import { u as
|
|
1
|
+
import { defineComponent as g, ref as n, computed as x, watch as d, openBlock as e, createElementBlock as o, createElementVNode as m, createBlock as _, normalizeClass as h, toDisplayString as k } from "vue";
|
|
2
|
+
import { u as b, a as T, c as B, y as E, E as N, _ as H } from "./index-D9QQL7U6.mjs";
|
|
3
3
|
import { codeToHtml as W } from "shiki";
|
|
4
|
-
import { _ as C } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
4
|
+
import { _ as C } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
5
5
|
const L = {
|
|
6
6
|
key: 0,
|
|
7
7
|
class: "vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full"
|
|
@@ -12,7 +12,7 @@ const L = {
|
|
|
12
12
|
key: 3,
|
|
13
13
|
class: "vfp-w-full vfp-h-full vfp-overflow-auto",
|
|
14
14
|
style: { background: "var(--fp-code-bg)" }
|
|
15
|
-
}, z = ["innerHTML"], D = /* @__PURE__ */
|
|
15
|
+
}, z = ["innerHTML"], D = /* @__PURE__ */ g({
|
|
16
16
|
__name: "index",
|
|
17
17
|
props: {
|
|
18
18
|
url: {},
|
|
@@ -21,10 +21,10 @@ const L = {
|
|
|
21
21
|
htmlPreview: { type: Boolean, default: !1 }
|
|
22
22
|
},
|
|
23
23
|
setup(l) {
|
|
24
|
-
const u = l, { t: w } =
|
|
24
|
+
const u = l, { t: w } = b(), y = T(), c = B(), r = n(""), t = n(""), v = n(!0), s = n(null), f = x(() => E(u.fileName)), i = async () => {
|
|
25
25
|
v.value = !0, s.value = null;
|
|
26
26
|
try {
|
|
27
|
-
const a = await N(u.url, { fetcher:
|
|
27
|
+
const a = await N(u.url, { fetcher: y.value });
|
|
28
28
|
if (r.value = a, f.value !== "text")
|
|
29
29
|
try {
|
|
30
30
|
t.value = await W(a, {
|
|
@@ -46,7 +46,7 @@ const L = {
|
|
|
46
46
|
r.value && f.value !== "text" && i();
|
|
47
47
|
}), (a, p) => v.value ? (e(), o("div", L, [...p[0] || (p[0] = [
|
|
48
48
|
m("div", { class: "vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin" }, null, -1)
|
|
49
|
-
])])) : s.value ? (e(),
|
|
49
|
+
])])) : s.value ? (e(), _(C, {
|
|
50
50
|
key: 1,
|
|
51
51
|
message: s.value
|
|
52
52
|
}, null, 8, ["message"])) : l.htmlPreview && f.value === "html" ? (e(), o("div", M, [
|
|
@@ -63,12 +63,12 @@ const L = {
|
|
|
63
63
|
innerHTML: t.value
|
|
64
64
|
}, null, 10, z)) : (e(), o("pre", {
|
|
65
65
|
key: 0,
|
|
66
|
-
class: h(["vfp-
|
|
67
|
-
},
|
|
66
|
+
class: h(["vfp-py-6 vfp-px-4 vfp-text-fg-primary vfp-font-mono vfp-text-sm", l.wordWrap ? "vfp-whitespace-pre-wrap vfp-break-words" : "vfp-whitespace-pre"])
|
|
67
|
+
}, k(r.value), 3))
|
|
68
68
|
]));
|
|
69
69
|
}
|
|
70
|
-
}), V = /* @__PURE__ */ H(D, [["__scopeId", "data-v-
|
|
70
|
+
}), V = /* @__PURE__ */ H(D, [["__scopeId", "data-v-e26ca32c"]]);
|
|
71
71
|
export {
|
|
72
72
|
V as default
|
|
73
73
|
};
|
|
74
|
-
//# sourceMappingURL=index-
|
|
74
|
+
//# sourceMappingURL=index-CWpU8prZ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CWpU8prZ.mjs","sources":["../../src/renderers/Text/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport { getLanguageFromFileName, fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { codeToHtml } from 'shiki';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport { useResolvedTheme } from '../../composables/useResolvedTheme';\nimport RendererError from '../RendererError.vue';\n\nconst props = withDefaults(defineProps<{\n url: string;\n fileName: string;\n wordWrap?: boolean;\n htmlPreview?: boolean;\n}>(), {\n wordWrap: true,\n htmlPreview: false,\n});\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\nconst resolvedTheme = useResolvedTheme();\n\nconst content = ref<string>('');\nconst highlighted = ref<string>('');\nconst loading = ref(true);\nconst error = ref<string | null>(null);\n\nconst language = computed(() => getLanguageFromFileName(props.fileName));\n\nconst loadText = async () => {\n loading.value = true;\n error.value = null;\n try {\n const text = await fetchTextUtf8(props.url, { fetcher: fetcher.value });\n content.value = text;\n\n if (language.value !== 'text') {\n try {\n highlighted.value = await codeToHtml(text, {\n lang: language.value,\n theme: resolvedTheme.value === 'light' ? 'github-light' : 'dark-plus',\n });\n } catch {\n highlighted.value = '';\n }\n } else {\n highlighted.value = '';\n }\n } catch (err) {\n console.error(err);\n error.value = t.value('text.load_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, loadText, { immediate: true });\nwatch(resolvedTheme, () => {\n // 主题切换时重新高亮\n if (content.value && language.value !== 'text') {\n loadText();\n }\n});\n</script>\n\n<template>\n <div v-if=\"loading\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <RendererError v-else-if=\"error\" :message=\"error\" />\n\n <!-- HTML 预览模式 -->\n <div v-else-if=\"htmlPreview && language === 'html'\" class=\"vfp-w-full vfp-h-full vfp-bg-surface-toolbar\">\n <iframe\n :srcdoc=\"content\"\n sandbox=\"allow-same-origin\"\n class=\"vfp-w-full vfp-h-full vfp-border-0\"\n :title=\"fileName\"\n />\n </div>\n\n <!-- 源码模式 -->\n <div v-else class=\"vfp-w-full vfp-h-full vfp-overflow-auto\" style=\"background: var(--fp-code-bg);\">\n <pre\n v-if=\"!highlighted\"\n class=\"vfp-py-6 vfp-px-4 vfp-text-fg-primary vfp-font-mono vfp-text-sm\"\n :class=\"wordWrap ? 'vfp-whitespace-pre-wrap vfp-break-words' : 'vfp-whitespace-pre'\"\n >{{ content }}</pre>\n <div v-else class=\"shiki-wrapper\" :class=\"{ 'no-wrap': !wordWrap }\" v-html=\"highlighted\" />\n </div>\n</template>\n\n<style scoped>\n.shiki-wrapper :deep(pre) {\n margin: 0;\n padding: 1.5rem 1.5rem 1.5rem 0;\n background: transparent !important;\n font-size: 0.875rem;\n overflow-x: auto;\n}\n.shiki-wrapper :deep(code) {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n counter-reset: line;\n}\n.shiki-wrapper :deep(code .line) {\n counter-increment: line;\n display: inline-block;\n width: 100%;\n padding-left: 4.5em;\n text-indent: -4.5em;\n}\n.shiki-wrapper :deep(code .line::before) {\n content: counter(line);\n display: inline-block;\n width: 3em;\n padding-right: 1em;\n margin-right: 0.5em;\n text-align: right;\n color: var(--fp-fg-disabled);\n user-select: none;\n border-right: 1px solid var(--fp-line);\n}\n.shiki-wrapper.no-wrap :deep(code) {\n white-space: pre;\n word-break: normal;\n overflow-wrap: normal;\n}\n.shiki-wrapper:not(.no-wrap) :deep(code) {\n white-space: pre-wrap;\n word-break: break-word;\n overflow-wrap: break-word;\n}\n</style>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","resolvedTheme","useResolvedTheme","content","ref","highlighted","loading","error","language","computed","getLanguageFromFileName","loadText","text","fetchTextUtf8","codeToHtml","err","watch","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_createBlock","RendererError","_hoisted_2","_hoisted_4","_normalizeClass"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AASA,UAAMA,IAAQC,GAUR,EAAE,GAAAC,EAAA,IAAMC,EAAA,GACRC,IAAUC,EAAA,GACVC,IAAgBC,EAAA,GAEhBC,IAAUC,EAAY,EAAE,GACxBC,IAAcD,EAAY,EAAE,GAC5BE,IAAUF,EAAI,EAAI,GAClBG,IAAQH,EAAmB,IAAI,GAE/BI,IAAWC,EAAS,MAAMC,EAAwBf,EAAM,QAAQ,CAAC,GAEjEgB,IAAW,YAAY;AAC3B,MAAAL,EAAQ,QAAQ,IAChBC,EAAM,QAAQ;AACd,UAAI;AACF,cAAMK,IAAO,MAAMC,EAAclB,EAAM,KAAK,EAAE,SAASI,EAAQ,OAAO;AAGtE,YAFAI,EAAQ,QAAQS,GAEZJ,EAAS,UAAU;AACrB,cAAI;AACF,YAAAH,EAAY,QAAQ,MAAMS,EAAWF,GAAM;AAAA,cACzC,MAAMJ,EAAS;AAAA,cACf,OAAOP,EAAc,UAAU,UAAU,iBAAiB;AAAA,YAAA,CAC3D;AAAA,UACH,QAAQ;AACN,YAAAI,EAAY,QAAQ;AAAA,UACtB;AAAA;AAEA,UAAAA,EAAY,QAAQ;AAAA,MAExB,SAASU,GAAK;AACZ,gBAAQ,MAAMA,CAAG,GACjBR,EAAM,QAAQV,EAAE,MAAM,kBAAkB;AAAA,MAC1C,UAAA;AACE,QAAAS,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAEA,WAAAU,EAAM,MAAMrB,EAAM,KAAKgB,GAAU,EAAE,WAAW,IAAM,GACpDK,EAAMf,GAAe,MAAM;AAEzB,MAAIE,EAAQ,SAASK,EAAS,UAAU,UACtCG,EAAA;AAAA,IAEJ,CAAC,aAIYL,EAAA,SAAXW,EAAA,GAAAC,EAIM,OAJNC,GAIM,CAAA,GAAAC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA;AAAA,MAHJC,EAEE,OAAA,EADA,OAAM,oHAAA,GAAmH,MAAA,EAAA;AAAA,IAAA,QAInGd,EAAA,cAA1Be,EAAoDC,GAAA;AAAA;MAAlB,SAAShB,EAAA;AAAA,IAAA,4BAG3BX,EAAA,eAAeY,EAAA,UAAQ,UAAvCS,EAAA,GAAAC,EAOM,OAPNM,GAOM;AAAA,MANJH,EAKE,UAAA;AAAA,QAJC,QAAQlB,EAAA;AAAA,QACT,SAAQ;AAAA,QACR,OAAM;AAAA,QACL,OAAOP,EAAA;AAAA,MAAA;WAKZqB,EAAA,GAAAC,EAOM,OAPNO,GAOM;AAAA,MALKpB,EAAA,cAITa,EAA2F,OAAA;AAAA;QAA/E,OAAKQ,EAAA,CAAC,iBAAe,EAAA,WAAA,CAAuB9B,EAAA,SAAA,CAAQ,CAAA;AAAA,QAAI,WAAQS,EAAA;AAAA,MAAA,yBAL5Ea,EAIoB,OAAA;AAAA;QAFlB,OAAKQ,EAAA,CAAC,mEACE9B,EAAA,WAAQ,4CAAA,oBAAA,CAAA;AAAA,MAAA,KACdO,EAAA,KAAO,GAAA,CAAA;AAAA;;;"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { defineComponent as U, ref as w, watch as k, nextTick as L, onMounted as G, onBeforeUnmount as Q, openBlock as O, createElementBlock as A, normalizeStyle as B, createElementVNode as g, toDisplayString as Y, unref as z, createVNode as Z, createCommentVNode as I, createBlock as D } from "vue";
|
|
2
|
+
import { B as ee, u as te } from "./index-D9QQL7U6.mjs";
|
|
3
|
+
import * as N from "pdfjs-dist/build/pdf.mjs";
|
|
4
|
+
import { _ as ne } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
5
|
+
import { X as re } from "lucide-vue-next";
|
|
6
|
+
const le = { class: "vfp-flex vfp-items-center vfp-justify-between vfp-px-4 vfp-py-3 vfp-border-b vfp-border-line-weak vfp-flex-shrink-0" }, ae = { class: "vfp-text-fg-primary vfp-font-medium vfp-text-sm" }, ie = ["innerHTML"], se = {
|
|
7
|
+
key: 1,
|
|
8
|
+
class: "vfp-flex vfp-items-center vfp-justify-center vfp-min-h-screen"
|
|
9
|
+
}, oe = {
|
|
10
|
+
key: 2,
|
|
11
|
+
class: "vfp-flex vfp-flex-col vfp-items-center"
|
|
12
|
+
}, pe = /* @__PURE__ */ U({
|
|
13
|
+
__name: "index",
|
|
14
|
+
props: {
|
|
15
|
+
url: {},
|
|
16
|
+
zoom: {},
|
|
17
|
+
currentPage: {},
|
|
18
|
+
showOutline: { type: Boolean }
|
|
19
|
+
},
|
|
20
|
+
emits: ["pageChange", "totalPagesChange", "pageWidthChange", "toggleOutline"],
|
|
21
|
+
setup(C, { emit: _ }) {
|
|
22
|
+
ee(N);
|
|
23
|
+
const y = C, h = _, { t: R } = te(), E = w(0), b = w(null), M = w(!0), v = w([]), T = w(null), P = w(null), f = w(null), S = /* @__PURE__ */ new Map();
|
|
24
|
+
let i = null;
|
|
25
|
+
const u = /* @__PURE__ */ new Map();
|
|
26
|
+
let c = null;
|
|
27
|
+
const q = async (t, e, n = 0) => {
|
|
28
|
+
var r, a, l;
|
|
29
|
+
for (let o = 0; o < t.length; o++) {
|
|
30
|
+
const s = t[o], p = `${s.title}-${o}-${n}`;
|
|
31
|
+
try {
|
|
32
|
+
let d = null;
|
|
33
|
+
const m = s.dest;
|
|
34
|
+
if (typeof m == "string") {
|
|
35
|
+
const x = await ((r = e.getDestination) == null ? void 0 : r.call(e, m));
|
|
36
|
+
x && x[0] && typeof x[0] == "object" && (d = await ((a = e.getPageIndex) == null ? void 0 : a.call(e, x[0])) + 1);
|
|
37
|
+
} else Array.isArray(m) && m[0] && typeof m[0] == "object" && (d = await ((l = e.getPageIndex) == null ? void 0 : l.call(e, m[0])) + 1);
|
|
38
|
+
d !== null && d > 0 && S.set(p, d), s.items && s.items.length > 0 && await q(s.items, e, n + 1);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, K = (t) => {
|
|
43
|
+
let e = null, n = 1 / 0;
|
|
44
|
+
S.forEach((r, a) => {
|
|
45
|
+
if (r <= t) {
|
|
46
|
+
const l = t - r;
|
|
47
|
+
l < n && (n = l, e = a);
|
|
48
|
+
}
|
|
49
|
+
}), e !== T.value && (T.value = e);
|
|
50
|
+
}, W = async (t, e) => {
|
|
51
|
+
var n, r, a;
|
|
52
|
+
if (!(!i || !f.value))
|
|
53
|
+
try {
|
|
54
|
+
let l;
|
|
55
|
+
if (typeof t == "string") {
|
|
56
|
+
const p = await ((n = i.getDestination) == null ? void 0 : n.call(i, t));
|
|
57
|
+
if (p && p[0])
|
|
58
|
+
l = await ((r = i.getPageIndex) == null ? void 0 : r.call(i, p[0])) + 1;
|
|
59
|
+
else
|
|
60
|
+
return;
|
|
61
|
+
} else if (Array.isArray(t) && t[0])
|
|
62
|
+
l = await ((a = i.getPageIndex) == null ? void 0 : a.call(i, t[0])) + 1;
|
|
63
|
+
else
|
|
64
|
+
return;
|
|
65
|
+
T.value = e;
|
|
66
|
+
const s = f.value.querySelectorAll("[data-page-number]")[l - 1];
|
|
67
|
+
s && s.scrollIntoView({ behavior: "smooth", block: "start" }), setTimeout(() => h("toggleOutline"), 300);
|
|
68
|
+
} catch (l) {
|
|
69
|
+
console.error("大纲跳转失败:", l);
|
|
70
|
+
}
|
|
71
|
+
}, $ = (t, e = 0) => t.map((n, r) => {
|
|
72
|
+
const a = `${n.title}-${r}-${e}`, o = T.value === a ? "vfp-bg-surface-2 vfp-text-fg-primary vfp-font-medium" : "vfp-text-fg-secondary hover:vfp-text-fg-primary hover:vfp-bg-surface-2";
|
|
73
|
+
let s = `<li style="margin-left: ${e > 0 ? 16 : 0}px;">
|
|
74
|
+
<button
|
|
75
|
+
data-outline-key="${a}"
|
|
76
|
+
data-outline-dest="${JSON.stringify(n.dest).replace(/"/g, """)}"
|
|
77
|
+
class="vfp-w-full vfp-text-left vfp-py-2 vfp-px-3 vfp-text-sm vfp-rounded vfp-transition-all vfp-truncate ${o}"
|
|
78
|
+
title="${n.title}"
|
|
79
|
+
>
|
|
80
|
+
${n.title}
|
|
81
|
+
</button>`;
|
|
82
|
+
return n.items && n.items.length > 0 && (s += `<ul>${$(n.items, e + 1)}</ul>`), s += "</li>", s;
|
|
83
|
+
}).join(""), X = async (t, e) => {
|
|
84
|
+
if (!i) return;
|
|
85
|
+
const n = u.get(t);
|
|
86
|
+
if (!(!n || n.rendering)) {
|
|
87
|
+
n.rendering = !0;
|
|
88
|
+
try {
|
|
89
|
+
const r = await i.getPage(t), a = r.getViewport({ scale: e }), l = document.createElement("canvas");
|
|
90
|
+
l.width = a.width, l.height = a.height, l.style.maxWidth = "100%", l.style.height = "auto", l.style.borderRadius = "0", l.style.display = "block";
|
|
91
|
+
const o = l.getContext("2d");
|
|
92
|
+
if (!o) return;
|
|
93
|
+
const s = r.render({ canvasContext: o, viewport: a });
|
|
94
|
+
if (n.renderTask = s, await s.promise, t === 1) {
|
|
95
|
+
const p = r.getViewport({ scale: 1 });
|
|
96
|
+
h("pageWidthChange", p.width);
|
|
97
|
+
}
|
|
98
|
+
n.element.innerHTML = "", n.element.appendChild(l), n.rendered = !0;
|
|
99
|
+
} catch (r) {
|
|
100
|
+
(r == null ? void 0 : r.name) !== "RenderingCancelledException" && console.error(`渲染页面 ${t} 失败:`, r);
|
|
101
|
+
} finally {
|
|
102
|
+
n.rendering = !1, n.renderTask = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}, V = (t) => {
|
|
106
|
+
const e = u.get(t);
|
|
107
|
+
if (!e) return;
|
|
108
|
+
e.renderTask && (e.renderTask.cancel(), e.renderTask = null);
|
|
109
|
+
const n = e.element.querySelector("canvas");
|
|
110
|
+
if (n) {
|
|
111
|
+
const r = n.getContext("2d");
|
|
112
|
+
r && r.clearRect(0, 0, n.width, n.height);
|
|
113
|
+
}
|
|
114
|
+
e.element.innerHTML = "", e.rendered = !1, e.rendering = !1;
|
|
115
|
+
}, F = () => {
|
|
116
|
+
if (!i || !f.value) return;
|
|
117
|
+
const t = f.value.querySelector(".pdf-pages");
|
|
118
|
+
if (t) {
|
|
119
|
+
t.innerHTML = "", u.clear();
|
|
120
|
+
for (let e = 1; e <= E.value; e++) {
|
|
121
|
+
const n = document.createElement("div");
|
|
122
|
+
n.className = "vfp-relative vfp-flex vfp-justify-center vfp-min-h-[800px]", n.setAttribute("data-page-number", String(e)), t.appendChild(n), u.set(e, {
|
|
123
|
+
element: n,
|
|
124
|
+
rendered: !1,
|
|
125
|
+
rendering: !1,
|
|
126
|
+
renderTask: null
|
|
127
|
+
}), c && c.observe(n);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, J = async () => {
|
|
131
|
+
if (b.value = null, M.value = !0, E.value = 0, i) {
|
|
132
|
+
try {
|
|
133
|
+
i.destroy();
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
i = null;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
i = await N.getDocument({ url: y.url }).promise;
|
|
140
|
+
const e = i.numPages;
|
|
141
|
+
E.value = e, h("totalPagesChange", e), h("pageChange", 1);
|
|
142
|
+
try {
|
|
143
|
+
const n = await i.getOutline();
|
|
144
|
+
n && (v.value = n, S.clear(), await q(n, i));
|
|
145
|
+
} catch (n) {
|
|
146
|
+
console.warn("PDF 大纲提取失败:", n);
|
|
147
|
+
}
|
|
148
|
+
M.value = !1;
|
|
149
|
+
} catch (t) {
|
|
150
|
+
console.error("PDF 加载错误:", t), b.value = R.value("pdf.load_failed"), M.value = !1;
|
|
151
|
+
}
|
|
152
|
+
}, j = () => {
|
|
153
|
+
if (!f.value || u.size === 0) return;
|
|
154
|
+
const t = f.value, e = t.scrollTop, n = t.clientHeight, r = e + n / 2;
|
|
155
|
+
let a = 1, l = 1 / 0;
|
|
156
|
+
u.forEach((o, s) => {
|
|
157
|
+
const p = o.element.getBoundingClientRect(), d = t.getBoundingClientRect(), m = p.top - d.top + p.height / 2 + e, x = Math.abs(r - m);
|
|
158
|
+
x < l && (l = x, a = s);
|
|
159
|
+
}), a !== y.currentPage && h("pageChange", a);
|
|
160
|
+
}, H = () => {
|
|
161
|
+
L(() => {
|
|
162
|
+
var e;
|
|
163
|
+
const t = (e = P.value) == null ? void 0 : e.querySelector(".outline-items");
|
|
164
|
+
t && t.addEventListener("click", (n) => {
|
|
165
|
+
const a = n.target.closest("[data-outline-key]");
|
|
166
|
+
if (!a) return;
|
|
167
|
+
const l = a.getAttribute("data-outline-key"), o = a.getAttribute("data-outline-dest");
|
|
168
|
+
if (!(!l || !o))
|
|
169
|
+
try {
|
|
170
|
+
const s = JSON.parse(o);
|
|
171
|
+
W(s, l);
|
|
172
|
+
} catch (s) {
|
|
173
|
+
console.error("解析大纲目标失败:", s);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
return k(() => y.url, () => {
|
|
179
|
+
y.url && J();
|
|
180
|
+
}, { immediate: !0 }), k(E, (t) => {
|
|
181
|
+
t > 0 && L(() => {
|
|
182
|
+
F();
|
|
183
|
+
});
|
|
184
|
+
}), k(() => y.zoom, () => {
|
|
185
|
+
u.forEach((t, e) => {
|
|
186
|
+
t.rendered && V(e);
|
|
187
|
+
}), setTimeout(() => {
|
|
188
|
+
c && f.value && u.forEach((t) => {
|
|
189
|
+
c == null || c.unobserve(t.element), c == null || c.observe(t.element);
|
|
190
|
+
});
|
|
191
|
+
}, 150);
|
|
192
|
+
}), k(() => y.currentPage, (t) => {
|
|
193
|
+
t > 0 && S.size > 0 && K(t);
|
|
194
|
+
}), k(T, () => {
|
|
195
|
+
L(() => {
|
|
196
|
+
var e;
|
|
197
|
+
const t = (e = P.value) == null ? void 0 : e.querySelector(".outline-items");
|
|
198
|
+
t && v.value.length > 0 && (t.innerHTML = `<ul>${$(v.value)}</ul>`, H());
|
|
199
|
+
});
|
|
200
|
+
}), k(v, () => {
|
|
201
|
+
L(() => {
|
|
202
|
+
var e;
|
|
203
|
+
const t = (e = P.value) == null ? void 0 : e.querySelector(".outline-items");
|
|
204
|
+
!t && v.value.length > 0 ? setTimeout(() => {
|
|
205
|
+
var r;
|
|
206
|
+
const n = (r = P.value) == null ? void 0 : r.querySelector(".outline-items");
|
|
207
|
+
n && (n.innerHTML = `<ul>${$(v.value)}</ul>`, H());
|
|
208
|
+
}, 100) : t && v.value.length > 0 && (t.innerHTML = `<ul>${$(v.value)}</ul>`, H());
|
|
209
|
+
});
|
|
210
|
+
}, { deep: !0 }), G(() => {
|
|
211
|
+
c = new IntersectionObserver(
|
|
212
|
+
(e) => {
|
|
213
|
+
e.forEach((n) => {
|
|
214
|
+
const r = Number(n.target.getAttribute("data-page-number"));
|
|
215
|
+
if (r)
|
|
216
|
+
if (n.isIntersecting)
|
|
217
|
+
X(r, y.zoom);
|
|
218
|
+
else {
|
|
219
|
+
const a = u.get(r);
|
|
220
|
+
a && a.rendered && V(r);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
root: f.value,
|
|
226
|
+
rootMargin: "500px 0px",
|
|
227
|
+
threshold: 0
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
const t = f.value;
|
|
231
|
+
t && t.addEventListener("scroll", j);
|
|
232
|
+
}), Q(() => {
|
|
233
|
+
c && (c.disconnect(), c = null);
|
|
234
|
+
const t = f.value;
|
|
235
|
+
if (t && t.removeEventListener("scroll", j), u.forEach((e) => {
|
|
236
|
+
e.renderTask && e.renderTask.cancel();
|
|
237
|
+
}), u.clear(), i) {
|
|
238
|
+
try {
|
|
239
|
+
i.destroy();
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
242
|
+
i = null;
|
|
243
|
+
}
|
|
244
|
+
}), (t, e) => (O(), A("div", {
|
|
245
|
+
ref_key: "containerRef",
|
|
246
|
+
ref: P,
|
|
247
|
+
class: "vfp-relative vfp-w-full vfp-h-full"
|
|
248
|
+
}, [
|
|
249
|
+
v.value.length > 0 ? (O(), A("div", {
|
|
250
|
+
key: 0,
|
|
251
|
+
class: "vfp-absolute vfp-inset-0 vfp-z-20 vfp-flex vfp-transition-opacity vfp-duration-300",
|
|
252
|
+
style: B({
|
|
253
|
+
opacity: C.showOutline ? 1 : 0,
|
|
254
|
+
pointerEvents: C.showOutline ? "auto" : "none"
|
|
255
|
+
})
|
|
256
|
+
}, [
|
|
257
|
+
g("div", {
|
|
258
|
+
class: "vfp-w-72 vfp-max-w-[80%] vfp-h-full vfp-bg-surface-overlay vfp-backdrop-blur-xl vfp-border-r vfp-border-line-weak vfp-flex vfp-flex-col vfp-shadow-2xl vfp-transition-transform vfp-duration-300",
|
|
259
|
+
style: B({ transform: C.showOutline ? "translateX(0)" : "translateX(-100%)" })
|
|
260
|
+
}, [
|
|
261
|
+
g("div", le, [
|
|
262
|
+
g("span", ae, Y(z(R)("toolbar.outline")), 1),
|
|
263
|
+
g("button", {
|
|
264
|
+
onClick: e[0] || (e[0] = (n) => h("toggleOutline")),
|
|
265
|
+
class: "vfp-text-fg-tertiary hover:vfp-text-fg-primary vfp-transition-colors"
|
|
266
|
+
}, [
|
|
267
|
+
Z(z(re), { class: "vfp-w-4 vfp-h-4" })
|
|
268
|
+
])
|
|
269
|
+
]),
|
|
270
|
+
g("div", {
|
|
271
|
+
class: "vfp-flex-1 vfp-overflow-y-auto vfp-py-4 vfp-px-1 outline-items",
|
|
272
|
+
innerHTML: $(v.value)
|
|
273
|
+
}, null, 8, ie)
|
|
274
|
+
], 4),
|
|
275
|
+
g("div", {
|
|
276
|
+
class: "vfp-flex-1 vfp-transition-opacity vfp-duration-300",
|
|
277
|
+
style: B({ background: C.showOutline ? "rgba(0,0,0,0.3)" : "transparent" }),
|
|
278
|
+
onClick: e[1] || (e[1] = (n) => h("toggleOutline"))
|
|
279
|
+
}, null, 4)
|
|
280
|
+
], 4)) : I("", !0),
|
|
281
|
+
g("div", {
|
|
282
|
+
ref_key: "scrollContainerRef",
|
|
283
|
+
ref: f,
|
|
284
|
+
class: "vfp-w-full vfp-h-full vfp-overflow-auto vfp-py-6 vfp-px-4"
|
|
285
|
+
}, [
|
|
286
|
+
b.value ? (O(), D(ne, {
|
|
287
|
+
key: 0,
|
|
288
|
+
message: b.value
|
|
289
|
+
}, null, 8, ["message"])) : I("", !0),
|
|
290
|
+
!b.value && M.value ? (O(), A("div", se, [...e[2] || (e[2] = [
|
|
291
|
+
g("div", { class: "vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin" }, null, -1)
|
|
292
|
+
])])) : I("", !0),
|
|
293
|
+
b.value ? I("", !0) : (O(), A("div", oe, [...e[3] || (e[3] = [
|
|
294
|
+
g("div", { class: "pdf-pages vfp-flex vfp-flex-col vfp-gap-4" }, null, -1)
|
|
295
|
+
])]))
|
|
296
|
+
], 512)
|
|
297
|
+
], 512));
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
export {
|
|
301
|
+
pe as default
|
|
302
|
+
};
|
|
303
|
+
//# sourceMappingURL=index-CYa_XneH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CYa_XneH.mjs","sources":["../../src/renderers/Pdf/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';\nimport { configurePdfWorker } from '@eternalheart/file-preview-core';\n// @ts-ignore - pdfjs-dist 类型路径\nimport * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\nimport { X } from 'lucide-vue-next';\n\n// 在模块加载时配置 PDF.js worker(默认走 CDN)\nconfigurePdfWorker(pdfjsLib);\n\ninterface PdfOutlineItem {\n title: string;\n dest: any;\n items?: PdfOutlineItem[];\n}\n\ninterface PdfPageProxy {\n getViewport(opts: { scale: number }): { width: number; height: number };\n render(opts: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }): {\n promise: Promise<void>;\n cancel(): void;\n };\n}\n\ninterface PdfDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<PdfPageProxy>;\n getOutline(): Promise<PdfOutlineItem[] | null>;\n destroy(): void;\n}\n\ninterface PageState {\n element: HTMLDivElement;\n rendered: boolean;\n rendering: boolean;\n renderTask: any;\n}\n\nconst props = defineProps<{\n url: string;\n zoom: number;\n currentPage: number;\n showOutline?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: 'pageChange', page: number): void;\n (e: 'totalPagesChange', total: number): void;\n (e: 'pageWidthChange', width: number): void;\n (e: 'toggleOutline'): void;\n}>();\n\nconst { t } = useTranslator();\n\nconst numPages = ref(0);\nconst error = ref<string | null>(null);\nconst isLoading = ref(true);\nconst outline = ref<PdfOutlineItem[]>([]);\nconst activeOutlineItem = ref<string | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nconst scrollContainerRef = ref<HTMLDivElement | null>(null);\nconst outlinePageMap = new Map<string, number>();\nlet pdfDoc: PdfDocumentProxy | null = null;\nconst pageStates = new Map<number, PageState>();\nlet observer: IntersectionObserver | null = null;\n\n// 构建大纲-页码映射\nconst buildOutlinePageMap = async (items: PdfOutlineItem[], pdfDocument: PdfDocumentProxy, depth = 0) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const itemKey = `${item.title}-${i}-${depth}`;\n\n try {\n let pageNumber: number | null = null;\n const dest = item.dest;\n\n if (typeof dest === 'string') {\n const namedDest = await (pdfDocument as any).getDestination?.(dest);\n if (namedDest && namedDest[0] && typeof namedDest[0] === 'object') {\n pageNumber = await (pdfDocument as any).getPageIndex?.(namedDest[0]) + 1;\n }\n } else if (Array.isArray(dest) && dest[0] && typeof dest[0] === 'object') {\n pageNumber = await (pdfDocument as any).getPageIndex?.(dest[0]) + 1;\n }\n\n if (pageNumber !== null && pageNumber > 0) {\n outlinePageMap.set(itemKey, pageNumber);\n }\n\n if (item.items && item.items.length > 0) {\n await buildOutlinePageMap(item.items, pdfDocument, depth + 1);\n }\n } catch (err) {\n // 静默失败\n }\n }\n};\n\n// 根据当前页码更新激活的大纲项\nconst updateActiveOutlineByPage = (page: number) => {\n let closestItem: string | null = null;\n let closestDistance = Infinity;\n\n outlinePageMap.forEach((itemPage, itemKey) => {\n if (itemPage <= page) {\n const distance = page - itemPage;\n if (distance < closestDistance) {\n closestDistance = distance;\n closestItem = itemKey;\n }\n }\n });\n\n if (closestItem !== activeOutlineItem.value) {\n activeOutlineItem.value = closestItem;\n }\n};\n\n// 处理大纲点击跳转\nconst handleOutlineClick = async (dest: any, itemKey: string) => {\n if (!pdfDoc || !scrollContainerRef.value) return;\n\n try {\n let pageNumber: number;\n\n if (typeof dest === 'string') {\n const namedDest = await (pdfDoc as any).getDestination?.(dest);\n if (namedDest && namedDest[0]) {\n pageNumber = await (pdfDoc as any).getPageIndex?.(namedDest[0]) + 1;\n } else {\n return;\n }\n } else if (Array.isArray(dest) && dest[0]) {\n pageNumber = await (pdfDoc as any).getPageIndex?.(dest[0]) + 1;\n } else {\n return;\n }\n\n // 设置激活项\n activeOutlineItem.value = itemKey;\n\n // 滚动到目标页面\n const pages = scrollContainerRef.value.querySelectorAll('[data-page-number]');\n const targetPage = pages[pageNumber - 1];\n if (targetPage) {\n targetPage.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n\n // 跳转后自动关闭侧边栏\n setTimeout(() => emit('toggleOutline'), 300);\n } catch (err) {\n console.error('大纲跳转失败:', err);\n }\n};\n\n// 渲染大纲项(递归组件会更复杂,这里用简单的方式)\nconst renderOutlineItemsHtml = (items: PdfOutlineItem[], depth = 0): string => {\n return items.map((item, i) => {\n const itemKey = `${item.title}-${i}-${depth}`;\n const isActive = activeOutlineItem.value === itemKey;\n const activeClass = isActive\n ? 'vfp-bg-surface-2 vfp-text-fg-primary vfp-font-medium'\n : 'vfp-text-fg-secondary hover:vfp-text-fg-primary hover:vfp-bg-surface-2';\n\n let html = `<li style=\"margin-left: ${depth > 0 ? 16 : 0}px;\">\n <button\n data-outline-key=\"${itemKey}\"\n data-outline-dest=\"${JSON.stringify(item.dest).replace(/\"/g, '"')}\"\n class=\"vfp-w-full vfp-text-left vfp-py-2 vfp-px-3 vfp-text-sm vfp-rounded vfp-transition-all vfp-truncate ${activeClass}\"\n title=\"${item.title}\"\n >\n ${item.title}\n </button>`;\n\n if (item.items && item.items.length > 0) {\n html += `<ul>${renderOutlineItemsHtml(item.items, depth + 1)}</ul>`;\n }\n\n html += '</li>';\n return html;\n }).join('');\n};\n\nconst renderPage = async (pageNumber: number, scale: number) => {\n if (!pdfDoc) return;\n const state = pageStates.get(pageNumber);\n if (!state || state.rendering) return;\n\n state.rendering = true;\n\n try {\n const page = await pdfDoc.getPage(pageNumber);\n const viewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n canvas.style.maxWidth = '100%';\n canvas.style.height = 'auto';\n canvas.style.borderRadius = '0';\n canvas.style.display = 'block';\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const renderTask = page.render({ canvasContext: ctx, viewport });\n state.renderTask = renderTask;\n await renderTask.promise;\n\n // 上报第一页原始宽度\n if (pageNumber === 1) {\n const baseViewport = page.getViewport({ scale: 1 });\n emit('pageWidthChange', baseViewport.width);\n }\n\n state.element.innerHTML = '';\n state.element.appendChild(canvas);\n\n state.rendered = true;\n } catch (err: any) {\n if (err?.name !== 'RenderingCancelledException') {\n console.error(`渲染页面 ${pageNumber} 失败:`, err);\n }\n } finally {\n state.rendering = false;\n state.renderTask = null;\n }\n};\n\nconst clearPageCanvas = (pageNumber: number) => {\n const state = pageStates.get(pageNumber);\n if (!state) return;\n\n if (state.renderTask) {\n state.renderTask.cancel();\n state.renderTask = null;\n }\n\n const canvas = state.element.querySelector('canvas');\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (ctx) {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n }\n }\n\n state.element.innerHTML = '';\n state.rendered = false;\n state.rendering = false;\n};\n\nconst initPagePlaceholders = () => {\n if (!pdfDoc || !scrollContainerRef.value) return;\n\n const wrapper = scrollContainerRef.value.querySelector('.pdf-pages') as HTMLDivElement | null;\n if (!wrapper) return;\n\n wrapper.innerHTML = '';\n pageStates.clear();\n\n for (let i = 1; i <= numPages.value; i++) {\n const pageDiv = document.createElement('div');\n pageDiv.className = 'vfp-relative vfp-flex vfp-justify-center vfp-min-h-[800px]';\n pageDiv.setAttribute('data-page-number', String(i));\n wrapper.appendChild(pageDiv);\n\n pageStates.set(i, {\n element: pageDiv,\n rendered: false,\n rendering: false,\n renderTask: null,\n });\n\n if (observer) {\n observer.observe(pageDiv);\n }\n }\n};\n\nconst loadPdf = async () => {\n error.value = null;\n isLoading.value = true;\n numPages.value = 0;\n\n if (pdfDoc) {\n try {\n pdfDoc.destroy();\n } catch {\n // ignore\n }\n pdfDoc = null;\n }\n\n try {\n const loadingTask = pdfjsLib.getDocument({ url: props.url });\n pdfDoc = (await loadingTask.promise) as PdfDocumentProxy;\n const total = pdfDoc.numPages;\n\n numPages.value = total;\n emit('totalPagesChange', total);\n emit('pageChange', 1);\n\n // 提取大纲\n try {\n const outlineData = await pdfDoc.getOutline();\n if (outlineData) {\n outline.value = outlineData;\n // 构建大纲-页码映射\n outlinePageMap.clear();\n await buildOutlinePageMap(outlineData, pdfDoc);\n }\n } catch (err) {\n console.warn('PDF 大纲提取失败:', err);\n }\n\n isLoading.value = false;\n } catch (err) {\n console.error('PDF 加载错误:', err);\n error.value = t.value('pdf.load_failed');\n isLoading.value = false;\n }\n};\n\nconst handleScroll = () => {\n if (!scrollContainerRef.value || pageStates.size === 0) return;\n\n const container = scrollContainerRef.value;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const scrollCenter = scrollTop + containerHeight / 2;\n\n let currentVisiblePage = 1;\n let minDistance = Infinity;\n\n pageStates.forEach((state, pageNumber) => {\n const rect = state.element.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const pageCenter = rect.top - containerRect.top + rect.height / 2 + scrollTop;\n\n const distance = Math.abs(scrollCenter - pageCenter);\n if (distance < minDistance) {\n minDistance = distance;\n currentVisiblePage = pageNumber;\n }\n });\n\n if (currentVisiblePage !== props.currentPage) {\n emit('pageChange', currentVisiblePage);\n }\n};\n\n// 设置大纲点击事件代理\nconst setupOutlineEventDelegation = () => {\n nextTick(() => {\n const outlineContainer = containerRef.value?.querySelector('.outline-items');\n if (!outlineContainer) return;\n\n outlineContainer.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement;\n const button = target.closest('[data-outline-key]');\n if (!button) return;\n\n const itemKey = button.getAttribute('data-outline-key');\n const destStr = button.getAttribute('data-outline-dest');\n if (!itemKey || !destStr) return;\n\n try {\n const dest = JSON.parse(destStr);\n handleOutlineClick(dest, itemKey);\n } catch (err) {\n console.error('解析大纲目标失败:', err);\n }\n });\n });\n};\n\n// 监听 URL 变化\nwatch(() => props.url, () => {\n if (props.url) {\n loadPdf();\n }\n}, { immediate: true });\n\n// 监听 numPages 变化,初始化占位符\nwatch(numPages, (val) => {\n if (val > 0) {\n nextTick(() => {\n initPagePlaceholders();\n });\n }\n});\n\n// 监听 zoom 变化\nwatch(() => props.zoom, () => {\n pageStates.forEach((state, pageNumber) => {\n if (state.rendered) {\n clearPageCanvas(pageNumber);\n }\n });\n\n setTimeout(() => {\n if (observer && scrollContainerRef.value) {\n pageStates.forEach((state) => {\n observer?.unobserve(state.element);\n observer?.observe(state.element);\n });\n }\n }, 150);\n});\n\n// 监听 currentPage 变化,更新大纲高亮\nwatch(() => props.currentPage, (page) => {\n if (page > 0 && outlinePageMap.size > 0) {\n updateActiveOutlineByPage(page);\n }\n});\n\n// 监听 activeOutlineItem 变化,更新大纲 UI\nwatch(activeOutlineItem, () => {\n nextTick(() => {\n const outlineContainer = containerRef.value?.querySelector('.outline-items');\n if (!outlineContainer) return;\n\n // 重新渲染大纲项\n if (outline.value.length > 0) {\n outlineContainer.innerHTML = `<ul>${renderOutlineItemsHtml(outline.value)}</ul>`;\n // 重新绑定事件\n setupOutlineEventDelegation();\n }\n });\n});\n\n// 监听 outline 变化,渲染大纲\nwatch(outline, () => {\n nextTick(() => {\n const outlineContainer = containerRef.value?.querySelector('.outline-items');\n if (!outlineContainer && outline.value.length > 0) {\n // 容器还没准备好,等待下一次\n setTimeout(() => {\n const container = containerRef.value?.querySelector('.outline-items');\n if (container) {\n container.innerHTML = `<ul>${renderOutlineItemsHtml(outline.value)}</ul>`;\n setupOutlineEventDelegation();\n }\n }, 100);\n } else if (outlineContainer && outline.value.length > 0) {\n outlineContainer.innerHTML = `<ul>${renderOutlineItemsHtml(outline.value)}</ul>`;\n setupOutlineEventDelegation();\n }\n });\n}, { deep: true });\n\nonMounted(() => {\n // 设置 IntersectionObserver\n observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const pageNumber = Number(entry.target.getAttribute('data-page-number'));\n if (!pageNumber) return;\n\n if (entry.isIntersecting) {\n renderPage(pageNumber, props.zoom);\n } else {\n const state = pageStates.get(pageNumber);\n if (state && state.rendered) {\n clearPageCanvas(pageNumber);\n }\n }\n });\n },\n {\n root: scrollContainerRef.value,\n rootMargin: '500px 0px',\n threshold: 0,\n }\n );\n\n // 监听滚动\n const container = scrollContainerRef.value;\n if (container) {\n container.addEventListener('scroll', handleScroll);\n }\n});\n\nonBeforeUnmount(() => {\n // 清理\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n\n const container = scrollContainerRef.value;\n if (container) {\n container.removeEventListener('scroll', handleScroll);\n }\n\n pageStates.forEach((state) => {\n if (state.renderTask) {\n state.renderTask.cancel();\n }\n });\n pageStates.clear();\n\n if (pdfDoc) {\n try {\n pdfDoc.destroy();\n } catch {\n // ignore\n }\n pdfDoc = null;\n }\n});\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"vfp-relative vfp-w-full vfp-h-full\">\n <!-- 大纲侧边栏 -->\n <div\n v-if=\"outline.length > 0\"\n class=\"vfp-absolute vfp-inset-0 vfp-z-20 vfp-flex vfp-transition-opacity vfp-duration-300\"\n :style=\"{\n opacity: showOutline ? 1 : 0,\n pointerEvents: showOutline ? 'auto' : 'none',\n }\"\n >\n <div\n class=\"vfp-w-72 vfp-max-w-[80%] vfp-h-full vfp-bg-surface-overlay vfp-backdrop-blur-xl vfp-border-r vfp-border-line-weak vfp-flex vfp-flex-col vfp-shadow-2xl vfp-transition-transform vfp-duration-300\"\n :style=\"{ transform: showOutline ? 'translateX(0)' : 'translateX(-100%)' }\"\n >\n <div class=\"vfp-flex vfp-items-center vfp-justify-between vfp-px-4 vfp-py-3 vfp-border-b vfp-border-line-weak vfp-flex-shrink-0\">\n <span class=\"vfp-text-fg-primary vfp-font-medium vfp-text-sm\">{{ t('toolbar.outline') }}</span>\n <button\n @click=\"emit('toggleOutline')\"\n class=\"vfp-text-fg-tertiary hover:vfp-text-fg-primary vfp-transition-colors\"\n >\n <X class=\"vfp-w-4 vfp-h-4\" />\n </button>\n </div>\n <div class=\"vfp-flex-1 vfp-overflow-y-auto vfp-py-4 vfp-px-1 outline-items\" v-html=\"renderOutlineItemsHtml(outline)\" />\n </div>\n <div\n class=\"vfp-flex-1 vfp-transition-opacity vfp-duration-300\"\n :style=\"{ background: showOutline ? 'rgba(0,0,0,0.3)' : 'transparent' }\"\n @click=\"emit('toggleOutline')\"\n />\n </div>\n\n <div\n ref=\"scrollContainerRef\"\n class=\"vfp-w-full vfp-h-full vfp-overflow-auto vfp-py-6 vfp-px-4\"\n >\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <div v-if=\"!error && isLoading\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-min-h-screen\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <div v-if=\"!error\" class=\"vfp-flex vfp-flex-col vfp-items-center\">\n <div class=\"pdf-pages vfp-flex vfp-flex-col vfp-gap-4\" />\n </div>\n </div>\n </div>\n</template>\n"],"names":["configurePdfWorker","pdfjsLib","props","__props","emit","__emit","t","useTranslator","numPages","ref","error","isLoading","outline","activeOutlineItem","containerRef","scrollContainerRef","outlinePageMap","pdfDoc","pageStates","observer","buildOutlinePageMap","items","pdfDocument","depth","i","item","itemKey","pageNumber","dest","namedDest","_a","_b","_c","updateActiveOutlineByPage","page","closestItem","closestDistance","itemPage","distance","handleOutlineClick","targetPage","err","renderOutlineItemsHtml","activeClass","html","renderPage","scale","state","viewport","canvas","ctx","renderTask","baseViewport","clearPageCanvas","initPagePlaceholders","wrapper","pageDiv","loadPdf","total","outlineData","handleScroll","container","scrollTop","containerHeight","scrollCenter","currentVisiblePage","minDistance","rect","containerRect","pageCenter","setupOutlineEventDelegation","nextTick","outlineContainer","e","button","destStr","watch","val","onMounted","entries","entry","onBeforeUnmount","_createElementBlock","_normalizeStyle","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","_unref","_createVNode","X","_createBlock","RendererError","_openBlock","_hoisted_4","_cache","_hoisted_5"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineComponent as P, ref as y, watch as S, computed as h, openBlock as a, createElementBlock as r, createElementVNode as e, createBlock as V, unref as c, normalizeClass as b, Fragment as w, renderList as k, toDisplayString as o, createCommentVNode as
|
|
2
|
-
import { u as j, a as z,
|
|
3
|
-
import { _ as M } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
1
|
+
import { defineComponent as P, ref as y, watch as S, computed as h, openBlock as a, createElementBlock as r, createElementVNode as e, createBlock as V, unref as c, normalizeClass as b, Fragment as w, renderList as k, toDisplayString as o, createCommentVNode as T } from "vue";
|
|
2
|
+
import { u as j, a as z, T as A, P as d, E as D, _ as I } from "./index-D9QQL7U6.mjs";
|
|
3
|
+
import { _ as M } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
4
4
|
const O = {
|
|
5
5
|
key: 0,
|
|
6
6
|
class: "vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-bg-[#0f0f12]"
|
|
@@ -54,7 +54,7 @@ const O = {
|
|
|
54
54
|
}), g = h(() => {
|
|
55
55
|
var s;
|
|
56
56
|
return ((s = n.value) == null ? void 0 : s.metadata) ?? {};
|
|
57
|
-
}),
|
|
57
|
+
}), N = (s) => d(s).slice(3, 8);
|
|
58
58
|
return (s, t) => p.value ? (a(), r("div", O, [...t[0] || (t[0] = [
|
|
59
59
|
e("div", { class: "vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin" }, null, -1)
|
|
60
60
|
])])) : i.value || !n.value ? (a(), V(M, {
|
|
@@ -79,7 +79,7 @@ const O = {
|
|
|
79
79
|
t[1] || (t[1] = e("span", { class: "cue-arrow" }, "→", -1)),
|
|
80
80
|
e("span", G, o(c(d)(l.end)), 1),
|
|
81
81
|
e("span", H, "#" + o(l.id ?? x + 1), 1),
|
|
82
|
-
l.style ? (a(), r("span", J, o(l.style), 1)) :
|
|
82
|
+
l.style ? (a(), r("span", J, o(l.style), 1)) : T("", !0)
|
|
83
83
|
]),
|
|
84
84
|
l.words && l.words.length > 0 ? (a(), r("div", K, [
|
|
85
85
|
(a(!0), r(w, null, k(l.words, (_, L) => (a(), r("span", {
|
|
@@ -87,7 +87,7 @@ const O = {
|
|
|
87
87
|
class: "cue-word",
|
|
88
88
|
title: c(d)(_.start)
|
|
89
89
|
}, [
|
|
90
|
-
e("span", U, o(
|
|
90
|
+
e("span", U, o(N(_.start)), 1),
|
|
91
91
|
e("span", W, o(_.text), 1)
|
|
92
92
|
], 8, Q))), 128))
|
|
93
93
|
])) : (a(), r("p", {
|
|
@@ -103,12 +103,12 @@ const O = {
|
|
|
103
103
|
g.value.length ? (a(), r(w, { key: 0 }, [
|
|
104
104
|
t[4] || (t[4] = e("span", { class: "dot" }, "·", -1)),
|
|
105
105
|
e("span", null, o(g.value.length), 1)
|
|
106
|
-
], 64)) :
|
|
106
|
+
], 64)) : T("", !0)
|
|
107
107
|
])
|
|
108
108
|
], 2));
|
|
109
109
|
}
|
|
110
|
-
}), re = /* @__PURE__ */ I(ee, [["__scopeId", "data-v-
|
|
110
|
+
}), re = /* @__PURE__ */ I(ee, [["__scopeId", "data-v-a5bde9e5"]]);
|
|
111
111
|
export {
|
|
112
112
|
re as default
|
|
113
113
|
};
|
|
114
|
-
//# sourceMappingURL=index-
|
|
114
|
+
//# sourceMappingURL=index-CbJkkdEB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CbJkkdEB.mjs","sources":["../../src/renderers/Subtitle/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport {\n parseSubtitle,\n formatSubtitleTime,\n fetchTextUtf8,\n type SubtitleParseResult,\n type SubtitleFormat,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n fileName: string;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst text = ref<string>('');\nconst loading = ref(true);\nconst error = ref<string | null>(null);\n\nconst FORMAT_BY_EXT: Record<string, SubtitleFormat> = {\n srt: 'srt',\n vtt: 'vtt',\n lrc: 'lrc',\n elrc: 'elrc',\n ass: 'ass',\n ssa: 'ssa',\n ttml: 'ttml',\n dfxp: 'ttml',\n};\n\nconst getFormat = (fileName: string): SubtitleFormat | undefined => {\n const ext = fileName.split('.').pop()?.toLowerCase() || '';\n return FORMAT_BY_EXT[ext];\n};\n\nconst load = async () => {\n loading.value = true;\n error.value = null;\n try {\n text.value = await fetchTextUtf8(props.url, { fetcher: fetcher.value });\n } catch (err) {\n console.error(err);\n error.value = t.value('subtitle.load_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, load, { immediate: true });\n\nconst parsed = computed<SubtitleParseResult | null>(() => {\n if (!text.value) return null;\n try {\n return parseSubtitle(text.value, getFormat(props.fileName));\n } catch (err) {\n console.error(err);\n return null;\n }\n});\n\nconst isLyric = computed(() => parsed.value?.format === 'lrc' || parsed.value?.format === 'elrc');\n\nconst meta = computed<Record<string, string>>(() => parsed.value?.metadata ?? {});\n\nconst wordTimeShort = (t: number) => formatSubtitleTime(t).slice(3, 8);\n</script>\n\n<template>\n <div\n v-if=\"loading\"\n class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-bg-[#0f0f12]\"\n >\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <RendererError\n v-else-if=\"error || !parsed\"\n :message=\"error || t('subtitle.parse_failed')\"\n class=\"vfp-bg-[#0f0f12]\"\n />\n\n <div\n v-else\n class=\"vfp-relative vfp-w-full vfp-h-full vfp-bg-[#0f0f12]\"\n :class=\"isLyric ? 'flavor-lyric' : 'flavor-subtitle'\"\n >\n <!-- 内容滚动区 -->\n <div class=\"content-scroll\">\n <div class=\"timeline\">\n <div class=\"timeline-line\" />\n <ol class=\"cues\">\n <li v-for=\"(cue, i) in parsed.cues\" :key=\"'cue-' + i\" class=\"cue-row\">\n <div class=\"cue-dot\" />\n <div class=\"cue-meta\">\n <span class=\"cue-time\">{{ formatSubtitleTime(cue.start) }}</span>\n <span class=\"cue-arrow\">→</span>\n <span class=\"cue-time\">{{ formatSubtitleTime(cue.end) }}</span>\n <span class=\"cue-id\">#{{ cue.id ?? i + 1 }}</span>\n <span v-if=\"cue.style\" class=\"cue-style\">{{ cue.style }}</span>\n </div>\n <div v-if=\"cue.words && cue.words.length > 0\" class=\"cue-words\">\n <span\n v-for=\"(word, wi) in cue.words\"\n :key=\"'w-' + wi\"\n class=\"cue-word\"\n :title=\"formatSubtitleTime(word.start)\"\n >\n <span class=\"cue-word-time\">{{ wordTimeShort(word.start) }}</span>\n <span class=\"cue-word-text\">{{ word.text }}</span>\n </span>\n </div>\n <p v-else class=\"cue-text\" :class=\"{ lyric: isLyric }\">{{ cue.text }}</p>\n </li>\n </ol>\n </div>\n </div>\n\n <!-- 右下角浮签 -->\n <div class=\"status-pill\">\n <span>{{ parsed.cues.length }} {{ isLyric ? t('subtitle.lines') : t('subtitle.cues') }}</span>\n <template v-if=\"meta.length\">\n <span class=\"dot\">·</span>\n <span>{{ meta.length }}</span>\n </template>\n </div>\n </div>\n</template>\n\n<style scoped>\n.content-scroll {\n width: 100%;\n height: 100%;\n overflow: auto;\n padding: 1.5rem 1rem 4rem;\n}\n.status-pill {\n pointer-events: none;\n position: absolute;\n bottom: 0.75rem;\n right: 0.75rem;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.25rem 0.625rem;\n border-radius: 9999px;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(8px);\n border: 1px solid var(--fp-line);\n font-size: 0.625rem;\n color: var(--fp-fg-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-variant-numeric: tabular-nums;\n}\n.dot {\n color: var(--fp-fg-disabled);\n}\n\n.timeline {\n position: relative;\n}\n.timeline-line {\n position: absolute;\n left: 5px;\n top: 0.5rem;\n bottom: 0.5rem;\n width: 1px;\n background: var(--fp-line-weak);\n}\n.cues {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 1.25rem;\n}\n.cue-row {\n position: relative;\n padding-left: 1.5rem;\n}\n.cue-dot {\n position: absolute;\n left: 0;\n top: 0.4rem;\n width: 0.75rem;\n height: 0.75rem;\n border-radius: 9999px;\n background: var(--fp-surface-3);\n border: 2px solid #0f0f12;\n transition: background-color 0.2s;\n}\n.flavor-lyric .cue-row:hover .cue-dot {\n background: rgb(167, 139, 250);\n}\n.flavor-subtitle .cue-row:hover .cue-dot {\n background: rgb(56, 189, 248);\n}\n.cue-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0.25rem 0.75rem;\n margin-bottom: 0.375rem;\n}\n.cue-time {\n font-size: 0.6875rem;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n color: var(--fp-fg-muted);\n font-variant-numeric: tabular-nums;\n}\n.cue-arrow {\n font-size: 0.6875rem;\n color: var(--fp-fg-disabled);\n}\n.cue-id {\n font-size: 0.625rem;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n color: var(--fp-fg-disabled);\n font-variant-numeric: tabular-nums;\n}\n.cue-style {\n font-size: 0.5625rem;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n color: var(--fp-fg-tertiary);\n padding: 0.125rem 0.375rem;\n border-radius: 0.25rem;\n background: var(--fp-line-weak);\n border: 1px solid var(--fp-line);\n}\n.cue-text {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n line-height: 1.625;\n color: var(--fp-fg-primary);\n font-size: 0.875rem;\n min-height: 1.25rem;\n transition: color 0.2s;\n}\n.cue-text.lyric {\n font-size: 1rem;\n font-weight: 500;\n}\n.cue-row:hover .cue-text {\n color: #fff;\n}\n.cue-words {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 0.375rem;\n font-size: 1rem;\n color: var(--fp-fg-primary);\n line-height: 1.625;\n transition: color 0.2s;\n}\n.cue-row:hover .cue-words {\n color: #fff;\n}\n.cue-word {\n display: inline-flex;\n flex-direction: column;\n align-items: flex-start;\n}\n.cue-word-time {\n font-size: 0.5625rem;\n color: var(--fp-fg-disabled);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n line-height: 1;\n font-variant-numeric: tabular-nums;\n}\n.cue-word-text {\n line-height: 1.4;\n}\n</style>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","text","ref","loading","error","FORMAT_BY_EXT","getFormat","fileName","ext","_a","watch","fetchTextUtf8","err","parsed","computed","parseSubtitle","isLyric","_b","meta","wordTimeShort","formatSubtitleTime","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_createBlock","RendererError","_unref","_normalizeClass","_hoisted_2","_hoisted_3","_hoisted_4","_Fragment","cue","i","_hoisted_5","_hoisted_6","_toDisplayString","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","word","wi","_hoisted_12","_hoisted_13","_hoisted_14"],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import { defineComponent as E, ref as p, onMounted as T, watch as D, onBeforeUnm
|
|
|
2
2
|
import W from "exceljs";
|
|
3
3
|
import H from "x-data-spreadsheet";
|
|
4
4
|
/* empty css */
|
|
5
|
-
import { u as R, a as C, I } from "./index-
|
|
6
|
-
import { _ as L } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
5
|
+
import { u as R, a as C, I } from "./index-D9QQL7U6.mjs";
|
|
6
|
+
import { _ as L } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
7
7
|
const F = { class: "vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full" }, N = {
|
|
8
8
|
key: 0,
|
|
9
9
|
class: "vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10"
|
|
@@ -114,4 +114,4 @@ const F = { class: "vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-fu
|
|
|
114
114
|
export {
|
|
115
115
|
U as default
|
|
116
116
|
};
|
|
117
|
-
//# sourceMappingURL=index-
|
|
117
|
+
//# sourceMappingURL=index-CoITLoYL.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-CoITLoYL.mjs","sources":["../../src/renderers/Xlsx/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount } from 'vue';\nimport ExcelJS from 'exceljs';\nimport Spreadsheet from 'x-data-spreadsheet';\nimport 'x-data-spreadsheet/dist/xspreadsheet.css';\nimport { convertWorkbookToSpreadsheetData } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet sheetData: Record<string, unknown>[] | null = null;\nlet resizeObserver: ResizeObserver | null = null;\nlet resizeTimeout: number | null = null;\nlet lastDimensions = { width: 0, height: 0 };\n\nconst calculateDimensions = () => {\n if (!containerRef.value) return { width: 800, height: 600 };\n const rawWidth = containerRef.value.clientWidth;\n const rawHeight = containerRef.value.clientHeight;\n const width = rawWidth > 100 ? rawWidth : 800;\n const height = rawHeight > 100 ? rawHeight : 600;\n return { width, height };\n};\n\nconst mountSpreadsheet = () => {\n if (!containerRef.value || !sheetData) return;\n\n containerRef.value.innerHTML = '';\n\n const { width, height } = calculateDimensions();\n const isMobile = width < 640;\n\n const s = new Spreadsheet(containerRef.value, {\n mode: 'read',\n showToolbar: false,\n showContextmenu: false,\n showGrid: true,\n row: {\n len: 100,\n height: 25,\n },\n col: {\n len: 26,\n width: isMobile ? 80 : 100,\n indexWidth: isMobile ? 40 : 60,\n minWidth: isMobile ? 40 : 60,\n },\n view: {\n height: () => height,\n width: () => width,\n },\n });\n\n s.loadData(sheetData as unknown as Record<string, unknown>);\n};\n\nconst loadExcel = async () => {\n if (!containerRef.value) return;\n\n loading.value = true;\n error.value = null;\n\n try {\n const response = await fetcher.value(props.url, {\n mode: 'cors',\n credentials: 'omit',\n redirect: 'follow',\n });\n\n if (!response.ok) {\n if (response.status === 404) throw new Error(t.value('xlsx.not_found'));\n if (response.status === 403) throw new Error('无权限访问此文件');\n throw new Error(`文件加载失败 (${response.status})`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n if (arrayBuffer.byteLength === 0) {\n throw new Error('文件为空');\n }\n\n const workbook = new ExcelJS.Workbook();\n await workbook.xlsx.load(arrayBuffer);\n const data = convertWorkbookToSpreadsheetData(workbook);\n\n sheetData = data as unknown as Record<string, unknown>[];\n mountSpreadsheet();\n loading.value = false;\n } catch (err) {\n console.error('Excel 解析错误:', err);\n let errorMsg = t.value('xlsx.parse_failed');\n if (err instanceof Error) errorMsg = err.message;\n error.value = errorMsg;\n loading.value = false;\n }\n};\n\nonMounted(() => {\n if (!containerRef.value) return;\n\n let isInitialRender = true;\n\n resizeObserver = new ResizeObserver(() => {\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensions = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) return;\n\n lastDimensions = newDimensions;\n\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n\n resizeTimeout = window.setTimeout(() => {\n if (sheetData) mountSpreadsheet();\n }, 500);\n });\n\n resizeObserver.observe(containerRef.value);\n\n setTimeout(() => {\n requestAnimationFrame(() => loadExcel());\n }, 100);\n});\n\nwatch(\n () => props.url,\n (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) loadExcel();\n }\n);\n\nonBeforeUnmount(() => {\n if (resizeObserver) resizeObserver.disconnect();\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n sheetData = null;\n if (containerRef.value) containerRef.value.innerHTML = '';\n});\n</script>\n\n<template>\n <div class=\"vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full\">\n <div\n v-if=\"loading\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10\"\n >\n <div class=\"vfp-text-center\">\n <div\n class=\"vfp-w-10 vfp-h-10 md:vfp-w-12 md:vfp-h-12 vfp-mx-auto vfp-mb-3 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n <p class=\"vfp-text-xs md:vfp-text-sm vfp-text-fg-secondary vfp-font-medium\">{{ t('xlsx.loading') }}</p>\n </div>\n </div>\n\n <RendererError\n v-if=\"error && !loading\"\n :message=\"t('xlsx.load_failed')\"\n :detail=\"error\"\n class=\"vfp-absolute vfp-inset-0 vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10\"\n />\n\n <div\n v-if=\"!error\"\n ref=\"containerRef\"\n class=\"xlsx-spreadsheet-container vfp-w-full vfp-h-full\"\n :style=\"{ opacity: loading ? 0 : 1 }\"\n />\n </div>\n</template>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","loading","ref","error","containerRef","sheetData","resizeObserver","resizeTimeout","lastDimensions","calculateDimensions","rawWidth","rawHeight","width","height","mountSpreadsheet","isMobile","Spreadsheet","loadExcel","response","arrayBuffer","workbook","ExcelJS","convertWorkbookToSpreadsheetData","err","errorMsg","onMounted","isInitialRender","newDimensions","widthDiff","heightDiff","watch","newUrl","onBeforeUnmount","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_createElementVNode","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_createBlock","RendererError"],"mappings":";;;;;;;;;;;;;;;AAUA,UAAMA,IAAQC,GAIR,EAAE,GAAAC,EAAA,IAAMC,EAAA,GACRC,IAAUC,EAAA,GAEVC,IAAUC,EAAI,EAAI,GAClBC,IAAQD,EAAmB,IAAI,GAC/BE,IAAeF,EAA2B,IAAI;AACpD,QAAIG,IAA8C,MAC9CC,IAAwC,MACxCC,IAA+B,MAC/BC,IAAiB,EAAE,OAAO,GAAG,QAAQ,EAAA;AAEzC,UAAMC,IAAsB,MAAM;AAChC,UAAI,CAACL,EAAa,MAAO,QAAO,EAAE,OAAO,KAAK,QAAQ,IAAA;AACtD,YAAMM,IAAWN,EAAa,MAAM,aAC9BO,IAAYP,EAAa,MAAM,cAC/BQ,IAAQF,IAAW,MAAMA,IAAW,KACpCG,IAASF,IAAY,MAAMA,IAAY;AAC7C,aAAO,EAAE,OAAAC,GAAO,QAAAC,EAAA;AAAA,IAClB,GAEMC,IAAmB,MAAM;AAC7B,UAAI,CAACV,EAAa,SAAS,CAACC,EAAW;AAEvC,MAAAD,EAAa,MAAM,YAAY;AAE/B,YAAM,EAAE,OAAAQ,GAAO,QAAAC,EAAA,IAAWJ,EAAA,GACpBM,IAAWH,IAAQ;AAuBzB,MArBU,IAAII,EAAYZ,EAAa,OAAO;AAAA,QAC5C,MAAM;AAAA,QACN,aAAa;AAAA,QACb,iBAAiB;AAAA,QACjB,UAAU;AAAA,QACV,KAAK;AAAA,UACH,KAAK;AAAA,UACL,QAAQ;AAAA,QAAA;AAAA,QAEV,KAAK;AAAA,UACH,KAAK;AAAA,UACL,OAAOW,IAAW,KAAK;AAAA,UACvB,YAAYA,IAAW,KAAK;AAAA,UAC5B,UAAUA,IAAW,KAAK;AAAA,QAAA;AAAA,QAE5B,MAAM;AAAA,UACJ,QAAQ,MAAMF;AAAA,UACd,OAAO,MAAMD;AAAA,QAAA;AAAA,MACf,CACD,EAEC,SAASP,CAA+C;AAAA,IAC5D,GAEMY,IAAY,YAAY;AAC5B,UAAKb,EAAa,OAElB;AAAA,QAAAH,EAAQ,QAAQ,IAChBE,EAAM,QAAQ;AAEd,YAAI;AACF,gBAAMe,IAAW,MAAMnB,EAAQ,MAAMJ,EAAM,KAAK;AAAA,YAC9C,MAAM;AAAA,YACN,aAAa;AAAA,YACb,UAAU;AAAA,UAAA,CACX;AAED,cAAI,CAACuB,EAAS;AACZ,kBAAIA,EAAS,WAAW,MAAW,IAAI,MAAMrB,EAAE,MAAM,gBAAgB,CAAC,IAClEqB,EAAS,WAAW,MAAW,IAAI,MAAM,UAAU,IACjD,IAAI,MAAM,WAAWA,EAAS,MAAM,GAAG;AAG/C,gBAAMC,IAAc,MAAMD,EAAS,YAAA;AAEnC,cAAIC,EAAY,eAAe;AAC7B,kBAAM,IAAI,MAAM,MAAM;AAGxB,gBAAMC,IAAW,IAAIC,EAAQ,SAAA;AAC7B,gBAAMD,EAAS,KAAK,KAAKD,CAAW,GAGpCd,IAFaiB,EAAiCF,CAAQ,GAGtDN,EAAA,GACAb,EAAQ,QAAQ;AAAA,QAClB,SAASsB,GAAK;AACZ,kBAAQ,MAAM,eAAeA,CAAG;AAChC,cAAIC,IAAW3B,EAAE,MAAM,mBAAmB;AAC1C,UAAI0B,aAAe,UAAOC,IAAWD,EAAI,UACzCpB,EAAM,QAAQqB,GACdvB,EAAQ,QAAQ;AAAA,QAClB;AAAA;AAAA,IACF;AAEA,WAAAwB,EAAU,MAAM;AACd,UAAI,CAACrB,EAAa,MAAO;AAEzB,UAAIsB,IAAkB;AAEtB,MAAApB,IAAiB,IAAI,eAAe,MAAM;AACxC,YAAIoB,GAAiB;AACnB,UAAAA,IAAkB,IAClBlB,IAAiBC,EAAA;AACjB;AAAA,QACF;AAEA,cAAMkB,IAAgBlB,EAAA,GAChBmB,IAAY,KAAK,IAAIpB,EAAe,QAAQmB,EAAc,KAAK,GAC/DE,IAAa,KAAK,IAAIrB,EAAe,SAASmB,EAAc,MAAM;AAExE,QAAIC,IAAY,MAAMC,IAAa,OAEnCrB,IAAiBmB,GAEbpB,MAAkB,QAAM,aAAaA,CAAa,GAEtDA,IAAgB,OAAO,WAAW,MAAM;AACtC,UAAIF,KAAWS,EAAA;AAAA,QACjB,GAAG,GAAG;AAAA,MACR,CAAC,GAEDR,EAAe,QAAQF,EAAa,KAAK,GAEzC,WAAW,MAAM;AACf,8BAAsB,MAAMa,GAAW;AAAA,MACzC,GAAG,GAAG;AAAA,IACR,CAAC,GAEDa;AAAA,MACE,MAAMnC,EAAM;AAAA,MACZ,CAACoC,MAAW;AAEV,QAAIA,KAAQd,EAAA;AAAA,MACd;AAAA,IAAA,GAGFe,EAAgB,MAAM;AACpB,MAAI1B,OAA+B,WAAA,GAC/BC,MAAkB,QAAM,aAAaA,CAAa,GACtDF,IAAY,MACRD,EAAa,UAAOA,EAAa,MAAM,YAAY;AAAA,IACzD,CAAC,cAIC6B,EAAA,GAAAC,EA0BM,OA1BNC,GA0BM;AAAA,MAxBIlC,EAAA,SADRgC,EAAA,GAAAC,EAUM,OAVNE,GAUM;AAAA,QANJC,EAKM,OALNC,GAKM;AAAA,0BAJJD,EAEE,OAAA,EADA,OAAM,iKAAA,GAAgK,MAAA,EAAA;AAAA,UAExKA,EAAuG,KAAvGE,GAAuGC,EAAxBC,EAAA5C,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,QAAA;;MAK5EM,EAAA,UAAUF,EAAA,cADlByC,EAKEC,GAAA;AAAA;QAHC,SAASF,EAAA5C,CAAA,EAAC,kBAAA;AAAA,QACV,QAAQM,EAAA;AAAA,QACT,OAAM;AAAA,MAAA;MAICA,EAAA,0BADT+B,EAKE,OAAA;AAAA;iBAHI;AAAA,QAAJ,KAAI9B;AAAA,QACJ,OAAM;AAAA,QACL,oBAAkBH,EAAA,QAAO,IAAA,GAAA;AAAA,MAAA;;;;"}
|