mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-29 07:56:37 +08:00
fix(ui): improve background handling and PWA update prompts (#419)
## Changes - 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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -54,7 +54,7 @@ export const ThemeSettings: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="border border-gray-200 dark:border-gray-700">
|
||||
<Card className="shadow-md">
|
||||
<CardBody className="p-6">
|
||||
<h2 className="text-lg font-medium text-gray-900 dark:text-white mb-5">
|
||||
主题设置
|
||||
|
||||
@@ -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) => (
|
||||
<div className="flex flex-col gap-3">
|
||||
<span className="text-sm font-medium text-foreground">发现新版本,是否立即刷新以应用更新?</span>
|
||||
<div className="flex gap-2 justify-end">
|
||||
<button
|
||||
className="px-3 py-1.5 text-xs font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors"
|
||||
onClick={() => {
|
||||
updateSW(true);
|
||||
toast.dismiss(t.id);
|
||||
}}
|
||||
>
|
||||
刷新
|
||||
</button>
|
||||
<button
|
||||
className="px-3 py-1.5 text-xs font-medium bg-default-200 text-default-700 hover:bg-default-300 rounded-md transition-colors"
|
||||
onClick={() => toast.dismiss(t.id)}
|
||||
>
|
||||
稍后
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{ duration: Infinity, position: "bottom-right" }
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<BrowserRouter>
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
@@ -638,7 +641,23 @@ export default function ConfigPage() {
|
||||
onPress={() => bgImageFileInputRef.current?.click()}
|
||||
isLoading={bgImageUploading}
|
||||
>
|
||||
选择图片
|
||||
上传图片
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="flat"
|
||||
onPress={() => handleConfigChange("app_bg_image", "theme")}
|
||||
isDisabled={bgImageUploading || isTheme}
|
||||
>
|
||||
自适应纯色 (跟随深色模式)
|
||||
</Button>
|
||||
<Button
|
||||
color="default"
|
||||
variant="flat"
|
||||
onPress={() => handleConfigChange("app_bg_image", "#ffffff")}
|
||||
isDisabled={bgImageUploading || bgImage === "#ffffff"}
|
||||
>
|
||||
白色纯色
|
||||
</Button>
|
||||
{bgImage && (
|
||||
<Button
|
||||
@@ -647,11 +666,12 @@ export default function ConfigPage() {
|
||||
onPress={() => handleConfigChange("app_bg_image", "")}
|
||||
isDisabled={bgImageUploading}
|
||||
>
|
||||
清除背景
|
||||
恢复默认
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{bgImage && (
|
||||
|
||||
{bgImage && isImage && (
|
||||
<div className="relative rounded-xl overflow-hidden border border-divider">
|
||||
<img
|
||||
src={bgImage}
|
||||
@@ -663,6 +683,23 @@ export default function ConfigPage() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bgImage && isTheme && (
|
||||
<div className="relative rounded-xl border border-divider bg-background h-32 flex items-center justify-center">
|
||||
<span className="text-foreground text-sm font-medium">当前为自适应纯色背景</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bgImage && isSolidColor && (
|
||||
<div
|
||||
className="relative rounded-xl border border-divider h-32 flex items-center justify-center"
|
||||
style={{ backgroundColor: bgImage }}
|
||||
>
|
||||
<span className="text-gray-500 bg-white/80 dark:bg-black/80 px-2 py-1 rounded text-sm font-medium border border-gray-200 dark:border-gray-800 shadow-sm">
|
||||
纯色背景 ({bgImage})
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -327,7 +327,7 @@ export default function LimitPage() {
|
||||
<StaggerList className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4">
|
||||
{filteredRules.map((rule) => (
|
||||
<StaggerItem key={rule.id}>
|
||||
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden h-full">
|
||||
<Card className="shadow-sm overflow-hidden h-full">
|
||||
<CardHeader className="pb-2 md:pb-2">
|
||||
<div className="flex justify-between items-start w-full">
|
||||
<div>
|
||||
@@ -413,7 +413,7 @@ export default function LimitPage() {
|
||||
)
|
||||
) : (
|
||||
/* 空状态 */
|
||||
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
||||
<Card className="shadow-sm bg-default-50/50">
|
||||
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
||||
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
||||
暂无限速规则
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -8,7 +8,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
VitePWA({
|
||||
registerType: "autoUpdate",
|
||||
registerType: "prompt",
|
||||
injectRegister: "auto",
|
||||
includeAssets: ["favicon.ico", "apple-touch-icon.png"],
|
||||
manifest: {
|
||||
|
||||
Reference in New Issue
Block a user