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 ||  
13 - 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png'  
14 - "  
15 - mode="aspectFill"  
16 - class="avatar"  
17 - /> 4 + <view v-if="userStore.isLogin" class="section-card user-header" hover-class="card-hover" @tap="goMyPersonalData">
  5 + <image :src="userInfo.avatar ||
  6 + 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png'
  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,408 +107,456 @@ @@ -142,408 +107,456 @@
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 = [  
166 - { type: 1, key: 'courseNum', label: '待上课' },  
167 - { type: 2, key: 'courseWaitNum', label: '等待中' },  
168 - { type: 3, key: 'courseEvaluationWaitNum', label: '历史课程' },  
169 - ];  
170 -  
171 - // 资产账户字段清洗配置映射 (对应接口数据类型:number 与 integer)  
172 - const accountConfig = [];  
173 -  
174 - /**  
175 - * JSDoc 核心资产数值洗涤函数  
176 - * @param {number|undefined} val 原始金钱/数量值  
177 - * @param {boolean} isFloat 是否需要保留两位小数  
178 - * @returns {string|number} 格式化后的安全渲染字符串  
179 - */  
180 - const formatAccountValue = (val, isFloat) => {  
181 - if (val === undefined || val === null) return isFloat ? '0.00' : 0;  
182 - return isFloat ? Number(val).toFixed(2) : Math.floor(val);  
183 - };  
184 -  
185 - /**  
186 - * 路由守卫拦截转发  
187 - * @param {string} url 目标绝对/相对地址  
188 - */  
189 - const authNavigateTo = (url) => {  
190 - if (!userStore.isLogin) {  
191 - goLogin();  
192 - return;  
193 - }  
194 - uni.navigateTo({  
195 - url,  
196 - fail: (err) => console.error(`[Router] 页面跳转失败 ${url}: `, err),  
197 - });  
198 - };  
199 -  
200 - /**  
201 - * 统一处理功能网格的点击分发  
202 - * @param {Object} item 节点配置项  
203 - */  
204 - const handleGridItemClick = (item) => {  
205 - if (item.text === '联系客服') {  
206 - // #ifndef MP-WEIXIN  
207 - // 兜底非微信小程序平台(如H5、App),可以正常走原来的普通客服页面路由  
208 - authNavigateTo(item.url);  
209 - // #endif  
210 - return;  
211 - }  
212 -  
213 - // 其他正常功能,正常走路由拦截守卫 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 = [
  137 + { type: 1, key: 'courseNum', label: '待上课' },
  138 + { type: 2, key: 'courseWaitNum', label: '等待中' },
  139 + { type: 3, key: 'courseEvaluationWaitNum', label: '历史课程' },
  140 +];
  141 +
  142 +// 资产账户字段清洗配置映射 (对应接口数据类型:number 与 integer)
  143 +const accountConfig = [];
  144 +
  145 +/**
  146 + * JSDoc 核心资产数值洗涤函数
  147 + * @param {number|undefined} val 原始金钱/数量值
  148 + * @param {boolean} isFloat 是否需要保留两位小数
  149 + * @returns {string|number} 格式化后的安全渲染字符串
  150 + */
  151 +const formatAccountValue = (val, isFloat) => {
  152 + if (val === undefined || val === null) return isFloat ? '0.00' : 0;
  153 + return isFloat ? Number(val).toFixed(2) : Math.floor(val);
  154 +};
  155 +
  156 +/**
  157 + * 路由守卫拦截转发
  158 + * @param {string} url 目标绝对/相对地址
  159 + */
  160 +const authNavigateTo = (url) => {
  161 + if (!userStore.isLogin) {
  162 + goLogin();
  163 + return;
  164 + }
  165 + uni.navigateTo({
  166 + url,
  167 + fail: (err) => console.error(`[Router] 页面跳转失败 ${url}: `, err),
  168 + });
  169 +};
  170 +
  171 +/**
  172 + * 统一处理功能网格的点击分发
  173 + * @param {Object} item 节点配置项
  174 + */
  175 +const handleGridItemClick = (item) => {
  176 + if (item.text === '联系客服') {
  177 + // #ifndef MP-WEIXIN
  178 + // 兜底非微信小程序平台(如H5、App),可以正常走原来的普通客服页面路由
214 authNavigateTo(item.url); 179 authNavigateTo(item.url);
215 - };  
216 -  
217 - /**  
218 - * 异步高内聚合并请求  
219 - * 解决因先后触发 setData 导致微信小程序底层 AppService 与 WebView 之间高频拥堵卡顿的问题  
220 - */  
221 - const fetchPageData = async () => {  
222 - try {  
223 - const [userRes, levelRes] = await Promise.all([  
224 - UserApi.getUserInfo(),  
225 - MemberApi.getMemberLevel(),  
226 - ]);  
227 -  
228 - const rawUser = userRes.data || {};  
229 - userInfo.value = rawUser;  
230 -  
231 - // 架构重构:数据拉取后一次性计算出等级映射结果,拒绝在 computed 内部循环执行实例化  
232 - const levels = levelRes.data?.detailList || [];  
233 - if (rawUser.level !== undefined && levels.length > 0) {  
234 - const target = levels.find((item) => item.id === rawUser.level);  
235 - memberLevelName.value = target ? target.name : '';  
236 - } else {  
237 - memberLevelName.value = '';  
238 - }  
239 - } catch (error) {  
240 - console.error('[API Error] 拉取个人资产信息流失败:', error);  
241 - }  
242 - }; 180 + // #endif
  181 + return;
  182 + }
