jyf-parser.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. <template>
  2. <view>
  3. <slot v-if="!nodes.length" />
  4. <!--#ifdef APP-PLUS-NVUE-->
  5. <web-view id="top" ref="web" :style="'margin-top:-2px;height:'+height+'px'" @onPostMessage="_message" />
  6. <!--#endif-->
  7. <!--#ifndef APP-PLUS-NVUE-->
  8. <view id="top" :style="showAm+(selectable?';user-select:text;-webkit-user-select:text':'')">
  9. <!--#ifdef H5-->
  10. <div :id="'rtf'+uid"></div>
  11. <!--#endif-->
  12. <!--#ifndef H5-->
  13. <trees :nodes="nodes" :lazy-load="lazyLoad" />
  14. <!--#endif-->
  15. </view>
  16. <!--#endif-->
  17. </view>
  18. </template>
  19. <script>
  20. // #ifndef H5 || APP-PLUS-NVUE
  21. import trees from './libs/trees';
  22. var cache = {},
  23. // #ifdef MP-WEIXIN || MP-TOUTIAO
  24. fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null,
  25. // #endif
  26. Parser = require('./libs/MpHtmlParser.js');
  27. var dom;
  28. // 计算 cache 的 key
  29. function hash(str) {
  30. for (var i = str.length, val = 5381; i--;)
  31. val += (val << 5) + str.charCodeAt(i);
  32. return val;
  33. }
  34. // #endif
  35. // #ifdef H5 || APP-PLUS-NVUE
  36. var rpx = uni.getSystemInfoSync().screenWidth / 750,
  37. cfg = require('./libs/config.js');
  38. // #endif
  39. // #ifdef APP-PLUS-NVUE
  40. var weexDom = weex.requireModule('dom');
  41. // #endif
  42. /**
  43. * Parser 富文本组件
  44. * @tutorial https://github.com/jin-yufeng/Parser
  45. * @property {String|Array} html 富文本数据
  46. * @property {Boolean} autopause 是否在播放一个视频时自动暂停其他视频
  47. * @property {Boolean} autoscroll 是否自动给所有表格添加一个滚动层
  48. * @property {Boolean} autosetTitle 是否自动将 title 标签中的内容设置到页面标题
  49. * @property {Number} compress 压缩等级
  50. * @property {String} domain 图片、视频等链接的主域名
  51. * @property {Boolean} lazyLoad 是否开启图片懒加载
  52. * @property {Boolean} selectable 是否开启长按复制
  53. * @property {Object} tagStyle 标签的默认样式
  54. * @property {Boolean} showWithAnimation 是否使用渐显动画
  55. * @property {Boolean} useAnchor 是否使用锚点
  56. * @property {Boolean} useCache 是否缓存解析结果
  57. * @event {Function} parse 解析完成事件
  58. * @event {Function} load dom 加载完成事件
  59. * @event {Function} ready 所有图片加载完毕事件
  60. * @event {Function} error 错误事件
  61. * @event {Function} imgtap 图片点击事件
  62. * @event {Function} linkpress 链接点击事件
  63. * @example <jyf-parser :html="html"></jyf-parser>
  64. * @author JinYufeng
  65. * @version 20200513
  66. * @listens MIT
  67. */
  68. export default {
  69. name: 'parser',
  70. data() {
  71. return {
  72. // #ifdef H5
  73. uid: this._uid,
  74. // #endif
  75. // #ifdef APP-PLUS-NVUE
  76. height: 1,
  77. // #endif
  78. // #ifndef APP-PLUS-NVUE
  79. showAm: '',
  80. imgs: [],
  81. // #endif
  82. nodes: []
  83. }
  84. },
  85. // #ifndef H5 || APP-PLUS-NVUE
  86. components: {
  87. trees
  88. },
  89. // #endif
  90. props: {
  91. 'html': null,
  92. 'autopause': {
  93. type: Boolean,
  94. default: true
  95. },
  96. 'autoscroll': Boolean,
  97. 'autosetTitle': {
  98. type: Boolean,
  99. default: true
  100. },
  101. // #ifndef H5 || APP-PLUS-NVUE
  102. 'compress': Number,
  103. 'useCache': Boolean,
  104. // #endif
  105. 'domain': String,
  106. 'lazyLoad': Boolean,
  107. 'selectable': Boolean,
  108. 'tagStyle': Object,
  109. 'showWithAnimation': Boolean,
  110. 'useAnchor': Boolean
  111. },
  112. watch: {
  113. html(html) {
  114. this.setContent(html);
  115. }
  116. },
  117. mounted() {
  118. // 图片数组
  119. this.imgList = [];
  120. this.imgList.each = function(f) {
  121. for (var i = 0, len = this.length; i < len; i++)
  122. this.setItem(i, f(this[i], i, this));
  123. }
  124. this.imgList.setItem = function(i, src) {
  125. if (i == void 0 || !src) return;
  126. // #ifndef MP-ALIPAY || APP-PLUS
  127. // 去重
  128. if (src.indexOf('http') == 0 && this.includes(src)) {
  129. var newSrc = '';
  130. for (var j = 0, c; c = src[j]; j++) {
  131. if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
  132. newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
  133. }
  134. newSrc += src.substr(j);
  135. return this[i] = newSrc;
  136. }
  137. // #endif
  138. this[i] = src;
  139. // 暂存 data src
  140. if (src.includes('data:image')) {
  141. var filePath, info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
  142. if (!info) return;
  143. // #ifdef MP-WEIXIN || MP-TOUTIAO
  144. filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
  145. fs && fs.writeFile({
  146. filePath,
  147. data: info[3],
  148. encoding: info[2],
  149. success: () => this[i] = filePath
  150. })
  151. // #endif
  152. // #ifdef APP-PLUS
  153. filePath = `_doc/parser_tmp/${Date.now()}.${info[1]}`;
  154. var bitmap = new plus.nativeObj.Bitmap();
  155. bitmap.loadBase64Data(src, () => {
  156. bitmap.save(filePath, {}, () => {
  157. bitmap.clear()
  158. this[i] = filePath;
  159. })
  160. })
  161. // #endif
  162. }
  163. }
  164. // #ifdef H5
  165. this.document = document.getElementById('rtf' + this._uid);
  166. // #endif
  167. // #ifndef H5 || APP-PLUS-NVUE
  168. if (dom) this.document = new dom(this);
  169. // #endif
  170. // #ifdef APP-PLUS-NVUE
  171. this.document = this.$refs.web;
  172. this.$nextTick(() => {
  173. // #endif
  174. if (this.html) this.setContent(this.html);
  175. // #ifdef APP-PLUS-NVUE
  176. })
  177. // #endif
  178. },
  179. beforeDestroy() {
  180. // #ifdef H5
  181. if (this._observer) this._observer.disconnect();
  182. // #endif
  183. this.imgList.each(src => {
  184. // #ifdef APP-PLUS
  185. if (src && src.includes('_doc')) {
  186. plus.io.resolveLocalFileSystemURL(src, entry => {
  187. entry.remove();
  188. });
  189. }
  190. // #endif
  191. // #ifdef MP-WEIXIN || MP-TOUTIAO
  192. if (src && src.includes(uni.env.USER_DATA_PATH))
  193. fs && fs.unlink({
  194. filePath: src
  195. })
  196. // #endif
  197. })
  198. clearInterval(this._timer);
  199. },
  200. methods: {
  201. // #ifdef H5 || APP-PLUS-NVUE
  202. _Dom2Str(nodes) {
  203. var str = '';
  204. for (var node of nodes) {
  205. if (node.type == 'text')
  206. str += node.text;
  207. else {
  208. str += ('<' + node.name);
  209. for (var attr in node.attrs || {})
  210. str += (' ' + attr + '="' + node.attrs[attr] + '"');
  211. if (!node.children || !node.children.length) str += '>';
  212. else str += ('>' + this._Dom2Str(node.children) + '</' + node.name + '>');
  213. }
  214. }
  215. return str;
  216. },
  217. _handleHtml(html, append) {
  218. if (typeof html != 'string') html = this._Dom2Str(html.nodes || html);
  219. if (!append) {
  220. // 处理 tag-style 和 userAgentStyles
  221. var style = '<style>@keyframes show{0%{opacity:0}100%{opacity:1}}img{max-width:100%}';
  222. for (var item in cfg.userAgentStyles)
  223. style += `${item}{${cfg.userAgentStyles[item]}}`;
  224. for (item in this.tagStyle)
  225. style += `${item}{${this.tagStyle[item]}}`;
  226. style += '</style>';
  227. html = style + html;
  228. }
  229. // 处理 rpx
  230. if (html.includes('rpx'))
  231. html = html.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * rpx + 'px');
  232. return html;
  233. },
  234. // #endif
  235. setContent(html, append) {
  236. // #ifdef APP-PLUS-NVUE
  237. if (!html)
  238. return this.height = 1;
  239. if (append)
  240. this.$refs.web.evalJs("var d=document.createElement('div');d.innerHTML='" + html.replace(/'/g, "\\'") +
  241. "';document.getElementById('parser').appendChild(d)");
  242. else {
  243. html =
  244. '<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1' +
  245. (this.selectable ? '' : ',user-scalable=no') + '"><base href="' + this.domain + '"><div id="parser">' + this._handleHtml(
  246. html) +
  247. '</div><script>"use strict";function post(n){if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){var t={data:[n]};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(t):window.__dcloud_weex_.postMessage(JSON.stringify(t))}}function waitReady(){return new Promise(function(e){var t=document.getElementById("parser"),r=t.scrollHeight,n=setInterval(function(){r==t.scrollHeight?(clearInterval(n),e(r)):r=t.scrollHeight},500)})}' +
  248. (this.showWithAnimation ? 'document.body.style.animation="show .5s",' : '') +
  249. 'setTimeout(function(){post({action:"load",text:document.body.innerText,height:document.getElementById("parser").scrollHeight+16})},50);</' +
  250. 'script>';
  251. this.$refs.web.evalJs("document.write('" + html.replace(/'/g, "\\'") + "');document.close()");
  252. }
  253. this.$refs.web.evalJs(
  254. 'var e=document.getElementsByTagName("title");e.length&&post({action:"getTitle",title:e[0].innerText});for(var t,o=document.getElementsByTagName("style"),r=0;t=o[r];r++)t.innerHTML=t.innerHTML.replace(/body/g,"#parser");for(var n,i=document.getElementsByTagName("img"),a=[],s=0,c=0;n=i[s];s++)n.onerror=function(){post({action:"error",source:"img",target:this})},n.hasAttribute("ignore")||"A"==n.parentElement.nodeName||(n.i=c++,a.push(n.src),n.onclick=function(){post({action:"preview",img:{i:this.i,src:this.src}})});post({action:"getImgList",imgList:a});for(var m,g=document.getElementsByTagName("a"),l=0;m=g[l];l++)m.onclick=function(){var e,t=this.getAttribute("href");if("#"==t[0]){var o=document.getElementById(t.substr(1));o&&(e=o.offsetTop)}return post({action:"linkpress",href:t,offset:e}),!1};for(var u,d=document.getElementsByTagName("video"),f=0;u=d[f];f++)u.style.maxWidth="100%",u.onerror=function(){post({action:"error",source:"video",target:this})}' +
  255. (this.autopause ? ',u.onplay=function(){for(var e,t=0;e=d[t];t++)e!=this&&e.pause()}' : '') +
  256. ';for(var p,h=document.getElementsByTagName("audio"),v=0;p=h[v];v++)p.onerror=function(){post({action:"error",source:"audio",target:this})};' +
  257. (this.autoscroll ?
  258. 'for(var y,T=document.getElementsByTagName("table"),E=0;y=T[E];E++){var N=document.createElement("div");N.style.overflow="scroll",y.parentNode.replaceChild(N,y),N.appendChild(y)}' :
  259. '') + ';waitReady().then(function(e){post({action:"ready",height:e+16})})'
  260. )
  261. this.nodes = [1];
  262. // #endif
  263. // #ifdef H5
  264. if (!html) {
  265. if (this.rtf && !append) this.rtf.parentNode.removeChild(this.rtf);
  266. return;
  267. }
  268. var div = document.createElement('div');
  269. if (!append) {
  270. if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
  271. this.rtf = div;
  272. } else {
  273. if (!this.rtf) this.rtf = div;
  274. else this.rtf.appendChild(div);
  275. }
  276. div.innerHTML = this._handleHtml(html, append);
  277. for (var styles = this.rtf.getElementsByTagName('style'), i = 0, style; style = styles[i++];) {
  278. style.innerHTML = style.innerHTML.replace(/body/g, '#rtf' + this._uid);
  279. style.setAttribute('scoped', 'true');
  280. }
  281. // 懒加载
  282. if (!this._observer && this.lazyLoad && IntersectionObserver) {
  283. this._observer = new IntersectionObserver(changes => {
  284. for (let item, i = 0; item = changes[i++];) {
  285. if (item.isIntersecting) {
  286. item.target.src = item.target.getAttribute('data-src');
  287. item.target.removeAttribute('data-src');
  288. this._observer.unobserve(item.target);
  289. }
  290. }
  291. }, {
  292. rootMargin: '500px 0px 500px 0px'
  293. })
  294. }
  295. var _ts = this;
  296. // 获取标题
  297. var title = this.rtf.getElementsByTagName('title');
  298. if (title.length && this.autosetTitle)
  299. uni.setNavigationBarTitle({
  300. title: title[0].innerText
  301. })
  302. // 图片处理
  303. this.imgList.length = 0;
  304. var imgs = this.rtf.getElementsByTagName('img');
  305. for (let i = 0, j = 0, img; img = imgs[i]; i++) {
  306. var src = img.getAttribute('src');
  307. if (this.domain && src) {
  308. if (src[0] == '/') {
  309. if (src[1] == '/')
  310. img.src = (this.domain.includes('://') ? this.domain.split('://')[0] : '') + ':' + src;
  311. else img.src = this.domain + src;
  312. } else if (!src.includes('://')) img.src = this.domain + '/' + src;
  313. }
  314. if (!img.hasAttribute('ignore') && img.parentElement.nodeName != 'A') {
  315. img.i = j++;
  316. _ts.imgList.push(img.src || img.getAttribute('data-src'));
  317. img.onclick = function() {
  318. var preview = true;
  319. this.ignore = () => preview = false;
  320. _ts.$emit('imgtap', this);
  321. if (preview) {
  322. uni.previewImage({
  323. current: this.i,
  324. urls: _ts.imgList
  325. });
  326. }
  327. }
  328. }
  329. img.onerror = function() {
  330. _ts.$emit('error', {
  331. source: 'img',
  332. target: this,
  333. context: {
  334. setSrc: src => this.src = src
  335. }
  336. });
  337. }
  338. if (_ts.lazyLoad && this._observer && img.src && img.i != 0) {
  339. img.setAttribute('data-src', img.src);
  340. img.removeAttribute('src');
  341. this._observer.observe(img);
  342. }
  343. }
  344. // 链接处理
  345. var links = this.rtf.getElementsByTagName('a');
  346. for (var link of links) {
  347. link.onclick = function() {
  348. var jump = true,
  349. href = this.getAttribute('href');
  350. _ts.$emit('linkpress', {
  351. href,
  352. ignore: () => jump = false
  353. });
  354. if (jump && href) {
  355. if (href[0] == '#') {
  356. if (_ts.useAnchor) {
  357. _ts.navigateTo({
  358. id: href.substr(1)
  359. })
  360. }
  361. } else if (href.indexOf('http') == 0 || href.indexOf('//') == 0)
  362. return true;
  363. else {
  364. uni.navigateTo({
  365. url: href
  366. })
  367. }
  368. }
  369. return false;
  370. }
  371. }
  372. // 视频处理
  373. var videos = this.rtf.getElementsByTagName('video');
  374. _ts.videoContexts = videos;
  375. for (let video, i = 0; video = videos[i++];) {
  376. video.style.maxWidth = '100%';
  377. video.onerror = function() {
  378. _ts.$emit('error', {
  379. source: 'video',
  380. target: this,
  381. context: this
  382. });
  383. }
  384. video.onplay = function() {
  385. if (_ts.autopause)
  386. for (let item, i = 0; item = _ts.videoContexts[i++];)
  387. if (item != this) item.pause();
  388. }
  389. }
  390. // 音频处理
  391. var audios = this.rtf.getElementsByTagName('audio');
  392. for (var audio of audios)
  393. audio.onerror = function() {
  394. _ts.$emit('error', {
  395. source: 'audio',
  396. target: this,
  397. context: this
  398. });
  399. }
  400. // 表格处理
  401. if (this.autoscroll) {
  402. var tables = this.rtf.getElementsByTagName('table');
  403. for (var table of tables) {
  404. var div = document.createElement('div');
  405. div.style.overflow = 'scroll';
  406. table.parentNode.replaceChild(div, table);
  407. div.appendChild(table);
  408. }
  409. }
  410. if (!append) this.document.appendChild(this.rtf);
  411. this.$nextTick(() => {
  412. this.nodes = [1];
  413. this.$emit('load');
  414. });
  415. setTimeout(() => this.showAm = '', 500);
  416. // #endif
  417. // #ifndef APP-PLUS-NVUE
  418. // #ifndef H5
  419. var nodes;
  420. if (!html)
  421. return this.nodes = [];
  422. else if (typeof html == 'string') {
  423. let parser = new Parser(html, this);
  424. // 缓存读取
  425. if (this.useCache) {
  426. var hashVal = hash(html);
  427. if (cache[hashVal])
  428. nodes = cache[hashVal];
  429. else {
  430. nodes = parser.parse();
  431. cache[hashVal] = nodes;
  432. }
  433. } else nodes = parser.parse();
  434. this.$emit('parse', nodes);
  435. } else if (Object.prototype.toString.call(html) == '[object Array]') {
  436. // 非本插件产生的 array 需要进行一些转换
  437. if (html.length && html[0].PoweredBy != 'Parser') {
  438. let parser = new Parser(html, this);
  439. (function f(ns) {
  440. for (var i = 0, n; n = ns[i]; i++) {
  441. if (n.type == 'text') continue;
  442. n.attrs = n.attrs || {};
  443. for (var item in n.attrs)
  444. if (typeof n.attrs[item] != 'string') n.attrs[item] = n.attrs[item].toString();
  445. parser.matchAttr(n, parser);
  446. if (n.children && n.children.length) {
  447. parser.STACK.push(n);
  448. f(n.children);
  449. parser.popNode(parser.STACK.pop());
  450. } else n.children = void 0;
  451. }
  452. })(html);
  453. }
  454. nodes = html;
  455. } else if (typeof html == 'object' && html.nodes) {
  456. nodes = html.nodes;
  457. console.warn('错误的 html 类型:object 类型已废弃');
  458. } else
  459. return console.warn('错误的 html 类型:' + typeof html);
  460. if (append) this.nodes = this.nodes.concat(nodes);
  461. else this.nodes = nodes;
  462. if (nodes.length && nodes[0].title && this.autosetTitle)
  463. uni.setNavigationBarTitle({
  464. title: nodes[0].title
  465. })
  466. this.$nextTick(() => {
  467. this.imgList.length = 0;
  468. this.videoContexts = [];
  469. this.$emit('load');
  470. })
  471. // #endif
  472. var height;
  473. clearInterval(this._timer);
  474. this._timer = setInterval(() => {
  475. // #ifdef H5
  476. this.rect = this.rtf.getBoundingClientRect();
  477. // #endif
  478. // #ifndef H5
  479. // #ifdef APP-PLUS
  480. uni.createSelectorQuery().in(this)
  481. // #endif
  482. // #ifndef APP-PLUS
  483. this.createSelectorQuery()
  484. // #endif
  485. .select('#top').boundingClientRect().exec(res => {
  486. this.rect = res[0];
  487. // #endif
  488. if (this.rect.height == height) {
  489. this.$emit('ready', this.rect)
  490. clearInterval(this._timer);
  491. }
  492. height = this.rect.height;
  493. // #ifndef H5
  494. });
  495. // #endif
  496. }, 350);
  497. if (this.showWithAnimation && !append) this.showAm = 'animation:show .5s';
  498. // #endif
  499. },
  500. getText(ns = this.nodes) {
  501. var txt = '';
  502. // #ifdef APP-PLUS-NVUE
  503. txt = this._text;
  504. // #endif
  505. // #ifdef H5
  506. txt = this.rtf.innerText;
  507. // #endif
  508. // #ifndef H5 || APP-PLUS-NVUE
  509. for (var i = 0, n; n = ns[i++];) {
  510. if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
  511. .replace(/&amp;/g, '&');
  512. else if (n.type == 'br') txt += '\n';
  513. else {
  514. // 块级标签前后加换行
  515. var block = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] >
  516. '0' && n.name[1] < '7');
  517. if (block && txt && txt[txt.length - 1] != '\n') txt += '\n';
  518. if (n.children) txt += this.getText(n.children);
  519. if (block && txt[txt.length - 1] != '\n') txt += '\n';
  520. else if (n.name == 'td' || n.name == 'th') txt += '\t';
  521. }
  522. }
  523. // #endif
  524. return txt;
  525. },
  526. navigateTo(obj) {
  527. if (!this.useAnchor)
  528. return obj.fail && obj.fail({
  529. errMsg: 'Anchor is disabled'
  530. })
  531. // #ifdef APP-PLUS-NVUE
  532. if (!obj.id)
  533. weexDom.scrollToElement(this.$refs.web);
  534. else
  535. this.$refs.web.evalJs('var pos=document.getElementById("' + obj.id +
  536. '");if(pos)post({action:"linkpress",href:"#",offset:pos.offsetTop+' + (obj.offset || 0) + '})');
  537. obj.success && obj.success({
  538. errMsg: 'pageScrollTo:ok'
  539. });
  540. // #endif
  541. // #ifdef H5
  542. if (!obj.id) {
  543. window.scrollTo(0, this.rtf.offsetTop);
  544. return obj.success && obj.success({
  545. errMsg: 'pageScrollTo:ok'
  546. });
  547. }
  548. var target = document.getElementById(obj.id);
  549. if (!target) return obj.fail && obj.fail({
  550. errMsg: 'Label not found'
  551. });
  552. obj.scrollTop = this.rtf.offsetTop + target.offsetTop + (obj.offset || 0);
  553. uni.pageScrollTo(obj);
  554. // #endif
  555. // #ifndef H5 || APP-PLUS-NVUE
  556. var Scroll = (selector, component) => {
  557. uni.createSelectorQuery().in(component ? component : this).select(selector).boundingClientRect().selectViewport()
  558. .scrollOffset()
  559. .exec(res => {
  560. if (!res || !res[0])
  561. return obj.fail && obj.fail({
  562. errMsg: 'Label not found'
  563. });
  564. obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
  565. uni.pageScrollTo(obj);
  566. })
  567. }
  568. if (!obj.id) Scroll('#top');
  569. else {
  570. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  571. Scroll('#top >>> #' + obj.id + ', #top >>> .' + obj.id);
  572. // #endif
  573. // #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
  574. for (var anchor of this.anchors)
  575. if (anchor.id == obj.id)
  576. Scroll('#' + obj.id + ', .' + obj.id, anchor.node);
  577. // #endif
  578. }
  579. // #endif
  580. },
  581. getVideoContext(id) {
  582. // #ifndef APP-PLUS-NVUE
  583. if (!id) return this.videoContexts;
  584. else
  585. for (var i = this.videoContexts.length; i--;)
  586. if (this.videoContexts[i].id == id) return this.videoContexts[i];
  587. // #endif
  588. },
  589. // #ifdef APP-PLUS-NVUE
  590. _message(e) {
  591. // 接收 web-view 消息
  592. var data = e.detail.data[0];
  593. if (data.action == 'load') {
  594. this.$emit('load');
  595. this.height = data.height;
  596. this._text = data.text;
  597. } else if (data.action == 'getTitle') {
  598. if (this.autosetTitle)
  599. uni.setNavigationBarTitle({
  600. title: data.title
  601. })
  602. } else if (data.action == 'getImgList') {
  603. this.imgList.length = 0;
  604. for (var i = data.imgList.length; i--;)
  605. this.imgList.setItem(i, data.imgList[i]);
  606. } else if (data.action == 'preview') {
  607. var preview = true;
  608. data.img.ignore = () => preview = false;
  609. this.$emit('imgtap', data.img);
  610. if (preview)
  611. uni.previewImage({
  612. current: data.img.i,
  613. urls: this.imgList
  614. })
  615. } else if (data.action == 'linkpress') {
  616. var jump = true,
  617. href = data.href;
  618. this.$emit('linkpress', {
  619. href,
  620. ignore: () => jump = false
  621. })
  622. if (jump && href) {
  623. if (href[0] == '#') {
  624. if (this.useAnchor)
  625. weexDom.scrollToElement(this.$refs.web, {
  626. offset: data.offset
  627. })
  628. } else if (href.includes('://'))
  629. plus.runtime.openWeb(href);
  630. else
  631. uni.navigateTo({
  632. url: href
  633. })
  634. }
  635. } else if (data.action == 'error')
  636. this.$emit('error', {
  637. source: data.source,
  638. target: data.target
  639. })
  640. else if (data.action == 'ready') {
  641. this.height = data.height;
  642. this.$nextTick(() => {
  643. uni.createSelectorQuery().in(this).select('#top').boundingClientRect().exec(res => {
  644. this.rect = res[0];
  645. this.$emit('ready', res[0]);
  646. })
  647. })
  648. }
  649. },
  650. // #endif
  651. }
  652. }
  653. </script>
  654. <style scoped>
  655. @keyframes show {
  656. 0% {
  657. opacity: 0;
  658. }
  659. 100% {
  660. opacity: 1;
  661. }
  662. }
  663. /* #ifdef MP-WEIXIN */
  664. :host {
  665. display: block;
  666. overflow: scroll;
  667. -webkit-overflow-scrolling: touch;
  668. }
  669. /* #endif */
  670. </style>