合同体检欠款催收问法律找律师定价法律知识账户
首页/AI 助手
AI Copilot · 得理 × 元典 × DeepSeek

三大模型驱动的
智能法律工作台

小犀 AI
小犀 AI得理 × 元典 × DeepSeek 三大模型 · 在线
得理 deli-lite-v2
⚖️ 小犀智能法律顾问
您好,我是小犀 · 您的专属法律参谋
得理 × 元典 × DeepSeek 三引擎协同,每个结论都有法条与判例溯源。直接描述您的问题,或点击下方快捷入口。
💬AI 法律咨询
📋合同审查
✍️文书生成
🏢企业风险
💼 公司欠薪 💔 离婚财产 🏠 租房纠纷 🚗 交通事故 ✍️ 借条模板 🏢 企业风险查询
🔒 对话加密 · 结论附法条溯源 · 1.81 亿裁判文书 / 607 万法规实时检索

⚡ 得理 × 元典 × DeepSeek 三大模型协同 · 每个结论都有法条溯源 · 1.81 亿裁判文书 / 607 万法规实时检索

⚖️ AI 输出仅供研究参考,不构成正式法律意见 · 正式意见请委托平台认证律师
要素式填写 诉讼类
引导式合同生成
🧮 诉讼费计算器 按《诉讼费用交纳办法》
📊 赔偿计算器
按受诉法院所在地标准,可修改
粘贴合同文本 三大模型审查
🔒

合同文本将发送至 AI 服务商分析。身份证号 / 银行卡号 / 手机号 / 邮箱等敏感信息将自动脱敏,报告引用一律打码,请放心粘贴。

📎
导入合同文件 Word / PDF / 图片,≤10MB · 或直接粘贴下方
选择文件
🗂️ 批量审查 最多 10 份

支持 txt / docx / pdf / 图片,≤10MB

⚖️ 规则引擎毫秒级识别常见风险条款,DeepSeek 深度分析补充遗漏并给出修订建议。立场配置影响审查视角与建议倾向。

📁 文件分析
📁
上传文件或粘贴文本,小犀先思考再分析

说清您的身份立场与目的(如"我是劳动者,公司以严重违纪解除,我想主张违法解除赔偿金"),分析会更精准。分析完成后可在同一输入框继续追问,无需重新上传。

🔍 举证风险 ⚖️ 有利/不利 📄 下一步取证
支持 txt / docx / pdf / 图片,≤10MB
AI 律师转接

三步描述案件,AI 推荐 Top3 对口律师

限时免费 · 咨询限 12 轮
Step 1 / 3 · 选择案件类型 33%
免费咨询限 12 轮 · 无任何强制委托义务
256-bit 加密传输 · 前端脱敏 · 数据不出域
1.81 亿+ 裁判文书 · 607 万+ 法律法规

支持语义检索 · 案例含案号/法院/裁判要旨 · 法规含时效状态

🔍 支持 企业名称 / 人员名称 / 商标名称 检索,查询涉诉记录、失信被执行、行政处罚、经营异常等风险(数据源:元典法律大数据)

'; } function printHtml(html) { const w = window.open('', '_blank'); if (!w) { LSQ.toast('请允许弹窗后重试'); return; } w.document.write(html); w.document.close(); w.focus(); setTimeout(() => { try { w.print(); } catch (e) {} }, 700); } // ═══ 案例全文查看 + PDF 导出 ═══ async function openCaseFull(r) { const modal = document.getElementById('caseFullModal'); const titleEl = document.getElementById('cfTitle'); const metaEl = document.getElementById('cfMeta'); const bodyEl = document.getElementById('cfBody'); titleEl.textContent = r.title || '案例全文'; metaEl.innerHTML = '' + esc([r.court, r.caseNumber, r.judgementDate].filter(Boolean).join(' · ')) + '' + (r.cause ? ' · ' + esc(r.cause) : ''); bodyEl.textContent = '正在加载全文…'; modal.classList.add('open'); document.body.style.overflow = 'hidden'; let fullText = r.content || ''; if (r.id) { try { const json = await api('/ai/cases/' + r.id); if (json.code === 0 && json.data) { const d = json.data; fullText = d.caseDetailContent || d.content || fullText; metaEl.innerHTML = '' + esc([d.court, d.caseNumber, d.judgementDate].filter(Boolean).join(' · ')) + '' + (d.cause ? ' · ' + esc(d.cause) : '') + (d.levelOfTria ? ' · ' + esc(d.levelOfTria) : ''); } } catch (e) { /* 保留列表摘要 */ } } bodyEl.textContent = fullText || '(未获取到全文内容)'; window._cfFullText = fullText; window._cfTitle = titleEl.textContent; } document.getElementById('cfPdf').addEventListener('click', () => { const t = window._cfFullText || document.getElementById('cfBody').textContent; const title = window._cfTitle || '案例全文'; const meta = document.getElementById('cfMeta').textContent; if (window.Export) { window.Export.open({ title: title, templateName: '案例全文', contentType: 'html', content: { html: '

案例信息:' + esc(meta) + '

' + esc(t) + '
' }, footerNote: '本案例全文来源于公开裁判文书,仅供参考,不构成正式法律意见。', version: 1, }); } else { printHtml(buildPrintHtml(title, '
' + esc(t) + '
', '案例信息:' + esc(meta) + '
法犀 · 生成时间:' + new Date().toLocaleString('zh-CN'))); } }); document.getElementById('searchBtn').addEventListener('click', doSearch); document.getElementById('searchInput').addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); }); /* ===== 类案检索报告生成 ===== */ let curReportId = null; const reportBtn = document.getElementById('reportBtn'); reportBtn.addEventListener('click', async () => { const q = document.getElementById('searchInput').value.trim(); if (!q) { LSQ.toast('请先输入检索词'); return; } if (!getToken()) { LSQ.toast('请先登录后生成报告'); return; } const box = document.getElementById('searchResults'); box.innerHTML = '
📊 AI 正在生成' + (mode === 'case' ? '类案' : '法规') + '研究报告(约需 15-40 秒)…
'; try { const json = await api('/ai/report/generate', { query: q, mode: mode }); if (json.code !== 0) throw new Error(json.message || '生成失败'); curReportId = json.data.id; const rep = json.data.report || {}; let html = '
' + '
' + '
📊 ' + esc(rep.title || '法律检索报告') + '' + 'AI 生成AI 报告
'; if (rep.meta && rep.meta.length) { html += '
' + rep.meta.map(m => '' + esc(m.k) + ':' + esc(m.v) + '').join('') + '
'; } (rep.sections || []).forEach((sec, i) => { html += '
' + (i + 1) + '. ' + esc(sec.heading || '') + ''; (sec.paragraphs || []).forEach(p => { html += '

' + esc(p) + '

'; }); if (sec.bullets && sec.bullets.length) { html += '
    ' + sec.bullets.map(b => '
  • ' + esc(b) + '
  • ').join('') + '