243 183
244 - onShow(() => {  
245 - if (userStore.isLogin) {  
246 - fetchPageData(); 184 + // 其他正常功能,正常走路由拦截守卫
  185 + authNavigateTo(item.url);
  186 +};
  187 +
  188 +/**
  189 + * 异步高内聚合并请求
  190 + * 解决因先后触发 setData 导致微信小程序底层 AppService 与 WebView 之间高频拥堵卡顿的问题
  191 + */
  192 +const fetchPageData = async () => {
  193 + try {
  194 + const [userRes, levelRes] = await Promise.all([
  195 + UserApi.getUserInfo(),
  196 + MemberApi.getMemberLevel(),
  197 + ]);
  198 +
  199 + const rawUser = userRes.data || {};
  200 + userInfo.value = rawUser;
  201 +
  202 + // 架构重构:数据拉取后一次性计算出等级映射结果,拒绝在 computed 内部循环执行实例化
  203 + const levels = levelRes.data?.detailList || [];
  204 + if (rawUser.level !== undefined && levels.length > 0) {
  205 + const target = levels.find((item) => item.id === rawUser.level);
  206 + memberLevelName.value = target ? target.name : '';
247 } else { 207 } else {
248 - userInfo.value = {};  
249 memberLevelName.value = ''; 208 memberLevelName.value = '';
250 } 209 }
251 - }); 210 + } catch (error) {
  211 + console.error('[API Error] 拉取个人资产信息流失败:', error);
  212 + }
  213 +};
  214 +
  215 +onShow(() => {
  216 + if (userStore.isLogin) {
  217 + fetchPageData();
  218 + } else {
  219 + userInfo.value = {};
  220 + memberLevelName.value = '';
  221 + }
  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;  
265 -  
266 - .my-page {  
267 - min-height: 100vh;  
268 - background-color: $page-bg;  
269 - padding: 20rpx 28rpx calc(40rpx + env(safe-area-inset-bottom)); /* 解决部分 iOS 底部 Tabbar 高度塌陷 */  
270 - box-sizing: border-box;  
271 -  
272 - .section-card {  
273 - background: #ffffff;  
274 - border-radius: 24rpx;  
275 - padding: 30rpx 20rpx;  
276 - margin-bottom: 24rpx;  
277 - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03); 234 +$brand-color: #ff6b00;
  235 +$page-bg: #f8f8f8;
  236 +
  237 +.my-page {
  238 + min-height: 100vh;
  239 + background-color: $page-bg;
  240 + padding: 20rpx 28rpx calc(40rpx + env(safe-area-inset-bottom));
  241 + /* 解决部分 iOS 底部 Tabbar 高度塌陷 */
  242 + box-sizing: border-box;
  243 +
  244 + .section-card {
  245 + background: #ffffff;
  246 + border-radius: 24rpx;
  247 + padding: 30rpx 20rpx;
  248 + margin-bottom: 24rpx;
  249 + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
  250 + }
  251 +
  252 + .user-header {
  253 + display: flex;
  254 + align-items: center;
  255 + padding: 34rpx 30rpx;
  256 +
  257 + .avatar {
  258 + width: 110rpx;
  259 + height: 110rpx;
  260 + border-radius: 50%;
  261 + background: #f0f0f0;
  262 + border: 4rpx solid #fff;
278 } 263 }
279 264
280 - .user-header { 265 + .info-content {
  266 + margin-left: 24rpx;
  267 + flex: 1;
  268 +
  269 + .name-row {
  270 + display: flex;
  271 + flex-direction: column;
  272 +
  273 + .nickname {
  274 + font-size: 34rpx;
  275 + font-weight: bold;
  276 + color: #333;
  277 + }
  278 +
  279 + .tag {
  280 + font-size: 20rpx;
  281 + color: $brand-color;
  282 +
  283 + padding: 4rpx 12rpx;
  284 + border-radius: 8rpx;
  285 +
  286 + vertical-align: middle;
  287 + }
  288 + }
  289 + }
  290 +
  291 + .right {
281 display: flex; 292 display: flex;
282 align-items: center; 293 align-items: center;
283 - padding: 34rpx 30rpx;  
284 - .avatar {  
285 - width: 110rpx;  
286 - height: 110rpx;  
287 - border-radius: 50%;  
288 - background: #f0f0f0;  
289 - border: 4rpx solid #fff;  
290 - }  
291 - .info-content {  
292 - margin-left: 24rpx;  
293 - flex: 1;  
294 - .name-row {  
295 - display: flex;  
296 - flex-direction: column;  
297 - .nickname {  
298 - font-size: 34rpx;  
299 - font-weight: bold;  
300 - color: #333;  
301 - }  
302 - .tag {  
303 - font-size: 20rpx;  
304 - color: $brand-color;  
305 -  
306 - padding: 4rpx 12rpx;  
307 - border-radius: 8rpx;  
308 -  
309 - vertical-align: middle;  
310 - }  
311 - } 294 + gap: 60rpx;
  295 +
  296 + .img {
  297 + width: 60rpx;
  298 + height: 60rpx;
312 } 299 }
313 - .right {  
314 - display: flex;  
315 - align-items: center;  
316 - gap: 60rpx;  
317 - .img {  
318 - width: 60rpx;  
319 - height: 60rpx;  
320 - } 300 + }
  301 + }
  302 +
  303 + .login-guide-box {
  304 + display: flex;
  305 + justify-content: space-between;
  306 + align-items: center;
  307 +
  308 + .title {
  309 + font-size: 32rpx;
  310 + font-weight: bold;
  311 + color: #333;
  312 + }
  313 +
  314 + .desc {
  315 + font-size: 22rpx;
  316 + color: #999;
  317 + margin-top: 4rpx;
  318 + display: block;
  319 + }
  320 +
  321 + .login-btn {
  322 + margin: 0;
  323 + background: $brand-color;
  324 + color: #fff;
  325 + font-size: 24rpx;
  326 + height: 64rpx;
  327 + line-height: 64rpx;
  328 + border-radius: 32rpx;
  329 + padding: 0 30rpx;
  330 +
  331 + &::after {
  332 + border: none;
321 } 333 }
  334 +
  335 + /* 清理小程序 button 默认黑边线 */
322 } 336 }
  337 + }
323 338
324 - .login-guide-box { 339 + .vip-banner {
  340 + background: #2b2b2b;
  341 + border-radius: 20rpx;
  342 + padding: 24rpx 30rpx;
  343 + display: flex;
  344 + justify-content: space-between;
  345 + align-items: center;
  346 + margin-bottom: 24rpx;
  347 +
  348 + .vip-info {
325 display: flex; 349 display: flex;
326 - justify-content: space-between;  
327 align-items: center; 350 align-items: center;
328 - .title {  
329 - font-size: 32rpx; 351 + }
  352 +
  353 + .vip-text {
  354 + color: #f1c40f;
  355 + font-size: 24rpx;
  356 + margin-left: 14rpx;
  357 + }
  358 +
  359 + .vip-btn {
  360 + background: linear-gradient(90deg, #f1c40f, #f39c12);
  361 + color: #333;
  362 + font-size: 20rpx;
  363 + font-weight: bold;
  364 + padding: 8rpx 20rpx;
  365 + border-radius: 30rpx;
  366 + }
  367 + }
  368 +
  369 + .quick-entry {
  370 + display: flex;
  371 + justify-content: space-around;
  372 +
  373 + .entry-item {
  374 + text-align: center;
  375 +
  376 + .num {
  377 + font-size: 38rpx;
330 font-weight: bold; 378 font-weight: bold;
331 color: #333; 379 color: #333;
332 } 380 }
333 - .desc { 381 +
  382 + .label {
334 font-size: 22rpx; 383 font-size: 22rpx;
335 color: #999; 384 color: #999;
336 margin-top: 4rpx; 385 margin-top: 4rpx;
337 display: block; 386 display: block;
338 } 387 }
339 - .login-btn {  
340 - margin: 0;  
341 - background: $brand-color;  
342 - color: #fff;  
343 - font-size: 24rpx;  
344 - height: 64rpx;  
345 - line-height: 64rpx;  
346 - border-radius: 32rpx;  
347 - padding: 0 30rpx;  
348 - &::after {  
349 - border: none;  
350 - } /* 清理小程序 button 默认黑边线 */  
351 - }  
352 } 388 }
  389 + }
  390 +
  391 + .banner-box {
  392 + height: 160rpx;
  393 + margin-bottom: 24rpx;
  394 + border-radius: 20rpx;
  395 + overflow: hidden;
  396 +
  397 + .banner-img {
  398 + width: 100%;
  399 + height: 100%;
  400 + }
  401 + }
  402 +
  403 + .apply-section {
  404 + display: grid;
  405 + grid-template-columns: repeat(5, 1fr);
353 406
354 - .vip-banner {  
355 - background: #2b2b2b;  
356 - border-radius: 20rpx;  
357 - padding: 24rpx 30rpx; 407 + .apply-item {
358 display: flex; 408 display: flex;
359 - justify-content: space-between; 409 + flex-direction: column;
360 align-items: center; 410 align-items: center;
361 - margin-bottom: 24rpx;  
362 - .vip-info { 411 +
  412 + .icon-bg {
  413 + width: 80rpx;
  414 + height: 80rpx;
  415 + background: #f9f9f9;
  416 + border-radius: 20rpx;
363 display: flex; 417 display: flex;
364 align-items: center; 418 align-items: center;
365 - }  
366 - .vip-text {  
367 - color: #f1c40f;  
368 - font-size: 24rpx;  
369 - margin-left: 14rpx;  
370 - }  
371 - .vip-btn {  
372 - background: linear-gradient(90deg, #f1c40f, #f39c12);  
373 - color: #333;  
374 - font-size: 20rpx;  
375 - font-weight: bold;  
376 - padding: 8rpx 20rpx;  
377 - border-radius: 30rpx;  
378 - }  
379 - } 419 + justify-content: center;
  420 + margin-bottom: 12rpx;
380 421
381 - .quick-entry {  
382 - display: flex;  
383 - justify-content: space-around;  
384 - .entry-item {  
385 - text-align: center;  
386 - .num {  
387 - font-size: 38rpx;  
388 - font-weight: bold;  
389 - color: #333;  
390 - }  
391 - .label {  
392 - font-size: 22rpx;  
393 - color: #999;  
394 - margin-top: 4rpx;  
395 - display: block; 422 + .img {
  423 + width: 44rpx;
  424 + height: 44rpx;
396 } 425 }
397 } 426 }
398 - }  
399 427
400 - .banner-box {  
401 - height: 160rpx;  
402 - margin-bottom: 24rpx;  
403 - border-radius: 20rpx;  
404 - overflow: hidden;  
405 - .banner-img {  
406 - width: 100%;  
407 - height: 100%; 428 + .text {
  429 + font-size: 22rpx;
  430 + color: #666;
408 } 431 }
409 } 432 }
  433 + }
410 434
411 - .apply-section {  
412 - display: grid;  
413 - grid-template-columns: repeat(5, 1fr);  
414 - .apply-item {  
415 - display: flex;  
416 - flex-direction: column;  
417 - align-items: center;  
418 - .icon-bg {  
419 - width: 80rpx;  
420 - height: 80rpx;  
421 - background: #f9f9f9;  
422 - border-radius: 20rpx;  
423 - display: flex;  
424 - align-items: center;  
425 - justify-content: center;  
426 - margin-bottom: 12rpx;  
427 - .img {  
428 - width: 44rpx;  
429 - height: 44rpx;  
430 - }  
431 - }  
432 - .text {  
433 - font-size: 22rpx; 435 + .account-grid {
  436 + display: grid;
  437 + grid-template-columns: repeat(4, 1fr);
  438 + row-gap: 34rpx;
  439 +
  440 + .account-item {
  441 + display: flex;
  442 + flex-direction: column;
  443 + align-items: center;
  444 +
  445 + .acc-val {
  446 + font-size: 30rpx;
  447 + font-weight: bold;
  448 + color: #333;
  449 +
  450 + .unit {
  451 + font-size: 20rpx;
  452 + font-weight: normal;
434 color: #666; 453 color: #666;
  454 + margin-left: 2rpx;
435 } 455 }
436 } 456 }
437 - }  
438 457
439 - .account-grid {  
440 - display: grid;  
441 - grid-template-columns: repeat(4, 1fr);  
442 - row-gap: 34rpx;  
443 - .account-item {  
444 - display: flex;  
445 - flex-direction: column;  
446 - align-items: center;  
447 - .acc-val {  
448 - font-size: 30rpx;  
449 - font-weight: bold;  
450 - color: #333;  
451 - .unit {  
452 - font-size: 20rpx;  
453 - font-weight: normal;  
454 - color: #666;  
455 - margin-left: 2rpx;  
456 - }  
457 - }  
458 - .acc-lab {  
459 - font-size: 22rpx;  
460 - color: #999;  
461 - margin-top: 4rpx;  
462 - } 458 + .acc-lab {
  459 + font-size: 22rpx;
  460 + color: #999;
  461 + margin-top: 4rpx;
463 } 462 }
464 } 463 }
  464 + }
465 465
466 - .icon-grid {  
467 - display: grid;  
468 - grid-template-columns: repeat(4, 1fr);  
469 - row-gap: 40rpx;  
470 - .icon-item {  
471 - display: flex;  
472 - flex-direction: column;  
473 - align-items: center; 466 + .icon-grid {
  467 + display: grid;
  468 + grid-template-columns: repeat(4, 1fr);
  469 + row-gap: 40rpx;
  470 +
  471 + .icon-item {
  472 + display: flex;
  473 + flex-direction: column;
  474 + align-items: center;
  475 + position: relative;
  476 +
  477 + .icon-img-wrap {
474 position: relative; 478 position: relative;
475 - .icon-img-wrap {  
476 - position: relative;  
477 - margin-bottom: 12rpx;  
478 - .icon-img {  
479 - width: 46rpx;  
480 - height: 46rpx;  
481 - }  
482 - .badge {  
483 - position: absolute;  
484 - top: -12rpx;  
485 - right: -42rpx; /* 适当拓宽右移,容纳后端多字文本 */  
486 - background: #ff4d4f;  
487 - color: #fff;  
488 - font-size: 18rpx;  
489 - padding: 2rpx 10rpx;  
490 - border-radius: 16rpx;  
491 - white-space: nowrap;  
492 - }  
493 - /* 未读状态小红点 */  
494 - .dot-badge {  
495 - position: absolute;  
496 - top: -4rpx;  
497 - right: -4rpx;  
498 - width: 14rpx;  
499 - height: 14rpx;  
500 - background: #ff4d4f;  
501 - border-radius: 50%;  
502 - }  
503 - }  
504 - .icon-text {  
505 - font-size: 24rpx;  
506 - color: #555; 479 + margin-bottom: 12rpx;
  480 +
  481 + .icon-img {
  482 + width: 46rpx;
  483 + height: 46rpx;
507 } 484 }
508 - .mp-contact-overlay-btn { 485 +
  486 + .badge {
509 position: absolute; 487 position: absolute;
510 - top: 0;  
511 - left: 0;  
512 - width: 100% !important;  
513 - height: 100% !important;  
514 - opacity: 0 !important; /* 核心:完全透明 */  
515 - border: none !important;  
516 - padding: 0 !important;  
517 - margin: 0 !important;  
518 - z-index: 10; /* 确保盖在图表和文字的最上方 */ 488 + top: -12rpx;
  489 + right: -42rpx;
  490 + /* 适当拓宽右移,容纳后端多字文本 */
  491 + background: #ff4d4f;
  492 + color: #fff;
  493 + font-size: 18rpx;
  494 + padding: 2rpx 10rpx;
  495 + border-radius: 16rpx;
  496 + white-space: nowrap;
  497 + }
519 498
520 - &::after {  
521 - border: none !important;  
522 - } 499 + /* 未读状态小红点 */
  500 + .dot-badge {
  501 + position: absolute;
  502 + top: -4rpx;
  503 + right: -4rpx;
  504 + width: 14rpx;
  505 + height: 14rpx;
  506 + background: #ff4d4f;
  507 + border-radius: 50%;
523 } 508 }
524 } 509 }
525 - }  
526 510
527 - .setting-item {  
528 - display: flex;  
529 - justify-content: space-between;  
530 - align-items: center;  
531 - padding: 24rpx 0;  
532 - border-bottom: 1rpx solid #f9f9f9;  
533 - &:last-child {  
534 - border-bottom: none; 511 + .icon-text {
  512 + font-size: 24rpx;
  513 + color: #555;
535 } 514 }
536 - .setting-text {  
537 - font-size: 28rpx;  
538 - color: #444; 515 +
  516 + .mp-contact-overlay-btn {
  517 + position: absolute;
  518 + top: 0;
  519 + left: 0;
  520 + width: 100% !important;
  521 + height: 100% !important;
  522 + opacity: 0 !important;
  523 + /* 核心:完全透明 */
  524 + border: none !important;
  525 + padding: 0 !important;
  526 + margin: 0 !important;
  527 + z-index: 10;
  528 + /* 确保盖在图表和文字的最上方 */
  529 +
  530 + &::after {
  531 + border: none !important;
  532 + }
539 } 533 }
540 } 534 }
  535 + }
541 536
542 - .opacity-hover {  
543 - opacity: 0.7; 537 + .setting-item {
  538 + display: flex;
  539 + justify-content: space-between;
  540 + align-items: center;
  541 + padding: 24rpx 0;
  542 + border-bottom: 1rpx solid #f9f9f9;
  543 +
  544 + &:last-child {
  545 + border-bottom: none;
544 } 546 }
545 - .card-hover {  
546 - background-color: #fcfcfc; 547 +
  548 + .setting-text {
  549 + font-size: 28rpx;
  550 + color: #444;
547 } 551 }
548 } 552 }
  553 +
  554 + .opacity-hover {
  555 + opacity: 0.7;
  556 + }
  557 +
  558 + .card-hover {
  559 + background-color: #fcfcfc;
  560 + }
  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;
@@ -82,7 +82,7 @@ @@ -82,7 +82,7 @@
82 <view class="action-top"> 82 <view class="action-top">
83 <text class="action-name">{{ unit.exercises[0]?.exerciseName || '动作名称' }}</text> 83 <text class="action-name">{{ unit.exercises[0]?.exerciseName || '动作名称' }}</text>
84 <text class="action-totalWeight">{{ unit.totalWeight 84 <text class="action-totalWeight">{{ unit.totalWeight
85 - }}kg</text> 85 + }}kg</text>
86 </view> 86 </view>
87 87
88 <!-- 下半部分:组次行 → 向左对齐图片 ✅核心--> 88 <!-- 下半部分:组次行 → 向左对齐图片 ✅核心-->
@@ -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,137 +122,125 @@ @@ -162,137 +122,125 @@
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({  
173 - // finished: false → 还有没有更多数据可以加载?false代表还有更多数据可以加载  
174 - recommend: { list: [], pageNo: 1, finished: false },  
175 - group: { list: [], pageNo: 1, finished: false },  
176 - private: { list: [], pageNo: 1, finished: false },  
177 - small: { list: [], pageNo: 1, finished: false },  
178 - follow: { list: [], pageNo: 1, finished: false },  
179 - });  
180 - const loading = ref(false);  
181 -  
182 - // 当前显示的列表(computed)  
183 - const currentRecommendList = computed(() => {  
184 - return tabData.value[currentTab.value]?.list || [];  
185 - });  
186 -  
187 - const finished = computed(() => {  
188 - return tabData.value[currentTab.value]?.finished || false;  
189 - });  
190 -  
191 - // 获取 storeId(请根据你项目实际情况调整)  
192 - const storeId = userStore.initUser.storeId;  
193 - // 切换 Tab  
194 - const switchTab = (type) => {  
195 - if (currentTab.value === type) return;  
196 - currentTab.value = type;  
197 - // 如果该 tab 还没加载过,触发加载  
198 - if (tabData.value[type].list.length === 0 && !tabData.value[type].finished) {  
199 - loadRecommendData();  
200 - } 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({
  135 + // finished: false → 还有没有更多数据可以加载?false代表还有更多数据可以加载
  136 + recommend: { list: [], pageNo: 1, finished: false },
  137 + group: { list: [], pageNo: 1, finished: false },
  138 + private: { list: [], pageNo: 1, finished: false },
  139 + small: { list: [], pageNo: 1, finished: false },
  140 + follow: { list: [], pageNo: 1, finished: false },
  141 +});
  142 +const loading = ref(false);
  143 +
  144 +// 当前显示的列表(computed)
  145 +const currentRecommendList = computed(() => {
  146 + return tabData.value[currentTab.value]?.list || [];
  147 +});
  148 +
  149 +const finished = computed(() => {
  150 + return tabData.value[currentTab.value]?.finished || false;
  151 +});
  152 +
  153 +// 获取 storeId(请根据你项目实际情况调整)
  154 +const storeId = userStore.initUser.storeId;
  155 +// 切换 Tab
  156 +const switchTab = (type) => {
  157 + if (currentTab.value === type) return;
  158 + currentTab.value = type;
  159 + // 如果该 tab 还没加载过,触发加载
  160 + if (tabData.value[type].list.length === 0 && !tabData.value[type].finished) {
  161 + loadRecommendData();
  162 + }
  163 +};
  164 +
  165 +// 加载推荐数据
  166 +const loadRecommendData = async () => {
  167 + const tab = currentTab.value;
  168 + const tabInfo = tabData.value[tab];
  169 + if (loading.value || tabInfo.finished) return;
  170 +
  171 + loading.value = true;
  172 +
  173 + // 映射 tab 到 type 参数(关注 tab 暂不传 type,或你可自定义)
  174 + const typeMap = { group: '1', private: '2', small: '3' };
  175 + const params = {
  176 + storeId,
  177 + pageNo: tabInfo.pageNo,
  178 + pageSize: 10,
  179 + type: typeMap[tab], // 推荐 和 关注 不传 type
  180 + cityId: userStore.initUser.cityId, //暂时定死了,后续可添加
201 }; 181 };
202 182
203 - // 加载推荐数据  
204 - const loadRecommendData = async () => {  
205 - const tab = currentTab.value;  
206 - const tabInfo = tabData.value[tab];  
207 - if (loading.value || tabInfo.finished) return;  
208 -  
209 - loading.value = true;  
210 -  
211 - // 映射 tab 到 type 参数(关注 tab 暂不传 type,或你可自定义)  
212 - const typeMap = { group: '1', private: '2', small: '3' };  
213 - const params = {  
214 - storeId,  
215 - pageNo: tabInfo.pageNo,  
216 - pageSize: 10,  
217 - type: typeMap[tab], // 推荐 和 关注 不传 type  
218 - cityId: userStore.initUser.cityId, //暂时定死了,后续可添加  
219 - };  
220 -  
221 - try {  
222 - const res = await coachUpdatesApi.coachmoments(params);  
223 - if (res.code === 0) {  
224 - const newData = res.data?.list || [];  
225 - tabInfo.list = [...tabInfo.list, ...newData];  
226 -  
227 - const total = res.data?.total || 0;  
228 - // 1 后端数据没有数据返回了  
229 - if (tabInfo.list.length >= total) {  
230 - tabInfo.finished = true;  
231 - // 2 后端还有数据,当前页+1进入下一页  
232 - } else {  
233 - tabInfo.pageNo += 1;  
234 - } 183 + try {
  184 + const res = await coachUpdatesApi.coachmoments(params);
  185 + if (res.code === 0) {
  186 + const newData = res.data?.list || [];
  187 + tabInfo.list = [...tabInfo.list, ...newData];
  188 +
  189 + const total = res.data?.total || 0;
  190 + // 1 后端数据没有数据返回了
  191 + if (tabInfo.list.length >= total) {
  192 + tabInfo.finished = true;
  193 + // 2 后端还有数据,当前页+1进入下一页
235 } else { 194 } else {
236 - uni.showToast({ title: res.msg || '加载失败', icon: 'none' }); 195 + tabInfo.pageNo += 1;
237 } 196 }
238 - } catch (err) {  
239 - console.error('加载推荐失败:', err);  
240 - console.log(params);  
241 - uni.showToast({ title: '网络错误', icon: 'none' });  
242 - } finally {  
243 - loading.value = false;  
244 - }  
245 - };  
246 -  
247 - // 上拉加载更多  
248 - const onScrollToLower = () => {  
249 - if (!finished.value && !loading.value) {  
250 - loadRecommendData(); 197 + } else {
  198 + uni.showToast({ title: res.msg || '加载失败', icon: 'none' });
251 } 199 }
252 - }; 200 + } catch (err) {
  201 + console.error('加载推荐失败:', err);
  202 + console.log(params);
  203 + uni.showToast({ title: '网络错误', icon: 'none' });
  204 + } finally {
  205 + loading.value = false;
  206 + }
  207 +};
253 208
254 - const planList = ref([]);  
255 - // 新手第一课接口,如果有数据就循环显示数据,没有就展示默认暂无新手计划  
256 - const loadPlans = async () => {  
257 - try {  
258 - const res = await QueryPlanApi.getPlanList();  
259 - const allPlans = res.data || [];  
260 - // 从后端拿所有训练计划 → 专门挑出 id=1 的那个计划 → 显示在首页 “新手入门第一课”  
261 - const targetPlan = allPlans.find((plan) => plan.id === 1);  
262 - // 找到了 id=1 → 显示它:标题、封面、难度、多少人练过  
263 - if (targetPlan) {  
264 - const difficultyText = { 1: '初阶', 2: '中阶', 3: '高阶' };  
265 - planList.value = [  
266 - {  
267 - id: targetPlan.id,  
268 - title: targetPlan.name,  
269 - cover:  
270 - targetPlan.urlCover ||  
271 - 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png',  
272 - tag: '火爆',  
273 - tagClass: 'hot-tag',  
274 - meta: ` ${difficultyText[targetPlan.difficultyLevel] || '初阶'} · ${  
275 - targetPlan.enrollmentCount 209 +// 上拉加载更多
  210 +const onScrollToLower = () => {
  211 + if (!finished.value && !loading.value) {
  212 + loadRecommendData();
  213 + }
  214 +};
  215 +
  216 +const planList = ref([]);
  217 +// 新手第一课接口,如果有数据就循环显示数据,没有就展示默认暂无新手计划
  218 +const loadPlans = async () => {
  219 + try {
  220 + const res = await QueryPlanApi.getPlanList();
  221 + const allPlans = res.data || [];
  222 + // 从后端拿所有训练计划 → 专门挑出 id=1 的那个计划 → 显示在首页 “新手入门第一课”
  223 + const targetPlan = allPlans.find((plan) => plan.id === 1);
  224 + // 找到了 id=1 → 显示它:标题、封面、难度、多少人练过
  225 + if (targetPlan) {
  226 + const difficultyText = { 1: '初阶', 2: '中阶', 3: '高阶' };
  227 + planList.value = [
  228 + {
  229 + id: targetPlan.id,
  230 + title: targetPlan.name,
  231 + cover:
  232 + targetPlan.urlCover ||
  233 + 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png',
  234 + tag: '火爆',
  235 + tagClass: 'hot-tag',
  236 + meta: ` ${difficultyText[targetPlan.difficultyLevel] || '初阶'} · ${targetPlan.enrollmentCount
276 }人练过`, 237 }人练过`,
277 - },  
278 - ];  
279 - } else {  
280 - planList.value = [  
281 - {  
282 - title: '暂无新手计划',  
283 - cover:  
284 - 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png',  
285 - tag: '',  
286 - tagClass: '',  
287 - meta: '',  
288 - },  
289 - ];  
290 - }  
291 - } catch (err) {  
292 - console.error('加载计划失败:', err); 238 + },
  239 + ];
  240 + } else {
293 planList.value = [ 241 planList.value = [
294 { 242 {
295 - title: '加载失败', 243 + title: '暂无新手计划',
296 cover: 244 cover:
297 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png', 245 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png',
298 tag: '', 246 tag: '',
@@ -301,386 +249,399 @@ @@ -301,386 +249,399 @@
301 }, 249 },
302 ]; 250 ];
303 } 251 }
304 - }; 252 + } catch (err) {
  253 + console.error('加载计划失败:', err);
  254 + planList.value = [
  255 + {
  256 + title: '加载失败',
  257 + cover:
  258 + 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260316/order-empty_1773628059920.png',
  259 + tag: '',
  260 + tagClass: '',
  261 + meta: '',
  262 + },
  263 + ];
  264 + }
  265 +};
