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:
2026-07-22 21:38:56 +08:00
parent ca0af6f7d2
commit b94eb706ff
64 changed files with 1572 additions and 173 deletions

View File

@@ -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)
});
}
});