1. UniApp OTA更新优化 - 移除进度条和下载中Toast(静默后台下载) - 安装成功后3秒自动重启,无用户感知 - 失败时仅console.error,不弹窗打扰用户 2. 项目分析报告 - 新增PROJECT_ANALYSIS_REPORT.md - 涵盖技术栈、数据模型、核心业务流程、潜在痛点和架构建议
181 lines
5.8 KiB
Vue
181 lines
5.8 KiB
Vue
<script>
|
||
import { getNotifications } from "./api/notification";
|
||
|
||
export default {
|
||
onLaunch() {
|
||
console.log("生产流转 T1.0.2 启动");
|
||
|
||
// 🚦 路由守卫:首页已是 scan,未登录才跳登录页(零闪烁)
|
||
const token = uni.getStorageSync("access_token") || uni.getStorageSync("refresh_token");
|
||
const user = uni.getStorageSync("user");
|
||
|
||
if (!token || !user) {
|
||
uni.reLaunch({ url: "/pages/login/login" });
|
||
}
|
||
// 已登录 → 原地渲染 scan 页,不需任何跳转
|
||
|
||
// 🚀 OTA 热更新检测(仅 App 端生效)
|
||
// #ifdef APP-PLUS
|
||
this.checkUpdate();
|
||
// #endif
|
||
},
|
||
onShow() {
|
||
console.log("App 显示");
|
||
this.updateTabBarBadge();
|
||
// 🚀 每次回到前台也检查更新,用户无需杀后台就能感知新版本
|
||
// #ifdef APP-PLUS
|
||
this.checkUpdate();
|
||
// #endif
|
||
},
|
||
onHide() {
|
||
console.log("App 隐藏");
|
||
},
|
||
methods: {
|
||
// ==========================================================
|
||
// OTA 热更新雷达 — 版本检测 + WGT 下载 + 静默安装
|
||
// ==========================================================
|
||
checkUpdate() {
|
||
// 🚀 节流:5分钟内不重复检查,避免 onShow 频繁触发弹窗骚扰
|
||
const now = Date.now();
|
||
if (this._lastUpdateCheck && now - this._lastUpdateCheck < 5 * 60 * 1000) {
|
||
console.log("[OTA] 距上次检查不足5分钟,跳过");
|
||
return;
|
||
}
|
||
this._lastUpdateCheck = now;
|
||
|
||
// 🔧 appWgtVersion 跟随 WGT 更新,解析算法: major*100 + lastNum
|
||
// "T1.0.1"→101, "T1.0.10"→110, "T1.0.99"→199
|
||
const sysInfo = uni.getSystemInfoSync();
|
||
const wgtVer = sysInfo.appWgtVersion || sysInfo.appVersion || "0";
|
||
const currentVersionCode = this.parseVersionCode(wgtVer);
|
||
const baseUrl = uni.getStorageSync("env_base_url") || "http://track_back.iris-rs.cn/api/v1";
|
||
|
||
console.log("[OTA] 本地版本:", wgtVer, "→ 数字:", currentVersionCode);
|
||
|
||
uni.request({
|
||
url: `${baseUrl}/app/check-update`,
|
||
method: "GET",
|
||
timeout: 8000,
|
||
success: (res) => {
|
||
if (res.statusCode !== 200) return;
|
||
const data = res.data;
|
||
if (!data || !data.wgt_url) {
|
||
console.log("[OTA] 服务端无可用更新包");
|
||
return;
|
||
}
|
||
|
||
const serverVersionCode = data.version_code || 0;
|
||
console.log("[OTA] 服务端版本:", data.version, "| 数字版本:", serverVersionCode);
|
||
|
||
// 客户端自行对比:服务端 > 本地 = 需要更新
|
||
if (serverVersionCode <= currentVersionCode) {
|
||
console.log("[OTA] 已是最新版本,无需更新");
|
||
return;
|
||
}
|
||
|
||
console.log("[OTA] 发现新版本:", data.version);
|
||
this.downloadAndInstall(data.wgt_url, data.version, data.description);
|
||
},
|
||
fail: () => {
|
||
console.log("[OTA] 版本检测网络失败,跳过");
|
||
},
|
||
});
|
||
},
|
||
|
||
/** 版本号→数字: "T1.0.10" → 1*100+10 = 110 */
|
||
parseVersionCode(v) {
|
||
if (!v) return 0;
|
||
const nums = v.match(/\d+/g);
|
||
if (!nums || nums.length < 2) return 0;
|
||
return parseInt(nums[0], 10) * 100 + parseInt(nums[nums.length - 1], 10);
|
||
},
|
||
|
||
downloadAndInstall(wgtUrl, newVersion, description) {
|
||
if (!wgtUrl) {
|
||
console.log("[OTA] 无 WGT 下载地址");
|
||
return;
|
||
}
|
||
|
||
const content = description
|
||
? `发现新版本 ${newVersion}\n\n${description}\n\n是否立即更新?`
|
||
: `发现新版本 ${newVersion},是否立即更新?`;
|
||
|
||
uni.showModal({
|
||
title: "版本更新",
|
||
content,
|
||
confirmText: "立即更新",
|
||
cancelText: "稍后再说",
|
||
success: (modalRes) => {
|
||
if (!modalRes.confirm) return;
|
||
|
||
// 🚀 静默更新:无进度条、无 toast、不监听 onProgressUpdate
|
||
uni.downloadFile({
|
||
url: wgtUrl,
|
||
success: (downloadRes) => {
|
||
if (downloadRes.statusCode !== 200) {
|
||
console.error("[OTA] 下载失败, statusCode:", downloadRes.statusCode);
|
||
return;
|
||
}
|
||
|
||
plus.runtime.install(
|
||
downloadRes.tempFilePath,
|
||
{ force: true },
|
||
() => {
|
||
console.log("[OTA] 安装成功,3秒后自动重启");
|
||
setTimeout(() => {
|
||
plus.runtime.restart();
|
||
}, 3000);
|
||
},
|
||
(err) => {
|
||
console.error("[OTA] 安装失败:", err.message);
|
||
}
|
||
);
|
||
},
|
||
fail: (err) => {
|
||
console.error("[OTA] 下载失败:", err.errMsg);
|
||
},
|
||
});
|
||
},
|
||
});
|
||
},
|
||
|
||
// ==========================================================
|
||
// TabBar 消息红点
|
||
// ==========================================================
|
||
async updateTabBarBadge() {
|
||
try {
|
||
let user = uni.getStorageSync("user");
|
||
if (typeof user === "string" && user) {
|
||
try { user = JSON.parse(user); } catch (e) { user = null; }
|
||
}
|
||
const userId = user?.username || user?.id || "";
|
||
if (!userId) return;
|
||
|
||
const res = await getNotifications(userId, 0, 1);
|
||
const unreadCount = res.unread_count || 0;
|
||
|
||
if (unreadCount > 0) {
|
||
uni.setTabBarBadge({
|
||
index: 2,
|
||
text: unreadCount > 99 ? "99+" : String(unreadCount),
|
||
});
|
||
} else {
|
||
uni.removeTabBarBadge({ index: 2 });
|
||
}
|
||
} catch {
|
||
// 静默失败
|
||
}
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
page {
|
||
background-color: #f3f4f6;
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
font-size: 14px;
|
||
color: #1f2937;
|
||
}
|
||
</style>
|