diff --git a/Web/PWA-README.md b/Web/PWA-README.md new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/Web/PWA-README.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Web/index.html b/Web/index.html index c86fb8d..b6e5d01 100644 --- a/Web/index.html +++ b/Web/index.html @@ -1,15 +1,37 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + 账单管理 diff --git a/Web/public/icons/icon-128x128.svg b/Web/public/icons/icon-128x128.svg new file mode 100644 index 0000000..44bcb59 --- /dev/null +++ b/Web/public/icons/icon-128x128.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-144x144.svg b/Web/public/icons/icon-144x144.svg new file mode 100644 index 0000000..48bc40b --- /dev/null +++ b/Web/public/icons/icon-144x144.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-152x152.svg b/Web/public/icons/icon-152x152.svg new file mode 100644 index 0000000..aa159c7 --- /dev/null +++ b/Web/public/icons/icon-152x152.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-192x192.svg b/Web/public/icons/icon-192x192.svg new file mode 100644 index 0000000..61d2062 --- /dev/null +++ b/Web/public/icons/icon-192x192.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-384x384.svg b/Web/public/icons/icon-384x384.svg new file mode 100644 index 0000000..f7c72af --- /dev/null +++ b/Web/public/icons/icon-384x384.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-512x512.svg b/Web/public/icons/icon-512x512.svg new file mode 100644 index 0000000..6e773a0 --- /dev/null +++ b/Web/public/icons/icon-512x512.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-72x72.svg b/Web/public/icons/icon-72x72.svg new file mode 100644 index 0000000..1ed6c9f --- /dev/null +++ b/Web/public/icons/icon-72x72.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-96x96.svg b/Web/public/icons/icon-96x96.svg new file mode 100644 index 0000000..5b239fc --- /dev/null +++ b/Web/public/icons/icon-96x96.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Web/public/icons/icon-temp.svg b/Web/public/icons/icon-temp.svg new file mode 100644 index 0000000..2c855ac --- /dev/null +++ b/Web/public/icons/icon-temp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Web/public/manifest.json b/Web/public/manifest.json new file mode 100644 index 0000000..cab70d3 --- /dev/null +++ b/Web/public/manifest.json @@ -0,0 +1,76 @@ +{ + "name": "账单管理系统", + "short_name": "账单管理", + "description": "个人账单管理与邮件解析系统", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#1989fa", + "orientation": "portrait-primary", + "icons": [ + { + "src": "/icons/icon-72x72.svg", + "sizes": "72x72", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-96x96.svg", + "sizes": "96x96", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-128x128.svg", + "sizes": "128x128", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-144x144.svg", + "sizes": "144x144", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-152x152.svg", + "sizes": "152x152", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-192x192.svg", + "sizes": "192x192", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-384x384.svg", + "sizes": "384x384", + "type": "image/svg+xml", + "purpose": "any maskable" + }, + { + "src": "/icons/icon-512x512.svg", + "sizes": "512x512", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ], + "categories": ["finance", "productivity"], + "screenshots": [], + "shortcuts": [ + { + "name": "查看账单", + "short_name": "账单", + "description": "快速查看账单列表", + "url": "/", + "icons": [ + { + "src": "/icons/icon-96x96.png", + "sizes": "96x96" + } + ] + } + ] +} diff --git a/Web/public/service-worker.js b/Web/public/service-worker.js new file mode 100644 index 0000000..9ec0dc7 --- /dev/null +++ b/Web/public/service-worker.js @@ -0,0 +1,141 @@ +const CACHE_NAME = 'emailbill-v1'; +const urlsToCache = [ + '/', + '/index.html', + '/favicon.ico', + '/manifest.json' +]; + +// 安装 Service Worker +self.addEventListener('install', (event) => { + console.log('[Service Worker] 安装中...'); + event.waitUntil( + caches.open(CACHE_NAME) + .then((cache) => { + console.log('[Service Worker] 缓存文件'); + return cache.addAll(urlsToCache); + }) + .then(() => self.skipWaiting()) + ); +}); + +// 激活 Service Worker +self.addEventListener('activate', (event) => { + console.log('[Service Worker] 激活中...'); + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== CACHE_NAME) { + console.log('[Service Worker] 删除旧缓存:', cacheName); + return caches.delete(cacheName); + } + }) + ); + }).then(() => self.clients.claim()) + ); +}); + +// 拦截请求 +self.addEventListener('fetch', (event) => { + const { request } = event; + const url = new URL(request.url); + + // 跳过跨域请求 + if (url.origin !== location.origin) { + return; + } + + // API请求使用网络优先策略 + if (url.pathname.startsWith('/api/')) { + event.respondWith( + fetch(request) + .then((response) => { + // 克隆响应以便缓存 + const responseClone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(request, responseClone); + }); + return response; + }) + .catch(() => { + // 网络失败时尝试从缓存获取 + return caches.match(request); + }) + ); + return; + } + + // 静态资源使用缓存优先策略 + event.respondWith( + caches.match(request) + .then((response) => { + if (response) { + return response; + } + return fetch(request).then((response) => { + // 检查是否是有效响应 + if (!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + const responseClone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(request, responseClone); + }); + + return response; + }); + }) + .catch(() => { + // 返回离线页面或默认内容 + if (request.destination === 'document') { + return caches.match('/index.html'); + } + }) + ); +}); + +// 后台同步 +self.addEventListener('sync', (event) => { + console.log('[Service Worker] 后台同步:', event.tag); + if (event.tag === 'sync-data') { + event.waitUntil(syncData()); + } +}); + +// 推送通知 +self.addEventListener('push', (event) => { + console.log('[Service Worker] 收到推送消息'); + const options = { + body: event.data ? event.data.text() : '您有新的账单消息', + icon: '/icons/icon-192x192.png', + badge: '/icons/icon-72x72.png', + vibrate: [200, 100, 200], + tag: 'emailbill-notification', + requireInteraction: false + }; + + event.waitUntil( + self.registration.showNotification('账单管理', options) + ); +}); + +// 通知点击 +self.addEventListener('notificationclick', (event) => { + console.log('[Service Worker] 通知被点击'); + event.notification.close(); + event.waitUntil( + clients.openWindow('/') + ); +}); + +// 数据同步函数 +async function syncData() { + try { + // 这里添加需要同步的逻辑 + console.log('[Service Worker] 执行数据同步'); + } catch (error) { + console.error('[Service Worker] 同步失败:', error); + } +} diff --git a/Web/src/App.vue b/Web/src/App.vue index 98fe4e2..be1b934 100644 --- a/Web/src/App.vue +++ b/Web/src/App.vue @@ -1,7 +1,7 @@