数据准确性 - 票数初始为 0,不再用 Math.sqrt 公式造假票 - 排序 tiebreaker 统一为 votes desc + no asc,确保稳定 - store.rank() 与 sortArtists() 行为对齐 Top12 / 出道位 - Top12Bar 仅收纳真正有票的人(votes > 0),0 票时显示"出道位尚未产生"空态 - ArtistCard / RankingRow / SearchModal / MyFanSupport / RankCard 的 inTop12 高亮全部加 votes > 0 守卫 - ArtistFilters 新增"实时排名 / 编号顺序"分段切换 + 首页 sortKey 状态 领奖台 (Top3Podium) - 1 人有票即可显示领奖台(此前要求 >= 3 人才显示) - 缺位的 #2/#3 用"虚位以待"占位卡片填充,与正式卡片同 3:4 比例对齐 - 全员 0 票时三张全部显示虚位以待 - 卡片间距拉大到 gap-8 sm:gap-12 排行榜页 (/ranking) - API 票数与本地乐观投票取 max() 合并,避免 API 落后覆盖本地新票 - 合并后重新排序与赋 rank - 仅 >= 12 人有票才显示出道线分隔与复活位标记 - 复活位 gapToDebut 计算修正 跨日额度 - selectRemaining 按 quotaDate 判断是否跨日,跨日自动回满额(此前会卡在昨日剩余值) 搜索 (SearchModal) - 改为订阅 store 拿活的票数,投票后立即反映 - 默认"热门 Top12"只在真正有票时显示,否则降级为"推荐艺人 · 编号顺序" - 票数显示统一走 formatVotes(0 票不再显示 0.0w) 人物详情 - 36 人真实数据接入,移除全部静态数据(slogan/birthday/cv/themeColor) - 接入人物小传 docx 数据(年龄/身高/性格/口头禅/技能/赛道/座右铭/长简介) - 视频区与版心同宽 + 首帧自动作为封面 + 整区点击播放/暂停 + 可拖拽进度条 - 表演图片改为三张氛围图竖向 3:4,左对齐 - 移除分享按钮,投票按钮全宽 个人页 (/me) - 移除等级/邀请好友/签到/编辑资料等静态数据 - 退出登录按钮在移动端 icon-only 显示(此前 sm:hidden 导致移动端无法登出) - 我的应援 list 基于真实 myVotesByArtist 派生,凯之类的投票真正同步过去 导航 - 余票徽章未登录态显示 0/0,已登录显示 N/10 - 登录/注册按钮样式与登录后头像胶囊保持一致(紫色实心) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
2.8 KiB
TypeScript
76 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
interface QuotaCardProps {
|
|
/** 今日剩余票数 */
|
|
remaining: number;
|
|
/** 每日总额度(用于「重置为 X 票」展示) */
|
|
dailyQuota: number;
|
|
}
|
|
|
|
export default function QuotaCard({ remaining, dailyQuota }: QuotaCardProps) {
|
|
return (
|
|
<div className="relative overflow-hidden rounded-2xl border border-purple-500/30 bg-gradient-to-br from-purple-500/[0.12] via-purple-500/[0.04] to-transparent shadow-[0_8px_32px_rgba(0,0,0,0.55),0_0_28px_rgba(139,92,246,0.2)]">
|
|
{/* 装饰:右侧紫色光晕 */}
|
|
<div
|
|
aria-hidden
|
|
className="absolute right-0 top-0 bottom-0 w-1/2 pointer-events-none"
|
|
style={{
|
|
background:
|
|
"radial-gradient(circle at 70% 50%, rgba(139,92,246,0.45) 0%, transparent 60%)",
|
|
}}
|
|
/>
|
|
{/* 装饰:右侧"水晶"占位(无素材时用 CSS 渲染的辉光六边形) */}
|
|
<CrystalDecoration />
|
|
|
|
<div className="relative p-6 sm:p-8">
|
|
<p className="text-xs text-white/70 tracking-wider">今日剩余票数</p>
|
|
<div className="mt-2 flex items-baseline gap-1">
|
|
<span className="font-display text-5xl sm:text-6xl text-white tabular-nums leading-none">
|
|
{remaining}
|
|
</span>
|
|
<span className="text-xl text-white/85 ml-1">票</span>
|
|
</div>
|
|
<p className="text-[11px] text-white/55 mt-3">
|
|
明日 00:00 重置为{" "}
|
|
<span className="text-purple-300 font-display tabular-nums">
|
|
{dailyQuota}
|
|
</span>{" "}
|
|
票
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/** 右侧装饰:用 CSS 渲染的简易"紫色水晶"效果,作为 3D 素材到位前的占位。 */
|
|
function CrystalDecoration() {
|
|
return (
|
|
<div
|
|
aria-hidden
|
|
className="absolute right-4 sm:right-8 top-1/2 -translate-y-1/2 w-32 h-32 sm:w-40 sm:h-40 pointer-events-none"
|
|
style={{ perspective: "600px" }}
|
|
>
|
|
<div
|
|
className="absolute inset-0 rounded-2xl border border-purple-300/40"
|
|
style={{
|
|
background:
|
|
"linear-gradient(135deg, rgba(196,181,253,0.4) 0%, rgba(139,92,246,0.25) 60%, rgba(91,33,182,0.4) 100%)",
|
|
transform: "rotate(45deg) skewY(8deg)",
|
|
boxShadow:
|
|
"0 0 24px rgba(196,181,253,0.5), inset 0 0 24px rgba(255,255,255,0.15)",
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute inset-3 rounded-xl"
|
|
style={{
|
|
background:
|
|
"linear-gradient(135deg, rgba(255,255,255,0.18) 0%, transparent 60%)",
|
|
transform: "rotate(45deg) skewY(8deg)",
|
|
}}
|
|
/>
|
|
<div className="absolute top-1/4 left-1/4 w-2 h-2 rounded-full bg-white/85 shadow-[0_0_8px_rgba(255,255,255,0.9)]" />
|
|
<div className="absolute bottom-1/4 right-1/4 w-1 h-1 rounded-full bg-purple-200 shadow-[0_0_6px_rgba(196,181,253,0.9)]" />
|
|
</div>
|
|
);
|
|
}
|