@robot-admin/naive-ui-components 0.4.3 → 0.4.4
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.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.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.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.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.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.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.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.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 +2 -2
- package/dist/constants2.d.ts +3 -3
- package/dist/constants3.d.ts +3 -3
- package/dist/constants4.d.ts +5 -5
- package/dist/constants5.d.ts +2 -2
- package/dist/data.d.ts +1 -1
- 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 +5 -5
- package/dist/index12.vue.d.ts +3 -3
- package/dist/index13.vue.d.ts +3 -3
- package/dist/index14.vue.d.ts +3 -3
- package/dist/index16.vue.d.ts +3 -3
- package/dist/index3.vue.d.ts +3 -3
- package/dist/index5.vue.d.ts +1 -1
- package/dist/index7.vue.d.ts +4 -4
- package/dist/index8.vue.d.ts +2 -2
- 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/useCollapsePanel.d.ts +2 -2
- package/dist/useCropperCore.d.ts +8 -8
- package/dist/useDraggableLayout.d.ts +4 -4
- package/dist/useDynamicFormState.d.ts +7 -7
- package/dist/useEdgeInteraction.d.ts +1 -1
- package/dist/useFullscreen.d.ts +2 -2
- package/dist/useInfiniteScroll.d.ts +1 -1
- package/dist/useModalEdit.d.ts +1 -1
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSearchState.d.ts +2 -2
- package/dist/useSignatureHistory.d.ts +1 -1
- 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 +4 -4
- package/package.json +1 -2
- 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
package/dist/C_Editor2.js
CHANGED
|
@@ -1,160 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { computed, createElementBlock, defineComponent, nextTick, normalizeClass, onBeforeUnmount, onMounted, openBlock, readonly, ref, vShow, watch, withDirectives } from "vue";
|
|
3
|
-
import E from "wangeditor";
|
|
4
|
-
|
|
5
|
-
//#region src/components/C_Editor/index.vue?vue&type=script&setup=true&lang.ts
|
|
6
|
-
const _hoisted_1 = ["id"];
|
|
7
|
-
var index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
8
|
-
name: "C_Editor",
|
|
9
|
-
__name: "index",
|
|
10
|
-
props: {
|
|
11
|
-
editorId: {},
|
|
12
|
-
modelValue: { default: "" },
|
|
13
|
-
placeholder: { default: "" },
|
|
14
|
-
disabled: {
|
|
15
|
-
type: Boolean,
|
|
16
|
-
default: false
|
|
17
|
-
},
|
|
18
|
-
readonly: {
|
|
19
|
-
type: Boolean,
|
|
20
|
-
default: false
|
|
21
|
-
},
|
|
22
|
-
height: { default: 240 },
|
|
23
|
-
theme: { default: "light" }
|
|
24
|
-
},
|
|
25
|
-
emits: [
|
|
26
|
-
"update:modelValue",
|
|
27
|
-
"editor-mounted",
|
|
28
|
-
"editor-change"
|
|
29
|
-
],
|
|
30
|
-
setup(__props, { expose: __expose, emit: __emit }) {
|
|
31
|
-
const props = __props;
|
|
32
|
-
const emit = __emit;
|
|
33
|
-
const editorContainer = ref(null);
|
|
34
|
-
const editorInstance = ref(null);
|
|
35
|
-
const isInitialized = ref(false);
|
|
36
|
-
const isDark = computed(() => props.theme === "dark");
|
|
37
|
-
const initializeEditor = () => {
|
|
38
|
-
if (!editorContainer.value || isInitialized.value) return;
|
|
39
|
-
try {
|
|
40
|
-
const editor = new E(editorContainer.value);
|
|
41
|
-
const editorConfig = editor.config;
|
|
42
|
-
editorConfig.placeholder = props.placeholder;
|
|
43
|
-
editorConfig.height = props.height - 50;
|
|
44
|
-
editorConfig.onchange = (html) => {
|
|
45
|
-
emit("update:modelValue", html);
|
|
46
|
-
emit("editor-change", html);
|
|
47
|
-
};
|
|
48
|
-
editor.create();
|
|
49
|
-
nextTick(() => {
|
|
50
|
-
if (props.modelValue) editor.txt.html(props.modelValue);
|
|
51
|
-
if (props.readonly) editor.disable();
|
|
52
|
-
editorInstance.value = editor;
|
|
53
|
-
isInitialized.value = true;
|
|
54
|
-
emit("editor-mounted", editor);
|
|
55
|
-
});
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const destroyEditor = () => {
|
|
61
|
-
if (editorInstance.value && isInitialized.value) try {
|
|
62
|
-
editorInstance.value.destroy();
|
|
63
|
-
editorInstance.value = null;
|
|
64
|
-
isInitialized.value = false;
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const setContent = (html) => {
|
|
70
|
-
if (editorInstance.value && isInitialized.value) try {
|
|
71
|
-
editorInstance.value.txt.html(html);
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const getContent = () => {
|
|
77
|
-
if (editorInstance.value && isInitialized.value) try {
|
|
78
|
-
return editorInstance.value.txt.html();
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
return "";
|
|
84
|
-
};
|
|
85
|
-
watch(() => props.modelValue, (newValue) => {
|
|
86
|
-
if (editorInstance.value && isInitialized.value) {
|
|
87
|
-
if (editorInstance.value.txt.html() !== newValue) editorInstance.value.txt.html(newValue || "");
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
watch(() => props.disabled, (disabled) => {
|
|
91
|
-
if (editorInstance.value && isInitialized.value) try {
|
|
92
|
-
if (disabled) editorInstance.value.disable();
|
|
93
|
-
else editorInstance.value.enable();
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.warn(`[C_Editor] 设置编辑器状态失败`, error);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
watch(() => props.readonly, (readonlyVal) => {
|
|
99
|
-
if (editorInstance.value && isInitialized.value) try {
|
|
100
|
-
if (readonlyVal) editorInstance.value.disable();
|
|
101
|
-
else editorInstance.value.enable();
|
|
102
|
-
} catch (error) {
|
|
103
|
-
console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
const handleEditorFocus = () => {
|
|
107
|
-
if (!editorContainer.value) return;
|
|
108
|
-
const container = editorContainer.value.closest(".form-demo");
|
|
109
|
-
if (container) {
|
|
110
|
-
container.classList.add("editor-focused");
|
|
111
|
-
const containerWidth = container.scrollWidth;
|
|
112
|
-
container.style.maxWidth = `${containerWidth}px`;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const handleEditorBlur = () => {
|
|
116
|
-
if (!editorContainer.value) return;
|
|
117
|
-
const container = editorContainer.value.closest(".form-demo");
|
|
118
|
-
if (container) {
|
|
119
|
-
container.classList.remove("editor-focused");
|
|
120
|
-
container.style.maxWidth = "";
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
onMounted(() => {
|
|
124
|
-
nextTick(() => {
|
|
125
|
-
initializeEditor();
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
onBeforeUnmount(() => {
|
|
129
|
-
destroyEditor();
|
|
130
|
-
});
|
|
131
|
-
__expose({
|
|
132
|
-
initializeEditor,
|
|
133
|
-
destroyEditor,
|
|
134
|
-
setContent,
|
|
135
|
-
getContent,
|
|
136
|
-
handleEditorFocus,
|
|
137
|
-
handleEditorBlur,
|
|
138
|
-
editorInstance: readonly(editorInstance),
|
|
139
|
-
isInitialized: readonly(isInitialized)
|
|
140
|
-
});
|
|
141
|
-
return (_ctx, _cache) => {
|
|
142
|
-
return withDirectives((openBlock(), createElementBlock("div", {
|
|
143
|
-
ref_key: "editorContainer",
|
|
144
|
-
ref: editorContainer,
|
|
145
|
-
id: _ctx.editorId,
|
|
146
|
-
class: normalizeClass(["w-full", { "editor-dark": isDark.value }]),
|
|
147
|
-
onFocusin: handleEditorFocus,
|
|
148
|
-
onFocusout: handleEditorBlur
|
|
149
|
-
}, null, 42, _hoisted_1)), [[vShow, isInitialized.value]]);
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
//#endregion
|
|
155
|
-
//#region src/components/C_Editor/index.vue
|
|
156
|
-
var C_Editor_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e2dec5c9"]]);
|
|
157
|
-
|
|
158
|
-
//#endregion
|
|
159
|
-
export { C_Editor_default as t };
|
|
1
|
+
import{t as e}from"./export-helper.js";import{computed as t,createElementBlock as n,defineComponent as r,nextTick as i,normalizeClass as a,onBeforeUnmount as o,onMounted as s,openBlock as c,readonly as l,ref as u,vShow as d,watch as f,withDirectives as p}from"vue";import m from"wangeditor";const h=[`id`];var g=e(r({name:`C_Editor`,__name:`index`,props:{editorId:{},modelValue:{default:``},placeholder:{default:``},disabled:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},height:{default:240},theme:{default:`light`}},emits:[`update:modelValue`,`editor-mounted`,`editor-change`],setup(e,{expose:r,emit:g}){let _=e,v=g,y=u(null),b=u(null),x=u(!1),S=t(()=>_.theme===`dark`),C=()=>{if(!(!y.value||x.value))try{let e=new m(y.value),t=e.config;t.placeholder=_.placeholder,t.height=_.height-50,t.onchange=e=>{v(`update:modelValue`,e),v(`editor-change`,e)},e.create(),i(()=>{_.modelValue&&e.txt.html(_.modelValue),_.readonly&&e.disable(),b.value=e,x.value=!0,v(`editor-mounted`,e)})}catch(e){console.error(`[C_Editor] 编辑器初始化失败: ${_.editorId}`,e)}},w=()=>{if(b.value&&x.value)try{b.value.destroy(),b.value=null,x.value=!1}catch(e){console.error(`[C_Editor] 编辑器销毁失败: ${_.editorId}`,e)}},T=e=>{if(b.value&&x.value)try{b.value.txt.html(e)}catch(e){console.warn(`[C_Editor] 设置编辑器内容失败: ${_.editorId}`,e)}},E=()=>{if(b.value&&x.value)try{return b.value.txt.html()}catch(e){return console.warn(`[C_Editor] 获取编辑器内容失败: ${_.editorId}`,e),``}return``};f(()=>_.modelValue,e=>{b.value&&x.value&&b.value.txt.html()!==e&&b.value.txt.html(e||``)}),f(()=>_.disabled,e=>{if(b.value&&x.value)try{e?b.value.disable():b.value.enable()}catch(e){console.warn(`[C_Editor] 设置编辑器状态失败`,e)}}),f(()=>_.readonly,e=>{if(b.value&&x.value)try{e?b.value.disable():b.value.enable()}catch(e){console.warn(`[C_Editor] 设置编辑器只读状态失败`,e)}});let D=()=>{if(!y.value)return;let e=y.value.closest(`.form-demo`);if(e){e.classList.add(`editor-focused`);let t=e.scrollWidth;e.style.maxWidth=`${t}px`}},O=()=>{if(!y.value)return;let e=y.value.closest(`.form-demo`);e&&(e.classList.remove(`editor-focused`),e.style.maxWidth=``)};return s(()=>{i(()=>{C()})}),o(()=>{w()}),r({initializeEditor:C,destroyEditor:w,setContent:T,getContent:E,handleEditorFocus:D,handleEditorBlur:O,editorInstance:l(b),isInitialized:l(x)}),(e,t)=>p((c(),n(`div`,{ref_key:`editorContainer`,ref:y,id:e.editorId,class:a([`w-full`,{"editor-dark":S.value}]),onFocusin:D,onFocusout:O},null,42,h)),[[d,x.value]])}}),[[`__scopeId`,`data-v-e2dec5c9`]]);export{g as t};
|
|
160
2
|
//# sourceMappingURL=C_Editor2.js.map
|
package/dist/C_Editor2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Editor2.js","names":["editorId"],"sources":["../src/components/C_Editor/index.vue","../src/components/C_Editor/index.vue","../src/components/C_Editor/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 富文本编辑器组件(简化主题版)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"editorContainer\"\r\n :id=\"editorId\"\r\n v-show=\"isInitialized\"\r\n class=\"w-full\"\r\n :class=\"{ 'editor-dark': isDark }\"\r\n @focusin=\"handleEditorFocus\"\r\n @focusout=\"handleEditorBlur\"\r\n ></div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n nextTick,\r\n onMounted,\r\n onBeforeUnmount,\r\n readonly,\r\n} from \"vue\";\r\nimport E from \"wangeditor\";\r\n\r\ndefineOptions({ name: \"C_Editor\" });\r\n\r\ninterface Props {\r\n editorId: string;\r\n modelValue?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n readonly?: boolean;\r\n height?: number;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"editor-mounted\", editor: any): void;\r\n (e: \"editor-change\", html: string): void;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: \"\",\r\n placeholder: \"\",\r\n disabled: false,\r\n readonly: false,\r\n height: 240,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst editorContainer = ref<HTMLElement | null>(null);\r\nconst editorInstance = ref<any>(null);\r\nconst isInitialized = ref<boolean>(false);\r\n\r\nconst isDark = computed(() => props.theme === \"dark\");\r\n\r\nconst initializeEditor = (): void => {\r\n if (!editorContainer.value || isInitialized.value) return;\r\n\r\n try {\r\n const editor = new E(editorContainer.value);\r\n const editorConfig = editor.config as any;\r\n editorConfig.placeholder = props.placeholder;\r\n editorConfig.height = props.height - 50;\r\n\r\n editorConfig.onchange = (html: string) => {\r\n emit(\"update:modelValue\", html);\r\n emit(\"editor-change\", html);\r\n };\r\n\r\n editor.create();\r\n\r\n nextTick(() => {\r\n if (props.modelValue) {\r\n editor.txt.html(props.modelValue);\r\n }\r\n if (props.readonly) {\r\n editor.disable();\r\n }\r\n editorInstance.value = editor;\r\n isInitialized.value = true;\r\n emit(\"editor-mounted\", editor);\r\n });\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);\r\n }\r\n};\r\n\r\nconst destroyEditor = (): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.destroy();\r\n editorInstance.value = null;\r\n isInitialized.value = false;\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst setContent = (html: string): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.txt.html(html);\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst getContent = (): string => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n return editorInstance.value.txt.html();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);\r\n return \"\";\r\n }\r\n }\r\n return \"\";\r\n};\r\n\r\nwatch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n const currentContent = editorInstance.value.txt.html();\r\n if (currentContent !== newValue) {\r\n editorInstance.value.txt.html(newValue || \"\");\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.disabled,\r\n (disabled) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (disabled) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonlyVal) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (readonlyVal) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nconst handleEditorFocus = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.add(\"editor-focused\");\r\n const containerWidth = container.scrollWidth;\r\n container.style.maxWidth = `${containerWidth}px`;\r\n }\r\n};\r\n\r\nconst handleEditorBlur = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.remove(\"editor-focused\");\r\n container.style.maxWidth = \"\";\r\n }\r\n};\r\n\r\nonMounted(() => {\r\n nextTick(() => {\r\n initializeEditor();\r\n });\r\n});\r\n\r\nonBeforeUnmount(() => {\r\n destroyEditor();\r\n});\r\n\r\ndefineExpose({\r\n initializeEditor,\r\n destroyEditor,\r\n setContent,\r\n getContent,\r\n handleEditorFocus,\r\n handleEditorBlur,\r\n editorInstance: readonly(editorInstance),\r\n isInitialized: readonly(isInitialized),\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n/* 暗色主题样式 */\r\n.editor-dark :deep(.w-e-toolbar) {\r\n background-color: #1f2937 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item) {\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item:hover) {\r\n background-color: #374151 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item.w-e-active) {\r\n background-color: #2080f0 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container) {\r\n background-color: #111827 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text) {\r\n background-color: #303033 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text:focus) {\r\n background-color: #111827 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container) {\r\n max-width: 100% !important;\r\n overflow-x: auto !important;\r\n box-sizing: border-box !important;\r\n}\r\n\r\n:deep(.w-e-text) {\r\n max-width: 100% !important;\r\n word-wrap: break-word !important;\r\n overflow-wrap: break-word !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container),\r\n:deep(.w-e-text) {\r\n transition:\r\n background-color 0.3s ease,\r\n border-color 0.3s ease,\r\n color 0.3s ease !important;\r\n}\r\n\r\n.editor-focused {\r\n overflow: hidden !important;\r\n max-width: 100% !important;\r\n}\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 富文本编辑器组件(简化主题版)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"editorContainer\"\r\n :id=\"editorId\"\r\n v-show=\"isInitialized\"\r\n class=\"w-full\"\r\n :class=\"{ 'editor-dark': isDark }\"\r\n @focusin=\"handleEditorFocus\"\r\n @focusout=\"handleEditorBlur\"\r\n ></div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n nextTick,\r\n onMounted,\r\n onBeforeUnmount,\r\n readonly,\r\n} from \"vue\";\r\nimport E from \"wangeditor\";\r\n\r\ndefineOptions({ name: \"C_Editor\" });\r\n\r\ninterface Props {\r\n editorId: string;\r\n modelValue?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n readonly?: boolean;\r\n height?: number;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"editor-mounted\", editor: any): void;\r\n (e: \"editor-change\", html: string): void;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: \"\",\r\n placeholder: \"\",\r\n disabled: false,\r\n readonly: false,\r\n height: 240,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst editorContainer = ref<HTMLElement | null>(null);\r\nconst editorInstance = ref<any>(null);\r\nconst isInitialized = ref<boolean>(false);\r\n\r\nconst isDark = computed(() => props.theme === \"dark\");\r\n\r\nconst initializeEditor = (): void => {\r\n if (!editorContainer.value || isInitialized.value) return;\r\n\r\n try {\r\n const editor = new E(editorContainer.value);\r\n const editorConfig = editor.config as any;\r\n editorConfig.placeholder = props.placeholder;\r\n editorConfig.height = props.height - 50;\r\n\r\n editorConfig.onchange = (html: string) => {\r\n emit(\"update:modelValue\", html);\r\n emit(\"editor-change\", html);\r\n };\r\n\r\n editor.create();\r\n\r\n nextTick(() => {\r\n if (props.modelValue) {\r\n editor.txt.html(props.modelValue);\r\n }\r\n if (props.readonly) {\r\n editor.disable();\r\n }\r\n editorInstance.value = editor;\r\n isInitialized.value = true;\r\n emit(\"editor-mounted\", editor);\r\n });\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);\r\n }\r\n};\r\n\r\nconst destroyEditor = (): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.destroy();\r\n editorInstance.value = null;\r\n isInitialized.value = false;\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst setContent = (html: string): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.txt.html(html);\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst getContent = (): string => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n return editorInstance.value.txt.html();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);\r\n return \"\";\r\n }\r\n }\r\n return \"\";\r\n};\r\n\r\nwatch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n const currentContent = editorInstance.value.txt.html();\r\n if (currentContent !== newValue) {\r\n editorInstance.value.txt.html(newValue || \"\");\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.disabled,\r\n (disabled) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (disabled) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonlyVal) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (readonlyVal) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nconst handleEditorFocus = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.add(\"editor-focused\");\r\n const containerWidth = container.scrollWidth;\r\n container.style.maxWidth = `${containerWidth}px`;\r\n }\r\n};\r\n\r\nconst handleEditorBlur = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.remove(\"editor-focused\");\r\n container.style.maxWidth = \"\";\r\n }\r\n};\r\n\r\nonMounted(() => {\r\n nextTick(() => {\r\n initializeEditor();\r\n });\r\n});\r\n\r\nonBeforeUnmount(() => {\r\n destroyEditor();\r\n});\r\n\r\ndefineExpose({\r\n initializeEditor,\r\n destroyEditor,\r\n setContent,\r\n getContent,\r\n handleEditorFocus,\r\n handleEditorBlur,\r\n editorInstance: readonly(editorInstance),\r\n isInitialized: readonly(isInitialized),\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n/* 暗色主题样式 */\r\n.editor-dark :deep(.w-e-toolbar) {\r\n background-color: #1f2937 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item) {\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item:hover) {\r\n background-color: #374151 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item.w-e-active) {\r\n background-color: #2080f0 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container) {\r\n background-color: #111827 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text) {\r\n background-color: #303033 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text:focus) {\r\n background-color: #111827 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container) {\r\n max-width: 100% !important;\r\n overflow-x: auto !important;\r\n box-sizing: border-box !important;\r\n}\r\n\r\n:deep(.w-e-text) {\r\n max-width: 100% !important;\r\n word-wrap: break-word !important;\r\n overflow-wrap: break-word !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container),\r\n:deep(.w-e-text) {\r\n transition:\r\n background-color 0.3s ease,\r\n border-color 0.3s ease,\r\n color 0.3s ease !important;\r\n}\r\n\r\n.editor-focused {\r\n overflow: hidden !important;\r\n max-width: 100% !important;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 富文本编辑器组件(简化主题版)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"editorContainer\"\r\n :id=\"editorId\"\r\n v-show=\"isInitialized\"\r\n class=\"w-full\"\r\n :class=\"{ 'editor-dark': isDark }\"\r\n @focusin=\"handleEditorFocus\"\r\n @focusout=\"handleEditorBlur\"\r\n ></div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n nextTick,\r\n onMounted,\r\n onBeforeUnmount,\r\n readonly,\r\n} from \"vue\";\r\nimport E from \"wangeditor\";\r\n\r\ndefineOptions({ name: \"C_Editor\" });\r\n\r\ninterface Props {\r\n editorId: string;\r\n modelValue?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n readonly?: boolean;\r\n height?: number;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"editor-mounted\", editor: any): void;\r\n (e: \"editor-change\", html: string): void;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: \"\",\r\n placeholder: \"\",\r\n disabled: false,\r\n readonly: false,\r\n height: 240,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst editorContainer = ref<HTMLElement | null>(null);\r\nconst editorInstance = ref<any>(null);\r\nconst isInitialized = ref<boolean>(false);\r\n\r\nconst isDark = computed(() => props.theme === \"dark\");\r\n\r\nconst initializeEditor = (): void => {\r\n if (!editorContainer.value || isInitialized.value) return;\r\n\r\n try {\r\n const editor = new E(editorContainer.value);\r\n const editorConfig = editor.config as any;\r\n editorConfig.placeholder = props.placeholder;\r\n editorConfig.height = props.height - 50;\r\n\r\n editorConfig.onchange = (html: string) => {\r\n emit(\"update:modelValue\", html);\r\n emit(\"editor-change\", html);\r\n };\r\n\r\n editor.create();\r\n\r\n nextTick(() => {\r\n if (props.modelValue) {\r\n editor.txt.html(props.modelValue);\r\n }\r\n if (props.readonly) {\r\n editor.disable();\r\n }\r\n editorInstance.value = editor;\r\n isInitialized.value = true;\r\n emit(\"editor-mounted\", editor);\r\n });\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);\r\n }\r\n};\r\n\r\nconst destroyEditor = (): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.destroy();\r\n editorInstance.value = null;\r\n isInitialized.value = false;\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst setContent = (html: string): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.txt.html(html);\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst getContent = (): string => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n return editorInstance.value.txt.html();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);\r\n return \"\";\r\n }\r\n }\r\n return \"\";\r\n};\r\n\r\nwatch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n const currentContent = editorInstance.value.txt.html();\r\n if (currentContent !== newValue) {\r\n editorInstance.value.txt.html(newValue || \"\");\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.disabled,\r\n (disabled) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (disabled) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonlyVal) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (readonlyVal) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nconst handleEditorFocus = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.add(\"editor-focused\");\r\n const containerWidth = container.scrollWidth;\r\n container.style.maxWidth = `${containerWidth}px`;\r\n }\r\n};\r\n\r\nconst handleEditorBlur = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.remove(\"editor-focused\");\r\n container.style.maxWidth = \"\";\r\n }\r\n};\r\n\r\nonMounted(() => {\r\n nextTick(() => {\r\n initializeEditor();\r\n });\r\n});\r\n\r\nonBeforeUnmount(() => {\r\n destroyEditor();\r\n});\r\n\r\ndefineExpose({\r\n initializeEditor,\r\n destroyEditor,\r\n setContent,\r\n getContent,\r\n handleEditorFocus,\r\n handleEditorBlur,\r\n editorInstance: readonly(editorInstance),\r\n isInitialized: readonly(isInitialized),\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n/* 暗色主题样式 */\r\n.editor-dark :deep(.w-e-toolbar) {\r\n background-color: #1f2937 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item) {\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item:hover) {\r\n background-color: #374151 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item.w-e-active) {\r\n background-color: #2080f0 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container) {\r\n background-color: #111827 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text) {\r\n background-color: #303033 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text:focus) {\r\n background-color: #111827 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container) {\r\n max-width: 100% !important;\r\n overflow-x: auto !important;\r\n box-sizing: border-box !important;\r\n}\r\n\r\n:deep(.w-e-text) {\r\n max-width: 100% !important;\r\n word-wrap: break-word !important;\r\n overflow-wrap: break-word !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container),\r\n:deep(.w-e-text) {\r\n transition:\r\n background-color 0.3s ease,\r\n border-color 0.3s ease,\r\n color 0.3s ease !important;\r\n}\r\n\r\n.editor-focused {\r\n overflow: hidden !important;\r\n max-width: 100% !important;\r\n}\r\n</style>\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"C_Editor2.js","names":["editorId"],"sources":["../src/components/C_Editor/index.vue","../src/components/C_Editor/index.vue","../src/components/C_Editor/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 富文本编辑器组件(简化主题版)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"editorContainer\"\r\n :id=\"editorId\"\r\n v-show=\"isInitialized\"\r\n class=\"w-full\"\r\n :class=\"{ 'editor-dark': isDark }\"\r\n @focusin=\"handleEditorFocus\"\r\n @focusout=\"handleEditorBlur\"\r\n ></div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n nextTick,\r\n onMounted,\r\n onBeforeUnmount,\r\n readonly,\r\n} from \"vue\";\r\nimport E from \"wangeditor\";\r\n\r\ndefineOptions({ name: \"C_Editor\" });\r\n\r\ninterface Props {\r\n editorId: string;\r\n modelValue?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n readonly?: boolean;\r\n height?: number;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"editor-mounted\", editor: any): void;\r\n (e: \"editor-change\", html: string): void;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: \"\",\r\n placeholder: \"\",\r\n disabled: false,\r\n readonly: false,\r\n height: 240,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst editorContainer = ref<HTMLElement | null>(null);\r\nconst editorInstance = ref<any>(null);\r\nconst isInitialized = ref<boolean>(false);\r\n\r\nconst isDark = computed(() => props.theme === \"dark\");\r\n\r\nconst initializeEditor = (): void => {\r\n if (!editorContainer.value || isInitialized.value) return;\r\n\r\n try {\r\n const editor = new E(editorContainer.value);\r\n const editorConfig = editor.config as any;\r\n editorConfig.placeholder = props.placeholder;\r\n editorConfig.height = props.height - 50;\r\n\r\n editorConfig.onchange = (html: string) => {\r\n emit(\"update:modelValue\", html);\r\n emit(\"editor-change\", html);\r\n };\r\n\r\n editor.create();\r\n\r\n nextTick(() => {\r\n if (props.modelValue) {\r\n editor.txt.html(props.modelValue);\r\n }\r\n if (props.readonly) {\r\n editor.disable();\r\n }\r\n editorInstance.value = editor;\r\n isInitialized.value = true;\r\n emit(\"editor-mounted\", editor);\r\n });\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);\r\n }\r\n};\r\n\r\nconst destroyEditor = (): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.destroy();\r\n editorInstance.value = null;\r\n isInitialized.value = false;\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst setContent = (html: string): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.txt.html(html);\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst getContent = (): string => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n return editorInstance.value.txt.html();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);\r\n return \"\";\r\n }\r\n }\r\n return \"\";\r\n};\r\n\r\nwatch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n const currentContent = editorInstance.value.txt.html();\r\n if (currentContent !== newValue) {\r\n editorInstance.value.txt.html(newValue || \"\");\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.disabled,\r\n (disabled) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (disabled) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonlyVal) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (readonlyVal) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nconst handleEditorFocus = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.add(\"editor-focused\");\r\n const containerWidth = container.scrollWidth;\r\n container.style.maxWidth = `${containerWidth}px`;\r\n }\r\n};\r\n\r\nconst handleEditorBlur = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.remove(\"editor-focused\");\r\n container.style.maxWidth = \"\";\r\n }\r\n};\r\n\r\nonMounted(() => {\r\n nextTick(() => {\r\n initializeEditor();\r\n });\r\n});\r\n\r\nonBeforeUnmount(() => {\r\n destroyEditor();\r\n});\r\n\r\ndefineExpose({\r\n initializeEditor,\r\n destroyEditor,\r\n setContent,\r\n getContent,\r\n handleEditorFocus,\r\n handleEditorBlur,\r\n editorInstance: readonly(editorInstance),\r\n isInitialized: readonly(isInitialized),\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n/* 暗色主题样式 */\r\n.editor-dark :deep(.w-e-toolbar) {\r\n background-color: #1f2937 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item) {\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item:hover) {\r\n background-color: #374151 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item.w-e-active) {\r\n background-color: #2080f0 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container) {\r\n background-color: #111827 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text) {\r\n background-color: #303033 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text:focus) {\r\n background-color: #111827 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container) {\r\n max-width: 100% !important;\r\n overflow-x: auto !important;\r\n box-sizing: border-box !important;\r\n}\r\n\r\n:deep(.w-e-text) {\r\n max-width: 100% !important;\r\n word-wrap: break-word !important;\r\n overflow-wrap: break-word !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container),\r\n:deep(.w-e-text) {\r\n transition:\r\n background-color 0.3s ease,\r\n border-color 0.3s ease,\r\n color 0.3s ease !important;\r\n}\r\n\r\n.editor-focused {\r\n overflow: hidden !important;\r\n max-width: 100% !important;\r\n}\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 富文本编辑器组件(简化主题版)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"editorContainer\"\r\n :id=\"editorId\"\r\n v-show=\"isInitialized\"\r\n class=\"w-full\"\r\n :class=\"{ 'editor-dark': isDark }\"\r\n @focusin=\"handleEditorFocus\"\r\n @focusout=\"handleEditorBlur\"\r\n ></div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n nextTick,\r\n onMounted,\r\n onBeforeUnmount,\r\n readonly,\r\n} from \"vue\";\r\nimport E from \"wangeditor\";\r\n\r\ndefineOptions({ name: \"C_Editor\" });\r\n\r\ninterface Props {\r\n editorId: string;\r\n modelValue?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n readonly?: boolean;\r\n height?: number;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"editor-mounted\", editor: any): void;\r\n (e: \"editor-change\", html: string): void;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: \"\",\r\n placeholder: \"\",\r\n disabled: false,\r\n readonly: false,\r\n height: 240,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst editorContainer = ref<HTMLElement | null>(null);\r\nconst editorInstance = ref<any>(null);\r\nconst isInitialized = ref<boolean>(false);\r\n\r\nconst isDark = computed(() => props.theme === \"dark\");\r\n\r\nconst initializeEditor = (): void => {\r\n if (!editorContainer.value || isInitialized.value) return;\r\n\r\n try {\r\n const editor = new E(editorContainer.value);\r\n const editorConfig = editor.config as any;\r\n editorConfig.placeholder = props.placeholder;\r\n editorConfig.height = props.height - 50;\r\n\r\n editorConfig.onchange = (html: string) => {\r\n emit(\"update:modelValue\", html);\r\n emit(\"editor-change\", html);\r\n };\r\n\r\n editor.create();\r\n\r\n nextTick(() => {\r\n if (props.modelValue) {\r\n editor.txt.html(props.modelValue);\r\n }\r\n if (props.readonly) {\r\n editor.disable();\r\n }\r\n editorInstance.value = editor;\r\n isInitialized.value = true;\r\n emit(\"editor-mounted\", editor);\r\n });\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);\r\n }\r\n};\r\n\r\nconst destroyEditor = (): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.destroy();\r\n editorInstance.value = null;\r\n isInitialized.value = false;\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst setContent = (html: string): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.txt.html(html);\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst getContent = (): string => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n return editorInstance.value.txt.html();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);\r\n return \"\";\r\n }\r\n }\r\n return \"\";\r\n};\r\n\r\nwatch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n const currentContent = editorInstance.value.txt.html();\r\n if (currentContent !== newValue) {\r\n editorInstance.value.txt.html(newValue || \"\");\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.disabled,\r\n (disabled) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (disabled) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonlyVal) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (readonlyVal) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nconst handleEditorFocus = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.add(\"editor-focused\");\r\n const containerWidth = container.scrollWidth;\r\n container.style.maxWidth = `${containerWidth}px`;\r\n }\r\n};\r\n\r\nconst handleEditorBlur = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.remove(\"editor-focused\");\r\n container.style.maxWidth = \"\";\r\n }\r\n};\r\n\r\nonMounted(() => {\r\n nextTick(() => {\r\n initializeEditor();\r\n });\r\n});\r\n\r\nonBeforeUnmount(() => {\r\n destroyEditor();\r\n});\r\n\r\ndefineExpose({\r\n initializeEditor,\r\n destroyEditor,\r\n setContent,\r\n getContent,\r\n handleEditorFocus,\r\n handleEditorBlur,\r\n editorInstance: readonly(editorInstance),\r\n isInitialized: readonly(isInitialized),\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n/* 暗色主题样式 */\r\n.editor-dark :deep(.w-e-toolbar) {\r\n background-color: #1f2937 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item) {\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item:hover) {\r\n background-color: #374151 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item.w-e-active) {\r\n background-color: #2080f0 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container) {\r\n background-color: #111827 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text) {\r\n background-color: #303033 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text:focus) {\r\n background-color: #111827 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container) {\r\n max-width: 100% !important;\r\n overflow-x: auto !important;\r\n box-sizing: border-box !important;\r\n}\r\n\r\n:deep(.w-e-text) {\r\n max-width: 100% !important;\r\n word-wrap: break-word !important;\r\n overflow-wrap: break-word !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container),\r\n:deep(.w-e-text) {\r\n transition:\r\n background-color 0.3s ease,\r\n border-color 0.3s ease,\r\n color 0.3s ease !important;\r\n}\r\n\r\n.editor-focused {\r\n overflow: hidden !important;\r\n max-width: 100% !important;\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 富文本编辑器组件(简化主题版)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"editorContainer\"\r\n :id=\"editorId\"\r\n v-show=\"isInitialized\"\r\n class=\"w-full\"\r\n :class=\"{ 'editor-dark': isDark }\"\r\n @focusin=\"handleEditorFocus\"\r\n @focusout=\"handleEditorBlur\"\r\n ></div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n nextTick,\r\n onMounted,\r\n onBeforeUnmount,\r\n readonly,\r\n} from \"vue\";\r\nimport E from \"wangeditor\";\r\n\r\ndefineOptions({ name: \"C_Editor\" });\r\n\r\ninterface Props {\r\n editorId: string;\r\n modelValue?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n readonly?: boolean;\r\n height?: number;\r\n theme?: \"light\" | \"dark\";\r\n}\r\n\r\ninterface Emits {\r\n (e: \"update:modelValue\", value: string): void;\r\n (e: \"editor-mounted\", editor: any): void;\r\n (e: \"editor-change\", html: string): void;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: \"\",\r\n placeholder: \"\",\r\n disabled: false,\r\n readonly: false,\r\n height: 240,\r\n theme: \"light\",\r\n});\r\n\r\nconst emit = defineEmits<Emits>();\r\n\r\nconst editorContainer = ref<HTMLElement | null>(null);\r\nconst editorInstance = ref<any>(null);\r\nconst isInitialized = ref<boolean>(false);\r\n\r\nconst isDark = computed(() => props.theme === \"dark\");\r\n\r\nconst initializeEditor = (): void => {\r\n if (!editorContainer.value || isInitialized.value) return;\r\n\r\n try {\r\n const editor = new E(editorContainer.value);\r\n const editorConfig = editor.config as any;\r\n editorConfig.placeholder = props.placeholder;\r\n editorConfig.height = props.height - 50;\r\n\r\n editorConfig.onchange = (html: string) => {\r\n emit(\"update:modelValue\", html);\r\n emit(\"editor-change\", html);\r\n };\r\n\r\n editor.create();\r\n\r\n nextTick(() => {\r\n if (props.modelValue) {\r\n editor.txt.html(props.modelValue);\r\n }\r\n if (props.readonly) {\r\n editor.disable();\r\n }\r\n editorInstance.value = editor;\r\n isInitialized.value = true;\r\n emit(\"editor-mounted\", editor);\r\n });\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器初始化失败: ${props.editorId}`, error);\r\n }\r\n};\r\n\r\nconst destroyEditor = (): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.destroy();\r\n editorInstance.value = null;\r\n isInitialized.value = false;\r\n } catch (error) {\r\n console.error(`[C_Editor] 编辑器销毁失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst setContent = (html: string): void => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n editorInstance.value.txt.html(html);\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器内容失败: ${props.editorId}`, error);\r\n }\r\n }\r\n};\r\n\r\nconst getContent = (): string => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n return editorInstance.value.txt.html();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 获取编辑器内容失败: ${props.editorId}`, error);\r\n return \"\";\r\n }\r\n }\r\n return \"\";\r\n};\r\n\r\nwatch(\r\n () => props.modelValue,\r\n (newValue) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n const currentContent = editorInstance.value.txt.html();\r\n if (currentContent !== newValue) {\r\n editorInstance.value.txt.html(newValue || \"\");\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.disabled,\r\n (disabled) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (disabled) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nwatch(\r\n () => props.readonly,\r\n (readonlyVal) => {\r\n if (editorInstance.value && isInitialized.value) {\r\n try {\r\n if (readonlyVal) editorInstance.value.disable();\r\n else editorInstance.value.enable();\r\n } catch (error) {\r\n console.warn(`[C_Editor] 设置编辑器只读状态失败`, error);\r\n }\r\n }\r\n },\r\n);\r\n\r\nconst handleEditorFocus = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.add(\"editor-focused\");\r\n const containerWidth = container.scrollWidth;\r\n container.style.maxWidth = `${containerWidth}px`;\r\n }\r\n};\r\n\r\nconst handleEditorBlur = (): void => {\r\n if (!editorContainer.value) return;\r\n const container = editorContainer.value.closest(\r\n \".form-demo\",\r\n ) as HTMLElement | null;\r\n if (container) {\r\n container.classList.remove(\"editor-focused\");\r\n container.style.maxWidth = \"\";\r\n }\r\n};\r\n\r\nonMounted(() => {\r\n nextTick(() => {\r\n initializeEditor();\r\n });\r\n});\r\n\r\nonBeforeUnmount(() => {\r\n destroyEditor();\r\n});\r\n\r\ndefineExpose({\r\n initializeEditor,\r\n destroyEditor,\r\n setContent,\r\n getContent,\r\n handleEditorFocus,\r\n handleEditorBlur,\r\n editorInstance: readonly(editorInstance),\r\n isInitialized: readonly(isInitialized),\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n/* 暗色主题样式 */\r\n.editor-dark :deep(.w-e-toolbar) {\r\n background-color: #1f2937 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item) {\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item:hover) {\r\n background-color: #374151 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-toolbar .w-e-menu .w-e-menu-item.w-e-active) {\r\n background-color: #2080f0 !important;\r\n color: #ffffff !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container) {\r\n background-color: #111827 !important;\r\n border-color: #374151 !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text) {\r\n background-color: #303033 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n.editor-dark :deep(.w-e-text-container .w-e-text:focus) {\r\n background-color: #111827 !important;\r\n color: #e5e7eb !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container) {\r\n max-width: 100% !important;\r\n overflow-x: auto !important;\r\n box-sizing: border-box !important;\r\n}\r\n\r\n:deep(.w-e-text) {\r\n max-width: 100% !important;\r\n word-wrap: break-word !important;\r\n overflow-wrap: break-word !important;\r\n}\r\n\r\n:deep(.w-e-toolbar),\r\n:deep(.w-e-text-container),\r\n:deep(.w-e-text) {\r\n transition:\r\n background-color 0.3s ease,\r\n border-color 0.3s ease,\r\n color 0.3s ease !important;\r\n}\r\n\r\n.editor-focused {\r\n overflow: hidden !important;\r\n max-width: 100% !important;\r\n}\r\n</style>\r\n"],"mappings":"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"}
|
package/dist/C_FilePreview.cjs
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,
|
|
2
|
-
require('./C_Icon.js');
|
|
3
|
-
const require_C_FilePreview = require('./C_FilePreview.js');
|
|
4
|
-
|
|
5
|
-
exports.C_FilePreview = require_C_FilePreview.C_FilePreview_default;
|
|
6
|
-
exports.useFilePreview = require_C_FilePreview.useFilePreview;
|
|
7
|
-
exports.useFullscreen = require_C_FilePreview.useFullscreen;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./C_Icon.js`);const e=require(`./C_FilePreview.js`);exports.C_FilePreview=e.t,exports.useFilePreview=e.r,exports.useFullscreen=e.n;
|
package/dist/C_FilePreview.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { n as useFullscreen, r as useFilePreview, t as C_FilePreview_default } from "./C_FilePreview2.js";
|
|
3
|
-
|
|
4
|
-
export { C_FilePreview_default as C_FilePreview, useFilePreview, useFullscreen };
|
|
1
|
+
import"./C_Icon2.js";import{n as e,r as t,t as n}from"./C_FilePreview2.js";export{n as C_FilePreview,t as useFilePreview,e as useFullscreen};
|
|
@@ -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\"\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 size=\"small\" @click=\"adjustZoom('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 </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\nimport { ref } from \"vue\";\r\nimport { NButton, NInputNumber } from \"naive-ui\";\r\nimport { createZoomHandler, ZOOM_CONFIGS } from \"../../data\";\r\n\r\nconst props = defineProps<{\r\n pdfUrl: string;\r\n totalPages: number;\r\n}>();\r\n\r\nconst currentPage = ref(1);\r\nconst scale = ref(1);\r\n\r\nconst adjustZoom = createZoomHandler(scale, ZOOM_CONFIGS.pdf);\r\n\r\nconst 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\"\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 size=\"small\" @click=\"adjustZoom('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 </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\nimport { ref } from \"vue\";\r\nimport { NButton, NInputNumber } from \"naive-ui\";\r\nimport { createZoomHandler, ZOOM_CONFIGS } from \"../../data\";\r\n\r\nconst props = defineProps<{\r\n pdfUrl: string;\r\n totalPages: number;\r\n}>();\r\n\r\nconst currentPage = ref(1);\r\nconst scale = ref(1);\r\n\r\nconst adjustZoom = createZoomHandler(scale, ZOOM_CONFIGS.pdf);\r\n\r\nconst 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\"\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 size=\"small\" @click=\"adjustZoom('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 </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\nimport { ref } from \"vue\";\r\nimport { NButton, NInputNumber } from \"naive-ui\";\r\nimport { createZoomHandler, ZOOM_CONFIGS } from \"../../data\";\r\n\r\nconst props = defineProps<{\r\n pdfUrl: string;\r\n totalPages: number;\r\n}>();\r\n\r\nconst currentPage = ref(1);\r\nconst scale = ref(1);\r\n\r\nconst adjustZoom = createZoomHandler(scale, ZOOM_CONFIGS.pdf);\r\n\r\nconst 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 v-if=\"!autoPreview\" class=\"file-info-card\">\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 :type=\"fileConfig.tagType\" size=\"small\">\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 type=\"primary\" @click=\"openPreview\">\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 type=\"tertiary\" @click=\"downloadFile\">\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 ref=\"modalContainer\" class=\"modal-container\">\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 size=\"small\" type=\"tertiary\" @click=\"downloadFile\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n <NButton size=\"small\" type=\"tertiary\" @click=\"showModal = false\">\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 :type=\"fileConfig.tagType\" size=\"small\">\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 size=\"small\" type=\"primary\" @click=\"toggleFullscreen\">\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 v-if=\"loading\" size=\"large\">\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\nimport { ref, toRefs, watch } from \"vue\";\r\nimport { NButton, NEllipsis, NTag, NModal, NSpin, NResult } from \"naive-ui\";\r\nimport { formatFileSize } from \"./data\";\r\nimport PdfViewer from \"./components/PdfViewer/index.vue\";\r\nimport WordViewer from \"./components/WordViewer/index.vue\";\r\nimport ExcelViewer from \"./components/ExcelViewer/index.vue\";\r\nimport { useFilePreview } from \"./composables/useFilePreview\";\r\nimport { useFullscreen } from \"./composables/useFullscreen\";\r\n\r\ndefineOptions({ name: \"C_FilePreview\" });\r\n\r\nconst 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\nconst { file, url, fileName: propFileName, autoPreview } = toRefs(props);\r\n\r\nconst emit = defineEmits<{\r\n preview: [file: File | string];\r\n download: [file: File | string];\r\n}>();\r\n\r\n/* ==================== Composables ==================== */\r\nconst {\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\nconst modalContainer = ref<HTMLElement>();\r\nconst { isFullscreen, toggleFullscreen, exitFullscreen } =\r\n useFullscreen(modalContainer);\r\n\r\n/* ==================== Modal 关闭时退出全屏 ==================== */\r\nwatch(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 v-if=\"!autoPreview\" class=\"file-info-card\">\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 :type=\"fileConfig.tagType\" size=\"small\">\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 type=\"primary\" @click=\"openPreview\">\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 type=\"tertiary\" @click=\"downloadFile\">\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 ref=\"modalContainer\" class=\"modal-container\">\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 size=\"small\" type=\"tertiary\" @click=\"downloadFile\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n <NButton size=\"small\" type=\"tertiary\" @click=\"showModal = false\">\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 :type=\"fileConfig.tagType\" size=\"small\">\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 size=\"small\" type=\"primary\" @click=\"toggleFullscreen\">\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 v-if=\"loading\" size=\"large\">\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\nimport { ref, toRefs, watch } from \"vue\";\r\nimport { NButton, NEllipsis, NTag, NModal, NSpin, NResult } from \"naive-ui\";\r\nimport { formatFileSize } from \"./data\";\r\nimport PdfViewer from \"./components/PdfViewer/index.vue\";\r\nimport WordViewer from \"./components/WordViewer/index.vue\";\r\nimport ExcelViewer from \"./components/ExcelViewer/index.vue\";\r\nimport { useFilePreview } from \"./composables/useFilePreview\";\r\nimport { useFullscreen } from \"./composables/useFullscreen\";\r\n\r\ndefineOptions({ name: \"C_FilePreview\" });\r\n\r\nconst 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\nconst { file, url, fileName: propFileName, autoPreview } = toRefs(props);\r\n\r\nconst emit = defineEmits<{\r\n preview: [file: File | string];\r\n download: [file: File | string];\r\n}>();\r\n\r\n/* ==================== Composables ==================== */\r\nconst {\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\nconst modalContainer = ref<HTMLElement>();\r\nconst { isFullscreen, toggleFullscreen, exitFullscreen } =\r\n useFullscreen(modalContainer);\r\n\r\n/* ==================== Modal 关闭时退出全屏 ==================== */\r\nwatch(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 v-if=\"!autoPreview\" class=\"file-info-card\">\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 :type=\"fileConfig.tagType\" size=\"small\">\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 type=\"primary\" @click=\"openPreview\">\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 type=\"tertiary\" @click=\"downloadFile\">\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 ref=\"modalContainer\" class=\"modal-container\">\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 size=\"small\" type=\"tertiary\" @click=\"downloadFile\">\r\n <template #icon>\r\n <C_Icon name=\"ic:outline-download\" />\r\n </template>\r\n 下载\r\n </NButton>\r\n <NButton size=\"small\" type=\"tertiary\" @click=\"showModal = false\">\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 :type=\"fileConfig.tagType\" size=\"small\">\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 size=\"small\" type=\"primary\" @click=\"toggleFullscreen\">\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 v-if=\"loading\" size=\"large\">\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\nimport { ref, toRefs, watch } from \"vue\";\r\nimport { NButton, NEllipsis, NTag, NModal, NSpin, NResult } from \"naive-ui\";\r\nimport { formatFileSize } from \"./data\";\r\nimport PdfViewer from \"./components/PdfViewer/index.vue\";\r\nimport WordViewer from \"./components/WordViewer/index.vue\";\r\nimport ExcelViewer from \"./components/ExcelViewer/index.vue\";\r\nimport { useFilePreview } from \"./composables/useFilePreview\";\r\nimport { useFullscreen } from \"./composables/useFullscreen\";\r\n\r\ndefineOptions({ name: \"C_FilePreview\" });\r\n\r\nconst 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\nconst { file, url, fileName: propFileName, autoPreview } = toRefs(props);\r\n\r\nconst emit = defineEmits<{\r\n preview: [file: File | string];\r\n download: [file: File | string];\r\n}>();\r\n\r\n/* ==================== Composables ==================== */\r\nconst {\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\nconst modalContainer = ref<HTMLElement>();\r\nconst { isFullscreen, toggleFullscreen, exitFullscreen } =\r\n useFullscreen(modalContainer);\r\n\r\n/* ==================== Modal 关闭时退出全屏 ==================== */\r\nwatch(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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"}
|