wangeditor自定义扩展设置图片宽高

背景

当前项目中用到wangeditor 插件, 需要扩展图片宽高

项目背景 : vue2

项目最终效果

wangeditor的安装

bash 复制代码
npm install wangeditor/editor
npm install wangeditor/editor-for-vue

基本使用

xml 复制代码
// 可直接运行
<template>
  <el-dialog
    :visible.sync="dialogVisible"
    :close-on-click-modal="false"
    fullscreen
  >
    <template #title>
      <div class="editor-title1 flex justify-between">
        <div class="font-bold">标题:{{ title }}</div>
        <div class="flex item-center">
          <el-color-picker v-model="color" @change="handleColorChange" />
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button v-if="!disabled" type="primary" @click="handleSubmit">确定</el-button>
        </div>
      </div>
    </template>
    <div class="article-content">
      <div v-if="dialogVisible" class="left-box">
        <Toolbar :editor="editor" :default-config="toolbarConfig" :mode="mode" />
        <Editor
          v-model="html"
          style="height: 82vh; overflow-y: hidden;"
          :default-config="editorConfig"
          :mode="mode"
          @onCreated="onCreated"
        />
      </div>
      <div class="right-box">
        <div class="preview-content" :style="{ background: color, padding: '16px' }" v-html="html" />
      </div>
    </div>
  </el-dialog>
</template>

<script>
import { uploadCmsImage } from '@/api/cms/index'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { Boot } from '@wangeditor/editor'
import ImageWidthMenu from './a.js'

// 注册插件
export default {
  components: { Editor, Toolbar },  // 引入必须组件
  data() {
    return {
      color: '#000', // 设置背景色,便于在不同的背景中显示
      title: '',
      disabled: false,
      dialogVisible: false,
      editor: null,
      html: '',
      showUploading: false,
      toolbarConfig: {},
      editorConfig: {},
      mode: 'default' // or 'simple'
    }
  },
  created() {
   // 核心代码, 自定义扩展图片宽度 ----start----
    const imageWidth75 = {
      key: 'imageWidth75',
      factory() {
        return new ImageWidthMenu('75%')
      }
    }
    const imageWidth80 = {
      key: 'imageWidth80',
      factory() {
        return new ImageWidthMenu('80%')
      }
    }
    const imageWidth85 = {
      key: 'imageWidth85',
      factory() {
        return new ImageWidthMenu('85%')
      }
    }
    Boot.registerMenu(imageWidth75)
    Boot.registerMenu(imageWidth80)
    Boot.registerMenu(imageWidth85)
     // 核心代码, 自定义扩展图片宽度 ----end----
    this.editorConfig = {
      placeholder: '请输入内容...',
      MENU_CONF: {
      //  自定义图片上传接口
        uploadImage: {
          customUpload: async(file, insertFn) => {
            await this.handleCustomUpload(file, insertFn)
          }
        },
      //  自定义视频上传接口
        uploadVideo: {
          customUpload: async(file, insertFn) => {
            await this.handleCustomUpload(file, insertFn)
          }
        }
      },
      hoverbarKeys: {
        image: {
          menuKeys: [
            'imageWidth30', 
            'imageWidth50',
            'imageWidth75',  // 核心代码, 自定义扩展图片宽度
            'imageWidth80', // 核心代码, 自定义扩展图片宽度
            'imageWidth85', // 核心代码, 自定义扩展图片宽度
            'imageWidth100',
            'editImage',
            'viewImageLink',
            'deleteImage'
          ]
        }
      }
    }
  },
  beforeDestroy() {
    const editor = this.editor
    if (editor == null) return
    editor.destroy() // 组件销毁时,及时销毁编辑器
  },
  methods: {
    // 打开弹窗
    async open(data, disabled) {
      this.title = data.title
      this.html = data.content
      this.disabled = disabled
      this.dialogVisible = true
      this.color = '#000'
    },

    async handleCustomUpload(file, insertFn) {
      try {
        const params = new FormData()
        params.append('file', file)
        params.append('path', 'cms')
        const res = await uploadCmsImage(params)
        insertFn(res.data.url, file.name, res.data.url)
      } catch (e) {
        console.log(e)
      }
    },

    onCreated(editor) {
      this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
      setTimeout(() => {
        const textContainer = document.querySelector('.w-e-text-container')
        if (textContainer) {
          textContainer.style.fontSize = '24px'
        }
      }, 100)
      document.getElementsByClassName('w-e-text-container')[0].setAttribute('style', `background-color: ${this.color};`)
    },

    // 提交数据
    async handleSubmit() {
      this.$emit('updateContent', 'edit', this.html)
      this.dialogVisible = false
      this.html = ''
    },

    handleColorChange(color) {
      document.getElementsByClassName('w-e-text-container')[0].setAttribute('style', `background-color: ${color};`)
    }

  }
}
</script>
<style src="@wangeditor/editor/dist/css/style.css"></style>
<style lang="scss">
// 弹窗
.editor-dialog1 {
  border-radius: 0;

  .el-dialog__headerbtn {
    display: none;
  }

  .el-dialog__body {
    padding: 0 16px;
  }
}

