From b770c4a3b62a277727fbbb68e6bd86aa342a975e Mon Sep 17 00:00:00 2001 From: Yvan <8574526@qq,com> Date: Thu, 8 Jan 2026 14:11:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/about/index.tsx | 2 +- web/src/pages/dashboard/components/Banner.tsx | 73 +++-- .../dashboard/components/MiniLineChart.tsx | 86 +++++ web/src/pages/dashboard/components/Notice.tsx | 119 ++++--- .../dashboard/components/PluginTable.tsx | 155 ++++----- .../pages/dashboard/components/QuickLinks.tsx | 137 ++++---- .../pages/dashboard/components/StatCards.tsx | 56 ++-- .../dashboard/components/UpdateTable.tsx | 87 +++++ web/src/pages/dashboard/components/Wiki.tsx | 63 ++++ web/src/pages/dashboard/components/index.ts | 5 +- web/src/pages/dashboard/index.less | 161 ++++++++-- web/src/pages/dashboard/index.tsx | 297 +++++++++++++++--- 12 files changed, 912 insertions(+), 329 deletions(-) create mode 100644 web/src/pages/dashboard/components/MiniLineChart.tsx create mode 100644 web/src/pages/dashboard/components/UpdateTable.tsx create mode 100644 web/src/pages/dashboard/components/Wiki.tsx diff --git a/web/src/pages/about/index.tsx b/web/src/pages/about/index.tsx index 5a9facc..7131468 100644 --- a/web/src/pages/about/index.tsx +++ b/web/src/pages/about/index.tsx @@ -136,7 +136,7 @@ const AboutPage: React.FC = () => { {/* 头部信息 */}
- +
Kratos Admin diff --git a/web/src/pages/dashboard/components/Banner.tsx b/web/src/pages/dashboard/components/Banner.tsx index 93164ec..3c45498 100644 --- a/web/src/pages/dashboard/components/Banner.tsx +++ b/web/src/pages/dashboard/components/Banner.tsx @@ -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> ); }; diff --git a/web/src/pages/dashboard/components/MiniLineChart.tsx b/web/src/pages/dashboard/components/MiniLineChart.tsx new file mode 100644 index 0000000..75b462d --- /dev/null +++ b/web/src/pages/dashboard/components/MiniLineChart.tsx @@ -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; diff --git a/web/src/pages/dashboard/components/Notice.tsx b/web/src/pages/dashboard/components/Notice.tsx index 832d922..1834cde 100644 --- a/web/src/pages/dashboard/components/Notice.tsx +++ b/web/src/pages/dashboard/components/Notice.tsx @@ -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> ); }; diff --git a/web/src/pages/dashboard/components/PluginTable.tsx b/web/src/pages/dashboard/components/PluginTable.tsx index 7462639..abc53f8 100644 --- a/web/src/pages/dashboard/components/PluginTable.tsx +++ b/web/src/pages/dashboard/components/PluginTable.tsx @@ -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" /> diff --git a/web/src/pages/dashboard/components/QuickLinks.tsx b/web/src/pages/dashboard/components/QuickLinks.tsx index 0aadec2..19fa448 100644 --- a/web/src/pages/dashboard/components/QuickLinks.tsx +++ b/web/src/pages/dashboard/components/QuickLinks.tsx @@ -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> ); }; diff --git a/web/src/pages/dashboard/components/StatCards.tsx b/web/src/pages/dashboard/components/StatCards.tsx index c124494..f89f4aa 100644 --- a/web/src/pages/dashboard/components/StatCards.tsx +++ b/web/src/pages/dashboard/components/StatCards.tsx @@ -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> diff --git a/web/src/pages/dashboard/components/UpdateTable.tsx b/web/src/pages/dashboard/components/UpdateTable.tsx new file mode 100644 index 0000000..dd00d02 --- /dev/null +++ b/web/src/pages/dashboard/components/UpdateTable.tsx @@ -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; diff --git a/web/src/pages/dashboard/components/Wiki.tsx b/web/src/pages/dashboard/components/Wiki.tsx new file mode 100644 index 0000000..3a82760 --- /dev/null +++ b/web/src/pages/dashboard/components/Wiki.tsx @@ -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; diff --git a/web/src/pages/dashboard/components/index.ts b/web/src/pages/dashboard/components/index.ts index c0cd54d..0416bfd 100644 --- a/web/src/pages/dashboard/components/index.ts +++ b/web/src/pages/dashboard/components/index.ts @@ -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'; diff --git a/web/src/pages/dashboard/index.less b/web/src/pages/dashboard/index.less index 0a50580..50c67d2 100644 --- a/web/src/pages/dashboard/index.less +++ b/web/src/pages/dashboard/index.less @@ -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; } } diff --git a/web/src/pages/dashboard/index.tsx b/web/src/pages/dashboard/index.tsx index e184e05..30b4463 100644 --- a/web/src/pages/dashboard/index.tsx +++ b/web/src/pages/dashboard/index.tsx @@ -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 - - + + {/* 统计卡片行 */} - {/* Statistics Cards */} + {statsData.map((item, index) => ( + + +
+
+ {item.title} +
+ +
+
+ + {item.trend} +
+
+
+ {item.percent}%} + /> +
+
+
+ + ))} +
+ + {/* 主内容区 */} + + {/* 左侧内容数据 */} - + + + {contentData.map((item, index) => ( + +
+
+ {item.value} +
+
{item.name}
+ +
+ + ))} +
+
+
系统资源使用情况
+ + +
+
+ CPU 使用率 +
+ +
+ + +
+
+ 内存使用率 +
+ +
+ + +
+
+ 磁盘使用率 +
+ +
+ + +
+
+ API 调用量 +
+ +
+ +
+
+
- {/* Quick Links */} + {/* 右侧快捷功能 */} - - - - {/* Main Chart */} - - - - - {/* Notice */} - - - - - {/* Plugin Table */} - - - - - {/* Banner */} - - + + + {shortcuts.map((item, index) => ( + +
history.push(item.path)} + > +
{item.icon}
+ {item.title} +
+ + ))} +
+
- + + {/* 底部区域 */} + + {/* 公告 */} + + 查看更多} + className={styles.noticeCard} + > + ( + + + {item.typeTitle} + + {item.title} + + + + )} + /> + + + + {/* 文档链接 */} + + 查看更多} + className={styles.docCard} + > + ( + + + + + {item.title} + + + + )} + /> + + + + {/* 系统信息 */} + + +
+ 系统名称 + Kratos Admin +
+
+ 后端框架 + Go Kratos +
+
+ 前端框架 + React + Ant Design Pro +
+
+ 版本号 + v1.0.0 +
+
+ 服务器状态 + 运行中 +
+
+ +
+
); };