hy-app 0.6.4 → 0.6.6

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.
Files changed (106) hide show
  1. package/attributes.json +1 -1
  2. package/components/hy-address-picker/hy-address-picker.vue +249 -249
  3. package/components/hy-address-picker/props.ts +103 -103
  4. package/components/hy-button/hy-button.vue +320 -289
  5. package/components/hy-button/props.ts +143 -143
  6. package/components/hy-button/typing.d.ts +43 -35
  7. package/components/hy-calendar/header.vue +58 -58
  8. package/components/hy-calendar/hy-calendar.vue +8 -6
  9. package/components/hy-calendar/month.vue +402 -402
  10. package/components/hy-calendar/props.ts +169 -169
  11. package/components/hy-calendar/typing.d.ts +47 -45
  12. package/components/hy-cell-item/hy-cell-item.vue +161 -161
  13. package/components/hy-cell-item/props.ts +59 -59
  14. package/components/hy-check-button/hy-check-button.vue +135 -135
  15. package/components/hy-code-input/hy-code-input.vue +231 -231
  16. package/components/hy-code-input/props.ts +90 -90
  17. package/components/hy-config-provider/hy-config-provider.vue +53 -53
  18. package/components/hy-config-provider/props.ts +30 -30
  19. package/components/hy-coupon/hy-coupon.vue +183 -183
  20. package/components/hy-coupon/props.ts +108 -108
  21. package/components/hy-datetime-picker/hy-datetime-picker.vue +41 -55
  22. package/components/hy-datetime-picker/props.ts +144 -144
  23. package/components/hy-datetime-picker/typing.d.ts +2 -0
  24. package/components/hy-divider/props.ts +83 -83
  25. package/components/hy-empty/icon.ts +72 -72
  26. package/components/hy-folding-panel/hy-folding-panel-group.vue +162 -162
  27. package/components/hy-form/hy-form.vue +220 -220
  28. package/components/hy-icon/hy-icon.vue +112 -112
  29. package/components/hy-index-bar/hy-index-bar.vue +185 -185
  30. package/components/hy-index-bar/index.scss +64 -64
  31. package/components/hy-index-bar/props.ts +94 -94
  32. package/components/hy-index-bar/typing.d.ts +36 -36
  33. package/components/hy-input/hy-input.vue +333 -333
  34. package/components/hy-input/props.ts +186 -186
  35. package/components/hy-modal/hy-modal.vue +211 -211
  36. package/components/hy-modal/props.ts +94 -94
  37. package/components/hy-modal/typing.d.ts +16 -16
  38. package/components/hy-notice-bar/hy-row-notice.vue +121 -121
  39. package/components/hy-notify/hy-notify.vue +174 -174
  40. package/components/hy-number-step/hy-number-step.vue +367 -367
  41. package/components/hy-overlay/hy-overlay.vue +61 -61
  42. package/components/hy-overlay/props.ts +38 -38
  43. package/components/hy-pagination/hy-pagination.vue +136 -136
  44. package/components/hy-pagination/props.ts +58 -58
  45. package/components/hy-parse/hy-parse.vue +550 -550
  46. package/components/hy-parse/node/node.vue +781 -781
  47. package/components/hy-parse/parser.js +1455 -1455
  48. package/components/hy-parse/props.ts +19 -19
  49. package/components/hy-parse/typing.d.ts +68 -68
  50. package/components/hy-picker/hy-picker.vue +435 -435
  51. package/components/hy-picker/props.ts +122 -122
  52. package/components/hy-picker/typing.d.ts +38 -38
  53. package/components/hy-qrcode/props.ts +72 -72
  54. package/components/hy-qrcode/qrcode.js.bak +1433 -1433
  55. package/components/hy-radio/props.ts +97 -97
  56. package/components/hy-read-more/props.ts +48 -48
  57. package/components/hy-search/props.ts +133 -133
  58. package/components/hy-signature/canvasHelper.ts +51 -51
  59. package/components/hy-signature/props.ts +121 -121
  60. package/components/hy-skeleton/hy-skeleton.vue +142 -142
  61. package/components/hy-skeleton/props.ts +46 -46
  62. package/components/hy-skeleton/typing.d.ts +31 -31
  63. package/components/hy-steps/hy-steps.vue +275 -275
  64. package/components/hy-steps/typing.d.ts +25 -25
  65. package/components/hy-swiper/hy-swiper.vue +3 -3
  66. package/components/hy-swiper/index.scss +5 -5
  67. package/components/hy-swiper/props.ts +0 -1
  68. package/components/hy-table/hy-table.vue +630 -630
  69. package/components/hy-table/props.ts +62 -62
  70. package/components/hy-table/typing.d.ts +29 -29
  71. package/components/hy-tabs/hy-tabs.vue +336 -335
  72. package/components/hy-tabs/props.ts +84 -77
  73. package/components/hy-tag/hy-tag.vue +173 -173
  74. package/components/hy-tag/props.ts +89 -89
  75. package/components/hy-text/hy-text.vue +237 -237
  76. package/components/hy-text/props.ts +115 -115
  77. package/components/hy-textarea/hy-textarea.vue +198 -198
  78. package/components/hy-toast/hy-toast.vue +200 -200
  79. package/components/hy-toast/props.ts +3 -3
  80. package/components/hy-transition/hy-transition.vue +157 -157
  81. package/components/hy-transition/props.ts +32 -32
  82. package/components/hy-upload/hy-upload.vue +384 -384
  83. package/components/hy-watermark/hy-watermark.vue +1058 -1058
  84. package/components/hy-watermark/props.ts +109 -109
  85. package/global.d.ts +94 -94
  86. package/libs/api/http.ts +119 -119
  87. package/libs/composables/index.ts +8 -8
  88. package/libs/composables/useMessage.ts +149 -149
  89. package/libs/composables/useToast.ts +45 -45
  90. package/libs/composables/useTranslate.ts +10 -10
  91. package/libs/css/_config.scss +5 -5
  92. package/libs/index.ts +8 -8
  93. package/libs/locale/index.ts +32 -32
  94. package/libs/locale/lang/en-US.ts +84 -84
  95. package/libs/locale/lang/zh-CN.ts +87 -87
  96. package/libs/typing/index.ts +2 -2
  97. package/libs/typing/modules/common.d.ts +139 -139
  98. package/libs/typing/modules/form.ts +176 -176
  99. package/libs/typing/modules/http.d.ts +19 -19
  100. package/libs/typing/modules/index.d.ts +12 -12
  101. package/libs/utils/inside.ts +340 -340
  102. package/libs/utils/inspect.ts +140 -140
  103. package/libs/utils/utils.ts +525 -525
  104. package/package.json +81 -81
  105. package/tags.json +1 -1
  106. package/web-types.json +1 -1
