diff --git a/.gitignore b/.gitignore index 17ab75f..7e66538 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,147 @@ pnpm-debug.log* *.sln *.sw? +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +# End of https://www.toptal.com/developers/gitignore/api/node \ No newline at end of file diff --git a/dev-dist/sw.js b/dev-dist/sw.js index 48d0c19..2f5412d 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -67,9 +67,9 @@ if (!self.define) { }); }; } -define(['./workbox-5ea419d9'], (function (workbox) { 'use strict'; +define(['./workbox-20a2f87f'], (function (workbox) { 'use strict'; - importScripts("sw-cache-manager.js"); + importScripts("/sw-cache-manager.js"); self.skipWaiting(); workbox.clientsClaim(); @@ -82,55 +82,42 @@ define(['./workbox-5ea419d9'], (function (workbox) { 'use strict'; "url": "suppress-warnings.js", "revision": "d41d8cd98f00b204e9800998ecf8427e" }, { - "url": "/", - "revision": "0.0lkakoc2in8" + "url": "index.html", + "revision": "0.vjgf6ab2p68" }], {}); workbox.cleanupOutdatedCaches(); - workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/"), { + workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { allowlist: [/^\/$/] })); - workbox.registerRoute(/\.(?:js)$/i, new workbox.StaleWhileRevalidate({ - "cacheName": "js-cache", + workbox.registerRoute(({ + url + }) => url.pathname.startsWith("/assets/"), new workbox.CacheFirst({ + "cacheName": "assets-cache", plugins: [new workbox.ExpirationPlugin({ - maxEntries: 100, - maxAgeSeconds: 604800 + maxEntries: 200, + maxAgeSeconds: 5184000 + }), new workbox.CacheableResponsePlugin({ + statuses: [0, 200] })] }), 'GET'); - workbox.registerRoute(/\.(?:css)$/i, new workbox.StaleWhileRevalidate({ - "cacheName": "css-cache", + workbox.registerRoute(({ + url + }) => url.pathname.startsWith("/pwa/"), new workbox.StaleWhileRevalidate({ + "cacheName": "pwa-cache", plugins: [new workbox.ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 604800 - })] - }), 'GET'); - workbox.registerRoute(/\.(?:html)$/i, new workbox.NetworkFirst({ - "cacheName": "html-cache", - plugins: [new workbox.ExpirationPlugin({ - maxEntries: 20, - maxAgeSeconds: 86400 - })] - }), 'GET'); - workbox.registerRoute(/\.(?:png|jpg|jpeg|svg|gif)$/i, new workbox.StaleWhileRevalidate({ - "cacheName": "images-cache", - plugins: [new workbox.ExpirationPlugin({ - maxEntries: 50, - maxAgeSeconds: 2592000 - })] - }), 'GET'); - workbox.registerRoute(/\/cdn-cgi\/.*/i, new workbox.NetworkFirst({ - "cacheName": "cdn-cgi-cache", - "networkTimeoutSeconds": 10, - plugins: [new workbox.ExpirationPlugin({ - maxEntries: 50, - maxAgeSeconds: 86400 + }), new workbox.CacheableResponsePlugin({ + statuses: [0, 200] })] }), 'GET'); workbox.registerRoute(({ url }) => { - return url.origin !== self.location.origin; + const path = url.pathname; + return !(path.includes("/assets/") || path.includes("/pwa/")); }, new workbox.NetworkFirst({ - "cacheName": "external-resources", + "cacheName": "other-resources", "networkTimeoutSeconds": 10, plugins: [new workbox.ExpirationPlugin({ maxEntries: 100, diff --git a/public/config.json b/public/config.json deleted file mode 100644 index d2b9fe4..0000000 --- a/public/config.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "studentList": [ - "张三", - "李四", - "王五", - "赵六", - "钱七" - ], - "homeworkArrange": [ - [ - "语文", - "数学", - "英语" - ], - [ - "物理", - "化学", - "生物" - ], - [ - "政治", - "历史", - "地理" - ] - ], - "url": "http://localhost:3030" -} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index 8fb9f91..e0db255 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/image/apple-touch-icon-180x180.png b/public/pwa/image/apple-touch-icon-180x180.png similarity index 100% rename from public/image/apple-touch-icon-180x180.png rename to public/pwa/image/apple-touch-icon-180x180.png diff --git a/public/image/favicon.ico b/public/pwa/image/favicon.ico similarity index 100% rename from public/image/favicon.ico rename to public/pwa/image/favicon.ico diff --git a/public/image/logo.svg b/public/pwa/image/logo.svg similarity index 100% rename from public/image/logo.svg rename to public/pwa/image/logo.svg diff --git a/public/image/maskable-icon-512x512.png b/public/pwa/image/maskable-icon-512x512.png similarity index 100% rename from public/image/maskable-icon-512x512.png rename to public/pwa/image/maskable-icon-512x512.png diff --git a/public/image/pwa-192x192.png b/public/pwa/image/pwa-192x192.png similarity index 100% rename from public/image/pwa-192x192.png rename to public/pwa/image/pwa-192x192.png diff --git a/public/image/pwa-512x512.png b/public/pwa/image/pwa-512x512.png similarity index 100% rename from public/image/pwa-512x512.png rename to public/pwa/image/pwa-512x512.png diff --git a/public/image/pwa-64x64.png b/public/pwa/image/pwa-64x64.png similarity index 100% rename from public/image/pwa-64x64.png rename to public/pwa/image/pwa-64x64.png diff --git a/public/sw-cache-manager.js b/public/sw-cache-manager.js index 8d67967..8782a87 100644 --- a/public/sw-cache-manager.js +++ b/public/sw-cache-manager.js @@ -41,4 +41,4 @@ self.addEventListener('message', (event) => { } }); -console.log('Cache Manager extension loaded'); \ No newline at end of file +console.log('Cache Manager extension loaded'); \ No newline at end of file diff --git a/src/components/CacheManager.vue b/src/components/CacheManager.vue index 2143d78..ef85a8c 100644 --- a/src/components/CacheManager.vue +++ b/src/components/CacheManager.vue @@ -92,22 +92,22 @@ export default { }, async refreshCaches() { if (!this.serviceWorkerActive) return; - + this.loading = true; this.message = ''; this.caches = []; - + try { // 获取所有缓存名称 const cacheNames = await this.sendMessageToSW({ type: 'CACHE_KEYS' }); - + // 获取每个缓存的内容 for (const cacheName of cacheNames.cacheNames) { - const cacheContent = await this.sendMessageToSW({ - type: 'CACHE_CONTENT', - cacheName + const cacheContent = await this.sendMessageToSW({ + type: 'CACHE_CONTENT', + cacheName }); - + this.caches.push({ name: cacheName, urls: cacheContent.urls || [] @@ -122,11 +122,11 @@ export default { async clearCache(cacheName) { this.loading = true; try { - const result = await this.sendMessageToSW({ - type: 'CLEAR_CACHE', - cacheName + const result = await this.sendMessageToSW({ + type: 'CLEAR_CACHE', + cacheName }); - + if (result.success) { this.showMessage(`已清除缓存: ${this.formatCacheName(cacheName)}`, 'success'); await this.refreshCaches(); @@ -142,12 +142,12 @@ export default { async clearUrl(cacheName, url) { this.loading = true; try { - const result = await this.sendMessageToSW({ - type: 'CLEAR_URL', + const result = await this.sendMessageToSW({ + type: 'CLEAR_URL', cacheName, - url + url }); - + if (result.success) { this.showMessage(`已从缓存中删除: ${this.getFileName(url)}`, 'success'); await this.refreshCaches(); @@ -164,11 +164,11 @@ export default { if (!confirm('确定要清除所有缓存吗?这可能会导致应用需要重新下载资源。')) { return; } - + this.loading = true; try { const result = await this.sendMessageToSW({ type: 'CLEAR_ALL_CACHES' }); - + if (result.success) { this.showMessage('已清除所有缓存', 'success'); await this.refreshCaches(); @@ -187,14 +187,14 @@ export default { reject(new Error('Service Worker 未控制页面')); return; } - + const messageChannel = new MessageChannel(); messageChannel.port1.onmessage = (event) => { resolve(event.data); }; - + navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]); - + // 设置超时 setTimeout(() => { reject(new Error('Service Worker 响应超时')); @@ -218,14 +218,15 @@ export default { const urlObj = new URL(url); const pathParts = urlObj.pathname.split('/'); return pathParts[pathParts.length - 1] || urlObj.hostname; - } catch (e) { + } catch (error) { + console.error('获取文件名失败:', error); return url; } }, showMessage(message, type = 'info') { this.message = message; this.messageType = type; - + // 5秒后自动清除消息 setTimeout(() => { if (this.message === message) { @@ -235,4 +236,4 @@ export default { } } } - \ No newline at end of file + diff --git a/src/components/settings/cards/RandomPickerSettingsCard.vue b/src/components/settings/cards/RandomPickerSettingsCard.vue deleted file mode 100644 index 42a1405..0000000 --- a/src/components/settings/cards/RandomPickerSettingsCard.vue +++ /dev/null @@ -1,117 +0,0 @@ - - - - - 随机点名设置 - - - - - - - - - - - - 学生过滤设置 - - - - - - - - - 随机点名功能将在主页显示一个按钮,点击后可以随机抽取学生。 - - - - - - \ No newline at end of file diff --git a/src/pages/CacheManagement.vue b/src/pages/CacheManagement.vue index 132d0af..b8d37e2 100644 --- a/src/pages/CacheManagement.vue +++ b/src/pages/CacheManagement.vue @@ -2,8 +2,51 @@ - 缓存管理 - 在这里您可以查看和管理应用的缓存文件。清除缓存可能会导致应用需要重新下载资源。 + + mdi-database-cog-outline + + 缓存管理 + 管理应用的本地缓存资源 + + + + + + mdi-information-outline + 在这里您可以查看和管理应用的缓存文件。清除缓存可能会导致应用需要重新下载资源,但有助于解决某些显示问题。 + + + + + + + + + mdi-information + 什么是缓存? + + 缓存是浏览器在本地存储的网站资源副本,如图片、脚本和样式表等。这些缓存可以加快页面加载速度,减少数据使用,并在离线时提供基本功能。 + + + + + + + + + mdi-lightbulb-outline + 何时清除缓存? + + + 应用显示过时的内容 + 界面出现异常 + 应用功能不正常 + + + + + + @@ -17,6 +60,9 @@ export default { name: 'CacheManagementPage', components: { CacheManager + }, + metaInfo: { + title: '缓存管理' } } \ No newline at end of file diff --git a/src/pages/settings.vue b/src/pages/settings.vue index 687b6af..6a59c76 100644 --- a/src/pages/settings.vue +++ b/src/pages/settings.vue @@ -134,7 +134,7 @@ - + @@ -156,12 +156,10 @@ - - - + - + @@ -189,7 +187,6 @@ import AboutCard from '@/components/settings/AboutCard.vue'; import '../styles/settings.scss'; import dataProvider from '@/utils/dataProvider'; import SettingsExplorer from '@/components/settings/SettingsExplorer.vue'; -import RandomPickerSettingsCard from '@/components/settings/cards/RandomPickerSettingsCard.vue'; export default { name: 'Settings', @@ -205,8 +202,7 @@ export default { DataProviderSettingsCard, ThemeSettingsCard, EchoChamberCard, - SettingsExplorer, - RandomPickerSettingsCard + SettingsExplorer }, setup() { const { mobile } = useDisplay(); diff --git a/vite.config.mjs b/vite.config.mjs index 2570a60..65b8d53 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -19,86 +19,62 @@ export default defineConfig({ Layouts(), Vue({ template: { transformAssetUrls } - }), + }), VitePWA({ registerType: 'autoUpdate', devOptions: { - navigateFallback: '/', + navigateFallback: 'index.html', enabled: true, suppressWarnings: true, }, - - lang: 'zh-CN', + + lang: 'zh-CN', injectRegister: 'auto', strategies: 'generateSW', + + workbox: { - globPatterns: ['**/*.{js,css,html,png,svg,jpg,jpeg,gif,ico,woff,woff2,ttf,eot}'], - navigateFallback: '/', + globPatterns: ['*'], + navigateFallback: 'index.html', runtimeCaching: [ { - urlPattern: /\.(?:js)$/i, - handler: 'StaleWhileRevalidate', + urlPattern: ({ url }) => url.pathname.startsWith('/assets/'), + handler: 'CacheFirst', options: { - cacheName: 'js-cache', + cacheName: 'assets-cache', expiration: { - maxEntries: 100, - maxAgeSeconds: 60 * 60 * 24 * 7 // 7 天 - } - } - }, - { - urlPattern: /\.(?:css)$/i, - handler: 'StaleWhileRevalidate', - options: { - cacheName: 'css-cache', - expiration: { - maxEntries: 50, - maxAgeSeconds: 60 * 60 * 24 * 7 // 7 天 - } - } - }, - { - urlPattern: /\.(?:html)$/i, - handler: 'NetworkFirst', - options: { - cacheName: 'html-cache', - expiration: { - maxEntries: 20, - maxAgeSeconds: 60 * 60 * 24 // 1 天 - } - } - }, - { - urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/i, - handler: 'StaleWhileRevalidate', - options: { - cacheName: 'images-cache', - expiration: { - maxEntries: 50, - maxAgeSeconds: 60 * 60 * 24 * 30 // 30 天 - } - } - }, - { - urlPattern: /\/cdn-cgi\/.*/i, - handler: 'NetworkFirst', - options: { - cacheName: 'cdn-cgi-cache', - expiration: { - maxEntries: 50, - maxAgeSeconds: 60 * 60 * 24 // 1 天 + maxEntries: 200, + maxAgeSeconds: 60 * 60 * 24 * 60 // 60 天 }, - networkTimeoutSeconds: 10 + cacheableResponse: { + statuses: [0, 200] + } } }, { - // 匹配除了当前域名以外的所有请求 + urlPattern: ({ url }) => url.pathname.startsWith('/pwa/'), + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'pwa-cache', + expiration: { + maxEntries: 50, + maxAgeSeconds: 60 * 60 * 24 * 7 // 7 天 + }, + cacheableResponse: { + statuses: [0, 200] + } + } + }, + { + // 匹配除了上述规则外的所有请求 urlPattern: ({ url }) => { - return url.origin !== self.location.origin; + const path = url.pathname; + // 排除已经由其他规则处理的路径 + return !(path.includes('/assets/') || path.includes('/pwa/')); }, handler: 'NetworkFirst', options: { - cacheName: 'external-resources', + cacheName: 'other-resources', expiration: { maxEntries: 100, maxAgeSeconds: 60 * 60 * 24 // 1 天 @@ -108,12 +84,13 @@ export default defineConfig({ statuses: [0, 200] } } - } + }, + ], additionalManifestEntries: [], clientsClaim: true, skipWaiting: true, - importScripts: ['sw-cache-manager.js'] + importScripts: ['/sw-cache-manager.js'] }, manifest: { name: 'Classworks作业板', @@ -128,22 +105,22 @@ export default defineConfig({ }, icons: [ { - src: '/image/pwa-64x64.png', + src: '/pwa/image/pwa-64x64.png', sizes: '64x64', type: 'image/png' }, { - src: '/image/pwa-192x192.png', + src: '/pwa/image/pwa-192x192.png', sizes: '192x192', type: 'image/png' }, { - src: '/image/pwa-512x512.png', + src: '/pwa/image/pwa-512x512.png', sizes: '512x512', type: 'image/png' }, { - src: '/image/maskable-icon-512x512.png', + src: '/pwa/image/maskable-icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' @@ -154,9 +131,15 @@ export default defineConfig({ name: '随机点名', short_name: '随机点名', url: '/#random-picker', + icons: [ + { + src: '/pwa/image/pwa-64x64.png', + sizes: '64x64', + type: 'image/png' + } + ] }, ], - } }), // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme
在这里您可以查看和管理应用的缓存文件。清除缓存可能会导致应用需要重新下载资源。
缓存是浏览器在本地存储的网站资源副本,如图片、脚本和样式表等。这些缓存可以加快页面加载速度,减少数据使用,并在离线时提供基本功能。