Authored by qxm

Merge branch 'feature/qxm'

@@ -449,23 +449,15 @@ const recordList = ref([ @@ -449,23 +449,15 @@ const recordList = ref([
449 { h: '00', m: '00', s: '00', quickTimeDisplay: '60s', distance: '', weight: '', reps: '', duration: '', restTime: '', isActive: false }, 449 { h: '00', m: '00', s: '00', quickTimeDisplay: '60s', distance: '', weight: '', reps: '', duration: '', restTime: '', isActive: false },
450 ]); 450 ]);
451 451
452 -watch(recordList, () => {  
453 - // console.log('recordList变化,发送长度:', recordList.value.length)  
454 - emit('update:groupCount', recordList.value.length)  
455 -}, { deep: true, immediate: true })  
456 -  
457 const saveToStore = () => { 452 const saveToStore = () => {
458 let records = {}; 453 let records = {};
459 -  
460 if (props.type === 1) { 454 if (props.type === 1) {
461 // 普通动作 455 // 普通动作
462 records[props.actionDetail.id] = recordList.value; 456 records[props.actionDetail.id] = recordList.value;
463 -  
464 } else if (props.type === 2) { 457 } else if (props.type === 2) {
465 // 超级组 458 // 超级组
466 records = superRecordMap.value; 459 records = superRecordMap.value;
467 } 460 }
468 -  
469 // 调用 Pinia 保存 461 // 调用 Pinia 保存
470 trainingStore.saveUnitRecord(props.unitIndex, { 462 trainingStore.saveUnitRecord(props.unitIndex, {
471 records: records, 463 records: records,
@@ -473,6 +465,11 @@ const saveToStore = () => { @@ -473,6 +465,11 @@ const saveToStore = () => {
473 }); 465 });
474 }; 466 };
475 467
  468 +watch(recordList, () => {
  469 + // console.log('recordList变化,发送长度:', recordList.value.length)
  470 + emit('update:groupCount', recordList.value.length)
  471 +}, { deep: true, immediate: true })
  472 +
476 watch([recordList, superRecordMap, userWeight], () => { 473 watch([recordList, superRecordMap, userWeight], () => {
477 saveToStore(); 474 saveToStore();
478 console.log('+++++++++打印全局训练数据 trainingStore.unitRecords++++++++', trainingStore.unitRecords); 475 console.log('+++++++++打印全局训练数据 trainingStore.unitRecords++++++++', trainingStore.unitRecords);
@@ -63,13 +63,13 @@ @@ -63,13 +63,13 @@
63 </view> 63 </view>
64 <!-- 要点,历史,平替动作标签 --> 64 <!-- 要点,历史,平替动作标签 -->
65 <view class="content-tabs"> 65 <view class="content-tabs">
66 - <view class="tab-item" :class="{ active: contentTab === 0 }" @click="contentTab = 0"> 66 + <view class="tab-item" :class="{ active: contentTab === 0 }" @click="switchContentTab(0)">
67 要点 67 要点
68 </view> 68 </view>
69 - <view class="tab-item" :class="{ active: contentTab === 1 }" @click="contentTab = 1"> 69 + <view class="tab-item" :class="{ active: contentTab === 1 }" @click="switchContentTab(1)">
70 历史 70 历史
71 </view> 71 </view>
72 - <view class="tab-item" :class="{ active: contentTab === 2 }" @click="contentTab = 2" v-if="type === 1"> 72 + <view class="tab-item" :class="{ active: contentTab === 2 }" @click="switchContentTab(2)" v-if="type === 1">
73 平替动作 73 平替动作
74 </view> 74 </view>
75 </view> 75 </view>
@@ -189,7 +189,7 @@ @@ -189,7 +189,7 @@
189 <view v-if="contentTab === 2 && type === 1" class="tab-pane slide-up"> 189 <view v-if="contentTab === 2 && type === 1" class="tab-pane slide-up">
190 <view class="substitute-list"> 190 <view class="substitute-list">
191 <view class="sub-item" v-for="item in alternativeActions" :key="item.id" @click="openActionItem(item)"> 191 <view class="sub-item" v-for="item in alternativeActions" :key="item.id" @click="openActionItem(item)">
192 - <image :src="item.urlImage || lostImage" mode="aspectFill" class="img" /> 192 + <image :src="item.url3dAnimation || lostImage" mode="aspectFill" class="img" />
193 <view class="sub-info"> 193 <view class="sub-info">
194 <text class="name">{{ item.name }}</text> 194 <text class="name">{{ item.name }}</text>
195 195
@@ -221,6 +221,8 @@ import { onShareAppMessage } from '@dcloudio/uni-app'; @@ -221,6 +221,8 @@ import { onShareAppMessage } from '@dcloudio/uni-app';
221 // 静态配置 221 // 静态配置
222 222
223 const alternativeActions = ref([]); // 平替动作列表接口数据 223 const alternativeActions = ref([]); // 平替动作列表接口数据
  224 +const loadedHistory = ref(false)
  225 +const loadedAlternative = ref(false)
224 226
225 // 响应式状态 227 // 响应式状态
226 const actionShow = ref(false); 228 const actionShow = ref(false);
@@ -300,25 +302,42 @@ const startTraining = () => { @@ -300,25 +302,42 @@ const startTraining = () => {
300 }); 302 });
301 }; 303 };
302 304
303 -const open = (id, typeData) => { 305 +const switchContentTab = async (chooseTab) => {
  306 + contentTab.value = chooseTab;
  307 + // 切历史
  308 + if (chooseTab === 1 && !loadedHistory.value) {
  309 + await loadTrainHistoryDetail(actionId.value, type.value)
  310 + loadedHistory.value = true;
  311 + }
  312 + // 切平替
  313 + if (chooseTab === 2 && type.value === 1 && !loadedAlternative.value) {
  314 + await loadAlternativeActions(actionId.value)
  315 + loadedAlternative.value = true;
  316 + }
  317 +}
  318 +
  319 +const open = async (id, typeData) => {
304 actionId.value = Number(id); 320 actionId.value = Number(id);
305 type.value = typeData; 321 type.value = typeData;
306 contentTab.value = 0; 322 contentTab.value = 0;
307 console.log('动作详情页面接收id和类型:', actionId.value, type.value); 323 console.log('动作详情页面接收id和类型:', actionId.value, type.value);
308 // 如何判断是动作还是超级组 324 // 如何判断是动作还是超级组
309 if (typeData == 1) { 325 if (typeData == 1) {
310 - loadexercisedetail(actionId.value);  
311 - loadAlternativeActions(actionId.value);  
312 - checkExerciseFavorited(); 326 + await loadexercisedetail(actionId.value);
  327 + // await loadAlternativeActions(actionId.value);
  328 + await checkExerciseFavorited();
313 } else { 329 } else {
314 - loadsuperdetail(actionId.value);  
315 - checkSupersetFavorited(); 330 + await loadsuperdetail(actionId.value);
  331 + await checkSupersetFavorited();
316 } 332 }
317 333
318 console.log('请求参数actionId/typeData', actionId.value, type.value); 334 console.log('请求参数actionId/typeData', actionId.value, type.value);
319 335
  336 + // 重置加载训练历史列表和平替动作列表标记
  337 + loadedHistory.value = false
  338 + loadedAlternative.value = false
320 339
321 - loadTrainHistoryDetail(actionId.value, type.value); 340 + // loadTrainHistoryDetail(actionId.value, type.value);
322 actionShow.value = true; 341 actionShow.value = true;
323 }; 342 };
324 // 打开备注弹窗 343 // 打开备注弹窗
@@ -280,7 +280,7 @@ @@ -280,7 +280,7 @@
280 <CalendarColorPicker v-model:visible="showColorPopup" :daily-template-id="selectedPlanId" 280 <CalendarColorPicker v-model:visible="showColorPopup" :daily-template-id="selectedPlanId"
281 :current-color="currentPlan?.templateBackgroundColor || '#ffffff'" @success="calendarColorPickerSuccess" /> 281 :current-color="currentPlan?.templateBackgroundColor || '#ffffff'" @success="calendarColorPickerSuccess" />
282 282
283 - <MeiRiMoBanXiuGai ref="meirimobanRef" @saveSuccess="loaddailytemplate(selectedDate)" /> 283 + <MeiRiMoBanXiuGai ref="meirimobanRef" @saveSuccess="handleRenderSuccess" />
284 </view> 284 </view>
285 </up-popup> 285 </up-popup>
286 </template> 286 </template>
@@ -446,6 +446,12 @@ const loaddailytemplate = async () => { @@ -446,6 +446,12 @@ const loaddailytemplate = async () => {
446 } 446 }
447 }; 447 };
448 448
  449 +const handleRenderSuccess = async () => {
  450 +
  451 + await loaddailytemplate();
  452 + await nextTick()
  453 +}
  454 +
