feat: Wire all view components to Next.js App Router routes
- Create (admin) route group with AdminLayout wrapper - Add page.tsx route files for all 25 view pages (dashboard, issuers, users, trading, risk, compliance, system, disputes, coupons, finance, chain, reports, merchant, agent, insurance, analytics sub-pages, compliance sub-pages) - Update AdminLayout to use Next.js usePathname/useRouter for real URL-based navigation instead of internal state - Add 'use client' directive to view components using useState hooks - Fix 404 on /dashboard by creating proper App Router route structure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9ce42ed5ac
commit
d49200e876
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,5 @@
|
|||
import { AgentPanelPage } from '@/views/agent/AgentPanelPage';
|
||||
|
||||
export default function Agent() {
|
||||
return <AgentPanelPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { ConsumerProtectionPage } from '@/views/analytics/ConsumerProtectionPage';
|
||||
|
||||
export default function ConsumerProtection() {
|
||||
return <ConsumerProtectionPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { CouponAnalyticsPage } from '@/views/analytics/CouponAnalyticsPage';
|
||||
|
||||
export default function CouponAnalytics() {
|
||||
return <CouponAnalyticsPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { MarketMakerPage } from '@/views/analytics/MarketMakerPage';
|
||||
|
||||
export default function MarketMaker() {
|
||||
return <MarketMakerPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { UserAnalyticsPage } from '@/views/analytics/UserAnalyticsPage';
|
||||
|
||||
export default function UserAnalytics() {
|
||||
return <UserAnalyticsPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { ChainMonitorPage } from '@/views/chain/ChainMonitorPage';
|
||||
|
||||
export default function Chain() {
|
||||
return <ChainMonitorPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { IpoReadinessPage } from '@/views/compliance/IpoReadinessPage';
|
||||
|
||||
export default function IpoReadiness() {
|
||||
return <IpoReadinessPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { LicenseManagementPage } from '@/views/compliance/LicenseManagementPage';
|
||||
|
||||
export default function License() {
|
||||
return <LicenseManagementPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { CompliancePage } from '@/views/compliance/CompliancePage';
|
||||
|
||||
export default function Compliance() {
|
||||
return <CompliancePage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { SecFilingPage } from '@/views/compliance/SecFilingPage';
|
||||
|
||||
export default function SecFiling() {
|
||||
return <SecFilingPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { SoxCompliancePage } from '@/views/compliance/SoxCompliancePage';
|
||||
|
||||
export default function Sox() {
|
||||
return <SoxCompliancePage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { TaxCompliancePage } from '@/views/compliance/TaxCompliancePage';
|
||||
|
||||
export default function Tax() {
|
||||
return <TaxCompliancePage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { CouponManagementPage } from '@/views/coupons/CouponManagementPage';
|
||||
|
||||
export default function Coupons() {
|
||||
return <CouponManagementPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { DashboardPage } from '@/views/dashboard/DashboardPage';
|
||||
|
||||
export default function Dashboard() {
|
||||
return <DashboardPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { DisputePage } from '@/views/disputes/DisputePage';
|
||||
|
||||
export default function Disputes() {
|
||||
return <DisputePage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { FinanceManagementPage } from '@/views/finance/FinanceManagementPage';
|
||||
|
||||
export default function Finance() {
|
||||
return <FinanceManagementPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { InsurancePage } from '@/views/insurance/InsurancePage';
|
||||
|
||||
export default function Insurance() {
|
||||
return <InsurancePage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { IssuerManagementPage } from '@/views/issuers/IssuerManagementPage';
|
||||
|
||||
export default function Issuers() {
|
||||
return <IssuerManagementPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { AdminLayout } from '@/layouts/AdminLayout';
|
||||
|
||||
export default function AdminGroupLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <AdminLayout>{children}</AdminLayout>;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { MerchantRedemptionPage } from '@/views/merchant/MerchantRedemptionPage';
|
||||
|
||||
export default function Merchant() {
|
||||
return <MerchantRedemptionPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { ReportsPage } from '@/views/reports/ReportsPage';
|
||||
|
||||
export default function Reports() {
|
||||
return <ReportsPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { RiskCenterPage } from '@/views/risk/RiskCenterPage';
|
||||
|
||||
export default function Risk() {
|
||||
return <RiskCenterPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { SystemManagementPage } from '@/views/system/SystemManagementPage';
|
||||
|
||||
export default function System() {
|
||||
return <SystemManagementPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { TradingMonitorPage } from '@/views/trading/TradingMonitorPage';
|
||||
|
||||
export default function Trading() {
|
||||
return <TradingMonitorPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { UserManagementPage } from '@/views/users/UserManagementPage';
|
||||
|
||||
export default function Users() {
|
||||
return <UserManagementPage />;
|
||||
}
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
|
||||
/**
|
||||
* D. Web管理前端 - 主布局
|
||||
|
|
@ -68,12 +72,43 @@ const navItems: NavItem[] = [
|
|||
],
|
||||
},
|
||||
{ key: 'disputes', icon: '⚖️', label: '争议处理', badge: 8 },
|
||||
{ key: 'coupons', icon: '🎫', label: '券管理' },
|
||||
{ key: 'finance', icon: '💰', label: '财务管理' },
|
||||
{ key: 'chain', icon: '⛓️', label: '链上监控' },
|
||||
{ key: 'reports', icon: '📈', label: '报表中心' },
|
||||
{ key: 'merchant', icon: '🏪', label: '商户核销' },
|
||||
{ key: 'agent', icon: '🤖', label: '代理面板' },
|
||||
{ key: 'insurance', icon: '🛡️', label: '保险管理' },
|
||||
{
|
||||
key: 'analytics', icon: '📊', label: '数据分析',
|
||||
children: [
|
||||
{ key: 'analytics/users', icon: '', label: '用户分析' },
|
||||
{ key: 'analytics/coupons', icon: '', label: '券分析' },
|
||||
{ key: 'analytics/market-maker', icon: '', label: '做市商分析' },
|
||||
{ key: 'analytics/consumer-protection', icon: '', label: '消费者保护' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [activeKey, setActiveKey] = useState('dashboard');
|
||||
const [expandedKeys, setExpandedKeys] = useState<string[]>(['issuers', 'risk']);
|
||||
|
||||
// Derive activeKey from current pathname
|
||||
const activeKey = pathname.replace(/^\//, '') || 'dashboard';
|
||||
|
||||
// Auto-expand parent nav items based on current path
|
||||
const getInitialExpanded = () => {
|
||||
const expanded: string[] = [];
|
||||
navItems.forEach(item => {
|
||||
if (item.children && item.children.some(c => activeKey.startsWith(c.key) || activeKey === item.key)) {
|
||||
expanded.push(item.key);
|
||||
}
|
||||
});
|
||||
return expanded;
|
||||
};
|
||||
const [expandedKeys, setExpandedKeys] = useState<string[]>(getInitialExpanded);
|
||||
|
||||
const toggleExpand = (key: string) => {
|
||||
setExpandedKeys(prev =>
|
||||
|
|
@ -132,7 +167,13 @@ export const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children
|
|||
{navItems.map(item => (
|
||||
<div key={item.key}>
|
||||
<button
|
||||
onClick={() => item.children ? toggleExpand(item.key) : setActiveKey(item.key)}
|
||||
onClick={() => {
|
||||
if (item.children) {
|
||||
toggleExpand(item.key);
|
||||
} else {
|
||||
router.push('/' + item.key);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
|
|
@ -140,8 +181,8 @@ export const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children
|
|||
padding: '10px 12px',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
background: activeKey === item.key ? 'var(--color-primary-surface)' : 'transparent',
|
||||
color: activeKey === item.key ? 'var(--color-primary)' : 'var(--color-text-secondary)',
|
||||
background: activeKey === item.key || activeKey.startsWith(item.key + '/') ? 'var(--color-primary-surface)' : 'transparent',
|
||||
color: activeKey === item.key || activeKey.startsWith(item.key + '/') ? 'var(--color-primary)' : 'var(--color-text-secondary)',
|
||||
cursor: 'pointer',
|
||||
font: 'var(--text-label)',
|
||||
marginBottom: 2,
|
||||
|
|
@ -178,7 +219,7 @@ export const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children
|
|||
{item.children.map(sub => (
|
||||
<button
|
||||
key={sub.key}
|
||||
onClick={() => setActiveKey(sub.key)}
|
||||
onClick={() => router.push('/' + sub.key)}
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue