feat(升级中心): 实现备用更新方案并优化版本检查逻辑
This commit is contained in:
@@ -7,8 +7,8 @@ export const ENV = process.env.NODE_ENV || 'development';
|
|||||||
*/
|
*/
|
||||||
const BASE_URL_MAP = {
|
const BASE_URL_MAP = {
|
||||||
development: {
|
development: {
|
||||||
// MAIN: 'http://192.168.110.100:9300/pb/', // 张川川
|
MAIN: 'http://192.168.110.100:9300/pb/', // 张川川
|
||||||
MAIN: 'https://global.nuttyreading.com/', // 线上
|
// MAIN: 'https://global.nuttyreading.com/', // 线上
|
||||||
// PAYMENT: 'https://dev-pay.example.com', // 暂时用不到
|
// PAYMENT: 'https://dev-pay.example.com', // 暂时用不到
|
||||||
// CDN: 'https://cdn-dev.example.com', // 暂时用不到
|
// CDN: 'https://cdn-dev.example.com', // 暂时用不到
|
||||||
},
|
},
|
||||||
@@ -21,7 +21,6 @@ const BASE_URL_MAP = {
|
|||||||
|
|
||||||
export const APP_INFO = {
|
export const APP_INFO = {
|
||||||
TYPE: 'abroad', // APP 名称
|
TYPE: 'abroad', // APP 名称
|
||||||
VERSION_CODE: '1.0.0', // APP 版本号,可能升级的时候会用,这里需要再确定?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const REQUEST_TIMEOUT = 30000;
|
export const REQUEST_TIMEOUT = 30000;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { IRequestOptions } from '../types'
|
import type { IRequestOptions } from '../types'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { APP_INFO } from '@/api/config'
|
import { APP_INFO } from '@/api/config'
|
||||||
|
import { getCurrentVersion } from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version'
|
||||||
|
|
||||||
export function requestInterceptor(options: IRequestOptions): IRequestOptions {
|
export function requestInterceptor(options: IRequestOptions): IRequestOptions {
|
||||||
const headers = { ...(options.headers || {}) }
|
const headers = { ...(options.headers || {}) }
|
||||||
@@ -21,8 +22,11 @@ export function requestInterceptor(options: IRequestOptions): IRequestOptions {
|
|||||||
headers['Content-Type'] = 'application/json;charset=UTF-8'
|
headers['Content-Type'] = 'application/json;charset=UTF-8'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCurrentVersion().then((version_code: string) => {
|
||||||
|
headers['version_code'] = version_code || ''
|
||||||
|
})
|
||||||
|
|
||||||
headers['appType'] = APP_INFO.TYPE
|
headers['appType'] = APP_INFO.TYPE
|
||||||
headers['version_code'] = APP_INFO.VERSION_CODE || '1.0.0'
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
14
api/modules/sys.ts
Normal file
14
api/modules/sys.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { mainClient, skeletonClient } from '@/api/clients'
|
||||||
|
import type { IApiResponse } from '@/api/types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求更新包
|
||||||
|
*/
|
||||||
|
export async function requestUpdatePackage(type: string, version: string) {
|
||||||
|
const res = await skeletonClient.request<IApiResponse>({
|
||||||
|
url: 'common/apkConfig/getUpdateUrl',
|
||||||
|
method: 'POST',
|
||||||
|
data: { type, version }
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"coin": "Coin",
|
"coin": "Coin",
|
||||||
"days": "Days",
|
"days": "Days",
|
||||||
"and": "and",
|
"and": "and",
|
||||||
"call": "Call"
|
"queryFailed": "Query failed"
|
||||||
},
|
},
|
||||||
"tabar.course": "COURSE",
|
"tabar.course": "COURSE",
|
||||||
"tabar.book": "EBOOK",
|
"tabar.book": "EBOOK",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"coin": "天医币",
|
"coin": "天医币",
|
||||||
"days": "天",
|
"days": "天",
|
||||||
"and": "和",
|
"and": "和",
|
||||||
"call": "拨打电话"
|
"queryFailed": "查询失败"
|
||||||
},
|
},
|
||||||
"tabar.course": "课程",
|
"tabar.course": "课程",
|
||||||
"tabar.book": "图书",
|
"tabar.book": "图书",
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useSysStore } from '@/stores/sys'
|
import { useSysStore } from '@/stores/sys'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
@@ -74,6 +74,7 @@ import { useMessage } from '@/uni_modules/wot-design-uni'
|
|||||||
import { makePhoneCall, copyToClipboard } from '@/utils/index'
|
import { makePhoneCall, copyToClipboard } from '@/utils/index'
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
import update from "@/uni_modules/uni-upgrade-center-app/utils/check-update";
|
import update from "@/uni_modules/uni-upgrade-center-app/utils/check-update";
|
||||||
|
import { getCurrentVersion } from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version'
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n()
|
||||||
@@ -89,6 +90,9 @@ const statusBarHeight = ref(0)
|
|||||||
const showQrCode = ref(false)
|
const showQrCode = ref(false)
|
||||||
const showLanguageSelect = ref(false)
|
const showLanguageSelect = ref(false)
|
||||||
|
|
||||||
|
// 当前版本号
|
||||||
|
const currentVersion = ref('')
|
||||||
|
|
||||||
// 可选语言列表
|
// 可选语言列表
|
||||||
const availableLanguages = computed(() => [
|
const availableLanguages = computed(() => [
|
||||||
{ code: 'zh-Hans', name: t('locale.zh-hans') },
|
{ code: 'zh-Hans', name: t('locale.zh-hans') },
|
||||||
@@ -131,7 +135,7 @@ const settingItems = computed(() => [
|
|||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
label: t('user.checkVersion'),
|
label: t('user.checkVersion'),
|
||||||
value: '',
|
value: currentVersion.value,
|
||||||
type: 'version'
|
type: 'version'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@@ -163,14 +167,14 @@ const handleSettingClick = (item: any) => {
|
|||||||
* 拨打电话
|
* 拨打电话
|
||||||
*/
|
*/
|
||||||
const handlePhoneCall = (phoneNumber: string, title: string) => {
|
const handlePhoneCall = (phoneNumber: string, title: string) => {
|
||||||
makePhoneCall(phoneNumber, title, t)
|
makePhoneCall(phoneNumber, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 复制到剪贴板
|
* 复制到剪贴板
|
||||||
*/
|
*/
|
||||||
const handleCopyEmail = (content: string, title: string) => {
|
const handleCopyEmail = (content: string, title: string) => {
|
||||||
copyToClipboard(content, title, t)
|
copyToClipboard(content, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,14 +213,27 @@ const selectLanguage = (languageCode: string) => {
|
|||||||
*/
|
*/
|
||||||
const checkVersion = async () => {
|
const checkVersion = async () => {
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
var info = await update();
|
uni.showLoading()
|
||||||
console.log('版本检测信息', info)
|
try {
|
||||||
if(info.result.code == 0){
|
const info = await update();
|
||||||
|
if(info.result.code == 0){
|
||||||
|
uni.showToast({
|
||||||
|
title:info.result.message,
|
||||||
|
icon:'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('版本检测失败:', error)
|
||||||
|
const msg = error?.hasOwnProperty('message') && error.message
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title:info.result.message,
|
title: msg || t('user.checkVersionFailed'),
|
||||||
icon:'none'
|
icon: 'none',
|
||||||
|
duration: 5000
|
||||||
})
|
})
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifndef APP-PLUS
|
// #ifndef APP-PLUS
|
||||||
@@ -272,6 +289,12 @@ const performLogout = () => {
|
|||||||
url: '/pages/login/login'
|
url: '/pages/login/login'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
currentVersion.value = await getCurrentVersion()
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,31 +1,124 @@
|
|||||||
export default function() {
|
import { requestUpdatePackage } from '@/api/modules/sys'
|
||||||
// #ifdef APP-PLUS
|
import { getMaxVersion } from './tools'
|
||||||
return new Promise((resolve, reject) => {
|
import { getFileExtension } from '@/utils'
|
||||||
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
|
|
||||||
let data = {
|
/**
|
||||||
action: 'checkVersion',
|
* 统一检查更新入口
|
||||||
appid: plus.runtime.appid,
|
*/
|
||||||
appVersion: plus.runtime.version,
|
export default async function checkUpdate() {
|
||||||
wgtVersion: widgetInfo.version
|
// #ifdef APP-PLUS
|
||||||
}
|
try {
|
||||||
uniCloud.callFunction({
|
const upgradeData = await getUpgradeCheckData()
|
||||||
name: 'uni-upgrade-center',
|
|
||||||
data,
|
// 优先调用 uni-upgrade-center(3 秒超时)
|
||||||
success: (e) => {
|
const result = await withTimeout(
|
||||||
resolve(e)
|
callUpgradeCenter(upgradeData),
|
||||||
},
|
3000
|
||||||
fail: (error) => {
|
)
|
||||||
reject(error)
|
|
||||||
}
|
console.log('检查版本更新成功:', result)
|
||||||
})
|
return result
|
||||||
})
|
} catch (err) {
|
||||||
})
|
console.warn('uniCloud更新方案失败,启用备用方案:', err?.message)
|
||||||
// #endif
|
return await useBackupUpdate()
|
||||||
// #ifndef APP-PLUS
|
}
|
||||||
return new Promise((resolve, reject) => {
|
// #endif
|
||||||
reject({
|
|
||||||
message: '请在App中使用'
|
// #ifndef APP-PLUS
|
||||||
})
|
throw { message: '请在 App 中使用' }
|
||||||
})
|
// #endif
|
||||||
// #endif
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备用更新方案
|
||||||
|
*/
|
||||||
|
async function useBackupUpdate() {
|
||||||
|
const currentVersion = await getCurrentVersion()
|
||||||
|
|
||||||
|
if (!currentVersion) {
|
||||||
|
throw { message: '获取当前版本号失败' }
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('当前版本号:', currentVersion)
|
||||||
|
|
||||||
|
const res = await requestUpdatePackage('10', currentVersion)
|
||||||
|
|
||||||
|
if (!res || !res.updateUrl) {
|
||||||
|
throw { message: '没有匹配的更新包,当前版本无需升级,如您确定有更新,请卸载本版本后前往应用市场重新安装' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将服务器返回的更新信息转换为 uni-upgrade-center 格式
|
||||||
|
return {
|
||||||
|
result: {
|
||||||
|
...res,
|
||||||
|
url: res.updateUrl,
|
||||||
|
platform: ['Android', 'Ios'],
|
||||||
|
type: getFileExtension(res.updateUrl),
|
||||||
|
is_mandatory: true,
|
||||||
|
is_backup_update: true,
|
||||||
|
title: "更新",
|
||||||
|
contents: "当前版本已经弃用,请立即更新",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uni-upgrade-center 调用 Promise 化
|
||||||
|
*/
|
||||||
|
function callUpgradeCenter(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uniCloud.callFunction({
|
||||||
|
name: 'uni-upgrade-center',
|
||||||
|
data,
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时控制
|
||||||
|
*/
|
||||||
|
function withTimeout(promise, timeout) {
|
||||||
|
return Promise.race([
|
||||||
|
promise,
|
||||||
|
new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('请求超时')), timeout)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 upgrade-center 所需参数
|
||||||
|
*/
|
||||||
|
function getUpgradeCheckData() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
plus.runtime.getProperty(
|
||||||
|
plus.runtime.appid,
|
||||||
|
(widgetInfo) => {
|
||||||
|
resolve({
|
||||||
|
action: 'checkVersion',
|
||||||
|
appid: plus.runtime.appid,
|
||||||
|
appVersion: plus.runtime.version,
|
||||||
|
wgtVersion: widgetInfo.version
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前客户端版本(app / wgt 取最大)
|
||||||
|
*/
|
||||||
|
export async function getCurrentVersion() {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
const widgetInfo = await new Promise(resolve => {
|
||||||
|
plus.runtime.getProperty(plus.runtime.appid, resolve)
|
||||||
|
})
|
||||||
|
|
||||||
|
return getMaxVersion(
|
||||||
|
plus.runtime.version,
|
||||||
|
widgetInfo.version
|
||||||
|
)
|
||||||
|
// #endif
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ export default function() {
|
|||||||
if (!e.result) return;
|
if (!e.result) return;
|
||||||
const {
|
const {
|
||||||
code,
|
code,
|
||||||
|
is_backup_update, // 是否备用更新
|
||||||
message,
|
message,
|
||||||
is_silently, // 是否静默更新
|
is_silently, // 是否静默更新
|
||||||
url, // 安装包下载地址
|
url, // 安装包下载地址
|
||||||
@@ -17,17 +18,20 @@ export default function() {
|
|||||||
type // 安装包类型
|
type // 安装包类型
|
||||||
} = e.result;
|
} = e.result;
|
||||||
|
|
||||||
// 此处逻辑仅为实例,可自行编写
|
const hasUpdate = code > 0 || is_backup_update
|
||||||
if (code > 0) {
|
|
||||||
// 腾讯云和阿里云下载链接不同,需要处理一下,阿里云会原样返回
|
// 如果不是备用更新,需要处理下载链接
|
||||||
|
if (!is_backup_update) {
|
||||||
const {
|
const {
|
||||||
fileList
|
fileList
|
||||||
} = await uniCloud.getTempFileURL({
|
} = await uniCloud.getTempFileURL({
|
||||||
fileList: [url]
|
fileList: [url]
|
||||||
});
|
});
|
||||||
if (fileList[0].tempFileURL)
|
e.result.url = fileList[0].tempFileURL;
|
||||||
e.result.url = fileList[0].tempFileURL;
|
}
|
||||||
|
|
||||||
|
// 此处逻辑仅为实例,可自行编写
|
||||||
|
if (hasUpdate) {
|
||||||
resolve(e)
|
resolve(e)
|
||||||
|
|
||||||
// 静默更新,只有wgt有
|
// 静默更新,只有wgt有
|
||||||
|
|||||||
32
uni_modules/uni-upgrade-center-app/utils/tools.js
Normal file
32
uni_modules/uni-upgrade-center-app/utils/tools.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 比较版本号
|
||||||
|
* @param {string} v1 - 版本号1
|
||||||
|
* @param {string} v2 - 版本号2
|
||||||
|
* @returns {number} - 1表示v1大于v2,-1表示v1小于v2,0表示相等
|
||||||
|
*/
|
||||||
|
export function compareVersion(v1, v2) {
|
||||||
|
const arr1 = v1.split('.').map(Number)
|
||||||
|
const arr2 = v2.split('.').map(Number)
|
||||||
|
const maxLen = Math.max(arr1.length, arr2.length)
|
||||||
|
|
||||||
|
for (let i = 0; i < maxLen; i++) {
|
||||||
|
const n1 = arr1[i] ?? 0
|
||||||
|
const n2 = arr2[i] ?? 0
|
||||||
|
|
||||||
|
if (n1 > n2) return 1
|
||||||
|
if (n1 < n2) return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取较大版本号
|
||||||
|
* @param {string} v1 - 版本号1
|
||||||
|
* @param {string} v2 - 版本号2
|
||||||
|
* @returns {string} - 较大的版本号
|
||||||
|
*/
|
||||||
|
export function getMaxVersion(v1, v2) {
|
||||||
|
return compareVersion(v1, v2) >= 0 ? v1 : v2
|
||||||
|
}
|
||||||
@@ -130,3 +130,26 @@ export function parseTime(time: any, cFormat: string) {
|
|||||||
})
|
})
|
||||||
return time_str
|
return time_str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件扩展名
|
||||||
|
* @param {string} url - 文件的URL
|
||||||
|
* @returns {string} - 文件的扩展名(不包含点号),如果没有扩展名则返回空字符串
|
||||||
|
*/
|
||||||
|
export function getFileExtension(url: string) {
|
||||||
|
// 移除查询参数和hash
|
||||||
|
const cleanUrl = url.split(/[?#]/)[0];
|
||||||
|
|
||||||
|
// 获取文件名
|
||||||
|
const filename = cleanUrl.split('/').pop();
|
||||||
|
|
||||||
|
// 提取扩展名(支持多个点的情况)
|
||||||
|
const parts = filename?.split('.');
|
||||||
|
|
||||||
|
if (parts?.length && parts.length <= 1) {
|
||||||
|
return ''; // 没有扩展名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回最后一个点之后的部分
|
||||||
|
return parts?.pop() || '';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user