305 266
306 - // 跳转方法  
307 - const goPlanDetail = (item) => { 267 +// 跳转方法
  268 +const goPlanDetail = (item) => {
  269 + uni.navigateTo({
  270 + url: `/pages4/pages/xunji/xunji-xunlian-jihua?planid= ${item.id}`,
  271 + });
  272 +};
  273 +// 这是一个【跳转函数】,点击推荐里的内容,跳转到动态详情页,并且把当前是 “推荐 / 团课 / 私教 / 小班 / 关注” 这个分类带给下一页。
  274 +const goRecommendDetail = (item) => {
  275 + // 传递当前 tab 类型,方便目标页初始化
  276 + const typeMap = { group: '1', private: '2', small: '3', recommend: '', follow: '' };
  277 + const type = typeMap[currentTab.value] || '';
  278 + uni.navigateTo({
  279 + url: `/pages3/pages/coach/jiaolian-dongtai?type= ${encodeURIComponent(type)}`,
  280 + });
  281 +};
  282 +// 跳转到动态详情
  283 +const navigateToDetail = (item) => {
  284 + if (item.photo) {
308 uni.navigateTo({ 285 uni.navigateTo({
309 - url: `/pages4/pages/xunji/xunji-xunlian-jihua?planid= ${item.id}`, 286 + url: `/pages3/pages/coach/jiaolian-dongtai?id=${item.id}`,
310 }); 287 });
311 - };  
312 - // 这是一个【跳转函数】,点击推荐里的内容,跳转到动态详情页,并且把当前是 “推荐 / 团课 / 私教 / 小班 / 关注” 这个分类带给下一页。  
313 - const goRecommendDetail = (item) => {  
314 - // 传递当前 tab 类型,方便目标页初始化  
315 - const typeMap = { group: '1', private: '2', small: '3', recommend: '', follow: '' };  
316 - const type = typeMap[currentTab.value] || ''; 288 + } else {
317 uni.navigateTo({ 289 uni.navigateTo({
318 - url: `/pages3/pages/coach/jiaolian-dongtai?type= ${encodeURIComponent(type)}`, 290 + url: `/pages3/pages/coach/jiaolian-shipin-dongtai?id=${item.id}`,
319 }); 291 });
320 - };  
321 - // 跳转到动态详情  
322 - const navigateToDetail = (item) => {  
323 - if (item.photo) {  
324 - uni.navigateTo({  
325 - url: `/pages3/pages/coach/jiaolian-dongtai?id=${item.id}`,  
326 - });  
327 - } else {  
328 - uni.navigateTo({  
329 - url: `/pages3/pages/coach/jiaolian-shipin-dongtai?id=${item.id}`,  
330 - });  
331 - }  
332 - };  
333 - // 动态点赞  
334 - const handleDynamicLike = async (item, index) => {  
335 - // await CoachApi.likeCoachDynamic({  
336 - // id: item.id,  
337 - // status: item.isLike == 1 ? 0 : 1,  
338 - // });  
339 - // coachDynamicList.value[index].isLike = item.isLike == 1 ? 0 : 1;  
340 - try {  
341 - // 新状态:1=点赞,0=取消  
342 - const newStatus = item.isLike == 1 ? 0 : 1;  
343 - // 请求后端  
344 - await coachUpdatesApi.likeCoachDynamic({  
345 - id: item.id,  
346 - status: newStatus,  
347 - });  
348 - // 前端更新状态(修复后)  
349 - item.isLike = newStatus;  
350 - } catch (err) {  
351 - console.log('点赞失败', err);  
352 - }  
353 - };  
354 - // 实现动作库跳转,跳转到F:\hongxing-app\hongxing-new\pages\xunji\components\xunji-dongzuo.vue  
355 - const goToActionLibrary = () => {  
356 - uni.navigateTo({  
357 - url: '/pages/xunji/components/xunji-dongzuo', 292 + }
  293 +};
  294 +// 动态点赞
  295 +const handleDynamicLike = async (item, index) => {
  296 + // await CoachApi.likeCoachDynamic({
  297 + // id: item.id,
  298 + // status: item.isLike == 1 ? 0 : 1,
  299 + // });
  300 + // coachDynamicList.value[index].isLike = item.isLike == 1 ? 0 : 1;
  301 + try {
  302 + // 新状态:1=点赞,0=取消
  303 + const newStatus = item.isLike == 1 ? 0 : 1;
  304 + // 请求后端
  305 + await coachUpdatesApi.likeCoachDynamic({
  306 + id: item.id,
  307 + status: newStatus,
358 }); 308 });
359 - };  
360 - // 实现  
361 - // 初始化  
362 - onMounted(() => {  
363 - loadPlans();  
364 - loadRecommendData(); // 加载默认 tab(推荐)数据 309 + // 前端更新状态(修复后)
  310 + item.isLike = newStatus;
  311 + } catch (err) {
  312 + console.log('点赞失败', err);
  313 + }
  314 +};
  315 +// 实现动作库跳转,跳转到F:\hongxing-app\hongxing-new\pages\xunji\components\xunji-dongzuo.vue
  316 +const goToActionLibrary = () => {
  317 + uni.navigateTo({
  318 + url: '/pages/xunji/components/xunji-dongzuo',
365 }); 319 });
  320 +};
  321 +// 实现
  322 +// 初始化
  323 +onMounted(() => {
  324 + loadPlans();
  325 + loadRecommendData(); // 加载默认 tab(推荐)数据
  326 +});
