Add files via upload

This commit is contained in:
公明
2026-03-09 02:06:39 +08:00
committed by GitHub
parent 8a2177ffab
commit 7b1487383f
16 changed files with 3628 additions and 971 deletions
+88 -58
View File
@@ -255,7 +255,10 @@ async function loadConfig(loadTools = true) {
}
} catch (error) {
console.error('加载配置失败:', error);
alert('加载配置失败: ' + error.message);
const baseMsg = (typeof window !== 'undefined' && typeof window.t === 'function')
? window.t('settings.apply.loadFailed')
: '加载配置失败';
alert(baseMsg + ': ' + error.message);
}
}
@@ -269,7 +272,7 @@ async function loadToolsList(page = 1, searchKeyword = '') {
// 显示加载状态
if (toolsList) {
// 清空整个容器,包括可能存在的分页控件
toolsList.innerHTML = '<div class="tools-list-items"><div class="loading" style="padding: 20px; text-align: center; color: var(--text-muted);">⏳ 正在加载工具列表...</div></div>';
toolsList.innerHTML = '<div class="tools-list-items"><div class="loading" style="padding: 20px; text-align: center; color: var(--text-muted);">⏳ ' + (typeof window.t === 'function' ? window.t('mcp.loadingTools') : '正在加载工具列表...') + '</div></div>';
}
try {
@@ -324,8 +327,8 @@ async function loadToolsList(page = 1, searchKeyword = '') {
if (toolsList) {
const isTimeout = error.name === 'AbortError' || error.message.includes('timeout');
const errorMsg = isTimeout
? '加载工具列表超时,可能是外部MCP连接较慢。请点击"刷新"按钮重试,或检查外部MCP连接状态。'
: `加载工具列表失败: ${escapeHtml(error.message)}`;
? (typeof window.t === 'function' ? window.t('mcp.loadToolsTimeout') : '加载工具列表超时,可能是外部MCP连接较慢。请点击"刷新"按钮重试,或检查外部MCP连接状态。')
: (typeof window.t === 'function' ? window.t('mcp.loadToolsFailed') : '加载工具列表失败') + ': ' + escapeHtml(error.message);
toolsList.innerHTML = `<div class="error" style="padding: 20px; text-align: center;">${errorMsg}</div>`;
}
}
@@ -399,7 +402,7 @@ function renderToolsList() {
listContainer.innerHTML = '';
if (allTools.length === 0) {
listContainer.innerHTML = '<div class="empty">暂无工具</div>';
listContainer.innerHTML = '<div class="empty">' + (typeof window.t === 'function' ? window.t('mcp.noTools') : '暂无工具') + '</div>';
if (!toolsList.contains(listContainer)) {
toolsList.appendChild(listContainer);
}
@@ -428,8 +431,8 @@ function renderToolsList() {
let externalBadge = '';
if (toolState.is_external || tool.is_external) {
const externalMcpName = toolState.external_mcp || tool.external_mcp || '';
const badgeText = externalMcpName ? `外部 (${escapeHtml(externalMcpName)})` : '外部';
const badgeTitle = externalMcpName ? `外部MCP工具 - 来源:${escapeHtml(externalMcpName)}` : '外部MCP工具';
const badgeText = externalMcpName ? (typeof window.t === 'function' ? window.t('mcp.externalFrom', { name: escapeHtml(externalMcpName) }) : `外部 (${escapeHtml(externalMcpName)})`) : (typeof window.t === 'function' ? window.t('mcp.externalBadge') : '外部');
const badgeTitle = externalMcpName ? (typeof window.t === 'function' ? window.t('mcp.externalToolFrom', { name: escapeHtml(externalMcpName) }) : `外部MCP工具 - 来源:${escapeHtml(externalMcpName)}`) : (typeof window.t === 'function' ? window.t('mcp.externalBadge') : '外部MCP工具');
externalBadge = `<span class="external-tool-badge" title="${badgeTitle}">${badgeText}</span>`;
}
@@ -443,7 +446,7 @@ function renderToolsList() {
${escapeHtml(tool.name)}
${externalBadge}
</div>
<div class="tool-item-desc">${escapeHtml(tool.description || '无描述')}</div>
<div class="tool-item-desc">${escapeHtml(tool.description || (typeof window.t === 'function' ? window.t('mcp.noDescription') : '无描述'))}</div>
</div>
`;
listContainer.appendChild(toolItem);
@@ -481,12 +484,19 @@ function renderToolsPagination() {
const endItem = Math.min(page * toolsPagination.pageSize, total);
const savedPageSize = getToolsPageSize();
const t = typeof window.t === 'function' ? window.t : (k) => k;
const paginationT = (key, opts) => {
if (typeof window.t === 'function') return window.t(key, opts);
if (key === 'mcp.paginationInfo' && opts) return `显示 ${opts.start}-${opts.end} / 共 ${opts.total} 个工具`;
if (key === 'mcp.pageInfo' && opts) return `${opts.page} / ${opts.total}`;
return key;
};
pagination.innerHTML = `
<div class="pagination-info">
显示 ${startItem}-${endItem} / 共 ${total} 个工具${toolsSearchKeyword ? ` (搜索: "${escapeHtml(toolsSearchKeyword)}")` : ''}
${paginationT('mcp.paginationInfo', { start: startItem, end: endItem, total: total })}${toolsSearchKeyword ? ` (${t('common.search')}: "${escapeHtml(toolsSearchKeyword)}")` : ''}
</div>
<div class="pagination-page-size">
<label for="tools-page-size-pagination">每页:</label>
<label for="tools-page-size-pagination">${t('mcp.perPage')}</label>
<select id="tools-page-size-pagination" onchange="changeToolsPageSize()">
<option value="10" ${savedPageSize === 10 ? 'selected' : ''}>10</option>
<option value="20" ${savedPageSize === 20 ? 'selected' : ''}>20</option>
@@ -495,11 +505,11 @@ function renderToolsPagination() {
</select>
</div>
<div class="pagination-controls">
<button class="btn-secondary" onclick="loadToolsList(1, '${escapeHtml(toolsSearchKeyword)}')" ${page === 1 ? 'disabled' : ''}>首页</button>
<button class="btn-secondary" onclick="loadToolsList(${page - 1}, '${escapeHtml(toolsSearchKeyword)}')" ${page === 1 ? 'disabled' : ''}>上一页</button>
<span class="pagination-page">${page} / ${totalPages}</span>
<button class="btn-secondary" onclick="loadToolsList(${page + 1}, '${escapeHtml(toolsSearchKeyword)}')" ${page === totalPages ? 'disabled' : ''}>下一页</button>
<button class="btn-secondary" onclick="loadToolsList(${totalPages}, '${escapeHtml(toolsSearchKeyword)}')" ${page === totalPages ? 'disabled' : ''}>末页</button>
<button class="btn-secondary" onclick="loadToolsList(1, '${escapeHtml(toolsSearchKeyword)}')" ${page === 1 ? 'disabled' : ''}>${t('mcp.firstPage')}</button>
<button class="btn-secondary" onclick="loadToolsList(${page - 1}, '${escapeHtml(toolsSearchKeyword)}')" ${page === 1 ? 'disabled' : ''}>${t('mcp.prevPage')}</button>
<span class="pagination-page">${paginationT('mcp.pageInfo', { page: page, total: totalPages })}</span>
<button class="btn-secondary" onclick="loadToolsList(${page + 1}, '${escapeHtml(toolsSearchKeyword)}')" ${page === totalPages ? 'disabled' : ''}>${t('mcp.nextPage')}</button>
<button class="btn-secondary" onclick="loadToolsList(${totalPages}, '${escapeHtml(toolsSearchKeyword)}')" ${page === totalPages ? 'disabled' : ''}>${t('mcp.lastPage')}</button>
</div>
`;
@@ -693,9 +703,10 @@ async function updateToolsStats() {
totalEnabled = currentPageEnabled;
}
const tStats = typeof window.t === 'function' ? window.t : (k) => k;
statsEl.innerHTML = `
<span title="当前页启用的工具数">✅ 当前页已启用: <strong>${currentPageEnabled}</strong> / ${currentPageTotal}</span>
<span title="所有工具中启用的工具总数">📊 总计已启用: <strong>${totalEnabled}</strong> / ${totalTools}</span>
<span title="${tStats('mcp.currentPageEnabled')}">✅ ${tStats('mcp.currentPageEnabled')}: <strong>${currentPageEnabled}</strong> / ${currentPageTotal}</span>
<span title="${tStats('mcp.totalEnabled')}">📊 ${tStats('mcp.totalEnabled')}: <strong>${totalEnabled}</strong> / ${totalTools}</span>
`;
}
@@ -737,7 +748,10 @@ async function applySettings() {
}
if (hasError) {
alert('请填写所有必填字段(标记为 * 的字段)');
const msg = (typeof window !== 'undefined' && typeof window.t === 'function')
? window.t('settings.apply.fillRequired')
: '请填写所有必填字段(标记为 * 的字段)';
alert(msg);
return;
}
@@ -896,7 +910,10 @@ async function applySettings() {
if (!updateResponse.ok) {
const error = await updateResponse.json();
throw new Error(error.error || '更新配置失败');
const fallback = (typeof window !== 'undefined' && typeof window.t === 'function')
? window.t('settings.apply.applyFailed')
: '应用配置失败';
throw new Error(error.error || fallback);
}
// 应用配置
@@ -906,14 +923,23 @@ async function applySettings() {
if (!applyResponse.ok) {
const error = await applyResponse.json();
throw new Error(error.error || '应用配置失败');
const fallback = (typeof window !== 'undefined' && typeof window.t === 'function')
? window.t('settings.apply.applyFailed')
: '应用配置失败';
throw new Error(error.error || fallback);
}
alert('配置已成功应用!');
const successMsg = (typeof window !== 'undefined' && typeof window.t === 'function')
? window.t('settings.apply.applySuccess')
: '配置已成功应用!';
alert(successMsg);
closeSettings();
} catch (error) {
console.error('应用配置失败:', error);
alert('应用配置失败: ' + error.message);
const baseMsg = (typeof window !== 'undefined' && typeof window.t === 'function')
? window.t('settings.apply.applyFailed')
: '应用配置失败';
alert(baseMsg + ': ' + error.message);
}
}
@@ -1024,7 +1050,7 @@ async function saveToolsConfig() {
throw new Error(error.error || '应用配置失败');
}
alert('工具配置已成功保存!');
alert(typeof window.t === 'function' ? window.t('mcp.toolsConfigSaved') : '工具配置已成功保存!');
// 重新加载工具列表以反映最新状态
if (typeof loadToolsList === 'function') {
@@ -1032,7 +1058,7 @@ async function saveToolsConfig() {
}
} catch (error) {
console.error('保存工具配置失败:', error);
alert('保存工具配置失败: ' + error.message);
alert((typeof window.t === 'function' ? window.t('mcp.saveToolsConfigFailed') : '保存工具配置失败') + ': ' + error.message);
}
}
@@ -1079,7 +1105,7 @@ async function changePassword() {
}
if (hasError) {
alert('请正确填写当前密码和新密码,新密码至少 8 位且需要两次输入一致。');
alert(typeof window.t === 'function' ? window.t('settings.security.fillPasswordHint') : '请正确填写当前密码和新密码,新密码至少 8 位且需要两次输入一致。');
return;
}
@@ -1104,13 +1130,14 @@ async function changePassword() {
throw new Error(result.error || '修改密码失败');
}
alert('密码已更新,请使用新密码重新登录。');
const pwdMsg = typeof window.t === 'function' ? window.t('settings.security.passwordUpdated') : '密码已更新,请使用新密码重新登录。';
alert(pwdMsg);
resetPasswordForm();
handleUnauthorized({ message: '密码已更新,请使用新密码重新登录。', silent: false });
handleUnauthorized({ message: pwdMsg, silent: false });
closeSettings();
} catch (error) {
console.error('修改密码失败:', error);
alert('修改密码失败: ' + error.message);
alert((typeof window.t === 'function' ? window.t('settings.security.changePasswordFailed') : '修改密码失败') + ': ' + error.message);
} finally {
if (submitBtn) {
submitBtn.disabled = false;
@@ -1173,7 +1200,8 @@ function renderExternalMCPList(servers) {
if (!list) return;
if (Object.keys(servers).length === 0) {
list.innerHTML = '<div class="empty">📋 暂无外部MCP配置<br><span style="font-size: 0.875rem; margin-top: 8px; display: block;">点击"添加外部MCP"按钮开始配置</span></div>';
const emptyT = typeof window.t === 'function' ? window.t : (k) => k;
list.innerHTML = '<div class="empty">📋 ' + emptyT('mcp.noExternalMCP') + '<br><span style="font-size: 0.875rem; margin-top: 8px; display: block;">' + emptyT('mcp.clickToAddExternal') + '</span></div>';
return;
}
@@ -1184,10 +1212,11 @@ function renderExternalMCPList(servers) {
status === 'connecting' ? 'status-connecting' :
status === 'error' ? 'status-error' :
status === 'disabled' ? 'status-disabled' : 'status-disconnected';
const statusText = status === 'connected' ? '已连接' :
status === 'connecting' ? '连接中...' :
status === 'error' ? '连接失败' :
status === 'disabled' ? '已禁用' : '未连接';
const statusT = typeof window.t === 'function' ? window.t : (k) => k;
const statusText = status === 'connected' ? statusT('mcp.connected') :
status === 'connecting' ? statusT('mcp.connecting') :
status === 'error' ? statusT('mcp.connectionFailed') :
status === 'disabled' ? statusT('mcp.disabled') : statusT('mcp.disconnected');
const transport = server.config.transport || (server.config.command ? 'stdio' : 'http');
const transportIcon = transport === 'stdio' ? '⚙️' : '🌐';
@@ -1200,15 +1229,15 @@ function renderExternalMCPList(servers) {
</div>
<div class="external-mcp-item-actions">
${status === 'connected' || status === 'disconnected' || status === 'error' ?
`<button class="btn-small" id="btn-toggle-${escapeHtml(name)}" onclick="toggleExternalMCP('${escapeHtml(name)}', '${status}')" title="${status === 'connected' ? '停止连接' : '启动连接'}">
${status === 'connected' ? '⏸ 停止' : '▶ 启动'}
`<button class="btn-small" id="btn-toggle-${escapeHtml(name)}" onclick="toggleExternalMCP('${escapeHtml(name)}', '${status}')" title="${status === 'connected' ? statusT('mcp.stopConnection') : statusT('mcp.startConnection')}">
${status === 'connected' ? '⏸ ' + statusT('mcp.stop') : '▶ ' + statusT('mcp.start')}
</button>` :
status === 'connecting' ?
`<button class="btn-small" id="btn-toggle-${escapeHtml(name)}" disabled style="opacity: 0.6; cursor: not-allowed;">
⏳ 连接中...
</button>` : ''}
<button class="btn-small" onclick="editExternalMCP('${escapeHtml(name)}')" title="编辑配置" ${status === 'connecting' ? 'disabled' : ''}>✏️ 编辑</button>
<button class="btn-small btn-danger" onclick="deleteExternalMCP('${escapeHtml(name)}')" title="删除配置" ${status === 'connecting' ? 'disabled' : ''}>🗑 删除</button>
<button class="btn-small" onclick="editExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.editConfig')}" ${status === 'connecting' ? 'disabled' : ''}>✏️ ${statusT('common.edit')}</button>
<button class="btn-small btn-danger" onclick="deleteExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.deleteConfig')}" ${status === 'connecting' ? 'disabled' : ''}>🗑 ${statusT('common.delete')}</button>
</div>
</div>
${status === 'error' && server.error ? `
@@ -1217,31 +1246,31 @@ function renderExternalMCPList(servers) {
</div>` : ''}
<div class="external-mcp-item-details">
<div>
<strong>传输模式</strong>
<strong>${statusT('mcp.transportMode')}</strong>
<span>${transportIcon} ${escapeHtml(transport.toUpperCase())}</span>
</div>
${server.tool_count !== undefined && server.tool_count > 0 ? `
<div>
<strong>工具数量</strong>
<strong>${statusT('mcp.toolCount')}</strong>
<span style="font-weight: 600; color: var(--accent-color);">🔧 ${server.tool_count} 个工具</span>
</div>` : server.tool_count === 0 && status === 'connected' ? `
<div>
<strong>工具数量</strong>
<span style="color: var(--text-muted);">暂无工具</span>
<strong>${statusT('mcp.toolCount')}</strong>
<span style="color: var(--text-muted);">${statusT('mcp.noTools')}</span>
</div>` : ''}
${server.config.description ? `
<div>
<strong>描述</strong>
<strong>${statusT('mcp.description')}</strong>
<span>${escapeHtml(server.config.description)}</span>
</div>` : ''}
${server.config.timeout ? `
<div>
<strong>超时时间</strong>
<strong>${statusT('mcp.timeout')}</strong>
<span>${server.config.timeout} 秒</span>
</div>` : ''}
${transport === 'stdio' && server.config.command ? `
<div>
<strong>命令</strong>
<strong>${statusT('mcp.command')}</strong>
<span style="font-family: monospace; font-size: 0.8125rem;">${escapeHtml(server.config.command)}</span>
</div>` : ''}
${transport === 'http' && server.config.url ? `
@@ -1267,18 +1296,19 @@ function renderExternalMCPStats(stats) {
const disabled = stats.disabled || 0;
const connected = stats.connected || 0;
const statsT = typeof window.t === 'function' ? window.t : (k) => k;
statsEl.innerHTML = `
<span title="总配置数">📊 总数: <strong>${total}</strong></span>
<span title="已启用的配置数">✅ 已启用: <strong>${enabled}</strong></span>
<span title="已停用的配置数">⏸ 已停用: <strong>${disabled}</strong></span>
<span title="当前已连接的配置数">🔗 已连接: <strong>${connected}</strong></span>
<span title="${statsT('mcp.totalCount')}">📊 ${statsT('mcp.totalCount')}: <strong>${total}</strong></span>
<span title="${statsT('mcp.enabledCount')}">✅ ${statsT('mcp.enabledCount')}: <strong>${enabled}</strong></span>
<span title="${statsT('mcp.disabledCount')}">⏸ ${statsT('mcp.disabledCount')}: <strong>${disabled}</strong></span>
<span title="${statsT('mcp.connectedCount')}">🔗 ${statsT('mcp.connectedCount')}: <strong>${connected}</strong></span>
`;
}
// 显示添加外部MCP模态框
function showAddExternalMCPModal() {
currentEditingMCPName = null;
document.getElementById('external-mcp-modal-title').textContent = '添加外部MCP';
document.getElementById('external-mcp-modal-title').textContent = (typeof window.t === 'function' ? window.t('mcp.addExternalMCP') : '添加外部MCP');
document.getElementById('external-mcp-json').value = '';
document.getElementById('external-mcp-json-error').style.display = 'none';
document.getElementById('external-mcp-json-error').textContent = '';
@@ -1303,7 +1333,7 @@ async function editExternalMCP(name) {
const server = await response.json();
currentEditingMCPName = name;
document.getElementById('external-mcp-modal-title').textContent = '编辑外部MCP';
document.getElementById('external-mcp-modal-title').textContent = (typeof window.t === 'function' ? window.t('mcp.editExternalMCP') : '编辑外部MCP');
// 将配置转换为对象格式(key为名称)
const config = { ...server.config };
@@ -1325,7 +1355,7 @@ async function editExternalMCP(name) {
document.getElementById('external-mcp-modal').style.display = 'block';
} catch (error) {
console.error('编辑外部MCP失败:', error);
alert('编辑失败: ' + error.message);
alert((typeof window.t === 'function' ? window.t('mcp.operationFailed') : '编辑失败') + ': ' + error.message);
}
}
@@ -1528,7 +1558,7 @@ async function saveExternalMCP() {
}
// 轮询几次以拉取后端异步更新的工具数量(无固定延迟,拿到即停)
pollExternalMCPToolCount(null, 5);
alert('保存成功');
alert(typeof window.t === 'function' ? window.t('mcp.saveSuccess') : '保存成功');
} catch (error) {
console.error('保存外部MCP失败:', error);
errorDiv.textContent = '保存失败: ' + error.message;
@@ -1539,7 +1569,7 @@ async function saveExternalMCP() {
// 删除外部MCP
async function deleteExternalMCP(name) {
if (!confirm(`确定要删除外部MCP "${name}" 吗?`)) {
if (!confirm((typeof window.t === 'function' ? window.t('mcp.deleteExternalConfirm', { name: name }) : `确定要删除外部MCP "${name}" 吗?`))) {
return;
}
@@ -1558,10 +1588,10 @@ async function deleteExternalMCP(name) {
if (typeof window !== 'undefined' && typeof window.refreshMentionTools === 'function') {
window.refreshMentionTools();
}
alert('删除成功');
alert(typeof window.t === 'function' ? window.t('mcp.deleteSuccess') : '删除成功');
} catch (error) {
console.error('删除外部MCP失败:', error);
alert('删除失败: ' + error.message);
alert((typeof window.t === 'function' ? window.t('mcp.operationFailed') : '删除失败') + ': ' + error.message);
}
}
@@ -1626,7 +1656,7 @@ async function toggleExternalMCP(name, currentStatus) {
}
} catch (error) {
console.error('切换外部MCP状态失败:', error);
alert('操作失败: ' + error.message);
alert((typeof window.t === 'function' ? window.t('mcp.operationFailed') : '操作失败') + ': ' + error.message);
// 恢复按钮状态
if (button) {
@@ -1679,7 +1709,7 @@ async function pollExternalMCPStatus(name, maxAttempts = 30) {
window.refreshMentionTools();
}
if (status === 'error') {
alert('连接失败,请检查配置和网络连接');
alert(typeof window.t === 'function' ? window.t('mcp.connectionFailedCheck') : '连接失败,请检查配置和网络连接');
}
return;
} else if (status === 'connecting') {
@@ -1701,7 +1731,7 @@ async function pollExternalMCPStatus(name, maxAttempts = 30) {
if (typeof window !== 'undefined' && typeof window.refreshMentionTools === 'function') {
window.refreshMentionTools();
}
alert('连接超时,请检查配置和网络连接');
alert(typeof window.t === 'function' ? window.t('mcp.connectionTimeout') : '连接超时,请检查配置和网络连接');
}
// 在打开设置时加载外部MCP列表