feat: 实现设置页面 + 路由系统 + JWT认证体系 + 公告优化 + 错误类型系统
## 新增功能 ### 设置页面(Router + Modal 叠加) - HashRouter 路由系统,Electron file:// 协议兼容 - 设置页以居中 Modal 叠加首页,仅 X 按钮关闭 - 主题切换 Segmented 控件(与首页 Switch 共享 Context) - 大模型产物存储仓库路径配置(原生文件夹选择对话框) - 企业版额外:团队创作存储仓库路径 - 系统信息展示(平台/版本/版本类型/环境) - 版本更新检查(状态机:idle→checking→no-update/available→downloading→downloaded→error) ### JWT 认证体系 - 双 Token 机制:access_token + refresh_token - Axios 拦截器 401 自动刷新 + 并发请求去重队列 - 启动时 refresh_token 静默续期(自动登录) - 记住密码:Base64 编码存储,表单自动回填 - setTokenRefreshHandler 解耦模式:request.ts 不知晓 auth ### 自定义错误类型系统 - RefreshError 继承 RequestError,type = AUTH_EXPIRED - 全局拦截器捕获 RefreshError → 自动 clearToken + AUTH_REQUIRED - 任何位置 throw new RefreshError() 即可触发登录 Modal ### 公告卡片优化 - 长文本 Tooltip 悬浮预览 - 点击卡片弹出详情 Modal(全文保留换行) ### SettingsContext(类似 Vue Pinia) - 集中式设置状态管理 - localStorage 持久化:ele-heixiu-output-path / ele-heixiu-team-repo-path - useSettings() 全局消费 ## 架构变更 - App.tsx → HashRouter + AppRoutes + LoginPage 弹层 - Layout.tsx = NavBar + <Outlet /> 页面壳 - HomePage.tsx 从 App.tsx 提取 - NavBar 使用 useNavigate() 真实路由导航 - useUpdater 改为模块级单例 IPC 监听器(修复 MaxListenersExceededWarning) - electron/main.ts 新增 FILE_DIALOG IPC handler ## 依赖 - 新增 react-router-dom
This commit is contained in:
277
src/pages/home/HomePage.tsx
Normal file
277
src/pages/home/HomePage.tsx
Normal file
@@ -0,0 +1,277 @@
|
||||
// ============================================================
|
||||
// HomePage — 首页仪表盘(从 App.tsx 提取)
|
||||
// ============================================================
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Space,
|
||||
Tag,
|
||||
Typography,
|
||||
Switch as AntSwitch,
|
||||
Slider,
|
||||
Progress,
|
||||
theme as antTheme,
|
||||
} from 'antd';
|
||||
import {
|
||||
ApiOutlined,
|
||||
ThunderboltOutlined,
|
||||
BulbOutlined,
|
||||
BulbFilled,
|
||||
DownloadOutlined,
|
||||
SyncOutlined,
|
||||
CheckCircleOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
RocketOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { getPlatformName, isDev } from '@/utils/platform';
|
||||
import { gradientPrimary } from '@/theme/tokens';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { useUpdater } from '@/hooks/use-updater';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
export function HomePage() {
|
||||
const { isDark, toggleTheme } = useTheme();
|
||||
const { platform, edition, isLoggedIn, logout } = useAppContext();
|
||||
const { token } = antTheme.useToken();
|
||||
const [progress, setProgress] = useState(65);
|
||||
const {
|
||||
status: updateStatus,
|
||||
progress: updateProgress,
|
||||
info: updateInfo,
|
||||
error: updateError,
|
||||
checkForUpdates,
|
||||
installUpdate,
|
||||
} = useUpdater();
|
||||
|
||||
return (
|
||||
<div className="flex-1 p-8">
|
||||
<div className="mx-auto max-w-4xl space-y-6">
|
||||
{/* ---- 标题区 + 主题开关 ---- */}
|
||||
<div className="text-center space-y-2">
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<Title level={1} className="mb-0! gradient-primary-text">
|
||||
HeiXiu · 桌面工作台
|
||||
</Title>
|
||||
<AntSwitch
|
||||
checked={isDark}
|
||||
onChange={toggleTheme}
|
||||
checkedChildren={<BulbFilled />}
|
||||
unCheckedChildren={<BulbOutlined />}
|
||||
title={isDark ? '切换亮色模式' : '切换暗色模式'}
|
||||
/>
|
||||
</div>
|
||||
<Text type="secondary">
|
||||
Electron + React 19 + TypeScript + Ant Design + Tailwind CSS
|
||||
</Text>
|
||||
<br />
|
||||
<Space size={4}>
|
||||
<Tag color={isDark ? 'blue' : 'purple'}>
|
||||
{isDark ? '🌙 暗色' : '☀️ 亮色'}
|
||||
</Tag>
|
||||
<Tag color={edition === 'enterprise' ? 'gold' : 'green'}>
|
||||
{edition === 'enterprise' ? '企业版' : '个人版'}
|
||||
</Tag>
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<Tag color="blue">已登录</Tag>
|
||||
<Button size="small" type="link" danger onClick={logout}>
|
||||
退出登录
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Tag color="default">未登录</Tag>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{/* ---- 导航栏状态提示 ---- */}
|
||||
<Card size="small" variant="borderless" className="rounded-lg">
|
||||
<Text type="secondary" className="text-xs">
|
||||
💡 当前使用 <Text strong>H5 自定义导航栏</Text> 替代系统菜单。
|
||||
{platform === 'darwin'
|
||||
? ' macOS 保留了最小 Edit 菜单以支持 Cmd+C/V 等快捷键。'
|
||||
: ' Windows/Linux 已完全移除系统菜单栏。'}
|
||||
</Text>
|
||||
</Card>
|
||||
|
||||
{/* ---- 主题色预览 ---- */}
|
||||
<Card
|
||||
title="🎨 蓝紫渐变主题色预览"
|
||||
variant="borderless"
|
||||
className="rounded-lg shadow-lg"
|
||||
>
|
||||
<Space orientation="vertical" size="large" className="w-full">
|
||||
{/* 渐变展示 */}
|
||||
<div>
|
||||
<Text strong>品牌渐变</Text>
|
||||
<div className="flex gap-3 mt-2">
|
||||
<div
|
||||
className="flex-1 h-16 rounded-lg flex items-center justify-center text-white font-medium shadow-md"
|
||||
style={{ background: gradientPrimary }}
|
||||
>
|
||||
135° 渐变
|
||||
</div>
|
||||
<div
|
||||
className="flex-1 h-16 rounded-lg flex items-center justify-center text-white font-medium shadow-md"
|
||||
style={{ background: 'linear-gradient(90deg, #4F46E5, #7C3AED)' }}
|
||||
>
|
||||
90° 渐变
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 组件示例 */}
|
||||
<div>
|
||||
<Text strong>Ant Design 组件</Text>
|
||||
<Space wrap className="mt-2">
|
||||
<Button type="primary" icon={<ThunderboltOutlined />}>
|
||||
主按钮
|
||||
</Button>
|
||||
<Button>默认</Button>
|
||||
<Button type="dashed">虚线</Button>
|
||||
<Button type="text">文字</Button>
|
||||
<Button type="link">链接</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{/* 滑块 + 进度条 */}
|
||||
<div>
|
||||
<Text strong>交互组件</Text>
|
||||
<div className="flex items-center gap-4 mt-2">
|
||||
<span className="text-sm" style={{ color: token.colorTextSecondary }}>
|
||||
进度:
|
||||
</span>
|
||||
<Slider
|
||||
className="flex-1"
|
||||
value={progress}
|
||||
onChange={setProgress}
|
||||
min={0}
|
||||
max={100}
|
||||
/>
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={progress}
|
||||
size={48}
|
||||
strokeColor={{ '0%': '#4F46E5', '100%': '#7C3AED' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
{/* ---- 平台信息 ---- */}
|
||||
<Card title="🖥️ 系统信息" variant="borderless" className="rounded-lg shadow-lg">
|
||||
<Space orientation="vertical" size="middle" className="w-full">
|
||||
<div className="flex items-center gap-2">
|
||||
<ApiOutlined style={{ color: token.colorPrimary }} />
|
||||
<Text strong>运行平台:</Text>
|
||||
<Tag color="purple">{getPlatformName()}</Tag>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<ApiOutlined style={{ color: token.colorPrimary }} />
|
||||
<Text strong>运行环境:</Text>
|
||||
<Tag color={isDev() ? 'blue' : 'green'}>
|
||||
{isDev() ? '开发模式' : '生产模式'}
|
||||
</Tag>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<ApiOutlined style={{ color: token.colorPrimary }} />
|
||||
<Text strong>当前导航:</Text>
|
||||
<Tag color="purple">
|
||||
H5 自定义导航栏({edition === 'enterprise' ? '企业版' : '个人版'}
|
||||
布局)
|
||||
</Tag>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
{/* ---- 版本更新 ---- */}
|
||||
<Card title="🔄 版本更新" variant="borderless" className="rounded-lg shadow-lg">
|
||||
<Space orientation="vertical" size="middle" className="w-full">
|
||||
{/* 状态提示 */}
|
||||
{updateStatus === 'checking' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<SyncOutlined spin style={{ color: token.colorPrimary }} />
|
||||
<Text type="secondary">正在检查更新...</Text>
|
||||
</div>
|
||||
)}
|
||||
{updateStatus === 'no-update' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircleOutlined style={{ color: token.colorSuccess }} />
|
||||
<Text type="success">已是最新版本</Text>
|
||||
</div>
|
||||
)}
|
||||
{updateStatus === 'error' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<ExclamationCircleOutlined style={{ color: token.colorError }} />
|
||||
<Text type="danger">{updateError?.message || '检查更新失败'}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 发现新版本 */}
|
||||
{(updateStatus === 'available' || updateStatus === 'downloading') && (
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<RocketOutlined style={{ color: token.colorPrimary }} />
|
||||
<Text strong>发现新版本:{updateInfo?.version}</Text>
|
||||
{updateInfo?.forceUpdate && <Tag color="red">强制更新</Tag>}
|
||||
</div>
|
||||
{updateInfo?.releaseNotes && (
|
||||
<Text type="secondary" className="text-xs whitespace-pre-line">
|
||||
{updateInfo.releaseNotes}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 下载进度 */}
|
||||
{updateStatus === 'downloading' && (
|
||||
<Progress
|
||||
percent={updateProgress.percent}
|
||||
strokeColor={{ '0%': '#4F46E5', '100%': '#7C3AED' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 下载完成 */}
|
||||
{updateStatus === 'downloaded' && (
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircleOutlined style={{ color: token.colorSuccess }} />
|
||||
<Text>更新已下载完毕,点击按钮安装并重启</Text>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={installUpdate}
|
||||
>
|
||||
安装更新
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Button
|
||||
icon={<SyncOutlined spin={updateStatus === 'checking'} />}
|
||||
onClick={checkForUpdates}
|
||||
disabled={updateStatus === 'checking'}
|
||||
>
|
||||
检查更新
|
||||
</Button>
|
||||
{isDev() && (
|
||||
<Text type="secondary" className="text-xs">
|
||||
(生产环境启动 5 秒后自动检查)
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user