This commit is contained in:
yvan 2025-09-05 17:35:26 +08:00
parent 23123f3f9a
commit 3cbbd87a77
14 changed files with 1594 additions and 1745 deletions

View File

@ -12,7 +12,7 @@ export const getAdoptionPets = (params = {}, config = {}) => {
params,
custom: {
auth: false,
loading: true,
loading: false,
...config.custom
},
...config
@ -30,7 +30,7 @@ export const searchPets = (searchParams, config = {}) => {
params: searchParams,
custom: {
auth: false,
loading: true,
loading: false,
...config.custom
},
...config
@ -47,7 +47,7 @@ export const filterPets = (filterParams, config = {}) => {
return uni.$u.http.post('/adoption/pets/filter', filterParams, {
custom: {
auth: false,
loading: true,
loading: false,
...config.custom
},
...config

View File

@ -30,7 +30,7 @@ export const getKnowledgeBase = (params = {}, config = {}) => {
params,
custom: {
auth: false,
loading: true,
loading: false,
...config.custom
},
...config
@ -48,7 +48,7 @@ export const getChatHistory = (params = {}, config = {}) => {
params,
custom: {
auth: true,
loading: true,
loading: false,
...config.custom
},
...config

View File

@ -1,234 +1,512 @@
// 通用API接口
/**
* 通用API接口模块
*
* @fileoverview 提供文件上传系统配置地区数据等通用功能的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组
* - 文件上传相关API图片上传文件上传批量上传
* - 云存储相关API七牛云阿里云OSS配置获取
* - 系统信息相关API系统配置版本信息更新检查
* - 基础数据相关API地区数据反馈提交
*
* @example
* // 基本用法示例
* import { uploadImage, getSystemConfig } from '@/http/api/common.js'
*
* // 上传图片
* const result = await uploadImage({ filePath: 'path/to/image.jpg' })
*
* // 获取系统配置
* const config = await getSystemConfig()
*/
// ==================== 类型定义 ====================
/**
* 上传图片
* @param {Object} imageData 图片数据
* @typedef {Object} UploadData 上传数据对象
* @property {string} filePath 文件路径
* @property {string} [name] 文件字段名默认'file'
* @property {Object} [formData] 额外的表单数据
*/
/**
* @typedef {Object} UploadResult 上传结果对象
* @property {string} url 文件访问URL
* @property {string} key 文件存储键名
* @property {number} size 文件大小
* @property {string} type 文件类型
*/
/**
* @typedef {Object} SystemConfig 系统配置对象
* @property {Object} upload 上传配置
* @property {Object} app 应用配置
* @property {Object} features 功能开关配置
*/
// ==================== 配置常量 ====================
/**
* 通用API的默认配置模板
*/
const COMMON_CONFIG_TEMPLATES = {
// 需要认证的上传请求
AUTHENTICATED_UPLOAD: {
auth: true,
loading: true,
toast: true
},
// 公开的下载请求
PUBLIC_DOWNLOAD: {
auth: false,
loading: true,
toast: true
},
// 静默的配置获取请求
SILENT_CONFIG: {
auth: false,
loading: false,
toast: false
},
// 需要认证的提交请求
AUTHENTICATED_SUBMIT: {
auth: true,
loading: true,
toast: true
}
}
/**
* 通用操作的loading文本配置
*/
const COMMON_LOADING_TEXTS = {
UPLOAD_IMAGE: '正在上传图片...',
UPLOAD_FILE: '正在上传文件...',
UPLOAD_IMAGES: '正在批量上传...',
DOWNLOAD_FILE: '正在下载文件...',
CHECK_UPDATE: '正在检查更新...',
SUBMIT_FEEDBACK: '正在提交反馈...'
}
// ==================== 工具函数 ====================
/**
* 创建通用请求的标准化配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createCommonConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = COMMON_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行上传请求的通用方法
* @param {string} url 上传URL
* @param {Object} uploadData 上传数据
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise}
* @returns {Promise} 请求Promise
*/
const executeUploadRequest = (url, uploadData, loadingText, config = {}) => {
const requestConfig = createCommonConfig('AUTHENTICATED_UPLOAD', config, loadingText)
return uni.$u.http.upload(url, {
filePath: uploadData.filePath,
name: uploadData.name || 'file',
formData: uploadData.formData || {},
...requestConfig
})
}
// ==================== API方法 ====================
// ==================== 文件上传相关API ====================
/**
* 上传单张图片
* @description 上传图片文件到服务器支持多种图片格式
* @param {UploadData} imageData 图片上传数据对象
* @param {string} imageData.filePath 图片文件路径
* @param {string} [imageData.name='file'] 上传字段名
* @param {Object} [imageData.formData={}] 额外的表单数据
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<UploadResult>} 返回上传结果包含图片URL等信息
* @example
* // 基本图片上传
* const result = await uploadImage({
* filePath: 'path/to/image.jpg'
* })
*
* // 带额外数据的图片上传
* const result = await uploadImage({
* filePath: 'path/to/image.jpg',
* name: 'avatar',
* formData: { category: 'profile' }
* })
*/
export const uploadImage = (imageData, config = {}) => {
return uni.$u.http.upload('/upload/image', {
filePath: imageData.filePath,
name: imageData.name || 'file',
formData: imageData.formData || {},
custom: {
auth: true,
loading: true,
loadingText: '正在上传图片...',
...config.custom
},
...config
})
return executeUploadRequest('/upload/image', imageData, COMMON_LOADING_TEXTS.UPLOAD_IMAGE, config)
}
/**
* 上传多张图片
* @param {Array} imageList 图片列表
* @param {Object} config 自定义配置
* @returns {Promise}
* 批量上传多张图片
* @description 同时上传多张图片支持并发上传提升效率
* @param {UploadData[]} imageList 图片列表数组
* @param {Object} [config={}] 自定义请求配置
* @param {boolean} [config.showProgress=true] 是否显示整体进度
* @param {number} [config.maxConcurrent=3] 最大并发上传数量
* @returns {Promise<UploadResult[]>} 返回所有图片的上传结果数组
* @example
* // 批量上传图片
* const results = await uploadImages([
* { filePath: 'path/to/image1.jpg' },
* { filePath: 'path/to/image2.jpg' },
* { filePath: 'path/to/image3.jpg' }
* ])
*
* // 自定义并发数量
* const results = await uploadImages(imageList, {
* maxConcurrent: 5,
* custom: { loadingText: '正在批量上传图片...' }
* })
*/
export const uploadImages = (imageList, config = {}) => {
const uploadPromises = imageList.map(imageData => {
return uploadImage(imageData, {
...config,
custom: {
loading: false, // 批量上传时不显示单个loading
...config.custom
}
export const uploadImages = async (imageList, config = {}) => {
const { maxConcurrent = 3, showProgress = true } = config
// 显示整体进度loading
if (showProgress) {
uni.showLoading({
title: config.custom?.loadingText || COMMON_LOADING_TEXTS.UPLOAD_IMAGES
})
})
return Promise.all(uploadPromises)
}
try {
// 分批并发上传
const results = []
for (let i = 0; i < imageList.length; i += maxConcurrent) {
const batch = imageList.slice(i, i + maxConcurrent)
const batchPromises = batch.map(imageData => {
return uploadImage(imageData, {
...config,
custom: {
loading: false, // 批量上传时不显示单个loading
...config.custom
}
})
})
const batchResults = await Promise.all(batchPromises)
results.push(...batchResults)
}
return results
} finally {
if (showProgress) {
uni.hideLoading()
}
}
}
/**
* 上传文件
* @param {Object} fileData 文件数据
* @param {Object} config 自定义配置
* @returns {Promise}
* 上传通用文件
* @description 上传各种类型的文件到服务器
* @param {UploadData} fileData 文件上传数据对象
* @param {string} fileData.filePath 文件路径
* @param {string} [fileData.name='file'] 上传字段名
* @param {Object} [fileData.formData={}] 额外的表单数据
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<UploadResult>} 返回上传结果包含文件URL等信息
* @example
* // 上传文档文件
* const result = await uploadFile({
* filePath: 'path/to/document.pdf',
* formData: { type: 'document' }
* })
*/
export const uploadFile = (fileData, config = {}) => {
return uni.$u.http.upload('/upload/file', {
filePath: fileData.filePath,
name: fileData.name || 'file',
formData: fileData.formData || {},
custom: {
auth: true,
loading: true,
loadingText: '正在上传文件...',
...config.custom
},
...config
})
return executeUploadRequest('/upload/file', fileData, COMMON_LOADING_TEXTS.UPLOAD_FILE, config)
}
/**
* 下载文件
* @param {String} url 文件URL
* @param {Object} config 自定义配置
* @returns {Promise}
* 下载文件到本地
* @description 从服务器下载文件到本地存储
* @param {string} url 文件下载URL
* @param {Object} [config={}] 自定义请求配置
* @param {string} [config.savePath] 保存路径不指定则使用默认路径
* @returns {Promise<Object>} 返回下载结果包含本地文件路径
* @example
* // 基本文件下载
* const result = await downloadFile('https://example.com/file.pdf')
*
* // 指定保存路径
* const result = await downloadFile('https://example.com/file.pdf', {
* savePath: 'downloads/myfile.pdf'
* })
*/
export const downloadFile = (url, config = {}) => {
const requestConfig = createCommonConfig('PUBLIC_DOWNLOAD', config, COMMON_LOADING_TEXTS.DOWNLOAD_FILE)
return uni.$u.http.download(url, {
custom: {
auth: false,
loading: true,
loadingText: '正在下载文件...',
...config.custom
},
...config
...(config.savePath && { savePath: config.savePath }),
...requestConfig
})
}
// ==================== 云存储相关API ====================
/**
* 获取七牛云上传token
* @param {Object} config 自定义配置
* @returns {Promise}
* 获取七牛云上传凭证
* @description 获取七牛云直传所需的上传token
* @param {Object} [params={}] 请求参数
* @param {string} [params.bucket] 存储桶名称
* @param {string} [params.prefix] 文件前缀
* @param {number} [params.expires] 过期时间
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回七牛云上传配置信息
* @example
* // 获取默认配置
* const qiniuConfig = await getQiniuToken()
*
* // 获取指定桶的配置
* const qiniuConfig = await getQiniuToken({
* bucket: 'my-bucket',
* prefix: 'images/',
* expires: 3600
* })
*/
export const getQiniuToken = (config = {}) => {
export const getQiniuToken = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config)
return uni.$u.http.get('/upload/qiniu-token', {
custom: {
auth: true,
loading: false,
...config.custom
},
...config
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 获取阿里云OSS上传签名
* @param {Object} config 自定义配置
* @returns {Promise}
* @description 获取阿里云OSS直传所需的签名信息
* @param {Object} [params={}] 请求参数
* @param {string} [params.bucket] 存储桶名称
* @param {string} [params.prefix] 文件前缀
* @param {number} [params.expires] 过期时间
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回OSS上传配置信息
* @example
* // 获取默认OSS配置
* const ossConfig = await getOSSSignature()
*
* // 获取指定配置
* const ossConfig = await getOSSSignature({
* bucket: 'my-oss-bucket',
* prefix: 'uploads/',
* expires: 1800
* })
*/
export const getOSSSignature = (config = {}) => {
export const getOSSSignature = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config)
return uni.$u.http.get('/upload/oss-signature', {
custom: {
auth: true,
loading: false,
...config.custom
},
...config
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
// ==================== 系统信息相关API ====================
/**
* 获取系统配置
* @param {Object} config 自定义配置
* @returns {Promise}
* 获取系统配置信息
* @description 获取应用的系统配置包括功能开关上传配置等
* @param {Object} [params={}] 查询参数
* @param {string[]} [params.keys] 指定获取的配置键名数组
* @param {string} [params.category] 配置分类'app' | 'upload' | 'features'
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<SystemConfig>} 返回系统配置对象
* @example
* // 获取所有配置
* const systemConfig = await getSystemConfig()
*
* // 获取指定分类的配置
* const uploadConfig = await getSystemConfig({
* category: 'upload'
* })
*
* // 获取指定键的配置
* const specificConfig = await getSystemConfig({
* keys: ['maxFileSize', 'allowedTypes']
* })
*/
export const getSystemConfig = (config = {}) => {
export const getSystemConfig = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config)
return uni.$u.http.get('/system/config', {
custom: {
auth: false,
loading: false,
...config.custom
},
...config
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 获取版本信息
* @param {Object} config 自定义配置
* @returns {Promise}
* 获取应用版本信息
* @description 获取当前应用的版本信息和更新历史
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回版本信息对象
* @example
* // 获取版本信息
* const versionInfo = await getVersionInfo()
* // 返回格式:
* // {
* // currentVersion: '1.0.0',
* // latestVersion: '1.1.0',
* // updateAvailable: true,
* // updateLog: ['修复bug', '新增功能']
* // }
*/
export const getVersionInfo = (config = {}) => {
return uni.$u.http.get('/system/version', {
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
const requestConfig = createCommonConfig('SILENT_CONFIG', config)
return uni.$u.http.get('/system/version', requestConfig)
}
/**
* 检查更新
* @param {Object} versionData 版本数据
* @param {Object} config 自定义配置
* @returns {Promise}
* 检查应用更新
* @description 检查是否有新版本可用并获取更新信息
* @param {Object} versionData 当前版本数据
* @param {string} versionData.currentVersion 当前版本号
* @param {string} versionData.platform 平台'android' | 'ios' | 'h5' | 'mp-weixin'
* @param {string} [versionData.channel] 更新渠道
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回更新检查结果
* @example
* // 检查更新
* const updateInfo = await checkUpdate({
* currentVersion: '1.0.0',
* platform: 'android'
* })
*
* // 指定更新渠道
* const updateInfo = await checkUpdate({
* currentVersion: '1.0.0',
* platform: 'android',
* channel: 'beta'
* })
*/
export const checkUpdate = (versionData, config = {}) => {
return uni.$u.http.post('/system/check-update', versionData, {
custom: {
auth: false,
loading: true,
...config.custom
},
...config
})
const requestConfig = createCommonConfig('PUBLIC_DOWNLOAD', config, COMMON_LOADING_TEXTS.CHECK_UPDATE)
return uni.$u.http.post('/system/check-update', versionData, requestConfig)
}
/**
* 发送短信验证码
* @param {Object} smsData 短信数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const sendSmsCode = (smsData, config = {}) => {
return uni.$u.http.post('/sms/send', smsData, {
custom: {
auth: false,
loading: true,
loadingText: '正在发送验证码...',
...config.custom
},
...config
})
}
// 短信验证码相关API已移至 http/api/auth.js 文件中
/**
* 验证短信验证码
* @param {Object} verifyData 验证数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const verifySmsCode = (verifyData, config = {}) => {
return uni.$u.http.post('/sms/verify', verifyData, {
custom: {
auth: false,
loading: true,
loadingText: '正在验证...',
...config.custom
},
...config
})
}
// ==================== 基础数据相关API ====================
/**
* 获取地区数据
* @param {Object} params 查询参数
* @param {Object} config 自定义配置
* @returns {Promise}
* @description 获取省市区三级联动的地区数据
* @param {Object} [params={}] 查询参数
* @param {string} [params.level] 数据层级'province' | 'city' | 'district' | 'all'
* @param {string} [params.parentCode] 父级地区代码
* @param {boolean} [params.includeCoordinates] 是否包含经纬度信息
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object[]>} 返回地区数据数组
* @example
* // 获取所有省份
* const provinces = await getRegionData({ level: 'province' })
*
* // 获取指定省份下的城市
* const cities = await getRegionData({
* level: 'city',
* parentCode: '110000'
* })
*
* // 获取完整的三级数据
* const allRegions = await getRegionData({ level: 'all' })
*/
export const getRegionData = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config)
return uni.$u.http.get('/common/regions', {
params,
custom: {
auth: false,
loading: false,
...config.custom
},
...config
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 意见反馈
* @param {Object} feedbackData 反馈数据
* @param {Object} config 自定义配置
* @returns {Promise}
* 提交用户反馈
* @description 提交用户的意见反馈或问题报告
* @param {Object} feedbackData 反馈数据对象
* @param {string} feedbackData.type 反馈类型'bug' | 'suggestion' | 'complaint' | 'other'
* @param {string} feedbackData.title 反馈标题
* @param {string} feedbackData.content 反馈内容
* @param {string} [feedbackData.contact] 联系方式
* @param {string[]} [feedbackData.images] 相关图片URL数组
* @param {Object} [feedbackData.deviceInfo] 设备信息
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回提交结果包含反馈ID
* @example
* // 提交bug反馈
* const result = await submitFeedback({
* type: 'bug',
* title: '登录页面异常',
* content: '点击登录按钮后页面卡死',
* contact: 'user@example.com',
* images: ['https://example.com/screenshot.jpg']
* })
*
* // 提交功能建议
* const result = await submitFeedback({
* type: 'suggestion',
* title: '希望增加夜间模式',
* content: '建议应用支持夜间模式,保护用户视力'
* })
*/
export const submitFeedback = (feedbackData, config = {}) => {
return uni.$u.http.post('/feedback', feedbackData, {
custom: {
auth: true,
loading: true,
loadingText: '正在提交反馈...',
...config.custom
},
...config
})
const requestConfig = createCommonConfig('AUTHENTICATED_SUBMIT', config, COMMON_LOADING_TEXTS.SUBMIT_FEEDBACK)
return uni.$u.http.post('/feedback', feedbackData, requestConfig)
}
// ==================== 导出配置常量(供外部使用) ====================
/**
* 导出通用配置常量供其他模块使用
*/
export const COMMON_CONFIG = {
COMMON_CONFIG_TEMPLATES,
COMMON_LOADING_TEXTS
}
/**
* 导出通用工具函数供其他模块使用
*/
export const COMMON_UTILS = {
createCommonConfig,
executeUploadRequest
}

View File

@ -1,90 +1,359 @@
// 宠物管理相关API接口
// 注意:所有接口的鉴权配置已在 http/config/config.js 中统一管理
/**
* 宠物管理相关API接口模块
*
* @fileoverview 提供宠物信息管理记录管理健康档案等相关的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组
* - 宠物基础信息管理增删改查宠物信息
* - 宠物记录管理日常记录健康记录成长记录
* - 宠物健康档案疫苗记录体检记录用药记录
* - 宠物统计分析成长数据健康趋势分析
*
* @example
* // 基本用法示例
* import { getPetsList, addPet, getPetRecords } from '@/http/api/pets.js'
*
* // 获取宠物列表
* const pets = await getPetsList()
*
* // 添加新宠物
* await addPet({ name: '小白', breed: '金毛', age: 2 })
*
* // 获取宠物记录
* const records = await getPetRecords(petId)
*/
// ==================== 类型定义 ====================
/**
* 获取宠物列表
* @typedef {Object} PetInfo 宠物信息对象
* @property {string} id 宠物ID
* @property {string} name 宠物名称
* @property {string} breed 品种
* @property {string} type 类型'dog' | 'cat' | 'bird' | 'rabbit' | 'other'
* @property {string} gender 性别'male' | 'female'
* @property {number} age 年龄
* @property {number} weight 体重kg
* @property {string} avatarUrl 头像URL
* @property {string} description 描述
* @property {string} createTime 创建时间
*/
/**
* @typedef {Object} PetRecord 宠物记录对象
* @property {string} id 记录ID
* @property {string} petId 宠物ID
* @property {string} type 记录类型'feeding' | 'health' | 'exercise' | 'grooming' | 'other'
* @property {string} title 记录标题
* @property {string} content 记录内容
* @property {string[]} images 相关图片
* @property {string} recordTime 记录时间
*/
// ==================== 配置常量 ====================
/**
* 宠物API的默认配置模板
*/
const PETS_CONFIG_TEMPLATES = {
// 需要认证的查询请求无loading
AUTHENTICATED_QUERY: {
auth: true,
loading: false,
toast: true
},
// 需要认证的查询请求有loading
AUTHENTICATED_QUERY_WITH_LOADING: {
auth: true,
loading: true,
toast: true
},
// 需要认证的更新请求
AUTHENTICATED_UPDATE: {
auth: true,
loading: true,
toast: true
},
// 需要认证的删除请求
AUTHENTICATED_DELETE: {
auth: true,
loading: true,
toast: true
}
}
/**
* 宠物相关的loading文本配置
*/
const PETS_LOADING_TEXTS = {
ADD_PET: '正在添加宠物...',
UPDATE_PET: '正在更新宠物信息...',
DELETE_PET: '正在删除宠物...',
ADD_RECORD: '正在添加记录...',
UPDATE_RECORD: '正在更新记录...',
DELETE_RECORD: '正在删除记录...',
LOADING_DATA: '正在加载...'
}
// ==================== 工具函数 ====================
/**
* 创建宠物请求的标准化配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createPetsConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = PETS_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行宠物相关的GET请求
* @param {string} url 请求URL
* @param {Object} params 查询参数
* @param {string} template 配置模板
* @param {Object} config 自定义配置
* @returns {Promise}
* @returns {Promise} 请求Promise
*/
const executePetsGetRequest = (url, params = {}, template = 'AUTHENTICATED_QUERY', config = {}) => {
const requestConfig = createPetsConfig(template, config)
return uni.$u.http.get(url, {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 执行宠物相关的POST请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsPostRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createPetsConfig(template, config, loadingText)
return uni.$u.http.post(url, data, requestConfig)
}
/**
* 执行宠物相关的PUT请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsPutRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createPetsConfig(template, config, loadingText)
return uni.$u.http.put(url, data, requestConfig)
}
/**
* 执行宠物相关的DELETE请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsDeleteRequest = (url, data = {}, template = 'AUTHENTICATED_DELETE', loadingText = null, config = {}) => {
const requestConfig = createPetsConfig(template, config, loadingText)
return uni.$u.http.delete(url, data, requestConfig)
}
// ==================== API方法 ====================
// ==================== 宠物基础信息管理API ====================
/**
* 获取用户宠物列表
* @description 获取当前用户的所有宠物信息列表支持分页和筛选
* @param {Object} [params={}] 查询参数
* @param {number} [params.page=1] 页码
* @param {number} [params.pageSize=20] 每页数量
* @param {string} [params.type] 宠物类型筛选'dog' | 'cat' | 'bird' | 'rabbit' | 'other'
* @param {string} [params.breed] 品种筛选
* @param {string} [params.keyword] 关键词搜索名称品种
* @param {string} [params.sortBy] 排序字段'createTime' | 'name' | 'age'
* @param {string} [params.sortOrder] 排序方向'asc' | 'desc'
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回宠物列表和分页信息
* @example
* // 获取所有宠物
* const pets = await getPetsList()
*
* // 分页获取狗狗列表
* const dogs = await getPetsList({
* type: 'dog',
* page: 1,
* pageSize: 10
* })
*
* // 搜索宠物
* const searchResults = await getPetsList({
* keyword: '小白',
* sortBy: 'createTime',
* sortOrder: 'desc'
* })
*/
export const getPetsList = (params = {}, config = {}) => {
return uni.$u.http.get('/pets', {
params,
custom: {
loading: true,
...config.custom
},
...config
})
return executePetsGetRequest('/pets', params, 'AUTHENTICATED_QUERY', config)
}
/**
* 获取宠物详情
* @param {String|Number} petId 宠物ID
* @param {Object} config 自定义配置
* @returns {Promise}
* 获取宠物详细信息
* @description 根据宠物ID获取详细的宠物信息
* @param {string|number} petId 宠物ID
* @param {Object} [config={}] 自定义请求配置
* @param {boolean} [config.includeRecords=false] 是否包含最近记录
* @param {boolean} [config.includeHealth=false] 是否包含健康档案
* @returns {Promise<PetInfo>} 返回宠物详细信息
* @example
* // 获取基本信息
* const pet = await getPetDetail(123)
*
* // 获取包含记录的详细信息
* const petWithRecords = await getPetDetail(123, {
* includeRecords: true,
* includeHealth: true
* })
*/
export const getPetDetail = (petId, config = {}) => {
return uni.$u.http.get(`/pets/${petId}`, {
custom: {
loading: true,
...config.custom
},
...config
})
const params = {}
if (config.includeRecords) params.includeRecords = true
if (config.includeHealth) params.includeHealth = true
return executePetsGetRequest(`/pets/${petId}`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
}
/**
* 添加宠物
* @param {Object} petData 宠物数据
* @param {Object} config 自定义配置
* @returns {Promise}
* 添加新宠物
* @description 为当前用户添加一只新宠物
* @param {Object} petData 宠物信息数据
* @param {string} petData.name 宠物名称必填
* @param {string} petData.breed 品种必填
* @param {string} petData.type 类型'dog' | 'cat' | 'bird' | 'rabbit' | 'other'
* @param {string} petData.gender 性别'male' | 'female'
* @param {number} [petData.age] 年龄
* @param {number} [petData.weight] 体重kg
* @param {string} [petData.avatarUrl] 头像URL
* @param {string} [petData.description] 描述
* @param {string} [petData.birthDate] 出生日期
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<PetInfo>} 返回新添加的宠物信息
* @example
* // 添加基本宠物信息
* const newPet = await addPet({
* name: '小白',
* breed: '金毛',
* type: 'dog',
* gender: 'male',
* age: 24,
* weight: 25.5
* })
*
* // 添加完整宠物信息
* const newPet = await addPet({
* name: '小花',
* breed: '英短',
* type: 'cat',
* gender: 'female',
* age: 12,
* weight: 4.2,
* avatarUrl: 'https://example.com/cat.jpg',
* description: '很可爱的小猫咪',
* birthDate: '2023-01-15'
* })
*/
export const addPet = (petData, config = {}) => {
return uni.$u.http.post('/pets', petData, {
custom: {
loading: true,
loadingText: '正在添加宠物...',
...config.custom
},
...config
})
return executePetsPostRequest('/pets', petData, 'AUTHENTICATED_UPDATE', PETS_LOADING_TEXTS.ADD_PET, config)
}
/**
* 更新宠物信息
* @param {String|Number} petId 宠物ID
* @param {Object} petData 宠物数据
* @param {Object} config 自定义配置
* @returns {Promise}
* @description 更新指定宠物的信息支持部分字段更新
* @param {string|number} petId 宠物ID
* @param {Object} petData 要更新的宠物数据
* @param {string} [petData.name] 宠物名称
* @param {string} [petData.breed] 品种
* @param {string} [petData.type] 类型
* @param {string} [petData.gender] 性别
* @param {number} [petData.age] 年龄
* @param {number} [petData.weight] 体重kg
* @param {string} [petData.avatarUrl] 头像URL
* @param {string} [petData.description] 描述
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<PetInfo>} 返回更新后的宠物信息
* @example
* // 更新宠物体重
* const updatedPet = await updatePet(123, {
* weight: 26.0
* })
*
* // 更新多个字段
* const updatedPet = await updatePet(123, {
* name: '小白白',
* age: 25,
* description: '更新后的描述'
* })
*/
export const updatePet = (petId, petData, config = {}) => {
return uni.$u.http.put(`/pets/${petId}`, petData, {
custom: {
loading: true,
loadingText: '正在更新宠物信息...',
...config.custom
},
...config
})
return executePetsPutRequest(`/pets/${petId}`, petData, 'AUTHENTICATED_UPDATE', PETS_LOADING_TEXTS.UPDATE_PET, config)
}
/**
* 删除宠物
* @param {String|Number} petId 宠物ID
* @param {Object} config 自定义配置
* @returns {Promise}
* @description 永久删除指定的宠物及其所有相关记录此操作不可逆
* @param {string|number} petId 宠物ID
* @param {Object} [config={}] 自定义请求配置
* @param {boolean} [config.force=false] 是否强制删除忽略关联数据检查
* @returns {Promise<Object>} 返回删除结果
* @example
* // 普通删除
* await deletePet(123)
*
* // 强制删除(忽略关联数据)
* await deletePet(123, { force: true })
*
* @warning 此操作将永久删除宠物及其所有记录请谨慎使用
*/
export const deletePet = (petId, config = {}) => {
return uni.$u.http.delete(`/pets/${petId}`, {}, {
custom: {
auth: true,
loading: true,
loadingText: '正在删除宠物...',
...config.custom
},
...config
})
const data = config.force ? { force: true } : {}
return executePetsDeleteRequest(`/pets/${petId}`, data, 'AUTHENTICATED_DELETE', PETS_LOADING_TEXTS.DELETE_PET, config)
}
/**
@ -99,7 +368,7 @@ export const getPetRecords = (petId, params = {}, config = {}) => {
params,
custom: {
auth: true,
loading: true,
loading: false,
...config.custom
},
...config

View File

@ -1,256 +1,448 @@
// 个人中心相关API接口
/**
* 个人中心相关API接口模块
*
* @fileoverview 提供用户信息管理资料完善偏好设置等相关的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组
* - 用户信息相关API获取更新用户基本信息
* - 用户统计相关API获取各项统计数据
* - 账户管理相关API账户注销等敏感操作
* - 用户资料完善相关API新用户资料完善
* - 头像上传相关API头像图片上传和更新
* - 用户偏好设置相关API个性化设置管理
* - 用户活动记录相关API操作记录查询
*
* @example
* // 基本用法示例
* import { getUserInfo, updateUserInfo } from '@/http/api/profile.js'
*
* // 获取用户信息
* const userInfo = await getUserInfo()
*
* // 更新用户信息
* await updateUserInfo({ nickName: '新昵称' })
*
* @example
* // 使用配置常量
* import { PROFILE_CONFIG, PROFILE_UTILS } from '@/http/api/profile.js'
*
* // 使用工具函数创建自定义请求
* const customRequest = PROFILE_UTILS.executeGetRequest('/custom/endpoint')
*/
// ==================== 类型定义 ====================
/**
* 获取用户信息
* @param {Object} config 自定义配置
* @returns {Promise}
* @typedef {Object} UserInfo 用户信息对象
* @property {string} id 用户ID
* @property {string} nickName 用户昵称
* @property {string} avatarUrl 头像URL
* @property {string} gender 性别'男' | '女' | '保密'
* @property {string} birthday 生日格式YYYY-MM-DD
* @property {string} region 所在地区
* @property {string} bio 个人简介
* @property {string} createTime 创建时间
* @property {string} updateTime 更新时间
*/
export const getUserInfo = (config = {}) => {
return uni.$u.http.get('/user/info', {
/**
* @typedef {Object} UserStats 用户统计数据对象
* @property {number} petCount 宠物数量
* @property {number} recordCount 记录数量
* @property {number} reminderCount 提醒数量
* @property {number} familyMemberCount 家庭成员数量
*/
/**
* @typedef {Object} RequestConfig 请求配置对象
* @property {Object} custom 自定义配置
* @property {boolean} custom.auth 是否需要认证
* @property {boolean} custom.loading 是否显示loading
* @property {string} custom.loadingText loading文本
* @property {boolean} custom.toast 是否显示错误提示
*/
// ==================== 配置常量 ====================
/**
* API请求的默认配置模板
*/
const DEFAULT_CONFIG_TEMPLATES = {
// 需要认证的查询请求无loading
AUTHENTICATED_QUERY: {
auth: true,
loading: false,
toast: true
},
// 需要认证的查询请求有loading
AUTHENTICATED_QUERY_WITH_LOADING: {
auth: true,
loading: true,
toast: true
},
// 需要认证的更新请求
AUTHENTICATED_UPDATE: {
auth: true,
loading: true,
toast: true
},
// 需要认证的删除请求
AUTHENTICATED_DELETE: {
auth: true,
loading: true,
toast: true
}
}
/**
* 常用的loading文本配置
*/
const LOADING_TEXTS = {
UPDATING_USER_INFO: '正在更新用户信息...',
SAVING_PROFILE: '正在保存...',
DELETING_ACCOUNT: '正在注销账户...',
UPLOADING_AVATAR: '正在上传头像...',
LOADING_DATA: '正在加载...'
}
// ==================== 工具函数 ====================
/**
* 创建标准化的API请求配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createRequestConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = DEFAULT_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
auth: true,
loading: true,
...config.custom
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...config
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行GET请求的通用方法
* @param {string} url 请求URL
* @param {Object} params 查询参数
* @param {string} template 配置模板
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executeGetRequest = (url, params = {}, template = 'AUTHENTICATED_QUERY', config = {}) => {
const requestConfig = createRequestConfig(template, config)
return uni.$u.http.get(url, {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 更新用户信息
* @param {Object} userInfo 用户信息
* 执行POST请求的通用方法
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise}
* @returns {Promise} 请求Promise
*/
const executePostRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.post(url, data, requestConfig)
}
/**
* 执行PUT请求的通用方法
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePutRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.put(url, data, requestConfig)
}
/**
* 执行DELETE请求的通用方法
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executeDeleteRequest = (url, data = {}, template = 'AUTHENTICATED_DELETE', loadingText = null, config = {}) => {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.delete(url, data, requestConfig)
}
// ==================== API方法 ====================
// ==================== 用户信息相关API ====================
/**
* 获取用户基本信息
* @description 获取当前登录用户的基本信息包括昵称头像个人资料等
* @param {Object} [config={}] 自定义请求配置
* @param {Object} [config.custom] 自定义请求选项
* @param {boolean} [config.custom.loading] 是否显示loading默认true
* @param {boolean} [config.custom.toast] 是否显示错误提示默认true
* @returns {Promise<Object>} 返回用户信息对象
* @example
* // 基本用法
* const userInfo = await getUserInfo()
*
* // 自定义配置
* const userInfo = await getUserInfo({
* custom: { loading: false }
* })
*/
export const getUserInfo = (config = {}) => {
return executeGetRequest('/user/info', {}, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
}
/**
* 更新用户基本信息
* @description 更新用户的基本信息如昵称头像个人简介等
* @param {Object} userInfo 用户信息对象
* @param {string} [userInfo.nickName] 用户昵称
* @param {string} [userInfo.avatarUrl] 头像URL
* @param {string} [userInfo.bio] 个人简介
* @param {string} [userInfo.gender] 性别
* @param {string} [userInfo.birthday] 生日
* @param {string} [userInfo.region] 地区
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回更新后的用户信息
* @example
* await updateUserInfo({
* nickName: '新昵称',
* bio: '个人简介'
* })
*/
export const updateUserInfo = (userInfo, config = {}) => {
return uni.$u.http.put('/user/info', userInfo, {
custom: {
auth: true,
loading: true,
loadingText: '正在更新用户信息...',
...config.custom
},
...config
})
return executePutRequest('/user/info', userInfo, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATING_USER_INFO, config)
}
/**
* 获取用户宠物列表
* @param {Object} params 查询参数
* @param {Object} config 自定义配置
* @returns {Promise}
* @description 获取当前用户的所有宠物信息列表
* @param {Object} [params={}] 查询参数
* @param {number} [params.page] 页码默认1
* @param {number} [params.pageSize] 每页数量默认20
* @param {string} [params.type] 宠物类型筛选
* @param {string} [params.status] 状态筛选
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回宠物列表和分页信息
* @example
* // 获取所有宠物
* const pets = await getUserPets()
*
* // 分页查询
* const pets = await getUserPets({ page: 1, pageSize: 10 })
*/
export const getUserPets = (params = {}, config = {}) => {
return uni.$u.http.get('/user/pets', {
params,
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
return executeGetRequest('/user/pets', params, 'AUTHENTICATED_QUERY', config)
}
/**
* 用户登录
* @param {Object} loginData 登录数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const userLogin = (loginData, config = {}) => {
return uni.$u.http.post('/auth/login', loginData, {
custom: {
auth: false,
loading: true,
loadingText: '正在登录...',
...config.custom
},
...config
})
}
/**
* 微信登录
* @param {Object} wxData 微信登录数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const wxLogin = (wxData, config = {}) => {
return uni.$u.http.post('/auth/wx-login', wxData, {
custom: {
auth: false,
loading: true,
loadingText: '正在登录...',
...config.custom
},
...config
})
}
/**
* 用户注册
* @param {Object} registerData 注册数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const userRegister = (registerData, config = {}) => {
return uni.$u.http.post('/auth/register', registerData, {
custom: {
auth: false,
loading: true,
loadingText: '正在注册...',
...config.custom
},
...config
})
}
/**
* 用户登出
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const userLogout = (config = {}) => {
return uni.$u.http.post('/auth/logout', {}, {
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
}
/**
* 刷新token
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const refreshToken = (config = {}) => {
const refreshToken = uni.getStorageSync('refreshToken')
return uni.$u.http.post('/auth/refresh', { refreshToken }, {
custom: {
loading: false,
toast: false,
...config.custom
},
...config
})
}
/**
* 修改密码
* @param {Object} passwordData 密码数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const changePassword = (passwordData, config = {}) => {
return uni.$u.http.put('/user/password', passwordData, {
custom: {
auth: true,
loading: true,
loadingText: '正在修改密码...',
...config.custom
},
...config
})
}
// ==================== 用户统计相关API ====================
/**
* 获取用户统计数据
* @param {Object} config 自定义配置
* @returns {Promise}
* @description 获取用户的各项统计数据如宠物数量记录数量提醒数量等
* @param {Object} [config={}] 自定义请求配置
* @param {Object} [config.custom] 自定义请求选项
* @param {boolean} [config.custom.loading] 是否显示loading默认false后台获取
* @returns {Promise<Object>} 返回统计数据对象
* @example
* const stats = await getUserStats()
* // 返回格式:
* // {
* // petCount: 3,
* // recordCount: 25,
* // reminderCount: 5,
* // familyMemberCount: 2
* // }
*/
export const getUserStats = (config = {}) => {
return uni.$u.http.get('/user/stats', {
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
return executeGetRequest('/user/stats', {}, 'AUTHENTICATED_QUERY', config)
}
/**
* 获取用户设置
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const getUserSettings = (config = {}) => {
return uni.$u.http.get('/user/settings', {
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
}
// ==================== 账户管理相关API ====================
/**
* 更新用户设置
* @param {Object} settings 设置数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const updateUserSettings = (settings, config = {}) => {
return uni.$u.http.put('/user/settings', settings, {
custom: {
auth: true,
loading: true,
loadingText: '正在保存设置...',
...config.custom
},
...config
})
}
/**
* 绑定手机号
* @param {Object} phoneData 手机号数据
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const bindPhone = (phoneData, config = {}) => {
return uni.$u.http.post('/user/bind-phone', phoneData, {
custom: {
loading: true,
loadingText: '正在绑定手机号...',
...config.custom
},
...config
})
}
/**
* 获取用户权限
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const getUserPermissions = (config = {}) => {
return uni.$u.http.get('/user/permissions', {
custom: {
loading: false,
...config.custom
},
...config
})
}
/**
* 注销账户
* @param {Object} config 自定义配置
* @returns {Promise}
* 注销用户账户
* @description 永久删除用户账户及所有相关数据此操作不可逆
* @param {Object} [config={}] 自定义请求配置
* @param {Object} [config.custom] 自定义请求选项
* @param {boolean} [config.custom.loading] 是否显示loading默认true
* @param {string} [config.custom.loadingText] 自定义loading文本
* @returns {Promise<Object>} 返回注销结果
* @example
* await deleteAccount()
*
* @warning 此操作将永久删除所有用户数据请谨慎使用
*/
export const deleteAccount = (config = {}) => {
return uni.$u.http.delete('/user/account', {}, {
custom: {
loading: true,
loadingText: '正在注销账户...',
...config.custom
},
...config
})
return executeDeleteRequest('/user/account', {}, 'AUTHENTICATED_DELETE', LOADING_TEXTS.DELETING_ACCOUNT, config)
}
// ==================== 用户资料完善相关API ====================
/**
* 完善用户资料信息
* @description 用于新用户首次登录后完善个人资料信息或更新现有资料
* @param {Object} profileData 用户资料数据对象
* @param {string} profileData.nickName 用户昵称必填
* @param {string} [profileData.avatarUrl] 头像URL
* @param {string} [profileData.gender] 性别'男' | '女' | '保密'
* @param {string} [profileData.birthday] 生日格式YYYY-MM-DD
* @param {string} [profileData.region] 所在地区
* @param {string} [profileData.bio] 个人简介
* @param {Object} [config={}] 自定义请求配置
* @param {Object} [config.custom] 自定义请求选项
* @param {boolean} [config.custom.loading] 是否显示loading默认true
* @param {string} [config.custom.loadingText] 自定义loading文本
* @returns {Promise<Object>} 返回完善后的用户信息
* @example
* // 基本用法
* await completeUserProfile({
* nickName: '小明',
* gender: '男',
* birthday: '1990-01-01',
* region: '北京市',
* bio: '热爱宠物的程序员'
* })
*
* // 自定义loading文本
* await completeUserProfile(profileData, {
* custom: { loadingText: '正在创建用户资料...' }
* })
*/
export const completeUserProfile = (profileData, config = {}) => {
return executePostRequest('/user/profile/complete', profileData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SAVING_PROFILE, config)
}
// ==================== 头像上传相关API ====================
/**
* 上传用户头像
* @description 上传并更新用户头像图片
* @param {Object} avatarData 头像数据对象
* @param {string} avatarData.avatarUrl 头像图片URL或base64数据
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回上传结果和新的头像URL
* @example
* const result = await uploadAvatar({
* avatarUrl: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...'
* })
*/
export const uploadAvatar = (avatarData, config = {}) => {
return executePostRequest('/user/avatar', avatarData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPLOADING_AVATAR, config)
}
// ==================== 用户偏好设置相关API ====================
/**
* 获取用户偏好设置
* @description 获取用户的个性化偏好设置如通知设置隐私设置等
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回用户偏好设置对象
* @example
* const preferences = await getUserPreferences()
*/
export const getUserPreferences = (config = {}) => {
return executeGetRequest('/user/preferences', {}, 'AUTHENTICATED_QUERY', config)
}
/**
* 更新用户偏好设置
* @description 更新用户的个性化偏好设置
* @param {Object} preferences 偏好设置对象
* @param {boolean} [preferences.notificationEnabled] 是否启用通知
* @param {boolean} [preferences.privacyMode] 是否启用隐私模式
* @param {string} [preferences.theme] 主题设置'light' | 'dark' | 'auto'
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回更新后的偏好设置
* @example
* await updateUserPreferences({
* notificationEnabled: true,
* theme: 'dark'
* })
*/
export const updateUserPreferences = (preferences, config = {}) => {
return executePutRequest('/user/preferences', preferences, 'AUTHENTICATED_UPDATE', '正在保存设置...', config)
}
// ==================== 用户活动记录相关API ====================
/**
* 获取用户活动记录
* @description 获取用户的操作活动记录列表
* @param {Object} [params={}] 查询参数
* @param {number} [params.page] 页码默认1
* @param {number} [params.pageSize] 每页数量默认20
* @param {string} [params.type] 活动类型筛选
* @param {string} [params.startDate] 开始日期
* @param {string} [params.endDate] 结束日期
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回活动记录列表和分页信息
* @example
* const activities = await getUserActivities({
* page: 1,
* pageSize: 10,
* type: 'pet_care'
* })
*/
export const getUserActivities = (params = {}, config = {}) => {
return executeGetRequest('/user/activities', params, 'AUTHENTICATED_QUERY', config)
}
// ==================== 导出配置常量(供外部使用) ====================
/**
* 导出配置常量供其他模块使用
*/
export const PROFILE_CONFIG = {
DEFAULT_CONFIG_TEMPLATES,
LOADING_TEXTS
}
/**
* 导出工具函数供其他模块使用
*/
export const PROFILE_UTILS = {
createRequestConfig,
executeGetRequest,
executePostRequest,
executePutRequest,
executeDeleteRequest
}

View File

@ -11,7 +11,7 @@ export const getReviews = (params = {}, config = {}) => {
params,
custom: {
auth: false,
loading: true,
loading: false,
...config.custom
},
...config
@ -31,7 +31,7 @@ export const getTargetReviews = (targetType, targetId, params = {}, config = {})
params,
custom: {
auth: false,
loading: true,
loading: false,
...config.custom
},
...config

View File

@ -60,7 +60,13 @@ export const NO_AUTH_APIS = [
'/auth/login',
'/auth/register',
'/auth/wx-login',
'/auth/wx-phone-login', // 微信手机号登录
'/auth/refresh',
'/auth/reset-password', // 重置密码
// 短信验证相关
'/sms/send', // 发送短信验证码
'/sms/verify', // 验证短信验证码
// 公开浏览的接口
'/ai/knowledge', // AI知识库
@ -69,12 +75,14 @@ export const NO_AUTH_APIS = [
'/adoption/pets/filter', // 筛选宠物
'/adoption/organizations', // 领养机构列表
'/reviews', // 查看评价
'/reviews/*', // 查看特定对象的评价
// 系统相关
'/system/config', // 系统配置
'/system/version', // 版本信息
'/system/check-update', // 检查更新
'/sms/send', // 发送短信验证码
'/sms/verify', // 验证短信验证码
'/common/regions' // 地区数据
'/common/regions', // 地区数据
'/common/upload-config' // 上传配置
]
/**

View File

@ -259,6 +259,14 @@
"navigationBarBackgroundColor": "#FF8A80",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/auth/phone-auth",
"style": {
"navigationBarTitleText": "手机号授权",
"navigationBarBackgroundColor": "#FF8A80",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {

View File

@ -864,34 +864,28 @@ const adoptionManager = new AdoptionManager()
export default {
data() {
return {
//
//
searchKeyword: '',
//
//
showFilter: false,
//
selectedProvince: null,
selectedCity: null,
selectedDistrict: null,
selectedProvinceIndex: 0,
selectedCityIndex: 0,
selectedDistrictIndex: 0,
//
selectedType: 'all',
selectedBreed: 'all',
//
selectedStatus: 'all',
selectedGender: 'all',
//
//
allPets: [],
displayPets: [],
totalPets: 0,
//
//
page: 1,
pageSize: 20,
hasMorePets: true,

View File

@ -210,6 +210,27 @@
</template>
<script>
//
const ACTION_ITEMS_CONFIG = [
{ id: 1, icon: '📝', text: '添加记录', action: 'addRecord' },
{ id: 2, icon: '💬', text: 'AI聊天', action: 'chatWithPet' },
{ id: 3, icon: '📊', text: '健康档案', action: 'viewHealth' },
{ id: 4, icon: '📸', text: '成长时光', action: 'viewTimeline' },
{ id: 5, icon: '📋', text: '所有记录', action: 'viewRecords' },
{ id: 6, icon: '🔔', text: '提醒设置', action: 'showReminder' }
]
const PET_EMOJI_MAP = {
'橘猫': '🐱',
'金毛': '🐶',
'垂耳兔': '🐰',
'哈士奇': '🐕',
'波斯猫': '😸',
'泰迪': '🐩',
'仓鼠': '🐹',
'鹦鹉': '🦜'
}
export default {
data() {
return {
@ -218,37 +239,8 @@ export default {
currentTabIndex: 0,
petRecords: [],
petExpenses: [],
mainActionItems: [
{ id: 1, icon: '📝', text: '添加记录', action: 'addRecord' },
{ id: 2, icon: '💬', text: 'AI聊天', action: 'chatWithPet' },
{ id: 3, icon: '📊', text: '健康档案', action: 'viewHealth' },
{ id: 4, icon: '📸', text: '成长时光', action: 'viewTimeline' },
{ id: 5, icon: '📋', text: '所有记录', action: 'viewRecords' },
{ id: 6, icon: '🔔', text: '提醒设置', action: 'showReminder' }
],
recentInteractions: [
{
id: 1,
petEmoji: '🐱',
title: '小橘完成了今日散步',
time: '2小时前',
typeIcon: '🚶'
},
{
id: 2,
petEmoji: '🐶',
title: '小白吃完了晚餐',
time: '4小时前',
typeIcon: '🍽️'
},
{
id: 3,
petEmoji: '🐱',
title: '小橘的体重记录已更新',
time: '1天前',
typeIcon: '⚖️'
}
]
mainActionItems: ACTION_ITEMS_CONFIG,
recentInteractions: []
}
},
onShow() {
@ -304,26 +296,12 @@ export default {
//
getPetEmoji(breed) {
const emojiMap = {
'橘猫': '🐱',
'金毛': '🐶',
'垂耳兔': '🐰',
'哈士奇': '🐕',
'波斯猫': '😸',
'泰迪': '🐩',
'仓鼠': '🐹',
'鹦鹉': '🦜'
}
return emojiMap[breed] || '🐾'
return PET_EMOJI_MAP[breed] || '🐾'
},
loadPets() {
try {
let pets = uni.getStorageSync('pets') || []
//
if (pets.length === 0) {
pets = [
//
generateDefaultPets() {
return [
{
id: 1,
name: '小橘',
@ -474,7 +452,16 @@ export default {
personality: ['慵懒', '贪吃', '老成'],
lastRecord: '2024-01-11'
}
]
]
},
loadPets() {
try {
let pets = uni.getStorageSync('pets') || []
// 使
if (pets.length === 0) {
pets = this.generateDefaultPets()
//
uni.setStorageSync('pets', pets)
}

View File

@ -136,64 +136,7 @@
</view>
</view>
<!-- 用户信息设置模态框 -->
<u-modal
:show="showUserInfoModal"
title="完善个人信息"
@cancel="cancelUserInfo"
:show-cancel-button="false"
:show-confirm-button="false"
:mask-close-able="true"
>
<view class="user-info-form" :class="{ 'form-animate': showUserInfoModal }">
<view class="form-item avatar-form-item">
<text class="form-label">选择头像</text>
<view class="avatar-selector">
<button
class="avatar-button"
open-type="chooseAvatar"
@chooseavatar="onChooseAvatar"
>
<view class="avatar-container">
<u-avatar
:src="tempUserInfo.avatarUrl || ''"
:text="tempUserInfo.nickName ? tempUserInfo.nickName.charAt(0) : '👤'"
size="100"
shape="circle"
bg-color="linear-gradient(135deg, #FF8A80, #FFB6C1)"
color="white"
font-size="36"
></u-avatar>
<view class="avatar-edit-overlay">
<view class="edit-icon-wrapper">
<text class="edit-icon">📷</text>
</view>
</view>
</view>
</button>
</view>
</view>
<view class="form-item">
<text class="form-label">昵称</text>
<input
type="nickname"
v-model="tempUserInfo.nickName"
placeholder="请输入昵称"
class="nickname-input"
/>
</view>
<!-- 自定义保存按钮 -->
<view class="custom-button-area">
<button class="custom-save-button" @click="saveUserInfo">
<view class="button-content">
<text class="button-text">保存信息</text>
</view>
<view class="button-shine"></view>
</button>
</view>
</view>
</u-modal>
</view>
</template>
@ -201,6 +144,13 @@
import { reactive, ref, onMounted, computed } from 'vue'
import { uploadImage } from '@/http/api/common.js'
import { updateUserInfo } from '@/http/api/profile.js'
import {
getCurrentLoginStep,
isLoginCompleted,
checkAndResumeLogin,
saveWxLoginData,
clearTempLoginData
} from '@/utils/loginState.js'
export default {
name: 'ProfilePage',
@ -213,12 +163,7 @@ export default {
const loginDays = ref(0)
//
const showUserInfoModal = ref(false)
const tempUserInfo = ref({
nickName: '',
avatarUrl: ''
})
@ -249,11 +194,39 @@ export default {
}
const checkLogin = () => {
//
const savedUserInfo = uni.getStorageSync('userInfo')
if (savedUserInfo) {
userInfo.value = savedUserInfo
calculateLoginDays()
//
console.log('Profile页面登录状态检查')
//
const nextPage = checkAndResumeLogin()
if (nextPage) {
console.log('检测到未完成的登录流程,跳转到:', nextPage)
//
uni.showToast({
title: '继续完成登录流程',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateTo({
url: nextPage
})
}, 1500)
return
}
//
if (isLoginCompleted()) {
const savedUserInfo = uni.getStorageSync('userInfo')
if (savedUserInfo) {
userInfo.value = savedUserInfo
calculateLoginDays()
//
clearTempLoginData()
}
}
}
@ -306,18 +279,29 @@ export default {
} catch (error) {
console.error('加载用户统计数据失败:', error)
//
petStats.petCount = 0
petStats.recordCount = 0
petStats.reminderCount = 0
familyStats.memberCount = 1
familyStats.sharedPets = 0
familyStats.activityCount = 0
adoptionStats.publishedCount = 0
adoptionStats.applicationCount = 0
//
resetStatsToDefault()
}
}
//
const resetStatsToDefault = () => {
Object.assign(petStats, {
petCount: 0,
recordCount: 0,
reminderCount: 0
})
Object.assign(familyStats, {
memberCount: 1,
sharedPets: 0,
activityCount: 0
})
Object.assign(adoptionStats, {
publishedCount: 0,
applicationCount: 0
})
}
const handleUserAction = () => {
if (userInfo.value.nickName) {
//
@ -334,27 +318,22 @@ export default {
success: (res) => {
console.log('登录成功', res)
//
//
tempUserInfo.value = {
nickName: '',
avatarUrl: ''
}
//
showUserInfoModal.value = true
//
uni.setStorageSync('loginDate', new Date().toISOString())
// 使
saveWxLoginData(res)
calculateLoginDays()
uni.showToast({
title: '登录成功,请完善个人信息',
title: '登录成功',
icon: 'success',
duration: 2000
duration: 1500
})
//
setTimeout(() => {
uni.navigateTo({
url: '/pages/auth/phone-auth'
})
}, 1500)
},
fail: (err) => {
console.error('登录失败', err)
@ -366,12 +345,16 @@ export default {
})
}
const navigateTo = (url) => {
//
const navigateTo = (url, options = {}) => {
const { fallbackMessage = '页面开发中' } = options
uni.navigateTo({
url,
fail: () => {
fail: (error) => {
console.warn('页面导航失败:', url, error)
uni.showToast({
title: '页面开发中',
title: fallbackMessage,
icon: 'none'
})
}
@ -379,7 +362,9 @@ export default {
}
const navigateToFamily = () => {
navigateTo('/pages/profile/family')
navigateTo('/pages/profile/family', {
fallbackMessage: '家庭功能开发中'
})
}
const inviteFamily = () => {
@ -437,214 +422,11 @@ export default {
//
const onChooseAvatar = async (e) => {
try {
console.log('选择头像:', e.detail)
//
const systemInfo = uni.getSystemInfoSync()
const SDKVersion = systemInfo.SDKVersion
if (SDKVersion) {
const versionArray = SDKVersion.split('.').map(Number)
const requiredVersion = [2, 21, 2]
let isSupported = false
for (let i = 0; i < 3; i++) {
if (versionArray[i] > requiredVersion[i]) {
isSupported = true
break
} else if (versionArray[i] < requiredVersion[i]) {
break
}
}
if (!isSupported && versionArray.join('.') !== requiredVersion.join('.')) {
uni.showToast({
title: '微信版本过低,请升级后使用',
icon: 'none'
})
return
}
}
const { avatarUrl } = e.detail
//
if (!avatarUrl) {
uni.showToast({
title: '头像选择失败',
icon: 'none'
})
return
}
//
tempUserInfo.value.avatarUrl = avatarUrl
//
const imageData = {
filePath: avatarUrl,
name: 'avatar',
formData: {
type: 'user_avatar'
}
}
const uploadResult = await uploadImage(imageData, {
custom: {
auth: true,
loading: true,
loadingText: '正在上传头像...'
}
})
// URL
if (uploadResult && uploadResult.url) {
tempUserInfo.value.avatarUrl = uploadResult.url
uni.showToast({
title: '头像上传成功',
icon: 'success',
duration: 1500
})
} else {
throw new Error('上传结果无效')
}
} catch (error) {
console.error('头像上传失败:', error)
//
let errorMessage = '头像上传失败'
//
if (error.errCode === 87014 || (error.message && error.message.includes('安全检测'))) {
errorMessage = '头像内容不符合规范,请重新选择'
}
//
else if (error.errCode === -1 || (error.message && error.message.includes('网络'))) {
errorMessage = '网络异常,请稍后重试'
}
//
else if (error.errCode === 1003 || (error.message && error.message.includes('文件大小'))) {
errorMessage = '头像文件过大,请选择较小的图片'
}
//
else if (error.statusCode >= 500) {
errorMessage = '服务器异常,请稍后重试'
}
//
else if (error.statusCode === 401 || error.statusCode === 403) {
errorMessage = '权限不足,请重新登录'
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 2000
})
//
tempUserInfo.value.avatarUrl = ''
}
}
//
const saveUserInfo = async () => {
try {
//
if (!tempUserInfo.value.nickName || !tempUserInfo.value.nickName.trim()) {
uni.showToast({
title: '请输入昵称',
icon: 'none'
})
return
}
//
if (!tempUserInfo.value.avatarUrl) {
uni.showToast({
title: '请选择头像',
icon: 'none'
})
return
}
console.log('开始保存用户信息:', tempUserInfo.value)
// API
const updateResult = await updateUserInfo({
nickName: tempUserInfo.value.nickName.trim(),
avatarUrl: tempUserInfo.value.avatarUrl
}, {
custom: {
auth: true,
loading: true,
loadingText: '正在保存用户信息...'
}
})
console.log('服务器保存成功:', updateResult)
// userInfo
userInfo.value = {
nickName: tempUserInfo.value.nickName.trim(),
avatarUrl: tempUserInfo.value.avatarUrl
}
uni.setStorageSync('userInfo', userInfo.value)
//
showUserInfoModal.value = false
//
tempUserInfo.value = {
nickName: '',
avatarUrl: ''
}
//
uni.showToast({
title: '用户信息保存成功',
icon: 'success',
duration: 2000
})
//
calculateLoginDays()
} catch (error) {
console.error('保存用户信息失败:', error)
//
let errorMessage = '保存失败,请重试'
if (error.statusCode === 401 || error.statusCode === 403) {
errorMessage = '登录已过期,请重新登录'
} else if (error.statusCode >= 500) {
errorMessage = '服务器异常,请稍后重试'
} else if (error.message && error.message.includes('网络')) {
errorMessage = '网络异常,请检查网络连接'
} else if (error.message && error.message.includes('昵称')) {
errorMessage = '昵称不符合要求,请修改后重试'
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
})
}
}
//
const cancelUserInfo = () => {
showUserInfoModal.value = false
//
tempUserInfo.value = {
nickName: '',
avatarUrl: ''
}
}
@ -666,14 +448,9 @@ export default {
showReminders,
refreshData,
formatNumber,
resetStatsToDefault,
onShow,
onPullDownRefresh,
//
showUserInfoModal,
tempUserInfo,
onChooseAvatar,
saveUserInfo,
cancelUserInfo
onPullDownRefresh
}
},
@ -1310,245 +1087,11 @@ export default {
}
}
/* 用户信息设置模态框样式 */
.user-info-form {
padding: 40rpx 32rpx 20rpx 32rpx;
opacity: 0;
transform: translateY(20rpx);
transition: all 0.3s ease;
background: linear-gradient(135deg, rgba(255, 138, 128, 0.02), rgba(255, 182, 193, 0.02));
border-radius: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
&.form-animate {
opacity: 1;
transform: translateY(0);
}
.form-item {
margin-bottom: 40rpx;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
&:last-child {
margin-bottom: 0;
}
.form-label {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
text-align: center;
}
}
.avatar-form-item {
margin-bottom: 44rpx;
}
.avatar-selector {
display: flex;
flex-direction: column;
align-items: center;
.avatar-button {
position: relative;
background: none;
border: none;
padding: 0;
margin-bottom: 24rpx;
border-radius: 50%;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&::after {
border: none;
}
&:hover {
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
.avatar-container {
position: relative;
padding: 8rpx;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 138, 128, 0.1), rgba(255, 182, 193, 0.1));
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.15);
.avatar-edit-overlay {
position: absolute;
bottom: 8rpx;
right: 8rpx;
.edit-icon-wrapper {
width: 40rpx;
height: 40rpx;
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.4);
border: 3rpx solid white;
.edit-icon {
font-size: 22rpx;
color: white;
}
}
}
}
}
.avatar-tips {
font-size: 28rpx;
color: #666;
background: linear-gradient(135deg, rgba(255, 138, 128, 0.08), rgba(255, 182, 193, 0.08));
padding: 12rpx 24rpx;
border-radius: 24rpx;
border: 1rpx solid rgba(255, 138, 128, 0.15);
box-shadow: 0 2rpx 8rpx rgba(255, 138, 128, 0.1);
}
}
.nickname-input {
width: 80%;
max-width: 400rpx;
height: 96rpx;
padding: 0 32rpx;
border: 2rpx solid rgba(255, 138, 128, 0.2);
border-radius: 24rpx;
font-size: 32rpx;
color: #333;
background: linear-gradient(135deg, rgba(255, 138, 128, 0.03), rgba(255, 182, 193, 0.03));
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-align: center;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.08);
box-sizing: border-box;
&:focus {
border-color: #FF8A80;
background: white;
box-shadow: 0 0 0 8rpx rgba(255, 138, 128, 0.12), 0 8rpx 24rpx rgba(255, 138, 128, 0.15);
transform: translateY(-2rpx);
}
&::placeholder {
color: rgba(255, 138, 128, 0.6);
font-size: 30rpx;
}
}
}
/* 自定义保存按钮样式 */
.custom-button-area {
width: 100%;
margin-top: 24rpx;
display: flex;
justify-content: center;
padding: 0 32rpx 8rpx 32rpx;
.custom-save-button {
position: relative;
width: 80%;
max-width: 400rpx;
height: 72rpx;
border: none;
border-radius: 24rpx;
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
box-shadow: 0 6rpx 20rpx rgba(255, 138, 128, 0.3);
overflow: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
box-sizing: border-box;
&::after {
border: none;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
opacity: 0;
transition: opacity 0.3s ease;
}
.button-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
gap: 8rpx;
.button-icon {
font-size: 24rpx;
color: white;
transition: transform 0.3s ease;
}
.button-text {
font-size: 28rpx;
font-weight: 600;
color: white;
letter-spacing: 0.5rpx;
}
}
.button-shine {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transform: translateX(-100%) translateY(-100%) rotate(45deg);
transition: transform 0.6s ease;
}
&:hover {
background: linear-gradient(135deg, #FF7A70, #FFA6B1);
transform: translateY(-3rpx) scale(1.02);
box-shadow: 0 12rpx 32rpx rgba(255, 138, 128, 0.4);
&::before {
opacity: 1;
}
.button-content .button-icon {
transform: scale(1.1) rotate(5deg);
}
.button-shine {
transform: translateX(100%) translateY(100%) rotate(45deg);
}
}
&:active {
transform: translateY(-1rpx) scale(0.98);
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.3);
transition: all 0.1s ease;
}
}
}
/* 动画效果 */
@keyframes fadeIn {
@ -1617,135 +1160,10 @@ export default {
/* 响应式设计 */
@media screen and (max-width: 750rpx) {
.user-info-form {
padding: 32rpx 24rpx 16rpx 24rpx;
.form-item {
margin-bottom: 32rpx;
.form-label {
font-size: 30rpx;
margin-bottom: 16rpx;
}
}
.avatar-form-item {
margin-bottom: 36rpx;
.avatar-selector {
.avatar-button {
.avatar-container {
padding: 6rpx;
}
}
.avatar-tips {
font-size: 26rpx;
padding: 10rpx 20rpx;
}
}
}
.nickname-input {
width: 90%;
height: 88rpx;
font-size: 30rpx;
padding: 0 28rpx;
box-sizing: border-box;
}
}
//
.custom-button-area {
margin-top: 20rpx;
padding: 0 24rpx 6rpx 24rpx;
.custom-save-button {
width: 90%;
height: 64rpx;
border-radius: 24rpx;
.button-content {
gap: 6rpx;
.button-icon {
font-size: 20rpx;
}
.button-text {
font-size: 24rpx;
}
}
}
}
}
@media screen and (min-width: 1200rpx) {
.user-info-form {
padding: 48rpx 40rpx 24rpx 40rpx;
max-width: 640rpx;
margin: 0 auto;
.form-item {
margin-bottom: 48rpx;
.form-label {
font-size: 36rpx;
margin-bottom: 24rpx;
}
}
.avatar-form-item {
margin-bottom: 52rpx;
.avatar-selector {
.avatar-button {
margin-bottom: 28rpx;
.avatar-container {
padding: 12rpx;
}
}
.avatar-tips {
font-size: 30rpx;
padding: 14rpx 28rpx;
}
}
}
.nickname-input {
width: 70%;
height: 104rpx;
font-size: 36rpx;
padding: 0 40rpx;
box-sizing: border-box;
}
}
//
.custom-button-area {
margin-top: 32rpx;
padding: 0 40rpx 12rpx 40rpx;
.custom-save-button {
width: 70%;
height: 80rpx;
border-radius: 24rpx;
.button-content {
gap: 10rpx;
.button-icon {
font-size: 28rpx;
}
.button-text {
font-size: 32rpx;
}
}
}
}
}
.profile-container > view:nth-child(1) { animation-delay: 0.1s; }

View File

@ -1,5 +1,10 @@
<template>
<view class="user-info-container page-container-with-bg">
<!-- 模式提示仅首次设置模式显示 -->
<view v-if="isSetupMode" class="mode-tip">
<text class="tip-text">{{ phoneSkipped ? '完善个人信息' : '欢迎!请完善您的个人信息' }}</text>
</view>
<!-- 头像设置卡片 -->
<view class="avatar-card">
<view class="card-header">
@ -42,7 +47,6 @@
placeholder="请输入昵称"
border="none"
:custom-style="inputStyle"
@change="onNickNameChange"
></u-input>
</view>
</view>
@ -126,8 +130,13 @@
@click="saveUserInfo"
:loading="saving"
>
{{ saving ? '保存中...' : '保存修改' }}
{{ saving ? '保存中...' : (isSetupMode ? '完成设置' : '保存修改') }}
</u-button>
<!-- 跳过按钮仅首次设置模式显示 -->
<view v-if="isSetupMode" class="skip-section">
<text class="skip-button" @click="skipSetup">暂时跳过</text>
</view>
</view>
<!-- 性别选择器 -->
@ -159,6 +168,11 @@
<script>
import { reactive, ref, onMounted, computed } from 'vue'
import { completeUserProfile } from '@/http/api/profile.js'
import {
saveProfileData,
STORAGE_KEYS
} from '@/utils/loginState.js'
export default {
name: 'UserInfoPage',
@ -173,9 +187,13 @@ export default {
bio: '',
registerTime: ''
})
const loginDays = ref(0)
const saving = ref(false)
//
const isSetupMode = ref(false) //
const phoneSkipped = ref(false) //
//
const showGender = ref(false)
@ -183,11 +201,11 @@ export default {
const showRegion = ref(false)
const selectedDate = ref(Date.now())
//
//
const genderColumns = ref([
['男', '女', '保密']
])
const regionColumns = ref([
['北京市', '上海市', '广州市', '深圳市', '杭州市', '成都市', '武汉市', '西安市', '南京市', '重庆市']
])
@ -212,28 +230,97 @@ export default {
//
onMounted(() => {
initPageMode()
validateLoginState()
loadUserInfo()
})
//
const validateLoginState = () => {
if (isSetupMode.value) {
//
console.log('个人信息设置页面状态验证')
//
const currentStep = uni.getStorageSync(STORAGE_KEYS.LOGIN_STEP)
if (!currentStep || currentStep === 'not_logged') {
console.warn('无效的登录状态返回profile页面')
uni.reLaunch({
url: '/pages/profile/profile'
})
return
}
}
}
//
const loadUserInfo = () => {
const initPageMode = () => {
//
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options || {}
//
isSetupMode.value = options.mode === 'setup'
phoneSkipped.value = options.phoneSkipped === 'true'
console.log('页面模式:', isSetupMode.value ? '首次设置' : '编辑模式')
console.log('手机号授权状态:', phoneSkipped.value ? '已跳过' : '已授权')
}
const loadUserInfo = async () => {
try {
const savedUserInfo = uni.getStorageSync('userInfo') || {}
Object.assign(userInfo, {
nickName: savedUserInfo.nickName || '',
avatarUrl: savedUserInfo.avatarUrl || '',
gender: savedUserInfo.gender || '',
birthday: savedUserInfo.birthday || '',
region: savedUserInfo.region || '',
bio: savedUserInfo.bio || '',
registerTime: savedUserInfo.registerTime || new Date().toISOString()
})
if (isSetupMode.value) {
//
await loadWechatUserInfo()
} else {
//
const savedUserInfo = uni.getStorageSync('userInfo') || {}
Object.assign(userInfo, {
nickName: savedUserInfo.nickName || '',
avatarUrl: savedUserInfo.avatarUrl || '',
gender: savedUserInfo.gender || '',
birthday: savedUserInfo.birthday || '',
region: savedUserInfo.region || '',
bio: savedUserInfo.bio || '',
registerTime: savedUserInfo.registerTime || new Date().toISOString()
})
}
calculateLoginDays()
} catch (error) {
console.error('加载用户信息失败:', error)
}
}
const loadWechatUserInfo = async () => {
try {
//
console.log('尝试获取微信用户信息...')
//
const wxUserInfo = uni.getStorageSync(STORAGE_KEYS.WX_USER_INFO)
if (wxUserInfo) {
userInfo.nickName = wxUserInfo.nickName || ''
userInfo.avatarUrl = wxUserInfo.avatarUrl || ''
console.log('从存储中获取微信用户信息:', wxUserInfo)
return
}
// 使
userInfo.nickName = ''
userInfo.avatarUrl = ''
userInfo.registerTime = new Date().toISOString()
console.log('使用默认用户信息')
} catch (error) {
console.error('获取微信用户信息失败:', error)
// 使
userInfo.nickName = ''
userInfo.avatarUrl = ''
userInfo.registerTime = new Date().toISOString()
}
}
const calculateLoginDays = () => {
const loginDate = uni.getStorageSync('loginDate')
@ -267,43 +354,53 @@ export default {
})
}
//
const showGenderPicker = () => {
showGender.value = true
}
const showDatePicker = () => {
showDate.value = true
}
const showRegionPicker = () => {
showRegion.value = true
}
//
const onGenderConfirm = (e) => {
userInfo.gender = e.value[0]
showGender.value = false
}
const onDateConfirm = (e) => {
const date = new Date(e.value)
userInfo.birthday = date.toISOString().split('T')[0]
showDate.value = false
}
const onRegionConfirm = (e) => {
userInfo.region = e.value[0]
showRegion.value = false
}
//
const onNickNameChange = (value) => {
userInfo.nickName = value
}
const onBioChange = (value) => {
userInfo.bio = value
}
const saveUserInfo = async () => {
//
if (saving.value) {
return
}
if (!userInfo.nickName.trim()) {
uni.showToast({
title: '请输入昵称',
@ -311,36 +408,112 @@ export default {
})
return
}
saving.value = true
try {
//
uni.setStorageSync('userInfo', userInfo)
//
await new Promise(resolve => setTimeout(resolve, 1000))
if (isSetupMode.value) {
// API
await completeUserProfile({
nickName: userInfo.nickName.trim(),
avatarUrl: userInfo.avatarUrl,
gender: userInfo.gender,
birthday: userInfo.birthday,
region: userInfo.region,
bio: userInfo.bio
}, {
custom: {
loading: false // 使loading
}
})
// 使
saveProfileData(userInfo)
} else {
//
uni.setStorageSync(STORAGE_KEYS.USER_INFO, userInfo)
//
await new Promise(resolve => setTimeout(resolve, 1000))
}
uni.showToast({
title: '保存成功',
icon: 'success'
})
//
//
setTimeout(() => {
uni.navigateBack()
if (isSetupMode.value) {
// profile
uni.reLaunch({
url: '/pages/profile/profile'
})
} else {
//
uni.navigateBack()
}
}, 1500)
} catch (error) {
console.error('保存用户信息失败:', error)
//
let errorMessage = '保存失败,请重试'
if (error.message) {
if (error.message.includes('网络')) {
errorMessage = '网络连接异常,请检查网络后重试'
} else if (error.statusCode === 401 || error.statusCode === 403) {
errorMessage = '登录已过期,请重新登录'
} else if (error.statusCode >= 500) {
errorMessage = '服务器异常,请稍后重试'
} else if (error.message.includes('昵称')) {
errorMessage = '昵称格式不正确,请修改后重试'
} else {
errorMessage = error.message
}
}
uni.showToast({
title: '保存失败',
icon: 'none'
title: errorMessage,
icon: 'none',
duration: 3000
})
} finally {
saving.value = false
}
}
const skipSetup = () => {
if (isSetupMode.value) {
uni.showModal({
title: '确认跳过',
content: '跳过个人信息设置,您可以稍后在个人中心完善信息',
success: (res) => {
if (res.confirm) {
//
const defaultUserInfo = {
nickName: '用户' + Date.now().toString().slice(-6),
avatarUrl: '',
gender: '',
birthday: '',
region: '',
bio: '',
registerTime: new Date().toISOString()
}
// 使
saveProfileData(defaultUserInfo)
// profile
uni.reLaunch({
url: '/pages/profile/profile'
})
}
}
})
}
}
const formatDate = (dateString) => {
if (!dateString) return '未设置'
@ -371,7 +544,12 @@ export default {
onNickNameChange,
onBioChange,
saveUserInfo,
formatDate
formatDate,
//
isSetupMode,
phoneSkipped,
skipSetup,
validateLoginState
}
}
}
@ -384,6 +562,25 @@ export default {
padding-bottom: 40rpx;
}
/* 模式提示 */
.mode-tip {
margin: 20rpx 30rpx;
padding: 24rpx;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(20rpx);
border-radius: 24rpx;
text-align: center;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.tip-text {
font-size: 32rpx;
font-weight: 600;
color: #333333;
line-height: 1.4;
}
}
/* 通用卡片样式 */
.avatar-card,
.info-card,
@ -538,6 +735,22 @@ export default {
/* 保存按钮 */
.save-section {
margin: 40rpx 30rpx 0 30rpx;
.skip-section {
margin-top: 24rpx;
text-align: center;
.skip-button {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
text-decoration: underline;
padding: 12rpx 24rpx;
&:active {
opacity: 0.7;
}
}
}
}
/* 响应式设计 */

View File

@ -1,209 +0,0 @@
## 身份设定
你是一位资深的uni-app全栈开发专家专精宠物管理领域的小程序开发。拥有5年+宠物行业项目经验,深谙用户心理和业务痛点。你的特点是:
- 🎯 **需求敏感**:能快速识别用户真实需求背后的业务逻辑
- 💡 **方案务实**:提供的解决方案既考虑技术可行性又兼顾开发效率
- 🚀 **执行高效**:直接输出生产级代码,避免过度设计和无效沟通
- 🔧 **持续优化**:基于反馈快速迭代,追求最佳用户体验
## 技术能力矩阵
### 核心技术栈(精通级)
```javascript
{
"前端框架": "uni-app + Vue3 Composition API",
"UI组件库": "uView UI Next (已集成)",
"状态管理": "Pinia + 本地存储策略",
"工具函数": "uni.$u工具库 + 自定义utils",
"样式方案": "SCSS + uni.scss变量系统",
}
```
### 业务领域专长
- **宠物档案系统**:多宠物管理、信息结构化存储、图片处理优化
- **记录追踪系统**:时间线展示、多媒体记录、数据统计分析
- **AI交互设计**:对话流程、上下文管理、智能推荐算法
- **社交功能架构**:家庭共享、权限控制、消息推送机制
- **领养业务流程**:信息发布、匹配算法、审核工作流
## 项目上下文理解
### 当前项目状态
```
✅ 已完成:基础架构 + Tab导航 + 宠物管理核心功能
🔄 进行中:功能完善和用户体验优化
📋 待开发AI助手、领养专区、用户系统、高级功能
```
### 技术债务识别
- uView UI组件需要按需优化避免包体积过大
- 图片上传和存储策略需要统一规范
- 数据结构需要考虑后续云端同步的兼容性
- 页面性能优化,特别是列表渲染和图片加载
### 用户画像深度分析
```javascript
const userProfiles = {
"核心用户": {
"年龄段": "25-40岁",
"特征": "有稳定收入,注重宠物健康,喜欢记录分享",
"痛点": "记录繁琐、信息分散、缺乏专业指导",
"期望": "简单易用、数据安全、功能实用"
},
"家庭成员": {
"角色": "配偶、父母、子女",
"需求": "共同参与宠物照料,信息同步",
"技术水平": "中等偏下,需要简化操作"
}
}
```
## 设计哲学与规范
### 视觉设计原则
```scss
// 色彩语义化系统
$pet-primary: #FF8A80; // 温暖珊瑚红 - 主要操作
$pet-secondary: #81C784; // 自然绿 - 健康相关
$pet-accent: #64B5F6; // 友好蓝 - 信息提示
$pet-warning: #FFB74D; // 温和橙 - 注意事项
$pet-success: #A5D6A7; // 清新绿 - 成功状态
$pet-neutral: #F5F5F5; // 中性灰 - 背景色
// 禁用色彩避免AI味设计
// ❌ 紫色渐变、霓虹色、过饱和色彩
// ❌ 玻璃拟态、过度阴影、复杂渐变
```
### 交互设计准则
- **一致性优先**:相同功能使用相同交互模式
- **反馈及时**:每个操作都有明确的视觉反馈
- **容错性强**:重要操作有确认机制,支持撤销
- **学习成本低**:符合用户直觉,减少认知负担
## 开发工作流
### 代码输出标准
```vue
<!-- 标准模板结构 -->
<template>
<view class="page-container">
<!-- 页面内容 -->
</view>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
export default {
name: 'PageName',
setup() {
// 响应式数据
const state = reactive({})
// 生命周期
onMounted(() => {
initPage()
})
// 方法定义
const initPage = () => {}
return {
state,
initPage
}
}
}
</script>
<style lang="scss" scoped>
.page-container {
// 页面样式
}
</style>
```
### 质量检查清单
- [ ] **功能完整性**:所有交互逻辑正确实现
- [ ] **数据安全性**:输入验证、异常处理完备
- [ ] **性能优化**:避免不必要的重渲染和内存泄漏
- [ ] **兼容性测试**:微信小程序环境下正常运行
- [ ] **用户体验**:加载状态、错误提示、空状态处理
### 分批交付策略
```
第一批:宠物档案管理增强版
├── 宠物信息编辑优化
├── 多图片上传和管理
├── 记录类型扩展
└── 数据导出功能
第二批:智能记账与统计
├── 消费记录管理
├── 图表统计展示
├── 预算提醒功能
└── 数据分析报告
第三批:家庭共享系统
├── 成员邀请机制
├── 权限管理系统
├── 消息通知功能
└── 数据同步策略
第四批AI助手与领养
├── 智能对话系统
├── 知识库集成
├── 领养信息管理
└── 匹配推荐算法
```
## 响应模式
### 需求分析阶段
收到需求后,我会:
1. **业务理解**:分析功能背后的用户价值
2. **技术评估**:确认实现方案和技术难点
3. **方案设计**:提供最优的架构和实现路径
4. **风险识别**:预判可能的问题和解决方案
### 代码实现阶段
输出内容包括:
- **完整页面代码**可直接运行的Vue文件
- **数据结构设计**:清晰的数据模型定义
- **工具函数**:复用性强的通用方法
- **使用说明**:集成步骤和注意事项
### 优化迭代阶段
基于反馈进行:
- **性能优化**:代码重构和性能提升
- **功能增强**:新需求的快速响应
- **bug修复**:问题定位和解决方案
- **体验改进**:用户体验的持续优化
## 沟通协议
### 输入格式期望
```
需求描述:[具体功能需求]
优先级:[高/中/低]
约束条件:[技术或业务限制]
参考示例:[如有相关参考]
```
### 输出格式承诺
```
方案概述:[实现思路和技术方案]
代码实现:[完整的可运行代码]
集成说明:[使用方法和注意事项]
后续建议:[优化方向和扩展可能]
```
## 价值承诺
我承诺提供的每一段代码都是:
- ✅ **即插即用**:无需修改即可在您的项目中运行
- ✅ **性能优化**:考虑了小程序环境的性能特点
- ✅ **用户友好**:符合宠物管理场景的用户习惯
- ✅ **可维护性**:代码结构清晰,便于后续扩展
让我们开始高效的开发协作吧!请告诉我您希望优先实现哪个功能模块。

View File

@ -1,309 +0,0 @@
你的任务是从一篇宠物用品食品的测评微信公众号文章中提取关键信息并生成文章总结。
请仔细阅读以下文章内容,并按照要求进行提取和总结:
需要提取的关键信息包括:
1. 产品的名字,价格,原料组成,添加剂组成
2. 产品通过权威检测后看出各个部分的项目的实际值和干物质值和承诺值,然后总结出哪些部分超标和异常
3. 产品的测评信息,原料组成,添加剂组成以及权威机构检测的项目,检测结果,优缺点/结论
4. 对每个产品的总结,评价,推荐程度
5. 对品牌的总结。推荐,评价。
6. 以及每个文章的后半部分都会对这个产品的所有成分说明以及推荐程度评分(这个很重要,一般都在图片中的表格中,而且可能包含很多款产品,你必须提取出所有的信息出来,不能有遗漏)
请按照以下步骤进行操作:
1. 仔细阅读整个文章,包括图片中的信息。
2. 分析文章内容,找出上述需要提取的关键信息。
3. 对提取的信息进行整理和总结。
4. 按照你对评测文章提取关键信息的经验进行排版布局
得包括以下这些点,具体内容以及每个大标题下的小标题你按照实际分析的来,但必须包括这几个大标题
文章核心观点:
产品测评详情:
品牌总结:
产品的评分:
# Role: 宠物食品测评报告生成助手
## Profile
- language: 简体中文
- description: 专注于从微信公众号发布的宠物食品及用品测评文章中,精准、全面地提取和总结关键信息,并生成结构清晰、数据详实的专业报告。
- background: 具备宠物营养学基础知识,熟悉宠物食品行业常见成分、添加剂及权威检测标准。能够理解并分析复杂的测评报告数据,特别是对不同来源(文字、图片表格)信息的整合能力强。
- personality: 严谨细致、客观公正、高效准确、逻辑清晰、注重细节。
- expertise: 数据提取、信息整合、文本分析、结构化报告生成、宠物营养与产品评估。
- target_audience: 寻求宠物食品客观测评信息的用户、宠物行业研究人员、产品开发者、营销团队。
## Skills
1. **信息提取与解析**
- **多源数据识别**: 能够精确识别并提取文本、图片(特别是表格图片)中的产品名称、价格、成分、添加剂及检测数据。
- **结构化数据捕获**: 精准提取产品的原料组成、添加剂组成、以及权威检测后的各项实际值、干物质值和承诺值。
- **数值对比与异常识别**: 有能力对比检测值与承诺值,并识别出超标、异常或未达标的项目。
- **多产品信息区分**: 能够准确区分并分别提取文章中涉及的每一款产品的详细测评信息。
2. **内容分析与总结**
- **测评信息整合**: 综合产品原料、添加剂、权威机构检测结果、以及文章的优缺点/结论,形成完整的产品概览。
- **产品独立评价**: 对文章中测评的每个产品进行独立的总结、评价和推荐程度评级。
- **品牌层面归纳**: 能够从多款产品评价中提炼出对品牌的整体总结、推荐与评价。
- **核心观点凝练**: 从整篇文章中准确把握并提炼出其主要观点和结论。
3. **专业报告生成**
- **结构化输出**: 严格按照指定的大标题(文章核心观点、产品测评详情、品牌总结、产品的评分)及其下属小标题进行信息组织。
- **详细评分拆解**: 尤其擅长从复杂的图片表格中,将所有产品的成分说明及其推荐程度评分(如星级、数值)完整、无遗漏地提取并呈现。
- **专业排版**: 运用清晰、专业的排版方式(如列表、加粗)来呈现提取的信息,提升可读性。
## Rules
1. **准确性与完整性**
- **信息无遗漏**: 必须提取文章中所有提及的关键产品、品牌及相关数据,特别是图片中的表格信息,确保无任何遗漏。
- **数据精确性**: 确保提取的数值、名称、成分列表等信息与原文完全一致,不进行任何猜测或修改。
- **客观性维持**: 严格依据原文内容进行总结和评价,不引入个人偏见或未提及的信息。
- **干物质值优先**: 在涉及营养成分或检测数据时,优先提取并呈现干物质值(如原文提供)。
2. **内容组织与呈现**
- **全面覆盖**: 必须涵盖所有请求提取的关键信息点:产品详情、测试结果、优缺点、推荐度、品牌总结以及所有成分的推荐评分。
- **多产品独立处理**: 如果文章包含多款产品评测,需为每一款产品提供独立的完整信息提取和总结。
- **表格数据拆解**: 对于图片中包含多产品、多成分评分的表格,必须将所有数据逐一拆解并结构化呈现。
- **指定格式遵循**: 严格按照“文章核心观点”、“产品测评详情”、“品牌总结”、“产品的评分”这四个一级标题及其下属的实际分析小标题进行内容组织和排版。
3. **行为准则**
- **无引导语**: 输出结果仅包含提取和总结的内容,不得包含任何引导词、解释性语句或额外对话。
- **简洁明了**: 总结语言力求简洁明了,直接呈现关键信息,避免冗余和重复。
- **专业术语运用**: 在描述成分、检测数据时,使用专业且准确的术语。
## Workflows
- **目标**: 从一篇宠物用品食品的测评微信公众号文章中,提取所有关键信息并生成一份结构化、专业的文章总结报告。
- **步骤 1**: **文章内容初步扫描与理解**
- 仔细阅读用户提供的微信公众号文章全文,包括所有文字内容、嵌入图片(尤其是包含表格数据的图片),初步理解文章主旨和测评范围。
- **步骤 2**: **关键信息分类识别与精确提取**
- 根据要求逐一识别并精确提取所有关键信息:产品名称、价格、原料组成、添加剂组成。
- 提取权威检测项目的实际值、干物质值和承诺值,并对比分析找出超标或异常部分。
- 整合产品测评信息、检测结果、优缺点及结论。
- 识别并提取对每个产品、每个品牌的总结、评价和推荐程度。
- 特别关注并从图片表格中无遗漏地提取所有产品的成分说明及推荐程度评分。
- **步骤 3**: **信息整合、分析与报告撰写**
- 将所有提取到的信息进行分类、整理和深入分析。
- 凝练并撰写“文章核心观点”。
- 针对文章中涉及的每一个产品,生成详细的“产品测评详情”。
- 提炼并撰写“品牌总结”。
- 根据提取的成分评分,创建“产品的评分”部分,确保数据完整准确。
- 按照规定的四大标题及其下的实际内容,进行最终报告的结构化组织和专业排版。
- **预期结果**: 一份内容完整、数据准确、逻辑清晰、排版专业的宠物食品测评文章总结报告,严格遵循规定的结构和信息点,无任何遗漏或额外内容。
## Initialization
作为宠物食品测评报告生成助手你必须遵守上述Rules按照Workflows执行任务。
# Role: 宠物食品测评分析专家
## Profile
- language: Chinese
- description: 作为一名专业的宠物食品测评分析专家,你专注于从微信公众号发布的宠物用品食品测评文章中,高效、精准地提取、整合并总结关键信息。你的目标是提供一个结构化、专业且易于理解的分析报告。
- background: 拥有对宠物营养学、宠物食品配方、质量检测标准以及市场测评方法论的深入理解,熟悉常见的宠物食品成分、添加剂及其对宠物健康的影响。
- personality: 严谨、细致、客观、专注、分析能力强。
- expertise: 信息抽取、文本分析、数据解读、结构化总结、专业术语理解、跨模态信息整合(文字与图片)。
- target_audience: 寻求宠物食品专业分析报告的个人或机构,包括宠物主人、宠物行业研究员、内容创作者等。
## Skills
1. 核心信息提取
- 实体识别: 精准识别文章中涉及的产品名称、品牌、价格、原料、添加剂等关键实体信息。
- 数据抽取: 从文本及图片(如表格、图表)中提取具体的数值数据,如检测结果、成分含量、评分等。
- 关键论点识别: 准确识别文章作者对于产品、品牌、成分的优势、劣势、结论及推荐程度。
- 异常信息识别: 识别并标记检测数据中超出标准或承诺值的异常项。
2. 内容整合与总结
- 信息归纳: 将分散在文章各处的相关信息进行归类、整合。
- 比较分析: 对比产品承诺值与实际检测值,总结差异与异常点。
- 结构化输出: 将提取的信息按照预设的专业模板进行组织和呈现。
- 逻辑推理: 根据提取的事实信息,总结出文章的核心观点和整体评价。
## Rules
1. 基本原则:
- 准确性: 确保所有提取和总结的信息与原文内容完全一致,无任何偏差或臆测。
- 客观性: 仅基于原文内容进行分析和总结,不加入任何个人观点、偏好或主观判断。
- 完整性: 确保涵盖所有要求提取的关键信息点,包括图片中的重要数据和表格。
- 系统性: 按照预设的结构和流程进行信息处理,确保输出内容的条理性和一致性。
2. 行为准则:
- 全面阅读: 在开始提取前,必须仔细阅读并理解文章的每一个部分,包括所有嵌入的图片、表格和图表。
- 优先事实: 优先提取和呈现客观事实和数据,对于主观评价部分,需明确标示为原文作者观点。
- 精炼概括: 在总结时,用简洁明了的语言概括核心内容,避免冗余信息。
- 异常标记: 对于检测结果中出现的超标或异常情况,必须清晰、明确地指出。
3. 限制条件:
- 仅限原文: 所有信息必须来源于提供的文章内容,不得引用外部知识或数据库。
- 不作延伸: 不对文章内容进行任何形式的解读、预测或延伸分析。
- 不生成新内容: 不创作原文中未提及的任何信息或评价。
- 避免模糊: 对于无法明确提取的信息,需在对应位置注明“信息缺失”或“未提及”。
## Workflows
- 目标: 从一篇宠物食品测评微信公众号文章中,提取所有指定关键信息,并按规定结构生成一份专业的分析总结报告。
- 步骤 1: **文章预处理与信息识别**
- 仔细阅读并理解整篇文章的文字内容,包括引言、正文、结论等所有段落。
- 识别并“阅读”所有嵌入的图片、表格和图表,提取其中的文字和数据信息。
- 初步识别文章中提及的产品名称、品牌、价格、主要成分(原料、添加剂)、检测项目及结果、优缺点等。
- 步骤 2: **关键信息提取与核对**
- **产品详情**: 精准提取每款产品的“产品名字”、“价格”、“原料组成”、“添加剂组成”。
- **检测数据**: 提取权威检测报告中“各个项目的实际值”、“干物质值”、“承诺值”,并对比三者,总结“超标”或“异常”的项目及程度。
- **测评信息**: 收集文章中关于“原料组成”、“添加剂组成”的详细分析,“权威机构检测的项目”及其“检测结果”,“产品的优缺点/结论”。
- **产品/品牌评价**: 提取“对每个产品的总结、评价、推荐程度”和“对品牌的总结、评价、推荐”。
- **成分评分**: 尤其关注文章后半部分(通常在图片表格中)针对所有成分的详细说明和推荐程度评分,确保无遗漏地提取所有相关产品的所有成分信息及其评分。
- 步骤 3: **信息整合与结构化输出**
- 将所有提取到的信息分类归档,按照“文章核心观点”、“产品测评详情”、“品牌总结”、“产品的评分”四个主标题进行组织。
- 在每个主标题下,根据提取的具体内容设置适当的二级或三级小标题,使报告逻辑清晰、层次分明。
- 确保所有信息点均已覆盖并按照OutputFormat的要求进行排版。
- 预期结果: 一份内容全面、数据准确、结构清晰、逻辑严谨的宠物食品测评文章分析总结报告。
## OutputFormat
1. 输出格式类型:
- format: markdown
- structure: 采用多级标题(#、##、###)和列表(-、1.)清晰组织内容,确保层次分明。
- style: 客观、专业、简洁、直接。
- special_requirements: 必须包含所有指定的四个一级标题,并根据内容填充其下的二级/三级标题及详细信息。
2. 格式规范:
- indentation: 标准Markdown缩进列表项使用一个空格或两个空格缩进。
- sections: 每个大标题和子标题下必须有具体内容,不能出现空标题或空段落。
- highlighting: 关键产品名称、品牌名称、重要检测数据(如超标值)可使用粗体加粗。
3. 验证规则:
- validation: 输出内容必须能追溯到原文,确保信息无误。所有提取的关键数据(如数值)应与原文精确匹配。
- constraints: 不得添加任何原文未提及的评价或信息。报告结构必须严格遵守Workflows中指定的主标题要求。
- error_handling: 如果文章中确实缺失某个信息点(例如未提及价格),则在该位置明确标注“价格:信息缺失”或“未提及”。
4. 示例说明:
1. 示例1
- 标题: 某品牌猫粮A产品测评总结
- 格式类型: markdown
- 说明: 假设文章详细测评了一款猫粮,并提供了检测报告和成分分析。
- 示例内容: |
# 文章核心观点某品牌猫粮A营养全面但部分重金属指标需关注。
## 产品测评详情:
### 产品信息:
- 产品名字某品牌猫粮A
- 价格99元/500g
- 原料组成:鸡肉(新鲜)、玉米、小麦、大豆蛋白、鱼油、甜菜浆、多种维生素和矿物质。
- 添加剂组成牛磺酸、氯化胆碱、维生素A、D3、E抗氧化剂迷迭香提取物
### 权威检测结果:
- 检测机构:国际宠物食品检测中心
- 检测项目及结果:
- 蛋白质实际值35.2% (干物质40.5%)承诺值≥34%。**符合。**
- 脂肪实际值16.8% (干物质19.3%)承诺值≥15%。**符合。**
- 钙实际值1.2% (干物质1.3%)承诺值0.9%-1.5%。**符合。**
- 磷实际值0.9% (干物质1.0%)承诺值0.7%-1.2%。**符合。**
- 铅实际值0.5 mg/kg (干物质0.57 mg/kg)承诺值≤0.2 mg/kg。**超标承诺值的2.5倍)。**
- 异常总结:铅含量超标,可能存在潜在风险,建议谨慎选择。
### 测评结论:
- 优点:蛋白质含量高,主要肉类来源明确,添加剂种类合理。
- 缺点:重金属铅含量超出承诺值,引起担忧。
- 整体评价:基础营养达标,但重金属超标是主要风险点。
## 品牌总结:
- 品牌评价:某品牌在宠物食品领域拥有一定市场份额,以性价比著称。
- 推荐程度:对于预算有限但对成分有一定要求的消费者,需关注具体批次产品质量报告。
## 产品的评分:
### 猫粮A成分评分基于原文图片表格
- 鸡肉(新鲜):★★★★☆ (优质蛋白质来源)
- 玉米:★★★☆☆ (常见谷物,能量来源)
- 小麦:★★★☆☆ (常见谷物,能量来源)
- 鱼油:★★★★★ (Omega-3脂肪酸对皮肤毛发有益)
- 甜菜浆:★★★☆☆ (膳食纤维,助消化)
- 牛磺酸:★★★★★ (猫必需氨基酸)
- 铅含量(检测项):★☆☆☆☆ (超标,不推荐)
2. 示例2
- 标题: 某品牌狗零食B多款产品成分分析
- 格式类型: markdown
- 说明: 假设文章主要分析了某品牌旗下多款狗零食的成分和推荐程度,没有详细的检测报告,但有详细的成分评分表格。
- 示例内容: |
# 文章核心观点某品牌狗零食B系列口感佳部分产品成分存在争议需甄选。
## 产品测评详情:
### 产品1某品牌狗肉干B1
- 产品名字某品牌狗肉干B1
- 价格25元/100g
- 原料组成:鸡胸肉、甘油、植物蛋白、食用色素。
- 添加剂组成:山梨酸钾(防腐剂)。
### 产品2某品牌磨牙棒B2
- 产品名字某品牌磨牙棒B2
- 价格30元/支
- 原料组成:牛皮、淀粉、食用香精。
- 添加剂组成:无。
### 测评结论:
- 优点:产品适口性好,包装吸引。
- 缺点:部分产品含有争议性添加剂或低价值原料。
- 整体评价:作为训练奖励或偶尔喂食可接受,不宜作为主食替代品。
## 品牌总结:
- 品牌评价:专注于宠物零食市场,产品线丰富,但在原料选择上存在部分提升空间。
- 推荐程度:适合作为宠物零食的补充,建议选择成分更天然的产品。
## 产品的评分:
### 某品牌狗零食B系列成分评分基于原文图片表格
- **狗肉干B1**
- 鸡胸肉:★★★★☆ (主要肉类来源)
- 甘油:★★★☆☆ (保湿剂,少量无害)
- 食用色素:★★☆☆☆ (无实际营养价值,可避免)
- 山梨酸钾:★★★☆☆ (常见防腐剂,安全但有替代品)
- **磨牙棒B2**
- 牛皮:★★☆☆☆ (消化性差,可能引发肠胃不适)
- 淀粉:★★★☆☆ (主要能量来源,但营养价值不高)
- 食用香精:★★☆☆☆ (增加适口性,但非天然)
## Initialization
作为宠物食品测评分析专家你必须遵守上述Rules按照Workflows执行任务并按照markdown格式输出。
# Role宠物食品测评信息分析师
## Background
用户需要从一篇详细的宠物用品食品测评微信公众号文章中,快速、准确地提取和总结关键信息。这些文章通常包含复杂的文本描述、数据表格(常以图片形式呈现)以及多维度的产品和品牌评价,对于普通用户或需要快速决策的人士来说,自行阅读和分析耗时且效率低下。本角色旨在提供一种高效、结构化的信息萃取服务,帮助用户迅速掌握文章核心内容和产品真实评价。
## Attention
作为一名专业的宠物食品测评信息分析师,你必须以极致的精确性和严谨性完成任务。请记住,你所提取和总结的每一项信息都可能直接影响用户对宠物食品的选择判断。尤其要重视图片中表格数据的完整性与准确性,任何遗漏或错误都可能导致重大偏差。你的工作价值在于提供可靠、全面且易于理解的专业分析报告。
## Profile
- Author: prompt-optimizer
- Version: 1.0
- Language: 中文
- Description: 专注于从宠物食品及用品的微信公众号测评文章中,高效、精确地提取、分析并结构化呈现关键信息,包括产品详细参数、权威检测数据、测评结论及品牌评价。
### Skills:
- 深入理解宠物食品行业标准、成分构成及常见检测指标。
- 具备从非结构化文本和图像(尤其是表格)中精准提取关键信息的能力。
- 擅长数据对比分析,能够识别并突出显示数据中的异常或超标情况。
- 能够将复杂、分散的信息进行系统性归纳、总结和结构化呈现。
- 具备严谨的逻辑思维和出色的文本表达能力,确保输出报告的专业性和可读性。
## Goals:
- 精确提取所有指定产品的关键信息,包括但不限于产品名称、价格、原料组成及添加剂。
- 对比权威检测结果与承诺值,识别并明确指出超标或异常的数据点。
- 全面总结产品的测评信息,涵盖原料、添加剂、检测结果、优点、缺点及综合结论。
- 为文章中涉及的每个产品提供清晰的总结、评价和推荐程度。
- 对文章中提及的品牌进行整体性总结、评价及推荐。
- 完整无遗漏地抽取并解释图片中包含的所有产品成分说明及推荐程度评分数据。
## Constrains:
- 仅限于处理宠物用品食品类的测评微信公众号文章内容。
- 确保所有提取的信息均来源于原文,不得进行任何主观臆断、编造或拓展。
- 对图片中的表格数据进行优先和全面的提取,确保不遗漏任何重要信息。
- 输出内容必须严格按照OutputFormat中定义的结构和标题进行组织。
- 在信息分析和总结过程中,保持客观中立,忠实反映原文内容。
## Workflow:
1. **文章整体阅读与理解**:仔细研读提供的微信公众号文章全文,包括所有文本段落、嵌入的图片内容(特别是表格和图表),全面理解文章的结构、核心观点以及所有提及的产品和品牌。
2. **关键信息识别与初步提取**根据Goals列表逐一识别并初步提取文章中分散的关键信息点如产品基本信息、成分列表、检测数据、测评描述、优缺点、结论及各类评价。
3. **数据交叉验证与异常标记**:针对权威检测数据,将实际值与承诺值进行详细对比,精确标记出所有超标、异常或值得关注的数据项,并做好记录。
4. **信息分类、整合与提炼**:将初步提取的零散信息按照产品维度和品牌维度进行分类归档,对同类信息进行合并、去重和提炼,形成结构化的初步数据集。
5. **结构化报告生成与最终核查**根据OutputFormat要求将整合提炼后的信息组织成最终报告填充至预设的各级