Showing
1 changed file
with
404 additions
and
0 deletions
sheep/store/trainingStore.js
0 → 100644
| 1 | +import { defineStore } from 'pinia'; | ||
| 2 | +import ExercisesApi from '@/sheep/api/motion/exercises'; | ||
| 3 | +import SupersetsApi from '@/sheep/api/motion/supersets'; | ||
| 4 | +import TemplatesApi from '@/sheep/api/Template/Templates'; | ||
| 5 | + | ||
| 6 | +export const useTrainingStore = defineStore('training', { | ||
| 7 | + state: () => ({ | ||
| 8 | + id: null, // 页面传过来的 id | ||
| 9 | + type: null, // 页面传过来的 type | ||
| 10 | + actionDetail: {}, // 接口返回的详情 | ||
| 11 | + loading: false, | ||
| 12 | + unitRecords: {}, // 训练数据 | ||
| 13 | + trainingName: '', | ||
| 14 | + // 训练时间 | ||
| 15 | + totalSeconds: 0, | ||
| 16 | + isPause: true, | ||
| 17 | + timerInterval: null, | ||
| 18 | + showPicker: false, | ||
| 19 | + defaultTimeIndex: [0, 0, 0], | ||
| 20 | + trainingTimeText: '', //起始时间 | ||
| 21 | + min: false, | ||
| 22 | + isSystem: 1, //官方模板为1 | ||
| 23 | + dailyTemplateId: null, | ||
| 24 | + isTraining: false, | ||
| 25 | + }), | ||
| 26 | + | ||
| 27 | + actions: { | ||
| 28 | + // 1. 根据 id 和 type 加载数据(动作/超级组/模板) | ||
| 29 | + async loadTrainingDetail(id, type) { | ||
| 30 | + console.log('进入pinia的模板数据加载函数'); | ||
| 31 | + | ||
| 32 | + if (!id || !type) return; | ||
| 33 | + // 先保存 id 和 type 到 store | ||
| 34 | + this.id = id; | ||
| 35 | + this.type = type; | ||
| 36 | + this.loading = true; | ||
| 37 | + try { | ||
| 38 | + let res; | ||
| 39 | + if (type === 1) { | ||
| 40 | + res = await ExercisesApi.getExerciseById(id); | ||
| 41 | + } else if (type === 2) { | ||
| 42 | + res = await SupersetsApi.getSupersetsInfo(id); | ||
| 43 | + } else if (type === 3) { | ||
| 44 | + if (this.isSystem === 1) { | ||
| 45 | + res = await TemplatesApi.getTemplateDetail(id); | ||
| 46 | + } else { | ||
| 47 | + res = await TemplatesApi.getCustTemplateDetail(id); | ||
| 48 | + } | ||
| 49 | + console.log('pinia中打开模板数据:', res); | ||
| 50 | + } | ||
| 51 | + if (res?.data) { | ||
| 52 | + this.actionDetail = res.data; | ||
| 53 | + console.log('加载完模板数据之后进行赋值动态数据:++++++'); | ||
| 54 | + if (type === 3) { | ||
| 55 | + this.initTemplateRecords(); | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + } catch (err) { | ||
| 59 | + console.error('加载训练详情失败:', err); | ||
| 60 | + } finally { | ||
| 61 | + this.loading = false; | ||
| 62 | + } | ||
| 63 | + }, | ||
| 64 | + | ||
| 65 | + loadDailyTemplateForEdit(fullDailyPlan) { | ||
| 66 | + | ||
| 67 | + // 2. 🔥 核心:把【完整每日模板数据】直接赋值给 actionDetail | ||
| 68 | + // 动作、顺序、超级组、组数、全部保留,不请求接口 | ||
| 69 | + this.actionDetail = { ...fullDailyPlan }; | ||
| 70 | + | ||
| 71 | + console.log('✅ 每日模板编辑:已赋值完整模板数据到 actionDetail', this.actionDetail); | ||
| 72 | + | ||
| 73 | + // 3. 固定类型为模板 | ||
| 74 | + this.type = 3; | ||
| 75 | + | ||
| 76 | + // 4. 保存ID | ||
| 77 | + this.id = fullDailyPlan.templateId; | ||
| 78 | + | ||
| 79 | + // 5. 初始化训练记录(重量、次数、完成状态) | ||
| 80 | + this.unitRecords = {}; | ||
| 81 | + const units = this.actionDetail?.units || []; | ||
| 82 | + console.log('每日模板的unit',units); | ||
| 83 | + | ||
| 84 | + units.forEach((unit, unitIndex) => { | ||
| 85 | + let records = {}; | ||
| 86 | + // 按顺序赋值,不依赖 exerciseId,动作可改、可换 | ||
| 87 | + unit.exercises?.forEach((ex, exIndex) => { | ||
| 88 | + records[ex.exerciseId] = ex.sets || []; | ||
| 89 | + }); | ||
| 90 | + | ||
| 91 | + this.unitRecords[unitIndex] = { | ||
| 92 | + records: records, | ||
| 93 | + userWeight: 70, | ||
| 94 | + }; | ||
| 95 | + }); | ||
| 96 | + | ||
| 97 | + console.log('✅ 每日模板加载完成:', this.actionDetail, this.unitRecords); | ||
| 98 | + }, | ||
| 99 | + | ||
| 100 | + // 初始化模板自带的训练数据到 unitRecords | ||
| 101 | + initTemplateRecords() { | ||
| 102 | + console.log('✅=================== 我执行了!开始初始化模板数据'); | ||
| 103 | + console.log('EEEEEEEEEEEEEEEEEEEEEE'); | ||
| 104 | + // 只在空的时候初始化(避免重复覆盖) | ||
| 105 | + if (Object.keys(this.unitRecords).length > 0) return; | ||
| 106 | + if (this.dailyTemplateId) return; | ||
| 107 | + | ||
| 108 | + const units = this.actionDetail?.units || []; | ||
| 109 | + | ||
| 110 | + units.forEach((unit, unitIndex) => { | ||
| 111 | + let records = {}; | ||
| 112 | + const exercises = unit.exercises || []; | ||
| 113 | + | ||
| 114 | + exercises.forEach((ex) => { | ||
| 115 | + const sets = ex.sets || []; | ||
| 116 | + | ||
| 117 | + // 转换 sets → 页面标准格式 | ||
| 118 | + const converted = sets.map((set) => { | ||
| 119 | + const totalSec = set.duration || 0; | ||
| 120 | + const h = String(Math.floor(totalSec / 3600)).padStart(2, '0'); | ||
| 121 | + const m = String(Math.floor((totalSec % 3600) / 60)).padStart(2, '0'); | ||
| 122 | + const s = String(totalSec % 60).padStart(2, '0'); | ||
| 123 | + return { | ||
| 124 | + weight: set.weight ?? '', | ||
| 125 | + reps: set.reps ?? '', | ||
| 126 | + duration: set.duration ?? '', | ||
| 127 | + distance: set.distance ?? '', | ||
| 128 | + restTime: set.restTime ?? '', | ||
| 129 | + h, | ||
| 130 | + m, | ||
| 131 | + s, | ||
| 132 | + quickTimeDisplay: (set.restTime || 60) + 's', | ||
| 133 | + // isActive: false, | ||
| 134 | + isActive: (set.isCompleted ?? 0) === 1, | ||
| 135 | + }; | ||
| 136 | + }); | ||
| 137 | + | ||
| 138 | + records[ex.exerciseId] = converted; | ||
| 139 | + }); | ||
| 140 | + | ||
| 141 | + // 直接存入 Pinia | ||
| 142 | + this.unitRecords[unitIndex] = { | ||
| 143 | + records: records, | ||
| 144 | + userWeight: 70, | ||
| 145 | + }; | ||
| 146 | + }); | ||
| 147 | + }, | ||
| 148 | + | ||
| 149 | + //新增:每日模板专用赋值(真实训练记录) | ||
| 150 | + initDailyTemplateRecords() { | ||
| 151 | + console.log('✅ 每日模板:直接赋值真实训练记录'); | ||
| 152 | + this.unitRecords = {}; | ||
| 153 | + | ||
| 154 | + const units = this.actionDetail?.units || []; | ||
| 155 | + | ||
| 156 | + units.forEach((unit, unitIndex) => { | ||
| 157 | + let records = {}; | ||
| 158 | + const exercises = unit.exercises || []; | ||
| 159 | + | ||
| 160 | + // exercises.forEach((ex) => { | ||
| 161 | + // records[ex.exerciseId] = ex.sets || []; | ||
| 162 | + // }); | ||
| 163 | + exercises.forEach((ex) => { | ||
| 164 | + const sets = ex.sets || []; | ||
| 165 | + | ||
| 166 | + // 转换 sets → 页面标准格式 | ||
| 167 | + const converted = sets.map((set) => { | ||
| 168 | + const totalSec = set.duration || 0; | ||
| 169 | + const h = String(Math.floor(totalSec / 3600)).padStart(2, '0'); | ||
| 170 | + const m = String(Math.floor((totalSec % 3600) / 60)).padStart(2, '0'); | ||
| 171 | + const s = String(totalSec % 60).padStart(2, '0'); | ||
| 172 | + return { | ||
| 173 | + weight: set.weight ?? '', | ||
| 174 | + reps: set.reps ?? '', | ||
| 175 | + duration: set.duration ?? '', | ||
| 176 | + distance: set.distance ?? '', | ||
| 177 | + restTime: set.restTime ?? '', | ||
| 178 | + h, | ||
| 179 | + m, | ||
| 180 | + s, | ||
| 181 | + quickTimeDisplay: (set.restTime || 60) + 's', | ||
| 182 | + // isActive: false, | ||
| 183 | + isActive: (set.isCompleted ?? 0) === 1, | ||
| 184 | + }; | ||
| 185 | + }); | ||
| 186 | + | ||
| 187 | + converted.forEach((item, idx) => { | ||
| 188 | + console.log(`第${idx}组:`, item.h, item.m, item.s); | ||
| 189 | + }); | ||
| 190 | + records[ex.exerciseId] = converted; | ||
| 191 | + }); | ||
| 192 | + | ||
| 193 | + this.unitRecords[unitIndex] = { | ||
| 194 | + records: records, | ||
| 195 | + userWeight: 70, | ||
| 196 | + }; | ||
| 197 | + }); | ||
| 198 | + }, | ||
| 199 | + | ||
| 200 | + // ====================== | ||
| 201 | + // ✅ 工具方法放到这里 | ||
| 202 | + // ====================== | ||
| 203 | + convertUnitToActionDetail(unit) { | ||
| 204 | + //转为动作 | ||
| 205 | + const convertExercises = (list) => { | ||
| 206 | + return (list || []).map((item) => ({ | ||
| 207 | + id: item.exerciseId, | ||
| 208 | + name: item.exerciseName, | ||
| 209 | + // urlImage: item.exerciseCover || item.url3dAnimation, | ||
| 210 | + urlImage: item.url3dAnimation || item.urlImage, | ||
| 211 | + exerciseType: item.exerciseType, | ||
| 212 | + })); | ||
| 213 | + }; | ||
| 214 | + | ||
| 215 | + if (unit.unitType === 2) { | ||
| 216 | + return { | ||
| 217 | + id: unit.supersetId || unit.unitId, | ||
| 218 | + name: unit.unitName, | ||
| 219 | + exercises: convertExercises(unit.exercises), | ||
| 220 | + }; | ||
| 221 | + } | ||
| 222 | + if (unit.unitType === 1) { | ||
| 223 | + const act = (unit.exercises || [])[0] || {}; | ||
| 224 | + return { | ||
| 225 | + id: act.exerciseId, | ||
| 226 | + name: act.exerciseName, | ||
| 227 | + urlImage: act.exerciseCover || act.url3dAnimation, | ||
| 228 | + exerciseType: act.exerciseType, | ||
| 229 | + categoryDescription: act.categoryDescription, | ||
| 230 | + equipmentDescription: act.equipmentDescription, | ||
| 231 | + }; | ||
| 232 | + } | ||
| 233 | + return {}; | ||
| 234 | + }, | ||
| 235 | + | ||
| 236 | + // 替换动作时转换id 和 type 方便替换 新的数据详情 (actionDetail) | ||
| 237 | + replaceAction(newId, newType, newDetail) { | ||
| 238 | + this.id = newId; | ||
| 239 | + this.type = newType; | ||
| 240 | + this.actionDetail = { ...newDetail }; | ||
| 241 | + this.unitRecords = {}; | ||
| 242 | + }, | ||
| 243 | + | ||
| 244 | + // 把当前 动作/超级组 转为模板结构(type=3) | ||
| 245 | + convertToTemplate() { | ||
| 246 | + // 已经是模板,不用转 | ||
| 247 | + if (this.type === 3) { | ||
| 248 | + return; | ||
| 249 | + } | ||
| 250 | + const units = []; | ||
| 251 | + | ||
| 252 | + // 1. 动作 type=1 → 转 unitType=1 | ||
| 253 | + if (this.type === 1) { | ||
| 254 | + units.push({ | ||
| 255 | + unitType: 1, | ||
| 256 | + unitId: this.actionDetail.id, | ||
| 257 | + unitName: this.actionDetail.name, | ||
| 258 | + exercises: [ | ||
| 259 | + { | ||
| 260 | + exerciseId: this.actionDetail.id, | ||
| 261 | + exerciseName: this.actionDetail.name, | ||
| 262 | + exerciseType: this.actionDetail.exerciseType, | ||
| 263 | + urlImage: this.actionDetail.urlImage || this.actionDetail.url3dAnimation, | ||
| 264 | + categoryDescription: this.actionDetail.categoryDescription, | ||
| 265 | + equipmentDescription: this.actionDetail.equipmentDescription, | ||
| 266 | + }, | ||
| 267 | + ], | ||
| 268 | + }); | ||
| 269 | + } | ||
| 270 | + | ||
| 271 | + // 2. 超级组 type=2 → 转 unitType=2 | ||
| 272 | + else if (this.type === 2) { | ||
| 273 | + units.push({ | ||
| 274 | + unitType: 2, | ||
| 275 | + unitId: this.actionDetail.id, | ||
| 276 | + supersetId: this.actionDetail.id, | ||
| 277 | + unitName: this.actionDetail.name, | ||
| 278 | + exercises: this.actionDetail.exercises.map((item) => ({ | ||
| 279 | + exerciseId: item.id, | ||
| 280 | + exerciseName: item.name, | ||
| 281 | + exerciseType: item.exerciseType, | ||
| 282 | + urlImage: item.url3dAnimation || item.urlImage, | ||
| 283 | + })), | ||
| 284 | + }); | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + // 切换为模板模式 | ||
| 288 | + this.type = 3; | ||
| 289 | + this.actionDetail = { | ||
| 290 | + units, | ||
| 291 | + templateName: this.actionDetail.name || '训练', | ||
| 292 | + }; | ||
| 293 | + }, | ||
| 294 | + | ||
| 295 | + // 训练数据的处理 | ||
| 296 | + // 保存一个动作的训练数据 | ||
| 297 | + saveUnitRecord(unitIndex, data) { | ||
| 298 | + this.unitRecords[unitIndex] = { | ||
| 299 | + records: data.records || {}, | ||
| 300 | + userWeight: data.userWeight ?? 70, | ||
| 301 | + }; | ||
| 302 | + }, | ||
| 303 | + | ||
| 304 | + //获取一个动作的训练数据 | ||
| 305 | + getUnitRecord(unitIndex) { | ||
| 306 | + return ( | ||
| 307 | + this.unitRecords[unitIndex] || { | ||
| 308 | + records: {}, | ||
| 309 | + userWeight: 70, | ||
| 310 | + } | ||
| 311 | + ); | ||
| 312 | + }, | ||
| 313 | + | ||
| 314 | + //删除一个动作的所有数据 | ||
| 315 | + deleteUnitRecord(unitIndex) { | ||
| 316 | + delete this.unitRecords[unitIndex]; | ||
| 317 | + }, | ||
| 318 | + | ||
| 319 | + // 单独更新某个动作的体重 | ||
| 320 | + updateUnitUserWeight(unitIndex, userWeight) { | ||
| 321 | + // 如果这个 unit 还没有数据,先创建一个空结构 | ||
| 322 | + if (!this.unitRecords[unitIndex]) { | ||
| 323 | + this.unitRecords[unitIndex] = { | ||
| 324 | + records: {}, | ||
| 325 | + userWeight: 70, | ||
| 326 | + }; | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + // 直接修改体重 | ||
| 330 | + this.unitRecords[unitIndex].userWeight = userWeight; | ||
| 331 | + }, | ||
| 332 | + | ||
| 333 | + // 设置训练名称 | ||
| 334 | + setTrainingName(name) { | ||
| 335 | + this.trainingName = name; | ||
| 336 | + }, | ||
| 337 | + | ||
| 338 | + // 获取训练名称 | ||
| 339 | + getTrainingName() { | ||
| 340 | + return this.trainingName || '训练'; | ||
| 341 | + }, | ||
| 342 | + | ||
| 343 | + // ====================== | ||
| 344 | + // 👇 新增 全局计时 方法 | ||
| 345 | + // ====================== | ||
| 346 | + toggleTimer() { | ||
| 347 | + this.isPause = !this.isPause; | ||
| 348 | + if (!this.isPause) { | ||
| 349 | + // 开启定时器 | ||
| 350 | + this.timerInterval = setInterval(() => { | ||
| 351 | + this.totalSeconds++; | ||
| 352 | + }, 1000); | ||
| 353 | + } else { | ||
| 354 | + // 暂停定时器 | ||
| 355 | + clearInterval(this.timerInterval); | ||
| 356 | + this.timerInterval = null; | ||
| 357 | + } | ||
| 358 | + }, | ||
| 359 | + resetTimer() { | ||
| 360 | + this.totalSeconds = 0; | ||
| 361 | + }, | ||
| 362 | + setTotalSeconds(seconds) { | ||
| 363 | + this.totalSeconds = seconds; | ||
| 364 | + }, | ||
| 365 | + openTimePicker() { | ||
| 366 | + const h = Math.floor(this.totalSeconds / 3600); | ||
| 367 | + const m = Math.floor((this.totalSeconds % 3600) / 60); | ||
| 368 | + const s = this.totalSeconds % 60; | ||
| 369 | + this.defaultTimeIndex = [h, m, s]; | ||
| 370 | + this.showPicker = true; | ||
| 371 | + }, | ||
| 372 | + closeTimePicker() { | ||
| 373 | + this.showPicker = false; | ||
| 374 | + }, | ||
| 375 | + // 清空计时(在clearStore时调用) | ||
| 376 | + clearTimer() { | ||
| 377 | + if (this.timerInterval) { | ||
| 378 | + clearInterval(this.timerInterval); | ||
| 379 | + this.timerInterval = null; | ||
| 380 | + } | ||
| 381 | + this.totalSeconds = 0; | ||
| 382 | + this.isPause = true; | ||
| 383 | + this.showPicker = false; | ||
| 384 | + }, | ||
| 385 | + | ||
| 386 | + setTrainingTimeText(val) { | ||
| 387 | + this.trainingTimeText = val; | ||
| 388 | + }, | ||
| 389 | + // 清空 | ||
| 390 | + clearTrainingStore() { | ||
| 391 | + this.clearTimer(); | ||
| 392 | + this.id = null; | ||
| 393 | + this.type = null; | ||
| 394 | + this.actionDetail = {}; | ||
| 395 | + this.loading = false; | ||
| 396 | + this.unitRecords = {}; | ||
| 397 | + this.trainingTimeText = ''; | ||
| 398 | + this.min = false; | ||
| 399 | + this.isSystem = 1; | ||
| 400 | + }, | ||
| 401 | + }, | ||
| 402 | + | ||
| 403 | + persist: true, // 持久化 | ||
| 404 | +}); |
-
Please register or login to post a comment