🎨 飞行侠完善 P1 功能:座位图 + @Agent + 会议纪要
新增功能: - 座位可视化 - 圆形头像展示参会者 - @Agent 功能 - 定向消息给特定 Agent - 会议纪要生成 - Web 界面一键生成 - 参会者列表 API 文件变更: - meetings/views.py: mention_agent() 新接口 - templates/meeting_room.html: - 座位图 UI(圆形头像) - 生成纪要按钮 - @Agent 按钮 - test_mention.py: @Agent 测试脚本 测试结果: ✅ 完整功能测试 (7 项) ✅ 会议纪要测试 (JSON + Markdown) ✅ @Agent 功能测试
This commit is contained in:
@@ -217,18 +217,38 @@
|
||||
<input type="text" id="agentEmoji" placeholder="🦸" value="🦸">
|
||||
</div>
|
||||
<button class="btn" onclick="checkInbox()" style="width: 100%; margin-bottom: 10px;">📬 查阅信箱</button>
|
||||
<button class="btn" onclick="agentReply()" style="width: 100%;">💬 回复消息</button>
|
||||
<button class="btn" onclick="agentReply()" style="width: 100%; margin-bottom: 10px;">💬 回复消息</button>
|
||||
<button class="btn" onclick="mentionAgent()" style="width: 100%; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">📍 @Agent</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 座位图 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<h3 style="margin-bottom: 15px;">🪑 座位图 <span id="participantCount" class="badge">0</span></h3>
|
||||
<div class="messages" id="seatMap" style="min-height: 100px;">
|
||||
<p style="text-align: center; color: #999; padding: 20px;">暂无参会者</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<h3 style="margin-bottom: 15px;">💬 消息列表 <span id="messageCount" class="badge">0</span></h3>
|
||||
<div class="messages" id="messageList">
|
||||
<p style="text-align: center; color: #999; padding: 40px;">暂无消息,开始聊天吧!</p>
|
||||
</div>
|
||||
<button class="btn" onclick="loadMessages()" style="margin-top: 15px;">🔄 刷新消息</button>
|
||||
<div style="margin-top: 15px; display: flex; gap: 10px;">
|
||||
<button class="btn" onclick="loadMessages()">🔄 刷新</button>
|
||||
<button class="btn" onclick="loadParticipants()" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">👥 刷新座位</button>
|
||||
<button class="btn" onclick="generateMinutes()" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">📋 生成纪要</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 会议纪要显示区 -->
|
||||
<div id="minutesDisplay" style="margin-top: 20px; display: none;">
|
||||
<h3 style="margin-bottom: 15px;">📋 会议纪要</h3>
|
||||
<div class="messages" id="minutesContent" style="white-space: pre-wrap; font-family: monospace;"></div>
|
||||
<button class="btn" onclick="document.getElementById('minutesDisplay').style.display='none'" style="margin-top: 15px;">❌ 关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -499,6 +519,115 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadParticipants() {
|
||||
if (!currentMeetingId) {
|
||||
showStatus('❌ 请先创建或加入会议', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/meetings/${currentMeetingId}/participants/`);
|
||||
const participants = await res.json();
|
||||
|
||||
document.getElementById('participantCount').textContent = participants.length;
|
||||
|
||||
if (participants.length === 0) {
|
||||
document.getElementById('seatMap').innerHTML =
|
||||
'<p style="text-align: center; color: #999; padding: 20px;">暂无参会者</p>';
|
||||
} else {
|
||||
// 圆桌座位布局
|
||||
document.getElementById('seatMap').innerHTML = `
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; padding: 20px;">
|
||||
${participants.map(p => `
|
||||
<div style="
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
border-radius: 50%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||
">
|
||||
<div style="font-size: 24px; margin-bottom: 5px;">${p.agent_emoji || '👤'}</div>
|
||||
<div style="font-weight: 600;">${p.nickname || '参会者'}</div>
|
||||
${p.is_host ? '<div style="font-size: 10px; opacity: 0.8;">👑 主持</div>' : ''}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} catch (e) {
|
||||
showStatus(`❌ 加载座位失败:${e.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function generateMinutes() {
|
||||
if (!currentMeetingId) {
|
||||
showStatus('❌ 请先创建或加入会议', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/meetings/${currentMeetingId}/minutes/?output=markdown`);
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok && data.markdown) {
|
||||
document.getElementById('minutesContent').textContent = data.markdown;
|
||||
document.getElementById('minutesDisplay').style.display = 'block';
|
||||
showStatus('✅ 会议纪要已生成!', 'success');
|
||||
} else {
|
||||
showStatus(`❌ 生成失败:${data.error || '未知错误'}`, 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
showStatus(`❌ 请求失败:${e.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function mentionAgent() {
|
||||
const meetingId = currentMeetingId || prompt('请输入会议 ID:');
|
||||
const targetAgentId = prompt('@哪个 Agent?输入 agent_id:');
|
||||
const content = document.getElementById('messageContent').value;
|
||||
const senderAgentId = document.getElementById('agentId').value || 'human';
|
||||
const senderName = document.getElementById('agentName').value || '用户';
|
||||
const senderEmoji = document.getElementById('agentEmoji').value || '👤';
|
||||
|
||||
if (!meetingId || !targetAgentId || !content) {
|
||||
showStatus('❌ 请填写完整信息', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/meetings/${meetingId}/mention_agent/`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
target_agent_id: targetAgentId,
|
||||
content: content,
|
||||
sender_agent_id: senderAgentId,
|
||||
sender_name: senderName,
|
||||
sender_emoji: senderEmoji
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
document.getElementById('messageContent').value = '';
|
||||
showStatus(`✅ 已 @${targetAgentId}`, 'success');
|
||||
loadMessages();
|
||||
} else {
|
||||
showStatus(`❌ ${data.error || '发送失败'}`, 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
showStatus(`❌ 请求失败:${e.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 自动登录(如果记得凭证)
|
||||
document.getElementById('username').value = 'test';
|
||||
document.getElementById('password').value = 'test123';
|
||||
|
||||
Reference in New Issue
Block a user