单组件文件
代码如下:
<template>
<div class="editor-container">
<el-input
v-if="!isReadOnly"
v-model="localTitle"
placeholder="请输入文章标题"
size="large"
style="margin-bottom: 10px"
/>
<div :style="wrapperStyle" class="editor-wrapper">
<!-- 工具栏 -->
<Toolbar
v-if="!isReadOnly"
:editor="editorRef"
:defaultConfig="toolbarConfig"
style="border-bottom: 1px solid #ccc"
/>
<!-- 编辑器包一层容器,用于监听双击 -->
<div ref="editorWrapper">
<Editor
v-model="localContent"
:defaultConfig="editorConfig"
:style="editorStyle"
@onCreated="handleCreated"
/>
</div>
</div>
<!-- 相对于弹窗定位的右下角按钮 -->
<div v-if="!isReadOnly" class="bottom-buttons">
<el-button @click="$emit('cancel')">取消</el-button>
<el-button type="primary" @click="submit">提交</el-button>
</div>
<!-- 图片查看器 -->
<el-image-viewer
v-if="showViewer"
:url-list="[viewerUrl]"
@close="showViewer = false"
/>
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css'
import {ref, computed, shallowRef, watch, onBeforeUnmount, onMounted, onUnmounted} from 'vue'
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
import {sendAxiosRequest} from '@/utils/common.js'
import {ElMessage} from 'element-plus'
const props = defineProps({
title: String,
content: String,
isReadOnly: Boolean
})
const emits = defineEmits(['submit', 'cancel'])
const localTitle = ref(props.title || '')
const localContent = ref(props.content || '')
const editorRef = shallowRef(null)
const editorWrapper = ref(null)
const toolbarConfig = {
excludeKeys: ['insertVideo', 'uploadVideo']
}
const editorConfig = {
placeholder: '请输入正文...',
readOnly: props.isReadOnly,
MENU_CONF: {
uploadImage: {
async customUpload(file, insertFn) {
const formData = new FormData()
formData.append('file', file)
formData.append('spliceUrl', 'editorImage')
const result = await sendAxiosRequest('/pub-api/upload/uploadFile', formData)
if (result && !result.isError) {
insertFn(result.result['fileViewUrl'])
} else {
ElMessage.error(result.errMsg)
}
}
}
}
}
// 图片查看器
const showViewer = ref(false)
const viewerUrl = ref('')
function showImagePreview(url) {
viewerUrl.value = url
showViewer.value = true
}
function onDblClick(e) {
const target = e.target
if (target.tagName === 'IMG') {
showImagePreview(target.src)
}
}
function handleCreated(editor) {
editorRef.value = editor
}
function submit() {
if (!localTitle.value.trim()) {
ElMessage.error('请输入标题')
return
}
emits('submit', {
title: localTitle.value.trim(),
content: localContent.value.trim()
})
localTitle.value = ''
localContent.value = ''
}
const wrapperStyle = computed(() => ({
border: '0px solid #ccc',
marginBottom: '0px',
minHeight: props.isReadOnly ? '300px' : 'auto',
}))
const editorStyle = computed(() => ({
minHeight: props.isReadOnly ? '300px' : 'auto',
height: props.isReadOnly ? '100%' : '420px',
overflowY: 'auto',
}))
onMounted(() => {
if (editorWrapper.value) {
editorWrapper.value.addEventListener('dblclick', onDblClick)
}
})
onUnmounted(() => {
if (editorWrapper.value) {
editorWrapper.value.removeEventListener('dblclick', onDblClick)
}
})
onBeforeUnmount(() => {
if (editorRef.value) {
editorRef.value.destroy()
editorRef.value = null
}
})
watch(() => props.title, (val) => {
localTitle.value = val
})
watch(() => props.content, (val) => {
localContent.value = val
})
</script>
<style scoped>
.editor-container {
position: relative;
display: flex;
flex-direction: column;
height: 600px;
overflow: hidden;
}
.editor-wrapper {
flex: 1;
overflow-y: auto;
}
.bottom-buttons {
margin-top: auto;
text-align: right;
padding: 12px 0 0 0;
background-color: #fff;
}
:deep(img) {
cursor: zoom-in;
}
</style>