增加搜索

This commit is contained in:
2026-06-09 13:48:14 +08:00
parent 4429efdc7c
commit 5150a23256
2 changed files with 464 additions and 66 deletions
+55 -3
View File
@@ -144,12 +144,12 @@
padding: 3px 10px;
border-radius: 20px;
}
header .model-select {
header .profile-select {
max-width: 260px;
cursor: pointer;
outline: none;
}
header .model-select:disabled {
header .profile-select:disabled {
opacity: .65;
cursor: not-allowed;
}
@@ -475,9 +475,10 @@
{{ .Title }}
</div>
<div class="header-actions">
<select id="modelSelect" class="model-badge model-select" title="切换 OpenAI 配置">
<select id="modelSelect" class="model-badge profile-select" title="切换 OpenAI 配置">
<option value="{{ .OpenAIName }}">{{ .Model }}</option>
</select>
<select id="searchSelect" class="model-badge profile-select" title="切换搜索源"></select>
<button id="btnSearch" title="开启后,本轮提问会先联网搜索">联网搜索:关</button>
<button id="btnPreset" title="设置预先提示词">预设</button>
<button id="btnClear" title="开始新对话">新对话</button>
@@ -544,6 +545,8 @@ let pending = false;
let webSearchEnabled = false;
let openAIProfiles = [];
let activeOpenAIName = '{{ .OpenAIName }}';
let searchProfiles = [];
let activeSearchName = '';
let imageB64 = ''; // 当前待发送图片的 data URI
let imageName = '';
@@ -556,6 +559,7 @@ const btnClear = document.getElementById('btnClear');
const btnPreset = document.getElementById('btnPreset');
const btnSearch = document.getElementById('btnSearch');
const modelSelect = document.getElementById('modelSelect');
const searchSelect = document.getElementById('searchSelect');
const btnNewChat = document.getElementById('btnNewChat');
const convList = document.getElementById('convList');
const presetModal = document.getElementById('presetModal');
@@ -630,6 +634,7 @@ function setInputDisabled(disabled) {
fileInput.disabled = disabled;
btnSearch.disabled = disabled;
modelSelect.disabled = disabled || openAIProfiles.length <= 1;
searchSelect.disabled = disabled || searchProfiles.length <= 1;
}
function updateSearchButton() {
@@ -658,6 +663,27 @@ async function loadOpenAIProfiles() {
modelSelect.disabled = pending || openAIProfiles.length <= 1;
}
async function loadSearchProfiles() {
const res = await fetch('/api/search');
if (!res.ok) {
const err = await res.json().catch(() => ({ error: '加载搜索配置失败' }));
throw new Error(err.error || '加载搜索配置失败');
}
const data = await res.json();
searchProfiles = Array.isArray(data.profiles) ? data.profiles : [];
activeSearchName = data.active || '';
searchSelect.innerHTML = '';
for (const profile of searchProfiles) {
const opt = document.createElement('option');
opt.value = profile.name;
opt.textContent = `${profile.name} · ${profile.provider}`;
opt.selected = profile.name === activeSearchName;
searchSelect.appendChild(opt);
}
searchSelect.disabled = pending || searchProfiles.length <= 1;
}
// ── 对话列表 ──────────────────────────────────────────────
async function loadConversationList() {
try {
@@ -1063,6 +1089,31 @@ modelSelect.addEventListener('change', async () => {
alert(e.message);
}
});
searchSelect.addEventListener('change', async () => {
if (pending) {
searchSelect.value = activeSearchName;
return;
}
const nextName = searchSelect.value;
const prevName = activeSearchName;
try {
const res = await fetch('/api/search/active', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: nextName }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: '切换搜索源失败' }));
throw new Error(err.error || '切换搜索源失败');
}
const data = await res.json();
activeSearchName = data.active;
searchSelect.value = activeSearchName;
} catch (e) {
searchSelect.value = prevName;
alert(e.message);
}
});
btnNewChat.addEventListener('click', newConversation);
btnClear.addEventListener('click', newConversation);
btnPreset.addEventListener('click', openPresetModal);
@@ -1083,6 +1134,7 @@ presetModal.addEventListener('click', e => {
// 自动聚焦 & 初始化
updateSearchButton();
loadOpenAIProfiles().catch(e => alert(e.message));
loadSearchProfiles().catch(e => alert(e.message));
loadConversationList();
inputBox.focus();
</script>