diff --git a/pets-fe/pages/history/history.js b/pets-fe/pages/history/history.js
index db97e94..5205d9e 100644
--- a/pets-fe/pages/history/history.js
+++ b/pets-fe/pages/history/history.js
@@ -1,6 +1,7 @@
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;
@@ -15,23 +16,15 @@ function fmtTime(iso) {
if (d.toDateString() === y.toDateString()) return '昨天 ' + hm;
return d.getMonth() + 1 + '月' + d.getDate() + '日';
}
-// 记录类型 → 配色。和首页快速记录入口用同一套分组:
-// 体重/疫苗=橙、便便/饮食=绿、异常/用药=蓝、消费/照片=紫
-// 时间轴每条记录的色调。这页只读历史,不需要整份类型表,
-// 但仍要按分组着色——所以引一份轻量的 code→组 映射
-const recordTypes = require('../../utils/recordTypes.js');
+// 每条记录的分组配色。这页只读历史,引一份轻量的 code→组 映射就够
function toneOf(type) {
const t = recordTypes.get(type);
return (t && t.tone) || 'tone-1';
}
-
-// 「记一笔」的入口不再写死在这里:类型和分组由后端 record_types 表提供
-// (后台可配),前端只负责渲染。utils/recordTypes.js 是两处共用的缓存。
-
function mapRecord(r) {
return {
id: r.id,
- // 图标按 type 取(9 值枚举,可靠);老数据 icon 字段里的 emoji 只作兜底
+ // 图标按 type 取(可靠);老数据 icon 字段里的 emoji 只作兜底
type: r.type || r.icon || '',
tone: toneOf(r.type),
title: r.title,
@@ -40,57 +33,19 @@ function mapRecord(r) {
};
}
-// 时间轴类型筛选
+// 时间轴类型筛选。常用的一组,不是全部 24 种——seg-tabs 放满会挤成一条难点的横条
const REC_FILTERS = [
{ key: '', label: '全部' },
{ key: 'weight', label: '体重' },
- { key: 'poop', label: '便便' },
+ { key: 'poop', label: '排便' },
{ key: 'food', label: '饮食' },
{ key: 'symptom', label: '异常' },
- { key: 'medicine', label: '用药' },
{ key: 'vaccine', label: '疫苗' },
- { key: 'cost', label: '消费' },
+ { key: 'cost', label: '记账' },
{ key: 'photo', label: '照片' },
];
-function fmtDay(iso) {
- if (!iso) return '';
- const d = new Date(iso);
- return d.getMonth() + 1 + '月' + d.getDate() + '日';
-}
-
-// 用真实体重点生成趋势图:返回 { svg(折线), dots(可点圆点,坐标为百分比) }
-function buildChart(points) {
- const vals = points.map((p) => Number(p.value) || 0);
- const W = 300, H = 100, pad = 12, n = vals.length;
- let min = Math.min.apply(null, vals);
- let max = Math.max.apply(null, vals);
- if (max === min) max = min + 1;
- const xs = (i) => (n === 1 ? W / 2 : pad + ((W - 2 * pad) * i) / (n - 1));
- const ys = (v) => H - pad - ((H - 2 * pad) * (v - min)) / (max - min);
- const pts = vals.map((v, i) => xs(i).toFixed(1) + ',' + ys(v).toFixed(1)).join(' ');
- const svg =
- '';
- const dots = points.map((p, i) => ({
- xp: Number(((xs(i) / W) * 100).toFixed(2)),
- yp: Number(((ys(vals[i]) / H) * 100).toFixed(2)),
- value: vals[i],
- date: fmtDay(p.occurred_at),
- note: p.note || '',
- delta: i > 0 ? Number((vals[i] - vals[i - 1]).toFixed(2)) : null,
- }));
- return { svg: 'data:image/svg+xml,' + encodeURIComponent(svg), dots };
-}
-
-// 洞察等级 → 图标。alert 用警告号,其余按类别给
-const INS_ICON = { alert: 'warn', warn: 'warn', info: 'trend' };
-function mapInsight(i) {
- return { ...i, icon: i.level === 'info' ? (INS_ICON.info) : INS_ICON.alert };
-}
-
-const REC_PAGE = 6;
+const REC_PAGE = 8;
Page({
data: {
@@ -101,19 +56,11 @@ Page({
recHasMore: false,
recFilters: REC_FILTERS,
recFilterIdx: 0,
- insights: [],
- trendSvg: '',
- trendStats: null,
- trendDots: [],
- selDot: null,
- selIdx: -1,
- sheetShow: false,
- sheetType: '',
},
onLoad() {
this._unsub = store.subscribe((pet) => {
this.setData({ pet });
- if (!this._inited) return; // 首次由 onShow 加载
+ if (!this._inited) return;
this.loadRecords();
});
},
@@ -121,9 +68,9 @@ Page({
if (this._unsub) this._unsub();
},
onShow() {
- // 时间轴的分组配色要读类型缓存。这页可能是从报告页直接进来的,
- // 缓存没经过首页/记录页热过,所以自己拉一次(load 内部只会真请求一次)
- recordTypes.load().then(() => this.setData({ timeline: this.data.timeline })).catch(() => {});
+ // 分组配色要读类型缓存。这页可能直接从报告页进来,缓存没经过首页热过,
+ // 自己拉一次(load 内部只真请求一次),拉完刷新已有列表的配色
+ recordTypes.load().then(() => this.setData({ timeline: this.data.timeline.map((t) => ({ ...t, tone: toneOf(t.type) })) })).catch(() => {});
store
.ready()
.then(() => {
@@ -136,13 +83,37 @@ Page({
curFilter() {
return this.data.recFilters[this.data.recFilterIdx].key;
},
- // 来自 seg-tabs 的 change 事件
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;
@@ -162,96 +133,10 @@ Page({
},
});
},
- // 健康洞察:把散落的记录变成结论。点一条直接跳到对应的记录入口
- loadInsights() {
- const id = store.currentPetId();
- if (!id) return this.setData({ insights: [] });
- api
- .petInsights(id)
- .then((list) => this.setData({ insights: (list || []).map(mapInsight) }))
- .catch(() => this.setData({ insights: [] }));
- },
- onTapInsight(e) {
- const it = this.data.insights[e.currentTarget.dataset.index];
- if (it && it.action) this.openSheetType(it.action);
- },
- 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));
- this.loadTrend();
- this.loadInsights();
- },
- 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();
- },
- loadTrend() {
- const id = store.currentPetId();
- if (!id) {
- this.setData({ trendSvg: '', trendStats: null, trendDots: [], selDot: null, selIdx: -1 });
- return;
- }
- api
- .weightTrend(id)
- .then((points) => {
- points = points || [];
- if (points.length < 2) {
- this.setData({ trendSvg: '', trendStats: null, trendDots: [], selDot: null, selIdx: -1 });
- return;
- }
- const vals = points.map((p) => Number(p.value) || 0);
- const latest = vals[vals.length - 1];
- const delta = Number((latest - vals[0]).toFixed(2));
- const chart = buildChart(points);
- const lastIdx = chart.dots.length - 1;
- this.setData({
- trendSvg: chart.svg,
- trendDots: chart.dots,
- selDot: chart.dots[lastIdx], // 默认选中最新一次
- selIdx: lastIdx,
- trendStats: { latest, delta, count: points.length },
- });
- })
- .catch(() => {});
- },
- onTapPoint(e) {
- const i = e.currentTarget.dataset.index;
- this.setData({ selDot: this.data.trendDots[i], selIdx: i });
- },
previewImage(e) {
const src = e.currentTarget.dataset.src;
if (src) wx.previewImage({ urls: [src], current: src });
},
- openSheet(e) {
- this.openSheetType(e.currentTarget.dataset.type);
- },
- openSheetType(type) {
- this.setData({ sheetType: type, sheetShow: true });
- },
- closeSheet() {
- this.setData({ sheetShow: false });
- },
- onSheetSaved() {
- this.loadRecords();
- },
onAddPet() {
wx.navigateTo({ url: '/pages/petform/petform' });
},
diff --git a/pets-fe/pages/history/history.json b/pets-fe/pages/history/history.json
index 58b7029..6b28821 100644
--- a/pets-fe/pages/history/history.json
+++ b/pets-fe/pages/history/history.json
@@ -2,8 +2,6 @@
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar",
"pet-switch": "/components/pet-switch/pet-switch",
- "bottom-sheet": "/components/bottom-sheet/bottom-sheet",
- "fab": "/components/fab/fab",
"pt-icon": "/components/pt-icon/index",
"seg-tabs": "/components/seg-tabs/index"
}
diff --git a/pets-fe/pages/history/history.wxml b/pets-fe/pages/history/history.wxml
index aae27f3..d0db908 100644
--- a/pets-fe/pages/history/history.wxml
+++ b/pets-fe/pages/history/history.wxml
@@ -1,4 +1,4 @@
-
+
@@ -7,69 +7,30 @@
还没有毛孩子的档案
- 建好档并记上几笔,这里就会长出趋势和洞察。
+ 建好档并记上几笔,这里就会长出完整的记录流水。
建一份档案
-
- 健康洞察来自你的记录
-
-
-
- {{item.title}}
-
- {{item.detail}}
- {{item.evidence}}
-
-
-
-
- 体重趋势记录
- 最新 {{trendStats.latest}}kg · 较首次 {{trendStats.delta >= 0 ? '+' : ''}}{{trendStats.delta}}kg · 共 {{trendStats.count}} 次
-
-
-
-
-
-
-
-
-
- {{selDot.date}}
- {{selDot.value}}kg
- {{selDot.delta > 0 ? '+' : ''}}{{selDot.delta}}kg
-
- {{selDot.note}}
- 这次没有填备注
-
- 点击圆点查看每次记录
-
- 先记录几次体重,这里会自动画出趋势曲线
-
-
-
- 健康时间轴摘要
+
+
-
-
-
-
- {{item.title}}{{item.desc}}
-
+
+
+
+
+
+ {{item.title}}{{item.desc}}
+
+
+ 这个分类下还没有记录
- 这个分类下还没有记录
+ 长按任意一条可删除
+ 查看更多(共 {{recTotal}} 条)
+ 收起
- 长按任意一条可删除
- 查看更多(共 {{recTotal}} 条)
- 收起
-
+
-
-
diff --git a/pets-fe/pages/history/history.wxss b/pets-fe/pages/history/history.wxss
index 25c7996..8764002 100644
--- a/pets-fe/pages/history/history.wxss
+++ b/pets-fe/pages/history/history.wxss
@@ -1,29 +1,4 @@
-.mini-chart{
- height:256rpx;border-radius:var(--r-md);margin-top:var(--sp-4);position:relative;overflow:hidden;
- background:
- linear-gradient(180deg,rgba(115,190,157,.16),rgba(115,190,157,0)),
- repeating-linear-gradient(0deg,#fff,#fff 60rpx,#EFE8DE 62rpx);
-}
-.chart-line{position:absolute;left:36rpx;right:36rpx;top:44rpx;bottom:44rpx;width:auto;height:auto}
-.dot-layer{position:absolute;left:36rpx;right:36rpx;top:44rpx;bottom:44rpx}
-.dot{position:absolute;width:40rpx;height:40rpx;margin:-20rpx 0 0 -20rpx;border-radius:50%;}
-.dot::after{content:'';position:absolute;left:50%;top:50%;width:18rpx;height:18rpx;margin:-9rpx 0 0 -9rpx;border-radius:50%;background:#fff;border:4rpx solid var(--green);box-sizing:border-box}
-.dot.on::after{width:26rpx;height:26rpx;margin:-13rpx 0 0 -13rpx;background:var(--green);box-shadow:0 0 0 6rpx rgba(115,190,157,.25)}
-.trend-stats{margin-top:var(--sp-3);font-size:var(--fs-sm);color:var(--muted)}
-
-.point-detail{
- margin-top:var(--sp-3);padding:var(--sp-4);border-radius:var(--r-md);
- background:var(--green-soft);border:1rpx solid #DCEFE4;
-}
-.pd-main{display:flex;align-items:baseline;gap:var(--sp-3)}
-.pd-date{font-size:var(--fs-sm);color:var(--muted)}
-.pd-weight{font-size:var(--fs-xl);font-weight:var(--fw-b);color:var(--green-ink)}
-.pd-delta{font-size:var(--fs-sm);font-weight:var(--fw-b)}
-.pd-delta.up{color:var(--red-ink)}
-.pd-delta.down{color:var(--green-ink)}
-.pd-note{margin-top:var(--sp-2);font-size:var(--fs-md);color:var(--text-2);line-height:1.5}
-.pd-note.muted{color:var(--muted)}
-.chart-hint{margin-top:var(--sp-2);font-size:var(--fs-cap);color:var(--muted);text-align:center}
+/* 全部记录时间轴。洞察和体重趋势已经搬到报告页,这里只留流水 + 筛选 */
.health-timeline{display:flex;flex-direction:column;gap:var(--sp-2)}
.health-event{display:flex;gap:var(--sp-3);background:var(--surface-2);border-radius:var(--r-md);padding:var(--sp-4)}
@@ -42,18 +17,3 @@
margin-top:var(--sp-3);padding:var(--sp-3);text-align:center;color:var(--primary-dark);
background:var(--surface-2);border-radius:var(--r-md);font-size:var(--fs-sm);font-weight:var(--fw-b);
}
-
-/* 健康洞察 */
-.ins{border-radius:var(--r-md);padding:var(--sp-4);margin-bottom:var(--sp-2)}
-.ins:last-child{margin-bottom:0}
-.ins-info{background:var(--surface-2)}
-.ins-warn{background:var(--primary-soft)}
-.ins-alert{background:var(--red-soft)}
-.ins-head{display:flex;align-items:flex-start;gap:var(--sp-2)}
-.ins-ic{flex:none;margin-top:2rpx}
-.ins-info .ins-ic{color:var(--green-ink)}
-.ins-warn .ins-ic{color:var(--primary-ink)}
-.ins-alert .ins-ic{color:var(--red-ink)}
-.ins-title{flex:1;font-size:var(--fs-md);font-weight:var(--fw-b);line-height:1.4}
-.ins-detail{color:var(--text-2);font-size:var(--fs-sm);line-height:1.6;margin-top:var(--sp-2)}
-.ins-ev{color:var(--muted);font-size:var(--fs-cap);margin-top:var(--sp-2)}
diff --git a/pets-fe/pages/report/report.js b/pets-fe/pages/report/report.js
index 9972ee9..9af86a0 100644
--- a/pets-fe/pages/report/report.js
+++ b/pets-fe/pages/report/report.js
@@ -2,20 +2,64 @@ const store = require('../../utils/store.js');
const api = require('../../utils/api.js');
const { syncTabBar } = require('../../utils/tabbar.js');
+function fmtDay(iso) {
+ if (!iso) return '';
+ const d = new Date(iso);
+ return d.getMonth() + 1 + '月' + d.getDate() + '日';
+}
+
+// 体重点 → 趋势图。返回 { svg(折线 data-uri), dots(可点圆点,坐标为百分比) }。
+// 从历史页搬过来的:报告页才是「看数据」的地方,图不该埋在二级页
+function buildChart(points) {
+ const vals = points.map((p) => Number(p.value) || 0);
+ const W = 300, H = 100, pad = 12, n = vals.length;
+ let min = Math.min.apply(null, vals);
+ let max = Math.max.apply(null, vals);
+ if (max === min) max = min + 1;
+ const xs = (i) => (n === 1 ? W / 2 : pad + ((W - 2 * pad) * i) / (n - 1));
+ const ys = (v) => H - pad - ((H - 2 * pad) * (v - min)) / (max - min);
+ const pts = vals.map((v, i) => xs(i).toFixed(1) + ',' + ys(v).toFixed(1)).join(' ');
+ const svg =
+ '';
+ const dots = points.map((p, i) => ({
+ xp: Number(((xs(i) / W) * 100).toFixed(2)),
+ yp: Number(((ys(vals[i]) / H) * 100).toFixed(2)),
+ value: vals[i],
+ date: fmtDay(p.occurred_at),
+ note: p.note || '',
+ delta: i > 0 ? Number((vals[i] - vals[i - 1]).toFixed(2)) : null,
+ }));
+ return { svg: 'data:image/svg+xml,' + encodeURIComponent(svg), dots };
+}
+
+// 洞察等级 → 图标。alert/warn 用警告号,info 用趋势
+function mapInsight(i) {
+ return { ...i, icon: i.level === 'info' ? 'trend' : 'warn' };
+}
+
Page({
data: {
pet: {},
report: null,
gainText: '+0.0',
- bill: null,
+ insights: [],
+ trendSvg: '',
+ trendStats: null,
+ trendDots: [],
+ selDot: null,
+ selIdx: -1,
summary: null,
+ bill: null,
+ billTop: null,
sheetShow: false,
sheetType: '',
},
onLoad() {
this._unsub = store.subscribe((pet) => {
this.setData({ pet });
- if (!this._inited) return; // 首次由 onShow 加载
+ if (!this._inited) return;
this.loadData();
});
},
@@ -36,18 +80,74 @@ Page({
loadData() {
const id = store.currentPetId();
if (!id) return;
- Promise.all([api.weeklyReport(id), api.getBill(id, 'month'), api.healthSummary(id)])
- .then(([report, bill, summary]) => {
- const g = report.weight_gain || 0;
+ api
+ .weeklyReport(id)
+ .then((report) => {
+ const g = (report && report.weight_gain) || 0;
+ this.setData({ report, gainText: (g >= 0 ? '+' : '') + g.toFixed(1) });
+ })
+ .catch(() => {});
+ api
+ .petInsights(id)
+ .then((list) => this.setData({ insights: (list || []).map(mapInsight) }))
+ .catch(() => this.setData({ insights: [] }));
+ api
+ .healthSummary(id)
+ .then((summary) => this.setData({ summary }))
+ .catch(() => {});
+ // 账单只在报告页取一个概要,详细录入和流水在花销页
+ api
+ .getBill(id, 'month')
+ .then((bill) => {
+ const cats = (bill && bill.categories) || [];
+ this.setData({ bill, billTop: cats.length ? cats[0] : null });
+ })
+ .catch(() => {});
+ this.loadTrend(id);
+ },
+ loadTrend(id) {
+ api
+ .weightTrend(id)
+ .then((points) => {
+ points = points || [];
+ if (points.length < 2) {
+ this.setData({ trendSvg: '', trendStats: null, trendDots: [], selDot: null, selIdx: -1 });
+ return;
+ }
+ const vals = points.map((p) => Number(p.value) || 0);
+ const latest = vals[vals.length - 1];
+ const delta = Number((latest - vals[0]).toFixed(2));
+ const chart = buildChart(points);
+ const last = chart.dots.length - 1;
this.setData({
- report,
- gainText: (g >= 0 ? '+' : '') + g.toFixed(1),
- bill,
- summary,
+ trendSvg: chart.svg,
+ trendDots: chart.dots,
+ selDot: chart.dots[last], // 默认选中最新一次
+ selIdx: last,
+ trendStats: { latest, delta, count: points.length },
});
})
.catch(() => {});
},
+ onTapPoint(e) {
+ const i = e.currentTarget.dataset.index;
+ this.setData({ selDot: this.data.trendDots[i], selIdx: i });
+ },
+ // 点一条洞察,跳到对应类型的记录入口(洞察带 action = 记录类型 code)
+ onTapInsight(e) {
+ const it = this.data.insights[e.currentTarget.dataset.index];
+ if (it && it.action) wx.navigateTo({ url: '/pages/addrecord/addrecord?type=' + it.action });
+ },
+ // 体重趋势的「记一笔」直接去体重记录页(原来开的 weight 弹层已经搬成独立页)
+ goWeight() {
+ wx.navigateTo({ url: '/pages/addrecord/addrecord?type=weight' });
+ },
+ goExpense() {
+ wx.navigateTo({ url: '/pages/expense/expense' });
+ },
+ goHistory() {
+ wx.navigateTo({ url: '/pages/history/history' });
+ },
openSheet(e) {
this.setData({ sheetType: e.currentTarget.dataset.type, sheetShow: true });
},
@@ -57,11 +157,7 @@ Page({
onAddPet() {
wx.navigateTo({ url: '/pages/petform/petform' });
},
- goHistory() {
- wx.navigateTo({ url: '/pages/history/history' });
- },
onShareAppMessage() {
- // 带上宠物名,转发出去别人一眼知道是谁家的
const n = (this.data.pet && this.data.pet.name) || '我家毛孩子';
return { title: `${n} 的成长报告`, path: '/pages/report/report' };
},
diff --git a/pets-fe/pages/report/report.json b/pets-fe/pages/report/report.json
index cfab932..381942f 100644
--- a/pets-fe/pages/report/report.json
+++ b/pets-fe/pages/report/report.json
@@ -3,7 +3,6 @@
"nav-bar": "/components/nav-bar/nav-bar",
"pet-switch": "/components/pet-switch/pet-switch",
"bottom-sheet": "/components/bottom-sheet/bottom-sheet",
- "fab": "/components/fab/fab",
"pt-icon": "/components/pt-icon/index"
}
}
diff --git a/pets-fe/pages/report/report.wxml b/pets-fe/pages/report/report.wxml
index a4cb5d4..789c36b 100644
--- a/pets-fe/pages/report/report.wxml
+++ b/pets-fe/pages/report/report.wxml
@@ -11,59 +11,101 @@
建一份档案
+
+
- 本周成长报告{{report.health_status || '稳定成长'}}
- {{report.summary || '暂无本周数据'}}
+ 本周概览{{report.health_status || '稳定成长'}}
+ {{report.summary || '这周还没有足够的记录,多记几笔就有小结了'}}
{{report.tasks_completed || 0}}完成任务
- {{gainText}}kg 增长
+ {{gainText}}kg 变化
{{report.high_risk_count || 0}}高风险异常
-
-
-
+
+ 生成分享卡片
-
-
-
-
- 记录与趋势
- 洞察 · 体重 · 时间轴
-
+
+
+ 健康洞察来自你的记录
+
+
+
+ {{item.title}}
+
+
+ {{item.detail}}
+ {{item.evidence}}
+
- 健康摘要导出
+ 体重趋势记一笔
+ 最新 {{trendStats.latest}}kg · 较首次 {{trendStats.delta >= 0 ? '+' : ''}}{{trendStats.delta}}kg · 共 {{trendStats.count}} 次
+
+
+
+
+
+
+
+
+
+ {{selDot.date}}
+ {{selDot.value}}kg
+ {{selDot.delta > 0 ? '+' : ''}}{{selDot.delta}}kg
+
+ {{selDot.note}}
+ 这次没有填备注
+
+ 点击圆点查看每次记录
+
+ 先记录几次体重,这里会自动画出趋势曲线
+
+
+
+
+ 健康摘要就医摘要
疫苗进度{{summary.vaccine_progress || '—'}}
驱虫状态{{summary.deworm_status || '—'}}
体重趋势{{summary.weight_trend || '—'}}
异常记录{{summary.anomaly_count || 0}} 次
-
- 养宠账单记一笔
-
- ¥{{bill.total || 0}}本月花费
- ¥{{bill.max_single || 0}}最大支出
- {{bill.categories.length || 0}}消费类别
+
+
+ 养宠花销去记账 ›
+
+ ¥{{bill.total || 0}}
+ 本月{{billTop ? ',' + billTop.category + '花得最多' : ''}}
-
+
{{item.category}}
¥{{item.amount}}
+ 这个月还没有记过花销
-
- Pro 会员
- 解锁 365 天计划、PDF 健康档案、月度报告、多宠物管理与年度账单。
-
+
+
+
+
+ 全部记录
+ 按类型筛选、翻看历史
+
+
+
diff --git a/pets-fe/pages/report/report.wxss b/pets-fe/pages/report/report.wxss
index 2106ca1..51ed02c 100644
--- a/pets-fe/pages/report/report.wxss
+++ b/pets-fe/pages/report/report.wxss
@@ -1,7 +1,57 @@
.report-card{background:linear-gradient(135deg,#FFFFFF,#F2FAF6)}
.rc-text{color:var(--text-2);font-size:var(--fs-md);line-height:1.6}
+.rc-share{margin-top:var(--sp-4)}
-.bill-bars{display:flex;flex-direction:column;gap:var(--sp-3);margin-top:var(--sp-4)}
+/* ===== 健康洞察(从历史页搬来,报告页的主角)===== */
+.ins{border-radius:var(--r-md);padding:var(--sp-4);margin-bottom:var(--sp-2)}
+.ins:last-child{margin-bottom:0}
+.ins-info{background:var(--surface-2)}
+.ins-warn{background:var(--primary-soft)}
+.ins-alert{background:var(--red-soft)}
+.ins-head{display:flex;align-items:flex-start;gap:var(--sp-2)}
+.ins-ic{flex:none;margin-top:2rpx}
+.ins-info .ins-ic{color:var(--green-ink)}
+.ins-warn .ins-ic{color:var(--primary-ink)}
+.ins-alert .ins-ic{color:var(--red-ink)}
+.ins-title{flex:1;font-size:var(--fs-md);font-weight:var(--fw-b);line-height:1.4}
+.ins-go{flex:none;color:var(--muted2);margin-top:2rpx}
+.ins-detail{color:var(--text-2);font-size:var(--fs-sm);line-height:1.6;margin-top:var(--sp-2)}
+.ins-ev{color:var(--muted);font-size:var(--fs-cap);margin-top:var(--sp-2)}
+
+/* ===== 体重趋势曲线(从历史页搬来)===== */
+.trend-stats{margin-top:var(--sp-3);font-size:var(--fs-sm);color:var(--muted)}
+.mini-chart{
+ height:256rpx;border-radius:var(--r-md);margin-top:var(--sp-4);position:relative;overflow:hidden;
+ background:
+ linear-gradient(180deg,rgba(115,190,157,.16),rgba(115,190,157,0)),
+ repeating-linear-gradient(0deg,#fff,#fff 60rpx,#EFE8DE 62rpx);
+}
+.chart-line{position:absolute;left:36rpx;right:36rpx;top:44rpx;bottom:44rpx;width:auto;height:auto}
+.dot-layer{position:absolute;left:36rpx;right:36rpx;top:44rpx;bottom:44rpx}
+.dot{position:absolute;width:40rpx;height:40rpx;margin:-20rpx 0 0 -20rpx;border-radius:50%}
+.dot::after{content:'';position:absolute;left:50%;top:50%;width:18rpx;height:18rpx;margin:-9rpx 0 0 -9rpx;border-radius:50%;background:#fff;border:4rpx solid var(--green);box-sizing:border-box}
+.dot.on::after{width:26rpx;height:26rpx;margin:-13rpx 0 0 -13rpx;background:var(--green);box-shadow:0 0 0 6rpx rgba(115,190,157,.25)}
+.point-detail{
+ margin-top:var(--sp-3);padding:var(--sp-4);border-radius:var(--r-md);
+ background:var(--green-soft);border:1rpx solid #DCEFE4;
+}
+.pd-main{display:flex;align-items:baseline;gap:var(--sp-3)}
+.pd-date{font-size:var(--fs-sm);color:var(--muted)}
+.pd-weight{font-size:var(--fs-xl);font-weight:var(--fw-b);color:var(--green-ink)}
+.pd-delta{font-size:var(--fs-sm);font-weight:var(--fw-b)}
+.pd-delta.up{color:var(--red-ink)}
+.pd-delta.down{color:var(--green-ink)}
+.pd-note{margin-top:var(--sp-2);font-size:var(--fs-md);color:var(--text-2);line-height:1.5}
+.pd-note.muted{color:var(--muted)}
+.chart-hint{margin-top:var(--sp-2);font-size:var(--fs-cap);color:var(--muted);text-align:center}
+
+/* ===== 养宠花销概要(详细在花销页)===== */
+.bill-card:active{opacity:.85}
+.bill-sum{margin:var(--sp-2) 0 var(--sp-4)}
+.bill-total{font-size:56rpx;font-weight:var(--fw-b);color:var(--text);line-height:1.1}
+.bill-cur{font-size:var(--fs-lg);color:var(--muted);margin-right:4rpx}
+.bill-sub{font-size:var(--fs-sm);color:var(--muted);margin-top:4rpx}
+.bill-bars{display:flex;flex-direction:column;gap:var(--sp-3)}
.bill-row{display:grid;grid-template-columns:88rpx 1fr 108rpx;gap:var(--sp-3);align-items:center;font-size:var(--fs-sm)}
.bill-cat{color:var(--text)}
.bill-amt{font-weight:var(--fw-b);text-align:right}