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
|
Warning
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { fetchAdminDashboard, type AdminDashboard } from '@/features/admin/api/adminDashboard'
|
import { fetchAdminDashboard, type AdminDashboard } from '@/features/admin/api/adminDashboard'
|
||||||
import { useAdminTable } from '@/composables/useAdminTable'
|
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||||
import { useMoney } from '@/composables/useMoney'
|
import { useMoney } from '@/shared/composables/useMoney'
|
||||||
import { disputeStatusLabel, orderStatusLabel } from '@/utils/statusLabels'
|
import { disputeStatusLabel, orderStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { ElMessage } from 'element-plus'
|
|||||||
import { computed, reactive, ref } from 'vue'
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
|
||||||
import { fetchAdminListings, type AdminListingPage, type AdminListingQuery, type Listing } from '@/features/listings'
|
import { fetchAdminListings, type AdminListingPage, type AdminListingQuery, type Listing } from '@/features/listings'
|
||||||
import { useAdminTable } from '@/composables/useAdminTable'
|
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||||
import { useMoney } from '@/composables/useMoney'
|
import { useMoney } from '@/shared/composables/useMoney'
|
||||||
import {
|
import {
|
||||||
assetRegions,
|
assetRegions,
|
||||||
formatEstimatedRentalDuration,
|
formatEstimatedRentalDuration,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { fetchAdminMgrUsers, deleteAdminMgrUser, changeAdminPassword, type AdminMgrUser } from '@/features/admin/api/adminMgr'
|
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 { formatDateTime } from '@/utils/time'
|
||||||
|
|
||||||
import AdminUserDialog from './components/AdminUserDialog.vue'
|
import AdminUserDialog from './components/AdminUserDialog.vue'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
import { fetchAdminOrders, type Order } from '@/features/orders'
|
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 { handoffStatusLabel, orderStatusLabel, settlementStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { fetchRoles, deleteRole, type Role } from '@/features/admin/api/adminRoles'
|
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 AssignPermissionsDialog from './components/AssignPermissionsDialog.vue'
|
||||||
import RoleDialog from './components/RoleDialog.vue'
|
import RoleDialog from './components/RoleDialog.vue'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ElMessage } from 'element-plus'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { fetchAdminUsers, freezeAdminUser, unfreezeAdminUser, type AdminUserItem } from '@/features/admin/api/adminUsers'
|
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 { userStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { onBeforeUnmount, ref, type Ref } from 'vue'
|
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'
|
import { getAccessToken, type AuthScope } from '@/utils/authStorage'
|
||||||
|
|
||||||
export interface SSEMessage {
|
export interface SSEMessage {
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import {
|
|||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
type ChatConversation,
|
type ChatConversation,
|
||||||
type ChatMessage,
|
type ChatMessage,
|
||||||
} from '@/api/chats'
|
} from '@/features/chats/api/chats'
|
||||||
import { uploadFile } from '@/shared/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import ChatAttachmentImage from '@/components/ChatAttachmentImage.vue'
|
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'
|
import { formatDateMinute } from '@/utils/time'
|
||||||
|
|
||||||
const currentUserId = Number(localStorage.getItem('user_id') || 0)
|
const currentUserId = Number(localStorage.getItem('user_id') || 0)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { computed, onMounted, ref } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ChatDotRound, Refresh, Tickets } from '@element-plus/icons-vue'
|
import { ChatDotRound, Refresh, Tickets } from '@element-plus/icons-vue'
|
||||||
import { fetchChats, type ChatConversation } from '@/api/chats'
|
import { fetchChats, type ChatConversation } from '@/features/chats/api/chats'
|
||||||
import { useChatSSE, type ChatEvent } from '@/composables/useChatSSE'
|
import { useChatSSE, type ChatEvent } from '@/features/chats/composables/useChatSSE'
|
||||||
import { formatDateMinute } from '@/utils/time'
|
import { formatDateMinute } from '@/utils/time'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import {
|
|||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
type ChatConversation,
|
type ChatConversation,
|
||||||
type ChatMessage,
|
type ChatMessage,
|
||||||
} from '@/api/chats'
|
} from '@/features/chats/api/chats'
|
||||||
import { uploadFile } from '@/shared/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import ChatAttachmentImage from '@/components/ChatAttachmentImage.vue'
|
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'
|
import { formatDateMinute } from '@/utils/time'
|
||||||
|
|
||||||
const currentUserId = Number(localStorage.getItem('user_id') || 0)
|
const currentUserId = Number(localStorage.getItem('user_id') || 0)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { computed, onMounted, ref } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
import MobileBottomNav from '@/components/MobileBottomNav.vue'
|
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'
|
import { formatDateMinute } from '@/utils/time'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import {
|
|||||||
moneyRangeOptions,
|
moneyRangeOptions,
|
||||||
totalRangeOptions,
|
totalRangeOptions,
|
||||||
fireLevelRangeOptions,
|
fireLevelRangeOptions,
|
||||||
} from '@/composables/home/useFilterOptions'
|
} from '@/features/listings/composables/useFilterOptions'
|
||||||
import type { ListingPublishOptions } from '@/features/listings/api/listingOptions'
|
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 {
|
interface Props {
|
||||||
filters: HomeFilters
|
filters: HomeFilters
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { ArrowDown } from '@element-plus/icons-vue'
|
import { ArrowDown } from '@element-plus/icons-vue'
|
||||||
import { ElInputNumber, ElPopover } from 'element-plus'
|
import { ElInputNumber, ElPopover } from 'element-plus'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { rangeLabel, isRangeSelected } from '@/composables/home/useFilterOptions'
|
import { rangeLabel, isRangeSelected } from '@/features/listings/composables/useFilterOptions'
|
||||||
|
|
||||||
interface RangeOption {
|
interface RangeOption {
|
||||||
label: string
|
label: string
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import { rangeLabel, isRangeSelected } from '@/composables/home/useFilterOptions'
|
import { rangeLabel, isRangeSelected } from '@/features/listings/composables/useFilterOptions'
|
||||||
|
|
||||||
describe('useFilterOptions', () => {
|
describe('useFilterOptions', () => {
|
||||||
describe('rangeLabel', () => {
|
describe('rangeLabel', () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
import { useHomeFilters } from '@/composables/home/useHomeFilters'
|
import { useHomeFilters } from '@/features/listings/composables/useHomeFilters'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { emptyListingPublishOptions } from '@/api/listingOptions'
|
import { emptyListingPublishOptions } from '@/api/listingOptions'
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import {
|
|||||||
defaultHomeBanners,
|
defaultHomeBanners,
|
||||||
fetchMobileHomeConfig,
|
fetchMobileHomeConfig,
|
||||||
type HomeBannerSlide,
|
type HomeBannerSlide,
|
||||||
} from '@/api/homeConfig'
|
} from '@/features/listings/api/homeConfig'
|
||||||
import {
|
import {
|
||||||
emptyListingPublishOptions,
|
emptyListingPublishOptions,
|
||||||
type ListingPublishOptions,
|
type ListingPublishOptions,
|
||||||
} from '@/features/listings/api/listingOptions'
|
} from '@/features/listings/api/listingOptions'
|
||||||
import { useHomeFilters } from '@/composables/home/useHomeFilters'
|
import { useHomeFilters } from '@/features/listings/composables/useHomeFilters'
|
||||||
import { useListingQuery } from '@/composables/home/useListingQuery'
|
import { useListingQuery } from '@/features/listings/composables/useListingQuery'
|
||||||
import HomeAnnouncement from './components/HomeAnnouncement.vue'
|
import HomeAnnouncement from './components/HomeAnnouncement.vue'
|
||||||
import HomeBanner from './components/HomeBanner.vue'
|
import HomeBanner from './components/HomeBanner.vue'
|
||||||
import HomeStats from './components/HomeStats.vue'
|
import HomeStats from './components/HomeStats.vue'
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import {
|
|||||||
submitHandoff,
|
submitHandoff,
|
||||||
confirmReceive,
|
confirmReceive,
|
||||||
} from '../api/orders'
|
} from '../api/orders'
|
||||||
import { createDispute } from '@/api/disputes'
|
import { createDispute } from '@/features/disputes/api/disputes'
|
||||||
import { uploadFile } from '@/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import { fetchOrderChat } from '@/api/chats'
|
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||||
import { useSessionStore } from '@/stores/session'
|
import { useSessionStore } from '@/stores/session'
|
||||||
import { usePaymentPolling } from './usePaymentPolling'
|
import { usePaymentPolling } from './usePaymentPolling'
|
||||||
import { useSettlement } from './useSettlement'
|
import { useSettlement } from './useSettlement'
|
||||||
@@ -147,7 +147,7 @@ export function useOrderDetail() {
|
|||||||
async function handleUploadEvidence(file: File) {
|
async function handleUploadEvidence(file: File) {
|
||||||
uploadingEvidence.value = true
|
uploadingEvidence.value = true
|
||||||
try {
|
try {
|
||||||
const result = await uploadFile(file)
|
const result = await uploadFile(file, 'evidence')
|
||||||
return result.url
|
return result.url
|
||||||
} finally {
|
} finally {
|
||||||
uploadingEvidence.value = false
|
uploadingEvidence.value = false
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import QRCode from 'qrcode'
|
import QRCode from 'qrcode'
|
||||||
|
|
||||||
import { fetchOrderChat } from '@/api/chats'
|
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||||
import { createDispute } from '@/features/disputes'
|
import { createDispute } from '@/features/disputes'
|
||||||
import { uploadFile } from '@/shared/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
import { fetchFileBlobByURL, uploadFile } from '@/api/files'
|
import { fetchFileBlobByURL, uploadFile } from '@/shared/api/files'
|
||||||
import {
|
import {
|
||||||
emptyListingPublishOptions,
|
emptyListingPublishOptions,
|
||||||
emptyListingSalePriceConfig,
|
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 基础设施
|
// API 基础设施
|
||||||
export * from './client'
|
export * from './client'
|
||||||
|
export * from './files'
|
||||||
|
|||||||
Reference in New Issue
Block a user