366 </script> 327 </script>
367 328
368 <style lang="scss" scoped> 329 <style lang="scss" scoped>
369 - .container { 330 +.container {
  331 + width: 100%;
  332 + height: 100%;
  333 + padding: 20rpx;
  334 + box-sizing: border-box;
  335 +
  336 + .banner-section {
370 width: 100%; 337 width: 100%;
371 - height: 100%;  
372 - padding: 20rpx; 338 + height: 200rpx;
373 box-sizing: border-box; 339 box-sizing: border-box;
374 -  
375 - .banner-section {  
376 - width: 100%;  
377 - height: 200rpx; 340 + display: grid;
  341 + grid-template-columns: repeat(3, 1fr);
  342 + gap: 20rpx;
  343 + background-color: #fff;
  344 + justify-content: center;
  345 + align-items: center;
  346 + margin-bottom: 20rpx;
  347 +
  348 + .item {
  349 + padding: 20rpx;
378 box-sizing: border-box; 350 box-sizing: border-box;
379 - display: grid;  
380 - grid-template-columns: repeat(3, 1fr);  
381 - gap: 20rpx;  
382 - background-color: #fff;  
383 - justify-content: center; 351 + width: 100%;
  352 + height: 100%;
  353 + display: flex;
  354 + flex-direction: column;
384 align-items: center; 355 align-items: center;
385 - margin-bottom: 20rpx; 356 + gap: 5rpx;
386 357
387 - .item {  
388 - padding: 20rpx;  
389 - box-sizing: border-box;  
390 - width: 100%;  
391 - height: 100%;  
392 - display: flex;  
393 - flex-direction: column;  
394 - align-items: center;  
395 - gap: 5rpx;  
396 -  
397 - .img {  
398 - width: 100rpx;  
399 - height: 100rpx;  
400 - } 358 + .img {
  359 + width: 100rpx;
  360 + height: 100rpx;
  361 + }
401 362
402 - .title {  
403 - font-weight: bold;  
404 - } 363 + .title {
  364 + font-weight: bold;
  365 + }
405 366
406 - .desc {  
407 - font-size: 20rpx;  
408 - color: #999;  
409 - } 367 + .desc {
  368 + font-size: 20rpx;
  369 + color: #999;
410 } 370 }
411 } 371 }
  372 + }
412 373
413 - .plan-section {  
414 - background-color: #fff; 374 + .plan-section {
  375 + background-color: #fff;
415 376
416 - border-radius: 12rpx;  
417 - overflow: hidden; 377 + border-radius: 12rpx;
  378 + overflow: hidden;
418 379
419 - .section-header {  
420 - display: flex;  
421 - justify-content: space-between;  
422 - align-items: center;  
423 - padding: 20rpx 30rpx; 380 + .section-header {
  381 + display: flex;
  382 + justify-content: space-between;
  383 + align-items: center;
  384 + padding: 20rpx 30rpx;
424 385
425 - .section-title {  
426 - font-size: 28rpx;  
427 - font-weight: bold;  
428 - color: #333;  
429 - } 386 + .section-title {
  387 + font-size: 28rpx;
  388 + font-weight: bold;
  389 + color: #333;
430 } 390 }
  391 + }
431 392
432 - .plan-list { 393 + .plan-list {
  394 + display: flex;
  395 + flex-direction: column;
  396 + gap: 20rpx;
  397 + padding: 0 20rpx 20rpx;
  398 +
  399 + .plan-item {
  400 + width: 100%;
433 display: flex; 401 display: flex;
434 - flex-direction: column;  
435 - gap: 20rpx;  
436 - padding: 0 20rpx 20rpx; 402 + border-radius: 8rpx;
  403 + overflow: hidden;
  404 + background-color: #f5f5f5;
  405 + position: relative;
  406 + padding: 8rpx;
  407 + box-sizing: border-box;
437 408
438 - .plan-item {  
439 - width: 100%; 409 + .plan-cover {
  410 + width: 200rpx;
  411 + height: 140rpx;
  412 + }
  413 +
  414 + .plan-desc {
440 display: flex; 415 display: flex;
441 - border-radius: 8rpx;  
442 - overflow: hidden;  
443 - background-color: #f5f5f5;  
444 - position: relative;  
445 - padding: 8rpx;  
446 - box-sizing: border-box;  
447 -  
448 - .plan-cover {  
449 - width: 200rpx;  
450 - height: 140rpx;  
451 - } 416 + flex-direction: column;
  417 + justify-content: space-between;
  418 + padding: 10rpx;
452 419
453 - .plan-desc {  
454 - display: flex;  
455 - flex-direction: column;  
456 - justify-content: space-between;  
457 - padding: 10rpx;  
458 -  
459 - .plan-tag {  
460 - position: absolute;  
461 - top: 10rpx;  
462 - right: 10rpx;  
463 - padding: 2rpx 10rpx;  
464 - border-radius: 4rpx;  
465 - font-size: 20rpx;  
466 - margin-bottom: 6rpx;  
467 - } 420 + .plan-tag {
  421 + position: absolute;
  422 + top: 10rpx;
  423 + right: 10rpx;
  424 + padding: 2rpx 10rpx;
  425 + border-radius: 4rpx;
  426 + font-size: 20rpx;
  427 + margin-bottom: 6rpx;
  428 + }
468 429
469 - .hot-tag {  
470 - background-color: #ff4d4f;  
471 - color: #fff;  
472 - } 430 + .hot-tag {
  431 + background-color: #ff4d4f;
  432 + color: #fff;
  433 + }
473 434
474 - .new-tag {  
475 - background-color: #ffbb00;  
476 - color: #fff;  
477 - } 435 + .new-tag {
  436 + background-color: #ffbb00;
  437 + color: #fff;
  438 + }
478 439
479 - .plan-title {  
480 - font-size: 26rpx;  
481 - font-weight: 500;  
482 - color: #333;  
483 - margin-bottom: 6rpx;  
484 - } 440 + .plan-title {
  441 + font-size: 26rpx;
  442 + font-weight: 500;
  443 + color: #333;
  444 + margin-bottom: 6rpx;
  445 + }
485 446
486 - .plan-meta {  
487 - font-size: 22rpx;  
488 - color: #999;  
489 - } 447 + .plan-meta {
  448 + font-size: 22rpx;
  449 + color: #999;
490 } 450 }
491 } 451 }
492 } 452 }
493 } 453 }
  454 + }
494 455
495 - .recommend-section {  
496 - margin-top: 40rpx;  
497 - padding-bottom: 10rpx;  
498 - box-sizing: border-box; 456 + .recommend-section {
  457 + margin-top: 40rpx;
  458 + padding-bottom: 10rpx;
  459 + box-sizing: border-box;
499 460
500 - .recommend-header {  
501 - display: flex;  
502 - flex-direction: column;  
503 - margin-bottom: 20rpx; 461 + .recommend-header {
  462 + display: flex;
  463 + flex-direction: column;
  464 + margin-bottom: 20rpx;
504 465
505 - .header-title {  
506 - font-size: 32rpx;  
507 - color: #333;  
508 - font-weight: bold;  
509 - margin-bottom: 10rpx;  
510 - } 466 + .header-title {
  467 + font-size: 32rpx;
  468 + color: #333;
  469 + font-weight: bold;
  470 + margin-bottom: 10rpx;
  471 + }
511 472
512 - .tabs {  
513 - display: flex;  
514 - gap: 10rpx;  
515 - position: sticky;  
516 - top: 0;  
517 - z-index: 100;  
518 - background-color: #fff;  
519 - padding: 10rpx 0;  
520 - margin: 0 -30rpx;  
521 - padding-left: 30rpx;  
522 - padding-right: 30rpx;  
523 - box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);  
524 -  
525 - :deep(uni-button) {  
526 - padding: 8rpx 16rpx;  
527 - line-height: 1;  
528 - border-radius: 5rpx;  
529 -  
530 - &::after {  
531 - content: none;  
532 - } 473 + .tabs {
  474 + display: flex;
  475 + gap: 10rpx;
  476 + position: sticky;
  477 + top: 0;
  478 + z-index: 100;
  479 + background-color: #fff;
  480 + padding: 10rpx 0;
  481 + margin: 0 -30rpx;
  482 + padding-left: 30rpx;
  483 + padding-right: 30rpx;
  484 + box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
  485 +
  486 + :deep(uni-button) {
  487 + padding: 8rpx 16rpx;
  488 + line-height: 1;
  489 + border-radius: 5rpx;
  490 +
  491 + &::after {
  492 + content: none;
533 } 493 }
  494 + }
534 495
535 - .tab-btn {  
536 - background-color: white;  
537 - color: #333;  
538 - font-size: 24rpx;  
539 - margin: 0;  
540 - } 496 + .tab-btn {
  497 + background-color: white;
  498 + color: #333;
  499 + font-size: 24rpx;
  500 + margin: 0;
  501 + }
541 502
542 - .active {  
543 - background-color: #ffcc00;  
544 - color: white;  
545 - } 503 + .active {
  504 + background-color: #ffcc00;
  505 + color: white;
546 } 506 }
547 } 507 }
  508 + }
548 509
549 - .recommend-list {  
550 - display: grid;  
551 - grid-template-columns: 1fr 1fr;  
552 - gap: 20rpx; 510 + .recommend-list {
  511 + display: grid;
  512 + grid-template-columns: 1fr 1fr;
  513 + gap: 20rpx;
553 514
554 - .recommend-item {  
555 - background-color: white;  
556 - border-radius: 16rpx;  
557 - overflow: hidden;  
558 - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);  
559 - padding: 20rpx; 515 + .recommend-item {
  516 + background-color: white;
  517 + border-radius: 16rpx;
  518 + overflow: hidden;
  519 + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
  520 + padding: 20rpx;
560 521
561 - .video-img {  
562 - width: 100%;  
563 - height: 200rpx;  
564 - border-radius: 12rpx;  
565 - margin-bottom: 10rpx;  
566 - } 522 + .video-img {
  523 + width: 100%;
  524 + height: 200rpx;
  525 + border-radius: 12rpx;
  526 + margin-bottom: 10rpx;
  527 + }
567 528
568 - .video-title {  
569 - font-size: 28rpx;  
570 - color: #333;  
571 - margin-bottom: 10rpx;  
572 - display: -webkit-box;  
573 - -webkit-line-clamp: 2;  
574 - -webkit-box-orient: vertical;  
575 - overflow: hidden;  
576 - } 529 + .video-title {
  530 + font-size: 28rpx;
  531 + color: #333;
  532 + margin-bottom: 10rpx;
  533 + display: -webkit-box;
  534 + -webkit-line-clamp: 2;
  535 + -webkit-box-orient: vertical;
  536 + overflow: hidden;
  537 + }
577 538
578 - .video-info {  
579 - display: flex;  
580 - justify-content: space-between;  
581 - align-items: center;  
582 - font-size: 20rpx;  
583 - color: #666;  
584 - } 539 + .video-info {
  540 + display: flex;
  541 + justify-content: space-between;
  542 + align-items: center;
  543 + font-size: 20rpx;
  544 + color: #666;
585 } 545 }
586 } 546 }
  547 + }
587 548
588 - .load-more {  
589 - text-align: center;  
590 - padding: 20rpx;  
591 - font-size: 24rpx;  
592 - color: #999;  
593 - } 549 + .load-more {
  550 + text-align: center;
  551 + padding: 20rpx;
  552 + font-size: 24rpx;
  553 + color: #999;
594 } 554 }
  555 + }
