pet/pages/pets/pet-chat-simple.vue

443 lines
10 KiB
Vue

<template>
<view class="pet-chat-container">
<u-navbar :title="`与${petInfo.name}聊天`" left-icon="arrow-left" @left-click="goBack">
<template #right>
<u-icon name="more-circle" size="20" @click="showMenu = true"></u-icon>
</template>
</u-navbar>
<!-- 宠物信息卡片 -->
<view class="pet-info-card">
<u-avatar :src="petInfo.avatar || '/static/default-pet.png'" size="40" shape="circle"></u-avatar>
<view class="pet-info-text">
<u-text :text="petInfo.name" type="primary" size="14" bold></u-text>
<u-text :text="petInfo.personality" type="tips" size="12"></u-text>
</view>
<view class="chat-status">
<u-tag text="在线" type="success" size="mini"></u-tag>
</view>
</view>
<!-- 聊天消息列表 -->
<scroll-view
class="chat-messages"
scroll-y
:scroll-top="scrollTop"
scroll-with-animation
>
<view class="message-list">
<!-- 消息项 -->
<view
class="message-item"
v-for="message in messageList"
:key="message.id"
:class="message.type"
>
<view class="message-avatar" v-if="message.type === 'ai'">
<u-avatar :src="petInfo.avatar || '/static/default-pet.png'" size="32" shape="circle"></u-avatar>
</view>
<view class="message-content">
<view class="message-bubble" :class="message.type">
<u-text :text="message.content" size="14" :color="message.type === 'user' ? '#ffffff' : '#333333'"></u-text>
</view>
<view class="message-time">
<u-text :text="formatTime(message.timestamp)" type="tips" size="10"></u-text>
</view>
</view>
<view class="message-avatar" v-if="message.type === 'user'">
<u-avatar src="/static/user-avatar.png" size="32" shape="circle"></u-avatar>
</view>
</view>
<!-- AI正在输入提示 -->
<view class="message-item ai" v-if="aiTyping">
<view class="message-avatar">
<u-avatar :src="petInfo.avatar || '/static/default-pet.png'" size="32" shape="circle"></u-avatar>
</view>
<view class="message-content">
<view class="message-bubble ai typing">
<view class="typing-dots">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 输入区域 -->
<view class="chat-input-area">
<view class="input-container">
<u-input
v-model="inputMessage"
placeholder="和你的宠物说点什么..."
:border="false"
:custom-style="inputStyle"
@confirm="sendMessage"
confirm-type="send"
></u-input>
<view class="input-actions">
<u-button
type="primary"
size="mini"
text="发送"
@click="sendMessage"
:disabled="!inputMessage.trim()"
></u-button>
</view>
</view>
</view>
<!-- 聊天菜单弹窗 -->
<u-popup v-model="showMenu" mode="bottom" border-radius="20">
<view class="chat-menu">
<view class="menu-title">
<u-text text="聊天设置" type="primary" size="16" bold></u-text>
</view>
<view class="menu-items">
<view class="menu-item" @click="clearHistory">
<u-icon name="delete" size="20" color="#ff6b6b"></u-icon>
<u-text text="清空聊天记录" size="14"></u-text>
</view>
</view>
<view class="menu-cancel" @click="showMenu = false">
<u-text text="取消" type="info" size="14"></u-text>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
name: 'PetChatSimple',
data() {
return {
petId: '',
petInfo: {},
messageList: [],
inputMessage: '',
scrollTop: 0,
aiTyping: false,
showMenu: false,
inputStyle: {
backgroundColor: '#f8f9fa',
borderRadius: '20px',
padding: '10px 15px'
}
}
},
onLoad(options) {
this.petId = options.petId || '1'
this.loadPetInfo()
this.loadChatHistory()
},
methods: {
// 加载宠物信息
loadPetInfo() {
const mockPets = [
{
id: '1',
name: '小橘',
breed: '橘猫',
personality: '活泼好动,喜欢撒娇',
avatar: '/static/cat-avatar.jpg'
},
{
id: '2',
name: '小白',
breed: '金毛',
personality: '温顺友好,聪明伶俐',
avatar: '/static/dog-avatar.jpg'
}
]
this.petInfo = mockPets.find(pet => pet.id === this.petId) || mockPets[0]
},
// 加载聊天历史
loadChatHistory() {
const mockMessages = [
{
id: '1',
type: 'ai',
content: `你好主人!我是${this.petInfo.name},今天感觉特别开心呢!有什么想和我聊的吗?`,
timestamp: Date.now() - 3600000
},
{
id: '2',
type: 'user',
content: '小橘今天吃饭怎么样?',
timestamp: Date.now() - 3500000
},
{
id: '3',
type: 'ai',
content: '今天的小鱼干特别香!我吃得可开心了,还想要更多呢~主人你也要记得按时吃饭哦!',
timestamp: Date.now() - 3400000
}
]
this.messageList = mockMessages
this.scrollToBottom()
},
// 发送消息
sendMessage() {
if (!this.inputMessage.trim()) return
const userMessage = {
id: Date.now().toString(),
type: 'user',
content: this.inputMessage.trim(),
timestamp: Date.now()
}
this.messageList.push(userMessage)
const messageContent = this.inputMessage.trim()
this.inputMessage = ''
this.scrollToBottom()
this.simulateAIReply(messageContent)
},
// 模拟AI回复
simulateAIReply(userMessage) {
this.aiTyping = true
this.scrollToBottom()
setTimeout(() => {
const aiResponse = this.generateAIResponse(userMessage)
const aiMessage = {
id: (Date.now() + 1).toString(),
type: 'ai',
content: aiResponse,
timestamp: Date.now()
}
this.messageList.push(aiMessage)
this.aiTyping = false
this.scrollToBottom()
}, 1500)
},
// 生成AI回复内容
generateAIResponse(userMessage) {
const petName = this.petInfo.name
const responses = [
`${petName}最喜欢和主人聊天了!今天过得怎么样呀?`,
`主人好!${petName}今天心情特别好呢~`,
`哇!主人来找我聊天了,${petName}好开心!`,
`${petName}听不太懂,但是很开心能和主人聊天!`,
`主人说的话${petName}都会认真听的!`
]
return responses[Math.floor(Math.random() * responses.length)]
},
// 滚动到底部
scrollToBottom() {
this.$nextTick(() => {
this.scrollTop = 99999
})
},
// 格式化时间
formatTime(timestamp) {
const date = new Date(timestamp)
const now = new Date()
const diff = now - date
if (diff < 60000) return '刚刚'
if (diff < 3600000) return `${Math.floor(diff / 60000)}分钟前`
if (diff < 86400000) return `${Math.floor(diff / 3600000)}小时前`
return `${date.getMonth() + 1}${date.getDate()}`
},
// 清空聊天记录
clearHistory() {
uni.showModal({
title: '确认清空',
content: '确定要清空所有聊天记录吗?',
success: (res) => {
if (res.confirm) {
this.messageList = []
this.showMenu = false
uni.showToast({
title: '已清空聊天记录',
icon: 'success'
})
}
}
})
},
// 返回上一页
goBack() {
uni.navigateBack()
}
}
}
</script>
<style lang="scss" scoped>
.pet-chat-container {
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f8f9fa;
}
.pet-info-card {
display: flex;
align-items: center;
padding: 15px 20px;
background-color: #ffffff;
border-bottom: 1px solid #f0f0f0;
.pet-info-text {
flex: 1;
margin-left: 10px;
}
.chat-status {
margin-left: 10px;
}
}
.chat-messages {
flex: 1;
padding: 0 15px;
}
.message-list {
padding: 20px 0;
}
.message-item {
display: flex;
margin-bottom: 20px;
&.user {
flex-direction: row-reverse;
.message-content {
align-items: flex-end;
}
.message-bubble {
background-color: #FF8A80;
color: #ffffff;
}
}
&.ai {
.message-bubble {
background-color: #ffffff;
border: 1px solid #f0f0f0;
}
}
}
.message-avatar {
margin: 0 10px;
}
.message-content {
display: flex;
flex-direction: column;
max-width: 70%;
}
.message-bubble {
padding: 12px 16px;
border-radius: 18px;
word-wrap: break-word;
&.typing {
padding: 16px;
}
}
.message-time {
margin-top: 5px;
text-align: center;
}
.typing-dots {
display: flex;
gap: 4px;
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #ccc;
animation: typing 1.4s infinite ease-in-out;
&:nth-child(1) { animation-delay: -0.32s; }
&:nth-child(2) { animation-delay: -0.16s; }
}
}
@keyframes typing {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
.chat-input-area {
background-color: #ffffff;
border-top: 1px solid #f0f0f0;
padding: 15px 20px;
padding-bottom: calc(15px + env(safe-area-inset-bottom));
}
.input-container {
display: flex;
align-items: center;
gap: 10px;
}
.input-actions {
flex-shrink: 0;
}
.chat-menu {
padding: 20px;
.menu-title {
text-align: center;
padding-bottom: 20px;
border-bottom: 1px solid #f0f0f0;
}
.menu-items {
padding: 20px 0;
}
.menu-item {
display: flex;
align-items: center;
gap: 15px;
padding: 15px 0;
}
.menu-cancel {
text-align: center;
padding: 15px 0;
border-top: 1px solid #f0f0f0;
}
}
</style>