From bad707fce13ebe3a6f1937f8335adc3c9cb75a55 Mon Sep 17 00:00:00 2001 From: biss Date: Fri, 1 May 2026 08:25:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0notice=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- certs/certificate.html | 2 + certs/index.html | 2 + css/notice-banner.css | 136 +++++++++++++++++++ css/notice-page.css | 301 +++++++++++++++++++++++++++++++++++++++++ js/notice-banner.js | 175 ++++++++++++++++++++++++ notice/index.html | 141 +++++++++++++++++++ notices.json | 17 +++ pages/fmx.html | 4 +- pages/ljq.html | 4 +- pages/wyj.html | 4 +- pages/wzy.html | 4 +- pages/zjq.html | 4 +- ticket/detail.html | 2 + ticket/index.html | 2 + ticket/print.html | 5 +- xinhan/index.html | 2 + 16 files changed, 799 insertions(+), 6 deletions(-) create mode 100644 css/notice-banner.css create mode 100644 css/notice-page.css create mode 100644 js/notice-banner.js create mode 100644 notice/index.html create mode 100644 notices.json diff --git a/certs/certificate.html b/certs/certificate.html index 1715ac5..f8c4edc 100644 --- a/certs/certificate.html +++ b/certs/certificate.html @@ -8,6 +8,7 @@ + @@ -205,5 +206,6 @@ loadData(); + diff --git a/certs/index.html b/certs/index.html index d31bb05..c1d6226 100644 --- a/certs/index.html +++ b/certs/index.html @@ -8,6 +8,7 @@ + @@ -337,5 +338,6 @@ initializeSearchTermFromUrl(); + diff --git a/css/notice-banner.css b/css/notice-banner.css new file mode 100644 index 0000000..ee8003c --- /dev/null +++ b/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/css/notice-page.css b/css/notice-page.css new file mode 100644 index 0000000..6667c3d --- /dev/null +++ b/css/notice-page.css @@ -0,0 +1,301 @@ +: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; + } +} + +@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/js/notice-banner.js b/js/notice-banner.js new file mode 100644 index 0000000..798e694 --- /dev/null +++ b/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/notice/index.html b/notice/index.html new file mode 100644 index 0000000..69213a5 --- /dev/null +++ b/notice/index.html @@ -0,0 +1,141 @@ + + + + + + 通知详情 + + + + + + +
+ + +
+
+ Notice Center +

重要通知

+

2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。

+
+
+ 通知 +
INFO
+

长期有效

+
+
+ +
+
+
+ 2026-05-01 + 当前生效 +
+

重要通知

+

2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。

+ +
+
+ 适用范围 + 证书、信函与单据查询 +
+
+ 提醒类型 + 服务公告与使用提示 +
+
+ 查看方式 + 页面顶部通知横幅 +
+
+
+ + +
+
+ + + + diff --git a/notices.json b/notices.json new file mode 100644 index 0000000..f7c4ad6 --- /dev/null +++ b/notices.json @@ -0,0 +1,17 @@ +{ + "notices": [ + { + "id": "site-notice-2026-05-01", + "enabled": true, + "level": "info", + "badge": "通知", + "title": "重要通知", + "message": "2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。", + "linkText": "查看详情", + "url": "notice/index.html", + "dismissible": true, + "startsAt": "2026-05-01T00:00:00+08:00", + "endsAt": "" + } + ] +} diff --git a/pages/fmx.html b/pages/fmx.html index 5ab8b19..488a676 100644 --- a/pages/fmx.html +++ b/pages/fmx.html @@ -6,6 +6,7 @@ 多彩场景信函样例 + @@ -31,5 +32,6 @@ + - \ No newline at end of file + diff --git a/pages/ljq.html b/pages/ljq.html index 7677eba..35597e6 100644 --- a/pages/ljq.html +++ b/pages/ljq.html @@ -6,6 +6,7 @@ 多彩场景信函样例 + @@ -31,5 +32,6 @@ + - \ No newline at end of file + diff --git a/pages/wyj.html b/pages/wyj.html index 1aaa737..fbd8e66 100644 --- a/pages/wyj.html +++ b/pages/wyj.html @@ -6,6 +6,7 @@ 多彩场景信函样例 + @@ -31,5 +32,6 @@ + - \ No newline at end of file + diff --git a/pages/wzy.html b/pages/wzy.html index 52a41a7..476fb38 100644 --- a/pages/wzy.html +++ b/pages/wzy.html @@ -6,6 +6,7 @@ 多彩场景信函样例 + @@ -31,5 +32,6 @@ + - \ No newline at end of file + diff --git a/pages/zjq.html b/pages/zjq.html index e24a51d..9becc2c 100644 --- a/pages/zjq.html +++ b/pages/zjq.html @@ -6,6 +6,7 @@ 多彩场景信函样例 + @@ -31,5 +32,6 @@ + - \ No newline at end of file + diff --git a/ticket/detail.html b/ticket/detail.html index 82f4744..5ab5244 100644 --- a/ticket/detail.html +++ b/ticket/detail.html @@ -8,6 +8,7 @@ + @@ -132,5 +133,6 @@ document.addEventListener('DOMContentLoaded', loadTicketDetail); + diff --git a/ticket/index.html b/ticket/index.html index a4d46b5..01204e9 100644 --- a/ticket/index.html +++ b/ticket/index.html @@ -8,6 +8,7 @@ + @@ -223,5 +224,6 @@ document.getElementById('searchBtn').addEventListener('click', handleSearch); initializeSearchTermFromUrl(); + diff --git a/ticket/print.html b/ticket/print.html index 617c609..1fe5a75 100644 --- a/ticket/print.html +++ b/ticket/print.html @@ -4,6 +4,8 @@ 打印单据 + +