595 556
596 - .coach-section {  
597 - border-radius: 12rpx; 557 + .coach-section {
  558 + border-radius: 12rpx;
  559 + box-sizing: border-box;
  560 + padding: 20rpx 0;
  561 + // .section-header {
  562 + // display: flex;
  563 + // justify-content: space-between;
  564 + // align-items: center;
  565 + // padding: 20rpx 30rpx;
  566 +
  567 + // .section-title {
  568 + // font-size: 28rpx;
  569 + // font-weight: bold;
  570 + // color: #333;
  571 + // }
  572 +
  573 + // .more-btn {
  574 + // font-size: 24rpx;
  575 + // color: #666;
  576 + // display: flex;
  577 + // align-items: center;
  578 + // }
  579 + // }
  580 +
  581 + .coach-list {
  582 + display: grid;
  583 + grid-template-columns: repeat(2, 1fr);
  584 + grid-gap: 20rpx;
598 box-sizing: border-box; 585 box-sizing: border-box;
599 - padding: 20rpx 0;  
600 - // .section-header {  
601 - // display: flex;  
602 - // justify-content: space-between;  
603 - // align-items: center;  
604 - // padding: 20rpx 30rpx;  
605 -  
606 - // .section-title {  
607 - // font-size: 28rpx;  
608 - // font-weight: bold;  
609 - // color: #333;  
610 - // }  
611 -  
612 - // .more-btn {  
613 - // font-size: 24rpx;  
614 - // color: #666;  
615 - // display: flex;  
616 - // align-items: center;  
617 - // }  
618 - // }  
619 -  
620 - .coach-list {  
621 - display: grid;  
622 - grid-template-columns: repeat(2, 1fr);  
623 - grid-gap: 20rpx;  
624 - box-sizing: border-box;  
625 586
626 - .coach-item { 587 + .coach-item {
  588 + width: 100%;
  589 + margin-bottom: 20rpx;
  590 + border-radius: 8rpx;
  591 + overflow: hidden;
  592 + background-color: #fff;
  593 + position: relative;
  594 +
  595 + .video {
627 width: 100%; 596 width: 100%;
628 - margin-bottom: 20rpx;  
629 - border-radius: 8rpx;  
630 - overflow: hidden;  
631 - background-color: #fff;  
632 - position: relative; 597 + min-height: 200rpx;
  598 + }
633 599
634 - .video {  
635 - width: 100%;  
636 - min-height: 200rpx;  
637 - } 600 + .icon {
  601 + position: absolute;
  602 + top: 10rpx;
  603 + right: 10rpx;
  604 + }
638 605
639 - .icon {  
640 - position: absolute;  
641 - top: 10rpx;  
642 - right: 10rpx;  
643 - } 606 + .coach-cover {
  607 + width: 100%;
  608 + height: auto;
  609 + }
  610 +
  611 + .coach-desc {
  612 + padding: 10rpx;
644 613
645 - .coach-cover {  
646 - width: 100%;  
647 - height: auto; 614 + .coach-content {
  615 + font-size: 24rpx;
  616 + color: #333;
  617 + margin-bottom: 10rpx;
  618 + display: block;
648 } 619 }
649 620
650 - .coach-desc {  
651 - padding: 10rpx; 621 + .coach-meta {
  622 + display: flex;
  623 + align-items: center;
652 624
653 - .coach-content {  
654 - font-size: 24rpx;  
655 - color: #333;  
656 - margin-bottom: 10rpx;  
657 - display: block; 625 + .coach-avatar {
  626 + width: 40rpx;
  627 + height: 40rpx;
  628 + border-radius: 50%;
  629 + margin-right: 10rpx;
  630 + }
  631 +
  632 + .coach-name {
  633 + font-size: 22rpx;
  634 + color: #666;
  635 + flex: 1;
658 } 636 }
659 637
660 - .coach-meta {  
661 - display: flex;  
662 - align-items: center;  
663 -  
664 - .coach-avatar {  
665 - width: 40rpx;  
666 - height: 40rpx;  
667 - border-radius: 50%;  
668 - margin-right: 10rpx;  
669 - }  
670 -  
671 - .coach-name {  
672 - font-size: 22rpx;  
673 - color: #666;  
674 - flex: 1;  
675 - }  
676 -  
677 - .coach-like {  
678 - color: #ff6b35;  
679 - } 638 + .coach-like {
  639 + color: #ff6b35;
680 } 640 }
681 } 641 }
682 } 642 }
683 } 643 }
684 } 644 }
685 } 645 }
  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 ||  
