1、打开前言 | vue-element-plus-admin
2、找到相关链接
3、拉取代码https://github.com/kailong321200875/vue-element-plus-admin
4、拿到代码找到src->components->Editor.vue模块
Editor.vue模块
<script setup lang="ts">
import { onBeforeUnmount, computed, PropType, unref, nextTick, ref, watch, shallowRef } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IDomEditor, IEditorConfig, i18nChangeLanguage } from '@wangeditor/editor'
import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is'
import { ElMessage } from 'element-plus'
import { useLocaleStore } from '@/store/modules/locale'
import { upFileData } from '@/api/common'
import type { InsertFnType } from './types'
import {extractImageDataFromRtf, replaceImagesFileSourceWithInlineRepresentation } from './customPasteFile'
const PATH_IMG_URL = import.meta.env.VITE_API_BASE_IMG_PATH
const localeStore = useLocaleStore()
const currentLocale = computed(() => localeStore.getCurrentLocale)
i18nChangeLanguage(unref(currentLocale).lang)
const props = defineProps({
editorId: propTypes.string.def('wangeEditor-1'),
height: propTypes.oneOfType([Number, String]).def('500px'),
editorConfig: {
type: Object as PropType<IEditorConfig>,
default: () => undefined
},
modelValue: propTypes.string.def('')
})
const emit = defineEmits(['change', 'update:modelValue'])
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef<IDomEditor>()
const toolbarConfig = reactive({
excludeKeys: [
'uploadVideo'
]
})
const valueHtml = ref('')
watch(
() => props.modelValue,
(val: string) => {
if (val === unref(valueHtml)) return
valueHtml.value = val
},
{
immediate: true
}
)
// 监听
watch(
() => valueHtml.value,
(val: string) => {
emit('update:modelValue', val)
}
)
const handleCreated = (editor: IDomEditor) => {
editorRef.value = editor
}
// 编辑器配置
const editorConfig = computed((): IEditorConfig => {
return Object.assign(
{
readOnly: false,
customAlert: (s: string, t: string) => {
switch (t) {
case 'success':
ElMessage.success(s)
break
case 'info':
ElMessage.info(s)
break
case 'warning':
ElMessage.warning(s)
break
case 'error':
ElMessage.error(s)
break
default:
ElMessage.info(s)
break
}
},
autoFocus: false,
scroll: true,
uploadImgShowBase64: true,
MENU_CONF: {
'uploadImage': {
async customUpload(file: File, insertFn: InsertFnType) { // TS 语法
const formData = new FormData()
formData.append('file', file)
upFileData(formData).then(res => {
if (res && res.data) {
insertFn(PATH_IMG_URL + res.data.fileUrl, res.data.fileName, PATH_IMG_URL + res.data.fileUrl)
}
})
}
},
},
},
props.editorConfig || {}
)
})
/**
* 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑。
* @param editor
* @param event
* @returns
*/
const customPaste=(editor: IDomEditor, event): boolean => {
let html = event?.clipboardData.getData('text/html');
const rtf = event?.clipboardData.getData('text/rtf');
// 该条件分支即表示要自定义word粘贴
if (html && rtf) {
// 列表缩进会超出边框,直接过滤掉
html = html.replace(/text\-indent:\-(.*?)pt/gi, '')
// 从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
const imgSrcs = html.match(/<v:imagedata [^>]*src=['"]([^'"]+)[^>]*>/g);
// 如果有
if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
// 从rtf内容中查找图片数据
const rtfImageData = extractImageDataFromRtf(rtf);
console.log(rtfImageData,'ref')
// 如果找到
if (rtfImageData.length) {
// TODO:此处可以将图片上传到自己的服务器上,
// // 执行替换:将html内容中的img标签的src替换成ref中的图片数据,如果上面上传了则为图片路径
html = replaceImagesFileSourceWithInlineRepresentation(html, imgSrcs, rtfImageData)
editor.dangerouslyInsertHtml(html);
}
} else {
return true;
}
// 阻止默认的粘贴行为
event.preventDefault();
return false;
} else {
return true;
}
}
const editorStyle = computed(() => {
return {
height: isNumber(props.height) ? `${props.height}px` : props.height
}
})
// 回调函数
const handleChange = (editor: IDomEditor) => {
emit('change', editor)
}
// 组件销毁时,及时销毁编辑器
onBeforeUnmount(() => {
const editor = unref(editorRef.value)
// 销毁,并移除 editor
editor?.destroy()
})
const getEditorRef = async (): Promise<IDomEditor> => {
await nextTick()
return unref(editorRef.value) as IDomEditor
}
defineExpose({
getEditorRef
})
</script>
<template>
<div class="border-1 border-solid border-[var(--el-border-color)] z-10">
<!-- 工具栏 -->
<Toolbar :editor="editorRef" :editorId="editorId" :defaultConfig="toolbarConfig"
class="border-0 b-b-1 border-solid border-[var(--el-border-color)]" />
<!-- 编辑器 -->
<Editor v-model="valueHtml" :editorId="editorId" :defaultConfig="editorConfig" :style="editorStyle"
@on-change="handleChange" @on-created="handleCreated" @custom-paste="customPaste" />
<!-- <div class="hideSource" style="margin-top: 10px;width: 100%;">
<textarea
v-model="valueHtml"
style="width: 100%; height: 200px; outline: none"
></textarea>
</div> -->
</div>
</template>
<style src="@wangeditor/editor/dist/css/style.css"></style>
问题一:图片上传
修改上传图片自定义功能
后端接口
5、创建customPasteFile.ts模块
/**
* 从rtf内容中匹配返回图片数据的集合
* @param rtfData
* @return Array
*/
export const extractImageDataFromRtf = (rtfData) => {
if (!rtfData) {
return [];
}
const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g');
const images = rtfData.match(regexPicture);
const result =ref([]) as any;
if (images) {
for (const image of images) {
const imageType =ref(false) as any
if (image.includes('\\pngblip')) {
imageType.value = 'image/png';
} else if (image.includes('\\jpegblip')) {
imageType.value = 'image/jpeg';
}
if (imageType.value) {
result.value.push({
hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
type: imageType.value
});
}
}
}
return result.value;
}
/**
* 将html内容中img标签的属性值替换
* @param htmlData html内容
* @param imageSrcs html中img的属性src的值的集合
* @param imagesHexSources rtf中图片数据的集合,与html内容中的img标签对应
* @param isBase64Data 是否是Base64的图片数据
* @return String
*/
export const replaceImagesFileSourceWithInlineRepresentation=(htmlData, imageSrcs, imagesHexSources, isBase64Data =
true) => {
if (imageSrcs.length === imagesHexSources.length) {
for (let i = 0; i < imageSrcs.length; i++) {
const newSrc = isBase64Data ?
`<div id="w-e-image-container-1100" class="w-e-image-container"><img src='data:${imagesHexSources[i].type};base64,${_convertHexToBase64(imagesHexSources[i].hex)}'/></div>` :
imagesHexSources[i];
htmlData = htmlData.replace(imageSrcs[i], newSrc);
}
}
return htmlData;
}
/**
* 十六进制转base64
*/
export const _convertHexToBase64=(hexString)=>{
return btoa(hexString.match(/\w{2}/g).map(char => {
return String.fromCharCode(parseInt(char, 16));
}).join(''));
}
6、组件使用
问题二:从word复制粘贴图文不显示图片问题
主要修改了参考文章里面部分代码
1、修改从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
// 从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
const imgSrcs = html.match(/<v:imagedata [^>]*src=['"]([^'"]+)[^>]*>/g);
2、修改将html内容中img标签的属性值替换
const newSrc = isBase64Data ?
`<div id="w-e-image-container-1100" class="w-e-image-container"><img src='data:${imagesHexSources[i].type};base64,${_convertHexToBase64(imagesHexSources[i].hex)}'/></div>` :
imagesHexSources[i];