- 建立 Lucide 子集 iconfont(49 字形),全站 emoji 替换为 fc-icon 组件 - 重塑 Design Token(冷中性近黑底 + Volt 荧光绿 #B6F24E),消除硬编码颜色 - 首页登录后个性化:我的计划 / 我的收藏 / 今日推荐(纯前端复用现有 API) - 后端 DEFAULT_EMOJI→Lucide 名,PALETTE 对齐 Kinetic Dark 语义色板 - 修复 person-standing 缺失字形;14 个官方组合 seed emoji 全部映射到现有字形
173 lines
5.3 KiB
JavaScript
173 lines
5.3 KiB
JavaScript
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: {
|
||
statusBarHeight: 20,
|
||
stats: null,
|
||
bodyParts: [],
|
||
equipment: [],
|
||
featured: [],
|
||
collections: [],
|
||
loading: true,
|
||
quickEntries: [
|
||
{ 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() {
|
||
this.setData({ statusBarHeight: app.globalData.statusBarHeight || 20 });
|
||
this.loadData();
|
||
},
|
||
|
||
onShow() {
|
||
// tab 切回:已登录则静默刷新个性化(优先缓存)
|
||
if (isLogin()) this.loadPersonal(true);
|
||
},
|
||
|
||
loadData() {
|
||
this.setData({ loading: true });
|
||
Promise.all([
|
||
svc.getStats().catch(() => null),
|
||
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: 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' });
|
||
},
|
||
|
||
onQuick(e) {
|
||
wx.navigateTo({ url: e.currentTarget.dataset.url });
|
||
},
|
||
|
||
onSearch() {
|
||
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({
|
||
url: '/pages/category/category?dimension=bodyPart&value=' +
|
||
encodeURIComponent(value) + '&title=' + encodeURIComponent(label)
|
||
});
|
||
},
|
||
|
||
onScanEquipment(e) {
|
||
const { value, label } = e.currentTarget.dataset;
|
||
wx.navigateTo({
|
||
url: '/pages/category/category?dimension=equipment&value=' +
|
||
encodeURIComponent(value) + '&title=' + encodeURIComponent(label)
|
||
});
|
||
},
|
||
|
||
onCollection(e) {
|
||
wx.navigateTo({
|
||
url: '/pages/collection-detail/collection-detail?slug=' +
|
||
encodeURIComponent(e.currentTarget.dataset.slug)
|
||
});
|
||
}
|
||
});
|