.w-e-text-container h1 {
  font-size: 29px !important;
}

.w-e-text-container h2 {
  font-size: 27px !important;
}

.w-e-text-container h3 {
  font-size: 25px !important;
}

video {
  display: block;
  margin: 0 auto
}
</style>

<style lang="scss" scoped>
// 内容
.article-content {
  display: flex;
  height: calc(100vh - 90px);
  justify-content: space-between;
  box-sizing: border-box;

  .left-box {
    width: 49%;
    border-right: 1px solid #ccc;
    padding-right: 10px;
  }

  .right-box {
    width: 50%;
    height: 100%;
    overflow-y: auto;
  }
}

//标题
.editor-title1 {
  border-bottom: 2px solid #ddd;
  padding-bottom: 10px;
  box-sizing: border-box;

  .el-color-picker {
    margin-right: 10px;
  }
}
</style>

核心代码, 扩展代码

a.js 复制代码
import { DomEditor, SlateTransforms } from '@wangeditor/editor'
export default class ImageWidthMenu {
  constructor(width) {
    this.title = width || '70%'
    this.tag = 'button'
    this.width = width || '70%'
    // this.iconSvg = '<svg width="18" height="18" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M832 192H192c-17.7 0-32 14.3-32 32v576c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32z m-40 560H232V264h560v488z" fill="#333"/></svg>'
  }

  getValue(editor) {
    return ''
  }

  // 获取选中的图片节点
  getSelectedNode(editor) {
    const { selection } = editor
    if (!selection) return null
    return DomEditor.getSelectedNodeByType(editor, 'image')
  }

  // 检查是否激活(当前宽度是否为指定宽度)
  isActive(editor) {
    return false
    // const node = this.getSelectedNode(editor)
    // if (!node) return false
    // return node.style.width === this.width
  }

  // 没有选中图片则禁用

  isDisabled(editor) {
    if (editor.selection == null) return true

    const imageNode = this.getSelectedNode(editor)
    if (imageNode == null) {
      // 选区未处于 image node ,则禁用
      return true
    }
    return false
  }

  exec(editor) {
    if (this.isDisabled(editor)) return

    const imageNode = this.getSelectedNode(editor)
    if (imageNode == null) return

    // 隐藏 hoverbar
    const hoverbar = DomEditor.getHoverbar(editor)
    if (hoverbar) hoverbar.hideAndClean()

    const { style = {}} = imageNode // 假设 imageNode 是 ImageElement 类型
    const props = {
      style: {
        ...style,
        width: this.width, // 使用传入的 width 参数
        height: '' // 清空 height
      }
    }

    SlateTransforms.setNodes(editor, props, {
      match: n => DomEditor.checkNodeType(n, 'image')
    })
  }
}
相关推荐
呵阿咯咯1 小时前
Vue3项目记录
前端·vue.js
yigenhuochai1 小时前
Trae Solo 开发体验:从零到完整考试备考平台的奇妙之旅
前端·trae
夏目友人爱吃豆腐2 小时前
uniapp源码解析(Vue3/Vite版)
前端·vue.js·uni-app
JarvanMo2 小时前
Dart 3.10中的新的lint规则
前端
爱心发电丶2 小时前
基于UniappX开发电销APP,实现通话录音上传、通时通次
前端
sxjk19872 小时前
华为IMS系统主要接口备忘
运维·服务器·前端·核心网
T***u3332 小时前
前端Server Components性能分析 Server Components架构原理
前端
Q***f6352 小时前
前端动画性能优化,60fps实现技巧
前端
艾莉丝努力练剑2 小时前
【自动化测试实战篇】Web自动化测试实战:从用例编写到报告生成
前端·人工智能·爬虫·python·pycharm·自动化·测试