vue3 wangeditor 实现图片上传, 复制图片等

npm install wangeditor

<template>
    <div class="WangeEitorModeule">
        <Toolbar
            style="border-bottom: 1px solid #ccc"
            :editor="editorRef"
            :defaultConfig="toolbarConfig"
            :mode="mode"
        />
        <Editor
            style="height: 500px; overflow-y: hidden"
            :style="{ height: props.height }"
            v-model="valueHtml"
            :defaultConfig="editorConfig"
            :mode="mode"
            @onCreated="handleCreated"
            @customPaste="customPaste"
        />
    </div>
</template>
<script lang="ts" setup>
import { upload } from '@/Api/common'
import { computed, shallowRef, onBeforeUnmount, ref, watch, nextTick } from 'vue'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IEditorConfig } from '@wangeditor/editor'
import { useStore } from 'vuex'
const store = useStore()

interface Props {
    modelValue: string | any
    height?: string // 编辑器的高度
    placeholder?: string
}
interface EmitEvent {
    (e: 'update:modelValue', params: string): void
}
const props = withDefaults(defineProps<Props>(), {
    height: '550px',
    placeholder: '请输入内容...',
    modelValue: ''
})

const emit = defineEmits<EmitEvent>()

let valueHtml = ref<string>('')

watch(
    () => props.modelValue,
    newVal => {
        nextTick(() => {
            valueHtml.value = newVal
        })
    }
)

watch(
    () => valueHtml.value,
    newVal => {
        emit('update:modelValue', newVal)
    }
)

// const valueHtml = computed({
//     get() {
//         return props.modelValue
//     },
//     set(value: string) {
//         emit('update:modelValue', value)
//     }
// })

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()

const uploadImg = (file, insertFn) => {
    console.log('file', file)
    const formData = new FormData()

    formData.append('image', file as any)
    upload(formData).then(e => {
        if (e.RsCode == 3) {
            insertFn(
                store.state.MediaUrl + e.RsData.replaceAll('_', '/'),
                file.name,
                store.state.MediaUrl + e.RsData.replaceAll('_', '/')
            )
        }
    })
}

const uploadVideo = (file, insertFn) => {
    console.log('file', file)
    const formData = new FormData()

    formData.append('video', file as any)
    upload(formData).then(e => {
        if (e.RsCode == 3) {
            insertFn(
                store.state.MediaUrl + e.RsData.replaceAll('_', '/'),
                file.name,
                store.state.MediaUrl + e.RsData.replaceAll('_', '/')
            )
        }
    })
}

const mode = 'simple'
const toolbarConfig = {}
//上传图片的地址
const uploadFileUrl = '/api/api/Media/upload'
const editorConfig: Partial<IEditorConfig> = { placeholder: props.placeholder, MENU_CONF: {} }
editorConfig.MENU_CONF!['uploadImage'] = {
    // 自定义上传图片 方法
    customUpload: uploadImg,
    server: uploadFileUrl,
    uploadImgMaxLength: 9,
    maxFileSize: 5 * 1024 * 1024, // 单个文件的最大体积限制,默认为 5M
    fieldName: 'image',
    meta: {
        // source: 'sys_user_guide'
    }
}
editorConfig.MENU_CONF!['uploadVideo'] = {
    // 自定义上传视频 方法
    customUpload: uploadVideo,
    maxFileSize: 10 * 1024 * 1024,
    fieldName: 'file',
    meta: {
        source: 'sys_user_guide'
    }
}
const customPaste = (editor, event) => {
    // 获取粘贴的html部分(??没错粘贴word时候,一部分内容就是html),该部分包含了图片img标签
    let html = event.clipboardData.getData('text/html')

    // 获取rtf数据(从word、wps复制粘贴时有),复制粘贴过程中图片的数据就保存在rtf中
    const rtf = event.clipboardData.getData('text/rtf')

    if (html && rtf) {
        // 该条件分支即表示要自定义word粘贴

        // 列表缩进会超出边框,直接过滤掉
        html = html.replace(/text\-indent:\-(.*?)pt/gi, '')

        // 从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
        const imgSrcs = findAllImgSrcsFromHtml(html)

        // 如果有
        if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
            // 从rtf内容中查找图片数据
            const rtfImageData = extractImageDataFromRtf(rtf)

            // 如果找到
            if (rtfImageData.length) {
                // TODO:此处可以将图片上传到自己的服务器上

                // 执行替换:将html内容中的img标签的src替换成ref中的图片数据,如果上面上传了则为图片路径
                html = replaceImagesFileSourceWithInlineRepresentation(html, imgSrcs, rtfImageData)
                editor.dangerouslyInsertHtml(html)
            }
        }

        // 阻止默认的粘贴行为
        event.preventDefault()
        return false
    } else {
        return true
    }
}

