hejianhao
2026-08-28 22386dae1fa3533feb83763db45a4fc007d2d344
fix0806: 实现自动更新功能
3个文件已修改
116 ■■■■■ 已修改文件
traditionalApp/config/index.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traditionalApp/pages/mine/mine.vue 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traditionalApp/utils/app-version.js 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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/'
};
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;
            }
        }
    }
}
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)
    }
}
/**