diff --git a/src/app/(main)/dashboard/page.tsx b/src/app/(main)/dashboard/page.tsx index 6fc1804..725f4d5 100644 --- a/src/app/(main)/dashboard/page.tsx +++ b/src/app/(main)/dashboard/page.tsx @@ -932,7 +932,7 @@ export default function DashboardPage() { value={fmtInt(periodData?.coins.reward_video_coin_total)} delta={rewardVideoCoinRatio.value} deltaTone={rewardVideoCoinRatio.tone} - hint="激励视频金币已按产品口径计入领券奖励金币,这里单独展示来源占比。" + hint="独立统计激励视频发放,不再重复计入领券奖励。" /> {fmtCents(periodData?.cash.withdraw_success_cents)}。 累计已发放 {fmtCoinAmount(data.coins.granted_total)} 金币但累计真实提现仅 {fmtCents(data.cash.withdraw_success_cents)},发放与现金兑付背离属正常(金币沉淀/过期); - 需盯紧各来源占比突变与异常领取,防薅羊毛。 + 邀请奖励、后台手动加金币、福利页/老版本未打标签信息流不进入这 4 项,需盯紧各来源占比突变与异常领取。 diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index cccada5..c9046ed 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -12,7 +12,6 @@ import { HeartOutlined, LogoutOutlined, MessageOutlined, - MobileOutlined, MoneyCollectOutlined, NotificationOutlined, ProfileOutlined, @@ -21,26 +20,66 @@ import { TeamOutlined, UserOutlined, } from '@ant-design/icons'; -import { Avatar, Dropdown, Layout, Menu } from 'antd'; +import { Avatar, Dropdown, Layout, Tooltip } from 'antd'; import { clearAuth, getAdmin, getToken } from '@/lib/auth'; import type { AdminInfo } from '@/lib/types'; const { Sider, Header, Content } = Layout; -const MENU = [ - { key: '/dashboard', icon: , label: '数据大盘' }, - { key: '/users', icon: , label: '用户管理' }, - { key: '/devices', icon: , label: '设备管理' }, - { key: '/device-liveness', icon: , label: '设备存活' }, - { key: '/withdraws', icon: , label: '提现管理' }, - { key: '/price-reports', icon: , label: '低价审核' }, - { key: '/comparison-records', icon: , label: '比价记录' }, - { key: '/feedbacks', icon: , label: '用户反馈' }, - { key: '/coupon-data', icon: , label: '领券数据' }, - { key: '/ad-revenue', icon: , label: '广告配置' }, - { key: '/ad-revenue-report', icon: , label: '广告收益' }, - { key: '/cps', icon: , label: 'CPS 分发' }, - { key: '/config', icon: , label: '系统配置' }, +type NavItem = { + key: string; + icon: React.ReactNode; + label: string; + superOnly?: boolean; +}; + +type NavGroup = + | (NavItem & { children?: never }) + | { + key: string; + icon: React.ReactNode; + label: string; + children: NavItem[]; + superOnly?: boolean; + }; + +const hasChildren = (group: NavGroup): group is NavGroup & { children: NavItem[] } => + Array.isArray(group.children); + +const NAV_GROUPS: NavGroup[] = [ + { + key: 'dashboard', + icon: , + label: '看板', + children: [ + { key: '/dashboard', icon: , label: '数据大盘' }, + { key: '/coupon-data', icon: , label: '领券数据' }, + { key: '/ad-revenue-report', icon: , label: '广告收益' }, + { key: '/comparison-records', icon: , label: '比价记录' }, + { key: '/cps', icon: , label: 'CPS收益' }, + { key: '/device-liveness', icon: , label: '设备存活' }, + ], + }, + { + key: 'reward-review', + icon: , + label: '奖励审核', + children: [ + { key: '/withdraws', icon: , label: '提现审核' }, + { key: '/price-reports', icon: , label: '低价审核' }, + { key: '/feedbacks', icon: , label: '用户反馈' }, + ], + }, + { + key: 'data-config', + icon: , + label: '数据配置', + children: [ + { key: '/config', icon: , label: '系统配置' }, + { key: '/ad-revenue', icon: , label: '广告配置' }, + { key: '/users', icon: , label: '用户管理' }, + ], + }, { key: '/admins', icon: , label: '管理员', superOnly: true }, { key: '/event-logs', icon: , label: '埋点日志' }, { key: '/audit-logs', icon: , label: '审计日志' }, @@ -50,6 +89,7 @@ export default function MainLayout({ children }: { children: React.ReactNode }) const router = useRouter(); const pathname = usePathname(); const [admin, setAdmin] = useState(null); + const [collapsed, setCollapsed] = useState(false); useEffect(() => { if (!getToken()) { @@ -61,14 +101,19 @@ export default function MainLayout({ children }: { children: React.ReactNode }) if (!admin) return null; // 守卫期间不闪烁内容 - const items = MENU.filter((m) => !m.superOnly || admin.role === 'super_admin').map((m) => ({ - key: m.key, - icon: m.icon, - label: m.label, - })); - - // 选中态:取路径一级(/users/123 → /users) + // 选中态:取路径一级(/users/123 -> /users) const selectedKey = '/' + (pathname.split('/')[1] || 'dashboard'); + const canShow = (item: { superOnly?: boolean }) => !item.superOnly || admin.role === 'super_admin'; + const visibleGroups = NAV_GROUPS + .filter(canShow) + .map((group) => { + if (!hasChildren(group)) return group; + return { ...group, children: group.children.filter(canShow) }; + }) + .filter((group) => !hasChildren(group) || group.children.length > 0); + const flatNavItems = visibleGroups.flatMap((group) => + hasChildren(group) ? group.children : [group], + ); const logout = () => { clearAuth(); @@ -77,7 +122,15 @@ export default function MainLayout({ children }: { children: React.ReactNode }) return ( - +
运营后台
- router.push(key)} - /> +
{children} + ); }