添加notice功能
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/tech-query.css">
|
<link rel="stylesheet" href="../css/tech-query.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -205,5 +206,6 @@
|
|||||||
loadData();
|
loadData();
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/tech-query.css">
|
<link rel="stylesheet" href="../css/tech-query.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||||
<script src="https://unpkg.com/html5-qrcode"></script>
|
<script src="https://unpkg.com/html5-qrcode"></script>
|
||||||
</head>
|
</head>
|
||||||
@@ -337,5 +338,6 @@
|
|||||||
initializeSearchTermFromUrl();
|
initializeSearchTermFromUrl();
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>通知详情</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../css/notice-page.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="notice-page">
|
||||||
|
<nav class="topbar" aria-label="页面导航">
|
||||||
|
<a class="brand" href="../index.html">
|
||||||
|
<span class="brand-mark">BI</span>
|
||||||
|
<span>通知中心</span>
|
||||||
|
</a>
|
||||||
|
<a class="back-link" href="../index.html">返回首页</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section class="notice-hero">
|
||||||
|
<div class="hero-copy">
|
||||||
|
<span class="eyebrow">Notice Center</span>
|
||||||
|
<h1 id="noticeTitle">重要通知</h1>
|
||||||
|
<p id="noticeSummary">2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。</p>
|
||||||
|
</div>
|
||||||
|
<div class="status-panel" aria-label="通知状态">
|
||||||
|
<span class="status-badge" id="noticeBadge">通知</span>
|
||||||
|
<div class="status-code" id="noticeLevel">INFO</div>
|
||||||
|
<p id="noticeWindow">长期有效</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="notice-layout">
|
||||||
|
<article class="notice-card">
|
||||||
|
<div class="notice-meta">
|
||||||
|
<span id="noticeDate">2026-05-01</span>
|
||||||
|
<span id="noticeState">当前生效</span>
|
||||||
|
</div>
|
||||||
|
<h2 id="articleTitle">重要通知</h2>
|
||||||
|
<p id="articleMessage">2026 年上半年 4 月数据已完成更新。请按需进入相关查询页面查看最新记录。</p>
|
||||||
|
|
||||||
|
<div class="info-grid">
|
||||||
|
<div>
|
||||||
|
<span class="info-label">适用范围</span>
|
||||||
|
<strong>证书、信函与单据查询</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="info-label">提醒类型</span>
|
||||||
|
<strong>服务公告与使用提示</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="info-label">查看方式</span>
|
||||||
|
<strong>页面顶部通知横幅</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<aside class="side-card">
|
||||||
|
<h2>温馨提示</h2>
|
||||||
|
<p>如通知涉及查询范围、开放时间或数据更新,请以本页展示内容为准。完成阅读后,可返回首页继续使用相关查询服务。</p>
|
||||||
|
<a class="primary-action" href="../index.html">返回查询入口</a>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(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 "长期有效";
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -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": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+3
-1
@@ -6,6 +6,7 @@
|
|||||||
<title>多彩场景信函样例</title>
|
<title>多彩场景信函样例</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/style.css">
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -31,5 +32,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,7 @@
|
|||||||
<title>多彩场景信函样例</title>
|
<title>多彩场景信函样例</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/style.css">
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -31,5 +32,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,7 @@
|
|||||||
<title>多彩场景信函样例</title>
|
<title>多彩场景信函样例</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/style.css">
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -31,5 +32,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,7 @@
|
|||||||
<title>多彩场景信函样例</title>
|
<title>多彩场景信函样例</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/style.css">
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -31,5 +32,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,7 @@
|
|||||||
<title>多彩场景信函样例</title>
|
<title>多彩场景信函样例</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/style.css">
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -31,5 +32,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/tech-query.css">
|
<link rel="stylesheet" href="../css/tech-query.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -132,5 +133,6 @@
|
|||||||
document.addEventListener('DOMContentLoaded', loadTicketDetail);
|
document.addEventListener('DOMContentLoaded', loadTicketDetail);
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/tech-query.css">
|
<link rel="stylesheet" href="../css/tech-query.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||||
<script src="https://unpkg.com/html5-qrcode"></script>
|
<script src="https://unpkg.com/html5-qrcode"></script>
|
||||||
@@ -223,5 +224,6 @@
|
|||||||
document.getElementById('searchBtn').addEventListener('click', handleSearch);
|
document.getElementById('searchBtn').addEventListener('click', handleSearch);
|
||||||
initializeSearchTermFromUrl();
|
initializeSearchTermFromUrl();
|
||||||
</script>
|
</script>
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+4
-1
@@ -4,6 +4,8 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>打印单据</title>
|
<title>打印单据</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: Arial, "Microsoft YaHei";
|
font-family: Arial, "Microsoft YaHei";
|
||||||
@@ -171,5 +173,6 @@ async function load() {
|
|||||||
load();
|
load();
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
<script src="https://cdn.jsdmirror.cn/gh/bishshi/wechat-detect@main/wechat-detect.js"></script>
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="../css/tech-query.css">
|
<link rel="stylesheet" href="../css/tech-query.css">
|
||||||
|
<link rel="stylesheet" href="../css/notice-banner.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -152,5 +153,6 @@
|
|||||||
|
|
||||||
document.getElementById('searchBtn').addEventListener('click', handleSearch);
|
document.getElementById('searchBtn').addEventListener('click', handleSearch);
|
||||||
</script>
|
</script>
|
||||||
|
<script src="../js/notice-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user