CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s
- errors.py:IDEMPOTENCY_CONFLICT / TASK_STATE_INVALID 的 hint 改为「首页报告」口径(App 无任务中心页,经 hint 透出会误导) - report.html.j2:head 增加 author/description/generator meta(WeasyPrint 落入 PDF 属性,AI 生成内容标识 CR-05) - test_report_export.py:+2 单测(模板 meta 渲染 + PDF 产物 Info 字典解压断言,不新增依赖) 多 agent 评审 R3/R4 产物;单测 576 passed
260 lines
9.3 KiB
Django/Jinja
260 lines
9.3 KiB
Django/Jinja
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>{{ meta.brand }} · {{ meta.title }}</title>
|
|
<!-- CR-05 文件元数据隐式标识(N2'-①):WeasyPrint 会把 author/description/generator
|
|
映射进 PDF 文档属性(Author/Subject/Creator);27 位内容编号随合规出口 publish_asset() 落地,此处不伪造 -->
|
|
<meta name="author" content="美搭(AI 生成内容)">
|
|
<meta name="description" content="本报告由人工智能生成,仅供参考。报告 {{ meta.report_id }} v{{ meta.version }},生成于 {{ date_text }}">
|
|
<meta name="generator" content="美搭 AI 报告生成服务">
|
|
<style>
|
|
/* ── 页面与页脚:A4 分页,页脚带 AI 标识 + 免责语(合规 §合规与安全方案)── */
|
|
@page {
|
|
size: A4;
|
|
margin: 18mm 16mm 22mm 16mm;
|
|
@bottom-center {
|
|
content: "AI 生成内容,仅供参考 · 本报告由人工智能生成,不构成专业形象顾问意见 · 第 " counter(page) " / " counter(pages) " 页";
|
|
font-size: 7.5pt;
|
|
color: #9a8f8a;
|
|
}
|
|
}
|
|
@page cover {
|
|
/* 封面不显示页脚页码,保持仪式感 */
|
|
@bottom-center { content: none; }
|
|
}
|
|
|
|
:root {
|
|
--accent: #a03d5a;
|
|
--accent-soft: #f6ecef;
|
|
--ink: #2b2624;
|
|
--muted: #8a807b;
|
|
--line: #e4dbd6;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
html, body {
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: 'Noto Sans CJK SC', sans-serif;
|
|
color: var(--ink);
|
|
font-size: 10pt;
|
|
line-height: 1.75;
|
|
}
|
|
|
|
/* ── 每页水印:fixed 元素在 WeasyPrint 里逐页重复 ─────────────────────── */
|
|
.watermark {
|
|
position: fixed;
|
|
top: 42%;
|
|
left: 8%;
|
|
width: 84%;
|
|
text-align: center;
|
|
transform: rotate(-28deg);
|
|
font-size: 52pt;
|
|
font-weight: bold;
|
|
letter-spacing: 12pt;
|
|
color: rgba(160, 61, 90, 0.07);
|
|
}
|
|
|
|
/* ── 封面 ─────────────────────────────────────────────────────────────── */
|
|
.cover {
|
|
page: cover;
|
|
page-break-after: always;
|
|
height: 240mm;
|
|
text-align: center;
|
|
padding-top: 62mm;
|
|
}
|
|
.cover .brand {
|
|
font-size: 15pt;
|
|
letter-spacing: 10pt;
|
|
color: var(--accent);
|
|
margin-bottom: 6mm;
|
|
}
|
|
.cover h1 {
|
|
font-size: 30pt;
|
|
font-weight: bold;
|
|
letter-spacing: 4pt;
|
|
margin: 0 0 10mm 0;
|
|
}
|
|
.cover .rule {
|
|
width: 42mm;
|
|
border-top: 1.2pt solid var(--accent);
|
|
margin: 0 auto 10mm auto;
|
|
}
|
|
.cover .date { color: var(--muted); font-size: 11pt; letter-spacing: 2pt; }
|
|
.cover .version { color: var(--muted); font-size: 9pt; margin-top: 2mm; }
|
|
.cover .ai-badge {
|
|
display: inline-block;
|
|
margin-top: 52mm;
|
|
padding: 2.5mm 8mm;
|
|
border: 1pt solid var(--accent);
|
|
border-radius: 12mm;
|
|
color: var(--accent);
|
|
font-size: 10pt;
|
|
letter-spacing: 3pt;
|
|
}
|
|
|
|
/* ── 章节 ─────────────────────────────────────────────────────────────── */
|
|
h2.chapter {
|
|
font-size: 15pt;
|
|
color: var(--accent);
|
|
border-left: 3.5pt solid var(--accent);
|
|
padding-left: 4mm;
|
|
margin: 9mm 0 4mm 0;
|
|
page-break-after: avoid;
|
|
}
|
|
h3.sec-title {
|
|
font-size: 12pt;
|
|
margin: 7mm 0 2.5mm 0;
|
|
page-break-after: avoid;
|
|
}
|
|
p.para { margin: 0 0 3mm 0; text-align: justify; }
|
|
|
|
.summary-box {
|
|
background: var(--accent-soft);
|
|
border-radius: 3mm;
|
|
padding: 5mm 6mm;
|
|
margin: 4mm 0 2mm 0;
|
|
page-break-inside: avoid;
|
|
}
|
|
|
|
/* ── 核心结论卡片(档案摘要页)────────────────────────────────────────── */
|
|
.core-grid { width: 100%; border-collapse: separate; border-spacing: 2.5mm; }
|
|
.core-card {
|
|
background: #fbf8f7;
|
|
border: 0.6pt solid var(--line);
|
|
border-radius: 2.5mm;
|
|
padding: 3.5mm 4mm;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
page-break-inside: avoid;
|
|
}
|
|
.core-card .dim-name { font-size: 8.5pt; color: var(--muted); letter-spacing: 1pt; }
|
|
.core-card .dim-value { font-size: 11.5pt; font-weight: bold; margin: 1mm 0; }
|
|
.core-card .dim-value::before { content: "✓ "; color: var(--accent); }
|
|
|
|
/* ── 置信度条 ─────────────────────────────────────────────────────────── */
|
|
.conf { background: #eee4e0; border-radius: 2mm; height: 2.2mm; width: 26mm; display: inline-block; vertical-align: middle; }
|
|
.conf .fill { background: var(--accent); border-radius: 2mm; height: 2.2mm; }
|
|
.conf-num { font-size: 8pt; color: var(--muted); margin-left: 1.5mm; }
|
|
|
|
/* ── 色卡 ─────────────────────────────────────────────────────────────── */
|
|
.swatch { display: inline-block; width: 4.5mm; height: 4.5mm; border-radius: 1mm;
|
|
border: 0.4pt solid #c9bcb6; margin-right: 1mm; vertical-align: middle; }
|
|
.swatch-name { font-size: 8pt; color: var(--muted); margin-right: 2.5mm; }
|
|
|
|
/* ── 32 维明细表 ──────────────────────────────────────────────────────── */
|
|
table.dims { width: 100%; border-collapse: collapse; font-size: 8.5pt; margin-top: 3mm; }
|
|
table.dims th {
|
|
background: var(--accent);
|
|
color: #fff;
|
|
font-weight: normal;
|
|
padding: 2mm 2.5mm;
|
|
text-align: left;
|
|
}
|
|
table.dims td {
|
|
border-bottom: 0.5pt solid var(--line);
|
|
padding: 2mm 2.5mm;
|
|
vertical-align: top;
|
|
}
|
|
table.dims tr { page-break-inside: avoid; }
|
|
table.dims tr.skipped td { color: #b3a9a4; background: #faf8f7; }
|
|
.dim-no { color: var(--muted); width: 8mm; }
|
|
.dim-name { width: 24mm; font-weight: bold; }
|
|
.dim-evidence { color: var(--muted); font-size: 8pt; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="watermark">AI 生成内容</div>
|
|
|
|
<!-- ── 封面 ─────────────────────────────────────────────────────────── -->
|
|
<section class="cover">
|
|
<div class="brand">{{ meta.brand }}</div>
|
|
<h1>{{ meta.title }}</h1>
|
|
<div class="rule"></div>
|
|
<div class="date">{{ date_text }}</div>
|
|
<div class="version">报告编号 {{ meta.report_id }} · 第 {{ meta.version }} 版</div>
|
|
<div class="ai-badge">AI 生成内容</div>
|
|
</section>
|
|
|
|
<!-- ── 档案摘要 ───────────────────────────────────────────────────────── -->
|
|
<section>
|
|
<h2 class="chapter">档案摘要</h2>
|
|
{% if summary %}
|
|
<div class="summary-box">
|
|
<p class="para" style="margin:0">{{ summary }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if core_dimensions %}
|
|
<table class="core-grid">
|
|
{% for pair in core_dimensions | batch(2) %}
|
|
<tr>
|
|
{% for dim in pair %}
|
|
<td class="core-card">
|
|
<div class="dim-name">{{ dim.name_cn }}</div>
|
|
<div class="dim-value">{{ dim.value_text }}</div>
|
|
{% if dim.confidence_pct is not none %}
|
|
<span class="conf"><span class="fill" style="width: {{ dim.confidence_pct }}%"></span></span>
|
|
<span class="conf-num">置信度 {{ dim.confidence_pct }}%</span>
|
|
{% endif %}
|
|
{% for sw in dim.swatches %}
|
|
<span class="swatch" style="background: {{ sw.hex }}"></span><span class="swatch-name">{{ sw.name }}</span>
|
|
{% endfor %}
|
|
</td>
|
|
{% endfor %}
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<!-- ── 正文(copy.sections 全文渲染)───────────────────────────────────── -->
|
|
{% if sections %}
|
|
<section>
|
|
<h2 class="chapter">专属解读</h2>
|
|
{% for section in sections %}
|
|
<h3 class="sec-title">{{ loop.index }}. {{ section.title }}</h3>
|
|
{% for para in section.paragraphs %}
|
|
<p class="para">{{ para }}</p>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- ── 32 维诊断明细 ──────────────────────────────────────────────────── -->
|
|
{% if dimensions %}
|
|
<section>
|
|
<h2 class="chapter">诊断明细({{ ok_count }}/{{ dimensions | length }} 项已覆盖)</h2>
|
|
<table class="dims">
|
|
<thead>
|
|
<tr><th class="dim-no">#</th><th class="dim-name">维度</th><th>结论</th><th style="width:30mm">置信度</th><th style="width:52mm">依据</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for dim in dimensions %}
|
|
<tr class="{{ 'skipped' if dim.skipped else '' }}">
|
|
<td class="dim-no">{{ dim.number }}</td>
|
|
<td class="dim-name">{{ dim.name_cn }}</td>
|
|
<td>
|
|
{{ dim.value_text }}
|
|
{% for sw in dim.swatches %}
|
|
<span class="swatch" style="background: {{ sw.hex }}"></span><span class="swatch-name">{{ sw.name }}</span>
|
|
{% endfor %}
|
|
</td>
|
|
<td>
|
|
{% if dim.confidence_pct is not none %}
|
|
<span class="conf"><span class="fill" style="width: {{ dim.confidence_pct }}%"></span></span>
|
|
<span class="conf-num">{{ dim.confidence_pct }}%</span>
|
|
{% else %}—{% endif %}
|
|
</td>
|
|
<td class="dim-evidence">{{ dim.evidence }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
{% endif %}
|
|
|
|
</body>
|
|
</html>
|