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