优化layer组件tips的定位;重构meditor的插件机制
parent
d256f145cf
commit
fead12b0d2
|
@ -22,6 +22,7 @@ let defconf = {
|
|||
background: '#fff',
|
||||
mask: true, // 遮罩
|
||||
maskClose: false, // 遮罩点击关闭弹窗
|
||||
maskColor: null, // 遮罩背景色
|
||||
radius: '0px', // 弹窗圆角半径
|
||||
area: ['auto', 'auto'],
|
||||
title: '提示', // 弹窗主标题(在工具栏上的)
|
||||
|
@ -64,6 +65,7 @@ const close = function(id) {
|
|||
}, 200)
|
||||
} catch (err) {}
|
||||
}
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
|
||||
const repeat = function(str, num) {
|
||||
|
@ -208,6 +210,9 @@ class __layer__ {
|
|||
if (state.mask) {
|
||||
outerBox.classList.add('mask')
|
||||
}
|
||||
if (state.maskColor) {
|
||||
outerBox.style.background = state.maskColor
|
||||
}
|
||||
|
||||
layBox.classList.add('layer-box')
|
||||
layBox.classList.add('skin-' + state.skin)
|
||||
|
@ -373,40 +378,49 @@ class __layer__ {
|
|||
style.color = state.color
|
||||
style.opacity = 1
|
||||
let $container = Anot(container)
|
||||
let $doc = Anot(document)
|
||||
let $arrow = $container[0].querySelector('.arrow')
|
||||
let cw = $container.innerWidth()
|
||||
let ch = $container.innerHeight()
|
||||
let ol = $container.offset().left - document.body.scrollLeft
|
||||
let ot = $container.offset().top - document.body.scrollTop
|
||||
let ol = $container.offset().left - $doc.scrollLeft()
|
||||
let ot = $container.offset().top - $doc.scrollTop()
|
||||
|
||||
let layw = parseInt(css.width)
|
||||
let layh = parseInt(css.height)
|
||||
|
||||
let arrowOffset = ['top']
|
||||
|
||||
Anot(layerDom[$id][1]).css(style)
|
||||
|
||||
$container.bind('mouseenter', ev => {
|
||||
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
|
||||
style.top = ot + ch + 8
|
||||
tmpStyle.top = ot + ch + 8
|
||||
} else {
|
||||
$arrow.style.borderTopColor = state.background
|
||||
style.top = ot - layh - 8
|
||||
tmpStyle.top = ot - layh - 8
|
||||
}
|
||||
|
||||
if (ol + cw * 0.7 + layw > window.innerWidth) {
|
||||
style.left = ol + cw * 0.3 - layw
|
||||
tmpStyle.left = ol + cw * 0.3 - layw
|
||||
arrowOffset[1] = 'left'
|
||||
} else {
|
||||
style.left = ol + cw * 0.7
|
||||
tmpStyle.left = ol + cw * 0.7
|
||||
}
|
||||
|
||||
$arrow.classList.add('offset-' + arrowOffset.join('-'))
|
||||
Anot(layerDom[$id][1]).css(style)
|
||||
$container.bind('mouseenter', ev => {
|
||||
layerDom[$id][1].style.visibility = 'visible'
|
||||
Anot(layerDom[$id][1]).css(tmpStyle)
|
||||
})
|
||||
$container.bind('mouseleave', () => {
|
||||
setTimeout(() => {
|
||||
$arrow.classList.remove('offset-' + arrowOffset.join('-'))
|
||||
arrowOffset = ['top']
|
||||
$arrow.style.borderBottomColor = ''
|
||||
$arrow.style.borderTopColor = ''
|
||||
layerDom[$id][1].style.visibility = 'hidden'
|
||||
}, 100)
|
||||
})
|
||||
|
@ -436,6 +450,7 @@ class __layer__ {
|
|||
Anot(layerDom[$id][1]).css(style)
|
||||
|
||||
setTimeout(() => {
|
||||
document.body.style.overflow = 'hidden'
|
||||
layerDom[$id][1].classList.add('shift')
|
||||
setTimeout(_ => {
|
||||
Anot(layerDom[$id][1]).css(offsetStyle)
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
|
||||
|
||||
/* 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;}
|
||||
i.arrow {position:absolute;width:0;height:0;border:6px solid transparent;content: ""}
|
||||
|
@ -190,7 +190,6 @@
|
|||
|
||||
&.shift {transition: all .5s ease-out;}
|
||||
|
||||
.layer-box {position:absolute;}
|
||||
}
|
||||
&:active {z-index:65536;}
|
||||
}
|
||||
|
|
|
@ -7,110 +7,64 @@
|
|||
|
||||
'use strict'
|
||||
|
||||
define(['lib/layer/base', 'css!./attach'], function() {
|
||||
var Uploader = function(url) {
|
||||
this.url = url || ''
|
||||
this.init()
|
||||
}
|
||||
import 'layer/index'
|
||||
import './attach.scss'
|
||||
|
||||
Uploader.prototype = {
|
||||
init: function() {
|
||||
const $doc = Anot(document)
|
||||
|
||||
class Uploader {
|
||||
constructor(url) {
|
||||
this.url = url
|
||||
this.xhr = new XMLHttpRequest()
|
||||
this.form = new FormData()
|
||||
return this
|
||||
},
|
||||
field: function(key, val) {
|
||||
if (typeof key === 'object') {
|
||||
for (var i in key) {
|
||||
this.form.append(i, key[i])
|
||||
}
|
||||
} else {
|
||||
|
||||
field(key, val) {
|
||||
this.form.append(key, val)
|
||||
}
|
||||
return this
|
||||
},
|
||||
start: function() {
|
||||
var _this = this
|
||||
}
|
||||
onProgress(fn) {
|
||||
this.progress = fn
|
||||
return this
|
||||
}
|
||||
then(cb) {
|
||||
if (!this.url) {
|
||||
Anot.error('invalid upload url')
|
||||
}
|
||||
let defer = Promise.defer()
|
||||
|
||||
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)
|
||||
evt => {
|
||||
if (evt.lengthComputable && this.progress) {
|
||||
let 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
|
||||
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) {}
|
||||
_this.end && _this.end(res)
|
||||
defer.resolve(cb(res))
|
||||
} else {
|
||||
console.error(_this.xhr)
|
||||
defer.reject(this.xhr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.xhr.send(this.form)
|
||||
},
|
||||
onProgress: function(fn) {
|
||||
this.progress = fn
|
||||
return this
|
||||
},
|
||||
onEnd: function(fn) {
|
||||
this.end = fn
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
function uploadScreenshot(vm, blob) {
|
||||
var upload = new Uploader(vm.uploadUrl || ME.uploadUrl)
|
||||
if (ME.beforeUpload) {
|
||||
ME.beforeUpload(Date.now().toString(16) + '.jpg', function(qn) {
|
||||
upload
|
||||
.field('file', blob)
|
||||
.field('token', qn.token)
|
||||
.field('key', qn.key)
|
||||
.onEnd(function(json) {
|
||||
ME.insert(vm.$editor, '![截图](' + qn.url + ')')
|
||||
})
|
||||
.start()
|
||||
})
|
||||
} else {
|
||||
upload
|
||||
.field('file', blob)
|
||||
.onEnd(function(json) {
|
||||
ME.insert(vm.$editor, '![截图](' + json.data.url + ')')
|
||||
})
|
||||
.start()
|
||||
return defer.promise
|
||||
}
|
||||
}
|
||||
|
||||
var $init = function(vm) {
|
||||
if (!vm.uploadUrl && !ME.uploadUrl) {
|
||||
console.error(
|
||||
'使用附件上传,必须先设置uploadUrl;\n可以给vm增加uploadUrl属性,也可以通过ME.uploadUrl设置'
|
||||
)
|
||||
}
|
||||
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) {
|
||||
var txt = ev.clipboardData.getData('text/plain').trim(),
|
||||
html = ev.clipboardData.getData('text/html').trim()
|
||||
|
@ -175,134 +129,192 @@ define(['lib/layer/base', 'css!./attach'], function() {
|
|||
}
|
||||
ev.preventDefault()
|
||||
})
|
||||
},
|
||||
lang = {
|
||||
image: ['远程图片', '图片管理', '图片描述', '图片地址'],
|
||||
file: ['远程附件', '附件管理', '附件描述', '附件地址']
|
||||
},
|
||||
opened = false, //记录是否已经打开
|
||||
openType = 'image', //打开类型, 图片/附件
|
||||
cache = {
|
||||
}
|
||||
|
||||
let 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>'
|
||||
)
|
||||
}
|
||||
|
||||
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]} files [文件列表]
|
||||
* @param {[type]} vm [vm对象]
|
||||
* @param {[type]} type [image/file]
|
||||
* @param {[type]} tool [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({
|
||||
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>',
|
||||
progress: '<span class="red">0%(文件类型错误)</span>',
|
||||
url: ''
|
||||
})
|
||||
continue
|
||||
}
|
||||
if (ME.maxSize > 0 && it.size > ME.maxSize) {
|
||||
vm.uploadFile.push({
|
||||
if (vm.props.uploadSizeLimit && it.size > vm.props.uploadSizeLimit) {
|
||||
this.uploadQueue.push({
|
||||
name: it.name,
|
||||
progress: '<span class="red">0%(文件体积过大)</span>',
|
||||
url: ''
|
||||
})
|
||||
continue
|
||||
}
|
||||
var fixName =
|
||||
Date.now().toString(16) + it.name.slice(it.name.lastIndexOf('.'))
|
||||
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)
|
||||
|
||||
var idx = vm.uploadFile.length,
|
||||
upload = new Uploader(vm.uploadUrl || ME.uploadUrl)
|
||||
this.uploadQueue.push(attach)
|
||||
|
||||
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 + '%'
|
||||
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 + '%'
|
||||
})
|
||||
.onEnd(function(json) {
|
||||
vm.uploadFile[idx].url = qn.url
|
||||
.then(res => {
|
||||
return res.data
|
||||
})
|
||||
.start()
|
||||
})
|
||||
.then(data => {
|
||||
this.uploadQueue[idx].url = data.url
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
} else {
|
||||
upload
|
||||
.onProgress(function(val) {
|
||||
vm.uploadFile[idx].progress = val + '%'
|
||||
.onProgress(val => {
|
||||
this.uploadQueue[idx].progress = val + '%'
|
||||
})
|
||||
.onEnd(function(json) {
|
||||
vm.uploadFile[idx].url = json.data.url
|
||||
.then(res => {
|
||||
return res.data
|
||||
})
|
||||
.then(data => {
|
||||
this.uploadQueue[idx].url = data.url
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadScreenshot(vm, blob) {
|
||||
let name = new Date().format('YmdHis') + '.jpg'
|
||||
let attach = { name, url: '' }
|
||||
let upload = new Uploader(vm.props.uploadUrl).field('file', blob)
|
||||
|
||||
if (vm.props.beforeUpload) {
|
||||
vm.props
|
||||
.beforeUpload(attach, upload)
|
||||
.then(upload => {
|
||||
return upload.then(res => {
|
||||
return res.data
|
||||
})
|
||||
})
|
||||
.then(data => {
|
||||
vm.insert(`![截图](${data.url})`)
|
||||
})
|
||||
} else {
|
||||
upload
|
||||
.then(res => {
|
||||
return res.data
|
||||
})
|
||||
.then(data => {
|
||||
vm.insert(`![截图](${data.url})`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getAttach(vm, cb) {
|
||||
var xhr = new XMLHttpRequest(),
|
||||
url = vm.manageUrl || ME.manageUrl
|
||||
|
@ -334,105 +346,80 @@ define(['lib/layer/base', 'css!./attach'], function() {
|
|||
xhr.send()
|
||||
}
|
||||
|
||||
function showDialog(elem, vm) {
|
||||
opened = true
|
||||
var offset = yua(elem).offset(),
|
||||
layid = layer.open({
|
||||
function showDialog(elem, vm, tool) {
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
shade: false,
|
||||
fixed: true,
|
||||
offset: [offset.top + 37 - ME.doc.scrollTop()],
|
||||
offset: [offset.top + 37 - $doc.scrollTop()],
|
||||
tab: 2,
|
||||
attach: '',
|
||||
attachAlt: '',
|
||||
uploadFile: [], //当前上传的列表
|
||||
uploadQueue: [], //当前上传的列表
|
||||
attachList: [], //附件管理列表
|
||||
$switch: function(id) {
|
||||
var lvm = yua.vmodels[layid]
|
||||
|
||||
lvm.tab = id
|
||||
switchTab: function(id) {
|
||||
this.tab = id
|
||||
if (id === 3) {
|
||||
lvm.attachList.clear()
|
||||
if (cache[openType].length) {
|
||||
lvm.attachList = cache[openType]
|
||||
this.attachList.clear()
|
||||
if (cache[tool].length) {
|
||||
this.attachList = cache[tool]
|
||||
} else {
|
||||
getAttach(vm, function(json) {
|
||||
if (json) {
|
||||
cache[openType] = json.data.list.map(function(it) {
|
||||
cache[tool] = json.data.list.map(function(it) {
|
||||
it.thumb =
|
||||
openType === 'image'
|
||||
tool === 'image'
|
||||
? '<img src="' + it.url + '"/>'
|
||||
: '<em class="attach-icon"></em>'
|
||||
return it
|
||||
})
|
||||
lvm.attachList = json.data.list
|
||||
this.attachList = json.data.list
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
$select: yua.noop,
|
||||
$change: yua.noop,
|
||||
$insert: function(it) {
|
||||
select() {
|
||||
let ev = document.createEvent('MouseEvent')
|
||||
ev.initEvent('click', false, false)
|
||||
this.$refs.attach.dispatchEvent(ev)
|
||||
},
|
||||
change(ev) {
|
||||
this.files = ev.target.files
|
||||
uploadFile.call(this, vm, tool)
|
||||
},
|
||||
insert: function(it) {
|
||||
if (!it.url) {
|
||||
return
|
||||
}
|
||||
var val =
|
||||
(openType === 'image' ? '!' : '') +
|
||||
'[' +
|
||||
it.name +
|
||||
'](' +
|
||||
it.url +
|
||||
')'
|
||||
ME.insert(vm.$editor, val)
|
||||
let val = `\n${tool === 'image' ? '!' : ''}[${it.name}](${it.url})`
|
||||
vm.insert(val)
|
||||
},
|
||||
$confirm: function() {
|
||||
var lvm = yua.vmodels[layid]
|
||||
if (!lvm.attach || !lvm.attachAlt) {
|
||||
return layer.alert('描述和地址不能为空')
|
||||
confirm: function() {
|
||||
if (!this.attach || !this.attachAlt) {
|
||||
return layer.toast('描述和地址不能为空', 'error')
|
||||
}
|
||||
var val = '![' + lvm.attachAlt + '](' + lvm.attach + ')'
|
||||
let val = `\n${tool === 'image' ? '!' : ''}[${this.attachAlt}](${
|
||||
this.attach
|
||||
})`
|
||||
|
||||
ME.insert(vm.$editor, val)
|
||||
lvm.no()
|
||||
vm.insert(val)
|
||||
this.close()
|
||||
},
|
||||
success: function(id) {
|
||||
var _this = yua.vmodels[id],
|
||||
$file = document.body.querySelector('#meditor-attch')
|
||||
|
||||
_this.no = function() {
|
||||
layer.close(id)
|
||||
opened = false
|
||||
}
|
||||
_this.$select = function() {
|
||||
var ev = document.createEvent('MouseEvent')
|
||||
ev.initEvent('click', false, false)
|
||||
$file.dispatchEvent(ev)
|
||||
}
|
||||
_this.$change = function() {
|
||||
uploadFile(this.files, _this)
|
||||
}
|
||||
},
|
||||
content: fixCont()
|
||||
content: fixCont(vm, tool)
|
||||
})
|
||||
}
|
||||
|
||||
ME.addon.image = function(elem, vm) {
|
||||
if (opened) {
|
||||
return
|
||||
const addon = {
|
||||
attach(elem) {
|
||||
showDialog(elem, this, 'file')
|
||||
},
|
||||
image(elem) {
|
||||
showDialog(elem, this, 'image')
|
||||
}
|
||||
openType = 'image'
|
||||
showDialog(elem, vm)
|
||||
}
|
||||
|
||||
ME.addon.file = function(elem, vm) {
|
||||
if (opened) {
|
||||
return
|
||||
}
|
||||
openType = 'file'
|
||||
showDialog(elem, vm)
|
||||
}
|
||||
|
||||
return $init
|
||||
})
|
||||
export default addon
|
||||
|
|
|
@ -5,50 +5,68 @@
|
|||
* @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;
|
||||
|
||||
.item {position:relative;float:left;width:100px;height:49px;border-right:1px solid #ddd;cursor:pointer;}
|
||||
.item.active {background:#fff;}
|
||||
.item.active::after {position:absolute;left:0;bottom:-1px;width:100%;height:1px;background:#fff;content:""}
|
||||
a.action-close {top:5px;width:40px;height:40px;line-height:40px;font-size:20px;}
|
||||
a.action-close:hover {line-height:40px;border:0;}
|
||||
&:hover {color:nth($ct, 1);font-size:28px;}
|
||||
}
|
||||
|
||||
.cont-box {position:relative;width:100%;height:auto;min-height:200px;background:#fff;
|
||||
.tab-box {float:left;width:130px;height:300px;padding:10px 5px;text-align:center;background:nth($cp, 2);border-radius:5px;
|
||||
|
||||
.item {display:block;width:100%;height:40px;line-height:40px;border-radius:3px;cursor:pointer;
|
||||
|
||||
&.active {background:#fff;}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.cont-box {position:relative;float:right;width:480px;height:auto;min-height:200px;
|
||||
|
||||
.remote,
|
||||
.local {position:relative;width:60%;height:auto;margin:0 auto;padding:15px 0 30px;}
|
||||
.local {width:96%;}
|
||||
.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;
|
||||
|
||||
&.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;}
|
||||
.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;}
|
||||
}
|
||||
|
||||
|
||||
.select-file {width:100%;height:35px;line-height:35px;
|
||||
|
||||
.file {float:left;width:100px;height:35px;border-radius:3px;background:nth($cp, 1);text-align:center;cursor:pointer;
|
||||
|
||||
&:hover {background:nth($cp, 2);}
|
||||
&:active {background:nth($cp, 3);}
|
||||
}
|
||||
.tips {display:inline-block;padding:0 10px;}
|
||||
}
|
||||
|
||||
.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;}
|
||||
}
|
||||
|
||||
|
||||
.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;}
|
||||
|
|
|
@ -20,32 +20,33 @@ function trim(str, sign) {
|
|||
return str.replace(new RegExp('^' + sign + '|' + sign + '$', 'g'), '')
|
||||
}
|
||||
|
||||
const $doc = Anot(document)
|
||||
|
||||
const addon = {
|
||||
h1: function(elem, vm) {
|
||||
h1: function(elem) {
|
||||
let that = this
|
||||
let editor = vm.$refs.editor
|
||||
let offset = Anot(elem).offset()
|
||||
let wrap = this.selection(editor, true) || '在此输入文本'
|
||||
let wrap = this.selection(true) || '在此输入文本'
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
maskColor: 'rgba(255,255,255,0)',
|
||||
fixed: true,
|
||||
insert: function(level) {
|
||||
wrap = wrap.replace(/^#{1,6} /, '')
|
||||
wrap = that.repeat('#', level) + ' ' + wrap
|
||||
that.insert(editor, wrap, true)
|
||||
wrap = wrap.replace(/^(#{1,6} )?/, '#'.repeat(level) + ' ')
|
||||
that.insert(wrap, true)
|
||||
this.close()
|
||||
},
|
||||
offset: [
|
||||
offset.top + 40 - that.doc.scrollTop(),
|
||||
offset.top + 40 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - that.doc.scrollLeft()
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - that.doc.scrollTop(),
|
||||
left: offset.left - that.doc.scrollLeft()
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<ul class="do-meditor-h1 do-fn-noselect do-meditor__font">
|
||||
|
@ -58,60 +59,61 @@ const addon = {
|
|||
</ul>`
|
||||
})
|
||||
},
|
||||
quote: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
quote: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
wrap = '> ' + wrap
|
||||
|
||||
this.insert(vm.$refs.editor, wrap, true)
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
bold: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
bold: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
let wraped = trim(wrap, '\\*\\*')
|
||||
|
||||
wrap = wrap === wraped ? '**' + wrap + '**' : wraped
|
||||
|
||||
this.insert(vm.$refs.editor, wrap, true)
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
italic: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
italic: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
let wraped = trim(wrap, '_')
|
||||
|
||||
wrap = wrap === wraped ? '_' + wrap + '_' : wraped
|
||||
|
||||
this.insert(vm.$refs.editor, wrap, true)
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
through: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
through: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
let wraped = trim(wrap, '~~')
|
||||
|
||||
wrap = wrap === wraped ? '~~' + wrap + '~~' : wraped
|
||||
|
||||
this.insert(vm.$refs.editor, wrap, true)
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
unordered: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
unordered: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
wrap = '* ' + wrap
|
||||
|
||||
this.insert(vm.$refs.editor, wrap, false)
|
||||
this.insert(wrap, false)
|
||||
},
|
||||
ordered: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
ordered: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
wrap = '1. ' + wrap
|
||||
|
||||
this.insert(vm.$refs.editor, wrap, false)
|
||||
this.insert(wrap, false)
|
||||
},
|
||||
hr: function(elem, vm) {
|
||||
this.insert(vm.$refs.editor, '\n\n---\n\n', false)
|
||||
hr: function(elem) {
|
||||
this.insert('\n\n---\n\n', false)
|
||||
},
|
||||
link: function(elem, vm) {
|
||||
link: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
let wrap = this.selection(vm.$refs.editor) || ''
|
||||
let wrap = this.selection() || ''
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
maskColor: 'rgba(255,255,255,0)',
|
||||
fixed: true,
|
||||
link: '',
|
||||
linkName: wrap,
|
||||
|
@ -124,29 +126,29 @@ const addon = {
|
|||
this.linkTarget === 1 ? ' "target=_blank"' : ''
|
||||
})`
|
||||
|
||||
that.insert(vm.$refs.editor, val, false)
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
},
|
||||
offset: [
|
||||
offset.top + 40 - that.doc.scrollTop(),
|
||||
offset.top + 40 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - that.doc.scrollLeft()
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - that.doc.scrollTop(),
|
||||
left: offset.left - that.doc.scrollLeft()
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<div class="do-meditor-common do-meditor__font">
|
||||
<section class="input">
|
||||
<section>
|
||||
<input class="txt" :duplex="linkName" placeholder="链接文字"/>
|
||||
</section>
|
||||
<section class="input">
|
||||
<section>
|
||||
<input class="txt" :duplex="link" placeholder="链接地址"/>
|
||||
</section>
|
||||
<section>
|
||||
<label>
|
||||
<label class="label">
|
||||
<input
|
||||
name="link"
|
||||
type="radio"
|
||||
|
@ -155,7 +157,7 @@ const addon = {
|
|||
value="1"/>
|
||||
新窗口打开
|
||||
</label>
|
||||
<label>
|
||||
<label class="label">
|
||||
<input
|
||||
name="link"
|
||||
type="radio"
|
||||
|
@ -174,10 +176,10 @@ const addon = {
|
|||
</div>`
|
||||
})
|
||||
},
|
||||
time: function(elem, vm) {
|
||||
this.insert(vm.$refs.editor, new Date().format(), false)
|
||||
time: function(elem) {
|
||||
this.insert(new Date().format(), false)
|
||||
},
|
||||
face: function(elem, vm) {
|
||||
face: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
|
@ -186,6 +188,7 @@ const addon = {
|
|||
title: '插入表情',
|
||||
fixed: true,
|
||||
maskClose: true,
|
||||
maskColor: 'rgba(255,255,255,0)',
|
||||
arr: [
|
||||
'😀',
|
||||
'😅',
|
||||
|
@ -225,14 +228,14 @@ const addon = {
|
|||
'🙏'
|
||||
],
|
||||
offset: [
|
||||
offset.top + 40 - that.doc.scrollTop(),
|
||||
offset.top + 40 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - that.doc.scrollLeft()
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - that.doc.scrollTop(),
|
||||
left: offset.left - that.doc.scrollLeft()
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<ul class="do-meditor-face">
|
||||
|
@ -241,12 +244,12 @@ const addon = {
|
|||
</li>
|
||||
</ul>`,
|
||||
insert: function(val) {
|
||||
that.insert(vm.$refs.editor, val, false)
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
},
|
||||
table: function(elem, vm) {
|
||||
table: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
|
@ -255,15 +258,16 @@ const addon = {
|
|||
title: '0行 x 0列',
|
||||
fixed: true,
|
||||
maskClose: true,
|
||||
maskColor: 'rgba(255,255,255,0)',
|
||||
offset: [
|
||||
offset.top + 40 - that.doc.scrollTop(),
|
||||
offset.top + 40 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - that.doc.scrollLeft()
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - that.doc.scrollTop(),
|
||||
left: offset.left - that.doc.scrollLeft()
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
matrix: objArr(10).map(function() {
|
||||
return objArr(10)
|
||||
|
@ -313,26 +317,27 @@ const addon = {
|
|||
let x = ev.target.dataset.x - 0 + 1
|
||||
let y = ev.target.dataset.y - 0 + 1
|
||||
|
||||
let thead = `\n\n${that.repeat('| 表头 ', x)}|\n`
|
||||
let pipe = `${that.repeat('| -- ', x)}|\n`
|
||||
let tbody = that.repeat(that.repeat('| ', x) + '|\n', y)
|
||||
let thead = `\n\n${'| 表头 '.repeat(x)}|\n`
|
||||
let pipe = `${'| -- '.repeat(x)}|\n`
|
||||
let tbody = ('| '.repeat(x) + '|\n').repeat(y)
|
||||
|
||||
that.insert(vm.$refs.editor, thead + pipe + tbody, false)
|
||||
that.insert(thead + pipe + tbody, false)
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
image: function(elem, vm) {
|
||||
image: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
let wrap = this.selection(vm.$refs.editor) || ''
|
||||
let wrap = this.selection() || ''
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
maskColor: 'rgba(255,255,255,0)',
|
||||
fixed: true,
|
||||
img: '',
|
||||
imgAlt: wrap,
|
||||
|
@ -342,25 +347,25 @@ const addon = {
|
|||
}
|
||||
let val = `![${this.imgAlt}](${this.img})`
|
||||
|
||||
that.insert(vm.$refs.editor, val, false)
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
},
|
||||
offset: [
|
||||
offset.top + 40 - that.doc.scrollTop(),
|
||||
offset.top + 40 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - that.doc.scrollLeft()
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - that.doc.scrollTop(),
|
||||
left: offset.left - that.doc.scrollLeft()
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<div class="do-meditor-common do-meditor__font">
|
||||
<section class="input">
|
||||
<section>
|
||||
<input class="txt" :duplex="imgAlt" placeholder="图片描述"/>
|
||||
</section>
|
||||
<section class="input">
|
||||
<section>
|
||||
<input class="txt" :duplex="img" placeholder="图片地址"/>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -373,17 +378,17 @@ const addon = {
|
|||
`
|
||||
})
|
||||
},
|
||||
attach: function(elem, vm) {
|
||||
this.addon.link.call(this, elem, vm, false)
|
||||
attach: function(elem) {
|
||||
this.addon.link.call(this, elem)
|
||||
},
|
||||
inlinecode: function(elem, vm) {
|
||||
let wrap = this.selection(vm.$refs.editor) || '在此输入文本'
|
||||
inlinecode: function(elem) {
|
||||
let wrap = this.selection() || '在此输入文本'
|
||||
let wraped = trim(wrap, '`')
|
||||
|
||||
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
|
||||
layer.open({
|
||||
type: 7,
|
||||
|
@ -426,48 +431,55 @@ const addon = {
|
|||
],
|
||||
lang: 'javascript',
|
||||
code: '',
|
||||
$confirm: function() {
|
||||
var lvm = Anot.vmodels[layid]
|
||||
var val =
|
||||
'\n```' + lvm.lang + '\n' + (lvm.code || '//在此输入代码') + '\n```\n'
|
||||
|
||||
that.insert(vm.$refs.editor, val, false)
|
||||
layer.close(layid)
|
||||
maskClose: true,
|
||||
insert: function() {
|
||||
let val = `\n\`\`\`${this.lang}\n${this.code ||
|
||||
'// 在此输入代码'}\n\`\`\`\n`
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
},
|
||||
content:
|
||||
'<div class="do-meditor-codeblock do-meditor__font">' +
|
||||
'<section class="do-fn-cl"><span class="label">语言类型</span>' +
|
||||
'<select :duplex="lang">' +
|
||||
'<option :repeat="$lang" :attr-value="el.id">{{el.name || el.id}}</option>' +
|
||||
'</select>' +
|
||||
'</section>' +
|
||||
'<section>' +
|
||||
'<textarea :duplex="code" placeholder="在这里输入/粘贴代码"></textarea>' +
|
||||
'</section>' +
|
||||
'<section class="do-fn-cl">' +
|
||||
'<a href="javascript:;" class="submit" :click="$confirm">确定</a>' +
|
||||
'</section>' +
|
||||
'</div>'
|
||||
content: `
|
||||
<div class="do-meditor-codeblock do-meditor__font">
|
||||
<section class="do-fn-cl">
|
||||
<span class="label">语言类型</span>
|
||||
<select :duplex="lang">
|
||||
<option :repeat="$lang" :attr-value="el.id">{{el.name || el.id}}</option>
|
||||
</select>
|
||||
</section>
|
||||
<section>
|
||||
<textarea :duplex="code" placeholder="在这里输入/粘贴代码"></textarea>
|
||||
</section>
|
||||
<section class="do-fn-cl">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="do-meditor__button submit"
|
||||
:click="insert">确定</a>
|
||||
</section>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
},
|
||||
preview: function(elem, vm) {
|
||||
vm.preview = !vm.preview
|
||||
if (vm.preview) {
|
||||
vm.htmlTxt = vm.$htmlTxt
|
||||
preview: function() {
|
||||
this.preview = !this.preview
|
||||
if (this.preview) {
|
||||
this.htmlTxt = this.__tmp__
|
||||
}
|
||||
},
|
||||
fullscreen: function(elem, vm) {
|
||||
vm.fullscreen = !vm.fullscreen
|
||||
vm.$onFullscreen(vm.fullscreen)
|
||||
fullscreen: function() {
|
||||
this.fullscreen = !this.fullscreen
|
||||
if (typeof this.props.onFullscreen === 'function') {
|
||||
this.props.onFullscreen(this.fullscreen)
|
||||
}
|
||||
},
|
||||
about: function(elem) {
|
||||
var offset = Anot(elem).offset()
|
||||
let offset = Anot(elem).offset()
|
||||
layer.open({
|
||||
type: 7,
|
||||
title: '关于编辑器',
|
||||
maskClose: true,
|
||||
offset: [offset.top + 37 - this.doc.scrollTop()],
|
||||
shift: { top: offset.top - this.doc.scrollTop() },
|
||||
maskColor: 'rgba(255,255,255,0)',
|
||||
offset: [offset.top + 37 - $doc.scrollTop()],
|
||||
shift: { top: offset.top - $doc.scrollTop() },
|
||||
content:
|
||||
'<div class="do-meditor-about do-meditor__font">' +
|
||||
'<pre>' +
|
||||
|
@ -477,7 +489,7 @@ const addon = {
|
|||
'| | | | |__| (_| | | || (_) | |\n' +
|
||||
'|_| |_|_____\\__,_|_|\\__\\___/|_| ' +
|
||||
'v' +
|
||||
this.version +
|
||||
Anot.ui.meditor +
|
||||
'</pre>' +
|
||||
'<p>开源在线Markdown编辑器</p>' +
|
||||
'<p><a target="_blank" href="https://doui.cc/product/meditor">https://doui.cc/product/meditor</a></p>' +
|
||||
|
|
|
@ -17,14 +17,21 @@ marked.setOptions({
|
|||
return Prism.highlight(code, Prism.languages[lang])
|
||||
}
|
||||
})
|
||||
let editorVM = []
|
||||
if (!String.prototype.repeat) {
|
||||
String.prototype.repeat = function(num) {
|
||||
let result = ''
|
||||
while (num > 0) {
|
||||
result += this
|
||||
num--
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Anot.ui.meditor = '1.0.0'
|
||||
const log = console.log
|
||||
//存放编辑器公共静态资源
|
||||
window.ME = {
|
||||
version: Anot.ui.meditor,
|
||||
// 工具栏title
|
||||
toolbar: {
|
||||
const TOOLBAR = {
|
||||
pipe: '',
|
||||
h1: '标题',
|
||||
quote: '引用文本',
|
||||
|
@ -45,130 +52,36 @@ window.ME = {
|
|||
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
|
||||
}
|
||||
const DEFAULT_TOOLBAR = [
|
||||
'h1',
|
||||
'quote',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'through',
|
||||
'|',
|
||||
'unordered',
|
||||
'ordered',
|
||||
'|',
|
||||
'hr',
|
||||
'link',
|
||||
'time',
|
||||
'face',
|
||||
'|',
|
||||
'table',
|
||||
'image',
|
||||
'attach',
|
||||
'inlinecode',
|
||||
'blockcode',
|
||||
'|',
|
||||
'preview',
|
||||
'fullscreen',
|
||||
'|',
|
||||
'about'
|
||||
]
|
||||
|
||||
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()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* [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) {
|
||||
return inner ? '\n' + inner + '\n' : ''
|
||||
},
|
||||
br: '\n',
|
||||
'h([1-6])': function(str, level, attr, inner) {
|
||||
var h = ME.repeat('#', level)
|
||||
return '\n' + h + ' ' + inner + '\n'
|
||||
},
|
||||
hr: '\n\n___\n\n',
|
||||
const ELEMS = {
|
||||
a: function(str, attr, inner) {
|
||||
let href = attr.match(attrExp('href'))
|
||||
let title = attr.match(attrExp('title'))
|
||||
|
@ -212,18 +125,27 @@ var elems = {
|
|||
alt = (alt && alt[1]) || ''
|
||||
|
||||
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) {
|
||||
return new RegExp(field + '\\s?=\\s?["\']?([^"\']*)["\']?', 'i')
|
||||
function attrExp(field, flag = 'i') {
|
||||
return new RegExp(field + '\\s?=\\s?["\']?([^"\']*)["\']?', flag)
|
||||
}
|
||||
function tagExp(tag, open) {
|
||||
var exp = ''
|
||||
if (['br', 'hr', 'img'].indexOf(tag) > -1) {
|
||||
exp = '<' + tag + '([^>]*)\\/?>'
|
||||
exp = '<' + tag + '([^>]*?)\\/?>'
|
||||
} else {
|
||||
exp = '<' + tag + '([^>]*)>([\\s\\S]*?)<\\/' + tag + '>'
|
||||
exp = '<' + tag + '([^>]*?)>([\\s\\S]*?)<\\/' + tag + '>'
|
||||
}
|
||||
return new RegExp(exp, 'gi')
|
||||
}
|
||||
|
@ -233,14 +155,20 @@ function html2md(str) {
|
|||
} catch (err) {}
|
||||
|
||||
str = str.replace(/\t/g, ' ').replace(/<meta [^>]*>/, '')
|
||||
str = str.replace(
|
||||
/<(div|span|dl|dd|dt|table|tr|td|thead|tbody|i|em|strong|h[1-6]|ul|ol|li) [^>]*>/g,
|
||||
str = str
|
||||
.replace(attrExp('class', 'g'), '')
|
||||
.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) {
|
||||
var cb = elems[i],
|
||||
exp = tagExp(i)
|
||||
// log(str)
|
||||
for (let i in ELEMS) {
|
||||
let cb = ELEMS[i]
|
||||
let exp = tagExp(i)
|
||||
|
||||
if (i === 'blockquote') {
|
||||
while (str.match(exp)) {
|
||||
|
@ -250,6 +178,11 @@ function html2md(str) {
|
|||
str = str.replace(exp, cb)
|
||||
}
|
||||
|
||||
// 对另外3种同类标签做一次处理
|
||||
if (i === 'p') {
|
||||
exp = tagExp('div')
|
||||
str = str.replace(exp, cb)
|
||||
}
|
||||
if (i === 'em') {
|
||||
exp = tagExp('i')
|
||||
str = str.replace(exp, cb)
|
||||
|
@ -259,23 +192,23 @@ function html2md(str) {
|
|||
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)) {
|
||||
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,
|
||||
t,
|
||||
inner
|
||||
) {
|
||||
var li = inner.split('</li>')
|
||||
let li = inner.split('</li>')
|
||||
li.pop()
|
||||
|
||||
for (var i = 0, len = li.length; i < len; i++) {
|
||||
var pre = t === 'ol' ? i + 1 + '. ' : '* '
|
||||
for (let i = 0, len = li.length; i < len; i++) {
|
||||
let pre = t === 'ol' ? i + 1 + '. ' : '* '
|
||||
li[i] =
|
||||
pre +
|
||||
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 ')
|
||||
return n
|
||||
})
|
||||
|
@ -287,6 +220,7 @@ function html2md(str) {
|
|||
})
|
||||
}
|
||||
str = str
|
||||
|
||||
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
||||
.replace(/```([\w\W]*)```/g, function(str, inner) {
|
||||
inner = inner
|
||||
|
@ -298,52 +232,50 @@ function html2md(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) {
|
||||
name = (name + '').trim().toLowerCase()
|
||||
name = '|' === name ? 'pipe' : name
|
||||
return (
|
||||
'<span title="' +
|
||||
ME.toolbar[name] +
|
||||
'" class="do-meditor__icon icon-' +
|
||||
name +
|
||||
'" ' +
|
||||
(name !== 'pipe' ? ':click="onToolClick(\'' + name + '\', $event)"' : '') +
|
||||
'></span>'
|
||||
)
|
||||
let event = name === 'pipe' ? '' : `:click="onToolClick('${name}', $event)"`
|
||||
let title = TOOLBAR[name]
|
||||
return `
|
||||
<span title="${title}" class="do-meditor__icon icon-${name}" ${event}></span>`
|
||||
}
|
||||
|
||||
class MEObject {
|
||||
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', {
|
||||
construct: function(props, state) {
|
||||
if (props.hasOwnProperty('$show')) {
|
||||
state.editorVisible = props.$show
|
||||
delete props.$show
|
||||
}
|
||||
},
|
||||
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
|
||||
|
||||
|
@ -361,97 +293,150 @@ Anot.component('meditor', {
|
|||
:duplex="plainTxt"
|
||||
:on-paste="onPaste($event)"></textarea>
|
||||
<content
|
||||
ref="preview"
|
||||
class="md-preview do-marked-theme"
|
||||
:visible="preview"
|
||||
:html="htmlTxt"></content>
|
||||
</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) {
|
||||
console.log(this)
|
||||
// vm.$editor = elem.children[1]
|
||||
let $editor = Anot(this.$refs.editor)
|
||||
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)
|
||||
// //自动加载额外的插件
|
||||
// require(extraAddons, function() {
|
||||
// var args = Array.prototype.slice.call(arguments, 0)
|
||||
// args.forEach(function(addon) {
|
||||
// addon && addon(vm)
|
||||
// })
|
||||
// })
|
||||
$editor.bind('scroll', ev => {
|
||||
let syncTop =
|
||||
ev.target.scrollTop / ev.target.scrollHeight * preview.scrollHeight
|
||||
|
||||
// Anot(vm.$editor).bind('keydown', function(ev) {
|
||||
// var wrap = ME.selection(vm.$editor) || '',
|
||||
// select = !!wrap
|
||||
// //tab键改为插入2个空格,阻止默认事件,防止焦点失去
|
||||
// if (ev.keyCode === 9) {
|
||||
// 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)
|
||||
preview.scrollTop = syncTop
|
||||
})
|
||||
//编辑器成功加载的回调
|
||||
if (typeof this.props.onCreated === 'function') {
|
||||
this.props.onCreated(new MEObject(this))
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
plainTxt: function(val) {
|
||||
this.compile()
|
||||
//只有开启实时预览,才会赋值给htmlTxt
|
||||
if (this.preview) {
|
||||
this.htmlTxt = this.$htmlTxt
|
||||
this.htmlTxt = this.__tmp__
|
||||
}
|
||||
if (typeof this.props.onUpdate === 'function') {
|
||||
this.props.onUpdate(this.plainTxt, this.$htmlTxt)
|
||||
this.props.onUpdate(this.plainTxt, this.__tmp__)
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
disabled: false, //禁用编辑器
|
||||
fullscreen: false, //是否全屏
|
||||
preview: false, //是否显示预览
|
||||
// $editor: null, //编辑器元素
|
||||
preview: true, //是否显示预览
|
||||
editorVisible: true,
|
||||
$htmlTxt: '', //临时储存html文本
|
||||
htmlTxt: '', //用于预览渲染
|
||||
plainTxt: '', //纯md文本
|
||||
$safelyCompile: true
|
||||
addon // 已有插件
|
||||
},
|
||||
props: {
|
||||
safelyCompile: true,
|
||||
onSuccess: Anot.PropsTypes.isFunction(),
|
||||
onUpdate: Anot.PropsTypes.isFunction(),
|
||||
onFullscreen: Anot.PropsTypes.isFunction()
|
||||
},
|
||||
skip: ['addon', 'insert', 'selection'],
|
||||
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) {
|
||||
if (ME.addon[name]) {
|
||||
ME.addon[name].call(ME, ev.target, this)
|
||||
if (this.addon[name]) {
|
||||
this.addon[name].call(this, ev.target)
|
||||
} else {
|
||||
console.log('%c没有对应的插件%c[%s]', 'color:#f00;', '', name)
|
||||
}
|
||||
|
@ -464,24 +449,22 @@ Anot.component('meditor', {
|
|||
html = html2md(html)
|
||||
|
||||
if (html) {
|
||||
ME.insert(ev.target, html)
|
||||
this.insert(html)
|
||||
} else if (txt) {
|
||||
ME.insert(ev.target, txt)
|
||||
this.insert(txt)
|
||||
}
|
||||
log(ev.target.value)
|
||||
this.plainTxt = this.$refs.editor.value
|
||||
},
|
||||
compile: function() {
|
||||
log(this)
|
||||
var txt = this.plainTxt.trim()
|
||||
let txt = this.plainTxt.trim()
|
||||
|
||||
if (this.$safelyCompile) {
|
||||
if (this.props.safelyCompile) {
|
||||
txt = txt
|
||||
.replace(/<script([^>]*?)>/g, '<script$1>')
|
||||
.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);
|
||||
|
||||
::-webkit-scrollbar {width:5px;height:5px;background:#ebeeec;}
|
||||
::-webkit-scrollbar:hover {background:rgba(0,0,0,.05);}
|
||||
::-webkit-scrollbar {width:5px;height:5px;background:nth($cp, 2);}
|
||||
::-webkit-scrollbar:hover {background:nth($cp, 1);}
|
||||
::-webkit-scrollbar-button {display:none;}
|
||||
::-webkit-scrollbar-thumb {background:nth($ct, 1);}
|
||||
::-webkit-scrollbar-thumb:hover {background:nth($ct, 3);}
|
||||
::-webkit-scrollbar-thumb {background:nth($cgr, 2);}
|
||||
::-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);}
|
||||
&:hover {
|
||||
|
@ -126,30 +126,27 @@
|
|||
|
||||
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;background:none;color:nth($cgr, 1);font-size:14px;}
|
||||
}
|
||||
|
||||
.label {float: left;width:30%;text-align:center;background:#f7f7f7;}
|
||||
label {float: left;width:50%;}
|
||||
|
||||
.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%;}
|
||||
.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;
|
||||
|
||||
.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;}
|
||||
|
||||
&: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;}
|
||||
.submit {float:right;width:80px;height:35px;background:#ddd;color:#666;text-align:center;}
|
||||
textarea {width:100%;height:300px;padding:5px 10px;border:0;border-radius:5px;background:nth($cp, 2);font-size:14px;resize:none;outline:none;
|
||||
|
||||
&:focus {box-shadow:0 0 5px nth($ct, 2)}
|
||||
}
|
||||
.submit {float:right;width:80px;}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue