new version
parent
0234144dfb
commit
0cb17b6292
81
build.dev.js
81
build.dev.js
|
@ -15,20 +15,6 @@ const VERSION = require('./package.json').version
|
|||
const BUILD_DATE = new Date().format()
|
||||
|
||||
const BASE_SCSS = `
|
||||
$ct: #4db6ac #26a69a #009688;
|
||||
$cg: #81c784 #66bb6a #4caf50;
|
||||
$cpp: #9575cd #9575cd #673ab7;
|
||||
$cb: #64b5f6 #42a5f5 #2196f3;
|
||||
$cr: #ff5061 #eb3b48 #ce3742;
|
||||
$co: #ffb618 #f39c12 #e67e22;
|
||||
$cp: #f2f5fc #e8ebf4 #dae1e9;
|
||||
$cgr: #bdbdbd #9e9e9e #757575;
|
||||
$cd: #62778d #526273 #425064;
|
||||
|
||||
@mixin ts($c: all, $t: .1s, $m: ease-in-out){
|
||||
transition:$c $t $m;
|
||||
}
|
||||
|
||||
@mixin focus1(){
|
||||
box-shadow: 0 0 2px #88f7df;
|
||||
}
|
||||
|
@ -43,6 +29,36 @@ $cd: #62778d #526273 #425064;
|
|||
}
|
||||
::before,
|
||||
::after{box-sizing:border-box;}
|
||||
|
||||
:host {
|
||||
--color-teal-1: #4db6ac;
|
||||
--color-teal-2: #26a69a;
|
||||
--color-teal-3: #009688;
|
||||
--color-green-1: #81c784;
|
||||
--color-green-2: #66bb6a;
|
||||
--color-green-3: #4caf50;
|
||||
--color-purple-1: #9575cd;
|
||||
--color-purple-2: #9575cd;
|
||||
--color-purple-3: #673ab7;
|
||||
--color-blue-1: #64b5f6;
|
||||
--color-blue-2: #42a5f5;
|
||||
--color-blue-3: #2196f3;
|
||||
--color-red-1: #ff5061;
|
||||
--color-red-2: #eb3b48;
|
||||
--color-red-3: #ce3742;
|
||||
--color-orange-1: #ffb618;
|
||||
--color-orange-2: #f39c12;
|
||||
--color-orange-3: #e67e22;
|
||||
--color-plain-1: #f2f5fc;
|
||||
--color-plain-2: #e8ebf4;
|
||||
--color-plain-3: #dae1e9;
|
||||
--color-grey-1: #bdbdbd;
|
||||
--color-grey-2: #9e9e9e;
|
||||
--color-grey-3: #757575;
|
||||
--color-dark-1: #62778d;
|
||||
--color-dark-2: #526273;
|
||||
--color-dark-3: #425064;
|
||||
}
|
||||
`
|
||||
|
||||
function parseName(str) {
|
||||
|
@ -79,26 +95,27 @@ function mkWCFile({ style, html, js }) {
|
|||
style = compileScss(style)
|
||||
|
||||
let name = ''
|
||||
let props = ''
|
||||
|
||||
js = js.replace(/props = (\{\}|\{[\w\W]*?\n\s{2}?\})/, function(s, m) {
|
||||
props = m
|
||||
var attr = new Function(
|
||||
`try {
|
||||
var props = ${m}, attr = []
|
||||
for(var i in props){attr.push(i)}
|
||||
return attr
|
||||
} catch(err) {console.error(err);return []}
|
||||
`
|
||||
)()
|
||||
return `static get observedAttributes() {
|
||||
js = js.replace(/props = (\{\}|\{[\w\W]*?\n\s{2}?\})/, function(str) {
|
||||
var attr = str
|
||||
.split(/\n+/)
|
||||
.slice(1, -1)
|
||||
.map(it => {
|
||||
var tmp = it.split(':')
|
||||
return tmp[0].trim()
|
||||
})
|
||||
return `
|
||||
|
||||
static get observedAttributes() {
|
||||
return ${JSON.stringify(attr)}
|
||||
}
|
||||
|
||||
${str}
|
||||
`
|
||||
})
|
||||
|
||||
js = fixImport(js)
|
||||
.replace(/class ([a-zA-Z0-9]+)/, function(s, m) {
|
||||
.replace(/export default class ([a-zA-Z0-9]+)/, function(s, m) {
|
||||
name = m
|
||||
return `${s} extends HTMLElement `
|
||||
})
|
||||
|
@ -112,12 +129,6 @@ function mkWCFile({ style, html, js }) {
|
|||
enumerable: false,
|
||||
configurable: true
|
||||
})
|
||||
Object.defineProperty(this, 'props', {
|
||||
value: ${props},
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
})
|
||||
|
||||
this.root.innerHTML = \`<style>${style}</style>${html}\`
|
||||
`
|
||||
|
@ -138,10 +149,6 @@ function mkWCFile({ style, html, js }) {
|
|||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const log = console.log
|
||||
|
||||
${js}
|
||||
|
||||
if(!customElements.get('wc-${parseName(name)}')){
|
||||
|
|
|
@ -6,7 +6,7 @@ const fs = require('iofs')
|
|||
const path = require('path')
|
||||
const scss = require('node-sass')
|
||||
const chalk = require('chalk')
|
||||
const uglify = require('uglify-es')
|
||||
const { minify } = require('terser')
|
||||
|
||||
const sourceDir = path.resolve(__dirname, 'src')
|
||||
const buildDir = path.resolve(__dirname, 'dist')
|
||||
|
@ -15,20 +15,6 @@ const VERSION = require('./package.json').version
|
|||
const BUILD_DATE = new Date().format()
|
||||
|
||||
const BASE_SCSS = `
|
||||
$ct: #4db6ac #26a69a #009688;
|
||||
$cg: #81c784 #66bb6a #4caf50;
|
||||
$cpp: #9575cd #9575cd #673ab7;
|
||||
$cb: #64b5f6 #42a5f5 #2196f3;
|
||||
$cr: #ff5061 #eb3b48 #ce3742;
|
||||
$co: #ffb618 #f39c12 #e67e22;
|
||||
$cp: #f2f5fc #e8ebf4 #dae1e9;
|
||||
$cgr: #bdbdbd #9e9e9e #757575;
|
||||
$cd: #62778d #526273 #425064;
|
||||
|
||||
@mixin ts($c: all, $t: .1s, $m: ease-in-out){
|
||||
transition:$c $t $m;
|
||||
}
|
||||
|
||||
@mixin focus1(){
|
||||
box-shadow: 0 0 2px #88f7df;
|
||||
}
|
||||
|
@ -43,6 +29,36 @@ $cd: #62778d #526273 #425064;
|
|||
}
|
||||
::before,
|
||||
::after{box-sizing:border-box;}
|
||||
|
||||
:host {
|
||||
--color-teal-1: #4db6ac;
|
||||
--color-teal-2: #26a69a;
|
||||
--color-teal-3: #009688;
|
||||
--color-green-1: #81c784;
|
||||
--color-green-2: #66bb6a;
|
||||
--color-green-3: #4caf50;
|
||||
--color-purple-1: #9575cd;
|
||||
--color-purple-2: #9575cd;
|
||||
--color-purple-3: #673ab7;
|
||||
--color-blue-1: #64b5f6;
|
||||
--color-blue-2: #42a5f5;
|
||||
--color-blue-3: #2196f3;
|
||||
--color-red-1: #ff5061;
|
||||
--color-red-2: #eb3b48;
|
||||
--color-red-3: #ce3742;
|
||||
--color-orange-1: #ffb618;
|
||||
--color-orange-2: #f39c12;
|
||||
--color-orange-3: #e67e22;
|
||||
--color-plain-1: #f2f5fc;
|
||||
--color-plain-2: #e8ebf4;
|
||||
--color-plain-3: #dae1e9;
|
||||
--color-grey-1: #bdbdbd;
|
||||
--color-grey-2: #9e9e9e;
|
||||
--color-grey-3: #757575;
|
||||
--color-dark-1: #62778d;
|
||||
--color-dark-2: #526273;
|
||||
--color-dark-3: #425064;
|
||||
}
|
||||
`
|
||||
|
||||
function parseName(str) {
|
||||
|
@ -62,14 +78,14 @@ const compileJs = (entry, output) => {
|
|||
let t1 = Date.now()
|
||||
let buf = fs.cat(entry).toString()
|
||||
buf = fixImport(buf)
|
||||
let { code } = uglify.minify(buf)
|
||||
|
||||
minify(buf, { sourceMap: false }).then(res => {
|
||||
log(
|
||||
'编译JS: %s, 耗时 %s ms',
|
||||
chalk.green(entry),
|
||||
chalk.yellow(Date.now() - t1)
|
||||
)
|
||||
fs.echo(code, output)
|
||||
fs.echo(res.code, output)
|
||||
})
|
||||
}
|
||||
|
||||
// 编译样式
|
||||
|
@ -93,21 +109,27 @@ function mkWCFile({ style, html, js }) {
|
|||
html = html.replace(/\s+/g, ' ')
|
||||
|
||||
let name = ''
|
||||
let props = ''
|
||||
|
||||
js = js.replace(/props = (\{\}|\{[\w\W]*?\n\s{2}?\})/, function(s, m) {
|
||||
props = m
|
||||
var attr = new Function(
|
||||
`var props = ${m}, attr = []; for(var i in props){attr.push(i)}; return attr`
|
||||
)()
|
||||
return `static get observedAttributes() {
|
||||
js = js.replace(/props = (\{\}|\{[\w\W]*?\n\s{2}?\})/, function(str) {
|
||||
var attr = str
|
||||
.split(/\n+/)
|
||||
.slice(1, -1)
|
||||
.map(it => {
|
||||
var tmp = it.split(':')
|
||||
return tmp[0].trim()
|
||||
})
|
||||
return `
|
||||
|
||||
static get observedAttributes() {
|
||||
return ${JSON.stringify(attr)}
|
||||
}
|
||||
|
||||
${str}
|
||||
`
|
||||
})
|
||||
|
||||
js = fixImport(js)
|
||||
.replace(/class ([a-zA-Z0-9]+)/, function(s, m) {
|
||||
.replace(/export default class ([a-zA-Z0-9]+)/, function(s, m) {
|
||||
name = m
|
||||
return `${s} extends HTMLElement `
|
||||
})
|
||||
|
@ -121,12 +143,7 @@ function mkWCFile({ style, html, js }) {
|
|||
enumerable: false,
|
||||
configurable: true
|
||||
})
|
||||
Object.defineProperty(this, 'props', {
|
||||
value: ${props},
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
})
|
||||
|
||||
|
||||
this.root.innerHTML = \`<style>${style}</style>${html}\`
|
||||
`
|
||||
|
@ -139,8 +156,7 @@ function mkWCFile({ style, html, js }) {
|
|||
)
|
||||
.replace('adopted()', 'adoptedCallback()')
|
||||
|
||||
let res = uglify.minify(js)
|
||||
|
||||
return minify(js, { sourceMap: false }).then(res => {
|
||||
return `/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
|
@ -149,14 +165,13 @@ function mkWCFile({ style, html, js }) {
|
|||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
${res.code}
|
||||
|
||||
if(!customElements.get('wc-${parseName(name)}')){
|
||||
customElements.define('wc-${parseName(name)}', ${name})
|
||||
}
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
const compileWC = (entry, output) => {
|
||||
|
@ -170,8 +185,9 @@ const compileWC = (entry, output) => {
|
|||
html = html ? html[1] : ''
|
||||
js = js ? js[1] : ''
|
||||
|
||||
let result = mkWCFile({ style, html, js })
|
||||
fs.echo(result, output)
|
||||
mkWCFile({ style, html, js }).then(txt => {
|
||||
fs.echo(txt, output)
|
||||
})
|
||||
}
|
||||
|
||||
/*=======================================================*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@bd/wcui",
|
||||
"version": "2.0.0",
|
||||
"name": "@bytedo/wcui",
|
||||
"version": "1.0.0",
|
||||
"description": "基于wc开发的一套UI库, 面向未来, 面向electron",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/yutent/doui.git"
|
||||
"url": "git+https://github.com/bytedo/wcui.git"
|
||||
},
|
||||
"keywords": ["web-components", "wc", "components", "yutent"],
|
||||
"author": "yutent",
|
||||
|
@ -21,6 +21,6 @@
|
|||
"es.shim": "^1.1.2",
|
||||
"iofs": "^1.3.2",
|
||||
"node-sass": "^4.12.0",
|
||||
"uglify-es": "^3.3.9"
|
||||
"terser": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
font-size: 12px;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 16px;
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
transform: translateX(100%) translateY(-50%);
|
||||
|
@ -34,22 +34,22 @@
|
|||
}
|
||||
}
|
||||
:host([color='dark']) .dot {
|
||||
background: nth($cd, 1);
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
:host([color='green']) .dot {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
:host([color='blue']) .dot {
|
||||
background: nth($cb, 1);
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
:host([color='orange']) .dot {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
:host([color='purple']) .dot {
|
||||
background: nth($cpp, 1);
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
:host([color='teal']) .dot {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<canvas></canvas>
|
||||
<section>
|
||||
<wc-button color="blue" data-key="1" size="mini">1月</wc-button>
|
||||
<wc-button data-key="3" size="mini">3月</wc-button>
|
||||
<wc-button data-key="6" size="mini">半年</wc-button>
|
||||
<wc-button data-key="12" size="mini">1年</wc-button>
|
||||
<wc-button data-key="36" size="mini">3年</wc-button>
|
||||
<wc-button data-key="999" size="mini">所有</wc-button>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: flex;
|
||||
width: 680px;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
padding: 24px 0 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 680px;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
section {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
import '../form/button'
|
||||
|
||||
const DARK = '#62778d'
|
||||
const BLUE = '#64b5f6'
|
||||
const PLAIN = '#f2f5fc'
|
||||
|
||||
export default class Line {
|
||||
props = {
|
||||
list: []
|
||||
}
|
||||
|
||||
state = {
|
||||
key: 1,
|
||||
list: []
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
var elem = this.root.children[1]
|
||||
this.__SCENE__ = elem.firstElementChild
|
||||
this.__FILTER__ = elem.lastElementChild
|
||||
this.__CTX__ = this.__SCENE__.getContext('2d')
|
||||
this.__SCENE__.width = 680
|
||||
this.__SCENE__.height = 230
|
||||
}
|
||||
|
||||
_getTime(n) {
|
||||
var now = new Date()
|
||||
var time = { getTime: _ => 0 }
|
||||
var Y = now.getFullYear()
|
||||
var m = now.getMonth()
|
||||
var d = now.getDate()
|
||||
|
||||
switch (n) {
|
||||
case 1:
|
||||
time = new Date(Y, m - 1, d, 0, 0, 0)
|
||||
break
|
||||
case 3:
|
||||
time = new Date(Y, m - 3, d, 0, 0, 0)
|
||||
break
|
||||
case 6:
|
||||
time = new Date(Y, m - 6, d, 0, 0, 0)
|
||||
break
|
||||
case 12:
|
||||
time = new Date(Y - 1, m, d, 0, 0, 0)
|
||||
break
|
||||
case 36:
|
||||
time = new Date(Y - 3, m, d, 0, 0, 0)
|
||||
break
|
||||
}
|
||||
return time.getTime()
|
||||
}
|
||||
|
||||
_filter(n) {
|
||||
if (n < 999) {
|
||||
var time = this._getTime(n)
|
||||
this.state.list = this.props.list.filter(it => it.x >= time)
|
||||
} else {
|
||||
this.state.list = this.props.list.concat()
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
var { list, key } = this.state
|
||||
var ctx = this.__CTX__
|
||||
var x = 36
|
||||
var max = 0
|
||||
var min = Number.MAX_SAFE_INTEGER
|
||||
var step = 0 // 纵坐标间隔
|
||||
var dis = +(640 / list.length).toFixed(2) || 1 // 横坐标间隔
|
||||
var point
|
||||
var p1, p2, p3, p4
|
||||
var format = key > 12 ? 'Y/m' : 'm/d'
|
||||
|
||||
for (let it of list) {
|
||||
if (max < it.y) {
|
||||
max = it.y
|
||||
}
|
||||
if (min > it.y) {
|
||||
min = it.y
|
||||
}
|
||||
}
|
||||
|
||||
min = ~~(min / 100)
|
||||
max = Math.ceil(max / 100)
|
||||
step = ~~((max - min) / 3)
|
||||
|
||||
p1 = Math.floor(list.length / 4)
|
||||
p2 = Math.floor(list.length / 2)
|
||||
p3 = Math.floor((list.length * 3) / 4)
|
||||
p4 = list.length - 1
|
||||
|
||||
ctx.clearRect(0, 0, 680, 230)
|
||||
|
||||
// 纵坐标数值
|
||||
ctx.font = '12px Arial'
|
||||
ctx.textAlign = 'right'
|
||||
ctx.fillStyle = DARK
|
||||
ctx.fillText(min / 100, 32, 205)
|
||||
ctx.fillText((min + step) / 100, 32, 155)
|
||||
ctx.fillText((min + step + step) / 100, 32, 105)
|
||||
ctx.fillText((min + step + step + step) / 100, 32, 55)
|
||||
|
||||
ctx.font = '10px Arial'
|
||||
ctx.textAlign = 'left'
|
||||
ctx.fillText(new Date(list[0].x).format(format), x - 12, 225)
|
||||
ctx.fillText(new Date(list[p1].x).format(format), x + dis * p1 - 12, 225)
|
||||
ctx.fillText(new Date(list[p2].x).format(format), x + dis * p2 - 12, 225)
|
||||
ctx.fillText(new Date(list[p3].x).format(format), x + dis * p3 - 12, 225)
|
||||
ctx.fillText(
|
||||
new Date(list[p4].x).format(format),
|
||||
x + dis * p4 - 12 - (key > 12 ? 24 : 4),
|
||||
225
|
||||
)
|
||||
|
||||
// x轴参考线
|
||||
ctx.fillStyle = PLAIN
|
||||
ctx.fillRect(x, 50, 648, 1)
|
||||
ctx.fillRect(x, 100, 648, 1)
|
||||
ctx.fillRect(x, 150, 648, 1)
|
||||
ctx.fillRect(x, 200, 648, 1)
|
||||
|
||||
// y轴参考 线
|
||||
ctx.fillRect(x, 0, 1, 210)
|
||||
ctx.fillRect(x + dis * p1, 0, 1, 210)
|
||||
ctx.fillRect(x + dis * p2, 0, 1, 210)
|
||||
ctx.fillRect(x + dis * p3, 0, 1, 210)
|
||||
ctx.fillRect(x + dis * p4, 0, 1, 210)
|
||||
|
||||
point = list.shift()
|
||||
|
||||
// 曲线
|
||||
ctx.beginPath()
|
||||
ctx.strokeStyle = BLUE
|
||||
ctx.lineWidth = 1
|
||||
ctx.moveTo(x, 200 - (((point.y / 100 - min) / step) * 50).toFixed(0))
|
||||
|
||||
while (list.length) {
|
||||
let y
|
||||
|
||||
point = list.shift()
|
||||
|
||||
y = 200 - (((point.y / 100 - min) / step) * 50).toFixed(0)
|
||||
x += dis
|
||||
|
||||
ctx.lineTo(x, y)
|
||||
}
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
mounted() {
|
||||
$.bind(this.__FILTER__, 'click', ev => {
|
||||
var el = ev.target
|
||||
if (this.props.list.length < 1) {
|
||||
return
|
||||
}
|
||||
if (el.tagName === 'WC-BUTTON') {
|
||||
var k = +el.dataset.key
|
||||
|
||||
$.each(this.__FILTER__.children, function(it) {
|
||||
it.removeAttribute('color')
|
||||
})
|
||||
el.setAttribute('color', 'blue')
|
||||
|
||||
this.state.key = k
|
||||
this._filter(k)
|
||||
this.draw()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'list':
|
||||
try {
|
||||
var list = JSON.parse(val)
|
||||
list.forEach(it => (it.x = it.x * 1000))
|
||||
this.props.list = list
|
||||
this._filter(this.state.key)
|
||||
this.removeAttribute('list')
|
||||
this.draw()
|
||||
} catch (e) {}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<canvas></canvas>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
canvas {
|
||||
width: 680px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const RED = '#ff5061'
|
||||
const GREEN = '#4caf50'
|
||||
const BLUE = '#64b5f6'
|
||||
const GREY = '#bdbdbd'
|
||||
const PLAIN = '#f2f5fc'
|
||||
const DARK = '#62778d'
|
||||
|
||||
export default class Rank {
|
||||
props = {
|
||||
stat: {}
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
this.__SCENE__ = this.root.children[1]
|
||||
this.__CTX__ = this.__SCENE__.getContext('2d')
|
||||
this.__SCENE__.width = 680
|
||||
this.__SCENE__.height = 100
|
||||
}
|
||||
|
||||
draw() {
|
||||
var { rank, e1, e3, e6, e12, cm, cp } = this.props.stat
|
||||
var ctx = this.__CTX__
|
||||
var x = 32
|
||||
|
||||
while (rank.length < 60) {
|
||||
rank.unshift(0)
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, 680, 101)
|
||||
|
||||
ctx.font = '10px Arial'
|
||||
ctx.textAlign = 'right'
|
||||
ctx.fillStyle = RED
|
||||
ctx.fillText('10%', 28, 10)
|
||||
ctx.fillText('5%', 28, 30)
|
||||
ctx.fillStyle = GREEN
|
||||
ctx.fillText('-5%', 28, 80)
|
||||
ctx.fillText('-10%', 28, 100)
|
||||
|
||||
ctx.font = '10px menlo,Hiragino Sans GB'
|
||||
ctx.textAlign = 'left'
|
||||
ctx.fillStyle = DARK
|
||||
ctx.fillText('60天红绿榜', 160, 10)
|
||||
|
||||
ctx.font = '12px menlo,Hiragino Sans GB'
|
||||
ctx.fillText(`最近1个月收益: ${e1}%`, 360, 25)
|
||||
ctx.fillText(`最近3个月收益: ${e3}%`, 360, 45)
|
||||
ctx.fillText(`最近半年收益: ${e6}%`, 528, 25)
|
||||
ctx.fillText(`最近一年收益: ${e12}%`, 528, 45)
|
||||
|
||||
ctx.fillStyle = cp > 0 ? RED : cp === 0 ? GREY : GREEN
|
||||
ctx.fillRect(360, 65, 140, 20)
|
||||
ctx.fillRect(526, 65, 140, 20)
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.font = 'bold 14px menlo,Hiragino Sans GB'
|
||||
ctx.fillText(`实时净值: ¥${cm}`, 364, 80)
|
||||
ctx.fillText(`实时涨跌: ${cp}%`, 532, 80)
|
||||
|
||||
ctx.fillStyle = PLAIN
|
||||
ctx.fillRect(28, 25, 320, 1)
|
||||
ctx.fillRect(28, 75, 320, 1)
|
||||
ctx.fillStyle = GREY
|
||||
ctx.fillRect(28, 0, 1, 140)
|
||||
ctx.fillRect(0, 50, 348, 1)
|
||||
|
||||
while (rank.length) {
|
||||
var n = rank.shift()
|
||||
var y = Math.ceil(50 - (n / 10) * 50)
|
||||
|
||||
ctx.fillStyle = n > 0 ? RED : GREEN
|
||||
|
||||
if (y > 50) {
|
||||
ctx.fillRect(x, 50, 3, y - 50)
|
||||
} else {
|
||||
ctx.fillRect(x, y, 3, 50 - y)
|
||||
}
|
||||
|
||||
x += 5
|
||||
}
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'stat':
|
||||
try {
|
||||
var stat = JSON.parse(val)
|
||||
this.props.stat = stat
|
||||
this.removeAttribute('stat')
|
||||
this.draw()
|
||||
} catch (e) {}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -24,7 +24,7 @@
|
|||
width: 100%;
|
||||
max-height: 610px;
|
||||
margin: 10px 0;
|
||||
border: 1px solid nth($cp, 2);
|
||||
border: 1px solid var(--color-plain-2);
|
||||
border-radius: 2px;
|
||||
|
||||
.title {
|
||||
|
@ -36,7 +36,7 @@
|
|||
padding: 0 12px;
|
||||
line-height: 1;
|
||||
font-size: 12px;
|
||||
background: nth($cp, 2);
|
||||
background: var(--color-plain-2);
|
||||
user-select: none;
|
||||
|
||||
i {
|
||||
|
@ -45,23 +45,23 @@
|
|||
height: 10px;
|
||||
margin-right: 6px;
|
||||
border-radius: 50%;
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
i:nth-child(2) {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
i:nth-child(3) {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
|
||||
.act {
|
||||
--size: 16px;
|
||||
margin: 0 2px;
|
||||
color: nth($cgr, 2);
|
||||
color: var(--color-grey-2);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: nth($cgr, 3);
|
||||
color: var(--color-grey-3);
|
||||
}
|
||||
&.run {
|
||||
display: none;
|
||||
|
@ -75,8 +75,8 @@
|
|||
line-height: 18px;
|
||||
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
background: linear-gradient(to right, nth($cp, 1) 40px, #fff 40px);
|
||||
color: nth($cd, 1);
|
||||
background: linear-gradient(to right, var(--color-plain-1) 40px, #fff 40px);
|
||||
color: var(--color-dark-1);
|
||||
cursor: text;
|
||||
counter-reset: code;
|
||||
|
||||
|
@ -95,7 +95,7 @@
|
|||
height: 100%;
|
||||
padding-right: 5px;
|
||||
text-align: right;
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
content: counter(code);
|
||||
counter-increment: code;
|
||||
}
|
||||
|
@ -113,18 +113,22 @@
|
|||
|
||||
:host([dark]) {
|
||||
.code-box {
|
||||
border-color: nth($cd, 2);
|
||||
border-color: var(--color-dark-2);
|
||||
|
||||
.title {
|
||||
background: nth($cd, 2);
|
||||
background: var(--color-dark-2);
|
||||
}
|
||||
|
||||
.code {
|
||||
background: linear-gradient(to right, #596b7f 40px, nth($cd, 1) 40px);
|
||||
color: nth($cp, 3);
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
#596b7f 40px,
|
||||
var(--color-dark-1) 40px
|
||||
);
|
||||
color: var(--color-plain-3);
|
||||
|
||||
p::before {
|
||||
color: nth($cgr, 3);
|
||||
color: var(--color-grey-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +166,10 @@ export default class Code {
|
|||
|
||||
set value(txt) {
|
||||
this.props.content = txt
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
||||
txt = txt
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
*
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2020/12/03 18:24:47
|
||||
*/
|
||||
|
||||
// H: 色相, S: 饱和度, B/V: 亮度
|
||||
export function hsb2rgb(hsb) {
|
||||
var h = hsb.h
|
||||
var s = Math.round((hsb.s * 255) / 100)
|
||||
var v = Math.round((hsb.b * 255) / 100)
|
||||
var r = 0
|
||||
var g = 0
|
||||
var b = 0
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = v
|
||||
} else {
|
||||
var t1 = v
|
||||
var t2 = ((255 - s) * v) / 255
|
||||
var t3 = ((t1 - t2) * (h % 60)) / 60
|
||||
|
||||
//
|
||||
if (h === 360) {
|
||||
h = 0
|
||||
}
|
||||
|
||||
if (h < 60) {
|
||||
r = t1
|
||||
g = t2 + t3
|
||||
b = t2
|
||||
} else if (h < 120) {
|
||||
r = t1 - t3
|
||||
g = t1
|
||||
b = t2
|
||||
} else if (h < 180) {
|
||||
r = t2
|
||||
g = t1
|
||||
b = t2 + t3
|
||||
} else if (h < 240) {
|
||||
r = t2
|
||||
g = t1 - t3
|
||||
b = t1
|
||||
} else if (h < 300) {
|
||||
r = t2 + t3
|
||||
g = t2
|
||||
b = t1
|
||||
} else if (h < 360) {
|
||||
r = t1
|
||||
g = t2
|
||||
b = t1 - t3
|
||||
}
|
||||
}
|
||||
r = Math.round(r)
|
||||
g = Math.round(g)
|
||||
b = Math.round(b)
|
||||
|
||||
return { r, g, b }
|
||||
}
|
||||
|
||||
export function rgb2hex({ r, g, b }) {
|
||||
return [r, g, b].map(it => it.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
|
||||
export function hex2rgb(hex) {
|
||||
var r, g, b
|
||||
|
||||
hex = hex.replace(/^#/, '').split('')
|
||||
|
||||
if (hex.length === 3) {
|
||||
r = parseInt(hex[0] + hex[0], 16)
|
||||
g = parseInt(hex[1] + hex[1], 16)
|
||||
b = parseInt(hex[2] + hex[2], 16)
|
||||
} else {
|
||||
r = parseInt(hex[0] + hex[1], 16)
|
||||
g = parseInt(hex[2] + hex[3], 16)
|
||||
b = parseInt(hex[4] + hex[5], 16)
|
||||
}
|
||||
|
||||
return { r, g, b }
|
||||
}
|
||||
|
||||
export function rgb2hsb({ r, g, b }) {
|
||||
var hsb = { h: 0, s: 0, b: 0 }
|
||||
var max = Math.max(r, g, b)
|
||||
var min = Math.min(r, g, b)
|
||||
var delta = max - min
|
||||
|
||||
hsb.b = max
|
||||
hsb.s = max === 0 ? 0 : (delta * 255) / max
|
||||
|
||||
if (hsb.s === 0) {
|
||||
hsb.h = -1
|
||||
} else {
|
||||
if (r === max) {
|
||||
hsb.h = (g - b) / delta
|
||||
} else if (g === max) {
|
||||
hsb.h = 2 + (b - r) / delta
|
||||
} else {
|
||||
hsb.h = 4 + (r - g) / delta
|
||||
}
|
||||
}
|
||||
hsb.h *= 60
|
||||
|
||||
if (hsb.h < 0) {
|
||||
hsb.h += 360
|
||||
}
|
||||
|
||||
hsb.s *= 100 / 255
|
||||
hsb.b *= 100 / 255
|
||||
|
||||
return hsb
|
||||
}
|
||||
|
||||
export function hex2hsb(hex) {
|
||||
return rgb2hsb(hex2rgb(hex))
|
||||
}
|
|
@ -0,0 +1,475 @@
|
|||
<template>
|
||||
<div class="color-picker">
|
||||
<section class="preview alpha-bg">
|
||||
<span></span>
|
||||
</section>
|
||||
<div class="color-panel">
|
||||
<section class="dashboard">
|
||||
<div class="scene"><span class="thumb"></span></div>
|
||||
<div class="pool">
|
||||
<span class="thumb hue-thumb"></span>
|
||||
<input class="hue" max="360" min="0" step="1" type="range" />
|
||||
</div>
|
||||
</section>
|
||||
<section class="alpha-box alpha-bg">
|
||||
<div class="bar"></div>
|
||||
<span class="thumb alpha-thumb"></span>
|
||||
<input class="alpha" max="100" min="0" step="1" type="range" />
|
||||
</section>
|
||||
<section class="input-box">
|
||||
<input class="input" maxlength="28" />
|
||||
<a class="clear">清除</a>
|
||||
<a class="submit">确定</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.alpha-bg {
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
var(--color-grey-1) 25%,
|
||||
transparent 25%,
|
||||
transparent 75%,
|
||||
var(--color-grey-1) 75%,
|
||||
var(--color-grey-1)
|
||||
),
|
||||
linear-gradient(
|
||||
45deg,
|
||||
var(--color-grey-1) 25%,
|
||||
transparent 25%,
|
||||
transparent 75%,
|
||||
var(--color-grey-1) 75%,
|
||||
var(--color-grey-1)
|
||||
);
|
||||
background-size: 12px 12px;
|
||||
background-position: 0 0, 6px 6px;
|
||||
}
|
||||
.color-picker {
|
||||
position: relative;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
.preview {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid var(--color-grey-1);
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 3px solid #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.color-panel {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 32px;
|
||||
width: 310px;
|
||||
padding: 5px;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
|
||||
|
||||
.dashboard {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.scene {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 280px;
|
||||
height: 180px;
|
||||
background: #f00;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
background: linear-gradient(to right, #fff, transparent);
|
||||
}
|
||||
&::after {
|
||||
background: linear-gradient(to bottom, transparent, #000);
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: rgba(32, 32, 32, 0.3);
|
||||
box-shadow: 0 0 0 1px #fff;
|
||||
transform: translate(-5px, -5px);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pool {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 12px;
|
||||
height: 180px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
#f00 0,
|
||||
#ff0 17%,
|
||||
#0f0 33%,
|
||||
#0ff 50%,
|
||||
#00f 67%,
|
||||
#f0f 83%,
|
||||
#f00
|
||||
);
|
||||
|
||||
.thumb {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
width: 12px;
|
||||
height: 0;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
|
||||
transform: translateY(-6px);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
.hue {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 180px;
|
||||
appearance: slider-vertical;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alpha-box {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
margin: 12px 0;
|
||||
|
||||
.bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
background: linear-gradient(to right, transparent, #f00);
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
width: 0;
|
||||
height: 12px;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
|
||||
transform: translateX(-6px);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.alpha {
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
|
||||
.input {
|
||||
width: 200px;
|
||||
height: 24px;
|
||||
padding: 0 6px;
|
||||
line-height: 22px;
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
color: var(--color-dark-1);
|
||||
}
|
||||
|
||||
.clear,
|
||||
.submit {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.clear {
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
|
||||
.submit {
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
color: var(--color-plain-1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
import { hsb2rgb, rgb2hex, hex2rgb, rgb2hsb } from './helper'
|
||||
|
||||
export default class Color {
|
||||
props = {
|
||||
value: ''
|
||||
}
|
||||
|
||||
state = {
|
||||
hsb: { h: 0, s: 100, b: 100 },
|
||||
rgba: { r: 255, g: 0, b: 0, a: 100 }
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
var elem = this.root.children[1]
|
||||
|
||||
this.__PREVIEW__ = elem.children[0].children[0]
|
||||
this.__SCENE__ = elem.querySelector('.scene')
|
||||
this.__SCENE_THUMB__ = this.__SCENE__.querySelector('.thumb')
|
||||
this.__HUE__ = elem.querySelector('.hue')
|
||||
this.__HUE_THUMB__ = elem.querySelector('.hue-thumb')
|
||||
this.__ALPHA__ = elem.querySelector('.alpha')
|
||||
this.__ALPHA_BAR__ = elem.querySelector('.alpha-box .bar')
|
||||
this.__ALPHA_THUMB__ = elem.querySelector('.alpha-thumb')
|
||||
this.__INPUT__ = elem.querySelector('.input')
|
||||
}
|
||||
|
||||
_updateView() {
|
||||
var { hsb, rgba } = this.state
|
||||
var scene, color, alphaColor
|
||||
var x, y
|
||||
|
||||
x = Math.ceil((hsb.s * 280) / 100)
|
||||
y = 180 - Math.ceil((hsb.b * 180) / 100)
|
||||
|
||||
scene = '#' + rgb2hex(hsb2rgb({ h: hsb.h, s: 100, b: 100 }))
|
||||
alphaColor = '#' + rgb2hex(rgba)
|
||||
|
||||
if (rgba.a < 100) {
|
||||
color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a / 100})`
|
||||
} else {
|
||||
color = alphaColor
|
||||
}
|
||||
|
||||
this._moveSceneThumb(x, y)
|
||||
|
||||
this.__HUE_THUMB__.style.top = hsb.h / 2 + 'px'
|
||||
this.__SCENE__.style.backgroundColor = scene
|
||||
this.__PREVIEW__.style.backgroundColor = color
|
||||
this.__ALPHA_BAR__.style.background = `linear-gradient(to right, transparent, ${alphaColor})`
|
||||
|
||||
this.__INPUT__.value = color
|
||||
}
|
||||
|
||||
_moveSceneThumb(x, y) {
|
||||
this.__SCENE_THUMB__.style.cssText = `left: ${x}px; top:${y}px`
|
||||
}
|
||||
|
||||
_changeColor(x, y) {
|
||||
var { rgba, hsb } = this.state
|
||||
var alphaColor
|
||||
|
||||
hsb.s = ~~((100 * x) / 280)
|
||||
hsb.b = ~~((100 * (180 - y)) / 180)
|
||||
Object.assign(rgba, hsb2rgb(hsb))
|
||||
|
||||
var { r, g, b, a } = rgba
|
||||
|
||||
if (rgba.a < 100) {
|
||||
this.__INPUT__.value = `rgba(${r}, ${g}, ${b}, ${a / 100})`
|
||||
} else {
|
||||
this.__INPUT__.value = `#${rgb2hex({ r, g, b })}`
|
||||
}
|
||||
|
||||
alphaColor = `linear-gradient(to right, transparent, rgb(${r}, ${g}, ${b}))`
|
||||
|
||||
this.__PREVIEW__.style.backgroundColor = this.__INPUT__.value
|
||||
this.__ALPHA_BAR__.style.background = alphaColor
|
||||
this._moveSceneThumb(x, y)
|
||||
this.dispatchEvent(new CustomEvent('input'))
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.__INPUT__.value
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
var isHex
|
||||
var rgb
|
||||
|
||||
val = val.toLowerCase()
|
||||
|
||||
if (!val) {
|
||||
return
|
||||
}
|
||||
|
||||
isHex = /^#[0-9a-f]{3,6}$/.test(val)
|
||||
|
||||
if (isHex) {
|
||||
Object.assign(this.state.rgba, hex2rgb(val), { a: 100 })
|
||||
} else {
|
||||
var res = val.match(/rgba?\((\d+),\s*?(\d+),\s*?(\d+)[,\s]*?([\d\.]+)?\)/)
|
||||
if (res) {
|
||||
this.state.rgba = { r: +res[1], g: +res[2], b: +res[3], a: 100 }
|
||||
if (res[4] !== undefined) {
|
||||
this.state.rgba.a = ~~(res[4] * 100)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.state.hsb = rgb2hsb(this.state.rgba)
|
||||
|
||||
this._updateView()
|
||||
}
|
||||
|
||||
mounted() {
|
||||
var handleMove
|
||||
|
||||
this.handleChange = $.bind(this.__INPUT__, 'change', ev => {
|
||||
this.value = this.__INPUT__.value
|
||||
})
|
||||
|
||||
this.handleDown = $.bind(this.__SCENE__, 'mousedown', ev => {
|
||||
ev.preventDefault()
|
||||
|
||||
var { pageX, pageY } = ev
|
||||
var { left, top } = $.offset(this.__SCENE__)
|
||||
var w = 280
|
||||
var h = 180
|
||||
var x = pageX - left
|
||||
var y = pageY - top
|
||||
|
||||
this._changeColor(x, y)
|
||||
|
||||
handleMove = $.bind(document, 'mousemove', ev => {
|
||||
ev.preventDefault()
|
||||
|
||||
var { pageX, pageY } = ev
|
||||
|
||||
var x = pageX - left
|
||||
var y = pageY - top
|
||||
var rgb
|
||||
|
||||
x = x < 0 ? 0 : x > 280 ? 280 : x
|
||||
y = y < 0 ? 0 : y > 180 ? 180 : y
|
||||
|
||||
this._changeColor(x, y)
|
||||
})
|
||||
})
|
||||
|
||||
this.handleUp = $.bind(document, 'mouseup', ev => {
|
||||
ev.preventDefault()
|
||||
$.unbind(document, 'mousemove', handleMove)
|
||||
})
|
||||
|
||||
// 颜色池
|
||||
this.handleInput1 = $.bind(this.__HUE__, 'input', ev => {
|
||||
var h = 360 - this.__HUE__.value
|
||||
var { s, b } = this.state.hsb
|
||||
var rgba = this.state.rgba
|
||||
var hsb = { h, s, b }
|
||||
var scene, color, alphaColor
|
||||
|
||||
Object.assign(rgba, hsb2rgb(hsb))
|
||||
|
||||
this.state.hsb = hsb
|
||||
this.state.rgba = rgba
|
||||
|
||||
scene = '#' + rgb2hex(hsb2rgb({ h, s: 100, b: 100 }))
|
||||
alphaColor = '#' + rgb2hex(rgba)
|
||||
|
||||
if (rgba.a < 100) {
|
||||
color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a / 100})`
|
||||
} else {
|
||||
color = alphaColor
|
||||
}
|
||||
|
||||
this.__HUE_THUMB__.style.top = h / 2 + 'px'
|
||||
this.__SCENE__.style.backgroundColor = scene
|
||||
this.__PREVIEW__.style.backgroundColor = color
|
||||
this.__ALPHA_BAR__.style.background = `linear-gradient(to right, transparent, ${alphaColor})`
|
||||
|
||||
this.__INPUT__.value = color
|
||||
})
|
||||
|
||||
// 透明度
|
||||
this.handleInput2 = $.bind(this.__ALPHA__, 'input', ev => {
|
||||
var a = this.__ALPHA__.value
|
||||
var { r, g, b } = this.state.rgba
|
||||
var color = `rgba(${r}, ${g}, ${b}, ${a / 100})`
|
||||
|
||||
this.state.rgba.a = a
|
||||
|
||||
this.__ALPHA_THUMB__.style.left = a + '%'
|
||||
this.__PREVIEW__.style.backgroundColor = color
|
||||
this.__INPUT__.value = color
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.unbind(this.__SCENE__, 'mousedown', this.handleDown)
|
||||
$.unbind(document, 'mousedown', this.handleUp)
|
||||
$.unbind(this.__INPUT__, 'change', this.handleChange)
|
||||
$.unbind(this.__HUE__, 'input', this.handleInput1)
|
||||
$.unbind(this.__ALPHA__, 'input', this.handleInput2)
|
||||
|
||||
delete this.handleDown
|
||||
delete this.handleUp
|
||||
delete this.handleChange
|
||||
delete this.handleInput1
|
||||
delete this.handleInput2
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,47 +0,0 @@
|
|||
# crypto & md5 加密组件
|
||||
|
||||
|
||||
|
||||
|
||||
## md5
|
||||
> 这里使用的是第三方的 SparkMD5 实现
|
||||
|
||||
```javascript
|
||||
|
||||
import { md5, md5Sum } from 'crypto/md5'
|
||||
|
||||
// 直接计算字段串的md5值
|
||||
console.log(md5('123456'))
|
||||
|
||||
|
||||
// 计算该文件的md5签名
|
||||
var file = /*...*/ //文件表单获取
|
||||
var fs = new FileReader()
|
||||
fs.onload = function(){
|
||||
console.log(md5Sum(this.result))
|
||||
}
|
||||
fs.readAsBinaryString(file)
|
||||
|
||||
```
|
||||
|
||||
|
||||
## crypto
|
||||
> 这里使用的是浏览器内置的实现, 需要在https下或本地才能用。
|
||||
>> 返回值都是Promise对象
|
||||
|
||||
```javascript
|
||||
|
||||
import hash, { sha1, sha256, sha512, hmac, base64encode, base64decode } from 'crypto/index'
|
||||
|
||||
|
||||
|
||||
hash('sha-1', '123456')
|
||||
// 等价于
|
||||
sha1('123456')
|
||||
|
||||
|
||||
// hmac签名
|
||||
hmac('sha-1', '123456', 'a key', 'hex')
|
||||
|
||||
|
||||
```
|
|
@ -1,77 +0,0 @@
|
|||
const encoder = new TextEncoder()
|
||||
const subtle = window.crypto.subtle
|
||||
|
||||
/**
|
||||
* String 转 Uint8Array
|
||||
*/
|
||||
function str2uint(txt) {
|
||||
return encoder.encode(txt)
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayBuffer 转 hex
|
||||
*/
|
||||
function ab2hex(buf) {
|
||||
var uint8 = new Uint8Array(buf)
|
||||
return [...uint8].map(n => n.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayBuffer 转 Binary
|
||||
*/
|
||||
function ab2bin(buf) {
|
||||
var bin = ''
|
||||
var uint8 = new Uint8Array(buf)
|
||||
for (var i = 0; i < uint8.length; i++) {
|
||||
bin += String.fromCharCode(uint8[i])
|
||||
}
|
||||
return bin
|
||||
}
|
||||
|
||||
/* ------------------------------------- */
|
||||
|
||||
export default function hash(type, str) {
|
||||
return subtle.digest(type, str2uint(str)).then(buf => ab2hex(buf))
|
||||
}
|
||||
|
||||
export function sha1(str) {
|
||||
return hash('sha-1', str)
|
||||
}
|
||||
|
||||
export function sha256(str) {
|
||||
return hash('sha-256', str)
|
||||
}
|
||||
|
||||
export function sha512(str) {
|
||||
return hash('sha-512', str)
|
||||
}
|
||||
|
||||
export function hmac(mode, str, key, output) {
|
||||
key = key === '' ? new Uint8Array(16) : str2uint(key)
|
||||
return subtle
|
||||
.importKey('raw', key, { name: 'HMAC', hash: { name: mode } }, true, [
|
||||
'sign',
|
||||
'verify'
|
||||
])
|
||||
.then(cKey => {
|
||||
return subtle.sign('HMAC', cKey, str2uint(str)).then(buf => {
|
||||
if (output === 'binary') {
|
||||
return ab2bin(buf)
|
||||
} else if (output === 'hex') {
|
||||
return ab2hex(buf)
|
||||
} else if (output === 'base64') {
|
||||
return window.btoa(ab2bin(buf))
|
||||
}
|
||||
return new Uint8Array(buf)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 支持对中文的base64编码
|
||||
export function base64encode(str) {
|
||||
return window.btoa(unescape(encodeURIComponent(str)))
|
||||
}
|
||||
|
||||
export function base64decode(str) {
|
||||
return decodeURIComponent(escape(window.atob(str)))
|
||||
}
|
|
@ -1,764 +0,0 @@
|
|||
/*
|
||||
* Fastest md5 implementation around (JKM md5).
|
||||
* Credits: Joseph Myers
|
||||
*
|
||||
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html
|
||||
* @see http://jsperf.com/md5-shootout/7
|
||||
*/
|
||||
|
||||
/* this function is much faster,
|
||||
so if possible we use it. Some IEs
|
||||
are the only ones I know of that
|
||||
need the idiotic second function,
|
||||
generated by an if clause. */
|
||||
var add32 = function(a, b) {
|
||||
return (a + b) & 0xffffffff
|
||||
},
|
||||
hex_chr = [
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f'
|
||||
]
|
||||
|
||||
function cmn(q, a, b, x, s, t) {
|
||||
a = add32(add32(a, q), add32(x, t))
|
||||
return add32((a << s) | (a >>> (32 - s)), b)
|
||||
}
|
||||
|
||||
function md5cycle(x, k) {
|
||||
var a = x[0],
|
||||
b = x[1],
|
||||
c = x[2],
|
||||
d = x[3]
|
||||
|
||||
a += (((b & c) | (~b & d)) + k[0] - 680876936) | 0
|
||||
a = (((a << 7) | (a >>> 25)) + b) | 0
|
||||
d += (((a & b) | (~a & c)) + k[1] - 389564586) | 0
|
||||
d = (((d << 12) | (d >>> 20)) + a) | 0
|
||||
c += (((d & a) | (~d & b)) + k[2] + 606105819) | 0
|
||||
c = (((c << 17) | (c >>> 15)) + d) | 0
|
||||
b += (((c & d) | (~c & a)) + k[3] - 1044525330) | 0
|
||||
b = (((b << 22) | (b >>> 10)) + c) | 0
|
||||
a += (((b & c) | (~b & d)) + k[4] - 176418897) | 0
|
||||
a = (((a << 7) | (a >>> 25)) + b) | 0
|
||||
d += (((a & b) | (~a & c)) + k[5] + 1200080426) | 0
|
||||
d = (((d << 12) | (d >>> 20)) + a) | 0
|
||||
c += (((d & a) | (~d & b)) + k[6] - 1473231341) | 0
|
||||
c = (((c << 17) | (c >>> 15)) + d) | 0
|
||||
b += (((c & d) | (~c & a)) + k[7] - 45705983) | 0
|
||||
b = (((b << 22) | (b >>> 10)) + c) | 0
|
||||
a += (((b & c) | (~b & d)) + k[8] + 1770035416) | 0
|
||||
a = (((a << 7) | (a >>> 25)) + b) | 0
|
||||
d += (((a & b) | (~a & c)) + k[9] - 1958414417) | 0
|
||||
d = (((d << 12) | (d >>> 20)) + a) | 0
|
||||
c += (((d & a) | (~d & b)) + k[10] - 42063) | 0
|
||||
c = (((c << 17) | (c >>> 15)) + d) | 0
|
||||
b += (((c & d) | (~c & a)) + k[11] - 1990404162) | 0
|
||||
b = (((b << 22) | (b >>> 10)) + c) | 0
|
||||
a += (((b & c) | (~b & d)) + k[12] + 1804603682) | 0
|
||||
a = (((a << 7) | (a >>> 25)) + b) | 0
|
||||
d += (((a & b) | (~a & c)) + k[13] - 40341101) | 0
|
||||
d = (((d << 12) | (d >>> 20)) + a) | 0
|
||||
c += (((d & a) | (~d & b)) + k[14] - 1502002290) | 0
|
||||
c = (((c << 17) | (c >>> 15)) + d) | 0
|
||||
b += (((c & d) | (~c & a)) + k[15] + 1236535329) | 0
|
||||
b = (((b << 22) | (b >>> 10)) + c) | 0
|
||||
|
||||
a += (((b & d) | (c & ~d)) + k[1] - 165796510) | 0
|
||||
a = (((a << 5) | (a >>> 27)) + b) | 0
|
||||
d += (((a & c) | (b & ~c)) + k[6] - 1069501632) | 0
|
||||
d = (((d << 9) | (d >>> 23)) + a) | 0
|
||||
c += (((d & b) | (a & ~b)) + k[11] + 643717713) | 0
|
||||
c = (((c << 14) | (c >>> 18)) + d) | 0
|
||||
b += (((c & a) | (d & ~a)) + k[0] - 373897302) | 0
|
||||
b = (((b << 20) | (b >>> 12)) + c) | 0
|
||||
a += (((b & d) | (c & ~d)) + k[5] - 701558691) | 0
|
||||
a = (((a << 5) | (a >>> 27)) + b) | 0
|
||||
d += (((a & c) | (b & ~c)) + k[10] + 38016083) | 0
|
||||
d = (((d << 9) | (d >>> 23)) + a) | 0
|
||||
c += (((d & b) | (a & ~b)) + k[15] - 660478335) | 0
|
||||
c = (((c << 14) | (c >>> 18)) + d) | 0
|
||||
b += (((c & a) | (d & ~a)) + k[4] - 405537848) | 0
|
||||
b = (((b << 20) | (b >>> 12)) + c) | 0
|
||||
a += (((b & d) | (c & ~d)) + k[9] + 568446438) | 0
|
||||
a = (((a << 5) | (a >>> 27)) + b) | 0
|
||||
d += (((a & c) | (b & ~c)) + k[14] - 1019803690) | 0
|
||||
d = (((d << 9) | (d >>> 23)) + a) | 0
|
||||
c += (((d & b) | (a & ~b)) + k[3] - 187363961) | 0
|
||||
c = (((c << 14) | (c >>> 18)) + d) | 0
|
||||
b += (((c & a) | (d & ~a)) + k[8] + 1163531501) | 0
|
||||
b = (((b << 20) | (b >>> 12)) + c) | 0
|
||||
a += (((b & d) | (c & ~d)) + k[13] - 1444681467) | 0
|
||||
a = (((a << 5) | (a >>> 27)) + b) | 0
|
||||
d += (((a & c) | (b & ~c)) + k[2] - 51403784) | 0
|
||||
d = (((d << 9) | (d >>> 23)) + a) | 0
|
||||
c += (((d & b) | (a & ~b)) + k[7] + 1735328473) | 0
|
||||
c = (((c << 14) | (c >>> 18)) + d) | 0
|
||||
b += (((c & a) | (d & ~a)) + k[12] - 1926607734) | 0
|
||||
b = (((b << 20) | (b >>> 12)) + c) | 0
|
||||
|
||||
a += ((b ^ c ^ d) + k[5] - 378558) | 0
|
||||
a = (((a << 4) | (a >>> 28)) + b) | 0
|
||||
d += ((a ^ b ^ c) + k[8] - 2022574463) | 0
|
||||
d = (((d << 11) | (d >>> 21)) + a) | 0
|
||||
c += ((d ^ a ^ b) + k[11] + 1839030562) | 0
|
||||
c = (((c << 16) | (c >>> 16)) + d) | 0
|
||||
b += ((c ^ d ^ a) + k[14] - 35309556) | 0
|
||||
b = (((b << 23) | (b >>> 9)) + c) | 0
|
||||
a += ((b ^ c ^ d) + k[1] - 1530992060) | 0
|
||||
a = (((a << 4) | (a >>> 28)) + b) | 0
|
||||
d += ((a ^ b ^ c) + k[4] + 1272893353) | 0
|
||||
d = (((d << 11) | (d >>> 21)) + a) | 0
|
||||
c += ((d ^ a ^ b) + k[7] - 155497632) | 0
|
||||
c = (((c << 16) | (c >>> 16)) + d) | 0
|
||||
b += ((c ^ d ^ a) + k[10] - 1094730640) | 0
|
||||
b = (((b << 23) | (b >>> 9)) + c) | 0
|
||||
a += ((b ^ c ^ d) + k[13] + 681279174) | 0
|
||||
a = (((a << 4) | (a >>> 28)) + b) | 0
|
||||
d += ((a ^ b ^ c) + k[0] - 358537222) | 0
|
||||
d = (((d << 11) | (d >>> 21)) + a) | 0
|
||||
c += ((d ^ a ^ b) + k[3] - 722521979) | 0
|
||||
c = (((c << 16) | (c >>> 16)) + d) | 0
|
||||
b += ((c ^ d ^ a) + k[6] + 76029189) | 0
|
||||
b = (((b << 23) | (b >>> 9)) + c) | 0
|
||||
a += ((b ^ c ^ d) + k[9] - 640364487) | 0
|
||||
a = (((a << 4) | (a >>> 28)) + b) | 0
|
||||
d += ((a ^ b ^ c) + k[12] - 421815835) | 0
|
||||
d = (((d << 11) | (d >>> 21)) + a) | 0
|
||||
c += ((d ^ a ^ b) + k[15] + 530742520) | 0
|
||||
c = (((c << 16) | (c >>> 16)) + d) | 0
|
||||
b += ((c ^ d ^ a) + k[2] - 995338651) | 0
|
||||
b = (((b << 23) | (b >>> 9)) + c) | 0
|
||||
|
||||
a += ((c ^ (b | ~d)) + k[0] - 198630844) | 0
|
||||
a = (((a << 6) | (a >>> 26)) + b) | 0
|
||||
d += ((b ^ (a | ~c)) + k[7] + 1126891415) | 0
|
||||
d = (((d << 10) | (d >>> 22)) + a) | 0
|
||||
c += ((a ^ (d | ~b)) + k[14] - 1416354905) | 0
|
||||
c = (((c << 15) | (c >>> 17)) + d) | 0
|
||||
b += ((d ^ (c | ~a)) + k[5] - 57434055) | 0
|
||||
b = (((b << 21) | (b >>> 11)) + c) | 0
|
||||
a += ((c ^ (b | ~d)) + k[12] + 1700485571) | 0
|
||||
a = (((a << 6) | (a >>> 26)) + b) | 0
|
||||
d += ((b ^ (a | ~c)) + k[3] - 1894986606) | 0
|
||||
d = (((d << 10) | (d >>> 22)) + a) | 0
|
||||
c += ((a ^ (d | ~b)) + k[10] - 1051523) | 0
|
||||
c = (((c << 15) | (c >>> 17)) + d) | 0
|
||||
b += ((d ^ (c | ~a)) + k[1] - 2054922799) | 0
|
||||
b = (((b << 21) | (b >>> 11)) + c) | 0
|
||||
a += ((c ^ (b | ~d)) + k[8] + 1873313359) | 0
|
||||
a = (((a << 6) | (a >>> 26)) + b) | 0
|
||||
d += ((b ^ (a | ~c)) + k[15] - 30611744) | 0
|
||||
d = (((d << 10) | (d >>> 22)) + a) | 0
|
||||
c += ((a ^ (d | ~b)) + k[6] - 1560198380) | 0
|
||||
c = (((c << 15) | (c >>> 17)) + d) | 0
|
||||
b += ((d ^ (c | ~a)) + k[13] + 1309151649) | 0
|
||||
b = (((b << 21) | (b >>> 11)) + c) | 0
|
||||
a += ((c ^ (b | ~d)) + k[4] - 145523070) | 0
|
||||
a = (((a << 6) | (a >>> 26)) + b) | 0
|
||||
d += ((b ^ (a | ~c)) + k[11] - 1120210379) | 0
|
||||
d = (((d << 10) | (d >>> 22)) + a) | 0
|
||||
c += ((a ^ (d | ~b)) + k[2] + 718787259) | 0
|
||||
c = (((c << 15) | (c >>> 17)) + d) | 0
|
||||
b += ((d ^ (c | ~a)) + k[9] - 343485551) | 0
|
||||
b = (((b << 21) | (b >>> 11)) + c) | 0
|
||||
|
||||
x[0] = (a + x[0]) | 0
|
||||
x[1] = (b + x[1]) | 0
|
||||
x[2] = (c + x[2]) | 0
|
||||
x[3] = (d + x[3]) | 0
|
||||
}
|
||||
|
||||
function md5blk(s) {
|
||||
var md5blks = [],
|
||||
i /* Andy King said do it this way. */
|
||||
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] =
|
||||
s.charCodeAt(i) +
|
||||
(s.charCodeAt(i + 1) << 8) +
|
||||
(s.charCodeAt(i + 2) << 16) +
|
||||
(s.charCodeAt(i + 3) << 24)
|
||||
}
|
||||
return md5blks
|
||||
}
|
||||
|
||||
function md5blk_array(a) {
|
||||
var md5blks = [],
|
||||
i /* Andy King said do it this way. */
|
||||
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] =
|
||||
a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24)
|
||||
}
|
||||
return md5blks
|
||||
}
|
||||
|
||||
function md51(s) {
|
||||
var n = s.length,
|
||||
state = [1732584193, -271733879, -1732584194, 271733878],
|
||||
i,
|
||||
length,
|
||||
tail,
|
||||
tmp,
|
||||
lo,
|
||||
hi
|
||||
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk(s.substring(i - 64, i)))
|
||||
}
|
||||
s = s.substring(i - 64)
|
||||
length = s.length
|
||||
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3)
|
||||
}
|
||||
tail[i >> 2] |= 0x80 << (i % 4 << 3)
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail)
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Beware that the final length might not fit in 32 bits so we take care of that
|
||||
tmp = n * 8
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/)
|
||||
lo = parseInt(tmp[2], 16)
|
||||
hi = parseInt(tmp[1], 16) || 0
|
||||
|
||||
tail[14] = lo
|
||||
tail[15] = hi
|
||||
|
||||
md5cycle(state, tail)
|
||||
return state
|
||||
}
|
||||
|
||||
function md51_array(a) {
|
||||
var n = a.length,
|
||||
state = [1732584193, -271733879, -1732584194, 271733878],
|
||||
i,
|
||||
length,
|
||||
tail,
|
||||
tmp,
|
||||
lo,
|
||||
hi
|
||||
|
||||
for (i = 64; i <= n; i += 64) {
|
||||
md5cycle(state, md5blk_array(a.subarray(i - 64, i)))
|
||||
}
|
||||
|
||||
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1
|
||||
// containing the last element of the parent array if the sub array specified starts
|
||||
// beyond the length of the parent array - weird.
|
||||
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
|
||||
a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0)
|
||||
|
||||
length = a.length
|
||||
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= a[i] << (i % 4 << 3)
|
||||
}
|
||||
|
||||
tail[i >> 2] |= 0x80 << (i % 4 << 3)
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail)
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Beware that the final length might not fit in 32 bits so we take care of that
|
||||
tmp = n * 8
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/)
|
||||
lo = parseInt(tmp[2], 16)
|
||||
hi = parseInt(tmp[1], 16) || 0
|
||||
|
||||
tail[14] = lo
|
||||
tail[15] = hi
|
||||
|
||||
md5cycle(state, tail)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
function rhex(n) {
|
||||
var s = '',
|
||||
j
|
||||
for (j = 0; j < 4; j += 1) {
|
||||
s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
function hex(x) {
|
||||
var i
|
||||
for (i = 0; i < x.length; i += 1) {
|
||||
x[i] = rhex(x[i])
|
||||
}
|
||||
return x.join('')
|
||||
}
|
||||
|
||||
// In some cases the fast add32 function cannot be used..
|
||||
if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
|
||||
add32 = function(x, y) {
|
||||
var lsw = (x & 0xffff) + (y & 0xffff),
|
||||
msw = (x >> 16) + (y >> 16) + (lsw >> 16)
|
||||
return (msw << 16) | (lsw & 0xffff)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
/**
|
||||
* ArrayBuffer slice polyfill.
|
||||
*
|
||||
* @see https://github.com/ttaubert/node-arraybuffer-slice
|
||||
*/
|
||||
|
||||
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
|
||||
;(function() {
|
||||
function clamp(val, length) {
|
||||
val = val | 0 || 0
|
||||
|
||||
if (val < 0) {
|
||||
return Math.max(val + length, 0)
|
||||
}
|
||||
|
||||
return Math.min(val, length)
|
||||
}
|
||||
|
||||
ArrayBuffer.prototype.slice = function(from, to) {
|
||||
var length = this.byteLength,
|
||||
begin = clamp(from, length),
|
||||
end = length,
|
||||
num,
|
||||
target,
|
||||
targetArray,
|
||||
sourceArray
|
||||
|
||||
if (to !== undefined) {
|
||||
end = clamp(to, length)
|
||||
}
|
||||
|
||||
if (begin > end) {
|
||||
return new ArrayBuffer(0)
|
||||
}
|
||||
|
||||
num = end - begin
|
||||
target = new ArrayBuffer(num)
|
||||
targetArray = new Uint8Array(target)
|
||||
|
||||
sourceArray = new Uint8Array(this, begin, num)
|
||||
targetArray.set(sourceArray)
|
||||
|
||||
return target
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
/**
|
||||
* Helpers.
|
||||
*/
|
||||
|
||||
function toUtf8(str) {
|
||||
str += ''
|
||||
if (/[\u0080-\uFFFF]/.test(str)) {
|
||||
str = unescape(encodeURIComponent(str))
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
||||
var length = str.length,
|
||||
buff = new ArrayBuffer(length),
|
||||
arr = new Uint8Array(buff),
|
||||
i
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
arr[i] = str.charCodeAt(i)
|
||||
}
|
||||
|
||||
return returnUInt8Array ? arr : buff
|
||||
}
|
||||
|
||||
function arrayBuffer2Utf8Str(buff) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buff))
|
||||
}
|
||||
|
||||
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
||||
var result = new Uint8Array(first.byteLength + second.byteLength)
|
||||
|
||||
result.set(new Uint8Array(first))
|
||||
result.set(new Uint8Array(second), first.byteLength)
|
||||
|
||||
return returnUInt8Array ? result : result.buffer
|
||||
}
|
||||
|
||||
function hexToBinaryString(hex) {
|
||||
var bytes = [],
|
||||
length = hex.length,
|
||||
x
|
||||
|
||||
for (x = 0; x < length - 1; x += 2) {
|
||||
bytes.push(parseInt(hex.substr(x, 2), 16))
|
||||
}
|
||||
|
||||
return String.fromCharCode.apply(String, bytes)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
/**
|
||||
* SparkMD5 OOP implementation.
|
||||
*
|
||||
* Use this class to perform an incremental md5, otherwise use the
|
||||
* static methods instead.
|
||||
*/
|
||||
|
||||
function SparkMD5() {
|
||||
// call reset to init the instance
|
||||
this.reset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string.
|
||||
* A conversion will be applied if an utf8 string is detected.
|
||||
*
|
||||
* @param {String} str The string to be appended
|
||||
*
|
||||
* @return {SparkMD5} The instance itself
|
||||
*/
|
||||
SparkMD5.prototype.append = function(str) {
|
||||
// Converts the string to utf8 bytes if necessary
|
||||
// Then append as binary
|
||||
this.appendBinary(toUtf8(str))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a binary string.
|
||||
*
|
||||
* @param {String} contents The binary string to be appended
|
||||
*
|
||||
* @return {SparkMD5} The instance itself
|
||||
*/
|
||||
SparkMD5.prototype.appendBinary = function(contents) {
|
||||
this._buff += contents
|
||||
this._length += contents.length
|
||||
|
||||
var length = this._buff.length,
|
||||
i
|
||||
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)))
|
||||
}
|
||||
|
||||
this._buff = this._buff.substring(i - 64)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the incremental computation, reseting the internal state and
|
||||
* returning the result.
|
||||
*
|
||||
* @param {Boolean} raw True to get the raw string, false to get the hex string
|
||||
*
|
||||
* @return {String} The result
|
||||
*/
|
||||
SparkMD5.prototype.end = function(raw) {
|
||||
var buff = this._buff,
|
||||
length = buff.length,
|
||||
i,
|
||||
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
ret
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3)
|
||||
}
|
||||
|
||||
this._finish(tail, length)
|
||||
ret = hex(this._hash)
|
||||
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret)
|
||||
}
|
||||
|
||||
this.reset()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state of the computation.
|
||||
*
|
||||
* @return {SparkMD5} The instance itself
|
||||
*/
|
||||
SparkMD5.prototype.reset = function() {
|
||||
this._buff = ''
|
||||
this._length = 0
|
||||
this._hash = [1732584193, -271733879, -1732584194, 271733878]
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal state of the computation.
|
||||
*
|
||||
* @return {Object} The state
|
||||
*/
|
||||
SparkMD5.prototype.getState = function() {
|
||||
return {
|
||||
buff: this._buff,
|
||||
length: this._length,
|
||||
hash: this._hash
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal state of the computation.
|
||||
*
|
||||
* @param {Object} state The state
|
||||
*
|
||||
* @return {SparkMD5} The instance itself
|
||||
*/
|
||||
SparkMD5.prototype.setState = function(state) {
|
||||
this._buff = state.buff
|
||||
this._length = state.length
|
||||
this._hash = state.hash
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases memory used by the incremental buffer and other additional
|
||||
* resources. If you plan to use the instance again, use reset instead.
|
||||
*/
|
||||
SparkMD5.prototype.destroy = function() {
|
||||
delete this._hash
|
||||
delete this._buff
|
||||
delete this._length
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the final calculation based on the tail.
|
||||
*
|
||||
* @param {Array} tail The tail (will be modified)
|
||||
* @param {Number} length The length of the remaining buffer
|
||||
*/
|
||||
SparkMD5.prototype._finish = function(tail, length) {
|
||||
var i = length,
|
||||
tmp,
|
||||
lo,
|
||||
hi
|
||||
|
||||
tail[i >> 2] |= 0x80 << (i % 4 << 3)
|
||||
if (i > 55) {
|
||||
md5cycle(this._hash, tail)
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
tail[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Do the final computation based on the tail and length
|
||||
// Beware that the final length may not fit in 32 bits so we take care of that
|
||||
tmp = this._length * 8
|
||||
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/)
|
||||
lo = parseInt(tmp[2], 16)
|
||||
hi = parseInt(tmp[1], 16) || 0
|
||||
|
||||
tail[14] = lo
|
||||
tail[15] = hi
|
||||
md5cycle(this._hash, tail)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the md5 hash on a string.
|
||||
* A conversion will be applied if utf8 string is detected.
|
||||
*
|
||||
* @param {String} str The string
|
||||
* @param {Boolean} [raw] True to get the raw string, false to get the hex string
|
||||
*
|
||||
* @return {String} The result
|
||||
*/
|
||||
SparkMD5.hash = function(str, raw) {
|
||||
// Converts the string to utf8 bytes if necessary
|
||||
// Then compute it using the binary function
|
||||
return SparkMD5.hashBinary(toUtf8(str), raw)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the md5 hash on a binary string.
|
||||
*
|
||||
* @param {String} content The binary string
|
||||
* @param {Boolean} [raw] True to get the raw string, false to get the hex string
|
||||
*
|
||||
* @return {String} The result
|
||||
*/
|
||||
SparkMD5.hashBinary = function(content, raw) {
|
||||
var hash = md51(content),
|
||||
ret = hex(hash)
|
||||
|
||||
return raw ? hexToBinaryString(ret) : ret
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
/**
|
||||
* SparkMD5 OOP implementation for array buffers.
|
||||
*
|
||||
* Use this class to perform an incremental md5 ONLY for array buffers.
|
||||
*/
|
||||
SparkMD5.ArrayBuffer = function() {
|
||||
// call reset to init the instance
|
||||
this.reset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an array buffer.
|
||||
*
|
||||
* @param {ArrayBuffer} arr The array to be appended
|
||||
*
|
||||
* @return {SparkMD5.ArrayBuffer} The instance itself
|
||||
*/
|
||||
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
|
||||
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
|
||||
length = buff.length,
|
||||
i
|
||||
|
||||
this._length += arr.byteLength
|
||||
|
||||
for (i = 64; i <= length; i += 64) {
|
||||
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)))
|
||||
}
|
||||
|
||||
this._buff =
|
||||
i - 64 < length
|
||||
? new Uint8Array(buff.buffer.slice(i - 64))
|
||||
: new Uint8Array(0)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the incremental computation, reseting the internal state and
|
||||
* returning the result.
|
||||
*
|
||||
* @param {Boolean} raw True to get the raw string, false to get the hex string
|
||||
*
|
||||
* @return {String} The result
|
||||
*/
|
||||
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
|
||||
var buff = this._buff,
|
||||
length = buff.length,
|
||||
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
i,
|
||||
ret
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
tail[i >> 2] |= buff[i] << (i % 4 << 3)
|
||||
}
|
||||
|
||||
this._finish(tail, length)
|
||||
ret = hex(this._hash)
|
||||
|
||||
if (raw) {
|
||||
ret = hexToBinaryString(ret)
|
||||
}
|
||||
|
||||
this.reset()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state of the computation.
|
||||
*
|
||||
* @return {SparkMD5.ArrayBuffer} The instance itself
|
||||
*/
|
||||
SparkMD5.ArrayBuffer.prototype.reset = function() {
|
||||
this._buff = new Uint8Array(0)
|
||||
this._length = 0
|
||||
this._hash = [1732584193, -271733879, -1732584194, 271733878]
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal state of the computation.
|
||||
*
|
||||
* @return {Object} The state
|
||||
*/
|
||||
SparkMD5.ArrayBuffer.prototype.getState = function() {
|
||||
var state = SparkMD5.prototype.getState.call(this)
|
||||
|
||||
// Convert buffer to a string
|
||||
state.buff = arrayBuffer2Utf8Str(state.buff)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal state of the computation.
|
||||
*
|
||||
* @param {Object} state The state
|
||||
*
|
||||
* @return {SparkMD5.ArrayBuffer} The instance itself
|
||||
*/
|
||||
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
|
||||
// Convert string to buffer
|
||||
state.buff = utf8Str2ArrayBuffer(state.buff, true)
|
||||
|
||||
return SparkMD5.prototype.setState.call(this, state)
|
||||
}
|
||||
|
||||
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy
|
||||
|
||||
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish
|
||||
|
||||
/**
|
||||
* Performs the md5 hash on an array buffer.
|
||||
*
|
||||
* @param {ArrayBuffer} arr The array buffer
|
||||
* @param {Boolean} [raw] True to get the raw string, false to get the hex one
|
||||
*
|
||||
* @return {String} The result
|
||||
*/
|
||||
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
|
||||
var hash = md51_array(new Uint8Array(arr)),
|
||||
ret = hex(hash)
|
||||
|
||||
return raw ? hexToBinaryString(ret) : ret
|
||||
}
|
||||
|
||||
var _sparkIns = new SparkMD5()
|
||||
|
||||
export function md5(str) {
|
||||
_sparkIns.append(str)
|
||||
return _sparkIns.end()
|
||||
}
|
||||
|
||||
export function md5Sum(binStr) {
|
||||
_sparkIns.appendBinary(binStr)
|
||||
return _sparkIns.end()
|
||||
}
|
||||
|
||||
export default _sparkIns
|
|
@ -1,219 +0,0 @@
|
|||
.CodeMirror {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #272822;
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.CodeMirror-scroll {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
outline: 0;
|
||||
}
|
||||
.CodeMirror-scrollbar {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
z-index: 5;
|
||||
}
|
||||
.CodeMirror-scrollbar-inner {
|
||||
width: 1px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-overlap {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
float: none;
|
||||
right: 0;
|
||||
min-width: 12px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-nonoverlap {
|
||||
min-width: 12px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-ie7 {
|
||||
min-width: 18px;
|
||||
}
|
||||
.CodeMirror-gutter {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background-color: transparent;
|
||||
border-right: 1px solid #454545;
|
||||
min-width: 2em;
|
||||
height: 100%;
|
||||
}
|
||||
.CodeMirror-gutter-text {
|
||||
color: #aaa;
|
||||
text-align: right;
|
||||
padding: 0.4em 0.2em 0.4em 0.4em;
|
||||
white-space: pre !important;
|
||||
cursor: default;
|
||||
}
|
||||
.CodeMirror-lines {
|
||||
padding: 0.4em;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
}
|
||||
.CodeMirror pre {
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-o-border-radius: 0;
|
||||
border-radius: 0;
|
||||
border-width: 0;
|
||||
background: 0 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
overflow: visible;
|
||||
}
|
||||
.CodeMirror-wrap pre {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
.CodeMirror-wrap .CodeMirror-scroll {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.CodeMirror textarea {
|
||||
outline: 0 !important;
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-cursor {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
border-left: 1px solid #9effff;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
background: 0 0;
|
||||
background: rgba(0, 200, 0, 0.4);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(
|
||||
startColorstr=#6600c800,
|
||||
endColorstr=#4c00c800
|
||||
);
|
||||
}
|
||||
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
}
|
||||
.CodeMirror-focused pre.CodeMirror-cursor {
|
||||
visibility: visible;
|
||||
}
|
||||
.CodeMirror-focused div.CodeMirror-selected,
|
||||
div.CodeMirror-selected {
|
||||
background: #49483e;
|
||||
}
|
||||
.CodeMirror-searching {
|
||||
background: #ffa;
|
||||
background: rgba(255, 255, 0, 0.4);
|
||||
}
|
||||
.cm-s-default span.cm-keyword {
|
||||
color: #f92672;
|
||||
}
|
||||
.cm-s-default span.cm-atom {
|
||||
color: #ae81ff;
|
||||
}
|
||||
.cm-s-default span.cm-number {
|
||||
color: #f30;
|
||||
}
|
||||
.cm-s-default span.cm-def {
|
||||
color: #fd971f;
|
||||
}
|
||||
.cm-s-default span.cm-variable {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.cm-s-default span.cm-variable-2 {
|
||||
color: #9effff;
|
||||
}
|
||||
.cm-s-default span.cm-property,
|
||||
.cm-s-default span.cm-variable-3 {
|
||||
color: #66d9ef;
|
||||
}
|
||||
.cm-s-default span.cm-operator {
|
||||
color: #9effff;
|
||||
}
|
||||
.cm-s-default span.cm-comment {
|
||||
color: #75715e;
|
||||
}
|
||||
.cm-s-default span.cm-string {
|
||||
color: #e6db74;
|
||||
}
|
||||
.cm-s-default span.cm-string-2 {
|
||||
color: #f50;
|
||||
}
|
||||
.cm-s-default span.cm-meta {
|
||||
color: #555;
|
||||
}
|
||||
.cm-s-default span.cm-error {
|
||||
background: #f92672;
|
||||
color: #f8f8f0;
|
||||
}
|
||||
.cm-s-default span.cm-qualifier {
|
||||
color: #75d908;
|
||||
}
|
||||
.cm-s-default span.cm-builtin {
|
||||
color: #66d9ef;
|
||||
}
|
||||
.cm-s-default span.cm-bracket {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.cm-s-default span.cm-tag {
|
||||
color: #f92672;
|
||||
}
|
||||
.cm-s-default span.cm-attribute {
|
||||
color: #a6e22e;
|
||||
}
|
||||
.cm-s-default span.cm-header {
|
||||
color: #ae81ff;
|
||||
}
|
||||
.cm-s-default span.cm-quote {
|
||||
color: #090;
|
||||
}
|
||||
.cm-s-default span.cm-hr {
|
||||
color: #999;
|
||||
}
|
||||
.cm-s-default span.cm-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
span.cm-header,
|
||||
span.cm-strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
span.cm-em {
|
||||
font-style: italic;
|
||||
}
|
||||
span.cm-emstrong {
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
}
|
||||
span.cm-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
span.cm-invalidchar {
|
||||
color: red;
|
||||
}
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {
|
||||
text-decoration: underline;
|
||||
color: #fff !important;
|
||||
}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
||||
color: #f22;
|
||||
}
|
||||
@media print {
|
||||
.CodeMirror pre.CodeMirror-cursor {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
.CodeMirror {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.CodeMirror-scroll {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
outline: 0;
|
||||
}
|
||||
.CodeMirror-scrollbar {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
z-index: 5;
|
||||
}
|
||||
.CodeMirror-scrollbar-inner {
|
||||
width: 1px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-overlap {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
float: none;
|
||||
right: 0;
|
||||
min-width: 12px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-nonoverlap {
|
||||
min-width: 12px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-ie7 {
|
||||
min-width: 18px;
|
||||
}
|
||||
.CodeMirror-gutter {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background-color: transparent;
|
||||
border-right: 1px solid #454545;
|
||||
min-width: 2em;
|
||||
height: 100%;
|
||||
}
|
||||
.CodeMirror-gutter-text {
|
||||
color: #aaa;
|
||||
text-align: right;
|
||||
padding: 0.4em 0.2em 0.4em 0.4em;
|
||||
white-space: pre !important;
|
||||
cursor: default;
|
||||
}
|
||||
.CodeMirror-lines {
|
||||
padding: 0.4em;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
}
|
||||
.CodeMirror pre {
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-o-border-radius: 0;
|
||||
border-radius: 0;
|
||||
border-width: 0;
|
||||
background: 0 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
overflow: visible;
|
||||
}
|
||||
.CodeMirror-wrap pre {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
.CodeMirror-wrap .CodeMirror-scroll {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.CodeMirror textarea {
|
||||
outline: 0 !important;
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-cursor {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
border-left: 1px solid #f00;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
background: 0 0;
|
||||
background: rgba(0, 200, 0, 0.4);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(
|
||||
startColorstr=#6600c800,
|
||||
endColorstr=#4c00c800
|
||||
);
|
||||
}
|
||||
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
}
|
||||
.CodeMirror-focused pre.CodeMirror-cursor {
|
||||
visibility: visible;
|
||||
}
|
||||
.CodeMirror-focused div.CodeMirror-selected,
|
||||
div.CodeMirror-selected {
|
||||
background: #49483e;
|
||||
}
|
||||
.CodeMirror-searching {
|
||||
background: #ffa;
|
||||
background: rgba(255, 255, 0, 0.4);
|
||||
}
|
||||
.cm-s-default span.cm-keyword {
|
||||
color: #f92672;
|
||||
}
|
||||
.cm-s-default span.cm-atom {
|
||||
color: #ae81ff;
|
||||
}
|
||||
.cm-s-default span.cm-number {
|
||||
color: #f30;
|
||||
}
|
||||
.cm-s-default span.cm-def {
|
||||
color: #fd971f;
|
||||
}
|
||||
.cm-s-default span.cm-variable {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.cm-s-default span.cm-variable-2 {
|
||||
color: #05a;
|
||||
}
|
||||
.cm-s-default span.cm-property,
|
||||
.cm-s-default span.cm-variable-3 {
|
||||
color: #3298dc;
|
||||
}
|
||||
.cm-s-default span.cm-operator {
|
||||
color: #9effff;
|
||||
}
|
||||
.cm-s-default span.cm-comment {
|
||||
border: 1px solid #eee;
|
||||
color: #75715e;
|
||||
}
|
||||
.cm-s-default span.cm-string {
|
||||
color: #a11;
|
||||
}
|
||||
.cm-s-default span.cm-string-2 {
|
||||
color: #f50;
|
||||
}
|
||||
.cm-s-default span.cm-meta {
|
||||
color: #555;
|
||||
}
|
||||
.cm-s-default span.cm-error {
|
||||
background: #f92672;
|
||||
color: #f8f8f0;
|
||||
}
|
||||
.cm-s-default span.cm-qualifier {
|
||||
color: #75d908;
|
||||
}
|
||||
.cm-s-default span.cm-builtin {
|
||||
color: #66d9ef;
|
||||
}
|
||||
.cm-s-default span.cm-bracket {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.cm-s-default span.cm-tag {
|
||||
color: #13b65a;
|
||||
}
|
||||
.cm-s-default span.cm-attribute {
|
||||
color: #a6e22e;
|
||||
}
|
||||
.cm-s-default span.cm-header {
|
||||
color: #f92672;
|
||||
}
|
||||
.cm-s-default span.cm-quote {
|
||||
color: #666;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.cm-s-default span.cm-hr {
|
||||
color: #999;
|
||||
}
|
||||
.cm-s-default span.cm-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
.cm-s-default span.cm-strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
span.cm-header,
|
||||
span.cm-strong {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
span.cm-em {
|
||||
font-style: italic;
|
||||
color: #f90;
|
||||
}
|
||||
span.cm-emstrong {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
span.cm-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
span.cm-invalidchar {
|
||||
color: red;
|
||||
}
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {
|
||||
text-decoration: underline;
|
||||
color: #fff !important;
|
||||
}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
||||
color: #f22;
|
||||
}
|
||||
@media print {
|
||||
.CodeMirror pre.CodeMirror-cursor {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
.do-meditor-attach{width:630px;height:300px;cursor:default;color:#526273}.do-meditor-attach ::-webkit-scrollbar{width:5px;height:5px;background:#f2f5fc}.do-meditor-attach ::-webkit-scrollbar:hover{background:#e8ebf4}.do-meditor-attach ::-webkit-scrollbar-button{display:none}.do-meditor-attach ::-webkit-scrollbar-thumb{background:#bdbdbd}.do-meditor-attach ::-webkit-scrollbar-thumb:hover{background:#9e9e9e}.do-meditor-attach .tab-box{float:left;width:130px;height:300px;padding:10px 5px;text-align:center;background:#f2f5fc;border-radius:5px}.do-meditor-attach .tab-box .item{display:block;width:100%;height:40px;line-height:40px;border-radius:3px;cursor:pointer}.do-meditor-attach .tab-box .item.active{background:#fff}.do-meditor-attach .cont-box{position:relative;float:right;width:480px;height:auto;min-height:200px}.do-meditor-attach .cont-box .remote,.do-meditor-attach .cont-box .local{position:relative;width:92%;height:auto;margin:0 auto}.do-meditor-attach .cont-box .remote{padding:30px 0}.do-meditor-attach .cont-box .hide{display:none}.do-meditor-attach .cont-box .section{display:block;width:100%;height:auto;margin:15px 0;line-height:35px}.do-meditor-attach .cont-box .section .submit{float:right;width:30%;height:45px;line-height:45px}.do-meditor-attach .cont-box .select-file{width:100%;height:35px;line-height:35px}.do-meditor-attach .cont-box .select-file .file{float:left;width:100px;height:35px;border-radius:3px;background:#e8ebf4;text-align:center;cursor:pointer}.do-meditor-attach .cont-box .select-file .file:hover{background:#f2f5fc}.do-meditor-attach .cont-box .select-file .file:active{background:#dae1e9}.do-meditor-attach .cont-box .select-file .tips{display:inline-block;padding:0 10px}.do-meditor-attach .cont-box .upload-box{width:100%;height:auto;min-height:255px;padding-top:10px}.do-meditor-attach .cont-box .upload-box .thead{width:100%;height:35px;line-height:35px;background:#f2f5fc}.do-meditor-attach .cont-box .upload-box .col{overflow:hidden;float:left;height:30px;padding:0 5px;text-align:center}.do-meditor-attach .cont-box .upload-box .col:nth-child(1){width:50%}.do-meditor-attach .cont-box .upload-box .col:nth-child(2){width:35%}.do-meditor-attach .cont-box .upload-box .col:nth-child(3){width:15%}.do-meditor-attach .cont-box .upload-box .tbody{overflow:hidden;overflow-y:auto;width:100%;height:220px}.do-meditor-attach .cont-box .upload-box .tbody p{display:block;width:100%;height:30px;line-height:30px}.do-meditor-attach .cont-box .upload-box .insert{display:inline-block;padding:3px 5px;line-height:13px;background:#f2f5fc;color:#526273;cursor:pointer}.do-meditor-attach .cont-box .upload-box .red{color:#f30}.do-meditor-attach .cont-box .manager{overflow:hidden;overflow-y:auto;width:100%;height:300px}.do-meditor-attach .cont-box .manager .list-box{width:100%;-webkit-column-count:4;column-count:4;-webkit-column-gap:5;column-gap:5}.do-meditor-attach .cont-box .manager .item{margin:0 0 10px;padding:5px;text-align:center;background:#f2f5fc;-webkit-column-break-inside:avoid;break-inside:avoid;transition:background .2s ease-in-out}.do-meditor-attach .cont-box .manager .item:hover{background:#dae1e9}.do-meditor-attach .cont-box .manager .item .thumb{display:block;width:100%}.do-meditor-attach .cont-box .manager .item .name{overflow:hidden;height:30px;line-height:30px;text-align:center}.do-meditor-attach .cont-box .manager .item img{width:100%;height:auto}.do-meditor-attach .cont-box .manager .item em{line-height:1;font-size:50px}
|
|
@ -1,84 +0,0 @@
|
|||
@charset "UTF-8";
|
||||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-20 19:13:24
|
||||
*
|
||||
*/
|
||||
@import 'var.scss';
|
||||
|
||||
.do-meditor-attach {width:630px;height:300px;cursor:default;color:nth($cd, 2);
|
||||
|
||||
::-webkit-scrollbar {width:5px;height:5px;background:nth($cp, 1);}
|
||||
::-webkit-scrollbar:hover {background:nth($cp, 2);}
|
||||
::-webkit-scrollbar-button {display:none;}
|
||||
::-webkit-scrollbar-thumb {background:nth($cgr, 1);}
|
||||
::-webkit-scrollbar-thumb:hover {background:nth($cgr, 2);}
|
||||
|
||||
.tab-box {float:left;width:130px;height:300px;padding:10px 5px;text-align:center;background:nth($cp, 1);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: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;
|
||||
|
||||
.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, 2);text-align:center;cursor:pointer;
|
||||
|
||||
&:hover {background:nth($cp, 1);}
|
||||
&: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, 1);}
|
||||
|
||||
.col {overflow:hidden;float:left;height:30px;padding:0 5px;text-align:center;}
|
||||
.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, 1);color:nth($cd, 2);cursor:pointer;}
|
||||
.red {color:#f30;}
|
||||
}
|
||||
|
||||
.manager {overflow:hidden;overflow-y:auto;width:100%;height:300px;
|
||||
|
||||
.list-box {width:100%;column-count:4;column-gap: 5;}
|
||||
.item {margin:0 0 10px;padding:5px;text-align:center;background:nth($cp, 1); break-inside: avoid;@include ts(background);
|
||||
|
||||
&:hover {background:nth($cp, 3)}
|
||||
|
||||
.thumb {display:block;width:100%;}
|
||||
.name {overflow:hidden;height:30px;line-height:30px; text-align:center;}
|
||||
img {width:100%;height:auto;}
|
||||
em {line-height:1;font-size:50px;}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
@charset "UTF-8";
|
||||
/**
|
||||
*
|
||||
* @authors Lincoln (875482941@qq.com)
|
||||
* @date 2017-09-07 20:32:11
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
@import "var.scss";
|
||||
|
||||
.do-sliders {position: relative;height: 100%;width: 100%;
|
||||
.skin{height: 100%}
|
||||
&.fullscreen{position: absolute;width: 100%;height: 100%;left: 0;top: 0;}
|
||||
.slider-content{position: relative;
|
||||
|
||||
.container {position: relative;height: 100%;overflow: hidden;
|
||||
.box {position: relative;height: 100%;width: 100%;transition: .4s;
|
||||
.page {position:relative;display: inline-block;height: 100%;vertical-align: middle;
|
||||
img {width: 100%;height: 100%;object-fit: cover;}
|
||||
.title-class {position: absolute;width: 30%;min-height: 30px;line-height: 30px;left: 0;bottom: 15%;right: 0;margin: auto;text-align: center;font-size: 25px;}
|
||||
.elm{height: 100%;width: 100%;background: #333;}
|
||||
.default-elm{width: 100%;height: 100%;}
|
||||
.default-btn{width: 100%;height: 100%;}
|
||||
}
|
||||
.fadeType{position: absolute;left: 0;top: 0;width: 100%;transition: .4s;}
|
||||
}
|
||||
}
|
||||
|
||||
.slider-btn {position: absolute;top: 50%;min-width: 50px;min-height: 50px;margin-top: -25px;text-decoration: none;font-size: 30px;line-height: 50px;text-align: center;border-radius: 100%;color: rgba(255,255,255,.6);font-weight: bold;z-index: 1;transition: .4s;}
|
||||
// .slider-btn:hover {color: #fff;}
|
||||
.slider-btn:nth-of-type(1){margin-left: 20px;}
|
||||
// .slider-btn:nth-of-type(1):hover{animation: left-to-right .3s;}
|
||||
.slider-btn:nth-of-type(2){right: 0;margin-right: 20px;}
|
||||
// .slider-btn:nth-of-type(2):hover{animation: right-to-left .3s;}
|
||||
}
|
||||
|
||||
.slider-preview-btn {position: absolute;bottom: 2%;height: 12%;width: 100%;margin: 0 auto;text-align: center;overflow: hidden;
|
||||
|
||||
span {display: inline-block;width: 10px;height: 10px;margin: 0 5px;border-radius: 100%;background: rgba(255,255,255,0.8);cursor: pointer;transition: .5s;}
|
||||
span:hover{background:nth($cg, 1);}
|
||||
.no-preview-act{background:nth($cg, 3);}
|
||||
|
||||
.btn-group{height: 100%;}
|
||||
|
||||
.btn-img{display: inline-block;position: relative;width: 80px;height: 100%;margin: 0 5px;transition: .4s;cursor: pointer;
|
||||
img {position: relative;width: 100%;height: 100%;object-fit: cover;transition: .4s;z-index: 2;}
|
||||
.preview-act{transform: scale(.95)}
|
||||
}
|
||||
.btn-img:after {content: '';position: absolute;left: 0;top: 0;width: 100%;height: 100%;opacity: 0;transition: .8s;z-index: 1;}
|
||||
.btn-img.act:after {opacity: 1;}
|
||||
}
|
||||
.slider-preview-btn-vertical{right: 10px;bottom: 50%;width: 20px;height: auto;text-align: center;transform: translate(0, 50%);}
|
||||
|
||||
.skin-0 .title-class{color: #e0e0e0}
|
||||
.skin-1 .title-class{color: nth($cr, 3)}
|
||||
.skin-2 .title-class{color: nth($cg, 3)}
|
||||
.skin-3 .title-class{color: nth($cb, 3)}
|
||||
|
||||
.skin-0 .slider-btn{color: rgba(255,255,255,.6)}
|
||||
.skin-1 .slider-btn{color: nth($cr, 3)}
|
||||
.skin-2 .slider-btn{color: nth($cg, 3)}
|
||||
.skin-3 .slider-btn{color: nth($cb, 3)}
|
||||
|
||||
.skin-0 .slider-btn:hover{color: #e0e0e0}
|
||||
.skin-1 .slider-btn:hover{color: nth($cr, 1)}
|
||||
.skin-2 .slider-btn:hover{color: nth($cg, 1)}
|
||||
.skin-3 .slider-btn:hover{color: nth($cb, 1)}
|
||||
|
||||
.skin-0 .btn-img:after{background: rgba(0,0,0,0.5)}
|
||||
.skin-1 .btn-img:after{background: nth($cr, 1)}
|
||||
.skin-2 .btn-img:after{background: nth($cg, 1)}
|
||||
.skin-3 .btn-img:after{background: nth($cb, 1)}
|
||||
|
||||
.skin-0 .default-elm{background: #e0e0e0}
|
||||
.skin-1 .default-elm{background: nth($cr, 1)}
|
||||
.skin-2 .default-elm{background: nth($cg, 1)}
|
||||
.skin-3 .default-elm{background: nth($cb, 1)}
|
||||
|
||||
.skin-0 .default-btn{background: #e0e0e0}
|
||||
.skin-1 .default-btn{background: nth($cr, 1)}
|
||||
.skin-2 .default-btn{background: nth($cg, 1)}
|
||||
.skin-3 .default-btn{background: nth($cb, 1)}
|
||||
|
||||
.skin-0 .no-preview-act{background: rgba(0,0,0,.6)}
|
||||
.skin-1 .no-preview-act{background: nth($cr, 3)}
|
||||
.skin-2 .no-preview-act{background: nth($cg, 3)}
|
||||
.skin-3 .no-preview-act{background: nth($cb, 3)}
|
||||
|
||||
.skin-0 .slider-preview-btn span:hover{background: rgba(0,0,0,.6)}
|
||||
.skin-1 .slider-preview-btn span:hover{background: nth($cr, 3)}
|
||||
.skin-2 .slider-preview-btn span:hover{background: nth($cg, 3)}
|
||||
.skin-3 .slider-preview-btn span:hover{background: nth($cb, 3)}
|
||||
|
||||
.h-83{height: 83%;}
|
||||
.h-100{height: 100%;}
|
||||
}
|
||||
|
||||
|
||||
@keyframes right-to-left{
|
||||
49% {-webkit-transform: translate(100%)}
|
||||
50% {-webkit-transform: translate(-100%);opacity: 0;}
|
||||
100% {opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes left-to-right{
|
||||
49% {-webkit-transform: translate(-100%)}
|
||||
50% {-webkit-transform: translate(100%);opacity: 0;}
|
||||
100% {opacity: 1;}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
.do-tree{overflow:hidden;overflow-y:auto;position:relative;display:block;width:100%;height:100%;line-height:30px;font-size:15px;color:#526273}.do-tree__item{overflow:hidden;min-height:30px}.do-tree__item .sub-tree{display:none;width:100%;padding-left:20px}.do-tree__item em,.do-tree__item span{display:block;cursor:pointer}.do-tree__item em{float:left;padding:0 5px;font-size:20px;color:#62778d}.do-tree__item span:hover{color:#62778d}.do-tree__item span.active{color:#425064;font-weight:bold}.do-tree__item span.checkbox{float:left;position:relative;width:18px;height:18px;margin:6px 5px 6px 0;line-height:16px;border:1px solid #526273;border-radius:3px;font-size:16px;text-align:center}.do-tree__item span.label{white-space:nowrap;text-overflow:ellipsis}.do-tree__item.open>.sub-tree{display:block}
|
|
@ -1,31 +0,0 @@
|
|||
@charset "UTF-8";
|
||||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-14 21:18:53
|
||||
*
|
||||
*/
|
||||
|
||||
@import 'var.scss';
|
||||
|
||||
|
||||
.do-tree {overflow:hidden;overflow-y:auto;position:relative;display:block;width:100%;height:100%;line-height:30px;font-size:15px;color:nth($cd, 2);
|
||||
|
||||
&__item {overflow:hidden; min-height:30px;
|
||||
|
||||
.sub-tree {display:none;width:100%;padding-left:20px;}
|
||||
|
||||
em,span {display:block;cursor:pointer;}
|
||||
em {float:left;padding:0 5px;font-size:20px;color:nth($cd, 1);}
|
||||
|
||||
span {
|
||||
|
||||
&:hover {color:nth($cd, 1);}
|
||||
&.active {color:nth($cd, 3);font-weight:bold;}
|
||||
&.checkbox {float:left;position:relative;width:18px;height:18px;margin:6px 5px 6px 0;line-height:16px;border:1px solid nth($cd, 2);border-radius:3px; font-size:16px;text-align:center;}
|
||||
&.label { white-space:nowrap; text-overflow:ellipsis;}
|
||||
}
|
||||
}
|
||||
&__item.open>.sub-tree {display:block;}
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
$ct: #4db6ac #26a69a #009688;
|
||||
$cg: #81c784 #66bb6a #4caf50;
|
||||
$cpp: #ba68c8 #ba68c8 #9c27b0;
|
||||
$cb: #64b5f6 #42a5f5 #2196f3;
|
||||
$cr: #ff5061 #eb3b48 #ce3742;
|
||||
$co: #ffb618 #f39c12 #e67e22;
|
||||
$cp: #f2f5fc #e8ebf4 #dae1e9;
|
||||
$cgr: #bdbdbd #9e9e9e #757575;
|
||||
$cd: #62778d #526273 #425064;
|
||||
|
||||
@mixin ts($c: all, $t: .2s, $m: ease-in-out){
|
||||
transition:$c $t $m;
|
||||
}
|
|
@ -4,8 +4,6 @@
|
|||
* @date 2019/08/23 19:41:21
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
import $ from '../utils'
|
||||
|
||||
const DEF_OPT = {
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
import Drag from './core'
|
||||
|
||||
Anot.directive('drag', {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
border-radius: 2px;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-2);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
|||
padding: 0 10px;
|
||||
margin: auto;
|
||||
line-height: 0;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: inherit;
|
||||
white-space: nowrap;
|
||||
background: #fff;
|
||||
|
@ -38,11 +38,11 @@
|
|||
cursor: inherit;
|
||||
|
||||
&:hover {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: nth($cgr, 1);
|
||||
border-color: var(--color-grey-1);
|
||||
}
|
||||
|
||||
&::-moz-focus-inner {
|
||||
|
@ -133,15 +133,15 @@
|
|||
:host([loading]),
|
||||
:host([disabled]) {
|
||||
cursor: not-allowed;
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
opacity: 0.6;
|
||||
|
||||
.icon {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
button {
|
||||
background: #fff;
|
||||
border-color: nth($cp, 3);
|
||||
border-color: var(--color-plain-3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,198 +156,198 @@
|
|||
}
|
||||
|
||||
:host([color='red']) button {
|
||||
background: nth($cr, 2);
|
||||
background: var(--color-red-2);
|
||||
&:hover {
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($cr, 3);
|
||||
background: var(--color-red-3);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($cr, 2);
|
||||
color: var(--color-red-2);
|
||||
&:hover {
|
||||
color: nth($cr, 1);
|
||||
color: var(--color-red-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($cr, 3);
|
||||
color: var(--color-red-3);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red'][loading]) button,
|
||||
:host([color='red'][disabled]) button {
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
|
||||
:host([color='blue']) button {
|
||||
background: nth($cb, 2);
|
||||
background: var(--color-blue-2);
|
||||
&:hover {
|
||||
background: nth($cb, 1);
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($cb, 3);
|
||||
background: var(--color-blue-3);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='blue'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($cb, 2);
|
||||
color: var(--color-blue-2);
|
||||
&:hover {
|
||||
color: nth($cb, 1);
|
||||
color: var(--color-blue-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($cb, 3);
|
||||
color: var(--color-blue-3);
|
||||
}
|
||||
}
|
||||
:host([color='blue'][loading]) button,
|
||||
:host([color='blue'][disabled]) button {
|
||||
background: nth($cb, 1);
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
|
||||
:host([color='green']) button {
|
||||
background: nth($cg, 2);
|
||||
background: var(--color-green-2);
|
||||
&:hover {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($cg, 3);
|
||||
background: var(--color-green-3);
|
||||
}
|
||||
}
|
||||
:host([color='green'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($cg, 2);
|
||||
color: var(--color-green-2);
|
||||
&:hover {
|
||||
color: nth($cg, 1);
|
||||
color: var(--color-green-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($cg, 3);
|
||||
color: var(--color-green-3);
|
||||
}
|
||||
}
|
||||
:host([color='green'][loading]) button,
|
||||
:host([color='green'][disabled]) button {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
|
||||
:host([color='teal']) button {
|
||||
background: nth($ct, 2);
|
||||
background: var(--color-teal-2);
|
||||
&:hover {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($ct, 3);
|
||||
background: var(--color-teal-3);
|
||||
}
|
||||
}
|
||||
:host([color='teal'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($ct, 2);
|
||||
color: var(--color-teal-2);
|
||||
&:hover {
|
||||
color: nth($ct, 1);
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($ct, 3);
|
||||
color: var(--color-teal-3);
|
||||
}
|
||||
}
|
||||
:host([color='teal'][loading]) button,
|
||||
:host([color='teal'][disabled]) button {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
|
||||
:host([color='orange']) button {
|
||||
background: nth($co, 2);
|
||||
background: var(--color-orange-2);
|
||||
&:hover {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($co, 3);
|
||||
background: var(--color-orange-3);
|
||||
}
|
||||
}
|
||||
:host([color='orange'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($co, 2);
|
||||
color: var(--color-orange-2);
|
||||
&:hover {
|
||||
color: nth($co, 1);
|
||||
color: var(--color-orange-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($co, 3);
|
||||
color: var(--color-orange-3);
|
||||
}
|
||||
}
|
||||
:host([color='orange'][loading]) button,
|
||||
:host([color='orange'][disabled]) button {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
|
||||
:host([color='dark']) button {
|
||||
background: nth($cd, 2);
|
||||
background: var(--color-dark-2);
|
||||
&:hover {
|
||||
background: nth($cd, 1);
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($cd, 3);
|
||||
background: var(--color-dark-3);
|
||||
}
|
||||
}
|
||||
:host([color='dark'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-2);
|
||||
&:hover {
|
||||
color: nth($cd, 1);
|
||||
color: var(--color-dark-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($cd, 3);
|
||||
color: var(--color-dark-3);
|
||||
}
|
||||
}
|
||||
:host([color='dark'][loading]) button,
|
||||
:host([color='dark'][disabled]) button {
|
||||
background: nth($cd, 1);
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
|
||||
:host([color='purple']) button {
|
||||
background: nth($cpp, 2);
|
||||
background: var(--color-purple-2);
|
||||
&:hover {
|
||||
background: nth($cpp, 1);
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($cpp, 3);
|
||||
background: var(--color-purple-3);
|
||||
}
|
||||
}
|
||||
:host([color='purple'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($cpp, 2);
|
||||
color: var(--color-purple-2);
|
||||
&:hover {
|
||||
color: nth($cpp, 1);
|
||||
color: var(--color-purple-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($cpp, 3);
|
||||
color: var(--color-purple-3);
|
||||
}
|
||||
}
|
||||
:host([color='purple'][loading]) button,
|
||||
:host([color='purple'][disabled]) button {
|
||||
background: nth($cpp, 1);
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
|
||||
:host([color='grey']) button {
|
||||
background: nth($cgr, 2);
|
||||
background: var(--color-grey-2);
|
||||
&:hover {
|
||||
background: nth($cgr, 1);
|
||||
background: var(--color-grey-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($cgr, 3);
|
||||
background: var(--color-grey-3);
|
||||
}
|
||||
}
|
||||
:host([color='grey'][text]) button {
|
||||
background: transparent;
|
||||
color: nth($cgr, 2);
|
||||
color: var(--color-grey-2);
|
||||
&:hover {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
&:active {
|
||||
color: nth($cgr, 3);
|
||||
color: var(--color-grey-3);
|
||||
}
|
||||
}
|
||||
:host([color='grey'][loading]) button,
|
||||
:host([color='grey'][disabled]) button {
|
||||
background: nth($cgr, 1);
|
||||
background: var(--color-grey-1);
|
||||
}
|
||||
|
||||
:host([no-border]) {
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
<template>
|
||||
<label>
|
||||
<wc-icon class="dot" is="checkbox-off"></wc-icon>
|
||||
<slot />
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: inline-flex;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 5px;
|
||||
line-height: 0;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
cursor: inherit;
|
||||
color: var(--color-grey-3);
|
||||
}
|
||||
|
||||
.dot {
|
||||
--size: 18px;
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
:host([readonly]) {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
:host([size='large']) {
|
||||
font-size: 16px;
|
||||
label {
|
||||
height: 42px;
|
||||
}
|
||||
.dot {
|
||||
--size: 22px;
|
||||
}
|
||||
}
|
||||
:host([size='medium']) {
|
||||
label {
|
||||
height: 38px;
|
||||
}
|
||||
.dot {
|
||||
--size: 20px;
|
||||
}
|
||||
}
|
||||
:host([size='mini']) {
|
||||
font-size: 12px;
|
||||
label {
|
||||
height: 20px;
|
||||
}
|
||||
.dot {
|
||||
--size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red']) label.checked {
|
||||
color: var(--color-red-1);
|
||||
.dot {
|
||||
border-color: var(--color-red-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='blue']) label.checked {
|
||||
color: var(--color-blue-1);
|
||||
.dot {
|
||||
border-color: var(--color-blue-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='green']) label.checked {
|
||||
color: var(--color-green-1);
|
||||
.dot {
|
||||
border-color: var(--color-green-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='teal']) label.checked {
|
||||
color: var(--color-teal-1);
|
||||
.dot {
|
||||
border-color: var(--color-teal-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='orange']) label.checked {
|
||||
color: var(--color-orange-1);
|
||||
.dot {
|
||||
border-color: var(--color-orange-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='dark']) label.checked {
|
||||
color: var(--color-dark-1);
|
||||
.dot {
|
||||
border-color: var(--color-dark-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='purple']) label.checked {
|
||||
color: var(--color-purple-1);
|
||||
.dot {
|
||||
border-color: var(--color-purple-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import '../icon/index'
|
||||
import $ from '../utils'
|
||||
|
||||
export default class CheckboxItem {
|
||||
props = {
|
||||
color: '',
|
||||
value: '',
|
||||
checked: false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
}
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
this.__SWITCH__ = this.root.lastElementChild
|
||||
this.__ICO__ = this.__SWITCH__.children[0]
|
||||
|
||||
this._isInGroup = false
|
||||
}
|
||||
|
||||
_checkGroup() {
|
||||
this._isInGroup = this.parentNode.tagName === 'WC-CHECKBOX'
|
||||
if (this._isInGroup && this.parentNode.root) {
|
||||
if (this.parentNode.value.includes(this.value)) {
|
||||
this.checked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.props.value = val
|
||||
}
|
||||
|
||||
get checked() {
|
||||
return this.props.checked
|
||||
}
|
||||
|
||||
set checked(val) {
|
||||
this.props.checked = !!val
|
||||
var { checked, color } = this.props
|
||||
this.__SWITCH__.classList.toggle('checked', checked)
|
||||
this.__ICO__.setAttribute('is', 'checkbox-' + (checked ? 'on' : 'off'))
|
||||
|
||||
if (checked) {
|
||||
this.__ICO__.setAttribute('color', color)
|
||||
} else {
|
||||
this.__ICO__.removeAttribute('color')
|
||||
}
|
||||
}
|
||||
|
||||
get readOnly() {
|
||||
return this.props.readonly
|
||||
}
|
||||
|
||||
set readOnly(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.readonly) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.readonly = true
|
||||
this.setAttribute('readonly', '')
|
||||
} else {
|
||||
this.props.readonly = false
|
||||
this.removeAttribute('readonly')
|
||||
}
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.props.disabled
|
||||
}
|
||||
|
||||
set disabled(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.disabled) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.disabled = true
|
||||
this.setAttribute('disabled', '')
|
||||
} else {
|
||||
this.props.disabled = false
|
||||
this.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this._checkGroup()
|
||||
|
||||
this._handlClick = $.bind(this, 'click', ev => {
|
||||
ev.preventDefault()
|
||||
|
||||
if (this.disabled || this.readOnly) {
|
||||
return
|
||||
}
|
||||
|
||||
this.checked = !this.checked
|
||||
|
||||
if (this._isInGroup) {
|
||||
this.parentNode.dispatchEvent(
|
||||
new CustomEvent('child-picked', {
|
||||
detail: { value: this.value, checked: this.checked }
|
||||
})
|
||||
)
|
||||
} else {
|
||||
this.dispatchEvent(new CustomEvent('input'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.unbind(this, 'click', this._handlClick)
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'value':
|
||||
case 'color':
|
||||
this.props[name] = val
|
||||
break
|
||||
|
||||
case 'checked':
|
||||
case 'readonly':
|
||||
case 'disabled':
|
||||
var k = name
|
||||
if (k === 'readonly') {
|
||||
k = 'readOnly'
|
||||
}
|
||||
this[k] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,163 +1,36 @@
|
|||
<template>
|
||||
<label>
|
||||
<wc-icon class="dot" is="checkbox-off"></wc-icon>
|
||||
<slot></slot>
|
||||
</label>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 5px;
|
||||
line-height: 0;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
cursor: inherit;
|
||||
color: nth($cgr, 3);
|
||||
}
|
||||
|
||||
.dot {
|
||||
--size: 18px;
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
:host([readonly]) {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
:host([size='large']) {
|
||||
font-size: 16px;
|
||||
label {
|
||||
height: 42px;
|
||||
}
|
||||
.dot {
|
||||
--size: 22px;
|
||||
}
|
||||
}
|
||||
:host([size='medium']) {
|
||||
label {
|
||||
height: 38px;
|
||||
}
|
||||
.dot {
|
||||
--size: 20px;
|
||||
}
|
||||
}
|
||||
:host([size='mini']) {
|
||||
font-size: 12px;
|
||||
label {
|
||||
height: 20px;
|
||||
}
|
||||
.dot {
|
||||
--size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red']) label.checked {
|
||||
color: nth($cr, 1);
|
||||
.dot {
|
||||
border-color: nth($cr, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='blue']) label.checked {
|
||||
color: nth($cb, 1);
|
||||
.dot {
|
||||
border-color: nth($cb, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cb, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='green']) label.checked {
|
||||
color: nth($cg, 1);
|
||||
.dot {
|
||||
border-color: nth($cg, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cg, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='teal']) label.checked {
|
||||
color: nth($ct, 1);
|
||||
.dot {
|
||||
border-color: nth($ct, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($ct, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='orange']) label.checked {
|
||||
color: nth($co, 1);
|
||||
.dot {
|
||||
border-color: nth($co, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($co, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='dark']) label.checked {
|
||||
color: nth($cd, 1);
|
||||
.dot {
|
||||
border-color: nth($cd, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cd, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='purple']) label.checked {
|
||||
color: nth($cpp, 1);
|
||||
.dot {
|
||||
border-color: nth($cpp, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cpp, 1);
|
||||
}
|
||||
display: inline-flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import '../icon/index'
|
||||
import $ from '../utils'
|
||||
import './checkbox-item'
|
||||
|
||||
export default class Checkbox {
|
||||
props = {
|
||||
label: '',
|
||||
color: '',
|
||||
value: [],
|
||||
checked: false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
value: []
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
}
|
||||
|
||||
this.__SWITCH__ = this.root.lastElementChild
|
||||
this.__ICO__ = this.__SWITCH__.children[0]
|
||||
_updateChildrenStat() {
|
||||
Array.from(this.children).forEach(it => {
|
||||
if (it.tagName === 'WC-CHECKBOX-ITEM' && it.root) {
|
||||
if (this.value.includes(it.value)) {
|
||||
it.checked = true
|
||||
} else {
|
||||
it.checked = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
get value() {
|
||||
|
@ -165,106 +38,41 @@ export default class Checkbox {
|
|||
}
|
||||
|
||||
set value(val) {
|
||||
if (Array.isArray(val)) {
|
||||
if (val === this.props.value) {
|
||||
return
|
||||
}
|
||||
this.props.value = val
|
||||
this.checked = this.props.value.includes(this.props.label)
|
||||
} else {
|
||||
console.error('checkbox组件的value必须是数组, 当前为: ' + typeof val)
|
||||
}
|
||||
}
|
||||
|
||||
get checked() {
|
||||
return this.props.checked
|
||||
}
|
||||
|
||||
set checked(val) {
|
||||
this.props.checked = !!val
|
||||
var { value, checked, label, color } = this.props
|
||||
this.__SWITCH__.classList.toggle('checked', checked)
|
||||
this.__ICO__.setAttribute('is', 'checkbox-' + (checked ? 'on' : 'off'))
|
||||
|
||||
var idx = value.indexOf(label)
|
||||
if (checked) {
|
||||
this.__ICO__.setAttribute('color', color)
|
||||
if (idx < 0) {
|
||||
value.push(label)
|
||||
}
|
||||
} else {
|
||||
this.__ICO__.removeAttribute('color')
|
||||
if (~idx) {
|
||||
value.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get readOnly() {
|
||||
return this.props.readonly
|
||||
}
|
||||
|
||||
set readOnly(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.readonly) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.readonly = true
|
||||
this.setAttribute('readonly', '')
|
||||
} else {
|
||||
this.props.readonly = false
|
||||
this.removeAttribute('readonly')
|
||||
}
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.props.disabled
|
||||
}
|
||||
|
||||
set disabled(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.disabled) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.disabled = true
|
||||
this.setAttribute('disabled', '')
|
||||
} else {
|
||||
this.props.disabled = false
|
||||
this.removeAttribute('disabled')
|
||||
}
|
||||
this._updateChildrenStat()
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this._handlClick = $.bind(this, 'click', ev => {
|
||||
ev.preventDefault()
|
||||
|
||||
if (!this.disabled && !this.readOnly) {
|
||||
this.checked = !this.checked
|
||||
this.dispatchEvent(new CustomEvent('input'))
|
||||
this._pickedFn = $.bind(this, 'child-picked', ev => {
|
||||
var tmp = [...this.props.value]
|
||||
var idx = tmp.indexOf(ev.detail.value)
|
||||
if (ev.detail.checked) {
|
||||
if (idx < 0) {
|
||||
tmp.push(ev.detail.value)
|
||||
}
|
||||
} else {
|
||||
if (~idx) {
|
||||
tmp.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
this.props.value = tmp
|
||||
this.dispatchEvent(new CustomEvent('input'))
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.unbind(this, 'click', this._handlClick)
|
||||
$.unbind(this, 'child-picked', this._pickedFn)
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'label':
|
||||
case 'color':
|
||||
this.props[name] = val
|
||||
break
|
||||
|
||||
case 'checked':
|
||||
case 'readonly':
|
||||
case 'disabled':
|
||||
var k = name
|
||||
if (k === 'readonly') {
|
||||
k = 'readOnly'
|
||||
case 'value':
|
||||
if (val) {
|
||||
this.value = val.split(/,\s*?/)
|
||||
}
|
||||
this[k] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ li {
|
|||
display: inline-block;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-1);
|
||||
border-radius: 2px;
|
||||
cursor: text;
|
||||
}
|
||||
|
@ -36,9 +36,9 @@ li {
|
|||
width: 100%;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: inherit;
|
||||
background: #fff;
|
||||
background: var(--bg-color, #fff);
|
||||
color: inherit;
|
||||
cursor: inherit;
|
||||
|
||||
|
@ -59,7 +59,7 @@ li {
|
|||
cursor: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
}
|
||||
textarea {
|
||||
|
@ -75,15 +75,15 @@ li {
|
|||
height: 30px;
|
||||
padding: 0 10px;
|
||||
line-height: 0;
|
||||
background: nth($cp, 1);
|
||||
background: var(--bg-color, --color-plain-1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.prepend {
|
||||
border-right: 1px solid nth($cp, 3);
|
||||
border-right: 1px solid var(--color-plain-3);
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
.append {
|
||||
border-left: 1px solid nth($cp, 3);
|
||||
border-left: 1px solid var(--color-plain-3);
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
&[prepend] .prepend,
|
||||
|
@ -99,7 +99,7 @@ li {
|
|||
--size: 20px;
|
||||
padding: 0 5px;
|
||||
margin: 0 5px;
|
||||
color: nth($cgr, 2);
|
||||
color: var(--color-grey-2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ li {
|
|||
|
||||
&:hover,
|
||||
&[focus] {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ li {
|
|||
cursor: not-allowed;
|
||||
|
||||
.label {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ li {
|
|||
:host(:focus-within) {
|
||||
@include focus1;
|
||||
.label {
|
||||
border-color: nth($cp, 3);
|
||||
border-color: var(--color-plain-3);
|
||||
}
|
||||
}
|
||||
:host(:focus-within[readonly]) {
|
||||
|
@ -277,20 +277,26 @@ export default class Input {
|
|||
value: '',
|
||||
icon: '',
|
||||
type: 'text',
|
||||
label: '',
|
||||
placeholder: '',
|
||||
mvidx: null, //下拉列表光标的索引ID
|
||||
maxlength: null,
|
||||
minlength: null,
|
||||
autofocus: false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
}
|
||||
|
||||
state = {
|
||||
mvidx: null //下拉列表光标的索引ID
|
||||
}
|
||||
|
||||
__init__() {
|
||||
var type = this.getAttribute('type')
|
||||
var input = ''
|
||||
|
||||
if (type !== 'textarea') {
|
||||
type = 'text'
|
||||
}
|
||||
|
||||
input = INPUTS[type]
|
||||
|
||||
/* render */
|
||||
|
@ -375,18 +381,18 @@ export default class Input {
|
|||
var items = Array.from(
|
||||
this.__LIST__.firstElementChild.firstElementChild.children
|
||||
)
|
||||
if (this.props.mvidx === null) {
|
||||
this.props.mvidx = 0
|
||||
if (this.state.mvidx === null) {
|
||||
this.state.mvidx = 0
|
||||
} else {
|
||||
this.props.mvidx += step
|
||||
this.state.mvidx += step
|
||||
}
|
||||
if (this.props.mvidx < 0) {
|
||||
this.props.mvidx = 0
|
||||
} else if (this.props.mvidx > items.length - 1) {
|
||||
this.props.mvidx = items.length - 1
|
||||
if (this.state.mvidx < 0) {
|
||||
this.state.mvidx = 0
|
||||
} else if (this.state.mvidx > items.length - 1) {
|
||||
this.state.mvidx = items.length - 1
|
||||
}
|
||||
items.forEach((it, i) => {
|
||||
if (i === this.props.mvidx) {
|
||||
if (i === this.state.mvidx) {
|
||||
this.__LIST__.firstElementChild.scrollTop = it.offsetTop - 150
|
||||
it.setAttribute('focus', '')
|
||||
} else {
|
||||
|
@ -407,12 +413,28 @@ export default class Input {
|
|||
)
|
||||
this._handleChange(ev)
|
||||
this.__LIST__.classList.remove('show')
|
||||
this.props.mvidx = null
|
||||
this.state.mvidx = null
|
||||
}
|
||||
|
||||
_updateAttr() {
|
||||
var { maxlength, minlength } = this.props
|
||||
|
||||
if (maxlength && maxlength > 0) {
|
||||
this.__INPUT__.setAttribute('maxlength', maxlength)
|
||||
} else {
|
||||
this.__INPUT__.removeAttribute('maxlength')
|
||||
}
|
||||
if (minlength && minlength > 0) {
|
||||
this.__INPUT__.setAttribute('minlength', minlength)
|
||||
} else {
|
||||
this.__INPUT__.removeAttribute('minlength')
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
var prepend = this.__PREPEND__.assignedNodes()
|
||||
var append = this.__APPEND__.assignedNodes()
|
||||
var { type } = this.props
|
||||
|
||||
// 相同插槽, 只允许1个
|
||||
while (prepend.length > 1) {
|
||||
|
@ -422,14 +444,14 @@ export default class Input {
|
|||
this.removeChild(append.pop())
|
||||
}
|
||||
|
||||
if (prepend.length && this.props.type !== 'textarea') {
|
||||
if (prepend.length && type !== 'textarea') {
|
||||
this.__OUTER__.setAttribute('prepend', '')
|
||||
}
|
||||
if (append.length && this.props.type !== 'textarea') {
|
||||
if (append.length && type !== 'textarea') {
|
||||
this.__OUTER__.setAttribute('append', '')
|
||||
}
|
||||
|
||||
var { type } = this.props
|
||||
this._updateAttr()
|
||||
|
||||
// 键盘事件
|
||||
this._handleSubmit = $.catch(this.__INPUT__, 'keydown', ev => {
|
||||
|
@ -439,7 +461,7 @@ export default class Input {
|
|||
// up: 38, down: 40
|
||||
if (ev.keyCode === 38 || ev.keyCode === 40) {
|
||||
// 仅普通文本表单, 密码和多行文本框不做响应
|
||||
if (this.type === 'text') {
|
||||
if (type === 'text') {
|
||||
return this._moveSelect(ev)
|
||||
}
|
||||
}
|
||||
|
@ -447,8 +469,8 @@ export default class Input {
|
|||
// textarea 要按Ctrl Or Cmd键, 才会触发
|
||||
if (ev.keyCode === 13) {
|
||||
// 如果是输入建议存在,则第1次回车的时候, 不触发提交
|
||||
if (this.type === 'text' && this.props.mvidx !== null) {
|
||||
return this._fetchSelect(this.props.mvidx, ev)
|
||||
if (type === 'text' && this.state.mvidx !== null) {
|
||||
return this._fetchSelect(this.state.mvidx, ev)
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -544,8 +566,6 @@ export default class Input {
|
|||
|
||||
break
|
||||
|
||||
// label和placeholder 功能相同
|
||||
case 'label':
|
||||
case 'placeholder':
|
||||
this.__INPUT__.setAttribute('placeholder', val)
|
||||
break
|
||||
|
@ -562,6 +582,12 @@ export default class Input {
|
|||
this.value = val
|
||||
break
|
||||
|
||||
case 'maxlength':
|
||||
case 'minlength':
|
||||
this.props[name] = val
|
||||
this._updateAttr()
|
||||
break
|
||||
|
||||
case 'readonly':
|
||||
case 'disabled':
|
||||
var k = name
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div class="label">
|
||||
<span data-act="-">-</span>
|
||||
<!-- <wc-icon class="icon" icon="minus"></wc-icon> -->
|
||||
<!-- <wc-icon class="icon" is="minus"></wc-icon> -->
|
||||
<input value="0" maxlength="9" />
|
||||
<span data-act="+">+</span>
|
||||
<!-- <wc-icon class="icon" icon="plus"></wc-icon> -->
|
||||
<!-- <wc-icon class="icon" is="plus"></wc-icon> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
height: 32px;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-2);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@
|
|||
margin: 0 auto;
|
||||
line-height: 0;
|
||||
font-size: 14px;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: inherit;
|
||||
background: #fff;
|
||||
background: var(--bg-color, #fff);
|
||||
color: inherit;
|
||||
cursor: text;
|
||||
|
||||
|
@ -39,17 +39,17 @@
|
|||
align-items: center;
|
||||
width: 32px;
|
||||
height: 100%;
|
||||
background: nth($cp, 1);
|
||||
background: var(--bg-color, --color-plain-1);
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 2px 0 0 2px;
|
||||
border-right: 1px solid nth($cp, 3);
|
||||
border-right: 1px solid var(--color-plain-3);
|
||||
}
|
||||
&:last-child {
|
||||
border-radius: 0 2px 2px 0;
|
||||
border-left: 1px solid nth($cp, 3);
|
||||
border-left: 1px solid var(--color-plain-3);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
|
@ -76,7 +76,7 @@
|
|||
cursor: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
}
|
||||
/* ----- */
|
||||
|
@ -96,7 +96,7 @@
|
|||
}
|
||||
}
|
||||
:host([disabled]) .label {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
flex: 1;
|
||||
height: var(--size, 10px);
|
||||
border-radius: 9px;
|
||||
background: nth($cp, 2);
|
||||
background: var(--color-plain-2);
|
||||
|
||||
span {
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
border-radius: 9px;
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,27 +36,27 @@
|
|||
}
|
||||
|
||||
:host([color='red']) label span {
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
|
||||
:host([color='blue']) label span {
|
||||
background: nth($cb, 1);
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
|
||||
:host([color='green']) label span {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
|
||||
:host([color='orange']) label span {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
|
||||
:host([color='dark']) label span {
|
||||
background: nth($cd, 1);
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
|
||||
:host([color='purple']) label span {
|
||||
background: nth($cpp, 1);
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
<template>
|
||||
<label>
|
||||
<span class="dot"></span>
|
||||
<slot />
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: inline-flex;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 5px;
|
||||
line-height: 1;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
cursor: inherit;
|
||||
color: var(--color-grey-3);
|
||||
|
||||
&.checked .dot::after {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.dot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 3px;
|
||||
border: 1px solid var(--color-grey-1);
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-grey-1);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([readonly]) {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
:host([size='large']) {
|
||||
label {
|
||||
min-width: 58px;
|
||||
height: 32px;
|
||||
}
|
||||
.dot {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
|
||||
&::after {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
:host([size='medium']) {
|
||||
label {
|
||||
min-width: 50px;
|
||||
height: 28px;
|
||||
}
|
||||
.dot {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
||||
&::after {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
:host([size='mini']) {
|
||||
label {
|
||||
height: 14px;
|
||||
}
|
||||
.dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
&::after {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red']) label.checked {
|
||||
color: var(--color-red-1);
|
||||
.dot {
|
||||
border-color: var(--color-red-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='blue']) label.checked {
|
||||
color: var(--color-blue-1);
|
||||
.dot {
|
||||
border-color: var(--color-blue-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='green']) label.checked {
|
||||
color: var(--color-green-1);
|
||||
.dot {
|
||||
border-color: var(--color-green-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='teal']) label.checked {
|
||||
color: var(--color-teal-1);
|
||||
.dot {
|
||||
border-color: var(--color-teal-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='orange']) label.checked {
|
||||
color: var(--color-orange-1);
|
||||
.dot {
|
||||
border-color: var(--color-orange-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='dark']) label.checked {
|
||||
color: var(--color-dark-1);
|
||||
.dot {
|
||||
border-color: var(--color-dark-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='purple']) label.checked {
|
||||
color: var(--color-purple-1);
|
||||
.dot {
|
||||
border-color: var(--color-purple-1);
|
||||
}
|
||||
.dot::after {
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
|
||||
export default class RadioItem {
|
||||
props = {
|
||||
value: '',
|
||||
checked: false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
}
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
this.__SWITCH__ = this.root.lastElementChild
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.props.value = val
|
||||
}
|
||||
|
||||
get checked() {
|
||||
return this.props.checked
|
||||
}
|
||||
|
||||
set checked(val) {
|
||||
this.props.checked = !!val
|
||||
this.__SWITCH__.classList.toggle('checked', this.props.checked)
|
||||
}
|
||||
|
||||
get readOnly() {
|
||||
return this.props.readonly
|
||||
}
|
||||
|
||||
set readOnly(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.readonly) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.readonly = true
|
||||
this.setAttribute('readonly', '')
|
||||
} else {
|
||||
this.props.readonly = false
|
||||
this.removeAttribute('readonly')
|
||||
}
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.props.disabled
|
||||
}
|
||||
|
||||
set disabled(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.disabled) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.disabled = true
|
||||
this.setAttribute('disabled', '')
|
||||
} else {
|
||||
this.props.disabled = false
|
||||
this.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (this.value === this.parentNode.value) {
|
||||
this.checked = true
|
||||
}
|
||||
|
||||
this._handleClick = $.catch(this, 'click', ev => {
|
||||
if (this.disabled || this.readOnly || this.checked) {
|
||||
return
|
||||
}
|
||||
|
||||
this.parentNode.dispatchEvent(
|
||||
new CustomEvent('child-picked', { detail: this.value })
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.unbind(this, 'click', this._handleClick)
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'value':
|
||||
this.value = val
|
||||
break
|
||||
|
||||
case 'checked':
|
||||
case 'readonly':
|
||||
case 'disabled':
|
||||
var k = name
|
||||
if (k === 'readonly') {
|
||||
k = 'readOnly'
|
||||
}
|
||||
this[k] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,282 +1,66 @@
|
|||
<template>
|
||||
<label>
|
||||
<span class="dot"></span>
|
||||
<slot></slot>
|
||||
</label>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 5px;
|
||||
line-height: 0;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
cursor: inherit;
|
||||
color: nth($cgr, 3);
|
||||
|
||||
&.checked .dot::after {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.dot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
border: 1px solid nth($cgr, 1);
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: nth($cgr, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([readonly]) {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
:host([size='large']) {
|
||||
label {
|
||||
min-width: 58px;
|
||||
height: 32px;
|
||||
}
|
||||
.dot {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
|
||||
&::after {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
:host([size='medium']) {
|
||||
label {
|
||||
min-width: 50px;
|
||||
height: 28px;
|
||||
}
|
||||
.dot {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
||||
&::after {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
:host([size='mini']) {
|
||||
label {
|
||||
height: 14px;
|
||||
}
|
||||
.dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
&::after {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red']) label.checked {
|
||||
color: nth($cr, 1);
|
||||
.dot {
|
||||
border-color: nth($cr, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='blue']) label.checked {
|
||||
color: nth($cb, 1);
|
||||
.dot {
|
||||
border-color: nth($cb, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cb, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='green']) label.checked {
|
||||
color: nth($cg, 1);
|
||||
.dot {
|
||||
border-color: nth($cg, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cg, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='teal']) label.checked {
|
||||
color: nth($ct, 1);
|
||||
.dot {
|
||||
border-color: nth($ct, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($ct, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='orange']) label.checked {
|
||||
color: nth($co, 1);
|
||||
.dot {
|
||||
border-color: nth($co, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($co, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='dark']) label.checked {
|
||||
color: nth($cd, 1);
|
||||
.dot {
|
||||
border-color: nth($cd, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cd, 1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='purple']) label.checked {
|
||||
color: nth($cpp, 1);
|
||||
.dot {
|
||||
border-color: nth($cpp, 1);
|
||||
}
|
||||
.dot::after {
|
||||
background: nth($cpp, 1);
|
||||
}
|
||||
display: inline-flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
import './radio-item'
|
||||
|
||||
export default class Radio {
|
||||
props = {
|
||||
label: '',
|
||||
checked: false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
value: null
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
}
|
||||
|
||||
this.__SWITCH__ = this.root.lastElementChild
|
||||
_updateChildrenStat() {
|
||||
Array.from(this.children).forEach(it => {
|
||||
if (it.tagName === 'WC-RADIO-ITEM' && it.root) {
|
||||
if (it.value === this.props.value) {
|
||||
it.checked = true
|
||||
} else {
|
||||
it.checked = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.props.label
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.checked = this.props.label === val
|
||||
}
|
||||
|
||||
get checked() {
|
||||
return this.props.checked
|
||||
}
|
||||
|
||||
set checked(val) {
|
||||
this.props.checked = !!val
|
||||
this.__SWITCH__.classList.toggle('checked', this.props.checked)
|
||||
}
|
||||
|
||||
get readOnly() {
|
||||
return this.props.readonly
|
||||
}
|
||||
|
||||
set readOnly(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.readonly) {
|
||||
if (val === this.props.value) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.readonly = true
|
||||
this.setAttribute('readonly', '')
|
||||
} else {
|
||||
this.props.readonly = false
|
||||
this.removeAttribute('readonly')
|
||||
}
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.props.disabled
|
||||
}
|
||||
|
||||
set disabled(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.disabled) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.disabled = true
|
||||
this.setAttribute('disabled', '')
|
||||
} else {
|
||||
this.props.disabled = false
|
||||
this.removeAttribute('disabled')
|
||||
}
|
||||
this.props.value = val
|
||||
this._updateChildrenStat()
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this._handleClick = $.catch(this, 'click', ev => {
|
||||
if (this.disabled || this.readOnly || this.checked) {
|
||||
return
|
||||
}
|
||||
|
||||
this.checked = true
|
||||
this._pickedFn = $.bind(this, 'child-picked', ev => {
|
||||
log('radio picked: ', ev.detail)
|
||||
this.value = ev.detail
|
||||
this.dispatchEvent(new CustomEvent('input'))
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.unbind(this, 'click', this._handleClick)
|
||||
$.unbind(this, 'child-picked', this._pickedFn)
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'label':
|
||||
this.props.label = val
|
||||
break
|
||||
|
||||
case 'checked':
|
||||
case 'readonly':
|
||||
case 'disabled':
|
||||
var k = name
|
||||
if (k === 'readonly') {
|
||||
k = 'readOnly'
|
||||
}
|
||||
this[k] = true
|
||||
case 'value':
|
||||
this.value = val
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
display: inline-block;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-2);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.label {
|
||||
|
@ -30,7 +30,7 @@
|
|||
height: 32px;
|
||||
line-height: 0;
|
||||
font-size: 14px;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: inherit;
|
||||
background: #fff;
|
||||
color: inherit;
|
||||
|
@ -53,7 +53,7 @@
|
|||
cursor: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,14 +66,14 @@
|
|||
height: 30px;
|
||||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
.prepend {
|
||||
border-right: 1px solid nth($cp, 3);
|
||||
border-right: 1px solid var(--color-plain-3);
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
.append {
|
||||
border-left: 1px solid nth($cp, 3);
|
||||
border-left: 1px solid var(--color-plain-3);
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
&[prepend] .prepend,
|
||||
|
@ -137,7 +137,7 @@
|
|||
|
||||
dt {
|
||||
font-size: 12px;
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
|
||||
dd {
|
||||
|
@ -145,16 +145,16 @@
|
|||
|
||||
&:hover,
|
||||
&[focus] {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
&[focus] {
|
||||
color: nth($ct, 1);
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
&[sub] {
|
||||
text-indent: 1em;
|
||||
}
|
||||
&[disabled] {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
cursor: not-allowed;
|
||||
background: none;
|
||||
}
|
||||
|
@ -163,7 +163,7 @@
|
|||
|
||||
/* --- */
|
||||
:host([disabled]) .label {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ label {
|
|||
|
||||
wc-icon {
|
||||
margin: 0 3px;
|
||||
@include ts(transform);
|
||||
transition: transform 0.1s easein-out;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
|
@ -55,25 +55,25 @@ label {
|
|||
}
|
||||
|
||||
:host([color='red']) label span {
|
||||
color: nth($cr, 1);
|
||||
color: var(--color-red-1);
|
||||
}
|
||||
:host([color='teal']) label span {
|
||||
color: nth($ct, 1);
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
:host([color='green']) label span {
|
||||
color: nth($cg, 1);
|
||||
color: var(--color-green-1);
|
||||
}
|
||||
:host([color='grey']) label span {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
:host([color='blue']) label span {
|
||||
color: nth($cb, 1);
|
||||
color: var(--color-blue-1);
|
||||
}
|
||||
:host([color='purple']) label span {
|
||||
color: nth($cpp, 1);
|
||||
color: var(--color-purple-1);
|
||||
}
|
||||
:host([color='orange']) label span {
|
||||
color: nth($co, 1);
|
||||
color: var(--color-orange-1);
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
padding: 3px;
|
||||
margin: 5px;
|
||||
border-radius: 21px;
|
||||
background: nth($cp, 3);
|
||||
background: var(--color-plain-3);
|
||||
cursor: inherit;
|
||||
|
||||
&.checked {
|
||||
flex-direction: row-reverse;
|
||||
background: nth($cgr, 3);
|
||||
background: var(--color-grey-3);
|
||||
}
|
||||
}
|
||||
.dot {
|
||||
|
@ -83,31 +83,31 @@
|
|||
}
|
||||
|
||||
:host([color='red']) label.checked {
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
|
||||
:host([color='blue']) label.checked {
|
||||
background: nth($cb, 1);
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
|
||||
:host([color='green']) label.checked {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
|
||||
:host([color='teal']) label.checked {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
|
||||
:host([color='orange']) label.checked {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
|
||||
:host([color='dark']) label.checked {
|
||||
background: nth($cd, 1);
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
|
||||
:host([color='purple']) label.checked {
|
||||
background: nth($cpp, 1);
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -40,35 +40,35 @@
|
|||
height: 20px;
|
||||
}
|
||||
:host([color='red']) {
|
||||
color: nth($cr, 1);
|
||||
color: var(--color-red-1);
|
||||
}
|
||||
|
||||
:host([color='blue']) {
|
||||
color: nth($cb, 1);
|
||||
color: var(--color-blue-1);
|
||||
}
|
||||
|
||||
:host([color='green']) {
|
||||
color: nth($cg, 1);
|
||||
color: var(--color-green-1);
|
||||
}
|
||||
|
||||
:host([color='teal']) {
|
||||
color: nth($ct, 1);
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
|
||||
:host([color='orange']) {
|
||||
color: nth($co, 1);
|
||||
color: var(--color-orange-1);
|
||||
}
|
||||
|
||||
:host([color='dark']) {
|
||||
color: nth($cd, 1);
|
||||
color: var(--color-dark-1);
|
||||
}
|
||||
|
||||
:host([color='purple']) {
|
||||
color: nth($cpp, 1);
|
||||
color: var(--color-purple-1);
|
||||
}
|
||||
|
||||
:host([color='grey']) {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
|
||||
@keyframes circle {
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
<template>
|
||||
<div class="keyboard">
|
||||
<main>
|
||||
<div class="row">
|
||||
<span class="reverse">Esc</span>
|
||||
<label>
|
||||
<span>F1</span>
|
||||
<span>F2</span>
|
||||
<span>F3</span>
|
||||
<span>F4</span>
|
||||
</label>
|
||||
<label>
|
||||
<span class="reverse">F5</span>
|
||||
<span class="reverse">F6</span>
|
||||
<span class="reverse">F7</span>
|
||||
<span class="reverse">F8</span>
|
||||
</label>
|
||||
<label>
|
||||
<span>F9</span>
|
||||
<span>F10</span>
|
||||
<span>F11</span>
|
||||
<span>F12</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row mt12">
|
||||
<span>\`</span>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
<span>4</span>
|
||||
<span>5</span>
|
||||
<span>6</span>
|
||||
<span>7</span>
|
||||
<span>8</span>
|
||||
<span>9</span>
|
||||
<span>0</span>
|
||||
<span>-</span>
|
||||
<span>=</span>
|
||||
<span class="w20 reverse">Backspace</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="w15 reverse">Tab</span>
|
||||
<span>Q</span>
|
||||
<span>W</span>
|
||||
<span>E</span>
|
||||
<span>R</span>
|
||||
<span>T</span>
|
||||
<span>Y</span>
|
||||
<span>U</span>
|
||||
<span>I</span>
|
||||
<span>O</span>
|
||||
<span>P</span>
|
||||
<span>[</span>
|
||||
<span>]</span>
|
||||
<span class="w15">\\</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="w20 reverse">Caps</span>
|
||||
<span>A</span>
|
||||
<span>S</span>
|
||||
<span>D</span>
|
||||
<span>F</span>
|
||||
<span>G</span>
|
||||
<span>H</span>
|
||||
<span>J</span>
|
||||
<span>K</span>
|
||||
<span>L</span>
|
||||
<span>;</span>
|
||||
<span>'</span>
|
||||
<span class="w25 reverse">Enter</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="w25 reverse">Shift</span>
|
||||
<span>Z</span>
|
||||
<span>X</span>
|
||||
<span>C</span>
|
||||
<span>V</span>
|
||||
<span>B</span>
|
||||
<span>N</span>
|
||||
<span>M</span>
|
||||
<span>,</span>
|
||||
<span>.</span>
|
||||
<span>/</span>
|
||||
<span class="w30 reverse">Shift</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="w15 reverse">Ctrl</span>
|
||||
<span class="reverse">Win</span>
|
||||
<span class="w12 reverse">Alt</span>
|
||||
<span class="w65">Space</span>
|
||||
<span class="w12 reverse">Alt</span>
|
||||
<span class="reverse">Win</span>
|
||||
<span class="reverse">Menu</span>
|
||||
<span class="w15 reverse">Ctrl</span>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="tool">
|
||||
<div class="row">
|
||||
<span class="reverse">Prt</span>
|
||||
<span class="reverse">Scr</span>
|
||||
<span class="reverse">Pau</span>
|
||||
</div>
|
||||
<div class="row mt12">
|
||||
<span class="reverse">Ins</span>
|
||||
<span class="reverse">Home</span>
|
||||
<span class="reverse">Pg▴</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="reverse">Del</span>
|
||||
<span class="reverse">End</span>
|
||||
<span class="reverse">Pg▾</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="null"></span>
|
||||
<span class="null"></span>
|
||||
<span class="null"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="null"></span>
|
||||
<span class="reverse">↑</span>
|
||||
<span class="null"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="reverse">←</span>
|
||||
<span class="reverse">↓</span>
|
||||
<span class="reverse">→</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="numpad">
|
||||
<div class="row">
|
||||
<span class="null"></span>
|
||||
</div>
|
||||
<div class="row mt12">
|
||||
<span class="reverse">Lock</span>
|
||||
<span class="reverse">/</span>
|
||||
<span class="reverse">*</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>7</span>
|
||||
<span>8</span>
|
||||
<span>9</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>4</span>
|
||||
<span>5</span>
|
||||
<span>6</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="w22">0</span>
|
||||
<span>.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="numpad-1">
|
||||
<div class="row">
|
||||
<span class="null"></span>
|
||||
</div>
|
||||
<div class="row mt12">
|
||||
<span class="reverse">-</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="h20 reverse">+</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="h20 reverse">Ent</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.keyboard {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 1198px;
|
||||
padding: 6px 6px 12px;
|
||||
background: #f7f8fb;
|
||||
color: #4caf50;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0 3px;
|
||||
line-height: 48px;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||
cursor: pointer;
|
||||
transition: box-shadow 0.05s ease-in, background 0.05s ease-in;
|
||||
|
||||
&.w12 {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
&.w15 {
|
||||
width: 72px;
|
||||
}
|
||||
&.w20 {
|
||||
width: 96px;
|
||||
}
|
||||
&.w22 {
|
||||
width: 102px;
|
||||
}
|
||||
&.w25 {
|
||||
width: 120px;
|
||||
}
|
||||
&.w30 {
|
||||
width: 144px;
|
||||
}
|
||||
&.w65 {
|
||||
width: 312px;
|
||||
}
|
||||
|
||||
&.h20 {
|
||||
height: 102px;
|
||||
line-height: 102px;
|
||||
}
|
||||
|
||||
&.reverse {
|
||||
background: #4caf50;
|
||||
color: #fff;
|
||||
}
|
||||
&.null {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
background: #f7f8fb;
|
||||
}
|
||||
&.reverse:active {
|
||||
background: #46a149;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 780px;
|
||||
|
||||
.row,
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin: 6px 0 0;
|
||||
|
||||
&.mt12 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tool,
|
||||
.numpad,
|
||||
.numpad-1 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 160px;
|
||||
margin: 0 16px;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 6px 0 0;
|
||||
&.mt12 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.numpad,
|
||||
.numpad-1 {
|
||||
margin: 0;
|
||||
}
|
||||
.numpad-1 {
|
||||
width: 54px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default class Keyboard {
|
||||
props = {}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -73,13 +73,13 @@
|
|||
height: 60px;
|
||||
padding: 15px;
|
||||
font-size: 16px;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-2);
|
||||
|
||||
wc-icon {
|
||||
--size: 14px;
|
||||
|
||||
&:hover {
|
||||
color: nth($cr, 1);
|
||||
color: var(--color-red-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,24 +122,24 @@
|
|||
}
|
||||
|
||||
::slotted(&__toast.style-info) {
|
||||
border: 1px solid nth($cp, 3);
|
||||
background: nth($cp, 1);
|
||||
color: nth($cgr, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
background: var(--color-plain-1);
|
||||
color: var(--color-grey-3);
|
||||
}
|
||||
::slotted(&__toast.style-success) {
|
||||
border: 1px solid #b3e19d;
|
||||
background: #f0f9eb;
|
||||
color: nth($cg, 2);
|
||||
color: var(--color-green-2);
|
||||
}
|
||||
::slotted(&__toast.style-warn) {
|
||||
border: 1px solid #faebb4;
|
||||
background: #fffbed;
|
||||
color: nth($co, 3);
|
||||
color: var(--color-orange-3);
|
||||
}
|
||||
::slotted(&__toast.style-error) {
|
||||
border: 1px solid #f5c4c4;
|
||||
background: #fef0f0;
|
||||
color: nth($cr, 1);
|
||||
color: var(--color-red-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@
|
|||
height: 30px;
|
||||
padding: 0 10px;
|
||||
margin: 0 5px;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
background: #fff;
|
||||
|
@ -169,11 +169,11 @@
|
|||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: nth($cgr, 1);
|
||||
border-color: var(--color-grey-1);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
|
@ -182,14 +182,14 @@
|
|||
|
||||
&:last-child {
|
||||
color: #fff;
|
||||
background: nth($ct, 2);
|
||||
background: var(--color-teal-2);
|
||||
border-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
&:active {
|
||||
background: nth($ct, 3);
|
||||
background: var(--color-teal-3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,7 +275,7 @@ function renderBtns(list) {
|
|||
return html
|
||||
}
|
||||
|
||||
class Layer {
|
||||
export default class Layer {
|
||||
props = {
|
||||
left: 'auto',
|
||||
right: 'auto',
|
||||
|
@ -789,6 +789,4 @@ Object.assign(_layer, {
|
|||
})
|
||||
|
||||
window.layer = _layer
|
||||
|
||||
export default _layer
|
||||
</script>
|
||||
|
|
|
@ -4,37 +4,32 @@
|
|||
* @date 2020/02/07 17:14:19
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
const HR_LIST = ['=', '-', '_', '*']
|
||||
const LIST_REG = /^(([\+\-\*])|(\d+\.))\s/
|
||||
const TODO_REG = /^\-\s\[(x|\s)\]\s/
|
||||
const ESCAPE_REG = /\\([-+*_`])/g
|
||||
const QLINK_REG = /^\[(\d+)\]: ([^\s]+)\s*?/
|
||||
const QLINK_REG = /^\[(\d+)\]: ([\S]+)\s*?((['"])[\s\S]*?\4)?\s*?$/
|
||||
const TAG_REG = /<([\w\-]+)([\w\W]*?)>/g
|
||||
const ATTR_REG = /^[\s\S]*?(style="[^"]*?")[\s\S]*?$/
|
||||
|
||||
const INLINE = {
|
||||
strong: [
|
||||
/__([^\s_])__(?!_)/g,
|
||||
/\*\*([^\s*])\*\*(?!\*)/g,
|
||||
/__([^\s][\s\S]*?[^\s])__(?!_)/g,
|
||||
/\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/g
|
||||
],
|
||||
em: [
|
||||
/_([^\s_])_(?!_)/g,
|
||||
/\*([^\s*])\*(?!\*)/g,
|
||||
/_([^\s][\s\S]*?[^\s])_(?!_)/g,
|
||||
/\*([^\s][\s\S]*?[^\s])\*(?!\*)/g
|
||||
],
|
||||
del: [/~~([^\~])~~(?!~)/g, /~~([^\s][\s\S]*?[^\s])~~(?!~)/g],
|
||||
qlink: /\[(.*?)\]\[(\d*?)\]/g
|
||||
code: /`([^`]*?[^`\\\s])`/g,
|
||||
strong: [/__([\s\S]*?[^\s\\])__(?!_)/g, /\*\*([\s\S]*?[^\s\\])\*\*(?!\*)/g],
|
||||
em: [/_([\s\S]*?[^\s\\])_(?!_)/g, /\*([\s\S]*?[^\s\\*])\*(?!\*)/g],
|
||||
del: /~~([\s\S]*?[^\s\\~])~~/g,
|
||||
qlink: /\[([^\]]*?)\]\[(\d*?)\]/g, // 引用链接
|
||||
img: /\!\[([^\]]*?)\]\(([^)]*?)\)/g,
|
||||
a: /\[([^\]]*?)\]\(([^)]*?)(\s+"([\s\S]*?)")*?\)/g,
|
||||
qlist: /((<blockquote class="md\-quote">)*?)([\+\-\*]|\d+\.) (.*)/ // 引用中的列表
|
||||
}
|
||||
const log = console.log
|
||||
|
||||
const Helper = {
|
||||
// 是否分割线
|
||||
isHr(str) {
|
||||
var s = str[0]
|
||||
if (HR_LIST.includes(s)) {
|
||||
return str.startsWith(s.repeat(3))
|
||||
var reg = new RegExp('^\\' + escape(s) + '{3,}$')
|
||||
return reg.test(str)
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
@ -67,7 +62,7 @@ const Helper = {
|
|||
},
|
||||
isQLink(str) {
|
||||
if (QLINK_REG.test(str)) {
|
||||
return RegExp.$2
|
||||
return { [RegExp.$1]: { l: RegExp.$2, t: RegExp.$3 } }
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
@ -80,21 +75,37 @@ const Decoder = {
|
|||
// 内联样式
|
||||
inline(str) {
|
||||
return str
|
||||
.replace(/`([^`]*?[^`\\\s])`/g, '<code class="inline">$1</code>')
|
||||
.replace(INLINE.code, '<code class="inline">$1</code>')
|
||||
.replace(INLINE.strong[0], '<strong>$1</strong>')
|
||||
.replace(INLINE.strong[1], '<strong>$1</strong>')
|
||||
.replace(INLINE.strong[2], '<strong>$1</strong>')
|
||||
.replace(INLINE.strong[3], '<strong>$1</strong>')
|
||||
.replace(INLINE.em[0], '<em>$1</em>')
|
||||
.replace(INLINE.em[1], '<em>$1</em>')
|
||||
.replace(INLINE.em[2], '<em>$1</em>')
|
||||
.replace(INLINE.em[3], '<em>$1</em>')
|
||||
.replace(INLINE.del[0], '<del>$1</del>')
|
||||
.replace(INLINE.del[1], '<del>$1</del>')
|
||||
.replace(/\!\[([^]*?)\]\(([^)]*?)\)/g, '<img src="$2" alt="$1">')
|
||||
.replace(/\[([^]*?)\]\(([^)]*?)\)/g, '<a href="$2">$1</a>')
|
||||
.replace(INLINE.qlink, (m, s, n) => {
|
||||
return `<a href="${this.__LINKS__[n - 1]}">${s}</a>`
|
||||
.replace(INLINE.del, '<del>$1</del>')
|
||||
.replace(INLINE.img, '<img src="$2" alt="$1">')
|
||||
.replace(INLINE.a, (m1, txt, link, m2, attr = '') => {
|
||||
var tmp = attr
|
||||
.split(';')
|
||||
.filter(_ => _)
|
||||
.map(_ => {
|
||||
var a = _.split('=')
|
||||
if (a.length > 1) {
|
||||
return `${a[0]}="${a[1]}"`
|
||||
} else {
|
||||
return `title="${_}"`
|
||||
}
|
||||
})
|
||||
.join(' ')
|
||||
|
||||
return `<a href="${link.trim()}" ${tmp}>${txt}</a>`
|
||||
})
|
||||
.replace(INLINE.qlink, (m, txt, n) => {
|
||||
var _ = this.__LINKS__[n]
|
||||
if (_) {
|
||||
var a = _.t ? `title=${_.t}` : ''
|
||||
return `<a href="${_.l}" ${a}>${txt}</a>`
|
||||
} else {
|
||||
return m
|
||||
}
|
||||
})
|
||||
.replace(ESCAPE_REG, '$1') // 处理转义字符
|
||||
},
|
||||
|
@ -131,7 +142,7 @@ const Decoder = {
|
|||
var stat = todoChecked === 1 ? 'checked' : ''
|
||||
var txt = todoChecked === 1 ? `<del>${word}</del>` : word
|
||||
|
||||
return `<section><wc-checkbox readonly ${stat}>${txt}</wc-checkbox></section>`
|
||||
return `<section><wc-checkbox-item readonly ${stat}>${txt}</wc-checkbox-item></section>`
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -151,8 +162,15 @@ class Tool {
|
|||
.replace(/\t/g, ' ')
|
||||
.replace(/\u00a0/g, ' ')
|
||||
.replace(/\u2424/g, '\n')
|
||||
.replace(TAG_REG, (m, name, attr) => {
|
||||
attr = attr.replace(/\n/g, '⨨☇') // 标签内的换行, 转为一组特殊字符, 方便后面还原
|
||||
if (attr) {
|
||||
attr = ' ' + attr
|
||||
}
|
||||
return `<${name + attr}>`
|
||||
})
|
||||
|
||||
var links = []
|
||||
var links = {}
|
||||
var list = []
|
||||
var lines = str.split('\n')
|
||||
var isCodeBlock = false // 是否代码块
|
||||
|
@ -184,10 +202,28 @@ class Tool {
|
|||
)
|
||||
isTable = true
|
||||
} else {
|
||||
var isQlink = Helper.isQLink(it)
|
||||
var qlink
|
||||
if (isCodeBlock) {
|
||||
it = it
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/⨨☇/g, '\n') // 代码块要还原回换行
|
||||
} else {
|
||||
it = it
|
||||
.replace(/(⨨☇)+/g, ' ') // 非代码块直接转为空格, 并进行xss过滤
|
||||
.replace(INLINE.code, (m, txt) => {
|
||||
return `\`${txt.replace(/</g, '<').replace(/>/g, '>')}\``
|
||||
})
|
||||
.replace(/<(\/?)script[^>]*?>/g, '<$1script>')
|
||||
.replace(TAG_REG, (m, name, attr) => {
|
||||
attr = attr.replace(ATTR_REG, '$1').trim()
|
||||
return `<${name} ${attr}>`
|
||||
})
|
||||
}
|
||||
qlink = Helper.isQLink(it)
|
||||
|
||||
if (isQlink) {
|
||||
links.push(isQlink)
|
||||
if (qlink) {
|
||||
Object.assign(links, qlink)
|
||||
} else {
|
||||
list.push(it)
|
||||
}
|
||||
|
@ -205,7 +241,6 @@ class Tool {
|
|||
list.push(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return new this(list, links)
|
||||
}
|
||||
|
||||
|
@ -223,50 +258,13 @@ class Tool {
|
|||
var orderListLevel = -1
|
||||
var unorderListLevel = -1
|
||||
|
||||
var isQuoteList = false // 引用中的列表, 只支持一层级
|
||||
var quoteListStyle = 0 // 1有序, 2 无序
|
||||
|
||||
//
|
||||
for (let it of this.list) {
|
||||
// 空行
|
||||
if (!it) {
|
||||
// 如果是在代码中, 直接拼接, 并加上换行
|
||||
if (isCodeBlock) {
|
||||
html += it + '\n'
|
||||
} else {
|
||||
emptyLineLength++
|
||||
|
||||
// 引用结束
|
||||
if (isBlockquote) {
|
||||
isBlockquote = false
|
||||
if (emptyLineLength > 0) {
|
||||
emptyLineLength = 0
|
||||
while (blockquoteLevel > 0) {
|
||||
blockquoteLevel--
|
||||
html += '</blockquote>'
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (isList) {
|
||||
while (orderListLevel > -1 || unorderListLevel > -1) {
|
||||
if (orderListLevel > unorderListLevel) {
|
||||
html += '</ol>'
|
||||
orderListLevel--
|
||||
} else {
|
||||
html += '</ul>'
|
||||
unorderListLevel--
|
||||
}
|
||||
}
|
||||
isList = false
|
||||
continue
|
||||
}
|
||||
|
||||
//
|
||||
if (isParagraph) {
|
||||
isParagraph = false
|
||||
html += '</p>'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 非空行
|
||||
if (it) {
|
||||
if (~it.indexOf('<table>') || ~it.indexOf('</table>')) {
|
||||
html += it
|
||||
isTable = !isTable
|
||||
|
@ -308,7 +306,7 @@ class Tool {
|
|||
|
||||
// 同上代码块的处理
|
||||
if (isCodeBlock) {
|
||||
html += it + '\n'
|
||||
html += '\n' + it
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -331,10 +329,8 @@ class Tool {
|
|||
|
||||
// 引用
|
||||
if (it.startsWith('>')) {
|
||||
if (isBlockquote) {
|
||||
html += '<br>'
|
||||
}
|
||||
html += it.replace(/^(>+) /, (p, m) => {
|
||||
let innerQuote // 是否有缩进引用
|
||||
it = it.replace(/^(>+) /, (p, m) => {
|
||||
let len = m.length
|
||||
let tmp = ''
|
||||
let loop = len
|
||||
|
@ -350,9 +346,49 @@ class Tool {
|
|||
}
|
||||
|
||||
blockquoteLevel = len
|
||||
innerQuote = !!tmp
|
||||
return tmp
|
||||
})
|
||||
|
||||
if (isBlockquote) {
|
||||
// 没有新的缩进引用时, 才添加换行
|
||||
if (innerQuote) {
|
||||
// 之前有引用的列表时, 直接结束列表
|
||||
if (isQuoteList) {
|
||||
html += `</${quoteListStyle === 1 ? 'ul' : 'ul'}>`
|
||||
isQuoteList = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let qListChecked = it.match(INLINE.qlist)
|
||||
if (qListChecked) {
|
||||
let tmp1 = qListChecked[1] // 缩进的标签
|
||||
let tmp2 = +qListChecked[3] // 有序还是无序
|
||||
let tmp3 = qListChecked.pop() // 文本
|
||||
let currListStyle = tmp2 === tmp2 ? 1 : 2
|
||||
var qlist = ''
|
||||
|
||||
// 已有列表
|
||||
if (isQuoteList) {
|
||||
// 因为只支持一层级的列表, 所以同一级别不区分有序无序, 强制统一
|
||||
} else {
|
||||
isQuoteList = true
|
||||
if (currListStyle === 1) {
|
||||
qlist += '<ol>'
|
||||
} else {
|
||||
qlist += '<ul>'
|
||||
}
|
||||
}
|
||||
|
||||
quoteListStyle = currListStyle
|
||||
|
||||
qlist += `<li>${tmp3}</li>`
|
||||
html += tmp1 + qlist
|
||||
} else {
|
||||
html += '<br>' + it
|
||||
}
|
||||
|
||||
isParagraph = false
|
||||
isBlockquote = true
|
||||
continue
|
||||
|
@ -375,15 +411,7 @@ class Tool {
|
|||
let level = Math.floor(ltrim / 2)
|
||||
let tag = listChecked > 0 ? 'ol' : 'ul'
|
||||
|
||||
if (!isList) {
|
||||
html += `<${tag}>`
|
||||
if (listChecked === 1) {
|
||||
orderListLevel = level
|
||||
} else {
|
||||
unorderListLevel = level
|
||||
}
|
||||
html += `<li>${word}</li>`
|
||||
} else {
|
||||
if (isList) {
|
||||
if (listChecked === 1) {
|
||||
if (level > orderListLevel) {
|
||||
html = html.replace(/<\/li>$/, '')
|
||||
|
@ -405,19 +433,72 @@ class Tool {
|
|||
}
|
||||
unorderListLevel = level
|
||||
}
|
||||
} else {
|
||||
html += `<${tag}>`
|
||||
if (listChecked === 1) {
|
||||
orderListLevel = level
|
||||
} else {
|
||||
unorderListLevel = level
|
||||
}
|
||||
html += `<li>${word}</li>`
|
||||
}
|
||||
|
||||
isList = true
|
||||
continue
|
||||
}
|
||||
|
||||
// log('it => ', isParagraph, it)
|
||||
// 无"> "前缀的引用, 继续拼到之前的, 并且不换行
|
||||
if (isBlockquote) {
|
||||
html += it
|
||||
continue
|
||||
}
|
||||
|
||||
if (isParagraph) {
|
||||
html += `${it}<br>`
|
||||
} else {
|
||||
html += `<p>${it}<br>`
|
||||
}
|
||||
isParagraph = true
|
||||
} else {
|
||||
// 如果是在代码中, 直接拼接, 并加上换行
|
||||
if (isCodeBlock) {
|
||||
html += it + '\n'
|
||||
} else {
|
||||
emptyLineLength++
|
||||
|
||||
// 引用结束
|
||||
if (isBlockquote) {
|
||||
isBlockquote = false
|
||||
if (emptyLineLength > 0) {
|
||||
emptyLineLength = 0
|
||||
while (blockquoteLevel > 0) {
|
||||
blockquoteLevel--
|
||||
html += '</blockquote>'
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (isList) {
|
||||
while (orderListLevel > -1 || unorderListLevel > -1) {
|
||||
if (orderListLevel > unorderListLevel) {
|
||||
html += '</ol>'
|
||||
orderListLevel--
|
||||
} else {
|
||||
html += '</ul>'
|
||||
unorderListLevel--
|
||||
}
|
||||
}
|
||||
isList = false
|
||||
continue
|
||||
}
|
||||
|
||||
//
|
||||
if (isParagraph) {
|
||||
isParagraph = false
|
||||
html += '</p>'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete this.list
|
||||
|
|
|
@ -6,28 +6,27 @@
|
|||
:host {
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
color: nth($cd, 1);
|
||||
color: var(--color-dark-1);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: nth($ct, 2);
|
||||
color: var(--color-teal-2);
|
||||
}
|
||||
a:hover {
|
||||
color: nth($ct, 1);
|
||||
color: var(--color-teal-1);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
em,
|
||||
del {
|
||||
color: nth($cgr, 2);
|
||||
color: var(--color-grey-2);
|
||||
}
|
||||
strong,
|
||||
strong em,
|
||||
strong,
|
||||
del {
|
||||
color: nth($cd, 3);
|
||||
strong {
|
||||
color: var(--color-dark-3);
|
||||
}
|
||||
a {
|
||||
strong,
|
||||
|
@ -41,7 +40,7 @@ del {
|
|||
padding: 0 2px;
|
||||
}
|
||||
p {
|
||||
margin: 15px 0;
|
||||
margin: 12px 0;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
|
@ -51,93 +50,24 @@ blockquote.md-quote {
|
|||
margin: 10px 0;
|
||||
padding: 5px 10px;
|
||||
line-height: 1.5;
|
||||
border-left: 5px solid nth($ct, 1);
|
||||
border-left: 5px solid var(--color-teal-1);
|
||||
background: #f2faf7;
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-2);
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 提醒文本 */
|
||||
.md-warn,
|
||||
.md-mark {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
min-height: 35px;
|
||||
margin: 3px 0;
|
||||
padding: 3px 8px 3px 35px;
|
||||
line-height: 27px;
|
||||
border: 1px solid nth($co, 2);
|
||||
border-radius: 5px;
|
||||
background: #fffbed;
|
||||
color: nth($co, 3);
|
||||
word-break: break-all;
|
||||
|
||||
p {
|
||||
margin: 0 !important;
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 6px;
|
||||
line-height: 1;
|
||||
font-size: 20px;
|
||||
color: nth($cr, 2);
|
||||
}
|
||||
}
|
||||
.md-mark {
|
||||
border-color: nth($ct, 1);
|
||||
color: nth($ct, 3);
|
||||
background: #edfbf8;
|
||||
i {
|
||||
color: nth($ct, 3);
|
||||
}
|
||||
}
|
||||
.md-task {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 30px;
|
||||
padding-right: 10px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
|
||||
&__box {
|
||||
float: left;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 6px;
|
||||
margin-left: 0;
|
||||
line-height: 1;
|
||||
border: 1px solid nth($cgr, 1);
|
||||
border-radius: 3px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
&.done {
|
||||
.md-task__box {
|
||||
color: nth($cgr, 1);
|
||||
border-color: nth($cp, 3);
|
||||
background: nth($cp, 3);
|
||||
}
|
||||
.md-task__text {
|
||||
color: nth($cgr, 1);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fieldset.md-hr {
|
||||
margin: 30px 0;
|
||||
border: 0;
|
||||
border-top: 1px dashed nth($cp, 3);
|
||||
border-top: 1px dashed var(--color-plain-3);
|
||||
|
||||
legend {
|
||||
padding: 0 5px;
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
&::before {
|
||||
content: '华丽丽的分割线';
|
||||
}
|
||||
|
@ -191,8 +121,8 @@ h5,
|
|||
h6 {
|
||||
a {
|
||||
&::before {
|
||||
content: '# ';
|
||||
color: nth($ct, 1);
|
||||
content: '∮ ';
|
||||
color: var(--color-teal-1);
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +135,7 @@ h1 {
|
|||
h2 {
|
||||
margin: 20px 0;
|
||||
font-size: 22px;
|
||||
border-bottom: 1px solid nth($cp, 2);
|
||||
border-bottom: 1px solid var(--color-plain-2);
|
||||
}
|
||||
h3 {
|
||||
margin: 20px 0 15px;
|
||||
|
@ -224,27 +154,27 @@ table {
|
|||
background-color: #fff;
|
||||
}
|
||||
thead tr {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid var(--color-plain-2);
|
||||
}
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #fbfbfb;
|
||||
background-color: #fcfdff;
|
||||
}
|
||||
}
|
||||
|
||||
code.inline {
|
||||
display: inline-block;
|
||||
display: inline;
|
||||
margin: 0 2px;
|
||||
padding: 0 2px;
|
||||
color: nth($co, 3);
|
||||
background: nth($cp, 1);
|
||||
color: var(--color-orange-3);
|
||||
background: var(--color-plain-1);
|
||||
border-radius: 2px;
|
||||
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
@ -252,10 +182,11 @@ code.inline {
|
|||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
import '../code/index'
|
||||
|
||||
import markd from './core'
|
||||
|
||||
import '../code/index'
|
||||
import '../form/checkbox'
|
||||
|
||||
export default class Markd {
|
||||
props = {
|
||||
toc: false
|
||||
|
@ -265,20 +196,51 @@ export default class Markd {
|
|||
/* render */
|
||||
|
||||
var elem = this.root.children[1]
|
||||
|
||||
this.__BOX__ = elem
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.__BOX__.innerHTML = markd(this.textContent)
|
||||
this.textContent = ''
|
||||
__parse__() {
|
||||
var txt = this.textContent.trim()
|
||||
this.value = txt
|
||||
}
|
||||
|
||||
$.bind(this.__BOX__, 'click', ev => {
|
||||
set value(txt) {
|
||||
if (txt) {
|
||||
this.__BOX__.innerHTML = markd(txt)
|
||||
this.textContent = ''
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.__BOX__.textContent = ''
|
||||
this.textContent = ''
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.__parse__()
|
||||
|
||||
this._headClickFn = $.bind(this.__BOX__, 'click', ev => {
|
||||
if (ev.target.className === 'md-head-link') {
|
||||
var ot = ev.target.offsetTop
|
||||
document.documentElement.scrollTop = ot
|
||||
}
|
||||
})
|
||||
|
||||
this.__observer = new MutationObserver(_ => {
|
||||
this.__parse__()
|
||||
})
|
||||
|
||||
this.__observer.observe(this, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.unbind(this.__BOX__, 'click', this._headClickFn)
|
||||
this.__observer.disconnect()
|
||||
this.clear()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* 基础拓展
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2020/10/14 17:52:44
|
||||
*/
|
||||
|
||||
import $ from '../utils'
|
||||
|
||||
var placeholder = '在此输入文本'
|
||||
|
||||
function trim(str, sign) {
|
||||
return str.replace(new RegExp('^' + sign + '|' + sign + '$', 'g'), '')
|
||||
}
|
||||
|
||||
function docScroll(k = 'X') {
|
||||
return window[`page${k.toUpperCase()}Offset`]
|
||||
}
|
||||
|
||||
// 通用的弹层触发
|
||||
function showDialog(dialog, elem) {
|
||||
var { left, top } = $.offset(elem)
|
||||
left -= docScroll('X')
|
||||
top += 29 - docScroll('Y')
|
||||
left += 'px'
|
||||
top += 'px'
|
||||
dialog.moveTo({ top, left })
|
||||
dialog.show()
|
||||
return Promise.resolve(dialog)
|
||||
}
|
||||
|
||||
export default {
|
||||
header(elem) {
|
||||
showDialog(this.__HEADER_ADDON__, elem)
|
||||
},
|
||||
|
||||
h(level) {
|
||||
var wrap = this.selection(true) || placeholder
|
||||
wrap = wrap.replace(/^(#+ )?/, '#'.repeat(level) + ' ')
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
quote(elem) {
|
||||
var wrap = this.selection(true) || placeholder
|
||||
wrap = wrap.replace(/^(>+ )?/, '> ')
|
||||
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
bold(elem) {
|
||||
var wrap = this.selection() || placeholder
|
||||
var unwrap = trim(wrap, '\\*\\*')
|
||||
wrap = wrap === unwrap ? `**${wrap}**` : unwrap
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
italic(elem) {
|
||||
var wrap = this.selection() || placeholder
|
||||
var unwrap = trim(wrap, '_')
|
||||
wrap = wrap === unwrap ? `_${wrap}_` : unwrap
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
through(elem) {
|
||||
var wrap = this.selection() || placeholder
|
||||
var unwrap = trim(wrap, '~~')
|
||||
wrap = wrap === unwrap ? `~~${wrap}~~` : unwrap
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
list(elem) {
|
||||
var wrap = this.selection(true) || placeholder
|
||||
|
||||
wrap = wrap.replace(/^([+\-*] )?/, '+ ')
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
order(elem) {
|
||||
var wrap = this.selection(true) || placeholder
|
||||
|
||||
wrap = wrap.replace(/^(\d+\. )?/, '1. ')
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
line(elem) {
|
||||
this.insert('\n\n---\n\n', false)
|
||||
},
|
||||
|
||||
code(elem) {
|
||||
var wrap = this.selection() || placeholder
|
||||
var unwrap = trim(wrap, '`')
|
||||
wrap = wrap === unwrap ? `\`${wrap}\`` : unwrap
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
|
||||
codeblock(elem) {
|
||||
this.insert('\n```language\n\n```\n')
|
||||
},
|
||||
|
||||
table(elem) {
|
||||
showDialog(this.__TABLE_ADDON__, elem)
|
||||
},
|
||||
|
||||
link(elem) {
|
||||
showDialog(this.__LINK_ADDON__, elem).then(dialog => {
|
||||
var wrap = this.selection() || placeholder
|
||||
dialog.__txt__.value = wrap
|
||||
})
|
||||
},
|
||||
|
||||
image(elem) {
|
||||
this._attach = 'image'
|
||||
showDialog(this.__ATTACH_ADDON__, elem)
|
||||
},
|
||||
|
||||
attach(elem) {
|
||||
this._attach = 'file'
|
||||
showDialog(this.__ATTACH_ADDON__, elem)
|
||||
},
|
||||
|
||||
fullscreen(elem) {
|
||||
//
|
||||
this.props.fullscreen = !this.props.fullscreen
|
||||
this.classList.toggle('fullscreen', this.props.fullscreen)
|
||||
elem.classList.toggle('active', this.props.fullscreen)
|
||||
},
|
||||
preview(elem) {
|
||||
this.props.preview = !this.props.preview
|
||||
this.__VIEW__.classList.toggle('active', this.props.preview)
|
||||
elem.classList.toggle('active', this.props.preview)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/**
|
||||
* 一些公共的东西
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2020/10/12 18:23:23
|
||||
*/
|
||||
|
||||
import ICONS from './svg'
|
||||
|
||||
const ELEMS = {
|
||||
a: function(str, attr, inner) {
|
||||
let href = attr.match(attrExp('href'))
|
||||
let title = attr.match(attrExp('title'))
|
||||
let tar = attr.match(attrExp('target'))
|
||||
let attrs = ''
|
||||
|
||||
href = (href && href[1]) || null
|
||||
title = (title && title[1]) || null
|
||||
tar = (tar && tar[1]) || '_self'
|
||||
|
||||
if (!href) {
|
||||
return inner || href
|
||||
}
|
||||
|
||||
href = href.replace('viod(0)', '')
|
||||
attrs = `target=${tar}`
|
||||
attrs += title ? `;title=${title}` : ''
|
||||
|
||||
return `[${inner || href}](${href} "${attrs}")`
|
||||
},
|
||||
em: function(str, attr, inner) {
|
||||
return (inner && '_' + inner + '_') || ''
|
||||
},
|
||||
strong: function(str, attr, inner) {
|
||||
return (inner && '**' + inner + '**') || ''
|
||||
},
|
||||
pre: function(str, attr, inner) {
|
||||
inner = inner.replace(/<[/]?code>/g, '')
|
||||
return '\n\n```\n' + inner + '\n```\n'
|
||||
},
|
||||
code: function(str, attr, inner) {
|
||||
return (inner && '`' + inner + '`') || ''
|
||||
},
|
||||
blockquote: function(str, attr, inner) {
|
||||
return '> ' + inner.trim()
|
||||
},
|
||||
img: function(str, attr, inner) {
|
||||
var src = attr.match(attrExp('src')),
|
||||
alt = attr.match(attrExp('alt'))
|
||||
|
||||
src = (src && src[1]) || ''
|
||||
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'
|
||||
}
|
||||
|
||||
const DEFAULT_TOOLS = [
|
||||
'header',
|
||||
'quote',
|
||||
'bold',
|
||||
'italic',
|
||||
'through',
|
||||
'list',
|
||||
'order',
|
||||
'line',
|
||||
'code',
|
||||
'codeblock',
|
||||
'table',
|
||||
'link',
|
||||
'image',
|
||||
'attach',
|
||||
'fullscreen',
|
||||
'preview'
|
||||
]
|
||||
|
||||
export const TOOL_TITLE = {
|
||||
header: '插入标题',
|
||||
h1: '一级标题',
|
||||
h2: '二级标题',
|
||||
h3: '三级标题',
|
||||
h4: '四级标题',
|
||||
h5: '五级标题',
|
||||
h6: '六级标题',
|
||||
quote: '引用文本',
|
||||
bold: '粗体',
|
||||
italic: '斜体',
|
||||
through: '横线',
|
||||
list: '无序列表',
|
||||
order: '有序列表',
|
||||
line: '分割线',
|
||||
code: '行内代码',
|
||||
codeblock: '插入代码块',
|
||||
table: '插入表格',
|
||||
link: '插入连接',
|
||||
image: '上传图片',
|
||||
attach: '上传附件',
|
||||
fullscreen: '全屏编辑',
|
||||
preview: '预览'
|
||||
}
|
||||
|
||||
export const IMAGE_EXP = /image\/(jpeg|gif|png|webp|bmp|vnd\.microsoft\.icon|svg\+xml)/
|
||||
|
||||
// html标签的属性正则
|
||||
function attrExp(field, flag = 'i') {
|
||||
return new RegExp(field + '\\s?=\\s?["\']?([^"\']*)["\']?', flag)
|
||||
}
|
||||
|
||||
// 生成html标签的正则
|
||||
function tagExp(tag, open) {
|
||||
var exp = ''
|
||||
if (['br', 'hr', 'img'].indexOf(tag) > -1) {
|
||||
exp = '<' + tag + '([^>]*?)\\/?>'
|
||||
} else {
|
||||
exp = '<' + tag + '([^>]*?)>([\\s\\S]*?)<\\/' + tag + '>'
|
||||
}
|
||||
return new RegExp(exp, 'gi')
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染工具栏图标
|
||||
*/
|
||||
export function renderToolbar(list, tag = 'span', dict = {}, showText = false) {
|
||||
return (list || DEFAULT_TOOLS)
|
||||
.map(it => {
|
||||
var title = showText ? '' : `title="${dict[it] || ''}"`
|
||||
var text = showText ? dict[it] || '' : ''
|
||||
|
||||
return `<${tag} data-act="${it}" ${title}><svg class="icon" viewBox="0 0 1024 1024"><path d="${
|
||||
ICONS[it]
|
||||
}"/></svg>${text}</${tag}>`
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* html转成md
|
||||
*/
|
||||
export function html2md(str) {
|
||||
try {
|
||||
str = decodeURIComponent(str)
|
||||
} catch (err) {}
|
||||
|
||||
str = str
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/<meta [^>]*>/, '')
|
||||
.replace(attrExp('class', 'g'), '')
|
||||
.replace(attrExp('style', 'g'), '')
|
||||
.replace(/<(?!a |img )(\w+) [^>]*>/g, '<$1>')
|
||||
.replace(/<svg[^>]*>.*?<\/svg>/g, '{invalid image}')
|
||||
|
||||
// log(str)
|
||||
for (let i in ELEMS) {
|
||||
let cb = ELEMS[i]
|
||||
let exp = tagExp(i)
|
||||
|
||||
if (i === 'blockquote') {
|
||||
while (str.match(exp)) {
|
||||
str = str.replace(exp, cb)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
if (i === 'strong') {
|
||||
exp = tagExp('b')
|
||||
str = str.replace(exp, cb)
|
||||
}
|
||||
}
|
||||
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(
|
||||
m,
|
||||
t,
|
||||
inner
|
||||
) {
|
||||
let li = inner.split('</li>')
|
||||
li.pop()
|
||||
|
||||
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) {
|
||||
n = n.trim().replace(/\n/g, '\n ')
|
||||
return n
|
||||
})
|
||||
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
||||
}
|
||||
return li.join('\n')
|
||||
})
|
||||
return '\n' + match.trim()
|
||||
})
|
||||
}
|
||||
str = str
|
||||
|
||||
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
||||
.replace(/```([\w\W]*)```/g, function(str, inner) {
|
||||
inner = inner
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
return '```' + inner + '```'
|
||||
})
|
||||
return str
|
||||
}
|
|
@ -1,29 +1,824 @@
|
|||
<template>
|
||||
<div class="meditor">
|
||||
<section class="toolbar"></section>
|
||||
<textarea cols="30" rows="10"></textarea>
|
||||
<wc-markd></wc-markd>
|
||||
<div class="editor-outbox">
|
||||
<textarea class="editor" spellcheck="false"></textarea>
|
||||
<wc-markd class="preview"></wc-markd>
|
||||
</div>
|
||||
</div>
|
||||
<wc-layer id="header"><ul class="addon-header"></ul></wc-layer>
|
||||
<wc-layer id="table"><ul class="addon-table"></ul></wc-layer>
|
||||
<wc-layer id="link">
|
||||
<ul class="addon-link">
|
||||
<li>
|
||||
<label>链接文本</label><wc-input class="txt" size="mini"></wc-input>
|
||||
</li>
|
||||
<li>
|
||||
<label>链接地址</label><wc-input class="link" size="mini"></wc-input>
|
||||
</li>
|
||||
<li>
|
||||
<label>打开方式</label>
|
||||
<wc-radio-group value="_self" class="mode">
|
||||
<wc-radio value="_self" size="mini">当前窗口</wc-radio>
|
||||
<wc-radio value="_blank" size="mini">新窗口</wc-radio>
|
||||
</wc-radio-group>
|
||||
</li>
|
||||
<li>
|
||||
<label></label>
|
||||
<wc-button class="submit" color="teal" size="mini">确定</wc-button>
|
||||
</li>
|
||||
</ul>
|
||||
</wc-layer>
|
||||
<wc-layer id="attach">
|
||||
<div class="addon-attach">
|
||||
<header class="tabs">
|
||||
<span data-key="remote" class="active">远程文件</span>
|
||||
<span data-key="locale">本地上传</span>
|
||||
</header>
|
||||
<ul class="remote active">
|
||||
<li>
|
||||
<label>文件描述</label><wc-input class="txt" size="mini"></wc-input>
|
||||
</li>
|
||||
<li>
|
||||
<label>文件地址</label><wc-input class="link" size="mini"></wc-input>
|
||||
</li>
|
||||
<li>
|
||||
<label></label>
|
||||
<wc-button class="submit" color="teal" size="mini">确定</wc-button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="locale">
|
||||
<div class="button"><input type="file" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</wc-layer>
|
||||
</template>
|
||||
|
||||
<style lang="scss"></style>
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: flex;
|
||||
min-width: 200px;
|
||||
min-height: 100px;
|
||||
// max-height: 360px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.meditor {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--color-plain-2);
|
||||
border-radius: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: none;
|
||||
height: 34px;
|
||||
padding: 5px;
|
||||
line-height: 24px;
|
||||
border-bottom: 1px solid var(--color-plain-2);
|
||||
|
||||
span {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 3px;
|
||||
border-radius: 3px;
|
||||
color: var(--color-dark-1);
|
||||
|
||||
.icon {
|
||||
overflow: hidden;
|
||||
width: 70%;
|
||||
height: 70%;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
&.active {
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-outbox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-height: 300px;
|
||||
background: #fff;
|
||||
|
||||
.editor,
|
||||
.preview {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.editor {
|
||||
height: 100%;
|
||||
padding: 5px 8px;
|
||||
line-height: 1.5;
|
||||
border: 0;
|
||||
font-size: 14px;
|
||||
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
color: var(--color-grey-3);
|
||||
background: none;
|
||||
outline: none;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.preview {
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
padding: 6px 12px;
|
||||
border-left: 1px solid var(--color-plain-2);
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addon-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 222px;
|
||||
height: 222px;
|
||||
padding: 2px;
|
||||
background: #fff;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 20px;
|
||||
|
||||
span {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--color-plain-1);
|
||||
|
||||
&.active {
|
||||
background: rgba(77, 182, 172, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addon-header {
|
||||
width: 108px;
|
||||
height: 190px;
|
||||
padding: 5px 0;
|
||||
line-height: 30px;
|
||||
user-select: none;
|
||||
background: #fff;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
padding: 0 12px;
|
||||
transition: background 0.1s ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addon-link {
|
||||
width: 320px;
|
||||
padding: 8px 5px;
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
margin-top: 6px;
|
||||
|
||||
label {
|
||||
width: 60px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
wc-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
wc-button {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addon-attach {
|
||||
width: 320px;
|
||||
padding: 8px 5px;
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--color-plain-2);
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
line-height: 28px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remote,
|
||||
.locale {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.locale {
|
||||
height: 120px;
|
||||
padding: 24px 32px;
|
||||
|
||||
.button {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 12px 16px;
|
||||
line-height: 46px;
|
||||
border: 1px dashed var(--color-plain-3);
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: background 0.1s ease-in-out;
|
||||
|
||||
&::after {
|
||||
content: '点击选择文件,或拖拽文件到此处';
|
||||
}
|
||||
&:hover,
|
||||
&.active {
|
||||
background: rgba(255, 228, 196, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
margin-top: 6px;
|
||||
|
||||
label {
|
||||
width: 60px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
wc-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
wc-button {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
.meditor {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
:host(.fullscreen) {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1024;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
|
||||
import '../form/input'
|
||||
import '../form/button'
|
||||
import '../form/radio-group'
|
||||
import '../layer/index'
|
||||
import '../markd/index'
|
||||
|
||||
import { renderToolbar, html2md, TOOL_TITLE, IMAGE_EXP } from './helper'
|
||||
import Addon from './addon'
|
||||
|
||||
export default class Meditor {
|
||||
props = {
|
||||
value: ''
|
||||
toolbar: null,
|
||||
value: '',
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
height: 180,
|
||||
preview: window.innerWidth > 768
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
var elem = this.root.children[1]
|
||||
var ct = this.root.children[1]
|
||||
this.__TOOLBAR__ = ct.children[0]
|
||||
this.__EDITOR__ = ct.children[1].firstElementChild
|
||||
this.__VIEW__ = ct.children[1].lastElementChild
|
||||
|
||||
this.__TOOL__ = elem.children[0]
|
||||
this.__EDITOR__ = elem.children[1]
|
||||
this.__VIEW__ = elem.children[2]
|
||||
this.__HEADER_ADDON__ = this.root.querySelector('#header')
|
||||
this.__TABLE_ADDON__ = this.root.querySelector('#table')
|
||||
this.__LINK_ADDON__ = this.root.querySelector('#link')
|
||||
this.__ATTACH_ADDON__ = this.root.querySelector('#attach')
|
||||
}
|
||||
|
||||
_updateToolbar() {
|
||||
var { toolbar, preview } = this.props
|
||||
var isToolbarShow = true
|
||||
if (Array.isArray(toolbar)) {
|
||||
if (toolbar.length) {
|
||||
this.__TOOLBAR__.innerHTML = renderToolbar(toolbar, 'span', TOOL_TITLE)
|
||||
} else {
|
||||
isToolbarShow = false
|
||||
}
|
||||
} else {
|
||||
this.__TOOLBAR__.innerHTML = renderToolbar(null, 'span', TOOL_TITLE)
|
||||
}
|
||||
this.__TOOLBAR__.classList.toggle('active', isToolbarShow)
|
||||
this.__TOOLBAR__.lastElementChild.classList.toggle('active', preview)
|
||||
this.__VIEW__.classList.toggle('active', preview)
|
||||
}
|
||||
|
||||
_createTableAddon() {
|
||||
var html = ''
|
||||
var $table = this.__TABLE_ADDON__.firstElementChild
|
||||
var $item
|
||||
var lastx = -1
|
||||
var lasty = -1
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
html += '<li>'
|
||||
for (let j = 0; j < 10; j++) {
|
||||
html += `<span data-x="${i}" data-y="${j}"></span>`
|
||||
}
|
||||
html += '</li>'
|
||||
}
|
||||
$table.innerHTML = html
|
||||
|
||||
$item = Array.from($table.children).map(it => Array.from(it.children))
|
||||
|
||||
$.bind($table, 'mousemove', ev => {
|
||||
if (ev.target.tagName === 'SPAN') {
|
||||
var x = +ev.target.dataset.x
|
||||
var y = +ev.target.dataset.y
|
||||
if (x === lastx && y === lasty) {
|
||||
return
|
||||
}
|
||||
lastx = x
|
||||
lasty = y
|
||||
for (var i = 0; i < 10; i++) {
|
||||
for (var j = 0; j < 10; j++) {
|
||||
$item[i][j].classList.toggle('active', i <= x && j <= y)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$.bind($table, 'mouseleave', ev => {
|
||||
lastx = -1
|
||||
lasty = -1
|
||||
for (var i = 0; i < 10; i++) {
|
||||
for (var j = 0; j < 10; j++) {
|
||||
$item[i][j].classList.remove('active')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$.bind($table, 'click', ev => {
|
||||
if (lastx < 0 || lasty < 0) {
|
||||
return
|
||||
}
|
||||
var thead = `\n\n${'| 表头 '.repeat(lastx)}|\n`
|
||||
var pipe = `${'| -- '.repeat(lastx)}|\n`
|
||||
var tbody = ('| '.repeat(lastx) + '|\n').repeat(lasty)
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
for (var j = 0; j < 10; j++) {
|
||||
$item[i][j].classList.remove('active')
|
||||
}
|
||||
}
|
||||
lastx = -1
|
||||
lasty = -1
|
||||
this.insert(thead + pipe + tbody, false)
|
||||
this.__TABLE_ADDON__.close()
|
||||
})
|
||||
|
||||
$.outside($table, _ => {
|
||||
lastx = -1
|
||||
lasty = -1
|
||||
this.__TABLE_ADDON__.close()
|
||||
})
|
||||
}
|
||||
|
||||
_createHeaderAddon() {
|
||||
var items = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
|
||||
var html = renderToolbar(items, 'li', TOOL_TITLE, true)
|
||||
var $header = this.__HEADER_ADDON__.firstElementChild
|
||||
|
||||
$header.innerHTML = html
|
||||
|
||||
$.bind($header, 'click', ev => {
|
||||
var elem = ev.target
|
||||
var level
|
||||
if (elem === ev.currentTarget) {
|
||||
return
|
||||
}
|
||||
while (elem.tagName !== 'LI') {
|
||||
elem = elem.parentNode
|
||||
}
|
||||
level = +elem.dataset.act.slice(1)
|
||||
Addon.h.call(this, level)
|
||||
this.__HEADER_ADDON__.close()
|
||||
})
|
||||
|
||||
$.outside($header, _ => {
|
||||
this.__HEADER_ADDON__.close()
|
||||
})
|
||||
}
|
||||
|
||||
_createLinkAddon() {
|
||||
var $link = this.__LINK_ADDON__.firstElementChild
|
||||
var $txt = $link.querySelector('.txt')
|
||||
var $url = $link.querySelector('.link')
|
||||
var $mode = $link.querySelector('.mode')
|
||||
var $submit = $link.querySelector('.submit')
|
||||
|
||||
this.__LINK_ADDON__.__txt__ = $txt
|
||||
// Addon.link.call(this, this.__TOOLBAR__.children[12])
|
||||
|
||||
$.bind($submit, 'click', ev => {
|
||||
var txt = $txt.value.trim()
|
||||
var url = $url.value.trim()
|
||||
var mode = $mode.value.trim()
|
||||
var attr = ''
|
||||
|
||||
if (mode === '_blank') {
|
||||
attr = ` "target=${mode}"`
|
||||
}
|
||||
if (txt && url) {
|
||||
this.insert(`[${txt}](${url + attr}) `)
|
||||
$txt.value = ''
|
||||
$url.value = ''
|
||||
this.__LINK_ADDON__.close()
|
||||
}
|
||||
})
|
||||
|
||||
$.outside($link, _ => {
|
||||
this.__LINK_ADDON__.close()
|
||||
})
|
||||
}
|
||||
|
||||
_createAttachAddon() {
|
||||
var $attach = this.__ATTACH_ADDON__.firstElementChild
|
||||
var $tabs = $attach.querySelector('.tabs')
|
||||
var $remote = $attach.querySelector('.remote')
|
||||
var $locale = $attach.querySelector('.locale')
|
||||
var $submit = $attach.querySelector('.submit')
|
||||
var $drag = $locale.querySelector('.button')
|
||||
var $file = $locale.querySelector('input')
|
||||
var $txt = $remote.querySelector('.txt')
|
||||
var $url = $remote.querySelector('.link')
|
||||
|
||||
$.bind($tabs, 'click', ev => {
|
||||
var key = ev.target.dataset.key
|
||||
|
||||
if (key) {
|
||||
ev.target.classList.toggle('active', true)
|
||||
//
|
||||
if (ev.target.nextElementSibling) {
|
||||
ev.target.nextElementSibling.classList.toggle('active', false)
|
||||
$remote.classList.toggle('active', true)
|
||||
$locale.classList.toggle('active', false)
|
||||
} else {
|
||||
ev.target.previousElementSibling.classList.toggle('active', false)
|
||||
$remote.classList.toggle('active', false)
|
||||
$locale.classList.toggle('active', true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 拖拽上传
|
||||
$.bind($drag, 'dragover', ev => {
|
||||
ev.stopPropagation()
|
||||
ev.preventDefault()
|
||||
$drag.classList.toggle('active', true)
|
||||
})
|
||||
$.bind($drag, 'dragleave', ev => {
|
||||
ev.stopPropagation()
|
||||
ev.preventDefault()
|
||||
$drag.classList.toggle('active', false)
|
||||
})
|
||||
$.bind($drag, 'drop', ev => {
|
||||
ev.stopPropagation()
|
||||
ev.preventDefault()
|
||||
$drag.classList.toggle('active', false)
|
||||
|
||||
var file = ev.dataTransfer.files[0]
|
||||
if (file) {
|
||||
if (this._attach === 'image') {
|
||||
if (IMAGE_EXP.test(file.type)) {
|
||||
this._handleUpload(file)
|
||||
}
|
||||
} else {
|
||||
this._handleUpload(file, '')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 点击上传
|
||||
$.bind($file, 'change', ev => {
|
||||
var file = ev.target.files[0]
|
||||
if (file) {
|
||||
ev.target.value = ''
|
||||
if (this._attach === 'image') {
|
||||
if (IMAGE_EXP.test(file.type)) {
|
||||
this._handleUpload(file)
|
||||
}
|
||||
} else {
|
||||
this._handleUpload(file, '')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 远程地址
|
||||
$.bind($submit, 'click', ev => {
|
||||
var txt = $txt.value.trim()
|
||||
var url = $url.value.trim()
|
||||
|
||||
if (txt && url) {
|
||||
this.insert(`${this._attach === 'image' ? '!' : ''}[${txt}](${url}) `)
|
||||
$txt.value = ''
|
||||
$url.value = ''
|
||||
this.__ATTACH_ADDON__.close()
|
||||
}
|
||||
})
|
||||
|
||||
$.outside($attach, _ => {
|
||||
this.__ATTACH_ADDON__.close()
|
||||
})
|
||||
}
|
||||
|
||||
// 处理文件上传
|
||||
_handleUpload(file, t = '!') {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('upload', {
|
||||
detail: {
|
||||
file,
|
||||
send: link => {
|
||||
this.insert(`${t}[${file.name}](${link})`)
|
||||
this.__ATTACH_ADDON__.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// 往文本框中插入内容
|
||||
insert(val, isSelect) {
|
||||
var dom = this.__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.value = dom.value
|
||||
}
|
||||
/**
|
||||
* [selection 获取选中的文本]
|
||||
* @param {[type]} dom [要操作的元素]
|
||||
* @param {[type]} forceHoleLine [是否强制光标所在的整行文本]
|
||||
*/
|
||||
selection(forceHoleLine) {
|
||||
var dom = this.__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)
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.__EDITOR__.value
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.__EDITOR__.value = val
|
||||
if (this.props.preview) {
|
||||
this.__VIEW__.textContent = val
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this._updateToolbar()
|
||||
|
||||
this._createHeaderAddon()
|
||||
this._createTableAddon()
|
||||
this._createLinkAddon()
|
||||
this._createAttachAddon()
|
||||
|
||||
$.bind(this.__EDITOR__, 'input', _ => {
|
||||
if (this.props.preview) {
|
||||
var txt = this.__EDITOR__.value.trim()
|
||||
if (txt) {
|
||||
this.__VIEW__.textContent = txt
|
||||
} else {
|
||||
this.__VIEW__.clear()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$.bind(this.__EDITOR__, '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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$.bind(this.__EDITOR__, 'paste', ev => {
|
||||
ev.preventDefault()
|
||||
var txt = ev.clipboardData.getData('text/plain').trim()
|
||||
var html = ev.clipboardData.getData('text/html').trim()
|
||||
var items = ev.clipboardData.items
|
||||
|
||||
if (html) {
|
||||
html = html2md(html)
|
||||
return this.insert(html)
|
||||
}
|
||||
|
||||
if (txt) {
|
||||
return this.insert(txt)
|
||||
}
|
||||
|
||||
if (items && items.length) {
|
||||
let file = null
|
||||
|
||||
for (let it of items) {
|
||||
file = it.getAsFile()
|
||||
break
|
||||
}
|
||||
if (file) {
|
||||
if (IMAGE_EXP.test(file.type)) {
|
||||
this._handleUpload(file)
|
||||
} else {
|
||||
this._handleUpload(file, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$.bind(this.__TOOLBAR__, 'click', ev => {
|
||||
ev.preventDefault()
|
||||
var elem = ev.target
|
||||
var act
|
||||
if (elem === ev.currentTarget) {
|
||||
return
|
||||
}
|
||||
while (elem.tagName !== 'SPAN') {
|
||||
elem = elem.parentNode
|
||||
}
|
||||
act = elem.dataset.act
|
||||
Addon[act].call(this, elem)
|
||||
})
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'toolbar':
|
||||
if (val) {
|
||||
val = val.split(',').map(it => it.trim())
|
||||
this.props.toolbar = val
|
||||
}
|
||||
break
|
||||
|
||||
case 'value':
|
||||
this.value = val
|
||||
break
|
||||
|
||||
case 'readonly':
|
||||
case 'disabled':
|
||||
var k = name
|
||||
if (k === 'readonly') {
|
||||
k = 'readOnly'
|
||||
}
|
||||
this[k] = true
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* icon字典
|
||||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2019/07/08 17:39:11
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
export default {
|
||||
header:
|
||||
'M128 457.15h329.14v109.71H347.43v329.13H237.71V566.86H128V457.15z m767.98-219.43H680.21v658.27h-117V237.72H347.44V128.01H896l-0.02 109.71z',
|
||||
h1:
|
||||
'M567.23 896h-62.15V523.45H222.22V896h-62.93V128h62.93v326.11h282.86V128h62.15v768z m297.48-1.48h-230.2v-59.5h86.99V535.26h-69.38v-47.71l6.3-1.26c32.06-6.4 57.01-15.6 78.51-28.96l1.9-1.18h47.43v378.86h78.45v59.51z',
|
||||
h2:
|
||||
'M864.54 896H639.87v-41.55l2.05-2.24C745.85 738.4 792.2 657.17 792.2 588.8c0-67.27-37.32-74.53-53.37-74.53-26.94 0-48.8 24.36-62.39 44.79l-5.89 8.85-34.57-42.49 3.6-4.88C663.09 488.62 693.86 456 743.82 456c63.42 0 104.4 51.46 104.4 131.1 0 40.81-12.01 83.37-36.73 130.12-19.26 36.44-45.83 75.01-83 120.37 12.26-1.15 24.97-2.13 36.18-2.13h99.87V896z m-297.34-0.37h-62.12V523.26H222.36v372.37h-62.91V128h62.91v325.95h282.72V128h62.12v767.63z',
|
||||
h3:
|
||||
'M750.38 895.4c-25.28 0-48.37-6.06-68.62-18.01-16.41-9.68-30.85-22.96-44.14-40.59l-3.25-4.31 29.65-47.34 6.88 8.82c23.21 29.74 47.46 43.58 76.3 43.58 36.26 0 59.69-26.54 59.69-67.61 0-50.29-30.34-73.71-95.49-73.71h-7.84v-54.51h7.84c30.05 0 52.33-6.8 66.22-20.2 11.9-11.49 17.94-28.18 17.94-49.63 0-34.99-18.06-56.51-48.32-57.62-27.59 1.28-49.4 21.14-62.85 37.61l-6.6 8.08-31.61-45.8 4.05-4.59c30.51-34.64 62.83-51.48 98.79-51.48 30.23 0 56.07 9.96 74.74 28.81 19.38 19.58 29.63 47.62 29.63 81.1 0 43.53-17.82 77.85-50.55 98.17 15.76 7.15 29.34 18.23 39.81 32.57 14.44 19.79 22.07 44.99 22.07 72.85 0 36.77-11.54 68.12-33.36 90.68-20.67 21.37-49.43 33.13-80.98 33.13z m-183.15 0.6h-62.15V523.45H222.22V896h-62.94V128h62.94v326.11h282.86V128h62.15v768z',
|
||||
h4:
|
||||
'M823.31 894.76h-52.82V778.48H634.31v-44.16l133.11-277.17h55.89v263.64h41.41v57.7h-41.41v116.27zM695.34 720.79h75.15V599.11c0-9.81 0.37-22.31 0.86-35.25-3.17 7.66-6.55 15.63-10.16 23.98l-0.17 0.37-65.68 132.58zM567.23 896h-62.15V523.45H222.22V896h-62.94V128h62.94v326.11h282.86V128h62.15v768z',
|
||||
h5:
|
||||
'M567.23 896h-62.15V523.45H222.22V896h-62.94V128h62.94v326.11h282.86V128h62.15v768z m181.59-0.16c-58.28 0-91.28-33.08-111.04-57.97l-3.36-4.24 28.84-48.43 7.06 8.89c30.07 37.86 54.74 42.97 74.91 42.97 16.75 0 32.15-8.23 43.37-23.16 12.45-16.58 19.03-39.78 19.03-67.1 0-27.27-5.93-49.76-17.16-65.04-10.5-14.28-25.51-21.83-43.44-21.83-19.59 0-31.69 7.27-49.89 22.55l-4.83 4.06-30.32-23.38 10.97-206.03h176v59.91H723.73l-6.59 97.77c12.81-6.63 25.67-9.67 40.24-9.67 31.08 0 57.49 11.98 76.37 34.65 20.55 24.67 30.97 60.1 30.97 105.32-0.01 98.95-58.31 150.73-115.9 150.73z',
|
||||
h6:
|
||||
'M567.22 895.92h-62.14V523.41H222.25v372.51h-62.93V128h62.93v326.08h282.83V128h62.14v767.92z m191.63 0.08c-36.45 0-67.06-18.74-88.53-54.2-22-36.32-33.62-90.02-33.62-155.31 0-75.99 13.35-134.66 39.68-174.4 23.41-35.32 56.12-54 94.61-54 33.88 0 62.8 15.03 85.97 44.68l3.66 4.69-33.35 45.27-6.25-8.98c-12.1-17.38-30.28-27.75-48.64-27.75-22.59 0-40.82 11.25-54.18 33.43-14.23 23.61-22.73 58.91-25.36 105.12 20.59-21.5 45.78-34.22 69.27-34.22 31.54 0 57.57 12.13 75.26 35.06 18.11 23.48 27.3 57.31 27.3 100.55 0 39.09-10.89 74.77-30.67 100.48-19.64 25.52-46.33 39.58-75.15 39.58z m-65.74-175.89c2.63 39.75 9.87 70.41 21.54 91.16 11.06 19.65 25.93 29.62 44.2 29.62 28.99 0 50.02-35.72 50.02-84.94 0-26.16-4.82-47.37-13.95-61.34-9.03-13.82-22.42-20.82-39.81-20.82-21.46-0.01-43.98 16.84-62 46.32z',
|
||||
codeblock:
|
||||
'M819.2 864H204.8c-42.35 0-76.8-35.89-76.8-80V240c0-44.11 34.45-80 76.8-80h614.4c42.35 0 76.8 35.89 76.8 80v544c0 44.11-34.45 80-76.8 80zM233.74 221.09c-23.02 0-41.74 19.58-41.74 43.64v494.55c0 24.06 18.72 43.64 41.74 43.64h556.52c23.02 0 41.74-19.57 41.74-43.64V264.73c0-24.06-18.72-43.64-41.74-43.64H233.74z m410.77 417.59l124.33-106.53c4.56-3.9 7.17-9.61 7.16-15.66 0-6.04-2.62-11.75-7.18-15.64L644.44 394.58l-0.08-0.07c-4.15-3.45-9.38-5.07-14.74-4.55-5.37 0.52-10.21 3.12-13.65 7.33-6.96 8.51-5.91 21.33 2.34 28.56l106.15 90.68-106.04 90.86c-5.59 4.64-8.33 12-7.14 19.23 1.2 7.3 6.21 13.39 13.08 15.91 2.23 0.82 4.55 1.21 6.85 1.21a20.11 20.11 0 0 0 13.3-5.06zM414.63 774.64c5.1-1.98 9.11-5.86 11.29-10.93L635.93 276.7c4.44-10.29-0.19-22.37-10.3-26.93-4.92-2.22-10.41-2.37-15.44-0.41-5.1 1.98-9.11 5.86-11.29 10.93L388.88 747.3c-4.44 10.28 0.18 22.36 10.3 26.93 2.63 1.19 5.42 1.78 8.21 1.78 2.45 0 4.89-0.46 7.24-1.37z m-20.52-129.77c8.5 0 16.14-5.45 19.02-13.56 2.84-8.03 0.43-17.11-6.01-22.61L299.6 516.99l107.47-91.67c5.54-4.65 8.23-12.01 7.03-19.19-1.21-7.22-6.17-13.28-12.95-15.81a20.027 20.027 0 0 0-20.06 3.61L255.2 501.31c-4.57 3.9-7.2 9.61-7.2 15.68 0 6.06 2.62 11.77 7.2 15.67l125.88 107.38a20 20 0 0 0 12.99 4.82c0.02 0.01 0.02 0.01 0.04 0.01z',
|
||||
code:
|
||||
'M686.87 681.71c-2.86 0-5.73-0.45-8.5-1.38-8.85-2.96-15.33-10.29-16.91-19.12-1.6-8.91 2-17.92 9.4-23.53L829.48 513.1 670.76 388.8c-5.33-4.28-8.57-10.3-9.12-16.94-0.55-6.57 1.61-12.96 6.08-17.97 9.08-10.2 24.78-11.64 35.74-3.29l0.08 0.06 183.01 143.31c6 4.7 9.44 11.66 9.44 19.08 0 7.43-3.43 14.39-9.43 19.09l-182.94 143.7c-4.72 3.84-10.7 5.87-16.75 5.87zM356.91 865.09c-3.4 0-6.86-0.65-10.16-2.01-6.53-2.7-11.49-7.7-13.96-14.08-2.37-6.12-2.13-12.77 0.67-18.72l308.99-656.84c5.75-12.23 20.9-17.85 33.78-12.53 6.53 2.7 11.49 7.7 13.96 14.09 2.37 6.12 2.13 12.77-0.67 18.72L380.54 850.56c-4.28 9.09-13.75 14.53-23.63 14.53zM339 682.25c-5.93 0-11.73-1.98-16.32-5.58L137.47 531.83c-6.02-4.71-9.47-11.67-9.47-19.11 0-7.44 3.45-14.41 9.47-19.11L322.7 348.77c6.94-5.51 16.59-7.13 25.13-4.21 8.73 2.99 15.15 10.27 16.74 19.01 1.61 8.84-1.93 17.84-9.24 23.47L194.59 512.72l160.78 125.72c8.53 6.67 11.7 17.8 7.87 27.69-3.73 9.64-13.46 16.12-24.21 16.12H339z',
|
||||
quote:
|
||||
'M328.25 527.53h120.17V896H128V617.21C128 312.66 234.77 149.55 448.42 128v138.19c-80.08 25.76-120.17 99.55-120.17 221.61v39.73z m447.62 0H896V896H575.58V617.21c0-304.55 106.74-467.66 320.32-489.21v138.19c-80.05 25.76-120.13 99.55-120.13 221.61v39.72h0.1z',
|
||||
bold:
|
||||
'M573.71 758.85h-192V594.28h192c45.53 0 82.29 36.76 82.29 82.29 0 45.53-36.76 82.28-82.29 82.28m-192-493.71h164.57c45.53 0 82.29 36.75 82.29 82.29s-36.75 82.29-82.29 82.29H381.71m307.21 70.76c53.21-37.3 90.51-98.19 90.51-153.05 0-123.98-96-219.43-219.43-219.43H217.14v768h386.19c115.2 0 203.52-93.26 203.52-207.91 0.01-83.38-47.17-154.7-117.93-187.61z',
|
||||
image:
|
||||
'M550.34 397.58h210.87L550.34 186.71v210.87M281.96 129.2h306.72l230.04 230.04v460.08c0 42.17-34.51 76.68-76.68 76.68H281.96c-42.56 0-76.68-34.51-76.68-76.68V205.88a76.406 76.406 0 0 1 22.38-54.3 76.406 76.406 0 0 1 54.3-22.38m0 690.12h460.08V512.6L588.68 665.96 512 589.28 281.96 819.32m76.68-421.74c-42.17 0-76.68 34.51-76.68 76.68s34.51 76.68 76.68 76.68 76.68-34.51 76.68-76.68-34.51-76.68-76.68-76.68z',
|
||||
attach:
|
||||
'M821.2 466.43L501.65 785.98c-63.92 63.92-168.48 63.92-232.4 0-63.91-63.91-63.91-168.48 0-232.4l334.07-334.07c40.67-40.67 104.58-40.67 145.25 0s40.67 104.58 0 145.25L414.5 698.83c-15.97 15.97-42.13 15.97-58.1 0-15.97-15.97-15.97-42.13 0-58.1l275.97-275.97-43.57-43.57-275.98 275.96c-40.66 40.67-40.67 104.58 0 145.25s104.58 40.67 145.25 0l334.07-334.07c63.91-63.91 63.92-168.48 0-232.4s-168.49-63.91-232.4 0L225.68 510c-88.6 88.6-88.6 230.95 0 319.55s230.95 88.6 319.55 0L864.78 510l-43.58-43.57z',
|
||||
link:
|
||||
'M338.96 895.99c-27.5 0-53.33-10.68-72.73-30.08L158.08 757.76c-19.4-19.4-30.08-45.24-30.08-72.74 0-27.5 10.68-53.33 30.08-72.71l135.97-135.97c12.84-12.84 33.74-12.84 46.58 0 6.22 6.22 9.65 14.5 9.65 23.3s-3.43 17.07-9.65 23.29L204.66 658.88c-14.41 14.41-14.41 37.87 0 52.29l108.15 108.15c14.41 14.42 37.87 14.42 52.31 0l216.29-216.3c6.94-6.94 10.75-16.22 10.75-26.14 0-9.92-3.82-19.21-10.75-26.15l-59.47-59.49c-6.22-6.22-9.65-14.49-9.65-23.29 0-8.8 3.43-17.07 9.65-23.29 12.84-12.84 33.74-12.84 46.58 0L628 504.14c19.38 19.36 30.06 45.19 30.06 72.73 0 27.54-10.67 53.37-30.06 72.73L411.71 865.9c-19.39 19.41-45.23 30.09-72.75 30.09zM478.8 588.96c-8.8 0-17.07-3.43-23.29-9.65l-59.49-59.48c-19.38-19.37-30.06-45.2-30.06-72.73s10.67-53.37 30.06-72.75l216.31-216.3c19.36-19.37 45.19-30.04 72.73-30.04 27.51 0 53.34 10.66 72.73 30.03L865.94 266.2c19.38 19.38 30.06 45.21 30.06 72.74 0 27.53-10.68 53.37-30.06 72.74L729.98 547.64c-6.22 6.22-14.49 9.65-23.29 9.65s-17.07-3.43-23.29-9.65c-6.22-6.22-9.65-14.49-9.65-23.29 0-8.8 3.43-17.07 9.65-23.29l135.96-135.97c6.93-6.95 10.76-16.23 10.76-26.15 0-9.92-3.82-19.21-10.76-26.15L711.2 204.64c-6.59-6.58-16.12-10.36-26.15-10.36h-0.02c-10.03 0-19.55 3.78-26.12 10.36L442.6 420.94c-6.94 6.94-10.76 16.23-10.76 26.15 0 9.92 3.82 19.2 10.76 26.14l59.49 59.49c12.84 12.85 12.84 33.74 0 46.58-6.22 6.23-14.49 9.66-23.29 9.66z',
|
||||
order:
|
||||
'M320 215v82h576v-82M320 553h576v-82H320m0 338h576v-82H320m-192 9h85.33v16h-42.67v32h42.67v16H128v32h128V704H128v32z m0-256h76.8L128 547.2V576h128v-32h-76.8l76.8-67.2V448H128m64-128h42V192h-84v32h42',
|
||||
italic:
|
||||
'M391.7 128c0.67 38.67 1.33 77.33 2 116h146c-58 169.14-115.99 338.28-173.99 507.42-67.14 0.17-134.28 0.33-201.42 0.5 0.06 47.64 0.11 95.28 0.17 142.92 176.19 0.39 352.39 0.77 528.58 1.16V751.42H510.29C571.1 581.95 631.9 412.47 692.7 243h167V128h-468z',
|
||||
through:
|
||||
'M896 514.38v69.72H742.75c34.91 74.61 33.16 278.89-217.48 278.89-290.79 1.74-279.62-226.6-279.62-226.6l138.59 1.74c1.05 117.48 110.31 117.48 131.96 116.09 22.34-1.74 105.77-1.39 112.41-82.97 2.79-38-35.61-66.93-77.5-87.15H128v-69.72h768M770.68 371.1l-138.94-1.04s5.93-96.57-114.5-96.92c-120.44-0.7-109.96 76.69-109.96 86.45 1.4 9.77 11.87 57.88 104.73 80.88H292.42S170.59 205.85 468.01 165.75c304.06-41.82 303.36 206.04 302.67 205.35z',
|
||||
|
||||
list:
|
||||
'M320 215v82h576v-82M320 553h576v-82H320m0 338h576v-82H320m-64-215c0 35.35-28.65 64-64 64s-64-28.65-64-64 28.65-64 64-64 64 28.65 64 64z m0 256c0 35.35-28.65 64-64 64s-64-28.65-64-64 28.65-64 64-64 64 28.65 64 64z m0-512c0 35.35-28.65 64-64 64s-64-28.65-64-64 28.65-64 64-64 64 28.65 64 64z',
|
||||
line:
|
||||
'M868.29 539.71H155.71c-15.28 0-27.71-12.43-27.71-27.71s12.43-27.71 27.71-27.71h712.58c15.28 0 27.71 12.43 27.71 27.71s-12.43 27.71-27.71 27.71z',
|
||||
preview:
|
||||
'M512 253.14c-174.51 0-322.97 106.97-384 258.86 61.03 151.89 209.49 258.86 384 258.86S834.97 663.89 896 512c-61.03-151.89-209.49-258.86-384-258.86z m0 431.49c-96 0-174.51-77.66-174.51-172.63S416 339.37 512 339.37 686.51 417.03 686.51 512 608 684.63 512 684.63z m0-276.17c-57.6 0-104.74 46.63-104.74 103.54S454.4 615.54 512 615.54 616.74 568.91 616.74 512 569.6 408.46 512 408.46z',
|
||||
table:
|
||||
'M860 159H164c-19.88 0-36 16.93-36 37.82v630.36c0 20.89 16.12 37.82 36 37.82h696c19.88 0 36-16.93 36-37.82V196.82c0-20.89-16.12-37.82-36-37.82z m-422 26.74c20.98 0 38 17.01 38 38 0 20.98-17.02 38-38 38-20.99 0-38-17.01-38-38s17.02-38 38-38z m-108 0c20.98 0 38 17.01 38 38 0 20.98-17.02 38-38 38-20.99 0-38-17.01-38-38s17.01-38 38-38z m-108 0c20.98 0 38 17.01 38 38 0 20.98-17.01 38-38 38s-38-17.01-38-38 17.01-38 38-38zM664 808V664h176v144H664z m-64-144v144H424V664h176z m-416 0h176v144H184V664z m480-190h176v144H664V474zM424 618V474h176v144H424z m-64 0H184V474h176v144z m480-338v144H664V280h176z m-240 0v144H424V280h176z m-385.54 0H360v144H184V280h30.46z',
|
||||
fullscreen:
|
||||
'M597.33 449.42l40.02 41.25 94.25-93.75 36.4 35V320H654.14l37.44 36.3-94.25 93.12z m-170.66 124.6l-40.41-40.68-93.53 93.93L256 592.76l0.95 111.24 113.96-0.9-37.78-35.78 93.54-93.3z m211.91-40.69l-41.25 40.02 93.75 94.24-35 36.4H768V590.14l-36.3 37.44-93.12-94.25z m-252.86-42.66l40.95-40.23-93.84-93.87L367.56 320l-111.56 0.5 0.48 113.91 36.02-37.62 93.22 93.88zM848 832H176c-26.47 0-48-19.57-48-43.64V235.64c0-24.06 21.53-43.64 48-43.64h672c26.47 0 48 19.58 48 43.64v552.73c0 24.06-21.53 43.63-48 43.63zM206.55 256c-8.02 0-14.55 5.74-14.55 12.8v486.4c0 7.06 6.53 12.8 14.55 12.8h610.91c8.02 0 14.55-5.74 14.55-12.8V268.8c0-7.06-6.53-12.8-14.55-12.8H206.55z'
|
||||
}
|
|
@ -1,372 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-19 21:17:26
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
import '../../layer/index'
|
||||
import 'css/meditor__attach.scss'
|
||||
|
||||
const $doc = Anot(document)
|
||||
const LANGUAGES = {
|
||||
zh: {
|
||||
IMAGE: {
|
||||
REMOTE: '远程图片',
|
||||
LOCAL: '本地上传',
|
||||
MANAGE: '图片管理',
|
||||
ALT: '图片描述',
|
||||
ADDRESS: '图片地址'
|
||||
},
|
||||
FILE: {
|
||||
REMOTE: '远程附件',
|
||||
LOCAL: '本地上传',
|
||||
MANAGE: '附件管理',
|
||||
ALT: '附件描述',
|
||||
ADDRESS: '附件地址'
|
||||
},
|
||||
BTN: '确定',
|
||||
INSERT: '插入',
|
||||
CHOOSE: '选择文件',
|
||||
LIMIT: '上传大小限制:单文件最大 ',
|
||||
SCREENSHOT: '截图',
|
||||
COMPRESS: '截图处理中...',
|
||||
TABLE: {
|
||||
NAME: '文件名',
|
||||
PROGRESS: '上传进度',
|
||||
HANDLE: '操作'
|
||||
},
|
||||
ERROR: {
|
||||
TYPE: '文件类型错误',
|
||||
SIZE: '文件体积过大',
|
||||
EMPTY: '描述和地址不能为空',
|
||||
UNDEFINED: '在node-webkit中saveAttach回调必须定义'
|
||||
}
|
||||
},
|
||||
en: {
|
||||
IMAGE: {
|
||||
REMOTE: 'Remote image',
|
||||
LOCAL: 'Local image',
|
||||
MANAGE: 'Manage',
|
||||
ALT: 'Image alt text',
|
||||
ADDRESS: 'Image address'
|
||||
},
|
||||
FILE: {
|
||||
REMOTE: 'Remote file',
|
||||
LOCAL: 'Local file',
|
||||
MANAGE: 'Manage',
|
||||
ALT: 'File alt text',
|
||||
ADDRESS: 'File address'
|
||||
},
|
||||
BTN: 'OK',
|
||||
INSERT: 'insert',
|
||||
CHOOSE: 'Choose file',
|
||||
LIMIT: 'Size of upload file limit to ',
|
||||
SCREENSHOT: 'screenshot',
|
||||
COMPRESS: 'Screenshot compressing...',
|
||||
TABLE: {
|
||||
NAME: 'name',
|
||||
PROGRESS: 'progress',
|
||||
HANDLE: 'handle'
|
||||
},
|
||||
ERROR: {
|
||||
TYPE: 'Forbidden type',
|
||||
SIZE: 'Too large',
|
||||
EMPTY: 'Alt text and address can not be null',
|
||||
UNDEFINED: 'Function saveAttach is not defined'
|
||||
}
|
||||
}
|
||||
}
|
||||
LANGUAGES['zh-CN'] = LANGUAGES.zh
|
||||
LANGUAGES['zh-TW'] = LANGUAGES.zh
|
||||
const lang =
|
||||
LANGUAGES[window.__ENV_LANG__ || navigator.language] || LANGUAGES.en
|
||||
|
||||
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">
|
||||
<dt class="tab-box" :drag="do-layer" data-limit="window">
|
||||
<span class="item" :class="active:tab === 1" :click="switchTab(1)">
|
||||
${lang[tool].REMOTE}
|
||||
</span>
|
||||
<span class="item" :class="active:tab === 2" :click="switchTab(2)">
|
||||
${lang[tool].LOCAL}
|
||||
</span>
|
||||
<span class="item" :class="active:tab === 3" :click="switchTab(3)">
|
||||
${lang[tool].MANAGE}
|
||||
</span>
|
||||
</dt>
|
||||
<dd class="cont-box">
|
||||
<div class="remote" :visible="tab === 1">
|
||||
<section class="section do-fn-cl">
|
||||
<input
|
||||
class="do-meditor__input"
|
||||
:duplex="attachAlt"
|
||||
placeholder="${lang[tool].ALT}" />
|
||||
</section>
|
||||
<section class="section do-fn-cl">
|
||||
<input
|
||||
class="do-meditor__input"
|
||||
:duplex="attach"
|
||||
placeholder="${lang[tool].ADDRESS}" />
|
||||
</section>
|
||||
<section class="section do-fn-cl">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="do-meditor__button submit"
|
||||
:click="confirm">${lang.BTN}</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">${lang.CHOOSE}</span>
|
||||
${limit ? `<span class="tips">(${lang.LIMIT + limit} MB)</span>` : ''}
|
||||
</div>
|
||||
<ul class="upload-box">
|
||||
<li class="thead">
|
||||
<span class="col">${lang.TABLE.NAME}</span>
|
||||
<span class="col">${lang.TABLE.PROGRESS}</span>
|
||||
<span class="col">${lang.TABLE.HANDLE}</span>
|
||||
</li>
|
||||
<li class="tbody">
|
||||
<p :for="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)">${
|
||||
lang.INSERT
|
||||
}</a></span>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="manager" :visible="tab === 3">
|
||||
<ul class="list-box">
|
||||
<li
|
||||
class="item"
|
||||
:for="attachList"
|
||||
:layer-tips="el.name"
|
||||
:click="insert(el)">
|
||||
|
||||
<span class="thumb" :html="el.thumb"></span>
|
||||
<p class="name" :text="el.name"></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</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%(' + lang.ERROR.TYPE + ')</span>',
|
||||
url: ''
|
||||
})
|
||||
continue
|
||||
}
|
||||
if (vm.props.uploadSizeLimit && it.size > vm.props.uploadSizeLimit) {
|
||||
this.uploadQueue.push({
|
||||
name: it.name,
|
||||
progress: '<span class="red">0%(' + lang.ERROR.SIZE + ')</span>',
|
||||
url: ''
|
||||
})
|
||||
continue
|
||||
}
|
||||
let fixName = new Date().format('YmdHis') + ext
|
||||
let attach = { name: it.name, fixName, progress: '100%', url: '' }
|
||||
|
||||
if (vm.props.saveAttach) {
|
||||
vm.props
|
||||
.saveAttach(attach, it)
|
||||
.then(url => {
|
||||
attach.url = url
|
||||
this.uploadQueue.push(attach)
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
} else {
|
||||
layer.toast(lang.ERROR.UNDEFINED, 'error')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadScreenshot(vm, blob) {
|
||||
let name = new Date().format('YmdHis') + '.jpg'
|
||||
let attach = { name, url: '' }
|
||||
|
||||
if (vm.props.saveAttach) {
|
||||
vm.props
|
||||
.saveAttach(attach, blob)
|
||||
.then(url => {
|
||||
vm.insert(`![${lang.SCREENSHOT}](${url})`)
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
} else {
|
||||
layer.toast(lang.ERROR.UNDEFINED, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function showDialog(elem, vm, tool) {
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
fixed: true,
|
||||
maskClose: true,
|
||||
offset: [offset.top + 35 - $doc.scrollTop()],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop()
|
||||
},
|
||||
tab: 2,
|
||||
attach: '',
|
||||
attachAlt: '',
|
||||
uploadQueue: [], //当前上传的列表
|
||||
attachList: [], //附件管理列表
|
||||
switchTab(id) {
|
||||
this.tab = id
|
||||
if (id === 3) {
|
||||
this.attachList.clear()
|
||||
if (vm.props.getAttachList) {
|
||||
vm.props
|
||||
.getAttachList(tool)
|
||||
.then(list => {
|
||||
list.forEach(it => {
|
||||
let ext = it.name.slice(it.name.lastIndexOf('.'))
|
||||
it.isImage = /^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext)
|
||||
it.thumb = it.isImage
|
||||
? `<img src="${it.url}" />`
|
||||
: `<em class="do-icon-txt"></em>`
|
||||
})
|
||||
return list
|
||||
})
|
||||
.then(list => {
|
||||
list = list.filter(it => {
|
||||
if (tool === 'IMAGE') {
|
||||
return it.isImage
|
||||
}
|
||||
return true
|
||||
})
|
||||
this.attachList = list
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
let val = `\n${tool === 'IMAGE' ? '!' : ''}[${it.name}](${it.url})`
|
||||
vm.insert(val)
|
||||
},
|
||||
confirm: function() {
|
||||
if (!this.attach || !this.attachAlt) {
|
||||
return layer.toast(lang.ERROR.EMPTY, 'error')
|
||||
}
|
||||
let val = `\n${tool === 'IMAGE' ? '!' : ''}[${this.attachAlt}](${
|
||||
this.attach
|
||||
})`
|
||||
|
||||
vm.insert(val)
|
||||
this.close()
|
||||
},
|
||||
content: fixCont(vm, tool)
|
||||
})
|
||||
}
|
||||
|
||||
const plugin = {
|
||||
__init__(ME) {
|
||||
Object.assign(ME.vm.addon, {
|
||||
attach(elem) {
|
||||
showDialog(elem, this, 'FILE')
|
||||
},
|
||||
image(elem) {
|
||||
showDialog(elem, this, 'IMAGE')
|
||||
}
|
||||
})
|
||||
|
||||
ME.vm.$refs.editor.addEventListener('paste', function(ev) {
|
||||
ev.preventDefault()
|
||||
let txt = ev.clipboardData.getData('text/plain')
|
||||
|
||||
//文本类型直接默认处理
|
||||
if (txt) {
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.clipboardData.items) {
|
||||
let items = ev.clipboardData.items
|
||||
let len = items.length
|
||||
let blob = null
|
||||
|
||||
for (let it of items) {
|
||||
if (it.type.indexOf('image') > -1) {
|
||||
blob = it.getAsFile()
|
||||
}
|
||||
}
|
||||
|
||||
if (blob !== null) {
|
||||
layer.toast(lang.COMPRESS)
|
||||
// 压缩截图,避免文件过大
|
||||
let reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
let img = document.createElement('img')
|
||||
let canvas = document.createElement('canvas')
|
||||
|
||||
img.onload = function() {
|
||||
canvas.width = img.width
|
||||
canvas.height = img.height
|
||||
|
||||
let ctx = canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
|
||||
|
||||
canvas.toBlob(
|
||||
obj => {
|
||||
uploadScreenshot(ME.vm, obj)
|
||||
},
|
||||
'image/jpeg',
|
||||
0.8
|
||||
)
|
||||
}
|
||||
img.src = this.result
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default plugin
|
|
@ -1,497 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-19 21:17:26
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
import '../../layer/index'
|
||||
import 'css/meditor__attach.scss'
|
||||
|
||||
const $doc = Anot(document)
|
||||
const LANGUAGES = {
|
||||
zh: {
|
||||
IMAGE: {
|
||||
REMOTE: '远程图片',
|
||||
LOCAL: '本地上传',
|
||||
MANAGE: '图片管理',
|
||||
ALT: '图片描述',
|
||||
ADDRESS: '图片地址'
|
||||
},
|
||||
FILE: {
|
||||
REMOTE: '远程附件',
|
||||
LOCAL: '本地上传',
|
||||
MANAGE: '附件管理',
|
||||
ALT: '附件描述',
|
||||
ADDRESS: '附件地址'
|
||||
},
|
||||
BTN: '确定',
|
||||
INSERT: '插入',
|
||||
CHOOSE: '选择文件',
|
||||
LIMIT: '上传大小限制:单文件最大 ',
|
||||
SCREENSHOT: '截图',
|
||||
COMPRESS: '截图处理中...',
|
||||
TABLE: {
|
||||
NAME: '文件名',
|
||||
PROGRESS: '上传进度',
|
||||
HANDLE: '操作'
|
||||
},
|
||||
ERROR: {
|
||||
TYPE: '文件类型错误',
|
||||
SIZE: '文件体积过大',
|
||||
EMPTY: '描述和地址不能为空'
|
||||
}
|
||||
},
|
||||
en: {
|
||||
IMAGE: {
|
||||
REMOTE: 'Remote image',
|
||||
LOCAL: 'Local image',
|
||||
MANAGE: 'Manage',
|
||||
ALT: 'Image alt text',
|
||||
ADDRESS: 'Image address'
|
||||
},
|
||||
FILE: {
|
||||
REMOTE: 'Remote file',
|
||||
LOCAL: 'Local file',
|
||||
MANAGE: 'Manage',
|
||||
ALT: 'File alt text',
|
||||
ADDRESS: 'File address'
|
||||
},
|
||||
BTN: 'OK',
|
||||
INSERT: 'insert',
|
||||
CHOOSE: 'Choose file',
|
||||
LIMIT: 'Size of upload file limit to ',
|
||||
SCREENSHOT: 'screenshot',
|
||||
COMPRESS: 'Screenshot compressing...',
|
||||
TABLE: {
|
||||
NAME: 'name',
|
||||
PROGRESS: 'progress',
|
||||
HANDLE: 'handle'
|
||||
},
|
||||
ERROR: {
|
||||
TYPE: 'Forbidden type',
|
||||
SIZE: 'Too large',
|
||||
EMPTY: 'Alt text and address can not be null'
|
||||
}
|
||||
}
|
||||
}
|
||||
LANGUAGES['zh-CN'] = LANGUAGES.zh
|
||||
LANGUAGES['zh-TW'] = LANGUAGES.zh
|
||||
const lang =
|
||||
LANGUAGES[window.__ENV_LANG__ || navigator.language] || LANGUAGES.en
|
||||
|
||||
class Uploader {
|
||||
constructor(url) {
|
||||
this.url = url
|
||||
this.xhr = new XMLHttpRequest()
|
||||
this.form = new FormData()
|
||||
}
|
||||
|
||||
field(key, val) {
|
||||
this.form.append(key, val)
|
||||
return 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)
|
||||
this.xhr.upload.addEventListener(
|
||||
'progress',
|
||||
evt => {
|
||||
if (evt.lengthComputable && this.progress) {
|
||||
let res = Math.round((evt.loaded * 100) / evt.total)
|
||||
this.progress(res)
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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">
|
||||
<dt class="tab-box" :drag="do-layer" data-limit="window">
|
||||
<span class="item" :class="active:tab === 1" :click="switchTab(1)">
|
||||
${lang[tool].REMOTE}
|
||||
</span>
|
||||
<span class="item" :class="active:tab === 2" :click="switchTab(2)">
|
||||
${lang[tool].LOCAL}
|
||||
</span>
|
||||
<span class="item" :class="active:tab === 3" :click="switchTab(3)">
|
||||
${lang[tool].MANAGE}
|
||||
</span>
|
||||
</dt>
|
||||
<dd class="cont-box">
|
||||
<div class="remote" :visible="tab === 1">
|
||||
<section class="section do-fn-cl">
|
||||
<input
|
||||
class="do-meditor__input"
|
||||
:duplex="attachAlt"
|
||||
placeholder="${lang[tool].ALT}" />
|
||||
</section>
|
||||
<section class="section do-fn-cl">
|
||||
<input
|
||||
class="do-meditor__input"
|
||||
:duplex="attach"
|
||||
placeholder="${lang[tool].ADDRESS}" />
|
||||
</section>
|
||||
<section class="section do-fn-cl">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="do-meditor__button submit"
|
||||
:click="confirm">${lang.BTN}</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">${lang.CHOOSE}</span>
|
||||
${limit ? `<span class="tips">(${lang.LIMIT + limit} MB)</span>` : ''}
|
||||
</div>
|
||||
<ul class="upload-box">
|
||||
<li class="thead">
|
||||
<span class="col">${lang.TABLE.NAME}</span>
|
||||
<span class="col">${lang.TABLE.PROGRESS}</span>
|
||||
<span class="col">${lang.TABLE.HANDLE}</span>
|
||||
</li>
|
||||
<li class="tbody">
|
||||
<p :for="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)">${
|
||||
lang.INSERT
|
||||
}</a></span>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="manager" :visible="tab === 3">
|
||||
<ul class="list-box">
|
||||
<li
|
||||
class="item"
|
||||
:for="attachList"
|
||||
:layer-tips="el.name"
|
||||
:click="insert(el)">
|
||||
|
||||
<span class="thumb" :html="el.thumb"></span>
|
||||
<p class="name" :text="el.name"></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</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%(' + lang.ERROR.TYPE + ')</span>',
|
||||
url: ''
|
||||
})
|
||||
continue
|
||||
}
|
||||
if (vm.props.uploadSizeLimit && it.size > vm.props.uploadSizeLimit) {
|
||||
this.uploadQueue.push({
|
||||
name: it.name,
|
||||
progress: '<span class="red">0%(' + lang.ERROR.SIZE + ')</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 => {
|
||||
if (vm.props.afterUpload) {
|
||||
return vm.props.afterUpload(res)
|
||||
} else {
|
||||
return res.data.url
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(url => {
|
||||
this.uploadQueue[idx].url = url
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
} else {
|
||||
upload
|
||||
.onProgress(val => {
|
||||
this.uploadQueue[idx].progress = val + '%'
|
||||
})
|
||||
.then(res => {
|
||||
if (vm.props.afterUpload) {
|
||||
return vm.props.afterUpload(res)
|
||||
} else {
|
||||
return res.data.url
|
||||
}
|
||||
})
|
||||
.then(url => {
|
||||
this.uploadQueue[idx].url = url
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(next => {
|
||||
if (!next) {
|
||||
return Promise.reject('something wrong with beforeUpload')
|
||||
}
|
||||
return upload.then(res => {
|
||||
if (vm.props.afterUpload) {
|
||||
return vm.props.afterUpload(res)
|
||||
} else {
|
||||
return res.data.url
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(url => {
|
||||
vm.insert(`![${lang.SCREENSHOT}](${url})`)
|
||||
})
|
||||
.catch(err => {
|
||||
Anot.error(err)
|
||||
})
|
||||
} else {
|
||||
upload
|
||||
.then(res => {
|
||||
if (vm.props.afterUpload) {
|
||||
return vm.props.afterUpload(res)
|
||||
} else {
|
||||
return res.data.url
|
||||
}
|
||||
})
|
||||
.then(url => {
|
||||
vm.insert(`![${lang.SCREENSHOT}](${url})`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function showDialog(elem, vm, tool) {
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
fixed: true,
|
||||
maskClose: true,
|
||||
offset: [offset.top + 35 - $doc.scrollTop()],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop()
|
||||
},
|
||||
tab: 2,
|
||||
attach: '',
|
||||
attachAlt: '',
|
||||
uploadQueue: [], //当前上传的列表
|
||||
attachList: [], //附件管理列表
|
||||
switchTab(id) {
|
||||
this.tab = id
|
||||
if (id === 3) {
|
||||
this.attachList.clear()
|
||||
if (vm.props.getAttachList) {
|
||||
vm.props
|
||||
.getAttachList(tool)
|
||||
.then(list => {
|
||||
list.forEach(it => {
|
||||
let ext = it.name.slice(it.name.lastIndexOf('.'))
|
||||
it.isImage = /^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext)
|
||||
it.thumb = it.isImage
|
||||
? `<img src="${it.url}" />`
|
||||
: `<em class="do-icon-txt"></em>`
|
||||
})
|
||||
return list
|
||||
})
|
||||
.then(list => {
|
||||
list = list.filter(it => {
|
||||
if (tool === 'IMAGE') {
|
||||
return it.isImage
|
||||
}
|
||||
return true
|
||||
})
|
||||
this.attachList = list
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
let val = `\n${tool === 'IMAGE' ? '!' : ''}[${it.name}](${it.url})`
|
||||
vm.insert(val)
|
||||
},
|
||||
confirm: function() {
|
||||
if (!this.attach || !this.attachAlt) {
|
||||
return layer.toast(lang.ERROR.EMPTY, 'error')
|
||||
}
|
||||
let val = `\n${tool === 'IMAGE' ? '!' : ''}[${this.attachAlt}](${
|
||||
this.attach
|
||||
})`
|
||||
|
||||
vm.insert(val)
|
||||
this.close()
|
||||
},
|
||||
content: fixCont(vm, tool)
|
||||
})
|
||||
}
|
||||
|
||||
const plugin = {
|
||||
__init__(ME) {
|
||||
Object.assign(ME.vm.addon, {
|
||||
attach(elem) {
|
||||
showDialog(elem, this, 'FILE')
|
||||
},
|
||||
image(elem) {
|
||||
showDialog(elem, this, 'IMAGE')
|
||||
}
|
||||
})
|
||||
|
||||
ME.vm.$refs.editor.addEventListener('paste', function(ev) {
|
||||
ev.preventDefault()
|
||||
let txt = ev.clipboardData.getData('text/plain')
|
||||
|
||||
//文本类型直接默认处理
|
||||
if (txt) {
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.clipboardData.items) {
|
||||
let items = ev.clipboardData.items
|
||||
let len = items.length
|
||||
let blob = null
|
||||
|
||||
for (let it of items) {
|
||||
if (it.type.indexOf('image') > -1) {
|
||||
blob = it.getAsFile()
|
||||
}
|
||||
}
|
||||
|
||||
if (blob !== null) {
|
||||
layer.toast(lang.COMPRESS)
|
||||
// 压缩截图,避免文件过大
|
||||
let reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
let img = document.createElement('img')
|
||||
let canvas = document.createElement('canvas')
|
||||
|
||||
img.onload = function() {
|
||||
canvas.width = img.width
|
||||
canvas.height = img.height
|
||||
|
||||
let ctx = canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
|
||||
|
||||
// chrome, Firefox, 以及支持toBlob 设置图片质量
|
||||
if (canvas.toBlob && (window.chrome || window.sidebar)) {
|
||||
canvas.toBlob(
|
||||
function(obj) {
|
||||
uploadScreenshot(ME.vm, obj)
|
||||
},
|
||||
'image/jpeg',
|
||||
0.8
|
||||
)
|
||||
} else {
|
||||
// IE和Safari的toBlob方法还不支持图片质量的设定
|
||||
// 需要先转base64再转回Blob
|
||||
let base64 = canvas.toDataURL('image/jpeg', 0.8)
|
||||
let buf = atob(base64.split(',')[1])
|
||||
let intArr = new Uint8Array(buf.length)
|
||||
let obj = null
|
||||
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
intArr[i] = buf.charCodeAt(i)
|
||||
}
|
||||
obj = new Blob([intArr], { type: 'image/jpeg' })
|
||||
|
||||
uploadScreenshot(ME.vm, obj)
|
||||
}
|
||||
}
|
||||
img.src = this.result
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default plugin
|
|
@ -1,531 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-17 21:41:48
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
import '../../layer/index'
|
||||
|
||||
function objArr(num) {
|
||||
let arr = []
|
||||
while (num > 0) {
|
||||
arr.push({ v: 0 })
|
||||
num--
|
||||
}
|
||||
return arr
|
||||
}
|
||||
function trim(str, sign) {
|
||||
return str.replace(new RegExp('^' + sign + '|' + sign + '$', 'g'), '')
|
||||
}
|
||||
|
||||
const $doc = Anot(document)
|
||||
const addon = {
|
||||
h1: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
let wrap = this.selection(true) || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
fixed: true,
|
||||
insert: function(level) {
|
||||
wrap = wrap.replace(/^(#{1,6} )?/, '#'.repeat(level) + ' ')
|
||||
that.insert(wrap, true)
|
||||
this.close()
|
||||
},
|
||||
offset: [
|
||||
offset.top + 35 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<ul class="do-meditor-h1 do-fn-noselect">
|
||||
<li :click="insert(1)" class="h1"><i class="do-meditor__icon icon-h1"></i>${
|
||||
Anot.ui.meditor.lang.HEADERS.H1
|
||||
}</li>
|
||||
<li :click="insert(2)" class="h2"><i class="do-meditor__icon icon-h2"></i>${
|
||||
Anot.ui.meditor.lang.HEADERS.H2
|
||||
}</li>
|
||||
<li :click="insert(3)" class="h3"><i class="do-meditor__icon icon-h3"></i>${
|
||||
Anot.ui.meditor.lang.HEADERS.H3
|
||||
}</li>
|
||||
<li :click="insert(4)" class="h4"><i class="do-meditor__icon icon-h4"></i>${
|
||||
Anot.ui.meditor.lang.HEADERS.H4
|
||||
}</li>
|
||||
<li :click="insert(5)" class="h5"><i class="do-meditor__icon icon-h5"></i>${
|
||||
Anot.ui.meditor.lang.HEADERS.H5
|
||||
}</li>
|
||||
<li :click="insert(6)" class="h6"><i class="do-meditor__icon icon-h6"></i>${
|
||||
Anot.ui.meditor.lang.HEADERS.H6
|
||||
}</li>
|
||||
</ul>`
|
||||
})
|
||||
},
|
||||
quote: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
wrap = '> ' + wrap
|
||||
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
bold: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
let wraped = trim(wrap, '\\*\\*')
|
||||
|
||||
wrap = wrap === wraped ? '**' + wrap + '**' : wraped
|
||||
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
italic: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
let wraped = trim(wrap, '_')
|
||||
|
||||
wrap = wrap === wraped ? '_' + wrap + '_' : wraped
|
||||
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
through: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
let wraped = trim(wrap, '~~')
|
||||
|
||||
wrap = wrap === wraped ? '~~' + wrap + '~~' : wraped
|
||||
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
unordered: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
wrap = '* ' + wrap
|
||||
|
||||
this.insert(wrap, false)
|
||||
},
|
||||
ordered: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
wrap = '1. ' + wrap
|
||||
|
||||
this.insert(wrap, false)
|
||||
},
|
||||
hr: function(elem) {
|
||||
this.insert('\n\n---\n\n', false)
|
||||
},
|
||||
link: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
let wrap = this.selection() || ''
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
fixed: true,
|
||||
link: '',
|
||||
linkName: wrap,
|
||||
linkTarget: 1,
|
||||
insert: function() {
|
||||
if (!this.link || !this.linkName) {
|
||||
return layer.toast(Anot.ui.meditor.lang.LINK.ERROR, 'error')
|
||||
}
|
||||
let val = `[${this.linkName}](${this.link} ${
|
||||
this.linkTarget === 1 ? ' "target=_blank"' : ''
|
||||
})`
|
||||
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
},
|
||||
offset: [
|
||||
offset.top + 35 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<div class="do-meditor-common">
|
||||
<section>
|
||||
<input class="do-meditor__input" :duplex="linkName" placeholder="${
|
||||
Anot.ui.meditor.lang.LINK.ALT
|
||||
}"/>
|
||||
</section>
|
||||
<section>
|
||||
<input class="do-meditor__input" :duplex="link" placeholder="${
|
||||
Anot.ui.meditor.lang.LINK.URL
|
||||
}"/>
|
||||
</section>
|
||||
<section>
|
||||
<label class="label">
|
||||
<input
|
||||
name="link"
|
||||
type="radio"
|
||||
class="radio"
|
||||
:duplex-number="linkTarget"
|
||||
value="1"/>
|
||||
${Anot.ui.meditor.lang.TARGET.BLANK}
|
||||
</label>
|
||||
<label class="label">
|
||||
<input
|
||||
name="link"
|
||||
type="radio"
|
||||
class="radio"
|
||||
:duplex-number="linkTarget"
|
||||
value="2"/>
|
||||
${Anot.ui.meditor.lang.TARGET.SELF}
|
||||
</label>
|
||||
</section>
|
||||
<section>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="do-meditor__button submit"
|
||||
:click="insert">${Anot.ui.meditor.lang.BTN.YES}</a>
|
||||
</section>
|
||||
</div>`
|
||||
})
|
||||
},
|
||||
time: function(elem) {
|
||||
this.insert(new Date().format(), false)
|
||||
},
|
||||
face: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
title: Anot.ui.meditor.lang.LAYER.FACE_TITLE,
|
||||
fixed: true,
|
||||
maskClose: true,
|
||||
arr: [
|
||||
'😀',
|
||||
'😅',
|
||||
'😂',
|
||||
'🤣',
|
||||
'😇',
|
||||
'😉',
|
||||
'😍',
|
||||
'😗',
|
||||
'😋',
|
||||
'😛',
|
||||
'😜',
|
||||
'🤨',
|
||||
'🧐',
|
||||
'🤓',
|
||||
'😎',
|
||||
'😞',
|
||||
'😔',
|
||||
'😭',
|
||||
'😤',
|
||||
'😡',
|
||||
'😱',
|
||||
'😰',
|
||||
'😓',
|
||||
'😬',
|
||||
'🙄',
|
||||
'😴',
|
||||
'😪',
|
||||
'🤮',
|
||||
'😷',
|
||||
'💩',
|
||||
'👻',
|
||||
'💀',
|
||||
'🤝',
|
||||
'👎',
|
||||
'👍',
|
||||
'🙏'
|
||||
],
|
||||
offset: [
|
||||
offset.top + 35 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<ul class="do-meditor-face">
|
||||
<li class="item" :for="arr">
|
||||
<span :html="el" :click="insert(el)"></span>
|
||||
</li>
|
||||
</ul>`,
|
||||
insert: function(val) {
|
||||
that.insert(val, false)
|
||||
}
|
||||
})
|
||||
},
|
||||
table: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
title: `0 ${Anot.ui.meditor.lang.TABLE.ROW} x 0 ${
|
||||
Anot.ui.meditor.lang.TABLE.COLUMN
|
||||
}`,
|
||||
fixed: true,
|
||||
maskClose: true,
|
||||
offset: [
|
||||
offset.top + 35 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
matrix: objArr(10).map(function() {
|
||||
return objArr(10)
|
||||
}),
|
||||
content: `
|
||||
<ul class="do-meditor-table" ref="table">
|
||||
<li :for="matrix">
|
||||
<span
|
||||
:for="o in el"
|
||||
:class="{active: o.v}"
|
||||
:data="{x: $index, y: $outer.$index}"></span>
|
||||
</li>
|
||||
</ul>`,
|
||||
success: function() {
|
||||
let tb = this.$refs.table
|
||||
let lastx, lasty
|
||||
let { lang } = Anot.ui.meditor
|
||||
|
||||
Anot(tb).bind('mousemove', ev => {
|
||||
if (ev.target.nodeName === 'SPAN') {
|
||||
let x = ev.target.dataset.x - 0
|
||||
let y = ev.target.dataset.y - 0
|
||||
if (x === lastx && y === lasty) {
|
||||
return
|
||||
}
|
||||
lastx = x
|
||||
lasty = y
|
||||
this.title = `${y + 1} ${lang.TABLE.ROW} x ${x + 1} ${
|
||||
lang.TABLE.COLUMN
|
||||
}`
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
for (let j = 0; j <= 9; j++) {
|
||||
this.matrix[i][j].v = i <= y && j <= x ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Anot(tb).bind('mouseleave', ev => {
|
||||
lastx = -1
|
||||
lasty = -1
|
||||
this.title = `0 ${lang.TABLE.ROW} x 0 ${lang.TABLE.COLUMN}`
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
for (let j = 0; j <= 9; j++) {
|
||||
this.matrix[i][j].v = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
Anot(tb).bind('click', ev => {
|
||||
if (ev.target.nodeName === 'SPAN') {
|
||||
let x = ev.target.dataset.x - 0 + 1
|
||||
let y = ev.target.dataset.y - 0 + 1
|
||||
|
||||
let thead = `\n\n${('| ' + lang.TABLE.THEAD + ' ').repeat(x)}|\n`
|
||||
let pipe = `${'| -- '.repeat(x)}|\n`
|
||||
let tbody = ('| '.repeat(x) + '|\n').repeat(y)
|
||||
|
||||
that.insert(thead + pipe + tbody, false)
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
image: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
let wrap = this.selection() || ''
|
||||
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
fixed: true,
|
||||
img: '',
|
||||
imgAlt: wrap,
|
||||
insert: function() {
|
||||
if (!this.img || !this.imgAlt) {
|
||||
return layer.toast(Anot.ui.meditor.lang.LINK.ERROR, 'error')
|
||||
}
|
||||
let val = `![${this.imgAlt}](${this.img})`
|
||||
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
},
|
||||
offset: [
|
||||
offset.top + 35 - $doc.scrollTop(),
|
||||
'auto',
|
||||
'auto',
|
||||
offset.left - $doc.scrollLeft()
|
||||
],
|
||||
shift: {
|
||||
top: offset.top - $doc.scrollTop(),
|
||||
left: offset.left - $doc.scrollLeft()
|
||||
},
|
||||
content: `
|
||||
<div class="do-meditor-common">
|
||||
<section>
|
||||
<input class="do-meditor__input" :duplex="imgAlt" placeholder="${
|
||||
Anot.ui.meditor.lang.IMAGE.ALT
|
||||
}"/>
|
||||
</section>
|
||||
<section>
|
||||
<input class="do-meditor__input" :duplex="img" placeholder="${
|
||||
Anot.ui.meditor.lang.IMAGE.URL
|
||||
}"/>
|
||||
</section>
|
||||
<section>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="do-meditor__button submit"
|
||||
:click="insert">${Anot.ui.meditor.lang.BTN.YES}</a>
|
||||
</section>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
},
|
||||
attach: function(elem) {
|
||||
this.addon.link.call(this, elem)
|
||||
},
|
||||
inlinecode: function(elem) {
|
||||
let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER
|
||||
let wraped = trim(wrap, '`')
|
||||
|
||||
wrap = wrap === wraped ? '`' + wrap + '`' : wraped
|
||||
this.insert(wrap, true)
|
||||
},
|
||||
blockcode: function(elem) {
|
||||
let that = this
|
||||
let offset = Anot(elem).offset()
|
||||
layer.open({
|
||||
type: 7,
|
||||
menubar: false,
|
||||
fixed: true,
|
||||
__lang__: [
|
||||
{ id: 'asp' },
|
||||
{ id: 'actionscript', name: 'ActionScript(3.0)/Flash/Flex' },
|
||||
{ id: 'bash', name: 'Bash/Shell/Bat' },
|
||||
{ id: 'css' },
|
||||
{ id: 'c', name: 'C' },
|
||||
{ id: 'cpp', name: 'C++' },
|
||||
{ id: 'csharp', name: 'C#' },
|
||||
{ id: 'coffeescript', name: 'CoffeeScript' },
|
||||
{ id: 'd', name: 'D' },
|
||||
{ id: 'dart' },
|
||||
{ id: 'delphi', name: 'Delphi/Pascal' },
|
||||
{ id: 'erlang' },
|
||||
{ id: 'go', name: 'Golang' },
|
||||
{ id: 'html' },
|
||||
{ id: 'java' },
|
||||
{ id: 'javascript' },
|
||||
{ id: 'json' },
|
||||
{ id: 'lua' },
|
||||
{ id: 'less' },
|
||||
{ id: 'markdown' },
|
||||
{ id: 'nginx' },
|
||||
{ id: 'objective-c' },
|
||||
{ id: 'php' },
|
||||
{ id: 'perl' },
|
||||
{ id: 'python' },
|
||||
{ id: 'r', name: 'R' },
|
||||
{ id: 'ruby' },
|
||||
{ id: 'sql' },
|
||||
{ id: 'sass', name: 'SASS/SCSS' },
|
||||
{ id: 'swift' },
|
||||
{ id: 'typescript' },
|
||||
{ id: 'xml' },
|
||||
{ id: 'yaml' },
|
||||
{ id: 'other', name: Anot.ui.meditor.lang.CODE.OTHER }
|
||||
],
|
||||
lang: 'javascript',
|
||||
code: '',
|
||||
maskClose: true,
|
||||
offset: [offset.top + 35 - $doc.scrollTop()],
|
||||
shift: { top: offset.top - $doc.scrollTop() },
|
||||
insert: function() {
|
||||
let val = `\n\`\`\`${this.lang}\n${this.code ||
|
||||
'// ' + Anot.ui.meditor.lang.PLACEHOLDER}\n\`\`\`\n`
|
||||
that.insert(val, false)
|
||||
this.close()
|
||||
},
|
||||
content: `
|
||||
<div class="do-meditor-codeblock">
|
||||
<section class="do-fn-cl">
|
||||
<div class="select">
|
||||
<select :duplex="lang">
|
||||
<option :for="__lang__" :attr-value="el.id">{{el.name || el.id}}</option>
|
||||
</select>
|
||||
<span class="trigon">
|
||||
<i class="do-icon-trigon-up"></i>
|
||||
<i class="do-icon-trigon-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<textarea class="do-meditor__input area" :duplex="code" placeholder="${
|
||||
Anot.ui.meditor.lang.PLACEHOLDER
|
||||
}"></textarea>
|
||||
</section>
|
||||
<section class="do-fn-cl">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="do-meditor__button submit"
|
||||
:click="insert">${Anot.ui.meditor.lang.BTN.YES}</a>
|
||||
</section>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
},
|
||||
preview: function() {
|
||||
this.preview = !this.preview
|
||||
if (this.preview) {
|
||||
this.htmlTxt = this.__tmp__
|
||||
}
|
||||
},
|
||||
fullscreen: function() {
|
||||
this.fullscreen = !this.fullscreen
|
||||
if (this.fullscreen) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
} else {
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
if (typeof this.props.onFullscreen === 'function') {
|
||||
this.props.onFullscreen(this.fullscreen)
|
||||
}
|
||||
},
|
||||
about: function(elem) {
|
||||
let offset = Anot(elem).offset()
|
||||
layer.open({
|
||||
type: 7,
|
||||
title: Anot.ui.meditor.lang.LAYER.ABOUT_TITLE,
|
||||
maskClose: true,
|
||||
offset: [offset.top + 35 - $doc.scrollTop()],
|
||||
shift: { top: offset.top - $doc.scrollTop() },
|
||||
content: `<div class="do-meditor-about">
|
||||
<pre>
|
||||
__ __ _____ _ _ _
|
||||
| \\/ | ____|__| (_) |_ ___ _ __
|
||||
| |\\/| | _| / _\` | | __/ _ \\| '__|
|
||||
| | | | |__| (_| | | || (_) | |
|
||||
|_| |_|_____\\__,_|_|\\__\\___/|_| v${Anot.ui.meditor.version}</pre>
|
||||
<p>${Anot.ui.meditor.lang.NAME}</p>
|
||||
<p><a target="_blank" href="https://doui.cc/product/meditor">https://doui.cc/product/meditor</a></p>
|
||||
<p>Copyright © 2017 Yutent, The MIT License.</p>
|
||||
</div>`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default addon
|
|
@ -1,616 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-17 16:37:12
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
import '../prism/base'
|
||||
import '../marked/index'
|
||||
import addon from './addon/base'
|
||||
import 'css/meditor.scss'
|
||||
|
||||
const LANGUAGES = {
|
||||
en: {
|
||||
NAME: 'Open Source Markdown Editor',
|
||||
TOOLBAR: {
|
||||
PIPE: '',
|
||||
H1: 'Header',
|
||||
QUOTE: 'Quote text',
|
||||
BOLD: 'Font-bold',
|
||||
ITALIC: 'Font-italic',
|
||||
THROUGH: 'Font-through',
|
||||
UNORDERED: 'Unordered list',
|
||||
ORDERED: 'Ordered list',
|
||||
LINK: 'Hyperlink',
|
||||
HR: 'Line',
|
||||
TIME: 'Insert current time',
|
||||
FACE: 'Face',
|
||||
TABLE: 'Insert table',
|
||||
IMAGE: 'Upload Pictures',
|
||||
FILE: 'Upload Files',
|
||||
INLINECODE: 'Inline code',
|
||||
BLOCKCODE: 'Block code',
|
||||
PREVIEW: 'Preview',
|
||||
FULLSCREEN: 'Fullscreen',
|
||||
ABOUT: 'About MEditor'
|
||||
},
|
||||
HEADERS: {
|
||||
H1: '#{1}',
|
||||
H2: '#{2}',
|
||||
H3: '#{3}',
|
||||
H4: '#{4}',
|
||||
H5: '#{5}',
|
||||
H6: '#{6}'
|
||||
},
|
||||
PLACEHOLDER: 'Type here',
|
||||
LINK: {
|
||||
ALT: 'Link text',
|
||||
URL: 'Link address',
|
||||
ERROR: 'Link address and text can not be null'
|
||||
},
|
||||
IMAGE: {
|
||||
ALT: 'Image alt text',
|
||||
URL: 'Image address'
|
||||
},
|
||||
TARGET: {
|
||||
BLANK: 'New window open',
|
||||
SELF: 'Current window open'
|
||||
},
|
||||
BTN: {
|
||||
YES: 'OK'
|
||||
},
|
||||
TABLE: {
|
||||
ROW: 'row',
|
||||
COLUMN: 'column',
|
||||
THEAD: 'thead'
|
||||
},
|
||||
LAYER: {
|
||||
FACE_TITLE: 'Insert Face',
|
||||
ABOUT_TITLE: 'About MEditor'
|
||||
},
|
||||
CODE: {
|
||||
OTHER: 'Other language'
|
||||
}
|
||||
},
|
||||
zh: {
|
||||
NAME: '开源在线Markdown编辑器',
|
||||
TOOLBAR: {
|
||||
PIPE: '',
|
||||
H1: '标题',
|
||||
QUOTE: '引用文本',
|
||||
BOLD: '粗体',
|
||||
ITALIC: '斜体',
|
||||
THROUGH: '删除线',
|
||||
UNORDERED: '无序列表',
|
||||
ORDERED: '有序列表',
|
||||
LINK: '超链接',
|
||||
HR: '横线',
|
||||
TIME: '插入当前时间',
|
||||
FACE: '表情',
|
||||
TABLE: '插入表格',
|
||||
IMAGE: '插入图片',
|
||||
FILE: '插入附件',
|
||||
INLINECODE: '行内代码',
|
||||
BLOCKCODE: '代码块',
|
||||
PREVIEW: '预览',
|
||||
FULLSCREEN: '全屏',
|
||||
ABOUT: '关于编辑器'
|
||||
},
|
||||
HEADERS: {
|
||||
H1: '一级标题',
|
||||
H2: '二级标题',
|
||||
H3: '三级标题',
|
||||
H4: '四级标题',
|
||||
H5: '五级标题',
|
||||
H6: '六级标题'
|
||||
},
|
||||
PLACEHOLDER: '在此输入文本',
|
||||
LINK: {
|
||||
ALT: '链接文字',
|
||||
URL: '链接地址',
|
||||
ERROR: '链接文字和地址不能为空'
|
||||
},
|
||||
IMAGE: {
|
||||
ALT: '图片描述',
|
||||
URL: '图片地址'
|
||||
},
|
||||
TARGET: {
|
||||
BLANK: '新窗口打开',
|
||||
SELF: '本窗口打开'
|
||||
},
|
||||
BTN: {
|
||||
YES: '确定'
|
||||
},
|
||||
TABLE: {
|
||||
ROW: '行',
|
||||
COLUMN: '列',
|
||||
THEAD: '表头'
|
||||
},
|
||||
LAYER: {
|
||||
FACE_TITLE: '插入表情',
|
||||
ABOUT_TITLE: '关于编辑器'
|
||||
},
|
||||
CODE: {
|
||||
OTHER: '其他语言'
|
||||
}
|
||||
}
|
||||
}
|
||||
LANGUAGES['zh-CN'] = LANGUAGES.zh
|
||||
LANGUAGES['zh-TW'] = LANGUAGES.zh
|
||||
const lang =
|
||||
LANGUAGES[window.__ENV_LANG__ || navigator.language] || LANGUAGES.en
|
||||
|
||||
marked.setOptions({
|
||||
highlight: function(code, lang) {
|
||||
return Prism.highlight(code, Prism.languages[lang])
|
||||
}
|
||||
})
|
||||
if (!String.prototype.repeat) {
|
||||
String.prototype.repeat = function(num) {
|
||||
let result = ''
|
||||
while (num > 0) {
|
||||
result += this
|
||||
num--
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Anot.ui.meditor = { version: '1.0.0', author: 'yutent', lang }
|
||||
const log = console.log
|
||||
|
||||
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) {
|
||||
let href = attr.match(attrExp('href'))
|
||||
let title = attr.match(attrExp('title'))
|
||||
let tar = attr.match(attrExp('target'))
|
||||
let attrs = ''
|
||||
|
||||
href = (href && href[1]) || null
|
||||
title = (title && title[1]) || null
|
||||
tar = (tar && tar[1]) || '_self'
|
||||
|
||||
if (!href) {
|
||||
return inner || href
|
||||
}
|
||||
|
||||
href = href.replace('viod(0)', '')
|
||||
attrs = `target=${tar}`
|
||||
attrs += title ? `;title=${title}` : ''
|
||||
|
||||
return `[${inner || href}](${href} "${attrs}")`
|
||||
},
|
||||
em: function(str, attr, inner) {
|
||||
return (inner && '_' + inner + '_') || ''
|
||||
},
|
||||
strong: function(str, attr, inner) {
|
||||
return (inner && '**' + inner + '**') || ''
|
||||
},
|
||||
pre: function(str, attr, inner) {
|
||||
inner = inner.replace(/<[/]?code>/g, '')
|
||||
return '\n\n```\n' + inner + '\n```\n'
|
||||
},
|
||||
code: function(str, attr, inner) {
|
||||
return (inner && '`' + inner + '`') || ''
|
||||
},
|
||||
blockquote: function(str, attr, inner) {
|
||||
return '> ' + inner.trim()
|
||||
},
|
||||
img: function(str, attr, inner) {
|
||||
var src = attr.match(attrExp('src')),
|
||||
alt = attr.match(attrExp('alt'))
|
||||
|
||||
src = (src && src[1]) || ''
|
||||
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, flag = 'i') {
|
||||
return new RegExp(field + '\\s?=\\s?["\']?([^"\']*)["\']?', flag)
|
||||
}
|
||||
function tagExp(tag, open) {
|
||||
var exp = ''
|
||||
if (['br', 'hr', 'img'].indexOf(tag) > -1) {
|
||||
exp = '<' + tag + '([^>]*?)\\/?>'
|
||||
} else {
|
||||
exp = '<' + tag + '([^>]*?)>([\\s\\S]*?)<\\/' + tag + '>'
|
||||
}
|
||||
return new RegExp(exp, 'gi')
|
||||
}
|
||||
function html2md(str) {
|
||||
try {
|
||||
str = decodeURIComponent(str)
|
||||
} catch (err) {}
|
||||
|
||||
str = str
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/<meta [^>]*>/, '')
|
||||
.replace(attrExp('class', 'g'), '')
|
||||
.replace(attrExp('style', 'g'), '')
|
||||
.replace(/<(?!a |img )(\w+) [^>]*>/g, '<$1>')
|
||||
.replace(/<svg[^>]*>.*?<\/svg>/g, '{invalid image}')
|
||||
|
||||
// log(str)
|
||||
for (let i in ELEMS) {
|
||||
let cb = ELEMS[i]
|
||||
let exp = tagExp(i)
|
||||
|
||||
if (i === 'blockquote') {
|
||||
while (str.match(exp)) {
|
||||
str = str.replace(exp, cb)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
if (i === 'strong') {
|
||||
exp = tagExp('b')
|
||||
str = str.replace(exp, cb)
|
||||
}
|
||||
}
|
||||
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(
|
||||
m,
|
||||
t,
|
||||
inner
|
||||
) {
|
||||
let li = inner.split('</li>')
|
||||
li.pop()
|
||||
|
||||
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) {
|
||||
n = n.trim().replace(/\n/g, '\n ')
|
||||
return n
|
||||
})
|
||||
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
||||
}
|
||||
return li.join('\n')
|
||||
})
|
||||
return '\n' + match.trim()
|
||||
})
|
||||
}
|
||||
str = str
|
||||
|
||||
.replace(/<[\/]?[\w]*[^>]*>/g, '')
|
||||
.replace(/```([\w\W]*)```/g, function(str, inner) {
|
||||
inner = inner
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
return '```' + inner + '```'
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
function tool(name) {
|
||||
name = (name + '').trim().toLowerCase()
|
||||
name = '|' === name ? 'pipe' : name
|
||||
|
||||
let title = lang.TOOLBAR[name.toUpperCase()]
|
||||
let extra = ''
|
||||
switch (name) {
|
||||
case 'preview':
|
||||
extra = ':class="{active: preview}"'
|
||||
break
|
||||
case 'fullscreen':
|
||||
extra = ':class="{active: fullscreen}"'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return `
|
||||
<span title="${title}" class="do-meditor__icon icon-${name}" data-name="${name}" ${extra}></span>`
|
||||
}
|
||||
|
||||
class MEObject {
|
||||
constructor(vm) {
|
||||
this.vm = vm
|
||||
this.id = vm.$id
|
||||
}
|
||||
|
||||
getVal() {
|
||||
return this.vm.value.trim()
|
||||
}
|
||||
getHtml() {
|
||||
return this.vm.__tmp__
|
||||
}
|
||||
setVal(txt) {
|
||||
this.vm.value = txt || ''
|
||||
}
|
||||
}
|
||||
|
||||
Anot.component('meditor', {
|
||||
__init__: function(props, state, next) {
|
||||
this.classList.add('do-meditor')
|
||||
|
||||
this.setAttribute(':css', '{height: height}')
|
||||
this.setAttribute(
|
||||
':class',
|
||||
'{fullscreen: fullscreen, preview: preview, disabled: disabled}'
|
||||
)
|
||||
|
||||
if (props.hasOwnProperty('disabled')) {
|
||||
state.disabled = true
|
||||
delete props.disabled
|
||||
}
|
||||
if (props.height) {
|
||||
if (
|
||||
(isFinite(props.height) && props.height > 180) ||
|
||||
/%$/.test(props.height)
|
||||
) {
|
||||
state.height = props.height
|
||||
}
|
||||
delete props.height
|
||||
}
|
||||
next()
|
||||
},
|
||||
render: function() {
|
||||
let toolbar = (this.props.toolbar || DEFAULT_TOOLBAR)
|
||||
.map(it => tool(it))
|
||||
.join('')
|
||||
|
||||
delete this.props.toolbar
|
||||
|
||||
return `
|
||||
<div class="tool-bar do-fn-noselect" :click="onToolClick">${toolbar}</div>
|
||||
<textarea
|
||||
ref="editor"
|
||||
class="editor-body"
|
||||
spellcheck="false"
|
||||
:attr="{disabled: disabled}"
|
||||
:css="{'padding-bottom': padding}"
|
||||
:duplex="value"></textarea>
|
||||
<content
|
||||
ref="preview"
|
||||
class="md-preview do-marked-theme"
|
||||
:css="{'padding-bottom': padding}"
|
||||
:visible="preview"
|
||||
:html="htmlTxt"></content>
|
||||
`
|
||||
},
|
||||
componentDidMount: function(vm, elem) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$editor.bind('paste', ev => {
|
||||
ev.preventDefault()
|
||||
let txt = ev.clipboardData.getData('text/plain').trim()
|
||||
let html = ev.clipboardData.getData('text/html').trim()
|
||||
|
||||
html = html2md(html)
|
||||
|
||||
if (html) {
|
||||
this.insert(html)
|
||||
} else if (txt) {
|
||||
this.insert(txt)
|
||||
}
|
||||
this.value = this.$refs.editor.value
|
||||
})
|
||||
|
||||
$editor.bind('scroll', ev => {
|
||||
let st = ev.target.scrollTop
|
||||
let sh = ev.target.scrollHeight
|
||||
let ch = ev.target.clientHeight
|
||||
let psh = preview.scrollHeight
|
||||
let syncTop = (st / (sh - ch)) * (psh - ch)
|
||||
preview.scrollTop = syncTop
|
||||
})
|
||||
//编辑器成功加载的回调
|
||||
if (typeof this.props.created === 'function') {
|
||||
this.props.created(new MEObject(this))
|
||||
}
|
||||
this.padding = (this.$elem.clientHeight / 2) >>> 0
|
||||
this.compile()
|
||||
if (this.preview) {
|
||||
this.htmlTxt = this.__tmp__
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function(val) {
|
||||
this.compile()
|
||||
//只有开启实时预览,才会赋值给htmlTxt
|
||||
if (this.preview) {
|
||||
this.htmlTxt = this.__tmp__
|
||||
}
|
||||
if (typeof this.props.onUpdate === 'function') {
|
||||
this.props.onUpdate(this.value, this.__tmp__)
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
padding: 90,
|
||||
height: 180,
|
||||
disabled: false, //禁用编辑器
|
||||
fullscreen: false, //是否全屏
|
||||
preview: window.innerWidth > 768, //是否显示预览
|
||||
htmlTxt: '', //用于预览渲染
|
||||
value: '', //纯md文本
|
||||
addon // 已有插件
|
||||
},
|
||||
props: {
|
||||
safeMode: true,
|
||||
revise: Anot.PropsTypes.isFunction(),
|
||||
created: 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.value = 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(ev) {
|
||||
if (ev.target.tagName.toLowerCase() !== 'span') {
|
||||
return
|
||||
}
|
||||
let name = ev.target.dataset.name
|
||||
if (this.disabled || name === 'pipe') {
|
||||
return
|
||||
}
|
||||
if (this.addon[name]) {
|
||||
this.addon[name].call(this, ev.target)
|
||||
} else {
|
||||
console.log('%c没有对应的插件%c[%s]', 'color:#f00;', '', name)
|
||||
}
|
||||
},
|
||||
|
||||
compile: function() {
|
||||
let txt = this.value.trim()
|
||||
|
||||
if (this.props.safeMode) {
|
||||
txt = txt
|
||||
.replace(/<script([^>]*?)>/g, '<script$1>')
|
||||
.replace(/<\/script>/g, '</script>')
|
||||
}
|
||||
//只解析,不渲染
|
||||
this.__tmp__ = marked(txt)
|
||||
if (typeof this.props.revise === 'function') {
|
||||
this.__tmp__ = this.props.revise(this.__tmp__)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -49,12 +49,12 @@ table {
|
|||
background: #fff;
|
||||
}
|
||||
thead tr {
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
}
|
||||
th {
|
||||
font-weight: bold;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
:host {
|
||||
display: block;
|
||||
line-height: 1;
|
||||
color: nth($cd, 1);
|
||||
color: var(--color-dark-1);
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
|
@ -29,7 +29,7 @@
|
|||
height: 32px;
|
||||
padding: 0 8px;
|
||||
margin: 0 3px;
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
|
@ -37,11 +37,11 @@
|
|||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
background: nth($cp, 2);
|
||||
background: var(--color-plain-2);
|
||||
}
|
||||
|
||||
&[curr] {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
@ -59,31 +59,31 @@
|
|||
}
|
||||
|
||||
:host([color='red']) button[curr] {
|
||||
background: nth($cr, 1);
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
|
||||
:host([color='blue']) button[curr] {
|
||||
background: nth($cb, 1);
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
|
||||
:host([color='green']) button[curr] {
|
||||
background: nth($cg, 1);
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
|
||||
:host([color='teal']) button[curr] {
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
|
||||
:host([color='orange']) button[curr] {
|
||||
background: nth($co, 1);
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
|
||||
:host([color='dark']) button[curr] {
|
||||
background: nth($cd, 1);
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
|
||||
:host([color='purple']) button[curr] {
|
||||
background: nth($cpp, 1);
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
min-width: 105px;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
color: nth($cd, 2);
|
||||
color: var(--color-dark-2);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@
|
|||
width: 100%;
|
||||
height: 32px;
|
||||
font-size: 13px;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
border-radius: inherit;
|
||||
white-space: nowrap;
|
||||
background: #fff;
|
||||
|
@ -60,9 +60,9 @@
|
|||
height: 30px;
|
||||
padding: 0 10px;
|
||||
font-size: 14px;
|
||||
border-right: 1px solid nth($cp, 3);
|
||||
border-right: 1px solid var(--color-plain-3);
|
||||
border-radius: 2px 0 0 2px;
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
}
|
||||
|
||||
span {
|
||||
|
@ -70,7 +70,7 @@
|
|||
padding: 0 5px;
|
||||
|
||||
&::placeholder {
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@
|
|||
wc-icon {
|
||||
--size: 14px;
|
||||
margin: 0 3px;
|
||||
color: nth($cgr, 1);
|
||||
color: var(--color-grey-1);
|
||||
cursor: pointer;
|
||||
}
|
||||
span {
|
||||
|
@ -160,7 +160,7 @@
|
|||
}
|
||||
|
||||
&[weekend] {
|
||||
color: nth($ct, 1);
|
||||
color: var(--color-teal-1);
|
||||
}
|
||||
&[picked] {
|
||||
color: #fff;
|
||||
|
@ -175,14 +175,14 @@
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: nth($ct, 1);
|
||||
background: var(--color-teal-1);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
&[disabled] {
|
||||
border-color: transparent;
|
||||
font-size: 12px;
|
||||
color: nth($cp, 3);
|
||||
color: var(--color-plain-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,8 +197,8 @@
|
|||
height: 30px;
|
||||
line-height: 28px;
|
||||
background: #fffbed;
|
||||
color: nth($co, 2);
|
||||
border: 1px solid nth($co, 1);
|
||||
color: var(--color-orange-2);
|
||||
border: 1px solid var(--color-orange-1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,446 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2018-03-25 23:59:13
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
import Format from './lib/format'
|
||||
|
||||
// 本地协议/头 判断正则
|
||||
const rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
|
||||
const log = console.log
|
||||
|
||||
const noop = function(e, res) {
|
||||
this.defer.resolve(res)
|
||||
}
|
||||
|
||||
let isLocal = false
|
||||
try {
|
||||
isLocal = rlocalProtocol.test(location.protocol)
|
||||
} catch (e) {}
|
||||
|
||||
let originAnchor = document.createElement('a')
|
||||
originAnchor.href = location.href
|
||||
|
||||
const NOBODY_METHODS = ['GET', 'HEAD']
|
||||
const ERRORS = {
|
||||
10001: 'Argument url is required',
|
||||
10012: 'Parse error',
|
||||
10100: 'Request canceled',
|
||||
10104: 'Request pending...',
|
||||
10200: 'Ok',
|
||||
10204: 'No content',
|
||||
10304: 'Not modified',
|
||||
10500: 'Internal Server Error',
|
||||
10504: 'Connected timeout'
|
||||
}
|
||||
|
||||
const FORM_TYPES = {
|
||||
form: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
json: 'application/json; charset=UTF-8',
|
||||
text: 'text/plain; charset=UTF-8'
|
||||
}
|
||||
|
||||
const convert = {
|
||||
text(val) {
|
||||
return val
|
||||
},
|
||||
xml(val, xml) {
|
||||
return xml !== undefined ? xml : Format.parseXML(val)
|
||||
},
|
||||
html(val) {
|
||||
return Format.parseHTML(val)
|
||||
},
|
||||
json(val) {
|
||||
return JSON.parse(val)
|
||||
},
|
||||
script(val) {
|
||||
return Format.parseJS(val)
|
||||
}
|
||||
}
|
||||
|
||||
class _Request {
|
||||
constructor(url = '', method = 'GET', param = {}) {
|
||||
if (!url) {
|
||||
throw new Error(ERRORS[10001])
|
||||
}
|
||||
|
||||
// url规范化
|
||||
url = url.replace(/#.*$/, '')
|
||||
|
||||
if (request.BASE_URL) {
|
||||
if (!/^([a-z]+:|\/\/)/.test(url)) {
|
||||
url = request.BASE_URL + url
|
||||
}
|
||||
}
|
||||
|
||||
method = method.toUpperCase()
|
||||
|
||||
this.xhr = new XMLHttpRequest()
|
||||
this.defer = Promise.defer()
|
||||
this.opt = {
|
||||
url,
|
||||
method,
|
||||
headers: {},
|
||||
data: {},
|
||||
dataType: 'text',
|
||||
withCredentials: false // 跨域选项,是否验证凭证
|
||||
}
|
||||
|
||||
// 取消网络请求
|
||||
this.defer.promise.abort = () => {
|
||||
this.cancel = true
|
||||
this.xhr.abort()
|
||||
}
|
||||
this.__next__(Object.assign({}, request.__INIT__, param))
|
||||
return this.defer.promise
|
||||
}
|
||||
|
||||
__next__(param) {
|
||||
/* -------------------------------------------------------------- */
|
||||
/* ------------------------ 1»» 配置头信息 ---------------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
if (param.headers) {
|
||||
Object.assign(this.opt.headers, param.headers)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* --------- 2»» 设置表单类型, 其中 form-data不能手动设置 ---------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
let hasAttach = false
|
||||
if (param.formType) {
|
||||
switch (param.formType) {
|
||||
case 'form':
|
||||
this.__set__('form')
|
||||
break
|
||||
case 'json':
|
||||
this.__set__('json')
|
||||
break
|
||||
case 'form-data':
|
||||
this.opt.method = 'POST'
|
||||
hasAttach = true
|
||||
break
|
||||
default:
|
||||
if (NOBODY_METHODS.includes(this.opt.method)) {
|
||||
this.__set__('form')
|
||||
} else {
|
||||
this.__set__('text')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.__set__('form')
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* ------------------- 3»» 设置缓存 ---------------------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
if (param.cache) {
|
||||
if (NOBODY_METHODS.includes(this.opt.method)) {
|
||||
this.opt.cache = true
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* ------------------- 4»» 设置超时时间(毫秒) --------------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
param.timeout = param.timeout >>> 0
|
||||
if (param.timeout > 0) {
|
||||
this.opt.timeout = param.timeout
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* -------------------------- 5»» 请求的内容 --------------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
if (param.data) {
|
||||
let type = typeof param.data
|
||||
|
||||
switch (type) {
|
||||
case 'number':
|
||||
case 'string':
|
||||
this.__set__('text')
|
||||
this.opt.data = param.data
|
||||
break
|
||||
case 'object':
|
||||
// 解析表单DOM
|
||||
if (param.data.nodeName === 'FORM') {
|
||||
this.opt.method = param.data.method.toUpperCase() || 'POST'
|
||||
|
||||
this.opt.data = Format.parseForm(param.data)
|
||||
hasAttach = this.opt.data.constructor === FormData
|
||||
|
||||
if (hasAttach) {
|
||||
delete this.opt.headers['content-type']
|
||||
}
|
||||
// 如果是一个 FormData对象
|
||||
// 则直接改为POST
|
||||
} else if (param.data.constructor === FormData) {
|
||||
hasAttach = true
|
||||
this.opt.method = 'POST'
|
||||
delete this.opt.headers['content-type']
|
||||
this.opt.data = param.data
|
||||
} else {
|
||||
// 有附件,则改为FormData
|
||||
if (hasAttach) {
|
||||
this.opt.data = Format.mkFormData(param.data)
|
||||
} else {
|
||||
this.opt.data = param.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* -------------------------- 6»» 处理跨域 --------------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
if (param.withCredentials) {
|
||||
this.opt.withCredentials = true
|
||||
}
|
||||
try {
|
||||
let anchor = document.createElement('a')
|
||||
anchor.href = this.opt.url
|
||||
|
||||
this.opt.crossDomain =
|
||||
originAnchor.protocol !== anchor.protocol ||
|
||||
originAnchor.host !== anchor.host
|
||||
} catch (err) {}
|
||||
|
||||
// 6.1»» 进一步处理跨域
|
||||
// 非跨域或跨域但支持Cors时自动加上一条header信息,用以标识这是ajax请求
|
||||
// 如果是跨域,开启Cors会需要服务端额外返回一些headers
|
||||
|
||||
if (this.opt.crossDomain) {
|
||||
if (this.opt.withCredentials) {
|
||||
this.xhr.withCredentials = true
|
||||
this.opt.headers['X-Requested-With'] = 'XMLHttpRequest'
|
||||
}
|
||||
} else {
|
||||
this.opt.headers['X-Requested-With'] = 'XMLHttpRequest'
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* ------------- 7»» 根据method类型, 处理g表单数据 ---------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
// 是否允许发送body
|
||||
let allowBody = !NOBODY_METHODS.includes(this.opt.method)
|
||||
if (allowBody) {
|
||||
if (!hasAttach) {
|
||||
if (param.formType === 'json') {
|
||||
this.opt.data = JSON.stringify(this.opt.data)
|
||||
} else {
|
||||
this.opt.data = Format.param(this.opt.data)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 否则拼接到url上
|
||||
this.opt.data = Format.param(this.opt.data)
|
||||
|
||||
if (this.opt.data) {
|
||||
this.opt.url += (/\?/.test(this.opt.url) ? '&' : '?') + this.opt.data
|
||||
}
|
||||
|
||||
if (this.opt.cache === false) {
|
||||
this.opt.url +=
|
||||
(/\?/.test(this.opt.url) ? '&' : '?') + '_=' + Math.random()
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* ------------- 8»» 设置响应的数据类型 ---------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
// arraybuffer | blob | document | json | text
|
||||
if (param.dataType) {
|
||||
this.opt.dataType = param.dataType.toLowerCase()
|
||||
}
|
||||
this.xhr.responseType = this.opt.dataType
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* ------------- 9»» 构造请求 ---------------- */
|
||||
/* -------------------------------------------------------------- */
|
||||
|
||||
// response ready
|
||||
this.xhr.onreadystatechange = ev => {
|
||||
if (this.opt.timeout > 0) {
|
||||
this.opt['time' + this.xhr.readyState] = ev.timeStamp
|
||||
if (this.xhr.readyState === 4) {
|
||||
this.opt.isTimeout =
|
||||
this.opt.time4 - this.opt.time1 > this.opt.timeout
|
||||
}
|
||||
}
|
||||
|
||||
if (this.xhr.readyState !== 4) {
|
||||
return
|
||||
}
|
||||
|
||||
this.__dispatch__(this.opt.isTimeout)
|
||||
}
|
||||
|
||||
// 9.1»» 初始化xhr
|
||||
this.xhr.open(this.opt.method, this.opt.url, true)
|
||||
|
||||
// 9.2»» 设置头信息
|
||||
for (let i in this.opt.headers) {
|
||||
this.xhr.setRequestHeader(i, this.opt.headers[i])
|
||||
}
|
||||
|
||||
// 9.3»» 发起网络请求
|
||||
this.xhr.send(this.opt.data)
|
||||
|
||||
// 9.4»» 超时处理
|
||||
if (this.opt.timeout && this.opt.timeout > 0) {
|
||||
this.xhr.timeout = this.opt.timeout
|
||||
}
|
||||
}
|
||||
|
||||
__set__(type) {
|
||||
this.opt.headers['content-type'] = FORM_TYPES[type]
|
||||
}
|
||||
|
||||
__dispatch__(isTimeout) {
|
||||
let result = {
|
||||
status: 200,
|
||||
statusText: 'ok',
|
||||
text: '',
|
||||
body: '',
|
||||
error: null
|
||||
}
|
||||
|
||||
// 主动取消
|
||||
if (this.cancel) {
|
||||
return this.__cancel__(result)
|
||||
}
|
||||
|
||||
// 超时
|
||||
if (isTimeout) {
|
||||
return this.__timeout__(result)
|
||||
}
|
||||
|
||||
// 是否请求成功(resful规范)
|
||||
let isSucc = this.xhr.status >= 200 && this.xhr.status < 400
|
||||
|
||||
let headers = this.xhr.getAllResponseHeaders().split('\n') || []
|
||||
let contentType = ''
|
||||
|
||||
//处理返回的 Header, 拿到content-type
|
||||
for (let it of headers) {
|
||||
it = it.trim()
|
||||
if (it) {
|
||||
it = it.split(':')
|
||||
let tmp = it.shift().toLowerCase()
|
||||
if (tmp === 'content-type') {
|
||||
contentType = it
|
||||
.join(':')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSucc) {
|
||||
result.status = this.xhr.status
|
||||
if (result.status === 204) {
|
||||
result.statusText = ERRORS[10204]
|
||||
} else if (result.status === 304) {
|
||||
result.statusText = ERRORS[10304]
|
||||
}
|
||||
} else {
|
||||
result.status = this.xhr.status || 500
|
||||
result.statusText = this.xhr.statusText || ERRORS[10500]
|
||||
result.error = new Error(result.statusText)
|
||||
}
|
||||
// log(this.opt.dataType, this.xhr)
|
||||
switch (this.opt.dataType) {
|
||||
case 'arraybuffer':
|
||||
case 'blob':
|
||||
case 'document':
|
||||
case 'json':
|
||||
result.text = result.body = this.xhr.response
|
||||
break
|
||||
// text
|
||||
default:
|
||||
try {
|
||||
//处理返回的数据
|
||||
let dataType = contentType.match(/json|xml|script|html/)
|
||||
|
||||
dataType = (dataType && dataType[0].toLowerCase()) || 'text'
|
||||
|
||||
result.text = this.xhr.response
|
||||
result.body = convert[dataType](result.text, this.xhr.response)
|
||||
} catch (err) {
|
||||
result.error = err
|
||||
result.statusText = ERRORS[10012]
|
||||
}
|
||||
break
|
||||
}
|
||||
this.__success__(isSucc, result)
|
||||
}
|
||||
|
||||
__success__(isSucc, result) {
|
||||
if (isSucc) {
|
||||
this.defer.resolve(result)
|
||||
} else {
|
||||
this.defer.reject(result)
|
||||
}
|
||||
delete this.xhr
|
||||
delete this.opt
|
||||
delete this.defer
|
||||
}
|
||||
|
||||
__cancel__(result) {
|
||||
result.status = 0
|
||||
result.statusText = ERRORS[10100]
|
||||
result.error = new Error(ERRORS[10100])
|
||||
|
||||
this.defer.reject(result)
|
||||
|
||||
delete this.xhr
|
||||
delete this.opt
|
||||
delete this.defer
|
||||
}
|
||||
|
||||
__timeout__(result) {
|
||||
result.status = 504
|
||||
result.statusText = ERRORS[10504]
|
||||
result.error = new Error(ERRORS[10504])
|
||||
|
||||
this.defer.reject(result)
|
||||
|
||||
delete this.xhr
|
||||
delete this.opt
|
||||
delete this.defer
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.request) {
|
||||
window.request = {
|
||||
get(url, param = {}) {
|
||||
return new _Request(url, 'GET', param)
|
||||
},
|
||||
post(url, param = {}) {
|
||||
return new _Request(url, 'POST', param)
|
||||
},
|
||||
upload(url, param = {}) {
|
||||
param.formType = 'form-data'
|
||||
return this.post(url, param)
|
||||
},
|
||||
download(url, param = {}) {
|
||||
param.dataType = 'blob'
|
||||
return this.get(url, param)
|
||||
},
|
||||
open(url, method = 'GET', param = {}) {
|
||||
if (typeof method === 'object') {
|
||||
param = method
|
||||
method = 'GET'
|
||||
}
|
||||
return new _Request(url, method, param)
|
||||
},
|
||||
version: '2.0.0-normal',
|
||||
init(param = {}) {
|
||||
this.__INIT__ = param
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default request
|
|
@ -1,225 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2016-11-26 16:35:45
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
function serialize(p, obj, q) {
|
||||
let k
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach(function(it, i) {
|
||||
k = p ? `${p}[${Array.isArray(it) ? i : ''}]` : i
|
||||
// k = p ? p + '[' + (Array.isArray(it) ? i : '') + ']' : i
|
||||
if (typeof it === 'object') {
|
||||
serialize(k, it, q)
|
||||
} else {
|
||||
q(k, it)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
for (let i in obj) {
|
||||
k = p ? `${p}[${i}]` : i
|
||||
// k = p ? p + '[' + i + ']' : i
|
||||
if (typeof obj[i] === 'object') {
|
||||
serialize(k, obj[i], q)
|
||||
} else {
|
||||
q(k, obj[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toS = Object.prototype.toString
|
||||
const doc = window.document
|
||||
const encode = encodeURIComponent
|
||||
const decode = decodeURIComponent
|
||||
|
||||
const TagHooks = function() {
|
||||
this.option = doc.createElement('select')
|
||||
this.thead = doc.createElement('table')
|
||||
this.td = doc.createElement('tr')
|
||||
this.area = doc.createElement('map')
|
||||
this.tr = doc.createElement('tbody')
|
||||
this.col = doc.createElement('colgroup')
|
||||
this.legend = doc.createElement('fieldset')
|
||||
this._default = doc.createElement('div')
|
||||
this.g = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
||||
|
||||
this.optgroup = this.option
|
||||
this.tbody = this.tfoot = this.colgroup = this.caption = this.thead
|
||||
this.th = this.td
|
||||
|
||||
'circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use'.replace(
|
||||
/,/g,
|
||||
m => {
|
||||
this[m] = this.g //处理svg
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const Helper = {
|
||||
tagHooks: new TagHooks(),
|
||||
rtagName: /<([\w:]+)/,
|
||||
rxhtml: /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
|
||||
scriptTypes: {
|
||||
'text/javascript': 1,
|
||||
'text/ecmascript': 1,
|
||||
'application/ecmascript': 1,
|
||||
'application/javascript': 1
|
||||
},
|
||||
rhtml: /<|&#?\w+;/
|
||||
}
|
||||
|
||||
export default {
|
||||
parseJS: function(code) {
|
||||
code = (code + '').trim()
|
||||
if (code) {
|
||||
if (code.indexOf('use strict') === 1) {
|
||||
let script = doc.createElement('script')
|
||||
script.text = code
|
||||
doc.head.appendChild(script).parentNode.removeChild(script)
|
||||
} else {
|
||||
eval(code)
|
||||
}
|
||||
}
|
||||
},
|
||||
parseXML: function(data, xml, tmp) {
|
||||
try {
|
||||
tmp = new DOMParser()
|
||||
xml = tmp.parseFromString(data, 'text/xml')
|
||||
} catch (e) {
|
||||
xml = void 0
|
||||
}
|
||||
|
||||
if (
|
||||
!xml ||
|
||||
!xml.documentElement ||
|
||||
xml.getElementsByTagName('parsererror').length
|
||||
) {
|
||||
console.error('Invalid XML: ' + data)
|
||||
}
|
||||
return xml
|
||||
},
|
||||
parseHTML: function(html) {
|
||||
let fragment = doc.createDocumentFragment().cloneNode(false)
|
||||
|
||||
if (typeof html !== 'string') {
|
||||
return fragment
|
||||
}
|
||||
|
||||
if (!Helper.rhtml.test(html)) {
|
||||
fragment.appendChild(document.createTextNode(html))
|
||||
return fragment
|
||||
}
|
||||
|
||||
html = html.replace(Helper.rxhtml, '<$1></$2>').trim()
|
||||
let tag = (Helper.rtagName.exec(html) || ['', ''])[1].toLowerCase()
|
||||
let wrap = Helper.tagHooks[tag] || Helper.tagHooks._default
|
||||
let firstChild = null
|
||||
|
||||
//使用innerHTML生成的script节点不会触发请求与执行text属性
|
||||
wrap.innerHTML = html
|
||||
let script = wrap.getElementsByTagName('script')
|
||||
if (script.length) {
|
||||
for (let i = 0, el; (el = script[i++]); ) {
|
||||
if (Helper.scriptTypes[el.type]) {
|
||||
let tmp = doc.createElement('script').cloneNode(false)
|
||||
el.attributes.forEach(function(attr) {
|
||||
tmp.setAttribute(attr.name, attr.value)
|
||||
})
|
||||
tmp.text = el.text
|
||||
el.parentNode.replaceChild(tmp, el)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ((firstChild = wrap.firstChild)) {
|
||||
fragment.appendChild(firstChild)
|
||||
}
|
||||
|
||||
return fragment
|
||||
},
|
||||
parseForm: function(form) {
|
||||
let data = {}
|
||||
let hasAttach = false
|
||||
for (let i = 0, field; (field = form.elements[i++]); ) {
|
||||
switch (field.type) {
|
||||
case 'select-one':
|
||||
case 'select-multiple':
|
||||
if (field.name.length && !field.disabled) {
|
||||
for (let j = 0, opt; (opt = field.options[j++]); ) {
|
||||
if (opt.selected) {
|
||||
data[field.name] = opt.value || opt.text
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'file':
|
||||
if (field.name.length && !field.disabled) {
|
||||
data[field.name] = field.files[0]
|
||||
hasAttach = true
|
||||
}
|
||||
break
|
||||
case undefined:
|
||||
case 'submit':
|
||||
case 'reset':
|
||||
case 'button':
|
||||
break //按钮啥的, 直接忽略
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
// 只处理选中的
|
||||
if (!field.checked) break
|
||||
default:
|
||||
if (field.name.length && !field.disabled) {
|
||||
data[field.name] = field.value
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果有附件, 改为FormData
|
||||
if (hasAttach) {
|
||||
return this.mkFormData(data)
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
},
|
||||
mkFormData(data) {
|
||||
let form = new FormData()
|
||||
for (let i in data) {
|
||||
let el = data[i]
|
||||
if (Array.isArray(el)) {
|
||||
el.forEach(function(it) {
|
||||
form.append(i + '[]', it)
|
||||
})
|
||||
} else {
|
||||
form.append(i, data[i])
|
||||
}
|
||||
}
|
||||
return form
|
||||
},
|
||||
param: function(obj) {
|
||||
if (!obj || typeof obj === 'string' || typeof obj === 'number') {
|
||||
return obj
|
||||
}
|
||||
|
||||
let arr = []
|
||||
let q = function(k, v) {
|
||||
if (/native code/.test(v)) {
|
||||
return
|
||||
}
|
||||
|
||||
v = typeof v === 'function' ? v() : v
|
||||
v = toS.call(v) !== '[object File]' ? encode(v) : v
|
||||
|
||||
arr.push(encode(k) + '=' + v)
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
serialize('', obj, q)
|
||||
}
|
||||
|
||||
return arr.join('&')
|
||||
}
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2017-04-14 21:04:50
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
//储存版本信息
|
||||
|
||||
//判定A标签的target属性是否指向自身
|
||||
//thanks https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L219
|
||||
function targetIsThisWindow(targetWindow) {
|
||||
if (
|
||||
!targetWindow ||
|
||||
targetWindow === window.name ||
|
||||
targetWindow === '_self' ||
|
||||
(targetWindow === 'top' && window == window.top)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
//hash前缀正则
|
||||
const PREFIX_REGEXP = /^(#!|#)[\/]?/
|
||||
const TRIM_REGEXP = /(^[/]+)|([/]+$)/g
|
||||
const DEFAULT_OPTIONS = {
|
||||
mode: 'hash', // hash | history
|
||||
allowReload: true //连续点击同一个链接是否重新加载
|
||||
}
|
||||
const LINKS = []
|
||||
const RULE_REGEXP = /(:id)|(\{id\})|(\{id:([A-z\d\,\[\]\{\}\-\+\*\?\!:\^\$]*)\})/g
|
||||
|
||||
class Router {
|
||||
constructor(options) {
|
||||
Anot.hideProperty(this, 'table', [])
|
||||
Anot.hideProperty(this, 'last', '')
|
||||
Anot.hideProperty(this, 'path', '')
|
||||
Anot.hideProperty(this, 'pathArr', [])
|
||||
Anot.hideProperty(this, 'ready', false)
|
||||
Anot.hideProperty(this, 'noMatch', null)
|
||||
Anot.hideProperty(
|
||||
this,
|
||||
'options',
|
||||
Object.assign({}, DEFAULT_OPTIONS, options)
|
||||
)
|
||||
this.__listen__()
|
||||
}
|
||||
|
||||
// 创建无new式Router实例
|
||||
static init(options = {}) {
|
||||
if (Anot.router) {
|
||||
throw new Error('不允许重复创建Router实例...')
|
||||
}
|
||||
|
||||
Anot.router = new this(options)
|
||||
return Anot.router
|
||||
}
|
||||
|
||||
// 事件监听
|
||||
__listen__() {
|
||||
let { mode } = this.options
|
||||
|
||||
Anot.bind(window, 'load, popstate', ev => {
|
||||
if (ev.type === 'load') {
|
||||
if (this.ready) {
|
||||
return
|
||||
}
|
||||
this.ready = true
|
||||
}
|
||||
|
||||
let path = mode === 'hash' ? location.hash : location.pathname
|
||||
|
||||
path = path.replace(PREFIX_REGEXP, '').trim()
|
||||
path = path.replace(TRIM_REGEXP, '')
|
||||
|
||||
if (ev.type === 'load') {
|
||||
this.go(path)
|
||||
// hash模式要手动触发一下路由检测
|
||||
if (mode === 'hash') {
|
||||
this.__check__(path)
|
||||
}
|
||||
} else {
|
||||
// 因为pushState不会触发popstate事件,
|
||||
// 所以这里只在hash模式或有ev.state的情况下才会主动触发路由检测
|
||||
if (mode === 'hash' || ev.state) {
|
||||
this.__check__(path)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//劫持页面上所有点击事件,如果事件源来自链接或其内部,
|
||||
//并且它不会跳出本页,并且以"#/"或"#!/"开头,那么触发go方法
|
||||
Anot.bind(document, 'click', ev => {
|
||||
let prevented =
|
||||
'defaultPrevented' in ev
|
||||
? ev.defaultPrevented
|
||||
: ev.returnValue === false
|
||||
|
||||
if (prevented || ev.ctrlKey || ev.metaKey || ev.which === 2) {
|
||||
return
|
||||
}
|
||||
|
||||
let target = ev.target
|
||||
while (target.nodeName !== 'A') {
|
||||
target = target.parentNode
|
||||
if (!target || target.tagName === 'BODY') {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (mode === 'history') {
|
||||
if (targetIsThisWindow(target.target)) {
|
||||
let href =
|
||||
target.getAttribute('href') || target.getAttribute('xlink:href')
|
||||
|
||||
if (
|
||||
!href ||
|
||||
/^(http[s]?:|ftp:)?\/\//.test(href) ||
|
||||
/^javascript:/.test(href)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// hash地址,只管修正前缀即可, 会触发popstate事件,所以这里只处理非hash的情况
|
||||
if (!PREFIX_REGEXP.test(href)) {
|
||||
// 非hash地址,则需要阻止默认事件
|
||||
// 并主动触发跳转, 同时强制清除hash
|
||||
ev.preventDefault()
|
||||
this.go(href, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
__parseRule__(rule, opts) {
|
||||
let re = rule.replace(RULE_REGEXP, function(m, p1, p2, p3, p4) {
|
||||
let w = '([\\w.-]'
|
||||
if (p1 || p2) {
|
||||
return w + '+)'
|
||||
} else {
|
||||
if (!/^\{[\d\,]+\}$/.test(p4)) {
|
||||
w = '('
|
||||
}
|
||||
return w + p4 + ')'
|
||||
}
|
||||
})
|
||||
re = re
|
||||
.replace(/(([^\\])([\/]+))/g, '$2\\/')
|
||||
.replace(/(([^\\])([\.]+))/g, '$2\\.')
|
||||
.replace(/(([^\\])([\-]+))/g, '$2\\-')
|
||||
.replace(/(\(.*)(\\[\-]+)(.*\))/g, '$1-$3')
|
||||
re = '^' + re + '$'
|
||||
opts.regexp = new RegExp(re)
|
||||
return opts
|
||||
}
|
||||
|
||||
__add__(rule, callback) {
|
||||
// 特殊值"!", 则自动作非匹配回调处理
|
||||
if (rule === '!') {
|
||||
this.noMatch = callback
|
||||
return
|
||||
}
|
||||
if (rule.charAt(0) !== '/') {
|
||||
console.error('路由规则必须以"/"开头')
|
||||
return
|
||||
}
|
||||
rule = rule.replace(/^[\/]+|[\/]+$|\s+/g, '')
|
||||
let opts = { rule, callback }
|
||||
|
||||
Anot.Array.ensure(this.table, this.__parseRule__(rule, opts))
|
||||
}
|
||||
|
||||
// 路由检测
|
||||
__check__(path) {
|
||||
let { allowReload } = this.options
|
||||
if (!allowReload && path === this.last) {
|
||||
return
|
||||
}
|
||||
|
||||
this.last = this.path
|
||||
this.path = path
|
||||
this.pathArr = path.split('/')
|
||||
LINKS.forEach(vm => {
|
||||
if (vm.rule.test(this.path)) {
|
||||
vm.active = true
|
||||
} else {
|
||||
vm.active = false
|
||||
}
|
||||
})
|
||||
for (let i = 0, route; (route = this.table[i++]); ) {
|
||||
let args = path.match(route.regexp)
|
||||
if (args) {
|
||||
args.shift()
|
||||
return route.callback.apply(route, args)
|
||||
}
|
||||
}
|
||||
this.noMatch && this.noMatch(this.path)
|
||||
}
|
||||
|
||||
// 跳转到路由
|
||||
go(path, forceCleanHash = false) {
|
||||
path = path.trim().replace(TRIM_REGEXP, '')
|
||||
let { mode } = this.options
|
||||
|
||||
if (mode === 'hash') {
|
||||
// 页面刷新时, 不主动添加空hash, 避免执行2次noMatch回调
|
||||
if (!path && path === location.hash) {
|
||||
return
|
||||
}
|
||||
location.hash = '!/' + path
|
||||
} else {
|
||||
let hash = forceCleanHash ? '' : location.hash
|
||||
let search = forceCleanHash ? '' : location.search
|
||||
if (forceCleanHash) {
|
||||
window.history.pushState({ path }, null, `/${path + search + hash}`)
|
||||
} else {
|
||||
window.history.replaceState({ path }, null, `/${path + search + hash}`)
|
||||
}
|
||||
// pushState不会触发popstate事件,所以要手动触发路由检测
|
||||
this.__check__(path)
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定路由事件
|
||||
on(rule, callback) {
|
||||
if (Array.isArray(rule)) {
|
||||
rule.forEach(it => {
|
||||
this.__add__(it, callback)
|
||||
})
|
||||
} else {
|
||||
this.__add__(rule, callback)
|
||||
}
|
||||
// 因为先初始化,才开始监听路由规则
|
||||
// 所以会导致wondow load的时候, 规则还没生效, 而生效之后,load已经结束
|
||||
// 所以这里需要手动再触发一次load
|
||||
Anot.fireDom(window, 'load')
|
||||
}
|
||||
}
|
||||
|
||||
export default Router
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
.container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -110,13 +109,16 @@ export default class Scroll {
|
|||
thumbX: 0,
|
||||
thumbY: 0,
|
||||
disabled: false,
|
||||
axis: 'xy' // 滚动方向, 默认x轴和y轴都可以滚动
|
||||
axis: 'xy', // 滚动方向, 默认x轴和y轴都可以滚动
|
||||
delay: 1000, // 节流延迟
|
||||
distance: 1 // 触发距离阀值, 单位像素
|
||||
}
|
||||
__init__() {
|
||||
/* render */
|
||||
this.__BOX__ = this.root.children[1]
|
||||
this.__X__ = this.root.children[2].children[0]
|
||||
this.__Y__ = this.root.children[3].children[0]
|
||||
this.__last__ = 0
|
||||
}
|
||||
|
||||
get scrollTop() {
|
||||
|
@ -157,6 +159,10 @@ export default class Scroll {
|
|||
return this.__BOX__.scrollHeight
|
||||
}
|
||||
|
||||
get scrollWidth() {
|
||||
return this.__BOX__.scrollWidth
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.props.disabled
|
||||
}
|
||||
|
@ -203,6 +209,30 @@ export default class Scroll {
|
|||
return moveY
|
||||
}
|
||||
|
||||
_fireReachEnd(action = 'reach-bottom') {
|
||||
var { sh, oh, delay, disabled } = this.props
|
||||
var top = this.__BOX__.scrollTop
|
||||
var now = Date.now()
|
||||
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
if (now - this.__last__ > delay) {
|
||||
if (action === 'reach-bottom') {
|
||||
if (oh + top < sh) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (top > 0) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.__last__ = now
|
||||
this.dispatchEvent(new CustomEvent(action))
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
// 初始化滚动条的位置和长度
|
||||
this._initFn = $.bind(this.__BOX__, 'mouseenter', ev => {
|
||||
|
@ -215,8 +245,9 @@ export default class Scroll {
|
|||
var oh = this.__BOX__.offsetHeight
|
||||
var sh = this.__BOX__.scrollHeight
|
||||
|
||||
var yh = ((oh * oh) / sh) >> 0
|
||||
var xw = ((ow * ow) / sw) >> 0
|
||||
var yh = ((oh * oh) / sh) >> 0 // 滚动条的高度
|
||||
var xw = ((ow * ow) / sw) >> 0 // 滚动条的宽度
|
||||
|
||||
if (yh < 50) {
|
||||
yh = 50
|
||||
}
|
||||
|
@ -319,6 +350,10 @@ export default class Scroll {
|
|||
|
||||
this.props.thumbY = fixedY
|
||||
this.__Y__.style.transform = `translateY(${fixedY}px)`
|
||||
|
||||
if (Math.abs(deltaY) > this.props.distance) {
|
||||
this._fireReachEnd(deltaY > 0 ? 'reach-bottom' : 'reach-top')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,6 +408,9 @@ export default class Scroll {
|
|||
}
|
||||
},
|
||||
mouseupFn = ev => {
|
||||
if (Math.abs(ev.pageY - startY) > this.props.distance) {
|
||||
this._fireReachEnd(ev.pageY > startY ? 'reach-bottom' : 'reach-top')
|
||||
}
|
||||
startX = null
|
||||
startY = null
|
||||
this.props.thumbX = moveX
|
||||
|
@ -410,22 +448,29 @@ export default class Scroll {
|
|||
$.catch(document, 'keydown', ev => {
|
||||
if (this._active) {
|
||||
var { oh, sh } = this.props
|
||||
var exec = false
|
||||
switch (ev.keyCode) {
|
||||
case 33: // pageUp
|
||||
exec = true
|
||||
this.scrollTop -= oh
|
||||
break
|
||||
case 34: // pageDown
|
||||
exec = true
|
||||
this.scrollTop += oh
|
||||
break
|
||||
case 35: // End
|
||||
exec = true
|
||||
this.scrollTop = sh
|
||||
break
|
||||
case 36: // Home
|
||||
exec = true
|
||||
this.scrollTop = 0
|
||||
break
|
||||
}
|
||||
if (exec) {
|
||||
ev.preventDefault()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.__observer = new MutationObserver(this._initFn)
|
||||
|
@ -452,6 +497,12 @@ export default class Scroll {
|
|||
case 'disabled':
|
||||
this[name] = true
|
||||
break
|
||||
case 'delay':
|
||||
this.props.delay = +val || 1000
|
||||
break
|
||||
case 'distance':
|
||||
this.props.distance = +val || 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
<template>
|
||||
<div class="slider-box">
|
||||
<div class="slider"></div>
|
||||
<input type="range" max="100" min="0" step="1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.slider-box {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--bg-color, --color-plain-3);
|
||||
border-radius: 3px;
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background: var(--color-teal-1);
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
opacity: 0;
|
||||
cursor: -webkit-grab;
|
||||
|
||||
&:active {
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([vertical]) {
|
||||
width: 6px;
|
||||
height: 100%;
|
||||
|
||||
.slider-box {
|
||||
align-items: flex-end;
|
||||
|
||||
.slider {
|
||||
height: 0;
|
||||
width: 6px;
|
||||
}
|
||||
input {
|
||||
appearance: slider-vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host([color='red']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-red-1);
|
||||
}
|
||||
}
|
||||
:host([color='orange']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-orange-1);
|
||||
}
|
||||
}
|
||||
:host([color='green']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-green-1);
|
||||
}
|
||||
}
|
||||
:host([color='blue']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-blue-1);
|
||||
}
|
||||
}
|
||||
:host([color='purple']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-purple-1);
|
||||
}
|
||||
}
|
||||
:host([color='grey']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-grey-1);
|
||||
}
|
||||
}
|
||||
:host([color='dark']) {
|
||||
.slider-box .slider {
|
||||
background: var(--color-dark-1);
|
||||
}
|
||||
}
|
||||
|
||||
:host([readonly]) {
|
||||
.slider-box,
|
||||
input {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
opacity: 0.6;
|
||||
.slider-box,
|
||||
input {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import $ from '../utils'
|
||||
|
||||
export default class Slider {
|
||||
props = {
|
||||
value: 0,
|
||||
max: 100,
|
||||
min: 0,
|
||||
step: 1,
|
||||
vertical: false, // 竖向
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
list: '' // input[type=range]的原生属性, 配合 datalist
|
||||
}
|
||||
|
||||
state = {
|
||||
decimal: 0
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
|
||||
var elem = this.root.children[1]
|
||||
this.__SLIDER__ = elem.children[0]
|
||||
this.__RANGE__ = elem.children[1]
|
||||
}
|
||||
|
||||
_updateRange() {
|
||||
var { max, min, step } = this.props
|
||||
var _decimal = (step + '').split('.')
|
||||
this.__RANGE__.setAttribute('max', max)
|
||||
this.__RANGE__.setAttribute('min', min)
|
||||
this.__RANGE__.setAttribute('step', step)
|
||||
if (_decimal.length > 1) {
|
||||
this.state.decimal = _decimal[1].length
|
||||
} else {
|
||||
this.state.decimal = 0
|
||||
}
|
||||
}
|
||||
|
||||
_updateValue(val) {
|
||||
var { max, min, vertical } = this.props
|
||||
|
||||
this.props.value = +val
|
||||
this.__RANGE__.value = +val
|
||||
|
||||
if (vertical) {
|
||||
this.__SLIDER__.style.height = `${(100 * val) / (max - min)}%`
|
||||
} else {
|
||||
this.__SLIDER__.style.width = `${(100 * val) / (max - min)}%`
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
return +this.props.value.toFixed(this.state.decimal)
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this._updateValue(+val || 0)
|
||||
}
|
||||
|
||||
get readOnly() {
|
||||
return this.props.readonly
|
||||
}
|
||||
|
||||
set readOnly(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.readonly) {
|
||||
return
|
||||
}
|
||||
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.readonly = true
|
||||
this.setAttribute('readonly', '')
|
||||
this.__RANGE__.setAttribute('disabled', '')
|
||||
} else {
|
||||
this.props.readonly = false
|
||||
this.removeAttribute('readonly')
|
||||
if (this.props.disabled === false) {
|
||||
this.__RANGE__.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.props.disabled
|
||||
}
|
||||
|
||||
set disabled(val) {
|
||||
var type = typeof val
|
||||
|
||||
if (val === this.props.disabled) {
|
||||
return
|
||||
}
|
||||
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||
this.props.disabled = true
|
||||
this.setAttribute('disabled', '')
|
||||
this.__RANGE__.setAttribute('disabled', '')
|
||||
} else {
|
||||
this.props.disabled = false
|
||||
this.removeAttribute('disabled')
|
||||
if (this.props.readonly === false) {
|
||||
this.__RANGE__.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this._inputFn = $.bind(this.__RANGE__, 'input', ev => {
|
||||
this._updateValue(this.__RANGE__.value)
|
||||
})
|
||||
}
|
||||
|
||||
unmount() {
|
||||
$.bind(this.__RANGE__, 'input', this._inputFn)
|
||||
delete this._inputFn
|
||||
}
|
||||
|
||||
watch() {
|
||||
switch (name) {
|
||||
case 'max':
|
||||
case 'min':
|
||||
case 'step':
|
||||
this.props[name] = +val
|
||||
this._updateRange()
|
||||
break
|
||||
|
||||
case 'value':
|
||||
this._updateValue(+val || 0)
|
||||
break
|
||||
|
||||
case 'vertical':
|
||||
this.props.vertical = true
|
||||
break
|
||||
|
||||
case 'readonly':
|
||||
this.readOnly = true
|
||||
break
|
||||
case 'disabled':
|
||||
this.disabled = true
|
||||
break
|
||||
|
||||
case 'list':
|
||||
this.__RANGE__.setAttribute('list', val)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,22 +0,0 @@
|
|||
<template>
|
||||
<label></label>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default class Slider {
|
||||
props = {
|
||||
value: 0,
|
||||
max: 100
|
||||
}
|
||||
|
||||
__init__() {
|
||||
/* render */
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,278 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* @authors yutent (yutent.io@gmail.com)
|
||||
* @date 2018-06-25 21:39:42
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
const __STORE__ = {}
|
||||
// 解析and条件
|
||||
function parse$And(it) {
|
||||
let result = ''
|
||||
for (let k in it) {
|
||||
let tmp = it[k]
|
||||
switch (Anot.type(tmp)) {
|
||||
case 'object':
|
||||
if (tmp.$has) {
|
||||
result += `it.${k}.indexOf(${JSON.stringify(tmp.$has)}) > -1`
|
||||
break
|
||||
}
|
||||
if (tmp.$in) {
|
||||
result += `${JSON.stringify(tmp.$in)}.indexOf(it.${k}) > -1`
|
||||
break
|
||||
}
|
||||
if (tmp.$regex) {
|
||||
result += `${tmp.$regex}.test(it.${k})`
|
||||
break
|
||||
}
|
||||
|
||||
// 区间解析
|
||||
if (tmp.$lt || tmp.$lte) {
|
||||
result += `it.${k} <${tmp.$lte ? '=' : ''} ${tmp.$lt || tmp.$lte}`
|
||||
if (tmp.$gt || tmp.$gte) {
|
||||
result += ` && it.${k} >${tmp.$gte ? '=' : ''} ${tmp.$gt ||
|
||||
tmp.$gte}`
|
||||
}
|
||||
break
|
||||
}
|
||||
if (tmp.$gt || tmp.$gte) {
|
||||
result += `it.${k} >${tmp.$gte ? '=' : ''} ${tmp.$gt || tmp.$gte}`
|
||||
break
|
||||
}
|
||||
if (tmp.$eq) {
|
||||
result += `it.${k} === ${tmp.$eq}`
|
||||
break
|
||||
}
|
||||
default:
|
||||
result += `it.${k} === ${JSON.stringify(it[k])}`
|
||||
break
|
||||
}
|
||||
result += ' && '
|
||||
}
|
||||
result = result.slice(0, -4)
|
||||
|
||||
if (!result) {
|
||||
result = 'true'
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 解析or条件
|
||||
function parse$Or(arr) {
|
||||
let result = ''
|
||||
|
||||
arr.forEach(it => {
|
||||
result += '('
|
||||
|
||||
result += parse$And(it)
|
||||
result += ') || '
|
||||
})
|
||||
return result.slice(0, -4)
|
||||
}
|
||||
|
||||
class AnotStore {
|
||||
constructor(name) {
|
||||
Anot.hideProperty(this, '__name__', name)
|
||||
Anot.hideProperty(this, '__LAST_QUERY__', '')
|
||||
Anot.hideProperty(this, '__QUERY_HISTORY__', [])
|
||||
if (!__STORE__[name]) {
|
||||
__STORE__[name] = []
|
||||
__STORE__[`${name}Dict`] = {}
|
||||
}
|
||||
}
|
||||
|
||||
static collection(name) {
|
||||
return new this(name)
|
||||
}
|
||||
|
||||
__MAKE_FN__(opt) {
|
||||
let fnStr = `
|
||||
let result = [];
|
||||
let num = 0;
|
||||
for (let it of arr) {
|
||||
if(`
|
||||
|
||||
if (opt.$or) {
|
||||
fnStr += parse$Or(opt.$or)
|
||||
} else {
|
||||
fnStr += parse$And(opt)
|
||||
}
|
||||
fnStr += `){
|
||||
result.push(it)
|
||||
num++
|
||||
if(limit > 0 && num >= limit){
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;`
|
||||
|
||||
return Function('arr', 'limit', fnStr)
|
||||
}
|
||||
|
||||
// 清除当前集合的数据及缓存, 默认只清除缓存
|
||||
clear(force) {
|
||||
this.__QUERY_HISTORY__ = []
|
||||
this.__LAST_QUERY__ = ''
|
||||
if (force) {
|
||||
__STORE__[this.__name__] = []
|
||||
__STORE__[`${this.__name__}Dict`] = {}
|
||||
}
|
||||
}
|
||||
|
||||
// 查询多条记录,返回数组
|
||||
getAll({ filter, limit = [] } = {}) {
|
||||
const collection = __STORE__[this.__name__]
|
||||
let result = []
|
||||
let forceLimited = false // 强制限制查询结果集
|
||||
if (!collection || !collection.length) {
|
||||
return result
|
||||
}
|
||||
|
||||
if (limit.length < 1) {
|
||||
limit = [0]
|
||||
}
|
||||
|
||||
if (limit.length < 2 && filter) {
|
||||
forceLimited = true
|
||||
if (limit[0] > 0) {
|
||||
limit.unshift(0)
|
||||
}
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
let query = JSON.stringify(filter)
|
||||
if (this.__LAST_QUERY__ === query) {
|
||||
result = this.__QUERY_HISTORY__.slice.apply(
|
||||
this.__QUERY_HISTORY__,
|
||||
limit
|
||||
)
|
||||
} else {
|
||||
let tmpFn = this.__MAKE_FN__(filter)
|
||||
result = tmpFn(collection, forceLimited ? limit[1] || 0 : 0)
|
||||
|
||||
// 非强制限制的查询, 缓存结果集
|
||||
if (!forceLimited) {
|
||||
this.__LAST_QUERY__ = query
|
||||
this.__QUERY_HISTORY__ = result
|
||||
result = this.__QUERY_HISTORY__.slice.apply(
|
||||
this.__QUERY_HISTORY__,
|
||||
limit
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = collection.slice.apply(collection, limit)
|
||||
}
|
||||
return Anot.deepCopy(result)
|
||||
}
|
||||
|
||||
// 查询单条记录, 返回Object对象
|
||||
get(_id) {
|
||||
const collectionDict = __STORE__[`${this.__name__}Dict`]
|
||||
return Anot.deepCopy(collectionDict[_id]) || null
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
count({ filter } = {}) {
|
||||
if (filter) {
|
||||
if (this.__LAST_QUERY__ === JSON.stringify(filter)) {
|
||||
return this.__QUERY_HISTORY__.length
|
||||
} else {
|
||||
return this.getAll({ filter, limit: [0] }).length
|
||||
}
|
||||
}
|
||||
return __STORE__[this.__name__].length
|
||||
}
|
||||
|
||||
__INSERT__(item, primary) {
|
||||
let collection = __STORE__[this.__name__]
|
||||
let collectionDict = __STORE__[`${this.__name__}Dict`]
|
||||
let _id = item[primary || 'id']
|
||||
let tmp = collectionDict[_id]
|
||||
// 已存在, 则直接更新
|
||||
if (tmp) {
|
||||
this.update(_id, item)
|
||||
} else {
|
||||
collection.push(item)
|
||||
collectionDict[_id] = item
|
||||
}
|
||||
}
|
||||
|
||||
// 插入数据, 可以同时插入多条
|
||||
insert(items, primary) {
|
||||
if (!Array.isArray(items)) {
|
||||
items = [items]
|
||||
}
|
||||
items.forEach(item => {
|
||||
this.__INSERT__(item, primary)
|
||||
})
|
||||
this.clear()
|
||||
}
|
||||
|
||||
// 按指定字段排序, 是否字符串排序, 是否逆序
|
||||
sort(key, locale, desc) {
|
||||
let fnStr = ''
|
||||
if (locale && window.Intl) {
|
||||
fnStr += `
|
||||
let col = new Intl.Collator('zh')
|
||||
`
|
||||
}
|
||||
if (desc) {
|
||||
fnStr += 'return arr.sort((b, a) => {'
|
||||
} else {
|
||||
fnStr += 'return arr.sort((a, b) => {'
|
||||
}
|
||||
|
||||
fnStr += `
|
||||
let filter = function(val) {
|
||||
try {
|
||||
return val.${key} || ''
|
||||
} catch (err) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
if (locale) {
|
||||
if (window.Intl) {
|
||||
fnStr += `return col.compare(filter(a), filter(b))`
|
||||
} else {
|
||||
fnStr += `return (filter(a) + '').localeCompare(filter(b), 'zh')`
|
||||
}
|
||||
} else {
|
||||
fnStr += `return filter(a) - filter(b)`
|
||||
}
|
||||
fnStr += '\n})'
|
||||
Function('arr', fnStr).call(this, __STORE__[this.__name__])
|
||||
this.clear()
|
||||
}
|
||||
|
||||
// 更新集合中的数据
|
||||
update(_id, data) {
|
||||
let collection = __STORE__[this.__name__]
|
||||
let collectionDict = __STORE__[`${this.__name__}Dict`]
|
||||
|
||||
let tmp = collectionDict[_id]
|
||||
let idx = collection.indexOf(tmp)
|
||||
|
||||
collection.splice(idx, 1, data)
|
||||
collectionDict[_id] = data
|
||||
}
|
||||
|
||||
// 删除集合中单条数据
|
||||
remove(_id) {
|
||||
let collection = __STORE__[this.__name__]
|
||||
let collectionDict = __STORE__[`${this.__name__}Dict`]
|
||||
|
||||
let tmp = collectionDict[_id]
|
||||
let idx = collection.indexOf(tmp)
|
||||
|
||||
collection.splice(idx, 1)
|
||||
delete collectionDict[_id]
|
||||
}
|
||||
}
|
||||
|
||||
Anot.store = window.store = AnotStore
|
||||
|
||||
export default AnotStore
|
|
@ -11,7 +11,7 @@
|
|||
:host {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
color: nth($cd, 1);
|
||||
color: var(--color-dark-1);
|
||||
}
|
||||
.table {
|
||||
display: flex;
|
||||
|
@ -23,9 +23,9 @@
|
|||
.tfoot {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
--border-bottom: 0;
|
||||
background: nth($cp, 1);
|
||||
background: var(--color-plain-1);
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
|||
}
|
||||
.tbody {
|
||||
flex: 1;
|
||||
border: 1px solid nth($cp, 3);
|
||||
border: 1px solid var(--color-plain-3);
|
||||
|
||||
::slotted(wc-tr:last-child) {
|
||||
--border-bottom: 0;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
min-width: 60px;
|
||||
// min-height: 36px;
|
||||
// height: auto;
|
||||
border-left: 1px solid nth($cp, 3);
|
||||
border-left: 1px solid var(--color-plain-3);
|
||||
|
||||
.cell {
|
||||
padding: var(--padding, 5px 8px);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
border-bottom: var(--border-bottom, 1px solid nth($cp, 3));
|
||||
border-bottom: var(--border-bottom, 1px solid var(--color-plain-3));
|
||||
}
|
||||
}
|
||||
:host(:hover) {
|
||||
|
|
23
src/utils.js
23
src/utils.js
|
@ -31,6 +31,28 @@ export default {
|
|||
}
|
||||
})(),
|
||||
|
||||
//取得距离页面左上角的坐标
|
||||
offset(node) {
|
||||
try {
|
||||
var rect = node.getBoundingClientRect()
|
||||
|
||||
if (rect.width || rect.height || node.getClientRects().length) {
|
||||
var doc = node.ownerDocument
|
||||
var root = doc.documentElement
|
||||
var win = doc.defaultView
|
||||
return {
|
||||
top: rect.top + win.pageYOffset - root.clientTop,
|
||||
left: rect.left + win.pageXOffset - root.clientLeft
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
left: 0,
|
||||
top: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 对象/数组遍历
|
||||
* 支持跳出
|
||||
|
@ -112,6 +134,7 @@ export default {
|
|||
fn(ev)
|
||||
})
|
||||
},
|
||||
|
||||
clearOutside(fn = noop) {
|
||||
this.unbind(document, 'mousedown', fn)
|
||||
}
|
||||
|
|
Reference in New Issue