From 4da852944e22fd33eff3f06e47e9bb716170d555 Mon Sep 17 00:00:00 2001
From: ZF sun <34314687@qq.com>
Date: Sun, 4 Jan 2026 09:40:09 +0800
Subject: [PATCH] =?UTF-8?q?chore(=E4=BB=A3=E7=A0=81=E5=90=88=E5=B9=B6):=20?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BB=8ECustom/common=E5=88=86=E6=94=AF?=
=?UTF-8?q?=E5=90=88=E5=B9=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
App.vue | 5 +-
common/css/main.scss | 1754 +++++------
common/css/mp_html_patch.scss | 4 +
common/js/config.js | 32 +-
common/js/golbalConfig.js | 36 +-
common/js/http.js | 2 +-
common/js/lang.js | 1 +
common/js/style_color.js | 879 +++---
common/js/util.js | 33 +-
components-diy/diy-bottom-nav.vue | 4 +-
components-diy/diy-category-item.vue | 4 +-
components-diy/diy-category.vue | 5 +-
components-diy/diy-digit.vue | 4 +-
components-diy/diy-index-page.vue | 4 +-
components-diy/diy-listmenu.vue | 2 +-
components-diy/diy-text.vue | 2 +-
components/common-payment/common-payment.vue | 19 +
components/hover-nav/hover-nav.vue | 6 +-
docs/H5_ROUTER_FIX_README.md | 32 +
docs/HISTORY_MODE_SUB_DIR_DEPLOY.md | 756 +++++
docs/SERVER_ROUTER_FALLBACK_CONFIG.md | 140 +
lang/en-us/contact/contact.js | 16 +-
lang/en-us/member/index.js | 24 +-
lang/zh-cn/agreement/contenr.js | 1 +
lang/zh-cn/contact/contact.js | 16 +-
lang/zh-cn/goods/category.js | 13 +-
lang/zh-cn/goods/detail.js | 6 +-
lang/zh-cn/goods/list.js | 22 +-
lang/zh-cn/member/index.js | 37 +-
pages.json | 8 +
pages/empty/index.vue | 3 +-
pages/index/index.vue | 216 +-
pages_goods/cart.vue | 3 +-
pages_goods/category.vue | 3 +-
pages_goods/detail.vue | 3 +-
pages_goods/list.vue | 12 +-
pages_goods/public/css/list.scss | 10 +
pages_order/detail.vue | 3 +-
pages_order/detail_local_delivery.vue | 3 +-
pages_order/detail_pickup.vue | 3 +-
pages_order/detail_point.vue | 3 +-
pages_order/list.vue | 3 +-
pages_order/payment.vue | 3 +-
pages_promotion/fenxiao/apply.vue | 3 +-
pages_promotion/fenxiao/bill.vue | 310 +-
pages_promotion/fenxiao/child_fenxiao.vue | 599 ++--
pages_promotion/fenxiao/goods_list.vue | 19 +-
pages_promotion/fenxiao/index.vue | 1189 +++----
pages_promotion/fenxiao/level.vue | 637 ++--
pages_promotion/fenxiao/order.vue | 421 +--
pages_promotion/fenxiao/order_detail.vue | 755 +++--
pages_promotion/fenxiao/promote.vue | 753 ++---
pages_promotion/fenxiao/promote_code.vue | 611 ++--
pages_promotion/fenxiao/ranking_list.vue | 849 ++---
pages_promotion/fenxiao/relation.vue | 327 +-
pages_promotion/fenxiao/team.vue | 736 ++---
pages_promotion/fenxiao/withdraw_apply.vue | 3 +-
pages_promotion/fenxiao/withdraw_list.vue | 433 +--
pages_promotion/fenxiao/withdrawal_detail.vue | 253 +-
pages_promotion/merch/detail.vue | 23 +-
pages_promotion/merch/merchcategory.vue | 3 +-
pages_promotion/merch/public/category.scss | 1856 +++++------
pages_promotion/point/detail.vue | 3 +-
pages_promotion/point/goods_list.vue | 17 +-
pages_promotion/point/list.vue | 3 +-
pages_promotion/point/order_list.vue | 20 +-
pages_promotion/point/payment.vue | 25 +-
pages_promotion/point/result.vue | 185 +-
pages_tool/agreement/contenr.vue | 107 +-
pages_tool/article/detail.vue | 3 +-
pages_tool/article/list.vue | 20 +-
pages_tool/contact/contact.vue | 100 +-
pages_tool/form/form.vue | 3 +-
pages_tool/form/formdata.vue | 165 +
pages_tool/goods/brand.vue | 16 +-
pages_tool/goods/coupon.vue | 19 +-
pages_tool/goods/coupon_receive.vue | 3 +-
pages_tool/goods/evaluate.vue | 706 +++--
pages_tool/goods/not_exist.vue | 85 +-
pages_tool/goods/search.vue | 3 +-
pages_tool/help/detail.vue | 3 +-
pages_tool/help/list.vue | 3 +-
pages_tool/index/diy.vue | 153 +-
pages_tool/login/find.vue | 3 +-
pages_tool/login/login.vue | 894 +++---
pages_tool/member/account.vue | 13 +-
pages_tool/member/account_edit.vue | 3 +-
pages_tool/member/address.vue | 982 +++---
pages_tool/member/address_edit.vue | 3 +-
pages_tool/member/apply_withdrawal.vue | 3 +-
pages_tool/member/assets.vue | 3 +-
pages_tool/member/balance.vue | 3 +-
pages_tool/member/balance_detail.vue | 11 +-
pages_tool/member/cancellation.vue | 3 +-
pages_tool/member/cancelrefuse.vue | 3 +-
pages_tool/member/cancelstatus.vue | 3 +-
pages_tool/member/cancelsuccess.vue | 3 +-
pages_tool/member/card.vue | 3 +-
pages_tool/member/card_agreement.vue | 3 +-
pages_tool/member/card_buy.vue | 3 +-
pages_tool/member/collection.vue | 11 +-
pages_tool/member/contact.vue | 3 +-
pages_tool/member/coupon.vue | 11 +-
pages_tool/member/footprint.vue | 11 +-
pages_tool/member/index.vue | 190 +-
pages_tool/member/info.vue | 3 +-
pages_tool/member/info_edit.vue | 3 +-
pages_tool/member/invite_friends.vue | 3 +-
pages_tool/member/level.vue | 3 +-
pages_tool/member/level_growth_rules.vue | 547 ++--
pages_tool/member/modify_face.vue | 3 +-
pages_tool/member/pay_password.vue | 3 +-
pages_tool/member/point.vue | 3 +-
pages_tool/member/point_detail.vue | 11 +-
pages_tool/member/withdrawal.vue | 11 +-
pages_tool/member/withdrawal_detail.vue | 3 +-
pages_tool/notice/detail.vue | 3 +-
pages_tool/notice/list.vue | 283 +-
pages_tool/order/activist.vue | 9 +-
pages_tool/order/detail_virtual.vue | 3 +-
pages_tool/order/evaluate.vue | 171 +-
pages_tool/order/logistics.vue | 3 +-
pages_tool/order/refund.vue | 3 +-
pages_tool/order/refund_batch.vue | 3 +-
pages_tool/order/refund_detail.vue | 3 +-
pages_tool/order/refund_goods_select.vue | 3 +-
pages_tool/order/refund_type_select.vue | 3 +-
pages_tool/pay/cashier.vue | 13 +-
pages_tool/pay/index.vue | 36 +-
pages_tool/pay/result.vue | 3 +-
pages_tool/pay/wx_pay.vue | 187 +-
pages_tool/recharge/list.vue | 3 +-
pages_tool/recharge/order_list.vue | 4 +-
pages_tool/storeclose/storeclose.vue | 197 +-
pages_tool/vr/index.vue | 103 +-
pages_tool/webview/webview.vue | 3 +-
store/index.js | 63 +-
uni_modules/mp-html/README.md | 453 +--
.../mp-html/components/mp-html/mp-html.vue | 998 +++---
.../mp-html/components/mp-html/node/node.vue | 1209 +++----
.../mp-html/components/mp-html/parser.js | 2793 +++++++++--------
uni_modules/mp-html/package.json | 150 +-
.../static/app-plus/mp-html/js/handler.js | 255 +-
.../static/app-plus/mp-html/local.html | 34 +-
vue.config.js | 103 +-
145 files changed, 13781 insertions(+), 11628 deletions(-)
create mode 100644 common/css/mp_html_patch.scss
create mode 100644 docs/H5_ROUTER_FIX_README.md
create mode 100644 docs/HISTORY_MODE_SUB_DIR_DEPLOY.md
create mode 100644 docs/SERVER_ROUTER_FALLBACK_CONFIG.md
create mode 100644 lang/zh-cn/agreement/contenr.js
create mode 100644 pages_tool/form/formdata.vue
diff --git a/App.vue b/App.vue
index af2d651..7382b24 100644
--- a/App.vue
+++ b/App.vue
@@ -1,7 +1,7 @@
-
-
+
+
+
+
+
+
+
+
+ {{ item.type_name }}
+ 账单编号: {{ item.account_no }}
+ {{ $util.timeStampTurnTime(item.create_time) }}
+
+
+ {{ item.money > 0 ? '+' + item.money : item.money }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/child_fenxiao.vue b/pages_promotion/fenxiao/child_fenxiao.vue
index 4fcf443..a3f6786 100644
--- a/pages_promotion/fenxiao/child_fenxiao.vue
+++ b/pages_promotion/fenxiao/child_fenxiao.vue
@@ -1,289 +1,310 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.nickname }}
- {{ item.level_name }}
-
- 加入时间:{{ $util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}
-
-
- {{ item.one_child_fenxiao_num + item.one_child_num }} 人
- {{ item.order_num }} 单
- {{ item.order_money|moneyFormat }} 元
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.nickname }}
+ {{
+ item.level_name }}
+
+ 加入时间:{{
+ $util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}
+
+
+ {{ item.one_child_fenxiao_num + item.one_child_num }} 人
+ {{ item.order_num }} 单
+ {{ item.order_money | moneyFormat }} 元
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/goods_list.vue b/pages_promotion/fenxiao/goods_list.vue
index 85fdee1..ef3f20c 100644
--- a/pages_promotion/fenxiao/goods_list.vue
+++ b/pages_promotion/fenxiao/goods_list.vue
@@ -1,6 +1,5 @@
-
-
+
@@ -133,10 +132,26 @@
diff --git a/pages_promotion/fenxiao/index.vue b/pages_promotion/fenxiao/index.vue
index 370447d..dc128d6 100644
--- a/pages_promotion/fenxiao/index.vue
+++ b/pages_promotion/fenxiao/index.vue
@@ -1,587 +1,604 @@
-
-
-
-
-
-
-
-
-
-
- 可{{ fenxiaoWords.withdraw }}(元)
-
- 提现明细
-
-
-
- {{ info.account }}
-
-
-
-
-
- 提现中(元)
- {{ info.account_withdraw_apply | moneyFormat }}
-
-
- 待入账(元)
- {{ info.in_progress_money | moneyFormat }}
-
-
- 提现
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 您还不是{{ fenxiaoWords.fenxiao_name }},请先提交申请
- 立即加入
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/pages_promotion/fenxiao/level.vue b/pages_promotion/fenxiao/level.vue
index 5a5f0d5..a673cb0 100644
--- a/pages_promotion/fenxiao/level.vue
+++ b/pages_promotion/fenxiao/level.vue
@@ -1,320 +1,319 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ fenxiaoInfo.nickname }}
- {{ item.level_name }}
-
-
-
- 一级分佣比率
- {{ item.one_rate }}%
-
-
- 二级分佣比率
- {{ item.two_rate }}%
-
-
- 三级分佣比率
- {{ item.three_rate }}%
-
-
-
-
-
-
-
-
-
-
-
-
-
- 快速升级技巧
-
- {{ levelInfo.complete > levelInfo.task_num ? levelInfo.task_num : levelInfo.complete }}
- /{{ levelInfo.task_num }}
-
-
-
-
-
-
- {{item.title}}
-
-
-
- {{ item.progress == 100 ? '已完成' : '未完成' }}
-
-
-
-
-
- {{item.desc}}
-
- {{ item.value }}
- /{{ item.condition }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/pages_promotion/fenxiao/order.vue b/pages_promotion/fenxiao/order.vue
index 589d0c2..36e0a17 100644
--- a/pages_promotion/fenxiao/order.vue
+++ b/pages_promotion/fenxiao/order.vue
@@ -1,203 +1,218 @@
-
-
-
-
-
- {{ item.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ orderItem.sku_name }}
-
- 返{{ fenxiaoWords.account }}
- {{ $lang('common.currencySymbol') }}
- {{ parseFloat(orderItem.commission).toFixed(2).split(".")[0] }}
- .{{ parseFloat(orderItem.commission).toFixed(2).split(".")[1] }}
-
-
-
-
- {{ $lang('common.currencySymbol') }}
- {{ parseFloat(orderItem.price).toFixed(2).split(".")[0] }}
- .{{ parseFloat(orderItem.price).toFixed(2).split(".")[1] }}
-
-
-
-
- {{ orderItem.num }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ orderItem.sku_name }}
+
+ 返{{ fenxiaoWords.account }}
+ {{ $lang('common.currencySymbol') }}
+ {{ parseFloat(orderItem.commission).toFixed(2).split(".")[0] }}
+ .{{ parseFloat(orderItem.commission).toFixed(2).split(".")[1] }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ parseFloat(orderItem.price).toFixed(2).split(".")[0] }}
+ .{{ parseFloat(orderItem.price).toFixed(2).split(".")[1] }}
+
+
+
+
+ {{ orderItem.num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/order_detail.vue b/pages_promotion/fenxiao/order_detail.vue
index 64b1888..4158687 100644
--- a/pages_promotion/fenxiao/order_detail.vue
+++ b/pages_promotion/fenxiao/order_detail.vue
@@ -1,378 +1,377 @@
-
-
-
-
-
-
-
-
-
-
- {{ orderData.sku_name }}
-
-
-
- ¥
- {{ parseFloat(orderData.price).toFixed(2).split(".")[0] }}
- .{{ parseFloat(orderData.price).toFixed(2).split(".")[1] }}
-
-
-
-
-
- {{ orderData.num }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- 订单编号:
- {{ orderData.order_no }}
-
-
- 分佣层级:
- {{ orderData.commission_level }}级
-
-
- 返佣金额:
-
- ¥
- {{ parseFloat(orderData.commission).toFixed(2).split(".")[0] }}
- .{{ parseFloat(orderData.commission).toFixed(2).split(".")[1] }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {{ orderData.sku_name }}
+
+
+
+ ¥
+ {{ parseFloat(orderData.price).toFixed(2).split(".")[0] }}
+ .{{ parseFloat(orderData.price).toFixed(2).split(".")[1] }}
+
+
+
+
+
+ {{ orderData.num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 订单编号:
+ {{ orderData.order_no }}
+
+
+ 分佣层级:
+ {{ orderData.commission_level }}级
+
+
+ 返佣金额:
+
+ ¥
+ {{ parseFloat(orderData.commission).toFixed(2).split(".")[0] }}
+ .{{ parseFloat(orderData.commission).toFixed(2).split(".")[1] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/promote.vue b/pages_promotion/fenxiao/promote.vue
index e234f96..e634a8d 100644
--- a/pages_promotion/fenxiao/promote.vue
+++ b/pages_promotion/fenxiao/promote.vue
@@ -1,363 +1,390 @@
-
-
-
-
-
-
-
-
-
- 累计收益
- {{ detailData.total_commission }}
- 元
-
-
-
-
-
- 已邀请好友
- 已下单好友
-
-
-
-
-
-
- {{ item.nickname }}
-
-
-
- 暂无已邀请好友,快去邀请吧
-
-
-
-
-
-
- 暂无活动规则
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ 累计收益
+ {{ detailData.total_commission }}
+ 元
+
+
+
+
+
+ 已邀请好友
+ 已下单好友
+
+
+
+
+
+
+ {{ item.nickname }}
+
+
+
+ 暂无已邀请好友,快去邀请吧
+
+
+
+
+
+
+ 暂无活动规则
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/promote_code.vue b/pages_promotion/fenxiao/promote_code.vue
index c0d5480..d1063e3 100644
--- a/pages_promotion/fenxiao/promote_code.vue
+++ b/pages_promotion/fenxiao/promote_code.vue
@@ -1,307 +1,306 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 长按识别图中二维码
-
-
-
- 保存海报
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pages_promotion/fenxiao/ranking_list.vue b/pages_promotion/fenxiao/ranking_list.vue
index 1f6c723..6a134eb 100644
--- a/pages_promotion/fenxiao/ranking_list.vue
+++ b/pages_promotion/fenxiao/ranking_list.vue
@@ -1,416 +1,433 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{info.nickname}}
-
- 点击提现
- 我的团队
-
-
-
-
-
-
-
-
-
-
-
- 分销佣金
- {{ info.today_commission}}元
-
-
-
-
-
-
-
-
-
- 佣金排行
- 您排行第{{ ranking }}名
-
-
-
-
-
-
-
-
-
-
-
- 推广人数
- {{ info.one_child_num}}人
-
-
-
-
-
-
-
-
-
- 推广排行
- 您排行第{{ ranking }}名
-
-
-
-
-
- 佣金排行
- 推广排行
-
-
-
- {{ index + 1 }}
-
-
-
-
- {{ item.nickname }}
-
-
- ¥{{ item.total_commission|moneyFormat }}
- {{ item.child_num }}人
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{info.nickname}}
+
+ 点击提现
+ 我的团队
+
+
+
+
+
+
+
+
+
+
+
+ 分销佣金
+ {{ info.today_commission}}元
+
+
+
+
+
+
+
+
+
+ 佣金排行
+ 您排行第{{ ranking }}名
+
+
+
+
+
+
+
+
+
+
+
+ 推广人数
+ {{ info.one_child_num}}人
+
+
+
+
+
+
+
+
+
+ 推广排行
+ 您排行第{{ ranking }}名
+
+
+
+
+
+ 佣金排行
+ 推广排行
+
+
+
+ {{ index + 1 }}
+
+
+
+
+ {{ item.nickname }}
+
+
+ ¥{{ item.total_commission|moneyFormat }}
+ {{ item.child_num }}人
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/relation.vue b/pages_promotion/fenxiao/relation.vue
index b1d5b9c..36927b5 100644
--- a/pages_promotion/fenxiao/relation.vue
+++ b/pages_promotion/fenxiao/relation.vue
@@ -1,152 +1,175 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ orderItem.sku_name }}
-
- 返{{ fenxiaoWords.account }}
- {{ $lang('common.currencySymbol') }}
- {{ orderItem.commission }}
-
-
-
-
- {{ $lang('common.currencySymbol') }}
- {{ orderItem.price }}
-
-
-
-
- {{ orderItem.num }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ orderItem.sku_name }}
+
+ 返{{ fenxiaoWords.account }}
+ {{
+ $lang('common.currencySymbol') }}
+ {{ orderItem.commission
+ }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ orderItem.price }}
+
+
+
+
+ {{ orderItem.num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/team.vue b/pages_promotion/fenxiao/team.vue
index 878cc7e..87d9a47 100644
--- a/pages_promotion/fenxiao/team.vue
+++ b/pages_promotion/fenxiao/team.vue
@@ -1,355 +1,381 @@
-
-
-
-
-
- {{ item.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.nickname }}
- {{ fenxiaoWords.fenxiao_name }}
-
- 加入时间:{{ $util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}
-
-
-
- {{ item.one_child_num }}
- 人
-
-
- {{ item.order_num }}
- 单
-
-
- {{ item.order_money | moneyFormat }}
- 元
-
-
-
-
-
-
-
- {{ item.nickname }}
-
-
- 加入时间:{{ $util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}
-
-
-
- 0
- 人
-
-
- {{ item.order_num }}
- 单
-
-
- {{ item.order_money | moneyFormat }}
- 元
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.nickname }}
+ {{
+ fenxiaoWords.fenxiao_name }}
+
+ 加入时间:{{
+ $util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}
+
+
+
+ {{ item.one_child_num }}
+ 人
+
+
+ {{ item.order_num }}
+ 单
+
+
+ {{ item.order_money | moneyFormat }}
+ 元
+
+
+
+
+
+
+
+ {{ item.nickname }}
+
+
+ 加入时间:{{
+ $util.timeStampTurnTime(item.bind_fenxiao_time).substring(0, 10) }}
+
+
+
+ 0
+ 人
+
+
+ {{ item.order_num }}
+ 单
+
+
+ {{ item.order_money | moneyFormat }}
+ 元
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/withdraw_apply.vue b/pages_promotion/fenxiao/withdraw_apply.vue
index f1e109d..e5dfb4e 100644
--- a/pages_promotion/fenxiao/withdraw_apply.vue
+++ b/pages_promotion/fenxiao/withdraw_apply.vue
@@ -1,6 +1,5 @@
-
-
+
提现到
diff --git a/pages_promotion/fenxiao/withdraw_list.vue b/pages_promotion/fenxiao/withdraw_list.vue
index 4c0a66f..60812fd 100644
--- a/pages_promotion/fenxiao/withdraw_list.vue
+++ b/pages_promotion/fenxiao/withdraw_list.vue
@@ -1,209 +1,224 @@
-
-
-
-
-
-
-
-
-
-
- {{ item.transfer_type=='balance'&&'余额' || item.transfer_type=='alipay'&&'支付宝' || item.transfer_type=='bank'&&'银行卡' || item.transfer_type=='wechatpay'&&'微信' }}
-
- {{ $util.timeStampTurnTime(item.create_time) }}
-
-
-
- ¥{{ item.money }}
- {{ item.status_name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {{ item.transfer_type=='balance'&&'余额' || item.transfer_type=='alipay'&&'支付宝' || item.transfer_type=='bank'&&'银行卡' || item.transfer_type=='wechatpay'&&'微信' }}
+
+ {{ $util.timeStampTurnTime(item.create_time) }}
+
+
+
+ ¥{{ item.money }}
+ {{ item.status_name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/fenxiao/withdrawal_detail.vue b/pages_promotion/fenxiao/withdrawal_detail.vue
index ab18b5b..51d563a 100644
--- a/pages_promotion/fenxiao/withdrawal_detail.vue
+++ b/pages_promotion/fenxiao/withdrawal_detail.vue
@@ -1,127 +1,126 @@
-
-
-
-
- -{{ detail.money }}
-
-
-
-
-
- 当前状态
- {{ detail.status_name }}
-
-
- 交易号
- {{ detail.withdraw_no }}
-
-
- 手续费
- ¥{{ detail.withdraw_rate_money }}
-
-
- 申请时间
- {{ $util.timeStampTurnTime(detail.create_time) }}
-
-
- 审核时间
- {{ $util.timeStampTurnTime(detail.audit_time) }}
-
-
- 银行名称
- {{ detail.bank_name }}
-
-
- 收款账号
- {{ detail.account_number }}
-
-
- 拒绝理由
- {{ detail.refuse_reason }}
-
-
- 转账方式名称
- {{ detail.transfer_name }}
-
-
- 转账时间
- {{ $util.timeStampTurnTime(detail.payment_time) }}
-
-
-
-
-
-
-
-
-
-
+
+
+
+ -{{ detail.money }}
+
+
+
+
+
+ 当前状态
+ {{ detail.status_name }}
+
+
+ 交易号
+ {{ detail.withdraw_no }}
+
+
+ 手续费
+ ¥{{ detail.withdraw_rate_money }}
+
+
+ 申请时间
+ {{ $util.timeStampTurnTime(detail.create_time) }}
+
+
+ 审核时间
+ {{ $util.timeStampTurnTime(detail.audit_time) }}
+
+
+ 银行名称
+ {{ detail.bank_name }}
+
+
+ 收款账号
+ {{ detail.account_number }}
+
+
+ 拒绝理由
+ {{ detail.refuse_reason }}
+
+
+ 转账方式名称
+ {{ detail.transfer_name }}
+
+
+ 转账时间
+ {{ $util.timeStampTurnTime(detail.payment_time) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_promotion/merch/detail.vue b/pages_promotion/merch/detail.vue
index e45ec69..c0a454c 100644
--- a/pages_promotion/merch/detail.vue
+++ b/pages_promotion/merch/detail.vue
@@ -1,6 +1,5 @@
-
-
+
@@ -79,7 +78,7 @@
.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
- {{ $lang('Make') ? $lang('Make') : '咨询' }}
+ {{ $lang('Make') ? $lang('Make') : '询底价' }}
@@ -164,7 +163,7 @@
.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
- {{ $lang('Make') ? $lang('Make') : '咨询' }}
+ {{ $lang('Make') ? $lang('Make') : '询底价' }}
@@ -294,13 +293,27 @@
import uniDrawer from '@/components/uni-drawer/uni-drawer.vue';
import uniTag from '@/components/uni-tag/uni-tag.vue';
import nsGoodsSkuIndex from '@/components/ns-goods-sku/ns-goods-sku-index.vue';
+ import MescrollUni from "@/components/mescroll/my-list-mescroll.vue";
+ import nsLogin from '@/components/ns-login/ns-login.vue';
+ import loadingCover from '@/components/loading-cover/loading-cover.vue';
+ import nsEmpty from '@/components/ns-empty/ns-empty.vue';
+ // #ifdef MP-WEIXIN
+ import privacyPopup from '@/components/wx-privacy-popup/privacy-popup.vue';
+ // #endif
import list from './public/js/list.js';
export default {
components: {
uniDrawer,
uniTag,
- nsGoodsSkuIndex
+ nsGoodsSkuIndex,
+ MescrollUni,
+ nsLogin,
+ loadingCover,
+ nsEmpty,
+ // #ifdef MP-WEIXIN
+ privacyPopup
+ // #endif
},
data() {
return {};
diff --git a/pages_promotion/merch/merchcategory.vue b/pages_promotion/merch/merchcategory.vue
index ce7f83e..91180a8 100644
--- a/pages_promotion/merch/merchcategory.vue
+++ b/pages_promotion/merch/merchcategory.vue
@@ -1,6 +1,5 @@
-
-
+
@@ -116,11 +115,25 @@
-
-
+
+
+
+
+
+ {{ $lang('exchangeSuccess') }}
+
+ {{ $lang('see') }}
+ {{ $lang('goHome') }}
+
+
+
+
+
+
+
diff --git a/pages_tool/agreement/contenr.vue b/pages_tool/agreement/contenr.vue
index 41b7087..7dfcaa9 100644
--- a/pages_tool/agreement/contenr.vue
+++ b/pages_tool/agreement/contenr.vue
@@ -1,55 +1,54 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pages_tool/article/detail.vue b/pages_tool/article/detail.vue
index e86ed84..d2c7c25 100644
--- a/pages_tool/article/detail.vue
+++ b/pages_tool/article/detail.vue
@@ -1,6 +1,5 @@
-
-
+
{{ detail.article_title }}
发表时间: {{ $util.timeStampTurnTime(detail.create_time) }}
diff --git a/pages_tool/article/list.vue b/pages_tool/article/list.vue
index fbca850..bcbbb96 100644
--- a/pages_tool/article/list.vue
+++ b/pages_tool/article/list.vue
@@ -1,6 +1,5 @@
-
-
+
@@ -35,6 +34,14 @@
+
+
diff --git a/pages_tool/goods/brand.vue b/pages_tool/goods/brand.vue
index 33e5334..1865f85 100644
--- a/pages_tool/goods/brand.vue
+++ b/pages_tool/goods/brand.vue
@@ -27,11 +27,25 @@
import uniGrid from '@/components/uni-grid/uni-grid.vue';
import uniGridItem from '@/components/uni-grid-item/uni-grid-item.vue';
import nsAdv from '@/pages_tool/components/ns-adv/ns-adv.vue';
+ import MescrollUni from "@/components/mescroll/my-list-mescroll.vue";
+ import nsLogin from '@/components/ns-login/ns-login.vue';
+ import loadingCover from '@/components/loading-cover/loading-cover.vue';
+ import nsEmpty from '@/components/ns-empty/ns-empty.vue';
+ // #ifdef MP-WEIXIN
+ import privacyPopup from '@/components/wx-privacy-popup/privacy-popup.vue';
+ // #endif
export default {
components: {
uniGrid,
uniGridItem,
- nsAdv
+ nsAdv,
+ MescrollUni,
+ nsLogin,
+ loadingCover,
+ nsEmpty,
+ // #ifdef MP-WEIXIN
+ privacyPopup
+ // #endif
},
data() {
return {
diff --git a/pages_tool/goods/coupon.vue b/pages_tool/goods/coupon.vue
index 5405b52..2b332ba 100644
--- a/pages_tool/goods/coupon.vue
+++ b/pages_tool/goods/coupon.vue
@@ -1,6 +1,5 @@
-
-
+
全部
@@ -71,7 +70,23 @@
-
-
+
+
+
+
+ {{ item.name }}({{ item.count }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.member_name[0] }}***{{ item.member_name[item.member_name.length - 1] }}
+
+ {{ item.member_name }}
+ {{ $util.timeStampTurnTime(item.create_time)
+ }}
+
+
+
+
+
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+ 商家回复:
+ {{ item.explain_first }}
+
+
+
+ 追加评价
+ {{ item.again_content }}
+
+
+
+
+
+
+
+
+
+ 商家回复:
+ {{ item.again_explain }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_tool/goods/not_exist.vue b/pages_tool/goods/not_exist.vue
index 0dc8b4a..36a10bc 100644
--- a/pages_tool/goods/not_exist.vue
+++ b/pages_tool/goods/not_exist.vue
@@ -1,43 +1,42 @@
-
-
-
-
-
- 您查看的商品不存在,可能已下架或被删除
-
-
-
-
-
-
-
+
+
+
+
+ 您查看的商品不存在,可能已下架或被删除
+
+
+
+
+
+
+
diff --git a/pages_tool/goods/search.vue b/pages_tool/goods/search.vue
index df4eddf..1b50446 100644
--- a/pages_tool/goods/search.vue
+++ b/pages_tool/goods/search.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/pages_tool/help/detail.vue b/pages_tool/help/detail.vue
index 4eabe01..cd41c7f 100644
--- a/pages_tool/help/detail.vue
+++ b/pages_tool/help/detail.vue
@@ -1,6 +1,5 @@
-
-
+
{{ detail.title }}
diff --git a/pages_tool/help/list.vue b/pages_tool/help/list.vue
index 77efc6c..32f5f6b 100644
--- a/pages_tool/help/list.vue
+++ b/pages_tool/help/list.vue
@@ -1,6 +1,5 @@
-
-
+
{{ item.class_name }}
diff --git a/pages_tool/index/diy.vue b/pages_tool/index/diy.vue
index e2b0461..fe93e58 100644
--- a/pages_tool/index/diy.vue
+++ b/pages_tool/index/diy.vue
@@ -1,95 +1,104 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages_tool/login/find.vue b/pages_tool/login/find.vue
index ab04b00..bf8281f 100644
--- a/pages_tool/login/find.vue
+++ b/pages_tool/login/find.vue
@@ -1,6 +1,5 @@
-
-
+
\ No newline at end of file
diff --git a/pages_tool/member/account.vue b/pages_tool/member/account.vue
index 203d0fb..6fa4744 100644
--- a/pages_tool/member/account.vue
+++ b/pages_tool/member/account.vue
@@ -1,6 +1,5 @@
-
-
+
@@ -66,12 +65,20 @@
import MescrollUni from "@/components/mescroll/my-list-mescroll.vue";
import nsLogin from '@/components/ns-login/ns-login.vue';
import loadingCover from '@/components/loading-cover/loading-cover.vue';
+ import nsEmpty from '@/components/ns-empty/ns-empty.vue';
+ // #ifdef MP-WEIXIN
+ import privacyPopup from '@/components/wx-privacy-popup/privacy-popup.vue';
+ // #endif
export default {
components: {
MescrollUni,
nsLogin,
- loadingCover
+ loadingCover,
+ nsEmpty,
+ // #ifdef MP-WEIXIN
+ privacyPopup
+ // #endif
},
data() {
return {
diff --git a/pages_tool/member/account_edit.vue b/pages_tool/member/account_edit.vue
index 1ff5f5a..0225527 100644
--- a/pages_tool/member/account_edit.vue
+++ b/pages_tool/member/account_edit.vue
@@ -1,6 +1,5 @@
-
-
+
{{ $lang('name') }}
diff --git a/pages_tool/member/address.vue b/pages_tool/member/address.vue
index 3a18f70..055f213 100644
--- a/pages_tool/member/address.vue
+++ b/pages_tool/member/address.vue
@@ -1,6 +1,5 @@
-
-
+
@@ -11,7 +10,8 @@
{{ item.name }}
{{ item.mobile }}
- {{ item.local_data }}
+ {{
+ item.local_data }}
{{ item.full_address }}{{ item.address }}
@@ -34,14 +34,16 @@
-
+
设为默认地址
设为默认地址
-
+
-
+
@@ -51,16 +53,21 @@
-
+
暂无收货地址,请添加
-
+
-
+
-
+
@@ -74,7 +81,9 @@
-
+
\ No newline at end of file
diff --git a/pages_tool/member/address_edit.vue b/pages_tool/member/address_edit.vue
index 4cdfccf..d028016 100644
--- a/pages_tool/member/address_edit.vue
+++ b/pages_tool/member/address_edit.vue
@@ -1,6 +1,5 @@
-
-
+
地址信息
diff --git a/pages_tool/member/apply_withdrawal.vue b/pages_tool/member/apply_withdrawal.vue
index 97b0762..d258e4f 100644
--- a/pages_tool/member/apply_withdrawal.vue
+++ b/pages_tool/member/apply_withdrawal.vue
@@ -1,6 +1,5 @@
-
-
+
提现到
diff --git a/pages_tool/member/assets.vue b/pages_tool/member/assets.vue
index bf72e84..000b039 100644
--- a/pages_tool/member/assets.vue
+++ b/pages_tool/member/assets.vue
@@ -1,6 +1,5 @@
-
-
+
风险提示:确认申请后您的资产将被清空且不可找回!
diff --git a/pages_tool/member/balance.vue b/pages_tool/member/balance.vue
index 4304607..1976189 100644
--- a/pages_tool/member/balance.vue
+++ b/pages_tool/member/balance.vue
@@ -1,6 +1,5 @@
-
-
+
-
+
diff --git a/pages_tool/member/coupon.vue b/pages_tool/member/coupon.vue
index 5e7eaaa..660888b 100644
--- a/pages_tool/member/coupon.vue
+++ b/pages_tool/member/coupon.vue
@@ -1,6 +1,5 @@
-
-
+
未使用
@@ -75,12 +74,18 @@ import nsEmpty from '@/components/ns-empty/ns-empty.vue';
import nsLogin from '@/components/ns-login/ns-login.vue';
import loadingCover from '@/components/loading-cover/loading-cover.vue';
import MescrollUni from "@/components/mescroll/my-list-mescroll.vue";
+// #ifdef MP-WEIXIN
+import privacyPopup from '@/components/wx-privacy-popup/privacy-popup.vue';
+// #endif
export default {
components: {
nsEmpty,
nsLogin,
loadingCover,
- MescrollUni
+ MescrollUni,
+ // #ifdef MP-WEIXIN
+ privacyPopup
+ // #endif
},
data() {
return {
diff --git a/pages_tool/member/footprint.vue b/pages_tool/member/footprint.vue
index f0a2aa8..4cf5824 100644
--- a/pages_tool/member/footprint.vue
+++ b/pages_tool/member/footprint.vue
@@ -1,6 +1,5 @@
-
-
+
{{ manage ? '完成' : '管理' }}
@@ -69,13 +68,19 @@ import nsEmpty from '@/components/ns-empty/ns-empty.vue';
import nsLogin from '@/components/ns-login/ns-login.vue';
import loadingCover from '@/components/loading-cover/loading-cover.vue';
import MescrollUni from "@/components/mescroll/my-list-mescroll.vue";
+// #ifdef MP-WEIXIN
+import privacyPopup from '@/components/wx-privacy-popup/privacy-popup.vue';
+// #endif
export default {
components: {
nsEmpty,
nsLogin,
loadingCover,
- MescrollUni
+ MescrollUni,
+ // #ifdef MP-WEIXIN
+ privacyPopup
+ // #endif
},
data() {
return {
diff --git a/pages_tool/member/index.vue b/pages_tool/member/index.vue
index bd574b8..1cabd7f 100644
--- a/pages_tool/member/index.vue
+++ b/pages_tool/member/index.vue
@@ -1,65 +1,75 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -79,6 +89,52 @@ export default {
tourl(url) {
this.$util.redirectTo(url);
+ },
+
+ // 退出登录
+ logout() {
+ uni.showModal({
+ title: this.$lang('logoutTitle'),
+ content: this.$lang('logoutContent'),
+ success: (res) => {
+ if (res.confirm) {
+ this.$store.commit('setToken', '');
+ this.$store.commit('setMemberInfo', '');
+ this.$store.dispatch('emptyCart');
+ uni.removeStorageSync('authInfo');
+ this.$util.redirectTo('/pages/member/index');
+ }
+ }
+ });
+ },
+
+ // 账号注销
+ cancellation() {
+ uni.showModal({
+ title: this.$lang('cancellationTitle'),
+ content: this.$lang('cancellationContent'),
+ confirmColor: '#000000',
+ success: res => {
+ if (res.confirm) {
+ this.$api.sendRequest({
+ url: '/membercancel/api/membercancel/apply',
+ success: rres => {
+ if (rres.code >= 0) {
+ this.$store.commit('setToken', '');
+ this.$store.commit('setMemberInfo', '');
+ this.$store.dispatch('emptyCart');
+ uni.removeStorageSync('authInfo');
+ this.$util.redirectTo('/pages/member/index');
+ } else {
+ this.$util.showToast({
+ title: rres.message
+ });
+ }
+ }
+ });
+ }
+ }
+ });
}
}
@@ -100,6 +156,7 @@ export default {
color: #F4391c
}
}
+
\ No newline at end of file
diff --git a/pages_tool/member/info.vue b/pages_tool/member/info.vue
index 066494d..844b6ae 100644
--- a/pages_tool/member/info.vue
+++ b/pages_tool/member/info.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/pages_tool/member/info_edit.vue b/pages_tool/member/info_edit.vue
index bf3de74..6a64810 100644
--- a/pages_tool/member/info_edit.vue
+++ b/pages_tool/member/info_edit.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/pages_tool/member/invite_friends.vue b/pages_tool/member/invite_friends.vue
index 028bed5..8321783 100644
--- a/pages_tool/member/invite_friends.vue
+++ b/pages_tool/member/invite_friends.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/pages_tool/member/level.vue b/pages_tool/member/level.vue
index e70b07a..c7e6d1d 100644
--- a/pages_tool/member/level.vue
+++ b/pages_tool/member/level.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/pages_tool/member/level_growth_rules.vue b/pages_tool/member/level_growth_rules.vue
index 14bdadf..69a684e 100644
--- a/pages_tool/member/level_growth_rules.vue
+++ b/pages_tool/member/level_growth_rules.vue
@@ -1,275 +1,274 @@
-
-
-
-
-
-
- 成长值说明
-
-
-
-
- 等级
- 成长值
-
-
- {{ item.level_name }}
- {{ item.growth }}
-
-
-
-
-
-
- 什么是成长值
-
- 成长值是消费者在店铺成为会员后,通过消费计算出来的值。成长值决定会员等级,会员等级越高,所享受的会员权益和会员礼包就越多。
-
-
-
-
- 如何获得成长值
-
-
- 1、注册会员送x成长值。
- 2、会员充值到余额送x成长值。
- 3、会员签到送x成长值。
- 4、会员消费x元,交易完成即可获得x个成长值。
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pages_tool/member/modify_face.vue b/pages_tool/member/modify_face.vue
index 7a2f258..593b9e1 100644
--- a/pages_tool/member/modify_face.vue
+++ b/pages_tool/member/modify_face.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/pages_tool/member/pay_password.vue b/pages_tool/member/pay_password.vue
index 53989b9..67e2774 100644
--- a/pages_tool/member/pay_password.vue
+++ b/pages_tool/member/pay_password.vue
@@ -1,6 +1,5 @@
-
-
+
请输入6位支付密码,建议不要使用重复或连续数字
验证码已发送至{{ memberInfo.mobile | mobile }}请在下方输入4位数字验证码
diff --git a/pages_tool/member/point.vue b/pages_tool/member/point.vue
index 984809f..3c4ae9d 100644
--- a/pages_tool/member/point.vue
+++ b/pages_tool/member/point.vue
@@ -1,6 +1,5 @@
-
-
+
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages_tool/webview/webview.vue b/pages_tool/webview/webview.vue
index c12f31f..62f7d93 100644
--- a/pages_tool/webview/webview.vue
+++ b/pages_tool/webview/webview.vue
@@ -1,6 +1,5 @@
-
-
+
diff --git a/store/index.js b/store/index.js
index a148bb5..17b9fad 100644
--- a/store/index.js
+++ b/store/index.js
@@ -5,6 +5,7 @@ Vue.use(Vuex)
import Http from '../common/js/http.js'
import { themeConfig } from '../common/js/config-external.js'
import configExternal from '../common/js/config-external.js'
+import util from '../common/js/util.js'
const store = new Vuex.Store({
state: {
@@ -14,6 +15,7 @@ const store = new Vuex.Store({
tabBarList: '',
siteState: 1,
themeStyle: '',
+ themeColor: '',
addonIsExist: {
bundling: 0,
coupon: 0,
@@ -59,7 +61,7 @@ const store = new Vuex.Store({
cartNumber: 0,
cartMoney: 0,
cartChange: 0,
- wechatConfigStatus:0,
+ wechatConfigStatus: 0,
bottomNavHidden: false, // 底部导航是否隐藏,true:隐藏,false:显示
aiUnreadCount: 10, // AI未读消息数量
globalAIKefuConfig: null, // AI客服配置
@@ -80,8 +82,8 @@ const store = new Vuex.Store({
wap_valid_time: 0,
},
copyright: null,
- initStatus:false,
- offlineWhiteList:['pages_order/payment','pages_order/list','pages_order/detail'],//线下支付白名单
+ initStatus: false,
+ offlineWhiteList: ['pages_order/payment', 'pages_order/list', 'pages_order/detail'],//线下支付白名单
canReceiveRegistergiftInfo: {
status: false,
path: ''
@@ -227,15 +229,19 @@ const store = new Vuex.Store({
setCartMoney(state, value) {
state.cartMoney = value;
},
- setInitStatus(state,value){
+ setInitStatus(state, value) {
state.initStatus = value
},
- setWechatConfigStatus(state,value){
+ setWechatConfigStatus(state, value) {
state.wechatConfigStatus = value
- },
+ },
// 设置AI未读消息数量
setAiUnreadCount(state, value) {
state.aiUnreadCount = value;
+ },
+ // 设置主题颜色
+ setThemeColor(state, value) {
+ state.themeColor = value;
}
},
getters: {
@@ -252,7 +258,8 @@ const store = new Vuex.Store({
success: res => {
var data = res.data;
if (data) {
- this.commit('setThemeStyle', configExternal.loadThemeSync(data.style_theme.name));
+ this.commit('setThemeStyle', configExternal.loadThemeSync(data.style_theme?.name));
+ this.dispatch('themeColorSet');
// 底部导航
this.commit('setTabBarList', data.diy_bottom_nav);
@@ -260,16 +267,16 @@ const store = new Vuex.Store({
this.commit('setAddonIsExist', data.addon_is_exist);
this.commit('setDefaultImg', data.default_img);
-
+
this.commit('setSiteInfo', data.site_info);
this.commit('setServicerConfig', data.servicer);
-
+
// 企业微信配置
if (data?.wxwork_config) {
this.commit('setWxworkConfig', data.wxwork_config);
}
-
+
this.commit('setCopyright', data.copyright);
this.commit('setMapConfig', data.map_config);
@@ -277,11 +284,11 @@ const store = new Vuex.Store({
this.commit('setglobalAIKefuConfig', data.ai_agent_config);
this.commit('setGlobalStoreConfig', data.store_config);
-
+
//联系我们
-
+
this.commit('setShopInfo', data.shop_info);
- this.commit('setWechatConfigStatus',data?.wechat_config_status);
+ this.commit('setWechatConfigStatus', data?.wechat_config_status);
// 默认总店
if (data.store_info) {
this.commit('setDefaultStoreInfo', data.store_info);
@@ -290,7 +297,7 @@ const store = new Vuex.Store({
this.commit('setDefaultStoreInfo', null);
this.commit('setGlobalStoreInfo', null);
}
- this.commit('setInitStatus',true)
+ this.commit('setInitStatus', true)
resolve(data);
}
}
@@ -401,6 +408,34 @@ const store = new Vuex.Store({
this.commit('setCartIds', ids);
+ },
+ // 生成主题颜色CSS变量
+ themeColorSet() {
+ console.log('样式颜色设置...');
+ let theme = this.state.themeStyle;
+ if (!theme?.main_color || !theme?.aux_color) return;
+ try {
+ let themeColor = `--base-color:${theme.main_color};--base-help-color:${theme.aux_color};`;
+ if (this.state.tabBarHeight != '56px') themeColor += `--tab-bar-height:${this.state.tabBarHeight};`
+ Object.keys(theme).forEach(key => {
+ let data = theme[key];
+ if (typeof (data) == "object") {
+ Object.keys(data).forEach(k => {
+ themeColor += '--' + k.replace(/_/g, "-") + ':' + data[k] + ';';
+ });
+ } else if (typeof (key) == "string" && key) {
+ themeColor += '--' + key.replace(/_/g, "-") + ':' + data + ';';
+ }
+ });
+ for (let i = 9; i >= 5; i--) {
+ let color = util.colourBlend(theme.main_color, '#ffffff', (i / 10));
+ themeColor += `--base-color-light-${i}:${color};`;
+ }
+ this.commit('setThemeColor', themeColor);
+ } catch (e) {
+ console.error('设置主题颜色失败', e);
+ }
+ console.log('themeColor => ', this.state.themeColor);
}
}
})
diff --git a/uni_modules/mp-html/README.md b/uni_modules/mp-html/README.md
index 7626289..784800e 100644
--- a/uni_modules/mp-html/README.md
+++ b/uni_modules/mp-html/README.md
@@ -1,194 +1,259 @@
-## 为减小组件包的大小,默认组件包中不包含编辑、latex 公式等扩展功能,需要使用扩展功能的请参考下方的 插件扩展 栏的说明
-
-## 功能介绍
-- 全端支持(含 `v3、NVUE`)
-- 支持丰富的标签(包括 `table`、`video`、`svg` 等)
-- 支持丰富的事件效果(自动预览图片、链接处理等)
-- 支持设置占位图(加载中、出错时、预览时)
-- 支持锚点跳转、长按复制等丰富功能
-- 支持大部分 *html* 实体
-- 丰富的插件(关键词搜索、内容编辑、`latex` 公式等)
-- 效率高、容错性强且轻量化
-
-查看 [功能介绍](https://jin-yufeng.gitee.io/mp-html/#/overview/feature) 了解更多
-
-## 使用方法
-- `uni_modules` 方式
- 1. 点击右上角的 `使用 HBuilder X 导入插件` 按钮直接导入项目或点击 `下载插件 ZIP` 按钮下载插件包并解压到项目的 `uni_modules/mp-html` 目录下
- 2. 在需要使用页面的 `(n)vue` 文件中添加
- ```html
-
-
- ```
- ```javascript
- export default {
- data() {
- return {
- html: 'Hello World!
'
- }
- }
- }
- ```
- 3. 需要更新版本时在 `HBuilder X` 中右键 `uni_modules/mp-html` 目录选择 `从插件市场更新` 即可
-
-- 源码方式
- 1. 从 [github](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 或 [gitee](https://gitee.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 下载源码
- 插件市场的 **非 uni_modules 版本** 无法更新,不建议从插件市场获取
- 2. 在需要使用页面的 `(n)vue` 文件中添加
- ```html
-
- ```
- ```javascript
- import mpHtml from '@/components/mp-html/mp-html'
- export default {
- // HBuilderX 2.5.5+ 可以通过 easycom 自动引入
- components: {
- mpHtml
- },
- data() {
- return {
- html: 'Hello World!
'
- }
- }
- }
- ```
-
-- npm 方式
- 1. 在项目根目录下执行
- ```bash
- npm install mp-html
- ```
- 2. 在需要使用页面的 `(n)vue` 文件中添加
- ```html
-
- ```
- ```javascript
- import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html'
- export default {
- // 不可省略
- components: {
- mpHtml
- },
- data() {
- return {
- html: 'Hello World!
'
- }
- }
- }
- ```
- 3. 需要更新版本时执行以下命令即可
- ```bash
- npm update mp-html
- ```
-
- 使用 *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.gitee.io/mp-html/#/overview/quickstart) 了解更多
-
-## 组件属性
-
-| 属性 | 类型 | 默认值 | 说明 |
-|:---:|:---:|:---:|---|
-| container-style | String | | 容器的样式([2.1.0+](https://jin-yufeng.gitee.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.gitee.io/mp-html/#/basic/prop) 了解更多
-
-## 组件事件
-
-| 名称 | 触发时机 |
-|:---:|---|
-| load | dom 树加载完毕时 |
-| ready | 图片加载完毕时 |
-| error | 发生渲染错误时 |
-| imgtap | 图片被点击时 |
-| linktap | 链接被点击时 |
-| play | 音视频播放时 |
-
-查看 [事件](https://jin-yufeng.gitee.io/mp-html/#/basic/event) 了解更多
-
-## api
-组件实例上提供了一些 `api` 方法可供调用
-
-| 名称 | 作用 |
-|:---:|---|
-| in | 将锚点跳转的范围限定在一个 scroll-view 内 |
-| navigateTo | 锚点跳转 |
-| getText | 获取文本内容 |
-| getRect | 获取富文本内容的位置和大小 |
-| setContent | 设置富文本内容 |
-| imgList | 获取所有图片的数组 |
-| pauseMedia | 暂停播放音视频([2.2.2+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v222)) |
-| setPlaybackRate | 设置音视频播放速率([2.4.0+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v240)) |
-
-查看 [api](https://jin-yufeng.gitee.io/mp-html/#/advanced/api) 了解更多
-
-## 插件扩展
-除基本功能外,本组件还提供了丰富的扩展,可按照需要选用
-
-| 名称 | 作用 |
-|:---:|---|
-| audio | 音乐播放器 |
-| editable | 富文本 **编辑**([示例项目](https://mp-html.oss-cn-hangzhou.aliyuncs.com/editable.zip)) |
-| 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) |
-
-从插件市场导入的包中 **不含有** 扩展插件,使用插件需通过微信小程序 `富文本插件` 获取或参考以下方法进行打包:
-1. 获取完整组件包
- ```bash
- npm install mp-html
- ```
-2. 编辑 `tools/config.js` 中的 `plugins` 项,选择需要的插件
-3. 生成新的组件包
- 在 `node_modules/mp-html` 目录下执行
- ```bash
- npm install
- npm run build:uni-app
- ```
-4. 拷贝 `dist/uni-app` 中的内容到项目根目录
-
-查看 [插件](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin) 了解更多
-
-## 关于 nvue
-`nvue` 使用原生渲染,不支持部分 `css` 样式,为实现和 `html` 相同的效果,组件内部通过 `web-view` 进行渲染,性能上差于原生,根据 `weex` 官方建议,`web` 标签仅应用在非常规的降级场景。因此,如果通过原生的方式(如 `richtext`)能够满足需要,则不建议使用本组件,如果有较多的富文本内容,则可以直接使用 `vue` 页面
-由于渲染方式与其他端不同,有以下限制:
-1. 不支持 `lazy-load` 属性
-2. 视频不支持全屏播放
-3. 如果在 `flex-direction: row` 的容器中使用,需要给组件设置宽度或设置 `flex: 1` 占满剩余宽度
-
-纯 `nvue` 模式下,[此问题](https://ask.dcloud.net.cn/question/119678) 修复前,不支持通过 `uni_modules` 引入,需要本地引入(将 [dist/uni-app](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 中的内容拷贝到项目根目录下)
-
-## 立即体验
-
-
-## 问题反馈
-遇到问题时,请先查阅 [常见问题](https://jin-yufeng.gitee.io/mp-html/#/question/faq) 和 [issue](https://github.com/jin-yufeng/mp-html/issues) 中是否已有相同的问题
-可通过 [issue](https://github.com/jin-yufeng/mp-html/issues/new/choose) 、插件问答或发送邮件到 [mp_html@126.com](mailto:mp_html@126.com) 提问,不建议在评论区提问(不方便回复)
-提问请严格按照 [issue 模板](https://github.com/jin-yufeng/mp-html/issues/new/choose) ,描述清楚使用环境、`html` 内容或可复现的 `demo` 项目以及复现方式,对于 **描述不清**、**无法复现** 或重复的问题将不予回复
-
-欢迎加入 `QQ` 交流群:
-群1(已满):`699734691`
-群2(已满):`778239129`
-群3:`960265313`
-
-查看 [问题反馈](https://jin-yufeng.gitee.io/mp-html/#/question/feedback) 了解更多
+# mp-html
+
+> 一个强大的小程序富文本组件
+
+
+
+[](https://www.npmjs.com/package/mp-html)
+
+[](https://coveralls.io/github/jin-yufeng/mp-html?branch=master)
+
+[](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
+
+ ```
+ 5. 在需要使用页面的 `js` 文件中添加
+
+ ```javascript
+ Page({
+ onLoad () {
+ this.setData({
+ html: 'Hello World!
'
+ })
+ }
+ })
+ ```
+- 源码方式
+ 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
+
+
+
+
+
+
+ ```
+- `npm` 方式
+ 1. 在项目目录下安装组件包
+
+ ```bash
+ npm install mp-html
+ ```
+ 2. 在需要使用页面的 `vue` 文件中添加
+
+ ```vue
+
+
+
+
+
+
+ ```
+
+ 使用 `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 | 链接被点击时 |
+| play | 音视频播放时([2.3.0+](https://jin-yufeng.github.io/mp-html/#/changelog/changelog#v230)) |
+| pause | 音视频暂停时([2.5.2+](https://jin-yufeng.github.io/mp-html/#/changelog/changelog#v252)) |
+| fullscreenchange | 视频全屏变化时([2.5.2+](https://jin-yufeng.github.io/mp-html/#/changelog/changelog#v252)) |
+
+查看 [事件](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) 了解更多
+
+## 使用案例
+
+| [官方示例](https://github.com/jin-yufeng/mp-html-demo) | 欢喜商城 | 多么生活 | 食法查 | 微慕 | 科学复习 |
+|:---:|:---:|:---:|:---:|:---:|:---:|
+|  |  |  |  |  |  |
+
+| [程序员技术之旅](https://github.com/fendoudebb/z-blog-wx) | 典典博客 | 优秀笔记 | 同城共享书 | [技术源 share](https://github.com/wangsrGit119/mini-blog-halo) | 你的代码写的真棒 |
+|:---:|:---:|:---:|:---:|:---:|:---:|
+|  |  |  |  |  |  |
+
+| 谛否 | 小莫唐尼 | [模版演示](https://github.com/zhihuifanqiechaodan/miniprogram-template) | AI瓦力 | 豆流便签 | 前端八股通 |
+|:---:|:---:|:---:|:---:|:---:|:---:|
+|  |  |  |  |  |  |
+
+以上排名不分先后,更多可见 [使用案例收集](https://github.com/jin-yufeng/mp-html/issues/27)(欢迎添加)
+
+## 许可与支持
+- 许可
+ 您可以免费的使用(包括商用)、复制或修改本组件 [MIT License](https://github.com/jin-yufeng/mp-html/blob/master/LICENSE)
+ 在用于生产环境前务必经过充分测试,由插件 `bug` 带来的损失概不负责(可以自行修改源码)
+
+- 联系
+ 欢迎加入 `QQ` 交流群:
+ 群1(已满):`699734691`
+ 群2(已满):`778239129`
+ 群3:`960265313`
+ 
+
+- 支持
+ 
+
+## 更新日志
+- v2.5.2 (20251214)
+ 1. `A` 增加了音视频暂停 [pause](https://jin-yufeng.github.io/mp-html/#/basic/event?id=pause) 和视频全屏 [fullscreenchange](https://jin-yufeng.github.io/mp-html/#/basic/event?id=fullscreenchange) 事件 [#495](https://github.com/jin-yufeng/mp-html/issues/495) [#595](https://github.com/jin-yufeng/mp-html/issues/595)
+ 2. `U` 优化了 [流式输出](https://jin-yufeng.github.io/mp-html/#/overview/feature?id=stream) 效果,通过差量更新解决闪烁问题 [详细](https://github.com/jin-yufeng/mp-html/issues/657)
+ 3. `U` `latex` 插件更新字体文件 [详细](https://github.com/jin-yufeng/mp-html/pull/647) by [@JiuyeXD](https://github.com/JiuyeXD)
+ 4. `U` 更新 `markdown` 插件中 `marked.js` 版本 [详细](https://github.com/jin-yufeng/mp-html/issues/672)
+ 5. `U` 微信小程序替换遗漏的废弃 `api` `getSystemInfoSync` [详细](https://github.com/jin-yufeng/mp-html/pull/653) by [@zcSkr](https://github.com/zcSkr)
+ 6. `F` 修复了 `markdown` 插件加粗文本遇到中文符号无效的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/664) by [@qp666](https://github.com/qp666)
+
+- 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)
+
+ 从 `1.x` 的升级方法可见 [更新指南](https://jin-yufeng.github.io/mp-html/#/changelog/changelog?id=v200)
+
+查看 [更新日志](https://jin-yufeng.github.io/mp-html/#/changelog/changelog) 了解更多
diff --git a/uni_modules/mp-html/components/mp-html/mp-html.vue b/uni_modules/mp-html/components/mp-html/mp-html.vue
index 5d1d36d..ec6d7f5 100644
--- a/uni_modules/mp-html/components/mp-html/mp-html.vue
+++ b/uni_modules/mp-html/components/mp-html/mp-html.vue
@@ -1,498 +1,500 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/mp-html/components/mp-html/node/node.vue b/uni_modules/mp-html/components/mp-html/node/node.vue
index 09252fd..4733445 100644
--- a/uni_modules/mp-html/components/mp-html/node/node.vue
+++ b/uni_modules/mp-html/components/mp-html/node/node.vue
@@ -1,587 +1,624 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{n.text}}
-
-
- {{n.text}}
-
- \n
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/uni_modules/mp-html/components/mp-html/parser.js b/uni_modules/mp-html/components/mp-html/parser.js
index 5a93000..574f6b7 100644
--- a/uni_modules/mp-html/components/mp-html/parser.js
+++ b/uni_modules/mp-html/components/mp-html/parser.js
@@ -1,1393 +1,1400 @@
-/**
- * @fileoverview html 解析器
- */
-
-// 配置
-const config = {
- // 信任的标签(保持标签名不变)
- trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
-
- // 块级标签(转为 div,其他的非信任标签转为 span)
- blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
-
- // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
- // 行内标签
- inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
- // #endif
-
- // 要移除的标签
- ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
-
- // 自闭合的标签
- voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
-
- // html 实体
- entities: {
- lt: '<',
- gt: '>',
- quot: '"',
- apos: "'",
- ensp: '\u2002',
- emsp: '\u2003',
- nbsp: '\xA0',
- semi: ';',
- ndash: '–',
- mdash: '—',
- middot: '·',
- lsquo: '‘',
- rsquo: '’',
- ldquo: '“',
- rdquo: '”',
- bull: '•',
- hellip: '…',
- larr: '←',
- uarr: '↑',
- rarr: '→',
- darr: '↓'
- },
-
- // 默认的标签样式
- tagStyle: {
- // #ifndef APP-PLUS-NVUE
- address: 'font-style:italic',
- big: 'display:inline;font-size:1.2em',
- caption: 'display:table-caption;text-align:center',
- center: 'text-align:center',
- cite: 'font-style:italic',
- dd: 'margin-left:40px',
- mark: 'background-color:yellow',
- pre: 'font-family:monospace;white-space:pre',
- s: 'text-decoration:line-through',
- small: 'display:inline;font-size:0.8em',
- strike: 'text-decoration:line-through',
- u: 'text-decoration:underline'
- // #endif
- },
-
- // svg 大小写对照表
- svgDict: {
- animatetransform: 'animateTransform',
- lineargradient: 'linearGradient',
- viewbox: 'viewBox',
- attributename: 'attributeName',
- repeatcount: 'repeatCount',
- repeatdur: 'repeatDur',
- foreignobject: 'foreignObject'
- }
-}
-const tagSelector={}
-const {
- windowWidth,
- // #ifdef MP-WEIXIN
- system
- // #endif
-} = uni.getSystemInfoSync()
-const blankChar = makeMap(' ,\r,\n,\t,\f')
-let idIndex = 0
-
-// #ifdef H5 || APP-PLUS
-config.ignoreTags.iframe = undefined
-config.trustTags.iframe = true
-config.ignoreTags.embed = undefined
-config.trustTags.embed = true
-// #endif
-// #ifdef APP-PLUS-NVUE
-config.ignoreTags.source = undefined
-config.ignoreTags.style = undefined
-// #endif
-
-/**
- * @description 创建 map
- * @param {String} str 逗号分隔
- */
-function makeMap (str) {
- const map = Object.create(null)
- const list = str.split(',')
- for (let i = list.length; i--;) {
- map[list[i]] = true
- }
- return map
-}
-
-/**
- * @description 解码 html 实体
- * @param {String} str 要解码的字符串
- * @param {Boolean} amp 要不要解码 &
- * @returns {String} 解码后的字符串
- */
-function decodeEntity (str, amp) {
- let i = str.indexOf('&')
- while (i !== -1) {
- const j = str.indexOf(';', i + 3)
- let code
- if (j === -1) break
- if (str[i + 1] === '#') {
- // { 形式的实体
- code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
- if (!isNaN(code)) {
- str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
- }
- } else {
- // 形式的实体
- code = str.substring(i + 1, j)
- if (config.entities[code] || (code === 'amp' && amp)) {
- str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
- }
- }
- i = str.indexOf('&', i + 1)
- }
- return str
-}
-
-/**
- * @description 合并多个块级标签,加快长内容渲染
- * @param {Array} nodes 要合并的标签数组
- */
-function mergeNodes (nodes) {
- let i = nodes.length - 1
- for (let j = i; j >= -1; j--) {
- if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
- if (i - j >= 5) {
- nodes.splice(j + 1, i - j, {
- name: 'div',
- attrs: {},
- children: nodes.slice(j + 1, i + 1)
- })
- }
- i = j - 1
- }
- }
-}
-
-/**
- * @description html 解析器
- * @param {Object} vm 组件实例
- */
-function Parser (vm) {
- this.options = vm || {}
- this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
- this.imgList = vm.imgList || []
- this.imgList._unloadimgs = 0
- this.plugins = vm.plugins || []
- this.attrs = Object.create(null)
- this.stack = []
- this.nodes = []
- this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
-}
-
-/**
- * @description 执行解析
- * @param {String} content 要解析的文本
- */
-Parser.prototype.parse = function (content) {
- // 插件处理
- for (let i = this.plugins.length; i--;) {
- if (this.plugins[i].onUpdate) {
- content = this.plugins[i].onUpdate(content, config) || content
- }
- }
-
- new Lexer(this).parse(content)
- // 出栈未闭合的标签
- while (this.stack.length) {
- this.popNode()
- }
- if (this.nodes.length > 50) {
- mergeNodes(this.nodes)
- }
- return this.nodes
-}
-
-/**
- * @description 将标签暴露出来(不被 rich-text 包含)
- */
-Parser.prototype.expose = function () {
- // #ifndef APP-PLUS-NVUE
- for (let i = this.stack.length; i--;) {
- const item = this.stack[i]
- if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
- item.c = 1
- }
- // #endif
-}
-
-/**
- * @description 处理插件
- * @param {Object} node 要处理的标签
- * @returns {Boolean} 是否要移除此标签
- */
-Parser.prototype.hook = function (node) {
- for (let i = this.plugins.length; i--;) {
- if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
- return false
- }
- }
- return true
-}
-
-/**
- * @description 将链接拼接上主域名
- * @param {String} url 需要拼接的链接
- * @returns {String} 拼接后的链接
- */
-Parser.prototype.getUrl = function (url) {
- const domain = this.options.domain
- if (url[0] === '/') {
- if (url[1] === '/') {
- // // 开头的补充协议名
- url = (domain ? domain.split('://')[0] : 'http') + ':' + url
- } else if (domain) {
- // 否则补充整个域名
- url = domain + url
- } /* #ifdef APP-PLUS */ else {
- url = plus.io.convertLocalFileSystemURL(url)
- } /* #endif */
- } else if (!url.includes('data:') && !url.includes('://')) {
- if (domain) {
- url = domain + '/' + url
- } /* #ifdef APP-PLUS */ else {
- url = plus.io.convertLocalFileSystemURL(url)
- } /* #endif */
- }
- return url
-}
-
-/**
- * @description 解析样式表
- * @param {Object} node 标签
- * @returns {Object}
- */
-Parser.prototype.parseStyle = function (node) {
- const attrs = node.attrs
- const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
- const styleObj = {}
- let tmp = ''
-
- if (attrs.id && !this.xml) {
- // 暴露锚点
- if (this.options.useAnchor) {
- this.expose()
- } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
- attrs.id = undefined
- }
- }
-
- // 转换 width 和 height 属性
- if (attrs.width) {
- styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
- attrs.width = undefined
- }
- if (attrs.height) {
- styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
- attrs.height = undefined
- }
-
- for (let i = 0, len = list.length; i < len; i++) {
- const info = list[i].split(':')
- if (info.length < 2) continue
- const key = info.shift().trim().toLowerCase()
- let value = info.join(':').trim()
- if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
- // 兼容性的 css 不压缩
- tmp += `;${key}:${value}`
- } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
- // 重复的样式进行覆盖
- if (value.includes('url')) {
- // 填充链接
- let j = value.indexOf('(') + 1
- if (j) {
- while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
- j++
- }
- value = value.substr(0, j) + this.getUrl(value.substr(j))
- }
- } else if (value.includes('rpx')) {
- // 转换 rpx(rich-text 内部不支持 rpx)
- value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
- }
- styleObj[key] = value
- }
- }
-
- node.attrs.style = tmp
- return styleObj
-}
-
-/**
- * @description 解析到标签名
- * @param {String} name 标签名
- * @private
- */
-Parser.prototype.onTagName = function (name) {
- this.tagName = this.xml ? name : name.toLowerCase()
- if (this.tagName === 'svg') {
- this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
- config.ignoreTags.style = undefined // svg 标签内 style 可用
- }
-}
-
-/**
- * @description 解析到属性名
- * @param {String} name 属性名
- * @private
- */
-Parser.prototype.onAttrName = function (name) {
- name = this.xml ? name : name.toLowerCase()
- // #ifdef (VUE3 && (H5 || APP-PLUS)) || APP-PLUS-NVUE
- if (name.includes('?') || name.includes(';')) {
- this.attrName = undefined
- return
- }
- // #endif
- if (name.substr(0, 5) === 'data-') {
- if (name === 'data-src' && !this.attrs.src) {
- // data-src 自动转为 src
- this.attrName = 'src'
- } else if (this.tagName === 'img' || this.tagName === 'a') {
- // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
- this.attrName = name
- } else {
- // 剩余的移除以减小大小
- this.attrName = undefined
- }
- } else {
- this.attrName = name
- this.attrs[name] = 'T' // boolean 型属性缺省设置
- }
-}
-
-/**
- * @description 解析到属性值
- * @param {String} val 属性值
- * @private
- */
-Parser.prototype.onAttrVal = function (val) {
- const name = this.attrName || ''
- if (name === 'style' || name === 'href') {
- // 部分属性进行实体解码
- this.attrs[name] = decodeEntity(val, true)
- } else if (name.includes('src')) {
- // 拼接主域名
- this.attrs[name] = this.getUrl(decodeEntity(val, true))
- } else if (name) {
- this.attrs[name] = val
- }
-}
-
-/**
- * @description 解析到标签开始
- * @param {Boolean} selfClose 是否有自闭合标识 />
- * @private
- */
-Parser.prototype.onOpenTag = function (selfClose) {
- // 拼装 node
- const node = Object.create(null)
- node.name = this.tagName
- node.attrs = this.attrs
- // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
- if (this.options.nodes.length) {
- node.type = 'node'
- }
- this.attrs = Object.create(null)
-
- const attrs = node.attrs
- const parent = this.stack[this.stack.length - 1]
- const siblings = parent ? parent.children : this.nodes
- const close = this.xml ? selfClose : config.voidTags[node.name]
-
- // 替换标签名选择器
- if (tagSelector[node.name]) {
- attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
- }
-
- // 转换 embed 标签
- if (node.name === 'embed') {
- // #ifndef H5 || APP-PLUS
- const src = attrs.src || ''
- // 按照后缀名和 type 将 embed 转为 video 或 audio
- if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
- node.name = 'video'
- } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
- node.name = 'audio'
- }
- if (attrs.autostart) {
- attrs.autoplay = 'T'
- }
- attrs.controls = 'T'
- // #endif
- // #ifdef H5 || APP-PLUS
- this.expose()
- // #endif
- }
-
- // #ifndef APP-PLUS-NVUE
- // 处理音视频
- if (node.name === 'video' || node.name === 'audio') {
- // 设置 id 以便获取 context
- if (node.name === 'video' && !attrs.id) {
- attrs.id = 'v' + idIndex++
- }
- // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
- if (!attrs.controls && !attrs.autoplay) {
- attrs.controls = 'T'
- }
- // 用数组存储所有可用的 source
- node.src = []
- if (attrs.src) {
- node.src.push(attrs.src)
- attrs.src = undefined
- }
- this.expose()
- }
- // #endif
-
- // 处理自闭合标签
- if (close) {
- if (!this.hook(node) || config.ignoreTags[node.name]) {
- // 通过 base 标签设置主域名
- if (node.name === 'base' && !this.options.domain) {
- this.options.domain = attrs.href
- } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
- // 设置 source 标签(仅父节点为 video 或 audio 时有效)
- parent.src.push(attrs.src)
- } /* #endif */
- return
- }
-
- // 解析 style
- const styleObj = this.parseStyle(node)
-
- // 处理图片
- if (node.name === 'img') {
- if (attrs.src) {
- // 标记 webp
- if (attrs.src.includes('webp')) {
- node.webp = 'T'
- }
- // data url 图片如果没有设置 original-src 默认为不可预览的小图片
- if (attrs.src.includes('data:') && this.options.previewImg !== 'all' && !attrs['original-src']) {
- attrs.ignore = 'T'
- }
- if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
- for (let i = this.stack.length; i--;) {
- const item = this.stack[i]
- if (item.name === 'a') {
- node.a = item.attrs
- }
- if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
- if (!styleObj.display || styleObj.display.includes('inline')) {
- node.t = 'inline-block'
- } else {
- node.t = styleObj.display
- }
- styleObj.display = undefined
- }
- // #ifndef H5 || APP-PLUS
- const style = item.attrs.style || ''
- if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
- styleObj.width = '100% !important'
- styleObj.height = ''
- for (let j = i + 1; j < this.stack.length; j++) {
- this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
- }
- } else if (style.includes('flex') && styleObj.width === '100%') {
- for (let j = i + 1; j < this.stack.length; j++) {
- const style = this.stack[j].attrs.style || ''
- if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
- styleObj.width = ''
- break
- }
- }
- } else if (style.includes('inline-block')) {
- if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
- item.attrs.style += ';max-width:' + styleObj.width
- styleObj.width = ''
- } else {
- item.attrs.style += ';max-width:100%'
- }
- }
- // #endif
- item.c = 1
- }
- attrs.i = this.imgList.length.toString()
- let src = attrs['original-src'] || attrs.src
- // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
- if (this.imgList.includes(src)) {
- // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
- let i = src.indexOf('://')
- if (i !== -1) {
- i += 3
- let newSrc = src.substr(0, i)
- for (; i < src.length; i++) {
- if (src[i] === '/') break
- newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
- }
- newSrc += src.substr(i)
- src = newSrc
- }
- }
- // #endif
- this.imgList.push(src)
- if (!node.t) {
- this.imgList._unloadimgs += 1
- }
- // #ifdef H5 || APP-PLUS
- if (this.options.lazyLoad) {
- attrs['data-src'] = attrs.src
- attrs.src = undefined
- }
- // #endif
- }
- }
- if (styleObj.display === 'inline') {
- styleObj.display = ''
- }
- // #ifndef APP-PLUS-NVUE
- if (attrs.ignore) {
- styleObj['max-width'] = styleObj['max-width'] || '100%'
- attrs.style += ';-webkit-touch-callout:none'
- }
- // #endif
- // 设置的宽度超出屏幕,为避免变形,高度转为自动
- if (parseInt(styleObj.width) > windowWidth) {
- styleObj.height = undefined
- }
- // 记录是否设置了宽高
- if (!isNaN(parseInt(styleObj.width))) {
- node.w = 'T'
- }
- if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
- node.h = 'T'
- }
- if (node.w && node.h && styleObj['object-fit']) {
- if (styleObj['object-fit'] === 'contain') {
- node.m = 'aspectFit'
- } else if (styleObj['object-fit'] === 'cover') {
- node.m = 'aspectFill'
- }
- }
- } else if (node.name === 'svg') {
- siblings.push(node)
- this.stack.push(node)
- this.popNode()
- return
- }
- for (const key in styleObj) {
- if (styleObj[key]) {
- attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
- }
- }
- attrs.style = attrs.style.substr(1) || undefined
- // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
- if (!attrs.style) {
- delete attrs.style
- }
- // #endif
- } else {
- if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
- this.pre = node.pre = 1
- }
- node.children = []
- this.stack.push(node)
- }
-
- // 加入节点树
- siblings.push(node)
-}
-
-/**
- * @description 解析到标签结束
- * @param {String} name 标签名
- * @private
- */
-Parser.prototype.onCloseTag = function (name) {
- // 依次出栈到匹配为止
- name = this.xml ? name : name.toLowerCase()
- let i
- for (i = this.stack.length; i--;) {
- if (this.stack[i].name === name) break
- }
- if (i !== -1) {
- while (this.stack.length > i) {
- this.popNode()
- }
- } else if (name === 'p' || name === 'br') {
- const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
- siblings.push({
- name,
- attrs: {
- class: tagSelector[name] || '',
- style: this.tagStyle[name] || ''
- }
- })
- }
-}
-
-/**
- * @description 处理标签出栈
- * @private
- */
-Parser.prototype.popNode = function () {
- const node = this.stack.pop()
- let attrs = node.attrs
- const children = node.children
- const parent = this.stack[this.stack.length - 1]
- const siblings = parent ? parent.children : this.nodes
-
- if (!this.hook(node) || config.ignoreTags[node.name]) {
- // 获取标题
- if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
- uni.setNavigationBarTitle({
- title: children[0].text
- })
- }
- siblings.pop()
- return
- }
-
- if (node.pre && this.pre !== 2) {
- // 是否合并空白符标识
- this.pre = node.pre = undefined
- for (let i = this.stack.length; i--;) {
- if (this.stack[i].pre) {
- this.pre = 1
- }
- }
- }
-
- const styleObj = {}
-
- // 转换 svg
- if (node.name === 'svg') {
- if (this.xml > 1) {
- // 多层 svg 嵌套
- this.xml--
- return
- }
- // #ifdef APP-PLUS-NVUE
- (function traversal (node) {
- if (node.name) {
- // 调整 svg 的大小写
- node.name = config.svgDict[node.name] || node.name
- for (const item in node.attrs) {
- if (config.svgDict[item]) {
- node.attrs[config.svgDict[item]] = node.attrs[item]
- node.attrs[item] = undefined
- }
- }
- for (let i = 0; i < (node.children || []).length; i++) {
- traversal(node.children[i])
- }
- }
- })(node)
- // #endif
- // #ifndef APP-PLUS-NVUE
- let src = ''
- const style = attrs.style
- attrs.style = ''
- attrs.xmlns = 'http://www.w3.org/2000/svg';
- (function traversal (node) {
- if (node.type === 'text') {
- src += node.text
- return
- }
- const name = config.svgDict[node.name] || node.name
- if (name === 'foreignObject') {
- for (const child of (node.children || [])) {
- if (child.attrs && !child.attrs.xmlns) {
- child.attrs.xmlns = 'http://www.w3.org/1999/xhtml'
- break
- }
- }
- }
- src += '<' + name
- for (const item in node.attrs) {
- const val = node.attrs[item]
- if (val) {
- src += ` ${config.svgDict[item] || item}="${val.replace(/"/g, '')}"`
- }
- }
- if (!node.children) {
- src += '/>'
- } else {
- src += '>'
- for (let i = 0; i < node.children.length; i++) {
- traversal(node.children[i])
- }
- src += '' + name + '>'
- }
- })(node)
- node.name = 'img'
- node.attrs = {
- src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
- style,
- ignore: 'T'
- }
- node.children = undefined
- // #endif
- this.xml = false
- config.ignoreTags.style = true
- return
- }
-
- // #ifndef APP-PLUS-NVUE
- // 转换 align 属性
- if (attrs.align) {
- if (node.name === 'table') {
- if (attrs.align === 'center') {
- styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
- } else {
- styleObj.float = attrs.align
- }
- } else {
- styleObj['text-align'] = attrs.align
- }
- attrs.align = undefined
- }
-
- // 转换 dir 属性
- if (attrs.dir) {
- styleObj.direction = attrs.dir
- attrs.dir = undefined
- }
-
- // 转换 font 标签的属性
- if (node.name === 'font') {
- if (attrs.color) {
- styleObj.color = attrs.color
- attrs.color = undefined
- }
- if (attrs.face) {
- styleObj['font-family'] = attrs.face
- attrs.face = undefined
- }
- if (attrs.size) {
- let size = parseInt(attrs.size)
- if (!isNaN(size)) {
- if (size < 1) {
- size = 1
- } else if (size > 7) {
- size = 7
- }
- styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
- }
- attrs.size = undefined
- }
- }
- // #endif
-
- // 一些编辑器的自带 class
- if ((attrs.class || '').includes('align-center')) {
- styleObj['text-align'] = 'center'
- }
-
- Object.assign(styleObj, this.parseStyle(node))
-
- if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
- styleObj['max-width'] = '100%'
- styleObj['box-sizing'] = 'border-box'
- }
-
- // #ifndef APP-PLUS-NVUE
- if (config.blockTags[node.name]) {
- node.name = 'div'
- } else if (!config.trustTags[node.name] && !this.xml) {
- // 未知标签转为 span,避免无法显示
- node.name = 'span'
- }
-
- if (node.name === 'a' || node.name === 'ad'
- // #ifdef H5 || APP-PLUS
- || node.name === 'iframe' // eslint-disable-line
- // #endif
- ) {
- this.expose()
- } else if (node.name === 'video') {
- if ((styleObj.height || '').includes('auto')) {
- styleObj.height = undefined
- }
- /* #ifdef APP-PLUS */
- let str = ''
- node.html = str
- /* #endif */
- } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
- // 列表处理
- const types = {
- a: 'lower-alpha',
- A: 'upper-alpha',
- i: 'lower-roman',
- I: 'upper-roman'
- }
- if (types[attrs.type]) {
- attrs.style += ';list-style-type:' + types[attrs.type]
- attrs.type = undefined
- }
- for (let i = children.length; i--;) {
- if (children[i].name === 'li') {
- children[i].c = 1
- }
- }
- } else if (node.name === 'table') {
- // 表格处理
- // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
- let padding = parseFloat(attrs.cellpadding)
- let spacing = parseFloat(attrs.cellspacing)
- const border = parseFloat(attrs.border)
- const bordercolor = styleObj['border-color']
- const borderstyle = styleObj['border-style']
- if (node.c) {
- // padding 和 spacing 默认 2
- if (isNaN(padding)) {
- padding = 2
- }
- if (isNaN(spacing)) {
- spacing = 2
- }
- }
- if (border) {
- attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
- }
- if (node.flag && node.c) {
- // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
- styleObj.display = 'grid'
- if (styleObj['border-collapse'] === 'collapse') {
- styleObj['border-collapse'] = undefined
- spacing = 0
- }
- if (spacing) {
- styleObj['grid-gap'] = spacing + 'px'
- styleObj.padding = spacing + 'px'
- } else if (border) {
- // 无间隔的情况下避免边框重叠
- attrs.style += ';border-left:0;border-top:0'
- }
-
- const width = [] // 表格的列宽
- const trList = [] // tr 列表
- const cells = [] // 保存新的单元格
- const map = {}; // 被合并单元格占用的格子
-
- (function traversal (nodes) {
- for (let i = 0; i < nodes.length; i++) {
- if (nodes[i].name === 'tr') {
- trList.push(nodes[i])
- } else if (nodes[i].name === 'colgroup') {
- let colI = 1
- for (const col of (nodes[i].children || [])) {
- if (col.name === 'col') {
- const style = col.attrs.style || ''
- const start = style.indexOf('width') ? style.indexOf(';width') : 0
- // 提取出宽度
- if (start !== -1) {
- let end = style.indexOf(';', start + 6)
- if (end === -1) {
- end = style.length
- }
- width[colI] = style.substring(start ? start + 7 : 6, end)
- }
- colI += 1
- }
- }
- } else {
- traversal(nodes[i].children || [])
- }
- }
- })(children)
-
- for (let row = 1; row <= trList.length; row++) {
- let col = 1
- for (let j = 0; j < trList[row - 1].children.length; j++) {
- const td = trList[row - 1].children[j]
- if (td.name === 'td' || td.name === 'th') {
- // 这个格子被上面的单元格占用,则列号++
- while (map[row + '.' + col]) {
- col++
- }
- let style = td.attrs.style || ''
- let start = style.indexOf('width') ? style.indexOf(';width') : 0
- // 提取出 td 的宽度
- if (start !== -1) {
- let end = style.indexOf(';', start + 6)
- if (end === -1) {
- end = style.length
- }
- if (!td.attrs.colspan) {
- width[col] = style.substring(start ? start + 7 : 6, end)
- }
- style = style.substr(0, start) + style.substr(end)
- }
- // 设置竖直对齐
- style += ';display:flex'
- start = style.indexOf('vertical-align')
- if (start !== -1) {
- const val = style.substr(start + 15, 10)
- if (val.includes('middle')) {
- style += ';align-items:center'
- } else if (val.includes('bottom')) {
- style += ';align-items:flex-end'
- }
- } else {
- style += ';align-items:center'
- }
- // 设置水平对齐
- start = style.indexOf('text-align')
- if (start !== -1) {
- const val = style.substr(start + 11, 10)
- if (val.includes('center')) {
- style += ';justify-content: center'
- } else if (val.includes('right')) {
- style += ';justify-content: right'
- }
- }
- style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
- // 处理列合并
- if (td.attrs.colspan) {
- style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
- if (!td.attrs.rowspan) {
- style += `;grid-row-start:${row};grid-row-end:${row + 1}`
- }
- col += parseInt(td.attrs.colspan) - 1
- }
- // 处理行合并
- if (td.attrs.rowspan) {
- style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
- if (!td.attrs.colspan) {
- style += `;grid-column-start:${col};grid-column-end:${col + 1}`
- }
- // 记录下方单元格被占用
- for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
- for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
- map[(row + rowspan) + '.' + (col - colspan)] = 1
- }
- }
- }
- if (style) {
- td.attrs.style = style
- }
- cells.push(td)
- col++
- }
- }
- if (row === 1) {
- let temp = ''
- for (let i = 1; i < col; i++) {
- temp += (width[i] ? width[i] : 'auto') + ' '
- }
- styleObj['grid-template-columns'] = temp
- }
- }
- node.children = cells
- } else {
- // 没有使用合并单元格的表格通过 table 布局实现
- if (node.c) {
- styleObj.display = 'table'
- }
- if (!isNaN(spacing)) {
- styleObj['border-spacing'] = spacing + 'px'
- }
- if (border || padding) {
- // 遍历
- (function traversal (nodes) {
- for (let i = 0; i < nodes.length; i++) {
- const td = nodes[i]
- if (td.name === 'th' || td.name === 'td') {
- if (border) {
- td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
- }
- if (padding) {
- td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
- }
- } else if (td.children) {
- traversal(td.children)
- }
- }
- })(children)
- }
- }
- // 给表格添加一个单独的横向滚动层
- if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
- const table = Object.assign({}, node)
- node.name = 'div'
- node.attrs = {
- style: 'overflow:auto'
- }
- node.children = [table]
- attrs = table.attrs
- }
- } else if ((node.name === 'tbody' || node.name === 'tr') && node.flag && node.c) {
- node.flag = undefined;
- (function traversal (nodes) {
- for (let i = 0; i < nodes.length; i++) {
- if (nodes[i].name === 'td') {
- // 颜色样式设置给单元格避免丢失
- for (const style of ['color', 'background', 'background-color']) {
- if (styleObj[style]) {
- nodes[i].attrs.style = style + ':' + styleObj[style] + ';' + (nodes[i].attrs.style || '')
- }
- }
- } else {
- traversal(nodes[i].children || [])
- }
- }
- })(children)
- } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
- for (let i = this.stack.length; i--;) {
- if (this.stack[i].name === 'table' || this.stack[i].name === 'tbody' || this.stack[i].name === 'tr') {
- this.stack[i].flag = 1 // 指示含有合并单元格
- }
- }
- } else if (node.name === 'ruby') {
- // 转换 ruby
- node.name = 'span'
- for (let i = 0; i < children.length - 1; i++) {
- if (children[i].type === 'text' && children[i + 1].name === 'rt') {
- children[i] = {
- name: 'div',
- attrs: {
- style: 'display:inline-block;text-align:center'
- },
- children: [{
- name: 'div',
- attrs: {
- style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
- },
- children: children[i + 1].children
- }, children[i]]
- }
- children.splice(i + 1, 1)
- }
- }
- } else if (node.c) {
- (function traversal (node) {
- node.c = 2
- for (let i = node.children.length; i--;) {
- const child = node.children[i]
- // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
- if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
- traversal(child)
- }
- // #endif
- if (!child.c || child.name === 'table') {
- node.c = 1
- }
- }
- })(node)
- }
-
- if ((styleObj.display || '').includes('flex') && !node.c) {
- for (let i = children.length; i--;) {
- const item = children[i]
- if (item.f) {
- item.attrs.style = (item.attrs.style || '') + item.f
- item.f = undefined
- }
- }
- }
- // flex 布局时部分样式需要提取到 rich-text 外层
- const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
- // #ifdef MP-WEIXIN
- // 检查基础库版本 virtualHost 是否可用
- && !(node.c && wx.getNFCAdapter) // eslint-disable-line
- // #endif
- // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
- && !node.c // eslint-disable-line
- // #endif
- if (flex) {
- node.f = ';max-width:100%'
- }
-
- if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
- mergeNodes(children)
- }
- // #endif
-
- for (const key in styleObj) {
- if (styleObj[key]) {
- const val = `;${key}:${styleObj[key].replace(' !important', '')}`
- /* #ifndef APP-PLUS-NVUE */
- if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
- node.f += val
- if (key === 'width') {
- attrs.style += ';width:100%'
- }
- } else /* #endif */ {
- attrs.style += val
- }
- }
- }
- attrs.style = attrs.style.substr(1) || undefined
- // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
- for (const key in attrs) {
- if (!attrs[key]) {
- delete attrs[key]
- }
- }
- // #endif
-}
-
-/**
- * @description 解析到文本
- * @param {String} text 文本内容
- */
-Parser.prototype.onText = function (text) {
- if (!this.pre) {
- // 合并空白符
- let trim = ''
- let flag
- for (let i = 0, len = text.length; i < len; i++) {
- if (!blankChar[text[i]]) {
- trim += text[i]
- } else {
- if (trim[trim.length - 1] !== ' ') {
- trim += ' '
- }
- if (text[i] === '\n' && !flag) {
- flag = true
- }
- }
- }
- // 去除含有换行符的空串
- if (trim === ' ') {
- if (flag) return
- // #ifdef VUE3
- else {
- const parent = this.stack[this.stack.length - 1]
- if (parent && parent.name[0] === 't') return
- }
- // #endif
- }
- text = trim
- }
- const node = Object.create(null)
- node.type = 'text'
- // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
- node.attrs = {}
- // #endif
- node.text = decodeEntity(text)
- if (this.hook(node)) {
- // #ifdef MP-WEIXIN
- if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
- this.expose()
- }
- // #endif
- const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
- siblings.push(node)
- }
-}
-
-/**
- * @description html 词法分析器
- * @param {Object} handler 高层处理器
- */
-function Lexer (handler) {
- this.handler = handler
-}
-
-/**
- * @description 执行解析
- * @param {String} content 要解析的文本
- */
-Lexer.prototype.parse = function (content) {
- this.content = content || ''
- this.i = 0 // 标记解析位置
- this.start = 0 // 标记一个单词的开始位置
- this.state = this.text // 当前状态
- for (let len = this.content.length; this.i !== -1 && this.i < len;) {
- this.state()
- }
-}
-
-/**
- * @description 检查标签是否闭合
- * @param {String} method 如果闭合要进行的操作
- * @returns {Boolean} 是否闭合
- * @private
- */
-Lexer.prototype.checkClose = function (method) {
- const selfClose = this.content[this.i] === '/'
- if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
- if (method) {
- this.handler[method](this.content.substring(this.start, this.i))
- }
- this.i += selfClose ? 2 : 1
- this.start = this.i
- this.handler.onOpenTag(selfClose)
- if (this.handler.tagName === 'script') {
- this.i = this.content.indexOf('', this.i)
- if (this.i !== -1) {
- this.i += 2
- this.start = this.i
- }
- this.state = this.endTag
- } else {
- this.state = this.text
- }
- return true
- }
- return false
-}
-
-/**
- * @description 文本状态
- * @private
- */
-Lexer.prototype.text = function () {
- this.i = this.content.indexOf('<', this.i) // 查找最近的标签
- if (this.i === -1) {
- // 没有标签了
- if (this.start < this.content.length) {
- this.handler.onText(this.content.substring(this.start, this.content.length))
- }
- return
- }
- const c = this.content[this.i + 1]
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
- // 标签开头
- if (this.start !== this.i) {
- this.handler.onText(this.content.substring(this.start, this.i))
- }
- this.start = ++this.i
- this.state = this.tagName
- } else if (c === '/' || c === '!' || c === '?') {
- if (this.start !== this.i) {
- this.handler.onText(this.content.substring(this.start, this.i))
- }
- const next = this.content[this.i + 2]
- if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
- // 标签结尾
- this.i += 2
- this.start = this.i
- this.state = this.endTag
- return
- }
- // 处理注释
- let end = '-->'
- if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
- end = '>'
- }
- this.i = this.content.indexOf(end, this.i)
- if (this.i !== -1) {
- this.i += end.length
- this.start = this.i
- }
- } else {
- this.i++
- }
-}
-
-/**
- * @description 标签名状态
- * @private
- */
-Lexer.prototype.tagName = function () {
- if (blankChar[this.content[this.i]]) {
- // 解析到标签名
- this.handler.onTagName(this.content.substring(this.start, this.i))
- while (blankChar[this.content[++this.i]]);
- if (this.i < this.content.length && !this.checkClose()) {
- this.start = this.i
- this.state = this.attrName
- }
- } else if (!this.checkClose('onTagName')) {
- this.i++
- }
-}
-
-/**
- * @description 属性名状态
- * @private
- */
-Lexer.prototype.attrName = function () {
- let c = this.content[this.i]
- if (blankChar[c] || c === '=') {
- // 解析到属性名
- this.handler.onAttrName(this.content.substring(this.start, this.i))
- let needVal = c === '='
- const len = this.content.length
- while (++this.i < len) {
- c = this.content[this.i]
- if (!blankChar[c]) {
- if (this.checkClose()) return
- if (needVal) {
- // 等号后遇到第一个非空字符
- this.start = this.i
- this.state = this.attrVal
- return
- }
- if (this.content[this.i] === '=') {
- needVal = true
- } else {
- this.start = this.i
- this.state = this.attrName
- return
- }
- }
- }
- } else if (!this.checkClose('onAttrName')) {
- this.i++
- }
-}
-
-/**
- * @description 属性值状态
- * @private
- */
-Lexer.prototype.attrVal = function () {
- const c = this.content[this.i]
- const len = this.content.length
- if (c === '"' || c === "'") {
- // 有冒号的属性
- this.start = ++this.i
- this.i = this.content.indexOf(c, this.i)
- if (this.i === -1) return
- this.handler.onAttrVal(this.content.substring(this.start, this.i))
- } else {
- // 没有冒号的属性
- for (; this.i < len; this.i++) {
- if (blankChar[this.content[this.i]]) {
- this.handler.onAttrVal(this.content.substring(this.start, this.i))
- break
- } else if (this.checkClose('onAttrVal')) return
- }
- }
- while (blankChar[this.content[++this.i]]);
- if (this.i < len && !this.checkClose()) {
- this.start = this.i
- this.state = this.attrName
- }
-}
-
-/**
- * @description 结束标签状态
- * @returns {String} 结束的标签名
- * @private
- */
-Lexer.prototype.endTag = function () {
- const c = this.content[this.i]
- if (blankChar[c] || c === '>' || c === '/') {
- this.handler.onCloseTag(this.content.substring(this.start, this.i))
- if (c !== '>') {
- this.i = this.content.indexOf('>', this.i)
- if (this.i === -1) return
- }
- this.start = ++this.i
- this.state = this.text
- } else {
- this.i++
- }
-}
-
-export default Parser
+/**
+ * @fileoverview html 解析器
+ */
+
+// 配置
+const config = {
+ // 信任的标签(保持标签名不变)
+ trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+
+ // 块级标签(转为 div,其他的非信任标签转为 span)
+ blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ // 行内标签
+ inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
+ // #endif
+
+ // 要移除的标签
+ ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
+
+ // 自闭合的标签
+ voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+
+ // html 实体
+ entities: {
+ lt: '<',
+ gt: '>',
+ quot: '"',
+ apos: "'",
+ ensp: '\u2002',
+ emsp: '\u2003',
+ nbsp: '\xA0',
+ semi: ';',
+ ndash: '–',
+ mdash: '—',
+ middot: '·',
+ lsquo: '‘',
+ rsquo: '’',
+ ldquo: '“',
+ rdquo: '”',
+ bull: '•',
+ hellip: '…',
+ larr: '←',
+ uarr: '↑',
+ rarr: '→',
+ darr: '↓'
+ },
+
+ // 默认的标签样式
+ tagStyle: {
+ // #ifndef APP-PLUS-NVUE
+ address: 'font-style:italic',
+ big: 'display:inline;font-size:1.2em',
+ caption: 'display:table-caption;text-align:center',
+ center: 'text-align:center',
+ cite: 'font-style:italic',
+ dd: 'margin-left:40px',
+ mark: 'background-color:yellow',
+ pre: 'font-family:monospace;white-space:pre',
+ s: 'text-decoration:line-through',
+ small: 'display:inline;font-size:0.8em',
+ strike: 'text-decoration:line-through',
+ u: 'text-decoration:underline'
+ // #endif
+ },
+
+ // svg 大小写对照表
+ svgDict: {
+ animatetransform: 'animateTransform',
+ lineargradient: 'linearGradient',
+ viewbox: 'viewBox',
+ attributename: 'attributeName',
+ repeatcount: 'repeatCount',
+ repeatdur: 'repeatDur',
+ foreignobject: 'foreignObject'
+ }
+}
+const tagSelector = {}
+let windowWidth, system
+// #ifdef MP-WEIXIN
+if (uni.canIUse('getWindowInfo')) {
+ windowWidth = uni.getWindowInfo().windowWidth
+ system = uni.getDeviceInfo().system
+} else {
+// #endif
+ const systemInfo = uni.getSystemInfoSync()
+ windowWidth = systemInfo.windowWidth
+ // #ifdef MP-WEIXIN
+ system = systemInfo.system
+}
+// #endif
+const blankChar = makeMap(' ,\r,\n,\t,\f')
+let idIndex = 0
+
+// #ifdef H5 || APP-PLUS
+config.ignoreTags.iframe = undefined
+config.trustTags.iframe = true
+config.ignoreTags.embed = undefined
+config.trustTags.embed = true
+// #endif
+// #ifdef APP-PLUS-NVUE
+config.ignoreTags.source = undefined
+config.ignoreTags.style = undefined
+// #endif
+
+/**
+ * @description 创建 map
+ * @param {String} str 逗号分隔
+ */
+function makeMap (str) {
+ const map = Object.create(null)
+ const list = str.split(',')
+ for (let i = list.length; i--;) {
+ map[list[i]] = true
+ }
+ return map
+}
+
+/**
+ * @description 解码 html 实体
+ * @param {String} str 要解码的字符串
+ * @param {Boolean} amp 要不要解码 &
+ * @returns {String} 解码后的字符串
+ */
+function decodeEntity (str, amp) {
+ let i = str.indexOf('&')
+ while (i !== -1) {
+ const j = str.indexOf(';', i + 3)
+ let code
+ if (j === -1) break
+ if (str[i + 1] === '#') {
+ // { 形式的实体
+ code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
+ if (!isNaN(code)) {
+ str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
+ }
+ } else {
+ // 形式的实体
+ code = str.substring(i + 1, j)
+ if (config.entities[code] || (code === 'amp' && amp)) {
+ str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
+ }
+ }
+ i = str.indexOf('&', i + 1)
+ }
+ return str
+}
+
+/**
+ * @description 合并多个块级标签,加快长内容渲染
+ * @param {Array} nodes 要合并的标签数组
+ */
+function mergeNodes (nodes) {
+ let i = nodes.length - 1
+ for (let j = i; j >= -1; j--) {
+ if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
+ if (i - j >= 5) {
+ nodes.splice(j + 1, i - j, {
+ name: 'div',
+ attrs: {},
+ children: nodes.slice(j + 1, i + 1)
+ })
+ }
+ i = j - 1
+ }
+ }
+}
+
+/**
+ * @description html 解析器
+ * @param {Object} vm 组件实例
+ */
+function Parser (vm) {
+ this.options = vm || {}
+ this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
+ this.imgList = vm.imgList || []
+ this.imgList._unloadimgs = 0
+ this.plugins = vm.plugins || []
+ this.attrs = Object.create(null)
+ this.stack = []
+ this.nodes = []
+ this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Parser.prototype.parse = function (content) {
+ // 插件处理
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onUpdate) {
+ content = this.plugins[i].onUpdate(content, config) || content
+ }
+ }
+
+ new Lexer(this).parse(content)
+ // 出栈未闭合的标签
+ while (this.stack.length) {
+ this.popNode()
+ }
+ if (this.nodes.length > 50) {
+ mergeNodes(this.nodes)
+ }
+ return this.nodes
+}
+
+/**
+ * @description 将标签暴露出来(不被 rich-text 包含)
+ */
+Parser.prototype.expose = function () {
+ // #ifndef APP-PLUS-NVUE
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
+ item.c = 1
+ }
+ // #endif
+}
+
+/**
+ * @description 处理插件
+ * @param {Object} node 要处理的标签
+ * @returns {Boolean} 是否要移除此标签
+ */
+Parser.prototype.hook = function (node) {
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
+ return false
+ }
+ }
+ return true
+}
+
+/**
+ * @description 将链接拼接上主域名
+ * @param {String} url 需要拼接的链接
+ * @returns {String} 拼接后的链接
+ */
+Parser.prototype.getUrl = function (url) {
+ const domain = this.options.domain
+ if (url[0] === '/') {
+ if (url[1] === '/') {
+ // // 开头的补充协议名
+ url = (domain ? domain.split('://')[0] : 'http') + ':' + url
+ } else if (domain) {
+ // 否则补充整个域名
+ url = domain + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ } else if (!url.includes('data:') && !url.includes('://')) {
+ if (domain) {
+ url = domain + '/' + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ }
+ return url
+}
+
+/**
+ * @description 解析样式表
+ * @param {Object} node 标签
+ * @returns {Object}
+ */
+Parser.prototype.parseStyle = function (node) {
+ const attrs = node.attrs
+ const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
+ const styleObj = {}
+ let tmp = ''
+
+ if (attrs.id && !this.xml) {
+ // 暴露锚点
+ if (this.options.useAnchor) {
+ this.expose()
+ } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
+ attrs.id = undefined
+ }
+ }
+
+ // 转换 width 和 height 属性
+ if (attrs.width) {
+ styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
+ attrs.width = undefined
+ }
+ if (attrs.height) {
+ styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
+ attrs.height = undefined
+ }
+
+ for (let i = 0, len = list.length; i < len; i++) {
+ const info = list[i].split(':')
+ if (info.length < 2) continue
+ const key = info.shift().trim().toLowerCase()
+ let value = info.join(':').trim()
+ if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
+ // 兼容性的 css 不压缩
+ tmp += `;${key}:${value}`
+ } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
+ // 重复的样式进行覆盖
+ if (value.includes('url')) {
+ // 填充链接
+ let j = value.indexOf('(') + 1
+ if (j) {
+ while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
+ j++
+ }
+ value = value.substr(0, j) + this.getUrl(value.substr(j))
+ }
+ } else if (value.includes('rpx')) {
+ // 转换 rpx(rich-text 内部不支持 rpx)
+ value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
+ }
+ styleObj[key] = value
+ }
+ }
+
+ node.attrs.style = tmp
+ return styleObj
+}
+
+/**
+ * @description 解析到标签名
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onTagName = function (name) {
+ this.tagName = this.xml ? name : name.toLowerCase()
+ if (this.tagName === 'svg') {
+ this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
+ config.ignoreTags.style = undefined // svg 标签内 style 可用
+ }
+}
+
+/**
+ * @description 解析到属性名
+ * @param {String} name 属性名
+ * @private
+ */
+Parser.prototype.onAttrName = function (name) {
+ name = this.xml ? name : name.toLowerCase()
+ // #ifdef (VUE3 && (H5 || APP-PLUS)) || APP-PLUS-NVUE
+ if (name.includes('?') || name.includes(';')) {
+ this.attrName = undefined
+ return
+ }
+ // #endif
+ if (name.substr(0, 5) === 'data-') {
+ if (name === 'data-src' && !this.attrs.src) {
+ // data-src 自动转为 src
+ this.attrName = 'src'
+ } else if (this.tagName === 'img' || this.tagName === 'a') {
+ // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
+ this.attrName = name
+ } else {
+ // 剩余的移除以减小大小
+ this.attrName = undefined
+ }
+ } else {
+ this.attrName = name
+ this.attrs[name] = 'T' // boolean 型属性缺省设置
+ }
+}
+
+/**
+ * @description 解析到属性值
+ * @param {String} val 属性值
+ * @private
+ */
+Parser.prototype.onAttrVal = function (val) {
+ const name = this.attrName || ''
+ if (name === 'style' || name === 'href') {
+ // 部分属性进行实体解码
+ this.attrs[name] = decodeEntity(val, true)
+ } else if (name.includes('src')) {
+ // 拼接主域名
+ this.attrs[name] = this.getUrl(decodeEntity(val, true))
+ } else if (name) {
+ this.attrs[name] = val
+ }
+}
+
+/**
+ * @description 解析到标签开始
+ * @param {Boolean} selfClose 是否有自闭合标识 />
+ * @private
+ */
+Parser.prototype.onOpenTag = function (selfClose) {
+ // 拼装 node
+ const node = Object.create(null)
+ node.name = this.tagName
+ node.attrs = this.attrs
+ // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
+ if (this.options.nodes.length) {
+ node.type = 'node'
+ }
+ this.attrs = Object.create(null)
+
+ const attrs = node.attrs
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+ const close = this.xml ? selfClose : config.voidTags[node.name]
+
+ // 替换标签名选择器
+ if (tagSelector[node.name]) {
+ attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
+ }
+
+ // 转换 embed 标签
+ if (node.name === 'embed') {
+ // #ifndef H5 || APP-PLUS
+ const src = attrs.src || ''
+ // 按照后缀名和 type 将 embed 转为 video 或 audio
+ if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
+ node.name = 'video'
+ } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
+ node.name = 'audio'
+ }
+ if (attrs.autostart) {
+ attrs.autoplay = 'T'
+ }
+ attrs.controls = 'T'
+ // #endif
+ // #ifdef H5 || APP-PLUS
+ this.expose()
+ // #endif
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 处理音视频
+ if (node.name === 'video' || node.name === 'audio') {
+ // 设置 id 以便获取 context
+ if (node.name === 'video' && !attrs.id) {
+ attrs.id = 'v' + idIndex++
+ }
+ // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
+ if (!attrs.controls && !attrs.autoplay) {
+ attrs.controls = 'T'
+ }
+ // 用数组存储所有可用的 source
+ node.src = []
+ if (attrs.src) {
+ node.src.push(attrs.src)
+ attrs.src = undefined
+ }
+ this.expose()
+ }
+ // #endif
+
+ // 处理自闭合标签
+ if (close) {
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 通过 base 标签设置主域名
+ if (node.name === 'base' && !this.options.domain) {
+ this.options.domain = attrs.href
+ } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
+ // 设置 source 标签(仅父节点为 video 或 audio 时有效)
+ parent.src.push(attrs.src)
+ } /* #endif */
+ return
+ }
+
+ // 解析 style
+ const styleObj = this.parseStyle(node)
+
+ // 处理图片
+ if (node.name === 'img') {
+ if (attrs.src) {
+ // 标记 webp
+ if (attrs.src.includes('webp')) {
+ node.webp = 'T'
+ }
+ // data url 图片如果没有设置 original-src 默认为不可预览的小图片
+ if (attrs.src.includes('data:') && this.options.previewImg !== 'all' && !attrs['original-src']) {
+ attrs.ignore = 'T'
+ }
+ if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.name === 'a') {
+ node.a = item.attrs
+ }
+ if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
+ if (!styleObj.display || styleObj.display.includes('inline')) {
+ node.t = 'inline-block'
+ } else {
+ node.t = styleObj.display
+ }
+ styleObj.display = undefined
+ }
+ // #ifndef H5 || APP-PLUS
+ const style = item.attrs.style || ''
+ if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
+ styleObj.width = '100% !important'
+ styleObj.height = ''
+ for (let j = i + 1; j < this.stack.length; j++) {
+ this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
+ }
+ } else if (style.includes('flex') && styleObj.width === '100%') {
+ for (let j = i + 1; j < this.stack.length; j++) {
+ const style = this.stack[j].attrs.style || ''
+ if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
+ styleObj.width = ''
+ break
+ }
+ }
+ } else if (style.includes('inline-block')) {
+ if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
+ item.attrs.style += ';max-width:' + styleObj.width
+ styleObj.width = ''
+ } else {
+ item.attrs.style += ';max-width:100%'
+ }
+ }
+ // #endif
+ item.c = 1
+ }
+ attrs.i = this.imgList.length.toString()
+ let src = attrs['original-src'] || attrs.src
+ // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
+ if (this.imgList.includes(src)) {
+ // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
+ let i = src.indexOf('://')
+ if (i !== -1) {
+ i += 3
+ let newSrc = src.substr(0, i)
+ for (; i < src.length; i++) {
+ if (src[i] === '/') break
+ newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
+ }
+ newSrc += src.substr(i)
+ src = newSrc
+ }
+ }
+ // #endif
+ this.imgList.push(src)
+ if (!node.t) {
+ this.imgList._unloadimgs += 1
+ }
+ // #ifdef H5 || APP-PLUS
+ if (this.options.lazyLoad) {
+ attrs['data-src'] = attrs.src
+ attrs.src = undefined
+ }
+ // #endif
+ }
+ }
+ if (styleObj.display === 'inline') {
+ styleObj.display = ''
+ }
+ // #ifndef APP-PLUS-NVUE
+ if (attrs.ignore) {
+ styleObj['max-width'] = styleObj['max-width'] || '100%'
+ attrs.style += ';-webkit-touch-callout:none'
+ }
+ // #endif
+ // 设置的宽度超出屏幕,为避免变形,高度转为自动
+ if (parseInt(styleObj.width) > windowWidth) {
+ styleObj.height = undefined
+ }
+ // 记录是否设置了宽高
+ if (!isNaN(parseInt(styleObj.width))) {
+ node.w = 'T'
+ }
+ if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
+ node.h = 'T'
+ }
+ if (node.w && node.h && styleObj['object-fit']) {
+ if (styleObj['object-fit'] === 'contain') {
+ node.m = 'aspectFit'
+ } else if (styleObj['object-fit'] === 'cover') {
+ node.m = 'aspectFill'
+ }
+ }
+ } else if (node.name === 'svg') {
+ siblings.push(node)
+ this.stack.push(node)
+ this.popNode()
+ return
+ }
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ if (!attrs.style) {
+ delete attrs.style
+ }
+ // #endif
+ } else {
+ if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
+ this.pre = node.pre = 1
+ }
+ node.children = []
+ this.stack.push(node)
+ }
+
+ // 加入节点树
+ siblings.push(node)
+}
+
+/**
+ * @description 解析到标签结束
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onCloseTag = function (name) {
+ // 依次出栈到匹配为止
+ name = this.xml ? name : name.toLowerCase()
+ let i
+ for (i = this.stack.length; i--;) {
+ if (this.stack[i].name === name) break
+ }
+ if (i !== -1) {
+ while (this.stack.length > i) {
+ this.popNode()
+ }
+ } else if (name === 'p' || name === 'br') {
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push({
+ name,
+ attrs: {
+ class: tagSelector[name] || '',
+ style: this.tagStyle[name] || ''
+ }
+ })
+ }
+}
+
+/**
+ * @description 处理标签出栈
+ * @private
+ */
+Parser.prototype.popNode = function () {
+ const node = this.stack.pop()
+ let attrs = node.attrs
+ const children = node.children
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 获取标题
+ if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
+ uni.setNavigationBarTitle({
+ title: children[0].text
+ })
+ }
+ siblings.pop()
+ return
+ }
+
+ if (node.pre && this.pre !== 2) {
+ // 是否合并空白符标识
+ this.pre = node.pre = undefined
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].pre) {
+ this.pre = 1
+ }
+ }
+ }
+
+ const styleObj = {}
+
+ // 转换 svg
+ if (node.name === 'svg') {
+ if (this.xml > 1) {
+ // 多层 svg 嵌套
+ this.xml--
+ return
+ }
+ // #ifdef APP-PLUS-NVUE
+ (function traversal (node) {
+ if (node.name) {
+ // 调整 svg 的大小写
+ node.name = config.svgDict[node.name] || node.name
+ for (const item in node.attrs) {
+ if (config.svgDict[item]) {
+ node.attrs[config.svgDict[item]] = node.attrs[item]
+ node.attrs[item] = undefined
+ }
+ }
+ for (let i = 0; i < (node.children || []).length; i++) {
+ traversal(node.children[i])
+ }
+ }
+ })(node)
+ // #endif
+ // #ifndef APP-PLUS-NVUE
+ let src = ''
+ const style = attrs.style
+ attrs.style = ''
+ attrs.xmlns = 'http://www.w3.org/2000/svg';
+ (function traversal (node) {
+ if (node.type === 'text') {
+ src += node.text
+ return
+ }
+ const name = config.svgDict[node.name] || node.name
+ if (name === 'foreignObject') {
+ for (const child of (node.children || [])) {
+ if (child.attrs && !child.attrs.xmlns) {
+ child.attrs.xmlns = 'http://www.w3.org/1999/xhtml'
+ break
+ }
+ }
+ }
+ src += '<' + name
+ for (const item in node.attrs) {
+ const val = node.attrs[item]
+ if (val) {
+ src += ` ${config.svgDict[item] || item}="${val.replace(/"/g, '')}"`
+ }
+ }
+ if (!node.children) {
+ src += '/>'
+ } else {
+ src += '>'
+ for (let i = 0; i < node.children.length; i++) {
+ traversal(node.children[i])
+ }
+ src += '' + name + '>'
+ }
+ })(node)
+ node.name = 'img'
+ node.attrs = {
+ src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+ style,
+ ignore: 'T'
+ }
+ node.children = undefined
+ // #endif
+ this.xml = false
+ config.ignoreTags.style = true
+ return
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 转换 align 属性
+ if (attrs.align) {
+ if (node.name === 'table') {
+ if (attrs.align === 'center') {
+ styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
+ } else {
+ styleObj.float = attrs.align
+ }
+ } else {
+ styleObj['text-align'] = attrs.align
+ }
+ attrs.align = undefined
+ }
+
+ // 转换 dir 属性
+ if (attrs.dir) {
+ styleObj.direction = attrs.dir
+ attrs.dir = undefined
+ }
+
+ // 转换 font 标签的属性
+ if (node.name === 'font') {
+ if (attrs.color) {
+ styleObj.color = attrs.color
+ attrs.color = undefined
+ }
+ if (attrs.face) {
+ styleObj['font-family'] = attrs.face
+ attrs.face = undefined
+ }
+ if (attrs.size) {
+ let size = parseInt(attrs.size)
+ if (!isNaN(size)) {
+ if (size < 1) {
+ size = 1
+ } else if (size > 7) {
+ size = 7
+ }
+ styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
+ }
+ attrs.size = undefined
+ }
+ }
+ // #endif
+
+ // 一些编辑器的自带 class
+ if ((attrs.class || '').includes('align-center')) {
+ styleObj['text-align'] = 'center'
+ }
+
+ Object.assign(styleObj, this.parseStyle(node))
+
+ if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
+ styleObj['max-width'] = '100%'
+ styleObj['box-sizing'] = 'border-box'
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ if (config.blockTags[node.name]) {
+ node.name = 'div'
+ } else if (!config.trustTags[node.name] && !this.xml) {
+ // 未知标签转为 span,避免无法显示
+ node.name = 'span'
+ }
+
+ if (node.name === 'a' || node.name === 'ad'
+ // #ifdef H5 || APP-PLUS
+ || node.name === 'iframe' // eslint-disable-line
+ // #endif
+ ) {
+ this.expose()
+ } else if (node.name === 'video') {
+ if ((styleObj.height || '').includes('auto')) {
+ styleObj.height = undefined
+ }
+ /* #ifdef APP-PLUS */
+ let str = ''
+ node.html = str
+ /* #endif */
+ } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
+ // 列表处理
+ const types = {
+ a: 'lower-alpha',
+ A: 'upper-alpha',
+ i: 'lower-roman',
+ I: 'upper-roman'
+ }
+ if (types[attrs.type]) {
+ attrs.style += ';list-style-type:' + types[attrs.type]
+ attrs.type = undefined
+ }
+ for (let i = children.length; i--;) {
+ if (children[i].name === 'li') {
+ children[i].c = 1
+ }
+ }
+ } else if (node.name === 'table') {
+ // 表格处理
+ // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
+ let padding = parseFloat(attrs.cellpadding)
+ let spacing = parseFloat(attrs.cellspacing)
+ const border = parseFloat(attrs.border)
+ const bordercolor = styleObj['border-color']
+ const borderstyle = styleObj['border-style']
+ if (node.c) {
+ // padding 和 spacing 默认 2
+ if (isNaN(padding)) {
+ padding = 2
+ }
+ if (isNaN(spacing)) {
+ spacing = 2
+ }
+ }
+ if (border) {
+ attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
+ }
+ if (node.flag && node.c) {
+ // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
+ styleObj.display = 'grid'
+ if (styleObj['border-collapse'] === 'collapse') {
+ styleObj['border-collapse'] = undefined
+ spacing = 0
+ }
+ if (spacing) {
+ styleObj['grid-gap'] = spacing + 'px'
+ styleObj.padding = spacing + 'px'
+ } else if (border) {
+ // 无间隔的情况下避免边框重叠
+ attrs.style += ';border-left:0;border-top:0'
+ }
+
+ const width = [] // 表格的列宽
+ const trList = [] // tr 列表
+ const cells = [] // 保存新的单元格
+ const map = {}; // 被合并单元格占用的格子
+
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].name === 'tr') {
+ trList.push(nodes[i])
+ } else if (nodes[i].name === 'colgroup') {
+ let colI = 1
+ for (const col of (nodes[i].children || [])) {
+ if (col.name === 'col') {
+ const style = col.attrs.style || ''
+ const start = style.indexOf('width') ? style.indexOf(';width') : 0
+ // 提取出宽度
+ if (start !== -1) {
+ let end = style.indexOf(';', start + 6)
+ if (end === -1) {
+ end = style.length
+ }
+ width[colI] = style.substring(start ? start + 7 : 6, end)
+ }
+ colI += 1
+ }
+ }
+ } else {
+ traversal(nodes[i].children || [])
+ }
+ }
+ })(children)
+
+ for (let row = 1; row <= trList.length; row++) {
+ let col = 1
+ for (let j = 0; j < trList[row - 1].children.length; j++) {
+ const td = trList[row - 1].children[j]
+ if (td.name === 'td' || td.name === 'th') {
+ // 这个格子被上面的单元格占用,则列号++
+ while (map[row + '.' + col]) {
+ col++
+ }
+ let style = td.attrs.style || ''
+ let start = style.indexOf('width') ? style.indexOf(';width') : 0
+ // 提取出 td 的宽度
+ if (start !== -1) {
+ let end = style.indexOf(';', start + 6)
+ if (end === -1) {
+ end = style.length
+ }
+ if (!td.attrs.colspan) {
+ width[col] = style.substring(start ? start + 7 : 6, end)
+ }
+ style = style.substr(0, start) + style.substr(end)
+ }
+ // 设置竖直对齐
+ style += ';display:flex'
+ start = style.indexOf('vertical-align')
+ if (start !== -1) {
+ const val = style.substr(start + 15, 10)
+ if (val.includes('middle')) {
+ style += ';align-items:center'
+ } else if (val.includes('bottom')) {
+ style += ';align-items:flex-end'
+ }
+ } else {
+ style += ';align-items:center'
+ }
+ // 设置水平对齐
+ start = style.indexOf('text-align')
+ if (start !== -1) {
+ const val = style.substr(start + 11, 10)
+ if (val.includes('center')) {
+ style += ';justify-content: center'
+ } else if (val.includes('right')) {
+ style += ';justify-content: right'
+ }
+ }
+ style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
+ // 处理列合并
+ if (td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
+ if (!td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + 1}`
+ }
+ col += parseInt(td.attrs.colspan) - 1
+ }
+ // 处理行合并
+ if (td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
+ if (!td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + 1}`
+ }
+ // 记录下方单元格被占用
+ for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
+ for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
+ map[(row + rowspan) + '.' + (col - colspan)] = 1
+ }
+ }
+ }
+ if (style) {
+ td.attrs.style = style
+ }
+ cells.push(td)
+ col++
+ }
+ }
+ if (row === 1) {
+ let temp = ''
+ for (let i = 1; i < col; i++) {
+ temp += (width[i] ? width[i] : 'auto') + ' '
+ }
+ styleObj['grid-template-columns'] = temp
+ }
+ }
+ node.children = cells
+ } else {
+ // 没有使用合并单元格的表格通过 table 布局实现
+ if (node.c) {
+ styleObj.display = 'table'
+ }
+ if (!isNaN(spacing)) {
+ styleObj['border-spacing'] = spacing + 'px'
+ }
+ if (border || padding) {
+ // 遍历
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ const td = nodes[i]
+ if (td.name === 'th' || td.name === 'td') {
+ if (border) {
+ td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
+ }
+ if (padding) {
+ td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
+ }
+ } else if (td.children) {
+ traversal(td.children)
+ }
+ }
+ })(children)
+ }
+ }
+ // 给表格添加一个单独的横向滚动层
+ if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
+ const table = Object.assign({}, node)
+ node.name = 'div'
+ node.attrs = {
+ style: 'overflow:auto'
+ }
+ node.children = [table]
+ attrs = table.attrs
+ }
+ } else if ((node.name === 'tbody' || node.name === 'tr') && node.flag && node.c) {
+ node.flag = undefined;
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].name === 'td') {
+ // 颜色样式设置给单元格避免丢失
+ for (const style of ['color', 'background', 'background-color']) {
+ if (styleObj[style]) {
+ nodes[i].attrs.style = style + ':' + styleObj[style] + ';' + (nodes[i].attrs.style || '')
+ }
+ }
+ } else {
+ traversal(nodes[i].children || [])
+ }
+ }
+ })(children)
+ } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].name === 'table' || this.stack[i].name === 'tbody' || this.stack[i].name === 'tr') {
+ this.stack[i].flag = 1 // 指示含有合并单元格
+ }
+ }
+ } else if (node.name === 'ruby') {
+ // 转换 ruby
+ node.name = 'span'
+ for (let i = 0; i < children.length - 1; i++) {
+ if (children[i].type === 'text' && children[i + 1].name === 'rt') {
+ children[i] = {
+ name: 'div',
+ attrs: {
+ style: 'display:inline-block;text-align:center'
+ },
+ children: [{
+ name: 'div',
+ attrs: {
+ style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
+ },
+ children: children[i + 1].children
+ }, children[i]]
+ }
+ children.splice(i + 1, 1)
+ }
+ }
+ } else if (node.c) {
+ (function traversal (node) {
+ node.c = 2
+ for (let i = node.children.length; i--;) {
+ const child = node.children[i]
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
+ traversal(child)
+ }
+ // #endif
+ if (!child.c || child.name === 'table') {
+ node.c = 1
+ }
+ }
+ })(node)
+ }
+
+ if ((styleObj.display || '').includes('flex') && !node.c) {
+ for (let i = children.length; i--;) {
+ const item = children[i]
+ if (item.f) {
+ item.attrs.style = (item.attrs.style || '') + item.f
+ item.f = undefined
+ }
+ }
+ }
+ // flex 布局时部分样式需要提取到 rich-text 外层
+ const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
+ // #ifdef MP-WEIXIN
+ // 检查基础库版本 virtualHost 是否可用
+ && !(node.c && wx.getNFCAdapter) // eslint-disable-line
+ // #endif
+ // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
+ && !node.c // eslint-disable-line
+ // #endif
+ if (flex) {
+ node.f = ';max-width:100%'
+ }
+
+ if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
+ mergeNodes(children)
+ }
+ // #endif
+
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ const val = `;${key}:${styleObj[key].replace(' !important', '')}`
+ /* #ifndef APP-PLUS-NVUE */
+ if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
+ node.f += val
+ if (key === 'width') {
+ attrs.style += ';width:100%'
+ }
+ } else /* #endif */ {
+ attrs.style += val
+ }
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ for (const key in attrs) {
+ if (!attrs[key]) {
+ delete attrs[key]
+ }
+ }
+ // #endif
+}
+
+/**
+ * @description 解析到文本
+ * @param {String} text 文本内容
+ */
+Parser.prototype.onText = function (text) {
+ if (!this.pre) {
+ // 合并空白符
+ let trim = ''
+ let flag
+ for (let i = 0, len = text.length; i < len; i++) {
+ if (!blankChar[text[i]]) {
+ trim += text[i]
+ } else {
+ if (trim[trim.length - 1] !== ' ') {
+ trim += ' '
+ }
+ if (text[i] === '\n' && !flag) {
+ flag = true
+ }
+ }
+ }
+ // 去除含有换行符的空串
+ if (trim === ' ') {
+ if (flag) return
+ // #ifdef VUE3
+ else {
+ const parent = this.stack[this.stack.length - 1]
+ if (parent && parent.name[0] === 't') return
+ }
+ // #endif
+ }
+ text = trim
+ }
+ const node = Object.create(null)
+ node.type = 'text'
+ // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
+ node.attrs = {}
+ // #endif
+ node.text = decodeEntity(text)
+ if (this.hook(node)) {
+ // #ifdef MP-WEIXIN
+ if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
+ this.expose()
+ }
+ // #endif
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push(node)
+ }
+}
+
+/**
+ * @description html 词法分析器
+ * @param {Object} handler 高层处理器
+ */
+function Lexer (handler) {
+ this.handler = handler
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Lexer.prototype.parse = function (content) {
+ this.content = content || ''
+ this.i = 0 // 标记解析位置
+ this.start = 0 // 标记一个单词的开始位置
+ this.state = this.text // 当前状态
+ for (let len = this.content.length; this.i !== -1 && this.i < len;) {
+ this.state()
+ }
+}
+
+/**
+ * @description 检查标签是否闭合
+ * @param {String} method 如果闭合要进行的操作
+ * @returns {Boolean} 是否闭合
+ * @private
+ */
+Lexer.prototype.checkClose = function (method) {
+ const selfClose = this.content[this.i] === '/'
+ if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
+ if (method) {
+ this.handler[method](this.content.substring(this.start, this.i))
+ }
+ this.i += selfClose ? 2 : 1
+ this.start = this.i
+ this.handler.onOpenTag(selfClose)
+ if (this.handler.tagName === 'script') {
+ this.i = this.content.indexOf('', this.i)
+ if (this.i !== -1) {
+ this.i += 2
+ this.start = this.i
+ }
+ this.state = this.endTag
+ } else {
+ this.state = this.text
+ }
+ return true
+ }
+ return false
+}
+
+/**
+ * @description 文本状态
+ * @private
+ */
+Lexer.prototype.text = function () {
+ this.i = this.content.indexOf('<', this.i) // 查找最近的标签
+ if (this.i === -1) {
+ // 没有标签了
+ if (this.start < this.content.length) {
+ this.handler.onText(this.content.substring(this.start, this.content.length))
+ }
+ return
+ }
+ const c = this.content[this.i + 1]
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ // 标签开头
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ this.start = ++this.i
+ this.state = this.tagName
+ } else if (c === '/' || c === '!' || c === '?') {
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ const next = this.content[this.i + 2]
+ if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+ // 标签结尾
+ this.i += 2
+ this.start = this.i
+ this.state = this.endTag
+ return
+ }
+ // 处理注释
+ let end = '-->'
+ if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
+ end = '>'
+ }
+ this.i = this.content.indexOf(end, this.i)
+ if (this.i !== -1) {
+ this.i += end.length
+ this.start = this.i
+ }
+ } else {
+ this.i++
+ }
+}
+
+/**
+ * @description 标签名状态
+ * @private
+ */
+Lexer.prototype.tagName = function () {
+ if (blankChar[this.content[this.i]]) {
+ // 解析到标签名
+ this.handler.onTagName(this.content.substring(this.start, this.i))
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < this.content.length && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+ } else if (!this.checkClose('onTagName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性名状态
+ * @private
+ */
+Lexer.prototype.attrName = function () {
+ let c = this.content[this.i]
+ if (blankChar[c] || c === '=') {
+ // 解析到属性名
+ this.handler.onAttrName(this.content.substring(this.start, this.i))
+ let needVal = c === '='
+ const len = this.content.length
+ while (++this.i < len) {
+ c = this.content[this.i]
+ if (!blankChar[c]) {
+ if (this.checkClose()) return
+ if (needVal) {
+ // 等号后遇到第一个非空字符
+ this.start = this.i
+ this.state = this.attrVal
+ return
+ }
+ if (this.content[this.i] === '=') {
+ needVal = true
+ } else {
+ this.start = this.i
+ this.state = this.attrName
+ return
+ }
+ }
+ }
+ } else if (!this.checkClose('onAttrName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性值状态
+ * @private
+ */
+Lexer.prototype.attrVal = function () {
+ const c = this.content[this.i]
+ const len = this.content.length
+ if (c === '"' || c === "'") {
+ // 有冒号的属性
+ this.start = ++this.i
+ this.i = this.content.indexOf(c, this.i)
+ if (this.i === -1) return
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ } else {
+ // 没有冒号的属性
+ for (; this.i < len; this.i++) {
+ if (blankChar[this.content[this.i]]) {
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ break
+ } else if (this.checkClose('onAttrVal')) return
+ }
+ }
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < len && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+}
+
+/**
+ * @description 结束标签状态
+ * @returns {String} 结束的标签名
+ * @private
+ */
+Lexer.prototype.endTag = function () {
+ const c = this.content[this.i]
+ if (blankChar[c] || c === '>' || c === '/') {
+ this.handler.onCloseTag(this.content.substring(this.start, this.i))
+ if (c !== '>') {
+ this.i = this.content.indexOf('>', this.i)
+ if (this.i === -1) return
+ }
+ this.start = ++this.i
+ this.state = this.text
+ } else {
+ this.i++
+ }
+}
+
+export default Parser
diff --git a/uni_modules/mp-html/package.json b/uni_modules/mp-html/package.json
index 84c07e3..91deca7 100644
--- a/uni_modules/mp-html/package.json
+++ b/uni_modules/mp-html/package.json
@@ -1,76 +1,74 @@
-{
- "id": "mp-html",
- "displayName": "mp-html 富文本组件【全端支持,支持编辑、latex等扩展】",
- "version": "v2.5.0",
- "description": "一个强大的富文本组件,高效轻量,功能丰富",
- "keywords": [
- "富文本",
- "编辑器",
- "html",
- "rich-text",
- "editor"
- ],
- "repository": "https://github.com/jin-yufeng/mp-html",
- "dcloudext": {
- "sale": {
- "regular": {
- "price": "0.00"
- },
- "sourcecode": {
- "price": "0.00"
- }
- },
- "contact": {
- "qq": ""
- },
- "declaration": {
- "ads": "无",
- "data": "无",
- "permissions": "无"
- },
- "npmurl": "https://www.npmjs.com/package/mp-html",
- "type": "component-vue"
- },
- "uni_modules": {
- "platforms": {
- "cloud": {
- "tcb": "y",
- "aliyun": "y"
- },
- "client": {
- "App": {
- "app-vue": "y",
- "app-nvue": "y"
- },
- "H5-mobile": {
- "Safari": "y",
- "Android Browser": "y",
- "微信浏览器(Android)": "y",
- "QQ浏览器(Android)": "y"
- },
- "H5-pc": {
- "Chrome": "y",
- "IE": "u",
- "Edge": "y",
- "Firefox": "y",
- "Safari": "y"
- },
- "小程序": {
- "微信": "y",
- "阿里": "y",
- "百度": "y",
- "字节跳动": "y",
- "QQ": "y"
- },
- "快应用": {
- "华为": "y",
- "联盟": "y"
- },
- "Vue": {
- "vue2": "y",
- "vue3": "y"
- }
- }
- }
- }
-}
\ No newline at end of file
+{
+ "name": "mp-html",
+ "version": "2.5.2",
+ "description": "小程序富文本组件",
+ "miniprogram": "dist/mp-weixin",
+ "repository": "https://github.com/jin-yufeng/mp-html",
+ "author": "Jin Yufeng",
+ "license": "MIT",
+ "keywords": [
+ "miniprogram",
+ "rich-text",
+ "html"
+ ],
+ "standard": {
+ "globals": ["App", "Page", "Component", "wx", "requirePlugin", "uni", "plus", "weex"],
+ "envs": ["jest", "browser"]
+ },
+ "jest": {
+ "testEnvironment": "jsdom",
+ "collectCoverageFrom": [
+ "dev/mp-weixin/components/mp-html/**/*.js"
+ ]
+ },
+ "scripts": {
+ "lint": "node lint.js",
+ "lintcss": "npx stylelint src/**/*.wxss",
+ "build:weixin": "gulp build --mp-weixin",
+ "build:qq": "gulp build --mp-qq",
+ "build:baidu": "gulp build --mp-baidu",
+ "build:alipay": "gulp build --mp-alipay",
+ "build:toutiao": "gulp build --mp-toutiao",
+ "build:uni-app": "gulp build --uni-app",
+ "build": "gulp build --mp-weixin & gulp build --mp-qq & gulp build --mp-baidu & gulp build --mp-alipay & gulp build --mp-toutiao & gulp build --uni-app",
+ "watch:weixin": "gulp watch --mp-weixin --dev",
+ "watch:qq": "gulp watch --mp-qq --dev",
+ "watch:baidu": "gulp watch --mp-baidu --dev",
+ "watch:alipay": "gulp watch --mp-alipay --dev",
+ "watch:toutiao": "gulp watch --mp-toutiao --dev",
+ "watch:uni-app": "gulp watch --uni-app --dev",
+ "dev:weixin": "gulp dev --mp-weixin --dev",
+ "dev:qq": "gulp dev --mp-qq --dev",
+ "dev:baidu": "gulp dev --mp-baidu --dev",
+ "dev:alipay": "gulp dev --mp-alipay --dev",
+ "dev:toutiao": "gulp dev --mp-toutiao --dev",
+ "dev:uni-app": "gulp dev --uni-app --dev",
+ "test": "gulp dev --mp-weixin --dev && npx jest",
+ "coverage": "gulp dev --mp-weixin --dev && npx jest --coverage",
+ "coveralls": "npx coveralls < coverage/lcov.info",
+ "clean": "gulp clean --all",
+ "clean:dev": "gulp clean --all --dev"
+ },
+ "devDependencies": {
+ "@babel/preset-env": "^7.12.1",
+ "coveralls": "^3.1.0",
+ "gulp": "^4.0.0",
+ "gulp-babel": "^8.0.0",
+ "gulp-clean": "^0.4.0",
+ "gulp-clean-css": "^4.3.0",
+ "gulp-htmlmin": "^5.0.1",
+ "gulp-if": "^3.0.0",
+ "gulp-plumber": "^1.2.1",
+ "gulp-size": "^3.0.0",
+ "gulp-uglify": "^2.1.2",
+ "jest": "^26.6.1",
+ "miniprogram-simulate": "^1.2.7",
+ "standard": "^16.0.3",
+ "stylelint": "^13.7.2",
+ "stylelint-config-recess-order": "^2.3.0",
+ "stylelint-config-standard": "^20.0.0",
+ "through2": "^4.0.2",
+ "uglify-js": "^2.8.29"
+ },
+ "dependencies": {}
+}
diff --git a/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js b/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
index 1d986bd..1b3ee25 100644
--- a/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
+++ b/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js
@@ -1 +1,254 @@
-"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 {
+ document.body.onclick = () =>
+ uni.postMessage({
+ data: {
+ action: 'onClick'
+ }
+ })
+ uni.postMessage({
+ data: {
+ action: 'onJSBridgeReady'
+ }
+ })
+})
+
+let options
+let medias = []
+
+/**
+ * @description 获取标签的所有属性
+ * @param {Element} ele
+ */
+function getAttrs (ele) {
+ const attrs = Object.create(null)
+ for (let i = ele.attributes.length; i--;) {
+ attrs[ele.attributes[i].name] = ele.attributes[i].value
+ }
+ return attrs
+}
+
+/**
+ * @description 图片加载出错
+ */
+function onImgError () {
+ if (options[1]) {
+ this.src = options[1]
+ this.onerror = null
+ }
+ // 取消监听点击
+ this.onclick = null
+ this.ontouchstart = null
+ uni.postMessage({
+ data: {
+ action: 'onError',
+ source: 'img',
+ attrs: getAttrs(this)
+ }
+ })
+}
+
+/**
+ * @description 检查是否所有图片加载完毕
+ */
+function checkReady () {
+ window.unloadimgs -= 1
+ if (window.unloadimgs === 0) {
+ // 所有图片加载完毕
+ uni.postMessage({
+ data: {
+ action: 'onReady'
+ }
+ })
+ }
+}
+
+/**
+ * @description 创建 dom 结构
+ * @param {object[]} nodes 节点数组
+ * @param {Element} parent 父节点
+ * @param {string} namespace 命名空间
+ */
+function createDom (nodes, parent, namespace) {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i]
+ let ele
+ if (!node.type || node.type === 'node') {
+ let name = node.name
+ // svg 需要设置 namespace
+ if (name === 'svg') {
+ namespace = 'http://www.w3.org/2000/svg'
+ }
+ if (name === 'html' || name === 'body') {
+ name = 'div'
+ }
+ // 创建标签
+ if (!namespace) {
+ ele = document.createElement(name)
+ } else {
+ ele = document.createElementNS(namespace, name)
+ }
+ // 设置属性
+ for (const item in node.attrs) {
+ ele.setAttribute(item, node.attrs[item])
+ }
+ // 递归创建子节点
+ if (node.children) {
+ createDom(node.children, ele, namespace)
+ }
+
+ // 处理图片
+ if (name === 'img') {
+ window.unloadimgs += 1
+ ele.onload = checkReady
+ ele.onerror = checkReady
+ if (!ele.src && ele.getAttribute('data-src')) {
+ ele.src = ele.getAttribute('data-src')
+ }
+ if (!node.attrs.ignore) {
+ // 监听图片点击事件
+ ele.onclick = function (e) {
+ e.stopPropagation()
+ uni.postMessage({
+ data: {
+ action: 'onImgTap',
+ attrs: getAttrs(this)
+ }
+ })
+ }
+ }
+ if (options[2]) {
+ const image = new Image()
+ image.src = ele.src
+ ele.src = options[2]
+ image.onload = function () {
+ ele.src = this.src
+ }
+ image.onerror = function () {
+ ele.onerror()
+ }
+ }
+ ele.onerror = onImgError
+ } else if (name === 'a') {
+ // 处理链接
+ ele.addEventListener('click', function (e) {
+ e.stopPropagation()
+ e.preventDefault() // 阻止默认跳转
+ const href = this.getAttribute('href')
+ let offset
+ if (href && href[0] === '#') {
+ offset = (document.getElementById(href.substr(1)) || {}).offsetTop
+ }
+ uni.postMessage({
+ data: {
+ action: 'onLinkTap',
+ attrs: getAttrs(this),
+ offset
+ }
+ })
+ }, true)
+ } else if (name === 'video' || name === 'audio') {
+ // 处理音视频
+ medias.push(ele)
+ if (!node.attrs.autoplay && !node.attrs.controls) {
+ ele.setAttribute('controls', 'true')
+ }
+ ele.onplay = function () {
+ uni.postMessage({
+ data: {
+ action: 'onPlay'
+ }
+ })
+ if (options[3]) {
+ for (let i = 0; i < medias.length; i++) {
+ if (medias[i] !== this) {
+ medias[i].pause()
+ }
+ }
+ }
+ }
+ ele.onerror = function () {
+ uni.postMessage({
+ data: {
+ action: 'onError',
+ source: name,
+ attrs: getAttrs(this)
+ }
+ })
+ }
+ } else if (name === 'table' && options[4] && !ele.style.cssText.includes('inline')) {
+ // 处理表格
+ const div = document.createElement('div')
+ div.style.overflow = 'auto'
+ div.appendChild(ele)
+ ele = div
+ } else if (name === 'svg') {
+ namespace = undefined
+ }
+ } else {
+ ele = document.createTextNode(node.text.replace(/&/g, '&'))
+ }
+ parent.appendChild(ele)
+ }
+}
+
+// 设置 html 内容
+window.setContent = function (nodes, opts, append) {
+ const ele = document.getElementById('content')
+
+ // 容器样式
+ if (opts[0]) {
+ document.body.style.cssText = opts[0]
+ }
+
+ // 长按复制
+ if (!opts[5]) {
+ ele.style.userSelect = 'none'
+ }
+
+ if (!append) {
+ ele.innerHTML = '' // 不追加则先清空
+ medias = []
+ }
+
+ options = opts
+ window.unloadimgs = 0
+ const fragment = document.createDocumentFragment()
+ createDom(nodes, fragment)
+ ele.appendChild(fragment)
+
+ // 触发事件
+ let height = ele.scrollHeight
+ uni.postMessage({
+ data: {
+ action: 'onLoad',
+ height
+ }
+ })
+ if (!window.unloadimgs) {
+ uni.postMessage({
+ data: {
+ action: 'onReady',
+ height
+ }
+ })
+ }
+
+ clearInterval(window.timer)
+ window.timer = setInterval(() => {
+ if (ele.scrollHeight !== height) {
+ height = ele.scrollHeight
+ uni.postMessage({
+ data: {
+ action: 'onHeightChange',
+ height: height
+ }
+ })
+ }
+ }, 350)
+}
+
+// 回收计时器
+window.onunload = function () {
+ clearInterval(window.timer)
+}
diff --git a/uni_modules/mp-html/static/app-plus/mp-html/local.html b/uni_modules/mp-html/static/app-plus/mp-html/local.html
index 04355c9..2e23fb4 100644
--- a/uni_modules/mp-html/static/app-plus/mp-html/local.html
+++ b/uni_modules/mp-html/static/app-plus/mp-html/local.html
@@ -1 +1,33 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vue.config.js b/vue.config.js
index d213d17..ebf8b7f 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -2,44 +2,73 @@ module.exports = {
productionSourceMap: false,
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
- config.optimization.minimizer[0].options.terserOptions = {
- compress: {
- drop_console: true, // 去除所有console语句
- drop_debugger: true, // 去除debugger语句
- dead_code: true, // 移除死代码
- unused: true, // 移除未使用的变量
- toplevel: true, // 清理顶层作用域未使用的变量
- passes: 3, // 大幅增加压缩次数
- reduce_funcs: true, // 合并或移除未使用的函数
- collapse_vars: true, // 折叠定义后不再修改的变量
- sequences: true, // 合并连续的变量声明
- evaluate: true, // 提前计算常量表达式
- unsafe: true, // 已压缩代码启用更激进的压缩策略
- unsafe_comps: true, // 优化比较操作
- reduce_vars: true, // 合并或移除变量
- join_vars: true, // 合并变量声明
- side_effects: false, // 假设函数调用没有副作用
- pure_funcs: ['console.log', 'console.warn', 'console.error', 'console.info', 'console.debug'], // 标记这些函数为纯函数,可以安全移除
- pure_getters: true, // 假设getter函数没有副作用
- unsafe_math: true, // 允许不安全的数学优化
- unsafe_proto: true, // 允许不安全的原型优化
- unsafe_regexp: true, // 允许不安全的正则表达式优化
- conditionals: true, // 优化条件表达式
- comparisons: true, // 优化比较操作
- booleans: true, // 优化布尔表达式
- typeofs: true // 优化typeof操作
- },
- format: {
- ascii_only: true, // 确保输出ASCII字符
- comments: false, // 去除所有注释
- beautify: false // 不美化输出
- },
- // 为已压缩代码启用更严格的处理
- parse: {
- bare_returns: true, // 允许顶级return语句
- expression: false // 禁用表达式模式
+ console.log('生产环境下开启极限压缩');
+
+ // 确保minimizer数组存在
+ if (config.optimization && config.optimization.minimizer && Array.isArray(config.optimization.minimizer)) {
+ // 查找TerserWebpackPlugin实例,而不是依赖索引
+ const terserPluginIndex = config.optimization.minimizer.findIndex(plugin => {
+ return plugin.constructor && plugin.constructor.name === 'TerserWebpackPlugin' ||
+ (plugin.options && plugin.options.terserOptions);
+ });
+
+ if (terserPluginIndex !== -1) {
+ // 确保options对象存在
+ if (!config.optimization.minimizer[terserPluginIndex].options) {
+ config.optimization.minimizer[terserPluginIndex].options = {};
+ }
+
+ // 设置terserOptions - 极限压缩配置
+ config.optimization.minimizer[terserPluginIndex].options.terserOptions = {
+ compress: {
+ drop_console: true, // 去除所有console语句
+ drop_debugger: true, // 去除debugger语句
+ dead_code: true, // 移除死代码
+ unused: true, // 移除未使用的变量
+ toplevel: true, // 清理顶层作用域未使用的变量
+ passes: 5, // 增加压缩次数以获得更好的压缩效果
+ sequences: true, // 合并连续的变量声明
+ evaluate: true, // 提前计算常量表达式
+ reduce_vars: true, // 合并或移除变量
+ join_vars: true, // 合并变量声明
+ conditionals: true, // 优化条件表达式
+ comparisons: true, // 优化比较操作
+ booleans: true, // 优化布尔表达式
+ typeofs: true, // 优化typeof操作
+ collapse_vars: true, // 折叠定义后不再修改的变量
+ reduce_funcs: true, // 合并或移除未使用的函数
+ inline: true, // 内联简单函数
+ side_effects: true, // 移除有副作用的代码
+ keep_fargs: false, // 移除未使用的函数参数
+ keep_fnames: false, // 不保留函数名称
+ pure_funcs: ['console.log', 'console.warn', 'console.error', 'console.info'], // 标记为纯函数的console方法
+ pure_getters: true // 假设getter函数没有副作用
+ },
+ format: {
+ ascii_only: true, // 确保输出ASCII字符
+ comments: false, // 去除所有注释
+ beautify: false, // 不美化输出
+ ecma: 5, // 输出兼容ECMAScript 5
+ wrap_func_args: false, // 不包裹函数参数
+ bracketize: true, // 使用大括号包裹所有块
+ quote_style: 1 // 使用单引号
+ },
+ parse: {
+ ecma: 8, // 使用ECMAScript 8解析
+ bare_returns: true // 允许顶级return语句
+ },
+ mangle: {
+ toplevel: true, // 混淆顶层作用域的变量名
+ keep_fnames: false, // 不保留函数名称
+ keep_classnames: false // 不保留类名称
+ },
+ module: false, // 不是ES模块
+ sourceMap: false // 不生成source map
+ };
+
+ console.log('terserOptions配置完成');
}
- };
+ }
}
}
};
\ No newline at end of file