Files
ai-chat-app/www/admin.js
hubian 3411de1612 feat: 后台管理系统 (Python Flask)
后台管理功能:
- 大模型接口配置管理(增删改、设为默认)
- 智能体管理(增删改、类别、标签、prompt、LLM配置、热度等)
- 搜索配置管理(Tavily等搜索接口)
- 系统设置(应用名称、版本、游客限制、管理员密码)
- 统计信息(LLM调用、搜索调用、智能体使用排行)

技术方案:
- Python Flask 后端服务 (端口 19020)
- SQLite 数据库存储配置
- 前端后台管理界面 (/admin)
- RESTful API 接口

后台入口:
- URL: http://localhost:19020/admin
- 默认账号: admin / admin123

前端配置获取:
- /api/config 获取LLM、搜索、智能体、系统配置
2026-04-27 12:34:01 +08:00

865 lines
30 KiB
JavaScript
Raw Permalink 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.
// AI助手 - 后台管理前端
const API_BASE = '';
let currentPage = 'stats';
let llmConfigs = [];
let agents = [];
let searchConfigs = [];
let systemConfigs = {};
// ==================== 登录 ====================
document.getElementById('loginBtn').addEventListener('click', async () => {
const username = document.getElementById('loginUsername').value.trim();
const password = document.getElementById('loginPassword').value;
if (!username || !password) {
showToast('请输入用户名和密码');
return;
}
try {
const res = await fetch(`${API_BASE}/api/admin/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await res.json();
if (data.success) {
localStorage.setItem('adminLoggedIn', 'true');
localStorage.setItem('adminUsername', username);
document.getElementById('loginPage').style.display = 'none';
document.getElementById('adminPage').style.display = 'block';
document.getElementById('adminName').textContent = username;
loadPage('stats');
} else {
showToast(data.error || '登录失败');
}
} catch (e) {
showToast('登录失败,请检查网络');
}
});
// 检查登录状态
if (localStorage.getItem('adminLoggedIn') === 'true') {
document.getElementById('loginPage').style.display = 'none';
document.getElementById('adminPage').style.display = 'block';
document.getElementById('adminName').textContent = localStorage.getItem('adminUsername');
loadPage('stats');
}
// 退出登录
document.getElementById('logoutBtn').addEventListener('click', () => {
localStorage.removeItem('adminLoggedIn');
localStorage.removeItem('adminUsername');
document.getElementById('adminPage').style.display = 'none';
document.getElementById('loginPage').style.display = 'flex';
});
// ==================== 侧边栏导航 ====================
document.querySelectorAll('.sidebar-item').forEach(item => {
item.addEventListener('click', () => {
const page = item.getAttribute('data-page');
document.querySelectorAll('.sidebar-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
loadPage(page);
});
});
// ==================== 页面加载 ====================
async function loadPage(page) {
currentPage = page;
const content = document.getElementById('mainContent');
switch (page) {
case 'stats':
await loadStatsPage(content);
break;
case 'llm':
await loadLLMPage(content);
break;
case 'agents':
await loadAgentsPage(content);
break;
case 'search':
await loadSearchPage(content);
break;
case 'system':
await loadSystemPage(content);
break;
}
}
// ==================== 统计页面 ====================
async function loadStatsPage(content) {
const stats = await fetchAPI('/api/admin/stats');
content.innerHTML = `
<div class="content-header">
<h1 class="content-title">统计信息</h1>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">🧠</div>
<div class="stat-value">${stats.llm_total_calls || 0}</div>
<div class="stat-label">LLM 总调用</div>
</div>
<div class="stat-card">
<div class="stat-icon">📊</div>
<div class="stat-value">${stats.llm_today_calls || 0}</div>
<div class="stat-label">今日 LLM 调用</div>
</div>
<div class="stat-card">
<div class="stat-icon">🔍</div>
<div class="stat-value">${stats.search_total_calls || 0}</div>
<div class="stat-label">搜索总调用</div>
</div>
<div class="stat-card">
<div class="stat-icon">📈</div>
<div class="stat-value">${stats.search_today_calls || 0}</div>
<div class="stat-label">今日搜索</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">🤖</div>
<div class="stat-value">${stats.agent_count || 0}</div>
<div class="stat-label">智能体数量</div>
</div>
<div class="stat-card">
<div class="stat-icon">🧠</div>
<div class="stat-value">${stats.llm_count || 0}</div>
<div class="stat-label">LLM 配置数量</div>
</div>
<div class="stat-card">
<div class="stat-icon">👥</div>
<div class="stat-value">${stats.total_users || 0}</div>
<div class="stat-label">注册用户数</div>
</div>
<div class="stat-card">
<div class="stat-icon">📅</div>
<div class="stat-value">${new Date().toLocaleDateString()}</div>
<div class="stat-label">统计日期</div>
</div>
</div>
<div class="data-table" style="margin-top: 24px;">
<h3 style="padding: 16px; background: var(--bg-color);">热门智能体使用排行</h3>
<table>
<thead>
<tr>
<th>智能体</th>
<th>使用次数</th>
</tr>
</thead>
<tbody>
${stats.agent_usage?.map(a => `
<tr>
<td>${a.log_key}</td>
<td>${a.count}</td>
</tr>
`).join('') || '<tr><td colspan="2">暂无数据</td></tr>'}
</tbody>
</table>
</div>
`;
}
// ==================== 大模型配置页面 ====================
async function loadLLMPage(content) {
llmConfigs = await fetchAPI('/api/admin/llm');
content.innerHTML = `
<div class="content-header">
<h1 class="content-title">大模型配置</h1>
<button class="add-btn" onclick="showAddLLMModal()">+ 添加配置</button>
</div>
<div class="data-table">
<table>
<thead>
<tr>
<th>名称</th>
<th>提供商</th>
<th>模型</th>
<th>API URL</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${llmConfigs.map(c => `
<tr>
<td>${c.name} ${c.is_default ? '<span class="default-badge">默认</span>' : ''}</td>
<td>${c.provider}</td>
<td>${c.model}</td>
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${c.api_url}</td>
<td>${c.is_active ? '✅ 启用' : '❌ 禁用'}</td>
<td>
<div class="action-btns">
<button class="action-btn edit" onclick="showEditLLMModal(${c.id})">编辑</button>
${!c.is_default ? `<button class="action-btn default" onclick="setDefaultLLM(${c.id})">设为默认</button>` : ''}
<button class="action-btn delete" onclick="deleteLLM(${c.id})">删除</button>
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
}
function showAddLLMModal() {
showModal('添加大模型配置', `
<div class="form-group">
<label class="form-label">名称</label>
<input type="text" class="form-input" id="llmName" placeholder="如智谱GLM">
</div>
<div class="form-group">
<label class="form-label">提供商</label>
<select class="form-select" id="llmProvider">
<option value="zhipu">智谱</option>
<option value="openai">OpenAI</option>
<option value="anthropic">Anthropic</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="form-group">
<label class="form-label">API URL</label>
<input type="text" class="form-input" id="llmApiUrl" placeholder="API endpoint URL">
</div>
<div class="form-group">
<label class="form-label">API Key</label>
<input type="text" class="form-input" id="llmApiKey" placeholder="API Key">
</div>
<div class="form-group">
<label class="form-label">模型</label>
<input type="text" class="form-input" id="llmModel" placeholder="如glm-4.5-air">
</div>
<div class="form-group">
<label class="form-label">Max Tokens</label>
<input type="number" class="form-input" id="llmMaxTokens" value="2048">
</div>
<div class="form-group">
<label class="form-label">Temperature</label>
<input type="number" class="form-input" id="llmTemperature" value="0.7" step="0.1">
</div>
<button class="form-submit" onclick="saveLLM()">保存</button>
`);
}
function showEditLLMModal(id) {
const config = llmConfigs.find(c => c.id === id);
if (!config) return;
showModal('编辑大模型配置', `
<div class="form-group">
<label class="form-label">名称</label>
<input type="text" class="form-input" id="llmName" value="${config.name}">
</div>
<div class="form-group">
<label class="form-label">提供商</label>
<select class="form-select" id="llmProvider">
<option value="zhipu" ${config.provider === 'zhipu' ? 'selected' : ''}>智谱</option>
<option value="openai" ${config.provider === 'openai' ? 'selected' : ''}>OpenAI</option>
<option value="anthropic" ${config.provider === 'anthropic' ? 'selected' : ''}>Anthropic</option>
<option value="custom" ${config.provider === 'custom' ? 'selected' : ''}>自定义</option>
</select>
</div>
<div class="form-group">
<label class="form-label">API URL</label>
<input type="text" class="form-input" id="llmApiUrl" value="${config.api_url}">
</div>
<div class="form-group">
<label class="form-label">API Key</label>
<input type="text" class="form-input" id="llmApiKey" value="${config.api_key}">
</div>
<div class="form-group">
<label class="form-label">模型</label>
<input type="text" class="form-input" id="llmModel" value="${config.model}">
</div>
<div class="form-group">
<label class="form-label">Max Tokens</label>
<input type="number" class="form-input" id="llmMaxTokens" value="${config.max_tokens}">
</div>
<div class="form-group">
<label class="form-label">Temperature</label>
<input type="number" class="form-input" id="llmTemperature" value="${config.temperature}" step="0.1">
</div>
<button class="form-submit" onclick="updateLLM(${id})">保存</button>
`);
}
async function saveLLM() {
const data = {
name: document.getElementById('llmName').value,
provider: document.getElementById('llmProvider').value,
api_url: document.getElementById('llmApiUrl').value,
api_key: document.getElementById('llmApiKey').value,
model: document.getElementById('llmModel').value,
max_tokens: parseInt(document.getElementById('llmMaxTokens').value),
temperature: parseFloat(document.getElementById('llmTemperature').value)
};
if (!data.name || !data.api_url || !data.api_key || !data.model) {
showToast('请填写完整信息');
return;
}
await fetchAPI('/api/admin/llm', 'POST', data);
closeModal();
showToast('添加成功');
loadPage('llm');
}
async function updateLLM(id) {
const data = {
name: document.getElementById('llmName').value,
provider: document.getElementById('llmProvider').value,
api_url: document.getElementById('llmApiUrl').value,
api_key: document.getElementById('llmApiKey').value,
model: document.getElementById('llmModel').value,
max_tokens: parseInt(document.getElementById('llmMaxTokens').value),
temperature: parseFloat(document.getElementById('llmTemperature').value)
};
await fetchAPI(`/api/admin/llm/${id}`, 'PUT', data);
closeModal();
showToast('更新成功');
loadPage('llm');
}
async function setDefaultLLM(id) {
await fetchAPI(`/api/admin/llm/${id}/default`, 'POST');
showToast('已设为默认');
loadPage('llm');
}
async function deleteLLM(id) {
if (!confirm('确定删除此配置?')) return;
await fetchAPI(`/api/admin/llm/${id}`, 'DELETE');
showToast('删除成功');
loadPage('llm');
}
// ==================== 智能体管理页面 ====================
async function loadAgentsPage(content) {
agents = await fetchAPI('/api/admin/agents');
llmConfigs = await fetchAPI('/api/admin/llm');
const categories = {
hot: '热门',
work: '工作',
study: '学习',
life: '生活'
};
content.innerHTML = `
<div class="content-header">
<h1 class="content-title">智能体管理</h1>
<button class="add-btn" onclick="showAddAgentModal()">+ 添加智能体</button>
</div>
<div class="data-table">
<table>
<thead>
<tr>
<th>头像</th>
<th>名称</th>
<th>类别</th>
<th>描述</th>
<th>热度</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${agents.map(a => `
<tr>
<td style="font-size: 24px;">${a.avatar}</td>
<td>${a.name}</td>
<td>${categories[a.category] || a.category}</td>
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${a.description || '-'}</td>
<td>${a.heat || 0}</td>
<td>${a.is_active ? '✅ 启用' : '❌ 禁用'}</td>
<td>
<div class="action-btns">
<button class="action-btn edit" onclick="showEditAgentModal('${a.agent_id}')">编辑</button>
<button class="action-btn delete" onclick="deleteAgent('${a.agent_id}')">删除</button>
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
}
function showAddAgentModal() {
showModal('添加智能体', `
<div class="form-group">
<label class="form-label">ID</label>
<input type="text" class="form-input" id="agentId" placeholder="如assistant">
</div>
<div class="form-group">
<label class="form-label">名称</label>
<input type="text" class="form-input" id="agentName" placeholder="如:通用助手">
</div>
<div class="form-group">
<label class="form-label">头像</label>
<select class="form-select" id="agentAvatar">
${['🤖', '✍️', '👨‍💻', '🌐', '💼', '📊', '📈', '📚', '🔤', '🔢', '🏥', '✈️', '🍳', '🧑', '😊', '😎', '🤓', '🦸', '🧙', '🥷'].map(a =>
`<option value="${a}">${a}</option>`
).join('')}
</select>
</div>
<div class="form-group">
<label class="form-label">类别</label>
<select class="form-select" id="agentCategory">
<option value="hot">热门</option>
<option value="work">工作</option>
<option value="study">学习</option>
<option value="life">生活</option>
</select>
</div>
<div class="form-group">
<label class="form-label">描述</label>
<input type="text" class="form-input" id="agentDescription" placeholder="智能体描述">
</div>
<div class="form-group">
<label class="form-label">System Prompt</label>
<textarea class="form-textarea" id="agentPrompt" placeholder="系统提示词"></textarea>
</div>
<div class="form-group">
<label class="form-label">LLM配置</label>
<select class="form-select" id="agentLLM">
<option value="">使用默认</option>
${llmConfigs.map(c => `<option value="${c.id}">${c.name}</option>`).join('')}
</select>
</div>
<div class="form-group">
<label class="form-label">热度</label>
<input type="number" class="form-input" id="agentHeat" value="0">
</div>
<div class="form-group">
<label class="form-label">标签</label>
<input type="text" class="form-input" id="agentTags" placeholder="多个标签用逗号分隔">
</div>
<div class="form-group">
<label style="display: flex; align-items: center; gap: 8px;">
<input type="checkbox" id="agentEnableSearch"> 启用联网搜索
</label>
</div>
<button class="form-submit" onclick="saveAgent()">保存</button>
`);
}
function showEditAgentModal(agentId) {
const agent = agents.find(a => a.agent_id === agentId);
if (!agent) return;
showModal('编辑智能体', `
<div class="form-group">
<label class="form-label">ID不可修改</label>
<input type="text" class="form-input" id="agentId" value="${agent.agent_id}" readonly style="background: #f5f7fa;">
</div>
<div class="form-group">
<label class="form-label">名称</label>
<input type="text" class="form-input" id="agentName" value="${agent.name}">
</div>
<div class="form-group">
<label class="form-label">头像</label>
<select class="form-select" id="agentAvatar">
${['🤖', '✍️', '👨‍💻', '🌐', '💼', '📊', '📈', '📚', '🔤', '🔢', '🏥', '✈️', '🍳', '🧑', '😊', '😎', '🤓', '🦸', '🧙', '🥷'].map(a =>
`<option value="${a}" ${a === agent.avatar ? 'selected' : ''}>${a}</option>`
).join('')}
</select>
</div>
<div class="form-group">
<label class="form-label">类别</label>
<select class="form-select" id="agentCategory">
<option value="hot" ${agent.category === 'hot' ? 'selected' : ''}>热门</option>
<option value="work" ${agent.category === 'work' ? 'selected' : ''}>工作</option>
<option value="study" ${agent.category === 'study' ? 'selected' : ''}>学习</option>
<option value="life" ${agent.category === 'life' ? 'selected' : ''}>生活</option>
</select>
</div>
<div class="form-group">
<label class="form-label">描述</label>
<input type="text" class="form-input" id="agentDescription" value="${agent.description || ''}">
</div>
<div class="form-group">
<label class="form-label">System Prompt</label>
<textarea class="form-textarea" id="agentPrompt">${agent.system_prompt || ''}</textarea>
</div>
<div class="form-group">
<label class="form-label">LLM配置</label>
<select class="form-select" id="agentLLM">
<option value="">使用默认</option>
${llmConfigs.map(c => `<option value="${c.id}" ${c.id === agent.llm_config_id ? 'selected' : ''}>${c.name}</option>`).join('')}
</select>
</div>
<div class="form-group">
<label class="form-label">热度</label>
<input type="number" class="form-input" id="agentHeat" value="${agent.heat || 0}">
</div>
<div class="form-group">
<label class="form-label">标签</label>
<input type="text" class="form-input" id="agentTags" value="${agent.tags || ''}">
</div>
<div class="form-group">
<label style="display: flex; align-items: center; gap: 8px;">
<input type="checkbox" id="agentEnableSearch" ${agent.enable_search ? 'checked' : ''}> 启用联网搜索
</label>
</div>
<button class="form-submit" onclick="updateAgent('${agentId}')">保存</button>
`);
}
async function saveAgent() {
const data = {
agent_id: document.getElementById('agentId').value,
name: document.getElementById('agentName').value,
avatar: document.getElementById('agentAvatar').value,
category: document.getElementById('agentCategory').value,
description: document.getElementById('agentDescription').value,
system_prompt: document.getElementById('agentPrompt').value,
llm_config_id: document.getElementById('agentLLM').value || null,
heat: parseInt(document.getElementById('agentHeat').value),
tags: document.getElementById('agentTags').value,
enable_search: document.getElementById('agentEnableSearch').checked ? 1 : 0
};
if (!data.agent_id || !data.name || !data.system_prompt) {
showToast('请填写完整信息');
return;
}
await fetchAPI('/api/admin/agents', 'POST', data);
closeModal();
showToast('添加成功');
loadPage('agents');
}
async function updateAgent(agentId) {
const data = {
name: document.getElementById('agentName').value,
avatar: document.getElementById('agentAvatar').value,
category: document.getElementById('agentCategory').value,
description: document.getElementById('agentDescription').value,
system_prompt: document.getElementById('agentPrompt').value,
llm_config_id: document.getElementById('agentLLM').value || null,
heat: parseInt(document.getElementById('agentHeat').value),
tags: document.getElementById('agentTags').value,
enable_search: document.getElementById('agentEnableSearch').checked ? 1 : 0
};
await fetchAPI(`/api/admin/agents/${agentId}`, 'PUT', data);
closeModal();
showToast('更新成功');
loadPage('agents');
}
async function deleteAgent(agentId) {
if (!confirm('确定删除此智能体?')) return;
await fetchAPI(`/api/admin/agents/${agentId}`, 'DELETE');
showToast('删除成功');
loadPage('agents');
}
// ==================== 搜索配置页面 ====================
async function loadSearchPage(content) {
searchConfigs = await fetchAPI('/api/admin/search');
content.innerHTML = `
<div class="content-header">
<h1 class="content-title">搜索配置</h1>
<button class="add-btn" onclick="showAddSearchModal()">+ 添加配置</button>
</div>
<div class="data-table">
<table>
<thead>
<tr>
<th>名称</th>
<th>提供商</th>
<th>API URL</th>
<th>最大结果数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${searchConfigs.map(c => `
<tr>
<td>${c.name} ${c.is_default ? '<span class="default-badge">默认</span>' : ''}</td>
<td>${c.provider}</td>
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${c.api_url}</td>
<td>${c.max_results}</td>
<td>${c.is_active ? '✅ 启用' : '❌ 禁用'}</td>
<td>
<div class="action-btns">
<button class="action-btn edit" onclick="showEditSearchModal(${c.id})">编辑</button>
${!c.is_default ? `<button class="action-btn default" onclick="setDefaultSearch(${c.id})">设为默认</button>` : ''}
<button class="action-btn delete" onclick="deleteSearch(${c.id})">删除</button>
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
}
function showAddSearchModal() {
showModal('添加搜索配置', `
<div class="form-group">
<label class="form-label">名称</label>
<input type="text" class="form-input" id="searchName" placeholder="如Tavily">
</div>
<div class="form-group">
<label class="form-label">提供商</label>
<select class="form-select" id="searchProvider">
<option value="tavily">Tavily</option>
<option value="google">Google</option>
<option value="bing">Bing</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="form-group">
<label class="form-label">API URL</label>
<input type="text" class="form-input" id="searchApiUrl" placeholder="API endpoint URL">
</div>
<div class="form-group">
<label class="form-label">API Key</label>
<input type="text" class="form-input" id="searchApiKey" placeholder="API Key">
</div>
<div class="form-group">
<label class="form-label">最大结果数</label>
<input type="number" class="form-input" id="searchMaxResults" value="10">
</div>
<button class="form-submit" onclick="saveSearch()">保存</button>
`);
}
function showEditSearchModal(id) {
const config = searchConfigs.find(c => c.id === id);
if (!config) return;
showModal('编辑搜索配置', `
<div class="form-group">
<label class="form-label">名称</label>
<input type="text" class="form-input" id="searchName" value="${config.name}">
</div>
<div class="form-group">
<label class="form-label">提供商</label>
<select class="form-select" id="searchProvider">
<option value="tavily" ${config.provider === 'tavily' ? 'selected' : ''}>Tavily</option>
<option value="google" ${config.provider === 'google' ? 'selected' : ''}>Google</option>
<option value="bing" ${config.provider === 'bing' ? 'selected' : ''}>Bing</option>
<option value="custom" ${config.provider === 'custom' ? 'selected' : ''}>自定义</option>
</select>
</div>
<div class="form-group">
<label class="form-label">API URL</label>
<input type="text" class="form-input" id="searchApiUrl" value="${config.api_url}">
</div>
<div class="form-group">
<label class="form-label">API Key</label>
<input type="text" class="form-input" id="searchApiKey" value="${config.api_key}">
</div>
<div class="form-group">
<label class="form-label">最大结果数</label>
<input type="number" class="form-input" id="searchMaxResults" value="${config.max_results}">
</div>
<button class="form-submit" onclick="updateSearch(${id})">保存</button>
`);
}
async function saveSearch() {
const data = {
name: document.getElementById('searchName').value,
provider: document.getElementById('searchProvider').value,
api_url: document.getElementById('searchApiUrl').value,
api_key: document.getElementById('searchApiKey').value,
max_results: parseInt(document.getElementById('searchMaxResults').value)
};
if (!data.name || !data.api_url || !data.api_key) {
showToast('请填写完整信息');
return;
}
await fetchAPI('/api/admin/search', 'POST', data);
closeModal();
showToast('添加成功');
loadPage('search');
}
async function updateSearch(id) {
const data = {
name: document.getElementById('searchName').value,
provider: document.getElementById('searchProvider').value,
api_url: document.getElementById('searchApiUrl').value,
api_key: document.getElementById('searchApiKey').value,
max_results: parseInt(document.getElementById('searchMaxResults').value)
};
await fetchAPI(`/api/admin/search/${id}`, 'PUT', data);
closeModal();
showToast('更新成功');
loadPage('search');
}
async function setDefaultSearch(id) {
await fetchAPI(`/api/admin/search/${id}/default`, 'POST');
showToast('已设为默认');
loadPage('search');
}
async function deleteSearch(id) {
if (!confirm('确定删除此配置?')) return;
await fetchAPI(`/api/admin/search/${id}`, 'DELETE');
showToast('删除成功');
loadPage('search');
}
// ==================== 系统设置页面 ====================
async function loadSystemPage(content) {
systemConfigs = await fetchAPI('/api/admin/system');
content.innerHTML = `
<div class="content-header">
<h1 class="content-title">系统设置</h1>
</div>
<div class="data-table" style="padding: 24px;">
<div class="form-group">
<label class="form-label">应用名称</label>
<input type="text" class="form-input" id="appName" value="${systemConfigs.app_name?.value || ''}">
<span style="color: #999; font-size: 12px;">${systemConfigs.app_name?.description || ''}</span>
</div>
<div class="form-group">
<label class="form-label">应用版本</label>
<input type="text" class="form-input" id="appVersion" value="${systemConfigs.app_version?.value || ''}">
</div>
<div class="form-group">
<label style="display: flex; align-items: center; gap: 8px;">
<input type="checkbox" id="enableSearch" ${systemConfigs.enable_search?.value === 'true' ? 'checked' : ''}>
启用联网搜索
</label>
</div>
<h3 style="margin: 24px 0 16px; padding-top: 16px; border-top: 1px solid #e2e8f0;">游客使用限制</h3>
<div class="form-group">
<label class="form-label">每日对话会话数</label>
<input type="number" class="form-input" id="guestChatSessions" value="${systemConfigs.guest_chat_sessions?.value || 1}">
</div>
<div class="form-group">
<label class="form-label">每日对话消息数</label>
<input type="number" class="form-input" id="guestChatMessages" value="${systemConfigs.guest_chat_messages?.value || 20}">
</div>
<div class="form-group">
<label class="form-label">每日智能体消息数</label>
<input type="number" class="form-input" id="guestAgentMessages" value="${systemConfigs.guest_agent_messages?.value || 20}">
</div>
<h3 style="margin: 24px 0 16px; padding-top: 16px; border-top: 1px solid #e2e8f0;">管理员设置</h3>
<div class="form-group">
<label class="form-label">管理员密码</label>
<input type="text" class="form-input" id="adminPassword" value="${systemConfigs.admin_password?.value || ''}" placeholder="修改管理员密码">
</div>
<button class="form-submit" onclick="saveSystemConfig()">保存设置</button>
</div>
`;
}
async function saveSystemConfig() {
const data = {
app_name: document.getElementById('appName').value,
app_version: document.getElementById('appVersion').value,
enable_search: document.getElementById('enableSearch').checked ? 'true' : 'false',
guest_chat_sessions: document.getElementById('guestChatSessions').value,
guest_chat_messages: document.getElementById('guestChatMessages').value,
guest_agent_messages: document.getElementById('guestAgentMessages').value,
admin_password: document.getElementById('adminPassword').value
};
await fetchAPI('/api/admin/system', 'POST', data);
showToast('保存成功');
loadPage('system');
}
// ==================== 工具函数 ====================
async function fetchAPI(url, method = 'GET', data = null) {
const options = {
method,
headers: { 'Content-Type': 'application/json' }
};
if (data) {
options.body = JSON.stringify(data);
}
const res = await fetch(`${API_BASE}${url}`, options);
return res.json();
}
function showModal(title, content) {
const modal = document.getElementById('modal');
const modalContent = document.getElementById('modalContent');
modalContent.innerHTML = `
<div class="modal-header">
<h2 class="modal-title">${title}</h2>
<span class="modal-close" onclick="closeModal()">×</span>
</div>
${content}
`;
modal.classList.add('show');
}
function closeModal() {
document.getElementById('modal').classList.remove('show');
}
function showToast(message) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 2000);
}
// 点击模态框背景关闭
document.getElementById('modal').addEventListener('click', (e) => {
if (e.target.id === 'modal') {
closeModal();
}
});