Files
openclaw-monitor/code/frontend/src/pages/Dashboard/index.js
flying-hero f5ac1e85bf feat: 优化日新功能交互流程
- 移除首页的'日新'按钮,每个龙虾卡片只保留'详情'按钮
- 日新功能整合到 LobsterDetail 组件内(弹窗形式)
- 删除 MemoryPage 路由组件(不再需要独立路由)
- 简化 App.js 路由配置,只保留两条路由:
  * / - Dashboard 首页
  * /lobster/:id - LobsterDetail 详情页

交互流程:
1. 首页点击'详情' → 进入龙虾详情页
2. 详情页点击'日新' → 打开 MemoryModal 弹窗
3. 弹窗内切换标签:成才之路 / 工作记忆
4. 关闭弹窗 → 返回详情页

优点:
- 首页更简洁,一个卡片一个按钮
- 所有龙虾共用弹窗组件,无需 n 个路由
- 功能整合,逻辑更清晰
- 更符合 React 组件化设计

📖 苟日新,日日新,又日新
2026-04-02 14:15:46 +08:00

183 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
const API_BASE = 'http://localhost:8000/api';
function Dashboard() {
const [lobsters, setLobsters] = useState([]);
const [loading, setLoading] = useState(true);
const navigate = useNavigate();
useEffect(() => {
fetchLobsters();
const interval = setInterval(fetchLobsters, 5000);
return () => clearInterval(interval);
}, []);
const fetchLobsters = async () => {
try {
const response = await axios.get(`${API_BASE}/lobsters/`);
setLobsters(response.data);
setLoading(false);
} catch (error) {
console.error('获取龙虾状态失败:', error);
}
};
if (loading) {
return <div className="loading">加载中...</div>;
}
return (
<div className="dashboard">
<h1>🦞 龙虾舰队监控中心</h1>
<div className="lobster-grid">
{lobsters.map(lobster => (
<div key={lobster.id} className="lobster-card">
<div className="lobster-header">
<span className="lobster-name">{lobster.emoji} {lobster.name}</span>
<span className={`status status-${lobster.status}`}>{lobster.status}</span>
</div>
<div className="lobster-info">
<p>专长{lobster.specialty}</p>
<p>端口{lobster.port}</p>
<p>容器{lobster.container}</p>
</div>
<div className="lobster-actions">
<button className="detail-btn" onClick={() => navigate(`/lobster/${lobster.id}`)}>
📊 详情
</button>
</div>
</div>
))}
</div>
</div>
);
}
export default Dashboard;
// CSS 样式
const styles = `
.dashboard {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.dashboard h1 {
color: #1a365d;
margin-bottom: 30px;
text-align: center;
}
.lobster-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 20px;
}
.lobster-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.lobster-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}
.lobster-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #e2e8f0;
}
.lobster-name {
font-size: 1.3em;
font-weight: bold;
color: #2d3748;
}
.status {
padding: 4px 12px;
border-radius: 20px;
font-size: 0.85em;
font-weight: 600;
}
.status-healthy {
background: #c6f6d5;
color: #22543d;
}
.status-warning {
background: #feebc8;
color: #744210;
}
.status-error {
background: #fed7d7;
color: #742a2a;
}
.lobster-info {
margin-bottom: 20px;
}
.lobster-info p {
margin: 8px 0;
color: #4a5568;
display: flex;
justify-content: space-between;
}
.lobster-actions {
display: flex;
gap: 10px;
}
.lobster-actions button {
flex: 1;
padding: 10px 16px;
border: none;
border-radius: 8px;
font-size: 1em;
cursor: pointer;
transition: all 0.2s;
font-weight: 600;
}
.detail-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.detail-btn:hover {
opacity: 0.9;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(102, 126, 234, 0.4);
}
.loading {
text-align: center;
padding: 50px;
color: #718096;
font-size: 1.2em;
}
`;
// 注入样式
if (typeof document !== 'undefined') {
const styleSheet = document.createElement('style');
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
}