优化处理
This commit is contained in:
+20
-19
@@ -52,7 +52,8 @@ export class ChatApiService {
|
||||
async createSession(userId: string, title?: string): Promise<ChatSession> {
|
||||
try {
|
||||
console.log('📝 创建会话API调用:', { userId, title })
|
||||
const response = await http.post<ConversationResponse>('/conversation', {
|
||||
// backend-single: POST /conversation/create
|
||||
const response = await http.post<ConversationResponse>('/conversation/create', {
|
||||
userId,
|
||||
title: title || `对话${Date.now()}`
|
||||
})
|
||||
@@ -88,25 +89,23 @@ export class ChatApiService {
|
||||
async getUserSessions(userId: string): Promise<ChatSession[]> {
|
||||
try {
|
||||
console.log('📂 获取用户会话API调用:', { userId })
|
||||
const response = await http.get<ConversationResponse[]>(`/conversation/user/${userId}`)
|
||||
// backend-single: GET /conversation/page?userId=xxx
|
||||
const response = await http.get<any>('/conversation/page', { params: { userId, current: 1, size: 100 } })
|
||||
console.log('📂 获取用户会话API响应:', response)
|
||||
|
||||
// 处理HTTP响应的data字段
|
||||
const data = (response as any).data || response
|
||||
// 处理HTTP响应的data字段(PageResult)
|
||||
const pageData = (response as any).data || response
|
||||
const records = pageData.records || []
|
||||
|
||||
// 后端返回ConversationResponse数组,需要转换为ChatSession格式
|
||||
if (Array.isArray(data)) {
|
||||
return data.map((conv: any) => ({
|
||||
id: conv.id,
|
||||
title: conv.title,
|
||||
userId: conv.userId || conv.user_id, // 兼容不同的字段名
|
||||
createTime: conv.createTime || conv.create_time,
|
||||
updateTime: conv.updateTime || conv.update_time,
|
||||
messageCount: conv.messageCount || conv.message_count || 0
|
||||
}))
|
||||
}
|
||||
|
||||
return []
|
||||
// 转换为ChatSession数组
|
||||
return records.map((conv: any) => ({
|
||||
id: conv.id,
|
||||
title: conv.title,
|
||||
userId: conv.userId || conv.user_id,
|
||||
createTime: conv.createTime || conv.create_time,
|
||||
updateTime: conv.updateTime || conv.update_time,
|
||||
messageCount: conv.messageCount || conv.message_count || 0
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error('❌ 获取用户会话失败:', error)
|
||||
return []
|
||||
@@ -133,7 +132,8 @@ export class ChatApiService {
|
||||
async deleteSession(sessionId: string): Promise<void> {
|
||||
try {
|
||||
console.log('🗑️ 删除会话API调用:', { sessionId })
|
||||
await http.delete(`/conversation/${sessionId}`)
|
||||
// backend-single: DELETE /conversation/delete?id=xxx
|
||||
await http.delete('/conversation/delete', { params: { id: sessionId } })
|
||||
console.log('✅ 删除会话成功')
|
||||
} catch (error) {
|
||||
console.error('❌ 删除会话失败:', error)
|
||||
@@ -147,7 +147,8 @@ export class ChatApiService {
|
||||
async updateSessionTitle(sessionId: string, title: string): Promise<void> {
|
||||
try {
|
||||
console.log('✏️ 更新会话标题API调用:', { sessionId, title })
|
||||
await http.put(`/conversation/${sessionId}`, { title })
|
||||
// backend-single: PUT /conversation/update 传id和title
|
||||
await http.put('/conversation/update', { id: sessionId, title })
|
||||
console.log('✅ 更新会话标题成功')
|
||||
} catch (error) {
|
||||
console.error('❌ 更新会话标题失败:', error)
|
||||
|
||||
@@ -57,7 +57,8 @@ export const messageApi = {
|
||||
// 获取用户消息分页
|
||||
getUserMessages: async (current: number = 1, size: number = 20) => {
|
||||
console.log('📨 调用getUserMessages API:', { current, size })
|
||||
const response = await http.get(`/message/user/page`, { params: { current, size } })
|
||||
// backend-single: GET /message/page (后端根据token识别用户)
|
||||
const response = await http.get(`/message/page`, { params: { current, size } })
|
||||
console.log('📨 getUserMessages API响应:', response)
|
||||
return response
|
||||
},
|
||||
@@ -65,23 +66,31 @@ export const messageApi = {
|
||||
// 搜索用户消息
|
||||
searchUserMessages: async (keyword: string, limit: number = 50) => {
|
||||
console.log('🔍 调用searchUserMessages API:', { keyword, limit })
|
||||
const response = await http.post(`/message/user/search`, { keyword, limit })
|
||||
console.log('🔍 searchUserMessages API响应:', response)
|
||||
return response
|
||||
// backend-single: POST /message/search
|
||||
const resp = await http.post(`/message/search`, { keyword, limit })
|
||||
console.log('🔍 searchUserMessages API响应:', resp)
|
||||
// 统一返回数组,兼容控制器返回 PageResult 结构
|
||||
const data: any = (resp as any).data || resp
|
||||
const records = data.records || data
|
||||
return Array.isArray(records) ? records : []
|
||||
},
|
||||
|
||||
// 获取用户最近的聊天记录 - 修复:使用POST请求匹配后端接口
|
||||
// 获取用户最近的聊天记录 - 返回数组,兼容后端 PageResult 结构
|
||||
getRecentMessages: async (limit: number = 10) => {
|
||||
console.log('📝 调用getRecentMessages API:', { limit })
|
||||
const response = await http.post(`/message/user/recent`, { limit })
|
||||
console.log('📝 getRecentMessages API响应:', response)
|
||||
return response
|
||||
// backend-single: POST /message/recent
|
||||
const resp = await http.post(`/message/recent`, { limit })
|
||||
console.log('📝 getRecentMessages API响应:', resp)
|
||||
const data: any = (resp as any).data || resp
|
||||
const records = data.records || data
|
||||
return Array.isArray(records) ? records : []
|
||||
},
|
||||
|
||||
// 获取消息详情
|
||||
getMessageById: async (id: string) => {
|
||||
console.log('📄 调用getMessageById API:', { id })
|
||||
const response = await http.get(`/message/${id}`)
|
||||
// backend-single: GET /message/detail?id=xxx
|
||||
const response = await http.get(`/message/detail`, { params: { id } })
|
||||
console.log('📄 getMessageById API响应:', response)
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -207,9 +207,9 @@ export class StompWebSocketService {
|
||||
console.log('📤 准备发送的聊天请求:', chatRequest)
|
||||
|
||||
try {
|
||||
// 发送到后端的/app/chat.send端点
|
||||
// 发送到后端的/app/chat/send端点(对应 @MessageMapping("/chat") + @MessageMapping("/send"))
|
||||
this.client.publish({
|
||||
destination: '/app/chat.send',
|
||||
destination: '/app/chat/send',
|
||||
body: JSON.stringify(chatRequest)
|
||||
})
|
||||
console.log('✅ STOMP聊天消息发送成功:', chatRequest)
|
||||
@@ -324,7 +324,7 @@ export class StompWebSocketService {
|
||||
|
||||
try {
|
||||
this.client.publish({
|
||||
destination: '/app/chat.connect',
|
||||
destination: '/app/chat/connect',
|
||||
body: JSON.stringify(connectRequest)
|
||||
})
|
||||
console.log('✅ STOMP连接消息发送成功:', connectRequest)
|
||||
|
||||
+13
-9
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
import type { ChatMessage, ChatSession } from '@/types'
|
||||
import { stompWebSocketService, type WebSocketMessage, type ConnectionStatus } from '@/services/stomp-websocket'
|
||||
import { useAuthStore } from './auth'
|
||||
@@ -381,31 +381,35 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
|
||||
// 添加AI回复消息(直接显示完整内容)
|
||||
const addAiReplyMessages = (content: string) => {
|
||||
const addAiReplyMessages = async (content: string) => {
|
||||
// 停止输入状态
|
||||
isTyping.value = false
|
||||
|
||||
// 使用 nextTick 确保 DOM 更新的顺序性,避免与定时同步并发
|
||||
await nextTick()
|
||||
|
||||
// 直接添加完整的AI回复
|
||||
const aiMessage = addMessage({
|
||||
content: content.trim(),
|
||||
type: 'ai',
|
||||
sessionId: currentSession.value?.id
|
||||
conversationId: currentSession.value?.id
|
||||
})
|
||||
|
||||
// 强制触发响应式更新
|
||||
console.log('AI消息已添加,当前消息总数:', messages.value.length)
|
||||
console.log('最新AI消息:', aiMessage)
|
||||
console.log('✅ AI消息已添加,当前消息总数:', messages.value.length)
|
||||
console.log('📝 最新AI消息:', aiMessage)
|
||||
console.log('📊 所有消息:', messages.value)
|
||||
}
|
||||
|
||||
// WebSocket消息处理
|
||||
let handleWebSocketMessage = (wsMessage: WebSocketMessage) => {
|
||||
let handleWebSocketMessage = async (wsMessage: WebSocketMessage) => {
|
||||
console.log('收到WebSocket消息:', wsMessage.type, wsMessage.senderType)
|
||||
|
||||
switch (wsMessage.type) {
|
||||
case 'TEXT':
|
||||
if (wsMessage.senderType === 'AI') {
|
||||
// AI回复消息 - 支持分段显示
|
||||
addAiReplyMessages(wsMessage.content)
|
||||
await addAiReplyMessages(wsMessage.content)
|
||||
}
|
||||
break
|
||||
|
||||
@@ -602,8 +606,8 @@ export const useChatStore = defineStore('chat', () => {
|
||||
onMessage: (callback: (message: any) => void) => {
|
||||
// 简单的消息监听实现
|
||||
const originalHandler = handleWebSocketMessage
|
||||
handleWebSocketMessage = (message: any) => {
|
||||
originalHandler(message)
|
||||
handleWebSocketMessage = async (message: any) => {
|
||||
await originalHandler(message)
|
||||
callback(message)
|
||||
}
|
||||
}
|
||||
|
||||
+35
-135
@@ -67,8 +67,8 @@
|
||||
<p class="mt-2 text-gray-600">加载对话记录中...</p>
|
||||
</div>
|
||||
|
||||
<!-- 欢迎消息 -->
|
||||
<div v-else-if="messages.length === 0" class="text-center py-12">
|
||||
<!-- 欢迎消息(使用 v-if,避免与列表互斥切换引发的 DOM 竞态) -->
|
||||
<div v-if="messages.length === 0" class="text-center py-12">
|
||||
<img
|
||||
:src="kaikaiAvatar"
|
||||
alt="开开"
|
||||
@@ -81,16 +81,16 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div v-else class="space-y-4">
|
||||
<!-- 消息列表(使用 v-show 保持节点稳定,移除key避免频繁重新挂载) -->
|
||||
<div v-show="messages.length > 0" class="space-y-4">
|
||||
<div
|
||||
v-for="(message, index) in messages"
|
||||
:key="`msg-${message.id}-${index}`"
|
||||
v-for="message in messages"
|
||||
:key="message.id"
|
||||
class="flex w-full items-end mb-4"
|
||||
:class="message.role === 'user' ? 'justify-end' : 'justify-start'"
|
||||
:class="message.type === 'user' ? 'justify-end' : 'justify-start'"
|
||||
>
|
||||
<!-- AI消息 -->
|
||||
<template v-if="message.role === 'assistant'">
|
||||
<template v-if="message.type === 'ai'">
|
||||
<img
|
||||
:src="kaikaiAvatar"
|
||||
alt="开开"
|
||||
@@ -105,7 +105,7 @@
|
||||
</template>
|
||||
|
||||
<!-- 用户消息 -->
|
||||
<template v-else-if="message.role === 'user'">
|
||||
<template v-else-if="message.type === 'user'">
|
||||
<div class="max-w-xs md:max-w-md lg:max-w-lg">
|
||||
<div class="bg-tech-blue text-white rounded-l-2xl rounded-tr-2xl p-3 px-4 shadow-md">
|
||||
<p class="leading-relaxed whitespace-pre-wrap">{{ message.content }}</p>
|
||||
@@ -122,27 +122,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- AI正在输入指示器 -->
|
||||
<div v-if="chatStore.isTyping" class="flex w-full items-end justify-start">
|
||||
<img
|
||||
:src="kaikaiAvatar"
|
||||
alt="开开"
|
||||
class="w-10 h-10 rounded-full mr-3 self-start flex-shrink-0"
|
||||
>
|
||||
<div class="max-w-xs md:max-w-md lg:max-w-lg">
|
||||
<div class="bg-white text-text-dark rounded-r-2xl rounded-tl-2xl p-3 px-4 shadow-md border border-gray-100">
|
||||
<div class="flex items-center space-x-1">
|
||||
<div class="flex space-x-1">
|
||||
<div class="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
|
||||
<div class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay: 0.1s"></div>
|
||||
<div class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay: 0.2s"></div>
|
||||
</div>
|
||||
<span class="text-sm text-gray-500 ml-2">开开正在输入...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -193,13 +172,16 @@ import BottomNavigation from '@/components/layout/BottomNavigation.vue'
|
||||
const chatStore = useChatStore()
|
||||
|
||||
// 响应式数据
|
||||
const messages = ref<ChatMessage[]>([])
|
||||
// 直接使用 chatStore.messages,避免计算属性导致的重新计算
|
||||
const messages = computed(() => chatStore.messages)
|
||||
|
||||
const inputMessage = ref('')
|
||||
const sending = ref(false)
|
||||
const loading = ref(false)
|
||||
const messagesContainer = ref<HTMLElement>()
|
||||
const messageInput = ref<HTMLTextAreaElement>()
|
||||
const lastSyncedMessageCount = ref(0) // 记录上次同步的消息数量
|
||||
// 定时同步句柄(避免在 onMounted 内部注册 onUnmounted)
|
||||
let syncInterval: any = null
|
||||
|
||||
// 头像
|
||||
const kaikaiAvatar = 'https://r2.flowith.net/files/o/1752574406770-thoughtful_kaikai_character_generation_index_1@1024x1024.png'
|
||||
@@ -267,18 +249,18 @@ const forceScrollToBottom = () => {
|
||||
// 加载消息
|
||||
const loadMessages = async () => {
|
||||
loading.value = true
|
||||
|
||||
|
||||
try {
|
||||
// 调用最近消息API
|
||||
const response = await messageApi.getRecentMessages(50)
|
||||
|
||||
|
||||
// 提取消息数据
|
||||
const messageList = response.data || response || []
|
||||
|
||||
|
||||
if (Array.isArray(messageList)) {
|
||||
// 转换消息格式
|
||||
const chatMessages = MessageService.convertToChatMessages(messageList)
|
||||
|
||||
|
||||
// 按时间排序(最早的在前面)
|
||||
chatMessages.sort((a, b) => {
|
||||
const parseTime = (timestamp: string | Date) => {
|
||||
@@ -291,24 +273,19 @@ const loadMessages = async () => {
|
||||
}
|
||||
return new Date().getTime()
|
||||
}
|
||||
|
||||
|
||||
return parseTime(a.timestamp) - parseTime(b.timestamp)
|
||||
})
|
||||
|
||||
messages.value = chatMessages
|
||||
|
||||
// 初始化同步计数
|
||||
lastSyncedMessageCount.value = chatStore.messages.length
|
||||
// 将消息添加到 chatStore
|
||||
chatStore.messages.splice(0, chatStore.messages.length, ...chatMessages)
|
||||
|
||||
// 强制滚动到底部
|
||||
await nextTick()
|
||||
forceScrollToBottom()
|
||||
} else {
|
||||
messages.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 加载消息失败:', error)
|
||||
messages.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -325,12 +302,8 @@ const sendMessage = async () => {
|
||||
sending.value = true
|
||||
|
||||
try {
|
||||
// 强制滚动到底部(为即将到来的消息做准备)
|
||||
await nextTick()
|
||||
forceScrollToBottom()
|
||||
|
||||
// 直接通过WebSocket发送消息,让chatStore处理消息添加
|
||||
// 这样避免重复添加消息
|
||||
// 计算属性会自动响应 chatStore 的变化
|
||||
await chatStore.sendMessage(content)
|
||||
|
||||
} catch (error) {
|
||||
@@ -340,53 +313,11 @@ const sendMessage = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 从chatStore同步消息(完全重新构建消息列表)
|
||||
const syncWithChatStore = () => {
|
||||
const storeMessages = chatStore.messages
|
||||
|
||||
// 如果store消息数量没有变化,跳过同步
|
||||
if (storeMessages.length === lastSyncedMessageCount.value) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('🔄 同步chatStore消息,数量:', storeMessages.length)
|
||||
|
||||
// 转换所有store消息
|
||||
const convertedMessages = storeMessages.map(msg => ({
|
||||
id: msg.id,
|
||||
content: msg.content,
|
||||
role: msg.type === 'user' ? 'user' : 'assistant',
|
||||
type: msg.type,
|
||||
timestamp: msg.timestamp,
|
||||
status: msg.status || 'sent',
|
||||
sender: msg.type === 'user' ? 'user' : 'ai'
|
||||
} as ChatMessage))
|
||||
|
||||
// 按时间排序
|
||||
convertedMessages.sort((a, b) => {
|
||||
const parseTime = (timestamp: string | Date) => {
|
||||
if (timestamp instanceof Date) return timestamp.getTime()
|
||||
if (typeof timestamp === 'string') {
|
||||
if (timestamp.includes(' ') && !timestamp.includes('T')) {
|
||||
return new Date(timestamp.replace(' ', 'T')).getTime()
|
||||
}
|
||||
return new Date(timestamp).getTime()
|
||||
}
|
||||
return new Date().getTime()
|
||||
}
|
||||
|
||||
return parseTime(a.timestamp) - parseTime(b.timestamp)
|
||||
})
|
||||
|
||||
// 完全替换消息列表
|
||||
messages.value = convertedMessages
|
||||
|
||||
// 更新同步计数
|
||||
lastSyncedMessageCount.value = storeMessages.length
|
||||
|
||||
// 强制滚动到底部
|
||||
nextTick(() => forceScrollToBottom())
|
||||
}
|
||||
// 监听消息变化,自动滚动到底部
|
||||
watch(() => messages.value.length, async () => {
|
||||
await nextTick()
|
||||
forceScrollToBottom()
|
||||
})
|
||||
|
||||
// 调整文本框高度
|
||||
const adjustTextareaHeight = () => {
|
||||
@@ -412,38 +343,16 @@ onMounted(async () => {
|
||||
|
||||
// 监听WebSocket消息
|
||||
try {
|
||||
chatStore.onMessage((message: any) => {
|
||||
// 创建AI消息
|
||||
const aiMessage: ChatMessage = {
|
||||
id: message.id || `ai_${Date.now()}`,
|
||||
content: message.content || message.message || String(message),
|
||||
role: 'assistant',
|
||||
type: 'ai',
|
||||
timestamp: message.timestamp || new Date().toISOString(),
|
||||
status: 'sent',
|
||||
sender: 'ai'
|
||||
}
|
||||
|
||||
// 添加到消息列表
|
||||
messages.value.push(aiMessage)
|
||||
|
||||
// 强制滚动到底部
|
||||
nextTick(() => forceScrollToBottom())
|
||||
chatStore.onMessage(async (_message: any) => {
|
||||
// 消息已经被添加到 chatStore,计算属性会自动更新
|
||||
console.log('📨 Chat页面收到WebSocket消息回调')
|
||||
await nextTick()
|
||||
forceScrollToBottom()
|
||||
})
|
||||
} catch (error) {
|
||||
console.warn('⚠️ 设置WebSocket监听器失败:', error)
|
||||
}
|
||||
|
||||
// 定期同步chatStore消息(确保不遗漏)
|
||||
const syncInterval = setInterval(() => {
|
||||
syncWithChatStore()
|
||||
}, 1000)
|
||||
|
||||
// 组件卸载时清理定时器
|
||||
onUnmounted(() => {
|
||||
clearInterval(syncInterval)
|
||||
})
|
||||
|
||||
|
||||
// 确保初始化完成后滚动到底部
|
||||
await nextTick()
|
||||
setTimeout(() => forceScrollToBottom(), 100)
|
||||
@@ -456,18 +365,9 @@ onUnmounted(() => {
|
||||
chatStore.disconnectWebSocket()
|
||||
})
|
||||
|
||||
// 监听消息变化,自动滚动
|
||||
watch(() => messages.value.length, () => {
|
||||
nextTick(() => forceScrollToBottom())
|
||||
})
|
||||
// 监听chatStore消息变化(移除,避免与 onMessage/定时同步重复触发导致渲染竞态)
|
||||
// 保留通过 onMessage 事件与定时器同步的方式,减少同一 tick 内的多次 DOM 更新
|
||||
|
||||
// 监听chatStore消息变化
|
||||
watch(() => chatStore.messages.length, (newLength, oldLength) => {
|
||||
if (newLength > oldLength) {
|
||||
// 有新消息时同步
|
||||
syncWithChatStore()
|
||||
}
|
||||
}, { immediate: false })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="forgot-page">
|
||||
<div class="card">
|
||||
<h2 class="title">重置密码</h2>
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="0">
|
||||
<el-form-item prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入手机号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="newPassword">
|
||||
<el-input v-model="form.newPassword" placeholder="请输入新密码" show-password clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="captcha">
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码(123456)" clearable />
|
||||
</el-form-item>
|
||||
<el-button type="primary" class="w-full" :loading="submitting" @click="onSubmit">提交</el-button>
|
||||
</el-form>
|
||||
<div class="mt-4 text-center">
|
||||
<router-link to="/login">返回登录</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage, FormInstance, FormRules } from 'element-plus'
|
||||
import AuthService from '@/services/auth'
|
||||
import type { ResetPasswordRequest } from '@/types/auth'
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const form = ref<ResetPasswordRequest>({ phone: '', newPassword: '', captcha: '' })
|
||||
|
||||
const rules: FormRules = {
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: ['blur', 'change'] }
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度6-20位', trigger: ['blur', 'change'] }
|
||||
],
|
||||
captcha: [
|
||||
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
try {
|
||||
submitting.value = true
|
||||
await AuthService.resetPassword(form.value)
|
||||
ElMessage.success('重置密码成功,请使用新密码登录')
|
||||
} catch (e) {
|
||||
ElMessage.error('重置密码失败,请稍后重试')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.forgot-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
.card { width: 360px; background: #fff; padding: 24px; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||
.title { text-align: center; margin-bottom: 16px; }
|
||||
.w-full { width: 100%; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user