2019-09-16 16:53:51 +08:00
|
|
|
<template>
|
|
|
|
<div class="neditor">
|
|
|
|
<section class="toolbar"></section>
|
|
|
|
<wc-scroll>
|
|
|
|
<div contenteditable="true" class="editor" spellcheck="false"></div>
|
|
|
|
</wc-scroll>
|
|
|
|
<div class="font-layer">
|
2019-10-31 20:45:24 +08:00
|
|
|
<span data-size="6">6号字体</span>
|
|
|
|
<span data-size="5">5号字体</span>
|
|
|
|
<span data-size="4">4号字体</span>
|
|
|
|
<span data-size="3">3号字体</span>
|
|
|
|
<span data-size="2">2号字体</span>
|
2019-09-16 16:53:51 +08:00
|
|
|
</div>
|
|
|
|
<div class="color-layer">
|
|
|
|
<span data-color="#f3f5fb"></span>
|
|
|
|
<span data-color="#dae1e9"></span>
|
|
|
|
<span data-color="#62778d"></span>
|
|
|
|
<span data-color="#58d68d"></span>
|
|
|
|
<span data-color="#3fc2a7"></span>
|
|
|
|
<span data-color="#52a3de"></span>
|
|
|
|
<span data-color="#ac61ce"></span>
|
|
|
|
<span data-color="#ffb618"></span>
|
|
|
|
<span data-color="#e67e22"></span>
|
|
|
|
<span data-color="#ff5061"></span>
|
|
|
|
<span data-color="#ff0000"></span>
|
|
|
|
<span data-color="#000000"></span>
|
|
|
|
</div>
|
|
|
|
<div class="link-layer">
|
|
|
|
<wc-input label="请输入链接地址"></wc-input>
|
|
|
|
<wc-button color="teal" size="mini">插入</wc-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
:host {
|
|
|
|
display: flex;
|
|
|
|
min-width: 200px;
|
|
|
|
min-height: 100px;
|
2021-05-25 19:33:55 +08:00
|
|
|
max-height: 640px;
|
2021-05-26 11:49:11 +08:00
|
|
|
border-radius: 3px;
|
2021-05-26 19:45:09 +08:00
|
|
|
transition: box-shadow 0.15s linear;
|
2019-09-16 16:53:51 +08:00
|
|
|
}
|
|
|
|
|
2019-09-24 11:47:08 +08:00
|
|
|
table {
|
2021-05-26 11:49:11 +08:00
|
|
|
width: 100%;
|
2019-09-24 11:47:08 +08:00
|
|
|
border-spacing: 0;
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
|
|
|
tr {
|
|
|
|
background: #fff;
|
|
|
|
}
|
|
|
|
thead tr {
|
2020-12-14 14:44:05 +08:00
|
|
|
background: var(--color-plain-1);
|
2019-09-24 11:47:08 +08:00
|
|
|
}
|
|
|
|
th,
|
|
|
|
td {
|
2021-05-26 11:49:11 +08:00
|
|
|
padding: 6px 13px;
|
|
|
|
border: 1px solid var(--color-plain-2);
|
|
|
|
vertical-align: middle;
|
2019-09-24 11:47:08 +08:00
|
|
|
}
|
|
|
|
th {
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
2021-05-26 11:49:11 +08:00
|
|
|
tr:nth-child(2n) {
|
|
|
|
background: #fcfdff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ul,
|
|
|
|
ol {
|
|
|
|
margin-left: 1em;
|
|
|
|
}
|
|
|
|
a {
|
|
|
|
color: var(--color-teal-1);
|
2019-09-24 11:47:08 +08:00
|
|
|
}
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
.neditor {
|
|
|
|
position: relative;
|
|
|
|
flex: 1;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
border: 1px solid #e7e8eb;
|
|
|
|
border-radius: inherit;
|
|
|
|
font-size: 14px;
|
|
|
|
}
|
|
|
|
.toolbar {
|
|
|
|
display: flex;
|
|
|
|
height: 34px;
|
|
|
|
padding: 5px;
|
|
|
|
line-height: 24px;
|
|
|
|
border-bottom: 1px solid #e7e8eb;
|
|
|
|
|
|
|
|
span {
|
|
|
|
position: relative;
|
|
|
|
overflow: hidden;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
width: 24px;
|
|
|
|
height: 24px;
|
|
|
|
margin: 0 3px;
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
|
|
input {
|
|
|
|
position: absolute;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
.icon {
|
|
|
|
overflow: hidden;
|
|
|
|
width: 70%;
|
|
|
|
height: 70%;
|
|
|
|
fill: currentColor;
|
|
|
|
color: #62778d;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
background: #f7f8fb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wc-scroll {
|
2021-05-25 19:33:55 +08:00
|
|
|
overflow: hidden;
|
2019-09-16 16:53:51 +08:00
|
|
|
flex: 1;
|
|
|
|
}
|
|
|
|
.editor {
|
|
|
|
height: 100%;
|
|
|
|
padding: 5px 8px;
|
|
|
|
outline: none;
|
|
|
|
|
|
|
|
img {
|
|
|
|
max-width: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 17:46:14 +08:00
|
|
|
:host([disabled]) {
|
|
|
|
.neditor {
|
|
|
|
cursor: not-allowed;
|
|
|
|
opacity: 0.6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 19:45:09 +08:00
|
|
|
:host([readonly]) {
|
|
|
|
.neditor {
|
|
|
|
cursor: default;
|
|
|
|
opacity: 0.8;
|
|
|
|
}
|
|
|
|
}
|
2021-05-27 13:44:57 +08:00
|
|
|
:host([readonly]),
|
|
|
|
:host([disabled]) {
|
|
|
|
.toolbar {
|
|
|
|
span:hover {
|
|
|
|
background: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-26 19:45:09 +08:00
|
|
|
|
|
|
|
:host(:focus-within) {
|
|
|
|
box-shadow: 0 0 0 2px var(--color-plain-a);
|
|
|
|
}
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
.font-layer,
|
|
|
|
.color-layer,
|
|
|
|
.link-layer {
|
|
|
|
visibility: hidden;
|
|
|
|
position: absolute;
|
|
|
|
left: 0;
|
|
|
|
top: 0;
|
|
|
|
z-index: 99;
|
|
|
|
width: 80px;
|
|
|
|
padding: 5px 0;
|
|
|
|
line-height: 25px;
|
|
|
|
background: #fff;
|
|
|
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
|
|
|
|
font-size: 13px;
|
|
|
|
user-select: none;
|
|
|
|
opacity: 0;
|
|
|
|
transition: all ease-in-out 0.2s;
|
|
|
|
|
|
|
|
&.fadein {
|
|
|
|
visibility: visible;
|
|
|
|
top: 34px;
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.font-layer {
|
|
|
|
span {
|
|
|
|
display: block;
|
|
|
|
padding: 0 8px;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
background: #f7f8fb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.color-layer {
|
|
|
|
display: flex;
|
|
|
|
flex-flow: row wrap;
|
|
|
|
left: 30px;
|
|
|
|
width: 96px;
|
|
|
|
|
|
|
|
span {
|
|
|
|
width: 20px;
|
|
|
|
height: 20px;
|
|
|
|
margin: 2px;
|
|
|
|
|
|
|
|
&:nth-child(1) {
|
|
|
|
background: #f3f5fb;
|
|
|
|
}
|
|
|
|
&:nth-child(2) {
|
|
|
|
background: #dae1e9;
|
|
|
|
}
|
|
|
|
&:nth-child(3) {
|
|
|
|
background: #62778d;
|
|
|
|
}
|
|
|
|
&:nth-child(4) {
|
|
|
|
background: #58d68d;
|
|
|
|
}
|
|
|
|
&:nth-child(5) {
|
|
|
|
background: #3fc2a7;
|
|
|
|
}
|
|
|
|
&:nth-child(6) {
|
|
|
|
background: #52a3de;
|
|
|
|
}
|
|
|
|
&:nth-child(7) {
|
|
|
|
background: #ac61ce;
|
|
|
|
}
|
|
|
|
&:nth-child(8) {
|
|
|
|
background: #ffb618;
|
|
|
|
}
|
|
|
|
&:nth-child(9) {
|
|
|
|
background: #e67e22;
|
|
|
|
}
|
|
|
|
&:nth-child(10) {
|
|
|
|
background: #ff5061;
|
|
|
|
}
|
|
|
|
&:nth-child(11) {
|
|
|
|
background: #ff0000;
|
|
|
|
}
|
|
|
|
&:nth-child(12) {
|
|
|
|
background: #000000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.link-layer {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
left: 330px;
|
|
|
|
width: 230px;
|
|
|
|
padding: 8px;
|
|
|
|
|
|
|
|
wc-button {
|
|
|
|
width: 40px;
|
|
|
|
margin-top: 8px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import ICONS from './svg'
|
|
|
|
import '../form/input'
|
|
|
|
import '../form/button'
|
2019-12-19 01:04:15 +08:00
|
|
|
import $ from '../utils'
|
2019-09-16 16:53:51 +08:00
|
|
|
|
|
|
|
const ACTTION = {
|
|
|
|
bold: 'bold',
|
|
|
|
italic: 'italic',
|
|
|
|
under: 'underline',
|
|
|
|
delete: 'strikeThrough',
|
|
|
|
left: 'justifyLeft',
|
|
|
|
center: 'justifyCenter',
|
|
|
|
right: 'justifyRight',
|
|
|
|
image: 'insertImage',
|
|
|
|
font: 'fontSize',
|
|
|
|
color: 'foreColor',
|
|
|
|
link: 'createLink',
|
|
|
|
ordered: 'insertOrderedList',
|
|
|
|
unordered: 'insertUnorderedList'
|
|
|
|
}
|
|
|
|
|
|
|
|
const DEFAULT_TOOLS = [
|
|
|
|
'font',
|
|
|
|
'color',
|
|
|
|
'bold',
|
|
|
|
'italic',
|
|
|
|
'under',
|
|
|
|
'delete',
|
|
|
|
'ordered',
|
|
|
|
'unordered',
|
|
|
|
'left',
|
|
|
|
'center',
|
|
|
|
'right',
|
|
|
|
'link',
|
2019-11-05 21:15:51 +08:00
|
|
|
'image'
|
2019-09-16 16:53:51 +08:00
|
|
|
]
|
|
|
|
|
|
|
|
function renderToolbar(list) {
|
|
|
|
return (list || DEFAULT_TOOLS)
|
|
|
|
.map(
|
|
|
|
it =>
|
|
|
|
`<span data-act="${it}"><svg class="icon" viewBox="0 0 1024 1024"><path d="${
|
|
|
|
ICONS[it]
|
|
|
|
}"/></svg>${it === 'image' ? '<input type="file">' : ''}</span>`
|
|
|
|
)
|
|
|
|
.join('')
|
|
|
|
}
|
|
|
|
|
2021-05-25 19:33:55 +08:00
|
|
|
export default class Editor {
|
2019-09-16 16:53:51 +08:00
|
|
|
props = {
|
2021-05-25 19:33:55 +08:00
|
|
|
toolbar: '',
|
2020-07-29 17:46:14 +08:00
|
|
|
value: '',
|
|
|
|
readonly: false,
|
|
|
|
disabled: false
|
2019-09-16 16:53:51 +08:00
|
|
|
}
|
|
|
|
|
2021-05-25 19:33:55 +08:00
|
|
|
state = {
|
|
|
|
toolbar: null
|
|
|
|
}
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
__init__() {
|
|
|
|
/* render */
|
|
|
|
|
|
|
|
var ct = this.root.children[1]
|
|
|
|
this.__TOOLBAR__ = ct.children[0]
|
2019-09-16 17:27:16 +08:00
|
|
|
this.__EDITOR__ = ct.children[1].firstElementChild
|
2019-09-16 16:53:51 +08:00
|
|
|
|
|
|
|
this.__FONT__ = ct.children[2]
|
|
|
|
this.__COLOR__ = ct.children[3]
|
|
|
|
this.__LINK__ = ct.children[4]
|
|
|
|
this.__LINK_BTN__ = this.__LINK__.querySelector('wc-button')
|
|
|
|
}
|
|
|
|
|
2021-05-25 19:33:55 +08:00
|
|
|
__stat__(name, val) {
|
2020-07-29 17:46:14 +08:00
|
|
|
var type = typeof val
|
|
|
|
|
2021-05-25 19:33:55 +08:00
|
|
|
if (val === this.props[name]) {
|
2020-07-29 17:46:14 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-05-27 13:44:57 +08:00
|
|
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
2021-05-25 19:33:55 +08:00
|
|
|
this.props[name] = true
|
|
|
|
this.setAttribute(name, '')
|
2020-07-29 17:46:14 +08:00
|
|
|
this.__EDITOR__.removeAttribute('contenteditable')
|
|
|
|
} else {
|
2021-05-25 19:33:55 +08:00
|
|
|
this.props[name] = false
|
|
|
|
this.removeAttribute(name)
|
2020-07-29 17:46:14 +08:00
|
|
|
this.__EDITOR__.setAttribute('contenteditable', true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-25 19:33:55 +08:00
|
|
|
get readOnly() {
|
|
|
|
return this.props.readonly
|
|
|
|
}
|
|
|
|
|
|
|
|
set readOnly(val) {
|
|
|
|
this.__stat__('readonly', val)
|
|
|
|
}
|
|
|
|
|
2020-07-29 17:46:14 +08:00
|
|
|
get disabled() {
|
|
|
|
return this.props.disabled
|
|
|
|
}
|
|
|
|
|
|
|
|
set disabled(val) {
|
2021-05-25 19:33:55 +08:00
|
|
|
this.__stat__('disabled', val)
|
2020-07-29 17:46:14 +08:00
|
|
|
}
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
get value() {
|
2019-09-24 11:47:08 +08:00
|
|
|
var html = this.__EDITOR__.innerHTML
|
|
|
|
if (~html.indexOf('<table>')) {
|
|
|
|
html =
|
|
|
|
'<style>table{border-spacing:0;border-collapse:collapse;}table tr{background:#fff;}table thead tr{background:#f3f5fb;}table th,table td{padding:6px 12px;border:1px solid #dae1e9;}table th{font-weight: bold;}</style>' +
|
|
|
|
html
|
|
|
|
}
|
|
|
|
return html
|
2019-09-16 16:53:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
set value(val) {
|
2019-09-16 17:27:16 +08:00
|
|
|
this.__EDITOR__.innerHTML = val
|
2019-09-16 16:53:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateToolbar() {
|
2021-05-25 19:33:55 +08:00
|
|
|
var { toolbar } = this.state
|
|
|
|
|
|
|
|
this.__TOOLBAR__.innerHTML = renderToolbar(toolbar)
|
2019-09-16 16:53:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 保存选中
|
|
|
|
saveSelection() {
|
|
|
|
var gs = this.root.getSelection()
|
|
|
|
if (gs.getRangeAt && gs.rangeCount) {
|
|
|
|
this.__SELECT__ = gs.getRangeAt(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 清除选中并重置选中
|
|
|
|
restoreSelection() {
|
|
|
|
var gs = this.root.getSelection()
|
|
|
|
if (this.__SELECT__) {
|
|
|
|
try {
|
|
|
|
gs.removeAllRanges()
|
|
|
|
} catch (err) {}
|
|
|
|
gs.addRange(this.__SELECT__)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 执行命令
|
|
|
|
exec(cmd, val = '') {
|
|
|
|
document.execCommand(cmd, false, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理图片
|
|
|
|
_handleImage(file) {
|
|
|
|
this.dispatchEvent(
|
|
|
|
new CustomEvent('upload', {
|
|
|
|
detail: {
|
|
|
|
file,
|
|
|
|
send: link => {
|
|
|
|
this.__EDITOR__.focus()
|
|
|
|
this.restoreSelection()
|
|
|
|
this.exec(ACTTION.image, link)
|
|
|
|
this.saveSelection()
|
|
|
|
// 修正插入的图片,宽度不得超出容器
|
|
|
|
this.__EDITOR__.querySelectorAll('img').forEach(_ => {
|
|
|
|
_.style.maxWidth = '100%'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
this._updateToolbar()
|
|
|
|
this.exec('styleWithCSS', true)
|
|
|
|
|
|
|
|
const LINK_INPUT = this.__LINK__.querySelector('wc-input')
|
|
|
|
const FILE_INPUT = this.__TOOLBAR__.querySelector('input')
|
|
|
|
|
|
|
|
if (FILE_INPUT) {
|
2019-12-19 01:04:15 +08:00
|
|
|
$.bind(FILE_INPUT, 'change', ev => {
|
2019-09-16 16:53:51 +08:00
|
|
|
this._handleImage(FILE_INPUT.files[0])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------ */
|
|
|
|
|
|
|
|
// 工具栏点击事件
|
2020-07-29 17:46:14 +08:00
|
|
|
this._toolFn = $.catch(this.__TOOLBAR__, 'click', ev => {
|
|
|
|
var target = ev.target
|
|
|
|
var act, val
|
|
|
|
|
2019-09-16 17:27:16 +08:00
|
|
|
this.restoreSelection()
|
2020-07-29 17:46:14 +08:00
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
if (ev.target === ev.currentTarget) {
|
|
|
|
return
|
|
|
|
}
|
2020-07-29 17:46:14 +08:00
|
|
|
|
|
|
|
if (this.props.readonly || this.props.disabled) {
|
2021-05-26 11:49:11 +08:00
|
|
|
return
|
2020-07-29 17:46:14 +08:00
|
|
|
}
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
while (target.tagName !== 'SPAN') {
|
|
|
|
target = target.parentNode
|
|
|
|
}
|
2020-07-29 17:46:14 +08:00
|
|
|
|
|
|
|
act = target.dataset.act
|
|
|
|
val = ''
|
2019-09-16 16:53:51 +08:00
|
|
|
|
|
|
|
switch (act) {
|
|
|
|
case 'font':
|
|
|
|
this.__COLOR__.classList.remove('fadein')
|
|
|
|
this.__LINK__.classList.remove('fadein')
|
|
|
|
|
|
|
|
if (this.__FONT__.classList.contains('fadein')) {
|
|
|
|
this.__FONT__.classList.remove('fadein')
|
|
|
|
} else {
|
|
|
|
this.__FONT__.classList.add('fadein')
|
|
|
|
}
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'color':
|
|
|
|
this.__LINK__.classList.remove('fadein')
|
|
|
|
this.__FONT__.classList.remove('fadein')
|
|
|
|
if (this.__COLOR__.classList.contains('fadein')) {
|
|
|
|
this.__COLOR__.classList.remove('fadein')
|
|
|
|
} else {
|
|
|
|
this.__COLOR__.classList.add('fadein')
|
|
|
|
}
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'link':
|
|
|
|
this.__COLOR__.classList.remove('fadein')
|
|
|
|
this.__FONT__.classList.remove('fadein')
|
|
|
|
if (this.__LINK__.classList.contains('fadein')) {
|
|
|
|
this.__LINK__.classList.remove('fadein')
|
|
|
|
} else {
|
|
|
|
this.__LINK__.classList.add('fadein')
|
|
|
|
}
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'image':
|
|
|
|
// 这里不作任何处理
|
|
|
|
break
|
|
|
|
|
|
|
|
default:
|
|
|
|
this.__EDITOR__.focus()
|
|
|
|
this.restoreSelection()
|
|
|
|
this.exec(ACTTION[act])
|
|
|
|
this.saveSelection()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// 字体大小设置
|
2019-12-19 01:04:15 +08:00
|
|
|
this._fontFn = $.bind(this.__FONT__, 'click', ev => {
|
2019-09-16 16:53:51 +08:00
|
|
|
if (ev.target === ev.currentTarget) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this.__FONT__.classList.remove('fadein')
|
|
|
|
this.__EDITOR__.focus()
|
|
|
|
this.restoreSelection()
|
|
|
|
this.exec(ACTTION.font, ev.target.dataset.size)
|
|
|
|
this.saveSelection()
|
|
|
|
})
|
|
|
|
|
|
|
|
// 颜色
|
2019-12-19 01:04:15 +08:00
|
|
|
this._colorFn = $.bind(this.__COLOR__, 'click', ev => {
|
2019-09-16 16:53:51 +08:00
|
|
|
if (ev.target === ev.currentTarget) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this.__COLOR__.classList.remove('fadein')
|
|
|
|
this.__EDITOR__.focus()
|
|
|
|
this.restoreSelection()
|
|
|
|
this.exec(ACTTION.color, ev.target.dataset.color)
|
|
|
|
this.saveSelection()
|
|
|
|
})
|
|
|
|
|
|
|
|
// 超链接
|
2021-05-26 11:49:11 +08:00
|
|
|
this.__linkFn = $.bind(this.__LINK_BTN__, 'click', ev => {
|
2019-09-16 16:53:51 +08:00
|
|
|
if (LINK_INPUT.value) {
|
|
|
|
this.__LINK__.classList.remove('fadein')
|
|
|
|
this.__EDITOR__.focus()
|
|
|
|
this.restoreSelection()
|
|
|
|
this.exec(ACTTION.link, LINK_INPUT.value)
|
|
|
|
this.saveSelection()
|
|
|
|
LINK_INPUT.value = ''
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
//监听鼠标事件的,以缓存选中状态
|
2019-12-19 01:04:15 +08:00
|
|
|
this.__mouseFn = $.bind(this.__EDITOR__, 'mouseleave', ev => {
|
2019-09-16 16:53:51 +08:00
|
|
|
this.saveSelection()
|
|
|
|
})
|
|
|
|
|
2019-12-19 01:04:15 +08:00
|
|
|
$.outside(this, ev => {
|
2019-09-16 17:27:16 +08:00
|
|
|
this.__FONT__.classList.remove('fadein')
|
|
|
|
this.__COLOR__.classList.remove('fadein')
|
|
|
|
this.__LINK__.classList.remove('fadein')
|
|
|
|
})
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
// 粘贴板事件
|
2019-12-19 01:04:15 +08:00
|
|
|
this.__pasteFn = $.bind(this.__EDITOR__, 'paste', ev => {
|
2019-09-16 16:53:51 +08:00
|
|
|
ev.preventDefault()
|
|
|
|
|
2019-09-24 11:47:08 +08:00
|
|
|
var html = ev.clipboardData.getData('text/html')
|
2019-09-16 16:53:51 +08:00
|
|
|
var txt = ev.clipboardData.getData('text/plain')
|
|
|
|
var items = ev.clipboardData.items
|
|
|
|
|
2019-09-24 11:47:08 +08:00
|
|
|
if (html) {
|
|
|
|
html = html
|
|
|
|
.replace(/\t/g, ' ')
|
2019-11-01 20:01:41 +08:00
|
|
|
.replace(/<\/?(meta|link|script)[^>]*?>/g, '')
|
|
|
|
.replace(/<!--[\w\W]*?-->/g, '')
|
2019-09-24 11:47:08 +08:00
|
|
|
.replace(
|
|
|
|
/<a[^>]*? href\s?=\s?["']?([^"']*)["']?[^>]*?>/g,
|
|
|
|
'<a href="$1">'
|
|
|
|
)
|
|
|
|
.replace(
|
2019-12-12 12:10:15 +08:00
|
|
|
/<img[^>]*? src\s?=\s?["']?([^"']*)["']?[^>]*?>/g,
|
2019-09-24 11:47:08 +08:00
|
|
|
'<img src="$1">'
|
|
|
|
)
|
|
|
|
.replace(/<(?!a|img)([\w\-]+)[^>]*>/g, '<$1>')
|
2020-07-29 17:46:14 +08:00
|
|
|
.replace(/<xml[^>]*?>[\w\W]*?<\/xml>/g, '')
|
|
|
|
.replace(/<style>[\w\W]*?<\/style>/g, '')
|
2019-09-24 11:47:08 +08:00
|
|
|
|
|
|
|
return this.exec('insertHtml', html)
|
|
|
|
}
|
|
|
|
|
2019-09-16 16:53:51 +08:00
|
|
|
if (txt) {
|
|
|
|
return this.exec('insertText', txt)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (items && items.length) {
|
|
|
|
let blob = null
|
|
|
|
for (let it of items) {
|
|
|
|
if (it.type.indexOf('image') > -1) {
|
|
|
|
blob = it.getAsFile()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this._handleImage(blob)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
this.__observer = new MutationObserver(_ => {
|
2021-05-25 19:33:55 +08:00
|
|
|
this.dispatchEvent(new CustomEvent('input'))
|
2019-09-16 16:53:51 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
this.__observer.observe(this.__EDITOR__, {
|
|
|
|
childList: true,
|
|
|
|
subtree: true,
|
|
|
|
characterData: true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-05-07 16:06:33 +08:00
|
|
|
unmounted() {
|
2019-12-19 01:04:15 +08:00
|
|
|
$.unbind(this.__TOOLBAR__, 'click', this.__toolFn)
|
|
|
|
$.unbind(this.__FONT__, 'click', this.__fontFn)
|
|
|
|
$.unbind(this.__COLOR__, 'click', this.__colorFn)
|
|
|
|
$.unbind(this.__LINK_BTN__, 'click', this.__linkFn)
|
|
|
|
$.unbind(this.__EDITOR__, 'mouseleave', this.__mouseFn)
|
|
|
|
$.unbind(this.__EDITOR__, 'paste', this.__pasteFn)
|
2019-09-16 16:53:51 +08:00
|
|
|
this.__observer.disconnect()
|
|
|
|
}
|
|
|
|
|
|
|
|
watch() {
|
|
|
|
switch (name) {
|
|
|
|
case 'toolbar':
|
2021-05-25 19:33:55 +08:00
|
|
|
if (val === null) {
|
|
|
|
this.state.toolbar = [...DEFAULT_TOOLS]
|
|
|
|
} else if (val) {
|
2019-09-16 16:53:51 +08:00
|
|
|
val = val.split(',').map(it => it.trim())
|
2021-05-25 19:33:55 +08:00
|
|
|
this.state.toolbar = val
|
2019-09-16 16:53:51 +08:00
|
|
|
}
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'value':
|
|
|
|
this.value = val
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'readonly':
|
|
|
|
case 'disabled':
|
2020-07-29 17:46:14 +08:00
|
|
|
var k = name
|
|
|
|
if (k === 'readonly') {
|
|
|
|
k = 'readOnly'
|
|
|
|
}
|
2021-05-27 13:44:57 +08:00
|
|
|
console.log(k, val)
|
2021-05-25 19:33:55 +08:00
|
|
|
this[k] = val !== null
|
2019-09-16 16:53:51 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|