PWA支持
10
Web/public/icons/icon-128x128.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad128" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="128" height="128" fill="url(#grad128)" rx="12.8"/>
|
||||
<text x="50%" y="50%" font-size="51.2" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 554 B |
10
Web/public/icons/icon-144x144.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad144" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="144" height="144" fill="url(#grad144)" rx="14.4"/>
|
||||
<text x="50%" y="50%" font-size="57.6" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 554 B |
10
Web/public/icons/icon-152x152.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="152" height="152" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad152" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="152" height="152" fill="url(#grad152)" rx="15.200000000000001"/>
|
||||
<text x="50%" y="50%" font-size="60.800000000000004" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 582 B |
10
Web/public/icons/icon-192x192.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad192" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="192" height="192" fill="url(#grad192)" rx="19.200000000000003"/>
|
||||
<text x="50%" y="50%" font-size="76.80000000000001" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 581 B |
10
Web/public/icons/icon-384x384.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="384" height="384" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad384" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="384" height="384" fill="url(#grad384)" rx="38.400000000000006"/>
|
||||
<text x="50%" y="50%" font-size="153.60000000000002" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 582 B |
10
Web/public/icons/icon-512x512.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad512" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="512" height="512" fill="url(#grad512)" rx="51.2"/>
|
||||
<text x="50%" y="50%" font-size="204.8" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 555 B |
10
Web/public/icons/icon-72x72.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad72" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="72" height="72" fill="url(#grad72)" rx="7.2"/>
|
||||
<text x="50%" y="50%" font-size="28.8" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 547 B |
10
Web/public/icons/icon-96x96.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="96" height="96" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad96" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1989fa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0b5fd6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="96" height="96" fill="url(#grad96)" rx="9.600000000000001"/>
|
||||
<text x="50%" y="50%" font-size="38.400000000000006" fill="white" text-anchor="middle" dy=".35em" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="bold">账</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 575 B |
4
Web/public/icons/icon-temp.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width='512' height='512' xmlns='http://www.w3.org/2000/svg'>
|
||||
<rect width='512' height='512' fill='#1989fa'/>
|
||||
<text x='50%' y='50%' font-size='200' fill='white' text-anchor='middle' dy='.3em' font-family='Arial, sans-serif' font-weight='bold'>账</text>
|
||||
</svg>
|
||||
76
Web/public/manifest.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
141
Web/public/service-worker.js
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||