chore: 修复大量导入路径错误
- 迁移 files API 到 shared/api/ - 修复所有 features 中的 @/api/ 导入 - 修复所有 features 中的 @/composables/ 导入 - 更新 useOrderDetail 中的 uploadFile 调用 类型错误:272 → 190 → 135
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
# Features 架构迁移 - 清理工作总结
|
||||
|
||||
**完成时间:** 2026-06-04
|
||||
**分支:** refactor/features-architecture
|
||||
**当前状态:** 清理进行中
|
||||
|
||||
---
|
||||
|
||||
## ✅ 已完成的清理工作
|
||||
|
||||
### 1. 路由配置更新 ✅
|
||||
**提交:** 3d5db9d
|
||||
|
||||
更新了所有路由文件,将 `@/views/` 改为 `@/features/*/views/`:
|
||||
- accountRoutes.ts - 10个路由
|
||||
- publicRoutes.ts - 2个路由
|
||||
- mobileRoutes.ts - 13个路由
|
||||
- sellerRoutes.ts - 4个路由
|
||||
- adminRoutes.ts - 15个路由
|
||||
- router/index.ts - 更新 authStorage 导入
|
||||
|
||||
**总计:** 44个路由已更新
|
||||
|
||||
---
|
||||
|
||||
### 2. 删除旧文件 ✅
|
||||
**提交:** 5e87858
|
||||
|
||||
删除了旧的目录结构:
|
||||
- ❌ api/ - 21个文件
|
||||
- ❌ views/ - 57个文件
|
||||
- ❌ composables/ - 33个文件
|
||||
|
||||
**总计:** 111个旧文件已删除,33,317行代码移除
|
||||
|
||||
---
|
||||
|
||||
### 3. 批量更新导入路径 ✅
|
||||
**提交:** 待提交
|
||||
|
||||
更新了所有 features 中的导入路径:
|
||||
- 8个 admin API 文件
|
||||
- 所有 admin 组件和视图
|
||||
- 所有 features 中的 Vue 文件
|
||||
- ChatAttachmentImage 组件
|
||||
|
||||
---
|
||||
|
||||
## 📊 清理进度
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 更新路由配置 | ✅ | 44个路由全部更新 |
|
||||
| 删除旧文件 | ✅ | 111个文件已删除 |
|
||||
| 更新导入路径 | 🔄 | 大部分完成,剩余~190个错误 |
|
||||
| 类型检查 | 🔄 | 从272个错误降到190个 |
|
||||
| 功能测试 | ⏳ | 待完成 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 剩余工作
|
||||
|
||||
### 类型错误修复(~190个)
|
||||
|
||||
主要问题类型:
|
||||
|
||||
#### 1. 缺失的模块
|
||||
- `@/shared/api/files` - files API 未迁移到 shared
|
||||
- `@/api/auth` - 部分文件还在使用旧路径
|
||||
- `@/api/wallet` - 部分文件还在使用旧路径
|
||||
- `@/api/realname` - 部分文件还在使用旧路径
|
||||
|
||||
#### 2. Composables 导入
|
||||
- `@/composables/useAdminTable` → `@/features/admin/composables/`
|
||||
- `@/composables/useMoney` → `@/shared/composables/`
|
||||
- `@/composables/useSmsCountdown` → `@/shared/composables/`
|
||||
|
||||
#### 3. 相对路径问题
|
||||
- admin 组件之间的导入需要使用相对路径
|
||||
- auth API 内部导入路径
|
||||
|
||||
#### 4. 类型导出冲突
|
||||
- `features/admin/index.ts` 中 AdminRole 重复导出
|
||||
|
||||
---
|
||||
|
||||
## 📁 当前目录结构
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── features/ ✅ 新架构
|
||||
│ ├── wallet/
|
||||
│ ├── chats/
|
||||
│ ├── orders/
|
||||
│ ├── listings/
|
||||
│ ├── auth/
|
||||
│ ├── seller/
|
||||
│ ├── disputes/
|
||||
│ └── admin/
|
||||
├── shared/ ✅ 共享层
|
||||
├── components/ ✅ 保留(全局组件)
|
||||
├── layouts/ ✅ 保留(布局模板)
|
||||
├── router/ ✅ 已更新
|
||||
├── stores/ ✅ 保留(全局状态)
|
||||
├── styles/ ✅ 保留(全局样式)
|
||||
├── types/ ✅ 保留(全局类型)
|
||||
└── utils/ ✅ 保留(工具函数)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Git 提交历史
|
||||
|
||||
1. **3d5db9d** - chore: 更新路由配置,使用 features 架构路径
|
||||
2. **5e87858** - chore: 删除旧的 api/, views/, composables/ 目录
|
||||
3. **待提交** - chore: 批量更新导入路径到 features 架构
|
||||
|
||||
---
|
||||
|
||||
## ⏭️ 下一步计划
|
||||
|
||||
### 1. 完成类型错误修复
|
||||
- 迁移 files API 到 shared
|
||||
- 修复所有剩余导入路径
|
||||
- 解决类型导出冲突
|
||||
- 目标:0个类型错误
|
||||
|
||||
### 2. 功能测试
|
||||
```bash
|
||||
npm run dev
|
||||
# 测试各个模块:
|
||||
# - 首页浏览
|
||||
# - 登录/注册
|
||||
# - 订单创建
|
||||
# - 聊天功能
|
||||
# - 管理后台
|
||||
```
|
||||
|
||||
### 3. 更新文档
|
||||
- 更新 README.md
|
||||
- 添加新架构说明
|
||||
- 更新开发指南
|
||||
|
||||
### 4. 创建 PR
|
||||
- 推送到远程
|
||||
- 创建 Pull Request
|
||||
- 等待团队审核
|
||||
|
||||
---
|
||||
|
||||
**当前状态:** 清理工作 70% 完成
|
||||
**预计剩余时间:** ~1小时
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
Warning
|
||||
} from '@element-plus/icons-vue'
|
||||
import { fetchAdminDashboard, type AdminDashboard } from '@/features/admin/api/adminDashboard'
|
||||
import { useAdminTable } from '@/composables/useAdminTable'
|
||||
import { useMoney } from '@/composables/useMoney'
|
||||
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||
import { useMoney } from '@/shared/composables/useMoney'
|
||||
import { disputeStatusLabel, orderStatusLabel } from '@/utils/statusLabels'
|
||||
import { formatDateTime } from '@/utils/time'
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import { ElMessage } from 'element-plus'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
|
||||
import { fetchAdminListings, type AdminListingPage, type AdminListingQuery, type Listing } from '@/features/listings'
|
||||
import { useAdminTable } from '@/composables/useAdminTable'
|
||||
import { useMoney } from '@/composables/useMoney'
|
||||
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||
import { useMoney } from '@/shared/composables/useMoney'
|
||||
import {
|
||||
assetRegions,
|
||||
formatEstimatedRentalDuration,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { fetchAdminMgrUsers, deleteAdminMgrUser, changeAdminPassword, type AdminMgrUser } from '@/features/admin/api/adminMgr'
|
||||
import { useAdminPaginatedTable } from '@/composables/useAdminPaginatedTable'
|
||||
import { useAdminPaginatedTable } from '@/features/admin/composables/useAdminPaginatedTable'
|
||||
import { formatDateTime } from '@/utils/time'
|
||||
|
||||
import AdminUserDialog from './components/AdminUserDialog.vue'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { fetchAdminOrders, type Order } from '@/features/orders'
|
||||
import { useAdminTable } from '@/composables/useAdminTable'
|
||||
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||
import { handoffStatusLabel, orderStatusLabel, settlementStatusLabel } from '@/utils/statusLabels'
|
||||
import { formatDateTime } from '@/utils/time'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { fetchRoles, deleteRole, type Role } from '@/features/admin/api/adminRoles'
|
||||
import { useAdminTable } from '@/composables/useAdminTable'
|
||||
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||
|
||||
import AssignPermissionsDialog from './components/AssignPermissionsDialog.vue'
|
||||
import RoleDialog from './components/RoleDialog.vue'
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ElMessage } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { fetchAdminUsers, freezeAdminUser, unfreezeAdminUser, type AdminUserItem } from '@/features/admin/api/adminUsers'
|
||||
import { useAdminPaginatedTable } from '@/composables/useAdminPaginatedTable'
|
||||
import { useAdminPaginatedTable } from '@/features/admin/composables/useAdminPaginatedTable'
|
||||
import { userStatusLabel } from '@/utils/statusLabels'
|
||||
import { formatDateTime } from '@/utils/time'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { onBeforeUnmount, ref, type Ref } from 'vue'
|
||||
import { refreshAccessToken } from '@/api/client'
|
||||
import { refreshAccessToken } from '@/shared/api/client'
|
||||
import { getAccessToken, type AuthScope } from '@/utils/authStorage'
|
||||
|
||||
export interface SSEMessage {
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
sendChatMessage,
|
||||
type ChatConversation,
|
||||
type ChatMessage,
|
||||
} from '@/api/chats'
|
||||
} from '@/features/chats/api/chats'
|
||||
import { uploadFile } from '@/shared/api/files'
|
||||
import ChatAttachmentImage from '@/components/ChatAttachmentImage.vue'
|
||||
import { useChatSSE, type ChatEvent } from '@/composables/useChatSSE'
|
||||
import { useChatSSE, type ChatEvent } from '@/features/chats/composables/useChatSSE'
|
||||
import { formatDateMinute } from '@/utils/time'
|
||||
|
||||
const currentUserId = Number(localStorage.getItem('user_id') || 0)
|
||||
|
||||
@@ -3,8 +3,8 @@ import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ChatDotRound, Refresh, Tickets } from '@element-plus/icons-vue'
|
||||
import { fetchChats, type ChatConversation } from '@/api/chats'
|
||||
import { useChatSSE, type ChatEvent } from '@/composables/useChatSSE'
|
||||
import { fetchChats, type ChatConversation } from '@/features/chats/api/chats'
|
||||
import { useChatSSE, type ChatEvent } from '@/features/chats/composables/useChatSSE'
|
||||
import { formatDateMinute } from '@/utils/time'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
sendChatMessage,
|
||||
type ChatConversation,
|
||||
type ChatMessage,
|
||||
} from '@/api/chats'
|
||||
} from '@/features/chats/api/chats'
|
||||
import { uploadFile } from '@/shared/api/files'
|
||||
import ChatAttachmentImage from '@/components/ChatAttachmentImage.vue'
|
||||
import { useChatSSE, type ChatEvent } from '@/composables/useChatSSE'
|
||||
import { useChatSSE, type ChatEvent } from '@/features/chats/composables/useChatSSE'
|
||||
import { formatDateMinute } from '@/utils/time'
|
||||
|
||||
const currentUserId = Number(localStorage.getItem('user_id') || 0)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast } from 'vant'
|
||||
import MobileBottomNav from '@/components/MobileBottomNav.vue'
|
||||
import { fetchChats, type ChatConversation } from '@/api/chats'
|
||||
import { fetchChats, type ChatConversation } from '@/features/chats/api/chats'
|
||||
import { formatDateMinute } from '@/utils/time'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
moneyRangeOptions,
|
||||
totalRangeOptions,
|
||||
fireLevelRangeOptions,
|
||||
} from '@/composables/home/useFilterOptions'
|
||||
} from '@/features/listings/composables/useFilterOptions'
|
||||
import type { ListingPublishOptions } from '@/features/listings/api/listingOptions'
|
||||
import type { FilterPopoverKey, HomeFilters } from '@/composables/home/useHomeFilters'
|
||||
import type { FilterPopoverKey, HomeFilters } from '@/features/listings/composables/useHomeFilters'
|
||||
|
||||
interface Props {
|
||||
filters: HomeFilters
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import { ElInputNumber, ElPopover } from 'element-plus'
|
||||
import { computed } from 'vue'
|
||||
import { rangeLabel, isRangeSelected } from '@/composables/home/useFilterOptions'
|
||||
import { rangeLabel, isRangeSelected } from '@/features/listings/composables/useFilterOptions'
|
||||
|
||||
interface RangeOption {
|
||||
label: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { rangeLabel, isRangeSelected } from '@/composables/home/useFilterOptions'
|
||||
import { rangeLabel, isRangeSelected } from '@/features/listings/composables/useFilterOptions'
|
||||
|
||||
describe('useFilterOptions', () => {
|
||||
describe('rangeLabel', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { useHomeFilters } from '@/composables/home/useHomeFilters'
|
||||
import { useHomeFilters } from '@/features/listings/composables/useHomeFilters'
|
||||
import { ref } from 'vue'
|
||||
import { emptyListingPublishOptions } from '@/api/listingOptions'
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
defaultHomeBanners,
|
||||
fetchMobileHomeConfig,
|
||||
type HomeBannerSlide,
|
||||
} from '@/api/homeConfig'
|
||||
} from '@/features/listings/api/homeConfig'
|
||||
import {
|
||||
emptyListingPublishOptions,
|
||||
type ListingPublishOptions,
|
||||
} from '@/features/listings/api/listingOptions'
|
||||
import { useHomeFilters } from '@/composables/home/useHomeFilters'
|
||||
import { useListingQuery } from '@/composables/home/useListingQuery'
|
||||
import { useHomeFilters } from '@/features/listings/composables/useHomeFilters'
|
||||
import { useListingQuery } from '@/features/listings/composables/useListingQuery'
|
||||
import HomeAnnouncement from './components/HomeAnnouncement.vue'
|
||||
import HomeBanner from './components/HomeBanner.vue'
|
||||
import HomeStats from './components/HomeStats.vue'
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
submitHandoff,
|
||||
confirmReceive,
|
||||
} from '../api/orders'
|
||||
import { createDispute } from '@/api/disputes'
|
||||
import { uploadFile } from '@/api/files'
|
||||
import { fetchOrderChat } from '@/api/chats'
|
||||
import { createDispute } from '@/features/disputes/api/disputes'
|
||||
import { uploadFile } from '@/shared/api/files'
|
||||
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePaymentPolling } from './usePaymentPolling'
|
||||
import { useSettlement } from './useSettlement'
|
||||
@@ -147,7 +147,7 @@ export function useOrderDetail() {
|
||||
async function handleUploadEvidence(file: File) {
|
||||
uploadingEvidence.value = true
|
||||
try {
|
||||
const result = await uploadFile(file)
|
||||
const result = await uploadFile(file, 'evidence')
|
||||
return result.url
|
||||
} finally {
|
||||
uploadingEvidence.value = false
|
||||
|
||||
@@ -5,7 +5,7 @@ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import QRCode from 'qrcode'
|
||||
|
||||
import { fetchOrderChat } from '@/api/chats'
|
||||
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||
import { createDispute } from '@/features/disputes'
|
||||
import { uploadFile } from '@/shared/api/files'
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { fetchFileBlobByURL, uploadFile } from '@/api/files'
|
||||
import { fetchFileBlobByURL, uploadFile } from '@/shared/api/files'
|
||||
import {
|
||||
emptyListingPublishOptions,
|
||||
emptyListingSalePriceConfig,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { apiClient } from './client'
|
||||
import type { ApiResponse } from '@/shared/types/types'
|
||||
import { optimizeImageForUpload } from '@/shared/utils/imageUpload'
|
||||
|
||||
export interface UploadedFile {
|
||||
object_key: string
|
||||
url: string
|
||||
thumbnail_url?: string
|
||||
medium_url?: string
|
||||
filename: string
|
||||
content_type: string
|
||||
size: number
|
||||
}
|
||||
|
||||
export async function uploadFile(file: File, scene: string) {
|
||||
const uploadTarget = await optimizeImageForUpload(file, scene)
|
||||
const form = new FormData()
|
||||
form.append('file', uploadTarget)
|
||||
form.append('scene', scene)
|
||||
const { data } = await apiClient.post<ApiResponse<UploadedFile>>('/files/upload', form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function uploadAdminFile(file: File, scene: string) {
|
||||
const uploadTarget = await optimizeImageForUpload(file, scene)
|
||||
const form = new FormData()
|
||||
form.append('file', uploadTarget)
|
||||
form.append('scene', scene)
|
||||
const { data } = await apiClient.post<ApiResponse<UploadedFile>>('/admin/files/upload', form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function fetchAdminFileBlob(key: string) {
|
||||
const { data } = await apiClient.get<Blob>('/admin/files/object', {
|
||||
params: { key },
|
||||
responseType: 'blob',
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchFileBlobByURL(fileURL: string) {
|
||||
const apiPath = fileURL.startsWith('/api/') ? fileURL.slice(4) : fileURL
|
||||
const { data } = await apiClient.get<Blob>(apiPath, {
|
||||
responseType: 'blob',
|
||||
})
|
||||
return data
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
// API 基础设施
|
||||
export * from './client'
|
||||
export * from './files'
|
||||
|
||||
Reference in New Issue
Block a user