From b450cd3a3d806fa82bdfcbb291c27960cc89a593 Mon Sep 17 00:00:00 2001 From: zhizhi200271 Date: Thu, 12 Mar 2026 20:21:48 +0800 Subject: [PATCH] =?UTF-8?q?DEV-004:=20M-DEVBOARD=E7=8E=AF=E8=8A=825=20?= =?UTF-8?q?=E7=9C=9F=E5=AE=9EAPI=E5=AF=B9=E6=8E=A5+=E7=A6=BB=E7=BA=BF?= =?UTF-8?q?=E9=99=8D=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/devboard/api-init.js | 81 +++++++++++++++ modules/devboard/api-init.js.bak | 75 ++++++++++++++ modules/devboard/api.js | 173 +++++++++++++++++++------------ modules/devboard/index.html | 70 +++++-------- modules/devboard/index.html.bak | 53 ++++++++++ 5 files changed, 343 insertions(+), 109 deletions(-) create mode 100644 modules/devboard/api-init.js create mode 100644 modules/devboard/api-init.js.bak create mode 100644 modules/devboard/index.html.bak diff --git a/modules/devboard/api-init.js b/modules/devboard/api-init.js new file mode 100644 index 00000000..c49bc311 --- /dev/null +++ b/modules/devboard/api-init.js @@ -0,0 +1,81 @@ +// ===================================== +// HoloLake DevBoard · API Init v1.0 +// 负责:异步加载 + 加载动画 + 自动刷新 +// 必须在 api.js 和 main.js 之后加载 +// ===================================== + +(function(){ + // -------------- 加载动画 -------------- + function showLoader(show) { + var el = document.getElementById('devboard-loader'); + if (!el && show) { + el = document.createElement('div'); + el.id = 'devboard-loader'; + el.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(10,14,23,0.92);display:flex;align-items:center;justify-content:center;z-index:10000'; + el.innerHTML = '
数据加载中...
'; + var style = document.createElement('style'); + style.textContent = '@keyframes dbspin{to{transform:rotate(360deg)}}'; + document.head.appendChild(style); + document.body.appendChild(el); + } + if (el) el.style.display = show ? 'flex' : 'none'; + } + + // -------------- 数据加载+渲染 -------------- + async function loadAndRender() { + try { + var developers = await apiGetDevelopers(); + var stats = await apiGetStats(); + var leaderboard = await apiGetLeaderboard(); + + // 使用 components.js 里实际存在的函数 + if (typeof renderDeveloperCards === 'function') { + renderDeveloperCards(developers); + } + + if (typeof renderRanking === 'function') { + renderRanking(leaderboard); + } + + // stats 暂时用控制台输出,等找到实际渲染函数再改 + console.log('[DevBoard] 统计数据:', stats); + + console.log('[DevBoard] 数据加载完成,共 ' + developers.length + ' 位开发者'); + } catch (err) { + console.error('[DevBoard] 数据加载异常: ', err); + } + } + + // -------------- 初始化入口 -------------- + async function init() { + showLoader(true); + + // 检测API状态 + var online = await apiHealthCheck(); + showApiStatus(online); + console.log('[DevBoard] API状态: ' + (online ? '在线(实时)' : '离线(降级)')); + + // 加载+渲染 + await loadAndRender(); + showLoader(false); + + // 自动刷新(仅API在线时) + if (online && API.REFRESH_MS > 0) { + console.log('[DevBoard] 自动刷新已启动,间隔 ' + (API.REFRESH_MS/1000) + ' 秒'); + setInterval(async function() { + try { + await loadAndRender(); + } catch (e) { + console.warn('[DevBoard] 自动刷新失败: ', e.message); + } + }, API.REFRESH_MS); + } + } + + // 等待DOM和所有脚本加载完成后启动 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/modules/devboard/api-init.js.bak b/modules/devboard/api-init.js.bak new file mode 100644 index 00000000..31ef43dc --- /dev/null +++ b/modules/devboard/api-init.js.bak @@ -0,0 +1,75 @@ +// ===================================== +// HoloLake DevBoard · API Init v1.0 +// 负责:异步加载 + 加载动画 + 自动刷新 +// 必须在 api.js 和 main.js 之后加载 +// ===================================== + +(function(){ + // -------------- 加载动画 -------------- + function showLoader(show) { + var el = document.getElementById('devboard-loader'); + if (!el && show) { + el = document.createElement('div'); + el.id = 'devboard-loader'; + el.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(10,14,23,0.92);display:flex;align-items:center;justify-content:center;z-index:10000'; + el.innerHTML = '
数据加载中...
'; + var style = document.createElement('style'); + style.textContent = '@keyframes dbspin{to{transform:rotate(360deg)}}'; + document.head.appendChild(style); + document.body.appendChild(el); + } + if (el) el.style.display = show ? 'flex' : 'none'; + } + + // -------------- 数据加载+渲染 -------------- + async function loadAndRender() { + try { + var developers = await apiGetDevelopers(); + var stats = await apiGetStats(); + var leaderboard = await apiGetLeaderboard(); + + // 用现有渲染函数(main.js / components.js 定义的) + if (typeof renderDeveloperCards === 'function') renderDeveloperCards(developers); + if (typeof renderStats === 'function') renderStats(stats); + if (typeof renderLeaderboard === 'function') renderLeaderboard(leaderboard); + if (typeof renderRankings === 'function') renderRankings(leaderboard); + + console.log('[DevBoard] 数据加载完成,共 ' + developers.length + ' 位开发者'); + } catch (err) { + console.error('[DevBoard] 数据加载异常: ', err); + } + } + + // -------------- 初始化入口 -------------- + async function init() { + showLoader(true); + + // 检测API状态 + var online = await apiHealthCheck(); + showApiStatus(online); + console.log('[DevBoard] API状态: ' + (online ? '在线(实时)' : '离线(降级)')); + + // 加载+渲染 + await loadAndRender(); + showLoader(false); + + // 自动刷新(仅API在线时) + if (online && API.REFRESH_MS > 0) { + console.log('[DevBoard] 自动刷新已启动,间隔 ' + (API.REFRESH_MS/1000) + ' 秒'); + setInterval(async function() { + try { + await loadAndRender(); + } catch (e) { + console.warn('[DevBoard] 自动刷新失败: ', e.message); + } + }, API.REFRESH_MS); + } + } + + // 等待DOM和所有脚本加载完成后启动 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/modules/devboard/api.js b/modules/devboard/api.js index 87481710..fd928078 100644 --- a/modules/devboard/api.js +++ b/modules/devboard/api.js @@ -1,79 +1,122 @@ -// api.js - API配置和模拟数据切换 +// ====================== +// HoloLake DevBoard · API Client v2.0 +// DEV-004 之之 · 环节5 · 真实API对接 +// 协议:SYSLOG-v4.0 +// ====================== -const CONFIG = { - // API基础地址(页页的接口) - BASE_URL: 'https://guanghulab.com/api', - - // 是否使用真实API(false = 使用模拟数据) - USE_REAL_API: false, - - // API端点 - endpoints: { - teamStatus: '/dashboard/team-status', - developerDetail: '/dashboard/dev/', // + id - modules: '/dashboard/modules' - } +const API = { + // ====== 配置区 ====== + // 页页的后端API地址(如果地址不对,问知秋确认) + BASE_URL: 'https://guanghulab.com/api/devboard', + TIMEOUT: 8000, + FALLBACK: true, // API挂了自动切换模拟数据 + REFRESH_MS: 30000, // 30秒自动刷新 + _cache: {} }; -// 检查API是否可用 -async function checkApiAvailability() { - if (!CONFIG.USE_REAL_API) return false; - +// ====== 通用请求(带超时+缓存+降级) ===================== +async function apiFetch(path) { + const url = API.BASE_URL + path; + const ctrl = new AbortController(); + const timer = setTimeout(() => ctrl.abort(), API.TIMEOUT); try { - const response = await fetch(`${CONFIG.BASE_URL}/health`, { - method: 'HEAD', - mode: 'cors', - cache: 'no-cache' + const res = await fetch(url, { + signal: ctrl.signal, + headers: { 'Accept': 'application/json' } }); - return response.ok; - } catch (error) { - console.log('API不可用,使用模拟数据'); + clearTimeout(timer); + if (!res.ok) throw new Error('HTTP ' + res.status); + const data = await res.json(); + API._cache[path] = { data: data, time: Date.now() }; + return data; + } catch (err) { + clearTimeout(timer); + console.warn(' 🚨 [DevBoard API] ' + path + ' 失败: ', err.message); + if (API._cache[path]) { + console.info(' 💾 [DevBoard API] 使用缓存'); + return API._cache[path].data; + } + if (API.FALLBACK) { + console.info(' 🛡️ [DevBoard API] 降级到模拟数据'); + return getMockData(path); + } + throw err; + } +} + +// ========== 公开接口 ========== +async function apiGetDevelopers() { + return apiFetch('/developers'); +} + +async function apiGetDeveloperDetail(devId) { + return apiFetch('/developers/' + devId); +} + +async function apiGetStats() { + return apiFetch('/stats'); +} + +async function apiGetLeaderboard() { + return apiFetch('/leaderboard'); +} + +// ========== 健康检查 ========== +async function apiHealthCheck() { + try { + const ctrl = new AbortController(); + const t = setTimeout(() => ctrl.abort(), 3000); + const res = await fetch(API.BASE_URL + '/health', { signal: ctrl.signal }); + clearTimeout(t); + return res.ok; + } catch (e) { return false; } } -// 获取团队状态 -async function getTeamStatus() { - if (CONFIG.USE_REAL_API) { - try { - const response = await fetch(`${CONFIG.BASE_URL}${CONFIG.endpoints.teamStatus}`); - if (response.ok) { - const data = await response.json(); - return data; - } - } catch (error) { - console.warn('API请求失败,使用模拟数据'); - } +// ========== 模拟数据(降级保护) ========== +function getMockData(path) { + var devs = [ + { dev_id:'DEV-001', name:'页页', module:'BC-集成', status:'进行中', streak:5, el:'EL-6', pca:{EXE:75,TEC:60,SYS:70,COL:80,INI:65} }, + { dev_id:'DEV-002', name:'肥猫', module:'M-STATUS', status:'进行中', streak:9, el:'EL-5', pca:{EXE:72,TEC:40,SYS:68,COL:82,INI:70} }, + { dev_id:'DEV-003', name:'燕樊', module:'M-MEMORY', status:'进行中', streak:7, el:'EL-5', pca:{EXE:70,TEC:55,SYS:65,COL:75,INI:68} }, + { dev_id:'DEV-004', name:'之之', module:'M-DEVBOARD', status:'进行中', streak:14, el:'EL-8', pca:{EXE:90,TEC:55,SYS:80,COL:85,INI:88} }, + { dev_id:'DEV-009', name:'花尔', module:'M20', status:'进行中', streak:4, el:'EL-8', pca:{EXE:65,TEC:45,SYS:55,COL:70,INI:60} }, + { dev_id:'DEV-010', name:'桔子', module:'M-CHANNEL', status:'进行中', streak:14, el:'EL-6', pca:{EXE:85,TEC:50,SYS:75,COL:80,INI:82} }, + { dev_id:'DEV-011', name:'匆匆那年',module:'M16', status:'等待中', streak:1, el:'EL-3', pca:{EXE:50,TEC:30,SYS:40,COL:55,INI:45} }, + { dev_id:'DEV-012', name:'Awen', module:'M22', status:'进行中', streak:13, el:'EL-6', pca:{EXE:88,TEC:45,SYS:78,COL:90,INI:80} }, + { dev_id:'DEV-013', name:'小兴', module:'M-AUTH', status:'进行中', streak:1, el:'EL-3', pca:{EXE:55,TEC:25,SYS:35,COL:60,INI:50} } + ]; + var stats = { total_developers:10, active:8, modules:12, code_lines:32000, longest_streak:14, avg_streak:7.5 }; + + if (path === '/developers') return devs; + if (path.indexOf('/developers/') === 0) { + var id = path.split('/').pop(); + return devs.find(function(d){ return d.dev_id === id; }) || devs[0]; } - - // 返回模拟数据 - return { - developers: window.getDevelopers ? window.getDevelopers() : [] - }; + if (path === '/stats') return stats; + if (path === '/leaderboard') return devs.slice().sort(function(a,b){ return b.streak - a.streak; }); + return []; } -// 获取开发者详情 -async getDeveloperDetail(devId) { - if (CONFIG.USE_REAL_API) { - try { - const response = await fetch(`${CONFIG.BASE_URL}${CONFIG.endpoints.developerDetail}${devId}`); - if (response.ok) { - const data = await response.json(); - return data; - } - } catch (error) { - console.warn('API请求失败,使用模拟数据'); - } +// ========== API状态指示器 ========== +function showApiStatus(online) { + var el = document.getElementById('api-status'); + if (!el) { + el = document.createElement('div'); + el.id = 'api-status'; + el.style.cssText = 'position:fixed;bottom:12px;right:12px;padding:6px 14px;border-radius:20px;font-size:12px;font-weight:600;z-index:9999;transition:all 0.3s;'; + document.body.appendChild(el); + } + if (online) { + el.textContent = '🟢 实时数据'; + el.style.backgroundColor = 'rgba(52,211,153,0.15)'; + el.style.color = '#34d399'; + el.style.border = '1px solid rgba(52,211,153,0.3)'; + } else { + el.textContent = '🟡 模拟数据'; + el.style.backgroundColor = 'rgba(251,191,36,0.15)'; + el.style.color = '#fbbf24'; + el.style.border = '1px solid rgba(251,191,36,0.3)'; } - - // 返回模拟数据 - return window.getDeveloperById ? window.getDeveloperById(devId) : null; } - -// 导出配置 -window.API = { - CONFIG, - checkApiAvailability, - getTeamStatus, - getDeveloperDetail -}; diff --git a/modules/devboard/index.html b/modules/devboard/index.html index f671be0a..eb5e1f60 100644 --- a/modules/devboard/index.html +++ b/modules/devboard/index.html @@ -1,53 +1,35 @@ - + - - 开发者实时看板 · 环节0~4 · 之之 - - - + + 开发者实时看板 · HoloLake + - -
-
-

⚡ 开发者实时看板 ⚡

-

DEV-004 · 之之 · 十三连胜追平桔子 · EL-6

-
- - -
- -
- - - -
-
- - -
- - -
-
-

团队PCA雷达

- -
-
-

连胜排行榜

-
-
-
-
+

🚀 HoloLake 开发者实时看板

+
+
+
- - - - - - + + + + + + + diff --git a/modules/devboard/index.html.bak b/modules/devboard/index.html.bak new file mode 100644 index 00000000..f671be0a --- /dev/null +++ b/modules/devboard/index.html.bak @@ -0,0 +1,53 @@ + + + + + + 开发者实时看板 · 环节0~4 · 之之 + + + + + + +
+
+

⚡ 开发者实时看板 ⚡

+

DEV-004 · 之之 · 十三连胜追平桔子 · EL-6

+
+ + +
+ +
+ + + +
+
+ + +
+ + +
+
+

团队PCA雷达

+ +
+
+

连胜排行榜

+
+
+
+
+ + + + + + + + + +