refactor: Context 与 Provider 合并 — 消除 contexts/ 目录,一文件一状态单元
- ThemeContext + useTheme → ThemeProvider.tsx(原 hooks/use-theme.ts 删除) - AppContext + useAppContext → AppProvider.tsx(原 contexts/app-context.ts 删除) - SettingsContext + useSettings → SettingsProvider.tsx(原 contexts/settings-context.ts 删除) - HomeContext + useHomeContext → HomeContent.tsx(原 contexts/home-context.ts 删除) - 删除空的 contexts/ 目录,20+ 导入路径同步更新 - TypeScript 编译通过(npx tsc --noEmit 零错误) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
import { useAppContext } from './contexts/app-context';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import { LoginPage } from './pages/login';
|
||||
import { SettingsPage } from './pages/settings';
|
||||
import { on, off, EVENTS } from './utils/event-bus';
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
// ============================================================
|
||||
// AppProvider — 应用状态 Provider 组件
|
||||
// AppProvider — 全局应用状态 Context + Provider + Hook
|
||||
//
|
||||
// 导出:
|
||||
// AppContext, useAppContext() — Context + Consumer Hook
|
||||
// AppContextValue — 类型
|
||||
// AppProvider — Provider 组件
|
||||
// ============================================================
|
||||
|
||||
import { useState, useMemo, useCallback, useEffect, type ReactNode } from 'react';
|
||||
import { createContext, useContext, useState, useMemo, useCallback, useEffect, type ReactNode } from 'react';
|
||||
import type { Platform, Edition } from '@shared/types';
|
||||
|
||||
import { getPlatform, isDev } from '@/utils/platform';
|
||||
import { safeIpcOn, safeIpcOff } from '@/utils/ipc';
|
||||
import { AppContext } from '@/contexts/app-context';
|
||||
import type { AppContextValue } from '@/contexts/app-context';
|
||||
import type { LoginResponseBody } from '@/services/modules';
|
||||
import { setToken, clearToken, initTokenStore } from '@/services/request';
|
||||
import { emit, EVENTS } from '@/utils/event-bus';
|
||||
@@ -23,6 +26,39 @@ import {
|
||||
initRefreshTokenStore,
|
||||
} from '@/services/auth-token';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface AppContextValue {
|
||||
/** 当前操作系统平台 */
|
||||
platform: Platform;
|
||||
/** 产品版本(个人版 / 企业版) */
|
||||
edition: Edition;
|
||||
/** 用户是否已登录 */
|
||||
isLoggedIn: boolean;
|
||||
/** 当前登录用户信息(未登录时为 null,结构与 LoginResponseBody 一致) */
|
||||
user: LoginResponseBody | null;
|
||||
/** 登录:保存 Token 并更新登录态(auth 为登录接口返回值,异步加密存储) */
|
||||
login: (auth: LoginResponseBody) => Promise<void>;
|
||||
/** 退出登录:清除 Token 和用户状态 */
|
||||
logout: () => void;
|
||||
/** 是否为开发环境 */
|
||||
isDevMode: boolean;
|
||||
}
|
||||
|
||||
export const AppContext = createContext<AppContextValue | null>(null);
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useAppContext(): AppContextValue {
|
||||
const ctx = useContext(AppContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppContext() 必须在 <AppProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// ---------- Provider 组件 ----------
|
||||
|
||||
interface AppProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,99 @@
|
||||
// ============================================================
|
||||
// SettingsProvider — 设置状态 Provider 组件
|
||||
// SettingsProvider — 设置项集中状态管理 Context + Provider + Hook
|
||||
//
|
||||
// 职责:
|
||||
// - 初始化时从 localStorage 读取已存储的设置项
|
||||
// - 提供 SettingsContext 给所有子组件
|
||||
// - 写操作同步更新 state + localStorage
|
||||
// - 存储路径的集中管理(read/write/clear)
|
||||
// - 持久化到 localStorage,遵循 ele-heixiu-* 键名规范
|
||||
// - 全局组件通过 useSettings() 消费
|
||||
// ============================================================
|
||||
|
||||
import { useState, useCallback, type ReactNode } from 'react';
|
||||
import {
|
||||
SettingsContext,
|
||||
readStoredOutputPath,
|
||||
writeStoredOutputPath,
|
||||
clearStoredOutputPath,
|
||||
readStoredTeamRepoPath,
|
||||
writeStoredTeamRepoPath,
|
||||
clearStoredTeamRepoPath,
|
||||
} from '@/contexts/settings-context';
|
||||
import type { SettingsContextValue } from '@/contexts/settings-context';
|
||||
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
|
||||
|
||||
// ---------- localStorage 键 ----------
|
||||
|
||||
const OUTPUT_PATH_KEY = 'ele-heixiu-output-path';
|
||||
const TEAM_REPO_PATH_KEY = 'ele-heixiu-team-repo-path';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface SettingsContextValue {
|
||||
/** 大模型产物存储仓库路径 */
|
||||
outputPath: string;
|
||||
/** 团队创作存储仓库路径(企业版) */
|
||||
teamRepoPath: string;
|
||||
/** 设置产物输出路径 */
|
||||
setOutputPath: (path: string) => void;
|
||||
/** 设置团队创作仓库路径 */
|
||||
setTeamRepoPath: (path: string) => void;
|
||||
/** 清除产物输出路径 */
|
||||
clearOutputPath: () => void;
|
||||
/** 清除团队创作仓库路径 */
|
||||
clearTeamRepoPath: () => void;
|
||||
}
|
||||
|
||||
export const SettingsContext = createContext<SettingsContextValue | null>(null);
|
||||
|
||||
// ---------- 工具函数(localStorage 读写)----------
|
||||
|
||||
export function readStoredOutputPath(): string {
|
||||
try {
|
||||
return localStorage.getItem(OUTPUT_PATH_KEY) || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function writeStoredOutputPath(path: string): void {
|
||||
try {
|
||||
localStorage.setItem(OUTPUT_PATH_KEY, path);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function clearStoredOutputPath(): void {
|
||||
try {
|
||||
localStorage.removeItem(OUTPUT_PATH_KEY);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function readStoredTeamRepoPath(): string {
|
||||
try {
|
||||
return localStorage.getItem(TEAM_REPO_PATH_KEY) || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function writeStoredTeamRepoPath(path: string): void {
|
||||
try {
|
||||
localStorage.setItem(TEAM_REPO_PATH_KEY, path);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function clearStoredTeamRepoPath(): void {
|
||||
try {
|
||||
localStorage.removeItem(TEAM_REPO_PATH_KEY);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useSettings(): SettingsContextValue {
|
||||
const ctx = useContext(SettingsContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useSettings() 必须在 <SettingsProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// ---------- Provider 组件 ----------
|
||||
|
||||
interface SettingsProviderProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ============================================================
|
||||
// ThemeProvider — 主题 Provider 组件
|
||||
// ThemeProvider — 主题 Context + Provider + Hook + 工具函数
|
||||
//
|
||||
// 桌面端(Electron)使用 View Transitions API 驱动主题切换:
|
||||
// GPU 合成器截取旧/新两帧做一次 cross-fade,替代 80+ 个
|
||||
@@ -7,20 +7,70 @@
|
||||
// 浏览器不支持时回退到 CSS transition 方案。
|
||||
// ============================================================
|
||||
|
||||
import { useState, useEffect, useLayoutEffect, useCallback, type ReactNode } from 'react';
|
||||
import { createContext, useContext, useState, useEffect, useLayoutEffect, useCallback, type ReactNode } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import zhCN from 'antd/locale/zh_CN';
|
||||
|
||||
import { getThemeConfig } from '../theme/antd-theme';
|
||||
import {
|
||||
ThemeContext,
|
||||
readStoredTheme,
|
||||
writeStoredTheme,
|
||||
applyHtmlTheme,
|
||||
STORAGE_KEY,
|
||||
} from '../hooks/use-theme';
|
||||
import type { ThemeContextValue } from '../hooks/use-theme';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface ThemeContextValue {
|
||||
isDark: boolean;
|
||||
toggleTheme: () => void;
|
||||
setLight: () => void;
|
||||
setDark: () => void;
|
||||
}
|
||||
|
||||
export const ThemeContext = createContext<ThemeContextValue | null>(null);
|
||||
|
||||
// ---------- localStorage 工具函数 ----------
|
||||
|
||||
export const STORAGE_KEY = 'ele-heixiu-theme';
|
||||
|
||||
export function readStoredTheme(): boolean {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored === 'dark') return true;
|
||||
if (stored === 'light') return false;
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
if (typeof window !== 'undefined' && window.matchMedia) {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function writeStoredTheme(isDark: boolean): void {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, isDark ? 'dark' : 'light');
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function applyHtmlTheme(isDark: boolean): void {
|
||||
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useTheme(): ThemeContextValue {
|
||||
const ctx = useContext(ThemeContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useTheme() 必须在 <ThemeProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// ---------- Provider 组件 ----------
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// ============================================================
|
||||
|
||||
import { useState, useRef, useCallback, useEffect, type ReactNode } from 'react';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
|
||||
// ---------- 类型 ----------
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Modal, Typography } from 'antd';
|
||||
import { FileTextOutlined } from '@ant-design/icons';
|
||||
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
|
||||
// Vite ?raw 导入:将 .txt 内容作为字符串直接内联
|
||||
import serviceAgreement from '@/assets/legal/用户服务协议.txt?raw';
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
import {useState, useCallback, useMemo} from 'react';
|
||||
import {App} from 'antd';
|
||||
|
||||
import {useAppContext} from '@/contexts/app-context';
|
||||
import {useTheme} from '@/hooks/use-theme';
|
||||
import {useAppContext} from '@/components/AppProvider';
|
||||
import {useTheme} from '@/components/ThemeProvider';
|
||||
import {emit, EVENTS} from '@/utils/event-bus.ts';
|
||||
import {NavItem} from './NavItem';
|
||||
import {AnnouncementDrawer} from './AnnouncementDrawer';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// 布局:图标(SVG)在上,文字在下
|
||||
// ============================================================
|
||||
|
||||
import { useTheme } from '@/hooks/use-theme.ts';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
import type { NavItemConfig } from './types';
|
||||
|
||||
interface NavItemProps {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// ============================================================
|
||||
// AppContext — 全局应用状态
|
||||
// ============================================================
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
import type { Platform, Edition } from '@shared/types';
|
||||
import type { LoginResponseBody } from '@/services/modules';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface AppContextValue {
|
||||
/** 当前操作系统平台 */
|
||||
platform: Platform;
|
||||
/** 产品版本(个人版 / 企业版) */
|
||||
edition: Edition;
|
||||
/** 用户是否已登录 */
|
||||
isLoggedIn: boolean;
|
||||
/** 当前登录用户信息(未登录时为 null,结构与 LoginResponseBody 一致) */
|
||||
user: LoginResponseBody | null;
|
||||
/** 登录:保存 Token 并更新登录态(auth 为登录接口返回值,异步加密存储) */
|
||||
login: (auth: LoginResponseBody) => Promise<void>;
|
||||
/** 退出登录:清除 Token 和用户状态 */
|
||||
logout: () => void;
|
||||
/** 是否为开发环境 */
|
||||
isDevMode: boolean;
|
||||
}
|
||||
|
||||
export const AppContext = createContext<AppContextValue | null>(null);
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useAppContext(): AppContextValue {
|
||||
const ctx = useContext(AppContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAppContext() 必须在 <AppProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// ============================================================
|
||||
// HomeContext — 首页状态管理
|
||||
// 管理:选中模型、任务分页、选中任务记录、刷新信号
|
||||
// ============================================================
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
import type { ModelsListResponseBody } from '@/services/modules/models';
|
||||
import type { TaskInfoItemResponseBody } from '@/services/modules/task';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface HomeContextValue {
|
||||
/** 当前选中的模型 */
|
||||
selectedModel: ModelsListResponseBody | null;
|
||||
setSelectedModel: (model: ModelsListResponseBody | null) => void;
|
||||
|
||||
/** 任务记录分页 — 当前页码 */
|
||||
taskPage: number;
|
||||
/** 任务记录分页 — 每页条数 */
|
||||
taskPageSize: number;
|
||||
setTaskPage: (page: number) => void;
|
||||
setTaskPageSize: (size: number) => void;
|
||||
|
||||
/** 当前选中的任务记录(用于右侧预览) */
|
||||
selectedTask: TaskInfoItemResponseBody | null;
|
||||
setSelectedTask: (task: TaskInfoItemResponseBody | null) => void;
|
||||
|
||||
/** 侧边栏折叠状态 */
|
||||
isLeftCollapsed: boolean;
|
||||
toggleLeftCollapsed: () => void;
|
||||
}
|
||||
|
||||
export const HomeContext = createContext<HomeContextValue | null>(null);
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useHomeContext(): HomeContextValue {
|
||||
const ctx = useContext(HomeContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useHomeContext() 必须在 <HomeProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// ============================================================
|
||||
// SettingsContext — 设置项集中状态管理(类似 Vue Pinia store)
|
||||
//
|
||||
// 职责:
|
||||
// - 存储路径的集中管理(read/write/clear)
|
||||
// - 持久化到 localStorage,遵循 ele-heixiu-* 键名规范
|
||||
// - 全局组件通过 useSettings() 消费
|
||||
// ============================================================
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
// ---------- localStorage 键 ----------
|
||||
|
||||
const OUTPUT_PATH_KEY = 'ele-heixiu-output-path';
|
||||
const TEAM_REPO_PATH_KEY = 'ele-heixiu-team-repo-path';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface SettingsContextValue {
|
||||
/** 大模型产物存储仓库路径 */
|
||||
outputPath: string;
|
||||
/** 团队创作存储仓库路径(企业版) */
|
||||
teamRepoPath: string;
|
||||
/** 设置产物输出路径 */
|
||||
setOutputPath: (path: string) => void;
|
||||
/** 设置团队创作仓库路径 */
|
||||
setTeamRepoPath: (path: string) => void;
|
||||
/** 清除产物输出路径 */
|
||||
clearOutputPath: () => void;
|
||||
/** 清除团队创作仓库路径 */
|
||||
clearTeamRepoPath: () => void;
|
||||
}
|
||||
|
||||
export const SettingsContext = createContext<SettingsContextValue | null>(null);
|
||||
|
||||
// ---------- 工具函数(localStorage 读写) ----------
|
||||
|
||||
export function readStoredOutputPath(): string {
|
||||
try {
|
||||
return localStorage.getItem(OUTPUT_PATH_KEY) || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function writeStoredOutputPath(path: string): void {
|
||||
try {
|
||||
localStorage.setItem(OUTPUT_PATH_KEY, path);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function clearStoredOutputPath(): void {
|
||||
try {
|
||||
localStorage.removeItem(OUTPUT_PATH_KEY);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function readStoredTeamRepoPath(): string {
|
||||
try {
|
||||
return localStorage.getItem(TEAM_REPO_PATH_KEY) || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function writeStoredTeamRepoPath(path: string): void {
|
||||
try {
|
||||
localStorage.setItem(TEAM_REPO_PATH_KEY, path);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function clearStoredTeamRepoPath(): void {
|
||||
try {
|
||||
localStorage.removeItem(TEAM_REPO_PATH_KEY);
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useSettings(): SettingsContextValue {
|
||||
const ctx = useContext(SettingsContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useSettings() 必须在 <SettingsProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
// ============================================================
|
||||
|
||||
import {useMemo} from 'react';
|
||||
import {useAppContext} from '@/contexts/app-context';
|
||||
import {useAppContext} from '@/components/AppProvider';
|
||||
import {
|
||||
TaskAPI,
|
||||
ModelAPI,
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// ============================================================
|
||||
// useTheme — 亮/暗主题切换 Hook
|
||||
// ============================================================
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface ThemeContextValue {
|
||||
isDark: boolean;
|
||||
toggleTheme: () => void;
|
||||
setLight: () => void;
|
||||
setDark: () => void;
|
||||
}
|
||||
|
||||
export const ThemeContext = createContext<ThemeContextValue | null>(null);
|
||||
|
||||
// ---------- 工具函数 ----------
|
||||
|
||||
export const STORAGE_KEY = 'ele-heixiu-theme';
|
||||
|
||||
export function readStoredTheme(): boolean {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored === 'dark') return true;
|
||||
if (stored === 'light') return false;
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
if (typeof window !== 'undefined' && window.matchMedia) {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function writeStoredTheme(isDark: boolean): void {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, isDark ? 'dark' : 'light');
|
||||
} catch {
|
||||
/* 静默忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
export function applyHtmlTheme(isDark: boolean): void {
|
||||
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useTheme(): ThemeContextValue {
|
||||
const ctx = useContext(ThemeContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useTheme() 必须在 <ThemeProvider> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
// ============================================================
|
||||
// HomeContent — 首页三列正文布局 + HomeProvider
|
||||
// HomeContent — 首页三列正文布局 + HomeProvider(Context + Provider + Hook)
|
||||
// 可拖拽调整三栏宽度,所有数据请求在登录后发送
|
||||
// ============================================================
|
||||
|
||||
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
||||
import { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
||||
import { theme as antTheme, Empty } from 'antd';
|
||||
|
||||
import { HomeContext } from '@/contexts/home-context';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import type { ModelsListResponseBody, TaskInfoItemResponseBody } from '@/services/modules';
|
||||
import { LeftPanel } from './components/LeftPanel';
|
||||
import { CenterPanel } from './components/CenterPanel';
|
||||
@@ -17,6 +16,38 @@ import { Typography } from 'antd';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
// ---------- Context 类型 ----------
|
||||
|
||||
export interface HomeContextValue {
|
||||
/** 当前选中的模型 */
|
||||
selectedModel: ModelsListResponseBody | null;
|
||||
setSelectedModel: (model: ModelsListResponseBody | null) => void;
|
||||
/** 任务记录分页 — 当前页码 */
|
||||
taskPage: number;
|
||||
/** 任务记录分页 — 每页条数 */
|
||||
taskPageSize: number;
|
||||
setTaskPage: (page: number) => void;
|
||||
setTaskPageSize: (size: number) => void;
|
||||
/** 当前选中的任务记录(用于右侧预览) */
|
||||
selectedTask: TaskInfoItemResponseBody | null;
|
||||
setSelectedTask: (task: TaskInfoItemResponseBody | null) => void;
|
||||
/** 侧边栏折叠状态 */
|
||||
isLeftCollapsed: boolean;
|
||||
toggleLeftCollapsed: () => void;
|
||||
}
|
||||
|
||||
export const HomeContext = createContext<HomeContextValue | null>(null);
|
||||
|
||||
// ---------- Consumer Hook ----------
|
||||
|
||||
export function useHomeContext(): HomeContextValue {
|
||||
const ctx = useContext(HomeContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useHomeContext() 必须在 <HomeContent> 内部调用');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// ---------- 常量 ----------
|
||||
|
||||
/** 左列最小/默认宽度 */
|
||||
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import type { UploadFile } from 'antd/es/upload';
|
||||
|
||||
import { useHomeContext } from '@/contexts/home-context';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import { useHomeContext } from '@/pages/home/HomeContent';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import { useSubmitTask } from '@/hooks/use-api';
|
||||
import { emit, EVENTS } from '@/utils/event-bus';
|
||||
import type { ModelsListResponseBody, TaskInfoItemResponseBody } from '@/services/modules';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Tabs, Spin, Empty, Tag, Typography, Button, Result, message, theme as a
|
||||
import { AppstoreOutlined } from '@ant-design/icons';
|
||||
|
||||
import { useModelList } from '@/hooks/use-api';
|
||||
import { useHomeContext } from '@/contexts/home-context';
|
||||
import { useHomeContext } from '@/pages/home/HomeContent';
|
||||
import { resolveCategoryLabel, MODEL_CATEGORIES } from '@/services/modules';
|
||||
import type { ModelsListResponseBody, ModelCategoryStringEnum } from '@/services/modules';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
CloseCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { useHomeContext } from '@/contexts/home-context';
|
||||
import { useHomeContext } from '@/pages/home/HomeContent';
|
||||
import { useTaskDetail } from '@/hooks/use-api';
|
||||
import type { TaskStatusString } from '@/services/modules';
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ import type { FilterValue, SorterResult } from 'antd/es/table/interface';
|
||||
import { HistoryOutlined, SearchOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
|
||||
import { useTaskList, useModelList } from '@/hooks/use-api';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import { useHomeContext } from '@/contexts/home-context';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import { useHomeContext } from '@/pages/home/HomeContent';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
import { on, off, EVENTS } from '@/utils/event-bus';
|
||||
import type {
|
||||
TaskInfoItemResponseBody,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useState, useCallback } from 'react';
|
||||
import { Modal, Input, Button, Typography, App } from 'antd';
|
||||
import { LockOutlined, KeyOutlined, SafetyOutlined } from '@ant-design/icons';
|
||||
|
||||
import { useTheme } from '@/hooks/use-theme.ts';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
import { AuthAPI } from '@/services/modules';
|
||||
import { validatePassword, validateConfirmPassword } from '../config/validators';
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
KeyOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { useTheme } from '@/hooks/use-theme.ts';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
import { AuthAPI } from '@/services/modules';
|
||||
import {
|
||||
validatePhone,
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Modal, Tabs, Typography, App } from 'antd';
|
||||
|
||||
import { useAppContext } from '@/contexts/app-context.ts';
|
||||
import { useTheme } from '@/hooks/use-theme.ts';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
import { LoginForm } from './components/LoginForm';
|
||||
import { RegisterForm } from './components/RegisterForm';
|
||||
import { ForgotPasswordModal } from './components/ForgotPasswordModal';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Modal } from 'antd';
|
||||
import { SettingOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import { isMacOS } from '@/utils/platform';
|
||||
import { ThemeSetting } from './blocks/ThemeSetting';
|
||||
import { StoragePathSetting } from './blocks/StoragePathSetting';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useState, useCallback } from 'react';
|
||||
import { Card, Button, Input, Typography, Space, App } from 'antd';
|
||||
import { FolderOpenOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import type { Edition } from '@shared/types';
|
||||
import { useSettings } from '@/contexts/settings-context';
|
||||
import { useSettings } from '@/components/SettingsProvider';
|
||||
import { hasIpc, safeIpcInvoke } from '@/utils/ipc';
|
||||
import { BIDIRECTIONAL } from '@shared/constants/ipc-channels';
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
InfoCircleOutlined,
|
||||
EnvironmentOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import { useAppContext } from '@/components/AppProvider';
|
||||
import { getPlatformName } from '@/utils/platform';
|
||||
import { APP_VERSION } from '@shared/constants/version';
|
||||
import { getEditionName } from '@shared/constants/app';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { Card, Segmented, Typography } from 'antd';
|
||||
import { BulbOutlined, BulbFilled } from '@ant-design/icons';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { useTheme } from '@/components/ThemeProvider';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user