diff --git a/code/backend/manage.py b/code/backend/manage.py
new file mode 100755
index 0000000..4cad47a
--- /dev/null
+++ b/code/backend/manage.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+import os
+import sys
+
+def main():
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django."
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+if __name__ == '__main__':
+ main()
diff --git a/code/frontend/package.json b/code/frontend/package.json
new file mode 100644
index 0000000..d975800
--- /dev/null
+++ b/code/frontend/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "lobster-monitor-frontend",
+ "version": "1.0.0",
+ "description": "龙虾舰队监控中心 - React 前端",
+ "private": true,
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-scripts": "5.0.1",
+ "axios": "^1.6.0"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/code/frontend/src/components/MemoryCalendar/index.js b/code/frontend/src/components/MemoryCalendar/index.js
new file mode 100644
index 0000000..9376d8b
--- /dev/null
+++ b/code/frontend/src/components/MemoryCalendar/index.js
@@ -0,0 +1,83 @@
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
+
+const API_BASE = 'http://localhost:8000/api';
+
+function MemoryCalendar({ lobsterId }) {
+ const [currentMonth, setCurrentMonth] = useState(new Date());
+ const [memoryDates, setMemoryDates] = useState([]);
+ const [selectedDate, setSelectedDate] = useState(null);
+ const [memoryContent, setMemoryContent] = useState('');
+
+ useEffect(() => {
+ fetchMemoryDates();
+ }, [currentMonth, lobsterId]);
+
+ const fetchMemoryDates = async () => {
+ try {
+ const response = await axios.get(`${API_BASE}/lobsters/${lobsterId}/memory/dates/`);
+ setMemoryDates(response.data.dates);
+ } catch (error) {
+ console.error('获取记忆日期失败:', error);
+ }
+ };
+
+ const fetchMemory = async (date) => {
+ try {
+ const response = await axios.get(`${API_BASE}/lobsters/${lobsterId}/memory/${date}/`);
+ setMemoryContent(response.data.content);
+ setSelectedDate(date);
+ } catch (error) {
+ console.error('获取记忆失败:', error);
+ }
+ };
+
+ const renderCalendar = () => {
+ const year = currentMonth.getFullYear();
+ const month = currentMonth.getMonth();
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
+ const days = [];
+
+ for (let day = 1; day <= daysInMonth; day++) {
+ const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
+ const hasMemory = memoryDates.includes(dateStr);
+
+ days.push(
+
hasMemory && fetchMemory(dateStr)}
+ >
+ {day}
+ {hasMemory && }
+
+ );
+ }
+
+ return days;
+ };
+
+ return (
+
+
+
+ {currentMonth.getFullYear()}年 {currentMonth.getMonth() + 1}月
+
+
+
+ {['日', '一', '二', '三', '四', '五', '六'].map(day => (
+
{day}
+ ))}
+ {renderCalendar()}
+
+ {memoryContent && (
+
+
📅 {selectedDate} 的记忆
+
{memoryContent}
+
+ )}
+
+ );
+}
+
+export default MemoryCalendar;
diff --git a/code/frontend/src/components/SearchBox/index.js b/code/frontend/src/components/SearchBox/index.js
new file mode 100644
index 0000000..70f1959
--- /dev/null
+++ b/code/frontend/src/components/SearchBox/index.js
@@ -0,0 +1,59 @@
+import React, { useState } from 'react';
+import axios from 'axios';
+
+const API_BASE = 'http://localhost:8000/api';
+
+function SearchBox({ lobsterId }) {
+ const [query, setQuery] = useState('');
+ const [results, setResults] = useState([]);
+ const [searching, setSearching] = useState(false);
+
+ const handleSearch = async (e) => {
+ e.preventDefault();
+ if (!query.trim()) return;
+
+ setSearching(true);
+ try {
+ const response = await axios.get(`${API_BASE}/lobsters/${lobsterId}/search/`, {
+ params: { q: query }
+ });
+ setResults(response.data.results);
+ } catch (error) {
+ console.error('搜索失败:', error);
+ } finally {
+ setSearching(false);
+ }
+ };
+
+ return (
+
+
+ {results.length > 0 && (
+
+
搜索结果 ({results.length})
+ {results.map((result, index) => (
+
+
{result.title}
+
{result.snippet}
+
+ 查看原文
+
+
+ ))}
+
+ )}
+
+ );
+}
+
+export default SearchBox;
diff --git a/code/frontend/src/components/ToolList/index.js b/code/frontend/src/components/ToolList/index.js
new file mode 100644
index 0000000..25aa1bf
--- /dev/null
+++ b/code/frontend/src/components/ToolList/index.js
@@ -0,0 +1,59 @@
+import React from 'react';
+
+function ToolList() {
+ const tools = [
+ {
+ name: 'Git 版本控制',
+ status: '🟢 运行中',
+ description: '代码版本管理服务',
+ access: [
+ { type: 'Web', url: 'http://localhost:18003' },
+ { type: 'Git', url: 'git://127.0.0.1:9418/monitor-dashboard.git' },
+ { type: 'HTTP', url: 'http://localhost:18003/monitor-dashboard.git' }
+ ],
+ usage: [
+ '克隆仓库:git clone http://localhost:18003/monitor-dashboard.git',
+ '提交更改:git add . && git commit -m "说明"',
+ '推送代码:git push origin master'
+ ]
+ }
+ ];
+
+ return (
+
+
🛠️ 工具列表
+ {tools.map((tool, index) => (
+
+
+
{tool.name}
+ {tool.status}
+
+
{tool.description}
+
+
+
访问方式:
+ {tool.access.map((item, i) => (
+
+ ))}
+
+
+
+
使用方法:
+
+ {tool.usage.map((item, i) => (
+ {item}
+ ))}
+
+
+
+ ))}
+
+ );
+}
+
+export default ToolList;
diff --git a/code/frontend/src/pages/Dashboard/index.js b/code/frontend/src/pages/Dashboard/index.js
new file mode 100644
index 0000000..95a4c64
--- /dev/null
+++ b/code/frontend/src/pages/Dashboard/index.js
@@ -0,0 +1,60 @@
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
+
+const API_BASE = 'http://localhost:8000/api';
+
+function Dashboard() {
+ const [lobsters, setLobsters] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ 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 加载中...
;
+ }
+
+ return (
+
+
🦞 龙虾舰队监控中心
+
+ {lobsters.map(lobster => (
+
+
+ {lobster.emoji} {lobster.name}
+ {lobster.status}
+
+
+
专长:{lobster.specialty}
+
端口:{lobster.port}
+
容器:{lobster.container}
+
+
+
+
+
+
+ ))}
+
+
+ );
+}
+
+export default Dashboard;