init: 毛孩子计划 小程序 + Go 后端 + 内嵌后台

- pets-fe: 微信原生小程序(首页/计划/记录/报告/社区/引导),
  服务端驱动、无假数据;弹层改用 scroll-view,打开时隐藏自定义 tabBar
- pets-be: Gin + GORM(MySQL, sundynix_ 前缀) + MinIO,统一响应/分页,
  微信 code2session 登录,provider-neutral AI(DeepSeek),go:embed React 后台
- 修复:分段选择类型不匹配(字符串 vs 数字)导致选不中

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-03 15:33:31 +08:00
commit 609f7d06cf
180 changed files with 15259 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
const store = require('../../utils/store.js');
const api = require('../../utils/api.js');
const GENDERS = ['男孩', '女孩', '不确定'];
function ageFromBirthday(bdayStr) {
if (!bdayStr) return '';
const b = new Date(bdayStr);
const now = new Date();
let months = (now.getFullYear() - b.getFullYear()) * 12 + (now.getMonth() - b.getMonth());
if (months < 0) months = 0;
return months < 24 ? months + '个月' : Math.floor(months / 12) + '岁';
}
Page({
data: {
step: 0,
petType: '猫猫',
petEmoji: '🐱',
petName: '',
birthday: '',
genderIdx: 0,
weight: '',
stage: '幼年期',
goals: [
{ label: '不知道每天该做什么', on: true },
{ label: '疫苗驱虫怕忘记', on: true },
{ label: '想记录体重和健康', on: false },
{ label: '担心生病不会判断', on: false },
{ label: '想控制养宠开销', on: false },
{ label: '想生成成长报告', on: false },
],
submitting: false,
},
onLoad() {
// 已有宠物则直接进首页
store
.ready()
.then(() => {
if (store.getPets().length > 0) {
wx.switchTab({ url: '/pages/home/home' });
}
})
.catch(() => {});
},
nextStep() {
this.setData({ step: Math.min(this.data.step + 1, 5) });
},
choosePet(e) {
const { type, emoji } = e.currentTarget.dataset;
this.setData({ petType: type, petEmoji: emoji });
},
onName(e) { this.setData({ petName: e.detail.value }); },
onWeight(e) { this.setData({ weight: e.detail.value }); },
onBirthday(e) { this.setData({ birthday: e.detail.value }); },
selectGender(e) { this.setData({ genderIdx: Number(e.currentTarget.dataset.index) }); },
// 先随便看看:不建数据,直接进首页浏览(无宠物时首页显示空态)
skipBrowse() { wx.switchTab({ url: '/pages/home/home' }); },
savePetNext() {
if (!(this.data.petName || '').trim()) {
wx.showToast({ title: '给它起个名字吧', icon: 'none' });
return;
}
this.nextStep();
},
chooseStage(e) {
this.setData({ stage: e.currentTarget.dataset.stage });
},
toggleGoal(e) {
const i = e.currentTarget.dataset.index;
this.setData({ [`goals[${i}].on`]: !this.data.goals[i].on });
},
async submit() {
if (this.data.submitting) return;
this.setData({ submitting: true });
wx.showLoading({ title: '生成计划中...' });
const body = {
name: (this.data.petName || '').trim(),
emoji: this.data.petEmoji,
type: this.data.petType,
gender: GENDERS[this.data.genderIdx],
birthday: this.data.birthday,
weight: this.data.weight,
stage: this.data.stage,
age: ageFromBirthday(this.data.birthday),
goals: this.data.goals.filter((g) => g.on).map((g) => g.label),
};
try {
await store.ready();
await api.onboarding(body);
await store.loadPets();
wx.hideLoading();
wx.switchTab({ url: '/pages/home/home' });
} catch (e) {
wx.hideLoading();
wx.showToast({ title: e.message || '创建失败', icon: 'none' });
this.setData({ submitting: false });
}
},
finishOnboard() { this.submit(); },
});