feat: 前端全量更新 - 含所有本次需求

- 移除秒合约页面及UI
- 合约页Tab改为Position Held/Transaction Record
- 存款/提款/转账页增加BTC/ETH
- 资产页重构(用户信息+盈亏+多语言)
- 注册页改版(匹配新设计稿)
- 日文全面翻译+首页菜单多语言
- 代理专属注册链接支持(?code=)
- 10个locale文件同步更新
This commit is contained in:
li
2026-03-30 20:14:20 +08:00
commit 5f40e4e1b5
605 changed files with 154871 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
## 3.0.12021-05-13
1. 跳转颜色和图标配置
2. 新增静默更新
## 3.0.02021-05-12
1. 支持uni_modules
+849
View File
@@ -0,0 +1,849 @@
// #ifdef APP-PLUS
import componentConfig from "../updataConfig.js"
const platform = uni.getSystemInfoSync().platform;
// 主颜色
const $mainColor = componentConfig.appUpdateColor ? componentConfig.appUpdateColor : "#FF5B78";
// 弹窗图标url
const $iconUrl = componentConfig.appUpdateIcon ? componentConfig.appUpdateIcon : "/static/ic_ar.png";
// 获取当前应用的版本号
export const getCurrentNo = function(callback) {
// 获取本地应用资源版本号
plus.runtime.getProperty(plus.runtime.appid, function(inf) {
callback && callback({
versionCode: inf.versionCode,
versionName: inf.version
});
});
}
// 从服务器下载应用资源包(wgt文件)
const getDownload = function(data) {
let dtask;
if(data.updateType == 'forcibly' || data.updateType == 'solicit'){
let popupData = {
progress: true,
buttonNum: 2
};
if(data.updateType == 'forcibly'){
popupData.buttonNum = 0;
}
let lastProgressValue = 0;
let popupObj = downloadPopup(popupData);
dtask = plus.downloader.createDownload(data.downloadUrl, {
filename: "_doc/update/"
}, function(download, status) {
// console.log('data对象',data);
if (status == 200) {
popupObj.change({
progressValue: 100,
progressTip:"Installing files...",
progress: true,
buttonNum: 0
});
plus.runtime.install(download.filename, {}, function() {
popupObj.change({
contentText: "App update complete",
buttonNum: 1,
progress: false
});
}, function(e) {
popupObj.cancel();
plus.nativeUI.alert("Failed install file[" + e.code + "]" + e.message);
});
} else {
popupObj.change({
contentText: "File download failed...",
buttonNum: 1,
progress: false
});
}
});
// console.log('对象dtask',dtask);
dtask.start();
dtask.addEventListener("statechanged", function(task, status) {
// console.log(task)
switch (task.state) {
case 1: // 开始
popupObj.change({
progressValue:0,
progressTip:"Ready download...",
progress: true
});
break;
case 2: // 已连接到服务器
popupObj.change({
progressValue:0,
progressTip:"Start download...",
progress: true
});
break;
case 3:
const progress = parseInt(task.downloadedSize / data.totalSize * 100);
// console.log(task);
// console.log('data对象',data);
if(progress - lastProgressValue >= 2){
lastProgressValue = progress;
popupObj.change({
progressValue:progress,
progressTip: "Download" + progress + "%",
progress: true
});
}
break;
}
});
// 取消下载
popupObj.cancelDownload = function(){
dtask && dtask.abort();
uni.showToast({
title: "Download canceled",
icon:"none"
});
}
// 重启APP
popupObj.reboot = function(){
plus.runtime.restart();
}
} else if(data.updateType == "silent"){
dtask = plus.downloader.createDownload(data.downloadUrl, {
filename: "_doc/update/"
}, function(download, status) {
if (status == 200) {
plus.runtime.install(download.filename, {}, function() {
console.log("App update complete");
}, function(e) {
plus.nativeUI.alert("Failed to install file[" + e.code + "]" + e.message);
});
} else {
plus.nativeUI.alert("File download failed...");
}
});
dtask.start();
}
}
// 文字换行
function drawtext(text, maxWidth) {
let textArr = text.split("");
let len = textArr.length;
// 上个节点
let previousNode = 0;
// 记录节点宽度
let nodeWidth = 0;
// 文本换行数组
let rowText = [];
// 如果是字母,侧保存长度
let letterWidth = 0;
// 汉字宽度
let chineseWidth = 14;
// otherFont宽度
let otherWidth = 7;
for (let i = 0; i < len; i++) {
if (/[\u4e00-\u9fa5]|[\uFE30-\uFFA0]/g.test(textArr[i])) {
if(letterWidth > 0){
if(nodeWidth + chineseWidth + letterWidth * otherWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i)
});
previousNode = i;
nodeWidth = chineseWidth;
letterWidth = 0;
} else {
nodeWidth += chineseWidth + letterWidth * otherWidth;
letterWidth = 0;
}
} else {
if(nodeWidth + chineseWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i)
});
previousNode = i;
nodeWidth = chineseWidth;
}else{
nodeWidth += chineseWidth;
}
}
} else {
if(/\n/g.test(textArr[i])){
rowText.push({
type: "break",
content: text.substring(previousNode, i)
});
previousNode = i + 1;
nodeWidth = 0;
letterWidth = 0;
}else if(textArr[i] == "\\" && textArr[i + 1] == "n"){
rowText.push({
type: "break",
content: text.substring(previousNode, i)
});
previousNode = i + 2;
nodeWidth = 0;
letterWidth = 0;
}else if(/[a-zA-Z0-9]/g.test(textArr[i])){
letterWidth += 1;
if(nodeWidth + letterWidth * otherWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i + 1 - letterWidth)
});
previousNode = i + 1 - letterWidth;
nodeWidth = letterWidth * otherWidth;
letterWidth = 0;
}
} else{
if(nodeWidth + otherWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i)
});
previousNode = i;
nodeWidth = otherWidth;
}else{
nodeWidth += otherWidth;
}
}
}
}
if (previousNode < len) {
rowText.push({
type: "text",
content: text.substring(previousNode, len)
});
}
return rowText;
}
// 是否更新弹窗
function updatePopup(data, callback) {
// 弹窗遮罩层
let maskLayer = new plus.nativeObj.View("maskLayer", { //先创建遮罩层
top: '0px',
left: '0px',
height: '100%',
width: '100%',
backgroundColor: 'rgba(0,0,0,0.5)'
});
// 以下为计算菜单的nview绘制布局,为固定算法,使用者无关关心
const screenWidth = plus.screen.resolutionWidth;
const screenHeight = plus.screen.resolutionHeight;
//弹窗容器宽度
const popupViewWidth = screenWidth * 0.7;
// 弹窗容器的Padding
const viewContentPadding = 20;
// 弹窗容器的宽度
const viewContentWidth = parseInt(popupViewWidth - (viewContentPadding * 2));
// 描述的列表
const descriptionList = drawtext(data.versionInfo, viewContentWidth);
// 弹窗容器高度
let popupViewHeight = 80 + 20 + 20 + 90 + 10;
let popupViewContentList = [{
src: $iconUrl,
id: "logo",
tag: "img",
position: {
top: "0px",
left: (popupViewWidth - 124) / 2 + "px",
width: "124px",
height: "80px",
}
},
{
tag: 'font',
id: 'title',
text: "New Version" + data.versionName,
textStyles: {
size: '18px',
color: "#333",
weight: "bold",
whiteSpace: "normal"
},
position: {
top: '90px',
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: "30px",
}
}];
const textHeight = 18;
let contentTop = 130;
descriptionList.forEach((item,index) => {
if(index > 0){
popupViewHeight += textHeight;
contentTop += textHeight;
}
popupViewContentList.push({
tag: 'font',
id: 'content' + index + 1,
text: item.content,
textStyles: {
size: '14px',
color: "#666",
lineSpacing: "50%",
align: "left"
},
position: {
top: contentTop + "px",
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: textHeight + "px",
}
});
if(item.type == "break"){
contentTop += 10;
popupViewHeight += 10;
}
});
if(data.updateType == "forcibly"){
popupViewContentList.push({
tag: 'rect', //绘制底边按钮
rectStyles:{
radius: "6px",
color: $mainColor
},
position:{
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: "30px"
}
});
popupViewContentList.push({
tag: 'font',
id: 'confirmText',
text: "Upgrade",
textStyles: {
size: '14px',
color: "#FFF",
lineSpacing: "0%",
},
position: {
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: "30px"
}
});
} else {
// 绘制底边按钮
popupViewContentList.push({
tag: 'rect',
id: 'cancelBox',
rectStyles: {
radius: "3px",
borderColor: "#f1f1f1",
borderWidth: "1px",
},
position: {
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px",
}
});
popupViewContentList.push({
tag: 'rect',
id: 'confirmBox',
rectStyles: {
radius: "3px",
color: $mainColor,
},
position: {
bottom: viewContentPadding + 'px',
left: ((viewContentWidth - viewContentPadding) / 2 + viewContentPadding * 2) + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px",
}
});
popupViewContentList.push({
tag: 'font',
id: 'cancelText',
text: "Not Upgrade",
textStyles: {
size: '14px',
color: "#666",
lineSpacing: "0%",
whiteSpace: "normal"
},
position: {
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px",
}
});
popupViewContentList.push({
tag: 'font',
id: 'confirmText',
text: "Upgrade",
textStyles: {
size: '14px',
color: "#FFF",
lineSpacing: "0%",
whiteSpace: "normal"
},
position: {
bottom: viewContentPadding + 'px',
left: ((viewContentWidth - viewContentPadding) / 2 + viewContentPadding * 2) + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px",
}
});
}
// 弹窗内容
let popupView = new plus.nativeObj.View("popupView", { //创建底部图标菜单
tag: "rect",
top: (screenHeight - popupViewHeight) / 2 + "px",
left: '15%',
height: popupViewHeight + "px",
width: "70%"
});
// 绘制白色背景
popupView.drawRect({
color: "#FFFFFF",
radius: "8px"
}, {
top: "40px",
height: popupViewHeight - 40 + "px",
});
popupView.draw(popupViewContentList);
popupView.addEventListener("click", function(e) {
let maxTop = popupViewHeight - viewContentPadding;
let maxLeft = popupViewWidth - viewContentPadding;
let buttonWidth = (viewContentWidth - viewContentPadding) / 2;
if (e.clientY > maxTop - 30 && e.clientY < maxTop) {
if(data.updateType == "forcibly"){
if(e.clientX > viewContentPadding && e.clientX < maxLeft){
// 立即升级
maskLayer.hide();
popupView.hide();
callback && callback();
}
} else {
// 暂不升级
if (e.clientX > viewContentPadding && e.clientX < maxLeft - buttonWidth - viewContentPadding) {
maskLayer.hide();
popupView.hide();
} else if (e.clientX > maxLeft - buttonWidth && e.clientX < maxLeft) {
// 立即升级
maskLayer.hide();
popupView.hide();
callback && callback();
}
}
}
});
if(data.updateType == "solicit"){
// 点击遮罩层
maskLayer.addEventListener("click", function() { //处理遮罩层点击
maskLayer.hide();
popupView.hide();
});
}
// 显示弹窗
maskLayer.show();
popupView.show();
}
// 文件下载的弹窗绘图
function downloadPopupDrawing(data){
// 以下为计算菜单的nview绘制布局,为固定算法,使用者无关关心
const screenWidth = plus.screen.resolutionWidth;
const screenHeight = plus.screen.resolutionHeight;
//弹窗容器宽度
const popupViewWidth = screenWidth * 0.7;
// 弹窗容器的Padding
const viewContentPadding = 20;
// 弹窗容器的宽度
const viewContentWidth = popupViewWidth - (viewContentPadding * 2);
// 弹窗容器高度
let popupViewHeight = viewContentPadding * 3 + 60;
let progressTip = data.progressTip || "Ready download...";
let contentText = data.contentText || "Updating for you, please wait";
let elementList = [
{
tag: 'rect', //背景色
color: '#FFFFFF',
rectStyles:{
radius: "8px"
}
},
{
tag: 'font',
id: 'title',
text: "Upgrade APP",
textStyles: {
size: '16px',
color: "#333",
weight: "bold",
verticalAlign: "middle",
whiteSpace: "normal"
},
position: {
top: viewContentPadding + 'px',
height: "30px",
}
},
{
tag: 'font',
id: 'content',
text: contentText,
textStyles: {
size: '14px',
color: "#333",
verticalAlign: "middle",
whiteSpace: "normal"
},
position: {
top: viewContentPadding * 2 + 30 + 'px',
height: "20px",
}
}
];
// 是否有进度条
if(data.progress){
popupViewHeight += viewContentPadding + 40;
elementList = elementList.concat([
{
tag: 'font',
id: 'progressValue',
text: progressTip,
textStyles: {
size: '14px',
color: $mainColor,
whiteSpace: "normal"
},
position: {
top: viewContentPadding * 4 + 20 + 'px',
height: "30px"
}
},
{
tag: 'rect', //绘制进度条背景
id: 'progressBg',
rectStyles:{
radius: "4px",
borderColor: "#f1f1f1",
borderWidth: "1px",
},
position:{
top: viewContentPadding * 4 + 60 + 'px',
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: "8px"
}
},
]);
}
if (data.buttonNum == 2) {
popupViewHeight += viewContentPadding + 30;
elementList = elementList.concat([
{
tag: 'rect', //绘制底边按钮
rectStyles:{
radius: "3px",
borderColor: "#f1f1f1",
borderWidth: "1px",
},
position:{
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px"
}
},
{
tag: 'rect', //绘制底边按钮
rectStyles:{
radius: "3px",
color: $mainColor
},
position:{
bottom: viewContentPadding + 'px',
left: ((viewContentWidth - viewContentPadding) / 2 + viewContentPadding * 2) + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px"
}
},
{
tag: 'font',
id: 'cancelText',
text: "Cancel",
textStyles: {
size: '14px',
color: "#666",
lineSpacing: "0%",
whiteSpace: "normal"
},
position: {
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px",
}
},
{
tag: 'font',
id: 'confirmText',
text: "Back",
textStyles: {
size: '14px',
color: "#FFF",
lineSpacing: "0%",
whiteSpace: "normal"
},
position: {
bottom: viewContentPadding + 'px',
left: ((viewContentWidth - viewContentPadding) / 2 + viewContentPadding * 2) + "px",
width: (viewContentWidth - viewContentPadding) / 2 + "px",
height: "30px",
}
}
]);
}
if (data.buttonNum == 1) {
popupViewHeight += viewContentPadding + 40;
elementList = elementList.concat([
{
tag: 'rect', //绘制底边按钮
rectStyles:{
radius: "6px",
color: $mainColor
},
position:{
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: "40px"
}
},
{
tag: 'font',
id: 'confirmText',
text: "Close",
textStyles: {
size: '14px',
color: "#FFF",
lineSpacing: "0%",
},
position: {
bottom: viewContentPadding + 'px',
left: viewContentPadding + "px",
width: viewContentWidth + "px",
height: "40px"
}
}
]);
}
return {
popupViewHeight:popupViewHeight,
popupViewWidth:popupViewWidth,
screenHeight:screenHeight,
viewContentWidth:viewContentWidth,
viewContentPadding:viewContentPadding,
elementList: elementList
};
}
// 文件下载的弹窗
function downloadPopup(data) {
// 弹窗遮罩层
let maskLayer = new plus.nativeObj.View("maskLayer", { //先创建遮罩层
top: '0px',
left: '0px',
height: '100%',
width: '100%',
backgroundColor: 'rgba(0,0,0,0.5)'
});
let popupViewData = downloadPopupDrawing(data);
// 弹窗内容
let popupView = new plus.nativeObj.View("popupView", { //创建底部图标菜单
tag: "rect",
top: (popupViewData.screenHeight - popupViewData.popupViewHeight) / 2 + "px",
left: '15%',
height: popupViewData.popupViewHeight + "px",
width: "70%",
});
let progressValue = 0;
let progressTip = 0;
let contentText = 0;
let buttonNum = 2;
if(data.buttonNum >= 0){
buttonNum = data.buttonNum;
}
popupView.draw(popupViewData.elementList);
let callbackData = {
change: function(res) {
let progressElement = [];
if(res.progressValue){
progressValue = res.progressValue;
// 绘制进度条
progressElement.push({
tag: 'rect', //绘制进度条背景
id: 'progressValueBg',
rectStyles:{
radius: "4px",
color: $mainColor
},
position:{
top: popupViewData.viewContentPadding * 4 + 60 + 'px',
left: popupViewData.viewContentPadding + "px",
width: popupViewData.viewContentWidth * (res.progressValue / 100) + "px",
height: "8px"
}
});
}
if(res.progressTip){
progressTip = res.progressTip;
progressElement.push({
tag: 'font',
id: 'progressValue',
text: res.progressTip,
textStyles: {
size: '14px',
color: $mainColor,
whiteSpace: "normal"
},
position: {
top: popupViewData.viewContentPadding * 4 + 20 + 'px',
height: "30px"
}
});
}
if(res.contentText){
contentText = res.contentText;
progressElement.push({
tag: 'font',
id: 'content',
text: res.contentText,
textStyles: {
size: '16px',
color: "#333",
whiteSpace: "normal"
},
position: {
top: popupViewData.viewContentPadding * 2 + 30 + 'px',
height: "30px",
}
});
}
if(res.buttonNum >= 0 && buttonNum != res.buttonNum){
buttonNum = res.buttonNum;
popupView.reset();
popupViewData = downloadPopupDrawing(Object.assign({
progressValue:progressValue,
progressTip:progressTip,
contentText:contentText,
},res));
let newElement = [];
popupViewData.elementList.map((item,index) => {
let have = false;
progressElement.forEach((childItem,childIndex) => {
if(item.id == childItem.id){
have = true;
}
});
if(!have){
newElement.push(item);
}
});
progressElement = newElement.concat(progressElement);
popupView.setStyle({
tag: "rect",
top: (popupViewData.screenHeight - popupViewData.popupViewHeight) / 2 + "px",
left: '15%',
height: popupViewData.popupViewHeight + "px",
width: "70%",
});
popupView.draw(progressElement);
}else{
popupView.draw(progressElement);
}
},
cancel: function() {
maskLayer.hide();
popupView.hide();
}
}
popupView.addEventListener("click", function(e) {
let maxTop = popupViewData.popupViewHeight - popupViewData.viewContentPadding;
let maxLeft = popupViewData.popupViewWidth - popupViewData.viewContentPadding;
if (e.clientY > maxTop - 40 && e.clientY < maxTop) {
if(buttonNum == 1){
// 单按钮
if (e.clientX > popupViewData.viewContentPadding && e.clientX < maxLeft) {
maskLayer.hide();
popupView.hide();
callbackData.reboot();
}
}else if(buttonNum == 2){
// 双按钮
let buttonWidth = (popupViewData.viewContentWidth - popupViewData.viewContentPadding) / 2;
if (e.clientX > popupViewData.viewContentPadding && e.clientX < maxLeft - buttonWidth - popupViewData.viewContentPadding) {
maskLayer.hide();
popupView.hide();
callbackData.cancelDownload();
} else if (e.clientX > maxLeft - buttonWidth && e.clientX < maxLeft) {
maskLayer.hide();
popupView.hide();
}
}
}
});
// 显示弹窗
maskLayer.show();
popupView.show();
// 改变进度条
return callbackData;
}
export default function(isPrompt = false) {
getCurrentNo(versionInfo => {
componentConfig.getServerNo(versionInfo, isPrompt, res => {
if (res.updateType == "forcibly" || res.updateType == "silent") {
if (/\.wgt$/i.test(res.downloadUrl)) {
getDownload(res);
} else if(/\.html$/i.test(res.downloadUrl)){
plus.runtime.openURL(res.downloadUrl);
} else {
if (platform == "android") {
getDownload(res);
} else {
plus.runtime.openURL(res.downloadUrl);
// 在App Store Connect中的App Store下的app信息,可找到appleId
// let appleId = '1600874657'
// plus.runtime.launchApplication({
// action: `itms-apps://itunes.apple.com/cn/app/id${appleId}?mt=8`
// }, function(e) {
// console.log('Open system default browser failed: ' + e.message);
// });
}
}
} else if(res.updateType == "solicit"){
updatePopup(res, function() {
if (/\.wgt$/i.test(res.downloadUrl)) {
getDownload(res);
} else if(/\.html$/i.test(res.downloadUrl)){
plus.runtime.openURL(res.downloadUrl);
} else {
if (platform == "android") {
getDownload(res);
} else {
plus.runtime.openURL(res.downloadUrl);
// 在App Store Connect中的App Store下的app信息,可找到appleId
// let appleId = '1600874657'
// plus.runtime.launchApplication({
// action: `itms-apps://itunes.apple.com/cn/app/id${appleId}?mt=8`
// }, function(e) {
// console.log('Open system default browser failed: ' + e.message);
// });
}
}
});
}
});
});
}
// #endif
+79
View File
@@ -0,0 +1,79 @@
{
"id": "zhouWei-APPUpdate",
"displayName": "APP版本更新、强制更新、静默更新、下载进度(wgt更新)",
"version": "3.0.1",
"description": "APP版本更新、强制更新、静默更新、漂亮弹窗、下载进度(wgt更新)",
"keywords": [
"APP版本更新",
"强制更新",
"版本更新",
"静默更新"
],
"repository": "https://github.com/zhouwei1994/uni-app-demo",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "465081029"
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "<uses-permission android:name=\\\"android.permission.INSTALL_PACKAGES\\\"/> \n<uses-permission android:name=\\\"android.permission.REQUEST_INSTALL_PACKAGES\\\"/>"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "n",
"Android Browser": "n",
"微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n"
},
"H5-pc": {
"Chrome": "n",
"IE": "n",
"Edge": "n",
"Firefox": "n",
"Safari": "n"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
+162
View File
@@ -0,0 +1,162 @@
# APP版本更新、强制更新、漂亮的更新界面、IOS更新(跳转IOS store)、wgt更新
| `QQ交流群(607391225)` | `微信交流群(加我好友备注"进群")` |
| ----------------------------|--------------------------- |
|![QQ交流群](http://qn.kemean.cn//upload/202004/14/15868301778472k7oubi6.png)|![微信交流群](https://qn.kemean.cn/upload/202010/13/weiXin_group_code.jpg)|
| QQ群号:607391225 |微信号:zhou0612wei|
### [点击跳转-插件示例](https://ext.dcloud.net.cn/plugin?id=2009)
### [点击跳转-5年的web前端开源的uni-app快速开发模板-下载看文档](https://ext.dcloud.net.cn/plugin?id=2009)
### 常见问题
1.安卓apk下载完成后没有更新APP?
答:问题是因为没有添加APP安装应用的权限,解决方法在`manifest.json`文件里面`APP模块权限配置``Android打包权限配置`勾选以下权限
```
<uses-permission android:name=\"android.permission.INSTALL_PACKAGES\"/>
<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>
```
若还有问题请看[安装apk无法执行的解决方案](https://ask.dcloud.net.cn/article/35703 "安装apk无法执行的解决方案")
2.APP更新后版本号没变,还是之前的版本号?
答:可能是更新的安装包没有升级版本号,`manifest.json`文件里面基本设置`应用版本号``应用版本名称`需要升高(保持一直减少问题)
3.APP更新后没有覆盖之前的APP?
答:可能是更新的安装包`包名`和APP的`包名`不一样
4.弹窗的图标不显示?
答:检查图片是不是放项目资源文件`static`,然后重新运行项目
5.版本号是在前端对比还是在后端接口对比?
答:当前案例是本地的版本号通过接口传递给后台,是后台对比的,若需要前端对比,请在接口返回数据的地方修改,不更新就不要调用`callback`方法
6.本地的版本号比接口的版本号高还弹窗升级窗口?
答:当前案例是本地的版本号通过接口传递给后台,后台对比是否需要升级,不需要升级就不要返回数据(特别是需要wgt更新的,建议这种方式)
### 第一步`关键`配置APP更新接口(可以参考上面的示例)
在项目目录下`config/componentConfig.js`里面如下配置
```
// 此方法是接口请求方法
import $http from '@/config/requestConfig'
export default {
// 发起ajax请求获取服务端版本号
getServerNo: (version, isPrompt = false, callback) => {
let httpData = {
version: version.versionCode,
// 版本名称
versionName: version.versionName,
// setupPage参数说明(判断用户是不是从设置页面点击的更新,如果是设置页面点击的更新,有不要用静默更新了,不然用户点击没反应很奇怪的)
setupPage: isPrompt
};
if (platform == "android") {
httpData.type = 1101;
} else {
httpData.type = 1102;
}
/* 接口入参说明
* version: 应用当前版本号(已自动获取)
* versionName: 应用当前版本名称(已自动获取)
* type:平台(1101是安卓,1102是IOS
*/
/****************以下是示例*******************/
// 可以用自己项目的请求方法(接口自己找后台要,插件不提供)
$http.get("api/common/v1/app_version", httpData,{
isPrompt: isPrompt
}).then(res => {
/* res的数据说明
* | 参数名称 | 一定返回 | 类型 | 描述
* | -------------|--------- | --------- | ------------- |
* | versionCode | y | int | 版本号 |
* | versionName | y | String | 版本名称 |
* | versionInfo | y | String | 版本信息 |
* | updateType | y | String | forcibly = 强制更新, solicit = 弹窗确认更新, silent = 静默更新 |
* | downloadUrl | y | String | 版本下载链接(IOS安装包更新请放跳转store应用商店链接,安卓apk和wgt文件放文件下载链接) |
*/
if (res && res.downloadUrl) {
// 兼容之前的版本(updateType是新版才有的参数)
if(res.updateType){
callback && callback(res);
} else {
if(res.forceUpdate){
res.updateType = "forcibly";
} else {
res.updateType = "solicit";
}
callback && callback(res);
}
} else if (isPrompt) {
uni.showToast({
title: "暂无新版本",
icon: "none"
});
}
});
/****************以上是示例*******************/
},
// 弹窗主颜色(不填默认粉色)
appUpdateColor: "f00",
// 弹窗图标(不填显示默认图标,链接配置示例如: '/static/demo/ic_attention.png'
appUpdateIcon: ''
}
```
### 第二步 使用方法
```
// App.vue页面
// #ifdef APP-PLUS
import APPUpdate from '@/uni_modules/zhouWei-APPUpdate/js_sdk/appUpdate';
// #endif
onLaunch: function(e) {
// #ifdef APP-PLUS
APPUpdate();
// #endif
}
```
### 第三步 添加APP安装应用的权限
在`manifest.json`文件里面`APP模块权限配置`的`Android打包权限配置`勾选以下权限
```
<uses-permission android:name=\"android.permission.INSTALL_PACKAGES\"/>
<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>
```
### 修改弹窗的主题色或弹窗图标
在`APPUpdate/index.js`里面上面`$mainColor`常量中定义主题颜色,`$iconUrl`常量中定义图标地址
### 检查APP是否有新版本(一般在设置页面使用)
```
// #ifdef APP-PLUS
import APPUpdate, { getCurrentNo } from '@/uni_modules/zhouWei-APPUpdate/js_sdk/appUpdate';
// #endif
export default {
data() {
return {
version: "" // 版本号
};
},
//第一次加载
onLoad(e) {
// #ifdef APP-PLUS
getCurrentNo(res => {
// 进页面获取当前APP版本号(用于页面显示)
this.version = res.version;
});
// #endif
},
//方法
methods: {
// 检查APP是否有新版本
onAPPUpdate() {
// true 没有新版本的时候有提示,默认:false
APPUpdate(true);
}
}
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+93
View File
@@ -0,0 +1,93 @@
/**** 此文件说明请看注释 *****/
// 可以用自己项目的请求方法
// 请求配置说明:https://ext.dcloud.net.cn/plugin?id=822
import {
req
} from '@/api/index';
/**** 结束 *****/
const platform = uni.getSystemInfoSync().platform;
export default {
// 发起ajax请求获取服务端版本号
getServerNo: (version, isPrompt = false, callback) => {
let httpData = {
version: version.versionCode,
// 版本名称
versionName: version.versionName,
// setupPage参数说明(判断用户是不是从设置页面点击的更新,如果是设置页面点击的更新,有不要用静默更新了,不然用户点击没反应很奇怪的)
setupPage: isPrompt
};
if (platform == "android") {
httpData.type = 1101;
} else {
httpData.type = 1102;
}
/* 接口入参说明
* version: 应用当前版本号(已自动获取)
* versionName: 应用当前版本名称(已自动获取)
* type:平台(1101是安卓,1102是IOS
*/
/****************以下是示例*******************/
// 可以用自己项目的请求方法(接口自己找后台要,插件不提供)
// console.log(httpData)
req("/publics/version").then(res => {
/* res的数据说明
* | 参数名称 | 一定返回 | 类型 | 描述
* | -------------|--------- | --------- | ------------- |
* | versionCode | y | int | 版本号 |
* | versionName | y | String | 版本名称 |
* | versionInfo | y | String | 版本信息 |
* | updateType | y | String | forcibly = 强制更新, solicit = 弹窗确认更新, silent = 静默更新 |
* | downloadUrl | y | String | 版本下载链接(IOS安装包更新请放跳转store应用商店链接,安卓apk和wgt文件放文件下载链接) |
*/
// console.log('下载链接', res)
if (res.code == 1) {
if (Number(res.data.newversion) > httpData.version) {
let temp = {
versionCode: res.data.newversion,
versionName: res.data.oldversion,
versionInfo: res.data.content,
updateType: 'solicit',
downloadUrl: res.data.downloadurl,
// downloadUrl:'https://dldir1.qq.com/weixin/android/weixin8016android2040.apk',
totalSize: res.data.packagesize
}
if (res.data.enforce == 1) {
temp.updateType = "forcibly";
} else {
temp.updateType = "solicit";
}
callback && callback(temp);
} else if (isPrompt) {
uni.showToast({
title: "You have the newest version",
icon: "none"
});
}
}
// if (res && res.data.downloadurl) {
// // 兼容之前的版本(updateType是新版才有的参数)
// if (res.updateType) {
// callback && callback(res.data);
// } else {
// if (res.data.enforce == 1) {
// res.data.updateType = "forcibly";
// } else {
// res.data.updateType = "solicit";
// }
// callback && callback(res.data);
// }
// } else if (isPrompt) {
// uni.showToast({
// title: "暂无新版本",
// icon: "none"
// });
// }
});
/****************以上是示例*******************/
},
// 弹窗主颜色(不填默认粉色)
appUpdateColor: "#FF5B78",
// 弹窗图标(不填显示默认图标,链接配置示例如: '/static/ic_ar.png'
appUpdateIcon: ''
}
+49
View File
@@ -0,0 +1,49 @@
/**
* 设置粘贴板数据
* @param {String} text 要设置的字符串
* 如果未设置参数,则清空数据
*/
function setClipboardText(text){
try{
var os = plus.os.name;
text = text||'';
if('iOS' == os){
// var UIPasteboard = plus.ios.importClass('UIPasteboard');
// var pasteboard = UIPasteboard.generalPasteboard();
// pasteboard.setValueforPasteboardType(text, 'public.utf8-plain-text');
var pasteboard = plus.ios.invoke('UIPasteboard', 'generalPasteboard');
plus.ios.invoke(pasteboard, 'setValue:forPasteboardType:', text, 'public.utf8-plain-text');
}else{
var main = plus.android.runtimeMainActivity();
// var Context = plus.android.importClass('android.content.Context');
// var clip = main.getSystemService(Context.CLIPBOARD_SERVICE);
var clip = main.getSystemService('clipboard');
plus.android.invoke(clip, 'setText', text);
}
}catch(e){
console.error('error @setClipboardText!!');
}
}
function getClipboardText(){
try{
var os = plus.os.name;
if('iOS' == os){
var pasteboard = plus.ios.invoke('UIPasteboard', 'generalPasteboard');
return plus.ios.invoke(pasteboard, 'valueForPasteboardType:', 'public.utf8-plain-text')
}else{
var main = plus.android.runtimeMainActivity();
var clip = main.getSystemService('clipboard');
return plus.android.invoke(clip, 'getText');
}
}catch(e){
console.error('error @getClipboardText!!');
}
}
module.exports = {
setText: setClipboardText,
getText: getClipboardText
}
+91
View File
@@ -0,0 +1,91 @@
let country = [
{
label: '台灣',
name:'台灣 +886',
value: '886'
},
{
label: 'England',
name:'England +44',
value: '44'
},
{
label: 'Монгол',
name:'Монгол +976',
value: '976'
},
{
label: 'Malaysia',
name:'Malaysia +60',
value: '60'
},
{
label: '대한민국',
name:'대한민국 +82',
value: '82'
},
{
label: 'La France',
name:'La France +33',
value: '33'
},
{
label: 'Italia',
name:'Italia +39',
value: '39'
},
{
label: 'Deutschland',
name:'Deutschland +49',
value: '49'
},
{
label: '日本',
name:'日本 +81',
value: '81'
},
// {
// label: 'ang Pilipinas',
// name:'ang Pilipinas +63',
// value: '63'
// },
// {
// label: 'Republic',
// name:'Republic +65',
// value: '65'
// },
// {
// label: 'Việt Nam',
// name:'Việt Nam +84',
// value: '84'
// },
// {
// label: 'မြန်မာ',
// name:'မြန်မာ +95',
// value: '95'
// },
// {
// label: 'ປະເທດລາວ',
// name:'ປະເທດລາວ +856',
// value: '856'
// },
// {
// label: 'ประเทศไทย',
// name:'ประเทศไทย +66',
// value: '66'
// },
// {
// label: 'বাংলা',
// name:'বাংলা +880',
// value: '880'
// },
// {
// label: '中国',
// name:'中国 +86',
// value: '86'
// },
]
export {
country
}
@@ -0,0 +1,121 @@
<!-- 自定义的下拉刷新view -->
<template>
<view class="refresher-container">
<!-- 这里的图片请换成自己项目的图片 -->
<!-- <image class="refresher-image" mode="" src="@/static/gif.gif"></image> -->
<text class="refresher-text">{{statusText}}</text>
</view>
</template>
<script>
export default {
data() {
return {
};
},
computed: {
statusText() {
// 获取当前语言
let currentLang = uni.getStorageSync('Language') || 'en-us';
// 语言映射:将项目语言代码映射到显示文本
const langMap = {
'zh-cn': {
0: '继续下拉刷新',
1: '松开立即刷新',
2: '正在刷新...',
3: '刷新成功'
},
'en-us': {
0: 'Pull down to refresh',
1: 'Release to refresh',
2: 'Refreshing...',
3: 'Refresh succeeded'
},
'jp': {
0: '引き続きプルダウンして更新',
1: '手を離してすぐに更新',
2: '更新中...',
3: '更新成功'
},
'kr': {
0: '계속 아래로 당겨 새로 고침',
1: '손을 놓고 즉시 새로 고침',
2: '새로 고침 중...',
3: '새로 고침 성공'
},
'mn': {
0: 'Үргэлжлүүлэн доош татаж шинэчлэх',
1: 'Гараа суллаад шууд шинэчлэх',
2: 'Шинэчилж байна...',
3: 'Шинэчлэлт амжилттай'
},
'ms': {
0: 'Teruskan tarik ke bawah untuk muat semula',
1: 'Lepaskan untuk muat semula',
2: 'Memuatkan semula...',
3: 'Muat semula berjaya'
},
'fre': {
0: 'Continuez à tirer vers le bas pour actualiser',
1: 'Relâchez pour actualiser',
2: 'Actualisation...',
3: 'Actualisation réussie'
},
'ger': {
0: 'Weiter nach unten ziehen zum Aktualisieren',
1: 'Loslassen zum Aktualisieren',
2: 'Aktualisierung...',
3: 'Aktualisierung erfolgreich'
},
'ily': {
0: 'Continua a trascinare verso il basso per aggiornare',
1: 'Rilascia per aggiornare',
2: 'Aggiornamento...',
3: 'Aggiornamento riuscito'
}
};
// 获取对应语言的文本,如果没有则使用英文
const texts = langMap[currentLang] || langMap['en-us'];
return texts[this.status] || '';
}
},
props: {
status: {
type: Number,
default: function() {
return 0;
},
},
}
}
</script>
<style scoped>
.refresher-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: 82rpx;
flex-direction: column;
align-items: center;
justify-content: center;
}
.refresher-image {
/* margin-top: 10rpx; */
width: 108rpx;
height: 82rpx;
}
.refresher-text {
margin-top: 10rpx;
font-size: 24rpx;
color: #3ECEB2;
/* background-image:-webkit-linear-gradient(45deg,#063AEE,#6415C2,#E40286); */
/* -webkit-background-clip:text; */
/* -webkit-text-fill-color:transparent; */
}
</style>
+633
View File
@@ -0,0 +1,633 @@
<template>
<view>
<slot v-if="!nodes.length" />
<!--#ifdef APP-PLUS-NVUE-->
<web-view id="_top" ref="web" :style="'margin-top:-2px;height:'+height+'px'" @onPostMessage="_message" />
<!--#endif-->
<!--#ifndef APP-PLUS-NVUE-->
<view id="_top" :style="showAm+(selectable?';user-select:text;-webkit-user-select:text':'')">
<!--#ifdef H5 || MP-360-->
<div :id="'rtf'+uid"></div>
<!--#endif-->
<!--#ifndef H5 || MP-360-->
<trees :nodes="nodes" :lazyLoad="lazyLoad" :loading="loadingImg" />
<!--#endif-->
</view>
<!--#endif-->
</view>
</template>
<script>
// #ifndef H5 || APP-PLUS-NVUE || MP-360
import trees from './libs/trees';
var cache = {},
// #ifdef MP-WEIXIN || MP-TOUTIAO
fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null,
// #endif
Parser = require('./libs/MpHtmlParser.js');
var dom;
// 计算 cache 的 key
function hash(str) {
for (var i = str.length, val = 5381; i--;)
val += (val << 5) + str.charCodeAt(i);
return val;
}
// #endif
// #ifdef H5 || APP-PLUS-NVUE || MP-360
var rpx = uni.getSystemInfoSync().windowWidth / 750,
cfg = require('./libs/config.js');
// #endif
// #ifdef APP-PLUS-NVUE
var weexDom = weex.requireModule('dom');
// #endif
/**
* Parser 富文本组件
* @tutorial https://github.com/jin-yufeng/Parser
* @property {String} html 富文本数据
* @property {Boolean} autopause 是否在播放一个视频时自动暂停其他视频
* @property {Boolean} autoscroll 是否自动给所有表格添加一个滚动层
* @property {Boolean} autosetTitle 是否自动将 title 标签中的内容设置到页面标题
* @property {Number} compress 压缩等级
* @property {String} domain 图片、视频等链接的主域名
* @property {Boolean} lazyLoad 是否开启图片懒加载
* @property {String} loadingImg 图片加载完成前的占位图
* @property {Boolean} selectable 是否开启长按复制
* @property {Object} tagStyle 标签的默认样式
* @property {Boolean} showWithAnimation 是否使用渐显动画
* @property {Boolean} useAnchor 是否使用锚点
* @property {Boolean} useCache 是否缓存解析结果
* @event {Function} parse 解析完成事件
* @event {Function} load dom 加载完成事件
* @event {Function} ready 所有图片加载完毕事件
* @event {Function} error 错误事件
* @event {Function} imgtap 图片点击事件
* @event {Function} linkpress 链接点击事件
* @author JinYufeng
* @version 20200615
* @listens MIT
*/
export default {
name: 'parser',
data() {
return {
// #ifdef H5 || MP-360
uid: this._uid,
// #endif
// #ifdef APP-PLUS-NVUE
height: 1,
// #endif
// #ifndef APP-PLUS-NVUE
showAm: '',
// #endif
nodes: []
}
},
// #ifndef H5 || APP-PLUS-NVUE || MP-360
components: {
trees
},
// #endif
props: {
html: String,
autopause: {
type: Boolean,
default: true
},
autoscroll: Boolean,
autosetTitle: {
type: Boolean,
default: true
},
// #ifndef H5 || APP-PLUS-NVUE || MP-360
compress: Number,
loadingImg: String,
useCache: Boolean,
// #endif
domain: String,
lazyLoad: Boolean,
selectable: Boolean,
tagStyle: Object,
showWithAnimation: Boolean,
useAnchor: Boolean
},
watch: {
html(html) {
this.setContent(html);
}
},
created() {
// 图片数组
this.imgList = [];
this.imgList.each = function(f) {
for (var i = 0, len = this.length; i < len; i++)
this.setItem(i, f(this[i], i, this));
}
this.imgList.setItem = function(i, src) {
if (i == void 0 || !src) return;
// #ifndef MP-ALIPAY || APP-PLUS
// 去重
if (src.indexOf('http') == 0 && this.includes(src)) {
var newSrc = src.split('://')[0];
for (var j = newSrc.length, c; c = src[j]; j++) {
if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
}
newSrc += src.substr(j);
return this[i] = newSrc;
}
// #endif
this[i] = src;
// 暂存 data src
if (src.includes('data:image')) {
var filePath, info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
if (!info) return;
// #ifdef MP-WEIXIN || MP-TOUTIAO
filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
fs && fs.writeFile({
filePath,
data: info[3],
encoding: info[2],
success: () => this[i] = filePath
})
// #endif
// #ifdef APP-PLUS
filePath = `_doc/parser_tmp/${Date.now()}.${info[1]}`;
var bitmap = new plus.nativeObj.Bitmap();
bitmap.loadBase64Data(src, () => {
bitmap.save(filePath, {}, () => {
bitmap.clear()
this[i] = filePath;
})
})
// #endif
}
}
},
mounted() {
// #ifdef H5 || MP-360
this.document = document.getElementById('rtf' + this._uid);
// #endif
// #ifndef H5 || APP-PLUS-NVUE || MP-360
if (dom) this.document = new dom(this);
// #endif
// #ifdef APP-PLUS-NVUE
this.document = this.$refs.web;
setTimeout(() => {
// #endif
if (this.html) this.setContent(this.html);
// #ifdef APP-PLUS-NVUE
}, 30)
// #endif
},
beforeDestroy() {
// #ifdef H5 || MP-360
if (this._observer) this._observer.disconnect();
// #endif
this.imgList.each(src => {
// #ifdef APP-PLUS
if (src && src.includes('_doc')) {
plus.io.resolveLocalFileSystemURL(src, entry => {
entry.remove();
});
}
// #endif
// #ifdef MP-WEIXIN || MP-TOUTIAO
if (src && src.includes(uni.env.USER_DATA_PATH))
fs && fs.unlink({
filePath: src
})
// #endif
})
clearInterval(this._timer);
},
methods: {
// #ifdef H5 || APP-PLUS-NVUE || MP-360
_handleHtml(html, append) {
if (!append) {
// 处理 tag-style 和 userAgentStyles
var style = '<style>@keyframes _show{0%{opacity:0}100%{opacity:1}}img{max-width:100%}';
for (var item in cfg.userAgentStyles)
style += `${item}{${cfg.userAgentStyles[item]}}`;
for (item in this.tagStyle)
style += `${item}{${this.tagStyle[item]}}`;
style += '</style>';
html = style + html;
}
// 处理 rpx
if (html.includes('rpx'))
html = html.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * rpx + 'px');
return html;
},
// #endif
setContent(html, append) {
// #ifdef APP-PLUS-NVUE
if (!html)
return this.height = 1;
if (append)
this.$refs.web.evalJs("var b=document.createElement('div');b.innerHTML='" + html.replace(/'/g, "\\'") +
"';document.getElementById('parser').appendChild(b)");
else {
html =
'<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><base href="' +
this.domain + '"><div id="parser"' + (this.selectable ? '>' : ' style="user-select:none">') + this._handleHtml(html).replace(/\n/g, '\\n') +
'</div><script>"use strict";function e(e){if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){var t={data:[e]};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(t):window.__dcloud_weex_.postMessage(JSON.stringify(t))}}' +
(this.showWithAnimation ? 'document.body.style.animation="_show .5s",' : '') +
'setTimeout(function(){e({action:"load",text:document.body.innerText,height:document.getElementById("parser").scrollHeight+16})},50);\x3c/script>';
this.$refs.web.evalJs("document.write('" + html.replace(/'/g, "\\'") + "');document.close()");
}
this.$refs.web.evalJs(
'var t=document.getElementsByTagName("title");t.length&&e({action:"getTitle",title:t[0].innerText});for(var o,n=document.getElementsByTagName("style"),r=0;o=n[r++];)o.innerHTML=o.innerHTML.replace(/body/g,"#parser");for(var i,a=document.getElementsByTagName("img"),s=[],c=0,l=0;i=a[c];c++)i.onerror=function(){' +
(cfg.errorImg ? 'this.src="' + cfg.errorImg + '",' : '') +
'e({action:"error",source:"img",target:this})},i.hasAttribute("ignore")||"A"==i.parentElement.nodeName||(i.i=l++,s.push(i.src),i.onclick=function(){e({action:"preview",img:{i:this.i,src:this.src}})});e({action:"getImgList",imgList:s});for(var d,u=document.getElementsByTagName("a"),g=0;d=u[g];g++)d.onclick=function(){var t,o=this.getAttribute("href");if("#"==o[0]){var n=document.getElementById(o.substr(1));n&&(t=n.offsetTop)}return e({action:"linkpress",href:o,offset:t}),!1};for(var m,f=document.getElementsByTagName("video"),h=0;m=f[h];h++)m.style.maxWidth="100%",m.onerror=function(){e({action:"error",source:"video",target:this})}' +
(this.autopause ? ',m.onplay=function(){for(var e,t=0;e=f[t];t++)e!=this&&e.pause()}' : '') +
';for(var v,y=document.getElementsByTagName("audio"),_=0;v=y[_];_++)v.onerror=function(){e({action:"error",source:"audio",target:this})};' +
(this.autoscroll ? 'for(var p,w=document.getElementsByTagName("table"),T=0;p=w[T];T++){var E=document.createElement("div");E.style.overflow="scroll",p.parentNode.replaceChild(E,p),E.appendChild(p)}' : '') +
'(function(){return new Promise(function(e){var t=document.getElementById("parser"),o=t.scrollHeight,n=setInterval(function(){o==t.scrollHeight?(clearInterval(n),e(o)):o=t.scrollHeight},500)})})().then(function(t){e({action:"ready",height:t+16})})'
)
this.nodes = [1];
// #endif
// #ifdef H5 || MP-360
if (!html) {
if (this.rtf && !append) this.rtf.parentNode.removeChild(this.rtf);
return;
}
var div = document.createElement('div');
if (!append) {
if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
this.rtf = div;
} else {
if (!this.rtf) this.rtf = div;
else this.rtf.appendChild(div);
}
div.innerHTML = this._handleHtml(html, append);
for (var styles = this.rtf.getElementsByTagName('style'), i = 0, style; style = styles[i++];) {
style.innerHTML = style.innerHTML.replace(/body/g, '#rtf' + this._uid);
style.setAttribute('scoped', 'true');
}
// 懒加载
if (!this._observer && this.lazyLoad && IntersectionObserver) {
this._observer = new IntersectionObserver(changes => {
for (let item, i = 0; item = changes[i++];) {
if (item.isIntersecting) {
item.target.src = item.target.getAttribute('data-src');
item.target.removeAttribute('data-src');
this._observer.unobserve(item.target);
}
}
}, {
rootMargin: '500px 0px 500px 0px'
})
}
var _ts = this;
// 获取标题
var title = this.rtf.getElementsByTagName('title');
if (title.length && this.autosetTitle)
uni.setNavigationBarTitle({
title: title[0].innerText
})
// 图片处理
this.imgList.length = 0;
var imgs = this.rtf.getElementsByTagName('img');
for (let i = 0, j = 0, img; img = imgs[i]; i++) {
var src = img.getAttribute('src');
if (this.domain && src) {
if (src[0] == '/') {
if (src[1] == '/')
img.src = (this.domain.includes('://') ? this.domain.split('://')[0] : '') + ':' + src;
else img.src = this.domain + src;
} else if (!src.includes('://')) img.src = this.domain + '/' + src;
}
if (!img.hasAttribute('ignore') && img.parentElement.nodeName != 'A') {
img.i = j++;
_ts.imgList.push(img.src || img.getAttribute('data-src'));
img.onclick = function() {
var preview = true;
this.ignore = () => preview = false;
_ts.$emit('imgtap', this);
if (preview) {
uni.previewImage({
current: this.i,
urls: _ts.imgList
});
}
}
}
img.onerror = function() {
if (cfg.errorImg)
_ts.imgList[this.i] = this.src = cfg.errorImg;
_ts.$emit('error', {
source: 'img',
target: this
});
}
if (_ts.lazyLoad && this._observer && img.src && img.i != 0) {
img.setAttribute('data-src', img.src);
img.removeAttribute('src');
this._observer.observe(img);
}
}
// 链接处理
var links = this.rtf.getElementsByTagName('a');
for (var link of links) {
link.onclick = function() {
var jump = true,
href = this.getAttribute('href');
_ts.$emit('linkpress', {
href,
ignore: () => jump = false
});
if (jump && href) {
if (href[0] == '#') {
if (_ts.useAnchor) {
_ts.navigateTo({
id: href.substr(1)
})
}
} else if (href.indexOf('http') == 0 || href.indexOf('//') == 0)
return true;
else
uni.navigateTo({
url: href
})
}
return false;
}
}
// 视频处理
var videos = this.rtf.getElementsByTagName('video');
_ts.videoContexts = videos;
for (let video, i = 0; video = videos[i++];) {
video.style.maxWidth = '100%';
video.onerror = function() {
_ts.$emit('error', {
source: 'video',
target: this
});
}
video.onplay = function() {
if (_ts.autopause)
for (let item, i = 0; item = _ts.videoContexts[i++];)
if (item != this) item.pause();
}
}
// 音频处理
var audios = this.rtf.getElementsByTagName('audio');
for (var audio of audios)
audio.onerror = function() {
_ts.$emit('error', {
source: 'audio',
target: this
});
}
// 表格处理
if (this.autoscroll) {
var tables = this.rtf.getElementsByTagName('table');
for (var table of tables) {
let div = document.createElement('div');
div.style.overflow = 'scroll';
table.parentNode.replaceChild(div, table);
div.appendChild(table);
}
}
if (!append) this.document.appendChild(this.rtf);
this.$nextTick(() => {
this.nodes = [1];
this.$emit('load');
});
setTimeout(() => this.showAm = '', 500);
// #endif
// #ifndef APP-PLUS-NVUE
// #ifndef H5 || MP-360
var nodes;
if (!html) return this.nodes = [];
var parser = new Parser(html, this);
// 缓存读取
if (this.useCache) {
var hashVal = hash(html);
if (cache[hashVal])
nodes = cache[hashVal];
else {
nodes = parser.parse();
cache[hashVal] = nodes;
}
} else nodes = parser.parse();
this.$emit('parse', nodes);
if (append) this.nodes = this.nodes.concat(nodes);
else this.nodes = nodes;
if (nodes.length && nodes.title && this.autosetTitle)
uni.setNavigationBarTitle({
title: nodes.title
})
if (this.imgList) this.imgList.length = 0;
this.videoContexts = [];
this.$nextTick(() => {
(function f(cs) {
for (var i = cs.length; i--;) {
if (cs[i].top) {
cs[i].controls = [];
cs[i].init();
f(cs[i].$children);
}
}
})(this.$children)
this.$emit('load');
})
// #endif
var height;
clearInterval(this._timer);
this._timer = setInterval(() => {
// #ifdef H5 || MP-360
this.rect = this.rtf.getBoundingClientRect();
// #endif
// #ifndef H5 || MP-360
uni.createSelectorQuery().in(this)
.select('#_top').boundingClientRect().exec(res => {
if (!res) return;
this.rect = res[0];
// #endif
if (this.rect.height == height) {
this.$emit('ready', this.rect)
clearInterval(this._timer);
}
height = this.rect.height;
// #ifndef H5 || MP-360
});
// #endif
}, 350);
if (this.showWithAnimation && !append) this.showAm = 'animation:_show .5s';
// #endif
},
getText(ns = this.nodes) {
var txt = '';
// #ifdef APP-PLUS-NVUE
txt = this._text;
// #endif
// #ifdef H5 || MP-360
txt = this.rtf.innerText;
// #endif
// #ifndef H5 || APP-PLUS-NVUE || MP-360
for (var i = 0, n; n = ns[i++];) {
if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
.replace(/&amp;/g, '&');
else if (n.type == 'br') txt += '\n';
else {
// 块级标签前后加换行
var block = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] >
'0' && n.name[1] < '7');
if (block && txt && txt[txt.length - 1] != '\n') txt += '\n';
if (n.children) txt += this.getText(n.children);
if (block && txt[txt.length - 1] != '\n') txt += '\n';
else if (n.name == 'td' || n.name == 'th') txt += '\t';
}
}
// #endif
return txt;
},
navigateTo(obj) {
if (!this.useAnchor)
return obj.fail && obj.fail({
errMsg: 'Anchor is disabled'
})
// #ifdef APP-PLUS-NVUE
if (!obj.id)
weexDom.scrollToElement(this.$refs.web);
else
this.$refs.web.evalJs('var pos=document.getElementById("' + obj.id +
'");if(pos)post({action:"linkpress",href:"#",offset:pos.offsetTop+' + (obj.offset || 0) + '})');
obj.success && obj.success({
errMsg: 'pageScrollTo:ok'
});
// #endif
// #ifdef H5 || MP-360
if (!obj.id) {
window.scrollTo(0, this.rtf.offsetTop);
return obj.success && obj.success({
errMsg: 'pageScrollTo:ok'
});
}
var target = document.getElementById(obj.id);
if (!target) return obj.fail && obj.fail({
errMsg: 'Label not found'
});
obj.scrollTop = this.rtf.offsetTop + target.offsetTop + (obj.offset || 0);
uni.pageScrollTo(obj);
// #endif
// #ifndef H5 || APP-PLUS-NVUE || MP-360
var d = ' ';
// #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
d = '>>>';
// #endif
uni.createSelectorQuery().in(this).select('#_top' + (obj.id ? d + '#' + obj.id + ',#_top' + d + '.' + obj.id : '')).boundingClientRect()
.selectViewport().scrollOffset().exec(res => {
if (!res || !res[0])
return obj.fail && obj.fail({
errMsg: 'Label not found'
});
obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
// #ifdef MP-ALIPAY
obj.duration = 300;
my.
// #endif
// #ifndef MP-ALIPAY
uni.
// #endif
pageScrollTo(obj);
})
// #endif
},
getVideoContext(id) {
// #ifndef APP-PLUS-NVUE
if (!id) return this.videoContexts;
else
for (var i = this.videoContexts.length; i--;)
if (this.videoContexts[i].id == id) return this.videoContexts[i];
// #endif
},
// #ifdef APP-PLUS-NVUE
_message(e) {
// 接收 web-view 消息
var data = e.detail.data[0];
if (data.action == 'load') {
this.$emit('load');
this.height = data.height;
this._text = data.text;
} else if (data.action == 'getTitle') {
if (this.autosetTitle)
uni.setNavigationBarTitle({
title: data.title
})
} else if (data.action == 'getImgList') {
this.imgList.length = 0;
for (var i = data.imgList.length; i--;)
this.imgList.setItem(i, data.imgList[i]);
} else if (data.action == 'preview') {
var preview = true;
data.img.ignore = () => preview = false;
this.$emit('imgtap', data.img);
if (preview)
uni.previewImage({
current: data.img.i,
urls: this.imgList
})
} else if (data.action == 'linkpress') {
var jump = true,
href = data.href;
this.$emit('linkpress', {
href,
ignore: () => jump = false
})
if (jump && href) {
if (href[0] == '#') {
if (this.useAnchor)
weexDom.scrollToElement(this.$refs.web, {
offset: data.offset
})
} else if (href.includes('://'))
plus.runtime.openWeb(href);
else
uni.navigateTo({
url: href
})
}
} else if (data.action == 'error') {
if (data.source == 'img' && cfg.errorImg)
this.imgList.setItem(data.target.i, cfg.errorImg);
this.$emit('error', {
source: data.source,
target: data.target
})
} else if (data.action == 'ready') {
this.height = data.height;
this.$nextTick(() => {
uni.createSelectorQuery().in(this).select('#_top').boundingClientRect().exec(res => {
this.rect = res[0];
this.$emit('ready', res[0]);
})
})
}
},
// #endif
}
}
</script>
<style>
@keyframes _show {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* #ifdef MP-WEIXIN */
:host {
display: block;
/* overflow: scroll; */
-webkit-overflow-scrolling: touch;
}
/* #endif */
</style>
+97
View File
@@ -0,0 +1,97 @@
const cfg = require('./config.js'),
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
function CssHandler(tagStyle) {
var styles = Object.assign(Object.create(null), cfg.userAgentStyles);
for (var item in tagStyle)
styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
this.styles = styles;
}
CssHandler.prototype.getStyle = function(data) {
this.styles = new parser(data, this.styles).parse();
}
CssHandler.prototype.match = function(name, attrs) {
var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
if (attrs.class) {
var items = attrs.class.split(' ');
for (var i = 0, item; item = items[i]; i++)
if (tmp = this.styles['.' + item])
matched += tmp + ';';
}
if (tmp = this.styles['#' + attrs.id])
matched += tmp + ';';
return matched;
}
module.exports = CssHandler;
function parser(data, init) {
this.data = data;
this.floor = 0;
this.i = 0;
this.list = [];
this.res = init;
this.state = this.Space;
}
parser.prototype.parse = function() {
for (var c; c = this.data[this.i]; this.i++)
this.state(c);
return this.res;
}
parser.prototype.section = function() {
return this.data.substring(this.start, this.i);
}
// 状态机
parser.prototype.Space = function(c) {
if (c == '.' || c == '#' || isLetter(c)) {
this.start = this.i;
this.state = this.Name;
} else if (c == '/' && this.data[this.i + 1] == '*')
this.Comment();
else if (!cfg.blankChar[c] && c != ';')
this.state = this.Ignore;
}
parser.prototype.Comment = function() {
this.i = this.data.indexOf('*/', this.i) + 1;
if (!this.i) this.i = this.data.length;
this.state = this.Space;
}
parser.prototype.Ignore = function(c) {
if (c == '{') this.floor++;
else if (c == '}' && !--this.floor) this.state = this.Space;
}
parser.prototype.Name = function(c) {
if (cfg.blankChar[c]) {
this.list.push(this.section());
this.state = this.NameSpace;
} else if (c == '{') {
this.list.push(this.section());
this.Content();
} else if (c == ',') {
this.list.push(this.section());
this.Comma();
} else if (!isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
this.state = this.Ignore;
}
parser.prototype.NameSpace = function(c) {
if (c == '{') this.Content();
else if (c == ',') this.Comma();
else if (!cfg.blankChar[c]) this.state = this.Ignore;
}
parser.prototype.Comma = function() {
while (cfg.blankChar[this.data[++this.i]]);
if (this.data[this.i] == '{') this.Content();
else {
this.start = this.i--;
this.state = this.Name;
}
}
parser.prototype.Content = function() {
this.start = ++this.i;
if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
var content = this.section();
for (var i = 0, item; item = this.list[i++];)
if (this.res[item]) this.res[item] += ';' + content;
else this.res[item] = content;
this.list = [];
this.state = this.Space;
}
+534
View File
@@ -0,0 +1,534 @@
/**
* html 解析器
* @tutorial https://github.com/jin-yufeng/Parser
* @version 20200615
* @author JinYufeng
* @listens MIT
*/
const cfg = require('./config.js'),
blankChar = cfg.blankChar,
CssHandler = require('./CssHandler.js'),
windowWidth = uni.getSystemInfoSync().windowWidth;
var emoji;
function MpHtmlParser(data, options = {}) {
this.attrs = {};
this.CssHandler = new CssHandler(options.tagStyle, windowWidth);
this.data = data;
this.domain = options.domain;
this.DOM = [];
this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
options.prot = (this.domain || '').includes('://') ? this.domain.split('://')[0] : 'http';
this.options = options;
this.state = this.Text;
this.STACK = [];
// 工具函数
this.bubble = () => {
for (var i = this.STACK.length, item; item = this.STACK[--i];) {
if (cfg.richOnlyTags[item.name]) {
if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
return false;
}
item.c = 1;
}
return true;
}
this.decode = (val, amp) => {
var i = -1,
j, en;
while (1) {
if ((i = val.indexOf('&', i + 1)) == -1) break;
if ((j = val.indexOf(';', i + 2)) == -1) break;
if (val[i + 1] == '#') {
en = parseInt((val[i + 2] == 'x' ? '0' : '') + val.substring(i + 2, j));
if (!isNaN(en)) val = val.substr(0, i) + String.fromCharCode(en) + val.substr(j + 1);
} else {
en = val.substring(i + 1, j);
if (cfg.entities[en] || en == amp)
val = val.substr(0, i) + (cfg.entities[en] || '&') + val.substr(j + 1);
}
}
return val;
}
this.getUrl = url => {
if (url[0] == '/') {
if (url[1] == '/') url = this.options.prot + ':' + url;
else if (this.domain) url = this.domain + url;
} else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
url = this.domain + '/' + url;
return url;
}
this.isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
this.section = () => this.data.substring(this.start, this.i);
this.parent = () => this.STACK[this.STACK.length - 1];
this.siblings = () => this.STACK.length ? this.parent().children : this.DOM;
}
MpHtmlParser.prototype.parse = function() {
if (emoji) this.data = emoji.parseEmoji(this.data);
for (var c; c = this.data[this.i]; this.i++)
this.state(c);
if (this.state == this.Text) this.setText();
while (this.STACK.length) this.popNode(this.STACK.pop());
return this.DOM;
}
// 设置属性
MpHtmlParser.prototype.setAttr = function() {
var name = this.attrName.toLowerCase(),
val = this.attrVal;
if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
else if (val) {
if (name == 'src' || (name == 'data-src' && !this.attrs.src)) this.attrs.src = this.getUrl(this.decode(val, 'amp'));
else if (name == 'href' || name == 'style') this.attrs[name] = this.decode(val, 'amp');
else if (name.substr(0, 5) != 'data-') this.attrs[name] = val;
}
this.attrVal = '';
while (blankChar[this.data[this.i]]) this.i++;
if (this.isClose()) this.setNode();
else {
this.start = this.i;
this.state = this.AttrName;
}
}
// 设置文本节点
MpHtmlParser.prototype.setText = function() {
var back, text = this.section();
if (!text) return;
text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
if (back) {
this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
let j = this.start + text.length;
for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
return;
}
if (!this.pre) {
// 合并空白符
var tmp = [];
for (let i = text.length, c; c = text[--i];)
if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
text = tmp.join('');
}
this.siblings().push({
type: 'text',
text: this.decode(text)
});
}
// 设置元素节点
MpHtmlParser.prototype.setNode = function() {
var node = {
name: this.tagName.toLowerCase(),
attrs: this.attrs
},
close = cfg.selfClosingTags[node.name];
this.attrs = {};
if (!cfg.ignoreTags[node.name]) {
// 处理属性
var attrs = node.attrs,
style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
styleObj = {};
if (attrs.id) {
if (this.options.compress & 1) attrs.id = void 0;
else if (this.options.useAnchor) this.bubble();
}
if ((this.options.compress & 2) && attrs.class) attrs.class = void 0;
switch (node.name) {
case 'a':
case 'ad': // #ifdef APP-PLUS
case 'iframe':
// #endif
this.bubble();
break;
case 'font':
if (attrs.color) {
styleObj['color'] = attrs.color;
attrs.color = void 0;
}
if (attrs.face) {
styleObj['font-family'] = attrs.face;
attrs.face = void 0;
}
if (attrs.size) {
var size = parseInt(attrs.size);
if (size < 1) size = 1;
else if (size > 7) size = 7;
var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
styleObj['font-size'] = map[size - 1];
attrs.size = void 0;
}
break;
case 'embed':
// #ifndef APP-PLUS
var src = node.attrs.src || '',
type = node.attrs.type || '';
if (type.includes('video') || src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8'))
node.name = 'video';
else if (type.includes('audio') || src.includes('.m4a') || src.includes('.wav') || src.includes('.mp3') || src.includes(
'.aac'))
node.name = 'audio';
else break;
if (node.attrs.autostart)
node.attrs.autoplay = 'T';
node.attrs.controls = 'T';
// #endif
// #ifdef APP-PLUS
this.bubble();
break;
// #endif
case 'video':
case 'audio':
if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
else this[`${node.name}Num`]++;
if (node.name == 'video') {
if (this.videoNum > 3)
node.lazyLoad = 1;
if (attrs.width) {
styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px');
attrs.width = void 0;
}
if (attrs.height) {
styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px');
attrs.height = void 0;
}
}
attrs.source = [];
if (attrs.src) {
attrs.source.push(attrs.src);
attrs.src = void 0;
}
this.bubble();
break;
case 'td':
case 'th':
if (attrs.colspan || attrs.rowspan)
for (var k = this.STACK.length, item; item = this.STACK[--k];)
if (item.name == 'table') {
item.c = void 0;
break;
}
}
if (attrs.align) {
styleObj['text-align'] = attrs.align;
attrs.align = void 0;
}
// 压缩 style
var styles = style.split(';');
style = '';
for (var i = 0, len = styles.length; i < len; i++) {
var info = styles[i].split(':');
if (info.length < 2) continue;
let key = info[0].trim().toLowerCase(),
value = info.slice(1).join(':').trim();
if (value.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value.includes(
'safe'))
style += `;${key}:${value}`;
else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
styleObj[key] = value;
}
if (node.name == 'img') {
if (attrs.src && !attrs.ignore) {
if (this.bubble())
attrs.i = (this.imgNum++).toString();
else attrs.ignore = 'T';
}
if (attrs.ignore) {
style += ';-webkit-touch-callout:none';
styleObj['max-width'] = '100%';
}
var width;
if (styleObj.width) width = styleObj.width;
else if (attrs.width) width = attrs.width.includes('%') ? attrs.width : attrs.width + 'px';
if (width) {
styleObj.width = width;
attrs.width = '100%';
if (parseInt(width) > windowWidth) {
styleObj.height = '';
if (attrs.height) attrs.height = void 0;
}
}
if (styleObj.height) {
attrs.height = styleObj.height;
styleObj.height = '';
} else if (attrs.height && !attrs.height.includes('%'))
attrs.height += 'px';
}
for (var key in styleObj) {
var value = styleObj[key];
if (!value) continue;
if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
// 填充链接
if (value.includes('url')) {
var j = value.indexOf('(');
if (j++ != -1) {
while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
value = value.substr(0, j) + this.getUrl(value.substr(j));
}
}
// 转换 rpx
else if (value.includes('rpx'))
value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px');
else if (key == 'white-space' && value.includes('pre'))
this.pre = node.pre = true;
style += `;${key}:${value}`;
}
style = style.substr(1);
if (style) attrs.style = style;
if (!close) {
node.children = [];
if (node.name == 'pre' && cfg.highlight) {
this.remove(node);
this.pre = node.pre = true;
}
this.siblings().push(node);
this.STACK.push(node);
} else if (!cfg.filter || cfg.filter(node, this) != false)
this.siblings().push(node);
} else {
if (!close) this.remove(node);
else if (node.name == 'source') {
var parent = this.parent();
if (parent && (parent.name == 'video' || parent.name == 'audio') && node.attrs.src)
parent.attrs.source.push(node.attrs.src);
} else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
}
if (this.data[this.i] == '/') this.i++;
this.start = this.i + 1;
this.state = this.Text;
}
// 移除标签
MpHtmlParser.prototype.remove = function(node) {
var name = node.name,
j = this.i;
// 处理 svg
var handleSvg = () => {
var src = this.data.substring(j, this.i + 1);
if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
var i = j;
while (this.data[j] != '<') j--;
src = this.data.substring(j, i) + src;
var parent = this.parent();
if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
this.siblings().push({
name: 'img',
attrs: {
src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
style: (/vertical[^;]+/.exec(node.attrs.style) || []).shift(),
ignore: 'T'
}
})
}
if (node.name == 'svg' && this.data[j] == '/') return handleSvg(this.i++);
while (1) {
if ((this.i = this.data.indexOf('</', this.i + 1)) == -1) {
if (name == 'pre' || name == 'svg') this.i = j;
else this.i = this.data.length;
return;
}
this.start = (this.i += 2);
while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
if (this.section().toLowerCase() == name) {
// 代码块高亮
if (name == 'pre') {
this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) + this.data
.substr(this.i - 5);
return this.i = j;
} else if (name == 'style')
this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
else if (name == 'title')
this.DOM.title = this.data.substring(j + 1, this.i - 7);
if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
if (name == 'svg') handleSvg();
return;
}
}
}
// 节点出栈处理
MpHtmlParser.prototype.popNode = function(node) {
// 空白符处理
if (node.pre) {
node.pre = this.pre = void 0;
for (let i = this.STACK.length; i--;)
if (this.STACK[i].pre)
this.pre = true;
}
var siblings = this.siblings(),
len = siblings.length,
childs = node.children;
if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
return siblings.pop();
var attrs = node.attrs;
// 替换一些标签名
if (cfg.blockTags[node.name]) node.name = 'div';
else if (!cfg.trustTags[node.name]) node.name = 'span';
// 去除块标签前后空串
if (node.name == 'div' || node.name == 'p' || node.name[0] == 't') {
if (len > 1 && siblings[len - 2].text == ' ')
siblings.splice(--len - 1, 1);
if (childs.length && childs[childs.length - 1].text == ' ')
childs.pop();
}
// 处理列表
if (node.c && (node.name == 'ul' || node.name == 'ol')) {
if ((node.attrs.style || '').includes('list-style:none')) {
for (let i = 0, child; child = childs[i++];)
if (child.name == 'li')
child.name = 'div';
} else if (node.name == 'ul') {
var floor = 1;
for (let i = this.STACK.length; i--;)
if (this.STACK[i].name == 'ul') floor++;
if (floor != 1)
for (let i = childs.length; i--;)
childs[i].floor = floor;
} else {
for (let i = 0, num = 1, child; child = childs[i++];)
if (child.name == 'li') {
child.type = 'ol';
child.num = ((num, type) => {
if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
if (type == 'i' || type == 'I') {
num = (num - 1) % 99 + 1;
var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
if (type == 'i') return res.toLowerCase();
return res;
}
return num;
})(num++, attrs.type) + '.';
}
}
}
// 处理表格的边框
if (node.name == 'table') {
var padding = attrs.cellpadding,
spacing = attrs.cellspacing,
border = attrs.border;
if (node.c) {
this.bubble();
attrs.style = (attrs.style || '') + ';display:table';
if (!padding) padding = 2;
if (!spacing) spacing = 2;
}
if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
if (border || padding || node.c)
(function f(ns) {
for (var i = 0, n; n = ns[i]; i++) {
if (n.type == 'text') continue;
var style = n.attrs.style || '';
if (node.c && n.name[0] == 't') {
n.c = 1;
style += ';display:table-' + (n.name == 'th' || n.name == 'td' ? 'cell' : (n.name == 'tr' ? 'row' : 'row-group'));
}
if (n.name == 'th' || n.name == 'td') {
if (border) style = `border:${border}px solid gray;${style}`;
if (padding) style = `padding:${padding}px;${style}`;
} else f(n.children || []);
if (style) n.attrs.style = style;
}
})(childs)
if (this.options.autoscroll) {
var table = Object.assign({}, node);
node.name = 'div';
node.attrs = {
style: 'overflow:scroll'
}
node.children = [table];
}
}
this.CssHandler.pop && this.CssHandler.pop(node);
// 自动压缩
if (node.name == 'div' && !Object.keys(attrs).length && childs.length == 1 && childs[0].name == 'div')
siblings[len - 1] = childs[0];
}
// 状态机
MpHtmlParser.prototype.Text = function(c) {
if (c == '<') {
var next = this.data[this.i + 1],
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
if (isLetter(next)) {
this.setText();
this.start = this.i + 1;
this.state = this.TagName;
} else if (next == '/') {
this.setText();
if (isLetter(this.data[++this.i + 1])) {
this.start = this.i + 1;
this.state = this.EndTag;
} else this.Comment();
} else if (next == '!') {
this.setText();
this.Comment();
}
}
}
MpHtmlParser.prototype.Comment = function() {
var key;
if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
else key = '>';
if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
else this.i += key.length - 1;
this.start = this.i + 1;
this.state = this.Text;
}
MpHtmlParser.prototype.TagName = function(c) {
if (blankChar[c]) {
this.tagName = this.section();
while (blankChar[this.data[this.i]]) this.i++;
if (this.isClose()) this.setNode();
else {
this.start = this.i;
this.state = this.AttrName;
}
} else if (this.isClose()) {
this.tagName = this.section();
this.setNode();
}
}
MpHtmlParser.prototype.AttrName = function(c) {
if (c == '=' || blankChar[c] || this.isClose()) {
this.attrName = this.section();
if (blankChar[c])
while (blankChar[this.data[++this.i]]);
if (this.data[this.i] == '=') {
while (blankChar[this.data[++this.i]]);
this.start = this.i--;
this.state = this.AttrValue;
} else this.setAttr();
}
}
MpHtmlParser.prototype.AttrValue = function(c) {
if (c == '"' || c == "'") {
this.start++;
if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
this.attrVal = this.section();
this.i++;
} else {
for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
this.attrVal = this.section();
}
this.setAttr();
}
MpHtmlParser.prototype.EndTag = function(c) {
if (blankChar[c] || c == '>' || c == '/') {
var name = this.section().toLowerCase();
for (var i = this.STACK.length; i--;)
if (this.STACK[i].name == name) break;
if (i != -1) {
var node;
while ((node = this.STACK.pop()).name != name) this.popNode(node);
this.popNode(node);
} else if (name == 'p' || name == 'br')
this.siblings().push({
name,
attrs: {}
});
this.i = this.data.indexOf('>', this.i);
this.start = this.i + 1;
if (this.i == -1) this.i = this.data.length;
else this.state = this.Text;
}
}
module.exports = MpHtmlParser;
+93
View File
@@ -0,0 +1,93 @@
/* 配置文件 */
// #ifdef MP-WEIXIN
const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
// #endif
module.exports = {
// 出错占位图
errorImg: null,
// 过滤器函数
filter: null,
// 代码高亮函数
highlight: null,
// 文本处理函数
onText: null,
// 实体编码列表
entities: {
quot: '"',
apos: "'",
semi: ';',
nbsp: '\xA0',
ensp: '\u2002',
emsp: '\u2003',
ndash: '',
mdash: '—',
middot: '·',
lsquo: '',
rsquo: '',
ldquo: '“',
rdquo: '”',
bull: '•',
hellip: '…'
},
blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
boolAttrs: makeMap('allowfullscreen,autoplay,autostart,controls,ignore,loop,muted'),
// 块级标签,将被转为 div
blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (
// #ifdef MP-WEIXIN
canIUse ? '' :
// #endif
',pre')),
// 将被移除的标签
ignoreTags: makeMap(
'area,base,canvas,frame,input,link,map,meta,param,script,source,style,svg,textarea,title,track,wbr'
// #ifdef MP-WEIXIN
+ (canIUse ? ',rp' : '')
// #endif
// #ifndef APP-PLUS
+ ',iframe'
// #endif
),
// 只能被 rich-text 显示的标签
richOnlyTags: makeMap('a,colgroup,fieldset,legend,table'
// #ifdef MP-WEIXIN
+ (canIUse ? ',bdi,bdo,caption,rt,ruby' : '')
// #endif
),
// 自闭合的标签
selfClosingTags: makeMap(
'area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'
),
// 信任的标签
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,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'
// #ifdef MP-WEIXIN
+ (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')
// #endif
// #ifdef APP-PLUS
+ ',embed,iframe'
// #endif
),
// 默认的标签样式
userAgentStyles: {
address: 'font-style:italic',
big: 'display:inline;font-size:1.2em',
blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
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;overflow:scroll',
s: 'text-decoration:line-through',
small: 'display:inline;font-size:0.8em',
u: 'text-decoration:underline'
}
}
function makeMap(str) {
var map = Object.create(null),
list = str.split(',');
for (var i = list.length; i--;)
map[list[i]] = true;
return map;
}
+20
View File
@@ -0,0 +1,20 @@
var inline = {
abbr: 1,
b: 1,
big: 1,
code: 1,
del: 1,
em: 1,
i: 1,
ins: 1,
label: 1,
q: 1,
small: 1,
span: 1,
strong: 1
}
module.exports = {
use: function(item) {
return !item.c && !inline[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
}
}
+519
View File
@@ -0,0 +1,519 @@
<template>
<view class="interlayer">
<block v-for="(n, i) in nodes" v-bind:key="i">
<!--图片-->
<view v-if="n.name=='img'" :class="'_img '+n.attrs.class" :style="n.attrs.style" :data-attrs="n.attrs" @tap="imgtap">
<rich-text v-if="controls[i]!=0" :nodes="[{attrs:{src:loading&&(controls[i]||0)<2?loading:(lazyLoad&&!controls[i]?placeholder:(controls[i]==3?errorImg:n.attrs.src||'')),alt:n.attrs.alt||'',width:n.attrs.width||'',style:'-webkit-touch-callout:none;max-width:100%;display:block'+(n.attrs.height?';height:'+n.attrs.height:'')},name:'img'}]" />
<image class="_image" :src="lazyLoad&&!controls[i]?placeholder:n.attrs.src" :lazy-load="lazyLoad"
:show-menu-by-longpress="!n.attrs.ignore" :data-i="i" :data-index="n.attrs.i" data-source="img" @load="loadImg"
@error="error" />
</view>
<!--文本-->
<text v-else-if="n.type=='text'" decode>{{n.text}}</text>
<!--#ifndef MP-BAIDU-->
<text v-else-if="n.name=='br'">\n</text>
<!--#endif-->
<!--视频-->
<view v-else-if="((n.lazyLoad&&!n.attrs.autoplay)||(n.name=='video'&&!loadVideo))&&controls[i]==undefined" :id="n.attrs.id" :class="'_video '+(n.attrs.class||'')"
:style="n.attrs.style" :data-i="i" @tap="_loadVideo" />
<video v-else-if="n.name=='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay||controls[i]==0"
:controls="!n.attrs.autoplay||n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.attrs.source[controls[i]||0]"
:unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" :data-i="i" data-source="video" @error="error" @play="play" />
<!--音频-->
<audio v-else-if="n.name=='audio'" :ref="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author"
:autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster"
:src="n.attrs.source[controls[i]||0]" :data-i="i" :data-id="n.attrs.id" data-source="audio"
@error.native="error" @play.native="play" />
<!--链接-->
<view v-else-if="n.name=='a'" :id="n.attrs.id" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style"
:data-attrs="n.attrs" @tap="linkpress">
<trees class="_span" :nodes="n.children" />
</view>
<!--广告-->
<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']" :appid="n.attrs.appid" :apid="n.attrs.apid" :type="n.attrs.type" :adpid="n.attrs.adpid" data-source="ad" @error="error" />-->
<!--列表-->
<view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex'">
<view v-if="n.type=='ol'" class="_ol-bef">{{n.num}}</view>
<view v-else class="_ul-bef">
<view v-if="n.floor%3==0" class="_ul-p1"></view>
<view v-else-if="n.floor%3==2" class="_ul-p2" />
<view v-else class="_ul-p1" style="border-radius:50%"></view>
</view>
<!--#ifdef MP-ALIPAY-->
<view class="_li">
<trees :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY-->
<trees class="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
<!--#endif-->
</view>
<!--表格-->
<view v-else-if="n.name=='table'&&n.c" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:table'">
<view v-for="(tbody, o) in n.children" v-bind:key="o" :class="tbody.attrs.class" :style="(tbody.attrs.style||'')+(tbody.name[0]=='t'?';display:table-'+(tbody.name=='tr'?'row':'row-group'):'')">
<view v-for="(tr, p) in tbody.children" v-bind:key="p" :class="tr.attrs.class" :style="(tr.attrs.style||'')+(tr.name[0]=='t'?';display:table-'+(tr.name=='tr'?'row':'cell'):'')">
<trees v-if="tr.name=='td'" :nodes="tr.children" />
<block v-else>
<!--#ifdef MP-ALIPAY-->
<view v-for="(td, q) in tr.children" v-bind:key="q" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')">
<trees :nodes="td.children" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY-->
<trees v-for="(td, q) in tr.children" v-bind:key="q" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')"
:nodes="td.children" />
<!--#endif-->
</block>
</view>
</view>
</view>
<!--#ifdef APP-PLUS-->
<iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder"
:width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
<embed v-else-if="n.name=='embed'" :style="n.attrs.style" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
<!--#endif-->
<!--富文本-->
<!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
<rich-text v-else-if="handler.use(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" />
<!--#endif-->
<!--#ifndef MP-WEIXIN || MP-QQ || APP-PLUS-->
<rich-text v-else-if="!n.c" :id="n.attrs.id" :nodes="[n]" style="display:inline" />
<!--#endif-->
<!--#ifdef MP-ALIPAY-->
<view v-else :id="n.attrs.id" :class="'_'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style">
<trees :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY-->
<trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style" :nodes="n.children"
:lazyLoad="lazyLoad" :loading="loading" />
<!--#endif-->
</block>
</view>
</template>
<script module="handler" lang="wxs" src="./handler.wxs"></script>
<script>
global.Parser = {};
import trees from './trees'
const errorImg = require('../libs/config.js').errorImg;
export default {
components: {
trees
},
name: 'trees',
data() {
return {
controls: [],
placeholder: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="225"/>',
errorImg,
loadVideo:
// #ifdef APP-PLUS
false
// #endif
// #ifndef APP-PLUS
true
// #endif
}
},
props: {
nodes: Array,
lazyLoad: Boolean,
loading: String
},
mounted() {
for (this.top = this.$parent; this.top.$options.name != 'parser'; this.top = this.top.$parent);
this.init();
},
// #ifdef APP-PLUS
beforeDestroy() {
this.observer && this.observer.disconnect();
},
// #endif
methods: {
init() {
for (var i = this.nodes.length, n; n = this.nodes[--i];) {
if (n.name == 'img') {
this.top.imgList.setItem(n.attrs.i, n.attrs.src);
// #ifdef APP-PLUS
if (this.lazyLoad && !this.observer) {
this.observer = uni.createIntersectionObserver(this).relativeToViewport({
top: 500,
bottom: 500
});
this.$nextTick(() => {
this.observer.observe('._img', res => {
if (res.intersectionRatio) {
for (var j = this.nodes.length; j--;)
if (this.nodes[j].name == 'img')
this.$set(this.controls, j, 1);
this.observer.disconnect();
}
})
})
}
// #endif
} else if (n.name == 'video' || n.name == 'audio') {
var ctx;
if (n.name == 'video') {
ctx = uni.createVideoContext(n.attrs.id
// #ifndef MP-BAIDU
, this
// #endif
);
} else if (this.$refs[n.attrs.id])
ctx = this.$refs[n.attrs.id][0];
if (ctx) {
ctx.id = n.attrs.id;
this.top.videoContexts.push(ctx);
}
}
}
// #ifdef APP-PLUS
// APP 上避免 video 错位需要延时渲染
setTimeout(() => {
this.loadVideo = true;
}, 1000)
// #endif
},
play(e) {
var contexts = this.top.videoContexts;
if (contexts.length > 1 && this.top.autopause)
for (var i = contexts.length; i--;)
if (contexts[i].id != e.currentTarget.dataset.id)
contexts[i].pause();
},
imgtap(e) {
var attrs = e.currentTarget.dataset.attrs;
if (!attrs.ignore) {
var preview = true,
data = {
id: e.target.id,
src: attrs.src,
ignore: () => preview = false
};
global.Parser.onImgtap && global.Parser.onImgtap(data);
this.top.$emit('imgtap', data);
if (preview) {
var urls = this.top.imgList,
current = urls[attrs.i] ? parseInt(attrs.i) : (urls = [attrs.src], 0);
uni.previewImage({
current,
urls
})
}
}
},
loadImg(e) {
var i = e.currentTarget.dataset.i;
if (this.lazyLoad && !this.controls[i]) {
// #ifdef QUICKAPP-WEBVIEW
this.$set(this.controls, i, 0);
this.$nextTick(function() {
// #endif
// #ifndef APP-PLUS
this.$set(this.controls, i, 1);
// #endif
// #ifdef QUICKAPP-WEBVIEW
})
// #endif
} else if (this.loading && this.controls[i] != 2) {
// #ifdef QUICKAPP-WEBVIEW
this.$set(this.controls, i, 0);
this.$nextTick(function() {
// #endif
this.$set(this.controls, i, 2);
// #ifdef QUICKAPP-WEBVIEW
})
// #endif
}
},
linkpress(e) {
var jump = true,
attrs = e.currentTarget.dataset.attrs;
attrs.ignore = () => jump = false;
global.Parser.onLinkpress && global.Parser.onLinkpress(attrs);
this.top.$emit('linkpress', attrs);
if (jump) {
// #ifdef MP
if (attrs['app-id']) {
return uni.navigateToMiniProgram({
appId: attrs['app-id'],
path: attrs.path
})
}
// #endif
if (attrs.href) {
if (attrs.href[0] == '#') {
if (this.top.useAnchor)
this.top.navigateTo({
id: attrs.href.substring(1)
})
} else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0) {
// #ifdef APP-PLUS
plus.runtime.openWeb(attrs.href);
// #endif
// #ifndef APP-PLUS
uni.setClipboardData({
data: attrs.href,
success: () =>
uni.showToast({
title: '链接已复制'
})
})
// #endif
} else
uni.navigateTo({
url: attrs.href,
fail() {
uni.switchTab({
url: attrs.href,
})
}
})
}
}
},
error(e) {
var target = e.currentTarget,
source = target.dataset.source,
i = target.dataset.i;
if (source == 'video' || source == 'audio') {
// 加载其他 source
var index = this.controls[i] ? this.controls[i].i + 1 : 1;
if (index < this.nodes[i].attrs.source.length)
this.$set(this.controls, i, index);
if (e.detail.__args__)
e.detail = e.detail.__args__[0];
} else if (errorImg && source == 'img') {
this.top.imgList.setItem(target.dataset.index, errorImg);
this.$set(this.controls, i, 3);
}
this.top && this.top.$emit('error', {
source,
target,
errMsg: e.detail.errMsg
});
},
_loadVideo(e) {
this.$set(this.controls, e.target.dataset.i, 0);
}
}
}
</script>
<style>
/* 在这里引入自定义样式 */
/* 链接和图片效果 */
._a {
display: inline;
padding: 1.5px 0 1.5px 0;
color: #366092;
word-break: break-all;
}
._hover {
text-decoration: underline;
opacity: 0.7;
}
._img {
position: relative;
display: inline-block;
max-width: 100%;
}
/* #ifdef MP-WEIXIN */
:host {
display: inline;
}
/* #endif */
/* #ifdef MP || QUICKAPP-WEBVIEW */
.interlayer {
display: inherit;
flex-direction: inherit;
flex-wrap: inherit;
align-content: inherit;
align-items: inherit;
justify-content: inherit;
width: 100%;
white-space: inherit;
}
/* #endif */
._b,
._strong {
font-weight: bold;
}
._blockquote,
._div,
._p,
._ol,
._ul,
._li {
display: block;
}
._code {
font-family: monospace;
}
._del {
text-decoration: line-through;
}
._em,
._i {
font-style: italic;
}
._h1 {
font-size: 2em;
}
._h2 {
font-size: 1.5em;
}
._h3 {
font-size: 1.17em;
}
._h5 {
font-size: 0.83em;
}
._h6 {
font-size: 0.67em;
}
._h1,
._h2,
._h3,
._h4,
._h5,
._h6 {
display: block;
font-weight: bold;
}
._image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
._ins {
text-decoration: underline;
}
._li {
flex: 1;
width: 0;
}
._ol-bef {
width: 36px;
margin-right: 5px;
text-align: right;
}
._ul-bef {
margin: 0 12px 0 23px;
line-height: normal;
}
._ol-bef,
._ul_bef {
flex: none;
user-select: none;
}
._ul-p1 {
display: inline-block;
width: 0.3em;
height: 0.3em;
overflow: hidden;
line-height: 0.3em;
}
._ul-p2 {
display: inline-block;
width: 0.23em;
height: 0.23em;
border: 0.05em solid black;
border-radius: 50%;
}
._q::before {
content: '"';
}
._q::after {
content: '"';
}
._sub {
font-size: smaller;
vertical-align: sub;
}
._sup {
font-size: smaller;
vertical-align: super;
}
/* #ifdef MP-ALIPAY || APP-PLUS || QUICKAPP-WEBVIEW*/
._abbr,
._b,
._code,
._del,
._em,
._i,
._ins,
._label,
._q,
._span,
._strong,
._sub,
._sup {
display: inline;
}
/* #endif */
/* #ifdef MP-WEIXIN || MP-QQ */
.__bdo,
.__bdi,
.__ruby,
.__rt {
display: inline-block;
}
/* #endif */
._video {
position: relative;
display: inline-block;
width: 300px;
height: 225px;
background-color: black;
}
._video::after {
position: absolute;
top: 50%;
left: 50%;
margin: -15px 0 0 -15px;
content: '';
border-color: transparent transparent transparent white;
border-style: solid;
border-width: 15px 0 15px 30px;
}
</style>
+138
View File
@@ -0,0 +1,138 @@
<template>
<view>
<view class="relative" :style="{height: `calc(${statusBar}px + ${height}px)`}">
<view class="width-100b fixed1" :style="{height: `calc(${statusBar}px + ${height}px)`,background:background}">
<view class="width-100b flex align-center fixed1 justify-center text-center" :style="{paddingTop: statusBar + 'px', height: `calc(${statusBar}px + 55px)`}">
<text class="midetitle bold-font" :style="{color: color}">{{ title }}</text>
<i @click="goBack" v-if="black" class="cuIcon-back font-30" style="position: absolute;left: 30rpx;" :style="{color: blackColor}"></i>
<image @tap="Jump" v-if="isRight" style="position: absolute;right: 30rpx;" :style="'width:'+rightWidth +'rpx;height:' + rightHeight + 'rpx'"
:src="righturl" />
<text @tap='Jump' v-if="isRightext" class="font-30 bold-font" :style="{color: color}" style="position: absolute;right: 30rpx;">{{ rightText }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
//标题
title: {
type: String
},
height: {
type: String,
default: '55'
},
// 标题字体颜色
// color: {
// type: String,
// default: '#17181A'
// },
// 返回图标颜色
// blackColor: {
// type: String,
// default: '#000'
// },
// 右侧文字颜色
rightextColor: {
type: String,
default: '#17181A'
},
// 是否有右侧图片
isRight: {
type: Boolean,
default: false
},
// 是否有右侧文字
isRightext: {
type: Boolean,
default: false
},
// 右侧文字内容
rightText: {
type: String,
default: '设置'
},
// 右侧图片路径
righturl: {
type: String,
default: '/static/logo.png'
},
// 宽度
rightWidth: {
type: String,
default: '38'
},
// 高度
rightHeight: {
type: String,
default: '38'
},
// 导航背景色
// background: {
// type: String,
// default: '#FFFFFF'
// },
isGoback: {
type: Boolean,
default: true
},
black: {
type: Boolean,
default: true
}
},
data() {
return {
statusBar: uni.getSystemInfoSync().statusBarHeight,
background:'',
color:'',
blackColor:'',
}
},
created() {
this.background = this.$store.state.lightTheme.bg
this.color = this.$store.state.lightTheme.text
this.blackColor = this.$store.state.lightTheme.text
// this.rightextColor = this.$store.state.lightTheme.text
},
methods: {
Jump() {
this.$emit('Jump')
},
goBack() {
if (!this.isGoback) {
this.$emit('isNoback')
// uni.redirectTo({
// url: '/pages/index/index'
// })
} else {
this.router.goBack()
}
}
}
}
</script>
<style scoped>
.fixed1 {
position: fixed;
top: 0;
z-index: 990;
}
.relative{
position: relative;
}
.font-32{
font-size: 32rpx;
}
.midetitle{
font-size: 16px;
/* font-weight: 700; */
}
.font-40{
font-size: 40rpx;
}
</style>
+46
View File
@@ -0,0 +1,46 @@
let darkTheme = {
theme: "dark",
text: "#FFFFFF",
bg: "#020202",
fu_bg:'#1A1A1A',
fu_text:'#B7B7B7',
selectcolor:'color-white',
selectbg:'bj-white',
color:'color-B7B7B7',
selectedIconPath0:'/static/tabs/home.png',
selectedIconPath1:'/static/tabs/heyue.png',
selectedIconPath2:'/static/tabs/jiaoyi.png',
selectedIconPath3:'/static/tabs/lianghua.png',
selectedIconPath4:'/static/tabs/zichan.png',
selectedColor:'#FFFFFF',
backgroundColor:'#020202',
borderStyle:'white',
imgs:'/static/imgs/black',
assetbg:'#1A1A1A',
}
let lightTheme = {
theme: "light",
text: "#020202",
bg: "#FFFFFF",
fu_bg:'#F2F2F2',
fu_text:'#909090',
selectcolor:'color-020202',
selectbg:'bj-020202',
color:'color-909090',
selectedIconPath0:'/static/lighttabs/home_1.png',
selectedIconPath1:'/static/lighttabs/hangqing_1.png',
selectedIconPath2:'/static/lighttabs/heyue_1.png',
selectedIconPath3:'/static/lighttabs/jiaoyi_1.png',
selectedIconPath4:'/static/lighttabs/zichan_1.png',
selectedColor:'#020202',
backgroundColor:'#FFFFFF',
borderStyle:'black',
imgs:'/static/imgs/light',
assetbg:'#020202',
}
export {
darkTheme,
lightTheme
}