diff --git a/code/backend/backend/settings.py b/code/backend/backend/settings.py index bd6ee3b..727943f 100644 --- a/code/backend/backend/settings.py +++ b/code/backend/backend/settings.py @@ -104,3 +104,7 @@ REST_FRAMEWORK = { 'rest_framework.permissions.AllowAny', ] } + +# 端口配置(避免与会议厅冲突) +# 会议厅:3000/8000 +# 监控中心:4000/9000 diff --git a/code/frontend/src/components/MeetingRoom/index.js b/code/frontend/src/components/MeetingRoom/index.js new file mode 100644 index 0000000..e31d479 --- /dev/null +++ b/code/frontend/src/components/MeetingRoom/index.js @@ -0,0 +1,485 @@ +import React, { useState } from 'react'; + +const API_BASE = 'http://localhost:8000/api'; + +function MeetingRoom({ agents, onRefresh, onAgentToMeeting, onAgentFromMeeting }) { + const [meetingAgents, setMeetingAgents] = useState([]); + const [isMeeting, setIsMeeting] = useState(false); + const [meetingTopic, setMeetingTopic] = useState(''); + const [meetingMinutes, setMeetingMinutes] = useState(''); + const [draggedAgent, setDraggedAgent] = useState(null); + + // 拖拽进入会议室 + const handleDragOver = (e) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + }; + + // 拖拽离开会议室 + const handleDragLeave = () => { + // 不做任何操作 + }; + + // 放入会议室 + const handleDrop = async (e) => { + e.preventDefault(); + + const agentId = e.dataTransfer.getData('agentId'); + const agentName = e.dataTransfer.getData('agentName'); + const agentEmoji = e.dataTransfer.getData('agentEmoji'); + + if (agentId) { + const agent = { + id: parseInt(agentId), + name: agentName, + emoji: agentEmoji, + seat: meetingAgents.length, // 自动分配座位 + }; + + // 添加到会议室 + if (!meetingAgents.find(a => a.id === agent.id)) { + setMeetingAgents([...meetingAgents, agent]); + + // 通知父组件 + if (onAgentToMeeting) { + onAgentToMeeting(parseInt(agentId)); + } + } + } + }; + + // 从会议室拖出 + const handleSeatDragStart = (e, agent) => { + e.dataTransfer.setData('agentId', agent.id.toString()); + e.dataTransfer.setData('agentName', agent.name); + e.dataTransfer.setData('agentEmoji', agent.emoji); + e.dataTransfer.effectAllowed = 'move'; + + // 从会议室移除 + setMeetingAgents(meetingAgents.filter(a => a.id !== agent.id)); + + // 通知父组件 + if (onAgentFromMeeting) { + onAgentFromMeeting(agent.id); + } + }; + + // 开始会议 + const startMeeting = () => { + if (meetingAgents.length === 0) { + alert('🏛️ 请先拖入至少一只龙虾!'); + return; + } + + setIsMeeting(true); + + // 生成会议发言 + const speeches = meetingAgents.map(agent => { + const speech = generateSpeech(agent); + return `${agent.emoji} **${agent.name}**: ${speech}`; + }); + + const minutes = ` +# 🏛️ 龙虾议事厅会议纪要 + +**时间**: ${new Date().toLocaleString('zh-CN')} +**主题**: ${meetingTopic || '例行会议'} +**参会**: ${meetingAgents.map(a => a.name).join('、')} + +--- + +## 📝 发言记录 + +${speeches.join('\n\n')} + +--- + +## ✅ 会议决议 + +待生成... + +--- + +*Generated by Agent Diary* 🦀 + `; + + setMeetingMinutes(minutes); + }; + + // 生成发言(根据专长) + const generateSpeech = (agent) => { + const speeches = { + '飞行侠': '作为主力,我会继续全力支持各项工作!大家有什么问题都可以找我!', + '道童': '道德经云:道可道,非常道。我认为我们应该顺应自然,无为而治。', + '墨子': '兼爱非攻!从技术角度来说,我建议优化架构,提高效率。', + '织网者': '网站建设方面,我建议加强用户体验,优化界面设计。', + '费曼': '从物理学角度看,我们应该找到最简单的解决方案。', + '守望者': '监控数据显示,系统运行稳定。我会继续密切关注。', + '白泽': '作为秘书,我会做好协调工作,确保信息畅通。', + '谛听': '我收集到的情报显示,用户对我们的功能很满意!', + }; + + return speeches[agent.name] || '我会尽力完成我的任务!'; + }; + + // 导出纪要 + const exportMinutes = () => { + if (!meetingMinutes) { + alert('📝 请先开始会议!'); + return; + } + + const blob = new Blob([meetingMinutes], { type: 'text/markdown' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `会议纪要-${new Date().toISOString().split('T')[0]}.md`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }; + + // 清空会议室 + const clearRoom = () => { + setMeetingAgents([]); + setIsMeeting(false); + setMeetingMinutes(''); + }; + + return ( +
🪑 拖拽龙虾到会议室
+从下方拖入龙虾,围坐开会
+{meetingMinutes}
+ 正在加载议事厅...
+专长:{agent.specialty}
+端口:{agent.port}
+