这个原本是给 vm-manager 写的一个富文本编辑器,后来以为独立出来维护比较方便,就单独分离出来放到NPM。之因此说人人都会写, 是由于这个组件实现起来确实比较简单,不须要很厉害的Js水平, 基本是对document.execCommand()指令的绑定。在此将过程分享给你们html
预览地址 luosijie.github.io/vm-editor/vue
源码地址 github.com/luosijie/vm…git
npm install --save vm-editor复制代码
//upload绑定事件将编译的html字符传给父组件
<VmEditor @upload="showHtml"></VmEditor>
<script>
import VmEditor from 'vm-editor'
export default {
...
methods: {
showHtml: function(data){
console.log(data)
}
}
}
</script>复制代码
由于是Vue组件, 因此写这样的一个组件,须要掌握的知识点有:github
组件由 菜单部分和内容区域 2大部分组成, 其中菜单区域又由各类 指令按钮 组成,部分指令按钮还有下拉选项npm
指令按钮是 execConmand 的装载器,须要实现如下功能浏览器
<template>
<button class="vm-editor-button" :class="{ active: slot }">
// 显示按钮图标
<img :src="require('../assets/iconimg/' + icon + '.png')" height="16" width="16" alt="" @click="showSlot">
<!-- <i :class="icon" @click="showSlot"></i> -->
// 部分按钮须要实现点击展开下拉菜单
<slot v-if="slot"></slot>
</button>
</template>
<style>
...
// 划过显示背景
button.vm-editor-button:hover{
background-color: #eee;
}
...
</style>
export default {
name: 'VmEditorButton',
props: {
icon: {
type: String,
default: 'heading'
}
},
data: function () {
return {
slot: false
}
},
methods: {
showSlot () {
this.slot === false ? this.slot = true : this.slot = false
}
}
}
</script>复制代码
<template>
<div class="vm-editor-menu">
// 引入按钮组建, 经过 **click事件** 绑定封装的 **execCommand方法** 实现样式的变化
<VmEditorButton icon="paragraph" @click.native="execCommand('formatBlock', '<p>')">
</VmEditorButton>
<VmEditorButton icon="heading">
<VmEditorDropdown>
// 这是部分按钮须要下拉菜单功能
<ul class="vm-editor-ul">
<li @click="execCommand('formatBlock', '<h1>')">
<h1>H1</h1>
</li>
<li @click="execCommand('formatBlock', '<h2>')">
<h2>H2</h2>
</li>
<li @click="execCommand('formatBlock', '<h3>')">
<h3>H3</h3>
</li>
<li @click="execCommand('formatBlock', '<h4>')">
<h4>H4</h4>
</li>
<li @click="execCommand('formatBlock', '<h5>')">
<h5>H5</h5>
</li>
</ul>
</VmEditorDropdown>
</VmEditorButton>
// 省略其余按钮代码
...
<slot></slot>
</div>
</template>
<style>
...
</style>
<script>
...
export default {
name: 'VmEditorMenu',
components: {
VmEditorButton,
VmEditorDropdown,
VmEditorAddlink,
VmEditorAddimage,
VmEditorFontcolor
},
methods: {
// 封装 document.execCommand 指令
execCommand: function (commandName, valueArgument) {
// let body = document.querySelector('.body');
if (!valueArgument) {
valueArgument = null
}
document.execCommand('styleWithCSS', null, true)
document.execCommand(commandName, false, valueArgument)
},
// 插入图片功能
setImage: function (evt) {
let reader = new FileReader()
let file = evt.target.files[0]
reader.readAsDataURL(file)
reader.onload = function (evt) {
let base64Image = evt.target.result
document.execCommand('insertImage', false, base64Image)
}
}
}
}
</script>复制代码
另外还要实现导出html的功能bash
<div class="vm-editor">
<VmEditorMenu>
<div class="global-control">
// 处处html的按钮,放在这里由于,须要获取 内容区域 的html数据
<VmEditorButton icon="upload" @click.native="uploadHtml"></VmEditorButton>
</div>
</VmEditorMenu>
// 内容区域 只要设置 **contenteditable="true"** 就能够了,其余的交给指令去作
<div class="content" contenteditable="true" v-html="html">
</div>
</div>
<style>
...
</style>
<script>
name: 'VmEditor',
components: {
VmEditorMenu,
VmEditorButton
},
data: function () {
return {
html: 'Please Enter ...'
}
},
methods: {
导出html数据
// 目前 内容区域 的样式都是 **CSS样式**, 导出时须要转化为 **内联样式**
uploadHtml: function () {
// 获取各个模块的 CSS样式
let style = {
ul: `
margin: 10px 20px;
list-style-type: square;
padding: 0;
`,
ol: `
margin: 10px 20px;
list-style-type: decimal;
padding: 0;
`,
li: `
display: list-item;
padding: 0;
`,
hr: `
margin: 15px 0;
border-top: 1px solid #eeeff1;
`,
pre: `
display: block;
margin: 10px 0;
padding: 8px;
border-radius: 4px;
background-color: #f2f2f2;
color: #656565;
font-size: 14px;
`,
blockquote: `
display: block;
border-left: 4px solid #ddd;
margin: 15px 0;
padding: 0 15px;
`,
img: `
margin: 20px 0;
`,
a: `
color: #41b883;
`
}
let html = document.getElementsByClassName('content')[0]
let htmlContainerParent = document.createElement('div')
let htmlContainer = document.createElement('div')
let tagNames = Object.keys(style)
// 遍历html节点并插入对应的内联样式
for (let i = 0; i < tagNames.length; i++) {
let _tagNames = html.getElementsByTagName(tagNames[i])
if (_tagNames.length > 0) {
for (let j = 0; j < _tagNames.length; j++) {
_tagNames[j].style = style[tagNames[i]]
}
}
}
htmlContainer.style = `
text-align: left;
padding: 15px;
font-size: 16px;
`
htmlContainer.innerHTML = html.innerHTML
htmlContainerParent.appendChild(htmlContainer)
// 注册自定义事件 **upload**
this.$emit('upload', htmlContainerParent.innerHTML)
}
}
}
</script>复制代码
其余的组建主要是按钮下拉菜单, 由于每一个都不同,因此要独立出来app
由于这个富文本编辑器的开发时间比较短,没有认真研究相似优秀插件的源码 也没有 深刻调研过富文本编辑器的需求。
只是参考了一些同类编辑器的实现效果和UI风格,好比simditor,而后简单实现了一下功能。编辑器
因此确定还有不少须要改善的地方,比较明显的有:ide
无论怎样,仅供你们学习使用
先这样了, 欢迎star