Authored by qxm

修复黄色悬浮圆球,我的训练青色圆球,官方和我的模板选择页面,去训练和添加模板弹窗

@@ -50,15 +50,15 @@ @@ -50,15 +50,15 @@
50 } 50 }
51 }, 51 },
52 { 52 {
53 - "path": "pages/xunji/xunji-wode-moban", 53 + "path": "pages/xunji/wode-xinjian-moban",
54 "style": { 54 "style": {
55 - "navigationBarTitleText": "我的模版" 55 + "navigationBarTitleText": "新建我的模版"
56 } 56 }
57 }, 57 },
58 { 58 {
59 - "path": "pages/xunji/xunji-rili-tianjia", 59 + "path": "pages/xunji/xunji-wode-moban",
60 "style": { 60 "style": {
61 - "navigationBarTitleText": "日历添加训练动作" 61 + "navigationBarTitleText": "我的模版"
62 } 62 }
63 }, 63 },
64 { 64 {
@@ -113,6 +113,14 @@ @@ -113,6 +113,14 @@
113 "navigationBarTitleText": "新增超级组" 113 "navigationBarTitleText": "新增超级组"
114 } 114 }
115 }, 115 },
  116 +
  117 + {
  118 + "path": "pages/xunji/wode-jihua-paike",
  119 + "style": {
  120 + "navigationBarTitleText": "我的计划详情的排课设置"
  121 + }
  122 + },
  123 +
