Files
CarLog/client/src/stores/pwa.js
T
wsh5485 fe17886ac4 feat: 洗车管理系统 v2.8 — 个人 detailer 单用户全栈应用
- 车辆 / 洗车 / 加油 / 充电 / 保养 / 保险 完整 CRUD + 软删
- AI 截图识别(5 类型 OCR schema):OpenAI 兼容 + MiniMax M3
- 化学品 / Grocy 实例对接 + 库存镜像同步
- 仪表盘:30 天频次 + 健康度 + 同比环比 + 油价趋势 + 年均养护
- 月度报表:Excel 6 sheet + PDF
- PWA:manifest / SW / 离线缓存 / iOS 引导
- 安全:bcrypt + CSRF + 登录锁定(IP/用户/全局三级)+ 401 自动跳登录 + 表单草稿
- 高 ROI 8 功能:里程/提醒/成本/搜索/标签/通知/同比/成就
- 3 个新 migration(0016/0017/0018)+ 18 个迁移全幂等
- 101/101 测试通过(含 ipRateLimit / CSRF / retry / stats / tags / notifications)
- 部署:宝塔面板文档 + PM2 + Nginx
2026-06-20 21:11:54 +08:00

90 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// client/src/stores/pwa.js — PWA 状态:更新提示 / 安装提示 / 离线
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const usePwaStore = defineStore('pwa', () => {
/** true = 已有新版本 SW 等激活,需要刷新 */
const needRefresh = ref(false);
/** true = 资源已缓存完,可离线用 */
const offlineReady = ref(false);
/** 浏览器/桌面触发安装的事件(Android/Desktop Chrome */
const installPromptEvent = ref(null);
/** 已安装(standalone 模式运行) */
const isInstalled = computed(() => {
if (typeof window === 'undefined') return false;
return (
window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true // iOS Safari
);
});
/** iOS 设备 + Safari + 未安装 → 引导走"分享 → 添加到主屏幕" */
const isIosSafari = computed(() => {
if (typeof window === 'undefined') return false;
const ua = window.navigator.userAgent;
return /iPad|iPhone|iPod/.test(ua) && /Safari/.test(ua) && !/CriOS|FxiOS|EdgiOS/.test(ua);
});
let updateFn = null;
function bindRegisterSw(registerFn) {
updateFn = registerFn;
}
function triggerNeedRefresh() {
needRefresh.value = true;
}
function triggerOfflineReady() {
offlineReady.value = true;
// 5s 后自动收起
setTimeout(() => (offlineReady.value = false), 5000);
}
async function applyUpdate() {
if (updateFn) {
await updateFn(true);
needRefresh.value = false;
} else {
window.location.reload();
}
}
function dismissNeedRefresh() {
needRefresh.value = false;
}
function dismissOfflineReady() {
offlineReady.value = false;
}
async function promptInstall() {
const e = installPromptEvent.value;
if (!e) return false;
e.prompt();
const choice = await e.userChoice;
installPromptEvent.value = null;
return choice.outcome === 'accepted';
}
function captureInstallPrompt(e) {
e.preventDefault();
installPromptEvent.value = e;
}
// 全局监听 beforeinstallprompt
if (typeof window !== 'undefined') {
window.addEventListener('beforeinstallprompt', captureInstallPrompt);
window.addEventListener('appinstalled', () => {
installPromptEvent.value = null;
});
}
return {
needRefresh,
offlineReady,
installPromptEvent,
isInstalled,
isIosSafari,
bindRegisterSw,
triggerNeedRefresh,
triggerOfflineReady,
applyUpdate,
dismissNeedRefresh,
dismissOfflineReady,
promptInstall,
};
});