trees.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <template>
  2. <view style="display: inherit;white-space: inherit;">
  3. <block v-for='(item, index) in nodes' v-bind:key='index'>
  4. <!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS || MP-ALIPAY-->
  5. <block v-if="handler.isContinue(item)">
  6. <!--#endif-->
  7. <!--#ifdef MP-BAIDU || MP-TOUTIAO || H5-->
  8. <block v-if="!item.continue">
  9. <!--#endif-->
  10. <!--图片-->
  11. <!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
  12. <rich-text v-if="item.name=='img'" :id="item.attrs.id||''" class="img" :style="'text-indent:0;'+handler.getStyle(item.attrs.style,'inline-block')"
  13. :nodes='handler.setImgStyle(item,imgMode,imgLoad)' :data-ignore='item.attrs.ignore' :data-src='item.attrs.src'
  14. :data-current='item.current' @tap='previewEvent' />
  15. <!--#endif-->
  16. <!--#ifdef MP-ALIPAY-->
  17. <rich-text v-if="item.name=='img'" :id="item.attrs.id||''" :style="'text-indent:0;'+handler.getStyle(item.attrs.style,'inline-block')"
  18. :nodes='handler.setImgStyle(item,imgMode)' :data-ignore='item.attrs.ignore' :data-src='item.attrs.src'
  19. :data-current='item.current' @tap='previewEvent' />
  20. <!--#endif-->
  21. <!--#ifdef MP-BAIDU || MP-TOUTIAO || H5-->
  22. <rich-text v-if="item.name=='img'" :id="item.attrs.id||''" :style="'text-indent:0;'+item.attrs.containStyle" :nodes='[item]' :data-ignore='item.attrs.ignore'
  23. :data-src='item.attrs.src' :data-current='item.current' @tap='previewEvent' />
  24. <!--#endif-->
  25. <!--文本-->
  26. <!--#ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS-->
  27. <block v-else-if="item.type=='text'">
  28. <text v-if="!item.decode" decode>{{item.text}}</text>
  29. <rich-text v-else style="display:inline-block" :nodes="[item]"></rich-text>
  30. </block>
  31. <!--#endif-->
  32. <!--#ifdef MP-ALIPAY-->
  33. <text v-else-if="item.type=='text'" decode>{{item.text}}</text>
  34. <!--#endif-->
  35. <text v-else-if="item.name=='br'">\n</text>
  36. <!--视频-->
  37. <block v-else-if="item.name=='video'">
  38. <!--#ifdef APP-PLUS-->
  39. <view v-if="(!loadVideo||item.attrs.id[item.attrs.id.length-1]>'3')&&(!controls[item.attrs.id]||!controls[item.attrs.id].play)"
  40. :class="'pvideo '+(item.attrs.class||'')" :style="item.attrs.style" :data-id="item.attrs.id" @tap="_loadVideo">
  41. <view class="video-triangle"></view>
  42. </view>
  43. <!--#endif-->
  44. <!--#ifndef APP-PLUS-->
  45. <view v-if="item.attrs.id[item.attrs.id.length-1]>'3'&&(!controls[item.attrs.id]||!controls[item.attrs.id].play)"
  46. :class="'pvideo '+(item.attrs.class||'')" :style="item.attrs.style" :data-id="item.attrs.id" @tap="_loadVideo">
  47. <view class="video-triangle"></view>
  48. </view>
  49. <!--#endif-->
  50. <video v-else :src='controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index]:item.attrs.src' :id="item.attrs.id||''"
  51. :loop='item.attrs.loop' :controls='item.attrs.controls' :autoplay="item.attrs.autoplay||(controls[item.attrs.id]&&controls[item.attrs.id].play)"
  52. :unit-id="item.attrs['unit-id']" :class="'v '+(item.attrs.class||'')" :muted="item.attrs.muted" :style="item.attrs.style"
  53. :data-id="item.attrs.id" :data-source="item.attrs.source" @play='playEvent' @error="videoError" />
  54. </block>
  55. <!--音频-->
  56. <audio v-else-if="item.name=='audio'" :src='controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index]:item.attrs.src'
  57. :id="item.attrs.id||''" :loop='item.attrs.loop' :controls='item.attrs.controls' :poster='item.attrs.poster' :name='item.attrs.name'
  58. :author='item.attrs.author' :class="item.attrs.class||''" :style="item.attrs.style" :data-id="item.attrs.id"
  59. :data-source="item.attrs.source" @error="audioError" />
  60. <!--链接-->
  61. <view v-else-if="item.name=='a'" :class="'a '+(item.attrs.class||'')" :style="item.attrs.style" :data-href='item.attrs.href'
  62. hover-class="navigator-hover" :hover-start-time="25" :hover-stay-time="300" @tap="tapEvent">
  63. <!--#ifdef H5-->
  64. <trees :nodes="item.children" :imgMode="imgMode" />
  65. <!--#endif-->
  66. <!--#ifndef H5-->
  67. <trees :nodes="item.children" :imgMode="imgMode" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
  68. <!--#endif-->
  69. </view>
  70. <!--广告-->
  71. <!--#ifdef MP-WEIXIN || MP-QQ-->
  72. <ad v-else-if="item.name=='ad'" :unit-id="item.attrs['unit-id']" :class="item.attrs.class||''" :style="item.attrs.style"
  73. @error="adError"></ad>
  74. <!--#endif-->
  75. <!--#ifdef MP-BAIDU-->
  76. <ad v-else-if="item.name=='ad'" :appid="item.attrs.appid" :apid="item.attrs.apid" :type="item.attrs.type" :class="item.attrs.class||''"
  77. :style="item.attrs.style" @error="adError"></ad>
  78. <!--#endif-->
  79. <!--富文本-->
  80. <!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
  81. <rich-text v-else :id="item.attrs.id||''" :class="item.name" :style="''+handler.getStyle(item.attrs.style,'block')" :nodes="handler.setStyle(item)" />
  82. <!--#endif-->
  83. <!--#ifdef MP-BAIDU || MP-TOUTIAO || H5-->
  84. <rich-text v-else :id="item.attrs.id||''" :class="item.name" :style="item.attrs?item.attrs.containStyle:''" :nodes="[item]" />
  85. <!--#endif-->
  86. </block>
  87. <!--#ifdef MP-ALIPAY || H5-->
  88. <view v-else :id="item.attrs.id||''" :class="item.name+' '+(item.attrs.class||'')" :style="item.attrs.style">
  89. <trees :nodes="item.children" :imgMode="imgMode" />
  90. </view>
  91. <!--#endif-->
  92. <!--#ifndef MP-ALIPAY || H5-->
  93. <trees v-else :id="item.attrs.id||''" :class="item.name+' '+(item.attrs.class||'')" :style="item.attrs.style" :nodes="item.children"
  94. :imgMode="imgMode" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
  95. <!--#endif-->
  96. </block>
  97. </view>
  98. </template>
  99. <script module="handler" lang="wxs" src="./handler.wxs"></script>
  100. <script module="handler" lang="sjs" src="./handler.sjs"></script>
  101. <script>
  102. import trees from "./trees"
  103. export default {
  104. components: {
  105. trees
  106. },
  107. name: 'trees',
  108. data() {
  109. return {
  110. controls: {},
  111. // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
  112. imgLoad: false
  113. // #endif
  114. }
  115. },
  116. props: {
  117. nodes: {
  118. type: Array,
  119. default: []
  120. },
  121. // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
  122. lazyLoad: {
  123. type: Boolean,
  124. default: false
  125. },
  126. // #endif
  127. // #ifdef APP-PLUS
  128. loadVideo: {
  129. type: Boolean,
  130. default: false
  131. },
  132. // #endif
  133. imgMode: {
  134. type: String,
  135. default: "default"
  136. }
  137. },
  138. mounted() {
  139. // 获取顶层组件
  140. this._top = this.$parent;
  141. while (this._top.$options.name != 'parser') {
  142. if (this._top._top) {
  143. this._top = this._top._top;
  144. break;
  145. }
  146. this._top = this._top.$parent;
  147. }
  148. },
  149. // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
  150. beforeDestroy() {
  151. if (this._observer)
  152. this._observer.disconnect();
  153. },
  154. // #endif
  155. methods: {
  156. // #ifndef MP-ALIPAY
  157. playEvent(e) {
  158. if (this._top.videoContext.length > 1 && this._top.autopause) {
  159. for (let video of this._top.videoContext) {
  160. if (video.id == e.currentTarget.dataset.id) continue;
  161. video.context.pause();
  162. }
  163. }
  164. },
  165. // #endif
  166. previewEvent(e) {
  167. if (!e.currentTarget.dataset.ignore) {
  168. var preview = true;
  169. this._top.$emit('imgtap', {
  170. id: e.target.id,
  171. src: e.currentTarget.dataset.src,
  172. ignore: () => preview = false
  173. });
  174. if (preview && this._top.autopreview) {
  175. uni.previewImage({
  176. current: parseInt(e.currentTarget.dataset.current),
  177. urls: this._top.imgList.length ? this._top.imgList : [e.currentTarget.dataset.src]
  178. });
  179. }
  180. }
  181. },
  182. tapEvent(e) {
  183. var jump = true;
  184. this._top.$emit('linkpress', {
  185. href: e.currentTarget.dataset.href,
  186. ignore: () => jump = false
  187. });
  188. if (jump && e.currentTarget.dataset.href) {
  189. if (e.currentTarget.dataset.href[0]) {
  190. if (this._top.useAnchor)
  191. this._top.navigateTo({
  192. id: e.currentTarget.dataset.href.substring(1)
  193. })
  194. } else if (/^http/.test(e.currentTarget.dataset.href)) {
  195. if (this._top.autocopy) {
  196. // #ifndef H5
  197. uni.setClipboardData({
  198. data: e.currentTarget.dataset.href,
  199. success() {
  200. uni.showToast({
  201. title: '链接已复制'
  202. });
  203. }
  204. });
  205. // #endif
  206. // #ifdef H5
  207. window.location.href = e.currentTarget.dataset.href;
  208. // #endif
  209. }
  210. } else
  211. uni.navigateTo({
  212. url: e.currentTarget.dataset.href
  213. })
  214. }
  215. },
  216. triggerError(source, target, errMsg, errCode) {
  217. this._top.$emit('error', {
  218. source,
  219. target,
  220. errMsg,
  221. errCode
  222. });
  223. },
  224. loadSource(currentTarget) {
  225. if (!this.controls[currentTarget.id] && currentTarget.source.length > 1) {
  226. this.$set(this.controls, currentTarget.id, {
  227. play: false,
  228. index: 1
  229. })
  230. } else if (this.controls[currentTarget.id] && currentTarget.source.length > this.controls[
  231. currentTarget.id].index + 1) {
  232. this.$set(this.controls[currentTarget.id], "index", this.controls[currentTarget.id].index + 1);
  233. }
  234. },
  235. adError(e) {
  236. this.triggerError("ad", e.currentTarget, "", e.detail.errorCode);
  237. },
  238. videoError(e) {
  239. this.loadSource(e.currentTarget.dataset);
  240. this.triggerError("video", e.currentTarget, e.detail.errMsg);
  241. },
  242. audioError(e) {
  243. this.loadSource(e.currentTarget.dataset);
  244. this.triggerError("audio", e.currentTarget, e.detail.errMsg);
  245. },
  246. _loadVideo(e) {
  247. this.$set(this.controls, e.currentTarget.dataset.id, {
  248. play: true,
  249. index: 0
  250. })
  251. }
  252. }
  253. }
  254. </script>
  255. <style>
  256. /* 可以在这里引入自定义的外部样式 */
  257. /* 链接受到点击的hover-class,可自定义修改 */
  258. .navigator-hover {
  259. opacity: 0.7;
  260. text-decoration: underline;
  261. }
  262. /* 以下内容不建议修改 */
  263. :host {
  264. display: inherit;
  265. float: inherit;
  266. }
  267. .a {
  268. display: inline;
  269. color: #366092;
  270. }
  271. /* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */
  272. .sub,
  273. .sup,
  274. .bdo,
  275. .bdi,
  276. .ruby,
  277. .rt {
  278. display: inline-block !important;
  279. }
  280. /* #endif */
  281. .div,
  282. .blockquote,
  283. .p {
  284. display: block;
  285. }
  286. .b,
  287. .strong {
  288. display: inline;
  289. font-weight: bold;
  290. }
  291. .em,
  292. .i {
  293. display: inline;
  294. font-style: italic;
  295. }
  296. .del {
  297. display: inline;
  298. text-decoration: line-through;
  299. }
  300. .ins {
  301. display: inline;
  302. text-decoration: underline;
  303. }
  304. .code {
  305. display: inline;
  306. font-family: monospace;
  307. }
  308. .big {
  309. font-size: 1.2em;
  310. display: inline;
  311. }
  312. .small {
  313. font-size: 0.8em;
  314. display: inline;
  315. }
  316. .q,
  317. .span,
  318. .label,
  319. .abbr {
  320. display: inline;
  321. }
  322. .q::before {
  323. content: '"';
  324. }
  325. .q::after {
  326. content: '"';
  327. }
  328. .pvideo {
  329. background-color: black;
  330. width: 300px;
  331. height: 225px;
  332. display: inline-block;
  333. position: relative;
  334. }
  335. .video-triangle {
  336. border-width: 15px 0 15px 30px;
  337. border-style: solid;
  338. border-color: transparent transparent transparent white;
  339. position: absolute;
  340. left: 50%;
  341. top: 50%;
  342. margin: -15px 0 0 -15px;
  343. }
  344. </style>