45 - 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png'  
46 - "  
47 - mode="aspectFill"  
48 - ></image> 29 + <image class="avatar" :src="userInfo.avatar ||
  30 + 'https://fitness-hcxtec-bucket.oss-cn-shenzhen.aliyuncs.com/20260526/默认头像_1779779926983.png'
  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,277 +77,283 @@ @@ -96,277 +77,283 @@
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(() => {  
134 - if (drawer.value) {  
135 - drawer.value.close();  
136 - }  
137 - });  
138 -  
139 - onLoad((options) => {  
140 - if (!userStore.isLogin) {  
141 - uni.redirectTo({  
142 - url: '/pages7/pages/index/login',  
143 - });  
144 - return;  
145 - }  
146 - // 确保解析路由参数的正确性  
147 - if (options && options.currentTab) {  
148 - currentTab.value = Number(options.currentTab);  
149 - }  
150 - });  
151 -  
152 - // --- 原有业务方法补充与修复 ---  
153 -  
154 - // 打开抽屉  
155 - const openDrawer = () => {  
156 - if (drawer.value) {  
157 - drawer.value.open();  
158 - }  
159 - };  
160 -  
161 - // 修复模板直接调用 uni.navigateTo 的多端兼容问题  
162 - const navigateTo = (url) => {  
163 - uni.navigateTo({ url });  
164 - };  
165 -  
166 - // 处理标签点击  
167 - const handleTabClick = (index) => {  
168 - currentTab.value = index;  
169 - // #ifdef H5  
170 - switch (index) {  
171 - case 0:  
172 - currentComponent.value = xunjiXunji;  
173 - break;  
174 - case 1:  
175 - currentComponent.value = xunjiXunlianjihua;  
176 - break;  
177 - case 2:  
178 - currentComponent.value = xunjiRili;  
179 - break;  
180 - case 3:  
181 - currentComponent.value = xunjiDongzuo;  
182 - break;  
183 - case 4:  
184 - currentComponent.value = xunjiMoban;  
185 - break;  
186 - case 5:  
187 - currentComponent.value = xunjiShuju;  
188 - break;  
189 - default:  
190 - currentComponent.value = xunjiXunlianjihua;  
191 - }  
192 - // #endif  
193 - };  
194 -  
195 - // 空实现占位(防止模板点击报错,保持你原有的未给出的方法定义)  
196 - const goWidgetLib = () => {  
197 - uni.navigateTo({ url: '/pages4/pages/xunji/widget' });  
198 - };  
199 - const goFeedback = () => {};  
200 - const goUserGroup = () => {};  
201 - const goTutorial = () => {};  
202 -</script>  
203 -<style scoped lang="scss">  
204 - .home-page {  
205 - width: 100%;  
206 - height: 100vh;  
207 - // #ifdef H5  
208 - padding-bottom: 90rpx;  
209 - // #endif  
210 - background-color: #f5f5f5;  
211 - box-sizing: border-box;  
212 - overflow: hidden;  
213 - display: flex;  
214 - flex-direction: column;  
215 -  
216 - .content {  
217 - flex: 1;  
218 - box-sizing: border-box;  
219 - overflow: hidden;  
220 - padding-top: 70rpx;  
221 - // #ifdef MP-WEIXIN  
222 - padding-top: 200rpx;  
223 - // #endif  
224 - } 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(() => {
  121 + if (drawer.value) {
  122 + drawer.value.close();
225 } 123 }
226 -  
227 - .tab-bar {  
228 - display: flex;  
229 - align-items: flex-end;  
230 - justify-content: space-between;  
231 - padding: 0px 20px;  
232 - padding-top: 20rpx;  
233 - background-color: #fff;  
234 -  
235 - position: fixed;  
236 - top: 0;  
237 - left: 0;  
238 - right: 0;  
239 - z-index: 999;  
240 - box-sizing: border-box; 124 +});
  125 +
  126 +onLoad((options) => {
  127 + if (!userStore.isLogin) {
  128 + uni.redirectTo({
  129 + url: '/pages7/pages/index/login',
  130 + });
  131 + return;
241 } 132 }
242 -  
243 - /* 标签项:默认样式 */  
244 - .tab-item {  
245 - font-size: 16px;  
246 - color: #666;  
247 - padding: 5px 0;  
248 - padding-top: 30rpx; 133 + // 确保解析路由参数的正确性
  134 + if (options && options.currentTab) {
  135 + currentTab.value = Number(options.currentTab);
249 } 136 }
  137 +});
