uview-plus 3.1.22 → 3.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,500 +1,576 @@
1
- <template>
2
- <view :id="attrs.id" :class="'_'+name+' '+attrs.class" :style="attrs.style">
3
- <block v-for="(n, i) in childs" v-bind:key="i">
4
- <!-- 图片 -->
5
- <!-- 占位图 -->
6
- <image v-if="n.name=='img'&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
7
- <!-- 显示图片 -->
8
- <!-- #ifdef H5 || APP-PLUS -->
9
- <img v-if="n.name=='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]==-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap"/>
10
- <!-- #endif -->
11
- <!-- #ifndef H5 || APP-PLUS -->
12
- <image v-if="n.name=='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]==-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="n.h?'':'widthFix'" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
13
- <!-- #endif -->
14
- <!-- 文本 -->
15
- <!-- #ifndef MP-BAIDU -->
16
- <text v-else-if="n.type=='text'" decode>{{n.text}}</text>
17
- <!-- #endif -->
18
- <text v-else-if="n.name=='br'">\n</text>
19
- <!-- 链接 -->
20
- <view v-else-if="n.name=='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
21
- <node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
22
- </view>
23
- <!-- 视频 -->
24
- <!-- #ifdef APP-PLUS -->
25
- <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" />
26
- <!-- #endif -->
27
- <!-- #ifndef APP-PLUS -->
28
- <video v-else-if="n.name=='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
29
- <!-- #endif -->
30
- <!-- #ifdef H5 || APP-PLUS -->
31
- <iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
32
- <embed v-else-if="n.name=='embed'" :style="n.attrs.style" :src="n.attrs.src" />
33
- <!-- #endif -->
34
- <!-- #ifndef MP-TOUTIAO -->
35
- <!-- 音频 -->
36
- <!-- <audio v-else-if="n.name=='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" /> -->
37
- <!-- #endif -->
38
- <view v-else-if="(n.name=='table'&&n.c)||n.name=='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
39
- <node v-if="n.name=='li'" :childs="n.children" :opts="opts" />
40
- <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
41
- <node v-if="tbody.name=='td'||tbody.name=='th'" :childs="tbody.children" :opts="opts" />
42
- <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
43
- <view v-if="tr.name=='td'||tr.name=='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
44
- <node :childs="tr.children" :opts="opts" />
45
- </view>
46
- <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
47
- <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
48
- <node :childs="td.children" :opts="opts" />
49
- </view>
50
- </view>
51
- </block>
52
- </view>
53
- </view>
54
-
55
- <!-- 富文本 -->
56
- <!-- #ifdef H5 || MP-WEIXIN || MP-QQ || APP-PLUS || MP-360 -->
57
- <rich-text v-else-if="handler.use(n)" :id="n.attrs.id" :style="n.f" :nodes="[n]" />
58
- <!-- #endif -->
59
- <!-- #ifndef H5 || MP-WEIXIN || MP-QQ || APP-PLUS || MP-360 -->
60
- <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="n.f+';display:inline'" :preview="false" :nodes="[n]" />
61
- <!-- #endif -->
62
- <!-- 继续递归 -->
63
- <view v-else-if="n.c==2" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
64
- <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
65
- </view>
66
- <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
67
- </block>
68
- </view>
69
- </template>
70
- <script module="handler" lang="wxs">
71
- // 行内标签列表
72
- var inlineTags = {
73
- abbr: true,
74
- b: true,
75
- big: true,
76
- code: true,
77
- del: true,
78
- em: true,
79
- i: true,
80
- ins: true,
81
- label: true,
82
- q: true,
83
- small: true,
84
- span: true,
85
- strong: true,
86
- sub: true,
87
- sup: true
88
- }
89
- /**
90
- * @description 是否使用 rich-text 显示剩余内容
91
- */
92
- module.exports = {
93
- use: function (item) {
94
- // 微信和 QQ 的 rich-text inline 布局无效
95
- if (inlineTags[item.name] || (item.attrs.style || '').indexOf('display:inline') != -1)
96
- return false
97
- return !item.c
98
- }
99
- }
100
- </script>
101
- <script>
102
-
103
- import node from './node'
104
- export default {
105
- name: 'node',
106
- // #ifdef MP-WEIXIN
107
- options: {
108
- virtualHost: true
109
- },
110
- // #endif
111
- data() {
112
- return {
113
- ctrl: {}
114
- }
115
- },
116
- props: {
117
- name: String,
118
- attrs: {
119
- type: Object,
120
- default() {
121
- return {}
122
- }
123
- },
124
- childs: Array,
125
- opts: Array
126
- },
127
- components: {
128
- // #ifdef VUE2
129
- node
130
- // #endif
131
- },
132
- mounted() {
133
- for (this.root = this.$parent; this.root.$options.name != 'mp-html'; this.root = this.root.$parent);
134
- // #ifdef H5 || APP-PLUS
135
- if (this.opts[0]) {
136
- for (var i = this.childs.length; i--;)
137
- if (this.childs[i].name == 'img')
138
- break
139
- if (i != -1) {
140
- this.observer = uni.createIntersectionObserver(this).relativeToViewport({
141
- top: 500,
142
- bottom: 500
143
- })
144
- this.observer.observe('._img', res => {
145
- if (res.intersectionRatio) {
146
- this.$set(this.ctrl, 'load', 1)
147
- this.observer.disconnect()
148
- }
149
- })
150
- }
151
- }
152
- // #endif
153
- },
154
- beforeDestroy() {
155
- // #ifdef H5 || APP-PLUS
156
- if (this.observer)
157
- this.observer.disconnect()
158
- // #endif
159
- },
160
- methods:{
161
- // #ifdef MP-WEIXIN
162
- toJSON() { },
163
- // #endif
164
- /**
165
- * @description 播放视频事件
166
- * @param {Event} e
167
- */
168
- play(e) {
169
- // #ifndef APP-PLUS
170
- if (this.root.pauseVideo) {
171
- var flag = false, id = e.target.id
172
- for (var i = this.root._videos.length; i--;) {
173
- if (this.root._videos[i].id == id)
174
- flag = true
175
- else
176
- this.root._videos[i].pause() // 自动暂停其他视频
177
- }
178
- // 将自己加入列表
179
- if (!flag) {
180
- var ctx = uni.createVideoContext(id
181
- // #ifndef MP-BAIDU
182
- , this
183
- // #endif
184
- )
185
- ctx.id = id
186
- this.root._videos.push(ctx)
187
- }
188
- }
189
- // #endif
190
- },
191
-
192
- /**
193
- * @description 图片点击事件
194
- * @param {Event} e
195
- */
196
- imgTap(e) {
197
- var node = this.childs[e.currentTarget.dataset.i]
198
- if (node.a)
199
- return this.linkTap(node.a)
200
- if (node.attrs.ignore)
201
- return
202
- // #ifdef H5 || APP-PLUS
203
- node.attrs.src = node.attrs.src || node.attrs['data-src']
204
- // #endif
205
- this.root.$emit('imgTap', node.attrs)
206
- // 自动预览图片
207
- if (this.root.previewImg)
208
- uni.previewImage({
209
- current: parseInt(node.attrs.i),
210
- urls: this.root.imgList
211
- })
212
- },
213
-
214
- /**
215
- * @description 图片长按
216
- */
217
- imgLongTap(e) {
218
- // #ifdef APP-PLUS
219
- var attrs = this.childs[e.currentTarget.dataset.i].attrs
220
- if (!attrs.ignore)
221
- uni.showActionSheet({
222
- itemList: ['保存图片'],
223
- success: () => {
224
- uni.downloadFile({
225
- url: this.root.imgList[attrs.i],
226
- success: res => {
227
- uni.saveImageToPhotosAlbum({
228
- filePath: res.tempFilePath,
229
- success() {
230
- uni.showToast({
231
- title: '保存成功'
232
- })
233
- }
234
- })
235
- }
236
- })
237
- }
238
- })
239
- // #endif
240
- },
241
-
242
- /**
243
- * @description 图片加载完成事件
244
- * @param {Event} e
245
- */
246
- imgLoad(e) {
247
- var i = e.currentTarget.dataset.i
248
- // #ifndef H5 || APP-PLUS
249
- // 设置原宽度
250
- if (!this.childs[i].w)
251
- this.$set(this.ctrl, i, e.detail.width)
252
- else
253
- // #endif
254
- // 加载完毕,取消加载中占位图
255
- if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] == -1)
256
- this.$set(this.ctrl, i, 1)
257
- },
258
-
259
- /**
260
- * @description 链接点击事件
261
- * @param {Event} e
262
- */
263
- linkTap(e) {
264
- var attrs = e.currentTarget ? this.childs[e.currentTarget.dataset.i].attrs : e,
265
- href = attrs.href
266
- this.root.$emit('linkTap', attrs)
267
- if (href) {
268
- // 跳转锚点
269
- if (href[0] == '#')
270
- this.root.navigateTo(href.substring(1)).catch(() => { })
271
- // 复制外部链接
272
- else if (href.includes('://')) {
273
- if (this.root.copyLink) {
274
- // #ifdef H5
275
- window.open(href)
276
- // #endif
277
- // #ifdef MP
278
- uni.setClipboardData({
279
- data: href,
280
- success: () =>
281
- uni.showToast({
282
- title: '链接已复制'
283
- })
284
- })
285
- // #endif
286
- // #ifdef APP-PLUS
287
- plus.runtime.openWeb(href)
288
- // #endif
289
- }
290
- }
291
- // 跳转页面
292
- else
293
- uni.navigateTo({
294
- url: href,
295
- fail() {
296
- uni.switchTab({
297
- url: href,
298
- fail() { }
299
- })
300
- }
301
- })
302
- }
303
- },
304
-
305
- /**
306
- * @description 错误事件
307
- * @param {Event} e
308
- */
309
- mediaError(e) {
310
- var i = e.currentTarget.dataset.i,
311
- node = this.childs[i]
312
- // 加载其他源
313
- if (node.name == 'video' || node.name == 'audio') {
314
- var index = (this.ctrl[i] || 0) + 1
315
- if (index > node.src.length)
316
- index = 0
317
- if (index < node.src.length)
318
- return this.$set(this.ctrl, i, index)
319
- }
320
- // 显示错误占位图
321
- else if (node.name == 'img' && this.opts[2])
322
- this.$set(this.ctrl, i, -1)
323
- if (this.root)
324
- this.root.$emit('error', {
325
- source: node.name,
326
- attrs: node.attrs,
327
- errMsg: e.detail.errMsg
328
- })
329
- }
330
- }
331
- }
332
- </script>
333
- <style>
334
- /* a 标签默认效果 */
335
- ._a {
336
- padding: 1.5px 0 1.5px 0;
337
- color: #366092;
338
- word-break: break-all;
339
- }
340
-
341
- /* a 标签点击态效果 */
342
- ._hover {
343
- text-decoration: underline;
344
- opacity: 0.7;
345
- }
346
-
347
- /* 图片默认效果 */
348
- ._img {
349
- max-width: 100%;
350
- -webkit-touch-callout: none;
351
- }
352
-
353
- /* 内部样式 */
354
-
355
- ._b,
356
- ._strong {
357
- font-weight: bold;
358
- }
359
-
360
- ._code {
361
- font-family: monospace;
362
- }
363
-
364
- ._del {
365
- text-decoration: line-through;
366
- }
367
-
368
- ._em,
369
- ._i {
370
- font-style: italic;
371
- }
372
-
373
- ._h1 {
374
- font-size: 2em;
375
- }
376
-
377
- ._h2 {
378
- font-size: 1.5em;
379
- }
380
-
381
- ._h3 {
382
- font-size: 1.17em;
383
- }
384
-
385
- ._h5 {
386
- font-size: 0.83em;
387
- }
388
-
389
- ._h6 {
390
- font-size: 0.67em;
391
- }
392
-
393
- ._h1,
394
- ._h2,
395
- ._h3,
396
- ._h4,
397
- ._h5,
398
- ._h6 {
399
- display: block;
400
- font-weight: bold;
401
- }
402
-
403
- ._image {
404
- height: 1px;
405
- }
406
-
407
- ._ins {
408
- text-decoration: underline;
409
- }
410
-
411
- ._li {
412
- display: list-item;
413
- }
414
-
415
- ._ol {
416
- list-style-type: decimal;
417
- }
418
-
419
- ._ol,
420
- ._ul {
421
- display: block;
422
- padding-left: 40px;
423
- margin: 1em 0;
424
- }
425
-
426
- ._q::before {
427
- content: '"';
428
- }
429
-
430
- ._q::after {
431
- content: '"';
432
- }
433
-
434
- ._sub {
435
- font-size: smaller;
436
- vertical-align: sub;
437
- }
438
-
439
- ._sup {
440
- font-size: smaller;
441
- vertical-align: super;
442
- }
443
-
444
- ._thead,
445
- ._tbody,
446
- ._tfoot {
447
- display: table-row-group;
448
- }
449
-
450
- ._tr {
451
- display: table-row;
452
- }
453
-
454
- ._td,
455
- ._th {
456
- display: table-cell;
457
- vertical-align: middle;
458
- }
459
-
460
- ._th {
461
- font-weight: bold;
462
- text-align: center;
463
- }
464
-
465
- ._ul {
466
- list-style-type: disc;
467
- }
468
-
469
- ._ul ._ul {
470
- margin: 0;
471
- list-style-type: circle;
472
- }
473
-
474
- ._ul ._ul ._ul {
475
- list-style-type: square;
476
- }
477
-
478
- ._abbr,
479
- ._b,
480
- ._code,
481
- ._del,
482
- ._em,
483
- ._i,
484
- ._ins,
485
- ._label,
486
- ._q,
487
- ._span,
488
- ._strong,
489
- ._sub,
490
- ._sup {
491
- display: inline;
492
- }
493
-
494
- /* #ifdef APP-PLUS */
495
- ._video {
496
- width: 300px;
497
- height: 225px;
498
- }
499
- /* #endif */
500
- </style>
1
+ <template>
2
+ <view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
3
+ <block v-for="(n, i) in childs" v-bind:key="i">
4
+ <!-- 图片 -->
5
+ <!-- 占位图 -->
6
+ <image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
7
+ <!-- 显示图片 -->
8
+ <!-- #ifdef H5 || (APP-PLUS && VUE2) -->
9
+ <img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
10
+ <!-- #endif -->
11
+ <!-- #ifndef H5 || (APP-PLUS && VUE2) -->
12
+ <!-- 表格中的图片,使用 rich-text 防止大小不正确 -->
13
+ <rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="'<img class=\'_img\' style=\''+n.attrs.style+'\' src=\''+n.attrs.src+'\'>'" :data-i="i" @tap.stop="imgTap" />
14
+ <!-- #endif -->
15
+ <!-- #ifndef H5 || APP-PLUS -->
16
+ <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
17
+ <!-- #endif -->
18
+ <!-- #ifdef APP-PLUS && VUE3 -->
19
+ <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
20
+ <!-- #endif -->
21
+ <!-- 文本 -->
22
+ <!-- #ifdef MP-WEIXIN -->
23
+ <text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
24
+ <!-- #endif -->
25
+ <!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
26
+ <text v-else-if="n.text" decode>{{n.text}}</text>
27
+ <!-- #endif -->
28
+ <text v-else-if="n.name==='br'">\n</text>
29
+ <!-- 链接 -->
30
+ <view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
31
+ <node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
32
+ </view>
33
+ <!-- 视频 -->
34
+ <!-- #ifdef APP-PLUS -->
35
+ <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
36
+ <!-- #endif -->
37
+ <!-- #ifndef APP-PLUS -->
38
+ <video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
39
+ <!-- #endif -->
40
+ <!-- #ifdef H5 || APP-PLUS -->
41
+ <iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
42
+ <embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
43
+ <!-- #endif -->
44
+ <!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
45
+ <!-- 音频 -->
46
+ <audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
47
+ <!-- #endif -->
48
+ <view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
49
+ <node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
50
+ <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
51
+ <node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
52
+ <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
53
+ <view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
54
+ <node :childs="tr.children" :opts="opts" />
55
+ </view>
56
+ <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
57
+ <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
58
+ <node :childs="td.children" :opts="opts" />
59
+ </view>
60
+ </view>
61
+ </block>
62
+ </view>
63
+ </view>
64
+
65
+ <!-- 富文本 -->
66
+ <!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
67
+ <rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
68
+ <!-- #endif -->
69
+ <!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
70
+ <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="n.f+';display:inline'" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
71
+ <!-- #endif -->
72
+ <!-- 继续递归 -->
73
+ <view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
74
+ <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
75
+ </view>
76
+ <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
77
+ </block>
78
+ </view>
79
+ </template>
80
+ <script module="handler" lang="wxs">
81
+ // 行内标签列表
82
+ var inlineTags = {
83
+ abbr: true,
84
+ b: true,
85
+ big: true,
86
+ code: true,
87
+ del: true,
88
+ em: true,
89
+ i: true,
90
+ ins: true,
91
+ label: true,
92
+ q: true,
93
+ small: true,
94
+ span: true,
95
+ strong: true,
96
+ sub: true,
97
+ sup: true
98
+ }
99
+ /**
100
+ * @description 判断是否为行内标签
101
+ */
102
+ module.exports = {
103
+ isInline: function (tagName, style) {
104
+ return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
105
+ }
106
+ }
107
+ </script>
108
+ <script>
109
+
110
+ import node from './node'
111
+ export default {
112
+ name: 'node',
113
+ options: {
114
+ // #ifdef MP-WEIXIN
115
+ virtualHost: true,
116
+ // #endif
117
+ // #ifdef MP-TOUTIAO
118
+ addGlobalClass: false
119
+ // #endif
120
+ },
121
+ data () {
122
+ return {
123
+ ctrl: {},
124
+ // #ifdef MP-WEIXIN
125
+ isiOS: uni.getSystemInfoSync().system.includes('iOS')
126
+ // #endif
127
+ }
128
+ },
129
+ props: {
130
+ name: String,
131
+ attrs: {
132
+ type: Object,
133
+ default () {
134
+ return {}
135
+ }
136
+ },
137
+ childs: Array,
138
+ opts: Array
139
+ },
140
+ components: {
141
+
142
+ // #ifndef (H5 || APP-PLUS) && VUE3
143
+ node
144
+ // #endif
145
+ },
146
+ mounted () {
147
+ this.$nextTick(() => {
148
+ for (this.root = this.$parent; this.root.$options.name !== 'u-parse'; this.root = this.root.$parent);
149
+ })
150
+ // #ifdef H5 || APP-PLUS
151
+ if (this.opts[0]) {
152
+ let i
153
+ for (i = this.childs.length; i--;) {
154
+ if (this.childs[i].name === 'img') break
155
+ }
156
+ if (i !== -1) {
157
+ this.observer = uni.createIntersectionObserver(this).relativeToViewport({
158
+ top: 500,
159
+ bottom: 500
160
+ })
161
+ this.observer.observe('._img', res => {
162
+ if (res.intersectionRatio) {
163
+ this.$set(this.ctrl, 'load', 1)
164
+ this.observer.disconnect()
165
+ }
166
+ })
167
+ }
168
+ }
169
+ // #endif
170
+ },
171
+ beforeDestroy () {
172
+ // #ifdef H5 || APP-PLUS
173
+ if (this.observer) {
174
+ this.observer.disconnect()
175
+ }
176
+ // #endif
177
+ },
178
+ methods:{
179
+ // #ifdef MP-WEIXIN
180
+ toJSON () { return this },
181
+ // #endif
182
+ /**
183
+ * @description 播放视频事件
184
+ * @param {Event} e
185
+ */
186
+ play (e) {
187
+ this.root.$emit('play')
188
+ // #ifndef APP-PLUS
189
+ if (this.root.pauseVideo) {
190
+ let flag = false
191
+ const id = e.target.id
192
+ for (let i = this.root._videos.length; i--;) {
193
+ if (this.root._videos[i].id === id) {
194
+ flag = true
195
+ } else {
196
+ this.root._videos[i].pause() // 自动暂停其他视频
197
+ }
198
+ }
199
+ // 将自己加入列表
200
+ if (!flag) {
201
+ const ctx = uni.createVideoContext(id
202
+ // #ifndef MP-BAIDU
203
+ , this
204
+ // #endif
205
+ )
206
+ ctx.id = id
207
+ if (this.root.playbackRate) {
208
+ ctx.playbackRate(this.root.playbackRate)
209
+ }
210
+ this.root._videos.push(ctx)
211
+ }
212
+ }
213
+ // #endif
214
+ },
215
+
216
+ /**
217
+ * @description 图片点击事件
218
+ * @param {Event} e
219
+ */
220
+ imgTap (e) {
221
+ const node = this.childs[e.currentTarget.dataset.i]
222
+ if (node.a) {
223
+ this.linkTap(node.a)
224
+ return
225
+ }
226
+ if (node.attrs.ignore) return
227
+ // #ifdef H5 || APP-PLUS
228
+ node.attrs.src = node.attrs.src || node.attrs['data-src']
229
+ // #endif
230
+ this.root.$emit('imgtap', node.attrs)
231
+ // 自动预览图片
232
+ if (this.root.previewImg) {
233
+ uni.previewImage({
234
+ // #ifdef MP-WEIXIN
235
+ showmenu: this.root.showImgMenu,
236
+ // #endif
237
+ // #ifdef MP-ALIPAY
238
+ enablesavephoto: this.root.showImgMenu,
239
+ enableShowPhotoDownload: this.root.showImgMenu,
240
+ // #endif
241
+ current: parseInt(node.attrs.i),
242
+ urls: this.root.imgList
243
+ })
244
+ }
245
+ },
246
+
247
+ /**
248
+ * @description 图片长按
249
+ */
250
+ imgLongTap (e) {
251
+ // #ifdef APP-PLUS
252
+ const attrs = this.childs[e.currentTarget.dataset.i].attrs
253
+ if (this.opts[3] && !attrs.ignore) {
254
+ uni.showActionSheet({
255
+ itemList: ['保存图片'],
256
+ success: () => {
257
+ const save = path => {
258
+ uni.saveImageToPhotosAlbum({
259
+ filePath: path,
260
+ success () {
261
+ uni.showToast({
262
+ title: '保存成功'
263
+ })
264
+ }
265
+ })
266
+ }
267
+ if (this.root.imgList[attrs.i].startsWith('http')) {
268
+ uni.downloadFile({
269
+ url: this.root.imgList[attrs.i],
270
+ success: res => save(res.tempFilePath)
271
+ })
272
+ } else {
273
+ save(this.root.imgList[attrs.i])
274
+ }
275
+ }
276
+ })
277
+ }
278
+ // #endif
279
+ },
280
+
281
+ /**
282
+ * @description 图片加载完成事件
283
+ * @param {Event} e
284
+ */
285
+ imgLoad (e) {
286
+ const i = e.currentTarget.dataset.i
287
+ /* #ifndef H5 || (APP-PLUS && VUE2) */
288
+ if (!this.childs[i].w) {
289
+ // 设置原宽度
290
+ this.$set(this.ctrl, i, e.detail.width)
291
+ } else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
292
+ // 加载完毕,取消加载中占位图
293
+ this.$set(this.ctrl, i, 1)
294
+ }
295
+ this.checkReady()
296
+ },
297
+
298
+ /**
299
+ * @description 检查是否所有图片加载完毕
300
+ */
301
+ checkReady () {
302
+ if (!this.root.lazyLoad) {
303
+ this.root._unloadimgs -= 1
304
+ if (!this.root._unloadimgs) {
305
+ setTimeout(() => {
306
+ this.root.getRect().then(rect => {
307
+ this.root.$emit('ready', rect)
308
+ }).catch(() => {
309
+ this.root.$emit('ready', {})
310
+ })
311
+ }, 350)
312
+ }
313
+ }
314
+ },
315
+
316
+ /**
317
+ * @description 链接点击事件
318
+ * @param {Event} e
319
+ */
320
+ linkTap (e) {
321
+ const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
322
+ const attrs = node.attrs || e
323
+ const href = attrs.href
324
+ this.root.$emit('linktap', Object.assign({
325
+ innerText: this.root.getText(node.children || []) // 链接内的文本内容
326
+ }, attrs))
327
+ if (href) {
328
+ if (href[0] === '#') {
329
+ // 跳转锚点
330
+ this.root.navigateTo(href.substring(1)).catch(() => { })
331
+ } else if (href.split('?')[0].includes('://')) {
332
+ // 复制外部链接
333
+ if (this.root.copyLink) {
334
+ // #ifdef H5
335
+ window.open(href)
336
+ // #endif
337
+ // #ifdef MP
338
+ uni.setClipboardData({
339
+ data: href,
340
+ success: () =>
341
+ uni.showToast({
342
+ title: '链接已复制'
343
+ })
344
+ })
345
+ // #endif
346
+ // #ifdef APP-PLUS
347
+ plus.runtime.openWeb(href)
348
+ // #endif
349
+ }
350
+ } else {
351
+ // 跳转页面
352
+ uni.navigateTo({
353
+ url: href,
354
+ fail () {
355
+ uni.switchTab({
356
+ url: href,
357
+ fail () { }
358
+ })
359
+ }
360
+ })
361
+ }
362
+ }
363
+ },
364
+
365
+ /**
366
+ * @description 错误事件
367
+ * @param {Event} e
368
+ */
369
+ mediaError (e) {
370
+ const i = e.currentTarget.dataset.i
371
+ const node = this.childs[i]
372
+ // 加载其他源
373
+ if (node.name === 'video' || node.name === 'audio') {
374
+ let index = (this.ctrl[i] || 0) + 1
375
+ if (index > node.src.length) {
376
+ index = 0
377
+ }
378
+ if (index < node.src.length) {
379
+ this.$set(this.ctrl, i, index)
380
+ return
381
+ }
382
+ } else if (node.name === 'img') {
383
+ // #ifdef H5 && VUE3
384
+ if (this.opts[0] && !this.ctrl.load) return
385
+ // #endif
386
+ // 显示错误占位图
387
+ if (this.opts[2]) {
388
+ this.$set(this.ctrl, i, -1)
389
+ }
390
+ this.checkReady()
391
+ }
392
+ if (this.root) {
393
+ this.root.$emit('error', {
394
+ source: node.name,
395
+ attrs: node.attrs,
396
+ // #ifndef H5 && VUE3
397
+ errMsg: e.detail.errMsg
398
+ // #endif
399
+ })
400
+ }
401
+ }
402
+ }
403
+ }
404
+ </script>
405
+ <style>
406
+ /* a 标签默认效果 */
407
+ ._a {
408
+ padding: 1.5px 0 1.5px 0;
409
+ color: #366092;
410
+ word-break: break-all;
411
+ }
412
+
413
+ /* a 标签点击态效果 */
414
+ ._hover {
415
+ text-decoration: underline;
416
+ opacity: 0.7;
417
+ }
418
+
419
+ /* 图片默认效果 */
420
+ ._img {
421
+ max-width: 100%;
422
+ -webkit-touch-callout: none;
423
+ }
424
+
425
+ /* 内部样式 */
426
+
427
+ ._block {
428
+ display: block;
429
+ }
430
+
431
+ ._b,
432
+ ._strong {
433
+ font-weight: bold;
434
+ }
435
+
436
+ ._code {
437
+ font-family: monospace;
438
+ }
439
+
440
+ ._del {
441
+ text-decoration: line-through;
442
+ }
443
+
444
+ ._em,
445
+ ._i {
446
+ font-style: italic;
447
+ }
448
+
449
+ ._h1 {
450
+ font-size: 2em;
451
+ }
452
+
453
+ ._h2 {
454
+ font-size: 1.5em;
455
+ }
456
+
457
+ ._h3 {
458
+ font-size: 1.17em;
459
+ }
460
+
461
+ ._h5 {
462
+ font-size: 0.83em;
463
+ }
464
+
465
+ ._h6 {
466
+ font-size: 0.67em;
467
+ }
468
+
469
+ ._h1,
470
+ ._h2,
471
+ ._h3,
472
+ ._h4,
473
+ ._h5,
474
+ ._h6 {
475
+ display: block;
476
+ font-weight: bold;
477
+ }
478
+
479
+ ._image {
480
+ height: 1px;
481
+ }
482
+
483
+ ._ins {
484
+ text-decoration: underline;
485
+ }
486
+
487
+ ._li {
488
+ display: list-item;
489
+ }
490
+
491
+ ._ol {
492
+ list-style-type: decimal;
493
+ }
494
+
495
+ ._ol,
496
+ ._ul {
497
+ display: block;
498
+ padding-left: 40px;
499
+ margin: 1em 0;
500
+ }
501
+
502
+ ._q::before {
503
+ content: '"';
504
+ }
505
+
506
+ ._q::after {
507
+ content: '"';
508
+ }
509
+
510
+ ._sub {
511
+ font-size: smaller;
512
+ vertical-align: sub;
513
+ }
514
+
515
+ ._sup {
516
+ font-size: smaller;
517
+ vertical-align: super;
518
+ }
519
+
520
+ ._thead,
521
+ ._tbody,
522
+ ._tfoot {
523
+ display: table-row-group;
524
+ }
525
+
526
+ ._tr {
527
+ display: table-row;
528
+ }
529
+
530
+ ._td,
531
+ ._th {
532
+ display: table-cell;
533
+ vertical-align: middle;
534
+ }
535
+
536
+ ._th {
537
+ font-weight: bold;
538
+ text-align: center;
539
+ }
540
+
541
+ ._ul {
542
+ list-style-type: disc;
543
+ }
544
+
545
+ ._ul ._ul {
546
+ margin: 0;
547
+ list-style-type: circle;
548
+ }
549
+
550
+ ._ul ._ul ._ul {
551
+ list-style-type: square;
552
+ }
553
+
554
+ ._abbr,
555
+ ._b,
556
+ ._code,
557
+ ._del,
558
+ ._em,
559
+ ._i,
560
+ ._ins,
561
+ ._label,
562
+ ._q,
563
+ ._span,
564
+ ._strong,
565
+ ._sub,
566
+ ._sup {
567
+ display: inline;
568
+ }
569
+
570
+ /* #ifdef APP-PLUS */
571
+ ._video {
572
+ width: 300px;
573
+ height: 225px;
574
+ }
575
+ /* #endif */
576
+ </style>