document.addEventListener('DOMContentLoaded', async () => { try { const response = await fetch('/api/config/defaults'); const result = await response.json(); if (result.success && result.config) { const config = result.config; if (config.brain) { if (config.brain.username) { document.getElementById('brainUsername').value = config.brain.username; } if (config.brain.password) { document.getElementById('brainPassword').value = config.brain.password; } } if (config.llm) { if (config.llm.api_key) { document.getElementById('llmApiKey').value = config.llm.api_key; } if (config.llm.base_url) { document.getElementById('llmBaseUrl').value = config.llm.base_url; } if (config.llm.model) { document.getElementById('llmModel').value = config.llm.model; } } if (config.transformer) { if (config.transformer.top_n_datafield) { document.getElementById('topNDatafield').value = config.transformer.top_n_datafield; } if (config.transformer.data_type) { document.getElementById('dataType').value = config.transformer.data_type; } if (config.transformer.alpha_id) { document.getElementById('alphaId').value = config.transformer.alpha_id; } } } } catch (error) { console.error('加载默认配置失败:', error); } }); const form = document.getElementById('transformerForm'); const submitBtn = document.getElementById('submitBtn'); const downloadBtn = document.getElementById('downloadBtn'); const loginAndFetchBtn = document.getElementById('loginAndFetchBtn'); const testLLMBtn = document.getElementById('testLLMBtn'); const downloadDatafieldsBtn = document.getElementById('downloadDatafieldsBtn'); const regionSelect = document.getElementById('region'); const delaySelect = document.getElementById('delay'); const universeSelect = document.getElementById('universe'); const dataTypeSelect = document.getElementById('dataType'); const categoryButtons = document.getElementById('category-buttons'); let optionsData = {}; dataTypeSelect.addEventListener('change', function() { if (this.value === 'VECTOR') { if (!confirm("请确保您输入的原型Alpha中正确地使用了vector operator,否则极容易造成数据类型错误!")) { this.value = 'MATRIX'; } } }); loginAndFetchBtn.addEventListener('click', async () => { const username = document.getElementById('brainUsername').value.trim(); const password = document.getElementById('brainPassword').value; if (!username || !password) { alert('请先填写BRAIN用户名和密码'); return; } loginAndFetchBtn.disabled = true; loginAndFetchBtn.textContent = '正在登录...'; try { const response = await fetch('/api/transformer/login-and-fetch-options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const result = await response.json(); if (result.success) { optionsData = result.options; populateRegionSelect(); regionSelect.disabled = false; if (result.categories) { populateCategoryButtons(result.categories); } loginAndFetchBtn.textContent = '登录成功'; } else { alert('登录失败: ' + result.error); loginAndFetchBtn.disabled = false; loginAndFetchBtn.textContent = '登录BRAIN并获取选项'; } } catch (error) { alert('登录出错: ' + error.message); loginAndFetchBtn.disabled = false; loginAndFetchBtn.textContent = '登录BRAIN并获取选项'; } }); testLLMBtn.addEventListener('click', async () => { const apiKey = document.getElementById('llmApiKey').value.trim(); const baseUrl = document.getElementById('llmBaseUrl').value.trim(); const model = document.getElementById('llmModel').value.trim(); if (!apiKey || !baseUrl || !model) { alert('请先填写完整的 LLM 配置'); return; } testLLMBtn.disabled = true; testLLMBtn.textContent = '测试中...'; testLLMBtn.classList.remove('btn-success', 'btn-error'); try { const response = await fetch('/api/test-llm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ llm_api_key: apiKey, llm_base_url: baseUrl, llm_model: model }) }); const result = await response.json(); if (result.success) { testLLMBtn.textContent = '连接成功'; testLLMBtn.classList.add('btn-success'); } else { testLLMBtn.textContent = '连接失败'; testLLMBtn.classList.add('btn-error'); } } catch (error) { testLLMBtn.textContent = '连接失败'; testLLMBtn.classList.add('btn-error'); } finally { testLLMBtn.disabled = false; } }); function populateRegionSelect() { while (regionSelect.options.length > 1) { regionSelect.remove(1); } const regions = Object.keys(optionsData); regions.forEach(region => { const option = document.createElement('option'); option.value = region; option.textContent = region; regionSelect.appendChild(option); }); } function populateCategoryButtons(categories) { categories.forEach(category => { const btn = document.createElement('button'); btn.type = 'button'; btn.dataset.value = category.id || category; btn.textContent = category.name || category; btn.onclick = function() { toggleCategory(this); }; btn.className = 'btn'; btn.style.cssText = 'padding: 4px 12px; font-size: 11px;'; categoryButtons.appendChild(btn); }); } // 全选/反选功能 function toggleAllCategories() { const otherBtns = categoryButtons.querySelectorAll('button:not(#cat-all)'); // 检查是否全部选中 let allSelected = true; otherBtns.forEach(b => { if (!isCategoryButtonSelected(b)) { allSelected = false; } }); if (allSelected) { // 全部选中 -> 全不选 otherBtns.forEach(b => { b.style.backgroundColor = 'white'; b.style.color = 'black'; }); } else { // 不是全部选中 -> 全选 otherBtns.forEach(b => { b.style.backgroundColor = '#87CEEB'; // 浅蓝色 b.style.color = 'black'; }); } } // 点击具体类别按钮 function toggleCategory(btn) { if (isCategoryButtonSelected(btn)) { btn.style.backgroundColor = 'white'; btn.style.color = 'black'; } else { btn.style.backgroundColor = '#87CEEB'; // 浅蓝色 btn.style.color = 'black'; } } // 检查类别按钮是否被选中的辅助函数 function isCategoryButtonSelected(btn) { const bg = btn.style.backgroundColor; // 浅蓝色或 rgb(135, 206, 235) 都表示选中 return bg === '#87CEEB' || bg === 'rgb(135, 206, 235)' || bg === 'lightblue'; } // 获取选中的类别列表 function getSelectedCategories() { const otherBtns = categoryButtons.querySelectorAll('button:not(#cat-all)'); // 获取选中的具体类别(使用新的选中判断) const selected = []; otherBtns.forEach(b => { if (isCategoryButtonSelected(b)) { selected.push(b.dataset.value); } }); // 如果没有选中任何具体类别,返回所有类别(默认全选) if (selected.length === 0) { const allCategories = []; otherBtns.forEach(b => { allCategories.push(b.dataset.value); }); return allCategories; } return selected; } regionSelect.addEventListener('change', () => { const selectedRegion = regionSelect.value; universeSelect.innerHTML = ''; universeSelect.disabled = true; if (selectedRegion && optionsData[selectedRegion]) { const delays = Object.keys(optionsData[selectedRegion]); // 保留 delaySelect 的当前值(如果有效) const currentDelay = delaySelect.value; delaySelect.innerHTML = ''; delays.forEach(delay => { const option = document.createElement('option'); option.value = delay; option.textContent = delay; if (delay === currentDelay) { option.selected = true; } delaySelect.appendChild(option); }); delaySelect.disabled = false; // 触发 delay change 事件来更新 universe if (currentDelay && optionsData[selectedRegion][currentDelay]) { delaySelect.dispatchEvent(new Event('change')); } } }); delaySelect.addEventListener('change', () => { const selectedRegion = regionSelect.value; const selectedDelay = delaySelect.value; universeSelect.innerHTML = ''; universeSelect.disabled = true; if (selectedRegion && selectedDelay && optionsData[selectedRegion][selectedDelay]) { const universes = optionsData[selectedRegion][selectedDelay]; universes.forEach(universe => { const option = document.createElement('option'); option.value = universe; option.textContent = universe; universeSelect.appendChild(option); }); universeSelect.disabled = false; } }); form.addEventListener('submit', async (e) => { e.preventDefault(); const formData = { alpha_id: document.getElementById('alphaId').value.trim(), llm_api_key: document.getElementById('llmApiKey').value.trim(), llm_base_url: document.getElementById('llmBaseUrl').value.trim(), llm_model: document.getElementById('llmModel').value.trim(), brain_username: document.getElementById('brainUsername').value.trim(), brain_password: document.getElementById('brainPassword').value.trim(), top_n_datafield: parseInt(document.getElementById('topNDatafield').value) || 50, max_retries: parseInt(document.getElementById('maxRetries').value) || 20, data_type: document.getElementById('dataType').value || 'MATRIX' }; const region = document.getElementById('region').value; const delay = document.getElementById('delay').value; const universe = document.getElementById('universe').value; if (region) formData.user_region = region; if (delay) formData.user_delay = parseInt(delay); if (universe) formData.user_universe = universe; // 使用 getSelectedCategories 获取选中的类别 const selectedCategories = getSelectedCategories(); if (selectedCategories.length > 0) { formData.user_category = selectedCategories; } submitBtn.disabled = true; submitBtn.textContent = '处理中...'; try { const response = await fetch('/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.success) { const successCount = result.expressions_success ? result.expressions_success.length : 0; const candidatesData = result.candidates || {}; const candidateTemplatesCount = Object.keys(candidatesData).length; const errorCount = result.expressions_error ? result.expressions_error.length : 0; // 显示下载按钮 downloadBtn.style.display = 'block'; downloadBtn.textContent = '下载结果 (' + successCount + '成功, ' + candidateTemplatesCount + '模板)'; downloadBtn.onclick = function() { const alphaId = document.getElementById('alphaId').value.trim(); window.location.href = '/api/download/' + alphaId; }; } else { downloadBtn.style.display = 'none'; downloadBtn.textContent = '下载结果 (ZIP)'; } } catch (error) { downloadBtn.style.display = 'none'; downloadBtn.textContent = '下载结果 (ZIP)'; } finally { submitBtn.disabled = false; submitBtn.textContent = '生成变种'; } }); // 下载数据字段缓存按钮 downloadDatafieldsBtn.addEventListener('click', async () => { const region = regionSelect.value; const delay = delaySelect.value; const universe = universeSelect.value; if (!region || !delay || !universe) { alert('请先选择地区、Delay和股票池'); return; } const brainUsername = document.getElementById('brainUsername').value.trim(); const brainPassword = document.getElementById('brainPassword').value.trim(); if (!brainUsername || !brainPassword) { alert('请先输入BRAIN用户名和密码'); return; } // 获取选中的类别(使用新函数) const selectedCategories = getSelectedCategories(); console.log('选中的类别:', selectedCategories); downloadDatafieldsBtn.disabled = true; downloadDatafieldsBtn.textContent = '正在下载...'; try { const requestBody = { username: brainUsername, password: brainPassword, region: region, delay: parseInt(delay), universe: universe, data_type: dataTypeSelect.value, category: selectedCategories // 总是传递类别列表 }; const response = await fetch('/api/download-datafields', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }); const result = await response.json(); if (!result.success) { alert('下载失败: ' + (result.error || '未知错误')); } } catch (error) { alert('下载出错: ' + error.message); } finally { downloadDatafieldsBtn.disabled = false; downloadDatafieldsBtn.textContent = '下载数据字段缓存'; } });