vue3+wangEditor 5 的图片自定义上传

因为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" />

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值