feat: Features架构迁移 - P0和P1部分完成
## 完成的工作 ### P0: 基础设施准备 - 创建 features/ 和 shared/ 目录结构 - 迁移共享资源:API基础设施、工具函数、类型定义 - 迁移通用composables:useMoney, useSmsCountdown, usePricingCalculator - 迁移全局样式文件 - 建立模块化导出系统 ### P1.1: 钱包模块 (wallet) - 迁移 API: wallet.ts - 迁移 Views: WalletView.vue - 新增 Composable: useWallet.ts (封装钱包状态管理) - 更新导入路径到 shared/ ### P1.2: 聊天模块 (chats) - 迁移 API: chats.ts - 迁移 Views: ChatView, MessagesView (桌面+移动) - 迁移 Composables: useChatSSE.ts - 迁移 Components: ChatAttachmentImage.vue - 更新导入路径到 shared/ ## 技术改进 - 修复 shared/composables 导出问题 (default → 命名导出) - 修复 shared/api/client.ts 类型导入路径 - 建立清晰的模块边界和导出规范 ## 文档 - 添加完整的迁移计划文档 - 添加进度跟踪文档 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
10acca637e
commit
b5903a169f
@@ -0,0 +1,118 @@
|
||||
import { onBeforeUnmount, ref, type Ref } from 'vue'
|
||||
import { refreshAccessToken } from '@/api/client'
|
||||
import { getAccessToken, type AuthScope } from '@/utils/authStorage'
|
||||
|
||||
export interface SSEMessage {
|
||||
id: number
|
||||
conversation_id: number
|
||||
sender_type: string
|
||||
sender_id: number
|
||||
sender_role: string
|
||||
sender_name: string
|
||||
content_type: string
|
||||
content: string
|
||||
attachment_urls: string[]
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface ChatEvent {
|
||||
type: 'new_message' | 'conversation_updated'
|
||||
conversation_id: number
|
||||
message?: SSEMessage
|
||||
}
|
||||
|
||||
type EventHandler = (event: ChatEvent) => void
|
||||
|
||||
const reconnectDelay = 3000
|
||||
|
||||
export function useChatSSE(scope: AuthScope, endpoint: string) {
|
||||
const connected: Ref<boolean> = ref(false)
|
||||
let source: EventSource | null = null
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let stopped = false
|
||||
let refreshing = false
|
||||
const handlers: EventHandler[] = []
|
||||
|
||||
function onEvent(handler: EventHandler) {
|
||||
handlers.push(handler)
|
||||
}
|
||||
|
||||
function connect() {
|
||||
if (stopped || source) return
|
||||
const token = getAccessToken(scope)
|
||||
if (!token) return
|
||||
|
||||
const url = `${endpoint}?token=${encodeURIComponent(token)}`
|
||||
source = new EventSource(url)
|
||||
|
||||
source.addEventListener('connected', () => {
|
||||
connected.value = true
|
||||
})
|
||||
|
||||
source.addEventListener('new_message', (e) => {
|
||||
try {
|
||||
const data = JSON.parse((e as MessageEvent).data) as ChatEvent
|
||||
handlers.forEach(h => h(data))
|
||||
} catch { /* ignore */ }
|
||||
})
|
||||
|
||||
source.addEventListener('conversation_updated', (e) => {
|
||||
try {
|
||||
const data = JSON.parse((e as MessageEvent).data) as ChatEvent
|
||||
handlers.forEach(h => h(data))
|
||||
} catch { /* ignore */ }
|
||||
})
|
||||
|
||||
source.onerror = async () => {
|
||||
connected.value = false
|
||||
source?.close()
|
||||
source = null
|
||||
if (stopped || refreshing) return
|
||||
|
||||
refreshing = true
|
||||
try {
|
||||
await refreshAccessToken(scope)
|
||||
if (!stopped) {
|
||||
reconnectTimer = setTimeout(connect, reconnectDelay)
|
||||
}
|
||||
} catch {
|
||||
closeSource()
|
||||
} finally {
|
||||
refreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeSource() {
|
||||
if (reconnectTimer) {
|
||||
clearTimeout(reconnectTimer)
|
||||
reconnectTimer = null
|
||||
}
|
||||
source?.close()
|
||||
source = null
|
||||
connected.value = false
|
||||
}
|
||||
|
||||
function handleAuthStorageChanged(event: Event) {
|
||||
const detail = (event as CustomEvent<{ scope?: AuthScope }>).detail
|
||||
if (detail?.scope !== scope || stopped) return
|
||||
closeSource()
|
||||
connect()
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
stopped = true
|
||||
closeSource()
|
||||
window.removeEventListener('auth-storage-changed', handleAuthStorageChanged)
|
||||
}
|
||||
|
||||
window.addEventListener('auth-storage-changed', handleAuthStorageChanged)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
disconnect()
|
||||
})
|
||||
|
||||
connect()
|
||||
|
||||
return { connected, onEvent, disconnect }
|
||||
}
|
||||
Reference in New Issue
Block a user