背景
当前项目中用到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')
})
}
}