feat(auth): access token 缩到 2 小时 + refresh token 机制 #3

Merged
Blizzard merged 58 commits from feat/dev into main 2026-07-30 10:18:07 +00:00
9 changed files with 178 additions and 219 deletions
Showing only changes of commit b8b3ba99b4 - Show all commits
+7 -13
View File
@@ -358,16 +358,10 @@ page{scrollbar-width:none;-ms-overflow-style:none}
.no-pet-b{font-size:var(--fs-lg);font-weight:var(--fw-b)} .no-pet-b{font-size:var(--fs-lg);font-weight:var(--fw-b)}
.no-pet-p{color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-2) 0 var(--sp-4);line-height:1.6} .no-pet-p{color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-2) 0 var(--sp-4);line-height:1.6}
/* 宠物切换条固定在导航栏下方。它原本在 scroll-view 里,滚下去就找不到了, /* 顶部固定条:宠物切换 / 社区分类。
而切宠物是随时要用的动作——尤其多宠用户,看着 A 的数据想切到 B 它在文档流里而不是浮层——内容永远不会滚到它下面,所以不需要任何背景。
还得先滚回顶部。移出滚动区,四个主页面通用。 */ 之前给了不透明底色 + 分隔线,等于在页面渐变上贴了块补丁,
.pet-bar{ 屏幕上就出现三条颜色不搭的横带。 */
flex:none; .top-bar{flex:none;padding:var(--sp-2) var(--pad-x) 0}
padding:var(--sp-2) var(--pad-x) 0; .top-bar .pet-switch{padding-bottom:var(--sp-2)}
background:rgba(250,247,242,.92); .top-bar .st-pill{padding-bottom:var(--sp-2)}
backdrop-filter:blur(20rpx);
-webkit-backdrop-filter:blur(20rpx);
border-bottom:1rpx solid rgba(238,230,220,.7);
}
/* 条内的 pet-switch 自己带 padding-bottom,这里不再叠加 */
.pet-bar .pet-switch{padding-bottom:var(--sp-2)}
+8 -1
View File
@@ -52,6 +52,7 @@ Page({
sheetPostId: '', sheetPostId: '',
loaded: false, loaded: false,
loadErr: '', loadErr: '',
intoView: '',
}, },
onLoad() { onLoad() {
this._unsub = store.subscribe((pet) => this.setData({ pet })); this._unsub = store.subscribe((pet) => this.setData({ pet }));
@@ -85,9 +86,15 @@ Page({
}, },
// 来自 seg-tabs 的 change 事件 // 来自 seg-tabs 的 change 事件
switchFeedTab(e) { switchFeedTab(e) {
this.setData({ feedIdx: Number(e.detail.index) }); // 切分类必须回到顶部:不然从「推荐」滚到一半切到「关注」,
// 看到的是新列表的中间,会以为内容错乱了
this.setData({ feedIdx: Number(e.detail.index), intoView: 'feed-top' });
this.loadPosts(); this.loadPosts();
}, },
// 滚动时清掉锚点,否则下次再设同一个值不会触发
onFeedScroll() {
if (this.data.intoView) this.setData({ intoView: '' });
},
likePost(e) { likePost(e) {
const i = e.currentTarget.dataset.index; const i = e.currentTarget.dataset.index;
const post = this.data.posts[i]; const post = this.data.posts[i];
+7 -5
View File
@@ -3,14 +3,16 @@ module.exports.isUrl = function (s) { return s && s.indexOf('http') === 0; }
</wxs> </wxs>
<nav-bar title="宠友圈"></nav-bar> <nav-bar title="宠友圈"></nav-bar>
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}"> <view class="top-bar feed-bar">
<view class="page-body">
<seg-tabs items="{{feedTabs}}" current="{{feedIdx}}" bind:change="switchFeedTab"></seg-tabs> <seg-tabs items="{{feedTabs}}" current="{{feedIdx}}" bind:change="switchFeedTab"></seg-tabs>
<view class="feed-post" bindtap="openSheet" data-type="createPost">
<view class="compose-card"> <pt-icon name="plus" size="{{28}}"></pt-icon>发布
<button class="btn btn-dark btn-block" bindtap="openSheet" data-type="createPost"><pt-icon name="plus" size="{{30}}"></pt-icon>发布图文</button>
</view> </view>
</view>
<scroll-view class="page-scroll" scroll-y="{{true}}" scroll-into-view="{{intoView}}" bindscroll="onFeedScroll" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body">
<view id="feed-top"></view>
<view wx:for="{{posts}}" wx:key="id" class="post-card" bindlongpress="onLongPressPost" data-index="{{index}}"> <view wx:for="{{posts}}" wx:key="id" class="post-card" bindlongpress="onLongPressPost" data-index="{{index}}">
<view class="post-head"> <view class="post-head">
<view class="post-avatar">{{item.author_emoji}}</view> <view class="post-avatar">{{item.author_emoji}}</view>
+10
View File
@@ -46,3 +46,13 @@
margin-top:var(--sp-3);display:inline-block;padding:var(--sp-2) var(--sp-6);border-radius:var(--r-full); 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); background:var(--primary-soft);color:var(--primary-dark);font-weight:var(--fw-b);font-size:var(--fs-sm);
} }
/* 分类标签吸顶,发布按钮收进同一条:原来那个大黑按钮独占 124rpx,
而它是低频操作,不该在每屏都占着首屏 */
.feed-bar{display:flex;align-items:flex-start;gap:var(--sp-3)}
.feed-bar .st{flex:1;min-width:0}
.feed-post{
flex:none;display:flex;align-items:center;gap:4rpx;
height:var(--h-sm);padding:0 var(--sp-3);border-radius:var(--r-full);
background:var(--cta);color:#fff;font-size:var(--fs-sm);font-weight:var(--fw-b);
}
+1 -1
View File
@@ -1,6 +1,6 @@
<nav-bar title="肉垫计划"></nav-bar> <nav-bar title="肉垫计划"></nav-bar>
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view> <view class="top-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}"> <scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body"> <view class="page-body">
+1 -1
View File
@@ -1,6 +1,6 @@
<nav-bar title="养宠计划"></nav-bar> <nav-bar title="养宠计划"></nav-bar>
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view> <view class="top-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}"> <scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body"> <view class="page-body">
+1 -1
View File
@@ -1,6 +1,6 @@
<nav-bar title="快速记录"></nav-bar> <nav-bar title="快速记录"></nav-bar>
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view> <view class="top-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}"> <scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body"> <view class="page-body">
+1 -1
View File
@@ -1,6 +1,6 @@
<nav-bar title="成长报告"></nav-bar> <nav-bar title="成长报告"></nav-bar>
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view> <view class="top-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}"> <scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
<view class="page-body"> <view class="page-body">
+141 -195
View File
@@ -1,35 +1,12 @@
<!doctype html> <!doctype html><meta charset="utf-8"><title>顶部条</title><style>
<meta charset="utf-8"> body{font-family:-apple-system,"PingFang SC",sans-serif;margin:0;padding:20px;background:#EDEAE4}
<title>肉垫计划 · 设计预览 v2</title> .pt-i{display:inline-block;background-color:currentColor;-webkit-mask-image:var(--m);mask-image:var(--m);
<!-- -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;vertical-align:-.15em}
设计验证页:由 app.wxss 自动翻译而来(rpx→px,÷2),不参与小程序打包 .phone{width:375px;border-radius:20px;overflow:hidden;box-shadow:0 8px 30px rgba(0,0,0,.12)}
(见 project.config.json 的 packOptions.ignore)。改 app.wxss 后重新生成即可。 .nav{height:44px;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:600;
--> background:linear-gradient(180deg,rgba(250,247,242,.94),rgba(250,247,242,0))}
<style> h3{font:600 13px/1 -apple-system;margin:0 0 8px;color:#555}
body{font-family:-apple-system,"PingFang SC",sans-serif;background:#F1EEE8;color:#2D2925;margin:0;padding:28px 32px 80px} .wrap{display:flex;gap:24px}
h2{font-size:17px;font-weight:600;margin:36px 0 6px}
h2:first-of-type{margin-top:0}
.hint{font-size:12px;color:#8D8277;margin:8px 0 14px}
.wall{display:grid;grid-template-columns:repeat(10,1fr);gap:16px 8px;max-width:760px}
.cell{text-align:center;font-size:10px;color:#8D8277}
.cell .pt-i{width:20px;height:20px;color:#2D2925;display:block;margin:0 auto 6px}
.sizes span{margin-right:26px}
.row{display:flex;flex-wrap:wrap;align-items:center;gap:10px}
.stage{max-width:375px;background:#F8F6F2;border-radius:16px;padding:14px}
.grid4{display:grid;grid-template-columns:repeat(4,88px);gap:9px}
.qk{border-radius:16px;padding:12px 0;display:flex;flex-direction:column;align-items:center;gap:6px;font-size:12px;font-weight:600}
.stat-b{display:block;font-size:18px;font-weight:600;margin-bottom:4px}
.stat-s{font-size:11px;color:#8D8277}
.tabbar{display:flex;max-width:375px;background:#fff;border-top:1px solid #EEE6DC;border-radius:0 0 12px 12px}
.tb{flex:1;text-align:center;padding:9px 0 12px;font-size:10px;color:#8D8277}
.tb.on{color:#DD8420}
.tb .pt-i{width:21px;height:21px;display:block;margin:0 auto 3px}
.dark{background:#2D2925;color:#fff;border-radius:20px;padding:13px 22px;display:inline-block;font-size:14px}
.pt-i{display:inline-block;flex:none;background-color:currentColor;
-webkit-mask-image:var(--m);mask-image:var(--m);
-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
-webkit-mask-position:center;mask-position:center;
-webkit-mask-size:100% 100%;mask-size:100% 100%;vertical-align:-.15em}
.pt-i-weight{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik03IDIwbDEwIDAiIC8+PHBhdGggZD0iTTYgNmw2IC0xbDYgMSIgLz48cGF0aCBkPSJNMTIgM2wwIDE3IiAvPjxwYXRoIGQ9Ik05IDEybC0zIC02bC0zIDZhMyAzIDAgMCAwIDYgMCIgLz48cGF0aCBkPSJNMjEgMTJsLTMgLTZsLTMgNmEzIDMgMCAwIDAgNiAwIiAvPjwvc3ZnPg==")} .pt-i-weight{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik03IDIwbDEwIDAiIC8+PHBhdGggZD0iTTYgNmw2IC0xbDYgMSIgLz48cGF0aCBkPSJNMTIgM2wwIDE3IiAvPjxwYXRoIGQ9Ik05IDEybC0zIC02bC0zIDZhMyAzIDAgMCAwIDYgMCIgLz48cGF0aCBkPSJNMjEgMTJsLTMgLTZsLTMgNmEzIDMgMCAwIDAgNiAwIiAvPjwvc3ZnPg==")}
.pt-i-poop{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik0xMCAxMmguMDEiIC8+PHBhdGggZD0iTTE0IDEyaC4wMSIgLz48cGF0aCBkPSJNMTAgMTZhMy41IDMuNSAwIDAgMCA0IDAiIC8+PHBhdGggZD0iTTExIDRjMiAwIDMuNSAxLjUgMy41IDRsLjE2NCAwYTIuNSAyLjUgMCAwIDEgMi4xOTYgMy4zMmEzIDMgMCAwIDEgMS42MTUgMy4wNjNhMyAzIDAgMCAxIC0xLjI5OSA1LjYwN2wtLjE3NiAwaC0xMGEzIDMgMCAwIDEgLTEuNDc0IC01LjYxM2EzIDMgMCAwIDEgMS42MTUgLTMuMDYyYTIuNSAyLjUgMCAwIDEgMi4xOTUgLTMuMzJsLjE2NCAwYzEuNSAwIDIuNSAtMiAxLjUgLTRsMCAuMDA1IiAvPjwvc3ZnPg==")} .pt-i-poop{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik0xMCAxMmguMDEiIC8+PHBhdGggZD0iTTE0IDEyaC4wMSIgLz48cGF0aCBkPSJNMTAgMTZhMy41IDMuNSAwIDAgMCA0IDAiIC8+PHBhdGggZD0iTTExIDRjMiAwIDMuNSAxLjUgMy41IDRsLjE2NCAwYTIuNSAyLjUgMCAwIDEgMi4xOTYgMy4zMmEzIDMgMCAwIDEgMS42MTUgMy4wNjNhMyAzIDAgMCAxIC0xLjI5OSA1LjYwN2wtLjE3NiAwaC0xMGEzIDMgMCAwIDEgLTEuNDc0IC01LjYxM2EzIDMgMCAwIDEgMS42MTUgLTMuMDYyYTIuNSAyLjUgMCAwIDEgMi4xOTUgLTMuMzJsLjE2NCAwYzEuNSAwIDIuNSAtMiAxLjUgLTRsMCAuMDA1IiAvPjwvc3ZnPg==")}
@@ -69,7 +46,6 @@ h2:first-of-type{margin-top:0}
.pt-i-dog{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik0xMSA1aDIiIC8+PHBhdGggZD0iTTE5IDEyYy0uNjY3IDUuMzMzIC0yLjMzMyA4IC01IDhoLTRjLTIuNjY3IDAgLTQuMzMzIC0yLjY2NyAtNSAtOCIgLz48cGF0aCBkPSJNMTEgMTZjMCAuNjY3IC4zMzMgMSAxIDFzMSAtLjMzMyAxIC0xaC0yIiAvPjxwYXRoIGQ9Ik0xMiAxOHYyIiAvPjxwYXRoIGQ9Ik0xMCAxMXYuMDEiIC8+PHBhdGggZD0iTTE0IDExdi4wMSIgLz48cGF0aCBkPSJNNSA0bDYgLjk3bC02LjIzOCA2LjY4OGExLjAyMSAxLjAyMSAwIDAgMSAtMS40MSAuMTExYS45NTMgLjk1MyAwIDAgMSAtLjMyNyAtLjk1NGwxLjk3NSAtNi44MTUiIC8+PHBhdGggZD0iTTE5IDRsLTYgLjk3bDYuMjM4IDYuNjg4Yy4zNTggLjQwOCAuOTg5IC40NTggMS40MSAuMTExYS45NTMgLjk1MyAwIDAgMCAuMzI3IC0uOTU0bC0xLjk3NSAtNi44MTUiIC8+PC9zdmc+")} .pt-i-dog{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik0xMSA1aDIiIC8+PHBhdGggZD0iTTE5IDEyYy0uNjY3IDUuMzMzIC0yLjMzMyA4IC01IDhoLTRjLTIuNjY3IDAgLTQuMzMzIC0yLjY2NyAtNSAtOCIgLz48cGF0aCBkPSJNMTEgMTZjMCAuNjY3IC4zMzMgMSAxIDFzMSAtLjMzMyAxIC0xaC0yIiAvPjxwYXRoIGQ9Ik0xMiAxOHYyIiAvPjxwYXRoIGQ9Ik0xMCAxMXYuMDEiIC8+PHBhdGggZD0iTTE0IDExdi4wMSIgLz48cGF0aCBkPSJNNSA0bDYgLjk3bC02LjIzOCA2LjY4OGExLjAyMSAxLjAyMSAwIDAgMSAtMS40MSAuMTExYS45NTMgLjk1MyAwIDAgMSAtLjMyNyAtLjk1NGwxLjk3NSAtNi44MTUiIC8+PHBhdGggZD0iTTE5IDRsLTYgLjk3bDYuMjM4IDYuNjg4Yy4zNTggLjQwOCAuOTg5IC40NTggMS40MSAuMTExYS45NTMgLjk1MyAwIDAgMCAuMzI3IC0uOTU0bC0xLjk3NSAtNi44MTUiIC8+PC9zdmc+")}
.pt-i-user{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik04IDdhNCA0IDAgMSAwIDggMGE0IDQgMCAwIDAgLTggMCIgLz48cGF0aCBkPSJNNiAyMXYtMmE0IDQgMCAwIDEgNCAtNGg0YTQgNCAwIDAgMSA0IDR2MiIgLz48L3N2Zz4=")} .pt-i-user{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik04IDdhNCA0IDAgMSAwIDggMGE0IDQgMCAwIDAgLTggMCIgLz48cGF0aCBkPSJNNiAyMXYtMmE0IDQgMCAwIDEgNCAtNGg0YTQgNCAwIDAgMSA0IDR2MiIgLz48L3N2Zz4=")}
.pt-i-clock{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik0zIDEyYTkgOSAwIDEgMCAxOCAwYTkgOSAwIDAgMCAtMTggMCIgLz48cGF0aCBkPSJNMTIgN3Y1bDMgMyIgLz48L3N2Zz4=")} .pt-i-clock{--m:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgPjxwYXRoIGQ9Ik0zIDEyYTkgOSAwIDEgMCAxOCAwYTkgOSAwIDAgMCAtMTggMCIgLz48cGF0aCBkPSJNMTIgN3Y1bDMgMyIgLz48L3N2Zz4=")}
/* ===== 全局设计令牌 + 通用样式 ===== /* ===== 全局设计令牌 + 通用样式 =====
规则:任何页面/组件都不要再写死色值、圆角、字号,一律用下面的 token。 规则:任何页面/组件都不要再写死色值、圆角、字号,一律用下面的 token。
收敛前的现状是 22 种圆角、26 档字号、9 种近似浅橙、7 处一次性阴影, 收敛前的现状是 22 种圆角、26 档字号、9 种近似浅橙、7 处一次性阴影,
@@ -251,6 +227,16 @@ view,text{box-sizing:border-box}
.tone-3{background:var(--blue-soft);color:var(--blue-ink)} .tone-3{background:var(--blue-soft);color:var(--blue-ink)}
.tone-4{background:var(--purple-soft);color:var(--purple-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:64px;
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 套长得差不多的写法散在各页 */ /* 空态。原本有 6 套长得差不多的写法散在各页 */
.empty{padding:28px var(--sp-1);text-align:center;color:var(--muted);font-size:var(--fs-sm)} .empty{padding:28px var(--sp-1);text-align:center;color:var(--muted);font-size:var(--fs-sm)}
.empty.lg{padding:48px 0} .empty.lg{padding:48px 0}
@@ -359,6 +345,8 @@ view,text{box-sizing:border-box}
/* 统计格 */ /* 统计格 */
.stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-3);margin:var(--sp-4) 0} .stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-3);margin:var(--sp-4) 0}
.stat{background:#fff;border:0.5px solid var(--line);border-radius:var(--r-md);padding:var(--sp-4) var(--sp-2);text-align:center} .stat{background:#fff;border:0.5px 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:3px;font-weight:var(--fw-b)}
.stat-s{font-size:var(--fs-cap);color:var(--muted)}
/* 文章卡 */ /* 文章卡 */
.article-card{ .article-card{
@@ -403,169 +391,127 @@ view,text{box-sizing:border-box}
.msg.ai{background:var(--green-soft);align-self:flex-start} .msg.ai{background:var(--green-soft);align-self:flex-start}
.msg.user{background:var(--primary-soft);align-self:flex-end} .msg.user{background:var(--primary-soft);align-self:flex-end}
/* 没有宠物时的引导块。四个主页面都会 currentPetId 为空直接 return
留下一堆空卡片却不解释为什么——用户以为是坏了。 */
.no-pet{
background:#fff;border-radius:var(--r-lg);box-shadow:var(--sd-1);
padding:var(--sp-6) var(--sp-5);text-align:center;margin-bottom:var(--sp-4);
}
.no-pet-ic{
width:52px;height:52px;border-radius:var(--r-md);margin:0 auto var(--sp-3);
display:flex;align-items:center;justify-content:center;
background:var(--primary-soft);color:var(--primary-ink);
}
.no-pet-b{font-size:var(--fs-lg);font-weight:var(--fw-b)}
.no-pet-p{color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-2) 0 var(--sp-4);line-height:1.6}
/* 顶部固定条:宠物切换 / 社区分类。
它在文档流里而不是浮层——内容永远不会滚到它下面,所以不需要任何背景。
之前给了不透明底色 + 分隔线,等于在页面渐变上贴了块补丁,
屏幕上就出现三条颜色不搭的横带。 */
.top-bar{flex:none;padding:var(--sp-2) var(--pad-x) 0}
.top-bar .pet-switch{padding-bottom:var(--sp-2)}
.top-bar .st-pill{padding-bottom:var(--sp-2)}
.st{white-space:nowrap}
/* 横滑药丸:数量不定、可滚动(社区分类、记录类型筛选) */
.st-pill{padding:0 0 var(--sp-4)}
.st-pill .st-item{
display:inline-block;height:var(--h-sm);line-height:var(--h-sm);padding:0 var(--sp-4);
margin-right:var(--sp-2);border-radius:var(--r-full);border:0.5px solid var(--line);
background:#fff;color:var(--muted);font-size:var(--fs-sm);font-weight:var(--fw-b);
}
.st-pill .st-item.on{color:var(--primary-dark);background:var(--primary-soft);border-color:var(--primary-line)}
/* 灰槽白块:数量固定的互斥视图(计划页三段) */
.st-solid{
display:flex;gap:var(--sp-1);padding:var(--sp-1);border-radius:var(--r-md);
background:#EFE8DE;margin-bottom:var(--sp-4);
}
.st-solid .st-item{
flex:1;height:var(--h-sm);line-height:var(--h-sm);text-align:center;border-radius:var(--r-sm);
color:var(--muted);font-size:var(--fs-md);font-weight:var(--fw-b);
}
.st-solid .st-item.on{background:#fff;color:var(--primary-dark)}
.compose-card{margin-bottom:var(--sp-4)}
.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:40px;height:40px;border-radius:var(--r-sm);background:var(--primary-soft);
display:flex;align-items:center;justify-content:center;font-size:20px;flex:none;
}
.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:0.5px;
}
.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:var(--sp-1);margin-bottom:var(--sp-3)}
.photo-tile{
aspect-ratio:1;border-radius:var(--r-sm);background:var(--primary-soft);
display:flex;align-items:center;justify-content:center;font-size:28px;
}
/* 三等分。原来是 space-around 排三个宽度不一的项,点赞数一变宽度就跳 */
.post-actions{display:flex;border-top:0.5px solid var(--line);padding-top:var(--sp-2)}
.pa-btn{
flex:1;height:36px;
display:flex;align-items:center;justify-content:center;gap:var(--sp-1);
color:var(--muted);font-weight:var(--fw);font-size:var(--fs-sm);
}
.pa-n{line-height:1}
.pa-btn.liked{color:var(--red-ink)}
.pa-btn:active{background:var(--surface-2);border-radius:var(--r-sm)}
/* 分享用微信原生 open-type=share,得抹掉 button 默认外观 */
.pa-share{background:transparent;border:none;padding:0;margin:0;line-height:inherit}
.pa-share::after{border:none}
/* 关注按钮 */
.follow-btn{
flex:none;font-size:var(--fs-cap);font-weight:var(--fw-b);padding:3px var(--sp-3);border-radius:var(--r-full);
color:var(--primary-dark);background:var(--primary-soft);border:0.5px solid var(--primary-line);
}
.follow-btn.on{color:var(--muted);background:#F4F1EC;border-color:var(--line)}
.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);
}
/* 分类标签吸顶,发布按钮收进同一条:原来那个大黑按钮独占 62px,
而它是低频操作,不该在每屏都占着首屏 */
.feed-bar{display:flex;align-items:flex-start;gap:var(--sp-3)}
.feed-bar .st{flex:1;min-width:0}
.feed-post{
flex:none;display:flex;align-items:center;gap:2px;
height:var(--h-sm);padding:0 var(--sp-3);border-radius:var(--r-full);
background:var(--cta);color:#fff;font-size:var(--fs-sm);font-weight:var(--fw-b);
}
</style> </style>
<body class="mp"> <div class="wrap">
<div><h3>首页顶部</h3><div class="phone mp"><div class="nav">肉垫计划</div>
<div class="top-bar"><div class="pet-switch">
<div class="pet-chip active"><div class="mini-avatar">🐶</div>钞票</div>
<div class="pet-chip"><div class="mini-avatar"><i class="pt-i pt-i-plus" style="width:15px;height:15px"></i></div>添加</div></div></div>
<div style="padding:4px 20px 16px">
<div style="font-size:20px;font-weight:600">下午好,开发用户</div>
<div style="font-size:12px;color:#8D8277;margin-top:4px">今天也要照顾好 钞票</div>
</div>
<div style="padding:0 20px 20px"><div class="card" style="margin:0">卡片内容…</div></div></div></div>
<h2>1 · 图标全集(38 个)</h2> <div><h3>社区顶部</h3><div class="phone mp"><div class="nav">宠友圈</div>
<p class="hint">来源 Tabler Icons (MIT)。mask-image + currentColor,不是字体——Tabler 是描边图标,转字体要先展开轮廓,漏了会生成空字形。</p> <div class="top-bar feed-bar">
<div class="wall"><div class="cell"><i class="pt-i pt-i-weight"></i><span>weight</span></div><div class="cell"><i class="pt-i pt-i-poop"></i><span>poop</span></div><div class="cell"><i class="pt-i pt-i-food"></i><span>food</span></div><div class="cell"><i class="pt-i pt-i-symptom"></i><span>symptom</span></div><div class="cell"><i class="pt-i pt-i-medicine"></i><span>medicine</span></div><div class="cell"><i class="pt-i pt-i-cost"></i><span>cost</span></div><div class="cell"><i class="pt-i pt-i-vaccine"></i><span>vaccine</span></div><div class="cell"><i class="pt-i pt-i-deworm"></i><span>deworm</span></div><div class="cell"><i class="pt-i pt-i-photo"></i><span>photo</span></div><div class="cell"><i class="pt-i pt-i-home"></i><span>home</span></div><div class="cell"><i class="pt-i pt-i-plan"></i><span>plan</span></div><div class="cell"><i class="pt-i pt-i-record"></i><span>record</span></div><div class="cell"><i class="pt-i pt-i-report"></i><span>report</span></div><div class="cell"><i class="pt-i pt-i-community"></i><span>community</span></div><div class="cell"><i class="pt-i pt-i-back"></i><span>back</span></div><div class="cell"><i class="pt-i pt-i-next"></i><span>next</span></div><div class="cell"><i class="pt-i pt-i-plus"></i><span>plus</span></div><div class="cell"><i class="pt-i pt-i-check"></i><span>check</span></div><div class="cell"><i class="pt-i pt-i-close"></i><span>close</span></div><div class="cell"><i class="pt-i pt-i-trash"></i><span>trash</span></div><div class="cell"><i class="pt-i pt-i-edit"></i><span>edit</span></div><div class="cell"><i class="pt-i pt-i-share"></i><span>share</span></div><div class="cell"><i class="pt-i pt-i-comment"></i><span>comment</span></div><div class="cell"><i class="pt-i pt-i-like"></i><span>like</span></div><div class="cell"><i class="pt-i pt-i-like-on"></i><span>like-on</span></div><div class="cell"><i class="pt-i pt-i-more"></i><span>more</span></div><div class="cell"><i class="pt-i pt-i-bell"></i><span>bell</span></div><div class="cell"><i class="pt-i pt-i-settings"></i><span>settings</span></div><div class="cell"><i class="pt-i pt-i-ai"></i><span>ai</span></div><div class="cell"><i class="pt-i pt-i-warn"></i><span>warn</span></div><div class="cell"><i class="pt-i pt-i-doc"></i><span>doc</span></div><div class="cell"><i class="pt-i pt-i-note"></i><span>note</span></div><div class="cell"><i class="pt-i pt-i-book"></i><span>book</span></div><div class="cell"><i class="pt-i pt-i-trend"></i><span>trend</span></div><div class="cell"><i class="pt-i pt-i-cat"></i><span>cat</span></div><div class="cell"><i class="pt-i pt-i-dog"></i><span>dog</span></div><div class="cell"><i class="pt-i pt-i-user"></i><span>user</span></div><div class="cell"><i class="pt-i pt-i-clock"></i><span>clock</span></div></div> <div class="st st-pill">
<span class="st-item on">推荐</span><span class="st-item">关注</span><span class="st-item">新手求助</span><span class="st-item">晒宠</span>
<h2>2 · 尺寸可读性</h2>
<p class="hint">32rpx(16px) 是正文里最常用的尺寸,重点看这一档描边够不够清楚。</p>
<div class="sizes">
<span><i class="pt-i pt-i-weight" style="width:16px;height:16px"></i> 16px / 32rpx</span>
<span><i class="pt-i pt-i-weight" style="width:20px;height:20px"></i> 20px / 40rpx</span>
<span><i class="pt-i pt-i-weight" style="width:24px;height:24px"></i> 24px / 48rpx</span>
<span><i class="pt-i pt-i-weight" style="width:28px;height:28px"></i> 28px / 56rpx</span>
</div>
<h2>3 · tabBar</h2>
<div class="tabbar">
<div class="tb on"><i class="pt-i pt-i-home"></i>首页</div>
<div class="tb"><i class="pt-i pt-i-plan"></i>计划</div>
<div class="tb"><i class="pt-i pt-i-record"></i>记录</div>
<div class="tb"><i class="pt-i pt-i-report"></i>报告</div>
<div class="tb"><i class="pt-i pt-i-community"></i>社区</div>
</div>
<h2>4 · 圆角 5 档</h2>
<div class="row">
<span class="tag" style="border-radius:var(--r-xs)">xs 12</span>
<span class="tag" style="border-radius:var(--r-sm)">sm 20</span>
<span class="tag" style="border-radius:var(--r-md)">md 28</span>
<span class="tag" style="border-radius:var(--r-lg)">lg 40</span>
<span class="tag" style="border-radius:var(--r-full)">full</span>
</div>
<h2>5 · 字号 6 档 / 字重只留两档</h2>
<div>
<div style="font-size:var(--fs-2xl);font-weight:var(--fw-b)">2xl 48 · 页面大标题</div>
<div style="font-size:var(--fs-xl);font-weight:var(--fw-b)">xl 40 · 卡片主数值</div>
<div style="font-size:var(--fs-lg);font-weight:var(--fw-b)">lg 32 · 小标题与按钮</div>
<div style="font-size:var(--fs-md)">md 28 · 正文</div>
<div style="font-size:var(--fs-sm);color:var(--muted)">sm 24 · 辅助说明</div>
<div style="font-size:var(--fs-cap);color:var(--muted)">cap 22 · 角标</div>
</div>
<h2>6 · 按钮</h2>
<div class="row">
<div class="btn btn-primary">保存记录</div>
<div class="btn btn-dark"><i class="pt-i pt-i-ai" style="width:16px;height:16px"></i> 问问 AI</div>
<div class="btn btn-ghost">取消</div>
<div class="btn btn-soft">开通 Pro</div>
<div class="btn btn-small">管理</div>
</div>
<p class="hint">主按钮保持暖橙:它有 36 处使用、绝大多数是弹层里的「保存」,改成深色会让全站主色消失。深色只给页面级首要 CTA。</p>
<h2>7 · 标签 / 状态 chip / 4 色分类块</h2>
<div class="row">
<span class="tag">已完成</span><span class="tag warn">待处理</span>
<span class="tag red">异常</span><span class="tag blue">经验</span><span class="tag purple">精选</span>
</div>
<div class="row" style="margin-top:10px">
<span class="chip"><i class="pt-i pt-i-trend" style="width:12px;height:12px"></i> 体重稳定</span>
<span class="chip green"><i class="pt-i pt-i-check" style="width:12px;height:12px"></i> 任务全清</span>
<span class="chip red"><i class="pt-i pt-i-warn" style="width:12px;height:12px"></i> 驱虫将到期</span>
</div>
<div class="grid4" style="margin-top:14px">
<div class="qk tone-1"><i class="pt-i pt-i-weight" style="width:22px;height:22px"></i><span>体重</span></div>
<div class="qk tone-2"><i class="pt-i pt-i-food" style="width:22px;height:22px"></i><span>饮食</span></div>
<div class="qk tone-3"><i class="pt-i pt-i-symptom" style="width:22px;height:22px"></i><span>异常</span></div>
<div class="qk tone-4"><i class="pt-i pt-i-photo" style="width:22px;height:22px"></i><span>照片</span></div>
</div>
<h2>8 · 卡片 / 任务 / 统计 / 进度</h2>
<div class="stage">
<div class="card">
<div class="section-head"><span class="sh-title">今日任务</span><span class="link">管理</span></div>
<div class="task-list">
<div class="task done"><span class="circle"><i class="pt-i pt-i-check" style="width:11px;height:11px"></i></span><div class="task-text"><div class="tt-b">早餐喂食</div><div class="tt-p">已完成</div></div></div>
<div class="task"><span class="circle"></span><div class="task-text"><div class="tt-b">记录体重</div><div class="tt-p">上次 4.2kg,建议每周一次</div></div><span class="priority">重要</span></div>
</div> </div>
<div class="feed-post"><i class="pt-i pt-i-plus" style="width:14px;height:14px"></i>发布</div>
</div> </div>
<div class="card"> <div style="padding:0 20px 20px"><div class="post-card" style="margin:0">
<div class="progress-row"><span>本周完成度</span><span>72%</span></div> <div class="post-head"><div class="post-avatar">🐮</div>
<div class="bar"><div class="bar-fill" style="width:72%"></div></div> <div class="post-user"><div class="pu-b">奶牛猫饲养员</div><div class="pu-s">28 分钟前</div></div>
<div class="stats-grid"> <div class="follow-btn">+ 关注</div></div>
<div class="stat"><div class="stat-b">9</div><div class="stat-s">记录</div></div> <div class="post-content">刚打完第二针疫苗…</div></div></div></div></div>
<div class="stat"><div class="stat-b">4.2</div><div class="stat-s">体重kg</div></div>
<div class="stat"><div class="stat-b">12</div><div class="stat-s">距驱虫</div></div>
</div>
<div class="record-row"><span class="bold">疫苗进度</span><span class="muted">3/4 已完成</span></div>
<div class="record-row"><span class="bold">驱虫状态</span><span class="muted">12 天后到期</span></div>
</div>
</div>
<h2>9 · 表单族(弹层里 28 种 type 全靠这几个类)</h2>
<div class="stage">
<div class="card">
<div class="field"><label>体重 (kg)</label><div class="input">4.2</div></div>
<div class="field"><label>状态</label>
<div class="seg"><div class="seg-btn selected">正常</div><div class="seg-btn">软便</div><div class="seg-btn">拉稀</div></div>
</div>
<div class="field"><label>症状</label>
<div class="mini-options"><span class="option selected">呕吐</span><span class="option">拉稀</span><span class="option">精神差</span><span class="option">皮肤红</span></div>
</div>
<div class="field"><label>备注</label><div class="textarea placeholder">写点什么…</div></div>
<div class="check-list">
<div class="check selected"><span class="box"><i class="pt-i pt-i-check" style="width:11px;height:11px"></i></span>健康档案与体重趋势</div>
<div class="check"><span class="box"></span>AI 深度分析</div>
</div>
<div class="sheet-actions"><div class="btn btn-ghost">取消</div><div class="btn btn-primary">保存</div></div>
</div>
</div>
<h2>10 · 其它公共块</h2>
<div class="stage">
<div class="card">
<div class="risk-box"><i class="pt-i pt-i-warn" style="width:18px;height:18px"></i> 风险中 · 建议观察 24 小时</div>
<div class="article-card no-shadow"><div class="article-icon"><i class="pt-i pt-i-vaccine" style="width:24px;height:24px"></i></div><div><div class="ac-b">幼猫疫苗怎么打</div><div class="ac-p">首免、二免、三免的时间点和注意事项</div></div></div>
<div class="plan-chat"><div class="msg ai">小橘最近体重稳定,继续保持现在的喂食量就好。</div><div class="msg user">那驱虫呢?</div></div>
<div class="empty">还没有记录,来记第一笔吧</div>
</div>
</div>
<h2>11 · 首页门面(方案 C</h2>
<p class="hint">状态环 + 状态 chip 做门面,今日任务提到首屏,4 色分类块替掉 8 宫格 emoji,最后一块深色 CTA。</p>
<div class="stage">
<div class="card" style="display:flex;gap:14px;align-items:center">
<div style="position:relative;width:90px;height:90px;flex:none">
<img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2242%22%20fill%3D%22none%22%20stroke%3D%22%23FFE6BA%22%20stroke-width%3D%228%22%20stroke-linecap%3D%22round%22%2F%3E%3Cg%20transform%3D%22rotate(-90%2050%2050)%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2242%22%20fill%3D%22none%22%20stroke%3D%22%23F5A947%22%20stroke-width%3D%228%22%20stroke-linecap%3D%22round%22%20stroke-dasharray%3D%22263.9%22%20stroke-dashoffset%3D%2273.9%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E" style="width:100%;height:100%;display:block">
<div style="position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center">
<div style="font-size:var(--fs-xl);font-weight:var(--fw-b);line-height:1">72<span style="font-size:var(--fs-cap)">%</span></div>
<div style="font-size:var(--fs-cap);color:var(--muted)">完成度</div>
</div>
</div>
<div style="flex:1;min-width:0">
<div style="font-size:var(--fs-lg);font-weight:var(--fw-b)">小橘今天状态不错</div>
<div class="muted" style="font-size:var(--fs-sm);margin-top:2px">还有 3 件事没做完</div>
<div class="row" style="gap:6px;margin-top:8px">
<span class="chip green"><i class="pt-i pt-i-trend" style="width:12px;height:12px"></i> 体重稳定</span>
<span class="chip"><i class="pt-i pt-i-warn" style="width:12px;height:12px"></i> 驱虫将到期</span>
</div>
</div>
</div>
<div class="card">
<div class="section-head"><span class="sh-title">今日任务</span><span class="link">管理</span></div>
<div class="task-list">
<div class="task done"><span class="circle"><i class="pt-i pt-i-check" style="width:11px;height:11px"></i></span><div class="task-text"><div class="tt-b">早餐喂食</div></div></div>
<div class="task"><span class="circle"></span><div class="task-text"><div class="tt-b">记录体重</div><div class="tt-p">上次 4.2kg</div></div><span class="priority">重要</span></div>
<div class="task"><span class="circle"></span><div class="task-text"><div class="tt-b">梳毛 10 分钟</div></div></div>
</div>
</div>
<div class="card">
<div class="section-head"><span class="sh-title">快速记录</span></div>
<div class="grid4" style="grid-template-columns:repeat(4,1fr)">
<div class="qk tone-1"><i class="pt-i pt-i-weight" style="width:22px;height:22px"></i><span>体重</span></div>
<div class="qk tone-2"><i class="pt-i pt-i-food" style="width:22px;height:22px"></i><span>饮食</span></div>
<div class="qk tone-3"><i class="pt-i pt-i-symptom" style="width:22px;height:22px"></i><span>异常</span></div>
<div class="qk tone-4"><i class="pt-i pt-i-photo" style="width:22px;height:22px"></i><span>照片</span></div>
</div>
</div>
<div class="btn btn-dark btn-block"><i class="pt-i pt-i-ai" style="width:16px;height:16px"></i> 问问 AI 养宠助手</div>
</div>