這個原本是給 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