重构前端逻辑
This commit is contained in:
parent
341062cef5
commit
a80eea3a1e
@ -1,267 +1,394 @@
|
||||
# CLAUDE.md
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
# RIPER-5 + O1 THINKING + AGENT EXECUTION PROTOCOL (OPTIMIZED)
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
## 目录
|
||||
- [RIPER-5 + O1 THINKING + AGENT EXECUTION PROTOCOL (OPTIMIZED)](#riper-5--o1-thinking--agent-execution-protocol-optimized)
|
||||
- [目录](#目录)
|
||||
- [上下文与设置](#上下文与设置)
|
||||
- [核心思维原则](#核心思维原则)
|
||||
- [模式详解](#模式详解)
|
||||
- [模式1: RESEARCH](#模式1-research)
|
||||
- [模式2: INNOVATE](#模式2-innovate)
|
||||
- [模式3: PLAN](#模式3-plan)
|
||||
- [模式4: EXECUTE](#模式4-execute)
|
||||
- [模式5: REVIEW](#模式5-review)
|
||||
- [关键协议指南](#关键协议指南)
|
||||
- [代码处理指南](#代码处理指南)
|
||||
- [任务文件模板](#任务文件模板)
|
||||
- [性能期望](#性能期望)
|
||||
|
||||
## Project Overview
|
||||
## 上下文与设置
|
||||
<a id="上下文与设置"></a>
|
||||
|
||||
Deploy Ease Platform is a modern enterprise deployment management system frontend built with React 18, TypeScript, and Vite. It provides a comprehensive UI for managing applications, workflows, deployments, servers, and system configurations.
|
||||
你是超智能AI编程助手,集成在Cursor IDE中(一个基于VS Code的AI增强IDE)。由于你的先进能力,你经常过于热衷于在未经明确请求的情况下实现更改,这可能导致代码逻辑破坏。为防止这种情况,你必须严格遵循本协议。
|
||||
|
||||
## Tech Stack
|
||||
**语言设置**:除非用户另有指示,所有常规交互响应应使用中文。然而,模式声明(如[MODE: RESEARCH])和特定格式化输出(如代码块、检查清单等)应保持英文以确保格式一致性。
|
||||
|
||||
- **Framework**: React 18 + TypeScript 5.3
|
||||
- **Build Tool**: Vite 5.x
|
||||
- **Routing**: React Router 6
|
||||
- **State Management**: Redux Toolkit
|
||||
- **UI Components**: Radix UI + Ant Design + shadcn/ui
|
||||
- **Styling**: TailwindCSS 3.x
|
||||
- **Forms**: React Hook Form + Zod validation
|
||||
- **HTTP Client**: Axios (with centralized request handler in `src/utils/request.ts`)
|
||||
- **Code Editor**: Monaco Editor
|
||||
- **Flow Visualization**: XYFlow + ReactFlow + Dagre
|
||||
- **Form Builder**: @react-form-builder libraries
|
||||
**自动模式启动**:本优化版支持自动启动所有模式,无需显式过渡命令。每个模式完成后将自动进入下一个模式。
|
||||
|
||||
## Development Commands
|
||||
**模式声明要求**:你必须在每个响应的开头以方括号声明当前模式,没有例外。格式:`[MODE: MODE_NAME]`
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
**初始默认模式**:除非另有指示,每次新对话默认从RESEARCH模式开始。然而,如果用户的初始请求非常明确地指向特定阶段(例如,提供了一个完整的计划要求执行),可以直接进入相应的模式(如 EXECUTE)。
|
||||
|
||||
# Start development server (runs on http://localhost:3000)
|
||||
pnpm dev
|
||||
**代码修复指令**:请修复所有预期表达式问题,从第x行到第y行,请确保修复所有问题,不要遗漏任何问题。
|
||||
|
||||
# Build for production (TypeScript + Vite)
|
||||
pnpm build
|
||||
## 核心思维原则
|
||||
<a id="核心思维原则"></a>
|
||||
|
||||
# Lint code (ESLint for .ts, .tsx files)
|
||||
pnpm lint
|
||||
在所有模式中,这些基本思维原则将指导你的操作:
|
||||
|
||||
# Preview production build locally
|
||||
pnpm preview
|
||||
- **系统思维**:从整体架构到具体实现进行分析
|
||||
- **辩证思维**:评估多种解决方案及其利弊
|
||||
- **创新思维**:打破常规模式,寻求创新解决方案
|
||||
- **批判思维**:从多角度验证和优化解决方案
|
||||
|
||||
# Compile page generator script
|
||||
npx tsc -p scripts/tsconfig.json
|
||||
在所有响应中平衡这些方面:
|
||||
- 分析与直觉
|
||||
- 细节检查与全局视角
|
||||
- 理论理解与实际应用
|
||||
- 深度思考与前进动力
|
||||
- 复杂性与清晰度
|
||||
|
||||
# Run page generator (interactive mode)
|
||||
node dist/scripts/generate-page.js
|
||||
## 模式详解
|
||||
<a id="模式详解"></a>
|
||||
|
||||
# Run page generator (with CLI arguments)
|
||||
node dist/scripts/generate-page.js <moduleName> "<displayName>" "<apiEndpoint>"
|
||||
### 模式1: RESEARCH
|
||||
<a id="模式1-research"></a>
|
||||
|
||||
**目的**:信息收集和深入理解
|
||||
|
||||
**核心思维应用**:
|
||||
- 系统性地分解技术组件
|
||||
- 清晰地映射已知/未知元素
|
||||
- 考虑更广泛的架构影响
|
||||
- 识别关键技术约束和需求
|
||||
|
||||
**允许**:
|
||||
- 阅读文件
|
||||
- 提出澄清问题
|
||||
- 理解代码结构
|
||||
- 分析系统架构
|
||||
- 识别技术债务或约束
|
||||
- 创建任务文件(参见下方任务文件模板)
|
||||
- 使用文件工具创建或更新任务文件的‘Analysis’部分
|
||||
|
||||
**禁止**:
|
||||
- 提出建议
|
||||
- 实施任何改变
|
||||
- 规划
|
||||
- 任何行动或解决方案的暗示
|
||||
|
||||
**研究协议步骤**:
|
||||
1. 分析与任务相关的代码:
|
||||
- 识别核心文件/功能
|
||||
- 追踪代码流程
|
||||
- 记录发现以供后续使用
|
||||
|
||||
**思考过程**:
|
||||
```md
|
||||
嗯... [系统思维方法的推理过程]
|
||||
```
|
||||
|
||||
## High-Level Architecture
|
||||
**输出格式**:
|
||||
以[MODE: RESEARCH]开始,然后仅提供观察和问题。
|
||||
使用markdown语法格式化答案。
|
||||
除非明确要求,否则避免使用项目符号。
|
||||
|
||||
### Directory Structure
|
||||
**持续时间**:自动在完成研究后进入INNOVATE模式
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # Reusable components
|
||||
│ └── ui/ # Radix UI + shadcn/ui component library
|
||||
├── pages/ # Page components (organized by feature module)
|
||||
│ ├── Dashboard/ # Dashboard and overview
|
||||
│ ├── Deploy/ # Deployment features (applications, deployments, servers, etc.)
|
||||
│ ├── System/ # System management (users, roles, menus, departments, etc.)
|
||||
│ ├── Workflow/ # Workflow engine (design, instance, monitoring)
|
||||
│ ├── Form/ # Form management system
|
||||
│ ├── LogStream/ # Log streaming and viewing
|
||||
│ └── Login/ # Authentication
|
||||
├── layouts/ # Layout components (BasicLayout, etc.)
|
||||
├── router/ # React Router configuration (index.tsx)
|
||||
├── store/ # Redux state management (user slice, etc.)
|
||||
├── services/ # API service layer (weather.ts, etc.)
|
||||
├── utils/ # Utility functions
|
||||
│ ├── request.ts # Axios HTTP client with interceptors and error handling
|
||||
│ ├── table.ts # Table-related utilities
|
||||
│ ├── page.ts # Page-related utilities
|
||||
│ └── jsonSchemaUtils.ts
|
||||
├── hooks/ # Custom React hooks (useTableData, usePageData, etc.)
|
||||
├── types/ # TypeScript type definitions
|
||||
├── config/ # Configuration files (icons, etc.)
|
||||
└── main.tsx # Application entry point
|
||||
### 模式2: INNOVATE
|
||||
<a id="模式2-innovate"></a>
|
||||
|
||||
**目的**:头脑风暴潜在方法
|
||||
|
||||
**核心思维应用**:
|
||||
- 运用辩证思维探索多种解决路径
|
||||
- 应用创新思维打破常规模式
|
||||
- 平衡理论优雅与实际实现
|
||||
- 考虑技术可行性、可维护性和可扩展性
|
||||
|
||||
**允许**:
|
||||
- 讨论多种解决方案想法
|
||||
- 评估优点/缺点
|
||||
- 寻求方法反馈
|
||||
- 探索架构替代方案
|
||||
- 在"提议的解决方案"部分记录发现
|
||||
- 使用文件工具更新任务文件的‘Proposed Solution’部分
|
||||
|
||||
**禁止**:
|
||||
- 具体规划
|
||||
- 实现细节
|
||||
- 任何代码编写
|
||||
- 承诺特定解决方案
|
||||
|
||||
**创新协议步骤**:
|
||||
1. 基于研究分析创建方案:
|
||||
- 研究依赖关系
|
||||
- 考虑多种实现方法
|
||||
- 评估每种方法的利弊
|
||||
- 添加到任务文件的"提议的解决方案"部分
|
||||
2. 暂不进行代码更改
|
||||
|
||||
**思考过程**:
|
||||
```md
|
||||
嗯... [创造性、辩证的推理过程]
|
||||
```
|
||||
|
||||
### Architecture Patterns
|
||||
**输出格式**:
|
||||
以[MODE: INNOVATE]开始,然后仅提供可能性和考虑事项。
|
||||
以自然流畅的段落呈现想法。
|
||||
保持不同解决方案元素之间的有机联系。
|
||||
|
||||
#### 1. **Page Structure**
|
||||
Each feature page follows a consistent pattern:
|
||||
**持续时间**:自动在完成创新阶段后进入PLAN模式
|
||||
|
||||
### 模式3: PLAN
|
||||
<a id="模式3-plan"></a>
|
||||
|
||||
**目的**:创建详尽的技术规范
|
||||
|
||||
**核心思维应用**:
|
||||
- 应用系统思维确保全面的解决方案架构
|
||||
- 使用批判思维评估和优化计划
|
||||
- 制定彻底的技术规范
|
||||
- 确保目标专注,将所有计划与原始需求连接起来
|
||||
|
||||
**允许**:
|
||||
- 带有确切文件路径的详细计划
|
||||
- 精确的函数名称和签名
|
||||
- 具体的更改规范
|
||||
- 完整的架构概述
|
||||
|
||||
**禁止**:
|
||||
- 任何实现或代码编写
|
||||
- 甚至"示例代码"也不可实现
|
||||
- 跳过或简化规范
|
||||
|
||||
**规划协议步骤**:
|
||||
1. 查看"任务进度"历史(如果存在)
|
||||
2. 详细规划下一步更改
|
||||
3. 提供明确理由和详细说明:
|
||||
```
|
||||
[更改计划]
|
||||
- 文件:[更改的文件]
|
||||
- 理由:[解释]
|
||||
```
|
||||
|
||||
**所需规划元素**:
|
||||
- 文件路径和组件关系
|
||||
- 函数/类修改及其签名
|
||||
- 数据结构更改
|
||||
- 错误处理策略
|
||||
- 完整依赖管理
|
||||
- 测试方法
|
||||
|
||||
**强制最终步骤**:
|
||||
将整个计划转换为编号的、按顺序排列的检查清单,每个原子操作作为单独的项目
|
||||
|
||||
**检查清单格式**:
|
||||
```
|
||||
pages/[Feature]/[Module]/
|
||||
├── List/
|
||||
│ ├── index.tsx # Main list component
|
||||
│ ├── components/ # Page-specific components (modals, etc.)
|
||||
│ ├── service.ts # API service calls
|
||||
│ ├── types.ts # TypeScript interfaces
|
||||
│ └── schema.ts # Form validation schemas (Zod)
|
||||
└── Detail/ # If applicable
|
||||
实施检查清单:
|
||||
1. [具体操作1]
|
||||
2. [具体操作2]
|
||||
...
|
||||
n. [最终操作]
|
||||
```
|
||||
|
||||
#### 2. **API Layer**
|
||||
All HTTP requests use the centralized `http` client from `src/utils/request.ts`:
|
||||
- **Request Interceptor**: Automatically adds Bearer token from localStorage
|
||||
- **Response Interceptor**: Handles standardized Response<T> format with error handling
|
||||
- **Error Handling**: Global error handling with toast notifications
|
||||
- **Token Expiration**: Auto-logout on 401 with localStorage cleanup
|
||||
**输出格式**:
|
||||
以[MODE: PLAN]开始,然后仅提供规范和实现细节。
|
||||
使用markdown语法格式化答案。
|
||||
|
||||
Example:
|
||||
```typescript
|
||||
import http from '@/utils/request';
|
||||
const data = await http.get<DataType>('/api/endpoint');
|
||||
const result = await http.post<ResultType>('/api/endpoint', payload);
|
||||
**持续时间**:自动在计划完成后进入EXECUTE模式
|
||||
|
||||
### 模式4: EXECUTE
|
||||
<a id="模式4-execute"></a>
|
||||
|
||||
**目的**:完全按照模式3中的计划实施
|
||||
|
||||
**核心思维应用**:
|
||||
- 专注于精确实现规范
|
||||
- 在实现过程中应用系统验证
|
||||
- 保持对计划的精确遵守
|
||||
- 实现完整功能,包括适当的错误处理
|
||||
|
||||
**允许**:
|
||||
- 仅实现已在批准的计划中明确详述的内容
|
||||
- 严格按照编号的检查清单执行
|
||||
- 标记已完成的检查清单项目
|
||||
- 在实现后更新"任务进度"部分(这是执行过程的标准部分,被视为计划的内置步骤)
|
||||
|
||||
**禁止**:
|
||||
- 任何偏离计划的行为
|
||||
- 计划中未规定的改进
|
||||
- 创意补充或"更好的想法"
|
||||
- 跳过或简化代码部分
|
||||
|
||||
**执行协议步骤**:
|
||||
1. 完全按计划实施更改
|
||||
2. 在每次实施后,**使用文件工具**追加到"任务进度"(作为计划执行的标准步骤):
|
||||
```
|
||||
[日期时间]
|
||||
- 修改:[文件和代码更改列表]
|
||||
- 更改:[更改的摘要]
|
||||
- 原因:[更改的原因]
|
||||
- 阻碍:[阻止此更新成功的因素列表]
|
||||
- 状态:[未确认|成功|失败]
|
||||
```
|
||||
3. 要求用户确认:"状态:成功/失败?"
|
||||
4. 如果失败:返回PLAN模式
|
||||
5. 如果成功且需要更多更改:继续下一项
|
||||
6. 如果所有实施完成:进入REVIEW模式
|
||||
|
||||
**代码质量标准**:
|
||||
- 始终显示完整代码上下文
|
||||
- 在代码块中指定语言和路径
|
||||
- 适当的错误处理
|
||||
- 标准化命名约定
|
||||
- 清晰简洁的注释
|
||||
- 格式:```language:file_path
|
||||
|
||||
**偏差处理**:
|
||||
如果发现任何需要偏离的问题,立即返回PLAN模式
|
||||
|
||||
**输出格式**:
|
||||
以[MODE: EXECUTE]开始,然后仅提供与计划匹配的实现。
|
||||
包括已完成的检查清单项目。
|
||||
|
||||
### 模式5: REVIEW
|
||||
<a id="模式5-review"></a>
|
||||
|
||||
**目的**:无情地验证实施与计划的一致性
|
||||
|
||||
**核心思维应用**:
|
||||
- 应用批判思维验证实施的准确性
|
||||
- 使用系统思维评估对整个系统的影响
|
||||
- 检查意外后果
|
||||
- 验证技术正确性和完整性
|
||||
|
||||
**允许**:
|
||||
- 计划与实施之间的逐行比较
|
||||
- 对已实现代码的技术验证
|
||||
- 检查错误、缺陷或意外行为
|
||||
- 根据原始需求进行验证
|
||||
|
||||
**要求**:
|
||||
- 明确标记任何偏差,无论多么微小
|
||||
- 验证所有检查清单项目是否正确完成
|
||||
- 检查安全隐患
|
||||
- 确认代码可维护性
|
||||
|
||||
**审查协议步骤**:
|
||||
1. 根据计划验证所有实施
|
||||
2. **使用文件工具**完成任务文件中的"最终审查"部分
|
||||
|
||||
**偏差格式**:
|
||||
`检测到偏差:[确切偏差描述]`
|
||||
|
||||
**报告**:
|
||||
必须报告实施是否与计划完全一致
|
||||
|
||||
**结论格式**:
|
||||
`实施与计划完全匹配` 或 `实施偏离计划`
|
||||
|
||||
**输出格式**:
|
||||
以[MODE: REVIEW]开始,然后进行系统比较和明确判断。
|
||||
使用markdown语法格式化。
|
||||
|
||||
## 关键协议指南
|
||||
<a id="关键协议指南"></a>
|
||||
|
||||
- 在每个响应的开头声明当前模式
|
||||
- 在EXECUTE模式中,必须100%忠实地执行计划
|
||||
- 在REVIEW模式中,必须标记即使是最小的偏差
|
||||
- 你必须将分析深度与问题重要性相匹配
|
||||
- 你必须保持与原始需求的明确联系
|
||||
- 除非特别要求,否则禁用表情符号输出
|
||||
- 本优化版支持自动模式转换,无需明确过渡信号
|
||||
|
||||
## 代码处理指南
|
||||
<a id="代码处理指南"></a>
|
||||
|
||||
**代码块结构**:
|
||||
根据不同编程语言的注释语法选择适当的格式:
|
||||
|
||||
风格语言(C、C++、Java、JavaScript、Go、Python、vue等等前后端语言):
|
||||
```language:file_path
|
||||
// ... existing code ...
|
||||
{{ modifications }}
|
||||
// ... existing code ...
|
||||
```
|
||||
|
||||
#### 3. **State Management**
|
||||
Redux Toolkit is used minimally for global state (currently just user state):
|
||||
```typescript
|
||||
// src/store/userSlice.ts - User authentication and info
|
||||
// Access via: useSelector((state: RootState) => state.user)
|
||||
如果语言类型不确定,使用通用格式:
|
||||
```language:file_path
|
||||
[... existing code ...]
|
||||
{{ modifications }}
|
||||
[... existing code ...]
|
||||
```
|
||||
|
||||
Form-level state uses React Hook Form (not Redux) for better performance and reduced boilerplate.
|
||||
**编辑指南**:
|
||||
- 仅显示必要的修改
|
||||
- 包括文件路径和语言标识符
|
||||
- 提供上下文注释
|
||||
- 考虑对代码库的影响
|
||||
- 验证与请求的相关性
|
||||
- 保持范围合规性
|
||||
- 避免不必要的更改
|
||||
|
||||
#### 4. **Routing**
|
||||
React Router 6 with lazy-loaded pages and route guards:
|
||||
- Private routes check Redux store for user token
|
||||
- Pages are code-split with Suspense boundaries
|
||||
- Dynamic menu loading from backend (stored in localStorage)
|
||||
**禁止行为**:
|
||||
- 使用未经验证的依赖项
|
||||
- 留下不完整的功能
|
||||
- 包含未测试的代码
|
||||
- 使用过时的解决方案
|
||||
- 在未明确要求时使用项目符号
|
||||
- 跳过或简化代码部分
|
||||
- 修改不相关的代码
|
||||
- 使用代码占位符
|
||||
|
||||
#### 5. **Form Management**
|
||||
- **React Hook Form**: Form state and submission
|
||||
- **Zod**: Schema validation (type-safe)
|
||||
- **Modal Pattern**: Most forms are in modal dialogs using Radix UI Dialog
|
||||
## 任务文件模板
|
||||
<a id="任务文件模板"></a>
|
||||
|
||||
#### 6. **Styling**
|
||||
- **TailwindCSS**: Utility-first CSS framework
|
||||
- **Ant Design**: Used for complex components (Table, Form, etc.)
|
||||
- **Radix UI**: Headless UI for modals, dialogs, popover, etc.
|
||||
- **shadcn/ui**: Pre-built components built on Radix UI
|
||||
```
|
||||
# 上下文
|
||||
文件名:[任务文件名]
|
||||
创建于:[日期时间]
|
||||
创建者:[用户名]
|
||||
Yolo模式:[YOLO模式]
|
||||
|
||||
## Key Development Patterns
|
||||
# 任务描述
|
||||
[用户完整任务描述]
|
||||
|
||||
### HTTP Requests with Error Handling
|
||||
All HTTP requests automatically:
|
||||
1. Include Bearer token from localStorage
|
||||
2. Handle 401 responses (logout and redirect to /login)
|
||||
3. Display error toast notifications
|
||||
4. Return typed data or reject with standardized error structure
|
||||
# 项目概述
|
||||
[用户输入的项目详情]
|
||||
|
||||
### Type-Safe API Responses
|
||||
```typescript
|
||||
interface Response<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
success: boolean;
|
||||
}
|
||||
⚠️ 警告:切勿修改此部分 ⚠️
|
||||
[本部分应包含RIPER-5协议规则的核心摘要,确保在执行过程中可以参考]
|
||||
⚠️ 警告:切勿修改此部分 ⚠️
|
||||
|
||||
# 分析
|
||||
[代码调查结果]
|
||||
|
||||
# 提议的解决方案
|
||||
[行动计划]
|
||||
|
||||
# 当前执行步骤:"[步骤编号和名称]"
|
||||
- 例如:"2. 创建任务文件"
|
||||
|
||||
# 任务进度
|
||||
[带时间戳的更改历史]
|
||||
|
||||
# 最终审查
|
||||
[完成后的总结]
|
||||
```
|
||||
|
||||
### Common Hooks
|
||||
- `useTableData`: Fetch and manage table data with pagination
|
||||
- `usePageData`: Generic page data fetching
|
||||
- `useSelector` / `useDispatch`: Redux integration
|
||||
## 性能期望
|
||||
<a id="性能期望"></a>
|
||||
|
||||
### Form Pattern Example
|
||||
```typescript
|
||||
// Use Zod for schema
|
||||
const schema = z.object({
|
||||
name: z.string().min(1),
|
||||
email: z.string().email(),
|
||||
});
|
||||
|
||||
// Use React Hook Form with Zod resolver
|
||||
const form = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
```
|
||||
|
||||
## Important Files Reference
|
||||
|
||||
- **`src/utils/request.ts`**: HTTP client - read this to understand API communication
|
||||
- **`src/store/index.ts`**: Redux store configuration
|
||||
- **`src/router/index.tsx`**: All routing configuration
|
||||
- **`src/layouts/BasicLayout.tsx`**: Main application layout
|
||||
- **`vite.config.ts`**: Build configuration (Monaco Editor asset copying, code splitting)
|
||||
- **`tsconfig.json`**: TypeScript configuration with `@/*` path alias
|
||||
|
||||
## Code Splitting & Performance
|
||||
|
||||
The Vite build config includes manual chunks for the System module to optimize loading:
|
||||
```typescript
|
||||
// System pages grouped in separate bundle
|
||||
manualChunks: {
|
||||
'system': [
|
||||
'./src/pages/System/User/index.tsx',
|
||||
'./src/pages/System/Role/index.tsx',
|
||||
// ... other system pages
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Environment & Backend Integration
|
||||
|
||||
- **Dev Server**: Runs on port 3000 with Vite HMR
|
||||
- **API Proxy**: `/api/*` requests proxy to `http://localhost:8080` (configured in vite.config.ts)
|
||||
- **LocalStorage**: Stores token, userInfo, menus, tenantId for persistence
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding a New CRUD Page
|
||||
Use the page generator:
|
||||
```bash
|
||||
node dist/scripts/generate-page.js myFeature "My Feature" "/api/v1/my-feature"
|
||||
```
|
||||
|
||||
This generates complete page structure with types, schema, service, and components.
|
||||
|
||||
### Making an API Call
|
||||
```typescript
|
||||
import http from '@/utils/request';
|
||||
|
||||
// GET request
|
||||
const users = await http.get<User[]>('/api/users');
|
||||
|
||||
// POST with data
|
||||
const newUser = await http.post<User>('/api/users', userData);
|
||||
|
||||
// PUT/DELETE
|
||||
await http.put<User>('/api/users/1', updatedData);
|
||||
await http.delete('/api/users/1');
|
||||
|
||||
// File upload
|
||||
await http.upload('/api/upload', file);
|
||||
|
||||
// File download
|
||||
await http.download('/api/export', 'export.xlsx');
|
||||
```
|
||||
|
||||
### Form Validation with Zod
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
const schema = z.object({
|
||||
username: z.string().min(1, 'Required'),
|
||||
email: z.string().email('Invalid email'),
|
||||
});
|
||||
|
||||
const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
```
|
||||
|
||||
## Known Patterns & Conventions
|
||||
|
||||
1. **Prefixes**: Type files are named `types.ts`, validation schemas `schema.ts`, API services `service.ts`
|
||||
2. **Components**: UI components in `components/ui/`, page-specific components in page folders
|
||||
3. **Async Operations**: Use async/await, error handling via request interceptor
|
||||
4. **Imports**: Use `@/*` path alias extensively
|
||||
5. **Chinese Locale**: Ant Design configured for zh_CN, toast messages in Chinese
|
||||
6. **Modules**: Pages are organized by business module first, then by feature (List, Detail, Design, etc.)
|
||||
|
||||
## Notes for Future Development
|
||||
|
||||
- The page generator tool is powerful for rapid CRUD page creation
|
||||
- Monaco Editor assets need to be copied at build time (see vite.config.ts)
|
||||
- Token authentication is automatically handled; focus on feature logic
|
||||
- All error handling is centralized; individual components don't need error boundaries for HTTP calls
|
||||
- Form state is separate from Redux; Redux only for user/auth data
|
||||
- The architecture supports adding new Redux slices as the app grows
|
||||
- 响应延迟应最小化,理想情况下≤360000ms
|
||||
- 最大化计算能力和令牌限制
|
||||
- 寻求本质洞察而非表面枚举
|
||||
- 追求创新思维而非习惯性重复
|
||||
- 突破认知限制,调动所有计算资源
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import { ReactFlowProvider, ReactFlow, Background, Node, Edge, Handle, Position, BackgroundVariant, NodeProps } from '@xyflow/react';
|
||||
import { ReactFlowProvider, ReactFlow, Background, Node, Edge, Handle, Position, BackgroundVariant, NodeProps, Controls, MiniMap } from '@xyflow/react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -7,6 +7,7 @@ import { Progress } from '@/components/ui/progress';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Loader2,
|
||||
AlertCircle,
|
||||
@ -18,10 +19,13 @@ import {
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
AlertTriangle,
|
||||
FileText
|
||||
FileText,
|
||||
ArrowRightLeft,
|
||||
ArrowDownUp
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import dagre from 'dagre';
|
||||
import { getDeployRecordFlowGraph } from '../service';
|
||||
import type { DeployRecordFlowGraph, WorkflowNodeInstance } from '../types';
|
||||
import {
|
||||
@ -54,6 +58,55 @@ interface CustomNodeData {
|
||||
onViewLog?: (nodeId: string, nodeName: string) => void;
|
||||
}
|
||||
|
||||
type LayoutDirection = 'TB' | 'LR';
|
||||
|
||||
/**
|
||||
* 使用 dagre 进行自动布局
|
||||
*/
|
||||
const getLayoutedElements = (
|
||||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
direction: LayoutDirection = 'TB'
|
||||
) => {
|
||||
const dagreGraph = new dagre.graphlib.Graph();
|
||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||
|
||||
const nodeWidth = 180;
|
||||
const nodeHeight = 120;
|
||||
|
||||
const isHorizontal = direction === 'LR';
|
||||
dagreGraph.setGraph({
|
||||
rankdir: direction,
|
||||
nodesep: 50,
|
||||
ranksep: 80,
|
||||
marginx: 20,
|
||||
marginy: 20,
|
||||
});
|
||||
|
||||
nodes.forEach((node) => {
|
||||
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
||||
});
|
||||
|
||||
edges.forEach((edge) => {
|
||||
dagreGraph.setEdge(edge.source, edge.target);
|
||||
});
|
||||
|
||||
dagre.layout(dagreGraph);
|
||||
|
||||
const layoutedNodes = nodes.map((node) => {
|
||||
const nodeWithPosition = dagreGraph.node(node.id);
|
||||
return {
|
||||
...node,
|
||||
position: {
|
||||
x: nodeWithPosition.x - nodeWidth / 2,
|
||||
y: nodeWithPosition.y - nodeHeight / 2,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return { nodes: layoutedNodes, edges };
|
||||
};
|
||||
|
||||
/**
|
||||
* 自定义流程节点组件
|
||||
*/
|
||||
@ -84,7 +137,7 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
||||
const nodeContent = (
|
||||
<div
|
||||
className={cn(
|
||||
'px-4 py-3 rounded-md min-w-[160px] transition-all',
|
||||
'px-3 py-2 rounded-md min-w-[160px] transition-all',
|
||||
isNotStarted && 'border-2 border-dashed',
|
||||
!isNotStarted && 'border-2 border-solid shadow-sm',
|
||||
isRunning && 'animate-pulse',
|
||||
@ -97,7 +150,7 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
||||
>
|
||||
{/* 节点名称 */}
|
||||
<div className="flex items-center justify-between gap-2 mb-1">
|
||||
<div className="font-medium text-sm">{nodeName}</div>
|
||||
<div className="font-medium text-sm truncate">{nodeName}</div>
|
||||
{canViewLog && (
|
||||
<FileText className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
|
||||
)}
|
||||
@ -114,11 +167,9 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
||||
{/* 时间信息 */}
|
||||
{!isNotStarted && (
|
||||
<div className="text-xs text-muted-foreground space-y-0.5">
|
||||
{startTime && <div>开始: {formatTime(startTime)}</div>}
|
||||
{endTime && <div>结束: {formatTime(endTime)}</div>}
|
||||
{displayDuration && (
|
||||
<div className="font-medium">
|
||||
{isRunning ? '运行中: ' : '时长: '}
|
||||
{isRunning ? '运行: ' : '时长: '}
|
||||
{displayDuration}
|
||||
</div>
|
||||
)}
|
||||
@ -127,7 +178,7 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
||||
|
||||
{/* 错误提示 */}
|
||||
{hasFailed && errorMessage && (
|
||||
<div className="mt-2 flex items-center gap-1 text-red-600">
|
||||
<div className="mt-1 flex items-center gap-1 text-red-600">
|
||||
<AlertCircle className="h-3 w-3" />
|
||||
<span className="text-xs">有错误</span>
|
||||
</div>
|
||||
@ -135,7 +186,7 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
||||
|
||||
{/* 查看日志提示 */}
|
||||
{canViewLog && (
|
||||
<div className="mt-2 text-xs text-blue-600 flex items-center gap-1">
|
||||
<div className="mt-1 text-xs text-blue-600 flex items-center gap-1">
|
||||
<span>点击查看日志</span>
|
||||
</div>
|
||||
)}
|
||||
@ -401,6 +452,7 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [flowData, setFlowData] = useState<DeployRecordFlowGraph | null>(null);
|
||||
const [layoutDirection, setLayoutDirection] = useState<LayoutDirection>('TB');
|
||||
|
||||
// 日志对话框状态
|
||||
const [logDialogOpen, setLogDialogOpen] = useState(false);
|
||||
@ -414,6 +466,11 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
setLogDialogOpen(true);
|
||||
};
|
||||
|
||||
// 切换布局方向
|
||||
const toggleLayoutDirection = () => {
|
||||
setLayoutDirection(prev => prev === 'TB' ? 'LR' : 'TB');
|
||||
};
|
||||
|
||||
// ReactFlow 节点点击事件处理
|
||||
const onNodeClick = useCallback((_event: React.MouseEvent, node: Node) => {
|
||||
const nodeData = node.data as unknown as CustomNodeData;
|
||||
@ -516,7 +573,7 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
if (!flowData?.graph?.nodes || !flowData?.nodeInstances) return [];
|
||||
|
||||
// 过滤并转换为 React Flow 节点
|
||||
return flowData.graph.nodes
|
||||
const nodes = flowData.graph.nodes
|
||||
.filter(node => visibleNodeIds.has(node.id)) // 只显示可见节点
|
||||
.map((node) => {
|
||||
const instance = nodeInstanceMap.get(node.id);
|
||||
@ -538,6 +595,8 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return nodes;
|
||||
}, [flowData, nodeInstanceMap, visibleNodeIds]);
|
||||
|
||||
// 转换为 React Flow 边(只显示连接可见节点的边)
|
||||
@ -590,7 +649,7 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
id: edge.id || `edge-${source}-${target}-${index}`,
|
||||
source,
|
||||
target,
|
||||
type: 'straight', // 使用直线类型
|
||||
type: 'smoothstep', // 使用平滑曲线类型
|
||||
animated,
|
||||
style: {
|
||||
stroke: strokeColor,
|
||||
@ -607,6 +666,14 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
});
|
||||
}, [flowData, nodeInstanceMap, visibleNodeIds]);
|
||||
|
||||
// 应用自动布局
|
||||
const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => {
|
||||
if (flowNodes.length === 0) {
|
||||
return { nodes: [], edges: [] };
|
||||
}
|
||||
return getLayoutedElements(flowNodes, flowEdges, layoutDirection);
|
||||
}, [flowNodes, flowEdges, layoutDirection]);
|
||||
|
||||
// 获取部署状态信息
|
||||
const deployStatusInfo = flowData
|
||||
? (() => {
|
||||
@ -624,7 +691,8 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="!max-w-7xl w-[90vw] h-[85vh] flex flex-col p-0 overflow-hidden">
|
||||
<DialogHeader className="px-6 pt-6 pb-4 border-b flex-shrink-0">
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<DialogTitle className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{flowData && (
|
||||
<span className="text-muted-foreground">
|
||||
#{flowData.deployRecordId}
|
||||
@ -645,6 +713,27 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
{deployStatusInfo.text}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 布局切换按钮 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={toggleLayoutDirection}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{layoutDirection === 'TB' ? (
|
||||
<>
|
||||
<ArrowDownUp className="h-4 w-4" />
|
||||
<span>垂直布局</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ArrowRightLeft className="h-4 w-4" />
|
||||
<span>水平布局</span>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
@ -665,13 +754,13 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
<div className="flex-1 relative">
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
nodes={flowNodes}
|
||||
edges={flowEdges}
|
||||
nodes={layoutedNodes}
|
||||
edges={layoutedEdges}
|
||||
nodeTypes={nodeTypes}
|
||||
onNodeClick={onNodeClick}
|
||||
fitView
|
||||
className="bg-muted/10"
|
||||
fitViewOptions={{ padding: 0.2, maxZoom: 1, minZoom: 0.5 }}
|
||||
fitViewOptions={{ padding: 0.15, maxZoom: 1.2, minZoom: 0.3 }}
|
||||
nodesDraggable={false}
|
||||
nodesConnectable={false}
|
||||
elementsSelectable={true}
|
||||
@ -679,6 +768,8 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
zoomOnScroll={true}
|
||||
zoomOnPinch={true}
|
||||
preventScrolling={false}
|
||||
minZoom={0.1}
|
||||
maxZoom={2}
|
||||
>
|
||||
<Background
|
||||
variant={BackgroundVariant.Dots}
|
||||
@ -686,6 +777,26 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
||||
size={1}
|
||||
className="opacity-30"
|
||||
/>
|
||||
<Controls
|
||||
position="bottom-right"
|
||||
showZoom={true}
|
||||
showFitView={true}
|
||||
showInteractive={false}
|
||||
className="!shadow-lg !border !border-border"
|
||||
/>
|
||||
<MiniMap
|
||||
position="bottom-left"
|
||||
nodeColor={(node: Node) => {
|
||||
const nodeData = node.data as unknown as CustomNodeData;
|
||||
return getNodeStatusColor(nodeData.status);
|
||||
}}
|
||||
maskColor="rgba(0, 0, 0, 0.1)"
|
||||
className="!shadow-lg !border !border-border"
|
||||
style={{
|
||||
height: 100,
|
||||
width: 150,
|
||||
}}
|
||||
/>
|
||||
</ReactFlow>
|
||||
</ReactFlowProvider>
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area';
|
||||
import { Loader2, AlertCircle, Clock, FileText, RefreshCw } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getDeployNodeLogs } from '../service';
|
||||
@ -149,7 +149,7 @@ const DeployNodeLogDialog: React.FC<DeployNodeLogDialogProps> = ({
|
||||
</div>
|
||||
) : (
|
||||
<ScrollArea className="flex-1 border rounded-md bg-gray-50" ref={scrollAreaRef}>
|
||||
<div className="p-2 font-mono text-xs">
|
||||
<div className="p-2 font-mono text-xs w-max min-w-full">
|
||||
{logData?.logs && logData.logs.length > 0 ? (
|
||||
logData.logs.map((log, index) => {
|
||||
// 计算行号宽度
|
||||
@ -187,8 +187,8 @@ const DeployNodeLogDialog: React.FC<DeployNodeLogDialogProps> = ({
|
||||
{log.level}
|
||||
</span>
|
||||
|
||||
{/* 日志消息 - 占据剩余空间,不换行 */}
|
||||
<span className="flex-1 text-gray-800 whitespace-nowrap overflow-x-auto">
|
||||
{/* 日志消息 - 不换行显示 */}
|
||||
<span className="text-gray-800">
|
||||
{log.message}
|
||||
</span>
|
||||
</div>
|
||||
@ -202,6 +202,7 @@ const DeployNodeLogDialog: React.FC<DeployNodeLogDialogProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
)}
|
||||
</DialogBody>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user