Files
fitness-coach/miniprogram/pages/detail/detail.js
wm a0894837bd ux: 动作收藏按钮从顶部浮窗移到底部,与加入计划并排 + 图标美化
- detail 顶部 favbtn 浮窗(需避让胶囊、丑)移除,改为底部 addbar 内与 CTA 并排的方形按钮
- fc-icon 新增 filled 属性:收藏态实心心形(Lucide 描边图标默认 fill=none,filled=true 时 fill=stroke 变实心)
- 收藏按钮美化:圆角 24rpx 方形 + 发丝边框,未收藏描边(text-2)、已收藏实心(accent)+ accent-soft 底色 + accent-ring 边框 + 点按 scale 反馈
- detail.js 清理 favRight/favTop 胶囊避让逻辑(不再需要)
2026-07-22 23:25:17 +08:00

149 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const app = getApp();
const svc = require('../../services/exercise');
const favSvc = require('../../services/favorite');
const planSvc = require('../../services/plan');
const auth = require('../../utils/auth');
Page({
data: {
statusBarHeight: 20,
id: '',
planId: '',
exercise: null,
heroSrc: '',
heroGif: '',
heroError: false,
related: [],
loading: true,
favorited: false
},
onLoad(query) {
const id = query.id || '';
const planId = query.planId || '';
this.setData({ statusBarHeight: app.globalData.statusBarHeight || 20, id, planId });
// 防御id 缺失直接显示空态,避免请求 /api/exercises/ 导致错乱或 404
if (!id) {
this.setData({ loading: false });
return;
}
this.load();
if (auth.isLogin()) {
favSvc.exerciseStatus(id).then((r) => {
this.setData({ favorited: !!(r && r.favorited) });
}).catch(() => {});
}
},
load() {
this.setData({ loading: true });
svc.getExercise(this.data.id).then((ex) => {
// 海报用超分 JPG(720px 清晰);动图用原始 GIF由 gif-player 组件逐帧播放
const heroSrc = ex && (ex.image || ex.gifUrl) ? (ex.image || ex.gifUrl) : '';
const heroGif = ex && ex.gifUrl ? ex.gifUrl : '';
this.setData({ exercise: ex, heroSrc, heroGif, heroError: false, loading: false });
if (ex && ex.target) {
svc.listExercises({ target: ex.target, pageSize: 6 }).then((res) => {
const related = ((res && res.items) || [])
.filter(i => i.id !== ex.id)
.slice(0, 6);
this.setData({ related });
}).catch(() => {});
}
}).catch(() => {
this.setData({ loading: false });
});
},
// 收藏 / 取消收藏(心形按钮)
onToggleFav() {
auth.ensureLogin().then(() => {
const id = this.data.id;
const wasFav = this.data.favorited;
const op = wasFav ? favSvc.removeExercise(id) : favSvc.addExercise(id);
op.then(() => {
this.setData({ favorited: !wasFav });
wx.showToast({ title: wasFav ? '已取消收藏' : '已收藏', icon: 'none' });
}).catch(() => {});
}).catch(() => {});
},
// 底部主按钮:加入计划(真实选择器)
onAdd() {
if (this.data.planId) {
planSvc.addExercise(this.data.planId, this.data.id).then(() => {
wx.showToast({ title: '已加入计划', icon: 'success' });
setTimeout(() => wx.navigateBack(), 500);
}).catch(() => {});
return;
}
auth.ensureLogin().then(() => planSvc.listMine()).then((res) => {
const plans = (res && res.items) || res || [];
if (!plans.length) {
wx.showModal({
title: '还没有训练计划',
content: '先创建一个计划,再把动作加进去吧~',
confirmText: '去创建',
cancelText: '稍后',
success: (r) => { if (r.confirm) wx.navigateTo({ url: '/pages/plan-edit/plan-edit' }); }
});
return;
}
const names = plans.slice(0, 6).map((p) => p.name);
wx.showActionSheet({
itemList: names,
success: (sheet) => {
const plan = plans[sheet.tapIndex];
if (!plan) return;
planSvc.addExercise(plan.id, this.data.id).then(() => {
wx.showToast({ title: '已加入「' + plan.name + '」', icon: 'success' });
}).catch(() => {});
}
});
}).catch(() => {});
},
onBack() {
const pages = getCurrentPages();
if (pages.length > 1) {
wx.navigateBack();
} else {
wx.reLaunch({ url: '/pages/index/index' });
}
},
// 详情大图加载失败JPG↔GIF 互为降级,最后 emoji 占位,保证不空白
onHeroError() {
const ex = this.data.exercise || {};
const cur = this.data.heroSrc;
if (cur === ex.image && ex.gifUrl) {
this.setData({ heroSrc: ex.gifUrl });
} else if (cur === ex.gifUrl && ex.image) {
this.setData({ heroSrc: ex.image });
} else {
this.setData({ heroError: true });
}
},
onExercise(e) {
if (!e || !e.detail || !e.detail.id) return;
wx.navigateTo({ url: '/pages/detail/detail?id=' + encodeURIComponent(e.detail.id) });
},
onShareAppMessage() {
const ex = this.data.exercise || {};
const name = ex.displayName || ex.name || '';
return {
title: name ? ('FITCOACH · ' + name) : 'FITCOACH 智能健身教练',
path: '/pages/detail/detail?id=' + this.data.id
};
},
onShareTimeline() {
return {
title: 'FITCOACH · 精准训练推荐',
query: 'id=' + this.data.id
};
}
});