Files
hyapi-server/resources/qiye.html

3526 lines
151 KiB
HTML
Raw Normal View History

2026-04-21 22:36:48 +08:00
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>企业全景报告</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Noto+Serif+SC:wght@600;700&display=swap"
rel="stylesheet"
/>
<style>
:root {
--blue-950: #172554;
--blue-900: #1e3a8a;
--blue-800: #1e40af;
--blue-700: #1d4ed8;
--blue-600: #2563eb;
--blue-500: #3b82f6;
--blue-200: #bfdbfe;
--blue-100: #dbeafe;
--blue-50: #eff6ff;
--red-800: #991b1b;
--red-700: #b91c1c;
--red-600: #dc2626;
--red-100: #fee2e2;
--red-50: #fef2f2;
--ink: #1e293b;
--ink-secondary: #334155;
--muted: #64748b;
--line: #e2e8f0;
--line-soft: #f1f5f9;
--surface: #ffffff;
--surface-raised: #f8fafc;
--brand: var(--blue-800);
--brand-mid: var(--blue-700);
--accent-blue: var(--blue-600);
--accent-red: var(--red-600);
--success: #0d9488;
--success-soft: rgba(13, 148, 136, 0.1);
--warn: #ca8a04;
--warn-soft: rgba(202, 138, 4, 0.12);
--danger: var(--red-700);
--danger-soft: rgba(220, 38, 38, 0.1);
--radius: 12px;
--radius-sm: 8px;
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
--shadow-md: 0 4px 24px rgba(15, 23, 42, 0.06);
--shadow-header: 0 1px 3px rgba(15, 23, 42, 0.05);
--table-border: #d1d5db;
--table-line: #e5e7eb;
--table-head: #f9fafb;
--header-tint-from: #e8f0ff;
--header-tint-mid: #f0f6ff;
--header-tint-to: #ffffff;
--header-border: rgba(30, 64, 175, 0.22);
--header-strip-start: #172554;
--header-strip-mid: #1d4ed8;
--header-strip-end: #b91c1c;
--font-sans:
"Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
--font-serif: "Noto Serif SC", "Songti SC", "SimSun", serif;
--primary: var(--blue-700);
--primary-dark: var(--blue-900);
--text: var(--ink);
--text-muted: var(--muted);
--border: var(--line);
--bg-card: var(--surface);
--bg-page: #ffffff;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--font-sans);
font-size: 15px;
line-height: 1.65;
color: var(--ink);
background: var(--bg-page);
min-height: 100vh;
}
.page {
position: relative;
max-width: 1040px;
margin: 40px auto 56px;
padding: 0 0 44px;
background: var(--surface);
border-radius: var(--radius);
border: 1px solid var(--line);
box-shadow: var(--shadow-md);
overflow: hidden;
scroll-behavior: smooth;
}
.page::before {
content: "";
display: block;
height: 2px;
background: var(--blue-800);
}
.page-inner {
padding: 0 40px 8px;
}
@media (max-width: 768px) {
.page {
margin: 16px;
border-radius: var(--radius-sm);
}
.page-inner {
padding: 0 20px 8px;
}
}
.top-nav {
display: none;
}
.top-nav-title {
font-weight: 600;
color: var(--ink);
}
.top-nav-links {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.top-nav-links a {
padding: 0;
border-radius: 0;
background: transparent;
color: var(--blue-700);
text-decoration: none;
border: none;
cursor: default;
pointer-events: none;
}
.header {
position: relative;
margin: 28px 0 20px;
padding: 0;
border-radius: var(--radius);
background: linear-gradient(
165deg,
var(--header-tint-from) 0%,
var(--header-tint-mid) 38%,
var(--header-tint-to) 72%
);
border: 1px solid var(--header-border);
box-shadow:
inset 3px 0 0 var(--blue-800),
0 1px 2px rgba(30, 58, 138, 0.06),
0 18px 50px -14px rgba(30, 64, 175, 0.14);
color: var(--ink);
overflow: hidden;
}
.header::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(
90deg,
var(--header-strip-start) 0%,
var(--header-strip-mid) 62%,
var(--header-strip-end) 100%
);
z-index: 1;
}
.header-inner {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 32px 40px;
align-items: center;
padding: 30px 32px 32px;
padding-top: 28px;
}
.header-main {
padding: 0;
min-width: 0;
}
.header-score {
margin: 0;
padding: 0;
min-width: 0;
align-self: stretch;
display: flex;
align-items: center;
justify-content: flex-end;
background: transparent;
border: none;
box-shadow: none;
}
.header-score-inner {
width: 100%;
max-width: 200px;
padding: 22px 24px;
text-align: center;
background: rgba(255, 255, 255, 0.92);
border: 1px solid rgba(37, 99, 235, 0.2);
border-radius: var(--radius-sm);
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.85) inset,
0 10px 28px -12px rgba(30, 64, 175, 0.2);
border-top: 3px solid var(--blue-700);
}
@media (max-width: 768px) {
.header-inner {
grid-template-columns: 1fr;
gap: 24px;
padding: 24px 20px 28px;
padding-top: 22px;
}
.header-score {
justify-content: stretch;
}
.header-score-inner {
max-width: none;
}
}
.header-main h1.report-title {
margin: 0 0 14px;
font-family: var(--font-serif);
font-size: clamp(1.5rem, 4vw, 2rem);
font-weight: 700;
letter-spacing: 0.02em;
color: var(--blue-950);
line-height: 1.3;
word-break: break-word;
}
.header-main .subtitle {
font-size: 15px;
font-weight: 600;
color: var(--ink-secondary);
word-break: break-word;
line-height: 1.55;
}
.header-main .subtitle + .subtitle {
margin-top: 8px;
font-size: 13px;
font-weight: 400;
color: var(--muted);
}
.header-meta-line {
margin-top: 16px;
font-size: 12px;
font-weight: 500;
letter-spacing: 0.03em;
color: var(--muted);
}
.header-tags {
margin-top: 16px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag {
display: inline-flex;
align-items: center;
padding: 5px 10px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.01em;
background: var(--surface-raised);
border: 1px solid var(--line);
color: var(--ink-secondary);
}
.header .tag {
background: rgba(255, 255, 255, 0.75);
border: 1px solid rgba(37, 99, 235, 0.22);
color: var(--blue-900);
}
.header-score .score {
font-family: var(--font-serif);
font-size: 2.35rem;
font-weight: 700;
color: var(--blue-900);
font-variant-numeric: tabular-nums;
line-height: 1;
letter-spacing: 0.02em;
}
.header-score .label {
margin-top: 14px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.08em;
color: var(--muted);
line-height: 1.5;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.header-score .pill {
border-width: 1px;
margin-top: 0;
margin-left: 0;
display: inline-flex;
}
.header-score .pill.low {
background: var(--success-soft);
color: var(--success);
border-color: rgba(13, 148, 136, 0.35);
}
.header-score .pill.mid {
background: var(--warn-soft);
color: var(--warn);
border-color: rgba(202, 138, 4, 0.35);
}
.header-score .pill.high {
background: var(--danger-soft);
color: var(--red-700);
border-color: rgba(220, 38, 38, 0.4);
}
.print-btn {
display: none;
}
.pdf-actions-fixed {
position: fixed;
top: 24px;
right: 24px;
z-index: 1000;
}
.pdf-fixed-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border-radius: 8px;
border: 1px solid var(--line);
background: var(--surface);
color: var(--ink-secondary);
font-family: var(--font-sans);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.04em;
cursor: pointer;
box-shadow: var(--shadow-sm);
transition:
transform 0.2s ease,
box-shadow 0.2s ease,
background 0.2s ease,
color 0.2s ease,
border-color 0.2s ease;
}
.pdf-fixed-btn:hover {
background: var(--blue-800);
color: #fff;
border-color: var(--blue-800);
box-shadow: var(--shadow-sm);
transform: none;
}
.pdf-fixed-btn:disabled {
opacity: 0.55;
cursor: not-allowed;
transform: none;
}
.pdf-loading-overlay {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(6px);
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
}
.pdf-loading-overlay[aria-hidden="true"] {
display: none;
}
.pdf-loading-content {
background: var(--surface);
padding: 36px 44px;
border-radius: var(--radius);
text-align: center;
border: 1px solid var(--line);
box-shadow: var(--shadow-md);
}
.pdf-loading-spinner {
width: 44px;
height: 44px;
border: 2px solid var(--line);
border-top-color: var(--blue-800);
border-radius: 50%;
margin: 0 auto 18px;
animation: pdf-spin 0.8s linear infinite;
}
.pdf-loading-content p {
margin: 0;
font-size: 15px;
font-weight: 500;
color: var(--ink-secondary);
}
@keyframes pdf-spin {
to {
transform: rotate(360deg);
}
}
.report-sections {
margin-top: 36px;
display: flex;
flex-direction: column;
gap: 22px;
}
.section-card {
scroll-margin-top: 24px;
padding: 0;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-sm);
overflow: hidden;
}
.section-card h2 {
margin: 0;
padding: 14px 24px 14px 20px;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0.04em;
color: var(--ink);
background: var(--table-head);
border-bottom: 1px solid var(--table-border);
border-left: 3px solid var(--blue-800);
}
.section-card .section-body {
padding: 24px 28px 28px;
}
.section-body h3,
.section-card .section-body h3 {
margin: 24px 0 12px;
font-size: 0.9375rem;
font-weight: 700;
color: var(--ink-secondary);
}
.section-body h3:first-child,
.section-card .section-body h3:first-of-type {
margin-top: 0;
}
.report-subheading {
margin: 20px 0 10px !important;
font-size: 0.8125rem !important;
font-weight: 700 !important;
letter-spacing: 0.04em !important;
color: var(--ink-secondary) !important;
}
.report-annual-card-title {
margin: 0 0 14px !important;
font-family: var(--font-sans);
font-size: 1.0625rem !important;
font-weight: 700 !important;
color: var(--ink) !important;
padding-bottom: 10px;
border-bottom: 1px solid var(--table-border);
}
.report-annual-shell {
margin-bottom: 20px;
padding: 20px 22px 20px;
background: var(--surface);
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
box-shadow: none;
border-top: none;
border-left: 3px solid var(--blue-800);
}
.report-annual-shell:last-child {
margin-bottom: 0;
}
.kv-table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
font-size: 13px;
line-height: 1.55;
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
overflow: hidden;
font-variant-numeric: tabular-nums;
}
.kv-table th,
.kv-table td {
padding: 10px 14px;
vertical-align: top;
border-bottom: 1px solid var(--table-line);
}
.kv-table tr:last-child th,
.kv-table tr:last-child td {
border-bottom: none;
}
.kv-table tbody tr:nth-child(even) {
background: #fafafa;
}
.kv-table tbody tr:nth-child(odd) {
background: var(--surface);
}
.kv-table th {
width: 148px;
max-width: 40%;
font-weight: 600;
font-size: 12.5px;
color: var(--muted);
background: var(--table-head);
text-align: left;
border-right: 1px solid var(--table-line);
}
.kv-table td {
word-break: break-word;
color: var(--ink);
font-weight: 400;
}
.kv-grid {
--cols: 3;
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
gap: 0;
font-size: 13px;
line-height: 1.55;
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
overflow: hidden;
font-variant-numeric: tabular-nums;
}
@media (max-width: 900px) {
.kv-grid {
--cols: 2;
}
}
@media (max-width: 560px) {
.kv-grid {
--cols: 1;
}
}
.kv-grid .kv-pair {
display: flex;
flex-wrap: wrap;
gap: 4px 8px;
padding: 10px 14px;
border-bottom: 1px solid var(--table-line);
border-right: 1px solid var(--table-line);
align-items: baseline;
background: var(--surface);
}
.kv-grid .kv-pair:nth-child(even) {
background: #fafafa;
}
.kv-grid .kv-pair .k {
color: var(--muted);
font-weight: 600;
font-size: 12px;
flex-shrink: 0;
}
.kv-grid .kv-pair .k::after {
content: "";
}
.kv-grid .kv-pair .v {
word-break: break-word;
min-width: 0;
color: var(--ink);
font-weight: 400;
}
.item-list {
list-style: none;
padding: 0;
margin: 0;
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
overflow: hidden;
}
.item-list li {
padding: 12px 16px;
border-bottom: 1px solid var(--table-line);
font-size: 13px;
line-height: 1.55;
background: var(--surface);
}
.item-list li:nth-child(even) {
background: #fafafa;
}
.item-list li:last-child {
border-bottom: none;
}
.item-list li .item-title {
font-weight: 700;
color: var(--ink);
margin-bottom: 4px;
font-size: 13px;
}
.item-list li .item-meta {
color: var(--muted);
font-size: 12.5px;
line-height: 1.55;
}
.item-block {
background: var(--surface-raised);
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
border-left: 3px solid var(--blue-800);
padding: 12px 16px;
margin-bottom: 10px;
font-size: 13px;
line-height: 1.55;
color: var(--ink-secondary);
}
.item-block .item-kv span.k {
color: var(--muted);
margin-right: 6px;
font-weight: 600;
font-size: 12px;
}
.empty-hint {
color: var(--muted);
font-size: 13px;
padding: 18px 16px;
text-align: center;
background: var(--surface-raised);
border: 1px dashed var(--line);
border-radius: var(--radius-sm);
}
.count-hint {
font-size: 12px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--muted);
margin-bottom: 12px;
}
.cell-object .cell-kv-line {
margin: 6px 0;
line-height: 1.55;
}
.cell-object .cell-kv-line:first-child {
margin-top: 0;
}
.cell-object .cell-k {
display: inline-block;
min-width: 11em;
color: var(--muted);
font-size: 12.5px;
font-weight: 600;
vertical-align: top;
}
.cell-object .cell-k::after {
content: "";
}
.cell-object .cell-v {
display: inline-block;
color: var(--ink);
font-size: 13px;
max-width: 100%;
word-break: break-word;
}
.cell-object .cell-object {
margin-top: 6px;
margin-left: 4px;
padding-left: 10px;
border-left: 2px solid var(--table-line);
}
.pill {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
margin-left: 8px;
vertical-align: middle;
}
.pill.low {
background: var(--success-soft);
color: var(--success);
border: 1px solid rgba(13, 148, 136, 0.28);
}
.pill.mid {
background: var(--warn-soft);
color: var(--warn);
border: 1px solid rgba(202, 138, 4, 0.3);
}
.pill.high {
background: var(--danger-soft);
color: var(--red-700);
border: 1px solid rgba(220, 38, 38, 0.35);
}
.risk-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 11px 0;
font-size: 14px;
border-bottom: 1px solid var(--line-soft);
}
.risk-row .ok {
color: var(--success);
font-weight: 700;
}
.risk-row .bad {
color: var(--red-600);
font-weight: 700;
}
.risk-points-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px 16px;
margin-top: 10px;
}
@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;
justify-content: space-between;
gap: 10px;
padding: 10px 14px;
background: var(--surface);
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
border-left: 3px solid var(--line);
}
.risk-point-name {
color: var(--muted);
font-weight: 500;
}
.risk-status-ok {
color: var(--success);
font-weight: 700;
font-size: 12px;
}
.risk-status-bad {
color: var(--red-600);
font-weight: 700;
font-size: 12px;
}
.raw-section summary {
cursor: pointer;
padding: 12px 16px;
font-weight: 600;
font-size: 13px;
color: var(--ink-secondary);
background: var(--table-head);
border: 1px solid var(--table-border);
border-radius: var(--radius-sm);
}
.raw-section pre {
font-size: 11px;
line-height: 1.6;
font-family:
ui-monospace, "Cascadia Code", "SF Mono", Consolas,
monospace;
background: #1e293b;
color: #e2e8f0;
padding: 16px;
border-radius: 0 0 var(--radius-sm) var(--radius-sm);
overflow: auto;
max-height: 320px;
margin: 0;
border: 1px solid var(--table-border);
border-top: none;
}
@media (max-width: 768px) {
.section-card .section-body {
padding: 18px 16px 20px;
}
.section-card h2 {
padding: 12px 16px 12px 14px;
}
}
@media print {
body {
background: #fff !important;
margin: 0;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.page {
max-width: none;
margin: 0;
padding: 0;
border: none;
box-shadow: none;
border-radius: 0;
}
.page::before {
height: 2px;
}
.page-inner {
padding: 0 12mm;
}
.top-nav {
display: none !important;
}
.pdf-actions-fixed,
.pdf-loading-overlay {
display: none !important;
}
.header {
background: linear-gradient(
180deg,
#e8f0ff 0%,
#f8fafc 45%,
#ffffff 100%
) !important;
color: var(--ink) !important;
box-shadow: none !important;
border: 1px solid var(--line);
border-left: 3px solid var(--blue-800);
border-radius: 0;
margin-top: 8px;
}
.header-inner {
grid-template-columns: 1fr;
padding: 20px 0 16px;
gap: 20px;
}
.header::before {
background: linear-gradient(
90deg,
var(--header-strip-start) 0%,
var(--header-strip-mid) 62%,
var(--header-strip-end) 100%
);
}
.header-main {
padding: 0;
}
.header-score-inner {
border-top-width: 3px;
border-top-color: var(--blue-700);
border-color: rgba(37, 99, 235, 0.2);
background: #fff !important;
box-shadow: none;
max-width: 260px;
margin-left: auto;
margin-right: auto;
}
.header-main h1.report-title {
color: var(--blue-950) !important;
}
.header-main .subtitle,
.header-main .subtitle + .subtitle {
color: var(--ink-secondary) !important;
}
.header-meta-line {
color: var(--muted) !important;
}
.tag {
background: var(--surface-raised);
color: var(--ink-secondary);
border-color: var(--line);
}
.header .tag {
background: #fff !important;
color: var(--blue-900) !important;
border-color: rgba(37, 99, 235, 0.25) !important;
}
.header-score {
background: transparent !important;
border: none !important;
margin: 0 !important;
justify-content: center;
}
.header-score .score {
color: var(--blue-900) !important;
}
.header-score .label {
color: var(--muted) !important;
}
.header-main {
text-align: center;
width: 100%;
}
.header-score {
width: 100%;
}
.section-card {
box-shadow: none;
border-radius: 0;
border: none;
border-bottom: 1px solid var(--line);
page-break-inside: auto;
break-inside: auto;
padding: 0 !important;
margin-top: 6px !important;
background: #fff !important;
}
.section-card h2 {
background: var(--table-head) !important;
border-left-color: var(--blue-800);
color: var(--ink);
}
.section-card .section-body {
padding: 12px 0 16px !important;
}
.item-list li {
page-break-inside: auto;
break-inside: auto;
}
.raw-section pre {
max-height: none;
page-break-inside: avoid;
break-inside: avoid;
}
a {
color: var(--ink);
text-decoration: none;
}
}
</style>
</head>
<body>
<div class="page">
<div class="page-inner">
<!-- 隐藏的导航容器,仅用于脚本生成目录,页面不展示 -->
<nav class="top-nav">
<div id="navLinks"></div>
</nav>
<header class="header">
<div class="header-inner">
<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 class="header-meta-line" id="reportTimeLabel">
报告生成时间:-
</div>
</div>
<aside class="header-score" aria-label="综合评分">
<div class="header-score-inner">
<div class="score" id="overallScore">--</div>
<div class="label">
综合评分
<span id="riskLevelPill" class="pill low"
>风险:-</span
>
</div>
</div>
</aside>
</div>
</header>
<main id="reportSections" class="report-sections"></main>
</div>
</div>
<!-- 固定悬浮的保存 PDF 按钮(在 .page 外,不会被截进 PDF -->
<div class="pdf-actions-fixed">
<button id="btnSavePdf" class="pdf-fixed-btn" type="button">
保存为 PDF
</button>
</div>
<!-- 生成 PDF 时的加载浮层 -->
<div
id="pdfLoadingOverlay"
class="pdf-loading-overlay"
aria-hidden="true"
>
<div class="pdf-loading-content">
<div class="pdf-loading-spinner" aria-hidden="true"></div>
<p id="pdfLoadingMessage">正在准备 PDF请稍候...</p>
</div>
</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",
"annualReports",
"taxViolations",
"ownTaxNotices",
];
var sectionTitles = {
riskOverview: "风险情况(综合分析)",
basic: "一、主体概览(企业基础信息)",
branches: "二、分支机构",
shareholding: "三、股权与控制(持股结构)",
controller: "四、实际控制人",
beneficiaries: "五、最终受益人",
investments: "六、对外投资",
guarantees: "七、对外担保(披露摘要)",
management: "八、人员与组织(高管与任职)",
assets: "九、资产与经营(财务摘要)",
licenses: "十、行政许可与资质",
activities: "十一、经营活动(招投标)",
inspections: "十二、抽查检查",
risks: "十三、风险与合规",
timeline: "十四、发展时间线",
listed: "十五、上市信息",
annualReports: "十六、企业年报(公示)",
taxViolations: "十七、税收违法",
ownTaxNotices: "十八、欠税公告",
};
// 保持所有板块单列纵向排布
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: "股票信息",
// 上市公司信息字段
bizScope: "经营范围",
regAddr: "注册地址",
regCapital: "注册资本",
cur: "币种",
curName: "币种名称",
// 欠税公告QYGL7D9A
taxIdNumber: "纳税人识别号",
taxpayerName: "纳税人名称",
taxCategory: "欠税税种",
ownTaxBalance: "欠税余额",
ownTaxAmount: "欠税金额",
newOwnTaxBalance: "当前新发生欠税余额",
taxType: "税务类型",
publishDate: "发布时间",
department: "税务机关",
legalPersonName: "法人或负责人名称",
personIdNumber: "证件号码",
personIdName: "法人证件名称",
taxpayerType: "纳税人类型",
regType: "注册类型",
// 税收违法QYGL8848
entityName: "企业名称",
entityCategory: "企业类别",
taxpayerCode: "纳税人识别号",
agencyPersonInfo: "直接责任中介机构信息",
illegalStartDate: "违法事实开始时间",
illegalEndDate: "违法事实结束时间",
illegalTime: "违法行为发生时间",
publishDepartment: "信息公示税务机关",
checkDepartment: "案件检查机关",
belongDepartment: "涉案企业所属税务机关",
punishBasis: "处罚法律依据及具体内容",
illegalFact: "具体违法事实描述",
caseType: "案件性质",
police: "案件移送公安机关情况",
// 企业年报QYGLDJ12
registerCode: "注册号",
organizationCode: "组织机构代码",
creditCode: "统一社会信用代码",
entityStatus: "企业经营状态",
telephone: "企业联系电话",
postCode: "邮政编码",
entityBusinessScope: "主营业务范围",
employeeCnt: "从业人数",
entityPractitionerFemaleAmount: "其中女性从业人数",
actualEmployeeCnt: "实际员工数量",
mainBusinessIncome: "主营业务收入",
netProfit: "净利润",
totalSalesAmount: "销售总额",
totalNetProfit: "利润总额",
totalAssets: "资产总额",
totalLiabilities: "负债总额",
totalOwnerEquity: "所有者权益合计",
totalTaxAmount: "纳税总额",
reportYear: "年报年份",
reportName: "年报名称",
reportDate: "年报提交日期",
isWebsite: "是否有网站或网店",
isInvest: "是否有对外投资信息或购买股权",
isExternalGuarantee: "是否提供对外担保",
isEquityTransfer: "是否发生股东股权转让",
entityHoldingInfo: "企业控股情况信息",
guaranteeInfo: "对外担保信息摘要",
investInfo: "对外投资信息摘要",
actualRegistCapitalInfo: "注册资本实缴情况",
reportChangeInfo: "年报变更记录",
reportBranch: "分支机构信息",
reportContributeInfo: "出资情况信息",
reportRecordInfo: "备案信息",
websiteType: "网址类型",
websiteName: "网址名称",
websiteUrl: "网址",
stockName: "股东名称",
identifyType: "证件类型",
stockPercent: "持股比例",
realCapiItems: "实缴出资详情",
shouldCapiItems: "应缴出资详情",
totalRealCapi: "实缴出资总额",
totalShouldCapi: "应缴出资总额",
investName: "被投资企业名称",
investCapi: "投资金额",
investPercent: "投资比例",
entityId: "主体标识",
investRegNo: "注册号/统一社会信用代码",
seqNo: "序号",
socialSecurity: "社会保险(分项参保人数)",
endowmentInsuranceEmployeeCnt: "城镇职工基本养老保险参保人数",
unemploymentInsuranceEmployeeCnt: "失业保险参保人数",
medicalInsuranceEmployeeCnt: "职工基本医疗保险参保人数",
injuryInsuranceEmployeeCnt: "工伤保险参保人数",
maternityInsuranceEmployeeCnt: "生育保险参保人数",
creditor: "债权人",
debtor: "债务人",
amount: "主债权数额",
period: "履行债务的期限",
guaranteeTime: "保证期间",
guaranteeType: "保证方式",
guaranteeScope: "保证范围",
beforePercent: "变更前股权比例",
afterPercent: "变更后股权比例",
changeDate: "变更日期",
updateItem: "修改事项",
beforeUpdate: "修改前内容",
afterUpdate: "修改后内容",
investType: "出资方式",
realCapi: "实缴额",
realCapiDate: "实缴日期",
shoudCapi: "应缴额",
shouldCapiDate: "应缴日期",
};
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 renderScalarForCell(x) {
if (x == null || x === "") return "-";
if (typeof x === "boolean") return x ? "是" : "否";
if (typeof x === "number") return String(x);
if (typeof x === "string") return esc(x);
return esc(String(x));
}
function renderObjectAsLines(obj, depth) {
depth = depth || 0;
if (depth > 5) {
try {
return esc(JSON.stringify(obj));
} catch (e) {
return "-";
}
}
if (obj == null) return "-";
if (typeof obj !== "object")
return renderScalarForCell(obj);
if (Array.isArray(obj)) {
if (!obj.length) return "-";
var allScalar = obj.every(function (x) {
return (
x == null ||
typeof x === "string" ||
typeof x === "number" ||
typeof x === "boolean"
);
});
if (allScalar) {
return obj.map(renderScalarForCell).join("、");
}
return obj.length ? "共 " + obj.length + " 条" : "-";
}
var keys = Object.keys(obj);
if (!keys.length) return "-";
return (
"<div class='cell-object'>" +
keys
.map(function (k) {
var val = obj[k];
var inner;
if (
val != null &&
typeof val === "object" &&
!Array.isArray(val) &&
Object.keys(val).length > 0
) {
inner = renderObjectAsLines(val, depth + 1);
} else {
inner = renderCell(val);
}
return (
"<div class='cell-kv-line'><span class='cell-k'>" +
esc(label(k)) +
"</span><span class='cell-v'>" +
inner +
"</span></div>"
);
})
.join("") +
"</div>"
);
}
function renderCell(v) {
if (v == null || v === "") return "-";
if (typeof v === "boolean") return v ? "是" : "否";
if (typeof v === "number") return String(v);
if (typeof v === "object") {
if (Array.isArray(v)) {
if (!v.length) return "-";
var allScalar = v.every(function (x) {
return (
x == null ||
typeof x === "string" ||
typeof x === "number" ||
typeof x === "boolean"
);
});
if (allScalar) {
return v.map(renderScalarForCell).join("、");
}
return "共 " + v.length + " 条";
}
return renderObjectAsLines(v, 0);
}
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",
"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 || "-";
var html = kvGrid(o, keys, 3);
// 经营业务范围:单独占一整行,避免挤在多列网格里
html +=
"<h3>经营业务范围</h3>" +
"<div class='item-block'>" +
esc(v.businessScope || "-") +
"</div>";
return html;
}
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 arDj12 =
reportData &&
Array.isArray(reportData.annualReports) &&
reportData.annualReports.length > 0;
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>";
if (
arDj12 &&
(!Array.isArray(v.paidInDetails) ||
!v.paidInDetails.length)
) {
html +=
"<p class='empty-hint'>暂无实缴/应缴明细。</p>";
} else {
html += 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>";
if (
arDj12 &&
(!Array.isArray(v.subscribedCapitalDetails) ||
!v.subscribedCapitalDetails.length)
) {
html +=
"<p class='empty-hint'>暂无认缴出资明细。</p>";
} else {
html += 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>";
html += 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 arDj12 =
reportData &&
Array.isArray(reportData.annualReports) &&
reportData.annualReports.length > 0;
// 招投标:对角色、类型做友好映射
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>";
if (
arDj12 &&
(!Array.isArray(v.websites) || !v.websites.length)
) {
html +=
"<p class='empty-hint'>暂无网站或网店信息。</p>";
} else {
html += 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>"
);
}
function renderCapiItemList(arr) {
if (!Array.isArray(arr) || !arr.length) return "";
return itemList(arr, function (r) {
var parts = [];
if (r.investType) parts.push("方式:" + esc(r.investType));
if (r.realCapi != null && r.realCapi !== "")
parts.push("实缴:" + esc(r.realCapi));
if (r.shoudCapi != null && r.shoudCapi !== "")
parts.push("应缴:" + esc(r.shoudCapi));
if (r.shouldCapi != null && r.shouldCapi !== "")
parts.push("应缴:" + esc(r.shouldCapi));
if (r.realCapiDate)
parts.push("实缴日:" + esc(r.realCapiDate));
if (r.shouldCapiDate)
parts.push("应缴日:" + esc(r.shouldCapiDate));
return (
"<div class='item-meta'>" +
(parts.length
? parts.join("")
: esc(JSON.stringify(r))) +
"</div>"
);
});
}
function renderAnnualShareholders(list) {
if (!Array.isArray(list) || !list.length) return "";
var html = "";
list.forEach(function (sh) {
html +=
"<div class='item-block' style='margin-bottom:8px'>";
html +=
"<div class='item-title'>" +
esc(sh.stockName || "-") +
"</div>";
var meta = [];
if (sh.identifyType)
meta.push("证件类型:" + esc(sh.identifyType));
if (sh.stockPercent)
meta.push("持股比例:" + esc(sh.stockPercent));
if (
sh.totalRealCapi != null &&
sh.totalRealCapi !== ""
)
meta.push("实缴总额:" + esc(sh.totalRealCapi));
if (
sh.totalShouldCapi != null &&
sh.totalShouldCapi !== ""
)
meta.push("应缴总额:" + esc(sh.totalShouldCapi));
if (meta.length)
html +=
"<div class='item-meta'>" +
meta.join("") +
"</div>";
if (sh.realCapiItems && sh.realCapiItems.length) {
html +=
"<h4 class='report-subheading'>实缴明细</h4>";
html += renderCapiItemList(sh.realCapiItems);
}
if (sh.shouldCapiItems && sh.shouldCapiItems.length) {
html +=
"<h4 class='report-subheading'>应缴明细</h4>";
html += renderCapiItemList(sh.shouldCapiItems);
}
html += "</div>";
});
return html;
}
function renderAnnualReports(arr) {
if (!Array.isArray(arr) || !arr.length)
return "<p class='empty-hint'>暂无企业年报数据</p>";
var sorted = arr.slice().sort(function (a, b) {
var ya = parseInt(a.reportYear, 10) || 0;
var yb = parseInt(b.reportYear, 10) || 0;
return yb - ya;
});
var html =
"<p class='count-hint'>共 " + sorted.length + " 份</p>";
sorted.forEach(function (y) {
html += "<div class='report-annual-shell'>";
html +=
"<h3 class='report-annual-card-title'>" +
esc(
y.reportName ||
(y.reportYear
? y.reportYear + " 年报"
: "年报"),
) +
"</h3>";
var sumKeys = [
"reportYear",
"reportDate",
"entityName",
"creditCode",
"registerCode",
"organizationCode",
"entityStatus",
"address",
"telephone",
"postCode",
"entityBusinessScope",
"employeeCnt",
"entityPractitionerFemaleAmount",
"actualEmployeeCnt",
"mainBusinessIncome",
"netProfit",
"totalSalesAmount",
"totalNetProfit",
"totalAssets",
"totalLiabilities",
"totalOwnerEquity",
"totalTaxAmount",
"isWebsite",
"isInvest",
"isExternalGuarantee",
"isEquityTransfer",
"entityHoldingInfo",
"guaranteeInfo",
"actualRegistCapitalInfo",
"reportBranch",
"reportContributeInfo",
"reportRecordInfo",
];
html += kvGrid(y, sumKeys, 3);
if (y.reportWebsiteInfo && y.reportWebsiteInfo.length) {
html +=
"<h4 class='report-subheading'>网站或网店</h4>";
html += itemList(y.reportWebsiteInfo, function (w) {
return (
"<div class='item-meta'>" +
esc(w.websiteName) +
"" +
esc(w.websiteType) +
" " +
esc(w.websiteUrl) +
"</div>"
);
});
}
if (
y.reportShareholderInfo &&
y.reportShareholderInfo.length
) {
html +=
"<h4 class='report-subheading'>股东及出资</h4>";
html += renderAnnualShareholders(
y.reportShareholderInfo,
);
}
if (y.reportInvestInfo && y.reportInvestInfo.length) {
html +=
"<h4 class='report-subheading'>年报对外投资</h4>";
html += itemList(y.reportInvestInfo, function (inv) {
return (
"<div class='item-title'>" +
esc(inv.investName) +
"</div><div class='item-meta'>投资金额:" +
esc(inv.investCapi) +
";比例:" +
esc(inv.investPercent) +
"</div>"
);
});
}
if (
y.investInfo &&
Array.isArray(y.investInfo) &&
y.investInfo.length
) {
html +=
"<h4 class='report-subheading'>对外投资</h4>";
html += itemList(y.investInfo, function (inv) {
var parts = [];
if (
inv.investCapi != null &&
inv.investCapi !== ""
) {
parts.push(
"投资金额:" + esc(inv.investCapi),
);
}
if (
inv.investPercent != null &&
inv.investPercent !== ""
) {
parts.push(
"投资比例:" + esc(inv.investPercent),
);
}
if (
inv.investRegNo != null &&
inv.investRegNo !== ""
) {
parts.push(
"注册号/统一码:" +
esc(inv.investRegNo),
);
}
if (inv.entityId != null && inv.entityId !== "") {
parts.push(
"主体标识:" + esc(inv.entityId),
);
}
if (inv.seqNo != null && inv.seqNo !== "") {
parts.push("序号:" + esc(inv.seqNo));
}
var meta =
parts.length > 0
? parts.join("")
: "—";
return (
"<div class='item-title'>" +
esc(inv.investName || "—") +
"</div><div class='item-meta'>" +
meta +
"</div>"
);
});
}
if (
y.reportSocialSecurityInfo &&
typeof y.reportSocialSecurityInfo === "object" &&
!Array.isArray(y.reportSocialSecurityInfo)
) {
html +=
"<h4 class='report-subheading'>社会保险信息</h4>";
html += kvTable(
y.reportSocialSecurityInfo,
Object.keys(
y.reportSocialSecurityInfo,
).sort(),
);
}
if (
y.reportGuaranteeInfo &&
y.reportGuaranteeInfo.length
) {
html +=
"<h4 class='report-subheading'>对外担保</h4>";
html += itemList(y.reportGuaranteeInfo, function (g) {
return kvTable(g, Object.keys(g));
});
}
if (
y.reportEquityChangeInfo &&
y.reportEquityChangeInfo.length
) {
html +=
"<h4 class='report-subheading'>股权变更</h4>";
html += itemList(
y.reportEquityChangeInfo,
function (e) {
return (
"<div class='item-title'>" +
esc(e.name) +
"</div><div class='item-meta'>" +
esc(e.beforePercent) +
" → " +
esc(e.afterPercent) +
"" +
esc(e.changeDate) +
"</div>"
);
},
);
}
var ch =
y.reportChangeInfo ||
y.rpportChangeInfo;
if (ch) {
if (typeof ch === "string" && ch.trim()) {
html +=
"<h4 class='report-subheading'>年报变更摘要</h4><div class='item-block'>" +
esc(ch) +
"</div>";
} else if (Array.isArray(ch) && ch.length) {
html +=
"<h4 class='report-subheading'>年报修改记录</h4>";
html += itemList(ch, function (c) {
return (
"<div class='item-title'>" +
esc(c.updateItem) +
"</div><div class='item-meta'>" +
esc(c.updateDate) +
"</div><div class='item-block' style='margin-top:4px'><strong>修改前:</strong>" +
esc(c.beforeUpdate) +
"<br/><strong>修改后:</strong>" +
esc(c.afterUpdate) +
"</div>"
);
});
}
}
html += "</div>";
});
return html;
}
function renderTaxViolationsSection(v) {
if (!v || !Array.isArray(v.items) || !v.items.length)
return "<p class='empty-hint'>暂无税收违法记录</p>";
return itemList(v.items, function (it) {
return (
"<div class='item-title'>" +
esc(it.entityName) +
"</div>" +
"<div class='item-meta'>纳税人识别号:" +
esc(it.taxpayerCode) +
";案件性质:" +
esc(it.caseType) +
";企业类别:" +
esc(it.entityCategory) +
"</div>" +
"<div class='item-block' style='margin-top:6px'><div><strong>违法事实:</strong>" +
esc(it.illegalFact) +
"</div><div style='margin-top:4px'><strong>处罚依据:</strong>" +
esc(it.punishBasis) +
"</div></div>" +
"<div class='item-meta'>违法期间:" +
esc(it.illegalStartDate) +
" ~ " +
esc(it.illegalEndDate) +
";公示时间:" +
esc(it.illegalTime) +
"</div>" +
"<div class='item-meta'>公示机关:" +
esc(it.publishDepartment) +
";检查机关:" +
esc(it.checkDepartment) +
";所属税务机关:" +
esc(it.belongDepartment) +
"</div>" +
"<div class='item-meta'>移送公安:" +
esc(it.police || "—") +
";中介机构:" +
esc(it.agencyPersonInfo || "—") +
"</div>"
);
});
}
function renderOwnTaxNotices(v) {
if (!v || !Array.isArray(v.items) || !v.items.length)
return "<p class='empty-hint'>暂无欠税公告</p>";
return itemList(v.items, function (it) {
return (
"<div class='item-title'>" +
esc(it.taxCategory || it.taxpayerName) +
"</div>" +
"<div class='item-meta'>纳税人:" +
esc(it.taxpayerName) +
";识别号:" +
esc(it.taxIdNumber) +
";税务类型:" +
esc(it.taxType) +
"</div>" +
"<div class='item-meta'>欠税余额:" +
esc(it.ownTaxBalance) +
";欠税金额:" +
esc(it.ownTaxAmount) +
";新发欠税余额:" +
esc(it.newOwnTaxBalance) +
"</div>" +
"<div class='item-meta'>发布日期:" +
esc(it.publishDate) +
";主管税务机关:" +
esc(it.department) +
"</div>" +
"<div class='item-meta'>法定代表人:" +
esc(it.legalPersonName) +
"" +
esc(it.personIdName) +
"" +
esc(it.personIdNumber) +
"</div>"
);
});
}
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,
annualReports: renderAnnualReports,
taxViolations: renderTaxViolationsSection,
ownTaxNotices: renderOwnTaxNotices,
};
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」先轮询预生成状态再 GET /pdf服务端优先读缓存必要时现场生成
var saveBtn = document.getElementById("btnSavePdf");
var loadingOverlay = document.getElementById("pdfLoadingOverlay");
var pdfLoadingMessage = document.getElementById("pdfLoadingMessage");
function setPdfLoadingText(t) {
if (pdfLoadingMessage) pdfLoadingMessage.textContent = t;
}
function pollPdfStatusUntilReady(reportId, maxMs, intervalMs) {
return new Promise(function (resolve) {
var start = Date.now();
function tick() {
fetch(
"/reports/qygl/" +
encodeURIComponent(reportId) +
"/pdf/status",
)
.then(function (r) {
if (!r.ok) return { status: "error" };
return r.json();
})
.then(function (j) {
if (!j || !j.status) {
setPdfLoadingText("正在准备 PDF…");
if (Date.now() - start >= maxMs) {
resolve("timeout");
return;
}
setTimeout(tick, intervalMs);
return;
}
if (j.status === "ready") {
setPdfLoadingText("正在下载…");
resolve("ready");
return;
}
if (j.status === "none") {
setPdfLoadingText(
j.message ||
"正在生成 PDF请稍候…",
);
resolve("none");
return;
}
if (j.status === "failed") {
setPdfLoadingText(
j.message ||
"预生成未完成,正在现场生成…",
);
resolve("failed");
return;
}
if (j.status === "pending") {
setPdfLoadingText(
j.message || "排队生成 PDF…",
);
} else if (j.status === "generating") {
setPdfLoadingText(
j.message || "正在生成 PDF…",
);
} else {
setPdfLoadingText("正在准备 PDF…");
}
if (Date.now() - start >= maxMs) {
resolve("timeout");
return;
}
setTimeout(tick, intervalMs);
})
.catch(function () {
resolve("error");
});
}
tick();
});
}
if (saveBtn) {
saveBtn.addEventListener("click", function () {
var btnText = saveBtn.textContent;
saveBtn.disabled = true;
saveBtn.textContent = "生成中...";
if (loadingOverlay) {
loadingOverlay.setAttribute("aria-hidden", "false");
}
setPdfLoadingText("正在准备 PDF…");
function restoreBtn() {
saveBtn.disabled = false;
saveBtn.textContent = btnText;
if (loadingOverlay) {
loadingOverlay.setAttribute("aria-hidden", "true");
}
}
try {
var path = window.location.pathname || "";
var segments = path.split("/");
var reportId = null;
for (var i = 0; i < segments.length; i++) {
if (
segments[i] === "qygl" &&
i + 1 < segments.length &&
segments[i + 1]
) {
reportId = segments[i + 1];
break;
}
}
if (
!reportId &&
reportData &&
reportData.reportId &&
typeof reportData.reportId === "string"
) {
reportId = reportData.reportId;
}
if (!reportId) {
console.error(
"无法从当前 URL 解析报告编号,路径为",
path,
);
restoreBtn();
return;
}
var pdfUrl =
"/reports/qygl/" +
encodeURIComponent(reportId) +
"/pdf";
pollPdfStatusUntilReady(
reportId,
120000,
400,
).then(function () {
return fetch(pdfUrl, { method: "GET" });
})
.then(function (resp) {
if (!resp.ok) {
throw new Error(
"生成 PDF 接口返回错误状态:" +
resp.status,
);
}
var contentType =
resp.headers.get("Content-Type") || "";
if (
contentType.indexOf(
"application/pdf",
) === -1 &&
contentType.indexOf(
"application/octet-stream",
) === -1
) {
return resp.text().then(function (txt) {
throw new Error(
"生成 PDF 失败:" + txt,
);
});
}
return resp.blob();
})
.then(function (blob) {
var fileName = "企业全景报告.pdf";
if (
reportData &&
reportData.entName &&
typeof reportData.entName === "string"
) {
fileName =
reportData.entName +
"_企业全景报告.pdf";
}
var url = window.URL.createObjectURL(blob);
var a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
restoreBtn();
})
.catch(function (e) {
console.error("生成 PDF 失败(后端接口)", e);
restoreBtn();
alert("生成 PDF 失败,请稍后重试");
});
} catch (e) {
console.error("触发生成 PDF 失败", e);
restoreBtn();
}
});
}
})();
</script>
</body>
</html>