366 lines
10 KiB
Markdown
366 lines
10 KiB
Markdown
# Design Document: 代码拆分重构
|
||
|
||
## Overview
|
||
|
||
本设计文档描述如何将三个大型代码文件拆分为更小、更可维护的模块。采用 React 自定义 Hooks 和组件拆分的方式,遵循单一职责原则。
|
||
|
||
## Architecture
|
||
|
||
```
|
||
components/
|
||
├── upload/ # 上传相关组件(已有部分)
|
||
│ ├── UploadModal.tsx # 主组件(重构后)
|
||
│ ├── UploadForm.tsx # 新增:表单字段组件
|
||
│ ├── SpecialBatchUploadUI.tsx # 新增:特殊批量上传UI
|
||
│ └── ... (已有文件保持不变)
|
||
├── admin/ # 新增:管理后台组件
|
||
│ ├── UserManagement.tsx
|
||
│ ├── MaterialManagement.tsx
|
||
│ ├── CategoryManagement.tsx
|
||
│ ├── SystemSettings.tsx
|
||
│ └── AdminStats.tsx
|
||
└── home/ # 新增:首页组件
|
||
├── FilterPanel.tsx
|
||
├── MaterialGrid.tsx
|
||
├── MaterialDetailModal.tsx
|
||
├── SearchBar.tsx
|
||
└── Pagination.tsx
|
||
|
||
hooks/
|
||
├── useUploadState.ts # 新增:上传状态管理
|
||
├── useCategoryData.ts # 新增:分类数据获取
|
||
├── useBatchUpload.ts # 新增:批量上传逻辑
|
||
├── useDragHandlers.ts # 新增:拖拽处理
|
||
├── useAdminState.ts # 新增:管理后台状态
|
||
├── useHomeState.ts # 新增:首页状态
|
||
└── useMaterials.ts # 新增:素材数据获取
|
||
```
|
||
|
||
## Components and Interfaces
|
||
|
||
### 1. UploadModal 拆分
|
||
|
||
#### useUploadState Hook
|
||
```typescript
|
||
// hooks/useUploadState.ts
|
||
interface UploadState {
|
||
// 文件状态
|
||
previewImages: File[]
|
||
previewUrls: string[]
|
||
displayImage: File | null
|
||
displayImageUrl: string
|
||
modelImages: File[]
|
||
modelImageUrls: string[]
|
||
zipFile: File | null
|
||
|
||
// 表单状态
|
||
productModel: string
|
||
categoryName: string
|
||
sceneName: string
|
||
platformName: string
|
||
productCategoryName: string
|
||
materialType: string
|
||
renderer: string
|
||
|
||
// 上传状态
|
||
uploading: boolean
|
||
uploadProgress: number
|
||
uploadSpeed: number
|
||
isProcessing: boolean
|
||
error: string
|
||
|
||
// 批量上传状态
|
||
batchFiles: BatchFileItem[]
|
||
isBatchMode: boolean
|
||
batchUploadStatus: BatchUploadStatus | null
|
||
}
|
||
|
||
export function useUploadState(): {
|
||
state: UploadState
|
||
actions: UploadStateActions
|
||
}
|
||
```
|
||
|
||
#### useCategoryData Hook
|
||
```typescript
|
||
// hooks/useCategoryData.ts
|
||
interface CategoryData {
|
||
categories: Array<{ id: string; name: string }>
|
||
scenes: Array<{ id: string; name: string }>
|
||
platforms: Array<{ id: string; name: string }>
|
||
productCategories: Array<{ id: string; name: string }>
|
||
}
|
||
|
||
export function useCategoryData(): {
|
||
data: CategoryData
|
||
loading: boolean
|
||
refresh: () => Promise<void>
|
||
}
|
||
```
|
||
|
||
#### useBatchUpload Hook
|
||
```typescript
|
||
// hooks/useBatchUpload.ts
|
||
export function useBatchUpload(materialType: string): {
|
||
// 特殊类型批量文件
|
||
batchMusicFiles: Array<{ file: File; productModel: string }>
|
||
batchVideoFiles: Array<{ file: File; productModel: string }>
|
||
batch3DFiles: Array<{ file: File; productModel: string; previewImage?: File }>
|
||
batchBrandVIFiles: Array<{ file: File; productModel: string }>
|
||
batchBuyerShowFiles: Array<{ productModel: string; images: File[] }>
|
||
batchXiaohongshuFiles: Array<{ productModel: string; images: File[] }>
|
||
|
||
// 操作函数
|
||
handleBatchFileSelect: (e: React.ChangeEvent<HTMLInputElement>) => void
|
||
handleBatchMusicUpload: () => Promise<void>
|
||
handleCancelBatchUpload: () => Promise<void>
|
||
// ... 其他批量上传函数
|
||
}
|
||
```
|
||
|
||
#### useDragHandlers Hook
|
||
```typescript
|
||
// hooks/useDragHandlers.ts
|
||
export function useDragHandlers(): {
|
||
isDragging: boolean
|
||
isPreviewDragging: boolean
|
||
isDisplayImageDragging: boolean
|
||
|
||
handleDragEnter: (e: React.DragEvent) => void
|
||
handleDragLeave: (e: React.DragEvent) => void
|
||
handleDragOver: (e: React.DragEvent) => void
|
||
handleDrop: (e: React.DragEvent) => void
|
||
handlePreviewDrop: (e: React.DragEvent) => void
|
||
handleDisplayImageDrop: (e: React.DragEvent) => void
|
||
}
|
||
```
|
||
|
||
#### UploadForm Component
|
||
```typescript
|
||
// components/upload/UploadForm.tsx
|
||
interface UploadFormProps {
|
||
materialType: string
|
||
productModel: string
|
||
categoryName: string
|
||
sceneName: string
|
||
platformName: string
|
||
productCategoryName: string
|
||
renderer: string
|
||
categories: Array<{ id: string; name: string }>
|
||
scenes: Array<{ id: string; name: string }>
|
||
platforms: Array<{ id: string; name: string }>
|
||
productCategories: Array<{ id: string; name: string }>
|
||
onProductModelChange: (value: string) => void
|
||
onCategoryChange: (value: string) => void
|
||
onSceneChange: (value: string) => void
|
||
onPlatformChange: (value: string) => void
|
||
onProductCategoryChange: (value: string) => void
|
||
onRendererChange: (value: string) => void
|
||
onAddCategory: (name: string) => Promise<void>
|
||
onAddScene: (name: string) => Promise<void>
|
||
}
|
||
```
|
||
|
||
### 2. AdminPage 拆分
|
||
|
||
#### useAdminState Hook
|
||
```typescript
|
||
// hooks/useAdminState.ts
|
||
interface AdminState {
|
||
activeTab: 'materials' | 'users' | 'categories' | 'departments' | 'permissions' | 'superAdmins'
|
||
materials: Material[]
|
||
users: User[]
|
||
categories: Category[]
|
||
scenes: Scene[]
|
||
platforms: Platform[]
|
||
productCategories: ProductCategory[]
|
||
loading: boolean
|
||
// ... 其他状态
|
||
}
|
||
|
||
export function useAdminState(): {
|
||
state: AdminState
|
||
actions: AdminStateActions
|
||
}
|
||
```
|
||
|
||
#### Admin Components
|
||
```typescript
|
||
// components/admin/UserManagement.tsx
|
||
interface UserManagementProps {
|
||
users: User[]
|
||
departments: Department[]
|
||
onUpdateUser: (userId: string, data: Partial<User>) => Promise<void>
|
||
onDeleteUser: (userId: string) => Promise<void>
|
||
}
|
||
|
||
// components/admin/MaterialManagement.tsx
|
||
interface MaterialManagementProps {
|
||
materials: Material[]
|
||
showRecycleBin: boolean
|
||
onDelete: (ids: string[]) => Promise<void>
|
||
onRestore: (ids: string[]) => Promise<void>
|
||
onSearch: (keyword: string) => void
|
||
}
|
||
|
||
// components/admin/CategoryManagement.tsx
|
||
interface CategoryManagementProps {
|
||
categories: Category[]
|
||
scenes: Scene[]
|
||
platforms: Platform[]
|
||
productCategories: ProductCategory[]
|
||
onAdd: (type: string, name: string) => Promise<void>
|
||
onEdit: (type: string, id: string, name: string) => Promise<void>
|
||
onDelete: (type: string, id: string) => Promise<void>
|
||
}
|
||
```
|
||
|
||
### 3. HomePage 拆分
|
||
|
||
#### useHomeState Hook
|
||
```typescript
|
||
// hooks/useHomeState.ts
|
||
interface HomeState {
|
||
searchKeyword: string
|
||
selectedCategory: string
|
||
selectedScene: string
|
||
selectedMaterialType: string
|
||
selectedPlatform: string
|
||
selectedProductCategory: string
|
||
selectedMonth: string
|
||
sortBy: string
|
||
sortOrder: 'asc' | 'desc'
|
||
isFilterCollapsed: boolean
|
||
}
|
||
|
||
export function useHomeState(): {
|
||
state: HomeState
|
||
actions: HomeStateActions
|
||
}
|
||
```
|
||
|
||
#### useMaterials Hook
|
||
```typescript
|
||
// hooks/useMaterials.ts
|
||
interface UseMaterialsOptions {
|
||
searchKeyword: string
|
||
selectedCategory: string
|
||
selectedScene: string
|
||
selectedMaterialType: string
|
||
selectedPlatform: string
|
||
selectedProductCategory: string
|
||
selectedMonth: string
|
||
sortBy: string
|
||
sortOrder: 'asc' | 'desc'
|
||
}
|
||
|
||
export function useMaterials(options: UseMaterialsOptions): {
|
||
materials: Material[]
|
||
loading: boolean
|
||
hasMore: boolean
|
||
totalCount: number
|
||
loadMore: () => Promise<void>
|
||
refresh: () => Promise<void>
|
||
}
|
||
```
|
||
|
||
#### Home Components
|
||
```typescript
|
||
// components/home/FilterPanel.tsx
|
||
interface FilterPanelProps {
|
||
categories: Category[]
|
||
scenes: Scene[]
|
||
platforms: Platform[]
|
||
productCategories: ProductCategory[]
|
||
materialTypes: MaterialType[]
|
||
months: Month[]
|
||
selectedCategory: string
|
||
selectedScene: string
|
||
selectedMaterialType: string
|
||
selectedPlatform: string
|
||
selectedProductCategory: string
|
||
selectedMonth: string
|
||
sortBy: string
|
||
sortOrder: 'asc' | 'desc'
|
||
isCollapsed: boolean
|
||
onCategoryChange: (value: string) => void
|
||
onSceneChange: (value: string) => void
|
||
onMaterialTypeChange: (value: string) => void
|
||
onPlatformChange: (value: string) => void
|
||
onProductCategoryChange: (value: string) => void
|
||
onMonthChange: (value: string) => void
|
||
onSortByChange: (value: string) => void
|
||
onSortOrderChange: (value: 'asc' | 'desc') => void
|
||
onToggleCollapse: () => void
|
||
}
|
||
|
||
// components/home/MaterialGrid.tsx
|
||
interface MaterialGridProps {
|
||
materials: Material[]
|
||
loading: boolean
|
||
onMaterialClick: (material: Material) => void
|
||
onDownload: (material: Material) => void
|
||
onLike: (material: Material) => void
|
||
onFavorite: (material: Material) => void
|
||
onShare: (material: Material) => void
|
||
}
|
||
|
||
// components/home/SearchBar.tsx
|
||
interface SearchBarProps {
|
||
value: string
|
||
onChange: (value: string) => void
|
||
placeholder?: string
|
||
}
|
||
|
||
// components/home/Pagination.tsx
|
||
interface PaginationProps {
|
||
hasMore: boolean
|
||
isLoading: boolean
|
||
onLoadMore: () => void
|
||
}
|
||
```
|
||
|
||
## Data Models
|
||
|
||
数据模型保持不变,继续使用现有的 Prisma 模型和 TypeScript 接口。
|
||
|
||
## Correctness Properties
|
||
|
||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||
|
||
由于本次重构主要是代码组织优化,不涉及业务逻辑变更,因此主要通过以下方式验证正确性:
|
||
|
||
1. **构建验证**: 重构后代码能够成功通过 TypeScript 编译和 Next.js 构建
|
||
2. **行数验证**: 主文件行数符合目标要求
|
||
3. **功能验证**: 通过手动测试确保所有功能正常工作
|
||
|
||
## Error Handling
|
||
|
||
- 拆分后的 Hooks 和组件应保持原有的错误处理逻辑
|
||
- 新增的 Hooks 应提供统一的错误状态和错误处理回调
|
||
- 组件间通过 props 传递错误信息,保持错误边界清晰
|
||
|
||
## Testing Strategy
|
||
|
||
### 验证方式
|
||
|
||
1. **TypeScript 类型检查**: 运行 `npx tsc --noEmit` 确保无类型错误
|
||
2. **ESLint 检查**: 运行 `npm run lint` 确保代码风格一致
|
||
3. **构建测试**: 运行 `npm run build` 确保能够成功构建
|
||
4. **手动功能测试**:
|
||
- 测试上传弹窗的所有上传类型
|
||
- 测试管理后台的所有功能标签页
|
||
- 测试首页的筛选、搜索、分页功能
|
||
|
||
### 回归测试清单
|
||
|
||
- [ ] 单个文件上传(各种素材类型)
|
||
- [ ] 批量文件上传(音乐、视频、3D场景等)
|
||
- [ ] 拖拽上传
|
||
- [ ] 分类/场景/平台筛选
|
||
- [ ] 搜索功能
|
||
- [ ] 分页加载
|
||
- [ ] 用户管理
|
||
- [ ] 素材管理(删除、恢复)
|
||
- [ ] 分类管理(增删改)
|