250 138
251 - /* 激活态标签:下划线+深色文字 */  
252 - .tab-item.active {  
253 - color: #333;  
254 - border-bottom: 2px solid #000;  
255 - font-weight: 500;  
256 - } 139 +// --- 原有业务方法补充与修复 ---
257 140
258 - /* 菜单按钮:样式 */  
259 - .menu-btn {  
260 - font-size: 20px;  
261 - color: #666; 141 +// 打开抽屉
  142 +const openDrawer = () => {
  143 + if (drawer.value) {
  144 + drawer.value.open();
262 } 145 }
263 -  
264 - .card-subtitle {  
265 - font-size: 24rpx;  
266 - color: #666;  
267 - margin-bottom: 10rpx; 146 +};
  147 +
  148 +// 修复模板直接调用 uni.navigateTo 的多端兼容问题
  149 +const navigateTo = (url) => {
  150 + uni.navigateTo({ url });
  151 +};
  152 +
  153 +// 处理标签点击
  154 +const handleTabClick = (index) => {
  155 + currentTab.value = index;
  156 + // #ifdef H5
  157 + switch (index) {
  158 + case 0:
  159 + currentComponent.value = xunjiXunji;
  160 + break;
  161 + case 1:
  162 + currentComponent.value = xunjiXunlianjihua;
  163 + break;
  164 + case 2:
  165 + currentComponent.value = xunjiRili;
  166 + break;
  167 + case 3:
  168 + currentComponent.value = xunjiDongzuo;
  169 + break;
  170 + case 4:
  171 + currentComponent.value = xunjiMoban;
  172 + break;
  173 + case 5:
  174 + currentComponent.value = xunjiShuju;
  175 + break;
  176 + default:
  177 + currentComponent.value = xunjiXunlianjihua;
268 } 178 }
269 -  
270 - .plan-btn {  
271 - width: 100%;  
272 - height: 50rpx;  
273 - background-color: #2eaa8a;  
274 - color: white;  
275 - font-size: 24rpx;  
276 - font-weight: bold;  
277 - border-radius: 25rpx;  
278 - line-height: 50rpx;  
279 - text-align: center;  
280 - margin-top: 10rpx;  
281 - }  
282 -  
283 - .card-value {  
284 - font-size: 36rpx;  
285 - color: #333;  
286 - margin-bottom: 10rpx;  
287 - }  
288 -  
289 - .card-desc {  
290 - font-size: 24rpx;  
291 - color: #666;  
292 - }  
293 -  
294 - /* 抽屉样式 */  
295 - .drawer-content {  
296 - background-color: #ffffff;  
297 - height: 100%;  
298 - padding: 20rpx;  
299 - box-sizing: border-box;  
300 - }  
301 -  
302 - /* 用户头像和信息区域 */  
303 - .user-header {  
304 - display: flex;  
305 - align-items: center;  
306 - padding: 20rpx 0;  
307 - border-bottom: 1px solid #f0f0f0;  
308 - }  
309 -  
310 - .user-header .avatar {  
311 - width: 80rpx;  
312 - height: 80rpx;  
313 - border-radius: 50%;  
314 - margin-right: 20rpx;  
315 - margin-top: 0;  
316 - justify-content: flex-start;  
317 - }  
318 -  
319 - .user-info {  
320 - display: flex;  
321 - flex-direction: column;  
322 - }  
323 -  
324 - .nickname {  
325 - font-size: 28rpx;  
326 - font-weight: 500;  
327 - color: #333333;  
328 - margin-bottom: 8rpx;  
329 - }  
330 -  
331 - .user-id {  
332 - font-size: 24rpx;  
333 - color: #999999;  
334 - }  
335 -  
336 - /* 广告横幅 */  
337 - .ad-banner {  
338 - width: 100%;  
339 - height: 200rpx;  
340 - margin: 20rpx 0;  
341 - border-radius: 12rpx;  
342 - }  
343 -  
344 - /* 菜单列表 */  
345 - .menu-list {  
346 - margin-top: 20rpx;  
347 - }  
348 -  
349 - .menu-item {  
350 - display: flex;  
351 - align-items: center;  
352 - justify-content: space-between;  
353 - padding: 30rpx 0;  
354 - border-bottom: 1px solid #f0f0f0;  
355 - }  
356 -  
357 - .menu-item:last-child {  
358 - border-bottom: none;  
359 - }  
360 -  
361 - .menu-text {  
362 - font-size: 28rpx;  
363 - color: #333333;  
364 - margin-left: 20rpx; 179 + // #endif
  180 +};
  181 +
  182 +// 空实现占位(防止模板点击报错,保持你原有的未给出的方法定义)
  183 +const goWidgetLib = () => {
  184 + uni.navigateTo({ url: '/pages4/pages/xunji/widget' });
  185 +};
  186 +const goFeedback = () => { };
  187 +const goUserGroup = () => { };
  188 +const goTutorial = () => { };
  189 +</script>
  190 +<style scoped lang="scss">
  191 +.home-page {
  192 + width: 100%;
  193 + height: 100vh;
  194 + // #ifdef H5
  195 + padding-bottom: 90rpx;
  196 + // #endif
  197 + background-color: #f5f5f5;
  198 + box-sizing: border-box;
  199 + overflow: hidden;
  200 + display: flex;
  201 + flex-direction: column;
  202 +
  203 + .content {
365 flex: 1; 204 flex: 1;
  205 + box-sizing: border-box;
  206 + overflow: hidden;
  207 + padding-top: 70rpx;
  208 + // #ifdef MP-WEIXIN
  209 + padding-top: 200rpx;
  210 + // #endif
366 } 211 }
367 -  
368 - /* 覆盖uni-icons默认颜色 */  
369 - :deep(.uni-icons) {  
370 - color: #666666;  
371 - } 212 +}
  213 +
  214 +.tab-bar {
  215 + display: flex;
  216 + align-items: flex-end;
  217 + justify-content: space-between;
  218 + padding: 0px 20px;
  219 + padding-top: 20rpx;
  220 + background-color: #fff;
  221 +
  222 + position: fixed;
  223 + top: 0;
  224 + left: 0;
  225 + right: 0;
  226 + z-index: 999;
  227 + box-sizing: border-box;
  228 +}
  229 +
  230 +/* 标签项:默认样式 */
  231 +.tab-item {
  232 + font-size: 16px;
  233 + color: #666;
  234 + padding: 5px 0;
  235 + padding-top: 30rpx;
  236 +}
  237 +
  238 +/* 激活态标签:下划线+深色文字 */
  239 +.tab-item.active {
  240 + color: #333;
  241 + border-bottom: 2px solid #000;
  242 + font-weight: 500;
  243 +}
  244 +
  245 +/* 菜单按钮:样式 */
  246 +.menu-btn {
  247 + font-size: 20px;
  248 + color: #666;
  249 +}
  250 +
  251 +.card-subtitle {
  252 + font-size: 24rpx;
  253 + color: #666;
  254 + margin-bottom: 10rpx;
  255 +}
  256 +
  257 +.plan-btn {
  258 + width: 100%;
  259 + height: 50rpx;
  260 + background-color: #2eaa8a;
  261 + color: white;
  262 + font-size: 24rpx;
  263 + font-weight: bold;
  264 + border-radius: 25rpx;
  265 + line-height: 50rpx;
  266 + text-align: center;
  267 + margin-top: 10rpx;
  268 +}
  269 +
  270 +.card-value {
  271 + font-size: 36rpx;
  272 + color: #333;
  273 + margin-bottom: 10rpx;
  274 +}
  275 +
  276 +.card-desc {
  277 + font-size: 24rpx;
  278 + color: #666;
  279 +}
  280 +
  281 +/* 抽屉样式 */
  282 +.drawer-content {
  283 + background-color: #ffffff;
  284 + height: 100%;
  285 + padding: 20rpx;
  286 + box-sizing: border-box;
  287 +}
  288 +
  289 +/* 用户头像和信息区域 */
  290 +.user-header {
  291 + display: flex;
  292 + align-items: center;
  293 + padding: 20rpx 0;
  294 + border-bottom: 1px solid #f0f0f0;
  295 +}
  296 +
  297 +.user-header .avatar {
  298 + width: 80rpx;
  299 + height: 80rpx;
  300 + border-radius: 50%;
  301 + margin-right: 20rpx;
  302 + margin-top: 0;
  303 + justify-content: flex-start;
  304 +}
  305 +
  306 +.user-info {
  307 + display: flex;
  308 + flex-direction: column;
  309 +}
  310 +
  311 +.nickname {
  312 + font-size: 28rpx;
  313 + font-weight: 500;
  314 + color: #333333;
  315 + margin-bottom: 8rpx;
  316 +}
  317 +
  318 +.user-id {
  319 + font-size: 24rpx;
  320 + color: #999999;
  321 +}
  322 +
  323 +/* 广告横幅 */
  324 +.ad-banner {
  325 + width: 100%;
  326 + height: 200rpx;
  327 + margin: 20rpx 0;
  328 + border-radius: 12rpx;
  329 +}
  330 +
  331 +/* 菜单列表 */
  332 +.menu-list {
  333 + margin-top: 20rpx;
  334 +}
  335 +
  336 +.menu-item {
  337 + display: flex;
  338 + align-items: center;
  339 + justify-content: space-between;
  340 + padding: 30rpx 0;
  341 + border-bottom: 1px solid #f0f0f0;
  342 +}
  343 +
  344 +.menu-item:last-child {
  345 + border-bottom: none;
  346 +}
  347 +
  348 +.menu-text {
  349 + font-size: 28rpx;
  350 + color: #333333;
  351 + margin-left: 20rpx;
  352 + flex: 1;
  353 +}
  354 +
  355 +/* 覆盖uni-icons默认颜色 */
  356 +:deep(.uni-icons) {
  357 + color: #666666;
  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: ["背阔肌"]  
277 - }  
278 - ]; 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);
  247 + }
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 - <!-- 去训练弹窗 -->  
188 - <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false"  
189 - :plan-title="'选择课程立即开始训练'" :plan-id="plandetail.id" /> 187 + <!-- 去训练弹窗,显示当前计划的所有模板,可以直接跳转到训练页面 -->
  188 + <template v-if="plandetail?.id">
  189 + <WodeJihuaTianjiaTancuang v-if="showPlanPopup" v-model:visible="showPlanPopup" :isAdd="false"
  190 + :plan-title="'选择课程立即开始训练'" :plan-id="plandetail.id" :is-my-plan="false"/>
  191 + </template>
  192 +
