// Idea House JavaScript - 处理数据字段选择和Coze API处理 // 全局变量 let dataFields = []; let filteredDataFields = []; let selectedFields = new Map(); // 字段ID -> 描述的映射 // 筛选状态变量 let columnFilters = { id: '', description: '', type: '', coverage: { min: null, max: null }, userCount: null, alphaCount: null }; let sortColumn = null; let sortOrder = 'asc'; // DOM加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { // 设置事件监听器 document.getElementById('loadDataFieldsBtn').addEventListener('click', loadDataFields); document.getElementById('clearSelectionBtn').addEventListener('click', clearSelection); document.getElementById('processFieldsBtn').addEventListener('click', processSelectedFields); // 设置API令牌切换按钮 const toggleBtn = document.getElementById('toggleApiTokenBtn'); const tokenInput = document.getElementById('cozeApiTokenInput'); toggleBtn.addEventListener('click', function() { if (tokenInput.type === 'password') { tokenInput.type = 'text'; toggleBtn.textContent = '隐藏'; } else { tokenInput.type = 'password'; toggleBtn.textContent = '显示'; } }); // 加载保存的配置 loadSavedCozeConfig(); // 设置保存配置按钮 document.getElementById('saveCozeConfigBtn').addEventListener('click', saveCozeConfig); // 设置清除配置按钮 document.getElementById('clearCozeConfigBtn').addEventListener('click', clearCozeConfig); // 设置筛选事件监听器 setupFilterEventListeners(); }); // 从BRAIN API加载数据字段 async function loadDataFields() { const region = document.getElementById('regionInput').value; const delay = document.getElementById('delayInput').value; const universe = document.getElementById('universeInput').value; const datasetId = document.getElementById('datasetInput').value; // 显示加载状态 document.getElementById('dataFieldsStats').textContent = '正在加载数据字段...'; document.getElementById('tableContainer').style.display = 'none'; try { // 从localStorage或cookie获取会话ID const sessionId = localStorage.getItem('brain_session_id'); if (!sessionId) { alert('请先从主页面连接到BRAIN'); return; } // 调用代理端点 const params = new URLSearchParams({ region: region, delay: delay, universe: universe, dataset_id: datasetId }); // 记录API调用 console.log('🚀 正在调用API获取数据字段和数据集描述'); console.log('📋 参数:', { region: region, delay: delay, universe: universe, dataset_id: datasetId }); // 并行获取数据字段和数据集描述 const [dataFieldsResponse, descriptionResponse] = await Promise.all([ fetch(`/idea-house/api/get-datafields-proxy?${params}`, { method: 'GET', headers: { 'Session-ID': sessionId } }), fetch(`/idea-house/api/get-dataset-description?${params}`, { method: 'GET', headers: { 'Session-ID': sessionId } }) ]); console.log('📥 收到响应:'); console.log(' 数据字段状态:', dataFieldsResponse.status); console.log(' 数据集描述状态:', descriptionResponse.status); if (!dataFieldsResponse.ok) { const errorData = await dataFieldsResponse.json(); throw new Error(errorData.error || '获取数据字段失败'); } dataFields = await dataFieldsResponse.json(); // 获取数据集描述(如果可用) let datasetDescription = ''; if (descriptionResponse.ok) { console.log('✅ 数据集描述响应正常,正在解析JSON...'); const descriptionData = await descriptionResponse.json(); console.log('📄 描述数据:', descriptionData); if (descriptionData.success) { datasetDescription = descriptionData.description; // 将其存储在全局中供后续使用 window.currentDatasetDescription = datasetDescription; console.log('✅ 数据集描述已存储:', datasetDescription); } else { console.log('⚠️ 描述响应success=false'); } } else { console.log('❌ 数据集描述响应不正常:', descriptionResponse.status); try { const errorData = await descriptionResponse.json(); console.log('❌ 错误详情:', errorData); } catch (e) { console.log('❌ 无法解析错误响应'); } } // 更新统计信息 document.getElementById('dataFieldsStats').textContent = `已加载 ${dataFields.length} 个数据字段`; // 使用数据集描述填充表格 populateDataFieldsTable(datasetDescription); // 显示表格 document.getElementById('tableContainer').style.display = 'block'; } catch (error) { console.error('加载数据字段失败:', error); document.getElementById('dataFieldsStats').textContent = `错误: ${error.message}`; alert(`加载数据字段失败: ${error.message}`); } } // 使用筛选和排序填充数据字段表格 function populateDataFieldsTable(datasetDescription) { console.log('📊 populateDataFieldsTable 被调用,描述:', datasetDescription); const tableBody = document.getElementById('dataFieldsTableBody'); const highCoverageFilter = document.getElementById('filterHighCoverage')?.checked || false; const popularFilter = document.getElementById('filterPopular')?.checked || false; const matrixOnlyFilter = document.getElementById('filterMatrixOnly')?.checked || false; // 显示数据集描述(如果可用) if (datasetDescription) { console.log('🎯 显示传入的数据集描述'); displayDatasetDescription(datasetDescription); } else if (window.currentDatasetDescription) { console.log('🎯 显示已存储的数据集描述'); displayDatasetDescription(window.currentDatasetDescription); } else { console.log('⚠️ 无数据集描述可用'); } // 应用筛选器 filteredDataFields = dataFields.filter(field => { // 列特定筛选器 // ID筛选器 if (columnFilters.id && !field.id.toLowerCase().includes(columnFilters.id.toLowerCase())) { return false; } // 描述筛选器 if (columnFilters.description && !field.description.toLowerCase().includes(columnFilters.description.toLowerCase())) { return false; } // 类型筛选器 if (columnFilters.type && field.type !== columnFilters.type) { return false; } // 覆盖率范围筛选器 if (columnFilters.coverage.min !== null && field.coverage * 100 < columnFilters.coverage.min) { return false; } if (columnFilters.coverage.max !== null && field.coverage * 100 > columnFilters.coverage.max) { return false; } // 用户数量筛选器 if (columnFilters.userCount !== null && field.userCount < columnFilters.userCount) { return false; } // Alpha数量筛选器 if (columnFilters.alphaCount !== null && field.alphaCount < columnFilters.alphaCount) { return false; } // 高覆盖率筛选器 if (highCoverageFilter && field.coverage < 0.9) { return false; } // 流行度筛选器 if (popularFilter && field.userCount < 1000) { return false; } // 矩阵类型筛选器 if (matrixOnlyFilter && field.type !== 'MATRIX') { return false; } return true; }); // 对筛选后的数据字段排序 if (sortColumn) { filteredDataFields.sort((a, b) => { let aVal = a[sortColumn]; let bVal = b[sortColumn]; // 处理数值 if (sortColumn === 'coverage' || sortColumn === 'userCount' || sortColumn === 'alphaCount') { aVal = Number(aVal); bVal = Number(bVal); } else { // 字符串比较 aVal = String(aVal).toLowerCase(); bVal = String(bVal).toLowerCase(); } if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1; if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1; return 0; }); } // 清空表格 tableBody.innerHTML = ''; if (filteredDataFields.length === 0) { tableBody.innerHTML = '
正在通过Coze工作流处理 ${selectedFields.size} 个选中的字段
📡 正在连接到Coze API...
⚙️ 正在运行工作流分析...
📊 正在生成洞察...
工作流ID: ${workflowId}
选中的字段: ${Object.keys(selectedFieldsObject).join(', ')}
✅ API凭据已验证
🔄 正在向Coze服务器发送请求...
⏳ 请等待响应...
已成功通过工作流处理
📥 收到来自Coze的响应
🎉 正在格式化结果...
请等待我们分析您选中的字段...
`; } } // 以markdown格式显示结果 function displayResults(result) { const resultsSection = document.getElementById('resultsSection'); const resultsContent = document.getElementById('resultsContent'); // 显示结果部分 resultsSection.style.display = 'block'; // 将结果格式化为markdown - 简化版本 let markdown = '# 分析结果\n\n'; // 添加选中字段部分 markdown += '## 选中的字段\n\n'; Object.entries(result.selected_fields).forEach(([fieldId, description]) => { markdown += `- **${fieldId}**: ${description}\n`; }); markdown += '\n'; // 添加输出部分 - 仅显示实际分析输出 markdown += '## 分析输出\n\n'; if (result.output) { // 如果输出已经是格式化文本,直接使用 if (typeof result.output === 'string') { markdown += result.output; } else { // 如果是对象,尝试美观地显示 markdown += '```json\n'; markdown += JSON.stringify(result.output, null, 2); markdown += '\n```\n'; } } else { markdown += '_无输出数据可用_'; } // 将markdown渲染为HTML resultsContent.innerHTML = renderMarkdown(markdown); // 滚动到结果部分 resultsSection.scrollIntoView({ behavior: 'smooth' }); } // 辅助函数格式化markdown(可选增强功能) function renderMarkdown(markdown) { // 这是一个改进的markdown渲染器,能更好地处理列表 let html = markdown; // 首先,转义HTML以防止XSS html = html.replace(/&/g, '&') .replace(//g, '>'); // 代码块(必须在行内代码之前) html = html.replace(/```([\s\S]*?)```/g, function(match, code) { return '' + code.trim() + '';
});
// 标题
html = html.replace(/^#### (.*$)/gim, '$1');
// 更仔细地处理列表
// 为了更好的列表处理,分割成行
const lines = html.split('\n');
let inList = false;
let processedLines = [];
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
// 检查行是否是列表项
if (line.match(/^[\*\-\+] /)) {
// 用适当的HTML替换列表标记
line = line.replace(/^[\*\-\+] (.*)$/, ''); html = '
' + html + '
'; // 清理空段落 html = html.replace(/\s*<\/p>/g, ''); html = html.replace(/
( (|
|
)/g, '$1');
html = html.replace(/(<\/ul>|<\/ol>|<\/pre>)<\/p>/g, '$1');
// 段落内的单换行符
html = html.replace(/\n/g, '
');
return html;
}
// 将Coze配置保存到localStorage
function saveCozeConfig() {
const cozeApiToken = document.getElementById('cozeApiTokenInput').value;
const workflowId = document.getElementById('workflowIdInput').value;
// 保存到localStorage
localStorage.setItem('coze_api_token', cozeApiToken);
localStorage.setItem('coze_workflow_id', workflowId);
// 显示成功消息
const messageElement = document.getElementById('saveConfigMessage');
messageElement.style.display = 'inline';
// 3秒后隐藏消息
setTimeout(() => {
messageElement.style.display = 'none';
}, 3000);
}
// 从localStorage加载保存的Coze配置
function loadSavedCozeConfig() {
const savedToken = localStorage.getItem('coze_api_token');
const savedWorkflowId = localStorage.getItem('coze_workflow_id');
if (savedToken) {
document.getElementById('cozeApiTokenInput').value = savedToken;
}
if (savedWorkflowId) {
document.getElementById('workflowIdInput').value = savedWorkflowId;
}
}
// 清除Coze配置并重置为默认值
function clearCozeConfig() {
// 从localStorage移除
localStorage.removeItem('coze_api_token');
localStorage.removeItem('coze_workflow_id');
// 重置为默认值
document.getElementById('cozeApiTokenInput').value = 'pat_OCxUpnmL7hCvUxEWwcKL5XwUOdoiA3eWLzwY6L8W9sQVN1saJnoMrDNyhFhEn63l';
document.getElementById('workflowIdInput').value = '7522912027267956786';
// 显示消息
const messageElement = document.getElementById('saveConfigMessage');
messageElement.textContent = '配置已重置为默认值!';
messageElement.style.color = '#ff9800';
messageElement.style.display = 'inline';
// 3秒后隐藏消息
setTimeout(() => {
messageElement.style.display = 'none';
messageElement.textContent = '配置已保存!';
messageElement.style.color = '#4caf50';
}, 3000);
}
// 设置筛选事件监听器
function setupFilterEventListeners() {
// 快速筛选复选框
const highCoverageFilter = document.getElementById('filterHighCoverage');
const popularFilter = document.getElementById('filterPopular');
const matrixOnlyFilter = document.getElementById('filterMatrixOnly');
// 筛选操作按钮
const selectAllBtn = document.getElementById('selectAllFiltered');
const clearAllBtn = document.getElementById('clearAllSelected');
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
// 设置快速筛选监听器
if (highCoverageFilter) highCoverageFilter.onchange = () => populateDataFieldsTable();
if (popularFilter) popularFilter.onchange = () => populateDataFieldsTable();
if (matrixOnlyFilter) matrixOnlyFilter.onchange = () => populateDataFieldsTable();
// 设置操作按钮监听器
if (selectAllBtn) selectAllBtn.onclick = selectAllFilteredFields;
if (clearAllBtn) clearAllBtn.onclick = clearAllSelectedFields;
if (selectAllCheckbox) {
selectAllCheckbox.onclick = (e) => {
e.stopPropagation();
if (selectAllCheckbox.checked) {
selectAllFilteredFields();
} else {
clearAllFilteredFields();
}
};
}
// 列筛选监听器
document.querySelectorAll('.column-filter').forEach(filter => {
filter.addEventListener('input', (e) => {
const column = e.target.dataset.column;
const value = e.target.value;
if (column === 'userCount' || column === 'alphaCount') {
columnFilters[column] = value ? parseInt(value) : null;
} else {
columnFilters[column] = value;
}
// 添加/移除活动类
if (value) {
e.target.classList.add('active');
} else {
e.target.classList.remove('active');
}
populateDataFieldsTable();
});
});
// 覆盖率范围筛选器
document.querySelectorAll('.column-filter-min, .column-filter-max').forEach(filter => {
filter.addEventListener('input', (e) => {
const isMin = e.target.classList.contains('column-filter-min');
const value = e.target.value;
if (isMin) {
columnFilters.coverage.min = value ? parseFloat(value) : null;
} else {
columnFilters.coverage.max = value ? parseFloat(value) : null;
}
// 添加/移除活动类
const minInput = e.target.parentElement.querySelector('.column-filter-min');
const maxInput = e.target.parentElement.querySelector('.column-filter-max');
if (minInput.value || maxInput.value) {
minInput.classList.add('active');
maxInput.classList.add('active');
} else {
minInput.classList.remove('active');
maxInput.classList.remove('active');
}
populateDataFieldsTable();
});
});
// 排序按钮监听器
document.querySelectorAll('.sort-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const column = e.target.dataset.column;
// 重置所有其他排序按钮
document.querySelectorAll('.sort-btn').forEach(b => {
if (b !== e.target) {
b.classList.remove('asc', 'desc');
b.dataset.order = 'asc';
}
});
// 切换排序顺序
if (sortColumn === column) {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
} else {
sortColumn = column;
sortOrder = 'asc';
}
e.target.dataset.order = sortOrder;
e.target.classList.remove('asc', 'desc');
e.target.classList.add(sortOrder);
populateDataFieldsTable();
});
});
}
// 更新数据字段统计信息
function updateDataFieldsStats() {
const dataFieldsCountEl = document.getElementById('dataFieldsCount');
const filteredCountEl = document.getElementById('filteredCount');
const selectedCountEl = document.getElementById('selectedFieldsCount');
if (dataFieldsCountEl) dataFieldsCountEl.textContent = `已加载 ${dataFields.length} 个字段`;
if (filteredCountEl) filteredCountEl.textContent = `${filteredDataFields.length} 个已筛选`;
if (selectedCountEl) selectedCountEl.textContent = `${selectedFields.size} 个已选中`;
}
// 填充类型筛选下拉菜单
function populateTypeFilter() {
const typeFilter = document.getElementById('typeFilter');
if (!typeFilter) return;
// 从当前数据字段获取唯一类型
const uniqueTypes = [...new Set(dataFields.map(field => field.type))].sort();
// 清除现有选项,除了"所有类型"
typeFilter.innerHTML = '';
uniqueTypes.forEach(type => {
const option = document.createElement('option');
option.value = type;
option.textContent = type;
typeFilter.appendChild(option);
});
// 如果存在选中的值,恢复它
if (columnFilters.type && uniqueTypes.includes(columnFilters.type)) {
typeFilter.value = columnFilters.type;
}
}
// 选中所有筛选后的字段
function selectAllFilteredFields() {
filteredDataFields.forEach(field => {
selectedFields.set(field.id, field.description);
const row = document.querySelector(`tr[data-field-id="${field.id}"]`);
if (row) {
const checkbox = row.querySelector('.field-checkbox');
checkbox.checked = true;
row.classList.add('selected');
}
});
updateSelectedFieldsDisplay();
updateDataFieldsStats();
updateSelectAllCheckbox();
}
// 清除所有选中的字段
function clearAllSelectedFields() {
selectedFields.clear();
// 更新所有复选框
document.querySelectorAll('.field-checkbox').forEach(checkbox => {
checkbox.checked = false;
checkbox.closest('tr').classList.remove('selected');
});
updateSelectedFieldsDisplay();
updateDataFieldsStats();
updateSelectAllCheckbox();
}
// 仅清除筛选后的字段
function clearAllFilteredFields() {
filteredDataFields.forEach(field => {
selectedFields.delete(field.id);
const row = document.querySelector(`tr[data-field-id="${field.id}"]`);
if (row) {
const checkbox = row.querySelector('.field-checkbox');
checkbox.checked = false;
row.classList.remove('selected');
}
});
updateSelectedFieldsDisplay();
updateDataFieldsStats();
updateSelectAllCheckbox();
}
// 更新全选复选框状态
function updateSelectAllCheckbox() {
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
if (!selectAllCheckbox) return;
const allFilteredSelected = filteredDataFields.length > 0 &&
filteredDataFields.every(field => selectedFields.has(field.id));
selectAllCheckbox.checked = allFilteredSelected;
selectAllCheckbox.indeterminate = !allFilteredSelected &&
filteredDataFields.some(field => selectedFields.has(field.id));
}
// 显示数据集描述
function displayDatasetDescription(description) {
console.log('🎨 显示数据集描述:', description);
// 检查数据集描述元素是否已存在
let descriptionElement = document.getElementById('datasetDescription');
if (!descriptionElement) {
console.log('📌 创建新的数据集描述元素');
// 如果元素不存在则创建它
const tableContainer = document.getElementById('tableContainer');
const dataFieldsControls = tableContainer.querySelector('.data-fields-controls');
// 为数据集描述创建一个新的div
descriptionElement = document.createElement('div');
descriptionElement.id = 'datasetDescription';
descriptionElement.className = 'dataset-description';
descriptionElement.style.cssText = `
padding: 15px;
background: #e8f5e9;
border: 1px solid #4caf50;
border-radius: 4px;
margin-bottom: 15px;
font-size: 14px;
line-height: 1.5;
`;
// 将其插入到控件之前
tableContainer.insertBefore(descriptionElement, dataFieldsControls);
console.log('✅ 数据集描述元素已创建并插入');
} else {
console.log('📌 使用现有的数据集描述元素');
}
// 更新内容
descriptionElement.innerHTML = `
数据集描述:
${description}
`;
console.log('✅ 数据集描述内容已更新');
}