116 { 124 {
117 "path": "pages/xunji/dongzuo-muluguanli", 125 "path": "pages/xunji/dongzuo-muluguanli",
118 "style": { 126 "style": {
@@ -131,6 +139,12 @@ @@ -131,6 +139,12 @@
131 "style": { 139 "style": {
132 "navigationBarTitleText": "" 140 "navigationBarTitleText": ""
133 } 141 }
  142 + },
  143 + {
  144 + "path": "pages/xunji/xunji-wode-jihua",
  145 + "style": {
  146 + "navigationBarTitleText": ""
  147 + }
134 } 148 }
135 ] 149 ]
136 }, 150 },
@@ -41,8 +41,8 @@ onMounted(() => { @@ -41,8 +41,8 @@ onMounted(() => {
41 <style lang="scss" scoped> 41 <style lang="scss" scoped>
42 .floating-train-btn { 42 .floating-train-btn {
43 position: fixed; 43 position: fixed;
44 - right: 20rpx;  
45 - bottom: 200rpx; 44 + right: 11rpx;
  45 + bottom: 304rpx;
46 display: flex; 46 display: flex;
47 align-items: center; 47 align-items: center;
48 background-color: #39d353; 48 background-color: #39d353;
1 <template> 1 <template>
2 <view class="my-page"> 2 <view class="my-page">
3 <!-- 登录头部区 --> 3 <!-- 登录头部区 -->
4 - <view  
5 - v-if="userStore.isLogin"  
6 - class="section-card user-header"  
7 - hover-class="card-hover"  
8 - @tap="goMyPersonalData"  
9 - >  
10 - <image  
11 - :src="  
12 - userInfo.avatar || 4 + <view v-if="userStore.isLogin" class="section-card user-header" hover-class="card-hover" @tap="goMyPersonalData">
  5 + <image :src="userInfo.avatar ||
13 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png' 6 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png'
14 - "  
15 - mode="aspectFill"  
16 - class="avatar"  
17 - /> 7 + " mode="aspectFill" class="avatar" />
18 <view class="info-content"> 8 <view class="info-content">
19 <view class="name-row"> 9 <view class="name-row">
20 <text class="nickname">{{ userInfo.nickname || '微信用户' }}</text> 10 <text class="nickname">{{ userInfo.nickname || '微信用户' }}</text>
@@ -48,13 +38,8 @@ @@ -48,13 +38,8 @@
48 38
49 <!-- 课程状态快速入口 --> 39 <!-- 课程状态快速入口 -->
50 <view class="section-card quick-entry"> 40 <view class="section-card quick-entry">
51 - <view  
52 - v-for="entry in quickEntryConfig"  
53 - :key="entry.type"  
54 - class="entry-item"  
55 - hover-class="opacity-hover"  
56 - @click="handleQuickEntry(entry.type)"  
57 - > 41 + <view v-for="entry in quickEntryConfig" :key="entry.type" class="entry-item" hover-class="opacity-hover"
  42 + @click="handleQuickEntry(entry.type)">
58 <text class="num">{{ userInfo[entry.key] || 0 }}</text> 43 <text class="num">{{ userInfo[entry.key] || 0 }}</text>
59 <text class="label">{{ entry.label }}</text> 44 <text class="label">{{ entry.label }}</text>
60 </view> 45 </view>
@@ -62,21 +47,14 @@ @@ -62,21 +47,14 @@
62 47
63 <!-- 广告位 --> 48 <!-- 广告位 -->
64 <view class="banner-box" @click="goJiamen"> 49 <view class="banner-box" @click="goJiamen">
65 - <image  
66 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/4_1773627891703.png"  
67 - mode="aspectFill"  
68 - class="banner-img"  
69 - /> 50 + <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/4_1773627891703.png"
  51 + mode="aspectFill" class="banner-img" />
70 </view> 52 </view>
71 53
72 <!-- 核心应用区 --> 54 <!-- 核心应用区 -->
73 <view class="section-card apply-section"> 55 <view class="section-card apply-section">
74 - <view  
75 - v-for="(item, index) in APPLY_CONFIG_LIST"  
76 - :key="index"  
77 - class="apply-item"  
78 - @click="authNavigateTo(item.url)"  
79 - > 56 + <view v-for="(item, index) in APPLY_CONFIG_LIST" :key="index" class="apply-item"
  57 + @click="authNavigateTo(item.url)">
80 <view class="icon-bg"> 58 <view class="icon-bg">
81 <image :src="item.icon" class="img" mode="aspectFit" /> 59 <image :src="item.icon" class="img" mode="aspectFit" />
82 </view> 60 </view>
@@ -87,12 +65,7 @@ @@ -87,12 +65,7 @@
87 <!-- 资产账户网格区 --> 65 <!-- 资产账户网格区 -->
88 <view v-if="userStore.isLogin" class="section-card"> 66 <view v-if="userStore.isLogin" class="section-card">
89 <view class="account-grid"> 67 <view class="account-grid">
90 - <view  
91 - v-for="(acc, idx) in accountConfig"  
92 - :key="idx"  
93 - class="account-item"  
94 - @click="authNavigateTo(acc.url)"  
95 - > 68 + <view v-for="(acc, idx) in accountConfig" :key="idx" class="account-item" @click="authNavigateTo(acc.url)">
96 <text class="acc-lab">{{ acc.label }}</text> 69 <text class="acc-lab">{{ acc.label }}</text>
97 <text class="acc-val"> 70 <text class="acc-val">
98 {{ formatAccountValue(userInfo[acc.key], acc.isFloat) }} 71 {{ formatAccountValue(userInfo[acc.key], acc.isFloat) }}
@@ -105,12 +78,8 @@ @@ -105,12 +78,8 @@
105 <!-- 功能矩阵九宫格 --> 78 <!-- 功能矩阵九宫格 -->
106 <view class="section-card icon-grid-box"> 79 <view class="section-card icon-grid-box">
107 <view class="icon-grid"> 80 <view class="icon-grid">
108 - <view  
109 - v-for="(item, index) in FUNCTION_CONFIG_LIST"  
110 - :key="index"  
111 - class="icon-item"  
112 - @click="handleGridItemClick(item)"  
113 - > 81 + <view v-for="(item, index) in FUNCTION_CONFIG_LIST" :key="index" class="icon-item"
  82 + @click="handleGridItemClick(item)">
114 <view class="icon-img-wrap"> 83 <view class="icon-img-wrap">
115 <image :src="item.icon" mode="aspectFit" class="icon-img" /> 84 <image :src="item.icon" mode="aspectFit" class="icon-img" />
116 85
@@ -120,11 +89,7 @@ @@ -120,11 +89,7 @@
120 </view> 89 </view>
121 <text class="icon-text">{{ item.text }}</text> 90 <text class="icon-text">{{ item.text }}</text>
122 91
123 - <button  
124 - v-if="item.text === '联系客服'"  
125 - open-type="contact"  
126 - class="mp-contact-overlay-btn"  
127 - /> 92 + <button v-if="item.text === '联系客服'" open-type="contact" class="mp-contact-overlay-btn" />
128 </view> 93 </view>
129 </view> 94 </view>
130 </view> 95 </view>
@@ -142,51 +107,57 @@ @@ -142,51 +107,57 @@
142 </view> 107 </view>
143 108
144 <Tabbar /> 109 <Tabbar />
  110 + <TrainingFloating />
145 </view> 111 </view>
146 </template> 112 </template>
147 113
148 <script setup> 114 <script setup>
149 - import { ref } from 'vue';  
150 - import UserApi from '@/sheep/api/member/user';  
151 - import MemberApi from '@/sheep/api/member/member';  
152 - import useUserStore from '@/sheep/store/user';  
153 - import { onShow } from '@dcloudio/uni-app';  
154 - // 响应式数据挂载  
155 - const userStore = useUserStore();  
156 - const userInfo = ref({});  
157 - const memberLevelName = ref('');  
158 -  
159 - // 固定的 UI 配置  
160 - const APPLY_CONFIG_LIST = [];  
161 -  
162 - const FUNCTION_CONFIG_LIST = [];  
163 -  
164 - // 课程计数状态配置映射  
165 - const quickEntryConfig = [ 115 +import { ref } from 'vue';
  116 +import UserApi from '@/sheep/api/member/user';
  117 +import MemberApi from '@/sheep/api/member/member';
  118 +import useUserStore from '@/sheep/store/user';
  119 +import { onShow } from '@dcloudio/uni-app';
  120 +import TrainingFloating from '@/pages/TrainingFloating.vue'
  121 +import { useTrainingStore } from '@/sheep/store/trainingStore';
  122 +const trainingStore = useTrainingStore();
  123 +
  124 +
  125 +// 响应式数据挂载
  126 +const userStore = useUserStore();
  127 +const userInfo = ref({});
  128 +const memberLevelName = ref('');
  129 +
  130 +// 固定的 UI 配置
  131 +const APPLY_CONFIG_LIST = [];
  132 +
  133 +const FUNCTION_CONFIG_LIST = [];
  134 +
  135 +// 课程计数状态配置映射
  136 +const quickEntryConfig = [
166 { type: 1, key: 'courseNum', label: '待上课' }, 137 { type: 1, key: 'courseNum', label: '待上课' },
167 { type: 2, key: 'courseWaitNum', label: '等待中' }, 138 { type: 2, key: 'courseWaitNum', label: '等待中' },
168 { type: 3, key: 'courseEvaluationWaitNum', label: '历史课程' }, 139 { type: 3, key: 'courseEvaluationWaitNum', label: '历史课程' },
169 - ]; 140 +];
170 141
171 - // 资产账户字段清洗配置映射 (对应接口数据类型:number 与 integer)  
172 - const accountConfig = []; 142 +// 资产账户字段清洗配置映射 (对应接口数据类型:number 与 integer)
  143 +const accountConfig = [];
173 144
174 - /** 145 +/**
175 * JSDoc 核心资产数值洗涤函数 146 * JSDoc 核心资产数值洗涤函数
176 * @param {number|undefined} val 原始金钱/数量值 147 * @param {number|undefined} val 原始金钱/数量值
177 * @param {boolean} isFloat 是否需要保留两位小数 148 * @param {boolean} isFloat 是否需要保留两位小数
178 * @returns {string|number} 格式化后的安全渲染字符串 149 * @returns {string|number} 格式化后的安全渲染字符串
179 */ 150 */
180 - const formatAccountValue = (val, isFloat) => { 151 +const formatAccountValue = (val, isFloat) => {
181 if (val === undefined || val === null) return isFloat ? '0.00' : 0; 152 if (val === undefined || val === null) return isFloat ? '0.00' : 0;
182 return isFloat ? Number(val).toFixed(2) : Math.floor(val); 153 return isFloat ? Number(val).toFixed(2) : Math.floor(val);
183 - }; 154 +};
184 155
185 - /** 156 +/**
186 * 路由守卫拦截转发 157 * 路由守卫拦截转发
187 * @param {string} url 目标绝对/相对地址 158 * @param {string} url 目标绝对/相对地址
188 */ 159 */
189 - const authNavigateTo = (url) => { 160 +const authNavigateTo = (url) => {
190 if (!userStore.isLogin) { 161 if (!userStore.isLogin) {
191 goLogin(); 162 goLogin();
192 return; 163 return;
@@ -195,13 +166,13 @@ @@ -195,13 +166,13 @@
195 url, 166 url,
196 fail: (err) => console.error(`[Router] 页面跳转失败 ${url}: `, err), 167 fail: (err) => console.error(`[Router] 页面跳转失败 ${url}: `, err),
197 }); 168 });
198 - }; 169 +};
199 170
200 - /** 171 +/**
201 * 统一处理功能网格的点击分发 172 * 统一处理功能网格的点击分发
202 * @param {Object} item 节点配置项 173 * @param {Object} item 节点配置项
203 */ 174 */
204 - const handleGridItemClick = (item) => { 175 +const handleGridItemClick = (item) => {
205 if (item.text === '联系客服') { 176 if (item.text === '联系客服') {
206 // #ifndef MP-WEIXIN 177 // #ifndef MP-WEIXIN
207 // 兜底非微信小程序平台(如H5、App),可以正常走原来的普通客服页面路由 178 // 兜底非微信小程序平台(如H5、App),可以正常走原来的普通客服页面路由
@@ -212,13 +183,13 @@ @@ -212,13 +183,13 @@
212 183
213 // 其他正常功能,正常走路由拦截守卫 184 // 其他正常功能,正常走路由拦截守卫
214 authNavigateTo(item.url); 185 authNavigateTo(item.url);
215 - }; 186 +};
216 187
217 - /** 188 +/**
218 * 异步高内聚合并请求 189 * 异步高内聚合并请求
219 * 解决因先后触发 setData 导致微信小程序底层 AppService 与 WebView 之间高频拥堵卡顿的问题 190 * 解决因先后触发 setData 导致微信小程序底层 AppService 与 WebView 之间高频拥堵卡顿的问题
220 */ 191 */
221 - const fetchPageData = async () => { 192 +const fetchPageData = async () => {
222 try { 193 try {
223 const [userRes, levelRes] = await Promise.all([ 194 const [userRes, levelRes] = await Promise.all([
224 UserApi.getUserInfo(), 195 UserApi.getUserInfo(),
@@ -239,34 +210,35 @@ @@ -239,34 +210,35 @@
239 } catch (error) { 210 } catch (error) {
240 console.error('[API Error] 拉取个人资产信息流失败:', error); 211 console.error('[API Error] 拉取个人资产信息流失败:', error);
241 } 212 }
242 - }; 213 +};
243 214
244 - onShow(() => { 215 +onShow(() => {
245 if (userStore.isLogin) { 216 if (userStore.isLogin) {
246 fetchPageData(); 217 fetchPageData();
247 } else { 218 } else {
248 userInfo.value = {}; 219 userInfo.value = {};
249 memberLevelName.value = ''; 220 memberLevelName.value = '';
250 } 221 }
251 - }); 222 +});
252 223
253 - // 路由跳转原子原子层  
254 - const goLogin = () => uni.navigateTo({ url: '/pages7/pages/index/login' });  
255 - const goMyPersonalData = () => authNavigateTo('/pages5/pages/user/wode-geren-ziliao');  
256 - const goAddVip = () => authNavigateTo('/pages5/pages/user/wode-hongxing-huiyuan'); 224 +// 路由跳转原子原子层
  225 +const goLogin = () => uni.navigateTo({ url: '/pages7/pages/index/login' });
  226 +const goMyPersonalData = () => authNavigateTo('/pages5/pages/user/wode-geren-ziliao');
  227 +const goAddVip = () => authNavigateTo('/pages5/pages/user/wode-hongxing-huiyuan');
257 228
258 - const goJiamen = () => uni.navigateTo({ url: '/pages7/pages/index/shouye-jiamen-hongxing' });  
259 - const handleQuickEntry = (type) => authNavigateTo(`/pages5/pages/user/wode-shangke?type=${type}`); 229 +const goJiamen = () => uni.navigateTo({ url: '/pages7/pages/index/shouye-jiamen-hongxing' });
  230 +const handleQuickEntry = (type) => authNavigateTo(`/pages5/pages/user/wode-shangke?type=${type}`);
260 </script> 231 </script>
261 232
262 <style scoped lang="scss"> 233 <style scoped lang="scss">
263 - $brand-color: #ff6b00;  
264 - $page-bg: #f8f8f8; 234 +$brand-color: #ff6b00;
  235 +$page-bg: #f8f8f8;
265 236
266 - .my-page { 237 +.my-page {
267 min-height: 100vh; 238 min-height: 100vh;
268 background-color: $page-bg; 239 background-color: $page-bg;
269 - padding: 20rpx 28rpx calc(40rpx + env(safe-area-inset-bottom)); /* 解决部分 iOS 底部 Tabbar 高度塌陷 */ 240 + padding: 20rpx 28rpx calc(40rpx + env(safe-area-inset-bottom));
  241 + /* 解决部分 iOS 底部 Tabbar 高度塌陷 */
270 box-sizing: border-box; 242 box-sizing: border-box;
271 243
272 .section-card { 244 .section-card {
@@ -281,6 +253,7 @@ @@ -281,6 +253,7 @@
281 display: flex; 253 display: flex;
282 align-items: center; 254 align-items: center;
283 padding: 34rpx 30rpx; 255 padding: 34rpx 30rpx;
  256 +
284 .avatar { 257 .avatar {
285 width: 110rpx; 258 width: 110rpx;
286 height: 110rpx; 259 height: 110rpx;
@@ -288,17 +261,21 @@ @@ -288,17 +261,21 @@
288 background: #f0f0f0; 261 background: #f0f0f0;
289 border: 4rpx solid #fff; 262 border: 4rpx solid #fff;
290 } 263 }
  264 +
291 .info-content { 265 .info-content {
292 margin-left: 24rpx; 266 margin-left: 24rpx;
293 flex: 1; 267 flex: 1;
  268 +
294 .name-row { 269 .name-row {
295 display: flex; 270 display: flex;
296 flex-direction: column; 271 flex-direction: column;
  272 +
297 .nickname { 273 .nickname {
298 font-size: 34rpx; 274 font-size: 34rpx;
299 font-weight: bold; 275 font-weight: bold;
300 color: #333; 276 color: #333;
301 } 277 }
  278 +
302 .tag { 279 .tag {
303 font-size: 20rpx; 280 font-size: 20rpx;
304 color: $brand-color; 281 color: $brand-color;
@@ -310,10 +287,12 @@ @@ -310,10 +287,12 @@
310 } 287 }
311 } 288 }
312 } 289 }
  290 +
313 .right { 291 .right {
314 display: flex; 292 display: flex;
315 align-items: center; 293 align-items: center;
316 gap: 60rpx; 294 gap: 60rpx;
  295 +
317 .img { 296 .img {
318 width: 60rpx; 297 width: 60rpx;
319 height: 60rpx; 298 height: 60rpx;
@@ -325,17 +304,20 @@ @@ -325,17 +304,20 @@
325 display: flex; 304 display: flex;
326 justify-content: space-between; 305 justify-content: space-between;
327 align-items: center; 306 align-items: center;
  307 +
328 .title { 308 .title {
329 font-size: 32rpx; 309 font-size: 32rpx;
330 font-weight: bold; 310 font-weight: bold;
331 color: #333; 311 color: #333;
332 } 312 }
  313 +
333 .desc { 314 .desc {
334 font-size: 22rpx; 315 font-size: 22rpx;
335 color: #999; 316 color: #999;
336 margin-top: 4rpx; 317 margin-top: 4rpx;
337 display: block; 318 display: block;
338 } 319 }
  320 +
339 .login-btn { 321 .login-btn {
340 margin: 0; 322 margin: 0;
341 background: $brand-color; 323 background: $brand-color;
@@ -345,9 +327,12 @@ @@ -345,9 +327,12 @@
345 line-height: 64rpx; 327 line-height: 64rpx;
346 border-radius: 32rpx; 328 border-radius: 32rpx;
347 padding: 0 30rpx; 329 padding: 0 30rpx;
  330 +
348 &::after { 331 &::after {
349 border: none; 332 border: none;
350 - } /* 清理小程序 button 默认黑边线 */ 333 + }
  334 +
  335 + /* 清理小程序 button 默认黑边线 */
351 } 336 }
352 } 337 }
353 338
@@ -359,15 +344,18 @@ @@ -359,15 +344,18 @@
359 justify-content: space-between; 344 justify-content: space-between;
360 align-items: center; 345 align-items: center;
361 margin-bottom: 24rpx; 346 margin-bottom: 24rpx;
  347 +
362 .vip-info { 348 .vip-info {
363 display: flex; 349 display: flex;
364 align-items: center; 350 align-items: center;
365 } 351 }
  352 +
366 .vip-text { 353 .vip-text {
367 color: #f1c40f; 354 color: #f1c40f;
368 font-size: 24rpx; 355 font-size: 24rpx;
369 margin-left: 14rpx; 356 margin-left: 14rpx;
370 } 357 }
  358 +
371 .vip-btn { 359 .vip-btn {
372 background: linear-gradient(90deg, #f1c40f, #f39c12); 360 background: linear-gradient(90deg, #f1c40f, #f39c12);
373 color: #333; 361 color: #333;
@@ -381,13 +369,16 @@ @@ -381,13 +369,16 @@
381 .quick-entry { 369 .quick-entry {
382 display: flex; 370 display: flex;
383 justify-content: space-around; 371 justify-content: space-around;
  372 +
384 .entry-item { 373 .entry-item {
385 text-align: center; 374 text-align: center;
  375 +
386 .num { 376 .num {
387 font-size: 38rpx; 377 font-size: 38rpx;
388 font-weight: bold; 378 font-weight: bold;
389 color: #333; 379 color: #333;
390 } 380 }
  381 +
391 .label { 382 .label {
392 font-size: 22rpx; 383 font-size: 22rpx;
393 color: #999; 384 color: #999;
@@ -402,6 +393,7 @@ @@ -402,6 +393,7 @@
402 margin-bottom: 24rpx; 393 margin-bottom: 24rpx;
403 border-radius: 20rpx; 394 border-radius: 20rpx;
404 overflow: hidden; 395 overflow: hidden;
  396 +
405 .banner-img { 397 .banner-img {
406 width: 100%; 398 width: 100%;
407 height: 100%; 399 height: 100%;
@@ -411,10 +403,12 @@ @@ -411,10 +403,12 @@
411 .apply-section { 403 .apply-section {
412 display: grid; 404 display: grid;
413 grid-template-columns: repeat(5, 1fr); 405 grid-template-columns: repeat(5, 1fr);
  406 +
414 .apply-item { 407 .apply-item {
415 display: flex; 408 display: flex;
416 flex-direction: column; 409 flex-direction: column;
417 align-items: center; 410 align-items: center;
  411 +
418 .icon-bg { 412 .icon-bg {
419 width: 80rpx; 413 width: 80rpx;
420 height: 80rpx; 414 height: 80rpx;
@@ -424,11 +418,13 @@ @@ -424,11 +418,13 @@
424 align-items: center; 418 align-items: center;
425 justify-content: center; 419 justify-content: center;
426 margin-bottom: 12rpx; 420 margin-bottom: 12rpx;
  421 +
427 .img { 422 .img {
428 width: 44rpx; 423 width: 44rpx;
429 height: 44rpx; 424 height: 44rpx;
430 } 425 }
431 } 426 }
  427 +
432 .text { 428 .text {
433 font-size: 22rpx; 429 font-size: 22rpx;
434 color: #666; 430 color: #666;
@@ -440,14 +436,17 @@ @@ -440,14 +436,17 @@
440 display: grid; 436 display: grid;
441 grid-template-columns: repeat(4, 1fr); 437 grid-template-columns: repeat(4, 1fr);
442 row-gap: 34rpx; 438 row-gap: 34rpx;
  439 +
443 .account-item { 440 .account-item {
444 display: flex; 441 display: flex;
445 flex-direction: column; 442 flex-direction: column;
446 align-items: center; 443 align-items: center;
  444 +
447 .acc-val { 445 .acc-val {
448 font-size: 30rpx; 446 font-size: 30rpx;
449 font-weight: bold; 447 font-weight: bold;
450 color: #333; 448 color: #333;
  449 +
451 .unit { 450 .unit {
452 font-size: 20rpx; 451 font-size: 20rpx;
453 font-weight: normal; 452 font-weight: normal;
@@ -455,6 +454,7 @@ @@ -455,6 +454,7 @@
455 margin-left: 2rpx; 454 margin-left: 2rpx;
456 } 455 }
457 } 456 }
  457 +
458 .acc-lab { 458 .acc-lab {
459 font-size: 22rpx; 459 font-size: 22rpx;
460 color: #999; 460 color: #999;
@@ -467,22 +467,27 @@ @@ -467,22 +467,27 @@
467 display: grid; 467 display: grid;
468 grid-template-columns: repeat(4, 1fr); 468 grid-template-columns: repeat(4, 1fr);
469 row-gap: 40rpx; 469 row-gap: 40rpx;
  470 +
470 .icon-item { 471 .icon-item {
471 display: flex; 472 display: flex;
472 flex-direction: column; 473 flex-direction: column;
473 align-items: center; 474 align-items: center;
474 position: relative; 475 position: relative;
  476 +
475 .icon-img-wrap { 477 .icon-img-wrap {
476 position: relative; 478 position: relative;
477 margin-bottom: 12rpx; 479 margin-bottom: 12rpx;
  480 +
478 .icon-img { 481 .icon-img {
479 width: 46rpx; 482 width: 46rpx;
480 height: 46rpx; 483 height: 46rpx;
481 } 484 }
  485 +
482 .badge { 486 .badge {
483 position: absolute; 487 position: absolute;
484 top: -12rpx; 488 top: -12rpx;
485 - right: -42rpx; /* 适当拓宽右移,容纳后端多字文本 */ 489 + right: -42rpx;
  490 + /* 适当拓宽右移,容纳后端多字文本 */
486 background: #ff4d4f; 491 background: #ff4d4f;
487 color: #fff; 492 color: #fff;
488 font-size: 18rpx; 493 font-size: 18rpx;
@@ -490,6 +495,7 @@ @@ -490,6 +495,7 @@
490 border-radius: 16rpx; 495 border-radius: 16rpx;
491 white-space: nowrap; 496 white-space: nowrap;
492 } 497 }
  498 +
493 /* 未读状态小红点 */ 499 /* 未读状态小红点 */
494 .dot-badge { 500 .dot-badge {
495 position: absolute; 501 position: absolute;
@@ -501,21 +507,25 @@ @@ -501,21 +507,25 @@
501 border-radius: 50%; 507 border-radius: 50%;
502 } 508 }
503 } 509 }
  510 +
504 .icon-text { 511 .icon-text {
505 font-size: 24rpx; 512 font-size: 24rpx;
506 color: #555; 513 color: #555;
507 } 514 }
  515 +
508 .mp-contact-overlay-btn { 516 .mp-contact-overlay-btn {
509 position: absolute; 517 position: absolute;
510 top: 0; 518 top: 0;
511 left: 0; 519 left: 0;
512 width: 100% !important; 520 width: 100% !important;
513 height: 100% !important; 521 height: 100% !important;
514 - opacity: 0 !important; /* 核心:完全透明 */ 522 + opacity: 0 !important;
  523 + /* 核心:完全透明 */
515 border: none !important; 524 border: none !important;
516 padding: 0 !important; 525 padding: 0 !important;
517 margin: 0 !important; 526 margin: 0 !important;
518 - z-index: 10; /* 确保盖在图表和文字的最上方 */ 527 + z-index: 10;
  528 + /* 确保盖在图表和文字的最上方 */
519 529
520 &::after { 530 &::after {
521 border: none !important; 531 border: none !important;
@@ -530,9 +540,11 @@ @@ -530,9 +540,11 @@
530 align-items: center; 540 align-items: center;
531 padding: 24rpx 0; 541 padding: 24rpx 0;
532 border-bottom: 1rpx solid #f9f9f9; 542 border-bottom: 1rpx solid #f9f9f9;
  543 +
533 &:last-child { 544 &:last-child {
534 border-bottom: none; 545 border-bottom: none;
535 } 546 }
  547 +
536 .setting-text { 548 .setting-text {
537 font-size: 28rpx; 549 font-size: 28rpx;
538 color: #444; 550 color: #444;
@@ -542,8 +554,9 @@ @@ -542,8 +554,9 @@
542 .opacity-hover { 554 .opacity-hover {
543 opacity: 0.7; 555 opacity: 0.7;
544 } 556 }
  557 +
545 .card-hover { 558 .card-hover {
546 background-color: #fcfcfc; 559 background-color: #fcfcfc;
547 } 560 }
548 - } 561 +}
549 </style> 562 </style>
@@ -295,7 +295,7 @@ const handleConfirm = async () => { @@ -295,7 +295,7 @@ const handleConfirm = async () => {
295 exerciseId: detail.id, 295 exerciseId: detail.id,
296 exerciseName: detail.name, 296 exerciseName: detail.name,
297 exerciseType: detail.exerciseType || 1, 297 exerciseType: detail.exerciseType || 1,
298 - urlImage: detail.urlImage || detail.url3dAnimation, 298 + urlImage: detail.url3dAnimation || detail.urlImage,
299 categoryDescription: detail.categoryDescription, 299 categoryDescription: detail.categoryDescription,
300 equipmentDescription: detail.equipmentDescription, 300 equipmentDescription: detail.equipmentDescription,
301 } 301 }
@@ -313,7 +313,8 @@ const handleConfirm = async () => { @@ -313,7 +313,8 @@ const handleConfirm = async () => {
313 exerciseId: e.id, 313 exerciseId: e.id,
314 exerciseName: e.name, 314 exerciseName: e.name,
315 exerciseType: e.exerciseType || 1, 315 exerciseType: e.exerciseType || 1,
316 - urlImage: e.urlImage || e.url3dAnimation, 316 + urlImage: e.url3dAnimation || e.urlImage,
  317 + // urlImage: e.urlImage || e.url3dAnimation,
317 })) 318 }))
318 }; 319 };
319 } 320 }
@@ -35,17 +35,20 @@ @@ -35,17 +35,20 @@
35 </view> 35 </view>
36 36
37 <view class="close-btn" @click="close"> 37 <view class="close-btn" @click="close">
38 - <text>×</text> 38 + <text>x</text>
39 </view> 39 </view>
40 </view> 40 </view>
41 -  
42 - <WodeJihuaTianjiaTancuang v-model:visible="showPlanPopup" @get-plan-list-length="getPlanListLength" :isAdd="true" /> 41 + <!-- 我的计划添加弹窗,显示最新添加的计划的模板列表 传递-->
  42 + <WodeJihuaTianjiaTancuang v-model:visible="showPlanPopup" @get-plan-list-length="getPlanListLength" :isAdd="true"
  43 + :plan-id="lastPlanId" :is-my-plan="true" />
43 </view> 44 </view>
44 </template> 45 </template>
45 46
46 <script setup> 47 <script setup>
47 -import { ref } from 'vue' 48 +import { ref, onMounted, computed } from 'vue'
48 import WodeJihuaTianjiaTancuang from '@/pages/xunji/components/wode-jihua-tianjia-tancuang.vue' 49 import WodeJihuaTianjiaTancuang from '@/pages/xunji/components/wode-jihua-tianjia-tancuang.vue'
  50 +import QueryPlanApi from '@/sheep/api/plan/queryplan'
  51 +
49 52
50 // 控制训练计划弹窗显示 53 // 控制训练计划弹窗显示
51 const showPlanPopup = ref(false) 54 const showPlanPopup = ref(false)
@@ -84,6 +87,38 @@ const handleFreeTraining = () => { @@ -84,6 +87,38 @@ const handleFreeTraining = () => {
84 uni.navigateTo({ url: '/pages4/pages/xunji/xunji-dongzuo-lianxi' }) 87 uni.navigateTo({ url: '/pages4/pages/xunji/xunji-dongzuo-lianxi' })
85 close() 88 close()
86 } 89 }
  90 +
  91 +// 获取我的计划列表的最后一个planid,传递到WodeJihuaTianjiaTancuang中
  92 +// 计划列表
  93 +const planList = ref([])
  94 +
  95 +// 获取我的全部训练计划
  96 +const fetchMyPlanList = async () => {
  97 + try {
  98 + const res = await QueryPlanApi.getMyPlan()
  99 + if (res.code === 0 && res.data) {
  100 + planList.value = res.data
  101 + } else {
  102 + uni.showToast({ title: '获取计划失败', icon: 'none' })
  103 + }
  104 + } catch (err) {
  105 + console.error('获取计划列表报错:', err)
  106 + uni.showToast({ title: '网络异常', icon: 'none' })
  107 + }
  108 +}
  109 +
  110 +// 计算属性:拿到列表最后一条计划id,无数据返回null
  111 +const lastPlanId = computed(() => {
  112 + if (!Array.isArray(planList.value) || planList.value.length === 0) return null
  113 + const lastPlan = planList.value.at(-1)
  114 + return lastPlan?.id ?? null
  115 +})
  116 +
  117 +onMounted(() => {
  118 + fetchMyPlanList()
  119 +})
  120 +
  121 +
87 </script> 122 </script>
88 123
89 <style lang="scss" scoped> 124 <style lang="scss" scoped>
@@ -111,8 +146,8 @@ const handleFreeTraining = () => { @@ -111,8 +146,8 @@ const handleFreeTraining = () => {
111 display: flex; 146 display: flex;
112 flex-direction: column; 147 flex-direction: column;
113 align-items: center; 148 align-items: center;
114 - /* 选项和关闭按钮间距 */  
115 gap: 70rpx; 149 gap: 70rpx;
  150 + margin-bottom: 100rpx;
116 } 151 }
117 152
118 /* 选项列表 */ 153 /* 选项列表 */
@@ -33,8 +33,8 @@ const closeAddTrainPopup = () => { @@ -33,8 +33,8 @@ const closeAddTrainPopup = () => {
33 <style lang="scss" scoped> 33 <style lang="scss" scoped>
34 .float-btn { 34 .float-btn {
35 position: fixed; 35 position: fixed;
36 - right: 30rpx;  
37 - bottom: 10rpx; 36 + right: 11rpx;
  37 + bottom: 169rpx;
38 width: 100rpx; 38 width: 100rpx;
39 height: 100rpx; 39 height: 100rpx;
40 background-color: #ffcc00; 40 background-color: #ffcc00;
@@ -105,10 +105,10 @@ @@ -105,10 +105,10 @@
105 {{ formatSecondsToHms(set.duration) }} 105 {{ formatSecondsToHms(set.duration) }}
106 </text> 106 </text>
107 <text class="set-content" v-if="[4, 5].includes(unit.exercises[0].exerciseType)"> 107 <text class="set-content" v-if="[4, 5].includes(unit.exercises[0].exerciseType)">
108 - {{ set.weight }}kg x {{ set.reps }}次 108 + {{ set.weight }}kg x {{ set.duration }}s x {{ set.restTime }}
109 </text> 109 </text>
110 <text class="set-content" v-if="unit.exercises[0].exerciseType === 6"> 110 <text class="set-content" v-if="unit.exercises[0].exerciseType === 6">
111 - {{ formatSecondsToHms(set.duration) }} 111 + {{ set.reps }}次 x {{ formatSecondsToHms(set.duration) }}
112 </text> 112 </text>
113 <text class="rest-time" v-if="set.restTime && unit.exercises[0].exerciseType != 6"> 113 <text class="rest-time" v-if="set.restTime && unit.exercises[0].exerciseType != 6">
114 {{ set.restTime || 0 }}s 114 {{ set.restTime || 0 }}s
@@ -464,8 +464,8 @@ const handleCompleteCheck = async () => { @@ -464,8 +464,8 @@ const handleCompleteCheck = async () => {
464 try { 464 try {
465 // 2. 调用接口 465 // 2. 调用接口
466 await dailytemplateApi.completeDailyTemplate(currentPlan.value.id) 466 await dailytemplateApi.completeDailyTemplate(currentPlan.value.id)
  467 + emit('refreshCalendar');
467 uni.showToast({ title: '一键打勾成功', icon: 'success' }) 468 uni.showToast({ title: '一键打勾成功', icon: 'success' })
468 -  
469 closeMorePopup() 469 closeMorePopup()
470 470
471 // 刷新页面数据 471 // 刷新页面数据
@@ -794,7 +794,7 @@ const startTraining = () => { @@ -794,7 +794,7 @@ const startTraining = () => {
794 uni.navigateTo({ 794 uni.navigateTo({
795 url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${currentPlan.value.templateId}&type=3&dailyTemplateId=${currentPlan.value.id}&isTraining=true`, 795 url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${currentPlan.value.templateId}&type=3&dailyTemplateId=${currentPlan.value.id}&isTraining=true`,
796 }); 796 });
797 - console.log('开始进入编辑模板页面,模板ID:', currentPlan.value.templateId, '每日模板ID:', currentPlan.value.id); 797 + console.log('去训练开始进入编辑模板页面,模板ID:', currentPlan.value.templateId, '每日模板ID:', currentPlan.value.id);
798 798
799 console.log('打印传递给动作训练页面的模板id', currentPlan.value.templateId); 799 console.log('打印传递给动作训练页面的模板id', currentPlan.value.templateId);
800 } else { 800 } else {
@@ -75,7 +75,7 @@ watch( @@ -75,7 +75,7 @@ watch(
75 if (rtype === 1) { 75 if (rtype === 1) {
76 newUnit = { 76 newUnit = {
77 unitType: 1, unitId: detail.id, unitName: detail.name, 77 unitType: 1, unitId: detail.id, unitName: detail.name,
78 - exercises: [{ exerciseId: detail.id, exerciseName: detail.name, exerciseType: detail.exerciseType, urlImage: detail.urlImage || detail.url3dAnimation, categoryDescription: detail.categoryDescription || '', equipmentDescription: detail.equipmentDescription || '', sets: [] }] 78 + exercises: [{ exerciseId: detail.id, exerciseName: detail.name, exerciseType: detail.exerciseType, urlImage: detail.url3dAnimation || detail.urlImage, categoryDescription: detail.categoryDescription || '', equipmentDescription: detail.equipmentDescription || '', sets: [] }]
79 } 79 }
80 } else { 80 } else {
81 newUnit = { 81 newUnit = {
@@ -104,40 +104,71 @@ const currUnit = computed(() => sourceInfo.value.unit || {}) @@ -104,40 +104,71 @@ const currUnit = computed(() => sourceInfo.value.unit || {})
104 const currUnitIndex = computed(() => sourceInfo.value.unitIndex ?? 0) 104 const currUnitIndex = computed(() => sourceInfo.value.unitIndex ?? 0)
105 105
106 // 格式化数据:统一 动作 / 超级组 结构 → 给 dongzuo 组件用 106 // 格式化数据:统一 动作 / 超级组 结构 → 给 dongzuo 组件用
  107 +// const formatDongzuoData = (unit) => {
  108 +// if (!unit) return {}
  109 +// console.log('++每日模板的unit++', unit);
  110 +
  111 +// if (unit.unitType === 1) {
  112 +// const ex = unit.exercises?.[0] || {}
  113 +// return {
  114 +// id: ex.exerciseId,
  115 +// name: ex.exerciseName,
  116 +// urlImage: ex.url3dAnimation,
  117 +// exerciseType: ex.exerciseType,
  118 +// categoryDescription: ex.categoryDescription || '',
  119 +// equipmentDescription: ex.equipmentDescription || '',
  120 +// }
  121 +// }
  122 +
  123 +// if (unit.unitType === 2) {
  124 +// return {
  125 +// id: unit.unitId,
  126 +// name: unit.unitName || '超级组',
  127 +// exercises: unit.exercises?.map(ex => ({
  128 +// id: ex.exerciseId,
  129 +// name: ex.exerciseName,
  130 +// urlImage: ex.url3dAnimation,
  131 +// exerciseType: ex.exerciseType,
  132 +// })) || []
  133 +// }
  134 +// }
  135 +
  136 +// return {}
  137 +// }
107 const formatDongzuoData = (unit) => { 138 const formatDongzuoData = (unit) => {
108 if (!unit) return {} 139 if (!unit) return {}
  140 + console.log('++每日模板的unit++', unit);
  141 + let result = {} // 定义接收结果的变量
109 142
110 // 1. 普通动作 type=1 143 // 1. 普通动作 type=1
111 if (unit.unitType === 1) { 144 if (unit.unitType === 1) {
112 const ex = unit.exercises?.[0] || {} 145 const ex = unit.exercises?.[0] || {}
113 - return { 146 + result = {
114 id: ex.exerciseId, 147 id: ex.exerciseId,
115 name: ex.exerciseName, 148 name: ex.exerciseName,
116 - urlImage: ex.urlImage, 149 + urlImage: ex.url3dAnimation,
117 exerciseType: ex.exerciseType, 150 exerciseType: ex.exerciseType,
118 categoryDescription: ex.categoryDescription || '', 151 categoryDescription: ex.categoryDescription || '',
119 equipmentDescription: ex.equipmentDescription || '', 152 equipmentDescription: ex.equipmentDescription || '',
120 } 153 }
121 } 154 }
122 -  
123 // 2. 超级组 type=2 155 // 2. 超级组 type=2
124 if (unit.unitType === 2) { 156 if (unit.unitType === 2) {
125 - return { 157 + result = {
126 id: unit.unitId, 158 id: unit.unitId,
127 name: unit.unitName || '超级组', 159 name: unit.unitName || '超级组',
128 - // 超级组需要把子动作全部格式化  
129 exercises: unit.exercises?.map(ex => ({ 160 exercises: unit.exercises?.map(ex => ({
130 id: ex.exerciseId, 161 id: ex.exerciseId,
131 name: ex.exerciseName, 162 name: ex.exerciseName,
132 - urlImage: ex.urlImage, 163 + urlImage: ex.url3dAnimation,
133 exerciseType: ex.exerciseType, 164 exerciseType: ex.exerciseType,
134 })) || [] 165 })) || []
135 } 166 }
136 } 167 }
137 168
138 - return {} 169 + console.log('格式化后结果:', result) // 打印最终数据
  170 + return result
139 } 171 }
140 -  
141 const handleDeleteAction = () => { 172 const handleDeleteAction = () => {
142 console.log('父组件处理删除动作') 173 console.log('父组件处理删除动作')
143 needDeleteUnit.value = true 174 needDeleteUnit.value = true
@@ -442,7 +473,7 @@ defineExpose({ open }) @@ -442,7 +473,7 @@ defineExpose({ open })
442 // padding: 0 20rpx 120rpx; 473 // padding: 0 20rpx 120rpx;
443 margin: 0 20rpx; 474 margin: 0 20rpx;
444 height: calc(100vh - 120rpx); 475 height: calc(100vh - 120rpx);
445 - background: #f5b5b5; 476 + background: #f5f5f5;
446 max-height: 70vh; 477 max-height: 70vh;
447 } 478 }
448 </style> 479 </style>
1 <template> 1 <template>
2 - <!-- 具体计划的模板列表(在模板详情和我的计划,还有黄色小球中挂载) --> 2 + <!-- 具体计划的模板列表(在模板详情和我的计划,还有黄色小球中挂载) 如果没有输入计划id,就是显示最后一个添加的计划-->
3 <up-popup :show="props.visible" mode="bottom" round="24rpx" @close="emit('update:visible', false)"> 3 <up-popup :show="props.visible" mode="bottom" round="24rpx" @close="emit('update:visible', false)">
4 <view class="up-popup-contain"> 4 <view class="up-popup-contain">
5 <!-- 顶部滑块 --> 5 <!-- 顶部滑块 -->
@@ -50,6 +50,7 @@ @@ -50,6 +50,7 @@
50 </view> 50 </view>
51 </up-popup> 51 </up-popup>
52 52
  53 + <!-- 计划列表弹窗,显示计划列表,选择后会回到当前计划包含的模板列表,可以去训练或者添加模板到日历 -->
53 <WodeJihuaLibiaoTancuang v-if="showPlanList && isAdd" ref="planListRef" @close="showPlanList = false" 54 <WodeJihuaLibiaoTancuang v-if="showPlanList && isAdd" ref="planListRef" @close="showPlanList = false"
54 :show="showPlanList" :MyPlanList=MyPlanList @select="onSelectPlan" /> 55 :show="showPlanList" :MyPlanList=MyPlanList @select="onSelectPlan" />
55 56
@@ -81,6 +82,10 @@ const props = defineProps({ @@ -81,6 +82,10 @@ const props = defineProps({
81 planId: { 82 planId: {
82 type: Number, 83 type: Number,
83 default: null 84 default: null
  85 + },
  86 + isMyPlan: {
  87 + type: Boolean,
  88 + default: false
84 } 89 }
85 }) 90 })
86 const emit = defineEmits(['update:visible', 'getPlanListLength']) 91 const emit = defineEmits(['update:visible', 'getPlanListLength'])
@@ -91,9 +96,17 @@ const MyPlanDetail = ref({}) @@ -91,9 +96,17 @@ const MyPlanDetail = ref({})
91 // ====================== 核心修复:获取计划详情 ====================== 96 // ====================== 核心修复:获取计划详情 ======================
92 const getMyPlanDetail = async (planId) => { 97 const getMyPlanDetail = async (planId) => {
93 if (!planId) return; 98 if (!planId) return;
  99 + let res;
94 try { 100 try {
95 - const res = await QueryPlanApi.getMyPlanDetail(planId);  
96 - console.log('✅ 计划详情获取成功 planId=', planId, res.data); 101 + if (props.isMyPlan) {
  102 + res = await QueryPlanApi.getMyPlanDetail(planId);
  103 + console.log('✅ 我的计划详情获取成功 planId=', planId, res.data);
  104 + }
  105 + else {
  106 + res = await QueryPlanApi.getPlanDetail(planId);
  107 + console.log('✅ 不是我的计划,官方计划,详情获取成功 planId=', planId, res.data);
  108 + }
  109 +
97 if (res.data) { 110 if (res.data) {
98 MyPlanDetail.value = res.data; 111 MyPlanDetail.value = res.data;
99 } else { 112 } else {
@@ -176,6 +189,8 @@ const getMyPlanList = async () => { @@ -176,6 +189,8 @@ const getMyPlanList = async () => {
176 189
177 onMounted(() => { 190 onMounted(() => {
178 getMyPlanList(); 191 getMyPlanList();
  192 + // console.log('props.planId=', planId);
  193 +
179 }); 194 });
180 195
181 </script> 196 </script>
@@ -380,7 +380,7 @@ onMounted(() => { @@ -380,7 +380,7 @@ onMounted(() => {
380 padding-bottom: 20rpx; 380 padding-bottom: 20rpx;
381 // #ifdef MP-WEIXIN 381 // #ifdef MP-WEIXIN
382 // 微信小程序端可能需要更多底部空间 382 // 微信小程序端可能需要更多底部空间
383 - padding-bottom: 100rpx; 383 + padding-bottom: 200rpx;
384 384
385 // #endif 385 // #endif
386 .nav-item { 386 .nav-item {
@@ -178,11 +178,14 @@ const loadPlans = async (year, month) => { @@ -178,11 +178,14 @@ const loadPlans = async (year, month) => {
178 const nextYear = month + 1 > 12 ? year + 1 : year; 178 const nextYear = month + 1 > 12 ? year + 1 : year;
179 179
180 // 并行请求三个月数据 180 // 并行请求三个月数据
181 - const [prevRes, currRes, nextRes] = await Promise.all([  
182 - DailyTemplateApi.getCalendarPlans(prevYear, prevMonth),  
183 - DailyTemplateApi.getCalendarPlans(year, month),  
184 - DailyTemplateApi.getCalendarPlans(nextYear, nextMonth),  
185 - ]); 181 + // const [prevRes, currRes, nextRes] = await Promise.all([
  182 + // DailyTemplateApi.getCalendarPlans(prevYear, prevMonth),
  183 + // DailyTemplateApi.getCalendarPlans(year, month),
  184 + // DailyTemplateApi.getCalendarPlans(nextYear, nextMonth),
  185 + // ]);
  186 + const prevRes = await DailyTemplateApi.getCalendarPlans(prevYear, prevMonth);
  187 + const currRes = await DailyTemplateApi.getCalendarPlans(year, month);
  188 + const nextRes = await DailyTemplateApi.getCalendarPlans(nextYear, nextMonth);
186 189
187 // 合并三个月数据 190 // 合并三个月数据
188 const allPlans = [ 191 const allPlans = [
@@ -315,10 +318,7 @@ const selectDate = async (date) => { @@ -315,10 +318,7 @@ const selectDate = async (date) => {
315 selectedDate.value = date.dateKey; 318 selectedDate.value = date.dateKey;
316 // ShowGridCellPopup.value = true; 319 // ShowGridCellPopup.value = true;
317 await nextTick() 320 await nextTick()
318 - console.log('AAAAAAAAAAAAAAAAAAAAAAAAAAA');  
319 gridPopupRef.value?.openPopup(); 321 gridPopupRef.value?.openPopup();
320 - console.log('BBBBBBBBBBBBBBBBBB');  
321 -  
322 }; 322 };
323 323
324 </script> 324 </script>
@@ -5,26 +5,20 @@ @@ -5,26 +5,20 @@
5 <view class="banner-section"> 5 <view class="banner-section">
6 <!-- 跳转有问题 --> 6 <!-- 跳转有问题 -->
7 <view class="item" @click="goToActionLibrary"> 7 <view class="item" @click="goToActionLibrary">
8 - <image  
9 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/21_1773626442072.png"  
10 - class="img"  
11 - ></image> 8 + <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/21_1773626442072.png"
  9 + class="img"></image>
12 <view class="title">动作库</view> 10 <view class="title">动作库</view>
13 <view class="desc">真人视频讲解</view> 11 <view class="desc">真人视频讲解</view>
14 </view> 12 </view>
15 <view class="item"> 13 <view class="item">
16 - <image  
17 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/22_1773626449583.png"  
18 - class="img"  
19 - ></image> 14 + <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/22_1773626449583.png"
  15 + class="img"></image>
20 <view class="title">训练计划</view> 16 <view class="title">训练计划</view>
21 <view class="desc">周期性目标达成</view> 17 <view class="desc">周期性目标达成</view>
22 </view> 18 </view>
23 <view class="item"> 19 <view class="item">
24 - <image  
25 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/23_1773627239158.png"  
26 - class="img"  
27 - ></image> 20 + <image src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/23_1773627239158.png"
  21 + class="img"></image>
28 <view class="title">训练模板</view> 22 <view class="title">训练模板</view>
29 <view class="desc">精选课程模板</view> 23 <view class="desc">精选课程模板</view>
30 </view> 24 </view>
@@ -37,12 +31,7 @@ @@ -37,12 +31,7 @@
37 </view> 31 </view>
38 <view class="plan-list"> 32 <view class="plan-list">
39 <!-- 点击可跳转 --> 33 <!-- 点击可跳转 -->
40 - <view  
41 - v-for="(item, index) in planList"  
42 - :key="index"  
43 - class="plan-item"  
44 - @tap="goPlanDetail(item)"  
45 - > 34 + <view v-for="(item, index) in planList" :key="index" class="plan-item" @tap="goPlanDetail(item)">
46 <image :src="item.cover" mode="aspectFill" class="plan-cover" /> 35 <image :src="item.cover" mode="aspectFill" class="plan-cover" />
47 <view class="plan-desc"> 36 <view class="plan-desc">
48 <text class="plan-tag" :class="item.tagClass">{{ item.tag }}</text> 37 <text class="plan-tag" :class="item.tagClass">{{ item.tag }}</text>
@@ -58,35 +47,19 @@ @@ -58,35 +47,19 @@
58 <view class="recommend-header"> 47 <view class="recommend-header">
59 <text class="header-title">为你推荐</text> 48 <text class="header-title">为你推荐</text>
60 <view class="tabs"> 49 <view class="tabs">
61 - <button  
62 - class="tab-btn"  
63 - :class="{ active: currentTab === 'recommend' }"  
64 - @tap="switchTab('recommend')"  
65 - > 50 + <button class="tab-btn" :class="{ active: currentTab === 'recommend' }" @tap="switchTab('recommend')">
66 推荐 51 推荐
67 </button> 52 </button>
68 - <button  
69 - class="tab-btn"  
70 - :class="{ active: currentTab === 'group' }"  
71 - @tap="switchTab('group')"  
72 - > 53 + <button class="tab-btn" :class="{ active: currentTab === 'group' }" @tap="switchTab('group')">
73 团课 54 团课
74 </button> 55 </button>
75 - <button  
76 - class="tab-btn"  
77 - :class="{ active: currentTab === 'private' }"  
78 - @tap="switchTab('private')"  
79 - > 56 + <button class="tab-btn" :class="{ active: currentTab === 'private' }" @tap="switchTab('private')">
80 私教 57 私教
81 </button> 58 </button>
82 <!-- <button class="tab-btn" :class="{ active: currentTab === 'small' }" @tap="switchTab('small')"> 59 <!-- <button class="tab-btn" :class="{ active: currentTab === 'small' }" @tap="switchTab('small')">
83 小班课 60 小班课
84 </button> --> 61 </button> -->
85 - <button  
86 - class="tab-btn"  
87 - :class="{ active: currentTab === 'follow' }"  
88 - @tap="switchTab('follow')"  
89 - > 62 + <button class="tab-btn" :class="{ active: currentTab === 'follow' }" @tap="switchTab('follow')">
90 关注 63 关注
91 </button> 64 </button>
92 </view> 65 </view>
@@ -96,21 +69,12 @@ @@ -96,21 +69,12 @@
96 69
97 <view class="coach-section"> 70 <view class="coach-section">
98 <view class="coach-list"> 71 <view class="coach-list">
99 - <view  
100 - class="coach-item"  
101 - v-for="(item, index) in currentRecommendList"  
102 - :key="item.id"  
103 - @click="navigateToDetail(item)"  
104 - > 72 + <view class="coach-item" v-for="(item, index) in currentRecommendList" :key="item.id"
  73 + @click="navigateToDetail(item)">
105 <!-- 1. 如果有视频 --> 74 <!-- 1. 如果有视频 -->
106 <template v-if="item.video"> 75 <template v-if="item.video">
107 - <video  
108 - :src="item.video"  
109 - :controls="false"  
110 - :show-fullscreen-btn="false"  
111 - :show-center-play-btn="false"  
112 - class="video"  
113 - ></video> 76 + <video :src="item.video" :controls="false" :show-fullscreen-btn="false" :show-center-play-btn="false"
  77 + class="video"></video>
114 <up-icon name="play-right" size="20" class="icon" color="#fff"></up-icon> 78 <up-icon name="play-right" size="20" class="icon" color="#fff"></up-icon>
115 </template> 79 </template>
116 <!-- 2. 如果没有视频 --> 80 <!-- 2. 如果没有视频 -->
@@ -122,12 +86,8 @@ @@ -122,12 +86,8 @@
122 <view class="coach-meta"> 86 <view class="coach-meta">
123 <image :src="item.coachAvatar" mode="aspectFill" class="coach-avatar" /> 87 <image :src="item.coachAvatar" mode="aspectFill" class="coach-avatar" />
124 <text class="coach-name">{{ item.coachName }}</text> 88 <text class="coach-name">{{ item.coachName }}</text>
125 - <up-icon  
126 - name="thumb-up"  
127 - :color="item.isLike ? '#D96248' : '#666'"  
128 - size="20"  
129 - @click="handleDynamicLike(item, index)"  
130 - ></up-icon> 89 + <up-icon name="thumb-up" :color="item.isLike ? '#D96248' : '#666'" size="20"
  90 + @click="handleDynamicLike(item, index)"></up-icon>
131 </view> 91 </view>
132 </view> 92 </view>
133 </view> 93 </view>
@@ -162,46 +122,48 @@ @@ -162,46 +122,48 @@
162 </template> 122 </template>
163 123
164 <script setup> 124 <script setup>
165 - import { ref, computed, onMounted } from 'vue';  
166 - import QueryPlanApi from '@/sheep/api/plan/queryplan';  
167 - import coachUpdatesApi from '@/sheep/api/Coach/CoachUpdates'; // 请确认路径正确  
168 - import useUserStore from '@/sheep/store/user';  
169 - const userStore = useUserStore();  
170 - // ====== Tab 状态 ======  
171 - const currentTab = ref('recommend');  
172 - const tabData = ref({ 125 +import { ref, computed, onMounted } from 'vue';
  126 +import QueryPlanApi from '@/sheep/api/plan/queryplan';
  127 +import coachUpdatesApi from '@/sheep/api/Coach/CoachUpdates'; // 请确认路径正确
  128 +import useUserStore from '@/sheep/store/user';
  129 +import floatYellowBtn from '@/pages/xunji/components/rili-components/float-yellow-btn.vue';
  130 +
  131 +const userStore = useUserStore();
  132 +// ====== Tab 状态 ======
  133 +const currentTab = ref('recommend');
  134 +const tabData = ref({
173 // finished: false → 还有没有更多数据可以加载?false代表还有更多数据可以加载 135 // finished: false → 还有没有更多数据可以加载?false代表还有更多数据可以加载
174 recommend: { list: [], pageNo: 1, finished: false }, 136 recommend: { list: [], pageNo: 1, finished: false },
175 group: { list: [], pageNo: 1, finished: false }, 137 group: { list: [], pageNo: 1, finished: false },
176 private: { list: [], pageNo: 1, finished: false }, 138 private: { list: [], pageNo: 1, finished: false },
177 small: { list: [], pageNo: 1, finished: false }, 139 small: { list: [], pageNo: 1, finished: false },
178 follow: { list: [], pageNo: 1, finished: false }, 140 follow: { list: [], pageNo: 1, finished: false },
179 - });  
180 - const loading = ref(false); 141 +});
  142 +const loading = ref(false);
181 143
182 - // 当前显示的列表(computed)  
183 - const currentRecommendList = computed(() => { 144 +// 当前显示的列表(computed)
  145 +const currentRecommendList = computed(() => {
184 return tabData.value[currentTab.value]?.list || []; 146 return tabData.value[currentTab.value]?.list || [];
185 - }); 147 +});
186 148
187 - const finished = computed(() => { 149 +const finished = computed(() => {
188 return tabData.value[currentTab.value]?.finished || false; 150 return tabData.value[currentTab.value]?.finished || false;
189 - }); 151 +});
190 152
191 - // 获取 storeId(请根据你项目实际情况调整)  
192 - const storeId = userStore.initUser.storeId;  
193 - // 切换 Tab  
194 - const switchTab = (type) => { 153 +// 获取 storeId(请根据你项目实际情况调整)
  154 +const storeId = userStore.initUser.storeId;
  155 +// 切换 Tab
  156 +const switchTab = (type) => {
195 if (currentTab.value === type) return; 157 if (currentTab.value === type) return;
196 currentTab.value = type; 158 currentTab.value = type;
197 // 如果该 tab 还没加载过,触发加载 159 // 如果该 tab 还没加载过,触发加载
198 if (tabData.value[type].list.length === 0 && !tabData.value[type].finished) { 160 if (tabData.value[type].list.length === 0 && !tabData.value[type].finished) {
199 loadRecommendData(); 161 loadRecommendData();
200 } 162 }
201 - }; 163 +};
202 164
203 - // 加载推荐数据  
204 - const loadRecommendData = async () => { 165 +// 加载推荐数据
  166 +const loadRecommendData = async () => {
205 const tab = currentTab.value; 167 const tab = currentTab.value;
206 const tabInfo = tabData.value[tab]; 168 const tabInfo = tabData.value[tab];
207 if (loading.value || tabInfo.finished) return; 169 if (loading.value || tabInfo.finished) return;
@@ -242,18 +204,18 @@ @@ -242,18 +204,18 @@
242 } finally { 204 } finally {
243 loading.value = false; 205 loading.value = false;
244 } 206 }
245 - }; 207 +};
246 208
247 - // 上拉加载更多  
248 - const onScrollToLower = () => { 209 +// 上拉加载更多
  210 +const onScrollToLower = () => {
249 if (!finished.value && !loading.value) { 211 if (!finished.value && !loading.value) {
250 loadRecommendData(); 212 loadRecommendData();
251 } 213 }
252 - }; 214 +};
253 215
254 - const planList = ref([]);  
255 - // 新手第一课接口,如果有数据就循环显示数据,没有就展示默认暂无新手计划  
256 - const loadPlans = async () => { 216 +const planList = ref([]);
  217 +// 新手第一课接口,如果有数据就循环显示数据,没有就展示默认暂无新手计划
  218 +const loadPlans = async () => {
257 try { 219 try {
258 const res = await QueryPlanApi.getPlanList(); 220 const res = await QueryPlanApi.getPlanList();
259 const allPlans = res.data || []; 221 const allPlans = res.data || [];
@@ -271,8 +233,7 @@ @@ -271,8 +233,7 @@
271 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png', 233 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png',
272 tag: '火爆', 234 tag: '火爆',
273 tagClass: 'hot-tag', 235 tagClass: 'hot-tag',
274 - meta: ` ${difficultyText[targetPlan.difficultyLevel] || '初阶'} · ${  
275 - targetPlan.enrollmentCount 236 + meta: ` ${difficultyText[targetPlan.difficultyLevel] || '初阶'} · ${targetPlan.enrollmentCount
276 }人练过`, 237 }人练过`,
277 }, 238 },
278 ]; 239 ];
@@ -301,25 +262,25 @@ @@ -301,25 +262,25 @@
301 }, 262 },
302 ]; 263 ];
303 } 264 }
304 - }; 265 +};
305 266
306 - // 跳转方法  
307 - const goPlanDetail = (item) => { 267 +// 跳转方法
  268 +const goPlanDetail = (item) => {
308 uni.navigateTo({ 269 uni.navigateTo({
309 url: `/pages4/pages/xunji/xunji-xunlian-jihua?planid= ${item.id}`, 270 url: `/pages4/pages/xunji/xunji-xunlian-jihua?planid= ${item.id}`,
310 }); 271 });
311 - };  
312 - // 这是一个【跳转函数】,点击推荐里的内容,跳转到动态详情页,并且把当前是 “推荐 / 团课 / 私教 / 小班 / 关注” 这个分类带给下一页。  
313 - const goRecommendDetail = (item) => { 272 +};
  273 +// 这是一个【跳转函数】,点击推荐里的内容,跳转到动态详情页,并且把当前是 “推荐 / 团课 / 私教 / 小班 / 关注” 这个分类带给下一页。
  274 +const goRecommendDetail = (item) => {
314 // 传递当前 tab 类型,方便目标页初始化 275 // 传递当前 tab 类型,方便目标页初始化
315 const typeMap = { group: '1', private: '2', small: '3', recommend: '', follow: '' }; 276 const typeMap = { group: '1', private: '2', small: '3', recommend: '', follow: '' };
316 const type = typeMap[currentTab.value] || ''; 277 const type = typeMap[currentTab.value] || '';
317 uni.navigateTo({ 278 uni.navigateTo({
318 url: `/pages3/pages/coach/jiaolian-dongtai?type= ${encodeURIComponent(type)}`, 279 url: `/pages3/pages/coach/jiaolian-dongtai?type= ${encodeURIComponent(type)}`,
319 }); 280 });
320 - };  
321 - // 跳转到动态详情  
322 - const navigateToDetail = (item) => { 281 +};
  282 +// 跳转到动态详情
  283 +const navigateToDetail = (item) => {
323 if (item.photo) { 284 if (item.photo) {
324 uni.navigateTo({ 285 uni.navigateTo({
325 url: `/pages3/pages/coach/jiaolian-dongtai?id=${item.id}`, 286 url: `/pages3/pages/coach/jiaolian-dongtai?id=${item.id}`,
@@ -329,9 +290,9 @@ @@ -329,9 +290,9 @@
329 url: `/pages3/pages/coach/jiaolian-shipin-dongtai?id=${item.id}`, 290 url: `/pages3/pages/coach/jiaolian-shipin-dongtai?id=${item.id}`,
330 }); 291 });
331 } 292 }
332 - };  
333 - // 动态点赞  
334 - const handleDynamicLike = async (item, index) => { 293 +};
  294 +// 动态点赞
  295 +const handleDynamicLike = async (item, index) => {
335 // await CoachApi.likeCoachDynamic({ 296 // await CoachApi.likeCoachDynamic({
336 // id: item.id, 297 // id: item.id,
337 // status: item.isLike == 1 ? 0 : 1, 298 // status: item.isLike == 1 ? 0 : 1,
@@ -350,23 +311,23 @@ @@ -350,23 +311,23 @@
350 } catch (err) { 311 } catch (err) {
351 console.log('点赞失败', err); 312 console.log('点赞失败', err);
352 } 313 }
353 - };  
354 - // 实现动作库跳转,跳转到F:\hongxing-app\hongxing-new\pages\xunji\components\xunji-dongzuo.vue  
355 - const goToActionLibrary = () => { 314 +};
  315 +// 实现动作库跳转,跳转到F:\hongxing-app\hongxing-new\pages\xunji\components\xunji-dongzuo.vue
  316 +const goToActionLibrary = () => {
356 uni.navigateTo({ 317 uni.navigateTo({
357 url: '/pages/xunji/components/xunji-dongzuo', 318 url: '/pages/xunji/components/xunji-dongzuo',
358 }); 319 });
359 - };  
360 - // 实现  
361 - // 初始化  
362 - onMounted(() => { 320 +};
  321 +// 实现
  322 +// 初始化
  323 +onMounted(() => {
363 loadPlans(); 324 loadPlans();
364 loadRecommendData(); // 加载默认 tab(推荐)数据 325 loadRecommendData(); // 加载默认 tab(推荐)数据
365 - }); 326 +});
366 </script> 327 </script>
367 328
368 <style lang="scss" scoped> 329 <style lang="scss" scoped>
369 - .container { 330 +.container {
370 width: 100%; 331 width: 100%;
371 height: 100%; 332 height: 100%;
372 padding: 20rpx; 333 padding: 20rpx;
@@ -682,5 +643,5 @@ @@ -682,5 +643,5 @@
682 } 643 }
683 } 644 }
684 } 645 }
685 - } 646 +}
686 </style> 647 </style>
1 <template> 1 <template>
2 <view class="home-page" v-if="userStore.isLogin"> 2 <view class="home-page" v-if="userStore.isLogin">
3 <view class="tab-bar" :style="{ paddingTop: menuButtonHeight + topSafeArea + 'px' }"> 3 <view class="tab-bar" :style="{ paddingTop: menuButtonHeight + topSafeArea + 'px' }">
4 - <view class="tab-item" :class="{ active: currentTab === 0 }" @click="handleTabClick(0)"  
5 - >训记</view  
6 - >  
7 - <view class="tab-item" :class="{ active: currentTab === 1 }" @click="handleTabClick(1)"  
8 - >计划</view  
9 - >  
10 - <view class="tab-item" :class="{ active: currentTab === 2 }" @click="handleTabClick(2)"  
11 - >日历</view  
12 - >  
13 - <view class="tab-item" :class="{ active: currentTab === 3 }" @click="handleTabClick(3)"  
14 - >动作</view  
15 - >  
16 - <view class="tab-item" :class="{ active: currentTab === 4 }" @click="handleTabClick(4)"  
17 - >模板</view  
18 - >  
19 - <view class="tab-item" :class="{ active: currentTab === 5 }" @click="handleTabClick(5)"  
20 - >数据</view  
21 - > 4 + <view class="tab-item" :class="{ active: currentTab === 0 }" @click="handleTabClick(0)">训记</view>
  5 + <view class="tab-item" :class="{ active: currentTab === 1 }" @click="handleTabClick(1)">计划</view>
  6 + <view class="tab-item" :class="{ active: currentTab === 2 }" @click="handleTabClick(2)">日历</view>
  7 + <view class="tab-item" :class="{ active: currentTab === 3 }" @click="handleTabClick(3)">动作</view>
  8 + <view class="tab-item" :class="{ active: currentTab === 4 }" @click="handleTabClick(4)">模板</view>
  9 + <view class="tab-item" :class="{ active: currentTab === 5 }" @click="handleTabClick(5)">数据</view>
22 <view class="menu-btn" @click="openDrawer"> 10 <view class="menu-btn" @click="openDrawer">
23 <uni-icons type="bars" size="20"></uni-icons> 11 <uni-icons type="bars" size="20"></uni-icons>
24 </view> 12 </view>
@@ -38,24 +26,17 @@ @@ -38,24 +26,17 @@
38 <uni-drawer ref="drawer" mode="right" :mask="true" :width="280"> 26 <uni-drawer ref="drawer" mode="right" :mask="true" :width="280">
39 <view class="drawer-content" :style="{ paddingTop: menuButtonHeight + topSafeArea + 'px' }"> 27 <view class="drawer-content" :style="{ paddingTop: menuButtonHeight + topSafeArea + 'px' }">
40 <view class="user-header"> 28 <view class="user-header">
41 - <image  
42 - class="avatar"  
43 - :src="  
44 - userInfo.avatar || 29 + <image class="avatar" :src="userInfo.avatar ||
45 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png' 30 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png'
46 - "  
47 - mode="aspectFill"  
48 - ></image> 31 + " mode="aspectFill"></image>
49 <view class="user-info"> 32 <view class="user-info">
50 <text class="nickname">{{ userInfo.nickname || '未登录' }}</text> 33 <text class="nickname">{{ userInfo.nickname || '未登录' }}</text>
51 </view> 34 </view>
52 </view> 35 </view>
53 36
54 - <image  
55 - class="ad-banner" 37 + <image class="ad-banner"
56 src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/37_1773628025534.png" 38 src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/37_1773628025534.png"
57 - mode="aspectFill"  
58 - ></image> 39 + mode="aspectFill"></image>
59 40
60 <view class="menu-list"> 41 <view class="menu-list">
61 <view class="menu-item" @click="navigateTo('/pages4/pages/xunji/xunji-wode-zhuye')"> 42 <view class="menu-item" @click="navigateTo('/pages4/pages/xunji/xunji-wode-zhuye')">
@@ -96,47 +77,53 @@ @@ -96,47 +77,53 @@
96 </view> 77 </view>
97 </view> 78 </view>
98 </uni-drawer> 79 </uni-drawer>
  80 +
  81 + <TrainingFloating />
99 </view> 82 </view>
  83 +
100 </template> 84 </template>
101 85
102 <script setup> 86 <script setup>
103 - import { ref, shallowRef, computed, onMounted } from 'vue';  
104 - import { onLoad, onHide } from '@dcloudio/uni-app';  
105 - import useUserStore from '@/sheep/store/user';  
106 - import { getMenuButtonHeight, getTopSafeArea } from '@/utils/safeArea';  
107 -  
108 - // 组件导入  
109 - import xunjiRili from '@/pages/xunji/components/xunji-rili.vue';  
110 - import xunjiDongzuo from '@/pages/xunji/components/xunji-dongzuo.vue';  
111 - import xunjiMoban from '@/pages/xunji/components/xunji-moban.vue';  
112 - import xunjiShuju from '@/pages/xunji/components/xunji-shuju.vue';  
113 - import xunjiXunji from '@/pages/xunji/components/xunji-xunji.vue';  
114 - import xunjiXunlianjihua from '@/pages/xunji/components/xunji-xunlianjihua.vue';  
115 - import TrainingFloating from '@/pages/TrainingFloating.vue';  
116 -  
117 - // 状态管理  
118 - const userStore = useUserStore();  
119 -  
120 - // --- 补全缺失的响应式变量定义 ---  
121 - const currentTab = ref(0);  
122 - const currentComponent = shallowRef(xunjiXunji); // 用于 H5 端动态组件切换(如保留原功能逻辑)  
123 - const drawer = ref(null); // uni-drawer 的组件实例引用  
124 -  
125 - // 安全区域相关变量(从工具函数异步或同步获取)  
126 - const menuButtonHeight = ref(getMenuButtonHeight ? getMenuButtonHeight() : 0);  
127 - const topSafeArea = ref(getTopSafeArea ? getTopSafeArea() : 0);  
128 -  
129 - // 用户信息计算属性(防止 store 异步更新时报错)  
130 - const userInfo = computed(() => userStore.userInfo || {});  
131 -  
132 - // --- 原有页面生命周期维护 ---  
133 - onHide(() => { 87 +import { ref, shallowRef, computed, onMounted } from 'vue';
  88 +import { onLoad, onHide } from '@dcloudio/uni-app';
  89 +import useUserStore from '@/sheep/store/user';
  90 +import { getMenuButtonHeight, getTopSafeArea } from '@/utils/safeArea';
  91 +
  92 +// 组件导入
  93 +import xunjiRili from '@/pages/xunji/components/xunji-rili.vue';
  94 +import xunjiDongzuo from '@/pages/xunji/components/xunji-dongzuo.vue';
  95 +import xunjiMoban from '@/pages/xunji/components/xunji-moban.vue';
  96 +import xunjiShuju from '@/pages/xunji/components/xunji-shuju.vue';
  97 +import xunjiXunji from '@/pages/xunji/components/xunji-xunji.vue';
  98 +import xunjiXunlianjihua from '@/pages/xunji/components/xunji-xunlianjihua.vue';
  99 +
  100 +import TrainingFloating from '@/pages/TrainingFloating.vue'
  101 +import { useTrainingStore } from '@/sheep/store/trainingStore';
  102 +const trainingStore = useTrainingStore();
  103 +
  104 +// 状态管理
  105 +const userStore = useUserStore();
  106 +
  107 +// --- 补全缺失的响应式变量定义 ---
  108 +const currentTab = ref(0);
  109 +const currentComponent = shallowRef(xunjiXunji); // 用于 H5 端动态组件切换(如保留原功能逻辑)
  110 +const drawer = ref(null); // uni-drawer 的组件实例引用
  111 +
  112 +// 安全区域相关变量(从工具函数异步或同步获取)
  113 +const menuButtonHeight = ref(getMenuButtonHeight ? getMenuButtonHeight() : 0);
  114 +const topSafeArea = ref(getTopSafeArea ? getTopSafeArea() : 0);
  115 +
  116 +// 用户信息计算属性(防止 store 异步更新时报错)
  117 +const userInfo = computed(() => userStore.userInfo || {});
  118 +
  119 +// --- 原有页面生命周期维护 ---
  120 +onHide(() => {
134 if (drawer.value) { 121 if (drawer.value) {
135 drawer.value.close(); 122 drawer.value.close();
136 } 123 }
137 - }); 124 +});
138 125
139 - onLoad((options) => { 126 +onLoad((options) => {
140 if (!userStore.isLogin) { 127 if (!userStore.isLogin) {
141 uni.redirectTo({ 128 uni.redirectTo({
142 url: '/pages7/pages/index/login', 129 url: '/pages7/pages/index/login',
@@ -147,24 +134,24 @@ @@ -147,24 +134,24 @@
147 if (options && options.currentTab) { 134 if (options && options.currentTab) {
148 currentTab.value = Number(options.currentTab); 135 currentTab.value = Number(options.currentTab);
149 } 136 }
150 - }); 137 +});
151 138
152 - // --- 原有业务方法补充与修复 --- 139 +// --- 原有业务方法补充与修复 ---
153 140
154 - // 打开抽屉  
155 - const openDrawer = () => { 141 +// 打开抽屉
  142 +const openDrawer = () => {
156 if (drawer.value) { 143 if (drawer.value) {
157 drawer.value.open(); 144 drawer.value.open();
158 } 145 }
159 - }; 146 +};
160 147
161 - // 修复模板直接调用 uni.navigateTo 的多端兼容问题  
162 - const navigateTo = (url) => { 148 +// 修复模板直接调用 uni.navigateTo 的多端兼容问题
  149 +const navigateTo = (url) => {
163 uni.navigateTo({ url }); 150 uni.navigateTo({ url });
164 - }; 151 +};
165 152
166 - // 处理标签点击  
167 - const handleTabClick = (index) => { 153 +// 处理标签点击
  154 +const handleTabClick = (index) => {
168 currentTab.value = index; 155 currentTab.value = index;
169 // #ifdef H5 156 // #ifdef H5
170 switch (index) { 157 switch (index) {
@@ -190,18 +177,18 @@ @@ -190,18 +177,18 @@
190 currentComponent.value = xunjiXunlianjihua; 177 currentComponent.value = xunjiXunlianjihua;
191 } 178 }
192 // #endif 179 // #endif
193 - }; 180 +};
194 181
195 - // 空实现占位(防止模板点击报错,保持你原有的未给出的方法定义)  
196 - const goWidgetLib = () => { 182 +// 空实现占位(防止模板点击报错,保持你原有的未给出的方法定义)
  183 +const goWidgetLib = () => {
197 uni.navigateTo({ url: '/pages4/pages/xunji/widget' }); 184 uni.navigateTo({ url: '/pages4/pages/xunji/widget' });
198 - };  
199 - const goFeedback = () => {};  
200 - const goUserGroup = () => {};  
201 - const goTutorial = () => {}; 185 +};
  186 +const goFeedback = () => { };
  187 +const goUserGroup = () => { };
  188 +const goTutorial = () => { };
202 </script> 189 </script>
203 <style scoped lang="scss"> 190 <style scoped lang="scss">
204 - .home-page { 191 +.home-page {
205 width: 100%; 192 width: 100%;
206 height: 100vh; 193 height: 100vh;
207 // #ifdef H5 194 // #ifdef H5
@@ -222,9 +209,9 @@ @@ -222,9 +209,9 @@
222 padding-top: 200rpx; 209 padding-top: 200rpx;
223 // #endif 210 // #endif
224 } 211 }
225 - } 212 +}
226 213
227 - .tab-bar { 214 +.tab-bar {
228 display: flex; 215 display: flex;
229 align-items: flex-end; 216 align-items: flex-end;
230 justify-content: space-between; 217 justify-content: space-between;
@@ -238,36 +225,36 @@ @@ -238,36 +225,36 @@
238 right: 0; 225 right: 0;
239 z-index: 999; 226 z-index: 999;
240 box-sizing: border-box; 227 box-sizing: border-box;
241 - } 228 +}
242 229
243 - /* 标签项:默认样式 */  
244 - .tab-item { 230 +/* 标签项:默认样式 */
  231 +.tab-item {
245 font-size: 16px; 232 font-size: 16px;
246 color: #666; 233 color: #666;
247 padding: 5px 0; 234 padding: 5px 0;
248 padding-top: 30rpx; 235 padding-top: 30rpx;
249 - } 236 +}
250 237
251 - /* 激活态标签:下划线+深色文字 */  
252 - .tab-item.active { 238 +/* 激活态标签:下划线+深色文字 */
  239 +.tab-item.active {
253 color: #333; 240 color: #333;
254 border-bottom: 2px solid #000; 241 border-bottom: 2px solid #000;
255 font-weight: 500; 242 font-weight: 500;
256 - } 243 +}
257 244
258 - /* 菜单按钮:样式 */  
259 - .menu-btn { 245 +/* 菜单按钮:样式 */
  246 +.menu-btn {
260 font-size: 20px; 247 font-size: 20px;
261 color: #666; 248 color: #666;
262 - } 249 +}
263 250
264 - .card-subtitle { 251 +.card-subtitle {
265 font-size: 24rpx; 252 font-size: 24rpx;
266 color: #666; 253 color: #666;
267 margin-bottom: 10rpx; 254 margin-bottom: 10rpx;
268 - } 255 +}
269 256
270 - .plan-btn { 257 +.plan-btn {
271 width: 100%; 258 width: 100%;
272 height: 50rpx; 259 height: 50rpx;
273 background-color: #2eaa8a; 260 background-color: #2eaa8a;
@@ -278,95 +265,95 @@ @@ -278,95 +265,95 @@
278 line-height: 50rpx; 265 line-height: 50rpx;
279 text-align: center; 266 text-align: center;
280 margin-top: 10rpx; 267 margin-top: 10rpx;
281 - } 268 +}
282 269
283 - .card-value { 270 +.card-value {
284 font-size: 36rpx; 271 font-size: 36rpx;
285 color: #333; 272 color: #333;
286 margin-bottom: 10rpx; 273 margin-bottom: 10rpx;
287 - } 274 +}
288 275
289 - .card-desc { 276 +.card-desc {
290 font-size: 24rpx; 277 font-size: 24rpx;
291 color: #666; 278 color: #666;
292 - } 279 +}
293 280
294 - /* 抽屉样式 */  
295 - .drawer-content { 281 +/* 抽屉样式 */
  282 +.drawer-content {
296 background-color: #ffffff; 283 background-color: #ffffff;
297 height: 100%; 284 height: 100%;
298 padding: 20rpx; 285 padding: 20rpx;
299 box-sizing: border-box; 286 box-sizing: border-box;
300 - } 287 +}
301 288
302 - /* 用户头像和信息区域 */  
303 - .user-header { 289 +/* 用户头像和信息区域 */
  290 +.user-header {
304 display: flex; 291 display: flex;
305 align-items: center; 292 align-items: center;
306 padding: 20rpx 0; 293 padding: 20rpx 0;
307 border-bottom: 1px solid #f0f0f0; 294 border-bottom: 1px solid #f0f0f0;
308 - } 295 +}
309 296
310 - .user-header .avatar { 297 +.user-header .avatar {
311 width: 80rpx; 298 width: 80rpx;
312 height: 80rpx; 299 height: 80rpx;
313 border-radius: 50%; 300 border-radius: 50%;
314 margin-right: 20rpx; 301 margin-right: 20rpx;
315 margin-top: 0; 302 margin-top: 0;
316 justify-content: flex-start; 303 justify-content: flex-start;
317 - } 304 +}
318 305
319 - .user-info { 306 +.user-info {
320 display: flex; 307 display: flex;
321 flex-direction: column; 308 flex-direction: column;
322 - } 309 +}
323 310
324 - .nickname { 311 +.nickname {
325 font-size: 28rpx; 312 font-size: 28rpx;
326 font-weight: 500; 313 font-weight: 500;
327 color: #333333; 314 color: #333333;
328 margin-bottom: 8rpx; 315 margin-bottom: 8rpx;
329 - } 316 +}
330 317
331 - .user-id { 318 +.user-id {
332 font-size: 24rpx; 319 font-size: 24rpx;
333 color: #999999; 320 color: #999999;
334 - } 321 +}
335 322
336 - /* 广告横幅 */  
337 - .ad-banner { 323 +/* 广告横幅 */
  324 +.ad-banner {
338 width: 100%; 325 width: 100%;
339 height: 200rpx; 326 height: 200rpx;
340 margin: 20rpx 0; 327 margin: 20rpx 0;
341 border-radius: 12rpx; 328 border-radius: 12rpx;
342 - } 329 +}
343 330
344 - /* 菜单列表 */  
345 - .menu-list { 331 +/* 菜单列表 */
  332 +.menu-list {
346 margin-top: 20rpx; 333 margin-top: 20rpx;
347 - } 334 +}
348 335
349 - .menu-item { 336 +.menu-item {
350 display: flex; 337 display: flex;
351 align-items: center; 338 align-items: center;
352 justify-content: space-between; 339 justify-content: space-between;
353 padding: 30rpx 0; 340 padding: 30rpx 0;
354 border-bottom: 1px solid #f0f0f0; 341 border-bottom: 1px solid #f0f0f0;
355 - } 342 +}
356 343
357 - .menu-item:last-child { 344 +.menu-item:last-child {
358 border-bottom: none; 345 border-bottom: none;
359 - } 346 +}
360 347
361 - .menu-text { 348 +.menu-text {
362 font-size: 28rpx; 349 font-size: 28rpx;
363 color: #333333; 350 color: #333333;
364 margin-left: 20rpx; 351 margin-left: 20rpx;
365 flex: 1; 352 flex: 1;
366 - } 353 +}
367 354
368 - /* 覆盖uni-icons默认颜色 */  
369 - :deep(.uni-icons) { 355 +/* 覆盖uni-icons默认颜色 */
  356 +:deep(.uni-icons) {
370 color: #666666; 357 color: #666666;
371 - } 358 +}
372 </style> 359 </style>
@@ -145,7 +145,7 @@ @@ -145,7 +145,7 @@
145 {{ detail.reps }}次 145 {{ detail.reps }}次
146 </view> 146 </view>
147 <view class="detail-value" v-if="unit.exercises[0].exerciseType === 3"> 147 <view class="detail-value" v-if="unit.exercises[0].exerciseType === 3">
148 - {{ detail.duration }}次 148 + {{ formatSeconds(detail.duration) }}
149 </view> 149 </view>
150 <view class="detail-value" v-if="unit.exercises[0].exerciseType === 6"> 150 <view class="detail-value" v-if="unit.exercises[0].exerciseType === 6">
151 {{ detail.duration }}组 x {{ formatSeconds(detail.duration) }} 151 {{ detail.duration }}组 x {{ formatSeconds(detail.duration) }}
@@ -192,7 +192,7 @@ @@ -192,7 +192,7 @@
192 {{ ex.sets[idx - 1].reps }}次 192 {{ ex.sets[idx - 1].reps }}次
193 </text> 193 </text>
194 <text v-if="ex.exerciseType === 3"> 194 <text v-if="ex.exerciseType === 3">
195 - {{ ex.sets[idx - 1].duration }}次 195 + {{ formatSeconds(ex.sets[idx - 1].duration) }}
196 </text> 196 </text>
197 <text v-if="ex.exerciseType === 6"> 197 <text v-if="ex.exerciseType === 6">
198 {{ ex.sets[idx - 1].duration }}组 x {{ formatSeconds(ex.sets[idx - 1].duration) }} 198 {{ ex.sets[idx - 1].duration }}组 x {{ formatSeconds(ex.sets[idx - 1].duration) }}
@@ -425,7 +425,7 @@ const startDaiTemplateTraining = () => { @@ -425,7 +425,7 @@ const startDaiTemplateTraining = () => {
425 uni.navigateTo({ 425 uni.navigateTo({
426 url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${TemplateDetail.value.id}&type=3&dailyTemplateId=${TemplateDetail.value.dailyTemplateId}&isTraining=true`, 426 url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${TemplateDetail.value.id}&type=3&dailyTemplateId=${TemplateDetail.value.dailyTemplateId}&isTraining=true`,
427 }); 427 });
428 - console.log('开始进入编辑模板页面,模板ID:', TemplateDetail.value.id, '每日模板ID:', TemplateDetail.value.dailyTemplateId); 428 + console.log('开始进入每日moan训练页面,模板ID:', TemplateDetail.value.id, '每日模板ID:', TemplateDetail.value.dailyTemplateId);
429 429
430 console.log('打印传递给动作训练页面的模板id', TemplateDetail.value.id); 430 console.log('打印传递给动作训练页面的模板id', TemplateDetail.value.id);
431 431
@@ -66,11 +66,9 @@ @@ -66,11 +66,9 @@
66 </scroll-view> 66 </scroll-view>
67 67
68 68
69 - <view class="v-else"> 69 + <view v-else>
70 70
71 <!-- 文件夹 --> 71 <!-- 文件夹 -->
72 -  
73 -  
74 <!-- 个人模板 --> 72 <!-- 个人模板 -->
75 <scroll-view scroll-y="true" class="template-list personal-template-list"> 73 <scroll-view scroll-y="true" class="template-list personal-template-list">
76 <view class="grid-container"> 74 <view class="grid-container">
@@ -160,7 +158,7 @@ const getPartCategories = async () => { @@ -160,7 +158,7 @@ const getPartCategories = async () => {
160 const switchTab = async (tab) => { 158 const switchTab = async (tab) => {
161 activeTab.value = tab; 159 activeTab.value = tab;
162 if (tab === 'official') { 160 if (tab === 'official') {
163 - TemplatesList(); 161 + getTemplatesList();
164 } else { 162 } else {
165 doFilter(); 163 doFilter();
166 } 164 }
@@ -203,7 +201,7 @@ const doFilter = async () => { @@ -203,7 +201,7 @@ const doFilter = async () => {
203 isFiltering.value = false; 201 isFiltering.value = false;
204 // 官方回到模板大类 202 // 官方回到模板大类
205 if (activeTab.value === 'official') { 203 if (activeTab.value === 'official') {
206 - TemplatesList(); 204 + getTemplatesList();
207 } else { 205 } else {
208 getMyTemplates(); 206 getMyTemplates();
209 } 207 }
@@ -221,12 +219,14 @@ const doFilter = async () => { @@ -221,12 +219,14 @@ const doFilter = async () => {
221 res = await TemplatesApi.queryCustTemplate(0, muscleId, sceneId); 219 res = await TemplatesApi.queryCustTemplate(0, muscleId, sceneId);
222 } 220 }
223 templateList.value = res.data || []; 221 templateList.value = res.data || [];
  222 + console.log('templateList.value', templateList.value);
  223 +
224 } catch (err) { 224 } catch (err) {
225 console.log('筛选失败', err); 225 console.log('筛选失败', err);
226 } 226 }
227 }; 227 };
228 228
229 -const TemplatesList = async () => { 229 +const getTemplatesList = async () => {
230 try { 230 try {
231 const response = await TemplatesApi.getTemplates(); 231 const response = await TemplatesApi.getTemplates();
232 templateList.value = response.data; 232 templateList.value = response.data;
@@ -238,44 +238,13 @@ const TemplatesList = async () => { @@ -238,44 +238,13 @@ const TemplatesList = async () => {
238 238
239 // 获取我的模板(复用同一个 templateList) 239 // 获取我的模板(复用同一个 templateList)
240 const getMyTemplates = async () => { 240 const getMyTemplates = async () => {
241 - // try {  
242 - // // 调用你自己的“我的模板”接口  
243 - // const res = await TemplatesApi.individualTemplates();  
244 - // console.log('打印个人模板res=', res);  
245 - // templateList.value = res.data;  
246 - // } catch (err) {  
247 - // console.log('我的模板加载失败', err);  
248 - // }  
249 - // 获取我的模板(现在用模拟数据,不影响你原来代码)  
250 - templateList.value = [  
251 - {  
252 - id: 1001,  
253 - name: "胸部基础训练",  
254 - urlCover: "https://picsum.photos/400/300?1",  
255 - exerciseCount: 6,  
256 - totalSets: 18,  
257 - totalWeight: 50,  
258 - primaryMuscleNames: ["胸肌", "三头肌"]  
259 - },  
260 - {  
261 - id: 1002,  
262 - name: "腿部力量训练",  
263 - urlCover: "https://picsum.photos/400/300?2",  
264 - exerciseCount: 5,  
265 - totalSets: 15,  
266 - totalWeight: 120,  
267 - primaryMuscleNames: ["股四头", "臀部"]  
268 - },  
269 - {  
270 - id: 1003,  
271 - name: "背部塑形计划",  
272 - urlCover: "https://picsum.photos/400/300?3",  
273 - exerciseCount: 7,  
274 - totalSets: 21,  
275 - totalWeight: 70,  
276 - primaryMuscleNames: ["背阔肌"] 241 + try {
  242 + const res = await TemplatesApi.individualTemplates();
  243 + console.log('打印个人模板res=', res);
  244 + templateList.value = res.data;
  245 + } catch (err) {
  246 + console.log('我的模板加载失败', err);
277 } 247 }
278 - ];  
279 }; 248 };
280 249
281 // 返回上一页 250 // 返回上一页
@@ -294,7 +263,7 @@ const goToPersonalDetail = (item) => { @@ -294,7 +263,7 @@ const goToPersonalDetail = (item) => {
294 // 跳转到模板详情页 263 // 跳转到模板详情页
295 const goToTemplate = (item) => { 264 const goToTemplate = (item) => {
296 uni.navigateTo({ 265 uni.navigateTo({
297 - url: `/pages4/pages/xunji/xunji-moban?id=${item.id}` 266 + url: `/pages4/pages/xunji/xunji-moban?id=${item.id}&isBigTemOffice=true`
298 }); 267 });
299 }; 268 };
300 269
@@ -305,7 +274,7 @@ const menuButtonInfo = ref({ @@ -305,7 +274,7 @@ const menuButtonInfo = ref({
305 }); 274 });
306 275
307 onMounted(() => { 276 onMounted(() => {
308 - TemplatesList(); 277 + getTemplatesList();
309 getPartCategories(); 278 getPartCategories();
310 279
311 // 获取小程序胶囊位置信息,实现导航栏与胶囊完美对齐 280 // 获取小程序胶囊位置信息,实现导航栏与胶囊完美对齐
1 -<template>  
2 - <view class="calendar-page">  
3 - <!-- 顶部标签栏 -->  
4 - <view class="top-tabs">  
5 - <text  
6 - v-for="(tab, index) in topTabs"  
7 - :key="index"  
8 - class="tab-item"  
9 - :class="{ active: activeTab === tab }"  
10 - @click="switchTab(tab)"  
11 - >  
12 - {{ tab }}  
13 - </text>  
14 - </view>  
15 -  
16 - <!-- 年月选择器 -->  
17 - <view class="year-month-picker">  
18 - <button class="picker-btn" @click="showYearMonthPicker"> {{ currentMonth }} ▼ </button>  
19 - </view>  
20 -  
21 - <!-- 功能按钮 -->  
22 - <view class="action-buttons">  
23 - <button class="action-btn">分享</button>  
24 - <button class="action-btn">周/月报</button>  
25 - <button class="action-btn">说明</button>  
26 - </view>  
27 -  
28 - <!-- 日历网格 -->  
29 - <view class="calendar-grid">  
30 - <!-- 星期标题 -->  
31 - <view class="week-header">  
32 - <text v-for="(day, index) in weekDays" :key="index" class="week-day">{{ day }}</text>  
33 - </view>  
34 -  
35 - <!-- 日期网格 -->  
36 - <view class="date-grid">  
37 - <view  
38 - v-for="(date, index) in dates"  
39 - :key="index"  
40 - class="date-cell"  
41 - :class="{ today: date.today }"  
42 - @click="selectDate"  
43 - >  
44 - <text class="date-number">{{ date.day }}</text>  
45 - <text v-if="date.today" class="today-text">今</text>  
46 - </view>  
47 - </view>  
48 - </view>  
49 -  
50 - <!-- 底部 TabBar -->  
51 - <view class="bottom-tabbar">  
52 - <navigator url="/pages/xunji/xunji" class="tab-item">  
53 - <image  
54 - class="tab-icon"  
55 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png"  
56 - mode="aspectFill"  
57 - ></image>  
58 - <text class="tab-text">首页</text>  
59 - </navigator>  
60 - <navigator url="/pages/course/course" class="tab-item">  
61 - <image  
62 - class="tab-icon"  
63 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png"  
64 - mode="aspectFill"  
65 - ></image>  
66 - <text class="tab-text">课程</text>  
67 - </navigator>  
68 - <navigator url="/pages/enter-gym/enter-gym" class="tab-item active">  
69 - <image  
70 - class="tab-icon"  
71 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png"  
72 - mode="aspectFill"  
73 - ></image>  
74 - <text class="tab-text">进馆</text>  
75 - </navigator>  
76 - <navigator url="/pages/training-log/training-log" class="tab-item">  
77 - <image  
78 - class="tab-icon"  
79 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png"  
80 - mode="aspectFill"  
81 - ></image>  
82 - <text class="tab-text">训记</text>  
83 - </navigator>  
84 - <navigator url="/pages/my/my" class="tab-item">  
85 - <image  
86 - class="tab-icon"  
87 - src="https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png"  
88 - mode="aspectFill"  
89 - ></image>  
90 - <text class="tab-text">我的</text>  
91 - </navigator>  
92 - </view>  
93 - </view>  
94 -</template>  
95 -  
96 -<script setup>  
97 - import { ref } from 'vue';  
98 -  
99 - // 响应式数据  
100 - const activeTab = ref('日历');  
101 - const currentMonth = ref('2025.10月');  
102 - const weekDays = ref(['一', '二', '三', '四', '五', '六', '日']);  
103 - const topTabs = ref(['概要', '计划', '日历', '动作', '模板', '数据']);  
104 -  
105 - const dates = ref([  
106 - { day: 29, today: false },  
107 - { day: 30, today: false },  
108 - { day: 1, today: false },  
109 - { day: 2, today: false },  
110 - { day: 3, today: false },  
111 - { day: 4, today: false },  
112 - { day: 5, today: false },  
113 - { day: 6, today: false },  
114 - { day: 7, today: false },  
115 - { day: 8, today: false },  
116 - { day: 9, today: false },  
117 - { day: 10, today: false },  
118 - { day: 11, today: false },  
119 - { day: 12, today: false },  
120 - { day: 13, today: false },  
121 - { day: 14, today: false },  
122 - { day: 15, today: false },  
123 - { day: 16, today: true },  
124 - { day: 17, today: false },  
125 - { day: 18, today: false },  
126 - { day: 19, today: false },  
127 - { day: 20, today: false },  
128 - { day: 21, today: false },  
129 - { day: 22, today: false },  
130 - { day: 23, today: false },  
131 - { day: 24, today: false },  
132 - { day: 25, today: false },  
133 - { day: 26, today: false },  
134 - { day: 27, today: false },  
135 - { day: 28, today: false },  
136 - { day: 29, today: false },  
137 - { day: 30, today: false },  
138 - { day: 31, today: false },  
139 - { day: 1, today: false },  
140 - { day: 2, today: false },  
141 - ]);  
142 -  
143 - // 方法  
144 - const switchTab = (tab) => {  
145 - activeTab.value = tab;  
146 - };  
147 -  
148 - const showYearMonthPicker = () => {  
149 - uni.showActionSheet({  
150 - itemList: ['2025.10月', '2025.11月', '2025.12月'],  
151 - success: (res) => {  
152 - if (res.tapIndex === 0) {  
153 - currentMonth.value = '2025.10月';  
154 - } else if (res.tapIndex === 1) {  
155 - currentMonth.value = '2025.11月';  
156 - } else {  
157 - currentMonth.value = '2025.12月';  
158 - }  
159 - },  
160 - });  
161 - };  
162 -  
163 - const selectDate = () => {  
164 - uni.navigateTo({  
165 - url: '/pages4/pages/xunji/xunji-rili-tianjia',  
166 - });  
167 - };  
168 -</script>  
169 -  
170 -<style scoped lang="scss">  
171 - .calendar-page {  
172 - background-color: white;  
173 - min-height: 100vh;  
174 - }  
175 -  
176 - .top-tabs {  
177 - display: flex;  
178 - justify-content: space-around;  
179 - padding: 20rpx 0;  
180 - border-bottom: 1px solid #eee;  
181 - font-size: 28rpx;  
182 - color: #333;  
183 - }  
184 -  
185 - .tab-item {  
186 - padding: 10rpx 0;  
187 - border-bottom: 2rpx solid transparent;  
188 - }  
189 -  
190 - .tab-item.active {  
191 - color: #ffd700;  
192 - border-bottom-color: #ffd700;  
193 - }  
194 -  
195 - .year-month-picker {  
196 - padding: 20rpx;  
197 - display: flex;  
198 - justify-content: flex-start;  
199 - }  
200 -  
201 - .picker-btn {  
202 - background-color: white;  
203 - border: 1px solid #ddd;  
204 - border-radius: 20rpx;  
205 - padding: 10rpx 20rpx;  
206 - font-size: 26rpx;  
207 - color: #333;  
208 - }  
209 -  
210 - .action-buttons {  
211 - display: flex;  
212 - justify-content: flex-end;  
213 - padding: 20rpx;  
214 - gap: 20rpx;  
215 - }  
216 -  
217 - .action-btn {  
218 - background-color: white;  
219 - border: 1px solid #ddd;  
220 - border-radius: 20rpx;  
221 - padding: 10rpx 20rpx;  
222 - font-size: 24rpx;  
223 - color: #333;  
224 - }  
225 -  
226 - .calendar-grid {  
227 - margin: 20rpx;  
228 - }  
229 -  
230 - .week-header {  
231 - display: flex;  
232 - justify-content: space-between;  
233 - padding: 20rpx 0;  
234 - font-size: 26rpx;  
235 - color: #999;  
236 - }  
237 -  
238 - .week-day {  
239 - width: 40rpx;  
240 - text-align: center;  
241 - }  
242 -  
243 - .date-grid {  
244 - display: grid;  
245 - grid-template-columns: repeat(7, 1fr);  
246 - gap: 10rpx;  
247 - }  
248 -  
249 - .date-cell {  
250 - display: flex;  
251 - flex-direction: column;  
252 - align-items: center;  
253 - justify-content: center;  
254 - height: 80rpx;  
255 - border: 1px solid #eee;  
256 - border-radius: 8rpx;  
257 - font-size: 26rpx;  
258 - color: #333;  
259 - position: relative;  
260 - }  
261 -  
262 - .date-cell.today {  
263 - background-color: #f5f5f5;  
264 - color: #ffd700;  
265 - }  
266 -  
267 - .today-text {  
268 - position: absolute;  
269 - bottom: 10rpx;  
270 - font-size: 20rpx;  
271 - color: #ffd700;  
272 - }  
273 -  
274 - .bottom-tabbar {  
275 - display: flex;  
276 - justify-content: space-around;  
277 - padding: 20rpx;  
278 - background-color: white;  
279 - border-top: 1px solid #eee;  
280 - position: fixed;  
281 - bottom: 0;  
282 - left: 0;  
283 - right: 0;  
284 - z-index: 999;  
285 - }  
286 -  
287 - .tab-item {  
288 - display: flex;  
289 - flex-direction: column;  
290 - align-items: center;  
291 - font-size: 24rpx;  
292 - color: #333;  
293 - }  
294 -  
295 - .tab-item.active {  
296 - color: #ffd700;  
297 - }  
298 -  
299 - .tab-icon {  
300 - width: 40rpx;  
301 - height: 40rpx;  
302 - margin-bottom: 6rpx;  
303 - }  
304 -</style>  
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 <text>结束这个计划</text> 57 <text>结束这个计划</text>
58 </view> --> 58 </view> -->
59 <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false" 59 <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false"
60 - :plan-title="'选择课程立即开始训练'" :plan-id="selectPlanId" /> 60 + :plan-title="'选择课程立即开始训练'" :plan-id="selectPlanId" :is-my-plan="true" />
61 61
62 </view> 62 </view>
63 63
@@ -176,7 +176,7 @@ @@ -176,7 +176,7 @@
176 </view> 176 </view>
177 </template> 177 </template>
178 </view> 178 </view>
179 - 179 + <!-- 加入计划的弹窗 -->
180 <WeekSelectPopup v-model:visible="showWeekPopup" :max-count="plandetail.frequencyPerWeek" :plan-detail="plandetail" 180 <WeekSelectPopup v-model:visible="showWeekPopup" :max-count="plandetail.frequencyPerWeek" :plan-detail="plandetail"
181 @success="loadDetail" :is-update="isUpdate" /> 181 @success="loadDetail" :is-update="isUpdate" />
182 182
@@ -184,9 +184,12 @@ @@ -184,9 +184,12 @@
184 <AddToCalendarPopup ref="calendarPopupRef" :plan-id="plandetail.id" :template-id="currentTemplateId" 184 <AddToCalendarPopup ref="calendarPopupRef" :plan-id="plandetail.id" :template-id="currentTemplateId"
185 @success="handleCalendarSuccess" /> 185 @success="handleCalendarSuccess" />
186 186
187 - <!-- 去训练弹窗 --> 187 + <!-- 去训练弹窗,显示当前计划的所有模板,可以直接跳转到训练页面 -->
  188 + <template v-if="plandetail?.id">
188 <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false" 189 <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false"
189 - :plan-title="'选择课程立即开始训练'" :plan-id="plandetail.id" /> 190 + :plan-title="'选择课程立即开始训练'" :plan-id="plandetail.id" :is-my-plan="false"/>
  191 + </template>
  192 +
190 </view> 193 </view>
191 </template> 194 </template>
192 195
@@ -2,13 +2,73 @@ import request from '@/sheep/request'; @@ -2,13 +2,73 @@ import request from '@/sheep/request';
2 2
3 const dailytemplateApi = { 3 const dailytemplateApi = {
4 //获取每日模板 4 //获取每日模板
5 - getdailytemplate:(date)=>{ 5 + getdailytemplate: (date) => {
6 return request({ 6 return request({
7 - url:'/app/daily/template/DailyTemplates',  
8 - method:'GET',  
9 - params:{date} 7 + url: '/app/daily/template/DailyTemplates',
  8 + method: 'GET',
  9 + params: { date },
10 }); 10 });
11 - } 11 + },
  12 + // 删除当日模板
  13 + deleteDailyTemplate: (id) => {
  14 + return request({
  15 + url: `/app/daily/template/${id}`,
  16 + method: 'DELETE',
  17 + });
  18 + },
  19 + // 更新或者添加日期备注颜色
  20 + AddOrUpdateDateNote: (data) => {
  21 + return request({
  22 + url: `/app/daily/template/note`,
  23 + method: 'POST',
  24 + data,
  25 + });
  26 + },
  27 + // 删除日期备注
  28 + deleteDateNote: (date) => {
  29 + return request({
  30 + url: `/app/daily/template/note`,
  31 + method: 'DELETE',
  32 + params: { date },
  33 + });
  34 + },
  35 + // 获得日期备注详情
  36 + getNoteDetail: (id) => {
  37 + return request({
  38 + url: `/app/daily/template/note/${id}`,
  39 + method: 'GET',
  40 + });
  41 + },
  42 + // 更新日期备注详情
  43 + updateNote: (data) => {
  44 + return request({
  45 + url: `/app/daily/template/note`,
  46 + method: 'PUT',
  47 + data,
  48 + });
  49 + },
  50 + //
  51 + backgroundColor: (data) => {
  52 + return request({
  53 + url: '/app/daily/template/backgroundColor',
  54 + method: 'PUT',
  55 + data,
  56 + });
  57 + },
  58 + completeDailyTemplate: (id) => {
  59 + return request({
  60 + url: `/app/daily/template/complete/${id}`,
  61 + method: 'POST',
  62 + });
  63 + },
  64 + // 更新每日模板
  65 + updateDailyTemplate: (data) => {
  66 + return request({
  67 + url: `/app/daily/template`,
  68 + method: 'PUT',
  69 + data,
  70 + });
  71 + },
12 }; 72 };
13 73
14 export default dailytemplateApi; 74 export default dailytemplateApi;
@@ -8,6 +8,7 @@ const TemplatesApi = { @@ -8,6 +8,7 @@ const TemplatesApi = {
8 method: 'GET', 8 method: 'GET',
9 }); 9 });
10 }, 10 },
  11 + // 获得模板大类列表(官方)
11 getTemplates: () => { 12 getTemplates: () => {
12 return request({ 13 return request({
13 url: '/app/templates/selectGroup', 14 url: '/app/templates/selectGroup',
@@ -15,27 +16,60 @@ const TemplatesApi = { @@ -15,27 +16,60 @@ const TemplatesApi = {
15 }); 16 });
16 }, 17 },
17 18
18 - //获得模板详情  
19 - getTemplateDetail: (id)=>{ 19 + // 获得个人模板列表
  20 + individualTemplates: () => {
20 return request({ 21 return request({
21 - url:'/app/templates/getTemplateDetail',  
22 - method:'GET',  
23 - params:{id} 22 + url: '/app/templates/getCustTemplate',
  23 + method: 'GET',
  24 + });
  25 + },
  26 +
  27 + // 条件查询个人列表
  28 + queryCustTemplate: (groupId, muscleId, sceneId) => {
  29 + return request({
  30 + url: '/app/templates/queryCustTemplate',
  31 + method: 'GET',
  32 + params: {
  33 + groupId,
  34 + muscleId,
  35 + sceneId,
  36 + },
  37 + });
  38 + },
  39 +
  40 + // 条件查询官方模板
  41 + getOfficeTemplates: (muscleId, sceneId) => {
  42 + return request({
  43 + url: '/app/templates/queryTemplatesByCondition',
  44 + method: 'GET',
  45 + params: {
  46 + muscleId,
  47 + sceneId,
  48 + },
  49 + });
  50 + },
  51 +
  52 + //获得官方模板详情
  53 + getTemplateDetail: (id) => {
  54 + return request({
  55 + url: '/app/templates/getTemplateDetail',
  56 + method: 'GET',
  57 + params: { id },
24 }); 58 });
25 }, 59 },
26 // 获得所有的部位 60 // 获得所有的部位
27 - getPartAllCategories: ()=> { 61 + getPartAllCategories: () => {
28 return request({ 62 return request({
29 - url:'/app/motion/categories/sub/get',  
30 - method:'GET',  
31 - }) 63 + url: '/app/motion/categories/sub/get',
  64 + method: 'GET',
  65 + });
32 }, 66 },
33 // 根据场景和部位筛选模板 67 // 根据场景和部位筛选模板
34 queryTemplatesByCondition: (params) => { 68 queryTemplatesByCondition: (params) => {
35 return request({ 69 return request({
36 url: '/app/templates/queryTemplatesByCondition', 70 url: '/app/templates/queryTemplatesByCondition',
37 method: 'GET', 71 method: 'GET',
38 - params: params // 72 + params: params, //
39 }); 73 });
40 }, 74 },
41 //获得细分锻炼部位列表 75 //获得细分锻炼部位列表
@@ -44,7 +78,117 @@ const TemplatesApi = { @@ -44,7 +78,117 @@ const TemplatesApi = {
44 url: '/app/motion/exercises/sub-categories', 78 url: '/app/motion/exercises/sub-categories',
45 method: 'GET', 79 method: 'GET',
46 }); 80 });
47 - } 81 + },
  82 + // 创建个人模板
  83 + createCustTemplate: (data) => {
  84 + return request({
  85 + url: '/app/templates/createCustTemplate',
  86 + method: 'POST',
  87 + data,
  88 + });
  89 + },
  90 +
  91 + // 更新个人模板
  92 + updateCustTemplate: (data) => {
  93 + return request({
  94 + url: '/app/templates/updateCustTemplate',
  95 + method: 'PUT',
  96 + data,
  97 + });
  98 + },
  99 + // 删除个人模板
  100 + deleteCustTemplate: (templateId) => {
  101 + return request({
  102 + url: '/app/templates/deleteCustTemplate',
  103 + method: 'DELETE',
  104 + params: { templateId: templateId },
  105 + });
  106 + },
  107 + // 获得个人模板详情
  108 + getCustTemplateDetail: (id) => {
  109 + return request({
  110 + url: '/app/templates/getCustTemplateDetail',
  111 + method: 'GET',
  112 + params: { id: id },
  113 + });
  114 + },
  115 +
  116 + // 创建模板文件夹
  117 + createGroup: (name) => {
  118 + return request({
  119 + url: '/app/templates/createGroup',
  120 + method: 'POST',
  121 + data: {
  122 + name: name,
  123 + },
  124 + });
  125 + },
  126 + // 获得模板文件夹列表
  127 + getTemplateFileList: () => {
  128 + return request({
  129 + url: '/app/templates/getCustTemplateGroup',
  130 + method: 'GET',
  131 + });
  132 + },
  133 + // 删除模板文件夹
  134 + deleteCustTemplateGroup: (groupId) => {
  135 + return request({
  136 + url: '/app/templates/deleteCustTemplateGroup',
  137 + method: 'DELETE',
  138 + params: { groupId: groupId },
  139 + });
  140 + },
  141 + // 更新模板文件夹
  142 + updateTemplateGroup: (groupId, templateId) => {
  143 + return request({
  144 + url: '/app/templates/updateTemplateGroup',
  145 + method: 'PUT',
  146 + data: {
  147 + groupId,
  148 + templateId,
  149 + },
  150 + });
  151 + },
  152 + // 更新个人模板文件夹名称
  153 + updateTemplateGroupName: (data) => {
  154 + return request({
  155 + url: '/app/templates/updateTemplateGroup/name',
  156 + method: 'PUT',
  157 + data,
  158 + });
  159 + },
  160 + // 获得个人模板文件夹名称
  161 + getTemplateGroupNname: (groupId) => {
  162 + return request({
  163 + url: '/app/templates/getTemplateGroup/name',
  164 + method: 'GET',
  165 + params: {
  166 + groupId: groupId,
  167 + },
  168 + });
  169 + },
  170 + // 设置日历颜色
  171 + putCalendarColor: (templateId, backgroundColor) => {
  172 + return request({
  173 + url: '/app/daily/template/template/calendarColor',
  174 + method: 'PUT',
  175 + data: {
  176 + templateId,
  177 + backgroundColor,
  178 + },
  179 + });
  180 + },
  181 +
  182 + // 是否添加计划
  183 + isAddPlan: (id) => {
  184 + return request({
  185 + url: '/app/plan/isAdd',
  186 + method: 'GET',
  187 + params: {
  188 + id,
  189 + },
  190 + });
  191 + },
48 }; 192 };
49 193
50 export default TemplatesApi; 194 export default TemplatesApi;
@@ -2,7 +2,7 @@ import request from '@/sheep/request'; @@ -2,7 +2,7 @@ import request from '@/sheep/request';
2 2
3 const TrainingApi = { 3 const TrainingApi = {
4 //创建训练历史 4 //创建训练历史
5 - creatTrainHistory: (data) => { 5 + createTrainHistory: (data) => {
6 return request({ 6 return request({
7 url: '/app/training/history', 7 url: '/app/training/history',
8 method: 'POST', 8 method: 'POST',
@@ -165,7 +165,7 @@ const ExercisesApi = { @@ -165,7 +165,7 @@ const ExercisesApi = {
165 return request({ 165 return request({
166 url: `/app/motion/exercises/notes/add`, 166 url: `/app/motion/exercises/notes/add`,
167 method: 'POST', 167 method: 'POST',
168 - data 168 + data,
169 }); 169 });
170 }, 170 },
171 }; 171 };
1 import request from '@/sheep/request'; 1 import request from '@/sheep/request';
  2 +import { method } from 'lodash';
2 3
3 // 查询计划API 4 // 查询计划API
4 const QueryPlanApi = { 5 const QueryPlanApi = {
@@ -7,7 +8,7 @@ const QueryPlanApi = { @@ -7,7 +8,7 @@ const QueryPlanApi = {
7 url: '/app/plan/queryPlan', 8 url: '/app/plan/queryPlan',
8 method: 'GET', // 匹配文档的GET请求方式 9 method: 'GET', // 匹配文档的GET请求方式
9 params: { 10 params: {
10 - categoryId 11 + categoryId,
11 }, // 传递query参数(可选) 12 }, // 传递query参数(可选)
12 }); 13 });
13 }, 14 },
@@ -23,10 +24,33 @@ const QueryPlanApi = { @@ -23,10 +24,33 @@ const QueryPlanApi = {
23 url: '/app/plan/getPlanDetail', 24 url: '/app/plan/getPlanDetail',
24 method: 'GET', 25 method: 'GET',
25 params: { 26 params: {
26 - id 27 + id,
27 }, 28 },
28 }); 29 });
29 }, 30 },
  31 +
  32 + // 获取我的计划详情(含课程)
  33 + getMyPlanDetail: (id) => {
  34 + return request({
  35 + url: '/app/plan/getmyPlanDetail',
  36 + method: 'GET',
  37 + params: {
  38 + id,
  39 + },
  40 + });
  41 + },
  42 +
  43 + // 根据每日模板ID获取模板详情
  44 + getDailyTemplateDetail: (id) => {
  45 + return request({
  46 + url: '/app/plan/getDailyTemplateDetail',
  47 + method: 'GET',
  48 + params: {
  49 + id,
  50 + },
  51 + });
  52 + },
  53 +
30 // 根据筛选条件获得当前计划分类下的计划列表 54 // 根据筛选条件获得当前计划分类下的计划列表
31 getFilteredPlanList: (params) => { 55 getFilteredPlanList: (params) => {
32 return request({ 56 return request({
@@ -49,39 +73,119 @@ const QueryPlanApi = { @@ -49,39 +73,119 @@ const QueryPlanApi = {
49 return request({ 73 return request({
50 url: '/app/plan/arrangePlan', 74 url: '/app/plan/arrangePlan',
51 method: 'POST', 75 method: 'POST',
52 - data 76 + data,
  77 + });
  78 + },
  79 + // 重新开始计划
  80 + reStartPlan: (data) => {
  81 + return request({
  82 + url: '/app/plan/rearrangePlan',
  83 + method: 'POST',
  84 + data,
53 }); 85 });
54 }, 86 },
  87 + // 获得本周计划
  88 + getWeekPlan: (id) => {
  89 + return request({
  90 + url: `/app/plan/getWeekPlan`,
  91 + method: 'GET',
  92 + params: {
  93 + id,
  94 + },
  95 + });
  96 + },
  97 +
55 // 添加计划 98 // 添加计划
56 addPlan: (id) => { 99 addPlan: (id) => {
57 return request({ 100 return request({
58 - url:`/app/plan/addPlan`, 101 + url: `/app/plan/addPlan`,
59 method: 'GET', 102 method: 'GET',
60 params: { 103 params: {
61 - id  
62 - }  
63 - }) 104 + id,
  105 + },
  106 + });
64 }, 107 },
65 // 是否添加计划 108 // 是否添加计划
66 isAddPlan: (id) => { 109 isAddPlan: (id) => {
67 return request({ 110 return request({
68 - url:`/app/plan/isAdd`, 111 + url: `/app/plan/isAdd`,
69 method: 'GET', 112 method: 'GET',
70 params: { 113 params: {
71 - id  
72 - }  
73 - }) 114 + id,
  115 + },
  116 + });
74 }, 117 },
75 // 添加计划到日历 118 // 添加计划到日历
76 addPlanToCalendar: (data) => { 119 addPlanToCalendar: (data) => {
77 - return request ({ 120 + return request({
78 url: `/app/daily/template`, 121 url: `/app/daily/template`,
79 method: 'POST', 122 method: 'POST',
80 - data  
81 - })  
82 - } 123 + data,
  124 + });
  125 + },
  126 + // 获得我的计划列表
  127 + getMyPlan: () => {
  128 + return request({
  129 + url: '/app/plan/getMyPlan',
  130 + method: 'GET',
  131 + });
  132 + },
  133 + // 结束这个计划
  134 + deletePlan: (id) => {
  135 + return request({
  136 + url: '/app/plan/deletePlan',
  137 + method: 'DELETE',
  138 + params: {
  139 + id,
  140 + },
  141 + });
  142 + },
  143 + // 获得个人模板详情
  144 + getMyPlanDetail: (id) => {
  145 + return request({
  146 + url: '/app/plan/getmyPlanDetail',
  147 + method: 'GET',
  148 + params: {
  149 + id,
  150 + },
  151 + });
  152 + },
83 153
  154 + // 获取计划日历数据(本月+下月)
  155 + getPlanArrangeCalendar: (Id) => {
  156 + return request({
  157 + url: '/app/plan/getCalendar',
  158 + method: 'GET',
  159 + params: {
  160 + Id,
  161 + },
  162 + });
  163 + },
  164 +
  165 + // 移动到
  166 + updateDailyTemplateDate: (data) => {
  167 + return request({
  168 + url: '/app/plan/updateDailyTemplateDate',
  169 + method: 'POST',
  170 + data,
  171 + });
  172 + },
84 173
85 -} 174 + // 收藏-取消收藏计划
  175 + toggleFavorite: (planId, status) => {
  176 + return request({
  177 + url: '/app/plan/favorite/toggle',
  178 + method: 'POST',
  179 + data: { planId, status },
  180 + });
  181 + },
  182 + // 检查是否已收藏计划
  183 + checkFavorite: (planId) => {
  184 + return request({
  185 + url: `/app/plan/favorite/check/${planId}`,
  186 + method: 'GET',
  187 + });
  188 + },
  189 +};
86 190
87 export default QueryPlanApi; 191 export default QueryPlanApi;
@@ -38,13 +38,17 @@ export const useTrainingStore = defineStore('training', { @@ -38,13 +38,17 @@ export const useTrainingStore = defineStore('training', {
38 let res; 38 let res;
39 if (type === 1) { 39 if (type === 1) {
40 res = await ExercisesApi.getExerciseById(id); 40 res = await ExercisesApi.getExerciseById(id);
  41 + console.log('动作详情:', res);
41 } else if (type === 2) { 42 } else if (type === 2) {
42 res = await SupersetsApi.getSupersetsInfo(id); 43 res = await SupersetsApi.getSupersetsInfo(id);
  44 + console.log('超级组详情:', res);
43 } else if (type === 3) { 45 } else if (type === 3) {
44 if (this.isSystem === 1) { 46 if (this.isSystem === 1) {
45 res = await TemplatesApi.getTemplateDetail(id); 47 res = await TemplatesApi.getTemplateDetail(id);
  48 + console.log('官方模板详情:', res);
46 } else { 49 } else {
47 res = await TemplatesApi.getCustTemplateDetail(id); 50 res = await TemplatesApi.getCustTemplateDetail(id);
  51 + console.log('个人模板详情:', res);
48 } 52 }
49 console.log('pinia中打开模板数据:', res); 53 console.log('pinia中打开模板数据:', res);
50 } 54 }
@@ -63,12 +67,12 @@ export const useTrainingStore = defineStore('training', { @@ -63,12 +67,12 @@ export const useTrainingStore = defineStore('training', {
63 }, 67 },
64 68
65 loadDailyTemplateForEdit(fullDailyPlan) { 69 loadDailyTemplateForEdit(fullDailyPlan) {
66 -  
67 - // 2. 🔥 核心:把【完整每日模板数据】直接赋值给 actionDetail  
68 // 动作、顺序、超级组、组数、全部保留,不请求接口 70 // 动作、顺序、超级组、组数、全部保留,不请求接口
  71 + console.log('fullDailyPlan', fullDailyPlan);
  72 +
69 this.actionDetail = { ...fullDailyPlan }; 73 this.actionDetail = { ...fullDailyPlan };
70 74
71 - console.log('每日模板编辑:已赋值完整模板数据到 actionDetail', this.actionDetail); 75 + console.log('每日模板编辑:已赋值完整模板数据到 actionDetail', this.actionDetail);
72 76
73 // 3. 固定类型为模板 77 // 3. 固定类型为模板
74 this.type = 3; 78 this.type = 3;
@@ -79,7 +83,7 @@ export const useTrainingStore = defineStore('training', { @@ -79,7 +83,7 @@ export const useTrainingStore = defineStore('training', {
79 // 5. 初始化训练记录(重量、次数、完成状态) 83 // 5. 初始化训练记录(重量、次数、完成状态)
80 this.unitRecords = {}; 84 this.unitRecords = {};
81 const units = this.actionDetail?.units || []; 85 const units = this.actionDetail?.units || [];
82 - console.log('每日模板的unit',units); 86 + console.log('每日模板的unit', units);
83 87
84 units.forEach((unit, unitIndex) => { 88 units.forEach((unit, unitIndex) => {
85 let records = {}; 89 let records = {};
@@ -99,8 +103,6 @@ export const useTrainingStore = defineStore('training', { @@ -99,8 +103,6 @@ export const useTrainingStore = defineStore('training', {
99 103
100 // 初始化模板自带的训练数据到 unitRecords 104 // 初始化模板自带的训练数据到 unitRecords
101 initTemplateRecords() { 105 initTemplateRecords() {
102 - console.log('✅=================== 我执行了!开始初始化模板数据');  
103 - console.log('EEEEEEEEEEEEEEEEEEEEEE');  
104 // 只在空的时候初始化(避免重复覆盖) 106 // 只在空的时候初始化(避免重复覆盖)
105 if (Object.keys(this.unitRecords).length > 0) return; 107 if (Object.keys(this.unitRecords).length > 0) return;
106 if (this.dailyTemplateId) return; 108 if (this.dailyTemplateId) return;
@@ -201,13 +203,13 @@ export const useTrainingStore = defineStore('training', { @@ -201,13 +203,13 @@ export const useTrainingStore = defineStore('training', {
201 // ✅ 工具方法放到这里 203 // ✅ 工具方法放到这里
202 // ====================== 204 // ======================
203 convertUnitToActionDetail(unit) { 205 convertUnitToActionDetail(unit) {
204 - //转为动作 206 + //超级组转为动作
205 const convertExercises = (list) => { 207 const convertExercises = (list) => {
206 return (list || []).map((item) => ({ 208 return (list || []).map((item) => ({
207 id: item.exerciseId, 209 id: item.exerciseId,
208 name: item.exerciseName, 210 name: item.exerciseName,
209 // urlImage: item.exerciseCover || item.url3dAnimation, 211 // urlImage: item.exerciseCover || item.url3dAnimation,
210 - urlImage: item.url3dAnimation || item.urlImage, 212 + urlImage: item.url3dAnimation || item.exerciseCover || item.urlImage,
211 exerciseType: item.exerciseType, 213 exerciseType: item.exerciseType,
212 })); 214 }));
213 }; 215 };
@@ -221,10 +223,12 @@ export const useTrainingStore = defineStore('training', { @@ -221,10 +223,12 @@ export const useTrainingStore = defineStore('training', {
221 } 223 }
222 if (unit.unitType === 1) { 224 if (unit.unitType === 1) {
223 const act = (unit.exercises || [])[0] || {}; 225 const act = (unit.exercises || [])[0] || {};
  226 + console.log('------------act=', act);
224 return { 227 return {
225 id: act.exerciseId, 228 id: act.exerciseId,
226 name: act.exerciseName, 229 name: act.exerciseName,
227 - urlImage: act.exerciseCover || act.url3dAnimation, 230 + urlImage: act.exerciseCover || act.url3dAnimation || act.urlImage,
  231 + // urlImage
228 exerciseType: act.exerciseType, 232 exerciseType: act.exerciseType,
229 categoryDescription: act.categoryDescription, 233 categoryDescription: act.categoryDescription,
230 equipmentDescription: act.equipmentDescription, 234 equipmentDescription: act.equipmentDescription,
@@ -260,7 +264,8 @@ export const useTrainingStore = defineStore('training', { @@ -260,7 +264,8 @@ export const useTrainingStore = defineStore('training', {
260 exerciseId: this.actionDetail.id, 264 exerciseId: this.actionDetail.id,
261 exerciseName: this.actionDetail.name, 265 exerciseName: this.actionDetail.name,
262 exerciseType: this.actionDetail.exerciseType, 266 exerciseType: this.actionDetail.exerciseType,
263 - urlImage: this.actionDetail.urlImage || this.actionDetail.url3dAnimation, 267 + // urlImage: this.actionDetail.urlImage || this.actionDetail.url3dAnimation,
  268 + urlImage: this.actionDetail.url3dAnimation || this.actionDetail.urlImage,
264 categoryDescription: this.actionDetail.categoryDescription, 269 categoryDescription: this.actionDetail.categoryDescription,
265 equipmentDescription: this.actionDetail.equipmentDescription, 270 equipmentDescription: this.actionDetail.equipmentDescription,
266 }, 271 },