Files
pet/pages/assistant/assistant.vue
T
2025-08-12 15:04:01 +08:00

330 lines
8.2 KiB
Vue

<template>
<view class="assistant-container">
<u-navbar title="AI助手" :border="false" bg-color="#4ecdc4">
<template #right>
<u-icon name="book" color="white" size="20" @click="openKnowledge"></u-icon>
</template>
</u-navbar>
<!-- 快捷问题 -->
<view class="quick-questions" v-if="messageList.length <= 1">
<u-text text="常见问题" type="primary" size="16" bold margin="20rpx 0"></u-text>
<view class="question-grid">
<u-button
v-for="question in quickQuestions"
:key="question.id"
:text="question.text"
type="info"
size="mini"
plain
@click="askQuestion(question.text)"
></u-button>
</view>
</view>
<!-- 聊天区域 -->
<scroll-view class="chat-area" scroll-y="true" :scroll-top="scrollTop" scroll-with-animation="true">
<view class="message-list">
<view class="message-item" v-for="(message, index) in messageList" :key="index"
:class="message.type === 'user' ? 'user-message' : 'ai-message'">
<u-avatar
v-if="message.type === 'user'"
:src="userAvatar"
size="35"
shape="circle"
></u-avatar>
<u-avatar
v-else
src="/static/ai-avatar.png"
size="35"
shape="circle"
></u-avatar>
<view class="message-content">
<u-text :text="message.content" size="14" :type="message.type === 'user' ? 'primary' : 'info'"></u-text>
<u-text :text="message.time" type="tips" size="10" margin="8rpx 0 0 0"></u-text>
</view>
</view>
<!-- AI思考中状态 -->
<view class="message-item ai-message" v-if="isThinking">
<u-avatar src="/static/ai-avatar.png" size="35" shape="circle"></u-avatar>
<view class="message-content thinking">
<u-loading-icon mode="flower" color="#4ecdc4"></u-loading-icon>
<u-text text="AI正在思考中..." type="tips" size="12" margin="0 0 0 20rpx"></u-text>
</view>
</view>
</view>
</scroll-view>
<!-- 输入区域 -->
<view class="input-area">
<view class="input-container">
<u-input
v-model="inputMessage"
placeholder="请输入您的问题..."
:border="false"
bg-color="#f5f5f5"
shape="circle"
@confirm="sendMessage"
confirm-type="send"
></u-input>
<u-button
:text="inputMessage.trim() ? '发送' : '🎤'"
type="primary"
size="mini"
shape="circle"
:disabled="isThinking"
@click="inputMessage.trim() ? sendMessage() : startVoiceInput()"
></u-button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
inputMessage: '',
scrollTop: 0,
isThinking: false,
userAvatar: '/static/user-avatar.png',
messageList: [
{
type: 'ai',
content: '您好!我是您的宠物AI助手🐾\n\n我可以为您解答关于宠物饲养、健康、训练、营养等方面的问题。有什么可以帮助您的吗?',
time: this.getCurrentTime()
}
],
quickQuestions: [
{ id: 1, text: '猫咪不吃饭怎么办?' },
{ id: 2, text: '狗狗需要多久洗一次澡?' },
{ id: 3, text: '宠物疫苗接种时间' },
{ id: 4, text: '如何训练宠物定点上厕所?' },
{ id: 5, text: '宠物发烧的症状' },
{ id: 6, text: '幼猫喂养注意事项' }
],
// 知识库数据
knowledgeBase: {
'喂食': {
keywords: ['喂食', '吃饭', '食物', '饮食', '营养'],
responses: [
'定时定量喂食很重要,成年猫每天2-3次,幼猫3-4次。',
'选择优质的宠物食品,避免给宠物吃人类食物。',
'确保宠物随时有清洁的饮用水。'
]
},
'健康': {
keywords: ['健康', '生病', '症状', '发烧', '呕吐', '腹泻'],
responses: [
'定期体检很重要,建议每年至少一次。',
'注意观察宠物的食欲、精神状态和排便情况。',
'如果出现异常症状,请及时就医。'
]
},
'疫苗': {
keywords: ['疫苗', '接种', '免疫', '预防'],
responses: [
'幼猫幼犬8-12周开始接种疫苗。',
'成年宠物每年需要加强免疫。',
'常见疫苗包括三联疫苗和狂犬疫苗。'
]
},
'训练': {
keywords: ['训练', '教育', '行为', '定点', '上厕所'],
responses: [
'训练需要耐心和一致性。',
'使用正向强化,奖励好行为。',
'避免体罚,这会让宠物产生恐惧。'
]
},
'洗澡': {
keywords: ['洗澡', '清洁', '卫生'],
responses: [
'狗狗一般1-2周洗一次澡,猫咪通常不需要经常洗澡。',
'使用宠物专用洗浴用品。',
'洗澡后要及时吹干,避免感冒。'
]
}
}
}
},
methods: {
getCurrentTime() {
const now = new Date()
return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`
},
askQuestion(question) {
this.inputMessage = question
this.sendMessage()
},
sendMessage() {
if (!this.inputMessage.trim() || this.isThinking) return
// 添加用户消息
const userMessage = {
type: 'user',
content: this.inputMessage,
time: this.getCurrentTime()
}
this.messageList.push(userMessage)
// 开始AI思考
this.isThinking = true
this.scrollToBottom()
// 模拟AI回复
setTimeout(() => {
const aiMessage = {
type: 'ai',
content: this.getAIResponse(this.inputMessage),
time: this.getCurrentTime()
}
this.messageList.push(aiMessage)
this.isThinking = false
this.scrollToBottom()
}, 1500 + Math.random() * 1000) // 1.5-2.5秒随机延迟
this.inputMessage = ''
},
getAIResponse(question) {
// 智能匹配知识库
for (let category in this.knowledgeBase) {
const { keywords, responses } = this.knowledgeBase[category]
if (keywords.some(keyword => question.includes(keyword))) {
const randomResponse = responses[Math.floor(Math.random() * responses.length)]
return `${randomResponse}\n\n💡 如果您需要更详细的建议,建议咨询专业的宠物医生。`
}
}
// 默认回复
const defaultResponses = [
'这是一个很好的问题!建议您咨询专业的宠物医生获得更准确的建议。',
'每只宠物的情况都不同,建议根据具体情况来处理。您可以查看我们的知识库了解更多信息。',
'感谢您的提问!这个问题比较复杂,建议您带宠物去专业的宠物医院检查。',
'我理解您的担心。建议您记录宠物的具体症状,然后咨询专业医生。'
]
return defaultResponses[Math.floor(Math.random() * defaultResponses.length)]
},
scrollToBottom() {
this.$nextTick(() => {
this.scrollTop = 999999
})
},
startVoiceInput() {
uni.showToast({
title: '语音功能开发中',
icon: 'none'
})
},
openKnowledge() {
uni.navigateTo({
url: '/pages/assistant/knowledge'
})
}
}
}
</script>
<style lang="scss" scoped>
.assistant-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f8f9fa;
}
.quick-questions {
background-color: white;
padding: 20rpx;
margin: 20rpx;
border-radius: 20rpx;
.question-grid {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
:deep(.u-button) {
margin: 0;
}
}
}
.chat-area {
flex: 1;
padding: 20rpx;
}
.message-list {
.message-item {
display: flex;
margin-bottom: 30rpx;
align-items: flex-start;
&.user-message {
flex-direction: row-reverse;
.message-content {
background-color: #4ecdc4;
margin-right: 20rpx;
border-radius: 20rpx 20rpx 8rpx 20rpx;
:deep(.u-text) {
color: white !important;
}
}
}
&.ai-message {
.message-content {
background-color: white;
margin-left: 20rpx;
border-radius: 20rpx 20rpx 20rpx 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
&.thinking {
display: flex;
align-items: center;
padding: 20rpx;
}
}
}
}
}
.message-content {
max-width: 70%;
padding: 20rpx;
:deep(.u-text) {
line-height: 1.5;
word-break: break-word;
white-space: pre-wrap;
}
}
.input-area {
background-color: white;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
.input-container {
display: flex;
align-items: center;
gap: 20rpx;
:deep(.u-input) {
flex: 1;
}
}
}
</style>