2026-04-11 11:51:54 +08:00
<!DOCTYPE html>
< html lang = "zh-CN" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2026-04-12 16:43:39 +08:00
< title > AI 对话系统 v2.0< / title >
2026-04-11 11:51:54 +08:00
< link href = "https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel = "stylesheet" >
2026-04-12 19:11:50 +08:00
< link href = "https://cdn.jsdelivr.net/npm/github-markdown-css@5.2.0/github-markdown-light.min.css" rel = "stylesheet" >
2026-04-11 11:51:54 +08:00
< style >
2026-04-12 16:43:39 +08:00
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; height: 100vh; display: flex; }
.sidebar { width: 260px; background: #202123; color: #fff; display: flex; flex-direction: column; }
.sidebar-header { padding: 16px; border-bottom: 1px solid #4d4d4f; }
.new-chat-btn { width: 100%; padding: 12px 16px; background: transparent; border: 1px solid #4d4d4f; border-radius: 6px; color: #fff; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 14px; transition: background 0.2s; }
.new-chat-btn:hover { background: #2a2b32; }
.conversation-list { flex: 1; overflow-y: auto; padding: 8px; }
.conversation-item { padding: 12px; border-radius: 6px; cursor: pointer; display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; color: #ececf1; }
.conversation-item:hover { background: #2a2b32; }
.conversation-item.active { background: #343541; }
.conversation-item .title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.conversation-item .delete-btn { opacity: 0; background: none; border: none; color: #999; cursor: pointer; padding: 4px; }
.conversation-item:hover .delete-btn { opacity: 1; }
.main-content { flex: 1; display: flex; flex-direction: column; background: #fff; }
.chat-header { padding: 16px 24px; border-bottom: 1px solid #e5e5e5; display: flex; align-items: center; justify-content: space-between; }
.chat-header h1 { font-size: 18px; font-weight: 600; }
.header-controls { display: flex; align-items: center; gap: 16px; }
.agent-selector { display: flex; align-items: center; gap: 8px; }
2026-04-12 19:11:50 +08:00
.agent-selector select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; cursor: pointer; min-width: 150px; }
.ws-status { font-size: 12px; color: #666; background: #f0f0f0; padding: 4px 8px; border-radius: 4px; }
.ws-status.connected { color: #10a37f; }
2026-04-12 16:43:39 +08:00
.messages-container { flex: 1; overflow-y: auto; padding: 24px; }
.message { max-width: 800px; margin: 0 auto 24px; display: flex; gap: 16px; }
.message-avatar { width: 36px; height: 36px; border-radius: 4px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 18px; }
.message.user .message-avatar { background: #5436da; color: #fff; }
.message.assistant .message-avatar { background: #19c37d; color: #fff; }
2026-04-12 19:11:50 +08:00
.message-body { flex: 1; }
.message-content { line-height: 1.6; position: relative; }
2026-04-12 16:43:39 +08:00
2026-04-12 19:11:50 +08:00
/* Markdown样式 */
.markdown-body { font-size: 15px; }
.markdown-body pre { background: #f6f8fa; border-radius: 6px; padding: 16px; overflow-x: auto; }
.markdown-body code { background: #f6f8fa; padding: 2px 6px; border-radius: 4px; font-size: 85%; }
.markdown-body pre code { background: transparent; padding: 0; }
.markdown-body blockquote { border-left: 4px solid #dfe2e5; padding-left: 16px; color: #6a737d; }
/* 用户消息样式 */
.user-message-text { background: #f0f0f0; padding: 12px 16px; border-radius: 12px; font-size: 15px; }
2026-04-12 16:43:39 +08:00
2026-04-12 19:11:50 +08:00
/* 复制按钮 */
.copy-btn { position: absolute; top: 8px; right: 8px; padding: 4px 8px; background: rgba(0,0,0,0.05); border: 1px solid #ddd; border-radius: 4px; cursor: pointer; font-size: 12px; color: #666; opacity: 0; transition: opacity 0.2s; }
.message-content:hover .copy-btn { opacity: 1; }
.copy-btn:hover { background: rgba(0,0,0,0.1); }
.copy-btn.copied { color: #10a37f; border-color: #10a37f; }
/* 思考内容样式 */
.thinking-block { background: #f8f9fa; border-left: 3px solid #667eea; padding: 12px 16px; margin: 8px 0 16px 0; font-size: 14px; color: #666; border-radius: 4px; position: relative; }
.thinking-header { display: flex; align-items: center; justify-content: space-between; cursor: pointer; }
.thinking-header span { font-weight: 500; color: #667eea; }
.thinking-toggle { font-size: 12px; color: #667eea; }
.thinking-content { margin-top: 12px; display: none; }
.thinking-content.expanded { display: block; }
/* Agent信息 */
.agent-info { font-size: 12px; color: #999; margin-top: 8px; }
/* 输入区域 */
.input-container { padding: 16px 24px; border-top: 1px solid #e5e5e5; }
.input-area { max-width: 800px; margin: 0 auto; display: flex; gap: 12px; align-items: flex-start; }
.input-box { flex: 1; display: flex; gap: 8px; align-items: flex-end; }
.input-box textarea { flex: 1; padding: 12px 16px; border: 1px solid #ddd; border-radius: 12px; font-size: 16px; resize: none; outline: none; max-height: 200px; line-height: 1.5; }
.input-box textarea:focus { border-color: #10a37f; }
2026-04-12 16:43:39 +08:00
.send-btn { width: 48px; height: 48px; border-radius: 12px; background: #10a37f; border: none; color: #fff; cursor: pointer; font-size: 20px; display: flex; align-items: center; justify-content: center; transition: background 0.2s; }
.send-btn:hover { background: #0d8c6d; }
.send-btn:disabled { background: #ccc; cursor: not-allowed; }
2026-04-12 19:11:50 +08:00
/* 快捷语句 - 右侧 */
.quick-phrases-panel { width: 200px; padding: 8px; background: #f8f9fa; border-radius: 12px; max-height: 300px; overflow-y: auto; }
.quick-phrases-title { font-size: 12px; color: #999; margin-bottom: 8px; display: flex; align-items: center; justify-content: space-between; }
.quick-phrases-title button { background: none; border: 1px solid #ddd; padding: 2px 6px; border-radius: 4px; cursor: pointer; font-size: 12px; }
.quick-phrases-list { display: flex; flex-direction: column; gap: 6px; }
.quick-phrase-item { padding: 8px 12px; background: #fff; border: 1px solid #e0e0e0; border-radius: 6px; cursor: pointer; font-size: 13px; color: #333; transition: all 0.2s; position: relative; }
.quick-phrase-item:hover { background: #e8f5e9; border-color: #10a37f; }
.quick-phrase-item .phrase-delete { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); opacity: 0; cursor: pointer; color: #999; }
.quick-phrase-item:hover .phrase-delete { opacity: 1; }
/* 弹窗 */
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: none; align-items: center; justify-content: center; z-index: 1000; }
.modal-overlay.show { display: flex; }
.modal-box { background: #fff; padding: 24px; border-radius: 12px; max-width: 400px; width: 90%; }
.modal-box h3 { margin-bottom: 16px; }
.modal-box input { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 8px; margin-bottom: 16px; }
.modal-buttons { display: flex; gap: 12px; justify-content: flex-end; }
.modal-buttons button { padding: 8px 16px; border-radius: 8px; cursor: pointer; }
2026-04-12 16:43:39 +08:00
.welcome { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; }
.welcome h2 { font-size: 28px; margin-bottom: 16px; color: #333; }
2026-04-12 19:11:50 +08:00
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 3px; }
2026-04-11 11:51:54 +08:00
< / style >
< / head >
< body >
< div class = "sidebar" >
< div class = "sidebar-header" >
2026-04-12 19:11:50 +08:00
< button class = "new-chat-btn" onclick = "createNewConversation()" > < i class = "ri-add-line" > < / i > 新对话< / button >
2026-04-11 11:51:54 +08:00
< / div >
2026-04-12 19:11:50 +08:00
< div class = "conversation-list" id = "conversationList" > < / div >
2026-04-11 11:51:54 +08:00
< / div >
< div class = "main-content" >
< div class = "chat-header" >
2026-04-12 16:43:39 +08:00
< h1 > AI 对话 v2.0< / h1 >
< div class = "header-controls" >
< div class = "agent-selector" >
2026-04-12 19:11:50 +08:00
< select id = "agentSelect" onchange = "switchAgent()" > < option value = "" > 加载中...< / option > < / select >
2026-04-12 16:43:39 +08:00
< / div >
2026-04-12 19:11:50 +08:00
< div class = "ws-status" id = "wsStatus" > 连接中...< / div >
2026-04-12 16:43:39 +08:00
< / div >
2026-04-11 11:51:54 +08:00
< / div >
< div class = "messages-container" id = "messagesContainer" >
2026-04-12 19:11:50 +08:00
< div class = "welcome" > < h2 > 👋 开始对话< / h2 > < p > 选择Agent, 开始聊天< / p > < / div >
2026-04-11 11:51:54 +08:00
< / div >
< div class = "input-container" >
2026-04-12 19:11:50 +08:00
< div class = "input-area" >
< div class = "input-box" >
< textarea id = "messageInput" placeholder = "输入消息..." rows = "1" > < / textarea >
< button class = "send-btn" id = "sendBtn" onclick = "sendMessage()" > < i class = "ri-send-plane-fill" > < / i > < / button >
< / div >
<!-- 快捷语句右侧 -->
< div class = "quick-phrases-panel" >
< div class = "quick-phrases-title" >
< span > < i class = "ri-flashlight-line" > < / i > 快捷语句< / span >
< button onclick = "showAddPhraseModal()" > < i class = "ri-add-line" > < / i > < / button >
< / div >
< div class = "quick-phrases-list" id = "quickPhrasesList" > < / div >
2026-04-12 16:43:39 +08:00
< / div >
< / div >
< / div >
< / div >
<!-- 添加快捷语句弹窗 -->
2026-04-12 19:11:50 +08:00
< div class = "modal-overlay" id = "phraseModal" >
< div class = "modal-box" >
2026-04-12 16:43:39 +08:00
< h3 > < i class = "ri-add-line" > < / i > 添加快捷语句< / h3 >
2026-04-12 19:11:50 +08:00
< input type = "text" id = "newPhraseInput" placeholder = "输入内容..." maxlength = "100" >
< div class = "modal-buttons" >
< button style = "background:#f5f5f5;border:1px solid #ddd;" onclick = "hidePhraseModal()" > 取消< / button >
< button style = "background:#10a37f;color:#fff;border:none;" onclick = "addPhrase()" > 添加< / button >
2026-04-12 16:43:39 +08:00
< / div >
2026-04-11 11:51:54 +08:00
< / div >
< / div >
2026-04-12 19:11:50 +08:00
<!-- Markdown渲染库 -->
< script src = "https://cdn.jsdelivr.net/npm/marked/marked.min.js" > < / script >
2026-04-11 11:51:54 +08:00
< script >
2026-04-12 19:11:50 +08:00
marked.setOptions({ breaks: true, gfm: true });
2026-04-11 11:51:54 +08:00
let ws = null;
2026-04-12 16:43:39 +08:00
let userId = 'main_user';
2026-04-11 11:51:54 +08:00
let currentConversationId = null;
2026-04-12 16:43:39 +08:00
let currentAgentId = null;
let agents = [];
let quickPhrases = [];
2026-04-11 11:51:54 +08:00
document.addEventListener('DOMContentLoaded', () => {
2026-04-12 16:43:39 +08:00
loadAgents();
loadQuickPhrases();
2026-04-11 11:51:54 +08:00
connectWebSocket();
loadConversations();
setupTextarea();
});
2026-04-12 19:11:50 +08:00
// 加载Agent
2026-04-12 16:43:39 +08:00
async function loadAgents() {
try {
const res = await fetch('/api/v2/agents');
const data = await res.json();
agents = data.agents || [];
const defaultAgent = agents.find(a => a.is_default) || agents[0];
if (defaultAgent) currentAgentId = defaultAgent.id;
2026-04-12 17:58:47 +08:00
renderAgentSelect();
2026-04-12 19:11:50 +08:00
} catch (e) { console.error('加载Agent失败:', e); }
2026-04-12 16:43:39 +08:00
}
2026-04-12 19:11:50 +08:00
function renderAgentSelect() {
2026-04-12 17:06:39 +08:00
const select = document.getElementById('agentSelect');
select.innerHTML = agents.filter(a => a.is_active).map(a =>
2026-04-12 19:11:50 +08:00
`< option value = "${a.id}" $ { a . id = == currentAgentId ? ' selected ' : ' ' } > ${a.display_name || a.name}< / option > `
2026-04-12 17:06:39 +08:00
).join('');
}
function updateAgentSelect(agentId) {
currentAgentId = agentId;
2026-04-12 19:11:50 +08:00
document.getElementById('agentSelect').value = agentId;
2026-04-12 17:06:39 +08:00
}
2026-04-12 17:01:05 +08:00
async function switchAgent() {
2026-04-12 19:11:50 +08:00
const newAgentId = parseInt(document.getElementById('agentSelect').value);
if (newAgentId & & newAgentId !== currentAgentId) {
currentAgentId = newAgentId;
if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ action: 'switch_agent', agent_id: currentAgentId }));
2026-04-12 17:01:05 +08:00
await createNewConversation();
2026-04-12 19:11:50 +08:00
showAgentSwitchNotice();
2026-04-12 16:43:39 +08:00
}
}
2026-04-12 19:11:50 +08:00
function showAgentSwitchNotice() {
const agent = agents.find(a => a.id === currentAgentId);
document.getElementById('messagesContainer').innerHTML = `
< div class = "welcome" > < h2 > 🔄 已切换 Agent< / h2 > < p style = "color:#667eea;font-size:18px;" > < strong > ${agent?.display_name || '助手'}< / strong > < / p > < / div >
2026-04-12 16:43:39 +08:00
`;
}
2026-04-12 19:11:50 +08:00
// WebSocket
2026-04-11 11:51:54 +08:00
function connectWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
2026-04-12 19:11:50 +08:00
ws = new WebSocket(`${protocol}//${window.location.host}/ws/${userId}`);
ws.onopen = () => { document.getElementById('wsStatus').textContent = '已连接'; document.getElementById('wsStatus').classList.add('connected'); };
ws.onmessage = (e) => handleWebSocketMessage(JSON.parse(e.data));
ws.onclose = () => { document.getElementById('wsStatus').textContent = '断开'; document.getElementById('wsStatus').classList.remove('connected'); setTimeout(connectWebSocket, 3000); };
ws.onerror = () => document.getElementById('wsStatus').textContent = '错误';
2026-04-11 11:51:54 +08:00
}
function handleWebSocketMessage(data) {
switch (data.type) {
2026-04-12 19:11:50 +08:00
case 'history': displayHistory(data.messages); if (data.agent_id) updateAgentSelect(data.agent_id); break;
case 'conversation_created': currentConversationId = data.conversation_id; loadConversations(); break;
case 'new_conversation': currentConversationId = data.conversation_id; loadConversations(); clearMessages(); break;
case 'agent_switched': currentAgentId = data.agent_id; break;
case 'stream_end': document.getElementById('sendBtn').disabled = false; break;
case 'user_message': appendMessage('user', data.message.content); break;
case 'assistant_message': appendMessage('assistant', data.message.content, data.message.thinking_content, data.message.agent_name); document.getElementById('sendBtn').disabled = false; break;
case 'error': showError(data.message); document.getElementById('sendBtn').disabled = false; break;
2026-04-11 11:51:54 +08:00
}
}
2026-04-12 19:11:50 +08:00
// 消息渲染
function appendMessage(role, content, thinking = null, agentName = null) {
2026-04-11 11:51:54 +08:00
const container = document.getElementById('messagesContainer');
2026-04-12 16:43:39 +08:00
container.querySelector('.welcome')?.remove();
2026-04-11 11:51:54 +08:00
2026-04-12 16:43:39 +08:00
const div = document.createElement('div');
div.className = `message ${role}`;
2026-04-11 11:51:54 +08:00
const avatar = role === 'user' ? '👤' : '🤖';
2026-04-12 19:11:50 +08:00
let html = `< div class = "message-avatar" > ${avatar}< / div > < div class = "message-body" > `;
2026-04-11 11:51:54 +08:00
2026-04-12 16:43:39 +08:00
// 思考内容
if (thinking) {
2026-04-12 19:11:50 +08:00
html += `< div class = "thinking-block" >
< div class = "thinking-header" onclick = "toggleThinking(this)" >
< span > < i class = "ri-lightbulb-line" > < / i > 思考过程< / span >
< span class = "thinking-toggle" > 点击展开< / span >
< / div >
< div class = "thinking-content" > ${escapeHtml(thinking)}< / div >
2026-04-12 16:43:39 +08:00
< / div > `;
}
2026-04-12 19:11:50 +08:00
// 消息内容
html += `< div class = "message-content" > `;
if (role === 'assistant') {
// AI消息用Markdown渲染
html += `< div class = "markdown-body" > ${marked.parse(content)}< / div > `;
} else {
// 用户消息普通显示
html += `< div class = "user-message-text" > ${escapeHtml(content)}< / div > `;
}
// 复制按钮
html += `< button class = "copy-btn" onclick = "copyMessage(this, '${escapeHtml(content).replace(/'/g, " \ \ ' " ) } ' ) " > < i class = "ri-copy-line" > < / i > 复制< / button > `;
html += `< / div > `;
2026-04-11 11:51:54 +08:00
2026-04-12 16:43:39 +08:00
// Agent信息
if (role === 'assistant' & & agentName) {
2026-04-12 19:11:50 +08:00
html += `< div class = "agent-info" > < i class = "ri-robot-line" > < / i > ${agentName}< / div > `;
2026-04-12 16:43:39 +08:00
}
html += '< / div > ';
div.innerHTML = html;
container.appendChild(div);
2026-04-11 11:51:54 +08:00
container.scrollTop = container.scrollHeight;
}
2026-04-12 19:11:50 +08:00
function toggleThinking(header) {
const block = header.parentElement;
const content = block.querySelector('.thinking-content');
const toggle = header.querySelector('.thinking-toggle');
if (content.classList.contains('expanded')) {
content.classList.remove('expanded');
toggle.textContent = '点击展开';
2026-04-12 16:43:39 +08:00
} else {
2026-04-12 19:11:50 +08:00
content.classList.add('expanded');
toggle.textContent = '点击折叠';
2026-04-12 16:43:39 +08:00
}
}
2026-04-12 19:11:50 +08:00
function copyMessage(btn, text) {
navigator.clipboard.writeText(text).then(() => {
btn.innerHTML = '< i class = "ri-check-line" > < / i > 已复制';
btn.classList.add('copied');
setTimeout(() => { btn.innerHTML = '< i class = "ri-copy-line" > < / i > 复制'; btn.classList.remove('copied'); }, 2000);
});
}
function escapeHtml(text) {
return text.replace(/& /g, '& ').replace(/< /g, '< ').replace(/>/g, '> ');
}
function displayHistory(messages) {
const container = document.getElementById('messagesContainer');
container.innerHTML = '';
messages.forEach(m => appendMessage(m.role, m.content, m.thinking_content));
}
function clearMessages() {
document.getElementById('messagesContainer').innerHTML = '< div class = "welcome" > < h2 > 👋 开始对话< / h2 > < / div > ';
2026-04-11 11:51:54 +08:00
}
2026-04-12 19:11:50 +08:00
function showError(msg) {
const container = document.getElementById('messagesContainer');
const div = document.createElement('div');
div.className = 'message assistant';
div.innerHTML = `< div class = "message-avatar" > ❌< / div > < div class = "message-body" > < div class = "message-content" style = "color:#dc3545;" > ${msg}< / div > < / div > `;
container.appendChild(div);
}
// 会话管理
2026-04-11 11:51:54 +08:00
async function loadConversations() {
2026-04-12 19:11:50 +08:00
const res = await fetch('/api/conversations');
const data = await res.json();
const conversations = data.conversations || [];
renderConversations(conversations);
if (!currentConversationId & & conversations.length > 0) selectConversation(conversations[0].id);
2026-04-11 11:51:54 +08:00
}
2026-04-12 19:11:50 +08:00
function renderConversations(list) {
2026-04-11 11:51:54 +08:00
const container = document.getElementById('conversationList');
2026-04-12 19:11:50 +08:00
if (list.length === 0) { container.innerHTML = '< div style = "text-align:center;color:#999;padding:40px;" > 暂无对话< / div > '; return; }
container.innerHTML = list.map(c => `
2026-04-12 16:43:39 +08:00
< div class = "conversation-item ${c.id === currentConversationId ? 'active' : ''}" onclick = "selectConversation('${c.id}')" >
2026-04-12 19:11:50 +08:00
< span class = "title" > ${c.title || '新对话'}< / span >
< button class = "delete-btn" onclick = "deleteConversation('${c.id}',event)" > < i class = "ri-delete-bin-line" > < / i > < / button >
2026-04-11 11:51:54 +08:00
< / div >
`).join('');
}
2026-04-12 16:43:39 +08:00
function selectConversation(id) {
currentConversationId = id;
2026-04-12 19:11:50 +08:00
renderConversations([]);
loadConversations();
if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ action: 'select_conversation', conversation_id: id }));
2026-04-11 11:51:54 +08:00
}
async function createNewConversation() {
2026-04-12 19:11:50 +08:00
const res = await fetch('/api/conversations', { method: 'POST' });
const data = await res.json();
currentConversationId = data.id;
clearMessages();
loadConversations();
2026-04-11 11:51:54 +08:00
}
2026-04-12 19:11:50 +08:00
async function deleteConversation(id, e) {
e.stopPropagation();
2026-04-12 16:43:39 +08:00
if (!confirm('确定删除?')) return;
2026-04-12 19:11:50 +08:00
await fetch(`/api/conversations/${id}`, { method: 'DELETE' });
if (id === currentConversationId) { currentConversationId = null; clearMessages(); }
loadConversations();
2026-04-11 11:51:54 +08:00
}
// 发送消息
function sendMessage() {
const input = document.getElementById('messageInput');
2026-04-12 16:43:39 +08:00
const msg = input.value.trim();
if (!msg) return;
2026-04-11 11:51:54 +08:00
document.getElementById('sendBtn').disabled = true;
input.value = '';
input.style.height = 'auto';
2026-04-12 16:43:39 +08:00
if (ws?.readyState === WebSocket.OPEN) {
2026-04-12 19:11:50 +08:00
ws.send(JSON.stringify({ action: 'chat', message: msg, conversation_id: currentConversationId, agent_id: currentAgentId }));
2026-04-11 11:51:54 +08:00
}
}
function setupTextarea() {
const textarea = document.getElementById('messageInput');
2026-04-12 19:11:50 +08:00
textarea.addEventListener('keydown', e => { if (e.key === 'Enter' & & !e.shiftKey) { e.preventDefault(); sendMessage(); } });
textarea.addEventListener('input', () => { textarea.style.height = 'auto'; textarea.style.height = Math.min(textarea.scrollHeight, 200) + 'px'; });
2026-04-11 11:51:54 +08:00
}
2026-04-12 16:43:39 +08:00
2026-04-12 19:11:50 +08:00
// 快捷语句
function loadQuickPhrases() {
const saved = localStorage.getItem('quickPhrases');
quickPhrases = saved ? JSON.parse(saved) : ['请帮我总结一下', '帮我分析这个问题', '用简单的话解释一下', '给我举个例子', '有什么建议吗?'];
renderQuickPhrases();
}
function renderQuickPhrases() {
const container = document.getElementById('quickPhrasesList');
container.innerHTML = quickPhrases.map((p, i) => `
< div class = "quick-phrase-item" onclick = "usePhrase('${escapeHtml(p)}')" > ${p}< span class = "phrase-delete" onclick = "deletePhrase(${i},event)" > < i class = "ri-close-line" > < / i > < / span > < / div >
`).join('');
}
function usePhrase(text) {
document.getElementById('messageInput').value = text;
document.getElementById('messageInput').focus();
}
function deletePhrase(index, e) {
e.stopPropagation();
quickPhrases.splice(index, 1);
localStorage.setItem('quickPhrases', JSON.stringify(quickPhrases));
renderQuickPhrases();
}
function showAddPhraseModal() {
document.getElementById('phraseModal').classList.add('show');
document.getElementById('newPhraseInput').value = '';
document.getElementById('newPhraseInput').focus();
}
function hidePhraseModal() { document.getElementById('phraseModal').classList.remove('show'); }
function addPhrase() {
const phrase = document.getElementById('newPhraseInput').value.trim();
if (phrase) {
quickPhrases.push(phrase);
localStorage.setItem('quickPhrases', JSON.stringify(quickPhrases));
renderQuickPhrases();
hidePhraseModal();
}
}
document.getElementById('newPhraseInput').addEventListener('keydown', e => { if (e.key === 'Enter') addPhrase(); if (e.key === 'Escape') hidePhraseModal(); });
2026-04-11 11:51:54 +08:00
< / script >
< / body >
< / html >