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 = '下载数据字段缓存';
}
});