'; } html += '
'; }); html += '
' + '' + '
'; box.innerHTML = html; // 报告导出:统一格式选择(PDF/Word,品牌模板) document.getElementById('reportExport').addEventListener('click', () => { if (!curReportId) { LSQ.toast('请先生成检索报告'); return; } const rpTitle = document.querySelector('#reportResult h3') ? document.querySelector('#reportResult h3').textContent.trim() : '法律检索报告'; if (window.Export) { window.Export.pick({ title: rpTitle, templateName: '法律检索报告' }, (fmt) => { exportReportFile(fmt); }); } else { exportReportFile('pdf'); } }); async function exportReportFile(fmt) { try { const headers = { 'Content-Type': 'application/json' }; headers['X-Guest-Id'] = getGuestId(); const t = getToken(); if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/report/' + curReportId + '/export?format=' + fmt, { headers }); if (!res.ok) throw new Error('导出失败'); const blob = await res.blob(); const url = URL.createObjectURL(blob); const cd = res.headers.get('Content-Disposition') || ''; const fnMatch = cd.match(/filename\*=UTF-8''([^;]+)/); const fileName = fnMatch ? decodeURIComponent(fnMatch[1]) : ('法律检索报告_' + Date.now() + '.' + (fmt === 'pdf' ? 'pdf' : 'docx')); const a = document.createElement('a'); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 1000); LSQ.toast('已导出 ' + (fmt === 'pdf' ? 'PDF' : 'Word')); } catch (err) { LSQ.toast('⚠️ ' + err.message); } } } catch (err) { box.innerHTML = '
⚠️ ' + (err.message || '报告生成失败') + '
'; } }); /* ===== 合同审查 ===== */ const rvBtn = document.getElementById('rvBtn'); const rvContent = document.getElementById('rvContent'); const rvExportWrap = document.getElementById('rvExportWrap'); let rvReviewId = null; // 导出批注版:统一格式选择(PDF/Word) document.getElementById('rvExportAnnotated').addEventListener('click', () => { if (!rvReviewId) { LSQ.toast('请先完成合同审查'); return; } if (window.Export) { window.Export.pick({ title: '合同审查批注版', templateName: '合同审查报告' }, (fmt) => { exportAnnotatedFile(fmt); }); } else { exportAnnotatedFile('pdf'); } }); async function exportAnnotatedFile(fmt) { try { const headers = { 'Content-Type': 'application/json' }; headers['X-Guest-Id'] = getGuestId(); const t = getToken(); if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/contract/reviews/' + rvReviewId + '/annotated?format=' + fmt, { headers }); if (!res.ok) throw new Error('导出失败'); const blob = await res.blob(); const url = URL.createObjectURL(blob); const cd = res.headers.get('Content-Disposition') || ''; const fnMatch = cd.match(/filename\*=UTF-8''([^;]+)/); const fileName = fnMatch ? decodeURIComponent(fnMatch[1]) : ('合同审查批注版_' + Date.now() + '.' + (fmt === 'pdf' ? 'pdf' : 'docx')); const a = document.createElement('a'); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 1000); LSQ.toast('已导出 ' + (fmt === 'pdf' ? 'PDF' : 'Word')); } catch (err) { LSQ.toast('⚠️ ' + err.message); } } let rvStance = 'neutral'; let rvLeverage = 'medium'; // 立场切换 document.querySelectorAll('#rvStanceTabs .tab').forEach(btn => { btn.addEventListener('click', function () { rvStance = this.dataset.stance; document.querySelectorAll('#rvStanceTabs .tab').forEach(b => b.classList.remove('on')); this.classList.add('on'); }); }); // 谈判地位切换 document.querySelectorAll('#rvLeverageTabs .tab').forEach(btn => { btn.addEventListener('click', function () { rvLeverage = this.dataset.lev; document.querySelectorAll('#rvLeverageTabs .tab').forEach(b => b.classList.remove('on')); this.classList.add('on'); }); }); // 导入 Word/PDF 合同 const rvFile = document.getElementById('rvFile'); const rvImportMain = document.getElementById('rvImportMain'); const rvFileName = document.getElementById('rvFileName'); const rvBatchFiles = document.getElementById('rvBatchFiles'); const rvBatchBtn = document.getElementById('rvBatchBtn'); const rvBatchList = document.getElementById('rvBatchList'); if (rvImportMain) rvImportMain.addEventListener('click', () => rvFile.click()); if (rvBatchBtn) rvBatchBtn.addEventListener('click', () => rvBatchFiles.click()); rvBatchFiles.addEventListener('change', async function () { const files = Array.from(this.files || []); if (!files.length) return; if (files.length > 10) { LSQ.toast('单次最多 10 份'); this.value = ''; return; } rvBatchList.style.display = 'block'; rvBatchList.innerHTML = '⏳ 上传解析中…'; try { const fd = new FormData(); files.forEach(f => fd.append('files', f)); const t = getToken(); const headers = {}; headers['X-Guest-Id'] = getGuestId(); if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/upload/batch', { method: 'POST', headers: headers, body: fd }); const json = await res.json().catch(() => ({ code: -1, message: '响应解析失败' })); if (json.code !== 0) throw new Error(json.message || '批量上传失败'); const okItems = json.data.results.filter(r => r.ok); rvBatchList.innerHTML = '✅ 解析成功 ' + okItems.length + ' 份,开始批量审查(约 15-40 秒/份)…'; // 调批量审查接口 const items = okItems.map(r => ({ name: r.name, content: r.text })); const body = { items }; const st = document.querySelector('#rvStanceTabs .tab.on'); const lv = document.querySelector('#rvLeverageTabs .tab.on'); if (st) body.stance = st.dataset.st; if (lv) body.leverage = lv.dataset.lev; const j2 = await api('/ai/contract/review/batch', body); if (j2.code !== 0) throw new Error(j2.message || '批量审查失败'); let html = '📋 批量审查结果(' + j2.data.okCount + '/' + j2.data.results.length + ' 成功)'; rvBatchList.innerHTML = html; LSQ.toast('批量审查完成'); } catch (err) { rvBatchList.innerHTML = '⚠️ ' + esc(err.message || '批量审查失败'); } finally { this.value = ''; } }); rvFile.addEventListener('change', async function () { const f = this.files[0]; if (!f) return; rvFileName.textContent = '上传中…'; try { const fd = new FormData(); fd.append('file', f); const t = getToken(); const headers = {}; if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/upload', { method: 'POST', headers: headers, body: fd }); const json = await res.json().catch(() => ({ code: -1, message: '响应解析失败' })); if (json.code !== 0) throw new Error(json.message || '上传失败'); rvContent.value = json.data.text; rvFileName.textContent = '✅ ' + f.name + '(' + json.data.length + ' 字,已填入)'; rvShowParties(json.data.text); LSQ.toast('文件已解析,可直接审查'); } catch (err) { rvFileName.textContent = '⚠️ ' + (err.message || '上传失败'); } finally { this.value = ''; } }); const rvStatus = document.getElementById('rvStatus'); const rvScoreBar = document.getElementById('rvScoreBar'); const rvScoreTxt = document.getElementById('rvScoreTxt'); const rvScoreFill = document.getElementById('rvScoreFill'); const rvLevelBadge = document.getElementById('rvLevelBadge'); // ── 报告 Tab 切换 ── const rvReportTabs = document.getElementById('rvReportTabs'); const rvReportBody = document.getElementById('rvReportBody'); const rvOverviewContent = document.getElementById('rvOverviewContent'); const rvIssuesContent = document.getElementById('rvIssuesContent'); const rvHighlightsContent = document.getElementById('rvHighlightsContent'); const rvCompanyContent = document.getElementById('rvCompanyContent'); const rvSuggestionsContent = document.getElementById('rvSuggestionsContent'); const rvProgressPanel = document.getElementById('rvProgressPanel'); const rvProgressList = document.getElementById('rvProgressList'); const rvIssueCount = document.getElementById('rvIssueCount'); const rvCompanyCount = document.getElementById('rvCompanyCount'); rvReportTabs.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', function () { rvReportTabs.querySelectorAll('button').forEach(b => b.classList.remove('on')); this.classList.add('on'); rvReportBody.querySelectorAll('.rv-pane').forEach(p => p.classList.remove('on')); const pane = rvReportBody.querySelector('.rv-pane[data-pane="' + this.dataset.rvtab + '"]'); if (pane) pane.classList.add('on'); }); }); // ── 分类进度渲染(parse / rules / company / ai)+ 进度条 ── const RV_STAGES = [ { key: 'parse', label: '文本解析', icon: '📄' }, { key: 'rules', label: '规则引擎扫描', icon: '⚡' }, { key: 'company', label: '企业主体核验', icon: '🏢' }, { key: 'ai', label: 'AI 深度分析', icon: '🧠' } ]; function rvResetProgress() { rvProgressList.innerHTML = RV_STAGES.map(s => '
' + '
' + s.icon + ' ' + s.label + '' + '等待
' + '
' + '
' + '
' ).join(''); rvProgressPanel.style.display = 'block'; } function rvSetStage(key, status, msg) { const row = rvProgressList.querySelector('[data-stage="' + key + '"]'); if (!row) return; const state = row.querySelector('.rv-stage-state'); const bar = row.querySelector('.rv-stage-bar'); if (status === 'running') { state.textContent = msg || '进行中…'; state.style.color = 'var(--accent)'; bar.style.width = '45%'; bar.style.background = 'var(--accent)'; } else if (status === 'done') { state.textContent = '✅ ' + (msg || '完成'); state.style.color = 'var(--success)'; bar.style.width = '100%'; bar.style.background = 'var(--success)'; } else if (status === 'error') { state.textContent = '⚠️ ' + (msg || '失败'); state.style.color = 'var(--danger)'; bar.style.width = '100%'; bar.style.background = 'var(--danger)'; } } // ── 甲乙方识别(前端正则,导入/输入即触发)── function rvDetectParties(text) { const out = { a: '', b: '' }; if (!text) return out; const detect = (side) => { const s = side === 'a' ? '甲' : '乙'; // 1) 名称/简称形式:甲方名称:XX / 甲方(简称"XX"):XX let m = text.match(new RegExp(s + '\\s*方(?:名称)?(?:[((][^))]*[))])?[::]{1,2}\\s*([^\\s,。;;\\n]{2,40})')); if (!m) { // 2) 普通形式:甲方:XX m = text.match(new RegExp(s + '\\s*方[::]\\s*([^\\s,。;;\\n]{2,40})')); } if (!m) return ''; let v = m[1].replace(/[((].*?[))]/g, '').trim(); return v.length >= 2 ? v : ''; }; out.a = detect('a'); out.b = detect('b'); return out; } function rvShowParties(text) { const p = rvDetectParties(text); const card = document.getElementById('rvPartiesCard'); const elA = document.getElementById('rvPartyA'); const elB = document.getElementById('rvPartyB'); const hint = document.getElementById('rvPartiesHint'); if (!card) return; elA.textContent = p.a || '未识别'; elB.textContent = p.b || '未识别'; const hasAny = p.a || p.b; card.style.display = hasAny ? 'block' : 'none'; if (hint) hint.style.display = hasAny ? 'none' : 'block'; } // 输入/粘贴即时识别 let rvDetectTimer = null; rvContent.addEventListener('input', () => { clearTimeout(rvDetectTimer); rvDetectTimer = setTimeout(() => rvShowParties(rvContent.value), 400); }); rvBtn.addEventListener('click', async () => { const text = rvContent.value.trim(); if (text.length < 50) { LSQ.toast('请粘贴完整合同文本(不少于 50 字)'); return; } if (!getToken()) { LSQ.toast('请先登录后再审查合同'); return; } rvStatus.textContent = '审查中…'; rvStatus.className = 'badge gold'; rvScoreBar.style.display = 'none'; rvReportTabs.style.display = 'none'; rvReportTabs.querySelectorAll('button').forEach(b => b.style.display = ''); rvResetProgress(); const sp = document.getElementById('rvSummaryPanel'); if (sp) sp.style.display = 'none'; rvShowParties(text); rvOverviewContent.textContent = 'AI 正在分析合同条款与法律风险,约需 15-40 秒…'; rvIssuesContent.innerHTML = ''; rvHighlightsContent.innerHTML = ''; rvCompanyContent.innerHTML = ''; rvSuggestionsContent.innerHTML = ''; try { // SSE 流式审查(分类进度) const headers = { 'Content-Type': 'application/json' }; headers['X-Guest-Id'] = getGuestId(); const t = getToken(); if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/contract/review/stream', { method: 'POST', headers: headers, body: JSON.stringify({ content: text, stance: rvStance, leverage: rvLeverage }) }); if (!res.ok) { const err = await res.json().catch(() => ({})); if (res.status === 429 || err.code === 429) { rvStatus.textContent = '今日免费额度已用完'; rvStatus.className = 'badge red'; showUpgrade(err.data || {}); LSQ.toast('免费额度已用完,可购买 AI 合同快审继续'); if (window.Pay) { Pay.open('ai_contract', { onPaid: function () { LSQ.toast('购买成功,权益已到账,请重新点击审查'); } }); } } else { throw new Error(err.message || '审查失败'); } rvProgressPanel.style.display = 'none'; return; } const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = ''; let result = null; while (true) { const { done, value } = await reader.read(); if (done) break; buf += decoder.decode(value, { stream: true }); const lines = buf.split('\n'); buf = lines.pop(); for (const line of lines) { const s = line.trim(); if (!s.startsWith('data:')) continue; const data = s.slice(5).trim(); if (data === '[DONE]') continue; let j; try { j = JSON.parse(data); } catch (e) { continue; } if (j.type === 'stage') rvSetStage(j.stage, j.status, j.msg); else if (j.type === 'error') throw new Error(j.msg || '审查失败'); else if (j.type === 'done') result = j.data; } } if (!result) throw new Error('审查未返回结果'); const dt = result; rvReviewId = dt.id || null; if (rvReviewId) rvExportWrap.style.display = 'flex'; // 评分条 rvScoreBar.style.display = 'block'; rvScoreTxt.textContent = '风险评分 ' + dt.riskScore + ' / 100'; rvScoreFill.style.width = dt.riskScore + '%'; rvLevelBadge.textContent = dt.riskLevel + '风险'; rvLevelBadge.className = 'badge ' + (dt.riskLevel === '高' ? 'red' : dt.riskLevel === '中' ? 'gold' : 'green'); rvStatus.textContent = '审查完成'; rvStatus.className = 'badge green'; setAiLabel('rvAiLabel', true); // 显式标识(D-14) const ai = dt.ai || {}; const issues = (ai.issues || []).concat(dt.rules.filter(r => !(ai.issues || []).some(x => (x.risk || '').indexOf(r.name) >= 0))); // 结果汇总面板 const rvSummaryGrid = document.getElementById('rvSummaryGrid'); if (rvSummaryGrid) { const sensCount = (dt.sensitive || {}).count || 0; const compCount = (dt.companies || []).length; rvSummaryGrid.innerHTML = '
风险评分
' + dt.riskScore + '
' + dt.riskLevel + '风险
' + '
风险点
' + issues.length + '
' + '
脱敏
' + sensCount + '
' + '
企业核验
' + compCount + '
'; document.getElementById('rvSummaryPanel').style.display = 'block'; } // ── 概览 Tab ── let ov = ''; const sens = dt.sensitive || {}; if (sens.count > 0) { ov += '
🔒 已自动脱敏 ' + sens.count + ' 处敏感信息(' + (sens.types || []).join('、') + '),报告引用均已打码
'; } if (ai.summary) ov += '

