Files
tyapi-server/resources/qiye.html
2026-03-10 19:15:54 +08:00

2187 lines
95 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>企业全景报告</title>
<style>
:root {
--primary: #2563eb;
--primary-dark: #1d4ed8;
--text: #1f2933;
--text-muted: #6b7280;
--border: #e5e7eb;
--bg-card: #fff;
--bg-page: #f5f7fa;
--danger: #dc2626;
--success: #16a34a;
--warn: #d97706;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI",
"PingFang SC", "Microsoft YaHei", sans-serif;
margin: 0;
background: var(--bg-page);
color: var(--text);
line-height: 1.5;
}
.page {
max-width: 1100px;
margin: 24px auto 40px;
padding: 16px 24px 40px;
background: #ffffff;
border-radius: 18px;
border: 1px solid var(--border);
}
.top-nav {
display: none;
}
.top-nav-title {
font-weight: 600;
color: #111827;
}
.top-nav-links {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.top-nav-links a {
padding: 0;
border-radius: 0;
background: transparent;
color: var(--primary);
text-decoration: none;
border: none;
cursor: default;
pointer-events: none; /* 去掉锚点点击功能 */
}
.top-nav-links a:hover {
background: transparent;
}
.header {
background: linear-gradient(
135deg,
var(--primary),
var(--primary-dark)
);
color: #fff;
padding: 24px 28px;
border-radius: 18px;
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 16px;
}
.header-main {
min-width: 0;
}
.header-main h1 {
margin: 0 0 8px;
font-size: 26px;
word-break: break-all;
}
.header-main .subtitle {
opacity: 0.9;
font-size: 14px;
word-break: break-all;
}
.header-tags {
margin-top: 8px;
}
.tag {
display: inline-block;
padding: 4px 10px;
border-radius: 999px;
font-size: 12px;
margin-right: 6px;
margin-bottom: 4px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.header-score {
text-align: right;
min-width: 140px;
}
.header-actions {
margin-top: 8px;
}
.print-btn {
display: inline-flex;
align-items: center;
padding: 6px 12px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.7);
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-size: 12px;
cursor: pointer;
transition:
background 0.15s ease,
color 0.15s ease,
transform 0.1s ease;
}
.print-btn:hover {
background: #ffffff;
color: var(--primary-dark);
transform: translateY(-1px);
}
.header-score .score {
font-size: 32px;
font-weight: 700;
}
.header-score .label {
font-size: 13px;
opacity: 0.85;
}
.report-sections {
margin-top: 24px;
}
.section-card {
background: transparent;
border-radius: 0;
padding: 18px 0 22px;
/* 去掉阴影,保持简洁报告风格 */
box-shadow: none !important;
border: none;
border-bottom: 1px solid var(--border);
scroll-margin-top: 16px;
}
.section-card:last-of-type {
border-bottom: none;
}
.section-card + .section-card {
margin-top: 12px; /* 大模块上下留间距 */
}
.section-card h2 {
margin: 0 0 16px;
font-size: 17px;
border-left: 4px solid var(--primary);
padding-left: 10px;
color: #111827;
}
.section-card h3 {
margin: 16px 0 8px;
font-size: 14px;
color: #374151;
font-weight: 600;
}
.section-card h3:first-of-type {
margin-top: 0;
}
/* 保持单列布局,模块上下排列,便于阅读和导出 */
.kv-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.kv-table th,
.kv-table td {
padding: 8px 10px;
vertical-align: top;
border-bottom: 1px solid #f3f4f6;
}
.kv-table th {
width: 140px;
color: var(--text-muted);
font-weight: 500;
text-align: left;
}
.kv-table td {
word-break: break-word;
}
/* 主体概览:多列网格,节省纵向空间 */
.kv-grid {
--cols: 3;
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
gap: 10px 24px;
font-size: 13px;
}
@media (max-width: 768px) {
.kv-grid {
--cols: 2;
}
}
.kv-grid .kv-pair {
display: flex;
flex-wrap: wrap;
gap: 4px 6px;
padding: 6px 0;
border-bottom: 1px solid #f3f4f6;
align-items: baseline;
}
.kv-grid .kv-pair .k {
color: var(--text-muted);
font-weight: 500;
flex-shrink: 0;
}
.kv-grid .kv-pair .k::after {
content: "";
}
.kv-grid .kv-pair .v {
word-break: break-word;
min-width: 0;
}
.item-list {
list-style: none;
padding: 0;
margin: 0;
}
.item-list li {
padding: 10px 12px;
border-bottom: 1px dashed var(--border);
font-size: 13px;
}
.item-list li:last-child {
border-bottom: none;
}
.item-list li .item-title {
font-weight: 600;
margin-bottom: 4px;
}
.item-list li .item-meta {
color: var(--text-muted);
font-size: 12px;
}
.item-block {
background: #f9fafb;
border-radius: 8px;
padding: 10px 12px;
margin-bottom: 8px;
font-size: 13px;
}
.item-block .item-kv {
margin: 4px 0;
}
.item-block .item-kv span.k {
color: var(--text-muted);
margin-right: 8px;
}
.empty-hint {
color: var(--text-muted);
font-size: 13px;
padding: 12px 0;
}
.count-hint {
font-size: 12px;
color: var(--text-muted);
margin-bottom: 8px;
}
.pill {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
margin-left: 6px;
}
.pill.low {
background: #ecfdf3;
color: #166534;
}
.pill.mid {
background: #fef9c3;
color: #92400e;
}
.pill.high {
background: #fee2e2;
color: #b91c1c;
}
.risk-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
font-size: 13px;
}
.risk-row .ok {
color: var(--success);
}
.risk-row .bad {
color: var(--danger);
}
/* 风险点一览:多列彩色标签 */
.risk-points-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 6px 12px;
margin-top: 4px;
}
@media (max-width: 768px) {
.risk-points-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.risk-point {
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.risk-point-name {
color: var(--text-muted);
}
.risk-status-ok {
color: var(--success);
font-weight: 600;
}
.risk-status-bad {
color: var(--danger);
font-weight: 600;
}
.raw-section summary {
cursor: pointer;
padding: 8px 0;
font-weight: 600;
color: var(--text-muted);
}
.raw-section pre {
font-size: 11px;
background: #0f172a;
color: #e2e8f0;
padding: 12px;
border-radius: 8px;
overflow: auto;
max-height: 320px;
}
@media (max-width: 768px) {
.header {
flex-direction: column;
}
.header-score {
text-align: left;
}
}
/* 打印 / 导出 PDF 友好布局 */
@media print {
body {
background: #ffffff !important;
margin: 0;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.page {
max-width: none;
margin: 0;
padding: 10mm 12mm 12mm;
}
/* 导出 PDF 时隐藏导航 */
.top-nav {
display: none !important;
}
.print-btn {
display: none !important;
}
/* 抬头简化为白底、去圆角和阴影 */
.header {
background: #ffffff !important;
color: #111827 !important;
border-radius: 0;
box-shadow: none;
padding: 10px 0 8px;
border-bottom: 1px solid #e5e7eb;
}
.header-score .score {
font-size: 26px;
}
/* 主体卡片:去阴影,避免被分页截断 */
.section-card {
box-shadow: none;
border-radius: 0;
page-break-inside: avoid;
break-inside: avoid;
}
.section-card h2 {
page-break-after: avoid;
}
.item-list li {
page-break-inside: avoid;
break-inside: avoid;
}
/* 原始 JSON 展示在 PDF 中通常不需要过高容器限制 */
.raw-section pre {
max-height: none;
page-break-inside: avoid;
break-inside: avoid;
}
a {
color: #111827;
text-decoration: none;
}
}
</style>
</head>
<body>
<div class="page">
<!-- 隐藏的导航容器,仅用于脚本生成目录,页面不展示 -->
<nav class="top-nav">
<div id="navLinks"></div>
</nav>
<header class="header">
<div class="header-main">
<h1 class="report-title">企业全景报告</h1>
<div class="subtitle">
<span id="entName">企业名称</span>
</div>
<div class="subtitle">
<span id="creditCodeLabel">统一社会信用代码:-</span> ·
<span id="entStatusLabel">经营状态:-</span> ·
<span id="entTypeLabel">企业类型:-</span>
</div>
<div class="header-tags" id="headerTags"></div>
<div
style="opacity: 0.9; font-size: 12px; margin-top: 6px"
id="reportTimeLabel"
>
报告生成时间:-
</div>
</div>
<div class="header-score">
<div class="score" id="overallScore">--</div>
<div class="label">
综合评分
<span id="riskLevelPill" class="pill low">风险:-</span>
</div>
<div class="header-actions">
<button id="btnSavePdf" class="print-btn">保存为 PDF</button>
</div>
</div>
</header>
<main id="reportSections" class="report-sections"></main>
</div>
<!-- 前端截图并生成 PDF 所需依赖 -->
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js"></script>
<script>
(function () {
// 后端通过模板引擎注入的企业报告数据QYGLJ1U9 聚合结果)
// ReportJSON 为合法的 JSON 对象字面量
var reportData = {{ .ReportJSON }};
var sectionOrder = [
"riskOverview",
"basic",
"branches",
"shareholding",
"controller",
"beneficiaries",
"investments",
"guarantees",
"management",
"assets",
"licenses",
"activities",
"inspections",
"risks",
"timeline",
"listed",
];
var sectionTitles = {
riskOverview: "风险情况(综合分析)",
basic: "一、主体概览(企业基础信息)",
branches: "二、分支机构",
shareholding: "三、股权与控制",
controller: "四、实际控制人",
beneficiaries: "五、最终受益人",
investments: "六、对外投资",
guarantees: "七、对外担保",
management: "八、人员与组织",
assets: "九、资产与经营(年报)",
licenses: "十、行政许可与资质",
activities: "十一、经营活动",
inspections: "十二、抽查检查",
risks: "十三、风险与合规",
timeline: "十四、发展时间线",
listed: "十五、上市信息",
};
// 保持所有板块单列纵向排布
var keyLabels = {
entName: "企业名称",
creditCode: "统一社会信用代码",
regNo: "注册号",
orgCode: "组织机构代码",
entType: "企业类型",
entTypeCode: "企业类型编码",
entityTypeCode: "实体类型编码",
establishDate: "成立日期",
registeredCapital: "注册资本",
regCap: "注册资本",
regCapCurrency: "注册资本币种",
regCapCurrencyCode: "注册资本币种代码",
regOrg: "登记机关",
regOrgCode: "注册地址行政编号",
regProvince: "所在省份",
provinceCode: "所在省份编码",
regCity: "所在城市",
regCityCode: "所在城市编码",
regDistrict: "所在区/县",
districtCode: "所在区/县编码",
address: "住址",
postalCode: "邮编",
legalRepresentative: "法定代表人",
compositionForm: "组成形式",
approvedBusinessItem: "许可经营项目",
status: "经营状态",
statusCode: "经营状态编码",
operationPeriodFrom: "经营期限自",
operationPeriodTo: "经营期限至",
approveDate: "核准日期",
cancelDate: "注销日期",
revokeDate: "吊销日期",
cancelReason: "注销原因",
revokeReason: "吊销原因",
oldNames: "曾用名",
businessScope: "经营业务范围",
lastAnnuReportYear: "最后年检年度",
name: "名称",
type: "类型",
percent: "比例",
reason: "判定依据",
source: "来源",
id: "标识",
path: "控制路径",
topHolderName: "第一大股东",
topHolderPercent: "第一大股东持股",
top5TotalPercent: "前五大合计",
shareholderCount: "股东总数",
currency: "币种",
hasEquityPledges: "存在股权出质",
riskLevel: "风险等级",
riskScore: "风险得分",
totalCount: "对外投资数量",
totalAmount: "投资总额(万)",
employeeCount: "从业人数",
femaleEmployeeCount: "女性从业人数",
yearReportId: "年报标识",
investor: "股东/投资人",
changeDate: "变更日期",
detailBefore: "变更前",
detailAfter: "变更后",
changeType: "变更类型",
updateDate: "变更日期",
updateContent: "变更内容",
date: "日期",
title: "标题",
overallScore: "总体评分",
tags: "标签",
isListed: "是否上市",
company: "上市公司信息",
stock: "股票信息",
};
function label(key) {
return keyLabels[key] || key;
}
function esc(s) {
if (s == null || s === "") return "-";
return String(s);
}
// 职务字段格式化:部分数据源为英文/代码,如 EXEC、EXEC&&LR、HEXEC
function formatPosition(position) {
if (!position) return "";
var p = String(position).trim();
if (!p) return "";
// 将可能存在的转义形式统一为普通字符
p = p.replace(/\\u0026/gi, "&");
// 已知常见职务代码映射
var dict = {
EXEC: "高管",
LR: "法定代表人",
HEXEC: "执行合伙人",
};
// 拆分出可能的代码片段(用非字母字符作为分隔)
var parts = p
.split(/[^A-Z]/i)
.map(function (s) {
return s.trim();
})
.filter(Boolean);
if (parts.length === 0) {
return p;
}
var mapped = parts.map(function (code) {
return dict[code] || code;
});
// 多个角色用顿号连接,例如 "高管、法定代表人"
return mapped.join("、");
}
function renderCell(v) {
if (v == null || v === "") return "-";
if (typeof v === "boolean") return v ? "是" : "否";
if (typeof v === "number") return v;
if (typeof v === "object") {
if (Array.isArray(v))
return v.length ? "共 " + v.length + " 条" : "-";
return "对象";
}
return esc(v);
}
// 传入 keys 时:渲染所有键,无值显示 -;未传 keys 时:按对象自身键渲染
function kvTable(obj, keys) {
if (!obj || typeof obj !== "object")
return "<p class='empty-hint'>暂无数据</p>";
var keyList = keys && keys.length ? keys : Object.keys(obj);
var rows = keyList.map(function (k) {
return (
"<tr><th>" +
esc(label(k)) +
"</th><td>" +
renderCell(obj[k]) +
"</td></tr>"
);
});
return rows.length
? "<table class='kv-table'>" +
rows.join("") +
"</table>"
: "<p class='empty-hint'>暂无数据</p>";
}
// 多列网格展示 key-value用于主体概览等需节省纵向空间的区块
function kvGrid(obj, keys, cols) {
if (!obj || typeof obj !== "object")
return "<p class='empty-hint'>暂无数据</p>";
var keyList = keys && keys.length ? keys : Object.keys(obj);
var parts = keyList.map(function (k) {
return (
"<div class='kv-pair'><span class='k'>" +
esc(label(k)) +
"</span><span class='v'>" +
renderCell(obj[k]) +
"</span></div>"
);
});
var c = cols || 3;
return parts.length
? "<div class='kv-grid' style='--cols:" +
c +
"'>" +
parts.join("") +
"</div>"
: "<p class='empty-hint'>暂无数据</p>";
}
function itemList(arr, fn) {
if (!Array.isArray(arr) || !arr.length)
return "<p class='empty-hint'>暂无数据</p>";
var html =
"<p class='count-hint'>共 " +
arr.length +
" 条</p><ul class='item-list'>";
arr.forEach(function (item, i) {
html +=
"<li>" +
(fn
? fn(item, i)
: typeof item === "object"
? JSON.stringify(item)
: esc(item)) +
"</li>";
});
return html + "</ul>";
}
function renderBasic(v) {
if (!v) return "<p class='empty-hint'>暂无基础信息</p>";
// 与《企业全量信息核验V2_返回字段说明》BASIC 字段顺序对应
var keys = [
"entName",
"creditCode",
"regNo",
"orgCode",
"entType",
"entTypeCode",
"entityTypeCode",
"establishDate",
"registeredCapital",
"regCap",
"regCapCurrency",
"regCapCurrencyCode",
"regOrg",
"regOrgCode",
"regProvince",
"provinceCode",
"regCity",
"regCityCode",
"regDistrict",
"districtCode",
"address",
"postalCode",
"legalRepresentative",
"compositionForm",
"approvedBusinessItem",
"status",
"statusCode",
"operationPeriodFrom",
"operationPeriodTo",
"approveDate",
"cancelDate",
"revokeDate",
"cancelReason",
"revokeReason",
"oldNames",
"businessScope",
"lastAnnuReportYear",
];
var o = {};
for (var k in v) o[k] = v[k];
// 曾用名:统一为字符串展示,无数据时显示 -(保证「曾用名」行始终出现)
o.oldNames =
v.oldNames &&
Array.isArray(v.oldNames) &&
v.oldNames.length
? v.oldNames.join("")
: v.oldNames || "-";
return kvGrid(o, keys, 3);
}
function renderBranches(v) {
return itemList(v, function (b) {
return (
"<div class='item-title'>" +
esc(b.name) +
"</div>" +
"<div class='item-meta'>注册号:" +
esc(b.regNo) +
";信用代码:" +
esc(b.creditCode) +
";登记机关:" +
esc(b.regOrg) +
"</div>"
);
});
}
function renderShareholding(v) {
if (!v) return "<p class='empty-hint'>暂无股权信息</p>";
var html = "";
var summaryKeys = [
"shareholderCount",
"registeredCapital",
"currency",
"topHolderName",
"topHolderPercent",
"top5TotalPercent",
"hasEquityPledges",
];
var hasAny = summaryKeys.some(function (k) {
return (
v[k] !== undefined && v[k] !== null && v[k] !== ""
);
});
if (hasAny) {
var rows = [];
// 股东总数
if (v.shareholderCount != null) {
rows.push({
k: "股东总数",
v: esc(v.shareholderCount),
});
}
// 注册资本(加单位元)
if (v.registeredCapital != null) {
rows.push({
k: "注册资本",
v: esc(v.registeredCapital) + " 元",
});
}
// 币种
if (v.currency) {
rows.push({
k: "币种",
v: esc(v.currency),
});
}
// 第一大股东及持股
if (v.topHolderName || v.topHolderPercent != null) {
rows.push({
k: "第一大股东",
v: esc(v.topHolderName || "-"),
});
if (v.topHolderPercent != null) {
rows.push({
k: "第一大股东持股",
v:
(v.topHolderPercent * 100).toFixed(1) +
"%",
});
}
}
// 前五大合计
if (v.top5TotalPercent != null) {
rows.push({
k: "前五大合计",
v: (v.top5TotalPercent * 100).toFixed(1) + "%",
});
}
// 是否存在股权出质
if (v.hasEquityPledges != null) {
rows.push({
k: "存在股权出质",
v: v.hasEquityPledges ? "是" : "否",
});
}
if (rows.length) {
var t =
"<table class='kv-table'><tbody>" +
rows
.map(function (r) {
return (
"<tr><th>" +
esc(r.k) +
"</th><td>" +
esc(r.v) +
"</td></tr>"
);
})
.join("") +
"</tbody></table>";
html += "<h3>股权汇总</h3>" + t;
}
}
// 股东明细:优先展示来自股权全景/全量信息的有效字段,避免大段空白
html +=
"<h3>股东明细</h3>" +
itemList(v.shareholders, function (sh) {
var pct =
(sh.ownershipPercent != null
? sh.ownershipPercent
: sh.sharePercent) || 0;
var title =
"<div class='item-title'>" +
esc(sh.name) +
"" +
(pct * 100).toFixed(1) +
"%</div>";
var parts = [];
if (sh.type) {
parts.push("类型:" + esc(sh.type));
}
if (
sh.subscribedAmount != null &&
sh.subscribedAmount !== ""
) {
var sub = "认缴:" + esc(sh.subscribedAmount);
if (
sh.subscribedCurrency ||
sh.subscribedCurrencyCode
) {
sub +=
" " +
esc(sh.subscribedCurrency || "") +
(sh.subscribedCurrencyCode
? "" +
esc(sh.subscribedCurrencyCode) +
""
: "");
}
parts.push(sub);
}
if (sh.subscribedDate) {
parts.push("认缴日:" + esc(sh.subscribedDate));
}
if (
sh.subscribedMethod ||
sh.subscribedMethodCode
) {
var subm =
"认缴方式:" +
esc(sh.subscribedMethod || "");
if (sh.subscribedMethodCode) {
subm +=
"" +
esc(sh.subscribedMethodCode) +
"";
}
parts.push(subm);
}
if (sh.paidAmount != null && sh.paidAmount !== "") {
parts.push("实缴:" + esc(sh.paidAmount));
}
if (sh.paidDate) {
parts.push("实缴日:" + esc(sh.paidDate));
}
if (sh.paidMethod) {
parts.push("实缴方式:" + esc(sh.paidMethod));
}
if (sh.creditCode) {
parts.push(
"股东信用代码:" + esc(sh.creditCode),
);
}
if (sh.regNo) {
parts.push("股东注册号:" + esc(sh.regNo));
}
if (sh.isHistory != null) {
parts.push(
"是否历史:" + (sh.isHistory ? "是" : "否"),
);
}
var meta =
parts.length > 0
? "<div class='item-meta'>" +
parts.join("") +
"</div>"
: "";
return title + meta;
});
html +=
"<h3>股权变更记录</h3>" +
itemList(v.equityChanges, function (e) {
var name = e.shareholderName;
// 如果是纯数字可能是内部ID在标题中不展示
var isId =
typeof name === "string" &&
/^[0-9]+$/.test(name);
return (
"<div class='item-title'>" +
esc(e.changeDate) +
(isId || !name
? ""
: " " + esc(e.shareholderName)) +
"</div>" +
"<div class='item-meta'>变更前:" +
esc(e.percentBefore) +
" → 变更后:" +
esc(e.percentAfter) +
"</div>"
);
});
html +=
"<h3>股权出质</h3>" +
itemList(v.equityPledges, function (e) {
return (
"<div class='item-title'>" +
esc(e.regNo) +
" " +
esc(e.pledgor) +
" → " +
esc(e.pledgee) +
"</div>" +
"<div class='item-meta'>出质数额:" +
esc(e.pledgedAmount) +
" 元" +
";登记日:" +
esc(e.regDate) +
";状态:" +
esc(e.status) +
"</div>"
);
});
html +=
"<h3>实缴出资明细</h3>" +
itemList(v.paidInDetails, function (e) {
return (
"<div class='item-title'>" +
esc(e.investor) +
"</div>" +
"<div class='item-meta'>日期:" +
esc(e.paidDate) +
";方式:" +
esc(e.paidMethod) +
";累计实缴:" +
esc(e.accumulatedPaid) +
" 万元" +
"</div>"
);
});
html +=
"<h3>认缴出资明细</h3>" +
itemList(v.subscribedCapitalDetails, function (e) {
return (
"<div class='item-title'>" +
esc(e.investor) +
"</div>" +
"<div class='item-meta'>认缴日:" +
esc(e.subscribedDate) +
";方式:" +
esc(e.subscribedMethod) +
";累计认缴:" +
esc(e.accumulatedSubscribed) +
" 元" +
"</div>"
);
});
return html;
}
function renderController(v) {
if (!v) return "<p class='empty-hint'>暂无实控人信息</p>";
var html = kvTable(v, [
"id",
"name",
"type",
"percent",
"reason",
"source",
]);
if (v.path && typeof v.path === "object") {
html += "<h3>控制路径</h3>";
if (v.path.nodes && v.path.nodes.length) {
html +=
"<p class='count-hint'>节点 " +
v.path.nodes.length +
" 个</p>";
v.path.nodes.forEach(function (n) {
var id =
n.entityId != null ? n.entityId : n.uid;
html +=
"<div class='item-block'><span class='k'>" +
esc(n.label) +
"</span>" +
esc(n.name) +
(id != null
? "id: " + esc(id) + ""
: "") +
"</div>";
});
}
if (v.path.links && v.path.links.length) {
html +=
"<p class='count-hint'>链接 " +
v.path.links.length +
" 条</p>";
}
}
return html;
}
function renderBeneficiaries(v) {
return itemList(v, function (b) {
var pct = "-";
if (b.percent != null && b.percent !== 0) {
pct = (b.percent * 100).toFixed(1) + "%";
}
var title =
"<div class='item-title'>" +
esc(b.name) +
(pct !== "-" ? "" + pct + "" : "") +
"</div>";
var parts = [];
if (b.type) {
parts.push("类型:" + esc(b.type));
}
if (b.reason) {
parts.push("原因:" + esc(b.reason));
}
var meta =
parts.length > 0
? "<div class='item-meta'>" +
parts.join("") +
"</div>"
: "";
return title + meta;
});
}
function renderInvestments(v) {
if (!v) return "<p class='empty-hint'>暂无投资信息</p>";
var html =
"<h3>汇总</h3>" +
kvTable(v, ["totalCount", "totalAmount"]);
html +=
"<h3>对外投资列表</h3>" +
itemList(v.list || v.entities, function (inv) {
var pct =
inv.investPercent != null
? (inv.investPercent * 100).toFixed(1)
: "-";
var title =
"<div class='item-title'>" +
esc(inv.entName) +
"" +
pct +
"%</div>";
var parts = [];
if (inv.creditCode) {
parts.push("信用代码:" + esc(inv.creditCode));
}
if (
inv.investAmount != null &&
inv.investAmount !== ""
) {
var amt =
"投资额:" + esc(inv.investAmount) + " 万";
if (inv.investCurrency) {
amt +=
"(单位:" +
esc(inv.investCurrency) +
"";
}
parts.push(amt);
}
if (inv.entStatus) {
parts.push("状态:" + esc(inv.entStatus));
}
var meta =
parts.length > 0
? "<div class='item-meta'>" +
parts.join("") +
"</div>"
: "";
return title + meta;
});
html +=
"<h3>法定代表人个人对外投资</h3>" +
itemList(
v.legalRepresentativeInvestments ||
v.legalPersonInvestments,
function (inv) {
var pct = "-";
if (
inv.investPercent != null &&
Number(inv.investPercent) !== 0
) {
pct =
(inv.investPercent * 100).toFixed(1) +
"%";
}
var title =
"<div class='item-title'>" +
esc(inv.entName) +
(pct !== "-" ? "" + pct + "" : "") +
"</div>";
var parts = [];
if (inv.creditCode) {
parts.push(
"信用代码:" + esc(inv.creditCode),
);
}
// 优先展示投资额,其次在无投资额但有注册资本时展示注册资本
if (
inv.investAmount != null &&
inv.investAmount !== "" &&
Number(inv.investAmount) !== 0
) {
parts.push(
"投资额:" +
esc(inv.investAmount) +
" 万",
);
} else if (
inv.regCap != null &&
inv.regCap !== "" &&
Number(inv.regCap) !== 0
) {
parts.push(
"注册资本:" +
esc(inv.regCap) +
" 万(企业注册资本)",
);
}
if (inv.entStatus) {
parts.push("状态:" + esc(inv.entStatus));
}
var meta =
parts.length > 0
? "<div class='item-meta'>" +
parts.join("") +
"</div>"
: "";
return title + meta;
},
);
return html;
}
function renderGuarantees(v) {
if (!Array.isArray(v) || !v.length) {
return "<p class='empty-hint'>暂无对外担保信息</p>";
}
// 只展示有关键字段的记录,避免一堆空行
var coreKeys = [
"mortgagor",
"creditor",
"principalAmount",
"principalKind",
"guaranteeType",
"periodFrom",
"periodTo",
];
var list = v.filter(function (g) {
return coreKeys.some(function (k) {
return g[k] != null && g[k] !== "";
});
});
if (!list.length) {
return (
"<p class='empty-hint'>存在 " +
v.length +
" 条年报披露的对外担保记录,但债务人、债权人等关键字段为空,暂无法展示详细明细。</p>"
);
}
return (
"<p class='count-hint'>共 " +
list.length +
" 条(年报披露)</p>" +
itemList(list, function (g) {
return (
"<div class='item-title'>" +
esc(g.mortgagor || "-") +
" → " +
esc(g.creditor || "-") +
"</div>" +
"<div class='item-meta'>主债权数额:" +
esc(g.principalAmount || "-") +
";种类:" +
esc(g.principalKind || "-") +
";保证方式:" +
esc(g.guaranteeType || "-") +
";期限:" +
esc(g.periodFrom || "-") +
" 至 " +
esc(g.periodTo || "-") +
"</div>"
);
})
);
}
function renderManagement(v) {
if (!v) return "<p class='empty-hint'>暂无人员信息</p>";
var html =
"<h3>高管列表</h3>" +
itemList(v.executives, function (e) {
return (
"<div class='item-title'>" +
esc(e.name) +
"</div><div class='item-meta'>" +
esc(formatPosition(e.position)) +
"</div>"
);
});
html +=
"<h3>从业与社保</h3>" +
kvTable(v, [
"employeeCount",
"femaleEmployeeCount",
"socialSecurity",
]);
html += "<h3>法定代表人其他任职</h3>";
var others =
v.legalRepresentativeOtherPositions ||
v.legalPersonOtherPositions;
html += itemList(others, function (f) {
var title =
"<div class='item-title'>" +
esc(f.entName) +
"</div>";
var parts = [];
if (f.position) {
parts.push(
"职务:" + esc(formatPosition(f.position)),
);
}
if (f.name) {
parts.push("姓名:" + esc(f.name));
}
if (f.entStatus) {
parts.push("企业状态:" + esc(f.entStatus));
}
if (f.creditCode) {
parts.push("信用代码:" + esc(f.creditCode));
}
if (f.regNo) {
parts.push("注册号:" + esc(f.regNo));
}
var meta =
parts.length > 0
? "<div class='item-meta'>" +
parts.join("") +
"</div>"
: "";
return title + meta;
});
return html;
}
function renderAssets(v) {
if (!v) return "<p class='empty-hint'>暂无资产数据</p>";
var years = Array.isArray(v.years) ? v.years : [];
if (!years.length)
return "<p class='empty-hint'>暂无资产数据</p>";
// 过滤掉所有核心字段都为空的年度记录,避免一堆空行
var coreKeys = [
"assetTotal",
"revenueTotal",
"netProfit",
"liabilityTotal",
"equityTotal",
"profitTotal",
];
var list = years.filter(function (y) {
return coreKeys.some(function (k) {
return y[k] != null && y[k] !== "";
});
});
if (!list.length) {
return "<p class='empty-hint'>存在年报资产经营记录,但资产总额、营收、净利润等核心字段为空,暂无法展示详细明细。</p>";
}
return (
"<h3>按年度资产经营数据</h3>" +
itemList(list, function (y) {
return (
"<div class='item-title'>" +
esc(y.year) +
" " +
esc(y.reportDate || "") +
"</div>" +
"<div class='item-meta'>资产总额:" +
esc(y.assetTotal || "-") +
";营收:" +
esc(y.revenueTotal || "-") +
";净利润:" +
esc(y.netProfit || "-") +
";负债:" +
esc(y.liabilityTotal || "-") +
"</div>"
);
})
);
}
function renderLicenses(v) {
if (!v) return "<p class='empty-hint'>暂无许可信息</p>";
var html =
"<h3>行政许可列表</h3>" +
itemList(v.permits, function (p) {
return (
"<div class='item-title'>" +
esc(p.name) +
"</div>" +
"<div class='item-meta'>有效期:" +
esc(p.valFrom) +
" ~ " +
esc(p.valTo) +
";许可机关:" +
esc(p.licAnth) +
"" +
esc(p.licItem) +
"</div>"
);
});
html +=
"<h3>许可变更记录</h3>" +
itemList(v.permitChanges, function (p) {
return (
"<div class='item-title'>" +
esc(p.changeDate) +
" " +
esc(p.changeType) +
"</div>" +
"<div class='item-meta'>变更前:" +
(p.detailBefore || "-").substring(0, 80) +
"…;变更后:" +
(p.detailAfter || "-").substring(0, 80) +
"…</div>"
);
});
html +=
"<h3>知识产权出质</h3>" +
itemList(v.ipPledges, function (i) {
return (
"<div class='item-block'>" +
(typeof i === "object"
? JSON.stringify(i)
: esc(i)) +
"</div>"
);
});
return html;
}
function renderActivities(v) {
if (!v) return "<p class='empty-hint'>暂无经营活动数据</p>";
// 招投标:对角色、类型做友好映射
var bidRoleMap = {
10: "招标人/采购人",
20: "中标人/供应商",
30: "代理机构",
};
var announceTypeMap = {
10: "招标公告",
20: "中标结果公告",
};
var html =
"<h3>招投标</h3>" +
itemList(v.bids, function (b) {
var t =
b.announcetitle ||
b.ANNOUNCETITLE ||
b.title ||
"-";
var roleCode = b.bidrole || b.BIDROLE;
var roleText =
bidRoleMap[Number(roleCode)] ||
(roleCode != null && roleCode !== ""
? "角色代码:" + roleCode
: "-");
var typeCode =
b.announceType ||
b.annoucetype ||
b.ANNOUNCETYPE;
var typeText =
announceTypeMap[Number(typeCode)] ||
(typeCode != null && typeCode !== ""
? "类型代码:" + typeCode
: "-");
return (
"<div class='item-title'>" +
esc(t) +
"</div>" +
"<div class='item-meta'>角色:" +
esc(roleText) +
";类型:" +
esc(typeText) +
"</div>"
);
});
html +=
"<h3>网站/网店</h3>" +
itemList(v.websites, function (w) {
return (
"<div class='item-title'>" +
esc(w.websitname || w.WEBSITNAME || w.name) +
"</div>" +
"<div class='item-meta'>类型:" +
esc(w.webtype || w.WEBTYPE) +
";域名:" +
esc(w.domain || w.DOMAIN) +
"</div>"
);
});
return html;
}
function renderInspections(v) {
return itemList(v, function (i) {
return (
"<div class='item-title'>" +
esc(i.inspectDate) +
" " +
esc(i.result) +
"</div>" +
"<div class='item-meta'>数据类型:" +
esc(i.dataType) +
";检查机关:" +
esc(i.regOrg) +
"</div>"
);
});
}
function renderRisks(v) {
if (!v) return "<p class='empty-hint'>暂无风险数据</p>";
var hasKeys = [
"hasCourtJudgments",
"hasJudicialAssists",
"hasDishonestDebtors",
"hasLimitHighDebtors",
"hasAdminPenalty",
"hasException",
"hasSeriousIllegal",
"hasTaxOwing",
"hasSeriousTaxIllegal",
"hasMortgage",
"hasEquityPledges",
"hasQuickCancel",
];
var hasLabels = [
"裁判文书",
"司法协助",
"失信被执行人",
"限高被执行人",
"行政处罚",
"经营异常",
"严重违法",
"欠税",
"重大税收违法",
"动产抵押",
"股权出质",
"简易注销",
];
var html = "<h3>风险项</h3><ul class='item-list'>";
hasKeys.forEach(function (k, idx) {
var val = v[k];
if (val === undefined)
val =
v[
k
.replace("Judgments", "JudicialCase")
.replace("Assists", "JudicialAid")
.replace("EquityPledges", "StockPawn")
];
html +=
"<li class='risk-row'><span>" +
hasLabels[idx] +
"</span><span class='" +
(val ? "bad" : "ok") +
"'>" +
(val ? "存在" : "未发现") +
"</span></li>";
});
html += "</ul>";
html +=
"<h3>裁判文书</h3>" +
itemList(
v.courtJudgments || v.judicialCases,
function (c) {
return (
"<div class='item-title'>" +
esc(
c.caseNumber ||
c.CASENUMBER ||
c.judicialDocumentId,
) +
"</div>" +
"<div class='item-meta'>案由:" +
esc(c.caseCause || c.CASECAUSE) +
";结果:" +
esc(c.judgeResult || c.JUDGERESULT) +
"</div>"
);
},
);
html +=
"<h3>司法协助</h3>" +
itemList(
v.judicialAssists || v.judicialAids,
function (a) {
return (
"<div class='item-title'>" +
esc(a.iname || a.INAME || a.marketName) +
"</div>" +
"<div class='item-meta'>法院:" +
esc(a.courtName || a.COURTNAME) +
";股权数额:" +
esc(a.shaream || a.SHAREAM) +
" 元</div>"
);
},
);
html +=
"<h3>失信被执行人</h3>" +
itemList(v.dishonestDebtors, function (d) {
return (
"<div class='item-title'>案号 " +
esc(d.caseNo) +
"</div>" +
"<div class='item-meta'>执行法院:" +
esc(d.execCourt) +
";履行情况:" +
esc(d.performanceStatus) +
";发布时间:" +
esc(d.publishDate) +
"</div>"
);
});
html +=
"<h3>限高被执行人</h3>" +
itemList(v.limitHighDebtors, function (x) {
return (
"<div class='item-title'>" +
esc(x.ah || x.caseNo) +
"</div>" +
"<div class='item-meta'>执行法院:" +
esc(x.zxfy || x.execCourt) +
"" +
esc(x.fbrq || x.publishDate) +
"</div>"
);
});
// 按案件类型展示涉诉信息(来自企业司法认证 entout
if (v.litigation && v.litigation.totalCases) {
var lit = v.litigation;
var typeLabels = {
administrative: "行政案件",
implement: "执行案件",
preservation: "非诉保全审查",
civil: "民事案件",
criminal: "刑事案件",
bankrupt: "强制清算与破产案件",
jurisdict: "管辖案件",
compensate: "赔偿案件",
};
html += "<h3>涉诉案件汇总</h3>";
var rows = [];
Object.keys(typeLabels).forEach(function (k) {
var sec = lit[k];
if (sec && sec.count) {
rows.push({
k: typeLabels[k],
v: sec.count + " 件",
});
}
});
if (rows.length) {
html +=
"<table class='kv-table'><tbody>" +
rows
.map(function (r) {
return (
"<tr><th>" +
esc(r.k) +
"</th><td>" +
esc(r.v) +
"</td></tr>"
);
})
.join("") +
"</tbody></table>";
}
Object.keys(typeLabels).forEach(function (k) {
var sec = lit[k];
if (!sec || !sec.cases || !sec.cases.length) return;
html +=
"<h3>" +
typeLabels[k] +
"</h3>" +
itemList(sec.cases, function (c) {
return (
"<div class='item-title'>" +
esc(c.caseNo) +
"" +
esc(c.court) +
"</div>" +
"<div class='item-meta'>" +
"地区:" +
esc(c.region) +
";审级:" +
esc(c.trialLevel) +
";案件类型:" +
esc(c.caseType) +
";案由:" +
esc(c.cause) +
";标的金额:" +
esc(c.amount) +
";立案日期:" +
esc(c.filingDate) +
";裁判日期:" +
esc(c.judgmentDate) +
";胜败结果:" +
esc(c.victoryResult) +
"</div>"
);
});
});
}
html +=
"<h3>行政处罚</h3>" +
itemList(v.adminPenalties, function (p) {
return (
"<div class='item-title'>" +
esc(p.pendecno || p.PENDECNO) +
"</div>" +
"<div class='item-meta'>违法类型:" +
esc(p.illegacttype || p.ILLEGACTTYPE) +
";机关:" +
esc(p.penauth || p.PENAUTH) +
";日期:" +
esc(p.pendecissdate || p.PENDECISSDATE) +
"</div>"
);
});
html +=
"<h3>行政处罚变更记录</h3>" +
itemList(v.adminPenaltyUpdates, function (p) {
return (
"<div class='item-title'>" +
esc(p.updateDate) +
"</div><div class='item-meta'>" +
esc(p.updateContent) +
"</div>"
);
});
html +=
"<h3>经营异常</h3>" +
itemList(v.exceptions, function (e) {
return (
"<div class='item-title'>" +
esc(e.indate || e.INDATE) +
"</div>" +
"<div class='item-meta'>原因:" +
esc(e.inreason || e.INREASON) +
";移出:" +
esc(e.outdate || e.OUTDATE) +
"</div>"
);
});
html +=
"<h3>严重违法</h3>" +
itemList(v.seriousIllegals, function (s) {
return (
"<div class='item-block'>" +
(typeof s === "object"
? JSON.stringify(s)
: esc(s)) +
"</div>"
);
});
html +=
"<h3>动产抵押</h3>" +
itemList(v.mortgages, function (m) {
return (
"<div class='item-title'>" +
esc(m.regNo) +
" " +
esc(m.guaranteedAmount) +
"</div>" +
"<div class='item-meta'>登记日:" +
esc(m.regDate) +
";机关:" +
esc(m.regOrg) +
";状态:" +
esc(m.status) +
"</div>"
);
});
html += "<h3>简易注销</h3>";
if (v.quickCancel) {
html += kvTable(v.quickCancel, [
"entName",
"creditCode",
"regNo",
"regOrg",
"noticeFromDate",
"noticeToDate",
"cancelResult",
]);
if (
v.quickCancel.dissents &&
v.quickCancel.dissents.length
) {
html +=
"<p class='count-hint'>异议 " +
v.quickCancel.dissents.length +
" 条</p>";
v.quickCancel.dissents.forEach(function (d) {
html +=
"<div class='item-block'>" +
esc(d.dissentOrg) +
"" +
esc(d.dissentDes) +
" " +
esc(d.dissentDate) +
"</div>";
});
}
} else {
html += "<p class='empty-hint'>无</p>";
}
html += "<h3>清算信息</h3>";
if (v.liquidation) {
html += kvTable(v.liquidation, [
"principal",
"members",
]);
} else {
html += "<p class='empty-hint'>无</p>";
}
html += "<h3>纳税与欠税</h3>";
var tax = v.taxRecords;
if (tax) {
html +=
"<p class='count-hint'>纳税A级年度" +
(tax.taxLevelAYears && tax.taxLevelAYears.length
? tax.taxLevelAYears.length + " 条"
: "无") +
";欠税:" +
(tax.taxOwings && tax.taxOwings.length
? tax.taxOwings.length + " 条"
: "无") +
"</p>";
html += itemList(tax.taxOwings, function (t) {
return (
"<div class='item-title'>" +
esc(t.taxOwedType || t.taxowedtype) +
"</div>" +
"<div class='item-meta'>合计:" +
esc(t.totalOwedAmount || t.totalowedamount) +
";公示日:" +
esc(t.publishDate || t.publishdate) +
"</div>"
);
});
} else {
html += "<p class='empty-hint'>无</p>";
}
return html;
}
function renderRiskOverview(v) {
if (!v) return "<p class='empty-hint'>暂无风险分析</p>";
var html = "";
// 风险点清单:命中 / 未命中,多列展示
if (Array.isArray(v.items) && v.items.length) {
html += "<h3>风险点一览</h3>";
html += "<div class='risk-points-grid'>";
v.items.forEach(function (it) {
var statusText = it.hit ? "命中" : "未命中";
var statusClass = it.hit
? "risk-status-bad"
: "risk-status-ok";
html +=
"<div class='risk-point'><span class='risk-point-name'>" +
esc(it.name) +
"</span><span class='" +
statusClass +
"'>" +
statusText +
"</span></div>";
});
html += "</div>";
}
return html;
}
function renderTimeline(v) {
if (!v || !v.length)
return "<p class='empty-hint'>暂无发展时间线</p>";
return itemList(v, function (item) {
var title =
"<div class='item-title'>" +
esc(item.date) +
" " +
esc(item.type) +
"</div>";
var body = "<div>" + esc(item.title || "") + "</div>";
var meta = "";
if (item.detailBefore || item.detailAfter) {
// 对长文本拆成更易读的多行段落
var before = (item.detailBefore || "")
.replace(//g, "<br/>")
.replace(/+/g, "");
var after = (item.detailAfter || "")
.replace(//g, "<br/>")
.replace(/+/g, "");
meta =
"<div class='item-meta'>" +
"<div><strong>变更前:</strong><br/>" +
before +
"</div>" +
"<div style='margin-top:4px'><strong>变更后:</strong><br/>" +
after +
"</div>" +
"</div>";
}
return title + body + meta;
});
}
function renderListed(v) {
if (!v)
return "<p class='empty-hint'>非上市或暂无上市信息</p>";
var html =
"<h3>是否上市</h3><p>" +
(v.isListed ? "是" : "否") +
"</p>";
if (v.company) {
html +=
"<h3>上市公司信息</h3>" +
kvTable(v.company, [
"bizScope",
"creditCode",
"regAddr",
"regCapital",
"orgCode",
"cur",
"curName",
]);
}
html += "<h3>股票信息</h3>";
if (v.stock)
html +=
"<div class='item-block'>" +
(typeof v.stock === "object"
? JSON.stringify(v.stock)
: esc(v.stock)) +
"</div>";
else html += "<p class='empty-hint'>无</p>";
html +=
"<h3>十大股东</h3>" +
itemList(v.topShareholders, function (s) {
return (
"<div class='item-title'>" +
esc(s.name || s.shaname) +
"</div>" +
"<div class='item-meta'>" +
esc(s.percent || s.fundedratio) +
"</div>"
);
});
html +=
"<h3>上市高管</h3>" +
itemList(v.listedManagers, function (m) {
return (
"<div class='item-title'>" +
esc(m.name || m.perName) +
"</div>" +
"<div class='item-meta'>" +
esc(m.position) +
"</div>"
);
});
return html;
}
function renderScores(v) {
if (!v) return "<p class='empty-hint'>暂无评分</p>";
var html = kvTable(v, [
"overallScore",
"stabilityScore",
"equityTransparencyScore",
"complianceScore",
"activityScore",
]);
if (v.tags && v.tags.length) {
html += "<h3>标签</h3><p>";
v.tags.forEach(function (t) {
html += "<span class='tag'>" + esc(t) + "</span> ";
});
html += "</p>";
}
return html;
}
function renderBasicList(v) {
return itemList(v, function (b) {
return (
"<div class='item-title'>" +
esc(b.entName || b.entname) +
"</div>" +
"<div class='item-meta'>信用代码:" +
esc(b.creditCode || b.creditNo || b.creditno) +
";注册号:" +
esc(b.regNo) +
"</div>"
);
});
}
function renderRaw(v) {
if (!v) return "<p class='empty-hint'>无原始数据</p>";
return (
"<details class='raw-section'><summary>展开 jiguangFull / judicialCertFull</summary>" +
"<pre>" +
esc(JSON.stringify(v, null, 2)).substring(0, 15000) +
(JSON.stringify(v).length > 15000
? "\n…已截断"
: "") +
"</pre></details>"
);
}
var sectionRenderers = {
riskOverview: renderRiskOverview,
basic: renderBasic,
branches: renderBranches,
shareholding: renderShareholding,
controller: renderController,
beneficiaries: renderBeneficiaries,
investments: renderInvestments,
guarantees: renderGuarantees,
management: renderManagement,
assets: renderAssets,
licenses: renderLicenses,
activities: renderActivities,
inspections: renderInspections,
risks: renderRisks,
timeline: renderTimeline,
listed: renderListed,
};
function renderSectionContent(key, value) {
var fn = sectionRenderers[key];
if (fn) return fn(value);
if (value == null)
return "<p class='empty-hint'>暂无数据</p>";
if (Array.isArray(value))
return itemList(value, function (item) {
return typeof item === "object"
? "<div class='item-block'>" +
kvTable(item, Object.keys(item)) +
"</div>"
: esc(item);
});
if (typeof value === "object")
return kvTable(value, Object.keys(value));
return "<p>" + esc(value) + "</p>";
}
function render() {
var d = reportData;
document.getElementById("entName").textContent =
d.entName || "企业名称";
document.getElementById("creditCodeLabel").textContent =
"统一社会信用代码:" + (d.creditCode || "-");
document.getElementById("entStatusLabel").textContent =
"经营状态:" +
((d.basic && (d.basic.status || d.basic.entStatus)) ||
"-");
document.getElementById("entTypeLabel").textContent =
"企业类型:" + ((d.basic && d.basic.entType) || "-");
document.getElementById("reportTimeLabel").textContent =
"报告生成时间:" + (d.reportTime || "-");
// 使用 riskOverview 中的风险得分和等级替换原综合评分
var ro = d.riskOverview || {};
document.getElementById("overallScore").textContent =
ro.riskScore != null ? ro.riskScore : "-";
var level = ro.riskLevel || "-";
var pill = document.getElementById("riskLevelPill");
pill.textContent = "风险:" + level;
pill.className = "pill low";
if (level === "中") pill.classList.add("mid");
else if (level === "高") pill.classList.add("high");
var tagsBox = document.getElementById("headerTags");
tagsBox.innerHTML = "";
(ro.tags || []).forEach(function (t) {
var span = document.createElement("span");
span.className = "tag";
span.textContent = t;
tagsBox.appendChild(span);
});
var nav = document.getElementById("navLinks");
nav.innerHTML = "";
sectionOrder.forEach(function (key) {
if (key === "raw") return;
var a = document.createElement("a");
a.href = "#section-" + key;
a.textContent = sectionTitles[key] || key;
nav.appendChild(a);
});
var container = document.getElementById("reportSections");
container.innerHTML = "";
sectionOrder.forEach(function (key) {
var value = d[key];
var card = document.createElement("div");
card.className = "section-card";
card.id = "section-" + key;
var title = document.createElement("h2");
title.textContent = sectionTitles[key] || key;
card.appendChild(title);
var body = document.createElement("div");
body.className = "section-body";
body.innerHTML = renderSectionContent(key, value);
card.appendChild(body);
container.appendChild(card);
});
}
function loadReport() {
try {
// 使用后端注入的 reportData 直接渲染
if (!reportData || typeof reportData !== "object") {
reportData = {
entName: "未加载到数据",
basic: {},
risks: {},
riskOverview: {},
};
}
render();
} catch (e) {
console.error("渲染企业报告失败", e);
}
}
loadReport();
// 绑定「保存为 PDF」按钮使用 html2canvas + jsPDF 截图生成 PDF避免依赖浏览器打印对话框
var saveBtn = document.getElementById("btnSavePdf");
if (saveBtn && window.html2canvas && window.jspdf && window.jspdf.jsPDF) {
saveBtn.addEventListener("click", function () {
try {
var pageEl = document.querySelector(".page");
if (!pageEl) {
console.error("未找到 .page 容器");
return;
}
html2canvas(pageEl, {
scale: 1.5,
useCORS: true,
scrollX: 0,
scrollY: -window.scrollY,
})
.then(function (canvas) {
// 使用 JPEG 可以避免部分环境下 PNG 解析问题Incomplete or corrupt PNG file
var imgData = canvas.toDataURL(
"image/jpeg",
0.95,
);
var pdf = new jspdf.jsPDF("p", "mm", "a4");
var pageWidth = pdf.internal.pageSize.getWidth();
var pageHeight = pdf.internal.pageSize.getHeight();
var imgWidth = pageWidth;
var imgHeight =
(canvas.height * imgWidth) / canvas.width;
var position = 0;
var heightLeft = imgHeight;
pdf.addImage(
imgData,
"JPEG",
0,
position,
imgWidth,
imgHeight,
);
heightLeft -= pageHeight;
while (heightLeft > 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(
imgData,
"JPEG",
0,
position,
imgWidth,
imgHeight,
);
heightLeft -= pageHeight;
}
var fileName = "企业全景报告.pdf";
if (
reportData &&
reportData.entName &&
typeof reportData.entName === "string"
) {
fileName =
reportData.entName +
"_企业全景报告.pdf";
}
pdf.save(fileName);
})
.catch(function (e) {
console.error("生成 PDF 失败", e);
});
} catch (e) {
console.error("触发生成 PDF 失败", e);
}
});
}
})();
</script>
</body>
</html>