trees.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. <template>
  2. <view class="interlayer">
  3. <block v-for="(n, index) in nodes" v-bind:key="index">
  4. <!--图片-->
  5. <!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
  6. <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)"
  7. :data-attrs="n.attrs" @tap="imgtap" @longpress="imglongtap" />
  8. <!--#endif-->
  9. <!--#ifdef MP-BAIDU || MP-TOUTIAO-->
  10. <rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="n.attrs.contain" :nodes='[n]' :data-attrs="n.attrs"
  11. @tap="imgtap" @longpress="imglongtap" />
  12. <!--#endif-->
  13. <!--文本-->
  14. <!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
  15. <rich-text v-else-if="n.decode" class="_entity" :nodes="[n]"></rich-text>
  16. <!--#endif-->
  17. <text v-else-if="n.type=='text'" decode>{{n.text}}</text>
  18. <text v-else-if="n.name=='br'">\n</text>
  19. <!--视频-->
  20. <view v-else-if="n.name=='video'">
  21. <view v-if="(!loadVideo||n.lazyLoad)&&!(controls[n.attrs.id]&&controls[n.attrs.id].play)" :id="n.attrs.id" :class="'_video '+(n.attrs.class||'')"
  22. :style="n.attrs.style" @tap="_loadVideo" />
  23. <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)"
  24. :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]"
  25. :unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" data-from="video" data-source="source" @error="error" @play="play" />
  26. </view>
  27. <!--音频-->
  28. <audio v-else-if="n.name=='audio'" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :autoplay="n.attrs.autoplay"
  29. :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]"
  30. :data-id="n.attrs.id" data-from="audio" data-source="source" @error="error" @play="play" />
  31. <!--链接-->
  32. <view v-else-if="n.name=='a'" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style"
  33. :data-attrs="n.attrs" @tap="linkpress">
  34. <trees class="_span" :nodes="n.children" />
  35. </view>
  36. <!--广告(按需打开注释)-->
  37. <!--#ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO-->
  38. <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']"
  39. data-from="ad" @error="error" />-->
  40. <!--#endif-->
  41. <!--#ifdef MP-BAIDU-->
  42. <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :appid="n.attrs.appid"
  43. :apid="n.attrs.apid" :type="n.attrs.type" data-from="ad" @error="error" />-->
  44. <!--#endif-->
  45. <!--#ifdef APP-PLUS-->
  46. <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :adpid="n.attrs.adpid"
  47. data-from="ad" @error="error" />-->
  48. <!--#endif-->
  49. <!--列表-->
  50. <view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex'">
  51. <view v-if="n.type=='ol'" class="_ol-bef">{{n.num}}</view>
  52. <view v-else class="_ul-bef">
  53. <view v-if="n.floor%3==0" class="_ul-p1">█</view>
  54. <view v-else-if="n.floor%3==2" class="_ul-p2" />
  55. <view v-else class="_ul-p1" style="border-radius:50%">█</view>
  56. </view>
  57. <!--#ifdef MP-ALIPAY-->
  58. <view class="_li">
  59. <trees :nodes="n.children" />
  60. </view>
  61. <!--#endif-->
  62. <!--#ifndef MP-ALIPAY-->
  63. <trees class="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
  64. <!--#endif-->
  65. </view>
  66. <!--表格-->
  67. <view v-else-if="n.name=='table'&&n.c" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:table'">
  68. <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'):'')">
  69. <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'):'')">
  70. <trees v-if="tr.name=='td'" :nodes="tr.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
  71. <block v-else>
  72. <!--#ifdef MP-ALIPAY-->
  73. <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'):'')">
  74. <trees :nodes="td.children" />
  75. </view>
  76. <!--#endif-->
  77. <!--#ifndef MP-ALIPAY-->
  78. <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'):'')"
  79. :nodes="td.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
  80. <!--#endif-->
  81. </block>
  82. </view>
  83. </view>
  84. </view>
  85. <!--#ifdef APP-PLUS-->
  86. <iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder"
  87. :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
  88. <embed v-else-if="n.name=='embed'" :style="n.attrs.style" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
  89. <!--#endif-->
  90. <!--富文本-->
  91. <!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
  92. <rich-text v-else-if="handler.useRichText(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" />
  93. <!--#endif-->
  94. <!--#ifdef MP-BAIDU || MP-TOUTIAO-->
  95. <rich-text v-else-if="!(n.c||n.continue)" :id="n.attrs.id" :class="_p" :style="n.attrs.contain" :nodes="[n]" />
  96. <!--#endif-->
  97. <!--#ifdef MP-ALIPAY-->
  98. <view v-else :id="n.attrs.id" :class="'_'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style">
  99. <trees :nodes="n.children" />
  100. </view>
  101. <!--#endif-->
  102. <!--#ifndef MP-ALIPAY-->
  103. <trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style" :nodes="n.children"
  104. :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
  105. <!--#endif-->
  106. </block>
  107. </view>
  108. </template>
  109. <script module="handler" lang="wxs" src="./handler.wxs"></script>
  110. <script module="handler" lang="sjs" src="./handler.sjs"></script>
  111. <script>
  112. global.Parser = {};
  113. import trees from './trees'
  114. export default {
  115. components: {
  116. trees
  117. },
  118. name: 'trees',
  119. data() {
  120. return {
  121. controls: {},
  122. // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
  123. imgLoad: false,
  124. // #endif
  125. // #ifndef APP-PLUS
  126. loadVideo: true
  127. // #endif
  128. }
  129. },
  130. props: {
  131. nodes: Array,
  132. // #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
  133. lazyLoad: Boolean,
  134. // #endif
  135. // #ifdef APP-PLUS
  136. loadVideo: Boolean
  137. // #endif
  138. },
  139. mounted() {
  140. // 获取顶层组件
  141. this.top = this.$parent;
  142. while (this.top.$options.name != 'parser') {
  143. if (this.top.top) {
  144. this.top = this.top.top;
  145. break;
  146. }
  147. this.top = this.top.$parent;
  148. }
  149. },
  150. // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
  151. beforeDestroy() {
  152. if (this.observer)
  153. this.observer.disconnect();
  154. },
  155. // #endif
  156. methods: {
  157. // #ifndef MP-ALIPAY
  158. play(e) {
  159. if (this.top.videoContexts.length > 1 && this.top.autopause)
  160. for (var i = this.top.videoContexts.length; i--;)
  161. if (this.top.videoContexts[i].id != e.currentTarget.dataset.id)
  162. this.top.videoContexts[i].pause();
  163. },
  164. // #endif
  165. imgtap(e) {
  166. var attrs = e.currentTarget.dataset.attrs;
  167. if (!attrs.ignore) {
  168. var preview = true, data = {
  169. id: e.target.id,
  170. src: attrs.src,
  171. ignore: () => preview = false
  172. };
  173. global.Parser.onImgtap && global.Parser.onImgtap(data);
  174. this.top.$emit('imgtap', data);
  175. if (preview) {
  176. var urls = this.top.imgList,
  177. current = urls[attrs.i] ? parseInt(attrs.i) : (urls = [attrs.src], 0);
  178. uni.previewImage({
  179. current,
  180. urls
  181. })
  182. }
  183. }
  184. },
  185. imglongtap(e) {
  186. var attrs = e.item.dataset.attrs;
  187. if (!attrs.ignore)
  188. this.top.$emit('imglongtap', {
  189. id: e.target.id,
  190. src: attrs.src
  191. })
  192. },
  193. linkpress(e) {
  194. var jump = true,
  195. attrs = e.currentTarget.dataset.attrs;
  196. attrs.ignore = () => jump = false;
  197. global.Parser.onLinkpress && global.Parser.onLinkpress(attrs);
  198. this.top.$emit('linkpress', attrs);
  199. if (jump) {
  200. // #ifdef MP
  201. if (attrs['app-id']) {
  202. return uni.navigateToMiniProgram({
  203. appId: attrs['app-id'],
  204. path: attrs.path
  205. })
  206. }
  207. // #endif
  208. if (attrs.href) {
  209. if (attrs.href[0] == '#') {
  210. if (this.top.useAnchor)
  211. this.top.navigateTo({
  212. id: attrs.href.substring(1)
  213. })
  214. } else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0) {
  215. // #ifdef APP-PLUS
  216. plus.runtime.openWeb(attrs.href);
  217. // #endif
  218. // #ifndef APP-PLUS
  219. uni.setClipboardData({
  220. data: attrs.href,
  221. success: () =>
  222. uni.showToast({
  223. title: '链接已复制'
  224. })
  225. })
  226. // #endif
  227. } else
  228. uni.navigateTo({
  229. url: attrs.href
  230. })
  231. }
  232. }
  233. },
  234. error(e) {
  235. var context, target = e.currentTarget,
  236. source = target.dataset.from;
  237. if (source == 'video' || source == 'audio') {
  238. // 加载其他 source
  239. var index = this.controls[target.id] ? this.controls[target.id].index + 1 : 1;
  240. if (index < target.dataset.source.length)
  241. this.$set(this.controls, target.id + '.index', index);
  242. if (source == 'video') context = uni.createVideoContext(target.id, this);
  243. }
  244. this.top && this.top.$emit('error', {
  245. source,
  246. target,
  247. errMsg: e.detail.errMsg,
  248. errCode: e.detail.errCode,
  249. context
  250. });
  251. },
  252. _loadVideo(e) {
  253. this.$set(this.controls, e.currentTarget.id, {
  254. play: true,
  255. index: 0
  256. })
  257. }
  258. }
  259. }
  260. </script>
  261. <style>
  262. /* 在这里引入自定义样式 */
  263. /* 链接和图片效果 */
  264. ._a {
  265. display: inline;
  266. color: #366092;
  267. word-break: break-all;
  268. padding: 1.5px 0 1.5px 0;
  269. }
  270. ._hover {
  271. opacity: 0.7;
  272. text-decoration: underline;
  273. }
  274. ._img {
  275. display: inline-block;
  276. text-indent: 0;
  277. }
  278. /* #ifdef MP-WEIXIN */
  279. :host {
  280. display: inline;
  281. }
  282. /* #endif */
  283. /* #ifdef MP */
  284. .interlayer {
  285. align-content: inherit;
  286. align-items: inherit;
  287. display: inherit;
  288. flex-direction: inherit;
  289. flex-wrap: inherit;
  290. justify-content: inherit;
  291. width: 100%;
  292. white-space: inherit;
  293. }
  294. /* #endif */
  295. ._b,
  296. ._strong {
  297. font-weight: bold;
  298. }
  299. ._blockquote,
  300. ._div,
  301. ._p,
  302. ._ol,
  303. ._ul,
  304. ._li {
  305. display: block;
  306. }
  307. ._code {
  308. font-family: monospace;
  309. }
  310. ._del {
  311. text-decoration: line-through;
  312. }
  313. ._em,
  314. ._i {
  315. font-style: italic;
  316. }
  317. ._h1 {
  318. font-size: 2em;
  319. }
  320. ._h2 {
  321. font-size: 1.5em;
  322. }
  323. ._h3 {
  324. font-size: 1.17em;
  325. }
  326. ._h5 {
  327. font-size: 0.83em;
  328. }
  329. ._h6 {
  330. font-size: 0.67em;
  331. }
  332. ._h1,
  333. ._h2,
  334. ._h3,
  335. ._h4,
  336. ._h5,
  337. ._h6 {
  338. display: block;
  339. font-weight: bold;
  340. }
  341. ._ins {
  342. text-decoration: underline;
  343. }
  344. ._li {
  345. flex: 1;
  346. width: 0;
  347. }
  348. ._ol-bef {
  349. margin-right: 5px;
  350. text-align: right;
  351. width: 36px;
  352. }
  353. ._ul-bef {
  354. line-height: normal;
  355. margin: 0 12px 0 23px;
  356. }
  357. ._ol-bef,
  358. ._ul_bef {
  359. flex: none;
  360. user-select: none;
  361. }
  362. ._ul-p1 {
  363. display: inline-block;
  364. height: 0.3em;
  365. line-height: 0.3em;
  366. overflow: hidden;
  367. width: 0.3em;
  368. }
  369. ._ul-p2 {
  370. border: 0.05em solid black;
  371. border-radius: 50%;
  372. display: inline-block;
  373. height: 0.23em;
  374. width: 0.23em;
  375. }
  376. ._q::before {
  377. content: '"';
  378. }
  379. ._q::after {
  380. content: '"';
  381. }
  382. ._sub {
  383. font-size: smaller;
  384. vertical-align: sub;
  385. }
  386. ._sup {
  387. font-size: smaller;
  388. vertical-align: super;
  389. }
  390. /* #ifndef MP-WEIXIN */
  391. ._abbr,
  392. ._b,
  393. ._code,
  394. ._del,
  395. ._em,
  396. ._i,
  397. ._ins,
  398. ._label,
  399. ._q,
  400. ._span,
  401. ._strong,
  402. ._sub,
  403. ._sup {
  404. display: inline;
  405. }
  406. /* #endif */
  407. /* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */
  408. .__bdo,
  409. .__bdi,
  410. .__ruby,
  411. .__rt,
  412. ._entity {
  413. display: inline-block;
  414. }
  415. /* #endif */
  416. ._video {
  417. background-color: black;
  418. display: inline-block;
  419. height: 225px;
  420. position: relative;
  421. width: 300px;
  422. }
  423. ._video::after {
  424. border-color: transparent transparent transparent white;
  425. border-style: solid;
  426. border-width: 15px 0 15px 30px;
  427. content: '';
  428. left: 50%;
  429. margin: -15px 0 0 -15px;
  430. position: absolute;
  431. top: 50%;
  432. }
  433. </style>