449 const switchPlan = (index) => { 455 const switchPlan = (index) => {
450 currentPlanIndex.value = index; 456 currentPlanIndex.value = index;
451 console.log('【切换标签后 - currentPlan】', currentPlan.value); 457 console.log('【切换标签后 - currentPlan】', currentPlan.value);
@@ -43,20 +43,18 @@ const isRenderDongzuo = ref(false) @@ -43,20 +43,18 @@ const isRenderDongzuo = ref(false)
43 // 标记是否需要删除当前动作组 43 // 标记是否需要删除当前动作组
44 const needDeleteUnit = ref(false) 44 const needDeleteUnit = ref(false)
45 45
46 -// watch(  
47 -// () => templateStore.replaceId,  
48 -// (id) => {  
49 -// if (id) {  
50 -// needReplaceUnit.value = true  
51 -// uni.showToast({  
52 -// title: '已标记替换',  
53 -// icon: 'success'  
54 -// })  
55 -// }  
56 -// }  
57 -// )  
58 -  
59 -// 原watch不动,补充拉取新数据、替换source.unit 46 +// 缓存打开弹窗传入的数据
  47 +let sourceInfo = ref({
  48 + unit: null,
  49 + unitIndex: null,
  50 + dailyTemplate: null
  51 +})
  52 +// 当前操作的unit和下标
  53 +const currUnit = computed(() => sourceInfo.value.unit || {})
  54 +const currUnitIndex = computed(() => sourceInfo.value.unitIndex ?? 0)
  55 +
  56 +//当替换动作的时候, 原watch不动,补充拉取新数据、替换source.unit
  57 +const replaceCacheUnit = ref(null)
60 watch( 58 watch(
61 () => templateStore.replaceId, 59 () => templateStore.replaceId,
62 async (id) => { 60 async (id) => {
@@ -97,7 +95,29 @@ watch( @@ -97,7 +95,29 @@ watch(
97 } 95 }
98 console.log('newUnit', newUnit); 96 console.log('newUnit', newUnit);
99 } 97 }
  98 +
  99 + // ============【新增改动】缓存拼装好的替换单元,保存复用============
  100 + replaceCacheUnit.value = newUnit
  101 + // =================================================================
  102 +
  103 +
100 sourceInfo.value.unit = newUnit 104 sourceInfo.value.unit = newUnit
  105 +
  106 + // ========== 新增:同步初始化当前unitIndex的Pinia训练记录 ==========
  107 + const unitIdx = sourceInfo.value.unitIndex
  108 + const exercises = newUnit.exercises || []
  109 + let records = {}
  110 + exercises.forEach(ex => {
  111 + // 新动作无历史sets,给默认空白一组(和open初始化逻辑统一)
  112 + records[ex.exerciseId] = [{
  113 + h: '00', m: '00', s: '00', quickTimeDisplay: '60s',
  114 + distance: '', weight: '', reps: '', duration: '', restTime: '', isActive: false
  115 + }]
  116 + })
  117 + // 覆盖当前unitIndex的仓库记录
  118 + trainingStore.saveUnitRecord(unitIdx, { records, userWeight: 70 })
  119 + // =================================================================
  120 +
101 // 销毁重建dongzuo实现刷新 121 // 销毁重建dongzuo实现刷新
102 isRenderDongzuo.value = false 122 isRenderDongzuo.value = false
103 await nextTick() 123 await nextTick()
@@ -106,49 +126,7 @@ watch( @@ -106,49 +126,7 @@ watch(
106 } 126 }
107 ) 127 )
108 128
109 -  
110 -// 缓存打开弹窗传入的数据  
111 -let sourceInfo = ref({  
112 - unit: null,  
113 - unitIndex: null,  
114 - dailyTemplate: null  
115 -})  
116 -// 当前操作的unit和下标  
117 -const currUnit = computed(() => sourceInfo.value.unit || {})  
118 -const currUnitIndex = computed(() => sourceInfo.value.unitIndex ?? 0)  
119 -  
120 -// 格式化数据:统一 动作 / 超级组 结构 → 给 dongzuo 组件用  
121 -// const formatDongzuoData = (unit) => {  
122 -// if (!unit) return {}  
123 -// console.log('++每日模板的unit++', unit);  
124 -  
125 -// if (unit.unitType === 1) {  
126 -// const ex = unit.exercises?.[0] || {}  
127 -// return {  
128 -// id: ex.exerciseId,  
129 -// name: ex.exerciseName,  
130 -// urlImage: ex.url3dAnimation,  
131 -// exerciseType: ex.exerciseType,  
132 -// categoryDescription: ex.categoryDescription || '',  
133 -// equipmentDescription: ex.equipmentDescription || '',  
134 -// }  
135 -// }  
136 -  
137 -// if (unit.unitType === 2) {  
138 -// return {  
139 -// id: unit.unitId,  
140 -// name: unit.unitName || '超级组',  
141 -// exercises: unit.exercises?.map(ex => ({  
142 -// id: ex.exerciseId,  
143 -// name: ex.exerciseName,  
144 -// urlImage: ex.url3dAnimation,  
145 -// exerciseType: ex.exerciseType,  
146 -// })) || []  
147 -// }  
148 -// }  
149 -  
150 -// return {}  
151 -// } 129 +// 格式化传入动作组件的unit
152 const formatDongzuoData = (unit) => { 130 const formatDongzuoData = (unit) => {
153 if (!unit) return {} 131 if (!unit) return {}
154 console.log('++每日模板的unit++', unit); 132 console.log('++每日模板的unit++', unit);
@@ -183,6 +161,8 @@ const formatDongzuoData = (unit) => { @@ -183,6 +161,8 @@ const formatDongzuoData = (unit) => {
183 console.log('格式化后结果:', result) // 打印最终数据 161 console.log('格式化后结果:', result) // 打印最终数据
184 return result 162 return result
185 } 163 }
  164 +
  165 +//
186 const handleDeleteAction = () => { 166 const handleDeleteAction = () => {
187 console.log('父组件处理删除动作') 167 console.log('父组件处理删除动作')
188 needDeleteUnit.value = true 168 needDeleteUnit.value = true
@@ -262,10 +242,12 @@ const closePop = () => { @@ -262,10 +242,12 @@ const closePop = () => {
262 242
263 // 【保存修改:核心,组装参数调用更新接口】 243 // 【保存修改:核心,组装参数调用更新接口】
264 const saveEdit = async () => { 244 const saveEdit = async () => {
265 - if (!dongzuoSingleRef.value) return uni.showToast({ title: '获取数据失败', icon: 'none' }) 245 + if (!dongzuoSingleRef.value)
  246 + return uni.showToast({ title: '获取数据失败', icon: 'none' })
  247 +
266 const { unit, unitIndex, dailyTemplate } = sourceInfo.value 248 const { unit, unitIndex, dailyTemplate } = sourceInfo.value
267 249
268 - // ================== 新增:删除逻辑 ================== 250 + // ================== 删除逻辑 ==================
269 if (needDeleteUnit.value) { 251 if (needDeleteUnit.value) {
270 // 1. 复制所有 unit,删掉当前这个 252 // 1. 复制所有 unit,删掉当前这个
271 const allUnits = [...dailyTemplate.units] 253 const allUnits = [...dailyTemplate.units]
@@ -290,112 +272,19 @@ const saveEdit = async () => { @@ -290,112 +272,19 @@ const saveEdit = async () => {
290 // 直接 return,不执行后面的修改逻辑 272 // 直接 return,不执行后面的修改逻辑
291 return 273 return
292 } 274 }
293 - // ====================================================  
294 275
295 - // ========================替换  
296 - if (needReplaceUnit.value) {  
297 - const replaceId = templateStore.replaceId  
298 - const replaceType = templateStore.replaceType 276 + // ========================替换==================
  277 + // =========【新增:替换动作分支,覆盖基础unit】=========
  278 + let baseUnit = { ...unit }
299 279
300 - if (!replaceId || !replaceType) {  
301 - uni.showToast({ title: '替换异常', icon: 'none' })  
302 - return 280 + if (needReplaceUnit.value && replaceCacheUnit.value) {
  281 + // 使用watch提前缓存好的替换单元作为基础载体
  282 + baseUnit = { ...replaceCacheUnit.value }
303 } 283 }
  284 + // =====================================================
304 285
305 - try {  
306 - let detail = null  
307 -  
308 - // ======================  
309 - // 👇 完全移植你现成的接口逻辑(动作替换)  
310 - // ======================  
311 - if (replaceType === 1) {  
312 - // 普通动作  
313 - const res = await ExercisesApi.getExerciseById(replaceId)  
314 - detail = res.data  
315 - console.log('detail=', detail);  
316 -  
317 - } else if (replaceType === 2) {  
318 - // 超级组  
319 - const res = await SupersetsApi.getSupersetsInfo(replaceId)  
320 - detail = res.data  
321 - console.log('detail=', detail);  
322 - }  
323 -  
324 - if (!detail) {  
325 - uni.showToast({ title: '获取动作详情失败', icon: 'none' })  
326 - return  
327 - }  
328 -  
329 - // ======================  
330 - // 组装 newUnit  
331 - // ======================  
332 - let newUnit = null  
333 - if (replaceType === 1) {  
334 - newUnit = {  
335 - unitType: 1,  
336 - unitId: detail.id,  
337 - unitName: detail.name,  
338 - exercises: [  
339 - {  
340 - exerciseId: detail.id,  
341 - exerciseName: detail.name,  
342 - exerciseType: detail.exerciseType,  
343 - urlImage: detail.url3dAnimation || detail.exerciseCover,  
344 - // urlImage: detail.urlImage || detail.url3dAnimation,  
345 - categoryDescription: detail.categoryDescription || '',  
346 - equipmentDescription: detail.equipmentDescription || '',  
347 - sets: []  
348 - }  
349 - ]  
350 - }  
351 - } else if (replaceType === 2) {  
352 - newUnit = {  
353 - unitType: 2,  
354 - unitId: detail.id,  
355 - unitName: detail.name,  
356 - exercises: detail.exercises.map(e => ({  
357 - exerciseId: e.id,  
358 - exerciseName: e.name,  
359 - exerciseType: e.exerciseType,  
360 - urlImage: e.url3dAnimation || e.exerciseCover,  
361 - sets: []  
362 - }))  
363 - }  
364 - }  
365 -  
366 - // ======================  
367 - // 替换到模板数组  
368 - // ======================  
369 - const allUnits = [...dailyTemplate.units]  
370 - allUnits[unitIndex] = newUnit  
371 -  
372 - console.log('allUnits[unitIndex]=', allUnits[unitIndex]);  
373 -  
374 - await dailytemplateApi.updateDailyTemplate({  
375 - dailyTemplateId: dailyTemplate.id,  
376 - units: allUnits  
377 - })  
378 -  
379 - uni.showToast({ title: '替换成功', icon: 'success' })  
380 - needReplaceUnit.value = false  
381 - emit('saveSuccess')  
382 - closePop()  
383 - return  
384 -  
385 - } catch (err) {  
386 - uni.showToast({ title: '替换失败', icon: 'none' })  
387 - } finally {  
388 - // 最后清空  
389 - templateStore.clearReplaceAction()  
390 - }  
391 - }  
392 -  
393 -  
394 -  
395 -  
396 - // 动作替换  
397 286
398 - let targetExercises = [...unit.exercises] 287 + let targetExercises = [...baseUnit.exercises]
399 if (unit.unitType === 1) { 288 if (unit.unitType === 1) {
400 // 单动作:exercises只有1个 289 // 单动作:exercises只有1个
401 const exItem = targetExercises[0] 290 const exItem = targetExercises[0]
@@ -48,6 +48,9 @@ const props = defineProps({ @@ -48,6 +48,9 @@ const props = defineProps({
48 exerciseId: { 48 exerciseId: {
49 type: [String, Number], 49 type: [String, Number],
50 required: true 50 required: true
  51 + },
  52 + historyType: {
  53 + type: String
51 } 54 }
52 }); 55 });
53 56