You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2 年之前
2 年之前
2 年之前
2 年之前
2 年之前
2 年之前
2 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. <template>
  2. <view :class="'interlayer '+(c||'')" :style="s">
  3. <block v-for="(n, i) in nodes" v-bind:key="i">
  4. <!--图片-->
  5. <view v-if="n.name=='img'" :class="'_img '+n.attrs.class" :style="n.attrs.style" :data-attrs="n.attrs" @tap.stop="imgtap">
  6. <rich-text v-if="ctrl[i]!=0" :nodes="[{attrs:{src:loading&&(ctrl[i]||0)<2?loading:(lazyLoad&&!ctrl[i]?placeholder:(ctrl[i]==3?errorImg:n.attrs.src||'')),alt:n.attrs.alt||'',width:n.attrs.width||'',style:'-webkit-touch-callout:none;max-width:100%;display:block'+(n.attrs.height?';height:'+n.attrs.height:'')},name:'img'}]" />
  7. <image class="_image" :src="lazyLoad&&!ctrl[i]?placeholder:n.attrs.src" :lazy-load="lazyLoad"
  8. :show-menu-by-longpress="!n.attrs.ignore" :data-i="i" :data-index="n.attrs.i" data-source="img" @load="loadImg"
  9. @error="error" />
  10. </view>
  11. <!--文本-->
  12. <text v-else-if="n.type=='text'" decode>{{n.text}}</text>
  13. <!--#ifndef MP-BAIDU-->
  14. <text v-else-if="n.name=='br'">\n</text>
  15. <!--#endif-->
  16. <!--视频-->
  17. <view v-else-if="((n.lazyLoad&&!n.attrs.autoplay)||(n.name=='video'&&!loadVideo))&&ctrl[i]==undefined" :id="n.attrs.id"
  18. :class="'_video '+(n.attrs.class||'')" :style="n.attrs.style" :data-i="i" @tap.stop="_loadVideo" />
  19. <video v-else-if="n.name=='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay||ctrl[i]==0"
  20. :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.attrs.source[ctrl[i]||0]"
  21. :unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" :data-i="i" data-source="video" @error="error" @play="play" />
  22. <!--音频-->
  23. <audio v-else-if="n.name=='audio'" :ref="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author"
  24. :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster"
  25. :src="n.attrs.source[ctrl[i]||0]" :data-i="i" :data-id="n.attrs.id" data-source="audio" @error.native="error"
  26. @play.native="play" />
  27. <!--链接-->
  28. <view v-else-if="n.name=='a'" :id="n.attrs.id" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style"
  29. :data-attrs="n.attrs" @tap.stop="linkpress">
  30. <trees class="_span" c="_span" :nodes="n.children" />
  31. </view>
  32. <!--广告-->
  33. <!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']" :appid="n.attrs.appid" :apid="n.attrs.apid" :type="n.attrs.type" :adpid="n.attrs.adpid" data-source="ad" @error="error" />-->
  34. <!--列表-->
  35. <view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex;flex-direction:row'">
  36. <view v-if="n.type=='ol'" class="_ol-bef">{{n.num}}</view>
  37. <view v-else class="_ul-bef">
  38. <view v-if="n.floor%3==0" class="_ul-p1">█</view>
  39. <view v-else-if="n.floor%3==2" class="_ul-p2" />
  40. <view v-else class="_ul-p1" style="border-radius:50%">█</view>
  41. </view>
  42. <trees class="_li" c="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
  43. </view>
  44. <!--表格-->
  45. <view v-else-if="n.name=='table'&&n.c&&n.flag" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:grid'">
  46. <trees v-for="(cell,n) in n.children" v-bind:key="n" :class="cell.attrs.class" :c="cell.attrs.class" :style="cell.attrs.style"
  47. :s="cell.attrs.style" :nodes="cell.children" />
  48. </view>
  49. <view v-else-if="n.name=='table'&&n.c" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:table'">
  50. <view v-for="(tbody, o) in n.children" v-bind:key="o" :class="tbody.attrs.class" :style="(tbody.attrs.style||'')+(tbody.name[0]=='t'?';display:table-'+(tbody.name=='tr'?'row':'row-group'):'')">
  51. <view v-for="(tr, p) in tbody.children" v-bind:key="p" :class="tr.attrs.class" :style="(tr.attrs.style||'')+(tr.name[0]=='t'?';display:table-'+(tr.name=='tr'?'row':'cell'):'')">
  52. <trees v-if="tr.name=='td'" :nodes="tr.children" />
  53. <trees v-else v-for="(td, q) in tr.children" v-bind:key="q" :class="td.attrs.class" :c="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')"
  54. :s="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')" :nodes="td.children" />
  55. </view>
  56. </view>
  57. </view>
  58. <!--#ifdef APP-PLUS-->
  59. <iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder"
  60. :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
  61. <embed v-else-if="n.name=='embed'" :style="n.attrs.style" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
  62. <!--#endif-->
  63. <!--富文本-->
  64. <!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
  65. <rich-text v-else-if="use(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" />
  66. <!--#endif-->
  67. <!--#ifndef MP-WEIXIN || MP-QQ || APP-PLUS-->
  68. <rich-text v-else-if="!n.c" :id="n.attrs.id" :nodes="[n]" style="display:inline" />
  69. <!--#endif-->
  70. <trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :c="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')"
  71. :style="n.attrs.style" :s="n.attrs.style" :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
  72. </block>
  73. </view>
  74. </template>
  75. <script>
  76. var inline = {
  77. abbr: 1,
  78. b: 1,
  79. big: 1,
  80. code: 1,
  81. del: 1,
  82. em: 1,
  83. i: 1,
  84. ins: 1,
  85. label: 1,
  86. q: 1,
  87. small: 1,
  88. span: 1,
  89. strong: 1,
  90. sub: 1,
  91. sup: 1
  92. }
  93. let globalData = {};
  94. globalData.Parser = {};
  95. //import trees from './trees'
  96. import cfg from '../libs/config.js'
  97. const errorImg = cfg.errorImg;
  98. export default {
  99. components: {
  100. //trees
  101. },
  102. name: 'trees',
  103. data() {
  104. return {
  105. ctrl: [],
  106. placeholder: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="225"/>',
  107. errorImg,
  108. loadVideo: typeof plus == 'undefined',
  109. // #ifndef MP-ALIPAY
  110. c: '',
  111. s: ''
  112. // #endif
  113. }
  114. },
  115. props: {
  116. nodes: Array,
  117. lazyLoad: Boolean,
  118. loading: String,
  119. preview: Boolean,
  120. // #ifdef MP-ALIPAY
  121. c: String,
  122. s: String
  123. // #endif
  124. },
  125. mounted() {
  126. for (this.top = this.$parent; this.top.$options.name != 'parser'; this.top = this.top.$parent);
  127. this.init();
  128. },
  129. // #ifdef APP-PLUS
  130. // #ifndef VUE3
  131. // 组件销毁前,将实例从u-form的缓存中移除
  132. beforeDestroy() {
  133. this.observer && this.observer.disconnect();
  134. },
  135. // #endif
  136. // #ifdef VUE3
  137. beforeUnmount() {
  138. this.observer && this.observer.disconnect();
  139. },
  140. // #endif
  141. // #endif
  142. methods: {
  143. init() {
  144. for (var i = this.nodes.length, n; n = this.nodes[--i];) {
  145. if (n.name == 'img') {
  146. this.top.imgList.setItem(n.attrs.i, n.attrs['original-src'] || n.attrs.src);
  147. // #ifdef APP-PLUS
  148. if (this.lazyLoad && !this.observer) {
  149. this.observer = uni.createIntersectionObserver(this).relativeToViewport({
  150. top: 500,
  151. bottom: 500
  152. });
  153. setTimeout(() => {
  154. this.observer.observe('._img', res => {
  155. if (res.intersectionRatio) {
  156. for (var j = this.nodes.length; j--;)
  157. if (this.nodes[j].name == 'img')
  158. this.$set(this.ctrl, j, 1);
  159. this.observer.disconnect();
  160. }
  161. })
  162. }, 0)
  163. }
  164. // #endif
  165. } else if (n.name == 'video' || n.name == 'audio') {
  166. var ctx;
  167. if (n.name == 'video') {
  168. ctx = uni.createVideoContext(n.attrs.id
  169. // #ifndef MP-BAIDU
  170. , this
  171. // #endif
  172. );
  173. } else if (this.$refs[n.attrs.id])
  174. ctx = this.$refs[n.attrs.id][0];
  175. if (ctx) {
  176. ctx.id = n.attrs.id;
  177. this.top.videoContexts.push(ctx);
  178. }
  179. }
  180. }
  181. // #ifdef APP-PLUS
  182. // APP 上避免 video 错位需要延时渲染
  183. setTimeout(() => {
  184. this.loadVideo = true;
  185. }, 1000)
  186. // #endif
  187. },
  188. play(e) {
  189. var contexts = this.top.videoContexts;
  190. if (contexts.length > 1 && this.top.autopause)
  191. for (var i = contexts.length; i--;)
  192. if (contexts[i].id != e.currentTarget.dataset.id)
  193. contexts[i].pause();
  194. },
  195. imgtap(e) {
  196. var attrs = e.currentTarget.dataset.attrs;
  197. if (!attrs.ignore) {
  198. var preview = this.preview,
  199. data = {
  200. id: e.target.id,
  201. src: attrs.src,
  202. ignore: () => preview = false
  203. };
  204. globalData.Parser.onImgtap && globalData.Parser.onImgtap(data);
  205. this.top.$emit('imgtap', data);
  206. if (preview) {
  207. var urls = this.top.imgList,
  208. current = urls[attrs.i] ? parseInt(attrs.i) : (urls = [attrs.src], 0);
  209. uni.previewImage({
  210. current,
  211. urls
  212. })
  213. }
  214. }
  215. },
  216. loadImg(e) {
  217. var i = e.currentTarget.dataset.i;
  218. if (this.lazyLoad && !this.ctrl[i]) {
  219. // #ifdef QUICKAPP-WEBVIEW
  220. this.$set(this.ctrl, i, 0);
  221. this.$nextTick(function() {
  222. // #endif
  223. // #ifndef APP-PLUS
  224. this.$set(this.ctrl, i, 1);
  225. // #endif
  226. // #ifdef QUICKAPP-WEBVIEW
  227. })
  228. // #endif
  229. } else if (this.loading && this.ctrl[i] != 2) {
  230. // #ifdef QUICKAPP-WEBVIEW
  231. this.$set(this.ctrl, i, 0);
  232. this.$nextTick(function() {
  233. // #endif
  234. this.$set(this.ctrl, i, 2);
  235. // #ifdef QUICKAPP-WEBVIEW
  236. })
  237. // #endif
  238. }
  239. },
  240. linkpress(e) {
  241. var jump = true,
  242. attrs = e.currentTarget.dataset.attrs;
  243. attrs.ignore = () => jump = false;
  244. globalData.Parser.onLinkpress && globalData.Parser.onLinkpress(attrs);
  245. this.top.$emit('linkpress', attrs);
  246. if (jump) {
  247. // #ifdef MP
  248. if (attrs['app-id']) {
  249. return uni.navigateToMiniProgram({
  250. appId: attrs['app-id'],
  251. path: attrs.path
  252. })
  253. }
  254. // #endif
  255. if (attrs.href) {
  256. if (attrs.href[0] == '#') {
  257. if (this.top.useAnchor)
  258. this.top.navigateTo({
  259. id: attrs.href.substring(1)
  260. })
  261. } else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0) {
  262. // #ifdef APP-PLUS
  263. plus.runtime.openWeb(attrs.href);
  264. // #endif
  265. // #ifndef APP-PLUS
  266. uni.setClipboardData({
  267. data: attrs.href,
  268. success: () =>
  269. uni.showToast({
  270. title: '链接已复制'
  271. })
  272. })
  273. // #endif
  274. } else
  275. uni.navigateTo({
  276. url: attrs.href,
  277. fail() {
  278. uni.switchTab({
  279. url: attrs.href,
  280. })
  281. }
  282. })
  283. }
  284. }
  285. },
  286. error(e) {
  287. var target = e.currentTarget,
  288. source = target.dataset.source,
  289. i = target.dataset.i;
  290. if (source == 'video' || source == 'audio') {
  291. // 加载其他 source
  292. var index = this.ctrl[i] ? this.ctrl[i].i + 1 : 1;
  293. if (index < this.nodes[i].attrs.source.length)
  294. this.$set(this.ctrl, i, index);
  295. if (e.detail.__args__)
  296. e.detail = e.detail.__args__[0];
  297. } else if (errorImg && source == 'img') {
  298. this.top.imgList.setItem(target.dataset.index, errorImg);
  299. this.$set(this.ctrl, i, 3);
  300. }
  301. this.top && this.top.$emit('error', {
  302. source,
  303. target,
  304. errMsg: e.detail.errMsg
  305. });
  306. },
  307. _loadVideo(e) {
  308. this.$set(this.ctrl, e.target.dataset.i, 0);
  309. },
  310. use(item) {
  311. return !item.c && !inline[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
  312. }
  313. }
  314. }
  315. </script>
  316. <style>
  317. /* 在这里引入自定义样式 */
  318. /* 链接和图片效果 */
  319. ._a {
  320. display: inline;
  321. padding: 1.5px 0 1.5px 0;
  322. color: #366092;
  323. word-break: break-all;
  324. }
  325. ._hover {
  326. text-decoration: underline;
  327. opacity: 0.7;
  328. }
  329. ._img {
  330. display: inline-block;
  331. max-width: 100%;
  332. overflow: hidden;
  333. }
  334. /* #ifdef MP-WEIXIN */
  335. :host {
  336. display: inline;
  337. }
  338. /* #endif */
  339. /* #ifndef MP-ALIPAY || APP-PLUS */
  340. .interlayer {
  341. display: inherit;
  342. flex-direction: inherit;
  343. flex-wrap: inherit;
  344. align-content: inherit;
  345. align-items: inherit;
  346. justify-content: inherit;
  347. width: 100%;
  348. white-space: inherit;
  349. }
  350. /* #endif */
  351. ._b,
  352. ._strong {
  353. font-weight: bold;
  354. }
  355. /* #ifndef MP-ALIPAY */
  356. ._blockquote,
  357. ._div,
  358. ._p,
  359. ._ol,
  360. ._ul,
  361. ._li {
  362. display: block;
  363. }
  364. /* #endif */
  365. ._code {
  366. font-family: monospace;
  367. }
  368. ._del {
  369. text-decoration: line-through;
  370. }
  371. ._em,
  372. ._i {
  373. font-style: italic;
  374. }
  375. ._h1 {
  376. font-size: 2em;
  377. }
  378. ._h2 {
  379. font-size: 1.5em;
  380. }
  381. ._h3 {
  382. font-size: 1.17em;
  383. }
  384. ._h5 {
  385. font-size: 0.83em;
  386. }
  387. ._h6 {
  388. font-size: 0.67em;
  389. }
  390. ._h1,
  391. ._h2,
  392. ._h3,
  393. ._h4,
  394. ._h5,
  395. ._h6 {
  396. display: block;
  397. font-weight: bold;
  398. }
  399. ._image {
  400. display: block;
  401. width: 100%;
  402. height: 360px;
  403. margin-top: -360px;
  404. opacity: 0;
  405. }
  406. ._ins {
  407. text-decoration: underline;
  408. }
  409. ._li {
  410. flex: 1;
  411. width: 0;
  412. }
  413. ._ol-bef {
  414. width: 36px;
  415. margin-right: 5px;
  416. text-align: right;
  417. }
  418. ._ul-bef {
  419. display: block;
  420. margin: 0 12px 0 23px;
  421. line-height: normal;
  422. }
  423. ._ol-bef,
  424. ._ul-bef {
  425. flex: none;
  426. user-select: none;
  427. }
  428. ._ul-p1 {
  429. display: inline-block;
  430. width: 0.3em;
  431. height: 0.3em;
  432. overflow: hidden;
  433. line-height: 0.3em;
  434. }
  435. ._ul-p2 {
  436. display: inline-block;
  437. width: 0.23em;
  438. height: 0.23em;
  439. border: 0.05em solid black;
  440. border-radius: 50%;
  441. }
  442. ._q::before {
  443. content: '"';
  444. }
  445. ._q::after {
  446. content: '"';
  447. }
  448. ._sub {
  449. font-size: smaller;
  450. vertical-align: sub;
  451. }
  452. ._sup {
  453. font-size: smaller;
  454. vertical-align: super;
  455. }
  456. /* #ifdef MP-ALIPAY || APP-PLUS || QUICKAPP-WEBVIEW */
  457. ._abbr,
  458. ._b,
  459. ._code,
  460. ._del,
  461. ._em,
  462. ._i,
  463. ._ins,
  464. ._label,
  465. ._q,
  466. ._span,
  467. ._strong,
  468. ._sub,
  469. ._sup {
  470. display: inline;
  471. }
  472. /* #endif */
  473. /* #ifdef MP-WEIXIN || MP-QQ */
  474. .__bdo,
  475. .__bdi,
  476. .__ruby,
  477. .__rt {
  478. display: inline-block;
  479. }
  480. /* #endif */
  481. ._video {
  482. position: relative;
  483. display: inline-block;
  484. width: 300px;
  485. height: 225px;
  486. background-color: black;
  487. }
  488. ._video::after {
  489. position: absolute;
  490. top: 50%;
  491. left: 50%;
  492. margin: -15px 0 0 -15px;
  493. content: '';
  494. border-color: transparent transparent transparent white;
  495. border-style: solid;
  496. border-width: 15px 0 15px 30px;
  497. }
  498. </style>