190 </view> 193 </view>
191 </template> 194 </template>
192 195
1 import request from '@/sheep/request'; 1 import request from '@/sheep/request';
2 2
3 const dailytemplateApi = { 3 const dailytemplateApi = {
4 - //获取每日模板  
5 - getdailytemplate:(date)=>{  
6 - return request({  
7 - url:'/app/daily/template/DailyTemplates',  
8 - method:'GET',  
9 - params:{date}  
10 - });  
11 - } 4 + //获取每日模板
  5 + getdailytemplate: (date) => {
  6 + return request({
  7 + url: '/app/daily/template/DailyTemplates',
  8 + method: 'GET',
  9 + params: { date },
  10 + });
  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;
@@ -2,49 +2,193 @@ import request from '@/sheep/request'; @@ -2,49 +2,193 @@ import request from '@/sheep/request';
2 2
3 //模板API 3 //模板API
4 const TemplatesApi = { 4 const TemplatesApi = {
5 - selecttemplates: (id) => {  
6 - return request({  
7 - url: `/app/templates/selectByGroup/${id}`,  
8 - method: 'GET',  
9 - });  
10 - },  
11 - getTemplates: () => {  
12 - return request({  
13 - url: '/app/templates/selectGroup',  
14 - method: 'GET',  
15 - });  
16 - },  
17 -  
18 - //获得模板详情  
19 - getTemplateDetail: (id)=>{  
20 - return request({  
21 - url:'/app/templates/getTemplateDetail',  
22 - method:'GET',  
23 - params:{id}  
24 - });  
25 - },  
26 - // 获得所有的部位  
27 - getPartAllCategories: ()=> {  
28 - return request({  
29 - url:'/app/motion/categories/sub/get',  
30 - method:'GET',  
31 - })  
32 - },  
33 - // 根据场景和部位筛选模板  
34 - queryTemplatesByCondition: (params) => {  
35 - return request({  
36 - url: '/app/templates/queryTemplatesByCondition',  
37 - method: 'GET',  
38 - params: params //  
39 - });  
40 - },  
41 - //获得细分锻炼部位列表  
42 - getSubCategoriesList: () => {  
43 - return request({  
44 - url: '/app/motion/exercises/sub-categories',  
45 - method: 'GET',  
46 - });  
47 - } 5 + selecttemplates: (id) => {
  6 + return request({
  7 + url: `/app/templates/selectByGroup/${id}`,
  8 + method: 'GET',
  9 + });
  10 + },
  11 + // 获得模板大类列表(官方)
  12 + getTemplates: () => {
  13 + return request({
  14 + url: '/app/templates/selectGroup',
  15 + method: 'GET',
  16 + });
  17 + },
  18 +
  19 + // 获得个人模板列表
  20 + individualTemplates: () => {
  21 + return request({
  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 },
  58 + });
  59 + },
  60 + // 获得所有的部位
  61 + getPartAllCategories: () => {
  62 + return request({
  63 + url: '/app/motion/categories/sub/get',
  64 + method: 'GET',
  65 + });
  66 + },
  67 + // 根据场景和部位筛选模板
  68 + queryTemplatesByCondition: (params) => {
  69 + return request({
  70 + url: '/app/templates/queryTemplatesByCondition',
  71 + method: 'GET',
  72 + params: params, //
  73 + });
  74 + },
  75 + //获得细分锻炼部位列表
  76 + getSubCategoriesList: () => {
  77 + return request({
  78 + url: '/app/motion/exercises/sub-categories',
  79 + method: 'GET',
  80 + });
  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 = {
5 - getPlanList: (categoryId) => {  
6 - return request({  
7 - url: '/app/plan/queryPlan',  
8 - method: 'GET', // 匹配文档的GET请求方式  
9 - params: {  
10 - categoryId  
11 - }, // 传递query参数(可选)  
12 - });  
13 - },  
14 - getCategories: () => {  
15 - return request({  
16 - url: '/app/plan/getCategories',  
17 - method: 'GET',  
18 - });  
19 - },  
20 - // 获取计划详情  
21 - getPlanDetail: (id) => {  
22 - return request({  
23 - url: '/app/plan/getPlanDetail',  
24 - method: 'GET',  
25 - params: {  
26 - id  
27 - },  
28 - });  
29 - },  
30 - // 根据筛选条件获得当前计划分类下的计划列表  
31 - getFilteredPlanList: (params) => {  
32 - return request({  
33 - url: '/app/plan/queryPlan',  
34 - method: 'GET',  
35 - params: params, // 直接透传所有筛选参数,支持任意组合  
36 - });  
37 - },  
38 - // 根据计划名字模糊查询计划列表  
39 - searchPlansByName: (name) => {  
40 - return request({  
41 - url: '/app/plan/searchByName',  
42 - method: 'GET',  
43 - params: { name },  
44 - });  
45 - }, 6 + getPlanList: (categoryId) => {
  7 + return request({
  8 + url: '/app/plan/queryPlan',
  9 + method: 'GET', // 匹配文档的GET请求方式
  10 + params: {
  11 + categoryId,
  12 + }, // 传递query参数(可选)
  13 + });
  14 + },
  15 + getCategories: () => {
  16 + return request({
  17 + url: '/app/plan/getCategories',
  18 + method: 'GET',
  19 + });
  20 + },
  21 + // 获取计划详情
  22 + getPlanDetail: (id) => {
  23 + return request({
  24 + url: '/app/plan/getPlanDetail',
  25 + method: 'GET',
  26 + params: {
  27 + id,
  28 + },
  29 + });
  30 + },
46 31
47 - // 安排计划  
48 - arrangePlan: (data) => {  
49 - return request({  
50 - url: '/app/plan/arrangePlan',  
51 - method: 'POST',  
52 - data  
53 - });  
54 - },  
55 - // 添加计划  
56 - addPlan: (id) => {  
57 - return request({  
58 - url:`/app/plan/addPlan`,  
59 - method: 'GET',  
60 - params: {  
61 - id  
62 - }  
63 - })  
64 - },  
65 - // 是否添加计划  
66 - isAddPlan: (id) => {  
67 - return request({  
68 - url:`/app/plan/isAdd`,  
69 - method: 'GET',  
70 - params: {  
71 - id  
72 - }  
73 - })  
74 - },  
75 - // 添加计划到日历  
76 - addPlanToCalendar: (data) => {  
77 - return request ({  
78 - url: `/app/daily/template`,  
79 - method: 'POST',  
80 - data  
81 - })  
82 - } 32 + // 获取我的计划详情(含课程)
  33 + getMyPlanDetail: (id) => {
  34 + return request({
  35 + url: '/app/plan/getmyPlanDetail',
  36 + method: 'GET',
  37 + params: {
  38 + id,
  39 + },
  40 + });
  41 + },
83 42
84 -  
85 -} 43 + // 根据每日模板ID获取模板详情
  44 + getDailyTemplateDetail: (id) => {
  45 + return request({
  46 + url: '/app/plan/getDailyTemplateDetail',
  47 + method: 'GET',
  48 + params: {
  49 + id,
  50 + },
  51 + });
  52 + },
86 53
87 -export default QueryPlanApi;  
  54 + // 根据筛选条件获得当前计划分类下的计划列表
  55 + getFilteredPlanList: (params) => {
  56 + return request({
  57 + url: '/app/plan/queryPlan',
  58 + method: 'GET',
  59 + params: params, // 直接透传所有筛选参数,支持任意组合
  60 + });
  61 + },
  62 + // 根据计划名字模糊查询计划列表
  63 + searchPlansByName: (name) => {
  64 + return request({
  65 + url: '/app/plan/searchByName',
  66 + method: 'GET',
  67 + params: { name },
  68 + });
  69 + },
  70 +
  71 + // 安排计划
  72 + arrangePlan: (data) => {
  73 + return request({
  74 + url: '/app/plan/arrangePlan',
  75 + method: 'POST',
  76 + data,
  77 + });
  78 + },
  79 + // 重新开始计划
  80 + reStartPlan: (data) => {
  81 + return request({
  82 + url: '/app/plan/rearrangePlan',
  83 + method: 'POST',
  84 + data,
  85 + });
  86 + },
  87 + // 获得本周计划
  88 + getWeekPlan: (id) => {
  89 + return request({
  90 + url: `/app/plan/getWeekPlan`,
  91 + method: 'GET',
  92 + params: {
  93 + id,
  94 + },
  95 + });
  96 + },
  97 +
  98 + // 添加计划
  99 + addPlan: (id) => {
  100 + return request({
  101 + url: `/app/plan/addPlan`,
  102 + method: 'GET',
  103 + params: {
  104 + id,
  105 + },
  106 + });
  107 + },
  108 + // 是否添加计划
  109 + isAddPlan: (id) => {
  110 + return request({
  111 + url: `/app/plan/isAdd`,
  112 + method: 'GET',
  113 + params: {
  114 + id,
  115 + },
  116 + });
  117 + },
  118 + // 添加计划到日历
  119 + addPlanToCalendar: (data) => {
  120 + return request({
  121 + url: `/app/daily/template`,
  122 + method: 'POST',
  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 + },
  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 + },
  173 +
  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 +};
  190 +
  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 },