vue-cli init
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
## 1.0.9(2024-04-11)
|
||||
优化使用说明
|
||||
## 1.0.8(2023-01-29)
|
||||
修复IOS下错误:“Can't find variable: osName __ERROR”
|
||||
## 1.0.7(2022-10-17)
|
||||
更新拼写错误
|
||||
## 1.0.6(2022-10-09)
|
||||
1.优化代码拼写错误
|
||||
@@ -0,0 +1,329 @@
|
||||
/**
|
||||
* @Descripttion: app升级弹框
|
||||
* @Version: 1.0.0
|
||||
* @Author: leefine
|
||||
*/
|
||||
|
||||
import config from '@/upgrade-config.js'
|
||||
import upgrade from './upgrade'
|
||||
|
||||
const {
|
||||
title = '发现新版本',
|
||||
confirmText = '立即更新',
|
||||
cancelTtext = '稍后再说',
|
||||
confirmBgColor = '#409eff',
|
||||
showCancel = true,
|
||||
titleAlign = 'left',
|
||||
descriAlign = 'left',
|
||||
icon
|
||||
} = config.upgrade;
|
||||
|
||||
class AppDialog {
|
||||
constructor() {
|
||||
this.maskEl = {}
|
||||
this.popupEl = {}
|
||||
this.screenHeight = 600;
|
||||
this.popupHeight = 230;
|
||||
this.popupWidth = 300;
|
||||
this.viewWidth = 260;
|
||||
this.descrTop = 130;
|
||||
this.viewPadding = 20;
|
||||
this.iconSize = 80;
|
||||
this.titleHeight = 30;
|
||||
this.textHeight = 18;
|
||||
this.textSpace = 10;
|
||||
this.popupContent = []
|
||||
this.apkUrl = '';
|
||||
}
|
||||
|
||||
// 显示
|
||||
show(apkUrl, changelog) {
|
||||
this.drawView(changelog)
|
||||
this.maskEl.show()
|
||||
this.popupEl.show()
|
||||
this.apkUrl = apkUrl;
|
||||
}
|
||||
|
||||
// 隐藏
|
||||
hide() {
|
||||
this.maskEl.hide()
|
||||
this.popupEl.hide()
|
||||
}
|
||||
|
||||
// 绘制
|
||||
drawView(changelog) {
|
||||
this.screenHeight = plus.screen.resolutionHeight;
|
||||
this.popupWidth = plus.screen.resolutionWidth * 0.8;
|
||||
this.popupHeight = this.viewPadding * 3 + this.iconSize + 100;
|
||||
this.viewWidth = this.popupWidth - this.viewPadding * 2;
|
||||
this.descrTop = this.viewPadding + this.iconSize + this.titleHeight;
|
||||
this.popupContent = [];
|
||||
|
||||
if (icon) {
|
||||
this.popupContent.push({
|
||||
id: 'logo',
|
||||
tag: 'img',
|
||||
src: icon,
|
||||
position: {
|
||||
top: '0px',
|
||||
left: (this.popupWidth - this.iconSize) / 2 + 'px',
|
||||
width: this.iconSize + 'px',
|
||||
height: this.iconSize + 'px'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.popupContent.push({
|
||||
id: 'logo',
|
||||
tag: 'img',
|
||||
src: '_pic/upgrade.png',
|
||||
position: {
|
||||
top: '0px',
|
||||
left: (this.popupWidth - this.iconSize) / 2 + 'px',
|
||||
width: this.iconSize + 'px',
|
||||
height: this.iconSize + 'px'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 标题
|
||||
if (title) {
|
||||
this.popupContent.push({
|
||||
id: 'title',
|
||||
tag: 'font',
|
||||
text: title,
|
||||
textStyles: {
|
||||
size: '18px',
|
||||
color: '#333',
|
||||
weight: 'bold',
|
||||
align: titleAlign
|
||||
},
|
||||
position: {
|
||||
top: this.descrTop - this.titleHeight - this.textSpace + 'px',
|
||||
left: this.viewPadding + 'px',
|
||||
width: this.viewWidth + 'px',
|
||||
height: this.titleHeight + 'px'
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.descrTop -= this.titleHeight;
|
||||
}
|
||||
|
||||
this.drawText(changelog)
|
||||
|
||||
// 取消
|
||||
if (showCancel) {
|
||||
const width = (this.viewWidth - this.viewPadding) / 2;
|
||||
const confirmLeft = width + this.viewPadding * 2;
|
||||
this.drawBtn('cancel', width, cancelTtext)
|
||||
this.drawBtn('confirm', width, confirmText, confirmLeft)
|
||||
} else {
|
||||
this.drawBtn('confirmBox', this.viewWidth, confirmText)
|
||||
}
|
||||
|
||||
this.drawBox(showCancel)
|
||||
}
|
||||
|
||||
// 描述内容
|
||||
drawText(changelog) {
|
||||
if (!changelog) return [];
|
||||
const textArr = changelog.split('')
|
||||
const len = textArr.length;
|
||||
let prevNode = 0;
|
||||
let nodeWidth = 0;
|
||||
let letterWidth = 0;
|
||||
const chineseWidth = 14;
|
||||
const otherWidth = 7;
|
||||
let rowText = [];
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
// 包含中文
|
||||
if (/[\u4e00-\u9fa5]|[\uFE30-\uFFA0]/g.test(textArr[i])) {
|
||||
// 包含字母
|
||||
let textWidth = ''
|
||||
if (letterWidth > 0) {
|
||||
textWidth = nodeWidth + chineseWidth + letterWidth * otherWidth;
|
||||
letterWidth = 0;
|
||||
} else {
|
||||
// 不含字母
|
||||
textWidth = nodeWidth + chineseWidth;
|
||||
}
|
||||
|
||||
if (textWidth > this.viewWidth) {
|
||||
rowArrText(i, chineseWidth)
|
||||
} else {
|
||||
nodeWidth = textWidth;
|
||||
}
|
||||
} else {
|
||||
// 不含中文
|
||||
// 包含换行符
|
||||
if (/\n/g.test(textArr[i])) {
|
||||
rowArrText(i, 0, 1)
|
||||
letterWidth = 0;
|
||||
} else if (textArr[i] == '\\' && textArr[i + 1] == 'n') {
|
||||
rowArrText(i, 0, 2)
|
||||
letterWidth = 0;
|
||||
} else if (/[a-zA-Z0-9]/g.test(textArr[i])) {
|
||||
// 包含字母数字
|
||||
letterWidth += 1;
|
||||
const textWidth = nodeWidth + letterWidth * otherWidth;
|
||||
if (textWidth > this.viewWidth) {
|
||||
const preNode = i + 1 - letterWidth;
|
||||
rowArrText(preNode, letterWidth * otherWidth)
|
||||
letterWidth = 0;
|
||||
}
|
||||
} else {
|
||||
if (nodeWidth + otherWidth > this.viewWidth) {
|
||||
rowArrText(i, otherWidth)
|
||||
} else {
|
||||
nodeWidth += otherWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevNode < len) {
|
||||
rowArrText(len, -1)
|
||||
}
|
||||
this.drawDesc(rowText)
|
||||
|
||||
function rowArrText(i, nWidth = 0, type = 0) {
|
||||
const typeVal = type > 0 ? 'break' : 'text';
|
||||
|
||||
rowText.push({
|
||||
type: typeVal,
|
||||
content: changelog.substring(prevNode, i)
|
||||
})
|
||||
|
||||
if (nWidth >= 0) {
|
||||
prevNode = i + type;
|
||||
nodeWidth = nWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 描述
|
||||
drawDesc(rowText) {
|
||||
rowText.forEach((item, index) => {
|
||||
if (index > 0) {
|
||||
this.descrTop += this.textHeight;
|
||||
this.popupHeight += this.textHeight;
|
||||
}
|
||||
|
||||
this.popupContent.push({
|
||||
id: 'content' + index + 1,
|
||||
tag: 'font',
|
||||
text: item.content,
|
||||
textStyles: {
|
||||
size: '14px',
|
||||
color: '#666',
|
||||
align: descriAlign
|
||||
},
|
||||
position: {
|
||||
top: this.descrTop + 'px',
|
||||
left: this.viewPadding + 'px',
|
||||
width: this.viewWidth + 'px',
|
||||
height: this.textHeight + 'px'
|
||||
}
|
||||
})
|
||||
|
||||
if (item.type == 'break') {
|
||||
this.descrTop += this.textSpace;
|
||||
this.popupHeight += this.textSpace;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 按钮
|
||||
drawBtn(id, width, text, left = this.viewPadding) {
|
||||
let boxColor = confirmBgColor,
|
||||
textColor = '#ffffff';
|
||||
if (id == 'cancel') {
|
||||
boxColor = '#f0f0f0';
|
||||
textColor = '#666666';
|
||||
}
|
||||
|
||||
this.popupContent.push({
|
||||
id: id + 'Box',
|
||||
tag: 'rect',
|
||||
rectStyles: {
|
||||
radius: '6px',
|
||||
color: boxColor
|
||||
},
|
||||
position: {
|
||||
bottom: this.viewPadding + 'px',
|
||||
left: left + 'px',
|
||||
width: width + 'px',
|
||||
height: '40px'
|
||||
}
|
||||
})
|
||||
|
||||
this.popupContent.push({
|
||||
id: id + 'Text',
|
||||
tag: 'font',
|
||||
text: text,
|
||||
textStyles: {
|
||||
size: '14px',
|
||||
color: textColor
|
||||
},
|
||||
position: {
|
||||
bottom: this.viewPadding + 'px',
|
||||
left: left + 'px',
|
||||
width: width + 'px',
|
||||
height: '40px'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 内容框
|
||||
drawBox(showCancel) {
|
||||
this.maskEl = new plus.nativeObj.View('maskEl', {
|
||||
top: '0px',
|
||||
left: '0px',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: 'rgba(0,0,0,0.5)'
|
||||
});
|
||||
|
||||
this.popupEl = new plus.nativeObj.View('popupEl', {
|
||||
tag: 'rect',
|
||||
top: (this.screenHeight - this.popupHeight) / 2 + 'px',
|
||||
left: '10%',
|
||||
height: this.popupHeight + 'px',
|
||||
width: '80%'
|
||||
});
|
||||
|
||||
// 白色背景
|
||||
this.popupEl.drawRect({
|
||||
color: '#ffffff',
|
||||
radius: '8px'
|
||||
}, {
|
||||
top: this.iconSize / 2 + 'px',
|
||||
height: this.popupHeight - this.iconSize / 2 + 'px'
|
||||
});
|
||||
|
||||
this.popupEl.draw(this.popupContent);
|
||||
|
||||
this.popupEl.addEventListener('click', e => {
|
||||
const maxTop = this.popupHeight - this.viewPadding;
|
||||
const maxLeft = this.popupWidth - this.viewPadding;
|
||||
const buttonWidth = (this.viewWidth - this.viewPadding) / 2;
|
||||
if (e.clientY > maxTop - 40 && e.clientY < maxTop) {
|
||||
if (showCancel) {
|
||||
// 取消
|
||||
// if(e.clientX>this.viewPadding && e.clientX<maxLeft-buttonWidth-this.viewPadding){}
|
||||
// 确定
|
||||
if (e.clientX > maxLeft - buttonWidth && e.clientX < maxLeft) {
|
||||
upgrade.checkOs(this.apkUrl)
|
||||
}
|
||||
} else {
|
||||
if (e.clientX > this.viewPadding && e.clientX < maxLeft) {
|
||||
upgrade.checkOs(this.apkUrl)
|
||||
}
|
||||
}
|
||||
this.hide()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new AppDialog()
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @Descripttion: app下载更新
|
||||
* @Version: 1.0.0
|
||||
* @Author: leefine
|
||||
*/
|
||||
|
||||
import config from '@/upgrade-config.js'
|
||||
const { upType=0 }=config.upgrade;
|
||||
|
||||
class Upgrade{
|
||||
|
||||
// 检测平台
|
||||
checkOs(apkUrl){
|
||||
uni.getSystemInfo({
|
||||
success:(res) => {
|
||||
if(res.osName=="android"){
|
||||
if(upType==1 && packageName){
|
||||
plus.runtime.openURL('market://details?id='+packageName)
|
||||
}else{
|
||||
this.downloadInstallApp(apkUrl)
|
||||
}
|
||||
}else if(res.osName=='ios' && appleId){
|
||||
// apple id 在 app conection 上传的位置可以看到 https://appstoreconnect.apple.com
|
||||
plus.runtime.launchApplication({
|
||||
action: `itms-apps://itunes.apple.com/cn/app/id${appleId}?mt=8`
|
||||
}, function(err) {
|
||||
uni.showToast({
|
||||
title:err.message,
|
||||
icon:'none'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 下载更新
|
||||
downloadInstallApp(apkUrl){
|
||||
const dtask = plus.downloader.createDownload(apkUrl, {}, function (d,status){
|
||||
// 下载完成
|
||||
if (status == 200){
|
||||
plus.runtime.install(plus.io.convertLocalFileSystemURL(d.filename),{},{},function(error){
|
||||
uni.showToast({
|
||||
title: '安装失败',
|
||||
icon:'none'
|
||||
});
|
||||
})
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: '更新失败',
|
||||
icon:'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
this.downloadProgress(dtask);
|
||||
}
|
||||
|
||||
// 下载进度
|
||||
downloadProgress(dtask){
|
||||
try{
|
||||
dtask.start(); //开启下载任务
|
||||
let prg=0;
|
||||
let showLoading=plus.nativeUI.showWaiting('正在下载');
|
||||
dtask.addEventListener('statechanged',function(task,status){
|
||||
// 给下载任务设置监听
|
||||
switch(task.state){
|
||||
case 1:
|
||||
showLoading.setTitle('正在下载');
|
||||
break;
|
||||
case 2:
|
||||
showLoading.setTitle('已连接到服务器');
|
||||
break;
|
||||
case 3:
|
||||
prg=parseInt((parseFloat(task.downloadedSize)/parseFloat(task.totalSize))*100);
|
||||
showLoading.setTitle('正在下载'+prg+'%');
|
||||
break;
|
||||
case 4:
|
||||
// 下载完成
|
||||
plus.nativeUI.closeWaiting();
|
||||
break;
|
||||
}
|
||||
})
|
||||
}catch(e){
|
||||
plus.nativeUI.closeWaiting();
|
||||
uni.showToast({
|
||||
title: '更新失败',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new Upgrade()
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"id": "app-upgrade",
|
||||
"displayName": "APP Upgrade 支持自动升级和手动检测升级,支持android和ios,支持从服务端获取最新的Change Log.",
|
||||
"version": "1.0.9",
|
||||
"description": "uni-app 开发 APP 自动更新及检测更新,弹窗提示系统端发布的更新内容,整包升级,可覆盖 tabbar,支持 安卓 和 苹果.",
|
||||
"keywords": [
|
||||
"app升级,",
|
||||
"APP",
|
||||
"Upgrade,自动更新,检测更新"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.6.15"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "1.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "1.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "sdk-js"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "u",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
# app-upgrade
|
||||
|
||||
> uni-app 开发 APP 检查更新,弹窗提示,整包升级,可覆盖 tabbar,支持 android 和 ios.
|
||||
|
||||
### 使用介绍
|
||||
|
||||
1. 使用 HBuilderx 导入插件,在相关文件中引入
|
||||
|
||||
> `app_upgrade(callback,type)` 方法接收两个参数:
|
||||
>> `callback(versionCode)` 回调函数携带参数为当前应用版本号,例如 100,用来比较是否有新版本,返回对象 *{'status': 0 无新版本 | 1 有新版本,'changelog':'更新内容', 'path': '新apk地址'}*
|
||||
>>
|
||||
>> `type` 0 主动升级,1 用户检测升级,默认为 0
|
||||
|
||||
2. **主动升级**,在 App.vue 中引入,在 onLaunch 应用生命周期中调用
|
||||
|
||||
```
|
||||
import app_upgrade from '@/uni_modules/app-upgrade/js_sdk/index.js'
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
app_upgrade(async (versionCode)=>{
|
||||
//查询是否更新
|
||||
const { statusCode,data }=await uni.request({
|
||||
url:'https://xxx',
|
||||
data:{
|
||||
'versionCode':versionCode
|
||||
},
|
||||
method:'POST'
|
||||
})
|
||||
|
||||
if(statusCode==200){
|
||||
return {
|
||||
changelog:data.changelog,
|
||||
status:data.status, // 0 无新版本 | 1 有新版本
|
||||
path:data.path // 新apk地址
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **用户检测升级**,在检测页面引入并调用
|
||||
|
||||
```
|
||||
import app_upgrade from '@/uni_modules/app-upgrade/js_sdk/index.js'
|
||||
|
||||
onUpdate(){
|
||||
app_upgrade(async (versionCode)=>{
|
||||
//查询是否更新
|
||||
const { statusCode,data }=await uni.request({
|
||||
url:'https://xxx',
|
||||
data:{
|
||||
'versionCode':versionCode
|
||||
},
|
||||
method:'POST'
|
||||
})
|
||||
|
||||
if(statusCode==200){
|
||||
return {
|
||||
changelog:data.changelog,
|
||||
status:data.status,
|
||||
path:data.path
|
||||
}
|
||||
}else{
|
||||
uni.showToast({
|
||||
title:'当前已是最新版',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
},1)
|
||||
}
|
||||
```
|
||||
|
||||
4. 在项目根目录下创建 `upgrade-config.js` 配置文件,写入以下配置,下列对象的键值根据自己实际情况设置
|
||||
|
||||
```
|
||||
export default {
|
||||
upgrade:{
|
||||
packageName:'',
|
||||
appleId:'',
|
||||
upType:0,
|
||||
timer:24,
|
||||
icon:'/static/logo.png',
|
||||
title:'发现新版本',
|
||||
confirmText:'立即更新',
|
||||
cancelTtext:'稍后再说',
|
||||
confirmBgColor:'#409eff',
|
||||
showCancel:true,
|
||||
titleAlign:'left',
|
||||
descriAlign:'left'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. upgrade-config.js `upgrade` 配置说明,全部非必填
|
||||
|
||||
|字段|描述|默认值|
|
||||
|-- |-- |-- |
|
||||
|upType|安卓升级方式,0 应用内更新 / 1 去应用商店更新|0|
|
||||
|packageName|android 包名,upType=1 时必填| |
|
||||
|appleId|ios 应用 id,<a href="https://appstoreconnect.apple.com" target="_blank">app store</a>中获取,ios必填| |
|
||||
|timer|主动检测间隔小时|24|
|
||||
|icon|弹窗图标| 如果为空使用内置Base64图片|
|
||||
|title|弹窗标题|发现新版本|
|
||||
|titleAlign|弹窗标题对齐方式,left / center|left|
|
||||
|confirmText|弹窗确定文字|立即更新|
|
||||
|confirmBgColor|弹窗确定背景颜色|#409eff|
|
||||
|cancelTtext|弹窗取消文字|稍后再说|
|
||||
|showCancel|弹窗是否显示取消,true / false|true|
|
||||
|descriAlign|更新描述对齐方式,left / center|left|
|
||||
Reference in New Issue
Block a user