<!-- trees 递归显示组件 github:https://github.com/jin-yufeng/Parser docs:https://jin-yufeng.github.io/Parser 插件市场:https://ext.dcloud.net.cn/plugin?id=805 author:JinYufeng update:2020/04/13 --> <template> <view class="interlayer"> <block v-for="(n, index) in nodes" v-bind:key="index"> <!--图片--> <!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS--> <rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="''+handler.getStyle(n.attrs.style)" :nodes="handler.getNode(n,!lazyLoad||imgLoad)" :data-attrs="n.attrs" @tap="imgtap" @longpress="imglongtap" /> <!--#endif--> <!--#ifdef MP-BAIDU || MP-TOUTIAO--> <rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="n.attrs.contain" :nodes='[n]' :data-attrs="n.attrs" @tap="imgtap" @longpress="imglongtap" /> <!--#endif--> <!--文本--> <!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS--> <rich-text v-else-if="n.decode" class="_entity" :nodes="[n]"></rich-text> <!--#endif--> <text v-else-if="n.type=='text'" decode>{{n.text}}</text> <text v-else-if="n.name=='br'">\n</text> <!--视频--> <view v-else-if="n.name=='video'"> <view v-if="(!loadVideo||n.lazyLoad)&&!(controls[n.attrs.id]&&controls[n.attrs.id].play)" :id="n.attrs.id" :class="'_video '+(n.attrs.class||'')" :style="n.attrs.style" @tap="_loadVideo" /> <video v-else :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay||(controls[n.attrs.id]&&controls[n.attrs.id].play)" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.attrs.source[(controls[n.attrs.id]&&controls[n.attrs.id].index)||0]" :unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" data-from="video" data-source="source" @error="error" @play="play" /> </view> <!--音频--> <audio v-else-if="n.name=='audio'" :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[(controls[n.attrs.id]&&controls[n.attrs.id].index)||0]" :data-id="n.attrs.id" data-from="audio" data-source="source" @error="error" @play="play" /> <!--链接--> <view v-else-if="n.name=='a'" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style" :data-attrs="n.attrs" @tap="linkpress"> <trees class="_span" :nodes="n.children" /> </view> <!--广告(按需打开注释)--> <!--#ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO--> <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']" data-from="ad" @error="error" />--> <!--#endif--> <!--#ifdef MP-BAIDU--> <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :appid="n.attrs.appid" :apid="n.attrs.apid" :type="n.attrs.type" data-from="ad" @error="error" />--> <!--#endif--> <!--#ifdef APP-PLUS--> <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :adpid="n.attrs.adpid" data-from="ad" @error="error" />--> <!--#endif--> <!--列表--> <view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex'"> <view v-if="n.type=='ol'" class="_ol-bef">{{n.num}}</view> <view v-else class="_ul-bef"> <view v-if="n.floor%3==0" class="_ul-p1">█</view> <view v-else-if="n.floor%3==2" class="_ul-p2" /> <view v-else class="_ul-p1" style="border-radius:50%">█</view> </view> <!--#ifdef MP-ALIPAY--> <view class="_li"> <trees :nodes="n.children" /> </view> <!--#endif--> <!--#ifndef MP-ALIPAY--> <trees class="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" /> <!--#endif--> </view> <!--表格--> <view v-else-if="n.name=='table'&&n.c" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:table'"> <view v-for="(tbody, i) in n.children" v-bind:key="i" :class="tbody.attrs.class" :style="(tbody.attrs.style||'')+(tbody.name[0]=='t'?';display:table-'+(tbody.name=='tr'?'row':'row-group'):'')"> <view v-for="(tr, j) in tbody.children" v-bind:key="j" :class="tr.attrs.class" :style="(tr.attrs.style||'')+(tr.name[0]=='t'?';display:table-'+(tr.name=='tr'?'row':'cell'):'')"> <trees v-if="tr.name=='td'" :nodes="tr.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" /> <block v-else> <!--#ifdef MP-ALIPAY--> <view v-for="(td, k) in tr.children" v-bind:key="k" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')"> <trees :nodes="td.children" /> </view> <!--#endif--> <!--#ifndef MP-ALIPAY--> <trees v-for="(td, k) in tr.children" v-bind:key="k" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')" :nodes="td.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" /> <!--#endif--> </block> </view> </view> </view> <!--#ifdef APP-PLUS--> <iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" /> <embed v-else-if="n.name=='embed'" :style="n.attrs.style" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" /> <!--#endif--> <!--富文本--> <!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS--> <rich-text v-else-if="handler.useRichText(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" /> <!--#endif--> <!--#ifdef MP-BAIDU || MP-TOUTIAO--> <rich-text v-else-if="!(n.c||n.continue)" :id="n.attrs.id" :class="_p" :style="n.attrs.contain" :nodes="[n]" /> <!--#endif--> <!--#ifdef MP-ALIPAY--> <view v-else :id="n.attrs.id" :class="'_'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style"> <trees :nodes="n.children" /> </view> <!--#endif--> <!--#ifndef MP-ALIPAY--> <trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style" :nodes="n.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" /> <!--#endif--> </block> </view> </template> <script module="handler" lang="wxs" src="./handler.wxs"></script> <script module="handler" lang="sjs" src="./handler.sjs"></script> <script> global.Parser = {}; import trees from './trees' export default { components: { trees }, name: 'trees', data() { return { controls: {}, // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS imgLoad: false, // #endif // #ifndef APP-PLUS loadVideo: true // #endif } }, props: { nodes: Array, // #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS lazyLoad: Boolean, // #endif // #ifdef APP-PLUS loadVideo: Boolean // #endif }, mounted() { // 获取顶层组件 this.top = this.$parent; while (this.top.$options.name != 'parser') { if (this.top.top) { this.top = this.top.top; break; } this.top = this.top.$parent; } }, // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS beforeDestroy() { if (this.observer) this.observer.disconnect(); }, // #endif methods: { // #ifndef MP-ALIPAY play(e) { if (this.top.videoContexts.length > 1 && this.top.autopause) for (var i = this.top.videoContexts.length; i--;) if (this.top.videoContexts[i].id != e.currentTarget.dataset.id) this.top.videoContexts[i].pause(); }, // #endif imgtap(e) { var attrs = e.currentTarget.dataset.attrs; if (!attrs.ignore) { var preview = true, data = { id: e.target.id, src: attrs.src, ignore: () => preview = false }; global.Parser.onImgtap && global.Parser.onImgtap(data); this.top.$emit('imgtap', data); if (preview) { var urls = this.top.imgList, current = urls[attrs.i] ? parseInt(attrs.i) : (urls = [attrs.src], 0); uni.previewImage({ current, urls }) } } }, imglongtap(e) { var attrs = e.item.dataset.attrs; if (!attrs.ignore) this.top.$emit('imglongtap', { id: e.target.id, src: attrs.src }) }, linkpress(e) { var jump = true, attrs = e.currentTarget.dataset.attrs; attrs.ignore = () => jump = false; global.Parser.onLinkpress && global.Parser.onLinkpress(attrs); this.top.$emit('linkpress', attrs); if (jump) { // #ifdef MP if (attrs['app-id']) { return uni.navigateToMiniProgram({ appId: attrs['app-id'], path: attrs.path }) } // #endif if (attrs.href) { if (attrs.href[0] == '#') { if (this.top.useAnchor) this.top.navigateTo({ id: attrs.href.substring(1) }) } else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0) { // #ifdef APP-PLUS plus.runtime.openWeb(attrs.href); // #endif // #ifndef APP-PLUS uni.setClipboardData({ data: attrs.href, success: () => uni.showToast({ title: '链接已复制' }) }) // #endif } else uni.navigateTo({ url: attrs.href }) } } }, error(e) { var context, target = e.currentTarget, source = target.dataset.from; if (source == 'video' || source == 'audio') { // 加载其他 source var index = this.controls[target.id] ? this.controls[target.id].index + 1 : 1; if (index < target.dataset.source.length) this.$set(this.controls, target.id + '.index', index); if (source == 'video') context = uni.createVideoContext(target.id, this); } this.top && this.top.$emit('error', { source, target, errMsg: e.detail.errMsg, errCode: e.detail.errCode, context }); }, _loadVideo(e) { this.$set(this.controls, e.currentTarget.id, { play: true, index: 0 }) } } } </script> <style> /* 在这里引入自定义样式 */ /* 链接和图片效果 */ ._a { display: inline; color: #366092; word-break: break-all; padding: 1.5px 0 1.5px 0; } ._hover { opacity: 0.7; text-decoration: underline; } ._img { display: inline-block; text-indent: 0; } /* #ifdef MP-WEIXIN */ :host { display: inline; } /* #endif */ /* #ifdef MP */ .interlayer { align-content: inherit; align-items: inherit; display: inherit; flex-direction: inherit; flex-wrap: inherit; justify-content: inherit; width: 100%; white-space: inherit; } /* #endif */ ._b, ._strong { font-weight: bold; } ._blockquote, ._div, ._p, ._ol, ._ul, ._li { display: block; } ._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; } ._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; } /* #ifndef MP-WEIXIN */ ._abbr, ._b, ._code, ._del, ._em, ._i, ._ins, ._label, ._q, ._span, ._strong, ._sub, ._sup { display: inline; } /* #endif */ /* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */ .__bdo, .__bdi, .__ruby, .__rt, ._entity { display: inline-block; } /* #endif */ ._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%; } </style>