271 lines
11 KiB
JavaScript
271 lines
11 KiB
JavaScript
import { moduleMock } from './mockData.js'
|
|
|
|
|
|
|
|
function clone(value) {
|
|
|
|
return JSON.parse(JSON.stringify(value))
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getDefaultModuleState() {
|
|
|
|
return {
|
|
|
|
trainingReport: clone(moduleMock.trainingReport),
|
|
|
|
coupons: clone(moduleMock.coupons),
|
|
|
|
couponCenter: clone(moduleMock.couponCenter),
|
|
|
|
pointsHistory: clone(moduleMock.pointsHistory),
|
|
|
|
pointsRewards: clone(moduleMock.pointsRewards),
|
|
|
|
redeemRecords: [],
|
|
|
|
referralRecords: clone(moduleMock.referralRecords),
|
|
|
|
myCourses: clone(moduleMock.myCourses),
|
|
|
|
checkInHistory: clone(moduleMock.checkInHistory)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function mergeModuleState(saved) {
|
|
|
|
const defaults = getDefaultModuleState()
|
|
|
|
if (!saved) return defaults
|
|
|
|
return {
|
|
|
|
trainingReport: { ...defaults.trainingReport, ...(saved.trainingReport || {}) },
|
|
|
|
coupons: saved.coupons?.length ? saved.coupons : defaults.coupons,
|
|
|
|
couponCenter: saved.couponCenter?.length ? saved.couponCenter : defaults.couponCenter,
|
|
|
|
pointsHistory: saved.pointsHistory?.length ? saved.pointsHistory : defaults.pointsHistory,
|
|
|
|
pointsRewards: saved.pointsRewards?.length ? saved.pointsRewards : defaults.pointsRewards,
|
|
|
|
redeemRecords: saved.redeemRecords || defaults.redeemRecords,
|
|
|
|
referralRecords: saved.referralRecords?.length ? saved.referralRecords : defaults.referralRecords,
|
|
|
|
myCourses: saved.myCourses ? mergeMyCourses(defaults.myCourses, saved.myCourses) : defaults.myCourses,
|
|
|
|
checkInHistory: saved.checkInHistory?.length ? saved.checkInHistory : defaults.checkInHistory
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function mergeMyCourses(defaults, saved) {
|
|
|
|
return {
|
|
|
|
group: saved.group || defaults.group,
|
|
|
|
private: saved.private || defaults.private,
|
|
|
|
online: saved.online?.length ? saved.online : defaults.online,
|
|
|
|
package: saved.package?.length ? saved.package : defaults.package
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function syncCouponSummary(store) {
|
|
|
|
const available = store.modules.coupons.filter((c) => c.status === 'available')
|
|
|
|
const top = available[0]
|
|
|
|
store.couponPoints = {
|
|
|
|
...store.couponPoints,
|
|
|
|
amount: top ? `¥${top.amount}` : '暂无',
|
|
|
|
couponDesc: top
|
|
|
|
? `满${top.minSpend}可用 · ${available.length}张`
|
|
|
|
: '暂无可用优惠券',
|
|
|
|
couponAction: available.length ? '去使用' : '去领取',
|
|
|
|
points: store.stats.pointsBalance,
|
|
|
|
pointsLabel: '我的积分',
|
|
|
|
pointsAction: '去兑换'
|
|
|
|
}
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
|
|
|
export function finalizeModules(store) {
|
|
|
|
syncCouponSummary(store)
|
|
|
|
store.checkIns = store.modules.checkInHistory.slice(0, 3).map((item) => ({ ...item }))
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getTrainingReportData(store, period = 'week') {
|
|
|
|
const report = store.modules.trainingReport
|
|
|
|
const trend = period === 'month' ? report.monthlyHours : report.weeklyHours
|
|
|
|
const calTrend = period === 'month' ? report.monthlyCalories : report.weeklyHours.map((w, i) => ({
|
|
|
|
label: w.label,
|
|
|
|
value: Math.round((report.summary.calories / 7) * (w.value || 0.5))
|
|
|
|
}))
|
|
|
|
return {
|
|
|
|
...report,
|
|
|
|
period,
|
|
|
|
summary: {
|
|
|
|
...report.summary,
|
|
|
|
hours: store.stats.trainingHours ?? report.summary.hours,
|
|
|
|
visits: report.summary.visits ?? store.stats.checkInCount ?? 5
|
|
|
|
},
|
|
|
|
trendHours: trend.map((t) => ({ ...t, id: t.label })),
|
|
|
|
trendCalories: calTrend.map((t) => ({ ...t, id: t.label })),
|
|
|
|
sessions: report.sessions.map((s) => ({ ...s }))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getTrainingSessionById(store, id) {
|
|
|
|
const session = store.modules.trainingReport.sessions.find((s) => s.id === Number(id))
|
|
|
|
if (!session) return null
|
|
|
|
return {
|
|
|
|
...session,
|
|
|
|
heartRate: '128 bpm',
|
|
|
|
comment: '动作标准,核心发力良好,下次可增加负重。',
|
|
|
|
checkInTime: `${session.date} ${session.time.split('-')[0]}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function filterTrainingSessions(store, filters = {}) {
|
|
|
|
let list = store.modules.trainingReport.sessions.map((s) => ({ ...s }))
|
|
|
|
if (filters.type && filters.type !== 'all') {
|
|
|
|
list = list.filter((s) => s.type === filters.type)
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getCouponsByStatus(store, status) {
|
|
|
|
return store.modules.coupons.filter((c) => c.status === status).map((c) => ({ ...c }))
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getCouponById(store, id) {
|
|
|
|
const c = store.modules.coupons.find((item) => item.id === Number(id))
|
|
|
|
return c ? { ...c } : null
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useCoupon(store, id) {
|
|
|
|
const coupon = store.modules.coupons.find((c) => c.id === id)
|
|
|
|
if (!coupon || coupon.status !== 'available') return null
|
|
|
|
coupon.status = 'used'
|
|
|
|
coupon.usedAt = new Date().toISOString().slice(0, 10)
|
|
|
|
syncCouponSummary(store)
|
|
|
|
return coupon
|
|
|
|
}
|
|
|
|
|
|
|
|
export function deleteExpiredCoupon(store, id) {
|
|
|
|
const idx = store.modules.coupons.findIndex((c) => c.id === id && c.status === 'expired')
|
|
|
|
if (idx >= 0) store.modules.coupons.splice(idx, 1)
|
|
|
|
syncCouponSummary(store)
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getCouponCenterList(store) {
|
|
|
|
return store.modules.couponCenter.map((c) => ({ ...c }))
|
|
|
|
}
|
|
|
|
|
|
|
|
export function claimCouponFromCenter(store, centerId) {
|
|
|
|
const item = store.modules.couponCenter.find((c) => c.id === centerId)
|
|
|
|
if (!item || item.claimed) return { ok: false, message: '已领取或不存在' }
|
|
|
|
const nextId = store.modules.coupons.reduce((m, c) => Math.max(m, c.id || 0), 0) + 1
|
|
|
|
const expire = new Date()
|
|
|
|
expire.setDate(expire.getDate() + item.expireDays)
|
|
|
|
store.modules.coupons.unshift({
|
|
|
|
id: nextId,
|
|
|
|
status: 'available',
|
|
|
|
amount: item.amount,
|
|
|
|
title: item.title,
|
|
|
|
desc: item.desc,
|
|
|
|
expire: expire.toISOString().slice(0, 10),
|
|
|
|
minSpend: item.minSpend,
|
|
|
|
tag: item.tag,
|
|
|
|
rules: `领取后${item.expireDays}天内有效,满${item.minSpend}可用`,
|
|
|
|
scope: '全门店',
|
|
|
|
flow: '预约课程时使用'
|
|
|
|
})
|
|
|
|
item.claimed = true
|
|
|
|
syncCouponSummary(store)
|
|
|
|
return { ok: true, message: '领取成功' }
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getPointsPageData(store) {
|
|
|
|
return {
|
|
|
|
balance: store.stats.pointsBalance,
|
|
|
|
config: moduleMock.pointsConfig,
|
|
|
|
history: store.modules.pointsHistory.map((h) => ({ ...h })),
|
|
|
|
rewards: store.modules.pointsRewards.map((r) => ({ ...r })),
|
|
|
|
redeemRecords: (store.modules.redeemRecords || []).map((r) => ({ ...r }))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function nextHistoryId(history) {
|
|
|
|
return (history || []).reduce((max, item) => Math.max(max, item.id || 0), 0) + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatNow() {
|
|
|
|
const d = new Date()
|
|
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
|
|
|
}
|
|
|
|
|
|
|
|
export function redeemPointsReward(store, rewardId) {
|
|
|
|
const reward = store.modules.pointsRewards.find((r) => r.id === rewardId)
|
|
|
|
if (!reward || reward.stock <= 0) return { ok: false, message: '库存不足' }
|
|
|
|
if (store.stats.pointsBalance < reward.cost) {
|
|
|
|
return { ok: false, message: '积分不足' }
|
|
|
|
}
|
|
|
|
store.stats.pointsBalance -= reward.cost
|
|
|
|
reward.stock -= 1
|
|
|
|
store.modules.pointsHistory.unshift({
|
|
|
|
id: nextHistoryId(store.modules.pointsHistory),
|
|
|
|
type: 'spend',
|
|
|
|
title: `兑换${reward.name}`,
|
|
|
|
amount: -reward.cost,
|
|
|
|
time: formatNow(),
|
|
|
|
balance: store.stats.pointsBalance
|
|
|
|
})
|
|
|
|
store.modules.redeemRecords = store.modules.redeemRecords || []
|
|
|
|
store.modules.redeemRecords.unshift({
|
|
|
|
id: nextHistoryId(store.modules.redeemRecords),
|
|
|
|
name: reward.name,
|
|
|
|
cost: reward.cost,
|
|
|
|
time: formatNow()
|
|
|
|
})
|
|
|
|
syncCouponSummary(store)
|
|
|
|
return { ok: true, message: '兑换成功', reward }
|
|
|
|
}
|
|
|
|
|
|
|
|
export function filterPointsHistory(store, filter = 'all') {
|
|
|
|
const list = store.modules.pointsHistory
|
|
|
|
if (filter === 'earn') return list.filter((h) => h.amount > 0).map((h) => ({ ...h }))
|
|
|
|
if (filter === 'spend') return list.filter((h) => h.amount < 0).map((h) => ({ ...h }))
|
|
|
|
return list.map((h) => ({ ...h }))
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getReferralPageData(store) {
|
|
|
|
return {
|
|
|
|
...store.referral,
|
|
|
|
records: store.modules.referralRecords.map((r) => ({ ...r })),
|
|
|
|
rules: moduleMock.referralRules,
|
|
|
|
rewardSummary: moduleMock.referralRewardSummary
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getMyCoursesData(store, tab) {
|
|
|
|
const data = store.modules.myCourses
|
|
|
|
if (tab === 'group') return { ...data.group }
|
|
|
|
if (tab === 'private') return { ...data.private }
|
|
|
|
if (tab === 'online') return { list: data.online.map((c) => ({ ...c })) }
|
|
|
|
if (tab === 'package') return { list: data.package.map((c) => ({ ...c })) }
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getOnlineCourseById(store, id) {
|
|
|
|
const course = store.modules.myCourses.online.find((c) => c.id === Number(id))
|
|
|
|
if (!course) return null
|
|
|
|
return {
|
|
|
|
...course,
|
|
|
|
chapters: [
|
|
|
|
{ id: 1, title: '热身激活', duration: '8分钟', done: true },
|
|
|
|
{ id: 2, title: '核心训练 A', duration: '12分钟', done: true },
|
|
|
|
{ id: 3, title: '核心训练 B', duration: '10分钟', done: true },
|
|
|
|
{ id: 4, title: '拉伸放松', duration: '8分钟', done: true },
|
|
|
|
{ id: 5, title: '进阶组合', duration: '15分钟', done: false },
|
|
|
|
{ id: 6, title: '总结复盘', duration: '5分钟', done: false }
|
|
|
|
],
|
|
|
|
comments: [{ user: '学员A', text: '跟着练很方便' }]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function updateOnlineProgress(store, courseId, progress) {
|
|
|
|
const course = store.modules.myCourses.online.find((c) => c.id === Number(courseId))
|
|
|
|
if (course) course.progress = progress
|
|
|
|
return course
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getCheckInHistory(store, filter = 'all') {
|
|
|
|
const list = store.modules.checkInHistory
|
|
|
|
if (filter === 'all') return list.map((i) => ({ ...i }))
|
|
|
|
return list.filter((i) => i.tagTheme === filter).map((i) => ({ ...i }))
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @deprecated use getMyCoursesData */
|
|
|
|
export function getMyCoursesByTab(store, tab) {
|
|
|
|
if (tab === 'ongoing') return store.modules.myCourses.group?.ongoing || []
|
|
|
|
if (tab === 'completed') return store.modules.myCourses.group?.completed || []
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
export { moduleMock }
|
|
|