📋 ' + esc(ai.summary) + '

'; ov += '
' + '
风险点
' + issues.length + '
' + '
规则命中
' + (dt.rules || []).length + '
' + '
有利条款
' + (ai.highlights || []).length + '
' + '
企业核验
' + (dt.companies || []).length + '
'; if (!ai.summary && !issues.length) ov += '

未发现明显风险条款。

'; rvOverviewContent.innerHTML = ov; // ── 风险点 Tab(默认 Top5,可展开全部)── if (issues.length) { rvIssueCount.textContent = issues.length; const renderIssues = (list) => list.map(it => { const lv = it.level || '中'; const badge = lv === '高' ? 'red' : lv === '中' ? 'gold' : 'green'; return '
' + '
' + lv + '' + esc(it.clause || it.match || '') + '
' + '

' + esc(it.risk || it.desc || '') + '

' + (it.suggestion ? '

💡 ' + esc(it.suggestion) + '

' : '') + '
'; }).join(''); let ih = '⚠️ 共发现 ' + issues.length + ' 处风险点'; ih += renderIssues(issues.slice(0, 5)); if (issues.length > 5) { ih += '' + ''; } rvIssuesContent.innerHTML = ih; const moreBtn = document.getElementById('rvMoreIssues'); if (moreBtn) moreBtn.addEventListener('click', function () { const wrap = document.getElementById('rvMoreIssuesWrap'); const open = wrap.style.display !== 'none'; wrap.style.display = open ? 'none' : 'block'; this.textContent = open ? '展开全部 ' + issues.length + ' 处风险点 ▾' : '收起 ▴'; }); } else { rvIssuesContent.innerHTML = '

未发现明显风险条款。

'; } // ── 有利条款 Tab ── if ((ai.highlights || []).length) { rvHighlightsContent.innerHTML = '✅ 有利条款 ' + ai.highlights.length + ' 条'; } else { rvHighlightsContent.innerHTML = '

未识别到明显有利条款。

