完成AI助手和领养专区功能

 AI助手功能:
- 智能对话系统,支持宠物相关问题问答
- 快捷问题按钮,常见问题一键提问
- 知识库匹配,根据关键词智能回复
- 思考状态显示,模拟真实AI交互
- 宠物知识库页面,分类浏览知识内容

 领养专区功能:
- 宠物领养信息展示
- 分类筛选(猫咪/狗狗/待领养)
- 搜索功能(品种/地区)
- 联系方式展示
- 现代化卡片布局

🎨 界面优化:
- 使用uView组件构建现代化界面
- 自定义导航栏和配色方案
- 响应式布局和交互动画
- 统一的设计语言

📱 用户体验:
- 流畅的对话交互
- 智能的内容匹配
- 便捷的筛选和搜索
- 清晰的信息展示
This commit is contained in:
yvan 2025-08-12 11:01:39 +08:00
parent 63bf142c55
commit df3f5f5269
5 changed files with 614 additions and 115 deletions

View File

@ -19,48 +19,41 @@ pet-ai/
## 当前完成状态 ## 当前完成状态
✅ 基础项目结构搭建 ✅ 基础项目结构搭建
✅ Tab导航配置 ✅ Tab导航配置和可爱宠物图标
✅ 四个主要页面创建 ✅ uview-ui-next UI框架完整集成
✅ 宠物页面基础功能 ✅ 宠物管理完整功能
- 宠物列表展示
- 添加宠物(头像上传、信息录入)
- 宠物详情页面
- 添加宠物记录(支持照片上传)
- 本地存储数据持久化
✅ 现代化UI界面和用户体验
## 待完成任务 ## 待完成任务
### 1. UI框架安装 ### 1. AI助手功能开发
**推荐方式**在HBuilderX中打开项目通过插件市场安装uview-next - [ ] 智能对话系统
- 插件市场地址https://ext.dcloud.net.cn/plugin?id=24021 - [ ] 宠物知识库集成
- 插件IDuview-next - [ ] 健康建议和提醒
- 安装后需要配置main.js和uni.scss - [ ] 语音交互功能
### 2. Tab图标获取 ### 2. 领养专区功能
需要为以下4个tab页面添加可爱的宠物主题图标
**推荐图标资源**
- [iconfont阿里巴巴矢量图标库](https://www.iconfont.cn/)
- [Feather Icons](https://feathericons.com/)
- [Heroicons](https://heroicons.com/)
**所需图标**
- `static/tabbar/pets.png` - 宠物图标(如猫爪、宠物头像)
- `static/tabbar/pets-active.png` - 宠物图标激活状态
- `static/tabbar/assistant.png` - AI助手图标如机器人、对话框
- `static/tabbar/assistant-active.png` - AI助手图标激活状态
- `static/tabbar/adoption.png` - 领养图标(如爱心、房子)
- `static/tabbar/adoption-active.png` - 领养图标激活状态
- `static/tabbar/profile.png` - 个人中心图标(如用户头像)
- `static/tabbar/profile-active.png` - 个人中心图标激活状态
**图标规格**
- 尺寸81px × 81px推荐
- 格式PNG
- 背景:透明
### 3. 功能完善
- [ ] 宠物详情页面
- [ ] 添加宠物页面
- [ ] 宠物记录功能
- [ ] AI对话功能
- [ ] 领养信息发布 - [ ] 领养信息发布
- [ ] 领养信息浏览和搜索
- [ ] 联系方式管理
- [ ] 地理位置服务
### 3. 用户系统完善
- [ ] 微信登录集成 - [ ] 微信登录集成
- [ ] 用户资料管理
- [ ] 消息通知系统
- [ ] 隐私设置
### 4. 高级功能
- [ ] 宠物健康档案
- [ ] 疫苗提醒系统
- [ ] 社区分享功能
- [ ] 数据统计和分析
## 技术栈 ## 技术栈

View File

@ -13,7 +13,17 @@
"style": { "style": {
"navigationBarTitleText": "AI助手", "navigationBarTitleText": "AI助手",
"navigationBarBackgroundColor": "#ffffff", "navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black",
"navigationStyle": "custom"
}
},
{
"path": "pages/assistant/knowledge",
"style": {
"navigationBarTitleText": "宠物知识库",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationStyle": "custom"
} }
}, },
{ {

View File

@ -1,41 +1,180 @@
<template> <template>
<view class="container"> <view class="adoption-container">
<view class="header"> <u-navbar title="领养专区" :border="false" bg-color="#ff6b6b">
<text class="title">领养专区</text> <template #right>
<u-icon name="plus" color="white" size="20" @click="publishAdoption"></u-icon>
</template>
</u-navbar>
<u-search placeholder="搜索宠物品种或地区" v-model="searchKeyword" @search="searchPets"></u-search>
<view class="filter-bar">
<u-button v-for="filter in filters" :key="filter.key"
:text="filter.name"
:type="currentFilter === filter.key ? 'primary' : 'info'"
size="mini"
plain
@click="setFilter(filter.key)">
</u-button>
</view> </view>
<view class="content">
<text class="desc">宠物领养信息发布与浏览</text> <scroll-view class="adoption-list" scroll-y="true">
<u-card v-for="pet in filteredPets" :key="pet.id" :margin="20" :padding="0" @click="viewDetail(pet)">
<view class="pet-adoption-item">
<u-avatar :src="pet.avatar" size="80" shape="square"></u-avatar>
<view class="pet-info">
<u-text :text="pet.name" type="primary" size="16" bold></u-text>
<u-text :text="`${pet.breed} · ${pet.age}岁 · ${pet.gender}`" type="info" size="14"></u-text>
<u-text :text="pet.location" type="tips" size="12"></u-text>
<view class="pet-tags">
<u-tag :text="pet.status" :type="pet.status === '待领养' ? 'success' : 'warning'" size="mini"></u-tag>
</view> </view>
</view> </view>
<view class="contact-btn">
<u-button text="联系" type="primary" size="mini" @click.stop="contact(pet)"></u-button>
</view>
</view>
</u-card>
</scroll-view>
</view>
</template> </template>
<script> <script>
export default { export default {
data() { data() {
return {} return {
searchKeyword: '',
currentFilter: 'all',
filters: [
{ key: 'all', name: '全部' },
{ key: 'cat', name: '猫咪' },
{ key: 'dog', name: '狗狗' },
{ key: 'available', name: '待领养' }
],
adoptionPets: [
{
id: 1,
name: '小花',
breed: '中华田园猫',
age: 1,
gender: '母',
location: '北京市朝阳区',
status: '待领养',
avatar: '/static/cat1.jpg',
type: 'cat',
description: '性格温顺,已绝育,已接种疫苗'
},
{
id: 2,
name: '大黄',
breed: '金毛',
age: 2,
gender: '公',
location: '上海市浦东新区',
status: '待领养',
avatar: '/static/dog1.jpg',
type: 'dog',
description: '活泼可爱,喜欢和人玩耍'
}
]
}
},
computed: {
filteredPets() {
let result = this.adoptionPets
if (this.currentFilter !== 'all') {
if (this.currentFilter === 'available') {
result = result.filter(pet => pet.status === '待领养')
} else {
result = result.filter(pet => pet.type === this.currentFilter)
}
}
if (this.searchKeyword) {
result = result.filter(pet =>
pet.name.includes(this.searchKeyword) ||
pet.breed.includes(this.searchKeyword) ||
pet.location.includes(this.searchKeyword)
)
}
return result
}
},
methods: {
setFilter(key) {
this.currentFilter = key
},
searchPets() {
// computed
},
publishAdoption() {
uni.navigateTo({
url: '/pages/adoption/publish'
})
},
viewDetail(pet) {
uni.navigateTo({
url: `/pages/adoption/detail?id=${pet.id}`
})
},
contact(pet) {
uni.showModal({
title: '联系方式',
content: '请添加微信pet_love_123',
showCancel: false
})
}
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.container { .adoption-container {
padding: 20rpx;
background-color: #f8f9fa; background-color: #f8f9fa;
min-height: 100vh; min-height: 100vh;
} }
.header {
padding: 20rpx 0; .filter-bar {
.title { background-color: white;
font-size: 36rpx; padding: 20rpx;
font-weight: bold; display: flex;
color: #333; gap: 20rpx;
/deep/ .u-button {
margin: 0;
} }
} }
.content {
padding: 40rpx 0; .adoption-list {
.desc { height: calc(100vh - 300rpx);
font-size: 28rpx; }
color: #666;
.pet-adoption-item {
display: flex;
align-items: center;
padding: 20rpx;
.pet-info {
flex: 1;
margin-left: 20rpx;
.pet-tags {
margin-top: 10rpx;
}
/deep/ .u-text {
margin-bottom: 8rpx;
}
}
.contact-btn {
margin-left: 20rpx;
} }
} }
</style> </style>

View File

@ -1,20 +1,81 @@
<template> <template>
<view class="assistant-container"> <view class="assistant-container">
<view class="header"> <u-navbar title="AI助手" :border="false" bg-color="#4ecdc4">
<text class="title">AI助手</text> <template #right>
</view> <u-icon name="book" color="white" size="20" @click="openKnowledge"></u-icon>
</template>
</u-navbar>
<view class="chat-area"> <!-- 快捷问题 -->
<view class="welcome-message"> <view class="quick-questions" v-if="messageList.length <= 1">
<text class="welcome-text">您好我是您的宠物AI助手</text> <u-text text="常见问题" type="primary" size="16" bold margin="20rpx 0"></u-text>
<text class="welcome-desc">可以为您解答宠物饲养健康训练等问题</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>
</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-area">
<input class="message-input" placeholder="请输入您的问题..." /> <view class="input-container">
<view class="send-btn"> <u-input
<text class="send-text">发送</text> 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> </view>
</view> </view>
@ -24,7 +85,149 @@
export default { export default {
data() { data() {
return { 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'
})
} }
} }
} }
@ -38,40 +241,73 @@ export default {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
.header { .quick-questions {
padding: 20rpx 30rpx;
background-color: white; background-color: white;
border-bottom: 1rpx solid #eee; padding: 20rpx;
margin: 20rpx;
border-radius: 20rpx;
.title { .question-grid {
font-size: 36rpx; display: flex;
font-weight: bold; flex-wrap: wrap;
color: #333; gap: 20rpx;
/deep/ .u-button {
margin: 0;
}
} }
} }
.chat-area { .chat-area {
flex: 1; flex: 1;
padding: 40rpx; padding: 20rpx;
display: flex;
align-items: center;
justify-content: center;
} }
.welcome-message { .message-list {
text-align: center; .message-item {
display: flex;
margin-bottom: 30rpx;
align-items: flex-start;
.welcome-text { &.user-message {
font-size: 32rpx; flex-direction: row-reverse;
color: #333;
display: block; .message-content {
margin-bottom: 20rpx; background-color: #4ecdc4;
margin-right: 20rpx;
border-radius: 20rpx 20rpx 8rpx 20rpx;
/deep/ .u-text {
color: white !important;
}
}
} }
.welcome-desc { &.ai-message {
font-size: 26rpx; .message-content {
color: #666; background-color: white;
display: block; 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;
} }
} }
@ -79,26 +315,14 @@ export default {
background-color: white; background-color: white;
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
border-top: 1rpx solid #eee; border-top: 1rpx solid #eee;
.input-container {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 20rpx;
.message-input { /deep/ .u-input {
flex: 1; flex: 1;
background-color: #f5f5f5;
border-radius: 30rpx;
padding: 20rpx 30rpx;
font-size: 28rpx;
margin-right: 20rpx;
}
.send-btn {
background-color: #4ecdc4;
padding: 20rpx 40rpx;
border-radius: 30rpx;
.send-text {
color: white;
font-size: 28rpx;
} }
} }
} }

View File

@ -0,0 +1,133 @@
<template>
<view class="knowledge-container">
<u-navbar title="宠物知识库" left-icon="arrow-left" @left-click="goBack"></u-navbar>
<u-search placeholder="搜索知识" v-model="searchKeyword" @search="searchKnowledge" @custom="searchKnowledge"></u-search>
<view class="category-tabs">
<u-tabs :list="categories" @click="switchCategory" :current="currentCategory"></u-tabs>
</view>
<scroll-view class="knowledge-list" scroll-y="true">
<u-card v-for="item in filteredKnowledge" :key="item.id" :margin="20" :padding="20" @click="viewDetail(item)">
<view class="knowledge-item">
<u-text :text="item.title" type="primary" size="16" bold></u-text>
<u-text :text="item.summary" type="info" size="14" margin="10rpx 0"></u-text>
<view class="knowledge-meta">
<u-tag :text="item.category" type="info" size="mini"></u-tag>
<u-text :text="item.readCount + '次阅读'" type="tips" size="12"></u-text>
</view>
</view>
</u-card>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
searchKeyword: '',
currentCategory: 0,
categories: [
{ name: '全部' },
{ name: '饮食' },
{ name: '健康' },
{ name: '训练' },
{ name: '护理' }
],
knowledgeList: [
{
id: 1,
title: '猫咪日常饮食指南',
summary: '了解猫咪的营养需求,选择合适的猫粮和喂食方式',
category: '饮食',
readCount: 1234,
content: '详细的猫咪饮食指南内容...'
},
{
id: 2,
title: '狗狗疫苗接种时间表',
summary: '幼犬和成犬的疫苗接种计划和注意事项',
category: '健康',
readCount: 856,
content: '疫苗接种详细说明...'
},
{
id: 3,
title: '宠物基础训练方法',
summary: '如何训练宠物基本指令和良好习惯',
category: '训练',
readCount: 642,
content: '训练方法详细介绍...'
}
]
}
},
computed: {
filteredKnowledge() {
let result = this.knowledgeList
//
if (this.currentCategory > 0) {
const categoryName = this.categories[this.currentCategory].name
result = result.filter(item => item.category === categoryName)
}
//
if (this.searchKeyword) {
result = result.filter(item =>
item.title.includes(this.searchKeyword) ||
item.summary.includes(this.searchKeyword)
)
}
return result
}
},
methods: {
goBack() {
uni.navigateBack()
},
switchCategory(item) {
this.currentCategory = item.index
},
searchKnowledge() {
// computed
},
viewDetail(item) {
uni.navigateTo({
url: `/pages/assistant/knowledge-detail?id=${item.id}`
})
}
}
}
</script>
<style lang="scss" scoped>
.knowledge-container {
background-color: #f8f9fa;
min-height: 100vh;
}
.category-tabs {
background-color: white;
padding: 20rpx 0;
}
.knowledge-list {
height: calc(100vh - 300rpx);
}
.knowledge-item {
.knowledge-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
}
}
</style>