feat: 添加系统设置页面,集成应用更新和缓存管理功能,优化用户体验

This commit is contained in:
2025-12-20 01:32:33 +07:00
parent 521585035e
commit 2703e6d007
8 changed files with 403 additions and 4 deletions

View File

@@ -0,0 +1,103 @@
/**
* 应用更新检查组合式函数
*/
export function useAppUpdate() {
const isChecking = ref(false);
const hasUpdate = ref(false);
const latestVersion = ref("");
const currentVersion = ref("1.0.0"); // 从 package.json 或环境变量读取
/**
* 检查是否有新版本
*/
async function checkForUpdate(): Promise<{
hasUpdate: boolean;
currentVersion: string;
latestVersion?: string;
}> {
isChecking.value = true;
try {
// 方案1: 从服务器检查版本(需要后端 API
// const response = await fetch('/api/version');
// const { version } = await response.json();
// latestVersion.value = version;
// 方案2: 检查 Service Worker 更新PWA 应用)
if ("serviceWorker" in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.update();
const hasNewWorker = registration.waiting !== null || registration.installing !== null;
hasUpdate.value = hasNewWorker;
if (hasNewWorker) {
return {
hasUpdate: true,
currentVersion: currentVersion.value,
latestVersion: "新版本可用",
};
}
}
}
// 模拟检查(实际应用中需要替换为真实的 API 调用)
await new Promise(resolve => setTimeout(resolve, 1000));
// 暂时返回无更新
return {
hasUpdate: false,
currentVersion: currentVersion.value,
};
}
catch (error) {
console.error("检查更新失败:", error);
throw error;
}
finally {
isChecking.value = false;
}
}
/**
* 应用更新(重新加载应用)
*/
async function applyUpdate(): Promise<void> {
if ("serviceWorker" in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration?.waiting) {
// 通知 Service Worker 跳过等待,立即激活
registration.waiting.postMessage({ type: "SKIP_WAITING" });
// 等待 Service Worker 激活后重新加载页面
navigator.serviceWorker.addEventListener("controllerchange", () => {
window.location.reload();
});
return;
}
}
// 如果没有 Service Worker直接重新加载
window.location.reload();
}
/**
* 强制刷新应用(清除缓存后重新加载)
*/
async function forceReload(): Promise<void> {
if ("caches" in window) {
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map(name => caches.delete(name)));
}
window.location.reload();
}
return {
isChecking,
hasUpdate,
currentVersion,
latestVersion,
checkForUpdate,
applyUpdate,
forceReload,
};
}

View File

@@ -0,0 +1,122 @@
/**
* 获取应用缓存大小的组合式函数
*/
export function useCacheSize() {
const cacheSize = ref<string>("计算中...");
const isLoading = ref(false);
/**
* 计算当前应用使用的缓存大小
*/
async function calculateCacheSize() {
isLoading.value = true;
try {
if ("storage" in navigator && "estimate" in navigator.storage) {
// 使用 Storage API 获取精确的存储使用量
const estimate = await navigator.storage.estimate();
const usageInMB = ((estimate.usage || 0) / (1024 * 1024)).toFixed(2);
cacheSize.value = `${usageInMB} MB`;
}
else {
// 降级方案:计算 localStorage 大小
let totalSize = 0;
for (const key in localStorage) {
if (Object.prototype.hasOwnProperty.call(localStorage, key)) {
totalSize += localStorage[key].length + key.length;
}
}
const sizeInKB = (totalSize / 1024).toFixed(2);
cacheSize.value = `${sizeInKB} KB (仅 localStorage)`;
}
}
catch (error) {
console.error("计算缓存大小失败:", error);
cacheSize.value = "未知";
}
finally {
isLoading.value = false;
}
}
/**
* 获取缓存大小的原始字节数
*/
async function getCacheSizeInBytes(): Promise<number> {
try {
if ("storage" in navigator && "estimate" in navigator.storage) {
const estimate = await navigator.storage.estimate();
return estimate.usage || 0;
}
else {
let totalSize = 0;
for (const key in localStorage) {
if (Object.prototype.hasOwnProperty.call(localStorage, key)) {
totalSize += localStorage[key].length + key.length;
}
}
return totalSize;
}
}
catch (error) {
console.error("获取缓存大小失败:", error);
return 0;
}
}
/**
* 清除应用缓存
*/
async function clearCache(): Promise<void> {
isLoading.value = true;
try {
// 清除 localStorage
localStorage.clear();
// 清除 sessionStorage
sessionStorage.clear();
// 清除 Cache API 缓存
if ("caches" in window) {
const cacheNames = await caches.keys();
await Promise.all(
cacheNames.map(cacheName => caches.delete(cacheName)),
);
}
// 清除 IndexedDB如果需要
if ("indexedDB" in window) {
const databases = await indexedDB.databases();
await Promise.all(
databases.map((db) => {
if (db.name) {
return new Promise<void>((resolve, reject) => {
const request = indexedDB.deleteDatabase(db.name!);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
return Promise.resolve();
}),
);
}
// 重新计算缓存大小
await calculateCacheSize();
}
catch (error) {
console.error("清除缓存失败:", error);
throw error;
}
finally {
isLoading.value = false;
}
}
return {
cacheSize,
isLoading,
calculateCacheSize,
getCacheSizeInBytes,
clearCache,
};
}