tiyan
This commit is contained in:
parent
3cab778234
commit
996a18043a
|
@ -45,6 +45,9 @@
|
|||
icon="el-icon-refresh-left"
|
||||
@click="resetFilter"
|
||||
>刷新重置</el-button>
|
||||
<el-checkbox v-model="showCreate" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
|
||||
创建时间
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div style="margin-top:10px">
|
||||
<el-button type="primary" @click="handleAddUser" icon="el-icon-plus">新增</el-button>
|
||||
|
@ -115,7 +118,7 @@
|
|||
<el-tag v-if="scope.row.workscope_name">{{ scope.row.workscope_name }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建日期" sortable='custom' prop="create_time">
|
||||
<el-table-column label="创建日期" sortable='custom' prop="create_time" v-if="showCreate">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.create_time }}</span>
|
||||
</template>
|
||||
|
@ -246,6 +249,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
tableKey: 0,
|
||||
showCreate: false,
|
||||
uploadUrl: uploadUrl(),
|
||||
popovervisible: false,
|
||||
consumer: defaultConsumer,
|
||||
|
|
|
@ -54,7 +54,7 @@ export default {
|
|||
{ required: true, message: "请输入", trigger: "blur" }
|
||||
],
|
||||
},
|
||||
is_show: false
|
||||
is_show: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
audio 扩展包
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
*/
|
||||
Component({
|
||||
data: {
|
||||
time: '00:00'
|
||||
},
|
||||
properties: {
|
||||
author: String,
|
||||
autoplay: Boolean,
|
||||
controls: Boolean,
|
||||
loop: Boolean,
|
||||
name: String,
|
||||
poster: String,
|
||||
src: {
|
||||
type: String,
|
||||
observer(src) {
|
||||
this.setSrc(src);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this._ctx = wx.createInnerAudioContext();
|
||||
this._ctx.onError((err) => {
|
||||
this.setData({
|
||||
error: true
|
||||
})
|
||||
this.triggerEvent('error', err);
|
||||
})
|
||||
this._ctx.onTimeUpdate(() => {
|
||||
var time = this._ctx.currentTime,
|
||||
min = parseInt(time / 60),
|
||||
sec = Math.ceil(time % 60),
|
||||
data = {};
|
||||
data.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec);
|
||||
if (!this.lastTime) data.value = time / this._ctx.duration * 100; // 不在拖动状态下
|
||||
this.setData(data);
|
||||
})
|
||||
this._ctx.onEnded(() => {
|
||||
this.setData({
|
||||
playing: false
|
||||
})
|
||||
})
|
||||
},
|
||||
detached() {
|
||||
this._ctx.destroy();
|
||||
},
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
if (this.data.playing && this._ctx.paused)
|
||||
this._ctx.play();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 设置源
|
||||
setSrc(src) {
|
||||
this._ctx.autoplay = this.data.autoplay;
|
||||
this._ctx.loop = this.data.loop;
|
||||
this._ctx.src = src;
|
||||
},
|
||||
// 播放
|
||||
play() {
|
||||
this._ctx.play();
|
||||
this.setData({
|
||||
playing: true
|
||||
})
|
||||
this.triggerEvent('play');
|
||||
},
|
||||
// 暂停
|
||||
pause() {
|
||||
this._ctx.pause();
|
||||
this.setData({
|
||||
playing: false
|
||||
})
|
||||
this.triggerEvent('pause');
|
||||
},
|
||||
// 移动进度条
|
||||
seek(sec) {
|
||||
this._ctx.seek(sec);
|
||||
},
|
||||
// 内部方法
|
||||
_seeking(e) {
|
||||
if (e.timeStamp - this.lastTime < 200) return;
|
||||
var time = Math.round(e.detail.value / 100 * this._ctx.duration),
|
||||
min = parseInt(time / 60),
|
||||
sec = time % 60;
|
||||
this.setData({
|
||||
time: (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
|
||||
})
|
||||
this.lastTime = e.timeStamp;
|
||||
},
|
||||
_seeked(e) {
|
||||
this.seek(e.detail.value / 100 * this._ctx.duration);
|
||||
this.lastTime = void 0;
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<view wx:if="{{controls}}" class="contain">
|
||||
<slider class="slider" activeColor="#585959" block-size="12" disabled="{{error}}" value="{{value}}" bindchanging="_seeking" bindchange="_seeked" />
|
||||
<view class="poster" style="background-image:url('{{poster}}')">
|
||||
<view class="button" bindtap="{{playing?'pause':'play'}}">
|
||||
<view class="{{playing?'pause':'play'}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="title">
|
||||
<view class="name">{{name||'未知音频'}}</view>
|
||||
<view class="author">{{author||'未知作者'}}</view>
|
||||
</view>
|
||||
<view class="time">{{time}}</view>
|
||||
</view>
|
|
@ -0,0 +1,75 @@
|
|||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
.author {
|
||||
color: #888;
|
||||
font-size: 28rpx;
|
||||
width: 140rpx;
|
||||
}
|
||||
.author, .name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.button {
|
||||
align-items: center;
|
||||
background-color: rgb(0, 0, 0, 0.2);
|
||||
border: 3.5rpx solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
height: 47rpx;
|
||||
justify-content: center;
|
||||
opacity: 0.9;
|
||||
overflow: hidden;
|
||||
width: 47rpx;
|
||||
}
|
||||
.contain {
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 650rpx;
|
||||
}
|
||||
.name {
|
||||
font-size: 33rpx;
|
||||
line-height: 80rpx;
|
||||
width: 320rpx;
|
||||
}
|
||||
.pause {
|
||||
width: 16.5rpx;
|
||||
height: 16.5rpx;
|
||||
background-color: white;
|
||||
}
|
||||
.play {
|
||||
border-bottom: 10rpx solid transparent;
|
||||
border-left: 18rpx solid white;
|
||||
border-top: 10rpx solid transparent;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
.poster {
|
||||
align-items: center;
|
||||
background-color: #e6e6e6;
|
||||
background-size: contain;
|
||||
display: flex;
|
||||
height: 152.35rpx;
|
||||
justify-content: center;
|
||||
width: 152.35rpx;
|
||||
}
|
||||
.slider {
|
||||
position: absolute;
|
||||
bottom: 18.75rpx;
|
||||
margin: 0;
|
||||
right: 35.15rpx;
|
||||
width: 316.4rpx;
|
||||
}
|
||||
.title {
|
||||
flex: 1;
|
||||
margin: 10rpx 0 0 35.15rpx;
|
||||
text-align: left;
|
||||
}
|
||||
.time {
|
||||
color: #888;
|
||||
font-size: 28rpx;
|
||||
margin: 16.4rpx 35.15rpx 0 0;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
解析和匹配 Css 的选择器
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/03/15
|
||||
*/
|
||||
var cfg = require('./config.js');
|
||||
class CssHandler {
|
||||
constructor(tagStyle) {
|
||||
var styles = Object.assign({}, cfg.userAgentStyles);
|
||||
for (var item in tagStyle)
|
||||
styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
|
||||
this.styles = styles;
|
||||
}
|
||||
getStyle = data => this.styles = new CssParser(data, this.styles).parse();
|
||||
match(name, attrs) {
|
||||
var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
|
||||
if (attrs.class) {
|
||||
var items = attrs.class.split(' ');
|
||||
for (var i = 0, item; item = items[i]; i++)
|
||||
if (tmp = this.styles['.' + item])
|
||||
matched += tmp + ';';
|
||||
}
|
||||
if (tmp = this.styles['#' + attrs.id])
|
||||
matched += tmp + ';';
|
||||
return matched;
|
||||
}
|
||||
}
|
||||
module.exports = CssHandler;
|
||||
class CssParser {
|
||||
constructor(data, init) {
|
||||
this.data = data;
|
||||
this.floor = 0;
|
||||
this.i = 0;
|
||||
this.list = [];
|
||||
this.res = init;
|
||||
this.state = this.Space;
|
||||
}
|
||||
parse() {
|
||||
for (var c; c = this.data[this.i]; this.i++)
|
||||
this.state(c);
|
||||
return this.res;
|
||||
}
|
||||
section = () => this.data.substring(this.start, this.i);
|
||||
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
// 状态机
|
||||
Space(c) {
|
||||
if (c == '.' || c == '#' || this.isLetter(c)) {
|
||||
this.start = this.i;
|
||||
this.state = this.Name;
|
||||
} else if (c == '/' && this.data[this.i + 1] == '*')
|
||||
this.Comment();
|
||||
else if (!cfg.blankChar[c] && c != ';')
|
||||
this.state = this.Ignore;
|
||||
}
|
||||
Comment() {
|
||||
this.i = this.data.indexOf('*/', this.i) + 1;
|
||||
if (!this.i) this.i = this.data.length;
|
||||
this.state = this.Space;
|
||||
}
|
||||
Ignore(c) {
|
||||
if (c == '{') this.floor++;
|
||||
else if (c == '}' && !--this.floor) this.state = this.Space;
|
||||
}
|
||||
Name(c) {
|
||||
if (cfg.blankChar[c]) {
|
||||
this.list.push(this.section());
|
||||
this.state = this.NameSpace;
|
||||
} else if (c == '{') {
|
||||
this.list.push(this.section());
|
||||
this.Content();
|
||||
} else if (c == ',') {
|
||||
this.list.push(this.section());
|
||||
this.Comma();
|
||||
} else if (!this.isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
|
||||
this.state = this.Ignore;
|
||||
}
|
||||
NameSpace(c) {
|
||||
if (c == '{') this.Content();
|
||||
else if (c == ',') this.Comma();
|
||||
else if (!cfg.blankChar[c]) this.state = this.Ignore;
|
||||
}
|
||||
Comma() {
|
||||
while (cfg.blankChar[this.data[++this.i]]);
|
||||
if (this.data[this.i] == '{') this.Content();
|
||||
else {
|
||||
this.start = this.i--;
|
||||
this.state = this.Name;
|
||||
}
|
||||
}
|
||||
Content() {
|
||||
this.start = ++this.i;
|
||||
if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
|
||||
var content = this.section();
|
||||
for (var i = 0, item; item = this.list[i++];)
|
||||
if (this.res[item]) this.res[item] += ';' + content;
|
||||
else this.res[item] = content;
|
||||
this.list = [];
|
||||
this.state = this.Space;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
将 html 解析为适用于小程序 rich-text 的 DOM 结构
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/04/26
|
||||
*/
|
||||
var cfg = require('./config.js'),
|
||||
blankChar = cfg.blankChar,
|
||||
CssHandler = require('./CssHandler.js'),
|
||||
screenWidth = wx.getSystemInfoSync().screenWidth;
|
||||
try {
|
||||
var emoji = require('./emoji.js');
|
||||
} catch (e) {}
|
||||
class MpHtmlParser {
|
||||
constructor(data, options = {}) {
|
||||
this.attrs = {};
|
||||
this.compress = options.compress;
|
||||
this.CssHandler = new CssHandler(options.tagStyle, screenWidth);
|
||||
this.data = data;
|
||||
this.domain = options.domain;
|
||||
this.DOM = [];
|
||||
this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
|
||||
this.protocol = this.domain && this.domain.includes('://') ? this.domain.split('://')[0] : 'http';
|
||||
this.state = this.Text;
|
||||
this.STACK = [];
|
||||
this.useAnchor = options.useAnchor;
|
||||
this.xml = options.xml;
|
||||
}
|
||||
parse() {
|
||||
if (emoji) this.data = emoji.parseEmoji(this.data);
|
||||
for (var c; c = this.data[this.i]; this.i++)
|
||||
this.state(c);
|
||||
if (this.state == this.Text) this.setText();
|
||||
while (this.STACK.length) this.popNode(this.STACK.pop());
|
||||
if (this.DOM.length) {
|
||||
this.DOM[0].PoweredBy = 'Parser';
|
||||
if (this.title) this.DOM[0].title = this.title;
|
||||
}
|
||||
return this.DOM;
|
||||
}
|
||||
// 设置属性
|
||||
setAttr() {
|
||||
var name = this.getName(this.attrName);
|
||||
if (cfg.trustAttrs[name]) {
|
||||
var val = this.attrVal;
|
||||
if (val) {
|
||||
if (name == 'src') this.attrs[name] = this.getUrl(this.decode(val, 'amp'));
|
||||
else if (name == 'href' || name == 'style') this.attrs[name] = this.decode(val, 'amp');
|
||||
else this.attrs[name] = val;
|
||||
} else if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
|
||||
}
|
||||
this.attrVal = '';
|
||||
while (blankChar[this.data[this.i]]) this.i++;
|
||||
if (this.isClose()) this.setNode();
|
||||
else {
|
||||
this.start = this.i;
|
||||
this.state = this.AttrName;
|
||||
}
|
||||
}
|
||||
// 设置文本节点
|
||||
setText() {
|
||||
var back, text = this.section();
|
||||
if (!text) return;
|
||||
text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
|
||||
if (back) {
|
||||
this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
|
||||
let j = this.start + text.length;
|
||||
for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
|
||||
return;
|
||||
}
|
||||
if (!this.pre) {
|
||||
// 合并空白符
|
||||
var tmp = [];
|
||||
for (let i = text.length, c; c = text[--i];)
|
||||
if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
|
||||
text = tmp.join('');
|
||||
if (text == ' ') return;
|
||||
}
|
||||
this.siblings().push({
|
||||
type: 'text',
|
||||
text: this.decode(text)
|
||||
});
|
||||
}
|
||||
// 设置元素节点
|
||||
setNode() {
|
||||
var node = {
|
||||
name: this.getName(this.tagName),
|
||||
attrs: this.attrs
|
||||
},
|
||||
close = cfg.selfClosingTags[node.name] || (this.xml && this.data[this.i] == '/');
|
||||
this.attrs = {};
|
||||
if (!cfg.ignoreTags[node.name]) {
|
||||
this.matchAttr(node);
|
||||
if (!close) {
|
||||
node.children = [];
|
||||
if (node.name == 'pre' && cfg.highlight) {
|
||||
this.remove(node);
|
||||
this.pre = node.pre = true;
|
||||
}
|
||||
this.siblings().push(node);
|
||||
this.STACK.push(node);
|
||||
} else if (!cfg.filter || cfg.filter(node, this) != false)
|
||||
this.siblings().push(node);
|
||||
} else {
|
||||
if (!close) this.remove(node);
|
||||
else if (node.name == 'source') {
|
||||
var parent = this.parent(),
|
||||
attrs = node.attrs;
|
||||
if (parent && attrs.src)
|
||||
if (parent.name == 'video' || parent.name == 'audio')
|
||||
parent.attrs.source.push(attrs.src);
|
||||
else {
|
||||
var i, media = attrs.media;
|
||||
if (parent.name == 'picture' && !parent.attrs.src && (!media || (media.includes('px') &&
|
||||
(((i = media.indexOf('min-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth > parseInt(media.substr(i + 1))) ||
|
||||
((i = media.indexOf('max-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth < parseInt(media.substr(i + 1)))))))
|
||||
parent.attrs.src = attrs.src;
|
||||
}
|
||||
} else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
|
||||
}
|
||||
if (this.data[this.i] == '/') this.i++;
|
||||
this.start = this.i + 1;
|
||||
this.state = this.Text;
|
||||
}
|
||||
// 移除标签
|
||||
remove(node) {
|
||||
var name = node.name,
|
||||
j = this.i;
|
||||
while (1) {
|
||||
if ((this.i = this.data.indexOf('</', this.i + 1)) == -1) {
|
||||
if (name == 'pre' || name == 'svg') this.i = j;
|
||||
else this.i = this.data.length;
|
||||
return;
|
||||
}
|
||||
this.start = (this.i += 2);
|
||||
while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
|
||||
if (this.getName(this.section()) == name) {
|
||||
// 代码块高亮
|
||||
if (name == 'pre') {
|
||||
this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) + this.data.substr(this.i - 5);
|
||||
return this.i = j;
|
||||
} else if (name == 'style')
|
||||
this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
|
||||
else if (name == 'title')
|
||||
this.title = this.data.substring(j + 1, this.i - 7);
|
||||
if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
|
||||
// 处理 svg
|
||||
if (name == 'svg') {
|
||||
var src = this.data.substring(j, this.i + 1);
|
||||
if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
|
||||
var i = j;
|
||||
while (this.data[j] != '<') j--;
|
||||
src = this.data.substring(j, i) + src;
|
||||
var parent = this.parent();
|
||||
if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
|
||||
parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
|
||||
this.siblings().push({
|
||||
name: 'img',
|
||||
attrs: {
|
||||
src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
|
||||
ignore: 'T'
|
||||
}
|
||||
})
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理属性
|
||||
matchAttr(node) {
|
||||
var attrs = node.attrs,
|
||||
style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
|
||||
styleObj = {};
|
||||
if (attrs.id) {
|
||||
if (this.compress & 1) attrs.id = void 0;
|
||||
else if (this.useAnchor) this.bubble();
|
||||
}
|
||||
if ((this.compress & 2) && attrs.class) attrs.class = void 0;
|
||||
switch (node.name) {
|
||||
case 'a':
|
||||
case 'ad':
|
||||
this.bubble();
|
||||
break;
|
||||
case 'font':
|
||||
if (attrs.color) {
|
||||
styleObj['color'] = attrs.color;
|
||||
attrs.color = void 0;
|
||||
}
|
||||
if (attrs.face) {
|
||||
styleObj['font-family'] = attrs.face;
|
||||
attrs.face = void 0;
|
||||
}
|
||||
if (attrs.size) {
|
||||
var size = parseInt(attrs.size);
|
||||
if (size < 1) size = 1;
|
||||
else if (size > 7) size = 7;
|
||||
var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
|
||||
styleObj['font-size'] = map[size - 1];
|
||||
attrs.size = void 0;
|
||||
}
|
||||
break;
|
||||
case 'video':
|
||||
case 'audio':
|
||||
if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
|
||||
else this[`${node.name}Num`]++;
|
||||
if (node.name == 'video') {
|
||||
if (this.videoNum > 3)
|
||||
node.lazyLoad = 1;
|
||||
if (attrs.width) {
|
||||
styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px');
|
||||
attrs.width = void 0;
|
||||
}
|
||||
if (attrs.height) {
|
||||
styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px');
|
||||
attrs.height = void 0;
|
||||
}
|
||||
}
|
||||
attrs.source = [];
|
||||
if (attrs.src) attrs.source.push(attrs.src);
|
||||
if (!attrs.controls && !attrs.autoplay)
|
||||
console.warn(`存在没有 controls 属性的 ${node.name} 标签,可能导致无法播放`, node);
|
||||
this.bubble();
|
||||
break;
|
||||
case 'td':
|
||||
case 'th':
|
||||
if (attrs.colspan || attrs.rowspan)
|
||||
for (var k = this.STACK.length, item; item = this.STACK[--k];)
|
||||
if (item.name == 'table') {
|
||||
item.c = void 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attrs.align) {
|
||||
styleObj['text-align'] = attrs.align;
|
||||
attrs.align = void 0;
|
||||
}
|
||||
// 压缩 style
|
||||
var styles = style.split(';');
|
||||
style = '';
|
||||
for (var i = 0, len = styles.length; i < len; i++) {
|
||||
var info = styles[i].split(':');
|
||||
if (info.length < 2) continue;
|
||||
let key = info[0].trim().toLowerCase(),
|
||||
value = info.slice(1).join(':').trim();
|
||||
if (value.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value.includes('safe'))
|
||||
style += `;${key}:${value}`;
|
||||
else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
|
||||
styleObj[key] = value;
|
||||
}
|
||||
if (node.name == 'img' || node.name == 'picture') {
|
||||
if (attrs['data-src']) {
|
||||
attrs.src = attrs.src || attrs['data-src'];
|
||||
attrs['data-src'] = void 0;
|
||||
}
|
||||
if ((attrs.src || node.name == 'picture') && !attrs.ignore) {
|
||||
if (this.bubble())
|
||||
attrs.i = (this.imgNum++).toString();
|
||||
else attrs.ignore = 'T';
|
||||
}
|
||||
if (attrs.ignore) styleObj['max-width'] = '100%';
|
||||
var width;
|
||||
if(styleObj.width) width = styleObj.width;
|
||||
else if(attrs.width) width = attrs.width.includes('%') ? attrs.width : attrs.width + 'px';
|
||||
if (width) {
|
||||
styleObj.width = width;
|
||||
attrs.width = '100%';
|
||||
if (parseInt(width) > screenWidth) {
|
||||
styleObj.height = '';
|
||||
if (attrs.height) attrs.height = void 0;
|
||||
}
|
||||
}
|
||||
if (styleObj.height) {
|
||||
attrs.height = styleObj.height;
|
||||
styleObj.height = '';
|
||||
} else if (attrs.height && !attrs.height.includes('%'))
|
||||
attrs.height += 'px';
|
||||
}
|
||||
for (var key in styleObj) {
|
||||
var value = styleObj[key];
|
||||
if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
|
||||
// 填充链接
|
||||
if (value.includes('url')) {
|
||||
var j = value.indexOf('(');
|
||||
if (j++ != -1) {
|
||||
while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
|
||||
value = value.substr(0, j) + this.getUrl(value.substr(j));
|
||||
}
|
||||
}
|
||||
// 转换 rpx
|
||||
else if (value.includes('rpx'))
|
||||
value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * screenWidth / 750 + 'px');
|
||||
else if (key == 'white-space' && value.includes('pre'))
|
||||
this.pre = node.pre = true;
|
||||
style += `;${key}:${value}`;
|
||||
}
|
||||
style = style.substr(1);
|
||||
if (style) attrs.style = style;
|
||||
}
|
||||
// 节点出栈处理
|
||||
popNode(node) {
|
||||
// 空白符处理
|
||||
if (node.pre) {
|
||||
node.pre = this.pre = void 0;
|
||||
for (let i = this.STACK.length; i--;)
|
||||
if (this.STACK[i].pre)
|
||||
this.pre = true;
|
||||
}
|
||||
if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
|
||||
return this.siblings().pop();
|
||||
var attrs = node.attrs;
|
||||
// 替换一些标签名
|
||||
if (node.name == 'picture') {
|
||||
node.name = 'img';
|
||||
if (!attrs.src && (node.children[0] || '').name == 'img')
|
||||
attrs.src = node.children[0].attrs.src;
|
||||
return node.children = void 0;
|
||||
}
|
||||
if (cfg.blockTags[node.name]) node.name = 'div';
|
||||
else if (!cfg.trustTags[node.name]) node.name = 'span';
|
||||
// 处理列表
|
||||
if (node.c && (node.name == 'ul' || node.name == 'ol')) {
|
||||
if ((node.attrs.style || '').includes('list-style:none')) {
|
||||
for (let i = 0, child; child = node.children[i++];)
|
||||
if (child.name == 'li')
|
||||
child.name = 'div';
|
||||
} else if (node.name == 'ul') {
|
||||
var floor = 1;
|
||||
for (let i = this.STACK.length; i--;)
|
||||
if (this.STACK[i].name == 'ul') floor++;
|
||||
if (floor != 1)
|
||||
for (let i = node.children.length; i--;)
|
||||
node.children[i].floor = floor;
|
||||
} else {
|
||||
for (let i = 0, num = 1, child; child = node.children[i++];)
|
||||
if (child.name == 'li') {
|
||||
child.type = 'ol';
|
||||
child.num = ((num, type) => {
|
||||
if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
|
||||
if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
|
||||
if (type == 'i' || type == 'I') {
|
||||
num = (num - 1) % 99 + 1;
|
||||
var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
|
||||
ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
|
||||
res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
|
||||
if (type == 'i') return res.toLowerCase();
|
||||
return res;
|
||||
}
|
||||
return num;
|
||||
})(num++, attrs.type) + '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理表格的边框
|
||||
if (node.name == 'table') {
|
||||
var padding = attrs.cellpadding,
|
||||
spacing = attrs.cellspacing,
|
||||
border = attrs.border;
|
||||
if (node.c) {
|
||||
this.bubble();
|
||||
attrs.style = (attrs.style || '') + ';display:table';
|
||||
if (!padding) padding = 2;
|
||||
if (!spacing) spacing = 2;
|
||||
}
|
||||
if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
|
||||
if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
|
||||
if (border || padding || node.c)
|
||||
(function f(ns) {
|
||||
for (var i = 0, n; n = ns[i]; i++) {
|
||||
if(n.type == 'text') continue;
|
||||
var style = n.attrs.style || '';
|
||||
if (node.c && n.name[0] == 't') {
|
||||
n.c = 1;
|
||||
style += ';display:table-' + (n.name == 'th' || n.name == 'td' ? 'cell' : (n.name == 'tr' ? 'row' : 'row-group'));
|
||||
}
|
||||
if (n.name == 'th' || n.name == 'td') {
|
||||
if (border) style = `border:${border}px solid gray;${style}`;
|
||||
if (padding) style = `padding:${padding}px;${style}`;
|
||||
} else f(n.children || []);
|
||||
if (style) n.attrs.style = style;
|
||||
}
|
||||
})(node.children)
|
||||
}
|
||||
this.CssHandler.pop && this.CssHandler.pop(node);
|
||||
// 自动压缩
|
||||
if (node.name == 'div' && !Object.keys(attrs).length) {
|
||||
var siblings = this.siblings();
|
||||
if (node.children.length == 1 && node.children[0].name == 'div')
|
||||
siblings[siblings.length - 1] = node.children[0];
|
||||
}
|
||||
}
|
||||
// 工具函数
|
||||
bubble() {
|
||||
for (var i = this.STACK.length, item; item = this.STACK[--i];) {
|
||||
if (cfg.richOnlyTags[item.name]) {
|
||||
if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
|
||||
return false;
|
||||
}
|
||||
item.c = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
decode(val, amp) {
|
||||
var i = -1,
|
||||
j, en;
|
||||
while (1) {
|
||||
if ((i = val.indexOf('&', i + 1)) == -1) break;
|
||||
if ((j = val.indexOf(';', i + 2)) == -1) break;
|
||||
if (val[i + 1] == '#') {
|
||||
en = parseInt((val[i + 2] == 'x' ? '0' : '') + val.substring(i + 2, j));
|
||||
if (!isNaN(en)) val = val.substr(0, i) + String.fromCharCode(en) + val.substr(j + 1);
|
||||
} else {
|
||||
en = val.substring(i + 1, j);
|
||||
if (cfg.entities[en] || en == amp)
|
||||
val = val.substr(0, i) + (cfg.entities[en] || '&') + val.substr(j + 1);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
getUrl(url) {
|
||||
if (url[0] == '/') {
|
||||
if (url[1] == '/') url = this.protocol + ':' + url;
|
||||
else if (this.domain) url = this.domain + url;
|
||||
} else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
|
||||
url = this.domain + '/' + url;
|
||||
return url;
|
||||
}
|
||||
getName = val => this.xml ? val : val.toLowerCase();
|
||||
isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
|
||||
section = () => this.data.substring(this.start, this.i);
|
||||
parent = () => this.STACK[this.STACK.length - 1];
|
||||
siblings = () => this.STACK.length ? this.parent().children : this.DOM;
|
||||
// 状态机
|
||||
Text(c) {
|
||||
if (c == '<') {
|
||||
var next = this.data[this.i + 1],
|
||||
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
if (isLetter(next)) {
|
||||
this.setText();
|
||||
this.start = this.i + 1;
|
||||
this.state = this.TagName;
|
||||
} else if (next == '/') {
|
||||
this.setText();
|
||||
if (isLetter(this.data[++this.i + 1])) {
|
||||
this.start = this.i + 1;
|
||||
this.state = this.EndTag;
|
||||
} else this.Comment();
|
||||
} else if (next == '!') {
|
||||
this.setText();
|
||||
this.Comment();
|
||||
}
|
||||
}
|
||||
}
|
||||
Comment() {
|
||||
var key;
|
||||
if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
|
||||
else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
|
||||
else key = '>';
|
||||
if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
|
||||
else this.i += key.length - 1;
|
||||
this.start = this.i + 1;
|
||||
this.state = this.Text;
|
||||
}
|
||||
TagName(c) {
|
||||
if (blankChar[c]) {
|
||||
this.tagName = this.section();
|
||||
while (blankChar[this.data[this.i]]) this.i++;
|
||||
if (this.isClose()) this.setNode();
|
||||
else {
|
||||
this.start = this.i;
|
||||
this.state = this.AttrName;
|
||||
}
|
||||
} else if (this.isClose()) {
|
||||
this.tagName = this.section();
|
||||
this.setNode();
|
||||
}
|
||||
}
|
||||
AttrName(c) {
|
||||
var blank = blankChar[c];
|
||||
if (blank) {
|
||||
this.attrName = this.section();
|
||||
c = this.data[this.i];
|
||||
}
|
||||
if (c == '=') {
|
||||
if (!blank) this.attrName = this.section();
|
||||
while (blankChar[this.data[++this.i]]);
|
||||
this.start = this.i--;
|
||||
this.state = this.AttrValue;
|
||||
} else if (blank) this.setAttr();
|
||||
else if (this.isClose()) {
|
||||
this.attrName = this.section();
|
||||
this.setAttr();
|
||||
}
|
||||
}
|
||||
AttrValue(c) {
|
||||
if (c == '"' || c == "'") {
|
||||
this.start++;
|
||||
if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
|
||||
this.attrVal = this.section();
|
||||
this.i++;
|
||||
} else {
|
||||
for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
|
||||
this.attrVal = this.section();
|
||||
}
|
||||
this.setAttr();
|
||||
}
|
||||
EndTag(c) {
|
||||
if (blankChar[c] || c == '>' || c == '/') {
|
||||
var name = this.getName(this.section());
|
||||
for (var i = this.STACK.length; i--;)
|
||||
if (this.STACK[i].name == name) break;
|
||||
if (i != -1) {
|
||||
var node;
|
||||
while ((node = this.STACK.pop()).name != name);
|
||||
this.popNode(node);
|
||||
} else if (name == 'p' || name == 'br')
|
||||
this.siblings().push({
|
||||
name,
|
||||
attrs: {}
|
||||
});
|
||||
this.i = this.data.indexOf('>', this.i);
|
||||
this.start = this.i + 1;
|
||||
if (this.i == -1) this.i = this.data.length;
|
||||
else this.state = this.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = MpHtmlParser;
|
|
@ -0,0 +1,102 @@
|
|||
/* 配置文件 */
|
||||
const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
|
||||
const Prism = require('./prism.js');
|
||||
module.exports = {
|
||||
// 过滤器函数
|
||||
filter(node, cxt) {
|
||||
if (node.name == 'pre')
|
||||
cxt.bubble(); // 使得 pre 不被 rich-text 包含(为实现长按复制)
|
||||
},
|
||||
// 代码高亮函数
|
||||
highlight(content, attrs) {
|
||||
var info = content.match(/<code.*?language-([a-z-]+).*?>([\s\S]+)<\/code.*?>/m);
|
||||
if (!info) return content;
|
||||
var lan = info[1];
|
||||
content = info[2].replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
||||
attrs['data-content'] = content; // 记录原始内容,长按复制时使用
|
||||
switch (lan) {
|
||||
case 'js':
|
||||
case 'javascript':
|
||||
content = Prism.highlight(content, Prism.languages.javascript, 'javascript');
|
||||
break;
|
||||
case 'html':
|
||||
case 'html-editor':
|
||||
case 'wxml':
|
||||
case 'vue':
|
||||
content = Prism.highlight(content, Prism.languages.html, 'html');
|
||||
break;
|
||||
case 'json':
|
||||
content = Prism.highlight(content, Prism.languages.json, 'json');
|
||||
break;
|
||||
case 'md':
|
||||
case 'md-editor':
|
||||
case 'markdown':
|
||||
content = Prism.highlight(content, Prism.languages.markdown, 'markdown');
|
||||
break;
|
||||
case 'c':
|
||||
case 'cpp':
|
||||
content = Prism.highlight(content, Prism.languages.clike, 'clike');
|
||||
}
|
||||
// 增加语言显示
|
||||
if (!lan.includes('editor'))
|
||||
content = `<span style="position:absolute;top:3px;right:8px;font-size:.6rem;color:#808080;font-weight:bold">${lan}</span><div style='overflow:auto;max-width:100%;padding-bottom:1em'>${content}</div>`;
|
||||
return content;
|
||||
},
|
||||
// 文本处理函数
|
||||
onText: null,
|
||||
// 实体编码列表
|
||||
entities: {
|
||||
quot: '"',
|
||||
apos: "'",
|
||||
semi: ';',
|
||||
nbsp: '\xA0',
|
||||
ndash: '–',
|
||||
mdash: '—',
|
||||
middot: '·',
|
||||
lsquo: '‘',
|
||||
rsquo: '’',
|
||||
ldquo: '“',
|
||||
rdquo: '”',
|
||||
bull: '•',
|
||||
hellip: '…'
|
||||
},
|
||||
blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
|
||||
inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong'),
|
||||
// 块级标签,将被转为 div
|
||||
blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (canIUse ? '' : ',pre')),
|
||||
// 将被移除的标签
|
||||
ignoreTags: makeMap('area,base,basefont,canvas,command,embed,frame,iframe,input,isindex,keygen,link,map,meta,param,script,source,style,svg,textarea,title,track,use,wbr' + (canIUse ? ',rp' : '')),
|
||||
// 只能被 rich-text 显示的标签
|
||||
richOnlyTags: makeMap('a,colgroup,fieldset,legend,picture,table' + (canIUse ? ',bdi,bdo,rt,ruby' : '')),
|
||||
// 自闭合的标签
|
||||
selfClosingTags: makeMap('area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
|
||||
// 信任的属性
|
||||
trustAttrs: makeMap('align,alt,app-id,author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,media,muted,name,path,poster,rowspan,size,span,src,start,style,type,unit-id,width,xmlns'),
|
||||
// bool 型的属性
|
||||
boolAttrs: makeMap('autoplay,controls,ignore,loop,muted'),
|
||||
// 信任的标签
|
||||
trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video' + (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')),
|
||||
// 默认的标签样式
|
||||
userAgentStyles: {
|
||||
address: 'font-style:italic',
|
||||
big: 'display:inline;font-size:1.2em',
|
||||
blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
|
||||
caption: 'display:table-caption;text-align:center',
|
||||
center: 'text-align:center',
|
||||
cite: 'font-style:italic',
|
||||
dd: 'margin-left:40px',
|
||||
mark: 'background-color:yellow',
|
||||
pre: 'font-family:monospace;white-space:pre;overflow:scroll',
|
||||
s: 'text-decoration:line-through',
|
||||
small: 'display:inline;font-size:0.8em',
|
||||
u: 'text-decoration:underline'
|
||||
}
|
||||
}
|
||||
|
||||
function makeMap(str) {
|
||||
var map = {},
|
||||
list = str.split(',');
|
||||
for (var i = list.length; i--;)
|
||||
map[list[i]] = true;
|
||||
return map;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
emoji 扩展包
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
*/
|
||||
const reg = /\[(\S+?)\]/g;
|
||||
const data = {
|
||||
'笑脸': '😄',
|
||||
'生病': '😷',
|
||||
'破涕为笑': '😂',
|
||||
'吐舌': '😝',
|
||||
'脸红': '😳',
|
||||
'恐惧': '😱',
|
||||
'失望': '😔',
|
||||
'无语': '😒',
|
||||
'眨眼': '😉',
|
||||
'酷': '😎',
|
||||
'哭': '😭',
|
||||
'痴迷': '😍',
|
||||
'吻': '😘',
|
||||
'思考': '🤔',
|
||||
'困惑': '😕',
|
||||
'颠倒': '🙃',
|
||||
'钱': '🤑',
|
||||
'惊讶': '😲',
|
||||
'白眼': '🙄',
|
||||
'叹气': '😤',
|
||||
'睡觉': '😴',
|
||||
'书呆子': '🤓',
|
||||
'愤怒': '😡',
|
||||
'面无表情': '😑',
|
||||
'张嘴': '😮',
|
||||
'量体温': '🤒',
|
||||
'呕吐': '🤮',
|
||||
'光环': '😇',
|
||||
'幽灵': '👻',
|
||||
'外星人': '👽',
|
||||
'机器人': '🤖',
|
||||
'捂眼镜': '🙈',
|
||||
'捂耳朵': '🙉',
|
||||
'捂嘴': '🙊',
|
||||
'婴儿': '👶',
|
||||
'男孩': '👦',
|
||||
'女孩': '👧',
|
||||
'男人': '👨',
|
||||
'女人': '👩',
|
||||
'老人': '👴',
|
||||
'老妇人': '👵',
|
||||
'警察': '👮',
|
||||
'王子': '🤴',
|
||||
'公主': '🤴',
|
||||
'举手': '🙋',
|
||||
'跑步': '🏃',
|
||||
'家庭': '👪',
|
||||
'眼睛': '👀',
|
||||
'鼻子': '👃',
|
||||
'耳朵': '👂',
|
||||
'舌头': '👅',
|
||||
'嘴': '👄',
|
||||
'心': '❤️',
|
||||
'心碎': '💔',
|
||||
'雪人': '☃️',
|
||||
'情书': '💌',
|
||||
'大便': '💩',
|
||||
'闹钟': '⏰',
|
||||
'眼镜': '👓',
|
||||
'雨伞': '☂️',
|
||||
'音乐': '🎵',
|
||||
'话筒': '🎤',
|
||||
'游戏机': '🎮',
|
||||
'喇叭': '📢',
|
||||
'耳机': '🎧',
|
||||
'礼物': '🎁',
|
||||
'电话': '📞',
|
||||
'电脑': '💻',
|
||||
'打印机': '🖨️',
|
||||
'手电筒': '🔦',
|
||||
'灯泡': '💡',
|
||||
'书本': '📖',
|
||||
'信封': '✉️',
|
||||
'药丸': '💊',
|
||||
'口红': '💄',
|
||||
'手机': '📱',
|
||||
'相机': '📷',
|
||||
'电视': '📺',
|
||||
'中': '🀄',
|
||||
'垃圾桶': '🚮',
|
||||
'厕所': '🚾',
|
||||
'感叹号': '❗',
|
||||
'禁': '🈲',
|
||||
'可': '🉑',
|
||||
'彩虹': '🌈',
|
||||
'旋风': '🌀',
|
||||
'雷电': '⚡',
|
||||
'雪花': '❄️',
|
||||
'星星': '⭐',
|
||||
'水滴': '💧',
|
||||
'玫瑰': '🌹',
|
||||
'加油': '💪',
|
||||
'左': '👈',
|
||||
'右': '👉',
|
||||
'上': '👆',
|
||||
'下': '👇',
|
||||
'手掌': '🖐️',
|
||||
'好的': '👌',
|
||||
'好': '👍',
|
||||
'差': '👎',
|
||||
'胜利': '✌',
|
||||
'拳头': '👊',
|
||||
'挥手': '👋',
|
||||
'鼓掌': '👏',
|
||||
'猴子': '🐒',
|
||||
'狗': '🐶',
|
||||
'狼': '🐺',
|
||||
'猫': '🐱',
|
||||
'老虎': '🐯',
|
||||
'马': '🐎',
|
||||
'独角兽': '🦄',
|
||||
'斑马': '🦓',
|
||||
'鹿': '🦌',
|
||||
'牛': '🐮',
|
||||
'猪': '🐷',
|
||||
'羊': '🐏',
|
||||
'长颈鹿': '🦒',
|
||||
'大象': '🐘',
|
||||
'老鼠': '🐭',
|
||||
'蝙蝠': '🦇',
|
||||
'刺猬': '🦔',
|
||||
'熊猫': '🐼',
|
||||
'鸽子': '🕊️',
|
||||
'鸭子': '🦆',
|
||||
'兔子': '🐇',
|
||||
'老鹰': '🦅',
|
||||
'青蛙': '🐸',
|
||||
'蛇': '🐍',
|
||||
'龙': '🐉',
|
||||
'鲸鱼': '🐳',
|
||||
'海豚': '🐬',
|
||||
'足球': '⚽',
|
||||
'棒球': '⚾',
|
||||
'篮球': '🏀',
|
||||
'排球': '🏐',
|
||||
'橄榄球': '🏉',
|
||||
'网球': '🎾',
|
||||
'骰子': '🎲',
|
||||
'鸡腿': '🍗',
|
||||
'蛋糕': '🎂',
|
||||
'啤酒': '🍺',
|
||||
'饺子': '🥟',
|
||||
'汉堡': '🍔',
|
||||
'薯条': '🍟',
|
||||
'意大利面': '🍝',
|
||||
'干杯': '🥂',
|
||||
'筷子': '🥢',
|
||||
'糖果': '🍬',
|
||||
'奶瓶': '🍼',
|
||||
'爆米花': '🍿',
|
||||
'邮局': '🏤',
|
||||
'医院': '🏥',
|
||||
'银行': '🏦',
|
||||
'酒店': '🏨',
|
||||
'学校': '🏫',
|
||||
'城堡': '🏰',
|
||||
'火车': '🚂',
|
||||
'高铁': '🚄',
|
||||
'地铁': '🚇',
|
||||
'公交': '🚌',
|
||||
'救护车': '🚑',
|
||||
'消防车': '🚒',
|
||||
'警车': '🚓',
|
||||
'出租车': '🚕',
|
||||
'汽车': '🚗',
|
||||
'货车': '🚛',
|
||||
'自行车': '🚲',
|
||||
'摩托': '🛵',
|
||||
'红绿灯': '🚥',
|
||||
'帆船': '⛵',
|
||||
'游轮': '🛳️',
|
||||
'轮船': '⛴️',
|
||||
'飞机': '✈️',
|
||||
'直升机': '🚁',
|
||||
'缆车': '🚠',
|
||||
'警告': '⚠️',
|
||||
'禁止': '⛔'
|
||||
}
|
||||
module.exports = {
|
||||
parseEmoji: (text) => text.replace(reg, ($, $1) => {
|
||||
if (data[$1]) {
|
||||
if (data[$1].includes('http'))
|
||||
return `<img src="${data[$1]}" style="width:16px;height:16px;display:inline-block" ignore>`;
|
||||
else return data[$1];
|
||||
} else return $;
|
||||
}),
|
||||
getEmoji: (key) => data[key],
|
||||
setEmoji: (key, emoji) => data[key] = emoji,
|
||||
removeEmoji: (key) => data[key] = void 0
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,81 @@
|
|||
/* PrismJS 1.19.0
|
||||
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
|
||||
* Based on https://github.com/chriskempson/tomorrow-theme
|
||||
* @author Rose Pritchard
|
||||
*/
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.attr-name,
|
||||
.token.namespace,
|
||||
.token.deleted {
|
||||
color: #e2777a;
|
||||
}
|
||||
|
||||
.token.function-name {
|
||||
color: #6196cc;
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.class-name,
|
||||
.token.constant,
|
||||
.token.symbol {
|
||||
color: #f8c555;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.important,
|
||||
.token.atrule,
|
||||
.token.keyword,
|
||||
.token.builtin {
|
||||
color: #cc99cd;
|
||||
}
|
||||
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.attr-value,
|
||||
.token.regex,
|
||||
.token.variable {
|
||||
color: #7ec699;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url {
|
||||
color: #67cdcc;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.token.inserted {
|
||||
color: green;
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
parser 主组件
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/04/25
|
||||
*/
|
||||
var cache = {},
|
||||
Parser = require('./libs/MpHtmlParser.js'),
|
||||
fs = wx.getFileSystemManager && wx.getFileSystemManager();
|
||||
try {
|
||||
var dom = require('./libs/document.js');
|
||||
} catch (e) {}
|
||||
// 计算 cache 的 key
|
||||
function hash(str) {
|
||||
for (var i = str.length, val = 5381; i--;)
|
||||
val += (val << 5) + str.charCodeAt(i);
|
||||
return val;
|
||||
}
|
||||
Component({
|
||||
options: {
|
||||
pureDataPattern: /^[acdgtux]|W/
|
||||
},
|
||||
properties: {
|
||||
'html': {
|
||||
type: null,
|
||||
observer(html) {
|
||||
if (this._refresh) this._refresh = false;
|
||||
else this.setContent(html, false, true);
|
||||
}
|
||||
},
|
||||
'autosetTitle': {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
'autopause': {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
'compress': Number,
|
||||
'domain': String,
|
||||
'gestureZoom': Boolean,
|
||||
'lazyLoad': Boolean,
|
||||
'selectable': Boolean,
|
||||
'tagStyle': Object,
|
||||
'showWithAnimation': Boolean,
|
||||
'useAnchor': Boolean,
|
||||
'useCache': Boolean,
|
||||
'xml': Boolean
|
||||
},
|
||||
relations: {
|
||||
'../parser-group/parser-group': {
|
||||
type: 'ancestor'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 图片数组
|
||||
this.imgList = [];
|
||||
this.imgList.setItem = function (i, src) {
|
||||
if (!i || !src) return;
|
||||
// 去重
|
||||
if (src.indexOf('http') == 0 && this.includes(src)) {
|
||||
var newSrc = '';
|
||||
for (var j = 0, c; c = src[j]; j++) {
|
||||
if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
|
||||
newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
|
||||
}
|
||||
newSrc += src.substr(j);
|
||||
return this[i] = newSrc;
|
||||
}
|
||||
this[i] = src;
|
||||
// 暂存 data src
|
||||
if (src.includes('data:image')) {
|
||||
var info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
|
||||
if (!info) return;
|
||||
var filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
|
||||
fs && fs.writeFile({
|
||||
filePath,
|
||||
data: info[3],
|
||||
encoding: info[2],
|
||||
success: () => this[i] = filePath
|
||||
})
|
||||
}
|
||||
}
|
||||
this.imgList.each = function (f) {
|
||||
for (var i = 0, len = this.length; i < len; i++)
|
||||
this.setItem(i, f(this[i], i, this));
|
||||
}
|
||||
if (dom) this.document = new dom(this);
|
||||
},
|
||||
detached() {
|
||||
// 删除暂存
|
||||
this.imgList.each(src => {
|
||||
if (src && src.includes(wx.env.USER_DATA_PATH) && fs)
|
||||
fs.unlink({
|
||||
filePath: src
|
||||
})
|
||||
})
|
||||
clearInterval(this._timer);
|
||||
},
|
||||
methods: {
|
||||
// 锚点跳转
|
||||
navigateTo(obj) {
|
||||
if (!this.data.useAnchor)
|
||||
return obj.fail && obj.fail({
|
||||
errMsg: 'Anchor is disabled'
|
||||
})
|
||||
this.createSelectorQuery()
|
||||
.select('.top' + (obj.id ? '>>>#' + obj.id : '')).boundingClientRect()
|
||||
.selectViewport().scrollOffset().exec(res => {
|
||||
if (!res[0])
|
||||
return this.group ? this.group.navigateTo(this.i, obj) :
|
||||
obj.fail && obj.fail({
|
||||
errMsg: 'Label not found'
|
||||
});
|
||||
obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
|
||||
wx.pageScrollTo(obj);
|
||||
})
|
||||
},
|
||||
// 获取文本
|
||||
getText(ns = this.data.html) {
|
||||
var txt = '';
|
||||
for (var i = 0, n; n = ns[i++];) {
|
||||
if (n.type == 'text') txt += n.text.replace(/ /g, '\u00A0').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
||||
else if (n.type == 'br') txt += '\n';
|
||||
else {
|
||||
// 块级标签前后加换行
|
||||
var br = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] > '0' && n.name[1] < '7');
|
||||
if (br && txt && txt[txt.length - 1] != '\n') txt += '\n';
|
||||
if (n.children) txt += this.getText(n.children);
|
||||
if (br && txt[txt.length - 1] != '\n') txt += '\n';
|
||||
else if (n.name == 'td' || n.name == 'th') txt += '\t';
|
||||
}
|
||||
}
|
||||
return txt;
|
||||
},
|
||||
// 获取视频 context
|
||||
getVideoContext(id) {
|
||||
if (!id) return this.videoContexts;
|
||||
for (var i = this.videoContexts.length; i--;)
|
||||
if (this.videoContexts[i].id == id) return this.videoContexts[i];
|
||||
},
|
||||
// 渲染富文本
|
||||
setContent(html, append, _watch) {
|
||||
var data = {};
|
||||
if (!html) {
|
||||
if (_watch || append) return;
|
||||
data.html = '';
|
||||
} else if (typeof html == 'string') {
|
||||
let parser = new Parser(html, this.data);
|
||||
// 缓存读取
|
||||
if (this.data.useCache) {
|
||||
var hashVal = hash(html);
|
||||
if (cache[hashVal]) data.html = cache[hashVal];
|
||||
else {
|
||||
data.html = parser.parse();
|
||||
cache[hashVal] = data.html;
|
||||
}
|
||||
} else data.html = parser.parse();
|
||||
this._refresh = true;
|
||||
this.triggerEvent('parse', data.html);
|
||||
} else if (html.constructor == Array) {
|
||||
// 转换不符合格式的 array
|
||||
if (html.length && html[0].PoweredBy != 'Parser') {
|
||||
let parser = new Parser('', this.data);
|
||||
(function f(ns) {
|
||||
for (var i = 0, n; n = ns[i]; i++) {
|
||||
if (n.type == 'text') continue;
|
||||
n.attrs = n.attrs || {};
|
||||
for (var key in n.attrs)
|
||||
if (typeof n.attrs[key] != 'string') n.attrs[key] = n.attrs[key].toString();
|
||||
parser.matchAttr(n);
|
||||
if (n.children) {
|
||||
parser.STACK.push(n);
|
||||
f(n.children);
|
||||
parser.popNode(parser.STACK.pop());
|
||||
}
|
||||
}
|
||||
})(html);
|
||||
data.html = html;
|
||||
}
|
||||
if (!_watch) data.html = html;
|
||||
} else if (typeof html == 'object' && html.nodes) {
|
||||
data.html = html.nodes;
|
||||
console.warn('错误的 html 类型:object 类型已废弃');
|
||||
} else
|
||||
return console.warn('错误的 html 类型:' + typeof html);
|
||||
if (append) {
|
||||
this._refresh = true;
|
||||
data.html = (this.data.html || []).concat(data.html);
|
||||
} else if (this.data.showWithAnimation) data.showAm = 'animation: show .5s';
|
||||
if (data.html || data.showAm) this.setData(data);
|
||||
// 设置标题
|
||||
if (this.data.html.length && this.data.html[0].title && this.data.autosetTitle)
|
||||
wx.setNavigationBarTitle({
|
||||
title: this.data.html[0].title
|
||||
})
|
||||
this.imgList.length = 0;
|
||||
this.videoContexts = [];
|
||||
var ns = this.selectAllComponents('.top,.top>>>._node');
|
||||
for (let i = 0, n; n = ns[i++];) {
|
||||
n.top = this;
|
||||
for (var j = 0, item; item = n.data.nodes[j++];) {
|
||||
if (item.c) continue;
|
||||
// 获取图片列表
|
||||
if (item.name == 'img')
|
||||
this.imgList.setItem(item.attrs.i, item.attrs.src);
|
||||
// 音视频控制
|
||||
else if (item.name == 'video' || item.name == 'audio') {
|
||||
var ctx;
|
||||
if (item.name == 'video') ctx = wx.createVideoContext(item.attrs.id, n);
|
||||
else ctx = n.selectComponent('#' + item.attrs.id);
|
||||
if (ctx) {
|
||||
ctx.id = item.attrs.id;
|
||||
this.videoContexts.push(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(wx.nextTick || setTimeout)(() => this.triggerEvent('load'), 50);
|
||||
var height;
|
||||
clearInterval(this._timer);
|
||||
this._timer = setInterval(() => {
|
||||
this.createSelectorQuery().select('.top').boundingClientRect(res => {
|
||||
this.rect = res;
|
||||
if (res.height == height) {
|
||||
this.triggerEvent('ready', res)
|
||||
clearInterval(this._timer);
|
||||
}
|
||||
height = res.height;
|
||||
}).exec();
|
||||
}, 350)
|
||||
},
|
||||
// 预加载
|
||||
preLoad(html, num) {
|
||||
if (typeof html == 'string') {
|
||||
var id = hash(html);
|
||||
html = new Parser(html, this.data).parse();
|
||||
cache[id] = html;
|
||||
}
|
||||
var imgs, wait = [];
|
||||
(function f(ns) {
|
||||
for (var i = 0, n; n = ns[i++];) {
|
||||
if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
|
||||
wait.push(n.attrs.src);
|
||||
f(n.children || []);
|
||||
}
|
||||
})(html);
|
||||
if (num) wait = wait.slice(0, num);
|
||||
this._wait = (this._wait || []).concat(wait);
|
||||
if (!this.data.imgs) imgs = this._wait.splice(0, 15);
|
||||
else if (this.data.imgs.length < 15)
|
||||
imgs = this.data.imgs.concat(this._wait.splice(0, 15 - this.data.imgs.length));
|
||||
imgs && this.setData({
|
||||
imgs
|
||||
});
|
||||
},
|
||||
_load(e) {
|
||||
if (this._wait.length)
|
||||
this.setData({
|
||||
[`imgs[${e.target.id}]`]: this._wait.shift()
|
||||
})
|
||||
},
|
||||
// 事件处理
|
||||
_tap(e) {
|
||||
if (this.data.gestureZoom && e.timeStamp - this._lastT < 300) {
|
||||
var initY = e.detail.y - e.currentTarget.offsetTop;
|
||||
if (this._zoom) {
|
||||
this._scaleAm.translateX(0).scale(1).step();
|
||||
wx.pageScrollTo({
|
||||
scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
|
||||
duration: 400
|
||||
})
|
||||
} else {
|
||||
var initX = e.detail.x - e.currentTarget.offsetLeft;
|
||||
this._initY = initY;
|
||||
this._scaleAm = wx.createAnimation({
|
||||
transformOrigin: `${initX}px ${this._initY}px 0`,
|
||||
timingFunction: 'ease-in-out'
|
||||
});
|
||||
this._scaleAm.scale(2).step();
|
||||
this._tMax = initX / 2;
|
||||
this._tMin = (initX - this.rect.width) / 2;
|
||||
this._tX = 0;
|
||||
}
|
||||
this._zoom = !this._zoom;
|
||||
this.setData({
|
||||
scaleAm: this._scaleAm.export()
|
||||
})
|
||||
}
|
||||
this._lastT = e.timeStamp;
|
||||
},
|
||||
_touchstart(e) {
|
||||
if (e.touches.length == 1)
|
||||
this._initX = this._lastX = e.touches[0].pageX;
|
||||
},
|
||||
_touchmove(e) {
|
||||
var diff = e.touches[0].pageX - this._lastX;
|
||||
if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
|
||||
this._lastX = e.touches[0].pageX;
|
||||
if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0)) return;
|
||||
this._tX += diff * Math.abs(this._lastX - this._initX) * 0.05;
|
||||
if (this._tX < this._tMin) this._tX = this._tMin;
|
||||
if (this._tX > this._tMax) this._tX = this._tMax;
|
||||
this._scaleAm.translateX(this._tX).step();
|
||||
this.setData({
|
||||
scaleAm: this._scaleAm.export()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"trees": "./trees/trees"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<!--parser 主组件-->
|
||||
<slot wx:if="{{!html[0].name&&!html[0].type}}" />
|
||||
<trees class="top" style="{{selectable?'user-select:text;-webkit-user-select:text;':''}}{{showAm}}" animation="{{scaleAm}}" lazy-load="{{lazyLoad}}" nodes="{{html[0].name||html[0].type?html:[]}}" bindtap="_tap" bindtouchstart="_touchstart" bindtouchmove="_touchmove" />
|
||||
<image wx:for="{{imgs}}" wx:key="index" id="{{index}}" src="{{item}}" hidden bindload="_load" />
|
|
@ -0,0 +1,16 @@
|
|||
:host {
|
||||
display: block;
|
||||
overflow: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.top {
|
||||
display: inherit;
|
||||
}
|
||||
@keyframes show {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
trees 递归子组件
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/04/25
|
||||
*/
|
||||
Component({
|
||||
data: {
|
||||
canIUse: !!wx.chooseMessageFile,
|
||||
placeholder: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='225'/>",
|
||||
inlineTags: require('../libs/config.js').inlineTags
|
||||
},
|
||||
properties: {
|
||||
nodes: Array,
|
||||
lazyLoad: Boolean
|
||||
},
|
||||
methods: {
|
||||
// 自定义事件
|
||||
copyCode(e) {
|
||||
wx.showActionSheet({
|
||||
itemList: ['复制代码'],
|
||||
success: () =>
|
||||
wx.setClipboardData({
|
||||
data: e.target.dataset.content
|
||||
})
|
||||
})
|
||||
},
|
||||
// 视频播放事件
|
||||
play(e) {
|
||||
this.top.group && this.top.group.pause(this.top.i);
|
||||
if (this.top.videoContexts.length > 1 && this.top.data.autopause)
|
||||
for (var i = this.top.videoContexts.length; i--;)
|
||||
if (this.top.videoContexts[i].id != e.currentTarget.id)
|
||||
this.top.videoContexts[i].pause();
|
||||
},
|
||||
// 图片事件
|
||||
imgtap(e) {
|
||||
var attrs = e.currentTarget.dataset.attrs;
|
||||
if (!attrs.ignore) {
|
||||
var preview = true;
|
||||
this.top.triggerEvent('imgtap', {
|
||||
id: e.currentTarget.id,
|
||||
src: attrs.src,
|
||||
ignore: () => preview = false
|
||||
})
|
||||
if (preview) {
|
||||
if (this.top.group) return this.top.group.preview(this.top.i, attrs.i);
|
||||
var urls = this.top.imgList,
|
||||
current = urls[attrs.i] ? urls[attrs.i] : (urls = [attrs.src], attrs.src);
|
||||
wx.previewImage({
|
||||
current,
|
||||
urls
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
loadImg(e) {
|
||||
var i = e.target.dataset.i;
|
||||
if (this.data.lazyLoad && !this.data.nodes[i].load)
|
||||
this.setData({
|
||||
[`nodes[${i}].load`]: true
|
||||
})
|
||||
},
|
||||
// 链接点击事件
|
||||
linkpress(e) {
|
||||
var jump = true,
|
||||
attrs = e.currentTarget.dataset.attrs;
|
||||
attrs.ignore = () => jump = false;
|
||||
this.top.triggerEvent('linkpress', attrs);
|
||||
if (jump) {
|
||||
if (attrs['app-id'])
|
||||
wx.navigateToMiniProgram({
|
||||
appId: attrs['app-id'],
|
||||
path: attrs.path
|
||||
})
|
||||
else if (attrs.href) {
|
||||
if (attrs.href[0] == '#')
|
||||
this.top.navigateTo({
|
||||
id: attrs.href.substring(1)
|
||||
})
|
||||
else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0)
|
||||
wx.setClipboardData({
|
||||
data: attrs.href,
|
||||
success: () =>
|
||||
wx.showToast({
|
||||
title: '链接已复制'
|
||||
})
|
||||
})
|
||||
else
|
||||
wx.navigateTo({
|
||||
url: attrs.href,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 错误事件
|
||||
error(e) {
|
||||
var context, source = e.target.dataset.source,
|
||||
i = e.target.dataset.i,
|
||||
node = this.data.nodes[i];
|
||||
if (source == 'video' || source == 'audio') {
|
||||
// 加载其他 source
|
||||
var index = (node.i || 0) + 1;
|
||||
if (index < node.attrs.source.length)
|
||||
return this.setData({
|
||||
[`nodes[${i}].i`]: index
|
||||
})
|
||||
if (this.top) context = this.top.getVideoContext(e.target.id);
|
||||
} else if (source == 'img')
|
||||
context = {
|
||||
setSrc: src => {
|
||||
this.setData({
|
||||
[`nodes[${i}].attrs.src`]: src
|
||||
})
|
||||
}
|
||||
}
|
||||
this.top && this.top.triggerEvent('error', {
|
||||
source,
|
||||
target: e.target,
|
||||
context,
|
||||
...e.detail
|
||||
})
|
||||
},
|
||||
// 加载视频
|
||||
loadVideo(e) {
|
||||
var i = e.target.dataset.i;
|
||||
this.setData({
|
||||
[`nodes[${i}].lazyLoad`]: false,
|
||||
[`nodes[${i}].attrs.autoplay`]: true
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"trees": "./trees",
|
||||
"myAudio": "../audio/audio"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<!--trees 递归子组件-->
|
||||
<wxs module="handler">
|
||||
module.exports = {
|
||||
visited: function (e, owner) {
|
||||
if (!e.instance.hasClass('_visited'))
|
||||
e.instance.addClass('_visited')
|
||||
owner.callMethod('linkpress', e);
|
||||
},
|
||||
useRichText: function (item, inlineTags) {
|
||||
return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
|
||||
}
|
||||
}
|
||||
</wxs>
|
||||
<block wx:for="{{nodes}}" wx:key="index" wx:for-item="n">
|
||||
<!--图片-->
|
||||
<view wx:if="{{n.name=='img'}}" id="{{n.attrs.id}}" class="_img {{n.attrs.class}}" style="{{n.attrs.style}}" data-attrs="{{n.attrs}}" bindtap="imgtap">
|
||||
<rich-text nodes="{{[{attrs:{src:lazyLoad&&!n.load?placeholder:n.attrs.src,alt:n.attrs.alt||'',width:n.attrs.width||'',style:'max-width:100%;display:inherit'+(n.attrs.height?';height:'+n.attrs.height:'')},name:'img'}]}}" />
|
||||
<image class="_image" src="{{lazyLoad&&!n.load?placeholder:n.attrs.src}}" lazy-load="{{lazyLoad}}" show-menu-by-longpress="{{!n.attrs.ignore}}" data-i="{{index}}" data-source="img" bindload="loadImg" binderror="error" />
|
||||
</view>
|
||||
<!--文本-->
|
||||
<text wx:elif="{{n.type=='text'}}" decode>{{n.text}}</text>
|
||||
<text wx:elif="{{n.name=='br'}}">\n</text>
|
||||
<!--链接-->
|
||||
<view wx:elif="{{n.name=='a'}}" class="_a {{n.attrs.class}}" hover-class="_hover" style="{{n.attrs.style}}" data-attrs="{{n.attrs}}" bindtap="{{canIUse?handler.visited:'linkpress'}}">
|
||||
<trees nodes="{{n.children}}" />
|
||||
</view>
|
||||
<!--视频-->
|
||||
<block wx:elif="{{n.name=='video'}}">
|
||||
<view wx:if="{{n.lazyLoad}}" id="{{n.attrs.id}}" class="_video {{n.attrs.class}}" style="{{n.attrs.style}}" data-i="{{index}}" bindtap="loadVideo" />
|
||||
<video wx:else id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" autoplay="{{n.attrs.autoplay}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" muted="{{n.attrs.muted}}" poster="{{n.attrs.poster}}" src="{{n.attrs.source[n.i||0]}}" unit-id="{{n.attrs['unit-id']}}" data-i="{{index}}" data-source="video" binderror="error" bindplay="play" />
|
||||
</block>
|
||||
<!--音频-->
|
||||
<myAudio wx:elif="{{n.name=='audio'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" author="{{n.attrs.author}}" autoplay="{{n.attrs.autoplay}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" name="{{n.attrs.name}}" poster="{{n.attrs.poster}}" src="{{n.attrs.source[n.i||0]}}" data-i="{{index}}" data-source="audio" binderror="error" bindplay="play" />
|
||||
<!--广告-->
|
||||
<ad wx:elif="{{n.name=='ad'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" unit-id="{{n.attrs['unit-id']}}" data-source="ad" binderror="error" />
|
||||
<!--列表-->
|
||||
<view wx:elif="{{n.name=='li'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}};display:flex">
|
||||
<view wx:if="{{n.type=='ol'}}" class="_ol-bef">{{n.num}}</view>
|
||||
<view wx:else class="_ul-bef">
|
||||
<view wx:if="{{n.floor%3==0}}" class="_ul-p1">█</view>
|
||||
<view wx:elif="{{n.floor%3==2}}" class="_ul-p2" />
|
||||
<view wx:else class="_ul-p1" style="border-radius:50%">█</view>
|
||||
</view>
|
||||
<trees class="_node _li" lazy-load="{{lazyLoad}}" nodes="{{n.children}}" />
|
||||
</view>
|
||||
<!--自定义标签-->
|
||||
<rich-text wx:elif="{{n.name=='pre'}}" class="_p" nodes="{{[n]}}" data-content="{{n.attrs['data-content']}}" bindlongpress="copyCode" />
|
||||
<!--富文本-->
|
||||
<rich-text wx:elif="{{handler.useRichText(n, inlineTags)}}" id="{{n.attrs.id}}" class="_p __{{n.name}}" nodes="{{[n]}}" />
|
||||
<!--继续递归-->
|
||||
<trees wx:else id="{{n.attrs.id}}" class="_node _{{n.name}} {{n.attrs.class}}" style="{{n.attrs.style}}" lazy-load="{{lazyLoad}}" nodes="{{n.children}}" />
|
||||
</block>
|
|
@ -0,0 +1,136 @@
|
|||
/* 在这里引入自定义样式 */
|
||||
@import "../libs/prism.wxss";
|
||||
|
||||
/* 链接和图片效果 */
|
||||
._a {
|
||||
color: #366092;
|
||||
display: inline;
|
||||
padding: 1.5px 0 1.5px 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
._hover {
|
||||
opacity: 0.7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
._visited {
|
||||
color: #551a8b;
|
||||
}
|
||||
._img {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
/* 内部样式 */
|
||||
:host {
|
||||
display: inline;
|
||||
}
|
||||
._blockquote, ._div, ._p, ._ul, ._ol, ._li {
|
||||
display: block;
|
||||
}
|
||||
._b, ._strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
._code {
|
||||
font-family: monospace;
|
||||
}
|
||||
._del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
._em, ._i {
|
||||
font-style: italic;
|
||||
}
|
||||
._h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
._h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
._h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
._h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
._h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
._h1, ._h2, ._h3, ._h4, ._h5, ._h6 {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
._image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
._ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
._li {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
}
|
||||
._ol-bef {
|
||||
margin-right: 5px;
|
||||
text-align: right;
|
||||
width: 36px;
|
||||
}
|
||||
._ul-bef {
|
||||
line-height: normal;
|
||||
margin: 0 12px 0 23px;
|
||||
}
|
||||
._ol-bef, ._ul_bef {
|
||||
flex: none;
|
||||
user-select: none;
|
||||
}
|
||||
._ul-p1 {
|
||||
display: inline-block;
|
||||
height: 0.3em;
|
||||
line-height: 0.3em;
|
||||
overflow: hidden;
|
||||
width: 0.3em;
|
||||
}
|
||||
._ul-p2 {
|
||||
border: 0.05em solid black;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 0.23em;
|
||||
width: 0.23em;
|
||||
}
|
||||
._q::before {
|
||||
content: '"';
|
||||
}
|
||||
._q::after {
|
||||
content: '"';
|
||||
}
|
||||
._sub {
|
||||
font-size: smaller;
|
||||
vertical-align: sub;
|
||||
}
|
||||
._sup {
|
||||
font-size: smaller;
|
||||
vertical-align: super;
|
||||
}
|
||||
.__bdi, .__bdo, .__ruby, .__rt {
|
||||
display: inline-block;
|
||||
}
|
||||
._video {
|
||||
background-color: black;
|
||||
display: inline-block;
|
||||
height: 225px;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
._video::after {
|
||||
border-color: transparent transparent transparent white;
|
||||
border-style: solid;
|
||||
border-width: 15px 0 15px 30px;
|
||||
content: '';
|
||||
left: 50%;
|
||||
margin: -15px 0 0 -15px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
|
@ -6,7 +6,8 @@ Page({
|
|||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
elink_f:''
|
||||
elink_f:'',
|
||||
domain:getApp().globalData.mediahost
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -68,6 +69,7 @@ Page({
|
|||
getDetail: function (id) {
|
||||
var that = this
|
||||
api.request(`/cms/article/${id}/`,'GET').then(res => {
|
||||
// res.data.content = res.data.content.replace(/"media/g, '"'+getApp().globalData.mediahost + '/media').replace(/\<img/gi, '<img style="max-width:100%;height:auto" ')
|
||||
that.setData(res.data)
|
||||
})
|
||||
},
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{
|
||||
"usingComponents": {}
|
||||
"usingComponents": {
|
||||
"parser": "/components/parser/parser"
|
||||
}
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
<view class="page">
|
||||
<web-view src="elink" wx:if="{{elink}}"></web-view>
|
||||
<view class="page" wx:else>
|
||||
<view class="page__hd" style="padding:20px">
|
||||
<view class="page__title" >{{title}}</view>
|
||||
<view class="page__desc">
|
||||
<span style="font-weight:bold;color:darkblue;font-size:16px">{{ifrom}} </span>
|
||||
<text class="weui-badge" style="background:orange" wx:if="{{is_top}}">置顶</text>
|
||||
<text class="weui-badge" style="background:green" wx:if="{{!elink}}">原创</text>
|
||||
<text class="weui-badge" style="background:green" >原创</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="page__bd">
|
||||
<view class="weui-article">
|
||||
<rich-text nodes="{{content}}"></rich-text>
|
||||
<parser html="{{content}}" domain="{{domain}}"/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
|
@ -11,10 +11,7 @@
|
|||
<view class="weui-media-box__bd">
|
||||
<h4 class="weui-media-box__title">{{item.name}}</h4>
|
||||
<view class="weui-media-box__desc">
|
||||
<span style="color:orange;font-weight:bold">{{item.type}}</span>
|
||||
--共
|
||||
<span style="color:darkblue;font-weight:bold">{{item.tmtotal}}</span>
|
||||
道/已答
|
||||
已答
|
||||
<span style="color:darkblue;font-weight:bold">{{item.ydtmtotal}}</span>
|
||||
道
|
||||
</view>
|
||||
|
|
|
@ -32,7 +32,7 @@ class Consumer(SoftCommonModel):
|
|||
subjects = models.ManyToManyField(Questioncat, verbose_name='付费学科', through='PaySubject')
|
||||
workscope = models.ForeignKey(WorkScope, verbose_name='工作类别', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
collects = models.ManyToManyField(Question, verbose_name='收藏试题')
|
||||
remain_count = models.IntegerField('可考次数', default=1)
|
||||
remain_count = models.IntegerField('体验次数', default=3)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
|
Loading…
Reference in New Issue