290 lines
5.6 KiB
Vue
290 lines
5.6 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="chat-page">
|
|||
|
|
<!-- 顶部 Tab 栏 -->
|
|||
|
|
<main-tabs :current="1"></main-tabs>
|
|||
|
|
|
|||
|
|
<!-- 聊天内容区域 -->
|
|||
|
|
<scroll-view
|
|||
|
|
class="message-container"
|
|||
|
|
scroll-y="true"
|
|||
|
|
:scroll-top="scrollTop"
|
|||
|
|
scroll-with-animation="true">
|
|||
|
|
<view class="message-list">
|
|||
|
|
<view
|
|||
|
|
v-for="(msg, index) in messages"
|
|||
|
|
:key="index"
|
|||
|
|
class="message-item"
|
|||
|
|
:class="msg.role === 'user' ? 'message-right' : 'message-left'">
|
|||
|
|
<image
|
|||
|
|
class="avatar"
|
|||
|
|
:src="msg.role === 'user' ? userAvatar : loverAvatar"
|
|||
|
|
mode="aspectFill">
|
|||
|
|
</image>
|
|||
|
|
<view class="message-bubble">
|
|||
|
|
<text class="message-text">{{ msg.content }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</scroll-view>
|
|||
|
|
|
|||
|
|
<!-- 底部输入框 -->
|
|||
|
|
<view class="input-bar">
|
|||
|
|
<input
|
|||
|
|
class="message-input"
|
|||
|
|
v-model="inputText"
|
|||
|
|
placeholder="输入消息..."
|
|||
|
|
confirm-type="send"
|
|||
|
|
@confirm="sendMessage"
|
|||
|
|
/>
|
|||
|
|
<view class="send-btn" @click="sendMessage">
|
|||
|
|
<text>发送</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import MainTabs from '@/components/main-tabs.vue';
|
|||
|
|
import { SessionInit, SessionSend } from '@/utils/api.js';
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
components: {
|
|||
|
|
MainTabs
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
messages: [],
|
|||
|
|
inputText: '',
|
|||
|
|
scrollTop: 0,
|
|||
|
|
sessionId: null,
|
|||
|
|
userAvatar: '',
|
|||
|
|
loverAvatar: '',
|
|||
|
|
sending: false
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
onLoad() {
|
|||
|
|
// 获取用户头像
|
|||
|
|
const userInfo = uni.getStorageSync('userinfo');
|
|||
|
|
this.userAvatar = userInfo?.avatar || '/static/images/avatar.png';
|
|||
|
|
|
|||
|
|
// 获取恋人头像
|
|||
|
|
const loverInfo = uni.getStorageSync('loverBasicList');
|
|||
|
|
this.loverAvatar = loverInfo?.image_url || '/static/images/avatar.png';
|
|||
|
|
|
|||
|
|
// 初始化会话
|
|||
|
|
this.initSession();
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
// 初始化会话
|
|||
|
|
initSession() {
|
|||
|
|
uni.showLoading({ title: '加载中...' });
|
|||
|
|
|
|||
|
|
SessionInit().then(res => {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
if (res.code === 1) {
|
|||
|
|
this.sessionId = res.data.session_id;
|
|||
|
|
// 加载历史消息(只显示最近10条)
|
|||
|
|
const allMessages = res.data.messages || [];
|
|||
|
|
this.messages = allMessages.slice(-10).map(msg => ({
|
|||
|
|
role: msg.role === 'lover' ? 'ai' : 'user',
|
|||
|
|
content: msg.content
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
// 滚动到底部
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.scrollToBottom();
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
uni.showToast({ title: res.msg || '加载失败', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
}).catch(err => {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
console.error('初始化会话失败:', err);
|
|||
|
|
uni.showToast({ title: '网络错误', icon: 'none' });
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 发送消息
|
|||
|
|
sendMessage() {
|
|||
|
|
if (!this.inputText.trim()) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (this.sending) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!this.sessionId) {
|
|||
|
|
uni.showToast({ title: '会话未初始化', icon: 'none' });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const userMessage = this.inputText.trim();
|
|||
|
|
this.inputText = '';
|
|||
|
|
|
|||
|
|
// 添加用户消息到列表
|
|||
|
|
this.messages.push({
|
|||
|
|
role: 'user',
|
|||
|
|
content: userMessage
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 滚动到底部
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.scrollToBottom();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 添加"思考中"提示
|
|||
|
|
const thinkingIndex = this.messages.length;
|
|||
|
|
this.messages.push({
|
|||
|
|
role: 'ai',
|
|||
|
|
content: '思考中...'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.sending = true;
|
|||
|
|
|
|||
|
|
// 发送到后端
|
|||
|
|
SessionSend({
|
|||
|
|
session_id: this.sessionId,
|
|||
|
|
message: userMessage
|
|||
|
|
}).then(res => {
|
|||
|
|
this.sending = false;
|
|||
|
|
|
|||
|
|
if (res.code === 1) {
|
|||
|
|
// 移除"思考中"
|
|||
|
|
this.messages.splice(thinkingIndex, 1);
|
|||
|
|
|
|||
|
|
// 添加 AI 回复
|
|||
|
|
this.messages.push({
|
|||
|
|
role: 'ai',
|
|||
|
|
content: res.data.reply || '...'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 滚动到底部
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.scrollToBottom();
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 移除"思考中"
|
|||
|
|
this.messages.splice(thinkingIndex, 1);
|
|||
|
|
uni.showToast({ title: res.msg || '发送失败', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
}).catch(err => {
|
|||
|
|
this.sending = false;
|
|||
|
|
// 移除"思考中"
|
|||
|
|
this.messages.splice(thinkingIndex, 1);
|
|||
|
|
console.error('发送消息失败:', err);
|
|||
|
|
uni.showToast({ title: '发送失败', icon: 'none' });
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 滚动到底部
|
|||
|
|
scrollToBottom() {
|
|||
|
|
this.scrollTop = 999999;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.chat-page {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100vh;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-container {
|
|||
|
|
flex: 1;
|
|||
|
|
margin-top: calc(80rpx + var(--status-bar-height));
|
|||
|
|
margin-bottom: 120rpx;
|
|||
|
|
padding: 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-list {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-item {
|
|||
|
|
display: flex;
|
|||
|
|
margin-bottom: 30rpx;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-left {
|
|||
|
|
flex-direction: row;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-right {
|
|||
|
|
flex-direction: row-reverse;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.avatar {
|
|||
|
|
width: 80rpx;
|
|||
|
|
height: 80rpx;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-bubble {
|
|||
|
|
max-width: 500rpx;
|
|||
|
|
padding: 20rpx 25rpx;
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
margin: 0 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-left .message-bubble {
|
|||
|
|
background: #fff;
|
|||
|
|
border-top-left-radius: 5rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-right .message-bubble {
|
|||
|
|
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-text {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-left .message-text {
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-right .message-text {
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-bar {
|
|||
|
|
position: fixed;
|
|||
|
|
bottom: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 20rpx;
|
|||
|
|
background: #fff;
|
|||
|
|
border-top: 1rpx solid #e5e5e5;
|
|||
|
|
z-index: 100;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message-input {
|
|||
|
|
flex: 1;
|
|||
|
|
height: 80rpx;
|
|||
|
|
padding: 0 25rpx;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
border-radius: 40rpx;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.send-btn {
|
|||
|
|
margin-left: 20rpx;
|
|||
|
|
padding: 20rpx 40rpx;
|
|||
|
|
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
|
|||
|
|
color: #fff;
|
|||
|
|
border-radius: 40rpx;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
}
|
|||
|
|
</style>
|