feat(fe): 其余 8 页 + 三个薄组件套用新设计语言

页面:
- record:3×3 emoji 九宫格(184rpx 高、全站最大的点击块)换成图标 + 4 色分类,
  和首页的 8 宫格合并成同一套 .quick-grid,此前它俩是同一个东西的两个副本
- record/plan/community:三套横向切换全部换成 seg-tabs
- record/plan/community/report/learn:砍掉与 nav-bar 重复的 46rpx 大标题,
  每页白赚约 130rpx 首屏
- 时间轴图标改按 HealthRecord.Type 取(9 值枚举,可靠),老数据里的 emoji
  只作兜底;写库的 icon 字段一个字符没动
- profile:设置行加分类图标 + 右侧箭头换图标
- community/report:主操作改深色 CTA,btn-primary 继续留给弹层的「保存」
- 6 套空态写法统一成 .empty

组件:
- nav-bar 的 ‹ 和 fab 的 "AI" 文字换成图标;fab 从蓝渐变改深色,
  和全站唯一主 CTA 的语义对齐
- pet-switch 的「+」换图标;宠物自己的 emoji 是用户数据,保留

pt-icon 开了 virtualHost,组件标签上的 class 不生效,需要定位的地方
(profile 行图标、箭头)都用外层 view 包了一层。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-29 10:54:18 +08:00
parent c076968b2c
commit 2fd25e26ef
36 changed files with 305 additions and 291 deletions
+12
View File
@@ -180,6 +180,16 @@ page{scrollbar-width:none;-ms-overflow-style:none}
.tone-3{background:var(--blue-soft);color:var(--blue-ink)}
.tone-4{background:var(--purple-soft);color:var(--purple-ink)}
/* 记录入口宫格。首页(4 列 8 个)和记录页(3 列 9 个)原本是同一个东西的两个副本,
高度/圆角/阴影/字号全不一样,这里合成一套。 */
.quick-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-2)}
.quick-grid.cols-3{grid-template-columns:repeat(3,1fr)}
.quick{
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);
}
/* 空态。原本有 6 套长得差不多的写法散在各页 */
.empty{padding:56rpx var(--sp-1);text-align:center;color:var(--muted);font-size:var(--fs-sm)}
.empty.lg{padding:96rpx 0}
@@ -288,6 +298,8 @@ page{scrollbar-width:none;-ms-overflow-style:none}
/* 统计格 */
.stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-3);margin:var(--sp-4) 0}
.stat{background:#fff;border:1rpx solid var(--line);border-radius:var(--r-md);padding:var(--sp-4) var(--sp-2);text-align:center}
.stat-b{display:block;font-size:var(--fs-xl);margin-bottom:6rpx;font-weight:var(--fw-b)}
.stat-s{font-size:var(--fs-cap);color:var(--muted)}
/* 文章卡 */
.article-card{
+3 -1
View File
@@ -1,4 +1,6 @@
{
"component": true,
"usingComponents": {}
"usingComponents": {
"pt-icon": "/components/pt-icon/index"
}
}
+1 -1
View File
@@ -1 +1 @@
<view class="fab" bindtap="onTap">AI</view>
<view class="fab" bindtap="onTap"><pt-icon name="ai" size="{{48}}"></pt-icon></view>
+4 -4
View File
@@ -1,10 +1,10 @@
.fab{
position:fixed;right:36rpx;bottom:calc(190rpx + env(safe-area-inset-bottom));
width:116rpx;height:116rpx;border-radius:44rpx;
background:linear-gradient(135deg,#78A6E8,#9FC0F5);
color:#fff;font-weight:900;font-size:40rpx;
width:108rpx;height:108rpx;border-radius:var(--r-md);
background:var(--cta);
color:#fff;
display:flex;align-items:center;justify-content:center;
box-shadow:0 28rpx 60rpx rgba(120,166,232,.32);
box-shadow:var(--sd-2);
z-index:60;
}
.fab:active{transform:scale(.96)}
+3 -1
View File
@@ -1,4 +1,6 @@
{
"component": true,
"usingComponents": {}
"usingComponents": {
"pt-icon": "/components/pt-icon/index"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<view class="nav-wrap" style="padding-top:{{statusBarHeight}}px;height:{{statusBarHeight + navBarHeight}}px;">
<view class="nav-inner" style="height:{{navBarHeight}}px;">
<view wx:if="{{showBack}}" class="nav-back" bindtap="onBack"></view>
<view wx:if="{{showBack}}" class="nav-back" bindtap="onBack"><pt-icon name="back" size="{{40}}"></pt-icon></view>
<view class="nav-title">{{title}}</view>
</view>
</view>
+2 -2
View File
@@ -9,11 +9,11 @@
position:relative;display:flex;align-items:center;justify-content:center;
}
.nav-title{
font-size:32rpx;font-weight:900;color:var(--text);
font-size:var(--fs-lg);font-weight:var(--fw-b);color:var(--text);
max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.nav-back{
position:absolute;left:20rpx;top:50%;transform:translateY(-50%);
width:64rpx;height:64rpx;display:flex;align-items:center;justify-content:center;
font-size:52rpx;line-height:1;color:var(--text);
color:var(--text);
}
@@ -1,4 +1,6 @@
{
"component": true,
"usingComponents": {}
"usingComponents": {
"pt-icon": "/components/pt-icon/index"
}
}
@@ -5,6 +5,6 @@
<view class="mini-avatar">{{item.emoji}}</view>{{item.name}}
</view>
<view class="pet-chip" bindtap="onAdd">
<view class="mini-avatar"></view>添加
<view class="mini-avatar"><pt-icon name="plus" size="{{30}}"></pt-icon></view>添加
</view>
</scroll-view>
+2 -1
View File
@@ -1,6 +1,7 @@
{
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar"
"nav-bar": "/components/nav-bar/nav-bar",
"pt-icon": "/components/pt-icon/index"
},
"navigationStyle": "custom",
"navigationBarTitleText": "新手知识"
+4 -4
View File
@@ -2,19 +2,19 @@
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body no-fab">
<view wx:if="{{loading}}" class="hint">加载中…</view>
<view wx:elif="{{error}}" class="hint">{{error}}</view>
<view wx:if="{{loading}}" class="empty lg">加载中…</view>
<view wx:elif="{{error}}" class="empty lg">{{error}}</view>
<block wx:elif="{{article}}">
<view class="art-head">
<view class="art-icon">{{article.icon}}</view>
<view class="art-icon"><pt-icon name="{{article.icon}}" size="{{56}}" fallback="book"></pt-icon></view>
<view class="art-title">{{article.title}}</view>
<view wx:if="{{article.description}}" class="art-desc">{{article.description}}</view>
</view>
<view class="card">
<view wx:if="{{article.content}}" class="art-content">{{article.content}}</view>
<view wx:else class="hint">这篇文章还没有正文。</view>
<view wx:else class="empty">这篇文章还没有正文。</view>
</view>
<button class="btn btn-primary btn-block" bindtap="goRecord">去记一笔</button>
+10 -8
View File
@@ -1,13 +1,15 @@
.hint{padding:60rpx 0;text-align:center;color:var(--muted);font-size:28rpx}
.art-head{padding:12rpx 0 28rpx}
.art-icon{font-size:72rpx;line-height:1;margin-bottom:16rpx}
.art-title{font-size:44rpx;font-weight:800;line-height:1.35;letter-spacing:-.5rpx}
.art-desc{margin-top:14rpx;color:var(--muted);font-size:27rpx;line-height:1.6}
.art-head{padding:var(--sp-2) 0 var(--sp-4)}
.art-icon{
width:96rpx;height:96rpx;border-radius:var(--r-md);margin-bottom:var(--sp-3);
display:flex;align-items:center;justify-content:center;
background:var(--primary-soft);color:var(--primary-ink);
}
.art-title{font-size:var(--fs-2xl);font-weight:var(--fw-b);line-height:1.35;letter-spacing:-.5rpx}
.art-desc{margin-top:var(--sp-2);color:var(--muted);font-size:var(--fs-md);line-height:1.6}
.art-content{
font-size:29rpx;line-height:1.85;color:#3D352E;
font-size:var(--fs-md);line-height:1.85;color:var(--text-2);
white-space:pre-line; /* 正文用真实换行排版 */
}
.art-foot{margin-top:28rpx;text-align:center;color:var(--muted);font-size:23rpx}
.art-foot{margin-top:var(--sp-4);text-align:center;color:var(--muted);font-size:var(--fs-cap)}
+2 -1
View File
@@ -67,8 +67,9 @@ Page({
toastErr(e);
});
},
// 来自 seg-tabs 的 change 事件
switchFeedTab(e) {
this.setData({ feedIdx: e.currentTarget.dataset.index });
this.setData({ feedIdx: Number(e.detail.index) });
this.loadPosts();
},
likePost(e) {
+3 -1
View File
@@ -2,6 +2,8 @@
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar",
"bottom-sheet": "/components/bottom-sheet/bottom-sheet",
"fab": "/components/fab/fab"
"fab": "/components/fab/fab",
"pt-icon": "/components/pt-icon/index",
"seg-tabs": "/components/seg-tabs/index"
}
}
+7 -15
View File
@@ -5,18 +5,10 @@ module.exports.isUrl = function (s) { return s && s.indexOf('http') === 0; }
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body">
<view class="page-title">
<view class="pt-h2">宠友圈</view>
<view class="pt-p">和宠友交流、晒宠、求助与经验分享。</view>
</view>
<scroll-view class="feed-tabs" scroll-x="true" enhanced="{{true}}" show-scrollbar="{{false}}">
<view wx:for="{{feedTabs}}" wx:key="*this"
class="feed-tab {{feedIdx === index ? 'active' : ''}}" data-index="{{index}}" bindtap="switchFeedTab">{{item}}</view>
</scroll-view>
<seg-tabs items="{{feedTabs}}" current="{{feedIdx}}" bind:change="switchFeedTab"></seg-tabs>
<view class="compose-card">
<button class="btn btn-primary btn-block" bindtap="openSheet" data-type="createPost"> 发布图文</button>
<button class="btn btn-dark btn-block" bindtap="openSheet" data-type="createPost"><pt-icon name="plus" size="{{30}}"></pt-icon>发布图文</button>
</view>
<view wx:for="{{posts}}" wx:key="id" class="post-card" bindlongpress="onLongPressPost" data-index="{{index}}">
@@ -35,17 +27,17 @@ module.exports.isUrl = function (s) { return s && s.indexOf('http') === 0; }
</block>
</view>
<view class="post-actions">
<view class="pa-btn {{item.liked ? 'liked' : ''}}" data-index="{{index}}" bindtap="likePost">{{item.liked ? '♥' : '♡'}} {{item.like_count}}</view>
<view class="pa-btn" data-id="{{item.id}}" bindtap="openComments">💬 {{item.comment_count}}</view>
<button class="pa-btn pa-share" open-type="share" data-content="{{item.content}}">分享</button>
<view class="pa-btn {{item.liked ? 'liked' : ''}}" data-index="{{index}}" bindtap="likePost"><pt-icon name="{{item.liked ? 'like-on' : 'like'}}" size="{{30}}"></pt-icon>{{item.like_count}}</view>
<view class="pa-btn" data-id="{{item.id}}" bindtap="openComments"><pt-icon name="comment" size="{{30}}"></pt-icon>{{item.comment_count}}</view>
<button class="pa-btn pa-share" open-type="share" data-content="{{item.content}}"><pt-icon name="share" size="{{30}}"></pt-icon>分享</button>
</view>
</view>
<view wx:if="{{loadErr}}" class="empty-hint">
<view wx:if="{{loadErr}}" class="empty lg">
{{loadErr}}
<view class="retry-btn" bindtap="loadPosts">点击重试</view>
</view>
<view wx:elif="{{loaded && posts.length === 0}}" class="empty-hint">
<view wx:elif="{{loaded && posts.length === 0}}" class="empty lg">
{{feedTabs[feedIdx] === '关注' ? '还没有关注的人,去「推荐」里关注几个宠友吧 🐾' : '还没有帖子,来发第一条吧 🐾'}}
</view>
</view>
+30 -36
View File
@@ -1,50 +1,44 @@
.feed-tabs{white-space:nowrap;padding:0 0 24rpx}
.feed-tab{
display:inline-block;border:1rpx solid var(--line);background:#fff;border-radius:999rpx;
height:68rpx;line-height:68rpx;padding:0 26rpx;color:var(--muted);font-size:26rpx;font-weight:900;
box-shadow:0 12rpx 32rpx rgba(70,48,25,.05);margin-right:16rpx;
}
.feed-tab.active{color:var(--primary-dark);background:#FFF4E4;border-color:#FFD39A}
.compose-card{margin-bottom:var(--sp-4)}
.compose-card{margin-bottom:28rpx}
.post-card{background:#fff;border-radius:48rpx;box-shadow:var(--shadow);padding:30rpx;margin-bottom:28rpx}
.post-head{display:flex;align-items:center;gap:20rpx;margin-bottom:22rpx}
.post-card{background:#fff;border-radius:var(--r-lg);box-shadow:var(--sd-1);padding:var(--sp-4);margin-bottom:var(--sp-4)}
.post-head{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-3)}
.post-avatar{
width:84rpx;height:84rpx;border-radius:32rpx;background:#FFF0D8;
display:flex;align-items:center;justify-content:center;font-size:44rpx;flex:none;
width:80rpx;height:80rpx;border-radius:var(--r-sm);background:var(--primary-soft);
display:flex;align-items:center;justify-content:center;font-size:40rpx;flex:none;
}
.post-user{flex:1}
.pu-b{font-size:28rpx;display:block;font-weight:700}
.ai-tag{display:inline-block;margin-left:10rpx;padding:0 10rpx;border-radius:8rpx;background:#EAF0FF;color:#3B6BE5;font-size:20rpx;font-weight:700;vertical-align:1rpx}
.pu-s{font-size:24rpx;color:var(--muted)}
.post-content{font-size:28rpx;line-height:1.62;color:#443D37;margin-bottom:24rpx}
.post-user{flex:1;min-width:0}
.pu-b{font-size:var(--fs-md);display:block;font-weight:var(--fw-b)}
.ai-tag{
display:inline-block;margin-left:var(--sp-1);padding:0 var(--sp-1);border-radius:var(--r-xs);
background:var(--blue-soft);color:var(--blue-ink);font-size:var(--fs-cap);font-weight:var(--fw-b);vertical-align:1rpx;
}
.pu-s{font-size:var(--fs-sm);color:var(--muted)}
.post-content{font-size:var(--fs-md);line-height:1.62;color:var(--text-2);margin-bottom:var(--sp-3)}
.photo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12rpx;margin-bottom:24rpx}
.photo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-1);margin-bottom:var(--sp-3)}
.photo-tile{
aspect-ratio:1;border-radius:32rpx;background:linear-gradient(135deg,#FFE6BA,#FFF8ED);
display:flex;align-items:center;justify-content:center;font-size:60rpx;
aspect-ratio:1;border-radius:var(--r-sm);background:var(--primary-soft);
display:flex;align-items:center;justify-content:center;font-size:56rpx;
}
.post-actions{display:flex;justify-content:space-around;border-top:1rpx solid var(--line);padding-top:20rpx}
.pa-btn{color:var(--muted);font-weight:900;font-size:26rpx}
.pa-btn.liked{color:var(--red)}
.empty-hint{text-align:center;color:var(--muted);font-size:26rpx;padding:80rpx 0}
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
/* 帖子分享按钮:用微信原生 open-type=share,需抹掉 button 默认外观 */
.pa-share{
background:transparent;border:none;padding:0;margin:0;line-height:inherit;
font-size:26rpx;font-weight:900;color:var(--muted);
.post-actions{display:flex;justify-content:space-around;border-top:1rpx solid var(--line);padding-top:var(--sp-3)}
.pa-btn{
display:flex;align-items:center;gap:var(--sp-1);
color:var(--muted);font-weight:var(--fw-b);font-size:var(--fs-sm);
}
.pa-btn.liked{color:var(--red-ink)}
/* 分享用微信原生 open-type=share,得抹掉 button 默认外观 */
.pa-share{background:transparent;border:none;padding:0;margin:0;line-height:inherit;height:auto}
.pa-share::after{border:none}
/* 关注按钮 */
.follow-btn{
flex:none;font-size:22rpx;font-weight:700;padding:6rpx 18rpx;border-radius:999rpx;
color:var(--primary-dark);background:#FFF2DC;border:1rpx solid #FFD39A;
flex:none;font-size:var(--fs-cap);font-weight:var(--fw-b);padding:6rpx var(--sp-3);border-radius:var(--r-full);
color:var(--primary-dark);background:var(--primary-soft);border:1rpx solid var(--primary-line);
}
.follow-btn.on{color:var(--muted);background:#F4F1EC;border-color:var(--line)}
.retry-btn{margin-top:20rpx;display:inline-block;padding:12rpx 36rpx;border-radius:999rpx;background:#FFF2DC;color:var(--primary-dark);font-weight:700;font-size:26rpx}
.retry-btn{
margin-top:var(--sp-3);display:inline-block;padding:var(--sp-2) var(--sp-6);border-radius:var(--r-full);
background:var(--primary-soft);color:var(--primary-dark);font-weight:var(--fw-b);font-size:var(--fs-sm);
}
-8
View File
@@ -37,13 +37,5 @@
.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}
/* 快速记录:靠颜色分类,不靠 emoji */
.quick-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-2)}
.quick{
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);
}
.ai-text{color:var(--text-2);font-size:var(--fs-md);line-height:1.6}
.ai-cta{margin-top:var(--sp-4)}
+2 -1
View File
@@ -2,6 +2,7 @@
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar",
"bottom-sheet": "/components/bottom-sheet/bottom-sheet",
"fab": "/components/fab/fab"
"fab": "/components/fab/fab",
"pt-icon": "/components/pt-icon/index"
}
}
+3 -3
View File
@@ -3,12 +3,12 @@
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body">
<view class="page-title">
<view class="pt-h2">新手养宠知识</view>
<view class="pt-p">新手养宠知识,看完顺手生成你的计划。</view>
<view class="pt-p">看完顺手生成你的计划。</view>
</view>
<view wx:if="{{articles.length === 0}}" class="empty lg">还没有文章</view>
<view wx:for="{{articles}}" wx:key="id" class="article-card" data-id="{{item.id}}" bindtap="openArticle">
<view class="article-icon">{{item.icon}}</view>
<view class="article-icon"><pt-icon name="{{item.icon}}" size="{{48}}" fallback="book"></pt-icon></view>
<view><text class="ac-b">{{item.title}}</text><view class="ac-p">{{item.desc}}</view></view>
</view>
</view>
+2 -1
View File
@@ -1,6 +1,7 @@
{
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar"
"nav-bar": "/components/nav-bar/nav-bar",
"pt-icon": "/components/pt-icon/index"
},
"navigationBarTitleText": "肉垫计划"
}
+9 -9
View File
@@ -5,7 +5,7 @@
<!-- step 0 欢迎 -->
<view wx:if="{{step === 0}}" class="step">
<view class="hero">
<view class="pet-logo">🐾</view>
<view class="pet-logo"><pt-icon name="community" size="{{88}}" color="#fff"></pt-icon></view>
<view class="hero-h1">肉垫计划</view>
<view class="hero-sub">新手养宠的 AI 日历、健康记录和成长报告。先生成计划,再每天照着做。</view>
</view>
@@ -24,10 +24,10 @@
<view class="step-sub">目前支持猫和狗,选一个开始。</view>
<view class="choice-grid">
<view class="choice-card {{petType === '猫猫' ? 'selected' : ''}}" data-type="猫猫" data-emoji="🐱" bindtap="choosePet">
<view class="animal">🐱</view><view class="choice-b">猫猫</view><view class="choice-s">幼猫、成年猫、老年猫</view>
<view class="animal"><pt-icon name="cat" size="{{52}}"></pt-icon></view><view class="choice-b">猫猫</view><view class="choice-s">幼猫、成年猫、老年猫</view>
</view>
<view class="choice-card {{petType === '狗狗' ? 'selected' : ''}}" data-type="狗狗" data-emoji="🐶" bindtap="choosePet">
<view class="animal">🐶</view><view class="choice-b">狗狗</view><view class="choice-s">幼犬、成年犬、老年犬</view>
<view class="animal"><pt-icon name="dog" size="{{52}}"></pt-icon></view><view class="choice-b">狗狗</view><view class="choice-s">幼犬、成年犬、老年犬</view>
</view>
</view>
<button class="btn btn-primary btn-block" bindtap="nextStep">下一步</button>
@@ -78,7 +78,7 @@
<view class="step-sub">多选,我们会据此为你定制计划。</view>
<view class="check-list">
<view wx:for="{{goals}}" wx:key="label" class="check {{item.on ? 'selected' : ''}}" data-index="{{index}}" bindtap="toggleGoal">
<view class="box">{{item.on ? '✓' : ''}}</view>{{item.label}}
<view class="box"><pt-icon wx:if="{{item.on}}" name="check" size="{{26}}"></pt-icon></view>{{item.label}}
</view>
</view>
<button class="btn btn-primary btn-block" bindtap="nextStep">生成专属计划</button>
@@ -89,11 +89,11 @@
<view class="step-title">正在生成专属养宠计划</view>
<view class="step-sub">正在根据它的信息生成专属计划…</view>
<view class="gen-card">
<view class="gen-line"><view class="tick"></view> 已识别:{{stage}}</view>
<view class="gen-line"><view class="tick"></view> 已生成:30 天新手计划</view>
<view class="gen-line"><view class="tick"></view> 已安排:疫苗 / 驱虫提醒</view>
<view class="gen-line"><view class="tick"></view> 已开启:体重、便便、饮食记录</view>
<view class="gen-line"><view class="tick"></view> 已准备:首张成长报告卡片</view>
<view class="gen-line"><view class="tick"><pt-icon name="check" size="{{26}}"></pt-icon></view> 已识别:{{stage}}</view>
<view class="gen-line"><view class="tick"><pt-icon name="check" size="{{26}}"></pt-icon></view> 已生成:30 天新手计划</view>
<view class="gen-line"><view class="tick"><pt-icon name="check" size="{{26}}"></pt-icon></view> 已安排:疫苗 / 驱虫提醒</view>
<view class="gen-line"><view class="tick"><pt-icon name="check" size="{{26}}"></pt-icon></view> 已开启:体重、便便、饮食记录</view>
<view class="gen-line"><view class="tick"><pt-icon name="check" size="{{26}}"></pt-icon></view> 已准备:首张成长报告卡片</view>
</view>
<button class="btn btn-primary btn-block" style="margin-top:36rpx" bindtap="finishOnboard">查看今日任务</button>
</view>
+36 -30
View File
@@ -1,53 +1,59 @@
.onboard-page{
padding:0 40rpx 56rpx;
padding:0 var(--pad-x) 56rpx;
min-height:100%;
background:
radial-gradient(circle at top right, rgba(245,169,71,.22), transparent 45%),
var(--bg);
}
.step{animation:fade .24s ease}
.skip-link{text-align:center;margin-top:28rpx;color:var(--muted);font-size:28rpx;padding:12rpx}
.skip-link{text-align:center;margin-top:var(--sp-4);color:var(--muted);font-size:var(--fs-md);padding:var(--sp-2)}
@keyframes fade{from{opacity:0;transform:translateY(16rpx)}to{opacity:1;transform:none}}
.hero{text-align:center;padding:60rpx 0 32rpx}
.hero{text-align:center;padding:60rpx 0 var(--sp-5)}
.pet-logo{
width:192rpx;height:192rpx;margin:0 auto 36rpx;border-radius:60rpx;
width:176rpx;height:176rpx;margin:0 auto var(--sp-5);border-radius:var(--r-lg);
background:linear-gradient(135deg,#FFE0AB,#FDB35C);
display:flex;align-items:center;justify-content:center;font-size:96rpx;
box-shadow:0 32rpx 72rpx rgba(245,169,71,.26);
display:flex;align-items:center;justify-content:center;font-size:88rpx;
box-shadow:var(--sd-2);
}
.hero-h1{font-size:64rpx;letter-spacing:-1.2rpx;font-weight:800}
.hero-sub{margin-top:20rpx;line-height:1.65;color:var(--muted);font-size:30rpx}
.hero-h1{font-size:var(--fs-2xl);letter-spacing:-1.2rpx;font-weight:var(--fw-b)}
.hero-sub{margin-top:var(--sp-3);line-height:1.65;color:var(--muted);font-size:var(--fs-md)}
.proof-row{display:grid;grid-template-columns:repeat(3,1fr);gap:16rpx;margin:40rpx 0}
.proof{background:#fff;border-radius:36rpx;padding:24rpx 16rpx;text-align:center;box-shadow:var(--shadow)}
.proof-b{display:block;font-size:32rpx;font-weight:800}
.proof-s{color:var(--muted);font-size:22rpx}
.proof-row{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-3);margin:var(--sp-6) 0}
.proof{background:#fff;border-radius:var(--r-md);padding:var(--sp-4) var(--sp-3);text-align:center;box-shadow:var(--sd-1)}
.proof-b{display:block;font-size:var(--fs-lg);font-weight:var(--fw-b)}
.proof-s{color:var(--muted);font-size:var(--fs-cap)}
.step-title{font-size:48rpx;letter-spacing:-.8rpx;line-height:1.24;margin:16rpx 0;font-weight:800}
.step-sub{color:var(--muted);line-height:1.6;font-size:28rpx;margin-bottom:32rpx}
.step-title{font-size:var(--fs-2xl);letter-spacing:-.8rpx;line-height:1.24;margin:var(--sp-3) 0;font-weight:var(--fw-b)}
.step-sub{color:var(--muted);line-height:1.6;font-size:var(--fs-md);margin-bottom:var(--sp-5)}
.choice-grid{display:grid;grid-template-columns:1fr 1fr;gap:24rpx;margin-bottom:36rpx}
.choice-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4);margin-bottom:var(--sp-5)}
.choice-card{
background:#fff;border:4rpx solid transparent;border-radius:48rpx;min-height:264rpx;
padding:36rpx 28rpx;box-shadow:var(--shadow);transition:.18s ease;
background:#fff;border:4rpx solid transparent;border-radius:var(--r-lg);min-height:248rpx;
padding:var(--sp-5) var(--sp-4);box-shadow:var(--sd-1);transition:.18s ease;
display:flex;flex-direction:column;align-items:flex-start;
}
.choice-card.selected{border-color:var(--primary);background:#FFF8EC}
.choice-card .animal{font-size:88rpx;margin-bottom:20rpx}
.choice-b{font-size:34rpx;font-weight:900}
.choice-s{display:block;color:var(--muted);font-size:24rpx;margin-top:12rpx}
.choice-card.selected{border-color:var(--primary);background:var(--primary-soft)}
.choice-card .animal{
width:96rpx;height:96rpx;border-radius:var(--r-md);margin-bottom:var(--sp-3);
display:flex;align-items:center;justify-content:center;
background:var(--primary-soft);color:var(--primary-ink);
}
.choice-card.selected .animal{background:#fff}
.choice-b{font-size:var(--fs-lg);font-weight:var(--fw-b)}
.choice-s{display:block;color:var(--muted);font-size:var(--fs-sm);margin-top:var(--sp-2)}
.form-card{background:#fff;border-radius:48rpx;padding:32rpx;box-shadow:var(--shadow);margin-bottom:36rpx}
.form-card{background:#fff;border-radius:var(--r-lg);padding:var(--sp-5);box-shadow:var(--sd-1);margin-bottom:var(--sp-5)}
.stage-list{display:flex;flex-direction:column;gap:22rpx;margin-bottom:36rpx}
.stage-card{background:#fff;border-radius:40rpx;border:4rpx solid transparent;padding:30rpx;box-shadow:var(--shadow)}
.stage-list{display:flex;flex-direction:column;gap:var(--sp-3);margin-bottom:var(--sp-5)}
.stage-card{background:#fff;border-radius:var(--r-md);border:4rpx solid transparent;padding:var(--sp-4);box-shadow:var(--sd-1)}
.stage-card.selected{border-color:var(--green);background:var(--green-soft)}
.stage-b{font-size:32rpx;font-weight:900}
.stage-p{color:var(--muted);font-size:24rpx;margin-top:10rpx}
.stage-b{font-size:var(--fs-lg);font-weight:var(--fw-b)}
.stage-p{color:var(--muted);font-size:var(--fs-sm);margin-top:var(--sp-1)}
.gen-card{margin-top:32rpx;background:#fff;border-radius:56rpx;padding:48rpx;box-shadow:var(--shadow2)}
.gen-line{display:flex;align-items:center;gap:20rpx;padding:20rpx 0;color:#5F554D;font-size:28rpx}
.gen-card{margin-top:var(--sp-5);background:#fff;border-radius:var(--r-lg);padding:var(--sp-6);box-shadow:var(--sd-2)}
.gen-line{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) 0;color:var(--text-2);font-size:var(--fs-md)}
.tick{
width:48rpx;height:48rpx;border-radius:50%;background:var(--green);color:#fff;
display:flex;align-items:center;justify-content:center;font-size:28rpx;font-weight:900;flex:none;
width:44rpx;height:44rpx;border-radius:50%;background:var(--green);color:#fff;
display:flex;align-items:center;justify-content:center;flex:none;
}
+7 -1
View File
@@ -31,6 +31,11 @@ Page({
data: {
pet: {},
planView: 'timeline',
planTabs: [
{ key: 'timeline', label: '路线图' },
{ key: 'calendar', label: '日历' },
{ key: 'aiPlan', label: 'AI 计划' },
],
plan: null,
calCells: [],
calLabel: '',
@@ -117,8 +122,9 @@ Page({
})
.catch(() => {});
},
// 来自 seg-tabs 的 change 事件,key 即 planView
switchPlanView(e) {
const view = e.currentTarget.dataset.view;
const view = e.detail.key;
this.setData({ planView: view });
if (view === 'calendar' && !this.data.calLoaded) this.loadCalendar();
},
+3 -1
View File
@@ -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",
"seg-tabs": "/components/seg-tabs/index"
}
}
+4 -13
View File
@@ -2,18 +2,9 @@
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body">
<view class="page-title">
<view class="pt-h2">养宠计划</view>
<view class="pt-p">按宠物查看路线图、日历与 AI 计划。</view>
</view>
<pet-switch bind:add="onAddPet"></pet-switch>
<view class="inline-tabs">
<view class="it-btn {{planView === 'timeline' ? 'active' : ''}}" data-view="timeline" bindtap="switchPlanView">路线图</view>
<view class="it-btn {{planView === 'calendar' ? 'active' : ''}}" data-view="calendar" bindtap="switchPlanView">日历</view>
<view class="it-btn {{planView === 'aiPlan' ? 'active' : ''}}" data-view="aiPlan" bindtap="switchPlanView">AI 计划</view>
</view>
<seg-tabs variant="solid" items="{{planTabs}}" current="{{planView}}" bind:change="switchPlanView"></seg-tabs>
<!-- 路线图 -->
<view wx:if="{{planView === 'timeline'}}">
@@ -29,13 +20,13 @@
<view wx:for="{{plan.tasks}}" wx:key="id" class="time-block">
<view class="time-label">{{item.day_label}}<text wx:if="{{item.dateLabel}}" class="time-date">{{item.dateLabel}}</text></view>
<view class="plan-task {{item.done ? 'done' : ''}}">
<view class="pk-check" catchtap="toggleTimelineTask" data-id="{{item.id}}">{{item.done ? '✓' : ''}}</view>
<view class="pk-check" catchtap="toggleTimelineTask" data-id="{{item.id}}"><pt-icon wx:if="{{item.done}}" name="check" size="{{22}}"></pt-icon></view>
<view class="pk-body" data-type="{{item.sheet_type}}" bindtap="openSheet">
<text class="pk-b">{{item.title}}</text><view class="pk-p">{{item.description}}</view>
</view>
</view>
</view>
<view wx:if="{{!plan}}" class="pt-p" style="padding:20rpx 0">暂无计划</view>
<view wx:if="{{!plan}}" class="empty">暂无计划</view>
</view>
</view>
@@ -64,7 +55,7 @@
<text class="dd-tag rem">提醒</text><text class="dd-b">{{item.title}}</text>
</view>
</block>
<view wx:else class="dd-empty">这天没有安排</view>
<view wx:else class="empty">这天没有安排</view>
</view>
</view>
+36 -39
View File
@@ -1,58 +1,55 @@
.inline-tabs{display:flex;padding:8rpx;gap:8rpx;border-radius:32rpx;background:#EFE8DE;margin-bottom:28rpx}
.it-btn{
flex:1;height:68rpx;line-height:68rpx;text-align:center;border-radius:24rpx;
color:var(--muted);font-weight:900;font-size:26rpx;
}
.it-btn.active{background:#fff;color:var(--primary-dark);box-shadow:0 12rpx 28rpx rgba(0,0,0,.06)}
.timeline{padding-left:36rpx;position:relative}
/* 竖线时间轴 */
.timeline{padding-left:var(--sp-6);position:relative}
.timeline::before{content:"";position:absolute;left:12rpx;top:12rpx;bottom:16rpx;width:4rpx;background:#E8DED0}
.time-block{position:relative;margin-bottom:28rpx}
.time-label{font-weight:900;color:var(--primary-dark);font-size:26rpx;margin-bottom:16rpx;position:relative}
.time-date{margin-left:12rpx;font-weight:600;color:var(--muted);font-size:22rpx}
.time-block{position:relative;margin-bottom:var(--sp-4)}
.time-label{font-weight:var(--fw-b);color:var(--primary-dark);font-size:var(--fs-sm);margin-bottom:var(--sp-3);position:relative}
.time-date{margin-left:var(--sp-2);font-weight:var(--fw);color:var(--muted);font-size:var(--fs-cap)}
.time-label::before{
content:"";position:absolute;left:-34rpx;top:8rpx;width:24rpx;height:24rpx;border-radius:50%;
background:var(--primary);border:6rpx solid #fff;box-shadow:0 0 0 4rpx #F5D5A8;
background:var(--primary);border:6rpx solid #fff;box-shadow:0 0 0 4rpx var(--primary-line);
}
.plan-task{
background:#fff;border-radius:var(--r-md);padding:var(--sp-4);box-shadow:var(--sd-1);
border:1rpx solid transparent;display:flex;gap:var(--sp-3);align-items:flex-start;
}
.plan-task{background:#fff;border-radius:36rpx;padding:26rpx;box-shadow:var(--shadow);border:1rpx solid transparent;display:flex;gap:18rpx;align-items:flex-start}
.plan-task.done{opacity:.55}
.pk-check{
width:44rpx;height:44rpx;border-radius:50%;border:4rpx solid #DED1C4;flex:none;margin-top:2rpx;
display:flex;align-items:center;justify-content:center;color:#fff;font-size:24rpx;font-weight:900;
display:flex;align-items:center;justify-content:center;color:#fff;
}
.plan-task.done .pk-check{background:var(--green);border-color:var(--green)}
.pk-body{flex:1}
.pk-b{font-size:28rpx;font-weight:700}
.pk-p{color:var(--muted);font-size:24rpx;line-height:1.45;margin-top:10rpx}
.ai-plan-tip{color:var(--muted);font-size:26rpx;line-height:1.6;margin-bottom:16rpx}
.pk-b{font-size:var(--fs-md);font-weight:var(--fw-b)}
.pk-p{color:var(--muted);font-size:var(--fs-sm);line-height:1.45;margin-top:var(--sp-1)}
.ai-plan-tip{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;margin-bottom:var(--sp-3)}
.calendar{background:#fff;border-radius:44rpx;padding:28rpx;box-shadow:var(--shadow)}
.cal-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:20rpx;font-weight:900;font-size:30rpx}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:12rpx;text-align:center}
.cal-w{font-size:22rpx;color:var(--muted);padding:8rpx 0}
/* 日历 */
.calendar{background:#fff;border-radius:var(--r-lg);padding:var(--sp-4);box-shadow:var(--sd-1)}
.cal-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--sp-3);font-weight:var(--fw-b);font-size:var(--fs-lg)}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--sp-2);text-align:center}
.cal-w{font-size:var(--fs-cap);color:var(--muted);padding:var(--sp-1) 0}
.cal-cell{
height:76rpx;border-radius:24rpx;display:flex;align-items:center;justify-content:center;
font-weight:800;font-size:26rpx;background:#FBFAF8;position:relative;
height:76rpx;border-radius:var(--r-sm);display:flex;align-items:center;justify-content:center;
font-weight:var(--fw-b);font-size:var(--fs-sm);background:var(--surface-2);position:relative;
}
.cal-cell.blank{background:transparent}
.cal-cell.today{background:#FFF3DF;color:var(--primary-dark)}
.cal-cell.tasked::after{content:"";position:absolute;bottom:10rpx;width:10rpx;height:10rpx;border-radius:50%;background:var(--green)}
.cal-cell.today{background:var(--primary-soft);color:var(--primary-dark)}
.cal-cell.tasked::after{content:"";position:absolute;bottom:8rpx;width:8rpx;height:8rpx;border-radius:50%;background:var(--green)}
.cal-cell.sel{background:var(--primary);color:#fff}
.cal-cell.sel.tasked::after{background:#fff}
.day-detail{margin-top:24rpx;border-top:1rpx solid var(--line);padding-top:24rpx}
.dd-title{font-weight:900;font-size:28rpx;margin-bottom:16rpx}
.dd-item{display:flex;align-items:center;gap:16rpx;padding:14rpx 0}
.dd-tag{flex:none;font-size:20rpx;font-weight:700;padding:2rpx 12rpx;border-radius:999rpx}
.dd-tag.plan{background:#FFF2DC;color:var(--primary-dark)}
.dd-tag.task{background:#E6F4EA;color:#2E7D5B}
.dd-tag.rem{background:#EAF0FF;color:#3B6BE5}
.dd-b{font-size:27rpx;color:var(--text)}
/* 某天的安排 */
.day-detail{margin-top:var(--sp-4);border-top:1rpx solid var(--line);padding-top:var(--sp-4)}
.dd-title{font-weight:var(--fw-b);font-size:var(--fs-md);margin-bottom:var(--sp-3)}
.dd-item{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) 0}
.dd-tag{flex:none;font-size:var(--fs-cap);font-weight:var(--fw-b);padding:2rpx var(--sp-2);border-radius:var(--r-full)}
.dd-tag.plan{background:var(--primary-soft);color:var(--primary-ink)}
.dd-tag.task{background:var(--green-soft);color:var(--green-ink)}
.dd-tag.rem{background:var(--blue-soft);color:var(--blue-ink)}
.dd-b{font-size:var(--fs-md);color:var(--text)}
.dd-b.done{text-decoration:line-through;color:var(--muted)}
.dd-empty{padding:20rpx 0;color:var(--muted);font-size:26rpx;text-align:center}
.extracted{display:grid;grid-template-columns:1fr 1fr;gap:16rpx;margin:24rpx 0}
.extract{background:#FBFAF8;border-radius:30rpx;padding:20rpx;font-size:24rpx}
.ex-b{display:block;font-size:26rpx;margin-bottom:6rpx;font-weight:700}
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
/* AI 提取信息 */
.extracted{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3);margin:var(--sp-4) 0}
.extract{background:var(--surface-2);border-radius:var(--r-sm);padding:var(--sp-3);font-size:var(--fs-sm)}
.ex-b{display:block;font-size:var(--fs-sm);margin-bottom:6rpx;font-weight:var(--fw-b);color:var(--muted)}
+2 -1
View File
@@ -2,6 +2,7 @@
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar",
"bottom-sheet": "/components/bottom-sheet/bottom-sheet",
"fab": "/components/fab/fab"
"fab": "/components/fab/fab",
"pt-icon": "/components/pt-icon/index"
}
}
+10 -10
View File
@@ -6,27 +6,27 @@
<view class="avatar ph-avatar" bindtap="onPickAvatar">
<image wx:if="{{user.avatar_url}}" class="ph-avatar-img" src="{{user.avatar_url}}" mode="aspectFill"></image>
<block wx:else>{{pet.emoji}}</block>
<view class="ph-avatar-cam">📷</view>
<view class="ph-avatar-cam"><pt-icon name="photo" size="{{28}}"></pt-icon></view>
</view>
<view class="ph-name">{{pet.name}}</view>
<view class="muted ph-meta">{{pet.age}}{{pet.weight}}{{pet.stage}}</view>
<view wx:if="{{pet.breed || pet.color}}" class="muted ph-meta">{{pet.breed}}{{pet.breed && pet.color ? ' · ' : ''}}{{pet.color}}</view>
</view>
<button class="btn btn-ghost btn-block" style="margin-bottom:28rpx" bindtap="onAddPet"> 添加毛孩子</button>
<button class="btn btn-ghost btn-block" style="margin-bottom:28rpx" bindtap="onAddPet"><pt-icon name="plus" size="{{30}}"></pt-icon>添加毛孩子</button>
<view class="profile-list">
<view class="profile-row" bindtap="onEditPet">宠物档案(编辑)<text class="pr-val">已完善 {{profilePct}}% </text></view>
<view class="profile-row" data-type="reminders" bindtap="openSheet">提醒设置 <text class="pr-val">{{summary.reminders}} 条 </text></view>
<view class="profile-row" bindtap="goRecord">健康记录 <text class="pr-val">{{summary.records}} 条 </text></view>
<view class="profile-row" data-type="managePets" bindtap="openSheet">多宠物管理 <text class="pr-val">{{summary.pets}} 只 </text></view>
<view class="profile-row" bindtap="onEditPet"><view class="pr-ic"><pt-icon name="user" size="{{34}}"></pt-icon></view><text class="pr-label">宠物档案(编辑)</text><text class="pr-val">已完善 {{profilePct}}%</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
<view class="profile-row" data-type="reminders" bindtap="openSheet"><view class="pr-ic"><pt-icon name="bell" size="{{34}}"></pt-icon></view><text class="pr-label">提醒设置</text><text class="pr-val">{{summary.reminders}} 条</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
<view class="profile-row" bindtap="goRecord"><view class="pr-ic"><pt-icon name="note" size="{{34}}"></pt-icon></view><text class="pr-label">健康记录</text><text class="pr-val">{{summary.records}} 条</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
<view class="profile-row" data-type="managePets" bindtap="openSheet"><view class="pr-ic"><pt-icon name="community" size="{{34}}"></pt-icon></view><text class="pr-label">多宠物管理</text><text class="pr-val">{{summary.pets}} 只</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
</view>
<view class="profile-list">
<view class="profile-row" data-type="pro" bindtap="openSheet">会员权益 <text class="pr-val">{{summary.pro_status === 'active' ? '已开通' : '未开通'}} </text></view>
<view class="profile-row" data-type="exportData" bindtap="openSheet">数据导出 <text class="pr-val">文本 </text></view>
<view class="profile-row" data-type="feedback" bindtap="openSheet">意见反馈 <text class="pr-val">去填写 </text></view>
<view class="profile-row" data-type="about" bindtap="openSheet">关于我们 <text class="pr-val">v0.2 </text></view>
<view class="profile-row" data-type="pro" bindtap="openSheet"><view class="pr-ic"><pt-icon name="like" size="{{34}}"></pt-icon></view><text class="pr-label">会员权益</text><text class="pr-val">{{summary.pro_status === 'active' ? '已开通' : '未开通'}}</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
<view class="profile-row" data-type="exportData" bindtap="openSheet"><view class="pr-ic"><pt-icon name="doc" size="{{34}}"></pt-icon></view><text class="pr-label">数据导出</text><text class="pr-val">文本</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
<view class="profile-row" data-type="feedback" bindtap="openSheet"><view class="pr-ic"><pt-icon name="comment" size="{{34}}"></pt-icon></view><text class="pr-label">意见反馈</text><text class="pr-val">去填写</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
<view class="profile-row" data-type="about" bindtap="openSheet"><view class="pr-ic"><pt-icon name="book" size="{{34}}"></pt-icon></view><text class="pr-label">关于我们</text><text class="pr-val">v0.2</text><view class="pr-arrow"><pt-icon name="next" size="{{28}}"></pt-icon></view></view>
</view>
<button class="btn btn-ghost btn-block" data-type="pro" bindtap="openSheet">Pro 会员(即将开放)</button>
+21 -12
View File
@@ -1,16 +1,25 @@
.profile-header{text-align:center;padding:24rpx 0 32rpx}
.ph-avatar{margin:0 auto 20rpx;width:152rpx;height:152rpx;font-size:76rpx;border-radius:56rpx;position:relative;overflow:visible}
.ph-avatar-img{width:100%;height:100%;border-radius:56rpx}
.ph-avatar-cam{position:absolute;right:-6rpx;bottom:-6rpx;width:52rpx;height:52rpx;line-height:52rpx;text-align:center;font-size:26rpx;background:#fff;border-radius:50%;box-shadow:var(--shadow)}
.ph-name{font-size:44rpx;font-weight:800}
.ph-meta{font-size:26rpx;margin-top:10rpx}
.profile-header{text-align:center;padding:var(--sp-4) 0 var(--sp-5)}
.ph-avatar{margin:0 auto var(--sp-3);width:152rpx;height:152rpx;font-size:76rpx;border-radius:var(--r-lg);position:relative;overflow:visible}
.ph-avatar-img{width:100%;height:100%;border-radius:var(--r-lg)}
.ph-avatar-cam{
position:absolute;right:-6rpx;bottom:-6rpx;width:52rpx;height:52rpx;
display:flex;align-items:center;justify-content:center;color:var(--text-2);
background:#fff;border-radius:50%;box-shadow:var(--sd-1);
}
.ph-name{font-size:var(--fs-2xl);font-weight:var(--fw-b)}
.ph-meta{font-size:var(--fs-sm);margin-top:var(--sp-1)}
.profile-list{background:#fff;border-radius:48rpx;box-shadow:var(--shadow);overflow:hidden;margin-bottom:28rpx}
.profile-list{background:#fff;border-radius:var(--r-lg);box-shadow:var(--sd-1);overflow:hidden;margin-bottom:var(--sp-4)}
.profile-row{
display:flex;align-items:center;justify-content:space-between;min-height:104rpx;padding:0 32rpx;
border-bottom:1rpx solid var(--line);font-size:28rpx;font-weight:800;
display:flex;align-items:center;gap:var(--sp-3);min-height:104rpx;padding:0 var(--sp-4);
border-bottom:1rpx solid var(--line);font-size:var(--fs-md);font-weight:var(--fw-b);
}
.profile-row:last-child{border-bottom:0}
.pr-val{color:var(--muted);font-weight:700;font-size:26rpx}
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
.pr-ic{
width:56rpx;height:56rpx;border-radius:var(--r-xs);flex:none;
display:flex;align-items:center;justify-content:center;
background:var(--primary-soft);color:var(--primary-ink);
}
.pr-label{flex:1;min-width:0}
.pr-val{color:var(--muted);font-weight:var(--fw);font-size:var(--fs-sm)}
.pr-arrow{color:var(--muted2);display:flex;align-items:center;flex:none}
+28 -2
View File
@@ -15,10 +15,34 @@ function fmtTime(iso) {
if (d.toDateString() === y.toDateString()) return '昨天 ' + hm;
return d.getMonth() + 1 + '月' + d.getDate() + '日';
}
// 记录类型 → 配色。和首页快速记录入口用同一套分组:
// 体重/疫苗=橙、便便/饮食=绿、异常/用药=蓝、消费/照片=紫
const TYPE_TONE = {
weight: 'tone-1', vaccine: 'tone-1', deworm: 'tone-1',
poop: 'tone-2', food: 'tone-2',
symptom: 'tone-3', medicine: 'tone-3',
cost: 'tone-4', photo: 'tone-4',
};
// 记一笔的九个入口
const RECORD_ITEMS = [
{ type: 'weight', icon: 'weight', label: '体重', tone: 'tone-1' },
{ type: 'food', icon: 'food', label: '饮食', tone: 'tone-2' },
{ type: 'poop', icon: 'poop', label: '便便', tone: 'tone-2' },
{ type: 'symptom', icon: 'symptom', label: '异常', tone: 'tone-3' },
{ type: 'medicine', icon: 'medicine', label: '用药', tone: 'tone-3' },
{ type: 'cost', icon: 'cost', label: '消费', tone: 'tone-4' },
{ type: 'vaccine', icon: 'vaccine', label: '疫苗', tone: 'tone-1' },
{ type: 'deworm', icon: 'deworm', label: '驱虫', tone: 'tone-1' },
{ type: 'photo', icon: 'photo', label: '照片', tone: 'tone-4' },
];
function mapRecord(r) {
return {
id: r.id,
icon: r.icon || '✍️',
// 图标按 type 取(9 值枚举,可靠);老数据 icon 字段里的 emoji 只作兜底
type: r.type || r.icon || '',
tone: TYPE_TONE[r.type] || 'tone-1',
title: r.title,
desc: fmtTime(r.occurred_at) + (r.description ? '' + r.description : ''),
image: r.image_url || '',
@@ -80,6 +104,7 @@ Page({
recHasMore: false,
recFilters: REC_FILTERS,
recFilterIdx: 0,
recordItems: RECORD_ITEMS,
trendSvg: '',
trendStats: null,
trendDots: [],
@@ -114,8 +139,9 @@ Page({
curFilter() {
return this.data.recFilters[this.data.recFilterIdx].key;
},
// 来自 seg-tabs 的 change 事件
onFilterTap(e) {
const i = Number(e.currentTarget.dataset.index);
const i = Number(e.detail.index);
if (i === this.data.recFilterIdx) return;
this.setData({ recFilterIdx: i }, () => this.loadRecords());
},
+3 -1
View File
@@ -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",
"seg-tabs": "/components/seg-tabs/index"
}
}
+12 -24
View File
@@ -2,23 +2,15 @@
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body">
<view class="page-title">
<view class="pt-h2">快速记录</view>
<view class="pt-p">先选择宠物,再记录体重、饮食、异常和消费。</view>
</view>
<pet-switch bind:add="onAddPet"></pet-switch>
<view class="record-grid">
<view class="record-item" data-type="weight" bindtap="openSheet"><text class="ri-ic">⚖️</text>体重</view>
<view class="record-item" data-type="food" bindtap="openSheet"><text class="ri-ic">🍽️</text>饮食</view>
<view class="record-item" data-type="poop" bindtap="openSheet"><text class="ri-ic">💩</text>便便</view>
<view class="record-item" data-type="symptom" bindtap="openSheet"><text class="ri-ic">🤒</text>异常</view>
<view class="record-item" data-type="medicine" bindtap="openSheet"><text class="ri-ic">💊</text>用药</view>
<view class="record-item" data-type="cost" bindtap="openSheet"><text class="ri-ic">💰</text>消费</view>
<view class="record-item" data-type="vaccine" bindtap="openSheet"><text class="ri-ic">💉</text>疫苗</view>
<view class="record-item" data-type="deworm" bindtap="openSheet"><text class="ri-ic">🛡️</text>驱虫</view>
<view class="record-item" data-type="photo" bindtap="openSheet"><text class="ri-ic">📷</text>照片</view>
<view class="card">
<view class="section-head"><view class="sh-title">记一笔</view></view>
<view class="quick-grid cols-3">
<view wx:for="{{recordItems}}" 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">
@@ -40,32 +32,28 @@
<text class="pd-weight">{{selDot.value}}kg</text>
<text wx:if="{{selDot.delta !== null}}" class="pd-delta {{selDot.delta > 0 ? 'up' : (selDot.delta < 0 ? 'down' : '')}}">{{selDot.delta > 0 ? '+' : ''}}{{selDot.delta}}kg</text>
</view>
<view wx:if="{{selDot.note}}" class="pd-note">📝 {{selDot.note}}</view>
<view wx:if="{{selDot.note}}" class="pd-note"><pt-icon name="note" size="{{26}}"></pt-icon> {{selDot.note}}</view>
<view wx:else class="pd-note muted">这次没有填备注</view>
</view>
<view class="chart-hint">点击圆点查看每次记录</view>
</block>
<view wx:else class="chart-empty">先记录几次体重,这里会自动画出趋势曲线</view>
<view wx:else class="empty">先记录几次体重,这里会自动画出趋势曲线</view>
</view>
<view class="card">
<view class="section-head"><view class="sh-title">健康时间轴</view><view class="link" data-type="vetSummary" bindtap="openSheet">摘要</view></view>
<scroll-view class="rec-filter" scroll-x="true" enhanced="{{true}}" show-scrollbar="{{false}}">
<view wx:for="{{recFilters}}" wx:key="key"
class="rf-chip {{recFilterIdx === index ? 'on' : ''}}"
data-index="{{index}}" bindtap="onFilterTap">{{item.label}}</view>
</scroll-view>
<seg-tabs items="{{recFilters}}" current="{{recFilterIdx}}" bind:change="onFilterTap"></seg-tabs>
<view class="health-timeline">
<view wx:for="{{timeline}}" wx:key="id" class="health-event"
bindlongpress="onDeleteRecord" data-index="{{index}}">
<view class="event-dot">{{item.icon}}</view>
<view class="event-dot {{item.tone}}"><pt-icon name="{{item.type}}" size="{{34}}" fallback="note"></pt-icon></view>
<view class="he-body">
<text class="he-b">{{item.title}}</text><view class="he-p">{{item.desc}}</view>
<image wx:if="{{item.image}}" class="he-img" src="{{item.image}}" mode="aspectFill" catchtap="previewImage" data-src="{{item.image}}"></image>
</view>
</view>
<view wx:if="{{timeline.length === 0}}" class="tl-empty">这个分类下还没有记录</view>
<view wx:if="{{timeline.length === 0}}" class="empty">这个分类下还没有记录</view>
</view>
<view wx:if="{{timeline.length}}" class="tl-tip">长按任意一条可删除</view>
<view wx:if="{{recHasMore}}" class="tl-more" bindtap="loadMoreRecords">查看更多(共 {{recTotal}} 条)</view>
+29 -40
View File
@@ -1,13 +1,5 @@
.record-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24rpx;margin-bottom:28rpx}
.record-item{
height:184rpx;background:#fff;border-radius:44rpx;box-shadow:var(--shadow);
display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16rpx;
font-weight:900;color:var(--text);font-size:28rpx;
}
.ri-ic{font-size:56rpx}
.mini-chart{
height:256rpx;border-radius:36rpx;margin-top:24rpx;position:relative;overflow:hidden;
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);
@@ -15,41 +7,38 @@
.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 #73BE9D;box-sizing:border-box}
.dot.on::after{width:26rpx;height:26rpx;margin:-13rpx 0 0 -13rpx;background:#73BE9D;box-shadow:0 0 0 6rpx rgba(115,190,157,.25)}
.trend-stats{margin-top:16rpx;font-size:26rpx;color:var(--muted)}
.point-detail{margin-top:20rpx;padding:24rpx 28rpx;border-radius:32rpx;background:#F3FAF6;border:1rpx solid #DCEFE4}
.pd-main{display:flex;align-items:baseline;gap:20rpx}
.pd-date{font-size:26rpx;color:var(--muted)}
.pd-weight{font-size:40rpx;font-weight:800;color:#2E7D5B}
.pd-delta{font-size:26rpx;font-weight:700}
.pd-delta.up{color:var(--red)}
.pd-delta.down{color:#2E7D5B}
.pd-note{margin-top:12rpx;font-size:28rpx;color:#4A4A46;line-height:1.5}
.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:12rpx;font-size:22rpx;color:var(--muted);text-align:center}
.chart-empty{margin-top:24rpx;padding:48rpx 24rpx;border-radius:36rpx;background:#FBFAF8;color:var(--muted);font-size:26rpx;text-align:center}
.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:20rpx}
.tl-empty{padding:36rpx 8rpx;text-align:center;color:var(--muted);font-size:26rpx}
.tl-more{margin-top:20rpx;padding:20rpx;text-align:center;color:var(--primary-dark);background:#FBFAF8;border-radius:28rpx;font-size:26rpx;font-weight:700}
.health-event{display:flex;gap:20rpx;background:#FBFAF8;border-radius:32rpx;padding:24rpx}
.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)}
/* 底色和文字色由 .tone-N 提供(记录类型决定),这里只管形状 */
.event-dot{
width:68rpx;height:68rpx;border-radius:26rpx;flex:none;
display:flex;align-items:center;justify-content:center;background:#FFF0D7;font-size:34rpx;
width:64rpx;height:64rpx;border-radius:var(--r-sm);flex:none;
display:flex;align-items:center;justify-content:center;
}
.he-b{font-size:28rpx;font-weight:700}
.he-p{color:var(--muted);font-size:24rpx;line-height:1.45;margin-top:6rpx}
.he-b{font-size:var(--fs-md);font-weight:var(--fw-b)}
.he-p{color:var(--muted);font-size:var(--fs-sm);line-height:1.45;margin-top:6rpx}
.he-body{flex:1;min-width:0}
.he-img{margin-top:14rpx;width:220rpx;height:220rpx;border-radius:20rpx}
.he-img{margin-top:var(--sp-2);width:220rpx;height:220rpx;border-radius:var(--r-sm)}
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
/* 时间轴类型筛选 */
.rec-filter{white-space:nowrap;margin-bottom:20rpx}
.rf-chip{
display:inline-block;padding:10rpx 26rpx;margin-right:14rpx;border-radius:999rpx;
background:#fff;border:1rpx solid var(--line);color:var(--muted);font-size:24rpx;font-weight:700;
.tl-tip{margin-top:var(--sp-3);text-align:center;color:var(--muted);font-size:var(--fs-cap)}
.tl-more{
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);
}
.rf-chip.on{background:#FFF2DC;color:var(--primary-dark);border-color:#FFD39A}
.tl-tip{margin-top:16rpx;text-align:center;color:var(--muted);font-size:22rpx}
+2 -1
View File
@@ -3,6 +3,7 @@
"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"
}
}
+1 -6
View File
@@ -2,11 +2,6 @@
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body">
<view class="page-title">
<view class="pt-h2">成长报告</view>
<view class="pt-p">每只宠物的成长报告、账单与健康摘要。</view>
</view>
<pet-switch bind:add="onAddPet"></pet-switch>
<view class="card report-card">
@@ -50,7 +45,7 @@
<view class="card">
<view class="section-head"><view class="sh-title">Pro 会员</view></view>
<view class="rc-text">解锁 365 天计划、PDF 健康档案、月度报告、多宠物管理与年度账单。</view>
<button class="btn btn-primary btn-block" style="margin-top:24rpx" data-type="pro" bindtap="openSheet">查看 Pro 权益</button>
<button class="btn btn-dark btn-block" style="margin-top:24rpx" data-type="pro" bindtap="openSheet">查看 Pro 权益</button>
</view>
</view>
</scroll-view>
+7 -10
View File
@@ -1,13 +1,10 @@
.report-card{background:linear-gradient(135deg,#FFFFFF,#F2FAF6)}
.rc-text{color:var(--muted);font-size:28rpx;line-height:1.6}
.stat-b{display:block;font-size:36rpx;margin-bottom:8rpx;font-weight:800}
.stat-s{font-size:22rpx;color:var(--muted)}
.rc-text{color:var(--text-2);font-size:var(--fs-md);line-height:1.6}
.bill-bars{display:flex;flex-direction:column;gap:20rpx;margin-top:24rpx}
.bill-row{display:grid;grid-template-columns:88rpx 1fr 108rpx;gap:16rpx;align-items:center;font-size:24rpx}
.bill-bars{display:flex;flex-direction:column;gap:var(--sp-3);margin-top:var(--sp-4)}
.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:900;text-align:right}
.mini-bar{height:18rpx;border-radius:999rpx;background:#F0E7DD;overflow:hidden}
.mini-bar-fill{display:block;height:100%;border-radius:999rpx;background:linear-gradient(90deg,var(--blue),#A9C8F6)}
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
.bill-amt{font-weight:var(--fw-b);text-align:right}
/* 账单条比全局 .bar 细一档,蓝色是「花费」的语义色 */
.mini-bar{height:var(--sp-2);border-radius:var(--r-full);background:#F0E7DD;overflow:hidden}
.mini-bar-fill{display:block;height:100%;border-radius:var(--r-full);background:var(--blue)}