Files
alpha_tools/backup_code/wqb-server/static/decoder.js
T
2026-02-05 10:18:53 +08:00

1009 lines
38 KiB
JavaScript
Executable File
Raw 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.
/**
* 模板解码模块 - 完整列表迭代方法
*
* 此模块使用笛卡尔积方法处理模板表达式的解码。
* 它依赖于 script.js 中定义的全局 'templates' 变量
*
* 函数列表:
* - decodeTemplates(): 解码模板的主函数
* - generateCombinations(): 使用笛卡尔积生成所有可能的组合
* - displayDecodedResults(): 显示解码结果
* - searchResults(): 在所有解码结果中搜索
* - copySingleResult(): 复制单个结果到剪贴板
* - copyAllResults(): 复制所有结果到剪贴板
* - downloadResults(): 将结果下载为文本文件
*/
// 全局变量,用于存储所有解码后的表达式以供搜索
let allDecodedExpressions = [];
let displayedExpressions = [];
const MAX_DISPLAY_RESULTS = 999;
// 使用完整列表方法解码模板
function decodeTemplates() {
const editor = document.getElementById('expressionEditor');
const expression = editor.value.trim();
const errorsDiv = document.getElementById('grammarErrors');
// 检查表达式是否为空
if (!expression) {
errorsDiv.innerHTML = '<div class="error-item"><strong>错误:</strong> 请输入要解码的表达式</div>';
return;
}
// 首先,检测所有模板
const templateRegex = /<(\w+)\/>/g;
const matches = [...expression.matchAll(templateRegex)];
const uniqueTemplates = [...new Set(matches.map(match => match[1]))];
// 检查是否有要解码的模板
if (uniqueTemplates.length === 0) {
errorsDiv.innerHTML = '<div class="error-item"><strong>错误:</strong> 在要解码的表达式中未找到模板</div>';
return;
}
// 检查是否所有模板都已配置
const unconfigured = [];
const templateValues = new Map();
uniqueTemplates.forEach(templateName => {
const template = templates.get(templateName);
if (!template || !template.variables || template.variables.length === 0) {
unconfigured.push(templateName);
} else {
templateValues.set(templateName, template.variables);
}
});
// 如果有任何模板未配置,显示错误
if (unconfigured.length > 0) {
errorsDiv.innerHTML = `<div class="error-item">
<strong>错误:</strong> 以下模板在解码前需要配置:
${unconfigured.map(t => `<span class="template-name" style="font-family: monospace;">&lt;${t}/&gt;</span>`).join(', ')}
</div>`;
return;
}
// 计算总组合数
let totalCombinations = 1;
templateValues.forEach(values => {
totalCombinations *= values.length;
});
// 如果组合数过多,发出警告
if (totalCombinations > 1000) {
if (!confirm(`这将生成 ${totalCombinations} 个表达式。可能需要一些时间。继续吗?`)) {
return;
}
}
// 生成所有组合(笛卡尔积)
const combinations = generateCombinations(templateValues);
// 生成解码后的表达式
const decodedExpressions = combinations.map(combination => {
let decodedExpression = expression;
combination.forEach(({template, value}) => {
const regex = new RegExp(`<${template}/>`, 'g');
decodedExpression = decodedExpression.replace(regex, value);
});
return decodedExpression;
});
// 全局存储所有表达式
allDecodedExpressions = decodedExpressions;
// 显示结果(限制为 MAX_DISPLAY_RESULTS)
displayDecodedResults(decodedExpressions.slice(0, MAX_DISPLAY_RESULTS), decodedExpressions.length);
// 清除错误并显示成功信息
errorsDiv.innerHTML = `<div class="success-message">
✓ 使用完整列表方法成功解码 ${decodedExpressions.length} 个表达式
${decodedExpressions.length > MAX_DISPLAY_RESULTS ?
`<br>⚠️ 显示前 ${MAX_DISPLAY_RESULTS} 个结果。使用搜索查找特定表达式。` : ''}
</div>`;
}
// 生成模板值的所有组合(笛卡尔积)
function generateCombinations(templateValues) {
const templates = Array.from(templateValues.keys());
if (templates.length === 0) return [];
const combinations = [];
function generate(index, current) {
if (index === templates.length) {
combinations.push([...current]);
return;
}
const template = templates[index];
const values = templateValues.get(template);
for (const value of values) {
current.push({template, value});
generate(index + 1, current);
current.pop();
}
}
generate(0, []);
return combinations;
}
// 显示解码结果
function displayDecodedResults(expressions, totalCount = null, isRandom = false) {
const resultsList = document.getElementById('resultsList');
// 清除之前的结果
resultsList.innerHTML = '';
// 如果结果数量超过显示数量,添加搜索框(仅用于完整迭代)
if (!isRandom && totalCount && totalCount > MAX_DISPLAY_RESULTS) {
const searchContainer = document.createElement('div');
searchContainer.className = 'results-search-container';
searchContainer.innerHTML = `
<input type="text" id="resultsSearchInput" class="results-search-input"
placeholder="在所有 ${totalCount} 个表达式中搜索...">
<button id="resultsSearchBtn" class="btn btn-secondary btn-small">搜索</button>
<button id="resultsClearSearchBtn" class="btn btn-outline btn-small" style="display: none;">清除搜索</button>
`;
resultsList.appendChild(searchContainer);
// 为搜索添加事件监听器
document.getElementById('resultsSearchBtn').addEventListener('click', searchResults);
document.getElementById('resultsSearchInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') searchResults();
});
document.getElementById('resultsClearSearchBtn').addEventListener('click', clearSearch);
}
// 添加关于结果数量的信息
if (expressions.length > 0) {
const infoDiv = document.createElement('div');
infoDiv.className = 'results-info';
if (isRandom) {
// 对于随机结果,显示实际选择的数量与总组合数的对比
const actualSelectedCount = allDecodedExpressions.length;
if (actualSelectedCount > expressions.length) {
infoDiv.innerHTML = `从 <strong>${totalCount}</strong> 个总组合中随机选择了 <strong>${actualSelectedCount}</strong> 个表达式<br>
<small>显示前 <strong>${expressions.length}</strong> 个结果。下载将包含所有 <strong>${actualSelectedCount}</strong> 个表达式。</small>`;
} else {
infoDiv.innerHTML = `从 <strong>${totalCount}</strong> 个总组合中随机选择了 <strong>${expressions.length}</strong> 个表达式`;
}
} else if (totalCount && totalCount > expressions.length) {
infoDiv.innerHTML = `总共生成了 <strong>${totalCount}</strong> 个表达式。
显示 <strong>${expressions.length}</strong> 个结果
${expressions.length === MAX_DISPLAY_RESULTS ? '(前999个)' : '(已过滤)'}。`;
} else {
infoDiv.textContent = `使用完整列表迭代生成了 ${expressions.length} 个表达式`;
}
resultsList.appendChild(infoDiv);
}
// 全局存储显示的表达式
displayedExpressions = expressions;
// 添加每个表达式
expressions.forEach((expr, index) => {
const resultItem = document.createElement('div');
resultItem.className = 'result-item';
const number = document.createElement('span');
number.className = 'result-number';
number.textContent = `${index + 1}.`;
const expression = document.createElement('span');
expression.className = 'result-expression';
expression.textContent = expr;
resultItem.appendChild(number);
resultItem.appendChild(expression);
// 复制按钮已禁用
// resultItem.appendChild(copyBtn);
resultsList.appendChild(resultItem);
});
// 显示结果标签页并更新徽章
const resultsTab = document.getElementById('resultsTab');
const resultsBadge = document.getElementById('resultsBadge');
resultsTab.style.display = 'flex';
resultsBadge.textContent = totalCount || expressions.length;
// 导航到结果页面
navigateToPage('results');
}
// 在所有结果中搜索
function searchResults() {
const searchInput = document.getElementById('resultsSearchInput');
const searchTerm = searchInput.value.trim().toLowerCase();
if (!searchTerm) {
// 如果搜索为空,再次显示前1000个
displayDecodedResults(allDecodedExpressions.slice(0, MAX_DISPLAY_RESULTS), allDecodedExpressions.length);
return;
}
// 根据搜索词过滤所有表达式
const filteredExpressions = allDecodedExpressions.filter(expr =>
expr.toLowerCase().includes(searchTerm)
);
// 显示过滤后的结果(仍然限制为 MAX_DISPLAY_RESULTS)
displayDecodedResults(filteredExpressions.slice(0, MAX_DISPLAY_RESULTS), allDecodedExpressions.length);
// 显示清除按钮
document.getElementById('resultsClearSearchBtn').style.display = 'inline-block';
// 更新信息消息
const errorsDiv = document.getElementById('grammarErrors');
if (filteredExpressions.length === 0) {
errorsDiv.innerHTML = `<div class="warning-message">
未找到匹配 "${searchTerm}" 的表达式
</div>`;
} else if (filteredExpressions.length > MAX_DISPLAY_RESULTS) {
errorsDiv.innerHTML = `<div class="success-message">
找到 ${filteredExpressions.length} 个表达式匹配 "${searchTerm}"。
显示前 ${MAX_DISPLAY_RESULTS} 个结果。
</div>`;
} else {
errorsDiv.innerHTML = `<div class="success-message">
找到 ${filteredExpressions.length} 个表达式匹配 "${searchTerm}"
</div>`;
}
}
// 清除搜索并显示原始结果
function clearSearch() {
document.getElementById('resultsSearchInput').value = '';
document.getElementById('resultsClearSearchBtn').style.display = 'none';
displayDecodedResults(allDecodedExpressions.slice(0, MAX_DISPLAY_RESULTS), allDecodedExpressions.length);
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 显示总共 ${allDecodedExpressions.length} 个表达式中的前 ${MAX_DISPLAY_RESULTS} 个
</div>`;
}
// 复制单个结果
function copySingleResult(expression) {
navigator.clipboard.writeText(expression).then(() => {
// 显示临时成功消息
const errorsDiv = document.getElementById('grammarErrors');
const prevContent = errorsDiv.innerHTML;
errorsDiv.innerHTML = '<div class="success-message">✓ 已复制到剪贴板</div>';
setTimeout(() => {
errorsDiv.innerHTML = prevContent;
}, 2000);
});
}
// 复制显示的结果
function copyDisplayedResults() {
// 复制所有当前显示的表达式
const expressions = displayedExpressions.join('\n');
navigator.clipboard.writeText(expressions).then(() => {
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 已复制 ${displayedExpressions.length} 个显示的表达式到剪贴板
</div>`;
});
}
// 复制所有结果
function copyAllResults() {
// 复制所有生成的表达式
const expressions = allDecodedExpressions.join('\n');
navigator.clipboard.writeText(expressions).then(() => {
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 所有 ${allDecodedExpressions.length} 个表达式已复制到剪贴板
</div>`;
}).catch(err => {
// 处理大剪贴板操作可能出现的错误
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="error-item">
<strong>错误:</strong> 复制到剪贴板失败。数据可能过大。
请改用"下载全部"按钮。
</div>`;
});
}
// 将结果下载为文本文件
function downloadResults() {
// 下载表达式(全部或随机选择)
const expressions = allDecodedExpressions.join('\n');
const blob = new Blob([expressions], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'decoded_expressions.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 已将 ${allDecodedExpressions.length} 个表达式下载为 decoded_expressions.txt
</div>`;
}
// 随机迭代 - 生成全部然后随机选择
function randomIteration() {
const editor = document.getElementById('expressionEditor');
const expression = editor.value.trim();
const errorsDiv = document.getElementById('grammarErrors');
const randomCountInput = document.getElementById('randomCount');
const randomCount = parseInt(randomCountInput.value) || 10;
// 检查表达式是否为空
if (!expression) {
errorsDiv.innerHTML = '<div class="error-item"><strong>错误:</strong> 请输入要解码的表达式</div>';
return;
}
// 首先,检测所有模板
const templateRegex = /<(\w+)\/>/g;
const matches = [...expression.matchAll(templateRegex)];
const uniqueTemplates = [...new Set(matches.map(match => match[1]))];
// 检查是否有要解码的模板
if (uniqueTemplates.length === 0) {
errorsDiv.innerHTML = '<div class="error-item"><strong>错误:</strong> 在要解码的表达式中未找到模板</div>';
return;
}
// 检查是否所有模板都已配置
const unconfigured = [];
const templateValues = new Map();
uniqueTemplates.forEach(templateName => {
const template = templates.get(templateName);
if (!template || !template.variables || template.variables.length === 0) {
unconfigured.push(templateName);
} else {
templateValues.set(templateName, template.variables);
}
});
// 如果有任何模板未配置,显示错误
if (unconfigured.length > 0) {
errorsDiv.innerHTML = `<div class="error-item">
<strong>错误:</strong> 以下模板在解码前需要配置:
${unconfigured.map(t => `<span class="template-name" style="font-family: monospace;">&lt;${t}/&gt;</span>`).join(', ')}
</div>`;
return;
}
// 计算总组合数
let totalCombinations = 1;
templateValues.forEach(values => {
totalCombinations *= values.length;
});
// 验证随机数量
if (randomCount > totalCombinations) {
errorsDiv.innerHTML = `<div class="warning-message">
⚠️ 请求了 ${randomCount} 个随机表达式,但只有 ${totalCombinations} 个唯一组合存在。
改为生成所有 ${totalCombinations} 个表达式。
</div>`;
}
// 生成所有组合(笛卡尔积)
const combinations = generateCombinations(templateValues);
// 生成所有解码后的表达式
const allExpressions = combinations.map(combination => {
let decodedExpression = expression;
combination.forEach(({template, value}) => {
const regex = new RegExp(`<${template}/>`, 'g');
decodedExpression = decodedExpression.replace(regex, value);
});
return decodedExpression;
});
// 随机选择请求数量的表达式
const selectedExpressions = [];
const actualCount = Math.min(randomCount, allExpressions.length);
if (actualCount === allExpressions.length) {
// 如果请求全部或更多,直接返回全部
selectedExpressions.push(...allExpressions);
} else {
// 无放回随机选择
const indices = new Set();
while (indices.size < actualCount) {
indices.add(Math.floor(Math.random() * allExpressions.length));
}
indices.forEach(index => {
selectedExpressions.push(allExpressions[index]);
});
}
// 全局存储所有选择的表达式以供下载(不受显示限制)
allDecodedExpressions = selectedExpressions;
// 对于显示,限制为 MAX_DISPLAY_RESULTS,但保留完整集合以供下载
const displayExpressions = selectedExpressions.slice(0, MAX_DISPLAY_RESULTS);
// 显示结果(显示有限,但下载计数完整)
displayDecodedResults(displayExpressions, allExpressions.length, true);
// 清除错误并显示成功信息,明确说明显示与下载的区别
if (selectedExpressions.length > MAX_DISPLAY_RESULTS) {
errorsDiv.innerHTML = `<div class="success-message">
✓ 从 ${allExpressions.length} 个总组合中随机选择了 ${selectedExpressions.length} 个表达式<br>
📺 显示前 ${MAX_DISPLAY_RESULTS} 个结果。下载将包含所有 ${selectedExpressions.length} 个表达式。
</div>`;
} else {
errorsDiv.innerHTML = `<div class="success-message">
✓ 从 ${allExpressions.length} 个总组合中随机选择了 ${selectedExpressions.length} 个表达式
</div>`;
}
}
// 为 Next Move 打开设置模态框
function openSettingsModal() {
const modal = document.getElementById('settingsModal');
modal.style.display = 'block';
updateTotalCombinations();
// 为设置输入添加事件监听器
document.querySelectorAll('.setting-value-input').forEach(input => {
input.addEventListener('input', updateTotalCombinations);
});
// 为添加设置按钮添加事件监听器
document.getElementById('addSettingBtn').addEventListener('click', addCustomSetting);
// 为测试周期滑块添加事件监听器
const testPeriodSlider = document.querySelector('.test-period-slider');
if (testPeriodSlider) {
testPeriodSlider.addEventListener('input', updateTestPeriodValue);
// 初始化显示值
updateTestPeriodValue();
}
}
// 关闭设置模态框
function closeSettingsModal() {
const modal = document.getElementById('settingsModal');
modal.style.display = 'none';
}
// 更新测试周期值显示
function updateTestPeriodValue() {
const slider = document.querySelector('.test-period-slider');
const valueDisplay = document.getElementById('testPeriodValue');
if (slider && valueDisplay) {
const totalMonths = parseInt(slider.value);
const years = Math.floor(totalMonths / 12);
const months = totalMonths % 12;
const periodValue = `P${years}Y${months}M`;
valueDisplay.textContent = periodValue;
// 更新滑块的 value 属性,以便 parseSettingValues 可以读取
slider.setAttribute('data-period-value', periodValue);
}
}
// 添加自定义设置行
function addCustomSetting() {
const tbody = document.getElementById('settingsTableBody');
const row = document.createElement('tr');
row.className = 'custom-setting-row';
row.innerHTML = `
<td><input type="text" class="setting-name-input form-input" placeholder="设置名称"></td>
<td><input type="text" class="setting-value-input" data-setting="custom" placeholder="值"></td>
<td><select class="setting-type-select"><option>string</option><option>number</option><option>boolean</option></select></td>
<td><button class="remove-setting-btn" onclick="removeCustomSetting(this)">移除</button></td>
`;
tbody.appendChild(row);
// 为新输入添加事件监听器
row.querySelector('.setting-value-input').addEventListener('input', updateTotalCombinations);
row.querySelector('.setting-name-input').addEventListener('input', updateTotalCombinations);
}
// 移除自定义设置行
function removeCustomSetting(button) {
button.closest('tr').remove();
updateTotalCombinations();
}
// 计算总组合数
function updateTotalCombinations() {
let totalCombinations = allDecodedExpressions.length;
// 获取所有设置及其值
const settingInputs = document.querySelectorAll('.setting-value-input');
settingInputs.forEach(input => {
const values = input.value.split(',').map(v => v.trim()).filter(v => v !== '');
if (values.length > 1) {
totalCombinations *= values.length;
}
});
document.getElementById('totalCombinations').textContent = totalCombinations.toLocaleString();
}
// 解析设置值(处理逗号分隔的值)
function parseSettingValues() {
const settings = {};
const variations = {};
const types = {};
// 获取预定义设置
const settingRows = document.querySelectorAll('#settingsTableBody tr');
settingRows.forEach(row => {
const nameCell = row.cells[0];
// 使用选择器或输入框获取值
let input = row.querySelector('.setting-value-input');
if (input) {
let settingName;
// 检查是否为自定义设置
const nameInput = row.querySelector('.setting-name-input');
if (nameInput) {
settingName = nameInput.value.trim();
if (!settingName) return; // 如果没有名称,跳过
} else {
settingName = nameCell.textContent.trim();
}
// 获取类型
const typeSelect = row.querySelector('.setting-type-select');
const type = typeSelect ? typeSelect.value : 'string';
types[settingName] = type;
// 对于选择下拉框,以不同方式获取值
let values = [];
if (input.tagName === 'SELECT') {
values = [input.value];
} else if (input.type === 'range' && settingName === 'testPeriod') {
// 测试周期滑块的特别处理
const periodValue = input.getAttribute('data-period-value') || 'P0Y0M';
values = [periodValue];
} else {
values = input.value.split(',').map(v => v.trim()).filter(v => v !== '');
}
// 根据类型转换值
const convertedValues = values.map(v => {
if (type === 'number') {
const num = parseFloat(v);
return isNaN(num) ? v : num;
} else if (type === 'boolean') {
if (typeof v === 'boolean') return v;
if (typeof v === 'string') {
if (v.toLowerCase() === 'true') return true;
if (v.toLowerCase() === 'false') return false;
}
return false;
} else {
return v;
}
});
if (convertedValues.length === 0) {
// 如果没有值,使用空字符串
settings[settingName] = '';
} else if (convertedValues.length === 1) {
// 单个值
settings[settingName] = convertedValues[0];
} else {
// 多个值 - 存储以供迭代
variations[settingName] = convertedValues;
settings[settingName] = convertedValues[0]; // 默认为第一个值
}
}
});
return { settings, variations, types };
}
// 生成所有设置组合
function generateSettingCombinations(baseSettings, variations) {
const variationKeys = Object.keys(variations);
if (variationKeys.length === 0) {
return [baseSettings];
}
const combinations = [];
function generate(index, current) {
if (index === variationKeys.length) {
combinations.push({...current});
return;
}
const key = variationKeys[index];
const values = variations[key];
for (const value of values) {
current[key] = value;
generate(index + 1, current);
}
}
generate(0, {...baseSettings});
return combinations;
}
// 将设置应用到表达式
function applySettings() {
const { settings, variations, types } = parseSettingValues();
// 始终包含 instrumentType 和 language
settings.instrumentType = settings.instrumentType || "EQUITY";
settings.language = settings.language || "FASTEXPR";
// 生成所有设置组合
const settingCombinations = generateSettingCombinations(settings, variations);
// 创建所有表达式-设置组合
const allCombinations = [];
allDecodedExpressions.forEach(expr => {
settingCombinations.forEach(settingCombo => {
const fullExpression = {
type: "REGULAR",
settings: settingCombo,
regular: expr.replace(/\n/g, '') // 移除换行符
};
allCombinations.push(JSON.stringify(fullExpression, null, 2));
});
});
// 创建 JSON 数组
const jsonContent = '[\n' + allCombinations.join(',\n') + '\n]';
// 下载带设置的表达式
const blob = new Blob([jsonContent], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'expressions_with_settings.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// 关闭模态框并显示成功信息
closeSettingsModal();
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 已将 ${allCombinations.length} 个表达式配置下载为 expressions_with_settings.json
</div>`;
}
// 全局存储测试结果
let allTestResults = [];
// 使用 BRAIN API 生成并测试表达式
async function generateAndTest() {
const { settings, variations, types } = parseSettingValues();
// 检查用户是否使用正确方法登录到 BRAIN
if (!window.brainAPI || !window.brainAPI.isConnectedToBrain()) {
alert('在测试表达式之前,请先连接到 BRAIN。');
return;
}
// 从全局变量获取会话 ID
const sessionId = brainSessionId;
if (!sessionId) {
alert('未找到 BRAIN 会话。请重新连接到 BRAIN。');
return;
}
// 始终包含 instrumentType 和 language
settings.instrumentType = settings.instrumentType || "EQUITY";
settings.language = settings.language || "FASTEXPR";
// 生成所有设置组合
const settingCombinations = generateSettingCombinations(settings, variations);
// 创建所有表达式-设置组合
const allCombinations = [];
allDecodedExpressions.forEach(expr => {
settingCombinations.forEach(settingCombo => {
const fullExpression = {
type: "REGULAR",
settings: settingCombo,
regular: expr.replace(/\n/g, '') // 移除换行符
};
allCombinations.push(fullExpression);
});
});
// 随机选择一个表达式进行测试
const randomIndex = Math.floor(Math.random() * allCombinations.length);
const testExpression = allCombinations[randomIndex];
// 关闭设置模态框并打开测试结果模态框
closeSettingsModal();
openBrainTestResultsModal();
// 显示进度
const progressDiv = document.getElementById('brainTestProgress');
const progressBarFill = document.getElementById('progressBarFill');
const progressText = document.getElementById('progressText');
const resultsDiv = document.getElementById('brainTestResults');
progressDiv.style.display = 'block';
resultsDiv.innerHTML = '';
allTestResults = [];
// 测试单个随机选择的表达式
progressText.textContent = `正在测试表达式 ${randomIndex + 1},共 ${allCombinations.length} 个(随机选择)...`;
progressBarFill.style.width = '50%';
try {
const response = await fetch('/api/test-expression', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Session-ID': sessionId
},
body: JSON.stringify(testExpression)
});
const result = await response.json();
// 存储结果
const testResult = {
expression: testExpression.regular,
settings: testExpression.settings,
success: result.success,
status: result.status || (result.success ? 'SUCCESS' : 'ERROR'),
message: result.message || result.error || '未知错误',
details: result,
totalPossible: allCombinations.length,
testedIndex: randomIndex + 1
};
allTestResults.push(testResult);
// 更新进度
progressBarFill.style.width = '100%';
progressText.textContent = '测试完成!';
// 短暂延迟后隐藏进度
setTimeout(() => {
progressDiv.style.display = 'none';
// 显示结果
displaySingleTestResult(testResult);
// 显示下载按钮
document.getElementById('downloadTestResultsBtn').style.display = 'inline-block';
document.getElementById('downloadExpressionWithSettingsBtn').style.display = 'inline-block';
}, 500);
} catch (error) {
const testResult = {
expression: testExpression.regular,
settings: testExpression.settings,
success: false,
status: 'ERROR',
message: `网络错误: ${error.message}`,
details: { error: error.message },
totalPossible: allCombinations.length,
testedIndex: randomIndex + 1
};
allTestResults.push(testResult);
progressDiv.style.display = 'none';
displaySingleTestResult(testResult);
document.getElementById('downloadTestResultsBtn').style.display = 'inline-block';
document.getElementById('downloadExpressionWithSettingsBtn').style.display = 'inline-block';
}
}
// 显示单个测试结果
function displaySingleTestResult(result) {
const resultsDiv = document.getElementById('brainTestResults');
// 添加摘要信息
const summaryDiv = document.createElement('div');
summaryDiv.className = 'test-summary';
summaryDiv.innerHTML = `
<h4>随机测试结果</h4>
<p>从 ${result.totalPossible} 个可能组合中随机选择了表达式 #${result.testedIndex}</p>
`;
resultsDiv.appendChild(summaryDiv);
// 添加测试结果
const resultItem = document.createElement('div');
resultItem.className = `test-result-item ${result.success && result.status !== 'ERROR' ? 'success' : 'error'}`;
const expressionDiv = document.createElement('div');
expressionDiv.className = 'test-result-expression';
expressionDiv.innerHTML = `<strong>表达式:</strong> ${result.expression}`;
// 按照笔记本中的样子显示消息
const messageDiv = document.createElement('div');
messageDiv.className = 'test-result-message';
messageDiv.style.whiteSpace = 'pre-wrap';
messageDiv.style.fontFamily = 'monospace';
messageDiv.style.backgroundColor = '#f5f5f5';
messageDiv.style.padding = '10px';
messageDiv.style.borderRadius = '4px';
messageDiv.style.marginTop = '10px';
// 格式化消息 - 如果是完整的响应对象,美观地显示它
if (result.details && result.details.full_response) {
const fullResponse = result.details.full_response;
// 如果是具有预期结构的对象,美观地格式化它
if (typeof fullResponse === 'object' && fullResponse.id && fullResponse.type && fullResponse.status) {
// 格式化为 Python 字典输出
messageDiv.textContent = JSON.stringify(fullResponse, null, 2).replace(/"/g, "'");
} else if (typeof fullResponse === 'object') {
// 对于其他对象,直接字符串化它们
messageDiv.textContent = JSON.stringify(fullResponse, null, 2);
} else {
// 对于非对象,显示消息字符串
messageDiv.textContent = result.message;
}
} else {
// 回退到简单消息
messageDiv.textContent = result.message;
}
resultItem.appendChild(expressionDiv);
resultItem.appendChild(messageDiv);
// 添加设置信息
const settingsDiv = document.createElement('div');
settingsDiv.className = 'test-result-message';
settingsDiv.innerHTML = '<strong>使用的设置:</strong>';
const settingsList = document.createElement('ul');
settingsList.style.margin = '5px 0';
settingsList.style.paddingLeft = '20px';
for (const [key, value] of Object.entries(result.settings)) {
const li = document.createElement('li');
li.textContent = `${key}: ${value}`;
settingsList.appendChild(li);
}
settingsDiv.appendChild(settingsList);
resultItem.appendChild(settingsDiv);
resultsDiv.appendChild(resultItem);
}
// 旧函数名的兼容性包装器
function addTestResultToDisplay(result, index) {
// 如果不存在,向结果添加索引信息
if (!result.testedIndex) {
result.testedIndex = index;
}
if (!result.totalPossible) {
result.totalPossible = allDecodedExpressions.length;
}
displaySingleTestResult(result);
}
// 显示测试摘要(保持兼容性)
function showTestSummary(total, success, error) {
const resultsDiv = document.getElementById('brainTestResults');
const summaryDiv = document.createElement('div');
summaryDiv.className = 'test-summary';
summaryDiv.innerHTML = `
<h4>测试摘要</h4>
<div class="test-summary-stats">
<div class="test-summary-stat">
<div class="test-summary-stat-value">${total}</div>
<div class="test-summary-stat-label">总测试数</div>
</div>
<div class="test-summary-stat" style="color: #28a745;">
<div class="test-summary-stat-value">${success}</div>
<div class="test-summary-stat-label">成功</div>
</div>
<div class="test-summary-stat" style="color: #dc3545;">
<div class="test-summary-stat-value">${error}</div>
<div class="test-summary-stat-label">错误</div>
</div>
<div class="test-summary-stat">
<div class="test-summary-stat-value">${((success / total) * 100).toFixed(1)}%</div>
<div class="test-summary-stat-label">成功率</div>
</div>
</div>
`;
resultsDiv.insertBefore(summaryDiv, resultsDiv.firstChild);
}
// 打开测试结果模态框
function openBrainTestResultsModal() {
const modal = document.getElementById('brainTestResultsModal');
modal.style.display = 'block';
}
// 关闭测试结果模态框
function closeBrainTestResultsModal() {
const modal = document.getElementById('brainTestResultsModal');
modal.style.display = 'none';
}
// 下载测试结果
function downloadTestResults() {
const results = allTestResults.map(result => ({
expression: result.expression,
settings: result.settings,
status: result.status,
message: result.message,
details: result.details
}));
const jsonContent = JSON.stringify(results, null, 2);
const blob = new Blob([jsonContent], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'brain_test_results.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 已下载 ${allTestResults.length} 个表达式的测试结果
</div>`;
}
// 下载带设置的表达式(与"生成并下载"相同)
function downloadExpressionWithSettings() {
// 从模态框获取当前设置(与 applySettings 相同的逻辑)
const { settings, variations, types } = parseSettingValues();
// 始终包含 instrumentType 和 language
settings.instrumentType = settings.instrumentType || "EQUITY";
settings.language = settings.language || "FASTEXPR";
// 生成所有设置组合
const settingCombinations = generateSettingCombinations(settings, variations);
// 创建所有表达式-设置组合
const allCombinations = [];
allDecodedExpressions.forEach(expr => {
settingCombinations.forEach(settingCombo => {
const fullExpression = {
type: "REGULAR",
settings: settingCombo,
regular: expr.replace(/\n/g, '') // 移除换行符
};
allCombinations.push(JSON.stringify(fullExpression, null, 2));
});
});
// 创建 JSON 数组
const jsonContent = '[\n' + allCombinations.join(',\n') + '\n]';
// 下载带设置的表达式
const blob = new Blob([jsonContent], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'expressions_with_settings.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
const errorsDiv = document.getElementById('grammarErrors');
errorsDiv.innerHTML = `<div class="success-message">
✓ 已将 ${allCombinations.length} 个表达式配置下载为 expressions_with_settings.json
</div>`;
}