330 lines
8.2 KiB
Vue
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>
|