- 新增 formatUSDC 函数:最多显示4位小数,自动去除尾随零(截断,不四舍五入) - 统一导出所有工具函数到 utils/index.ts - 替换所有页面中的USDC显示为 formatUSDC 函数 - TemplateList.tsx - CopyTradingAdd.tsx - PositionList.tsx - AccountList.tsx - AccountDetail.tsx - OrderList.tsx - Statistics.tsx - 更新前端开发规范,添加USDC格式化规范说明 - 统一工具函数导入方式(从 ../utils 导入)
333 lines
9.1 KiB
Plaintext
333 lines
9.1 KiB
Plaintext
---
|
||
alwaysApply: true
|
||
path: frontend/**
|
||
---
|
||
|
||
# 前端开发规范
|
||
|
||
## 代码完成规范
|
||
|
||
### TODO 处理规则
|
||
- **禁止**在代码中添加 TODO 注释
|
||
- **必须**根据 TODO 的内容直接完成代码实现
|
||
- 如果遇到暂时无法完全实现的功能,应该:
|
||
1. 实现一个可用的基础版本
|
||
2. 添加清晰的注释说明当前实现的限制和后续改进方向
|
||
3. 确保代码可以正常编译和运行
|
||
- **禁止**使用 `// TODO: 实现XXX` 这样的注释
|
||
- **禁止**使用 `// FIXME:` 或 `// XXX:` 这样的注释
|
||
- 如果某个功能需要依赖外部资源(如 API、库等),应该:
|
||
1. 先实现一个占位或模拟实现
|
||
2. 在注释中说明依赖关系和实现方式
|
||
3. 确保代码逻辑完整,不会因为未实现的功能而崩溃
|
||
|
||
### API 调用实现规则
|
||
- **必须**查找相关的 API 文档或接口定义
|
||
- **必须**根据 API 文档完成代码实现
|
||
- **禁止**在 API 调用处添加 TODO 注释
|
||
- 如果 API 文档不完整,应该:
|
||
1. 查找项目中已有的类似 API 调用作为参考
|
||
2. 查看 API 服务定义(如 `services/api.ts`)
|
||
3. 查看 API 文档(如 `docs/copy-trading-requirements.md`)
|
||
4. 实现一个可用的版本,包含错误处理
|
||
|
||
### 代码完成示例
|
||
|
||
```typescript
|
||
// ❌ 错误:添加 TODO 注释
|
||
const fetchAccountBalance = async (accountId: number) => {
|
||
// TODO: 调用 API 查询余额
|
||
return { balance: '0' }
|
||
}
|
||
|
||
// ✅ 正确:查找 API 定义并完成实现
|
||
// 1. 查找 API 服务定义:services/api.ts 中的 accounts.balance
|
||
// 2. 查找 API 文档:docs/copy-trading-requirements.md
|
||
// 3. 实现完整的 API 调用逻辑
|
||
const fetchAccountBalance = async (accountId: number) => {
|
||
try {
|
||
// 根据 API 服务定义调用接口
|
||
const response = await apiService.accounts.balance({ accountId })
|
||
|
||
// 根据 API 响应格式处理数据
|
||
if (response.data.code === 0 && response.data.data) {
|
||
return response.data.data
|
||
} else {
|
||
// API 调用失败时返回默认值
|
||
console.warn('查询余额失败,返回默认值:', response.data.msg)
|
||
return { balance: '0' }
|
||
}
|
||
} catch (error) {
|
||
console.error('查询余额异常:', error)
|
||
// 异常时返回默认值,确保代码可以正常运行
|
||
return { balance: '0' }
|
||
}
|
||
}
|
||
```
|
||
|
||
## 技术栈
|
||
- **框架**: React + TypeScript
|
||
- **UI库**: Ant Design 或 Material-UI(推荐 Ant Design Mobile 用于移动端)
|
||
- **HTTP客户端**: axios
|
||
- **状态管理**: Zustand 或 Redux
|
||
- **响应式设计**: 必须支持移动端和桌面端
|
||
|
||
## 移动端适配要求
|
||
|
||
### 响应式设计
|
||
- **必须支持移动端和桌面端**
|
||
- 使用响应式布局(Responsive Design)
|
||
- 移动端优先(Mobile First)设计原则
|
||
- 支持触摸操作和手势
|
||
|
||
### 断点设置
|
||
- **移动端**: < 768px
|
||
- **平板**: 768px - 1024px
|
||
- **桌面端**: > 1024px
|
||
|
||
### UI 组件适配
|
||
- 使用 Ant Design 的响应式组件
|
||
- 移动端使用 Ant Design Mobile(如果使用 Ant Design)
|
||
- 表格使用虚拟滚动或分页(移动端性能优化)
|
||
- 表单使用移动端友好的输入组件
|
||
|
||
### 布局适配
|
||
- 导航栏:移动端使用抽屉菜单,桌面端使用顶部导航
|
||
- 列表:移动端使用卡片布局,桌面端使用表格布局
|
||
- 按钮:移动端按钮尺寸不小于 44x44px(触摸友好)
|
||
- 间距:移动端使用更大的间距,提高可点击区域
|
||
|
||
### 性能优化
|
||
- 图片懒加载
|
||
- 代码分割(Code Splitting)
|
||
- 移动端减少动画效果
|
||
- 使用 CSS 媒体查询优化样式
|
||
|
||
## 代码规范
|
||
|
||
### 组件规范
|
||
- 使用函数式组件
|
||
- 使用 TypeScript 类型定义
|
||
- 组件文件使用 PascalCase 命名
|
||
|
||
```typescript
|
||
// ✅ 正确
|
||
interface MarketProps {
|
||
marketId: string;
|
||
platform: string;
|
||
}
|
||
|
||
export const MarketCard: React.FC<MarketProps> = ({ marketId, platform }) => {
|
||
// ...
|
||
};
|
||
|
||
// ❌ 错误
|
||
export const marketCard = (props: any) => { // 禁止使用any
|
||
// ...
|
||
};
|
||
```
|
||
|
||
### API调用规范
|
||
- 使用 axios 进行 HTTP 请求
|
||
- 统一错误处理
|
||
- 使用 TypeScript 定义响应类型
|
||
|
||
```typescript
|
||
// API服务
|
||
import axios from 'axios';
|
||
|
||
interface Market {
|
||
id: string;
|
||
marketId: string;
|
||
platform: string;
|
||
title: string;
|
||
}
|
||
|
||
export const marketService = {
|
||
getMarkets: async (): Promise<Market[]> => {
|
||
const response = await axios.get<Market[]>('/api/markets');
|
||
return response.data;
|
||
},
|
||
|
||
getMarketById: async (id: string): Promise<Market> => {
|
||
const response = await axios.get<Market>(`/api/markets/${id}`);
|
||
return response.data;
|
||
}
|
||
};
|
||
```
|
||
|
||
### 状态管理规范
|
||
- 使用 Zustand 或 Redux 管理全局状态
|
||
- 本地状态使用 `useState`
|
||
- 复杂状态使用 `useReducer`
|
||
|
||
```typescript
|
||
// Zustand Store示例
|
||
import { create } from 'zustand';
|
||
|
||
interface MarketStore {
|
||
markets: Market[];
|
||
setMarkets: (markets: Market[]) => void;
|
||
}
|
||
|
||
export const useMarketStore = create<MarketStore>((set) => ({
|
||
markets: [],
|
||
setMarkets: (markets) => set({ markets }),
|
||
}));
|
||
```
|
||
|
||
### USDC 金额格式化规范
|
||
- **必须**使用 `formatUSDC` 函数格式化所有 USDC 金额显示
|
||
- **禁止**直接使用 `toFixed()` 或 `parseFloat().toFixed()` 格式化 USDC
|
||
- **禁止**硬编码小数位数格式化 USDC
|
||
- 所有 USDC 金额显示必须统一使用 `formatUSDC` 函数
|
||
|
||
#### formatUSDC 函数说明
|
||
- **位置**: `src/utils/index.ts`
|
||
- **功能**: 格式化 USDC 金额,最多显示 4 位小数,自动去除尾随零(截断,不四舍五入)
|
||
- **参数**: `value: string | number | undefined | null`
|
||
- **返回值**: 格式化后的字符串,如果值为空或无效则返回 `'-'`
|
||
|
||
#### 使用示例
|
||
|
||
```typescript
|
||
// ✅ 正确:使用 formatUSDC 格式化 USDC 金额
|
||
import { formatUSDC } from '../utils'
|
||
|
||
const BalanceDisplay: React.FC<{ balance: string }> = ({ balance }) => {
|
||
return <span>{formatUSDC(balance)} USDC</span>
|
||
}
|
||
|
||
// ✅ 正确:在表格列中使用
|
||
const columns = [
|
||
{
|
||
title: '余额',
|
||
dataIndex: 'balance',
|
||
render: (balance: string) => `${formatUSDC(balance)} USDC`
|
||
}
|
||
]
|
||
|
||
// ✅ 正确:在统计组件中使用
|
||
<Statistic
|
||
title="总盈亏"
|
||
value={formatUSDC(stats?.totalPnl || '0')}
|
||
suffix="USDC"
|
||
/>
|
||
|
||
// ❌ 错误:直接使用 toFixed
|
||
const balance = parseFloat(value).toFixed(4) // 禁止
|
||
|
||
// ❌ 错误:硬编码格式化
|
||
const balance = `${parseFloat(value).toFixed(2)} USDC` // 禁止
|
||
|
||
// ❌ 错误:使用自定义格式化函数
|
||
const formatBalance = (value: string) => parseFloat(value).toFixed(4) // 禁止
|
||
```
|
||
|
||
#### 格式化规则
|
||
- **最多显示 4 位小数**:如果金额超过 4 位小数,截断到 4 位(不四舍五入)
|
||
- **自动去除尾随零**:去除不必要的尾随零和小数点
|
||
- **示例**:
|
||
- `formatUSDC(1.23)` => `"1.23"`
|
||
- `formatUSDC(1.23456)` => `"1.2345"`(截断,不四舍五入)
|
||
- `formatUSDC(1.2)` => `"1.2"`
|
||
- `formatUSDC(1)` => `"1"`
|
||
- `formatUSDC(1.23459)` => `"1.2345"`(截断,不四舍五入)
|
||
- `formatUSDC(null)` => `"-"`
|
||
- `formatUSDC(undefined)` => `"-"`
|
||
|
||
#### 工具函数统一导出
|
||
- 所有工具函数统一从 `src/utils/index.ts` 导出
|
||
- **必须**从 `../utils` 导入工具函数,而不是从具体文件导入
|
||
- `ethers.ts` 中的函数也会在 `index.ts` 中统一导出
|
||
|
||
```typescript
|
||
// ✅ 正确:从 utils 统一导入
|
||
import { formatUSDC, getAddressFromPrivateKey, isValidWalletAddress } from '../utils'
|
||
|
||
// ❌ 错误:从具体文件导入
|
||
import { formatUSDC } from '../utils/index'
|
||
import { getAddressFromPrivateKey } from '../utils/ethers'
|
||
```
|
||
|
||
## 移动端适配示例
|
||
|
||
### 响应式布局
|
||
```typescript
|
||
import { useMediaQuery } from 'react-responsive';
|
||
|
||
const MyComponent: React.FC = () => {
|
||
const isMobile = useMediaQuery({ maxWidth: 768 });
|
||
|
||
return (
|
||
<div className={isMobile ? 'mobile-layout' : 'desktop-layout'}>
|
||
{isMobile ? <MobileView /> : <DesktopView />}
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
### 移动端导航
|
||
```typescript
|
||
import { Drawer } from 'antd';
|
||
|
||
const MobileNav: React.FC = () => {
|
||
const [open, setOpen] = useState(false);
|
||
|
||
return (
|
||
<>
|
||
<Button onClick={() => setOpen(true)}>菜单</Button>
|
||
<Drawer
|
||
title="导航"
|
||
placement="left"
|
||
onClose={() => setOpen(false)}
|
||
open={open}
|
||
>
|
||
{/* 导航内容 */}
|
||
</Drawer>
|
||
</>
|
||
);
|
||
};
|
||
```
|
||
|
||
### 响应式表格
|
||
```typescript
|
||
import { Table } from 'antd';
|
||
|
||
const ResponsiveTable: React.FC = () => {
|
||
const isMobile = useMediaQuery({ maxWidth: 768 });
|
||
|
||
return (
|
||
<Table
|
||
dataSource={data}
|
||
columns={columns}
|
||
scroll={{ x: isMobile ? 600 : 'auto' }}
|
||
pagination={{
|
||
pageSize: isMobile ? 10 : 20,
|
||
showSizeChanger: !isMobile
|
||
}}
|
||
/>
|
||
);
|
||
};
|
||
```
|
||
|
||
## 禁止事项
|
||
|
||
### 代码质量
|
||
- ❌ 禁止使用 `any` 类型
|
||
- ❌ 禁止忽略错误处理
|
||
- ❌ 禁止硬编码API地址
|
||
- ❌ 禁止在组件中直接使用 `fetch`
|
||
- ❌ 禁止忽略移动端适配
|
||
|
||
### 类型安全
|
||
- ❌ 禁止使用 `any`
|
||
- ❌ 禁止忽略 TypeScript 类型检查
|
||
- ❌ 禁止使用 `@ts-ignore` 除非有明确原因
|
||
|
||
### 移动端适配
|
||
- ❌ 禁止固定宽度布局
|
||
- ❌ 禁止使用过小的触摸目标(< 44x44px)
|
||
- ❌ 禁止忽略移动端性能优化
|
||
- ❌ 禁止使用桌面端专用的交互方式(如 hover)
|