优化layer组件tips的定位;重构meditor的插件机制
parent
d256f145cf
commit
fead12b0d2
|
@ -22,6 +22,7 @@ let defconf = {
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
mask: true, // 遮罩
|
mask: true, // 遮罩
|
||||||
maskClose: false, // 遮罩点击关闭弹窗
|
maskClose: false, // 遮罩点击关闭弹窗
|
||||||
|
maskColor: null, // 遮罩背景色
|
||||||
radius: '0px', // 弹窗圆角半径
|
radius: '0px', // 弹窗圆角半径
|
||||||
area: ['auto', 'auto'],
|
area: ['auto', 'auto'],
|
||||||
title: '提示', // 弹窗主标题(在工具栏上的)
|
title: '提示', // 弹窗主标题(在工具栏上的)
|
||||||
|
@ -64,6 +65,7 @@ const close = function(id) {
|
||||||
}, 200)
|
}, 200)
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
|
document.body.style.overflow = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const repeat = function(str, num) {
|
const repeat = function(str, num) {
|
||||||
|
@ -208,6 +210,9 @@ class __layer__ {
|
||||||
if (state.mask) {
|
if (state.mask) {
|
||||||
outerBox.classList.add('mask')
|
outerBox.classList.add('mask')
|
||||||
}
|
}
|
||||||
|
if (state.maskColor) {
|
||||||
|
outerBox.style.background = state.maskColor
|
||||||
|
}
|
||||||
|
|
||||||
layBox.classList.add('layer-box')
|
layBox.classList.add('layer-box')
|
||||||
layBox.classList.add('skin-' + state.skin)
|
layBox.classList.add('skin-' + state.skin)
|
||||||
|
@ -373,40 +378,49 @@ class __layer__ {
|
||||||
style.color = state.color
|
style.color = state.color
|
||||||
style.opacity = 1
|
style.opacity = 1
|
||||||
let $container = Anot(container)
|
let $container = Anot(container)
|
||||||
|
let $doc = Anot(document)
|
||||||
let $arrow = $container[0].querySelector('.arrow')
|
let $arrow = $container[0].querySelector('.arrow')
|
||||||
let cw = $container.innerWidth()
|
let cw = $container.innerWidth()
|
||||||
let ch = $container.innerHeight()
|
let ch = $container.innerHeight()
|
||||||
let ol = $container.offset().left - document.body.scrollLeft
|
let ol = $container.offset().left - $doc.scrollLeft()
|
||||||
let ot = $container.offset().top - document.body.scrollTop
|
let ot = $container.offset().top - $doc.scrollTop()
|
||||||
|
|
||||||
let layw = parseInt(css.width)
|
let layw = parseInt(css.width)
|
||||||
let layh = parseInt(css.height)
|
let layh = parseInt(css.height)
|
||||||
|
|
||||||
let arrowOffset = ['top']
|
let arrowOffset = ['top']
|
||||||
|
|
||||||
if (ot + 18 < layh) {
|
|
||||||
arrowOffset[0] = 'bottom'
|
|
||||||
$arrow.style.borderBottomColor = state.background
|
|
||||||
style.top = ot + ch + 8
|
|
||||||
} else {
|
|
||||||
$arrow.style.borderTopColor = state.background
|
|
||||||
style.top = ot - layh - 8
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ol + cw * 0.7 + layw > window.innerWidth) {
|
|
||||||
style.left = ol + cw * 0.3 - layw
|
|
||||||
arrowOffset[1] = 'left'
|
|
||||||
} else {
|
|
||||||
style.left = ol + cw * 0.7
|
|
||||||
}
|
|
||||||
|
|
||||||
$arrow.classList.add('offset-' + arrowOffset.join('-'))
|
|
||||||
Anot(layerDom[$id][1]).css(style)
|
Anot(layerDom[$id][1]).css(style)
|
||||||
|
|
||||||
$container.bind('mouseenter', ev => {
|
$container.bind('mouseenter', ev => {
|
||||||
layerDom[$id][1].style.visibility = 'visible'
|
let tmpStyle = { visibility: 'visible' }
|
||||||
|
ol = $container.offset().left - $doc.scrollLeft()
|
||||||
|
ot = $container.offset().top - $doc.scrollTop()
|
||||||
|
|
||||||
|
if (ot + 18 < layh) {
|
||||||
|
arrowOffset[0] = 'bottom'
|
||||||
|
$arrow.style.borderBottomColor = state.background
|
||||||
|
tmpStyle.top = ot + ch + 8
|
||||||
|
} else {
|
||||||
|
$arrow.style.borderTopColor = state.background
|
||||||
|
tmpStyle.top = ot - layh - 8
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ol + cw * 0.7 + layw > window.innerWidth) {
|
||||||
|
tmpStyle.left = ol + cw * 0.3 - layw
|
||||||
|
arrowOffset[1] = 'left'
|
||||||
|
} else {
|
||||||
|
tmpStyle.left = ol + cw * 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
$arrow.classList.add('offset-' + arrowOffset.join('-'))
|
||||||
|
Anot(layerDom[$id][1]).css(tmpStyle)
|
||||||
})
|
})
|
||||||
$container.bind('mouseleave', () => {
|
$container.bind('mouseleave', () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
$arrow.classList.remove('offset-' + arrowOffset.join('-'))
|
||||||
|
arrowOffset = ['top']
|
||||||
|
$arrow.style.borderBottomColor = ''
|
||||||
|
$arrow.style.borderTopColor = ''
|
||||||
layerDom[$id][1].style.visibility = 'hidden'
|
layerDom[$id][1].style.visibility = 'hidden'
|
||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
|
@ -436,6 +450,7 @@ class __layer__ {
|
||||||
Anot(layerDom[$id][1]).css(style)
|
Anot(layerDom[$id][1]).css(style)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
document.body.style.overflow = 'hidden'
|
||||||
layerDom[$id][1].classList.add('shift')
|
layerDom[$id][1].classList.add('shift')
|
||||||
setTimeout(_ => {
|
setTimeout(_ => {
|
||||||
Anot(layerDom[$id][1]).css(offsetStyle)
|
Anot(layerDom[$id][1]).css(offsetStyle)
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
|
|
||||||
|
|
||||||
/* tips类弹层(type 5) */
|
/* tips类弹层(type 5) */
|
||||||
&.type-5 {visibility:hidden;z-index:65534;min-width:75px;max-width:600px;line-height:1.5;color:#fff;background:rgba(0,0,0,.5);opacity:0;box-shadow:none;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;
|
&.type-5 {visibility:hidden;position:fixed;z-index:65534;min-width:75px;max-width:600px;line-height:1.5;color:#fff;background:rgba(0,0,0,.5);opacity:0;box-shadow:none;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;
|
||||||
|
|
||||||
// &.active {visibility:visible;opacity:1;}
|
// &.active {visibility:visible;opacity:1;}
|
||||||
i.arrow {position:absolute;width:0;height:0;border:6px solid transparent;content: ""}
|
i.arrow {position:absolute;width:0;height:0;border:6px solid transparent;content: ""}
|
||||||
|
@ -190,7 +190,6 @@
|
||||||
|
|
||||||
&.shift {transition: all .5s ease-out;}
|
&.shift {transition: all .5s ease-out;}
|
||||||
|
|
||||||
.layer-box {position:absolute;}
|
|
||||||
}
|
}
|
||||||
&:active {z-index:65536;}
|
&:active {z-index:65536;}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,432 +7,419 @@
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
define(['lib/layer/base', 'css!./attach'], function() {
|
import 'layer/index'
|
||||||
var Uploader = function(url) {
|
import './attach.scss'
|
||||||
this.url = url || ''
|
|
||||||
this.init()
|
const $doc = Anot(document)
|
||||||
|
|
||||||
|
class Uploader {
|
||||||
|
constructor(url) {
|
||||||
|
this.url = url
|
||||||
|
this.xhr = new XMLHttpRequest()
|
||||||
|
this.form = new FormData()
|
||||||
}
|
}
|
||||||
|
|
||||||
Uploader.prototype = {
|
field(key, val) {
|
||||||
init: function() {
|
this.form.append(key, val)
|
||||||
this.xhr = new XMLHttpRequest()
|
return this
|
||||||
this.form = new FormData()
|
}
|
||||||
return this
|
onProgress(fn) {
|
||||||
},
|
this.progress = fn
|
||||||
field: function(key, val) {
|
return this
|
||||||
if (typeof key === 'object') {
|
}
|
||||||
for (var i in key) {
|
then(cb) {
|
||||||
this.form.append(i, key[i])
|
if (!this.url) {
|
||||||
}
|
Anot.error('invalid upload url')
|
||||||
} else {
|
|
||||||
this.form.append(key, val)
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
start: function() {
|
|
||||||
var _this = this
|
|
||||||
|
|
||||||
this.xhr.open('POST', this.url, true)
|
|
||||||
|
|
||||||
var startTime = Date.now()
|
|
||||||
|
|
||||||
this.xhr.upload.addEventListener(
|
|
||||||
'progress',
|
|
||||||
function(evt) {
|
|
||||||
if (evt.lengthComputable && _this.progress) {
|
|
||||||
var res = Math.round(evt.loaded * 100 / evt.total)
|
|
||||||
_this.progress(res)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
this.xhr.onreadystatechange = function() {
|
|
||||||
if (_this.xhr.readyState === 4) {
|
|
||||||
if (_this.xhr.status >= 200 && _this.xhr.status < 205) {
|
|
||||||
var res = _this.xhr.responseText
|
|
||||||
try {
|
|
||||||
res = JSON.parse(res)
|
|
||||||
} catch (err) {}
|
|
||||||
_this.end && _this.end(res)
|
|
||||||
} else {
|
|
||||||
console.error(_this.xhr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.xhr.send(this.form)
|
|
||||||
},
|
|
||||||
onProgress: function(fn) {
|
|
||||||
this.progress = fn
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
onEnd: function(fn) {
|
|
||||||
this.end = fn
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
}
|
let defer = Promise.defer()
|
||||||
|
|
||||||
function uploadScreenshot(vm, blob) {
|
this.xhr.open('POST', this.url, true)
|
||||||
var upload = new Uploader(vm.uploadUrl || ME.uploadUrl)
|
this.xhr.upload.addEventListener(
|
||||||
if (ME.beforeUpload) {
|
'progress',
|
||||||
ME.beforeUpload(Date.now().toString(16) + '.jpg', function(qn) {
|
evt => {
|
||||||
upload
|
if (evt.lengthComputable && this.progress) {
|
||||||
.field('file', blob)
|
let res = Math.round(evt.loaded * 100 / evt.total)
|
||||||
.field('token', qn.token)
|
this.progress(res)
|
||||||
.field('key', qn.key)
|
}
|
||||||
.onEnd(function(json) {
|
},
|
||||||
ME.insert(vm.$editor, '![截图](' + qn.url + ')')
|
false
|
||||||
})
|
)
|
||||||
.start()
|
|
||||||
|
this.xhr.onreadystatechange = () => {
|
||||||
|
if (this.xhr.readyState === 4) {
|
||||||
|
if (this.xhr.status >= 200 && this.xhr.status < 205) {
|
||||||
|
let res = this.xhr.responseText
|
||||||
|
try {
|
||||||
|
res = JSON.parse(res)
|
||||||
|
} catch (err) {}
|
||||||
|
defer.resolve(cb(res))
|
||||||
|
} else {
|
||||||
|
defer.reject(this.xhr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.xhr.send(this.form)
|
||||||
|
return defer.promise
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var $init = function(vm) {
|
||||||
|
vm.$editor.addEventListener('paste', function(ev) {
|
||||||
|
var txt = ev.clipboardData.getData('text/plain').trim(),
|
||||||
|
html = ev.clipboardData.getData('text/html').trim()
|
||||||
|
|
||||||
|
//文本类型直接默认处理
|
||||||
|
if (txt || html) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.clipboardData.items) {
|
||||||
|
var items = ev.clipboardData.items,
|
||||||
|
len = items.length,
|
||||||
|
blob = null
|
||||||
|
for (var i = 0, it; (it = items[i++]); ) {
|
||||||
|
if (it.type.indexOf('image') > -1) {
|
||||||
|
blob = it.getAsFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blob !== null) {
|
||||||
|
layer.msg('截图处理中...')
|
||||||
|
// 压缩截图,避免文件过大
|
||||||
|
var reader = new FileReader()
|
||||||
|
reader.onload = function() {
|
||||||
|
var img = document.createElement('img'),
|
||||||
|
canvas = document.createElement('canvas')
|
||||||
|
|
||||||
|
img.onload = function() {
|
||||||
|
canvas.width = img.width
|
||||||
|
canvas.height = img.height
|
||||||
|
|
||||||
|
var ctx = canvas.getContext('2d')
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||||
|
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
if (canvas.toBlob) {
|
||||||
|
canvas.toBlob(
|
||||||
|
function(obj) {
|
||||||
|
uploadScreenshot(vm, obj)
|
||||||
|
},
|
||||||
|
'image/jpeg',
|
||||||
|
0.8
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
var base64 = canvas.toDataURL('image/jpeg', 0.8),
|
||||||
|
buf = atob(base64.split(',')[1]),
|
||||||
|
arrBuf = new ArrayBuffer(buf.length),
|
||||||
|
intArr = new Uint8Array(arrBuf),
|
||||||
|
obj = null
|
||||||
|
|
||||||
|
for (var i = 0; i < buf.length; i++) {
|
||||||
|
intArr[i] = buf.charCodeAt(i)
|
||||||
|
}
|
||||||
|
obj = new Blob([intArr], { type: 'image/jpeg' })
|
||||||
|
uploadScreenshot(vm, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.src = this.result
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(blob)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev.preventDefault()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let cache = {
|
||||||
|
//缓存附件列表
|
||||||
|
image: [],
|
||||||
|
file: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const LANG = {
|
||||||
|
image: ['远程图片', '图片管理', '图片描述', '图片地址'],
|
||||||
|
file: ['远程附件', '附件管理', '附件描述', '附件地址']
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixCont = function(vm, tool) {
|
||||||
|
let limit = false
|
||||||
|
if (vm.props.uploadSizeLimit) {
|
||||||
|
limit = (vm.props.uploadSizeLimit / (1024 * 1024)).toFixed(2)
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<dl class="do-meditor-attach do-meditor__font">
|
||||||
|
<dt :click="close" class="do-icon-close close"></dt>
|
||||||
|
<dt class="tab-box" :drag="do-layer" data-limit="window">
|
||||||
|
<span class="item" :class="active:tab === 1" :click="switchTab(1)">
|
||||||
|
${LANG[tool][0]}
|
||||||
|
</span>
|
||||||
|
<span class="item" :class="active:tab === 2" :click="switchTab(2)">本地上传</span>
|
||||||
|
<span class="item" :class="active:tab === 3" :click="switchTab(3)">
|
||||||
|
${LANG[tool][1]}
|
||||||
|
</span>
|
||||||
|
</dt>
|
||||||
|
<dd class="cont-box">
|
||||||
|
<div class="remote" :visible="tab === 1">
|
||||||
|
<section class="section do-fn-cl">
|
||||||
|
<input
|
||||||
|
class="txt"
|
||||||
|
:duplex="attachAlt"
|
||||||
|
placeholder="${LANG[tool][2]}" />
|
||||||
|
</section>
|
||||||
|
<section class="section do-fn-cl">
|
||||||
|
<input
|
||||||
|
class="txt"
|
||||||
|
:duplex="attach"
|
||||||
|
placeholder="${LANG[tool][3]}" />
|
||||||
|
</section>
|
||||||
|
<section class="section do-fn-cl">
|
||||||
|
<a
|
||||||
|
href="javascript:;"
|
||||||
|
class="do-meditor__button submit"
|
||||||
|
:click="$confirm">确定</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="local" :visible="tab === 2">
|
||||||
|
<div class="select-file">
|
||||||
|
<input ref="attach" multiple :change="change" type="file" class="hide" />
|
||||||
|
<span class="file" :click="select">选择文件</span>
|
||||||
|
${
|
||||||
|
limit
|
||||||
|
? `<span class="tips">(上传大小限制:单文件最大${limit} MB)</span>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<ul class="upload-box">
|
||||||
|
<li class="thead">
|
||||||
|
<span class="col">文件名</span>
|
||||||
|
<span class="col">上传进度</span>
|
||||||
|
<span class="col">操作</span>
|
||||||
|
</li>
|
||||||
|
<li class="tbody">
|
||||||
|
<p :repeat="uploadQueue">
|
||||||
|
<span
|
||||||
|
class="col do-fn-ell"
|
||||||
|
:text="el.name"
|
||||||
|
:layer-tips="el.name"></span>
|
||||||
|
<span class="col" :html="el.progress"></span>
|
||||||
|
<span class="col"><a class="insert" :click="insert(el)">插入</a></span>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<ul class="manager" :visible="tab === 3">
|
||||||
|
<li class="item" :repeat="attachList" :click="$insert(el)">
|
||||||
|
<span class="thumb" :html="el.thumb"></span>
|
||||||
|
<p class="name" :attr-title="el.name" :text="el.name"></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
</dl>`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [uploadFile 文件上传]
|
||||||
|
* @param {[type]} vm [vm对象]
|
||||||
|
* @param {[type]} tool [image/file]
|
||||||
|
*/
|
||||||
|
function uploadFile(vm, tool) {
|
||||||
|
for (let it of this.files) {
|
||||||
|
let ext = it.name.slice(it.name.lastIndexOf('.'))
|
||||||
|
if (tool === 'image' && !/^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext)) {
|
||||||
|
this.uploadQueue.push({
|
||||||
|
name: it.name,
|
||||||
|
progress: '<span class="red">0%(文件类型错误)</span>',
|
||||||
|
url: ''
|
||||||
})
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (vm.props.uploadSizeLimit && it.size > vm.props.uploadSizeLimit) {
|
||||||
|
this.uploadQueue.push({
|
||||||
|
name: it.name,
|
||||||
|
progress: '<span class="red">0%(文件体积过大)</span>',
|
||||||
|
url: ''
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let idx = this.uploadQueue.length
|
||||||
|
let fixName = new Date().format('YmdHis') + ext
|
||||||
|
let attach = { name: it.name, fixName, progress: '0%', url: '' }
|
||||||
|
let upload = new Uploader(vm.props.uploadUrl).field('file', it)
|
||||||
|
|
||||||
|
this.uploadQueue.push(attach)
|
||||||
|
|
||||||
|
if (vm.props.beforeUpload) {
|
||||||
|
vm.props
|
||||||
|
.beforeUpload(attach, upload)
|
||||||
|
.then(next => {
|
||||||
|
if (!next) {
|
||||||
|
return Promise.reject('something wrong with beforeUpload')
|
||||||
|
}
|
||||||
|
return upload
|
||||||
|
.onProgress(val => {
|
||||||
|
this.uploadQueue[idx].progress = val + '%'
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
return res.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
this.uploadQueue[idx].url = data.url
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Anot.error(err)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
upload
|
upload
|
||||||
.field('file', blob)
|
.onProgress(val => {
|
||||||
.onEnd(function(json) {
|
this.uploadQueue[idx].progress = val + '%'
|
||||||
ME.insert(vm.$editor, '![截图](' + json.data.url + ')')
|
})
|
||||||
|
.then(res => {
|
||||||
|
return res.data
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
this.uploadQueue[idx].url = data.url
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Anot.error(err)
|
||||||
})
|
})
|
||||||
.start()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var $init = function(vm) {
|
function uploadScreenshot(vm, blob) {
|
||||||
if (!vm.uploadUrl && !ME.uploadUrl) {
|
let name = new Date().format('YmdHis') + '.jpg'
|
||||||
console.error(
|
let attach = { name, url: '' }
|
||||||
'使用附件上传,必须先设置uploadUrl;\n可以给vm增加uploadUrl属性,也可以通过ME.uploadUrl设置'
|
let upload = new Uploader(vm.props.uploadUrl).field('file', blob)
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!vm.manageUrl && !ME.manageUrl) {
|
|
||||||
console.error(
|
|
||||||
'使用附件管理功能,必须先设置manageUrl;\n可以给vm增加manageUrl属性,也可以通过ME.manageUrl设置'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!ME.maxSize) {
|
|
||||||
ME.maxSize = 4194304
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.$editor.addEventListener('paste', function(ev) {
|
if (vm.props.beforeUpload) {
|
||||||
var txt = ev.clipboardData.getData('text/plain').trim(),
|
vm.props
|
||||||
html = ev.clipboardData.getData('text/html').trim()
|
.beforeUpload(attach, upload)
|
||||||
|
.then(upload => {
|
||||||
//文本类型直接默认处理
|
return upload.then(res => {
|
||||||
if (txt || html) {
|
return res.data
|
||||||
return
|
})
|
||||||
}
|
})
|
||||||
|
.then(data => {
|
||||||
if (ev.clipboardData.items) {
|
vm.insert(`![截图](${data.url})`)
|
||||||
var items = ev.clipboardData.items,
|
})
|
||||||
len = items.length,
|
} else {
|
||||||
blob = null
|
upload
|
||||||
for (var i = 0, it; (it = items[i++]); ) {
|
.then(res => {
|
||||||
if (it.type.indexOf('image') > -1) {
|
return res.data
|
||||||
blob = it.getAsFile()
|
})
|
||||||
}
|
.then(data => {
|
||||||
}
|
vm.insert(`![截图](${data.url})`)
|
||||||
|
|
||||||
if (blob !== null) {
|
|
||||||
layer.msg('截图处理中...')
|
|
||||||
// 压缩截图,避免文件过大
|
|
||||||
var reader = new FileReader()
|
|
||||||
reader.onload = function() {
|
|
||||||
var img = document.createElement('img'),
|
|
||||||
canvas = document.createElement('canvas')
|
|
||||||
|
|
||||||
img.onload = function() {
|
|
||||||
canvas.width = img.width
|
|
||||||
canvas.height = img.height
|
|
||||||
|
|
||||||
var ctx = canvas.getContext('2d')
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
||||||
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
|
|
||||||
|
|
||||||
if (canvas.toBlob) {
|
|
||||||
canvas.toBlob(
|
|
||||||
function(obj) {
|
|
||||||
uploadScreenshot(vm, obj)
|
|
||||||
},
|
|
||||||
'image/jpeg',
|
|
||||||
0.8
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
var base64 = canvas.toDataURL('image/jpeg', 0.8),
|
|
||||||
buf = atob(base64.split(',')[1]),
|
|
||||||
arrBuf = new ArrayBuffer(buf.length),
|
|
||||||
intArr = new Uint8Array(arrBuf),
|
|
||||||
obj = null
|
|
||||||
|
|
||||||
for (var i = 0; i < buf.length; i++) {
|
|
||||||
intArr[i] = buf.charCodeAt(i)
|
|
||||||
}
|
|
||||||
obj = new Blob([intArr], { type: 'image/jpeg' })
|
|
||||||
uploadScreenshot(vm, obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img.src = this.result
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(blob)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ev.preventDefault()
|
|
||||||
})
|
})
|
||||||
},
|
|
||||||
lang = {
|
|
||||||
image: ['远程图片', '图片管理', '图片描述', '图片地址'],
|
|
||||||
file: ['远程附件', '附件管理', '附件描述', '附件地址']
|
|
||||||
},
|
|
||||||
opened = false, //记录是否已经打开
|
|
||||||
openType = 'image', //打开类型, 图片/附件
|
|
||||||
cache = {
|
|
||||||
//缓存附件列表
|
|
||||||
image: [],
|
|
||||||
file: []
|
|
||||||
},
|
|
||||||
fixCont = function() {
|
|
||||||
return (
|
|
||||||
'<dl class="do-meditor-attach do-meditor-font">' +
|
|
||||||
'<dt class="tab-box" :drag="do-layer" data-limit="window">' +
|
|
||||||
'<span class="item" :class="active:tab === 1" :click="$switch(1)">' +
|
|
||||||
lang[openType][0] +
|
|
||||||
'</span>' +
|
|
||||||
'<span class="item" :class="active:tab === 2" :click="$switch(2)">本地上传</span>' +
|
|
||||||
'<span class="item" :class="active:tab === 3" :click="$switch(3)">' +
|
|
||||||
lang[openType][1] +
|
|
||||||
'</span>' +
|
|
||||||
'<a href="javascript:;" :click="no" class="action-close do-ui-font"></a>' +
|
|
||||||
'</dt>' +
|
|
||||||
'<dt class="cont-box">' +
|
|
||||||
'<div class="remote" :visible="tab === 1">' +
|
|
||||||
'<section class="section do-fn-cl input"><span class="label">' +
|
|
||||||
lang[openType][2] +
|
|
||||||
'</span>' +
|
|
||||||
'<input class="txt" :duplex="attachAlt" />' +
|
|
||||||
'</section>' +
|
|
||||||
'<section class="section do-fn-cl input"><span class="label">' +
|
|
||||||
lang[openType][3] +
|
|
||||||
'</span>' +
|
|
||||||
'<input class="txt" :duplex="attach" />' +
|
|
||||||
'</section>' +
|
|
||||||
'<section class="section do-fn-cl">' +
|
|
||||||
'<a href="javascript:;" class="submit" :click="$confirm">确定</a>' +
|
|
||||||
'</section>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="local" :visible="tab === 2">' +
|
|
||||||
'<div class="select-file"><input id="meditor-attch" multiple :change="$change" type="file" class="hide" /><span class="file" :click="$select">选择文件</span><span class="tips">(上传大小限制:单文件最大 ' +
|
|
||||||
((ME.maxSize / 1048576).toFixed(2) - 0) +
|
|
||||||
'MB)</span></div>' +
|
|
||||||
'<ul class="upload-box">' +
|
|
||||||
'<li class="tr thead"><span class="td name">文件名</span><span class="td progress">上传进度</span><span class="td option">操作</span></li>' +
|
|
||||||
'<li class="tr" :repeat="uploadFile">' +
|
|
||||||
'<span class="td name" :text="el.name"></span>' +
|
|
||||||
'<span class="td progress" :html="el.progress"></span>' +
|
|
||||||
'<span class="td option"><a href="javascript:;" :click="$insert(el)">插入</a></span>' +
|
|
||||||
'</li>' +
|
|
||||||
'</ul>' +
|
|
||||||
'</div>' +
|
|
||||||
'<ul class="manager" :visible="tab === 3">' +
|
|
||||||
'<li class="item" :repeat="attachList" :click="$insert(el)">' +
|
|
||||||
'<span class="thumb" :html="el.thumb"></span>' +
|
|
||||||
'<p class="name" :attr-title="el.name" :text="el.name"></p>' +
|
|
||||||
'</li>' +
|
|
||||||
'</ul>' +
|
|
||||||
'</dt>' +
|
|
||||||
'</dl>'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [uploadFile 文件上传]
|
|
||||||
* @param {[type]} files [文件列表]
|
|
||||||
* @param {[type]} vm [vm对象]
|
|
||||||
* @param {[type]} type [image/file]
|
|
||||||
*/
|
|
||||||
function uploadFile(files, vm) {
|
|
||||||
for (var i = 0, it; (it = files[i++]); ) {
|
|
||||||
if (
|
|
||||||
openType === 'image' &&
|
|
||||||
!/\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(it.name)
|
|
||||||
) {
|
|
||||||
vm.uploadFile.push({
|
|
||||||
name: it.name,
|
|
||||||
progress: '<span class="red">0%(失败,不允许的文件类型)</span>',
|
|
||||||
url: ''
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (ME.maxSize > 0 && it.size > ME.maxSize) {
|
|
||||||
vm.uploadFile.push({
|
|
||||||
name: it.name,
|
|
||||||
progress: '<span class="red">0%(文件体积过大)</span>',
|
|
||||||
url: ''
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var fixName =
|
|
||||||
Date.now().toString(16) + it.name.slice(it.name.lastIndexOf('.'))
|
|
||||||
|
|
||||||
var idx = vm.uploadFile.length,
|
|
||||||
upload = new Uploader(vm.uploadUrl || ME.uploadUrl)
|
|
||||||
|
|
||||||
vm.uploadFile.push({ name: it.name, progress: '0%', url: '' })
|
|
||||||
|
|
||||||
upload.field('file', it)
|
|
||||||
|
|
||||||
if (ME.beforeUpload) {
|
|
||||||
ME.beforeUpload(fixName, function(qn) {
|
|
||||||
upload
|
|
||||||
.field('token', qn.token)
|
|
||||||
.field('key', qn.key)
|
|
||||||
.onProgress(function(val) {
|
|
||||||
vm.uploadFile[idx].progress = val + '%'
|
|
||||||
})
|
|
||||||
.onEnd(function(json) {
|
|
||||||
vm.uploadFile[idx].url = qn.url
|
|
||||||
})
|
|
||||||
.start()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
upload
|
|
||||||
.onProgress(function(val) {
|
|
||||||
vm.uploadFile[idx].progress = val + '%'
|
|
||||||
})
|
|
||||||
.onEnd(function(json) {
|
|
||||||
vm.uploadFile[idx].url = json.data.url
|
|
||||||
})
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getAttach(vm, cb) {
|
function getAttach(vm, cb) {
|
||||||
var xhr = new XMLHttpRequest(),
|
var xhr = new XMLHttpRequest(),
|
||||||
url = vm.manageUrl || ME.manageUrl
|
url = vm.manageUrl || ME.manageUrl
|
||||||
|
|
||||||
if (/\?/.test(url)) {
|
if (/\?/.test(url)) {
|
||||||
url += '&type=' + openType
|
url += '&type=' + openType
|
||||||
|
} else {
|
||||||
|
url += '?type=' + openType
|
||||||
|
}
|
||||||
|
url += '&t=' + Math.random()
|
||||||
|
|
||||||
|
xhr.open('GET', url, true)
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (
|
||||||
|
this.readyState === 4 &&
|
||||||
|
this.status === 200 &&
|
||||||
|
this.responseText !== ''
|
||||||
|
) {
|
||||||
|
var res = this.responseText
|
||||||
|
try {
|
||||||
|
res = JSON.parse(res)
|
||||||
|
} catch (err) {}
|
||||||
|
cb(res)
|
||||||
} else {
|
} else {
|
||||||
url += '?type=' + openType
|
if (this.status !== 200 && this.responseText)
|
||||||
|
console.error(this.responseText)
|
||||||
}
|
}
|
||||||
url += '&t=' + Math.random()
|
|
||||||
|
|
||||||
xhr.open('GET', url, true)
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (
|
|
||||||
this.readyState === 4 &&
|
|
||||||
this.status === 200 &&
|
|
||||||
this.responseText !== ''
|
|
||||||
) {
|
|
||||||
var res = this.responseText
|
|
||||||
try {
|
|
||||||
res = JSON.parse(res)
|
|
||||||
} catch (err) {}
|
|
||||||
cb(res)
|
|
||||||
} else {
|
|
||||||
if (this.status !== 200 && this.responseText)
|
|
||||||
console.error(this.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.send()
|
|
||||||
}
|
}
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
|
||||||
function showDialog(elem, vm) {
|
function showDialog(elem, vm, tool) {
|
||||||
opened = true
|
let offset = Anot(elem).offset()
|
||||||
var offset = yua(elem).offset(),
|
|
||||||
layid = layer.open({
|
|
||||||
type: 7,
|
|
||||||
menubar: false,
|
|
||||||
shade: false,
|
|
||||||
fixed: true,
|
|
||||||
offset: [offset.top + 37 - ME.doc.scrollTop()],
|
|
||||||
tab: 2,
|
|
||||||
attach: '',
|
|
||||||
attachAlt: '',
|
|
||||||
uploadFile: [], //当前上传的列表
|
|
||||||
attachList: [], //附件管理列表
|
|
||||||
$switch: function(id) {
|
|
||||||
var lvm = yua.vmodels[layid]
|
|
||||||
|
|
||||||
lvm.tab = id
|
layer.open({
|
||||||
if (id === 3) {
|
type: 7,
|
||||||
lvm.attachList.clear()
|
menubar: false,
|
||||||
if (cache[openType].length) {
|
fixed: true,
|
||||||
lvm.attachList = cache[openType]
|
offset: [offset.top + 37 - $doc.scrollTop()],
|
||||||
} else {
|
tab: 2,
|
||||||
getAttach(vm, function(json) {
|
attach: '',
|
||||||
if (json) {
|
attachAlt: '',
|
||||||
cache[openType] = json.data.list.map(function(it) {
|
uploadQueue: [], //当前上传的列表
|
||||||
it.thumb =
|
attachList: [], //附件管理列表
|
||||||
openType === 'image'
|
switchTab: function(id) {
|
||||||
? '<img src="' + it.url + '"/>'
|
this.tab = id
|
||||||
: '<em class="attach-icon"></em>'
|
if (id === 3) {
|
||||||
return it
|
this.attachList.clear()
|
||||||
})
|
if (cache[tool].length) {
|
||||||
lvm.attachList = json.data.list
|
this.attachList = cache[tool]
|
||||||
}
|
} else {
|
||||||
|
getAttach(vm, function(json) {
|
||||||
|
if (json) {
|
||||||
|
cache[tool] = json.data.list.map(function(it) {
|
||||||
|
it.thumb =
|
||||||
|
tool === 'image'
|
||||||
|
? '<img src="' + it.url + '"/>'
|
||||||
|
: '<em class="attach-icon"></em>'
|
||||||
|
return it
|
||||||
})
|
})
|
||||||
|
this.attachList = json.data.list
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
},
|
}
|
||||||
$select: yua.noop,
|
}
|
||||||
$change: yua.noop,
|
},
|
||||||
$insert: function(it) {
|
select() {
|
||||||
if (!it.url) {
|
let ev = document.createEvent('MouseEvent')
|
||||||
return
|
ev.initEvent('click', false, false)
|
||||||
}
|
this.$refs.attach.dispatchEvent(ev)
|
||||||
var val =
|
},
|
||||||
(openType === 'image' ? '!' : '') +
|
change(ev) {
|
||||||
'[' +
|
this.files = ev.target.files
|
||||||
it.name +
|
uploadFile.call(this, vm, tool)
|
||||||
'](' +
|
},
|
||||||
it.url +
|
insert: function(it) {
|
||||||
')'
|
if (!it.url) {
|
||||||
ME.insert(vm.$editor, val)
|
return
|
||||||
},
|
}
|
||||||
$confirm: function() {
|
let val = `\n${tool === 'image' ? '!' : ''}[${it.name}](${it.url})`
|
||||||
var lvm = yua.vmodels[layid]
|
vm.insert(val)
|
||||||
if (!lvm.attach || !lvm.attachAlt) {
|
},
|
||||||
return layer.alert('描述和地址不能为空')
|
confirm: function() {
|
||||||
}
|
if (!this.attach || !this.attachAlt) {
|
||||||
var val = '![' + lvm.attachAlt + '](' + lvm.attach + ')'
|
return layer.toast('描述和地址不能为空', 'error')
|
||||||
|
}
|
||||||
|
let val = `\n${tool === 'image' ? '!' : ''}[${this.attachAlt}](${
|
||||||
|
this.attach
|
||||||
|
})`
|
||||||
|
|
||||||
ME.insert(vm.$editor, val)
|
vm.insert(val)
|
||||||
lvm.no()
|
this.close()
|
||||||
},
|
},
|
||||||
success: function(id) {
|
|
||||||
var _this = yua.vmodels[id],
|
|
||||||
$file = document.body.querySelector('#meditor-attch')
|
|
||||||
|
|
||||||
_this.no = function() {
|
content: fixCont(vm, tool)
|
||||||
layer.close(id)
|
})
|
||||||
opened = false
|
}
|
||||||
}
|
|
||||||
_this.$select = function() {
|
const addon = {
|
||||||
var ev = document.createEvent('MouseEvent')
|
attach(elem) {
|
||||||
ev.initEvent('click', false, false)
|
showDialog(elem, this, 'file')
|
||||||
$file.dispatchEvent(ev)
|
},
|
||||||
}
|
image(elem) {
|
||||||
_this.$change = function() {
|
showDialog(elem, this, 'image')
|
||||||
uploadFile(this.files, _this)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
content: fixCont()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ME.addon.image = function(elem, vm) {
|
export default addon
|
||||||
if (opened) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
openType = 'image'
|
|
||||||
showDialog(elem, vm)
|
|
||||||
}
|
|
||||||
|
|
||||||
ME.addon.file = function(elem, vm) {
|
|
||||||
if (opened) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
openType = 'file'
|
|
||||||
showDialog(elem, vm)
|
|
||||||
}
|
|
||||||
|
|
||||||
return $init
|
|
||||||
})
|
|
||||||
|
|
|
@ -5,58 +5,76 @@
|
||||||
* @date 2017-04-20 19:13:24
|
* @date 2017-04-20 19:13:24
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@import 'var.scss';
|
||||||
|
|
||||||
.do-meditor-attach {width:630px;height:auto;background:#f7f7f7;cursor:default;
|
.do-meditor-attach {width:630px;height:300px;cursor:default;color:nth($cgr, 1);
|
||||||
|
dt.close {position:absolute;z-index:65539;top:-8px;right:-8px;width:40px;height:40px;line-height:40px;font-size:20px;text-align:center;cursor:pointer;
|
||||||
|
|
||||||
.tab-box {width:100%;height:50px;line-height:49px;border-bottom:1px solid #e2e2e2;text-align:center;
|
&:hover {color:nth($ct, 1);font-size:28px;}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-box {float:left;width:130px;height:300px;padding:10px 5px;text-align:center;background:nth($cp, 2);border-radius:5px;
|
||||||
|
|
||||||
.item {position:relative;float:left;width:100px;height:49px;border-right:1px solid #ddd;cursor:pointer;}
|
.item {display:block;width:100%;height:40px;line-height:40px;border-radius:3px;cursor:pointer;
|
||||||
.item.active {background:#fff;}
|
|
||||||
.item.active::after {position:absolute;left:0;bottom:-1px;width:100%;height:1px;background:#fff;content:""}
|
&.active {background:#fff;}
|
||||||
a.action-close {top:5px;width:40px;height:40px;line-height:40px;font-size:20px;}
|
}
|
||||||
a.action-close:hover {line-height:40px;border:0;}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cont-box {position:relative;float:right;width:480px;height:auto;min-height:200px;
|
||||||
|
|
||||||
|
.remote,
|
||||||
|
.local {position:relative;width:92%;height:auto;margin:0 auto;}
|
||||||
|
|
||||||
|
.remote {padding:30px 0;}
|
||||||
|
.hide {display:none;}
|
||||||
|
|
||||||
|
.section {display:block;width:100%;height:auto;margin:15px 0;line-height:35px;
|
||||||
|
|
||||||
|
.txt {width:100%;height:45px;padding:0 10px;border:0;border-radius:5px;background:nth($cp, 2);color:nth($cgr, 1);font-size:14px;}
|
||||||
|
.submit {float:right;width:30%;height:45px;line-height:45px;}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cont-box {position:relative;width:100%;height:auto;min-height:200px;background:#fff;
|
|
||||||
|
|
||||||
.remote,
|
.select-file {width:100%;height:35px;line-height:35px;
|
||||||
.local {position:relative;width:60%;height:auto;margin:0 auto;padding:15px 0 30px;}
|
|
||||||
.local {width:96%;}
|
.file {float:left;width:100px;height:35px;border-radius:3px;background:nth($cp, 1);text-align:center;cursor:pointer;
|
||||||
|
|
||||||
.hide {display:none;}
|
&:hover {background:nth($cp, 2);}
|
||||||
|
&:active {background:nth($cp, 3);}
|
||||||
.section {display:block;width:100%;height:auto;margin:15px 0;line-height:35px;
|
}
|
||||||
|
.tips {display:inline-block;padding:0 10px;}
|
||||||
&.input {line-height:33px;border:1px solid #e9e9e9;}
|
|
||||||
|
|
||||||
.label {float: left;width:30%;text-align:center;background:#f7f7f7;}
|
|
||||||
.txt {float: left;width:70%;height:33px;padding:0 8px;border:0;border-left:1px solid #e9e9e9;background:#fff;color:#666;}
|
|
||||||
.submit {float:right;width:30%;height:35px;background:#ddd;color:#666;text-align:center;}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.select-file {width:100%;height:35px;line-height:33px}
|
|
||||||
.select-file span.file {float:left;width:100px;height:35px;border:1px solid #ddd;background:#f7f7f7;color:#666;text-align:center;cursor:pointer;}
|
|
||||||
.select-file span.tips {display:inline-block;padding:0 10px;line-height:35px;color:#666;}
|
|
||||||
.upload-box {width:100%;height:auto;min-height:190px;margin:10px 0;border:1px solid #e2e2e2;}
|
|
||||||
.upload-box .tr {width:100%;height:35px;line-height:35px;text-align:center;}
|
|
||||||
.upload-box .tr:hover {background:#fafafa}
|
|
||||||
.upload-box .thead {line-height:34px;border-bottom:1px solid #e2e2e2;background:#f7f7f7;}
|
|
||||||
|
|
||||||
.upload-box .td {float:left;}
|
|
||||||
.upload-box .td.name {width:45%;}
|
|
||||||
.upload-box .td.progress {width:40%;}
|
|
||||||
.upload-box .td.option {width:15%;}
|
|
||||||
.upload-box .td.option a {display:inline-block;padding:3px 5px;line-height:13px;border:1px solid #e2e2e2;background:#f7f7f7;color:#666;}
|
|
||||||
.upload-box .td .red {color:#f30;}
|
|
||||||
|
|
||||||
.manager {overflow:hidden;overflow-y:auto;width:100%;height:320px;padding:10px;}
|
|
||||||
.manager .item {float:left;width:22%;height:130px;margin:10px 1.5%;padding:5px;}
|
|
||||||
.manager .item:hover {background:#f7f7f7;}
|
|
||||||
.manager .thumb {display:block;width:100%;height:100px;}
|
|
||||||
.manager .name {overflow:hidden;height:20px;line-height:30px; text-align:center;}
|
|
||||||
.manager img {width:100%;height:100%;}
|
|
||||||
.manager .attach-icon {display:inline-block;width:100%;height:100px;text-align:center;font:50px/100px "ui font" !important;-webkit-font-smoothing: antialiased;-webkit-text-stroke-width: 0.2px;-moz-osx-font-smoothing: grayscale;}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-box {width:100%;height:auto;min-height:255px;padding-top:10px;
|
||||||
|
|
||||||
|
.thead {width:100%;height:35px;line-height:35px;background:nth($cp, 2);}
|
||||||
|
.thead .col {text-align:center;}
|
||||||
|
|
||||||
|
.col {float:left;height:30px;padding:0 5px;}
|
||||||
|
.col:nth-child(1) {width:50%}
|
||||||
|
.col:nth-child(2) {width:35%}
|
||||||
|
.col:nth-child(3) {width:15%}
|
||||||
|
|
||||||
|
.tbody {overflow:hidden;overflow-y:auto;width:100%;height:220px;
|
||||||
|
p {display:block;width:100%;height:30px;line-height:30px;}
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert {display:inline-block;padding:3px 5px;line-height:13px;background:nth($cp, 2);color:nth($cgr, 1);cursor:pointer;}
|
||||||
|
.red {color:#f30;}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.manager {overflow:hidden;overflow-y:auto;width:100%;height:320px;padding:10px;}
|
||||||
|
.manager .item {float:left;width:22%;height:130px;margin:10px 1.5%;padding:5px;}
|
||||||
|
.manager .item:hover {background:#f7f7f7;}
|
||||||
|
.manager .thumb {display:block;width:100%;height:100px;}
|
||||||
|
.manager .name {overflow:hidden;height:20px;line-height:30px; text-align:center;}
|
||||||
|
.manager img {width:100%;height:100%;}
|
||||||
|
.manager .attach-icon {display:inline-block;width:100%;height:100px;text-align:center;font:50px/100px "ui font" !important;-webkit-font-smoothing: antialiased;-webkit-text-stroke-width: 0.2px;-moz-osx-font-smoothing: grayscale;}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,32 +20,33 @@ function trim(str, sign) {
|
||||||
return str.replace(new RegExp('^' + sign + '|' + sign + '$', 'g'), '')
|
return str.replace(new RegExp('^' + sign + '|' + sign + '$', 'g'), '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const $doc = Anot(document)
|
||||||
|
|
||||||
const addon = {
|
const addon = {
|
||||||
h1: function(elem, vm) {
|
h1: function(elem) {
|
||||||
let that = this
|
let that = this
|
||||||
let editor = vm.$refs.editor
|
|
||||||
let offset = Anot(elem).offset()
|
let offset = Anot(elem).offset()
|
||||||
let wrap = this.selection(editor, true) || '在此输入文本'
|
let wrap = this.selection(true) || '在此输入文本'
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 7,
|
type: 7,
|
||||||
menubar: false,
|
menubar: false,
|
||||||
maskClose: true,
|
maskClose: true,
|
||||||
|
maskColor: 'rgba(255,255,255,0)',
|
||||||
fixed: true,
|
fixed: true,
|
||||||
insert: function(level) {
|
insert: function(level) {
|
||||||
wrap = wrap.replace(/^#{1,6} /, '')
|
wrap = wrap.replace(/^(#{1,6} )?/, '#'.repeat(level) + ' ')
|
||||||
wrap = that.repeat('#', level) + ' ' + wrap
|
that.insert(wrap, true)
|
||||||
that.insert(editor, wrap, true)
|
|
||||||
this.close()
|
this.close()
|
||||||
},
|
},
|
||||||
offset: [
|
offset: [
|
||||||
offset.top + 40 - that.doc.scrollTop(),
|
offset.top + 40 - $doc.scrollTop(),
|
||||||
'auto',
|
'auto',
|
||||||
'auto',
|
'auto',
|
||||||
offset.left - that.doc.scrollLeft()
|
offset.left - $doc.scrollLeft()
|
||||||
],
|
],
|
||||||
shift: {
|
shift: {
|
||||||
top: offset.top - that.doc.scrollTop(),
|
top: offset.top - $doc.scrollTop(),
|
||||||
left: offset.left - that.doc.scrollLeft()
|
left: offset.left - $doc.scrollLeft()
|
||||||
},
|
},
|
||||||
content: `
|
content: `
|
||||||
<ul class="do-meditor-h1 do-fn-noselect do-meditor__font">
|
<ul class="do-meditor-h1 do-fn-noselect do-meditor__font">
|
||||||
|
@ -58,60 +59,61 @@ const addon = {
|
||||||
</ul>`
|
</ul>`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
quote: function(elem, vm) {
|
quote: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
wrap = '> ' + wrap
|
wrap = '> ' + wrap
|
||||||
|
|
||||||
this.insert(vm.$refs.editor, wrap, true)
|
this.insert(wrap, true)
|
||||||
},
|
},
|
||||||
bold: function(elem, vm) {
|
bold: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
let wraped = trim(wrap, '\\*\\*')
|
let wraped = trim(wrap, '\\*\\*')
|
||||||
|
|
||||||
wrap = wrap === wraped ? '**' + wrap + '**' : wraped
|
wrap = wrap === wraped ? '**' + wrap + '**' : wraped
|
||||||
|
|
||||||
this.insert(vm.$refs.editor, wrap, true)
|
this.insert(wrap, true)
|
||||||
},
|
},
|
||||||
italic: function(elem, vm) {
|
italic: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
let wraped = trim(wrap, '_')
|
let wraped = trim(wrap, '_')
|
||||||
|
|
||||||
wrap = wrap === wraped ? '_' + wrap + '_' : wraped
|
wrap = wrap === wraped ? '_' + wrap + '_' : wraped
|
||||||
|
|
||||||
this.insert(vm.$refs.editor, wrap, true)
|
this.insert(wrap, true)
|
||||||
},
|
},
|
||||||
through: function(elem, vm) {
|
through: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
let wraped = trim(wrap, '~~')
|
let wraped = trim(wrap, '~~')
|
||||||
|
|
||||||
wrap = wrap === wraped ? '~~' + wrap + '~~' : wraped
|
wrap = wrap === wraped ? '~~' + wrap + '~~' : wraped
|
||||||
|
|
||||||
this.insert(vm.$refs.editor, wrap, true)
|
this.insert(wrap, true)
|
||||||
},
|
},
|
||||||
unordered: function(elem, vm) {
|
unordered: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
wrap = '* ' + wrap
|
wrap = '* ' + wrap
|
||||||
|
|
||||||
this.insert(vm.$refs.editor, wrap, false)
|
this.insert(wrap, false)
|
||||||
},
|
},
|
||||||
ordered: function(elem, vm) {
|
ordered: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
wrap = '1. ' + wrap
|
wrap = '1. ' + wrap
|
||||||
|
|
||||||
this.insert(vm.$refs.editor, wrap, false)
|
this.insert(wrap, false)
|
||||||
},
|
},
|
||||||
hr: function(elem, vm) {
|
hr: function(elem) {
|
||||||
this.insert(vm.$refs.editor, '\n\n---\n\n', false)
|
this.insert('\n\n---\n\n', false)
|
||||||
},
|
},
|
||||||
link: function(elem, vm) {
|
link: function(elem) {
|
||||||
let that = this
|
let that = this
|
||||||
let offset = Anot(elem).offset()
|
let offset = Anot(elem).offset()
|
||||||
let wrap = this.selection(vm.$refs.editor) || ''
|
let wrap = this.selection() || ''
|
||||||
|
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 7,
|
type: 7,
|
||||||
menubar: false,
|
menubar: false,
|
||||||
maskClose: true,
|
maskClose: true,
|
||||||
|
maskColor: 'rgba(255,255,255,0)',
|
||||||
fixed: true,
|
fixed: true,
|
||||||
link: '',
|
link: '',
|
||||||
linkName: wrap,
|
linkName: wrap,
|
||||||
|
@ -124,29 +126,29 @@ const addon = {
|
||||||
this.linkTarget === 1 ? ' "target=_blank"' : ''
|
this.linkTarget === 1 ? ' "target=_blank"' : ''
|
||||||
})`
|
})`
|
||||||
|
|
||||||
that.insert(vm.$refs.editor, val, false)
|
that.insert(val, false)
|
||||||
this.close()
|
this.close()
|
||||||
},
|
},
|
||||||
offset: [
|
offset: [
|
||||||
offset.top + 40 - that.doc.scrollTop(),
|
offset.top + 40 - $doc.scrollTop(),
|
||||||
'auto',
|
'auto',
|
||||||
'auto',
|
'auto',
|
||||||
offset.left - that.doc.scrollLeft()
|
offset.left - $doc.scrollLeft()
|
||||||
],
|
],
|
||||||
shift: {
|
shift: {
|
||||||
top: offset.top - that.doc.scrollTop(),
|
top: offset.top - $doc.scrollTop(),
|
||||||
left: offset.left - that.doc.scrollLeft()
|
left: offset.left - $doc.scrollLeft()
|
||||||
},
|
},
|
||||||
content: `
|
content: `
|
||||||
<div class="do-meditor-common do-meditor__font">
|
<div class="do-meditor-common do-meditor__font">
|
||||||
<section class="input">
|
<section>
|
||||||
<input class="txt" :duplex="linkName" placeholder="链接文字"/>
|
<input class="txt" :duplex="linkName" placeholder="链接文字"/>
|
||||||
</section>
|
</section>
|
||||||
<section class="input">
|
<section>
|
||||||
<input class="txt" :duplex="link" placeholder="链接地址"/>
|
<input class="txt" :duplex="link" placeholder="链接地址"/>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<label>
|
<label class="label">
|
||||||
<input
|
<input
|
||||||
name="link"
|
name="link"
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -155,7 +157,7 @@ const addon = {
|
||||||
value="1"/>
|
value="1"/>
|
||||||
新窗口打开
|
新窗口打开
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class="label">
|
||||||
<input
|
<input
|
||||||
name="link"
|
name="link"
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -174,10 +176,10 @@ const addon = {
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
time: function(elem, vm) {
|
time: function(elem) {
|
||||||
this.insert(vm.$refs.editor, new Date().format(), false)
|
this.insert(new Date().format(), false)
|
||||||
},
|
},
|
||||||
face: function(elem, vm) {
|
face: function(elem) {
|
||||||
let that = this
|
let that = this
|
||||||
let offset = Anot(elem).offset()
|
let offset = Anot(elem).offset()
|
||||||
|
|
||||||
|
@ -186,6 +188,7 @@ const addon = {
|
||||||
title: '插入表情',
|
title: '插入表情',
|
||||||
fixed: true,
|
fixed: true,
|
||||||
maskClose: true,
|
maskClose: true,
|
||||||
|
maskColor: 'rgba(255,255,255,0)',
|
||||||
arr: [
|
arr: [
|
||||||
'😀',
|
'😀',
|
||||||
'😅',
|
'😅',
|
||||||
|
@ -225,14 +228,14 @@ const addon = {
|
||||||
'🙏'
|
'🙏'
|
||||||
],
|
],
|
||||||
offset: [
|
offset: [
|
||||||
offset.top + 40 - that.doc.scrollTop(),
|
offset.top + 40 - $doc.scrollTop(),
|
||||||
'auto',
|
'auto',
|
||||||
'auto',
|
'auto',
|
||||||
offset.left - that.doc.scrollLeft()
|
offset.left - $doc.scrollLeft()
|
||||||
],
|
],
|
||||||
shift: {
|
shift: {
|
||||||
top: offset.top - that.doc.scrollTop(),
|
top: offset.top - $doc.scrollTop(),
|
||||||
left: offset.left - that.doc.scrollLeft()
|
left: offset.left - $doc.scrollLeft()
|
||||||
},
|
},
|
||||||
content: `
|
content: `
|
||||||
<ul class="do-meditor-face">
|
<ul class="do-meditor-face">
|
||||||
|
@ -241,12 +244,12 @@ const addon = {
|
||||||
</li>
|
</li>
|
||||||
</ul>`,
|
</ul>`,
|
||||||
insert: function(val) {
|
insert: function(val) {
|
||||||
that.insert(vm.$refs.editor, val, false)
|
that.insert(val, false)
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
table: function(elem, vm) {
|
table: function(elem) {
|
||||||
let that = this
|
let that = this
|
||||||
let offset = Anot(elem).offset()
|
let offset = Anot(elem).offset()
|
||||||
|
|
||||||
|
@ -255,15 +258,16 @@ const addon = {
|
||||||
title: '0行 x 0列',
|
title: '0行 x 0列',
|
||||||
fixed: true,
|
fixed: true,
|
||||||
maskClose: true,
|
maskClose: true,
|
||||||
|
maskColor: 'rgba(255,255,255,0)',
|
||||||
offset: [
|
offset: [
|
||||||
offset.top + 40 - that.doc.scrollTop(),
|
offset.top + 40 - $doc.scrollTop(),
|
||||||
'auto',
|
'auto',
|
||||||
'auto',
|
'auto',
|
||||||
offset.left - that.doc.scrollLeft()
|
offset.left - $doc.scrollLeft()
|
||||||
],
|
],
|
||||||
shift: {
|
shift: {
|
||||||
top: offset.top - that.doc.scrollTop(),
|
top: offset.top - $doc.scrollTop(),
|
||||||
left: offset.left - that.doc.scrollLeft()
|
left: offset.left - $doc.scrollLeft()
|
||||||
},
|
},
|
||||||
matrix: objArr(10).map(function() {
|
matrix: objArr(10).map(function() {
|
||||||
return objArr(10)
|
return objArr(10)
|
||||||
|
@ -313,26 +317,27 @@ const addon = {
|
||||||
let x = ev.target.dataset.x - 0 + 1
|
let x = ev.target.dataset.x - 0 + 1
|
||||||
let y = ev.target.dataset.y - 0 + 1
|
let y = ev.target.dataset.y - 0 + 1
|
||||||
|
|
||||||
let thead = `\n\n${that.repeat('| 表头 ', x)}|\n`
|
let thead = `\n\n${'| 表头 '.repeat(x)}|\n`
|
||||||
let pipe = `${that.repeat('| -- ', x)}|\n`
|
let pipe = `${'| -- '.repeat(x)}|\n`
|
||||||
let tbody = that.repeat(that.repeat('| ', x) + '|\n', y)
|
let tbody = ('| '.repeat(x) + '|\n').repeat(y)
|
||||||
|
|
||||||
that.insert(vm.$refs.editor, thead + pipe + tbody, false)
|
that.insert(thead + pipe + tbody, false)
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
image: function(elem, vm) {
|
image: function(elem) {
|
||||||
let that = this
|
let that = this
|
||||||
let offset = Anot(elem).offset()
|
let offset = Anot(elem).offset()
|
||||||
let wrap = this.selection(vm.$refs.editor) || ''
|
let wrap = this.selection() || ''
|
||||||
|
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 7,
|
type: 7,
|
||||||
menubar: false,
|
menubar: false,
|
||||||
maskClose: true,
|
maskClose: true,
|
||||||
|
maskColor: 'rgba(255,255,255,0)',
|
||||||
fixed: true,
|
fixed: true,
|
||||||
img: '',
|
img: '',
|
||||||
imgAlt: wrap,
|
imgAlt: wrap,
|
||||||
|
@ -342,25 +347,25 @@ const addon = {
|
||||||
}
|
}
|
||||||
let val = `![${this.imgAlt}](${this.img})`
|
let val = `![${this.imgAlt}](${this.img})`
|
||||||
|
|
||||||
that.insert(vm.$refs.editor, val, false)
|
that.insert(val, false)
|
||||||
this.close()
|
this.close()
|
||||||
},
|
},
|
||||||
offset: [
|
offset: [
|
||||||
offset.top + 40 - that.doc.scrollTop(),
|
offset.top + 40 - $doc.scrollTop(),
|
||||||
'auto',
|
'auto',
|
||||||
'auto',
|
'auto',
|
||||||
offset.left - that.doc.scrollLeft()
|
offset.left - $doc.scrollLeft()
|
||||||
],
|
],
|
||||||
shift: {
|
shift: {
|
||||||
top: offset.top - that.doc.scrollTop(),
|
top: offset.top - $doc.scrollTop(),
|
||||||
left: offset.left - that.doc.scrollLeft()
|
left: offset.left - $doc.scrollLeft()
|
||||||
},
|
},
|
||||||
content: `
|
content: `
|
||||||
<div class="do-meditor-common do-meditor__font">
|
<div class="do-meditor-common do-meditor__font">
|
||||||
<section class="input">
|
<section>
|
||||||
<input class="txt" :duplex="imgAlt" placeholder="图片描述"/>
|
<input class="txt" :duplex="imgAlt" placeholder="图片描述"/>
|
||||||
</section>
|
</section>
|
||||||
<section class="input">
|
<section>
|
||||||
<input class="txt" :duplex="img" placeholder="图片地址"/>
|
<input class="txt" :duplex="img" placeholder="图片地址"/>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -373,17 +378,17 @@ const addon = {
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
attach: function(elem, vm) {
|
attach: function(elem) {
|
||||||
this.addon.link.call(this, elem, vm, false)
|
this.addon.link.call(this, elem)
|
||||||
},
|
},
|
||||||
inlinecode: function(elem, vm) {
|
inlinecode: function(elem) {
|
||||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
let wrap = this.selection() || '在此输入文本'
|
||||||
let wraped = trim(wrap, '`')
|
let wraped = trim(wrap, '`')
|
||||||
|
|
||||||
wrap = wrap === wraped ? '`' + wrap + '`' : wraped
|
wrap = wrap === wraped ? '`' + wrap + '`' : wraped
|
||||||
this.insert(vm.$refs.editor, wrap, true)
|
this.insert(wrap, true)
|
||||||
},
|
},
|
||||||
blockcode: function(elem, vm) {
|
blockcode: function(elem) {
|
||||||
let that = this
|
let that = this
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 7,
|
type: 7,
|
||||||
|
@ -426,48 +431,55 @@ const addon = {
|
||||||
],
|
],
|
||||||
lang: 'javascript',
|
lang: 'javascript',
|
||||||
code: '',
|
code: '',
|
||||||
$confirm: function() {
|
maskClose: true,
|
||||||
var lvm = Anot.vmodels[layid]
|
insert: function() {
|
||||||
var val =
|
let val = `\n\`\`\`${this.lang}\n${this.code ||
|
||||||
'\n```' + lvm.lang + '\n' + (lvm.code || '//在此输入代码') + '\n```\n'
|
'// 在此输入代码'}\n\`\`\`\n`
|
||||||
|
that.insert(val, false)
|
||||||
that.insert(vm.$refs.editor, val, false)
|
this.close()
|
||||||
layer.close(layid)
|
|
||||||
},
|
},
|
||||||
content:
|
content: `
|
||||||
'<div class="do-meditor-codeblock do-meditor__font">' +
|
<div class="do-meditor-codeblock do-meditor__font">
|
||||||
'<section class="do-fn-cl"><span class="label">语言类型</span>' +
|
<section class="do-fn-cl">
|
||||||
'<select :duplex="lang">' +
|
<span class="label">语言类型</span>
|
||||||
'<option :repeat="$lang" :attr-value="el.id">{{el.name || el.id}}</option>' +
|
<select :duplex="lang">
|
||||||
'</select>' +
|
<option :repeat="$lang" :attr-value="el.id">{{el.name || el.id}}</option>
|
||||||
'</section>' +
|
</select>
|
||||||
'<section>' +
|
</section>
|
||||||
'<textarea :duplex="code" placeholder="在这里输入/粘贴代码"></textarea>' +
|
<section>
|
||||||
'</section>' +
|
<textarea :duplex="code" placeholder="在这里输入/粘贴代码"></textarea>
|
||||||
'<section class="do-fn-cl">' +
|
</section>
|
||||||
'<a href="javascript:;" class="submit" :click="$confirm">确定</a>' +
|
<section class="do-fn-cl">
|
||||||
'</section>' +
|
<a
|
||||||
'</div>'
|
href="javascript:;"
|
||||||
|
class="do-meditor__button submit"
|
||||||
|
:click="insert">确定</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
preview: function(elem, vm) {
|
preview: function() {
|
||||||
vm.preview = !vm.preview
|
this.preview = !this.preview
|
||||||
if (vm.preview) {
|
if (this.preview) {
|
||||||
vm.htmlTxt = vm.$htmlTxt
|
this.htmlTxt = this.__tmp__
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fullscreen: function(elem, vm) {
|
fullscreen: function() {
|
||||||
vm.fullscreen = !vm.fullscreen
|
this.fullscreen = !this.fullscreen
|
||||||
vm.$onFullscreen(vm.fullscreen)
|
if (typeof this.props.onFullscreen === 'function') {
|
||||||
|
this.props.onFullscreen(this.fullscreen)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
about: function(elem) {
|
about: function(elem) {
|
||||||
var offset = Anot(elem).offset()
|
let offset = Anot(elem).offset()
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 7,
|
type: 7,
|
||||||
title: '关于编辑器',
|
title: '关于编辑器',
|
||||||
maskClose: true,
|
maskClose: true,
|
||||||
offset: [offset.top + 37 - this.doc.scrollTop()],
|
maskColor: 'rgba(255,255,255,0)',
|
||||||
shift: { top: offset.top - this.doc.scrollTop() },
|
offset: [offset.top + 37 - $doc.scrollTop()],
|
||||||
|
shift: { top: offset.top - $doc.scrollTop() },
|
||||||
content:
|
content:
|
||||||
'<div class="do-meditor-about do-meditor__font">' +
|
'<div class="do-meditor-about do-meditor__font">' +
|
||||||
'<pre>' +
|
'<pre>' +
|
||||||
|
@ -477,7 +489,7 @@ const addon = {
|
||||||
'| | | | |__| (_| | | || (_) | |\n' +
|
'| | | | |__| (_| | | || (_) | |\n' +
|
||||||
'|_| |_|_____\\__,_|_|\\__\\___/|_| ' +
|
'|_| |_|_____\\__,_|_|\\__\\___/|_| ' +
|
||||||
'v' +
|
'v' +
|
||||||
this.version +
|
Anot.ui.meditor +
|
||||||
'</pre>' +
|
'</pre>' +
|
||||||
'<p>开源在线Markdown编辑器</p>' +
|
'<p>开源在线Markdown编辑器</p>' +
|
||||||
'<p><a target="_blank" href="https://doui.cc/product/meditor">https://doui.cc/product/meditor</a></p>' +
|
'<p><a target="_blank" href="https://doui.cc/product/meditor">https://doui.cc/product/meditor</a></p>' +
|
||||||
|
|
|
@ -17,158 +17,71 @@ marked.setOptions({
|
||||||
return Prism.highlight(code, Prism.languages[lang])
|
return Prism.highlight(code, Prism.languages[lang])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
let editorVM = []
|
if (!String.prototype.repeat) {
|
||||||
Anot.ui.meditor = '1.0.0'
|
String.prototype.repeat = function(num) {
|
||||||
const log = console.log
|
let result = ''
|
||||||
//存放编辑器公共静态资源
|
while (num > 0) {
|
||||||
window.ME = {
|
result += this
|
||||||
version: Anot.ui.meditor,
|
num--
|
||||||
// 工具栏title
|
|
||||||
toolbar: {
|
|
||||||
pipe: '',
|
|
||||||
h1: '标题',
|
|
||||||
quote: '引用文本',
|
|
||||||
bold: '粗体',
|
|
||||||
italic: '斜体',
|
|
||||||
through: '删除线',
|
|
||||||
unordered: '无序列表',
|
|
||||||
ordered: '有序列表',
|
|
||||||
link: '超链接',
|
|
||||||
hr: '横线',
|
|
||||||
time: '插入当前时间',
|
|
||||||
face: '表情',
|
|
||||||
table: '插入表格',
|
|
||||||
image: '插入图片',
|
|
||||||
file: '插入附件',
|
|
||||||
inlinecode: '行内代码',
|
|
||||||
blockcode: '代码块',
|
|
||||||
preview: '预览',
|
|
||||||
fullscreen: '全屏',
|
|
||||||
about: '关于编辑器'
|
|
||||||
},
|
|
||||||
addon, // 已有插件
|
|
||||||
// 往文本框中插入内容
|
|
||||||
insert: function(dom, val, isSelect) {
|
|
||||||
if (document.selection) {
|
|
||||||
dom.focus()
|
|
||||||
let range = document.selection.createRange()
|
|
||||||
range.text = val
|
|
||||||
dom.focus()
|
|
||||||
range.moveStart('character', -1)
|
|
||||||
} else if (dom.selectionStart || dom.selectionStart === 0) {
|
|
||||||
let startPos = dom.selectionStart
|
|
||||||
let endPos = dom.selectionEnd
|
|
||||||
let scrollTop = dom.scrollTop
|
|
||||||
|
|
||||||
dom.value =
|
|
||||||
dom.value.slice(0, startPos) +
|
|
||||||
val +
|
|
||||||
dom.value.slice(endPos, dom.value.length)
|
|
||||||
|
|
||||||
dom.selectionStart = isSelect ? startPos : startPos + val.length
|
|
||||||
dom.selectionEnd = startPos + val.length
|
|
||||||
dom.scrollTop = scrollTop
|
|
||||||
dom.focus()
|
|
||||||
} else {
|
|
||||||
dom.value += val
|
|
||||||
dom.focus()
|
|
||||||
}
|
}
|
||||||
},
|
return result
|
||||||
/**
|
|
||||||
* [selection 获取选中的文本]
|
|
||||||
* @param {[type]} dom [要操作的元素]
|
|
||||||
* @param {[type]} line [是否强制选取整行]
|
|
||||||
*/
|
|
||||||
selection: function(dom, line) {
|
|
||||||
if (document.selection) {
|
|
||||||
return document.selection.createRange().text
|
|
||||||
} else {
|
|
||||||
let startPos = dom.selectionStart
|
|
||||||
let endPos = dom.selectionEnd
|
|
||||||
|
|
||||||
if (endPos) {
|
|
||||||
//强制选择整行
|
|
||||||
if (line) {
|
|
||||||
startPos = dom.value.slice(0, startPos).lastIndexOf('\n')
|
|
||||||
|
|
||||||
let tmpEnd = dom.value.slice(endPos).indexOf('\n')
|
|
||||||
tmpEnd = tmpEnd < 0 ? 0 : tmpEnd
|
|
||||||
|
|
||||||
startPos += 1 //把\n加上
|
|
||||||
endPos += tmpEnd
|
|
||||||
|
|
||||||
dom.selectionStart = startPos
|
|
||||||
dom.selectionEnd = endPos
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//强制选择整行
|
|
||||||
if (line) {
|
|
||||||
endPos = dom.value.indexOf('\n')
|
|
||||||
endPos = endPos < 0 ? dom.value.length : endPos
|
|
||||||
dom.selectionEnd = endPos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dom.value.slice(startPos, endPos)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
repeat: function(str, num) {
|
|
||||||
if (String.prototype.repeat) {
|
|
||||||
return str.repeat(num)
|
|
||||||
} else {
|
|
||||||
var result = ''
|
|
||||||
while (num > 0) {
|
|
||||||
result += str
|
|
||||||
num--
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
},
|
|
||||||
get: function(id) {
|
|
||||||
if (id === void 0) {
|
|
||||||
id = editorVM.length - 1
|
|
||||||
}
|
|
||||||
var vm = editorVM[id]
|
|
||||||
if (vm) {
|
|
||||||
return {
|
|
||||||
id: vm.$id,
|
|
||||||
getVal: function() {
|
|
||||||
return vm.plainTxt.trim()
|
|
||||||
},
|
|
||||||
getHtml: function() {
|
|
||||||
return vm.$htmlTxt
|
|
||||||
},
|
|
||||||
setVal: function(txt) {
|
|
||||||
vm.plainTxt = txt || ''
|
|
||||||
},
|
|
||||||
show: function() {
|
|
||||||
vm.editorVisible = true
|
|
||||||
},
|
|
||||||
hide: function() {
|
|
||||||
vm.editorVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
doc: Anot(document)
|
|
||||||
}
|
|
||||||
//获取真实的引用路径,避免因为不同的目录结构导致加载失败的情况
|
|
||||||
for (var i in Anot.modules) {
|
|
||||||
if (/meditor/.test(i)) {
|
|
||||||
ME.path = i.slice(0, i.lastIndexOf('/'))
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var elems = {
|
|
||||||
p: function(str, attr, inner) {
|
Anot.ui.meditor = '1.0.0'
|
||||||
return inner ? '\n' + inner + '\n' : ''
|
const log = console.log
|
||||||
},
|
// 工具栏title
|
||||||
br: '\n',
|
const TOOLBAR = {
|
||||||
'h([1-6])': function(str, level, attr, inner) {
|
pipe: '',
|
||||||
var h = ME.repeat('#', level)
|
h1: '标题',
|
||||||
return '\n' + h + ' ' + inner + '\n'
|
quote: '引用文本',
|
||||||
},
|
bold: '粗体',
|
||||||
hr: '\n\n___\n\n',
|
italic: '斜体',
|
||||||
|
through: '删除线',
|
||||||
|
unordered: '无序列表',
|
||||||
|
ordered: '有序列表',
|
||||||
|
link: '超链接',
|
||||||
|
hr: '横线',
|
||||||
|
time: '插入当前时间',
|
||||||
|
face: '表情',
|
||||||
|
table: '插入表格',
|
||||||
|
image: '插入图片',
|
||||||
|
file: '插入附件',
|
||||||
|
inlinecode: '行内代码',
|
||||||
|
blockcode: '代码块',
|
||||||
|
preview: '预览',
|
||||||
|
fullscreen: '全屏',
|
||||||
|
about: '关于编辑器'
|
||||||
|
}
|
||||||
|
const DEFAULT_TOOLBAR = [
|
||||||
|
'h1',
|
||||||
|
'quote',
|
||||||
|
'|',
|
||||||
|
'bold',
|
||||||
|
'italic',
|
||||||
|
'through',
|
||||||
|
'|',
|
||||||
|
'unordered',
|
||||||
|
'ordered',
|
||||||
|
'|',
|
||||||
|
'hr',
|
||||||
|
'link',
|
||||||
|
'time',
|
||||||
|
'face',
|
||||||
|
'|',
|
||||||
|
'table',
|
||||||
|
'image',
|
||||||
|
'attach',
|
||||||
|
'inlinecode',
|
||||||
|
'blockcode',
|
||||||
|
'|',
|
||||||
|
'preview',
|
||||||
|
'fullscreen',
|
||||||
|
'|',
|
||||||
|
'about'
|
||||||
|
]
|
||||||
|
|
||||||
|
const ELEMS = {
|
||||||
a: function(str, attr, inner) {
|
a: function(str, attr, inner) {
|
||||||
let href = attr.match(attrExp('href'))
|
let href = attr.match(attrExp('href'))
|
||||||
let title = attr.match(attrExp('title'))
|
let title = attr.match(attrExp('title'))
|
||||||
|
@ -212,18 +125,27 @@ var elems = {
|
||||||
alt = (alt && alt[1]) || ''
|
alt = (alt && alt[1]) || ''
|
||||||
|
|
||||||
return '![' + alt + '](' + src + ')'
|
return '![' + alt + '](' + src + ')'
|
||||||
}
|
},
|
||||||
|
p: function(str, attr, inner) {
|
||||||
|
return inner ? '\n' + inner : ''
|
||||||
|
},
|
||||||
|
br: '\n',
|
||||||
|
'h([1-6])': function(str, level, attr, inner) {
|
||||||
|
let h = '#'.repeat(level)
|
||||||
|
return '\n' + h + ' ' + inner + '\n'
|
||||||
|
},
|
||||||
|
hr: '\n\n___\n\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
function attrExp(field) {
|
function attrExp(field, flag = 'i') {
|
||||||
return new RegExp(field + '\\s?=\\s?["\']?([^"\']*)["\']?', 'i')
|
return new RegExp(field + '\\s?=\\s?["\']?([^"\']*)["\']?', flag)
|
||||||
}
|
}
|
||||||
function tagExp(tag, open) {
|
function tagExp(tag, open) {
|
||||||
var exp = ''
|
var exp = ''
|
||||||
if (['br', 'hr', 'img'].indexOf(tag) > -1) {
|
if (['br', 'hr', 'img'].indexOf(tag) > -1) {
|
||||||
exp = '<' + tag + '([^>]*)\\/?>'
|
exp = '<' + tag + '([^>]*?)\\/?>'
|
||||||
} else {
|
} else {
|
||||||
exp = '<' + tag + '([^>]*)>([\\s\\S]*?)<\\/' + tag + '>'
|
exp = '<' + tag + '([^>]*?)>([\\s\\S]*?)<\\/' + tag + '>'
|
||||||
}
|
}
|
||||||
return new RegExp(exp, 'gi')
|
return new RegExp(exp, 'gi')
|
||||||
}
|
}
|
||||||
|
@ -233,14 +155,20 @@ function html2md(str) {
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
|
|
||||||
str = str.replace(/\t/g, ' ').replace(/<meta [^>]*>/, '')
|
str = str.replace(/\t/g, ' ').replace(/<meta [^>]*>/, '')
|
||||||
str = str.replace(
|
str = str
|
||||||
/<(div|span|dl|dd|dt|table|tr|td|thead|tbody|i|em|strong|h[1-6]|ul|ol|li) [^>]*>/g,
|
.replace(attrExp('class', 'g'), '')
|
||||||
'<$1>'
|
.replace(attrExp('style', 'g'), '')
|
||||||
)
|
str = str
|
||||||
|
.replace(
|
||||||
|
/<(div|span|header|footer|nav|dl|dd|dt|table|tr|td|thead|tbody|i|em|b|strong|h[1-6]|ul|ol|li|p|pre) [^>]*>/g,
|
||||||
|
'<$1>'
|
||||||
|
)
|
||||||
|
.replace(/<svg [^>]*>.*?<\/svg>/g, '{invalid image}')
|
||||||
|
|
||||||
for (var i in elems) {
|
// log(str)
|
||||||
var cb = elems[i],
|
for (let i in ELEMS) {
|
||||||
exp = tagExp(i)
|
let cb = ELEMS[i]
|
||||||
|
let exp = tagExp(i)
|
||||||
|
|
||||||
if (i === 'blockquote') {
|
if (i === 'blockquote') {
|
||||||
while (str.match(exp)) {
|
while (str.match(exp)) {
|
||||||
|
@ -250,6 +178,11 @@ function html2md(str) {
|
||||||
str = str.replace(exp, cb)
|
str = str.replace(exp, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对另外3种同类标签做一次处理
|
||||||
|
if (i === 'p') {
|
||||||
|
exp = tagExp('div')
|
||||||
|
str = str.replace(exp, cb)
|
||||||
|
}
|
||||||
if (i === 'em') {
|
if (i === 'em') {
|
||||||
exp = tagExp('i')
|
exp = tagExp('i')
|
||||||
str = str.replace(exp, cb)
|
str = str.replace(exp, cb)
|
||||||
|
@ -259,23 +192,23 @@ function html2md(str) {
|
||||||
str = str.replace(exp, cb)
|
str = str.replace(exp, cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var liExp = /<(ul|ol)[^>]*>(?:(?!<ul|<ol)[\s\S])*?<\/\1>/gi
|
let liExp = /<(ul|ol)>(?:(?!<ul|<ol)[\s\S])*?<\/\1>/gi
|
||||||
while (str.match(liExp)) {
|
while (str.match(liExp)) {
|
||||||
str = str.replace(liExp, function(match) {
|
str = str.replace(liExp, function(match) {
|
||||||
match = match.replace(/<(ul|ol)[^>]*>([\s\S]*?)<\/\1>/gi, function(
|
match = match.replace(/<(ul|ol)>([\s\S]*?)<\/\1>/gi, function(
|
||||||
m,
|
m,
|
||||||
t,
|
t,
|
||||||
inner
|
inner
|
||||||
) {
|
) {
|
||||||
var li = inner.split('</li>')
|
let li = inner.split('</li>')
|
||||||
li.pop()
|
li.pop()
|
||||||
|
|
||||||
for (var i = 0, len = li.length; i < len; i++) {
|
for (let i = 0, len = li.length; i < len; i++) {
|
||||||
var pre = t === 'ol' ? i + 1 + '. ' : '* '
|
let pre = t === 'ol' ? i + 1 + '. ' : '* '
|
||||||
li[i] =
|
li[i] =
|
||||||
pre +
|
pre +
|
||||||
li[i]
|
li[i]
|
||||||
.replace(/\s*<li[^>]*>([\s\S]*)/i, function(m, n) {
|
.replace(/\s*<li>([\s\S]*)/i, function(m, n) {
|
||||||
n = n.trim().replace(/\n/g, '\n ')
|
n = n.trim().replace(/\n/g, '\n ')
|
||||||
return n
|
return n
|
||||||
})
|
})
|
||||||
|
@ -287,6 +220,7 @@ function html2md(str) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
str = str
|
str = str
|
||||||
|
|
||||||
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
||||||
.replace(/```([\w\W]*)```/g, function(str, inner) {
|
.replace(/```([\w\W]*)```/g, function(str, inner) {
|
||||||
inner = inner
|
inner = inner
|
||||||
|
@ -298,52 +232,50 @@ function html2md(str) {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultToolbar = [
|
|
||||||
'h1',
|
|
||||||
'quote',
|
|
||||||
'|',
|
|
||||||
'bold',
|
|
||||||
'italic',
|
|
||||||
'through',
|
|
||||||
'|',
|
|
||||||
'unordered',
|
|
||||||
'ordered',
|
|
||||||
'|',
|
|
||||||
'hr',
|
|
||||||
'link',
|
|
||||||
'time',
|
|
||||||
'face',
|
|
||||||
'|',
|
|
||||||
'table',
|
|
||||||
'image',
|
|
||||||
'attach',
|
|
||||||
'inlinecode',
|
|
||||||
'blockcode',
|
|
||||||
'|',
|
|
||||||
'preview',
|
|
||||||
'fullscreen',
|
|
||||||
'|',
|
|
||||||
'about'
|
|
||||||
],
|
|
||||||
extraAddons = []
|
|
||||||
|
|
||||||
function tool(name) {
|
function tool(name) {
|
||||||
name = (name + '').trim().toLowerCase()
|
name = (name + '').trim().toLowerCase()
|
||||||
name = '|' === name ? 'pipe' : name
|
name = '|' === name ? 'pipe' : name
|
||||||
return (
|
let event = name === 'pipe' ? '' : `:click="onToolClick('${name}', $event)"`
|
||||||
'<span title="' +
|
let title = TOOLBAR[name]
|
||||||
ME.toolbar[name] +
|
return `
|
||||||
'" class="do-meditor__icon icon-' +
|
<span title="${title}" class="do-meditor__icon icon-${name}" ${event}></span>`
|
||||||
name +
|
}
|
||||||
'" ' +
|
|
||||||
(name !== 'pipe' ? ':click="onToolClick(\'' + name + '\', $event)"' : '') +
|
class MEObject {
|
||||||
'></span>'
|
constructor(vm) {
|
||||||
)
|
this.vm = vm
|
||||||
|
this.id = vm.$id
|
||||||
|
}
|
||||||
|
|
||||||
|
getVal() {
|
||||||
|
return this.vm.plainTxt.trim()
|
||||||
|
}
|
||||||
|
getHtml() {
|
||||||
|
return this.vm.__tmp__
|
||||||
|
}
|
||||||
|
setVal(txt) {
|
||||||
|
this.vm.plainTxt = txt || ''
|
||||||
|
}
|
||||||
|
show() {
|
||||||
|
this.vm.editorVisible = true
|
||||||
|
}
|
||||||
|
hide() {
|
||||||
|
this.vm.editorVisible = false
|
||||||
|
}
|
||||||
|
extends(addon) {
|
||||||
|
Object.assign(this.vm.addon, addon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Anot.component('meditor', {
|
Anot.component('meditor', {
|
||||||
|
construct: function(props, state) {
|
||||||
|
if (props.hasOwnProperty('$show')) {
|
||||||
|
state.editorVisible = props.$show
|
||||||
|
delete props.$show
|
||||||
|
}
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
let toolbar = (this.toolbar || defaultToolbar).map(it => tool(it)).join('')
|
let toolbar = (this.toolbar || DEFAULT_TOOLBAR).map(it => tool(it)).join('')
|
||||||
|
|
||||||
delete this.toolbar
|
delete this.toolbar
|
||||||
|
|
||||||
|
@ -360,98 +292,151 @@ Anot.component('meditor', {
|
||||||
:attr="{disabled: disabled}"
|
:attr="{disabled: disabled}"
|
||||||
:duplex="plainTxt"
|
:duplex="plainTxt"
|
||||||
:on-paste="onPaste($event)"></textarea>
|
:on-paste="onPaste($event)"></textarea>
|
||||||
<content
|
<content
|
||||||
|
ref="preview"
|
||||||
class="md-preview do-marked-theme"
|
class="md-preview do-marked-theme"
|
||||||
:visible="preview"
|
:visible="preview"
|
||||||
:html="htmlTxt"></content>
|
:html="htmlTxt"></content>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
|
|
||||||
construct: function(props, state) {
|
|
||||||
// Anot.mix(base, opt, attr)
|
|
||||||
// if (base.$addons && Array.isArray(base.$addons)) {
|
|
||||||
// extraAddons = base.$addons.map(function(name) {
|
|
||||||
// return ME.path + '/addon/' + name
|
|
||||||
// })
|
|
||||||
// delete base.$addons
|
|
||||||
// }
|
|
||||||
if (props.hasOwnProperty('$show')) {
|
|
||||||
state.editorVisible = props.$show
|
|
||||||
delete props.$show
|
|
||||||
}
|
|
||||||
},
|
|
||||||
componentWillMount: function(vm) {},
|
|
||||||
componentDidMount: function(vm, elem) {
|
componentDidMount: function(vm, elem) {
|
||||||
console.log(this)
|
let $editor = Anot(this.$refs.editor)
|
||||||
// vm.$editor = elem.children[1]
|
let preview = this.$refs.preview
|
||||||
|
$editor.bind('keydown', ev => {
|
||||||
|
let wrap = this.selection() || ''
|
||||||
|
let select = !!wrap
|
||||||
|
//tab键改为插入2个空格,阻止默认事件,防止焦点失去
|
||||||
|
if (ev.keyCode === 9) {
|
||||||
|
ev.preventDefault()
|
||||||
|
wrap = wrap
|
||||||
|
.split('\n')
|
||||||
|
.map(function(it) {
|
||||||
|
return ev.shiftKey ? it.replace(/^\s\s/, '') : ' ' + it
|
||||||
|
})
|
||||||
|
.join('\n')
|
||||||
|
this.insert(wrap, select)
|
||||||
|
}
|
||||||
|
//修复按退格键删除选中文本时,选中的状态不更新的bug
|
||||||
|
if (ev.keyCode === 8) {
|
||||||
|
if (select) {
|
||||||
|
ev.preventDefault()
|
||||||
|
this.insert('', select)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// editorVM.push(vm)
|
$editor.bind('scroll', ev => {
|
||||||
// //自动加载额外的插件
|
let syncTop =
|
||||||
// require(extraAddons, function() {
|
ev.target.scrollTop / ev.target.scrollHeight * preview.scrollHeight
|
||||||
// var args = Array.prototype.slice.call(arguments, 0)
|
|
||||||
// args.forEach(function(addon) {
|
|
||||||
// addon && addon(vm)
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
|
|
||||||
// Anot(vm.$editor).bind('keydown', function(ev) {
|
preview.scrollTop = syncTop
|
||||||
// var wrap = ME.selection(vm.$editor) || '',
|
})
|
||||||
// select = !!wrap
|
//编辑器成功加载的回调
|
||||||
// //tab键改为插入2个空格,阻止默认事件,防止焦点失去
|
if (typeof this.props.onCreated === 'function') {
|
||||||
// if (ev.keyCode === 9) {
|
this.props.onCreated(new MEObject(this))
|
||||||
// wrap = wrap
|
}
|
||||||
// .split('\n')
|
|
||||||
// .map(function(it) {
|
|
||||||
// return ev.shiftKey ? it.replace(/^\s\s/, '') : ' ' + it
|
|
||||||
// })
|
|
||||||
// .join('\n')
|
|
||||||
// ME.insert(this, wrap, select)
|
|
||||||
// ev.preventDefault()
|
|
||||||
// }
|
|
||||||
// //修复按退格键删除选中文本时,选中的状态不更新的bug
|
|
||||||
// if (ev.keyCode === 8) {
|
|
||||||
// if (select) {
|
|
||||||
// ME.insert(this, '', select)
|
|
||||||
// ev.preventDefault()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// //编辑器成功加载的回调
|
|
||||||
// vm.$onSuccess(ME.get(), vm)
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
plainTxt: function(val) {
|
plainTxt: function(val) {
|
||||||
this.compile()
|
this.compile()
|
||||||
//只有开启实时预览,才会赋值给htmlTxt
|
//只有开启实时预览,才会赋值给htmlTxt
|
||||||
if (this.preview) {
|
if (this.preview) {
|
||||||
this.htmlTxt = this.$htmlTxt
|
this.htmlTxt = this.__tmp__
|
||||||
}
|
}
|
||||||
if (typeof this.props.onUpdate === 'function') {
|
if (typeof this.props.onUpdate === 'function') {
|
||||||
this.props.onUpdate(this.plainTxt, this.$htmlTxt)
|
this.props.onUpdate(this.plainTxt, this.__tmp__)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
disabled: false, //禁用编辑器
|
disabled: false, //禁用编辑器
|
||||||
fullscreen: false, //是否全屏
|
fullscreen: false, //是否全屏
|
||||||
preview: false, //是否显示预览
|
preview: true, //是否显示预览
|
||||||
// $editor: null, //编辑器元素
|
|
||||||
editorVisible: true,
|
editorVisible: true,
|
||||||
$htmlTxt: '', //临时储存html文本
|
|
||||||
htmlTxt: '', //用于预览渲染
|
htmlTxt: '', //用于预览渲染
|
||||||
plainTxt: '', //纯md文本
|
plainTxt: '', //纯md文本
|
||||||
$safelyCompile: true
|
addon // 已有插件
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
safelyCompile: true,
|
||||||
onSuccess: Anot.PropsTypes.isFunction(),
|
onSuccess: Anot.PropsTypes.isFunction(),
|
||||||
onUpdate: Anot.PropsTypes.isFunction(),
|
onUpdate: Anot.PropsTypes.isFunction(),
|
||||||
onFullscreen: Anot.PropsTypes.isFunction()
|
onFullscreen: Anot.PropsTypes.isFunction()
|
||||||
},
|
},
|
||||||
|
skip: ['addon', 'insert', 'selection'],
|
||||||
methods: {
|
methods: {
|
||||||
|
// 往文本框中插入内容
|
||||||
|
insert(val, isSelect) {
|
||||||
|
let dom = this.$refs.editor
|
||||||
|
if (document.selection) {
|
||||||
|
dom.focus()
|
||||||
|
let range = document.selection.createRange()
|
||||||
|
range.text = val
|
||||||
|
dom.focus()
|
||||||
|
range.moveStart('character', -1)
|
||||||
|
} else if (dom.selectionStart || dom.selectionStart === 0) {
|
||||||
|
let startPos = dom.selectionStart
|
||||||
|
let endPos = dom.selectionEnd
|
||||||
|
let scrollTop = dom.scrollTop
|
||||||
|
|
||||||
|
dom.value =
|
||||||
|
dom.value.slice(0, startPos) +
|
||||||
|
val +
|
||||||
|
dom.value.slice(endPos, dom.value.length)
|
||||||
|
|
||||||
|
dom.selectionStart = isSelect ? startPos : startPos + val.length
|
||||||
|
dom.selectionEnd = startPos + val.length
|
||||||
|
dom.scrollTop = scrollTop
|
||||||
|
dom.focus()
|
||||||
|
} else {
|
||||||
|
dom.value += val
|
||||||
|
dom.focus()
|
||||||
|
}
|
||||||
|
this.plainTxt = dom.value
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* [selection 获取选中的文本]
|
||||||
|
* @param {[type]} dom [要操作的元素]
|
||||||
|
* @param {[type]} forceHoleLine [是否强制光标所在的整行文本]
|
||||||
|
*/
|
||||||
|
selection(forceHoleLine) {
|
||||||
|
let dom = this.$refs.editor
|
||||||
|
if (document.selection) {
|
||||||
|
return document.selection.createRange().text
|
||||||
|
} else {
|
||||||
|
let startPos = dom.selectionStart
|
||||||
|
let endPos = dom.selectionEnd
|
||||||
|
|
||||||
|
if (endPos) {
|
||||||
|
//强制选择整行
|
||||||
|
if (forceHoleLine) {
|
||||||
|
startPos = dom.value.slice(0, startPos).lastIndexOf('\n')
|
||||||
|
|
||||||
|
let tmpEnd = dom.value.slice(endPos).indexOf('\n')
|
||||||
|
tmpEnd = tmpEnd < 0 ? dom.value.slice(endPos).length : tmpEnd
|
||||||
|
|
||||||
|
startPos += 1 // 把\n加上
|
||||||
|
endPos += tmpEnd
|
||||||
|
|
||||||
|
dom.selectionStart = startPos
|
||||||
|
dom.selectionEnd = endPos
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//强制选择整行
|
||||||
|
if (forceHoleLine) {
|
||||||
|
endPos = dom.value.indexOf('\n')
|
||||||
|
endPos = endPos < 0 ? dom.value.length : endPos
|
||||||
|
dom.selectionEnd = endPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dom.focus()
|
||||||
|
return dom.value.slice(startPos, endPos)
|
||||||
|
}
|
||||||
|
},
|
||||||
onToolClick: function(name, ev) {
|
onToolClick: function(name, ev) {
|
||||||
if (ME.addon[name]) {
|
if (this.addon[name]) {
|
||||||
ME.addon[name].call(ME, ev.target, this)
|
this.addon[name].call(this, ev.target)
|
||||||
} else {
|
} else {
|
||||||
console.log('%c没有对应的插件%c[%s]', 'color:#f00;', '', name)
|
console.log('%c没有对应的插件%c[%s]', 'color:#f00;', '', name)
|
||||||
}
|
}
|
||||||
|
@ -464,24 +449,22 @@ Anot.component('meditor', {
|
||||||
html = html2md(html)
|
html = html2md(html)
|
||||||
|
|
||||||
if (html) {
|
if (html) {
|
||||||
ME.insert(ev.target, html)
|
this.insert(html)
|
||||||
} else if (txt) {
|
} else if (txt) {
|
||||||
ME.insert(ev.target, txt)
|
this.insert(txt)
|
||||||
}
|
}
|
||||||
log(ev.target.value)
|
|
||||||
this.plainTxt = this.$refs.editor.value
|
this.plainTxt = this.$refs.editor.value
|
||||||
},
|
},
|
||||||
compile: function() {
|
compile: function() {
|
||||||
log(this)
|
let txt = this.plainTxt.trim()
|
||||||
var txt = this.plainTxt.trim()
|
|
||||||
|
|
||||||
if (this.$safelyCompile) {
|
if (this.props.safelyCompile) {
|
||||||
txt = txt
|
txt = txt
|
||||||
.replace(/<script([^>]*?)>/g, '<script$1>')
|
.replace(/<script([^>]*?)>/g, '<script$1>')
|
||||||
.replace(/<\/script>/g, '</script>')
|
.replace(/<\/script>/g, '</script>')
|
||||||
}
|
}
|
||||||
//只解析,不渲染
|
//只解析,不渲染
|
||||||
this.$htmlTxt = marked(txt)
|
this.__tmp__ = marked(txt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
|
|
||||||
.do-meditor {position:relative;width:100%;height:100%;min-height:180px;padding-top:41px;border:1px solid nth($cp, 3);background:#fff;color:#666;box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
|
.do-meditor {position:relative;width:100%;height:100%;min-height:180px;padding-top:41px;border:1px solid nth($cp, 3);background:#fff;color:#666;box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
|
||||||
|
|
||||||
::-webkit-scrollbar {width:5px;height:5px;background:#ebeeec;}
|
::-webkit-scrollbar {width:5px;height:5px;background:nth($cp, 2);}
|
||||||
::-webkit-scrollbar:hover {background:rgba(0,0,0,.05);}
|
::-webkit-scrollbar:hover {background:nth($cp, 1);}
|
||||||
::-webkit-scrollbar-button {display:none;}
|
::-webkit-scrollbar-button {display:none;}
|
||||||
::-webkit-scrollbar-thumb {background:nth($ct, 1);}
|
::-webkit-scrollbar-thumb {background:nth($cgr, 2);}
|
||||||
::-webkit-scrollbar-thumb:hover {background:nth($ct, 3);}
|
::-webkit-scrollbar-thumb:hover {background:nth($cgr, 1);}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-body{overflow:hidden;overflow-y:auto;float:left;width:100%;height:100%;padding:5px 5px 50px;border:0;outline:none;resize:none;color:#666;background:#f7f8fb;font-size:14px;}
|
.editor-body{overflow:hidden;overflow-y:auto;float:left;width:100%;height:100%;padding:5px 5px 50px;line-height:2;border:0;outline:none;resize:none;color:#666;background:#fff;font-size:14px;}
|
||||||
|
|
||||||
.md-preview {float:right;overflow:hidden;overflow-y:auto;display:block;width:50%;height:100%;padding:10px 10px 50px;line-height:2;border-left:1px solid #ddd;color:#666;font-size:14px;background:#ff0;}
|
.md-preview {float:right;overflow:hidden;overflow-y:auto;display:block;width:50%;height:100%;padding:10px 10px 50px;line-height:2;border-left:1px solid #ddd;color:#666;font-size:14px;background:#fff;}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.do-meditor__button {overflow:hidden;position:relative;display:inline-block;width:auto;min-width:60px;height:40px;margin-left:5px;padding:0 10px;color:nth($ct, 1);text-align:center;
|
.do-meditor__button {overflow:hidden;position:relative;display:inline-block;width:auto;min-width:60px;height:40px;margin-left:5px;padding:0 10px;line-height:40px;color:nth($ct, 1);text-align:center;
|
||||||
|
|
||||||
&::before {position:absolute;left:-50%;top:-50%;z-index:-1;display:block;width:200%;height:200%;border-radius:50%;background:nth($cp, 2); content:"";opacity:0;transform: scale(0, .0); transition:opacity 1.3s cubic-bezier(0.23, 1, 0.32, 1),transform 1.3s cubic-bezier(0.23, 1, 0.32, 1);}
|
&::before {position:absolute;left:-50%;top:-50%;z-index:-1;display:block;width:200%;height:200%;border-radius:50%;background:nth($cp, 2); content:"";opacity:0;transform: scale(0, .0); transition:opacity 1.3s cubic-bezier(0.23, 1, 0.32, 1),transform 1.3s cubic-bezier(0.23, 1, 0.32, 1);}
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -126,30 +126,27 @@
|
||||||
|
|
||||||
section {width:100%;height: 40px;margin:10px 0;line-height:40px;
|
section {width:100%;height: 40px;margin:10px 0;line-height:40px;
|
||||||
|
|
||||||
&.input {border-radius:5px;background:nth($cp, 2);
|
.txt {width:100%;height:40px;padding:0 10px;border:0;border-radius:5px;background:nth($cp, 2);color:nth($cgr, 1);font-size:14px;}
|
||||||
|
.label {float: left;width:50%;}
|
||||||
.txt {width:100%;height:40px;padding:0 10px;border:0;background:none;color:nth($cgr, 1);font-size:14px;}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {float: left;width:30%;text-align:center;background:#f7f7f7;}
|
|
||||||
label {float: left;width:50%;}
|
|
||||||
|
|
||||||
.submit {float:right;width:30%;}
|
.submit {float:right;width:30%;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.do-meditor-codeblock {width:780px;height:auto;padding:15px 20px;background:#fafafa;
|
.do-meditor-codeblock {width:780px;height:auto;
|
||||||
section {display:block;width:100%;height:auto;margin:10px 0;line-height:35px;
|
section {display:block;width:100%;height:auto;margin:10px 0;line-height:35px;
|
||||||
|
|
||||||
.label {float: left;width:80px;}
|
.label {float: left;width:80px;}
|
||||||
select {float:left;width:200px;height:35px;padding:0 30px 0 10px;border:0;border-bottom:1px solid #e7e7e7;background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAMAAABV0m3JAAAADFBMVEUAAAD///+Pj4+JiYkxcGihAAAABHRSTlMAABBwqVQF9wAAADNJREFUeNqlzjEOACAMw8DQ/v/PSE5FFhaEx5usdekBuzRVH0RtCqJYELUFrVjQigX/5jdvzgDh9izlMQAAAABJRU5ErkJggg==) no-repeat right 8px;color:nth($cgr, 1);outline:none;-webkit-appearance:none;-moz-appearance: none;@include ts;
|
select {float:left;width:200px;height:35px;padding:0 30px 0 10px;border:0;border-radius:0;border-bottom:1px solid nth($cp, 3);background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAMAAABV0m3JAAAADFBMVEUAAAD///+Pj4+JiYkxcGihAAAABHRSTlMAABBwqVQF9wAAADNJREFUeNqlzjEOACAMw8DQ/v/PSE5FFhaEx5usdekBuzRVH0RtCqJYELUFrVjQigX/5jdvzgDh9izlMQAAAABJRU5ErkJggg==) no-repeat right 12px;color:nth($cgr, 1);outline:none;-webkit-appearance:none;-moz-appearance: none;@include ts;
|
||||||
|
|
||||||
&::-ms-expand {display:none;}
|
&::-ms-expand {display:none;}
|
||||||
|
|
||||||
&:focus {border-color:nth($ct, 1);}
|
&:focus {box-shadow:0 0 5px nth($ct, 2)}
|
||||||
}
|
}
|
||||||
textarea {width:100%;height:300px;padding:5px 10px;border:1px solid #ddd;background:#fff;resize:none;outline:none;}
|
textarea {width:100%;height:300px;padding:5px 10px;border:0;border-radius:5px;background:nth($cp, 2);font-size:14px;resize:none;outline:none;
|
||||||
.submit {float:right;width:80px;height:35px;background:#ddd;color:#666;text-align:center;}
|
|
||||||
|
&:focus {box-shadow:0 0 5px nth($ct, 2)}
|
||||||
|
}
|
||||||
|
.submit {float:right;width:80px;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue