4235 lines
180 KiB
HTML
4235 lines
180 KiB
HTML
<!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 {
|
||
/* QYGL7HBN 企业综合报告主题(与 bdrp QYGLJ1U9 组件视觉对齐) */
|
||
--qy-accent: #005be3;
|
||
--qy-accent-soft: #3bd8ff;
|
||
--qy-label-bg: #ebf2fd;
|
||
--qy-label-color: #005be3;
|
||
--qy-label-border: #a5c5f6;
|
||
--qy-value-border: #d1d5db;
|
||
--qy-gradient: linear-gradient(90deg, #005be3 0%, #3bd8ff 100%);
|
||
--blue-950: #0f2d6e;
|
||
--blue-900: #1e3a8a;
|
||
--blue-800: #005be3;
|
||
--blue-700: #005be3;
|
||
--blue-600: #2563eb;
|
||
--blue-500: #3b82f6;
|
||
--blue-200: #bfdbfe;
|
||
--blue-100: #dbeafe;
|
||
--blue-50: #eff6ff;
|
||
--red-700: #b91c1c;
|
||
--red-600: #dc2626;
|
||
--ink: #1f2937;
|
||
--ink-secondary: #374151;
|
||
--muted: #6b7280;
|
||
--line: #dce4ef;
|
||
--line-soft: #eef2f7;
|
||
--surface: #ffffff;
|
||
--surface-raised: #f8fafc;
|
||
--brand: var(--qy-accent);
|
||
--success: #0d9488;
|
||
--success-soft: rgba(13, 148, 136, 0.1);
|
||
--warn: #d97706;
|
||
--warn-soft: rgba(217, 119, 6, 0.12);
|
||
--danger: var(--red-600);
|
||
--danger-soft: rgba(220, 38, 38, 0.1);
|
||
--radius: 8px;
|
||
--radius-sm: 6px;
|
||
--shadow-sm: 0 2px 6px rgba(0, 91, 227, 0.08);
|
||
--shadow-md: none;
|
||
--table-border: var(--qy-label-border);
|
||
--table-line: var(--line-soft);
|
||
--table-head: var(--qy-label-bg);
|
||
--font-sans:
|
||
"Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||
--primary: var(--qy-accent);
|
||
--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: 0 auto;
|
||
padding: 0 8px 56px;
|
||
background: var(--surface);
|
||
border: none;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
overflow: hidden;
|
||
scroll-behavior: smooth;
|
||
--qy-accent: #005be3;
|
||
--qy-accent-soft: #3bd8ff;
|
||
--qy-label-bg: #ebf2fd;
|
||
--qy-label-color: #005be3;
|
||
--qy-label-border: #a5c5f6;
|
||
--qy-value-border: #d1d5db;
|
||
--qy-border: #dce4ef;
|
||
--qy-row-line: #eef2f7;
|
||
--qy-muted: #6b7280;
|
||
--qy-text: #1f2937;
|
||
--qy-surface: #fff;
|
||
--qy-module-gap: 10px;
|
||
--qy-cell-py: 6px;
|
||
--qy-cell-px: 12px;
|
||
--qy-fs-table: 13px;
|
||
--qy-fs-body: 14px;
|
||
--qy-fs-subhead: 14px;
|
||
--qy-gradient: linear-gradient(90deg, #005be3 0%, #3bd8ff 100%);
|
||
--qy-success: #0d9488;
|
||
--qy-danger: #dc2626;
|
||
font-size: var(--qy-fs-body);
|
||
color: var(--qy-text);
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.page::before {
|
||
display: none;
|
||
}
|
||
|
||
.page-inner {
|
||
padding: 0 8px 8px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.page {
|
||
margin: 0;
|
||
padding: 0 4px 48px;
|
||
}
|
||
.page-inner {
|
||
padding: 0 6px 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: 0 0 20px;
|
||
padding: 28px 20px 24px;
|
||
border-radius: var(--radius);
|
||
min-height: clamp(168px, 28vw, 220px);
|
||
background-color: #f0f7ff;
|
||
border: none;
|
||
box-shadow: none;
|
||
color: var(--ink);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header-poster {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center right;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
|
||
.header::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1;
|
||
pointer-events: none;
|
||
background: linear-gradient(
|
||
90deg,
|
||
rgba(255, 255, 255, 0.94) 0%,
|
||
rgba(255, 255, 255, 0.78) 42%,
|
||
rgba(255, 255, 255, 0.25) 68%,
|
||
transparent 100%
|
||
);
|
||
}
|
||
|
||
.header-inner {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
max-width: 78%;
|
||
padding: 0;
|
||
margin-top: 0;
|
||
background: transparent;
|
||
text-align: left;
|
||
}
|
||
|
||
.header-hero-badges {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 8px 10px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.header {
|
||
padding: 20px 14px 18px;
|
||
min-height: 200px;
|
||
}
|
||
.header::before {
|
||
background: linear-gradient(
|
||
180deg,
|
||
rgba(255, 255, 255, 0.92) 0%,
|
||
rgba(255, 255, 255, 0.82) 55%,
|
||
rgba(255, 255, 255, 0.45) 100%
|
||
);
|
||
}
|
||
.header-poster {
|
||
object-position: 75% center;
|
||
}
|
||
.header-inner {
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
|
||
.header-main {
|
||
padding: 0;
|
||
min-width: 0;
|
||
width: 100%;
|
||
text-align: left;
|
||
}
|
||
|
||
.header-hero-kicker {
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.header-score {
|
||
margin: 16px 0 0;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 8px 10px;
|
||
}
|
||
|
||
.header-score-inner {
|
||
display: inline-flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 8px 10px;
|
||
padding: 0;
|
||
background: transparent;
|
||
border: none;
|
||
box-shadow: none;
|
||
max-width: none;
|
||
text-align: left;
|
||
}
|
||
|
||
.header-main h1.report-title {
|
||
margin: 0 0 6px;
|
||
font-size: clamp(1.55rem, 4.8vw, 2.15rem);
|
||
font-weight: 800;
|
||
letter-spacing: 0.02em;
|
||
color: #6b7280;
|
||
line-height: 1.3;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.report-title-en {
|
||
margin: 0 0 14px;
|
||
font-size: clamp(0.95rem, 2.4vw, 1.55rem);
|
||
letter-spacing: 0.03em;
|
||
font-weight: 1000;
|
||
line-height: 1.25;
|
||
text-transform: uppercase;
|
||
color: transparent;
|
||
-webkit-text-stroke: 0.75px #9ca3af;
|
||
}
|
||
|
||
.header-main .subtitle.ent-name {
|
||
margin: 0 0 16px;
|
||
font-size: clamp(1.45rem, 4.5vw, 2rem);
|
||
font-weight: 800;
|
||
line-height: 1.35;
|
||
color: var(--qy-accent);
|
||
word-break: break-word;
|
||
}
|
||
|
||
.header-main .subtitle {
|
||
font-size: clamp(14px, 3.6vw, 17px);
|
||
font-weight: 400;
|
||
color: var(--ink-secondary);
|
||
word-break: break-word;
|
||
line-height: 1.75;
|
||
}
|
||
|
||
.header-main .subtitle + .subtitle {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.header-meta-line {
|
||
margin-top: 8px;
|
||
font-size: clamp(13px, 3.2vw, 15px);
|
||
font-weight: 400;
|
||
color: var(--muted);
|
||
}
|
||
|
||
.header-tags {
|
||
margin-top: 12px;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px 10px;
|
||
}
|
||
|
||
.tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 6px 12px;
|
||
border-radius: 999px;
|
||
font-size: clamp(12px, 3vw, 14px);
|
||
font-weight: 600;
|
||
line-height: 1.2;
|
||
border: 1px solid #93c5fd;
|
||
background: #eff6ff;
|
||
color: #1d4ed8;
|
||
}
|
||
|
||
.header .tag {
|
||
background: #eff6ff;
|
||
border-color: #93c5fd;
|
||
color: #1d4ed8;
|
||
}
|
||
|
||
.header-score .score {
|
||
display: inline-flex;
|
||
padding: 8px 16px;
|
||
border-radius: 7px;
|
||
font-size: clamp(14px, 3.4vw, 17px);
|
||
font-weight: 700;
|
||
line-height: 1.25;
|
||
color: #fff;
|
||
background: var(--qy-gradient);
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.header-score .label {
|
||
margin: 0;
|
||
font-size: inherit;
|
||
font-weight: 700;
|
||
letter-spacing: normal;
|
||
color: inherit;
|
||
display: inline-flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.header-score .pill {
|
||
border-width: 0;
|
||
margin: 0;
|
||
display: inline-flex;
|
||
padding: 8px 16px;
|
||
border-radius: 7px;
|
||
font-size: clamp(14px, 3.4vw, 17px);
|
||
font-weight: 700;
|
||
color: #fff;
|
||
background: var(--qy-gradient);
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.header-score .pill.low {
|
||
background: linear-gradient(90deg, #0d9488 0%, #14b8a6 100%);
|
||
}
|
||
|
||
.header-score .pill.mid {
|
||
background: linear-gradient(90deg, #d97706 0%, #f59e0b 100%);
|
||
}
|
||
|
||
.header-score .pill.high {
|
||
background: linear-gradient(90deg, #dc2626 0%, #f87171 100%);
|
||
}
|
||
|
||
.print-btn {
|
||
display: none;
|
||
}
|
||
|
||
.pdf-actions-fixed {
|
||
position: fixed;
|
||
right: 16px;
|
||
bottom: 80px;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.pdf-fixed-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 18px;
|
||
border-radius: 999px;
|
||
border: none;
|
||
background: var(--qy-accent);
|
||
color: #fff;
|
||
font-family: var(--font-sans);
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
box-shadow: 0 4px 14px rgba(0, 91, 227, 0.28);
|
||
}
|
||
|
||
.pdf-fixed-btn:hover {
|
||
opacity: 0.92;
|
||
}
|
||
|
||
.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: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 28px;
|
||
}
|
||
|
||
.section-card {
|
||
scroll-margin-top: 24px;
|
||
padding: 0 6px 8px;
|
||
background: transparent;
|
||
border: none;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
overflow: visible;
|
||
}
|
||
|
||
.section-card h2 {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 10px 12px;
|
||
margin: 0 0 18px;
|
||
padding: 0 6px 0 2px;
|
||
font-weight: 700;
|
||
letter-spacing: normal;
|
||
color: var(--qy-accent);
|
||
background: transparent;
|
||
border: none;
|
||
}
|
||
|
||
.section-card h2 .section-no {
|
||
flex-shrink: 0;
|
||
display: inline-block;
|
||
font-size: clamp(2rem, 6vw, 2.85rem);
|
||
font-weight: 800;
|
||
font-style: italic;
|
||
line-height: 1.15;
|
||
padding: 0 0.2em 0 0.06em;
|
||
background: var(--qy-gradient);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
}
|
||
|
||
.section-card h2 .section-title-cn {
|
||
margin: 0;
|
||
font-size: clamp(1.05rem, 3vw, 1.28rem);
|
||
font-weight: 700;
|
||
color: var(--qy-accent);
|
||
line-height: 1.35;
|
||
min-width: 0;
|
||
}
|
||
|
||
.section-card .section-body {
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.section-body h3,
|
||
.section-card .section-body h3 {
|
||
margin: var(--qy-module-gap) 0 0;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
font-size: var(--qy-fs-subhead);
|
||
font-weight: 700;
|
||
text-align: center;
|
||
color: var(--qy-label-color);
|
||
background: var(--qy-label-bg);
|
||
border: 1px solid var(--qy-label-border);
|
||
}
|
||
|
||
.section-body h3:first-child,
|
||
.section-card .section-body h3:first-of-type {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.report-subheading,
|
||
.qy-sub-heading {
|
||
margin: var(--qy-module-gap) 0 0 !important;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px) !important;
|
||
font-size: var(--qy-fs-subhead) !important;
|
||
font-weight: 700 !important;
|
||
letter-spacing: normal !important;
|
||
text-align: center !important;
|
||
color: var(--qy-label-color) !important;
|
||
background: var(--qy-label-bg) !important;
|
||
border: 1px solid var(--qy-label-border) !important;
|
||
border-radius: 0 !important;
|
||
}
|
||
|
||
.report-subheading:first-child,
|
||
.qy-sub-heading:first-child {
|
||
margin-top: 0 !important;
|
||
}
|
||
|
||
.report-annual-card-title {
|
||
margin: 0 0 0 !important;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px) !important;
|
||
font-size: var(--qy-fs-subhead) !important;
|
||
font-weight: 700 !important;
|
||
text-align: center !important;
|
||
color: var(--qy-label-color) !important;
|
||
background: var(--qy-label-bg) !important;
|
||
border: 1px solid var(--qy-label-border) !important;
|
||
border-bottom: 1px solid var(--qy-label-border) !important;
|
||
}
|
||
|
||
.report-annual-shell {
|
||
margin-bottom: var(--qy-module-gap);
|
||
padding: 0;
|
||
background: var(--qy-surface);
|
||
border: 1px solid var(--qy-label-border);
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.report-annual-shell:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.kv-table-wrap {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
margin-bottom: 0;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.kv-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
border-spacing: 0;
|
||
font-size: var(--qy-fs-table);
|
||
line-height: 1.55;
|
||
border: 1px solid var(--qy-label-border);
|
||
border-radius: 0;
|
||
table-layout: fixed;
|
||
}
|
||
|
||
.kv-table th,
|
||
.kv-table td {
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
vertical-align: middle;
|
||
word-break: break-word;
|
||
border: 1px solid var(--qy-label-border);
|
||
}
|
||
|
||
.kv-table tbody tr:nth-child(even),
|
||
.kv-table tbody tr:nth-child(odd) {
|
||
background: var(--qy-surface);
|
||
}
|
||
|
||
.kv-table th {
|
||
width: 28%;
|
||
font-weight: 600;
|
||
font-size: var(--qy-fs-table);
|
||
color: var(--qy-label-color);
|
||
background: var(--qy-label-bg);
|
||
text-align: left;
|
||
}
|
||
|
||
.kv-table td {
|
||
color: var(--qy-text);
|
||
font-weight: 400;
|
||
background: var(--qy-surface);
|
||
border-left: none;
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.kv-table th {
|
||
width: 36%;
|
||
}
|
||
}
|
||
|
||
.qy-info-grid {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
border-spacing: 0;
|
||
font-size: var(--qy-fs-table);
|
||
table-layout: fixed;
|
||
border: 1px solid var(--qy-label-border);
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.qy-info-grid th {
|
||
width: 18%;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
font-weight: 600;
|
||
color: var(--qy-label-color);
|
||
background: var(--qy-label-bg);
|
||
border: 1px solid var(--qy-label-border);
|
||
vertical-align: middle;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.qy-info-grid td {
|
||
width: 32%;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
color: var(--qy-text);
|
||
background: var(--qy-surface);
|
||
border: 1px solid var(--qy-value-border);
|
||
border-left: none;
|
||
vertical-align: middle;
|
||
word-break: break-word;
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.qy-info-grid {
|
||
display: block;
|
||
}
|
||
.qy-info-grid tbody,
|
||
.qy-info-grid tr {
|
||
display: block;
|
||
width: 100%;
|
||
}
|
||
.qy-info-grid th,
|
||
.qy-info-grid td {
|
||
display: block;
|
||
width: 100% !important;
|
||
box-sizing: border-box;
|
||
}
|
||
.qy-info-grid td {
|
||
border-top: none;
|
||
border-left: 1px solid var(--qy-value-border);
|
||
}
|
||
.qy-info-grid td + th {
|
||
border-top: 1px solid var(--qy-label-border);
|
||
}
|
||
}
|
||
|
||
.report-brand-footer {
|
||
position: relative;
|
||
margin: 32px 0 16px;
|
||
padding: 36px 16px;
|
||
text-align: center;
|
||
border-radius: 4px;
|
||
min-height: clamp(100px, 22vw, 140px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
background-color: #f4f8fc;
|
||
}
|
||
|
||
.footer-poster {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
|
||
.report-brand-footer p {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin: 0;
|
||
padding: 0 16px;
|
||
font-size: clamp(1rem, 3vw, 1.25rem);
|
||
font-weight: 700;
|
||
color: var(--qy-accent);
|
||
text-shadow: 0 1px 8px rgba(255, 255, 255, 0.85);
|
||
}
|
||
|
||
.kv-grid {
|
||
display: none;
|
||
}
|
||
|
||
.item-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: var(--qy-module-gap) 0 0;
|
||
border: 1px solid var(--qy-label-border);
|
||
border-radius: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.item-list:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.item-list li {
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
border-bottom: 1px solid var(--qy-label-border);
|
||
font-size: var(--qy-fs-table);
|
||
line-height: 1.55;
|
||
background: var(--qy-surface);
|
||
}
|
||
|
||
.item-list li:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.item-list li .item-title {
|
||
font-weight: 600;
|
||
color: var(--qy-text);
|
||
margin-bottom: 4px;
|
||
font-size: var(--qy-fs-subhead);
|
||
}
|
||
|
||
.item-list li .item-meta {
|
||
color: var(--qy-muted);
|
||
font-size: var(--qy-fs-table);
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.item-block,
|
||
.record-card {
|
||
margin-top: var(--qy-module-gap);
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
background: transparent;
|
||
border: 1px solid var(--qy-label-border);
|
||
border-radius: 0;
|
||
font-size: var(--qy-fs-table);
|
||
line-height: 1.55;
|
||
color: var(--qy-text);
|
||
}
|
||
|
||
.item-block:first-child,
|
||
.record-card:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.report-annual-shell {
|
||
padding: 0;
|
||
}
|
||
|
||
.report-annual-shell .report-annual-card-title + .kv-table-wrap,
|
||
.report-annual-shell .report-annual-card-title + table,
|
||
.report-annual-shell .report-annual-card-title + .qy-info-grid {
|
||
margin: 0;
|
||
}
|
||
|
||
.item-block .item-kv span.k {
|
||
color: var(--qy-label-color);
|
||
margin-right: 6px;
|
||
font-weight: 600;
|
||
font-size: var(--qy-fs-table);
|
||
}
|
||
|
||
.empty-hint {
|
||
margin: 0;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
font-size: var(--qy-fs-table);
|
||
text-align: center;
|
||
color: var(--qy-muted);
|
||
background: var(--qy-surface);
|
||
border: 1px solid var(--qy-label-border);
|
||
border-radius: 0;
|
||
}
|
||
|
||
.count-hint {
|
||
margin: 0;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
font-size: var(--qy-fs-table);
|
||
font-weight: 600;
|
||
color: var(--qy-label-color);
|
||
background: var(--qy-label-bg);
|
||
border: 1px solid var(--qy-label-border);
|
||
border-bottom: none;
|
||
}
|
||
|
||
.count-hint + .item-list,
|
||
.count-hint + .kv-table-wrap,
|
||
.count-hint + .qy-info-grid {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.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(--qy-label-color);
|
||
font-size: var(--qy-fs-table);
|
||
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);
|
||
}
|
||
|
||
/* 页眉综合评分区:避免被正文 .pill 浅色样式覆盖 */
|
||
.header-score .pill.low,
|
||
.header-score .pill.mid,
|
||
.header-score .pill.high {
|
||
color: #fff;
|
||
border: none;
|
||
}
|
||
|
||
.header-score .pill.low {
|
||
background: linear-gradient(90deg, #0d9488 0%, #14b8a6 100%);
|
||
}
|
||
|
||
.header-score .pill.mid {
|
||
background: linear-gradient(90deg, #d97706 0%, #f59e0b 100%);
|
||
}
|
||
|
||
.header-score .pill.high {
|
||
background: linear-gradient(90deg, #dc2626 0%, #f87171 100%);
|
||
}
|
||
|
||
.risk-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: var(--qy-cell-py) 0;
|
||
font-size: var(--qy-fs-body);
|
||
border-bottom: 1px solid var(--qy-row-line);
|
||
}
|
||
|
||
.item-list li.risk-row {
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
border-bottom: 1px solid var(--qy-row-line);
|
||
}
|
||
|
||
.risk-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.risk-row .ok {
|
||
color: var(--qy-success);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.risk-row .bad {
|
||
color: var(--qy-danger);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.risk-points-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr));
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.risk-point {
|
||
font-size: var(--qy-fs-table);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
padding: var(--qy-cell-py) var(--qy-cell-px);
|
||
background: var(--qy-label-bg);
|
||
color: var(--qy-text);
|
||
border: 1px solid var(--qy-label-border);
|
||
border-radius: 0;
|
||
}
|
||
|
||
.risk-point-name {
|
||
color: var(--qy-text);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.risk-status-ok {
|
||
color: var(--qy-success);
|
||
font-weight: 500;
|
||
font-size: var(--qy-fs-table);
|
||
}
|
||
|
||
.risk-status-bad {
|
||
color: var(--qy-danger);
|
||
font-weight: 500;
|
||
font-size: var(--qy-fs-table);
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
|
||
@page {
|
||
size: A4 portrait;
|
||
margin: 10mm 12mm;
|
||
}
|
||
|
||
/* 全页斜向透明红字水印;position:fixed 在 Chrome 打印/PDF 时每页重复 */
|
||
.report-watermark {
|
||
position: fixed;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: max-content;
|
||
max-width: none;
|
||
transform: translate(-50%, -50%) rotate(-45deg);
|
||
margin: 0;
|
||
padding: 0;
|
||
font-family: var(--font-sans);
|
||
font-size: 56px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
line-height: 1;
|
||
white-space: nowrap;
|
||
color: rgba(220, 38, 38, 0.14);
|
||
pointer-events: none;
|
||
user-select: none;
|
||
z-index: 500;
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
|
||
/* Headless Chrome 导出 PDF 时使用(不切换 print 媒体,避免海报/布局异常) */
|
||
body.pdf-export {
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
body.pdf-export .pdf-actions-fixed,
|
||
body.pdf-export .pdf-loading-overlay,
|
||
body.pdf-export .top-nav {
|
||
display: none !important;
|
||
}
|
||
body.pdf-export .page {
|
||
max-width: none;
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 0 0 24px;
|
||
overflow: visible;
|
||
}
|
||
body.pdf-export .page-inner {
|
||
padding: 0 !important;
|
||
}
|
||
body.pdf-export .report-sections,
|
||
body.pdf-export #reportSections {
|
||
padding: 0 12px;
|
||
}
|
||
body.pdf-export .section-card h2 {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
body.pdf-export .header {
|
||
margin: 0 0 16px !important;
|
||
border: none !important;
|
||
border-radius: 0 !important;
|
||
box-shadow: none !important;
|
||
padding: 28px 20px 24px !important;
|
||
min-height: clamp(168px, 22vw, 200px) !important;
|
||
overflow: hidden !important;
|
||
}
|
||
body.pdf-export .header-poster {
|
||
position: absolute !important;
|
||
inset: 0 !important;
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
object-fit: cover !important;
|
||
object-position: center right !important;
|
||
}
|
||
body.pdf-export .header::before {
|
||
display: block !important;
|
||
}
|
||
body.pdf-export .header-inner {
|
||
max-width: 78% !important;
|
||
text-align: left !important;
|
||
align-items: flex-start !important;
|
||
}
|
||
body.pdf-export .header-main {
|
||
text-align: left !important;
|
||
width: 100% !important;
|
||
}
|
||
body.pdf-export .header-score {
|
||
justify-content: flex-start !important;
|
||
width: 100% !important;
|
||
margin-top: 12px !important;
|
||
}
|
||
body.pdf-export .header-score-inner {
|
||
display: inline-flex !important;
|
||
flex-direction: row !important;
|
||
flex-wrap: nowrap !important;
|
||
align-items: center !important;
|
||
justify-content: flex-start !important;
|
||
gap: 10px !important;
|
||
max-width: none !important;
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
body.pdf-export .header-score .label {
|
||
display: inline-flex !important;
|
||
margin: 0 !important;
|
||
background: transparent !important;
|
||
}
|
||
body.pdf-export .header-score .score,
|
||
body.pdf-export .header-score .pill {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
body.pdf-export .header-poster,
|
||
body.pdf-export .footer-poster {
|
||
display: block !important;
|
||
visibility: visible !important;
|
||
opacity: 1 !important;
|
||
-webkit-print-color-adjust: exact !important;
|
||
print-color-adjust: exact !important;
|
||
}
|
||
|
||
@media print {
|
||
html,
|
||
body {
|
||
background: #fff !important;
|
||
margin: 0;
|
||
width: 100%;
|
||
min-height: auto;
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
.page {
|
||
max-width: none;
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 0;
|
||
border: none;
|
||
box-shadow: none;
|
||
border-radius: 0;
|
||
overflow: visible;
|
||
}
|
||
.page::before {
|
||
display: none;
|
||
}
|
||
.page-inner {
|
||
padding: 0 !important;
|
||
}
|
||
.top-nav {
|
||
display: none !important;
|
||
}
|
||
.pdf-actions-fixed,
|
||
.pdf-loading-overlay {
|
||
display: none !important;
|
||
}
|
||
.header {
|
||
background-color: #f0f7ff !important;
|
||
color: var(--ink) !important;
|
||
box-shadow: none !important;
|
||
border: none !important;
|
||
border-radius: 0;
|
||
margin-top: 0;
|
||
padding: 28px 20px 24px !important;
|
||
min-height: 180px !important;
|
||
overflow: hidden !important;
|
||
page-break-inside: avoid;
|
||
break-inside: avoid-page;
|
||
}
|
||
.header-poster,
|
||
.footer-poster {
|
||
position: absolute !important;
|
||
inset: 0 !important;
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
max-width: none !important;
|
||
object-fit: cover !important;
|
||
-webkit-print-color-adjust: exact !important;
|
||
print-color-adjust: exact !important;
|
||
}
|
||
.header-poster {
|
||
object-position: center right !important;
|
||
}
|
||
.footer-poster {
|
||
object-position: center !important;
|
||
}
|
||
.header-inner {
|
||
padding: 0 !important;
|
||
margin-top: 0 !important;
|
||
max-width: 78% !important;
|
||
gap: 0;
|
||
background: transparent !important;
|
||
text-align: left !important;
|
||
}
|
||
.header::before {
|
||
display: block !important;
|
||
}
|
||
.header-main {
|
||
padding: 0;
|
||
text-align: left !important;
|
||
width: 100% !important;
|
||
}
|
||
.header-score-inner {
|
||
display: inline-flex !important;
|
||
flex-direction: row !important;
|
||
flex-wrap: nowrap !important;
|
||
align-items: center !important;
|
||
justify-content: flex-start !important;
|
||
gap: 10px !important;
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none;
|
||
max-width: none !important;
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
}
|
||
.header-score .label {
|
||
display: inline-flex !important;
|
||
background: transparent !important;
|
||
}
|
||
.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: 12px 0 0 !important;
|
||
justify-content: flex-start !important;
|
||
width: 100% !important;
|
||
}
|
||
.header-score .score {
|
||
color: #fff !important;
|
||
background: var(--qy-gradient) !important;
|
||
}
|
||
.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: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
color: var(--qy-accent);
|
||
padding: 0 6px 0 2px !important;
|
||
page-break-after: avoid;
|
||
break-after: avoid-page;
|
||
}
|
||
.section-card .section-body {
|
||
padding: 12px 0 16px !important;
|
||
}
|
||
.kv-table thead {
|
||
display: table-header-group;
|
||
}
|
||
.kv-table tr,
|
||
.qy-info-grid tr {
|
||
page-break-inside: avoid;
|
||
break-inside: avoid-page;
|
||
}
|
||
.record-card,
|
||
.item-block,
|
||
.report-brand-footer {
|
||
page-break-inside: avoid;
|
||
break-inside: avoid-page;
|
||
}
|
||
.report-brand-footer {
|
||
min-height: 100px !important;
|
||
padding: 36px 16px !important;
|
||
margin: 24px 0 0;
|
||
background-color: #f4f8fc !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;
|
||
}
|
||
.report-watermark {
|
||
display: block !important;
|
||
font-size: 42pt;
|
||
color: rgba(220, 38, 38, 0.16) !important;
|
||
}
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="report-watermark" aria-hidden="true">海宇数据</div>
|
||
<div class="page">
|
||
<div class="page-inner">
|
||
<!-- 隐藏的导航容器,仅用于脚本生成目录,页面不展示 -->
|
||
<nav class="top-nav">
|
||
<div id="navLinks"></div>
|
||
</nav>
|
||
<header class="header">
|
||
<img
|
||
class="header-poster"
|
||
src="{{ .BannerPosterURL }}"
|
||
alt=""
|
||
aria-hidden="true"
|
||
/>
|
||
<div class="header-inner">
|
||
<div class="header-main">
|
||
<div class="header-hero-kicker">
|
||
<h1 class="report-title">企业综合报告</h1>
|
||
<p class="report-title-en">ENTERPRISE COMPREHENSIVE REPORT</p>
|
||
</div>
|
||
<div class="subtitle ent-name" id="entName">企业名称</div>
|
||
<div class="subtitle">
|
||
<span id="creditCodeLabel"
|
||
>统一社会信用代码:-</span
|
||
>
|
||
·
|
||
<span id="entStatusLabel">经营状态:-</span>
|
||
·
|
||
<span id="entTypeLabel">企业类型:-</span>
|
||
</div>
|
||
<div class="header-meta-line" id="reportTimeLabel">
|
||
报告生成时间:-
|
||
</div>
|
||
<div class="header-tags" id="headerTags"></div>
|
||
</div>
|
||
<aside class="header-score" aria-label="综合评分">
|
||
<div class="header-score-inner header-hero-badges">
|
||
<div class="score" id="overallScore">--</div>
|
||
<span id="riskLevelPill" class="pill low">风险:-</span>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
</header>
|
||
<main id="reportSections" class="report-sections"></main>
|
||
<footer class="report-brand-footer">
|
||
<img
|
||
class="footer-poster"
|
||
src="{{ .FooterPosterURL }}"
|
||
alt=""
|
||
aria-hidden="true"
|
||
/>
|
||
<p>更新更全信息欢迎使用海宇数据</p>
|
||
</footer>
|
||
</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 () {
|
||
// 后端通过模板引擎注入的企业报告数据(QYGL7HBN 聚合结果)
|
||
// ReportJSON 为合法的 JSON 对象字面量
|
||
var reportData = {{ .ReportJSON }};
|
||
var sectionOrder = [
|
||
"basic",
|
||
"branches",
|
||
"shareholding",
|
||
"controller",
|
||
"beneficiaries",
|
||
"investments",
|
||
"guarantees",
|
||
"management",
|
||
"assets",
|
||
"licenses",
|
||
"activities",
|
||
"inspections",
|
||
"risks",
|
||
"timeline",
|
||
"listed",
|
||
"annualReports",
|
||
"taxViolations",
|
||
"ownTaxNotices",
|
||
"customsCredit",
|
||
];
|
||
var sectionTitles = {
|
||
basic: "一、主体概览(企业基础信息)",
|
||
branches: "二、分支机构",
|
||
shareholding: "三、股权与控制(持股结构)",
|
||
controller: "四、实际控制人",
|
||
beneficiaries: "五、最终受益人",
|
||
investments: "六、对外投资",
|
||
guarantees: "七、对外担保(披露摘要)",
|
||
management: "八、人员与组织(高管与任职)",
|
||
assets: "九、资产与经营(财务摘要)",
|
||
licenses: "十、行政许可与资质",
|
||
activities: "十一、经营活动(招投标)",
|
||
inspections: "十二、抽查检查",
|
||
risks: "十三、风险与合规",
|
||
timeline: "十四、发展时间线",
|
||
listed: "十五、上市信息",
|
||
annualReports: "十六、企业年报(公示)",
|
||
taxViolations: "十七、税收违法",
|
||
ownTaxNotices: "十八、欠税公告",
|
||
customsCredit: "十九、进出口信用(海关)",
|
||
};
|
||
// 保持所有板块单列纵向排布
|
||
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: "风险得分",
|
||
dishonestDebtorCount: "失信被执行人数量",
|
||
limitHighDebtorCount: "限制高消费被执行人数量",
|
||
total: "记录总数",
|
||
hasAbnormal: "是否存在异常",
|
||
hasPunish: "是否存在处罚",
|
||
isCancelled: "是否注销",
|
||
customsRegisterCode: "海关注册编码",
|
||
customsRegisterDate: "注册日期",
|
||
creditLevel: "信用等级",
|
||
customsLevel: "海关等级",
|
||
customsBusinessType: "经营类别",
|
||
validDate: "有效期",
|
||
abnormalInfo: "异常信息",
|
||
punishInfoText: "处罚信息",
|
||
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 recordGet(obj) {
|
||
var keys = Array.prototype.slice.call(arguments, 1);
|
||
for (var i = 0; i < keys.length; i++) {
|
||
var k = keys[i];
|
||
if (
|
||
obj &&
|
||
obj[k] != null &&
|
||
obj[k] !== ""
|
||
) {
|
||
return obj[k];
|
||
}
|
||
}
|
||
return undefined;
|
||
}
|
||
|
||
function normalizeEntStatus(value) {
|
||
if (value == null || value === "") return undefined;
|
||
if (value === 1 || value === "1") return "存续";
|
||
if (value === 2 || value === "2") return "注销";
|
||
if (value === 3 || value === "3") return "吊销";
|
||
if (
|
||
typeof value === "string" &&
|
||
Number.isNaN(Number(value))
|
||
) {
|
||
return value;
|
||
}
|
||
return String(value);
|
||
}
|
||
|
||
function normalizeBasicListRecord(raw) {
|
||
if (!raw || typeof raw !== "object") return {};
|
||
var statusRaw = recordGet(
|
||
raw,
|
||
"ENTSTATUS",
|
||
"status",
|
||
"statusCode",
|
||
"ENTSTATUSCODE",
|
||
);
|
||
return {
|
||
entName: recordGet(raw, "ENTNAME", "entName"),
|
||
creditCode: recordGet(
|
||
raw,
|
||
"CREDITNO",
|
||
"SHXYDM",
|
||
"creditCode",
|
||
),
|
||
regNo: recordGet(raw, "REGNO", "regNo"),
|
||
orgCode: recordGet(raw, "ORGCODES", "orgCode"),
|
||
entType: recordGet(raw, "ENTTYPE", "entType"),
|
||
establishDate: recordGet(raw, "ESDATE", "establishDate"),
|
||
registeredCapital: recordGet(
|
||
raw,
|
||
"REGCAP",
|
||
"registeredCapital",
|
||
"regCap",
|
||
),
|
||
legalRepresentative: recordGet(
|
||
raw,
|
||
"FRNAME",
|
||
"legalRepresentative",
|
||
),
|
||
status: normalizeEntStatus(statusRaw),
|
||
approveDate: recordGet(raw, "APPRDATE", "approveDate"),
|
||
regProvince: recordGet(
|
||
raw,
|
||
"REGORGPROVINCE",
|
||
"regProvince",
|
||
),
|
||
};
|
||
}
|
||
|
||
var basicListGridKeys = [
|
||
"entName",
|
||
"creditCode",
|
||
"regNo",
|
||
"orgCode",
|
||
"entType",
|
||
"establishDate",
|
||
"registeredCapital",
|
||
"legalRepresentative",
|
||
"status",
|
||
"approveDate",
|
||
"regProvince",
|
||
];
|
||
|
||
var basicListLabelOverrides = {
|
||
creditCode: "信用代码",
|
||
legalRepresentative: "法定代表人",
|
||
registeredCapital: "注册资本",
|
||
establishDate: "成立日期",
|
||
approveDate: "核准日期",
|
||
regProvince: "所在省份",
|
||
orgCode: "组织机构代码",
|
||
};
|
||
|
||
function formatInReason(value) {
|
||
if (value == null || value === "") return "-";
|
||
var code = String(value).trim();
|
||
var map = {
|
||
"04": "未按规定公示年度报告",
|
||
"01": "未依照《企业信息公示暂行条例》第八条规定的期限公示年度报告",
|
||
};
|
||
return map[code] || code;
|
||
}
|
||
|
||
function normalizeRiskTag(tag) {
|
||
if (tag == null || tag === "") return "";
|
||
if (typeof tag === "string") return tag.trim();
|
||
if (typeof tag === "object" && tag.name) {
|
||
return String(tag.name).trim();
|
||
}
|
||
return String(tag).trim();
|
||
}
|
||
|
||
function formatPunishInfo(raw) {
|
||
if (!raw || raw === "[]" || raw === "无") return "—";
|
||
try {
|
||
var arr = JSON.parse(raw);
|
||
if (Array.isArray(arr)) {
|
||
return arr
|
||
.map(function (p) {
|
||
return (
|
||
"案件性质:" +
|
||
(p.case_type || "—") +
|
||
";当事人:" +
|
||
(p.case_litigant || "—") +
|
||
";编号:" +
|
||
(p.case_no || "—")
|
||
);
|
||
})
|
||
.join("\n");
|
||
}
|
||
} catch (e) {
|
||
/* ignore */
|
||
}
|
||
return String(raw);
|
||
}
|
||
|
||
function normalizeCustomsItem(it) {
|
||
if (!it || typeof it !== "object") return {};
|
||
return Object.assign({}, it, {
|
||
punishInfoText: formatPunishInfo(it.punishInfo),
|
||
});
|
||
}
|
||
|
||
var customsDetailKeys = [
|
||
"entityName",
|
||
"customsRegisterCode",
|
||
"customsRegisterDate",
|
||
"creditLevel",
|
||
"customsLevel",
|
||
"customsBusinessType",
|
||
"validDate",
|
||
"abnormalInfo",
|
||
];
|
||
|
||
function customsDetailKeysFor(it) {
|
||
var keys = customsDetailKeys.slice();
|
||
if (
|
||
it &&
|
||
it.punishInfo &&
|
||
formatPunishInfo(it.punishInfo) !== "—"
|
||
) {
|
||
keys.push("punishInfoText");
|
||
}
|
||
return keys.filter(function (k) {
|
||
return label(k) && label(k) !== k;
|
||
});
|
||
}
|
||
|
||
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
|
||
? "<div class='kv-table-wrap'><table class='kv-table kv-table-vertical qy-kv-table'><tbody>" +
|
||
rows.join("") +
|
||
"</tbody></table></div>"
|
||
: "<p class='empty-hint'>暂无数据</p>";
|
||
}
|
||
|
||
// 多列键值表(与 Vue KvGrid 一致:每行最多 2 组 label/value)
|
||
function kvGrid(obj, keys, cols, labelOverrides) {
|
||
if (!obj || typeof obj !== "object")
|
||
return "<p class='empty-hint'>暂无数据</p>";
|
||
var keyList = keys && keys.length ? keys : Object.keys(obj);
|
||
var overrides = labelOverrides || {};
|
||
var pairs = keyList.map(function (k) {
|
||
return {
|
||
k: k,
|
||
label: overrides[k] || label(k),
|
||
val: renderCell(obj[k]),
|
||
};
|
||
});
|
||
if (!pairs.length) return "<p class='empty-hint'>暂无数据</p>";
|
||
var pairsPerRow = Math.max(1, Math.min(2, cols || 2));
|
||
var html =
|
||
"<table class='qy-info-grid qy-kv-table'><tbody>";
|
||
for (var i = 0; i < pairs.length; i += pairsPerRow) {
|
||
html += "<tr>";
|
||
for (var j = 0; j < pairsPerRow; j += 1) {
|
||
var p = pairs[i + j];
|
||
if (p) {
|
||
html +=
|
||
"<th>" +
|
||
esc(p.label) +
|
||
"</th><td>" +
|
||
p.val +
|
||
"</td>";
|
||
} else {
|
||
html +=
|
||
"<th class='qy-info-grid-filler'></th><td class='qy-info-grid-filler'></td>";
|
||
}
|
||
}
|
||
html += "</tr>";
|
||
}
|
||
html += "</tbody></table>";
|
||
return html;
|
||
}
|
||
|
||
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, 2);
|
||
// 经营业务范围:单独占一整行,避免挤在多列网格里
|
||
html +=
|
||
"<h3>经营业务范围</h3>" +
|
||
"<div class='item-block'>" +
|
||
esc(v.businessScope || "-") +
|
||
"</div>";
|
||
var bl = reportData && reportData.basicList;
|
||
if (Array.isArray(bl)) {
|
||
if (bl.length) {
|
||
html +=
|
||
"<h3 class='report-subheading qy-sub-heading'>关联主体列表</h3>";
|
||
if (bl.length > 1) {
|
||
html +=
|
||
"<p class='count-hint'>共 " +
|
||
bl.length +
|
||
" 个关联主体</p>";
|
||
}
|
||
bl.forEach(function (b, i) {
|
||
html += "<div class='record-card'>";
|
||
if (bl.length > 1) {
|
||
html +=
|
||
"<p class='count-hint'>主体 " +
|
||
(i + 1) +
|
||
"</p>";
|
||
}
|
||
html += kvGrid(
|
||
normalizeBasicListRecord(b),
|
||
basicListGridKeys,
|
||
2,
|
||
basicListLabelOverrides,
|
||
);
|
||
html += "</div>";
|
||
});
|
||
} else {
|
||
html +=
|
||
"<p class='empty-hint'>暂无关联主体信息</p>";
|
||
}
|
||
}
|
||
return html;
|
||
}
|
||
|
||
function renderBranches(v) {
|
||
return itemList(v, function (b) {
|
||
return (
|
||
"<div class='item-title'>" +
|
||
esc(
|
||
recordGet(
|
||
b,
|
||
"name",
|
||
"entName",
|
||
"ENTNAME",
|
||
"branchName",
|
||
),
|
||
) +
|
||
"</div>" +
|
||
"<div class='item-meta'>注册号:" +
|
||
esc(recordGet(b, "regNo", "REGNO")) +
|
||
";信用代码:" +
|
||
esc(
|
||
recordGet(
|
||
b,
|
||
"creditCode",
|
||
"CREDITNO",
|
||
"SHXYDM",
|
||
),
|
||
) +
|
||
";登记机关:" +
|
||
esc(recordGet(b, "regOrg", "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 =
|
||
"<div class='kv-table-wrap'><table class='kv-table kv-table-vertical qy-kv-table'><tbody>" +
|
||
rows
|
||
.map(function (r) {
|
||
return (
|
||
"<tr><th>" +
|
||
esc(r.k) +
|
||
"</th><td>" +
|
||
esc(r.v) +
|
||
"</td></tr>"
|
||
);
|
||
})
|
||
.join("") +
|
||
"</tbody></table></div>";
|
||
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 html = kvGrid(
|
||
v,
|
||
[
|
||
"riskLevel",
|
||
"riskScore",
|
||
"dishonestDebtorCount",
|
||
"limitHighDebtorCount",
|
||
],
|
||
2,
|
||
);
|
||
var hasKeys = [
|
||
"hasCourtJudgments",
|
||
"hasJudicialAssists",
|
||
"hasDishonestDebtors",
|
||
"hasLimitHighDebtors",
|
||
"hasAdminPenalty",
|
||
"hasException",
|
||
"hasSeriousIllegal",
|
||
"hasTaxOwing",
|
||
"hasSeriousTaxIllegal",
|
||
"hasMortgage",
|
||
"hasEquityPledges",
|
||
"hasQuickCancel",
|
||
];
|
||
var hasLabels = [
|
||
"裁判文书",
|
||
"司法协助",
|
||
"失信被执行人",
|
||
"限高被执行人",
|
||
"行政处罚",
|
||
"经营异常",
|
||
"严重违法",
|
||
"欠税",
|
||
"重大税收违法",
|
||
"动产抵押",
|
||
"股权出质",
|
||
"简易注销",
|
||
];
|
||
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 +=
|
||
"<div class='kv-table-wrap'><table class='kv-table kv-table-vertical qy-kv-table'><tbody>" +
|
||
rows
|
||
.map(function (r) {
|
||
return (
|
||
"<tr><th>" +
|
||
esc(r.k) +
|
||
"</th><td>" +
|
||
esc(r.v) +
|
||
"</td></tr>"
|
||
);
|
||
})
|
||
.join("") +
|
||
"</tbody></table></div>";
|
||
}
|
||
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) {
|
||
var inR = recordGet(e, "inreason", "INREASON");
|
||
return (
|
||
"<div class='item-title'>" +
|
||
esc(
|
||
recordGet(e, "entName", "ENTNAME") ||
|
||
recordGet(e, "indate", "INDATE"),
|
||
) +
|
||
"</div>" +
|
||
"<div class='item-meta'>列入日期:" +
|
||
esc(recordGet(e, "indate", "INDATE")) +
|
||
";原因:" +
|
||
esc(formatInReason(inR)) +
|
||
";移出:" +
|
||
esc(recordGet(e, "outdate", "OUTDATE")) +
|
||
";列入机关:" +
|
||
esc(recordGet(e, "YR_REGORG", "yrRegOrg")) +
|
||
"</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 (
|
||
v.riskScore != null ||
|
||
(v.riskLevel != null && v.riskLevel !== "")
|
||
) {
|
||
html +=
|
||
"<h3 class='report-subheading qy-sub-heading'>综合风险</h3>" +
|
||
kvGrid(v, ["riskScore", "riskLevel"], 2);
|
||
}
|
||
if (v.tags && v.tags.length) {
|
||
html += "<h3>标签</h3><p>";
|
||
v.tags.forEach(function (t) {
|
||
var s = normalizeRiskTag(t);
|
||
if (s) {
|
||
html +=
|
||
"<span class='tag'>" + esc(s) + "</span> ";
|
||
}
|
||
});
|
||
html += "</p>";
|
||
}
|
||
// 风险点清单:命中 / 未命中,多列展示
|
||
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>";
|
||
}
|
||
if (!html) {
|
||
return "<p class='empty-hint'>暂无风险分析明细</p>";
|
||
}
|
||
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 qy-sub-heading'>实缴明细</h4>";
|
||
html += renderCapiItemList(sh.realCapiItems);
|
||
}
|
||
if (sh.shouldCapiItems && sh.shouldCapiItems.length) {
|
||
html +=
|
||
"<h4 class='report-subheading qy-sub-heading'>应缴明细</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 qy-sub-heading'>网站或网店</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 qy-sub-heading'>股东及出资</h4>";
|
||
html += renderAnnualShareholders(
|
||
y.reportShareholderInfo,
|
||
);
|
||
}
|
||
if (y.reportInvestInfo && y.reportInvestInfo.length) {
|
||
html +=
|
||
"<h4 class='report-subheading qy-sub-heading'>年报对外投资</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 qy-sub-heading'>对外投资</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 qy-sub-heading'>社会保险信息</h4>";
|
||
html += kvTable(
|
||
y.reportSocialSecurityInfo,
|
||
Object.keys(
|
||
y.reportSocialSecurityInfo,
|
||
).sort(),
|
||
);
|
||
}
|
||
if (
|
||
y.reportGuaranteeInfo &&
|
||
y.reportGuaranteeInfo.length
|
||
) {
|
||
html +=
|
||
"<h4 class='report-subheading qy-sub-heading'>对外担保</h4>";
|
||
html += itemList(y.reportGuaranteeInfo, function (g) {
|
||
return kvTable(g, Object.keys(g));
|
||
});
|
||
}
|
||
if (
|
||
y.reportEquityChangeInfo &&
|
||
y.reportEquityChangeInfo.length
|
||
) {
|
||
html +=
|
||
"<h4 class='report-subheading qy-sub-heading'>股权变更</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 qy-sub-heading'>年报变更摘要</h4><div class='item-block'>" +
|
||
esc(ch) +
|
||
"</div>";
|
||
} else if (Array.isArray(ch) && ch.length) {
|
||
html +=
|
||
"<h4 class='report-subheading qy-sub-heading'>年报修改记录</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>"
|
||
);
|
||
});
|
||
}
|
||
|
||
function renderCustomsCredit(v) {
|
||
if (!v) return "<p class='empty-hint'>暂无海关信用信息</p>";
|
||
var html =
|
||
"<h3 class='report-subheading qy-sub-heading'>进出口信用概览</h3>" +
|
||
kvGrid(
|
||
v,
|
||
[
|
||
"total",
|
||
"hasAbnormal",
|
||
"hasPunish",
|
||
"isCancelled",
|
||
],
|
||
2,
|
||
);
|
||
if (Array.isArray(v.items) && v.items.length) {
|
||
html +=
|
||
"<h3 class='report-subheading qy-sub-heading'>海关信用明细</h3>";
|
||
v.items.forEach(function (it, i) {
|
||
html += "<div class='qy-kv-record'>";
|
||
if (v.items.length > 1) {
|
||
html +=
|
||
"<p class='count-hint'>" +
|
||
esc(
|
||
it.entityName ||
|
||
"明细 " + (i + 1),
|
||
) +
|
||
"</p>";
|
||
}
|
||
html += kvTable(
|
||
normalizeCustomsItem(it),
|
||
customsDetailKeysFor(it),
|
||
);
|
||
html += "</div>";
|
||
});
|
||
} else {
|
||
html += "<p class='empty-hint'>暂无海关信用明细</p>";
|
||
}
|
||
return html;
|
||
}
|
||
|
||
var sectionRenderers = {
|
||
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,
|
||
customsCredit: renderCustomsCredit,
|
||
};
|
||
|
||
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 formatSectionHeading(title) {
|
||
if (!title) return "";
|
||
return String(title)
|
||
.replace(/^[一二三四五六七八九十]+、\s*/, "")
|
||
.replace(/(([^)]+))/g, "—$1");
|
||
}
|
||
|
||
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;
|
||
var levelStr = String(level ?? "").trim();
|
||
pill.className = "pill";
|
||
if (levelStr.includes("高")) pill.classList.add("high");
|
||
else if (levelStr.includes("中")) pill.classList.add("mid");
|
||
else pill.classList.add("low");
|
||
var tagsBox = document.getElementById("headerTags");
|
||
tagsBox.innerHTML = "";
|
||
(ro.tags || []).forEach(function (t) {
|
||
var s = normalizeRiskTag(t);
|
||
if (!s) return;
|
||
var span = document.createElement("span");
|
||
span.className = "tag";
|
||
span.textContent = s;
|
||
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 = "";
|
||
var sectionIndex = 0;
|
||
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");
|
||
var rawTitle = sectionTitles[key] || key;
|
||
var no = String(sectionIndex + 1).padStart(2, "0");
|
||
sectionIndex += 1;
|
||
title.innerHTML =
|
||
"<span class='section-no'>" +
|
||
no +
|
||
"</span><span class='section-title-cn'>" +
|
||
esc(formatSectionHeading(rawTitle)) +
|
||
"</span>";
|
||
card.appendChild(title);
|
||
var body = document.createElement("div");
|
||
body.className = "section-body";
|
||
body.innerHTML = renderSectionContent(key, value);
|
||
card.appendChild(body);
|
||
container.appendChild(card);
|
||
});
|
||
document.body.dataset.qyglReportReady = "1";
|
||
}
|
||
|
||
function loadReport() {
|
||
try {
|
||
// 使用后端注入的 reportData 直接渲染
|
||
if (!reportData || typeof reportData !== "object") {
|
||
reportData = {
|
||
entName: "未加载到数据",
|
||
basic: {},
|
||
risks: {},
|
||
riskOverview: {},
|
||
};
|
||
}
|
||
render();
|
||
} catch (e) {
|
||
console.error("渲染企业报告失败", e);
|
||
}
|
||
}
|
||
loadReport();
|
||
|
||
document.body.dataset.qyglReportReady = document.body.dataset.qyglReportReady || "1";
|
||
|
||
// 绑定「保存为 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>
|