'; } // ── 企业主体 Tab ── const comps = dt.companies || []; const LV = { red: ['高危', 'var(--danger)'], yellow: ['关注', 'var(--warn,#D4A843)'], green: ['正常', 'var(--success)'], unknown: ['未知', 'var(--text-3)'] }; if (comps.length) { rvCompanyCount.textContent = comps.length; rvCompanyContent.innerHTML = '🏢 签约主体核验(公开数据)' + comps.map(c => { const lv = LV[c.level] || LV.unknown; return '
' + '
' + esc(c.name) + '' + lv[0] + '
' + (c.reasons && c.reasons.length ? '

' + c.reasons.map(r => '• ' + esc(r)).join('
') + '

' : '') + (c.baseInfo && (c.baseInfo.legalRep || c.baseInfo.capital || c.baseInfo.status) ? '

' + [c.baseInfo.legalRep ? '法人:' + esc(c.baseInfo.legalRep) : '', c.baseInfo.capital ? '注册资本:' + esc(c.baseInfo.capital) : '', c.baseInfo.status ? '状态:' + esc(c.baseInfo.status) : ''].filter(Boolean).join(' · ') + '

' : '') + '
'; }).join('') + '

数据来源:企业信用公开数据(元典),仅供参考,不构成法律意见。

'; } else { rvCompanyContent.innerHTML = '

未从合同识别到签约企业主体(需含「甲方:XX公司」格式),或数据源未配置。个人签约主体暂不核验。

'; const companyTab = rvReportTabs.querySelector('[data-rvtab="company"]'); if (companyTab) companyTab.style.display = 'none'; } // ── 修订建议 Tab ── const suggs = issues.filter(it => it.suggestion); if (suggs.length) { rvSuggestionsContent.innerHTML = '💡 修订建议 ' + suggs.length + ' 条(可直接替换)' + suggs.map((it, i) => '
' + '
' + (i + 1) + '. ' + esc(it.clause || it.name || '') + '
' + '

' + esc(it.suggestion) + '

' ).join(''); } else { rvSuggestionsContent.innerHTML = '

暂无修订建议。

'; } rvReportTabs.style.display = 'flex'; } catch (err) { rvStatus.textContent = '审查失败'; rvStatus.className = 'badge red'; rvOverviewContent.textContent = '⚠️ ' + (err.message || '审查失败,请稍后重试'); } }); /* ===== 文件分析(四引擎 · 对话式流式) ===== */ let anMode = 'case'; let anFileText = ''; // 附件解析正文(仅后台使用,不回填输入框) let anSessionId = null; // 分析会话 ID(对应 ai_sessions.id,可导出/多轮) let anStreaming = false; const anModeTabs = document.getElementById('anModeTabs'); const anChatBody = document.getElementById('anChatBody'); const anInput = document.getElementById('anInput'); const anFile = document.getElementById('anFile'); const anWelcome = document.getElementById('anWelcome'); const anAttachWrap = document.getElementById('anAttachWrap'); const AN_NAMES = { case: '案件分析', evidence: '证据分析', contract: '合同解读', law: '法律解读' }; // 模式切换 anModeTabs.querySelectorAll('.tab').forEach(btn => { btn.addEventListener('click', function () { anMode = this.dataset.an; anModeTabs.querySelectorAll('.tab').forEach(b => b.classList.remove('on')); this.classList.add('on'); }); }); // 输入框自适应高度 function anAutoGrow() { if (!anInput) return; anInput.style.height = 'auto'; anInput.style.height = Math.min(anInput.scrollHeight, 160) + 'px'; } if (anInput) anInput.addEventListener('input', anAutoGrow); // ── 附件:仅暂存后台,展示为可移除芯片,不占用输入框 ── document.getElementById('anFileBtn').addEventListener('click', () => anFile.click()); anFile.addEventListener('change', async function () { const f = this.files[0]; if (!f) return; document.getElementById('anFileName').textContent = '解析中…'; try { const fd = new FormData(); fd.append('file', f); const headers = { 'X-Guest-Id': getGuestId() }; const t = getToken(); if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/upload', { method: 'POST', headers, body: fd }); const json = await res.json().catch(() => ({ code: -1, message: '响应解析失败' })); if (json.code !== 0) throw new Error(json.message || '上传失败'); anFileText = json.data.text || ''; document.getElementById('anFileName').textContent = '已解析:' + f.name; renderAttach(f.name, json.data.length); } catch (err) { document.getElementById('anFileName').textContent = '⚠️ ' + (err.message || '上传失败'); } finally { this.value = ''; } }); function renderAttach(name, len) { anAttachWrap.style.display = 'flex'; anAttachWrap.innerHTML = '📄' + '' + esc(name) + '' + '' + len + ' 字' + ''; const clr = document.getElementById('anFileClear'); if (clr) clr.addEventListener('click', function () { anFileText = ''; anAttachWrap.style.display = 'none'; anAttachWrap.innerHTML = ''; document.getElementById('anFileName').textContent = '支持 txt / docx / pdf / 图片,≤10MB'; LSQ.toast('已移除附件'); }); } // 欢迎态快捷问题 if (anWelcome) { anWelcome.querySelectorAll('.an-chip').forEach(function (c) { c.addEventListener('click', function () { anInput.value = c.dataset.q || ''; anAutoGrow(); anSend(); }); }); } // 追加消息气泡 function anPush(role) { const wrap = document.createElement('div'); wrap.className = 'an-msg ' + role; wrap.innerHTML = '
'; anChatBody.appendChild(wrap); anChatBody.scrollTop = anChatBody.scrollHeight; return wrap.querySelector('.an-bubble'); } // ── 发送(流式:先思考后正文,同框展示,可多轮追问)── async function anSend() { if (anStreaming) return; const typed = (anInput.value || '').trim(); const isFirst = !anSessionId; const material = anFileText || (isFirst ? typed : ''); if (isFirst && !material) { LSQ.toast('请先上传文件或粘贴至少 20 字材料'); return; } if (isFirst && !anFileText && typed && typed.length < 20) { LSQ.toast('材料过短,请补充至 20 字以上'); return; } if (!typed && !material) { LSQ.toast('请输入您的问题或分析需求'); return; } if (!getToken()) { LSQ.toast('请先登录后再使用文件分析'); return; } const requirement = typed || '请对以上材料进行全面分析,指出关键法律问题、风险与应对建议。'; anStreaming = true; if (anWelcome) anWelcome.style.display = 'none'; // 用户气泡 const ub = anPush('user'); let uTxt = typed; if (isFirst && anFileText) { const nmEl = anAttachWrap.querySelector('.an-attach__nm'); uTxt = (typed ? typed + '\n' : '') + '📄 附件:' + (nmEl ? nmEl.textContent : '已上传附件'); } ub.textContent = uTxt || '(分析以上材料)'; anInput.value = ''; anAutoGrow(); // 助手气泡:思考段 + 正文段 const ab = anPush('bot'); ab.innerHTML = '' + '
'; const thinkEl = ab.querySelector('.an-think'); const thinkBody = ab.querySelector('.an-think-body'); const textEl = ab.querySelector('.an-text'); const cursor = ab.querySelector('.an-cursor'); let thinkShown = false, bodyStarted = false, full = ''; try { const headers = { 'Content-Type': 'application/json' }; headers['X-Guest-Id'] = getGuestId(); const t = getToken(); if (t) headers['Authorization'] = 'Bearer ' + t; const res = await fetch('/api/ai/analyze/chat', { method: 'POST', headers: headers, body: JSON.stringify({ type: anMode, material: material, requirement: requirement, session_id: anSessionId }), }); if (res.status === 429) { const err = await res.json().catch(() => ({})); ab.closest('.an-msg').remove(); if (err.data) showUpgrade(err.data); LSQ.toast(err.message || '今日额度已用完,可购买 AI 风险评估继续'); return; } if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.message || '请求失败'); } const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buf += decoder.decode(value, { stream: true }); const lines = buf.split('\n'); buf = lines.pop(); for (const line of lines) { const str = line.trim(); if (!str.startsWith('data:')) continue; const data = str.slice(5).trim(); if (data === '[DONE]') continue; let j; try { j = JSON.parse(data); } catch (e) { continue; } if (j.session_id) { anSessionId = j.session_id; continue; } if (j.reasoning) { thinkShown = true; thinkEl.style.display = 'block'; thinkBody.textContent += j.reasoning; anChatBody.scrollTop = anChatBody.scrollHeight; } if (j.content) { if (!bodyStarted) { bodyStarted = true; if (thinkShown) thinkEl.classList.add('done'); // 正文开始 → 思考段淡化 } textEl.textContent += j.content; full += j.content; anChatBody.scrollTop = anChatBody.scrollHeight; } if (j.error) throw new Error(j.error); } } if (cursor) cursor.remove(); if (!full) { textEl.innerHTML = '(未获取到内容,请重试)'; } else { markAiContent(ab); // 合规标识(D-14) setAiLabel('anAiLabel', true); document.getElementById('anExport').style.display = 'inline-flex'; // 首轮案件分析:附时间轴 + 全景图 if (anMode === 'case' && !ab.dataset.enriched) { ab.dataset.enriched = '1'; const box = document.createElement('div'); box.className = 'an-enrich'; box.innerHTML = '
'; ab.appendChild(box); const src = anFileText || typed; extractTimeline(src, box.querySelector('.an-timeline')); extractPanorama(src, box.querySelector('.an-panorama')); } } } catch (err) { if (cursor) cursor.remove(); textEl.innerHTML = '⚠️ ' + esc(err.message || '分析失败,请稍后重试') + ''; } finally { anStreaming = false; } } document.getElementById('anBtn').addEventListener('click', anSend); if (anInput) anInput.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); anSend(); } }); /* 分析报告导出(会话级 · 统一品牌模板) */ async function downloadAnalysis(format) { if (!anSessionId) { LSQ.toast('请先完成一次分析'); return; } try { const res = await fetch('/api/ai/sessions/' + anSessionId + '/export?format=' + format, { headers: (() => { const h = { 'X-Guest-Id': getGuestId() }; const t = getToken(); if (t) h['Authorization'] = 'Bearer ' + t; return h; })(), }); if (!res.ok) throw new Error('导出失败'); const blob = await res.blob(); const url = URL.createObjectURL(blob); const cd = res.headers.get('Content-Disposition') || ''; const fnMatch = cd.match(/filename\*=UTF-8''([^;]+)/); const fileName = fnMatch ? decodeURIComponent(fnMatch[1]) : ('文件分析_' + Date.now() + '.' + (format === 'pdf' ? 'pdf' : 'docx')); const a = document.createElement('a'); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 1000); LSQ.toast('已导出 ' + (format === 'pdf' ? 'PDF' : 'Word')); } catch (err) { LSQ.toast('⚠️ ' + err.message); } } document.getElementById('anExport').addEventListener('click', function () { if (!anSessionId) { LSQ.toast('请先完成一次分析'); return; } const typeName = AN_NAMES[anMode] || '文件分析'; if (window.Export) { window.Export.pick({ title: typeName, templateName: '文件分析报告' }, function (fmt) { downloadAnalysis(fmt); }); } else { downloadAnalysis('pdf'); } }); /* 时间轴提取(渲染到指定容器) */ async function extractTimeline(text, box) { if (!box) return; box.innerHTML = '

⏳ 正在提取案件时间轴…

'; try { const json = await api('/ai/analyze/timeline', { text: String(text || '').slice(0, 6000) }); if (json.code !== 0) throw new Error(json.message || '提取失败'); const d = json.data; let html = '🕐 案件时间轴'; html += '
'; for (const n of d.nodes || []) { const typeIcon = { contract: '📄', payment: '💰', breach: '⚠️', legal: '⚖️', other: '📌' }[n.type] || '📌'; html += '
'; html += ''; html += '
' + esc(n.date || '') + ' ' + typeIcon + '
'; html += '
' + esc(n.event || '') + ''; if (n.detail) html += '
' + esc(n.detail) + '
'; html += '
'; } html += '
'; box.innerHTML = html; } catch (err) { box.innerHTML = '

⚠️ 时间轴提取失败:' + esc(err.message) + '

'; } } /* ═══ 案件全景图(实体-关系可视化)═══ */ async function extractPanorama(text, box) { if (!box) return; box.innerHTML = '

⏳ 正在提取案件全景…

'; try { const json = await api('/ai/analyze/panorama', { text: String(text || '').slice(0, 6000) }); if (json.code !== 0) throw new Error(json.message || '提取失败'); const d = json.data; let html = '🗺️ 案件全景'; if (d.summary) html += '

' + esc(d.summary) + '

'; const typeIcons = { party: '👤', lawyer: '⚖️', court: '🏛️', company: '🏢', property: '🏠', document: '📄', other: '📌' }; html += '
'; for (const e of d.entities || []) { html += '' + (typeIcons[e.type] || '📌') + ' ' + esc(e.name) + (e.desc ? ' · ' + esc(e.desc) + '' : '') + ''; } html += '
'; if (d.relations && d.relations.length) { const nameOf = {}; (d.entities || []).forEach(function (e) { nameOf[e.id] = e.name; }); html += '
'; for (const r of d.relations) { const from = nameOf[r.from] || r.from, to = nameOf[r.to] || r.to; html += '
' + '' + esc(from) + ' —' + esc(r.label || '') + '→ ' + esc(to) + '
'; } html += '
'; } if (d.claims && d.claims.length) { html += '
🎯 争议焦点
'; } box.innerHTML = html; } catch (err) { box.innerHTML = '

