Compare commits
4 Commits
66aa21085e
...
1dde942ff1
| Author | SHA1 | Date |
|---|---|---|
|
|
1dde942ff1 | |
|
|
6678bce527 | |
|
|
befb1dd8f9 | |
|
|
7bc3d491b8 |
|
|
@ -1,249 +0,0 @@
|
||||||
// HTTP API使用示例
|
|
||||||
// 注意:鉴权配置已简化,只需要在 http/config/config.js 中配置不需要鉴权的接口即可
|
|
||||||
|
|
||||||
// 导入API模块
|
|
||||||
import { petsApi, assistantApi, adoptionApi, profileApi, commonApi, addNoAuthApis, setEnvironment } from '@/http/index.js'
|
|
||||||
|
|
||||||
// 或者导入所有API
|
|
||||||
// import api from '@/http/index.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
petsList: [],
|
|
||||||
userInfo: {},
|
|
||||||
loading: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// 示例1:获取宠物列表(自动鉴权)
|
|
||||||
async loadPets() {
|
|
||||||
try {
|
|
||||||
// 使用默认配置,自动根据接口判断是否需要鉴权
|
|
||||||
const pets = await petsApi.getPetsList()
|
|
||||||
this.petsList = pets
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取宠物列表失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例2:添加不需要鉴权的接口
|
|
||||||
addCustomNoAuthApis() {
|
|
||||||
// 如果有自定义的接口不需要鉴权,可以这样添加
|
|
||||||
addNoAuthApis([
|
|
||||||
'/custom/public-api',
|
|
||||||
'/special/no-auth-endpoint'
|
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例3:切换环境
|
|
||||||
switchEnvironment() {
|
|
||||||
// 根据需要切换环境
|
|
||||||
// #ifdef H5
|
|
||||||
setEnvironment('development') // H5开发环境
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
setEnvironment('production') // 小程序生产环境
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// 或者根据条件动态切换
|
|
||||||
const isDev = process.env.NODE_ENV === 'development'
|
|
||||||
setEnvironment(isDev ? 'development' : 'production')
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例2:添加宠物
|
|
||||||
async addNewPet() {
|
|
||||||
try {
|
|
||||||
const petData = {
|
|
||||||
name: '小白',
|
|
||||||
breed: '金毛',
|
|
||||||
age: 2,
|
|
||||||
gender: '公'
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await petsApi.addPet(petData, {
|
|
||||||
custom: {
|
|
||||||
auth: true,
|
|
||||||
loading: true,
|
|
||||||
toast: true // 显示成功/失败提示
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '添加成功',
|
|
||||||
icon: 'success'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 重新加载列表
|
|
||||||
this.loadPets()
|
|
||||||
} catch (error) {
|
|
||||||
// 错误已在拦截器中处理,这里可以做额外处理
|
|
||||||
console.error('添加宠物失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例3:AI助手对话
|
|
||||||
async sendMessageToAI() {
|
|
||||||
try {
|
|
||||||
const messageData = {
|
|
||||||
message: '我的猫咪最近不爱吃饭,怎么办?',
|
|
||||||
petId: 123
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await assistantApi.sendMessage(messageData, {
|
|
||||||
custom: {
|
|
||||||
auth: true,
|
|
||||||
loading: true,
|
|
||||||
loadingText: 'AI正在思考中...'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('AI回复:', response.reply)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('AI对话失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例4:用户登录
|
|
||||||
async userLogin() {
|
|
||||||
try {
|
|
||||||
const loginData = {
|
|
||||||
username: 'user@example.com',
|
|
||||||
password: '123456'
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await profileApi.userLogin(loginData, {
|
|
||||||
custom: {
|
|
||||||
auth: false, // 登录接口不需要token
|
|
||||||
loading: true,
|
|
||||||
loadingText: '正在登录...'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 保存token和用户信息
|
|
||||||
uni.setStorageSync('token', result.token)
|
|
||||||
uni.setStorageSync('userInfo', result.userInfo)
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '登录成功',
|
|
||||||
icon: 'success'
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error('登录失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例5:上传图片
|
|
||||||
async uploadPetImage() {
|
|
||||||
try {
|
|
||||||
// 选择图片
|
|
||||||
const chooseResult = await uni.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
sizeType: ['compressed'],
|
|
||||||
sourceType: ['album', 'camera']
|
|
||||||
})
|
|
||||||
|
|
||||||
const imageData = {
|
|
||||||
filePath: chooseResult.tempFilePaths[0],
|
|
||||||
name: 'petImage',
|
|
||||||
formData: {
|
|
||||||
type: 'pet',
|
|
||||||
petId: 123
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadResult = await commonApi.uploadImage(imageData, {
|
|
||||||
custom: {
|
|
||||||
auth: true,
|
|
||||||
loading: true,
|
|
||||||
loadingText: '正在上传图片...'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('上传成功:', uploadResult.url)
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '上传成功',
|
|
||||||
icon: 'success'
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error('上传失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例6:搜索领养宠物
|
|
||||||
async searchAdoptionPets() {
|
|
||||||
try {
|
|
||||||
const searchParams = {
|
|
||||||
keyword: '金毛',
|
|
||||||
type: 'dog',
|
|
||||||
age: '1-3',
|
|
||||||
location: '北京'
|
|
||||||
}
|
|
||||||
|
|
||||||
const pets = await adoptionApi.searchPets(searchParams, {
|
|
||||||
custom: {
|
|
||||||
auth: false, // 搜索不需要登录
|
|
||||||
loading: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('搜索结果:', pets)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('搜索失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例7:批量操作
|
|
||||||
async batchOperations() {
|
|
||||||
try {
|
|
||||||
// 并发执行多个请求
|
|
||||||
const [pets, userInfo, adoptionPets] = await Promise.all([
|
|
||||||
petsApi.getPetsList(),
|
|
||||||
profileApi.getUserInfo(),
|
|
||||||
adoptionApi.getAdoptionPets()
|
|
||||||
])
|
|
||||||
|
|
||||||
console.log('批量获取数据成功:', { pets, userInfo, adoptionPets })
|
|
||||||
} catch (error) {
|
|
||||||
console.error('批量操作失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 示例8:自定义错误处理
|
|
||||||
async customErrorHandling() {
|
|
||||||
try {
|
|
||||||
const result = await petsApi.getPetsList({}, {
|
|
||||||
custom: {
|
|
||||||
auth: true,
|
|
||||||
loading: true,
|
|
||||||
toast: false, // 不显示默认错误提示
|
|
||||||
catch: true // 允许catch捕获错误
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
// 自定义错误处理
|
|
||||||
if (error.code === 401) {
|
|
||||||
uni.showModal({
|
|
||||||
title: '提示',
|
|
||||||
content: '登录已过期,请重新登录',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
uni.reLaunch({
|
|
||||||
url: '/pages/login/login'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: error.message || '操作失败',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="adoption-container page-container-with-bg">
|
<view class="adoption-container page-container-unified">
|
||||||
<!-- 头部搜索栏 -->
|
<!-- 头部搜索栏 -->
|
||||||
<view class="header-section">
|
<view class="header-section">
|
||||||
<view class="search-wrapper">
|
<view class="search-wrapper">
|
||||||
|
|
@ -1192,7 +1192,7 @@ export default {
|
||||||
/* 头部搜索栏 */
|
/* 头部搜索栏 */
|
||||||
.header-section {
|
.header-section {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 24rpx 20rpx;
|
padding: 0 0 24rpx 0;
|
||||||
|
|
||||||
.search-wrapper {
|
.search-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1287,7 +1287,7 @@ export default {
|
||||||
.filter-panel {
|
.filter-panel {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
padding: 24rpx 20rpx;
|
padding: 24rpx 0;
|
||||||
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
|
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
@ -1514,7 +1514,7 @@ export default {
|
||||||
/* 宠物列表区域 */
|
/* 宠物列表区域 */
|
||||||
.pets-scroll {
|
.pets-scroll {
|
||||||
height: calc(100vh - 100rpx);
|
height: calc(100vh - 100rpx);
|
||||||
padding: 0 20rpx;
|
padding: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="assistant-container page-container-with-bg">
|
<view class="assistant-container page-container-unified">
|
||||||
<!-- 宠物助手信息卡片 -->
|
<!-- 宠物助手信息卡片 -->
|
||||||
<view class="assistant-info-card">
|
<view class="assistant-info-card">
|
||||||
<view class="assistant-avatar-wrapper">
|
<view class="assistant-avatar-wrapper">
|
||||||
|
|
@ -191,31 +191,54 @@ export default {
|
||||||
sendMessage() {
|
sendMessage() {
|
||||||
if (!this.inputMessage.trim() || this.isThinking) return
|
if (!this.inputMessage.trim() || this.isThinking) return
|
||||||
|
|
||||||
|
// 保存用户输入内容
|
||||||
|
const userInput = this.inputMessage
|
||||||
|
|
||||||
// 添加用户消息
|
// 添加用户消息
|
||||||
const userMessage = {
|
const userMessage = {
|
||||||
type: 'user',
|
type: 'user',
|
||||||
content: this.inputMessage,
|
content: userInput,
|
||||||
time: this.getCurrentTime()
|
time: this.getCurrentTime()
|
||||||
}
|
}
|
||||||
this.messageList.push(userMessage)
|
this.messageList.push(userMessage)
|
||||||
|
|
||||||
|
// 清空输入框
|
||||||
|
this.inputMessage = ''
|
||||||
|
|
||||||
// 开始AI思考
|
// 开始AI思考
|
||||||
this.isThinking = true
|
this.isThinking = true
|
||||||
this.scrollToBottom()
|
|
||||||
|
// 确保DOM更新后再滚动到思考状态
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.forceScrollToBottom()
|
||||||
|
// 在思考过程中持续滚动,确保思考动画可见
|
||||||
|
setTimeout(() => {
|
||||||
|
this.forceScrollToBottom()
|
||||||
|
}, 200)
|
||||||
|
setTimeout(() => {
|
||||||
|
this.forceScrollToBottom()
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
|
||||||
// 模拟AI回复
|
// 模拟AI回复
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const aiMessage = {
|
const aiMessage = {
|
||||||
type: 'ai',
|
type: 'ai',
|
||||||
content: this.getAIResponse(this.inputMessage),
|
content: this.getAIResponse(userInput),
|
||||||
time: this.getCurrentTime()
|
time: this.getCurrentTime()
|
||||||
}
|
}
|
||||||
this.messageList.push(aiMessage)
|
this.messageList.push(aiMessage)
|
||||||
this.isThinking = false
|
this.isThinking = false
|
||||||
this.scrollToBottom()
|
|
||||||
}, 1500 + Math.random() * 1000) // 1.5-2.5秒随机延迟
|
|
||||||
|
|
||||||
this.inputMessage = ''
|
// AI回复完成后强制滚动到底部
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.forceScrollToBottom()
|
||||||
|
// 延迟再次滚动,确保消息完全显示
|
||||||
|
setTimeout(() => {
|
||||||
|
this.forceScrollToBottom()
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
}, 1500 + Math.random() * 1000) // 1.5-2.5秒随机延迟
|
||||||
},
|
},
|
||||||
|
|
||||||
getAIResponse(question) {
|
getAIResponse(question) {
|
||||||
|
|
@ -245,6 +268,17 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 强制滚动到底部,用于确保消息可见
|
||||||
|
forceScrollToBottom() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 使用时间戳确保每次滚动都能触发
|
||||||
|
this.scrollTop = Date.now()
|
||||||
|
setTimeout(() => {
|
||||||
|
this.scrollTop = 999999
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
startVoiceInput() {
|
startVoiceInput() {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '语音功能开发中',
|
title: '语音功能开发中',
|
||||||
|
|
@ -273,7 +307,7 @@ export default {
|
||||||
.assistant-info-card {
|
.assistant-info-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 20rpx 30rpx 0 30rpx;
|
margin: 0 0 0 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -359,7 +393,7 @@ export default {
|
||||||
.quick-questions {
|
.quick-questions {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 20rpx 30rpx;
|
margin: 20rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
|
|
@ -400,7 +434,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-messages {
|
.chat-messages {
|
||||||
margin-top: 20rpx;
|
margin-top: 24rpx;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
@ -425,21 +459,31 @@ export default {
|
||||||
|
|
||||||
.message-item.user {
|
.message-item.user {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 0 10rpx 0 80rpx;
|
padding: 0 0 0 60rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-item.ai {
|
.message-item.ai {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding: 0 80rpx 0 10rpx;
|
padding: 0 60rpx 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-avatar {
|
.message-avatar {
|
||||||
width: 64rpx;
|
width: 64rpx;
|
||||||
height: 64rpx;
|
height: 64rpx;
|
||||||
margin: 0 16rpx;
|
margin: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* AI消息头像 - 右边距 */
|
||||||
|
.message-item.ai .message-avatar {
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 用户消息头像 - 左边距 */
|
||||||
|
.message-item.user .message-avatar {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.assistant-avatar, .user-avatar {
|
.assistant-avatar, .user-avatar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -555,10 +599,8 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-area {
|
.chat-input-area {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
padding: 16rpx 20rpx;
|
padding: 16rpx 0;
|
||||||
//padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
|
||||||
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
|
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,48 +608,131 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
border-radius: 28rpx;
|
border-radius: 28rpx;
|
||||||
padding: 0 20rpx;
|
padding: 0 20rpx;
|
||||||
backdrop-filter: blur(10rpx);
|
backdrop-filter: blur(15rpx);
|
||||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
border: 3rpx solid rgba(255, 255, 255, 0.98);
|
||||||
height: 56rpx;
|
height: 56rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
|
||||||
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1rpx rgba(255, 255, 255, 0.4),
|
||||||
|
0 2rpx 8rpx rgba(255, 255, 255, 0.15),
|
||||||
|
0 4rpx 16rpx rgba(255, 255, 255, 0.1),
|
||||||
|
inset 0 1rpx 0 rgba(255, 255, 255, 0.2);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 25rpx;
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.05) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: rgba(255, 255, 255, 1);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 2rpx rgba(255, 255, 255, 0.6),
|
||||||
|
0 0 20rpx rgba(255, 255, 255, 0.3),
|
||||||
|
0 4rpx 12rpx rgba(255, 255, 255, 0.2),
|
||||||
|
0 8rpx 24rpx rgba(255, 255, 255, 0.15),
|
||||||
|
inset 0 1rpx 0 rgba(255, 255, 255, 0.3);
|
||||||
|
transform: translateY(-2rpx);
|
||||||
|
}
|
||||||
|
|
||||||
.message-input {
|
.message-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #333333;
|
color: #ffffff;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.95);
|
||||||
|
font-weight: 400;
|
||||||
|
text-shadow: 0 1rpx 2rpx rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.send-button {
|
.send-button {
|
||||||
padding: 12rpx 20rpx;
|
height: 56rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
background: rgba(200, 200, 200, 0.5);
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.01) 100%);
|
||||||
transition: all 0.3s ease;
|
border: 3rpx solid rgba(255, 255, 255, 0.85);
|
||||||
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
backdrop-filter: blur(10rpx);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1rpx rgba(255, 255, 255, 0.3),
|
||||||
|
0 2rpx 6rpx rgba(255, 255, 255, 0.12),
|
||||||
|
0 4rpx 12rpx rgba(255, 255, 255, 0.08),
|
||||||
|
inset 0 1rpx 0 rgba(255, 255, 255, 0.15);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 21rpx;
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, transparent 50%, rgba(255, 255, 255, 0.03) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.96) translateY(1rpx);
|
||||||
|
}
|
||||||
|
|
||||||
.send-text {
|
.send-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #666666;
|
color: rgba(255, 255, 255, 0.9);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
text-shadow: 0 1rpx 2rpx rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.send-button.active {
|
.send-button.active {
|
||||||
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 100%);
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%);
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.4);
|
border: 3rpx solid rgba(255, 255, 255, 0.98);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 2rpx rgba(255, 255, 255, 0.5),
|
||||||
|
0 0 16rpx rgba(255, 255, 255, 0.25),
|
||||||
|
0 4rpx 10rpx rgba(255, 255, 255, 0.18),
|
||||||
|
0 6rpx 20rpx rgba(255, 255, 255, 0.12),
|
||||||
|
inset 0 1rpx 0 rgba(255, 255, 255, 0.25);
|
||||||
|
transform: translateY(-2rpx);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, transparent 50%, rgba(255, 255, 255, 0.06) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
.send-text {
|
.send-text {
|
||||||
color: #ffffff;
|
color: rgba(255, 255, 255, 0.98);
|
||||||
|
font-weight: 600;
|
||||||
|
text-shadow: 0 1rpx 3rpx rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,25 @@
|
||||||
<view class="category-tabs">
|
<view class="category-tabs">
|
||||||
<u-tabs :list="categories" @click="switchCategory" :current="currentCategory"></u-tabs>
|
<u-tabs :list="categories" @click="switchCategory" :current="currentCategory"></u-tabs>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="knowledge-list" scroll-y="true">
|
<view class="knowledge-list">
|
||||||
<view v-for="item in filteredKnowledge" :key="item.id" class="knowledge-card" @click="viewDetail(item)">
|
<scroll-view class="knowledge-scroll" scroll-y="true">
|
||||||
<view class="knowledge-item">
|
<view class="knowledge-content">
|
||||||
<text class="knowledge-title">{{ item.title }}</text>
|
<view v-for="item in filteredKnowledge" :key="item.id" class="knowledge-card" @click="viewDetail(item)">
|
||||||
<text class="knowledge-summary">{{ item.summary }}</text>
|
<view class="knowledge-item">
|
||||||
<view class="knowledge-meta">
|
<text class="knowledge-title">{{ item.title }}</text>
|
||||||
<view class="category-tag">
|
<text class="knowledge-summary">{{ item.summary }}</text>
|
||||||
<text class="tag-text">{{ item.category }}</text>
|
<view class="knowledge-meta">
|
||||||
|
<view class="category-tag">
|
||||||
|
<text class="tag-text">{{ item.category }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="read-count">{{ item.readCount }}次阅读</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="read-count">{{ item.readCount }}次阅读</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</scroll-view>
|
||||||
</scroll-view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -107,39 +111,57 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.knowledge-container {
|
.knowledge-container {
|
||||||
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
width: 100vw;
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 20rpx 30rpx;
|
margin: 20rpx 30rpx 0 30rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-tabs {
|
.category-tabs {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 20rpx 30rpx;
|
margin: 20rpx 30rpx 0 30rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 20rpx 0;
|
padding: 20rpx 0;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.knowledge-list {
|
.knowledge-list {
|
||||||
height: calc(100vh - 300rpx);
|
margin-top: 20rpx;
|
||||||
padding: 0 30rpx;
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-scroll {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-content {
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
padding-bottom: 80rpx;
|
||||||
|
min-height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.knowledge-card {
|
.knowledge-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 20rpx 0;
|
margin-bottom: 24rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="pets-container gradient-bg page-container">
|
<view class="pets-container page-container-unified">
|
||||||
<!-- 标签切换容器 -->
|
<!-- 标签切换容器 -->
|
||||||
<view class="tabs-container" v-if="petsList.length > 0">
|
<view class="tabs-container" v-if="petsList.length > 0">
|
||||||
<view class="tabs-header">
|
<view class="tabs-header">
|
||||||
|
|
@ -653,12 +653,12 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pets-container {
|
.pets-container {
|
||||||
padding-bottom: 120rpx;
|
/* 边距由外层page-container-unified统一管理 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container {
|
.tabs-container {
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: rgba(255, 255, 255, 0.85);
|
||||||
margin: 20rpx 20rpx 30rpx;
|
margin: 0 0 30rpx 0;
|
||||||
border-radius: 40rpx;
|
border-radius: 40rpx;
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
@ -758,7 +758,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
padding: 30rpx;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-panel {
|
.tab-panel {
|
||||||
|
|
@ -941,7 +941,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.functions-section {
|
.functions-section {
|
||||||
margin: 16rpx 30rpx 20rpx;
|
margin: 16rpx 0 20rpx 0;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
padding: 16rpx 0;
|
padding: 16rpx 0;
|
||||||
|
|
@ -1037,7 +1037,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards-section {
|
.cards-section {
|
||||||
padding: 0 30rpx;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="profile-container page-container-with-bg">
|
<view class="profile-container page-container-unified">
|
||||||
<!-- 用户信息卡片 -->
|
<!-- 用户信息卡片 -->
|
||||||
<view class="user-info-card">
|
<view class="user-info-card">
|
||||||
<view class="user-avatar-section" :class="{ 'logged-in': userInfo.nickName }" @click="handleUserAction">
|
<view class="user-avatar-section" :class="{ 'logged-in': userInfo.nickName }" @click="handleUserAction">
|
||||||
|
|
@ -690,14 +690,14 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.profile-container {
|
.profile-container {
|
||||||
padding-bottom: 40rpx;
|
/* 边距由外层page-container-unified统一管理 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 用户信息卡片 */
|
/* 用户信息卡片 */
|
||||||
.user-info-card {
|
.user-info-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 0 30rpx 24rpx 30rpx;
|
margin: 0 0 24rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -837,7 +837,7 @@ export default {
|
||||||
.stats-card {
|
.stats-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 0 30rpx 24rpx 30rpx;
|
margin: 0 0 24rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
|
|
@ -907,7 +907,7 @@ export default {
|
||||||
.family-card {
|
.family-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 0 30rpx 24rpx 30rpx;
|
margin: 0 0 24rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
|
|
@ -1025,7 +1025,7 @@ export default {
|
||||||
.adoption-card {
|
.adoption-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 0 30rpx 24rpx 30rpx;
|
margin: 0 0 24rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
|
|
@ -1122,7 +1122,7 @@ export default {
|
||||||
@media (max-width: 375px) {
|
@media (max-width: 375px) {
|
||||||
.profile-container {
|
.profile-container {
|
||||||
.user-info-card {
|
.user-info-card {
|
||||||
margin: 0 20rpx 20rpx 20rpx;
|
margin: 0 0 20rpx 0;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1130,7 +1130,7 @@ export default {
|
||||||
.family-card,
|
.family-card,
|
||||||
.adoption-card,
|
.adoption-card,
|
||||||
.settings-card {
|
.settings-card {
|
||||||
margin: 0 20rpx 20rpx 20rpx;
|
margin: 0 0 20rpx 0;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1245,7 +1245,7 @@ export default {
|
||||||
.settings-card {
|
.settings-card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(20rpx);
|
backdrop-filter: blur(20rpx);
|
||||||
margin: 0 30rpx 24rpx 30rpx;
|
margin: 0 0 24rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,17 @@
|
||||||
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 统一布局架构 - 页面容器 */
|
||||||
|
.page-container-unified {
|
||||||
|
@extend .page-container-with-bg;
|
||||||
|
padding: 20rpx 30rpx 0 30rpx; /* 上边距20rpx,左右边距30rpx,下边距0 */
|
||||||
|
|
||||||
|
/* 响应式设计 - 小屏幕使用较小边距 */
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
padding: 20rpx 20rpx 0 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 通用背景渐变 */
|
/* 通用背景渐变 */
|
||||||
.gradient-bg {
|
.gradient-bg {
|
||||||
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
||||||
|
|
@ -175,6 +186,18 @@
|
||||||
border-radius: 28rpx;
|
border-radius: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 统一布局架构 - 卡片样式 */
|
||||||
|
.card-unified {
|
||||||
|
@extend .card;
|
||||||
|
margin: 0 0 24rpx 0; /* 仅保留下边距用于卡片间隔 */
|
||||||
|
|
||||||
|
/* 响应式设计 - 小屏幕调整间距 */
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
margin: 0 0 20rpx 0;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 通用按钮样式 */
|
/* 通用按钮样式 */
|
||||||
.btn {
|
.btn {
|
||||||
padding: 16rpx 32rpx;
|
padding: 16rpx 32rpx;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue