Files
controller-v2/client/src/pages/VUPage.tsx
T

124 lines
3.9 KiB
TypeScript
Raw Normal View History

/* ============================================================
VU PAGE — VU Meter Gallery
Design: Reference luxsin_x8_VU表.png
- Card-based VU meter style selector
- Active = cyan check circle below card
- Inactive = empty circle
============================================================ */
import { useDevice } from "@/contexts/DeviceContext";
import { cn } from "@/lib/utils";
import { Check, ChevronLeft } from "lucide-react";
import { useLocation } from "wouter";
interface VUStyle {
index: number;
name: string;
description: string;
preview: string;
}
const VU_STYLES: VUStyle[] = [
{
index: 0,
name: "经典复古",
description: "琥珀色指针表盘,经典模拟风格",
preview: "/vu/vu1.png",
},
{
index: 1,
name: "现代平面",
description: "LED 电平条,简洁现代风格",
preview: "/vu/vu2.png",
},
{
index: 2,
name: "数字显示",
description: "数字电平显示,精准直观",
preview: "/vu/vu3.png",
},
{
index: 3,
name: "条形图",
description: "垂直条形图,专业风格",
preview: "/vu/vu4.png",
},
{
index: 4,
name: "渐变光谱",
description: "彩虹渐变光谱,炫酷动感",
preview: "/vu/vu5.png",
},
{
index: 5,
name: "频谱分析",
description: "实时频谱分析,专业监测",
preview: "/vu/vu6.png",
},
];
export default function VUPage() {
const [, setLocation] = useLocation();
const { deviceState, updateSetting } = useDevice();
const currentVU = deviceState?.vu ?? 0;
return (
<div className="min-h-screen bg-black">
<div className="page-header">
<button onClick={() => setLocation("/")} className="mr-4 text-white/60 active:text-white transition-colors">
<ChevronLeft size={24} />
</button>
<h1 className="flex-1 text-center text-[17px] font-semibold text-white">VU 表库</h1>
<div className="w-8" />
</div>
<div className="px-4 pb-32 pt-4 space-y-5">
{VU_STYLES.map((style) => {
const isActive = currentVU === style.index;
return (
<div key={style.index} className="space-y-3">
{/* Card */}
<div
className={cn(
"ios-list-group p-3 cursor-pointer transition-all duration-200",
isActive && "border-[#00FFF6]/30"
)}
style={isActive ? { borderColor: "rgba(0,255,246,0.3)", boxShadow: "0 0 20px rgba(0,255,246,0.08)" } : {}}
onClick={() => updateSetting({ vu: style.index })}
>
<img
src={style.preview}
alt={style.name}
className="w-full h-28 object-contain rounded-xl bg-black/20"
/>
</div>
{/* Selection indicator */}
<div className="flex justify-center">
<button
onClick={() => updateSetting({ vu: style.index })}
className={cn(
"w-7 h-7 rounded-full flex items-center justify-center transition-all duration-200",
isActive
? "bg-[#00FFF6] text-black shadow-[0_0_12px_rgba(0,255,246,0.5)]"
: "border-2 border-white/20 text-transparent"
)}
>
{isActive && <Check size={14} strokeWidth={3} />}
</button>
</div>
{/* Style name */}
<div className="text-center">
<p className={cn("text-[15px] font-medium", isActive ? "text-[#00FFF6]" : "text-white/60")}>
{style.name}
</p>
<p className="text-[12px] text-white/35 mt-0.5">{style.description}</p>
</div>
</div>
);
})}
</div>
</div>
);
}