seaislee1209 6b22e1fa3f
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m5s
fix: 用户端文案从「额度」改为具体单位「次数」— 消除点数概念混淆
- Sidebar 左下角:去钻石图标(避免用户带入即梦/豆包的"点数"概念)+
  数据从 daily_seconds (秒数池残留) 改为 daily_generation_limit (次数);
  文案 "剩余额度"→"今日剩余次数"(必须写全,用户不猜);
  数字字号放大 14→18,tabular-nums 稳定排版
- ProfilePage 预警 banner: "今日额度已使用 X%"→"今日生成次数已用 X%";
  "今日额度已用完"→"今日生成次数已用完"
- generation.ts 错误映射: "额度不足,请联系管理员"→
  "今日生成次数或团队余额不足,请联系管理员"(两种可能都列出)

秒数池(daily_seconds_limit)是 v0.10.0 计费改次数+金额前的遗留概念,
这次把用户端可见的"额度"全部替换为明确的"生成次数/余额"单位,避免用户
把"额度"理解成即梦/豆包的"点数"来找客服问问题。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:07:02 +08:00

114 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '../store/auth';
import logoImg from '../assets/logo_32.png';
import styles from './Sidebar.module.css';
export function Sidebar() {
const navigate = useNavigate();
const location = useLocation();
const user = useAuthStore((s) => s.user);
const quota = useAuthStore((s) => s.quota);
const isActive = (path: string) => location.pathname === path;
const role = user?.role;
// 今日剩余生成次数v0.10.0 起计费体系为次数+金额,不再是秒数池)
const dailyRemaining = quota
? (quota.daily_generation_limit === -1
? Infinity
: Math.max(0, quota.daily_generation_limit - quota.daily_generation_used))
: 0;
return (
<aside className={styles.sidebar}>
{/* Logo */}
<div className={styles.logo} onClick={() => navigate(role === 'super_admin' ? '/admin/dashboard' : '/app')}>
<img src={logoImg} alt="AirDrama" width="32" height="32" />
</div>
{/* Nav items */}
<nav className={styles.navItems}>
{/* Video generation - team members and team admins only */}
{role !== 'super_admin' && (
<>
<div
className={`${styles.navItem} ${isActive('/app') ? styles.active : ''}`}
onClick={() => navigate('/app')}
>
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span></span>
</div>
<div
className={`${styles.navItem} ${isActive('/user-assets') ? styles.active : ''}`}
onClick={() => navigate('/user-assets')}
>
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<rect x="3" y="3" width="18" height="18" rx="2" />
<path d="M3 9h18M9 3v18" />
</svg>
<span></span>
</div>
</>
)}
{/* Team management - team admin only */}
{role === 'team_admin' && (
<div
className={`${styles.navItem} ${location.pathname.startsWith('/team') ? styles.active : ''}`}
onClick={() => navigate('/team/dashboard')}
>
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
</svg>
<span></span>
</div>
)}
</nav>
{/* Bottom section: quota + avatar + admin */}
<div className={styles.bottom}>
{/* Quota display - not for super admin */}
{role !== 'super_admin' && (
<div
className={styles.quota}
onClick={() => navigate('/profile')}
title="今日剩余生成次数(实际扣费以火山 token 消耗为准)"
>
<span className={styles.quotaNumber}>
{dailyRemaining === Infinity ? '∞' : dailyRemaining.toLocaleString()}
</span>
<span className={styles.quotaLabel}></span>
</div>
)}
{/* Admin entry - super admin only */}
{role === 'super_admin' && (
<div
className={styles.adminBtn}
onClick={() => navigate('/admin/dashboard')}
title="管理后台"
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<circle cx="12" cy="12" r="3" />
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" />
</svg>
</div>
)}
{/* User avatar */}
<div
className={styles.avatar}
onClick={() => navigate(role === 'super_admin' ? '/admin/dashboard' : '/profile')}
title={user?.username || '个人中心'}
>
{user?.username?.charAt(0).toUpperCase() || 'U'}
</div>
</div>
</aside>
);
}