📅 最后更新:2026-03-26
📦 版本:1.0.0
📍 项目路径:D:\Development\wechatapp\Pet reptile
📋 目录
项目概述
产品定位
一款专为爬宠玩家设计的轻量级、打卡式饲养记录工具。
核心价值
- ✅ 解决玩家忘记喂食/换垫材时间的痛点
- ✅ 动态顺延日程(实际打卡日 + 频率 = 下次计划)
- ✅ 体重折线图可视化成长曲线
- ✅ 科学饲养反馈和养成成就感
运行环境
- 微信小程序(iOS/Android 双端微信)
- 微信云开发(免服务器部署)
技术架构
┌─────────────────────────────────────────────────────┐
│ 微信小程序客户端 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 今日 │ │ 爱宠 │ │ 详情 │ │ 我的 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 微信云开发平台 │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ 云数据库 │ │ 云存储 │ │ 云函数 │ │
│ │ (MongoDB) │ │ (文件存储) │ │ (Node.js) │ │
│ └──────────────┘ └──────────────┘ └───────────┘ │
└─────────────────────────────────────────────────────┘
技术栈
| 层级 | 技术 | 说明 |
|---|---|---|
| 前端 | WXML/WXSS/JS | 微信小程序原生框架 |
| 图表 | Canvas | 原生 Canvas 绘制体重折线图 |
| 后端 | 微信云开发 | 免服务器部署 |
| 数据库 | 云数据库 | 类 MongoDB NoSQL |
| 存储 | 云存储 | 宠物头像等文件 |
| 鉴权 | 云函数 | 获取用户 openid |
目录结构
Pet reptile/
│
├── app.js # 全局入口(云开发初始化)
├── app.json # 全局配置(页面路由、TabBar)
├── app.wxss # 全局样式(主题色、通用类)
├── project.config.json # 项目配置(IDE 用)
├── sitemap.json # 搜索索引配置
│
├── pages/ # 页面目录
│ ├── index/ # 首页(今日待办)
│ │ ├── index.wxml
│ │ ├── index.wxss
│ │ ├── index.js
│ │ └── index.json
│ │
│ ├── pets/ # 宠物列表页
│ │ ├── pets.wxml
│ │ ├── pets.wxss
│ │ ├── pets.js
│ │ └── pets.json
│ │
│ ├── pet-detail/ # 宠物详情页(图表)
│ │ ├── pet-detail.wxml
│ │ ├── pet-detail.wxss
│ │ ├── pet-detail.js
│ │ └── pet-detail.json
│ │
│ ├── add-pet/ # 添加/编辑宠物
│ │ ├── add-pet.wxml
│ │ ├── add-pet.wxss
│ │ ├── add-pet.js
│ │ └── add-pet.json
│ │
│ ├── weight-record/ # 体重/打卡记录
│ │ ├── weight-record.wxml
│ │ ├── weight-record.wxss
│ │ ├── weight-record.js
│ │ └── weight-record.json
│ │
│ └── mine/ # 我的页面
│ ├── mine.wxml
│ ├── mine.wxss
│ ├── mine.js
│ └── mine.json
│
├── components/ # 自定义组件(预留)
│ ├── pet-card/
│ └── feed-modal/
│
├── cloudfunctions/ # 云函数目录
│ └── getOpenid/ # 获取 openid
│ ├── index.js
│ ├── package.json
│ └── config.json
│
├── utils/ # 工具函数
│ └── util.js
│
└── assets/ # 静态资源
└── images/ # 图片资源
├── default-pet.png # 默认宠物头像
├── default-avatar.png # 默认用户头像
└── *.png # TabBar 图标
数据库设计
1. 宠物档案表 (pet_info)
| 字段名 | 类型 | 说明 | 备注 |
|---|---|---|---|
_id | String | 宠物唯一 ID | 主键(自动生成) |
user_openid | String | 用户 openid | 关联具体用户 |
name | String | 宠物昵称 | 例如:小黑 |
species | String | 品种 | 例如:豹纹守宫 |
avatar | String | 头像文件 ID | 云存储 fileID |
arrivalDate | String | 到家日期 | YYYY-MM-DD |
initialWeight | Number | 初始体重 (g) | |
feed_interval | Number | 喂食频率 (天) | 例如:3 |
sub_interval | Number | 垫材更换频率 (天) | 例如:15 |
next_feed_date | String | 下次计划喂食日期 | 🌟首页判断核心 |
next_sub_date | String | 下次换垫材日期 | 🌟首页判断核心 |
created_at | Date | 创建时间 | 服务器时间 |
2. 喂食记录表 (feed_logs)
| 字段名 | 类型 | 说明 | 备注 |
|---|---|---|---|
_id | String | 记录唯一 ID | 主键 |
pet_id | String | 宠物 ID | 外键 |
feed_date | String | 实际打卡日期 | YYYY-MM-DD |
food_type | String | 食物种类 | 杜比亚/乳鼠等 |
amount | String | 喂食量/备注 | 选填 |
created_at | Date | 记录生成时间 |
3. 体重记录表 (weight_logs)
| 字段名 | 类型 | 说明 | 备注 |
|---|---|---|---|
_id | String | 记录唯一 ID | 主键 |
pet_id | String | 宠物 ID | 外键 |
weight | Number | 体重数值 (g) | |
record_date | String | 测量日期 | YYYY-MM-DD |
created_at | Date | 记录录入时间 |
4. 垫材更换记录表 (substrate_logs)
| 字段名 | 类型 | 说明 | 备注 |
|---|---|---|---|
_id | String | 记录唯一 ID | 主键 |
pet_id | String | 宠物 ID | 外键 |
change_date | String | 更换日期 | YYYY-MM-DD |
sub_type | String | 垫材种类 | 厨房纸/爬沙等 |
created_at | Date | 记录生成时间 |
核心功能说明
3.1 动态喂食打卡系统 ⚠️
核心逻辑
下次计划日期 = 上一次实际打卡日期 + 喂食频率 (N 天)
场景示例
假设设定每 3 天喂一次:
- 原计划 3 号喂 → 用户忘记
- 实际 4 号才打卡 → 系统自动计算
4 + 3 = 7 - 下次计划更新为 7 号(而非 6 号)
代码实现
// pages/index/index.js - 确认打卡
async confirmFeed() {
const { currentPetId, feedDate } = this.data;
// 1. 添加喂食记录
await this.addFeedRecord(currentPetId, feedDate, foodType, amount);
// 2. 获取宠物信息
const pet = await this.getPet(currentPetId);
// 3. 计算下次日期(核心逻辑)
const nextDate = app.dateAdd(feedDate, pet.feed_interval);
// 4. 更新宠物档案
await this.updatePet(currentPetId, { next_feed_date: nextDate });
}
3.2 首页状态判断
| 状态 | 条件 | 显示文案 | 样式 |
|---|---|---|---|
| 正常待办 | next_date === today | “今天需要喂食啦” | 橙色警告 |
| 逾期提醒 | next_date < today | “已逾期 X 天未喂食” | 红色警示 |
| 清闲状态 | next_date > today | “距离下次还有 X 天” | 绿色正常 |
3.3 体重折线图
技术实现
- 使用原生
Canvas绘制(无需第三方库) - 最多显示最近 10 条记录
- 平滑曲线 + 渐变填充
绘制流程
1. 获取 Canvas 节点 → 2. 设置 DPR 缩放 → 3. 计算坐标映射
→ 4. 绘制网格线 → 5. 绘制折线 → 6. 绘制渐变填充
→ 7. 绘制数据点 → 8. 绘制坐标标签
3.4 防误触机制
撤销功能(待实现)
- 打卡后 10 分钟内 可撤销
- 或 当天内 可删除
- 删除后日程状态回滚
开发指南
环境准备
- 安装微信开发者工具
- 下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
- 选择稳定版(Stable Build)
- 导入项目
- 打开开发者工具
- 导入项目 → 选择
D:\Development\wechatapp\Pet reptile - 填入自己的 AppID(或使用测试号)
- 开通云开发
- 工具栏 → 云开发 → 开通
- 创建环境(免费版即可)
- 复制环境 ID 到
app.js
配置修改
app.js – 云环境 ID
wx.cloud.init({
env: 'your-env-id', // 替换为你的云环境 ID
traceUser: true
});
project.config.json – AppID
{
"appid": "your-appid", // 替换为你的小程序 AppID
"projectname": "pet-reptile"
}
云函数部署
- 右键
cloudfunctions/getOpenid目录 - 选择 上传并部署:云端安装依赖
- 等待部署完成(状态变为 ✔)
数据库初始化
在云开发控制台创建以下集合:
pet_infofeed_logsweight_logssubstrate_logs
设置索引
// pet_info
{ user_openid: 1 }
{ next_feed_date: 1 }
{ next_sub_date: 1 }
// feed_logs
{ pet_id: 1, feed_date: -1 }
// weight_logs
{ pet_id: 1, record_date: -1 }
// substrate_logs
{ pet_id: 1, change_date: -1 }
调试技巧
1. 模拟数据(开发测试)
// pages/index/index.js - getPets()
if (!wx.cloud) {
// 模拟数据
resolve({
data: [
{
_id: 'test1',
name: '测试宠物',
species: '豹纹守宫',
next_feed_date: '2026-03-26',
next_sub_date: '2026-04-01'
}
]
});
return;
}
2. 清除缓存
工具栏 → 清缓存 → 清除全部缓存
3. 真机预览
工具栏 → 预览 → 扫码在手机上查看
部署流程
1. 代码上传
- 微信开发者工具 → 右上角 上传
- 填写版本号和备注
- 上传成功后在 版本管理 查看
2. 提交审核
- 版本管理 → 开发版本 → 提交审核
- 填写审核信息:
- 功能介绍:爬宠饲养记录工具
- 测试账号:(如有需要)
- 补充说明:无社交/支付功能
3. 发布上线
- 审核通过后 → 版本管理 → 生产版本
- 点击 发布
- 用户端即可搜索到小程序
4. 备案与认证
小程序备案
- 个人主体:免费,需身份证
- 时间:3-7 个工作日
- 流程:基本信息 → 主营类目 → 腾讯审核 → 管局备案
小程序认证
- 个人:30 元/年
- 企业:300 元/年
- 认证后才可分享和搜索
常见问题
Q1: 云开发初始化失败
A: 检查以下几点:
- 云环境 ID 是否正确
- 是否已开通云开发服务
- 云函数是否已部署
Q2: 获取 openid 失败
A:
- 确认
getOpenid云函数已部署 - 检查云函数权限配置
- 查看云函数日志排查错误
Q3: Canvas 图表不显示
A:
- 检查
canvas-id是否匹配 - 确保在
onReady生命周期获取节点 - 真机调试时注意 DPR 缩放
Q4: 日期计算偏差
A:
- 统一使用
YYYY-MM-DD字符串格式 - 避免时区问题(服务器时间 vs 本地时间)
- 使用工具函数
app.formatDate()
Q5: 数据查询超过 20 条限制
A: 云数据库单次查询最多返回 20 条,需分页:
const db = wx.cloud.database();
const MAX_LIMIT = 20;
// 分批获取
const batch1 = await db.collection('feed_logs')
.skip(0).limit(MAX_LIMIT).get();
const batch2 = await db.collection('feed_logs')
.skip(MAX_LIMIT).limit(MAX_LIMIT).get();
更新日志
v1.0.0 (2026-03-26)
- ✅ 初始版本发布
- ✅ 宠物档案管理(增删改查)
- ✅ 动态喂食打卡系统
- ✅ 垫材更换提醒
- ✅ 体重记录与折线图
- ✅ 首页待办事项
- ✅ 数据导出功能
待开发功能
- 打卡撤销功能(10 分钟内)
- 多宠物批量打卡
- 饲养日记/相册
- 温度/湿度记录
- 数据报表(月/周统计)
- 提醒通知(订阅消息)
- 社区分享功能
📞 技术支持
- 微信开放文档:https://developers.weixin.qq.com/miniprogram/dev/
- 云开发文档:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html
- 问题反馈:项目 Issues
最后更新:2026-03-26 22:50
message:预览 Error: app.json: [“tabBar”][“list”][0][“iconPath”]: “assets/images/today.png” 未找到
[“tabBar”][“list”][0][“selectedIconPath”]: “assets/images/today-active.png” 未找到
[“tabBar”][“list”][1][“iconPath”]: “assets/images/pets.png” 未找到
[“tabBar”][“list”][1][“selectedIconPath”]: “assets/images/pets-active.png” 未找到
[“tabBar”][“list”][2][“iconPath”]: “assets/images/mine.png” 未找到
[“tabBar”][“list”][2][“selectedIconPath”]: “assets/images/mine-active.png” 未找到
File: app.json
appid: wx72f836c54512f38f
openid: o6zAJsxP29Qlt2Wsqx24HLNEctE4
ideVersion: 2.01.2510290
osType: win32-x64
time: 2026-03-26 22:55:13
// pages/pet-detail/pet-detail.js initCanvas 方法内部
// 初始化 Canvas(在 setData 回调中调用,确保 DOM 已渲染)
initCanvas() {
console.log('initCanvas 执行,weightRecords:', this.data.weightRecords.length);
// 如果没有数据,不初始化 Canvas
if (this.data.weightRecords.length === 0) {
console.log('暂无体重记录,跳过 Canvas 初始化');
return;
}
// 直接查询 Canvas,不需要 setTimeout
const query = wx.createSelectorQuery();
query.select('#weightChart')
.fields({ node: true, size: true })
.exec((res) => {
console.log('Canvas 查询结果:', res);
if (res[0] && res[0].node) {
const canvas = res[0].node;
const ctx = canvas.getContext('2d'); // 获取 2D 上下文
const dpr = wx.getSystemInfoSync().pixelRatio;
const width = res[0].width || 300;
const height = res[0].height || 400;
console.log('Canvas 尺寸:', { width, height, dpr });
// 设置画布尺寸 - 这是内部绘图表面的物理分辨率
canvas.width = width * dpr;
canvas.height = height * dpr;
// 👇👇👇 新增核心代码:对上下文进行缩放,适配高分辨率屏幕 👇👇👇
// 所有的绘制坐标和尺寸(包括 ctx.font 的像素值)都会自动乘以 dpr
ctx.scale(dpr, dpr);
// 👆👆👆 新增结束 👆👆👆
this.setData({
chartWidth: width,
chartHeight: height,
canvasInstance: canvas // 存储已缩放上下文的 canvas 实例
}, () => {
console.log('Canvas 实例已设置,宽度:', this.data.chartWidth);
// 测试:直接画一个红色方块
// 注意:此测试直接在 initCanvas 内部获取了 ctx,它也是被缩放过的
const testCtx = canvas.getContext('2d');
testCtx.fillStyle = '#FF0000';
testCtx.fillRect(0, 0, 100, 100); // 这是逻辑像素 (0,0,100,100)
console.log('🔴 测试:绘制了红色方块 (逻辑像素 0,0,100,100)');
// 绘制图表
if (this.data.weightRecords.length > 0) {
console.log('绘制图表,记录数:', this.data.weightRecords.length);
// drawWeightChart 内部将使用已经 scale 过的 ctx,无需任何修改
this.drawWeightChart(this.data.weightRecords);
}
});
} else {
console.error('❌ Canvas 节点未找到,res:', res);
}
});
},