因为wangEditor的图片上传参数无法满足需求所以需要用自己的上传来覆盖原先的上传
覆盖原生的图片上传操作方法是
async customUpload(file: File, insertFn: InsertFnType) { // TS 语法,这里是覆盖原生的上传,这里覆盖的原因是我们图片要做一定的加密,但是原生的不能做到携带我们的参数
// file 即选中的文件
// 最后插入图片
// insertFn(url, alt, href)
console.log('shangchaun :>> ', file, insertFn);
let url = import.meta.env.VITE_UPLOAD_URL//这里是请求路径
try {
const response = await axios.post(url,
{
file: file,
sign: getSign(file.name),
fileName: file.name,
path: null
}
, {
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json, text/plain, */*',
},
}); // 替换为你的 API 端点和数据
console.log('POST response:', response.data);
if (response.data.code != 500) {
insertFn(response.data.data, response.data.data, response.data.data) // 插入图片,参数:图片 src、alt、href
} else {
message.error(response.data.msg)
}
} catch (error) {
console.error('Error posting data:', error);
}
}
在这里需要注意在进行post请求时,需要注意headers里的对象和原生的是否一致,这一点很重要
第二就是返回的传输路径需要使用insertFn方法再插入进富文本框
完整代码,这个是封装的富文本组件的内容
这个是Editor.vue
<script lang="ts" setup>
import { PropType } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { i18nChangeLanguage, IDomEditor, IEditorConfig, IToolbarConfig } 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 { getAccessToken, getTenantId } from '@/utils/auth'
import axios from 'axios'
const message = useMessage() // 消息弹窗
import CryptoJS from 'crypto-js';
const secretKey = import.meta.env.VITE_SECRET_KEY //这里是自定义的加密秘钥
// cosnt file = ref({})
defineOptions({ name: 'Editor' })
type InsertFnType = (url: string, alt: string, href: string) => void
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<Partial<IEditorConfig>>,
default: () => undefined
},
readonly: propTypes.bool.def(false),
modelValue: propTypes.string.def('')
})
const emit = defineEmits(['change', 'update:modelValue'])
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef<IDomEditor>()
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 getSign = (name) => {//加密秘钥的处理,和上传参数一并带去
let secret = [secretKey.substring(0, 4), secretKey.substring(4, 8), secretKey.substring(8)]
let str = secret[0] + null + secret[1] + name + secret[2]
let sign = CryptoJS.MD5(str).toString();
return sign;
// 修改 meta 的值
}
const handleCreated = (editor: IDomEditor) => {
editorRef.value = editor
}
const toolbarConfig: Partial<IToolbarConfig> = {
excludeKeys: [
'uploadVideo', // 排除菜单组,写菜单组 key 的值即可
"group-video",
]
}
// 编辑器配置
const editorConfig = computed((): IEditorConfig => {
return Object.assign(
{
placeholder: '请输入内容...',
readOnly: props.readonly,
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,
MENU_CONF: {
['uploadImage']: {
server: import.meta.env.VITE_UPLOAD_URL,
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 5 * 1024 * 1024,
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 10,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
meta: { updateSupport: 0 },
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: true,
// 自定义增加 http header
headers: {
Accept: '*',
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
},
// 跨域是否传递 cookie ,默认为 false
withCredentials: true,
// 超时时间,默认为 10 秒
timeout: 5 * 1000, // 5 秒
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
fieldName: 'file',
// 上传之前触发
onBeforeUpload(file: File) {
// console.log('kankansheikuai22222 :>> ');
// for (const key in file) {
// handleFileChange(file[key])
// }
return file
},
// 上传进度的回调函数
onProgress(progress: number) {
// progress 是 0-100 的数字
console.log('progress', progress)
},
onSuccess(file: File, res: any) {
console.log('onSuccess', file, res)
},
onFailed(file: File, res: any) {
alert(res.message)
console.log('onFailed', file, res)
},
onError(file: File, err: any, res: any) {
alert(err.message)
console.error('onError', file, err, res)
},
// 自定义插入图片
customInsert(res: any, insertFn: InsertFnType) {
insertFn(res.data, 'image', res.data)
},
async customUpload(file: File, insertFn: InsertFnType) { // TS 语法,这里是覆盖原生的上传,这里覆盖的原因是我们图片要做一定的加密,但是原生的不能做到携带我们的参数
// file 即选中的文件
// 最后插入图片
// insertFn(url, alt, href)
console.log('shangchaun :>> ', file, insertFn);
let url = import.meta.env.VITE_UPLOAD_URL
try {
const response = await axios.post(url,
{
file: file,
sign: getSign(file.name),
fileName: file.name,
path: null
}
, {
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json, text/plain, */*',
},
}); // 替换为你的 API 端点和数据
console.log('POST response:', response.data);
if (response.data.code != 500) {
insertFn(response.data.data, response.data.data, response.data.data) // 插入图片,参数:图片 src、alt、href
} else {
message.error(response.data.msg)
}
} catch (error) {
console.error('Error posting data:', error);
}
}
},
// ['uploadVideo']: {
// server: import.meta.env.VITE_UPLOAD_URL,
// // 单个文件的最大体积限制,默认为 5M
// maxFileSize: 5 * 1024 * 1024,
// // 最多可上传几个文件,默认为 5
// maxNumberOfFiles: 5,
// // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
// allowedFileTypes: ['video/*'],
// // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
// meta: {},
// // 将 meta 拼接到 url 参数中,默认 false
// metaWithUrl: true,
// // 自定义增加 http header
// headers: {
// Accept: '*',
// Authorization: 'Bearer ' + getAccessToken(),
// 'tenant-id': getTenantId()
// },
// // 跨域是否传递 cookie ,默认为 false
// withCredentials: true,
// // 超时时间,默认为 10 秒
// timeout: 10 * 1000, // 10 秒
// // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
// fieldName: 'file',
// // 视频不支持 base64 格式插入
// // 上传之前触发
// onBeforeUpload(file: File) {
// return file
// },
// // 自定义插入图片
// customInsert(res: any, insertFn: InsertFnType) {
// insertFn(res.data, 'image', res.data)
// },
// async customUpload(file: File, insertFn: InsertFnType) { // TS 语法,这里是覆盖原生的上传,这里覆盖的原因是我们图片要做一定的加密,但是原生的不能做到携带我们的参数
// // file 即选中的文件
// // 最后插入图片
// // insertFn(url, alt, href)
// let url = import.meta.env.VITE_UPLOAD_URL
// try {
// const response = await axios.post(url,
// {
// file: file,
// sign: getSign(file.name),
// fileName: file.name,
// path: null
// }
// , {
// headers: {
// 'Content-Type': 'multipart/form-data',
// 'Accept': 'application/json, text/plain, */*',
// },
// }); // 替换为你的 API 端点和数据
// console.log('POST response:', response.data);
// if (response.data.code != 500) {
// insertFn(response.data.data) // 插入图片,参数:url poster
// } else {
// message.error(response.data.msg)
// }
// } catch (error) {
// console.error('Error posting data:', error);
// }
// }
// }
},
uploadImgShowBase64: true
},
props.editorConfig || {}
)
})
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-99">
<!-- 工具栏 -->
<Toolbar :editor="editorRef" :editorId="editorId" :defaultConfig="toolbarConfig"
class="border-0 b-b-1 border-solid border-[var(--el-border-color)]" />
<!-- 编辑器 -->
<Editor v-model="valueHtml" :defaultConfig="editorConfig" :editorId="editorId" :style="editorStyle"
@on-change="handleChange" @on-created="handleCreated" />
</div>
</template>
<style src="@wangeditor/editor/dist/css/style.css"></style>
index.ts
import Editor from './src/Editor.vue'
import { IDomEditor } from '@wangeditor/editor'
export interface EditorExpose {
getEditorRef: () => Promise<IDomEditor>
}
export { Editor }
使用
<Editor v-model="formData.content" height="150px" />