mp-html.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. "use strict";
  2. const common_vendor = require("../../../../common/vendor.js");
  3. const uni_modules_mpHtml_components_mpHtml_parser = require("./parser.js");
  4. const node = () => "./node/node.js";
  5. const plugins = [];
  6. const _sfc_main = {
  7. name: "mp-html",
  8. data() {
  9. return {
  10. nodes: []
  11. };
  12. },
  13. props: {
  14. containerStyle: {
  15. type: String,
  16. default: ""
  17. },
  18. content: {
  19. type: String,
  20. default: ""
  21. },
  22. copyLink: {
  23. type: [Boolean, String],
  24. default: true
  25. },
  26. domain: String,
  27. errorImg: {
  28. type: String,
  29. default: ""
  30. },
  31. lazyLoad: {
  32. type: [Boolean, String],
  33. default: false
  34. },
  35. loadingImg: {
  36. type: String,
  37. default: ""
  38. },
  39. pauseVideo: {
  40. type: [Boolean, String],
  41. default: true
  42. },
  43. previewImg: {
  44. type: [Boolean, String],
  45. default: true
  46. },
  47. scrollTable: [Boolean, String],
  48. selectable: [Boolean, String],
  49. setTitle: {
  50. type: [Boolean, String],
  51. default: true
  52. },
  53. showImgMenu: {
  54. type: [Boolean, String],
  55. default: true
  56. },
  57. tagStyle: Object,
  58. useAnchor: [Boolean, Number]
  59. },
  60. emits: ["load", "ready", "imgtap", "linktap", "play", "error"],
  61. components: {
  62. node
  63. },
  64. watch: {
  65. content(content) {
  66. this.setContent(content);
  67. }
  68. },
  69. created() {
  70. this.plugins = [];
  71. for (let i = plugins.length; i--; ) {
  72. this.plugins.push(new plugins[i](this));
  73. }
  74. },
  75. mounted() {
  76. if (this.content && !this.nodes.length) {
  77. this.setContent(this.content);
  78. }
  79. },
  80. beforeDestroy() {
  81. this._hook("onDetached");
  82. },
  83. methods: {
  84. /**
  85. * @description 将锚点跳转的范围限定在一个 scroll-view 内
  86. * @param {Object} page scroll-view 所在页面的示例
  87. * @param {String} selector scroll-view 的选择器
  88. * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
  89. */
  90. in(page, selector, scrollTop) {
  91. if (page && selector && scrollTop) {
  92. this._in = {
  93. page,
  94. selector,
  95. scrollTop
  96. };
  97. }
  98. },
  99. /**
  100. * @description 锚点跳转
  101. * @param {String} id 要跳转的锚点 id
  102. * @param {Number} offset 跳转位置的偏移量
  103. * @returns {Promise}
  104. */
  105. navigateTo(id, offset) {
  106. return new Promise((resolve, reject) => {
  107. if (!this.useAnchor) {
  108. reject(Error("Anchor is disabled"));
  109. return;
  110. }
  111. offset = offset || parseInt(this.useAnchor) || 0;
  112. let deep = " ";
  113. deep = ">>>";
  114. const selector = common_vendor.index.createSelectorQuery().in(this._in ? this._in.page : this).select((this._in ? this._in.selector : "._root") + (id ? `${deep}#${id}` : "")).boundingClientRect();
  115. if (this._in) {
  116. selector.select(this._in.selector).scrollOffset().select(this._in.selector).boundingClientRect();
  117. } else {
  118. selector.selectViewport().scrollOffset();
  119. }
  120. selector.exec((res) => {
  121. if (!res[0]) {
  122. reject(Error("Label not found"));
  123. return;
  124. }
  125. const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset;
  126. if (this._in) {
  127. this._in.page[this._in.scrollTop] = scrollTop;
  128. } else {
  129. common_vendor.index.pageScrollTo({
  130. scrollTop,
  131. duration: 300
  132. });
  133. }
  134. resolve();
  135. });
  136. });
  137. },
  138. /**
  139. * @description 获取文本内容
  140. * @return {String}
  141. */
  142. getText(nodes) {
  143. let text = "";
  144. (function traversal(nodes2) {
  145. for (let i = 0; i < nodes2.length; i++) {
  146. const node2 = nodes2[i];
  147. if (node2.type === "text") {
  148. text += node2.text.replace(/&amp;/g, "&");
  149. } else if (node2.name === "br") {
  150. text += "\n";
  151. } else {
  152. const isBlock = node2.name === "p" || node2.name === "div" || node2.name === "tr" || node2.name === "li" || node2.name[0] === "h" && node2.name[1] > "0" && node2.name[1] < "7";
  153. if (isBlock && text && text[text.length - 1] !== "\n") {
  154. text += "\n";
  155. }
  156. if (node2.children) {
  157. traversal(node2.children);
  158. }
  159. if (isBlock && text[text.length - 1] !== "\n") {
  160. text += "\n";
  161. } else if (node2.name === "td" || node2.name === "th") {
  162. text += " ";
  163. }
  164. }
  165. }
  166. })(nodes || this.nodes);
  167. return text;
  168. },
  169. /**
  170. * @description 获取内容大小和位置
  171. * @return {Promise}
  172. */
  173. getRect() {
  174. return new Promise((resolve, reject) => {
  175. common_vendor.index.createSelectorQuery().in(this).select("#_root").boundingClientRect().exec((res) => res[0] ? resolve(res[0]) : reject(Error("Root label not found")));
  176. });
  177. },
  178. /**
  179. * @description 暂停播放媒体
  180. */
  181. pauseMedia() {
  182. for (let i = (this._videos || []).length; i--; ) {
  183. this._videos[i].pause();
  184. }
  185. },
  186. /**
  187. * @description 设置媒体播放速率
  188. * @param {Number} rate 播放速率
  189. */
  190. setPlaybackRate(rate) {
  191. this.playbackRate = rate;
  192. for (let i = (this._videos || []).length; i--; ) {
  193. this._videos[i].playbackRate(rate);
  194. }
  195. },
  196. /**
  197. * @description 设置内容
  198. * @param {String} content html 内容
  199. * @param {Boolean} append 是否在尾部追加
  200. */
  201. setContent(content, append) {
  202. if (!append || !this.imgList) {
  203. this.imgList = [];
  204. }
  205. const nodes = new uni_modules_mpHtml_components_mpHtml_parser.Parser(this).parse(content);
  206. this.$set(this, "nodes", append ? (this.nodes || []).concat(nodes) : nodes);
  207. this._videos = [];
  208. this.$nextTick(() => {
  209. this._hook("onLoad");
  210. this.$emit("load");
  211. });
  212. if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
  213. let height = 0;
  214. const callback = (rect) => {
  215. if (!rect || !rect.height)
  216. rect = {};
  217. if (rect.height === height) {
  218. this.$emit("ready", rect);
  219. } else {
  220. height = rect.height;
  221. setTimeout(() => {
  222. this.getRect().then(callback).catch(callback);
  223. }, 350);
  224. }
  225. };
  226. this.getRect().then(callback).catch(callback);
  227. } else {
  228. if (!this.imgList._unloadimgs) {
  229. this.getRect().then((rect) => {
  230. this.$emit("ready", rect);
  231. }).catch(() => {
  232. this.$emit("ready", {});
  233. });
  234. }
  235. }
  236. },
  237. /**
  238. * @description 调用插件钩子函数
  239. */
  240. _hook(name) {
  241. for (let i = plugins.length; i--; ) {
  242. if (this.plugins[i][name]) {
  243. this.plugins[i][name]();
  244. }
  245. }
  246. }
  247. }
  248. };
  249. if (!Array) {
  250. const _component_node = common_vendor.resolveComponent("node");
  251. _component_node();
  252. }
  253. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  254. return common_vendor.e({
  255. a: !$data.nodes[0]
  256. }, !$data.nodes[0] ? {} : {
  257. b: common_vendor.p({
  258. childs: $data.nodes,
  259. opts: [$props.lazyLoad, $props.loadingImg, $props.errorImg, $props.showImgMenu, $props.selectable],
  260. name: "span"
  261. })
  262. }, {
  263. c: common_vendor.n(($props.selectable ? "_select " : "") + "_root"),
  264. d: common_vendor.s($props.containerStyle)
  265. });
  266. }
  267. const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__file", "D:/zx/mall-front-app/uni_modules/mp-html/components/mp-html/mp-html.vue"]]);
  268. wx.createComponent(Component);