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

3518 lines
151 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" />
<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) {
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>