feat: add card skins support
This commit is contained in:
13
apps/web/components/faces/circles.tsx
Normal file
13
apps/web/components/faces/circles.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { CardFaceProps } from "./types";
|
||||
|
||||
/** 卡面:光泽 + 装饰圆形 */
|
||||
export function CirclesFace(_: CardFaceProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="absolute inset-0 bg-linear-to-tr from-white/20 via-transparent to-transparent" />
|
||||
<div className="absolute -right-8 -top-8 size-44 rounded-full bg-white/10" />
|
||||
<div className="absolute right-4 -bottom-12 size-36 rounded-full bg-white/[0.07]" />
|
||||
<div className="absolute -left-6 -bottom-4 size-24 rounded-full bg-black/10" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
48
apps/web/components/faces/glow.tsx
Normal file
48
apps/web/components/faces/glow.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { CardFaceProps } from "./types";
|
||||
|
||||
/** 卡面:渐变光晕 + 噪点纹理 */
|
||||
export function GlowFace(_: CardFaceProps) {
|
||||
return (
|
||||
<>
|
||||
{/* 噪点纹理 SVG filter */}
|
||||
<svg className="absolute size-0">
|
||||
<filter id="card-noise">
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="0.65"
|
||||
numOctaves="3"
|
||||
stitchTiles="stitch"
|
||||
/>
|
||||
<feColorMatrix type="saturate" values="0" />
|
||||
<feBlend in="SourceGraphic" mode="overlay" result="blend" />
|
||||
<feComposite in="blend" in2="SourceGraphic" operator="in" />
|
||||
</filter>
|
||||
</svg>
|
||||
|
||||
{/* 噪点层 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.18] mix-blend-overlay"
|
||||
style={{ filter: "url(#card-noise)" }}
|
||||
/>
|
||||
|
||||
{/* 左下光晕 */}
|
||||
<div
|
||||
className="absolute -bottom-8 -left-8 size-48 rounded-full opacity-60 blur-3xl"
|
||||
style={{ background: "radial-gradient(circle, white 0%, transparent 70%)" }}
|
||||
/>
|
||||
{/* 右上光晕 */}
|
||||
<div
|
||||
className="absolute -right-6 -top-6 size-40 rounded-full opacity-40 blur-2xl"
|
||||
style={{ background: "radial-gradient(circle, white 0%, transparent 70%)" }}
|
||||
/>
|
||||
{/* 中心微光 */}
|
||||
<div
|
||||
className="absolute inset-x-0 top-1/2 mx-auto size-32 -translate-y-1/2 rounded-full opacity-20 blur-3xl"
|
||||
style={{ background: "radial-gradient(circle, white 0%, transparent 70%)" }}
|
||||
/>
|
||||
|
||||
{/* 顶部高光条 */}
|
||||
<div className="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-white/50 to-transparent" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
17
apps/web/components/faces/index.ts
Normal file
17
apps/web/components/faces/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { LineFace } from "./line";
|
||||
import { CirclesFace } from "./circles";
|
||||
import { GlowFace } from "./glow";
|
||||
import { VipFace } from "./vip";
|
||||
import { RedeyeFace } from "./redeye";
|
||||
export type { CardFaceProps, CardFaceComponent } from "./types";
|
||||
import type { CardFaceComponent } from "./types";
|
||||
|
||||
export const CARD_FACE_REGISTRY = {
|
||||
line: LineFace,
|
||||
circles: CirclesFace,
|
||||
glow: GlowFace,
|
||||
vip: VipFace,
|
||||
redeye: RedeyeFace,
|
||||
} satisfies Record<string, CardFaceComponent>;
|
||||
|
||||
export type CardFaceName = keyof typeof CARD_FACE_REGISTRY;
|
||||
17
apps/web/components/faces/line.tsx
Normal file
17
apps/web/components/faces/line.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { CardFaceProps } from "./types";
|
||||
|
||||
/** 卡面:斜线纹理 + 右上角光晕 */
|
||||
export function LineFace(_: CardFaceProps) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.07]"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"repeating-linear-gradient(135deg, #fff 0px, #fff 1px, transparent 1px, transparent 12px)",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute -right-10 -top-10 size-48 rounded-full bg-white/15 blur-2xl" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
241
apps/web/components/faces/redeye.tsx
Normal file
241
apps/web/components/faces/redeye.tsx
Normal file
@@ -0,0 +1,241 @@
|
||||
import type { CardFaceProps } from "./types";
|
||||
|
||||
/** 卡面:深黑底色 + 深红光晕与锐利几何线条 */
|
||||
export function RedeyeFace(_: CardFaceProps) {
|
||||
return (
|
||||
<>
|
||||
{/* 深黑底色 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: "linear-gradient(145deg, #0a0505 0%, #120808 45%, #0d0404 100%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 红色主光晕:左上 */}
|
||||
<div
|
||||
className="absolute -left-8 -top-8 size-56 rounded-full opacity-25 blur-3xl"
|
||||
style={{
|
||||
background: "radial-gradient(circle, #cc1020 0%, #7a0810 50%, transparent 75%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 红色副光晕:右下 */}
|
||||
<div
|
||||
className="absolute -bottom-10 -right-6 size-44 rounded-full opacity-15 blur-3xl"
|
||||
style={{
|
||||
background: "radial-gradient(circle, #e01828 0%, transparent 70%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 顶部红色高光边 */}
|
||||
<div
|
||||
className="absolute inset-x-0 top-0 h-[1.5px]"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to right, transparent 5%, #cc1020 25%, #ff2233 50%, #cc1020 75%, transparent 95%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 左侧竖向红线 */}
|
||||
<div
|
||||
className="absolute bottom-0 left-5 top-0 w-px opacity-40"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to bottom, transparent, #cc1020 20%, #ff2233 50%, #cc1020 80%, transparent)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 右侧双竖线 */}
|
||||
<div
|
||||
className="absolute bottom-3 right-4 top-3 w-px opacity-30"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to bottom, transparent, #993010 30%, #cc2010 60%, transparent)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="absolute bottom-3 right-[18px] top-3 w-px opacity-15"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to bottom, transparent, #993010 30%, #cc2010 60%, transparent)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 红色横向扫描线组 */}
|
||||
<div
|
||||
className="absolute inset-x-0 opacity-[0.06]"
|
||||
style={{
|
||||
top: "30%",
|
||||
height: "1px",
|
||||
background:
|
||||
"linear-gradient(to right, transparent 0%, #ff2233 40%, #ff5566 60%, transparent 100%)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="absolute inset-x-0 opacity-[0.04]"
|
||||
style={{
|
||||
top: "32%",
|
||||
height: "1px",
|
||||
background: "linear-gradient(to right, transparent 10%, #ff2233 50%, transparent 90%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 电路板风格折角线:左上 */}
|
||||
<svg
|
||||
className="absolute left-6 top-3 opacity-20"
|
||||
width="40"
|
||||
height="28"
|
||||
viewBox="0 0 40 28"
|
||||
fill="none"
|
||||
>
|
||||
<polyline points="0,28 0,8 12,0 40,0" stroke="#ff2233" strokeWidth="1" fill="none" />
|
||||
<circle cx="12" cy="0" r="1.5" fill="#ff2233" />
|
||||
</svg>
|
||||
|
||||
{/* 电路板风格折角线:右下 */}
|
||||
<svg
|
||||
className="absolute bottom-3 right-6 opacity-20"
|
||||
width="40"
|
||||
height="28"
|
||||
viewBox="0 0 40 28"
|
||||
fill="none"
|
||||
>
|
||||
<polyline points="40,0 40,20 28,28 0,28" stroke="#cc1020" strokeWidth="1" fill="none" />
|
||||
<circle cx="28" cy="28" r="1.5" fill="#cc1020" />
|
||||
</svg>
|
||||
|
||||
{/* 风格菱形 */}
|
||||
<svg
|
||||
className="absolute opacity-[0.12]"
|
||||
style={{ right: "28px", top: "50%", transform: "translateY(-50%)" }}
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
>
|
||||
<polygon points="16,1 31,16 16,31 1,16" stroke="#ff2233" strokeWidth="1.5" fill="none" />
|
||||
<polygon points="16,7 25,16 16,25 7,16" stroke="#ff2233" strokeWidth="1" fill="none" />
|
||||
<circle cx="16" cy="16" r="2" fill="#ff2233" />
|
||||
</svg>
|
||||
|
||||
{/* 徽标 — 八角框 + 内部对角交叉 + 中心菱形 */}
|
||||
<svg
|
||||
className="absolute opacity-60"
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
filter: "drop-shadow(0 0 6px #cc102088)",
|
||||
}}
|
||||
width="56"
|
||||
height="56"
|
||||
viewBox="0 0 56 56"
|
||||
fill="none"
|
||||
>
|
||||
{/* 外八角轮廓 */}
|
||||
<polygon
|
||||
points="16,2 40,2 54,16 54,40 40,54 16,54 2,40 2,16"
|
||||
stroke="#cc1020"
|
||||
strokeWidth="1.5"
|
||||
fill="#0d0404"
|
||||
fillOpacity="0.85"
|
||||
/>
|
||||
{/* 内八角 */}
|
||||
<polygon
|
||||
points="20,8 36,8 48,20 48,36 36,48 20,48 8,36 8,20"
|
||||
stroke="#cc1020"
|
||||
strokeWidth="0.75"
|
||||
strokeOpacity="0.5"
|
||||
fill="none"
|
||||
/>
|
||||
{/* 对角线:左上 → 右下 */}
|
||||
<line
|
||||
x1="16"
|
||||
y1="16"
|
||||
x2="40"
|
||||
y2="40"
|
||||
stroke="#cc1020"
|
||||
strokeWidth="1"
|
||||
strokeOpacity="0.7"
|
||||
/>
|
||||
{/* 对角线:右上 → 左下 */}
|
||||
<line
|
||||
x1="40"
|
||||
y1="16"
|
||||
x2="16"
|
||||
y2="40"
|
||||
stroke="#cc1020"
|
||||
strokeWidth="1"
|
||||
strokeOpacity="0.7"
|
||||
/>
|
||||
{/* 横向中轴 */}
|
||||
<line
|
||||
x1="6"
|
||||
y1="28"
|
||||
x2="50"
|
||||
y2="28"
|
||||
stroke="#cc1020"
|
||||
strokeWidth="0.75"
|
||||
strokeOpacity="0.4"
|
||||
/>
|
||||
{/* 纵向中轴 */}
|
||||
<line
|
||||
x1="28"
|
||||
y1="6"
|
||||
x2="28"
|
||||
y2="50"
|
||||
stroke="#cc1020"
|
||||
strokeWidth="0.75"
|
||||
strokeOpacity="0.4"
|
||||
/>
|
||||
{/* 中心菱形 */}
|
||||
<polygon
|
||||
points="28,18 38,28 28,38 18,28"
|
||||
stroke="#ff2233"
|
||||
strokeWidth="1.25"
|
||||
fill="#1a0505"
|
||||
fillOpacity="0.9"
|
||||
/>
|
||||
{/* 中心圆点 */}
|
||||
<circle cx="28" cy="28" r="3" fill="#ff2233" />
|
||||
<circle cx="28" cy="28" r="1.5" fill="#ff6677" />
|
||||
{/* 四个顶点刻度点 */}
|
||||
<circle cx="28" cy="6" r="1.25" fill="#cc1020" fillOpacity="0.8" />
|
||||
<circle cx="50" cy="28" r="1.25" fill="#cc1020" fillOpacity="0.8" />
|
||||
<circle cx="28" cy="50" r="1.25" fill="#cc1020" fillOpacity="0.8" />
|
||||
<circle cx="6" cy="28" r="1.25" fill="#cc1020" fillOpacity="0.8" />
|
||||
</svg>
|
||||
|
||||
{/* 噪点 SVG filter */}
|
||||
<svg className="absolute size-0">
|
||||
<filter id="arasaka-noise">
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="0.8"
|
||||
numOctaves="4"
|
||||
stitchTiles="stitch"
|
||||
/>
|
||||
<feColorMatrix type="saturate" values="0" />
|
||||
<feBlend in="SourceGraphic" mode="overlay" result="blend" />
|
||||
<feComposite in="blend" in2="SourceGraphic" operator="in" />
|
||||
</filter>
|
||||
</svg>
|
||||
|
||||
{/* 噪点层 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.1] mix-blend-overlay"
|
||||
style={{ filter: "url(#arasaka-noise)" }}
|
||||
/>
|
||||
|
||||
{/* 底部红色压边 */}
|
||||
<div
|
||||
className="absolute inset-x-0 bottom-0 h-px opacity-30"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to right, transparent 10%, #7a0810 40%, #cc1020 60%, transparent 90%)",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
apps/web/components/faces/types.ts
Normal file
10
apps/web/components/faces/types.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { ComponentType } from "react";
|
||||
|
||||
/**
|
||||
* 卡面(卡底装饰)组件的 props。
|
||||
* 卡面仅负责视觉装饰(颜色纹理、光效、几何图形等),不接收任何业务数据。
|
||||
*/
|
||||
export type CardFaceProps = Record<string, never>;
|
||||
|
||||
/** 卡面组件类型 */
|
||||
export type CardFaceComponent = ComponentType<CardFaceProps>;
|
||||
129
apps/web/components/faces/vip.tsx
Normal file
129
apps/web/components/faces/vip.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import type { CardFaceProps } from "./types";
|
||||
|
||||
/** 卡面:黑金 VIP — 深黑底色 + 金色光晕与描边,体现尊贵身份 */
|
||||
export function VipFace(_: CardFaceProps) {
|
||||
return (
|
||||
<>
|
||||
{/* 完全覆盖底部 palette,建立黑金底色 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #0f0f0f 0%, #1a1610 40%, #0d0d0d 100%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 金色主光晕:右上角 */}
|
||||
<div
|
||||
className="absolute -right-10 -top-10 size-52 rounded-full opacity-30 blur-3xl"
|
||||
style={{
|
||||
background: "radial-gradient(circle, #d4a843 0%, #9a6f1a 50%, transparent 75%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 金色副光晕:左下角 */}
|
||||
<div
|
||||
className="absolute -bottom-12 -left-6 size-44 rounded-full opacity-20 blur-3xl"
|
||||
style={{
|
||||
background: "radial-gradient(circle, #c49b2e 0%, transparent 70%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 中部横向光带 */}
|
||||
<div
|
||||
className="absolute inset-x-0 top-1/2 h-px -translate-y-1/2 opacity-20"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to right, transparent 0%, #d4a843 30%, #f0d060 50%, #d4a843 70%, transparent 100%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 顶部金色高光边 */}
|
||||
<div
|
||||
className="absolute inset-x-0 top-0 h-px"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to right, transparent 5%, #c8992a 30%, #f5d060 50%, #c8992a 70%, transparent 95%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 底部深金色压边 */}
|
||||
<div
|
||||
className="absolute inset-x-0 bottom-0 h-px opacity-50"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to right, transparent 10%, #9a6f1a 40%, #b8881f 60%, transparent 90%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 右侧竖向装饰线 */}
|
||||
<div
|
||||
className="absolute bottom-4 right-5 top-4 w-px opacity-15"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(to bottom, transparent, #d4a843 30%, #f0d060 60%, transparent)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 细噪点 SVG filter */}
|
||||
<svg className="absolute size-0">
|
||||
<filter id="vip-noise">
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="0.75"
|
||||
numOctaves="4"
|
||||
stitchTiles="stitch"
|
||||
/>
|
||||
<feColorMatrix type="saturate" values="0" />
|
||||
<feBlend in="SourceGraphic" mode="overlay" result="blend" />
|
||||
<feComposite in="blend" in2="SourceGraphic" operator="in" />
|
||||
</filter>
|
||||
</svg>
|
||||
|
||||
{/* 噪点层:增加高端质感 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.12] mix-blend-overlay"
|
||||
style={{ filter: "url(#vip-noise)" }}
|
||||
/>
|
||||
|
||||
{/* 整体金色微光叠层 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.04]"
|
||||
style={{
|
||||
background: "linear-gradient(120deg, transparent 20%, #d4a843 50%, transparent 80%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 菱形网格纹 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.06]"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"repeating-linear-gradient(45deg, #d4a843 0px, #d4a843 1px, transparent 1px, transparent 14px), repeating-linear-gradient(-45deg, #d4a843 0px, #d4a843 1px, transparent 1px, transparent 14px)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* VIP 大水印文字 */}
|
||||
<div
|
||||
className="pointer-events-none absolute -bottom-4 -left-2 select-none font-black leading-none tracking-widest opacity-[0.07]"
|
||||
style={{
|
||||
fontSize: "84px",
|
||||
color: "#d4a843",
|
||||
fontFamily: "serif",
|
||||
letterSpacing: "0.15em",
|
||||
}}
|
||||
>
|
||||
VIP
|
||||
</div>
|
||||
|
||||
{/* 斜向扫光带 */}
|
||||
<div
|
||||
className="absolute -inset-y-4 w-12 -rotate-12 opacity-[0.08] blur-sm"
|
||||
style={{
|
||||
left: "38%",
|
||||
background:
|
||||
"linear-gradient(to bottom, transparent, #f5d060 30%, #fff8dc 50%, #f5d060 70%, transparent)",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
140
apps/web/components/id-tag-card.tsx
Normal file
140
apps/web/components/id-tag-card.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import { ThunderboltFill } from "@gravity-ui/icons";
|
||||
import { Nfc } from "lucide-react";
|
||||
import { CARD_FACE_REGISTRY, type CardFaceName } from "@/components/faces";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Palette
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const CARD_PALETTES = [
|
||||
"bg-linear-to-br from-blue-600 to-indigo-700",
|
||||
"bg-linear-to-br from-violet-600 to-purple-700",
|
||||
"bg-linear-to-br from-emerald-600 to-teal-700",
|
||||
"bg-linear-to-br from-rose-500 to-pink-700",
|
||||
"bg-linear-to-br from-amber-500 to-orange-600",
|
||||
"bg-linear-to-br from-slate-600 to-zinc-700",
|
||||
];
|
||||
|
||||
function paletteForId(idTag: string): string {
|
||||
const hash = idTag.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0);
|
||||
return CARD_PALETTES[hash % CARD_PALETTES.length];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Layout — 内容元素(余额、logo、卡号)的排列方式
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type CardLayoutName = "center" | "around";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IdTagCard
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type IdTagCardProps = {
|
||||
idTag: string;
|
||||
balance: number;
|
||||
isSelected?: boolean;
|
||||
/** 内容排列方式:余额、logo、卡号等信息元素的布局 */
|
||||
layout?: CardLayoutName;
|
||||
/** 卡底装饰风格:纹理、光效、几何图形等视觉元素 */
|
||||
skin?: CardFaceName;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export function IdTagCard({
|
||||
idTag,
|
||||
balance,
|
||||
isSelected = false,
|
||||
layout = "around",
|
||||
skin = "circles",
|
||||
onClick,
|
||||
}: IdTagCardProps) {
|
||||
const palette = paletteForId(idTag);
|
||||
const Skin = CARD_FACE_REGISTRY[skin];
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className={[
|
||||
"relative w-full overflow-hidden rounded-2xl cursor-pointer select-none",
|
||||
"aspect-[1.586/1] transition-all duration-200 active:scale-[0.97]",
|
||||
isSelected
|
||||
? "ring-3 ring-offset-2 ring-offset-background ring-accent shadow-2xl shadow-accent/25"
|
||||
: "ring-1 ring-black/8 hover:ring-accent/40 shadow-md hover:shadow-xl",
|
||||
].join(" ")}
|
||||
>
|
||||
{/* 渐变底色 */}
|
||||
<div className={`absolute inset-0 ${palette}`} />
|
||||
|
||||
{/* 卡底装饰 */}
|
||||
<Skin />
|
||||
|
||||
{/* 内容布局 */}
|
||||
{layout === "center" ? (
|
||||
<div className="relative flex h-full flex-col justify-between px-5 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[9px] font-bold uppercase tracking-[0.2em] text-white/50">
|
||||
储值卡
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<ThunderboltFill className="size-3.5 text-white/60" />
|
||||
<span className="text-[9px] font-bold uppercase tracking-[0.15em] text-white/60">
|
||||
Helios
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
<p className="text-[24px] font-medium leading-none tracking-[0.22em] text-white drop-shadow-md">
|
||||
{idTag.replace(/(.{4})/, "$1 ")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end justify-between">
|
||||
<Nfc className="size-6 rotate-180 text-white/30 stroke-[1.5]" />
|
||||
<div className="text-right">
|
||||
<p className="text-[8px] font-semibold uppercase tracking-[0.15em] text-white/40">
|
||||
余额
|
||||
</p>
|
||||
<p className="text-lg font-bold leading-tight text-white drop-shadow">
|
||||
¥{(balance / 100).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative flex h-full flex-col justify-between px-5 py-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<Nfc className="size-7 rotate-180 text-white/35 stroke-[1.5]" />
|
||||
<div className="flex items-center gap-1">
|
||||
<ThunderboltFill className="size-3.5 text-white/60" />
|
||||
<span className="text-[9px] font-bold uppercase tracking-[0.15em] text-white/60">
|
||||
Helios
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="flex flex-col items-start gap-0.5">
|
||||
<p className="text-[10px] font-semibold uppercase tracking-[0.15em] text-white/40">
|
||||
储值余额
|
||||
</p>
|
||||
<p className="text-[22px] font-bold text-white drop-shadow">
|
||||
¥{(balance / 100).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-end gap-0.5">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-[0.12em] text-white/35">
|
||||
储值卡
|
||||
</span>
|
||||
<p className="text-lg font-medium tracking-widest text-white/90 drop-shadow">
|
||||
{idTag.replace(/(.{4})/, "$1 ")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user