/** * 文件职责:全局状态上下文,集中维护管理端共享状态与 reducer 分发逻辑。 */ const { createContext, useContext, useReducer } = React; // 管理端的全局状态树;由于项目未使用打包器,这里采用单文件 context 统一收口共享状态。 const initialState = { // 当前主内容区展示的视图 key。 currentView: 'status', // 全局加载态,通常用于首屏或全量刷新。 loading: false, // 最近一次操作失败信息,会显示在页面顶部错误卡片中。 error: '', // 首页状态汇总数据,对应后端 /api/status。 status: null, // 已知会话列表,可来自 HTTP 首载或 WebSocket 推送。 sessions: [], // 当前生效的全局配置快照,对应后端 /api/config。 config: null, // 调度任务列表,对应后端 /api/jobs。 jobs: [], // 通知列表与元信息,对应后端 /api/notifications。 notifications: [], notificationsMeta: { unread_count: 0, last_sync_at: '', total_count: 0, }, // Markdown 文档浏览页的文件目录、当前文档与选中路径。 markdownFiles: [], markdownDocument: null, selectedMarkdownPath: '', // 会话详情页当前选中的会话 ID。 selectedSession: '', // 当前选中会话的 base / override / effective 详情。 sessionDetail: null, // 主题优先读取本地存储,确保刷新页面后仍能保持用户偏好。 theme: localStorage.getItem('theme') || 'light', }; function reducer(state, action) { // reducer 负责统一处理所有状态写入,避免各组件直接散写共享状态。 switch (action.type) { case 'TOGGLE_THEME': // 主题切换时同步写入 localStorage,保证跨页面刷新可恢复。 const newTheme = state.theme === 'light' ? 'dark' : 'light'; localStorage.setItem('theme', newTheme); return { ...state, theme: newTheme }; case 'SET_VIEW': return { ...state, currentView: action.payload }; case 'SET_LOADING': return { ...state, loading: action.payload }; case 'SET_ERROR': // 统一把 falsy 值收敛为空字符串,减少渲染层判断分支。 return { ...state, error: action.payload || '' }; case 'SET_STATUS': return { ...state, status: action.payload }; case 'SET_SESSIONS': return { ...state, sessions: action.payload || [] }; case 'SET_CONFIG': return { ...state, config: action.payload || null }; case 'SET_JOBS': return { ...state, jobs: action.payload || [] }; case 'SET_NOTIFICATIONS': return { ...state, notifications: action.payload || [] }; case 'SET_NOTIFICATIONS_META': return { ...state, notificationsMeta: action.payload || { unread_count: 0, last_sync_at: '', total_count: 0, }, }; case 'SET_MARKDOWN_FILES': return { ...state, markdownFiles: action.payload || [] }; case 'SET_MARKDOWN_DOCUMENT': return { ...state, markdownDocument: action.payload || null }; case 'SET_SELECTED_MARKDOWN_PATH': return { ...state, selectedMarkdownPath: action.payload || '' }; case 'SET_SELECTED_SESSION': return { ...state, selectedSession: action.payload || '' }; case 'SET_SESSION_DETAIL': return { ...state, sessionDetail: action.payload || null }; default: // 未识别 action 时保持原状态,避免误 dispatch 导致页面崩溃。 return state; } } // 初始值为 null,强制消费方必须在 Provider 内部使用,避免静默使用默认值。 const AppContext = createContext(null); function AppProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); return ( {children} ); } function useAppContext() { const ctx = useContext(AppContext); // 这里主动抛错,方便在开发阶段尽早发现 Provider 包裹缺失。 if (!ctx) throw new Error('useAppContext must be used within AppProvider'); return ctx; } // 由于当前前端采用多文件 UMD / Babel 直接挂载方案,因此通过 window 暴露给其他脚本使用。 window.AppProvider = AppProvider; window.useAppContext = useAppContext;