<!-- WangEditor 富文本编辑器 插件地址:https://www.wangeditor.com/ -->
|
<template>
|
<div class="editor-wrapper">
|
<Toolbar
|
class="editor-toolbar"
|
:editor="editorRef"
|
:mode="mode"
|
:defaultConfig="toolbarConfig"
|
/>
|
<Editor
|
:style="{ height: height, overflowY: 'hidden' }"
|
v-model="modelValue"
|
:mode="mode"
|
:defaultConfig="editorConfig"
|
@onCreated="onCreateEditor"
|
/>
|
</div>
|
</template>
|
|
<script setup>
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
|
import '@wangeditor/editor/dist/css/style.css'
|
import { onBeforeUnmount, onMounted, shallowRef, computed } from 'vue'
|
import { useUserStore } from '@/store/modules/user'
|
import EmojiText from '@/utils/ui/emojo'
|
import request from '@/utils/http'
|
defineOptions({ name: 'ArtWangEditor' })
|
const { VITE_API_URL } = import.meta.env
|
const props = defineProps({
|
height: { required: false, default: '500px' },
|
mode: { required: false, default: 'default' },
|
placeholder: { required: false, default: '请输入内容...' },
|
excludeKeys: { required: false, default: () => ['fontFamily'] },
|
isCustomUpload: { required: false, default: false }
|
})
|
const modelValue = defineModel({ required: true })
|
const editorRef = shallowRef()
|
const userStore = useUserStore()
|
const DEFAULT_UPLOAD_CONFIG = {
|
maxFileSize: 3 * 1024 * 1024,
|
// 3MB
|
maxNumberOfFiles: 10,
|
fieldName: 'file',
|
allowedFileTypes: ['image/*']
|
}
|
const uploadServer = computed(
|
() => props.uploadConfig?.server || `${VITE_API_URL}/api/common/upload/wangeditor`
|
)
|
const mergedUploadConfig = computed(() => ({
|
...DEFAULT_UPLOAD_CONFIG,
|
...props.uploadConfig
|
}))
|
const toolbarConfig = computed(() => {
|
const config = {}
|
if (props.toolbarKeys && props.toolbarKeys.length > 0) {
|
config.toolbarKeys = props.toolbarKeys
|
}
|
if (props.insertKeys) {
|
config.insertKeys = props.insertKeys
|
}
|
if (props.excludeKeys && props.excludeKeys.length > 0) {
|
config.excludeKeys = props.excludeKeys
|
}
|
return config
|
})
|
const editorConfig = {
|
placeholder: props.placeholder,
|
MENU_CONF: {
|
uploadImage: {
|
fieldName: mergedUploadConfig.value.fieldName,
|
maxFileSize: mergedUploadConfig.value.maxFileSize,
|
maxNumberOfFiles: mergedUploadConfig.value.maxNumberOfFiles,
|
allowedFileTypes: mergedUploadConfig.value.allowedFileTypes,
|
server: uploadServer.value,
|
headers: {
|
Authorization: userStore.accessToken
|
},
|
onSuccess() {
|
ElMessage.success(`图片上传成功 ${EmojiText[200]}`)
|
},
|
onError(file, err, res) {
|
console.error('图片上传失败:', err, res)
|
ElMessage.error(`图片上传失败 ${EmojiText[500]}`)
|
}
|
}
|
}
|
}
|
if (props.uploadConfig?.isCustomUpload && props.uploadConfig?.server && editorConfig.MENU_CONF) {
|
editorConfig.MENU_CONF.uploadImage.customUpload = async (file, insertFn) => {
|
try {
|
const formData = new FormData()
|
formData.append(mergedUploadConfig.value.fieldName, file)
|
const response = await request.post({
|
url: props.uploadConfig?.server,
|
data: formData,
|
headers: {
|
'Content-Type': 'multipart/form-data',
|
Authorization: userStore.accessToken
|
}
|
})
|
const { url, alt, href } = response
|
if (!url) {
|
throw new Error('上传失败,请检查服务端配置')
|
}
|
insertFn(url, alt, href)
|
ElMessage.success(`图片上传成功 ${EmojiText[200]}`)
|
} catch (error) {
|
console.error('图片上传失败:', error)
|
ElMessage.error(`图片上传失败 ${EmojiText[500]}`)
|
}
|
}
|
}
|
const onCreateEditor = (editor) => {
|
editorRef.value = editor
|
editor.on('fullScreen', () => {
|
console.log('编辑器进入全屏模式')
|
})
|
applyCustomIcons()
|
}
|
const applyCustomIcons = () => {
|
let retryCount = 0
|
const maxRetries = 10
|
const retryDelay = 100
|
const tryApplyIcons = () => {
|
const editor = editorRef.value
|
if (!editor) {
|
if (retryCount < maxRetries) {
|
retryCount++
|
setTimeout(tryApplyIcons, retryDelay)
|
}
|
return
|
}
|
const editorContainer = editor.getEditableContainer().closest('.editor-wrapper')
|
if (!editorContainer) {
|
if (retryCount < maxRetries) {
|
retryCount++
|
setTimeout(tryApplyIcons, retryDelay)
|
}
|
return
|
}
|
const toolbar = editorContainer.querySelector('.w-e-toolbar')
|
const toolbarButtons = editorContainer.querySelectorAll('.w-e-bar-item button[data-menu-key]')
|
if (toolbar && toolbarButtons.length > 0) {
|
return
|
}
|
if (retryCount < maxRetries) {
|
retryCount++
|
setTimeout(tryApplyIcons, retryDelay)
|
} else {
|
console.warn('工具栏渲染超时,无法应用自定义图标 - 编辑器实例:', editor.id)
|
}
|
}
|
requestAnimationFrame(tryApplyIcons)
|
}
|
defineExpose({
|
/** 获取编辑器实例 */
|
getEditor: () => editorRef.value,
|
/** 设置编辑器内容 */
|
setHtml: (html) => editorRef.value?.setHtml(html),
|
/** 获取编辑器内容 */
|
getHtml: () => editorRef.value?.getHtml(),
|
/** 清空编辑器 */
|
clear: () => editorRef.value?.clear(),
|
/** 聚焦编辑器 */
|
focus: () => editorRef.value?.focus()
|
})
|
onMounted(() => {})
|
onBeforeUnmount(() => {
|
const editor = editorRef.value
|
if (editor) {
|
editor.destroy()
|
}
|
})
|
</script>
|
|
<style lang="scss">
|
@use './style';
|
</style>
|