Initial commit

This commit is contained in:
admin
2025-12-08 14:39:07 +08:00
commit 9d4f78656b
782 changed files with 66418 additions and 0 deletions

View File

@@ -0,0 +1 @@
*.min.js

View File

@@ -0,0 +1,30 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:vue/essential"
],
"globals": {
"Component": "readonly",
"wx": "readonly",
"qq": "readonly",
"swan": "readonly",
"my": "readonly",
"tt": "readonly",
"uni": "readonly",
"plus": "readonly",
"weex": "readonly",
"requirePlugin": "readonly"
},
"rules": {
"semi": [
"error",
"never"
],
"no-console": "error"
}
}

View File

@@ -0,0 +1,23 @@
---
name: 提交 Bug
about: 如果发现某部分功能表现与文档描述不符或出错,请选择此模板反馈
title: ''
labels: ''
assignees: ''
---
<!--注意:必须严格按照下列要求反馈 bug以便尽快确定问题并修复不符合要求的反馈不予处理-->
## 使用环境
<!--请注明使用的小程序平台和基础库版本等环境信息-->
## 问题描述
<!--请详细描述遇到的问题,可以附上截图-->
## 复现方式
<!--
注意:无法复现的问题将不予处理
如果将您使用的 html 内容拷贝到示例项目中就能复现问题,请在下方附上您使用的 html 内容
如果在示例项目中无法复现,请附上能够复现的 demo 项目并说明复现方式
-->

View File

@@ -0,0 +1,14 @@
---
name: 新功能需求
about: 如果需要某些新功能或有改进建议,请选择此模板反馈
title: ''
labels: ''
assignees: ''
---
<!--注意:提出的新功能或改进建议应有一定的应用场景和通用性,如果您已经实现了改进可以直接提交 pr-->
## 新功能描述
## 应用场景

View File

@@ -0,0 +1,12 @@
---
name: 咨询问题
about: 如果找不到文档对某功能的描述或描述不清,请选择此模板咨询
title: ''
labels: ''
assignees: ''
---
<!--注意:提问前请确认已经在文档中查找过没有相关资料或文档没有描述清楚-->
## 问题描述

View File

@@ -0,0 +1,6 @@
{
"extends": [
"stylelint-config-standard",
"stylelint-config-recess-order"
]
}

21
wechat-mini-program/node_modules/mp-html/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019-present Jin Yufeng
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

241
wechat-mini-program/node_modules/mp-html/README.md generated vendored Normal file
View File

