版本更新接口
This commit is contained in:
parent
f496010e73
commit
d3fe6f39d2
11
App.vue
11
App.vue
|
|
@ -2,6 +2,9 @@
|
|||
// #ifdef VUE3
|
||||
import { initDictData } from '@/utils/dict'
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
import { initVersionCheck } from '@/utils/update'
|
||||
// #endif
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
|
|
@ -32,6 +35,14 @@
|
|||
uni.$uv.toast('网络连接不可用,请检查网络设置')
|
||||
}
|
||||
})
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// 检查应用版本更新(延迟 2 秒,避免影响启动速度)
|
||||
initVersionCheck({
|
||||
silent: false, // 显示提示
|
||||
delay: 2000 // 延迟 2 秒检查
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show')
|
||||
|
|
|
|||
|
|
@ -29,4 +29,7 @@ export * from './common';
|
|||
export * from './verify';
|
||||
|
||||
// 导入项目相关 API
|
||||
export * from './project';
|
||||
export * from './project';
|
||||
|
||||
// 导入版本更新相关 API
|
||||
export * from './update';
|
||||
19
api/update.js
Normal file
19
api/update.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* 版本更新相关 API
|
||||
*/
|
||||
|
||||
/**
|
||||
* 检查应用版本
|
||||
* @param {Object} params - 版本检查参数
|
||||
* @param {string} params.versionName - 当前版本名称(如 1.0.1)
|
||||
* @param {string} params.platform - 平台类型(android/ios)
|
||||
* @returns {Promise} 返回版本信息
|
||||
*/
|
||||
export const checkAppVersion = (params) => {
|
||||
return uni.$uv.http.post('/app/version/check', params, {
|
||||
custom: {
|
||||
auth: false, // 版本检查可能不需要登录
|
||||
toast: false // 不自动提示错误
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"name" : "OfficeSystem",
|
||||
"appid" : "__UNI__53A0BE0",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionName" : "1.0.1",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
|
|
@ -39,7 +39,11 @@
|
|||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INSTALL_PACKAGES\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>"
|
||||
]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
|
|
|
|||
349
utils/update.js
Normal file
349
utils/update.js
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/**
|
||||
* 应用版本更新工具类
|
||||
* 支持版本检查和 APK 下载安装
|
||||
*/
|
||||
|
||||
import { checkAppVersion } from '@/api/update'
|
||||
|
||||
/**
|
||||
* 获取当前应用版本信息
|
||||
* @returns {Object} {versionName}
|
||||
*/
|
||||
export const getCurrentVersion = () => {
|
||||
// #ifdef APP-PLUS
|
||||
const versionName = plus.runtime.version || ''
|
||||
return {
|
||||
versionName: versionName
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS
|
||||
// 非 APP 环境,从 manifest.json 读取(开发环境)
|
||||
return {
|
||||
versionName: '1.0.1'
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较版本号
|
||||
* @param {string} version1 - 版本号1(如 "1.0.1")
|
||||
* @param {string} version2 - 版本号2(如 "1.0.2")
|
||||
* @returns {number} 1: version1 > version2, -1: version1 < version2, 0: 相等
|
||||
*/
|
||||
export const compareVersion = (version1, version2) => {
|
||||
const v1parts = version1.split('.').map(Number)
|
||||
const v2parts = version2.split('.').map(Number)
|
||||
const maxLength = Math.max(v1parts.length, v2parts.length)
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const v1part = v1parts[i] || 0
|
||||
const v2part = v2parts[i] || 0
|
||||
|
||||
if (v1part > v2part) return 1
|
||||
if (v1part < v2part) return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有新版本
|
||||
* @param {boolean} silent - 是否静默检查(不显示提示)
|
||||
* @returns {Promise<Object|null>} 返回新版本信息,无新版本返回 null
|
||||
*/
|
||||
export const checkForUpdate = async (silent = false) => {
|
||||
try {
|
||||
const currentVersion = getCurrentVersion()
|
||||
const platform = uni.getSystemInfoSync().platform
|
||||
|
||||
// 只在 Android 平台检查更新
|
||||
// #ifdef APP-PLUS
|
||||
if (platform !== 'android') {
|
||||
if (!silent) {
|
||||
console.log('当前平台不支持自动更新:', platform)
|
||||
}
|
||||
return null
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS
|
||||
// 非 APP 环境,跳过检查
|
||||
return null
|
||||
// #endif
|
||||
|
||||
const params = {
|
||||
versionName: currentVersion.versionName,
|
||||
platform: 'android'
|
||||
}
|
||||
|
||||
const result = await checkAppVersion(params)
|
||||
|
||||
if (!result || !result.hasUpdate) {
|
||||
if (!silent) {
|
||||
uni.showToast({
|
||||
title: '已是最新版本',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('检查版本更新失败:', error)
|
||||
if (!silent) {
|
||||
uni.showToast({
|
||||
title: '检查更新失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示版本更新提示
|
||||
* @param {Object} updateInfo - 更新信息
|
||||
* @param {string} updateInfo.versionName - 新版本号
|
||||
* @param {string} updateInfo.downloadUrl - 下载地址
|
||||
* @param {string} updateInfo.updateLog - 更新日志
|
||||
* @param {boolean} updateInfo.forceUpdate - 是否强制更新
|
||||
* @param {number} updateInfo.fileSize - 文件大小(字节)
|
||||
*/
|
||||
export const showUpdateDialog = (updateInfo) => {
|
||||
const fileSizeMB = updateInfo.fileSize
|
||||
? (updateInfo.fileSize / 1024 / 1024).toFixed(2) + 'MB'
|
||||
: '未知大小'
|
||||
|
||||
const content = updateInfo.updateLog
|
||||
? `版本:${updateInfo.versionName}\n大小:${fileSizeMB}\n\n${updateInfo.updateLog}`
|
||||
: `发现新版本 ${updateInfo.versionName}\n大小:${fileSizeMB}`
|
||||
|
||||
uni.showModal({
|
||||
title: '发现新版本',
|
||||
content: content,
|
||||
showCancel: !updateInfo.forceUpdate,
|
||||
cancelText: '稍后更新',
|
||||
confirmText: '立即更新',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
downloadAndInstall(updateInfo.downloadUrl, updateInfo.versionName)
|
||||
} else if (updateInfo.forceUpdate) {
|
||||
// 强制更新时,取消也退出应用
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.quit()
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载并安装 APK
|
||||
* @param {string} downloadUrl - APK 下载地址
|
||||
* @param {string} versionName - 版本名称(用于文件命名)
|
||||
*/
|
||||
export const downloadAndInstall = (downloadUrl, versionName = '') => {
|
||||
// #ifdef APP-PLUS
|
||||
if (!downloadUrl) {
|
||||
uni.showToast({
|
||||
title: '下载地址无效',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 显示下载进度
|
||||
let downloadTask = null
|
||||
let progressModal = null
|
||||
|
||||
// 创建下载任务
|
||||
downloadTask = uni.downloadFile({
|
||||
url: downloadUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
// 下载成功,安装 APK
|
||||
installApk(res.tempFilePath)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('下载失败:', err)
|
||||
uni.showToast({
|
||||
title: '下载失败:' + (err.errMsg || '未知错误'),
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 监听下载进度
|
||||
downloadTask.onProgressUpdate((res) => {
|
||||
const progress = res.progress
|
||||
const received = (res.totalBytesWritten / 1024 / 1024).toFixed(2)
|
||||
const total = (res.totalBytesExpectedToWrite / 1024 / 1024).toFixed(2)
|
||||
|
||||
// 显示下载进度(使用 uni.showLoading)
|
||||
uni.showLoading({
|
||||
title: `下载中 ${progress}%`,
|
||||
mask: true
|
||||
})
|
||||
|
||||
if (progress === 100) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS
|
||||
// 非 APP 环境,提示用户手动下载
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `请访问以下地址下载新版本:\n${downloadUrl}`,
|
||||
showCancel: false,
|
||||
confirmText: '复制链接',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// #ifdef H5
|
||||
// H5 环境可以尝试复制到剪贴板
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(downloadUrl).then(() => {
|
||||
uni.showToast({
|
||||
title: '链接已复制',
|
||||
icon: 'success'
|
||||
})
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装 APK 文件
|
||||
* @param {string} filePath - APK 文件路径
|
||||
*/
|
||||
const installApk = (filePath) => {
|
||||
// #ifdef APP-PLUS
|
||||
try {
|
||||
// Android 8.0+ 需要请求安装权限
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
const androidVersion = systemInfo.system.split(' ')[1] || '0'
|
||||
const majorVersion = parseInt(androidVersion.split('.')[0]) || 0
|
||||
|
||||
// Android 8.0 (API 26) 及以上需要请求安装权限
|
||||
if (majorVersion >= 8) {
|
||||
// 检查是否有安装权限
|
||||
const main = plus.android.runtimeMainActivity()
|
||||
const pkName = main.getPackageName()
|
||||
const Intent = plus.android.importClass('android.content.Intent')
|
||||
const Settings = plus.android.importClass('android.provider.Settings')
|
||||
const Uri = plus.android.importClass('android.net.Uri')
|
||||
const Build = plus.android.importClass('android.os.Build')
|
||||
|
||||
// Android 8.0+ 使用 REQUEST_INSTALL_PACKAGES 权限
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
const PackageManager = plus.android.importClass('android.content.pm.PackageManager')
|
||||
const pm = main.getPackageManager()
|
||||
const hasPermission = pm.canRequestPackageInstalls()
|
||||
|
||||
if (!hasPermission) {
|
||||
// 请求安装权限
|
||||
uni.showModal({
|
||||
title: '需要安装权限',
|
||||
content: '应用需要安装权限才能更新,是否前往设置开启?',
|
||||
confirmText: '前往设置',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
const intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
|
||||
const uri = Uri.parse('package:' + pkName)
|
||||
intent.setData(uri)
|
||||
main.startActivity(intent)
|
||||
|
||||
// 提示用户开启权限后重新安装
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请在设置中开启"允许安装未知应用"权限,然后返回应用重新更新',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('打开设置失败:', e)
|
||||
uni.showToast({
|
||||
title: '无法打开设置',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 plus.runtime.install 安装 APK
|
||||
plus.runtime.install(
|
||||
filePath,
|
||||
{
|
||||
force: false // 不强制安装
|
||||
},
|
||||
() => {
|
||||
uni.showToast({
|
||||
title: '安装成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 安装成功后,延迟退出应用以便用户重启
|
||||
setTimeout(() => {
|
||||
plus.runtime.quit()
|
||||
}, 1500)
|
||||
},
|
||||
(error) => {
|
||||
console.error('安装失败:', error)
|
||||
uni.showModal({
|
||||
title: '安装失败',
|
||||
content: '请检查是否允许安装未知来源应用,或手动安装下载的 APK 文件',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
})
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('安装 APK 异常:', error)
|
||||
uni.showToast({
|
||||
title: '安装失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化版本检查(在 App 启动时调用)
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {boolean} options.silent - 是否静默检查(默认 false)
|
||||
* @param {number} options.delay - 延迟检查时间(毫秒,默认 2000)
|
||||
*/
|
||||
export const initVersionCheck = (options = {}) => {
|
||||
const { silent = false, delay = 2000 } = options
|
||||
|
||||
setTimeout(() => {
|
||||
checkForUpdate(silent).then(updateInfo => {
|
||||
if (updateInfo) {
|
||||
showUpdateDialog(updateInfo)
|
||||
}
|
||||
})
|
||||
}, delay)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user