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>
|