178 lines
4.1 KiB
Vue
178 lines
4.1 KiB
Vue
<template>
|
|
<a-spin :loading="false" class="w-full h-full editor-container">
|
|
<div class="main">
|
|
<Toolbar :class="toolbarClass" class="toolbar" :editor="editorRef" :defaultConfig="toolbarConfig" v-if="props.displayMode === 'edit'" />
|
|
<Editor :class="editorClass" class="editor" @onChange="handleChange" v-model="valueHtml" :defaultConfig="editorConfig" @onCreated="handleCreated" key="main" />
|
|
</div>
|
|
<Direcotry class="directory" v-model:content="valueHtml" v-if="showDirecotry" />
|
|
</a-spin>
|
|
<Preview v-model:visible="showPreview" :content="previewHtml" />
|
|
</template>
|
|
|
|
<script setup>
|
|
import Preview from "./modules/preview.vue";
|
|
import Direcotry from "./modules/directory.vue";
|
|
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
|
|
import { uploadVideo, uploadImage, uploadToAliyun } from "@/utils/file";
|
|
import { Message } from "@arco-design/web-vue";
|
|
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
|
|
|
|
import { ref, computed, onBeforeUnmount, shallowRef, watch, nextTick, defineExpose, onMounted } from "vue";
|
|
import { registerPreviewMenu, toolbarConfig } from "./config";
|
|
|
|
const props = defineProps({
|
|
displayMode: {
|
|
type: String,
|
|
default: "edit" // 'edit' 表示编辑器模式,'view' 表示只读/阅读模式
|
|
},
|
|
modelValue: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
showDirecotry: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
|
|
|
const previewHtml = ref(""); // 只读副本
|
|
|
|
const showPreview = ref(false);
|
|
|
|
onMounted(() => {
|
|
registerPreviewMenu(() => {
|
|
previewHtml.value = valueHtml.value;
|
|
showPreview.value = true;
|
|
});
|
|
});
|
|
|
|
const toolbarClass = computed(() => {
|
|
return props.displayMode === "view" ? "toolbar-view" : "toolbar-edit";
|
|
});
|
|
|
|
const editorClass = computed(() => {
|
|
return props.displayMode === "view" ? "editor-view" : "editor-edit";
|
|
});
|
|
|
|
// 编辑器实例,必须用 shallowRef
|
|
const editorRef = shallowRef();
|
|
defineExpose({
|
|
getText: () => {
|
|
if (editorRef.value) {
|
|
return editorRef.value.getText();
|
|
} else {
|
|
console.error("Editor not initialized");
|
|
return "";
|
|
}
|
|
}
|
|
});
|
|
|
|
// 内容 HTML
|
|
const valueHtml = ref("");
|
|
|
|
// 目录
|
|
|
|
watch(
|
|
() => props.modelValue,
|
|
(newVal) => {
|
|
valueHtml.value = newVal;
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
const handleChange = () => {
|
|
emit("update:modelValue", valueHtml);
|
|
};
|
|
|
|
const editorConfig = {
|
|
placeholder: props.displayMode === "edit" ? "请输入内容..." : "",
|
|
readOnly: props.displayMode === "view",
|
|
MENU_CONF: {} // ✅ 这一步不能漏!
|
|
};
|
|
|
|
editorConfig.MENU_CONF["uploadImage"] = {
|
|
// 自定义选择图片
|
|
customBrowseAndUpload(insertFn) {
|
|
uploadImage().then((data) => {
|
|
if (data) {
|
|
uploadToAliyun(data.blob, data.name, data.size).then((res) => {
|
|
insertFn(res.url, res.fileName, "");
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
editorConfig.MENU_CONF["uploadVideo"] = {
|
|
customBrowseAndUpload(insertFn) {
|
|
uploadVideo().then((data) => {
|
|
if (data) {
|
|
uploadToAliyun(data.blob, data.name, data.size).then((res) => {
|
|
insertFn(res.url, res.fileName, "");
|
|
});
|
|
} else {
|
|
Message.error("上传失败,请稍后再试!");
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// 组件销毁时,也及时销毁编辑器
|
|
onBeforeUnmount(() => {
|
|
const editor = editorRef.value;
|
|
if (editor == null) return;
|
|
editor.destroy();
|
|
});
|
|
|
|
const handleCreated = (editor) => {
|
|
editorRef.value = editor;
|
|
|
|
nextTick(() => {});
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.toolbar {
|
|
}
|
|
.toolbar-view {
|
|
}
|
|
.toolbar-edit {
|
|
border: 1px solid #ccc;
|
|
}
|
|
.editor {
|
|
overflow-y: hidden;
|
|
:deep(.w-e-text-container video) {
|
|
width: 100% !important;
|
|
}
|
|
}
|
|
.editor-view {
|
|
height: auto;
|
|
:deep(.w-e-text-container) {
|
|
background-color: transparent;
|
|
}
|
|
:deep(.w-e-text-container .w-e-scroll [data-slate-editor]) {
|
|
overflow-y: auto;
|
|
padding: 0 0px !important;
|
|
}
|
|
}
|
|
.editor-edit {
|
|
height: 500px !important;
|
|
border: 1px solid #ccc;
|
|
border-top: transparent;
|
|
}
|
|
|
|
.editor-container {
|
|
display: flex;
|
|
gap: 44px;
|
|
.directory {
|
|
width: 200px;
|
|
height: 450px;
|
|
}
|
|
.main {
|
|
flex: 1;
|
|
}
|
|
}
|
|
</style>
|