60baff8a2b
## tab 结构
首页 / 记录 / 报告 / 社区 / 我的。计划从 tab 移出变二级页(导航栏补了
返回键),从「我的 → 养护计划」进。
## 消灭 5 处硬编码
tabBar 高亮原来靠每个 tab 页在 onShow 里写死 selected: 0/1/2/3/4,
调一次顺序就得同时改五个文件,漏一个就高亮错位——这次把计划换成我的
正好会踩到。改成 tabBar 组件在 pageLifetimes.show 里按当前 route 自己
匹配,五处硬编码全删。
## profile 拆成两页
原来的 profile 混了两类东西:「我是谁」和「怎么设置」。拆开:
pages/mine(tab) 个人名片(头像/昵称/签名/关注粉丝记录三个数)
+ 我的毛孩子列表 + 常用入口 + 齿轮进设置
pages/settings 提醒设置、会员权益、数据导出、意见反馈、关于我们
「我的」上放了个「看看别人眼里的我」,点进去就是 pages/user 那个公开
主页——自己看和别人看必须是同一个页面同一套渲染,否则两边会慢慢长歪。
这也是 Phase D 装扮功能的落点。
首页齿轮从「进 profile」改成「进设置」(个人页已经在 tab 上了)。
旧的 pages/profile 整个删掉,没留兼容层。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
218 lines
6.4 KiB
JavaScript
218 lines
6.4 KiB
JavaScript
const store = require('../../utils/store.js');
|
|
const api = require('../../utils/api.js');
|
|
const { toastErr } = require('../../utils/ui.js');
|
|
|
|
function todayStr() {
|
|
const d = new Date();
|
|
const p = (n) => (n < 10 ? '0' + n : '' + n);
|
|
return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate());
|
|
}
|
|
|
|
function mapTask(t) {
|
|
return {
|
|
id: t.id,
|
|
title: t.title,
|
|
sub: t.description,
|
|
priority: t.priority,
|
|
done: t.done,
|
|
sheet: t.sheet_type,
|
|
};
|
|
}
|
|
|
|
// 快速记录入口。颜色按类别分组(体重/疫苗=橙、便便/饮食=绿、异常/用药=蓝、消费/照片=紫),
|
|
// 靠颜色和图标区分,不再靠 emoji。
|
|
const QUICK_ITEMS = [
|
|
{ type: 'weight', icon: 'weight', label: '体重', tone: 'tone-1' },
|
|
{ type: 'poop', icon: 'poop', label: '便便', tone: 'tone-2' },
|
|
{ type: 'food', icon: 'food', label: '饮食', tone: 'tone-2' },
|
|
{ type: 'symptom', icon: 'symptom', label: '异常', tone: 'tone-3' },
|
|
{ type: 'cost', icon: 'cost', label: '消费', tone: 'tone-4' },
|
|
{ type: 'medicine', icon: 'medicine', label: '用药', tone: 'tone-3' },
|
|
{ type: 'photo', icon: 'photo', label: '照片', tone: 'tone-4' },
|
|
{ type: 'vaccine', icon: 'vaccine', label: '疫苗', tone: 'tone-1' },
|
|
];
|
|
|
|
// 门面上的状态 chip:健康状态 + 最多两条洞察
|
|
function heroChips(summary) {
|
|
const chips = [];
|
|
if (summary.health_status) {
|
|
chips.push({ text: summary.health_status, icon: 'check', tone: 'green' });
|
|
}
|
|
(summary.insights || []).slice(0, 2).forEach((it) => {
|
|
if (it && it.bold) chips.push({ text: it.bold, icon: 'trend', tone: '' });
|
|
});
|
|
return chips;
|
|
}
|
|
|
|
Page({
|
|
data: {
|
|
pet: {},
|
|
summary: { greeting: '', insights: [], week: [], advice: '', health_pct: 0, health_status: '正常' },
|
|
tasks: [],
|
|
quickItems: QUICK_ITEMS,
|
|
heroChips: [],
|
|
undoneText: '',
|
|
firstArticle: null,
|
|
selectedDate: '',
|
|
taskLabel: '今日任务',
|
|
todayDate: '',
|
|
sheetShow: false,
|
|
sheetType: '',
|
|
},
|
|
onLoad() {
|
|
this.setData({ todayDate: todayStr() });
|
|
this._unsub = store.subscribe((pet) => {
|
|
this.setData({ pet });
|
|
// 首次由 onShow 统一加载,这里只处理之后的「切换宠物 / 数据变更」,避免重复请求
|
|
if (!this._inited) return;
|
|
this.loadAll();
|
|
});
|
|
},
|
|
onUnload() {
|
|
if (this._unsub) this._unsub();
|
|
},
|
|
onShow() {
|
|
store
|
|
.ready()
|
|
.then(() => {
|
|
this._inited = true;
|
|
this.setData({ pet: store.getPet() });
|
|
this.loadAll();
|
|
})
|
|
.catch((e) => wx.showToast({ title: e.message || '加载失败', icon: 'none' }));
|
|
},
|
|
loadAll() {
|
|
this.loadSummary();
|
|
this.loadTasks();
|
|
if (!this.data.firstArticle) {
|
|
api
|
|
.listArticles()
|
|
.then((list) => {
|
|
if (list && list[0]) {
|
|
this.setData({ firstArticle: { id: list[0].id, icon: list[0].icon, title: list[0].title, desc: list[0].description } });
|
|
}
|
|
})
|
|
.catch(() => {});
|
|
}
|
|
},
|
|
loadSummary() {
|
|
const id = store.currentPetId();
|
|
if (!id) return;
|
|
api
|
|
.homeSummary(id)
|
|
.then((summary) => this.setData({ summary, heroChips: heroChips(summary) }))
|
|
.catch((e) => toastErr(e));
|
|
},
|
|
loadTasks() {
|
|
const id = store.currentPetId();
|
|
if (!id) return;
|
|
const date = this.data.selectedDate || this.data.todayDate;
|
|
api
|
|
.getTasks(id, date)
|
|
.then((tasks) => this.setTasks((tasks || []).map(mapTask)))
|
|
.catch((e) => toastErr(e));
|
|
},
|
|
// 任务列表落地时顺手算「还有几件事没做完」,门面上要用
|
|
setTasks(tasks) {
|
|
const undone = tasks.filter((t) => !t.done).length;
|
|
this.setData({
|
|
tasks,
|
|
undoneText: tasks.length === 0
|
|
? '这天还没有安排'
|
|
: undone > 0 ? '还有 ' + undone + ' 件事没做完' : '今天的事都做完啦',
|
|
});
|
|
},
|
|
onTapDay(e) {
|
|
const { date, active } = e.currentTarget.dataset;
|
|
if (!date) return;
|
|
const parts = date.split('-');
|
|
this.setData({
|
|
selectedDate: date,
|
|
taskLabel: active ? '今日任务' : +parts[1] + '月' + +parts[2] + '日',
|
|
});
|
|
this.loadTasks();
|
|
},
|
|
backToToday() {
|
|
this.setData({ selectedDate: this.data.todayDate, taskLabel: '今日任务' });
|
|
this.loadTasks();
|
|
},
|
|
onTapTask(e) {
|
|
const i = e.currentTarget.dataset.index;
|
|
const t = this.data.tasks[i];
|
|
if (t.sheet) {
|
|
this.openSheetType(t.sheet);
|
|
return;
|
|
}
|
|
api
|
|
.toggleTask(t.id)
|
|
.then((res) => {
|
|
const tasks = this.data.tasks.slice();
|
|
tasks[i] = Object.assign({}, tasks[i], { done: res.done });
|
|
this.setTasks(tasks);
|
|
this.loadSummary();
|
|
})
|
|
.catch((e) => toastErr(e, '操作失败'));
|
|
},
|
|
completeTasks() {
|
|
const id = store.currentPetId();
|
|
if (!id) return;
|
|
api
|
|
.completeAllTasks(id)
|
|
.then((tasks) => {
|
|
this.setTasks((tasks || []).map(mapTask));
|
|
this.loadSummary();
|
|
})
|
|
.catch((e) => toastErr(e, '操作失败'));
|
|
},
|
|
openSheet(e) {
|
|
this.openSheetType(e.currentTarget.dataset.type);
|
|
},
|
|
openSheetType(type) {
|
|
this.setData({ sheetType: type, sheetShow: true });
|
|
},
|
|
closeSheet() {
|
|
// 只关闭。数据变更由 bind:saved 或 store 的宠物变更通知触发刷新,
|
|
// 不必每次关弹层(哪怕只是看了眼 AI)都全量重拉一遍
|
|
this.setData({ sheetShow: false });
|
|
},
|
|
onSheetSave() {
|
|
this.loadTasks();
|
|
this.loadSummary();
|
|
},
|
|
onAddPet() {
|
|
this.openSheetType('addPet');
|
|
},
|
|
// 没建档时点门面卡应该去「添加」,而不是打开一个空的编辑表单
|
|
onHeroTap() {
|
|
if (this.data.pet && this.data.pet.id) this.openSheetType('editPet');
|
|
else this.openSheetType('addPet');
|
|
},
|
|
onEditPet() {
|
|
this.openSheetType('editPet');
|
|
},
|
|
onFab() {
|
|
wx.navigateTo({ url: '/pages/ai/ai' });
|
|
},
|
|
goSettings() {
|
|
wx.navigateTo({ url: '/pages/settings/settings' });
|
|
},
|
|
goLearn() {
|
|
wx.navigateTo({ url: '/pages/learn/learn' });
|
|
},
|
|
// 首页那张卡片直接打开这篇文章正文
|
|
openFirstArticle() {
|
|
const a = this.data.firstArticle;
|
|
if (a && a.id) wx.navigateTo({ url: `/pages/article/article?id=${a.id}` });
|
|
else this.goLearn();
|
|
},
|
|
goRecord() {
|
|
wx.switchTab({ url: '/pages/record/record' });
|
|
},
|
|
onShareAppMessage() {
|
|
return { title: '肉垫计划 · 每天照顾好毛孩子', path: '/pages/home/home' };
|
|
},
|
|
onShareTimeline() {
|
|
return { title: '肉垫计划 · 每天照顾好毛孩子' };
|
|
},
|
|
});
|