feat: 完成情绪博物馆项目重构和功能增强 - 新增日记评论和帖子功能 - 重构前端架构,优化用户体验 - 完善WebSocket通信机制 - 更新项目文档和部署配置

This commit is contained in:
2025-07-27 10:05:59 +08:00
parent 6903ac1c0d
commit cc886cd4d5
126 changed files with 21179 additions and 15734 deletions
-65
View File
@@ -1,65 +0,0 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { ThemeConfig } from '@/types'
export const useAppStore = defineStore('app', () => {
// 应用状态
const loading = ref(false)
const mobileMenuVisible = ref(false)
const theme = ref<ThemeConfig>({
primaryColor: '#4A90E2',
secondaryColor: '#F5A623',
backgroundColor: '#F7F8FA',
textColor: '#333333',
borderRadius: '8px'
})
// 设备信息
const isMobile = ref(false)
const screenWidth = ref(window.innerWidth)
// 方法
const setLoading = (value: boolean) => {
loading.value = value
}
const toggleMobileMenu = () => {
mobileMenuVisible.value = !mobileMenuVisible.value
}
const closeMobileMenu = () => {
mobileMenuVisible.value = false
}
const updateScreenWidth = () => {
screenWidth.value = window.innerWidth
isMobile.value = window.innerWidth < 768
}
const setTheme = (newTheme: Partial<ThemeConfig>) => {
theme.value = { ...theme.value, ...newTheme }
}
// 初始化
const init = () => {
updateScreenWidth()
window.addEventListener('resize', updateScreenWidth)
}
return {
// 状态
loading,
mobileMenuVisible,
theme,
isMobile,
screenWidth,
// 方法
setLoading,
toggleMobileMenu,
closeMobileMenu,
updateScreenWidth,
setTheme,
init
}
})
+284
View File
@@ -0,0 +1,284 @@
/**
* 认证状态管理
*/
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
import AuthService from '@/services/auth'
import { handleApiError } from '@/utils/errorHandler'
import type {
LoginRequest,
RegisterRequest,
AuthResponse,
UserInfo
} from '@/types/auth'
export const useAuthStore = defineStore('auth', () => {
// 状态
const accessToken = ref<string>('')
const refreshToken = ref<string>('')
const userInfo = ref<UserInfo | null>(null)
// 移除权限状态,该功能不存在
const isLoading = ref(false)
// 计算属性
const isLoggedIn = computed(() => !!accessToken.value && !!userInfo.value)
const userId = computed(() => userInfo.value?.id || '')
const username = computed(() => userInfo.value?.username || '')
const nickname = computed(() => userInfo.value?.nickname || '')
const avatar = computed(() => userInfo.value?.avatar || '')
const email = computed(() => userInfo.value?.email || '')
const phone = computed(() => userInfo.value?.phone || '')
// 移除权限检查方法,该功能不存在
/**
* 初始化认证状态
*/
const initAuth = async () => {
try {
console.log('🔄 初始化认证状态...')
// 从本地存储恢复token
const storedAccessToken = localStorage.getItem('access_token')
const storedRefreshToken = localStorage.getItem('refresh_token')
const storedUserInfo = localStorage.getItem('user_info')
console.log('🔄 本地存储状态:', {
hasToken: !!storedAccessToken,
hasRefreshToken: !!storedRefreshToken,
hasUserInfo: !!storedUserInfo
})
if (storedAccessToken && storedUserInfo) {
// 恢复认证状态
accessToken.value = storedAccessToken
refreshToken.value = storedRefreshToken || ''
userInfo.value = JSON.parse(storedUserInfo)
console.log('🔄 认证状态已恢复')
// 简单验证:尝试获取用户信息来验证token是否有效
try {
await getCurrentUserInfo()
console.log('🔄 Token验证成功')
} catch (error) {
console.warn('🔄 Token可能已过期,但不强制登出:', error)
// 不强制登出,让用户在下次API调用时处理
}
} else {
console.log('🔄 无有效的本地认证信息')
}
} catch (error) {
console.error('🔄 初始化认证状态失败:', error)
// 不自动登出,避免清除用户刚登录的状态
}
}
/**
* 用户登录
*/
const login = async (loginData: LoginRequest): Promise<boolean> => {
try {
isLoading.value = true
console.log('🔐 开始登录流程...')
const response = await AuthService.login(loginData)
console.log('🔐 登录响应数据:', response)
// 保存认证信息
setAuthData(response)
console.log('🔐 认证信息已保存')
// 验证token是否正确保存
const savedToken = localStorage.getItem('access_token')
console.log('🔐 保存的token:', savedToken ? '已保存' : '未保存')
// 获取最新的用户信息
await getCurrentUserInfo()
ElMessage.success('登录成功')
return true
} catch (error: any) {
console.error('🔐 登录失败:', error)
handleApiError(error, '用户登录')
return false
} finally {
isLoading.value = false
}
}
/**
* 用户注册
*/
const register = async (registerData: RegisterRequest): Promise<boolean> => {
try {
isLoading.value = true
const response = await AuthService.register(registerData)
// 保存认证信息
setAuthData(response)
// 移除权限获取,该接口不存在
ElMessage.success('注册成功')
return true
} catch (error: any) {
handleApiError(error, '用户注册')
return false
} finally {
isLoading.value = false
}
}
/**
* 用户登出
*/
const logout = async (): Promise<void> => {
try {
// 调用登出接口
if (accessToken.value) {
await AuthService.logout()
}
} catch (error) {
console.error('登出接口调用失败:', error)
} finally {
// 清除本地状态
clearAuthData()
ElMessage.success('已退出登录')
}
}
/**
* 刷新Token
*/
const refreshAccessToken = async (): Promise<boolean> => {
try {
if (!refreshToken.value) {
throw new Error('没有刷新令牌')
}
const response = await AuthService.refreshToken({
refreshToken: refreshToken.value
})
// 更新认证信息
setAuthData(response)
return true
} catch (error) {
console.error('刷新Token失败:', error)
// 刷新失败,清除认证状态
await logout()
return false
}
}
/**
* 获取当前用户信息
*/
const getCurrentUserInfo = async (): Promise<void> => {
try {
const response = await AuthService.getCurrentUserInfo()
// 后端直接返回用户信息,不是嵌套在userInfo字段中
userInfo.value = response
// 更新本地存储
localStorage.setItem('user_info', JSON.stringify(response))
} catch (error) {
console.error('获取用户信息失败:', error)
}
}
/**
* 静默恢复本地认证状态(不进行API调用)
*/
const restoreLocalAuth = () => {
try {
const storedAccessToken = localStorage.getItem('access_token')
const storedRefreshToken = localStorage.getItem('refresh_token')
const storedUserInfo = localStorage.getItem('user_info')
if (storedAccessToken && storedUserInfo) {
accessToken.value = storedAccessToken
refreshToken.value = storedRefreshToken || ''
userInfo.value = JSON.parse(storedUserInfo)
console.log('🔄 本地认证状态已恢复')
return true
}
return false
} catch (error) {
console.error('🔄 恢复本地认证状态失败:', error)
return false
}
}
/**
* 设置认证数据
*/
const setAuthData = (authData: AuthResponse): void => {
accessToken.value = authData.accessToken
refreshToken.value = authData.refreshToken
userInfo.value = authData.userInfo
// 保存到本地存储
localStorage.setItem('access_token', authData.accessToken)
localStorage.setItem('refresh_token', authData.refreshToken)
localStorage.setItem('user_info', JSON.stringify(authData.userInfo))
}
/**
* 清除认证数据
*/
const clearAuthData = (): void => {
accessToken.value = ''
refreshToken.value = ''
userInfo.value = null
// 清除本地存储
localStorage.removeItem('access_token')
localStorage.removeItem('refresh_token')
localStorage.removeItem('user_info')
}
/**
* 更新用户信息
*/
const updateUserInfo = (newUserInfo: Partial<UserInfo>): void => {
if (userInfo.value) {
userInfo.value = { ...userInfo.value, ...newUserInfo }
localStorage.setItem('user_info', JSON.stringify(userInfo.value))
}
}
return {
// 状态
accessToken,
refreshToken,
userInfo,
isLoading,
// 计算属性
isLoggedIn,
userId,
username,
nickname,
avatar,
email,
phone,
// 方法
initAuth,
restoreLocalAuth,
login,
register,
logout,
refreshAccessToken,
getCurrentUserInfo,
setAuthData,
clearAuthData,
updateUserInfo
}
})
+316 -37
View File
@@ -1,12 +1,34 @@
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
import { ref, computed, watch } from 'vue'
import type { ChatMessage, ChatSession } from '@/types'
import webSocketService, { type WebSocketMessage, type ConnectionStatus } from '@/services/websocket'
import { useUserStore } from './user'
import { stompWebSocketService, type WebSocketMessage, type ConnectionStatus } from '@/services/stomp-websocket'
import { useAuthStore } from './auth'
import { chatApi } from '@/services/chat'
import MessageService, { messageApi } from '@/services/message'
// 聊天消息类型
export interface ChatMessage {
id: string
content: string
type: 'user' | 'ai'
timestamp: string
conversationId?: string
status?: 'sending' | 'sent' | 'delivered' | 'read' | 'failed'
error?: string
}
// 聊天会话类型
export interface ChatSession {
id: string
title: string
userId?: string
createTime: string
updateTime: string
messageCount: number
}
export const useChatStore = defineStore('chat', () => {
const userStore = useUserStore()
const authStore = useAuthStore()
// 聊天状态
const currentSession = ref<ChatSession | null>(null)
@@ -17,7 +39,16 @@ export const useChatStore = defineStore('chat', () => {
const connectionStatus = ref<ConnectionStatus>('DISCONNECTED')
const wsConnected = ref(false)
// 方法
// 计算属性
const currentMessages = computed(() => {
if (!currentSession.value) return []
return messages.value.filter(msg =>
msg.conversationId === currentSession.value?.id ||
msg.sessionId === currentSession.value?.id
)
})
// 添加消息
const addMessage = (message: Omit<ChatMessage, 'id' | 'timestamp'>) => {
const newMessage: ChatMessage = {
...message,
@@ -60,8 +91,8 @@ export const useChatStore = defineStore('chat', () => {
})
try {
// 仅通过WebSocket推送,后端会统一处理消息保存
webSocketService.sendChatMessage(content, currentSession.value?.id)
// 仅通过STOMP WebSocket推送,后端会统一处理消息保存
stompWebSocketService.sendChatMessage(content, currentSession.value?.id)
// 更新消息状态为已发送
updateMessageStatus(userMessage.id, 'sent')
@@ -87,8 +118,13 @@ export const useChatStore = defineStore('chat', () => {
// 创建会话:同步后端
const createSession = async (title?: string) => {
let newSession: ChatSession
if (userStore.user?.id) {
newSession = await chatApi.createSession(userStore.user.id, title || `对话${sessions.value.length + 1}`)
const currentUserId = authStore.userInfo?.id || authStore.userId
console.log('📝 创建会话,当前用户ID:', currentUserId)
if (currentUserId) {
newSession = await chatApi.createSession(currentUserId, title || `对话${sessions.value.length + 1}`)
console.log('✅ 已为登录用户创建会话:', newSession)
} else {
newSession = {
id: Date.now().toString(),
@@ -97,6 +133,7 @@ export const useChatStore = defineStore('chat', () => {
updateTime: new Date().toISOString(),
messageCount: 0
}
console.log('⚠️ 为访客创建本地会话:', newSession)
}
sessions.value.unshift(newSession)
currentSession.value = newSession
@@ -104,7 +141,7 @@ export const useChatStore = defineStore('chat', () => {
// 如果WebSocket已连接,设置新的会话ID
if (wsConnected.value) {
webSocketService.setConversationId(newSession.id)
stompWebSocketService.setConversationId(newSession.id)
}
return newSession
@@ -119,18 +156,38 @@ export const useChatStore = defineStore('chat', () => {
// 如果WebSocket已连接,更新会话ID
if (wsConnected.value) {
webSocketService.setConversationId(sessionId)
stompWebSocketService.setConversationId(sessionId)
}
}
}
// 加载会话消息:从后端获取
// 加载会话消息:使用现有的消息API
const loadSessionMessages = async (sessionId: string) => {
console.log('📨 开始加载会话消息:', sessionId)
console.log('💡 注意:后端没有按会话ID获取消息的接口,使用最近消息代替')
try {
const msgs = await chatApi.getAllSessionMessages(sessionId)
messages.value = msgs
// 由于后端没有按会话ID获取消息的接口,我们使用最近消息
// 这是一个临时方案,理想情况下应该在后端添加相应接口
console.log('📨 使用最近消息API代替会话消息...')
const response = await messageApi.getRecentMessages(50)
console.log('📨 最近消息API响应:', response)
// 处理API响应数据结构
const messageList = response.data || response || []
console.log('📨 提取的消息列表:', messageList)
const chatMessages = MessageService.convertToChatMessages(messageList)
console.log('📨 转换后的聊天消息:', chatMessages)
// 如果需要过滤特定会话的消息,可以在这里添加过滤逻辑
// const sessionMessages = chatMessages.filter(msg => msg.sessionId === sessionId)
messages.value = chatMessages
console.log('📨 会话消息加载完成,消息数量:', messages.value.length)
} catch (error) {
console.error('Failed to load session messages:', error)
console.error('❌ 加载会话消息失败:', error)
messages.value = []
}
}
@@ -156,14 +213,171 @@ export const useChatStore = defineStore('chat', () => {
}
}
// 清空消息
const clearMessages = () => {
messages.value = []
}
const searchMessages = (keyword: string) => {
return messages.value.filter(message =>
message.content.toLowerCase().includes(keyword.toLowerCase())
)
// 搜索消息:支持本地搜索和远程搜索
const searchMessages = async (keyword: string) => {
console.log('🔍 开始搜索消息:', { keyword })
if (!keyword.trim()) {
console.log('🔍 搜索关键词为空,返回空结果')
return []
}
try {
// 先尝试远程搜索
console.log('🔍 尝试远程搜索...')
const response = await messageApi.searchUserMessages(keyword, 50)
console.log('🔍 远程搜索API响应:', response)
// 处理API响应数据结构
const searchResults = response.data || response || []
console.log('🔍 提取的搜索结果:', searchResults)
const chatMessages = MessageService.convertToChatMessages(searchResults)
console.log('🔍 转换后的搜索结果:', chatMessages)
return chatMessages
} catch (error) {
console.error('❌ 远程搜索失败,使用本地搜索:', error)
// 如果远程搜索失败,使用本地搜索
const localResults = messages.value.filter(message =>
message.content.toLowerCase().includes(keyword.toLowerCase())
)
console.log('🔍 本地搜索结果:', localResults)
return localResults
}
}
// 加载用户历史消息
const loadUserMessages = async (page: number = 1, size: number = 20) => {
console.log('📨 开始加载用户历史消息:', { page, size })
try {
const response = await messageApi.getUserMessages(page, size)
console.log('📨 API响应原始数据:', response)
// 处理API响应数据结构
const result = response.data || response
const messageList = result.records || result.list || []
console.log('📨 提取的消息列表:', messageList)
const chatMessages = MessageService.convertToChatMessages(messageList)
console.log('📨 转换后的聊天消息:', chatMessages)
if (page === 1) {
// 第一页,替换现有消息
messages.value = chatMessages
console.log('📨 第一页数据已加载,消息总数:', messages.value.length)
} else {
// 后续页,追加到现有消息
messages.value = [...messages.value, ...chatMessages]
console.log('📨 追加数据已加载,消息总数:', messages.value.length)
}
const returnData = {
list: messageList,
total: result.total || 0,
page: result.current || page,
size: result.size || size,
pages: result.pages || 0
}
console.log('📨 返回的分页数据:', returnData)
return returnData
} catch (error) {
console.error('❌ 加载用户历史消息失败:', error)
return { list: [], total: 0, page, size, pages: 0 }
}
}
// 加载最近消息
const loadRecentMessages = async (limit: number = 10) => {
console.log('📝 开始加载最近消息:', { limit })
try {
// 直接使用messageApi,避免多层封装
const response = await messageApi.getRecentMessages(limit)
console.log('📝 最近消息API响应:', response)
// 处理响应数据 - 根据您的修改,messageApi现在返回 response.data || response
let messageList = []
if (Array.isArray(response)) {
messageList = response
console.log('📝 直接使用响应数组,消息数量:', messageList.length)
} else if (response && response.data && Array.isArray(response.data)) {
messageList = response.data
console.log('📝 使用response.data,消息数量:', messageList.length)
} else {
console.warn('📝 无法识别的响应格式:', response)
messageList = []
}
console.log('📝 提取的最近消息列表:', messageList)
console.log('📝 第一条消息示例:', messageList[0])
if (messageList.length === 0) {
console.log('📝 没有找到最近消息')
messages.value = []
return []
}
const chatMessages = MessageService.convertToChatMessages(messageList)
console.log('📝 转换后的最近消息:', chatMessages)
// 详细检查每条消息的转换结果
chatMessages.forEach((msg, index) => {
console.log(`📝 消息${index + 1}:`, {
id: msg.id,
content: msg.content.substring(0, 20) + '...',
sender: msg.sender,
type: msg.type,
role: msg.role,
timestamp: msg.timestamp
})
})
// 按时间排序(最新的在后面)
chatMessages.sort((a, b) => {
// 处理时间格式 "2025-07-26 22:09:10" -> ISO格式
const parseTime = (timestamp: string | Date) => {
if (timestamp instanceof Date) {
return timestamp.getTime()
}
if (typeof timestamp === 'string') {
// 如果是 "2025-07-26 22:09:10" 格式,转换为ISO格式
if (timestamp.includes(' ') && !timestamp.includes('T')) {
const isoString = timestamp.replace(' ', 'T')
return new Date(isoString).getTime()
}
return new Date(timestamp).getTime()
}
return new Date().getTime()
}
const timeA = parseTime(a.timestamp)
const timeB = parseTime(b.timestamp)
console.log('📝 排序比较:', {
a: { id: a.id.substring(0, 8), timestamp: a.timestamp, parsed: new Date(timeA).toLocaleString() },
b: { id: b.id.substring(0, 8), timestamp: b.timestamp, parsed: new Date(timeB).toLocaleString() },
result: timeA - timeB
})
return timeA - timeB
})
messages.value = chatMessages
console.log('📝 最近消息已加载并排序,消息总数:', messages.value.length)
return chatMessages
} catch (error) {
console.error('❌ 加载最近消息失败:', error)
messages.value = []
return []
}
}
// 添加AI回复消息(直接显示完整内容)
@@ -184,7 +398,7 @@ export const useChatStore = defineStore('chat', () => {
}
// WebSocket消息处理
const handleWebSocketMessage = (wsMessage: WebSocketMessage) => {
let handleWebSocketMessage = (wsMessage: WebSocketMessage) => {
console.log('收到WebSocket消息:', wsMessage.type, wsMessage.senderType)
switch (wsMessage.type) {
@@ -232,10 +446,16 @@ export const useChatStore = defineStore('chat', () => {
// WebSocket连接管理
const connectWebSocket = async () => {
try {
// 优先使用userInfo中的用户ID,如果没有则使用user中的ID
const userId = userStore.userInfo?.id || userStore.user?.id || undefined
// 获取当前登录用户的ID
const userId = authStore.userInfo?.id || authStore.userId || undefined
console.log('🔌 准备连接WebSocket,当前用户:', {
userId,
userInfo: authStore.userInfo,
isLoggedIn: authStore.isLoggedIn,
accessToken: authStore.accessToken ? '已有token' : '无token'
})
await webSocketService.connect(userId, {
await stompWebSocketService.connect(userId, {
onMessage: handleWebSocketMessage,
onConnect: () => {
console.log('WebSocket连接成功')
@@ -244,7 +464,7 @@ export const useChatStore = defineStore('chat', () => {
// 设置会话ID
if (currentSession.value?.id) {
webSocketService.setConversationId(currentSession.value.id)
stompWebSocketService.setConversationId(currentSession.value.id)
}
},
onDisconnect: () => {
@@ -281,27 +501,69 @@ export const useChatStore = defineStore('chat', () => {
}
const disconnectWebSocket = () => {
webSocketService.disconnect()
stompWebSocketService.disconnect()
wsConnected.value = false
isConnected.value = false
isTyping.value = false
}
// 初始化
// 初始化聊天 - 参考web项目的实现
const initChat = async () => {
// 如果没有会话,创建一个默认会话
if (sessions.value.length === 0) {
await createSession('与开开的对话')
}
console.log('🚀 初始化聊天功能...')
// 连接WebSocket
await connectWebSocket()
try {
// 1. 首先尝试加载最近消息(优先显示历史数据)
console.log('📨 优先加载最近消息...')
await loadRecentMessages(20)
// 2. 尝试加载用户的历史会话
const currentUserId = authStore.userInfo?.id || authStore.userId
if (currentUserId) {
try {
console.log('📂 尝试加载用户会话,用户ID:', currentUserId)
const userSessions = await chatApi.getUserSessions(currentUserId)
if (userSessions.length > 0) {
sessions.value = userSessions
currentSession.value = userSessions[0]
console.log('✅ 加载到用户会话:', userSessions.length, '个')
}
} catch (error) {
console.warn('⚠️ 加载用户会话失败,继续使用已加载的消息:', error)
}
} else {
console.log('⚠️ 未找到用户ID,无法加载用户会话')
}
// 3. 如果没有会话,创建一个默认会话
if (sessions.value.length === 0) {
console.log('📝 创建默认会话...')
await createSession('与开开的对话')
}
// 4. 如果有特定会话但消息为空,尝试加载会话消息
if (currentSession.value?.id && messages.value.length === 0) {
console.log('📨 尝试加载特定会话消息...')
try {
await loadSessionMessages(currentSession.value.id)
} catch (error) {
console.warn('⚠️ 加载会话消息失败,保持当前消息:', error)
}
}
// 5. 连接WebSocket
console.log('🔌 连接WebSocket...')
await connectWebSocket()
console.log('✅ 聊天功能初始化完成,当前消息数量:', messages.value.length)
} catch (error) {
console.error('❌ 聊天功能初始化失败:', error)
}
}
// 监听会话变化,更新WebSocket会话ID
watch(currentSession, (newSession) => {
if (newSession?.id && wsConnected.value) {
webSocketService.setConversationId(newSession.id)
stompWebSocketService.setConversationId(newSession.id)
}
})
@@ -314,19 +576,36 @@ export const useChatStore = defineStore('chat', () => {
isConnected,
connectionStatus,
wsConnected,
currentMessages,
// 方法
// 基础方法
addMessage,
sendMessage,
createSession,
switchSession,
loadSessionMessages,
deleteSession,
clearMessages,
searchMessages,
initChat,
// 消息加载方法
loadSessionMessages,
loadUserMessages,
loadRecentMessages,
searchMessages,
// WebSocket方法
connectWebSocket,
disconnectWebSocket,
handleWebSocketMessage
handleWebSocketMessage,
// 消息监听方法
onMessage: (callback: (message: any) => void) => {
// 简单的消息监听实现
const originalHandler = handleWebSocketMessage
handleWebSocketMessage = (message: any) => {
originalHandler(message)
callback(message)
}
}
}
})
-157
View File
@@ -1,157 +0,0 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { DiaryEntry } from '@/types'
export const useDiaryStore = defineStore('diary', () => {
// 日记状态
const entries = ref<DiaryEntry[]>([])
const currentEntry = ref<DiaryEntry | null>(null)
const isLoading = ref(false)
// 方法
const addEntry = async (content: string, mood?: string, tags?: string[]) => {
isLoading.value = true
try {
const newEntry: DiaryEntry = {
id: Date.now().toString(),
content,
mood,
tags: tags || [],
createTime: new Date().toISOString(),
updateTime: new Date().toISOString()
}
// TODO: 调用API保存日记
// const response = await diaryApi.createEntry(newEntry)
// 模拟AI回复
setTimeout(() => {
newEntry.aiReply = generateAIReply(content, mood)
entries.value.unshift(newEntry)
isLoading.value = false
}, 1000)
return newEntry
} catch (error) {
console.error('Failed to add diary entry:', error)
isLoading.value = false
throw error
}
}
const updateEntry = async (id: string, updates: Partial<DiaryEntry>) => {
const index = entries.value.findIndex(entry => entry.id === id)
if (index > -1) {
entries.value[index] = {
...entries.value[index],
...updates,
updateTime: new Date().toISOString()
}
// TODO: 调用API更新日记
// await diaryApi.updateEntry(id, updates)
}
}
const deleteEntry = async (id: string) => {
const index = entries.value.findIndex(entry => entry.id === id)
if (index > -1) {
entries.value.splice(index, 1)
// TODO: 调用API删除日记
// await diaryApi.deleteEntry(id)
}
}
const getEntry = (id: string) => {
return entries.value.find(entry => entry.id === id)
}
const searchEntries = (keyword: string) => {
return entries.value.filter(entry =>
entry.content.toLowerCase().includes(keyword.toLowerCase()) ||
entry.tags?.some(tag => tag.toLowerCase().includes(keyword.toLowerCase()))
)
}
const getEntriesByMood = (mood: string) => {
return entries.value.filter(entry => entry.mood === mood)
}
const getEntriesByDateRange = (startDate: string, endDate: string) => {
return entries.value.filter(entry => {
const entryDate = new Date(entry.createTime).toISOString().split('T')[0]
return entryDate >= startDate && entryDate <= endDate
})
}
const loadEntries = async () => {
isLoading.value = true
try {
// TODO: 从API加载日记列表
// const response = await diaryApi.getEntries()
// entries.value = response.data
// 临时模拟数据
entries.value = [
{
id: '1',
content: '今天天气很好,心情也不错。和朋友一起去公园散步,看到了很多美丽的花朵。',
mood: 'happy',
tags: ['散步', '朋友', '公园'],
createTime: new Date(Date.now() - 86400000).toISOString(),
updateTime: new Date(Date.now() - 86400000).toISOString(),
aiReply: '听起来你度过了美好的一天!和朋友一起在大自然中放松是很棒的体验。这样的时光能让我们感受到生活的美好。'
}
]
isLoading.value = false
} catch (error) {
console.error('Failed to load diary entries:', error)
isLoading.value = false
}
}
// 生成AI回复的辅助函数
const generateAIReply = (_content: string, mood?: string) => {
const replies = {
happy: [
'很高兴看到你心情愉快!继续保持这份美好的心情吧。',
'你的快乐感染了我!希望这份喜悦能持续下去。',
'看到你开心,我也很开心。愿你每天都有这样的好心情!'
],
sad: [
'我能感受到你的难过。记住,这只是暂时的,一切都会好起来的。',
'每个人都会有低落的时候,这很正常。我会陪伴你度过这段时光。',
'虽然现在感到难过,但请相信明天会更好。我一直在这里支持你。'
],
neutral: [
'感谢你分享今天的经历。每一天都是独特的,值得被记录。',
'生活就是这样平凡而珍贵。感谢你让我了解你的日常。',
'平静的日子也有它的美好。希望你能在平凡中发现小确幸。'
]
}
const moodReplies = replies[(mood as keyof typeof replies) || 'neutral'] || replies.neutral
return moodReplies[Math.floor(Math.random() * moodReplies.length)]
}
return {
// 状态
entries,
currentEntry,
isLoading,
// 方法
addEntry,
updateEntry,
deleteEntry,
getEntry,
searchEntries,
getEntriesByMood,
getEntriesByDateRange,
loadEntries
}
})
-11
View File
@@ -1,11 +0,0 @@
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
// 导出所有store
export { useUserStore } from './user'
export { useChatStore } from './chat'
export { useDiaryStore } from './diary'
export { useAppStore } from './app'
-157
View File
@@ -1,157 +0,0 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { authService, authUtils } from '@/services/auth'
import type { User } from '@/types'
import type { UserInfo, LoginRequest } from '@/types/auth'
export const useUserStore = defineStore('user', () => {
// 用户状态
const user = ref<User | null>(null)
const userInfo = ref<UserInfo | null>(null)
const token = ref<string>('')
const isLoading = ref(false)
const isLoggedIn = computed(() => !!token.value && (!!user.value || !!userInfo.value))
// 方法
const setUser = (userData: User) => {
user.value = userData
}
const setToken = (tokenValue: string) => {
token.value = tokenValue
// 存储到localStorage
if (tokenValue) {
localStorage.setItem('token', tokenValue)
} else {
localStorage.removeItem('token')
}
}
const setUserInfo = (userInfoData: UserInfo | null) => {
userInfo.value = userInfoData
// 存储到localStorage
if (userInfoData) {
localStorage.setItem('userInfo', JSON.stringify(userInfoData))
} else {
localStorage.removeItem('userInfo')
}
}
// 新的登录方法,支持认证服务
const loginWithAuth = async (loginData: LoginRequest) => {
isLoading.value = true
try {
const data = await authService.login(loginData)
setToken(data.accessToken)
setUserInfo(data.userInfo)
return data
} catch (error: any) {
throw error
} finally {
isLoading.value = false
}
}
const login = async (credentials: { username: string; password: string }) => {
try {
// TODO: 调用登录API
// const response = await authApi.login(credentials)
// setToken(response.data.token)
// setUser(response.data.user)
// 临时模拟登录
setToken('mock-token')
setUser({
id: '1',
username: credentials.username,
email: 'user@example.com',
nickname: '用户',
createTime: new Date().toISOString(),
updateTime: new Date().toISOString()
})
return true
} catch (error) {
console.error('Login failed:', error)
return false
}
}
const logout = async () => {
try {
await authService.logout()
} catch (error) {
console.error('Logout error:', error)
} finally {
// 清除状态和本地存储
user.value = null
userInfo.value = null
setToken('')
authUtils.clearAuth()
}
}
const updateProfile = (profileData: Partial<User>) => {
if (user.value) {
user.value = { ...user.value, ...profileData }
// TODO: 调用更新API
}
}
// 初始化用户状态
const initUser = () => {
const savedToken = authUtils.getToken()
const savedUserInfo = authUtils.getUserInfo()
console.log('初始化用户状态:', { savedToken: !!savedToken, savedUserInfo })
if (savedToken) {
setToken(savedToken)
}
if (savedUserInfo) {
setUserInfo(savedUserInfo)
}
console.log('用户状态初始化完成:', {
token: !!token.value,
userInfo: userInfo.value,
isLoggedIn: isLoggedIn.value
})
}
// 刷新用户信息
const refreshUserInfo = async () => {
if (!token.value) return
try {
const response = await authService.getUserInfo()
if (response.success) {
userInfo.value = response.data
authUtils.setUserInfo(response.data)
}
} catch (error) {
console.error('Refresh user info error:', error)
}
}
return {
// 状态
user,
userInfo,
token,
isLoading,
isLoggedIn,
// 方法
setUser,
setToken,
setUserInfo,
login,
loginWithAuth,
logout,
updateProfile,
initUser,
refreshUserInfo
}
})