前端重构

This commit is contained in:
Yvan 2026-01-08 14:11:10 +08:00
parent cf16ab6448
commit b770c4a3b6
12 changed files with 912 additions and 329 deletions

View File

@ -136,7 +136,7 @@ const AboutPage: React.FC = () => {
{/* 头部信息 */}
<Card className={styles.headerCard} variant="borderless">
<div className={styles.logoWrapper}>
<Logo size={80} />
<Logo size="large" />
</div>
<Title level={2} className={styles.title}>
Kratos Admin

View File

@ -1,36 +1,57 @@
/**
* KRA - Dashboard Banner Component
* GVA banner.vue -
*/
import React from 'react';
import { Card } from 'antd';
import Logo from '@/components/Logo/Logo';
import { Carousel } from 'antd';
interface BannerItem {
img: string;
link: string;
}
const banners: BannerItem[] = [
{
img: 'https://go-kratos.dev/img/logo.svg',
link: 'https://go-kratos.dev/'
},
{
img: 'https://avatars.githubusercontent.com/u/68029786?s=200&v=4',
link: 'https://github.com/go-kratos/kratos'
},
];
const Banner: React.FC = () => {
const openLink = (link: string) => {
window.open(link, '_blank');
};
return (
<Card
className="kra-dashboard-card"
styles={{ body: { padding: 0, height: '160px', overflow: 'hidden' } }}
variant="borderless"
>
<div style={{
height: '100%',
background: 'linear-gradient(135deg, #1890ff 0%, #722ed1 100%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
padding: '24px',
}}>
<Logo size="large" />
<h3 style={{ margin: '12px 0 4px', fontSize: '18px', fontWeight: 600 }}>
Kratos Admin
</h3>
<p style={{ margin: 0, fontSize: '12px', opacity: 0.85 }}>
</p>
</div>
</Card>
<div style={{ marginTop: '-8px', height: '160px', overflow: 'hidden' }}>
<Carousel autoplay>
{banners.map((item, index) => (
<div key={index}>
<div
onClick={() => openLink(item.link)}
style={{
height: '160px',
cursor: 'pointer',
background: 'linear-gradient(135deg, #1890ff 0%, #722ed1 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img
src={item.img}
alt="banner"
style={{ maxHeight: '80px', maxWidth: '80%', objectFit: 'contain' }}
/>
</div>
</div>
))}
</Carousel>
</div>
);
};

View File

@ -0,0 +1,86 @@
/**
* KRA - Mini Line Chart Component
* 线
*/
import React, { useEffect, useRef } from 'react';
interface MiniLineChartProps {
data: number[];
color?: string;
}
const MiniLineChart: React.FC<MiniLineChartProps> = ({ data, color = '#1890ff' }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const width = canvas.width;
const height = canvas.height;
const padding = 10;
// 清空画布
ctx.clearRect(0, 0, width, height);
if (data.length < 2) return;
const max = Math.max(...data);
const min = Math.min(...data);
const range = max - min || 1;
const stepX = (width - padding * 2) / (data.length - 1);
// 绘制渐变区域
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, `${color}40`);
gradient.addColorStop(1, `${color}00`);
ctx.beginPath();
ctx.moveTo(padding, height - padding);
data.forEach((value, index) => {
const x = padding + index * stepX;
const y = height - padding - ((value - min) / range) * (height - padding * 2);
if (index === 0) {
ctx.lineTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.lineTo(padding + (data.length - 1) * stepX, height - padding);
ctx.closePath();
ctx.fillStyle = gradient;
ctx.fill();
// 绘制折线
ctx.beginPath();
data.forEach((value, index) => {
const x = padding + index * stepX;
const y = height - padding - ((value - min) / range) * (height - padding * 2);
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.stroke();
}, [data, color]);
return (
<canvas
ref={canvasRef}
width={150}
height={80}
style={{ width: '100%', height: '100%' }}
/>
);
};
export default MiniLineChart;

View File

@ -1,61 +1,100 @@
/**
* KRA - Dashboard Notice Component
* GVA notice.vue
*/
import React from 'react';
import { Card, List, Tag, Typography } from 'antd';
import { NotificationOutlined } from '@ant-design/icons';
const { Text } = Typography;
import { Card, Tag, Tooltip } from 'antd';
interface NoticeItem {
id: number;
type: 'primary' | 'success' | 'warning' | 'danger' | 'info';
typeTitle: string;
title: string;
type: 'info' | 'warning' | 'success';
time: string;
}
const notices: NoticeItem[] = [
{ id: 1, title: '系统将于今晚进行维护升级', type: 'warning', time: '2小时前' },
{ id: 2, title: '新版本 v1.0.0 已发布', type: 'success', time: '1天前' },
{ id: 3, title: '欢迎使用 Kratos Admin', type: 'info', time: '3天前' },
{
type: 'primary',
typeTitle: '公告',
title: '欢迎使用 Kratos Admin 管理系统。'
},
{
type: 'success',
typeTitle: '通知',
title: '系统已完成最新版本升级。'
},
{
type: 'warning',
typeTitle: '警告',
title: '请定期备份重要数据。'
},
{
type: 'danger',
typeTitle: '违规',
title: '请勿在生产环境使用默认密码。'
},
{
type: 'info',
typeTitle: '信息',
title: '感谢您对开源事业的支持。'
},
{
type: 'primary',
typeTitle: '公告',
title: '让创意更有价值。'
},
{
type: 'success',
typeTitle: '通知',
title: '让劳动更有意义。'
},
{
type: 'warning',
typeTitle: '警告',
title: '让思维更有深度。'
},
{
type: 'danger',
typeTitle: '错误',
title: '让生活更有趣味。'
},
{
type: 'info',
typeTitle: '信息',
title: '让公司更有活力。'
}
];
const typeColors = {
info: 'blue',
warning: 'orange',
const typeColors: Record<string, string> = {
primary: 'blue',
success: 'green',
warning: 'orange',
danger: 'red',
info: 'default',
};
const Notice: React.FC = () => {
return (
<Card
title="公告"
className="kra-dashboard-card"
variant="borderless"
extra={<a href="#"></a>}
>
<List
size="small"
dataSource={notices}
renderItem={(item) => (
<List.Item style={{ padding: '8px 0' }}>
<div style={{ width: '100%' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
<NotificationOutlined style={{ color: '#999', fontSize: '12px' }} />
<Text ellipsis style={{ flex: 1, fontSize: '13px' }}>
{item.title}
</Text>
<Card title="公告" className="kra-dashboard-card" variant="borderless" extra={<a href="#"></a>}>
<div style={{ maxHeight: '200px', overflow: 'auto' }}>
{notices.map((item, index) => (
<div key={index} style={{ display: 'flex', alignItems: 'center', marginBottom: '6px', gap: '12px' }}>
<Tag color={typeColors[item.type]} style={{ margin: 0, flexShrink: 0 }}>
{item.typeTitle}
</Tag>
<Tooltip title={item.title} placement="top">
<div style={{
fontSize: '12px',
color: '#666',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}>
{item.title}
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Tag color={typeColors[item.type]} style={{ margin: 0 }}>
{item.type === 'info' ? '通知' : item.type === 'warning' ? '警告' : '成功'}
</Tag>
<Text type="secondary" style={{ fontSize: '12px' }}>{item.time}</Text>
</div>
</div>
</List.Item>
)}
/>
</Tooltip>
</div>
))}
</div>
</Card>
);
};

View File

@ -1,133 +1,98 @@
/**
* KRA - Dashboard Plugin Table Component
* GVA pluginTable.vue -
*/
import React from 'react';
import { Card, Table, Tag, Space, Button } from 'antd';
import { Card, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { GithubOutlined, LinkOutlined } from '@ant-design/icons';
interface PluginItem {
key: string;
name: string;
description: string;
version: string;
status: 'active' | 'inactive' | 'beta';
author: string;
ranking: number;
title: string;
click_num: number;
hot: number;
link: string;
}
const plugins: PluginItem[] = [
const tableData: PluginItem[] = [
{
key: '1',
name: '公告管理',
description: '系统公告发布与管理',
version: 'v1.0.0',
status: 'active',
author: 'KRA Team',
ranking: 1,
title: '组织管理插件:更方便管理组织,分配资源权限。',
click_num: 523,
hot: 263,
link: 'https://go-kratos.dev/'
},
{
key: '2',
name: '邮件服务',
description: '邮件配置与发送服务',
version: 'v1.0.0',
status: 'active',
author: 'KRA Team',
ranking: 2,
title: 'Kubernetes容器管理Kubernetes 原生资源管理提供炫酷的YAML编辑Pod终端。',
click_num: 416,
hot: 223,
link: 'https://go-kratos.dev/'
},
{
key: '3',
name: '代码生成器',
description: '自动生成 CRUD 代码',
version: 'v0.9.0',
status: 'beta',
author: 'KRA Team',
ranking: 3,
title: '定时任务配置化管理:本插件用于对系统内部的定时任务进行配置化管理。',
click_num: 337,
hot: 176,
link: 'https://go-kratos.dev/'
},
{
key: '4',
name: '文件管理',
description: '文件上传与管理',
version: 'v1.0.0',
status: 'active',
author: 'KRA Team',
ranking: 4,
title: '官网CMS系统基于Kratos开发的企业官网类CMS系统。',
click_num: 292,
hot: 145,
link: 'https://go-kratos.dev/'
},
{
ranking: 5,
title: '微信支付插件:提供扫码支付功能(需自行对接业务)。',
click_num: 173,
hot: 110,
link: 'https://go-kratos.dev/'
}
];
const statusColors = {
active: 'green',
inactive: 'default',
beta: 'orange',
};
const statusLabels = {
active: '已启用',
inactive: '未启用',
beta: '测试中',
};
const columns: ColumnsType<PluginItem> = [
{
title: '插件名称',
dataIndex: 'name',
key: 'name',
width: 120,
title: '排名',
dataIndex: 'ranking',
width: 80,
align: 'center',
},
{
title: '描述',
dataIndex: 'description',
key: 'description',
title: '插件标题',
dataIndex: 'title',
ellipsis: true,
},
{
title: '版本',
dataIndex: 'version',
key: 'version',
width: 80,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 80,
render: (status: keyof typeof statusColors) => (
<Tag color={statusColors[status]}>{statusLabels[status]}</Tag>
render: (text, record) => (
<a
href={record.link}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#1890ff' }}
>
{text}
</a>
),
},
{
title: '作者',
dataIndex: 'author',
key: 'author',
title: '关注度',
dataIndex: 'click_num',
width: 100,
},
{
title: '热度值',
dataIndex: 'hot',
width: 100,
},
];
const PluginTable: React.FC = () => {
return (
<Card
title="最新插件"
className="kra-dashboard-card"
variant="borderless"
extra={
<Space>
<Button
type="link"
icon={<GithubOutlined />}
href="https://github.com/go-kratos/kratos"
target="_blank"
>
GitHub
</Button>
<Button
type="link"
icon={<LinkOutlined />}
href="https://go-kratos.dev/"
target="_blank"
>
</Button>
</Space>
}
>
<Card title="最新插件" className="kra-dashboard-card" variant="borderless">
<Table
columns={columns}
dataSource={plugins}
dataSource={tableData}
rowKey="ranking"
pagination={false}
size="small"
/>

View File

@ -1,83 +1,100 @@
/**
* KRA - Dashboard Quick Links Component
* GVA quickLinks.vue
*/
import React from 'react';
import { Card, Row, Col } from 'antd';
import {
UserOutlined,
SettingOutlined,
FileTextOutlined,
DatabaseOutlined,
ApiOutlined,
MenuOutlined,
LinkOutlined,
SafetyOutlined,
UserOutlined,
FolderOutlined,
CodeOutlined,
ReadOutlined,
AppstoreOutlined,
} from '@ant-design/icons';
import { history } from '@umijs/max';
interface QuickLink {
title: string;
interface ShortcutItem {
icon: React.ReactNode;
title: string;
path: string;
color: string;
isExternal?: boolean;
}
const links: QuickLink[] = [
{ title: '用户管理', icon: <UserOutlined />, path: '/system/user', color: '#1890ff' },
{ title: '角色管理', icon: <SafetyOutlined />, path: '/system/authority', color: '#52c41a' },
{ title: '菜单管理', icon: <FileTextOutlined />, path: '/system/menu', color: '#faad14' },
{ title: 'API管理', icon: <ApiOutlined />, path: '/system/api', color: '#722ed1' },
{ title: '字典管理', icon: <DatabaseOutlined />, path: '/system/dictionary', color: '#eb2f96' },
{ title: '系统配置', icon: <SettingOutlined />, path: '/systemTools/system', color: '#13c2c2' },
// 内部快捷功能
const shortcuts: ShortcutItem[] = [
{ icon: <MenuOutlined />, title: '菜单管理', path: '/system/menu' },
{ icon: <LinkOutlined />, title: 'API管理', path: '/system/api' },
{ icon: <SafetyOutlined />, title: '角色管理', path: '/system/authority' },
{ icon: <UserOutlined />, title: '用户管理', path: '/system/user' },
{ icon: <FolderOutlined />, title: '自动化包', path: '/systemTools/autoCodeAdmin' },
{ icon: <CodeOutlined />, title: '自动代码', path: '/systemTools/autoCode' },
];
// 外部链接
const externalLinks: ShortcutItem[] = [
{ icon: <ReadOutlined />, title: 'Kratos文档', path: 'https://go-kratos.dev/', isExternal: true },
{ icon: <AppstoreOutlined />, title: 'GitHub', path: 'https://github.com/go-kratos/kratos', isExternal: true },
];
const QuickLinks: React.FC = () => {
const handleClick = (path: string) => {
history.push(path);
const handleClick = (item: ShortcutItem) => {
if (item.isExternal) {
window.open(item.path, '_blank');
} else {
history.push(item.path);
}
};
const renderItem = (item: ShortcutItem, index: number) => (
<Col span={8} key={index}>
<div
onClick={() => handleClick(item)}
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginBottom: '12px',
cursor: 'pointer',
}}
className="quick-link-item"
>
<div
style={{
width: '32px',
height: '32px',
borderRadius: '4px',
background: '#f0f0f0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'all 0.2s',
}}
className="quick-link-icon"
>
{item.icon}
</div>
<div style={{ fontSize: '12px', marginTop: '8px', color: '#666' }}>
{item.title}
</div>
</div>
</Col>
);
return (
<Card
title="快捷功能"
className="kra-dashboard-card"
variant="borderless"
>
<Row gutter={[12, 12]}>
{links.map((link, index) => (
<Col span={12} key={index}>
<div
onClick={() => handleClick(link.path)}
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '12px 8px',
borderRadius: '8px',
cursor: 'pointer',
transition: 'all 0.2s',
background: '#fafafa',
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = `${link.color}10`;
e.currentTarget.style.transform = 'translateY(-2px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = '#fafafa';
e.currentTarget.style.transform = 'translateY(0)';
}}
>
<div
style={{
fontSize: '20px',
color: link.color,
marginBottom: '4px',
}}
>
{link.icon}
</div>
<span style={{ fontSize: '12px', color: '#666' }}>{link.title}</span>
</div>
</Col>
))}
</Row>
<Card title="快捷功能" className="kra-dashboard-card" variant="borderless" extra={<a href="#"></a>}>
<div style={{ marginTop: '32px' }}>
<Row>{shortcuts.map(renderItem)}</Row>
<Row style={{ marginTop: '32px' }}>{externalLinks.map(renderItem)}</Row>
</div>
<style>{`
.quick-link-item:hover .quick-link-icon {
background: #1890ff !important;
color: #fff;
}
`}</style>
</Card>
);
};

View File

@ -1,36 +1,33 @@
/**
* KRA - Dashboard Statistics Cards
* GVA charts.vue -
*/
import React from 'react';
import { Row, Col, Card, Statistic } from 'antd';
import { UserOutlined, TeamOutlined, CheckCircleOutlined } from '@ant-design/icons';
import { ArrowUpOutlined } from '@ant-design/icons';
import MiniLineChart from './MiniLineChart';
interface StatItem {
title: string;
value: number;
icon: React.ReactNode;
color: string;
suffix?: string;
data: number[];
}
const stats: StatItem[] = [
{
title: '访问人数',
value: 12580,
icon: <UserOutlined />,
color: '#1890ff',
value: 268500,
data: [12, 22, 32, 45, 32, 78, 89, 92],
},
{
title: '新增客户',
value: 368,
icon: <TeamOutlined />,
color: '#52c41a',
value: 268500,
data: [1, 2, 43, 5, 67, 78, 89, 12],
},
{
title: '解决数量',
value: 1024,
icon: <CheckCircleOutlined />,
color: '#722ed1',
value: 268500,
data: [12, 22, 32, 45, 32, 78, 89, 92],
},
];
@ -40,28 +37,19 @@ const StatCards: React.FC = () => {
{stats.map((stat, index) => (
<Col xs={24} sm={8} key={index}>
<Card className="kra-dashboard-card" variant="borderless">
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
<div
style={{
width: '48px',
height: '48px',
borderRadius: '8px',
background: `${stat.color}15`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '24px',
color: stat.color,
}}
>
{stat.icon}
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<div style={{ fontWeight: 'bold', marginBottom: '8px' }}>{stat.title}</div>
<div style={{ marginTop: '16px' }}>
<Statistic value={stat.value} />
</div>
<div style={{ marginTop: '8px', color: '#52c41a', fontSize: '14px', fontWeight: 'bold' }}>
+80% <ArrowUpOutlined />
</div>
</div>
<div style={{ width: '50%', height: '80px', position: 'relative' }}>
<MiniLineChart data={stat.data} />
</div>
<Statistic
title={stat.title}
value={stat.value}
suffix={stat.suffix}
styles={{ content: { color: stat.color } }}
/>
</div>
</Card>
</Col>

View File

@ -0,0 +1,87 @@
/**
* KRA - Dashboard Update Table Component
* GVA table.vue -
*/
import React from 'react';
import { Card, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
interface UpdateItem {
ranking: number;
title: string;
click_num: number;
hot: number;
}
const tableData: UpdateItem[] = [
{
ranking: 1,
title: '更简洁的使用界面,更快速的操作体验',
click_num: 523,
hot: 263
},
{
ranking: 2,
title: '更优质的服务,更便捷的使用体验',
click_num: 416,
hot: 223
},
{
ranking: 3,
title: '更快速的创意实现,更高效的工作效率',
click_num: 337,
hot: 176
},
{
ranking: 4,
title: '更多的创意资源,更多的创意灵感',
click_num: 292,
hot: 145
},
{
ranking: 5,
title: '更合理的代码结构,更清晰的代码逻辑',
click_num: 173,
hot: 110
}
];
const columns: ColumnsType<UpdateItem> = [
{
title: '排名',
dataIndex: 'ranking',
width: 80,
align: 'center',
},
{
title: '内容标题',
dataIndex: 'title',
ellipsis: true,
},
{
title: '关注度',
dataIndex: 'click_num',
width: 100,
},
{
title: '热度值',
dataIndex: 'hot',
width: 100,
},
];
const UpdateTable: React.FC = () => {
return (
<Card title="最新更新" className="kra-dashboard-card" variant="borderless">
<Table
columns={columns}
dataSource={tableData}
rowKey="ranking"
pagination={false}
size="small"
/>
</Card>
);
};
export default UpdateTable;

View File

@ -0,0 +1,63 @@
/**
* KRA - Dashboard Wiki Component
* GVA wiki.vue -
*/
import React from 'react';
import { Card, Row, Col } from 'antd';
interface WikiItem {
title: string;
url: string;
}
const wikis: WikiItem[] = [
{
title: 'React',
url: 'https://react.dev/'
},
{
title: 'Kratos 文档',
url: 'https://go-kratos.dev/'
},
{
title: 'Ant Design',
url: 'https://ant.design/'
},
{
title: 'UmiJS',
url: 'https://umijs.org/'
},
{
title: 'GitHub 仓库',
url: 'https://github.com/go-kratos/kratos'
}
];
const Wiki: React.FC = () => {
return (
<Card title="文档" className="kra-dashboard-card" variant="borderless" extra={<a href="#"></a>}>
<Row gutter={[8, 8]}>
{wikis.map((item, index) => (
<Col span={12} key={index}>
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
style={{
fontSize: '14px',
color: '#666',
textDecoration: 'none',
}}
onMouseEnter={(e) => (e.currentTarget.style.color = '#1890ff')}
onMouseLeave={(e) => (e.currentTarget.style.color = '#666')}
>
{item.title}
</a>
</Col>
))}
</Row>
</Card>
);
};
export default Wiki;

View File

@ -1,9 +1,12 @@
/**
* KRA - Dashboard Components Index
* KRA - Dashboard Components Export
*/
export { default as Banner } from './Banner';
export { default as StatCards } from './StatCards';
export { default as Charts } from './Charts';
export { default as QuickLinks } from './QuickLinks';
export { default as Notice } from './Notice';
export { default as Wiki } from './Wiki';
export { default as UpdateTable } from './UpdateTable';
export { default as PluginTable } from './PluginTable';
export { default as MiniLineChart } from './MiniLineChart';

View File

@ -1,44 +1,143 @@
/**
* KRA - Dashboard Styles
* Dashboard 样式
*/
.kra-dashboard {
padding: 16px;
.dashboard {
:global {
.ant-page-header {
padding: 0;
}
}
}
.kra-dashboard-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
.statCard {
height: 100%;
}
.kra-dashboard-card-header {
.statContent {
display: flex;
justify-content: space-between;
align-items: center;
}
.statInfo {
flex: 1;
}
.statValue {
margin: 8px 0;
}
.statTrend {
font-size: 14px;
font-weight: 600;
}
.statChart {
margin-left: 16px;
}
.chartCard {
min-height: 300px;
}
.dataItem {
text-align: center;
}
.dataValue {
font-size: 28px;
font-weight: 600;
line-height: 1.2;
}
.dataName {
color: #666;
margin: 8px 0;
font-size: 14px;
}
.progressSection {
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid #f0f0f0;
}
.progressTitle {
font-size: 16px;
font-weight: 500;
color: #333;
}
.progressItem {
margin-bottom: 8px;
}
.progressLabel {
margin-bottom: 8px;
color: #666;
font-size: 14px;
:global(.anticon) {
margin-right: 8px;
}
}
.quickCard {
height: 100%;
}
.shortcutItem {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px 4px;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s;
&:hover {
background: #f0f5ff;
.shortcutIcon {
background: #1890ff;
color: #fff;
}
}
}
.shortcutIcon {
width: 32px;
height: 32px;
border-radius: 4px;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
transition: all 0.2s;
}
.shortcutTitle {
font-size: 12px;
margin-top: 8px;
color: #666;
text-align: center;
}
.noticeCard,
.docCard,
.sysCard {
height: 100%;
}
.sysItem {
display: flex;
justify-content: space-between;
padding: 16px;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
}
.kra-dashboard-card-body {
padding: 16px;
}
// Dark mode
:global(.dark) {
.kra-dashboard-card {
background: #1f1f1f;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.kra-dashboard-card-header {
border-color: #303030;
&:last-child {
border-bottom: none;
}
}

View File

@ -1,57 +1,272 @@
/**
* KRA - Dashboard Page
* Main dashboard with statistics, charts, and quick links
*
* GVA Dashboard
*/
import React from 'react';
import { Row, Col } from 'antd';
import { Helmet } from '@umijs/max';
import Banner from './components/Banner';
import StatCards from './components/StatCards';
import Charts from './components/Charts';
import QuickLinks from './components/QuickLinks';
import Notice from './components/Notice';
import PluginTable from './components/PluginTable';
import './index.less';
import { PageContainer } from '@ant-design/pro-components';
import { Card, Col, Row, Statistic, Tag, List, Typography, Progress, Space, Tooltip } from 'antd';
import {
UserOutlined,
TeamOutlined,
ApiOutlined,
MenuOutlined,
RiseOutlined,
SettingOutlined,
DatabaseOutlined,
LinkOutlined,
SafetyOutlined,
CloudServerOutlined,
CodeOutlined,
FolderOutlined,
} from '@ant-design/icons';
import { history } from '@umijs/max';
import styles from './index.less';
const { Text } = Typography;
// 统计卡片数据
const statsData = [
{ title: '访问人数', value: 268500, icon: <UserOutlined />, trend: '+80%', color: '#1890ff', percent: 80 },
{ title: '新增客户', value: 12580, icon: <TeamOutlined />, trend: '+25%', color: '#52c41a', percent: 65 },
{ title: '解决数量', value: 8846, icon: <ApiOutlined />, trend: '+15%', color: '#722ed1', percent: 45 },
];
// 快捷功能 - 与GVA quickLinks.vue 一致
const shortcuts = [
{ icon: <MenuOutlined />, title: '菜单管理', path: '/admin/menu' },
{ icon: <LinkOutlined />, title: 'API管理', path: '/admin/api' },
{ icon: <SafetyOutlined />, title: '角色管理', path: '/admin/authority' },
{ icon: <UserOutlined />, title: '用户管理', path: '/admin/user' },
{ icon: <FolderOutlined />, title: '自动化包', path: '/systemTools/autoCodeAdmin' },
{ icon: <CodeOutlined />, title: '自动代码', path: '/systemTools/autoCode' },
];
// 公告数据
const notices = [
{ type: 'processing', typeTitle: '公告', title: 'Kratos Admin v1.0 正式发布,欢迎使用!' },
{ type: 'success', typeTitle: '通知', title: '系统已完成安全升级,请放心使用。' },
{ type: 'warning', typeTitle: '警告', title: '请定期修改密码,确保账户安全。' },
{ type: 'error', typeTitle: '重要', title: '数据库将于本周日凌晨进行维护。' },
{ type: 'default', typeTitle: '信息', title: '感谢您对 Kratos Admin 的支持!' },
];
// 内容数据
const contentData = [
{ name: '用户', value: 128, color: '#1890ff' },
{ name: '角色', value: 8, color: '#52c41a' },
{ name: 'API', value: 256, color: '#722ed1' },
{ name: '菜单', value: 32, color: '#faad14' },
{ name: '字典', value: 15, color: '#13c2c2' },
{ name: '部门', value: 12, color: '#eb2f96' },
];
// 文档链接
const docLinks = [
{ title: 'Kratos 官方文档', url: 'https://go-kratos.dev/' },
{ title: 'Ant Design Pro', url: 'https://pro.ant.design/' },
{ title: 'React 官方文档', url: 'https://react.dev/' },
{ title: 'Go 语言文档', url: 'https://go.dev/doc/' },
];
const Dashboard: React.FC = () => {
return (
<div className="kra-dashboard">
<Helmet>
<title> - Kratos Admin</title>
</Helmet>
<PageContainer
header={{ title: '' }}
className={styles.dashboard}
>
{/* 统计卡片行 */}
<Row gutter={[16, 16]}>
{/* Statistics Cards */}
{statsData.map((item, index) => (
<Col xs={24} sm={12} lg={8} key={index}>
<Card className={styles.statCard} variant="borderless">
<div className={styles.statContent}>
<div className={styles.statInfo}>
<Text type="secondary">{item.title}</Text>
<div className={styles.statValue}>
<Statistic value={item.value} valueStyle={{ fontSize: 28, fontWeight: 600 }} />
</div>
<div className={styles.statTrend}>
<RiseOutlined style={{ color: '#52c41a' }} />
<Text style={{ color: '#52c41a', marginLeft: 4 }}>{item.trend}</Text>
</div>
</div>
<div className={styles.statChart}>
<Progress
type="circle"
percent={item.percent}
size={80}
strokeColor={item.color}
format={() => <span style={{ fontSize: 14 }}>{item.percent}%</span>}
/>
</div>
</div>
</Card>
</Col>
))}
</Row>
{/* 主内容区 */}
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
{/* 左侧内容数据 */}
<Col xs={24} lg={18}>
<StatCards />
<Card title="内容数据" variant="borderless" className={styles.chartCard}>
<Row gutter={[16, 24]}>
{contentData.map((item, index) => (
<Col xs={12} sm={8} md={4} key={index}>
<div className={styles.dataItem}>
<div className={styles.dataValue} style={{ color: item.color }}>
{item.value}
</div>
<div className={styles.dataName}>{item.name}</div>
<Progress
percent={Math.min((item.value / 300) * 100, 100)}
showInfo={false}
strokeColor={item.color}
size="small"
/>
</div>
</Col>
))}
</Row>
<div className={styles.progressSection}>
<div className={styles.progressTitle}>使</div>
<Row gutter={[32, 16]} style={{ marginTop: 16 }}>
<Col xs={24} sm={12}>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>
<CloudServerOutlined /> CPU 使
</div>
<Progress percent={45} strokeColor="#1890ff" />
</div>
</Col>
<Col xs={24} sm={12}>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>
<DatabaseOutlined /> 使
</div>
<Progress percent={68} strokeColor="#52c41a" />
</div>
</Col>
<Col xs={24} sm={12}>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>
<SafetyOutlined /> 使
</div>
<Progress percent={32} strokeColor="#722ed1" />
</div>
</Col>
<Col xs={24} sm={12}>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>
<ApiOutlined /> API
</div>
<Progress percent={85} strokeColor="#faad14" />
</div>
</Col>
</Row>
</div>
</Card>
</Col>
{/* Quick Links */}
{/* 右侧快捷功能 */}
<Col xs={24} lg={6}>
<QuickLinks />
</Col>
{/* Main Chart */}
<Col xs={24} lg={18}>
<Charts />
</Col>
{/* Notice */}
<Col xs={24} lg={6}>
<Notice />
</Col>
{/* Plugin Table */}
<Col xs={24} lg={18}>
<PluginTable />
</Col>
{/* Banner */}
<Col xs={24} lg={6}>
<Banner />
<Card title="快捷功能" variant="borderless" className={styles.quickCard}>
<Row gutter={[8, 16]}>
{shortcuts.map((item, index) => (
<Col span={8} key={index}>
<div
className={styles.shortcutItem}
onClick={() => history.push(item.path)}
>
<div className={styles.shortcutIcon}>{item.icon}</div>
<Text className={styles.shortcutTitle}>{item.title}</Text>
</div>
</Col>
))}
</Row>
</Card>
</Col>
</Row>
</div>
{/* 底部区域 */}
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
{/* 公告 */}
<Col xs={24} md={12} lg={8}>
<Card
title="公告"
variant="borderless"
extra={<a></a>}
className={styles.noticeCard}
>
<List
size="small"
dataSource={notices}
renderItem={(item) => (
<List.Item style={{ padding: '8px 0', border: 'none' }}>
<Space>
<Tag color={item.type}>{item.typeTitle}</Tag>
<Tooltip title={item.title}>
<Text ellipsis style={{ maxWidth: 200 }}>{item.title}</Text>
</Tooltip>
</Space>
</List.Item>
)}
/>
</Card>
</Col>
{/* 文档链接 */}
<Col xs={24} md={12} lg={8}>
<Card
title="文档"
variant="borderless"
extra={<a></a>}
className={styles.docCard}
>
<List
size="small"
dataSource={docLinks}
renderItem={(item) => (
<List.Item style={{ padding: '8px 0', border: 'none' }}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
<Space>
<LinkOutlined />
<Text>{item.title}</Text>
</Space>
</a>
</List.Item>
)}
/>
</Card>
</Col>
{/* 系统信息 */}
<Col xs={24} lg={8}>
<Card title="系统信息" variant="borderless" className={styles.sysCard}>
<div className={styles.sysItem}>
<Text type="secondary"></Text>
<Text strong>Kratos Admin</Text>
</div>
<div className={styles.sysItem}>
<Text type="secondary"></Text>
<Text strong>Go Kratos</Text>
</div>
<div className={styles.sysItem}>
<Text type="secondary"></Text>
<Text strong>React + Ant Design Pro</Text>
</div>
<div className={styles.sysItem}>
<Text type="secondary"></Text>
<Text strong>v1.0.0</Text>
</div>
<div className={styles.sysItem}>
<Text type="secondary"></Text>
<Tag color="success"></Tag>
</div>
</Card>
</Col>
</Row>
</PageContainer>
);
};