1:修复登录没有客户端问题
2:修复取消模板确认框在明细后面问题 3:修复开始训练打勾问题
Showing
8 changed files
with
198 additions
and
23 deletions
| @@ -8,7 +8,7 @@ SHOPRO_VERSION=v2.4.1 | @@ -8,7 +8,7 @@ SHOPRO_VERSION=v2.4.1 | ||
| 8 | # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) | 8 | # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) |
| 9 | SHOPRO_DEV_BASE_URL=http://192.168.1.200:48081 | 9 | SHOPRO_DEV_BASE_URL=http://192.168.1.200:48081 |
| 10 | # SHOPRO_DEV_BASE_URL=http://192.168.1.85:48080 | 10 | # SHOPRO_DEV_BASE_URL=http://192.168.1.85:48080 |
| 11 | - SHOPRO_DEV_BASE_URL=https://xunji.geaktec.com | 11 | + #SHOPRO_DEV_BASE_URL=https://xunji.geaktec.com |
| 12 | # SHOPRO_DEV_BASE_URL=http://api-dashboard.yudao.iocoder.cn/ | 12 | # SHOPRO_DEV_BASE_URL=http://api-dashboard.yudao.iocoder.cn/ |
| 13 | ### SHOPRO_DEV_BASE_URL=http://10.171.1.188:48080 | 13 | ### SHOPRO_DEV_BASE_URL=http://10.171.1.188:48080 |
| 14 | ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc | 14 | ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc |
| @@ -29,15 +29,21 @@ | @@ -29,15 +29,21 @@ | ||
| 29 | </view> | 29 | </view> |
| 30 | 30 | ||
| 31 | <template v-if="resdailyData.length > 0"> | 31 | <template v-if="resdailyData.length > 0"> |
| 32 | + <!-- Tab 按钮:如���有多个训记,用来源名作为标签 --> | ||
| 32 | <scroll-view class="plan-tabs" scroll-x v-if="resdailyData.length > 1"> | 33 | <scroll-view class="plan-tabs" scroll-x v-if="resdailyData.length > 1"> |
| 33 | <button class="tab-btn" :class="{ active: currentPlanIndex === index }" v-for="(item, index) in resdailyData" | 34 | <button class="tab-btn" :class="{ active: currentPlanIndex === index }" v-for="(item, index) in resdailyData" |
| 34 | :key="index" @click.stop="switchPlan(index)"> | 35 | :key="index" @click.stop="switchPlan(index)"> |
| 35 | - 训记{{ index + 1 }} | 36 | + {{ item.sourceType === 1 || item.sourceType === 2 ? (item.sourceName || item.name) : (item.name || '训记' + (index + 1)) }} |
| 36 | </button> | 37 | </button> |
| 37 | </scroll-view> | 38 | </scroll-view> |
| 38 | - <!-- 接口获取当天的多个训练计划 → 放进 resdailyData,currentPlan 自动变成 resdailyData[0] 一个模板--> | ||
| 39 | <!-- 训练内容区域 --> | 39 | <!-- 训练内容区域 --> |
| 40 | <view v-if="resdailyData.length > 0 && currentPlan" class="training-container"> | 40 | <view v-if="resdailyData.length > 0 && currentPlan" class="training-container"> |
| 41 | + <!-- 训练来源标签 --> | ||
| 42 | + <view class="source-tag-row" :class="getSourceClass(currentPlan.sourceType || 4)"> | ||
| 43 | + <text class="source-text"> | ||
| 44 | + {{ currentPlan.sourceType === 1 || currentPlan.sourceType === 2 ? (currentPlan.sourceName || currentPlan.name) : getSourceLabel(currentPlan.sourceType || 4) }} | ||
| 45 | + </text> | ||
| 46 | + </view> | ||
| 41 | <view class="unitCart"> | 47 | <view class="unitCart"> |
| 42 | <!-- 训练计划头部卡片 --> | 48 | <!-- 训练计划头部卡片 --> |
| 43 | <view class="plan-header-card"> | 49 | <view class="plan-header-card"> |
| @@ -416,6 +422,67 @@ const currentPlan = computed(() => { | @@ -416,6 +422,67 @@ const currentPlan = computed(() => { | ||
| 416 | return resdailyData.value[currentPlanIndex.value] || null; | 422 | return resdailyData.value[currentPlanIndex.value] || null; |
| 417 | }); | 423 | }); |
| 418 | 424 | ||
| 425 | +// ========= 训练来源分组展示 ========= | ||
| 426 | + | ||
| 427 | +/** sourceType → 中文标签 */ | ||
| 428 | +const getSourceLabel = (type) => { | ||
| 429 | + const labels = { | ||
| 430 | + 1: '来自计划', | ||
| 431 | + 2: '来自模板', | ||
| 432 | + 3: '自定义训练', | ||
| 433 | + 4: '自由训练', | ||
| 434 | + }; | ||
| 435 | + return labels[type] || '训练'; | ||
| 436 | +}; | ||
| 437 | + | ||
| 438 | +/** sourceType → 风格 class */ | ||
| 439 | +const getSourceClass = (type) => { | ||
| 440 | + const classes = { | ||
| 441 | + 1: 'source-plan', // 计划 | ||
| 442 | + 2: 'source-tpl', // 模板 | ||
| 443 | + 3: 'source-custom', // 自定义 | ||
| 444 | + 4: 'source-free', // 自由 | ||
| 445 | + }; | ||
| 446 | + return classes[type] || 'source-free'; | ||
| 447 | +}; | ||
| 448 | + | ||
| 449 | +/** 按 sourceType 分组后的模板列表,供 tab 切换使用 */ | ||
| 450 | +const sourceGrouped = computed(() => { | ||
| 451 | + const list = resdailyData.value || []; | ||
| 452 | + const map = new Map(); | ||
| 453 | + list.forEach((item) => { | ||
| 454 | + const key = item.sourceType ? `s${item.sourceType}_${item.sourceName || ''}` : `t_${item.dailyTemplateId}`; | ||
| 455 | + if (!map.has(key)) { | ||
| 456 | + const st = item.sourceType || 4; | ||
| 457 | + map.set(key, { | ||
| 458 | + sourceType: st, | ||
| 459 | + sourceName: item.sourceName || item.name, | ||
| 460 | + sourceLabel: item.sourceType === 1 || item.sourceType === 2 ? item.sourceName : getSourceLabel(st), | ||
| 461 | + items: [], | ||
| 462 | + }); | ||
| 463 | + } | ||
| 464 | + map.get(key).items.push(item); | ||
| 465 | + }); | ||
| 466 | + return Array.from(map.values()); | ||
| 467 | +}); | ||
| 468 | + | ||
| 469 | +/** 扁平化的模板列表(带 source 信息),用于直接渲染 */ | ||
| 470 | +const flatPlanList = computed(() => { | ||
| 471 | + const result = []; | ||
| 472 | + sourceGrouped.value.forEach((group) => { | ||
| 473 | + group.items.forEach((item, idx) => { | ||
| 474 | + result.push({ | ||
| 475 | + ...item, | ||
| 476 | + sourceType: group.sourceType, | ||
| 477 | + sourceName: group.sourceName, | ||
| 478 | + sourceLabel: group.sourceLabel, | ||
| 479 | + isFirstInGroup: idx === 0, | ||
| 480 | + }); | ||
| 481 | + }); | ||
| 482 | + }); | ||
| 483 | + return result; | ||
| 484 | +}); | ||
| 485 | + | ||
| 419 | const loaddailytemplate = async () => { | 486 | const loaddailytemplate = async () => { |
| 420 | if (!selectedDate.value) return; | 487 | if (!selectedDate.value) return; |
| 421 | console.log('【子组件】开始加载数据...'); | 488 | console.log('【子组件】开始加载数据...'); |
| @@ -980,6 +1047,34 @@ onMounted(() => { | @@ -980,6 +1047,34 @@ onMounted(() => { | ||
| 980 | font-weight: bold; | 1047 | font-weight: bold; |
| 981 | } | 1048 | } |
| 982 | 1049 | ||
| 1050 | +/* 训练来源标签 */ | ||
| 1051 | +.source-tag-row { | ||
| 1052 | + display: flex; | ||
| 1053 | + align-items: center; | ||
| 1054 | + padding: 16rpx 24rpx; | ||
| 1055 | + margin: 0rpx 20rpx; | ||
| 1056 | + border-radius: 12rpx; | ||
| 1057 | + font-size: 24rpx; | ||
| 1058 | + font-weight: 500; | ||
| 1059 | + | ||
| 1060 | + .source-text { | ||
| 1061 | + color: #fff; | ||
| 1062 | + } | ||
| 1063 | + | ||
| 1064 | + &.source-plan { | ||
| 1065 | + background: #3b82f6; /* 蓝色 - 计划 */ | ||
| 1066 | + } | ||
| 1067 | + &.source-tpl { | ||
| 1068 | + background: #8b5cf6; /* 紫色 - 模板 */ | ||
| 1069 | + } | ||
| 1070 | + &.source-custom { | ||
| 1071 | + background: #f59e0b; /* 橙色 - 自定义 */ | ||
| 1072 | + } | ||
| 1073 | + &.source-free { | ||
| 1074 | + background: #10b981; /* 绿色 - 自由 */ | ||
| 1075 | + } | ||
| 1076 | +} | ||
| 1077 | + | ||
| 983 | /* 训练计划头部卡片 */ | 1078 | /* 训练计划头部卡片 */ |
| 984 | .plan-header-card { | 1079 | .plan-header-card { |
| 985 | display: flex; | 1080 | display: flex; |
| @@ -247,17 +247,32 @@ const loadPlans = async (monthStr) => { | @@ -247,17 +247,32 @@ const loadPlans = async (monthStr) => { | ||
| 247 | planMap[dateStr] = planMap[dateStr] || []; | 247 | planMap[dateStr] = planMap[dateStr] || []; |
| 248 | if (Array.isArray(plan.templates)) { | 248 | if (Array.isArray(plan.templates)) { |
| 249 | let totalWeightForDay = 0; | 249 | let totalWeightForDay = 0; |
| 250 | + // 按 sourceType + sourceName 分组 | ||
| 251 | + const sourceMap = new Map(); | ||
| 250 | plan.templates.forEach((template) => { | 252 | plan.templates.forEach((template) => { |
| 251 | - if (template && template.name) { | ||
| 252 | - planMap[dateStr].push({ | ||
| 253 | - name: template.name, | 253 | + if (!template || !template.name) return; |
| 254 | + const sourceKey = template.sourceType | ||
| 255 | + ? `src_${template.sourceType}_${template.sourceName || ''}` | ||
| 256 | + : `tpl_${template.dailyTemplateId}`; | ||
| 257 | + if (!sourceMap.has(sourceKey)) { | ||
| 258 | + sourceMap.set(sourceKey, { | ||
| 259 | + name: template.sourceName || template.name, | ||
| 254 | bg: template.backgroundColor || '#fef08a', | 260 | bg: template.backgroundColor || '#fef08a', |
| 261 | + sourceType: template.sourceType || 4, | ||
| 262 | + sourceName: template.sourceName || template.name, | ||
| 263 | + dailyTemplateId: template.dailyTemplateId, | ||
| 264 | + totalWeight: 0, | ||
| 255 | }); | 265 | }); |
| 256 | } | 266 | } |
| 267 | + const group = sourceMap.get(sourceKey); | ||
| 257 | if (template && typeof template.totalWeight === 'number') { | 268 | if (template && typeof template.totalWeight === 'number') { |
| 269 | + group.totalWeight += template.totalWeight; | ||
| 258 | totalWeightForDay += template.totalWeight; | 270 | totalWeightForDay += template.totalWeight; |
| 259 | } | 271 | } |
| 260 | }); | 272 | }); |
| 273 | + sourceMap.forEach((group) => { | ||
| 274 | + planMap[dateStr].push(group); | ||
| 275 | + }); | ||
| 261 | if (totalWeightForDay > 0) { | 276 | if (totalWeightForDay > 0) { |
| 262 | weightMap[dateStr] = totalWeightForDay; | 277 | weightMap[dateStr] = totalWeightForDay; |
| 263 | } | 278 | } |
| @@ -346,14 +346,14 @@ const chartSeries = computed(() => { | @@ -346,14 +346,14 @@ const chartSeries = computed(() => { | ||
| 346 | const dataKey = TREND_DATA_KEYS[NumberTabIndex.value] || 'volume'; | 346 | const dataKey = TREND_DATA_KEYS[NumberTabIndex.value] || 'volume'; |
| 347 | 347 | ||
| 348 | // 当前周期数据 | 348 | // 当前周期数据 |
| 349 | - const currData = fullDateKeys.map((dateStr) => currDataMap[dateStr]?.[dataKey] ?? 0); | 349 | + const currData = fullDateKeys.map((dateStr) => convertUnit(currDataMap[dateStr]?.[dataKey], dataKey)); |
| 350 | 350 | ||
| 351 | // 上一周期数据(按日期偏移匹配) | 351 | // 上一周期数据(按日期偏移匹配) |
| 352 | const lastData = fullDateKeys.map((dateStr) => { | 352 | const lastData = fullDateKeys.map((dateStr) => { |
| 353 | const targetKey = isWeekly | 353 | const targetKey = isWeekly |
| 354 | ? dayjs(dateStr).subtract(7, 'day').format('YYYY-MM-DD') | 354 | ? dayjs(dateStr).subtract(7, 'day').format('YYYY-MM-DD') |
| 355 | : dayjs(dateStr).subtract(1, 'month').format('YYYY-MM-DD'); | 355 | : dayjs(dateStr).subtract(1, 'month').format('YYYY-MM-DD'); |
| 356 | - return lastDataMap[targetKey]?.[dataKey] ?? 0; | 356 | + return convertUnit(lastDataMap[targetKey]?.[dataKey], dataKey); |
| 357 | }); | 357 | }); |
| 358 | 358 | ||
| 359 | const currName = isWeekly ? '本周' : '本月'; | 359 | const currName = isWeekly ? '本周' : '本月'; |
| @@ -373,25 +373,39 @@ const listData = computed(() => { | @@ -373,25 +373,39 @@ const listData = computed(() => { | ||
| 373 | 373 | ||
| 374 | const d = weeklyData.value; | 374 | const d = weeklyData.value; |
| 375 | const fields = [ | 375 | const fields = [ |
| 376 | - { label: '容量', last: d.lastWeekTotalVolume, curr: d.totalVolume }, | ||
| 377 | - { label: '组数', last: d.lastWeekTotalSets, curr: d.totalSets }, | ||
| 378 | - { label: '时长', last: d.lastWeekTotalDuration, curr: d.totalDuration }, | ||
| 379 | - { label: '距离', last: d.lastWeekTotalDistance, curr: d.totalDistance }, | ||
| 380 | - { label: '次数', last: d.lastWeekTotalCount, curr: d.totalCount }, | 376 | + { label: '容量', key: 'volume', last: d.lastWeekTotalVolume, curr: d.totalVolume }, |
| 377 | + { label: '组数', key: 'setCount', last: d.lastWeekTotalSets, curr: d.totalSets }, | ||
| 378 | + { label: '时长', key: 'duration', last: d.lastWeekTotalDuration, curr: d.totalDuration }, | ||
| 379 | + { label: '距离', key: 'distance', last: d.lastWeekTotalDistance, curr: d.totalDistance }, | ||
| 380 | + { label: '次数', key: 'count', last: d.lastWeekTotalCount, curr: d.totalCount }, | ||
| 381 | ]; | 381 | ]; |
| 382 | 382 | ||
| 383 | - return fields.map(({ label, last, curr }) => { | ||
| 384 | - const lastVal = last ?? 0; | ||
| 385 | - const currVal = curr ?? 0; | 383 | + return fields.map(({ label, key, last, curr }) => { |
| 384 | + const lastVal = convertUnit(last, key); | ||
| 385 | + const currVal = convertUnit(curr, key); | ||
| 386 | return { | 386 | return { |
| 387 | label, | 387 | label, |
| 388 | lastPeriod: lastVal, | 388 | lastPeriod: lastVal, |
| 389 | currPeriod: currVal, | 389 | currPeriod: currVal, |
| 390 | - diff: currVal - lastVal, | 390 | + diff: Number((currVal - lastVal).toFixed(1)), |
| 391 | }; | 391 | }; |
| 392 | }); | 392 | }); |
| 393 | }); | 393 | }); |
| 394 | 394 | ||
| 395 | +/** | ||
| 396 | + * 字段单位换算:API 返回秒/米等,UI 展示为分/km 等 | ||
| 397 | + * duration(秒)→ 分钟 | ||
| 398 | + * 其它字段(volume/setCount/distance/count)保持原值 | ||
| 399 | + */ | ||
| 400 | +const convertUnit = (val, key) => { | ||
| 401 | + const v = Number(val) || 0; | ||
| 402 | + if (key === 'duration') { | ||
| 403 | + // 秒 → 分钟,保留 1 位小数(如 90s → 1.5) | ||
| 404 | + return Math.round((v / 60) * 10) / 10; | ||
| 405 | + } | ||
| 406 | + return v; | ||
| 407 | +}; | ||
| 408 | + | ||
| 395 | /** 肌肉弹窗图表 X 轴 */ | 409 | /** 肌肉弹窗图表 X 轴 */ |
| 396 | const muscleChartCategories = computed(() => { | 410 | const muscleChartCategories = computed(() => { |
| 397 | if (MainCurrentDateType.value === 'week') { | 411 | if (MainCurrentDateType.value === 'week') { |
| @@ -233,6 +233,14 @@ const handleDeleteTemplate = async () => { | @@ -233,6 +233,14 @@ const handleDeleteTemplate = async () => { | ||
| 233 | return; | 233 | return; |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | + // 在 showModal 前把 id 存到局部变量,防止异步回调执行时 currentTemplate.value 已被清空 | ||
| 237 | + const templateId = currentTemplate.value.id; | ||
| 238 | + | ||
| 239 | + // 必须先关闭 WebView 弹窗(up-popup),否则它会盖在 uni.showModal 原生弹窗上面 | ||
| 240 | + closeTemplateMenu(); | ||
| 241 | + | ||
| 242 | + // 延时一帧再弹原生确认框,避免与 closeTemplateMenu 的卸载动画在视觉上打架 | ||
| 243 | + setTimeout(() => { | ||
| 236 | uni.showModal({ | 244 | uni.showModal({ |
| 237 | title: '确认删除', | 245 | title: '确认删除', |
| 238 | content: '确定要删除这个模板吗?删除后无法恢复', | 246 | content: '确定要删除这个模板吗?删除后无法恢复', |
| @@ -241,10 +249,9 @@ const handleDeleteTemplate = async () => { | @@ -241,10 +249,9 @@ const handleDeleteTemplate = async () => { | ||
| 241 | if (res.confirm) { | 249 | if (res.confirm) { |
| 242 | try { | 250 | try { |
| 243 | uni.showLoading({ title: '删除中...' }); | 251 | uni.showLoading({ title: '删除中...' }); |
| 244 | - await TemplatesApi.deleteCustTemplate(currentTemplate.value.id); | 252 | + await TemplatesApi.deleteCustTemplate(templateId); |
| 245 | uni.hideLoading(); | 253 | uni.hideLoading(); |
| 246 | uni.showToast({ title: '删除成功', icon: 'success' }); | 254 | uni.showToast({ title: '删除成功', icon: 'success' }); |
| 247 | - closeTemplateMenu(); | ||
| 248 | // 通知父组件刷新列表 | 255 | // 通知父组件刷新列表 |
| 249 | emit('refresh'); | 256 | emit('refresh'); |
| 250 | 257 | ||
| @@ -256,6 +263,7 @@ const handleDeleteTemplate = async () => { | @@ -256,6 +263,7 @@ const handleDeleteTemplate = async () => { | ||
| 256 | } | 263 | } |
| 257 | } | 264 | } |
| 258 | }); | 265 | }); |
| 266 | + }, 200); | ||
| 259 | }; | 267 | }; |
| 260 | 268 | ||
| 261 | 269 |
| @@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
| 8 | <text class="time">{{ formattedTime.substring(3) }}</text> | 8 | <text class="time">{{ formattedTime.substring(3) }}</text> |
| 9 | <view class="triangle-icon"></view> | 9 | <view class="triangle-icon"></view> |
| 10 | </view> | 10 | </view> |
| 11 | - <view class="finish-btn" @click="save">完成</view> | 11 | + <view class="finish-btn" :class="{ disabled: !allUnitsChecked }" @click="save">完成</view> |
| 12 | </view> | 12 | </view> |
| 13 | 13 | ||
| 14 | <view class="action-list"> | 14 | <view class="action-list"> |
| @@ -431,6 +431,14 @@ | @@ -431,6 +431,14 @@ | ||
| 431 | unitActiveStates.value = [...unitActiveStates.value] | 431 | unitActiveStates.value = [...unitActiveStates.value] |
| 432 | } | 432 | } |
| 433 | 433 | ||
| 434 | + // 全部 unit 已勾选后才能提交 | ||
| 435 | + const allUnitsChecked = computed(() => { | ||
| 436 | + const list = actionDetail.value?.units || [] | ||
| 437 | + if (list.length === 0) return false | ||
| 438 | + if (unitActiveStates.value.length !== list.length) return false | ||
| 439 | + return unitActiveStates.value.every((v) => v === true) | ||
| 440 | + }) | ||
| 441 | + | ||
| 434 | const openSummaryPopup = () => { | 442 | const openSummaryPopup = () => { |
| 435 | summaryShow.value = true; | 443 | summaryShow.value = true; |
| 436 | summaryContent.value = ''; | 444 | summaryContent.value = ''; |
| @@ -507,6 +515,17 @@ | @@ -507,6 +515,17 @@ | ||
| 507 | }); | 515 | }); |
| 508 | 516 | ||
| 509 | const save = async () => { | 517 | const save = async () => { |
| 518 | + // 前置校验:所有 unit 必须已勾选(顶部 ✓)才能提交 | ||
| 519 | + if (!allUnitsChecked.value) { | ||
| 520 | + uni.showModal({ | ||
| 521 | + title: '提示', | ||
| 522 | + content: '还有训练没有完成,请先勾选所有动作后再提交', | ||
| 523 | + showCancel: false, | ||
| 524 | + confirmText: '知道了', | ||
| 525 | + }); | ||
| 526 | + return; | ||
| 527 | + } | ||
| 528 | + | ||
| 510 | await nextTick(); | 529 | await nextTick(); |
| 511 | const units = []; | 530 | const units = []; |
| 512 | const unitList = actionDetail.value?.units || []; | 531 | const unitList = actionDetail.value?.units || []; |
| @@ -552,16 +571,17 @@ | @@ -552,16 +571,17 @@ | ||
| 552 | 571 | ||
| 553 | try { | 572 | try { |
| 554 | if (trainingStore.isTraining) { | 573 | if (trainingStore.isTraining) { |
| 555 | - await TrainingApi.createTrainHistory({ units }); | 574 | + await TrainingApi.createTrainHistory({ units, allCompleted: allUnitsChecked.value }); |
| 556 | uni.showToast({ title: '训练提交成功', icon: 'success' }); | 575 | uni.showToast({ title: '训练提交成功', icon: 'success' }); |
| 557 | } else if (trainingStore.type === 3 && trainingStore.dailyTemplateId) { | 576 | } else if (trainingStore.type === 3 && trainingStore.dailyTemplateId) { |
| 558 | await dailytemplateApi.updateDailyTemplate({ | 577 | await dailytemplateApi.updateDailyTemplate({ |
| 559 | dailyTemplateId: trainingStore.dailyTemplateId, | 578 | dailyTemplateId: trainingStore.dailyTemplateId, |
| 560 | units, | 579 | units, |
| 580 | + allCompleted: allUnitsChecked.value, | ||
| 561 | }); | 581 | }); |
| 562 | uni.showToast({ title: '模板修改成功', icon: 'success' }); | 582 | uni.showToast({ title: '模板修改成功', icon: 'success' }); |
| 563 | } else { | 583 | } else { |
| 564 | - await TrainingApi.createTrainHistory({ units }); | 584 | + await TrainingApi.createTrainHistory({ units, allCompleted: allUnitsChecked.value }); |
| 565 | uni.showToast({ title: '训练提交成功', icon: 'success' }); | 585 | uni.showToast({ title: '训练提交成功', icon: 'success' }); |
| 566 | } | 586 | } |
| 567 | setTimeout(() => { | 587 | setTimeout(() => { |
| @@ -681,6 +701,14 @@ | @@ -681,6 +701,14 @@ | ||
| 681 | border-radius: 30rpx; | 701 | border-radius: 30rpx; |
| 682 | font-weight: bold; | 702 | font-weight: bold; |
| 683 | font-size: 28rpx; | 703 | font-size: 28rpx; |
| 704 | + transition: opacity 0.2s; | ||
| 705 | + | ||
| 706 | + &.disabled { | ||
| 707 | + background-color: #555; | ||
| 708 | + color: #999; | ||
| 709 | + opacity: 0.6; | ||
| 710 | + pointer-events: none; | ||
| 711 | + } | ||
| 684 | } | 712 | } |
| 685 | } | 713 | } |
| 686 | 714 |
| @@ -8,6 +8,7 @@ const AuthUtil = { | @@ -8,6 +8,7 @@ const AuthUtil = { | ||
| 8 | method: 'POST', | 8 | method: 'POST', |
| 9 | data, | 9 | data, |
| 10 | custom: { | 10 | custom: { |
| 11 | + isToken: false, | ||
| 11 | showSuccess: true, | 12 | showSuccess: true, |
| 12 | successMsg: '登录成功', | 13 | successMsg: '登录成功', |
| 13 | }, | 14 | }, |
| @@ -20,6 +21,7 @@ const AuthUtil = { | @@ -20,6 +21,7 @@ const AuthUtil = { | ||
| 20 | method: 'POST', | 21 | method: 'POST', |
| 21 | data, | 22 | data, |
| 22 | custom: { | 23 | custom: { |
| 24 | + isToken: false, | ||
| 23 | showSuccess: true, | 25 | showSuccess: true, |
| 24 | successMsg: '登录成功', | 26 | successMsg: '登录成功', |
| 25 | }, | 27 | }, |
| @@ -32,6 +34,7 @@ const AuthUtil = { | @@ -32,6 +34,7 @@ const AuthUtil = { | ||
| 32 | method: 'POST', | 34 | method: 'POST', |
| 33 | data, | 35 | data, |
| 34 | custom: { | 36 | custom: { |
| 37 | + isToken: false, | ||
| 35 | showLoading: false, | 38 | showLoading: false, |
| 36 | }, | 39 | }, |
| 37 | }); | 40 | }); |
| @@ -67,7 +70,9 @@ const AuthUtil = { | @@ -67,7 +70,9 @@ const AuthUtil = { | ||
| 67 | url: '/app/auth/setPassword', | 70 | url: '/app/auth/setPassword', |
| 68 | method: 'POST', | 71 | method: 'POST', |
| 69 | data, | 72 | data, |
| 70 | - | 73 | + custom: { |
| 74 | + isToken: false, | ||
| 75 | + }, | ||
| 71 | }); | 76 | }); |
| 72 | }, | 77 | }, |
| 73 | 78 | ||
| @@ -113,6 +118,7 @@ const AuthUtil = { | @@ -113,6 +118,7 @@ const AuthUtil = { | ||
| 113 | role: 1, | 118 | role: 1, |
| 114 | }, | 119 | }, |
| 115 | custom: { | 120 | custom: { |
| 121 | + isToken: false, | ||
| 116 | showSuccess: true, | 122 | showSuccess: true, |
| 117 | loadingMsg: '登录中', | 123 | loadingMsg: '登录中', |
| 118 | successMsg: '登录成功', | 124 | successMsg: '登录成功', |
| @@ -128,6 +134,7 @@ const AuthUtil = { | @@ -128,6 +134,7 @@ const AuthUtil = { | ||
| 128 | code, | 134 | code, |
| 129 | }, | 135 | }, |
| 130 | custom: { | 136 | custom: { |
| 137 | + isToken: false, | ||
| 131 | showSuccess: true, | 138 | showSuccess: true, |
| 132 | loadingMsg: '登录中', | 139 | loadingMsg: '登录中', |
| 133 | successMsg: '登录成功', | 140 | successMsg: '登录成功', |
| @@ -422,6 +422,14 @@ export const useTrainingStore = defineStore('training', { | @@ -422,6 +422,14 @@ export const useTrainingStore = defineStore('training', { | ||
| 422 | this.defaultTimeIndex = [0, 0, 0]; | 422 | this.defaultTimeIndex = [0, 0, 0]; |
| 423 | this.showPicker = false; | 423 | this.showPicker = false; |
| 424 | this.isTraining = false; | 424 | this.isTraining = false; |
| 425 | + | ||
| 426 | + // 绕过 persist 插件的异步 watch,直接同步写入 storage | ||
| 427 | + // 防止 navigateBack 后 persist flush 赶不及,导致 isTraining 残留为 true | ||
| 428 | + try { | ||
| 429 | + uni.setStorageSync('training', JSON.stringify(this.$state)); | ||
| 430 | + } catch (e) { | ||
| 431 | + // 静默处理,不影响主流程 | ||
| 432 | + } | ||
| 425 | }, | 433 | }, |
| 426 | }, | 434 | }, |
| 427 | 435 |
-
Please register or login to post a comment