/**
 * 从html代码中匹配返回图片标签img的属性src的值的集合
 * @param htmlData
 * @return Array
 */
function findAllImgSrcsFromHtml(htmlData) {
    let imgReg = /<img.*?(?:>|\/>)/gi //匹配图片中的img标签
    let srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i // 匹配图片中的src

    let arr = htmlData.match(imgReg) //筛选出所有的img
    if (!arr || (Array.isArray(arr) && !arr.length)) {
        return false
    }

    let srcArr = []
    for (let i = 0; i < arr.length; i++) {
        let src = arr[i].match(srcReg)
        // 获取图片地址
        srcArr.push(src[1])
    }
    console.log(srcArr)

    return srcArr
}

/**
 * 从rtf内容中匹配返回图片数据的集合
 * @param rtfData
 * @return Array
 */
function 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 = []

    if (images) {
        for (const image of images) {
            let imageType = ''

            if (image.includes('\\pngblip')) {
                imageType = 'image/png'
            } else if (image.includes('\\jpegblip')) {
                imageType = 'image/jpeg'
            }

            if (imageType) {
                result.push({
                    hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
                    type: imageType
                })
            }
        }
    }

    return result
}

/**
 * 将html内容中img标签的属性值替换
 * @param htmlData html内容
 * @param imageSrcs html中img的属性src的值的集合
 * @param imagesHexSources rtf中图片数据的集合,与html内容中的img标签对应
 * @param isBase64Data 是否是Base64的图片数据
 * @return String
 */
function replaceImagesFileSourceWithInlineRepresentation(
    htmlData,
    imageSrcs,
    imagesHexSources,
    isBase64Data = true
) {
    if (imageSrcs.length === imagesHexSources.length) {
        for (let i = 0; i < imageSrcs.length; i++) {
            const newSrc = isBase64Data
                ? `data:${imagesHexSources[i].type};base64,${_convertHexToBase64(
                      imagesHexSources[i].hex
                  )}`
                : imagesHexSources[i]

            htmlData = htmlData.replace(imageSrcs[i], newSrc)
        }
    }

    return htmlData
}

/**
 * 十六进制转base64
 */
function _convertHexToBase64(hexString) {
    return btoa(
        hexString
            .match(/\w{2}/g)
            .map(char => {
                return String.fromCharCode(parseInt(char, 16))
            })
            .join('')
    )
}

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
    const { value } = editorRef
    if (value === null) return
    value?.destroy()
})
const handleCreated = (editor: any) => {
    editorRef.value = editor // 记录 editor 实例,重要!
}
</script>
<style lang="scss" scoped>
  .WangeEitorModeule {
      border: 1px solid #ccc;
      z-index: 9999;
  }
</style>

使用

<WangeDitor
    ref="WangEditorRef"
    placeholder="请输入自定义内容"
    v-model:="value"
></WangeDitor>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了在Vue3中使用wangeditor实现上传图片功能,需要进行以下步骤: 1. 安装wangeditor和axios ```shell npm install wangeditor axios --save ``` 2. 在组件中引入wangeditor和axios ```javascript import WangEditor from 'wangeditor' import axios from 'axios' ``` 3. 在组件的mounted钩子函数中初始化wangeditor ```javascript mounted() { const editor = new WangEditor('#editor') editor.config.uploadImgServer = '/upload' // 上传图片的接口地址 editor.config.uploadFileName = 'file' // 上传图片的文件名 editor.config.uploadImgHeaders = { // 上传图片时需要携带的请求头 Authorization: 'Bearer ' + localStorage.getItem('token') } editor.config.uploadImgHooks = { // 上传图片的回调函数 customInsert: function (insertImg, result, editor) { if (result.code === 200) { insertImg(result.data.url) } else { alert('上传失败') } } } editor.create() } ``` 4. 在组件中添加一个textarea元素,并将其id设置为editor ```html <template> <div> <textarea id="editor"></textarea> </div> </template> ``` 5. 在后端实现上传图片的接口,并将其地址填写到第3步中的uploadImgServer属性中 ```javascript const express = require('express') const multer = require('multer') const app = express() const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, 'uploads/') }, filename: function (req, file, cb) { cb(null, Date.now() + '-' + file.originalname) } }) const upload = multer({ storage: storage }) app.post('/upload', upload.single('file'), (req, res) => { const file = req.file if (!file) { const error = new Error('Please upload a file') error.httpStatusCode = 400 return next(error) } res.send({ code: 200, data: { url: 'http://localhost:3000/' + file.path } }) }) app.listen(3000, () => { console.log('Server started on port 3000') }) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值