feat(fe): 新增 seg-tabs 与 stat-ring 组件
seg-tabs 合并三套长得像但尺寸各不同的横向切换:plan 的 .inline-tabs
(灰槽白块)、community 的 .feed-tab(白药丸+阴影)、record 的 .rf-chip
(白药丸无阴影,高度还差 10rpx)。variant="solid|pill" 二选一,items
同时支持字符串数组和 {key,label} 数组。
stat-ring 是首页门面用的完成度环。用 SVG data-uri 画,和 record 页的
体重折线图同一套路——canvas 在 scroll-view 里的层级、滚动跟随和截图
行为全是坑。中心内容走 slot。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
// 横向切换控件。此前有三套长得像但尺寸各不同的实现:
|
||||
// plan 的 .inline-tabs(灰槽 + 白块)、community 的 .feed-tab(白药丸 + 橙选中 + 阴影)、
|
||||
// record 的 .rf-chip(白药丸 + 橙选中 + 无阴影,高度还差 10rpx)
|
||||
// 这里合成一个:variant="solid" 对应灰槽白块,variant="pill" 对应横滑药丸。
|
||||
//
|
||||
// items 支持两种写法:
|
||||
// ['推荐','关注','经验'] —— current 传下标
|
||||
// [{key:'',label:'全部'},{key:'weight',label:'体重'}] —— current 传 key
|
||||
Component({
|
||||
options: { addGlobalClass: true },
|
||||
properties: {
|
||||
items: { type: Array, value: [] },
|
||||
current: { type: null, value: 0 },
|
||||
variant: { type: String, value: 'pill' },
|
||||
},
|
||||
data: { list: [] },
|
||||
observers: {
|
||||
'items,current': function (items, current) {
|
||||
const byKey = typeof current === 'string';
|
||||
const list = (items || []).map((it, i) => {
|
||||
const obj = it && typeof it === 'object';
|
||||
const key = obj ? it.key : i;
|
||||
return {
|
||||
key,
|
||||
label: obj ? it.label : it,
|
||||
active: byKey ? key === current : i === Number(current),
|
||||
};
|
||||
});
|
||||
this.setData({ list });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onTap(e) {
|
||||
const { index, key } = e.currentTarget.dataset;
|
||||
this.triggerEvent('change', { index: Number(index), key });
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<scroll-view wx:if="{{variant === 'pill'}}" class="st st-pill" scroll-x="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||
<view wx:for="{{list}}" wx:key="key" class="st-item {{item.active ? 'on' : ''}}"
|
||||
data-index="{{index}}" data-key="{{item.key}}" bindtap="onTap">{{item.label}}</view>
|
||||
</scroll-view>
|
||||
|
||||
<view wx:else class="st st-solid">
|
||||
<view wx:for="{{list}}" wx:key="key" class="st-item {{item.active ? 'on' : ''}}"
|
||||
data-index="{{index}}" data-key="{{item.key}}" bindtap="onTap">{{item.label}}</view>
|
||||
</view>
|
||||
@@ -0,0 +1,21 @@
|
||||
.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:1rpx 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)}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 完成度环。用 SVG data-uri 画(和 record 页的体重折线图同一套路),不用 canvas:
|
||||
// canvas 在 scroll-view 里的层级、滚动跟随和截图行为全是坑。
|
||||
const TAU = 2 * Math.PI;
|
||||
const R = 42; // viewBox 100 下的半径,留出描边宽度
|
||||
|
||||
function ringSvg(percent, stroke, track, fill) {
|
||||
const c = TAU * R;
|
||||
const p = Math.max(0, Math.min(100, Number(percent) || 0));
|
||||
const off = c * (1 - p / 100);
|
||||
const circle = (color, dash) =>
|
||||
'<circle cx="50" cy="50" r="' + R + '" fill="none" stroke="' + color +
|
||||
'" stroke-width="' + stroke + '" stroke-linecap="round"' + dash + '/>';
|
||||
return (
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">' +
|
||||
circle(track, '') +
|
||||
'<g transform="rotate(-90 50 50)">' +
|
||||
circle(fill, ' stroke-dasharray="' + c.toFixed(1) + '" stroke-dashoffset="' + off.toFixed(1) + '"') +
|
||||
'</g></svg>'
|
||||
);
|
||||
}
|
||||
|
||||
Component({
|
||||
options: { addGlobalClass: true },
|
||||
properties: {
|
||||
percent: { type: Number, value: 0 },
|
||||
size: { type: Number, value: 180 }, // rpx
|
||||
stroke: { type: Number, value: 8 }, // viewBox 单位,即直径的百分比
|
||||
track: { type: String, value: '#FFE6BA' },
|
||||
fill: { type: String, value: '#F5A947' },
|
||||
},
|
||||
data: { src: '', boxStyle: '' },
|
||||
observers: {
|
||||
'percent,size,stroke,track,fill': function (percent, size, stroke, track, fill) {
|
||||
const svg = ringSvg(percent, stroke, track, fill);
|
||||
this.setData({
|
||||
src: 'data:image/svg+xml,' + encodeURIComponent(svg),
|
||||
boxStyle: 'width:' + size + 'rpx;height:' + size + 'rpx',
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<view class="ring" style="{{boxStyle}}">
|
||||
<image class="ring-img" src="{{src}}" mode="widthFix"></image>
|
||||
<view class="ring-center"><slot></slot></view>
|
||||
</view>
|
||||
@@ -0,0 +1,6 @@
|
||||
.ring{position:relative;flex:none}
|
||||
.ring-img{width:100%;height:100%;display:block}
|
||||
.ring-center{
|
||||
position:absolute;left:0;top:0;right:0;bottom:0;
|
||||
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
||||
}
|
||||
@@ -526,3 +526,46 @@ view,text{box-sizing:border-box}
|
||||
</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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user