feat(fe): 首页改成品牌活泼版(本次设计语言的样板页)
- 门面换成完成度环 + 状态 chip:一眼能看到「今天还剩几件事」, 原来的 hero 卡只是把宠物信息平铺,占了 340rpx 却没给出任何判断 - 快速记录 8 宫格从 emoji 换成图标 + 4 色分类(体重/疫苗=橙、 便便/饮食=绿、异常/用药=蓝、消费/照片=紫),靠颜色分组而不是靠认 emoji - AI 建议卡底部加深色 CTA,全页唯一的深色块,指明主操作 - 顶部 🔔⚙ 换成图标;日期条、任务卡、空态全部走 token - 任务列表落地时顺手算未完成数,门面文案由它驱动 后 8 页按这套语言套用,不再重新发明。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,11 +19,39 @@ function mapTask(t) {
|
||||
};
|
||||
}
|
||||
|
||||
// 快速记录入口。颜色按类别分组(体重/疫苗=橙、便便/饮食=绿、异常/用药=蓝、消费/照片=紫),
|
||||
// 靠颜色和图标区分,不再靠 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: '今日任务',
|
||||
@@ -75,7 +103,7 @@ Page({
|
||||
if (!id) return;
|
||||
api
|
||||
.homeSummary(id)
|
||||
.then((summary) => this.setData({ summary }))
|
||||
.then((summary) => this.setData({ summary, heroChips: heroChips(summary) }))
|
||||
.catch((e) => toastErr(e));
|
||||
},
|
||||
loadTasks() {
|
||||
@@ -84,9 +112,19 @@ Page({
|
||||
const date = this.data.selectedDate || this.data.todayDate;
|
||||
api
|
||||
.getTasks(id, date)
|
||||
.then((tasks) => this.setData({ tasks: (tasks || []).map(mapTask) }))
|
||||
.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;
|
||||
@@ -111,7 +149,9 @@ Page({
|
||||
api
|
||||
.toggleTask(t.id)
|
||||
.then((res) => {
|
||||
this.setData({ [`tasks[${i}].done`]: res.done });
|
||||
const tasks = this.data.tasks.slice();
|
||||
tasks[i] = Object.assign({}, tasks[i], { done: res.done });
|
||||
this.setTasks(tasks);
|
||||
this.loadSummary();
|
||||
})
|
||||
.catch(() => {});
|
||||
@@ -122,7 +162,7 @@ Page({
|
||||
api
|
||||
.completeAllTasks(id)
|
||||
.then((tasks) => {
|
||||
this.setData({ tasks: (tasks || []).map(mapTask) });
|
||||
this.setTasks((tasks || []).map(mapTask));
|
||||
this.loadSummary();
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"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"
|
||||
"fab": "/components/fab/fab",
|
||||
"pt-icon": "/components/pt-icon/index",
|
||||
"stat-ring": "/components/stat-ring/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,30 +5,34 @@
|
||||
<view class="top-row">
|
||||
<view class="greeting">
|
||||
<view class="greet-h2">{{summary.greeting || '你好'}}</view>
|
||||
<view class="greet-p">今天也要照顾好 {{pet.name}} 🐾</view>
|
||||
<view class="greet-p">今天也要照顾好 {{pet.name}}</view>
|
||||
</view>
|
||||
<view class="icon-stack">
|
||||
<view class="icon-btn" data-type="reminders" bindtap="openSheet">🔔</view>
|
||||
<view class="icon-btn" bindtap="goProfile">⚙</view>
|
||||
<view class="icon-btn" data-type="reminders" bindtap="openSheet"><pt-icon name="bell" size="{{36}}"></pt-icon></view>
|
||||
<view class="icon-btn" bindtap="goProfile"><pt-icon name="settings" size="{{36}}"></pt-icon></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<pet-switch bind:add="onAddPet"></pet-switch>
|
||||
|
||||
<view class="card hero-card">
|
||||
<view class="pet-row" bindtap="onEditPet">
|
||||
<view class="avatar">{{pet.emoji}}</view>
|
||||
<view class="pet-main">
|
||||
<view class="pet-name-b">{{pet.name}}</view>
|
||||
<view class="pet-meta">{{pet.age}}|{{pet.weight}}|{{pet.stage}}</view>
|
||||
<!-- 门面:完成度环 + 当下状态。点进去编辑档案 -->
|
||||
<view class="card hero-card" bindtap="onEditPet">
|
||||
<view class="hero-row">
|
||||
<stat-ring percent="{{summary.health_pct}}" size="{{168}}">
|
||||
<view class="ring-num">{{summary.health_pct}}<text class="ring-unit">%</text></view>
|
||||
<view class="ring-cap">完成度</view>
|
||||
</stat-ring>
|
||||
<view class="hero-main">
|
||||
<view class="hero-title">{{pet.name || '还没有毛孩子'}}</view>
|
||||
<view class="hero-sub">{{undoneText}}</view>
|
||||
<view class="chip-row">
|
||||
<view wx:for="{{heroChips}}" wx:key="text" class="chip {{item.tone}}">
|
||||
<pt-icon name="{{item.icon}}" size="{{22}}"></pt-icon>{{item.text}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tag">{{summary.health_status || '正常'}}</view>
|
||||
</view>
|
||||
<view class="progress-row"><text>本周健康完成度</text><text>{{summary.health_pct}}%</text></view>
|
||||
<view class="bar"><view class="bar-fill" style="width:{{summary.health_pct}}%"></view></view>
|
||||
<scroll-view wx:if="{{summary.insights.length}}" class="insight-row" scroll-x="true" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||
<view wx:for="{{summary.insights}}" wx:key="text" class="insight"><text class="insight-b">{{item.bold}}</text>{{item.text}}</view>
|
||||
</scroll-view>
|
||||
<view class="hero-meta">{{pet.age}}|{{pet.weight}}|{{pet.stage}}</view>
|
||||
</view>
|
||||
|
||||
<view class="date-strip">
|
||||
@@ -50,11 +54,11 @@
|
||||
</view>
|
||||
<view class="task-list">
|
||||
<view wx:for="{{tasks}}" wx:key="title" class="task {{item.done ? 'done' : ''}}" data-index="{{index}}" bindtap="onTapTask">
|
||||
<view class="circle">✓</view>
|
||||
<view class="task-text"><text class="tt-b">{{item.title}}</text><view class="tt-p">{{item.sub}}</view></view>
|
||||
<view class="circle"><pt-icon wx:if="{{item.done}}" name="check" size="{{22}}"></pt-icon></view>
|
||||
<view class="task-text"><text class="tt-b">{{item.title}}</text><view wx:if="{{item.sub}}" class="tt-p">{{item.sub}}</view></view>
|
||||
<view wx:if="{{item.priority}}" class="priority">{{item.priority}}</view>
|
||||
</view>
|
||||
<view wx:if="{{tasks.length === 0}}" class="task-empty">这天没有任务记录</view>
|
||||
<view wx:if="{{tasks.length === 0}}" class="empty">这天没有任务记录</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -64,23 +68,20 @@
|
||||
<view class="link" bindtap="goRecord">更多</view>
|
||||
</view>
|
||||
<view class="quick-grid">
|
||||
<view class="quick" data-type="weight" bindtap="openSheet"><text class="q-ic">⚖️</text>体重</view>
|
||||
<view class="quick" data-type="poop" bindtap="openSheet"><text class="q-ic">💩</text>便便</view>
|
||||
<view class="quick" data-type="food" bindtap="openSheet"><text class="q-ic">🍽️</text>饮食</view>
|
||||
<view class="quick" data-type="symptom" bindtap="openSheet"><text class="q-ic">🤒</text>异常</view>
|
||||
<view class="quick" data-type="cost" bindtap="openSheet"><text class="q-ic">💰</text>消费</view>
|
||||
<view class="quick" data-type="medicine" bindtap="openSheet"><text class="q-ic">💊</text>用药</view>
|
||||
<view class="quick" data-type="photo" bindtap="openSheet"><text class="q-ic">📷</text>照片</view>
|
||||
<view class="quick" data-type="vaccine" bindtap="openSheet"><text class="q-ic">💉</text>疫苗</view>
|
||||
<view wx:for="{{quickItems}}" wx:key="type" class="quick {{item.tone}}" data-type="{{item.type}}" bindtap="openSheet">
|
||||
<pt-icon name="{{item.icon}}" size="{{44}}"></pt-icon>{{item.label}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card ai-banner">
|
||||
<view class="card">
|
||||
<view class="section-head">
|
||||
<view class="sh-title">AI 今日建议</view>
|
||||
<view class="link" data-type="ai" bindtap="openSheet">问问 AI</view>
|
||||
</view>
|
||||
<view class="ai-text">{{summary.advice || '正在生成今日建议…'}}</view>
|
||||
<view class="btn btn-dark btn-block ai-cta" data-type="ai" bindtap="openSheet">
|
||||
<pt-icon name="ai" size="{{32}}"></pt-icon>问问 AI 养宠助手
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
@@ -89,7 +90,7 @@
|
||||
<view class="link" bindtap="goLearn">查看</view>
|
||||
</view>
|
||||
<view wx:if="{{firstArticle}}" class="article-card no-shadow" style="margin-bottom:0" bindtap="openFirstArticle">
|
||||
<view class="article-icon">{{firstArticle.icon}}</view>
|
||||
<view class="article-icon"><pt-icon name="{{firstArticle.icon}}" size="{{48}}" fallback="book"></pt-icon></view>
|
||||
<view><text class="ac-b">{{firstArticle.title}}</text><view class="ac-p">{{firstArticle.desc}}</view></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,51 +1,49 @@
|
||||
.top-row{display:flex;align-items:center;justify-content:space-between;margin:4rpx 0 28rpx}
|
||||
.greet-h2{font-size:44rpx;line-height:1.15;letter-spacing:-.8rpx;font-weight:800}
|
||||
.greet-p{font-size:26rpx;color:var(--muted);margin-top:10rpx}
|
||||
.icon-stack{display:flex;gap:18rpx}
|
||||
.top-row{display:flex;align-items:center;justify-content:space-between;margin:4rpx 0 var(--sp-4)}
|
||||
.greet-h2{font-size:var(--fs-xl);line-height:1.15;letter-spacing:-.8rpx;font-weight:var(--fw-b)}
|
||||
.greet-p{font-size:var(--fs-sm);color:var(--muted);margin-top:var(--sp-1)}
|
||||
.icon-stack{display:flex;gap:var(--sp-2)}
|
||||
.icon-btn{
|
||||
width:80rpx;height:80rpx;border-radius:50%;background:#fff;box-shadow:var(--shadow);
|
||||
font-size:36rpx;display:flex;align-items:center;justify-content:center;
|
||||
width:76rpx;height:76rpx;border-radius:50%;background:#fff;box-shadow:var(--sd-1);
|
||||
color:var(--text-2);display:flex;align-items:center;justify-content:center;
|
||||
}
|
||||
|
||||
/* 门面卡 */
|
||||
.hero-card{
|
||||
background:
|
||||
radial-gradient(circle at 95% 16%, rgba(245,169,71,.25), transparent 30%),
|
||||
linear-gradient(135deg,#FFFFFF,#FFF5E6);
|
||||
min-height:340rpx;
|
||||
radial-gradient(circle at 95% 16%, rgba(245,169,71,.22), transparent 32%),
|
||||
linear-gradient(135deg,#FFFFFF,#FFF6E9);
|
||||
}
|
||||
.pet-row{display:flex;align-items:center;gap:26rpx;margin-bottom:28rpx}
|
||||
.pet-main{flex:1}
|
||||
.pet-name-b{font-size:42rpx;letter-spacing:-.4rpx;font-weight:800}
|
||||
.pet-meta{color:var(--muted);font-size:26rpx;margin-top:8rpx}
|
||||
|
||||
.insight-row{white-space:nowrap;margin-top:24rpx}
|
||||
.insight{
|
||||
display:inline-block;min-width:224rpx;border-radius:32rpx;
|
||||
background:rgba(255,255,255,.72);border:1rpx solid rgba(255,255,255,.9);
|
||||
padding:20rpx;font-size:24rpx;margin-right:16rpx;vertical-align:top;
|
||||
.hero-row{display:flex;align-items:center;gap:var(--sp-4)}
|
||||
.ring-num{font-size:var(--fs-xl);font-weight:var(--fw-b);line-height:1}
|
||||
.ring-unit{font-size:var(--fs-cap)}
|
||||
.ring-cap{font-size:var(--fs-cap);color:var(--muted);margin-top:4rpx}
|
||||
.hero-main{flex:1;min-width:0}
|
||||
.hero-title{font-size:var(--fs-lg);font-weight:var(--fw-b);letter-spacing:-.4rpx}
|
||||
.hero-sub{color:var(--muted);font-size:var(--fs-sm);margin-top:4rpx}
|
||||
.chip-row{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-2)}
|
||||
.hero-meta{
|
||||
color:var(--muted);font-size:var(--fs-sm);margin-top:var(--sp-3);
|
||||
padding-top:var(--sp-3);border-top:1rpx solid rgba(238,230,220,.8);
|
||||
}
|
||||
.insight-b{display:block;font-size:28rpx;margin-bottom:8rpx;font-weight:900}
|
||||
|
||||
.date-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:14rpx;margin-bottom:28rpx}
|
||||
/* 一周日期条 */
|
||||
.date-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--sp-2);margin-bottom:var(--sp-4)}
|
||||
.day{
|
||||
background:#fff;border-radius:32rpx;min-height:116rpx;text-align:center;padding:16rpx 8rpx;
|
||||
border:1rpx solid var(--line);color:var(--muted);font-size:22rpx;
|
||||
background:#fff;border-radius:var(--r-md);min-height:112rpx;text-align:center;padding:var(--sp-3) 4rpx;
|
||||
border:1rpx solid var(--line);color:var(--muted);font-size:var(--fs-cap);
|
||||
}
|
||||
.day-b{display:block;color:var(--text);font-size:30rpx;margin-top:8rpx;font-weight:700}
|
||||
.day.active{border-color:#FFD39A;background:#FFF4E4;color:var(--primary-dark)}
|
||||
.day.sel{border-color:var(--primary);background:#FFEFD6;color:var(--primary-dark);box-shadow:0 0 0 2rpx rgba(245,169,71,.35)}
|
||||
.day.has-dot::after{content:"";display:block;width:10rpx;height:10rpx;border-radius:50%;background:var(--green);margin:8rpx auto 0}
|
||||
.task-empty{padding:36rpx 8rpx;text-align:center;color:var(--muted);font-size:26rpx}
|
||||
.day-b{display:block;color:var(--text);font-size:var(--fs-md);margin-top:6rpx;font-weight:var(--fw-b)}
|
||||
.day.active{border-color:var(--primary-line);background:var(--primary-soft);color:var(--primary-dark)}
|
||||
.day.sel{border-color:var(--primary);background:var(--primary-soft);color:var(--primary-dark);box-shadow:0 0 0 2rpx rgba(245,169,71,.35)}
|
||||
.day.has-dot::after{content:"";display:block;width:8rpx;height:8rpx;border-radius:50%;background:var(--green);margin:6rpx auto 0}
|
||||
|
||||
.quick-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:18rpx}
|
||||
/* 快速记录:靠颜色分类,不靠 emoji */
|
||||
.quick-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-2)}
|
||||
.quick{
|
||||
background:#FBFAF8;border-radius:32rpx;min-height:132rpx;
|
||||
display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12rpx;
|
||||
font-weight:900;color:var(--text);font-size:24rpx;
|
||||
border-radius:var(--r-md);min-height:128rpx;
|
||||
display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-1);
|
||||
font-weight:var(--fw-b);font-size:var(--fs-sm);
|
||||
}
|
||||
.q-ic{font-size:44rpx}
|
||||
|
||||
.ai-banner{background:linear-gradient(135deg,#F2FAF6,#FFFFFF)}
|
||||
.ai-text{color:#59635D;font-size:28rpx;line-height:1.6}
|
||||
|
||||
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
|
||||
.ai-text{color:var(--text-2);font-size:var(--fs-md);line-height:1.6}
|
||||
.ai-cta{margin-top:var(--sp-4)}
|
||||
|
||||
Reference in New Issue
Block a user