98cea13d51
- 重命名:菜单与页面标题「反馈工单→用户反馈」「上报审核→低价审核」 - 反馈页「加群二维码」卡挪到系统配置页,新增「反馈二维码」tab - 修复反馈四态统计恒为 0:对接后端新增的 /admin/api/feedbacks/summary - 两页表格增强:用户列拆为 用户ID/手机号/用户昵称;新增 提交版本号、机型/OS版本 列; 点手机号弹抽屉看该用户全部记录,抽屉顶部汇总 提交/上报总计 + 奖励总计 - 低价审核:提交时间列可排序、审核结果与操作拆两列、顶部统计改卡片式、 Trace 列对齐比价记录页(有 trace_url 即可点) - 修复反馈时间显示快 8 小时:反馈时间为北京 wall-clock,改用 formatWallTime Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: zzhyyyyy <2685922758@qq.com> Reviewed-on: #28 Co-authored-by: zhuzihao <zhuzihao@wonderable.ai> Co-committed-by: zhuzihao <zhuzihao@wonderable.ai>
126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState } from 'react';
|
||
import { usePathname, useRouter } from 'next/navigation';
|
||
import {
|
||
BarChartOutlined,
|
||
DashboardOutlined,
|
||
DatabaseOutlined,
|
||
FileSearchOutlined,
|
||
FlagOutlined,
|
||
HeartOutlined,
|
||
LogoutOutlined,
|
||
MessageOutlined,
|
||
MobileOutlined,
|
||
MoneyCollectOutlined,
|
||
NotificationOutlined,
|
||
ProfileOutlined,
|
||
SettingOutlined,
|
||
ShareAltOutlined,
|
||
TeamOutlined,
|
||
UserOutlined,
|
||
} from '@ant-design/icons';
|
||
import { Avatar, Dropdown, Layout, Menu } 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: <DashboardOutlined />, label: '数据大盘' },
|
||
{ key: '/users', icon: <UserOutlined />, label: '用户管理' },
|
||
{ key: '/devices', icon: <MobileOutlined />, label: '设备管理' },
|
||
{ key: '/device-liveness', icon: <HeartOutlined />, label: '设备存活' },
|
||
{ key: '/withdraws', icon: <MoneyCollectOutlined />, label: '提现管理' },
|
||
{ key: '/price-reports', icon: <FlagOutlined />, label: '低价审核' },
|
||
{ key: '/comparison-records', icon: <ProfileOutlined />, label: '比价记录' },
|
||
{ key: '/feedbacks', icon: <MessageOutlined />, label: '用户反馈' },
|
||
{ key: '/ad-revenue', icon: <NotificationOutlined />, label: '广告配置' },
|
||
{ key: '/ad-revenue-report', icon: <BarChartOutlined />, label: '广告收益' },
|
||
{ key: '/cps', icon: <ShareAltOutlined />, label: 'CPS 分发' },
|
||
{ key: '/config', icon: <SettingOutlined />, label: '系统配置' },
|
||
{ key: '/admins', icon: <TeamOutlined />, label: '管理员', superOnly: true },
|
||
{ key: '/event-logs', icon: <DatabaseOutlined />, label: '埋点日志' },
|
||
{ key: '/audit-logs', icon: <FileSearchOutlined />, label: '审计日志' },
|
||
];
|
||
|
||
export default function MainLayout({ children }: { children: React.ReactNode }) {
|
||
const router = useRouter();
|
||
const pathname = usePathname();
|
||
const [admin, setAdmin] = useState<AdminInfo | null>(null);
|
||
|
||
useEffect(() => {
|
||
if (!getToken()) {
|
||
router.replace('/login');
|
||
return;
|
||
}
|
||
setAdmin(getAdmin());
|
||
}, [router]);
|
||
|
||
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)
|
||
const selectedKey = '/' + (pathname.split('/')[1] || 'dashboard');
|
||
|
||
const logout = () => {
|
||
clearAuth();
|
||
router.replace('/login');
|
||
};
|
||
|
||
return (
|
||
<Layout style={{ minHeight: '100vh' }}>
|
||
<Sider theme="dark" breakpoint="lg" collapsible>
|
||
<div
|
||
style={{
|
||
height: 48,
|
||
margin: 16,
|
||
color: '#fff',
|
||
fontWeight: 600,
|
||
textAlign: 'center',
|
||
lineHeight: '48px',
|
||
}}
|
||
>
|
||
运营后台
|
||
</div>
|
||
<Menu
|
||
theme="dark"
|
||
mode="inline"
|
||
selectedKeys={[selectedKey]}
|
||
items={items}
|
||
onClick={({ key }) => router.push(key)}
|
||
/>
|
||
</Sider>
|
||
<Layout>
|
||
<Header
|
||
style={{
|
||
background: '#fff',
|
||
padding: '0 24px',
|
||
display: 'flex',
|
||
justifyContent: 'flex-end',
|
||
alignItems: 'center',
|
||
}}
|
||
>
|
||
<Dropdown
|
||
menu={{
|
||
items: [
|
||
{ key: 'logout', icon: <LogoutOutlined />, label: '退出登录', onClick: logout },
|
||
],
|
||
}}
|
||
>
|
||
<span style={{ cursor: 'pointer' }}>
|
||
<Avatar size="small" icon={<UserOutlined />} /> {admin.username}({admin.role})
|
||
</span>
|
||
</Dropdown>
|
||
</Header>
|
||
<Content style={{ margin: 24 }}>{children}</Content>
|
||
</Layout>
|
||
</Layout>
|
||
);
|
||
}
|