| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- import { ref, onUnmounted } from 'vue'
- import { useI18n } from 'vue-i18n'
- import { useProgress } from './useProgress'
- import useUserStore from '@/stores/use-user-store'
- import { ucardApi } from '@/api/ucard'
- const LAST_CHECK_KEY = 'last_update_check_time'
- const SKIP_VERSION_KEY = 'skip_update_version'
- const DOWNLOAD_CACHE_KEY = 'app_download_cache'
- export function useAppUpdate() {
- const { t } = useI18n()
- const userStore = useUserStore()
- const progress = useProgress()
- const checking = ref(false)
- const updating = ref(false)
- let downloadTask: any = null
- let networkListener: any = null
- let downloadUrl = ''
- let savedFilePath = '_downloads/app_update.wgt'
- let currentNetworkType = ''
- /* ================== 对外入口 ================== */
- async function checkUpdate() {
- // #ifdef APP-PLUS
- if (checking.value) return
- checking.value = true
- try {
- const platform = uni.getSystemInfoSync().platform
- const equipmentType = platform === 'ios' ? 'ios' : 'Android'
- const res = await ucardApi.getAppVersionDetail({ equipmentType })
- if (res.code !== 200 || !res.data) return
- const update = res.data
- const currentVersion = await getCurrentVersion()
- const needUpdate = compareVersion(update.version, currentVersion) > 0
- userStore.saveAppVersion({
- currentVersion,
- version: update.version,
- isUpdate: !needUpdate
- })
- if (!needUpdate) return
- const skip = uni.getStorageSync(SKIP_VERSION_KEY)
- if (!update.forceUpdate && skip === update.version) return
- if (update.forceUpdate) {
- showForceUpdate(update)
- } else {
- showOptionalUpdate(update)
- }
- } finally {
- checking.value = false
- }
- // #endif
- }
- /* ================== 更新流程 ================== */
- function showForceUpdate(update: any) {
- uni.showModal({
- title: t('mine.p22'),
- content: t('mine.p23'),
- showCancel: false,
- confirmText: t('mine.p35'),
- success: () => doUpdate(update)
- })
- }
- function showOptionalUpdate(update: any) {
- uni.showModal({
- title: t('mine.p24'),
- content: t('mine.p25', { version: 'v' + update.version }),
- confirmText: t('mine.p35'),
- cancelText: t('mine.p36'),
- success: res => {
- if (res.confirm) doUpdate(update)
- else uni.setStorageSync(SKIP_VERSION_KEY, update.version)
- }
- })
- }
- function doUpdate(update: any) {
- const platform = uni.getSystemInfoSync().platform
- if (platform === 'ios') {
- plus.runtime.openURL(update.iosStoreUrl)
- return
- }
- downloadWgt(update.wgtUrl)
- }
- /* ================== 下载核心 ================== */
- function downloadWgt(url: string) {
- // #ifdef APP-PLUS
- if (downloadTask) return
- downloadUrl = url
- updating.value = true
- progress.show(t('mine.p29'))
- // 获取当前网络类型
- uni.getNetworkType({
- success: (res) => {
- currentNetworkType = res.networkType
- }
- })
- startNetworkMonitor()
- downloadTask = plus.downloader.createDownload(
- url,
- { filename: savedFilePath },
- (download, status) => {
- if (status === 200 || status === 206) {
- clearCache()
- stopNetworkMonitor()
- installWgt(download.filename)
- } else {
- fail(t('mine.p28'))
- }
- }
- )
- downloadTask.addEventListener('statechanged', (d: any) => {
- const state = d.state
- if (state === 3 && d.totalSize) { // 下载中
- const p = Math.floor((d.downloadedSize / d.totalSize) * 100)
- progress.update(p, `${t('mine.p29')} ${p}%`)
- saveCache(p)
- }
- })
- downloadTask.start()
- // #endif
- }
- /* ================== 网络处理 ================== */
- function startNetworkMonitor() {
- // #ifdef APP-PLUS
- networkListener = uni.onNetworkStatusChange(res => {
- if (!downloadTask || !updating.value) return
- // 只要有网络变化,直接尝试恢复下载
- if (!res.isConnected) {
- currentNetworkType = 'none'
- try {
- downloadTask.pause && downloadTask.pause()
- } catch (e) {
- console.error('暂停下载失败:', e)
- }
- return
- }
- // 网络恢复或切换,直接 resume,不依赖 state
- currentNetworkType = res.networkType
- try {
- if (downloadTask.resume) {
- downloadTask.resume()
- } else {
- downloadTask.start()
- }
- } catch (e) {
- // 失败时尝试用 start 重试
- try {
- downloadTask.start()
- } catch (e2) {
- console.error('start() 也失败:', e2)
- }
- }
- })
- // #endif
- }
- function stopNetworkMonitor() {
- if (networkListener) {
- uni.offNetworkStatusChange(networkListener)
- networkListener = null
- }
- currentNetworkType = ''
- }
- /* ================== 安装 ================== */
- function installWgt(path: string) {
- plus.runtime.install(
- path,
- { force: false },
- () => {
- progress.hide()
- updating.value = false
- uni.showModal({
- title: t('mine.p37'),
- content: t('mine.p38'),
- showCancel: false,
- success: () => plus.runtime.restart()
- })
- },
- () => fail(t('mine.p33'))
- )
- }
- /* ================== 缓存(仅 UI) ================== */
- function saveCache(progress: number) {
- uni.setStorageSync(DOWNLOAD_CACHE_KEY, {
- url: downloadUrl,
- progress,
- time: Date.now()
- })
- }
- function clearCache() {
- uni.removeStorageSync(DOWNLOAD_CACHE_KEY)
- }
- /* ================== 兜底失败 ================== */
- function fail(msg: string) {
- progress.hide()
- updating.value = false
- stopNetworkMonitor()
- if (downloadTask) {
- try { downloadTask.abort() } catch { }
- downloadTask = null
- }
- uni.showToast({ title: msg, icon: 'none' })
- }
- onUnmounted(() => {
- stopNetworkMonitor()
- })
- return { checkUpdate }
- }
- /* ================== 工具 ================== */
- function getCurrentVersion(): Promise<string> {
- return new Promise(resolve => {
- plus.runtime.getProperty(plus.runtime.appid, info => {
- resolve(info.version)
- })
- })
- }
- function compareVersion(v1: string, v2: string) {
- const a1 = v1.split('.')
- const a2 = v2.split('.')
- const len = Math.max(a1.length, a2.length)
- for (let i = 0; i < len; i++) {
- const n1 = parseInt(a1[i] || '0')
- const n2 = parseInt(a2[i] || '0')
- if (n1 > n2) return 1
- if (n1 < n2) return -1
- }
- return 0
- }
|