优化项目配置和页面结构

- 更新settings.local.json,移除冗余的权限设置,添加新的Playwright相关权限。
- 删除exhibition_demo_project_2025.md文档,清理不再使用的文件。
- 更新多个HTML页面,统一viewport设置,添加页面加载动画、错误处理和性能优化脚本。
- 统一使用Tailwind CSS的引入方式,提升页面加载性能。
- 增强导航组件,支持移动端菜单和返回顶部功能,改善用户体验。
This commit is contained in:
Yep_Q
2025-09-10 02:35:16 +08:00
parent 6d3825c4ab
commit 3825deb3e0
21 changed files with 4735 additions and 1462 deletions

View File

@@ -0,0 +1,283 @@
// Service Worker for performance optimization
const CACHE_NAME = 'n8n-demo-v1';
const STATIC_CACHE = 'static-v1';
const DYNAMIC_CACHE = 'dynamic-v1';
// 需要缓存的静态资源
const STATIC_FILES = [
'/',
'/index.html',
'/pages/overview.html',
'/pages/exhibition.html',
'/pages/marketing.html',
'/pages/operation.html',
'/pages/budget.html',
'/pages/risk.html',
'/js/nav-component.js',
'/js/back-to-top.js',
'/js/mobile-optimize.js',
'/js/performance-optimizer.js',
'/js/page-loader.js',
'/css/styles.css',
'/css/animations.css'
];
// 缓存策略配置
const CACHE_STRATEGIES = {
// 静态资源:缓存优先
static: {
pattern: /\.(js|css|woff|woff2|ttf|eot)$/,
strategy: 'cacheFirst',
maxAge: 30 * 24 * 60 * 60 * 1000 // 30天
},
// HTML页面网络优先失败时使用缓存
html: {
pattern: /\.html$/,
strategy: 'networkFirst',
maxAge: 24 * 60 * 60 * 1000 // 1天
},
// 图片:缓存优先
images: {
pattern: /\.(png|jpg|jpeg|gif|svg|webp)$/,
strategy: 'cacheFirst',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7天
},
// 外部资源:网络优先
external: {
pattern: /^https:\/\/(fonts\.googleapis\.com|cdnjs\.cloudflare\.com|cdn\.jsdelivr\.net)/,
strategy: 'networkFirst',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7天
}
};
// 安装事件 - 缓存静态资源
self.addEventListener('install', event => {
console.log('SW: Install event');
event.waitUntil(
caches.open(STATIC_CACHE)
.then(cache => {
console.log('SW: Caching static files');
return cache.addAll(STATIC_FILES);
})
.then(() => {
console.log('SW: Static files cached');
return self.skipWaiting();
})
.catch(err => {
console.error('SW: Failed to cache static files', err);
})
);
});
// 激活事件 - 清理旧缓存
self.addEventListener('activate', event => {
console.log('SW: Activate event');
event.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
// 删除旧版本的缓存
if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE) {
console.log('SW: Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => {
console.log('SW: Old caches cleaned');
return self.clients.claim();
})
);
});
// 获取事件 - 实现缓存策略
self.addEventListener('fetch', event => {
// 只处理 GET 请求
if (event.request.method !== 'GET') {
return;
}
const url = new URL(event.request.url);
// 跳过 chrome-extension 等协议
if (!url.protocol.startsWith('http')) {
return;
}
event.respondWith(handleFetch(event.request));
});
// 处理请求的核心函数
async function handleFetch(request) {
const url = new URL(request.url);
try {
// 确定缓存策略
const strategy = getCacheStrategy(request);
switch (strategy.strategy) {
case 'cacheFirst':
return await cacheFirst(request, strategy);
case 'networkFirst':
return await networkFirst(request, strategy);
case 'networkOnly':
return await fetch(request);
default:
return await networkFirst(request, strategy);
}
} catch (error) {
console.error('SW: Fetch failed:', error);
// 返回离线页面或缓存的响应
return await getOfflineResponse(request);
}
}
// 获取缓存策略
function getCacheStrategy(request) {
const url = request.url;
for (const [key, config] of Object.entries(CACHE_STRATEGIES)) {
if (config.pattern.test(url)) {
return config;
}
}
// 默认策略
return { strategy: 'networkFirst', maxAge: 24 * 60 * 60 * 1000 };
}
// 缓存优先策略
async function cacheFirst(request, strategy) {
const cacheName = isStaticResource(request) ? STATIC_CACHE : DYNAMIC_CACHE;
const cache = await caches.open(cacheName);
const cachedResponse = await cache.match(request);
if (cachedResponse) {
// 检查缓存是否过期
const cachedDate = new Date(cachedResponse.headers.get('date') || Date.now());
const now = new Date();
if (now - cachedDate < strategy.maxAge) {
return cachedResponse;
}
}
try {
const networkResponse = await fetch(request);
if (networkResponse.ok) {
// 缓存新响应
await cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
// 网络失败,返回缓存(即使过期)
if (cachedResponse) {
return cachedResponse;
}
throw error;
}
}
// 网络优先策略
async function networkFirst(request, strategy) {
const cacheName = isStaticResource(request) ? STATIC_CACHE : DYNAMIC_CACHE;
const cache = await caches.open(cacheName);
try {
const networkResponse = await fetch(request);
if (networkResponse.ok) {
// 缓存响应
await cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
// 网络失败,尝试从缓存获取
const cachedResponse = await cache.match(request);
if (cachedResponse) {
return cachedResponse;
}
throw error;
}
}
// 判断是否为静态资源
function isStaticResource(request) {
const url = new URL(request.url);
return STATIC_FILES.some(file => url.pathname.endsWith(file));
}
// 获取离线响应
async function getOfflineResponse(request) {
const url = new URL(request.url);
// 如果是 HTML 请求,返回主页
if (request.headers.get('accept')?.includes('text/html')) {
const cache = await caches.open(STATIC_CACHE);
return await cache.match('/index.html') || new Response('Offline', { status: 503 });
}
// 其他资源返回 503
return new Response('Offline', { status: 503 });
}
// 监听消息事件
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
if (event.data && event.data.type === 'GET_VERSION') {
event.ports[0].postMessage({ version: CACHE_NAME });
}
});
// 后台同步(如果支持)
self.addEventListener('sync', event => {
if (event.tag === 'background-sync') {
event.waitUntil(doBackgroundSync());
}
});
async function doBackgroundSync() {
// 这里可以执行后台同步任务
console.log('SW: Background sync');
}
// 推送通知(如果需要)
self.addEventListener('push', event => {
if (event.data) {
const data = event.data.json();
const options = {
body: data.body,
icon: data.icon || '/favicon.ico',
badge: '/badge.png',
vibrate: [200, 100, 200],
data: data.data,
actions: data.actions
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
}
});
// 通知点击事件
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data?.url || '/')
);
});