From 6f44dab3e9ba7e45b4ee2d4482ef8114d1748637 Mon Sep 17 00:00:00 2001 From: lowmaster-chen <1119780489@qq.com> Date: Wed, 1 Jul 2026 14:20:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E6=81=A2=E5=A4=8D=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=B7=A6=E4=BE=A7=E5=88=86=E7=BB=84=E5=AF=BC=E8=88=AA=E5=B9=B6?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E9=A2=86=E5=88=B8=E6=95=B0=E6=8D=AE=E5=85=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(main)/layout.tsx | 276 +++++++++++++++++++++++++++++++++----- 1 file changed, 245 insertions(+), 31 deletions(-) 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)} - /> + + {collapsed + ? flatNavItems.map((item) => ( + + router.push(item.key)} + aria-label={item.label} + > + {item.icon} + + + )) + : visibleGroups.map((group) => { + const isGroup = hasChildren(group); + const groupSelected = isGroup + ? group.children.some((child) => child.key === selectedKey) + : selectedKey === group.key; + if (!isGroup) { + return ( + router.push(group.key)} + > + {group.icon} + {group.label} + + ); + } + return ( + + + {group.icon} + {group.label} + + + {group.children.map((child) => ( + router.push(child.key)} + > + {child.icon} + {child.label} + + ))} + + + ); + })} + {children} + ); } -- 2.52.0 From cf474be2d55c5708bb4345d10927d43d49433268 Mon Sep 17 00:00:00 2001 From: lowmaster-chen <1119780489@qq.com> Date: Wed, 1 Jul 2026 14:56:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=A4=A7=E7=9B=98=E9=87=91=E5=B8=81=E5=8F=A3=E5=BE=84=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改了什么:更新激励视频金币与金币经济橙色说明,明确激励视频不再重复计入领券奖励,并说明未进入四项分类的奖励来源。 为什么改:配合后端金币四项加总口径,避免产品误解总数与分类卡片关系。 验证方式:npx tsc --noEmit。 --- src/app/(main)/dashboard/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 项,需盯紧各来源占比突变与异常领取。 -- 2.52.0