function renderJSON(obj) { if (obj === null) return `null`; if (typeof obj === 'number') return `${obj}`; if (typeof obj === 'boolean') return `${obj}`; if (typeof obj === 'string') { const escaped = obj.replace(//g, '>'); return `"${escaped}"`; } if (Array.isArray(obj)) { if (obj.length === 0) return '[]'; let html = '
[
'; obj.forEach((val, i) => { html += `
${renderJSON(val)}${i < obj.length - 1 ? ',' : ''}
`; }); html += '
]
'; return html; } if (typeof obj === 'object') { const keys = Object.keys(obj); if (keys.length === 0) return '{}'; let html = '
{
'; keys.forEach((k, i) => { html += `
"${k}": ${renderJSON(obj[k])}${i < keys.length - 1 ? ',' : ''}
`; }); html += '
}
'; return html; } return String(obj); } function renderDataOrRaw(dataObj, renderFunc) { if (!dataObj) return '

无可用数据

'; let safeRaw = ""; if (dataObj.error) { safeRaw = dataObj.raw_content ? dataObj.raw_content.replace(//g, ">") : "文件为空。"; return `

⚠️ JSON 解析失败

${safeRaw}
`; } else { safeRaw = JSON.stringify(dataObj, null, 2).replace(//g, ">"); } // Check global toggle state const isRaw = document.getElementById('global-json-toggle') && document.getElementById('global-json-toggle').checked; return `
${renderFunc(dataObj)}

📝 JSON 原文

${safeRaw}
`; } function renderRawCases(rawCasesObj) { if (!rawCasesObj || Object.keys(rawCasesObj).length === 0) return '无原始案例数据'; const reqId = requirements[currentSelectedIndex]?.id || "001"; // Build maps const sourceMap = {}; const sourceData = rawCasesObj['source']; const sourceList = sourceData ? (Array.isArray(sourceData) ? sourceData : sourceData.sources) : null; if (sourceList) { sourceList.forEach(s => { const sId = s.case_id || (s._raw && s._raw.case_id); const sUrl = s.source_url || s.url; if (sId) sourceMap[sId] = s; if (sUrl) sourceMap[sUrl] = s; }); } const detailMap = {}; const detailMapByUrl = {}; // Cache context for modal window._currentRawCasesContext = { rawCasesObj, sourceMap, detailMap, detailMapByUrl, reqId }; let totalStatsHtml = ''; const detailedCaseObj = rawCasesObj['case'] || rawCasesObj['case_detailed']; if (detailedCaseObj) { const cd = detailedCaseObj; let uniqueCases = new Set(); let calcWorkflow = 0; let calcCapabilities = 0; if (cd.cases) { cd.cases.forEach(c => { const cId = c.case_id || (c._raw && c._raw.case_id); const cUrl = c.source_url || c.url; const uniqueKey = cId || cUrl || Math.random().toString(); uniqueCases.add(uniqueKey); const workflowGroups = getWorkflowGroups(c); const capabilityItems = getCapabilityItems(c); if (workflowGroups.length > 0) calcWorkflow += workflowGroups.length; if (capabilityItems.length > 0) calcCapabilities += capabilityItems.length; if (cId) { if (workflowGroups.length > 0) detailMap[cId] = { ...detailMap[cId], workflow_groups: c.workflow_groups }; } if (cUrl) { if (workflowGroups.length > 0) detailMapByUrl[cUrl] = { ...detailMapByUrl[cUrl], workflow_groups: c.workflow_groups }; } }); } const displayTotal = uniqueCases.size > 0 ? uniqueCases.size : (cd.total !== undefined ? cd.total : 0); const displayWorkflowSuccess = calcWorkflow > 0 ? calcWorkflow : (cd.workflow_success !== undefined && cd.workflow_success !== null ? cd.workflow_success : (cd.success !== undefined && cd.success !== null ? cd.success : 0)); const displayCapabilitiesSuccess = calcCapabilities > 0 ? calcCapabilities : (cd.capabilities_success !== undefined && cd.capabilities_success !== null ? cd.capabilities_success : 0); if (cd.total !== undefined || uniqueCases.size > 0) { totalStatsHtml = `
${displayTotal} 未被过滤的帖子总数
${displayWorkflowSuccess} 工序提取成功数
${displayCapabilitiesSuccess} 能力提取成功数
`; } } let filteredStatsHtml = ''; if (rawCasesObj['filtered_cases']) { const fObj = rawCasesObj['filtered_cases']; let totalFiltered = 0; if (fObj.total !== undefined) totalFiltered = fObj.total; else if (fObj.cases) totalFiltered = fObj.cases.length; else if (fObj.sources) totalFiltered = fObj.sources.length; else if (fObj.by_reason) { Object.values(fObj.by_reason).forEach(r => { if (r.sources) totalFiltered += r.sources.length; else if (r.cases) totalFiltered += r.cases.length; }); } else if (Array.isArray(fObj)) totalFiltered = fObj.length; filteredStatsHtml = `
${totalFiltered} 被过滤的帖子数
`; } const allPlatforms = Object.keys(rawCasesObj).filter(p => p !== 'source' && p !== 'case_detailed' && p !== 'case' && p !== 'images'); const channelPlatforms = allPlatforms.filter(p => p !== 'filtered_cases' && p !== 'source_ex'); const hasFiltered = allPlatforms.includes('filtered_cases'); const hasExternal = allPlatforms.includes('source_ex'); let html = ''; html += `
`; html += ``; if (hasFiltered) html += ``; if (hasExternal) html += ``; html += ``; html += ``; html += `
`; const renderPaneContent = (pList, paneType) => { let paneHtml = ''; if (paneType === 'total' && typeof totalStatsHtml !== 'undefined' && totalStatsHtml) { paneHtml += totalStatsHtml; } else if (paneType === 'filtered_cases' && typeof filteredStatsHtml !== 'undefined' && filteredStatsHtml) { paneHtml += filteredStatsHtml; } let totalCases = 0; let seenIds = new Set(); let groupedHtml = {}; const getGroupKey = (c, p) => (p === 'filtered_cases' && c.filter_reason) ? `🚫 过滤原因: ${c.filter_reason}` : 'default'; let allCases = []; pList.forEach(p => { if (!rawCasesObj[p]) return; if (rawCasesObj[p].error) { const safeRaw = rawCasesObj[p].raw_content ? rawCasesObj[p].raw_content.replace(//g, ">") : "文件为空。"; paneHtml += `

⚠️ ${p} JSON 解析失败

${safeRaw}
`; return; } if (rawCasesObj[p].reason && p !== 'filtered_cases') { paneHtml += `
🛑 ${p} 提示: ${rawCasesObj[p].reason}
`; } let cases = []; if (Array.isArray(rawCasesObj[p])) { cases = rawCasesObj[p]; } else if (rawCasesObj[p].cases) { cases = rawCasesObj[p].cases; } else if (rawCasesObj[p].sources) { cases = rawCasesObj[p].sources; } else if (rawCasesObj[p].by_reason) { Object.entries(rawCasesObj[p].by_reason).forEach(([reasonKey, reasonObj]) => { if (reasonObj.sources && Array.isArray(reasonObj.sources)) { reasonObj.sources.forEach(src => { if (!src.filter_reason) src.filter_reason = reasonKey; cases.push(src); }); } }); } if (cases.length > 0) { if (!rawCasesObj['source'] && p !== 'source_ex' && p !== 'filtered_cases' && p !== 'source') { paneHtml += `

📝 ${p} 原始爬取数据 (未进行 1.5 数据源提取)

${renderJSON(rawCasesObj[p])}
`; return; } cases.forEach((c, idx) => { allCases.push({ c: c, p: p, originalIdx: idx }); }); } }); allCases.sort((aObj, bObj) => { const getScore = (item) => { const iId = item.case_id || (item._raw && item._raw.case_id) || (item.post && item.post.channel_content_id); const iUrl = item.source_url || item.url || (item.post && item.post.link); const mapped = sourceMap[iId] || sourceMap[iUrl] || (item._raw && sourceMap[item._raw.case_id]) || item; return mapped.evaluation && mapped.evaluation.quality ? (mapped.evaluation.quality.overall_score || 0) : 0; }; return getScore(bObj.c) - getScore(aObj.c); }); allCases.forEach(itemObj => { const c = itemObj.c; const p = itemObj.p; const idx = itemObj.originalIdx; totalCases++; const cId = c.case_id || (c._raw && c._raw.case_id) || (c.post && c.post.channel_content_id) || `temp_${p}_${idx}`; const cUrl = c.source_url || c.url || (c.post && c.post.link) || ''; const groupKey = getGroupKey(c, p); if (!groupedHtml[groupKey]) groupedHtml[groupKey] = ''; if (cId || cUrl || c.post) { const mappedS = sourceMap[cId] || sourceMap[cUrl] || (c._raw && sourceMap[c._raw.case_id]); if (p !== 'filtered_cases' && p !== 'source' && p !== 'source_ex' && !mappedS) return; if (cId && seenIds.has(cId)) return; if (cId) seenIds.add(cId); const s = mappedS || c; const post = s.post || s || {}; const images = post.images || []; const xImages = post.image_url_list || []; const ytThumbnails = post.thumbnails && post.thumbnails.length > 0 ? [post.thumbnails[post.thumbnails.length - 1].url] : []; const allImages = [...images, ...xImages.map(img => img.image_url), ...ytThumbnails].filter(Boolean); const coverImgUrl = allImages.length > 0 ? `/output/${reqId}/raw_cases/images/${cId}/00.jpg` : ''; const fallbackImgUrl = allImages.length > 0 ? allImages[0] : ''; const title = post.title || c.title || post.desc || (post.body_text ? post.body_text.substring(0, 30) + '...' : '') || cId || '无标题'; const author = post.channel_account_name || s.author || '-'; const likes = post.like_count !== undefined ? post.like_count : (post.likes !== undefined ? post.likes : '-'); const snippetStr = (post.body_text || post.body || '').substring(0, 100); const snippetHtml = snippetStr ? `
${snippetStr.replace(//g, ">")}...
` : ''; const platBadge = p.startsWith('case_') ? `${p.replace('case_', '').toUpperCase()}` : ''; const score = s.evaluation && s.evaluation.quality ? s.evaluation.quality.overall_score : null; let scoreBadge = ''; if (score !== null && score !== undefined) { let color = score >= 80 ? '#10b981' : (score >= 60 ? '#f59e0b' : '#ef4444'); scoreBadge = `
⭐️ ${score}
`; } let actionBtn = ''; if (p === 'source_ex') { const isImported = !!sourceMap[cId] || !!sourceMap[cUrl]; if (isImported) { actionBtn = ``; } else { actionBtn = ``; } } groupedHtml[groupKey] += `
${platBadge} ${scoreBadge} ${actionBtn} ${allImages.length > 0 ? `` : ''}
${title}
${allImages.length === 0 ? snippetHtml : ''}
👤 ${author}
❤️ ${likes}
`; } else { groupedHtml[groupKey] += `
📝 旧版格式 / 解析失败
点击查看详情
`; } }); if (totalCases === 0 && pList.length > 0 && !paneHtml.includes('解析失败') && !paneHtml.includes('未进行')) { paneHtml += `
暂无数据
`; } else { Object.entries(groupedHtml).forEach(([groupName, gHtml]) => { if (gHtml) { if (groupName !== 'default') { paneHtml += `

${groupName}

`; } paneHtml += `
${gHtml}
`; } }); } return paneHtml; }; html += `
${renderPaneContent([...channelPlatforms, 'source'], 'total')}
`; if (hasFiltered) html += ``; if (hasExternal) { html += ``; } html += `
`; return html; } window.importExternalCase = async function (e, caseId) { e.stopPropagation(); if (!confirm('确定要将该外部数据导入到 source 吗?')) return; try { const res = await fetch(`/api/requirements/${currentSelectedIndex}/import_source_ex`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ case_id: caseId }) }); const data = await res.json(); if (data.status === 'success') { fetchRequirementData(currentSelectedIndex); // Refresh data } else { alert('导入失败: ' + (data.detail || data.message || JSON.stringify(data))); } } catch (err) { alert('导入出错: ' + err); } }; window.importAllExternalCases = async function () { if (!confirm('确定要将所有外部数据全部导入到 source 吗?')) return; try { const res = await fetch(`/api/requirements/${currentSelectedIndex}/import_all_source_ex`, { method: 'POST' }); const data = await res.json(); if (data.status === 'success') { alert(`成功导入 ${data.count} 条数据!`); fetchRequirementData(currentSelectedIndex); // Refresh data } else { alert('全部导入失败: ' + (data.detail || data.message || JSON.stringify(data))); } } catch (err) { alert('导入出错: ' + err); } }; function selectSubTab(tabName) { document.querySelectorAll('#json-raw .sub-tab-btn').forEach(btn => { btn.classList.remove('active'); }); const activeBtn = document.querySelector(`#json-raw .sub-tab-btn[data-target="sub-tab-${tabName}"]`); if (activeBtn) activeBtn.classList.add('active'); document.querySelectorAll('#json-raw .sub-tab-pane').forEach(pane => { pane.classList.add('hidden'); }); document.getElementById(`sub-tab-${tabName}`).classList.remove('hidden'); } window.deleteClusterItems = async function (indices) { if (currentSelectedIndex === null) return; const data = window.dataCache[currentSelectedIndex]?.cluster; if (!data) return; let newData = Array.isArray(data) ? [...data] : { ...data }; let count = 0; // Sort indices descending to avoid shifting issues if it's an array indices.sort((a, b) => b - a); if (Array.isArray(newData)) { indices.forEach(i => { newData.splice(i, 1); count++; }); } else { const keys = Object.keys(newData); indices.forEach(i => { delete newData[keys[i]]; count++; }); } if (!confirm(`确定要删除选中的 ${count} 个项目吗?`)) return; try { const res = await fetch(`/api/requirements/${currentSelectedIndex}/save_cluster`, { method: 'POST', body: JSON.stringify(newData), headers: { 'Content-Type': 'application/json' } }); if (res.ok) { fetchRequirementData(currentSelectedIndex); } else { alert('删除失败'); } } catch (e) { console.error(e); alert('删除失败'); } }; window.deleteSingleCluster = function (idx) { deleteClusterItems([idx]); }; window.deleteSelectedClusters = function () { const checkboxes = document.querySelectorAll('.cluster-checkbox:checked'); const indices = Array.from(checkboxes).map(cb => parseInt(cb.dataset.idx)); if (indices.length === 0) { alert('请先选择要删除的项目'); return; } deleteClusterItems(indices); }; window.clearAllClusters = async function () { if (currentSelectedIndex === null) return; if (!confirm('确定要清空全部聚类结果吗?')) return; try { const res = await fetch(`/api/requirements/${currentSelectedIndex}/save_cluster`, { method: 'POST', body: JSON.stringify(Array.isArray(window.dataCache[currentSelectedIndex]?.cluster) ? [] : {}), headers: { 'Content-Type': 'application/json' } }); if (res.ok) { fetchRequirementData(currentSelectedIndex); } else { alert('清空失败'); } } catch (e) { console.error(e); alert('清空失败'); } }; function renderClusterDeletable(clusterData) { if (!clusterData || (Array.isArray(clusterData) && clusterData.length === 0) || (typeof clusterData === 'object' && Object.keys(clusterData).length === 0)) { return `
暂无聚类结果数据,请导入 JSON 文件
`; } let html = `
`; html += `
`; const items = Array.isArray(clusterData) ? clusterData : Object.entries(clusterData).map(([k, v]) => ({ key: k, value: v })); items.forEach((item, idx) => { const displayData = Array.isArray(clusterData) ? item : item.value; const displayKey = Array.isArray(clusterData) ? `导入记录 #${idx + 1}` : `Key: ${item.key}`; html += `
${renderJSON(displayData)}
`; }); html += `
`; return html; } window.selectGenericSubTab = function (prefix, targetId) { const parentContainer = document.getElementById(`container-${prefix}`); if (!parentContainer) return; parentContainer.querySelectorAll('.sub-tab-btn').forEach(btn => { btn.classList.remove('active'); }); const activeBtn = parentContainer.querySelector(`[data-target="${targetId}"]`); if (activeBtn) activeBtn.classList.add('active'); parentContainer.querySelectorAll('.sub-tab-pane').forEach(pane => { pane.classList.add('hidden'); }); const activePane = document.getElementById(targetId); if (activePane) activePane.classList.remove('hidden'); }; function renderWithSubTabs(dataMain, dataTemp, renderFn, tabPrefix) { if (!dataTemp || Object.keys(dataTemp).length === 0) { return renderDataOrRaw(dataMain, renderFn); } const mainHtml = renderDataOrRaw(dataMain, renderFn); const tempHtml = renderDataOrRaw(dataTemp, renderFn); return `
${mainHtml}
`; } function renderCaseTags(caseRefs) { if (!caseRefs || caseRefs.length === 0) return ''; let html = `
`; caseRefs.forEach(ref => { let caseId = null; let title = ref; const matchNew = ref.match(/^([a-z]+)_([^\s::]+)(?:[::\s]+(.*))?/); if (matchNew && matchNew[1] !== 'case') { caseId = `${matchNew[1]}_${matchNew[2]}`; title = matchNew[3] || ref; } else { const matchA = ref.match(/^case_([a-z]+)_([a-zA-Z0-9]+)(?:[::\s]+(.*))?/); if (matchA) { caseId = `${matchA[1]}-case_${matchA[2]}`; title = matchA[3] || ref; } else { const matchB = ref.match(/^([a-z]+)[\/\s](case_[a-zA-Z0-9]+)(?:[::\s]+(.*))?/); if (matchB) { caseId = `${matchB[1]}-${matchB[2]}`; title = matchB[3] || ref; } else { const matchC = ref.match(/^case_([a-zA-Z0-9]+)_([a-z]+)(?:[::\s]+(.*))?/); if (matchC) { caseId = `${matchC[2]}-case_${matchC[1]}`; title = matchC[3] || ref; } } } } if (caseId) { html += ` 🔍 ${caseId.replace('-', ' ')}
${title.substring(0, 40) + (title.length > 40 ? '...' : '')}
`; } else { html += `${ref}`; } }); html += `
`; return html; } function renderCapabilities(capsObj) { if (!capsObj || (!capsObj.extracted_capabilities && !capsObj.capabilities)) { return `
⚠️ 未知或非标准格式 (Capabilities)
${renderJSON(capsObj)}
`; } const caps = capsObj.capabilities || capsObj.extracted_capabilities; if (caps.length === 0) return '

未提取到能力。

'; let html = ``; caps.forEach(cap => { if (!cap.name && !cap.能力名称 && !cap.description && !cap.能力描述 && !cap.id) { html += `
⚠️ 非标准能力项
${renderJSON(cap)}
`; return; } const isNew = cap.is_new ? '✨ 新能力' : ''; html += `
⚡ [${cap.id || 'N/A'}] ${cap.name || cap.能力名称 || '未命名'}
${isNew}

${cap.description || cap.能力描述 || ''}

`; if (cap.enriched_details) { const ed = cap.enriched_details; if (ed.execution_process) { html += `
🚀 执行流程

${ed.execution_process}

`; } if (ed.core_parameters) { html += `
⚙️ 核心参数

${ed.core_parameters}

`; } if (ed.effects) { html += `
✨ 影响效果

${ed.effects}

`; } if (ed.visual_notes) { html += `
🖼️ 视觉备注

${ed.visual_notes}

`; } } else { html += `
✨ 影响 (Effects)
    `; if (cap.effects) cap.effects.forEach(eff => html += `
  • ${eff}
  • `); html += `
`; if (cap.implements && Object.keys(cap.implements).length > 0) { html += `
🛠️ 实现工具 (Tools)
`; for (const [tool, args] of Object.entries(cap.implements)) { html += `🔧 ${tool}`; } html += `
`; } } if (cap.case_references && cap.case_references.length > 0) { html += `
📌 来源案例
${renderCaseTags(cap.case_references)}
`; } html += `
`; }); return html; } function renderFragmentsGrid(fragments) { if (!fragments || fragments.length === 0) return '

没有片段数据。

'; window.allFragmentsMap = {}; // Reset map let html = `
`; fragments.forEach(f => { let cid = f.capability_id || ('temp_cap_' + Math.random().toString(36).substring(7)); window.allFragmentsMap[cid] = f; let tools = f.tools && f.tools.length > 0 ? f.tools.join(', ') : '无工具'; let actionDesc = f.action && f.action.description ? f.action.description : (f.capability_id || 'unknown'); let bodyText = f.body || f.body_excerpt || f.rationale || ''; if (bodyText.length > 180) bodyText = bodyText.substring(0, 180) + '...'; let inputs = f.inputs ? f.inputs.map(i => i.modality || '').filter(Boolean).join('+') : ''; let outputs = f.outputs ? f.outputs.map(o => o.modality || '').filter(Boolean).join('+') : ''; let ioStr = (inputs && outputs) ? `${inputs} -> ${outputs}` : (f.action && f.action.description ? f.action.description : '转换'); let applyHtml = ''; if (f.apply_to) { Object.values(f.apply_to).forEach(arr => { arr.forEach(item => { const parts = item.category_path ? item.category_path.split('/') : []; const label = parts.length > 0 ? parts[parts.length - 1] : '标签'; applyHtml += `${label}`; }); }); } const caseShort = f._caseId ? f._caseId.split('-').pop() : 'case'; html += `
${caseShort} ${f._workflowId || 'w'} ${ioStr}
${actionDesc} [${tools}]
${bodyText}
${applyHtml ? `
${applyHtml}
` : ''}
`; }); html += `
`; return html; } window.openFragDetail = function(fragId) { const f = window.allFragmentsMap[fragId]; if (!f) return; const modal = document.getElementById('frag-detail-modal'); const body = document.getElementById('frag-detail-modal-body'); let html = `
`; let inputs = f.inputs ? f.inputs.map(i => i.modality || '').filter(Boolean).join('+') : ''; let outputs = f.outputs ? f.outputs.map(o => o.modality || '').filter(Boolean).join('+') : ''; let ioStr = (inputs && outputs) ? `${inputs} → ${outputs}` : (f.action && f.action.description ? f.action.description : '转换'); html += `
${f._caseId ? f._caseId.split('-').pop() : 'case'} ${f._workflowId || 'w'} ${f.capability_id || 'unknown'}
`; html += `

I/O 模态

`; html += `
${ioStr}
`; if (f.inputs && f.inputs.length > 0) { html += `
IN ${f.inputs.map(i => `${i.description || '输入'} [${i.modality || '未知'}]`).join('')}
`; } if (f.outputs && f.outputs.length > 0) { html += `
OUT ${f.outputs.map(o => `${o.description || '输出'} [${o.modality || '未知'}]`).join('')}
`; } if (f.body || f.body_excerpt || f.rationale) { html += `

BODY

`; html += `
${f.body || f.body_excerpt || f.rationale}
`; } if (f.apply_to) { html += `

APPLY TO

`; Object.keys(f.apply_to).forEach(k => { f.apply_to[k].forEach(item => { html += `
${item.category_path || k}
${item.rationale || item.body_excerpt || '无推理说明'}
`; }); }); } if (f.suggest_apply_to && f.suggest_apply_to.length > 0) { html += `

SUGGEST APPLY TO (建议新增节点)

`; f.suggest_apply_to.forEach(item => { html += `
${item.path || '新节点'}
${item.rationale || item.body_excerpt || '无推理说明'}
`; }); } if (f.effects && f.effects.length > 0) { html += `

EFFECTS

`; f.effects.forEach((eff, i) => { html += `
#${i} ${eff.statement || 'Effect'}
判定标准: ${eff.criteria || '无'}
判定方式: ${eff.judge_method || '未知'}
`; if (eff.negative_examples && eff.negative_examples.length > 0) { html += `
反例:
    ${eff.negative_examples.map(ex => `
  • ${ex}
  • `).join('')}
`; } html += `
`; }); } html += `

其他

`; let tools = f.tools && f.tools.length > 0 ? f.tools.join(', ') : '无'; html += `
tools: ${tools}
`; if (f.artifact_type) html += `
artifact_type: ${f.artifact_type}
`; if (f.control_target) html += `
control_target: ${Array.isArray(f.control_target) ? f.control_target.join(', ') : f.control_target}
`; html += `
`; body.innerHTML = html; modal.classList.remove('hidden'); }; function renderBlueprint(bpObj) { if (!bpObj || (!bpObj.blueprints && !bpObj.clusters)) { return `
⚠️ 未知或非标准格式 (Blueprint)
${renderJSON(bpObj)}
`; } let html = ``; // New process.json format if (bpObj.clusters) { html += `

🎯 需求

${bpObj.requirement || ''}

`; bpObj.clusters.forEach(c => { if (!c.cluster_id && !c.cluster_name && !c.工序步骤) { html += `
⚠️ 非标准聚类项
${renderJSON(c)}
`; return; } const score = c.score || 0; const deg = score * 360; const scoreClass = score >= 0.8 ? '' : (score >= 0.5 ? 'medium' : 'low'); html += `
🧩 [${c.cluster_id || 'N/A'}] ${c.cluster_name || '未命名'}
${Math.round(score * 100)}%
匹配度得分
🧠 解释 (Explanation)

${c.explanation || ''}

📍 工序步骤
`; if (c.工序步骤) c.工序步骤.forEach(step => { html += `
步骤 ${step.步骤序号}
${step.步骤描述 || ''}
`; }); html += `
`; if (c.关联案例 && c.关联案例.length > 0) { html += `
📌 关联案例
${renderCaseTags(c.关联案例)}
`; } html += `
`; }); return html; } // Old blueprint format if (bpObj.distilled_cases && bpObj.distilled_cases.length > 0) { html += `

📚 蓝图来源案例

`; bpObj.distilled_cases.forEach(c => { let targetId = c.id; const matchA = targetId.match(/^case_([a-z]+)_(\d+)/); if (matchA) { targetId = `${matchA[1]}-case_${matchA[2]}`; } else { const matchB = targetId.match(/^([a-z]+)[\/\s](case_\d+)/); if (matchB) { targetId = `${matchB[1]}-${matchB[2]}`; } else { const matchC = targetId.match(/^case_(\d+)_([a-z]+)/); if (matchC) targetId = `${matchC[2]}-case_${matchC[1]}`; } } html += ` 🔍 ${c.id}
${c.title ? c.title.substring(0, 40) + (c.title.length > 40 ? '...' : '') : 'View Source'}
`; }); html += `
`; } if (bpObj.blueprints) bpObj.blueprints.forEach(bp => { if (!bp.name && !bp.phases) { html += `
⚠️ 非标准蓝图项
${renderJSON(bp)}
`; return; } html += `
🗺️ ${bp.name || '未命名'}
🧠 推理逻辑 (Reasoning)

${bp.reasoning || ''}

📍 阶段 (Phases)
`; if (bp.phases) bp.phases.forEach(ph => { html += `
${ph.phase || ''}
${ph.description || ''}
`; }); html += `
`; }); return html; } function renderStrategy(stratObj) { if (!stratObj || (!stratObj.strategies && !stratObj.workflow)) { return `
⚠️ 未知或非标准格式 (Strategy)
${renderJSON(stratObj)}
`; } let html = `

🎯 需求描述

${stratObj.requirement || ''}

`; // New Workflow Format if (stratObj.workflow) { stratObj.workflow.forEach(strat => { if (!strat.cluster_id && !strat.cluster_name && !strat.steps) { html += `
⚠️ 非标准工作流项
${renderJSON(strat)}
`; return; } const score = strat.score || 0; const deg = score * 360; const scoreClass = score >= 0.8 ? '' : (score >= 0.5 ? 'medium' : 'low'); let scoreHtml = `
${Math.round(score * 100)}%
匹配得分
${strat.explanation || ''}
`; html += `
🎯 [${strat.cluster_id}] ${strat.cluster_name || '未命名'}
${scoreHtml} ${(strat.关联案例 && strat.关联案例.length > 0) ? `
📌 关联案例
${renderCaseTags(strat.关联案例)}
` : ''}
🧱 工作流步骤
`; if (strat.steps) strat.steps.forEach(step => { html += `
步骤 ${step.步骤序号}
${step.步骤描述}
`; if (step.capabilities) { step.capabilities.forEach(cap => { html += `
⚡ [${cap.id}] ${cap.name}
${cap.description}
${(cap.case_references && cap.case_references.length > 0) ? `
${renderCaseTags(cap.case_references)}
` : ''}
`; }); } html += `
`; }); html += `
`; }); return html; } if (stratObj.strategies) { stratObj.strategies.sort((a, b) => (b.is_selected === true) - (a.is_selected === true)); stratObj.strategies.forEach(strat => { if (!strat.name && !strat.workflow_outline) { html += `
⚠️ 非标准策略项
${renderJSON(strat)}
`; return; } const isSelected = strat.is_selected; const icon = isSelected ? '🎯' : '🥈'; const badge = isSelected ? '⭐ 被选中的策略' : '备选策略'; let scoreHtml = ''; if (strat.coverage_score !== undefined) { const score = strat.coverage_score; const deg = score * 360; const scoreClass = score >= 0.8 ? '' : (score >= 0.5 ? 'medium' : 'low'); scoreHtml = `
${Math.round(score * 100)}%
覆盖率得分
${strat.coverage_explanation || ''}
`; } html += `
${icon} ${strat.name || '未命名'}
${badge}
${scoreHtml}
📥 来源: ${strat.source || 'N/A'}
`; if (strat.reasoning) html += `
🧠 推理逻辑 (Reasoning)

${strat.reasoning}

`; if (strat.why_not) html += `
❌ 为何未被选中

${strat.why_not}

`; if (strat.workflow_outline && strat.workflow_outline.length > 0) { html += `
🧱 工作流大纲
`; strat.workflow_outline.forEach(wo => { html += `
${wo.phase}
${wo.description}
`; if (wo.capabilities) { wo.capabilities.forEach(cap => html += `⚡ ${cap.name}`); } html += `
`; }); html += `
`; } html += `
`; }); } if (stratObj.uncovered_requirements && stratObj.uncovered_requirements.length > 0) { html += `
⚠️ Uncovered Requirements
    `; stratObj.uncovered_requirements.forEach(req => html += `
  • ${req}
  • `); html += `
`; } return html; }