Initial CMPP frontend prototype
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { CalendarDays, Download, FileText, Search } from 'lucide-react';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
Table,
|
||||
Tag,
|
||||
type TableColumn,
|
||||
} from '@/components/ui';
|
||||
|
||||
type LogLevel = 'info' | 'success' | 'warning' | 'error';
|
||||
|
||||
type SystemLog = {
|
||||
id: string;
|
||||
time: string;
|
||||
level: LogLevel;
|
||||
module: string;
|
||||
operator: string;
|
||||
action: string;
|
||||
detail: string;
|
||||
ip: string;
|
||||
};
|
||||
|
||||
const levelLabelMap: Record<LogLevel, string> = {
|
||||
info: '信息',
|
||||
success: '成功',
|
||||
warning: '警告',
|
||||
error: '错误',
|
||||
};
|
||||
|
||||
const levelToneMap: Record<LogLevel, 'info' | 'success' | 'warning' | 'danger'> = {
|
||||
info: 'info',
|
||||
success: 'success',
|
||||
warning: 'warning',
|
||||
error: 'danger',
|
||||
};
|
||||
|
||||
const logsSeed: SystemLog[] = [
|
||||
{ id: 'LOG001', time: '2026-03-17 14:35:22', level: 'info', module: '用户管理', operator: '张三', action: '创建用户', detail: '创建用户账号:李四(lisi@example.com)', ip: '192.168.1.100' },
|
||||
{ id: 'LOG002', time: '2026-03-17 14:20:15', level: 'success', module: '短信服务', operator: '李四', action: '发送短信', detail: '批量发送短信至500个号码,发送成功', ip: '192.168.1.101' },
|
||||
{ id: 'LOG003', time: '2026-03-17 13:45:33', level: 'warning', module: '彩信服务', operator: '王五', action: '模板审核', detail: '彩信模板“春节祝福”审核未通过,原因:内容包含敏感词', ip: '192.168.1.102' },
|
||||
{ id: 'LOG004', time: '2026-03-17 12:10:08', level: 'error', module: '系统管理', operator: '赵六', action: '登录失败', detail: '用户登录失败,错误:密码错误(连续3次)', ip: '192.168.1.103' },
|
||||
{ id: 'LOG005', time: '2026-03-17 11:30:45', level: 'info', module: '用户管理', operator: '张三', action: '修改权限', detail: '修改用户“孙七”的角色:普通用户 → 管理员', ip: '192.168.1.100' },
|
||||
{ id: 'LOG006', time: '2026-03-17 10:15:20', level: 'success', module: '短信服务', operator: '李四', action: '签名审核', detail: '短信签名“优品商城”审核通过', ip: '192.168.1.101' },
|
||||
{ id: 'LOG007', time: '2026-03-17 09:50:12', level: 'info', module: '彩信服务', operator: '王五', action: '创建模板', detail: '创建彩信模板“新品发布”(模板ID:MMS_1a2b3c4d)', ip: '192.168.1.102' },
|
||||
{ id: 'LOG008', time: '2026-03-17 09:05:33', level: 'error', module: '短信服务', operator: '李四', action: '发送失败', detail: '短信发送失败,错误:余额不足', ip: '192.168.1.101' },
|
||||
{ id: 'LOG009', time: '2026-03-17 08:40:18', level: 'warning', module: '系统管理', operator: 'system', action: '系统告警', detail: '系统磁盘使用率超过80%,当前使用率:85%', ip: '127.0.0.1' },
|
||||
];
|
||||
|
||||
export function ClientSystemLogsPage() {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [level, setLevel] = useState('all');
|
||||
const [module, setModule] = useState('all');
|
||||
const [range, setRange] = useState('today');
|
||||
|
||||
const moduleOptions = useMemo(() => {
|
||||
const modules = Array.from(new Set(logsSeed.map((item) => item.module)));
|
||||
return [{ label: '全部模块', value: 'all' }, ...modules.map((item) => ({ label: item, value: item }))];
|
||||
}, []);
|
||||
|
||||
const filteredLogs = logsSeed.filter((item) => {
|
||||
const target = `${item.operator} ${item.action} ${item.detail}`;
|
||||
const matchesKeyword = !keyword || target.toLowerCase().includes(keyword.toLowerCase());
|
||||
const matchesLevel = level === 'all' || item.level === level;
|
||||
const matchesModule = module === 'all' || item.module === module;
|
||||
return matchesKeyword && matchesLevel && matchesModule;
|
||||
});
|
||||
|
||||
const columns = useMemo<Array<TableColumn<SystemLog>>>(() => [
|
||||
{ key: 'time', title: '时间', width: '190px', render: (record) => <span className="muted">{record.time}</span> },
|
||||
{ key: 'level', title: '级别', width: '110px', render: (record) => <Tag tone={levelToneMap[record.level]}>{levelLabelMap[record.level]}</Tag> },
|
||||
{ key: 'module', title: '模块', width: '150px', render: (record) => <strong>{record.module}</strong> },
|
||||
{ key: 'operator', title: '操作人', width: '130px', render: (record) => <strong>{record.operator}</strong> },
|
||||
{ key: 'action', title: '操作', width: '160px', render: (record) => <strong>{record.action}</strong> },
|
||||
{ key: 'detail', title: '详情', render: (record) => <span className="system-log-detail">{record.detail}</span> },
|
||||
{ key: 'ip', title: 'IP地址', width: '150px', render: (record) => <span className="muted">{record.ip}</span> },
|
||||
], []);
|
||||
|
||||
return (
|
||||
<section className="page-stack system-page">
|
||||
<div className="system-page-toolbar">
|
||||
<div className="sms-send-title">
|
||||
<span className="sms-send-title__icon"><FileText size={22} /></span>
|
||||
<h1>系统日志</h1>
|
||||
</div>
|
||||
<Button icon={<Download size={17} />} variant="secondary">导出日志</Button>
|
||||
</div>
|
||||
|
||||
<div className="system-log-filters">
|
||||
<Input
|
||||
onChange={(event) => setKeyword(event.target.value)}
|
||||
placeholder="搜索操作人、操作或详情"
|
||||
prefix={<Search size={16} />}
|
||||
value={keyword}
|
||||
/>
|
||||
<Select
|
||||
onChange={(event) => setLevel(event.target.value)}
|
||||
options={[
|
||||
{ label: '全部级别', value: 'all' },
|
||||
{ label: '信息', value: 'info' },
|
||||
{ label: '成功', value: 'success' },
|
||||
{ label: '警告', value: 'warning' },
|
||||
{ label: '错误', value: 'error' },
|
||||
]}
|
||||
value={level}
|
||||
/>
|
||||
<Select onChange={(event) => setModule(event.target.value)} options={moduleOptions} value={module} />
|
||||
</div>
|
||||
|
||||
<div className="system-log-range">
|
||||
<span><CalendarDays size={18} /> 时间范围:</span>
|
||||
{[
|
||||
{ label: '今天', value: 'today' },
|
||||
{ label: '近7天', value: '7d' },
|
||||
{ label: '近30天', value: '30d' },
|
||||
{ label: '全部', value: 'all' },
|
||||
].map((item) => (
|
||||
<Button
|
||||
key={item.value}
|
||||
onClick={() => setRange(item.value)}
|
||||
size="sm"
|
||||
variant={range === item.value ? 'primary' : 'secondary'}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="surface system-table-card">
|
||||
<Table columns={columns} data={filteredLogs} emptyText="暂无系统日志" rowKey="id" />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user