@robot-admin/naive-ui-components 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/C_ActionBar.cjs +1 -5
- package/dist/C_ActionBar.js +1 -4
- package/dist/C_ActionBar.js.map +1 -0
- package/dist/C_ActionBar2.js +1 -195
- package/dist/C_ActionBar2.js.map +1 -1
- package/dist/{C_AntV-DGjscTWa.css.map → C_AntV-A3NXx_sF.css.map} +1 -1
- package/dist/C_AntV.cjs +1 -8
- package/dist/C_AntV.js +1 -4
- package/dist/C_AntV.js.map +1 -0
- package/dist/C_AntV2.js +4 -3149
- package/dist/C_AntV2.js.map +1 -1
- package/dist/C_Barcode.cjs +1 -4
- package/dist/C_Barcode.js +1 -3
- package/dist/C_Barcode.js.map +1 -0
- package/dist/C_Barcode2.js +1 -67
- package/dist/C_Barcode2.js.map +1 -1
- package/dist/C_Captcha.cjs +1 -4
- package/dist/C_Captcha.js +1 -3
- package/dist/C_Captcha.js.map +1 -0
- package/dist/C_Captcha2.js +1 -154
- package/dist/C_Captcha2.js.map +1 -1
- package/dist/C_Cascade.cjs +1 -4
- package/dist/C_Cascade.js +1 -3
- package/dist/C_Cascade.js.map +1 -0
- package/dist/C_Cascade2.js +1 -102
- package/dist/C_Cascade2.js.map +1 -1
- package/dist/C_City.cjs +1 -4
- package/dist/C_City.js +1 -3
- package/dist/C_City.js.map +1 -0
- package/dist/C_City2.js +1 -840
- package/dist/C_City2.js.map +1 -1
- package/dist/C_Code.cjs +1 -5
- package/dist/C_Code.js +1 -4
- package/dist/C_Code.js.map +1 -0
- package/dist/C_Code2.js +1 -346
- package/dist/C_Code2.js.map +1 -1
- package/dist/C_CollapsePanel.cjs +1 -6
- package/dist/C_CollapsePanel.js +1 -4
- package/dist/C_CollapsePanel.js.map +1 -0
- package/dist/C_CollapsePanel2.js +1 -318
- package/dist/C_CollapsePanel2.js.map +1 -1
- package/dist/C_Cron.cjs +1 -15
- package/dist/C_Cron.js +1 -4
- package/dist/C_Cron.js.map +1 -0
- package/dist/C_Cron2.js +1 -1209
- package/dist/C_Cron2.js.map +1 -1
- package/dist/C_Date.cjs +1 -4
- package/dist/C_Date.js +1 -3
- package/dist/C_Date.js.map +1 -0
- package/dist/C_Date2.js +1 -218
- package/dist/C_Date2.js.map +1 -1
- package/dist/C_Draggable.cjs +1 -5
- package/dist/C_Draggable.js +1 -3
- package/dist/C_Draggable.js.map +1 -0
- package/dist/C_Draggable2.js +1 -294
- package/dist/C_Draggable2.js.map +1 -1
- package/dist/C_Editor.cjs +1 -4
- package/dist/C_Editor.js +1 -3
- package/dist/C_Editor.js.map +1 -0
- package/dist/C_Editor2.js +1 -159
- package/dist/C_Editor2.js.map +1 -1
- package/dist/{C_FilePreview-B4XgTv-h.css.map → C_FilePreview-DHGlV4OR.css.map} +1 -1
- package/dist/C_FilePreview.cjs +1 -7
- package/dist/C_FilePreview.js +1 -4
- package/dist/C_FilePreview.js.map +1 -0
- package/dist/C_FilePreview2.js +1 -1031
- package/dist/C_FilePreview2.js.map +1 -1
- package/dist/{C_Form-DLb9FtAb.css.map → C_Form-CKHvXL3O.css.map} +1 -1
- package/dist/C_Form.cjs +1 -16
- package/dist/C_Form.js +1 -5
- package/dist/C_Form.js.map +1 -0
- package/dist/C_Form2.js +1 -2569
- package/dist/C_Form2.js.map +1 -1
- package/dist/{C_FormSearch-DlIEoh7X.css.map → C_FormSearch-DBNtnjaL.css.map} +1 -1
- package/dist/C_FormSearch.cjs +1 -6
- package/dist/C_FormSearch.js +1 -3
- package/dist/C_FormSearch.js.map +1 -0
- package/dist/C_FormSearch2.js +1 -355
- package/dist/C_FormSearch2.js.map +1 -1
- package/dist/C_FormulaEditor.cjs +1 -13
- package/dist/C_FormulaEditor.js +1 -4
- package/dist/C_FormulaEditor.js.map +1 -0
- package/dist/C_FormulaEditor2.js +1 -1432
- package/dist/C_FormulaEditor2.js.map +1 -1
- package/dist/C_FullCalendar-DLUdCtqf.css.map +1 -0
- package/dist/C_FullCalendar.cjs +1 -9
- package/dist/C_FullCalendar.js +1 -3
- package/dist/C_FullCalendar.js.map +1 -0
- package/dist/C_FullCalendar2.js +1 -376
- package/dist/C_FullCalendar2.js.map +1 -1
- package/dist/C_GlobalSearch.cjs +1 -5
- package/dist/C_GlobalSearch.js +1 -3
- package/dist/C_GlobalSearch.js.map +1 -0
- package/dist/C_GlobalSearch2.js +1 -335
- package/dist/C_GlobalSearch2.js.map +1 -1
- package/dist/C_Guide.cjs +1 -5
- package/dist/C_Guide.js +1 -4
- package/dist/C_Guide.js.map +1 -0
- package/dist/C_Guide2.js +1 -60
- package/dist/C_Guide2.js.map +1 -1
- package/dist/C_Icon.cjs +1 -4
- package/dist/C_Icon.js +1 -3
- package/dist/C_Icon.js.map +1 -0
- package/dist/C_Icon2.js +1 -285
- package/dist/C_Icon2.js.map +1 -1
- package/dist/C_ImageCropper.cjs +1 -6
- package/dist/C_ImageCropper.js +1 -4
- package/dist/C_ImageCropper.js.map +1 -0
- package/dist/C_ImageCropper2.js +1 -722
- package/dist/C_ImageCropper2.js.map +1 -1
- package/dist/C_Language.cjs +1 -5
- package/dist/C_Language.js +1 -4
- package/dist/C_Language.js.map +1 -0
- package/dist/C_Language2.js +1 -78
- package/dist/C_Language2.js.map +1 -1
- package/dist/C_Map.cjs +1 -7
- package/dist/C_Map.js +1 -3
- package/dist/C_Map.js.map +1 -0
- package/dist/C_Map2.js +1 -198
- package/dist/C_Map2.js.map +1 -1
- package/dist/{C_Markdown-C2uiBVSW.css.map → C_Markdown-BbTvdFCl.css.map} +1 -1
- package/dist/C_Markdown.cjs +1 -4
- package/dist/C_Markdown.js +1 -3
- package/dist/C_Markdown.js.map +1 -0
- package/dist/C_Markdown2.js +1 -185
- package/dist/C_Markdown2.js.map +1 -1
- package/dist/C_NotificationCenter.cjs +1 -20
- package/dist/C_NotificationCenter.js +1 -4
- package/dist/C_NotificationCenter.js.map +1 -0
- package/dist/C_NotificationCenter2.js +1 -1383
- package/dist/C_NotificationCenter2.js.map +1 -1
- package/dist/C_Progress.cjs +1 -4
- package/dist/C_Progress.js +1 -3
- package/dist/C_Progress.js.map +1 -0
- package/dist/C_Progress2.js +1 -102
- package/dist/C_Progress2.js.map +1 -1
- package/dist/C_QRCode.cjs +1 -5
- package/dist/C_QRCode.js +1 -3
- package/dist/C_QRCode.js.map +1 -0
- package/dist/C_QRCode2.js +1 -217
- package/dist/C_QRCode2.js.map +1 -1
- package/dist/C_Signature.cjs +1 -8
- package/dist/C_Signature.js +1 -4
- package/dist/C_Signature.js.map +1 -0
- package/dist/C_Signature2.js +1 -617
- package/dist/C_Signature2.js.map +1 -1
- package/dist/C_SplitPane.cjs +1 -6
- package/dist/C_SplitPane.js +1 -4
- package/dist/C_SplitPane.js.map +1 -0
- package/dist/C_SplitPane2.js +1 -355
- package/dist/C_SplitPane2.js.map +1 -1
- package/dist/C_Steps.cjs +1 -4
- package/dist/C_Steps.js +1 -3
- package/dist/C_Steps.js.map +1 -0
- package/dist/C_Steps2.js +1 -81
- package/dist/C_Steps2.js.map +1 -1
- package/dist/{C_Table-C55H4kPr.css.map → C_Table-tbUx-R4H.css.map} +1 -1
- package/dist/C_Table.cjs +1 -20
- package/dist/C_Table.js +1 -6
- package/dist/C_Table.js.map +1 -0
- package/dist/C_Table2.js +1 -3023
- package/dist/C_Table2.js.map +1 -1
- package/dist/C_Theme.cjs +1 -5
- package/dist/C_Theme.js +1 -4
- package/dist/C_Theme.js.map +1 -0
- package/dist/C_Theme2.js +1 -63
- package/dist/C_Theme2.js.map +1 -1
- package/dist/C_Time.cjs +1 -5
- package/dist/C_Time.js +1 -3
- package/dist/C_Time.js.map +1 -0
- package/dist/C_Time2.js +1 -198
- package/dist/C_Time2.js.map +1 -1
- package/dist/C_Tree.cjs +1 -7
- package/dist/C_Tree.js +1 -4
- package/dist/C_Tree.js.map +1 -0
- package/dist/C_Tree2.js +1 -440
- package/dist/C_Tree2.js.map +1 -1
- package/dist/C_Upload.cjs +1 -12
- package/dist/C_Upload.js +1 -4
- package/dist/C_Upload.js.map +1 -0
- package/dist/C_Upload2.js +2 -1365
- package/dist/C_Upload2.js.map +1 -1
- package/dist/C_VideoPlayer.cjs +1 -23
- package/dist/C_VideoPlayer.js +1 -3
- package/dist/C_VideoPlayer.js.map +1 -0
- package/dist/C_VideoPlayer2.js +3 -1932
- package/dist/C_VideoPlayer2.js.map +1 -1
- package/dist/C_VtableGantt.cjs +1 -6
- package/dist/C_VtableGantt.js +1 -4
- package/dist/C_VtableGantt.js.map +1 -0
- package/dist/C_VtableGantt2.js +1 -872
- package/dist/C_VtableGantt2.js.map +1 -1
- package/dist/C_WaterFall.cjs +1 -13
- package/dist/C_WaterFall.js +1 -3
- package/dist/C_WaterFall.js.map +1 -0
- package/dist/C_WaterFall2.js +1 -364
- package/dist/C_WaterFall2.js.map +1 -1
- package/dist/{C_WorkFlow-CSO86Cuc.css.map → C_WorkFlow-7F6op6qN.css.map} +1 -1
- package/dist/C_WorkFlow.cjs +1 -8
- package/dist/C_WorkFlow.js +1 -4
- package/dist/C_WorkFlow.js.map +1 -0
- package/dist/C_WorkFlow2.js +1 -1983
- package/dist/C_WorkFlow2.js.map +1 -1
- package/dist/chunk.js +1 -18
- package/dist/city.js +1 -4816
- package/dist/constants.d.ts +1 -1
- package/dist/constants2.d.ts +2 -2
- package/dist/constants4.d.ts +8 -8
- package/dist/constants5.d.ts +2 -2
- package/dist/data.d.ts +2 -2
- package/dist/export-helper.js +1 -9
- package/dist/index.cjs +2 -413
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -231
- package/dist/index.js.map +1 -1
- package/dist/index.vue.d.ts +2 -2
- package/dist/index10.vue.d.ts +6 -6
- package/dist/index12.vue.d.ts +4 -4
- package/dist/index13.vue.d.ts +1 -1
- package/dist/index14.vue.d.ts +1 -1
- package/dist/index15.vue.d.ts +1 -1
- package/dist/index16.vue.d.ts +2 -2
- package/dist/index2.vue.d.ts +2 -2
- package/dist/index3.vue.d.ts +4 -4
- package/dist/index5.vue.d.ts +1 -1
- package/dist/index7.vue.d.ts +4 -4
- package/dist/index8.vue.d.ts +3 -3
- package/dist/index9.vue.d.ts +1 -1
- package/dist/resolver.cjs +2 -89
- package/dist/resolver.cjs.map +1 -0
- package/dist/resolver.js +1 -86
- package/dist/resolver.js.map +1 -1
- package/dist/storage.js +1 -30
- package/dist/storage.js.map +1 -1
- package/dist/style.css +447 -447
- package/dist/useCalendarEvents.d.ts +1 -1
- package/dist/useCollapsePanel.d.ts +2 -2
- package/dist/useCropperCore.d.ts +3 -3
- package/dist/useDraggableLayout.d.ts +9 -9
- package/dist/useDynamicFormState.d.ts +5 -5
- package/dist/useEdgeInteraction.d.ts +4 -12
- package/dist/useInfiniteScroll.d.ts +2 -2
- package/dist/useModalEdit.d.ts +45 -21
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSearchState.d.ts +2 -2
- package/dist/useSignatureHistory.d.ts +2 -2
- package/dist/useSplitResize.d.ts +6 -6
- package/dist/useTimeSelection.d.ts +2 -2
- package/dist/useTreeOperations.d.ts +6 -6
- package/dist/useWorkflowValidation.d.ts +3 -3
- package/package.json +1 -2
- package/dist/C_FullCalendar-BULCIlVK.css.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants2.d.ts.map +0 -1
- package/dist/constants3.d.ts.map +0 -1
- package/dist/constants4.d.ts.map +0 -1
- package/dist/constants5.d.ts.map +0 -1
- package/dist/data.d.ts.map +0 -1
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.vue.d.ts.map +0 -1
- package/dist/index10.vue.d.ts.map +0 -1
- package/dist/index11.vue.d.ts.map +0 -1
- package/dist/index12.vue.d.ts.map +0 -1
- package/dist/index13.vue.d.ts.map +0 -1
- package/dist/index14.vue.d.ts.map +0 -1
- package/dist/index15.vue.d.ts.map +0 -1
- package/dist/index16.vue.d.ts.map +0 -1
- package/dist/index2.vue.d.ts.map +0 -1
- package/dist/index3.vue.d.ts.map +0 -1
- package/dist/index4.vue.d.ts.map +0 -1
- package/dist/index5.vue.d.ts.map +0 -1
- package/dist/index6.vue.d.ts.map +0 -1
- package/dist/index7.vue.d.ts.map +0 -1
- package/dist/index8.vue.d.ts.map +0 -1
- package/dist/index9.vue.d.ts.map +0 -1
- package/dist/resolver.d.cts.map +0 -1
- package/dist/resolver.d.ts.map +0 -1
- package/dist/useCalendarEvents.d.ts.map +0 -1
- package/dist/useCollapsePanel.d.ts.map +0 -1
- package/dist/useCropperCore.d.ts.map +0 -1
- package/dist/useDraggableLayout.d.ts.map +0 -1
- package/dist/useDynamicFormState.d.ts.map +0 -1
- package/dist/useEdgeInteraction.d.ts.map +0 -1
- package/dist/useFullscreen.d.ts.map +0 -1
- package/dist/useGlobalSearch.d.ts.map +0 -1
- package/dist/useInfiniteScroll.d.ts.map +0 -1
- package/dist/useModalEdit.d.ts.map +0 -1
- package/dist/useQRCode.d.ts.map +0 -1
- package/dist/useSearchState.d.ts.map +0 -1
- package/dist/useSignatureHistory.d.ts.map +0 -1
- package/dist/useSplitResize.d.ts.map +0 -1
- package/dist/useTimeSelection.d.ts.map +0 -1
- package/dist/useTreeOperations.d.ts.map +0 -1
- package/dist/useWorkflowValidation.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"C_FilePreview.js","names":["XLSX","totalPages","pdfUrl","headings","content","sheets","$emit"],"sources":["../src/components/C_FilePreview/data.ts","../src/components/C_FilePreview/components/PdfViewer/index.vue","../src/components/C_FilePreview/components/PdfViewer/index.vue","../src/components/C_FilePreview/components/PdfViewer/index.vue","../src/components/C_FilePreview/components/WordViewer/index.vue","../src/components/C_FilePreview/components/WordViewer/index.vue","../src/components/C_FilePreview/components/WordViewer/index.vue","../src/components/C_FilePreview/components/ExcelViewer/index.vue","../src/components/C_FilePreview/components/ExcelViewer/index.vue","../src/components/C_FilePreview/components/ExcelViewer/index.vue","../src/components/C_FilePreview/composables/useFilePreview.ts","../src/components/C_FilePreview/composables/useFullscreen.ts","../src/components/C_FilePreview/index.vue","../src/components/C_FilePreview/index.vue","../src/components/C_FilePreview/index.vue"],"sourcesContent":["/**\r\n * C_FilePreview 常量配置与工具函数\r\n * 类型定义统一在 ./types.ts\r\n */\r\nimport { type Ref } from \"vue\";\r\nimport * as XLSX from \"xlsx\";\r\nimport mammoth from \"mammoth\";\r\nimport type {\r\n FilePreviewType,\r\n FileConfig,\r\n ZoomConfig,\r\n ExcelRow,\r\n ExcelColumn,\r\n ExcelSheet,\r\n DocHeading,\r\n PdfLoadResult,\r\n WordLoadResult,\r\n ExcelLoadResult,\r\n} from \"./types\";\r\n\r\n/* ==================== 常量配置 ==================== */\r\n\r\nexport const FILE_TYPE_MAP: Record<string, FilePreviewType> = {\r\n pdf: \"pdf\",\r\n doc: \"word\",\r\n docx: \"word\",\r\n xls: \"excel\",\r\n xlsx: \"excel\",\r\n};\r\n\r\nexport const FILE_CONFIGS: Record<string, FileConfig> = {\r\n pdf: {\r\n tagType: \"error\",\r\n icon: \"ic:outline-picture-as-pdf\",\r\n color: \"#dc2626\",\r\n },\r\n word: { tagType: \"info\", icon: \"ic:outline-description\", color: \"#2563eb\" },\r\n excel: { tagType: \"success\", icon: \"ic:outline-grid-on\", color: \"#16a34a\" },\r\n unknown: {\r\n tagType: \"default\",\r\n icon: \"ic:outline-insert-drive-file\",\r\n color: \"#6b7280\",\r\n },\r\n};\r\n\r\nexport const ZOOM_CONFIGS: Record<string, ZoomConfig> = {\r\n pdf: { min: 0.5, max: 3, step: 0.25, default: 1 },\r\n word: { min: 50, max: 200, step: 10, default: 100 },\r\n};\r\n\r\nexport const PAGE_SIZE_OPTIONS = [20, 50, 100, 200];\r\n\r\nexport const FULLSCREEN_EVENTS = [\r\n \"fullscreenchange\",\r\n \"webkitfullscreenchange\",\r\n \"mozfullscreenchange\",\r\n \"MSFullscreenChange\",\r\n];\r\n\r\n/* ==================== 文件信息工具函数 ==================== */\r\n\r\nexport const extractFileNameFromUrl = (url: string): string => {\r\n try {\r\n const urlPath = url.split(\"/\").pop() || url.split(\"\\\\\").pop();\r\n if (urlPath?.includes(\".\")) {\r\n const decodedName = decodeURIComponent(urlPath.split(\"?\")[0]);\r\n if (decodedName && decodedName !== url) return decodedName;\r\n }\r\n } catch (error) {\r\n console.warn(\"解析URL文件名失败:\", error);\r\n }\r\n return \"未知文件\";\r\n};\r\n\r\nexport const formatFileSize = (bytes: number): string => {\r\n if (bytes === 0) return \"0 B\";\r\n const k = 1024;\r\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(k));\r\n return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + \" \" + sizes[i];\r\n};\r\n\r\nexport const getFileType = (fileName: string): FilePreviewType => {\r\n if (!fileName || fileName === \"未知文件\") return \"unknown\";\r\n const ext = fileName.toLowerCase().split(\".\").pop();\r\n if (!ext) return \"unknown\";\r\n return FILE_TYPE_MAP[ext] || \"unknown\";\r\n};\r\n\r\nexport const getFileConfig = (fileType: string): FileConfig => {\r\n return FILE_CONFIGS[fileType] || FILE_CONFIGS.unknown;\r\n};\r\n\r\n/* ==================== 缩放工具 ==================== */\r\n\r\nexport const createZoomHandler = (\r\n valueRef: Ref<number>,\r\n config: ZoomConfig,\r\n) => {\r\n return (action: \"in\" | \"out\" | \"reset\") => {\r\n const { min, max, step, default: defaultValue } = config;\r\n switch (action) {\r\n case \"reset\":\r\n valueRef.value = defaultValue;\r\n break;\r\n case \"in\":\r\n if (valueRef.value < max) valueRef.value += step;\r\n break;\r\n case \"out\":\r\n if (valueRef.value > min) valueRef.value -= step;\r\n break;\r\n }\r\n };\r\n};\r\n\r\n/* ==================== Excel 工具函数 ==================== */\r\n\r\nexport const getColumnLetter = (index: number): string => {\r\n let result = \"\";\r\n while (index >= 0) {\r\n result = String.fromCharCode(65 + (index % 26)) + result;\r\n index = Math.floor(index / 26) - 1;\r\n }\r\n return result;\r\n};\r\n\r\nexport const formatCellValue = (value: any): string => {\r\n if (!value) return \"\";\r\n const strValue = String(value).trim();\r\n\r\n if (!isNaN(Number(strValue)) && strValue !== \"\" && strValue.length < 15) {\r\n const num = Number(strValue);\r\n return Number.isInteger(num)\r\n ? num.toLocaleString()\r\n : num.toLocaleString(undefined, { maximumFractionDigits: 2 });\r\n }\r\n return strValue;\r\n};\r\n\r\nexport const getCellClass = (value: any): string => {\r\n if (!value) return \"cell-empty\";\r\n const strValue = String(value).trim();\r\n\r\n if (!isNaN(Number(strValue)) && strValue !== \"\") return \"cell-number\";\r\n if (strValue.match(/[■▬▪▫─━]/)) return \"cell-gantt\";\r\n if (\r\n strValue.match(/^\\d{4}[-/]\\d{2}[-/]\\d{2}/) ||\r\n strValue.match(/^\\d{2}[-/]\\d{2}[-/]\\d{4}/)\r\n )\r\n return \"cell-date\";\r\n if ([\"true\", \"false\"].includes(strValue.toLowerCase())) return \"cell-boolean\";\r\n return strValue.length > 20 ? \"cell-text cell-long\" : \"cell-text\";\r\n};\r\n\r\nexport const processExcelSheet = (\r\n worksheet: any,\r\n merges: any[],\r\n): { data: ExcelRow[]; columns: ExcelColumn[] } => {\r\n const range = XLSX.utils.decode_range(worksheet[\"!ref\"] || \"A1:A1\");\r\n const { c: maxCol, r: maxRow } = range.e;\r\n\r\n const columns: ExcelColumn[] = Array.from({ length: maxCol + 1 }, (_, c) => {\r\n const cellAddress = XLSX.utils.encode_cell({ r: 0, c });\r\n const cell = worksheet[cellAddress];\r\n const columnName = cell ? String(cell.v || \"\").trim() : `列${c + 1}`;\r\n return {\r\n title: columnName || `列${c + 1}`,\r\n key: `col_${c}`,\r\n width: Math.min(Math.max(columnName.length * 12, 80), 200),\r\n };\r\n });\r\n\r\n const mergeMap = new Map();\r\n merges.forEach(({ s: { r: sRow, c: sCol }, e: { r: eRow, c: eCol } }) => {\r\n for (let r = sRow; r <= eRow; r++) {\r\n for (let c = sCol; c <= eCol; c++) {\r\n mergeMap.set(`${r}-${c}`, {\r\n isMain: r === sRow && c === sCol,\r\n rowspan: eRow - sRow + 1,\r\n colspan: eCol - sCol + 1,\r\n });\r\n }\r\n }\r\n });\r\n\r\n const data: ExcelRow[] = Array.from({ length: maxRow + 1 }, (_, r) => {\r\n const row: ExcelRow = {};\r\n for (let c = 0; c <= maxCol; c++) {\r\n const cellAddress = XLSX.utils.encode_cell({ r, c });\r\n const cell = worksheet[cellAddress];\r\n const mergeInfo = mergeMap.get(`${r}-${c}`);\r\n\r\n row[`col_${c}`] = {\r\n value: cell ? cell.v || \"\" : \"\",\r\n merged: !!mergeInfo,\r\n hidden: mergeInfo && !mergeInfo.isMain,\r\n rowspan: mergeInfo?.isMain ? mergeInfo.rowspan : 1,\r\n colspan: mergeInfo?.isMain ? mergeInfo.colspan : 1,\r\n };\r\n }\r\n return row;\r\n });\r\n\r\n return { data, columns };\r\n};\r\n\r\n/* ==================== 文件加载器 ==================== */\r\n\r\nexport async function loadPdf(file: File): Promise<PdfLoadResult> {\r\n const fileUrl = URL.createObjectURL(file);\r\n return {\r\n url: fileUrl + \"#toolbar=1&navpanes=1&scrollbar=1&view=FitH\",\r\n totalPages: 1,\r\n };\r\n}\r\n\r\nexport async function loadWord(file: File): Promise<WordLoadResult> {\r\n const arrayBuffer = await file.arrayBuffer();\r\n const result = await mammoth.convertToHtml({\r\n arrayBuffer,\r\n styleMap: [\r\n \"p[style-name='Heading 1'] => h1:fresh\",\r\n \"p[style-name='Heading 2'] => h2:fresh\",\r\n \"p[style-name='Heading 3'] => h3:fresh\",\r\n \"p[style-name='Title'] => h1.title:fresh\",\r\n ],\r\n } as any);\r\n\r\n let content = result.value;\r\n const headings: DocHeading[] = [];\r\n\r\n /* 提取标题并注入 id */\r\n const tempDiv = document.createElement(\"div\");\r\n tempDiv.innerHTML = content;\r\n const headingElements = tempDiv.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\r\n\r\n Array.from(headingElements).forEach((el, index) => {\r\n const id = `heading-${index}`;\r\n const level = parseInt(el.tagName.charAt(1));\r\n const text = el.textContent || \"\";\r\n\r\n content = content.replace(\r\n el.outerHTML,\r\n el.outerHTML.replace(\r\n el.tagName.toLowerCase(),\r\n `${el.tagName.toLowerCase()} id=\"${id}\"`,\r\n ),\r\n );\r\n\r\n headings.push({ id, text, level });\r\n });\r\n\r\n return { content, headings };\r\n}\r\n\r\nexport async function loadExcel(file: File): Promise<ExcelLoadResult> {\r\n const arrayBuffer = await file.arrayBuffer();\r\n const workbook = XLSX.read(arrayBuffer, {\r\n type: \"array\",\r\n cellStyles: true,\r\n cellFormula: true,\r\n cellDates: true,\r\n });\r\n\r\n if (!workbook.SheetNames.length) {\r\n throw new Error(\"Excel文件中没有找到工作表\");\r\n }\r\n\r\n const sheets: ExcelSheet[] = workbook.SheetNames.map((name) => {\r\n const worksheet = workbook.Sheets[name];\r\n const merges = worksheet[\"!merges\"] || [];\r\n const { data, columns } = processExcelSheet(worksheet, merges);\r\n return { name, data, merges, columns };\r\n });\r\n\r\n return { sheets };\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n PdfViewer — PDF 文件预览子组件\r\n 负责: 工具栏(翻页+缩放) + iframe 渲染\r\n-->\r\n<template>\r\n <div class=\"file-container pdf-container\">\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex justify-between items-center\">\r\n <!-- 翻页控制 -->\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"currentPage <= 1\"\r\n @click=\"changePage('prev')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-chevron-left\" />\r\n </template>\r\n </NButton>\r\n <NInputNumber\r\n v-model:value=\"currentPage\"\r\n size=\"small\"\r\n :min=\"1\"\r\n :max=\"totalPages\"\r\n style=\"width: 80px\"\r\n @update:value=\"changePage as any\"\r\n />\r\n <span class=\"text-sm text-gray-600\">/ {{ totalPages }}</span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"currentPage >= totalPages\"\r\n @click=\"changePage('next')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-chevron-right\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n\r\n <!-- 缩放控制 -->\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"scale <= 0.5\"\r\n @click=\"adjustZoom('out')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-out\" />\r\n </template>\r\n </NButton>\r\n <span class=\"text-sm text-gray-600 min-w-12 text-center\">\r\n {{ Math.round(scale * 100) }}%\r\n </span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"scale >= 3\"\r\n @click=\"adjustZoom('in')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-in\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n @click=\"adjustZoom('reset')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-fit-screen\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"file-viewer\">\r\n <iframe\r\n v-if=\"pdfUrl\"\r\n :src=\"pdfUrl\"\r\n class=\"w-full h-full border-0\"\r\n title=\"PDF预览\"\r\n frameborder=\"0\"\r\n allowfullscreen\r\n />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref } from 'vue'\r\n import { NButton, NInputNumber } from 'naive-ui'\r\n import { createZoomHandler, ZOOM_CONFIGS } from '../../data'\r\n\r\n const props = defineProps<{\r\n pdfUrl: string\r\n totalPages: number\r\n }>()\r\n\r\n const currentPage = ref(1)\r\n const scale = ref(1)\r\n\r\n const adjustZoom = createZoomHandler(scale, ZOOM_CONFIGS.pdf)\r\n\r\n const changePage = (action: 'prev' | 'next' | number) => {\r\n if (typeof action === 'number') {\r\n currentPage.value = Math.max(1, Math.min(action, props.totalPages))\r\n } else {\r\n const delta = action === 'prev' ? -1 : 1\r\n currentPage.value = Math.max(\r\n 1,\r\n Math.min(currentPage.value + delta, props.totalPages)\r\n )\r\n }\r\n }\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n PdfViewer — PDF 文件预览子组件\r\n 负责: 工具栏(翻页+缩放) + iframe 渲染\r\n-->\r\n<template>\r\n <div class=\"file-container pdf-container\">\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex justify-between items-center\">\r\n <!-- 翻页控制 -->\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"currentPage <= 1\"\r\n @click=\"changePage('prev')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-chevron-left\" />\r\n </template>\r\n </NButton>\r\n <NInputNumber\r\n v-model:value=\"currentPage\"\r\n size=\"small\"\r\n :min=\"1\"\r\n :max=\"totalPages\"\r\n style=\"width: 80px\"\r\n @update:value=\"changePage as any\"\r\n />\r\n <span class=\"text-sm text-gray-600\">/ {{ totalPages }}</span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"currentPage >= totalPages\"\r\n @click=\"changePage('next')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-chevron-right\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n\r\n <!-- 缩放控制 -->\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"scale <= 0.5\"\r\n @click=\"adjustZoom('out')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-out\" />\r\n </template>\r\n </NButton>\r\n <span class=\"text-sm text-gray-600 min-w-12 text-center\">\r\n {{ Math.round(scale * 100) }}%\r\n </span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"scale >= 3\"\r\n @click=\"adjustZoom('in')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-in\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n @click=\"adjustZoom('reset')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-fit-screen\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"file-viewer\">\r\n <iframe\r\n v-if=\"pdfUrl\"\r\n :src=\"pdfUrl\"\r\n class=\"w-full h-full border-0\"\r\n title=\"PDF预览\"\r\n frameborder=\"0\"\r\n allowfullscreen\r\n />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref } from 'vue'\r\n import { NButton, NInputNumber } from 'naive-ui'\r\n import { createZoomHandler, ZOOM_CONFIGS } from '../../data'\r\n\r\n const props = defineProps<{\r\n pdfUrl: string\r\n totalPages: number\r\n }>()\r\n\r\n const currentPage = ref(1)\r\n const scale = ref(1)\r\n\r\n const adjustZoom = createZoomHandler(scale, ZOOM_CONFIGS.pdf)\r\n\r\n const changePage = (action: 'prev' | 'next' | number) => {\r\n if (typeof action === 'number') {\r\n currentPage.value = Math.max(1, Math.min(action, props.totalPages))\r\n } else {\r\n const delta = action === 'prev' ? -1 : 1\r\n currentPage.value = Math.max(\r\n 1,\r\n Math.min(currentPage.value + delta, props.totalPages)\r\n )\r\n }\r\n }\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n @use './index.scss';\r\n</style>\r\n","<!--\r\n PdfViewer — PDF 文件预览子组件\r\n 负责: 工具栏(翻页+缩放) + iframe 渲染\r\n-->\r\n<template>\r\n <div class=\"file-container pdf-container\">\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex justify-between items-center\">\r\n <!-- 翻页控制 -->\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"currentPage <= 1\"\r\n @click=\"changePage('prev')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-chevron-left\" />\r\n </template>\r\n </NButton>\r\n <NInputNumber\r\n v-model:value=\"currentPage\"\r\n size=\"small\"\r\n :min=\"1\"\r\n :max=\"totalPages\"\r\n style=\"width: 80px\"\r\n @update:value=\"changePage as any\"\r\n />\r\n <span class=\"text-sm text-gray-600\">/ {{ totalPages }}</span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"currentPage >= totalPages\"\r\n @click=\"changePage('next')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-chevron-right\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n\r\n <!-- 缩放控制 -->\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"scale <= 0.5\"\r\n @click=\"adjustZoom('out')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-out\" />\r\n </template>\r\n </NButton>\r\n <span class=\"text-sm text-gray-600 min-w-12 text-center\">\r\n {{ Math.round(scale * 100) }}%\r\n </span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"scale >= 3\"\r\n @click=\"adjustZoom('in')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-in\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n @click=\"adjustZoom('reset')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-fit-screen\" />\r\n </template>\r\n 适应\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"file-viewer\">\r\n <iframe\r\n v-if=\"pdfUrl\"\r\n :src=\"pdfUrl\"\r\n class=\"w-full h-full border-0\"\r\n title=\"PDF预览\"\r\n frameborder=\"0\"\r\n allowfullscreen\r\n />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref } from 'vue'\r\n import { NButton, NInputNumber } from 'naive-ui'\r\n import { createZoomHandler, ZOOM_CONFIGS } from '../../data'\r\n\r\n const props = defineProps<{\r\n pdfUrl: string\r\n totalPages: number\r\n }>()\r\n\r\n const currentPage = ref(1)\r\n const scale = ref(1)\r\n\r\n const adjustZoom = createZoomHandler(scale, ZOOM_CONFIGS.pdf)\r\n\r\n const changePage = (action: 'prev' | 'next' | number) => {\r\n if (typeof action === 'number') {\r\n currentPage.value = Math.max(1, Math.min(action, props.totalPages))\r\n } else {\r\n const delta = action === 'prev' ? -1 : 1\r\n currentPage.value = Math.max(\r\n 1,\r\n Math.min(currentPage.value + delta, props.totalPages)\r\n )\r\n }\r\n }\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n WordViewer — Word 文档预览子组件\r\n 负责: 目录导航 + 缩放 + 文档内容渲染\r\n-->\r\n<template>\r\n <div class=\"file-container word-container\">\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton size=\"small\" @click=\"showOutline = !showOutline\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-list\" />\r\n </template>\r\n {{ showOutline ? \"隐藏\" : \"显示\" }}目录\r\n </NButton>\r\n <NDivider vertical />\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"wordZoom <= 50\"\r\n @click=\"adjustWordZoom('out')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-out\" />\r\n </template>\r\n </NButton>\r\n <span class=\"text-sm text-gray-600 min-w-12 text-center\">\r\n {{ wordZoom }}%\r\n </span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"wordZoom >= 200\"\r\n @click=\"adjustWordZoom('in')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-in\" />\r\n </template>\r\n </NButton>\r\n <NButton size=\"small\" @click=\"adjustWordZoom('reset')\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-fit-screen\" />\r\n </template>\r\n 重置\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"word-layout\">\r\n <div class=\"word-main\">\r\n <!-- 侧边栏目录 -->\r\n <div v-if=\"showOutline\" class=\"word-outline\">\r\n <div class=\"outline-header\">\r\n <h3 class=\"text-sm font-semibold\">文档目录</h3>\r\n </div>\r\n <div class=\"outline-content\">\r\n <div\r\n v-for=\"(heading, index) in headings\"\r\n :key=\"index\"\r\n class=\"outline-item\"\r\n :class=\"`level-${heading.level}`\"\r\n @click=\"scrollToHeading(heading.id)\"\r\n >\r\n {{ heading.text }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主要内容区域 -->\r\n <div class=\"word-content\">\r\n <div\r\n class=\"word-document\"\r\n :style=\"{\r\n transform: `scale(${wordZoom / 100})`,\r\n transformOrigin: 'top center',\r\n }\"\r\n v-html=\"content\"\r\n ></div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref } from \"vue\";\r\nimport { NButton, NDivider } from \"naive-ui\";\r\nimport { createZoomHandler, ZOOM_CONFIGS } from \"../../data\";\r\nimport type { DocHeading } from \"../../types\";\r\n\r\ndefineProps<{\r\n content: string;\r\n headings: DocHeading[];\r\n}>();\r\n\r\nconst showOutline = ref(true);\r\nconst wordZoom = ref(100);\r\n\r\nconst adjustWordZoom = createZoomHandler(wordZoom, ZOOM_CONFIGS.word);\r\n\r\nconst scrollToHeading = (headingId: string) => {\r\n document\r\n .getElementById(headingId)\r\n ?.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n WordViewer — Word 文档预览子组件\r\n 负责: 目录导航 + 缩放 + 文档内容渲染\r\n-->\r\n<template>\r\n <div class=\"file-container word-container\">\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton size=\"small\" @click=\"showOutline = !showOutline\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-list\" />\r\n </template>\r\n {{ showOutline ? \"隐藏\" : \"显示\" }}目录\r\n </NButton>\r\n <NDivider vertical />\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"wordZoom <= 50\"\r\n @click=\"adjustWordZoom('out')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-out\" />\r\n </template>\r\n </NButton>\r\n <span class=\"text-sm text-gray-600 min-w-12 text-center\">\r\n {{ wordZoom }}%\r\n </span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"wordZoom >= 200\"\r\n @click=\"adjustWordZoom('in')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-in\" />\r\n </template>\r\n </NButton>\r\n <NButton size=\"small\" @click=\"adjustWordZoom('reset')\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-fit-screen\" />\r\n </template>\r\n 重置\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"word-layout\">\r\n <div class=\"word-main\">\r\n <!-- 侧边栏目录 -->\r\n <div v-if=\"showOutline\" class=\"word-outline\">\r\n <div class=\"outline-header\">\r\n <h3 class=\"text-sm font-semibold\">文档目录</h3>\r\n </div>\r\n <div class=\"outline-content\">\r\n <div\r\n v-for=\"(heading, index) in headings\"\r\n :key=\"index\"\r\n class=\"outline-item\"\r\n :class=\"`level-${heading.level}`\"\r\n @click=\"scrollToHeading(heading.id)\"\r\n >\r\n {{ heading.text }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主要内容区域 -->\r\n <div class=\"word-content\">\r\n <div\r\n class=\"word-document\"\r\n :style=\"{\r\n transform: `scale(${wordZoom / 100})`,\r\n transformOrigin: 'top center',\r\n }\"\r\n v-html=\"content\"\r\n ></div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref } from \"vue\";\r\nimport { NButton, NDivider } from \"naive-ui\";\r\nimport { createZoomHandler, ZOOM_CONFIGS } from \"../../data\";\r\nimport type { DocHeading } from \"../../types\";\r\n\r\ndefineProps<{\r\n content: string;\r\n headings: DocHeading[];\r\n}>();\r\n\r\nconst showOutline = ref(true);\r\nconst wordZoom = ref(100);\r\n\r\nconst adjustWordZoom = createZoomHandler(wordZoom, ZOOM_CONFIGS.word);\r\n\r\nconst scrollToHeading = (headingId: string) => {\r\n document\r\n .getElementById(headingId)\r\n ?.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<!--\r\n WordViewer — Word 文档预览子组件\r\n 负责: 目录导航 + 缩放 + 文档内容渲染\r\n-->\r\n<template>\r\n <div class=\"file-container word-container\">\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton size=\"small\" @click=\"showOutline = !showOutline\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-list\" />\r\n </template>\r\n {{ showOutline ? \"隐藏\" : \"显示\" }}目录\r\n </NButton>\r\n <NDivider vertical />\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"wordZoom <= 50\"\r\n @click=\"adjustWordZoom('out')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-out\" />\r\n </template>\r\n </NButton>\r\n <span class=\"text-sm text-gray-600 min-w-12 text-center\">\r\n {{ wordZoom }}%\r\n </span>\r\n <NButton\r\n size=\"small\"\r\n :disabled=\"wordZoom >= 200\"\r\n @click=\"adjustWordZoom('in')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-zoom-in\" />\r\n </template>\r\n </NButton>\r\n <NButton size=\"small\" @click=\"adjustWordZoom('reset')\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-fit-screen\" />\r\n </template>\r\n 重置\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"word-layout\">\r\n <div class=\"word-main\">\r\n <!-- 侧边栏目录 -->\r\n <div v-if=\"showOutline\" class=\"word-outline\">\r\n <div class=\"outline-header\">\r\n <h3 class=\"text-sm font-semibold\">文档目录</h3>\r\n </div>\r\n <div class=\"outline-content\">\r\n <div\r\n v-for=\"(heading, index) in headings\"\r\n :key=\"index\"\r\n class=\"outline-item\"\r\n :class=\"`level-${heading.level}`\"\r\n @click=\"scrollToHeading(heading.id)\"\r\n >\r\n {{ heading.text }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主要内容区域 -->\r\n <div class=\"word-content\">\r\n <div\r\n class=\"word-document\"\r\n :style=\"{\r\n transform: `scale(${wordZoom / 100})`,\r\n transformOrigin: 'top center',\r\n }\"\r\n v-html=\"content\"\r\n ></div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref } from \"vue\";\r\nimport { NButton, NDivider } from \"naive-ui\";\r\nimport { createZoomHandler, ZOOM_CONFIGS } from \"../../data\";\r\nimport type { DocHeading } from \"../../types\";\r\n\r\ndefineProps<{\r\n content: string;\r\n headings: DocHeading[];\r\n}>();\r\n\r\nconst showOutline = ref(true);\r\nconst wordZoom = ref(100);\r\n\r\nconst adjustWordZoom = createZoomHandler(wordZoom, ZOOM_CONFIGS.word);\r\n\r\nconst scrollToHeading = (headingId: string) => {\r\n document\r\n .getElementById(headingId)\r\n ?.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n ExcelViewer — Excel 表格预览子组件\r\n 负责: 工作表切换 + 表格渲染(合并单元格) + 分页 + 格式切换\r\n-->\r\n<template>\r\n <div class=\"file-container excel-container\">\r\n <!-- 工具栏 -->\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex justify-between items-center\">\r\n <div class=\"flex gap-2 items-center\">\r\n <NTabs\r\n v-if=\"sheets.length > 1\"\r\n v-model:value=\"activeSheet\"\r\n type=\"card\"\r\n size=\"small\"\r\n >\r\n <NTabPane\r\n v-for=\"sheet in sheets\"\r\n :key=\"sheet.name\"\r\n :name=\"sheet.name\"\r\n :tab=\"sheet.name\"\r\n />\r\n </NTabs>\r\n <span v-else class=\"text-sm text-gray-600\">\r\n 工作表: {{ activeSheet }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton size=\"small\" @click=\"showExcelFormat = !showExcelFormat\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-format-paint\" />\r\n </template>\r\n {{ showExcelFormat ? \"紧凑视图\" : \"完整格式\" }}\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"excel-viewer\">\r\n <!-- 信息面板 -->\r\n <div v-if=\"currentData.length > 0\" class=\"excel-info\">\r\n <NText depth=\"3\" class=\"text-sm\">\r\n 当前工作表: {{ activeSheet }} | 总行数: {{ currentData.length }} |\r\n 总列数: {{ currentColumns.length }} | 当前页:\r\n {{ currentExcelPage }}/{{ totalExcelPages }}\r\n </NText>\r\n </div>\r\n\r\n <!-- 表格 -->\r\n <div class=\"excel-table-container\">\r\n <div\r\n class=\"excel-table-wrapper\"\r\n :class=\"{ 'simple-mode': !showExcelFormat }\"\r\n >\r\n <table v-if=\"currentData.length > 0\" class=\"excel-table\">\r\n <thead>\r\n <tr>\r\n <th class=\"row-number\">行号</th>\r\n <th\r\n v-for=\"(col, index) in currentColumns\"\r\n :key=\"col.key\"\r\n class=\"excel-header\"\r\n :style=\"{ minWidth: col.width + 'px' }\"\r\n >\r\n <div class=\"header-content\">\r\n <span class=\"column-letter\">{{\r\n getColumnLetter(index)\r\n }}</span>\r\n <span class=\"column-title\">{{ col.title }}</span>\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr\r\n v-for=\"(row, rowIndex) in paginatedData\"\r\n :key=\"rowIndex\"\r\n class=\"excel-row\"\r\n >\r\n <td class=\"row-number\">\r\n {{ (currentExcelPage - 1) * pageSize + rowIndex + 1 }}\r\n </td>\r\n <td\r\n v-for=\"col in currentColumns\"\r\n :key=\"col.key\"\r\n v-show=\"!row[col.key]?.hidden\"\r\n class=\"excel-cell\"\r\n :class=\"[\r\n getCellClass(row[col.key]?.value),\r\n row[col.key]?.merged ? 'merged-cell' : '',\r\n !showExcelFormat ? 'compact-cell' : '',\r\n ]\"\r\n :rowspan=\"row[col.key]?.rowspan || 1\"\r\n :colspan=\"row[col.key]?.colspan || 1\"\r\n :title=\"row[col.key]?.value\"\r\n >\r\n {{ formatCellValue(row[col.key]?.value) }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div v-else class=\"excel-empty\">\r\n <NEmpty description=\"该工作表没有数据或解析失败\">\r\n <template #extra>\r\n <NButton size=\"small\" @click=\"$emit('reload')\">\r\n 重新解析\r\n </NButton>\r\n </template>\r\n </NEmpty>\r\n </div>\r\n </div>\r\n\r\n <!-- 分页 -->\r\n <div v-if=\"totalExcelPages > 1\" class=\"excel-pagination\">\r\n <NPagination\r\n v-model:page=\"currentExcelPage\"\r\n :page-count=\"totalExcelPages\"\r\n :page-size=\"pageSize\"\r\n show-size-picker\r\n :page-sizes=\"PAGE_SIZE_OPTIONS\"\r\n @update:page-size=\"handlePageSizeChange\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from \"vue\";\r\nimport { NButton, NTabs, NTabPane, NText, NEmpty, NPagination } from \"naive-ui\";\r\nimport {\r\n getColumnLetter,\r\n formatCellValue,\r\n getCellClass,\r\n PAGE_SIZE_OPTIONS,\r\n} from \"../../data\";\r\nimport type { ExcelSheet, ExcelRow, ExcelColumn } from \"../../types\";\r\n\r\nconst props = defineProps<{\r\n sheets: ExcelSheet[];\r\n}>();\r\n\r\ndefineEmits<{\r\n reload: [];\r\n}>();\r\n\r\n/* ==================== 内部状态 ==================== */\r\nconst activeSheet = ref(\"\");\r\nconst showExcelFormat = ref(false);\r\nconst currentExcelPage = ref(1);\r\nconst pageSize = ref(50);\r\n\r\n/* ==================== 当前工作表数据 ==================== */\r\nconst currentData = ref<ExcelRow[]>([]);\r\nconst currentColumns = ref<ExcelColumn[]>([]);\r\n\r\n/* ==================== 计算属性 ==================== */\r\nconst totalExcelPages = computed(() =>\r\n Math.ceil(currentData.value.length / pageSize.value),\r\n);\r\n\r\nconst paginatedData = computed(() => {\r\n const start = (currentExcelPage.value - 1) * pageSize.value;\r\n return currentData.value.slice(start, start + pageSize.value);\r\n});\r\n\r\n/* ==================== 方法 ==================== */\r\nconst handlePageSizeChange = (newPageSize: number) => {\r\n pageSize.value = newPageSize;\r\n currentExcelPage.value = 1;\r\n};\r\n\r\nconst syncSheetData = (sheetName: string) => {\r\n const sheet = props.sheets.find((s) => s.name === sheetName);\r\n if (sheet) {\r\n currentData.value = sheet.data;\r\n currentColumns.value = sheet.columns;\r\n currentExcelPage.value = 1;\r\n }\r\n};\r\n\r\n/* ==================== 监听器 ==================== */\r\nwatch(activeSheet, (newSheet) => {\r\n syncSheetData(newSheet);\r\n});\r\n\r\n/* sheets 变化时初始化 */\r\nwatch(\r\n () => props.sheets,\r\n (newSheets) => {\r\n if (newSheets.length > 0) {\r\n const [firstSheet] = newSheets;\r\n activeSheet.value = firstSheet.name;\r\n currentData.value = firstSheet.data;\r\n currentColumns.value = firstSheet.columns;\r\n currentExcelPage.value = 1;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n ExcelViewer — Excel 表格预览子组件\r\n 负责: 工作表切换 + 表格渲染(合并单元格) + 分页 + 格式切换\r\n-->\r\n<template>\r\n <div class=\"file-container excel-container\">\r\n <!-- 工具栏 -->\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex justify-between items-center\">\r\n <div class=\"flex gap-2 items-center\">\r\n <NTabs\r\n v-if=\"sheets.length > 1\"\r\n v-model:value=\"activeSheet\"\r\n type=\"card\"\r\n size=\"small\"\r\n >\r\n <NTabPane\r\n v-for=\"sheet in sheets\"\r\n :key=\"sheet.name\"\r\n :name=\"sheet.name\"\r\n :tab=\"sheet.name\"\r\n />\r\n </NTabs>\r\n <span v-else class=\"text-sm text-gray-600\">\r\n 工作表: {{ activeSheet }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton size=\"small\" @click=\"showExcelFormat = !showExcelFormat\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-format-paint\" />\r\n </template>\r\n {{ showExcelFormat ? \"紧凑视图\" : \"完整格式\" }}\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"excel-viewer\">\r\n <!-- 信息面板 -->\r\n <div v-if=\"currentData.length > 0\" class=\"excel-info\">\r\n <NText depth=\"3\" class=\"text-sm\">\r\n 当前工作表: {{ activeSheet }} | 总行数: {{ currentData.length }} |\r\n 总列数: {{ currentColumns.length }} | 当前页:\r\n {{ currentExcelPage }}/{{ totalExcelPages }}\r\n </NText>\r\n </div>\r\n\r\n <!-- 表格 -->\r\n <div class=\"excel-table-container\">\r\n <div\r\n class=\"excel-table-wrapper\"\r\n :class=\"{ 'simple-mode': !showExcelFormat }\"\r\n >\r\n <table v-if=\"currentData.length > 0\" class=\"excel-table\">\r\n <thead>\r\n <tr>\r\n <th class=\"row-number\">行号</th>\r\n <th\r\n v-for=\"(col, index) in currentColumns\"\r\n :key=\"col.key\"\r\n class=\"excel-header\"\r\n :style=\"{ minWidth: col.width + 'px' }\"\r\n >\r\n <div class=\"header-content\">\r\n <span class=\"column-letter\">{{\r\n getColumnLetter(index)\r\n }}</span>\r\n <span class=\"column-title\">{{ col.title }}</span>\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr\r\n v-for=\"(row, rowIndex) in paginatedData\"\r\n :key=\"rowIndex\"\r\n class=\"excel-row\"\r\n >\r\n <td class=\"row-number\">\r\n {{ (currentExcelPage - 1) * pageSize + rowIndex + 1 }}\r\n </td>\r\n <td\r\n v-for=\"col in currentColumns\"\r\n :key=\"col.key\"\r\n v-show=\"!row[col.key]?.hidden\"\r\n class=\"excel-cell\"\r\n :class=\"[\r\n getCellClass(row[col.key]?.value),\r\n row[col.key]?.merged ? 'merged-cell' : '',\r\n !showExcelFormat ? 'compact-cell' : '',\r\n ]\"\r\n :rowspan=\"row[col.key]?.rowspan || 1\"\r\n :colspan=\"row[col.key]?.colspan || 1\"\r\n :title=\"row[col.key]?.value\"\r\n >\r\n {{ formatCellValue(row[col.key]?.value) }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div v-else class=\"excel-empty\">\r\n <NEmpty description=\"该工作表没有数据或解析失败\">\r\n <template #extra>\r\n <NButton size=\"small\" @click=\"$emit('reload')\">\r\n 重新解析\r\n </NButton>\r\n </template>\r\n </NEmpty>\r\n </div>\r\n </div>\r\n\r\n <!-- 分页 -->\r\n <div v-if=\"totalExcelPages > 1\" class=\"excel-pagination\">\r\n <NPagination\r\n v-model:page=\"currentExcelPage\"\r\n :page-count=\"totalExcelPages\"\r\n :page-size=\"pageSize\"\r\n show-size-picker\r\n :page-sizes=\"PAGE_SIZE_OPTIONS\"\r\n @update:page-size=\"handlePageSizeChange\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from \"vue\";\r\nimport { NButton, NTabs, NTabPane, NText, NEmpty, NPagination } from \"naive-ui\";\r\nimport {\r\n getColumnLetter,\r\n formatCellValue,\r\n getCellClass,\r\n PAGE_SIZE_OPTIONS,\r\n} from \"../../data\";\r\nimport type { ExcelSheet, ExcelRow, ExcelColumn } from \"../../types\";\r\n\r\nconst props = defineProps<{\r\n sheets: ExcelSheet[];\r\n}>();\r\n\r\ndefineEmits<{\r\n reload: [];\r\n}>();\r\n\r\n/* ==================== 内部状态 ==================== */\r\nconst activeSheet = ref(\"\");\r\nconst showExcelFormat = ref(false);\r\nconst currentExcelPage = ref(1);\r\nconst pageSize = ref(50);\r\n\r\n/* ==================== 当前工作表数据 ==================== */\r\nconst currentData = ref<ExcelRow[]>([]);\r\nconst currentColumns = ref<ExcelColumn[]>([]);\r\n\r\n/* ==================== 计算属性 ==================== */\r\nconst totalExcelPages = computed(() =>\r\n Math.ceil(currentData.value.length / pageSize.value),\r\n);\r\n\r\nconst paginatedData = computed(() => {\r\n const start = (currentExcelPage.value - 1) * pageSize.value;\r\n return currentData.value.slice(start, start + pageSize.value);\r\n});\r\n\r\n/* ==================== 方法 ==================== */\r\nconst handlePageSizeChange = (newPageSize: number) => {\r\n pageSize.value = newPageSize;\r\n currentExcelPage.value = 1;\r\n};\r\n\r\nconst syncSheetData = (sheetName: string) => {\r\n const sheet = props.sheets.find((s) => s.name === sheetName);\r\n if (sheet) {\r\n currentData.value = sheet.data;\r\n currentColumns.value = sheet.columns;\r\n currentExcelPage.value = 1;\r\n }\r\n};\r\n\r\n/* ==================== 监听器 ==================== */\r\nwatch(activeSheet, (newSheet) => {\r\n syncSheetData(newSheet);\r\n});\r\n\r\n/* sheets 变化时初始化 */\r\nwatch(\r\n () => props.sheets,\r\n (newSheets) => {\r\n if (newSheets.length > 0) {\r\n const [firstSheet] = newSheets;\r\n activeSheet.value = firstSheet.name;\r\n currentData.value = firstSheet.data;\r\n currentColumns.value = firstSheet.columns;\r\n currentExcelPage.value = 1;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","<!--\r\n ExcelViewer — Excel 表格预览子组件\r\n 负责: 工作表切换 + 表格渲染(合并单元格) + 分页 + 格式切换\r\n-->\r\n<template>\r\n <div class=\"file-container excel-container\">\r\n <!-- 工具栏 -->\r\n <div class=\"file-toolbar\">\r\n <div class=\"flex justify-between items-center\">\r\n <div class=\"flex gap-2 items-center\">\r\n <NTabs\r\n v-if=\"sheets.length > 1\"\r\n v-model:value=\"activeSheet\"\r\n type=\"card\"\r\n size=\"small\"\r\n >\r\n <NTabPane\r\n v-for=\"sheet in sheets\"\r\n :key=\"sheet.name\"\r\n :name=\"sheet.name\"\r\n :tab=\"sheet.name\"\r\n />\r\n </NTabs>\r\n <span v-else class=\"text-sm text-gray-600\">\r\n 工作表: {{ activeSheet }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"flex gap-2 items-center\">\r\n <NButton size=\"small\" @click=\"showExcelFormat = !showExcelFormat\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-format-paint\" />\r\n </template>\r\n {{ showExcelFormat ? \"紧凑视图\" : \"完整格式\" }}\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"excel-viewer\">\r\n <!-- 信息面板 -->\r\n <div v-if=\"currentData.length > 0\" class=\"excel-info\">\r\n <NText depth=\"3\" class=\"text-sm\">\r\n 当前工作表: {{ activeSheet }} | 总行数: {{ currentData.length }} |\r\n 总列数: {{ currentColumns.length }} | 当前页:\r\n {{ currentExcelPage }}/{{ totalExcelPages }}\r\n </NText>\r\n </div>\r\n\r\n <!-- 表格 -->\r\n <div class=\"excel-table-container\">\r\n <div\r\n class=\"excel-table-wrapper\"\r\n :class=\"{ 'simple-mode': !showExcelFormat }\"\r\n >\r\n <table v-if=\"currentData.length > 0\" class=\"excel-table\">\r\n <thead>\r\n <tr>\r\n <th class=\"row-number\">行号</th>\r\n <th\r\n v-for=\"(col, index) in currentColumns\"\r\n :key=\"col.key\"\r\n class=\"excel-header\"\r\n :style=\"{ minWidth: col.width + 'px' }\"\r\n >\r\n <div class=\"header-content\">\r\n <span class=\"column-letter\">{{\r\n getColumnLetter(index)\r\n }}</span>\r\n <span class=\"column-title\">{{ col.title }}</span>\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr\r\n v-for=\"(row, rowIndex) in paginatedData\"\r\n :key=\"rowIndex\"\r\n class=\"excel-row\"\r\n >\r\n <td class=\"row-number\">\r\n {{ (currentExcelPage - 1) * pageSize + rowIndex + 1 }}\r\n </td>\r\n <td\r\n v-for=\"col in currentColumns\"\r\n :key=\"col.key\"\r\n v-show=\"!row[col.key]?.hidden\"\r\n class=\"excel-cell\"\r\n :class=\"[\r\n getCellClass(row[col.key]?.value),\r\n row[col.key]?.merged ? 'merged-cell' : '',\r\n !showExcelFormat ? 'compact-cell' : '',\r\n ]\"\r\n :rowspan=\"row[col.key]?.rowspan || 1\"\r\n :colspan=\"row[col.key]?.colspan || 1\"\r\n :title=\"row[col.key]?.value\"\r\n >\r\n {{ formatCellValue(row[col.key]?.value) }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div v-else class=\"excel-empty\">\r\n <NEmpty description=\"该工作表没有数据或解析失败\">\r\n <template #extra>\r\n <NButton size=\"small\" @click=\"$emit('reload')\">\r\n 重新解析\r\n </NButton>\r\n </template>\r\n </NEmpty>\r\n </div>\r\n </div>\r\n\r\n <!-- 分页 -->\r\n <div v-if=\"totalExcelPages > 1\" class=\"excel-pagination\">\r\n <NPagination\r\n v-model:page=\"currentExcelPage\"\r\n :page-count=\"totalExcelPages\"\r\n :page-size=\"pageSize\"\r\n show-size-picker\r\n :page-sizes=\"PAGE_SIZE_OPTIONS\"\r\n @update:page-size=\"handlePageSizeChange\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from \"vue\";\r\nimport { NButton, NTabs, NTabPane, NText, NEmpty, NPagination } from \"naive-ui\";\r\nimport {\r\n getColumnLetter,\r\n formatCellValue,\r\n getCellClass,\r\n PAGE_SIZE_OPTIONS,\r\n} from \"../../data\";\r\nimport type { ExcelSheet, ExcelRow, ExcelColumn } from \"../../types\";\r\n\r\nconst props = defineProps<{\r\n sheets: ExcelSheet[];\r\n}>();\r\n\r\ndefineEmits<{\r\n reload: [];\r\n}>();\r\n\r\n/* ==================== 内部状态 ==================== */\r\nconst activeSheet = ref(\"\");\r\nconst showExcelFormat = ref(false);\r\nconst currentExcelPage = ref(1);\r\nconst pageSize = ref(50);\r\n\r\n/* ==================== 当前工作表数据 ==================== */\r\nconst currentData = ref<ExcelRow[]>([]);\r\nconst currentColumns = ref<ExcelColumn[]>([]);\r\n\r\n/* ==================== 计算属性 ==================== */\r\nconst totalExcelPages = computed(() =>\r\n Math.ceil(currentData.value.length / pageSize.value),\r\n);\r\n\r\nconst paginatedData = computed(() => {\r\n const start = (currentExcelPage.value - 1) * pageSize.value;\r\n return currentData.value.slice(start, start + pageSize.value);\r\n});\r\n\r\n/* ==================== 方法 ==================== */\r\nconst handlePageSizeChange = (newPageSize: number) => {\r\n pageSize.value = newPageSize;\r\n currentExcelPage.value = 1;\r\n};\r\n\r\nconst syncSheetData = (sheetName: string) => {\r\n const sheet = props.sheets.find((s) => s.name === sheetName);\r\n if (sheet) {\r\n currentData.value = sheet.data;\r\n currentColumns.value = sheet.columns;\r\n currentExcelPage.value = 1;\r\n }\r\n};\r\n\r\n/* ==================== 监听器 ==================== */\r\nwatch(activeSheet, (newSheet) => {\r\n syncSheetData(newSheet);\r\n});\r\n\r\n/* sheets 变化时初始化 */\r\nwatch(\r\n () => props.sheets,\r\n (newSheets) => {\r\n if (newSheets.length > 0) {\r\n const [firstSheet] = newSheets;\r\n activeSheet.value = firstSheet.name;\r\n currentData.value = firstSheet.data;\r\n currentColumns.value = firstSheet.columns;\r\n currentExcelPage.value = 1;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n@use \"./index.scss\";\r\n</style>\r\n","/**\r\n * useFilePreview — 文件预览核心 composable\r\n * 管理:文件状态、加载、类型检测、下载\r\n */\r\nimport { ref, computed, watch, onUnmounted, type Ref } from \"vue\";\r\nimport {\r\n extractFileNameFromUrl,\r\n getFileType,\r\n getFileConfig,\r\n loadPdf,\r\n loadWord,\r\n loadExcel,\r\n} from \"../data\";\r\nimport type { FilePreviewType, DocHeading, ExcelSheet } from \"../types\";\r\n\r\nexport interface UseFilePreviewOptions {\r\n file: Ref<File | undefined>;\r\n url: Ref<string | undefined>;\r\n fileName: Ref<string>;\r\n}\r\n\r\n/**\r\n * 文件预览核心 composable — 管理文件状态、加载、类型检测、下载\r\n * @param options - 文件源配置(file / url / fileName)\r\n * @param emit - 组件事件发射器\r\n */\r\nexport function useFilePreview(\r\n options: UseFilePreviewOptions,\r\n emit: {\r\n (e: \"preview\", file: File | string): void;\r\n (e: \"download\", file: File | string): void;\r\n },\r\n) {\r\n const { file, url, fileName } = options;\r\n\r\n /* ==================== 基础状态 ==================== */\r\n const loading = ref(false);\r\n const error = ref(\"\");\r\n const fileSize = ref(0);\r\n const showModal = ref(false);\r\n\r\n /* ==================== 解析后的数据 ==================== */\r\n const pdfUrl = ref(\"\");\r\n const pdfTotalPages = ref(0);\r\n\r\n const wordContent = ref(\"\");\r\n const wordHeadings = ref<DocHeading[]>([]);\r\n\r\n const excelSheets = ref<ExcelSheet[]>([]);\r\n\r\n /* ==================== 计算属性 ==================== */\r\n const displayFileName = computed(() => {\r\n if (fileName.value && fileName.value !== \"未知文件\") return fileName.value;\r\n if (file.value?.name) return file.value.name;\r\n if (url.value) return extractFileNameFromUrl(url.value);\r\n return \"未知文件\";\r\n });\r\n\r\n const fileType = computed<FilePreviewType>(() =>\r\n getFileType(displayFileName.value),\r\n );\r\n const fileConfig = computed(() => getFileConfig(fileType.value));\r\n\r\n /* ==================== 内部方法 ==================== */\r\n const clearState = () => {\r\n error.value = \"\";\r\n loading.value = false;\r\n pdfUrl.value = \"\";\r\n pdfTotalPages.value = 0;\r\n wordContent.value = \"\";\r\n wordHeadings.value = [];\r\n excelSheets.value = [];\r\n };\r\n\r\n const setError = (msg: string) => {\r\n error.value = msg;\r\n loading.value = false;\r\n };\r\n\r\n /* ==================== 文件解析 ==================== */\r\n const resolveFile = async (): Promise<File> => {\r\n if (file.value) {\r\n fileSize.value = file.value.size;\r\n return file.value;\r\n }\r\n if (url.value) {\r\n const response = await fetch(url.value);\r\n if (!response.ok)\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\r\n\r\n const blob = await response.blob();\r\n const resolved = new File([blob], displayFileName.value, {\r\n type: blob.type,\r\n });\r\n fileSize.value = resolved.size;\r\n return resolved;\r\n }\r\n throw new Error(\"未提供文件或URL\");\r\n };\r\n\r\n const loadByType = async (\r\n currentFile: File,\r\n type: FilePreviewType,\r\n ): Promise<void> => {\r\n switch (type) {\r\n case \"pdf\": {\r\n const result = await loadPdf(currentFile);\r\n pdfUrl.value = result.url;\r\n pdfTotalPages.value = result.totalPages;\r\n break;\r\n }\r\n case \"word\": {\r\n const result = await loadWord(currentFile);\r\n wordContent.value = result.content;\r\n wordHeadings.value = result.headings;\r\n break;\r\n }\r\n case \"excel\": {\r\n const result = await loadExcel(currentFile);\r\n excelSheets.value = result.sheets;\r\n break;\r\n }\r\n default:\r\n throw new Error(\"不支持的文件格式\");\r\n }\r\n };\r\n\r\n /* ==================== 核心方法 ==================== */\r\n const loadFile = async () => {\r\n if (!file.value && !url.value) {\r\n setError(\"未提供文件或URL\");\r\n return;\r\n }\r\n\r\n clearState();\r\n loading.value = true;\r\n\r\n try {\r\n const currentFile = await resolveFile();\r\n await loadByType(currentFile, fileType.value);\r\n loading.value = false;\r\n } catch (err) {\r\n const errorMessage = err instanceof Error ? err.message : \"未知错误\";\r\n setError(`${fileType.value.toUpperCase()}文件加载失败: ${errorMessage}`);\r\n }\r\n };\r\n\r\n const openPreview = async () => {\r\n showModal.value = true;\r\n await loadFile();\r\n emit(\"preview\", file.value || url.value!);\r\n };\r\n\r\n const downloadFile = () => {\r\n if (file.value) {\r\n const downloadUrl = URL.createObjectURL(file.value);\r\n const a = Object.assign(document.createElement(\"a\"), {\r\n href: downloadUrl,\r\n download: displayFileName.value,\r\n });\r\n a.click();\r\n URL.revokeObjectURL(downloadUrl);\r\n } else if (url.value) {\r\n window.open(url.value, \"_blank\");\r\n }\r\n emit(\"download\", file.value || url.value!);\r\n };\r\n\r\n /* ==================== 副作用 ==================== */\r\n watch(\r\n () => file.value?.size,\r\n (newSize) => {\r\n fileSize.value = newSize || 0;\r\n },\r\n { immediate: true },\r\n );\r\n\r\n onUnmounted(() => {\r\n if (pdfUrl.value?.startsWith(\"blob:\")) {\r\n URL.revokeObjectURL(pdfUrl.value.split(\"#\")[0]);\r\n }\r\n });\r\n\r\n return {\r\n /* 状态 */\r\n loading,\r\n error,\r\n fileSize,\r\n showModal,\r\n /* 解析数据 */\r\n pdfUrl,\r\n pdfTotalPages,\r\n wordContent,\r\n wordHeadings,\r\n excelSheets,\r\n /* 计算属性 */\r\n displayFileName,\r\n fileType,\r\n fileConfig,\r\n /* 方法 */\r\n loadFile,\r\n openPreview,\r\n downloadFile,\r\n };\r\n}\r\n","/**\r\n * useFullscreen — 全屏切换 composable\r\n */\r\nimport { ref, onMounted, onUnmounted, type Ref } from \"vue\";\r\nimport { FULLSCREEN_EVENTS } from \"../data\";\r\n\r\n/**\r\n * 全屏切换 composable — 管理全屏状态与跨浏览器兼容\r\n * @param containerRef - 需要全屏的容器元素引用\r\n */\r\nexport function useFullscreen(containerRef: Ref<HTMLElement | undefined>) {\r\n const isFullscreen = ref(false);\r\n\r\n const handleFullscreenChange = () => {\r\n isFullscreen.value = !!document.fullscreenElement;\r\n };\r\n\r\n const tryMethods = (\r\n methods: Array<() => Promise<void>>,\r\n index = 0,\r\n ): Promise<void> => {\r\n if (index >= methods.length) {\r\n console.warn(\"无法切换全屏状态\");\r\n return Promise.resolve();\r\n }\r\n return methods[index]().catch(() => tryMethods(methods, index + 1));\r\n };\r\n\r\n const toggleFullscreen = async () => {\r\n const element = containerRef.value;\r\n if (!element) return;\r\n\r\n const isCurrentlyFullscreen = !!document.fullscreenElement;\r\n\r\n const methods = isCurrentlyFullscreen\r\n ? [\r\n () => document.exitFullscreen(),\r\n () => (document as any).webkitExitFullscreen(),\r\n () => (document as any).mozCancelFullScreen(),\r\n () => (document as any).msExitFullscreen(),\r\n ]\r\n : [\r\n () => element.requestFullscreen(),\r\n () => (element as any).webkitRequestFullscreen(),\r\n () => (element as any).mozRequestFullScreen(),\r\n () => (element as any).msRequestFullscreen(),\r\n ];\r\n\r\n await tryMethods(methods);\r\n };\r\n\r\n const exitFullscreen = async () => {\r\n if (document.fullscreenElement) {\r\n try {\r\n await document.exitFullscreen();\r\n } catch {\r\n /* ignore */\r\n }\r\n }\r\n isFullscreen.value = false;\r\n };\r\n\r\n onMounted(() => {\r\n FULLSCREEN_EVENTS.forEach((event) =>\r\n document.addEventListener(event, handleFullscreenChange),\r\n );\r\n });\r\n\r\n onUnmounted(() => {\r\n FULLSCREEN_EVENTS.forEach((event) =>\r\n document.removeEventListener(event, handleFullscreenChange),\r\n );\r\n });\r\n\r\n return { isFullscreen, toggleFullscreen, exitFullscreen };\r\n}\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-07-01\r\n * @Description: 文件预览组件(PDF/Word/Excel)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"c-file-preview-wrapper\">\r\n <!-- 文件信息卡片模式 -->\r\n <div\r\n v-if=\"!autoPreview\"\r\n class=\"file-info-card\"\r\n >\r\n <div class=\"file-info\">\r\n <div class=\"file-icon\">\r\n <C_Icon\r\n :name=\"fileConfig.icon\"\r\n :size=\"40\"\r\n :color=\"fileConfig.color\"\r\n />\r\n </div>\r\n\r\n <div class=\"file-details\">\r\n <div class=\"file-name\">\r\n <NEllipsis style=\"max-width: 250px\">{{\r\n displayFileName\r\n }}</NEllipsis>\r\n <NTag\r\n :type=\"fileConfig.tagType as any\"\r\n size=\"small\"\r\n >\r\n {{ fileType.toUpperCase() }}\r\n </NTag>\r\n </div>\r\n\r\n <div class=\"file-meta\">\r\n <span class=\"file-size\">{{ formatFileSize(fileSize) }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"file-actions\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openPreview\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-visibility\" />\r\n </template>\r\n 预览\r\n </NButton>\r\n\r\n <NButton\r\n type=\"tertiary\"\r\n @click=\"downloadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 模态框预览 -->\r\n <NModal\r\n v-model:show=\"showModal\"\r\n :mask-closable=\"false\"\r\n :closable=\"false\"\r\n :auto-focus=\"false\"\r\n transform-origin=\"center\"\r\n style=\"\r\n width: 85vw;\r\n max-width: 1200px;\r\n min-width: 800px;\r\n height: 75vh;\r\n max-height: 700px;\r\n min-height: 500px;\r\n \"\r\n >\r\n <div\r\n ref=\"modalContainer\"\r\n class=\"modal-container\"\r\n >\r\n <!-- 自定义头部 -->\r\n <div class=\"modal-header\">\r\n <div class=\"modal-title\">\r\n <C_Icon\r\n :name=\"fileConfig.icon\"\r\n :size=\"20\"\r\n :color=\"fileConfig.color\"\r\n />\r\n <span>{{ displayFileName }}</span>\r\n </div>\r\n <div class=\"modal-actions\">\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n @click=\"downloadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n @click=\"showModal = false\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 预览内容 -->\r\n <div class=\"modal-content\">\r\n <!-- 文件信息头部 -->\r\n <div class=\"preview-header\">\r\n <div class=\"flex justify-between items-center\">\r\n <div class=\"flex items-center gap-3\">\r\n <NTag\r\n :type=\"fileConfig.tagType as any\"\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon :name=\"fileConfig.icon\" />\r\n </template>\r\n {{ fileType.toUpperCase() }}\r\n </NTag>\r\n <NEllipsis style=\"max-width: 300px\">{{\r\n displayFileName\r\n }}</NEllipsis>\r\n <span class=\"text-sm text-gray-500\">{{\r\n formatFileSize(fileSize)\r\n }}</span>\r\n </div>\r\n\r\n <div class=\"flex gap-2\">\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n :disabled=\"loading\"\r\n @click=\"loadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-refresh\" />\r\n </template>\r\n 刷新\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n type=\"primary\"\r\n @click=\"toggleFullscreen\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen\r\n ? 'ic:outline-fullscreen-exit'\r\n : 'ic:outline-fullscreen'\r\n \"\r\n />\r\n </template>\r\n {{ isFullscreen ? '退出全屏' : '全屏' }}\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 预览内容区域 -->\r\n <div class=\"preview-content\">\r\n <!-- 加载和错误状态 -->\r\n <template v-if=\"loading || error\">\r\n <div class=\"status-container\">\r\n <NSpin\r\n v-if=\"loading\"\r\n size=\"large\"\r\n >\r\n <template #description>\r\n 正在加载{{ fileType.toUpperCase() }}文件...\r\n </template>\r\n </NSpin>\r\n <NResult\r\n v-else\r\n status=\"error\"\r\n title=\"预览失败\"\r\n :description=\"error\"\r\n >\r\n <template #footer>\r\n <NButton @click=\"loadFile\">重试</NButton>\r\n </template>\r\n </NResult>\r\n </div>\r\n </template>\r\n\r\n <!-- 文件预览 — 委托给子组件 -->\r\n <template v-else>\r\n <PdfViewer\r\n v-if=\"fileType === 'pdf'\"\r\n :pdf-url=\"pdfUrl\"\r\n :total-pages=\"pdfTotalPages\"\r\n />\r\n\r\n <WordViewer\r\n v-else-if=\"fileType === 'word'\"\r\n :content=\"wordContent\"\r\n :headings=\"wordHeadings\"\r\n />\r\n\r\n <ExcelViewer\r\n v-else-if=\"fileType === 'excel'\"\r\n :sheets=\"excelSheets\"\r\n @reload=\"loadFile\"\r\n />\r\n\r\n <!-- 不支持的文件类型 -->\r\n <NResult\r\n v-else\r\n status=\"warning\"\r\n title=\"不支持的文件格式\"\r\n :description=\"`暂不支持预览 ${fileType.toUpperCase()} 格式文件`\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, toRefs, watch } from 'vue'\r\n import { NButton, NEllipsis, NTag, NModal, NSpin, NResult } from 'naive-ui'\r\n import { formatFileSize } from './data'\r\n import PdfViewer from './components/PdfViewer/index.vue'\r\n import WordViewer from './components/WordViewer/index.vue'\r\n import ExcelViewer from './components/ExcelViewer/index.vue'\r\n import { useFilePreview } from './composables/useFilePreview'\r\n import { useFullscreen } from './composables/useFullscreen'\r\n\r\n defineOptions({ name: 'C_FilePreview' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n file?: File\r\n url?: string\r\n fileName?: string\r\n autoPreview?: boolean\r\n }>(),\r\n {\r\n fileName: '未知文件',\r\n autoPreview: false,\r\n }\r\n )\r\n\r\n const { file, url, fileName: propFileName, autoPreview } = toRefs(props)\r\n\r\n const emit = defineEmits<{\r\n preview: [file: File | string]\r\n download: [file: File | string]\r\n }>()\r\n\r\n /* ==================== Composables ==================== */\r\n const {\r\n loading,\r\n error,\r\n fileSize,\r\n showModal,\r\n pdfUrl,\r\n pdfTotalPages,\r\n wordContent,\r\n wordHeadings,\r\n excelSheets,\r\n displayFileName,\r\n fileType,\r\n fileConfig,\r\n loadFile,\r\n openPreview,\r\n downloadFile,\r\n } = useFilePreview({ file, url, fileName: propFileName }, emit)\r\n\r\n const modalContainer = ref<HTMLElement>()\r\n const { isFullscreen, toggleFullscreen, exitFullscreen } =\r\n useFullscreen(modalContainer)\r\n\r\n /* ==================== Modal 关闭时退出全屏 ==================== */\r\n watch(showModal, isShow => {\r\n if (!isShow) exitFullscreen()\r\n })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-07-01\r\n * @Description: 文件预览组件(PDF/Word/Excel)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"c-file-preview-wrapper\">\r\n <!-- 文件信息卡片模式 -->\r\n <div\r\n v-if=\"!autoPreview\"\r\n class=\"file-info-card\"\r\n >\r\n <div class=\"file-info\">\r\n <div class=\"file-icon\">\r\n <C_Icon\r\n :name=\"fileConfig.icon\"\r\n :size=\"40\"\r\n :color=\"fileConfig.color\"\r\n />\r\n </div>\r\n\r\n <div class=\"file-details\">\r\n <div class=\"file-name\">\r\n <NEllipsis style=\"max-width: 250px\">{{\r\n displayFileName\r\n }}</NEllipsis>\r\n <NTag\r\n :type=\"fileConfig.tagType as any\"\r\n size=\"small\"\r\n >\r\n {{ fileType.toUpperCase() }}\r\n </NTag>\r\n </div>\r\n\r\n <div class=\"file-meta\">\r\n <span class=\"file-size\">{{ formatFileSize(fileSize) }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"file-actions\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openPreview\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-visibility\" />\r\n </template>\r\n 预览\r\n </NButton>\r\n\r\n <NButton\r\n type=\"tertiary\"\r\n @click=\"downloadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 模态框预览 -->\r\n <NModal\r\n v-model:show=\"showModal\"\r\n :mask-closable=\"false\"\r\n :closable=\"false\"\r\n :auto-focus=\"false\"\r\n transform-origin=\"center\"\r\n style=\"\r\n width: 85vw;\r\n max-width: 1200px;\r\n min-width: 800px;\r\n height: 75vh;\r\n max-height: 700px;\r\n min-height: 500px;\r\n \"\r\n >\r\n <div\r\n ref=\"modalContainer\"\r\n class=\"modal-container\"\r\n >\r\n <!-- 自定义头部 -->\r\n <div class=\"modal-header\">\r\n <div class=\"modal-title\">\r\n <C_Icon\r\n :name=\"fileConfig.icon\"\r\n :size=\"20\"\r\n :color=\"fileConfig.color\"\r\n />\r\n <span>{{ displayFileName }}</span>\r\n </div>\r\n <div class=\"modal-actions\">\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n @click=\"downloadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n @click=\"showModal = false\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 预览内容 -->\r\n <div class=\"modal-content\">\r\n <!-- 文件信息头部 -->\r\n <div class=\"preview-header\">\r\n <div class=\"flex justify-between items-center\">\r\n <div class=\"flex items-center gap-3\">\r\n <NTag\r\n :type=\"fileConfig.tagType as any\"\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon :name=\"fileConfig.icon\" />\r\n </template>\r\n {{ fileType.toUpperCase() }}\r\n </NTag>\r\n <NEllipsis style=\"max-width: 300px\">{{\r\n displayFileName\r\n }}</NEllipsis>\r\n <span class=\"text-sm text-gray-500\">{{\r\n formatFileSize(fileSize)\r\n }}</span>\r\n </div>\r\n\r\n <div class=\"flex gap-2\">\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n :disabled=\"loading\"\r\n @click=\"loadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-refresh\" />\r\n </template>\r\n 刷新\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n type=\"primary\"\r\n @click=\"toggleFullscreen\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen\r\n ? 'ic:outline-fullscreen-exit'\r\n : 'ic:outline-fullscreen'\r\n \"\r\n />\r\n </template>\r\n {{ isFullscreen ? '退出全屏' : '全屏' }}\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 预览内容区域 -->\r\n <div class=\"preview-content\">\r\n <!-- 加载和错误状态 -->\r\n <template v-if=\"loading || error\">\r\n <div class=\"status-container\">\r\n <NSpin\r\n v-if=\"loading\"\r\n size=\"large\"\r\n >\r\n <template #description>\r\n 正在加载{{ fileType.toUpperCase() }}文件...\r\n </template>\r\n </NSpin>\r\n <NResult\r\n v-else\r\n status=\"error\"\r\n title=\"预览失败\"\r\n :description=\"error\"\r\n >\r\n <template #footer>\r\n <NButton @click=\"loadFile\">重试</NButton>\r\n </template>\r\n </NResult>\r\n </div>\r\n </template>\r\n\r\n <!-- 文件预览 — 委托给子组件 -->\r\n <template v-else>\r\n <PdfViewer\r\n v-if=\"fileType === 'pdf'\"\r\n :pdf-url=\"pdfUrl\"\r\n :total-pages=\"pdfTotalPages\"\r\n />\r\n\r\n <WordViewer\r\n v-else-if=\"fileType === 'word'\"\r\n :content=\"wordContent\"\r\n :headings=\"wordHeadings\"\r\n />\r\n\r\n <ExcelViewer\r\n v-else-if=\"fileType === 'excel'\"\r\n :sheets=\"excelSheets\"\r\n @reload=\"loadFile\"\r\n />\r\n\r\n <!-- 不支持的文件类型 -->\r\n <NResult\r\n v-else\r\n status=\"warning\"\r\n title=\"不支持的文件格式\"\r\n :description=\"`暂不支持预览 ${fileType.toUpperCase()} 格式文件`\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, toRefs, watch } from 'vue'\r\n import { NButton, NEllipsis, NTag, NModal, NSpin, NResult } from 'naive-ui'\r\n import { formatFileSize } from './data'\r\n import PdfViewer from './components/PdfViewer/index.vue'\r\n import WordViewer from './components/WordViewer/index.vue'\r\n import ExcelViewer from './components/ExcelViewer/index.vue'\r\n import { useFilePreview } from './composables/useFilePreview'\r\n import { useFullscreen } from './composables/useFullscreen'\r\n\r\n defineOptions({ name: 'C_FilePreview' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n file?: File\r\n url?: string\r\n fileName?: string\r\n autoPreview?: boolean\r\n }>(),\r\n {\r\n fileName: '未知文件',\r\n autoPreview: false,\r\n }\r\n )\r\n\r\n const { file, url, fileName: propFileName, autoPreview } = toRefs(props)\r\n\r\n const emit = defineEmits<{\r\n preview: [file: File | string]\r\n download: [file: File | string]\r\n }>()\r\n\r\n /* ==================== Composables ==================== */\r\n const {\r\n loading,\r\n error,\r\n fileSize,\r\n showModal,\r\n pdfUrl,\r\n pdfTotalPages,\r\n wordContent,\r\n wordHeadings,\r\n excelSheets,\r\n displayFileName,\r\n fileType,\r\n fileConfig,\r\n loadFile,\r\n openPreview,\r\n downloadFile,\r\n } = useFilePreview({ file, url, fileName: propFileName }, emit)\r\n\r\n const modalContainer = ref<HTMLElement>()\r\n const { isFullscreen, toggleFullscreen, exitFullscreen } =\r\n useFullscreen(modalContainer)\r\n\r\n /* ==================== Modal 关闭时退出全屏 ==================== */\r\n watch(showModal, isShow => {\r\n if (!isShow) exitFullscreen()\r\n })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n @use './index.scss';\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-07-01\r\n * @Description: 文件预览组件(PDF/Word/Excel)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"c-file-preview-wrapper\">\r\n <!-- 文件信息卡片模式 -->\r\n <div\r\n v-if=\"!autoPreview\"\r\n class=\"file-info-card\"\r\n >\r\n <div class=\"file-info\">\r\n <div class=\"file-icon\">\r\n <C_Icon\r\n :name=\"fileConfig.icon\"\r\n :size=\"40\"\r\n :color=\"fileConfig.color\"\r\n />\r\n </div>\r\n\r\n <div class=\"file-details\">\r\n <div class=\"file-name\">\r\n <NEllipsis style=\"max-width: 250px\">{{\r\n displayFileName\r\n }}</NEllipsis>\r\n <NTag\r\n :type=\"fileConfig.tagType as any\"\r\n size=\"small\"\r\n >\r\n {{ fileType.toUpperCase() }}\r\n </NTag>\r\n </div>\r\n\r\n <div class=\"file-meta\">\r\n <span class=\"file-size\">{{ formatFileSize(fileSize) }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"file-actions\">\r\n <NButton\r\n type=\"primary\"\r\n @click=\"openPreview\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-visibility\" />\r\n </template>\r\n 预览\r\n </NButton>\r\n\r\n <NButton\r\n type=\"tertiary\"\r\n @click=\"downloadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 模态框预览 -->\r\n <NModal\r\n v-model:show=\"showModal\"\r\n :mask-closable=\"false\"\r\n :closable=\"false\"\r\n :auto-focus=\"false\"\r\n transform-origin=\"center\"\r\n style=\"\r\n width: 85vw;\r\n max-width: 1200px;\r\n min-width: 800px;\r\n height: 75vh;\r\n max-height: 700px;\r\n min-height: 500px;\r\n \"\r\n >\r\n <div\r\n ref=\"modalContainer\"\r\n class=\"modal-container\"\r\n >\r\n <!-- 自定义头部 -->\r\n <div class=\"modal-header\">\r\n <div class=\"modal-title\">\r\n <C_Icon\r\n :name=\"fileConfig.icon\"\r\n :size=\"20\"\r\n :color=\"fileConfig.color\"\r\n />\r\n <span>{{ displayFileName }}</span>\r\n </div>\r\n <div class=\"modal-actions\">\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n @click=\"downloadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n @click=\"showModal = false\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 预览内容 -->\r\n <div class=\"modal-content\">\r\n <!-- 文件信息头部 -->\r\n <div class=\"preview-header\">\r\n <div class=\"flex justify-between items-center\">\r\n <div class=\"flex items-center gap-3\">\r\n <NTag\r\n :type=\"fileConfig.tagType as any\"\r\n size=\"small\"\r\n >\r\n <template #icon>\r\n <C_Icon :name=\"fileConfig.icon\" />\r\n </template>\r\n {{ fileType.toUpperCase() }}\r\n </NTag>\r\n <NEllipsis style=\"max-width: 300px\">{{\r\n displayFileName\r\n }}</NEllipsis>\r\n <span class=\"text-sm text-gray-500\">{{\r\n formatFileSize(fileSize)\r\n }}</span>\r\n </div>\r\n\r\n <div class=\"flex gap-2\">\r\n <NButton\r\n size=\"small\"\r\n type=\"tertiary\"\r\n :disabled=\"loading\"\r\n @click=\"loadFile\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-refresh\" />\r\n </template>\r\n 刷新\r\n </NButton>\r\n <NButton\r\n size=\"small\"\r\n type=\"primary\"\r\n @click=\"toggleFullscreen\"\r\n >\r\n <template #icon>\r\n <C_Icon\r\n :name=\"\r\n isFullscreen\r\n ? 'ic:outline-fullscreen-exit'\r\n : 'ic:outline-fullscreen'\r\n \"\r\n />\r\n </template>\r\n {{ isFullscreen ? '退出全屏' : '全屏' }}\r\n </NButton>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 预览内容区域 -->\r\n <div class=\"preview-content\">\r\n <!-- 加载和错误状态 -->\r\n <template v-if=\"loading || error\">\r\n <div class=\"status-container\">\r\n <NSpin\r\n v-if=\"loading\"\r\n size=\"large\"\r\n >\r\n <template #description>\r\n 正在加载{{ fileType.toUpperCase() }}文件...\r\n </template>\r\n </NSpin>\r\n <NResult\r\n v-else\r\n status=\"error\"\r\n title=\"预览失败\"\r\n :description=\"error\"\r\n >\r\n <template #footer>\r\n <NButton @click=\"loadFile\">重试</NButton>\r\n </template>\r\n </NResult>\r\n </div>\r\n </template>\r\n\r\n <!-- 文件预览 — 委托给子组件 -->\r\n <template v-else>\r\n <PdfViewer\r\n v-if=\"fileType === 'pdf'\"\r\n :pdf-url=\"pdfUrl\"\r\n :total-pages=\"pdfTotalPages\"\r\n />\r\n\r\n <WordViewer\r\n v-else-if=\"fileType === 'word'\"\r\n :content=\"wordContent\"\r\n :headings=\"wordHeadings\"\r\n />\r\n\r\n <ExcelViewer\r\n v-else-if=\"fileType === 'excel'\"\r\n :sheets=\"excelSheets\"\r\n @reload=\"loadFile\"\r\n />\r\n\r\n <!-- 不支持的文件类型 -->\r\n <NResult\r\n v-else\r\n status=\"warning\"\r\n title=\"不支持的文件格式\"\r\n :description=\"`暂不支持预览 ${fileType.toUpperCase()} 格式文件`\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </NModal>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, toRefs, watch } from 'vue'\r\n import { NButton, NEllipsis, NTag, NModal, NSpin, NResult } from 'naive-ui'\r\n import { formatFileSize } from './data'\r\n import PdfViewer from './components/PdfViewer/index.vue'\r\n import WordViewer from './components/WordViewer/index.vue'\r\n import ExcelViewer from './components/ExcelViewer/index.vue'\r\n import { useFilePreview } from './composables/useFilePreview'\r\n import { useFullscreen } from './composables/useFullscreen'\r\n\r\n defineOptions({ name: 'C_FilePreview' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n file?: File\r\n url?: string\r\n fileName?: string\r\n autoPreview?: boolean\r\n }>(),\r\n {\r\n fileName: '未知文件',\r\n autoPreview: false,\r\n }\r\n )\r\n\r\n const { file, url, fileName: propFileName, autoPreview } = toRefs(props)\r\n\r\n const emit = defineEmits<{\r\n preview: [file: File | string]\r\n download: [file: File | string]\r\n }>()\r\n\r\n /* ==================== Composables ==================== */\r\n const {\r\n loading,\r\n error,\r\n fileSize,\r\n showModal,\r\n pdfUrl,\r\n pdfTotalPages,\r\n wordContent,\r\n wordHeadings,\r\n excelSheets,\r\n displayFileName,\r\n fileType,\r\n fileConfig,\r\n loadFile,\r\n openPreview,\r\n downloadFile,\r\n } = useFilePreview({ file, url, fileName: propFileName }, emit)\r\n\r\n const modalContainer = ref<HTMLElement>()\r\n const { isFullscreen, toggleFullscreen, exitFullscreen } =\r\n useFullscreen(modalContainer)\r\n\r\n /* ==================== Modal 关闭时退出全屏 ==================== */\r\n watch(showModal, isShow => {\r\n if (!isShow) exitFullscreen()\r\n })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n @use './index.scss';\r\n</style>\r\n"],"mappings":"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"}
|