⚠️ 全景图提取失败:' + esc(err.message) + '

'; } } /* ===== 转真人律师(转接认证律师) ===== */ // XSS 转义工具 function esc(s) { return String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); } const ldDesc = document.getElementById('ldDesc'); const ldCity = document.getElementById('ldCity'); const ldPhone = document.getElementById('ldPhone'); const ldTypeChips = document.getElementById('ldTypeChips'); const ldStepLabel = document.getElementById('ldStepLabel'); const ldStepPct = document.getElementById('ldStepPct'); const ldStepBar = document.getElementById('ldStepBar'); const ldNext = document.getElementById('ldNext'); const ldPrev = document.getElementById('ldPrev'); const ldResult = document.getElementById('ldResult'); const ldCards = document.getElementById('ldCards'); let currentLeadId = null; let ldStep = 1; let ldTypeVal = ''; const LD_STEPS = [ { label: '选择案件类型', pct: '33%' }, { label: '描述案情', pct: '66%' }, { label: '联系方式', pct: '100%' } ]; // 分步导航 function showLdStep(n) { ldStep = n; document.querySelectorAll('.ld-step').forEach(function(el){ el.style.display = (parseInt(el.getAttribute('data-step'), 10) === n) ? '' : 'none'; }); var info = LD_STEPS[n - 1]; ldStepLabel.textContent = 'Step ' + n + ' / 3 · ' + info.label; ldStepPct.textContent = info.pct; ldStepBar.style.width = info.pct; ldPrev.style.display = (n === 1) ? 'none' : ''; var isLast = (n === 3); ldNext.style.display = isLast ? 'none' : ''; ldBtn.style.display = isLast ? '' : 'none'; if (isLast) ldBtn.disabled = false; } // chips 选择 ldTypeChips.addEventListener('click', function(e){ var chip = e.target.closest('.chip'); if (!chip) return; ldTypeChips.querySelectorAll('.chip').forEach(function(c){ c.classList.remove('on'); }); chip.classList.add('on'); ldTypeVal = chip.getAttribute('data-type') || ''; }); // 上一步 / 下一步 ldPrev.addEventListener('click', function(){ if (ldStep > 1) showLdStep(ldStep - 1); }); ldNext.addEventListener('click', function(){ if (ldStep === 1) { if (!ldTypeVal) { LSQ.toast('请先选择案件类型'); return; } showLdStep(2); } else if (ldStep === 2) { if (ldDesc.value.trim().length < 10) { LSQ.toast('请描述您的案件情况(不少于10字)'); return; } showLdStep(3); } }); // 渲染律师卡片 function renderLawyerCards(matches) { if (!matches || !matches.length) { ldCards.innerHTML = '
暂未匹配到合适的律师,请补充更多案件信息后重试。
'; return; } const rankBadges = ['🥇', '🥈', '🥉']; ldCards.innerHTML = matches.map((m, i) => { const scoreColor = m.score >= 80 ? 'var(--success)' : m.score >= 50 ? 'var(--accent-deep)' : 'var(--text-3)'; const bd = m.breakdown || {}; // 星级换算:匹配度 → 星级(>=90 五星 / >=75 四星 / >=60 三星 / >=40 二星 / 其余一星) const stars = m.score >= 90 ? 5 : m.score >= 75 ? 4 : m.score >= 60 ? 3 : m.score >= 40 ? 2 : 1; let starStr = ''; for (let si = 1; si <= 5; si++) starStr += ''; // 匹配因子明细 const bdHtml = '
' + '领域 ' + (bd.areas || 0) + '' + '城市 ' + (bd.city || 0) + '' + '经验 ' + (bd.exp || 0) + '' + '
'; return '
' + '
' + '
' + '
' + (rankBadges[i] || '') + '' + '
' + esc(m.name || '律师') + '' + '

' + esc(m.firm || '') + ' · ' + esc(m.city || '') + '

' + '
' + esc(m.score) + ' / 100' + '

匹配度

