"use client"; import Link from "next/link"; import { ChevronLeft, Heart, Check, Quote as QuoteIcon, Sparkles, Compass, MessageCircle, User, Ruler, Calendar, BookOpen, } from "lucide-react"; import type { Artist } from "@/types/artist"; import { TAG_LABEL } from "@/types/artist"; import ArtistPortrait from "@/components/cards/ArtistPortrait"; import VoteModal from "@/components/VoteModal"; import Button from "@/components/ui/Button"; import RankCard from "./RankCard"; import PerformanceVideo from "./PerformanceVideo"; import PerformanceGallery from "./PerformanceGallery"; import FloatingVoteButton from "@/components/FloatingVoteButton"; import FloatingBackButton from "@/components/FloatingBackButton"; import { useVoteStore, selectArtist, selectHasVoted } from "@/lib/store"; import { useVoteAction } from "@/hooks/useVoteAction"; import { cn } from "@/lib/cn"; interface ArtistDetailContentProps { artist: Artist; allArtists: Artist[]; } /** 把 "、 / , ," 分隔的串切成 chip 数组 */ function parseChips(text?: string): string[] { if (!text) return []; return text .split(/[、,,/]/) .map((s) => s.trim()) .filter(Boolean); } export default function ArtistDetailContent({ artist: initialArtist, allArtists: initialAll, }: ArtistDetailContentProps) { // 用 store 数据覆盖(投票后票数能马上变) const storeArtist = useVoteStore(selectArtist(initialArtist.id)); const storeAll = useVoteStore((s) => s.artists); const hasVoted = useVoteStore(selectHasVoted(initialArtist.id)); const artist = storeArtist ?? initialArtist; const allArtists = storeAll.length ? storeAll : initialAll; const { target, remaining, totalQuota, openVote, closeVote, confirmVote } = useVoteAction(); return ( <> {/* 面包屑 */}
全部艺人 / {artist.name}
{/* HERO · 立绘 + 身份信息 */}
openVote(artist)} hasVoted={hasVoted} />
{/* 性格 · 口头禅 */} {(artist.personality || artist.catchphrase) && (
{artist.personality && } {artist.catchphrase && }
)} {/* 核心技能 · 核心赛道 */} {(artist.skills || artist.track) && (
{artist.skills && ( } chips={parseChips(artist.skills)} /> )} {artist.track && ( } chips={parseChips(artist.track)} /> )}
)} {/* 人物小传 · 长简介 */} {artist.bio && (
)} {/* 表演视频 · 与版心同宽,首帧自动作为封面,整个视频区域可点击播放/暂停 */} {artist.videoUrl && (

视频不会自动播放,避免流量浪费

)} {/* 表演图片 · 三张氛围图,左对齐,竖向 3:4 */} {artist.gallery && artist.gallery.filter(Boolean).length > 0 && (
)} openVote(artist)} hasVoted={hasVoted} /> ); } /* ============================================================ * 子组件 · 统一品牌紫色,无 per-artist themeColor * ============================================================ */ interface HeroPanelProps { artist: Artist; allArtists: Artist[]; onVote: () => void; hasVoted: boolean; } function HeroPanel({ artist, allArtists, onVote, hasVoted }: HeroPanelProps) { return (
{/* 装饰光晕 */}
{/* 立绘 */}
{hasVoted && (
)}
{/* 身份信息 */}
{/* 编号 */}
No.{artist.no}
{/* 中文名 / 英文名 */}

{artist.name}

{artist.enName}

{/* 实力标签 */}
{artist.tags.map((t) => ( {TAG_LABEL[t]} ))}
{/* 年龄 / 身高 / 性别 */}
} label="年龄" value={artist.age != null ? `${artist.age} 岁` : "未公开"} /> } label="身高" value={`${artist.height} cm`} /> } label="性别" value={ artist.gender === "M" ? "男生" : artist.gender === "F" ? "女生" : "未公开" } />
{/* 座右铭 · 品牌紫引文,保持与全站视觉一致 */} {artist.motto && (

{artist.motto}

Motto · 座右铭

)} {/* 排名卡片 */} {/* 操作按钮 · 仅投票 */}
); } function MetaCell({ icon, label, value, }: { icon: React.ReactNode; label: string; value: string; }) { return (
{icon} {label}
{value}
); } /** * 性格 / 口头禅 双卡使用完全相同的容器规范: * - 圆角 2xl + 玻璃化 surface 背景 + 同样的 border / padding * - 左上角同样的紫色装饰条 * - 同款 SectionHeading * 唯一差异:内容呈现 —— 性格是段落正文,口头禅是大字引号。 */ function ProfileInfoCard({ title, subtitle, children, }: { title: string; subtitle: string; children: React.ReactNode; }) { return (
{children}
); } function PersonalityCard({ text }: { text: string }) { return (

{text}

); } function CatchphraseCard({ text }: { text: string }) { return (

“{text}”

); } function ChipCard({ label, subtitle, icon, chips, }: { label: string; subtitle: string; icon: React.ReactNode; chips: string[]; }) { return (
{icon}
{chips.length > 0 ? (
{chips.map((c, i) => ( {c} ))}
) : (

未公开

)}
); } function BiographyCard({ bio }: { bio: string }) { return (

{bio}

); } function SectionHeading({ title, subtitle, }: { title: string; subtitle: string; }) { return (

{title}

{subtitle}
); }