diff --git a/modules/M22-bulletin/index.html b/modules/M22-bulletin/index.html
index 82e85611..e81addec 100644
--- a/modules/M22-bulletin/index.html
+++ b/modules/M22-bulletin/index.html
@@ -4,6 +4,10 @@
HoloLake公告栏
+
+
+
+
@@ -22,13 +26,32 @@
-
+
+
+
+
+
+
+
+
+
@@ -40,7 +63,7 @@
-
+
diff --git a/modules/M22-bulletin/script.js b/modules/M22-bulletin/script.js
index 9c1950b9..0473d39e 100644
--- a/modules/M22-bulletin/script.js
+++ b/modules/M22-bulletin/script.js
@@ -1,5 +1,5 @@
// ===== script.js =====
-// 知秋:主逻辑·动态渲染+loading+错误处理+i18n+无障碍
+// 知秋:主逻辑·动态渲染+错误处理+i18n+无障碍+骨架屏
(function() {
// 状态变量
@@ -11,21 +11,39 @@
// DOM元素
const container = document.querySelector('.bulletin-container');
const channelTabs = document.querySelectorAll('.channel-tab');
+ const skeletonContainer = document.getElementById('skeleton-container');
+
+ // ========== 骨架屏控制 ==========
+ function showSkeleton() {
+ if (skeletonContainer) {
+ skeletonContainer.style.display = 'block';
+ }
+ }
+
+ function hideSkeleton() {
+ if (skeletonContainer) {
+ skeletonContainer.style.opacity = '0';
+ skeletonContainer.style.transition = 'opacity 0.3s ease';
+ setTimeout(() => {
+ skeletonContainer.style.display = 'none';
+ }, 300);
+ }
+ }
// 初始化
async function init() {
- console.log("【知秋】M22公告栏·环节6启动");
+ console.log("【知秋】M22公告栏·环节7启动");
// 监听语言切换事件
window.addEventListener('languagechange', () => {
- render(); // 语言变了,重新渲染
+ render();
});
- await loadBulletins(); // 加载数据
- render(); // 初次渲染
- setupEventListeners(); // 频道切换事件
- setupLangSwitcher(); // 语言切换器事件
- setupKeyboardNavigation(); // 键盘导航
+ await loadBulletins();
+ render();
+ setupEventListeners();
+ setupLangSwitcher();
+ setupKeyboardNavigation();
}
// 语言切换器
@@ -34,12 +52,10 @@
btn.addEventListener('click', () => {
const lang = btn.dataset.lang;
window.i18n.switchLang(lang);
- // 更新按钮激活状态
document.querySelectorAll('.lang-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
});
});
- // 设置当前语言按钮激活状态
const currentLang = window.i18n.currentLang;
document.querySelectorAll('.lang-btn').forEach(btn => {
if (btn.dataset.lang === currentLang) {
@@ -48,9 +64,8 @@
});
}
- // 键盘导航
+ // 键盘导航
function setupKeyboardNavigation() {
- // 给每个频道标签加上键盘事件
const tabs = document.querySelectorAll('.channel-tab');
tabs.forEach(tab => {
@@ -61,13 +76,11 @@
e.preventDefault();
const nextIndex = (currentIndex + 1) % tabs.length;
tabs[nextIndex].focus();
- // 自动激活聚焦的标签
tabs[nextIndex].click();
} else if (e.key === 'ArrowLeft') {
e.preventDefault();
const prevIndex = (currentIndex - 1 + tabs.length) % tabs.length;
tabs[prevIndex].focus();
- // 自动激活聚焦的标签
tabs[prevIndex].click();
} else if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
@@ -75,47 +88,26 @@
}
});
});
-
- // 也可以保留原来的 channel-bar 监听作为备用
- const channelBar = document.querySelector('.channel-bar');
- if (channelBar) {
- channelBar.addEventListener('keydown', (e) => {
- // 防止重复处理
- if (e.target.classList.contains('channel-tab')) return;
-
- const tabs = Array.from(document.querySelectorAll('.channel-tab'));
- const currentIndex = tabs.findIndex(tab => tab === document.activeElement);
-
- if (e.key === 'ArrowRight') {
- e.preventDefault();
- const nextIndex = (currentIndex + 1) % tabs.length;
- tabs[nextIndex].focus();
- tabs[nextIndex].click();
- } else if (e.key === 'ArrowLeft') {
- e.preventDefault();
- const prevIndex = (currentIndex - 1 + tabs.length) % tabs.length;
- tabs[prevIndex].focus();
- tabs[prevIndex].click();
- }
- });
- }
}
- // 加载公告(核心!)
+
+ // 加载公告
async function loadBulletins() {
+ showSkeleton(); // 显示骨架屏
loading = true;
error = null;
- render(); // 显示loading
try {
const data = await API.fetchBulletins();
allBulletins = data;
+ hideSkeleton(); // 数据到手,隐藏骨架屏
loading = false;
- render();
+ render(); // 渲染真实内容
} catch (err) {
console.error("加载失败:", err);
+ hideSkeleton(); // 出错也要隐藏骨架屏
loading = false;
error = "error";
- render();
+ render(); // 显示错误状态
}
}
@@ -123,17 +115,6 @@
function render() {
if (!container) return;
- // 加载状态
- if (loading) {
- container.innerHTML = `
-
-
-
${window.i18n.t('loading')}
-
- `;
- return;
- }
-
// 错误状态
if (error) {
const isOffline = !navigator.onLine;
@@ -178,13 +159,12 @@
return;
}
- // 使用环节4的createCard(如果不存在就降级)
+ // 渲染公告卡片
let html = '';
filtered.forEach((item, index) => {
if (typeof createCard === 'function') {
html += createCard(item);
} else {
- // 降级方案(保证显示)
html += `
${item.title}
@@ -200,7 +180,7 @@
container.innerHTML = html;
}
- // 事件监听(频道切换)
+ // 频道切换事件
function setupEventListeners() {
channelTabs.forEach(tab => {
tab.addEventListener('click', (e) => {
diff --git a/modules/M22-bulletin/style.css b/modules/M22-bulletin/style.css
index 351c2abb..3ffffa9f 100644
--- a/modules/M22-bulletin/style.css
+++ b/modules/M22-bulletin/style.css
@@ -520,4 +520,46 @@ body {
background: black;
border-color: black;
}
+}
+/* ========== 骨架屏样式 ========== */
+.skeleton {
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
+ background-size: 200% 100%;
+ animation: skeleton-loading 1.5s infinite;
+ border-radius: 4px;
+}
+
+@keyframes skeleton-loading {
+ 0% { background-position: 200% 0; }
+ 100% { background-position: -200% 0; }
+}
+
+.skeleton-text {
+ height: 16px;
+ margin-bottom: 8px;
+ width: 80%;
+}
+
+.skeleton-text.short {
+ width: 40%;
+}
+
+.skeleton-title {
+ height: 24px;
+ margin-bottom: 12px;
+ width: 60%;
+}
+
+.skeleton-card {
+ padding: 16px;
+ border-radius: 8px;
+ background: var(--card-bg, #fff);
+ margin-bottom: 12px;
+}
+
+/* 暗色主题适配 */
+[data-theme="dark"] .skeleton {
+ background: linear-gradient(90deg, #2a2a2a 25%, #333 50%, #2a2a2a 75%);
+ background-size: 200% 100%;
+ animation: skeleton-loading 1.5s infinite;
}
\ No newline at end of file