' + '
' + starStr + '
' + '
' + (m.areas ? m.areas.split(',').map(a => '' + esc(a) + '').join('') : '') + (m.years ? '执业 ' + esc(m.years) + ' 年' : '') + '
' + bdHtml + '' + '
'; }).join(''); } // 创建案源 // 创建案源(防抖:按钮禁用 + 同文案记忆拦截,双保险防重复提交) let lastLeadSubmit = { desc: '', ts: 0 }; document.getElementById('ldBtn').addEventListener('click', async () => { const desc = ldDesc.value.trim(); const city = ldCity.value.trim(); const caseType = ldTypeVal; if (desc.length < 10) { LSQ.toast('请描述您的案件情况(不少于10字)'); return; } if (!getToken()) { LSQ.toast('请先登录后再匹配律师'); return; } // 记忆防抖:60s 内相同描述禁止重复提交 const now = Date.now(); if (lastLeadSubmit.desc === desc && now - lastLeadSubmit.ts < 60000) { LSQ.toast('您已提交过相同案源,请勿重复发布'); return; } const btn = document.getElementById('ldBtn'); btn.disabled = true; btn.textContent = '匹配中…'; try { const json = await api('/ai/leads/manual', { description: desc, city: city, caseType: caseType }); if (json.code !== 0) throw new Error(json.message || '匹配失败'); lastLeadSubmit = { desc: desc, ts: Date.now() }; currentLeadId = json.data.leadId; renderLawyerCards(json.data.matches); ldResult.style.display = 'block'; loadLeadHistory(); } catch (err) { LSQ.toast('⚠️ ' + (err.message || '匹配失败')); } finally { btn.disabled = false; btn.textContent = '匹配专业律师'; } }); // 换一换 document.getElementById('ldRefresh').addEventListener('click', async () => { if (!currentLeadId) { LSQ.toast('请先创建案源'); return; } const btn = document.getElementById('ldRefresh'); btn.disabled = true; btn.textContent = '换一批…'; try { const json = await api('/ai/leads/' + currentLeadId + '/refresh'); if (json.code !== 0) throw new Error(json.message || '刷新失败'); renderLawyerCards(json.data.matches); } catch (err) { LSQ.toast('⚠️ ' + (err.message || '刷新失败')); } finally { btn.disabled = false; btn.textContent = '🔄 换一换'; } }); // 我的案源历史 async function loadLeadHistory() { // 未登录不请求(该接口需鉴权,匿名调用会 401) if (!getToken()) { var _h = document.getElementById('ldHistory'); if (_h) _h.style.display = 'none'; return; } try { const json = await api('/ai/leads', null, 'GET'); if (json.code !== 0 || !json.data.leads.length) return; const box = document.getElementById('ldHistory'); const list = document.getElementById('ldHistoryList'); box.style.display = 'block'; list.innerHTML = json.data.leads.slice(0, 5).map(l => '
' + '' + esc(l.description || '').slice(0, 40) + '' + '' + (l.status === 'matched' ? '已匹配' : '待匹配') + '' + '' + '
' ).join(''); // 删除按钮事件:确认后调 DELETE 接口 list.querySelectorAll('[data-del-lead]').forEach(function (btn) { btn.addEventListener('click', async function () { const id = btn.getAttribute('data-del-lead'); if (!confirm('确定删除这条案源吗?删除后不可恢复')) return; btn.disabled = true; btn.textContent = '删除中…'; try { const json = await api('/ai/leads/' + id, null, 'DELETE'); if (json.code !== 0) throw new Error(json.message || '删除失败'); LSQ.toast('✅ 案源已删除'); loadLeadHistory(); } catch (err) { LSQ.toast('⚠️ ' + (err.message || '删除失败')); btn.disabled = false; btn.textContent = '删除'; } }); }); } catch (e) { /* 静默 */ } } loadLeadHistory(); // 初始化加载 loadDocTypes(); loadContractTemplates(); /* ===== 子 Tab 切换(文书合同 / 法律检索) ===== */ function bindSubTabs(sel) { const box = document.querySelector(sel); if (!box) return; box.addEventListener('click', function (e) { const tab = e.target.closest('.tab'); if (!tab) return; box.querySelectorAll('.tab').forEach(function (t) { t.classList.remove('on'); }); tab.classList.add('on'); const key = tab.dataset.sub; const pane = box.parentElement; pane.querySelectorAll('[data-subpane]').forEach(function (p) { p.classList.toggle('on', p.dataset.subpane === key); }); }); } bindSubTabs('#docSubTabs'); bindSubTabs('#searchSubTabs'); /* ===== 律师工具:诉讼费计算器 ===== */ (function () { const feeBtn = document.getElementById('feeBtn'); if (!feeBtn) return; // 财产案件受理费(分段累计,诉讼费用交纳办法) function propertyFee(amt) { if (amt <= 0) return 0; if (amt <= 10000) return 50; if (amt <= 100000) return 50 + (amt - 10000) * 0.025; if (amt <= 200000) return 2300 + (amt - 100000) * 0.02; if (amt <= 500000) return 4300 + (amt - 200000) * 0.015; if (amt <= 1000000) return 8800 + (amt - 500000) * 0.01; if (amt <= 2000000) return 13800 + (amt - 1000000) * 0.009; if (amt <= 5000000) return 22800 + (amt - 2000000) * 0.008; if (amt <= 10000000) return 46800 + (amt - 5000000) * 0.007; if (amt <= 20000000) return 81800 + (amt - 10000000) * 0.006; return 141800 + (amt - 20000000) * 0.005; } // 执行费 function execFee(amt) { if (amt <= 0) return 0; if (amt <= 10000) return 50; if (amt <= 500000) return 50 + (amt - 10000) * 0.015; if (amt <= 5000000) return 7400 + (amt - 500000) * 0.01; if (amt <= 10000000) return 52400 + (amt - 5000000) * 0.005; return 77400 + (amt - 10000000) * 0.001; } // 保全费 function preserveFee(amt) { if (amt <= 0) return 0; if (amt <= 1000) return 30; if (amt <= 100000) return 30 + (amt - 1000) * 0.01; return Math.min(1020 + (amt - 100000) * 0.005, 5000); } function fmt(n) { return '¥' + Math.round(n).toLocaleString('zh-CN'); } feeBtn.addEventListener('click', function () { const type = document.getElementById('feeType').value; const amt = parseFloat(document.getElementById('feeAmount').value) || 0; const execAmt = parseFloat(document.getElementById('feeExec').value) || 0; const presAmt = parseFloat(document.getElementById('feePreserve').value) || 0; if (type === 'property' && amt <= 0) { LSQ.toast('请输入诉讼标的额'); return; } if (type === 'divorce' && amt <= 0) { LSQ.toast('请输入争议财产金额(无财产填 0)'); return; } let accept = 0, note = ''; if (type === 'property') { accept = propertyFee(amt); note = '财产案件受理费(分段累计)'; } else if (type === 'divorce') { accept = Math.max(50, Math.min(300, amt <= 200000 ? 200 : 200 + (amt - 200000) * 0.005)); note = '离婚案件受理费(含财产分割加收)'; } else if (type === 'labor') { accept = 10; note = '劳动争议受理费'; } else { accept = 300; note = '非财产案件受理费(多数 50-100 元,此处按常见标准估算)'; } const simple = document.getElementById('feeSimple').checked; const mediate = document.getElementById('feeMediate').checked; const half = simple || mediate; const acceptFinal = half ? accept / 2 : accept; const ef = execFee(execAmt); const pf = preserveFee(presAmt); const total = acceptFinal + ef + pf; const box = document.getElementById('feeResult'); box.style.display = 'block'; box.innerHTML = '
💰 费用估算结果
' + '
受理费' + fmt(acceptFinal) + '' + (half ? '(' + (simple ? '简易程序' : '') + (simple && mediate ? '+' : '') + (mediate ? '调解结案' : '') + '减半)' : '') + '
' + (execAmt > 0 ? '
执行申请费' + fmt(ef) + '
' : '') + (presAmt > 0 ? '
保全申请费' + fmt(pf) + '
' : '') + '
合计' + fmt(total) + '
' + '
' + note + ' · 具体以法院核定为准
'; }); })(); /* ===== 律师工具:赔偿计算器 ===== */ (function () { const compBtn = document.getElementById('compBtn'); if (!compBtn) return; const civilBox = document.getElementById('compCivilFields'); const injuryBox = document.getElementById('compInjuryFields'); // 模式切换 const modeTabs = document.getElementById('compModeTabs'); if (modeTabs) { modeTabs.addEventListener('click', function (e) { const t = e.target.closest('.tab'); if (!t) return; modeTabs.querySelectorAll('.tab').forEach(function (x) { x.classList.remove('on'); }); t.classList.add('on'); const m = t.dataset.compMode; if (m === 'civil') { civilBox.style.display = 'block'; injuryBox.style.display = 'none'; } else { civilBox.style.display = 'none'; injuryBox.style.display = 'block'; } const box = document.getElementById('compResult'); box.style.display = 'none'; }); } function n(id) { return parseFloat(document.getElementById(id).value) || 0; } function fmt(n) { return '¥' + Math.round(n).toLocaleString('zh-CN'); } compBtn.addEventListener('click', function () { const mode = (modeTabs.querySelector('.tab.on') || {}).dataset ? modeTabs.querySelector('.tab.on').dataset.compMode : 'civil'; const box = document.getElementById('compResult'); if (mode === 'civil') { const med = n('cMed'), income = n('cIncome'), lost = n('cLostDays'); const careDays = n('cCareDays'), careStd = n('cCareStd') || 150; const hospDays = n('cHospDays'), foodStd = n('cFoodStd') || 50; const nutrition = n('cNutrition'), transport = n('cTransport'); const level = parseInt(document.getElementById('cLevel').value) || 0; const base = n('cBase') || 50000; const loseWork = income * lost; const care = careDays * careStd; const food = hospDays * foodStd; const disab = level > 0 ? base * 20 * (1 - (level - 1) * 0.1) : 0; const total = med + loseWork + care + food + nutrition + transport + disab; box.style.display = 'block'; box.innerHTML = '
💰 赔偿估算
' + (med > 0 ? '
医疗费' + fmt(med) + '
' : '') + (loseWork > 0 ? '
误工费' + fmt(loseWork) + '(' + income.toLocaleString() + '元/月 ÷ 30 × ' + lost + '天)
' : '') + (care > 0 ? '
护理费' + fmt(care) + '
' : '') + (food > 0 ? '
住院伙食补助' + fmt(food) + '
' : '') + (nutrition > 0 ? '
营养费' + fmt(nutrition) + '
' : '') + (transport > 0 ? '
交通费' + fmt(transport) + '
' : '') + (disab > 0 ? '
残疾赔偿金' + fmt(disab) + '(' + base.toLocaleString() + '×20×' + (1 - (level - 1) * 0.1).toFixed(1) + ')
' : '') + '
合计' + fmt(total) + '
' + '
不含精神损害抚慰金、鉴定费等,以票据与鉴定结论为准
'; } else { const wage = n('gWage'), level = parseInt(document.getElementById('gLevel').value) || 1; const stay = n('gStay'), med = n('gMed'), care = n('gCare'), other = n('gOther'); const months = [0, 27, 25, 23, 21, 18, 16, 13, 11, 9, 7]; const once = wage * months[level]; const stayPay = wage * stay; const total = once + stayPay + med + care + other; box.style.display = 'block'; box.innerHTML = '
💰 工伤赔偿估算
' + '
一次性伤残补助金' + fmt(once) + '(' + wage.toLocaleString() + '×' + months[level] + '月)
' + (stayPay > 0 ? '
停工留薪期工资' + fmt(stayPay) + '
' : '') + (med > 0 ? '
医疗费' + fmt(med) + '
' : '') + (care > 0 ? '
护理费' + fmt(care) + '
' : '') + (other > 0 ? '
其他' + fmt(other) + '
' : '') + '
合计' + fmt(total) + '
' + '
五级以下工伤另含伤残津贴等按月项目,未计入
'; } }); })(); /* ===== 企业风险查询(元典) ===== */ (function () { 'use strict'; var API=(location.hostname==='localhost'||location.hostname==='127.0.0.1')?'http://localhost:3000/api':location.origin+'/api'; var $=function(s){return document.querySelector(s);}; var state = { id: null, pageNo: 1, hasMore: false, loading: false }; function esc(s){ return String(s==null?'':s).replace(/[&<>"']/g,function(c){ return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]; }); } // ═══ 接入状态检查 ═══ function checkStatus(){ fetch(API + '/company/status').then(function(r){return r.json();}).then(function(res){ if(res.code===0 && res.data && !res.data.configured){ $('#cpNoticeBar').classList.add('show'); } }).catch(function(){}); } // ═══ 企业检索 ═══ function doSearch(){ var name = $('#cpSearchInput').value.trim(); if(!name){ LSQ.toast('请输入企业/人员/商标名称'); return; } var btn = $('#cpSearchBtn'); btn.disabled = true; btn.textContent = '检索中…'; $('#cpSelList').classList.remove('show'); fetch(API + '/company/search?name=' + encodeURIComponent(name) + '&top_k=10') .then(function(r){ return r.json(); }) .then(function(res){ btn.disabled = false; btn.textContent = '检索分析'; if(res.code!==0){ LSQ.toast(res.message||'检索失败'); return; } var list = res.data || []; if(!list.length){ LSQ.toast('未找到该企业,请尝试更完整名称'); return; } $('#cpSelList').innerHTML = '
共找到 ' + list.length + ' 条候选,点击查看企业风险报告
' + list.map(function(c, i){ return '
'+ ''+esc(c['企业名称']||c.name||'')+''+ ''+esc(c['统一社会信用代码']||'')+''+ '
'; }).join(''); $('#cpSelList').classList.add('show'); $('#cpSelList').querySelectorAll('.sel-item').forEach(function(el){ el.addEventListener('click', function(){ var c = list[parseInt(el.getAttribute('data-i'))]; loadOverview(c['企业名称']||c.name||'', c.id); }); }); }) .catch(function(){ btn.disabled = false; btn.textContent = '检索分析'; LSQ.toast('网络异常,请重试'); }); } // ═══ 风险总览 ═══ function loadOverview(name, id){ $('#cpSelList').classList.remove('show'); $('#cpSearchInput').value = name; state.id = id; state.pageNo = 1; state.hasMore = false; $('#cpResultBox').classList.add('show'); $('#cpHeadCard').innerHTML = skeletonCard(); $('#cpRiskCard').innerHTML = skeletonCard(); $('#cpWritAggCard').innerHTML = skeletonCard(); $('#cpBizCard').innerHTML = skeletonCard(); $('#cpWritCard').innerHTML = skeletonCard(); var rb = $('#cpResultBox'); if(rb){ rb.scrollIntoView({ behavior: 'smooth', block: 'start' }); } fetch(API + '/company/overview?id=' + encodeURIComponent(id)) .then(function(r){ return r.json(); }) .then(function(res){ if(res.code!==0){ LSQ.toast(res.message||'查询失败'); return; } renderOverview(res.data); loadWrits(1, true); }) .catch(function(){ LSQ.toast('网络异常,请重试'); }); } function skeletonCard(){ return '
'+ '
'+ '
'+ '
'+ '
'; } function renderOverview(d){ var agg = d.aggregation || {}; var base = d.baseInfo || {}; var wa = d.writAgg || {}; var name = agg.name || base['企业名称'] || ''; // ── 头部 ── var status = base['经营状态'] || agg['经营状态'] || ''; var statusBadge = /存续|在营|开业/.test(status) ? '' : ' bad'; $('#cpHeadCard').innerHTML = '
'+ ''+ '
'+ '
'+esc(name)+'
'+ '
统一社会信用代码:'+esc(base['统一社会信用代码']||'—')+' · 法定代表人:'+esc(base['法定代表人']||'—')+'
'+ '
'+esc(base['企业类型']||'')+(base['行业']?' · '+esc(base['行业']):'')+'
'+ '
'+ (status ? ''+esc(status)+'' : '')+ '
'+ '
'+ kv('注册资本', base['注册资本']||'—')+ kv('成立日期', (base['成立日期']||'').replace(/年|月/g,'.').replace(/日/,'')||'—')+ kv('登记机关', base['登记机关']||'—')+ kv('注册地址', base['注册地址']||'—')+ '
'; // ── 风险雷达(含下钻 type)── var riskMods = [ { k:'失信被执行人', key:'失信被执行人统计', severe:true, type:'executions' }, { k:'被执行人', key:'被执行人统计', severe:true, type:'executed' }, { k:'行政处罚', key:'行政处罚统计', severe:true, type:'punishment' }, { k:'经营异常', key:'经营异常统计', severe:true, type:'abnormal' }, { k:'欠税公告', key:'欠税公告统计', severe:true, type:'tax' }, { k:'股权冻结', key:'股权冻结统计', severe:true, type:'frozen' }, { k:'严重违法', key:'严重违法统计', severe:true, type:'serious' }, { k:'股权出质', key:'股权出质统计', type:'pledge' }, { k:'对外担保', key:'对外担保统计', type:'guaranty' }, { k:'法院公告', key:'法院公告统计', type:'courtNotice' }, { k:'开庭公告', key:'开庭公告统计', type:'courtSession' }, { k:'涉诉文书', key:'涉诉文书', fromWrit:true, type:'writs' } ]; var writTotal = (wa && wa.total) || 0; var riskHtml = riskMods.map(function(m){ var n = m.fromWrit ? writTotal : ((agg[m.key]||{}).总数||0); var cls = 'ok'; if(m.severe){ cls = n>0 ? 'danger' : 'ok'; } else { cls = n>0 ? 'warn' : 'ok'; } var clickable = n>0 ? ' risk-clickable' : ''; var badge = n>0 ? '查看明细 ›' : ''; return '
'+n+'
'+m.k+'
'+badge+'
'; }).join(''); var severeTotal = 0; riskMods.forEach(function(m){ if(m.severe){ severeTotal += m.fromWrit ? writTotal : ((agg[m.key]||{}).总数||0); } }); var riskLevel = severeTotal === 0 ? '低' : (severeTotal < 5 ? '中' : (severeTotal < 20 ? '高' : '极高')); var levelCls = severeTotal === 0 ? 'lv-low' : (severeTotal < 5 ? 'lv-mid' : 'lv-high'); $('#cpRiskCard').innerHTML = '

