Add scheduled HTML templates no19-no27
This commit is contained in:
1 parent
dfb745a5e4
commit
8fbfd35296
10 files changed
+21873
No files matched your search
+63
@@ -267,6 +267,69 @@ TEMPLATES: tuple[TemplateSpec, ...] = (
|
||||
file="no18/no18.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no19",
|
||||
name="no.19",
|
||||
description="",
|
||||
file="no19/no.19.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no20",
|
||||
name="no.20",
|
||||
description="",
|
||||
file="no20/no.20.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no21",
|
||||
name="no.21",
|
||||
description="",
|
||||
file="no21/no.21.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no22",
|
||||
name="no.22",
|
||||
description="",
|
||||
file="no22/no.22.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no23",
|
||||
name="no.23",
|
||||
description="",
|
||||
file="no23/no.23.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no24",
|
||||
name="no.24",
|
||||
description="",
|
||||
file="no24/no.24.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no25",
|
||||
name="no.25",
|
||||
description="",
|
||||
file="no25/no.25.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no26",
|
||||
name="no.26",
|
||||
description="",
|
||||
file="no26/no.26.html",
|
||||
convertible=False,
|
||||
),
|
||||
TemplateSpec(
|
||||
id="no27",
|
||||
name="no.27",
|
||||
description="",
|
||||
file="no27/no.27.html",
|
||||
convertible=False,
|
||||
),
|
||||
)
|
||||
|
||||
_TEMPLATES_BY_ID: dict[str, TemplateSpec] = {t.id: t for t in TEMPLATES}
|
||||
|
||||
+1764
File diff suppressed because it is too large.
Load diff
+4252
File diff suppressed because it is too large.
Load diff
+3833
File diff suppressed because it is too large.
Load diff
+3484
File diff suppressed because it is too large.
Load diff
+2943
File diff suppressed because it is too large.
Load diff
+381
@@ -0,0 +1,381 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>日本涉台介入风险叙事专题研究系统</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #8f1f25;
|
||||
--bg2: #b8323a;
|
||||
--panel: #fff7f3;
|
||||
--card: #ffffff;
|
||||
--ink: #241616;
|
||||
--muted: #76605d;
|
||||
--line: rgba(120, 29, 35, .18);
|
||||
--red: #b7202e;
|
||||
--red-dark: #7b121b;
|
||||
--gold: #f3c969;
|
||||
--soft: #fff2eb;
|
||||
--green: #18724d;
|
||||
--orange: #b35b13;
|
||||
--blue: #275b91;
|
||||
--shadow: 0 18px 50px rgba(58, 9, 15, .18);
|
||||
--radius: 18px;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", "PingFang SC", Arial, sans-serif;
|
||||
background: radial-gradient(circle at 20% 0%, #d94c4c 0, transparent 24%), linear-gradient(140deg, var(--bg), #641018 70%);
|
||||
color: var(--ink);
|
||||
min-height: 100vh;
|
||||
}
|
||||
.app { max-width: 1480px; margin: 0 auto; padding: 28px; }
|
||||
.hero {
|
||||
color: #fff;
|
||||
padding: 24px 26px 22px;
|
||||
border: 1px solid rgba(255,255,255,.18);
|
||||
background: linear-gradient(135deg, rgba(255,255,255,.14), rgba(255,255,255,.06));
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 26px;
|
||||
box-shadow: var(--shadow);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hero::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 420px; height: 420px;
|
||||
border: 1px solid rgba(255,255,255,.12);
|
||||
border-radius: 50%;
|
||||
right: -160px; top: -220px;
|
||||
}
|
||||
.eyebrow { font-size: 13px; letter-spacing: .18em; opacity: .86; margin-bottom: 9px; }
|
||||
h1 { margin: 0; font-size: clamp(28px, 4vw, 48px); line-height: 1.15; }
|
||||
.subtitle { max-width: 920px; margin-top: 12px; color: rgba(255,255,255,.88); line-height: 1.75; }
|
||||
.hero-meta { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 18px; }
|
||||
.pill { display:inline-flex; align-items:center; gap:6px; padding: 7px 11px; border-radius: 999px; background: rgba(255,255,255,.16); color: #fff; border: 1px solid rgba(255,255,255,.18); font-size: 13px; }
|
||||
|
||||
.toolbar {
|
||||
margin: 20px 0 14px;
|
||||
background: var(--panel);
|
||||
border-radius: 22px;
|
||||
padding: 14px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.6fr .85fr .75fr .75fr;
|
||||
gap: 12px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid rgba(255,255,255,.42);
|
||||
}
|
||||
input, select {
|
||||
width: 100%;
|
||||
border: 1px solid var(--line);
|
||||
background: #fff;
|
||||
border-radius: 14px;
|
||||
padding: 12px 13px;
|
||||
font-size: 14px;
|
||||
color: var(--ink);
|
||||
outline: none;
|
||||
}
|
||||
input:focus, select:focus { border-color: rgba(183,32,46,.56); box-shadow: 0 0 0 3px rgba(183,32,46,.1); }
|
||||
.tabs {
|
||||
display: flex; flex-wrap: wrap; gap: 9px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.tab-btn {
|
||||
border: 0;
|
||||
background: rgba(255,255,255,.18);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255,255,255,.2);
|
||||
padding: 10px 13px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
transition: .18s ease;
|
||||
}
|
||||
.tab-btn.active { background: #fff; color: var(--red-dark); box-shadow: 0 10px 26px rgba(0,0,0,.13); }
|
||||
.tab-btn:hover { transform: translateY(-1px); }
|
||||
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border-radius: 26px;
|
||||
padding: 20px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid rgba(255,255,255,.44);
|
||||
min-height: 520px;
|
||||
}
|
||||
.tab-head { display:flex; justify-content:space-between; align-items:flex-start; gap:18px; margin-bottom:16px; }
|
||||
.tab-title { margin: 0; font-size: 25px; color: var(--red-dark); }
|
||||
.tab-desc { margin: 6px 0 0; color: var(--muted); line-height: 1.6; }
|
||||
.count-badge { flex: 0 0 auto; padding: 8px 11px; border-radius: 999px; background:#fff; color: var(--red-dark); border:1px solid var(--line); font-weight:800; }
|
||||
|
||||
.summary-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
|
||||
.summary-card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
padding: 18px;
|
||||
min-height: 144px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.summary-card::after { content:""; position:absolute; right:-38px; top:-42px; width: 110px; height:110px; border-radius:50%; background: rgba(183,32,46,.08); }
|
||||
.summary-card h3 { margin:0 0 8px; color:var(--red-dark); font-size:15px; }
|
||||
.summary-card .value { font-size: 25px; font-weight: 900; margin-bottom: 9px; color: #2b1114; }
|
||||
.summary-card p { margin:0; color:var(--muted); line-height: 1.62; font-size: 14px; }
|
||||
|
||||
.overview-grid { display:grid; grid-template-columns: 1.1fr .9fr; gap: 14px; margin-top: 14px; }
|
||||
.box { background:#fff; border:1px solid var(--line); border-radius:18px; padding:16px; }
|
||||
.box h3 { margin:0 0 12px; color: var(--red-dark); }
|
||||
.bar-row { display:grid; grid-template-columns: 105px 1fr 36px; align-items:center; gap:8px; margin: 9px 0; font-size:13px; color: var(--muted); }
|
||||
.bar { height: 10px; border-radius: 999px; background: #f4ded8; overflow: hidden; }
|
||||
.bar > span { display:block; height:100%; border-radius:999px; background: linear-gradient(90deg, var(--red), var(--gold)); }
|
||||
.risk-list { display:grid; gap:10px; }
|
||||
.risk-mini { border:1px solid var(--line); border-radius:14px; padding:11px; background:#fffdfb; cursor:pointer; }
|
||||
.risk-mini strong { color: var(--red-dark); }
|
||||
|
||||
.cards { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
|
||||
.card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
padding: 16px;
|
||||
display:flex; flex-direction:column; gap: 10px;
|
||||
min-height: 248px;
|
||||
transition: .18s ease;
|
||||
}
|
||||
.card:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(94, 20, 24, .12); }
|
||||
.card-top { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; }
|
||||
.card-id { font-size:12px; color:#fff; background: var(--red-dark); border-radius:999px; padding:4px 8px; font-weight: 800; }
|
||||
.risk { font-size:12px; border-radius:999px; padding:5px 8px; font-weight: 800; white-space: nowrap; }
|
||||
.risk.xh { background: #ffe1de; color:#9b111c; }
|
||||
.risk.h { background: #fff0d5; color:#9a4e00; }
|
||||
.risk.m { background: #e9f2ff; color:#24527d; }
|
||||
.card h3 { margin:0; color:var(--red-dark); font-size:17px; line-height:1.35; }
|
||||
.meta { display:flex; gap:6px; flex-wrap:wrap; }
|
||||
.tag { font-size:12px; color:var(--red-dark); background: var(--soft); border:1px solid rgba(183,32,46,.15); border-radius:999px; padding:4px 8px; }
|
||||
.card p { margin:0; color:var(--muted); line-height: 1.62; font-size: 14px; }
|
||||
.card-footer { margin-top:auto; display:flex; justify-content:space-between; align-items:center; gap:12px; }
|
||||
.evidence { font-size:12px; color:#5e4d49; }
|
||||
.btn {
|
||||
background: var(--red);
|
||||
color:#fff;
|
||||
border:0;
|
||||
border-radius:12px;
|
||||
padding:9px 12px;
|
||||
cursor:pointer;
|
||||
font-weight:800;
|
||||
box-shadow: 0 8px 18px rgba(183,32,46,.22);
|
||||
}
|
||||
.btn.secondary { background:#fff; color:var(--red-dark); border:1px solid var(--line); box-shadow:none; }
|
||||
.empty { padding: 38px; text-align:center; color:var(--muted); background:#fff; border:1px dashed var(--line); border-radius:18px; }
|
||||
|
||||
.keyword-wrap { display:grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap:14px; margin-top:16px; }
|
||||
.keyword-group { background:#fff; border:1px solid var(--line); border-radius:18px; padding:15px; }
|
||||
.keyword-group h3 { margin:0 0 10px; color: var(--red-dark); }
|
||||
.kw { display:inline-block; margin:5px; padding:7px 10px; background:var(--soft); border:1px solid rgba(183,32,46,.18); color:var(--red-dark); border-radius:999px; font-size:13px; }
|
||||
|
||||
.modal-backdrop { display:none; position:fixed; inset:0; background:rgba(45, 7, 10, .62); z-index:1000; padding: 28px; overflow:auto; }
|
||||
.modal-backdrop.open { display:block; }
|
||||
.modal { max-width: 980px; margin: 0 auto; background:#fffaf7; border-radius: 24px; border:1px solid rgba(255,255,255,.45); box-shadow: 0 30px 90px rgba(0,0,0,.34); overflow:hidden; }
|
||||
.modal-head { background: linear-gradient(135deg, var(--red-dark), var(--red)); color:#fff; padding: 20px 22px; display:flex; justify-content:space-between; gap:16px; align-items:flex-start; }
|
||||
.modal-head h2 { margin:0; font-size:24px; }
|
||||
.close { background:rgba(255,255,255,.16); border:1px solid rgba(255,255,255,.25); color:#fff; border-radius:12px; font-size:22px; cursor:pointer; width:42px; height:38px; }
|
||||
.modal-body { padding: 20px 22px 24px; }
|
||||
.detail-grid { display:grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap:10px; margin-bottom:16px; }
|
||||
.detail-cell { background:#fff; border:1px solid var(--line); border-radius:14px; padding:10px; }
|
||||
.detail-cell span { display:block; font-size:12px; color:var(--muted); margin-bottom:4px; }
|
||||
.detail-cell strong { color:var(--red-dark); }
|
||||
.section { background:#fff; border:1px solid var(--line); border-radius:16px; padding:15px; margin:12px 0; }
|
||||
.section h3 { margin:0 0 8px; color:var(--red-dark); }
|
||||
.section p { margin:0; line-height:1.8; color:#3b2929; }
|
||||
|
||||
.footer-note { color:rgba(255,255,255,.78); text-align:center; padding: 16px 0 4px; font-size:13px; }
|
||||
@media (max-width: 1100px) {
|
||||
.toolbar { grid-template-columns: 1fr 1fr; }
|
||||
.summary-grid, .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.overview-grid { grid-template-columns: 1fr; }
|
||||
.detail-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.app { padding: 14px; }
|
||||
.toolbar { grid-template-columns: 1fr; }
|
||||
.summary-grid, .cards, .keyword-wrap { grid-template-columns: 1fr; }
|
||||
.tab-head { flex-direction:column; }
|
||||
.detail-grid { grid-template-columns: 1fr; }
|
||||
.modal-backdrop { padding: 12px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<header class="hero">
|
||||
<div class="eyebrow">NARRATIVE RISK MONITORING DATABASE</div>
|
||||
<h1>日本涉台介入风险叙事专题研究系统</h1>
|
||||
<div class="subtitle">围绕“介入战争日本祸及自身”叙事的事件、人物、组织、制度、地方、媒体、经济与民意数据库</div>
|
||||
<div class="hero-meta">
|
||||
<span class="pill">版本:1.0</span>
|
||||
<span class="pill">更新:2026-06-30</span>
|
||||
<span class="pill">离线可用 · 内嵌数据</span>
|
||||
<span class="pill">Tab切换 · 搜索筛选 · 弹框详情</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="toolbar">
|
||||
<input id="q" placeholder="搜索关键词:高市、先岛、存立危机、冲绳、旅游、认知战……" />
|
||||
<select id="narrativeFilter"><option value="">全部叙事方向</option></select>
|
||||
<select id="riskFilter"><option value="">全部风险等级</option><option value="极高">极高</option><option value="高">高</option><option value="中">中</option><option value="中低">中低</option><option value="低">低</option></select>
|
||||
<select id="evidenceFilter"><option value="">全部证据状态</option><option value="已核实">已核实</option><option value="持续更新">持续更新</option><option value="待采集">待采集</option></select>
|
||||
</section>
|
||||
|
||||
<nav class="tabs" id="tabs"></nav>
|
||||
<main class="panel" id="content"></main>
|
||||
<div class="footer-note">本页面为研究型结构化展示,不替代原始文件核验;高风险条目应回溯国会记录、政府文件、机构公告和媒体原文。</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-backdrop" id="modalBackdrop" onclick="backdropClose(event)">
|
||||
<div class="modal" role="dialog" aria-modal="true">
|
||||
<div class="modal-head">
|
||||
<div>
|
||||
<div id="modalMeta" class="eyebrow"></div>
|
||||
<h2 id="modalTitle"></h2>
|
||||
</div>
|
||||
<button class="close" onclick="closeModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const DATA = {"project": {"title": "日本涉台介入风险叙事专题研究系统", "subtitle": "围绕“介入战争日本祸及自身”叙事的事件、人物、组织、制度、地方、媒体、经济与民意数据库", "version": "1.0", "language": "zh-CN", "last_updated": "2026-06-30", "description": "本数据用于展示日本方向涉台介入风险叙事中的事件、人物、组织、制度、地方、媒体、经济、民意与竞争叙事。"}, "tabs": [{"id": "overview", "name": "总览仪表盘", "dataset": null, "description": "展示专题总风险、叙事分布、热点地区、重点人物和最新事件。"}, {"id": "timeline", "name": "事件时间线", "dataset": "events_timeline", "description": "展示2014年以来日本涉台安全政策、政要表态、地方避难和中日冲突节点。"}, {"id": "actors_orgs", "name": "人物组织", "dataset": ["actors_japan", "organizations"], "description": "展示日本政府、政党、地方政治、专家、媒体和社会组织。"}, {"id": "security_laws", "name": "法律制度", "dataset": "security_laws", "description": "展示存立危机、重要影响、国民保护、日美安保等制度路径。"}, {"id": "local_frontline", "name": "地方前线", "dataset": "local_frontline", "description": "展示冲绳、先岛、石垣、宫古、与那国、九州、山口等地方风险。"}, {"id": "antiwar_networks", "name": "反战护宪网络", "dataset": "antiwar_networks", "description": "展示九条之会、日弁连、冲绳和平运动、地方反导弹组织和工会网络。"}, {"id": "conservative_backlash", "name": "保守反弹", "dataset": "conservative_backlash", "description": "展示威慑、防卫、中国认知战、台湾伙伴等竞争叙事。"}, {"id": "media_platform", "name": "媒体平台", "dataset": "media_platform_samples", "description": "展示全国媒体、地方媒体、YouTube、X、Yahoo、note等传播样本。"}, {"id": "economic_spillover", "name": "经济外溢", "dataset": "economic_spillover", "description": "展示旅游、航空、半导体、供应链、出口管制等风险。"}, {"id": "public_opinion", "name": "民意受众", "dataset": "public_opinion_youth", "description": "展示台湾有事、防卫费、修宪、青年、都市无党派和冲绳地方态度。"}, {"id": "japan_taiwan", "name": "日台关系", "dataset": "japan_taiwan_relations", "description": "展示日台实务窗口、亲台议员网络、地方交流、半导体合作和智库推演。"}, {"id": "corpus_keywords", "name": "语料词库", "dataset": "source_corpus_keywords", "description": "展示原始语料来源、日文关键词、平台监测词和证据状态。"}], "dashboard": {"summary_cards": [{"title": "核心叙事", "value": "介入战争日本祸及自身", "description": "围绕日本涉台介入可能引发战争卷入、地方牺牲、经济反制和社会撕裂的风险叙事。"}, {"title": "数据条目", "value": "164", "description": "覆盖事件、人物、组织、制度、地方、媒体、经济、民意和日台关系。"}, {"title": "高风险地区", "value": "冲绳、先岛、九州", "description": "冲绳本岛、石垣、宫古、与那国、九州和山口是地方风险最集中的区域。"}, {"title": "核心法律入口", "value": "存立危机事态", "description": "台湾有事是否触发日本集体自卫权,是舆论和国会争议核心。"}, {"title": "主要竞争叙事", "value": "威慑才能防止战争", "description": "保守派和安保界常用威慑、防卫、中国威胁等逻辑反击反介入叙事。"}, {"title": "总体风险", "value": "86 / 100", "description": "综合政治触发、地方影响、经济外溢、媒体扩散和反弹强度评估。"}], "narrative_distribution": [{"label": "地方牺牲", "count": 38}, {"label": "护宪反战", "count": 22}, {"label": "威慑防战", "count": 20}, {"label": "经济代价", "count": 18}, {"label": "中性解释", "count": 17}, {"label": "台湾伙伴", "count": 15}, {"label": "战争卷入", "count": 12}, {"label": "专业治理", "count": 12}, {"label": "中国胁迫", "count": 6}, {"label": "中国威胁", "count": 4}], "hotspots": [{"location": "日本", "count": 42}, {"location": "东京", "count": 15}, {"location": "冲绳", "count": 12}, {"location": "日本全国", "count": 9}, {"location": "宫古岛", "count": 5}, {"location": "华盛顿", "count": 4}, {"location": "与那国", "count": 4}, {"location": "石垣", "count": 3}, {"location": "九州/山口", "count": 3}, {"location": "熊本", "count": 3}, {"location": "先岛/九州/山口", "count": 2}, {"location": "中日之间", "count": 2}], "top_risks": [{"id": "E016", "title_cn": "高市早苗称台湾有事可能构成存立危机事态", "category": "首相表态", "summary": "高市早苗在国会答辩中将台湾有事与存立危机事态直接相连。", "detail": "该事件将保守派长期话语推入首相级官Line truncated
|
||||
const state = { activeTab: 'overview' };
|
||||
|
||||
const tabsEl = document.getElementById('tabs');
|
||||
const contentEl = document.getElementById('content');
|
||||
const qEl = document.getElementById('q');
|
||||
const narrativeEl = document.getElementById('narrativeFilter');
|
||||
const riskEl = document.getElementById('riskFilter');
|
||||
const evidenceEl = document.getElementById('evidenceFilter');
|
||||
|
||||
function init() {
|
||||
Object.values(DATA.narrative_labels).forEach(label => {
|
||||
const opt = document.createElement('option'); opt.value = label; opt.textContent = label; narrativeEl.appendChild(opt);
|
||||
});
|
||||
renderTabs();
|
||||
render();
|
||||
[qEl, narrativeEl, riskEl, evidenceEl].forEach(el => el.addEventListener('input', render));
|
||||
}
|
||||
|
||||
function renderTabs() {
|
||||
tabsEl.innerHTML = '';
|
||||
DATA.tabs.forEach(t => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'tab-btn' + (state.activeTab === t.id ? ' active' : '');
|
||||
btn.textContent = t.name;
|
||||
btn.onclick = () => { state.activeTab = t.id; qEl.value=''; renderTabs(); render(); };
|
||||
tabsEl.appendChild(btn);
|
||||
});
|
||||
}
|
||||
|
||||
function getActiveTab() { return DATA.tabs.find(t => t.id === state.activeTab); }
|
||||
function getItems(tab) {
|
||||
if (!tab || !tab.dataset) return [];
|
||||
if (Array.isArray(tab.dataset)) return tab.dataset.flatMap(k => DATA.datasets[k] || []);
|
||||
return DATA.datasets[tab.dataset] || [];
|
||||
}
|
||||
function textOf(x) {
|
||||
return [x.id, x.title_cn, x.category, x.summary, x.detail, x.location, x.narrative_label, ...(x.tags||[])].join(' ').toLowerCase();
|
||||
}
|
||||
function filtered(items) {
|
||||
const q = qEl.value.trim().toLowerCase();
|
||||
const n = narrativeEl.value;
|
||||
const r = riskEl.value;
|
||||
const e = evidenceEl.value;
|
||||
return items.filter(x => (!q || textOf(x).includes(q)) && (!n || x.narrative_label === n) && (!r || x.risk_level === r) && (!e || x.evidence_status === e));
|
||||
}
|
||||
function riskClass(level) { return level === '极高' ? 'xh' : level === '高' ? 'h' : 'm'; }
|
||||
|
||||
function render() {
|
||||
const tab = getActiveTab();
|
||||
if (state.activeTab === 'overview') return renderOverview();
|
||||
const items = filtered(getItems(tab));
|
||||
const keywordBlock = state.activeTab === 'corpus_keywords' ? renderKeywords() : '';
|
||||
contentEl.innerHTML = `
|
||||
<div class="tab-head">
|
||||
<div><h2 class="tab-title">${tab.name}</h2><p class="tab-desc">${tab.description}</p></div>
|
||||
<div class="count-badge">${items.length} 条</div>
|
||||
</div>
|
||||
${items.length ? `<div class="cards">${items.map(card).join('')}</div>` : '<div class="empty">没有符合当前筛选条件的数据。</div>'}
|
||||
${keywordBlock}
|
||||
`;
|
||||
}
|
||||
|
||||
function renderOverview() {
|
||||
const d = DATA.dashboard;
|
||||
const maxNarr = Math.max(...d.narrative_distribution.map(x => x.count));
|
||||
const maxHot = Math.max(...d.hotspots.map(x => x.count));
|
||||
contentEl.innerHTML = `
|
||||
<div class="tab-head">
|
||||
<div><h2 class="tab-title">总览仪表盘</h2><p class="tab-desc">展示专题总风险、叙事分布、热点地区、重点人物和最新事件。可通过上方搜索框对全库进行粗略检索。</p></div>
|
||||
<div class="count-badge">${Object.values(DATA.datasets).filter(Array.isArray).flat().length - DATA.datasets.keywords.length} 条</div>
|
||||
</div>
|
||||
<div class="summary-grid">${d.summary_cards.map(c => `<div class="summary-card"><h3>${c.title}</h3><div class="value">${c.value}</div><p>${c.description}</p></div>`).join('')}</div>
|
||||
<div class="overview-grid">
|
||||
<div class="box"><h3>叙事分布</h3>${d.narrative_distribution.map(x => `<div class="bar-row"><span>${x.label}</span><div class="bar"><span style="width:${Math.max(8, x.count/maxNarr*100)}%"></span></div><b>${x.count}</b></div>`).join('')}</div>
|
||||
<div class="box"><h3>热点地区</h3>${d.hotspots.slice(0,10).map(x => `<div class="bar-row"><span>${x.location}</span><div class="bar"><span style="width:${Math.max(8, x.count/maxHot*100)}%"></span></div><b>${x.count}</b></div>`).join('')}</div>
|
||||
</div>
|
||||
<div class="overview-grid">
|
||||
<div class="box"><h3>高风险条目</h3><div class="risk-list">${d.top_risks.slice(0,6).map(x => `<div class="risk-mini" onclick="openModalById('${x.id}')"><strong>${x.title_cn}</strong><br><span>${x.category} · 风险 ${x.risk_score} · ${x.narrative_label}</span></div>`).join('')}</div></div>
|
||||
<div class="box"><h3>主要风险驱动</h3><div class="meta">${d.risk_drivers.map(x => `<span class="tag">${x}</span>`).join('')}</div><div class="section"><h3>页面使用建议</h3><p>先从“事件时间线”定位触发节点,再到“法律制度”查看制度路径;涉及地方影响时进入“地方前线”和“反战护宪网络”;涉及传播扩散时进入“媒体平台”;涉及成本量化时进入“经济外溢”和“民意受众”。</p></div></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function card(x) {
|
||||
return `<article class="card">
|
||||
<div class="card-top"><span class="card-id">${x.id}</span><span class="risk ${riskClass(x.risk_level)}">${x.risk_level} · ${x.risk_score}</span></div>
|
||||
<h3>${x.title_cn}</h3>
|
||||
<div class="meta"><span class="tag">${x.category}</span><span class="tag">${x.narrative_label}</span><span class="tag">${x.location || '—'}</span></div>
|
||||
<p>${x.summary}</p>
|
||||
<div class="meta">${(x.tags || []).slice(0,4).map(t => `<span class="tag">${t}</span>`).join('')}</div>
|
||||
<div class="card-footer"><span class="evidence">证据:${x.evidence_status} · 可信度${x.confidence_level}</span><button class="btn" onclick="openModalById('${x.id}')">查看详情</button></div>
|
||||
</article>`;
|
||||
}
|
||||
|
||||
function renderKeywords() {
|
||||
return `<div class="keyword-wrap">${DATA.datasets.keywords.map(g => `<div class="keyword-group"><h3>${g.group}</h3>${g.terms.map(t => `<span class="kw">${t}</span>`).join('')}</div>`).join('')}</div>`;
|
||||
}
|
||||
|
||||
function allItems() { return Object.entries(DATA.datasets).filter(([k]) => k !== 'keywords').flatMap(([k,v]) => Array.isArray(v) ? v : []); }
|
||||
function openModalById(id) { const x = allItems().find(i => i.id === id); if (x) openModal(x); }
|
||||
function openModal(x) {
|
||||
document.getElementById('modalMeta').textContent = `${x.id} · ${x.category} · ${x.narrative_label}`;
|
||||
document.getElementById('modalTitle').textContent = x.title_cn;
|
||||
const metrics = x.metrics ? `<div class="section"><h3>关键指标</h3><p>${Object.entries(x.metrics).map(([k,v]) => `${k}:${v}`).join(';')}</p></div>` : '';
|
||||
document.getElementById('modalBody').innerHTML = `
|
||||
<div class="detail-grid">
|
||||
<div class="detail-cell"><span>风险等级</span><strong>${x.risk_level} · ${x.risk_score}</strong></div>
|
||||
<div class="detail-cell"><span>叙事方向</span><strong>${x.narrative_label}</strong></div>
|
||||
<div class="detail-cell"><span>时间</span><strong>${x.date || '持续'}</strong></div>
|
||||
<div class="detail-cell"><span>地点</span><strong>${x.location || '—'}</strong></div>
|
||||
<div class="detail-cell"><span>证据状态</span><strong>${x.evidence_status}</strong></div>
|
||||
<div class="detail-cell"><span>可信度</span><strong>${x.confidence_level}</strong></div>
|
||||
<div class="detail-cell"><span>类别</span><strong>${x.category}</strong></div>
|
||||
<div class="detail-cell"><span>编号</span><strong>${x.id}</strong></div>
|
||||
</div>
|
||||
<div class="section"><h3>摘要</h3><p>${x.summary}</p></div>
|
||||
<div class="section"><h3>详情解读</h3><p>${x.detail}</p></div>
|
||||
${metrics}
|
||||
<div class="section"><h3>关联标签</h3><p>${(x.tags || []).map(t => `#${t}`).join(' ')}</p></div>
|
||||
<div class="section"><h3>展示建议</h3><p>该条目可与同类事件、相关人物组织、地方节点、法律制度和媒体平台样本联动。若用于二级页面,建议补充时间线、争议双方观点、原始语料和平台传播样本。</p></div>
|
||||
`;
|
||||
document.getElementById('modalBackdrop').classList.add('open');
|
||||
}
|
||||
function closeModal() { document.getElementById('modalBackdrop').classList.remove('open'); }
|
||||
function backdropClose(e) { if (e.target.id === 'modalBackdrop') closeModal(); }
|
||||
window.addEventListener('keydown', e => { if(e.key === 'Escape') closeModal(); });
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+3623
File diff suppressed because it is too large.
Load diff
+1432
File diff suppressed because it is too large.
Load diff
+98
@@ -0,0 +1,98 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>菲律宾“不做美国棋子”叙事专题数据库 v0.2</title>
|
||||
<style>
|
||||
:root{--bg:#f7f1ed;--card:#fffdfb;--ink:#291817;--muted:#7a6764;--line:#eadbd4;--red:#a23434;--red2:#6f1f1f;--blue:#244b74;--blue2:#eaf2fb;--gold:#9f6a1a;--green:#477052;--shadow:0 18px 50px rgba(51,20,14,.09)}
|
||||
*{box-sizing:border-box} body{margin:0;background:linear-gradient(180deg,#f6eee9 0,#fbf7f3 100%);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",Arial,sans-serif;line-height:1.55}
|
||||
header{padding:26px 30px 18px;background:linear-gradient(135deg,#8f2929,#b6473d 62%,#d8a16f);color:white;position:sticky;top:0;z-index:20;box-shadow:0 12px 30px rgba(76,17,17,.18)}
|
||||
.topline{display:flex;justify-content:space-between;align-items:flex-start;gap:18px;max-width:1500px;margin:0 auto}.title h1{font-size:28px;margin:0 0 6px}.title p{margin:0;opacity:.88}.tools{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.search{min-width:280px;border:0;border-radius:14px;padding:12px 14px;font-size:14px;outline:none}.btn,label.btn{border:1px solid rgba(255,255,255,.55);background:rgba(255,255,255,.14);color:white;border-radius:13px;padding:10px 13px;cursor:pointer;font-size:14px}label.btn input{display:none}
|
||||
nav{background:#fff8f5;border-bottom:1px solid var(--line);position:sticky;top:105px;z-index:19}.tabs{display:flex;gap:8px;overflow:auto;padding:12px 28px;max-width:1500px;margin:0 auto}.tab{border:1px solid var(--line);background:#fff;border-radius:999px;padding:10px 15px;white-space:nowrap;cursor:pointer;color:var(--ink)}.tab.active{background:var(--red);color:white;border-color:var(--red)}
|
||||
.wrap{display:grid;grid-template-columns:280px minmax(0,1fr) 260px;gap:18px;max-width:1500px;margin:18px auto;padding:0 24px 40px}.panel,.card,.modal{background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:22px}.filters{padding:16px;position:sticky;top:170px;align-self:start}.filters h3,.side h3{margin:0 0 12px}.field{margin:0 0 12px}.field label{display:block;color:var(--muted);font-size:12px;margin:0 0 5px}.field input,.field select{width:100%;border:1px solid var(--line);background:#fff;border-radius:12px;padding:10px 11px;outline:none}.field input:focus,.field select:focus{border-color:#ce9287}.reset{width:100%;border:1px solid var(--line);background:#fff;border-radius:12px;padding:10px;cursor:pointer}.side{padding:16px;position:sticky;top:170px;align-self:start}.summary-item{display:flex;align-items:center;justify-content:space-between;border:1px solid var(--line);border-radius:14px;padding:10px 12px;margin:8px 0;background:#fff}.summary-item strong{font-size:19px;color:var(--red)}.summary-item span{font-size:13px;color:var(--muted)}
|
||||
main{min-width:0}.section-title{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:0 0 14px}.section-title h2{margin:0;font-size:24px}.section-title .sub{color:var(--muted);font-size:13px}.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:16px}.stat{padding:18px;border-radius:20px;background:#fff;border:1px solid var(--line);box-shadow:var(--shadow)}.stat .num{font-size:32px;font-weight:800;color:var(--red)}.stat .lab{color:var(--muted)}.two-col{display:grid;grid-template-columns:1.15fr .85fr;gap:14px}.cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.card{padding:16px;cursor:pointer;transition:.18s;min-height:176px}.card:hover{transform:translateY(-2px);box-shadow:0 22px 60px rgba(51,20,14,.15);border-color:#d7b9ac}.card h3{margin:8px 0 8px;font-size:18px}.card p{margin:0 0 10px;color:#5b4744}.meta-row{display:flex;gap:7px;align-items:center;flex-wrap:wrap}.pill{display:inline-flex;align-items:center;gap:4px;border:1px solid var(--line);border-radius:999px;padding:4px 8px;font-size:12px;background:#fff;color:#5d4c49;margin:2px}.pill.red{background:#fff0ee;color:var(--red2);border-color:#ebc6bf}.pill.blue{background:var(--blue2);color:var(--blue);border-color:#cfe0ee}.pill.green{background:#eef8ef;color:var(--green);border-color:#cfe6d2}.pill.gold{background:#fff6e6;color:var(--gold);border-color:#ecd7ad}.risk-high{background:#fff0ee;color:#8b1d1d;border-color:#e8beb8}.risk-medium{background:#fff7e8;color:#8a5b08;border-color:#ead6a4}.risk-low{background:#eef9f1;color:#426f49;border-color:#cfe7d5}.small{font-size:12px;color:var(--muted)}.badge-title{font-size:12px;color:var(--muted);margin-top:12px;text-transform:uppercase;letter-spacing:.06em}.timeline{position:relative;padding-left:18px}.timeline:before{content:"";position:absolute;left:4px;top:6px;bottom:6px;width:2px;background:#e3c8c0}.titem{position:relative;margin:0 0 14px}.titem:before{content:"";position:absolute;left:-21px;top:20px;width:12px;height:12px;border-radius:50%;background:var(--red);box-shadow:0 0 0 5px #f8e8e4}.tcard{background:#fff;border:1px solid var(--line);border-radius:18px;padding:15px;cursor:pointer}.tdate{font-size:12px;color:var(--red);font-weight:700}.table-wrap{overflow:auto;background:#fff;border:1px solid var(--line);border-radius:18px}table{border-collapse:separate;border-spacing:0;width:100%;min-width:900px}th,td{border-bottom:1px solid var(--line);padding:12px 12px;text-align:left;vertical-align:top}th{background:#fff5f2;color:#6a3330;font-size:13px;position:sticky;top:0}td{font-size:14px}.viewbar{display:flex;gap:8px;justify-content:flex-end;margin:-4px 0 12px}.viewbar button, .inline-btn{border:1px solid var(--line);background:#fff;border-radius:999px;padding:8px 12px;cursor:pointer}.viewbar button.active{background:var(--red);color:#fff;border-color:var(--red)}.matrix{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.risk-col{border:1px solid var(--line);background:#fff;border-radius:20px;padding:14px}.empty{border:1px dashed #d7bdb5;border-radius:18px;padding:35px;text-align:center;color:var(--muted);background:#fff}.linkish,.rel-card{cursor:pointer}.rel-card{border:1px solid var(--line);border-radius:14px;padding:10px;background:#fff;margin:6px 0}.rel-card strong{display:block}.rel-card span{color:var(--muted);font-size:12px}.mini-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.distance-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.distance{background:#fff;border:1px solid var(--line);border-radius:16px;padding:12px}.distance strong{display:block;color:var(--red);font-size:22px}.distance span{font-size:12px;color:var(--muted)}
|
||||
.modal-backdrop{position:fixed;inset:0;background:rgba(38,15,12,.5);display:none;align-items:center;justify-content:center;z-index:100;padding:20px}.modal-backdrop.show{display:flex}.modal{width:min(1120px,100%);max-height:90vh;overflow:auto}.modal-head{padding:20px 24px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;gap:12px;align-items:flex-start;position:sticky;top:0;background:#fffdfb;z-index:2}.modal-title{margin:0;font-size:24px}.close{border:0;background:#f4e6e2;border-radius:12px;width:40px;height:40px;font-size:24px;cursor:pointer}.modal-body{padding:20px 24px 28px}.hero{padding:18px;border:1px solid var(--line);border-radius:20px;background:linear-gradient(135deg,#fff7f4,#fffdfb);margin-bottom:14px}.hero h3{margin:0 0 8px}.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.detail-section{border:1px solid var(--line);background:#fff;border-radius:18px;padding:15px;margin-bottom:14px}.detail-section h4{margin:0 0 10px;color:var(--red2);font-size:16px}.detail-section p{margin:6px 0}.detail-section ul{margin:8px 0 0;padding-left:20px}.obj-table{width:100%;min-width:0}.obj-table th,.obj-table td{font-size:13px;padding:8px}.full{grid-column:1/-1}.note-box{background:#fff9ef;border:1px solid #ecd5ad;border-radius:16px;padding:12px}.source-list{font-size:12px;color:var(--muted);line-height:1.6}.kpi-strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:12px 0}.kpi{border:1px solid var(--line);border-radius:16px;padding:12px;background:#fff}.kpi b{display:block;color:var(--red);font-size:20px}.kpi span{font-size:12px;color:var(--muted)}
|
||||
@media(max-width:1120px){.wrap{grid-template-columns:1fr}.filters,.side{position:relative;top:0}.cards{grid-template-columns:repeat(2,minmax(0,1fr))}.stats{grid-template-columns:repeat(2,minmax(0,1fr))}.two-col,.matrix,.detail-grid{grid-template-columns:1fr}nav{top:120px}.distance-grid{grid-template-columns:repeat(2,1fr)}}
|
||||
@media(max-width:680px){header{position:relative}.topline{display:block}.tools{margin-top:12px}.search{min-width:100%;width:100%}nav{position:relative;top:0}.wrap{padding:0 12px}.cards{grid-template-columns:1fr}.stats{grid-template-columns:1fr}.distance-grid,.mini-grid,.kpi-strip{grid-template-columns:1fr}.modal-backdrop{padding:6px}.modal-head{padding:16px}.modal-body{padding:14px}.tabs{padding:10px 12px}}
|
||||
@media print{header,nav,.filters,.side,.tools,.modal-backdrop{display:none!important}.wrap{display:block;max-width:none}.card,.panel,.stat{box-shadow:none}.cards{grid-template-columns:1fr 1fr}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header><div class="topline"><div class="title"><h1 id="projectTitle">专题数据库</h1><p id="projectSubtitle"></p></div><div class="tools"><input id="globalSearch" class="search" placeholder="全局搜索:EDCA / Cagayan / BAYAN / 台海 / WPS"><label class="btn">导入JSON<input id="jsonFile" type="file" accept="application/json,.json"></label><button class="btn" onclick="downloadJson()">导出JSON</button></div></div></header>
|
||||
<nav><div class="tabs" id="tabs"></div></nav>
|
||||
<div class="wrap"><aside class="panel filters"><h3>筛选器</h3><div class="field"><label>关键词</label><input id="filterKeyword" placeholder="在当前库内搜索"></div><div class="field"><label>年份</label><select id="filterYear"></select></div><div class="field"><label>事件类型</label><select id="filterCategory"></select></div><div class="field"><label>省份</label><select id="filterProvince"></select></div><div class="field"><label>阵营</label><select id="filterCamp"></select></div><div class="field"><label>风险等级</label><select id="filterRisk"></select></div><div class="field"><label>战略方向</label><select id="filterAxis"></select></div><button class="reset" onclick="resetFilters()">清空筛选</button><div class="source-list" style="margin-top:14px">说明:本页面为公开资料研究展示工具,复杂字段以详情弹框结构化展示,不再输出原始JSON。</div></aside><main id="main"></main><aside class="panel side"><h3>数据摘要</h3><div id="summaryPanel"></div><h3 style="margin-top:18px">高频标签</h3><div id="tagCloud"></div></aside></div>
|
||||
<div class="modal-backdrop" id="modalBackdrop" onclick="backdropClose(event)"><div class="modal"><div class="modal-head"><div><h2 id="modalTitle" class="modal-title"></h2><div id="modalMeta" class="meta-row"></div></div><button class="close" onclick="closeModal()">×</button></div><div id="modalBody" class="modal-body"></div></div></div>
|
||||
<script id="db-json" type="application/json">{"meta": {"project_title": "菲律宾“不做美国棋子”叙事专题数据库", "project_subtitle": "Philippines “Not a US Pawn” Narrative Research Dashboard", "version": "0.2", "last_updated": "2026-06-29", "language": "zh-CN", "description": "用于展示菲律宾“菲律宾不做美国棋子”叙事专题的结构化知识库,覆盖EDCA地点、事件线、人物组织、叙事结构、竞争叙事、风险核查和跨库关联查询。", "scope_note": "本数据库服务于公开资料研究、舆情观察、风险评估与专题分析,不用于隐蔽影响、虚假传播或操控性动员。", "revision_note": "v0.2增强版:弹框详情改为结构化页面展示,补充研究字段,尽量保留前期MD知识库中的分析内容。"}, "tabs": [{"id": "overview", "label": "专题总览", "type": "dashboard"}, {"id": "timeline", "label": "事件时间线", "type": "timeline"}, {"id": "edca_sites", "label": "EDCA地点库", "type": "cards_table"}, {"id": "actors", "label": "人物组织库", "type": "cards_table"}, {"id": "narratives", "label": "叙事结构库", "type": "cards"}, {"id": "counter_narratives", "label": "竞争叙事库", "type": "cards"}, {"id": "risks", "label": "风险核查库", "type": "risk_matrix"}, {"id": "search", "label": "综合查询", "type": "search"}], "dictionaries": {"event_categories": {"edca_signature": "EDCA签署/制度化", "edca_expansion": "EDCA扩展", "edca_infrastructure": "EDCA基建", "balikatan": "Balikatan军演", "china_coast_guard": "中国海警事件", "ph_official_protest": "菲律宾官方抗议", "us_ph_meeting": "美菲高层会晤", "anti_edca_protest": "反EDCA/反军演抗议", "multilateral_security": "多边安全合作", "wps_civil_action": "WPS民间行动"}, "strategic_axis": {"taiwan_bashi": "台海—巴士海峡方向", "wps_scs": "西菲律宾海—南海方向", "northern_luzon": "北吕宋前沿方向", "central_luzon": "中吕宋支撑方向", "central_logistics": "中部群岛后勤方向", "mindanao": "棉兰老方向", "hadr": "人道援助与灾害响应"}, "actor_camps": {"marcos_official": "马科斯官方阵营", "duterte_camp": "杜特尔特阵营", "left_anti_base": "左翼反基地阵营", "wps_sovereignty": "WPS主权派", "fact_check": "事实核查机构", "security_expert": "安全专家与智库", "external_actor": "外部行为体"}, "actor_types": {"official_person": "官方人物", "political_person": "政治人物", "organization": "组织", "party_bloc": "政党/党团", "institution": "机构", "expert": "专家", "think_tank": "智库", "media_factcheck": "事实核查/媒体"}, "stance_values": {"support": "支持", "oppose": "反对", "mixed": "复杂/混合", "neutral": "中立", "unknown": "未知"}, "risk_levels": {"low": "低", "medium": "中", "high": "高"}, "site_types": {"naval_base": "海军基地", "airport": "机场", "army_camp": "陆军营地", "air_base": "空军基地", "airfield": "机场/航空场"}, "field_labels": {"id": "编号", "name": "名称", "name_cn": "中文名", "name_en": "英文名", "title": "标题", "summary": "摘要", "brief": "简述", "description": "说明", "province": "省份", "city_or_area": "城市/区域", "designation_year": "纳入年份", "site_type": "地点类型", "strategic_axis": "战略方向", "risk_level": "风险等级", "local_position": "地方态度", "tags": "标签", "distances": "近似距离", "detail": "详情", "strategic_value": "战略意义", "local_political_positions": "地方政治人物表态", "local_reactions": "地方居民抗议或支持", "infrastructure_projects": "基建项目", "risk_controversies": "风险争议", "official_response": "官方回应", "related_events": "关联事件", "related_actors": "关联人物组织", "related_sites": "关联地点", "background": "背景", "opposition_position": "反对方立场", "ph_official_position": "菲律宾官方立场", "us_position": "美国立场", "china_position": "中国立场", "research_note": "研究备注", "category": "类别", "date": "日期", "year": "年份", "location": "地点", "actors": "相关行为体", "narrative_signal": "叙事信号", "counter_narrative": "竞争叙事/反驳框架", "current_role": "当前身份", "camp": "阵营", "type": "类型", "stance_on_edca": "对EDCA立场", "stance_on_wps": "对WPS立场", "influence_level": "影响等级", "narrative_function": "叙事功能", "typical_positions": "典型立场", "typical_keywords": "典型关键词", "risk_note": "风险备注", "profile": "画像", "monitoring_focus": "监测重点", "core_claims": "核心命题", "target_audiences": "目标受众", "typical_expressions": "典型表达", "strength": "叙事强项", "weakness": "叙事弱项", "counter_narratives": "竞争叙事", "main_speakers": "主要提出者", "used_against": "用于反驳", "evidence_types": "证据类型", "advantage": "优势", "limitation": "限制", "trigLine truncated
|
||||
<script>
|
||||
let DB = JSON.parse(document.getElementById('db-json').textContent);
|
||||
const $=s=>document.querySelector(s), $$=s=>Array.from(document.querySelectorAll(s));
|
||||
let state={tab:'overview',view:'cards',filters:{},global:''};
|
||||
const collMap={event:'events',edca_site:'edca_sites',actor:'actors',narrative:'narratives',counter_narrative:'counter_narratives',risk:'risks'};
|
||||
const tabsForType={event:'timeline',edca_site:'edca_sites',actor:'actors',narrative:'narratives',counter_narrative:'counter_narratives',risk:'risks'};
|
||||
function arr(x){return Array.isArray(x)?x:[]}
|
||||
function esc(s){return String(s??'').replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]))}
|
||||
function dict(group,key){return DB.dictionaries?.[group]?.[key] || key || ''}
|
||||
function label(k){return DB.dictionaries?.field_labels?.[k] || k}
|
||||
function titleOf(o){return o?.title || o?.name_cn || o?.name || o?.name_en || o?.id || ''}
|
||||
function summaryOf(o){return o?.summary || o?.brief || o?.description || o?.narrative_function || ''}
|
||||
function risk(level){return level?`<span class="pill risk-${esc(level)}">风险:${esc(dict('risk_levels',level))}</span>`:''}
|
||||
function chips(tags,cls=''){return arr(tags).map(t=>`<span class="pill ${cls}">${esc(t)}</span>`).join('')}
|
||||
function getAll(){let out=[];Object.entries(collMap).forEach(([type,key])=>arr(DB.data?.[key]).forEach(x=>out.push({...x,__type:type})));return out}
|
||||
function find(id){for(const [type,key] of Object.entries(collMap)){const x=arr(DB.data?.[key]).find(i=>i.id===id); if(x) return {...x,__type:type}}return null}
|
||||
function searchText(o){return JSON.stringify(o).toLowerCase()}
|
||||
function matchKw(o,kw){return !kw || searchText(o).includes(kw.toLowerCase())}
|
||||
function setup(){ $('#projectTitle').textContent=DB.meta.project_title; $('#projectSubtitle').textContent=(DB.meta.project_subtitle||'')+' · '+(DB.meta.revision_note||''); renderTabs(); fillFilters(); bind(); render(); }
|
||||
function renderTabs(){ $('#tabs').innerHTML=arr(DB.tabs).map(t=>`<button class="tab ${state.tab===t.id?'active':''}" data-id="${esc(t.id)}">${esc(t.label)}</button>`).join(''); $$('#tabs .tab').forEach(b=>b.onclick=()=>{state.tab=b.dataset.id;renderTabs();render();}); }
|
||||
function optAll(label){return `<option value="">${label}</option>`}
|
||||
function fillFilters(){ const years=[...new Set(arr(DB.data.events).map(e=>e.year).filter(Boolean))].sort((a,b)=>a-b); $('#filterYear').innerHTML=optAll('全部年份')+years.map(y=>`<option>${y}</option>`).join(''); $('#filterCategory').innerHTML=optAll('全部类型')+Object.entries(DB.dictionaries.event_categories||{}).map(([k,v])=>`<option value="${esc(k)}">${esc(v)}</option>`).join(''); const ps=[...new Set(arr(DB.data.edca_sites).map(s=>s.province).filter(Boolean))].sort(); $('#filterProvince').innerHTML=optAll('全部省份')+ps.map(p=>`<option>${esc(p)}</option>`).join(''); $('#filterCamp').innerHTML=optAll('全部阵营')+Object.entries(DB.dictionaries.actor_camps||{}).map(([k,v])=>`<option value="${esc(k)}">${esc(v)}</option>`).join(''); $('#filterRisk').innerHTML=optAll('全部风险')+Object.entries(DB.dictionaries.risk_levels||{}).map(([k,v])=>`<option value="${esc(k)}">${esc(v)}</option>`).join(''); $('#filterAxis').innerHTML=optAll('全部方向')+Object.entries(DB.dictionaries.strategic_axis||{}).map(([k,v])=>`<option value="${esc(k)}">${esc(v)}</option>`).join(''); }
|
||||
function bind(){ ['filterKeyword','filterYear','filterCategory','filterProvince','filterCamp','filterRisk','filterAxis'].forEach(id=>{$('#'+id).oninput=collect;$('#'+id).onchange=collect}); $('#globalSearch').oninput=e=>{state.global=e.target.value.trim(); if(state.global){state.tab='search';renderTabs()} render()}; $('#jsonFile').onchange=importJson; window.onkeydown=e=>{if(e.key==='Escape')closeModal()}; }
|
||||
function collect(){ state.filters={keyword:$('#filterKeyword').value.trim(),year:$('#filterYear').value,category:$('#filterCategory').value,province:$('#filterProvince').value,camp:$('#filterCamp').value,risk:$('#filterRisk').value,axis:$('#filterAxis').value}; render(); }
|
||||
function resetFilters(){['filterKeyword','filterYear','filterCategory','filterProvince','filterCamp','filterRisk','filterAxis','globalSearch'].forEach(id=>$('#'+id).value='');state.filters={};state.global='';render()}
|
||||
function commonFilter(items){const f=state.filters||{};return items.filter(x=>{if(f.keyword&&!matchKw(x,f.keyword))return false;if(f.risk&&x.risk_level!==f.risk)return false;if(f.year&&x.year&&String(x.year)!==String(f.year))return false;if(f.category&&x.category&&x.category!==f.category)return false;if(f.province&&x.province&&x.province!==f.province)return false;if(f.camp&&x.camp&&x.camp!==f.camp)return false;if(f.axis&&x.strategic_axis&&!arr(x.strategic_axis).includes(f.axis))return false;return true})}
|
||||
function render(){renderSummary();const t=state.tab;if(t==='overview')overview();if(t==='timeline')timeline();if(t==='edca_sites')sites();if(t==='actors')actors();if(t==='narratives')narratives();if(t==='counter_narratives')counters();if(t==='risks')risks();if(t==='search')search();}
|
||||
function renderSummary(){const d=DB.data;const rows=[['EDCA地点',arr(d.edca_sites).length],['事件',arr(d.events).length],['人物组织',arr(d.actors).length],['叙事结构',arr(d.narratives).length],['竞争叙事',arr(d.counter_narratives).length],['风险项',arr(d.risks).length],['高风险地点',arr(d.edca_sites).filter(x=>x.risk_level==='high').length],['高风险事件',arr(d.events).filter(x=>x.risk_level==='high').length]];$('#summaryPanel').innerHTML=rows.map(([k,v])=>`<div class="summary-item"><strong>${v}</strong><span>${k}</span></div>`).join(''); const tags={}; getAll().forEach(x=>arr(x.tags).forEach(t=>tags[t]=(tags[t]||0)+1)); $('#tagCloud').innerHTML=Object.entries(tags).sort((a,b)=>b[1]-a[1]).slice(0,20).map(([t,n])=>`<span class="pill">${esc(t)} · ${n}</span>`).join('');}
|
||||
function overview(){const d=DB.data;const high=arr(d.edca_sites).filter(x=>x.risk_level==='high');const eventCats={};arr(d.events).forEach(e=>eventCats[e.category]=(eventCats[e.category]||0)+1); const camps={};arr(d.actors).forEach(a=>camps[a.camp]=(camps[a.camp]||0)+1); $('#main').innerHTML=`<div class="section-title"><div><h2>专题总览</h2><div class="sub">${esc(DB.meta.description)}</div></div><span class="pill red">${esc(DB.meta.version)} · ${esc(DB.meta.last_updated)}</span></div><div class="stats"><div class="stat"><div class="num">${arr(d.edca_sites).length}</div><div class="lab">EDCA地点</div></div><div class="stat"><div class="num">${arr(d.events).length}</div><div class="lab">事件线节点</div></div><div class="stat"><div class="num">${arr(d.actors).length}</div><div class="lab">人物组织</div></div><div class="stat"><div class="num">${high.length}</div><div class="lab">高风险地点</div></div></div><div class="two-col"><div class="panel" style="padding:18px"><h3>核心问题</h3><p>本页面围绕“菲律宾不做美国棋子 / not a US pawn”这一叙事,展示其与EDCA、美菲同盟、Balikatan军演、西菲律宾海主权、杜特尔特—马科斯政治竞争、地方风险和事实核查之间的关系。</p><p class="small">${esc(DB.meta.scope_note||'')}</p>${chips(DB.indexes?.common_tags)}</div><div class="panel" style="padding:18px"><h3>事件类型分布</h3>${Object.entries(eventCats).map(([k,v])=>`<div class="summary-item"><strong>${v}</strong><span>${esc(dict('event_categories',k))}</span></div>`).join('')}</div></div><div style="height:14px"></div><div class="two-col"><div class="panel" style="padding:18px"><h3>阵营分布</h3>${Object.entries(camps).map(([k,v])=>`<div class="summary-item"><strong>${v}</strong><span>${esc(dict('actor_camps',k))}</span></div>`).join('')}</div><div class="panel" style="padding:18px"><h3>展示原则</h3><ul><li>卡片只看摘要,弹框展示完整知识结构。</li><li>长字段按模块呈现,不再显示原始JSON。</li><li>所有关联对象支持点击跳转。</li><li>风险内容同时展示核查重点和边界。</li></ul></div></div><div style="height:14px"></div><div class="section-title"><h2>高风险EDCA地点</h2><button class="inline-btn" onclick="state.tab='edca_sites';renderTabs();render()">查看全部</button></div><div class="cards">${high.map(siteCard).join('')}</div>`}
|
||||
function timeline(){let items=commonFilter(arr(DB.data.events)).sort((a,b)=>String(b.date).localeCompare(String(a.date))); if(state.filters.province){const ids=arr(DB.data.edca_sites).filter(s=>s.province===state.filters.province).map(s=>s.id);items=items.filter(e=>arr(e.related_sites).some(id=>ids.includes(id))||matchKw(e,state.filters.province));} $('#main').innerHTML=`<div class="section-title"><h2>事件时间线</h2><span class="pill blue">命中 ${items.length} 条</span></div>`+(items.length?`<div class="timeline">${items.map(e=>`<div class="titem"><div class="tcard" onclick="openModal('event','${esc(e.id)}')"><div class="tdate">${esc(e.date)} · ${esc(dict('event_categories',e.category))}</div><h3>${esc(e.title)}</h3><p>${esc(e.summary)}</p><div class="meta-row">${risk(e.risk_level)}${chips(e.tags)}</div></div></div>`).join('')}</div>`:empty())}
|
||||
function sites(){let items=commonFilter(arr(DB.data.edca_sites)); $('#main').innerHTML=`<div class="section-title"><h2>EDCA九个地点库</h2><span class="pill blue">命中 ${items.length} 条</span></div>${viewbar('sites')} ${state.view==='table'?siteTable(items):`<div class="cards">${items.map(siteCard).join('')}</div>`}`}
|
||||
function siteCard(s){return `<div class="card" onclick="openModal('edca_site','${esc(s.id)}')"><div class="meta-row"><span class="pill red">${esc(s.province)}</span>${risk(s.risk_level)}</div><h3>${esc(s.name)}</h3><p>${esc(s.brief)}</p><div class="badge-title">战略方向</div><div>${arr(s.strategic_axis).map(a=>`<span class="pill blue">${esc(dict('strategic_axis',a))}</span>`).join('')}</div><div class="badge-title">标签</div>${chips(s.tags)}</div>`}
|
||||
function siteTable(items){return `<div class="table-wrap"><table><thead><tr><th>地点</th><th>省份</th><th>年份</th><th>类型</th><th>战略方向</th><th>地方态度</th><th>风险</th><th>详情</th></tr></thead><tbody>${items.map(s=>`<tr><td><b>${esc(s.name)}</b><br><span class="small">${esc(s.city_or_area)}</span></td><td>${esc(s.province)}</td><td>${esc(s.designation_year)}</td><td>${esc(dict('site_types',s.site_type))}</td><td>${arr(s.strategic_axis).map(a=>esc(dict('strategic_axis',a))).join('<br>')}</td><td>${esc(s.local_position)}</td><td>${risk(s.risk_level)}</td><td><button class="inline-btn" onclick="openModal('edca_site','${esc(s.id)}')">打开</button></td></tr>`).join('')}</tbody></table></div>`}
|
||||
function actors(){let items=commonFilter(arr(DB.data.actors)); $('#main').innerHTML=`<div class="section-title"><h2>人物组织库</h2><span class="pill blue">命中 ${items.length} 条</span></div>${viewbar('actors')} ${state.view==='table'?actorTable(items):`<div class="cards">${items.map(actorCard).join('')}</div>`}`}
|
||||
function actorCard(a){return `<div class="card" onclick="openModal('actor','${esc(a.id)}')"><div class="meta-row"><span class="pill red">${esc(dict('actor_camps',a.camp))}</span><span class="pill">${esc(dict('actor_types',a.type))}</span></div><h3>${esc(a.name_cn||a.name_en)}</h3><p>${esc(a.brief)}</p><div class="badge-title">叙事功能</div><p>${esc(a.narrative_function||'')}</p>${chips(a.tags)}</div>`}
|
||||
function actorTable(items){return `<div class="table-wrap"><table><thead><tr><th>名称</th><th>类型</th><th>阵营</th><th>当前角色</th><th>EDCA立场</th><th>WPS立场</th><th>影响</th><th>详情</th></tr></thead><tbody>${items.map(a=>`<tr><td><b>${esc(a.name_cn||a.name_en)}</b><br><span class="small">${esc(a.name_en||'')}</span></td><td>${esc(dict('actor_types',a.type))}</td><td>${esc(dict('actor_camps',a.camp))}</td><td>${esc(a.current_role)}</td><td>${esc(dict('stance_values',a.stance_on_edca))}</td><td>${esc(dict('stance_values',a.stance_on_wps))}</td><td>${esc(a.influence_level)}</td><td><button class="inline-btn" onclick="openModal('actor','${esc(a.id)}')">打开</button></td></tr>`).join('')}</tbody></table></div>`}
|
||||
function narratives(){const items=commonFilter(arr(DB.data.narratives)); $('#main').innerHTML=`<div class="section-title"><h2>叙事结构库</h2><span class="pill blue">命中 ${items.length} 条</span></div><div class="cards">${items.map(n=>`<div class="card" onclick="openModal('narrative','${esc(n.id)}')"><div class="meta-row"><span class="pill red">${esc(n.category)}</span>${risk(n.risk_level)}</div><h3>${esc(n.title)}</h3><p>${esc(n.summary)}</p>${chips(n.tags)}</div>`).join('')}</div>`}
|
||||
function counters(){const items=commonFilter(arr(DB.data.counter_narratives)); $('#main').innerHTML=`<div class="section-title"><h2>竞争叙事库</h2><span class="pill blue">命中 ${items.length} 条</span></div><div class="cards">${items.map(c=>`<div class="card" onclick="openModal('counter_narrative','${esc(c.id)}')"><div class="meta-row"><span class="pill blue">竞争叙事</span>${risk(c.risk_level)}</div><h3>${esc(c.title)}</h3><p>${esc(c.summary)}</p>${chips(c.tags)}</div>`).join('')}</div>`}
|
||||
function risks(){const items=commonFilter(arr(DB.data.risks)); const levels=['high','medium','low']; $('#main').innerHTML=`<div class="section-title"><h2>风险与事实核查库</h2><span class="pill blue">命中 ${items.length} 条</span></div><div class="matrix">${levels.map(l=>`<div class="risk-col"><h3>${esc(dict('risk_levels',l))}风险</h3>${items.filter(r=>r.risk_level===l).map(r=>`<div class="card" style="min-height:auto;margin-bottom:10px" onclick="openModal('risk','${esc(r.id)}')"><h3>${esc(r.title)}</h3><p>${esc(r.description)}</p>${chips(r.tags)}</div>`).join('')||'<div class="empty">暂无</div>'}</div>`).join('')}</div>`}
|
||||
function search(){const kw=state.global||state.filters.keyword||'';let items=getAll().filter(x=>matchKw(x,kw));items=commonFilter(items); const names={event:'事件',edca_site:'EDCA地点',actor:'人物组织',narrative:'叙事结构',counter_narrative:'竞争叙事',risk:'风险'}; $('#main').innerHTML=`<div class="section-title"><div><h2>综合查询中心</h2><div class="sub">${kw?'关键词:'+esc(kw):'可在顶部或左侧输入关键词,跨库检索所有对象。'}</div></div><span class="pill blue">命中 ${items.length} 条</span></div>`+(items.length?`<div class="table-wrap"><table><thead><tr><th>类型</th><th>标题/名称</th><th>摘要</th><th>标签</th><th>风险</th><th>详情</th></tr></thead><tbody>${items.map(i=>`<tr><td><span class="pill red">${esc(names[i.__type])}</span></td><td><b>${esc(titleOf(i))}</b></td><td>${esc(summaryOf(i)).slice(0,260)}</td><td>${chips(i.tags)}</td><td>${risk(i.risk_level)}</td><td><button class="inline-btn" onclick="openModal('${esc(i.__type)}','${esc(i.id)}')">打开</button></td></tr>`).join('')}</tbody></table></div>`:empty('暂无查询结果。'))}
|
||||
function viewbar(){return `<div class="viewbar"><button class="${state.view==='cards'?'active':''}" onclick="state.view='cards';render()">卡片</button><button class="${state.view==='table'?'active':''}" onclick="state.view='table';render()">表格</button></div>`}
|
||||
function empty(t='没有匹配结果。'){return `<div class="empty">${esc(t)}</div>`}
|
||||
function openModal(type,id){const item=find(id); if(!item)return; $('#modalTitle').textContent=titleOf(item); $('#modalMeta').innerHTML=modalMeta(item); $('#modalBody').innerHTML=modalBody(item,type); $('#modalBackdrop').classList.add('show')}
|
||||
function closeModal(){$('#modalBackdrop').classList.remove('show')}
|
||||
function backdropClose(e){if(e.target.id==='modalBackdrop')closeModal()}
|
||||
function modalMeta(o){let bits=[]; if(o.date)bits.push(`<span class="pill">${esc(o.date)}</span>`); if(o.category)bits.push(`<span class="pill blue">${esc(dict('event_categories',o.category))}</span>`); if(o.province)bits.push(`<span class="pill red">${esc(o.province)}</span>`); if(o.camp)bits.push(`<span class="pill red">${esc(dict('actor_camps',o.camp))}</span>`); if(o.risk_level)bits.push(risk(o.risk_level)); return bits.join('')+chips(o.tags)}
|
||||
function section(title,content,cls=''){if(!content)return''; return `<section class="detail-section ${cls}"><h4>${esc(title)}</h4>${content}</section>`}
|
||||
function paragraph(v){return v?`<p>${esc(v)}</p>`:''}
|
||||
function list(v){if(!v||!arr(v).length)return''; return `<ul>${arr(v).map(x=>`<li>${renderVal(x)}</li>`).join('')}</ul>`}
|
||||
function objGrid(o){if(!o||typeof o!=='object')return''; return `<div class="mini-grid">${Object.entries(o).filter(([k,v])=>v!==undefined&&v!==null&&v!==''&&!(Array.isArray(v)&&!v.length)).map(([k,v])=>`<div class="rel-card"><span>${esc(label(k))}</span><strong>${renderVal(v)}</strong></div>`).join('')}</div>`}
|
||||
function objTable(items){if(!arr(items).length)return''; if(typeof items[0]!=='object')return list(items); const keys=[...new Set(items.flatMap(o=>Object.keys(o)))]; return `<div class="table-wrap"><table class="obj-table"><thead><tr>${keys.map(k=>`<th>${esc(label(k))}</th>`).join('')}</tr></thead><tbody>${items.map(o=>`<tr>${keys.map(k=>`<td>${renderVal(o[k])}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`}
|
||||
function renderVal(v){if(v===undefined||v===null)return''; if(Array.isArray(v)){if(v.length&&typeof v[0]==='object')return objTable(v); return arr(v).map(x=>renderVal(x)).join('<br>')} if(typeof v==='object')return objGrid(v); const s=String(v); const x=find(s); if(x)return `<span class="linkish pill blue" onclick="jump('${esc(s)}')">${esc(titleOf(x))}</span>`; return esc(s)}
|
||||
function rels(ids){return arr(ids).map(id=>{const x=find(id); return x?`<div class="rel-card" onclick="jump('${esc(id)}')"><strong>${esc(titleOf(x))}</strong><span>${esc(typeName(x.__type))} · ${esc(summaryOf(x)).slice(0,88)}</span></div>`:`<div class="rel-card"><strong>${esc(id)}</strong></div>`}).join('')}
|
||||
function typeName(t){return {event:'事件',edca_site:'EDCA地点',actor:'人物组织',narrative:'叙事结构',counter_narrative:'竞争叙事',risk:'风险'}[t]||t}
|
||||
function distances(d){if(!d)return''; return `<div class="distance-grid">${Object.entries(d).map(([k,v])=>`<div class="distance"><strong>${esc(v)} km</strong><span>${esc(label(k))}</span></div>`).join('')}</div>`}
|
||||
function modalBody(o,type){
|
||||
let d=o.detail||{};
|
||||
if(type==='edca_site') return `<div class="hero"><h3>${esc(o.name)}</h3><p>${esc(o.brief)}</p><div class="kpi-strip"><div class="kpi"><b>${esc(o.designation_year)}</b><span>纳入年份</span></div><div class="kpi"><b>${esc(o.province)}</b><span>省份</span></div><div class="kpi"><b>${esc(dict('site_types',o.site_type))}</b><span>地点类型</span></div><div class="kpi"><b>${esc(dict('risk_levels',o.risk_level))}</b><span>风险等级</span></div></div></div><div class="detail-grid">${section('基本信息',objGrid({province:o.province,city_or_area:o.city_or_area,designation_year:o.designation_year,site_type:dict('site_types',o.site_type),strategic_axis:arr(o.strategic_axis).map(a=>dict('strategic_axis',a)).join(' / '),local_position:o.local_position}),'full')}${section('近似距离与战略方向',distances(o.distances),'full')}${section('战略意义',paragraph(d.strategic_value))}${section('叙事解读',list(d.narrative_interpretation))}${section('地方政治人物表态',objTable(d.local_political_positions),'full')}${section('地方居民抗议或支持',objGrid(d.local_reactions))}${section('基建项目',list(d.infrastructure_projects))}${section('风险争议',list(d.risk_controversies))}${section('官方回应',paragraph(d.official_response))}${section('竞争性解释',list(d.counter_reading))}${section('证据线索',list(d.evidence_clues))}${section('展示提示',`<div class="note-box">${esc(d.display_warning||'')}</div>`)}${section('研究缺口',list(d.research_gaps),'full')}${section('关联事件',rels(d.related_events),'full')}${section('关联人物组织',rels(d.related_actors),'full')}</div>`;
|
||||
if(type==='event') return `<div class="hero"><h3>${esc(o.title)}</h3><p>${esc(o.summary)}</p></div><div class="detail-grid">${section('时间地点',objGrid({date:o.date,year:o.year,location:o.location,category:dict('event_categories',o.category)}),'full')}${section('事件背景',paragraph(d.background))}${section('为什么重要',paragraph(d.why_it_matters||o.narrative_signal))}${section('叙事信号',paragraph(o.narrative_signal))}${section('竞争叙事/反驳框架',paragraph(o.counter_narrative))}${section('菲律宾官方立场',paragraph(d.ph_official_position))}${section('美国立场',paragraph(d.us_position))}${section('中国立场',paragraph(d.china_position))}${section('反对方立场',paragraph(d.opposition_position))}${section('叙事复杂性',paragraph(d.narrative_complication))}${section('研究备注',paragraph(d.research_note),'full')}${section('相关行为体',rels(o.actors),'full')}${section('关联地点',rels(o.related_sites),'full')}</div>`;
|
||||
if(type==='actor') return `<div class="hero"><h3>${esc(o.name_cn||o.name_en)}</h3><p>${esc(o.brief)}</p></div><div class="detail-grid">${section('基本信息',objGrid({name_en:o.name_en,type:dict('actor_types',o.type),camp:dict('actor_camps',o.camp),current_role:o.current_role,stance_on_edca:dict('stance_values',o.stance_on_edca),stance_on_wps:dict('stance_values',o.stance_on_wps),influence_level:o.influence_level}),'full')}${section('人物/组织画像',paragraph(d.profile))}${section('叙事功能',paragraph(o.narrative_function))}${section('典型立场',list(d.typical_positions))}${section('典型关键词',list(d.typical_keywords))}${section('监测重点',list(d.monitoring_focus),'full')}${section('风险备注',paragraph(d.risk_note))}${section('研究备注',paragraph(d.research_note))}${section('关联事件',rels(d.related_events),'full')}${section('关联地点',rels(d.related_sites),'full')}</div>`;
|
||||
if(type==='narrative') return `<div class="hero"><h3>${esc(o.title)}</h3><p>${esc(o.summary)}</p></div><div class="detail-grid">${section('核心命题',list(o.core_claims),'full')}${section('目标受众',list(o.target_audiences))}${section('典型表达',list(o.typical_expressions))}${section('适用场景',list(d.application_scenarios))}${section('叙事强项',paragraph(d.strength))}${section('叙事弱项',paragraph(d.weakness))}${section('分析备注',paragraph(d.analysis_note),'full')}${section('待补数据',list(d.data_to_collect))}${section('关联事件',rels(o.related_events),'full')}${section('关联人物组织',rels(o.related_actors),'full')}${section('竞争叙事',rels(d.counter_narratives),'full')}</div>`;
|
||||
if(type==='counter_narrative') return `<div class="hero"><h3>${esc(o.title)}</h3><p>${esc(o.summary)}</p></div><div class="detail-grid">${section('主要提出者',list(o.main_speakers))}${section('证据类型',list(o.evidence_types))}${section('优势',paragraph(d.advantage))}${section('限制',paragraph(d.limitation))}${section('观察场景',list(d.where_to_observe))}${section('对“美国棋子”叙事的影响',paragraph(d.impact_on_proxy_narrative),'full')}${section('用于反驳',rels(o.used_against),'full')}${section('关联事件',rels(d.related_events),'full')}</div>`;
|
||||
if(type==='risk') return `<div class="hero"><h3>${esc(o.title)}</h3><p>${esc(o.description)}</p></div><div class="detail-grid">${section('触发场景',list(o.trigger_scenarios),'full')}${section('事实核查重点',list(o.fact_check_focus),'full')}${section('风险边界与处理建议',list(o.mitigation_guidelines),'full')}${section('展示提示',`<div class="note-box">${esc(o.display_note||'')}</div>`)}${section('关联地点',rels(o.related_sites),'full')}${section('关联人物组织',rels(o.related_actors),'full')}</div>`;
|
||||
return section('详情',renderVal(o),'full')
|
||||
}
|
||||
function jump(id){const x=find(id); if(!x)return; closeModal(); state.tab=tabsForType[x.__type]||'search'; renderTabs(); render(); setTimeout(()=>openModal(x.__type,id),80)}
|
||||
function importJson(e){const file=e.target.files[0]; if(!file)return; const r=new FileReader(); r.onload=ev=>{try{DB=JSON.parse(ev.target.result);fillFilters();renderTabs();render();alert('JSON导入成功')}catch(err){alert('JSON解析失败:'+err.message)}}; r.readAsText(file,'utf-8')}
|
||||
function downloadJson(){const blob=new Blob([JSON.stringify(DB,null,2)],{type:'application/json;charset=utf-8'});const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download='philippines_not_us_pawn_narrative_db_v2_export.json';a.click();URL.revokeObjectURL(url)}
|
||||
setup();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user