feat: Kinetic Dark 设计语言 + Lucide 图标迁移 + 首页轻量个性化
- 建立 Lucide 子集 iconfont(49 字形),全站 emoji 替换为 fc-icon 组件 - 重塑 Design Token(冷中性近黑底 + Volt 荧光绿 #B6F24E),消除硬编码颜色 - 首页登录后个性化:我的计划 / 我的收藏 / 今日推荐(纯前端复用现有 API) - 后端 DEFAULT_EMOJI→Lucide 名,PALETTE 对齐 Kinetic Dark 语义色板 - 修复 person-standing 缺失字形;14 个官方组合 seed emoji 全部映射到现有字形
This commit is contained in:
@@ -1,54 +1,116 @@
|
||||
/* ===== FITCOACH 设计系统:高端暗金奢华主题 ===== */
|
||||
/* ===== Lucide 子集 iconfont(动态生成,font-family: fc-icon) ===== */
|
||||
@font-face {
|
||||
font-family: "fc-icon";
|
||||
src: url('assets/fonts/fc-icon.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
/* ===== FITCOACH 设计系统:动能暗色(Kinetic Dark) =====
|
||||
强调色 accent = Volt 荧光绿 #B6F24E(用户拍板)。
|
||||
为兼容现有页面对 --gold/--teal 等变量名的引用,保留变量名、改其色值为新调色板;
|
||||
并补全套语义色/字号/间距/圆角/阴影/动效 Token(真源 design-system/design-tokens.json)。 */
|
||||
page {
|
||||
/* 设计令牌 */
|
||||
--bg: #0E1110;
|
||||
--bg-grad: linear-gradient(160deg, #16201B 0%, #0E1110 55%);
|
||||
--surface: #171C19;
|
||||
--surface-2: #1F2622;
|
||||
--gold: #D9B36C;
|
||||
--gold-2: #F0D9A8;
|
||||
--gold-soft: rgba(217,179,108,0.12);
|
||||
--gold-line: rgba(217,179,108,0.28);
|
||||
--teal: #58C9B9;
|
||||
--text: #F3F5F2;
|
||||
--text-2: #98A39B;
|
||||
--text-3: #6B746D;
|
||||
--line: rgba(255,255,255,0.07);
|
||||
--radius: 28rpx;
|
||||
--radius-sm: 18rpx;
|
||||
--shadow: 0 12rpx 40rpx rgba(0,0,0,0.35);
|
||||
/* —— 颜色 —— */
|
||||
--bg: #0B0F0E; /* 页面背景(冷中性近黑) */
|
||||
--bg-grad: radial-gradient(120% 80% at 50% 0%, rgba(17,23,20,0) 0%, #0B0F0E 60%);
|
||||
--surface: #141A18; /* 卡片/容器 */
|
||||
--surface-2: #1C2421; /* 次级表面 chip/输入/选中 */
|
||||
--surface-warm: #1A211E; /* 悬浮/高亮表面 */
|
||||
|
||||
--text: #ECF1EF; /* 主文本 */
|
||||
--text-2: #9BA7A1; /* 次级文本 */
|
||||
--text-3: #6B7670; /* 三级/元数据(仅 ≥24rpx) */
|
||||
|
||||
--line: rgba(255,255,255,0.08); /* 默认边框/分隔线 */
|
||||
--border-strong: rgba(255,255,255,0.14); /* 强调边框(选中态) */
|
||||
--border-soft: rgba(255,255,255,0.05); /* 内部行分隔 */
|
||||
|
||||
/* 强调色(原 --gold 命名保留以兼容引用,值改为 Volt 绿) */
|
||||
--gold: #B6F24E; /* accent 主色 */
|
||||
--gold-2: #C7F56E; /* accent hover */
|
||||
--gold-soft: rgba(182,242,78,0.12); /* accent 淡底 */
|
||||
--gold-line: rgba(182,242,78,0.28); /* accent 边框 */
|
||||
--teal: #2BD9A6; /* success(原语义) */
|
||||
|
||||
/* 语义色(新增) */
|
||||
--accent: #B6F24E;
|
||||
--accent-hover: #C7F56E;
|
||||
--accent-active: #9EDB3A;
|
||||
--accent-on: #0E1A05; /* accent 背景上的前景(深绿,对比>7:1) */
|
||||
--accent-soft: rgba(182,242,78,0.12);
|
||||
--accent-ring: rgba(182,242,78,0.38);
|
||||
--success: #2BD9A6;
|
||||
--success-on: #04201A;
|
||||
--success-soft: rgba(43,217,166,0.12);
|
||||
--warn: #F2B53D;
|
||||
--danger: #FF5C5C; /* 替代旧 #E5544B */
|
||||
--danger-soft: rgba(255,92,92,0.12);
|
||||
--info: #4FA8FF;
|
||||
--bar-bg: rgba(11,15,14,0.92); /* 底部操作条毛玻璃底(=bg 带透明) */
|
||||
--float-bg: rgba(11,15,14,0.55); /* 浮动按钮底 */
|
||||
--scrim: rgba(0,0,0,0.5); /* 弹层遮罩 */
|
||||
|
||||
/* 字体三族(含中文) */
|
||||
--font-display: 'Manrope', 'PingFang SC', 'HarmonyOS Sans SC', 'Source Han Sans SC', 'Noto Sans CJK SC', sans-serif;
|
||||
--font-body: 'PingFang SC', 'HarmonyOS Sans SC', 'Source Han Sans SC', 'Noto Sans CJK SC', -apple-system, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace;
|
||||
|
||||
/* 字号阶梯(rpx,22–72) */
|
||||
--fs-xs: 22rpx; --fs-sm: 24rpx; --fs-base: 28rpx; --fs-md: 30rpx;
|
||||
--fs-lg: 32rpx; --fs-xl: 36rpx; --fs-2xl: 44rpx; --fs-3xl: 56rpx; --fs-4xl: 72rpx;
|
||||
|
||||
/* 字重 / 字距 */
|
||||
--fw-regular: 400; --fw-medium: 500; --fw-semibold: 600;
|
||||
--ls-eyebrow: 0.08em; --ls-title: -0.02em; --ls-body: 0;
|
||||
|
||||
/* 间距尺度(4px 网格) */
|
||||
--sp-1: 8rpx; --sp-2: 16rpx; --sp-3: 24rpx; --sp-4: 32rpx; --sp-5: 40rpx;
|
||||
--sp-6: 48rpx; --sp-8: 64rpx; --sp-10: 80rpx; --sp-12: 96rpx; --sp-16: 128rpx;
|
||||
|
||||
/* 圆角(收紧) */
|
||||
--radius-sm: 16rpx; --radius: 24rpx; --radius-md: 24rpx;
|
||||
--radius-lg: 32rpx; --radius-xl: 40rpx; --radius-pill: 9999rpx;
|
||||
|
||||
/* 阴影(Hairline First:默认无阴影,靠发丝边框) */
|
||||
--shadow: none;
|
||||
--shadow-ring: 0 0 0 1rpx rgba(255,255,255,0.08);
|
||||
--shadow-raised: 0 8rpx 24rpx rgba(0,0,0,0.32);
|
||||
--shadow-float: 0 12rpx 40rpx rgba(0,0,0,0.45);
|
||||
--shadow-glow: 0 8rpx 24rpx rgba(182,242,78,0.28); /* 唯一允许的彩色阴影:CTA */
|
||||
--focus-ring: 0 0 0 3rpx rgba(182,242,78,0.45);
|
||||
|
||||
/* 动效 */
|
||||
--motion-fast: 150ms; --motion-base: 200ms; --motion-slow: 300ms;
|
||||
--ease: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: -apple-system, "PingFang SC", "Helvetica Neue", Helvetica, sans-serif;
|
||||
font-size: 28rpx;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--fs-base);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
view, text, scroll-view, image, input {
|
||||
view, text, scroll-view, image, input, textarea, button {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ===== 通用工具类 ===== */
|
||||
.eyebrow {
|
||||
font-size: 22rpx;
|
||||
letter-spacing: 4rpx;
|
||||
color: var(--gold);
|
||||
font-size: var(--fs-xs);
|
||||
letter-spacing: var(--ls-eyebrow);
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-weight: var(--fw-semibold);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 40rpx 32rpx;
|
||||
}
|
||||
.section + .section {
|
||||
border-top: 1rpx solid var(--line);
|
||||
}
|
||||
.section { padding: var(--sp-5) var(--sp-4); }
|
||||
.section + .section { border-top: 1rpx solid var(--line); }
|
||||
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
border: 1rpx solid var(--line);
|
||||
}
|
||||
|
||||
@@ -56,10 +118,10 @@ view, text, scroll-view, image, input {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 10rpx 24rpx;
|
||||
border-radius: 999rpx;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--surface-2);
|
||||
color: var(--text-2);
|
||||
font-size: 24rpx;
|
||||
font-size: var(--fs-sm);
|
||||
border: 1rpx solid var(--line);
|
||||
}
|
||||
|
||||
@@ -70,7 +132,7 @@ view, text, scroll-view, image, input {
|
||||
|
||||
.empty {
|
||||
color: var(--text-3);
|
||||
font-size: 26rpx;
|
||||
font-size: var(--fs-sm);
|
||||
text-align: center;
|
||||
padding: 80rpx 0;
|
||||
}
|
||||
@@ -80,24 +142,22 @@ view, text, scroll-view, image, input {
|
||||
color: var(--text-2);
|
||||
background: var(--surface-2);
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 999rpx;
|
||||
border-radius: var(--radius-pill);
|
||||
}
|
||||
.tag--gold {
|
||||
color: var(--gold-2);
|
||||
background: var(--gold-soft);
|
||||
}
|
||||
|
||||
/* ===== 横向滚动提示:右侧渐隐遮罩,提示「还可向右滑动」 ===== */
|
||||
/* ===== 横向滚动右侧渐隐遮罩(提示可右滑) ===== */
|
||||
.hscroll, .chip-scroll, .filterbar, .emoji-row, .prog-scroll {
|
||||
position: relative;
|
||||
}
|
||||
.hscroll::after, .chip-scroll::after, .filterbar::after, .emoji-row::after, .prog-scroll::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0; right: 0; bottom: 0;
|
||||
width: 56rpx;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(90deg, rgba(14,17,16,0) 0%, rgba(14,17,16,0.9) 100%);
|
||||
background: linear-gradient(90deg, rgba(11,15,14,0) 0%, rgba(11,15,14,0.9) 100%);
|
||||
}
|
||||
|
||||
51
miniprogram/assets/fonts/fc-icon.json
Normal file
51
miniprogram/assets/fonts/fc-icon.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"zap": 61697,
|
||||
"x-circle": 61698,
|
||||
"user": 61699,
|
||||
"trophy": 61700,
|
||||
"trending-up": 61701,
|
||||
"trash-2": 61702,
|
||||
"target": 61703,
|
||||
"star": 61704,
|
||||
"sparkles": 61705,
|
||||
"sliders-horizontal": 61706,
|
||||
"share-2": 61707,
|
||||
"settings": 61708,
|
||||
"search": 61709,
|
||||
"rotate-cw": 61710,
|
||||
"refresh-cw": 61711,
|
||||
"plus": 61712,
|
||||
"play": 61713,
|
||||
"person-standing": 61714,
|
||||
"medal": 61715,
|
||||
"log-out": 61716,
|
||||
"layout-grid": 61717,
|
||||
"layers": 61718,
|
||||
"info": 61719,
|
||||
"image": 61720,
|
||||
"home": 61721,
|
||||
"heart": 61722,
|
||||
"grid-2x2": 61723,
|
||||
"folder-open": 61724,
|
||||
"flame": 61725,
|
||||
"filter": 61726,
|
||||
"dumbbell": 61727,
|
||||
"download": 61728,
|
||||
"clock": 61729,
|
||||
"clipboard-list": 61730,
|
||||
"circle-x": 61731,
|
||||
"chevron-right": 61732,
|
||||
"chevron-down": 61733,
|
||||
"check": 61734,
|
||||
"check-circle": 61735,
|
||||
"calendar": 61736,
|
||||
"bookmark": 61737,
|
||||
"bike": 61738,
|
||||
"bell": 61739,
|
||||
"award": 61740,
|
||||
"arrow-right": 61741,
|
||||
"arrow-left": 61742,
|
||||
"alert-circle": 61743,
|
||||
"activity": 61744,
|
||||
"accessibility": 61745
|
||||
}
|
||||
BIN
miniprogram/assets/fonts/fc-icon.ttf
Normal file
BIN
miniprogram/assets/fonts/fc-icon.ttf
Normal file
Binary file not shown.
@@ -1,17 +1,28 @@
|
||||
// 头像组件:纯 emoji 圆形头像(无二进制图片)。
|
||||
// props: avatar(emoji 字符串), size(rpx, 默认 120), bg(背景色, 默认 gold-soft)
|
||||
const { iconName } = require('../../utils/icon-name');
|
||||
|
||||
// 头像组件:圆形 Lucide 图标头像(无二进制图片)。
|
||||
// props: avatar(lucide 图标名 或 历史 emoji), size(rpx, 默认 120), bg(背景色)
|
||||
Component({
|
||||
properties: {
|
||||
avatar: { type: String, value: '💪' },
|
||||
avatar: { type: String, value: 'user' },
|
||||
size: { type: Number, value: 120 },
|
||||
bg: { type: String, value: 'rgba(217,179,108,0.12)' }
|
||||
bg: { type: String, value: 'rgba(182,242,78,0.12)' }
|
||||
},
|
||||
data: {
|
||||
fontSize: 60
|
||||
glyph: 'user',
|
||||
iconSize: 60
|
||||
},
|
||||
observers: {
|
||||
size: function (size) {
|
||||
this.setData({ fontSize: Math.round(size * 0.5) });
|
||||
avatar(v) {
|
||||
this.setData({ glyph: iconName(v, 'user') });
|
||||
},
|
||||
size(s) {
|
||||
this.setData({ iconSize: Math.round(s * 0.5) });
|
||||
}
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.setData({ glyph: iconName(this.data.avatar, 'user'), iconSize: Math.round(this.data.size * 0.5) });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
"usingComponents": {
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<view
|
||||
class="avatar"
|
||||
style="width: {{size}}rpx; height: {{size}}rpx; background: {{bg}}; font-size: {{fontSize}}rpx;">
|
||||
{{avatar || '💪'}}
|
||||
style="width: {{size}}rpx; height: {{size}}rpx; background: {{bg}};">
|
||||
<fc-icon name="{{glyph}}" size="{{iconSize}}" color="var(--accent)" />
|
||||
</view>
|
||||
|
||||
@@ -4,10 +4,10 @@ Component({
|
||||
},
|
||||
data: {
|
||||
items: [
|
||||
{ key: 'home', label: '首页', emoji: '🏠', page: '/pages/index/index' },
|
||||
{ key: 'category', label: '分类', emoji: '📚', page: '/pages/category/category' },
|
||||
{ key: 'plans', label: '计划', emoji: '📋', page: '/pages/my-plans/my-plans' },
|
||||
{ key: 'mine', label: '我的', emoji: '👤', page: '/pages/profile/profile' }
|
||||
{ key: 'home', label: '首页', icon: 'home', page: '/pages/index/index' },
|
||||
{ key: 'category', label: '分类', icon: 'layout-grid', page: '/pages/category/category' },
|
||||
{ key: 'plans', label: '计划', icon: 'clipboard-list', page: '/pages/my-plans/my-plans' },
|
||||
{ key: 'mine', label: '我的', icon: 'user', page: '/pages/profile/profile' }
|
||||
]
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
"usingComponents": {
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class="bn__item {{active === item.key ? 'bn__item--active' : ''}}"
|
||||
data-page="{{item.page}}"
|
||||
bindtap="onTap">
|
||||
<text class="bn__emoji">{{item.emoji}}</text>
|
||||
<fc-icon name="{{item.icon}}" size="40" color="{{active === item.key ? 'var(--accent)' : 'var(--text-3)'}}" />
|
||||
<text class="bn__label">{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
background: linear-gradient(90deg, var(--gold), var(--gold-2));
|
||||
}
|
||||
.bn__item--active { color: var(--gold); }
|
||||
.bn__emoji { font-size: 40rpx; line-height: 1; }
|
||||
.bn__emoji { line-height: 1; }
|
||||
.bn__label { font-size: 22rpx; margin-top: 6rpx; }
|
||||
.bn__item--active .bn__label { color: var(--gold-2); }
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
"usingComponents": {
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
mode="aspectFill"
|
||||
lazy-load="true"
|
||||
binderror="onImgError" />
|
||||
<view wx:else class="ex-card__ph">🏋️</view>
|
||||
<view wx:else class="ex-card__ph"><fc-icon name="dumbbell" size="64" color="var(--text-3)" /></view>
|
||||
<view wx:if="{{exercise.typeLabel}}" class="ex-card__type">{{exercise.typeLabel}}</view>
|
||||
<view wx:if="{{score > 0}}" class="ex-card__score">匹配 {{score}}</view>
|
||||
</view>
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
.ex-card__type {
|
||||
position: absolute; left: 12rpx; top: 12rpx;
|
||||
font-size: 20rpx; color: var(--text-2);
|
||||
background: rgba(0,0,0,0.45); padding: 4rpx 14rpx; border-radius: 999rpx;
|
||||
background: var(--scrim); padding: 4rpx 14rpx; border-radius: 999rpx;
|
||||
}
|
||||
.ex-card__score {
|
||||
position: absolute; right: 12rpx; top: 12rpx;
|
||||
font-size: 20rpx; color: #0E1110; font-weight: 700;
|
||||
font-size: 20rpx; color: var(--accent-on); font-weight: 700;
|
||||
background: var(--gold); padding: 4rpx 14rpx; border-radius: 999rpx;
|
||||
}
|
||||
.ex-card__body { padding: 16rpx 18rpx 20rpx; }
|
||||
|
||||
23
miniprogram/components/fc-icon/index.js
Normal file
23
miniprogram/components/fc-icon/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const iconMap = require('../../utils/icon-map');
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
// Lucide 图标名,如 search / dumbbell / heart
|
||||
name: { type: String, value: '' },
|
||||
// 字号(rpx)
|
||||
size: { type: Number, value: 20 },
|
||||
// 颜色,默认跟随当前文字色(currentColor 在 iconfont 下用具体色)
|
||||
color: { type: String, value: '' },
|
||||
},
|
||||
data: { glyph: '' },
|
||||
observers: {
|
||||
name(n) {
|
||||
this.setData({ glyph: (iconMap[n] || '') });
|
||||
},
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.setData({ glyph: (iconMap[this.data.name] || '') });
|
||||
},
|
||||
},
|
||||
});
|
||||
4
miniprogram/components/fc-icon/index.json
Normal file
4
miniprogram/components/fc-icon/index.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
1
miniprogram/components/fc-icon/index.wxml
Normal file
1
miniprogram/components/fc-icon/index.wxml
Normal file
@@ -0,0 +1 @@
|
||||
<text class="fc-icon" style="font-size:{{size}}rpx;{{color ? 'color:' + color + ';' : ''}}">{{glyph}}</text>
|
||||
10
miniprogram/components/fc-icon/index.wxss
Normal file
10
miniprogram/components/fc-icon/index.wxss
Normal file
@@ -0,0 +1,10 @@
|
||||
.fc-icon {
|
||||
font-family: "fc-icon";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
}
|
||||
.gif-loading__dot {
|
||||
width: 18rpx; height: 18rpx; border-radius: 999rpx;
|
||||
background: var(--gold, #c9a24b);
|
||||
background: var(--accent);
|
||||
animation: gif-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
.gif-loading__txt {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.navbar {
|
||||
background: linear-gradient(180deg, rgba(22,32,27,0.96) 0%, rgba(14,17,16,0.55) 100%);
|
||||
background: linear-gradient(180deg, var(--bar-bg) 0%, var(--float-bg) 100%);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
|
||||
@@ -2,6 +2,7 @@ const app = getApp();
|
||||
const planSvc = require('../../services/plan');
|
||||
const favSvc = require('../../services/favorite');
|
||||
const auth = require('../../utils/auth');
|
||||
const { iconName } = require('../../utils/icon-name');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -29,7 +30,7 @@ Page({
|
||||
this.setData({ loading: true });
|
||||
planSvc.officialExercises(this.data.slug, 1, 60).then((res) => {
|
||||
this.setData({
|
||||
collection: res.collection,
|
||||
collection: { ...res.collection, icon: iconName(res.collection.emoji) },
|
||||
exercises: (res && res.items) || [],
|
||||
total: (res && res.total) || 0,
|
||||
loading: false
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"navbar": "/components/navbar/index",
|
||||
"exercise-card": "/components/exercise-card/index",
|
||||
"section-header": "/components/section-header/index",
|
||||
"level-tag": "/components/level-tag/index"
|
||||
"level-tag": "/components/level-tag/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<navbar title="{{collection.name}}" show-back />
|
||||
|
||||
<view class="header" style="background: linear-gradient(135deg, {{collection.color}}26, var(--surface));">
|
||||
<view class="header__emoji" style="background: {{collection.color}}22; color: {{collection.color}};">{{collection.emoji}}</view>
|
||||
<view class="header__emoji" style="background: {{collection.color}}22; color: {{collection.color}};"><fc-icon name="{{collection.icon}}" size="48" color="{{collection.color}}" /></view>
|
||||
<view class="header__name" style="color: {{collection.color}};">{{collection.name}}</view>
|
||||
<view wx:if="{{collection.nameEn}}" class="header__en">{{collection.nameEn}}</view>
|
||||
<view class="header__desc">{{collection.description}}</view>
|
||||
@@ -31,7 +31,8 @@
|
||||
|
||||
<view class="actionbar">
|
||||
<view class="btn btn--ghost {{favorited ? 'btn--faved' : ''}}" bindtap="onToggleFav">
|
||||
<text>{{favorited ? '♥ 已收藏' : '♡ 收藏'}}</text>
|
||||
<fc-icon name="heart" size="28" color="{{favorited ? 'var(--accent)' : 'var(--text)'}}" />
|
||||
<text>{{favorited ? '已收藏' : '收藏'}}</text>
|
||||
</view>
|
||||
<view class="btn btn--gold" bindtap="onImport">
|
||||
<text>+ 导入我的计划</text>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
.actionbar {
|
||||
position: fixed; left: 0; right: 0; bottom: 0;
|
||||
display: flex; gap: 20rpx; padding: 20rpx 32rpx;
|
||||
background: rgba(14,17,16,0.92);
|
||||
background: var(--bar-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1rpx solid var(--line);
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
@@ -28,6 +28,6 @@
|
||||
flex: 1; display: flex; align-items: center; justify-content: center;
|
||||
border-radius: 999rpx; padding: 26rpx; font-size: 30rpx; font-weight: 700;
|
||||
}
|
||||
.btn--gold { background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%); color: #0E1110; }
|
||||
.btn--gold { background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%); color: var(--accent-on); }
|
||||
.btn--ghost { background: var(--surface-2); color: var(--text); border: 1rpx solid var(--gold-line); }
|
||||
.btn--faved { color: var(--gold-2); border-color: var(--gold); }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"exercise-card": "/components/exercise-card/index",
|
||||
"gif-player": "/components/gif-player/gif-player"
|
||||
"gif-player": "/components/gif-player/gif-player",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<navbar title="{{exercise.displayName || exercise.name}}" show-back />
|
||||
|
||||
<view class="favbtn" style="top: {{favTop}}px; right: {{favRight}}px;" bindtap="onToggleFav">
|
||||
<text>{{favorited ? '♥' : '♡'}}</text>
|
||||
<fc-icon name="heart" size="44" color="{{favorited ? 'var(--accent)' : 'var(--text-3)'}}" />
|
||||
</view>
|
||||
|
||||
<view class="hero">
|
||||
@@ -13,7 +13,7 @@
|
||||
mode="aspectFill"
|
||||
lazy-load="true"
|
||||
binderror="onHeroError" />
|
||||
<view wx:else class="hero__ph">🏋️</view>
|
||||
<view wx:else class="hero__ph"><fc-icon name="dumbbell" size="72" color="var(--text-3)" /></view>
|
||||
<!-- 上层叠一个动图播放器:Canvas 逐帧播放 GIF(微信 image 不播 GIF) -->
|
||||
<gif-player wx:if="{{heroGif}}" class="hero__gif" src="{{heroGif}}" />
|
||||
</view>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
.addbar {
|
||||
position: fixed; left: 0; right: 0; bottom: 0;
|
||||
padding: 20rpx 32rpx;
|
||||
background: rgba(14,17,16,0.92);
|
||||
background: var(--bar-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1rpx solid var(--line);
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
@@ -48,7 +48,7 @@
|
||||
.cta {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 32rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 32rpx;
|
||||
border-radius: 999rpx; padding: 26rpx;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
.favbtn {
|
||||
position: fixed; right: 28rpx; z-index: 60;
|
||||
width: 72rpx; height: 72rpx; border-radius: 999rpx;
|
||||
background: rgba(14,17,16,0.55);
|
||||
background: var(--float-bg);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1rpx solid var(--gold-line);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"exercise-card": "/components/exercise-card/index"
|
||||
"exercise-card": "/components/exercise-card/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{!loading}}" class="empty-box">
|
||||
<view class="empty-box__emoji">🔍</view>
|
||||
<view class="empty-box__emoji"><fc-icon name="search" size="64" color="var(--text-3)" /></view>
|
||||
<view class="empty-box__t">还没有收藏,去发现喜欢的动作吧</view>
|
||||
<view class="empty-box__btn" bindtap="goRecommend">去推荐</view>
|
||||
</view>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
.item { position: relative; }
|
||||
.remove {
|
||||
position: absolute; right: 12rpx; top: 12rpx; z-index: 5;
|
||||
background: rgba(229,84,75,0.92); color: #fff;
|
||||
background: var(--danger); color: var(--accent-on);
|
||||
font-size: 22rpx; padding: 8rpx 18rpx; border-radius: 999rpx;
|
||||
}
|
||||
.empty { color: var(--text-3); font-size: 26rpx; text-align: center; padding: 120rpx 0; }
|
||||
@@ -29,5 +29,5 @@
|
||||
.empty-box__btn {
|
||||
margin-top: 40rpx; padding: 22rpx 64rpx; border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 30rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 30rpx;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
const app = getApp();
|
||||
const svc = require('../../services/exercise');
|
||||
const planSvc = require('../../services/plan');
|
||||
const favSvc = require('../../services/favorite');
|
||||
const { iconName } = require('../../utils/icon-name');
|
||||
|
||||
function greeting() {
|
||||
const h = new Date().getHours();
|
||||
if (h < 6) return '夜深了,';
|
||||
if (h < 11) return '早上好,';
|
||||
if (h < 13) return '中午好,';
|
||||
if (h < 18) return '下午好,';
|
||||
if (h < 22) return '晚上好,';
|
||||
return '夜深了,';
|
||||
}
|
||||
|
||||
function isLogin() {
|
||||
return !!wx.getStorageSync('access_token');
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -11,13 +28,19 @@ Page({
|
||||
collections: [],
|
||||
loading: true,
|
||||
quickEntries: [
|
||||
{ key: 'recommend', label: '智能推荐', emoji: '✨', url: '/pages/recommend/recommend' },
|
||||
{ key: 'bodyPart', label: '按部位', emoji: '💪', url: '/pages/category/category?dimension=bodyPart' },
|
||||
{ key: 'equipment', label: '按器械', emoji: '🏋️', url: '/pages/category/category?dimension=equipment' },
|
||||
{ key: 'official', label: '官方计划', emoji: '📋', url: '/pages/collection/collection' },
|
||||
{ key: 'myPlans', label: '我的计划', emoji: '🗂️', url: '/pages/my-plans/my-plans' },
|
||||
{ key: 'mine', label: '我的', emoji: '👤', url: '/pages/profile/profile' }
|
||||
]
|
||||
{ key: 'recommend', label: '智能推荐', icon: 'sparkles', url: '/pages/recommend/recommend' },
|
||||
{ key: 'bodyPart', label: '按部位', icon: 'accessibility', url: '/pages/category/category?dimension=bodyPart' },
|
||||
{ key: 'equipment', label: '按器械', icon: 'dumbbell', url: '/pages/category/category?dimension=equipment' },
|
||||
{ key: 'official', label: '官方计划', icon: 'clipboard-list', url: '/pages/collection/collection' },
|
||||
{ key: 'myPlans', label: '我的计划', icon: 'folder-open', url: '/pages/my-plans/my-plans' },
|
||||
{ key: 'mine', label: '我的', icon: 'user', url: '/pages/profile/profile' }
|
||||
],
|
||||
// —— 轻量个性化(登录后)——
|
||||
isLogin: false,
|
||||
greeting: '',
|
||||
myPlans: [],
|
||||
myFavorites: [],
|
||||
todayRecommend: []
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -25,6 +48,11 @@ Page({
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// tab 切回:已登录则静默刷新个性化(优先缓存)
|
||||
if (isLogin()) this.loadPersonal(true);
|
||||
},
|
||||
|
||||
loadData() {
|
||||
this.setData({ loading: true });
|
||||
Promise.all([
|
||||
@@ -32,19 +60,70 @@ Page({
|
||||
svc.listExercises({ pageSize: 8 }).catch(() => ({ items: [] })),
|
||||
svc.listCollections().catch(() => [])
|
||||
]).then(([stats, list, collections]) => {
|
||||
const cols = (collections || []).map((c) => ({
|
||||
...c,
|
||||
icon: iconName(c.emoji)
|
||||
}));
|
||||
this.setData({
|
||||
stats,
|
||||
bodyParts: (stats && stats.bodyParts) || [],
|
||||
equipment: (stats && stats.equipment) || [],
|
||||
featured: (list && list.items) || [],
|
||||
collections: collections || [],
|
||||
collections: cols,
|
||||
loading: false
|
||||
});
|
||||
if (isLogin()) this.loadPersonal(false);
|
||||
}).catch(() => {
|
||||
this.setData({ loading: false });
|
||||
});
|
||||
},
|
||||
|
||||
// 轻量个性化:复用现有 API,纯前端聚合;silent=切 tab 静默刷新
|
||||
loadPersonal(silent) {
|
||||
if (!isLogin()) return;
|
||||
this.setData({ isLogin: true, greeting: greeting() });
|
||||
if (!silent) this.setData({ personalLoading: true });
|
||||
|
||||
const CACHE_KEY = 'fc_personal_cache';
|
||||
const cached = wx.getStorageSync(CACHE_KEY);
|
||||
const now = Date.now();
|
||||
if (cached && now - (cached.ts || 0) < 5 * 60 * 1000 && !silent) {
|
||||
this.applyPersonal(cached.data);
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
planSvc.listMine().catch(() => []),
|
||||
favSvc.listExercises().catch(() => [])
|
||||
]).then(([plans, favs]) => {
|
||||
const planArr = Array.isArray(plans) ? plans : (plans && plans.items) || [];
|
||||
const favArr = Array.isArray(favs) ? favs : (favs && favs.items) || [];
|
||||
const data = {
|
||||
myPlans: planArr.slice(0, 3).map((p) => ({
|
||||
id: p.id, name: p.name, count: p.exerciseCount || 0
|
||||
})),
|
||||
myFavorites: favArr.slice(0, 6),
|
||||
todayRecommend: (this.data.featured || []).slice(0, 3).map((e) => ({
|
||||
id: e.id, name: e.displayName || e.name,
|
||||
reason: '为你精选 · 高效入门'
|
||||
}))
|
||||
};
|
||||
wx.setStorageSync(CACHE_KEY, { ts: now, data });
|
||||
this.applyPersonal(data);
|
||||
}).catch(() => {
|
||||
this.setData({ personalLoading: false });
|
||||
});
|
||||
},
|
||||
|
||||
applyPersonal(data) {
|
||||
this.setData({
|
||||
myPlans: data.myPlans,
|
||||
myFavorites: data.myFavorites,
|
||||
todayRecommend: data.todayRecommend,
|
||||
personalLoading: false
|
||||
});
|
||||
},
|
||||
|
||||
goRecommend() {
|
||||
wx.navigateTo({ url: '/pages/recommend/recommend' });
|
||||
},
|
||||
@@ -57,6 +136,17 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/search/search' });
|
||||
},
|
||||
|
||||
onPlanTap(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
if (id) wx.navigateTo({ url: '/pages/plan-detail/plan-detail?id=' + encodeURIComponent(id) });
|
||||
},
|
||||
|
||||
onExercise(e) {
|
||||
const id = (e.detail && e.detail.id) || (e.currentTarget && e.currentTarget.dataset.id);
|
||||
if (!id) return;
|
||||
wx.navigateTo({ url: '/pages/detail/detail?id=' + encodeURIComponent(id) });
|
||||
},
|
||||
|
||||
onScanBodyPart(e) {
|
||||
const { value, label } = e.currentTarget.dataset;
|
||||
wx.navigateTo({
|
||||
@@ -78,12 +168,5 @@ Page({
|
||||
url: '/pages/collection-detail/collection-detail?slug=' +
|
||||
encodeURIComponent(e.currentTarget.dataset.slug)
|
||||
});
|
||||
},
|
||||
|
||||
onExercise(e) {
|
||||
if (!e || !e.detail || !e.detail.id) return;
|
||||
wx.navigateTo({
|
||||
url: '/pages/detail/detail?id=' + encodeURIComponent(e.detail.id)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"usingComponents": {
|
||||
"exercise-card": "/components/exercise-card/index",
|
||||
"section-header": "/components/section-header/index",
|
||||
"bottom-nav": "/components/bottom-nav/index"
|
||||
"bottom-nav": "/components/bottom-nav/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,64 @@
|
||||
<view class="hero__sub">精准匹配你的目标肌群,让每一次训练都更高效</view>
|
||||
|
||||
<view class="search-pill" bindtap="onSearch">
|
||||
<text class="search-pill__icon">🔍</text>
|
||||
<fc-icon name="search" size="28" color="var(--text-2)" />
|
||||
<text class="search-pill__ph">搜索动作 / 器械 / 部位</text>
|
||||
</view>
|
||||
|
||||
<view class="cta" bindtap="goRecommend">
|
||||
<text class="cta__icon">✨</text>
|
||||
<fc-icon name="sparkles" size="24" />
|
||||
<text class="cta__txt">智能推荐</text>
|
||||
<text class="cta__arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 为你准备(登录后个性化) -->
|
||||
<view class="section personal" wx:if="{{isLogin}}">
|
||||
<view class="personal__head">
|
||||
<view class="personal__titlewrap">
|
||||
<view class="eyebrow">FOR YOU</view>
|
||||
<view class="personal__hi">{{greeting}}准备好训练了吗?</view>
|
||||
</view>
|
||||
<fc-icon name="sparkles" size="22" color="var(--accent)" />
|
||||
</view>
|
||||
|
||||
<view class="personal__block" wx:if="{{myPlans.length}}">
|
||||
<view class="personal__sub">我的计划</view>
|
||||
<view class="plan-row">
|
||||
<view class="plan-chip" wx:for="{{myPlans}}" wx:key="id" data-id="{{item.id}}" bindtap="onExercise">
|
||||
<fc-icon name="clipboard-list" size="20" color="var(--accent)" />
|
||||
<view class="plan-chip__body">
|
||||
<text class="plan-chip__name">{{item.name}}</text>
|
||||
<text class="plan-chip__meta">{{item.count}} 个动作</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="personal__block" wx:if="{{todayRecommend.length}}">
|
||||
<view class="personal__sub">今日推荐</view>
|
||||
<view class="rec-row">
|
||||
<view class="rec-item" wx:for="{{todayRecommend}}" wx:key="id" data-id="{{item.id}}" catch:tap="onExercise">
|
||||
<view class="rec-item__body">
|
||||
<text class="rec-item__name">{{item.name}}</text>
|
||||
<text class="rec-item__reason">{{item.reason}}</text>
|
||||
</view>
|
||||
<fc-icon name="chevron-right" size="18" color="var(--text-3)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="personal__block" wx:if="{{myFavorites.length}}">
|
||||
<view class="personal__sub">我的收藏</view>
|
||||
<scroll-view scroll-x class="hscroll fav-scroll">
|
||||
<view class="fav-card" wx:for="{{myFavorites}}" wx:key="id" data-id="{{item.id}}" catch:tap="onExercise">
|
||||
<image class="fav-card__img" src="{{item.image}}" mode="aspectFill" lazy-load />
|
||||
<text class="fav-card__name">{{item.displayName || item.name}}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷入口 2x3 -->
|
||||
<view class="section quick">
|
||||
<view class="quick__grid">
|
||||
@@ -26,7 +73,7 @@
|
||||
class="quick__tile"
|
||||
data-url="{{item.url}}"
|
||||
bindtap="onQuick">
|
||||
<text class="quick__emoji">{{item.emoji}}</text>
|
||||
<fc-icon name="{{item.icon}}" size="28" color="var(--text)" />
|
||||
<text class="quick__label">{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -54,7 +101,7 @@
|
||||
style="background: {{item.color}}22; border-color: {{item.color}}55;"
|
||||
data-slug="{{item.slug}}"
|
||||
bindtap="onCollection">
|
||||
<text class="prog__emoji">{{item.emoji}}</text>
|
||||
<fc-icon name="{{item.icon}}" size="36" color="{{item.color}}" />
|
||||
<text class="prog__name" style="color: {{item.color}};">{{item.name}}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
@@ -2,59 +2,92 @@
|
||||
|
||||
.hero {
|
||||
padding: 20rpx 40rpx 48rpx;
|
||||
background: linear-gradient(150deg, #1C2A22 0%, #0E1110 70%);
|
||||
background: linear-gradient(160deg, var(--surface) 0%, var(--bg) 70%);
|
||||
border-bottom-left-radius: 48rpx;
|
||||
border-bottom-right-radius: 48rpx;
|
||||
}
|
||||
.hero__eyebrow { color: var(--gold); font-size: 22rpx; letter-spacing: 4rpx; }
|
||||
.hero__title { font-size: 56rpx; font-weight: 700; letter-spacing: 2rpx; margin-top: 14rpx; color: var(--text); }
|
||||
.hero__sub { font-size: 26rpx; color: var(--text-2); margin-top: 14rpx; }
|
||||
.hero__eyebrow { color: var(--accent); font-size: var(--fs-xs); letter-spacing: var(--ls-eyebrow); font-weight: var(--fw-semibold); }
|
||||
.hero__title { font-size: var(--fs-3xl); font-weight: 700; letter-spacing: var(--ls-title); margin-top: 14rpx; color: var(--text); }
|
||||
.hero__sub { font-size: var(--fs-sm); color: var(--text-2); margin-top: 14rpx; }
|
||||
|
||||
.search-pill {
|
||||
margin-top: 36rpx;
|
||||
display: flex; align-items: center;
|
||||
background: var(--surface); border: 1rpx solid var(--line);
|
||||
border-radius: 999rpx; padding: 22rpx 30rpx;
|
||||
border-radius: var(--radius-pill); padding: 22rpx 30rpx;
|
||||
}
|
||||
.search-pill__icon { font-size: 30rpx; margin-right: 16rpx; }
|
||||
.search-pill__ph { color: var(--text-3); font-size: 28rpx; }
|
||||
.search-pill__ph { color: var(--text-3); font-size: var(--fs-base); }
|
||||
|
||||
.cta {
|
||||
margin-top: 24rpx;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 32rpx;
|
||||
border-radius: 999rpx; padding: 28rpx;
|
||||
box-shadow: 0 12rpx 30rpx rgba(217,179,108,0.28);
|
||||
color: var(--accent-on); font-weight: 700; font-size: var(--fs-lg);
|
||||
border-radius: var(--radius-pill); padding: 28rpx;
|
||||
box-shadow: var(--shadow-glow);
|
||||
}
|
||||
.cta__icon { margin-right: 12rpx; }
|
||||
.cta__arrow { margin-left: 12rpx; font-size: 36rpx; }
|
||||
|
||||
.quick__grid { display: flex; flex-wrap: wrap; gap: 18rpx; }
|
||||
.quick__grid { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.quick__tile {
|
||||
width: calc((100% - 36rpx) / 3);
|
||||
width: calc((100% - 32rpx) / 3);
|
||||
background: var(--surface); border: 1rpx solid var(--line);
|
||||
border-radius: var(--radius); padding: 28rpx 18rpx;
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
position: relative; overflow: hidden;
|
||||
transition: background var(--motion-fast) var(--ease);
|
||||
}
|
||||
.quick__tile:active { background: var(--surface-2); }
|
||||
.quick__tile::after {
|
||||
content: ''; position: absolute; inset: 0;
|
||||
background: radial-gradient(circle at 50% 0%, var(--gold-soft), transparent 70%);
|
||||
}
|
||||
.quick__emoji { font-size: 48rpx; position: relative; z-index: 1; }
|
||||
.quick__label { font-size: 26rpx; margin-top: 12rpx; color: var(--text); position: relative; z-index: 1; }
|
||||
|
||||
/* ===== 为你准备(个性化) ===== */
|
||||
.personal__head { display: flex; align-items: center; justify-content: space-between; }
|
||||
.personal__hi { font-size: var(--fs-xl); font-weight: 600; color: var(--text); margin-top: 6rpx; }
|
||||
.personal__sub { font-size: var(--fs-sm); color: var(--text-2); font-weight: 600; margin: 28rpx 0 16rpx; }
|
||||
|
||||
.plan-row { display: flex; flex-direction: column; gap: 12rpx; }
|
||||
.plan-chip {
|
||||
display: flex; align-items: center; gap: 16rpx;
|
||||
background: var(--surface); border: 1rpx solid var(--line);
|
||||
border-radius: var(--radius); padding: 20rpx 24rpx;
|
||||
}
|
||||
.plan-chip:active { background: var(--surface-2); }
|
||||
.plan-chip__body { display: flex; flex-direction: column; }
|
||||
.plan-chip__name { font-size: var(--fs-base); color: var(--text); font-weight: 600; }
|
||||
.plan-chip__meta { font-size: 22rpx; color: var(--text-3); margin-top: 4rpx; }
|
||||
|
||||
.rec-row { display: flex; flex-direction: column; gap: 12rpx; }
|
||||
.rec-item {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
background: var(--surface); border: 1rpx solid var(--line);
|
||||
border-radius: var(--radius); padding: 20rpx 24rpx;
|
||||
}
|
||||
.rec-item:active { background: var(--surface-2); }
|
||||
.rec-item__body { display: flex; flex-direction: column; }
|
||||
.rec-item__name { font-size: var(--fs-base); color: var(--text); font-weight: 600; }
|
||||
.rec-item__reason { font-size: 22rpx; color: var(--accent); margin-top: 4rpx; }
|
||||
|
||||
.fav-scroll { white-space: nowrap; margin-top: 4rpx; }
|
||||
.fav-card { display: inline-flex; flex-direction: column; width: 200rpx; margin-right: 16rpx; vertical-align: top; }
|
||||
.fav-card__img {
|
||||
width: 200rpx; height: 200rpx; border-radius: var(--radius);
|
||||
background: var(--surface-2); border: 1rpx solid var(--line);
|
||||
}
|
||||
.fav-card__name { font-size: 24rpx; color: var(--text-2); margin-top: 10rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
|
||||
.hscroll { white-space: nowrap; margin-top: 8rpx; }
|
||||
.hscroll__item { display: inline-block; width: 300rpx; margin-right: 20rpx; vertical-align: top; }
|
||||
|
||||
.chip-scroll { white-space: nowrap; margin-top: 8rpx; }
|
||||
.prog {
|
||||
display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
min-width: 160rpx; padding: 24rpx 28rpx; margin-right: 18rpx;
|
||||
min-width: 160rpx; padding: 24rpx 28rpx; margin-right: 16rpx;
|
||||
border-radius: var(--radius-sm); border: 1rpx solid;
|
||||
}
|
||||
.prog__emoji { font-size: 44rpx; }
|
||||
.prog__name { font-size: 26rpx; font-weight: 700; margin-top: 10rpx; }
|
||||
|
||||
.bp-grid { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
@@ -70,9 +103,9 @@
|
||||
.eq {
|
||||
display: inline-flex; align-items: center; gap: 12rpx;
|
||||
background: var(--surface-2); border: 1rpx solid var(--line);
|
||||
border-radius: 999rpx; padding: 16rpx 28rpx; margin-right: 16rpx;
|
||||
border-radius: var(--radius-pill); padding: 16rpx 28rpx; margin-right: 16rpx;
|
||||
}
|
||||
.eq__name { font-size: 26rpx; color: var(--text); }
|
||||
.eq__count { font-size: 22rpx; color: var(--gold); }
|
||||
.eq__count { font-size: 22rpx; color: var(--accent); }
|
||||
|
||||
.empty { color: var(--text-3); font-size: 26rpx; text-align: center; padding: 40rpx 0; }
|
||||
.empty { color: var(--text-3); font-size: var(--fs-sm); text-align: center; padding: 40rpx 0; }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"plan-card": "/components/plan-card/index",
|
||||
"bottom-nav": "/components/bottom-nav/index"
|
||||
"bottom-nav": "/components/bottom-nav/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</block>
|
||||
|
||||
<view wx:elif="{{!loading}}" class="empty-box">
|
||||
<view class="empty-box__emoji">📋</view>
|
||||
<view class="empty-box__emoji"><fc-icon name="clipboard-list" size="64" color="var(--text-3)" /></view>
|
||||
<view class="empty-box__t">还没有计划,创建一个开始训练吧</view>
|
||||
<view class="empty-box__btn" bindtap="onNew">+ 新建计划</view>
|
||||
<view class="empty-box__link" bindtap="goOfficial">或浏览官方训练计划 ›</view>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.newbar__btn {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 32rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 32rpx;
|
||||
border-radius: 999rpx; padding: 26rpx;
|
||||
}
|
||||
.grid { display: flex; flex-wrap: wrap; gap: 18rpx; }
|
||||
@@ -20,6 +20,6 @@
|
||||
.empty-box__btn {
|
||||
margin-top: 44rpx; padding: 24rpx 88rpx; border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 30rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 30rpx;
|
||||
}
|
||||
.empty-box__link { margin-top: 28rpx; font-size: 26rpx; color: var(--gold); }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const app = getApp();
|
||||
const planSvc = require('../../services/plan');
|
||||
const { iconName } = require('../../utils/icon-name');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -24,7 +25,7 @@ Page({
|
||||
? raw.slice(0, 10)
|
||||
: (raw ? String(raw).slice(0, 10) : '');
|
||||
this.setData({
|
||||
plan: { ...plan, updatedAtText },
|
||||
plan: { ...plan, updatedAtText, icon: iconName(plan.emoji) },
|
||||
exercises: (plan && plan.exercises) || [],
|
||||
loading: false
|
||||
});
|
||||
@@ -61,7 +62,7 @@ Page({
|
||||
wx.showModal({
|
||||
title: '删除计划',
|
||||
content: '确定要删除该训练计划吗?此操作不可恢复。',
|
||||
confirmColor: '#E5544B',
|
||||
confirmColor: '#FF5C5C',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
planSvc.remove(this.data.id).then(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"exercise-card": "/components/exercise-card/index",
|
||||
"section-header": "/components/section-header/index"
|
||||
"section-header": "/components/section-header/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<navbar title="{{plan.name}}" show-back />
|
||||
|
||||
<view class="header" style="background: linear-gradient(135deg, {{plan.color}}26, var(--surface));">
|
||||
<view class="header__emoji" style="background: {{plan.color}}22; color: {{plan.color}};">{{plan.emoji}}</view>
|
||||
<view class="header__emoji" style="background: {{plan.color}}22; color: {{plan.color}};"><fc-icon name="{{plan.icon}}" size="48" color="{{plan.color}}" /></view>
|
||||
<view class="header__name" style="color: {{plan.color}};">{{plan.name}}</view>
|
||||
<view wx:if="{{plan.description}}" class="header__desc">{{plan.description}}</view>
|
||||
<view class="header__meta">共 {{exercises.length}} 个动作<text wx:if="{{plan.updatedAtText}}"> · 更新于 {{plan.updatedAtText}}</text></view>
|
||||
@@ -20,7 +20,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view wx:elif="{{!loading}}" class="empty-box">
|
||||
<view class="empty-box__emoji">🏋️</view>
|
||||
<view class="empty-box__emoji"><fc-icon name="dumbbell" size="64" color="var(--text-3)" /></view>
|
||||
<view class="empty-box__t">这个计划还没有动作,去添加吧</view>
|
||||
<view class="empty-box__btn" bindtap="onAdd">+ 添加动作</view>
|
||||
</view>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
.list__card { width: 100%; }
|
||||
.list__remove {
|
||||
position: absolute; right: 12rpx; top: 12rpx; z-index: 5;
|
||||
background: rgba(229,84,75,0.92); color: #fff;
|
||||
background: var(--danger); color: var(--accent-on);
|
||||
font-size: 22rpx; padding: 8rpx 18rpx; border-radius: 999rpx;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
.empty-box__btn {
|
||||
margin-top: 40rpx; padding: 22rpx 64rpx; border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 30rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 30rpx;
|
||||
}
|
||||
|
||||
.actionbar {
|
||||
position: fixed; left: 0; right: 0; bottom: 0;
|
||||
display: flex; gap: 16rpx; padding: 20rpx 24rpx;
|
||||
background: rgba(14,17,16,0.92);
|
||||
background: var(--bar-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1rpx solid var(--line);
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
@@ -45,6 +45,6 @@
|
||||
flex: 1; display: flex; align-items: center; justify-content: center;
|
||||
border-radius: 999rpx; padding: 24rpx 10rpx; font-size: 28rpx; font-weight: 700;
|
||||
}
|
||||
.btn--gold { background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%); color: #0E1110; }
|
||||
.btn--gold { background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%); color: var(--accent-on); }
|
||||
.btn--ghost { background: var(--surface-2); color: var(--text); border: 1rpx solid var(--line); flex: 0 0 auto; width: 180rpx; }
|
||||
.btn--del { background: rgba(229,84,75,0.16); color: #E5544B; border: 1rpx solid rgba(229,84,75,0.4); flex: 0 0 auto; width: 180rpx; }
|
||||
.btn--del { background: var(--danger-soft); color: var(--danger); border: 1rpx solid var(--danger-soft); flex: 0 0 auto; width: 180rpx; }
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
const app = getApp();
|
||||
const planSvc = require('../../services/plan');
|
||||
|
||||
const EMOJIS = ['📋', '💪', '🔥', '🏋️', '🏃', '🧘', '🤸', '🥊', '🚴', '⚡', '🌟', '🎯'];
|
||||
const COLORS = ['#D9B36C', '#58C9B9', '#9B8CFF', '#F08A5D', '#3FBF7F', '#5B9DF9'];
|
||||
// UGC 计划图标选择器:Lucide 运动子集(非 emoji)
|
||||
const ICONS = ['clipboard-list', 'dumbbell', 'flame', 'accessibility', 'person-standing',
|
||||
'bike', 'heart', 'target', 'trophy', 'medal', 'star', 'activity',
|
||||
'sparkles', 'zap', 'trending-up', 'folder-open'];
|
||||
// 计划主题色:对齐 Kinetic Dark 语义色板
|
||||
const COLORS = ['#B6F24E', '#2BD9A6', '#4FA8FF', '#F2B53D', '#FF5C5C', '#9BA7A1'];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -11,9 +15,9 @@ Page({
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
emoji: '📋',
|
||||
color: '#D9B36C',
|
||||
emojis: EMOJIS,
|
||||
emoji: 'clipboard-list',
|
||||
color: '#B6F24E',
|
||||
emojis: ICONS,
|
||||
colors: COLORS,
|
||||
loading: false,
|
||||
dirty: false // 是否有未保存的修改
|
||||
@@ -29,8 +33,8 @@ Page({
|
||||
this.setData({
|
||||
name: p.name || '',
|
||||
description: p.description || '',
|
||||
emoji: p.emoji || '📋',
|
||||
color: p.color || '#D9B36C',
|
||||
emoji: p.emoji || 'clipboard-list',
|
||||
color: p.color || '#B6F24E',
|
||||
loading: false
|
||||
});
|
||||
}).catch(() => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index"
|
||||
"navbar": "/components/navbar/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<view class="section">
|
||||
<view class="preview" style="background: linear-gradient(135deg, {{color}}26, var(--surface));">
|
||||
<view class="preview__emoji" style="background: {{color}}22; color: {{color}};">{{emoji}}</view>
|
||||
<view class="preview__emoji" style="background: {{color}}22; color: {{color}};"><fc-icon name="{{emoji}}" size="48" color="{{color}}" /></view>
|
||||
<view class="preview__name" style="color: {{color}};">{{name || '计划名称'}}</view>
|
||||
<view class="preview__desc">{{description || '计划描述(选填)'}}</view>
|
||||
</view>
|
||||
@@ -40,7 +40,9 @@
|
||||
class="emoji {{emoji === item ? 'emoji--active' : ''}}"
|
||||
style="{{emoji === item ? 'border-color:' + color + '; background:' + color + '22;' : ''}}"
|
||||
data-emoji="{{item}}"
|
||||
bindtap="onPickEmoji">{{item}}</view>
|
||||
bindtap="onPickEmoji">
|
||||
<fc-icon name="{{item}}" size="44" color="{{emoji === item ? color : 'var(--text)'}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
.savebar {
|
||||
position: fixed; left: 0; right: 0; bottom: 0;
|
||||
padding: 20rpx 32rpx;
|
||||
background: rgba(14,17,16,0.92);
|
||||
background: var(--bar-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1rpx solid var(--line);
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
@@ -49,6 +49,6 @@
|
||||
.savebar__btn {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 32rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 32rpx;
|
||||
border-radius: 999rpx; padding: 26rpx;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"plan-card": "/components/plan-card/index"
|
||||
"plan-card": "/components/plan-card/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</block>
|
||||
|
||||
<view wx:elif="{{!loading}}" class="empty-box">
|
||||
<view class="empty-box__emoji">⭐</view>
|
||||
<view class="empty-box__emoji"><fc-icon name="star" size="64" color="var(--text-3)" /></view>
|
||||
<view class="empty-box__t">还没有收藏官方计划</view>
|
||||
<view class="empty-box__btn" bindtap="goOfficial">浏览官方计划</view>
|
||||
</view>
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
.empty-box__btn {
|
||||
margin-top: 40rpx; padding: 22rpx 64rpx; border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 30rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 30rpx;
|
||||
}
|
||||
|
||||
@@ -3,20 +3,22 @@ const auth = require('../../utils/auth');
|
||||
const favSvc = require('../../services/favorite');
|
||||
const planSvc = require('../../services/plan');
|
||||
|
||||
const EMOJIS = ['💪', '🏋️', '🔥', '🏃', '🧘', '🤸', '🥊', '🚴', '⚡', '🌟', '🐯', '🦁'];
|
||||
// UGC 头像图标选择器:Lucide 运动子集(非 emoji)
|
||||
const ICONS = ['dumbbell', 'accessibility', 'person-standing', 'bike', 'heart', 'flame',
|
||||
'target', 'medal', 'trophy', 'award', 'star', 'activity', 'sparkles', 'zap', 'trending-up'];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 20,
|
||||
loggedIn: false,
|
||||
profile: { nickname: '', avatar: '💪' },
|
||||
profile: { nickname: '', avatar: 'user' },
|
||||
counts: { fav: 0, plans: 0, favPlans: 0 },
|
||||
loading: false,
|
||||
// 编辑态
|
||||
showEmojiPicker: false,
|
||||
emojis: EMOJIS,
|
||||
emojis: ICONS,
|
||||
draftNickname: '',
|
||||
draftAvatar: '💪'
|
||||
draftAvatar: 'user'
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -70,7 +72,7 @@ Page({
|
||||
if (res.confirm) {
|
||||
this.setData({
|
||||
draftNickname: (res.content || '').trim() || cur.nickname,
|
||||
draftAvatar: cur.avatar || '💪',
|
||||
draftAvatar: cur.avatar || 'user',
|
||||
showEmojiPicker: true
|
||||
});
|
||||
}
|
||||
@@ -112,7 +114,7 @@ Page({
|
||||
auth.logout();
|
||||
this.setData({
|
||||
loggedIn: false,
|
||||
profile: { nickname: '', avatar: '💪' },
|
||||
profile: { nickname: '', avatar: 'user' },
|
||||
counts: { fav: 0, plans: 0, favPlans: 0 }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"avatar": "/components/avatar/index",
|
||||
"bottom-nav": "/components/bottom-nav/index"
|
||||
"bottom-nav": "/components/bottom-nav/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<!-- 未登录 -->
|
||||
<view wx:if="{{!loggedIn}}" class="login">
|
||||
<view class="login__logo">💪</view>
|
||||
<view class="login__logo"><fc-icon name="dumbbell" size="64" color="var(--accent)" /></view>
|
||||
<view class="login__title">FITCOACH 智能健身教练</view>
|
||||
<view class="login__sub">登录后同步你的收藏与训练计划</view>
|
||||
<button class="login__btn" bindtap="onLogin">微信登录</button>
|
||||
@@ -38,22 +38,22 @@
|
||||
|
||||
<view class="menu card">
|
||||
<view class="cell" bindtap="goFav">
|
||||
<text class="cell__icon">❤️</text>
|
||||
<fc-icon class="cell__icon" name="heart" size="24" color="var(--text-2)" />
|
||||
<text class="cell__label">我的收藏动作</text>
|
||||
<text class="cell__arrow">›</text>
|
||||
</view>
|
||||
<view class="cell" bindtap="goPlans">
|
||||
<text class="cell__icon">🗂️</text>
|
||||
<fc-icon class="cell__icon" name="folder-open" size="24" color="var(--text-2)" />
|
||||
<text class="cell__label">我的训练计划</text>
|
||||
<text class="cell__arrow">›</text>
|
||||
</view>
|
||||
<view class="cell" bindtap="goFavPlans">
|
||||
<text class="cell__icon">⭐</text>
|
||||
<fc-icon class="cell__icon" name="star" size="24" color="var(--text-2)" />
|
||||
<text class="cell__label">收藏的官方计划</text>
|
||||
<text class="cell__arrow">›</text>
|
||||
</view>
|
||||
<view class="cell" bindtap="goOfficial">
|
||||
<text class="cell__icon">📋</text>
|
||||
<fc-icon class="cell__icon" name="clipboard-list" size="24" color="var(--text-2)" />
|
||||
<text class="cell__label">官方训练计划</text>
|
||||
<text class="cell__arrow">›</text>
|
||||
</view>
|
||||
@@ -72,7 +72,9 @@
|
||||
wx:key="*this"
|
||||
class="emoji {{draftAvatar === item ? 'emoji--active' : ''}}"
|
||||
data-emoji="{{item}}"
|
||||
bindtap="onPickEmoji">{{item}}</view>
|
||||
bindtap="onPickEmoji">
|
||||
<fc-icon name="{{item}}" size="48" color="var(--text)" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="sheet__nick">{{draftNickname || '未命名'}}</view>
|
||||
<view class="sheet__actions">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
.login__btn {
|
||||
margin-top: 56rpx; width: 420rpx;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 32rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 32rpx;
|
||||
border-radius: 999rpx; padding: 24rpx;
|
||||
}
|
||||
.login__btn::after { border: none; }
|
||||
@@ -24,7 +24,7 @@
|
||||
/* 个人中心 */
|
||||
.center { padding: 32rpx; }
|
||||
.profile {
|
||||
background: linear-gradient(135deg, rgba(217,179,108,0.14), var(--surface));
|
||||
background: linear-gradient(135deg, var(--accent-soft), var(--surface));
|
||||
border: 1rpx solid var(--gold-line);
|
||||
border-radius: var(--radius); padding: 36rpx;
|
||||
}
|
||||
@@ -57,7 +57,7 @@
|
||||
/* 头像选择弹层 */
|
||||
.sheet {
|
||||
position: fixed; inset: 0; z-index: 80;
|
||||
background: rgba(0,0,0,0.5);
|
||||
background: var(--scrim);
|
||||
display: flex; align-items: flex-end;
|
||||
}
|
||||
.sheet__panel {
|
||||
@@ -81,4 +81,4 @@
|
||||
font-size: 30rpx; font-weight: 700;
|
||||
}
|
||||
.sheet__btn--ghost { background: var(--surface-2); color: var(--text); border: 1rpx solid var(--line); }
|
||||
.sheet__btn--gold { background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%); color: #0E1110; }
|
||||
.sheet__btn--gold { background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%); color: var(--accent-on); }
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
.confirm {
|
||||
position: fixed; left: 0; right: 0; bottom: 0;
|
||||
padding: 20rpx 32rpx;
|
||||
background: rgba(14,17,16,0.92);
|
||||
background: var(--bar-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1rpx solid var(--line);
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
@@ -34,7 +34,7 @@
|
||||
.cta {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-2) 100%);
|
||||
color: #0E1110; font-weight: 700; font-size: 32rpx;
|
||||
color: var(--accent-on); font-weight: 700; font-size: 32rpx;
|
||||
border-radius: 999rpx; padding: 26rpx;
|
||||
}
|
||||
.cta__arrow { margin-left: 12rpx; font-size: 36rpx; }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"navbar": "/components/navbar/index",
|
||||
"exercise-card": "/components/exercise-card/index"
|
||||
"exercise-card": "/components/exercise-card/index",
|
||||
"fc-icon": "/components/fc-icon/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<navbar title="{{navTitle}}" show-back="{{selectMode}}" />
|
||||
<view class="searchbar">
|
||||
<view class="searchbar__box">
|
||||
<text class="searchbar__icon">🔍</text>
|
||||
<fc-icon class="searchbar__icon" name="search" size="28" color="var(--text-2)" />
|
||||
<input
|
||||
class="searchbar__input"
|
||||
placeholder="{{selectMode ? '选择动作加入计划' : '搜索动作 / 器械 / 部位'}}"
|
||||
|
||||
53
miniprogram/utils/icon-map.js
Normal file
53
miniprogram/utils/icon-map.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// AUTO-GENERATED by /tmp/gen_iconfont.mjs — 勿手改
|
||||
// Lucide (MIT) 子集 iconfont,font-family: fc-icon
|
||||
module.exports = {
|
||||
"zap": "\\uf101",
|
||||
"x-circle": "\\uf102",
|
||||
"user": "\\uf103",
|
||||
"trophy": "\\uf104",
|
||||
"trending-up": "\\uf105",
|
||||
"trash-2": "\\uf106",
|
||||
"target": "\\uf107",
|
||||
"star": "\\uf108",
|
||||
"sparkles": "\\uf109",
|
||||
"sliders-horizontal": "\\uf10a",
|
||||
"share-2": "\\uf10b",
|
||||
"settings": "\\uf10c",
|
||||
"search": "\\uf10d",
|
||||
"rotate-cw": "\\uf10e",
|
||||
"refresh-cw": "\\uf10f",
|
||||
"plus": "\\uf110",
|
||||
"play": "\\uf111",
|
||||
"person-standing": "\\uf112",
|
||||
"medal": "\\uf113",
|
||||
"log-out": "\\uf114",
|
||||
"layout-grid": "\\uf115",
|
||||
"layers": "\\uf116",
|
||||
"info": "\\uf117",
|
||||
"image": "\\uf118",
|
||||
"home": "\\uf119",
|
||||
"heart": "\\uf11a",
|
||||
"grid-2x2": "\\uf11b",
|
||||
"folder-open": "\\uf11c",
|
||||
"flame": "\\uf11d",
|
||||
"filter": "\\uf11e",
|
||||
"dumbbell": "\\uf11f",
|
||||
"download": "\\uf120",
|
||||
"clock": "\\uf121",
|
||||
"clipboard-list": "\\uf122",
|
||||
"circle-x": "\\uf123",
|
||||
"chevron-right": "\\uf124",
|
||||
"chevron-down": "\\uf125",
|
||||
"check": "\\uf126",
|
||||
"check-circle": "\\uf127",
|
||||
"calendar": "\\uf128",
|
||||
"bookmark": "\\uf129",
|
||||
"bike": "\\uf12a",
|
||||
"bell": "\\uf12b",
|
||||
"award": "\\uf12c",
|
||||
"arrow-right": "\\uf12d",
|
||||
"arrow-left": "\\uf12e",
|
||||
"alert-circle": "\\uf12f",
|
||||
"activity": "\\uf130",
|
||||
"accessibility": "\\uf131"
|
||||
};
|
||||
19
miniprogram/utils/icon-name.js
Normal file
19
miniprogram/utils/icon-name.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// emoji → Lucide 图标名 映射(后端历史数据里仍可能带 emoji 的字段,如 plan.emoji / collection.emoji)
|
||||
const EMOJI_TO_ICON = {
|
||||
'✨': 'sparkles', '💪': 'accessibility', '🏋️': 'dumbbell', '📋': 'clipboard-list',
|
||||
'🗂️': 'folder-open', '👤': 'user', '🏃': 'person-standing', '🔥': 'flame',
|
||||
'❤️': 'heart', '⭐': 'star', '🎯': 'target', '🧘': 'activity', '🥗': 'activity',
|
||||
'🏠': 'home', '📚': 'layout-grid', '📝': 'clipboard-list', '💡': 'sparkles',
|
||||
'🚀': 'trending-up', '⏱️': 'clock', '🏆': 'trophy', '🥇': 'medal',
|
||||
// 官方训练组合 seed emoji(后端 collections.ts)→ 全部映射到现有字形
|
||||
'⚡': 'zap', '🌟': 'star', '🌱': 'trending-up', '🌸': 'sparkles',
|
||||
'📈': 'trending-up', '🦵': 'dumbbell', '🦾': 'dumbbell', '🪢': 'activity'
|
||||
};
|
||||
|
||||
// 旧 emoji → lucide 名;已是 lucide 名则直接透传;未知返回 fallback
|
||||
function iconName(v, fallback) {
|
||||
if (!v) return fallback || 'folder-open';
|
||||
return EMOJI_TO_ICON[v] || v;
|
||||
}
|
||||
|
||||
module.exports = { EMOJI_TO_ICON, iconName };
|
||||
Reference in New Issue
Block a user