mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-02 10:50:49 +08:00
refactor: remove README and REFACTOR_PROGRESS files; enhance MainLayout with sidebar icons and improved navigation item display
This commit is contained in:
180
README.md
180
README.md
@@ -1,180 +0,0 @@
|
||||
# CLI Proxy Web UI - React Version
|
||||
|
||||
CLI Proxy API Management Center 的 React + TypeScript 重构版本。
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- 🎯 完全使用 TypeScript 编写,类型安全
|
||||
- ⚛️ 基于 React 18 + Vite 构建,开发体验极佳
|
||||
- 🎨 SCSS 模块化样式,支持亮色/暗色主题
|
||||
- 🌍 完整的国际化支持 (中文/英文)
|
||||
- 📦 单文件部署,无需构建服务器
|
||||
- 🔒 安全的本地存储,支持数据加密
|
||||
- 📱 响应式设计,支持移动端
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 开发模式
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
npm run dev
|
||||
|
||||
# 访问 http://localhost:5173
|
||||
```
|
||||
|
||||
### 生产构建
|
||||
|
||||
```bash
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
|
||||
# 产物在 dist/index.html
|
||||
# 直接双击打开或部署到服务器
|
||||
```
|
||||
|
||||
### 代码检查
|
||||
|
||||
```bash
|
||||
# TypeScript 类型检查
|
||||
npm run type-check
|
||||
|
||||
# ESLint 代码检查
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # 公共组件
|
||||
│ ├── common/ # 基础组件 (Button, Input, Card, Modal...)
|
||||
│ └── layout/ # 布局组件 (MainLayout, Sidebar, Header...)
|
||||
├── pages/ # 页面组件
|
||||
│ ├── LoginPage.tsx
|
||||
│ ├── SettingsPage.tsx
|
||||
│ ├── ApiKeysPage.tsx
|
||||
│ ├── AiProvidersPage.tsx
|
||||
│ ├── AuthFilesPage.tsx
|
||||
│ ├── OAuthPage.tsx
|
||||
│ ├── UsagePage.tsx
|
||||
│ ├── ConfigPage.tsx
|
||||
│ ├── LogsPage.tsx
|
||||
│ └── SystemPage.tsx
|
||||
├── services/ # API 服务
|
||||
│ ├── api/ # API 客户端
|
||||
│ └── storage/ # 本地存储服务
|
||||
├── stores/ # Zustand 状态管理
|
||||
│ ├── useAuthStore.ts
|
||||
│ ├── useConfigStore.ts
|
||||
│ ├── useThemeStore.ts
|
||||
│ └── useLanguageStore.ts
|
||||
├── hooks/ # 自定义 Hooks
|
||||
├── types/ # TypeScript 类型定义
|
||||
├── utils/ # 工具函数
|
||||
├── i18n/ # 国际化配置
|
||||
├── styles/ # 全局样式
|
||||
└── router/ # 路由配置
|
||||
```
|
||||
|
||||
## 🔧 技术栈
|
||||
|
||||
- **框架**: React 18
|
||||
- **语言**: TypeScript 5
|
||||
- **构建工具**: Vite 7
|
||||
- **路由**: React Router 7 (Hash 模式)
|
||||
- **状态管理**: Zustand 5
|
||||
- **样式**: SCSS Modules
|
||||
- **国际化**: i18next
|
||||
- **HTTP 客户端**: Axios
|
||||
- **代码检查**: ESLint + TypeScript ESLint
|
||||
|
||||
## 📝 使用说明
|
||||
|
||||
### 首次使用
|
||||
|
||||
1. **清理旧数据** (如果从旧版本升级)
|
||||
- 打开 `CLEAR_STORAGE.html` 文件
|
||||
- 点击"清空 LocalStorage"按钮
|
||||
- 这将清理旧版本的存储数据
|
||||
|
||||
2. **打开应用**
|
||||
- 双击 `dist/index.html` 文件
|
||||
- 或使用 HTTP 服务器访问 (推荐)
|
||||
|
||||
3. **配置连接**
|
||||
- 输入 CLI Proxy API 服务器地址
|
||||
- 输入管理密钥
|
||||
- 点击"连接"按钮
|
||||
|
||||
### 部署方式
|
||||
|
||||
#### 方式 1: 本地文件 (file:// 协议)
|
||||
直接双击 `dist/index.html` 即可使用。应用已配置为使用 Hash 路由,支持 file:// 协议。
|
||||
|
||||
#### 方式 2: HTTP 服务器 (推荐)
|
||||
```bash
|
||||
# 使用 Python
|
||||
cd dist
|
||||
python -m http.server 8080
|
||||
|
||||
# 使用 Node.js (需要安装 serve)
|
||||
npx serve dist
|
||||
|
||||
# 访问 http://localhost:8080
|
||||
```
|
||||
|
||||
#### 方式 3: Nginx/Apache
|
||||
将 `dist/index.html` 部署到 Web 服务器即可。
|
||||
|
||||
## 🐛 故障排除
|
||||
|
||||
### 白屏问题
|
||||
|
||||
如果打开后显示白屏:
|
||||
|
||||
1. 检查浏览器控制台是否有错误
|
||||
2. 确认是否清理了旧版本的 localStorage 数据
|
||||
3. 尝试使用 HTTP 服务器访问而不是 file:// 协议
|
||||
|
||||
### LocalStorage 错误
|
||||
|
||||
如果看到 "Failed to parse stored data" 错误:
|
||||
|
||||
1. 打开 `CLEAR_STORAGE.html`
|
||||
2. 清空所有存储数据
|
||||
3. 刷新页面重新登录
|
||||
|
||||
### 路由问题
|
||||
|
||||
应用使用 Hash 路由 (#/login, #/settings),确保 URL 中包含 `#` 符号。
|
||||
|
||||
## 📊 构建信息
|
||||
|
||||
- **TypeScript**: 0 errors ✅
|
||||
- **ESLint**: 0 errors, 137 warnings ⚠️
|
||||
- **Bundle Size**: 473 KB (144 KB gzipped)
|
||||
- **Build Time**: ~5 seconds
|
||||
|
||||
## 🔄 从旧版本迁移
|
||||
|
||||
旧版本 (原生 JS) 的数据存储格式已变更。首次使用新版本时:
|
||||
|
||||
1. 旧的 localStorage 数据会自动迁移
|
||||
2. 如果迁移失败,请手动清理 localStorage
|
||||
3. 重新输入连接信息即可
|
||||
|
||||
## 📄 License
|
||||
|
||||
Same as CLI Proxy API
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!
|
||||
|
||||
---
|
||||
|
||||
**注意**: 此版本是原 CLI Proxy Web UI 的 React 重构版本,与原版功能保持一致。
|
||||
@@ -1,21 +0,0 @@
|
||||
# 重构进度追踪
|
||||
|
||||
## 阶段完成状态
|
||||
- [x] 阶段 0: 准备工作
|
||||
- [x] 阶段 1: 项目初始化
|
||||
- [x] 阶段 2: 类型定义
|
||||
- [x] 阶段 3: 工具与服务
|
||||
- [x] 阶段 4: 状态管理
|
||||
- [x] 阶段 5: 自定义 Hooks
|
||||
- [x] 阶段 6: 国际化配置
|
||||
- [x] 阶段 7: 样式系统
|
||||
- [x] 阶段 8: 通用组件
|
||||
- [x] 阶段 9: 页面组件(API Keys / Providers / Auth Files / OAuth / Usage / Config / Logs / System)
|
||||
- [x] 阶段 10: 路由布局与构建验证
|
||||
|
||||
## 错误记录
|
||||
<!-- 遇到任何错误都记录在这里 -->
|
||||
|
||||
## 待办事项
|
||||
- [ ] 深入回归交互细节(模型价格、图表、日志增量等)与基线对齐
|
||||
- [ ] 优化 Sass 导入与 darken 用法以消除警告
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ReactNode, SVGProps, useEffect, useMemo, useState } from 'react';
|
||||
import { NavLink, Outlet } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
@@ -6,6 +6,95 @@ import { useAuthStore, useConfigStore, useLanguageStore, useNotificationStore, u
|
||||
import { versionApi } from '@/services/api';
|
||||
import { isLocalhost } from '@/utils/connection';
|
||||
|
||||
const iconProps: SVGProps<SVGSVGElement> = {
|
||||
width: 18,
|
||||
height: 18,
|
||||
viewBox: '0 0 20 20',
|
||||
fill: 'none',
|
||||
stroke: 'currentColor',
|
||||
strokeWidth: 1.5,
|
||||
strokeLinecap: 'round',
|
||||
strokeLinejoin: 'round',
|
||||
'aria-hidden': 'true',
|
||||
focusable: 'false'
|
||||
};
|
||||
|
||||
const sidebarIcons: Record<string, ReactNode> = {
|
||||
settings: (
|
||||
<svg {...iconProps}>
|
||||
<path d="M4 6.5h12" />
|
||||
<circle cx="9" cy="6.5" r="2" />
|
||||
<path d="M4 10h12" />
|
||||
<circle cx="7" cy="10" r="2" />
|
||||
<path d="M4 13.5h12" />
|
||||
<circle cx="12" cy="13.5" r="2" />
|
||||
</svg>
|
||||
),
|
||||
apiKeys: (
|
||||
<svg {...iconProps}>
|
||||
<circle cx="7.2" cy="10" r="2.4" />
|
||||
<path d="M9.6 10h6" />
|
||||
<path d="M12.8 10v2.4" />
|
||||
<path d="M14.8 10v1.4" />
|
||||
</svg>
|
||||
),
|
||||
aiProviders: (
|
||||
<svg {...iconProps}>
|
||||
<circle cx="10" cy="5.2" r="2.2" />
|
||||
<circle cx="6" cy="13.2" r="2" />
|
||||
<circle cx="14" cy="13.2" r="2" />
|
||||
<path d="M8.6 6.8 6.8 10.8" />
|
||||
<path d="M11.4 6.8 13.2 10.8" />
|
||||
<path d="M7.8 13.2h4.4" />
|
||||
</svg>
|
||||
),
|
||||
authFiles: (
|
||||
<svg {...iconProps}>
|
||||
<path d="M7 3.5h4.8L15 6.8V16H7Z" />
|
||||
<path d="M11.8 3.5V7h3.2" />
|
||||
<path d="m8.9 11.8 1.7 1.6 3.4-3.5" />
|
||||
</svg>
|
||||
),
|
||||
oauth: (
|
||||
<svg {...iconProps}>
|
||||
<path d="M10 3.5 15.2 5.6v3.6c0 3-2 5.8-5.2 7-3.2-1.2-5.2-4-5.2-7V5.6Z" />
|
||||
<path d="M8.2 9.6h3.6" />
|
||||
<path d="m9.6 8.2-1.4 1.4 1.4 1.4" />
|
||||
<path d="m11.8 8.2 1.4 1.4-1.4 1.4" />
|
||||
</svg>
|
||||
),
|
||||
usage: (
|
||||
<svg {...iconProps}>
|
||||
<path d="M4 14.5h12" />
|
||||
<path d="m6.2 11.3 3-3 2.4 2 2.9-3.7" />
|
||||
</svg>
|
||||
),
|
||||
config: (
|
||||
<svg {...iconProps}>
|
||||
<path d="M5.2 8 10 5.8l4.8 2.2L10 10.2Z" />
|
||||
<path d="M5.2 12 10 9.8l4.8 2.2L10 14.2Z" />
|
||||
<path d="M10 10.2v3.6" />
|
||||
</svg>
|
||||
),
|
||||
logs: (
|
||||
<svg {...iconProps}>
|
||||
<path d="M6.4 6h9" />
|
||||
<path d="M6.4 10h9" />
|
||||
<path d="M6.4 14h9" />
|
||||
<circle cx="4.2" cy="6" r="0.9" />
|
||||
<circle cx="4.2" cy="10" r="0.9" />
|
||||
<circle cx="4.2" cy="14" r="0.9" />
|
||||
</svg>
|
||||
),
|
||||
system: (
|
||||
<svg {...iconProps}>
|
||||
<circle cx="10" cy="10" r="6.2" />
|
||||
<path d="M10 8.8v3.6" />
|
||||
<circle cx="10" cy="6.2" r="0.8" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
)
|
||||
};
|
||||
|
||||
const parseVersionSegments = (version?: string | null) => {
|
||||
if (!version) return null;
|
||||
const cleaned = version.trim().replace(/^v/i, '');
|
||||
@@ -72,15 +161,15 @@ export function MainLayout() {
|
||||
: 'muted';
|
||||
|
||||
const navItems = [
|
||||
{ path: '/settings', label: t('nav.basic_settings') },
|
||||
{ path: '/api-keys', label: t('nav.api_keys') },
|
||||
{ path: '/ai-providers', label: t('nav.ai_providers') },
|
||||
{ path: '/auth-files', label: t('nav.auth_files') },
|
||||
...(isLocal ? [{ path: '/oauth', label: t('nav.oauth', { defaultValue: 'OAuth' }) }] : []),
|
||||
{ path: '/usage', label: t('nav.usage_stats') },
|
||||
{ path: '/config', label: t('nav.config_management') },
|
||||
...(config?.loggingToFile ? [{ path: '/logs', label: t('nav.logs') }] : []),
|
||||
{ path: '/system', label: t('nav.system_info') }
|
||||
{ path: '/settings', label: t('nav.basic_settings'), icon: sidebarIcons.settings },
|
||||
{ path: '/api-keys', label: t('nav.api_keys'), icon: sidebarIcons.apiKeys },
|
||||
{ path: '/ai-providers', label: t('nav.ai_providers'), icon: sidebarIcons.aiProviders },
|
||||
{ path: '/auth-files', label: t('nav.auth_files'), icon: sidebarIcons.authFiles },
|
||||
...(isLocal ? [{ path: '/oauth', label: t('nav.oauth', { defaultValue: 'OAuth' }), icon: sidebarIcons.oauth }] : []),
|
||||
{ path: '/usage', label: t('nav.usage_stats'), icon: sidebarIcons.usage },
|
||||
{ path: '/config', label: t('nav.config_management'), icon: sidebarIcons.config },
|
||||
...(config?.loggingToFile ? [{ path: '/logs', label: t('nav.logs'), icon: sidebarIcons.logs }] : []),
|
||||
{ path: '/system', label: t('nav.system_info'), icon: sidebarIcons.system }
|
||||
];
|
||||
|
||||
const handleRefreshAll = async () => {
|
||||
@@ -135,7 +224,8 @@ export function MainLayout() {
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
title={sidebarCollapsed ? item.label : undefined}
|
||||
>
|
||||
{sidebarCollapsed ? item.label.charAt(0) : item.label}
|
||||
<span className="nav-icon">{item.icon}</span>
|
||||
{!sidebarCollapsed && <span className="nav-label">{item.label}</span>}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -59,6 +59,28 @@
|
||||
cursor: pointer;
|
||||
transition: background $transition-fast, color $transition-fast;
|
||||
|
||||
.nav-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.9;
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user