diff --git a/src/assets/main.css b/src/assets/main.css index c347e20..fa211d2 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -11,10 +11,48 @@ body, padding: 0; } +/* + * 背景统一绘制在 上: + * 根元素的背景会被绘制到整个画布(canvas)上,铺满窗口的每一个像素, + * 不会出现“元素高度(100vh) 与实际窗口差几个像素”导致的上下留白。 + */ +html { + background-color: #eef2ff; + background-image: + radial-gradient(1200px 600px at 10% -10%, rgba(99, 102, 241, 0.28), transparent 60%), + radial-gradient(1000px 500px at 110% 10%, rgba(34, 211, 238, 0.28), transparent 55%), + linear-gradient(160deg, #eef2ff 0%, #f8fafc 45%, #eef7ff 100%); + background-repeat: no-repeat; +} + +html.dark { + background-color: #0b1220; + background-image: + radial-gradient(1200px 600px at 10% -10%, rgba(99, 102, 241, 0.35), transparent 60%), + radial-gradient(1000px 500px at 110% 10%, rgba(14, 165, 233, 0.28), transparent 55%), + linear-gradient(160deg, #0b1220 0%, #0f172a 45%, #111827 100%); +} + +/* 选中自定义背景图时替换为图片:等比缩放铺满,不拉伸变形 */ +html.has-bg-image { + background-image: var(--app-bg-image, none); + background-size: cover; + background-position: center center; + background-repeat: no-repeat; +} + body { overflow: hidden; font-family: 'Inter', 'HarmonyOS Sans SC', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif; -webkit-font-smoothing: antialiased; + /* 背景交由 绘制,body 保持透明避免覆盖画布背景 */ + background-color: transparent; +} + +/* 窗口圆角固定 8px,与 Windows 11 系统默认圆角保持一致 */ +#app { + border-radius: 8px; + overflow: hidden; } /* 悬浮磨砂玻璃面板 */ diff --git a/src/config/backgrounds.ts b/src/config/backgrounds.ts index ab13fb6..10a364b 100644 --- a/src/config/backgrounds.ts +++ b/src/config/backgrounds.ts @@ -1,6 +1,6 @@ import bg1 from "@/assets/images/bg-1.webp" import bg2 from "@/assets/images/bg-2.webp" -import bg3 from "@/assets/images/bg-3.webp" +// import bg3 from "@/assets/images/bg-3.webp" // 暂时下架「背景三」,需要时与下方选项一并恢复 /** 窗口背景图片标识 */ export type BackgroundKey = "none" | "bg-1" | "bg-2" | "bg-3" @@ -19,7 +19,7 @@ export const backgroundOptions: BackgroundOption[] = [ { key: "none", label: "settings.appearance.none", url: "" }, { key: "bg-1", label: "settings.appearance.bg1", url: bg1 }, { key: "bg-2", label: "settings.appearance.bg2", url: bg2 }, - { key: "bg-3", label: "settings.appearance.bg3", url: bg3 }, + // { key: "bg-3", label: "settings.appearance.bg3", url: bg3 }, ] export const DEFAULT_BACKGROUND: BackgroundKey = "none" diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index d3785e2..16279ba 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -22,7 +22,8 @@ "dashboard": "Dashboard", "servers": "Servers", "tunnels": "Tunnels", - "download": "Download frpc" + "download": "Download frpc", + "setting": "Settings" }, "system": { "cpu": "CPU usage", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index bfaeec5..27fb09f 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -22,7 +22,8 @@ "dashboard": "仪表盘", "servers": "服务器", "tunnels": "隧道列表", - "download": "Frpc 下载" + "download": "Frpc 下载", + "setting": "设置" }, "system": { "cpu": "CPU 占用", diff --git a/src/layout/Layout.vue b/src/layout/Layout.vue index 92cb1ab..f04b3ea 100644 --- a/src/layout/Layout.vue +++ b/src/layout/Layout.vue @@ -1,6 +1,6 @@ @@ -59,6 +56,7 @@ import { CloudServerOutlined, DashboardOutlined, DownloadOutlined, + SettingOutlined, } from "@ant-design/icons-vue" import { useI18n } from "vue-i18n" import type { MenuProps } from "ant-design-vue" @@ -74,6 +72,9 @@ const { cpu, memory } = useSystemStats() const selectedKeys = computed(() => [route.path]) +/** 设置项在运行状态下方独立渲染,子路由(如 /setting/appearance)同样高亮 */ +const settingSelectedKeys = computed(() => (route.path.startsWith("/setting") ? ["/setting"] : [])) + const menuItems = computed(() => [ { key: "/dashboard", icon: () => h(DashboardOutlined), label: t("menu.dashboard") }, { key: "/servers", icon: () => h(CloudServerOutlined), label: t("menu.servers") }, @@ -81,6 +82,10 @@ const menuItems = computed(() => [ { key: "/download", icon: () => h(DownloadOutlined), label: t("menu.download") }, ]) +const settingItems = computed(() => [ + { key: "/setting", icon: () => h(SettingOutlined), label: t("menu.setting") }, +]) + const onMenuClick: MenuProps["onClick"] = ({ key }) => { router.push(String(key)) } @@ -124,6 +129,15 @@ const statusText = computed(() => { border-color: rgba(255, 255, 255, 0.08); } +/* 系统信息与设置入口之间的分隔线 */ +.system-divider { + border-color: rgba(15, 23, 42, 0.06); +} + +.dark .system-divider { + border-color: rgba(255, 255, 255, 0.06); +} + /* 让 Antd 菜单透明化,融入磨砂玻璃面板 */ .aside-menu { background: transparent; diff --git a/src/stores/app.ts b/src/stores/app.ts index 38731ba..156efdc 100644 --- a/src/stores/app.ts +++ b/src/stores/app.ts @@ -32,6 +32,19 @@ export const useAppStore = defineStore("app", () => { const background = useStorage("frpilot:background", DEFAULT_BACKGROUND) const backgroundUrl = computed(() => getBackgroundUrl(background.value)) + // 把背景图同步到 (整窗背景,避免元素高度误差造成的上下留白) + watchEffect(() => { + const root = document.documentElement + const url = backgroundUrl.value + if (url) { + root.style.setProperty("--app-bg-image", `url('${url}')`) + root.classList.add("has-bg-image") + } else { + root.classList.remove("has-bg-image") + root.style.removeProperty("--app-bg-image") + } + }) + // Frpc 运行状态(后续由后端驱动) const runStatus = ref("stopped")