一、基础环境
1.1 项目依赖
package.json
{"name": "erp-web","version": "1.0.0","description": "ERP系统前端 - React 19","main": "index.js","type": "module","scripts": {"dev": "vite","build": "vite build","preview": "vite preview"},"dependencies": {"lucide-react": "^0.400.0","react": "^19.0.0","react-dom": "^19.0.0","react-router-dom": "^6.22.0","recharts": "^3.0.2"},"devDependencies": {"@vitejs/plugin-react": "^4.2.1","vite": "^5.1.0"},"engines": {"node": ">=18.0.0","pnpm": ">=8.0.0"},"packageManager": "pnpm@8.15.0","keywords": ["erp","react","dashboard"],"author": "源滚滚AI编程","license": "MIT"
}
1.2 vite 配置
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'export default defineConfig({plugins: [react()],resolve: {alias: {'@': path.resolve(__dirname, './src')}},server: {port: 3000,open: true},build: {outDir: 'dist',sourcemap: true}
})
1.3 根HTML
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"/><link rel="icon" type="image/svg+xml" href="/vite.svg"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>ERP系统 - 企业资源管理</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
1.4 全局样式
src/index.css
* {margin: 0;padding: 0;box-sizing: border-box;
}body {font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen','Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;background-color: #f8fafc;color: #1e293b;line-height: 1.6;
}#root {min-height: 100vh;
}/* 滚动条样式 */
::-webkit-scrollbar {width: 6px;height: 6px;
}::-webkit-scrollbar-track {background: #f1f5f9;
}::-webkit-scrollbar-thumb {background: #cbd5e1;border-radius: 3px;
}::-webkit-scrollbar-thumb:hover {background: #94a3b8;
}/* 按钮重置 */
button {border: none;background: none;cursor: pointer;font-family: inherit;
}/* 链接重置 */
a {text-decoration: none;color: inherit;
}/* 输入框重置 */
input, textarea, select {font-family: inherit;border: none;outline: none;
}/* 列表重置 */
ul, ol {list-style: none;
}
1.5 程序入口
src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App /></React.StrictMode>,
)
1.6 根组件样式
src/App.css
.app {display: flex;min-height: 100vh;background-color: #f8fafc;
}.main-content {flex: 1;display: flex;flex-direction: column;overflow: hidden;
}/* 响应式设计 */
@media (max-width: 768px) {.app {flex-direction: column;}.main-content {margin-left: 0;}
}
1.7 根组件
src/App.jsx
import React from 'react'
import './App.css'function App() {return (<div className="app"><h1>你好,React</h1></div>)
}export default App
二、核心布局组件
2.1 图标组件
src/components/Icon.jsx
import React from 'react'
import * as LucideIcons from 'lucide-react'/*** 通用Icon组件* @param {string} name - 图标名称(与lucide-react导出名一致)* @param {number} size - 图标大小* @param {string} color - 图标颜色* @param {string} className - 额外class* @param {object} rest - 其他props*/
const Icon = ({ name, size = 20, color = 'currentColor', className = '', ...rest }) => {const LucideIcon = LucideIcons[name]if (!LucideIcon) return nullreturn <LucideIcon size={size} color={color} className={className} {...rest} />
}export default Icon
2.2 头部组件样式
src/components/Header/Header.css
.header {background: white;padding: 20px 30px;display: flex;align-items: center;justify-content: space-between;border-bottom: 1px solid #e2e8f0;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}.header-left {display: flex;flex-direction: column;
}.page-title {font-size: 24px;font-weight: 700;color: #1e293b;margin: 0;
}.page-subtitle {font-size: 14px;color: #64748b;margin: 4px 0 0 0;
}.header-right {display: flex;align-items: center;gap: 20px;
}.search-box {position: relative;display: flex;align-items: center;
}.search-box svg {position: absolute;left: 12px;color: #94a3b8;
}.search-input {padding: 10px 10px 10px 40px;border: 1px solid #e2e8f0;border-radius: 8px;background: #f8fafc;width: 300px;font-size: 14px;transition: all 0.2s ease;
}.search-input:focus {outline: none;border-color: #667eea;background: white;box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}.header-actions {display: flex;align-items: center;gap: 12px;
}.action-btn {position: relative;padding: 8px;border-radius: 8px;color: #64748b;transition: all 0.2s ease;background: transparent;
}.action-btn:hover {background: #f1f5f9;color: #1e293b;
}.notification-badge {position: absolute;top: 4px;right: 4px;background: #ef4444;color: white;font-size: 10px;font-weight: 600;padding: 2px 6px;border-radius: 10px;min-width: 16px;text-align: center;
}.user-menu {margin-left: 8px;
}.user-btn {display: flex;align-items: center;gap: 8px;padding: 8px 12px;border-radius: 8px;background: #f1f5f9;color: #1e293b;font-weight: 500;transition: all 0.2s ease;
}.user-btn:hover {background: #e2e8f0;
}.user-avatar {width: 32px;height: 32px;background: #667eea;border-radius: 50%;display: flex;align-items: center;justify-content: center;color: white;
}/* 响应式设计 */
@media (max-width: 768px) {.header {padding: 15px 20px;flex-direction: column;gap: 15px;align-items: stretch;}.header-right {justify-content: space-between;}.search-input {width: 200px;}.page-title {font-size: 20px;}
}
2.3 头部组件
src/components/Header/index.jsx
import React from 'react'
import Icon from '@/components/Icon'
import './Header.css'const Header = () => {return (<header className="header"><div className="header-left"><h1 className="page-title">仪表板</h1><p className="page-subtitle">欢迎回来,管理员</p></div><div className="header-right"><div className="search-box"><Icon name="Search" size={20} color="#94a3b8" /><input type="text" placeholder="搜索..." className="search-input"/></div><div className="header-actions"><button className="action-btn notification-btn"><Icon name="Bell" size={20} /><span className="notification-badge">3</span></button><button className="action-btn"><Icon name="Settings" size={20} /></button><div className="user-menu"><button className="user-btn"><div className="user-avatar"><Icon name="User" size={16} /></div><span>管理员</span></button></div></div></div></header>)
}export default Header
2.4 侧边栏组件样式
src/components/Sidebar/Sidebar.css
.sidebar {width: 280px;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: white;display: flex;flex-direction: column;transition: all 0.3s ease;position: relative;z-index: 1000;
}.sidebar.collapsed {width: 80px;
}.sidebar-header {padding: 20px;display: flex;align-items: center;justify-content: space-between;border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}.logo {font-size: 20px;font-weight: 700;color: white;
}.collapse-btn {color: white;background: rgba(255, 255, 255, 0.1);border-radius: 8px;padding: 8px;transition: all 0.2s ease;
}.collapse-btn:hover {background: rgba(255, 255, 255, 0.2);
}.sidebar-nav {flex: 1;padding: 20px 0;
}.sidebar-nav ul {list-style: none;
}.nav-item {display: flex;align-items: center;padding: 12px 20px;color: rgba(255, 255, 255, 0.8);transition: all 0.2s ease;text-decoration: none;gap: 12px;
}.nav-item:hover {background: rgba(255, 255, 255, 0.1);color: white;
}.nav-item.active {background: rgba(255, 255, 255, 0.2);color: white;border-right: 3px solid white;
}.nav-item span {font-weight: 500;
}.sidebar-footer {padding: 20px;border-top: 1px solid rgba(255, 255, 255, 0.1);
}.user-info {display: flex;align-items: center;gap: 12px;
}.avatar {width: 40px;height: 40px;background: rgba(255, 255, 255, 0.2);border-radius: 50%;display: flex;align-items: center;justify-content: center;color: white;
}.user-details {display: flex;flex-direction: column;
}.user-name {font-weight: 600;font-size: 14px;
}.user-role {font-size: 12px;opacity: 0.8;
}/* 移动端样式 */
.mobile-menu-btn {display: none;position: fixed;top: 20px;left: 20px;z-index: 1001;background: #667eea;color: white;border-radius: 8px;padding: 8px;
}.mobile-overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 0.5);z-index: 999;
}@media (max-width: 768px) {.mobile-menu-btn {display: block;}.sidebar {position: fixed;left: -280px;top: 0;height: 100vh;transform: translateX(0);transition: transform 0.3s ease;}.sidebar.mobile-open {transform: translateX(280px);}.sidebar.collapsed {width: 280px;}
}
2.5 侧边栏组件
src/components/Sidebar/index.jsx
import React, { useState } from 'react'
import Icon from '@/components/Icon'
import './Sidebar.css'const Sidebar = () => {const [isCollapsed, setIsCollapsed] = useState(false)const [isMobileOpen, setIsMobileOpen] = useState(false)const menuItems = [{ icon: 'Home', label: '首页', active: true },{ icon: 'Users', label: '客户管理' },{ icon: 'Package', label: '库存管理' },{ icon: 'ShoppingCart', label: '订单管理' },{ icon: 'BarChart3', label: '财务报表' },{ icon: 'Settings', label: '系统设置' }]const toggleSidebar = () => {setIsCollapsed(!isCollapsed)}const toggleMobileMenu = () => {setIsMobileOpen(!isMobileOpen)}return (<>{/* 移动端菜单按钮 */}<button className="mobile-menu-btn" onClick={toggleMobileMenu}><Icon name={isMobileOpen ? 'X' : 'Menu'} size={24} /></button>{/* 侧边栏 */}<div className={`sidebar ${isCollapsed ? 'collapsed' : ''} ${isMobileOpen ? 'mobile-open' : ''}`}><div className="sidebar-header"><div className="logo">{!isCollapsed && <span>ERP系统</span>}</div><button className="collapse-btn" onClick={toggleSidebar}><Icon name={isCollapsed ? 'Menu' : 'X'} size={20} /></button></div><nav className="sidebar-nav"><ul>{menuItems.map((item, index) => (<li key={index}><a href="#" className={`nav-item ${item.active ? 'active' : ''}`}onClick={(e) => {e.preventDefault()}}><Icon name={item.icon} size={20} color={item.active ? '#fff' : '#e0e7ef'} />{!isCollapsed && <span>{item.label}</span>}</a></li>))}</ul></nav><div className="sidebar-footer"><div className="user-info"><div className="avatar"><Icon name="Users" size={20} /></div>{!isCollapsed && (<div className="user-details"><span className="user-name">管理员</span><span className="user-role">系统管理员</span></div>)}</div></div></div>{/* 移动端遮罩 */}{isMobileOpen && (<div className="mobile-overlay" onClick={toggleMobileMenu}></div>)}</>)
}export default Sidebar
2.6 面板组件
src/components/Dashboard/index.jsx
import React from 'react'const Dashboard = () => {return (<div className="dashboard"><h1>面板组件</h1></div>)
}export default Dashboard
2.7 根组件样式
src/App.css
.app {display: flex;min-height: 100vh;background-color: #f8fafc;
}.main-content {flex: 1;display: flex;flex-direction: column;overflow: hidden;
}/* 响应式设计 */
@media (max-width: 768px) {.app {flex-direction: column;}.main-content {margin-left: 0;}
}
2.8 根组件优化
src/App.jsx
import React from 'react'
import Sidebar from '@/components/Sidebar'
import Header from '@/components/Header'
import Dashboard from '@/components/Dashboard'
import './App.css'function App() {return (<div className="app"><Sidebar /><div className="main-content"><Header /><Dashboard /></div></div>)
}export default App
三、首页内容组件
3.1 统计卡片样式
src/components/StatCard/StatCard.css
.stat-card {background: white;border-radius: 12px;padding: 24px;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);transition: all 0.2s ease;border: 1px solid #e2e8f0;
}.stat-card:hover {transform: translateY(-2px);box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}.stat-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 16px;
}.stat-icon {width: 48px;height: 48px;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);border-radius: 12px;display: flex;align-items: center;justify-content: center;font-size: 20px;
}.stat-change {display: flex;align-items: center;gap: 4px;font-size: 12px;font-weight: 600;padding: 4px 8px;border-radius: 6px;
}.stat-change.positive {background: #dcfce7;color: #166534;
}.stat-change.negative {background: #fee2e2;color: #dc2626;
}.stat-content {display: flex;flex-direction: column;gap: 8px;
}.stat-title {font-size: 14px;font-weight: 500;color: #64748b;margin: 0;
}.stat-value {font-size: 28px;font-weight: 700;color: #1e293b;margin: 0;
}/* 响应式设计 */
@media (max-width: 768px) {.stat-card {padding: 20px;}.stat-value {font-size: 24px;}.stat-icon {width: 40px;height: 40px;font-size: 18px;}
}
3.2 统计卡片
src/components/StatCard/index.jsx
import React from 'react'
import Icon from '@/components/Icon'
import './StatCard.css'const StatCard = ({ title, value, change, changeType, icon }) => {return (<div className="stat-card"><div className="stat-header"><div className="stat-icon"><Icon name={icon} size={24} color="#fff" /></div><div className={`stat-change ${changeType}`}><Icon name={changeType === 'positive' ? 'TrendingUp' : 'TrendingDown'} size={16} /><span>{change}</span></div></div><div className="stat-content"><h3 className="stat-title">{title}</h3><p className="stat-value">{value}</p></div></div>)
}export default StatCard
3.3 销量折线图组件
src/components/Charts/SalesLineChart.jsx
import React from 'react'
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'const data = [{ date: '06-01', sales: 3200 },{ date: '06-05', sales: 4200 },{ date: '06-10', sales: 3900 },{ date: '06-15', sales: 5200 },{ date: '06-20', sales: 4800 },{ date: '06-25', sales: 6100 },{ date: '06-30', sales: 7000 },
]const SalesLineChart = () => (<ResponsiveContainer width="100%" height={260}><LineChart data={data} margin={{ top: 20, right: 30, left: 0, bottom: 0 }}><CartesianGrid strokeDasharray="3 3" /><XAxis dataKey="date" /><YAxis /><Tooltip /><Line type="monotone" dataKey="sales" stroke="#667eea" strokeWidth={3} dot={{ r: 5 }} activeDot={{ r: 7 }} /></LineChart></ResponsiveContainer>
)export default SalesLineChart
3.4 产品饼图组件
src/components/Charts/ProductPieChart.jsx
import React from 'react'
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer, Legend } from 'recharts'const data = [{ name: '电子产品', value: 400 },{ name: '服装', value: 300 },{ name: '食品', value: 300 },{ name: '家居', value: 200 },
]const COLORS = ['#667eea', '#764ba2', '#f093fb', '#f5576c']const ProductPieChart = () => (<ResponsiveContainer width="100%" height={260}><PieChart><Piedata={data}cx="50%"cy="50%"labelLine={false}label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}outerRadius={90}fill="#8884d8"dataKey="value">{data.map((entry, index) => (<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />))}</Pie><Tooltip /><Legend /></PieChart></ResponsiveContainer>
)export default ProductPieChart
3.5 图表卡片组件样式
src/components/ChartCard/ChartCard.css
.chart-card {background: white;border-radius: 12px;padding: 24px;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);border: 1px solid #e2e8f0;
}.chart-header {display: flex;justify-content: space-between;align-items: flex-start;margin-bottom: 24px;
}.chart-title {font-size: 18px;font-weight: 600;color: #1e293b;margin: 0 0 4px 0;
}.chart-subtitle {font-size: 14px;color: #64748b;margin: 0;
}.chart-actions {display: flex;gap: 8px;
}.chart-action-btn {padding: 6px 12px;background: #f1f5f9;color: #374151;border-radius: 6px;font-size: 12px;font-weight: 500;transition: all 0.2s ease;
}.chart-action-btn:hover {background: #e2e8f0;
}.chart-content {min-height: 300px;display: flex;align-items: center;justify-content: center;
}.chart-placeholder {display: flex;flex-direction: column;align-items: center;gap: 16px;color: #94a3b8;text-align: center;
}.chart-placeholder p {font-size: 14px;margin: 0;
}/* 柱状图样式 */
.chart-bars {display: flex;align-items: end;gap: 4px;height: 200px;padding: 20px 0;
}.chart-bar {width: 20px;background: linear-gradient(to top, #667eea, #764ba2);border-radius: 2px 2px 0 0;transition: all 0.3s ease;
}.chart-bar:hover {background: linear-gradient(to top, #5a67d8, #6b46c1);transform: scaleY(1.05);
}/* 饼图样式 */
.pie-segments {position: relative;width: 120px;height: 120px;border-radius: 50%;background: conic-gradient(var(--color) 0deg var(--percentage),#e2e8f0 var(--percentage) 360deg);
}.pie-segment {position: absolute;width: 100%;height: 100%;border-radius: 50%;background: conic-gradient(var(--color) 0deg var(--percentage),transparent var(--percentage) 360deg);
}/* 响应式设计 */
@media (max-width: 768px) {.chart-card {padding: 20px;}.chart-header {flex-direction: column;gap: 16px;align-items: stretch;}.chart-actions {justify-content: flex-end;}.chart-content {min-height: 250px;}.chart-bars {height: 150px;}.chart-bar {width: 16px;}
}
3.6 图表卡片组件
src/components/ChartCard/index.jsx
import React from 'react'
import SalesLineChart from '@/components/Charts/SalesLineChart'
import ProductPieChart from '@/components/Charts/ProductPieChart'
import './ChartCard.css'const ChartCard = ({ title, subtitle, type }) => {let chartContent = nullif (type === 'line') {chartContent = <SalesLineChart />} else if (type === 'pie') {chartContent = <ProductPieChart />}return (<div className="chart-card"><div className="chart-header"><div><h3 className="chart-title">{title}</h3><p className="chart-subtitle">{subtitle}</p></div><div className="chart-actions"><button className="chart-action-btn">导出</button><button className="chart-action-btn">刷新</button></div></div><div className="chart-content">{chartContent}</div></div>)
}export default ChartCard
3.7 最近订单组件样式
src/components/RecentOrders/RecentOrders.css
.recent-orders {background: white;border-radius: 12px;padding: 24px;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);border: 1px solid #e2e8f0;height: 100%;
}.orders-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;
}.orders-header h3 {font-size: 18px;font-weight: 600;color: #1e293b;margin: 0;
}.view-all-btn {padding: 6px 12px;background: #f1f5f9;color: #374151;border-radius: 6px;font-size: 12px;font-weight: 500;transition: all 0.2s ease;
}.view-all-btn:hover {background: #e2e8f0;
}.orders-list {display: flex;flex-direction: column;gap: 12px;
}.order-item {display: flex;align-items: center;gap: 16px;padding: 16px;background: #f8fafc;border-radius: 8px;transition: all 0.2s ease;border: 1px solid #e2e8f0;
}.order-item:hover {background: #f1f5f9;transform: translateX(4px);
}.order-info {flex: 1;display: flex;flex-direction: column;gap: 4px;
}.order-id {font-size: 14px;font-weight: 600;color: #1e293b;
}.order-customer {font-size: 13px;color: #64748b;
}.order-amount {font-size: 14px;font-weight: 600;color: #059669;
}.order-status {display: flex;align-items: center;gap: 6px;min-width: 80px;
}.status-icon {color: #94a3b8;
}.status-icon.completed {color: #059669;
}.status-icon.processing {color: #d97706;
}.status-icon.pending {color: #dc2626;
}.status-text {font-size: 12px;font-weight: 500;color: #64748b;
}.order-time {display: flex;flex-direction: column;align-items: flex-end;gap: 2px;min-width: 80px;
}.order-date {font-size: 12px;color: #64748b;
}.order-time-text {font-size: 11px;color: #94a3b8;
}.order-actions {padding: 4px;color: #94a3b8;border-radius: 4px;transition: all 0.2s ease;
}.order-actions:hover {background: #e2e8f0;color: #374151;
}/* 响应式设计 */
@media (max-width: 768px) {.recent-orders {padding: 20px;}.order-item {flex-direction: column;align-items: flex-start;gap: 12px;}.order-status {align-self: flex-start;}.order-time {align-self: flex-start;align-items: flex-start;}.order-actions {align-self: flex-end;margin-top: -40px;}
}
3.8 最近订单组件
src/components/RecentOrders/index.jsx
import React from 'react'
import { Clock, CheckCircle, AlertCircle, MoreVertical } from 'lucide-react'
import './RecentOrders.css'const RecentOrders = () => {const orders = [{id: '#ORD-001',customer: '张三',amount: '¥2,450',status: 'completed',date: '2024-01-15',time: '14:30'},{id: '#ORD-002',customer: '李四',amount: '¥1,890',status: 'pending',date: '2024-01-15',time: '13:45'},{id: '#ORD-003',customer: '王五',amount: '¥3,200',status: 'processing',date: '2024-01-15',time: '12:20'},{id: '#ORD-004',customer: '赵六',amount: '¥980',status: 'completed',date: '2024-01-15',time: '11:15'},{id: '#ORD-005',customer: '钱七',amount: '¥1,650',status: 'pending',date: '2024-01-15',time: '10:30'}]const getStatusIcon = (status) => {switch (status) {case 'completed':return <CheckCircle size={16} className="status-icon completed" />case 'processing':return <Clock size={16} className="status-icon processing" />case 'pending':return <AlertCircle size={16} className="status-icon pending" />default:return <Clock size={16} className="status-icon" />}}const getStatusText = (status) => {switch (status) {case 'completed':return '已完成'case 'processing':return '处理中'case 'pending':return '待处理'default:return '未知'}}return (<div className="recent-orders"><div className="orders-header"><h3>最近订单</h3><button className="view-all-btn">查看全部</button></div><div className="orders-list">{orders.map((order, index) => (<div key={index} className="order-item"><div className="order-info"><div className="order-id">{order.id}</div><div className="order-customer">{order.customer}</div><div className="order-amount">{order.amount}</div></div><div className="order-status">{getStatusIcon(order.status)}<span className="status-text">{getStatusText(order.status)}</span></div><div className="order-time"><div className="order-date">{order.date}</div><div className="order-time-text">{order.time}</div></div><button className="order-actions"><MoreVertical size={16} /></button></div>))}</div></div>)
}export default RecentOrders
3.9 首页组件样式
src/components/Dashboard/Dashboard.css
.dashboard {flex: 1;overflow-y: auto;background: #f8fafc;
}.dashboard-content {padding: 30px;max-width: 1400px;margin: 0 auto;
}/* 统计卡片网格 */
.stats-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));gap: 20px;margin-bottom: 30px;
}/* 主要内容区域 */
.dashboard-main {display: grid;grid-template-columns: 2fr 1fr;gap: 30px;margin-bottom: 30px;
}.charts-section {display: flex;flex-direction: column;gap: 20px;
}.orders-section {min-height: 400px;
}/* 快速操作区域 */
.quick-actions {background: white;border-radius: 12px;padding: 24px;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}.quick-actions h3 {font-size: 18px;font-weight: 600;color: #1e293b;margin-bottom: 20px;
}.actions-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));gap: 16px;
}.action-card {display: flex;flex-direction: column;align-items: center;gap: 12px;padding: 20px;background: #f8fafc;border-radius: 8px;transition: all 0.2s ease;border: 1px solid #e2e8f0;
}.action-card:hover {background: #f1f5f9;transform: translateY(-2px);box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}.action-icon {font-size: 24px;width: 48px;height: 48px;display: flex;align-items: center;justify-content: center;background: white;border-radius: 50%;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}.action-text {font-weight: 500;color: #374151;text-align: center;
}/* 响应式设计 */
@media (max-width: 1024px) {.dashboard-main {grid-template-columns: 1fr;}.charts-section {order: 2;}.orders-section {order: 1;}
}@media (max-width: 768px) {.dashboard-content {padding: 20px;}.stats-grid {grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));gap: 15px;}.actions-grid {grid-template-columns: repeat(2, 1fr);}
}@media (max-width: 480px) {.stats-grid {grid-template-columns: 1fr;}.actions-grid {grid-template-columns: 1fr;}
}
3.10 首页组件
src/components/Dashboard/index.jsx
import React from 'react'
import StatCard from '@/components/StatCard'
import ChartCard from '@/components/ChartCard'
import RecentOrders from '@/components/RecentOrders'
import './Dashboard.css'const Dashboard = () => {const stats = [{title: '总销售额',value: '¥128,450',change: '+12.5%',changeType: 'positive',icon: 'CreditCard'},{title: '订单数量',value: '1,234',change: '+8.2%',changeType: 'positive',icon: 'Package'},{title: '客户数量',value: '856',change: '+15.3%',changeType: 'positive',icon: 'Users'},{title: '库存预警',value: '12',change: '-3.1%',changeType: 'negative',icon: 'AlertTriangle'}]return (<div className="dashboard"><div className="dashboard-content">{/* 统计卡片 */}<div className="stats-grid">{stats.map((stat, index) => (<StatCard key={index} {...stat} />))}</div>{/* 图表和订单区域 */}<div className="dashboard-main"><div className="charts-section"><ChartCard title="销售趋势"subtitle="最近30天销售数据"type="line"/><ChartCard title="产品分类"subtitle="各产品类别销售占比"type="pie"/></div><div className="orders-section"><RecentOrders /></div></div>{/* 快速操作 */}<div className="quick-actions"><h3>快速操作</h3><div className="actions-grid"><button className="action-card"><span className="action-icon">➕</span><span className="action-text">新建订单</span></button><button className="action-card"><span className="action-icon">👤</span><span className="action-text">添加客户</span></button><button className="action-card"><span className="action-icon">📊</span><span className="action-text">生成报表</span></button><button className="action-card"><span className="action-icon">⚙️</span><span className="action-text">系统设置</span></button></div></div></div></div>)
}export default Dashboard