mart-admin/src/components/editor/index.vue

179 lines
4.5 KiB
Vue
Raw Normal View History

<template>
<div class="editor-container">
2024-11-01 23:31:28 +08:00
<Toolbar v-if="!props.disable" :editor="editorRef" :mode="mode" />
<div v-else class="replace">
<el-icon class="full" size="25px" @click="openCurrenFullscreen">
<ele-FullScreen />
</el-icon>
</div>
<Editor
:mode="mode"
:defaultConfig="state.editorConfig"
:style="{ height }"
v-model="state.editorVal"
@onCreated="handleCreated"
@onChange="handleChange"
/>
</div>
</template>
<script setup lang="ts" name="wngEditor">
// https://www.wangeditor.com/v5/for-frame.html#vue3
import '@wangeditor/editor/dist/css/style.css';
2024-11-01 23:31:28 +08:00
import { reactive, shallowRef, watch, onBeforeUnmount, ref } from 'vue';
import { IDomEditor } from '@wangeditor/editor';
import { Toolbar, Editor } from '@wangeditor/editor-for-vue';
import { articleApi } from '/@/api/article';
import { ElMessage } from 'element-plus';
// 定义父组件传过来的值
const props = defineProps({
// 是否禁用
disable: {
type: Boolean,
default: () => false,
},
// 内容框默认 placeholder
placeholder: {
type: String,
default: () => '请输入内容...',
},
// https://www.wangeditor.com/v5/getting-started.html#mode-%E6%A8%A1%E5%BC%8F
// 模式,可选 <default|simple>,默认 default
mode: {
type: String,
default: () => 'default',
},
// 高度
height: {
type: String,
default: () => '310px',
},
// 双向绑定,用于获取 editor.getHtml()
getHtml: String,
// 双向绑定,用于获取 editor.getText()
getText: String,
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['update:getHtml', 'update:getText']);
// 定义变量内容
const editorRef = shallowRef();
const state = reactive({
editorConfig: {
placeholder: props.placeholder,
2024-11-01 23:31:28 +08:00
readOnly: props.disable,
MENU_CONF:{}
},
editorVal: props.getHtml,
});
// 引入 api 请求接口
const artApi = articleApi();
// 自定义插入图片、视频方法类型
type InsertImgFnType = (url: string, alt: string, href: string) => void;
type InsertVideoFnType = (url: string, poster: string) => void
// 基本路径
const viteUrl = import.meta.env.VITE_API_URL;
// 图片上传配置
state.editorConfig.MENU_CONF['uploadImage'] = {
// 自定义插入图片
async customUpload(file: File, insertFn: InsertImgFnType) {
const formdata = new FormData();
formdata.append('file', file);
if(file.type.includes('image')){
let res = await artApi.uploadFile(formdata);
if(res?.success) insertFn(encodeURI(viteUrl+res.data.path), res.data.title, '#')
else ElMessage.error('图片上传失败!');
}else{
ElMessage.error('上传图片格式错误!');
}
},
};
// 视频上传配置
state.editorConfig.MENU_CONF['uploadVideo'] = {
// 自定义插入图片
async customUpload(file: File, insertFn: InsertVideoFnType) {
const formdata = new FormData();
formdata.append('file', file);
if(file.type.includes('video')){
let res = await artApi.uploadFile(formdata);
if(res?.success) insertFn(encodeURI(viteUrl+res.data.path), '')
else ElMessage.error('视频上传失败!');
}else{
ElMessage.error('上传视频格式错误!');
}
},
}
// 编辑器回调函数
const handleCreated = (editor: IDomEditor) => {
editorRef.value = editor;
};
// 编辑器内容改变时
const handleChange = (editor: IDomEditor) => {
emit('update:getHtml', editor.getHtml());
emit('update:getText', editor.getText());
};
2024-11-01 23:31:28 +08:00
const isFull = ref(false); // 是否全屏
// 全屏
const openCurrenFullscreen = () => {
const editor = editorRef.value // 获取 editor ,必须等待它渲染完之后
if (editor == null) return;
isFull.value ? editor.unFullScreen() : editor.fullScreen();
isFull.value = !isFull.value;
}
// 页面销毁时
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
// 监听是否禁用改变
// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
watch(
() => props.disable,
(bool) => {
const editor = editorRef.value;
if (editor == null) return;
2024-11-01 23:31:28 +08:00
console.log(bool)
bool ? editor.disable() : editor.enable();
},
{
deep: true,
}
);
// 监听双向绑定值改变,用于回显
watch(
() => props.getHtml,
(val) => {
state.editorVal = val;
},
{
deep: true,
}
);
</script>
2024-11-01 23:31:28 +08:00
<style scoped lang="scss">
.replace{
background-color: #ffffff;
border-top: 1px solid #e4e7ed;
border-left: 1px solid #e4e7ed;
border-right: 1px solid #e4e7ed;
display: flex;
justify-content: flex-end;
.full{
padding: 5px;
cursor: pointer;
}
}
</style>