const store = require('../../utils/store.js'); const api = require('../../utils/api.js'); const { toastErr } = require('../../utils/ui.js'); const recordTypes = require('../../utils/recordTypes.js'); function pad(n) { return n < 10 ? '0' + n : '' + n; } function fmtTime(iso) { if (!iso) return ''; const d = new Date(iso); const now = new Date(); const hm = pad(d.getHours()) + ':' + pad(d.getMinutes()); if (d.toDateString() === now.toDateString()) return '今天 ' + hm; const y = new Date(now.getTime() - 86400000); if (d.toDateString() === y.toDateString()) return '昨天 ' + hm; return d.getMonth() + 1 + '月' + d.getDate() + '日'; } // 每条记录的分组配色。这页只读历史,引一份轻量的 code→组 映射就够 function toneOf(type) { const t = recordTypes.get(type); return (t && t.tone) || 'tone-1'; } function mapRecord(r) { return { id: r.id, // 图标按 type 取(可靠);老数据 icon 字段里的 emoji 只作兜底 type: r.type || r.icon || '', tone: toneOf(r.type), title: r.title, desc: fmtTime(r.occurred_at) + (r.description ? '|' + r.description : ''), image: r.image_url || '', }; } // 时间轴类型筛选。常用的一组,不是全部 24 种——seg-tabs 放满会挤成一条难点的横条 const REC_FILTERS = [ { key: '', label: '全部' }, { key: 'weight', label: '体重' }, { key: 'poop', label: '排便' }, { key: 'food', label: '饮食' }, { key: 'symptom', label: '异常' }, { key: 'vaccine', label: '疫苗' }, { key: 'cost', label: '记账' }, { key: 'photo', label: '照片' }, ]; const REC_PAGE = 8; Page({ data: { pet: {}, timeline: [], recPage: 1, recTotal: 0, recHasMore: false, recFilters: REC_FILTERS, recFilterIdx: 0, }, onLoad() { this._unsub = store.subscribe((pet) => { this.setData({ pet }); if (!this._inited) return; this.loadRecords(); }); }, onUnload() { if (this._unsub) this._unsub(); }, onShow() { // 分组配色要读类型缓存。这页可能直接从报告页进来,缓存没经过首页热过, // 自己拉一次(load 内部只真请求一次),拉完刷新已有列表的配色 recordTypes.load().then(() => this.setData({ timeline: this.data.timeline.map((t) => ({ ...t, tone: toneOf(t.type) })) })).catch(() => {}); store .ready() .then(() => { this._inited = true; this.setData({ pet: store.getPet() }); this.loadRecords(); }) .catch((e) => wx.showToast({ title: e.message || '加载失败', icon: 'none' })); }, curFilter() { return this.data.recFilters[this.data.recFilterIdx].key; }, onFilterTap(e) { const i = Number(e.detail.index); if (i === this.data.recFilterIdx) return; this.setData({ recFilterIdx: i }, () => this.loadRecords()); }, loadRecords() { const id = store.currentPetId(); if (!id) return; api .getRecords(id, { page: 1, pageSize: REC_PAGE, type: this.curFilter() }) .then((res) => { const list = (res.list || []).map(mapRecord); this.setData({ timeline: list, recPage: 1, recTotal: res.total || 0, recHasMore: list.length < (res.total || 0) }); }) .catch((e) => toastErr(e)); }, loadMoreRecords() { const id = store.currentPetId(); if (!id) return; const next = this.data.recPage + 1; api .getRecords(id, { page: next, pageSize: REC_PAGE, type: this.curFilter() }) .then((res) => { const merged = this.data.timeline.concat((res.list || []).map(mapRecord)); this.setData({ timeline: merged, recPage: next, recTotal: res.total || 0, recHasMore: merged.length < (res.total || 0) }); }) .catch(() => {}); }, collapseRecords() { this.loadRecords(); }, onDeleteRecord(e) { const r = this.data.timeline[e.currentTarget.dataset.index]; if (!r || !r.id) return; wx.showModal({ title: '删除记录', content: '确定删除「' + r.title + '」?删除后不可恢复。', confirmColor: '#D9534F', success: (res) => { if (!res.confirm) return; api .deleteRecord(r.id) .then(() => { wx.showToast({ title: '已删除', icon: 'success' }); this.loadRecords(); }) .catch((err) => wx.showToast({ title: err.message || '删除失败', icon: 'none' })); }, }); }, previewImage(e) { const src = e.currentTarget.dataset.src; if (src) wx.previewImage({ urls: [src], current: src }); }, onAddPet() { wx.navigateTo({ url: '/pages/petform/petform' }); }, onShareAppMessage() { return { title: '用肉垫计划记录毛孩子的成长', path: '/pages/record/record' }; }, onShareTimeline() { return { title: '用肉垫计划记录毛孩子的成长' }; }, });