From cd4238274a12e6353e64eae2f8b8997f8557d67a Mon Sep 17 00:00:00 2001 From: sagitchu Date: Sun, 5 Apr 2026 20:53:25 +0800 Subject: [PATCH] fix(ui): improve background handling and PWA update prompts - Add adaptive solid color background support for dark/light themes. - Replace mesh gradient with semi-transparent blurred background images. - Fix background scrolling issue on mobile browsers. - Remove borders from specific cards for consistent UI styling. - Prevent caching of PWA service worker files in Nginx. - Introduce interactive toast prompt for PWA updates instead of silent refresh. --- vite-frontend/nginx.conf | 8 +++ vite-frontend/src/App.tsx | 21 +++++++- .../src/components/theme-settings.tsx | 2 +- vite-frontend/src/main.tsx | 32 ++++++++++- vite-frontend/src/pages/config.tsx | 45 ++++++++++++++-- vite-frontend/src/pages/limit.tsx | 4 +- vite-frontend/src/styles/globals.css | 54 +++++++++++++++---- vite-frontend/vite.config.ts | 2 +- 8 files changed, 146 insertions(+), 22 deletions(-) diff --git a/vite-frontend/nginx.conf b/vite-frontend/nginx.conf index 366fa57..dff20e4 100644 --- a/vite-frontend/nginx.conf +++ b/vite-frontend/nginx.conf @@ -43,6 +43,14 @@ http { root /usr/share/nginx/html; index index.html; + # PWA 和 Service Worker 不缓存 + location ~* (sw\.js|service-worker\.js|workbox-.*\.js|manifest\.webmanifest)$ { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires "0"; + try_files $uri =404; + } + # 静态文件缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 30d; diff --git a/vite-frontend/src/App.tsx b/vite-frontend/src/App.tsx index e9de9d9..d3b6e67 100644 --- a/vite-frontend/src/App.tsx +++ b/vite-frontend/src/App.tsx @@ -95,11 +95,28 @@ function App() { const updateBg = () => { const customBg = siteConfig.app_bg_image; if (customBg) { - document.documentElement.style.setProperty("--custom-bg-image", `url(${customBg})`); - document.documentElement.classList.add("has-custom-bg"); + if (customBg === "theme") { + document.documentElement.style.removeProperty("--custom-bg-image"); + document.documentElement.style.removeProperty("--custom-bg-color"); + document.documentElement.classList.add("has-theme-bg"); + document.documentElement.classList.remove("has-custom-bg"); + } else if (customBg.startsWith("http") || customBg.startsWith("data:") || customBg.startsWith("/") || customBg.startsWith("blob:")) { + document.documentElement.style.setProperty("--custom-bg-image", `url(${customBg})`); + document.documentElement.style.setProperty("--custom-bg-color", "transparent"); + document.documentElement.classList.add("has-custom-bg"); + document.documentElement.classList.remove("has-theme-bg"); + } else { + // Assume solid color like "#ffffff", "white", etc. + document.documentElement.style.setProperty("--custom-bg-image", "none"); + document.documentElement.style.setProperty("--custom-bg-color", customBg); + document.documentElement.classList.add("has-custom-bg"); + document.documentElement.classList.remove("has-theme-bg"); + } } else { document.documentElement.style.removeProperty("--custom-bg-image"); + document.documentElement.style.removeProperty("--custom-bg-color"); document.documentElement.classList.remove("has-custom-bg"); + document.documentElement.classList.remove("has-theme-bg"); } }; updateBg(); diff --git a/vite-frontend/src/components/theme-settings.tsx b/vite-frontend/src/components/theme-settings.tsx index fa874be..b685812 100644 --- a/vite-frontend/src/components/theme-settings.tsx +++ b/vite-frontend/src/components/theme-settings.tsx @@ -54,7 +54,7 @@ export const ThemeSettings: React.FC = () => { }; return ( - +

主题设置 diff --git a/vite-frontend/src/main.tsx b/vite-frontend/src/main.tsx index f2b370b..f3e2f8e 100644 --- a/vite-frontend/src/main.tsx +++ b/vite-frontend/src/main.tsx @@ -1,12 +1,42 @@ import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import { registerSW } from "virtual:pwa-register"; +import toast from "react-hot-toast"; import App from "./App.tsx"; import { Provider } from "./provider.tsx"; import "@/styles/globals.css"; -registerSW({ immediate: true }); +const updateSW = registerSW({ + immediate: true, + onNeedRefresh() { + toast( + (t) => ( +
+ 发现新版本,是否立即刷新以应用更新? +
+ + +
+
+ ), + { duration: Infinity, position: "bottom-right" } + ); + }, +}); ReactDOM.createRoot(document.getElementById("root")!).render( diff --git a/vite-frontend/src/pages/config.tsx b/vite-frontend/src/pages/config.tsx index d93678c..d2735b4 100644 --- a/vite-frontend/src/pages/config.tsx +++ b/vite-frontend/src/pages/config.tsx @@ -621,10 +621,13 @@ export default function ConfigPage() { const renderBgImageUploader = () => { const bgImage = configs["app_bg_image"] || ""; + const isImage = bgImage.startsWith("http") || bgImage.startsWith("data:") || bgImage.startsWith("/") || bgImage.startsWith("blob:"); + const isTheme = bgImage === "theme"; + const isSolidColor = bgImage && !isImage && !isTheme; return (
-
+
bgImageFileInputRef.current?.click()} isLoading={bgImageUploading} > - 选择图片 + 上传图片 + + + {bgImage && ( )}
- {bgImage && ( + + {bgImage && isImage && (
)} + + {bgImage && isTheme && ( +
+ 当前为自适应纯色背景 +
+ )} + + {bgImage && isSolidColor && ( +
+ + 纯色背景 ({bgImage}) + +
+ )}
); }; diff --git a/vite-frontend/src/pages/limit.tsx b/vite-frontend/src/pages/limit.tsx index 6741592..bea3f0a 100644 --- a/vite-frontend/src/pages/limit.tsx +++ b/vite-frontend/src/pages/limit.tsx @@ -327,7 +327,7 @@ export default function LimitPage() { {filteredRules.map((rule) => ( - +
@@ -413,7 +413,7 @@ export default function LimitPage() { ) ) : ( /* 空状态 */ - +

暂无限速规则 diff --git a/vite-frontend/src/styles/globals.css b/vite-frontend/src/styles/globals.css index 4f87bd8..61333fc 100644 --- a/vite-frontend/src/styles/globals.css +++ b/vite-frontend/src/styles/globals.css @@ -227,24 +227,56 @@ body { } .bg-mesh-gradient { - background: radial-gradient(at 0% 0%, #ff9a9e 0%, transparent 50%), - radial-gradient(at 100% 0%, #fecfef 0%, transparent 50%), - radial-gradient(at 100% 100%, #c2e9fb 0%, transparent 50%), - radial-gradient(at 0% 100%, #a1c4fd 0%, transparent 50%); background-color: #f2f2f7; + position: relative; + z-index: 0; } -.dark .bg-mesh-gradient { - background: radial-gradient(at 0% 0%, #4a2b30 0%, transparent 50%), - radial-gradient(at 100% 0%, #493b45 0%, transparent 50%), - radial-gradient(at 100% 100%, #354756 0%, transparent 50%), - radial-gradient(at 0% 100%, #2f394f 0%, transparent 50%); - background-color: #1c1c1e; +.bg-mesh-gradient::before { + content: ""; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + pointer-events: none; + background: url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop') no-repeat center center; + background-size: cover; +} + +:root.dark .bg-mesh-gradient { + background-color: #0b1020; +} + +.dark .bg-mesh-gradient::before { + background: url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?q=80&w=2564&auto=format&fit=crop') no-repeat center center; + background-size: cover; +} + +:root.has-theme-bg .bg-mesh-gradient { + background-image: none !important; + background-color: #f6f7fb !important; +} + +:root.dark.has-theme-bg .bg-mesh-gradient { + background-color: #0b1020 !important; +} + +:root.has-theme-bg .bg-mesh-gradient::before { + background-image: none !important; } :root.has-custom-bg .bg-mesh-gradient { - background: var(--custom-bg-image) no-repeat center center fixed !important; + background-image: none !important; + background-color: var(--custom-bg-color, transparent) !important; +} + +:root.has-custom-bg .bg-mesh-gradient::before { + background-image: var(--custom-bg-image, none) !important; background-size: cover !important; + background-position: center center !important; + background-repeat: no-repeat !important; } @layer utilities { diff --git a/vite-frontend/vite.config.ts b/vite-frontend/vite.config.ts index d7dbd9e..7216e7d 100644 --- a/vite-frontend/vite.config.ts +++ b/vite-frontend/vite.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ plugins: [ react(), VitePWA({ - registerType: "autoUpdate", + registerType: "prompt", injectRegister: "auto", includeAssets: ["favicon.ico", "apple-touch-icon.png"], manifest: {