⚠️ 风险雷达AI 生成数据来源:元典法律大数据

' + '
综合风险指数:' + severeTotal + ' 项 · 风险等级 ' + riskLevel + '
' + '
'+riskHtml+'
'; // ── 涉诉分布 ── var aggHtml = ''; if(wa && wa.total){ var dims = [ { key:'案由', label:'案由分布' }, { key:'文书种类', label:'文书种类' }, { key:'法院层级', label:'法院层级' }, { key:'结案方式', label:'结案方式' }, { key:'结案年份', label:'结案年份' } ]; aggHtml = dims.map(function(dim){ var rows = (wa[dim.key]||[]).slice(0,5); var max = rows.length ? rows[0].count : 1; return '
'+ '
'+dim.label+'
'+ (rows.length ? rows.map(function(r){ return '
'+esc(r.key)+''+ ''+ ''+r.count+'
'; }).join('') : '
')+ '
'; }).join(''); $('#cpWritAggCard').innerHTML = '

📊 涉诉分析 共 '+wa.total+' 件裁判文书网公开数据

'+ '
'+aggHtml+'
'; } else { $('#cpWritAggCard').innerHTML = '

📊 涉诉分析

🛡️
暂无公开涉诉记录
未检索到该企业相关裁判文书
'; } // ── 经营/知产概览(含下钻 type)── var bizMods = [ { k:'对外投资', key:'对外投资统计', type:'invest' }, { k:'商标', key:'商标统计', type:'brand' }, { k:'专利', key:'专利统计', type:'patent' }, { k:'软件著作权', key:'软件著作权统计', type:'softright' }, { k:'变更记录', key:'变更记录统计', type:'change' }, { k:'网站备案', key:'网站备案统计', type:'icp' } ]; var bizHtml = bizMods.map(function(m){ var n = (agg[m.key]||{}).总数 || 0; var clickable = n>0 ? ' risk-clickable' : ''; var badge = n>0 ? '查看明细 ›' : ''; return '
'+n+'
'+m.k+'
'+badge+'
'; }).join(''); $('#cpBizCard').innerHTML = '

📦 经营与知识产权概览

'+bizHtml+'
'; } function kv(k, v){ return '
'+k+'
'+esc(v)+'
'; } // ═══ 涉诉文书列表 ═══ function loadWrits(pageNo, reset){ if(!state.id || state.loading) return; state.loading = true; var btn = $('#cpWritMore'); if(btn){ btn.disabled = true; btn.textContent = '加载中…'; } fetch(API + '/company/writs?id=' + encodeURIComponent(state.id) + '&pageNo=' + pageNo) .then(function(r){ return r.json(); }) .then(function(res){ state.loading = false; if(res.code!==0){ LSQ.toast(res.message||'加载失败'); if(btn){btn.disabled=false;} return; } var d = res.data || {}; var list = d.list || []; state.hasMore = !!d.hasMore; state.pageNo = pageNo; var box = $('#cpWritList'); if(reset){ $('#cpWritCard').innerHTML = '

📄 涉诉文书裁判文书网公开数据 · 脱敏摘要

'; box = $('#cpWritList'); } if(!list.length){ if(reset){ $('#cpWritCard').innerHTML = '

📄 涉诉文书

