diff --git a/test_client/src/views/crm/consumer.vue b/test_client/src/views/crm/consumer.vue index 33ca475..d3d0749 100644 --- a/test_client/src/views/crm/consumer.vue +++ b/test_client/src/views/crm/consumer.vue @@ -45,6 +45,9 @@ icon="el-icon-refresh-left" @click="resetFilter" >刷新重置 + + 创建时间 +
新增 @@ -115,7 +118,7 @@ {{ scope.row.workscope_name }} - + @@ -246,6 +249,8 @@ export default { }, data() { return { + tableKey: 0, + showCreate: false, uploadUrl: uploadUrl(), popovervisible: false, consumer: defaultConsumer, diff --git a/test_client/src/views/news/newsupdate.vue b/test_client/src/views/news/newsupdate.vue index 29620da..8347395 100644 --- a/test_client/src/views/news/newsupdate.vue +++ b/test_client/src/views/news/newsupdate.vue @@ -54,7 +54,7 @@ export default { { required: true, message: "请输入", trigger: "blur" } ], }, - is_show: false + is_show: true } }, created() { diff --git a/test_mini/components/parser/audio/audio.js b/test_mini/components/parser/audio/audio.js new file mode 100644 index 0000000..e50640d --- /dev/null +++ b/test_mini/components/parser/audio/audio.js @@ -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; + } + } +}) \ No newline at end of file diff --git a/test_mini/components/parser/audio/audio.json b/test_mini/components/parser/audio/audio.json new file mode 100644 index 0000000..32640e0 --- /dev/null +++ b/test_mini/components/parser/audio/audio.json @@ -0,0 +1,3 @@ +{ + "component": true +} \ No newline at end of file diff --git a/test_mini/components/parser/audio/audio.wxml b/test_mini/components/parser/audio/audio.wxml new file mode 100644 index 0000000..bd30fbd --- /dev/null +++ b/test_mini/components/parser/audio/audio.wxml @@ -0,0 +1,13 @@ + + + + + + + + + {{name||'未知音频'}} + {{author||'未知作者'}} + + {{time}} + \ No newline at end of file diff --git a/test_mini/components/parser/audio/audio.wxss b/test_mini/components/parser/audio/audio.wxss new file mode 100644 index 0000000..d193004 --- /dev/null +++ b/test_mini/components/parser/audio/audio.wxss @@ -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; +} \ No newline at end of file diff --git a/test_mini/components/parser/libs/CssHandler.js b/test_mini/components/parser/libs/CssHandler.js new file mode 100644 index 0000000..fd9c486 --- /dev/null +++ b/test_mini/components/parser/libs/CssHandler.js @@ -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; + } +} \ No newline at end of file diff --git a/test_mini/components/parser/libs/MpHtmlParser.js b/test_mini/components/parser/libs/MpHtmlParser.js new file mode 100644 index 0000000..6368a4f --- /dev/null +++ b/test_mini/components/parser/libs/MpHtmlParser.js @@ -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) 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; \ No newline at end of file diff --git a/test_mini/components/parser/libs/config.js b/test_mini/components/parser/libs/config.js new file mode 100644 index 0000000..795f439 --- /dev/null +++ b/test_mini/components/parser/libs/config.js @@ -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(/([\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 = `${lan}
${content}
`; + 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; +} \ No newline at end of file diff --git a/test_mini/components/parser/libs/emoji.js b/test_mini/components/parser/libs/emoji.js new file mode 100644 index 0000000..d6dadee --- /dev/null +++ b/test_mini/components/parser/libs/emoji.js @@ -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 ``; + else return data[$1]; + } else return $; + }), + getEmoji: (key) => data[key], + setEmoji: (key, emoji) => data[key] = emoji, + removeEmoji: (key) => data[key] = void 0 +} \ No newline at end of file diff --git a/test_mini/components/parser/libs/prism.js b/test_mini/components/parser/libs/prism.js new file mode 100644 index 0000000..9a95229 --- /dev/null +++ b/test_mini/components/parser/libs/prism.js @@ -0,0 +1,9 @@ +/* PrismJS 1.19.0 +https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+json+markdown */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,r=0,C={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(r){return r instanceof _?new _(r.type,e(r.content),r.alias):Array.isArray(r)?r.map(e):r.replace(/&/g,"&").replace(/e.length)return;if(!(k instanceof _)){if(h&&y!=r.length-1){if(c.lastIndex=v,!(S=c.exec(e)))break;for(var b=S.index+(f&&S[1]?S[1].length:0),w=S.index+S[0].length,A=y,P=v,x=r.length;A"+a.content+""},!u.document)return u.addEventListener&&(C.disableWorkerMessageHandler||u.addEventListener("message",function(e){var r=JSON.parse(e.data),n=r.language,t=r.code,a=r.immediateClose;u.postMessage(C.highlight(t,C.languages[n],n)),a&&u.close()},!1)),C;var e=C.util.currentScript();function n(){C.manual||C.highlightAll()}if(e&&(C.filename=e.src,e.hasAttribute("data-manual")&&(C.manual=!0)),!C.manual){var t=document.readyState;"loading"===t||"interactive"===t&&e&&e.defer?document.addEventListener("DOMContentLoaded",n):window.requestAnimationFrame?window.requestAnimationFrame(n):window.setTimeout(n,16)}return C}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:(?!)*\]\s*)?>/i,greedy:!0},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var n={"included-cdata":{pattern://i,inside:s}};n["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var t={};t[a]={pattern:RegExp("(<__[\\s\\S]*?>)(?:\\s*|[\\s\\S])*?(?=<\\/__>)".replace(/__/g,a),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",t)}}),Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; +!function(s){var e=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\((?!\s*\))\s*)(?:[^()]|\((?:[^()]|\([^()]*\))*\))+?(?=\s*\))/,lookbehind:!0,alias:"selector"}}},url:{pattern:RegExp("url\\((?:"+e.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+e.source+")*?(?=\\s*\\{)"),string:{pattern:e,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),s.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:t.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:s.languages.css}},alias:"language-css"}},t.tag))}(Prism); +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&|\|\||[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?[.?]?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*[\s\S]*?\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript; +Prism.languages.json={property:{pattern:/"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,greedy:!0},string:{pattern:/"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,greedy:!0},comment:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,number:/-?\d+\.?\d*(?:e[+-]?\d+)?/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:true|false)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}}; +!function(d){function n(n,e){return n=n.replace(//g,"(?:\\\\.|[^\\\\\\n\r]|(?:\r?\n|\r)(?!\r?\n|\r))"),e&&(n=n+"|"+n.replace(/_/g,"\\*")),RegExp("((?:^|[^\\\\])(?:\\\\{2})*)(?:"+n+")")}var e="(?:\\\\.|``.+?``|`[^`\r\\n]+`|[^\\\\|\r\\n`])+",t="\\|?__(?:\\|__)+\\|?(?:(?:\r?\n|\r)|$)".replace(/__/g,e),a="\\|?[ \t]*:?-{3,}:?[ \t]*(?:\\|[ \t]*:?-{3,}:?[ \t]*)+\\|?(?:\r?\n|\r)";d.languages.markdown=d.languages.extend("markup",{}),d.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+t+a+"(?:"+t+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+t+a+")(?:"+t+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(e),inside:d.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+t+")"+a+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+t+"$"),inside:{"table-header":{pattern:RegExp(e),alias:"important",inside:d.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/(^[ \t]*(?:\r?\n|\r))(?: {4}|\t).+(?:(?:\r?\n|\r)(?: {4}|\t).+)*/m,lookbehind:!0,alias:"keyword"},{pattern:/``.+?``|`[^`\r\n]+`/,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\r?\n|\r))[\s\S]+?(?=(?:\r?\n|\r)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\r?\n|\r)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n("__(?:(?!_)|_(?:(?!_))+_)+__",!0),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n("_(?:(?!_)|__(?:(?!_))+__)+_",!0),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n("(~~?)(?:(?!~))+?\\2",!1),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},url:{pattern:n('!?\\[(?:(?!\\]))+\\](?:\\([^\\s)]+(?:[\t ]+"(?:\\\\.|[^"\\\\])*")?\\)| ?\\[(?:(?!\\]))+\\])',!1),lookbehind:!0,greedy:!0,inside:{variable:{pattern:/(\[)[^\]]+(?=\]$)/,lookbehind:!0},content:{pattern:/(^!?\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),["url","bold","italic","strike"].forEach(function(e){["url","bold","italic","strike"].forEach(function(n){e!==n&&(d.languages.markdown[e].inside.content.inside[n]=d.languages.markdown[n])})}),d.hooks.add("after-tokenize",function(n){"markdown"!==n.language&&"md"!==n.language||!function n(e){if(e&&"string"!=typeof e)for(var t=0,a=e.length;t 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() + }) + } + } + } +}) \ No newline at end of file diff --git a/test_mini/components/parser/parser.json b/test_mini/components/parser/parser.json new file mode 100644 index 0000000..85e49c9 --- /dev/null +++ b/test_mini/components/parser/parser.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "trees": "./trees/trees" + } +} \ No newline at end of file diff --git a/test_mini/components/parser/parser.wxml b/test_mini/components/parser/parser.wxml new file mode 100644 index 0000000..d905cce --- /dev/null +++ b/test_mini/components/parser/parser.wxml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/test_mini/components/parser/parser.wxss b/test_mini/components/parser/parser.wxss new file mode 100644 index 0000000..7801832 --- /dev/null +++ b/test_mini/components/parser/parser.wxss @@ -0,0 +1,16 @@ +:host { + display: block; + overflow: scroll; + -webkit-overflow-scrolling: touch; +} +.top { + display: inherit; +} +@keyframes show { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/test_mini/components/parser/trees/trees.js b/test_mini/components/parser/trees/trees.js new file mode 100644 index 0000000..24bf445 --- /dev/null +++ b/test_mini/components/parser/trees/trees.js @@ -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,", + 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 + }) + } + } +}) \ No newline at end of file diff --git a/test_mini/components/parser/trees/trees.json b/test_mini/components/parser/trees/trees.json new file mode 100644 index 0000000..125a52b --- /dev/null +++ b/test_mini/components/parser/trees/trees.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "trees": "./trees", + "myAudio": "../audio/audio" + } +} \ No newline at end of file diff --git a/test_mini/components/parser/trees/trees.wxml b/test_mini/components/parser/trees/trees.wxml new file mode 100644 index 0000000..61dea53 --- /dev/null +++ b/test_mini/components/parser/trees/trees.wxml @@ -0,0 +1,52 @@ + + +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 + } +} + + + + + + + + + {{n.text}} + \n + + + + + + + + + + + + + + + {{n.num}} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test_mini/components/parser/trees/trees.wxss b/test_mini/components/parser/trees/trees.wxss new file mode 100644 index 0000000..520b6df --- /dev/null +++ b/test_mini/components/parser/trees/trees.wxss @@ -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%; +} \ No newline at end of file diff --git a/test_mini/pages/article/detail.js b/test_mini/pages/article/detail.js index 737a648..70efbc9 100644 --- a/test_mini/pages/article/detail.js +++ b/test_mini/pages/article/detail.js @@ -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(/\ + + {{title}} {{ifrom}} 置顶 - 原创 + 原创 - + diff --git a/test_mini/pages/lianxi/index.wxml b/test_mini/pages/lianxi/index.wxml index c527cce..4877c07 100644 --- a/test_mini/pages/lianxi/index.wxml +++ b/test_mini/pages/lianxi/index.wxml @@ -11,10 +11,7 @@

{{item.name}}

- {{item.type}} - --共 - {{item.tmtotal}} - 道/已答 + 已答 {{item.ydtmtotal}} diff --git a/test_server/crm/models.py b/test_server/crm/models.py index 4bb67fc..092dec5 100644 --- a/test_server/crm/models.py +++ b/test_server/crm/models.py @@ -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: