两端同步流式输出,刷新状态,强制退出。
This commit is contained in:
@@ -507,6 +507,10 @@
|
||||
font-size: 48rpx;
|
||||
}
|
||||
|
||||
.chat-btn-group .head-btn:nth-child(4) {
|
||||
/* 刷新按钮 */
|
||||
}
|
||||
|
||||
.log-out {
|
||||
/* background: #ffd4d4; */
|
||||
}
|
||||
|
||||
@@ -99,10 +99,13 @@
|
||||
<view class="head-btn" @click="goWorkSpace">
|
||||
<view class="iconfont icon-wenjianjia"></view>
|
||||
</view>
|
||||
<view class="head-btn" @click="goCloudDatabase">
|
||||
<view class="iconfont icon-cloud"></view>
|
||||
</view>
|
||||
<view class="head-btn log-out" @click="logOut">
|
||||
<view class="head-btn" @click="goCloudDatabase">
|
||||
<view class="iconfont icon-cloud"></view>
|
||||
</view>
|
||||
<view class="head-btn" @click="handleRefresh">
|
||||
<uni-icons type="refreshempty" size="24" color="#52c41a"></uni-icons>
|
||||
</view>
|
||||
<view class="head-btn log-out" @click="logOut">
|
||||
<view class="iconfont icon-tuichu"></view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -839,6 +842,71 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新当前会话消息
|
||||
const handleRefresh = async () => {
|
||||
// 1. 重置输入框状态
|
||||
isThinking.value = false
|
||||
isSelfSent.value = false
|
||||
socketStore.isThinking = false
|
||||
textMessage.value = ''
|
||||
uploadedFiles.value = []
|
||||
previewFileArray.value = []
|
||||
// 移除流式消息占位
|
||||
if (streamingMessageId.value) {
|
||||
allmessages.value = allmessages.value.filter(m => m._id !== streamingMessageId.value)
|
||||
streamingMessageId.value = null
|
||||
}
|
||||
|
||||
const titleMap = { 0: 'AI消息', 1: '好友消息', 2: '群聊消息' }
|
||||
const title = titleMap[ChatType.value] || '消息'
|
||||
uni.showLoading({ title: `刷新${title}中...`, mask: true })
|
||||
|
||||
try {
|
||||
// 2. 检查当前 token 是否还有效
|
||||
const token = getToken()
|
||||
if (!token) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '登录已失效,请重新登录', icon: 'none', duration: 1500 })
|
||||
setTimeout(() => uni.reLaunch({ url: '/pages/Login/Login' }), 1500)
|
||||
return
|
||||
}
|
||||
await getUserInfo(token)
|
||||
|
||||
// 3. 确保 AI 对话模式下 socket 连接正常(PC 离线由 watcher 实时监控)
|
||||
if (ChatType.value === 0 && !socketStore.isConnected) {
|
||||
await handleConnect()
|
||||
}
|
||||
|
||||
// 4. 刷新消息
|
||||
switch (ChatType.value) {
|
||||
case 0:
|
||||
await takeConversationMessages()
|
||||
break
|
||||
case 1:
|
||||
await takeFriendMessages()
|
||||
break
|
||||
case 2:
|
||||
await takeGroupMessages()
|
||||
break
|
||||
default:
|
||||
await takeConversationMessages()
|
||||
break
|
||||
}
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '已刷新', icon: 'success', duration: 1200 })
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
// 区分 token 失效和其他错误
|
||||
const errMsg = typeof e === 'string' ? e : (e?.message || '')
|
||||
if (errMsg.includes('token') || errMsg.includes('Token') || errMsg.includes('登录') || errMsg.includes('过期')) {
|
||||
uni.showToast({ title: '登录已失效,请重新登录', icon: 'none', duration: 1500 })
|
||||
setTimeout(() => uni.reLaunch({ url: '/pages/Login/Login' }), 1500)
|
||||
} else {
|
||||
uni.showToast({ title: '刷新失败', icon: 'error', duration: 1500 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const previewFileArray = ref([])
|
||||
const uploadPhoto = () => {
|
||||
console.log('点击了拍照上传');
|
||||
@@ -1368,7 +1436,10 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
if (!oldVal && newVal) {
|
||||
console.log('AI开始回复(跨设备同步),锁定输入框');
|
||||
isThinking.value = true
|
||||
// 非本设备发起时,也需要添加流式气泡
|
||||
// 跨设备场景:先加载最新消息(含 PC 发的用户消息),再追加流式气泡
|
||||
if (!isSelfSent.value) {
|
||||
await takeConversationMessages()
|
||||
}
|
||||
if (!streamingMessageId.value) {
|
||||
streamingMessageId.value = 'streaming-' + Date.now()
|
||||
allmessages.value = [...allmessages.value, {
|
||||
@@ -1416,6 +1487,25 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
}
|
||||
})
|
||||
|
||||
// 监听 pc_offline,PC 端下线时立即强制跳转登录页
|
||||
watch(() => socketStore.pcOffline, (offline) => {
|
||||
if (offline) {
|
||||
isThinking.value = false
|
||||
socketStore.isThinking = false
|
||||
socketStore.pcOffline = false
|
||||
uni.showToast({
|
||||
title: 'PC端不在线,请先登录',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/Login/Login'
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
})
|
||||
|
||||
// 监听流式消息内容,实时更新 AI 回复(流式输出核心)
|
||||
watch(() => socketStore.messageString, (newContent) => {
|
||||
if (!streamingMessageId.value || !socketStore.isThinking) return
|
||||
|
||||
@@ -30,6 +30,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||
const maxLogCount = ref(20)
|
||||
|
||||
const authFailReason = ref('')
|
||||
const pcOffline = ref(false)
|
||||
|
||||
// ==================== 计算属性 ====================
|
||||
const isConnected = computed(() => connectionStatus.value === 'connected')
|
||||
@@ -66,6 +67,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||
...config.value,
|
||||
...options
|
||||
}
|
||||
pcOffline.value = false // 每次连接时重置 PC 离线标记
|
||||
addLog('info',
|
||||
`准备连接WebSocket。Token:${config.value.token || '未设置'}。ConversationId: ${config.value.conversationId || '未设置'}`
|
||||
)
|
||||
@@ -182,6 +184,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||
break
|
||||
|
||||
case 'pc_offline':
|
||||
pcOffline.value = true
|
||||
addLog('warn', 'PC 端已离线')
|
||||
break
|
||||
|
||||
@@ -287,6 +290,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||
logs,
|
||||
config,
|
||||
authFailReason,
|
||||
pcOffline,
|
||||
|
||||
// 计算属性
|
||||
isConnected,
|
||||
|
||||
Reference in New Issue
Block a user