📄
暂无涉诉文书
'; } return; } box.insertAdjacentHTML('beforeend', list.map(function(w){ var role = ''; if((w['起诉方']||[]).length && (w['应诉方']||[]).length){ var pl = w['起诉方']||[]; var dl = w['应诉方']||[]; var isP = pl.some(function(x){ return String(x).indexOf(state.compName||'')>-1; }); role = ''+(isP?'起诉方':'应诉方')+''; } return '
'+ '
'+esc(w['标题']||'')+'
'+ '
'+ (w['案号']?''+esc(w['案号'])+'':'')+ (w['案由']?''+esc(w['案由'])+'':'')+ (w['时间']?esc(String(w['时间']).slice(0,10))+' · ':'')+ (w['审判程序']?esc(w['审判程序'])+' · ':'')+ (w['文书种类']?esc(w['文书种类']):'')+ (w['裁判结果']?'
'+esc(String(w['裁判结果']).slice(0,300))+(String(w['裁判结果']).length>300?'…':'')+'
':'')+ '
'; }).join('')); btn = $('#cpWritMore'); if(btn){ btn.style.display = state.hasMore ? 'block' : 'none'; btn.disabled = false; btn.textContent = '加载更多 ▾'; } }) .catch(function(){ state.loading = false; var btn2 = $('#cpWritMore'); if(btn2){ btn2.disabled = false; btn2.textContent = '加载失败,重试 ▾'; } }); } // ═══ 企业风险报告 PDF 导出 ═══ function exportCompanyReport(){ var name = $('#cpSearchInput').value.trim() || '企业法律风险报告'; var headHtml = $('#cpHeadCard').innerHTML || ''; var riskHtml = $('#cpRiskCard').innerHTML || ''; var aggHtml = $('#cpWritAggCard').innerHTML || ''; var bizHtml = $('#cpBizCard').innerHTML || ''; var writHtml = $('#cpWritCard').innerHTML || ''; if(!headHtml && !riskHtml && !aggHtml && !bizHtml && !writHtml){ LSQ.toast('请先查询企业后再导出报告'); return; } var body = '
'+headHtml+'
'+ '
'+riskHtml+'
'+ '
'+aggHtml+'
'+ '
'+bizHtml+'
'+ '
'+writHtml+'
'; if (window.Export) { window.Export.open({ title: name + ' · 企业法律风险报告', templateName: '企业风险报告', contentType: 'html', content: { html: body }, footerNote: '数据来源:元典法律大数据 · 裁判文书网公开数据。本报告仅供参考,不构成正式法律意见。', version: 1, }); } else { var html = buildPrintHtml(name + ' · 企业法律风险报告', body, '数据来源:元典法律大数据 · 裁判文书网公开数据
法犀 · 生成时间:' + new Date().toLocaleString('zh-CN')); printHtml(html); } } var cpExportBtn = document.getElementById('cpExportPdf'); if(cpExportBtn){ cpExportBtn.addEventListener('click', exportCompanyReport); } // 查询后显示导出栏 var _origShow = $('#cpResultBox').classList; var _origAddShow = _origShow.add.bind(_origShow); _origShow.add = function(cls){ _origAddShow(cls); if(cls === 'show'){ var bar = document.getElementById('cpExportBar'); if(bar){ bar.style.display = 'block'; } } }; // ═══ 事件绑定 ═══ $('#cpSearchBtn').addEventListener('click', doSearch); $('#cpSearchInput').addEventListener('keydown', function(e){ if(e.key==='Enter') doSearch(); }); document.querySelectorAll('[data-ex]').forEach(function(el){ el.addEventListener('click', function(){ $('#cpSearchInput').value = el.getAttribute('data-ex'); doSearch(); }); }); $('#cpResultBox').addEventListener('click', function(e){ if(e.target && e.target.id === 'cpWritMore'){ loadWrits(state.pageNo + 1, false); } }); checkStatus(); // ═══ 企业明细下钻(点击风险/知产格子 → 弹窗列表)═══ var cdState = { type: null, name: '', pageNo: 1, hasMore: false, loading: false, list: [] }; // 各类型字段渲染模板(元典返回为中文键) var CD_TEMPLATES = { executions: { title: '失信被执行人', keys: [['案号','案号'],['执行法院','执行法院'],['立案时间','立案时间'],['履行情况','履行情况']] }, executed: { title: '被执行人', keys: [['案号','案号'],['执行法院','执行法院'],['立案时间','立案时间'],['执行标的','执行标的']] }, punishment: { title: '行政处罚', keys: [['决定文书号','决定文书号'],['处罚机关','处罚机关'],['处罚日期','处罚日期'],['处罚内容','处罚内容']] }, abnormal: { title: '经营异常', keys: [['列入原因','列入原因'],['列入机关','列入机关'],['列入日期','列入日期']] }, tax: { title: '欠税公告', keys: [['税种','税种'],['欠税余额','欠税余额'],['公告日期','公告日期']] }, serious: { title: '严重违法', keys: [['列入原因','列入原因'],['列入日期','列入日期'],['决定机关','决定机关']] }, frozen: { title: '股权冻结', keys: [['冻结数额','冻结数额'],['冻结日期','冻结日期'],['解冻日期','解冻日期'],['执行法院','执行法院']] }, pledge: { title: '股权出质', keys: [['出质人','出质人'],['质权人','质权人'],['出质股权数额','出质股权数额'],['状态','状态']] }, guaranty: { title: '对外担保', keys: [['担保金额','担保金额'],['担保起始日期','担保起始日期'],['担保期限','担保期限']] }, courtSession: { title: '开庭公告', keys: [['起诉方','起诉方'],['应诉方','应诉方'],['审理时间','审理时间'],['审理法院','审理法院']] }, courtNotice: { title: '法院公告', keys: [['起诉方','起诉方'],['应诉方','应诉方'],['审理法院','审理法院'],['公告内容','公告内容']] }, invest: { title: '对外投资', keys: [['被投资企业名称','被投资企业名称'],['投资比例','投资比例'],['投资金额','投资金额']] }, brand: { title: '商标', keys: [['商标名称','商标名称'],['注册号','注册号'],['类别','类别'],['流程状态','流程状态']] }, patent: { title: '专利', keys: [['专利名称','专利名称'],['申请公布号','申请公布号'],['申请日期','申请日期'],['发明人','发明人']] }, softright: { title: '软件著作权', keys: [['软件全称','软件全称'],['登记号','登记号'],['版本号','版本号'],['登记日期','登记日期']] }, worksright: { title: '作品著作权', keys: [['作品名称','作品名称'],['登记号','登记号'],['登记日期','登记日期']] }, icp: { title: '网站备案', keys: [['网站名称','网站名称'],['域名','域名'],['备案号','备案号'],['审核日期','审核日期']] }, change: { title: '变更记录', keys: [['变更时间','变更时间'],['变更项目','变更项目'],['变更前内容','变更前内容'],['变更后内容','变更后内容']] } }; // 数组/JSON 字符串 → 可读文本 function cdVal(v){ if(v==null) return ''; if(Array.isArray(v)) return v.join('、'); if(typeof v === 'object'){ try { var s = JSON.stringify(v); return s.length > 120 ? s.slice(0,120)+'…' : s; } catch(e){ return String(v); } } return String(v); } function renderCdList(reset){ var box = $('#cdList'); if(reset){ box.innerHTML = ''; } var tpl = CD_TEMPLATES[cdState.type]; if(!cdState.list.length){ box.innerHTML = '
暂无相关记录
'; $('#cdLoadMore').style.display = 'none'; return; } var html = cdState.list.map(function(item){ var keys = tpl.keys; var rows = keys.map(function(k){ var v = item[k[1]]; if(v==null || (Array.isArray(v)&&!v.length)) return ''; var val = cdVal(v); if(!val) return ''; if(String(k[1]).indexOf('内容')>-1 || String(k[1]).indexOf('原因')>-1 || String(k[1]).indexOf('前')>-1 || String(k[1]).indexOf('后')>-1){ return '
'+esc(k[0])+':'+esc(val)+'
'; } return ''+esc(k[0])+':'+esc(val)+''; }).filter(Boolean).join(''); var title = item[tpl.keys[0][1]] || (item['标题'] || item['名称'] || tpl.title); return '
'+esc(cdVal(title))+'
'+rows+'
'; }).join(''); box.insertAdjacentHTML('beforeend', html); var btn = $('#cdLoadMore'); btn.style.display = cdState.hasMore ? 'block' : 'none'; btn.disabled = false; btn.textContent = '加载更多 ▾'; } function loadCdDetail(pageNo, reset){ if(!state.id || cdState.loading) return; cdState.loading = true; var status = $('#cdStatus'); if(status){ status.textContent = '加载中…'; } fetch(API + '/company/detail?id=' + encodeURIComponent(state.id) + '&type=' + cdState.type + '&pageNo=' + pageNo) .then(function(r){ return r.json(); }) .then(function(res){ cdState.loading = false; if(res.code!==0){ if(status){ status.textContent = ''; } LSQ.toast(res.message||'加载失败'); return; } var d = res.data || {}; cdState.pageNo = pageNo; cdState.hasMore = !!d.hasMore; if(reset){ cdState.list = []; } cdState.list = cdState.list.concat(d.list || []); if(status){ status.textContent = ''; } renderCdList(reset); }) .catch(function(){ cdState.loading = false; if(status){ status.textContent = ''; } LSQ.toast('网络异常,请重试'); var btn = $('#cdLoadMore'); if(btn){ btn.disabled = false; btn.textContent = '加载失败,重试 ▾'; } }); } function openCdDetail(type, name, count){ var tpl = CD_TEMPLATES[type]; if(!tpl) return; cdState.type = type; cdState.name = name; cdState.pageNo = 1; cdState.hasMore = false; cdState.list = []; $('#cdTitle').textContent = name + ' · 明细'; $('#cdSubtitle').textContent = '共 ' + (count||'—') + ' 条'; $('#cdList').innerHTML = '
加载中…
'; $('#cdLoadMore').style.display = 'none'; LSQ.openModal('cdModal'); loadCdDetail(1, true); } // 风险格子点击(委托给容器,事件冒泡) $('#cpRiskCard').addEventListener('click', function(e){ var cell = e.target.closest('.risk-cell.risk-clickable'); if(!cell) return; openCdDetail(cell.getAttribute('data-dtype'), cell.getAttribute('data-dname'), cell.getAttribute('data-dcount')); }); $('#cpBizCard').addEventListener('click', function(e){ var cell = e.target.closest('.risk-cell.risk-clickable'); if(!cell) return; openCdDetail(cell.getAttribute('data-dtype'), cell.getAttribute('data-dname'), cell.getAttribute('data-dcount')); }); $('#cdLoadMore').addEventListener('click', function(){ if(cdState.loading) return; loadCdDetail(cdState.pageNo + 1, false); }); // ═══ v4.0 用量条:页面加载即拉取今日 AI 用量(游客带 guest_id) ═══ (function loadUsage() { fetch('/api/ai/usage', { method: 'GET', headers: { 'X-Guest-Id': getGuestId() }, }).then(function (r) { return r.json(); }).then(function (res) { if (res.code === 0 && res.data) renderUsageBar(res.data); }).catch(function () {}); })(); // ═══ URL 直达:ai.html?tab=search&sub=company → 企业风险子 tab(company.html 收敛后入口)═══ try { const qs = new URLSearchParams(location.search); let tabKey = qs.get('tab'); if (tabKey === 'doccontract') tabKey = 'tools'; if (tabKey && window.SwitchTab) window.SwitchTab(tabKey); const subKey = qs.get('sub'); if (tabKey === 'search' && subKey) { const subTab = document.querySelector('#searchSubTabs .tab[data-sub="' + subKey + '"]'); if (subTab) subTab.click(); } } catch (e) { /* 容错 */ } })(); })();