TrainingFloating.vue
3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<template>
<view class="floating-train-btn" v-if="showBall" @click="goToTrainingPage">
<view class="icon-wrap">
<up-icon name="plus-circle" color="#fff" size="24"></up-icon>
</view>
<view class="text-wrap">
<text class="status-text">训练中</text>
<text class="time-text">{{ formattedStartTime }}</text>
</view>
</view>
</template>
<script setup>
import { computed, watch, onMounted } from 'vue';
import { useTrainingStore } from '@/sheep/store/trainingStore';
import { trainingSnapshot } from '@/sheep/store/trainingSnapshot'
const trainingStore = useTrainingStore();
const showBall = computed(() => trainingSnapshot.visible && trainingSnapshot.data !== null)
// 格式化显示时间(快照中的 trainingTimeText)
const formattedStartTime = computed(() => {
if (!trainingSnapshot.data) return ''
return trainingSnapshot.data.trainingTimeText || ''
})
// ========== 点击悬浮球:还原快照 → 跳转训练页 ==========
const goToTrainingPage = () => {
if (!trainingSnapshot.data) return
// 安全检查:如果 store 里已经有不属于本次快照的训练数据(用户在编辑中)
if (trainingStore.id && trainingStore.id !== trainingSnapshot.data.id) {
uni.showModal({
title: '提示',
content: '当前已有另一组训练数据,恢复会覆盖它,确认吗?',
confirmText: '确认恢复',
cancelText: '取消',
success: (res) => {
if (res.confirm) doRestoreAndGo()
}
})
return
}
doRestoreAndGo()
}
const doRestoreAndGo = () => {
const snap = trainingSnapshot.data
// 1. 用 $patch 还原全量 state(Pinia 官方批量更新,保证响应式触发)
trainingStore.$patch(snap)
// 2. 补偿计时器:如果快照时计时器在跑,加上经过的时间
if (trainingSnapshot.timerWasRunning && trainingSnapshot.timestamp) {
const elapsed = Math.floor((Date.now() - trainingSnapshot.timestamp) / 1000)
trainingStore.totalSeconds = (snap.totalSeconds || 0) + elapsed
}
// 3. 如果快照时计时器在跑,重新启动 setInterval
if (trainingSnapshot.timerWasRunning) {
trainingStore.isPause = false
trainingStore.timerInterval = setInterval(() => {
trainingStore.totalSeconds++
}, 1000)
}
// 4. 清空快照,隐藏悬浮球
trainingSnapshot.data = null
trainingSnapshot.timestamp = null
trainingSnapshot.timerWasRunning = false
trainingSnapshot.visible = false
// 5. 跳转训练页面
uni.navigateTo({
url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${snap.id}&type=${snap.type}&isTraining=true`,
})
}
// 格式化显示“开始时间”
// const formattedStartTime = computed(() => trainingStore.trainingTimeText);
// 跳转到训练页面
// const goToTrainingPage = () => {
// // 悬浮球消失
// trainingStore.min = false;
// uni.navigateTo({
// url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${trainingStore.id}&type=${trainingStore.type}`,
// });
// };
watch(() => showBall.value, (newVal) => {
console.log('🟢 悬浮球组件监听到 showBall 变化:', newVal);
}, { immediate: true });
// 组件挂载时打印
onMounted(() => {
console.log('🟢 悬浮球组件已挂载,当前 showBall 值:', showBall.value);
});
</script>
<style lang="scss" scoped>
.floating-train-btn {
position: fixed;
right: 11rpx;
bottom: 304rpx;
display: flex;
align-items: center;
background-color: #39d353;
border-radius: 60rpx;
padding: 16rpx 20rpx;
box-shadow: 0 8rpx 20rpx rgba(57, 211, 83, 0.3);
z-index: 9999;
}
.icon-wrap {
margin-right: 12rpx;
display: flex;
align-items: center;
justify-content: center;
}
.text-wrap {
display: flex;
flex-direction: column;
color: #fff;
}
.status-text {
font-size: 26rpx;
font-weight: bold;
}
.time-text {
font-size: 22rpx;
opacity: 0.9;
}
</style>