// ============================================================ // 状态管理 // ============================================================ const state = { docs: [], page: 1, size: 10, total: 0, currentInstanceId: null, history: [], historyPage: 1, historySize: 10, historyTotal: 0, pollInterval: null, currentTaskId: null, genInterfaces: [], }; // ============================================================ // DOM 引用 // ============================================================ const docList = document.getElementById('docList'); const listCount = document.getElementById('listCount'); const pagination = document.getElementById('pagination'); const pageInfo = document.getElementById('pageInfo'); const prevPage = document.getElementById('prevPage'); const nextPage = document.getElementById('nextPage'); // 添加文档 const addDocBtn = document.getElementById('addDocBtn'); const addDocModal = document.getElementById('addDocModal'); const addDocModalClose = document.getElementById('addDocModalClose'); const addCancelBtn = document.getElementById('addCancelBtn'); const addSubmitBtn = document.getElementById('addSubmitBtn'); const addForm = document.getElementById('addDocForm'); const addUploadZone = document.getElementById('addUploadZone'); const addFileInput = document.getElementById('addFileInput'); const addFileInfo = document.getElementById('addFileInfo'); const addFileName = document.getElementById('addFileName'); const addRemoveFile = document.getElementById('addRemoveFile'); // 修改文档 const editDocModal = document.getElementById('editDocModal'); const editDocModalClose = document.getElementById('editDocModalClose'); const editCancelBtn = document.getElementById('editCancelBtn'); const editSubmitBtn = document.getElementById('editSubmitBtn'); const editForm = document.getElementById('editDocForm'); const editInstanceId = document.getElementById('editInstanceId'); const editDesc = document.getElementById('editDesc'); const editRefinedDocRaw = document.getElementById('editRefinedDocRaw'); const editRefinedDocPreview = document.getElementById('editRefinedDocPreview'); const editInterfaceList = document.getElementById('editInterfaceList'); const toggleMarkdownView = document.getElementById('toggleMarkdownView'); // 生成代码 const generateModal = document.getElementById('generateModal'); const generateModalClose = document.getElementById('generateModalClose'); const genCancelBtn = document.getElementById('genCancelBtn'); const genSubmitBtn = document.getElementById('genSubmitBtn'); const genForm = document.getElementById('generateForm'); const genInstanceId = document.getElementById('genInstanceId'); const genInterfacesList = document.getElementById('genInterfacesList'); const genInstanceIdHidden = document.getElementById('genInstanceIdHidden'); const genCodeType = document.getElementById('genCodeType'); const genSdkName = document.getElementById('genSdkName'); const genGenerateType = document.getElementById('genGenerateType'); const genApiKey = document.getElementById('genApiKey'); const genBaseUrl = document.getElementById('genBaseUrl'); const genModel = document.getElementById('genModel'); const genMsgContent = document.getElementById('genMsgContent'); const genMesSendToken = document.getElementById('genMesSendToken'); // 历史任务 const historyModal = document.getElementById('historyModal'); const historyModalClose = document.getElementById('historyModalClose'); const historyInstanceId = document.getElementById('historyInstanceId'); const historyList = document.getElementById('historyList'); const historyPagination = document.getElementById('historyPagination'); const historyPageInfo = document.getElementById('historyPageInfo'); const historyPrevPage = document.getElementById('historyPrevPage'); const historyNextPage = document.getElementById('historyNextPage'); const refreshHistoryBtn = document.getElementById('refreshHistoryBtn'); // 任务详情 const taskDetailModal = document.getElementById('taskDetailModal'); const taskDetailModalClose = document.getElementById('taskDetailModalClose'); const taskDetailId = document.getElementById('taskDetailId'); const taskDetailContent = document.getElementById('taskDetailContent'); // 用量详情 const usageModal = document.getElementById('usageModal'); const usageModalClose = document.getElementById('usageModalClose'); const usageContent = document.getElementById('usageContent'); // 刷新 const refreshListBtn = document.getElementById('refreshListBtn'); // ============================================================ // 工具函数 // ============================================================ function formatTime(isoStr) { if (!isoStr) return '-'; const d = new Date(isoStr); return d.toLocaleString('zh-CN'); } function getGenerateTypeLabel(type) { const map = { 1: '客户端 SDK', 2: '服务端骨架', }; return map[type] || type || '-'; } function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function parseInterfaces(interfaceStr) { if (!interfaceStr) return []; try { return typeof interfaceStr === 'string' ? JSON.parse(interfaceStr) : interfaceStr; } catch { return []; } } // ============================================================ // 文档列表 // ============================================================ async function loadDocs(page = state.page) { try { const formData = new URLSearchParams(); formData.append('page', String(page)); formData.append('page_size', String(state.size)); const response = await fetch('/api/v1/refine/list', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString(), }); const data = await response.json(); if (data.code !== 200) { throw new Error(data.message || '加载失败'); } state.docs = data.data.data || []; state.total = data.data.total || 0; state.page = page; renderDocs(); renderPagination(); } catch (err) { console.error('加载列表失败:', err); docList.innerHTML = `
加载失败: ${err.message}
`; } } function renderDocs() { if (state.docs.length === 0) { docList.innerHTML = '
暂无文档实例,点击「添加文档实例」创建
'; listCount.textContent = '共 0 个'; return; } let html = ''; state.docs.forEach(doc => { const interfaces = parseInterfaces(doc.interface); const interfaceTags = interfaces.map(i => `${i.method || 'POST'} ${escapeHtml(i.summary || '')}` ).join(''); html += `
${escapeHtml(doc.desc || '未命名')}
🆔 ${doc.instance_id} 📅 ${formatTime(doc.created_at)} 📊 ${interfaces.length} 个接口 ${doc.status === 1 ? '✅ 有效' : '❌ 无效'}
${interfaceTags}
`; }); docList.innerHTML = html; listCount.textContent = `共 ${state.total} 个`; docList.querySelectorAll('.btn-edit').forEach(btn => { btn.addEventListener('click', () => openEditModal(btn.dataset.instanceId)); }); docList.querySelectorAll('.btn-generate').forEach(btn => { btn.addEventListener('click', () => openGenerateModal(btn.dataset.instanceId)); }); docList.querySelectorAll('.btn-history').forEach(btn => { btn.addEventListener('click', () => openHistoryModal(btn.dataset.instanceId)); }); } function renderPagination() { const totalPages = Math.ceil(state.total / state.size); if (totalPages <= 1) { pagination.style.display = 'none'; return; } pagination.style.display = 'flex'; pageInfo.textContent = `${state.page} / ${totalPages}`; prevPage.disabled = state.page <= 1; nextPage.disabled = state.page >= totalPages; } prevPage.addEventListener('click', () => { if (state.page > 1) loadDocs(state.page - 1); }); nextPage.addEventListener('click', () => { const totalPages = Math.ceil(state.total / state.size); if (state.page < totalPages) loadDocs(state.page + 1); }); refreshListBtn.addEventListener('click', () => loadDocs(state.page)); // ============================================================ // 添加文档实例 // ============================================================ let addFileData = null; addDocBtn.addEventListener('click', () => { addFileData = null; addFileInfo.style.display = 'none'; addUploadZone.style.display = 'block'; addFileInput.value = ''; addForm.reset(); addSubmitBtn.disabled = false; addSubmitBtn.querySelector('.btn-text').textContent = '📝 精炼并保存'; addSubmitBtn.querySelector('.btn-spinner').style.display = 'none'; addDocModal.style.display = 'flex'; }); addDocModalClose.addEventListener('click', () => { addDocModal.style.display = 'none'; }); addCancelBtn.addEventListener('click', () => { addDocModal.style.display = 'none'; }); addUploadZone.addEventListener('click', () => addFileInput.click()); addUploadZone.addEventListener('dragover', (e) => { e.preventDefault(); addUploadZone.classList.add('dragover'); }); addUploadZone.addEventListener('dragleave', () => { addUploadZone.classList.remove('dragover'); }); addUploadZone.addEventListener('drop', (e) => { e.preventDefault(); addUploadZone.classList.remove('dragover'); if (e.dataTransfer.files.length > 0) { handleAddFile(e.dataTransfer.files[0]); } }); addFileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleAddFile(e.target.files[0]); } }); addRemoveFile.addEventListener('click', () => { addFileData = null; addFileInfo.style.display = 'none'; addUploadZone.style.display = 'block'; addFileInput.value = ''; }); function handleAddFile(file) { const validTypes = ['.md', '.txt', '.doc', '.docx', '.pdf']; const ext = '.' + file.name.split('.').pop().toLowerCase(); if (!validTypes.includes(ext)) { alert('不支持的文件类型'); return; } if (file.size > 50 * 1024 * 1024) { alert('文件大小超过 50MB'); return; } addFileData = file; addFileName.textContent = file.name; addFileInfo.style.display = 'flex'; addUploadZone.style.display = 'none'; } addForm.addEventListener('submit', async (e) => { e.preventDefault(); if (!addFileData) { alert('请上传文档文件'); return; } const apiKey = document.getElementById('addApiKey').value.trim(); const baseUrl = document.getElementById('addBaseUrl').value.trim(); const model = document.getElementById('addModel').value.trim(); const desc = document.getElementById('addDesc').value.trim(); if (!apiKey) { alert('请输入 API Key'); return; } if (!baseUrl) { alert('请输入 Base URL'); return; } if (!model) { alert('请输入大模型名称'); return; } addSubmitBtn.disabled = true; addSubmitBtn.querySelector('.btn-text').textContent = '精炼中...'; addSubmitBtn.querySelector('.btn-spinner').style.display = 'inline-block'; const formData = new FormData(); formData.append('file', addFileData); formData.append('llm_api_key', apiKey); formData.append('llm_base_url', baseUrl); formData.append('llm_model', model); formData.append('desc', desc || addFileData.name); try { const response = await fetch('/api/v1/refine', { method: 'POST', body: formData, }); const data = await response.json(); if (data.code !== 200) { throw new Error(data.message || '精炼失败'); } alert('✅ 文档精炼成功!'); addDocModal.style.display = 'none'; loadDocs(state.page); } catch (err) { alert('❌ ' + err.message); } finally { addSubmitBtn.disabled = false; addSubmitBtn.querySelector('.btn-text').textContent = '📝 精炼并保存'; addSubmitBtn.querySelector('.btn-spinner').style.display = 'none'; } }); // ============================================================ // 修改文档(接口列表只读展示,Markdown 渲染) // ============================================================ let editInterfaces = []; let showRawMarkdown = false; async function openEditModal(instanceId) { try { const formData = new URLSearchParams(); formData.append('page', '1'); formData.append('page_size', '100'); formData.append('instance_id', instanceId); const response = await fetch('/api/v1/refine/list', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString(), }); const data = await response.json(); if (data.code !== 200 || !data.data.data || data.data.data.length === 0) { throw new Error('文档不存在'); } const doc = data.data.data[0]; state.currentInstanceId = instanceId; editInstanceId.textContent = 'ID: ' + instanceId; editDesc.value = doc.desc || ''; // 接口列表只读展示 editInterfaces = parseInterfaces(doc.interface); renderEditInterfaces(); // Markdown 渲染精炼文档 const refinedDoc = doc.refined_doc || ''; editRefinedDocPreview.innerHTML = renderMarkdown(refinedDoc); editRefinedDocRaw.value = refinedDoc; // 默认显示预览,隐藏源码 showRawMarkdown = false; editRefinedDocPreview.style.display = 'block'; editRefinedDocRaw.style.display = 'none'; if (toggleMarkdownView) { toggleMarkdownView.textContent = '✏️ 修改'; } editDocModal.style.display = 'flex'; } catch (err) { alert('加载文档失败: ' + err.message); } } function renderEditInterfaces() { const container = editInterfaceList; container.innerHTML = ''; if (!editInterfaces || editInterfaces.length === 0) { container.innerHTML = '暂无接口'; return; } editInterfaces.forEach(iface => { const tag = document.createElement('span'); tag.className = 'tag'; tag.innerHTML = ` ${escapeHtml(iface.method || 'POST')} ${escapeHtml(iface.summary || '')} `; container.appendChild(tag); }); } // Markdown 渲染(支持标题、代码块、表格、列表) function renderMarkdown(text) { if (!text) return '暂无内容'; let html = text; // 代码块 ``` ... ``` html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (match, lang, code) => { return `
${escapeHtml(code)}
`; }); // 标题 html = html.replace(/^### (.*$)/gm, '

$1

'); html = html.replace(/^## (.*$)/gm, '

$1

'); html = html.replace(/^# (.*$)/gm, '

$1

'); // 表格 html = html.replace(/\|(.+)\|\n\|[-:| ]+\|\n((?:\|.+\|\n?)*)/g, (match, header, rows) => { const headers = header.split('|').filter(h => h.trim()); const headerHtml = `${headers.map(h => `${h.trim()}`).join('')}`; const bodyRows = rows.split('\n').filter(r => r.trim()).map(row => { const cells = row.split('|').filter(c => c.trim()); return `${cells.map(c => `${c.trim()}`).join('')}`; }); return `${headerHtml}${bodyRows.join('')}
`; }); // 列表 html = html.replace(/^- (.*$)/gm, '
  • $1
  • '); html = html.replace(/((?:
  • .*<\/li>\n?)+)/g, ''); // 粗体 html = html.replace(/\*\*(.*?)\*\*/g, '$1'); // 换行 html = html.replace(/\n/g, '
    '); return html; } // 切换 Markdown 源码/预览(修改按钮) if (toggleMarkdownView) { toggleMarkdownView.addEventListener('click', function() { showRawMarkdown = !showRawMarkdown; if (showRawMarkdown) { editRefinedDocPreview.style.display = 'none'; editRefinedDocRaw.style.display = 'block'; this.textContent = '📄 预览'; } else { editRefinedDocPreview.style.display = 'block'; editRefinedDocRaw.style.display = 'none'; // 重新渲染预览 editRefinedDocPreview.innerHTML = renderMarkdown(editRefinedDocRaw.value); this.textContent = '✏️ 修改'; } }); } editDocModalClose.addEventListener('click', () => { editDocModal.style.display = 'none'; }); editCancelBtn.addEventListener('click', () => { editDocModal.style.display = 'none'; }); editForm.addEventListener('submit', async (e) => { e.preventDefault(); const refinedDoc = editRefinedDocRaw.value.trim(); const desc = editDesc.value.trim(); editSubmitBtn.disabled = true; editSubmitBtn.querySelector('.btn-text').textContent = '保存中...'; editSubmitBtn.querySelector('.btn-spinner').style.display = 'inline-block'; try { const formData = new URLSearchParams(); formData.append('instance_id', state.currentInstanceId); if (refinedDoc) formData.append('refined_doc', refinedDoc); if (desc) formData.append('desc', desc); const response = await fetch('/api/v1/refine/update', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString(), }); const data = await response.json(); if (data.code !== 200) { throw new Error(data.message || '保存失败'); } alert('✅ 保存成功!'); editDocModal.style.display = 'none'; loadDocs(state.page); } catch (err) { alert('❌ ' + err.message); } finally { editSubmitBtn.disabled = false; editSubmitBtn.querySelector('.btn-text').textContent = '💾 保存修改'; editSubmitBtn.querySelector('.btn-spinner').style.display = 'none'; } }); // ============================================================ // 生成代码(接口可勾选,默认全选) // ============================================================ async function openGenerateModal(instanceId) { state.currentInstanceId = instanceId; genInstanceId.textContent = 'ID: ' + instanceId; genInstanceIdHidden.value = instanceId; genCodeType.value = 'go'; genForm.reset(); genSubmitBtn.disabled = false; genSubmitBtn.querySelector('.btn-text').textContent = '🚀 生成代码'; genSubmitBtn.querySelector('.btn-spinner').style.display = 'none'; // 加载接口列表(可勾选) try { const formData = new URLSearchParams(); formData.append('page', '1'); formData.append('page_size', '100'); formData.append('instance_id', instanceId); const response = await fetch('/api/v1/refine/list', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString(), }); const data = await response.json(); const container = genInterfacesList; container.innerHTML = ''; if (data.code === 200 && data.data.data && data.data.data.length > 0) { const doc = data.data.data[0]; const interfaces = parseInterfaces(doc.interface); state.genInterfaces = interfaces; if (interfaces.length === 0) { container.innerHTML = '暂无接口'; } else { // 默认全选 interfaces.forEach((iface, index) => { const label = document.createElement('label'); label.className = 'checkbox-item checked'; label.innerHTML = ` ${escapeHtml(iface.method || 'POST')} ${escapeHtml(iface.summary || '')} `; const checkbox = label.querySelector('input[type="checkbox"]'); checkbox.addEventListener('change', function() { label.classList.toggle('checked', this.checked); }); container.appendChild(label); }); } } else { container.innerHTML = '加载接口列表失败'; } } catch (err) { console.error('加载接口列表失败:', err); genInterfacesList.innerHTML = '加载接口列表失败'; } generateModal.style.display = 'flex'; } generateModalClose.addEventListener('click', () => { generateModal.style.display = 'none'; }); genCancelBtn.addEventListener('click', () => { generateModal.style.display = 'none'; }); genForm.addEventListener('submit', async (e) => { e.preventDefault(); const instanceId = genInstanceIdHidden.value; const sdkName = genSdkName.value.trim(); const generateType = genGenerateType.value; const apiKey = genApiKey.value.trim(); const baseUrl = genBaseUrl.value.trim(); const model = genModel.value.trim(); const msgContent = genMsgContent.value.trim(); const mesSendToken = genMesSendToken.value.trim(); // 获取选中的接口 const checkboxes = document.querySelectorAll('#genInterfacesList input[type="checkbox"]:checked'); const selectedInterfaces = []; checkboxes.forEach(cb => { const idx = parseInt(cb.dataset.index); if (state.genInterfaces && state.genInterfaces[idx]) { selectedInterfaces.push(state.genInterfaces[idx]); } }); if (selectedInterfaces.length === 0) { alert('请至少选择一个接口'); return; } if (!instanceId) { alert('文档实例ID不能为空'); return; } if (!sdkName) { alert('请输入仓库名称'); return; } if (!apiKey) { alert('请输入 API Key'); return; } if (!baseUrl) { alert('请输入 Base URL'); return; } if (!model) { alert('请输入大模型名称'); return; } genSubmitBtn.disabled = true; genSubmitBtn.querySelector('.btn-text').textContent = '提交中...'; genSubmitBtn.querySelector('.btn-spinner').style.display = 'inline-block'; try { const formData = new URLSearchParams(); formData.append('instance_id', instanceId); formData.append('llm_api_key', apiKey); formData.append('llm_base_url', baseUrl); formData.append('llm_model', model); formData.append('desc', sdkName); formData.append('code_type', 'go'); formData.append('generate_type', generateType); formData.append('interfaces', JSON.stringify(selectedInterfaces)); if (msgContent) formData.append('msg_content', msgContent); if (mesSendToken) formData.append('mes_send_token', mesSendToken); const response = await fetch('/api/v1/generate', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString(), }); const data = await response.json(); if (data.code !== 200) { throw new Error(data.message || '提交失败'); } const taskId = data.data?.task_id || data.task_id; alert(`✅ 任务已提交!任务ID: ${taskId}`); generateModal.style.display = 'none'; loadDocs(state.page); } catch (err) { alert('❌ ' + err.message); } finally { genSubmitBtn.disabled = false; genSubmitBtn.querySelector('.btn-text').textContent = '🚀 生成代码'; genSubmitBtn.querySelector('.btn-spinner').style.display = 'none'; } }); // ============================================================ // 历史任务列表(美化版) // ============================================================ async function openHistoryModal(instanceId) { state.currentInstanceId = instanceId; historyInstanceId.textContent = 'ID: ' + instanceId; state.historyPage = 1; historyModal.style.display = 'flex'; await loadHistory(instanceId, 1); } historyModalClose.addEventListener('click', () => { historyModal.style.display = 'none'; }); refreshHistoryBtn.addEventListener('click', () => { if (state.currentInstanceId) { loadHistory(state.currentInstanceId, state.historyPage); } }); async function loadHistory(instanceId, page = 1) { try { const formData = new URLSearchParams(); formData.append('page', String(page)); formData.append('page_size', String(state.historySize)); formData.append('task_id', instanceId); const response = await fetch('/api/v1/tasks/list', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString(), }); const data = await response.json(); if (data.code !== 200) { throw new Error(data.message || '加载失败'); } state.history = data.data.data || []; state.historyTotal = data.data.total || 0; state.historyPage = page; renderHistory(); } catch (err) { historyList.innerHTML = `
    加载失败: ${err.message}
    `; } } function renderHistory() { if (state.history.length === 0) { historyList.innerHTML = '
    暂无历史任务
    '; historyPagination.style.display = 'none'; return; } let html = ''; state.history.forEach(task => { const statusClass = task.task_status === 'completed' ? 'completed' : task.task_status === 'failed' ? 'failed' : task.task_status === 'pending' ? 'pending' : 'processing'; const interfaces = parseInterfaces(task.interfaces); let usageSummary = ''; let usageDetail = null; try { const ua = typeof task.use_age === 'string' ? JSON.parse(task.use_age) : task.use_age; if (ua) { usageSummary = `总: ${ua.total_tokens || 0}`; usageDetail = ua; } } catch {} html += `
    ${task.task_id}
    📅 ${formatTime(task.created_at)} 📦 ${escapeHtml(task.desc || '-')} 🤖 ${escapeHtml(task.llm_model || '-')} 📊 ${interfaces.length} 个接口 📋 ${getGenerateTypeLabel(task.generate_type)} 💻 ${escapeHtml(task.code_type || 'go')} ${task.repo_url ? `🔗 仓库` : ''} ${usageSummary ? `📊 ${usageSummary}` : ''}
    ${task.task_status}
    `; }); historyList.innerHTML = html; historyList.querySelectorAll('.history-item').forEach(item => { item.addEventListener('click', () => { const taskId = item.dataset.taskId; openTaskDetail(taskId); }); }); const totalPages = Math.ceil(state.historyTotal / state.historySize); if (totalPages <= 1) { historyPagination.style.display = 'none'; } else { historyPagination.style.display = 'flex'; historyPageInfo.textContent = `${state.historyPage} / ${totalPages}`; historyPrevPage.disabled = state.historyPage <= 1; historyNextPage.disabled = state.historyPage >= totalPages; } } historyPrevPage.addEventListener('click', () => { if (state.historyPage > 1 && state.currentInstanceId) { loadHistory(state.currentInstanceId, state.historyPage - 1); } }); historyNextPage.addEventListener('click', () => { const totalPages = Math.ceil(state.historyTotal / state.historySize); if (state.historyPage < totalPages && state.currentInstanceId) { loadHistory(state.currentInstanceId, state.historyPage + 1); } }); // ============================================================ // 用量详情弹窗(美化版) // ============================================================ function showUsageDetail(el) { const item = el.closest('.history-item'); if (!item) return; const usageData = JSON.parse(item.dataset.usage || '{}'); const content = usageContent; if (!usageData || Object.keys(usageData).length === 0 || usageData.total_tokens === 0) { content.innerHTML = '
    暂无用量数据
    '; usageModal.style.display = 'flex'; return; } const promptTokens = usageData.prompt_tokens || 0; const completionTokens = usageData.completion_tokens || 0; const totalTokens = usageData.total_tokens || 0; const detail = usageData.detail || []; let detailHtml = ''; if (detail.length > 0) { detailHtml = `
    分步详情
    ${detail.map(d => `
    ${escapeHtml(d.state_name || '未知步骤')} ${d.total_tokens || 0} tokens
    `).join('')}
    `; } content.innerHTML = `
    ${totalTokens}
    总 Tokens
    ${promptTokens}
    输入 Tokens (Prompt)
    ${completionTokens}
    输出 Tokens (Completion)
    ${detailHtml} `; usageModal.style.display = 'flex'; } usageModalClose.addEventListener('click', () => { usageModal.style.display = 'none'; }); // 点击弹窗外关闭 usageModal.addEventListener('click', (e) => { if (e.target === usageModal) { usageModal.style.display = 'none'; } }); // ============================================================ // 任务详情 // ============================================================ async function openTaskDetail(taskId) { taskDetailId.textContent = 'ID: ' + taskId; taskDetailContent.innerHTML = '
    加载中...
    '; taskDetailModal.style.display = 'flex'; try { const response = await fetch(`/api/v1/tasks/${taskId}`); const data = await response.json(); if (data.code !== 200) { throw new Error(data.message || '加载失败'); } const task = data.data?.Task || data.data || {}; renderTaskDetail(task); } catch (err) { taskDetailContent.innerHTML = `
    加载失败: ${err.message}
    `; } } function renderTaskDetail(task) { const interfaces = parseInterfaces(task.interfaces); const interfaceTags = interfaces.map(i => `${i.method || 'POST'} ${escapeHtml(i.summary || '')}` ).join(''); let useAge = ''; try { const ua = typeof task.use_age === 'string' ? JSON.parse(task.use_age) : task.use_age; if (ua) { useAge = `
    总 Tokens: ${ua.total_tokens || 0} ${ua.detail ? ua.detail.map(d => `${d.state_name || '未知'}: ${d.total_tokens || 0}` ).join('') : ''}
    `; } } catch {} taskDetailContent.innerHTML = `
    任务ID ${task.task_id}
    状态 ${task.task_status}
    仓库名称 ${escapeHtml(task.desc || '-')}
    生成类型 ${getGenerateTypeLabel(task.generate_type)}
    代码类型 ${escapeHtml(task.code_type || 'go')}
    大模型 ${escapeHtml(task.llm_model || '-')}
    接口列表 ${interfaceTags || '-'}
    创建时间 ${formatTime(task.created_at)}
    完成时间 ${formatTime(task.completed_at)}
    代码仓库 ${task.repo_url ? `${task.repo_url}` : '-'}
    错误信息 ${escapeHtml(task.error || '-')}
    Token 用量 ${useAge || '-'}
    `; } taskDetailModalClose.addEventListener('click', () => { taskDetailModal.style.display = 'none'; }); // ============================================================ // 初始化 // ============================================================ loadDocs(1);