训计日历的间歇动作完善,训练图片修改,复制到接口,备注
Showing
15 changed files
with
417 additions
and
220 deletions
| 1 | <template> | 1 | <template> |
| 2 | - <up-popup :show="show" mode="bottom" round="16" closeable @close="show = false" :safeAreaInsetBottom="false"> | 2 | + <up-popup :show="show" mode="bottom" round="16" :safeAreaInsetBottom="false"> |
| 3 | <view class="desc-container"> | 3 | <view class="desc-container"> |
| 4 | - <view class="title">动作备注</view> | ||
| 5 | - | ||
| 6 | - <view class="input-box"> | ||
| 7 | - <up-textarea | ||
| 8 | - v-model="tempNoteContent" | ||
| 9 | - placeholder="此处填写个人备注" | ||
| 10 | - autoHeight | ||
| 11 | - border="none" | ||
| 12 | - customStyle="background: #242424; padding: 20rpx; border-radius: 12rpx; color: #fff" | ||
| 13 | - placeholderStyle="color: #999" | ||
| 14 | - ></up-textarea> | ||
| 15 | - </view> | ||
| 16 | - | ||
| 17 | - <view class="footer"> | 4 | + <view class="title"> |
| 5 | + <view @click="show = false"> | ||
| 6 | + <up-icon name="close" color="#fff" size="20"></up-icon> | ||
| 7 | + </view> | ||
| 8 | + <text class="titleName"> | ||
| 9 | + 动作备注 | ||
| 10 | + </text> | ||
| 18 | <view class="btn" @click="saveNoteContent">保存</view> | 11 | <view class="btn" @click="saveNoteContent">保存</view> |
| 19 | </view> | 12 | </view> |
| 13 | + <view class="input-box"> | ||
| 14 | + <up-textarea v-model="tempNoteContent" placeholder="此处填写个人备注" class="noteConten" | ||
| 15 | + customStyle="border-radius:12rpx; padding:20rpx;background: #242424;" /> | ||
| 16 | + </view> | ||
| 20 | </view> | 17 | </view> |
| 21 | </up-popup> | 18 | </up-popup> |
| 22 | </template> | 19 | </template> |
| 23 | 20 | ||
| 24 | <script setup> | 21 | <script setup> |
| 25 | - import { ref } from 'vue'; | 22 | +import { ref } from 'vue'; |
| 26 | 23 | ||
| 27 | - const show = ref(false); | ||
| 28 | - const tempNoteContent = ref(''); // 临时编辑的备注内容 | 24 | +const show = ref(false); |
| 25 | +const tempNoteContent = ref(''); // 临时编辑的备注内容 | ||
| 29 | 26 | ||
| 30 | - const emit = defineEmits(['saveSuccess']); | ||
| 31 | - // 保存备注 | ||
| 32 | - const saveNoteContent = async () => { | ||
| 33 | - const content = tempNoteContent.value.trim(); | ||
| 34 | - // 如果内容为空,直接返回,不提交 | ||
| 35 | - if (!content) { | ||
| 36 | - uni.showToast({ title: '备注不能为空', icon: 'none' }); | ||
| 37 | - return; | ||
| 38 | - } | ||
| 39 | - emit('saveSuccess', content); | ||
| 40 | - // 关闭弹窗 | ||
| 41 | - show.value = false; | ||
| 42 | - }; | 27 | +const emit = defineEmits(['saveSuccess']); |
| 28 | +// 保存备注 | ||
| 29 | +const saveNoteContent = async () => { | ||
| 30 | + const content = tempNoteContent.value.trim(); | ||
| 31 | + // 如果内容为空,直接返回,不提交 | ||
| 32 | + if (!content) { | ||
| 33 | + uni.showToast({ title: '备注不能为空', icon: 'none' }); | ||
| 34 | + return; | ||
| 35 | + } | ||
| 36 | + emit('saveSuccess', content); | ||
| 37 | + // 关闭弹窗 | ||
| 38 | + show.value = false; | ||
| 39 | +}; | ||
| 43 | 40 | ||
| 44 | - const open = () => { | ||
| 45 | - show.value = true; | ||
| 46 | - }; | 41 | +const open = () => { |
| 42 | + show.value = true; | ||
| 43 | +}; | ||
| 47 | 44 | ||
| 48 | - defineExpose({ open }); | 45 | +defineExpose({ open }); |
| 49 | </script> | 46 | </script> |
| 50 | 47 | ||
| 51 | <style lang="scss" scoped> | 48 | <style lang="scss" scoped> |
| 52 | - .desc-container { | ||
| 53 | - background-color: #1a1a1a; | ||
| 54 | - padding: 40rpx 30rpx 40rpx; | ||
| 55 | - | ||
| 56 | - .title { | ||
| 57 | - color: #fff; | ||
| 58 | - font-size: 32rpx; | ||
| 59 | - font-weight: 500; | ||
| 60 | - text-align: center; | ||
| 61 | - margin-bottom: 40rpx; | ||
| 62 | - } | 49 | +.desc-container { |
| 50 | + background-color: #1a1a1a; | ||
| 51 | + padding: 40rpx 30rpx 40rpx; | ||
| 52 | + height: 75vh; | ||
| 63 | 53 | ||
| 64 | - .input-box { | ||
| 65 | - margin-bottom: 60rpx; | 54 | + .title { |
| 55 | + display: flex; | ||
| 56 | + justify-content: space-between; | ||
| 57 | + font-weight: 500; | ||
| 58 | + text-align: center; | ||
| 59 | + color: #fff; | ||
| 60 | + font-size: 32rpx; | ||
| 61 | + margin-bottom: 40rpx; | ||
| 62 | + } | ||
| 66 | 63 | ||
| 67 | - /* 穿透修改 u-textarea 内部文字颜色 */ | ||
| 68 | - :deep(.u-textarea__field) { | ||
| 69 | - color: #ccc !important; | ||
| 70 | - } | ||
| 71 | - } | 64 | + .btn { |
| 65 | + width: 100rpx; | ||
| 66 | + height: 50rpx; | ||
| 67 | + background-color: #fedc1f; | ||
| 68 | + color: #000; | ||
| 69 | + border-radius: 30rpx; | ||
| 70 | + display: flex; | ||
| 71 | + align-items: center; | ||
| 72 | + justify-content: center; | ||
| 73 | + font-size: 28rpx; | ||
| 74 | + } | ||
| 72 | 75 | ||
| 73 | - .footer { | ||
| 74 | - width: 100%; | ||
| 75 | - display: flex; | ||
| 76 | - justify-content: center; | ||
| 77 | - align-items: center; | 76 | + .input-box { |
| 77 | + margin-bottom: 60rpx; | ||
| 78 | 78 | ||
| 79 | - .btn { | ||
| 80 | - width: 100%; | ||
| 81 | - height: 80rpx; | ||
| 82 | - background-color: #fedc1f; | ||
| 83 | - color: #333; | ||
| 84 | - border-radius: 12rpx; | ||
| 85 | - text-align: center; | ||
| 86 | - line-height: 80rpx; | ||
| 87 | - } | 79 | + // 输入文字白色 |
| 80 | + :deep(.u-textarea__field) { | ||
| 81 | + color: #fff !important; | ||
| 88 | } | 82 | } |
| 83 | + | ||
| 89 | } | 84 | } |
| 85 | +} | ||
| 90 | </style> | 86 | </style> |
| @@ -305,7 +305,8 @@ const handleConfirm = async () => { | @@ -305,7 +305,8 @@ const handleConfirm = async () => { | ||
| 305 | exerciseId: detail.id, | 305 | exerciseId: detail.id, |
| 306 | exerciseName: detail.name, | 306 | exerciseName: detail.name, |
| 307 | exerciseType: detail.exerciseType, | 307 | exerciseType: detail.exerciseType, |
| 308 | - urlImage: detail.urlImage || detail.url3dAnimation, | 308 | + urlImage: detail.url3dAnimation || '', |
| 309 | + // urlImage: detail.urlImage || detail.url3dAnimation, | ||
| 309 | categoryDescription: detail.categoryDescription, | 310 | categoryDescription: detail.categoryDescription, |
| 310 | equipmentDescription: detail.equipmentDescription, | 311 | equipmentDescription: detail.equipmentDescription, |
| 311 | } | 312 | } |
| @@ -326,7 +327,8 @@ const handleConfirm = async () => { | @@ -326,7 +327,8 @@ const handleConfirm = async () => { | ||
| 326 | exerciseId: e.id, | 327 | exerciseId: e.id, |
| 327 | exerciseName: e.name, | 328 | exerciseName: e.name, |
| 328 | exerciseType: e.exerciseType, | 329 | exerciseType: e.exerciseType, |
| 329 | - urlImage: e.urlImage || e.url3dAnimation, | 330 | + urlImage: e.url3dAnimation || e.exerciseCover, |
| 331 | + // exerciseCover | ||
| 330 | })) | 332 | })) |
| 331 | }; | 333 | }; |
| 332 | } | 334 | } |
| @@ -282,7 +282,6 @@ const onWeightConfirm = (e) => { | @@ -282,7 +282,6 @@ const onWeightConfirm = (e) => { | ||
| 282 | showWeightPicker.value = false | 282 | showWeightPicker.value = false |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | -// 文字备注 | ||
| 286 | // 打开备注弹窗 | 285 | // 打开备注弹窗 |
| 287 | const openBeizhu = () => { | 286 | const openBeizhu = () => { |
| 288 | nextTick(() => { | 287 | nextTick(() => { |
| @@ -652,7 +651,8 @@ const addRow = () => { | @@ -652,7 +651,8 @@ const addRow = () => { | ||
| 652 | recordList.value.push({ | 651 | recordList.value.push({ |
| 653 | h: '00', m: '00', s: '00', | 652 | h: '00', m: '00', s: '00', |
| 654 | quickTimeDisplay: '60s', | 653 | quickTimeDisplay: '60s', |
| 655 | - distance: '', weight: '', reps: '', isActive: false, | 654 | + distance: '', weight: '', reps: '', duration: '', |
| 655 | + restTime: '', isActive: false, | ||
| 656 | }); | 656 | }); |
| 657 | }; | 657 | }; |
| 658 | const deleteRow = (index) => { | 658 | const deleteRow = (index) => { |
| @@ -167,7 +167,7 @@ | @@ -167,7 +167,7 @@ | ||
| 167 | <!-- <view class="difficulty">困难</view> --> | 167 | <!-- <view class="difficulty">困难</view> --> |
| 168 | <view class="difficulty">{{ | 168 | <view class="difficulty">{{ |
| 169 | item.weight ? item.weight + 'kg' : '无负重' | 169 | item.weight ? item.weight + 'kg' : '无负重' |
| 170 | - }}</view> | 170 | + }}</view> |
| 171 | </view> | 171 | </view> |
| 172 | <view class="group-chips"> | 172 | <view class="group-chips"> |
| 173 | <view class="chip" v-for="(set, index) in item.setConfigList" :key="index"> | 173 | <view class="chip" v-for="(set, index) in item.setConfigList" :key="index"> |
| @@ -347,6 +347,12 @@ const handleNoteSave = async (content) => { | @@ -347,6 +347,12 @@ const handleNoteSave = async (content) => { | ||
| 347 | actionDetail.value.userNote = content; | 347 | actionDetail.value.userNote = content; |
| 348 | } catch (e) { | 348 | } catch (e) { |
| 349 | console.log(e); | 349 | console.log(e); |
| 350 | + } finally { | ||
| 351 | + if (type == 1) { | ||
| 352 | + await loadexercisedetail(actionId.value) | ||
| 353 | + } else { | ||
| 354 | + await loadsuperdetail(actionId.value) | ||
| 355 | + } | ||
| 350 | } | 356 | } |
| 351 | }; | 357 | }; |
| 352 | 358 |
| @@ -55,7 +55,9 @@ | @@ -55,7 +55,9 @@ | ||
| 55 | </view> | 55 | </view> |
| 56 | <view class="plan-header-btns"> | 56 | <view class="plan-header-btns"> |
| 57 | <button class="plan-btn more-btn" @click.stop="handlePlanMore">更多</button> | 57 | <button class="plan-btn more-btn" @click.stop="handlePlanMore">更多</button> |
| 58 | - <button class="plan-btn copy-btn" @click.stop="openCalendarPopup(currentPlan?.templateId)">复制到</button> | 58 | + <button class="plan-btn copy-btn" @click.stop="openCopyCalendarPopup(currentPlan?.templateId)"> |
| 59 | + 复制到 | ||
| 60 | + </button> | ||
| 59 | <button class="plan-btn go-train-btn-small" @click.stop="handleTrainAgain"> | 61 | <button class="plan-btn go-train-btn-small" @click.stop="handleTrainAgain"> |
| 60 | <text class="go-train-text-sm">今日再练</text> | 62 | <text class="go-train-text-sm">今日再练</text> |
| 61 | <!-- <text class="go-train-tag-sm">GO</text> --> | 63 | <!-- <text class="go-train-tag-sm">GO</text> --> |
| @@ -82,7 +84,7 @@ | @@ -82,7 +84,7 @@ | ||
| 82 | <view class="action-top"> | 84 | <view class="action-top"> |
| 83 | <text class="action-name">{{ unit.exercises[0]?.exerciseName || '动作名称' }}</text> | 85 | <text class="action-name">{{ unit.exercises[0]?.exerciseName || '动作名称' }}</text> |
| 84 | <text class="action-totalWeight">{{ unit.totalWeight | 86 | <text class="action-totalWeight">{{ unit.totalWeight |
| 85 | - }}kg</text> | 87 | + }}kg</text> |
| 86 | </view> | 88 | </view> |
| 87 | 89 | ||
| 88 | <!-- 下半部分:组次行 → 向左对齐图片 ✅核心--> | 90 | <!-- 下半部分:组次行 → 向左对齐图片 ✅核心--> |
| @@ -93,10 +95,10 @@ | @@ -93,10 +95,10 @@ | ||
| 93 | <view class="set-left" :style="{ color: set.isCompleted === 1 ? '#333' : '#a2a2a2' }"> | 95 | <view class="set-left" :style="{ color: set.isCompleted === 1 ? '#333' : '#a2a2a2' }"> |
| 94 | <!-- <text class="indexData">{{ setIdx + 1 }}</text> --> | 96 | <!-- <text class="indexData">{{ setIdx + 1 }}</text> --> |
| 95 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 0"> | 97 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 0"> |
| 96 | - {{ set.weight }}kg × {{ set.reps }}次 | 98 | + {{ set.weight }}kg x {{ set.reps }}次 |
| 97 | </text> | 99 | </text> |
| 98 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 1"> | 100 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 1"> |
| 99 | - {{ formatSecondsToHms(set.duration) }} × {{ set.distance }}km | 101 | + {{ formatSecondsToHms(set.duration) }} x {{ set.distance }}km |
| 100 | </text> | 102 | </text> |
| 101 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 2"> | 103 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 2"> |
| 102 | {{ set.reps }}次 | 104 | {{ set.reps }}次 |
| @@ -108,13 +110,15 @@ | @@ -108,13 +110,15 @@ | ||
| 108 | {{ set.weight }}kg x {{ set.duration }}s x {{ set.restTime }} | 110 | {{ set.weight }}kg x {{ set.duration }}s x {{ set.restTime }} |
| 109 | </text> | 111 | </text> |
| 110 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 6"> | 112 | <text class="set-content" v-if="unit.exercises[0].exerciseType === 6"> |
| 111 | - {{ set.reps }}次 x {{ formatSecondsToHms(set.duration) }} | 113 | + {{ set.reps }}组 x {{ set.duration }}秒 x {{ set.restTime || 0 }}秒/组休息 |
| 112 | </text> | 114 | </text> |
| 115 | + </view> | ||
| 116 | + <view class="set-right"> | ||
| 113 | <text class="rest-time" v-if="set.restTime && unit.exercises[0].exerciseType != 6"> | 117 | <text class="rest-time" v-if="set.restTime && unit.exercises[0].exerciseType != 6"> |
| 114 | {{ set.restTime || 0 }}s | 118 | {{ set.restTime || 0 }}s |
| 115 | </text> | 119 | </text> |
| 120 | + <text class="check" v-if="set.isCompleted === 1">✓</text> | ||
| 116 | </view> | 121 | </view> |
| 117 | - <text class="check" v-if="set.isCompleted === 1">✓</text> | ||
| 118 | </view> | 122 | </view> |
| 119 | </view> | 123 | </view> |
| 120 | </view> | 124 | </view> |
| @@ -165,30 +169,31 @@ | @@ -165,30 +169,31 @@ | ||
| 165 | <view class="set-content" | 169 | <view class="set-content" |
| 166 | :style="{ color: ex.sets[idx].isCompleted === 1 ? '#333' : '#a2a2a2' }"> | 170 | :style="{ color: ex.sets[idx].isCompleted === 1 ? '#333' : '#a2a2a2' }"> |
| 167 | <!-- 适配不同动作类型的显示文本 --> | 171 | <!-- 适配不同动作类型的显示文本 --> |
| 168 | - <text v-if="ex.exerciseType === 0"> | ||
| 169 | - {{ ex.sets[idx].weight }}kg × {{ ex.sets[idx].reps }}次 | 172 | + <text v-if="ex.exerciseType === 0" class="detai-data"> |
| 173 | + {{ ex.sets[idx].weight }}kg x {{ ex.sets[idx].reps }}次 | ||
| 170 | </text> | 174 | </text> |
| 171 | - <text v-if="ex.exerciseType === 1"> | ||
| 172 | - {{ formatSecondsToHms(ex.sets[idx].duration) }} × {{ | 175 | + <text v-if="ex.exerciseType === 1" class="detai-data"> |
| 176 | + {{ formatSecondsToHms(ex.sets[idx].duration) }} x {{ | ||
| 173 | ex.sets[idx].distance | 177 | ex.sets[idx].distance |
| 174 | }}km | 178 | }}km |
| 175 | </text> | 179 | </text> |
| 176 | - <text v-if="ex.exerciseType === 2"> | 180 | + <text v-if="ex.exerciseType === 2" class="detai-data"> |
| 177 | {{ ex.sets[idx].reps }}次 | 181 | {{ ex.sets[idx].reps }}次 |
| 178 | </text> | 182 | </text> |
| 179 | - <text v-if="ex.exerciseType === 3"> | 183 | + <text v-if="ex.exerciseType === 3" class="detai-data"> |
| 180 | {{ formatSecondsToHms(ex.sets[idx].duration) }} | 184 | {{ formatSecondsToHms(ex.sets[idx].duration) }} |
| 181 | </text> | 185 | </text> |
| 182 | - <text v-if="[4, 5].includes(ex.exerciseType)"> | ||
| 183 | - {{ ex.sets[idx].weight }}kg × {{ ex.sets[idx].reps }}次 | 186 | + <text v-if="[4, 5].includes(ex.exerciseType)" class="detai-data"> |
| 187 | + {{ ex.sets[idx].weight }}kg x {{ ex.sets[idx].reps }}次 | ||
| 184 | </text> | 188 | </text> |
| 185 | - <text v-if="ex.exerciseType === 6"> | ||
| 186 | - {{ formatSecondsToHms(ex.sets[idx].duration) }} | 189 | + <text v-if="ex.exerciseType === 6" class="detai-data"> |
| 190 | + {{ ex.sets[idx].reps }}次 x {{ ex.sets[idx].duration }}秒 x {{ ex.sets[idx].restTime | ||
| 191 | + }}秒/组休息 | ||
| 187 | </text> | 192 | </text> |
| 188 | - </view> | ||
| 189 | - <view class="rest-time" v-if="ex.sets[idx]?.restTime && ex.exerciseType !== 6" | ||
| 190 | - :style="{ color: ex.sets[idx].isCompleted === 1 ? '#999 !important' : '#a2a2a2 !important' }"> | ||
| 191 | - {{ ex.sets[idx].restTime }}s | 193 | + <view class="rest-time" v-if="ex.sets[idx]?.restTime && ex.exerciseType !== 6" |
| 194 | + :style="{ color: ex.sets[idx].isCompleted === 1 ? '#999 !important' : '#a2a2a2 !important' }"> | ||
| 195 | + {{ ex.sets[idx].restTime }}s | ||
| 196 | + </view> | ||
| 192 | </view> | 197 | </view> |
| 193 | <text class="check" v-if="ex.sets[idx].isCompleted === 1">✓</text> | 198 | <text class="check" v-if="ex.sets[idx].isCompleted === 1">✓</text> |
| 194 | </template> | 199 | </template> |
| @@ -225,7 +230,7 @@ | @@ -225,7 +230,7 @@ | ||
| 225 | </view> --> | 230 | </view> --> |
| 226 | </view> | 231 | </view> |
| 227 | 232 | ||
| 228 | - <AddTrainPopup v-model:visible="showAddTrainPopup" /> | 233 | + <AddTrainPopup v-model:visible="showAddTrainPopup" @successAddTrain="handleAddTrain" /> |
| 229 | 234 | ||
| 230 | <!-- 日期备注弹窗 --> | 235 | <!-- 日期备注弹窗 --> |
| 231 | <RiliRiqibeizhu v-model:visible="showRiqibeizhu" :date="date" :note-id="currentEditId" | 236 | <RiliRiqibeizhu v-model:visible="showRiqibeizhu" :date="date" :note-id="currentEditId" |
| @@ -268,12 +273,13 @@ | @@ -268,12 +273,13 @@ | ||
| 268 | <text class="item-text delete-text">删除整个计划</text> | 273 | <text class="item-text delete-text">删除整个计划</text> |
| 269 | </view> | 274 | </view> |
| 270 | </up-popup> | 275 | </up-popup> |
| 271 | - <!-- 复制到弹窗 --> | 276 | + <!-- 复制到弹窗/移动到/添加到 --> |
| 272 | <AddToCalendarPopup ref="calendarPopupRef" :template-id="currentPlan?.templateId ?? 0" | 277 | <AddToCalendarPopup ref="calendarPopupRef" :template-id="currentPlan?.templateId ?? 0" |
| 273 | - @success="handleCalendarSuccess" :mask-click="true" @click.stop /> | 278 | + @success="handleCalendarSuccess" :mask-click="true" @click.stop |
| 279 | + :daily-template-id="currentPlan?.dailyTemplateId" :is-copy="isCopyMode" /> | ||
| 274 | <!-- 设置日历颜色弹窗 --> | 280 | <!-- 设置日历颜色弹窗 --> |
| 275 | <CalendarColorPicker v-model:visible="showColorPopup" :daily-template-id="selectedPlanId" | 281 | <CalendarColorPicker v-model:visible="showColorPopup" :daily-template-id="selectedPlanId" |
| 276 | - :current-color="currentPlan?.templateBackgroundColor || '#ffffff'" @success="loaddailytemplate" /> | 282 | + :current-color="currentPlan?.templateBackgroundColor || '#ffffff'" @success="calendarColorPickerSuccess" /> |
| 277 | 283 | ||
| 278 | <MeiRiMoBanXiuGai ref="meirimobanRef" @saveSuccess="loaddailytemplate(selectedDate)" /> | 284 | <MeiRiMoBanXiuGai ref="meirimobanRef" @saveSuccess="loaddailytemplate(selectedDate)" /> |
| 279 | </view> | 285 | </view> |
| @@ -311,6 +317,7 @@ const resdailyData = ref([]); | @@ -311,6 +317,7 @@ const resdailyData = ref([]); | ||
| 311 | const currentPlanIndex = ref(0); | 317 | const currentPlanIndex = ref(0); |
| 312 | 318 | ||
| 313 | const isMoveMode = ref(false); | 319 | const isMoveMode = ref(false); |
| 320 | +const isCopyMode = ref(false); | ||
| 314 | const showRiqibeizhu = ref(false) | 321 | const showRiqibeizhu = ref(false) |
| 315 | const moreShow = ref(false) | 322 | const moreShow = ref(false) |
| 316 | const calendarPopupRef = ref(null); | 323 | const calendarPopupRef = ref(null); |
| @@ -398,6 +405,11 @@ const openColorPopup = () => { | @@ -398,6 +405,11 @@ const openColorPopup = () => { | ||
| 398 | showColorPopup.value = true | 405 | showColorPopup.value = true |
| 399 | } | 406 | } |
| 400 | 407 | ||
| 408 | +const handleAddTrain = async () => { | ||
| 409 | + await loaddailytemplate(); | ||
| 410 | + // 更新主页面 | ||
| 411 | + emit('refreshCalendar'); | ||
| 412 | +} | ||
| 401 | // 当前正在显示的训记 | 413 | // 当前正在显示的训记 |
| 402 | const currentPlan = computed(() => { | 414 | const currentPlan = computed(() => { |
| 403 | if (!resdailyData.value.length) return null; | 415 | if (!resdailyData.value.length) return null; |
| @@ -672,6 +684,7 @@ const handleTrainAgain = async () => { | @@ -672,6 +684,7 @@ const handleTrainAgain = async () => { | ||
| 672 | if (res.code === 0) { | 684 | if (res.code === 0) { |
| 673 | uni.showToast({ title: '今日再练成功', icon: 'success' }); | 685 | uni.showToast({ title: '今日再练成功', icon: 'success' }); |
| 674 | loaddailytemplate(props.date); | 686 | loaddailytemplate(props.date); |
| 687 | + emit('refreshCalendar') | ||
| 675 | } else { | 688 | } else { |
| 676 | uni.showToast({ title: res.msg || '添加失败', icon: 'none' }); | 689 | uni.showToast({ title: res.msg || '添加失败', icon: 'none' }); |
| 677 | } | 690 | } |
| @@ -753,15 +766,45 @@ const handleMoveTo = async () => { | @@ -753,15 +766,45 @@ const handleMoveTo = async () => { | ||
| 753 | }); | 766 | }); |
| 754 | }; | 767 | }; |
| 755 | 768 | ||
| 769 | +// 复制到 | ||
| 770 | +const openCopyCalendarPopup = (templateId) => { | ||
| 771 | + if (!templateId) return uni.showToast({ title: '模板ID不存在', icon: 'none' }) | ||
| 772 | + console.log('currentPlan?.dailyTemplateId=', currentPlan?.dailyTemplateId); | ||
| 773 | + isCopyMode.value = true | ||
| 774 | + isMoveMode.value = false // 清空移动标记,互斥 | ||
| 775 | + moreShow.value = false | ||
| 776 | + nextTick(() => { | ||
| 777 | + calendarPopupRef.value.open(); | ||
| 778 | + }); | ||
| 779 | +} | ||
| 780 | + | ||
| 756 | const handleCalendarSuccess = () => { | 781 | const handleCalendarSuccess = () => { |
| 757 | - console.log('✅ 添加日历成功,当前模式:', isMoveMode.value ? '移动' : '复制'); | ||
| 758 | - if (!isMoveMode.value) { | 782 | + // console.log('✅ 添加日历成功,当前模式:', isMoveMode.value ? '移动' : '复制'); |
| 783 | + // 复制模式:只提示成功,不用删原数据 | ||
| 784 | + // if (!isMoveMode.value) { | ||
| 785 | + // uni.showToast({ | ||
| 786 | + // title: '复制成功', | ||
| 787 | + // icon: 'success' | ||
| 788 | + // }); | ||
| 789 | + // return; | ||
| 790 | + // } | ||
| 791 | + console.log('✅ 添加日历成功', { | ||
| 792 | + move: isMoveMode.value, | ||
| 793 | + copy: isCopyMode.value | ||
| 794 | + }); | ||
| 795 | + | ||
| 796 | + // 复制模式:仅刷新日历,不删除原数据 | ||
| 797 | + if (isCopyMode.value) { | ||
| 759 | uni.showToast({ | 798 | uni.showToast({ |
| 760 | title: '复制成功', | 799 | title: '复制成功', |
| 761 | icon: 'success' | 800 | icon: 'success' |
| 762 | }); | 801 | }); |
| 802 | + // 重置标记 | ||
| 803 | + isCopyMode.value = false | ||
| 804 | + emit('refreshCalendar') | ||
| 763 | return; | 805 | return; |
| 764 | } | 806 | } |
| 807 | + // 移动模式:子组件已把数据新增到新日期,这里删除原日期的训练记录 | ||
| 765 | if (!currentPlan.value?.id) { | 808 | if (!currentPlan.value?.id) { |
| 766 | return; | 809 | return; |
| 767 | } | 810 | } |
| @@ -769,6 +812,7 @@ const handleCalendarSuccess = () => { | @@ -769,6 +812,7 @@ const handleCalendarSuccess = () => { | ||
| 769 | .then(() => { | 812 | .then(() => { |
| 770 | uni.showToast({ title: '移动成功', icon: 'success' }); | 813 | uni.showToast({ title: '移动成功', icon: 'success' }); |
| 771 | loaddailytemplate(selectedDate.value); | 814 | loaddailytemplate(selectedDate.value); |
| 815 | + emit('refreshCalendar') | ||
| 772 | }) | 816 | }) |
| 773 | .catch(err => { | 817 | .catch(err => { |
| 774 | uni.showToast({ title: '移动失败', icon: 'none' }); | 818 | uni.showToast({ title: '移动失败', icon: 'none' }); |
| @@ -776,6 +820,11 @@ const handleCalendarSuccess = () => { | @@ -776,6 +820,11 @@ const handleCalendarSuccess = () => { | ||
| 776 | isMoveMode.value = false | 820 | isMoveMode.value = false |
| 777 | }; | 821 | }; |
| 778 | 822 | ||
| 823 | +const calendarColorPickerSuccess = () => { | ||
| 824 | + loaddailytemplate(selectedDate.value) | ||
| 825 | + emit('refreshCalendar') | ||
| 826 | +} | ||
| 827 | + | ||
| 779 | // 去训练 | 828 | // 去训练 |
| 780 | const startTraining = () => { | 829 | const startTraining = () => { |
| 781 | // console.log('++点击了去训练++'); | 830 | // console.log('++点击了去训练++'); |
| @@ -1068,6 +1117,14 @@ onMounted(() => { | @@ -1068,6 +1117,14 @@ onMounted(() => { | ||
| 1068 | .set-content { | 1117 | .set-content { |
| 1069 | flex-grow: 1; | 1118 | flex-grow: 1; |
| 1070 | color: inherit; | 1119 | color: inherit; |
| 1120 | + display: flex; | ||
| 1121 | + justify-content: space-between; | ||
| 1122 | + white-space: nowrap; | ||
| 1123 | + | ||
| 1124 | +} | ||
| 1125 | + | ||
| 1126 | +.detai-data { | ||
| 1127 | + white-space: nowrap; | ||
| 1071 | } | 1128 | } |
| 1072 | 1129 | ||
| 1073 | // 主卡片:横向排列 | 1130 | // 主卡片:横向排列 |
| @@ -1153,6 +1210,11 @@ onMounted(() => { | @@ -1153,6 +1210,11 @@ onMounted(() => { | ||
| 1153 | /* 控制 indexData、set-content、rest-time 之间的间距 */ | 1210 | /* 控制 indexData、set-content、rest-time 之间的间距 */ |
| 1154 | } | 1211 | } |
| 1155 | 1212 | ||
| 1213 | +.set-right { | ||
| 1214 | + display: flex; | ||
| 1215 | + gap: 20rpx; | ||
| 1216 | +} | ||
| 1217 | + | ||
| 1156 | // 灰色小圆点序号 | 1218 | // 灰色小圆点序号 |
| 1157 | .indexData { | 1219 | .indexData { |
| 1158 | width: 32rpx; | 1220 | width: 32rpx; |
| @@ -372,18 +372,30 @@ const saveEdit = async () => { | @@ -372,18 +372,30 @@ const saveEdit = async () => { | ||
| 372 | if (unit.unitType === 1) { | 372 | if (unit.unitType === 1) { |
| 373 | // 单动作:exercises只有1个 | 373 | // 单动作:exercises只有1个 |
| 374 | const exItem = targetExercises[0] | 374 | const exItem = targetExercises[0] |
| 375 | + const exerciseType = exItem.exerciseType; | ||
| 376 | + console.log('----------exerciseType---------', exerciseType); | ||
| 375 | // expose出来的recordList | 377 | // expose出来的recordList |
| 376 | const newSets = dongzuoSingleRef.value.recordList.map((item, idx) => { | 378 | const newSets = dongzuoSingleRef.value.recordList.map((item, idx) => { |
| 377 | // 组件数据转回后端sets结构(和原页面save组装逻辑一致) | 379 | // 组件数据转回后端sets结构(和原页面save组装逻辑一致) |
| 378 | - const duration = Number(item.h) * 3600 + Number(item.m) * 60 + Number(item.s) || 0 | ||
| 379 | - const rest = Number(item.quickTimeDisplay.replace('s', '')) || 0 | 380 | + let duration = 0 |
| 381 | + let restTime = 0 | ||
| 382 | + // 间歇动作单独取值 | ||
| 383 | + if (exerciseType === 6) { | ||
| 384 | + duration = Number(item.duration) || 0 | ||
| 385 | + restTime = Number(item.restTime) || 0 | ||
| 386 | + } else { | ||
| 387 | + // 普通动作时分秒换算 | ||
| 388 | + duration = Number(item.h) * 3600 + Number(item.m) * 60 + Number(item.s) || 0 | ||
| 389 | + restTime = Number(item.quickTimeDisplay.replace('s', '')) || 0 | ||
| 390 | + } | ||
| 380 | return { | 391 | return { |
| 381 | setIndex: idx + 1, | 392 | setIndex: idx + 1, |
| 382 | weight: Number(item.weight) || 0, | 393 | weight: Number(item.weight) || 0, |
| 383 | reps: Number(item.reps) || 0, | 394 | reps: Number(item.reps) || 0, |
| 384 | distance: Number(item.distance) || 0, | 395 | distance: Number(item.distance) || 0, |
| 385 | duration, | 396 | duration, |
| 386 | - restTime: rest, | 397 | + // restTime: rest, |
| 398 | + restTime, | ||
| 387 | isCompleted: item.isActive ? 1 : 0 | 399 | isCompleted: item.isActive ? 1 : 0 |
| 388 | } | 400 | } |
| 389 | }) | 401 | }) |
| @@ -392,17 +404,30 @@ const saveEdit = async () => { | @@ -392,17 +404,30 @@ const saveEdit = async () => { | ||
| 392 | // 超级组:superRecordMap{exId:[]} | 404 | // 超级组:superRecordMap{exId:[]} |
| 393 | const superMap = dongzuoSingleRef.value.superRecordMap | 405 | const superMap = dongzuoSingleRef.value.superRecordMap |
| 394 | targetExercises.forEach(ex => { | 406 | targetExercises.forEach(ex => { |
| 407 | + // 获得当前超级组的动作类型 | ||
| 408 | + const exerciseType = ex.exerciseType; | ||
| 409 | + console.log('----------exerciseType---------', exerciseType); | ||
| 410 | + | ||
| 395 | const setArr = superMap[ex.exerciseId] || [] | 411 | const setArr = superMap[ex.exerciseId] || [] |
| 396 | ex.sets = setArr.map((item, idx) => { | 412 | ex.sets = setArr.map((item, idx) => { |
| 397 | - const duration = Number(item.h) * 3600 + Number(item.m) * 60 + Number(item.s) || 0 | ||
| 398 | - const rest = Number(item.quickTimeDisplay.replace('s', '')) || 0 | 413 | + let duration = 0 |
| 414 | + let restTime = 0 | ||
| 415 | + // 子动作是间歇则取专属字段 | ||
| 416 | + if (exerciseType === 6) { | ||
| 417 | + duration = Number(item.duration) || 0 | ||
| 418 | + restTime = Number(item.restTime) || 0 | ||
| 419 | + } else { | ||
| 420 | + duration = Number(item.h) * 3600 + Number(item.m) * 60 + Number(item.s) || 0 | ||
| 421 | + restTime = Number(item.quickTimeDisplay.replace('s', '')) || 0 | ||
| 422 | + } | ||
| 399 | return { | 423 | return { |
| 400 | setIndex: idx + 1, | 424 | setIndex: idx + 1, |
| 401 | weight: Number(item.weight) || 0, | 425 | weight: Number(item.weight) || 0, |
| 402 | reps: Number(item.reps) || 0, | 426 | reps: Number(item.reps) || 0, |
| 403 | distance: Number(item.distance) || 0, | 427 | distance: Number(item.distance) || 0, |
| 404 | duration, | 428 | duration, |
| 405 | - restTime: rest, | 429 | + // restTime: rest, |
| 430 | + restTime, | ||
| 406 | isCompleted: item.isActive ? 1 : 0 | 431 | isCompleted: item.isActive ? 1 : 0 |
| 407 | } | 432 | } |
| 408 | }) | 433 | }) |
| @@ -473,7 +498,7 @@ defineExpose({ open }) | @@ -473,7 +498,7 @@ defineExpose({ open }) | ||
| 473 | // padding: 0 20rpx 120rpx; | 498 | // padding: 0 20rpx 120rpx; |
| 474 | margin: 0 20rpx; | 499 | margin: 0 20rpx; |
| 475 | height: calc(100vh - 120rpx); | 500 | height: calc(100vh - 120rpx); |
| 476 | - background: #f5f5f5; | 501 | + background: #242424; |
| 477 | max-height: 70vh; | 502 | max-height: 70vh; |
| 478 | } | 503 | } |
| 479 | </style> | 504 | </style> |
| @@ -43,6 +43,7 @@ | @@ -43,6 +43,7 @@ | ||
| 43 | <script setup> | 43 | <script setup> |
| 44 | import { ref, computed, watch } from 'vue'; | 44 | import { ref, computed, watch } from 'vue'; |
| 45 | import QueryPlanApi from '@/sheep/api/plan/queryplan'; | 45 | import QueryPlanApi from '@/sheep/api/plan/queryplan'; |
| 46 | +import dailytemplateApi from '@/sheep/api/Template/Dailytemplate'; | ||
| 46 | 47 | ||
| 47 | const props = defineProps({ | 48 | const props = defineProps({ |
| 48 | planId: { type: [String, Number], required: false }, | 49 | planId: { type: [String, Number], required: false }, |
| @@ -50,9 +51,11 @@ const props = defineProps({ | @@ -50,9 +51,11 @@ const props = defineProps({ | ||
| 50 | isMove: { | 51 | isMove: { |
| 51 | type: Boolean, | 52 | type: Boolean, |
| 52 | default: false | 53 | default: false |
| 53 | - } | 54 | + }, |
| 55 | + isCopy: { type: Boolean, default: false }, | ||
| 56 | + dailyTemplateId: { type: Number, default: 0 } | ||
| 54 | }); | 57 | }); |
| 55 | -const emit = defineEmits(['success']); | 58 | +const emit = defineEmits(['success', 'clearCopyFlag']); |
| 56 | 59 | ||
| 57 | // 弹窗显示控制 | 60 | // 弹窗显示控制 |
| 58 | const showPopup = ref(false); | 61 | const showPopup = ref(false); |
| @@ -174,6 +177,7 @@ const close = () => { | @@ -174,6 +177,7 @@ const close = () => { | ||
| 174 | // 重置到当前月份(可选) | 177 | // 重置到当前月份(可选) |
| 175 | currentYear.value = new Date().getFullYear(); | 178 | currentYear.value = new Date().getFullYear(); |
| 176 | currentMonth.value = new Date().getMonth() + 1; | 179 | currentMonth.value = new Date().getMonth() + 1; |
| 180 | + emit('clearCopyFlag') | ||
| 177 | }; | 181 | }; |
| 178 | 182 | ||
| 179 | // 打开弹窗(暴露给父组件) | 183 | // 打开弹窗(暴露给父组件) |
| @@ -192,27 +196,56 @@ const confirmAdd = async () => { | @@ -192,27 +196,56 @@ const confirmAdd = async () => { | ||
| 192 | return; | 196 | return; |
| 193 | } | 197 | } |
| 194 | try { | 198 | try { |
| 195 | - if (props.isMove) { | 199 | + // if (props.isMove) { |
| 200 | + // const params = { | ||
| 201 | + // id: props.templateId, | ||
| 202 | + // trainingDate: selectedDates.value[0] || '' | ||
| 203 | + // }; | ||
| 204 | + // // 添加到日历弹窗的参数 | ||
| 205 | + // console.log('移动到:params', params); | ||
| 206 | + // await QueryPlanApi.updateDailyTemplateDate(params); | ||
| 207 | + // uni.showToast({ title: '移动成功', icon: 'success' }); | ||
| 208 | + // } else { | ||
| 209 | + // const params = { | ||
| 210 | + // id: props.templateId, | ||
| 211 | + // planId: props.planId, | ||
| 212 | + // trainDateList: selectedDates.value | ||
| 213 | + // }; | ||
| 214 | + // // 添加到日历弹窗的参数 | ||
| 215 | + // console.log('添加到params', params); | ||
| 216 | + // await QueryPlanApi.addPlanToCalendar(params); | ||
| 217 | + // uni.showToast({ title: '添加到日历成功', icon: 'success' }); | ||
| 218 | + // } | ||
| 219 | + // ========== 新增复制分支 优先判断 ========== | ||
| 220 | + console.log('rops.dailyTemplateId=', props.dailyTemplateId); | ||
| 221 | + if (props.isCopy) { | ||
| 222 | + // 调用你新增的复制接口 | ||
| 223 | + await dailytemplateApi.copyTempleteDailyTemplate(props.dailyTemplateId, selectedDates.value) | ||
| 224 | + uni.showToast({ title: '复制成功', icon: 'success' }); | ||
| 225 | + console.log('调用复制接口成功'); | ||
| 226 | + | ||
| 227 | + } else if (props.isMove) { | ||
| 228 | + // 移动到 | ||
| 196 | const params = { | 229 | const params = { |
| 197 | id: props.templateId, | 230 | id: props.templateId, |
| 198 | trainingDate: selectedDates.value[0] || '' | 231 | trainingDate: selectedDates.value[0] || '' |
| 199 | }; | 232 | }; |
| 200 | - // 添加到日历弹窗的参数 | ||
| 201 | - console.log('移动到:params', params); | ||
| 202 | await QueryPlanApi.updateDailyTemplateDate(params); | 233 | await QueryPlanApi.updateDailyTemplateDate(params); |
| 203 | uni.showToast({ title: '移动成功', icon: 'success' }); | 234 | uni.showToast({ title: '移动成功', icon: 'success' }); |
| 235 | + console.log('移动接口成功'); | ||
| 204 | } else { | 236 | } else { |
| 237 | + //添加到日历 | ||
| 205 | const params = { | 238 | const params = { |
| 206 | id: props.templateId, | 239 | id: props.templateId, |
| 207 | planId: props.planId, | 240 | planId: props.planId, |
| 208 | trainDateList: selectedDates.value | 241 | trainDateList: selectedDates.value |
| 209 | }; | 242 | }; |
| 210 | - // 添加到日历弹窗的参数 | ||
| 211 | - console.log('添加到params', params); | ||
| 212 | await QueryPlanApi.addPlanToCalendar(params); | 243 | await QueryPlanApi.addPlanToCalendar(params); |
| 213 | uni.showToast({ title: '添加到日历成功', icon: 'success' }); | 244 | uni.showToast({ title: '添加到日历成功', icon: 'success' }); |
| 245 | + console.log('添加到接口成功'); | ||
| 214 | } | 246 | } |
| 215 | 247 | ||
| 248 | + | ||
| 216 | close(); | 249 | close(); |
| 217 | emit('success'); | 250 | emit('success'); |
| 218 | } catch (err) { | 251 | } catch (err) { |
| @@ -37,10 +37,10 @@ | @@ -37,10 +37,10 @@ | ||
| 37 | <scroll-view class="template-list" enable-flex scroll-y> | 37 | <scroll-view class="template-list" enable-flex scroll-y> |
| 38 | <!-- 用父容器包裹 v-if 分支,解决 key 冲突 --> | 38 | <!-- 用父容器包裹 v-if 分支,解决 key 冲突 --> |
| 39 | <view v-if="!isFiltering" class="sub-template-list"> | 39 | <view v-if="!isFiltering" class="sub-template-list"> |
| 40 | - <view v-for="(item, index) in templateList" :key="index" class="template-item"> | 40 | + <view v-for="(item, index) in templateList" :key="index" class="template-item" @click="gototemplate(item)"> |
| 41 | <image :src="item.urlCover" mode="aspectFill" class="template-img"></image> | 41 | <image :src="item.urlCover" mode="aspectFill" class="template-img"></image> |
| 42 | <view> | 42 | <view> |
| 43 | - <view class="template-content" @click="gototemplate(item)"> | 43 | + <view class="template-content"> |
| 44 | <view class="template-title">{{ item.name }}</view> | 44 | <view class="template-title">{{ item.name }}</view> |
| 45 | <view class="template-count">{{ item.templatesCount }}个模板</view> | 45 | <view class="template-count">{{ item.templatesCount }}个模板</view> |
| 46 | <view class="template-desc">{{ item.description }}</view> | 46 | <view class="template-desc">{{ item.description }}</view> |
| @@ -10,13 +10,13 @@ | @@ -10,13 +10,13 @@ | ||
| 10 | <view class="title">动作库</view> | 10 | <view class="title">动作库</view> |
| 11 | <view class="desc">真人视频讲解</view> | 11 | <view class="desc">真人视频讲解</view> |
| 12 | </view> | 12 | </view> |
| 13 | - <view class="item"> | 13 | + <view class="item" @click="goToTrainPlan"> |
| 14 | <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/22_1773626449583.png" | 14 | <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/22_1773626449583.png" |
| 15 | class="img"></image> | 15 | class="img"></image> |
| 16 | <view class="title">训练计划</view> | 16 | <view class="title">训练计划</view> |
| 17 | <view class="desc">周期性目标达成</view> | 17 | <view class="desc">周期性目标达成</view> |
| 18 | </view> | 18 | </view> |
| 19 | - <view class="item"> | 19 | + <view class="item" @click="goToTrainTemplate"> |
| 20 | <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/23_1773627239158.png" | 20 | <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/23_1773627239158.png" |
| 21 | class="img"></image> | 21 | class="img"></image> |
| 22 | <view class="title">训练模板</view> | 22 | <view class="title">训练模板</view> |
| @@ -122,7 +122,7 @@ | @@ -122,7 +122,7 @@ | ||
| 122 | </template> | 122 | </template> |
| 123 | 123 | ||
| 124 | <script setup> | 124 | <script setup> |
| 125 | -import { ref, computed, onMounted } from 'vue'; | 125 | +import { ref, computed, onMounted, getCurrentInstance } from 'vue'; |
| 126 | import QueryPlanApi from '@/sheep/api/plan/queryplan'; | 126 | import QueryPlanApi from '@/sheep/api/plan/queryplan'; |
| 127 | import coachUpdatesApi from '@/sheep/api/Coach/CoachUpdates'; // 请确认路径正确 | 127 | import coachUpdatesApi from '@/sheep/api/Coach/CoachUpdates'; // 请确认路径正确 |
| 128 | import useUserStore from '@/sheep/store/user'; | 128 | import useUserStore from '@/sheep/store/user'; |
| @@ -312,12 +312,55 @@ const handleDynamicLike = async (item, index) => { | @@ -312,12 +312,55 @@ const handleDynamicLike = async (item, index) => { | ||
| 312 | console.log('点赞失败', err); | 312 | console.log('点赞失败', err); |
| 313 | } | 313 | } |
| 314 | }; | 314 | }; |
| 315 | +// const goXunjiTab = (tabIndex) => { | ||
| 316 | +// uni.navigateTo({ | ||
| 317 | +// url: `/pages4/pages/xunji/xunji-main?currentTab=${tabIndex}` | ||
| 318 | +// }) | ||
| 319 | +// } | ||
| 320 | + | ||
| 321 | + | ||
| 315 | // 实现动作库跳转,跳转到F:\hongxing-app\hongxing-new\pages\xunji\components\xunji-dongzuo.vue | 322 | // 实现动作库跳转,跳转到F:\hongxing-app\hongxing-new\pages\xunji\components\xunji-dongzuo.vue |
| 323 | +// const goToActionLibrary = () => { | ||
| 324 | +// uni.navigateTo({ | ||
| 325 | +// url: '/pages/xunji/components/xunji-dongzuo', | ||
| 326 | +// }); | ||
| 327 | +// }; | ||
| 328 | + | ||
| 329 | +const instance = getCurrentInstance() | ||
| 330 | +// 获取父页面(pages/xunji/xunji.vue) | ||
| 331 | +const parentPage = instance.parent | ||
| 332 | + | ||
| 333 | +// 切换训记Tab内部子页面(适配底部tabBar) | ||
| 334 | +const switchXunjiTab = (tabIndex) => { | ||
| 335 | + console.log('准备跳转训记Tab,目标索引:', tabIndex); | ||
| 336 | + // 存入本地缓存,训记页面onShow读取 | ||
| 337 | + uni.setStorageSync('xunjiTargetTab', tabIndex); | ||
| 338 | + // 修正路径:去掉开头斜杠,匹配tabBar标准格式 | ||
| 339 | + uni.switchTab({ | ||
| 340 | + url: 'pages/xunji/xunji', | ||
| 341 | + success: () => { | ||
| 342 | + console.log('switchTab切换训记页面成功'); | ||
| 343 | + }, | ||
| 344 | + fail: (err) => { | ||
| 345 | + console.error('switchTab切换失败', err); | ||
| 346 | + uni.removeStorageSync('xunjiTargetTab'); | ||
| 347 | + uni.showToast({ title: '跳转失败,请检查tabBar配置', icon: 'none' }); | ||
| 348 | + } | ||
| 349 | + }) | ||
| 350 | +} | ||
| 316 | const goToActionLibrary = () => { | 351 | const goToActionLibrary = () => { |
| 317 | - uni.navigateTo({ | ||
| 318 | - url: '/pages/xunji/components/xunji-dongzuo', | ||
| 319 | - }); | ||
| 320 | -}; | 352 | + parentPage?.exposed?.switchTabIndex(3) |
| 353 | +} | ||
| 354 | + | ||
| 355 | +// 训练计划 | ||
| 356 | +const goToTrainPlan = () => { | ||
| 357 | + parentPage?.exposed?.switchTabIndex(1) | ||
| 358 | +} | ||
| 359 | +// 训练模板 | ||
| 360 | +const goToTrainTemplate = () => { | ||
| 361 | + parentPage?.exposed?.switchTabIndex(4) | ||
| 362 | +} | ||
| 363 | + | ||
| 321 | // 实现 | 364 | // 实现 |
| 322 | // 初始化 | 365 | // 初始化 |
| 323 | onMounted(() => { | 366 | onMounted(() => { |
| @@ -85,7 +85,7 @@ | @@ -85,7 +85,7 @@ | ||
| 85 | 85 | ||
| 86 | <script setup> | 86 | <script setup> |
| 87 | import { ref, shallowRef, computed, onMounted } from 'vue'; | 87 | import { ref, shallowRef, computed, onMounted } from 'vue'; |
| 88 | -import { onLoad, onHide } from '@dcloudio/uni-app'; | 88 | +import { onLoad, onHide, onShow } from '@dcloudio/uni-app'; |
| 89 | import useUserStore from '@/sheep/store/user'; | 89 | import useUserStore from '@/sheep/store/user'; |
| 90 | import { getMenuButtonHeight, getTopSafeArea } from '@/utils/safeArea'; | 90 | import { getMenuButtonHeight, getTopSafeArea } from '@/utils/safeArea'; |
| 91 | 91 | ||
| @@ -136,6 +136,18 @@ onLoad((options) => { | @@ -136,6 +136,18 @@ onLoad((options) => { | ||
| 136 | } | 136 | } |
| 137 | }); | 137 | }); |
| 138 | 138 | ||
| 139 | +onShow(() => { | ||
| 140 | + const targetTab = uni.getStorageSync('xunjiTargetTab'); | ||
| 141 | + if (targetTab !== '' && targetTab != null) { | ||
| 142 | + const tabNum = Number(targetTab); | ||
| 143 | + currentTab.value = tabNum; | ||
| 144 | + handleTabClick(tabNum); | ||
| 145 | + // 使用后清空缓存,避免下次进来自动跳转 | ||
| 146 | + uni.removeStorageSync('xunjiTargetTab'); | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | +}) | ||
| 150 | + | ||
| 139 | // --- 原有业务方法补充与修复 --- | 151 | // --- 原有业务方法补充与修复 --- |
| 140 | 152 | ||
| 141 | // 打开抽屉 | 153 | // 打开抽屉 |
| @@ -186,6 +198,10 @@ const goWidgetLib = () => { | @@ -186,6 +198,10 @@ const goWidgetLib = () => { | ||
| 186 | const goFeedback = () => { }; | 198 | const goFeedback = () => { }; |
| 187 | const goUserGroup = () => { }; | 199 | const goUserGroup = () => { }; |
| 188 | const goTutorial = () => { }; | 200 | const goTutorial = () => { }; |
| 201 | + | ||
| 202 | +defineExpose({ | ||
| 203 | + switchTabIndex: handleTabClick | ||
| 204 | +}) | ||
| 189 | </script> | 205 | </script> |
| 190 | <style scoped lang="scss"> | 206 | <style scoped lang="scss"> |
| 191 | .home-page { | 207 | .home-page { |
| @@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
| 19 | {{ TemplateDetail?.equipmentsUsed }} | 19 | {{ TemplateDetail?.equipmentsUsed }} |
| 20 | </template> | 20 | </template> |
| 21 | <template v-else> | 21 | <template v-else> |
| 22 | - {{ TemplateDetail?.equipmentNames.join(',') }} | 22 | + {{ (TemplateDetail?.equipmentNames || []).join(',') }} |
| 23 | </template> | 23 | </template> |
| 24 | 24 | ||
| 25 | </text> | 25 | </text> |
| @@ -35,14 +35,14 @@ | @@ -35,14 +35,14 @@ | ||
| 35 | <!-- 主要训练部位 --> | 35 | <!-- 主要训练部位 --> |
| 36 | <text class="section-title">主要训练部位: </text> | 36 | <text class="section-title">主要训练部位: </text> |
| 37 | <text class="description"> | 37 | <text class="description"> |
| 38 | - {{ TemplateDetail.primaryMuscleNames?.join(', ') || '暂无数据' }} | 38 | + {{ (TemplateDetail.primaryMuscleNames || []).join(', ') || '暂无数据' }} |
| 39 | </text> | 39 | </text> |
| 40 | <!-- 换行 --> | 40 | <!-- 换行 --> |
| 41 | <view class="divider"></view> | 41 | <view class="divider"></view> |
| 42 | <!-- 次要训练部位 --> | 42 | <!-- 次要训练部位 --> |
| 43 | <text class="section-title">次要训练部位: </text> | 43 | <text class="section-title">次要训练部位: </text> |
| 44 | <text class="description"> | 44 | <text class="description"> |
| 45 | - {{ TemplateDetail.secondaryMuscleNames?.join(', ') || '暂无数据' }} | 45 | + {{ (TemplateDetail.secondaryMuscleNames || []).join(', ') || '暂无数据' }} |
| 46 | </text> | 46 | </text> |
| 47 | </view> | 47 | </view> |
| 48 | </view> | 48 | </view> |
| @@ -89,43 +89,6 @@ | @@ -89,43 +89,6 @@ | ||
| 89 | 89 | ||
| 90 | </view> | 90 | </view> |
| 91 | 91 | ||
| 92 | - <!-- 动作内容循环:只循环内容,不循环卡片 --> | ||
| 93 | - <!-- <view v-for="(item, index) in unit.exercises" :key="index" class="exercise-item-inner"> | ||
| 94 | - <view class="exercise-header"> | ||
| 95 | - <view class="exercise-info"> | ||
| 96 | - <template v-if="unit.unitType === 2"> | ||
| 97 | - <text class="super-exercise-name">{{ item.exerciseName }}</text> | ||
| 98 | - </template> | ||
| 99 | - </view> | ||
| 100 | - </view> | ||
| 101 | - | ||
| 102 | - <view v-if="item.sets && item.sets.length > 0" class="exercise-details"> | ||
| 103 | - <view v-for="(detail, i) in item.sets" :key="i" class="detail-row"> | ||
| 104 | - <view class="detail-label">{{ i + 1 }}</view> | ||
| 105 | - <view class="sets-data"> | ||
| 106 | - <view class="detail-value" v-if="[0, 4, 5].includes(item.exerciseType)"> | ||
| 107 | - {{ detail.weight }}kg × {{ detail.reps }}次 | ||
| 108 | - </view> | ||
| 109 | - <view class="detail-value" v-if="item.exerciseType === 1"> | ||
| 110 | - {{ detail.distance }}km x {{ formatSeconds(detail.duration) }} | ||
| 111 | - </view> | ||
| 112 | - <view class="detail-value" v-if="item.exerciseType === 2"> | ||
| 113 | - {{ detail.reps }}次 | ||
| 114 | - </view> | ||
| 115 | - <view class="detail-value" v-if="item.exerciseType === 3"> | ||
| 116 | - {{ detail.duration }}次 | ||
| 117 | - </view> | ||
| 118 | - <view class="detail-value" v-if="item.exerciseType === 6"> | ||
| 119 | - {{ detail.duration }}组 x {{ formatSeconds(detail.duration) }} | ||
| 120 | - </view> | ||
| 121 | - <view class="rest" v-if="detail.restTime"> | ||
| 122 | - {{ detail.restTime }}s | ||
| 123 | - </view> | ||
| 124 | - </view> | ||
| 125 | - </view> | ||
| 126 | - </view> | ||
| 127 | - </view> --> | ||
| 128 | - | ||
| 129 | <!-- 情况1:普通动作 unitType = 1 --> | 92 | <!-- 情况1:普通动作 unitType = 1 --> |
| 130 | <view v-if="unit.unitType === 1 && unit.exercises && unit.exercises.length" class="normal-action-wrap"> | 93 | <view v-if="unit.unitType === 1 && unit.exercises && unit.exercises.length" class="normal-action-wrap"> |
| 131 | <!-- 取第一个动作(普通动作unit里永远只有1个exercise) --> | 94 | <!-- 取第一个动作(普通动作unit里永远只有1个exercise) --> |
| @@ -238,7 +201,7 @@ | @@ -238,7 +201,7 @@ | ||
| 238 | <uni-icons type="refresh" size="22" color="#fff"></uni-icons> | 201 | <uni-icons type="refresh" size="22" color="#fff"></uni-icons> |
| 239 | <text class="item-text">删除此次排课</text> | 202 | <text class="item-text">删除此次排课</text> |
| 240 | </view> | 203 | </view> |
| 241 | - <view class="more-item" @click.stop="confirmDelete(plandetail.groupId)"> | 204 | + <view class="more-item" @click.stop="confirmDelete(TemplateDetail.planId)"> |
| 242 | <uni-icons type="close" size="22" color="#fff"></uni-icons> | 205 | <uni-icons type="close" size="22" color="#fff"></uni-icons> |
| 243 | <text class="item-text">结束整个计划</text> | 206 | <text class="item-text">结束整个计划</text> |
| 244 | </view> | 207 | </view> |
| @@ -356,7 +319,7 @@ const loadtemplatedetail = async () => { | @@ -356,7 +319,7 @@ const loadtemplatedetail = async () => { | ||
| 356 | } | 319 | } |
| 357 | TemplateDetail.value = res.data; | 320 | TemplateDetail.value = res.data; |
| 358 | TemplateUnits.value = res.data.units || []; | 321 | TemplateUnits.value = res.data.units || []; |
| 359 | - console.log('✅ 模板详情:', TemplateDetail.value); | 322 | + console.log('✅ TemplateDetail.value模板详情:', TemplateDetail.value); |
| 360 | console.log('✅ 模板unts详情:', TemplateUnits.value); | 323 | console.log('✅ 模板unts详情:', TemplateUnits.value); |
| 361 | 324 | ||
| 362 | } catch (err) { | 325 | } catch (err) { |
| @@ -486,6 +449,8 @@ const handleDeleteTemplate = async (dailyTemplateId) => { | @@ -486,6 +449,8 @@ const handleDeleteTemplate = async (dailyTemplateId) => { | ||
| 486 | await dailytemplateApi.deleteDailyTemplate(dailyTemplateId); | 449 | await dailytemplateApi.deleteDailyTemplate(dailyTemplateId); |
| 487 | uni.showToast({ title: '删除成功', icon: 'success' }); | 450 | uni.showToast({ title: '删除成功', icon: 'success' }); |
| 488 | // moreShow.value = false; // 关闭弹窗 | 451 | // moreShow.value = false; // 关闭弹窗 |
| 452 | + // 返回上一页 | ||
| 453 | + setTimeout(() => uni.navigateBack(), 800) | ||
| 489 | } catch (err) { | 454 | } catch (err) { |
| 490 | console.error('删除失败', err); | 455 | console.error('删除失败', err); |
| 491 | uni.showToast({ title: '删除失败,请重试', icon: 'none' }); | 456 | uni.showToast({ title: '删除失败,请重试', icon: 'none' }); |
| @@ -493,7 +458,7 @@ const handleDeleteTemplate = async (dailyTemplateId) => { | @@ -493,7 +458,7 @@ const handleDeleteTemplate = async (dailyTemplateId) => { | ||
| 493 | } | 458 | } |
| 494 | }); | 459 | }); |
| 495 | }; | 460 | }; |
| 496 | - | 461 | +// 删除整个个人计划 |
| 497 | const confirmDelete = async (id) => { | 462 | const confirmDelete = async (id) => { |
| 498 | try { | 463 | try { |
| 499 | console.log('打印计划个人id:', id); | 464 | console.log('打印计划个人id:', id); |
| @@ -503,7 +468,7 @@ const confirmDelete = async (id) => { | @@ -503,7 +468,7 @@ const confirmDelete = async (id) => { | ||
| 503 | 468 | ||
| 504 | if (res.code === 0 && res.data) { | 469 | if (res.code === 0 && res.data) { |
| 505 | // 2. 删除成功,从列表移除 | 470 | // 2. 删除成功,从列表移除 |
| 506 | - planList.value = planList.value.filter(item => item.id !== id); | 471 | + // planList.value = planList.value.filter(item => item.id !== id); |
| 507 | uni.showToast({ title: '计划已结束', icon: 'success' }); | 472 | uni.showToast({ title: '计划已结束', icon: 'success' }); |
| 508 | } else { | 473 | } else { |
| 509 | uni.showToast({ title: res.msg || '操作失败', icon: 'none' }); | 474 | uni.showToast({ title: res.msg || '操作失败', icon: 'none' }); |
| @@ -512,8 +477,25 @@ const confirmDelete = async (id) => { | @@ -512,8 +477,25 @@ const confirmDelete = async (id) => { | ||
| 512 | console.error('删除计划失败:', err); | 477 | console.error('删除计划失败:', err); |
| 513 | uni.showToast({ title: '网络请求失败', icon: 'none' }); | 478 | uni.showToast({ title: '网络请求失败', icon: 'none' }); |
| 514 | } finally { | 479 | } finally { |
| 515 | - // 3. 关闭弹窗 | ||
| 516 | - activePlanId.value = null; | 480 | + { |
| 481 | + setTimeout(() => { | ||
| 482 | + // 获取页面栈 | ||
| 483 | + const pages = getCurrentPages(); | ||
| 484 | + // 这里返回上上个(我的计划列表页面) | ||
| 485 | + const backNum = pages.length - 2; | ||
| 486 | + if (backNum > 0) { | ||
| 487 | + // 回退对应层数 | ||
| 488 | + uni.navigateBack({ | ||
| 489 | + delta: backNum | ||
| 490 | + }); | ||
| 491 | + } else { | ||
| 492 | + // 兜底:页面栈不足时防止报错,直接返回我的计划列表页面 | ||
| 493 | + uni.navigateTo({ | ||
| 494 | + url: '/pages4/pages/xunji/xunji-wode-jihua' | ||
| 495 | + }) | ||
| 496 | + } | ||
| 497 | + }, 1500) | ||
| 498 | + } | ||
| 517 | } | 499 | } |
| 518 | }; | 500 | }; |
| 519 | 501 |
| @@ -6,7 +6,7 @@ | @@ -6,7 +6,7 @@ | ||
| 6 | <view class="banner-section"> | 6 | <view class="banner-section"> |
| 7 | <image :src="plandetail.urlCover" class="banner-img"> | 7 | <image :src="plandetail.urlCover" class="banner-img"> |
| 8 | <!-- 收藏 --> | 8 | <!-- 收藏 --> |
| 9 | - <uni-icons :type="isPlanAdded ? 'heart-filled' : 'heart'" size="24" color="#fff" class="collect-btn" | 9 | + <uni-icons :type="isFavorite ? 'heart-filled' : 'heart'" size="24" color="#fff" class="collect-btn" |
| 10 | @click="toggleAddPlan"> | 10 | @click="toggleAddPlan"> |
| 11 | </uni-icons> | 11 | </uni-icons> |
| 12 | <!-- 分享按钮 --> | 12 | <!-- 分享按钮 --> |
| @@ -126,8 +126,9 @@ | @@ -126,8 +126,9 @@ | ||
| 126 | 126 | ||
| 127 | <!-- 按钮区域 --> | 127 | <!-- 按钮区域 --> |
| 128 | <view class="button-group"> | 128 | <view class="button-group"> |
| 129 | - <template v-if="!plandetail.isAdded && !isMyPlan"> | ||
| 130 | - <button type="default" @click="showPlanPopup = true" class="btn" size="large" | 129 | + <template v-if="!isPlanAdded && !isMyPlan"> |
| 130 | + <!-- 官方计划的底部按钮(未添加) --> | ||
| 131 | + <button type="default" @click="openTrainingPopup" class="btn" size="large" | ||
| 131 | style="background-color: white; flex: 1"> | 132 | style="background-color: white; flex: 1"> |
| 132 | 单节课程训练 | 133 | 单节课程训练 |
| 133 | </button> | 134 | </button> |
| @@ -136,18 +137,20 @@ | @@ -136,18 +137,20 @@ | ||
| 136 | 加入计划 | 137 | 加入计划 |
| 137 | </button> | 138 | </button> |
| 138 | </template> | 139 | </template> |
| 139 | - <template v-else-if="plandetail.isAdded && !isMyPlan"> | 140 | + <!-- 官方计划已添加 --> |
| 141 | + <template v-else-if="isPlanAdded && !isMyPlan"> | ||
| 140 | <button type="default" @click.stop="confirmDelete(plandetail.id)" class="btn end" size="large" | 142 | <button type="default" @click.stop="confirmDelete(plandetail.id)" class="btn end" size="large" |
| 141 | style="background-color: white; flex: 1"> | 143 | style="background-color: white; flex: 1"> |
| 142 | 结束计划 | 144 | 结束计划 |
| 143 | </button> | 145 | </button> |
| 144 | - <button type="default" @click="showPlanPopup = true" class="btn ones" size="large" | 146 | + <button type="default" @click="openTrainingPopup" class="btn ones" size="large" |
| 145 | style="background-color: #ffd700; flex: 1.5"> | 147 | style="background-color: #ffd700; flex: 1.5"> |
| 146 | 马上开练 | 148 | 马上开练 |
| 147 | </button> | 149 | </button> |
| 148 | </template> | 150 | </template> |
| 151 | + <!-- 个人计划 --> | ||
| 149 | <template v-else> | 152 | <template v-else> |
| 150 | - <button type="default" class="btn end" @click="showPlanPopup = true" size="large" | 153 | + <button type="default" class="btn end" @click="openTrainingPopup" size="large" |
| 151 | style="background-color: white; flex: 1"> | 154 | style="background-color: white; flex: 1"> |
| 152 | 马上开练 | 155 | 马上开练 |
| 153 | </button> | 156 | </button> |
| @@ -176,18 +179,18 @@ | @@ -176,18 +179,18 @@ | ||
| 176 | </view> | 179 | </view> |
| 177 | </template> | 180 | </template> |
| 178 | </view> | 181 | </view> |
| 179 | - <!-- 加入计划的弹窗 --> | 182 | + <!-- 加入计划/重新开始计划的弹窗 --> |
| 180 | <WeekSelectPopup v-model:visible="showWeekPopup" :max-count="plandetail.frequencyPerWeek" :plan-detail="plandetail" | 183 | <WeekSelectPopup v-model:visible="showWeekPopup" :max-count="plandetail.frequencyPerWeek" :plan-detail="plandetail" |
| 181 | - @success="loadDetail" :is-update="isUpdate" /> | 184 | + @success="loadDetailUpdate" :is-update="isUpdate" /> |
| 182 | 185 | ||
| 183 | <!-- 添加到日历弹窗子组件 --> | 186 | <!-- 添加到日历弹窗子组件 --> |
| 184 | <AddToCalendarPopup ref="calendarPopupRef" :plan-id="plandetail.id" :template-id="currentTemplateId" | 187 | <AddToCalendarPopup ref="calendarPopupRef" :plan-id="plandetail.id" :template-id="currentTemplateId" |
| 185 | @success="handleCalendarSuccess" /> | 188 | @success="handleCalendarSuccess" /> |
| 186 | 189 | ||
| 187 | - <!-- 去训练弹窗,显示当前计划的所有模板,可以直接跳转到训练页面 --> | 190 | + <!-- 去训练弹窗,显示当前计划的所有模板,可以直接跳转到训练页面,区分个人计划和官方计划(传递的是官方的计划id) --> |
| 188 | <template v-if="plandetail?.id"> | 191 | <template v-if="plandetail?.id"> |
| 189 | <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false" | 192 | <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false" |
| 190 | - :plan-title="'选择课程立即开始训练'" :plan-id="plandetail.id" :is-my-plan="false"/> | 193 | + :plan-title="'选择课程立即开始训练'" :plan-id="trainingPlanId" :is-my-plan="false" /> |
| 191 | </template> | 194 | </template> |
| 192 | 195 | ||
| 193 | </view> | 196 | </view> |
| @@ -211,8 +214,7 @@ const route = defineProps(['planid']); // Vue 3 + uni-app 支持 | @@ -211,8 +214,7 @@ const route = defineProps(['planid']); // Vue 3 + uni-app 支持 | ||
| 211 | const pages = getCurrentPages(); | 214 | const pages = getCurrentPages(); |
| 212 | const currentPage = pages[pages.length - 1]; | 215 | const currentPage = pages[pages.length - 1]; |
| 213 | const planIdFromUrl = currentPage.options?.planid; | 216 | const planIdFromUrl = currentPage.options?.planid; |
| 214 | -// 计划是否已添加(收藏) | ||
| 215 | -const isPlanAdded = ref(false); | 217 | + |
| 216 | // const plandetail = ref([]); | 218 | // const plandetail = ref([]); |
| 217 | const plandetail = ref({}); | 219 | const plandetail = ref({}); |
| 218 | //传参 | 220 | //传参 |
| @@ -248,23 +250,6 @@ const showPlanPopup = ref(false) | @@ -248,23 +250,6 @@ const showPlanPopup = ref(false) | ||
| 248 | // 更多弹窗显隐 | 250 | // 更多弹窗显隐 |
| 249 | const showMorePopup = ref(false) | 251 | const showMorePopup = ref(false) |
| 250 | 252 | ||
| 251 | - | ||
| 252 | -// 接口weeklySchedule转日历渲染数据 | ||
| 253 | -// const formatWeekData = (weekArr) => { | ||
| 254 | -// const weekMap = { 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '日' } | ||
| 255 | -// return weekArr.map((item, index) => { | ||
| 256 | -// const dayNum = item.date[2] | ||
| 257 | -// let topStr = index === 0 ? '今' : weekMap[item.dayIndex] | ||
| 258 | -// const trainStr = item.scheduled ? item.templateName : '' | ||
| 259 | -// return { | ||
| 260 | -// topText: topStr, | ||
| 261 | -// num: String(dayNum), | ||
| 262 | -// trainName: trainStr, | ||
| 263 | -// templateId: item.templateId, | ||
| 264 | -// dailytemplateId: item.dailytemplateId, | ||
| 265 | -// } | ||
| 266 | -// }) | ||
| 267 | -// } | ||
| 268 | // 接口weeklySchedule转日历渲染数据(修复版:星期根据真实日期计算,100%对齐num) | 253 | // 接口weeklySchedule转日历渲染数据(修复版:星期根据真实日期计算,100%对齐num) |
| 269 | const formatWeekData = (weekArr) => { | 254 | const formatWeekData = (weekArr) => { |
| 270 | // JS星期:0=日,1=一,2=二,3=三,4=四,5=五,6=六 | 255 | // JS星期:0=日,1=一,2=二,3=三,4=四,5=五,6=六 |
| @@ -304,6 +289,17 @@ const restartPlan = () => { | @@ -304,6 +289,17 @@ const restartPlan = () => { | ||
| 304 | console.log('重新开始计划,planId:', plandetail.value.id) | 289 | console.log('重新开始计划,planId:', plandetail.value.id) |
| 305 | uni.showToast({ title: '已重新开始计划', icon: 'success' }) | 290 | uni.showToast({ title: '已重新开始计划', icon: 'success' }) |
| 306 | } | 291 | } |
| 292 | +const trainingPlanId = ref(0) | ||
| 293 | +const openTrainingPopup = () => { | ||
| 294 | + showPlanPopup.value = true; | ||
| 295 | + if (isMyPlan.value) { | ||
| 296 | + trainingPlanId.value = plandetail.value.planId | ||
| 297 | + | ||
| 298 | + } else { | ||
| 299 | + trainingPlanId.value = plandetail.value.id | ||
| 300 | + } | ||
| 301 | + console.log('trainingPlanId=', trainingPlanId.value); | ||
| 302 | +} | ||
| 307 | 303 | ||
| 308 | // 跳转到排课页面 | 304 | // 跳转到排课页面 |
| 309 | const openArrageClass = () => { | 305 | const openArrageClass = () => { |
| @@ -365,10 +361,11 @@ const confirmDelete = async (id) => { | @@ -365,10 +361,11 @@ const confirmDelete = async (id) => { | ||
| 365 | 361 | ||
| 366 | if (res.code === 0 && res.data) { | 362 | if (res.code === 0 && res.data) { |
| 367 | uni.showToast({ title: '计划已结束', icon: 'success' }); | 363 | uni.showToast({ title: '计划已结束', icon: 'success' }); |
| 368 | - | ||
| 369 | - setTimeout(() => { | ||
| 370 | - uni.navigateBack() | ||
| 371 | - }, 1500) | 364 | + if (isMyPlan.value) { |
| 365 | + setTimeout(() => { | ||
| 366 | + uni.navigateBack() | ||
| 367 | + }, 1500) | ||
| 368 | + } | ||
| 372 | } else { | 369 | } else { |
| 373 | uni.showToast({ title: res.msg || '操作失败', icon: 'none' }); | 370 | uni.showToast({ title: res.msg || '操作失败', icon: 'none' }); |
| 374 | } | 371 | } |
| @@ -376,7 +373,10 @@ const confirmDelete = async (id) => { | @@ -376,7 +373,10 @@ const confirmDelete = async (id) => { | ||
| 376 | console.error('删除计划失败:', err); | 373 | console.error('删除计划失败:', err); |
| 377 | uni.showToast({ title: '网络请求失败', icon: 'none' }); | 374 | uni.showToast({ title: '网络请求失败', icon: 'none' }); |
| 378 | } finally { | 375 | } finally { |
| 379 | - loadDetail(); | 376 | + if (!isMyPlan.value) { |
| 377 | + loadDetail(); | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | } | 380 | } |
| 381 | } | 381 | } |
| 382 | // 用户点取消 → 什么都不做 | 382 | // 用户点取消 → 什么都不做 |
| @@ -433,37 +433,55 @@ const loadDetail = async () => { | @@ -433,37 +433,55 @@ const loadDetail = async () => { | ||
| 433 | } | 433 | } |
| 434 | }; | 434 | }; |
| 435 | 435 | ||
| 436 | -// ====================== 计划收藏/添加功能 ====================== | 436 | +const loadDetailUpdate = () => { |
| 437 | + loadDetail(); | ||
| 438 | + isAddToMyPlanList(); | ||
| 439 | +} | ||
| 437 | 440 | ||
| 438 | -// 检查当前计划是否已经添加 | ||
| 439 | -const checkPlanAddedStatus = async () => { | 441 | +// // 检查当前计划是否已经添加到我的计划列表 |
| 442 | +// 计划是否已添加 | ||
| 443 | +const isPlanAdded = ref(false); | ||
| 444 | +const isAddToMyPlanList = async () => { | ||
| 440 | if (!id.value) return; | 445 | if (!id.value) return; |
| 441 | try { | 446 | try { |
| 442 | - const res = await QueryPlanApi.isAddPlan(id.value); | ||
| 443 | - // 接口返回 true = 已添加 | 447 | + const res = await QueryPlanApi.isAddPlan(Number(id.value)); |
| 448 | + // 后端返回布尔值,赋值状态 | ||
| 444 | isPlanAdded.value = res.data === true; | 449 | isPlanAdded.value = res.data === true; |
| 445 | - console.log("计划是否已添加:", isPlanAdded.value); | 450 | + console.log("当前计划是否已加入我的计划:", isPlanAdded.value); |
| 446 | } catch (err) { | 451 | } catch (err) { |
| 447 | - console.error("检查计划添加状态失败:", err); | 452 | + console.error("查询是否加入计划失败:", err); |
| 448 | isPlanAdded.value = false; | 453 | isPlanAdded.value = false; |
| 449 | } | 454 | } |
| 450 | }; | 455 | }; |
| 451 | 456 | ||
| 457 | +const isFavorite = ref(false) | ||
| 458 | +// 检查当前计划是否已经收藏 | ||
| 459 | +const checkPlanAddedStatus = async () => { | ||
| 460 | + if (!id.value) return; | ||
| 461 | + try { | ||
| 462 | + const res = await QueryPlanApi.checkFavorite(id.value); | ||
| 463 | + console.log('计划是否已经收藏:', res); | ||
| 464 | + isFavorite.value = res.data === true; | ||
| 465 | + console.log("计划是否已经收藏:", isFavorite.value); | ||
| 466 | + } catch (err) { | ||
| 467 | + console.error("检查计划收藏状态失败:", err); | ||
| 468 | + isFavorite.value = false; | ||
| 469 | + } | ||
| 470 | +}; | ||
| 471 | + | ||
| 452 | // 切换添加/取消收藏计划 | 472 | // 切换添加/取消收藏计划 |
| 453 | const toggleAddPlan = async () => { | 473 | const toggleAddPlan = async () => { |
| 454 | if (!id.value) { | 474 | if (!id.value) { |
| 455 | uni.showToast({ title: "计划ID不存在", icon: "none" }); | 475 | uni.showToast({ title: "计划ID不存在", icon: "none" }); |
| 456 | return; | 476 | return; |
| 457 | } | 477 | } |
| 458 | - | ||
| 459 | - const originalStatus = isPlanAdded.value; | ||
| 460 | - // 乐观更新UI(点了立刻变爱心) | ||
| 461 | - isPlanAdded.value = !originalStatus; | ||
| 462 | - | 478 | + const originalStatus = isFavorite.value; |
| 479 | + // 点了立刻变爱心) | ||
| 480 | + isFavorite.value = !originalStatus; | ||
| 463 | try { | 481 | try { |
| 464 | // 调用添加计划接口 | 482 | // 调用添加计划接口 |
| 465 | - await QueryPlanApi.addPlan(id.value); | ||
| 466 | - | 483 | + const params = isFavorite.value ? 1 : 0; |
| 484 | + await QueryPlanApi.toggleFavorite(Number(id.value), params); | ||
| 467 | if (!originalStatus) { | 485 | if (!originalStatus) { |
| 468 | uni.showToast({ title: "收藏计划成功" }); | 486 | uni.showToast({ title: "收藏计划成功" }); |
| 469 | } else { | 487 | } else { |
| @@ -471,7 +489,7 @@ const toggleAddPlan = async () => { | @@ -471,7 +489,7 @@ const toggleAddPlan = async () => { | ||
| 471 | } | 489 | } |
| 472 | } catch (err) { | 490 | } catch (err) { |
| 473 | // 失败回滚 | 491 | // 失败回滚 |
| 474 | - isPlanAdded.value = originalStatus; | 492 | + isFavorite.value = originalStatus; |
| 475 | console.error("操作失败:", err); | 493 | console.error("操作失败:", err); |
| 476 | uni.showToast({ title: "操作失败", icon: "none" }); | 494 | uni.showToast({ title: "操作失败", icon: "none" }); |
| 477 | } | 495 | } |
| @@ -491,6 +509,7 @@ const openCalendarPopup = (planId, templateId) => { | @@ -491,6 +509,7 @@ const openCalendarPopup = (planId, templateId) => { | ||
| 491 | } | 509 | } |
| 492 | }; | 510 | }; |
| 493 | 511 | ||
| 512 | + | ||
| 494 | // 日历添加成功的回调(可选,可做刷新/提示) | 513 | // 日历添加成功的回调(可选,可做刷新/提示) |
| 495 | const handleCalendarSuccess = () => { | 514 | const handleCalendarSuccess = () => { |
| 496 | console.log('课程已成功添加到日历'); | 515 | console.log('课程已成功添加到日历'); |
| @@ -512,7 +531,8 @@ onLoad((options) => { | @@ -512,7 +531,8 @@ onLoad((options) => { | ||
| 512 | console.log("计划详情接收的参数:", options); | 531 | console.log("计划详情接收的参数:", options); |
| 513 | id.value = options.planid; | 532 | id.value = options.planid; |
| 514 | isMyPlan.value = options.isMyPlan === "true"; | 533 | isMyPlan.value = options.isMyPlan === "true"; |
| 515 | - checkPlanAddedStatus(); // 检查计划是否已添加 | 534 | + checkPlanAddedStatus(); // 检查计划是否已收藏 |
| 535 | + isAddToMyPlanList(); // 新增:查询是否加入我的计划 | ||
| 516 | // #ifdef MP-WEIXIN | 536 | // #ifdef MP-WEIXIN |
| 517 | wx.showShareMenu({ | 537 | wx.showShareMenu({ |
| 518 | withShareTicket: true, | 538 | withShareTicket: true, |
| @@ -69,6 +69,17 @@ const dailytemplateApi = { | @@ -69,6 +69,17 @@ const dailytemplateApi = { | ||
| 69 | data, | 69 | data, |
| 70 | }); | 70 | }); |
| 71 | }, | 71 | }, |
| 72 | + // 复制每日模板 | ||
| 73 | + copyTempleteDailyTemplate: (sourceTemplateId,targetDates) => { | ||
| 74 | + return request({ | ||
| 75 | + url: `/app/daily/template/copy`, | ||
| 76 | + method: 'POST', | ||
| 77 | + data:{ | ||
| 78 | + sourceTemplateId, | ||
| 79 | + targetDates | ||
| 80 | + } | ||
| 81 | + }); | ||
| 82 | + }, | ||
| 72 | }; | 83 | }; |
| 73 | 84 | ||
| 74 | export default dailytemplateApi; | 85 | export default dailytemplateApi; |
| @@ -162,7 +162,7 @@ const QueryPlanApi = { | @@ -162,7 +162,7 @@ const QueryPlanApi = { | ||
| 162 | }); | 162 | }); |
| 163 | }, | 163 | }, |
| 164 | 164 | ||
| 165 | - // 移动到 | 165 | + // 移动到/复制到 |
| 166 | updateDailyTemplateDate: (data) => { | 166 | updateDailyTemplateDate: (data) => { |
| 167 | return request({ | 167 | return request({ |
| 168 | url: '/app/plan/updateDailyTemplateDate', | 168 | url: '/app/plan/updateDailyTemplateDate', |
| @@ -227,7 +227,8 @@ export const useTrainingStore = defineStore('training', { | @@ -227,7 +227,8 @@ export const useTrainingStore = defineStore('training', { | ||
| 227 | return { | 227 | return { |
| 228 | id: act.exerciseId, | 228 | id: act.exerciseId, |
| 229 | name: act.exerciseName, | 229 | name: act.exerciseName, |
| 230 | - urlImage: act.exerciseCover || act.url3dAnimation || act.urlImage, | 230 | + // urlImage: act.exerciseCover || act.url3dAnimation || act.urlImage, |
| 231 | + urlImage: act.url3dAnimation || act.exerciseCover || act.urlImage, | ||
| 231 | // urlImage | 232 | // urlImage |
| 232 | exerciseType: act.exerciseType, | 233 | exerciseType: act.exerciseType, |
| 233 | categoryDescription: act.categoryDescription, | 234 | categoryDescription: act.categoryDescription, |
-
Please register or login to post a comment