版本更新接口

This commit is contained in:
WindowBird 2025-11-19 11:54:52 +08:00
parent f496010e73
commit d3fe6f39d2
5 changed files with 389 additions and 3 deletions

11
App.vue
View File

@ -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')

View File

@ -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
View 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 // 不自动提示错误
}
});
};

View File

@ -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
View 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)
}