Showing
21 changed files
with
910 additions
and
626 deletions
| @@ -6,9 +6,9 @@ SHOPRO_VERSION=v2.4.1 | @@ -6,9 +6,9 @@ SHOPRO_VERSION=v2.4.1 | ||
| 6 | # SHOPRO_BASE_URL=http://mall.hcxtec.com | 6 | # SHOPRO_BASE_URL=http://mall.hcxtec.com |
| 7 | # SHOPRO_BASE_URL=https://xunji.geaktec.com | 7 | # SHOPRO_BASE_URL=https://xunji.geaktec.com |
| 8 | # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) | 8 | # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) |
| 9 | -# SHOPRO_DEV_BASE_URL=http://192.168.1.200:48081 | 9 | +SHOPRO_DEV_BASE_URL=http://192.168.1.200:48081 |
| 10 | # SHOPRO_DEV_BASE_URL=http://192.168.1.85:48080 | 10 | # SHOPRO_DEV_BASE_URL=http://192.168.1.85:48080 |
| 11 | -SHOPRO_DEV_BASE_URL=https://xunji.geaktec.com | 11 | +# SHOPRO_DEV_BASE_URL=https://xunji.geaktec.com |
| 12 | # SHOPRO_DEV_BASE_URL=http://api-dashboard.yudao.iocoder.cn/ | 12 | # SHOPRO_DEV_BASE_URL=http://api-dashboard.yudao.iocoder.cn/ |
| 13 | ### SHOPRO_DEV_BASE_URL=http://10.171.1.188:48080 | 13 | ### SHOPRO_DEV_BASE_URL=http://10.171.1.188:48080 |
| 14 | ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc | 14 | ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc |
.workbuddy/memory/2026-06-23.md
deleted
100644 → 0
| 1 | -# 2026-06-23 工作日志 | ||
| 2 | - | ||
| 3 | -## 悬浮球快照方案 — 完整改造方案 | ||
| 4 | - | ||
| 5 | -分析了 exercising 健身小程序中 trainingStore(Pinia)被三个功能模块(动作训练、超级组、每日模板编辑/修改)共享导致的数据冲突问题。 | ||
| 6 | - | ||
| 7 | -### 问题根因 | ||
| 8 | -- `grid-cell-content-popup.vue` 的编辑按钮 (`templateEdit`) 调用 `loadDailyTemplateForEdit()` 覆盖 store | ||
| 9 | -- `wode-xinjian-moban.vue` 的 `goBack()` / `handleSave()` 调用 `clearTrainingStore()` 清空 store | ||
| 10 | -- `meiri-moban-xiugai.vue` 的修改弹窗(弹窗模式)已通过快照解决,但编辑和新建是页面跳转模式,快照来不及归还 | ||
| 11 | - | ||
| 12 | -### 最终方案:悬浮球持有快照 | ||
| 13 | - | ||
| 14 | -核心思路:最小化时将 trainingStore 全量深拷贝到共享 reactive 模块 `trainingSnapshot`(存在 JS 堆内存,非 Pinia),悬浮球由此独立控制显隐和回显。 | ||
| 15 | - | ||
| 16 | -改动清单: | ||
| 17 | -1. **新建** `sheep/store/trainingSnapshot.js` — 共享 reactive 快照模块 | ||
| 18 | -2. **重写** `pages/TrainingFloating.vue` — 自有 showBall + 回显逻辑 + 计时器补偿 | ||
| 19 | -3. **修改** `pages4/.../xunji-dongzuo-lianxi.vue` openMin() — 保存快照到共享模块 | ||
| 20 | -4. **删除** `sheep/store/trainingStore.js` clearTrainingStore() 中 `this.min = false` | ||
| 21 | -5. **不改** grid-cell-content-popup.vue 和 wode-xinjian-moban.vue | ||
| 22 | -6. **修复** meiri-moban-xiugai.vue 中 Object.assign → $patch | ||
| 23 | - | ||
| 24 | -总计约 113 行,4 文件改动,2 文件不动。 |
| 1 | <template> | 1 | <template> |
| 2 | - <view class="floating-train-btn" v-if="trainingStore.min" @click="goToTrainingPage"> | 2 | + <view class="floating-train-btn" v-if="showBall" @click="goToTrainingPage"> |
| 3 | <view class="icon-wrap"> | 3 | <view class="icon-wrap"> |
| 4 | <up-icon name="plus-circle" color="#fff" size="24"></up-icon> | 4 | <up-icon name="plus-circle" color="#fff" size="24"></up-icon> |
| 5 | </view> | 5 | </view> |
| @@ -13,28 +13,91 @@ | @@ -13,28 +13,91 @@ | ||
| 13 | <script setup> | 13 | <script setup> |
| 14 | import { computed, watch, onMounted } from 'vue'; | 14 | import { computed, watch, onMounted } from 'vue'; |
| 15 | import { useTrainingStore } from '@/sheep/store/trainingStore'; | 15 | import { useTrainingStore } from '@/sheep/store/trainingStore'; |
| 16 | +import { trainingSnapshot } from '@/sheep/store/trainingSnapshot' | ||
| 16 | 17 | ||
| 17 | const trainingStore = useTrainingStore(); | 18 | const trainingStore = useTrainingStore(); |
| 18 | 19 | ||
| 19 | -// 格式化显示“开始时间” | ||
| 20 | -const formattedStartTime = computed(() => trainingStore.trainingTimeText); | ||
| 21 | 20 | ||
| 22 | -// 跳转到训练页面 | 21 | +const showBall = computed(() => trainingSnapshot.visible && trainingSnapshot.data !== null) |
| 22 | + | ||
| 23 | +// 格式化显示时间(快照中的 trainingTimeText) | ||
| 24 | +const formattedStartTime = computed(() => { | ||
| 25 | + if (!trainingSnapshot.data) return '' | ||
| 26 | + return trainingSnapshot.data.trainingTimeText || '' | ||
| 27 | +}) | ||
| 28 | + | ||
| 29 | +// ========== 点击悬浮球:还原快照 → 跳转训练页 ========== | ||
| 23 | const goToTrainingPage = () => { | 30 | const goToTrainingPage = () => { |
| 24 | - // 悬浮球消失 | ||
| 25 | - trainingStore.min = false; | 31 | + if (!trainingSnapshot.data) return |
| 32 | + | ||
| 33 | + // 安全检查:如果 store 里已经有不属于本次快照的训练数据(用户在编辑中) | ||
| 34 | + if (trainingStore.id && trainingStore.id !== trainingSnapshot.data.id) { | ||
| 35 | + uni.showModal({ | ||
| 36 | + title: '提示', | ||
| 37 | + content: '当前已有另一组训练数据,恢复会覆盖它,确认吗?', | ||
| 38 | + confirmText: '确认恢复', | ||
| 39 | + cancelText: '取消', | ||
| 40 | + success: (res) => { | ||
| 41 | + if (res.confirm) doRestoreAndGo() | ||
| 42 | + } | ||
| 43 | + }) | ||
| 44 | + return | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + doRestoreAndGo() | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +const doRestoreAndGo = () => { | ||
| 51 | + const snap = trainingSnapshot.data | ||
| 52 | + | ||
| 53 | + // 1. 用 $patch 还原全量 state(Pinia 官方批量更新,保证响应式触发) | ||
| 54 | + trainingStore.$patch(snap) | ||
| 55 | + | ||
| 56 | + // 2. 补偿计时器:如果快照时计时器在跑,加上经过的时间 | ||
| 57 | + if (trainingSnapshot.timerWasRunning && trainingSnapshot.timestamp) { | ||
| 58 | + const elapsed = Math.floor((Date.now() - trainingSnapshot.timestamp) / 1000) | ||
| 59 | + trainingStore.totalSeconds = (snap.totalSeconds || 0) + elapsed | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // 3. 如果快照时计时器在跑,重新启动 setInterval | ||
| 63 | + if (trainingSnapshot.timerWasRunning) { | ||
| 64 | + trainingStore.isPause = false | ||
| 65 | + trainingStore.timerInterval = setInterval(() => { | ||
| 66 | + trainingStore.totalSeconds++ | ||
| 67 | + }, 1000) | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + // 4. 清空快照,隐藏悬浮球 | ||
| 71 | + trainingSnapshot.data = null | ||
| 72 | + trainingSnapshot.timestamp = null | ||
| 73 | + trainingSnapshot.timerWasRunning = false | ||
| 74 | + trainingSnapshot.visible = false | ||
| 75 | + | ||
| 76 | + // 5. 跳转训练页面 | ||
| 26 | uni.navigateTo({ | 77 | uni.navigateTo({ |
| 27 | - url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${trainingStore.id}&type=${trainingStore.type}&isTraining=true`, | ||
| 28 | - }); | ||
| 29 | -}; | 78 | + url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${snap.id}&type=${snap.type}&isTraining=true`, |
| 79 | + }) | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +// 格式化显示“开始时间” | ||
| 83 | +// const formattedStartTime = computed(() => trainingStore.trainingTimeText); | ||
| 84 | + | ||
| 85 | +// 跳转到训练页面 | ||
| 86 | +// const goToTrainingPage = () => { | ||
| 87 | +// // 悬浮球消失 | ||
| 88 | +// trainingStore.min = false; | ||
| 89 | +// uni.navigateTo({ | ||
| 90 | +// url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${trainingStore.id}&type=${trainingStore.type}`, | ||
| 91 | +// }); | ||
| 92 | +// }; | ||
| 30 | 93 | ||
| 31 | -watch(() => trainingStore.min, (newVal) => { | ||
| 32 | - console.log('🟢 悬浮球组件监听到 min 变化:', newVal); | 94 | +watch(() => showBall.value, (newVal) => { |
| 95 | + console.log('🟢 悬浮球组件监听到 showBall 变化:', newVal); | ||
| 33 | }, { immediate: true }); | 96 | }, { immediate: true }); |
| 34 | 97 | ||
| 35 | // 组件挂载时打印 | 98 | // 组件挂载时打印 |
| 36 | onMounted(() => { | 99 | onMounted(() => { |
| 37 | - console.log('🟢 悬浮球组件已挂载,当前 min 值:', trainingStore.min); | 100 | + console.log('🟢 悬浮球组件已挂载,当前 showBall 值:', showBall.value); |
| 38 | }); | 101 | }); |
| 39 | </script> | 102 | </script> |
| 40 | 103 |
| 1 | <template> | 1 | <template> |
| 2 | <view class="charts-box"> | 2 | <view class="charts-box"> |
| 3 | - <!-- :canvas2d="true" tooltipFormat="showYLable" :onmovetip="true" --> | 3 | + <!-- :canvas2d="true"(导致穿透的原因) tooltipFormat="showYLable" :onmovetip="true" --> |
| 4 | <qiun-data-charts :type="chartType" :opts="opts" :chartData="chartData" :reshow="reshow" :tooltipShow="true" /> | 4 | <qiun-data-charts :type="chartType" :opts="opts" :chartData="chartData" :reshow="reshow" :tooltipShow="true" /> |
| 5 | </view> | 5 | </view> |
| 6 | </template> | 6 | </template> |
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | <view class="goal-list"> | 7 | <view class="goal-list"> |
| 8 | <view class="goal-item"> | 8 | <view class="goal-item"> |
| 9 | <view class="serial"> | 9 | <view class="serial"> |
| 10 | - {{ subIndex + 1 }}{{ String.fromCharCode(65 + subIndex) }}</view> | 10 | + {{ setIndex + 1 }}{{ String.fromCharCode(65 + subIndex) }}</view> |
| 11 | <!-- ========================================== --> | 11 | <!-- ========================================== --> |
| 12 | <!-- 0:独立 重量+次数 --> | 12 | <!-- 0:独立 重量+次数 --> |
| 13 | <!-- ========================================== --> | 13 | <!-- ========================================== --> |
| @@ -178,7 +178,8 @@ const props = defineProps({ | @@ -178,7 +178,8 @@ const props = defineProps({ | ||
| 178 | userWeight: { | 178 | userWeight: { |
| 179 | type: Number, | 179 | type: Number, |
| 180 | default: 70 | 180 | default: 70 |
| 181 | - } | 181 | + }, |
| 182 | + | ||
| 182 | }) | 183 | }) |
| 183 | 184 | ||
| 184 | const emit = defineEmits(['open-time-picker']) | 185 | const emit = defineEmits(['open-time-picker']) |
| @@ -84,7 +84,7 @@ | @@ -84,7 +84,7 @@ | ||
| 84 | <view class="action-top"> | 84 | <view class="action-top"> |
| 85 | <text class="action-name">{{ unit.exercises[0]?.exerciseName || '动作名称' }}</text> | 85 | <text class="action-name">{{ unit.exercises[0]?.exerciseName || '动作名称' }}</text> |
| 86 | <text class="action-totalWeight">{{ unit.totalWeight | 86 | <text class="action-totalWeight">{{ unit.totalWeight |
| 87 | - }}kg</text> | 87 | + }}kg</text> |
| 88 | </view> | 88 | </view> |
| 89 | 89 | ||
| 90 | <!-- 下半部分:组次行 → 向左对齐图片 ✅核心--> | 90 | <!-- 下半部分:组次行 → 向左对齐图片 ✅核心--> |
| @@ -233,8 +233,8 @@ | @@ -233,8 +233,8 @@ | ||
| 233 | 233 | ||
| 234 | <!-- 日期备注弹窗 --> | 234 | <!-- 日期备注弹窗 --> |
| 235 | <RiliRiqibeizhu v-model:visible="showRiqibeizhu" :date="date" :note-id="currentEditId" | 235 | <RiliRiqibeizhu v-model:visible="showRiqibeizhu" :date="date" :note-id="currentEditId" |
| 236 | - :history-list="noteHistoryList" @save="loaddailytemplate" @close="handleCloseNotePopup" | ||
| 237 | - @refreshMain="handleRefreshMain" /> | 236 | + :history-list="noteHistoryList" :note-content-list="noteList" @save="loaddailytemplate" |
| 237 | + @close="handleCloseNotePopup" @refreshMain="handleRefreshMain" /> | ||
| 238 | 238 | ||
| 239 | <!-- 更多弹窗 --> | 239 | <!-- 更多弹窗 --> |
| 240 | <up-popup :show="moreShow" mode="bottom" mask-click @close="closeMorePopup" :safe-area-inset-bottom="true"> | 240 | <up-popup :show="moreShow" mode="bottom" mask-click @close="closeMorePopup" :safe-area-inset-bottom="true"> |
| @@ -424,7 +424,7 @@ const loaddailytemplate = async () => { | @@ -424,7 +424,7 @@ const loaddailytemplate = async () => { | ||
| 424 | 424 | ||
| 425 | const noteListFromApi = resdaily.data.notes; | 425 | const noteListFromApi = resdaily.data.notes; |
| 426 | noteList.value = noteListFromApi; | 426 | noteList.value = noteListFromApi; |
| 427 | - console.log('打印每日模板详情的备注列表:noteList', noteList); | 427 | + console.log('打印每日模板详情的备注列表:noteList', noteList.value); |
| 428 | 428 | ||
| 429 | const historyFromApi = resdaily.data.noteHistoryList || []; | 429 | const historyFromApi = resdaily.data.noteHistoryList || []; |
| 430 | console.log('打印历史备注:', historyFromApi); | 430 | console.log('打印历史备注:', historyFromApi); |
| @@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
| 5 | <!-- 顶部导航栏 --> | 5 | <!-- 顶部导航栏 --> |
| 6 | <view class="popup-header"> | 6 | <view class="popup-header"> |
| 7 | <view class="close-btn" @click="handleClose"> | 7 | <view class="close-btn" @click="handleClose"> |
| 8 | - <text class="close-icon">×</text> | 8 | + <up-icon name="close" color="#333" size="20"></up-icon> |
| 9 | </view> | 9 | </view> |
| 10 | <text class="popup-title"> {{ isEdit ? '编辑日程备注' : '新增日程备注' }}</text> | 10 | <text class="popup-title"> {{ isEdit ? '编辑日程备注' : '新增日程备注' }}</text> |
| 11 | <button class="save-btn" @click="handleSave">保存</button> | 11 | <button class="save-btn" @click="handleSave">保存</button> |
| @@ -55,7 +55,8 @@ const props = defineProps({ | @@ -55,7 +55,8 @@ const props = defineProps({ | ||
| 55 | visible: { type: Boolean, default: false }, | 55 | visible: { type: Boolean, default: false }, |
| 56 | date: { type: String, required: true }, | 56 | date: { type: String, required: true }, |
| 57 | noteId: { type: [Number, String], default: null }, | 57 | noteId: { type: [Number, String], default: null }, |
| 58 | - historyList: { type: Array, default: () => [] } | 58 | + historyList: { type: Array, default: () => [] }, |
| 59 | + noteContentList: { type: Array, default: () => [] } | ||
| 59 | }); | 60 | }); |
| 60 | 61 | ||
| 61 | const emit = defineEmits(['update:visible', 'save', 'close', 'refreshMain']); | 62 | const emit = defineEmits(['update:visible', 'save', 'close', 'refreshMain']); |
| @@ -110,13 +111,43 @@ const selectHistoryNote = (item) => { | @@ -110,13 +111,43 @@ const selectHistoryNote = (item) => { | ||
| 110 | const handleClose = () => { | 111 | const handleClose = () => { |
| 111 | emit('update:visible', false); | 112 | emit('update:visible', false); |
| 112 | emit('close'); | 113 | emit('close'); |
| 114 | + // 清空文本 | ||
| 115 | + noteContent.value = ''; | ||
| 116 | + selectedColorIndex.value = 0; | ||
| 113 | }; | 117 | }; |
| 114 | const handleSave = async () => { | 118 | const handleSave = async () => { |
| 115 | - if (!noteContent.value.trim()) return uni.showToast({ title: "请输入内容" }); | 119 | + const content = noteContent.value.trim() |
| 120 | + if (!content) return uni.showToast({ title: "请输入内容" }); | ||
| 121 | + // 判断输入内容是否已存在于当日备注列表 | ||
| 122 | + | ||
| 123 | + // 全部场景都校验重复 | ||
| 124 | + const isRepeat = props.noteContentList.some(item => { | ||
| 125 | + if (isEdit.value && item.id === Number(props.noteId)) { | ||
| 126 | + return false | ||
| 127 | + } | ||
| 128 | + // 内容相同则判定重复 | ||
| 129 | + return item.content === content | ||
| 130 | + }) | ||
| 131 | + | ||
| 132 | + console.log('isRepeat---', isRepeat); | ||
| 133 | + if (isRepeat) { | ||
| 134 | + uni.showToast({ title: "操作失败:备注存在重复值", icon: "none" }) | ||
| 135 | + return; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + | ||
| 139 | + if (isRepeat) { | ||
| 140 | + uni.showToast({ title: "操作失败:备注存在重复值", icon: "none" }) | ||
| 141 | + return; | ||
| 142 | + } | ||
| 143 | + | ||
| 116 | let data = { | 144 | let data = { |
| 117 | content: noteContent.value, | 145 | content: noteContent.value, |
| 118 | backgroundColor: colorList.value[selectedColorIndex.value] | 146 | backgroundColor: colorList.value[selectedColorIndex.value] |
| 119 | }; | 147 | }; |
| 148 | + | ||
| 149 | + // 当日已经有的备注不允许再次输入,提示返回 | ||
| 150 | + | ||
| 120 | try { | 151 | try { |
| 121 | if (isEdit.value) { | 152 | if (isEdit.value) { |
| 122 | // 编辑:必须传 ID | 153 | // 编辑:必须传 ID |
| @@ -90,7 +90,7 @@ const activeSceneId = ref('0'); | @@ -90,7 +90,7 @@ const activeSceneId = ref('0'); | ||
| 90 | const partList = ref([]); | 90 | const partList = ref([]); |
| 91 | const rawPartData = ref([]); // 存储接口返回的原始部位数据 | 91 | const rawPartData = ref([]); // 存储接口返回的原始部位数据 |
| 92 | const showPartDropdown = ref(false); | 92 | const showPartDropdown = ref(false); |
| 93 | -const activePart = ref('不限'); | 93 | +const activePart = ref('部位'); |
| 94 | const activePartId = ref('0'); // 新增:默认选中"不限",id=0 | 94 | const activePartId = ref('0'); // 新增:默认选中"不限",id=0 |
| 95 | // 场景筛选相关状态 | 95 | // 场景筛选相关状态 |
| 96 | const sceneList = ref([ | 96 | const sceneList = ref([ |
| @@ -101,7 +101,7 @@ const sceneList = ref([ | @@ -101,7 +101,7 @@ const sceneList = ref([ | ||
| 101 | // { id: 4, title: '办公室' }, | 101 | // { id: 4, title: '办公室' }, |
| 102 | ]); | 102 | ]); |
| 103 | const showSceneDropdown = ref(false); | 103 | const showSceneDropdown = ref(false); |
| 104 | -const activeScene = ref('不限'); | 104 | +const activeScene = ref('场景'); |
| 105 | 105 | ||
| 106 | // 获取模板大类列表 | 106 | // 获取模板大类列表 |
| 107 | const TemplatesList = async () => { | 107 | const TemplatesList = async () => { |
| @@ -147,20 +147,27 @@ const toggleSceneDropdown = () => { | @@ -147,20 +147,27 @@ const toggleSceneDropdown = () => { | ||
| 147 | 147 | ||
| 148 | // 选择部位 | 148 | // 选择部位 |
| 149 | const selectPart = (item) => { | 149 | const selectPart = (item) => { |
| 150 | - activePart.value = item.title; | ||
| 151 | activePartId.value = item.id; | 150 | activePartId.value = item.id; |
| 151 | + // 判断是否是不限id=0 | ||
| 152 | + if (item.id === '0') { | ||
| 153 | + activePart.value = '部位'; | ||
| 154 | + } else { | ||
| 155 | + activePart.value = item.title; | ||
| 156 | + } | ||
| 152 | showPartDropdown.value = false; | 157 | showPartDropdown.value = false; |
| 153 | - // 加这一行:选完部位立刻筛选 | ||
| 154 | doFilter(); | 158 | doFilter(); |
| 155 | }; | 159 | }; |
| 156 | 160 | ||
| 157 | // 选择场景 | 161 | // 选择场景 |
| 158 | const selectScene = (item) => { | 162 | const selectScene = (item) => { |
| 159 | - activeScene.value = item.title; | ||
| 160 | - // 关键:把选中的场景ID存起来 | ||
| 161 | activeSceneId.value = item.id; | 163 | activeSceneId.value = item.id; |
| 164 | + // 判断是否是不限id=0 | ||
| 165 | + if (item.id === '0') { | ||
| 166 | + activeScene.value = '场景'; | ||
| 167 | + } else { | ||
| 168 | + activeScene.value = item.title; | ||
| 169 | + } | ||
| 162 | showSceneDropdown.value = false; | 170 | showSceneDropdown.value = false; |
| 163 | - // 选择完,立即执行筛选! | ||
| 164 | doFilter(); | 171 | doFilter(); |
| 165 | }; | 172 | }; |
| 166 | 173 | ||
| @@ -177,6 +184,9 @@ const doFilter = async () => { | @@ -177,6 +184,9 @@ const doFilter = async () => { | ||
| 177 | if (musclesId === '0' && scene === '0') { | 184 | if (musclesId === '0' && scene === '0') { |
| 178 | // 情况A:都不限 -> 恢复显示【模板大类】 | 185 | // 情况A:都不限 -> 恢复显示【模板大类】 |
| 179 | isFiltering.value = false; | 186 | isFiltering.value = false; |
| 187 | + // 重置按钮文字为默认 | ||
| 188 | + activePart.value = '部位'; | ||
| 189 | + activeScene.value = '场景'; | ||
| 180 | // 大类列表之前已经加载过了,直接显示 | 190 | // 大类列表之前已经加载过了,直接显示 |
| 181 | return; | 191 | return; |
| 182 | } | 192 | } |
| @@ -252,46 +262,7 @@ onMounted(async () => { | @@ -252,46 +262,7 @@ onMounted(async () => { | ||
| 252 | align-items: center; | 262 | align-items: center; |
| 253 | justify-content: center; | 263 | justify-content: center; |
| 254 | width: auto; | 264 | width: auto; |
| 255 | - min-width: 150rpx; | ||
| 256 | - height: 50rpx; | ||
| 257 | - background-color: #fff; | ||
| 258 | - color: #333; | ||
| 259 | - font-size: 24rpx; | ||
| 260 | - border: 2rpx solid #ddd; | ||
| 261 | - border-radius: 25rpx; | ||
| 262 | - | ||
| 263 | - .text { | ||
| 264 | - margin-right: 5rpx; | ||
| 265 | - } | ||
| 266 | - } | ||
| 267 | - | ||
| 268 | - /* 筛选包装器 */ | ||
| 269 | - .filter-wrapper { | ||
| 270 | - position: relative; | ||
| 271 | - z-index: 10; | ||
| 272 | - } | ||
| 273 | - | ||
| 274 | - /* 筛选按钮激活状态 */ | ||
| 275 | - .filter-btn.active { | ||
| 276 | - border-color: #43b05e; | ||
| 277 | - background-color: #e6f7f0; | ||
| 278 | - color: #43b05e; | ||
| 279 | - } | ||
| 280 | - | ||
| 281 | - .filter-section { | ||
| 282 | - display: flex; | ||
| 283 | - gap: 24rpx; | ||
| 284 | - padding: 20rpx 30rpx; | ||
| 285 | - background-color: white; | ||
| 286 | - flex-wrap: wrap; | ||
| 287 | - } | ||
| 288 | - | ||
| 289 | - .filter-btn { | ||
| 290 | - display: flex; | ||
| 291 | - align-items: center; | ||
| 292 | - justify-content: center; | ||
| 293 | - width: auto; | ||
| 294 | - min-width: 125rpx; | 265 | + min-width: 70rpx; |
| 295 | height: 50rpx; | 266 | height: 50rpx; |
| 296 | background: #fff; | 267 | background: #fff; |
| 297 | color: #333; | 268 | color: #333; |
| @@ -317,33 +288,6 @@ onMounted(async () => { | @@ -317,33 +288,6 @@ onMounted(async () => { | ||
| 317 | z-index: 10; | 288 | z-index: 10; |
| 318 | } | 289 | } |
| 319 | 290 | ||
| 320 | - .dropdown-menu { | ||
| 321 | - position: absolute; | ||
| 322 | - top: calc(100% + 8rpx); | ||
| 323 | - left: 0; | ||
| 324 | - background: #fff; | ||
| 325 | - border-radius: 14rpx; | ||
| 326 | - box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.08); | ||
| 327 | - padding: 12rpx 0; | ||
| 328 | - z-index: 100; | ||
| 329 | - min-width: 160rpx; | ||
| 330 | - max-height: 320rpx; | ||
| 331 | - overflow-y: auto; | ||
| 332 | - } | ||
| 333 | - | ||
| 334 | - .dropdown-item { | ||
| 335 | - padding: 16rpx 24rpx; | ||
| 336 | - font-size: 26rpx; | ||
| 337 | - color: #333; | ||
| 338 | - white-space: nowrap; | ||
| 339 | - } | ||
| 340 | - | ||
| 341 | - .dropdown-item.selected { | ||
| 342 | - background: #f0f9f4; | ||
| 343 | - color: #2e9d5a; | ||
| 344 | - font-weight: 500; | ||
| 345 | - } | ||
| 346 | - | ||
| 347 | /* 下拉菜单样式 */ | 291 | /* 下拉菜单样式 */ |
| 348 | .dropdown-menu { | 292 | .dropdown-menu { |
| 349 | position: absolute; | 293 | position: absolute; |
| @@ -365,6 +309,7 @@ onMounted(async () => { | @@ -365,6 +309,7 @@ onMounted(async () => { | ||
| 365 | padding: 16rpx 24rpx; | 309 | padding: 16rpx 24rpx; |
| 366 | font-size: 26rpx; | 310 | font-size: 26rpx; |
| 367 | color: #333; | 311 | color: #333; |
| 312 | + white-space: nowrap; | ||
| 368 | } | 313 | } |
| 369 | 314 | ||
| 370 | /* 下拉菜单项悬停样式 */ | 315 | /* 下拉菜单项悬停样式 */ |
| @@ -381,13 +326,13 @@ onMounted(async () => { | @@ -381,13 +326,13 @@ onMounted(async () => { | ||
| 381 | } | 326 | } |
| 382 | 327 | ||
| 383 | .template-list { | 328 | .template-list { |
| 384 | - | ||
| 385 | margin-top: 120rpx; | 329 | margin-top: 120rpx; |
| 386 | padding: 0 30rpx; | 330 | padding: 0 30rpx; |
| 387 | box-sizing: border-box; | 331 | box-sizing: border-box; |
| 388 | flex: 1; | 332 | flex: 1; |
| 389 | display: flex; | 333 | display: flex; |
| 390 | flex-direction: column; | 334 | flex-direction: column; |
| 335 | + | ||
| 391 | // #ifdef MP-WEIXIN | 336 | // #ifdef MP-WEIXIN |
| 392 | padding-bottom: 200rpx; | 337 | padding-bottom: 200rpx; |
| 393 | height: calc(100vh - 274rpx); | 338 | height: calc(100vh - 274rpx); |
| @@ -397,7 +342,6 @@ onMounted(async () => { | @@ -397,7 +342,6 @@ onMounted(async () => { | ||
| 397 | margin-top: 87px; | 342 | margin-top: 87px; |
| 398 | // 关键修复:给H5固定高度,扣除顶部筛选栏77px + 自身margin-top87px | 343 | // 关键修复:给H5固定高度,扣除顶部筛选栏77px + 自身margin-top87px |
| 399 | height: calc(100vh - 77px - 87px); | 344 | height: calc(100vh - 77px - 87px); |
| 400 | - // padding-bottom: 40px; | ||
| 401 | /* #endif */ | 345 | /* #endif */ |
| 402 | 346 | ||
| 403 | .sub-template-list { | 347 | .sub-template-list { |
| @@ -449,7 +393,11 @@ onMounted(async () => { | @@ -449,7 +393,11 @@ onMounted(async () => { | ||
| 449 | } | 393 | } |
| 450 | 394 | ||
| 451 | &:last-child { | 395 | &:last-child { |
| 452 | - margin-bottom: 45rpx; | 396 | + margin-bottom: 5rpx; |
| 397 | + | ||
| 398 | + /* #ifdef H5 */ | ||
| 399 | + margin-bottom: 30rpx; | ||
| 400 | + /* #endif */ | ||
| 453 | } | 401 | } |
| 454 | } | 402 | } |
| 455 | } | 403 | } |
| @@ -351,6 +351,7 @@ const selectDate = async (date) => { | @@ -351,6 +351,7 @@ const selectDate = async (date) => { | ||
| 351 | :deep(.uni-date__x-input) { | 351 | :deep(.uni-date__x-input) { |
| 352 | height: 40rpx; | 352 | height: 40rpx; |
| 353 | line-height: 40rpx; | 353 | line-height: 40rpx; |
| 354 | + padding: 0 10rpx; | ||
| 354 | } | 355 | } |
| 355 | 356 | ||
| 356 | // 微信小程序端样式适配 | 357 | // 微信小程序端样式适配 |
| @@ -362,13 +363,14 @@ const selectDate = async (date) => { | @@ -362,13 +363,14 @@ const selectDate = async (date) => { | ||
| 362 | box-sizing: border-box; | 363 | box-sizing: border-box; |
| 363 | 364 | ||
| 364 | :deep(.uni-date-x) { | 365 | :deep(.uni-date-x) { |
| 365 | - height: 100%; | ||
| 366 | - min-height: 40rpx; | 366 | + height: 35rpx; |
| 367 | + // min-height: 40rpx; | ||
| 367 | border-radius: 50rpx; | 368 | border-radius: 50rpx; |
| 368 | background-color: transparent; | 369 | background-color: transparent; |
| 369 | padding: 0; | 370 | padding: 0; |
| 370 | margin: 0; | 371 | margin: 0; |
| 371 | border: none !important; | 372 | border: none !important; |
| 373 | + | ||
| 372 | } | 374 | } |
| 373 | 375 | ||
| 374 | :deep(.uni-date-editor--x) { | 376 | :deep(.uni-date-editor--x) { |
| @@ -381,11 +383,11 @@ const selectDate = async (date) => { | @@ -381,11 +383,11 @@ const selectDate = async (date) => { | ||
| 381 | } | 383 | } |
| 382 | 384 | ||
| 383 | :deep(.uni-date__x-input) { | 385 | :deep(.uni-date__x-input) { |
| 384 | - height: 40rpx; | ||
| 385 | - line-height: 40rpx; | 386 | + // height: 40rpx; |
| 387 | + // line-height: 40rpx; | ||
| 386 | font-size: 24rpx; | 388 | font-size: 24rpx; |
| 387 | color: #333; | 389 | color: #333; |
| 388 | - padding: 0; | 390 | + padding: 0 10rpx; |
| 389 | text-align: center; | 391 | text-align: center; |
| 390 | flex: 1; | 392 | flex: 1; |
| 391 | overflow: hidden; | 393 | overflow: hidden; |
| @@ -49,12 +49,12 @@ | @@ -49,12 +49,12 @@ | ||
| 49 | 49 | ||
| 50 | <view class="statistical"> | 50 | <view class="statistical"> |
| 51 | <template v-if="!showLastPeriod"> | 51 | <template v-if="!showLastPeriod"> |
| 52 | - <LineChart :categories="chartCategories" :series="chartSeries" chartType="column" :reshow="true" | ||
| 53 | - :extra="{ column: { width: trendChartColumnWidth } }" /> | 52 | + <LineChart v-if="chartCategories.length > 0" :categories="chartCategories" :series="chartSeries" |
| 53 | + chartType="column" :reshow="true" :extra="{ column: { width: trendChartColumnWidth } }" /> | ||
| 54 | </template> | 54 | </template> |
| 55 | <template v-else> | 55 | <template v-else> |
| 56 | - <LineChart :categories="chartCategories" :series="chartSeries" chartType="line" :reshow="true" | ||
| 57 | - :extra="{ column: { width: trendChartColumnWidth } }" /> | 56 | + <LineChart v-if="chartCategories.length > 0" :categories="chartCategories" :series="chartSeries" |
| 57 | + chartType="line" :reshow="true" :extra="{ column: { width: trendChartColumnWidth } }" /> | ||
| 58 | </template> | 58 | </template> |
| 59 | </view> | 59 | </view> |
| 60 | 60 |
| @@ -4,18 +4,14 @@ | @@ -4,18 +4,14 @@ | ||
| 4 | <view class="banner-wrapper"> | 4 | <view class="banner-wrapper"> |
| 5 | <swiper class="swiper"> | 5 | <swiper class="swiper"> |
| 6 | <swiper-item class="swiper-item"> | 6 | <swiper-item class="swiper-item"> |
| 7 | - <image | ||
| 8 | - class="img" | 7 | + <image class="img" |
| 9 | src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/37_1773628025534.png" | 8 | src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/37_1773628025534.png" |
| 10 | - mode="aspectFill" | ||
| 11 | - /> | 9 | + mode="aspectFill" /> |
| 12 | </swiper-item> | 10 | </swiper-item> |
| 13 | <swiper-item class="swiper-item"> | 11 | <swiper-item class="swiper-item"> |
| 14 | - <image | ||
| 15 | - class="img" | 12 | + <image class="img" |
| 16 | src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/38_1773628032712.png" | 13 | src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/38_1773628032712.png" |
| 17 | - mode="aspectFill" | ||
| 18 | - /> | 14 | + mode="aspectFill" /> |
| 19 | </swiper-item> | 15 | </swiper-item> |
| 20 | </swiper> | 16 | </swiper> |
| 21 | </view> | 17 | </view> |
| @@ -23,41 +19,26 @@ | @@ -23,41 +19,26 @@ | ||
| 23 | <!-- 筛选条 --> | 19 | <!-- 筛选条 --> |
| 24 | <!-- 后端计划分类的计划列表只返回了难度字段,所有只有这个字段的筛选生效了 --> | 20 | <!-- 后端计划分类的计划列表只返回了难度字段,所有只有这个字段的筛选生效了 --> |
| 25 | <view class="filter-bar"> | 21 | <view class="filter-bar"> |
| 26 | - <view | ||
| 27 | - v-for="(item, index) in filterList" | ||
| 28 | - :key="index" | ||
| 29 | - class="filter-item" | ||
| 30 | - :class="{ active: activeFilter === index }" | ||
| 31 | - @click=" | 22 | + <view v-for="(item, index) in filterList" :key="index" class="filter-item" |
| 23 | + :class="{ active: activeFilter === index }" @click=" | ||
| 32 | activeFilter = index; | 24 | activeFilter = index; |
| 33 | - toggleDrawer(index); | ||
| 34 | - " | ||
| 35 | - > | 25 | + toggleDrawer(index); |
| 26 | + "> | ||
| 36 | <text class="text">{{ | 27 | <text class="text">{{ |
| 37 | selectedFilters[item] === '不限' ? item : `${selectedFilters[item]}` | 28 | selectedFilters[item] === '不限' ? item : `${selectedFilters[item]}` |
| 38 | }}</text> | 29 | }}</text> |
| 39 | <uni-icons type="bottom" size="14" color="#666" /> | 30 | <uni-icons type="bottom" size="14" color="#666" /> |
| 40 | </view> | 31 | </view> |
| 41 | <view class="filter-search"> | 32 | <view class="filter-search"> |
| 42 | - <uni-icons | ||
| 43 | - type="search" | ||
| 44 | - size="20" | ||
| 45 | - color="#333" | ||
| 46 | - style="border: 1px solid #c5c5c5; border-radius: 30rpx; padding: 4rpx 6rpx" | ||
| 47 | - @click="navigateToSearch" | ||
| 48 | - /> | 33 | + <uni-icons type="search" size="20" color="#333" |
| 34 | + style="border: 1px solid #c5c5c5; border-radius: 30rpx; padding: 4rpx 6rpx" @click="navigateToSearch" /> | ||
| 49 | </view> | 35 | </view> |
| 50 | </view> | 36 | </view> |
| 51 | 37 | ||
| 52 | <!-- 下拉抽屉组件 --> | 38 | <!-- 下拉抽屉组件 --> |
| 53 | <view class="filter-drawer" v-show="drawerShow" @click.stop> | 39 | <view class="filter-drawer" v-show="drawerShow" @click.stop> |
| 54 | <view class="drawer-content"> | 40 | <view class="drawer-content"> |
| 55 | - <view | ||
| 56 | - v-for="(item, idx) in filterDrawerData" | ||
| 57 | - :key="idx" | ||
| 58 | - class="drawer-item" | ||
| 59 | - @click="selectDrawerItem(item)" | ||
| 60 | - > | 41 | + <view v-for="(item, idx) in filterDrawerData" :key="idx" class="drawer-item" @click="selectDrawerItem(item)"> |
| 61 | <text class="drawer-item-text">{{ item }}</text> | 42 | <text class="drawer-item-text">{{ item }}</text> |
| 62 | </view> | 43 | </view> |
| 63 | </view> | 44 | </view> |
| @@ -67,33 +48,19 @@ | @@ -67,33 +48,19 @@ | ||
| 67 | <view class="content-layout"> | 48 | <view class="content-layout"> |
| 68 | <!-- 左侧分类 --> | 49 | <!-- 左侧分类 --> |
| 69 | <scroll-view class="sidebar" scroll-y enable-flex> | 50 | <scroll-view class="sidebar" scroll-y enable-flex> |
| 70 | - <view | ||
| 71 | - v-for="(item, index) in categoryList" | ||
| 72 | - :key="item.id" | ||
| 73 | - class="category-item" | ||
| 74 | - :class="{ active: activeCategory === index }" | ||
| 75 | - @click="switchCategory(index)" | ||
| 76 | - > | 51 | + <view v-for="(item, index) in categoryList" :key="item.id" class="category-item" |
| 52 | + :class="{ active: activeCategory === index }" @click="switchCategory(index)"> | ||
| 77 | <text class="text">{{ item.name }}</text> | 53 | <text class="text">{{ item.name }}</text> |
| 78 | </view> | 54 | </view> |
| 79 | </scroll-view> | 55 | </scroll-view> |
| 80 | 56 | ||
| 81 | <!-- 右侧计划列表 --> | 57 | <!-- 右侧计划列表 --> |
| 82 | <scroll-view class="plan-list-wrap" scroll-y enable-flex> | 58 | <scroll-view class="plan-list-wrap" scroll-y enable-flex> |
| 83 | - <view | ||
| 84 | - v-for="plan in currentPlanList" | ||
| 85 | - :key="plan.id" | ||
| 86 | - class="plan-card" | ||
| 87 | - @tap="navigateToDetail(plan)" | ||
| 88 | - > | 59 | + <view v-for="plan in currentPlanList" :key="plan.id" class="plan-card" @tap="navigateToDetail(plan)"> |
| 89 | <image class="plan-cover" :src="plan.cover" mode="aspectFill" /> | 60 | <image class="plan-cover" :src="plan.cover" mode="aspectFill" /> |
| 90 | <view class="plan-info"> | 61 | <view class="plan-info"> |
| 91 | <view class="plan-tag-row"> | 62 | <view class="plan-tag-row"> |
| 92 | - <view | ||
| 93 | - v-if="plan.tag" | ||
| 94 | - class="plan-tag" | ||
| 95 | - :class="plan.tag === '火爆' ? 'tag-hot' : 'tag-new'" | ||
| 96 | - > | 63 | + <view v-if="plan.tag" class="plan-tag" :class="plan.tag === '火爆' ? 'tag-hot' : 'tag-new'"> |
| 97 | <text>{{ plan.tag }}</text> | 64 | <text>{{ plan.tag }}</text> |
| 98 | </view> | 65 | </view> |
| 99 | </view> | 66 | </view> |
| @@ -113,438 +80,449 @@ | @@ -113,438 +80,449 @@ | ||
| 113 | </template> | 80 | </template> |
| 114 | 81 | ||
| 115 | <script setup> | 82 | <script setup> |
| 116 | - import { computed, onMounted, ref } from 'vue'; | ||
| 117 | - // ✅ getCurrentPages 是全局 API,无需 import | ||
| 118 | - import QueryPlanApi from '@/sheep/api/plan/queryplan'; | ||
| 119 | - | ||
| 120 | - // ====== 筛选相关(保持原样)====== | ||
| 121 | - const filterList = ref(['频率', '难度', '场景', '人群']); | ||
| 122 | - const drawerShow = ref(false); | ||
| 123 | - const activeFilter = ref(null); | ||
| 124 | - const selectedFilters = ref({ | ||
| 125 | - 频率: '不限', | ||
| 126 | - 难度: '不限', | ||
| 127 | - 场景: '不限', | ||
| 128 | - 人群: '不限', | ||
| 129 | - }); | ||
| 130 | - const filterOptionsMap = { | ||
| 131 | - 频率: ['不限', '1练/周', '2练/周', '3练/周', '4练/周', '5练/周'], | ||
| 132 | - 难度: ['不限', '初阶', '中阶', '高阶'], | ||
| 133 | - 场景: ['不限', '健身房', '仅哑铃', '仅哑铃+杠铃'], | ||
| 134 | - 人群: ['不限', '青少年', '成年人', '中老年', '运动损伤'], | ||
| 135 | - }; | ||
| 136 | - | ||
| 137 | - const filterDrawerData = computed(() => { | ||
| 138 | - const currentFilterName = filterList.value[activeFilter.value]; | ||
| 139 | - return filterOptionsMap[currentFilterName] || []; | ||
| 140 | - }); | ||
| 141 | - | ||
| 142 | - const toggleDrawer = (index) => { | ||
| 143 | - if (activeFilter.value === index) { | ||
| 144 | - drawerShow.value = !drawerShow.value; | ||
| 145 | - } else { | ||
| 146 | - drawerShow.value = true; | ||
| 147 | - } | ||
| 148 | - activeFilter.value = index; | ||
| 149 | - }; | ||
| 150 | - | ||
| 151 | - const selectDrawerItem = (item) => { | ||
| 152 | - const currentFilterName = filterList.value[activeFilter.value]; | ||
| 153 | - selectedFilters.value[currentFilterName] = item; | ||
| 154 | - drawerShow.value = false; | ||
| 155 | - console.log('筛选条件更新:', selectedFilters.value); | ||
| 156 | - }; | ||
| 157 | - | ||
| 158 | - // ====== 分类与计划 ====== | ||
| 159 | - const categoryList = ref([]); | ||
| 160 | - const activeCategory = ref(0); | ||
| 161 | - const planData = ref([]); // 存储所有已加载的计划(带 _categoryId) | ||
| 162 | - const difficultyText = { 1: '初阶', 2: '中阶', 3: '高阶' }; | ||
| 163 | - // 完全匹配你后台的数字,复制这一段! | ||
| 164 | - const filterMap = { | ||
| 165 | - 频率: { | ||
| 166 | - 不限: 0, | ||
| 167 | - '1练/周': 1, | ||
| 168 | - '2练/周': 2, | ||
| 169 | - '3练/周': 3, | ||
| 170 | - '4练/周': 4, | ||
| 171 | - '5练/周': 5, | ||
| 172 | - '6练/周': 6, | ||
| 173 | - '7练/周': 7, | ||
| 174 | - }, | ||
| 175 | - 难度: { 不限: 0, 初阶: 1, 中阶: 2, 高阶: 3 }, | ||
| 176 | - 场景: { 不限: 1, 健身房: 2, 仅哑铃: 3, '仅哑铃+杠铃': 4 }, | ||
| 177 | - 人群: { 不限: 1, 青少年: 2, 成年人: 3, 中老年: 4, 运动损伤: 5 }, | ||
| 178 | - }; | ||
| 179 | - const currentPlanList = computed(() => { | ||
| 180 | - const categoryId = categoryList.value[activeCategory.value]?.id; | ||
| 181 | - if (categoryId == null) return []; | ||
| 182 | - | ||
| 183 | - let list = planData.value.filter((p) => p._categoryId === categoryId); | ||
| 184 | - | ||
| 185 | - // 频率 | ||
| 186 | - if (selectedFilters.value.频率 !== '不限') { | ||
| 187 | - const val = filterMap.频率[selectedFilters.value.频率]; | ||
| 188 | - list = list.filter((item) => item.frequency === val); | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - // 难度 | ||
| 192 | - if (selectedFilters.value.难度 !== '不限') { | ||
| 193 | - const val = filterMap.难度[selectedFilters.value.难度]; | ||
| 194 | - list = list.filter((item) => item.difficultyLevel === val); | ||
| 195 | - } | 83 | +import { computed, onMounted, ref } from 'vue'; |
| 84 | +// ✅ getCurrentPages 是全局 API,无需 import | ||
| 85 | +import QueryPlanApi from '@/sheep/api/plan/queryplan'; | ||
| 86 | + | ||
| 87 | +// ====== 筛选相关(保持原样)====== | ||
| 88 | +const filterList = ref(['频率', '难度', '场景', '人群']); | ||
| 89 | +const drawerShow = ref(false); | ||
| 90 | +const activeFilter = ref(null); | ||
| 91 | +const selectedFilters = ref({ | ||
| 92 | + 频率: '不限', | ||
| 93 | + 难度: '不限', | ||
| 94 | + 场景: '不限', | ||
| 95 | + 人群: '不限', | ||
| 96 | +}); | ||
| 97 | +const filterOptionsMap = { | ||
| 98 | + 频率: ['不限', '1练/周', '2练/周', '3练/周', '4练/周', '5练/周'], | ||
| 99 | + 难度: ['不限', '初阶', '中阶', '高阶'], | ||
| 100 | + 场景: ['不限', '健身房', '仅哑铃', '仅哑铃+杠铃'], | ||
| 101 | + 人群: ['不限', '青少年', '成年人', '中老年', '运动损伤'], | ||
| 102 | +}; | ||
| 103 | + | ||
| 104 | +const filterDrawerData = computed(() => { | ||
| 105 | + const currentFilterName = filterList.value[activeFilter.value]; | ||
| 106 | + return filterOptionsMap[currentFilterName] || []; | ||
| 107 | +}); | ||
| 108 | + | ||
| 109 | +const toggleDrawer = (index) => { | ||
| 110 | + if (activeFilter.value === index) { | ||
| 111 | + drawerShow.value = !drawerShow.value; | ||
| 112 | + } else { | ||
| 113 | + drawerShow.value = true; | ||
| 114 | + } | ||
| 115 | + activeFilter.value = index; | ||
| 116 | +}; | ||
| 117 | + | ||
| 118 | +const selectDrawerItem = (item) => { | ||
| 119 | + const currentFilterName = filterList.value[activeFilter.value]; | ||
| 120 | + selectedFilters.value[currentFilterName] = item; | ||
| 121 | + drawerShow.value = false; | ||
| 122 | + console.log('筛选条件更新:', selectedFilters.value); | ||
| 123 | +}; | ||
| 124 | + | ||
| 125 | +// ====== 分类与计划 ====== | ||
| 126 | +const categoryList = ref([]); | ||
| 127 | +const activeCategory = ref(0); | ||
| 128 | +const planData = ref([]); // 存储所有已加载的计划(带 _categoryId) | ||
| 129 | +const difficultyText = { 1: '初阶', 2: '中阶', 3: '高阶' }; | ||
| 130 | +// 完全匹配你后台的数字,复制这一段! | ||
| 131 | +const filterMap = { | ||
| 132 | + 频率: { | ||
| 133 | + 不限: 0, | ||
| 134 | + '1练/周': 1, | ||
| 135 | + '2练/周': 2, | ||
| 136 | + '3练/周': 3, | ||
| 137 | + '4练/周': 4, | ||
| 138 | + '5练/周': 5, | ||
| 139 | + '6练/周': 6, | ||
| 140 | + '7练/周': 7, | ||
| 141 | + }, | ||
| 142 | + 难度: { 不限: 0, 初阶: 1, 中阶: 2, 高阶: 3 }, | ||
| 143 | + 场景: { 不限: 1, 健身房: 2, 仅哑铃: 3, '仅哑铃+杠铃': 4 }, | ||
| 144 | + 人群: { 不限: 1, 青少年: 2, 成年人: 3, 中老年: 4, 运动损伤: 5 }, | ||
| 145 | +}; | ||
| 146 | +const currentPlanList = computed(() => { | ||
| 147 | + const categoryId = categoryList.value[activeCategory.value]?.id; | ||
| 148 | + if (categoryId == null) return []; | ||
| 149 | + | ||
| 150 | + let list = planData.value.filter((p) => p._categoryId === categoryId); | ||
| 151 | + | ||
| 152 | + // 频率 | ||
| 153 | + if (selectedFilters.value.频率 !== '不限') { | ||
| 154 | + const val = filterMap.频率[selectedFilters.value.频率]; | ||
| 155 | + list = list.filter((item) => item.frequency === val); | ||
| 156 | + } | ||
| 196 | 157 | ||
| 197 | - // 场景 | ||
| 198 | - if (selectedFilters.value.场景 !== '不限') { | ||
| 199 | - const val = filterMap.场景[selectedFilters.value.场景]; | ||
| 200 | - list = list.filter((item) => item.scene === val); | ||
| 201 | - } | 158 | + // 难度 |
| 159 | + if (selectedFilters.value.难度 !== '不限') { | ||
| 160 | + const val = filterMap.难度[selectedFilters.value.难度]; | ||
| 161 | + list = list.filter((item) => item.difficultyLevel === val); | ||
| 162 | + } | ||
| 202 | 163 | ||
| 203 | - // 人群 | ||
| 204 | - if (selectedFilters.value.人群 !== '不限') { | ||
| 205 | - const val = filterMap.人群[selectedFilters.value.人群]; | ||
| 206 | - list = list.filter((item) => item.population === val); | ||
| 207 | - } | 164 | + // 场景 |
| 165 | + if (selectedFilters.value.场景 !== '不限') { | ||
| 166 | + const val = filterMap.场景[selectedFilters.value.场景]; | ||
| 167 | + list = list.filter((item) => item.scene === val); | ||
| 168 | + } | ||
| 208 | 169 | ||
| 209 | - return list; | ||
| 210 | - }); | 170 | + // 人群 |
| 171 | + if (selectedFilters.value.人群 !== '不限') { | ||
| 172 | + const val = filterMap.人群[selectedFilters.value.人群]; | ||
| 173 | + list = list.filter((item) => item.population === val); | ||
| 174 | + } | ||
| 211 | 175 | ||
| 212 | - // ✅ 切换分类时加载数据 | ||
| 213 | - const switchCategory = async (index) => { | ||
| 214 | - activeCategory.value = index; | ||
| 215 | - const categoryId = categoryList.value[index]?.id; | ||
| 216 | - if (categoryId != null) { | ||
| 217 | - // 检查是否已加载过该分类 | ||
| 218 | - const isLoaded = planData.value.some((p) => p._categoryId === categoryId); | ||
| 219 | - if (!isLoaded) { | ||
| 220 | - await loadPlansByCategory(categoryId); | ||
| 221 | - } | ||
| 222 | - } | ||
| 223 | - }; | ||
| 224 | - | ||
| 225 | - // ====== 数据加载 ====== | ||
| 226 | - const loadCategories = async () => { | ||
| 227 | - try { | ||
| 228 | - // 获得计划分类,左侧导航栏显示用 | ||
| 229 | - const res = await QueryPlanApi.getCategories(); | ||
| 230 | - categoryList.value = res.data || []; | ||
| 231 | - console.log('加载计划列表categoryList.value:', categoryList.value); | ||
| 232 | - if (categoryList.value.length > 0) { | ||
| 233 | - await switchCategory(0); // 加载第一个分类 | ||
| 234 | - } | ||
| 235 | - } catch (err) { | ||
| 236 | - console.error('加载分类失败:', err); | ||
| 237 | - uni.showToast({ title: '加载分类失败', icon: 'none' }); | ||
| 238 | - } | ||
| 239 | - }; | ||
| 240 | - | ||
| 241 | - // ✅ 核心:加载计划,并手动附加 _categoryId | ||
| 242 | - const loadPlansByCategory = async (categoryId) => { | ||
| 243 | - try { | ||
| 244 | - // 根据计划分类ID获得计划列表,右侧计划列表显示用 | ||
| 245 | - const res = await QueryPlanApi.getPlanList(categoryId); | ||
| 246 | - console.log('加载计划列表res.data:', res.data); | ||
| 247 | - const plans = (res.data || []).map((item) => ({ | ||
| 248 | - id: item.id, | ||
| 249 | - title: item.name, | ||
| 250 | - meta: `${difficultyText[item.difficultyLevel] || '初阶'} · ${item.enrollmentCount}人练过`, | ||
| 251 | - tag: item.enrollmentCount > 500 ? '火爆' : item.enrollmentCount > 0 ? 'New' : '', | ||
| 252 | - cover: item.urlCover || '默认图', | ||
| 253 | - _categoryId: categoryId, | ||
| 254 | - frequency: item.frequencyPerWeek, // 新增 | ||
| 255 | - difficultyLevel: item.difficultyLevel, // 新增 | ||
| 256 | - scene: item.trainingScene, // 新增 | ||
| 257 | - population: item.targetPeople, // 新增 | ||
| 258 | - })); | ||
| 259 | - // 合并数据(避免重复) | ||
| 260 | - planData.value = [...planData.value.filter((p) => p._categoryId !== categoryId), ...plans]; | ||
| 261 | - // 打印planData | ||
| 262 | - console.log('加载计划列表planData.value:', planData.value); | ||
| 263 | - } catch (err) { | ||
| 264 | - console.error('加载计划失败:', err); | ||
| 265 | - uni.showToast({ title: '加载计划失败', icon: 'none' }); | 176 | + return list; |
| 177 | +}); | ||
| 178 | + | ||
| 179 | +// ✅ 切换分类时加载数据 | ||
| 180 | +const switchCategory = async (index) => { | ||
| 181 | + activeCategory.value = index; | ||
| 182 | + const categoryId = categoryList.value[index]?.id; | ||
| 183 | + if (categoryId != null) { | ||
| 184 | + // 检查是否已加载过该分类 | ||
| 185 | + const isLoaded = planData.value.some((p) => p._categoryId === categoryId); | ||
| 186 | + if (!isLoaded) { | ||
| 187 | + await loadPlansByCategory(categoryId); | ||
| 266 | } | 188 | } |
| 267 | - }; | ||
| 268 | - | ||
| 269 | - // 跳转到搜索页面 | ||
| 270 | - const navigateToSearch = () => { | ||
| 271 | - const app = getApp(); | ||
| 272 | - app.globalData.allPlansForSearch = planData.value; | ||
| 273 | - uni.navigateTo({ | ||
| 274 | - url: '/pages4/pages/xunji/jihua-search', | ||
| 275 | - }); | ||
| 276 | - }; | ||
| 277 | - // 跳转到计划详情页 | ||
| 278 | - const navigateToDetail = (plan) => { | ||
| 279 | - uni.navigateTo({ | ||
| 280 | - url: `/pages4/pages/xunji/xunji-xunlian-jihua?planid=${plan.id}`, | ||
| 281 | - }); | ||
| 282 | - }; | ||
| 283 | - | ||
| 284 | - // ====== 返回按钮 ====== | ||
| 285 | - const back = () => { | ||
| 286 | - const pages = getCurrentPages(); | ||
| 287 | - if (pages.length > 1) { | ||
| 288 | - uni.navigateBack(); | ||
| 289 | - } else { | ||
| 290 | - uni.switchTab({ url: '/pages/index/index' }); // 请替换为你的首页路径 | 189 | + } |
| 190 | +}; | ||
| 191 | + | ||
| 192 | +// ====== 数据加载 ====== | ||
| 193 | +const loadCategories = async () => { | ||
| 194 | + try { | ||
| 195 | + // 获得计划分类,左侧导航栏显示用 | ||
| 196 | + const res = await QueryPlanApi.getCategories(); | ||
| 197 | + categoryList.value = res.data || []; | ||
| 198 | + console.log('加载计划列表categoryList.value:', categoryList.value); | ||
| 199 | + if (categoryList.value.length > 0) { | ||
| 200 | + await switchCategory(0); // 加载第一个分类 | ||
| 291 | } | 201 | } |
| 292 | - }; | ||
| 293 | - | ||
| 294 | - onMounted(() => { | ||
| 295 | - loadCategories(); | 202 | + } catch (err) { |
| 203 | + console.error('加载分类失败:', err); | ||
| 204 | + uni.showToast({ title: '加载分类失败', icon: 'none' }); | ||
| 205 | + } | ||
| 206 | +}; | ||
| 207 | + | ||
| 208 | +// ✅ 核心:加载计划,并手动附加 _categoryId | ||
| 209 | +const loadPlansByCategory = async (categoryId) => { | ||
| 210 | + try { | ||
| 211 | + // 根据计划分类ID获得计划列表,右侧计划列表显示用 | ||
| 212 | + const res = await QueryPlanApi.getPlanList(categoryId); | ||
| 213 | + console.log('加载计划列表res.data:', res.data); | ||
| 214 | + const plans = (res.data || []).map((item) => ({ | ||
| 215 | + id: item.id, | ||
| 216 | + title: item.name, | ||
| 217 | + meta: `${difficultyText[item.difficultyLevel] || '初阶'} · ${item.enrollmentCount}人练过`, | ||
| 218 | + tag: item.enrollmentCount > 500 ? '火爆' : item.enrollmentCount > 0 ? 'New' : '', | ||
| 219 | + cover: item.urlCover || '默认图', | ||
| 220 | + _categoryId: categoryId, | ||
| 221 | + frequency: item.frequencyPerWeek, // 新增 | ||
| 222 | + difficultyLevel: item.difficultyLevel, // 新增 | ||
| 223 | + scene: item.trainingScene, // 新增 | ||
| 224 | + population: item.targetPeople, // 新增 | ||
| 225 | + })); | ||
| 226 | + // 合并数据(避免重复) | ||
| 227 | + planData.value = [...planData.value.filter((p) => p._categoryId !== categoryId), ...plans]; | ||
| 228 | + // 打印planData | ||
| 229 | + console.log('加载计划列表planData.value:', planData.value); | ||
| 230 | + } catch (err) { | ||
| 231 | + console.error('加载计划失败:', err); | ||
| 232 | + uni.showToast({ title: '加载计划失败', icon: 'none' }); | ||
| 233 | + } | ||
| 234 | +}; | ||
| 235 | + | ||
| 236 | +// 跳转到搜索页面 | ||
| 237 | +const navigateToSearch = () => { | ||
| 238 | + const app = getApp(); | ||
| 239 | + app.globalData.allPlansForSearch = planData.value; | ||
| 240 | + uni.navigateTo({ | ||
| 241 | + url: '/pages4/pages/xunji/jihua-search', | ||
| 296 | }); | 242 | }); |
| 297 | -</script> | ||
| 298 | - | ||
| 299 | -<style lang="scss" scoped> | ||
| 300 | - /* ========== 新增:页面头部样式 ========== */ | ||
| 301 | - .page-header { | ||
| 302 | - display: flex; | ||
| 303 | - align-items: center; | ||
| 304 | - padding: 30rpx; | ||
| 305 | - background: #fff; | ||
| 306 | - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); | ||
| 307 | - position: sticky; | ||
| 308 | - top: 0; | ||
| 309 | - z-index: 999; | 243 | +}; |
| 244 | +// 跳转到计划详情页 | ||
| 245 | +const navigateToDetail = (plan) => { | ||
| 246 | + uni.navigateTo({ | ||
| 247 | + url: `/pages4/pages/xunji/xunji-xunlian-jihua?planid=${plan.id}`, | ||
| 248 | + }); | ||
| 249 | +}; | ||
| 250 | + | ||
| 251 | +// ====== 返回按钮 ====== | ||
| 252 | +const back = () => { | ||
| 253 | + const pages = getCurrentPages(); | ||
| 254 | + if (pages.length > 1) { | ||
| 255 | + uni.navigateBack(); | ||
| 256 | + } else { | ||
| 257 | + uni.switchTab({ url: '/pages/index/index' }); // 请替换为你的首页路径 | ||
| 310 | } | 258 | } |
| 259 | +}; | ||
| 311 | 260 | ||
| 312 | - .page-title { | ||
| 313 | - font-size: 32rpx; | ||
| 314 | - font-weight: bold; | ||
| 315 | - color: #333; | ||
| 316 | - flex: 1; | ||
| 317 | - text-align: center; | ||
| 318 | - } | 261 | +onMounted(() => { |
| 262 | + loadCategories(); | ||
| 263 | +}); | ||
| 264 | +</script> | ||
| 319 | 265 | ||
| 320 | - /* ========== 以下是你原有的全部样式(完全保留) ========== */ | ||
| 321 | - .plan-page { | 266 | +<style lang="scss" scoped> |
| 267 | +/* ========== 新增:页面头部样式 ========== */ | ||
| 268 | +// .page-header { | ||
| 269 | +// display: flex; | ||
| 270 | +// align-items: center; | ||
| 271 | +// padding: 30rpx; | ||
| 272 | +// background: #fff; | ||
| 273 | +// box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); | ||
| 274 | +// position: sticky; | ||
| 275 | +// top: 0; | ||
| 276 | +// z-index: 999; | ||
| 277 | +// } | ||
| 278 | + | ||
| 279 | +// .page-title { | ||
| 280 | +// font-size: 32rpx; | ||
| 281 | +// font-weight: bold; | ||
| 282 | +// color: #333; | ||
| 283 | +// flex: 1; | ||
| 284 | +// text-align: center; | ||
| 285 | +// } | ||
| 286 | + | ||
| 287 | +/* ========== 以下是你原有的全部样式(完全保留) ========== */ | ||
| 288 | +.plan-page { | ||
| 289 | + width: 100%; | ||
| 290 | + height: 100vh; | ||
| 291 | + background-color: #f5f5f5; | ||
| 292 | + box-sizing: border-box; | ||
| 293 | + overflow: hidden; | ||
| 294 | + display: flex; | ||
| 295 | + flex-direction: column; | ||
| 296 | +} | ||
| 297 | + | ||
| 298 | +/* 顶部横幅 */ | ||
| 299 | +.banner-wrapper { | ||
| 300 | + height: 250rpx; | ||
| 301 | + width: 100%; | ||
| 302 | + margin: 20rpx 0; | ||
| 303 | + | ||
| 304 | + .swiper { | ||
| 322 | width: 100%; | 305 | width: 100%; |
| 323 | height: 100%; | 306 | height: 100%; |
| 324 | - background-color: #f5f5f5; | ||
| 325 | - box-sizing: border-box; | ||
| 326 | - overflow: hidden; | ||
| 327 | - display: flex; | ||
| 328 | - flex-direction: column; | ||
| 329 | - } | ||
| 330 | - | ||
| 331 | - /* 顶部横幅 */ | ||
| 332 | - .banner-wrapper { | ||
| 333 | - height: 250rpx; | ||
| 334 | - width: 100%; | ||
| 335 | - margin: 20rpx 0; | ||
| 336 | 307 | ||
| 337 | - .swiper { | 308 | + .swiper-item { |
| 338 | width: 100%; | 309 | width: 100%; |
| 339 | height: 100%; | 310 | height: 100%; |
| 311 | + padding: 20rpx; | ||
| 312 | + box-sizing: border-box; | ||
| 340 | 313 | ||
| 341 | - .swiper-item { | 314 | + .img { |
| 342 | width: 100%; | 315 | width: 100%; |
| 343 | - height: 100%; | ||
| 344 | - padding: 20rpx; | ||
| 345 | - box-sizing: border-box; | ||
| 346 | - | ||
| 347 | - .img { | ||
| 348 | - width: 100%; | ||
| 349 | - height: 220rpx; | ||
| 350 | - border-radius: 20rpx; | ||
| 351 | - } | 316 | + height: 220rpx; |
| 317 | + border-radius: 20rpx; | ||
| 352 | } | 318 | } |
| 353 | } | 319 | } |
| 354 | } | 320 | } |
| 355 | - | ||
| 356 | - /* 筛选条 */ | ||
| 357 | - .filter-bar { | ||
| 358 | - margin: 0 20rpx 20rpx; | ||
| 359 | - padding: 12rpx 18rpx; | ||
| 360 | - border-radius: 40rpx; | ||
| 361 | - display: flex; | 321 | +} |
| 322 | + | ||
| 323 | +/* 筛选条 */ | ||
| 324 | +.filter-bar { | ||
| 325 | + margin: 0 20rpx 20rpx; | ||
| 326 | + padding: 12rpx 18rpx; | ||
| 327 | + border-radius: 40rpx; | ||
| 328 | + display: flex; | ||
| 329 | + align-items: center; | ||
| 330 | + box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.03); | ||
| 331 | + | ||
| 332 | + .filter-item { | ||
| 333 | + flex-direction: row; | ||
| 362 | align-items: center; | 334 | align-items: center; |
| 363 | - box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.03); | ||
| 364 | - | ||
| 365 | - .filter-item { | ||
| 366 | - flex-direction: row; | ||
| 367 | - align-items: center; | ||
| 368 | - padding: 8rpx 18rpx; | ||
| 369 | - border-radius: 30rpx; | ||
| 370 | - border: 1px solid #c5c5c5; | ||
| 371 | - margin-right: 10rpx; | ||
| 372 | - background-color: #f7f7f7; | ||
| 373 | - display: flex; | ||
| 374 | - | ||
| 375 | - .text { | ||
| 376 | - font-size: 24rpx; | ||
| 377 | - color: #666; | ||
| 378 | - margin-right: 6rpx; | ||
| 379 | - } | ||
| 380 | - | ||
| 381 | - &.active { | ||
| 382 | - background-color: #e6f7f0; | ||
| 383 | - | ||
| 384 | - .text { | ||
| 385 | - color: #26c165; | ||
| 386 | - } | ||
| 387 | - } | ||
| 388 | - } | ||
| 389 | - | ||
| 390 | - .filter-search { | ||
| 391 | - margin-left: auto; | ||
| 392 | - width: 56rpx; | ||
| 393 | - height: 56rpx; | ||
| 394 | - border-radius: 50%; | ||
| 395 | - background-color: #f7f7f7; | ||
| 396 | - display: flex; | ||
| 397 | - align-items: center; | ||
| 398 | - justify-content: center; | ||
| 399 | - } | ||
| 400 | - } | 335 | + padding: 8rpx 18rpx; |
| 336 | + border-radius: 30rpx; | ||
| 337 | + border: 1px solid #c5c5c5; | ||
| 338 | + margin-right: 10rpx; | ||
| 339 | + background-color: #f7f7f7; | ||
| 340 | + display: flex; | ||
| 401 | 341 | ||
| 402 | - //下拉抽屉 | ||
| 403 | - .filter-drawer { | ||
| 404 | - position: absolute; | ||
| 405 | - top: 560rpx; | ||
| 406 | - left: 20rpx; | ||
| 407 | - right: 20rpx; | ||
| 408 | - z-index: 999; | ||
| 409 | - border-radius: 0 0 40rpx 40rpx; | ||
| 410 | - background-color: #fff; | ||
| 411 | - box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.06); | ||
| 412 | - overflow: hidden; | ||
| 413 | - | ||
| 414 | - .drawer-content { | ||
| 415 | - padding: 10rpx 0; | 342 | + .text { |
| 343 | + font-size: 24rpx; | ||
| 344 | + color: #666; | ||
| 345 | + margin-right: 6rpx; | ||
| 416 | } | 346 | } |
| 417 | 347 | ||
| 418 | - .drawer-item { | ||
| 419 | - padding: 20rpx 30rpx; | ||
| 420 | - font-size: 26rpx; | ||
| 421 | - color: #333; | ||
| 422 | - transition: background-color 0.2s ease; | 348 | + &.active { |
| 349 | + background-color: #e6f7f0; | ||
| 423 | 350 | ||
| 424 | - &:active { | ||
| 425 | - background-color: #f5f5f5; | ||
| 426 | - } | ||
| 427 | - | ||
| 428 | - .drawer-item-text { | ||
| 429 | - display: block; | ||
| 430 | - width: 100%; | 351 | + .text { |
| 352 | + color: #26c165; | ||
| 431 | } | 353 | } |
| 432 | } | 354 | } |
| 433 | } | 355 | } |
| 434 | 356 | ||
| 435 | - /* 主体布局 */ | ||
| 436 | - .content-layout { | ||
| 437 | - flex: 1; | 357 | + .filter-search { |
| 358 | + margin-left: auto; | ||
| 359 | + width: 56rpx; | ||
| 360 | + height: 56rpx; | ||
| 361 | + border-radius: 50%; | ||
| 362 | + background-color: #f7f7f7; | ||
| 438 | display: flex; | 363 | display: flex; |
| 439 | - box-sizing: border-box; | ||
| 440 | - overflow: hidden; | ||
| 441 | - min-height: 0; | 364 | + align-items: center; |
| 365 | + justify-content: center; | ||
| 366 | + } | ||
| 367 | +} | ||
| 368 | + | ||
| 369 | +//下拉抽屉 | ||
| 370 | +.filter-drawer { | ||
| 371 | + position: absolute; | ||
| 372 | + top: 560rpx; | ||
| 373 | + | ||
| 374 | + /* #ifdef H5 */ | ||
| 375 | + top: 247px; | ||
| 376 | + /* #endif */ | ||
| 377 | + | ||
| 378 | + left: 20rpx; | ||
| 379 | + right: 20rpx; | ||
| 380 | + z-index: 999; | ||
| 381 | + border-radius: 0 0 40rpx 40rpx; | ||
| 382 | + background-color: #fff; | ||
| 383 | + box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.06); | ||
| 384 | + overflow: hidden; | ||
| 385 | + | ||
| 386 | + .drawer-content { | ||
| 387 | + padding: 10rpx 0; | ||
| 442 | } | 388 | } |
| 443 | 389 | ||
| 444 | - /* 左侧分类 */ | ||
| 445 | - .sidebar { | ||
| 446 | - width: 180rpx; | ||
| 447 | - background-color: #ffffff; | ||
| 448 | - border-radius: 16rpx; | ||
| 449 | - height: 100%; | ||
| 450 | - box-sizing: border-box; | ||
| 451 | - overflow: hidden; | 390 | + .drawer-item { |
| 391 | + padding: 20rpx 30rpx; | ||
| 392 | + font-size: 26rpx; | ||
| 393 | + color: #333; | ||
| 394 | + transition: background-color 0.2s ease; | ||
| 452 | 395 | ||
| 453 | - .category-item { | ||
| 454 | - padding: 22rpx 24rpx; | ||
| 455 | - font-size: 24rpx; | ||
| 456 | - color: #666; | ||
| 457 | - position: relative; | 396 | + &:active { |
| 397 | + background-color: #f5f5f5; | ||
| 398 | + } | ||
| 458 | 399 | ||
| 459 | - &.active { | ||
| 460 | - background-color: #e8f8f0; | ||
| 461 | - color: #26c165; | ||
| 462 | - font-weight: 600; | 400 | + .drawer-item-text { |
| 401 | + display: block; | ||
| 402 | + width: 100%; | ||
| 403 | + } | ||
| 404 | + } | ||
| 405 | +} | ||
| 406 | + | ||
| 407 | +/* 主体布局 */ | ||
| 408 | +.content-layout { | ||
| 409 | + flex: 1; | ||
| 410 | + display: flex; | ||
| 411 | + box-sizing: border-box; | ||
| 412 | + overflow: hidden; | ||
| 413 | + min-height: 0; | ||
| 414 | +} | ||
| 415 | + | ||
| 416 | +/* 左侧分类 */ | ||
| 417 | +.sidebar { | ||
| 418 | + width: 180rpx; | ||
| 419 | + background-color: #ffffff; | ||
| 420 | + border-radius: 16rpx; | ||
| 421 | + height: 72%; | ||
| 422 | + /* #ifdef H5 */ | ||
| 423 | + height: 86%; | ||
| 424 | + /* #endif */ | ||
| 425 | + box-sizing: border-box; | ||
| 426 | + overflow: hidden; | ||
| 427 | + | ||
| 428 | + .category-item { | ||
| 429 | + padding: 22rpx 24rpx; | ||
| 430 | + font-size: 24rpx; | ||
| 431 | + color: #666; | ||
| 432 | + position: relative; | ||
| 463 | 433 | ||
| 464 | - &::before { | ||
| 465 | - content: ''; | ||
| 466 | - position: absolute; | ||
| 467 | - right: 0; | ||
| 468 | - top: 16rpx; | ||
| 469 | - bottom: 16rpx; | ||
| 470 | - width: 6rpx; | ||
| 471 | - border-radius: 0 4rpx 4rpx 0; | ||
| 472 | - background-color: #26c165; | ||
| 473 | - } | 434 | + &.active { |
| 435 | + background-color: #e8f8f0; | ||
| 436 | + color: #26c165; | ||
| 437 | + font-weight: 600; | ||
| 438 | + | ||
| 439 | + &::before { | ||
| 440 | + content: ''; | ||
| 441 | + position: absolute; | ||
| 442 | + right: 0; | ||
| 443 | + top: 16rpx; | ||
| 444 | + bottom: 16rpx; | ||
| 445 | + width: 6rpx; | ||
| 446 | + border-radius: 0 4rpx 4rpx 0; | ||
| 447 | + background-color: #26c165; | ||
| 474 | } | 448 | } |
| 475 | } | 449 | } |
| 476 | } | 450 | } |
| 477 | - | ||
| 478 | - /* 右侧计划列表 */ | ||
| 479 | - .plan-list-wrap { | ||
| 480 | - flex: 1; | ||
| 481 | - padding: 0 20rpx; | ||
| 482 | - box-sizing: border-box; | ||
| 483 | - height: 100%; | ||
| 484 | - overflow: hidden; | 451 | +} |
| 452 | + | ||
| 453 | +/* 右侧计划列表 */ | ||
| 454 | +.plan-list-wrap { | ||
| 455 | + flex: 1; | ||
| 456 | + padding: 0 20rpx; | ||
| 457 | + box-sizing: border-box; | ||
| 458 | + height: 72%; | ||
| 459 | + /* #ifdef H5 */ | ||
| 460 | + height: 86%; | ||
| 461 | + /* #endif */ | ||
| 462 | + overflow: hidden; | ||
| 463 | +} | ||
| 464 | + | ||
| 465 | +.plan-card { | ||
| 466 | + position: relative; | ||
| 467 | + margin-bottom: 16rpx; | ||
| 468 | + border-radius: 16rpx; | ||
| 469 | + | ||
| 470 | + .plan-cover { | ||
| 471 | + width: 100%; | ||
| 472 | + height: 230rpx; | ||
| 473 | + border-radius: 5rpx; | ||
| 485 | } | 474 | } |
| 486 | 475 | ||
| 487 | - .plan-card { | ||
| 488 | - position: relative; | ||
| 489 | - margin-bottom: 16rpx; | ||
| 490 | - border-radius: 16rpx; | ||
| 491 | - | ||
| 492 | - .plan-cover { | ||
| 493 | - width: 100%; | ||
| 494 | - height: 230rpx; | ||
| 495 | - border-radius: 5rpx; | ||
| 496 | - } | 476 | + .plan-info { |
| 477 | + position: absolute; | ||
| 478 | + left: 20rpx; | ||
| 479 | + right: 20rpx; | ||
| 480 | + bottom: 20rpx; | ||
| 481 | + color: #fff; | ||
| 497 | 482 | ||
| 498 | - .plan-info { | ||
| 499 | - position: absolute; | ||
| 500 | - left: 20rpx; | ||
| 501 | - right: 20rpx; | ||
| 502 | - bottom: 20rpx; | ||
| 503 | - color: #fff; | ||
| 504 | - | ||
| 505 | - .plan-tag-row { | ||
| 506 | - margin-bottom: 8rpx; | ||
| 507 | - | ||
| 508 | - .plan-tag { | ||
| 509 | - display: inline-flex; | ||
| 510 | - padding: 4rpx 10rpx; | ||
| 511 | - border-radius: 6rpx; | ||
| 512 | - font-size: 20rpx; | ||
| 513 | - line-height: 1; | ||
| 514 | - | ||
| 515 | - &.tag-hot { | ||
| 516 | - background-color: #ff4d4f; | ||
| 517 | - } | ||
| 518 | - | ||
| 519 | - &.tag-new { | ||
| 520 | - background-color: #ffbb00; | ||
| 521 | - } | ||
| 522 | - } | ||
| 523 | - } | 483 | + .plan-tag-row { |
| 484 | + margin-bottom: 8rpx; | ||
| 524 | 485 | ||
| 525 | - .plan-text { | ||
| 526 | - display: flex; | ||
| 527 | - flex-direction: column; | 486 | + .plan-tag { |
| 487 | + display: inline-flex; | ||
| 488 | + padding: 4rpx 10rpx; | ||
| 489 | + border-radius: 6rpx; | ||
| 490 | + font-size: 20rpx; | ||
| 491 | + line-height: 1; | ||
| 528 | 492 | ||
| 529 | - .plan-title { | ||
| 530 | - font-size: 28rpx; | ||
| 531 | - font-weight: 600; | ||
| 532 | - margin-bottom: 6rpx; | 493 | + &.tag-hot { |
| 494 | + background-color: #ff4d4f; | ||
| 533 | } | 495 | } |
| 534 | 496 | ||
| 535 | - .plan-meta { | ||
| 536 | - font-size: 22rpx; | ||
| 537 | - color: rgba(255, 255, 255, 0.8); | 497 | + &.tag-new { |
| 498 | + background-color: #ffbb00; | ||
| 538 | } | 499 | } |
| 539 | } | 500 | } |
| 540 | } | 501 | } |
| 541 | - } | ||
| 542 | 502 | ||
| 543 | - /* 空状态 */ | ||
| 544 | - .empty-tip { | ||
| 545 | - text-align: center; | ||
| 546 | - padding: 100rpx 0; | ||
| 547 | - color: #999; | ||
| 548 | - font-size: 28rpx; | 503 | + .plan-text { |
| 504 | + display: flex; | ||
| 505 | + flex-direction: column; | ||
| 506 | + | ||
| 507 | + .plan-title { | ||
| 508 | + font-size: 28rpx; | ||
| 509 | + font-weight: 600; | ||
| 510 | + margin-bottom: 6rpx; | ||
| 511 | + } | ||
| 512 | + | ||
| 513 | + .plan-meta { | ||
| 514 | + font-size: 22rpx; | ||
| 515 | + color: rgba(255, 255, 255, 0.8); | ||
| 516 | + } | ||
| 517 | + } | ||
| 549 | } | 518 | } |
| 519 | +} | ||
| 520 | + | ||
| 521 | +/* 空状态 */ | ||
| 522 | +.empty-tip { | ||
| 523 | + text-align: center; | ||
| 524 | + padding: 100rpx 0; | ||
| 525 | + color: #999; | ||
| 526 | + font-size: 28rpx; | ||
| 527 | +} | ||
| 550 | </style> | 528 | </style> |
| @@ -317,8 +317,13 @@ const saveTemplateTitle = () => { | @@ -317,8 +317,13 @@ const saveTemplateTitle = () => { | ||
| 317 | 317 | ||
| 318 | // 返回上一页 | 318 | // 返回上一页 |
| 319 | const goBack = () => { | 319 | const goBack = () => { |
| 320 | - uni.navigateBack(); | ||
| 321 | trainingStore.clearTrainingStore() | 320 | trainingStore.clearTrainingStore() |
| 321 | + setTimeout(() => { | ||
| 322 | + // 发送全局刷新信号,列表页会立刻执行刷新 | ||
| 323 | + uni.$emit('refreshTemplateList'); | ||
| 324 | + uni.navigateBack(); | ||
| 325 | + }, 1500); | ||
| 326 | + | ||
| 322 | }; | 327 | }; |
| 323 | 328 | ||
| 324 | // 保存模板(完整修复间歇训练 type=6) | 329 | // 保存模板(完整修复间歇训练 type=6) |
| @@ -399,12 +404,10 @@ const handleSave = async () => { | @@ -399,12 +404,10 @@ const handleSave = async () => { | ||
| 399 | console.log('保存成功', res); | 404 | console.log('保存成功', res); |
| 400 | uni.hideLoading(); | 405 | uni.hideLoading(); |
| 401 | uni.showToast({ title: '保存成功' }); | 406 | uni.showToast({ title: '保存成功' }); |
| 402 | - | 407 | + trainingStore.clearTrainingStore(); |
| 403 | setTimeout(() => { | 408 | setTimeout(() => { |
| 404 | - uni.redirectTo({ | ||
| 405 | - url: '/pages4/pages/xunji/xunji-wode-moban' | ||
| 406 | - }); | ||
| 407 | - trainingStore.clearTrainingStore(); | 409 | + uni.navigateBack(); |
| 410 | + | ||
| 408 | }, 1500); | 411 | }, 1500); |
| 409 | 412 | ||
| 410 | } catch (err) { | 413 | } catch (err) { |
| @@ -173,6 +173,7 @@ import dailytemplateApi from '@/sheep/api/Template/Dailytemplate'; | @@ -173,6 +173,7 @@ import dailytemplateApi from '@/sheep/api/Template/Dailytemplate'; | ||
| 173 | import { useTrainingStore } from '@/sheep/store/trainingStore' | 173 | import { useTrainingStore } from '@/sheep/store/trainingStore' |
| 174 | import addActions from '@/pages/xunji/components/dongzuo-lianxi/add-actions.vue' | 174 | import addActions from '@/pages/xunji/components/dongzuo-lianxi/add-actions.vue' |
| 175 | import ActionSort from '@/pages/xunji/components/dongzuo-lianxi/dongzuo-paixu.vue' | 175 | import ActionSort from '@/pages/xunji/components/dongzuo-lianxi/dongzuo-paixu.vue' |
| 176 | +import { trainingSnapshot } from '@/sheep/store/trainingSnapshot' | ||
| 176 | 177 | ||
| 177 | const trainingStore = useTrainingStore() | 178 | const trainingStore = useTrainingStore() |
| 178 | const hasConverted = ref(false); | 179 | const hasConverted = ref(false); |
| @@ -595,6 +596,39 @@ const openMin = () => { | @@ -595,6 +596,39 @@ const openMin = () => { | ||
| 595 | 596 | ||
| 596 | console.log('最小化跳转后trainingStore.isTraining', trainingStore.isTraining); | 597 | console.log('最小化跳转后trainingStore.isTraining', trainingStore.isTraining); |
| 597 | trainingStore.min = true; | 598 | trainingStore.min = true; |
| 599 | + // 保存快照 | ||
| 600 | + trainingSnapshot.data = JSON.parse(JSON.stringify({ | ||
| 601 | + id: trainingStore.id, | ||
| 602 | + type: trainingStore.type, | ||
| 603 | + actionDetail: trainingStore.actionDetail, | ||
| 604 | + loading: trainingStore.loading, | ||
| 605 | + unitRecords: trainingStore.unitRecords, | ||
| 606 | + trainingName: trainingStore.trainingName, | ||
| 607 | + totalSeconds: trainingStore.totalSeconds, | ||
| 608 | + isPause: trainingStore.isPause, | ||
| 609 | + showPicker: trainingStore.showPicker, | ||
| 610 | + defaultTimeIndex: trainingStore.defaultTimeIndex, | ||
| 611 | + trainingTimeText: trainingStore.trainingTimeText, | ||
| 612 | + min: trainingStore.min, | ||
| 613 | + isSystem: trainingStore.isSystem, | ||
| 614 | + dailyTemplateId: trainingStore.dailyTemplateId, | ||
| 615 | + isTraining: trainingStore.isTraining, | ||
| 616 | + })) | ||
| 617 | + | ||
| 618 | + // 2. 记录时间戳和计时器状态 | ||
| 619 | + trainingSnapshot.timestamp = Date.now() | ||
| 620 | + trainingSnapshot.timerWasRunning = !trainingStore.isPause | ||
| 621 | + | ||
| 622 | + // 3. 显示悬浮球 | ||
| 623 | + trainingSnapshot.visible = true | ||
| 624 | + | ||
| 625 | + // 4. 如果计时器在跑,先暂停(setInterval 跨页面会丢失,回显时用时间戳补偿) | ||
| 626 | + if (!trainingStore.isPause) { | ||
| 627 | + trainingStore.toggleTimer() | ||
| 628 | + } | ||
| 629 | + | ||
| 630 | + console.log('🟡 快照已保存:id=', trainingSnapshot.data.id, | ||
| 631 | + 'totalSeconds=', trainingSnapshot.data.totalSeconds) | ||
| 598 | // uni.navigateTo({ | 632 | // uni.navigateTo({ |
| 599 | // url: '/pages/xunji/components/xunji-dongzuo', // 改成你训练页面的实际路径 | 633 | // url: '/pages/xunji/components/xunji-dongzuo', // 改成你训练页面的实际路径 |
| 600 | // }); | 634 | // }); |
| @@ -55,7 +55,10 @@ | @@ -55,7 +55,10 @@ | ||
| 55 | </view> | 55 | </view> |
| 56 | <!-- 动作列表 --> | 56 | <!-- 动作列表 --> |
| 57 | <view class="section"> | 57 | <view class="section"> |
| 58 | - <text class="section-title">动作列表</text> | 58 | + <view class="section-top"> |
| 59 | + <text class="section-title">动作列表</text> | ||
| 60 | + <view v-if="date" class="right-btn" @click="templateEdit(TemplateDetail)">修改训练内容</view> | ||
| 61 | + </view> | ||
| 59 | <view v-if="TemplateUnits.length > 0"> | 62 | <view v-if="TemplateUnits.length > 0"> |
| 60 | <!-- 循环 unit,每个 unit 一个卡片 --> | 63 | <!-- 循环 unit,每个 unit 一个卡片 --> |
| 61 | <view v-for="(unit, unitIndex) in TemplateUnits" :key="unitIndex" class="exercise-item" | 64 | <view v-for="(unit, unitIndex) in TemplateUnits" :key="unitIndex" class="exercise-item" |
| @@ -111,9 +114,9 @@ | @@ -111,9 +114,9 @@ | ||
| 111 | {{ formatSeconds(detail.duration) }} | 114 | {{ formatSeconds(detail.duration) }} |
| 112 | </view> | 115 | </view> |
| 113 | <view class="detail-value" v-if="unit.exercises[0].exerciseType === 6"> | 116 | <view class="detail-value" v-if="unit.exercises[0].exerciseType === 6"> |
| 114 | - {{ detail.duration }}组 x {{ formatSeconds(detail.duration) }} | 117 | + {{ detail.reps }}组 x {{ detail.duration }}秒 x {{ detail.restTime }}秒/组休息 |
| 115 | </view> | 118 | </view> |
| 116 | - <view class="rest" v-if="detail.restTime"> | 119 | + <view class="rest" v-if="detail.restTime && unit.exercises[0].exerciseType !== 6"> |
| 117 | {{ detail.restTime }}s | 120 | {{ detail.restTime }}s |
| 118 | </view> | 121 | </view> |
| 119 | </view> | 122 | </view> |
| @@ -158,9 +161,10 @@ | @@ -158,9 +161,10 @@ | ||
| 158 | {{ formatSeconds(ex.sets[idx - 1].duration) }} | 161 | {{ formatSeconds(ex.sets[idx - 1].duration) }} |
| 159 | </text> | 162 | </text> |
| 160 | <text v-if="ex.exerciseType === 6"> | 163 | <text v-if="ex.exerciseType === 6"> |
| 161 | - {{ ex.sets[idx - 1].duration }}组 x {{ formatSeconds(ex.sets[idx - 1].duration) }} | 164 | + {{ ex.sets[idx - 1].reps }}组 x {{ ex.sets[idx - 1].duration }}秒 x {{ ex.sets[idx - 1].restTime |
| 165 | + }}秒/组休息 | ||
| 162 | </text> | 166 | </text> |
| 163 | - <view class="rest" v-if="ex.sets[idx - 1].restTime"> | 167 | + <view class="rest" v-if="ex.sets[idx - 1].restTime && ex.exerciseType !== 6"> |
| 164 | {{ ex.sets[idx - 1].restTime }}s | 168 | {{ ex.sets[idx - 1].restTime }}s |
| 165 | </view> | 169 | </view> |
| 166 | </view> | 170 | </view> |
| @@ -534,6 +538,20 @@ const isUnlocked = computed(() => { | @@ -534,6 +538,20 @@ const isUnlocked = computed(() => { | ||
| 534 | const openActionItem = (item) => { | 538 | const openActionItem = (item) => { |
| 535 | open(item.id, 1); | 539 | open(item.id, 1); |
| 536 | }; | 540 | }; |
| 541 | +// 修改每日模板训练内容 | ||
| 542 | +const templateEdit = (TemplateDetail) => { | ||
| 543 | + | ||
| 544 | + console.log('开始进入编辑模板页面,模板ID:', TemplateDetail.id, '每日模板ID:', TemplateDetail.dailyTemplateId); | ||
| 545 | + | ||
| 546 | + trainingStore.isSystem = TemplateDetail.isSystem; | ||
| 547 | + trainingStore.loadDailyTemplateForEdit(TemplateDetail); | ||
| 548 | + trainingStore.initDailyTemplateRecords() | ||
| 549 | + | ||
| 550 | + uni.navigateTo({ | ||
| 551 | + url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${TemplateDetail.id}&type=3&dailyTemplateId=${TemplateDetail.dailyTemplateId}`, | ||
| 552 | + }); | ||
| 553 | + | ||
| 554 | +}; | ||
| 537 | 555 | ||
| 538 | const isMyPlan = ref(false) | 556 | const isMyPlan = ref(false) |
| 539 | const isDailytemplateId = ref(false) | 557 | const isDailytemplateId = ref(false) |
| @@ -645,6 +663,46 @@ onLoad((options) => { | @@ -645,6 +663,46 @@ onLoad((options) => { | ||
| 645 | border-bottom: 1px solid #333; | 663 | border-bottom: 1px solid #333; |
| 646 | } | 664 | } |
| 647 | 665 | ||
| 666 | +.section-top { | ||
| 667 | + display: flex; | ||
| 668 | + justify-content: space-between; | ||
| 669 | + align-items: center; | ||
| 670 | + margin-bottom: 20rpx; | ||
| 671 | +} | ||
| 672 | + | ||
| 673 | +.section-title { | ||
| 674 | + font-size: 32rpx; | ||
| 675 | + font-weight: bold; | ||
| 676 | + // margin-bottom: 20rpx; | ||
| 677 | +} | ||
| 678 | + | ||
| 679 | +.right-btn { | ||
| 680 | + font-size: 24rpx; | ||
| 681 | + width: 140rpx; | ||
| 682 | + /* #ifdef H5 */ | ||
| 683 | + width: 99px; | ||
| 684 | + /* #endif */ | ||
| 685 | + height: 37rpx; | ||
| 686 | + padding: 10rpx 20rpx; | ||
| 687 | + background: rgba(90, 90, 90, 0.45); | ||
| 688 | + justify-content: center; | ||
| 689 | + display: flex; | ||
| 690 | + align-items: center; | ||
| 691 | + border-radius: 20rpx; | ||
| 692 | +} | ||
| 693 | + | ||
| 694 | +.description { | ||
| 695 | + font-size: 28rpx; | ||
| 696 | + margin-bottom: 10rpx; | ||
| 697 | +} | ||
| 698 | + | ||
| 699 | +.empty-tip { | ||
| 700 | + text-align: center; | ||
| 701 | + padding: 40rpx 0; | ||
| 702 | + color: #999; | ||
| 703 | + font-size: 28rpx; | ||
| 704 | +} | ||
| 705 | + | ||
| 648 | .section-title { | 706 | .section-title { |
| 649 | font-size: 32rpx; | 707 | font-size: 32rpx; |
| 650 | font-weight: bold; | 708 | font-weight: bold; |
| @@ -3,20 +3,39 @@ | @@ -3,20 +3,39 @@ | ||
| 3 | <template> | 3 | <template> |
| 4 | <view class="plan-detail-page"> | 4 | <view class="plan-detail-page"> |
| 5 | <!-- 顶部安全区占位 --> | 5 | <!-- 顶部安全区占位 --> |
| 6 | - <view class="status-bar-placeholder" :style="{ height: statusBarHeight + 'px' }"></view> | 6 | + <!-- <view class="status-bar-placeholder" :style="{ height: statusBarHeight + 'px' }"></view> --> |
| 7 | <!-- 顶部导航栏 这里是模板大类的名字--> | 7 | <!-- 顶部导航栏 这里是模板大类的名字--> |
| 8 | - <view class="header" :style="{ height: headerHeight + 'px' }"> | 8 | + <!-- <view class="header"> |
| 9 | <view class="back-btn" @click="navigateBack"> | 9 | <view class="back-btn" @click="navigateBack"> |
| 10 | <uni-icons class="back-icon" type="left" size="28" color="#fff"></uni-icons> | 10 | <uni-icons class="back-icon" type="left" size="28" color="#fff"></uni-icons> |
| 11 | </view> | 11 | </view> |
| 12 | <view class="title">{{ templateList.name }}</view> | 12 | <view class="title">{{ templateList.name }}</view> |
| 13 | - </view> | ||
| 14 | 13 | ||
| 15 | - <scroll-view class="content" scroll-y enable-backdrop-filter="{{false}}"> | ||
| 16 | <view class="description"> | 14 | <view class="description"> |
| 17 | - <text>{{ templateList.description }}</text> | 15 | + <text>{{ templateList?.description }}</text> |
| 16 | + </view> | ||
| 17 | + </view> --> | ||
| 18 | + | ||
| 19 | + <view class="page-header" hover-class="none"> | ||
| 20 | + <!-- 动态导航栏:高度与胶囊对齐 --> | ||
| 21 | + <view class="nav-bar" :style="{ | ||
| 22 | + paddingTop: menuButtonInfo.top + 'px', | ||
| 23 | + height: menuButtonInfo.height + 'px' | ||
| 24 | + }"> | ||
| 25 | + <view class="nav-left" @click="goBack"> | ||
| 26 | + <uni-icons class="back-icon" type="left" size="28" color="#fff"></uni-icons> | ||
| 27 | + </view> | ||
| 28 | + <view class="nav-title">{{ templateList.name }}</view> | ||
| 29 | + <view class="nav-right"></view> | ||
| 30 | + </view> | ||
| 31 | + <view class="description"> | ||
| 32 | + {{ templateList?.description }} | ||
| 18 | </view> | 33 | </view> |
| 19 | 34 | ||
| 35 | + </view> | ||
| 36 | + | ||
| 37 | + <scroll-view class="content" scroll-y enable-backdrop-filter="{{false}}"> | ||
| 38 | + | ||
| 20 | <view class="filter-bar"> | 39 | <view class="filter-bar"> |
| 21 | 40 | ||
| 22 | <!-- 部位筛选 --> | 41 | <!-- 部位筛选 --> |
| @@ -109,11 +128,11 @@ const filteredTemplates = ref([]); // 筛选后列表 | @@ -109,11 +128,11 @@ const filteredTemplates = ref([]); // 筛选后列表 | ||
| 109 | const partList = ref([]); | 128 | const partList = ref([]); |
| 110 | const rawPartData = ref([]); | 129 | const rawPartData = ref([]); |
| 111 | const activePartId = ref('0'); | 130 | const activePartId = ref('0'); |
| 112 | -const activePart = ref('不限'); | 131 | +const activePart = ref('部位'); |
| 113 | 132 | ||
| 114 | // 场景(固定) | 133 | // 场景(固定) |
| 115 | const activeSceneId = ref('0'); | 134 | const activeSceneId = ref('0'); |
| 116 | -const activeScene = ref('不限'); | 135 | +const activeScene = ref('场景'); |
| 117 | const sceneList = ref([ | 136 | const sceneList = ref([ |
| 118 | { id: '0', title: '不限' }, | 137 | { id: '0', title: '不限' }, |
| 119 | { id: '1', title: '健身房' }, | 138 | { id: '1', title: '健身房' }, |
| @@ -129,7 +148,7 @@ const templateMenuRef = ref(null) | @@ -129,7 +148,7 @@ const templateMenuRef = ref(null) | ||
| 129 | const showPartDropdown = ref(false); | 148 | const showPartDropdown = ref(false); |
| 130 | const showSceneDropdown = ref(false); | 149 | const showSceneDropdown = ref(false); |
| 131 | // 返回上一页函数 | 150 | // 返回上一页函数 |
| 132 | -const navigateBack = () => { | 151 | +const goBack = () => { |
| 133 | uni.navigateBack(); | 152 | uni.navigateBack(); |
| 134 | }; | 153 | }; |
| 135 | // 获取部位分类(接口,和首页完全一样) | 154 | // 获取部位分类(接口,和首页完全一样) |
| @@ -269,7 +288,7 @@ onLoad((options) => { | @@ -269,7 +288,7 @@ onLoad((options) => { | ||
| 269 | isIcon = true | 288 | isIcon = true |
| 270 | } | 289 | } |
| 271 | console.log("模板大类ID:", id.value); | 290 | console.log("模板大类ID:", id.value); |
| 272 | - console.log("是否官方大类:", isBigTemOffice); | 291 | + console.log("是否官方大类:", isBigTemOffice.value); |
| 273 | 292 | ||
| 274 | loadTemplates(id.value); | 293 | loadTemplates(id.value); |
| 275 | }); | 294 | }); |
| @@ -277,6 +296,41 @@ onMounted(() => { | @@ -277,6 +296,41 @@ onMounted(() => { | ||
| 277 | const systemInfo = uni.getSystemInfoSync(); | 296 | const systemInfo = uni.getSystemInfoSync(); |
| 278 | statusBarHeight.value = systemInfo.statusBarHeight; | 297 | statusBarHeight.value = systemInfo.statusBarHeight; |
| 279 | getPartCategories(); | 298 | getPartCategories(); |
| 299 | + | ||
| 300 | + | ||
| 301 | + // 获取小程序胶囊位置信息,实现导航栏与胶囊完美对齐 | ||
| 302 | + // #ifdef MP-WEIXIN | ||
| 303 | + try { | ||
| 304 | + const rect = uni.getMenuButtonBoundingClientRect(); | ||
| 305 | + if (rect) { | ||
| 306 | + menuButtonInfo.value = { | ||
| 307 | + top: rect.top, | ||
| 308 | + height: rect.height | ||
| 309 | + }; | ||
| 310 | + } | ||
| 311 | + } catch (e) { | ||
| 312 | + console.log('获取胶囊信息失败,使用默认值', e); | ||
| 313 | + } | ||
| 314 | + // #endif | ||
| 315 | + | ||
| 316 | + // #ifndef MP-WEIXIN | ||
| 317 | + // 非微信小程序环境,使用系统状态栏高度 + 标准导航栏高度 | ||
| 318 | + try { | ||
| 319 | + const systemInfo = uni.getSystemInfoSync(); | ||
| 320 | + const statusBarHeight = systemInfo.statusBarHeight || 20; | ||
| 321 | + menuButtonInfo.value = { | ||
| 322 | + top: statusBarHeight, | ||
| 323 | + height: 44 // 标准导航栏高度 | ||
| 324 | + }; | ||
| 325 | + } catch (e) { | ||
| 326 | + console.log('获取系统信息失败', e); | ||
| 327 | + } | ||
| 328 | + // #endif | ||
| 329 | +}); | ||
| 330 | + | ||
| 331 | +const menuButtonInfo = ref({ | ||
| 332 | + top: 44, // 默认值,避免获取失败时样式异常 | ||
| 333 | + height: 32 | ||
| 280 | }); | 334 | }); |
| 281 | 335 | ||
| 282 | </script> | 336 | </script> |
| @@ -286,29 +340,96 @@ onMounted(() => { | @@ -286,29 +340,96 @@ onMounted(() => { | ||
| 286 | .plan-detail-page { | 340 | .plan-detail-page { |
| 287 | width: 100%; | 341 | width: 100%; |
| 288 | height: 100vh; | 342 | height: 100vh; |
| 289 | - background-color: #f5f5f5; | ||
| 290 | - box-sizing: border-box; | ||
| 291 | display: flex; | 343 | display: flex; |
| 292 | flex-direction: column; | 344 | flex-direction: column; |
| 345 | + background-color: #f5f5f5; | ||
| 346 | + box-sizing: border-box; | ||
| 293 | } | 347 | } |
| 294 | 348 | ||
| 295 | -.status-bar-placeholder { | 349 | +// .status-bar-placeholder { |
| 350 | +// width: 100%; | ||
| 351 | +// background-color: #1a1a1a; | ||
| 352 | +// } | ||
| 353 | + | ||
| 354 | +.page-header { | ||
| 355 | + position: fixed; | ||
| 296 | width: 100%; | 356 | width: 100%; |
| 297 | - background-color: #1a1a1a; | 357 | + top: 0; |
| 358 | + left: 0; | ||
| 359 | + right: 0; | ||
| 360 | + background-color: rgba(26, 26, 26, 0.9); | ||
| 361 | + z-index: 999; | ||
| 362 | + flex-shrink: 0; | ||
| 363 | + // background-color: #fff; | ||
| 364 | + | ||
| 365 | +} | ||
| 366 | + | ||
| 367 | +/* 动态导航栏:高度与胶囊完全对齐 */ | ||
| 368 | +.nav-bar { | ||
| 369 | + display: flex; | ||
| 370 | + align-items: center; | ||
| 371 | + justify-content: space-between; | ||
| 372 | + // background-color: #1a1a1a; | ||
| 373 | + /* 左右留出安全间距,避免内容贴边 */ | ||
| 374 | + padding-left: 16rpx; | ||
| 375 | + padding-right: 16rpx; | ||
| 376 | + /* 高度和顶部内边距由动态 style 控制 */ | ||
| 377 | + box-sizing: content-box; | ||
| 378 | +} | ||
| 379 | + | ||
| 380 | +/* 左侧返回按钮区域 - 固定宽度确保居中对齐 */ | ||
| 381 | +.nav-left { | ||
| 382 | + width: 60rpx; | ||
| 383 | + display: flex; | ||
| 384 | + align-items: center; | ||
| 385 | + justify-content: flex-start; | ||
| 386 | + flex-shrink: 0; | ||
| 298 | } | 387 | } |
| 299 | 388 | ||
| 389 | +.back-icon { | ||
| 390 | + display: block; | ||
| 391 | +} | ||
| 392 | + | ||
| 393 | +/* 标题区域 - 自适应居中 */ | ||
| 394 | +.nav-title { | ||
| 395 | + flex: 1; | ||
| 396 | + text-align: center; | ||
| 397 | + font-size: 36rpx; | ||
| 398 | + font-weight: bold; | ||
| 399 | + color: #fff; | ||
| 400 | + | ||
| 401 | + line-height: 1; | ||
| 402 | +} | ||
| 403 | + | ||
| 404 | +.nav-right { | ||
| 405 | + width: 60rpx; | ||
| 406 | + flex-shrink: 0; | ||
| 407 | +} | ||
| 408 | + | ||
| 409 | +.description { | ||
| 410 | + padding: 20rpx 30rpx 20rpx; | ||
| 411 | + color: #fff; | ||
| 412 | + // background-color: #1a1a1a; | ||
| 413 | + font-size: 28rpx; | ||
| 414 | + line-height: 46rpx; | ||
| 415 | + | ||
| 416 | +} | ||
| 417 | + | ||
| 418 | + | ||
| 419 | +// | ||
| 420 | + | ||
| 300 | .header { | 421 | .header { |
| 301 | width: 100%; | 422 | width: 100%; |
| 302 | - position: relative; | ||
| 303 | - background-color: #1a1a1a; | ||
| 304 | - color: #fff; | 423 | + // position: relative; |
| 305 | display: flex; | 424 | display: flex; |
| 306 | align-items: center; | 425 | align-items: center; |
| 307 | justify-content: center; | 426 | justify-content: center; |
| 427 | + background-color: #1a1a1a; | ||
| 428 | + color: #fff; | ||
| 308 | font-size: 36rpx; | 429 | font-size: 36rpx; |
| 309 | font-weight: 600; | 430 | font-weight: 600; |
| 310 | - z-index: 10; | ||
| 311 | - z-index: 999; | 431 | + // z-index: 10; |
| 432 | + // z-index: 999; | ||
| 312 | } | 433 | } |
| 313 | 434 | ||
| 314 | .back-btn { | 435 | .back-btn { |
| @@ -335,7 +456,13 @@ onMounted(() => { | @@ -335,7 +456,13 @@ onMounted(() => { | ||
| 335 | flex: 1; | 456 | flex: 1; |
| 336 | min-height: 0; | 457 | min-height: 0; |
| 337 | padding-top: 20rpx; | 458 | padding-top: 20rpx; |
| 338 | - margin-top: v-bind(statusBarHeight + headerHeight + 'px'); | 459 | + // margin-top: v-bind(statusBarHeight + headerHeight + 'px'); |
| 460 | + margin-top: 236rpx; | ||
| 461 | + | ||
| 462 | + /* #ifdef H5 */ | ||
| 463 | + min-height: 890px; | ||
| 464 | + margin-top: 100px; | ||
| 465 | + /* #endif */ | ||
| 339 | } | 466 | } |
| 340 | 467 | ||
| 341 | .grid-container { | 468 | .grid-container { |
| @@ -369,15 +496,6 @@ onMounted(() => { | @@ -369,15 +496,6 @@ onMounted(() => { | ||
| 369 | height: 300rpx; | 496 | height: 300rpx; |
| 370 | } | 497 | } |
| 371 | 498 | ||
| 372 | -.description { | ||
| 373 | - padding: 30rpx 30rpx 20rpx; | ||
| 374 | - color: #666; | ||
| 375 | - font-size: 28rpx; | ||
| 376 | - line-height: 46rpx; | ||
| 377 | - background-color: #fff; | ||
| 378 | - border-bottom: 1px solid #eee; | ||
| 379 | -} | ||
| 380 | - | ||
| 381 | .card-info { | 499 | .card-info { |
| 382 | // 核心优化:增强背景对比度 + 视觉层次 | 500 | // 核心优化:增强背景对比度 + 视觉层次 |
| 383 | position: absolute; | 501 | position: absolute; |
| @@ -469,19 +587,19 @@ onMounted(() => { | @@ -469,19 +587,19 @@ onMounted(() => { | ||
| 469 | } | 587 | } |
| 470 | 588 | ||
| 471 | .filter-item { | 589 | .filter-item { |
| 590 | + height: 60rpx; | ||
| 472 | display: flex; | 591 | display: flex; |
| 473 | align-items: center; | 592 | align-items: center; |
| 474 | justify-content: center; | 593 | justify-content: center; |
| 475 | - padding: 0 30rpx; | ||
| 476 | - height: 60rpx; | 594 | + padding: 0 15rpx; |
| 477 | line-height: 60rpx; | 595 | line-height: 60rpx; |
| 478 | border-radius: 30rpx; | 596 | border-radius: 30rpx; |
| 597 | + background-color: #fff; | ||
| 479 | border: 1px solid #ddd; | 598 | border: 1px solid #ddd; |
| 480 | color: #333; | 599 | color: #333; |
| 481 | font-size: 28rpx; | 600 | font-size: 28rpx; |
| 482 | font-weight: 400; | 601 | font-weight: 400; |
| 483 | gap: 8rpx; | 602 | gap: 8rpx; |
| 484 | - background-color: #fff; | ||
| 485 | 603 | ||
| 486 | &:active { | 604 | &:active { |
| 487 | background-color: #f5f5f5; | 605 | background-color: #f5f5f5; |
| @@ -491,7 +609,7 @@ onMounted(() => { | @@ -491,7 +609,7 @@ onMounted(() => { | ||
| 491 | /* 筛选器容器 */ | 609 | /* 筛选器容器 */ |
| 492 | .filter-wrapper { | 610 | .filter-wrapper { |
| 493 | position: relative; | 611 | position: relative; |
| 494 | - z-index: 999; | 612 | + // z-index: 999; |
| 495 | } | 613 | } |
| 496 | 614 | ||
| 497 | /* 下拉菜单 */ | 615 | /* 下拉菜单 */ |
| @@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
| 19 | <view class="filter-group"> | 19 | <view class="filter-group"> |
| 20 | <!-- 部位 --> | 20 | <!-- 部位 --> |
| 21 | <view class="filter-wrapper"> | 21 | <view class="filter-wrapper"> |
| 22 | - <view class="tab-item" :class="{ active: activePartId !== '0' }" @click="togglePartDropdown"> | 22 | + <view class="tab-item" :class="{ active: activePartId !== null }" @click="togglePartDropdown"> |
| 23 | {{ activePartId === null ? '部位' : activePart }} | 23 | {{ activePartId === null ? '部位' : activePart }} |
| 24 | <uni-icons :type="showPartDropdown ? 'up' : 'down'" size="18" color="#333"></uni-icons> | 24 | <uni-icons :type="showPartDropdown ? 'up' : 'down'" size="18" color="#333"></uni-icons> |
| 25 | </view> | 25 | </view> |
| @@ -32,7 +32,7 @@ | @@ -32,7 +32,7 @@ | ||
| 32 | </view> | 32 | </view> |
| 33 | <!-- 场景 --> | 33 | <!-- 场景 --> |
| 34 | <view class="filter-wrapper"> | 34 | <view class="filter-wrapper"> |
| 35 | - <view class="tab-item" :class="{ active: activeSceneId !== '0' }" @click="toggleSceneDropdown"> | 35 | + <view class="tab-item" :class="{ active: activeSceneId !== null }" @click="toggleSceneDropdown"> |
| 36 | {{ activeSceneId === null ? '场景' : activeScene }} | 36 | {{ activeSceneId === null ? '场景' : activeScene }} |
| 37 | <uni-icons :type="showSceneDropdown ? 'up' : 'down'" size="18" color="#333"></uni-icons> | 37 | <uni-icons :type="showSceneDropdown ? 'up' : 'down'" size="18" color="#333"></uni-icons> |
| 38 | </view> | 38 | </view> |
| @@ -170,7 +170,8 @@ | @@ -170,7 +170,8 @@ | ||
| 170 | </template> | 170 | </template> |
| 171 | 171 | ||
| 172 | <script setup> | 172 | <script setup> |
| 173 | -import { onMounted, ref, nextTick } from 'vue'; | 173 | +import { onMounted, ref, nextTick, onUnmounted } from 'vue'; |
| 174 | +import { onShow } from '@dcloudio/uni-app'; | ||
| 174 | import TemplatesApi from '@/sheep/api/Template/Templates'; | 175 | import TemplatesApi from '@/sheep/api/Template/Templates'; |
| 175 | import TemplateMenuPopup from '@/pages4/components/TemplateMenuPopup.vue' | 176 | import TemplateMenuPopup from '@/pages4/components/TemplateMenuPopup.vue' |
| 176 | 177 | ||
| @@ -468,12 +469,44 @@ const menuButtonInfo = ref({ | @@ -468,12 +469,44 @@ const menuButtonInfo = ref({ | ||
| 468 | height: 32 | 469 | height: 32 |
| 469 | }); | 470 | }); |
| 470 | 471 | ||
| 472 | +// 添加 onShow 生命周期 | ||
| 473 | +onShow(() => { | ||
| 474 | + | ||
| 475 | + console.log('==== onShow执行了 ===='); | ||
| 476 | + activePartId.value = null; | ||
| 477 | + activePart.value = '不限'; | ||
| 478 | + activeSceneId.value = null; | ||
| 479 | + activeScene.value = '不限'; | ||
| 480 | + showPartDropdown.value = false; | ||
| 481 | + showSceneDropdown.value = false; | ||
| 482 | + | ||
| 483 | + console.log('onShow刷新页面'); | ||
| 484 | + | ||
| 485 | + | ||
| 486 | + getMyTemplates(); | ||
| 487 | + getFolderList(); | ||
| 488 | +}); | ||
| 489 | + | ||
| 471 | onMounted(() => { | 490 | onMounted(() => { |
| 472 | // TemplatesList(); | 491 | // TemplatesList(); |
| 473 | getMyTemplates(); | 492 | getMyTemplates(); |
| 474 | getPartCategories(); | 493 | getPartCategories(); |
| 475 | getFolderList(); | 494 | getFolderList(); |
| 476 | 495 | ||
| 496 | + | ||
| 497 | + uni.$on('refreshTemplateList', () => { | ||
| 498 | + console.log('收到刷新事件,强制刷新模板'); | ||
| 499 | + // 重置筛选条件,和onShow逻辑保持一致 | ||
| 500 | + activePartId.value = null; | ||
| 501 | + activePart.value = '不限'; | ||
| 502 | + activeSceneId.value = null; | ||
| 503 | + activeScene.value = '不限'; | ||
| 504 | + showPartDropdown.value = false; | ||
| 505 | + showSceneDropdown.value = false; | ||
| 506 | + // 执行刷新 | ||
| 507 | + handleRefreshTemplate(); | ||
| 508 | + }); | ||
| 509 | + | ||
| 477 | // 获取小程序胶囊位置信息,实现导航栏与胶囊完美对齐 | 510 | // 获取小程序胶囊位置信息,实现导航栏与胶囊完美对齐 |
| 478 | // #ifdef MP-WEIXIN | 511 | // #ifdef MP-WEIXIN |
| 479 | try { | 512 | try { |
| @@ -503,6 +536,10 @@ onMounted(() => { | @@ -503,6 +536,10 @@ onMounted(() => { | ||
| 503 | } | 536 | } |
| 504 | // #endif | 537 | // #endif |
| 505 | }); | 538 | }); |
| 539 | + | ||
| 540 | +onUnmounted(() => { | ||
| 541 | + uni.$off('refreshTemplateList'); | ||
| 542 | +}); | ||
| 506 | </script> | 543 | </script> |
| 507 | 544 | ||
| 508 | <style scoped lang="scss"> | 545 | <style scoped lang="scss"> |
| @@ -729,7 +766,7 @@ onMounted(() => { | @@ -729,7 +766,7 @@ onMounted(() => { | ||
| 729 | /* 正确的下拉菜单 */ | 766 | /* 正确的下拉菜单 */ |
| 730 | .filter-wrapper { | 767 | .filter-wrapper { |
| 731 | position: relative; | 768 | position: relative; |
| 732 | - z-index: 9999; | 769 | + // z-index: 9999 |
| 733 | } | 770 | } |
| 734 | 771 | ||
| 735 | .dropdown-menu { | 772 | .dropdown-menu { |
| @@ -198,16 +198,26 @@ const logout = () => { | @@ -198,16 +198,26 @@ const logout = () => { | ||
| 198 | uni.showModal({ | 198 | uni.showModal({ |
| 199 | title: '提示', | 199 | title: '提示', |
| 200 | content: '确定要退出登录吗?', | 200 | content: '确定要退出登录吗?', |
| 201 | - success: (res) => { | ||
| 202 | - if (res.confirm) { | 201 | + success: async (res) => { |
| 202 | + // if (res.confirm) { | ||
| 203 | + // // 调用退出登录接口 | ||
| 204 | + // AuthUtil.logout().then(() => { | ||
| 205 | + // // 清空用户信息 | ||
| 206 | + // userStore.logout(); | ||
| 207 | + | ||
| 208 | + // // 跳转到登录页 | ||
| 209 | + // uni.reLaunch({ url: '/pages/index/index' }); | ||
| 210 | + // }); | ||
| 211 | + // } | ||
| 212 | + try { | ||
| 203 | // 调用退出登录接口 | 213 | // 调用退出登录接口 |
| 204 | - AuthUtil.logout().then(() => { | ||
| 205 | - // 清空用户信息 | ||
| 206 | - userStore.logout(); | ||
| 207 | - | ||
| 208 | - // 跳转到登录页 | ||
| 209 | - uni.reLaunch({ url: '/pages/index/index' }); | ||
| 210 | - }); | 214 | + await AuthUtil.logout(); |
| 215 | + // 清空用户信息 | ||
| 216 | + userStore.logout(); | ||
| 217 | + // 跳转到正确登录页 | ||
| 218 | + uni.reLaunch({ url: '/pages7/pages/index/login' }); | ||
| 219 | + } catch (err) { | ||
| 220 | + uni.showToast({ title: '退出失败', icon: 'none' }); | ||
| 211 | } | 221 | } |
| 212 | }, | 222 | }, |
| 213 | }); | 223 | }); |
| @@ -56,7 +56,10 @@ | @@ -56,7 +56,10 @@ | ||
| 56 | <view class="input-box"> | 56 | <view class="input-box"> |
| 57 | <u-icon name="lock" size="20" color="#a1a8b3" class="input-icon" /> | 57 | <u-icon name="lock" size="20" color="#a1a8b3" class="input-icon" /> |
| 58 | <input type="password" v-model="password" placeholder="请输入您的密码" placeholder-style="color: #a1a8b3" | 58 | <input type="password" v-model="password" placeholder="请输入您的密码" placeholder-style="color: #a1a8b3" |
| 59 | - class="native-input" :password="true" /> | 59 | + class="native-input" :password="!passwordShow" /> |
| 60 | + <!-- --> | ||
| 61 | + <u-icon :name="passwordShow ? 'eye' : 'eye-off'" size="20" color="#a1a8b3" class="eye-icon" | ||
| 62 | + @click="passwordShow = !passwordShow" /> | ||
| 60 | </view> | 63 | </view> |
| 61 | </view> | 64 | </view> |
| 62 | </view> | 65 | </view> |
| @@ -70,7 +73,7 @@ | @@ -70,7 +73,7 @@ | ||
| 70 | <u-icon v-if="isAgreed" name="checkbox-mark" size="12" color="#ffffff" /> | 73 | <u-icon v-if="isAgreed" name="checkbox-mark" size="12" color="#ffffff" /> |
| 71 | </view> | 74 | </view> |
| 72 | <view class="agreement-text"> | 75 | <view class="agreement-text"> |
| 73 | - 我已阅读并同意 FitFlow | 76 | + 我已阅读并同意 自己练 |
| 74 | <text class="link-text" @click.stop="goToAgreement('service')">《用户服务协议》</text> | 77 | <text class="link-text" @click.stop="goToAgreement('service')">《用户服务协议》</text> |
| 75 | 与 | 78 | 与 |
| 76 | <text class="link-text" @click.stop="goToAgreement('privacy')">《隐私政策》</text> | 79 | <text class="link-text" @click.stop="goToAgreement('privacy')">《隐私政策》</text> |
| @@ -98,6 +101,7 @@ const activeTab = ref('sms'); // sms: 免密, password: 密码 | @@ -98,6 +101,7 @@ const activeTab = ref('sms'); // sms: 免密, password: 密码 | ||
| 98 | const phoneNumber = ref(''); | 101 | const phoneNumber = ref(''); |
| 99 | const verifyCode = ref(''); | 102 | const verifyCode = ref(''); |
| 100 | const password = ref(''); | 103 | const password = ref(''); |
| 104 | +const passwordShow = ref(false) // 控制密码是否明文显示 | ||
| 101 | const isAgreed = ref(false); | 105 | const isAgreed = ref(false); |
| 102 | 106 | ||
| 103 | // 防抖及加载动画状态变量 | 107 | // 防抖及加载动画状态变量 |
| @@ -396,6 +400,10 @@ onHide(() => { | @@ -396,6 +400,10 @@ onHide(() => { | ||
| 396 | margin-right: 16rpx; | 400 | margin-right: 16rpx; |
| 397 | } | 401 | } |
| 398 | 402 | ||
| 403 | + .eye-icon { | ||
| 404 | + margin-left: 16rpx; | ||
| 405 | + } | ||
| 406 | + | ||
| 399 | .native-input { | 407 | .native-input { |
| 400 | flex: 1; | 408 | flex: 1; |
| 401 | height: 100%; | 409 | height: 100%; |
| @@ -29,7 +29,6 @@ const options = { | @@ -29,7 +29,6 @@ const options = { | ||
| 29 | isToken: true, | 29 | isToken: true, |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | - | ||
| 33 | /** 跳转到登录页(navigateTo 失败时 reLaunch,避免页面栈异常) */ | 32 | /** 跳转到登录页(navigateTo 失败时 reLaunch,避免页面栈异常) */ |
| 34 | function navigateToLogin() { | 33 | function navigateToLogin() { |
| 35 | uni.navigateTo({ | 34 | uni.navigateTo({ |
sheep/store/trainingSnapshot.js
0 → 100644
| 1 | +// sheep/store/trainingSnapshot.js | ||
| 2 | +import { reactive } from 'vue'; | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * 训练快照 — 悬浮球与训练页面的共享数据桥梁 | ||
| 6 | + * 不是 Pinia store,是模块级 reactive 对象,存在 JS 堆内存中 | ||
| 7 | + * 页面跳转、clearTrainingStore() 都碰不到它 | ||
| 8 | + */ | ||
| 9 | +export const trainingSnapshot = reactive({ | ||
| 10 | + /** 全量 store 状态快照(JSON 深拷贝) */ | ||
| 11 | + data: null, | ||
| 12 | + /** 快照时间戳(毫秒),用于回显时补偿计时器 */ | ||
| 13 | + timestamp: null, | ||
| 14 | + /** 快照时计时器是否正在运行 */ | ||
| 15 | + timerWasRunning: false, | ||
| 16 | + /** 悬浮球是否应该显示(独立于 trainingStore.min) */ | ||
| 17 | + visible: false, | ||
| 18 | +}); |
| @@ -416,7 +416,7 @@ export const useTrainingStore = defineStore('training', { | @@ -416,7 +416,7 @@ export const useTrainingStore = defineStore('training', { | ||
| 416 | this.loading = false; | 416 | this.loading = false; |
| 417 | this.unitRecords = {}; | 417 | this.unitRecords = {}; |
| 418 | this.trainingTimeText = ''; | 418 | this.trainingTimeText = ''; |
| 419 | - this.min = false; | 419 | + // this.min = false; |
| 420 | this.isSystem = 1; | 420 | this.isSystem = 1; |
| 421 | this.trainingName = ''; | 421 | this.trainingName = ''; |
| 422 | this.defaultTimeIndex = [0, 0, 0]; | 422 | this.defaultTimeIndex = [0, 0, 0]; |
-
Please register or login to post a comment