151 lines
3.3 KiB
Vue
151 lines
3.3 KiB
Vue
<template>
|
||
<div class="chat-window">
|
||
<div class="messages" ref="messageList">
|
||
<div v-for="(msg, index) in messages" :key="index" :class="msg.role">
|
||
<strong>{{ msg.role === 'user' ? '你' : '助手' }}:</strong>
|
||
<span>{{ msg.content }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="input-box">
|
||
<textarea
|
||
v-model="input"
|
||
@keydown.enter.prevent="handleSend"
|
||
placeholder="请输入..."
|
||
/>
|
||
<button @click="handleSend" :disabled="loading">发送</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
messages: [],
|
||
input: '',
|
||
loading: false,
|
||
eventSource: null,
|
||
conversationId: '3841325303385792512'
|
||
};
|
||
},
|
||
watch: {
|
||
conversationId: {
|
||
immediate: true,
|
||
handler() {
|
||
this.messages = []; // 如果你有加载历史消息,可以在此请求
|
||
this.closeStream();
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
async handleSend() {
|
||
if (!this.input.trim()) return;
|
||
this.loading = true;
|
||
|
||
// 添加用户消息
|
||
const userMessage = { role: 'user', content: this.input };
|
||
this.messages.push(userMessage);
|
||
|
||
// 添加助手消息框架
|
||
const assistantMessage = { role: 'assistant', content: '' };
|
||
this.messages.push(assistantMessage);
|
||
|
||
const message = encodeURIComponent(this.input);
|
||
const url = `/api/ichat/message/completion/`;
|
||
const payload = {
|
||
conversation: this.conversationId,
|
||
message: message
|
||
}
|
||
this.input = '';
|
||
this.scrollToBottom();
|
||
|
||
try {
|
||
const response = await fetch('/api/ichat/message/completion/', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (response.ok) {
|
||
// 更新 assistant 内容
|
||
assistantMessage.content = result.answer || '';
|
||
|
||
// 保存 conversation_id(首次发送消息时获取)
|
||
if (!this.conversationId && result.conversation_id) {
|
||
this.conversationId = result.conversation_id;
|
||
}
|
||
|
||
this.scrollToBottom();
|
||
} else {
|
||
throw new Error(result.detail || '请求失败');
|
||
}
|
||
} catch (err) {
|
||
console.error('发送失败:', err);
|
||
assistantMessage.content = '[助手响应失败,请稍后再试]';
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
scrollToBottom() {
|
||
this.$nextTick(() => {
|
||
const el = this.$refs.messageList;
|
||
if (el) el.scrollTop = el.scrollHeight;
|
||
});
|
||
},
|
||
closeStream() {
|
||
if (this.eventSource) {
|
||
this.eventSource.close();
|
||
this.eventSource = null;
|
||
}
|
||
}
|
||
},
|
||
beforeUnmount() {
|
||
this.closeStream();
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.chat-window {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
padding: 10px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.messages {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding-right: 10px;
|
||
}
|
||
|
||
.user, .assistant {
|
||
margin-bottom: 10px;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.input-box {
|
||
display: flex;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
textarea {
|
||
flex: 1;
|
||
resize: none;
|
||
height: 60px;
|
||
padding: 8px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
button {
|
||
margin-left: 10px;
|
||
padding: 0 16px;
|
||
}
|
||
</style>
|