diff --git a/src/oc-community-frontend/src/components/ChatWindow.vue b/src/oc-community-frontend/src/components/ChatWindow.vue index 1027090..2625264 100644 --- a/src/oc-community-frontend/src/components/ChatWindow.vue +++ b/src/oc-community-frontend/src/components/ChatWindow.vue @@ -182,6 +182,33 @@ function formatDateTime(ms) { } catch (e) { return '' } } +// 查找下一个“休闲”类活动的开始时间(返回 Date 或 null) +function findNextRelaxStart(oc) { + try { + if (!oc || !Array.isArray(oc.schedule)) return null + const now = new Date() + const todayStr = now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0') + '-' + String(now.getDate()).padStart(2,'0') + // 收集未来或当日尚未开始的活动 + const candidates = [] + for (const s of oc.schedule) { + const dateStr = s.date || todayStr + const timeStr = (s.time || '00:00').slice(0,5) + const start = new Date(`${dateStr}T${timeStr}`) + if (start.getTime() > Date.now()) { + const eventText = (s.event || '').toLowerCase() + if (/休息|放松|休闲|看电影|娱乐|咖啡|喝茶/ig.test(eventText)) { + candidates.push(start) + } + } + } + if (!candidates.length) return null + candidates.sort((a,b) => a.getTime() - b.getTime()) + return candidates[0] + } catch (e) { + return null + } +} + // 当 activeContact 或其日程变更时,如果有待处理的占位回复,重新计算并重设定时器 watch(() => [props.activeContact && props.activeContact.schedule ? props.activeContact.schedule.length : 0, currentScheduleEnd.value], () => { // 当 schedule 或 currentScheduleEnd 变化时,如果存在一个待处理的占位回复,重新安排定时器 @@ -265,12 +292,23 @@ const performAIReply = async (batchId, userText) => { if (Array.isArray(reply)) reply = reply.join('\n') reply = String(reply).trim() if (!reply) reply = '(刚在忙,你说啥?)' - + const hadPlaceholder = messages.value.some(m => m.batchId === batchId && m.isPlaceholder) messages.value = messages.value.map(m => m.batchId === batchId && m.isPlaceholder ? { ...m, text: reply, timestamp: Date.now(), isPlaceholder: false } : m ) + if (!hadPlaceholder) { + // 如果没有占位消息,则直接追加 AI 回复 + const replyMsg = { + senderId: props.activeContact?.id || 'ai', + text: reply, + timestamp: Date.now(), + batchId: batchId, + isPlaceholder: false + } + messages.value = [...messages.value, replyMsg] + } emit('update:modelValue', messages.value) } catch (err) { console.error('AI回复错误:', err) @@ -282,11 +320,22 @@ const performAIReply = async (batchId, userText) => { console.error('服务器无响应:', err.request) errMsg = '服务失联,请检查网络' } + const hadPlaceholder = messages.value.some(m => m.batchId === batchId && m.isPlaceholder) messages.value = messages.value.map(m => m.batchId === batchId && m.isPlaceholder ? { ...m, text: errMsg, timestamp: Date.now(), isPlaceholder: false } : m ) + if (!hadPlaceholder) { + const errMsgObj = { + senderId: props.activeContact?.id || 'ai', + text: errMsg, + timestamp: Date.now(), + batchId: batchId, + isPlaceholder: false + } + messages.value = [...messages.value, errMsgObj] + } emit('update:modelValue', messages.value) } } @@ -331,18 +380,7 @@ const handleSend = async () => { const API_KEY = import.meta.env.VITE_ZHIPU_API_KEY - // 创建AI占位消息 - 添加相同的batchId - const aiPlaceholderMsg = { - senderId: props.activeContact?.id || 'ai', - text: '对方正在输入中...', - timestamp: Date.now() + 1, - batchId: messageBatchId, - isPlaceholder: true // 明确标记这是占位消息 - } - - nextMessages = [...messages.value, aiPlaceholderMsg] - messages.value = nextMessages - emit('update:modelValue', nextMessages) + // 不再插入“对方正在输入中...”占位提示,直接等待 AI 回复(或发送忙碌提示) const isBusy = statusClass.value === 'busy' || statusText.value.startsWith('正在') @@ -351,6 +389,29 @@ const handleSend = async () => { pendingPlaceholderTs = messageBatchId if (isBusy) { + // 立即发送一条忙碌提示(包含下一个休闲活动开始时间),并保留占位消息,随后仍然触发 AI 回复 + const nextRelax = findNextRelaxStart(props.activeContact || {}) + let timeHint = '' + if (nextRelax) { + timeHint = formatDateTime(nextRelax.getTime()) + } else if (currentScheduleEnd.value) { + timeHint = formatDateTime(currentScheduleEnd.value) + } else { + timeHint = '' + } + + const busyText = `我正在${currentActivity.value || '忙碌'},稍后回复哦` + (timeHint ? `(下次空闲:${timeHint})` : '') + const busyNoticeMsg = { + senderId: props.activeContact?.id || 'oc', + text: busyText, + timestamp: Date.now() + 1, + system: true + } + // 插入忙碌提示到消息中并通知父组件 + nextMessages = [...messages.value, busyNoticeMsg] + messages.value = nextMessages + emit('update:modelValue', nextMessages) + // 计算等待时间(若有日程结束时间则等待至结束,否则默认 5 分钟) let waitMs = 0 if (currentScheduleEnd.value) {