feat: 添加PWA支持,支持离线访问和添加到主屏幕
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="theme-color" content="#1890ff" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="轻远ERP" />
|
||||
<meta name="description" content="轻远电力老挝分公司财务管理系统" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="apple-touch-icon" href="/logo.png" />
|
||||
<title>轻远电力老挝ERP - Qingyuan Power Laos</title>
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
@@ -12,5 +19,19 @@
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script>
|
||||
// 注册 Service Worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.then((registration) => {
|
||||
console.log('SW registered:', registration);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('SW registration failed:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Generate PWA Icons</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>PWA图标生成器</h2>
|
||||
<p>请使用浏览器打开此文件,然后右键保存生成的图标</p>
|
||||
<div id="icons"></div>
|
||||
|
||||
<script>
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
const container = document.getElementById('icons');
|
||||
|
||||
sizes.forEach(size => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// 背景
|
||||
ctx.fillStyle = '#1890ff';
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
// 文字
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.font = `bold ${size * 0.5}px Arial, sans-serif`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('轻', size / 2, size / 2 + size * 0.05);
|
||||
|
||||
// 显示
|
||||
const div = document.createElement('div');
|
||||
div.style.margin = '10px';
|
||||
div.innerHTML = `<p>${size}x${size}</p>`;
|
||||
const img = document.createElement('img');
|
||||
img.src = canvas.toDataURL('image/png');
|
||||
img.style.width = '64px';
|
||||
img.style.height = '64px';
|
||||
img.style.border = '1px solid #ccc';
|
||||
div.appendChild(img);
|
||||
|
||||
// 下载链接
|
||||
const link = document.createElement('a');
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.download = `icon-${size}x${size}.png`;
|
||||
link.textContent = '下载';
|
||||
link.style.marginLeft = '10px';
|
||||
div.appendChild(link);
|
||||
|
||||
container.appendChild(div);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" fill="#1890ff"/>
|
||||
<text x="256" y="320" font-family="Arial, sans-serif" font-size="240" font-weight="bold" text-anchor="middle" fill="white">轻</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 255 B |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "轻远电力老挝ERP",
|
||||
"short_name": "轻远ERP",
|
||||
"description": "轻远电力老挝分公司财务管理系统",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#1890ff",
|
||||
"orientation": "portrait",
|
||||
"scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/logo.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Service Worker for PWA
|
||||
const CACHE_NAME = 'qingyuan-erp-v1';
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json',
|
||||
'/logo.png'
|
||||
];
|
||||
|
||||
// 安装时缓存静态资源
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(STATIC_ASSETS);
|
||||
}).catch((err) => {
|
||||
console.log('Cache failed:', err);
|
||||
})
|
||||
);
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
// 激活时清理旧缓存
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames
|
||||
.filter((name) => name !== CACHE_NAME)
|
||||
.map((name) => caches.delete(name))
|
||||
);
|
||||
})
|
||||
);
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
// 拦截请求,优先使用缓存
|
||||
self.addEventListener('fetch', (event) => {
|
||||
// 跳过非GET请求和API请求
|
||||
if (event.request.method !== 'GET' ||
|
||||
event.request.url.includes('/api/') ||
|
||||
event.request.url.includes('chrome-extension')) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.respondWith(
|
||||
caches.match(event.request).then((response) => {
|
||||
// 如果缓存中有,返回缓存
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// 否则发起网络请求
|
||||
return fetch(event.request).then((networkResponse) => {
|
||||
// 缓存新请求
|
||||
if (networkResponse.status === 200) {
|
||||
const responseToCache = networkResponse.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
}
|
||||
return networkResponse;
|
||||
}).catch(() => {
|
||||
// 网络失败时返回离线页面
|
||||
if (event.request.mode === 'navigate') {
|
||||
return caches.match('/index.html');
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user