Authored by qxm

动作训练的pinia文件提交

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