@@ -1,550 +1,550 @@
1
- <template>
2
- <view id="_root" :class="(selectable ? '_select ' : '') + '_root'" :style="containerStyle">
3
- <slot v-if="!nodes[0]" />
4
- <!-- #ifndef APP-PLUS-NVUE -->
5
- <node
6
- v-else
7
- :childs="nodes"
8
- :opts="[lazyLoad, loadingImg, errorImg, showImgMenu, selectable]"
9
- name="span"
10
- />
11
- <!-- #endif -->
12
- <!-- #ifdef APP-PLUS-NVUE -->
13
- <web-view
14
- ref="web"
15
- src="/static/app-plus/mp-html/local.html"
16
- :style="'margin-top:-2px;height:' + height + 'px'"
17
- @onPostMessage="_onMessage"
18
- />
19
- <!-- #endif -->
20
- </view>
21
- </template>
22
-
23
- <script>
24
- /**
25
- * mp-html v2.4.1
26
- * @description 富文本组件
27
- * @tutorial https://github.com/jin-yufeng/mp-html
28
- * @property {String} container-style 容器的样式
29
- * @property {String} content 用于渲染的 html 字符串
30
- * @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
31
- * @property {String} domain 主域名,用于拼接链接
32
- * @property {String} error-img 图片出错时的占位图链接
33
- * @property {Boolean} lazy-load 是否开启图片懒加载
34
- * @property {string} loading-img 图片加载过程中的占位图链接
35
- * @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
36
- * @property {Boolean} preview-img 是否允许图片被点击时自动预览
37
- * @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
38
- * @property {Boolean | String} selectable 是否开启长按复制
39
- * @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
40
- * @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
41
- * @property {Object} tag-style 标签的默认样式
42
- * @property {Boolean | Number} use-anchor 是否使用锚点链接
43
- * @event {Function} load dom 结构加载完毕时触发
44
- * @event {Function} ready 所有图片加载完毕时触发
45
- * @event {Function} imgTap 图片被点击时触发
46
- * @event {Function} linkTap 链接被点击时触发
47
- * @event {Function} play 音视频播放时触发
48
- * @event {Function} error 媒体加载出错时触发
49
- */
50
- // #ifndef APP-PLUS-NVUE
51
- import node from './node/node'
52
- // #endif
53
- import Parser from './parser'
54
- const plugins = []
55
- // #ifdef APP-PLUS-NVUE
56
- const dom = weex.requireModule('dom')
57
- // #endif
58
- export default {
59
- name: 'hy-parse',
60
- data() {
61
- return {
62
- nodes: [],
63
- // #ifdef APP-PLUS-NVUE
64
- height: 3
65
- // #endif
66
- }
67
- },
68
- props: {
69
- containerStyle: {
70
- type: String,
71
- default: ''
72
- },
73
- content: {
74
- type: String,
75
- default: ''
76
- },
77
- copyLink: {
78
- type: [Boolean, String],
79
- default: true
80
- },
81
- domain: String,
82
- errorImg: {
83
- type: String,
84
- default: ''
85
- },
86
- lazyLoad: {
87
- type: [Boolean, String],
88
- default: false
89
- },
90
- loadingImg: {
91
- type: String,
92
- default: ''
93
- },
94
- pauseVideo: {
95
- type: [Boolean, String],
96
- default: true
97
- },
98
- previewImg: {
99
- type: [Boolean, String],
100
- default: true
101
- },
102
- scrollTable: [Boolean, String],
103
- selectable: [Boolean, String],
104
- setTitle: {
105
- type: [Boolean, String],
106
- default: true
107
- },
108
- showImgMenu: {
109
- type: [Boolean, String],
110
- default: true
111
- },
112
- tagStyle: Object,
113
- useAnchor: [Boolean, Number]
114
- },
115
- // #ifdef VUE3
116
- emits: ['load', 'ready', 'imgTap', 'linkTap', 'play', 'error'],
117
- // #endif
118
- // #ifndef APP-PLUS-NVUE
119
- components: {
120
- node
121
- },
122
- // #endif
123
- watch: {
124
- content(content) {
125
- this.setContent(content)
126
- }
127
- },
128
- created() {
129
- this.plugins = []
130
- for (let i = plugins.length; i--; ) {
131
- this.plugins.push(new plugins[i](this))
132
- }
133
- },
134
- mounted() {
135
- if (this.content && !this.nodes.length) {
136
- this.setContent(this.content)
137
- }
138
- },
139
- beforeUnmount() {
140
- this._hook('onDetached')
141
- },
142
- methods: {
143
- /**
144
- * @description 将锚点跳转的范围限定在一个 scroll-view 内
145
- * @param {Object} page scroll-view 所在页面的示例
146
- * @param {String} selector scroll-view 的选择器
147
- * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
148
- */
149
- in(page, selector, scrollTop) {
150
- // #ifndef APP-PLUS-NVUE
151
- if (page && selector && scrollTop) {
152
- this._in = {
153
- page,
154
- selector,
155
- scrollTop
156
- }
157
- }
158
- // #endif
159
- },
160
-
161
- /**
162
- * @description 锚点跳转
163
- * @param {String} id 要跳转的锚点 id
164
- * @param {Number} offset 跳转位置的偏移量
165
- * @returns {Promise}
166
- */
167
- navigateTo(id, offset) {
168
- return new Promise((resolve, reject) => {
169
- if (!this.useAnchor) {
170
- reject(Error('Anchor is disabled'))
171
- return
172
- }
173
- offset = offset || parseInt(this.useAnchor) || 0
174
- // #ifdef APP-PLUS-NVUE
175
- if (!id) {
176
- dom.scrollToElement(this.$refs.web, {
177
- offset
178
- })
179
- resolve()
180
- } else {
181
- this._navigateTo = {
182
- resolve,
183
- reject,
184
- offset
185
- }
186
- this.$refs.web.evalJs(
187
- 'uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' +
188
- id +
189
- ')||{}).offsetTop}})'
190
- )
191
- }
192
- // #endif
193
- // #ifndef APP-PLUS-NVUE
194
- let deep = ' '
195
- // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
196
- deep = '>>>'
197
- // #endif
198
- const selector = uni
199
- .createSelectorQuery()
200
- // #ifndef MP-ALIPAY
201
- .in(this._in ? this._in.page : this)
202
- // #endif
203
- .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : ''))
204
- .boundingClientRect()
205
- if (this._in) {
206
- selector
207
- .select(this._in.selector)
208
- .scrollOffset()
209
- .select(this._in.selector)
210
- .boundingClientRect()
211
- } else {
212
- // 获取 scroll-view 的位置和滚动距离
213
- selector.selectViewport().scrollOffset() // 获取窗口的滚动距离
214
- }
215
- selector.exec((res) => {
216
- if (!res[0]) {
217
- reject(Error('Label not found'))
218
- return
219
- }
220
- const scrollTop =
221
- res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
222
- if (this._in) {
223
- // scroll-view 跳转
224
- this._in.page[this._in.scrollTop] = scrollTop
225
- } else {
226
- // 页面跳转
227
- uni.pageScrollTo({
228
- scrollTop,
229
- duration: 300
230
- })
231
- }
232
- resolve()
233
- })
234
- // #endif
235
- })
236
- },
237
-
238
- /**
239
- * @description 获取文本内容
240
- * @return {String}
241
- */
242
- getText(nodes) {
243
- let text = ''
244
- ;(function traversal(nodes) {
245
- for (let i = 0; i < nodes.length; i++) {
246
- const node = nodes[i]
247
- if (node.type === 'text') {
248
- text += node.text.replace(/&amp;/g, '&')
249
- } else if (node.name === 'br') {
250
- text += '\n'
251
- } else {
252
- // 块级标签前后加换行
253
- const isBlock =
254
- node.name === 'p' ||
255
- node.name === 'div' ||
256
- node.name === 'tr' ||
257
- node.name === 'li' ||
258
- (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
259
- if (isBlock && text && text[text.length - 1] !== '\n') {
260
- text += '\n'
261
- }
262
- // 递归获取子节点的文本
263
- if (node.children) {
264
- traversal(node.children)
265
- }
266
- if (isBlock && text[text.length - 1] !== '\n') {
267
- text += '\n'
268
- } else if (node.name === 'td' || node.name === 'th') {
269
- text += '\t'
270
- }
271
- }
272
- }
273
- })(nodes || this.nodes)
274
- return text
275
- },
276
-
277
- /**
278
- * @description 获取内容大小和位置
279
- * @return {Promise}
280
- */
281
- getRect() {
282
- return new Promise((resolve, reject) => {
283
- uni.createSelectorQuery()
284
- // #ifndef MP-ALIPAY
285
- .in(this)
286
- // #endif
287
- .select('#_root')
288
- .boundingClientRect()
289
- .exec((res) =>
290
- res[0] ? resolve(res[0]) : reject(Error('Root label not found'))
291
- )
292
- })
293
- },
294
-
295
- /**
296
- * @description 暂停播放媒体
297
- */
298
- pauseMedia() {
299
- for (let i = (this._videos || []).length; i--; ) {
300
- this._videos[i].pause()
301
- }
302
- // #ifdef APP-PLUS
303
- const command =
304
- 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
305
- // #ifndef APP-PLUS-NVUE
306
- let page = this.$parent
307
- while (!page.$scope) page = page.$parent
308
- page.$scope.$getAppWebview().evalJS(command)
309
- // #endif
310
- // #ifdef APP-PLUS-NVUE
311
- this.$refs.web.evalJs(command)
312
- // #endif
313
- // #endif
314
- },
315
-
316
- /**
317
- * @description 设置媒体播放速率
318
- * @param {Number} rate 播放速率
319
- */
320
- setPlaybackRate(rate) {
321
- this.playbackRate = rate
322
- for (let i = (this._videos || []).length; i--; ) {
323
- this._videos[i].playbackRate(rate)
324
- }
325
- // #ifdef APP-PLUS
326
- const command =
327
- 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' +
328
- rate
329
- // #ifndef APP-PLUS-NVUE
330
- let page = this.$parent
331
- while (!page.$scope) page = page.$parent
332
- page.$scope.$getAppWebview().evalJS(command)
333
- // #endif
334
- // #ifdef APP-PLUS-NVUE
335
- this.$refs.web.evalJs(command)
336
- // #endif
337
- // #endif
338
- },
339
-
340
- /**
341
- * @description 设置内容
342
- * @param {String} content html 内容
343
- * @param {Boolean} append 是否在尾部追加
344
- */
345
- setContent(content, append) {
346
- if (!append || !this.imgList) {
347
- this.imgList = []
348
- }
349
- const nodes = new Parser(this).parse(content)
350
- // #ifdef APP-PLUS-NVUE
351
- if (this._ready) {
352
- this._set(nodes, append)
353
- }
354
- // #endif
355
- this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
356
-
357
- // #ifndef APP-PLUS-NVUE
358
- this._videos = []
359
- this.$nextTick(() => {
360
- this._hook('onLoad')
361
- this.$emit('load')
362
- })
363
-
364
- if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
365
- // 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
366
- let height = 0
367
- const callback = (rect) => {
368
- if (!rect || !rect.height) rect = {}
369
- // 350ms 总高度无变化就触发 ready 事件
370
- if (rect.height === height) {
371
- this.$emit('ready', rect)
372
- } else {
373
- height = rect.height
374
- setTimeout(() => {
375
- this.getRect().then(callback).catch(callback)
376
- }, 350)
377
- }
378
- }
379
- this.getRect().then(callback).catch(callback)
380
- } else {
381
- // 未设置懒加载,等待所有图片加载完毕
382
- if (!this.imgList._unloadimgs) {
383
- this.getRect()
384
- .then((rect) => {
385
- this.$emit('ready', rect)
386
- })
387
- .catch(() => {
388
- this.$emit('ready', {})
389
- })
390
- }
391
- }
392
- // #endif
393
- },
394
-
395
- /**
396
- * @description 调用插件钩子函数
397
- */
398
- _hook(name) {
399
- for (let i = plugins.length; i--; ) {
400
- if (this.plugins[i][name]) {
401
- this.plugins[i][name]()
402
- }
403
- }
404
- },
405
-
406
- // #ifdef APP-PLUS-NVUE
407
- /**
408
- * @description 设置内容
409
- */
410
- _set(nodes, append) {
411
- this.$refs.web.evalJs(
412
- 'setContent(' +
413
- JSON.stringify(nodes).replace(/%22/g, '') +
414
- ',' +
415
- JSON.stringify([
416
- this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''),
417
- this.errorImg,
418
- this.loadingImg,
419
- this.pauseVideo,
420
- this.scrollTable,
421
- this.selectable
422
- ]) +
423
- ',' +
424
- append +
425
- ')'
426
- )
427
- },
428
-
429
- /**
430
- * @description 接收到 web-view 消息
431
- */
432
- _onMessage(e) {
433
- const message = e.detail.data[0]
434
- switch (message.action) {
435
- // web-view 初始化完毕
436
- case 'onJSBridgeReady':
437
- this._ready = true
438
- if (this.nodes) {
439
- this._set(this.nodes)
440
- }
441
- break
442
- // 内容 dom 加载完毕
443
- case 'onLoad':
444
- this.height = message.height
445
- this._hook('onLoad')
446
- this.$emit('load')
447
- break
448
- // 所有图片加载完毕
449
- case 'onReady':
450
- this.getRect()
451
- .then((res) => {
452
- this.$emit('ready', res)
453
- })
454
- .catch(() => {
455
- this.$emit('ready', {})
456
- })
457
- break
458
- // 总高度发生变化
459
- case 'onHeightChange':
460
- this.height = message.height
461
- break
462
- // 图片点击
463
- case 'onImgTap':
464
- this.$emit('imgTap', message.attrs)
465
- if (this.previewImg) {
466
- uni.previewImage({
467
- current: parseInt(message.attrs.i),
468
- urls: this.imgList
469
- })
470
- }
471
- break
472
- // 链接点击
473
- case 'onLinkTap': {
474
- const href = message.attrs.href
475
- this.$emit('linkTap', message.attrs)
476
- if (href) {
477
- // 锚点跳转
478
- if (href[0] === '#') {
479
- if (this.useAnchor) {
480
- dom.scrollToElement(this.$refs.web, {
481
- offset: message.offset
482
- })
483
- }
484
- } else if (href.includes('://')) {
485
- // 打开外链
486
- if (this.copyLink) {
487
- plus.runtime.openWeb(href)
488
- }
489
- } else {
490
- uni.navigateTo({
491
- url: href,
492
- fail() {
493
- uni.switchTab({
494
- url: href
495
- })
496
- }
497
- })
498
- }
499
- }
500
- break
501
- }
502
- case 'onPlay':
503
- this.$emit('play')
504
- break
505
- // 获取到锚点的偏移量
506
- case 'getOffset':
507
- if (typeof message.offset === 'number') {
508
- dom.scrollToElement(this.$refs.web, {
509
- offset: message.offset + this._navigateTo.offset
510
- })
511
- this._navigateTo.resolve()
512
- } else {
513
- this._navigateTo.reject(Error('Label not found'))
514
- }
515
- break
516
- // 点击
517
- case 'onClick':
518
- this.$emit('tap')
519
- this.$emit('click')
520
- break
521
- // 出错
522
- case 'onError':
523
- this.$emit('error', {
524
- source: message.source,
525
- attrs: message.attrs
526
- })
527
- }
528
- }
529
- // #endif
530
- }
531
- }
532
- </script>
533
-
534
- <style>
535
- /* #ifndef APP-PLUS-NVUE */
536
- /* 根节点样式 */
537
- ._root {
538
- padding: 1px 0;
539
- overflow-x: auto;
540
- overflow-y: hidden;
541
- -webkit-overflow-scrolling: touch;
542
- }
543
-
544
- /* 长按复制 */
545
- ._select {
546
- user-select: text;
547
- }
548
-
549
- /* #endif */
550
- </style>
1
+ <template>
2
+ <view id="_root" :class="(selectable ? '_select ' : '') + '_root'" :style="containerStyle">
3
+ <slot v-if="!nodes[0]" />
4
+ <!-- #ifndef APP-PLUS-NVUE -->
5
+ <node
6
+ v-else
7
+ :childs="nodes"
8
+ :opts="[lazyLoad, loadingImg, errorImg, showImgMenu, selectable]"
9
+ name="span"
10
+ />
11
+ <!-- #endif -->
12
+ <!-- #ifdef APP-PLUS-NVUE -->
13
+ <web-view
14
+ ref="web"
15
+ src="/static/app-plus/mp-html/local.html"
16
+ :style="'margin-top:-2px;height:' + height + 'px'"
17
+ @onPostMessage="_onMessage"
18
+ />
19
+ <!-- #endif -->
20
+ </view>
21
+ </template>
22
+
23
+ <script>
24
+ /**
25
+ * mp-html v2.4.1
26
+ * @description 富文本组件
27
+ * @tutorial https://github.com/jin-yufeng/mp-html
28
+ * @property {String} container-style 容器的样式
29
+ * @property {String} content 用于渲染的 html 字符串
30
+ * @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
31
+ * @property {String} domain 主域名,用于拼接链接
32
+ * @property {String} error-img 图片出错时的占位图链接
33
+ * @property {Boolean} lazy-load 是否开启图片懒加载
34
+ * @property {string} loading-img 图片加载过程中的占位图链接
35
+ * @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
36
+ * @property {Boolean} preview-img 是否允许图片被点击时自动预览
37
+ * @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
38
+ * @property {Boolean | String} selectable 是否开启长按复制
39
+ * @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
40
+ * @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
41
+ * @property {Object} tag-style 标签的默认样式
42
+ * @property {Boolean | Number} use-anchor 是否使用锚点链接
43
+ * @event {Function} load dom 结构加载完毕时触发
44
+ * @event {Function} ready 所有图片加载完毕时触发
45
+ * @event {Function} imgTap 图片被点击时触发
46
+ * @event {Function} linkTap 链接被点击时触发
47
+ * @event {Function} play 音视频播放时触发
48
+ * @event {Function} error 媒体加载出错时触发
49
+ */
50
+ // #ifndef APP-PLUS-NVUE
51
+ import node from './node/node'
52
+ // #endif
53
+ import Parser from './parser'
54
+ const plugins = []
55
+ // #ifdef APP-PLUS-NVUE
56
+ const dom = weex.requireModule('dom')
57
+ // #endif
58
+ export default {
59
+ name: 'hy-parse',
60
+ data() {
61
+ return {
62
+ nodes: [],
63
+ // #ifdef APP-PLUS-NVUE
64
+ height: 3
65
+ // #endif
66
+ }
67
+ },
68
+ props: {
69
+ containerStyle: {
70
+ type: String,
71
+ default: ''
72
+ },
73
+ content: {
74
+ type: String,
75
+ default: ''
76
+ },
77
+ copyLink: {
78
+ type: [Boolean, String],
79
+ default: true
80
+ },
81
+ domain: String,
82
+ errorImg: {
83
+ type: String,
84
+ default: ''
85
+ },
86
+ lazyLoad: {
87
+ type: [Boolean, String],
88
+ default: false
89
+ },
90
+ loadingImg: {
91
+ type: String,
92
+ default: ''
93
+ },
94
+ pauseVideo: {
95
+ type: [Boolean, String],
96
+ default: true
97
+ },
98
+ previewImg: {
99
+ type: [Boolean, String],
100
+ default: true
101
+ },
102
+ scrollTable: [Boolean, String],
103
+ selectable: [Boolean, String],
104
+ setTitle: {
105
+ type: [Boolean, String],
106
+ default: true
107
+ },
108
+ showImgMenu: {
109
+ type: [Boolean, String],
110
+ default: true
111
+ },
112
+ tagStyle: Object,
113
+ useAnchor: [Boolean, Number]
114
+ },
115
+ // #ifdef VUE3
116
+ emits: ['load', 'ready', 'imgTap', 'linkTap', 'play', 'error'],
117
+ // #endif
118
+ // #ifndef APP-PLUS-NVUE
119
+ components: {
120
+ node
121
+ },
122
+ // #endif
123
+ watch: {
124
+ content(content) {
125
+ this.setContent(content)
126
+ }
127
+ },
128
+ created() {
129
+ this.plugins = []
130
+ for (let i = plugins.length; i--; ) {
131
+ this.plugins.push(new plugins[i](this))
132
+ }
133
+ },
134
+ mounted() {
135
+ if (this.content && !this.nodes.length) {
136
+ this.setContent(this.content)
137
+ }
138
+ },
139
+ beforeUnmount() {
140
+ this._hook('onDetached')
141
+ },
142
+ methods: {
143
+ /**
144
+ * @description 将锚点跳转的范围限定在一个 scroll-view 内
145
+ * @param {Object} page scroll-view 所在页面的示例
146
+ * @param {String} selector scroll-view 的选择器
147
+ * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
148
+ */
149
+ in(page, selector, scrollTop) {
150
+ // #ifndef APP-PLUS-NVUE
151
+ if (page && selector && scrollTop) {
152
+ this._in = {
153
+ page,
154
+ selector,
155
+ scrollTop
156
+ }
157
+ }
158
+ // #endif
159
+ },
160
+
161
+ /**
162
+ * @description 锚点跳转
163
+ * @param {String} id 要跳转的锚点 id
164
+ * @param {Number} offset 跳转位置的偏移量
165
+ * @returns {Promise}
166
+ */
167
+ navigateTo(id, offset) {
168
+ return new Promise((resolve, reject) => {
169
+ if (!this.useAnchor) {
170
+ reject(Error('Anchor is disabled'))
171
+ return
172
+ }
173
+ offset = offset || parseInt(this.useAnchor) || 0
174
+ // #ifdef APP-PLUS-NVUE
175
+ if (!id) {
176
+ dom.scrollToElement(this.$refs.web, {
177
+ offset
178
+ })
179
+ resolve()
180
+ } else {
181
+ this._navigateTo = {
182
+ resolve,
183
+ reject,
184
+ offset
185
+ }
186
+ this.$refs.web.evalJs(
187
+ 'uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' +
188
+ id +
189
+ ')||{}).offsetTop}})'
190
+ )
191
+ }
192
+ // #endif
193
+ // #ifndef APP-PLUS-NVUE
194
+ let deep = ' '
195
+ // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
196
+ deep = '>>>'
197
+ // #endif
198
+ const selector = uni
199
+ .createSelectorQuery()
200
+ // #ifndef MP-ALIPAY
201
+ .in(this._in ? this._in.page : this)
202
+ // #endif
203
+ .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : ''))
204
+ .boundingClientRect()
205
+ if (this._in) {
206
+ selector
207
+ .select(this._in.selector)
208
+ .scrollOffset()
209
+ .select(this._in.selector)
210
+ .boundingClientRect()
211
+ } else {
212
+ // 获取 scroll-view 的位置和滚动距离
213
+ selector.selectViewport().scrollOffset() // 获取窗口的滚动距离
214
+ }
215
+ selector.exec((res) => {
216
+ if (!res[0]) {
217
+ reject(Error('Label not found'))
218
+ return
219
+ }
220
+ const scrollTop =
221
+ res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
222
+ if (this._in) {
223
+ // scroll-view 跳转
224
+ this._in.page[this._in.scrollTop] = scrollTop
225
+ } else {
226
+ // 页面跳转
227
+ uni.pageScrollTo({
228
+ scrollTop,
229
+ duration: 300
230
+ })
231
+ }
232
+ resolve()
233
+ })
234
+ // #endif
235
+ })
236
+ },
237
+
238
+ /**
239
+ * @description 获取文本内容
240
+ * @return {String}
241
+ */
242
+ getText(nodes) {
243
+ let text = ''
244
+ ;(function traversal(nodes) {
245
+ for (let i = 0; i < nodes.length; i++) {
246
+ const node = nodes[i]
247
+ if (node.type === 'text') {
248
+ text += node.text.replace(/&amp;/g, '&')
249
+ } else if (node.name === 'br') {
250
+ text += '\n'
251
+ } else {
252
+ // 块级标签前后加换行
253
+ const isBlock =
254
+ node.name === 'p' ||
255
+ node.name === 'div' ||
256
+ node.name === 'tr' ||
257
+ node.name === 'li' ||
258
+ (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
259
+ if (isBlock && text && text[text.length - 1] !== '\n') {
260
+ text += '\n'
261
+ }
262
+ // 递归获取子节点的文本
263
+ if (node.children) {
264
+ traversal(node.children)
265
+ }
266
+ if (isBlock && text[text.length - 1] !== '\n') {
267
+ text += '\n'
268
+ } else if (node.name === 'td' || node.name === 'th') {
269
+ text += '\t'
270
+ }
271
+ }
272
+ }
273
+ })(nodes || this.nodes)
274
+ return text
275
+ },
276
+
277
+ /**
278
+ * @description 获取内容大小和位置
279
+ * @return {Promise}
280
+ */
281
+ getRect() {
282
+ return new Promise((resolve, reject) => {
283
+ uni.createSelectorQuery()
284
+ // #ifndef MP-ALIPAY
285
+ .in(this)
286
+ // #endif
287
+ .select('#_root')
288
+ .boundingClientRect()
289
+ .exec((res) =>
290
+ res[0] ? resolve(res[0]) : reject(Error('Root label not found'))
291
+ )
292
+ })
293
+ },
294
+
295
+ /**
296
+ * @description 暂停播放媒体
297
+ */
298
+ pauseMedia() {
299
+ for (let i = (this._videos || []).length; i--; ) {
300
+ this._videos[i].pause()
301
+ }
302
+ // #ifdef APP-PLUS
303
+ const command =
304
+ 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
305
+ // #ifndef APP-PLUS-NVUE
306
+ let page = this.$parent
307
+ while (!page.$scope) page = page.$parent
308
+ page.$scope.$getAppWebview().evalJS(command)
309
+ // #endif
310
+ // #ifdef APP-PLUS-NVUE
311
+ this.$refs.web.evalJs(command)
312
+ // #endif
313
+ // #endif
314
+ },
315
+
316
+ /**
317
+ * @description 设置媒体播放速率
318
+ * @param {Number} rate 播放速率
319
+ */
320
+ setPlaybackRate(rate) {
321
+ this.playbackRate = rate
322
+ for (let i = (this._videos || []).length; i--; ) {
323
+ this._videos[i].playbackRate(rate)
324
+ }
325
+ // #ifdef APP-PLUS
326
+ const command =
327
+ 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' +
328
+ rate
329
+ // #ifndef APP-PLUS-NVUE
330
+ let page = this.$parent
331
+ while (!page.$scope) page = page.$parent
332
+ page.$scope.$getAppWebview().evalJS(command)
333
+ // #endif
334
+ // #ifdef APP-PLUS-NVUE
335
+ this.$refs.web.evalJs(command)
336
+ // #endif
337
+ // #endif
338
+ },
339
+
340
+ /**
341
+ * @description 设置内容
342
+ * @param {String} content html 内容
343
+ * @param {Boolean} append 是否在尾部追加
344
+ */
345
+ setContent(content, append) {
346
+ if (!append || !this.imgList) {
347
+ this.imgList = []
348
+ }
349
+ const nodes = new Parser(this).parse(content)
350
+ // #ifdef APP-PLUS-NVUE
351
+ if (this._ready) {
352
+ this._set(nodes, append)
353
+ }
354
+ // #endif
355
+ this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
356
+
357
+ // #ifndef APP-PLUS-NVUE
358
+ this._videos = []
359
+ this.$nextTick(() => {
360
+ this._hook('onLoad')
361
+ this.$emit('load')
362
+ })
363
+
364
+ if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
365
+ // 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
366
+ let height = 0
367
+ const callback = (rect) => {
368
+ if (!rect || !rect.height) rect = {}
369
+ // 350ms 总高度无变化就触发 ready 事件
370
+ if (rect.height === height) {
371
+ this.$emit('ready', rect)
372
+ } else {
373
+ height = rect.height
374
+ setTimeout(() => {
375
+ this.getRect().then(callback).catch(callback)
376
+ }, 350)
377
+ }
378
+ }
379
+ this.getRect().then(callback).catch(callback)
380
+ } else {
381
+ // 未设置懒加载,等待所有图片加载完毕
382
+ if (!this.imgList._unloadimgs) {
383
+ this.getRect()
384
+ .then((rect) => {
385
+ this.$emit('ready', rect)
386
+ })
387
+ .catch(() => {
388
+ this.$emit('ready', {})
389
+ })
390
+ }
391
+ }
392
+ // #endif
393
+ },
394
+
395
+ /**
396
+ * @description 调用插件钩子函数
397
+ */
398
+ _hook(name) {
399
+ for (let i = plugins.length; i--; ) {
400
+ if (this.plugins[i][name]) {
401
+ this.plugins[i][name]()
402
+ }
403
+ }
404
+ },
405
+
406
+ // #ifdef APP-PLUS-NVUE
407
+ /**
408
+ * @description 设置内容
409
+ */
410
+ _set(nodes, append) {
411
+ this.$refs.web.evalJs(
412
+ 'setContent(' +
413
+ JSON.stringify(nodes).replace(/%22/g, '') +
414
+ ',' +
415
+ JSON.stringify([
416
+ this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''),
417
+ this.errorImg,
418
+ this.loadingImg,
419
+ this.pauseVideo,
420
+ this.scrollTable,
421
+ this.selectable
422
+ ]) +
423
+ ',' +
424
+ append +
425
+ ')'
426
+ )
427
+ },
428
+
429
+ /**
430
+ * @description 接收到 web-view 消息
431
+ */
432
+ _onMessage(e) {
433
+ const message = e.detail.data[0]
434
+ switch (message.action) {
435
+ // web-view 初始化完毕
436
+ case 'onJSBridgeReady':
437
+ this._ready = true
438
+ if (this.nodes) {
439
+ this._set(this.nodes)
440
+ }
441
+ break
442
+ // 内容 dom 加载完毕
443
+ case 'onLoad':
444
+ this.height = message.height
445
+ this._hook('onLoad')
446
+ this.$emit('load')
447
+ break
448
+ // 所有图片加载完毕
449
+ case 'onReady':
450
+ this.getRect()
451
+ .then((res) => {
452
+ this.$emit('ready', res)
453
+ })
454
+ .catch(() => {
455
+ this.$emit('ready', {})
456
+ })
457
+ break
458
+ // 总高度发生变化
459
+ case 'onHeightChange':
460
+ this.height = message.height
461
+ break
462
+ // 图片点击
463
+ case 'onImgTap':
464
+ this.$emit('imgTap', message.attrs)
465
+ if (this.previewImg) {
466
+ uni.previewImage({
467
+ current: parseInt(message.attrs.i),
468
+ urls: this.imgList
469
+ })
470
+ }
471
+ break
472
+ // 链接点击
473
+ case 'onLinkTap': {
474
+ const href = message.attrs.href
475
+ this.$emit('linkTap', message.attrs)
476
+ if (href) {
477
+ // 锚点跳转
478
+ if (href[0] === '#') {
479
+ if (this.useAnchor) {
480
+ dom.scrollToElement(this.$refs.web, {
481
+ offset: message.offset
482
+ })
483
+ }
484
+ } else if (href.includes('://')) {
485
+ // 打开外链
486
+ if (this.copyLink) {
487
+ plus.runtime.openWeb(href)
488
+ }
489
+ } else {
490
+ uni.navigateTo({
491
+ url: href,
492
+ fail() {
493
+ uni.switchTab({
494
+ url: href
495
+ })
496
+ }
497
+ })
498
+ }
499
+ }
500
+ break
501
+ }
502
+ case 'onPlay':
503
+ this.$emit('play')
504
+ break
505
+ // 获取到锚点的偏移量
506
+ case 'getOffset':
507
+ if (typeof message.offset === 'number') {
508
+ dom.scrollToElement(this.$refs.web, {
509
+ offset: message.offset + this._navigateTo.offset
510
+ })
511
+ this._navigateTo.resolve()
512
+ } else {
513
+ this._navigateTo.reject(Error('Label not found'))
514
+ }
515
+ break
516
+ // 点击
517
+ case 'onClick':
518
+ this.$emit('tap')
519
+ this.$emit('click')
520
+ break
521
+ // 出错
522
+ case 'onError':
523
+ this.$emit('error', {
524
+ source: message.source,
525
+ attrs: message.attrs
526
+ })
527
+ }
528
+ }
529
+ // #endif
530
+ }
531
+ }
532
+ </script>
533
+
534
+ <style>
535
+ /* #ifndef APP-PLUS-NVUE */
536
+ /* 根节点样式 */
537
+ ._root {
538
+ padding: 1px 0;
539
+ overflow-x: auto;
540
+ overflow-y: hidden;
541
+ -webkit-overflow-scrolling: touch;
542
+ }
543
+
544
+ /* 长按复制 */
545
+ ._select {
546
+ user-select: text;
547
+ }
548
+
549
+ /* #endif */
550
+ </style>