diff --git a/2000s/certs/certificate.html b/2000s/certs/certificate.html
new file mode 100644
index 0000000..29c1da7
--- /dev/null
+++ b/2000s/certs/certificate.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+ 证书核验详情
+
+
+
+
+
+
+
+
+
+
+
返回查询
+
+
+
+
+
+
+
+
+ Certificate Preview
+ 证书原件预览
+ 点击下方预览区域或使用顶部按钮,即可加载对应证书原件图片。
+
+
![证书原件]()
+
+ 证
+ 点击加载证书图片
+ 原件图片将从当前证书编号对应的 `img/*.svg` 文件中读取。
+
+
+
+
+
+
+
+
+ Certificate Detail
+ 证书明细信息
+ 以下信息由证书数据库实时读取,用于核验与归档展示。
+
+
+ | 证书编号 | - |
+ | 持有人姓名 | - |
+ | 荣誉名称 | - |
+ | 颁发日期 | - |
+ | 颁发机构 | - |
+ | 详细描述 | - |
+ | 系统备注 |
+ | 核验时间 | - |
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2000s/certs/index.html b/2000s/certs/index.html
new file mode 100644
index 0000000..df10193
--- /dev/null
+++ b/2000s/certs/index.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+ 证书查询系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
检索条件
+
输入编号、姓名或奖项名后即可开始查询
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
查询结果
+
点击“查看详情”进入证书详情页
+
+
+
+
请输入查询条件,或使用扫码功能快速检索证书。
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2000s/css/notice-banner.css b/2000s/css/notice-banner.css
new file mode 100644
index 0000000..ee8003c
--- /dev/null
+++ b/2000s/css/notice-banner.css
@@ -0,0 +1,136 @@
+.notice-banner {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 9999;
+ display: none;
+ justify-content: center;
+ padding: 10px 14px;
+ color: #102033;
+ font-family: "Noto Sans SC", "Microsoft YaHei", Arial, sans-serif;
+ background: linear-gradient(90deg, #fff7d6, #dff7ff);
+ border-bottom: 1px solid rgba(16, 32, 51, 0.14);
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
+}
+
+.notice-banner.is-visible {
+ display: flex;
+}
+
+.notice-banner__inner {
+ width: min(100%, 1120px);
+ display: grid;
+ grid-template-columns: auto 1fr auto auto;
+ align-items: center;
+ gap: 10px;
+}
+
+.notice-banner__badge {
+ min-width: 40px;
+ padding: 4px 9px;
+ border-radius: 999px;
+ color: #ffffff;
+ background: #2563eb;
+ font-size: 12px;
+ font-weight: 700;
+ text-align: center;
+ line-height: 1.2;
+}
+
+.notice-banner[data-level="warning"] .notice-banner__badge {
+ background: #b45309;
+}
+
+.notice-banner[data-level="error"] .notice-banner__badge {
+ background: #be123c;
+}
+
+.notice-banner[data-level="success"] .notice-banner__badge {
+ background: #047857;
+}
+
+.notice-banner__content {
+ min-width: 0;
+ font-size: 14px;
+ line-height: 1.45;
+}
+
+.notice-banner__title {
+ margin-right: 6px;
+ font-weight: 700;
+}
+
+.notice-banner__message {
+ color: rgba(16, 32, 51, 0.84);
+}
+
+.notice-banner__link {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 30px;
+ padding: 0 12px;
+ border-radius: 999px;
+ color: #ffffff;
+ background: #075985;
+ font-size: 14px;
+ font-weight: 700;
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+.notice-banner__link:hover {
+ background: #0369a1;
+ text-decoration: none;
+}
+
+.notice-banner__close {
+ width: 30px;
+ height: 30px;
+ border: 0;
+ border-radius: 50%;
+ color: #102033;
+ background: rgba(16, 32, 51, 0.08);
+ font-size: 20px;
+ line-height: 1;
+ cursor: pointer;
+}
+
+.notice-banner__close:hover {
+ background: rgba(16, 32, 51, 0.14);
+}
+
+body.has-notice-banner {
+ padding-top: calc(var(--notice-body-pad-top, 0px) + var(--notice-banner-height, 0px) + 12px) !important;
+}
+
+@media (max-width: 640px) {
+ .notice-banner {
+ padding: 9px 10px;
+ }
+
+ .notice-banner__inner {
+ grid-template-columns: auto 1fr auto;
+ gap: 8px;
+ }
+
+ .notice-banner__link {
+ grid-column: 2 / 3;
+ justify-self: start;
+ }
+
+ .notice-banner__content {
+ font-size: 13px;
+ }
+}
+
+@media print {
+ .notice-banner {
+ display: none !important;
+ }
+
+ body.has-notice-banner {
+ padding-top: var(--notice-body-pad-top, 0px) !important;
+ }
+}
diff --git a/2000s/css/notice-page.css b/2000s/css/notice-page.css
new file mode 100644
index 0000000..2d5a173
--- /dev/null
+++ b/2000s/css/notice-page.css
@@ -0,0 +1,536 @@
+:root {
+ --bg: #07111f;
+ --panel: rgba(9, 20, 40, 0.78);
+ --panel-strong: rgba(10, 24, 46, 0.94);
+ --line: rgba(103, 213, 255, 0.22);
+ --line-strong: rgba(103, 213, 255, 0.48);
+ --text: #edf7ff;
+ --muted: #96b2d1;
+ --accent: #67d5ff;
+ --accent-strong: #2de2a6;
+ --warning: #ffcc66;
+ --font-display: "Orbitron", "Segoe UI", sans-serif;
+ --font-body: "Noto Sans SC", "Microsoft YaHei", sans-serif;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html {
+ color-scheme: dark;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ color: var(--text);
+ font-family: var(--font-body);
+ background:
+ linear-gradient(135deg, rgba(7, 17, 31, 0.96), rgba(10, 28, 54, 0.96)),
+ repeating-linear-gradient(90deg, rgba(103, 213, 255, 0.055) 0 1px, transparent 1px 72px),
+ repeating-linear-gradient(0deg, rgba(103, 213, 255, 0.045) 0 1px, transparent 1px 72px);
+}
+
+a {
+ color: inherit;
+}
+
+.notice-page {
+ width: min(calc(100% - 32px), 1120px);
+ margin: 0 auto;
+ padding: 28px 0 56px;
+}
+
+.topbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ margin-bottom: 34px;
+}
+
+.brand,
+.back-link,
+.primary-action {
+ display: inline-flex;
+ align-items: center;
+ min-height: 40px;
+ border-radius: 999px;
+ text-decoration: none;
+}
+
+.brand {
+ gap: 10px;
+ color: var(--text);
+ font-weight: 700;
+}
+
+.brand-mark {
+ display: inline-grid;
+ place-items: center;
+ width: 40px;
+ height: 40px;
+ border: 1px solid var(--line-strong);
+ border-radius: 12px;
+ color: var(--accent);
+ font-family: var(--font-display);
+ background: rgba(103, 213, 255, 0.08);
+}
+
+.back-link {
+ padding: 0 16px;
+ border: 1px solid var(--line);
+ color: var(--muted);
+ background: rgba(255, 255, 255, 0.04);
+}
+
+.back-link:hover,
+.primary-action:hover {
+ border-color: var(--line-strong);
+ color: var(--text);
+}
+
+.notice-hero {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 280px;
+ gap: 22px;
+ align-items: stretch;
+ margin-bottom: 22px;
+}
+
+.hero-copy,
+.status-panel,
+.notice-card,
+.side-card {
+ border: 1px solid var(--line);
+ background: var(--panel);
+ box-shadow: 0 28px 60px rgba(0, 0, 0, 0.32);
+ backdrop-filter: blur(18px);
+}
+
+.hero-copy {
+ padding: clamp(28px, 5vw, 54px);
+ border-radius: 24px;
+}
+
+.eyebrow {
+ display: inline-block;
+ margin-bottom: 14px;
+ color: var(--accent);
+ font-family: var(--font-display);
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+h1,
+h2,
+p {
+ margin-top: 0;
+}
+
+h1 {
+ margin-bottom: 16px;
+ font-size: clamp(34px, 6vw, 64px);
+ line-height: 1.05;
+}
+
+.hero-copy p {
+ max-width: 760px;
+ margin-bottom: 0;
+ color: var(--muted);
+ font-size: 17px;
+ line-height: 1.8;
+}
+
+.status-panel {
+ display: flex;
+ min-height: 240px;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 24px;
+ border-radius: 24px;
+}
+
+.status-badge {
+ width: max-content;
+ padding: 7px 12px;
+ border-radius: 999px;
+ color: #06111f;
+ background: var(--warning);
+ font-size: 13px;
+ font-weight: 800;
+}
+
+.status-code {
+ color: var(--accent);
+ font-family: var(--font-display);
+ font-size: 44px;
+ font-weight: 700;
+}
+
+.status-panel p {
+ margin-bottom: 0;
+ color: var(--muted);
+}
+
+.notice-layout {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 320px;
+ gap: 22px;
+ align-items: start;
+}
+
+.notice-card,
+.side-card {
+ border-radius: 20px;
+}
+
+.notice-card {
+ padding: clamp(24px, 4vw, 40px);
+}
+
+.notice-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-bottom: 22px;
+}
+
+.notice-meta span,
+.info-label {
+ color: var(--muted);
+ font-size: 13px;
+}
+
+.notice-meta span {
+ padding: 6px 10px;
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.04);
+}
+
+.notice-card h2,
+.side-card h2 {
+ margin-bottom: 12px;
+ font-size: 24px;
+}
+
+.notice-card p,
+.side-card p {
+ color: var(--muted);
+ line-height: 1.8;
+}
+
+.info-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 12px;
+ margin-top: 30px;
+}
+
+.info-grid div {
+ min-height: 92px;
+ padding: 16px;
+ border: 1px solid var(--line);
+ border-radius: 16px;
+ background: rgba(255, 255, 255, 0.04);
+}
+
+.info-grid strong {
+ display: block;
+ margin-top: 8px;
+ font-size: 16px;
+}
+
+.side-card {
+ padding: 24px;
+}
+
+code {
+ color: var(--accent);
+ font-family: Consolas, "Courier New", monospace;
+}
+
+.primary-action {
+ justify-content: center;
+ width: 100%;
+ margin-top: 14px;
+ padding: 0 18px;
+ border: 1px solid rgba(45, 226, 166, 0.5);
+ color: #06111f;
+ background: linear-gradient(135deg, var(--accent), var(--accent-strong));
+ font-weight: 800;
+}
+
+@media (max-width: 840px) {
+ .notice-hero,
+ .notice-layout {
+ grid-template-columns: 1fr;
+ }
+
+ .status-panel {
+ min-height: 180px;
+ }
+
+ .info-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Light visual refresh for notice detail */
+html {
+ color-scheme: light;
+}
+
+body {
+ color: #172033;
+ background:
+ linear-gradient(135deg, rgba(236, 248, 255, 0.92), rgba(255, 255, 255, 0.96) 48%, rgba(245, 250, 255, 0.94)),
+ linear-gradient(90deg, rgba(37, 99, 235, 0.045) 1px, transparent 1px),
+ linear-gradient(0deg, rgba(20, 184, 166, 0.035) 1px, transparent 1px);
+ background-size: auto, 52px 52px, 52px 52px;
+}
+
+.brand {
+ color: #172033;
+}
+
+.brand-mark {
+ color: #2563eb;
+ background: rgba(37, 99, 235, 0.07);
+ border-color: rgba(37, 99, 235, 0.2);
+}
+
+.back-link {
+ color: #66758a;
+ background: #ffffff;
+ border-color: rgba(120, 144, 176, 0.24);
+}
+
+.back-link:hover,
+.primary-action:hover {
+ color: #172033;
+ border-color: rgba(37, 99, 235, 0.36);
+}
+
+.hero-copy,
+.status-panel,
+.notice-card,
+.side-card {
+ background: rgba(255, 255, 255, 0.84);
+ border-color: rgba(120, 144, 176, 0.24);
+ box-shadow: 0 18px 44px rgba(47, 74, 112, 0.12);
+ backdrop-filter: blur(16px);
+}
+
+.eyebrow,
+.status-code,
+code {
+ color: #2563eb;
+}
+
+h1,
+h2,
+.info-grid strong {
+ color: #172033;
+}
+
+.hero-copy p,
+.status-panel p,
+.notice-card p,
+.side-card p,
+.notice-meta span,
+.info-label {
+ color: #66758a;
+}
+
+.status-badge {
+ color: #854d0e;
+ background: #fef3c7;
+}
+
+.notice-meta span,
+.info-grid div {
+ background: rgba(248, 251, 255, 0.86);
+ border-color: rgba(120, 144, 176, 0.2);
+}
+
+.primary-action {
+ color: #ffffff;
+ background: linear-gradient(135deg, #2563eb, #0891b2);
+ border-color: rgba(37, 99, 235, 0.16);
+ box-shadow: 0 12px 24px rgba(37, 99, 235, 0.16);
+}
+
+/* Youthful unified layout refresh */
+:root {
+ --joy-blue: #2563eb;
+ --joy-cyan: #06b6d4;
+ --joy-mint: #10b981;
+ --joy-coral: #fb7185;
+ --joy-yellow: #facc15;
+ --joy-ink: #172033;
+ --joy-muted: #627187;
+ --joy-line: rgba(96, 116, 148, 0.18);
+ --joy-card: rgba(255, 255, 255, 0.9);
+}
+
+body {
+ background:
+ linear-gradient(128deg, rgba(255, 255, 255, 0.96) 0 34%, rgba(232, 249, 255, 0.9) 34% 58%, rgba(255, 246, 249, 0.92) 58% 100%),
+ linear-gradient(90deg, rgba(37, 99, 235, 0.04) 1px, transparent 1px),
+ linear-gradient(0deg, rgba(16, 185, 129, 0.035) 1px, transparent 1px);
+ background-size: auto, 44px 44px, 44px 44px;
+}
+
+.notice-page {
+ padding-top: 24px;
+}
+
+.topbar {
+ margin-bottom: 22px;
+}
+
+.brand-mark {
+ border-radius: 10px;
+ color: #ffffff;
+ background: linear-gradient(135deg, var(--joy-blue), var(--joy-coral));
+ border-color: transparent;
+}
+
+.back-link {
+ min-height: 38px;
+ border-radius: 12px;
+}
+
+.notice-hero {
+ grid-template-columns: minmax(0, 1fr) minmax(220px, 0.28fr);
+ gap: 16px;
+ margin-bottom: 16px;
+}
+
+.notice-layout {
+ grid-template-columns: minmax(0, 1fr) minmax(260px, 0.34fr);
+ gap: 16px;
+}
+
+.hero-copy,
+.status-panel,
+.notice-card,
+.side-card {
+ border-radius: 18px;
+ border-color: var(--joy-line);
+ background: var(--joy-card);
+ box-shadow: 0 16px 34px rgba(37, 63, 101, 0.1);
+}
+
+.hero-copy {
+ min-height: 250px;
+ display: grid;
+ align-content: center;
+ padding: clamp(26px, 5vw, 48px);
+}
+
+.hero-copy,
+.notice-card,
+.side-card,
+.status-panel {
+ position: relative;
+ overflow: hidden;
+}
+
+.hero-copy::after,
+.notice-card::after,
+.side-card::after,
+.status-panel::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 4px;
+ background: linear-gradient(90deg, var(--joy-blue), var(--joy-cyan), var(--joy-mint), var(--joy-coral));
+ pointer-events: none;
+}
+
+.eyebrow {
+ width: max-content;
+ padding: 8px 14px;
+ border-radius: 999px;
+ background: linear-gradient(135deg, rgba(37, 99, 235, 0.11), rgba(6, 182, 212, 0.11));
+ color: var(--joy-blue);
+ letter-spacing: 0.12em;
+}
+
+h1 {
+ letter-spacing: 0;
+}
+
+.status-panel {
+ position: relative;
+ min-height: 250px;
+ justify-content: center;
+ gap: 22px;
+ border-top: 5px solid var(--joy-coral);
+}
+
+.status-code {
+ color: var(--joy-coral);
+ font-size: clamp(34px, 4vw, 44px);
+}
+
+.status-badge,
+.notice-meta span {
+ border: 0;
+}
+
+.status-badge {
+ color: #8a4b05;
+ background: #fff2bf;
+}
+
+.notice-meta span,
+.info-grid div {
+ border-radius: 14px;
+ background: #f8fbff;
+ border-color: rgba(96, 116, 148, 0.12);
+}
+
+.notice-card,
+.side-card {
+ padding: clamp(22px, 4vw, 34px);
+}
+
+.info-grid {
+ gap: 10px;
+ margin-top: 24px;
+}
+
+.primary-action {
+ min-height: 46px;
+ border-radius: 12px;
+ background: linear-gradient(135deg, var(--joy-blue), var(--joy-cyan));
+}
+
+@media (max-width: 520px) {
+ .notice-page {
+ width: min(calc(100% - 20px), 1120px);
+ padding-top: 16px;
+ }
+
+ .topbar {
+ align-items: flex-start;
+ flex-direction: column;
+ margin-bottom: 22px;
+ }
+
+ .hero-copy,
+ .status-panel,
+ .notice-card,
+ .side-card {
+ border-radius: 16px;
+ }
+}
diff --git a/2000s/css/style.css b/2000s/css/style.css
new file mode 100644
index 0000000..2742f43
--- /dev/null
+++ b/2000s/css/style.css
@@ -0,0 +1,186 @@
+/* ===========================================
+ 1. 通用响应式底座 (所有样式均需依赖)
+ =========================================== */
+:root {
+ --text-color: #333;
+ --paper-bg: #fff;
+ --wish-color: #333;
+ --border-color: #ddd;
+ --font-family-base: "Noto Serif SC", serif;
+ --font-family-sig: "Noto Serif SC", serif; /* 默认署名字体 */
+}
+
+body {
+ margin: 0;
+ padding: 20px;
+ background-color: #f6f6f6; /* 默认外背景 */
+ font-family: var(--font-family-base);
+ color: var(--text-color);
+ display: flex;
+ justify-content: center;
+ transition: background 0.3s;
+}
+
+/* 自适应容器 */
+.letter-container {
+ width: 100%;
+ max-width: 800px;
+ min-height: 85vh;
+ padding: 5% 7%;
+ box-sizing: border-box;
+ position: relative;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.05);
+ background-color: var(--paper-bg);
+ border: 1px solid var(--border-color);
+ transition: all 0.3s ease;
+}
+
+/* 手机端适配 */
+@media (max-width: 600px) {
+ body { padding: 10px; }
+ .letter-container { padding: 35px 20px; }
+}
+
+/* 传统书信格式公共逻辑 */
+.salutation {
+ font-size: 1.2rem;
+ margin-top: 0;
+ margin-bottom: 1.5rem;
+ font-weight: bold;
+}
+
+.content p {
+ text-indent: 2em; /* 首行缩进 */
+ line-height: 1.8;
+ text-align: justify;
+ margin-bottom: 1rem;
+}
+
+.closing { margin-top: 2.5rem; }
+.wish-prefix { text-indent: 2em; margin: 0; color: var(--wish-color); }
+.wish-suffix { font-weight: bold; margin-top: 5px; color: var(--wish-color); }
+
+.footer {
+ text-align: right;
+ margin-top: 4rem;
+ line-height: 1.6;
+}
+
+.signature {
+ font-size: 1.1em;
+ font-family: var(--font-family-sig);
+}
+
+.text-right {
+ text-align: right;
+}
+
+/* ===========================================
+ 2. 原始场景皮肤 (Modern, Traditional, Vintage)
+ =========================================== */
+/* ... (保留你之前的定义,此处略以节省空间,直接写新的) ... */
+.theme-modern { --paper-bg: #fff; border-top: 4px solid #333; }
+.theme-traditional { --paper-bg: #fdfaf2; writing-mode: vertical-rl; height: 650px; }
+@media (max-width: 600px) { .theme-traditional { writing-mode: horizontal-tb; height: auto; } }
+.theme-vintage { --paper-bg: #fff; border: 2px solid #b71c1c; background-image: repeating-linear-gradient(transparent, transparent 31px, rgba(183, 28, 28, 0.1) 31px, rgba(183, 28, 28, 0.1) 32px); line-height: 32px; --text-color: #b71c1c; --wish-color: #b71c1c; }
+
+
+/* ===========================================
+ 3. [新增] 青春与多样场景皮肤
+ =========================================== */
+
+/* --- A. 青春派对 (Youthful) --- */
+/* 特点:明亮多彩、活力、柔和 */
+.theme-youth {
+ --paper-bg: #fff;
+ --text-color: #4a4a4a;
+ --wish-color: #ff6b6b; /* 鲜艳的颜色 */
+ --border-color: #ffe0e0;
+ font-family: "PingFang SC", "Microsoft YaHei", sans-serif; /* 使用现代无衬线字体 */
+
+ border-radius: 15px; /* 圆角增加亲和力 */
+ border: 3px solid #ffe0e0;
+}
+
+/* 装饰:顶部和底部加点彩色条纹 */
+.theme-youth::before {
+ content: "";
+ position: absolute;
+ top: 0; left: 0; right: 0; height: 8px;
+ background: linear-gradient(90deg, #ff6b6b, #feca57, #48dbfb, #ff9f43);
+ border-radius: 15px 15px 0 0;
+}
+
+.theme-youth .salutation { color: #222; }
+.theme-youth .content p { text-indent: 0; text-align: left; background-color: #fcfcfc; padding: 10px; border-radius: 8px; margin-bottom: 1.2rem; } /* 类似气泡感 */
+.theme-youth .wish-prefix { text-indent: 0; padding-left: 1rem; } /* 青春风不一定强制缩进 */
+.theme-youth .wish-suffix { font-weight: 800; font-size: 1.1rem; }
+.theme-youth .footer { color: #888; }
+.theme-youth .signature { color: #333; font-weight: bold; }
+
+
+/* --- B. 青葱校园 (Campus) --- */
+/* 特点:数学格子纸、怀旧、清爽蓝调 */
+.theme-campus {
+ --paper-bg: #fdfdfd;
+ --text-color: #3e3e3e;
+ --wish-color: #2980b9; /* 校园蓝 */
+ --border-color: #87ceeb;
+ font-family: "SimSun", serif; /* 怀旧宋体 */
+
+ /* 模拟蓝格子纸 */
+ background-image:
+ linear-gradient(rgba(135, 206, 235, 0.3) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(135, 206, 235, 0.3) 1px, transparent 1px);
+ background-size: 25px 25px; /* 格子大小 */
+}
+
+/* 顶部加一个类似书签或校徽的圆 */
+.theme-campus::after {
+ content: "CAMPUS";
+ position: absolute;
+ top: -15px; right: 30px;
+ background-color: #2980b9;
+ color: white;
+ padding: 5px 15px;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ font-family: sans-serif;
+}
+
+.theme-campus .content p { line-height: 1.6; text-indent: 2em; }
+.theme-campus .closing { border-top: 1px dashed rgba(135, 206, 235, 0.5); padding-top: 1rem; }
+.theme-campus .footer .date { color: #888; }
+
+
+/* --- C. 极简和风 (Wabi-Sabi / Minimalist) --- */
+/* 特点:淡雅、素净、极低对比度、高端商务或长辈 */
+.theme-wabi {
+ --paper-bg: #fafafa;
+ --text-color: #555;
+ --wish-color: #777;
+ --border-color: transparent; /* 无边框 */
+ font-family: "Source Han Serif SC", serif; /* 高质量宋体 */
+
+ background-color: #fff;
+ padding: 8% 10%; /* 更宽松的内边距 */
+ border-radius: 0;
+ box-shadow: none; /* 去掉阴影,极度扁平 */
+}
+
+/* 手机端去掉过于宽松的内边距 */
+@media (max-width: 600px) {
+ .theme-wabi { padding: 40px 20px; }
+}
+
+/* 装饰:只在底部加一条极淡的灰线 */
+.theme-wabi .footer {
+ text-align: right;
+ border-top: 1px solid #f0f0f0;
+ padding-top: 1.5rem;
+}
+
+.theme-wabi .salutation { font-weight: normal; font-size: 1.2rem; color: #333; }
+.theme-wabi .content p { text-indent: 2em; line-height: 2; color: #666; font-size: 0.95rem; }
+.theme-wabi .wish-suffix { font-weight: normal; color: #777; }
+.theme-wabi .signature { font-family: "Zhi Mang Xing", cursive; font-size: 1.8rem; color: #333; } /* 使用Google Font书法字体 */
diff --git a/2000s/css/tech-query.css b/2000s/css/tech-query.css
new file mode 100644
index 0000000..4a608e5
--- /dev/null
+++ b/2000s/css/tech-query.css
@@ -0,0 +1,1710 @@
+:root {
+ --bg-primary: #07111f;
+ --bg-secondary: #0d1b31;
+ --panel: rgba(9, 20, 40, 0.78);
+ --panel-strong: rgba(10, 24, 46, 0.92);
+ --line: rgba(98, 180, 255, 0.22);
+ --line-strong: rgba(98, 180, 255, 0.45);
+ --text-primary: #ecf6ff;
+ --text-secondary: #93afd1;
+ --accent: #67d5ff;
+ --accent-strong: #3ea2ff;
+ --accent-soft: rgba(103, 213, 255, 0.14);
+ --success: #2de2a6;
+ --warning: #ffcc66;
+ --danger: #ff6b8a;
+ --shadow: 0 28px 60px rgba(0, 0, 0, 0.35);
+ --radius-xl: 28px;
+ --radius-lg: 20px;
+ --radius-md: 14px;
+ --radius-sm: 10px;
+ --content-width: 1180px;
+ --grid-gap: 18px;
+ --font-display: "Orbitron", "Segoe UI", sans-serif;
+ --font-body: "Noto Sans SC", "Segoe UI", sans-serif;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html {
+ color-scheme: dark;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ background:
+ radial-gradient(circle at top left, rgba(62, 162, 255, 0.18), transparent 28%),
+ radial-gradient(circle at top right, rgba(45, 226, 166, 0.08), transparent 22%),
+ linear-gradient(135deg, #040914 0%, #07111f 45%, #0a1730 100%);
+ position: relative;
+ overflow-x: hidden;
+}
+
+body::before,
+body::after {
+ content: "";
+ position: fixed;
+ inset: 0;
+ pointer-events: none;
+ z-index: -1;
+}
+
+body::before {
+ background-image:
+ linear-gradient(rgba(103, 213, 255, 0.06) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(103, 213, 255, 0.06) 1px, transparent 1px);
+ background-size: 42px 42px;
+ mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.65), transparent 92%);
+}
+
+body::after {
+ background:
+ radial-gradient(circle at 20% 20%, rgba(103, 213, 255, 0.08), transparent 16%),
+ radial-gradient(circle at 80% 35%, rgba(103, 213, 255, 0.05), transparent 18%);
+}
+
+a {
+ color: inherit;
+}
+
+.is-hidden {
+ display: none;
+}
+
+.mt-18 {
+ margin-top: 18px;
+}
+
+.page-shell {
+ width: min(calc(100% - 32px), var(--content-width));
+ margin: 0 auto;
+ padding: 36px 0 48px;
+}
+
+.page-header {
+ display: grid;
+ gap: 18px;
+ grid-template-columns: 1.35fr 0.85fr;
+ align-items: stretch;
+ margin-bottom: 24px;
+}
+
+.hero-card,
+.stat-card,
+.panel-card {
+ position: relative;
+ overflow: hidden;
+ background: linear-gradient(180deg, rgba(10, 24, 46, 0.9), rgba(7, 18, 35, 0.82));
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xl);
+ box-shadow: var(--shadow);
+ backdrop-filter: blur(18px);
+}
+
+.hero-card {
+ padding: 32px;
+}
+
+.hero-card::before,
+.stat-card::before,
+.panel-card::before,
+.entry-card::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, rgba(103, 213, 255, 0.12), transparent 42%, rgba(103, 213, 255, 0.06));
+ pointer-events: none;
+}
+
+.eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 14px;
+ border-radius: 999px;
+ border: 1px solid rgba(103, 213, 255, 0.28);
+ background: rgba(103, 213, 255, 0.08);
+ color: var(--accent);
+ font-size: 12px;
+ letter-spacing: 0.24em;
+ text-transform: uppercase;
+}
+
+.hero-card h1,
+.landing-hero h1 {
+ margin: 18px 0 12px;
+ font-family: var(--font-display);
+ font-size: clamp(28px, 4.4vw, 48px);
+ line-height: 1.08;
+ letter-spacing: 0.06em;
+}
+
+.hero-card p,
+.landing-hero p,
+.stat-card p,
+.panel-subtitle,
+.empty-state,
+.loading-state,
+.error-state {
+ color: var(--text-secondary);
+}
+
+.hero-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-top: 22px;
+}
+
+.meta-chip {
+ padding: 10px 14px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(147, 175, 209, 0.14);
+ font-size: 13px;
+ color: var(--text-primary);
+}
+
+.stat-card {
+ padding: 28px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 18px;
+}
+
+.stat-label {
+ color: var(--text-secondary);
+ font-size: 13px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+}
+
+.stat-value {
+ font-family: var(--font-display);
+ font-size: clamp(28px, 4vw, 44px);
+ color: var(--accent);
+ text-shadow: 0 0 18px rgba(103, 213, 255, 0.28);
+}
+
+.panel-card {
+ padding: 24px;
+}
+
+.panel-title-row {
+ display: flex;
+ justify-content: space-between;
+ gap: 16px;
+ align-items: center;
+ margin-bottom: 18px;
+}
+
+.panel-title {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+}
+
+.search-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: var(--grid-gap);
+ align-items: end;
+}
+
+.search-grid.compact {
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+}
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.form-label {
+ color: var(--text-secondary);
+ font-size: 13px;
+ letter-spacing: 0.06em;
+}
+
+.tech-input,
+.tech-button,
+.tech-link-button {
+ border-radius: var(--radius-md);
+ border: 1px solid var(--line);
+ min-height: 48px;
+ font: inherit;
+}
+
+.tech-input {
+ width: 100%;
+ padding: 0 16px;
+ background: rgba(6, 14, 28, 0.8);
+ color: var(--text-primary);
+ outline: none;
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+}
+
+.tech-input::placeholder {
+ color: rgba(147, 175, 209, 0.68);
+}
+
+.tech-input:focus {
+ border-color: var(--line-strong);
+ box-shadow: 0 0 0 4px rgba(62, 162, 255, 0.14);
+}
+
+.input-with-action {
+ position: relative;
+}
+
+.input-with-action .tech-input {
+ padding-right: 56px;
+}
+
+.icon-action {
+ position: absolute;
+ right: 8px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 40px;
+ height: 40px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 12px;
+ border: 1px solid transparent;
+ background: transparent;
+ color: var(--accent);
+ cursor: pointer;
+ transition: transform 0.18s ease, background 0.18s ease, border-color 0.18s ease;
+}
+
+.icon-action:hover {
+ transform: translateY(-50%) scale(1.04);
+ background: rgba(103, 213, 255, 0.1);
+ border-color: rgba(103, 213, 255, 0.18);
+}
+
+.tech-button,
+.tech-link-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ padding: 0 20px;
+ background: linear-gradient(135deg, rgba(103, 213, 255, 0.24), rgba(62, 162, 255, 0.44));
+ color: #f7fbff;
+ font-weight: 700;
+ letter-spacing: 0.03em;
+ text-decoration: none;
+ cursor: pointer;
+ transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
+}
+
+.tech-button:hover,
+.tech-link-button:hover {
+ transform: translateY(-1px);
+ border-color: rgba(103, 213, 255, 0.4);
+ box-shadow: 0 12px 30px rgba(62, 162, 255, 0.2);
+}
+
+.tech-button.secondary,
+.tech-link-button.secondary {
+ background: rgba(255, 255, 255, 0.04);
+ color: var(--text-primary);
+}
+
+.results-card {
+ min-height: 260px;
+}
+
+.results-shell {
+ border-radius: var(--radius-lg);
+ border: 1px solid rgba(103, 213, 255, 0.16);
+ overflow: hidden;
+ background: rgba(3, 8, 18, 0.46);
+}
+
+.table-wrapper {
+ overflow-x: auto;
+}
+
+.results-summary {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 12px;
+ padding: 16px;
+ border-bottom: 1px solid rgba(103, 213, 255, 0.12);
+}
+
+.summary-stat-tile {
+ min-height: 86px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 8px;
+ padding: 14px 16px;
+ border-radius: var(--radius-md);
+ border: 1px solid rgba(103, 213, 255, 0.14);
+ background: rgba(255, 255, 255, 0.035);
+}
+
+.summary-stat-tile span,
+.stats-panel-title,
+.muted-note {
+ color: var(--text-secondary);
+}
+
+.summary-stat-tile span {
+ font-size: 13px;
+ letter-spacing: 0.08em;
+}
+
+.summary-stat-tile strong {
+ font-family: var(--font-display);
+ font-size: 30px;
+ color: var(--accent);
+ line-height: 1;
+}
+
+.stats-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
+ gap: 16px;
+ padding: 0 16px 16px;
+}
+
+.stats-panel {
+ min-width: 0;
+ padding: 16px;
+ border-radius: var(--radius-md);
+ border: 1px solid rgba(103, 213, 255, 0.14);
+ background: rgba(255, 255, 255, 0.025);
+}
+
+.stats-panel-title {
+ margin-bottom: 14px;
+ font-size: 13px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+}
+
+.bar-chart {
+ display: grid;
+ gap: 12px;
+}
+
+.chart-row {
+ display: grid;
+ grid-template-columns: minmax(88px, 0.35fr) minmax(120px, 1fr) 56px;
+ gap: 12px;
+ align-items: center;
+}
+
+.chart-row-label,
+.award-count-title {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.chart-row-label {
+ color: var(--text-primary);
+ font-weight: 600;
+}
+
+.chart-bar-track {
+ height: 12px;
+ overflow: hidden;
+ border-radius: 999px;
+ background: rgba(103, 213, 255, 0.1);
+}
+
+.chart-bar-fill {
+ height: 100%;
+ border-radius: inherit;
+ background: linear-gradient(90deg, var(--accent), var(--success));
+ box-shadow: 0 0 18px rgba(103, 213, 255, 0.25);
+}
+
+.chart-row-value {
+ color: var(--accent);
+ font-weight: 700;
+ text-align: right;
+}
+
+.award-count-grid {
+ display: grid;
+ gap: 10px;
+}
+
+.award-count-chip {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto auto;
+ gap: 8px;
+ align-items: center;
+ padding: 12px 14px;
+ border-radius: var(--radius-sm);
+ border: 1px solid rgba(147, 175, 209, 0.14);
+ background: rgba(3, 8, 18, 0.36);
+}
+
+.award-count-chip strong {
+ color: var(--success);
+ font-family: var(--font-display);
+}
+
+.award-count-chip span:last-child {
+ color: var(--text-secondary);
+ font-size: 13px;
+}
+
+.muted-note {
+ padding: 12px 0;
+ text-align: center;
+}
+
+.result-table {
+ width: 100%;
+ min-width: 720px;
+ border-collapse: collapse;
+}
+
+.result-table th,
+.result-table td {
+ padding: 16px 18px;
+ border-bottom: 1px solid rgba(103, 213, 255, 0.08);
+ text-align: left;
+}
+
+.result-table th {
+ background: rgba(103, 213, 255, 0.08);
+ color: var(--text-secondary);
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.result-table td {
+ color: var(--text-primary);
+ background: rgba(255, 255, 255, 0.01);
+}
+
+.result-table tbody tr:hover td {
+ background: rgba(103, 213, 255, 0.04);
+}
+
+.table-highlight {
+ color: var(--accent);
+ font-weight: 700;
+}
+
+.text-truncate {
+ display: inline-block;
+ max-width: 220px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ vertical-align: bottom;
+}
+
+.status-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 12px;
+ border-radius: 999px;
+ font-size: 13px;
+ font-weight: 700;
+ line-height: 1;
+ border: 1px solid transparent;
+}
+
+.status-pill.done {
+ color: #081a14;
+ background: rgba(45, 226, 166, 0.9);
+}
+
+.status-pill.pending {
+ color: #251400;
+ background: rgba(255, 204, 102, 0.92);
+}
+
+.loading-state,
+.empty-state,
+.error-state {
+ min-height: 220px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: 36px 24px;
+ font-size: 15px;
+}
+
+.error-state {
+ color: #ffd7df;
+}
+
+.page-footer {
+ width: min(calc(100% - 32px), var(--content-width));
+ margin: 0 auto 24px;
+ color: rgba(147, 175, 209, 0.74);
+ font-size: 13px;
+ text-align: center;
+}
+
+.scanner-overlay {
+ display: none;
+ position: fixed;
+ inset: 0;
+ z-index: 9999;
+ background: rgba(2, 7, 16, 0.92);
+ backdrop-filter: blur(14px);
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ gap: 18px;
+ padding: 24px;
+}
+
+.scanner-frame {
+ position: relative;
+ width: min(92vw, 520px);
+ height: min(54vw, 240px);
+ min-height: 180px;
+ border-radius: 28px;
+ overflow: hidden;
+ border: 1px solid rgba(103, 213, 255, 0.28);
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
+ background: #000;
+}
+
+.scanner-frame::before,
+.scanner-frame::after {
+ content: "";
+ position: absolute;
+ inset: 16px;
+ border: 1px solid rgba(103, 213, 255, 0.25);
+ border-radius: 18px;
+ pointer-events: none;
+}
+
+.scanner-line {
+ position: absolute;
+ left: 8%;
+ width: 84%;
+ height: 2px;
+ background: linear-gradient(90deg, transparent, var(--accent), transparent);
+ box-shadow: 0 0 18px rgba(103, 213, 255, 0.7);
+ animation: scanner-sweep 2.2s linear infinite;
+ z-index: 2;
+}
+
+@keyframes scanner-sweep {
+ 0% {
+ top: 16%;
+ }
+
+ 100% {
+ top: 84%;
+ }
+}
+
+#reader {
+ width: 100% !important;
+ height: 100% !important;
+}
+
+.scanner-text {
+ color: var(--text-secondary);
+ font-size: 14px;
+ letter-spacing: 0.04em;
+}
+
+.landing-shell {
+ width: min(calc(100% - 32px), 1240px);
+ margin: 0 auto;
+ padding: 36px 0 48px;
+}
+
+.landing-hero {
+ position: relative;
+ padding: 38px;
+ border-radius: 34px;
+ overflow: hidden;
+ background:
+ linear-gradient(135deg, rgba(10, 24, 46, 0.95), rgba(7, 18, 35, 0.86)),
+ linear-gradient(90deg, rgba(103, 213, 255, 0.18), transparent);
+ border: 1px solid var(--line);
+ box-shadow: var(--shadow);
+}
+
+.landing-grid {
+ margin-top: 24px;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 18px;
+}
+
+.entry-card {
+ position: relative;
+ overflow: hidden;
+ display: grid;
+ gap: 18px;
+ padding: 24px;
+ border-radius: 24px;
+ text-decoration: none;
+ background: linear-gradient(180deg, rgba(9, 20, 40, 0.88), rgba(7, 18, 35, 0.86));
+ border: 1px solid var(--line);
+ box-shadow: var(--shadow);
+ transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.entry-card:hover {
+ transform: translateY(-4px);
+ border-color: rgba(103, 213, 255, 0.4);
+ box-shadow: 0 24px 48px rgba(0, 0, 0, 0.38);
+}
+
+.entry-kicker {
+ color: var(--accent);
+ font-size: 12px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+}
+
+.entry-card h2 {
+ margin: 0;
+ font-size: 24px;
+}
+
+.entry-card p {
+ margin: 0;
+ color: var(--text-secondary);
+ line-height: 1.7;
+}
+
+.entry-meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ color: var(--text-primary);
+ font-size: 14px;
+}
+
+.entry-arrow {
+ width: 42px;
+ height: 42px;
+ border-radius: 999px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(103, 213, 255, 0.12);
+ color: var(--accent);
+}
+
+.detail-shell {
+ width: min(calc(100% - 32px), 1120px);
+ margin: 0 auto;
+ padding: 28px 0 48px;
+}
+
+.top-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 14px;
+ margin-bottom: 18px;
+}
+
+.top-actions-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.detail-grid {
+ display: grid;
+ grid-template-columns: 1.1fr 0.9fr;
+ gap: 18px;
+}
+
+.preview-panel,
+.summary-panel,
+.detail-panel {
+ position: relative;
+ overflow: hidden;
+ border-radius: var(--radius-xl);
+ border: 1px solid var(--line);
+ background: linear-gradient(180deg, rgba(10, 24, 46, 0.92), rgba(7, 18, 35, 0.84));
+ box-shadow: var(--shadow);
+}
+
+.preview-panel,
+.summary-panel,
+.detail-panel {
+ padding: 24px;
+}
+
+.preview-panel::before,
+.summary-panel::before,
+.detail-panel::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, rgba(103, 213, 255, 0.08), transparent 45%, rgba(103, 213, 255, 0.05));
+ pointer-events: none;
+}
+
+.section-kicker {
+ color: var(--accent);
+ font-size: 12px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+}
+
+.section-heading {
+ margin: 12px 0 10px;
+ font-size: 26px;
+ line-height: 1.2;
+}
+
+.section-copy {
+ margin: 0;
+ color: var(--text-secondary);
+ line-height: 1.75;
+}
+
+.summary-stack {
+ display: grid;
+ gap: 14px;
+ margin-top: 20px;
+}
+
+.summary-tile {
+ padding: 16px 18px;
+ border-radius: 18px;
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(103, 213, 255, 0.12);
+}
+
+.summary-label {
+ display: block;
+ margin-bottom: 8px;
+ color: var(--text-secondary);
+ font-size: 12px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.summary-value {
+ font-size: 18px;
+ font-weight: 700;
+ color: var(--text-primary);
+ word-break: break-word;
+}
+
+.summary-value.emphasis {
+ color: var(--accent);
+}
+
+.certificate-stage {
+ position: relative;
+ min-height: 520px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 24px;
+ border: 1px solid rgba(103, 213, 255, 0.14);
+ background: #ffffff;
+ overflow: hidden;
+ cursor: pointer;
+}
+
+.certificate-stage img {
+ width: 100%;
+ height: auto;
+ display: none;
+ position: relative;
+ z-index: 1;
+}
+
+.certificate-hint {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ text-align: center;
+ color: #64748b;
+ padding: 24px;
+}
+
+.certificate-hint strong {
+ color: #0f172a;
+ font-size: 18px;
+}
+
+.hint-orb {
+ width: 72px;
+ height: 72px;
+ border-radius: 999px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(103, 213, 255, 0.1);
+ border: 1px solid rgba(103, 213, 255, 0.18);
+ color: var(--accent);
+ font-size: 30px;
+ box-shadow: 0 0 24px rgba(103, 213, 255, 0.12);
+}
+
+.info-table-shell {
+ overflow: hidden;
+ border-radius: 20px;
+ border: 1px solid rgba(103, 213, 255, 0.14);
+ background: rgba(3, 8, 18, 0.4);
+}
+
+.info-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.info-table th,
+.info-table td {
+ padding: 16px 18px;
+ border-bottom: 1px solid rgba(103, 213, 255, 0.08);
+ text-align: left;
+ vertical-align: top;
+}
+
+.info-table th {
+ width: 28%;
+ color: var(--text-secondary);
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ background: rgba(103, 213, 255, 0.06);
+}
+
+.info-table td {
+ color: var(--text-primary);
+ line-height: 1.7;
+}
+
+.ticket-header {
+ display: flex;
+ justify-content: space-between;
+ gap: 16px;
+ align-items: flex-start;
+ margin-bottom: 20px;
+}
+
+.ticket-title {
+ margin: 10px 0 6px;
+ font-size: clamp(26px, 4vw, 36px);
+ line-height: 1.16;
+}
+
+.ticket-subtitle {
+ margin: 0;
+ color: var(--text-secondary);
+}
+
+.ticket-detail-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 14px;
+}
+
+.detail-item {
+ padding: 16px 18px;
+ border-radius: 18px;
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(103, 213, 255, 0.12);
+}
+
+.detail-item.wide {
+ grid-column: 1 / -1;
+}
+
+.detail-label {
+ display: block;
+ margin-bottom: 8px;
+ color: var(--text-secondary);
+ font-size: 12px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.detail-value {
+ color: var(--text-primary);
+ font-size: 16px;
+ line-height: 1.65;
+ word-break: break-word;
+}
+
+.detail-value.money {
+ color: var(--accent);
+ font-weight: 700;
+}
+
+.feedback-card {
+ min-height: 220px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ color: var(--text-secondary);
+ font-size: 15px;
+}
+
+@media (max-width: 1040px) {
+ .page-header,
+ .landing-grid,
+ .detail-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .search-grid,
+ .search-grid.compact {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 720px) {
+ .page-shell,
+ .landing-shell,
+ .detail-shell {
+ width: min(calc(100% - 20px), var(--content-width));
+ padding-top: 20px;
+ }
+
+ .hero-card,
+ .stat-card,
+ .panel-card,
+ .landing-hero,
+ .entry-card,
+ .preview-panel,
+ .summary-panel,
+ .detail-panel {
+ border-radius: 22px;
+ }
+
+ .hero-card,
+ .landing-hero,
+ .panel-card,
+ .entry-card,
+ .preview-panel,
+ .summary-panel,
+ .detail-panel {
+ padding: 20px;
+ }
+
+ .search-grid,
+ .search-grid.compact {
+ grid-template-columns: 1fr;
+ }
+
+ .results-summary,
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .chart-row {
+ grid-template-columns: minmax(74px, 0.4fr) minmax(90px, 1fr) 52px;
+ gap: 8px;
+ }
+
+ .result-table {
+ min-width: 620px;
+ }
+
+ .ticket-header,
+ .top-actions {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .ticket-detail-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .certificate-stage {
+ min-height: 360px;
+ }
+}
+
+/* Light visual refresh for query modules */
+html {
+ color-scheme: light;
+}
+
+body {
+ background:
+ linear-gradient(135deg, rgba(236, 248, 255, 0.92), rgba(255, 255, 255, 0.96) 48%, rgba(245, 250, 255, 0.94)),
+ linear-gradient(90deg, rgba(37, 99, 235, 0.045) 1px, transparent 1px),
+ linear-gradient(0deg, rgba(20, 184, 166, 0.035) 1px, transparent 1px);
+ background-size: auto, 52px 52px, 52px 52px;
+ color: #172033;
+}
+
+body::before {
+ background:
+ linear-gradient(120deg, rgba(37, 99, 235, 0.08), transparent 34%),
+ linear-gradient(300deg, rgba(20, 184, 166, 0.06), transparent 32%);
+ mask-image: none;
+}
+
+body::after {
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.64), transparent 62%);
+}
+
+.hero-card,
+.stat-card,
+.panel-card,
+.landing-hero,
+.entry-card,
+.preview-panel,
+.summary-panel,
+.detail-panel {
+ background: rgba(255, 255, 255, 0.84);
+ border-color: rgba(120, 144, 176, 0.24);
+ box-shadow: 0 18px 44px rgba(47, 74, 112, 0.12);
+ backdrop-filter: blur(16px);
+}
+
+.hero-card::before,
+.stat-card::before,
+.panel-card::before,
+.entry-card::before,
+.preview-panel::before,
+.summary-panel::before,
+.detail-panel::before {
+ background: linear-gradient(135deg, rgba(37, 99, 235, 0.07), transparent 46%, rgba(20, 184, 166, 0.06));
+}
+
+.eyebrow,
+.entry-kicker,
+.section-kicker,
+.stat-value,
+.summary-stat-tile strong,
+.chart-row-value,
+.table-highlight,
+.summary-value.emphasis,
+.detail-value.money,
+.hint-orb {
+ color: #2563eb;
+ text-shadow: none;
+}
+
+.hero-card h1,
+.landing-hero h1,
+.panel-title,
+.entry-card h2,
+.section-heading,
+.ticket-title,
+.chart-row-label,
+.summary-value,
+.detail-value,
+.info-table td,
+.result-table td {
+ color: #172033;
+}
+
+.hero-card p,
+.landing-hero p,
+.entry-card p,
+.stat-card p,
+.panel-subtitle,
+.empty-state,
+.loading-state,
+.scanner-text,
+.ticket-subtitle,
+.section-copy,
+.summary-label,
+.detail-label,
+.form-label,
+.stats-panel-title,
+.muted-note,
+.award-count-chip span:last-child,
+.info-table th,
+.page-footer {
+ color: #66758a;
+}
+
+.eyebrow {
+ border-color: rgba(37, 99, 235, 0.18);
+ background: rgba(37, 99, 235, 0.07);
+}
+
+.meta-chip,
+.summary-stat-tile,
+.stats-panel,
+.award-count-chip,
+.summary-tile,
+.detail-item {
+ background: rgba(248, 251, 255, 0.86);
+ border-color: rgba(120, 144, 176, 0.2);
+ color: #172033;
+}
+
+.tech-input {
+ background: #ffffff;
+ border-color: rgba(120, 144, 176, 0.32);
+ color: #172033;
+ box-shadow: 0 1px 2px rgba(47, 74, 112, 0.04);
+}
+
+.tech-input::placeholder {
+ color: #94a3b8;
+}
+
+.tech-input:focus {
+ border-color: rgba(37, 99, 235, 0.55);
+ box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.1);
+}
+
+.icon-action {
+ color: #2563eb;
+}
+
+.icon-action:hover,
+.entry-arrow {
+ background: rgba(37, 99, 235, 0.08);
+ border-color: rgba(37, 99, 235, 0.16);
+}
+
+.tech-button,
+.tech-link-button {
+ background: linear-gradient(135deg, #2563eb, #0891b2);
+ border-color: rgba(37, 99, 235, 0.16);
+ color: #ffffff;
+ box-shadow: 0 12px 24px rgba(37, 99, 235, 0.16);
+}
+
+.tech-button:hover,
+.tech-link-button:hover {
+ border-color: rgba(37, 99, 235, 0.36);
+ box-shadow: 0 14px 28px rgba(37, 99, 235, 0.2);
+}
+
+.tech-button.secondary,
+.tech-link-button.secondary {
+ background: #ffffff;
+ color: #172033;
+}
+
+.results-shell,
+.info-table-shell {
+ background: rgba(255, 255, 255, 0.72);
+ border-color: rgba(120, 144, 176, 0.2);
+}
+
+.results-summary,
+.result-table th,
+.result-table td,
+.info-table th,
+.info-table td {
+ border-color: rgba(120, 144, 176, 0.16);
+}
+
+.result-table th,
+.info-table th {
+ background: rgba(37, 99, 235, 0.06);
+}
+
+.result-table td {
+ background: rgba(255, 255, 255, 0.56);
+}
+
+.result-table tbody tr:hover td {
+ background: rgba(37, 99, 235, 0.055);
+}
+
+.chart-bar-track {
+ background: rgba(37, 99, 235, 0.12);
+}
+
+.chart-bar-fill {
+ background: linear-gradient(90deg, #2563eb, #14b8a6);
+ box-shadow: none;
+}
+
+.award-count-chip strong {
+ color: #047857;
+}
+
+.status-pill.done {
+ color: #064e3b;
+ background: #bbf7d0;
+}
+
+.status-pill.pending {
+ color: #854d0e;
+ background: #fef3c7;
+}
+
+.error-state {
+ color: #be123c;
+}
+
+.certificate-stage {
+ border-color: rgba(120, 144, 176, 0.18);
+ box-shadow: inset 0 0 0 1px rgba(120, 144, 176, 0.08);
+}
+
+.scanner-overlay {
+ background: rgba(241, 247, 255, 0.92);
+}
+
+.scanner-frame {
+ border-color: rgba(37, 99, 235, 0.28);
+ box-shadow: 0 20px 50px rgba(47, 74, 112, 0.22);
+}
+
+.scanner-frame::before,
+.scanner-frame::after {
+ border-color: rgba(37, 99, 235, 0.26);
+}
+
+.scanner-line {
+ background: linear-gradient(90deg, transparent, #2563eb, transparent);
+ box-shadow: 0 0 18px rgba(37, 99, 235, 0.48);
+}
+
+/* Youthful unified layout refresh */
+:root {
+ --joy-blue: #2563eb;
+ --joy-cyan: #06b6d4;
+ --joy-mint: #10b981;
+ --joy-coral: #fb7185;
+ --joy-yellow: #facc15;
+ --joy-ink: #172033;
+ --joy-muted: #627187;
+ --joy-line: rgba(96, 116, 148, 0.18);
+ --joy-card: rgba(255, 255, 255, 0.9);
+}
+
+body {
+ background:
+ linear-gradient(128deg, rgba(255, 255, 255, 0.96) 0 34%, rgba(232, 249, 255, 0.9) 34% 58%, rgba(255, 246, 249, 0.92) 58% 100%),
+ linear-gradient(90deg, rgba(37, 99, 235, 0.04) 1px, transparent 1px),
+ linear-gradient(0deg, rgba(16, 185, 129, 0.035) 1px, transparent 1px);
+ background-size: auto, 44px 44px, 44px 44px;
+}
+
+body::before {
+ background:
+ linear-gradient(110deg, transparent 0 8%, rgba(37, 99, 235, 0.1) 8% 9%, transparent 9% 100%),
+ linear-gradient(110deg, transparent 0 72%, rgba(251, 113, 133, 0.12) 72% 73%, transparent 73% 100%),
+ linear-gradient(110deg, transparent 0 84%, rgba(16, 185, 129, 0.1) 84% 85%, transparent 85% 100%);
+}
+
+body::after {
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.5), transparent 54%),
+ linear-gradient(90deg, rgba(250, 204, 21, 0.08), transparent 28%, rgba(6, 182, 212, 0.06));
+}
+
+.page-shell,
+.landing-shell {
+ padding-top: 28px;
+}
+
+.page-header {
+ grid-template-columns: minmax(0, 1fr) minmax(220px, 0.28fr);
+ gap: 16px;
+ margin-bottom: 16px;
+}
+
+.hero-card,
+.stat-card,
+.panel-card,
+.landing-hero,
+.entry-card,
+.preview-panel,
+.summary-panel,
+.detail-panel {
+ border-radius: 18px;
+ border-color: var(--joy-line);
+ background: var(--joy-card);
+ box-shadow: 0 16px 34px rgba(37, 63, 101, 0.1);
+}
+
+.hero-card::before,
+.stat-card::before,
+.panel-card::before,
+.entry-card::before,
+.preview-panel::before,
+.summary-panel::before,
+.detail-panel::before {
+ background:
+ linear-gradient(135deg, rgba(37, 99, 235, 0.08), transparent 32%),
+ linear-gradient(315deg, rgba(251, 113, 133, 0.08), transparent 35%);
+}
+
+.hero-card {
+ padding: 28px;
+ display: grid;
+ align-content: center;
+ min-height: 236px;
+}
+
+.hero-card h1,
+.landing-hero h1 {
+ margin-top: 16px;
+ letter-spacing: 0;
+ color: var(--joy-ink);
+}
+
+.hero-card p,
+.landing-hero p,
+.entry-card p,
+.section-copy {
+ line-height: 1.72;
+}
+
+.eyebrow,
+.entry-kicker,
+.section-kicker {
+ border: 0;
+ border-radius: 999px;
+ background: linear-gradient(135deg, rgba(37, 99, 235, 0.11), rgba(6, 182, 212, 0.11));
+ color: var(--joy-blue);
+ letter-spacing: 0.12em;
+ font-weight: 800;
+}
+
+.hero-meta {
+ gap: 10px;
+ margin-top: 18px;
+}
+
+.meta-chip {
+ border: 0;
+ background: #f8fbff;
+ box-shadow: inset 0 0 0 1px rgba(96, 116, 148, 0.12);
+}
+
+.stat-card {
+ padding: 22px;
+ justify-content: center;
+ border-top: 5px solid var(--joy-coral);
+}
+
+.stat-card::after,
+.panel-card::after,
+.entry-card::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 4px;
+ background: linear-gradient(90deg, var(--joy-blue), var(--joy-cyan), var(--joy-mint), var(--joy-coral));
+ pointer-events: none;
+}
+
+.stat-value {
+ color: var(--joy-coral);
+ font-size: clamp(24px, 3vw, 36px);
+}
+
+.panel-card {
+ padding: 22px;
+ margin-top: 16px;
+}
+
+.panel-title-row {
+ margin-bottom: 16px;
+ padding-bottom: 14px;
+ border-bottom: 1px solid rgba(96, 116, 148, 0.12);
+}
+
+.panel-title {
+ letter-spacing: 0;
+}
+
+.search-grid {
+ grid-template-columns: minmax(260px, 1.5fr) repeat(2, minmax(150px, 0.72fr)) minmax(128px, 0.55fr);
+}
+
+.search-grid.compact {
+ grid-template-columns: minmax(150px, 0.9fr) minmax(190px, 1.1fr) repeat(2, minmax(140px, 0.72fr)) minmax(128px, 0.55fr);
+}
+
+.tech-input,
+.tech-button,
+.tech-link-button {
+ min-height: 46px;
+ border-radius: 12px;
+}
+
+.tech-input {
+ background: #fbfdff;
+}
+
+.tech-button,
+.tech-link-button {
+ background: linear-gradient(135deg, var(--joy-blue), var(--joy-cyan));
+ box-shadow: 0 12px 22px rgba(37, 99, 235, 0.18);
+}
+
+.tech-button.secondary,
+.tech-link-button.secondary {
+ background: #ffffff;
+ box-shadow: inset 0 0 0 1px rgba(96, 116, 148, 0.16);
+}
+
+.results-shell,
+.info-table-shell {
+ border-radius: 16px;
+}
+
+.empty-state,
+.loading-state,
+.feedback-card {
+ min-height: 180px;
+ background:
+ linear-gradient(135deg, rgba(37, 99, 235, 0.045), transparent 42%),
+ linear-gradient(315deg, rgba(251, 113, 133, 0.05), transparent 36%);
+}
+
+.result-table th {
+ background: #f3f8ff;
+ color: #4f5f74;
+}
+
+.result-table tbody tr:nth-child(even) td {
+ background: rgba(248, 251, 255, 0.72);
+}
+
+.status-pill.done {
+ color: #066047;
+ background: #d8fff0;
+}
+
+.status-pill.pending {
+ color: #8a4b05;
+ background: #fff2bf;
+}
+
+.landing-hero {
+ min-height: 260px;
+ padding: clamp(28px, 5vw, 54px);
+ display: grid;
+ align-content: end;
+ border-top: 5px solid var(--joy-blue);
+}
+
+.landing-grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 16px;
+}
+
+.entry-card {
+ min-height: 246px;
+ align-content: space-between;
+ transform: translateY(0);
+}
+
+.entry-card:nth-child(2) {
+ border-top: 5px solid var(--joy-mint);
+}
+
+.entry-card:nth-child(3) {
+ border-top: 5px solid var(--joy-coral);
+}
+
+.entry-card:hover {
+ transform: translateY(-6px);
+ box-shadow: 0 22px 42px rgba(37, 63, 101, 0.14);
+}
+
+.entry-meta {
+ padding-top: 12px;
+ border-top: 1px solid rgba(96, 116, 148, 0.12);
+}
+
+.entry-arrow {
+ color: #ffffff;
+ background: linear-gradient(135deg, var(--joy-blue), var(--joy-coral));
+}
+
+.detail-grid {
+ grid-template-columns: minmax(0, 1.05fr) minmax(280px, 0.72fr);
+}
+
+.summary-stack,
+.ticket-detail-grid {
+ gap: 12px;
+}
+
+.summary-tile,
+.detail-item,
+.summary-stat-tile,
+.stats-panel,
+.award-count-chip {
+ border-radius: 14px;
+ background: #f8fbff;
+}
+
+.certificate-stage {
+ border-radius: 16px;
+}
+
+@media (max-width: 1040px) {
+ .page-header,
+ .landing-grid,
+ .detail-grid,
+ .search-grid,
+ .search-grid.compact {
+ grid-template-columns: 1fr;
+ }
+
+ .stat-card {
+ min-height: 118px;
+ }
+}
+
+@media (max-width: 720px) {
+ .page-shell,
+ .landing-shell,
+ .detail-shell {
+ padding-top: 14px;
+ }
+
+ .hero-card,
+ .landing-hero,
+ .panel-card,
+ .entry-card,
+ .preview-panel,
+ .summary-panel,
+ .detail-panel {
+ padding: 18px;
+ }
+
+ .panel-title-row {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+}
+
+@media print {
+ body {
+ background: #ffffff;
+ color: #111827;
+ }
+
+ body::before,
+ body::after,
+ .page-footer,
+ .top-actions {
+ display: none !important;
+ }
+
+ .detail-shell,
+ .page-shell {
+ width: 100%;
+ max-width: none;
+ padding: 0;
+ }
+
+ .detail-grid {
+ display: block;
+ }
+
+ .preview-panel,
+ .summary-panel,
+ .detail-panel,
+ .panel-card,
+ .hero-card,
+ .stat-card {
+ background: #ffffff !important;
+ color: #111827 !important;
+ box-shadow: none !important;
+ border: 1px solid #d1d5db !important;
+ }
+
+ .preview-panel::before,
+ .summary-panel::before,
+ .detail-panel::before,
+ .panel-card::before,
+ .hero-card::before,
+ .stat-card::before {
+ display: none !important;
+ }
+
+ .section-kicker,
+ .summary-label,
+ .detail-label,
+ .info-table th,
+ .ticket-subtitle,
+ .section-copy,
+ .summary-value,
+ .detail-value,
+ .top-actions-group,
+ .status-pill,
+ .page-shell * {
+ color: inherit !important;
+ }
+
+ .certificate-stage {
+ min-height: 0;
+ border: none !important;
+ background: #ffffff !important;
+ page-break-after: always;
+ break-after: page;
+ }
+
+ .preview-panel {
+ padding: 0 !important;
+ border: none !important;
+ background: #ffffff !important;
+ }
+
+ .preview-panel > .section-kicker,
+ .preview-panel > .section-heading,
+ .preview-panel > .section-copy,
+ .summary-panel {
+ display: none !important;
+ }
+
+ .certificate-hint {
+ display: none !important;
+ }
+
+ .certificate-stage img {
+ display: block !important;
+ }
+
+ .info-table-shell {
+ border: 1px solid #d1d5db !important;
+ background: #ffffff !important;
+ }
+
+ .detail-panel {
+ break-before: page;
+ }
+
+ @page {
+ size: A4 portrait;
+ margin: 10mm;
+ }
+}
diff --git a/2000s/css/ticket-print.css b/2000s/css/ticket-print.css
new file mode 100644
index 0000000..99ebfeb
--- /dev/null
+++ b/2000s/css/ticket-print.css
@@ -0,0 +1,95 @@
+body {
+ font-family: Arial, "Microsoft YaHei";
+ margin: 0;
+ color: #172033;
+ background:
+ linear-gradient(128deg, rgba(255, 255, 255, 0.96) 0 34%, rgba(232, 249, 255, 0.9) 34% 58%, rgba(255, 246, 249, 0.92) 58% 100%);
+}
+
+/* ===== A4 容器 ===== */
+.page {
+ width: 210mm;
+ min-height: 297mm;
+ margin: 18px auto;
+ padding: 20mm;
+ position: relative;
+ background: white;
+ box-shadow: 0 18px 44px rgba(47, 74, 112, 0.12);
+ border-top: 5px solid #2563eb;
+}
+
+/* ===== 水印 ===== */
+.watermark {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) rotate(-30deg);
+ font-size: 50px;
+ color: rgba(0, 0, 0, 0.08);
+ white-space: nowrap;
+ pointer-events: none;
+ z-index: 0;
+ text-align: center;
+}
+
+/* ===== 标题 ===== */
+.title {
+ text-align: center;
+ font-size: 22px;
+ font-weight: bold;
+ margin-bottom: 20px;
+ color: #172033;
+ letter-spacing: 0.04em;
+}
+
+/* ===== 信息表格 ===== */
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 10px;
+ position: relative;
+ z-index: 1;
+}
+
+.table td {
+ border: 1px solid #dbe4ef;
+ padding: 11px 12px;
+ font-size: 14px;
+}
+
+.label {
+ width: 25%;
+ color: #66758a;
+ background: linear-gradient(135deg, #f3f8ff, #f8fbff);
+ font-weight: 700;
+}
+
+/* ===== 页脚 ===== */
+.footer {
+ position: absolute;
+ bottom: 20mm;
+ left: 20mm;
+ right: 20mm;
+ font-size: 12px;
+ color: #666;
+ display: flex;
+ justify-content: space-between;
+}
+
+/* ===== 打印优化 ===== */
+@media print {
+ .no-print {
+ display: none;
+ }
+
+ body {
+ margin: 0;
+ background: #ffffff;
+ }
+
+ .page {
+ margin: auto;
+ box-shadow: none;
+ border-top: 0;
+ }
+}
diff --git a/2000s/js/certs-certificate.js b/2000s/js/certs-certificate.js
new file mode 100644
index 0000000..4c6fcd6
--- /dev/null
+++ b/2000s/js/certs-certificate.js
@@ -0,0 +1,119 @@
+const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
+ const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
+ const sbClient = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
+
+ async function loadData() {
+ const id = new URLSearchParams(window.location.search).get('id');
+ if (!id) return;
+
+ try {
+ const { data, error } = await sbClient
+ .from('certificates')
+ .select('*')
+ .eq('id', id)
+ .single();
+
+ if (error || !data) throw error || new Error('未找到证书信息');
+
+ const verifyTime = new Date().toLocaleString('zh-CN');
+ document.getElementById('info-card').style.display = 'block';
+ document.getElementById('d-cert-number').innerText = data.cert_number || '-';
+ document.getElementById('d-holder-name').innerText = data.holder_name || '-';
+ document.getElementById('d-honor-title').innerText = data.honor_title || '-';
+ document.getElementById('d-issue-date').innerText = data.issue_date || '-';
+ document.getElementById('d-issuer').innerText = data.issuer || '官方授权机构';
+ document.getElementById('d-desc').innerText = data.description || '官方荣誉认证';
+ document.getElementById('d-remarks').innerText = data.remarks || '无';
+ document.getElementById('d-verify-time').innerText = verifyTime;
+
+ document.getElementById('summary-cert-number').innerText = data.cert_number || '-';
+ document.getElementById('summary-holder-name').innerText = data.holder_name || '-';
+ document.getElementById('summary-verify-time').innerText = verifyTime;
+
+ if (data.status === 'valid') {
+ document.getElementById('display-status').innerHTML = '有效';
+ } else if (data.status === 'invalid') {
+ document.getElementById('display-status').innerHTML = '无效';
+ } else {
+ document.getElementById('display-status').innerHTML = '待核验';
+ }
+
+ window.currentCertNumber = data.cert_number;
+ } catch (e) {
+ console.error('加载失败:', e);
+ alert('证书信息检索失败。');
+ }
+ }
+
+ function loadCertificateImage() {
+ const certNumber = window.currentCertNumber;
+ if (!certNumber) return;
+
+ const img = document.getElementById('cert-img');
+ const hint = document.getElementById('click-hint');
+
+ if (img.src && img.complete && img.naturalWidth > 0) {
+ img.style.display = 'block';
+ if (hint) hint.style.display = 'none';
+ return;
+ }
+
+ if (hint) {
+ hint.innerHTML = '载图片加载中正在读取证书原件,请稍候。';
+ }
+
+ img.src = `https://s3.biss.click/CERTS/${encodeURIComponent(certNumber)}.svg`;
+ img.onload = () => {
+ img.style.display = 'block';
+ if (hint) hint.style.display = 'none';
+ };
+ img.onerror = () => {
+ if (hint) {
+ hint.innerHTML = '!图片加载失败未找到对应证书图片文件';
+ }
+ };
+ }
+
+ async function ensureCertificateImageLoaded() {
+ const certNumber = window.currentCertNumber;
+ const img = document.getElementById('cert-img');
+ const hint = document.getElementById('click-hint');
+
+ if (!certNumber) {
+ throw new Error('未找到证书编号');
+ }
+
+ if (img.src && img.complete && img.naturalWidth > 0) {
+ img.style.display = 'block';
+ if (hint) hint.style.display = 'none';
+ return;
+ }
+
+ if (hint) {
+ hint.innerHTML = '载图片加载中正在为打印准备证书原件,请稍候。';
+ }
+
+ await new Promise((resolve, reject) => {
+ img.onload = () => {
+ img.style.display = 'block';
+ if (hint) hint.style.display = 'none';
+ resolve();
+ };
+ img.onerror = () => {
+ reject(new Error('证书图片加载失败'));
+ };
+ img.src = `https://s3.biss.click/CERTS/${encodeURIComponent(certNumber)}.svg`;
+ });
+ }
+
+ async function printCertificate() {
+ try {
+ await ensureCertificateImageLoaded();
+ window.print();
+ } catch (error) {
+ console.error('打印前加载证书失败:', error);
+ alert('证书原件加载失败,无法打印。');
+ }
+ }
+
+ loadData();
diff --git a/2000s/js/certs-index.js b/2000s/js/certs-index.js
new file mode 100644
index 0000000..ce51650
--- /dev/null
+++ b/2000s/js/certs-index.js
@@ -0,0 +1,246 @@
+const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
+ const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
+ const sbClient = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
+
+ const html5QrCode = new Html5Qrcode("reader");
+ const scannerOverlay = document.getElementById('scanner-overlay');
+
+ document.getElementById('startScanBtn').addEventListener('click', async () => {
+ scannerOverlay.style.display = 'flex';
+
+ const config = {
+ fps: 15,
+ qrbox: { width: 320, height: 140 },
+ aspectRatio: 1.0
+ };
+
+ if (typeof Html5QrcodeSupportedFormats !== 'undefined') {
+ config.formatsToSupport = [
+ Html5QrcodeSupportedFormats.CODE_128,
+ Html5QrcodeSupportedFormats.CODE_39,
+ Html5QrcodeSupportedFormats.CODE_93,
+ Html5QrcodeSupportedFormats.EAN_13,
+ Html5QrcodeSupportedFormats.EAN_8,
+ Html5QrcodeSupportedFormats.UPC_A,
+ Html5QrcodeSupportedFormats.UPC_E,
+ Html5QrcodeSupportedFormats.ITF,
+ Html5QrcodeSupportedFormats.CODABAR
+ ];
+ }
+
+ try {
+ await html5QrCode.start(
+ { facingMode: "environment" },
+ config,
+ (decodedText) => {
+ document.getElementById('searchTerm').value = decodedText;
+ closeScanner();
+ handleSearch();
+ },
+ () => {}
+ );
+ } catch (err) {
+ alert("无法启动摄像头,请检查 HTTPS 权限或设备连接。");
+ scannerOverlay.style.display = 'none';
+ }
+ });
+
+ async function closeScanner() {
+ if (html5QrCode.isScanning) {
+ await html5QrCode.stop();
+ }
+ scannerOverlay.style.display = 'none';
+ }
+
+ document.getElementById('stopScanBtn').addEventListener('click', closeScanner);
+
+ async function handleSearch() {
+ const term = document.getElementById('searchTerm').value.trim();
+ const start = document.getElementById('dateStart').value;
+ const end = document.getElementById('dateEnd').value;
+ const display = document.getElementById('resultsArea');
+
+ if (!term) {
+ alert("请输入证书编号、持有人姓名或奖项名。");
+ return;
+ }
+
+ display.innerHTML = '正在连接证书数据库并执行检索...
';
+
+ try {
+ let query = sbClient.from('certificates').select('*');
+ query = query.or(`cert_number.ilike.%${term}%,holder_name.ilike.%${term}%,honor_title.ilike.%${term}%`);
+
+ if (start) query = query.gte('issue_date', start);
+ if (end) query = query.lte('issue_date', end);
+
+ const { data, error } = await query.order('issue_date', { ascending: false });
+ if (error) throw error;
+
+ const awardRecipientCounts = await loadAwardRecipientCounts(data);
+ renderResults(data, awardRecipientCounts);
+ } catch (err) {
+ display.innerHTML = `检索失败:${err.message}
`;
+ }
+ }
+
+ async function loadAwardRecipientCounts(data) {
+ const awardTitles = [...new Set((data || []).map(item => item.honor_title).filter(Boolean))];
+ if (awardTitles.length === 0) return {};
+
+ const countPairs = await Promise.all(awardTitles.map(async title => {
+ const { count, error } = await sbClient
+ .from('certificates')
+ .select('id', { count: 'exact', head: true })
+ .eq('honor_title', title);
+
+ if (error) throw error;
+ return [title, count || 0];
+ }));
+
+ return Object.fromEntries(countPairs);
+ }
+
+ function escapeHtml(value) {
+ return String(value ?? '').replace(/[&<>"']/g, char => ({
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ }[char]));
+ }
+
+ function renderAwardStats(data, awardRecipientCounts) {
+ const holderCounts = (data || []).reduce((counts, item) => {
+ const holderName = item.holder_name || '未填写';
+ counts[holderName] = (counts[holderName] || 0) + 1;
+ return counts;
+ }, {});
+
+ const holderStats = Object.entries(holderCounts)
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0], 'zh-CN'));
+ const maxHolderCount = Math.max(...holderStats.map(([, count]) => count), 1);
+
+ const awardStats = [...new Set((data || []).map(item => item.honor_title).filter(Boolean))]
+ .map(title => ({
+ title,
+ count: awardRecipientCounts[title] || 0
+ }))
+ .sort((a, b) => b.count - a.count || a.title.localeCompare(b.title, 'zh-CN'));
+
+ const holderBars = holderStats.map(([name, count]) => `
+
+
${escapeHtml(name)}
+
+
${count} 项
+
+ `).join('');
+
+ const awardChips = awardStats.map(item => `
+
+ ${escapeHtml(item.title)}
+ ${item.count}
+ 人获得
+
+ `).join('');
+
+ return `
+
+
+ 查询结果
+ ${data.length}
+
+
+ 涉及人员
+ ${holderStats.length}
+
+
+ 奖项种类
+ ${awardStats.length}
+
+
+
+
+ 个人奖项数量
+ ${holderBars}
+
+
+ 查询奖项获得人数
+ ${awardChips || '
暂无奖项名称
'}
+
+
+ `;
+ }
+
+ function renderResults(data, awardRecipientCounts = {}) {
+ const display = document.getElementById('resultsArea');
+ if (!data || data.length === 0) {
+ display.innerHTML = '未找到匹配的证书记录。
';
+ return;
+ }
+
+ let html = `
+ ${renderAwardStats(data, awardRecipientCounts)}
+
+
+
+
+ | 证书编号 |
+ 持有人 |
+ 荣誉标题 |
+ 颁发日期 |
+ 操作 |
+
+
+
+ `;
+
+ data.forEach(item => {
+ html += `
+
+ | ${escapeHtml(item.cert_number || '-')} |
+ ${escapeHtml(item.holder_name || '-')} |
+ ${escapeHtml(item.honor_title || '-')} |
+ ${escapeHtml(item.issue_date || '-')} |
+ |
+
+ `;
+ });
+
+ html += '
';
+ display.innerHTML = html;
+ }
+
+ function goToDetail(id) {
+ window.location.href = `certificate.html?id=${id}`;
+ }
+
+ function initializeSearchTermFromUrl() {
+ const params = new URLSearchParams(window.location.search);
+ const presetTerm =
+ params.get('searchTerm') ||
+ params.get('term') ||
+ params.get('keyword') ||
+ params.get('cert_number') ||
+ params.get('certNumber') ||
+ params.get('number') ||
+ params.get('awardName') ||
+ params.get('honor_title') ||
+ params.get('honorTitle') ||
+ params.get('award') ||
+ params.get('q') ||
+ '';
+
+ if (presetTerm) {
+ document.getElementById('searchTerm').value = presetTerm;
+ }
+ }
+
+ document.getElementById('searchBtn').addEventListener('click', handleSearch);
+ document.getElementById('searchTerm').addEventListener('keypress', e => {
+ if (e.key === 'Enter') handleSearch();
+ });
+ initializeSearchTermFromUrl();
diff --git a/2000s/js/notice-banner.js b/2000s/js/notice-banner.js
new file mode 100644
index 0000000..798e694
--- /dev/null
+++ b/2000s/js/notice-banner.js
@@ -0,0 +1,175 @@
+(function () {
+ var currentScript = document.currentScript;
+ var source = currentScript && currentScript.getAttribute("data-notice-src");
+
+ if (!source && currentScript && currentScript.src) {
+ source = currentScript.src.replace(/js\/notice-banner\.js(?:\?.*)?$/, "notices.json");
+ }
+
+ if (!source) {
+ source = "notices.json";
+ }
+
+ var cacheKey = Math.floor(Date.now() / (5 * 60 * 1000));
+ var joiner = source.indexOf("?") === -1 ? "?" : "&";
+
+ fetch(source + joiner + "v=" + cacheKey, { cache: "no-store" })
+ .then(function (response) {
+ if (!response.ok) {
+ throw new Error("Notice source unavailable");
+ }
+ return response.json();
+ })
+ .then(function (payload) {
+ var notice = selectNotice(payload);
+ if (notice) {
+ renderNotice(notice);
+ }
+ })
+ .catch(function () {
+ // The page should stay quiet when the optional notice source is missing.
+ });
+
+ function selectNotice(payload) {
+ var notices = Array.isArray(payload) ? payload : payload && payload.notices;
+
+ if (!Array.isArray(notices)) {
+ return null;
+ }
+
+ var now = Date.now();
+ var path = normalizePath(window.location.pathname);
+
+ return notices.find(function (notice) {
+ if (!notice || notice.enabled === false) {
+ return false;
+ }
+
+ if (notice.startsAt && Date.parse(notice.startsAt) > now) {
+ return false;
+ }
+
+ if (notice.endsAt && Date.parse(notice.endsAt) < now) {
+ return false;
+ }
+
+ if (notice.pages && notice.pages.length) {
+ return notice.pages.some(function (page) {
+ return pathMatches(path, normalizePath(page));
+ });
+ }
+
+ return true;
+ });
+ }
+
+ function renderNotice(notice) {
+ var dismissedId = window.localStorage.getItem("dismissedNoticeId");
+ var noticeId = String(notice.id || "");
+
+ if (notice.dismissible !== false && noticeId && dismissedId === noticeId) {
+ return;
+ }
+
+ var body = document.body;
+ var originalPadding = window.getComputedStyle(body).paddingTop || "0px";
+ body.style.setProperty("--notice-body-pad-top", originalPadding);
+
+ var banner = document.createElement("div");
+ banner.className = "notice-banner";
+ banner.setAttribute("role", "status");
+ banner.setAttribute("aria-live", "polite");
+ banner.dataset.level = notice.level || "info";
+
+ var inner = document.createElement("div");
+ inner.className = "notice-banner__inner";
+
+ var badge = document.createElement("span");
+ badge.className = "notice-banner__badge";
+ badge.textContent = notice.badge || levelLabel(notice.level);
+
+ var content = document.createElement("div");
+ content.className = "notice-banner__content";
+
+ if (notice.title) {
+ var title = document.createElement("span");
+ title.className = "notice-banner__title";
+ title.textContent = notice.title;
+ content.appendChild(title);
+ }
+
+ var message = document.createElement("span");
+ message.className = "notice-banner__message";
+ message.textContent = notice.message || "";
+ content.appendChild(message);
+
+ inner.appendChild(badge);
+ inner.appendChild(content);
+
+ if (notice.url) {
+ var link = document.createElement("a");
+ link.className = "notice-banner__link";
+ link.href = resolveUrl(notice.url, source);
+ link.textContent = notice.linkText || "查看详情";
+ inner.appendChild(link);
+ }
+
+ var close = document.createElement("button");
+ close.className = "notice-banner__close";
+ close.type = "button";
+ close.setAttribute("aria-label", "关闭通知");
+ close.textContent = "×";
+ close.addEventListener("click", function () {
+ if (notice.dismissible !== false && noticeId) {
+ window.localStorage.setItem("dismissedNoticeId", noticeId);
+ }
+ banner.remove();
+ body.classList.remove("has-notice-banner");
+ body.style.removeProperty("--notice-banner-height");
+ });
+
+ inner.appendChild(close);
+ banner.appendChild(inner);
+ body.insertBefore(banner, body.firstChild);
+
+ requestAnimationFrame(function () {
+ banner.classList.add("is-visible");
+ body.style.setProperty("--notice-banner-height", banner.offsetHeight + "px");
+ body.classList.add("has-notice-banner");
+ });
+ }
+
+ function levelLabel(level) {
+ var labels = {
+ warning: "提醒",
+ error: "紧急",
+ success: "完成"
+ };
+
+ return labels[level] || "通知";
+ }
+
+ function normalizePath(path) {
+ return String(path || "").replace(/\\/g, "/").replace(/^.*?:\/\/[^/]+/, "");
+ }
+
+ function resolveUrl(url, base) {
+ try {
+ return new URL(url, new URL(base, window.location.href)).href;
+ } catch (error) {
+ return url;
+ }
+ }
+
+ function pathMatches(path, pattern) {
+ if (!pattern || pattern === "*") {
+ return true;
+ }
+
+ if (pattern.charAt(pattern.length - 1) === "*") {
+ return path.indexOf(pattern.slice(0, -1)) === 0;
+ }
+
+ return path === pattern || path.endsWith(pattern);
+ }
+})();
diff --git a/2000s/js/notice-index.js b/2000s/js/notice-index.js
new file mode 100644
index 0000000..f7c87e4
--- /dev/null
+++ b/2000s/js/notice-index.js
@@ -0,0 +1,70 @@
+(function () {
+ fetch("../notices.json?v=" + Math.floor(Date.now() / 300000), { cache: "no-store" })
+ .then(function (response) {
+ if (!response.ok) {
+ throw new Error("notice source unavailable");
+ }
+ return response.json();
+ })
+ .then(function (payload) {
+ var notices = Array.isArray(payload) ? payload : payload.notices;
+ var notice = Array.isArray(notices) && notices.find(function (item) {
+ return item && item.enabled !== false;
+ });
+
+ if (notice) {
+ renderNotice(notice);
+ }
+ })
+ .catch(function () {});
+
+ function renderNotice(notice) {
+ setText("noticeTitle", notice.title || "重要通知");
+ setText("noticeSummary", notice.message || "");
+ setText("noticeBadge", notice.badge || "通知");
+ setText("noticeLevel", String(notice.level || "info").toUpperCase());
+ setText("articleTitle", notice.title || "重要通知");
+ setText("articleMessage", notice.message || "");
+ setText("noticeDate", formatDate(notice.startsAt) || "未设置开始时间");
+ setText("noticeWindow", formatWindow(notice.startsAt, notice.endsAt));
+ }
+
+ function setText(id, text) {
+ var node = document.getElementById(id);
+ if (node) {
+ node.textContent = text;
+ }
+ }
+
+ function formatDate(value) {
+ if (!value) {
+ return "";
+ }
+
+ var date = new Date(value);
+ if (Number.isNaN(date.getTime())) {
+ return "";
+ }
+
+ return date.toLocaleDateString("zh-CN", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit"
+ });
+ }
+
+ function formatWindow(start, end) {
+ var startText = formatDate(start);
+ var endText = formatDate(end);
+
+ if (startText && endText) {
+ return startText + " 至 " + endText;
+ }
+
+ if (startText) {
+ return startText + " 起生效";
+ }
+
+ return "长期有效";
+ }
+ })();
diff --git a/2000s/js/ticket-detail.js b/2000s/js/ticket-detail.js
new file mode 100644
index 0000000..8ca1f79
--- /dev/null
+++ b/2000s/js/ticket-detail.js
@@ -0,0 +1,100 @@
+const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
+ const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
+
+ if (!window._sbClient) {
+ window._sbClient = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
+ }
+ const sbClient = window._sbClient;
+
+ async function loadTicketDetail() {
+ const urlParams = new URLSearchParams(window.location.search);
+ const ticketId = urlParams.get('id');
+
+ if (!ticketId) {
+ showError('未提供单据 ID。');
+ return;
+ }
+
+ try {
+ const { data: ticket, error } = await sbClient
+ .from('tickets')
+ .select('*')
+ .eq('id', ticketId)
+ .single();
+
+ if (error) throw error;
+ if (!ticket) {
+ showError('未找到该单据。');
+ return;
+ }
+
+ displayTicketDetail(ticket);
+ } catch (error) {
+ showError(error.message || '加载失败');
+ }
+ }
+
+ function displayTicketDetail(ticket) {
+ const el = document.getElementById('detailContent');
+ const processedLabel = ticket.processed ? '已处理' : '未处理';
+ const completionLabel = ticket.status ? '已办结' : '未办结';
+ const amount = Number(ticket.amount || 0).toLocaleString('zh-CN', {
+ style: 'currency',
+ currency: 'CNY'
+ });
+
+ el.classList.remove('feedback-card');
+ el.innerHTML = `
+
+
+
+ ${item('编号', ticket.ticket_number)}
+ ${item('姓名', ticket.customer_name)}
+ ${item('事由', ticket.reason)}
+ ${item('处理结果', ticket.result)}
+ ${item('金额', amount, 'money')}
+ ${item('开具人', ticket.issuer)}
+ ${item('创建时间', formatDate(ticket.created_at))}
+ ${item('办结日期', formatDate(ticket.processed_at))}
+ ${item('处理状态', `${processedLabel}`, '', true)}
+ ${item('办结状态', `${completionLabel}`, '', true)}
+ ${item('备注', ticket.remarks || '无', '', false, true)}
+
+ `;
+ }
+
+ function item(label, value, extraClass = '', isHTML = false, isWide = false) {
+ return `
+
+ ${label}
+
+
+ `;
+ }
+
+ function goPrint() {
+ window.open(`print.html?id=${new URLSearchParams(location.search).get('id')}`, '_blank');
+ }
+
+ function formatDate(dateString) {
+ if (!dateString) return '暂无';
+ return new Date(dateString).toLocaleString('zh-CN');
+ }
+
+ function showError(msg) {
+ const el = document.getElementById('detailContent');
+ el.classList.add('feedback-card');
+ el.innerHTML = `${msg}
`;
+ }
+
+ document.addEventListener('DOMContentLoaded', loadTicketDetail);
diff --git a/2000s/js/ticket-index.js b/2000s/js/ticket-index.js
new file mode 100644
index 0000000..8488d38
--- /dev/null
+++ b/2000s/js/ticket-index.js
@@ -0,0 +1,132 @@
+const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
+ const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
+ const sbClient = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
+
+ let html5QrCode = null;
+
+ document.getElementById('startScanBtn').addEventListener('click', async () => {
+ document.getElementById('scanner-overlay').style.display = 'flex';
+ if (html5QrCode) {
+ try {
+ await html5QrCode.stop();
+ } catch (e) {}
+ }
+ html5QrCode = new Html5Qrcode("reader");
+ const config = { fps: 15, qrbox: { width: 250, height: 250 }, aspectRatio: 1.0 };
+ try {
+ await html5QrCode.start({ facingMode: "environment" }, config, (text) => {
+ document.getElementById('searchTerm').value = text;
+ closeScanner();
+ handleSearch();
+ });
+ } catch (err) {
+ alert("摄像头启动失败。");
+ closeScanner();
+ }
+ });
+
+ async function closeScanner() {
+ if (html5QrCode && html5QrCode.isScanning) {
+ await html5QrCode.stop();
+ }
+ document.getElementById('scanner-overlay').style.display = 'none';
+ }
+
+ document.getElementById('stopScanBtn').addEventListener('click', closeScanner);
+
+ async function handleSearch() {
+ const searchTerm = document.getElementById('searchTerm').value.trim();
+ const dateFrom = document.getElementById('dateFrom').value;
+ const dateTo = document.getElementById('dateTo').value;
+ const resultsDiv = document.getElementById('searchResults');
+
+ resultsDiv.innerHTML = '正在连接单据数据库并执行检索...
';
+
+ try {
+ let query = sbClient.from('tickets').select('*');
+ if (searchTerm) {
+ query = query.or(`ticket_number.ilike.%${searchTerm}%,customer_name.ilike.%${searchTerm}%`);
+ }
+ if (dateFrom) query = query.gte('created_at', dateFrom);
+ if (dateTo) query = query.lte('created_at', dateTo);
+
+ const { data, error } = await query.order('created_at', { ascending: false });
+ if (error) throw error;
+
+ displayResults(data);
+ } catch (error) {
+ resultsDiv.innerHTML = `查询失败:${error.message}
`;
+ }
+ }
+
+ function displayResults(tickets) {
+ const resultsDiv = document.getElementById('searchResults');
+ if (!tickets || tickets.length === 0) {
+ resultsDiv.innerHTML = '未找到符合条件的单据记录。
';
+ return;
+ }
+
+ let html = `
+
+
+
+
+ | 编号 |
+ 姓名 |
+ 事由 |
+ 金额 |
+ 日期 |
+ 状态 |
+ 操作 |
+
+
+
+ `;
+
+ tickets.forEach(ticket => {
+ const amount = (ticket.amount || 0).toLocaleString('zh-CN', { style: 'currency', currency: 'CNY' });
+ const reason = ticket.reason || '';
+ const displayReason = reason.length > 18
+ ? `${reason}`
+ : reason;
+
+ html += `
+
+ | ${ticket.ticket_number || ''} |
+ ${ticket.customer_name || ''} |
+ ${displayReason} |
+ ${amount} |
+ ${ticket.created_at ? new Date(ticket.created_at).toLocaleDateString('zh-CN') : '-'} |
+
+
+ ${ticket.processed ? '已处理' : '未处理'}
+
+ |
+ |
+
+ `;
+ });
+
+ html += '
';
+ resultsDiv.innerHTML = html;
+ }
+
+ function initializeSearchTermFromUrl() {
+ const params = new URLSearchParams(window.location.search);
+ const presetTerm =
+ params.get('searchTerm') ||
+ params.get('term') ||
+ params.get('keyword') ||
+ params.get('ticket_number') ||
+ params.get('ticketNumber') ||
+ params.get('number') ||
+ params.get('q') ||
+ '';
+
+ if (presetTerm) {
+ document.getElementById('searchTerm').value = presetTerm;
+ }
+ }
+
+ document.getElementById('searchBtn').addEventListener('click', handleSearch);
+ initializeSearchTermFromUrl();
diff --git a/2000s/js/ticket-print.js b/2000s/js/ticket-print.js
new file mode 100644
index 0000000..41006d8
--- /dev/null
+++ b/2000s/js/ticket-print.js
@@ -0,0 +1,47 @@
+const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
+const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
+
+const sb = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
+
+function formatDate(d){
+ return new Date(d).toLocaleString('zh-CN');
+}
+
+function updateWatermark(customerName) {
+ const now = new Date().toLocaleString('zh-CN');
+
+ const watermarkText = `BISS · ${customerName || 'Unknown'} · ${now}`;
+
+ document.getElementById('watermark').innerText = watermarkText;
+}
+
+async function load() {
+ const id = new URLSearchParams(location.search).get('id');
+
+ const { data } = await sb.from('tickets').select('*').eq('id', id).single();
+
+ if (!data) return;
+
+ document.getElementById('ticket_number').innerText = data.ticket_number || '';
+ document.getElementById('customer_name').innerText = data.customer_name || '';
+ document.getElementById('reason').innerText = data.reason || '';
+ document.getElementById('amount').innerText = '¥' + (data.amount || 0).toFixed(2);
+ document.getElementById('issuer').innerText = data.issuer || '';
+ document.getElementById('created_at').innerText = formatDate(data.created_at);
+ document.getElementById('processed').innerText = data.processed ? '已办结' : '未办结';
+ document.getElementById('processed_at').innerText = data.processed_at ? formatDate(data.processed_at) : '暂无';
+ document.getElementById('remarks').innerText = data.remarks || '无';
+
+ // 打印时间
+ document.getElementById('print_time').innerText =
+ new Date().toLocaleString('zh-CN');
+
+ updateWatermark(data.customer_name);
+
+ // 自动打印
+ setTimeout(() => {
+ window.print();
+ }, 300);
+}
+
+load();
diff --git a/2000s/js/xinhan-index.js b/2000s/js/xinhan-index.js
new file mode 100644
index 0000000..a929d39
--- /dev/null
+++ b/2000s/js/xinhan-index.js
@@ -0,0 +1,73 @@
+const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
+ const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
+ const sb = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
+
+ async function handleSearch() {
+ const recipientTerm = document.getElementById('recipientTerm').value.trim();
+ const contentTerm = document.getElementById('searchTerm').value.trim();
+ const from = document.getElementById('dateFrom').value;
+ const to = document.getElementById('dateTo').value;
+ const out = document.getElementById('searchResults');
+
+ out.innerHTML = '正在连接信函数据库并执行检索...
';
+
+ let q = sb.from('xinhan').select('*');
+ if (recipientTerm) q = q.ilike('recipient', `%${recipientTerm}%`);
+ if (contentTerm) q = q.ilike('content', `%${contentTerm}%`);
+ if (from) q = q.gte('sent_date', from);
+ if (to) q = q.lte('sent_date', to);
+
+ const { data, error } = await q.order('sent_date', { ascending: false });
+ if (error) {
+ out.innerHTML = `查询失败:${error.message}
`;
+ return;
+ }
+ renderResults(data || []);
+ }
+
+ function renderResults(rows) {
+ const container = document.getElementById('searchResults');
+ if (!rows.length) {
+ container.innerHTML = '未找到符合条件的信函记录。
';
+ return;
+ }
+
+ let html = `
+
+
+
+
+ | ID |
+ 收件人 |
+ 目的地 |
+ 寄出日期 |
+ 寄达日期 |
+ 内容摘要 |
+
+
+
+ `;
+
+ rows.forEach(row => {
+ const content = row.content || '';
+ const summary = content.length > 60
+ ? `${content}`
+ : content;
+
+ html += `
+
+ | ${row.id} |
+ ${row.recipient || ''} |
+ ${row.destination || ''} |
+ ${row.sent_date || ''} |
+ ${row.arrival_date || ''} |
+ ${summary} |
+
+ `;
+ });
+
+ html += '
';
+ container.innerHTML = html;
+ }
+
+ document.getElementById('searchBtn').addEventListener('click', handleSearch);
diff --git a/2000s/notice/index.html b/2000s/notice/index.html
new file mode 100644
index 0000000..25c5ad8
--- /dev/null
+++ b/2000s/notice/index.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+ 通知详情
+
+
+
+
+
+
+
+
+
+
+
+
Notice Center
+
重要通知
+
2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。
+
+
+
+
+
+
+
+ 2026-05-01
+ 当前生效
+
+ 重要通知
+ 2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。
+
+
+
+ 适用范围
+ 证书、信函与单据查询
+
+
+ 提醒类型
+ 服务公告与使用提示
+
+
+ 查看方式
+ 页面顶部通知横幅
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2000s/ticket/detail.html b/2000s/ticket/detail.html
new file mode 100644
index 0000000..ea0cf28
--- /dev/null
+++ b/2000s/ticket/detail.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+ 单据详情
+
+
+
+
+
+
+
+
+
+
+
返回查询页
+
+
+
+
+
+
+
+ 正在加载单据详情...
+
+
+
+
+
+
+
+
+
+
diff --git a/2000s/ticket/index.html b/2000s/ticket/index.html
new file mode 100644
index 0000000..b3e7542
--- /dev/null
+++ b/2000s/ticket/index.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+ 单据查询系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
输入查询条件后开始检索,或使用扫码功能快速定位单据。
+
+
+
+
+
+
+
请将二维码保持在扫描框内
+
+
+
+
+
+
+
+
+
diff --git a/2000s/ticket/print.html b/2000s/ticket/print.html
new file mode 100644
index 0000000..b30b043
--- /dev/null
+++ b/2000s/ticket/print.html
@@ -0,0 +1,46 @@
+
+
+
+
+打印单据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
单据详情
+
+
+ | 编号 | |
+ | 姓名 | |
+ | 事由 | |
+ | 金额 | |
+ | 开具人 | |
+ | 日期 | |
+ | 办结否 | |
+ | 办结日期 | |
+ | 备注 |
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2000s/xinhan/index.html b/2000s/xinhan/index.html
new file mode 100644
index 0000000..b4857e0
--- /dev/null
+++ b/2000s/xinhan/index.html
@@ -0,0 +1,84 @@
+
+
+
+
+
+ 信函查询系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
检索条件
+
多字段组合查询,快速过滤信函记录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+