From 22386dae1fa3533feb83763db45a4fc007d2d344 Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期五, 28 八月 2026 09:36:25 +0800
Subject: [PATCH] fix0806: 实现自动更新功能
---
traditionalApp/pages/mine/mine.vue | 49 +++++++++++++++++++++++-
traditionalApp/utils/app-version.js | 65 ++++++++++++++++++++++++++++++--
traditionalApp/config/index.js | 2
3 files changed, 109 insertions(+), 7 deletions(-)
diff --git a/traditionalApp/config/index.js b/traditionalApp/config/index.js
index 968c0f7..5c925dd 100644
--- a/traditionalApp/config/index.js
+++ b/traditionalApp/config/index.js
@@ -2,7 +2,7 @@
// BASE_URL: 'http://221.182.45.100:48080',
BASE_URL: 'https://haizhentong.com',
OPENINSTALL_URL: 'https://m.malink.cn/s/7bIzeu',
- APP_STORE_URL: '',
+ APP_STORE_URL: 'https://apps.apple.com/cn/app/%E6%B5%B7%E8%AF%8A%E5%BA%B7%E9%81%93/id6762694057',
uploadUrl: "https://haizhentong.com/api/open/file/upload",
imageUrl: 'http://221.182.45.100:11224/images/'
};
diff --git a/traditionalApp/pages/mine/mine.vue b/traditionalApp/pages/mine/mine.vue
index f057f8a..70770e0 100644
--- a/traditionalApp/pages/mine/mine.vue
+++ b/traditionalApp/pages/mine/mine.vue
@@ -36,10 +36,11 @@
</view>
<view class="list-section card-box">
- <view class="list-item" v-for="(item, index) in bottomMenuList" :key="`${item.title}-${index}`" @tap="goPage(item)">
+ <view class="list-item" v-for="(item, index) in bottomMenuList" :key="`${item.title}-${index}`" @tap="handleBottomMenuClick(item)">
<view class="list-left">
<image class="list-icon" :class="item.iconClass" :src="item.icon" mode="aspectFit"></image>
<text class="list-title">{{ item.title }}</text>
+ <view v-if="item.action === 'app-update' && hasAppUpdate" class="update-dot"></view>
</view>
<u-icon name="arrow-right" color="#999" size="16"></u-icon>
</view>
@@ -55,7 +56,7 @@
import { onLoad, onShow } from '@dcloudio/uni-app'
import { getNoticeList } from '../notice/service'
import { getUserCenter } from './service'
-import { getRuntimeVersionName } from '@/utils/app-version.js'
+import { getRuntimeVersionName, promptAppVersionUpdate, queryAppVersionUpdate } from '@/utils/app-version.js'
import { hasStoredSession } from '@/utils/auth-session.js'
import manifestConfig from '../../manifest.json'
@@ -95,6 +96,7 @@
url: string
iconClass?: string
requiresLogin?: boolean
+ action?: 'app-update'
}
interface NoticeSummaryItem {
@@ -105,6 +107,8 @@
const unreadNoticeTotal = ref(0)
const manifestVersionName = String(manifestConfig.versionName || '')
const appVersionText = ref(manifestVersionName)
+const hasAppUpdate = ref(false)
+const appUpdateInfo = ref<any>(null)
const userCenter = ref<UserCenterData>(createDefaultUserCenter())
const userRole = computed<UserRole>(() => {
return isDoctorLikeUser(userCenter.value) ? 'doctor' : 'personal'
@@ -160,6 +164,7 @@
{ title: '我的课程', icon: '/static/image/mine/myClass.png', url: '/pages/myCourses/myCourses', iconClass: 'icon-myclass', requiresLogin: true },
{ title: '帮助中心', icon: '/static/image/mine/help.png', url: '/pages/helpCenter/helpCenter', iconClass: 'icon-help' },
{ title: '意见反馈', icon: '/static/image/mine/message.png', url: '/pages/feedback/feedback', iconClass: 'icon-feedback' },
+ { title: '更新版本', icon: '/static/image/mine/refish.png', url: '', iconClass: 'icon-update', action: 'app-update' as const },
]
const liveOrderItem = { title: '直播订单', icon: '/static/image/mine/dingdan.png', url: buildWebviewUrl(WEIZAN_LIVE_ORDER_URL, '直播订单'), iconClass: 'icon-live-order', requiresLogin: true }
const liveStatsList = Number(userCenter.value.type) === 1
@@ -227,7 +232,21 @@
syncCachedUserInfo()
loadUserCenter()
loadUnreadNoticeTotal()
+ loadAppUpdateStatus()
})
+
+/**
+ * 静默检查版本更新,仅用于控制更新入口红点。
+ */
+async function loadAppUpdateStatus() {
+ try {
+ appUpdateInfo.value = await queryAppVersionUpdate()
+ hasAppUpdate.value = !!appUpdateInfo.value
+ } catch {
+ appUpdateInfo.value = null
+ hasAppUpdate.value = false
+ }
+}
/**
* 拉取当前登录用户的通知未读总数。
@@ -381,6 +400,24 @@
return
}
goPage(item)
+}
+
+/**
+ * 更新版本使用统一弹窗,其余菜单保持原跳转行为。
+ */
+async function handleBottomMenuClick(item: MenuItem) {
+ if (item.action !== 'app-update') {
+ goPage(item)
+ return
+ }
+ if (!appUpdateInfo.value) {
+ await loadAppUpdateStatus()
+ }
+ if (!appUpdateInfo.value) {
+ uni.showToast({ title: '当前已是最新版本', icon: 'none' })
+ return
+ }
+ promptAppVersionUpdate(appUpdateInfo.value)
}
/**
@@ -710,6 +747,14 @@
color: #1A1A1A;
margin-left: 22rpx;
}
+
+ .update-dot {
+ width: 16rpx;
+ height: 16rpx;
+ margin-left: 12rpx;
+ border-radius: 50%;
+ background: #E64340;
+ }
}
}
}
diff --git a/traditionalApp/utils/app-version.js b/traditionalApp/utils/app-version.js
index 308eafa..8e6b9e1 100644
--- a/traditionalApp/utils/app-version.js
+++ b/traditionalApp/utils/app-version.js
@@ -133,7 +133,7 @@
success: (modalResult) => {
if (modalResult.confirm) {
confirmed = true
- openDownloadUrl(updateInfo.downloadUrl)
+ startAppUpdate(updateInfo.downloadUrl)
return
}
},
@@ -158,13 +158,70 @@
}
/**
- * 打开后台配置的安装包或下载地址。
+ * iOS 跳转 App Store;Android 下载完整 APK 后拉起系统安装界面。
*/
-function openDownloadUrl(downloadUrl) {
+function startAppUpdate(downloadUrl) {
if (!downloadUrl) {
return
}
- plus.runtime.openURL(downloadUrl)
+ if (isIosPlatform()) {
+ plus.runtime.openURL(downloadUrl)
+ return
+ }
+ const downloadTask = plus.downloader.createDownload(downloadUrl, {}, (download, status) => {
+ if (status !== 200 || !download.filename) {
+ handleUpdateFailure('安装包下载失败,请重试')
+ return
+ }
+ const apkPath = plus.io.convertLocalFileSystemURL(download.filename)
+ plus.runtime.install(apkPath, {}, undefined, () => {
+ handleUpdateFailure('安装失败,请重试')
+ })
+ })
+ downloadTask.start()
+}
+
+/**
+ * 静默查询当前 APP 是否存在可用更新,供页面展示更新状态。
+ */
+export function queryAppVersionUpdate() {
+ // #ifndef APP-PLUS
+ return Promise.resolve(null)
+ // #endif
+ return resolveRuntimeVersionInfo()
+ .then((runtimeInfo) => requestVersionCheck(runtimeInfo))
+ .then((payload) => {
+ const updateInfo = payload && payload.code === 0 ? payload.data : null
+ if (!updateInfo || updateInfo.hasUpdate !== true) {
+ return null
+ }
+ const downloadUrl = resolveUpdateUrl(updateInfo)
+ return downloadUrl ? Object.assign({}, updateInfo, { downloadUrl }) : null
+ })
+}
+
+/**
+ * 使用统一更新弹窗展示指定版本信息。
+ */
+export function promptAppVersionUpdate(updateInfo) {
+ if (updateInfo && updateInfo.hasUpdate === true) {
+ showUpdateModal(updateInfo)
+ }
+}
+
+/**
+ * 下载或安装失败后重新开放更新入口。
+ */
+function handleUpdateFailure(title) {
+ uni.showToast({
+ title,
+ icon: 'none'
+ })
+ if (latestUpdateInfo) {
+ setTimeout(() => {
+ showUpdateModal(latestUpdateInfo)
+ }, 1500)
+ }
}
/**
--
Gitblit v1.7.1