@@ -0,0 +1,241 @@
# mp-html
> 一个强大的小程序富文本组件
![star](https://img.shields.io/github/stars/jin-yufeng/mp-html)
![forks](https://img.shields.io/github/forks/jin-yufeng/mp-html)
[![npm](https://img.shields.io/npm/v/mp-html)](https://www.npmjs.com/package/mp-html)
![downloads](https://img.shields.io/npm/dt/mp-html)
[![Coverage Status](https://coveralls.io/repos/github/jin-yufeng/mp-html/badge.svg?branch=master)](https://coveralls.io/github/jin-yufeng/mp-html?branch=master)
![license](https://img.shields.io/github/license/jin-yufeng/mp-html)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
## 功能介绍
- 支持在多个主流的小程序平台和 `uni-app` 中使用
- 支持丰富的标签(包括 `table``video``svg` 等)
- 支持丰富的事件效果(自动预览图片、链接处理等)
- 支持设置占位图(加载中、出错时、预览时)
- 支持锚点跳转、长按复制等丰富功能
- 支持大部分 *html* 实体
- 丰富的插件(关键词搜索、内容编辑、`latex` 公式等)
- 效率高、容错性强且轻量化(`≈25KB``9KB gzipped`
查看 [功能介绍](https://jin-yufeng.github.io/mp-html/#/overview/feature) 了解更多
## 使用方法
### 原生平台
- `npm` 方式
1. 在项目目录下安装组件包
```bash
npm install mp-html
```
2. 开发者工具中勾选 `使用 npm 模块`(若没有此选项则不需要)并点击 `工具 - 构建 npm`
3. 在需要使用页面的 `json` 文件中添加
```json
{
"usingComponents": {
"mp-html": "mp-html"
}
}
```
4. 在需要使用页面的 `wxml` 文件中添加
```html
<mp-html content="{{html}}" />
```
5. 在需要使用页面的 `js` 文件中添加
```javascript
Page({
onLoad () {
this.setData({
html: '<div>Hello World!</div>'
})
}
})
```
- 源码方式
1. 将源码中对应平台的代码包(`dist/platform`)拷贝到 `components` 目录下,更名为 `mp-html`
2. 在需要使用页面的 `json` 文件中添加
```json
{
"usingComponents": {
"mp-html": "/components/mp-html/index"
}
}
```
后续步骤同上
查看 [快速开始](https://jin-yufeng.github.io/mp-html/#/overview/quickstart) 了解更多
### uni-app
- 源码方式
1. 将源码中 `dist/uni-app` 内的内容拷贝到项目根目录下
可以直接通过 [插件市场](https://ext.dcloud.net.cn/plugin?id=805) 引入
2. 在需要使用页面的 `vue` 文件中添加
```vue
<template>
<view>
<mp-html :content="html" />
</view>
</template>
<script>
import mpHtml from '@/components/mp-html/mp-html'
export default {
// HBuilderX 2.5.5+ 可以通过 easycom 自动引入
components: {
mpHtml
},
data () {
return {
html: '<div>Hello World!</div>'
}
}
}
</script>
```
- `npm` 方式
1. 在项目目录下安装组件包
```bash
npm install mp-html
```
2. 在需要使用页面的 `vue` 文件中添加
```vue
<template>
<view>
<mp-html :content="html" />
</view>
</template>
<script>
import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html'
export default {
// 不可省略
components: {
mpHtml
},
data () {
return {
html: '<div>Hello World!</div>'
}
}
}
</script>
```
使用 `cli` 方式运行的项目,通过 `npm` 方式引入时,需要在 `vue.config.js` 中配置 `transpileDependencies`,详情可见 [#330](https://github.com/jin-yufeng/mp-html/issues/330#issuecomment-913617687)
如果在 `nvue` 中使用还要将 `dist/uni-app/static` 目录下的内容拷贝到项目的 `static` 目录下,否则无法运行
查看 [快速开始](https://jin-yufeng.github.io/mp-html/#/overview/quickstart) 了解更多
## 组件属性
| 属性 | 类型 | 默认值 | 说明 |
|:---:|:---:|:---:|---|
| container-style | String | | 容器的样式([2.1.0+](https://jin-yufeng.github.io/mp-html/#/changelog/changelog#v210) |
| content | String | | 用于渲染的 html 字符串 |
| copy-link | Boolean | true | 是否允许外部链接被点击时自动复制 |
| domain | String | | 主域名(用于链接拼接) |
| error-img | String | | 图片出错时的占位图链接 |
| lazy-load | Boolean | false | 是否开启图片懒加载 |
| loading-img | String | | 图片加载过程中的占位图链接 |
| pause-video | Boolean | true | 是否在播放一个视频时自动暂停其他视频 |
| preview-img | Boolean | true | 是否允许图片被点击时自动预览 |
| scroll-table | Boolean | false | 是否给每个表格添加一个滚动层使其能单独横向滚动 |
| selectable | Boolean | false | 是否开启文本长按复制 |
| set-title | Boolean | true | 是否将 title 标签的内容设置到页面标题 |
| show-img-menu | Boolean | true | 是否允许图片被长按时显示菜单 |
| tag-style | Object | | 设置标签的默认样式 |
| use-anchor | Boolean | false | 是否使用锚点链接 |
查看 [属性](https://jin-yufeng.github.io/mp-html/#/basic/prop) 了解更多
## 组件事件
| 名称 | 触发时机 |
|:---:|---|
| load | dom 树加载完毕时 |
| ready | 图片加载完毕时 |
| error | 发生渲染错误时 |
| imgtap | 图片被点击时 |
| linktap | 链接被点击时 |
查看 [事件](https://jin-yufeng.github.io/mp-html/#/basic/event) 了解更多
## api
组件实例上提供了一些 `api` 方法可供调用
| 名称 | 作用 |
|:---:|---|
| in | 将锚点跳转的范围限定在一个 scroll-view 内 |
| navigateTo | 锚点跳转 |
| getText | 获取文本内容 |
| getRect | 获取富文本内容的位置和大小 |
| setContent | 设置富文本内容 |
| imgList | 获取所有图片的数组 |
| pauseMedia | 暂停播放音视频([2.2.2+](https://jin-yufeng.github.io/mp-html/#/changelog/changelog#v222) |
| setPlaybackRate | 设置音视频播放速率([2.4.0+](https://jin-yufeng.github.io/mp-html/#/changelog/changelog#v240) |
查看 [api](https://jin-yufeng.github.io/mp-html/#/advanced/api) 了解更多
## 插件扩展
除基本功能外,本组件还提供了丰富的扩展,可按照需要选用
| 名称 | 作用 |
|:---:|---|
| audio | 音乐播放器 |
| editable | 富文本编辑 |
| emoji | 解析 emoji |
| highlight | 代码块高亮显示 |
| markdown | 渲染 markdown |
| search | 关键词搜索 |
| style | 匹配 style 标签中的样式 |
| txv-video | 使用腾讯视频 |
| img-cache | 图片缓存 by [@PentaTea](https://github.com/PentaTea) |
| latex | 渲染 latex 公式 by [@Zeng-J](https://github.com/Zeng-J) |
| card | 卡片展示 by [@whoooami](https://github.com/whoooami) |
查看 [插件](https://jin-yufeng.github.io/mp-html/#/advanced/plugin) 了解更多
## 许可与支持
- 许可
您可以免费的使用(包括商用)、复制或修改本组件 [MIT License](https://github.com/jin-yufeng/mp-html/blob/master/LICENSE)
在用于生产环境前务必经过充分测试,由插件 `bug` 带来的损失概不负责(可以自行修改源码)
- 联系
欢迎加入 `QQ` 交流群:
群1已满`699734691`
群2已满`778239129`
群3`960265313`
## 更新日志
- v2.5.1 (20250420)
1. `U` `uni-app` 包适配鸿蒙 `APP` [详细](https://github.com/jin-yufeng/mp-html/issues/615)
2. `U` 微信小程序替换废弃 `api` `getSystemInfoSync` [详细](https://github.com/jin-yufeng/mp-html/issues/613)
3. `F` 修复了微信小程序 `glass-easel` 框架下真机换行异常的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/607) by [@PaperStrike](https://github.com/PaperStrike)
4. `F` 修复了 `uni-app` 包 `app` 端播放视频可能报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/617)
5. `F` 修复了 `latex` 插件可能出现 `xxx can be used only in display mode` 的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/632)
6. `F` 修复了 `uni-app` 包 `latex` 公式可能不显示的问题 [#599](https://github.com/jin-yufeng/mp-html/issues/599)、[#627](https://github.com/jin-yufeng/mp-html/issues/627)
- v2.5.0 (20240422)
1. `U` `play` 事件增加返回 `src` 等信息 [详细](https://github.com/jin-yufeng/mp-html/issues/526)
2. `U` `preview-img` 属性支持设置为 `all` 开启 `base64` 图片预览 [详细](https://github.com/jin-yufeng/mp-html/issues/536)
3. `U` `editable` 插件增加简易模式(点击文字直接编辑)
4. `U` `latex` 插件支持块级公式 [详细](https://github.com/jin-yufeng/mp-html/issues/582)
5. `F` 修复了表格部分情况下背景丢失的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/587)
6. `F` 修复了部分 `svg` 无法显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/591)
7. `F` 修复了 `uni-app` 包 `h5` 和 `app` 端部分情况下样式无法识别的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/518)
8. `F` 修复了 `latex` 插件部分情况下显示不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/580)
9. `F` 修复了 `editable` 插件表格无法删除的问题
10. `F` 修复了 `editable` 插件 `uni-app` 包 `vue3` `h5` 端点击图片报错的问题
11. `F` 修复了 `editable` 插件 `uni-app` 包点击表格没有菜单栏的问题
从 `1.x` 的升级方法可见 [更新指南](https://jin-yufeng.github.io/mp-html/#/changelog/changelog?id=v200)
查看 [更新日志](https://jin-yufeng.github.io/mp-html/#/changelog/changelog) 了解更多

View File

@@ -0,0 +1 @@
._root{padding:1px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}._select{-webkit-user-select:text;user-select:text}

View File

@@ -0,0 +1 @@
<view class="_root {{selectable?'_select':''}}" style="{{containerStyle}}"><slot a:if="{{!nodes[0]}}"/><node id="_root" childs="{{nodes}}" opts="{{[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]}}" onAdd="_add"/></view>

View File

@@ -0,0 +1,8 @@
"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}/*!
* mp-html v2.5.1
* https://github.com/jin-yufeng/mp-html
*
* Released under the MIT license
* Author: Jin Yufeng
*/
var t=require("./parser"),n=[];Component({data:{nodes:[]},props:{containerStyle:"",content:"",copyLink:!0,domain:"",errorImg:"",lazyLoad:!1,loadingImg:"",pauseVideo:!0,previewImg:!0,scrollTable:!1,setTitle:!0,showImgMenu:!0,tagStyle:{}},didMount:function(){this.plugins=[];for(var e=n.length;e--;)this.plugins.push(new n[e](this));this.props.content&&this.setContent(this.props.content)},didUpdate:function(e){e.content!==this.props.content&&this.setContent(this.props.content)},didUnmount:function(){this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(t,n){var i=this;return new Promise(function(o,r){if(!i.props.useAnchor)return void r(Error("Anchor is disabled"));var s=my.createSelectorQuery().select((i._in?i._in.selector:"._root")+(t?"".concat(" ","#").concat(t):"")).boundingClientRect();i._in?s.select(i._in.selector).scrollOffset().select(i._in.selector).boundingClientRect():s.selectViewport().scrollOffset(),s.exec(function(t){if(!t[0])return void r(Error("Label not found"));var s=t[1].scrollTop+t[0].top-(t[2]?t[2].top:0)+(n||parseInt(i.props.useAnchor)||0);i._in?i._in.page.setData(e({},i._in.scrollTop,s)):my.pageScrollTo({scrollTop:s,duration:300}),o()})})},getText:function(e){var t="";return function e(n){for(var i=0;i<n.length;i++){var o=n[i];if("text"===o.type)t+=o.text.replace(/&amp;/g,"&");else if("br"===o.name)t+="\n";else{var r="p"===o.name||"div"===o.name||"tr"===o.name||"li"===o.name||"h"===o.name[0]&&o.name[1]>"0"&&o.name[1]<"7";r&&t&&"\n"!==t[t.length-1]&&(t+="\n"),o.children&&e(o.children),r&&"\n"!==t[t.length-1]?t+="\n":"td"!==o.name&&"th"!==o.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){return new Promise(function(e,t){my.createSelectorQuery().select("._root").boundingClientRect().exec(function(n){return n[0]?e(n[0]):t(Error("Root label not found"))})})},pauseMedia:function(){for(var e=(this._videos||[]).length;e--;)this._videos[e].pause()},setPlaybackRate:function(e){this.playbackRate=e;for(var t=(this._videos||[]).length;t--;)this._videos[t].playbackRate(e)},setContent:function(e,n){var i=this;this.imgList&&n||(this.imgList=[]),this._videos=[];var o={},r=new t(this).parse(e);if(n)for(var s=this.data.nodes.length,a=r.length;a--;)o["nodes[".concat(s+a,"]")]=r[a];else o.nodes=r;if(this.setData(o,function(){i._hook("onLoad"),i.props.onLoad&&i.props.onLoad()}),this.props.lazyLoad||this.imgList._unloadimgs<this.imgList.length/2){var l=0,c=function e(t){t&&t.height||(t={}),t.height===l?i.props.onReady&&i.props.onReady(t):(l=t.height,setTimeout(function(){i.getRect().then(e).catch(e)},350))};this.getRect().then(c).catch(c)}else this.imgList._unloadimgs||this.getRect().then(function(e){i.props.onReady&&i.props.onReady(e)}).catch(function(){i.props.onReady&&i.props.onReady({})})},_hook:function(e){for(var t=n.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()},_add:function(e){e.root=this}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

View File

@@ -0,0 +1 @@
._a{padding:1.5px 0 1.5px 0;color:#366092;word-break:break-all}._hover{text-decoration:underline;opacity:.7}._img{max-width:100%;-webkit-touch-callout:none}._b,._strong{font-weight:700}._code{font-family:monospace}._del{text-decoration:line-through}._em,._i{font-style:italic}._h1{font-size:2em}._h2{font-size:1.5em}._h3{font-size:1.17em}._h5{font-size:.83em}._h6{font-size:.67em}._h1,._h2,._h3,._h4,._h5,._h6{display:block;font-weight:700}._ins{text-decoration:underline}._li{display:list-item}._ol{list-style-type:decimal}._ol,._ul{display:block;padding-left:40px;margin:1em 0}._q::before{content:'"'}._q::after{content:'"'}._sub{font-size:smaller;vertical-align:sub}._sup{font-size:smaller;vertical-align:super}._tbody,._tfoot,._thead{display:table-row-group}._tr{display:table-row}._td,._th{display:table-cell;vertical-align:middle}._th{font-weight:700;text-align:center}._ul{list-style-type:disc}._ul ._ul{margin:0;list-style-type:circle}._ul ._ul ._ul{list-style-type:square}._abbr,._b,._code,._del,._em,._i,._ins,._label,._q,._span,._strong,._sub,._sup{display:inline}

View File

@@ -0,0 +1 @@
<template name="el"><block a:if="{{n.name==='img'}}"><rich-text a:if="{{n.t}}" style="display:{{n.t}}" nodes="<img class='_img' style='{{n.attrs.style}}' src='{{n.attrs.src}}'>" data-i="{{i}}" catchTap="imgTap"/><block a:else><image a:if="{{(opts[1]&&!ctrl[i])||ctrl[i]<0}}" class="_img" style="{{n.attrs.style}}" src="{{ctrl[i]<0?opts[2]:opts[1]}}" mode="widthFix"/><image 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':(n.m||'scaleToFill'))}}" lazy-load="{{opts[0]}}" data-i="{{i}}" onLoad="imgLoad" onError="mediaError" catchTap="imgTap" onLongTap="noop"/></block></block><text a:elif="{{n.name==='br'}}">{{'\n'}}</text><view a:elif="{{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}}" catchTap="linkTap"><template is="node" data="{{childs:n.children,path:i+'_',opts:opts,ctrl:ctrl}}"></template></view><video a:elif="{{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}}" onPlay="play" onError="mediaError"/><audio a:elif="{{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}}" onPlay="play" onError="mediaError"/><rich-text a:else id="{{n.attrs.id}}" style="display:inline;{{n.f}}" nodes="{{[n]}}"/></template><template name="node"><block a:for="{{childs}}" a:for-item="n" a:for-index="i" a:key="i"><template a:if="{{!n.c}}" is="el" data="{{n:n,i:path+i,opts:opts,ctrl:ctrl}}"/><view a:else id="{{n.attrs.id}}" class="_{{n.name}} {{n.attrs.class}}" style="{{n.attrs.style}}"><template is="node" data="{{childs:n.children,path:path+i+'_',opts:opts,ctrl:ctrl}}"></template></view></block></template><template is="node" data="{{childs:childs,path:'',opts:opts,ctrl:ctrl}}"></template>

View File

@@ -0,0 +1 @@
"use strict";function t(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,i)}return r}function e(e){for(var i=1;i<arguments.length;i++){var o=null!=arguments[i]?arguments[i]:{};i%2?t(Object(o),!0).forEach(function(t){r(e,t,o[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))})}return e}function r(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}Component({data:{ctrl:{}},props:{childs:[],opts:[]},options:{addGlobalClass:!0},didMount:function(){this.props.onAdd(this)},methods:{noop:function(){},getNode:function(t){try{for(var e=t.split("_"),r=this.props.childs[e[0]],i=1;i<e.length;i++)r=r.children[e[i]];return r}catch(t){return{text:"",attrs:{},children:[]}}},play:function(t){var r=t.target.dataset.i,i=this.getNode(r);if(this.root.props.onPlay&&this.root.props.onPlay({source:i.name,attrs:e(e({},i.attrs),{},{src:i.src[this.data.ctrl[r]||0]})}),this.root.props.pauseVideo){for(var o=!1,a=t.target.id,s=this.root._videos.length;s--;)this.root._videos[s].id===a?o=!0:this.root._videos[s].pause();if(!o){var n=my.createVideoContext(a,this);n.id=a,this.root.playbackRate&&n.playbackRate(this.root.playbackRate),this.root._videos.push(n)}}},imgTap:function(t){var e=this.getNode(t.target.dataset.i);if(e.a)return this.linkTap(e.a);if(!e.attrs.ignore&&(this.root.props.onImgtap&&this.root.props.onImgtap(e.attrs),this.root.props.previewImg)){var r=e.i;my.previewImage({enablesavephoto:this.root.props.showImgMenu,enableShowPhotoDownload:this.root.props.showImgMenu,current:r,urls:this.root.imgList})}},imgLoad:function(t){var e,i=t.target.dataset.i,o=this.getNode(i);o.w?(this.props.opts[1]&&!this.data.ctrl[i]||-1===this.data.ctrl[i])&&(e=1):e=t.detail.width,e&&this.setData(r({},"ctrl."+i,e)),this.checkReady()},checkReady:function(){var t=this;this.root.props.lazyLoad||(this.root.imgList._unloadimgs-=1,this.root.imgList._unloadimgs||setTimeout(function(){t.root.getRect().then(function(e){t.root.props.onReady&&t.root.props.onReady(e)}).catch(function(){t.root.props.onReady&&t.root.props.onReady({})})},350))},linkTap:function(t){var e=t.currentTarget?this.getNode(t.currentTarget.dataset.i):{},r=e.attrs||t,i=r.href;this.root.props.onLinktap&&this.root.props.onLinktap(Object.assign({innerText:this.root.getText(e.children||[])},r)),i&&("#"===i[0]?this.root.navigateTo(i.substring(1)).catch(function(){}):i.split("?")[0].includes("://")?this.root.props.copyLink&&my.setClipboard({text:i,success:function(){return my.showToast({content:"链接已复制"})}}):my.navigateTo({url:i,fail:function(){my.switchTab({url:i,fail:function(){}})}}))},mediaError:function(t){var e=t.target.dataset.i,i=this.getNode(e);if("video"===i.name||"audio"===i.name){var o=(this.data.ctrl[e]||0)+1;if(o>i.src.length&&(o=0),o<i.src.length)return this.setData(r({},"ctrl."+e,o))}else"img"===i.name&&(this.props.opts[2]&&this.setData(r({},"ctrl."+e,-1)),this.checkReady());this.root&&this.root.props.onError&&this.root.props.onError({source:i.name,attrs:i.attrs,errMsg:t.detail.errMsg})}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node"}}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
._root{padding:1px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}._select{-webkit-user-select:text;user-select:text}

View File

@@ -0,0 +1,8 @@
"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}/*!
* mp-html v2.5.1
* https://github.com/jin-yufeng/mp-html
*
* Released under the MIT license
* Author: Jin Yufeng
*/
var t=require("./parser"),n=[];Component({data:{nodes:[]},properties:{containerStyle:String,content:{type:String,value:"",observer:function(e){this.setContent(e)}},copyLink:{type:Boolean,value:!0},domain:String,errorImg:String,lazyLoad:Boolean,loadingImg:String,pauseVideo:{type:Boolean,value:!0},previewImg:{type:null,value:!0},scrollTable:Boolean,selectable:null,setTitle:{type:Boolean,value:!0},showImgMenu:{type:Boolean,value:!0},tagStyle:Object,useAnchor:null},created:function(){this.plugins=[];for(var e=n.length;e--;)this.plugins.push(new n[e](this))},detached:function(){this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(t,n){var i=this;return new Promise(function(o,r){if(!i.data.useAnchor)return void r(Error("Anchor is disabled"));var a=swan.createSelectorQuery().in(i._in?i._in.page:i).select((i._in?i._in.selector:"._root")+(t?"".concat(" ","#").concat(t):"")).boundingClientRect();i._in?a.select(i._in.selector).scrollOffset().select(i._in.selector).boundingClientRect():a.selectViewport().scrollOffset(),a.exec(function(t){if(!t[0])return void r(Error("Label not found"));var a=t[1].scrollTop+t[0].top-(t[2]?t[2].top:0)+(n||parseInt(i.data.useAnchor)||0);i._in?i._in.page.setData(e({},i._in.scrollTop,a)):swan.pageScrollTo({scrollTop:a,duration:300}),o()})})},getText:function(e){var t="";return function e(n){for(var i=0;i<n.length;i++){var o=n[i];if("text"===o.type)t+=o.text.replace(/&amp;/g,"&");else if("br"===o.name)t+="\n";else{var r="p"===o.name||"div"===o.name||"tr"===o.name||"li"===o.name||"h"===o.name[0]&&o.name[1]>"0"&&o.name[1]<"7";r&&t&&"\n"!==t[t.length-1]&&(t+="\n"),o.children&&e(o.children),r&&"\n"!==t[t.length-1]?t+="\n":"td"!==o.name&&"th"!==o.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){var e=this;return new Promise(function(t,n){swan.createSelectorQuery().in(e).select("._root").boundingClientRect().exec(function(e){return e[0]?t(e[0]):n(Error("Root label not found"))})})},pauseMedia:function(){for(var e=(this._videos||[]).length;e--;)this._videos[e].pause()},setPlaybackRate:function(e){this.playbackRate=e;for(var t=(this._videos||[]).length;t--;)this._videos[t].playbackRate(e)},setContent:function(e,n){var i=this;this.imgList&&n||(this.imgList=[]),this._videos=[];var o={},r=new t(this).parse(e);if(n)for(var a=this.data.nodes.length,s=r.length;s--;)o["nodes[".concat(a+s,"]")]=r[s];else o.nodes=r;if(this.setData(o,function(){i._hook("onLoad"),i.triggerEvent("load")}),this.data.lazyLoad||this.imgList._unloadimgs<this.imgList.length/2){var l=0,c=function e(t){t&&t.height||(t={}),t.height===l?i.triggerEvent("ready",t):(l=t.height,setTimeout(function(){i.getRect().then(e).catch(e)},350))};this.getRect().then(c).catch(c)}else this.imgList._unloadimgs||this.getRect().then(function(e){i.triggerEvent("ready",e)}).catch(function(){i.triggerEvent("ready",{})})},_hook:function(e){for(var t=n.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()},_add:function(e){e.detail.root=this}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

View File

@@ -0,0 +1 @@
<view class="_root {{selectable?'_select':''}}" style="{{containerStyle}}"><slot s-if="!nodes[0]"/><node id="_root" childs="{{nodes}}" opts="{{[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]}}" catchadd="_add"/></view>

View File

@@ -0,0 +1 @@
._a{padding:1.5px 0 1.5px 0;color:#366092;word-break:break-all}._hover{text-decoration:underline;opacity:.7}._img{max-width:100%;-webkit-touch-callout:none}._b,._strong{font-weight:700}._code{font-family:monospace}._del{text-decoration:line-through}._em,._i{font-style:italic}._h1{font-size:2em}._h2{font-size:1.5em}._h3{font-size:1.17em}._h5{font-size:.83em}._h6{font-size:.67em}._h1,._h2,._h3,._h4,._h5,._h6{display:block;font-weight:700}._ins{text-decoration:underline}._li{display:list-item}._ol{list-style-type:decimal}._ol,._ul{display:block;padding-left:40px;margin:1em 0}._q::before{content:'"'}._q::after{content:'"'}._sub{font-size:smaller;vertical-align:sub}._sup{font-size:smaller;vertical-align:super}._tbody,._tfoot,._thead{display:table-row-group}._tr{display:table-row}._td,._th{display:table-cell;vertical-align:middle}._th{font-weight:700;text-align:center}._ul{list-style-type:disc}._ul ._ul{margin:0;list-style-type:circle}._ul ._ul ._ul{list-style-type:square}._abbr,._b,._code,._del,._em,._i,._ins,._label,._q,._span,._strong,._sub,._sup{display:inline}._blockquote,._div,._p{display:block}

View File

@@ -0,0 +1 @@
"use strict";function t(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,i)}return r}function e(e){for(var i=1;i<arguments.length;i++){var o=null!=arguments[i]?arguments[i]:{};i%2?t(Object(o),!0).forEach(function(t){r(e,t,o[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))})}return e}function r(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}Component({data:{ctrl:{}},properties:{childs:Array,opts:Array},options:{addGlobalClass:!0},attached:function(){this.triggerEvent("add",this,{bubbles:!0,composed:!0})},methods:{noop:function(){},getNode:function(t){try{for(var e=t.split("_"),r=this.data.childs[e[0]],i=1;i<e.length;i++)r=r.children[e[i]];return r}catch(t){return{text:"",attrs:{},children:[]}}},play:function(t){var r=t.target.dataset.i,i=this.getNode(r);if(this.root.triggerEvent("play",{source:i.name,attrs:e(e({},i.attrs),{},{src:i.src[this.data.ctrl[r]||0]})}),this.root.data.pauseVideo){for(var o=!1,a=t.target.id,s=this.root._videos.length;s--;)this.root._videos[s].id===a?o=!0:this.root._videos[s].pause();if(!o){var n=swan.createVideoContext(a);n.id=a,this.root.playbackRate&&n.playbackRate(this.root.playbackRate),this.root._videos.push(n)}}},imgTap:function(t){var e=this.getNode(t.target.dataset.i);if(e.a)return this.linkTap(e.a);if(!e.attrs.ignore&&(this.root.triggerEvent("imgtap",e.attrs),this.root.data.previewImg)){var r=this.root.imgList[e.i];swan.previewImage({current:r,urls:this.root.imgList})}},imgLoad:function(t){var e,i=t.target.dataset.i,o=this.getNode(i);o.w?(this.data.opts[1]&&!this.data.ctrl[i]||-1===this.data.ctrl[i])&&(e=1):e=t.detail.width,e&&this.setData(r({},"ctrl."+i,e)),this.checkReady()},checkReady:function(){var t=this;this.root.data.lazyLoad||(this.root.imgList._unloadimgs-=1,this.root.imgList._unloadimgs||setTimeout(function(){t.root.getRect().then(function(e){t.root.triggerEvent("ready",e)}).catch(function(){t.root.triggerEvent("ready",{})})},350))},linkTap:function(t){var e=t.currentTarget?this.getNode(t.currentTarget.dataset.i):{},r=e.attrs||t,i=r.href;this.root.triggerEvent("linktap",Object.assign({innerText:this.root.getText(e.children||[])},r)),i&&("#"===i[0]?this.root.navigateTo(i.substring(1)).catch(function(){}):i.split("?")[0].includes("://")?this.root.data.copyLink&&swan.setClipboardData({data:i,success:function(){return swan.showToast({title:"链接已复制"})}}):swan.navigateTo({url:i,fail:function(){swan.switchTab({url:i,fail:function(){}})}}))},mediaError:function(t){var e=t.target.dataset.i,i=this.getNode(e);if("video"===i.name||"audio"===i.name){var o=(this.data.ctrl[e]||0)+1;if(o>i.src.length&&(o=0),o<i.src.length)return this.setData(r({},"ctrl."+e,o))}else"img"===i.name&&(this.data.opts[2]&&this.setData(r({},"ctrl."+e,-1)),this.checkReady());this.root&&this.root.triggerEvent("error",{source:i.name,attrs:i.attrs,errMsg:t.detail.errMsg})}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node"}}

View File

@@ -0,0 +1 @@
<template name="el"><block s-if="n.name==='img'"><rich-text s-if="n.t" style="display:{{n.t}}" nodes="<img class='_img' style='{{n.attrs.style}}' src='{{n.attrs.src}}'>" data-i="{{i}}" catchtap="imgTap"/><block s-else><image s-if="(opts[1]&&!ctrl[i])||ctrl[i]<0" class="_img" style="{{n.attrs.style}}" src="{{ctrl[i]<0?opts[2]:opts[1]}}" mode="widthFix"/><image 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':(n.m||'scaleToFill'))}}" lazy-load="{{opts[0]}}" webp="{{n.webp}}" image-menu-prevent="{{!opts[3]||n.attrs.ignore}}" data-i="{{i}}" bindload="imgLoad" binderror="mediaError" catchtap="imgTap" bindlongpress="noop"/></block></block><text s-elif="{{n.name==='br'}}">{{'\n'}}</text><view s-elif="{{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}}" catchtap="linkTap"><block s-for="n.children" s-key="index"><template is="el" data="{{{n:item,i:i+'_'+index,opts:opts,ctrl:ctrl}}}"></template></block></view><video s-elif="{{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}}" bindplay="play" binderror="mediaError"/><audio s-elif="{{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}}" bindplay="play" binderror="mediaError"/><rich-text s-else id="{{n.attrs.id}}" style="{{n.f}}" selectable="{{opts[4]}}" nodes="{{[n]}}"/></template><block s-for="childs" s-for-item="n1" s-for-index="i1" s-key="i1"><template s-if="!n1.c" is="el" data="{{{n:n1,i:''+i1,opts:opts,ctrl:ctrl}}}"/><view s-else id="{{n1.attrs.id}}" class="_{{n1.name}} {{n1.attrs.class}}" style="{{n1.attrs.style}}"><block s-for="n1.children" s-for-item="n2" s-for-index="i2" s-key="i2"><template s-if="!n2.c" is="el" data="{{{n:n2,i:i1+'_'+i2,opts:opts,ctrl:ctrl}}}"/><view s-else id="{{n2.attrs.id}}" class="_{{n2.name}} {{n2.attrs.class}}" style="{{n2.attrs.style}}"><block s-for="n2.children" s-for-item="n3" s-for-index="i3" s-key="i3"><template s-if="!n3.c" is="el" data="{{{n:n3,i:i1+'_'+i2+'_'+i3,opts:opts,ctrl:ctrl}}}"/><view s-else id="{{n3.attrs.id}}" class="_{{n3.name}} {{n3.attrs.class}}" style="{{n3.attrs.style}}"><block s-for="n3.children" s-for-item="n4" s-for-index="i4" s-key="i4"><template s-if="!n4.c" is="el" data="{{{n:n4,i:i1+'_'+i2+'_'+i3+'_'+i4,opts:opts,ctrl:ctrl}}}"/><view s-else id="{{n4.attrs.id}}" class="_{{n4.name}} {{n4.attrs.class}}" style="{{n4.attrs.style}}"><block s-for="n4.children" s-for-item="n5" s-for-index="i5" s-key="i5"><template s-if="!n5.c" is="el" data="{{{n:n5,i:i1+'_'+i2+'_'+i3+'_'+i4+'_'+i5,opts:opts,ctrl:ctrl}}}"/><node s-else id="{{n5.attrs.id}}" class="_{{n5.name}} {{n5.attrs.class}}" style="{{n5.attrs.style}}" childs="{{n5.children}}" opts="{{opts}}"/></block></view></block></view></block></view></block></view></block>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}/*!
* mp-html v2.5.1
* https://github.com/jin-yufeng/mp-html
*
* Released under the MIT license
* Author: Jin Yufeng
*/
var t=require("./parser"),n=[];Component({data:{nodes:[]},properties:{containerStyle:String,content:{type:String,value:"",observer:function(e){this.setContent(e)}},copyLink:{type:Boolean,value:!0},domain:String,errorImg:String,lazyLoad:Boolean,loadingImg:String,pauseVideo:{type:Boolean,value:!0},previewImg:{type:null,value:!0},scrollTable:Boolean,selectable:null,setTitle:{type:Boolean,value:!0},showImgMenu:{type:Boolean,value:!0},tagStyle:Object,useAnchor:null},created:function(){this.plugins=[];for(var e=n.length;e--;)this.plugins.push(new n[e](this))},detached:function(){this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(t,n){var i=this;return new Promise(function(o,r){if(!i.data.useAnchor)return void r(Error("Anchor is disabled"));var a=qq.createSelectorQuery().in(i._in?i._in.page:i).select((i._in?i._in.selector:"._root")+(t?"".concat(">>>","#").concat(t):"")).boundingClientRect();i._in?a.select(i._in.selector).scrollOffset().select(i._in.selector).boundingClientRect():a.selectViewport().scrollOffset(),a.exec(function(t){if(!t[0])return void r(Error("Label not found"));var a=t[1].scrollTop+t[0].top-(t[2]?t[2].top:0)+(n||parseInt(i.data.useAnchor)||0);i._in?i._in.page.setData(e({},i._in.scrollTop,a)):qq.pageScrollTo({scrollTop:a,duration:300}),o()})})},getText:function(e){var t="";return function e(n){for(var i=0;i<n.length;i++){var o=n[i];if("text"===o.type)t+=o.text.replace(/&amp;/g,"&");else if("br"===o.name)t+="\n";else{var r="p"===o.name||"div"===o.name||"tr"===o.name||"li"===o.name||"h"===o.name[0]&&o.name[1]>"0"&&o.name[1]<"7";r&&t&&"\n"!==t[t.length-1]&&(t+="\n"),o.children&&e(o.children),r&&"\n"!==t[t.length-1]?t+="\n":"td"!==o.name&&"th"!==o.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){var e=this;return new Promise(function(t,n){qq.createSelectorQuery().in(e).select("._root").boundingClientRect().exec(function(e){return e[0]?t(e[0]):n(Error("Root label not found"))})})},pauseMedia:function(){for(var e=(this._videos||[]).length;e--;)this._videos[e].pause()},setPlaybackRate:function(e){this.playbackRate=e;for(var t=(this._videos||[]).length;t--;)this._videos[t].playbackRate(e)},setContent:function(e,n){var i=this;this.imgList&&n||(this.imgList=[]),this._videos=[];var o={},r=new t(this).parse(e);if(n)for(var a=this.data.nodes.length,s=r.length;s--;)o["nodes[".concat(a+s,"]")]=r[s];else o.nodes=r;if(this.setData(o,function(){i._hook("onLoad"),i.triggerEvent("load")}),this.data.lazyLoad||this.imgList._unloadimgs<this.imgList.length/2){var l=0,c=function e(t){t&&t.height||(t={}),t.height===l?i.triggerEvent("ready",t):(l=t.height,setTimeout(function(){i.getRect().then(e).catch(e)},350))};this.getRect().then(c).catch(c)}else this.imgList._unloadimgs||this.getRect().then(function(e){i.triggerEvent("ready",e)}).catch(function(){i.triggerEvent("ready",{})})},_hook:function(e){for(var t=n.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()},_add:function(e){e.detail.root=this}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

View File

@@ -0,0 +1 @@
<view class="_root {{selectable?'_select':''}}" style="{{containerStyle}}"><slot qq:if="{{!nodes[0]}}"/><node id="_root" childs="{{nodes}}" opts="{{[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]}}" catchadd="_add"/></view>

View File

@@ -0,0 +1 @@
._root{padding:1px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}._select{-webkit-user-select:text;user-select:text}

View File

@@ -0,0 +1 @@
"use strict";function t(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,i)}return r}function e(e){for(var i=1;i<arguments.length;i++){var o=null!=arguments[i]?arguments[i]:{};i%2?t(Object(o),!0).forEach(function(t){r(e,t,o[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))})}return e}function r(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}Component({data:{ctrl:{}},properties:{childs:Array,opts:Array},options:{addGlobalClass:!0},attached:function(){this.triggerEvent("add",this,{bubbles:!0,composed:!0})},methods:{noop:function(){},getNode:function(t){try{for(var e=t.split("_"),r=this.data.childs[e[0]],i=1;i<e.length;i++)r=r.children[e[i]];return r}catch(t){return{text:"",attrs:{},children:[]}}},play:function(t){var r=t.target.dataset.i,i=this.getNode(r);if(this.root.triggerEvent("play",{source:i.name,attrs:e(e({},i.attrs),{},{src:i.src[this.data.ctrl[r]||0]})}),this.root.data.pauseVideo){for(var o=!1,a=t.target.id,s=this.root._videos.length;s--;)this.root._videos[s].id===a?o=!0:this.root._videos[s].pause();if(!o){var n=qq.createVideoContext(a,this);n.id=a,this.root.playbackRate&&n.playbackRate(this.root.playbackRate),this.root._videos.push(n)}}},imgTap:function(t){var e=this.getNode(t.target.dataset.i);if(e.a)return this.linkTap(e.a);if(!e.attrs.ignore&&(this.root.triggerEvent("imgtap",e.attrs),this.root.data.previewImg)){var r=this.root.imgList[e.i];qq.previewImage({current:r,urls:this.root.imgList})}},imgLoad:function(t){var e,i=t.target.dataset.i,o=this.getNode(i);o.w?(this.data.opts[1]&&!this.data.ctrl[i]||-1===this.data.ctrl[i])&&(e=1):e=t.detail.width,e&&this.setData(r({},"ctrl."+i,e)),this.checkReady()},checkReady:function(){var t=this;this.root.data.lazyLoad||(this.root.imgList._unloadimgs-=1,this.root.imgList._unloadimgs||setTimeout(function(){t.root.getRect().then(function(e){t.root.triggerEvent("ready",e)}).catch(function(){t.root.triggerEvent("ready",{})})},350))},linkTap:function(t){var e=t.currentTarget?this.getNode(t.currentTarget.dataset.i):{},r=e.attrs||t,i=r.href;this.root.triggerEvent("linktap",Object.assign({innerText:this.root.getText(e.children||[])},r)),i&&("#"===i[0]?this.root.navigateTo(i.substring(1)).catch(function(){}):i.split("?")[0].includes("://")?this.root.data.copyLink&&qq.setClipboardData({data:i,success:function(){return qq.showToast({title:"链接已复制"})}}):qq.navigateTo({url:i,fail:function(){qq.switchTab({url:i,fail:function(){}})}}))},mediaError:function(t){var e=t.target.dataset.i,i=this.getNode(e);if("video"===i.name||"audio"===i.name){var o=(this.data.ctrl[e]||0)+1;if(o>i.src.length&&(o=0),o<i.src.length)return this.setData(r({},"ctrl."+e,o))}else"img"===i.name&&(this.data.opts[2]&&this.setData(r({},"ctrl."+e,-1)),this.checkReady());this.root&&this.root.triggerEvent("error",{source:i.name,attrs:i.attrs,errMsg:t.detail.errMsg})}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node"}}

View File

@@ -0,0 +1 @@
<qs module="isInline">var e={abbr:!0,b:!0,big:!0,code:!0,del:!0,em:!0,i:!0,ins:!0,label:!0,q:!0,small:!0,span:!0,strong:!0,sub:!0,sup:!0};module.exports=function(n,i){return e[n]||-1!==(i||"").indexOf("inline")};</qs><template name="el"><block qq:if="{{n.name==='img'}}"><rich-text qq:if="{{n.t}}" style="display:{{n.t}}" nodes="<img class='_img' style='{{n.attrs.style}}' src='{{n.attrs.src}}'>" data-i="{{i}}" catchtap="imgTap"/><block qq:else><image qq:if="{{(opts[1]&&!ctrl[i])||ctrl[i]<0}}" class="_img" style="{{n.attrs.style}}" src="{{ctrl[i]<0?opts[2]:opts[1]}}" mode="widthFix"/><image 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':(n.m||'scaleToFill'))}}" lazy-load="{{opts[0]}}" data-i="{{i}}" bindload="imgLoad" binderror="mediaError" catchtap="imgTap" bindlongpress="noop"/></block></block><text qq:elif="{{n.text}}" decode>{{n.text}}</text><text qq:elif="{{n.name==='br'}}">{{'\n'}}</text><view qq:elif="{{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}}" catchtap="linkTap"><node childs="{{n.children}}" opts="{{opts}}" style="display:inherit"/></view><video qq:elif="{{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}}" bindplay="play" binderror="mediaError"/><audio qq:elif="{{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}}" bindplay="play" binderror="mediaError"/><rich-text qq:else id="{{n.attrs.id}}" style="{{n.f}}" nodes="{{[n]}}"/></template><block qq:for="{{childs}}" qq:for-item="n1" qq:for-index="i1" qq:key="i1"><template qq:if="{{!n1.c&&(!n1.children||n1.name==='a'||!isInline(n1.name,n1.attrs.style))}}" is="el" data="{{n:n1,i:''+i1,opts:opts,ctrl:ctrl}}"/><view qq:else id="{{n1.attrs.id}}" class="_{{n1.name}} {{n1.attrs.class}}" style="{{n1.attrs.style}}"><block qq:for="{{n1.children}}" qq:for-item="n2" qq:for-index="i2" qq:key="i2"><template qq:if="{{!n2.c&&(!n2.children||n2.name==='a'||!isInline(n2.name,n2.attrs.style))}}" is="el" data="{{n:n2,i:i1+'_'+i2,opts:opts,ctrl:ctrl}}"/><view qq:else id="{{n2.attrs.id}}" class="_{{n2.name}} {{n2.attrs.class}}" style="{{n2.attrs.style}}"><block qq:for="{{n2.children}}" qq:for-item="n3" qq:for-index="i3" qq:key="i3"><template qq:if="{{!n3.c&&(!n3.children||n3.name==='a'||!isInline(n3.name,n3.attrs.style))}}" is="el" data="{{n:n3,i:i1+'_'+i2+'_'+i3,opts:opts,ctrl:ctrl}}"/><view qq:else id="{{n3.attrs.id}}" class="_{{n3.name}} {{n3.attrs.class}}" style="{{n3.attrs.style}}"><block qq:for="{{n3.children}}" qq:for-item="n4" qq:for-index="i4" qq:key="i4"><template qq:if="{{!n4.c&&(!n4.children||n4.name==='a'||!isInline(n4.name,n4.attrs.style))}}" is="el" data="{{n:n4,i:i1+'_'+i2+'_'+i3+'_'+i4,opts:opts,ctrl:ctrl}}"/><view qq:else id="{{n4.attrs.id}}" class="_{{n4.name}} {{n4.attrs.class}}" style="{{n4.attrs.style}}"><block qq:for="{{n4.children}}" qq:for-item="n5" qq:for-index="i5" qq:key="i5"><template qq:if="{{!n5.c&&(!n5.children||n5.name==='a'||!isInline(n5.name,n5.attrs.style))}}" is="el" data="{{n:n5,i:i1+'_'+i2+'_'+i3+'_'+i4+'_'+i5,opts:opts,ctrl:ctrl}}"/><node qq:else id="{{n5.attrs.id}}" class="_{{n5.name}} {{n5.attrs.class}}" style="{{n5.attrs.style}}" childs="{{n5.children}}" opts="{{opts}}"/></block></view></block></view></block></view></block></view></block>

View File

@@ -0,0 +1 @@
._a{padding:1.5px 0 1.5px 0;color:#366092;word-break:break-all}._hover{text-decoration:underline;opacity:.7}._img{max-width:100%;-webkit-touch-callout:none}._b,._strong{font-weight:700}._code{font-family:monospace}._del{text-decoration:line-through}._em,._i{font-style:italic}._h1{font-size:2em}._h2{font-size:1.5em}._h3{font-size:1.17em}._h5{font-size:.83em}._h6{font-size:.67em}._h1,._h2,._h3,._h4,._h5,._h6{display:block;font-weight:700}._ins{text-decoration:underline}._li{display:list-item}._ol{list-style-type:decimal}._ol,._ul{display:block;padding-left:40px;margin:1em 0}._q::before{content:'"'}._q::after{content:'"'}._sub{font-size:smaller;vertical-align:sub}._sup{font-size:smaller;vertical-align:super}._tbody,._tfoot,._thead{display:table-row-group}._tr{display:table-row}._td,._th{display:table-cell;vertical-align:middle}._th{font-weight:700;text-align:center}._ul{list-style-type:disc}._ul ._ul{margin:0;list-style-type:circle}._ul ._ul ._ul{list-style-type:square}._abbr,._b,._code,._del,._em,._i,._ins,._label,._q,._span,._strong,._sub,._sup{display:inline}._blockquote,._div,._p{display:block}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}/*!
* mp-html v2.5.1
* https://github.com/jin-yufeng/mp-html
*
* Released under the MIT license
* Author: Jin Yufeng
*/
var t=require("./parser"),n=[];Component({data:{nodes:[]},properties:{containerStyle:String,content:{type:String,value:"",observer:function(e){this.setContent(e)}},copyLink:{type:Boolean,value:!0},domain:String,errorImg:String,lazyLoad:Boolean,loadingImg:String,pauseVideo:{type:Boolean,value:!0},previewImg:{type:null,value:!0},scrollTable:Boolean,selectable:null,setTitle:{type:Boolean,value:!0},showImgMenu:{type:Boolean,value:!0},tagStyle:Object,useAnchor:null},created:function(){this.plugins=[];for(var e=n.length;e--;)this.plugins.push(new n[e](this))},detached:function(){this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(t,n){var i=this;return new Promise(function(o,r){if(!i.data.useAnchor)return void r(Error("Anchor is disabled"));var a=tt.createSelectorQuery().in(i._in?i._in.page:i).select((i._in?i._in.selector:"._root")+(t?"".concat(">>>","#").concat(t):"")).boundingClientRect();i._in?a.select(i._in.selector).scrollOffset().select(i._in.selector).boundingClientRect():a.selectViewport().scrollOffset(),a.exec(function(t){if(!t[0])return void r(Error("Label not found"));var a=t[1].scrollTop+t[0].top-(t[2]?t[2].top:0)+(n||parseInt(i.data.useAnchor)||0);i._in?i._in.page.setData(e({},i._in.scrollTop,a)):tt.pageScrollTo({scrollTop:a,duration:300}),o()})})},getText:function(e){var t="";return function e(n){for(var i=0;i<n.length;i++){var o=n[i];if("text"===o.type)t+=o.text.replace(/&amp;/g,"&");else if("br"===o.name)t+="\n";else{var r="p"===o.name||"div"===o.name||"tr"===o.name||"li"===o.name||"h"===o.name[0]&&o.name[1]>"0"&&o.name[1]<"7";r&&t&&"\n"!==t[t.length-1]&&(t+="\n"),o.children&&e(o.children),r&&"\n"!==t[t.length-1]?t+="\n":"td"!==o.name&&"th"!==o.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){var e=this;return new Promise(function(t,n){tt.createSelectorQuery().in(e).select("._root").boundingClientRect().exec(function(e){return e[0]?t(e[0]):n(Error("Root label not found"))})})},pauseMedia:function(){for(var e=(this._videos||[]).length;e--;)this._videos[e].pause()},setPlaybackRate:function(e){this.playbackRate=e;for(var t=(this._videos||[]).length;t--;)this._videos[t].playbackRate(e)},setContent:function(e,n){var i=this;this.imgList&&n||(this.imgList=[]),this._videos=[];var o={},r=new t(this).parse(e);if(n)for(var a=this.data.nodes.length,s=r.length;s--;)o["nodes[".concat(a+s,"]")]=r[s];else o.nodes=r;if(this.setData(o),this.selectComponent("#_root",function(e){e.root=i,i._hook("onLoad"),i.triggerEvent("load")}),this.data.lazyLoad||this.imgList._unloadimgs<this.imgList.length/2){var l=0,c=function e(t){t&&t.height||(t={}),t.height===l?i.triggerEvent("ready",t):(l=t.height,setTimeout(function(){i.getRect().then(e).catch(e)},350))};this.getRect().then(c).catch(c)}else this.imgList._unloadimgs||this.getRect().then(function(e){i.triggerEvent("ready",e)}).catch(function(){i.triggerEvent("ready",{})})},_hook:function(e){for(var t=n.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

View File

@@ -0,0 +1 @@
<view class="_root {{selectable?'_select':''}}" style="{{containerStyle}}"><slot tt:if="{{!nodes[0]}}"/><node id="_root" childs="{{nodes}}" opts="{{[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]}}"/></view>

View File

@@ -0,0 +1 @@
._root{padding:1px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}._select{-webkit-user-select:text;user-select:text}

View File

@@ -0,0 +1 @@
"use strict";function t(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,i)}return r}function e(e){for(var i=1;i<arguments.length;i++){var o=null!=arguments[i]?arguments[i]:{};i%2?t(Object(o),!0).forEach(function(t){r(e,t,o[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))})}return e}function r(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}Component({data:{ctrl:{}},properties:{childs:Array,opts:Array},options:{addGlobalClass:!0},methods:{noop:function(){},getNode:function(t){try{for(var e=t.split("_"),r=this.data.childs[e[0]],i=1;i<e.length;i++)r=r.children[e[i]];return r}catch(t){return{text:"",attrs:{},children:[]}}},play:function(t){var r=t.target.dataset.i,i=this.getNode(r);if(this.root.triggerEvent("play",{source:i.name,attrs:e(e({},i.attrs),{},{src:i.src[this.data.ctrl[r]||0]})}),this.root.data.pauseVideo){for(var o=!1,a=t.target.id,s=this.root._videos.length;s--;)this.root._videos[s].id===a?o=!0:this.root._videos[s].pause();if(!o){var n=tt.createVideoContext(a,this);n.id=a,this.root.playbackRate&&n.playbackRate(this.root.playbackRate),this.root._videos.push(n)}}},imgTap:function(t){var e=this.getNode(t.target.dataset.i);if(e.a)return this.linkTap(e.a);if(!e.attrs.ignore&&(this.root.triggerEvent("imgtap",e.attrs),this.root.data.previewImg)){var r=this.root.imgList[e.i];tt.previewImage({current:r,urls:this.root.imgList})}},imgLoad:function(t){var e,i=t.target.dataset.i,o=this.getNode(i);o.w?(this.data.opts[1]&&!this.data.ctrl[i]||-1===this.data.ctrl[i])&&(e=1):e=t.detail.width,e&&e!==this.data.ctrl[i]&&this.setData(r({},"ctrl."+i,e)),this.checkReady()},checkReady:function(){var t=this;this.root.data.lazyLoad||(this.root.imgList._unloadimgs-=1,this.root.imgList._unloadimgs||setTimeout(function(){t.root.getRect().then(function(e){t.root.triggerEvent("ready",e)}).catch(function(){t.root.triggerEvent("ready",{})})},350))},linkTap:function(t){var e=t.currentTarget?this.getNode(t.currentTarget.dataset.i):{},r=e.attrs||t,i=r.href;this.root.triggerEvent("linktap",Object.assign({innerText:this.root.getText(e.children||[])},r)),i&&("#"===i[0]?this.root.navigateTo(i.substring(1)).catch(function(){}):i.split("?")[0].includes("://")?this.root.data.copyLink&&tt.setClipboardData({data:i,success:function(){return tt.showToast({title:"链接已复制"})}}):tt.navigateTo({url:i,fail:function(){tt.switchTab({url:i,fail:function(){}})}}))},mediaError:function(t){var e=t.target.dataset.i,i=this.getNode(e);if("video"===i.name||"audio"===i.name){var o=(this.data.ctrl[e]||0)+1;if(o>i.src.length&&(o=0),o<i.src.length)return this.setData(r({},"ctrl."+e,o))}else"img"===i.name&&(this.data.opts[2]&&this.setData(r({},"ctrl."+e,-1)),this.checkReady());this.root&&this.root.triggerEvent("error",{source:i.name,attrs:i.attrs,errMsg:t.detail.errMsg})}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node"}}

View File

@@ -0,0 +1 @@
<template name="el"><block tt:if="{{n.name==='img'}}"><rich-text tt:if="{{n.t}}" style="display:{{n.t}}" nodes="<img class='_img' style='{{n.attrs.style}}' src='{{n.attrs.src}}'>" data-i="{{i}}" catchtap="imgTap"/><block tt:else><image tt:if="{{(opts[1]&&!ctrl[i])||ctrl[i]<0}}" class="_img" style="{{n.attrs.style}}" src="{{ctrl[i]<0?opts[2]:opts[1]}}" mode="widthFix"/><image 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':(n.m||'scaleToFill'))}}" lazy-load="{{opts[0]}}" data-i="{{i}}" bindload="imgLoad" binderror="mediaError" catchtap="imgTap" bindlongpress="noop"/></block></block><text tt:elif="{{n.text}}" decode>{{n.text}}</text><text tt:elif="{{n.name==='br'}}">{{'\n'}}</text><view tt:elif="{{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}}" catchtap="linkTap"><template is="node" data="{{childs:n.children,path:i+'_',opts:opts,ctrl:ctrl}}"></template></view><video tt:elif="{{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}}" bindplay="play" binderror="mediaError"/><rich-text tt:else id="{{n.attrs.id}}" style="{{n.f}}" nodes="{{[n]}}"/></template><template name="node"><block tt:for="{{childs}}" tt:for-item="n" tt:for-index="i" tt:key="i"><template tt:if="{{!n.c}}" is="el" data="{{n:n,i:path+i,opts:opts,ctrl:ctrl}}"/><view tt:else id="{{n.attrs.id}}" class="_{{n.name}} {{n.attrs.class}}" style="{{n.attrs.style}}"><template is="node" data="{{childs:n.children,path:path+i+'_',opts:opts,ctrl:ctrl}}"></template></view></block></template><template is="node" data="{{childs:childs,path:'',opts:opts,ctrl:ctrl}}"></template>

View File

@@ -0,0 +1 @@
._a{padding:1.5px 0 1.5px 0;color:#366092;word-break:break-all}._hover{text-decoration:underline;opacity:.7}._img{max-width:100%;-webkit-touch-callout:none}._b,._strong{font-weight:700}._code{font-family:monospace}._del{text-decoration:line-through}._em,._i{font-style:italic}._h1{font-size:2em}._h2{font-size:1.5em}._h3{font-size:1.17em}._h5{font-size:.83em}._h6{font-size:.67em}._h1,._h2,._h3,._h4,._h5,._h6{display:block;font-weight:700}._ins{text-decoration:underline}._li{display:list-item}._ol{list-style-type:decimal}._ol,._ul{display:block;padding-left:40px;margin:1em 0}._q::before{content:'"'}._q::after{content:'"'}._sub{font-size:smaller;vertical-align:sub}._sup{font-size:smaller;vertical-align:super}._tbody,._tfoot,._thead{display:table-row-group}._tr{display:table-row}._td,._th{display:table-cell;vertical-align:middle}._th{font-weight:700;text-align:center}._ul{list-style-type:disc}._ul ._ul{margin:0;list-style-type:circle}._ul ._ul ._ul{list-style-type:square}._abbr,._b,._code,._del,._em,._i,._ins,._label,._q,._span,._strong,._sub,._sup{display:inline}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}/*!
* mp-html v2.5.1
* https://github.com/jin-yufeng/mp-html
*
* Released under the MIT license
* Author: Jin Yufeng
*/
var t=require("./parser"),n=[];Component({data:{nodes:[]},properties:{containerStyle:String,content:{type:String,value:"",observer:function(e){this.setContent(e)}},copyLink:{type:Boolean,value:!0},domain:String,errorImg:String,lazyLoad:Boolean,loadingImg:String,pauseVideo:{type:Boolean,value:!0},previewImg:{type:null,value:!0},scrollTable:Boolean,selectable:null,setTitle:{type:Boolean,value:!0},showImgMenu:{type:Boolean,value:!0},tagStyle:Object,useAnchor:null},created:function(){this.plugins=[];for(var e=n.length;e--;)this.plugins.push(new n[e](this))},detached:function(){this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(t,n){var i=this;return new Promise(function(o,r){if(!i.data.useAnchor)return void r(Error("Anchor is disabled"));var a=wx.createSelectorQuery().in(i._in?i._in.page:i).select((i._in?i._in.selector:"._root")+(t?"".concat(">>>","#").concat(t):"")).boundingClientRect();i._in?a.select(i._in.selector).scrollOffset().select(i._in.selector).boundingClientRect():a.selectViewport().scrollOffset(),a.exec(function(t){if(!t[0])return void r(Error("Label not found"));var a=t[1].scrollTop+t[0].top-(t[2]?t[2].top:0)+(n||parseInt(i.data.useAnchor)||0);i._in?i._in.page.setData(e({},i._in.scrollTop,a)):wx.pageScrollTo({scrollTop:a,duration:300}),o()})})},getText:function(e){var t="";return function e(n){for(var i=0;i<n.length;i++){var o=n[i];if("text"===o.type)t+=o.text.replace(/&amp;/g,"&");else if("br"===o.name)t+="\n";else{var r="p"===o.name||"div"===o.name||"tr"===o.name||"li"===o.name||"h"===o.name[0]&&o.name[1]>"0"&&o.name[1]<"7";r&&t&&"\n"!==t[t.length-1]&&(t+="\n"),o.children&&e(o.children),r&&"\n"!==t[t.length-1]?t+="\n":"td"!==o.name&&"th"!==o.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){var e=this;return new Promise(function(t,n){wx.createSelectorQuery().in(e).select("._root").boundingClientRect().exec(function(e){return e[0]?t(e[0]):n(Error("Root label not found"))})})},pauseMedia:function(){for(var e=(this._videos||[]).length;e--;)this._videos[e].pause()},setPlaybackRate:function(e){this.playbackRate=e;for(var t=(this._videos||[]).length;t--;)this._videos[t].playbackRate(e)},setContent:function(e,n){var i=this;this.imgList&&n||(this.imgList=[]),this._videos=[];var o={},r=new t(this).parse(e);if(n)for(var a=this.data.nodes.length,s=r.length;s--;)o["nodes[".concat(a+s,"]")]=r[s];else o.nodes=r;if(this.setData(o,function(){i._hook("onLoad"),i.triggerEvent("load")}),this.data.lazyLoad||this.imgList._unloadimgs<this.imgList.length/2){var l=0,c=function e(t){t&&t.height||(t={}),t.height===l?i.triggerEvent("ready",t):(l=t.height,setTimeout(function(){i.getRect().then(e).catch(e)},350))};this.getRect().then(c).catch(c)}else this.imgList._unloadimgs||this.getRect().then(function(e){i.triggerEvent("ready",e)}).catch(function(){i.triggerEvent("ready",{})})},_hook:function(e){for(var t=n.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()},_add:function(e){e.detail.root=this}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

View File

@@ -0,0 +1 @@
<view class="_root {{selectable?'_select':''}}" style="{{containerStyle}}"><slot wx:if="{{!nodes[0]}}"/><node id="_root" childs="{{nodes}}" opts="{{[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]}}" catchadd="_add"/></view>

View File

@@ -0,0 +1 @@
._root{padding:1px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}._select{-webkit-user-select:text;user-select:text}

View File

@@ -0,0 +1 @@
"use strict";function t(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,i)}return r}function e(e){for(var i=1;i<arguments.length;i++){var o=null!=arguments[i]?arguments[i]:{};i%2?t(Object(o),!0).forEach(function(t){r(e,t,o[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))})}return e}function r(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}Component({data:{ctrl:{},isiOS:wx.getSystemInfoSync().system.includes("iOS")},properties:{childs:Array,opts:Array},options:{addGlobalClass:!0},attached:function(){this.triggerEvent("add",this,{bubbles:!0,composed:!0})},methods:{noop:function(){},getNode:function(t){try{for(var e=t.split("_"),r=this.data.childs[e[0]],i=1;i<e.length;i++)r=r.children[e[i]];return r}catch(t){return{text:"",attrs:{},children:[]}}},play:function(t){var r=t.target.dataset.i,i=this.getNode(r);if(this.root.triggerEvent("play",{source:i.name,attrs:e(e({},i.attrs),{},{src:i.src[this.data.ctrl[r]||0]})}),this.root.data.pauseVideo){for(var o=!1,s=t.target.id,a=this.root._videos.length;a--;)this.root._videos[a].id===s?o=!0:this.root._videos[a].pause();if(!o){var n=wx.createVideoContext(s,this);n.id=s,this.root.playbackRate&&n.playbackRate(this.root.playbackRate),this.root._videos.push(n)}}},imgTap:function(t){var e=this.getNode(t.target.dataset.i);if(e.a)return this.linkTap(e.a);if(!e.attrs.ignore&&(this.root.triggerEvent("imgtap",e.attrs),this.root.data.previewImg)){var r=this.root.imgList[e.i];wx.previewImage({showmenu:this.root.data.showImgMenu,current:r,urls:this.root.imgList})}},imgLoad:function(t){var e,i=t.target.dataset.i,o=this.getNode(i);o.w?(this.data.opts[1]&&!this.data.ctrl[i]||-1===this.data.ctrl[i])&&(e=1):e=t.detail.width,e&&this.setData(r({},"ctrl."+i,e)),this.checkReady()},checkReady:function(){var t=this;this.root.data.lazyLoad||(this.root.imgList._unloadimgs-=1,this.root.imgList._unloadimgs||setTimeout(function(){t.root.getRect().then(function(e){t.root.triggerEvent("ready",e)}).catch(function(){t.root.triggerEvent("ready",{})})},350))},linkTap:function(t){var e=t.currentTarget?this.getNode(t.currentTarget.dataset.i):{},r=e.attrs||t,i=r.href;this.root.triggerEvent("linktap",Object.assign({innerText:this.root.getText(e.children||[])},r)),i&&("#"===i[0]?this.root.navigateTo(i.substring(1)).catch(function(){}):i.split("?")[0].includes("://")?this.root.data.copyLink&&wx.setClipboardData({data:i,success:function(){return wx.showToast({title:"链接已复制"})}}):wx.navigateTo({url:i,fail:function(){wx.switchTab({url:i,fail:function(){}})}}))},mediaError:function(t){var e=t.target.dataset.i,i=this.getNode(e);if("video"===i.name||"audio"===i.name){var o=(this.data.ctrl[e]||0)+1;if(o>i.src.length&&(o=0),o<i.src.length)return this.setData(r({},"ctrl."+e,o))}else"img"===i.name&&(this.data.opts[2]&&this.setData(r({},"ctrl."+e,-1)),this.checkReady());this.root&&this.root.triggerEvent("error",{source:i.name,attrs:i.attrs,errMsg:t.detail.errMsg})}}});

View File

@@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node"}}

View File

@@ -0,0 +1 @@
<wxs module="isInline">var e={abbr:!0,b:!0,big:!0,code:!0,del:!0,em:!0,i:!0,ins:!0,label:!0,q:!0,small:!0,span:!0,strong:!0,sub:!0,sup:!0};module.exports=function(n,i){return e[n]||-1!==(i||"").indexOf("inline")};</wxs><template name="el"><block wx:if="{{n.name==='img'}}"><rich-text wx:if="{{n.t}}" style="display:{{n.t}}" nodes="<img class='_img' style='{{n.attrs.style}}' src='{{n.attrs.src}}'>" data-i="{{i}}" catchtap="imgTap"/><block wx:else><image wx:if="{{(opts[1]&&!ctrl[i])||ctrl[i]<0}}" class="_img" style="{{n.attrs.style}}" src="{{ctrl[i]<0?opts[2]:opts[1]}}" mode="widthFix"/><image 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':(n.m||'scaleToFill'))}}" lazy-load="{{opts[0]}}" webp="{{n.webp}}" show-menu-by-longpress="{{opts[3]&&!n.attrs.ignore}}" data-i="{{i}}" bindload="imgLoad" binderror="mediaError" catchtap="imgTap" bindlongpress="noop"/></block></block><text wx:elif="{{n.text}}" user-select="{{opts[4]=='force'&&isiOS}}" decode>{{n.text}}</text><text wx:elif="{{n.name==='br'}}">{{'\n'}}</text><view wx:elif="{{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}}" catchtap="linkTap"><node childs="{{n.children}}" opts="{{opts}}" style="display:inherit"/></view><video wx:elif="{{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}}" bindplay="play" binderror="mediaError"/><audio wx:elif="{{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}}" bindplay="play" binderror="mediaError"/><rich-text wx:else id="{{n.attrs.id}}" style="{{n.f}}" user-select="{{opts[4]}}" nodes="{{[n]}}"/></template><block wx:for="{{childs}}" wx:for-item="n1" wx:for-index="i1" wx:key="i1"><template wx:if="{{!n1.c&&(!n1.children||n1.name==='a'||!isInline(n1.name,n1.attrs.style))}}" is="el" data="{{n:n1,i:''+i1,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n1.attrs.id}}" class="_{{n1.name}} {{n1.attrs.class}}" style="{{n1.attrs.style}}"><block wx:for="{{n1.children}}" wx:for-item="n2" wx:for-index="i2" wx:key="i2"><template wx:if="{{!n2.c&&(!n2.children||n2.name==='a'||!isInline(n2.name,n2.attrs.style))}}" is="el" data="{{n:n2,i:i1+'_'+i2,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n2.attrs.id}}" class="_{{n2.name}} {{n2.attrs.class}}" style="{{n2.attrs.style}}"><block wx:for="{{n2.children}}" wx:for-item="n3" wx:for-index="i3" wx:key="i3"><template wx:if="{{!n3.c&&(!n3.children||n3.name==='a'||!isInline(n3.name,n3.attrs.style))}}" is="el" data="{{n:n3,i:i1+'_'+i2+'_'+i3,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n3.attrs.id}}" class="_{{n3.name}} {{n3.attrs.class}}" style="{{n3.attrs.style}}"><block wx:for="{{n3.children}}" wx:for-item="n4" wx:for-index="i4" wx:key="i4"><template wx:if="{{!n4.c&&(!n4.children||n4.name==='a'||!isInline(n4.name,n4.attrs.style))}}" is="el" data="{{n:n4,i:i1+'_'+i2+'_'+i3+'_'+i4,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n4.attrs.id}}" class="_{{n4.name}} {{n4.attrs.class}}" style="{{n4.attrs.style}}"><block wx:for="{{n4.children}}" wx:for-item="n5" wx:for-index="i5" wx:key="i5"><template wx:if="{{!n5.c&&(!n5.children||n5.name==='a'||!isInline(n5.name,n5.attrs.style))}}" is="el" data="{{n:n5,i:i1+'_'+i2+'_'+i3+'_'+i4+'_'+i5,opts:opts,ctrl:ctrl}}"/><node wx:else id="{{n5.attrs.id}}" class="_{{n5.name}} {{n5.attrs.class}}" style="{{n5.attrs.style}}" childs="{{n5.children}}" opts="{{opts}}"/></block></view></block></view></block></view></block></view></block>

View File

@@ -0,0 +1 @@
._a{padding:1.5px 0 1.5px 0;color:#366092;word-break:break-all}._hover{text-decoration:underline;opacity:.7}._img{max-width:100%;-webkit-touch-callout:none}._b,._strong{font-weight:700}._code{font-family:monospace}._del{text-decoration:line-through}._em,._i{font-style:italic}._h1{font-size:2em}._h2{font-size:1.5em}._h3{font-size:1.17em}._h5{font-size:.83em}._h6{font-size:.67em}._h1,._h2,._h3,._h4,._h5,._h6{display:block;font-weight:700}._ins{text-decoration:underline}._li{display:list-item}._ol{list-style-type:decimal}._ol,._ul{display:block;padding-left:40px;margin:1em 0}._q::before{content:'"'}._q::after{content:'"'}._sub{font-size:smaller;vertical-align:sub}._sup{font-size:smaller;vertical-align:super}._tbody,._tfoot,._thead{display:table-row-group}._tr{display:table-row}._td,._th{display:table-cell;vertical-align:middle}._th{font-weight:700;text-align:center}._ul{list-style-type:disc}._ul ._ul{margin:0;list-style-type:circle}._ul ._ul ._ul{list-style-type:square}._abbr,._b,._code,._del,._em,._i,._ins,._label,._q,._span,._strong,._sub,._sup{display:inline}._blockquote,._div,._p{display:block}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,498 @@
<template>
<view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
<slot v-if="!nodes[0]" />
<!-- #ifndef APP-PLUS-NVUE -->
<node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]" name="span" />
<!-- #endif -->
<!-- #ifdef APP-PLUS-NVUE -->
<web-view ref="web" src="/static/app-plus/mp-html/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
<!-- #endif -->
</view>
</template>
<script>
/**
* mp-html v2.5.1
* @description 富文本组件
* @tutorial https://github.com/jin-yufeng/mp-html
* @property {String} container-style 容器的样式
* @property {String} content 用于渲染的 html 字符串
* @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
* @property {String} domain 主域名,用于拼接链接
* @property {String} error-img 图片出错时的占位图链接
* @property {Boolean} lazy-load 是否开启图片懒加载
* @property {string} loading-img 图片加载过程中的占位图链接
* @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
* @property {Boolean} preview-img 是否允许图片被点击时自动预览
* @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
* @property {Boolean | String} selectable 是否开启长按复制
* @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
* @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
* @property {Object} tag-style 标签的默认样式
* @property {Boolean | Number} use-anchor 是否使用锚点链接
* @event {Function} load dom 结构加载完毕时触发
* @event {Function} ready 所有图片加载完毕时触发
* @event {Function} imgtap 图片被点击时触发
* @event {Function} linktap 链接被点击时触发
* @event {Function} play 音视频播放时触发
* @event {Function} error 媒体加载出错时触发
*/
// #ifndef APP-PLUS-NVUE
import node from './node/node'
// #endif
import Parser from './parser'
const plugins=[]
// #ifdef APP-PLUS-NVUE
const dom = weex.requireModule('dom')
// #endif
export default {
name: 'mp-html',
data () {
return {
nodes: [],
// #ifdef APP-PLUS-NVUE
height: 3
// #endif
}
},
props: {
containerStyle: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
copyLink: {
type: [Boolean, String],
default: true
},
domain: String,
errorImg: {
type: String,
default: ''
},
lazyLoad: {
type: [Boolean, String],
default: false
},
loadingImg: {
type: String,
default: ''
},
pauseVideo: {
type: [Boolean, String],
default: true
},
previewImg: {
type: [Boolean, String],
default: true
},
scrollTable: [Boolean, String],
selectable: [Boolean, String],
setTitle: {
type: [Boolean, String],
default: true
},
showImgMenu: {
type: [Boolean, String],
default: true
},
tagStyle: Object,
useAnchor: [Boolean, Number]
},
// #ifdef VUE3
emits: ['load', 'ready', 'imgtap', 'linktap', 'play', 'error'],
// #endif
// #ifndef APP-PLUS-NVUE
components: {
node
},
// #endif
watch: {
content (content) {
this.setContent(content)
}
},
created () {
this.plugins = []
for (let i = plugins.length; i--;) {
this.plugins.push(new plugins[i](this))
}
},
mounted () {
if (this.content && !this.nodes.length) {
this.setContent(this.content)
}
},
beforeDestroy () {
this._hook('onDetached')
},
methods: {
/**
* @description 将锚点跳转的范围限定在一个 scroll-view 内
* @param {Object} page scroll-view 所在页面的示例
* @param {String} selector scroll-view 的选择器
* @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
*/
in (page, selector, scrollTop) {
// #ifndef APP-PLUS-NVUE
if (page && selector && scrollTop) {
this._in = {
page,
selector,
scrollTop
}
}
// #endif
},
/**
* @description 锚点跳转
* @param {String} id 要跳转的锚点 id
* @param {Number} offset 跳转位置的偏移量
* @returns {Promise}
*/
navigateTo (id, offset) {
return new Promise((resolve, reject) => {
if (!this.useAnchor) {
reject(Error('Anchor is disabled'))
return
}
offset = offset || parseInt(this.useAnchor) || 0
// #ifdef APP-PLUS-NVUE
if (!id) {
dom.scrollToElement(this.$refs.web, {
offset
})
resolve()
} else {
this._navigateTo = {
resolve,
reject,
offset
}
this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})')
}
// #endif
// #ifndef APP-PLUS-NVUE
let deep = ' '
// #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
deep = '>>>'
// #endif
const selector = uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this._in ? this._in.page : this)
// #endif
.select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect()
if (this._in) {
selector.select(this._in.selector).scrollOffset()
.select(this._in.selector).boundingClientRect()
} else {
// 获取 scroll-view 的位置和滚动距离
selector.selectViewport().scrollOffset() // 获取窗口的滚动距离
}
selector.exec(res => {
if (!res[0]) {
reject(Error('Label not found'))
return
}
const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
if (this._in) {
// scroll-view 跳转
this._in.page[this._in.scrollTop] = scrollTop
} else {
// 页面跳转
uni.pageScrollTo({
scrollTop,
duration: 300
})
}
resolve()
})
// #endif
})
},
/**
* @description 获取文本内容
* @return {String}
*/
getText (nodes) {
let text = '';
(function traversal (nodes) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
if (node.type === 'text') {
text += node.text.replace(/&amp;/g, '&')
} else if (node.name === 'br') {
text += '\n'
} else {
// 块级标签前后加换行
const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
if (isBlock && text && text[text.length - 1] !== '\n') {
text += '\n'
}
// 递归获取子节点的文本
if (node.children) {
traversal(node.children)
}
if (isBlock && text[text.length - 1] !== '\n') {
text += '\n'
} else if (node.name === 'td' || node.name === 'th') {
text += '\t'
}
}
}
})(nodes || this.nodes)
return text
},
/**
* @description 获取内容大小和位置
* @return {Promise}
*/
getRect () {
return new Promise((resolve, reject) => {
uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found')))
})
},
/**
* @description 暂停播放媒体
*/
pauseMedia () {
for (let i = (this._videos || []).length; i--;) {
this._videos[i].pause()
}
// #ifdef APP-PLUS
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
// #ifndef APP-PLUS-NVUE
let page = this.$parent
while (!page.$scope) page = page.$parent
page.$scope.$getAppWebview().evalJS(command)
// #endif
// #ifdef APP-PLUS-NVUE
this.$refs.web.evalJs(command)
// #endif
// #endif
},
/**
* @description 设置媒体播放速率
* @param {Number} rate 播放速率
*/
setPlaybackRate (rate) {
this.playbackRate = rate
for (let i = (this._videos || []).length; i--;) {
this._videos[i].playbackRate(rate)
}
// #ifdef APP-PLUS
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
// #ifndef APP-PLUS-NVUE
let page = this.$parent
while (!page.$scope) page = page.$parent
page.$scope.$getAppWebview().evalJS(command)
// #endif
// #ifdef APP-PLUS-NVUE
this.$refs.web.evalJs(command)
// #endif
// #endif
},
/**
* @description 设置内容
* @param {String} content html 内容
* @param {Boolean} append 是否在尾部追加
*/
setContent (content, append) {
if (!append || !this.imgList) {
this.imgList = []
}
const nodes = new Parser(this).parse(content)
// #ifdef APP-PLUS-NVUE
if (this._ready) {
this._set(nodes, append)
}
// #endif
this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
// #ifndef APP-PLUS-NVUE
this._videos = []
this.$nextTick(() => {
this._hook('onLoad')
this.$emit('load')
})
if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
// 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
let height = 0
const callback = rect => {
if (!rect || !rect.height) rect = {}
// 350ms 总高度无变化就触发 ready 事件
if (rect.height === height) {
this.$emit('ready', rect)
} else {
height = rect.height
setTimeout(() => {
this.getRect().then(callback).catch(callback)
}, 350)
}
}
this.getRect().then(callback).catch(callback)
} else {
// 未设置懒加载,等待所有图片加载完毕
if (!this.imgList._unloadimgs) {
this.getRect().then(rect => {
this.$emit('ready', rect)
}).catch(() => {
this.$emit('ready', {})
})
}
}
// #endif
},
/**
* @description 调用插件钩子函数
*/
_hook (name) {
for (let i = plugins.length; i--;) {
if (this.plugins[i][name]) {
this.plugins[i][name]()
}
}
},
// #ifdef APP-PLUS-NVUE
/**
* @description 设置内容
*/
_set (nodes, append) {
this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes).replace(/%22/g, '') + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
},
/**
* @description 接收到 web-view 消息
*/
_onMessage (e) {
const message = e.detail.data[0]
switch (message.action) {
// web-view 初始化完毕
case 'onJSBridgeReady':
this._ready = true
if (this.nodes) {
this._set(this.nodes)
}
break
// 内容 dom 加载完毕
case 'onLoad':
this.height = message.height
this._hook('onLoad')
this.$emit('load')
break
// 所有图片加载完毕
case 'onReady':
this.getRect().then(res => {
this.$emit('ready', res)
}).catch(() => {
this.$emit('ready', {})
})
break
// 总高度发生变化
case 'onHeightChange':
this.height = message.height
break
// 图片点击
case 'onImgTap':
this.$emit('imgtap', message.attrs)
if (this.previewImg) {
uni.previewImage({
current: parseInt(message.attrs.i),
urls: this.imgList
})
}
break
// 链接点击
case 'onLinkTap': {
const href = message.attrs.href
this.$emit('linktap', message.attrs)
if (href) {
// 锚点跳转
if (href[0] === '#') {
if (this.useAnchor) {
dom.scrollToElement(this.$refs.web, {
offset: message.offset
})
}
} else if (href.includes('://')) {
// 打开外链
if (this.copyLink) {
plus.runtime.openWeb(href)
}
} else {
uni.navigateTo({
url: href,
fail () {
uni.switchTab({
url: href
})
}
})
}
}
break
}
case 'onPlay':
this.$emit('play')
break
// 获取到锚点的偏移量
case 'getOffset':
if (typeof message.offset === 'number') {
dom.scrollToElement(this.$refs.web, {
offset: message.offset + this._navigateTo.offset
})
this._navigateTo.resolve()
} else {
this._navigateTo.reject(Error('Label not found'))
}
break
// 点击
case 'onClick':
this.$emit('tap')
this.$emit('click')
break
// 出错
case 'onError':
this.$emit('error', {
source: message.source,
attrs: message.attrs
})
}
}
// #endif
}
}
</script>
<style>
/* #ifndef APP-PLUS-NVUE */
/* 根节点样式 */
._root {
padding: 1px 0;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
}
/* 长按复制 */
._select {
user-select: text;
}
/* #endif */
</style>

View File

@@ -0,0 +1,597 @@
<template>
<view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
<block v-for="(n, i) in childs" v-bind:key="i">
<!-- 图片 -->
<!-- 占位图 -->
<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" />
<!-- 显示图片 -->
<!-- #ifdef H5 || (APP-PLUS && VUE2) -->
<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" />
<!-- #endif -->
<!-- #ifndef H5 || (APP-PLUS && VUE2) -->
<!-- 表格中的图片使用 rich-text 防止大小不正确 -->
<rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="[{attrs:{style:n.attrs.style||'',src:n.attrs.src},name:'img'}]" :data-i="i" @tap.stop="imgTap" />
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<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]+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':(n.m||'scaleToFill'))" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- #ifndef H5 || APP-PLUS || MP-KUAISHOU -->
<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':(n.m||'scaleToFill'))" :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" />
<!-- #endif -->
<!-- #ifdef MP-KUAISHOU -->
<image v-else-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" :lazy-load="opts[0]" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap"></image>
<!-- #endif -->
<!-- #ifdef APP-PLUS && VUE3 -->
<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':(n.m||''))" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- 文本 -->
<!-- #ifdef MP-WEIXIN -->
<text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
<text v-else-if="n.text" decode>{{n.text}}</text>
<!-- #endif -->
<text v-else-if="n.name==='br'">\n</text>
<!-- 链接 -->
<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">
<node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
</view>
<!-- 视频 -->
<!-- #ifdef APP-PLUS -->
<view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" :data-i="i" @vplay.stop="play" />
<!-- #endif -->
<!-- #ifndef APP-PLUS -->
<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" />
<!-- #endif -->
<!-- #ifdef H5 || APP-PLUS -->
<iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
<embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
<!-- #endif -->
<!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
<!-- 音频 -->
<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" />
<!-- #endif -->
<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">
<node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
<view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
<node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
<block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
<view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
<node :childs="tr.children" :opts="opts" />
</view>
<view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
<view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
<node :childs="td.children" :opts="opts" />
</view>
</view>
</block>
</view>
</view>
<!-- 富文本 -->
<!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
<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]" />
<!-- #endif -->
<!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
<rich-text v-else-if="!n.c" :id="n.attrs.id" :style="'display:inline;'+n.f" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
<!-- #endif -->
<!-- 继续递归 -->
<view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
<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" />
</view>
<node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
</block>
</view>
</template>
<script module="handler" lang="wxs">
// 行内标签列表
var inlineTags = {
abbr: true,
b: true,
big: true,
code: true,
del: true,
em: true,
i: true,
ins: true,
label: true,
q: true,
small: true,
span: true,
strong: true,
sub: true,
sup: true
}
/**
* @description 判断是否为行内标签
*/
module.exports = {
isInline: function (tagName, style) {
return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
}
}
</script>
<script>
import node from './node'
export default {
name: 'node',
options: {
// #ifdef MP-WEIXIN
virtualHost: true,
// #endif
// #ifdef MP-TOUTIAO
addGlobalClass: false
// #endif
},
data () {
return {
ctrl: {},
// #ifdef MP-WEIXIN
isiOS: uni.getSystemInfoSync().system.includes('iOS')
// #endif
}
},
props: {
name: String,
attrs: {
type: Object,
default () {
return {}
}
},
childs: Array,
opts: Array
},
components: {
// #ifndef ((H5 || APP-PLUS) && VUE3) || APP-HARMONY
node
// #endif
},
mounted () {
this.$nextTick(() => {
for (this.root = this.$parent; this.root.$options.name !== 'mp-html'; this.root = this.root.$parent);
})
// #ifdef H5 || APP-PLUS
if (this.opts[0]) {
let i
for (i = this.childs.length; i--;) {
if (this.childs[i].name === 'img') break
}
if (i !== -1) {
this.observer = uni.createIntersectionObserver(this).relativeToViewport({
top: 500,
bottom: 500
})
this.observer.observe('._img', res => {
if (res.intersectionRatio) {
this.$set(this.ctrl, 'load', 1)
this.observer.disconnect()
}
})
}
}
// #endif
},
beforeDestroy () {
// #ifdef H5 || APP-PLUS
if (this.observer) {
this.observer.disconnect()
}
// #endif
},
methods:{
// #ifdef MP-WEIXIN
toJSON () { return this },
// #endif
/**
* @description 播放视频事件
* @param {Event} e
*/
play (e) {
const i = e.currentTarget.dataset.i
const node = this.childs[i]
this.root.$emit('play', {
source: node.name,
attrs: {
...node.attrs,
src: node.src[this.ctrl[i] || 0]
}
})
// #ifndef APP-PLUS
if (this.root.pauseVideo) {
let flag = false
const id = e.target.id
for (let i = this.root._videos.length; i--;) {
if (this.root._videos[i].id === id) {
flag = true
} else {
this.root._videos[i].pause() // 自动暂停其他视频
}
}
// 将自己加入列表
if (!flag) {
const ctx = uni.createVideoContext(id
// #ifndef MP-BAIDU
, this
// #endif
)
ctx.id = id
if (this.root.playbackRate) {
ctx.playbackRate(this.root.playbackRate)
}
this.root._videos.push(ctx)
}
}
// #endif
},
/**
* @description 图片点击事件
* @param {Event} e
*/
imgTap (e) {
const node = this.childs[e.currentTarget.dataset.i]
if (node.a) {
this.linkTap(node.a)
return
}
if (node.attrs.ignore) return
// #ifdef H5 || APP-PLUS
node.attrs.src = node.attrs.src || node.attrs['data-src']
// #endif
// #ifndef APP-HARMONY
this.root.$emit('imgtap', node.attrs)
// #endif
// #ifdef APP-HARMONY
this.root.$emit('imgtap', {
...node.attrs
})
// #endif
// 自动预览图片
if (this.root.previewImg) {
uni.previewImage({
// #ifdef MP-WEIXIN
showmenu: this.root.showImgMenu,
// #endif
// #ifdef MP-ALIPAY
enablesavephoto: this.root.showImgMenu,
enableShowPhotoDownload: this.root.showImgMenu,
// #endif
current: parseInt(node.attrs.i),
urls: this.root.imgList
})
}
},
/**
* @description 图片长按
*/
imgLongTap (e) {
// #ifdef APP-PLUS
const attrs = this.childs[e.currentTarget.dataset.i].attrs
if (this.opts[3] && !attrs.ignore) {
uni.showActionSheet({
itemList: ['保存图片'],
success: () => {
const save = path => {
uni.saveImageToPhotosAlbum({
filePath: path,
success () {
uni.showToast({
title: '保存成功'
})
}
})
}
if (this.root.imgList[attrs.i].startsWith('http')) {
uni.downloadFile({
url: this.root.imgList[attrs.i],
success: res => save(res.tempFilePath)
})
} else {
save(this.root.imgList[attrs.i])
}
}
})
}
// #endif
},
/**
* @description 图片加载完成事件
* @param {Event} e
*/
imgLoad (e) {
const i = e.currentTarget.dataset.i
/* #ifndef H5 || (APP-PLUS && VUE2) */
if (!this.childs[i].w) {
// 设置原宽度
this.$set(this.ctrl, i, e.detail.width)
} else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
// 加载完毕,取消加载中占位图
this.$set(this.ctrl, i, 1)
}
this.checkReady()
},
/**
* @description 检查是否所有图片加载完毕
*/
checkReady () {
if (this.root && !this.root.lazyLoad) {
this.root._unloadimgs -= 1
if (!this.root._unloadimgs) {
setTimeout(() => {
this.root.getRect().then(rect => {
this.root.$emit('ready', rect)
}).catch(() => {
this.root.$emit('ready', {})
})
}, 350)
}
}
},
/**
* @description 链接点击事件
* @param {Event} e
*/
linkTap (e) {
const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
const attrs = node.attrs || e
const href = attrs.href
this.root.$emit('linktap', Object.assign({
innerText: this.root.getText(node.children || []) // 链接内的文本内容
}, attrs))
if (href) {
if (href[0] === '#') {
// 跳转锚点
this.root.navigateTo(href.substring(1)).catch(() => { })
} else if (href.split('?')[0].includes('://')) {
// 复制外部链接
if (this.root.copyLink) {
// #ifdef H5
window.open(href)
// #endif
// #ifdef MP
uni.setClipboardData({
data: href,
success: () =>
uni.showToast({
title: '链接已复制'
})
})
// #endif
// #ifdef APP-PLUS
plus.runtime.openWeb(href)
// #endif
}
} else {
// 跳转页面
uni.navigateTo({
url: href,
fail () {
uni.switchTab({
url: href,
fail () { }
})
}
})
}
}
},
/**
* @description 错误事件
* @param {Event} e
*/
mediaError (e) {
const i = e.currentTarget.dataset.i
const node = this.childs[i]
// 加载其他源
if (node.name === 'video' || node.name === 'audio') {
let index = (this.ctrl[i] || 0) + 1
if (index > node.src.length) {
index = 0
}
if (index < node.src.length) {
this.$set(this.ctrl, i, index)
return
}
} else if (node.name === 'img') {
// #ifdef H5 && VUE3
if (this.opts[0] && !this.ctrl.load) return
// #endif
// 显示错误占位图
if (this.opts[2]) {
this.$set(this.ctrl, i, -1)
}
this.checkReady()
}
if (this.root) {
this.root.$emit('error', {
source: node.name,
attrs: node.attrs,
// #ifndef H5 && VUE3
errMsg: e.detail.errMsg
// #endif
})
}
}
}
}
</script>
<style>
/* a 标签默认效果 */
._a {
padding: 1.5px 0 1.5px 0;
color: #366092;
word-break: break-all;
}
/* a 标签点击态效果 */
._hover {
text-decoration: underline;
opacity: 0.7;
}
/* 图片默认效果 */
._img {
max-width: 100%;
-webkit-touch-callout: none;
}
/* 内部样式 */
._block {
display: block;
}
._b,
._strong {
font-weight: bold;
}
._code {
font-family: monospace;
}
._del {
text-decoration: line-through;
}
._em,
._i {
font-style: italic;
}
._h1 {
font-size: 2em;
}
._h2 {
font-size: 1.5em;
}
._h3 {
font-size: 1.17em;
}
._h5 {
font-size: 0.83em;
}
._h6 {
font-size: 0.67em;
}
._h1,
._h2,
._h3,
._h4,
._h5,
._h6 {
display: block;
font-weight: bold;
}
._image {
height: 1px;
}
._ins {
text-decoration: underline;
}
._li {
display: list-item;
}
._ol {
list-style-type: decimal;
}
._ol,
._ul {
display: block;
padding-left: 40px;
margin: 1em 0;
}
._q::before {
content: '"';
}
._q::after {
content: '"';
}
._sub {
font-size: smaller;
vertical-align: sub;
}
._sup {
font-size: smaller;
vertical-align: super;
}
._thead,
._tbody,
._tfoot {
display: table-row-group;
}
._tr {
display: table-row;
}
._td,
._th {
display: table-cell;
vertical-align: middle;
}
._th {
font-weight: bold;
text-align: center;
}
._ul {
list-style-type: disc;
}
._ul ._ul {
margin: 0;
list-style-type: circle;
}
._ul ._ul ._ul {
list-style-type: square;
}
._abbr,
._b,
._code,
._del,
._em,
._i,
._ins,
._label,
._q,
._span,
._strong,
._sub,
._sup {
display: inline;
}
/* #ifdef APP-PLUS */
._video {
width: 300px;
height: 225px;
}
/* #endif */
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
"use strict";function t(t){for(var e=Object.create(null),n=t.attributes.length;n--;)e[t.attributes[n].name]=t.attributes[n].value;return e}function e(){a[1]&&(this.src=a[1],this.onerror=null),this.onclick=null,this.ontouchstart=null,uni.postMessage({data:{action:"onError",source:"img",attrs:t(this)}})}function n(){window.unloadimgs-=1,0===window.unloadimgs&&uni.postMessage({data:{action:"onReady"}})}function o(r,s,c){for(var d=0;d<r.length;d++)!function(d){var u=r[d],l=void 0;if(u.type&&"node"!==u.type)l=document.createTextNode(u.text.replace(/&amp;/g,"&"));else{var g=u.name;"svg"===g&&(c="http://www.w3.org/2000/svg"),"html"!==g&&"body"!==g||(g="div"),l=c?document.createElementNS(c,g):document.createElement(g);for(var p in u.attrs)l.setAttribute(p,u.attrs[p]);if(u.children&&o(u.children,l,c),"img"===g){if(window.unloadimgs+=1,l.onload=n,l.onerror=n,!l.src&&l.getAttribute("data-src")&&(l.src=l.getAttribute("data-src")),u.attrs.ignore||(l.onclick=function(e){e.stopPropagation(),uni.postMessage({data:{action:"onImgTap",attrs:t(this)}})}),a[2]){var h=new Image;h.src=l.src,l.src=a[2],h.onload=function(){l.src=this.src},h.onerror=function(){l.onerror()}}l.onerror=e}else if("a"===g)l.addEventListener("click",function(e){e.stopPropagation(),e.preventDefault();var n,o=this.getAttribute("href");o&&"#"===o[0]&&(n=(document.getElementById(o.substr(1))||{}).offsetTop),uni.postMessage({data:{action:"onLinkTap",attrs:t(this),offset:n}})},!0);else if("video"===g||"audio"===g)i.push(l),u.attrs.autoplay||u.attrs.controls||l.setAttribute("controls","true"),l.onplay=function(){if(uni.postMessage({data:{action:"onPlay"}}),a[3])for(var t=0;t<i.length;t++)i[t]!==this&&i[t].pause()},l.onerror=function(){uni.postMessage({data:{action:"onError",source:g,attrs:t(this)}})};else if("table"===g&&a[4]&&!l.style.cssText.includes("inline")){var f=document.createElement("div");f.style.overflow="auto",f.appendChild(l),l=f}else"svg"===g&&(c=void 0)}s.appendChild(l)}(d)}document.addEventListener("UniAppJSBridgeReady",function(){document.body.onclick=function(){return uni.postMessage({data:{action:"onClick"}})},uni.postMessage({data:{action:"onJSBridgeReady"}})});var a,i=[];window.setContent=function(t,e,n){var r=document.getElementById("content");e[0]&&(document.body.style.cssText=e[0]),e[5]||(r.style.userSelect="none"),n||(r.innerHTML="",i=[]),a=e,window.unloadimgs=0;var s=document.createDocumentFragment();o(t,s),r.appendChild(s);var c=r.scrollHeight;uni.postMessage({data:{action:"onLoad",height:c}}),window.unloadimgs||uni.postMessage({data:{action:"onReady",height:c}}),clearInterval(window.timer),window.timer=setInterval(function(){r.scrollHeight!==c&&(c=r.scrollHeight,uni.postMessage({data:{action:"onHeightChange",height:c}}))},350)},window.onunload=function(){clearInterval(window.timer)};

View File

@@ -0,0 +1 @@
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function t(e,t){return n.call(e,t)}var i=[],a=function(e,n){var t={options:{timestamp:+new Date},name:e,arg:n};if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){if("postMessage"===e){var a={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(a):window.__dcloud_weex_.postMessage(JSON.stringify(a))}var o={type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(o):window.__dcloud_weex_.postMessageToService(JSON.stringify(o))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:t,pageId:""},"*");if(0===i.length){var r=plus.webview.currentWebview();if(!r)throw new Error("plus.webview.currentWebview() is undefined");var d=r.parent(),s="";s=d?d.id:r.id,i.push(s)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}},"__uniapp__service");else{var w=JSON.stringify(t);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(w,",").concat(JSON.stringify(i),");"))}},o={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;a("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("redirectTo",{url:encodeURI(n)})},getEnv:function(e){window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a("postMessage",e.data||{})}},r=/uni-app/i.test(navigator.userAgent),d=/Html5Plus/i.test(navigator.userAgent),s=/complete|loaded|interactive/;var w=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var u=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var g=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var v=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);for(var l,_=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},f=[function(e){if(r||d)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&s.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),o},function(e){if(v)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(w){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(u)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){return document.addEventListener("DOMContentLoaded",e),o}],m=0;m<f.length&&!(l=f[m](_));m++);l||(l={});var E="undefined"!=typeof uni?uni:{};if(!E.navigateTo)for(var b in l)t(l,b)&&(E[b]=l[b]);return E.webView=l,E}));

View File

@@ -0,0 +1 @@
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>body,html{width:100%;height:100%;overflow-x:scroll;overflow-y:hidden}body{margin:0}video{width:300px;height:225px}img{max-width:100%;-webkit-touch-callout:none}</style></head><body><div id="content" style="overflow:hidden"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>

View File

View File

@@ -0,0 +1,56 @@
# mp-html
> 一个强大的小程序富文本组件
![star](https://img.shields.io/github/stars/jin-yufeng/mp-html)
![forks](https://img.shields.io/github/forks/jin-yufeng/mp-html)
[![npm](https://img.shields.io/npm/v/mp-html)](https://www.npmjs.com/package/mp-html)
![downloads](https://img.shields.io/npm/dt/mp-html)
[![Coverage Status](https://coveralls.io/repos/github/jin-yufeng/mp-html/badge.svg?branch=master)](https://coveralls.io/github/jin-yufeng/mp-html?branch=master)
![license](https://img.shields.io/github/license/jin-yufeng/mp-html)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
## 📢 概况 :id=overview
显示动态 *html* 富文本是很多应用必要的需求,小程序平台不支持 *dom* 操作使得这成为一个难题,其自带的 *rich-text* 组件支持的标签少且屏蔽所有事件,难以实际应用。因此就有了这样一个能够便捷的在小程序平台上处理富文本的组件,还支持丰富的扩展功能。
## 📋 特性 :id=feature
- 支持在多个主流的小程序平台和 *uni-app* 中使用
- 支持丰富的标签(包括 *table*、*video*、*svg* 等)
- 支持丰富的事件效果(自动预览图片、链接处理等)
- 支持设置占位图(加载中、出错时、预览时)
- 支持锚点跳转、长按复制等丰富功能
- 支持大部分 *html* 实体
- 丰富的插件(关键词搜索、内容编辑、*latex* 公式 等)
- 效率高、容错性强且轻量化(*≈25KB**9KB gzipped*
查看 [功能介绍](overview/feature) 了解更多
## 🎉 使用案例 :id=case
| [官方示例](https://github.com/jin-yufeng/mp-html-demo) | 欢喜商城 | 多么生活 | 食法查 | 微慕 | 古典文学名著阅读 |
|:---:|:---:|:---:|:---:|:---:|:---:|
| ![富文本插件](assets/case/富文本插件.jpg) | ![欢喜商城](assets/case/欢喜商城.png) | ![多么生活](assets/case/多么生活.jpg) | ![食法查](assets/case/食法查.png) | ![微慕](assets/case/微慕.jpg) | ![古典文学名著阅读](assets/case/古典文学名著阅读.jpg) |
| 科学复习 | [程序员技术之旅](https://github.com/fendoudebb/z-blog-wx) | 典典博客 | 优秀笔记 | 365 刷题 | 同城共享书 |
|:---:|:---:|:---:|:---:|:---:|:---:|
| ![科学复习](assets/case/科学复习.png) | ![程序员技术之旅](assets/case/程序员技术之旅.jpg) | ![典典博客](assets/case/典典博客.jpg) | ![优秀笔记](assets/case/优秀笔记.jpg) | ![365刷题](assets/case/365刷题.jpg) | ![同城共享书](assets/case/同城共享书.jpg) |
| [技术源 share](https://github.com/wangsrGit119/mini-blog-halo) | 你的代码写的真棒 | 谛否 | 小莫唐尼 | [模版演示](https://github.com/zhihuifanqiechaodan/miniprogram-template) | AI瓦力 |
|:---:|:---:|:---:|:---:|:---:|:---:|
| ![技术源share](assets/case/技术源share.jpg) | ![你的代码写的真棒](assets/case/你的代码写的真棒.jpg) | ![谛否](assets/case/谛否.jpg) | ![小莫唐尼](assets/case/小莫唐尼.png) | ![MiniProgram模版演示](assets/case/MiniProgram模版演示.jpg) | ![AI瓦力](assets/case/AI瓦力.jpg) |
以上排名不分先后,更多可见 [使用案例收集](https://github.com/jin-yufeng/mp-html/issues/27)(欢迎添加)
## 🎈 联系与支持 :id=sponsor
![group](assets/group.jpg)
![支持](assets/sponsor.png)
## 📃 许可 :id=license
[MIT License](https://github.com/jin-yufeng/mp-html/blob/master/LICENSE)
?> 您可以免费的使用(包括商用)、复制或修改本组件
!> 在用于生产环境前,务必进行仔细测试,由本组件 *bug* 带来的损失概不负责
---
Powered by docsify

View File

@@ -0,0 +1,12 @@
![logo](assets/logo/logo.png)
# mp-html <small>2.5.1</small>
> 一个强大的小程序富文本组件
- 全面的标签支持
- 多平台使用支持
- 丰富的附加功能
[GitHub](https://github.com/jin-yufeng/mp-html)
[快速开始](/overview/quickstart)

View File

@@ -0,0 +1,20 @@
- 概览
- [🚀 快速开始](overview/quickstart)
- [🎉 功能介绍](overview/feature)
- 基本使用
- [🔨 属性](basic/prop)
- [📫 事件](basic/event)
- 进阶使用
- [🔎 api](advanced/api)
- [📌 插件](advanced/plugin)
- [🔧 二次开发](advanced/develop)
- 疑问解答
- [📘 常见问题](question/faq)
- [📩 反馈](question/feedback)
- [📝 贡献指南](question/contribution)
- 更新日志
- [📖 更新日志](changelog/changelog)

View File

@@ -0,0 +1,230 @@
# 🔎 api
组件的实例上挂载了一些实用的 *api* 方法可供调用
## 获取组件实例 :id=getCompent
- *uni-app*
```vue
<template>
<view>
<mp-html ref="article" />
</view>
</template>
<script>
export default {
onLoad () {
var ctx = this.$refs.article
}
}
</script>
```
- 支付宝小程序
需开启 [component2](https://opendocs.alipay.com/mini/framework/component-ref) 模式
```axml
<mp-html ref="article">
```
```javascript
Page({
article (ctx) {
// 获得组件实例
}
})
```
- 其他小程序平台
```wxml
<mp-html id="article" />
```
```javascript
Page({
onLoad () {
// 微信、QQ、百度
var ctx = this.selectComponent('#article')
// 头条
this.selectComponent('#article', ctx => {
})
}
})
```
## in
功能:将锚点跳转的范围限定在一个 *scroll-view*(需要开启纵向滚动)内
输入值:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|:---:|:---:|:---:|:---:|---|
| page | object | 是 | - | scroll-view 标签所在页面实例 |
| selector | string | 是 | - | scroll-view 标签 的选择器 |
| scrollTop | string | 是 | - | scroll-view 标签 scrollTop 属性绑定的变量名 |
返回值:无
示例:
```wxml
<scroll-view id="scroll" style="height:300px" scroll-top="{{top}}" scroll-y scroll-with-animation>
<mp-html id="article" content="{{html}}" />
</scroll-view>
```
```javascript
Page({
onLoad () {
// ctx 为组件实例
ctx.in(this, '#scroll', 'top')
}
})
```
!> 在 *scroll-view* 中使用时需要注意如果使用了视频,需要保证该平台的 *video* 标签支持同层渲染
## navigateTo
功能:锚点跳转
前提是 [use-anchor](basic/prop#use-anchor) 属性的值为 *true*
必须在 [load](basic/event#load) 事件触发后使用,建议在 [ready](basic/event#ready) 事件触发后使用以保证跳转位置准确
输入值:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|:---:|:---:|:---:|:---:|---|
| id | string | 否 | - | 要跳转的锚点 id为空则跳转到开头 |
| offset | number | 否 | 0 | 跳转位置的偏移量 |
返回值:**Promise**
该方法中传入的 *offset* 优先级高于 [use-anchor](basic/prop#use-anchor) 属性
示例:
```javascript
Page({
ready () {
// ctx 为组件实例
ctx.navigateTo('anchor').then(() => {
console.log('跳转成功')
}).catch(err => {
console.log('跳转失败:', err)
})
}
})
```
## getText
功能:获取文本内容
必须在 [load](basic/event#load) 事件触发后使用
输入值:无
返回值:**String**
## getRect
功能:获取富文本内容的位置和大小
如果开启了 [lazy-load](basic/prop#lazy-load)[ready](basic/event#ready) 事件返回的不是最终大小,可通过此方法获得实时的大小和位置信息
输入值:无
返回值:**Promise**
示例:
```javascript
Page({
getRect () {
// ctx 为组件实例
ctx.getRect().then(rect => {
console.log(rect) // boundingClientRect 信息
}).catch(err => {
console.log('获取失败', err)
})
}
})
```
!> 该方法有小概率可能获取失败,需要做好错误处理
## setContent
功能:设置富文本内容
此方法的功能与 [content](basic/prop#content) 属性基本一致,但此方法的设置不需要经过视图层且可以从尾部追加
输入值:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|:---:|:---:|:---:|:---:|---|
| content | string | 是 | - | 要渲染的 html 字符串 |
| append | boolean | 否 | false | 是否从尾部追加 |
返回值:无
!> 调用此方法会触发 [load](basic/event#load) 和 [ready](basic/event#ready) 事件,请勿在事件处理函数中调用,否则可能陷入死循环
## imgList
功能:获取所有图片的数组
该数组用于图片预览,对其进行修改可以在自动预览时生效(如修改为高清图链接或转存 *base64*
!> 这是一个属性,不是一个函数
请不要增删此数组(可以修改),否则在自动预览时可能出现问题
```javascript
Page({
load () {
// ctx 为组件实例
var cover = ctx.imgList[0] // 首张图可以作为转发封面图
ctx.imgList.forEach((src, i, array) => {
console.log(src)
// 替换为高清图链接
array[i] = src.replace('thumb', '')
// 转存 base64 便于预览
var fs = wx.getFileSystemManager && wx.getFileSystemManager()
var info = src.match(/data:image\/(\S+?);(\S+?),(.+)/)
if (!info) return
var filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`
fs && fs.writeFile({
filePath,
data: info[3],
encoding: info[2],
success: () => array[i] = filePath
})
})
}
})
```
## pauseMedia
?> [2.2.2](changelog/changelog#v222) 版本起支持
功能:暂停正在播放的视频或音频
?> 和 [play](basic/event#play) 事件配合可以实现与页面中其他音视频进行互斥播放
输入值:无
返回值:无
示例:
```javascript
Page({
onHide () {
// ctx 为组件实例
ctx.pauseMedia() // 页面跳转或隐藏时暂停播放
}
})
```
## setPlaybackRate
?> [2.4.0](changelog/changelog#v240) 版本起支持
功能:设置音视频的播放速率
输入值:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|:---:|:---:|:---:|:---:|---|
| rate | number | 是 | - | 播放速率,一般支持 0.5~2.0 |
返回值:无
示例:
```javascript
Page({
// 点击设置速率按钮
setPlaybackRate () {
wx.showActionSheet({
itemList: ['0.5', '1.0', '1.25', '1.5', '2.0'],
success: res => {
const rate = [0.5, 1.0, 1.25, 1.5, 2.0][res.tapIndex]
// ctx 为组件实例
ctx.setPlaybackRate(rate)
}
})
}
})
```

View File

@@ -0,0 +1,114 @@
# 二次开发 :id=develop
## 📣 说明 :id=notice
二次开发请在 *src* 目录下进行修改,修改完成后可通过下述方法自动生成各平台的代码包
为方便维护,本项目原生包多个平台共用一套源代码,在编写时直接按照微信端的写法进行编写即可,[生成代码包](#pack) 时会自动进行转换
自动转换已经抹平了大部分平台之间的差异(文件后缀名、*api* 格式等),需要注意的是 **访问组件的属性** 时,请通过 *this.properties* 访问而不是 *this.data*,因为在支付宝平台中两者不互通
个别问题可以自行修改 *tools/converter.js* 进行处理
附项目结构:
```
├─dev生成的各平台示例项目
├─dist生成的各平台代码包
│ ├─mp-alipay
│ ├─mp-baidu
│ ├─mp-qq
│ ├─mp-toutiao
│ ├─mp-weixin
│ └─uni-app
├─docs文档由 docsify 生成)
├─plugins插件源代码
├─src组件源代码
│ ├─miniprogram原生包源代码
│ └─uni-appuni-app 包源代码)
├─test测试代码
├─tools构建工具
│ ├─demo示例项目源代码
│ │ ├─miniprogram原生平台示例项目
│ │ └─uni-appuni-app 平台示例项目)
| ├─config.js构建工具的配置项
| ├─converter.js将微信端的代码转换到各个平台
| ├─ifdef.js处理条件编译
| ├─minifier.js处理 json 和 wxs 的压缩)
| └─plugin.js处理插件构建
├─.eslintrc.jsoneslint 配置)
├─.stylelintrc.jsonstylelint 配置)
├─gulpfile.jsgulp 生成文件)
├─LICENSE许可证 MIT
└─package.json项目配置
```
?> 对于较复杂的修改,如果能通过 [编写插件](advanced/plugin#develop) 方式实现更推荐插件方式,这样在组件包升级的时候便于维护和管理
## 🎈 条件编译 :id=ifdef
不同平台之间一些差异的地方可能无法简单的通过替换解决,因此本项目中引入了一种条件编译机制解决平台差异,可在修改时加以利用(条件编译是指在生成包的过程中就仅保留本平台需要的代码,与运行过程中的 *if* 判断不同)
方式 *1*(适用于 *js*、*wxml*、*wxss* 文件)
仅在某平台下需要使用的代码放在两个注释(各种注释格式皆可)之间即可,示例:
```javascript
// #ifdef MP-WEIXIN
console.log('这是微信平台')
// #endif
// #ifndef MP-WEIXIN
console.log('这不是微信平台')
// #endif
```
方式 *2*(适用于 *wxml* 文件)
对于仅在某一平台使用的属性,可在属性名前加 *平台名:* ,示例:
```wxml
<!-- show-menu-by-longpress 属性将仅被生成到微信包中 -->
<image mp-weixin:show-menu-by-longpress="xxx" />
```
说明:
1. 可用的平台名称:*mp-weixin*, *mp-qq*, *mp-baidu*, *mp-alipay*, *mp-toutiao*(不区分大小写)
2. *#if(n)def**#endif* 必须成对出现,否则会报错(可以多层嵌套)
3. 如果编译过程中发现问题可以自行修改 *tools/ifdef.js* 进行处理
## 📦 生成组件包 :id=pack
修改完成后,可按以下步骤生成新的组件包
*mp-html* 文件夹下执行:
?> 以下命令需要在组件包根目录下执行,即包含 [package.json](https://github.com/jin-yufeng/mp-html/blob/master/package.json) 的目录(如果通过 *npm* 获取就是 *node_modules/mp-html*
1. 安装依赖
```bash
# 通过 npm 安装
npm install
# 或通过 yarn 安装
yarn
```
2. 生成代码包到 *dist* 文件夹
```bash
# 生成微信包到 dist/mp-weixin
npm run build:weixin
# 生成 qq 包到 dist/mp-qq
npm run build:qq
# 生成百度包到 dist/mp-baidu
npm run build:baidu
# 生成支付宝包到 dist/mp-alipay
npm run build:alipay
# 生成头条包到 dist/mp-toutiao
npm run build:toutiao
# 生成 uni-app 包到 dist/uni-app
npm run build:uni-app
# 生成所有包
npm run build
```
?> 如需修改打包过程中的配置(*babel*, *uglifyJs* 等),可以对 *tools/config.js* 进行修改
## 🔦 检查和测试 :id=test
假设已安装好依赖
```bash
npm run lint # eslint 检查
npm run lintcss # stylelint 检查
npm run lintcss --fix # 检查并修复
npm run test # 执行 jest 测试
npm run coverage # 测试代码覆盖率
```
可以向 *test* 目录下添加新的测试用例进行测试

View File

@@ -0,0 +1,488 @@
# 📌 插件 :id=plugin
> 可以在这里选择需要的插件以实现更加丰富的功能
## 使用插件 :id=use
!> 直接将插件文件夹拷贝到组件包中无法生效,请通过以下方式生成包含扩展的组件包
#### 小程序方式
!> 该方式暂不可用
?> 该方式适合不熟悉 *npm* 的用户
1. 通过 [小程序方式](overview/quickstart#mp) 获取包含扩展插件的组件包
2. 将下载的组件包解压,原生小程序复制到 *components* 目录下,*uni-app* 复制到项目根目录下,按照源码方式引入即可,详见 [引入方式](overview/quickstart#use)
#### npm 方式
1. 获取完整的组件包
通过 [npm](overview/quickstart#npm) 或 [git](overview/quickstart#git) 等方式获取 **包含完整项目** 的组件包(注意从 *uni-app* 的插件市场中导入的包中仅包含构建后的组件,**不包含** 构建工具和插件)
![step1](../assets/tutorials/01.png)
2. 选择需要的插件
参考下方插件使用说明,确定要使用的插件,将其名称填入 [tools/config.js](https://github.com/jin-yufeng/mp-html/blob/master/tools/config.js#L8) 中的 *plugins*
如果想仅在部分平台使用该插件,可以在该插件目录下的 *build.js**platform* 字段中填入需要的平台名称
![step2](../assets/tutorials/02.png)
3. 生成组件包
设置完成后,可通过项目提供的命令行工具生成新的组件包,具体见 [生成组件包](advanced/develop#pack)
![step3_1](../assets/tutorials/03.png)
![step3_2](../assets/tutorials/04.png)
4. 按照源码或 *npm* 方式引入构建后的组件包进行使用即可,详见 [引入方式](overview/quickstart#use)
## audio
功能:音乐播放器
大小:*≈4KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √(nvue 不支持) |
!> 百度小程序原生包在此 [问题](https://smartprogram.baidu.com/forum/topic/show/125787) 未解决前无法使用
说明:
在大多数小程序平台,*audio* 标签已被废弃或无法使用,本插件可以代替 *audio* 标签播放音乐,并实现以下优化:
1. [pause-video](basic/prop#pause-video) 属性也可以应用于音频,即播放一个音视频时可以自动暂停其他正在播放的音视频
2. 增加了一个可以拖动的进度条
3. 组件大小可以根据页面宽度自动调整
4. 支持 *autoplay* 属性
5. 播放被后台打断时,页面显示后自动继续播放
基础库要求:
支付宝 *1.23.4+* ,其余平台满足 [最低要求](question/faq#lib) 即可
*5* 条仅微信 *2.2.3+* 、*QQ*、百度支持
?> 如果希望页面上使用本组件,组件的路径为 *path/to/mp-html/audio/audio*
属性和事件基本同 *audio* 组件,组件实例上提供了 *setSrc*、*play*、*seek*、*pause*、*stop* 方法可供控制播放状态
## editable
功能:富文本编辑
下表列出了本插件与原生 *editor* 组件的功能差异,可按需选用
| 组件 | 优点 | 缺点 |
|:---:|:---:|:---:|
| 原生 *editor* | 底层通过 *contenteditable* 实现,编辑流畅 | 支持标签少(不支持音视频、表格以及 *section* 等常用标签)、部分小程序平台不支持或低版本不兼容 |
| 本插件 | 支持标签全面、支持平台全面 | 编辑灵活性不够强 |
大小:*≈17.5KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √(nvue 不支持) |
##### 示例项目 :id=editable_demo
微信小程序点击 [代码片段](https://developers.weixin.qq.com/s/S2ZpZDm87fQP) 即可在微信开发者工具中导入;*uni-app* 下载 [示例项目](https://mp-html.oss-cn-hangzhou.aliyuncs.com/editable.zip) 在 *HBuilder X* 中打开即可体验;注意示例项目中不一定包含最新版本,仅供参考使用方法
?> 也可以参考示例小程序 [源代码](https://github.com/jin-yufeng/mp-html-demo)
说明:
引入本插件后,会给组件添加以下属性:
| 属性名 | 类型 | 默认值 | 说明 |
|:---:|:---:|:---:|:---:|
| editable | Boolean | false | 是否开启内容编辑 |
| placeholder | String | 请输入 | 输入框为空时占位符([2.1.0+](changelog/changelog#v210) |
?> [2.5.0](changelog/changelog#v250) 版本起支持将 *editable* 属性设置为 *"simple"* 来开启简易模式,简易模式下,点击文字内容直接进入编辑,不再弹出操作菜单栏和方框
添加以下事件:
| 事件名 | 触发时机 | 用途 |
|:---:|:---:|:---:|
| remove[2.2.0+](changelog/changelog#v220) | 删除图片/视频/音频标签时 | 删除已上传的线上文件 |
支持以下操作:
| 类型 | 操作 |
|:---:|:---:|
| 文本 | 修改 |
| 图片 | 更换链接、调整宽度、设置成超链接([2.0.4+](changelog/changelog#v204))、设置预览图链接、禁用预览、删除 |
| 链接 | 更换链接、删除 |
| 音视频 | 设置封面、设置循环播放、设置自动播放([2.2.0+](changelog/changelog#v220))、删除 |
| 普通标签 | 设置字体大小、颜色([2.4.2+](changelog/changelog#v242))、斜体、粗体、下划线([2.0.4+](changelog/changelog#v204))、居中、缩进、删除 |
?> [2.2.1](changelog/changelog#v221) 版本起所有标签支持上下移动操作,但仅限同级标签间移动,即在有同级标签且非第一个(或最后一个)时可以上移(或下移)
?> 在支付宝小程序中使用时需要在页面样式中添加 *page { position: relative; }* 避免 *tooltip* 错位
?> 菜单项可以通过编辑 *plugins/editable/config.js* 进行修改,仅可以删减或调整顺序,添加或更名无效(颜色设置除外)
[组件实例](advanced/api#getCompent) 上提供了以下方法(*editable* 属性为 *true* 时才可以调用):
| 名称 | 功能 |
|:---:|:---:|
| undo | 撤销一个操作 |
| redo | 重做一个操作 |
| insertHtml | 在光标处插入指定 html 内容([2.1.0+](changelog/changelog#v210) |
| insertImg | 在光标处插入一张图片 |
| insertTable(rows, cols) | 在光标处插入一个 rows 行 cols 列的表格([2.1.3+](changelog/changelog#v213) |
| insertVideo | 在光标处插入一个视频 |
| insertAudio | 在光标处插入一个音频 |
| insertLink | 在光标处插入一个链接 |
| insertText | 在光标处插入一段文本 |
| clear | 清空内容 |
| getContent | 获取编辑后的 html 内容 |
?> 考虑到不同场景下希望获取链接的方法不同,需要在初始时给组件设置一个 *getSrc* 方法(否则插入图片、音视频、链接或修改链接等操作无法使用),每次组件内需要链接时会调用此方法,开发者可在此方法中自行决定如何获取链接,返回 **线上地址** 即可(具体用法见下方示例)
[2.2.0](changelog/changelog#v220) 版本起设置了 [domain](basic/prop#domain) 属性时,返回的地址可以缺省主域名
编辑完成后,通过 *getContent* 方法获取编辑后的 *html*,最后将 *editable* 属性设置为 *false* 即可正常渲染
!> 点击保存按钮时,部分平台 *tap* 事件早于 *blur* 事件触发,直接获取内容可能导致无法获取当前编辑的文本内容,因此建议设置一个小的延时后获取(可参考下方示例,[详细](https://github.com/jin-yufeng/mp-html/issues/368)
示例:
```javascript
Page({
onLoad () {
// ctx 为组件实例,获取方法见上
/**
* @description 设置获取链接的方法
* @param {String} type 链接的类型img/video/audio/link
* @param {String} value 修改链接时,这里会传入旧值
* @returns {Promise} 返回线上地址2.2.0 版本起设置了 domain 属性时,可以缺省主域名)
* type 为 audio/video 时,可以返回一个源地址数组
* 2.1.3 版本起 type 为 audio 时,可以返回一个 object包含 src、name、author、poster 等字段
* 2.2.0 版本起 type 为 img 时,可以返回一个源地址数组,表示插入多张图片(修改链接时仅限一张)
*/
this.ctx.getSrc = (type, value) => {
return new Promise((resolve, reject) => {
// 以图片为例
if (type == 'img') {
wx.chooseImage({
count: value === undefined ? 9 : 1, // 2.2.0 版本起插入图片时支持多张(修改图片链接时仅限一张)
success: res => {
wx.showLoading({
title: '上传中'
});
(async ()=>{
const arr = []
for (let item of res.tempFilePaths) {
// 依次上传
const src = await upload(item)
arr.push(src)
}
return arr
})().then(res => {
wx.hideLoading()
resolve(res)
})
},
fail: reject
})
}
})
}
},
finishEdit () {
setTimeout(() => {
var html = ctx.getContent() // 获取编辑好的 html
// 上传 html
wx.request({
url: 'xxx',
data: {
html
},
success: () => {
this.setData({
editable: false // 结束编辑
})
}
})
}, 50)
}
})
```
注意事项:
不要在 *editable* 属性被设置为 *true* 前通过 [setContent](advanced/api#setContent) 方法(用 [content](basic/prop#content) 属性)设置内容,否则在切换为 *true* 后会变成空白
## emoji
功能:解析 *emoji*
大小:*≈3KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √ |
说明:
将形如 *[笑脸]* 的文本替换为 *emoji* 字符 😄
匹配模式可以通过修改 *reg* 变量实现
默认配置了 *177* 个常用的 *emoji* 小表情,可以自行按照需要修改 *data* 变量
?> 与 [editable](#editable) 插件共用时,导出编辑好的 *html* 内容,会将 *emoji* 字符编码为文本形式,便于存储
## highlight
功能:代码块高亮显示
大小:*≈16KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √ |
说明:
编辑 *plugins/highlight/config.js* ,可以选择是否需要以下功能:
- *copyByLongPress* 是否需要长按代码块时显示复制代码内容菜单(*uni-app nvue* 暂不支持)
- *showLanguageName* 是否在代码块右上角显示语言的名称
- *showLineNumber* 是否在左侧显示行号
!> 修改该配置后需要重新 [生成组件包](advanced/develop#pack),在构建后的组件包中修改配置无法生效
引入本插件后,*html* 中符合以下格式的 *pre* 将被高亮处理:
```html
<!-- pre 中内含一个 code并在 pre 或 code 的 class 中设置 language- -->
<pre><code class="language-css">p { color: red }</code></pre>
```
?> 与 [editable](#editable) 插件共用时,编辑状态下,不会进行高亮,可以直接修改代码文本
?> 本插件的高亮功能依赖于 [prismjs](https://prismjs.com/),默认配置中仅支持 *html*、*css*、*c-like*、*javascript* 语言和 *Tomorrow Night* 主题,如果需要更多语言或更换主题请前往 [官网](https://prismjs.com/download.html) 下载对应的 *prism.min.js**prism.css* 并替换 *plugins/highlight/* 目录下的文件(*prismjs* 的插件大多涉及 *dom* 操作,基本不可用,请勿选择)
## markdown
功能:渲染 *markdown*
大小:*≈37KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √ |
说明:
引入本插件后,会给组件添加一个 *markdown* 属性,将该属性设置为 *true* 后,即可通过 [content](basic/prop#content) 属性或 [setContent](advanced/api#setContent) 方法设置 *markdown* 内容即可
?> 若开启 [use-anchor](basic/prop#use-anchor) 属性,所有标题 `# xxx` 都会被设置为锚点,通过链接 `[xxx](#xxx)` 可以直接跳转
?> 本插件通过 [marked](https://github.com/markedjs/marked) 解析 *markdown* 文本,部分 *css* 摘选自 [github-markdown-css](https://github.com/sindresorhus/github-markdown-css)
?> 本插件可以和 [highlight](#highlight) 插件共用,实现 *markdown* 中代码块的高亮效果
## search
功能:关键词搜索
大小:*≈1.5KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √(nvue 不支持) |
说明:
引入后会在 [组件实例](advanced/api#getCompent) 上挂载一个 *search* 方法,用于关键词搜索
输入值
| 参数名 | 类型 | 默认值 | 说明 |
|:---:|:---:|:---:|---|
| key | String 或 RegExp | - | 要搜索的关键词,支持字符串和正则 |
| anchor | Boolean | false | 是否将搜索结果设置为锚点 |
| style | String | background-color:yellow | 标记搜索结果的样式 |
返回值:*Promise*
| 属性 | 类型 | 说明 |
|:---:|:---:|---|
| num | Number | 搜索结果数量 |
| highlight | Function(i, style='background-color:#FF9632') | 高亮第 i1 ~ num个结果将其样式设置为 style |
| jump | Function(i, offset) | 跳转到第 i1 ~ num个结果偏移量为 offsetanchor 为 true 才可用 |
示例:
```javascript
function search (key) {
// ctx 为组件实例
ctx.search(key, true).then(res => {
res.highlight(1)
res.jump(1, -50) // 高亮第 1 个结果并跳转到该位置,偏移量 -50
})
}
```
?> 具体用法可以参考示例小程序 [源代码](https://github.com/jin-yufeng/mp-html-demo)
附加说明:
1. 不传入 *key*(或为空)时即可取消搜索,取消所有的高亮,还原到原来的效果
2. 进行新的搜索时旧的搜索结果将被还原,旧的结果中的 *highlight* 等方法不再可用
3. 调用 *highlight* 方法高亮一个结果时,之前被高亮的结果会被还原,即始终只有一个结果被高亮
4. *key* 传入字符串时大小写敏感,如果要忽略大小写可以用正则的 *i*(字符串搜索效率高于正则)
5. 设置 *anchor**true* 会一定程度上降低效率,非必要不要开启
6. 暂不支持跨标签搜索,即只有一个文本节点内包含整个关键词才能被搜索到
## style
功能:解析和匹配 *style* 标签中的样式
?> 这里的 *style* 标签指的是传入 [content](basic/prop#content) 属性中的 *html* 里包含的 *style* 标签,且 *style* 标签要放在其他标签前面才能生效
大小:*≈3.5KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √ (nvue 直接支持) |
说明:
支持以下选择器:
| 名称 | 示例 |
|:---:|---|
| 标签名选择器 | p {} |
| class 选择器 | .class {} |
| id 选择器 | #id {} |
| 多选择器交集 | p.class {} |
| 多选择器并集 | p, .class {} |
| 后代选择器 | .class1 .class2 {} |
| 子选择器 | .class1 > .class2 {} |
| 伪类 | .class::before {} |
伪类仅支持 *before**after*,支持 *attr* 方法
不支持的选择器(属性选择器等)将被忽略
!> 由于小程序中无法动态写入 *css*,本插件的实现原理是通过解析,将匹配的样式添加到各标签的行内 *style* 中去,请慎用宽泛的选择器,以免大大增加解析结果大小,减慢渲染速度
## txv-video
功能:使用腾讯视频
大小:*≈1KB*
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | | | | √ (h5 和 app 直接支持) |
说明:
引入本插件后,*html* 中符合下方格式的 *iframe* 标签(*src* 中含有 *vid*)将被转为通过腾讯视频播放:
```html
<iframe src="https://v.qq.com/txp/iframe/player.html?vid=xxxxxx" allowFullScreen="true"></iframe>
```
同时,其可以被 [pause-video](basic/prop#pause-video) 属性控制
!> 本插件仅用于将官方 [腾讯视频插件](https://github.com/tvfe/txv-miniprogram-plugin) 应用于本组件,使用前请确认已经成功申请使用该插件并按要求在小程序 *app.json* 中配置完成(*uni-app* 中的配置方法可以参考 [#103](https://github.com/jin-yufeng/mp-html/issues/103#issuecomment-654586246)),否则可能报错 **This application has not registered any plugins yet** 且无法生效
?> 腾讯视频插件 [v2](https://github.com/tvfe/txv-miniprogram-plugin) 默认自动播放,[v1](https://github.com/tvfe/txv-miniprogram-plugin/blob/master/archieve/readme.md) 不会,可按需选择
## img-cache
功能:图片本地缓存
大小:*≈4KB*
作者:[@PentaTea](https://github.com/PentaTea)
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| | | | | | √(仅支持 app 的 vue 页面) |
说明:
引入本插件后,会给组件添加一个 *img-cache* 属性,将该属性设置为 *true* 后,将自动下载引用的图片并将 *src* 属性更换为本地地址
同时在 [组件实例](advanced/api#getCompent) 上挂载了 *imgCache* 对象,扩充缓存控制能力
*imgCache* 对象属性和方法:
| 属性 | 功能 |
|:---:|:---:|
| list | 当前缓存的 url 列表 |
| get(url) | 传入 url 获得本地地址 |
| delete(url) | 传入 url 删除缓存记录 |
| add(url) | 传入 url 并下载目标为缓存 |
| clear() | 清空所有缓存 |
!> 请尽量确保 *src* 中含有文件后缀名,不以后缀结尾也没关系,插件会从路径中推测合理的图片后缀,如果完全不包含后缀信息可能会无法保存到相册
## latex
功能:渲染 *latex* 公式
大小:**≈300KB**
作者:[@Zeng-J](https://github.com/Zeng-J)
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √ |
说明:
引入本插件后,会将 *$xxx$* 的文本内容按照 *latex* 规则进行解析和渲染
?> [2.5.0](changelog/changelog#v250) 版本起支持 *$$xxx$$* 形式的块级公式
?> 与 [editable](#editable) 插件共用时,编辑状态下,公式不会渲染,可以直接修改公式文本
?> 在 *js* 的字符串中写 *latex* 公式时需注意 *\\* 会变成转义符,要使用 *\\\\**String.raw``* 的方式
?> 本插件通过 [katex-mini](https://github.com/rojer95/katex-mini) 解析 *latex* 文本,[字体文件](https://github.com/KaTeX/KaTeX/tree/main/fonts) 建议自行转存
## card
功能:商品(联络人)信息卡
大小:*≈7KB*
作者:[@whoooami](https://github.com/whoooami)
支持平台:
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|:---:|:---:|:---:|:---:|:---:|:---:|
| √ | √ | √ | √ | √ | √(nvue 不支持) |
效果图:
![效果图](../assets/plugin/card.png)
参数列表:
|参数名|是否必须|类型|说明|
|:---- |:---|:----- |----- |
|src|是|String|图片Url|
|title|是|String|标题|
|desc|是|String|描述|
|url|是|String|跳转url|
|color|是|String|文字颜色|
|bgcolor|是|String|卡片背景颜色|
|border|是|String|卡片边框颜色|
说明:
1. 可以显示商品信息卡片/联络人信息卡片
基础库要求:
满足最低要求即可
?> 如果希望页面上使用本组件,组件的路径为 *path/to/mp-html/card/card*
## 开发插件 :id=develop
一个插件大致需要以下文件(*plugin/template* 中提供了一个模板)
- *build.js*
构建文件,需要导出一个 *object*,可以内含以下项:
| 名称 | 类型 | 默认值 | 功能 |
|:---:|:---:|:---:|---|
| main | string | index.js | 入口文件路径 |
| platform | string[] | ['mp-weixin', 'mp-qq', 'mp-baidu', 'mp-alipay', 'mp-toutiao', 'uni-app'] | 支持使用的平台 |
| template | string | - | 要被添加到模板文件中的标签 (nvue 不可用) |
| methods | object | {} | 用于处理模板中事件的方法 (nvue 不可用) |
| style | string | - | 用于模板文件的 css 样式 |
| import | string&#124;string[] | - | 用于模板文件的 css 文件路径 |
| usingComponents | object | {} | 用于模板的组件或插件列表 (nvue 不可用) |
| handler | function | - | 自定义文件处理方法 |
- *index.js*
入口文件,导出一个 *function*,每个组件在被创建时,会依次实例化各个插件,并传入组件实例可供调用
插件实例上可以挂载以下钩子方法,将在对应时机被调用
| 名称 | 触发时机 | 参数 | 返回值 |
|:---:|:---:|:---:|:---:|
| onUpdate | 更新 html 内容时触发 | 更新的 html 内容和解析配置 | 如果对输入值进行了修改,则返回修改后的内容 |
| onParse | 解析到一个标签时触发 | 标签和解析器实例 | 返回 false 将移除该标签 |
| onLoad | dom 树加载完成时触发 | - | - |
| onDetached | 组件被移除时触发 | - | - |
- *README.md*
使用说明
- *其他依赖文件*
插件目录下,除了 *.md* 的文件、*build.js* 和 *import* 字段中的 *css* 文件,其他的都会被拷贝到生成的组件包中,因此请不要放置无关文件
对于仅在原生平台中使用的内容可放在 *miniprogram* 目录下,仅在 *uni-app* 中使用的内容可放在 *uni-app* 目录下,两个目录下可以分别放置 *build.js*
如果在插件中需要用到解析器(将 *html* 字符串解析为 *nodes* 数组),可以引入 *src/parser.js* 使用,方法如下:
```javascript
const Parser = require('../parser.js')
var instance = new Parser(vm) // 实例化解析器,传入组件实例将自动获取相关配置
var nodes = instance.parse(content) // 解析完成
```
!> 如果编写了插件,在升级组件包时请注意备份,避免丢失
?> 欢迎提交实用的插件 [pull requests](https://github.com/jin-yufeng/mp-html/pulls) 到 *plugins* 文件夹

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -0,0 +1,173 @@
# 📫 事件 :id=event
## 获取方式 :id=method
- *uni-app*
*@* + 事件名 或 *v-on:* + 事件名
事件信息从 *event* 中获取
```vue
<template>
<view>
<mp-html @ready="ready" />
</view>
</template>
<script>
export default {
methods: {
ready (e) {
console.log(e)
}
}
}
</script>
```
- 支付宝小程序
*on* + 事件名(首字母大写)
事件信息从 *event* 中获取
```axml
<mp-html onReady="ready">
```
```javascript
Page({
ready (e) {
console.log(e)
}
})
```
- 其他小程序平台
*bind* + 事件名
事件信息从 *event.detail* 中获取
```wxml
<mp-html bindready="ready" />
```
```javascript
Page({
ready (e) {
console.log(e.detail)
}
})
```
## load
触发时机:*dom* 树加载完毕时
返回值:无
用途:可以调用 [api](advanced/api) 函数
## ready
触发时机:图片加载完毕时(不包含懒加载的图片)
?> 判断方式是 *350ms* 总高度无变化就认为加载完毕,部分情况下可能不准确;[2.4.0](changelog/changelog#v240) 版本起 [lazy-load](basic/prop#lazy-load) 属性为 *false* 时根据图片的 *load* 事件判断,可以基本准确触发
返回值:富文本区域的 *boundingClientRect* 结构体,包含大小位置信息
用途:此时进行 [锚点跳转](advanced/api#navigateTo) 可以基本保证跳转位置正确
?> 如果设置了 [懒加载](basic/prop#lazy-load),此时返回的大小不一定是最终大小,如果需要实时的大小,可以调用 [getRect](advanced/api#getRect) 方法
## error
触发时机:发生渲染错误时
返回值:一个 *object*,其中 *source* 为错误来源(包括 *img*、*video*、*audio**attrs* 为该标签的属性列表(包含 *src* 等信息),*errMsg* 是错误信息
用途:收集错误信息,减少使用出错率高的链接
## imgtap
触发时机:图片被点击时
返回值:该 *img* 标签的属性列表
用途:
默认情况下图片被点击时将自动预览(具体处理可见 [图片效果](overview/feature#img)),如果不希望如此,可将 [preview-img](basic/prop#preview-img) 属性设置为 *false* 并在这里自定义处理
如果需要用到富文本中所有图片的数组,可以通过 [imgList](advanced/api#imgList) 的 *api* 获取
示例:
```javascript
Page({
imgtap (e) {
// 对做了某种标记的图片进行预览
if (e.detail['data-flag']) {
wx.previewImage({
urls: [e.detail.src] // 仅预览单张图片
})
}
}
})
```
## linktap
触发时机:链接被点击时
返回值:该 *a* 标签的属性列表
?> [2.0.5](changelog/changelog#v205) 版本起增加返回该标签内部文本 *innerText*
用途:
默认情况下链接被点击时,对于外部链接,将被拷贝到剪贴板,如果不希望如此,可以将 [copy-link](basic/prop#copy-link) 属性的值设置为 *false* 后在这里自定义处理,可参考以下方案:
1. 跳转 *web-view*
跳转到一个新的页面,该页面放置一个 [web-view](https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html) 用于显示外部网页(需要注意 *web-view* 的使用限制)
2. 跳转其他小程序
对于其他小程序的链接(可通过自行设置的 *data-* 属性判断),通过 [navigateToMiniProgram](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/miniprogram-navigate/wx.navigateToMiniProgram.html) 接口跳转(需要注意跳转限制)
3. 下载文档
对于文档类的链接(可通过后缀名或自行设置的 *data-* 属性判断),可以通过 [downloadFile](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html) 接口下载文件后通过 [openDocument](https://developers.weixin.qq.com/miniprogram/dev/api/file/wx.openDocument.html) 接口打开文档(需要注意下载域名限制)
4. 下载压缩包
对于压缩包类的链接(可通过后缀名或自行设置的 *data-* 属性判断),可以通过 [downloadFile](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html) 接口下载文件后通过 [FileSystemManager.unzip](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.unzip.html) 接口解压,然后进行打开文档等操作(需要注意下载域名限制)
对于大文件(可通过 *data-* 属性标注),还可以在下载前进行询问,下载过程中通过 [DownloadTask](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.html) 提示下载进度等
示例:
```javascript
Page({
linktap (e) {
if (e.detail.href.includes('.doc')) {
// 下载 doc 文件
wx.downloadFile({
url: e.detail.href,
success (res) {
wx.hideLoading()
wx.openDocument({
filePath: res.tempFilePath
})
},
fail (err) {
wx.hideLoading()
wx.showModal({
title: '失败',
content: err.errMsg,
showCancel: false
})
}
})
} else if (e.detail.href.includes('xxx.com')) {
// 跳转到 webview
wx.navigateTo({
url: 'pages/webview/webview?url=' + e.detail.href,
})
} else if (e.detail['data-appid']) {
// 跳转其他小程序
wx.navigateToMiniProgram({
appId: e.detail['data-appid']
})
}
}
})
```
## play
?> [2.3.0](changelog/changelog#v230) 版本起支持
触发时机:音视频播放时
返回值:无
?> [2.5.0](changelog/changelog#v250) 版本起返回值变更为一个 *object*,其中 *source* 为播放来源(包括 *video*、*audio**attrs* 为该标签的属性列表(包含 *src* 等信息),可以统计播放量等信息
用途:和 [pauseMedia](advanced/api#pauseMedia) 的 `api` 配合可以实现与页面中其他音视频进行互斥播放
示例:
```javascript
Page({
// 页面中的音视频播放事件
pagePlay() {
// ctx 为组件实例
ctx.pauseMedia() // 暂停组件内的音视频播放
},
// 组件的 play 事件
componentPlay() {
// media 为页面中的音视频实例
media.pause() // 暂停页面中的音视频播放
}
})
```

View File

@@ -0,0 +1,141 @@
# 🔨 属性 :id=prop
!> 需要将某个属性设置为 *false* 时,应写作 *attr="{{false}}"*
## container-style
?> [2.1.0](changelog/changelog#v210) 版本起支持
功能:设置容器的样式
类型:*String*
示例:
```css
padding: 5px; /* 设置内边距 */
font-size: 18px; /* 设置默认的字体大小 */
overflow: hidden; /* 禁用横向滚动 */
display: inline; /* 行内显示 */
white-space: pre-wrap; /* 保留空格和换行符 */
white-space: pre-line; /* 保留换行符 */
```
## content
功能:用于渲染的 *html* 字符串
类型:*String*
## copy-link
功能:是否允许外部链接被点击时自动复制
类型:*Boolean*
默认值:*true*
?> 对于 *uni-app**h5**app* 平台,外链是能够直接跳转的,这种情况下如果该属性为 *true* 则直接跳转外链(而不是复制链接),为 *false* 则不跳转
## domain
功能:主域名(用于链接拼接)
类型:*String*
示例:
```html
<!-- 假设 domain 属性被设置为 https://example.com
以下链接均会被拼接为 https://example.com/path -->
<img src="//example.com/path" />
<img src="/path" />
<div style="background-image:url('path')"></div>
```
?> 通过 *base* 标签也可以设置主域名,但优先级低于此属性
!> 该属性必须填写 协议名://域名 的完整链接
暂不支持拼接含有 *../* 的相对路径链接
*a* 标签的 *href* 属性可能需要跳转到小程序内路径,因此不进行 *domain* 拼接
设置该属性后将无法使用本地图片
## error-img
功能:图片出错时的占位图链接
类型:*String*
!> 该属性不会进行拼接 [domain](#domain),需传入完整路径(可以使用本地路径)
## lazy-load
功能:是否开启图片懒加载
类型:*Boolean*
默认值:*false*
?> 不同平台懒加载的时机不同,具体参考各平台 *image* 组件懒加载的时机
## loading-img
功能:图片加载过程中的占位图链接
类型:*String*
!> 该属性不会进行拼接 [domain](#domain),需传入完整路径(可以使用本地路径)
## pause-video
功能:是否在播放一个视频时自动暂停其他视频
类型:*Boolean*
默认值:*true*
?> 如果需要多个视频同时播放的,请将此属性设置为 *false*
## preview-img
功能:是否允许图片被点击时自动预览
类型:*Boolean*
默认值:*true*
?> 自动预览允许左右滑动查看所有图片,如果不希望如此可以禁用自动预览并在 [imgtap](basic/event#imgtap) 事件中自行处理
!> 默认情况下 *base64* 图片无法点击预览,[2.5.0](changelog/changelog#v250) 版本起支持将本属性设置为 *"all"* 开启 *base64* 图片的预览,但需要注意各平台 *previewImage**api**base64* 图片支持度不高,需充分测试后使用,如果无法预览,可参考 [imgList](advanced/api#imgList) 中的方法进行转存
## scroll-table
功能:是否给每个表格添加一个滚动层使其能单独横向滚动
类型:*Boolean*
默认值:*false*
!> 如果表格设置了 *inline* 布局,该属性将不会生效以免破坏行内布局
## selectable
功能:是否开启文本长按复制
类型:*Boolean* / *String*
默认值:*false*
!> 将本属性的值设置为 *true* 在微信 *iOS* 端可能失效,[2.0.5](changelog/changelog#v205) 版本起支持将本属性设置为 "*force*" 来支持,但会带来以下影响:
1、所有文本块会显示为 *inline-block*(通过 [text](https://developers.weixin.qq.com/miniprogram/dev/component/text.html) 标签的 *user-select* 属性实现),需要自行适配
2、文字下划线、删除线等效果将失效[详细](https://github.com/jin-yufeng/mp-html/issues/382)
3、所有文本块都无法被 *rich-text* 包含,一定程度上增加标签数,减慢渲染速度
从 [2.3.1](changelog/changelog#v231) 版本起对此问题进行优化(通过 [rich-text](https://developers.weixin.qq.com/miniprogram/dev/component/rich-text.html) 标签的 *user-select* 属性实现,基础库 *2.24.0* 及以上生效),第 *3* 个问题已解决,第 *1*、*2* 个问题部分情况下还会存在
## set-title
功能:是否将 *title* 标签的内容设置到页面标题
类型:*Boolean*
默认值:*true*
## show-img-menu
?> [2.3.0](changelog/changelog#v230) 版本起支持控制预览时是否长按显示菜单(仅微信、百度小程序有效)
功能:是否允许图片被长按时显示菜单
类型:*Boolean*
默认值:*true*
!> 该属性目前仅微信、百度和 *uni-app**app* 平台有效
## tag-style
功能:设置标签的默认样式
类型:*Object*
示例:
```javascript
// 格式为 标签名: 样式
{
a: 'color:red' // a 标签默认为红色
}
```
?> 该属性非响应式,需要在设置 [content](#content) 属性前设置才能生效,动态修改不能实时生效
!> 该属性的原理是解析到各标签的内联 *style* 属性中去,如果对特别常用的标签设置默认样式,将大大加大解析结果大小,减慢渲染速度,这种情况下建议通过 [外部样式](overview/quickstart#setting) 引入
## use-anchor
功能:是否使用锚点链接
类型:*Boolean* / *Number*
默认值:*false*
?> 传入一个数字时表示跳转锚点的偏移量(单位 *px*
?> 开启该属性会将所有设置了 *id* 属性的标签都暴露出来,一定程度上减慢渲染速度,非必要不要开启

View File

@@ -0,0 +1,322 @@
# 📖 更新日志 :id=changelog
## v2.5.1
发布时间:*2025-04-20*
主要更新:
1. `U` `uni-app` 包适配鸿蒙 `APP` [详细](https://github.com/jin-yufeng/mp-html/issues/615)
2. `U` 微信小程序替换废弃 `api` `getSystemInfoSync` [详细](https://github.com/jin-yufeng/mp-html/issues/613)
3. `F` 修复了微信小程序 `glass-easel` 框架下真机换行异常的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/607) by [@PaperStrike](https://github.com/PaperStrike)
4. `F` 修复了 `uni-app``app` 端播放视频可能报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/617)
5. `F` 修复了 [latex](advanced/plugin#latex) 插件可能出现 `xxx can be used only in display mode` 的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/632)
6. `F` 修复了 `uni-app` 包 [latex](advanced/plugin#latex) 公式可能不显示的问题 [#599](https://github.com/jin-yufeng/mp-html/issues/599)、[#627](https://github.com/jin-yufeng/mp-html/issues/627)
## v2.5.0
发布时间:*2024-04-22*
主要更新:
1. `U` [play](basic/event#play) 事件增加返回 `src` 等信息 [详细](https://github.com/jin-yufeng/mp-html/issues/526)
2. `U` [preview-img](basic/prop#preview-img) 属性支持设置为 `all` 开启 `base64` 图片预览 [详细](https://github.com/jin-yufeng/mp-html/issues/536)
3. `U` [editable](advanced/plugin#editable) 插件增加简易模式(点击文字直接编辑)
4. `U` [latex](advanced/plugin#latex) 插件支持块级公式 [详细](https://github.com/jin-yufeng/mp-html/issues/582)
5. `F` 修复了表格部分情况下背景丢失的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/587)
6. `F` 修复了部分 `svg` 无法显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/591)
7. `F` 修复了 `uni-app``h5``app` 端部分情况下样式无法识别的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/518)
8. `F` 修复了 [latex](advanced/plugin#latex) 插件部分情况下显示不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/580)
9. `F` 修复了 [editable](advanced/plugin#editable) 插件表格无法删除的问题
10. `F` 修复了 [editable](advanced/plugin#editable) 插件 `uni-app``vue3` `h5` 端点击图片报错的问题
11. `F` 修复了 [editable](advanced/plugin#editable) 插件 `uni-app` 包点击表格没有菜单栏的问题
## v2.4.3
发布时间:*2024-01-21*
主要更新:
1. `A` 增加 [card](advanced/plugin#card) 插件 [详细](https://github.com/jin-yufeng/mp-html/pull/533) by [@whoooami](https://github.com/whoooami)
2. `F` 修复了 `svg` 中包含 `foreignobject` 可能不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/523)
3. `F` 修复了合并单元格的表格部分情况下显示不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/561)
4. `F` 修复了 `img` 标签设置 `object-fit` 无效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/567)
5. `F` 修复了 [latex](advanced/plugin#latex) 插件公式会换行的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/540)
6. `F` 修复了 `uni-app` 包 [editable](advanced/plugin#editable) 和 [audio](advanced/plugin#audio) 插件共用时点击 `audio` 无法编辑的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/529) by [@whoooami](https://github.com/whoooami)
7. `F` 修复了支付宝小程序设置了宽高的图片可能显示不正常的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/544)
8. `F` 修复了 `uni-app` 包微信小程序部分情况下图片会报错 `replace of undefined` 的问题
9. `F` 修复了 `uni-app` 包快手小程序图片不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/571)
## v2.4.2
发布时间:*2023-05-14*
主要更新:
1. `A` [editable](advanced/plugin#editable) 插件支持修改文字颜色 [详细](https://github.com/jin-yufeng/mp-html/issues/254)
2. `F` 修复了 `svg` 中有 `style` 不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/505)
3. `F` 修复了 `uni-app` 包使用旧版编译器可能报错 `Bad attr nodes` 的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/472)
4. `F` 修复了 `uni-app``app` 端可能出现无法读取 `lazyLoad` 的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/513)
5. `F` 修复了 [editable](advanced/plugin#editable) 插件在点击换图时未拼接 [domain](basic/prop#domain) 的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/497) by [@TwoKe945](https://github.com/TwoKe945)
6. `F` 修复了 [latex](advanced/plugin#latex) 插件部分情况下不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/515)
7. `F` 修复了 [editable](advanced/plugin#editable) 插件点击音视频时其他标签框不消失的问题
## v2.4.1
发布时间:*2022-12-25*
主要更新:
1. `F` 修复了没有图片时 [ready](basic/event#ready) 事件可能不触发的问题
2. `F` 修复了加载过程中可能出现 `Root label not found` 错误的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/470)
3. `F` 修复了 [audio](advanced/plugin#audio) 插件退出页面可能会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/457)
4. `F` 修复了 `uni-app``vue3` 运行到 `app``HBuilder X 3.6.10` 以上报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/480)
5. `F` 修复了原生包链接内有图片时可能错误换行的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/464)
6. `F` 修复了 `uni-app``nvue` 端链接中包含 `%22` 时可能无法显示的问题
7. `F` 修复了 `uni-app``vue3` 使用 [highlight](advanced/plugin#highlight) 插件可能报错的问题
8. `F` 修复了头条小程序使用 [editable](advanced/plugin#editable) 插件内容为空时点击会报错的问题
## v2.4.0
发布时间:*2022-08-27*
主要更新:
1. `A` 增加了 [setPlaybackRate](advanced/api#setPlaybackRate) 的 `api`,可以设置音视频的播放速率 [详细](https://github.com/jin-yufeng/mp-html/issues/452)
2. `A` 示例小程序代码开源 [详细](https://github.com/jin-yufeng/mp-html-demo)
3. `U` 优化 [ready](basic/event#ready) 事件触发时机,未设置懒加载的情况下基本可以准确触发 [详细](https://github.com/jin-yufeng/mp-html/issues/195)
4. `U` [highlight](advanced/plugin#highlight) 插件在编辑状态下不进行高亮处理,便于编辑
5. `F` 修复了 `flex` 布局下图片大小可能不正确的问题
6. `F` 修复了 [selectable](basic/prop#selectable) 属性没有设置 `force` 也可能出现渲染异常的问题
7. `F` 修复了表格中的图片大小可能不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/448)
8. `F` 修复了含有合并单元格的表格可能无法设置竖直对齐的问题
9. `F` 修复了 [editable](advanced/plugin#editable) 插件在 `scroll-view` 中使用时工具条位置可能不正确的问题
10. `F` 修复了 `uni-app``vue3` 使用 [search](advanced/plugin#search) 插件可能导致错误换行的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/449)
## v2.3.2
发布时间:*2022-08-13*
主要更新:
1. `A` 增加 [latex](advanced/plugin#latex) 插件,可以渲染数学公式 [详细](https://github.com/jin-yufeng/mp-html/pull/447) by [@Zeng-J](https://github.com/Zeng-J)
2. `U` 优化根节点下有很多标签的长内容渲染速度
3. `U` [highlight](advanced/plugin#highlight) 插件适配 `lang-xxx` 格式
4. `F` 修复了 `table` 标签设置 `border` 属性后可能无法修改边框样式的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/439) by [@zouxingjie](https://github.com/zouxingjie)
5. `F` 修复了 [editable](advanced/plugin#editable) 插件输入连续空格无效的问题
6. `F` 修复了 `uni-app``vue3` 图片设置 `inline` 会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/438)
7. `F` 修复了 `uni-app``vue3` 使用 `table` 可能报错的问题
## v2.3.1
发布时间:*2022-05-20*
主要更新:
1. `U` `uni-app``app` 端支持使用本地图片
2. `U` 优化了微信小程序 [selectable](basic/prop#selectable) 属性在 `ios` 端的处理 [详细](basic/prop#selectable)
3. `U` 原生包取消样式隔离,部分平台可以直接引入页面样式 [#376](https://github.com/jin-yufeng/mp-html/issues/376)、[详细](overview/quickstart#externStyle)
4. `F` 修复了 [editable](advanced/plugin#editable) 插件不在顶部时 `tooltip` 位置可能错误的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/430)
5. `F` 修复了 `uni-app``vue3` 运行到微信小程序可能报错丢失内容的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/414)
6. `F` 修复了 `uni-app``vue3` 部分标签可能被错误换行的问题
7. `F` 修复了原生包个别情况下可能报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/428)
8. `F` 修复了 [editable](advanced/plugin#editable) 插件 `uni-app` 包的 `app` 端插入视频无法预览的问题
## v2.3.0
发布时间:*2022-04-01*
主要更新:
1. `A` 增加了 `play` 事件,音视频播放时触发,可用于与页面其他音视频进行互斥播放 [详细](basic/event#play)
2. `U` [show-img-menu](basic/prop#show-img-menu) 属性支持控制预览时是否长按弹出菜单
3. `U` 优化 `wxs` 处理,提高渲染性能 [详细](https://developers.weixin.qq.com/community/develop/article/doc/0006cc2b204740f601bd43fa25a413)
4. `U` `video` 标签支持 `object-fit` 属性
5. `U` 增加支持一些常用实体编码 [详细](https://github.com/jin-yufeng/mp-html/issues/418)
6. `F` 修复了图片仅设置高度可能不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/410)
7. `F` 修复了 `video` 标签高度设置为 `auto` 不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/411)
8. `F` 修复了使用 `grid` 布局时可能样式错误的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/413)
9. `F` 修复了含有合并单元格的表格部分情况下显示异常的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/417)
10. `F` 修复了百度小程序使用 `br` 标签可能部分不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/409)
11. `F` 修复了 [editable](advanced/plugin#editable) 插件连续插入内容时顺序不正确的问题
12. `F` 修复了 `uni-app``vue3` 使用 [audio](advanced/plugin#audio) 插件报错的问题
13. `F` 修复了 `uni-app` 包 [highlight](advanced/plugin#highlight) 插件使用自定义的 `prism.min.js` 报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/416)
## v2.2.2
发布时间:*2022-02-26*
主要更新:
1. `A` 增加了 [pauseMedia](advanced/api#pauseMedia) 的 `api`,可用于暂停播放音视频 [详细](https://github.com/jin-yufeng/mp-html/issues/317)
2. `U` 优化了长内容的加载速度
3. `U` `uni-app` 包适配 `vue3` [#389](https://github.com/jin-yufeng/mp-html/issues/389)、[#398](https://github.com/jin-yufeng/mp-html/pull/398) by [@zhouhuafei](https://github.com/zhouhuafei)、[#400](https://github.com/jin-yufeng/mp-html/issues/400)
4. `F` 修复了小程序端图片高度设置为百分比时可能不显示的问题
5. `F` 修复了 [highlight](advanced/plugin#highlight) 插件部分情况下可能显示不完整的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/403)
## v2.2.1
发布时间:*2021-12-24*
主要更新:
1. `A` [editable](advanced/plugin#editable) 插件增加上下移动标签功能
2. `U` [editable](advanced/plugin#editable) 插件支持在文本中间光标处插入内容
3. `F` 修复了 `uni-app``nvue` 端设置 `margin` 后可能导致高度不正确的问题
4. `F` 修复了 [highlight](advanced/plugin#highlight) 插件使用压缩版的 `prism.css` 可能导致背景失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/367)
5. `F` 修复了 `uni-app` 包编辑状态下使用 [emoji](advanced/plugin#emoji) 插件内容为空时可能报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/371)
6. `F` 修复了使用 [editable](advanced/plugin#editable) 插件后将 [selectable](basic/prop#selectable) 属性设置为 `force` 不生效的问题
## v2.2.0
发布时间:*2021-10-11*
主要更新:
1. `A` 增加 [customElements](overview/quickstart#customelements) 配置项,便于添加自定义功能性标签 [详细](https://github.com/jin-yufeng/mp-html/issues/350)
2. `A` [editable](advanced/plugin#editable) 插件增加切换音视频自动播放状态的功能 [详细](https://github.com/jin-yufeng/mp-html/pull/341) by [@leeseett](https://github.com/leeseett)
3. `A` [editable](advanced/plugin#editable) 插件删除媒体标签时触发 `remove` 事件,便于删除已上传的文件
4. `U` [editable](advanced/plugin#editable) 插件 `insertImg` 方法支持同时插入多张图片 [详细](https://github.com/jin-yufeng/mp-html/issues/342)
5. `U` [editable](advanced/plugin#editable) 插入图片和音视频时支持拼接 [domian](basic/prop#domain) 主域名
6. `F` 修复了内部链接参数中包含 `://` 时被认为是外部链接的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/356)
7. `F` 修复了部分 `svg` 标签名或属性名大小写不正确时不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/351)
8. `F` 修复了 `uni-app``nvue` 页面运行到非 `app` 平台时可能样式错误的问题
9. `D` 移除了 `ad` 配置项(由 [customElements](overview/quickstart#customelements) 代替)
## v2.1.5
发布时间:*2021-08-13*
主要更新:
1. `A` 增加支持标签的 `dir` 属性
2. `F` 修复了 `ruby` 标签文字与拼音没有居中对齐的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/325)
3. `F` 修复了音视频标签内有 `a` 标签时可能无法播放的问题
4. `F` 修复了 [externStyle](overview/quickstart#setting) 中的 `class` 名包含下划线或数字时可能失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/326)
5. `F` 修复了 `uni-app``h5` 端引入 [externStyle](overview/quickstart#setting) 可能不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/326)
6. `F` 修复了微信原生包 `a` 标签的 `style` 中包含 `inline` 时不响应事件的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/332)
## v2.1.4
发布时间:*2021-07-14*
主要更新:
1. `F` 修复了 `rt` 标签无法设置样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/318)
2. `F` 修复了表格中有单元格同时合并行和列时可能显示不正确的问题
3. `F` 修复了 `uni-app` 包的 `app` 端无法关闭图片长按菜单的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/322)
4. `F` 修复了 [editable](advanced/plugin#editable) 插件只能添加图片链接不能修改的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/312) by [@leeseett](https://github.com/leeseett)
## v2.1.3
发布时间:*2021-06-12*
主要更新:
1. `A` [editable](advanced/plugin#editable) 插件增加 `insertTable` 方法
2. `U` [editable](advanced/plugin#editable) 插件支持编辑表格中的空白单元格 [详细](https://github.com/jin-yufeng/mp-html/issues/310)
3. `F` 修复了 [externStyle](overview/quickstart#setting) 中使用伪类可能失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/298)
4. `F` 修复了多个组件同时使用时 [tag-style](basic/prop#tag-style) 属性时可能互相影响的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/305) by [@woodguoyu](https://github.com/woodguoyu)
5. `F` 修复了包含 `linearGradient``svg` 可能无法显示的问题
6. `F` 修复了 `uni-app` 包编译到头条小程序时可能报错的问题
7. `F` 修复了 `uni-app``nvue` 端不触发 `click` 事件的问题
8. `F` 修复了 [editable](advanced/plugin#editable) 插件尾部插入时无法撤销的问题
9. `F` 修复了 [editable](advanced/plugin#editable) 插件的 `insertHtml` 方法只能在末尾插入的问题
10. `F` 修复了 [editable](advanced/plugin#editable) 插件插入音频不显示的问题
## v2.1.2
发布时间:*2021-04-24*
主要更新:
1. `A` 增加了 [img-cache](advanced/plugin#img-cache) 插件,可以在 `uni-app``app` 端缓存图片 [详细](https://github.com/jin-yufeng/mp-html/issues/292) by [@PentaTea](https://github.com/PentaTea)
2. `U` 支持通过 [container-style](basic/prop#container-style) 属性设置 `white-space` 来保留连续空格和换行符 [详细](question/faq#space)
3. `U` 代码风格符合 [standard](https://standardjs.com) 标准
4. `U` `uni-app` 包使用 [editable](advanced/plugin#editable) 插件编辑状态下支持预览视频 [详细](https://github.com/jin-yufeng/mp-html/issues/286)
5. `F` 修复了 `svg` 标签内嵌 `svg` 时无法显示的问题
6. `F` 修复了微信原生包 `img` 标签的 `style` 中包含 `inline` 时不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/287)
7. `F` 修复了支付宝和头条小程序部分区域不可复制的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/291)
## v2.1.1
发布时间:*2021-04-09*
主要更新:
1. `F` 修复了对 `p` 标签设置 [tag-style](basic/prop#tag-style) 可能不生效的问题
2. `F` 修复了 `svg` 标签中的文本无法显示的问题
3. `F` 修复了 `uni-app` 包的 `nvue` 端不设置 [container-style](basic/prop#container-style) 可能报错的问题
4. `F` 修复了 `uni-app` 包使用 [editable](advanced/plugin#editable) 插件编辑表格时可能报错的问题
5. `F` 修复了 `uni-app` 包使用 [highlight](advanced/plugin#highlight) 插件运行到头条小程序时可能没有样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/280)
6. `F` 修复了 `uni-app` 包使用 [editable](advanced/plugin#editable) 插件 `editable` 属性为 `false` 时会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/284)
7. `F` 修复了 [style](advanced/plugin#style) 插件连续子选择器失效的问题
8. `F` 修复了 [editable](advanced/plugin#editable) 插件无法修改图片和字体大小的问题
## v2.1.0
发布时间:*2021-03-20*
主要更新:
1. `A` 增加了 [container-style](basic/prop#container-style) 属性 [详细](https://gitee.com/jin-yufeng/mp-html/pulls/1)
2. `A` 增加支持 `strike` 标签
3. `A` `editable` 插件增加 `placeholder` 属性 [详细](advanced/plugin#editable)
4. `A` `editable` 插件增加 `insertHtml` 方法 [详细](advanced/plugin#editable)
5. `U` 外部样式支持标签名选择器 [详细](overview/quickstart#setting)
6. `F` 修复了 `uni-app``nvue` 端部分情况下可能不显示的问题
## v2.0.5
发布时间:*2021-03-12*
主要更新:
1. `U` [linktap](basic/event#linktap) 事件增加返回内部文本内容 `innerText` [详细](https://github.com/jin-yufeng/mp-html/issues/271)
2. `U` [selectable](basic/prop#selectable) 属性设置为 `force` 时能够在微信 `iOS` 端生效(文本块会变成 `inline-block` [详细](https://github.com/jin-yufeng/mp-html/issues/267)
3. `F` 修复了部分情况下竖向无法滚动的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/182)
4. `F` 修复了 `uni-app` 包多次修改富文本数据时部分内容可能不显示的问题
5. `F` 修复了百度小程序真机部分内容不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/272)
6. `F` 修复了 [腾讯视频](advanced/plugin#txv-video) 插件可能无法播放的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/265)
7. `F` 修复了 [highlight](advanced/plugin#highlight) 插件没有设置高亮语言时没有应用默认样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/276) by [@fuzui](https://github.com/fuzui)
## v2.0.4
发布时间:*2021-01-31*
主要更新:
1. `A` [editable](advanced/plugin#editable) 插件增加下划线和图片超链接的功能 [详细](https://github.com/jin-yufeng/mp-html/issues/254)
2. `U` 支付宝和头条小程序原生包直接通过 `template` 递归实现渲染
3. `F` 修复了 `img` 标签设置 `data-src` 可能导致图片不显示的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/257)
4. `F` 修复了 `script` 标签中的 `<` 会被解析为标签的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/259)
5. `F` 修复了 `uni-app` 包的 `app` 端播放视频时可能高度突然变小的问题
6. `F` 修复了 `uni-app` 包的 `app` 端长按图片会报错的问题
7. `F` 修复了 `uni-app` 包的 `nvue` 端使用 [editable](advanced/plugin#editable) 插件后无法显示的问题
8. `F` 修复了 [editable](advanced/plugin#editable) 插件删除图片和切换内容时可能出现错误选择框的问题
9. `F` 修复了 [editable](advanced/plugin#editable) 插件无法编辑链接文本内容的问题
## v2.0.3
发布时间:*2021-01-15*
主要更新:
1. `U` 图片被点击时不冒泡(可以与整体的点击区分开,不影响链接中的图片)
2. `F` 修复了图片链接缺省协议名时可能无法预览的问题
3. `F` 修复了原生包 `video``audio` 标签内放置文本会报错的问题
4. `F` 修复了 [editable](advanced/plugin#editable) 插件清空内容时弹窗可能不消失的问题
5. `F` 修复了 [highlight](advanced/plugin#highlight) 插件部分情况下样式不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/231)
6. `F` 修复了文档首页跳转到快速开始页时 `404` 的问题 by [@AnsonZnl](https://github.com/AnsonZnl)
## v2.0.2
发布时间:*2021-01-08*
主要更新:
1. `U` `uni-app` 包微信端利用 `virtualHost` 去除夹层,加快渲染
2. `F` 修复了部分情况下 `flex` 布局显示不正确的问题
3. `F` 修复了设置 [loading-img](basic/prop#loading-img) 会导致懒加载失效的问题
4. `F` 修复了头条原生包部分情况下表格显示不正确的问题
5. `F` 修复了 `uni-app``h5``app` 端部分插件样式无法应用的问题
6. `F` 修复了 `uni-app` 包没有自动将 `data-src` 设置为 `src` 的问题
7. `F` 修复了 `uni-app` 包的 `nvue` 端 [getRect](advanced/api#getRect) 方法无法使用的问题
8. `F` 修复了 `uni-app` 包运行到华为快应用可能报错的问题
9. `F` 修复了在低版本 taro 中使用可能无法正确解析的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/248)
10. `F` 修复了 [highlight](advanced/plugin#highlight) 插件 `pre``code` 之间有空白符时无法高亮的问题
11. `F` 修复了 [editable](advanced/plugin#editable) 插件清空内容后插入可能报错的问题
## v2.0.1
发布时间:*2021-01-01*
主要更新:
1. `F` 修复了 `a` 标签自动跳转到不存在页面时可能报错的问题
2. `F` 修复了含合并单元格的表格设置列宽可能导致显示不正确的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/239)
3. `F` 修复了表格中的图片可能错位的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/194)
4. `F` 修复了原生包使用 [editable](advanced/plugin#editable) 插件点击标签时可能报错的问题
5. `F` 修复了 `uni-app``h5``app` 端可能无法使用 `iframe``embed` 标签的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/245)
6. `F` 修复了 `uni-app` 使用部分插件会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/246)
## v2.0.0
发布时间:*2020-12-26*
主要更新:
1. `U` 通过 `gulp` 进行构建,自动生成各平台压缩版代码,减小引入包大小 [详细](advanced/develop#pack)
2. `U` 没有设置 `href` 属性的 `a` 标签不应用链接的样式,可以用作一般标签的点击处理 [详细](question/faq#tap)
3. `U` 提供了统一的插件接口,扩展更加方便(代码高亮、`markdown` 等都可以直接通过引入插件实现)[详细](advanced/plugin)
4. `U` 实现了简单的编辑功能 [详细](advanced/plugin#editable)
5. `U` 支持生成各平台的示例项目,便于调试 [详细](overview/quickstart#demo)
6. `U` 原生包共用一份源代码(构建时进行自动转换),注释更加详细,便于了解和维护
7. `U` 通过 `jest` 进行单元测试,进一步保证代码质量 [详细](advanced/develop#test)
8. `U` 去除了一些冗余功能,进一步减小包大小(约 `24.5KB`
9. `U` 减少了递归节点树,加快渲染速度
10. `U` `uni-app``app` 端使用非原生的 `video` 显示视频以解决无法同层带来的系列问题
11. `U` `uni-app``nvue` 端支持通过 `bgColor` 属性设置背景色(默认白色,不可设置为透明)
12. `F` 修复了 `uni-app``nvue` 端从不可见区域到可见时可能显示不正确的问题
*v1.x* 更新指南:
- 组件路径和名称
*parser* -> *mp-html*
- *npm* 包名
*parser-wx* -> *mp-html*
- 组件属性
更名:
*html* -> [content](basic/prop#content)
*autopause* -> [pause-video](basic/prop#pause-video)
*autoscroll* -> [scroll-table](basic/prop#scroll-table)
*autosetTitle* -> [set-title](basic/prop#set-title)
移除:
*compress*, *show-with-animation*, *use-cache*
新增:
[copy-link](basic/prop#copy-link), [error-img](basic/prop#error-img), [preview-img](basic/prop#preview-img), [show-img-menu](basic/prop#show-img-menu)
修改:
[use-anchor](basic/prop#use-anchor) 支持传入数字表示跳转偏移量
- 组件事件
更名 *linkpress* -> [linktap](basic/event#linktap)
移除 *parse* 事件
*imgtap**linktap* 事件中不再返回 *ignore* 方法,可以使用 [preview-img](basic/prop#preview-img) 和 [copy-link](basic/prop#copy-link) 属性禁用自动预览/拷贝
链接被点击时,不再支持自动跳转其他小程序,可以自行在 [linktap](basic/event#linktap) 事件中进行跳转
- api
[navigateTo](advanced/api#navigateTo) 返回 *Promise*,不再采用 *success**fail* 回调
*rect* 变更为 [getRect](advanced/api#getRect) 方法
移除 *getVideoContext* 方法
[imgList](advanced/api#imgList) 不再包含 *setItem**each* 方法
- 其他
默认不再支持匹配 *style* 标签中的样式,如有需要请使用 [style](advanced/plugin#style) 插件
不再支持 *base64* 图片预览(默认为不可预览的小图片)
取消了配置项(相关解析配置在 *parser.js* 中,*filter* 等方法用 [插件](advanced/plugin) 的方式替代)
取消了视频的懒加载(应避免在一个页面中使用过多视频,以免卡顿)
*ad* 标签默认不添加到模板(详见 [个性化](overview/quickstart#setting)

View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>小程序富文本组件</title>
<link rel="icon" href="assets/logo/logo.ico" type="image/x-icon">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="小程序富文本组件">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="lib/vue.css">
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script")
hm.src = "https://hm.baidu.com/hm.js?69554d980eb5eb786919e3c3ad37b2ce"
var s = document.getElementsByTagName("script")[0]
s.parentNode.insertBefore(hm, s)
})()
</script>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'mp-html',
repo: 'https://github.com/jin-yufeng/mp-html',
coverpage: true,
loadSidebar: true,
subMaxLevel: 3,
auto2top: true,
search: {
placeholder: '搜索',
noData: '找不到结果'
}
}
// 渲染测试内容
function render () {
document.getElementById("show").srcdoc='<meta name="viewport" content="width=device-width"><style>img{max-width:100%}</style>'+document.getElementById("input").value.replace(/<script[\s\S]+?<\/script>/g, '')
}
// 清空测试内容
function reset () {
document.getElementById("input").value = ''
document.getElementById("show").srcdoc = ''
}
</script>
<script src="lib/docsify.min.js"></script>
<script src="lib/search.min.js"></script>
<script src="lib/prism-bash.min.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More