feat(定时任务): 新增第三个 HTML 模板 no.3(只展示)

- no.3.html(3.8MB)复制进 templates/no3/,无 const DATA= 注入点 → 注册为
  convertible=False 只展示模板(同 no.2,后续接转换再开)
- 前端下拉/参考列表从 API 拉取,no.3 自动出现,无需前端改动
- 测试覆盖 no.3 存在/只展示/文件可读

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
董宏凯andClaude Opus 4.8 committed 2026-06-24 14:38:49 +08:00
1 parent 089d3e171f
commit 5a2441cc9f
2 files changed
+192

No files matched your search

@@ -140,6 +140,14 @@ TEMPLATES: tuple[TemplateSpec, ...] = (
# 暂只展示已有数据,不做 JSON 转换/填充(后续接入转换后再开)。
convertible=False,
),
TemplateSpec(
id="no3",
name="no.3",
description="",
file="no3/no3.html",
# 无 const DATA= 注入点,暂只展示已有数据(后续接入转换后再开)。
convertible=False,
),
)
_TEMPLATES_BY_ID: dict[str, TemplateSpec] = {t.id: t for t in TEMPLATES}
@@ -0,0 +1,184 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>台湾海上作战力量知识展示系统(全文增强版)</title>
<style>
:root{--red:#b4232d;--red-deep:#7f111b;--red-2:#d34a53;--pink:#fff4f4;--bg:#f7f3f3;--card:#fff;--line:#ecd6d8;--text:#312b2c;--muted:#7a6b6d;--shadow:0 10px 30px rgba(98,24,33,.08);--radius:14px}
*{box-sizing:border-box} html,body{margin:0;min-height:100%;font-family:"Microsoft YaHei","PingFang SC","Noto Sans CJK SC",Arial,sans-serif;color:var(--text);background:var(--bg)}
button,input,select{font:inherit} button{cursor:pointer}.app{min-height:100vh}.topbar{height:72px;background:linear-gradient(110deg,var(--red-deep),var(--red));color:#fff;display:flex;align-items:center;padding:0 28px;position:sticky;top:0;z-index:50;box-shadow:0 8px 30px rgba(91,12,20,.22)}
.brand{display:flex;align-items:center;gap:14px;min-width:360px}.brand-mark{width:42px;height:42px;border-radius:13px;background:rgba(255,255,255,.16);display:grid;place-items:center;border:1px solid rgba(255,255,255,.28);font-weight:900}.brand h1{font-size:19px;margin:0;letter-spacing:.4px}.brand p{margin:4px 0 0;font-size:12px;color:#f8dfe2}
.global-search{margin-left:auto;width:min(520px,42vw);position:relative}.global-search input{width:100%;height:42px;border-radius:22px;border:1px solid rgba(255,255,255,.3);background:rgba(255,255,255,.12);color:#fff;padding:0 46px 0 18px;outline:none}.global-search input::placeholder{color:#f4d8db}.search-icon{position:absolute;right:15px;top:10px;font-size:18px}.top-actions{display:flex;gap:8px;margin-left:14px}.ghost-btn{border:1px solid rgba(255,255,255,.32);background:rgba(255,255,255,.10);color:#fff;border-radius:10px;padding:9px 12px}
.tabbar{background:#fff;border-bottom:1px solid var(--line);display:flex;gap:6px;padding:10px 24px;position:sticky;top:72px;z-index:45;overflow:auto}.tab-btn{border:0;background:transparent;color:#6e5c5e;padding:11px 17px;border-radius:11px;white-space:nowrap;font-weight:700}.tab-btn:hover{background:var(--pink);color:var(--red)}.tab-btn.active{background:var(--red);color:#fff;box-shadow:0 7px 16px rgba(180,35,45,.22)}
.content{padding:22px 26px 50px;max-width:1680px;margin:0 auto}.tab-page{display:none}.tab-page.active{display:block}.page-head{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:17px;gap:16px}.page-head h2{margin:0;font-size:25px;color:var(--red-deep)}.page-head p{margin:7px 0 0;color:var(--muted);max-width:840px;line-height:1.7}.page-actions{display:flex;gap:8px}.btn{border:1px solid var(--line);background:#fff;color:var(--red-deep);padding:9px 13px;border-radius:10px;font-weight:700}.btn.primary{background:var(--red);color:#fff;border-color:var(--red)}.btn.soft{background:var(--pink);border-color:#f2c9cd}.btn:hover{transform:translateY(-1px)}
.hero{background:linear-gradient(125deg,#fff,#fff7f7);border:1px solid var(--line);border-radius:20px;box-shadow:var(--shadow);padding:24px;display:grid;grid-template-columns:1.25fr .75fr;gap:24px;position:relative;overflow:hidden}.hero:after{content:"";position:absolute;width:270px;height:270px;border-radius:50%;right:-110px;top:-125px;background:radial-gradient(circle,rgba(180,35,45,.15),rgba(180,35,45,0))}.hero-title{font-size:29px;margin:0;color:var(--red-deep)}.hero-desc{color:#655557;line-height:1.85;margin:13px 0 16px}.hero-tags{display:flex;gap:8px;flex-wrap:wrap}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:999px;padding:6px 10px;background:var(--pink);color:var(--red-deep);border:1px solid #f1ced1;font-size:12px;font-weight:700}.hero-panel{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;align-content:center}.mini-stat{background:#fff;border:1px solid var(--line);border-radius:14px;padding:13px 15px}.mini-stat b{font-size:24px;color:var(--red)}.mini-stat span{display:block;color:var(--muted);font-size:12px;margin-top:4px}
.stat-grid{display:grid;grid-template-columns:repeat(5,minmax(140px,1fr));gap:13px;margin-top:16px}.stat-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:17px;box-shadow:0 5px 18px rgba(85,30,35,.045);position:relative}.stat-card:before{content:"";position:absolute;left:0;top:17px;width:4px;height:34px;background:var(--red);border-radius:0 4px 4px 0}.stat-card strong{font-size:27px;color:var(--red-deep);display:block}.stat-card span{font-size:13px;color:var(--muted);display:block;margin-top:5px}.section-title{display:flex;align-items:center;justify-content:space-between;margin:27px 0 12px}.section-title h3{margin:0;color:var(--red-deep);font-size:18px}.section-title small{color:var(--muted)}
.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:15px}.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:15px}.panel{background:#fff;border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:18px}.panel-title{display:flex;justify-content:space-between;align-items:center;margin-bottom:13px}.panel-title h3,.panel-title h4{margin:0;color:var(--red-deep)}.domain-bars{display:flex;flex-direction:column;gap:11px}.bar-row{display:grid;grid-template-columns:120px 1fr 44px;gap:10px;align-items:center;font-size:13px}.bar-track{height:9px;background:#f2e5e6;border-radius:8px;overflow:hidden}.bar-fill{height:100%;background:linear-gradient(90deg,var(--red),#e36b73);border-radius:8px}.system-card{padding:18px;border-radius:14px;border:1px solid var(--line);background:linear-gradient(145deg,#fff,#fff8f8)}.system-card h4{margin:0 0 7px;color:var(--red-deep)}.system-card p{color:var(--muted);line-height:1.7;margin:0}.system-list{display:flex;flex-wrap:wrap;gap:7px;margin-top:13px}
.toolbar{background:#fff;border:1px solid var(--line);border-radius:14px;padding:12px;display:flex;flex-wrap:wrap;gap:9px;align-items:center;margin-bottom:15px;box-shadow:0 5px 17px rgba(80,30,34,.035)}.control{height:38px;border:1px solid var(--line);border-radius:9px;background:#fff;padding:0 11px;color:var(--text);min-width:145px;outline:none}.control:focus{border-color:var(--red)}.toolbar .spacer{flex:1}.segmented{display:flex;background:#f5ebec;border-radius:9px;padding:3px}.seg-btn{border:0;background:transparent;padding:7px 10px;border-radius:7px;color:#6c595b}.seg-btn.active{background:#fff;color:var(--red);box-shadow:0 2px 8px rgba(90,30,35,.09)}
.card-grid{display:grid;grid-template-columns:repeat(3,minmax(260px,1fr));gap:14px}.record-card{background:#fff;border:1px solid var(--line);border-radius:15px;padding:17px;box-shadow:0 7px 22px rgba(90,28,35,.045);transition:.18s;position:relative;overflow:hidden}.record-card:hover{transform:translateY(-3px);box-shadow:0 14px 28px rgba(90,28,35,.09);border-color:#ddb3b7}.record-card:after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;background:linear-gradient(90deg,var(--red),#e7959a);opacity:.8}.record-top{display:flex;gap:10px;justify-content:space-between}.record-title{font-size:17px;font-weight:800;color:var(--red-deep);margin:0}.status{font-size:11px;border-radius:99px;padding:5px 8px;background:#f6e7e8;color:var(--red-deep);white-space:nowrap}.record-meta{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0}.meta-pill{background:#f7f3f3;color:#715f61;border-radius:6px;padding:4px 7px;font-size:11px}.record-desc{font-size:13px;line-height:1.65;color:#665759;min-height:43px}.record-foot{display:flex;align-items:center;justify-content:space-between;margin-top:14px}.confidence{color:var(--muted);font-size:11px}.link-btn{border:0;background:transparent;color:var(--red);font-weight:800;padding:4px 0}
.table-wrap{display:none;background:#fff;border:1px solid var(--line);border-radius:14px;overflow:auto}.table-wrap.active{display:block}.data-table{width:100%;border-collapse:collapse;min-width:960px}.data-table th{background:#fff4f4;color:var(--red-deep);padding:12px;text-align:left;font-size:12px;position:sticky;top:0}.data-table td{padding:11px 12px;border-top:1px solid #f1e4e5;font-size:12px;vertical-align:top}.data-table tr:hover td{background:#fffafa}.pager{display:flex;justify-content:center;align-items:center;gap:7px;margin:17px}.pager button{border:1px solid var(--line);background:#fff;border-radius:8px;padding:7px 10px;color:var(--red-deep)}.pager button.active{background:var(--red);color:#fff}
.org-layout{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:15px}.org-canvas{background:#fff;border:1px solid var(--line);border-radius:15px;padding:20px;min-height:670px;overflow:auto;box-shadow:var(--shadow)}.org-tree,.org-tree ul{list-style:none;margin:0;padding-left:28px;position:relative}.org-tree>li{padding-left:0}.org-tree ul:before{content:"";position:absolute;top:0;bottom:18px;left:12px;border-left:1px solid #d8b4b7}.org-tree li{position:relative;padding:6px 0 6px 21px}.org-tree li:before{content:"";position:absolute;left:12px;top:25px;width:10px;border-top:1px solid #d8b4b7}.org-tree>li:before{display:none}.org-node{display:inline-grid;grid-template-columns:25px 1fr auto;gap:8px;align-items:center;min-width:235px;max-width:370px;border:1px solid var(--line);background:#fff;border-radius:11px;padding:9px 10px;box-shadow:0 4px 12px rgba(100,30,36,.05);transition:.17s}.org-node:hover,.org-node.selected{border-color:var(--red);background:#fff6f6}.expand-btn{width:24px;height:24px;border:0;background:#f5e4e6;color:var(--red);border-radius:7px;font-weight:900}.org-node-name{font-size:13px;font-weight:800;color:var(--red-deep)}.org-node-type{font-size:10px;color:var(--muted);margin-top:3px}.org-side{background:#fff;border:1px solid var(--line);border-radius:15px;padding:18px;box-shadow:var(--shadow);height:max-content;position:sticky;top:140px}.org-empty{color:var(--muted);line-height:1.8;text-align:center;padding:55px 15px}.org-side h3{color:var(--red-deep);margin:0 0 10px}.kv-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:9px}.kv-card{background:#fff8f8;border:1px solid #f1dddd;border-radius:10px;padding:10px}.kv-card small{color:var(--muted);display:block;margin-bottom:4px}.kv-card b{color:var(--red-deep);font-size:13px}.side-summary{font-size:13px;line-height:1.7;color:#65565a;margin:12px 0}.relation-list{display:flex;flex-wrap:wrap;gap:6px}.relation-chip{font-size:11px;background:#f8eeee;color:#7d2830;border:1px solid #eed2d5;border-radius:999px;padding:5px 8px}
.subtabs{display:flex;gap:8px;margin-bottom:13px}.subtab-btn{border:1px solid var(--line);background:#fff;border-radius:10px;padding:9px 14px;color:#6e5b5d;font-weight:700}.subtab-btn.active{background:var(--red);color:#fff;border-color:var(--red)}
.timeline{position:relative;padding-left:20px}.timeline:before{content:"";position:absolute;left:5px;top:4px;bottom:4px;width:2px;background:#edd4d7}.timeline-item{position:relative;padding:0 0 18px 15px}.timeline-item:before{content:"";position:absolute;left:-19px;top:4px;width:10px;height:10px;border-radius:50%;background:var(--red);box-shadow:0 0 0 4px #f8e8ea}.timeline-item h5{margin:0 0 5px;color:var(--red-deep)}.timeline-item p{margin:0;color:var(--muted);font-size:12px;line-height:1.6}
.drawer-mask,.modal-mask{position:fixed;inset:0;background:rgba(45,18,21,.42);z-index:80;display:none}.drawer-mask.open,.modal-mask.open{display:block}.drawer{position:absolute;right:0;top:0;height:100%;width:min(490px,94vw);background:#fff;box-shadow:-18px 0 45px rgba(50,15,20,.18);display:flex;flex-direction:column}.drawer-head{background:linear-gradient(115deg,var(--red-deep),var(--red));color:#fff;padding:20px}.drawer-head h3{margin:0;font-size:22px}.drawer-head p{margin:7px 0 0;color:#f6dfe1;font-size:13px}.close-btn{position:absolute;right:15px;top:14px;width:34px;height:34px;border-radius:10px;border:1px solid rgba(255,255,255,.3);background:rgba(255,255,255,.12);color:#fff;font-size:20px}.drawer-body{padding:18px;overflow:auto;flex:1}.drawer-actions{padding:14px 18px;border-top:1px solid var(--line);display:flex;gap:8px}.quick-section{border:1px solid var(--line);border-radius:12px;padding:13px;margin-bottom:11px}.quick-section h4{margin:0 0 8px;color:var(--red-deep)}.quick-section p{margin:0;color:#65575a;line-height:1.65;font-size:13px}
.modal-mask{padding:26px;overflow:auto}.modal-page{max-width:1250px;margin:0 auto;background:#fff;border-radius:18px;min-height:calc(100vh - 52px);box-shadow:0 30px 80px rgba(50,10,17,.3);overflow:hidden}.modal-hero{background:linear-gradient(120deg,var(--red-deep),var(--red));color:#fff;padding:28px 32px;position:relative}.modal-hero h2{margin:0;font-size:27px}.modal-hero p{margin:9px 0;color:#f8dfe1;line-height:1.7;max-width:850px}.modal-tabs{display:flex;gap:6px;padding:11px 20px;border-bottom:1px solid var(--line);overflow:auto;position:sticky;top:0;background:#fff;z-index:2}.modal-tab{border:0;background:#fff4f4;color:#6e5356;border-radius:9px;padding:9px 12px;white-space:nowrap}.modal-tab.active{background:var(--red);color:#fff}.modal-content{padding:22px 27px}.detail-section{display:none}.detail-section.active{display:block}.detail-section h3{color:var(--red-deep);margin:0 0 15px}.detail-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:13px}.detail-block{border:1px solid var(--line);border-radius:13px;padding:15px;background:linear-gradient(145deg,#fff,#fffbfb)}.detail-block h4{margin:0 0 8px;color:#7f1b24;font-size:14px}.detail-value{color:#5f5153;font-size:13px;line-height:1.75;word-break:break-word}.detail-value ul{margin:5px 0;padding-left:19px}.detail-value li{margin:5px 0}.unknown{color:#9a7d80;font-style:italic}.markdown-content{line-height:1.85;color:#574a4c}.markdown-content h1,.markdown-content h2,.markdown-content h3{color:var(--red-deep)}.markdown-content code{background:#f7eded;padding:2px 5px;border-radius:5px}.markdown-content pre{background:#2c2526;color:#fff;border-radius:10px;padding:13px;overflow:auto}
.search-hero{background:#fff;border:1px solid var(--line);border-radius:16px;padding:22px;box-shadow:var(--shadow)}.big-search{display:flex;gap:9px}.big-search input{flex:1;height:50px;border:1px solid var(--line);border-radius:12px;padding:0 16px;font-size:15px;outline:none}.big-search input:focus{border-color:var(--red);box-shadow:0 0 0 4px #f7e4e6}.search-groups{display:grid;grid-template-columns:190px 1fr;gap:16px;margin-top:16px}.facet{background:#fff;border:1px solid var(--line);border-radius:13px;padding:13px;height:max-content}.facet h4{margin:0 0 10px;color:var(--red-deep)}.facet label{display:flex;gap:8px;align-items:center;font-size:12px;margin:8px 0;color:#625456}.result-list{display:flex;flex-direction:column;gap:10px}.search-result{background:#fff;border:1px solid var(--line);border-radius:13px;padding:15px}.search-result h4{margin:0;color:var(--red-deep)}.search-result p{margin:7px 0;color:#67585a;line-height:1.6;font-size:13px}.mark{background:#ffe0a8;color:#6f3500;padding:0 2px;border-radius:2px}.empty-state{padding:65px 20px;text-align:center;color:var(--muted);background:#fff;border:1px dashed #dbbec1;border-radius:14px}
.notice{border-left:4px solid var(--red);background:#fff7f7;padding:12px 14px;border-radius:0 10px 10px 0;color:#675457;font-size:12px;line-height:1.7}.footer-note{text-align:center;color:#8a7779;font-size:11px;margin-top:28px}
@media(max-width:1100px){.stat-grid{grid-template-columns:repeat(3,1fr)}.card-grid,.grid-3{grid-template-columns:repeat(2,1fr)}.org-layout{grid-template-columns:1fr}.org-side{position:static}.hero{grid-template-columns:1fr}.search-groups{grid-template-columns:1fr}}
@media(max-width:720px){.topbar{padding:0 13px}.brand{min-width:0}.brand p{display:none}.global-search{display:none}.content{padding:16px 12px}.tabbar{padding:8px 10px;top:72px}.stat-grid,.card-grid,.grid-2,.grid-3,.detail-grid{grid-template-columns:1fr}.page-head{flex-direction:column}.org-node{min-width:210px}.modal-mask{padding:0}.modal-page{min-height:100vh;border-radius:0}.search-groups{grid-template-columns:1fr}}
/* v1.1 全文增强详情 */
.drawer{width:min(620px,96vw)}
.drawer-body{background:linear-gradient(180deg,#fff,#fffafa)}
.drawer-summary{font-size:14px;line-height:1.85;color:#594a4c;margin:0 0 14px}
.drawer-metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:12px 0 15px}
.drawer-metric{border:1px solid var(--line);background:#fff;border-radius:11px;padding:10px;text-align:center}
.drawer-metric b{display:block;color:var(--red);font-size:20px}.drawer-metric span{font-size:11px;color:var(--muted)}
.drawer-section{background:#fff;border:1px solid var(--line);border-radius:13px;padding:14px;margin:11px 0}
.drawer-section h4{margin:0 0 9px;color:var(--red-deep);display:flex;justify-content:space-between;gap:8px}
.drawer-section .mini-content{font-size:13px;line-height:1.75;color:#625356}
.preview-list{margin:0;padding-left:19px}.preview-list li{margin:5px 0}
.source-path{font-family:Consolas,monospace;background:#f8eeee;border:1px solid #efd8da;border-radius:8px;padding:8px;word-break:break-all;font-size:11px;color:#79585c}
.modal-mask{padding:12px}.modal-page{max-width:1580px;min-height:calc(100vh - 24px);border-radius:16px}
.modal-hero{padding:24px 30px}.modal-hero h2{font-size:29px}.modal-hero-grid{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px;align-items:end}
.modal-hero-stats{display:grid;grid-template-columns:repeat(4,minmax(86px,1fr));gap:8px;min-width:390px}
.hero-stat{background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.24);border-radius:11px;padding:10px;text-align:center}
.hero-stat b{display:block;font-size:20px}.hero-stat span{font-size:10px;color:#f7dfe1}
.modal-tabs{padding:10px 22px;top:0}.modal-content{padding:0;background:#faf7f7}
.detail-section{min-height:560px}
.detail-layout{display:grid;grid-template-columns:225px minmax(0,1fr) 285px;gap:16px;align-items:start;padding:20px}
.detail-nav,.detail-aside{position:sticky;top:67px;background:#fff;border:1px solid var(--line);border-radius:13px;padding:13px;max-height:calc(100vh - 120px);overflow:auto}
.detail-nav h4,.detail-aside h4{margin:0 0 10px;color:var(--red-deep)}
.toc-link{display:block;width:100%;text-align:left;border:0;background:transparent;color:#6c575a;border-left:3px solid transparent;padding:7px 8px;border-radius:5px;font-size:12px;line-height:1.4}
.toc-link:hover{background:#fff3f4;color:var(--red);border-left-color:var(--red)}
.toc-l3{padding-left:20px;color:#887477}.toc-l4{padding-left:30px;color:#9b8588}
.detail-main{min-width:0}
.detail-dashboard{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
.detail-module{background:#fff;border:1px solid var(--line);border-radius:14px;padding:17px;box-shadow:0 5px 18px rgba(80,25,32,.04);scroll-margin-top:82px}
.detail-module.full{grid-column:1/-1}.detail-module h3{margin:0 0 13px;color:var(--red-deep);font-size:18px;border-bottom:1px solid #f1e0e1;padding-bottom:9px}
.field-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
.field-card{border:1px solid #f0dfe0;background:linear-gradient(145deg,#fff,#fffafa);border-radius:11px;padding:12px;min-width:0}
.field-card.wide{grid-column:1/-1}.field-card h4{margin:0 0 7px;color:#81202a;font-size:13px}.field-value{font-size:13px;line-height:1.78;color:#594c4e;word-break:break-word}
.field-value p{margin:5px 0}.field-value ul,.field-value ol{padding-left:20px;margin:5px 0}.field-value li{margin:5px 0}
.value-chips{display:flex;flex-wrap:wrap;gap:6px}.value-chip{background:#fff1f2;border:1px solid #efd0d3;color:#7f242d;border-radius:8px;padding:5px 8px;font-size:12px}
.evidence-note{margin-top:8px;background:#fff5df;border-left:3px solid #d5942e;padding:8px 10px;border-radius:6px;color:#765a2e;font-size:11px}
.evidence-state{font-size:10px;border-radius:99px;padding:3px 7px;background:#eee;color:#6d6062;margin-left:6px}
.timeline{position:relative;margin:4px 0;padding-left:23px}.timeline:before{content:"";position:absolute;left:7px;top:5px;bottom:5px;border-left:2px solid #e5c0c3}.timeline-item{position:relative;padding:0 0 13px 8px;color:#5d4e50;font-size:13px;line-height:1.7}.timeline-item:before{content:"";position:absolute;left:-21px;top:6px;width:9px;height:9px;border-radius:50%;background:var(--red);box-shadow:0 0 0 4px #fae9ea}
.markdown-shell{background:#fff;border:1px solid var(--line);border-radius:14px;padding:24px;box-shadow:0 5px 18px rgba(80,25,32,.04);min-height:600px}
.markdown-content{font-size:14px;line-height:1.9;color:#4f4345}.markdown-content h1{font-size:26px;border-bottom:2px solid var(--red);padding-bottom:10px}.markdown-content h2{font-size:21px;margin-top:30px;border-left:5px solid var(--red);padding:6px 0 6px 12px;background:linear-gradient(90deg,#fff3f4,transparent)}.markdown-content h3{font-size:17px;margin-top:23px}.markdown-content h4{font-size:15px;color:#8f2731}.markdown-content blockquote{margin:14px 0;border-left:4px solid var(--red);background:#fff4f4;padding:12px 16px;color:#654b4f}.markdown-content table{width:100%;border-collapse:collapse;margin:15px 0;font-size:12px}.markdown-content th{background:#fff0f1;color:#7f111b}.markdown-content th,.markdown-content td{border:1px solid #ead5d7;padding:9px;vertical-align:top}.markdown-content hr{border:0;border-top:1px solid var(--line);margin:22px 0}.markdown-content a{color:var(--red)}
.wiki-link{border:0;background:#fff0f1;color:var(--red);font-weight:700;border-radius:6px;padding:2px 6px;cursor:pointer}.wiki-link:hover{background:var(--red);color:#fff}
.aside-stat-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:7px}.aside-stat{background:#fff6f6;border:1px solid #efd9db;border-radius:9px;padding:8px}.aside-stat b{display:block;color:var(--red);font-size:17px}.aside-stat span{font-size:10px;color:var(--muted)}
.aside-group{margin-top:14px}.aside-group h5{margin:0 0 7px;color:#7d242d}.aside-list{display:flex;flex-direction:column;gap:6px}.aside-rel{border:1px solid #efd9db;background:#fffafa;border-radius:8px;padding:8px;text-align:left;color:#5e4d50;font-size:11px;cursor:pointer}.aside-rel:hover{border-color:var(--red);color:var(--red)}
.relation-groups{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:15px}.relation-group{background:#fff;border:1px solid var(--line);border-radius:13px;padding:15px}.relation-group h3{font-size:16px}.relation-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}.relation-card{border:1px solid #eed8da;background:#fffafa;border-radius:10px;padding:11px;cursor:pointer}.relation-card:hover{border-color:var(--red);transform:translateY(-1px)}.relation-card b{display:block;color:#7f111b}.relation-card span{font-size:11px;color:var(--muted)}
.evidence-page{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;padding:20px}.evidence-panel{background:#fff;border:1px solid var(--line);border-radius:14px;padding:17px}.evidence-panel.wide{grid-column:1/-1}.evidence-panel h3{margin:0 0 12px;color:var(--red-deep)}
.original-badge{display:inline-flex;align-items:center;gap:5px;background:#ecf7ef;color:#2d6e42;border:1px solid #cce5d3;border-radius:99px;padding:5px 9px;font-size:11px;font-weight:700}
@media(max-width:1180px){.detail-layout{grid-template-columns:190px minmax(0,1fr)}.detail-aside{display:none}.modal-hero-grid{grid-template-columns:1fr}.modal-hero-stats{min-width:0}}
@media(max-width:820px){.detail-layout{display:block;padding:12px}.detail-nav{position:static;max-height:none;margin-bottom:12px}.detail-dashboard,.field-grid,.relation-groups,.relation-cards,.evidence-page{grid-template-columns:1fr}.detail-module,.field-card.wide,.evidence-panel.wide{grid-column:auto}.modal-hero-stats{grid-template-columns:repeat(2,1fr)}.drawer-metrics{grid-template-columns:repeat(2,1fr)}}
</style>
<style id="fallback-style">
#staticFallback{padding:22px 26px 50px;max-width:1680px;margin:0 auto}.fallback-alert{background:#fff7e8;border:1px solid #edd3a4;color:#735626;border-radius:12px;padding:12px 15px;margin:14px 0}.fallback-grid{display:grid;grid-template-columns:repeat(4,minmax(150px,1fr));gap:12px}.fallback-card{background:#fff;border:1px solid #ecd6d8;border-radius:14px;padding:17px;box-shadow:0 7px 20px rgba(90,28,35,.05)}.fallback-card b{display:block;color:#7f111b;font-size:25px}.fallback-card span{color:#7a6b6d;font-size:12px}.fallback-section{background:#fff;border:1px solid #ecd6d8;border-radius:15px;padding:18px;margin-top:15px}.fallback-section h3{color:#7f111b;margin:0 0 10px}.fallback-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.fallback-list div{background:#fff7f7;border:1px solid #f0dfe0;border-radius:9px;padding:10px;color:#594c4e}.runtime-error{background:#fff0f0;border:1px solid #d56a73;color:#7f111b;border-radius:10px;padding:12px;margin:12px 26px;font-weight:700}@media(max-width:800px){.fallback-grid,.fallback-list{grid-template-columns:1fr 1fr}}@media(max-width:520px){.fallback-grid,.fallback-list{grid-template-columns:1fr}}
</style>
</head>
<body>
<div class="app">
<header class="topbar">
<div class="brand"><div class="brand-mark">海</div><div><h1>台湾海上作战力量知识展示系统</h1><p>公开来源知识图谱与档案检索平台</p></div></div>
<div class="global-search"><input id="globalSearch" placeholder="搜索单位、舰艇、装备、基地、项目或任务…"><span class="search-icon">⌕</span></div>
<div class="top-actions"><button class="ghost-btn" id="dataInfoBtn">数据说明</button></div>
</header>
<nav class="tabbar" id="tabbar"><button class="tab-btn active" data-tab="总览">总览</button><button class="tab-btn " data-tab="组织机构">组织机构</button><button class="tab-btn " data-tab="单位档案">单位档案</button><button class="tab-btn " data-tab="舰艇装备">舰艇与装备</button><button class="tab-btn " data-tab="基地保障">基地与保障</button><button class="tab-btn " data-tab="建军项目">建军项目</button><button class="tab-btn " data-tab="战略任务">战略任务</button><button class="tab-btn " data-tab="综合检索">综合检索</button></nav>
<div id="runtimeError" class="runtime-error" hidden></div>
<section id="staticFallback">
<div class="hero">
<div><h2 class="hero-title">海上力量知识数据一体化展示</h2><p class="hero-desc">页面正在载入完整知识数据。即使浏览器限制脚本,下面仍会显示知识库概况和主要入口,不会出现空白页面。</p><div class="hero-tags"><span class="chip">公开来源</span><span class="chip">415个知识条目</span><span class="chip">Markdown全文接入</span><span class="chip">红白业务界面</span></div></div>
<div class="hero-panel"><div class="mini-stat"><b>415</b><span>知识条目</span></div><div class="mini-stat"><b>351</b><span>实体关系</span></div><div class="mini-stat"><b>159</b><span>组织与单位</span></div><div class="mini-stat"><b>135</b><span>舰艇档案</span></div></div>
</div>
<div class="fallback-alert">若页面始终停留在此静态概览,说明当前预览环境禁止执行本地脚本。下载本页面或完整包后,用 Chrome、Edge 等浏览器打开即可使用全部查询、组织图、弹框和二级详情功能。</div>
<div class="fallback-grid">
<div class="fallback-card"><b>159</b><span>组织与单位</span></div><div class="fallback-card"><b>38</b><span>舰级档案</span></div><div class="fallback-card"><b>97</b><span>单舰履历</span></div><div class="fallback-card"><b>37</b><span>非舰艇装备</span></div>
<div class="fallback-card"><b>12</b><span>战略环境</span></div><div class="fallback-card"><b>23</b><span>基地与保障</span></div><div class="fallback-card"><b>20</b><span>建军项目</span></div><div class="fallback-card"><b>25</b><span>战略任务</span></div>
</div>
<div class="fallback-section"><h3>主要知识入口</h3><div class="fallback-list"><div>组织机构:海军、海巡双根组织架构及单位关系</div><div>单位档案:沿革、任务、装备、训练、部署和保障</div><div>舰艇装备:舰级共性、单舰履历和非舰艇装备</div><div>基地保障:军港、外岛、船厂、维修和韧性体系</div><div>建军项目:潜艇、轻巡、岸置反舰、无人和海巡造舰</div><div>战略任务:平时、危机、战时和恢复阶段任务链</div></div></div>
</section>
<main class="content">
<section class="tab-page active" data-tab="总览" id="page-overview"></section>
<section class="tab-page" data-tab="组织机构" id="page-org"></section>
<section class="tab-page" data-tab="单位档案" id="page-units"></section>
<section class="tab-page" data-tab="舰艇装备" id="page-equipment"></section>
<section class="tab-page" data-tab="基地保障" id="page-bases"></section>
<section class="tab-page" data-tab="建军项目" id="page-projects"></section>
<section class="tab-page" data-tab="战略任务" id="page-tasks"></section>
<section class="tab-page" data-tab="综合检索" id="page-search"></section>
</main>
</div>
<div class="drawer-mask" id="drawerMask"><aside class="drawer"><button class="close-btn" id="drawerClose">×</button><div class="drawer-head" id="drawerHead"></div><div class="drawer-body" id="drawerBody"></div><div class="drawer-actions"><button class="btn" id="drawerCancel">关闭</button><button class="btn primary" id="fullDetailBtn">查看完整档案</button></div></aside></div>
<div class="modal-mask" id="modalMask"><article class="modal-page"><div class="modal-hero" id="modalHero"><button class="close-btn" id="modalClose">×</button></div><div class="modal-tabs" id="modalTabs"></div><div class="modal-content" id="modalContent"></div></article></div>
<script type="application/json" id="pageConfigData">{"系统信息":{"系统名称":"台湾海上作战力量知识展示系统","副标题":"公开来源知识图谱与档案检索平台","版本":"1.2","数据版本":"LLM WIKI v4.0 全文稳健版","语言":"中文","默认标签页":"总览"},"主题":{"主色":"#b4232d","深色":"#7f111b","浅色":"#fff4f4","背景":"#f7f3f3","卡片":"#ffffff","边框":"#ecd6d8","正文":"#312b2c","辅助文字":"#7a6b6d","圆角":"14px","阴影":"0 10px 30px rgba(98, 24, 33, .08)"},"全局交互":{"支持标签页切换":true,"支持全局检索":true,"支持关键词高亮":true,"支持卡片表格切换":true,"支持详情侧栏":true,"支持完整二级详情页":true,"支持组织节点展开收起":true,"支持关联跳转":true,"支持状态与类型筛选":true,"默认分页条数":12,"支持Markdown原文全文展示":true,"支持原文目录导航":true,"支持结构化档案全量展示":true,"支持时间线式事件展示":true},"标签页":[{"标识":"总览","名称":"总览","布局":"仪表盘","数据源":["统计数据","战略环境","建军项目","战略任务"],"区块":["顶部摘要","统计卡片","知识域覆盖","体系概览","重点项目","任务入口","数据质量说明"]},{"标识":"组织机构","名称":"组织机构","布局":"组织架构图+详情侧栏","数据源":["组织树","单位档案","关系数据"],"筛选":["所属体系","单位层级","单位类型","关系类型"],"详情方式":["右侧摘要","全屏二级详情"]},{"标识":"单位档案","名称":"单位档案","布局":"卡片/表格","数据源":["单位档案"],"筛选":["单位名称","所属体系","单位类型","单位层级","公开区域","可信度"],"详情方式":["详情侧栏","全屏二级详情"]},{"标识":"舰艇装备","名称":"舰艇与装备","布局":"三级子标签","子标签":["舰级档案","单舰履历","非舰艇装备"],"筛选":["名称","状态","平台类别","装备类别","使用方"],"详情方式":["详情弹框","全屏二级详情"]},{"标识":"基地保障","名称":"基地与保障","布局":"专题卡片+关系面板","数据源":["基地与保障专题","单位关系"],"筛选":["关键词","基地类型","关联单位"],"详情方式":["专题详情页"]},{"标识":"建军项目","名称":"建军项目","布局":"项目卡片+时间线","数据源":["建军项目专题"],"筛选":["项目名称","状态关键词","关联单位装备"],"详情方式":["项目详情页"]},{"标识":"战略任务","名称":"战略任务","布局":"任务卡片+任务链","数据源":["战略任务专题","关系数据"],"筛选":["任务名称","任务阶段","主责单位"],"详情方式":["任务详情页"]},{"标识":"综合检索","名称":"综合检索","布局":"跨库检索","数据源":["全部知识条目"],"筛选":["关键词","知识类别","状态","可信度"],"详情方式":["结果预览","完整详情"]}],"详情页模板":{"单位档案":["页头摘要","基本信息","沿革与组织","任务与能力","装备与保障","活动与事件","关联关系","资料与边界"],"舰级档案":["页头摘要","概览","动力与机动","感知与通信","武器与载荷","发展与差异","关联单舰","资料来源"],"单舰履历":["页头摘要","基本信息","生命周期","训练与行动","关联舰级","资料来源"],"装备档案":["页头摘要","基本信息","技术参数","任务与编配","人员与保障","能力评估","关联关系","资料来源"],"专题档案":["页头摘要","知识正文","关联实体","资料状态"],"统一增强详情":["完整档案","知识原文","关联关系","资料与证据","左侧章节导航","右侧信息摘要","关联实体跳转"]},"中文状态":{"现役":"正常","有限现役":"注意","测试中":"测试","在建":"建设","规划中":"规划","转型中":"转型","历史":"历史","待核":"待核"}}</script>
<script type="application/json" id="appData">{"元数据":{"名称":"台湾海上作战力量前端展示数据(单文件稳健版)","版本":"1.2","数据来源版本":"LLM WIKI v4.0 原子知识页","更新时间":"2026-06-24","原文接入":true},"统计数据":{"知识条目":415,"组织与单位":159,"舰级档案":38,"单舰履历":97,"非舰艇装备":37,"战略环境":12,"基地与保障":23,"建军项目":20,"战略任务":25,"关系数据":351,"方法规范":4},"组织树":[{"编号":"NAVY-HQ","名称":"海军司令部","单位类型":"军种司令部","单位层级":"军种级","所属体系":"海军","子节点":[{"编号":"NAVY-HQ-COMPTROLLER","名称":"主计处","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-HQ-PERSONNEL","名称":"人事军务处","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-HQ-INTEL","名称":"军事情报处","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-RECRUIT-SOUTH","名称":"南区人才招募中心","单位类型":"人才招募单位","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"NAVY-HQ-LOG","名称":"后勤处","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-HQ-READINESS","名称":"战备训练处","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-HQ-COMBAT-SYS","名称":"战斗系统处","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-HQ-POLWAR","名称":"政治作战室","单位类型":"幕僚单位","单位层级":"机关幕僚","所属体系":"海军","子节点":[]},{"编号":"NAVY-ETD-CMD","名称":"教育训练暨准则发展指挥部","单位类型":"指挥部","单位层级":"一级或二级指挥机关","所属体系":"海军","子节点":[{"编号":"TRG-NAVY-SPEC","名称":"海军专长训练中心","单位类型":"训练中心","单位层级":"专业训练机构","所属体系":"海军","子节点":[]},{"编号":"TRG-NAVY-RECRUIT","名称":"海军新兵训练中心","单位类型":"训练中心","单位层级":"专业训练机构","所属体系":"海军","子节点":[]},{"编号":"TRG-MARINE-SPEC","名称":"海军陆战队专长训练中心","单位类型":"训练中心","单位层级":"专业训练机构","所属体系":"海军","子节点":[]},{"编号":"TRG-MARINE-RECRUIT","名称":"海军陆战队新兵训练中心","单位类型":"训练中心","单位层级":"专业训练机构","所属体系":"海军","子节点":[]}]},{"编号":"NAVY-MAINT-CMD","名称":"海军保修指挥部","单位类型":"指挥部","单位层级":"一级或二级指挥机关","所属体系":"海军","子节点":[{"编号":"MAINT-KEELUNG","名称":"基隆后勤支援指挥部","单位类型":"后勤支援指挥部","单位层级":"区域后勤单位","所属体系":"海军","子节点":[]},{"编号":"MAINT-ZUOYING","名称":"左营后勤支援指挥部","单位类型":"后勤支援指挥部","单位层级":"区域后勤单位","所属体系":"海军","子节点":[]},{"编号":"MAINT-COMBAT-SYS-FACTORY","名称":"海军战斗系统工厂","单位类型":"工厂","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"MAINT-SUPPLY-DEPOT","名称":"海军补给总库","单位类型":"库","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"MAINT-SUAO","名称":"苏澳后勤支援指挥部","单位类型":"后勤支援指挥部","单位层级":"区域后勤单位","所属体系":"海军","子节点":[]},{"编号":"MAINT-MAGONG","名称":"马公后勤支援指挥部","单位类型":"后勤支援指挥部","单位层级":"区域后勤单位","所属体系":"海军","子节点":[]}]},{"编号":"NAVY-ACADEMY","名称":"海军军官学校","单位类型":"学校","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"NAVY-SERVICE-GRP","名称":"海军勤务大队","单位类型":"大队","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"NAVY-MEDICAL-OFFICE","名称":"海军司令部医务所","单位类型":"直属支援单位","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"NAVY-OCEAN-BUREAU","名称":"海军大气海洋局","单位类型":"局","单位层级":"专业或基层单位","所属体系":"海军","子节点":[]},{"编号":"NAVY-FLEET-CMD","名称":"海军舰队指挥部","单位类型":"指挥部","单位层级":"一级或二级指挥机关","所属体系":"海军","子节点":[{"编号":"NAVY-131","名称":"一三一舰队","单位类型":"舰队","单位层级":"作战舰队","所属体系":"海军","子节点":[]},{"编号":"NAVY-19Line truncated
<script>
window.__PAGE_CONFIG__=JSON.parse(document.getElementById('pageConfigData').textContent);
window.__APP_DATA__=JSON.parse(document.getElementById('appData').textContent);
const C=window.__PAGE_CONFIG__, D=window.__APP_DATA__;
const state={tab:'总览',selected:null,unitView:'card',unitPage:1,equipSub:'舰级档案',equipPage:1,searchQuery:'',searchTypes:new Set()};
const esc=s=>String(s??'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));
const flat=v=>{if(v==null)return'';if(Array.isArray(v))return v.map(flat).filter(Boolean).join(';');if(typeof v==='object'){if('值'in v)return flat(v.值);return Object.entries(v).map(([k,x])=>`${k}:${flat(x)}`).filter(x=>!x.endsWith(':')).join(';')}return String(v)};
const list=v=>{if(v==null||v==='')return[];if(Array.isArray(v))return v.flatMap(list).filter(Boolean);if(typeof v==='object'&&'值'in v)return list(v.值);return [String(v)]};
const getAll=()=>['单位档案','舰级档案','单舰履历','非舰艇装备','战略环境','基地与保障','建军项目','战略任务','方法规范'].flatMap(k=>D[k]||[]);
const recById=id=>getAll().find(x=>x.编号===id);
const pageName={总览:'总览',组织机构:'组织机构',单位档案:'单位档案',舰艇装备:'舰艇与装备',基地保障:'基地与保障',建军项目:'建军项目',战略任务:'战略任务',综合检索:'综合检索'};
function switchTab(tab){state.tab=tab;document.querySelectorAll('.tab-btn').forEach(b=>b.classList.toggle('active',b.dataset.tab===tab));document.querySelectorAll('.tab-page').forEach(p=>p.classList.toggle('active',p.dataset.tab===tab));if(tab==='综合检索')setTimeout(()=>document.getElementById('searchInput')?.focus(),80);location.hash=encodeURIComponent(tab)}
function renderTabbar(){document.getElementById('tabbar').innerHTML=C.标签页.map(x=>`<button class="tab-btn ${x.标识===state.tab?'active':''}" data-tab="${x.标识}">${x.名称}</button>`).join('');document.querySelectorAll('.tab-btn').forEach(b=>b.onclick=()=>switchTab(b.dataset.tab))}
function statusClass(s){return /现役|已交装/.test(s)?'ok':/在建|测试|转型/.test(s)?'warn':'muted'}
function recordCard(r){const m=r.知识原文?.统计||{};return `<article class="record-card" data-id="${esc(r.编号)}"><div class="record-top"><h4 class="record-title">${esc(r.名称)}</h4><span class="status">${esc(r.状态||r.类别)}</span></div><div class="record-meta">${(r.标签||[]).slice(0,4).map(x=>`<span class="meta-pill">${esc(x)}</span>`).join('')}</div><div class="record-desc">${esc(r.摘要||'暂无摘要')}</div><div class="record-meta"><span class="meta-pill">${m.正文字符数||0}字</span><span class="meta-pill">${m.章节数||Object.keys(r.详情||{}).length}章节</span><span class="meta-pill">${(r.关联关系||[]).length}项关联</span></div><div class="record-foot"><span class="confidence">可信度:${esc(r.可信度||'待核')}</span><button class="link-btn" data-open="${esc(r.编号)}">查看完整知识 →</button></div></article>`}
function renderCards(el,records,page=1,pageSize=12){const start=(page-1)*pageSize,items=records.slice(start,start+pageSize);el.innerHTML=items.length?`<div class="card-grid">${items.map(recordCard).join('')}</div>${pager(records.length,page,pageSize)}`:`<div class="empty-state">未找到符合条件的数据</div>`;bindOpen(el)}
function pager(total,page,size){const pages=Math.ceil(total/size);if(pages<=1)return'';let btns='';for(let i=Math.max(1,page-2);i<=Math.min(pages,page+2);i++)btns+=`<button class="${i===page?'active':''}" data-page="${i}">${i}</button>`;return `<div class="pager"><button data-page="${Math.max(1,page-1)}">上一页</button>${btns}<button data-page="${Math.min(pages,page+1)}">下一页</button><span>共 ${total} 条</span></div>`}
function bindOpen(root=document){root.querySelectorAll('[data-open]').forEach(b=>b.onclick=e=>{e.stopPropagation();openDrawer(b.dataset.open)});root.querySelectorAll('.record-card').forEach(c=>c.onclick=()=>openDrawer(c.dataset.id))}
function prettyValue(v){if(v==null||v===''||(Array.isArray(v)&&!v.length))return'<span class="unknown">公开资料未确认</span>';if(typeof v==='object'&&!Array.isArray(v)&&'值'in v){const vals=list(v.值);return `${vals.length?`<ul>${vals.map(x=>`<li>${esc(x)}</li>`).join('')}</ul>`:'<span class="unknown">公开资料未确认</span>'}${v.说明?`<div class="notice">${esc(v.说明)}</div>`:''}`};if(Array.isArray(v))return v.length?`<ul>${v.map(x=>`<li>${esc(flat(x))}</li>`).join('')}</ul>`:'<span class="unknown">公开资料未确认</span>';if(typeof v==='object')return `<div class="detail-grid">${Object.entries(v).map(([k,x])=>`<div class="detail-block"><h4>${esc(k)}</h4><div class="detail-value">${prettyValue(x)}</div></div>`).join('')}</div>`;return esc(v)}
function isEmptyValue(v){return v==null||v===''||(Array.isArray(v)&&v.length===0)||(typeof v==='object'&&!Array.isArray(v)&&Object.keys(v).length===0)}
function meaningfulEntries(obj,limit=8){const out=[];const walk=(v,prefix='')=>{if(out.length>=limit||v==null)return;if(typeof v==='object'&&!Array.isArray(v)&&'值'in v){if(!isEmptyValue(v.值))out.push([prefix,v]);return}if(Array.isArray(v)){if(v.length)out.push([prefix,v]);return}if(typeof v==='object'){for(const [k,x] of Object.entries(v)){walk(x,prefix?`${prefix} / ${k}`:k);if(out.length>=limit)break}}else if(String(v).trim())out.push([prefix,v])};walk(obj);return out}
function renderPreviewValue(v){if(v&&typeof v==='object'&&!Array.isArray(v)&&'值'in v)v=v.值;if(Array.isArray(v))return `<ul class="preview-list">${v.slice(0,6).map(x=>`<li>${esc(flat(x))}</li>`).join('')}</ul>`;return `<div>${esc(shortText(flat(v),420))}</div>`}
function openDrawer(id){const r=recById(id);if(!r)return;state.selected=r;const m=r.知识原文?.统计||{};document.getElementById('drawerHead').innerHTML=`<h3>${esc(r.名称)}</h3><p>${esc(r.类别)} · ${esc(r.状态||'状态待核')} · 可信度 ${esc(r.可信度||'待核')}</p>`;const entries=meaningfulEntries(r.详情||{},7);document.getElementById('drawerBody').innerHTML=`<p class="drawer-summary">${esc(r.知识原文?.一句话浓缩||r.摘要||'')}</p><div class="drawer-metrics"><div class="drawer-metric"><b>${m.正文字符数||0}</b><span>原文字数</span></div><div class="drawer-metric"><b>${m.章节数||Object.keys(r.详情||{}).length}</b><span>知识章节</span></div><div class="drawer-metric"><b>${(r.关联关系||[]).length}</b><span>关联实体</span></div><div class="drawer-metric"><b>${esc(r.可信度||'待核')}</b><span>可信度</span></div></div><section class="drawer-section"><h4>核心档案摘要 <span class="original-badge">已接入MD原文</span></h4><div class="mini-content">${entries.map(([k,v])=>`<div class="field-card" style="margin-bottom:8px"><h4>${esc(k)}</h4><div class="field-value">${renderPreviewValue(v)}</div></div>`).join('')||'<span class="unknown">暂无结构化字段</span>'}</div></section><section class="drawer-section"><h4>原文章节</h4><div class="value-chips">${(r.知识原文?.目录||[]).filter(x=>x.层级===2).slice(0,12).map(x=>`<span class="value-chip">${esc(x.标题)}</span>`).join('')||'<span class="unknown">暂无目录</span>'}</div></section><section class="drawer-section"><h4>关联关系</h4><div class="relation-list">${(r.关联关系||[]).slice(0,12).map(x=>`<button class="aside-rel" data-open="${esc(x.编号)}">${esc(x.关系)} · ${esc(x.名称)}</button>`).join('')||'<span class="unknown">暂无结构化关系</span>'}</div></section><section class="drawer-section"><h4>来源文件</h4><div class="source-path">${esc(r.来源路径||r.知识原文?.来源文件||'未记录')}</div></section>`;document.getElementById('drawerMask').classList.add('open');bindOpen(document.getElementById('drawerBody'))}
function shortText(s,n){s=String(s||'');return s.length>n?s.slice(0,n)+'…':s}
function closeDrawer(){document.getElementById('drawerMask').classList.remove('open')}
function valueLooksTimeline(key,v){return /历史|时间|演习|部署|事件|事故|维修|改装|更新|里程碑|训练活动/.test(key)&&Array.isArray(v)&&v.length>0}
function renderRichValue(v,key=''){if(v==null||v===''||(Array.isArray(v)&&!v.length))return '<span class="unknown">公开资料未确认</span>';if(typeof v==='object'&&!Array.isArray(v)&&'值'in v){const stateLabel=v.状态?`<span class="evidence-state">${esc(v.状态)}</span>`:'';return `${renderRichValue(v.值,key)}${stateLabel}${v.说明?`<div class="evidence-note">${esc(v.说明)}</div>`:''}`};if(valueLooksTimeline(key,v))return `<div class="timeline">${v.map(x=>`<div class="timeline-item">${esc(flat(x))}</div>`).join('')}</div>`;if(Array.isArray(v)){const vals=v.map(flat).filter(Boolean);const chipMode=vals.length<=12&&vals.every(x=>x.length<=32);return chipMode?`<div class="value-chips">${vals.map(x=>`<span class="value-chip">${esc(x)}</span>`).join('')}</div>`:`<ul>${vals.map(x=>`<li>${esc(x)}</li>`).join('')}</ul>`}if(typeof v==='object')return `<div class="field-grid">${Object.entries(v).map(([k,x])=>fieldCard(k,x)).join('')}</div>`;const s=String(v);return s.includes('\n')?s.split(/\n+/).map(x=>`<p>${esc(x)}</p>`).join(''):esc(s)}
function fieldCard(k,v){const long=Array.isArray(v)&&v.length>4||typeof v==='string'&&v.length>170||typeof v==='object'&&!Array.isArray(v)&&!('值'in v);return `<div class="field-card ${long?'wide':''}"><h4>${esc(k)}</h4><div class="field-value">${renderRichValue(v,k)}</div></div>`}
function structuredModules(r){return Object.entries(r.详情||{}).map(([title,obj],i)=>`<section class="detail-module ${title==='知识正文'?'full':''}" id="struct-${i}"><h3>${esc(title)}</h3>${obj&&typeof obj==='object'&&!Array.isArray(obj)?`<div class="field-grid">${Object.entries(obj).map(([k,v])=>fieldCard(k,v)).join('')}</div>`:`<div class="field-value">${renderRichValue(obj,title)}</div>`}</section>`).join('')||'<div class="empty-state">暂无结构化档案</div>'}
function structuredToc(r){return Object.keys(r.详情||{}).map((t,i)=>`<button class="toc-link" data-scroll="struct-${i}">${esc(t)}</button>`).join('')}
function originalToc(r){return (r.知识原文?.目录||[]).filter(x=>x.层级>=2&&x.层级<=4).map(x=>`<button class="toc-link toc-l${x.层级}" data-scroll="${esc(x.锚点)}">${esc(x.标题)}</button>`).join('')||'<span class="unknown">暂无目录</span>'}
function asideHtml(r){const m=r.知识原文?.统计||{};return `<div class="aside-stat-grid"><div class="aside-stat"><b>${m.正文字符数||0}</b><span>正文字符</span></div><div class="aside-stat"><b>${m.章节数||0}</b><span>原文章节</span></div><div class="aside-stat"><b>${(r.关联关系||[]).length}</b><span>关联关系</span></div><div class="aside-stat"><b>${esc(r.可信度||'待核')}</b><span>可信度</span></div></div><div class="aside-group"><h5>档案状态</h5><div class="value-chips"><span class="value-chip">${esc(r.类别)}</span><span class="value-chip">${esc(r.状态||'待核')}</span><span class="value-chip">${esc(r.更新时间||'')}</span></div></div><div class="aside-group"><h5>快速关联</h5><div class="aside-list">${(r.关联关系||[]).slice(0,12).map(x=>`<button class="aside-rel" data-open="${esc(x.编号)}"><b>${esc(x.关系)}</b><br>${esc(x.名称)}</button>`).join('')||'<span class="unknown">暂无关联</span>'}</div></div><div class="aside-group"><h5>来源文件</h5><div class="source-path">${esc(r.来源路径||'未记录')}</div></div>`}
function relationHtml(r){const grouped={};(r.关联关系||[]).forEach(x=>(grouped[x.关系]??=[]).push(x));return `<div style="padding:20px"><div class="relation-groups">${Object.entries(grouped).map(([rel,items])=>`<section class="relation-group"><h3>${esc(rel)} <span class="evidence-state">${items.length}</span></h3><div class="relation-cards">${items.map(x=>`<article class="relation-card" data-open="${esc(x.编号)}"><b>${esc(x.名称)}</b><span>${esc(x.方向)} · ${esc(x.编号)}</span></article>`).join('')}</div></section>`).join('')||'<div class="empty-state">暂无结构化关系</div>'}</div></div>`}
function evidenceHtml(r){const m=r.知识原文?.统计||{};const gaps=[];const walk=(v,k='')=>{if(v==null)return;if(Array.isArray(v)){if(/缺口|不确定|局限/.test(k))gaps.push(...v.map(flat));return}if(typeof v==='object'){for(const [a,b] of Object.entries(v))walk(b,a)}};walk(r.详情||{});return `<div class="evidence-page"><section class="evidence-panel"><h3>档案元数据</h3><div class="field-grid">${fieldCard('知识编号',r.编号)}${fieldCard('知识类别',r.类别)}${fieldCard('当前状态',r.状态||'待核')}${fieldCard('可信度',r.可信度||'待核')}${fieldCard('更新时间',r.更新时间||'未记录')}${fieldCard('来源路径',r.来源路径||'未记录')}</div></section><section class="evidence-panel"><h3>原文覆盖统计</h3><div class="field-grid">${Object.entries(m).map(([k,v])=>fieldCard(k,v)).join('')}</div></section><section class="evidence-panel wide"><h3>信息缺口与使用边界</h3>${gaps.length?`<ul>${[...new Set(gaps)].map(x=>`<li>${esc(x)}</li>`).join('')}</ul>`:'<div class="notice">未在结构化字段中提取到单独的信息缺口;仍需遵循不展示实时部署、精确阵位、库存和值班规律的边界。</div>'}</section><section class="evidence-panel wide"><h3>Markdown原文文件</h3><div class="source-path">${esc(r.知识原文?.来源文件||r.来源路径||'未记录')}</div><p>完整原文已嵌入“知识原文”标签,不再只展示摘要字段。</p></section></div>`}
function activateModalTab(key){document.querySelectorAll('.modal-tab').forEach(x=>x.classList.toggle('active',x.dataset.i===key));document.querySelectorAll('.detail-section').forEach(x=>x.classList.toggle('active',x.dataset.i===key))}
function bindScrollLinks(root=document){root.querySelectorAll('[data-scroll]').forEach(b=>b.onclick=()=>{const el=document.getElementById(b.dataset.scroll);el?.scrollIntoView({behavior:'smooth',block:'start'})})}
function openModal(r=state.selected){if(!r)return;closeDrawer();state.selected=r;const m=r.知识原文?.统计||{};document.getElementById('modalHero').innerHTML=`<button class="close-btn" id="modalClose2">×</button><div class="modal-hero-grid"><div><h2>${esc(r.名称)}</h2><p>${esc(r.知识原文?.一句话浓缩||r.摘要||'')}</p><div class="hero-tags">${(r.标签||[]).map(x=>`<span class="chip">${esc(x)}</span>`).join('')}<span class="chip">可信度 ${esc(r.可信度||'待核')}</span><span class="original-badge">Markdown原文已接入</span></div></div><div class="modal-hero-stats"><div class="hero-stat"><b>${m.正文字符数||0}</b><span>正文字符</span></div><div class="hero-stat"><b>${m.章节数||Object.keys(r.详情||{}).length}</b><span>知识章节</span></div><div class="hero-stat"><b>${(r.关联关系||[]).length}</b><span>关联实体</span></div><div class="hero-stat"><b>${esc(r.状态||'待核')}</b><span>当前状态</span></div></div></div>`;document.getElementById('modalTabs').innerHTML=`<button class="modal-tab active" data-i="structured">完整档案</button><button class="modal-tab" data-i="original">知识原文</button><button class="modal-tab" data-i="relations">关联关系</button><button class="modal-tab" data-i="evidence">资料与证据</button>`;document.getElementById('modalContent').innerHTML=`<section class="detail-section active" data-i="structured"><div class="detail-layout"><nav class="detail-nav"><h4>档案目录</h4>${structuredToc(r)}</nav><main class="detail-main"><div class="detail-dashboard">${structuredModules(r)}</div></main><aside class="detail-aside"><h4>档案摘要</h4>${asideHtml(r)}</aside></div></section><section class="detail-section" data-i="original"><div class="detail-layout"><nav class="detail-nav"><h4>原文目录</h4>${originalToc(r)}</nav><main class="detail-main"><article class="markdown-shell"><div class="markdown-content">${r.知识原文?.HTML||'<div class="empty-state">未接入Markdown原文</div>'}</div></article></main><aside class="detail-aside"><h4>原文信息</h4>${asideHtml(r)}</aside></div></section><section class="detail-section" data-i="relations">${relationHtml(r)}</section><section class="detail-section" data-i="evidence">${evidenceHtml(r)}</section>`;document.getElementById('modalMask').classList.add('open');document.getElementById('modalClose2').onclick=closeModal;document.querySelectorAll('.modal-tab').forEach(b=>b.onclick=()=>activateModalTab(b.dataset.i));bindOpen(document.getElementById('modalContent'));bindScrollLinks(document.getElementById('modalContent'))}
function renderSection(obj){if(obj&&typeof obj==='object'&&!Array.isArray(obj)&&Object.keys(obj).length===1&&'内容'in obj)return `<div class="markdown-content">${simpleMarkdown(obj.内容)}</div>`;if(obj&&typeof obj==='object'&&!Array.isArray(obj))return `<div class="detail-grid">${Object.entries(obj).map(([k,v])=>`<div class="detail-block"><h4>${esc(k)}</h4><div class="detail-value">${prettyValue(v)}</div></div>`).join('')}</div>`;return `<div class="detail-block"><div class="detail-value">${prettyValue(obj)}</div></div>`}
function simpleMarkdown(md){let s=esc(md||'');s=s.replace(/^### (.*)$/gm,'<h3>$1</h3>').replace(/^## (.*)$/gm,'<h2>$1</h2>').replace(/^# (.*)$/gm,'<h1>$1</h1>');s=s.replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>').replace(/`([^`]+)`/g,'<code>$1</code>');s=s.replace(/^[-*] (.*)$/gm,'<li>$1</li>').replace(/(?:<li>.*<\/li>\n?)+/g,m=>`<ul>${m}</ul>`);s=s.replace(/\n\n/g,'</p><p>').replace(/\n/g,'<br>');return `<p>${s}</p>`}
function closeModal(){document.getElementById('modalMask').classList.remove('open')}
function overview(){const s=D.统计数据;const domains=[['组织与单位',s.组织与单位],['舰级档案',s.舰级档案],['单舰履历',s.单舰履历],['非舰艇装备',s.非舰艇装备],['基地与保障',s.基地与保障],['建军项目',s.建军项目],['战略任务',s.战略任务]];const max=Math.max(...domains.map(x=>x[1]));document.getElementById('page-overview').innerHTML=`<div class="hero"><div><h2 class="hero-title">海上力量知识数据一体化展示</h2><p class="hero-desc">基于统一清单、单位档案、舰级与单舰履历、装备、基地、建军项目、战略任务和关系图谱,提供中文化、多标签、可查询、可钻取的业务展示。</p><div class="hero-tags"><span class="chip">公开来源</span><span class="chip">知识图谱</span><span class="chip">单位与舰艇双层档案</span><span class="chip">数据版本 v4.0 全文版</span></div></div><div class="hero-panel"><div class="mini-stat"><b>${s.知识条目}</b><span>知识条目</span></div><div class="mini-stat"><b>${s.关系数据}</b><span>实体关系</span></div><div class="mini-stat"><b>${s.组织与单位}</b><span>组织与单位</span></div><div class="mini-stat"><b>${s.舰级档案+s.单舰履历}</b><span>舰艇档案</span></div></div></div><div class="stat-grid">${domains.slice(0,5).map(([n,v])=>`<div class="stat-card"><strong>${v}</strong><span>${n}</span></div>`).join('')}</div><div class="grid-2"><div class="panel"><div class="panel-title"><h3>知识域覆盖</h3><small>条目数量</small></div><div class="domain-bars">${domains.map(([n,v])=>`<div class="bar-row"><span>${n}</span><div class="bar-track"><div class="bar-fill" style="width:${Math.max(6,v/max*100)}%"></div></div><b>${v}</b></div>`).join('')}</div></div><div class="panel"><div class="panel-title"><h3>组织体系</h3><button class="btn soft" onclick="switchTab('组织机构')">查看组织图</button></div><div class="grid-2"><div class="system-card"><h4>海军体系</h4><p>以海军司令部为根,覆盖舰队、陆战队、训练、保修、通信、监侦和造船发展。</p><div class="system-list"><span class="chip">舰队指挥</span><span class="chip">陆战队</span><span class="chip">训练保障</span></div></div><div class="system-card"><h4>海巡体系</h4><p>以海巡署为根,覆盖区域分署、舰队分署、侦防分署、巡防区及岸海侦防单位。</p><div class="system-list"><span class="chip">区域分署</span><span class="chip">巡防区</span><span class="chip">舰队分署</span></div></div></div></div></div><div class="section-title"><h3>重点建军项目</h3><button class="btn" onclick="switchTab('建军项目')">全部项目</button></div><div id="overviewProjects"></div><div class="section-title"><h3>战略任务入口</h3><button class="btn" onclick="switchTab('战略任务')">全部任务</button></div><div id="overviewTasks"></div><div class="notice">数据展示严格区分公开确认、分析归纳和待核信息;不展示实时部署、精确阵位、值班规律、弹药库存和非公开战时授权。</div><div class="footer-note">数据源:台湾海上作战力量 LLM WIKI v4.0(Markdown全文增强)</div>`;renderCards(document.getElementById('overviewProjects'),D.建军项目.slice(0,6),1,6);renderCards(document.getElementById('overviewTasks'),D.战略任务.slice(0,6),1,6)}
function renderOrg(){const types=[...new Set(D.单位档案.map(x=>x.单位类型).filter(Boolean))].sort();document.getElementById('page-org').innerHTML=`<div class="page-head"><div><h2>组织机构</h2><p>以组织树展示海军和海巡的行政层级;点击节点查看单位摘要,可进一步进入完整单位档案。行政隶属、勤务管制与协同关系分开表达。</p></div></div><div class="toolbar"><select class="control" id="orgSystem"><option value="全部">全部体系</option><option>海军</option><option>海军陆战队</option><option>海巡</option></select><select class="control" id="orgType"><option value="全部">全部单位类型</option>${types.map(x=>`<option>${esc(x)}</option>`).join('')}</select><input class="control" id="orgKeyword" placeholder="定位单位名称"><button class="btn soft" id="expandTwo">展开两级</button><button class="btn" id="collapseAll">全部收起</button></div><div class="org-layout"><div class="org-canvas"><ul class="org-tree" id="orgTree"></ul></div><aside class="org-side" id="orgSide"><div class="org-empty">点击组织图中的节点<br>查看单位摘要和关联信息</div></aside></div>`;drawOrg();document.getElementById('orgSystem').onchange=drawOrg;document.getElementById('orgType').onchange=drawOrg;document.getElementById('orgKeyword').oninput=drawOrg;document.getElementById('expandTwo').onclick=()=>{document.querySelectorAll('#orgTree ul').forEach((u,i)=>{if(Number(u.dataset.depth||9)<=2)u.style.display='block'})};document.getElementById('collapseAll').onclick=()=>document.querySelectorAll('#orgTree ul').forEach(u=>u.style.display='none')}
function drawOrg(){const sys=document.getElementById('orgSystem')?.value||'全部',typ=document.getElementById('orgType')?.value||'全部',kw=(document.getElementById('orgKeyword')?.value||'').trim();const filterNode=n=>{const rec=recById(n.编号);const own=(sys==='全部'||rec?.所属体系===sys)&&(typ==='全部'||rec?.单位类型===typ)&&(!kw||n.名称.includes(kw));const kids=(n.子节点||[]).map(filterNode).filter(Boolean);return own||kids.length?{...n,子节点:kids,_match:own}:null};let trees=D.组织树.map(filterNode).filter(Boolean);document.getElementById('orgTree').innerHTML=trees.map(n=>orgHtml(n,0)).join('')||'<li class="empty-state">未找到组织节点</li>';document.querySelectorAll('.expand-btn').forEach(b=>b.onclick=e=>{e.stopPropagation();const ul=b.closest('li').querySelector(':scope > ul');if(ul){const open=ul.style.display!=='none';ul.style.display=open?'none':'block';b.textContent=open?'+':'−'}});document.querySelectorAll('.org-node').forEach(n=>n.onclick=()=>selectOrg(n.dataset.id))}
function orgHtml(n,depth){const kids=n.子节点||[];return `<li><div class="org-node ${n._match?'selected':''}" data-id="${esc(n.编号)}">${kids.length?`<button class="expand-btn">${depth<1?'−':'+'}</button>`:'<span></span>'}<div><div class="org-node-name">${esc(n.名称)}</div><div class="org-node-type">${esc(n.单位类型)} · ${esc(n.单位层级)}</div></div><span class="status">${kids.length} 下级</span></div>${kids.length?`<ul data-depth="${depth+1}" style="display:${depth<1?'block':'none'}">${kids.map(k=>orgHtml(k,depth+1)).join('')}</ul>`:''}</li>`}
function selectOrg(id){const r=recById(id);if(!r)return;document.querySelectorAll('.org-node').forEach(n=>n.classList.toggle('selected',n.dataset.id===id));document.getElementById('orgSide').innerHTML=`<h3>${esc(r.名称)}</h3><div class="kv-grid"><div class="kv-card"><small>单位类型</small><b>${esc(r.单位类型)}</b></div><div class="kv-card"><small>单位层级</small><b>${esc(r.单位层级)}</b></div><div class="kv-card"><small>所属体系</small><b>${esc(r.所属体系)}</b></div><div class="kv-card"><small>可信度</small><b>${esc(r.可信度)}</b></div></div><p class="side-summary">${esc(r.摘要)}</p><div class="relation-list">${(r.关联关系||[]).slice(0,12).map(x=>`<span class="relation-chip">${esc(x.关系)} · ${esc(x.名称)}</span>`).join('')}</div><div style="margin-top:15px;display:flex;gap:8px"><button class="btn soft" onclick="openDrawer('${esc(id)}')">快速查看</button><button class="btn primary" onclick="openModal(recById('${esc(id)}'))">完整档案</button></div>`}
function unitPage(){const systems=[...new Set(D.单位档案.map(x=>x.所属体系).filter(Boolean))].sort(),types=[...new Set(D.单位档案.map(x=>x.单位类型).filter(Boolean))].sort();document.getElementById('page-units').innerHTML=`<div class="page-head"><div><h2>单位档案</h2><p>按统一档案结构查询组织与单位,支持卡片和表格视图;详情页包括沿革、组织、任务、装备、保障、训练、事件和资料边界。</p></div></div><div class="toolbar"><input class="control" id="unitKeyword" placeholder="搜索单位名称或任务"><select class="control" id="unitSystem"><option value="全部">全部体系</option>${systems.map(x=>`<option>${esc(x)}</option>`).join('')}</select><select class="control" id="unitType"><option value="全部">全部类型</option>${types.map(x=>`<option>${esc(x)}</option>`).join('')}</select><select class="control" id="unitConfidence"><option value="全部">全部可信度</option><option>A</option><option>A/B</option><option>B</option><option>C</option></select><div class="spacer"></div><div class="segmented"><button class="seg-btn active" data-v="card">卡片</button><button class="seg-btn" data-v="table">表格</button></div></div><div id="unitCards"></div><div class="table-wrap" id="unitTable"></div>`;['unitKeyword','unitSystem','unitType','unitConfidence'].forEach(id=>document.getElementById(id).oninput=()=>{state.unitPage=1;drawUnits()});document.querySelectorAll('#page-units .seg-btn').forEach(b=>b.onclick=()=>{state.unitView=b.dataset.v;document.querySelectorAll('#page-units .seg-btn').forEach(x=>x.classList.toggle('active',x===b));drawUnits()});drawUnits()}
function filteredUnits(){const kw=(document.getElementById('unitKeyword')?.value||'').trim(),sys=document.getElementById('unitSystem')?.value||'全部',typ=document.getElementById('unitType')?.value||'全部',conf=document.getElementById('unitConfidence')?.value||'全部';return D.单位档案.filter(r=>(!kw||`${r.名称}${r.摘要}${flat(r.详情)}${r.知识原文?.HTML||''}`.includes(kw))&&(sys==='全部'||r.所属体系===sys)&&(typ==='全部'||r.单位类型===typ)&&(conf==='全部'||r.可信度===conf))}
function drawUnits(){const records=filteredUnits(),cards=document.getElementById('unitCards'),table=document.getElementById('unitTable');if(state.unitView==='card'){cards.style.display='block';table.classList.remove('active');renderCards(cards,records,state.unitPage,12);cards.querySelectorAll('[data-page]').forEach(b=>b.onclick=()=>{state.unitPage=Number(b.dataset.page);drawUnits()})}else{cards.style.display='none';table.classList.add('active');table.innerHTML=`<table class="data-table"><thead><tr><th>单位名称</th><th>体系</th><th>单位类型</th><th>层级</th><th>上级单位</th><th>主要摘要</th><th>可信度</th><th>操作</th></tr></thead><tbody>${records.map(r=>`<tr><td><b>${esc(r.名称)}</b></td><td>${esc(r.所属体系)}</td><td>${esc(r.单位类型)}</td><td>${esc(r.单位层级)}</td><td>${esc(r.上级单位||'—')}</td><td>${esc(shortText(r.摘要,90))}</td><td>${esc(r.可信度)}</td><td><button class="link-btn" data-open="${esc(r.编号)}">详情</button></td></tr>`).join('')}</tbody></table>`;bindOpen(table)}}
function equipmentPage(){document.getElementById('page-equipment').innerHTML=`<div class="page-head"><div><h2>舰艇与装备</h2><p>舰级共性、单舰履历和非舰艇装备分层展示,避免将同型舰艇视为状态完全一致。</p></div></div><div class="subtabs"><button class="subtab-btn active" data-sub="舰级档案">舰级档案</button><button class="subtab-btn" data-sub="单舰履历">单舰履历</button><button class="subtab-btn" data-sub="非舰艇装备">非舰艇装备</button></div><div class="toolbar"><input class="control" id="equipKeyword" placeholder="搜索舰级、舰名或装备"><select class="control" id="equipStatus"><option value="全部">全部状态</option><option>现役</option><option>有限现役</option><option>测试中</option><option>在建</option><option>规划中</option><option>待核</option></select><select class="control" id="equipCategory"><option value="全部">全部类别</option></select></div><div id="equipCards"></div>`;document.querySelectorAll('.subtab-btn').forEach(b=>b.onclick=()=>{state.equipSub=b.dataset.sub;state.equipPage=1;document.querySelectorAll('.subtab-btn').forEach(x=>x.classList.toggle('active',x===b));fillEquipCategories();drawEquip()});document.getElementById('equipKeyword').oninput=()=>{state.equipPage=1;drawEquip()};document.getElementById('equipStatus').onchange=()=>{state.equipPage=1;drawEquip()};document.getElementById('equipCategory').onchange=()=>{state.equipPage=1;drawEquip()};fillEquipCategories();drawEquip()}
function equipSource(){return state.equipSub==='舰级档案'?D.舰级档案:state.equipSub==='单舰履历'?D.单舰履历:D.非舰艇装备}
function fillEquipCategories(){const src=equipSource(),key=state.equipSub==='舰级档案'?'平台类别':state.equipSub==='单舰履历'?'舰级':'装备类别';const vals=[...new Set(src.map(x=>x[key]).filter(Boolean))].sort();document.getElementById('equipCategory').innerHTML=`<option value="全部">全部类别</option>${vals.map(x=>`<option>${esc(x)}</option>`).join('')}`}
function drawEquip(){const kw=(document.getElementById('equipKeyword')?.value||'').trim(),st=document.getElementById('equipStatus')?.value||'全部',cat=document.getElementById('equipCategory')?.value||'全部',key=state.equipSub==='舰级档案'?'平台类别':state.equipSub==='单舰履历'?'舰级':'装备类别';const rec=equipSource().filter(r=>(!kw||`${r.名称}${r.摘要}${flat(r.详情)}${r.知识原文?.HTML||''}`.includes(kw))&&(st==='全部'||r.状态===st)&&(cat==='全部'||r[key]===cat));const el=document.getElementById('equipCards');renderCards(el,rec,state.equipPage,12);el.querySelectorAll('[data-page]').forEach(b=>b.onclick=()=>{state.equipPage=Number(b.dataset.page);drawEquip()})}
function thematicPage(target,data,title,desc){document.getElementById(target).innerHTML=`<div class="page-head"><div><h2>${title}</h2><p>${desc}</p></div></div><div class="toolbar"><input class="control" id="${target}Kw" placeholder="输入关键词进行筛选"><div class="spacer"></div><span class="chip">共 ${data.length} 条专题</span></div><div id="${target}Cards"></div>`;const draw=()=>{const kw=(document.getElementById(`${target}Kw`).value||'').trim();renderCards(document.getElementById(`${target}Cards`),data.filter(x=>!kw||`${x.名称}${x.摘要}${flat(x.详情)}${x.知识原文?.HTML||''}`.includes(kw)),1,18)};document.getElementById(`${target}Kw`).oninput=draw;draw()}
function searchPage(){const cats=[...new Set(getAll().map(x=>x.类别))];document.getElementById('page-search').innerHTML=`<div class="page-head"><div><h2>综合检索</h2><p>跨单位、舰级、单舰、装备、环境、基地、项目和任务进行全文检索,支持结果分类与关联跳转。</p></div></div><div class="search-hero"><div class="big-search"><input id="searchInput" placeholder="输入名称、任务、装备、基地或事件关键词"><button class="btn primary" id="searchBtn">检索</button></div><div class="search-groups"><aside class="facet"><h4>知识类别</h4>${cats.map(c=>`<label><input type="checkbox" value="${esc(c)}"> ${esc(c)} <span>(${getAll().filter(x=>x.类别===c).length})</span></label>`).join('')}</aside><div><div class="section-title"><h3 id="searchCount">检索结果</h3></div><div class="result-list" id="searchResults"></div></div></div></div>`;document.getElementById('searchBtn').onclick=drawSearch;document.getElementById('searchInput').onkeydown=e=>{if(e.key==='Enter')drawSearch()};document.querySelectorAll('.facet input').forEach(x=>x.onchange=drawSearch);drawSearch()}
function drawSearch(){const q=(document.getElementById('searchInput')?.value||state.searchQuery||'').trim();state.searchQuery=q;const checked=[...document.querySelectorAll('.facet input:checked')].map(x=>x.value);let rec=getAll().filter(r=>(!checked.length||checked.includes(r.类别))&&(!q||`${r.名称}${r.摘要}${flat(r.详情)}${r.知识原文?.HTML||''}${flat(r.关联关系)}`.toLowerCase().includes(q.toLowerCase()))).slice(0,100);document.getElementById('searchCount').textContent=`检索结果(${rec.length}${rec.length===100?'+':''})`;document.getElementById('searchResults').innerHTML=rec.length?rec.map(r=>`<article class="search-result"><div class="record-top"><h4>${highlight(r.名称,q)}</h4><span class="status">${esc(r.类别)}</span></div><p>${highlight(r.摘要||'',q)}</p><div class="record-foot"><div>${(r.标签||[]).slice(0,4).map(x=>`<span class="meta-pill">${esc(x)}</span>`).join('')}</div><button class="link-btn" data-open="${esc(r.编号)}">打开档案 →</button></div></article>`).join(''):'<div class="empty-state">没有检索到匹配内容</div>';bindOpen(document.getElementById('searchResults'))}
function highlight(s,q){s=esc(s);if(!q)return s;const safe=q.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');return s.replace(new RegExp(safe,'ig'),m=>`<span class="mark">${m}</span>`)}
function init(){document.documentElement.dataset.appState='loading';const hash=decodeURIComponent(location.hash.slice(1));if(pageName[hash])state.tab=hash;renderTabbar();overview();renderOrg();unitPage();equipmentPage();thematicPage('page-bases',D.基地与保障,'基地与保障','以专题卡片呈现基地类型、基地体系、港口、船厂、后勤和韧性;详情中通过关系数据跳转关联单位与装备。');thematicPage('page-projects',D.建军项目,'建军项目','以项目专题展示建设目标、阶段、相关单位装备、形成战力条件和风险;复杂内容进入完整详情页。');thematicPage('page-tasks',D.战略任务,'战略任务','按平时、危机、战时和恢复任务展示任务目的、执行链、主责单位、装备基地与成功标准。');searchPage();switchTab(state.tab);document.getElementById('drawerClose').onclick=closeDrawer;document.getElementById('drawerCancel').onclick=closeDrawer;document.getElementById('drawerMask').onclick=e=>{if(e.target.id==='drawerMask')closeDrawer()};document.getElementById('modalMask').onclick=e=>{if(e.target.id==='modalMask')closeModal()};document.getElementById('modalClose').onclick=closeModal;document.getElementById('fullDetailBtn').onclick=()=>openModal();document.getElementById('globalSearch').onkeydown=e=>{if(e.key==='Enter'){state.searchQuery=e.target.value;switchTab('综合检索');document.getElementById('searchInput').value=state.searchQuery;drawSearch()}};document.getElementById('dataInfoBtn').onclick=()=>{const temp={编号:'DATA-INFO',名称:'数据说明',类别:'方法规范',状态:'公开数据',摘要:'本页面使用台湾海上作战力量 LLM WIKI v4.0 的结构化数据与415个Markdown原子知识页构建。',可信度:'A/B',标签:['公开来源','中文展示','不含实时敏感信息'],详情:{'数据范围':{'知识条目':D.统计数据.知识条目,'组织与单位':D.统计数据.组织与单位,'舰艇档案':D.统计数据.舰级档案+D.统计数据.单舰履历,'关系数据':D.统计数据.关系数据},'展示原则':{'原则':['舰级与单舰分层','行政隶属与勤务管制分开','未知信息明确标注','不展示实时部署与精确阵位']}}};state.selected=temp;openDrawerTemp(temp)}}
function openDrawerTemp(r){state.selected=r;document.getElementById('drawerHead').innerHTML=`<h3>${esc(r.名称)}</h3><p>${esc(r.类别)}</p>`;document.getElementById('drawerBody').innerHTML=`<p class="side-summary">${esc(r.摘要)}</p>${Object.entries(r.详情).map(([t,o])=>`<section class="quick-section"><h4>${esc(t)}</h4><p>${esc(shortText(flat(o),500))}</p></section>`).join('')}`;document.getElementById('drawerMask').classList.add('open')}
try{init();document.documentElement.dataset.appState='ready';const fb=document.getElementById('staticFallback');if(fb)fb.style.display='none';}catch(err){document.documentElement.dataset.appState='error';const eb=document.getElementById('runtimeError');if(eb){eb.hidden=false;eb.textContent='页面数据加载失败:'+(err&&err.message?err.message:String(err));}console.error(err);}
</script>
</body>
</html>