feat(ui): redesign with transparent liquid glass (#414)

* docs: add design spec for iOS 26 Liquid Glass UI redesign

* docs: add implementation plan for iOS 26 Liquid Glass UI redesign

* feat(ui): add liquid glass CSS variables and update root layouts

* feat(ui): update Card and Modal components to iOS 26 Liquid Glass style

* feat(ui): redesign dashboard page to Liquid Glass style

* feat(ui): redesign node management page cards and charts

* feat(ui): redesign monitor page to Liquid Glass style

* feat(ui): redesign group and panel-sharing cards to Liquid Glass

* feat(ui): redesign settings and user components to Liquid Glass

* feat(ui): redesign profile and auth pages to Liquid Glass

* fix(ui): revert to LineChart to fix compilation issues

* refactor(ui): extract dashboard user header and cleanup admin layout

* feat(ui): refine monitor page hero metrics and fix build

* fix(ui): real data for monitor hero metrics and card padding adjustments

* fix(ui): apply global card padding and fix index layouts

* feat(ui): implement liquid-glass-react for all Card components

* fix(ui): resolve liquid-glass-react rendering issues causing invisible text and broken layout

* fix(ui): native CSS liquid-glass effect replacing NPM package

* fix(ui): cleanup duplicate closing tags and complete liquid glass tables

* fix(ui): apply perfect rounded corners to list views and table headers in non-compact mode

* fix(ui): apply liquid glass styling to grouped rules view in forward page

* fix(ui): re-implement liquid-glass natively to prevent text distortion and remove package dependency

* fix(ui): remove mouse distortion and preserve proper liquid glass texture

* fix(ui): remove svg filter causing text distortion and replace with pure css liquid glass

* fix(ui): apply properly scoped border radius to table headers

* fix(ui): remove hardcoded bg-default-50/60 from TableHeader

* fix(ui): apply global liquid glass styling and transparent overlays to all modals and dialogs

* fix(ui): remove absolute/relative class conflicts hiding Dialog modales and implement pure transparent layout

* feat(ui): apply global liquid glass styling to cards, buttons, and diagnosis modals
This commit is contained in:
sagit
2026-04-05 13:22:12 +08:00
committed by GitHub
parent ebf412b9df
commit 938e9ff4fd
32 changed files with 1349 additions and 317 deletions
+64 -4
View File
@@ -1,9 +1,16 @@
import { useState } from "react";
import toast from "react-hot-toast";
import { useNavigate } from "react-router-dom";
import { AnimatedPage } from "@/components/animated-page";
import { Card, CardBody, CardHeader } from "@/shadcn-bridge/heroui/card";
import { Button } from "@/shadcn-bridge/heroui/button";
import {
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
} from "@/shadcn-bridge/heroui/dropdown";
import {
Modal,
ModalContent,
@@ -15,6 +22,8 @@ import { AnnouncementBanner } from "@/pages/dashboard/components/announcement-ba
import { AnnouncementModal } from "@/pages/dashboard/components/announcement-modal";
import { FlowChartCard } from "@/pages/dashboard/components/flow-chart-card";
import { MetricCard } from "@/pages/dashboard/components/metric-card";
import { getSessionName } from "@/utils/session";
import { safeLogout } from "@/utils/logout";
import {
formatNodeRenewalTime,
getNodeRenewalCycleLabel,
@@ -35,6 +44,15 @@ interface AddressItem {
}
export default function DashboardPage() {
const navigate = useNavigate();
const username = getSessionName() || "User";
const handleLogout = () => {
safeLogout();
toast.success("已退出登录");
navigate("/");
};
const {
loading,
userInfo,
@@ -630,6 +648,48 @@ export default function DashboardPage() {
return (
<AnimatedPage className="px-3 lg:px-6 py-2 lg:py-4">
{/* 顶部个人状态和下拉菜单 */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-2xl lg:text-3xl font-bold text-foreground">Good morning, {username}</h1>
<p className="text-sm text-default-500 mt-1">Here's what's happening with your network today.</p>
</div>
<div className="flex items-center gap-3">
<Dropdown placement="bottom-end">
<DropdownTrigger>
<Button
className="rounded-full w-10 h-10 min-w-0 bg-primary text-white font-bold text-sm shadow-[0_4px_12px_rgba(0,122,255,0.3)]"
isIconOnly
variant="solid"
>
{username.slice(0, 2).toUpperCase()}
</Button>
</DropdownTrigger>
<DropdownMenu aria-label="用户菜单" className="bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl border border-white/50 dark:border-white/10 rounded-2xl shadow-[0_10px_30px_rgba(0,0,0,0.1)]">
<DropdownItem
key="profile"
startContent={
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
}
onPress={() => navigate("/profile")}
>
个人资料
</DropdownItem>
<DropdownItem
key="logout"
className="text-danger"
color="danger"
startContent={
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clipRule="evenodd" /></svg>
}
onPress={handleLogout}
>
退出登录
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
{announcement && <AnnouncementBanner announcement={announcement} />}
{announcement && (
<AnnouncementModal
@@ -823,7 +883,7 @@ export default function DashboardPage() {
{/* 隧道权限 - 管理员不显示 */}
{!isAdmin && (
<Card className="mb-6 lg:mb-8 border border-gray-200 dark:border-default-200 shadow-md">
<Card className="mb-6 lg:mb-8">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<svg
@@ -945,7 +1005,7 @@ export default function DashboardPage() {
)}
{/* 规则配置 */}
<Card className="border border-gray-200 dark:border-default-200 shadow-md">
<Card>
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<svg
@@ -1016,7 +1076,7 @@ export default function DashboardPage() {
</h4>
<div className="space-y-1">
<button
className={`block px-2 py-1 bg-green-100 dark:bg-green-500/20 text-green-700 dark:text-green-300 rounded font-mono text-xs truncate ${hasMultipleIps(forward.inIp) ? "cursor-pointer hover:bg-green-200 dark:hover:bg-green-500/30" : ""}`}
className={`block px-2 py-1 bg-default-50/50 dark:bg-default-100/20 backdrop-blur-md rounded-lg border border-default-200/50 dark:border-default-700/50 font-mono text-xs truncate text-foreground transition-colors duration-200 ${hasMultipleIps(forward.inIp) ? "cursor-pointer hover:bg-default-100 dark:hover:bg-default-200/50" : ""}`}
disabled={!hasMultipleIps(forward.inIp)}
title={formatInAddress(
forward.inIp,
@@ -1037,7 +1097,7 @@ export default function DashboardPage() {
↓
</div>
<button
className={`block px-2 py-1 bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-300 rounded font-mono text-xs truncate ${hasMultipleRemoteAddresses(forward.remoteAddr) ? "cursor-pointer hover:bg-blue-200 dark:hover:bg-blue-500/30" : ""}`}
className={`block px-2 py-1 bg-default-50/50 dark:bg-default-100/20 backdrop-blur-md rounded-lg border border-default-200/50 dark:border-default-700/50 font-mono text-xs truncate text-foreground transition-colors duration-200 ${hasMultipleRemoteAddresses(forward.remoteAddr) ? "cursor-pointer hover:bg-default-100 dark:hover:bg-default-200/50" : ""}`}
disabled={
!hasMultipleRemoteAddresses(
forward.remoteAddr,