Initial CMPP frontend prototype
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
Activity,
|
||||
BarChart3,
|
||||
Building2,
|
||||
FileCheck2,
|
||||
Gauge,
|
||||
Hash,
|
||||
ImageIcon,
|
||||
Layers3,
|
||||
MessageSquare,
|
||||
Phone,
|
||||
TrendingUp,
|
||||
RadioTower,
|
||||
ReceiptText,
|
||||
Send,
|
||||
Settings,
|
||||
Shield,
|
||||
ShieldOff,
|
||||
ShieldCheck,
|
||||
Users,
|
||||
UserX,
|
||||
} from 'lucide-react';
|
||||
import { AppShell } from '@/layouts/AppShell';
|
||||
|
||||
export function AdminLayout() {
|
||||
return (
|
||||
<AppShell
|
||||
title="CMPP 运营端"
|
||||
subtitle="平台运营管理中心"
|
||||
workspaceName="平台运营工作区"
|
||||
userName="运营"
|
||||
userRole="平台管理员"
|
||||
navSections={[
|
||||
{
|
||||
title: '运营概览',
|
||||
icon: Gauge,
|
||||
items: [
|
||||
{ label: '运营看板', to: '/admin', icon: Gauge },
|
||||
{ label: '发送监控', to: '/admin/monitor', icon: Activity },
|
||||
{ label: '数据统计', to: '/admin/analytics', icon: BarChart3 },
|
||||
{ label: '客户管理', to: '/admin/customers', icon: Building2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '审核中心',
|
||||
icon: FileCheck2,
|
||||
items: [
|
||||
{ label: '企业认证审核', to: '/admin/enterprise-audit', icon: ShieldCheck },
|
||||
{ label: '短信审核', to: '/admin/sms-audit', icon: MessageSquare },
|
||||
{ label: '短信模板审核', to: '/admin/templates', icon: FileCheck2 },
|
||||
{ label: '彩信模板审核', to: '/admin/signatures', icon: ImageIcon },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '通道管理',
|
||||
icon: RadioTower,
|
||||
items: [
|
||||
{ label: '短信通道管理', to: '/admin/channels', icon: RadioTower },
|
||||
{ label: '彩信通道管理', to: '/admin/mms-channels', icon: ImageIcon },
|
||||
{ label: '短信通道组管理', to: '/admin/channel-groups', icon: Layers3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '发送任务',
|
||||
icon: Send,
|
||||
items: [
|
||||
{ label: '短信任务进度', to: '/admin/sms-task-progress', icon: TrendingUp },
|
||||
{ label: '彩信任务进度', to: '/admin/mms-task-progress', icon: BarChart3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据详单',
|
||||
icon: ReceiptText,
|
||||
items: [
|
||||
{ label: '短信记录', to: '/admin/sms-records', icon: MessageSquare },
|
||||
{ label: '彩信记录', to: '/admin/mms-records', icon: ImageIcon },
|
||||
{ label: '短信上行记录', to: '/admin/sms-uplink-records', icon: MessageSquare },
|
||||
{ label: '充值记录', to: '/admin/recharge-records', icon: ReceiptText },
|
||||
{ label: '账单流水', to: '/admin/billing', icon: ReceiptText },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '安全控制',
|
||||
icon: Shield,
|
||||
items: [
|
||||
{ label: '企业黑名单', to: '/admin/enterprise-blacklist', icon: UserX },
|
||||
{ label: '全局黑名单', to: '/admin/global-blacklist', icon: ShieldOff },
|
||||
{ label: '敏感词管理', to: '/admin/sensitive-words', icon: Shield },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '系统管理',
|
||||
icon: Settings,
|
||||
items: [
|
||||
{ label: '用户管理', to: '/admin/users', icon: Users },
|
||||
{ label: '手机号段库', to: '/admin/phone-segments', icon: Phone },
|
||||
{ label: '引流信息字段库', to: '/admin/drainage-fields', icon: Hash },
|
||||
{ label: '系统配置', to: '/admin/settings', icon: Settings },
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import type { ComponentType } from 'react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Bell,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
CircleHelp,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
Search,
|
||||
Sparkles,
|
||||
} from 'lucide-react';
|
||||
import { NavLink, Outlet } from 'react-router-dom';
|
||||
|
||||
export type ShellNavItem = {
|
||||
label: string;
|
||||
to: string;
|
||||
icon: ComponentType<{ size?: number; strokeWidth?: number }>;
|
||||
};
|
||||
|
||||
export type ShellNavSection = {
|
||||
title: string;
|
||||
icon?: ComponentType<{ size?: number; strokeWidth?: number }>;
|
||||
items: ShellNavItem[];
|
||||
};
|
||||
|
||||
type AppShellProps = {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
workspaceName: string;
|
||||
userName: string;
|
||||
userRole: string;
|
||||
navSections: ShellNavSection[];
|
||||
};
|
||||
|
||||
export function AppShell({
|
||||
title,
|
||||
subtitle,
|
||||
workspaceName,
|
||||
userName,
|
||||
userRole,
|
||||
navSections,
|
||||
}: AppShellProps) {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [closedSections, setClosedSections] = useState<Record<string, boolean>>({});
|
||||
const ToggleIcon = collapsed ? PanelLeftOpen : PanelLeftClose;
|
||||
|
||||
return (
|
||||
<div className={['app-shell', collapsed ? 'app-shell--collapsed' : ''].filter(Boolean).join(' ')}>
|
||||
<aside className="sidebar">
|
||||
<div className="brand-block">
|
||||
<div className="brand-icon">
|
||||
<Sparkles size={18} />
|
||||
</div>
|
||||
<div className="brand-copy">
|
||||
<div className="brand-mark">{title}</div>
|
||||
<div className="brand-subtitle">{subtitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="side-nav" aria-label="主导航">
|
||||
{navSections.map((section) => (
|
||||
<section className="side-nav-section" key={section.title}>
|
||||
{section.icon ? (
|
||||
<button
|
||||
aria-expanded={!closedSections[section.title]}
|
||||
className="side-nav-group-toggle"
|
||||
onClick={() => setClosedSections((current) => ({ ...current, [section.title]: !current[section.title] }))}
|
||||
type="button"
|
||||
>
|
||||
<section.icon size={18} strokeWidth={2.1} />
|
||||
<span className="side-nav-group-label">{section.title}</span>
|
||||
{closedSections[section.title] ? (
|
||||
<ChevronRight className="side-nav-group-chevron" size={16} />
|
||||
) : (
|
||||
<ChevronDown className="side-nav-group-chevron" size={16} />
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
<p>{section.title}</p>
|
||||
)}
|
||||
<div className={['side-nav-list', closedSections[section.title] ? 'side-nav-list--closed' : ''].join(' ')}>
|
||||
{section.items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<NavLink key={item.to} to={item.to} end title={item.label}>
|
||||
<Icon size={17} strokeWidth={2.1} />
|
||||
<span>{item.label}</span>
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="sidebar-footer">
|
||||
<span>当前空间</span>
|
||||
<strong>{workspaceName}</strong>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="main-area">
|
||||
<header className="topbar">
|
||||
<div className="topbar-left">
|
||||
<button
|
||||
className="icon-button"
|
||||
onClick={() => setCollapsed((value) => !value)}
|
||||
type="button"
|
||||
aria-label={collapsed ? '展开导航' : '收起导航'}
|
||||
>
|
||||
<ToggleIcon size={18} />
|
||||
</button>
|
||||
<label className="topbar-search">
|
||||
<Search size={17} />
|
||||
<input placeholder="搜索模板、客户、发送记录" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="topbar-actions">
|
||||
<button className="icon-button" type="button" aria-label="帮助中心">
|
||||
<CircleHelp size={18} />
|
||||
</button>
|
||||
<button className="icon-button has-dot" type="button" aria-label="通知">
|
||||
<Bell size={18} />
|
||||
</button>
|
||||
<button className="user-menu" type="button">
|
||||
<span className="user-avatar">{userName.slice(0, 1)}</span>
|
||||
<span>
|
||||
<strong>{userName}</strong>
|
||||
<small>{userRole}</small>
|
||||
</span>
|
||||
<ChevronDown size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="page-content">
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
BadgeDollarSign,
|
||||
ClipboardList,
|
||||
FileText,
|
||||
Home,
|
||||
ImageIcon,
|
||||
MessageSquareText,
|
||||
PenLine,
|
||||
ReceiptText,
|
||||
Settings,
|
||||
ShieldCheck,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import { AppShell } from '@/layouts/AppShell';
|
||||
|
||||
export function ClientLayout() {
|
||||
return (
|
||||
<AppShell
|
||||
title="CMPP 客户端"
|
||||
subtitle="短信服务控制台"
|
||||
workspaceName="华东一区客户空间"
|
||||
userName="赵先生"
|
||||
userRole="企业管理员"
|
||||
navSections={[
|
||||
{
|
||||
title: '概览',
|
||||
items: [{ label: '工作台', to: '/client', icon: Home }],
|
||||
},
|
||||
{
|
||||
title: '短信业务',
|
||||
items: [
|
||||
{ label: '短信发送', to: '/client/send', icon: MessageSquareText },
|
||||
{ label: '查看批量任务', to: '/client/batch-tasks', icon: ClipboardList },
|
||||
{ label: '短信发送详情', to: '/client/send-detail', icon: ClipboardList },
|
||||
{ label: '查看上行短信', to: '/client/uplink-messages', icon: MessageSquareText },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '短信基础配置',
|
||||
items: [
|
||||
{ label: '短信应用', to: '/client/applications', icon: FileText },
|
||||
{ label: '模板管理', to: '/client/templates', icon: FileText },
|
||||
{ label: '签名与引流信息', to: '/client/signatures', icon: PenLine },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '彩信服务',
|
||||
items: [
|
||||
{ label: '签名报备', to: '/client/mms-signatures', icon: PenLine },
|
||||
{ label: '彩信模板管理', to: '/client/mms-templates', icon: ImageIcon },
|
||||
{ label: '发送彩信', to: '/client/mms-send', icon: MessageSquareText },
|
||||
{ label: '查看批量任务', to: '/client/mms-batch-tasks', icon: ClipboardList },
|
||||
{ label: '彩信发送详情', to: '/client/mms-send-detail', icon: ClipboardList },
|
||||
{ label: '查看上行彩信', to: '/client/mms-uplink-messages', icon: ImageIcon },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '账户',
|
||||
items: [
|
||||
{ label: '充值套餐', to: '/client/billing', icon: BadgeDollarSign },
|
||||
{ label: '账单流水', to: '/client/invoices', icon: ReceiptText },
|
||||
{ label: '账号设置', to: '/client/settings', icon: Settings },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '系统管理',
|
||||
items: [
|
||||
{ label: '企业认证', to: '/client/enterprise-auth', icon: ShieldCheck },
|
||||
{ label: '用户管理', to: '/client/users', icon: Users },
|
||||
{ label: '系统日志', to: '/client/system-logs', icon: FileText },
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user