Compare commits
3 Commits
05c4763e49
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b6d94b21cd | |||
| f1ad3eeb95 | |||
| 178a51d261 |
@@ -62,3 +62,47 @@
|
||||
- `.info-bubble` margin-top 24→40rpx(承接原 timer-status 间距)、padding 20→24rpx。
|
||||
- 关键机制备忘:**resting 时 status 字段值仍是 'running'**,状态行转绿必须靠 `isResting` 单独加类,不能只看 status。
|
||||
- 验证:grep 无 `timer-status` 代码残留(仅注释文字),js 未改动。
|
||||
|
||||
## 进度弧"0 点对齐"问题:两轮修复被否定,已全部回滚(待用户澄清)
|
||||
- 用户反馈(两次截图):训练时粗进度弧起点与 0 点/顶部起点差一点没对齐,运动过程中更明显(循环与单组模式都出现过)。
|
||||
- 尝试过两轮修复(progress-ring.js `_drawSetArcs` 半径/清除区微调),用户判定"完全理解错了",**要求撤销**。
|
||||
- 已回滚:`git diff` 确认 progress-ring.js 与 HEAD(`05c4763` 分段弧初始实现:glowPad=6、arcR=radius+lw/2+3、arcW=8、清除区 clip 内缘 arcR-band)完全一致,`node --check` 通过。
|
||||
- 教训:对齐类问题不要凭数值推理直接改半径,先让用户明确"0 点"的参照物(刻度环/呼吸环/分段弧?)与出现模式,再定位。
|
||||
- **最终定位与修复(用户澄清"所有运动中,进度弧零点与表盘 0 点没对齐")**:
|
||||
- 根因确凿:`_drawSetArcs` 每段起点 `+gap/2`(1.25°),**外圈分段弧第一段从 12 点偏右 1.25° 开始**(半径 ~207dpr 处 ≈ 4.5dpr ≈ 1.5px),而内圈进度弧/12 点长刻度都从精确 -π/2 开始 → 循环模式外圈与内圈 0 点肉眼错开。
|
||||
- 修复:`a0 = startAngle + (i-1)*segAngle`(第一段精确 -π/2 起),`a1 = a0 + segAngle - gap`(段尾留 gap)。段间空隙均匀(2.5°×3),收尾空隙落在 12 点左侧,环带以 12 点为起点。node 验算:段起点 270°/30°/150°,空隙 2.5° 均匀,首段 12 点 ✓。
|
||||
- 单组/自由模式无分段弧,canvas 内刻度/进度弧同基准(-π/2),数学对齐;若用户仍反馈错位需再查(可能涉及 canvas 布局/参照物)。
|
||||
- 注意:`gap/2` 对称偏移是"段内居中空隙"的常见写法,环形进度环带与刻度基准对齐时必须从 -π/2 起算。
|
||||
- **用户叫停("算了,这个问题先不修复了")**:零点对齐问题整体搁置。该轮 progress-ring.js 的角度修复(仅影响循环模式、覆盖不了单组模式现象)已 `git checkout` 回退,与 HEAD 一致。等用户想继续时再排查,方向可能是 canvas 布局/参照物而非半径。
|
||||
|
||||
## 新功能:训练时长低于阈值不记录(config.minRecordSeconds,默认 20 秒)
|
||||
- 需求:训练有效时长 < 20 秒(可配置)不写入记录,并给用户明确提示;阈值收进 config.js。
|
||||
- **`config.js`**:新增 `minRecordSeconds: 20`(带注释:不写记录、不更新连胜;循环按有效撑持秒数=各组时长之和判断)。
|
||||
- **`pages/timer/timer.js`**:
|
||||
- `finishTraining()`:`elapsed >= minSec` 才播完成语音(低于阈值不庆祝)。
|
||||
- `_onCircuitComplete()`:循环自然完成路径同样按 `workTotal >= minSec` 播语音。
|
||||
- `_saveAndShowCompletion(elapsed)` 开头加**阈值兜底**:`elapsed < minSec` → `_showNotRecorded(elapsed, minSec)` 直接 return。手动结束与循环自然完成两条路径都汇聚到这里,任何入口都不会把短时训练落库。
|
||||
- 新增 `_showNotRecorded(elapsed, minSec)`:复用完成弹窗(避免系统原生 modal 在 WebView stacking context 下 hit-test 不稳),不保存记录/连胜、无 confetti/streak/科学提示,大数字照常 countUp 到 elapsed;置 `completionNotRecorded: true`、`minRecordSeconds: minSec`、标题"未达最低记录时长"。
|
||||
- data 新增初始字段 `completionNotRecorded: false`、`minRecordSeconds: 20`。
|
||||
- **状态残留坑(已修)**:`onTrainAgain`(再来一次)与 `_saveAndShowCompletion` 正常分支都必须显式 `completionNotRecorded: false`,否则上轮未记录后重练,下次正常完成弹窗会误显示"未计入记录"且隐藏分享/记录按钮。
|
||||
- **`timer.wxml`**:完成弹窗新增 `.completion-not-recorded` 提示块(infoFill 图标 + "本次训练未计入记录" + 副行"撑满 X 秒以上才会记录并计入连续打卡");未记录态 `wx:if="{{!completionNotRecorded}}"` 隐藏"查看记录/分享"(没有成绩可看/可晒),仅留"再来一次 + 回到首页"。
|
||||
- **`timer.wxss`**:新增 `.completion-not-recorded` 主色淡底信息块(与 completion-tip 同风格,tipIn 延迟 0.5s 入场)。
|
||||
- 保留逻辑:`elapsed < 3` 秒仍走 toast「训练时间太短」+ navigateBack(几乎未开始,不弹完成弹窗)。
|
||||
- 验证:node --check 通过;grep 确认 minRecordSeconds/completionNotRecorded/_showNotRecorded 三处字段在 config/js/wxml 引用一致。
|
||||
|
||||
## 分段弧两端粗细不一致(已修)
|
||||
- 用户反馈:循环训练时分段弧"两端粗细不一致"。
|
||||
- 根因(progress-ring.js `_drawSetArcs`):发光光点坐标固定取段尾角 `a1`(`dx=cx+arcR*cos(a1)`),当前段(active)/休息预告段(next)的尾端 = 光点+8dpr 发光晕(直径≈弧宽+),首端却只是 `lineCap:'round'` 普通圆头 → 同段弧尾粗头细。已完成/未开始段两端都是普通圆头,粗细一致,所以现象集中在运动中那一段。
|
||||
- 修复:光点移到**段中点** `am=(a0+a1)/2`(3 行),两端自然对称,光点仍作"当前组"标记。node --check 通过。
|
||||
- 注意:若以后想给分段弧加"当前组跟随倒计时移动"的进度感,光点可改为随剩余时间在段内插值,但需先确认用户是否要这个效果。
|
||||
|
||||
## 分段弧间距 + 光点置顶(用户反馈"两弧太近、弧压着光点")
|
||||
- 用户反馈两点:①分段弧与进度弧距离太近;②光点被分段弧压住(视觉上弧盖着光点)。
|
||||
- **间距修复**(progress-ring.js,size=420 时验证):
|
||||
- 根因:间距 = gap − arcW/2 = 3−4 = **−1dpr(重叠 1)**。因为分段弧 arcR 仅比进度弧外缘靠外 3dpr,而弧半宽 4dpr。
|
||||
- `_draw()` 的 `glowPad` 6→14dpr(进度弧半径内缩 8,给外圈腾空间);`_drawSetArcs()` 的 radius pad 6→14(**两处必须同步**,已加注释)且 arcR 间距 `+3→+9dpr`。结果:进度弧外缘 204→196、分段弧内缘 203→201,间距 −1→**+5dpr**。副作用:单组/自由模式的进度环也内缩 ~8dpr(约 4%),中心数字区略小;进度弧尾端发光点 shadowBlur=12 阴影反而从"被 canvas 裁"变完整。
|
||||
- 段间空隙 gap=2.5° 是段间距,**与环间距无关,不动**。
|
||||
- **光点置顶**:原实现光点在逐段循环内、每段弧 stroke 之后画,理论上应在弧上,但视觉上被 round cap 相邻段边缘"吞掉"。重构为**两遍绘制**:pass1 只画所有弧并收集 emphasized 段光点(x/y/color),pass2 统一在所有弧之上画光点。同时光点加大加亮:dotR 从 `0.85~1.2×弧半宽` → `1.15~1.4×`,alpha `0.55~1.0` → `0.7~1.0`,白芯 0.85→0.9,读起来像"珠珠骑在弧上"。
|
||||
- 光点仍居段中点(沿用上轮两端对称修复);外缘 209dpr 不超 canvas 半宽 210,与 breath-aura(内缘 ~232)无重叠。
|
||||
- 验证:node --check 通过;数值全部核算。未提交 git。
|
||||
- **间距仍不够(用户反馈"还是太近")→ 再翻倍**:canvas 半宽 210 是硬上限,剩余空间仅 ~5dpr,要明显拉开只能继续内缩进度弧。glowPad 14→18、arcR 外移量 9→14,间距 **5→10dpr**(进度弧外缘 196→192,分段弧内缘 201→202,光点外缘 211.6 超画布 0.8px 亚像素无感)。进度环直径累计缩 ~6%(408→384)。约束备忘:**间距 = arcR外移量 − arcW/2,且 ≤ glowPad − arcW − margin**;想再拉开只能继续缩环或减弧宽。
|
||||
|
||||
@@ -29,3 +29,4 @@
|
||||
- 美化按"见效快优先"分批,每轮 `node --check` + grep 残留再交付。
|
||||
- **git push 须 `dangerouslyDisableSandbox:true`**(沙箱隔离致 github SSL 超时);无 SSH,仅 HTTPS remote。
|
||||
- **GitHub 链路不稳**:国内环境到 `github.com:443` 常 SSL 重置/HTTP2 framing 失败(非代码问题),`git.soao.net`(soao) 国内远程稳定。策略:soao 为主远程、实时同步;`origin`(GitHub) 失败则延后推或本地代理(`127.0.0.1:7890` 等)临时 `git -C <repo> config http.proxy` 推完 `config --unset` 撤销。代码已 commit 则零丢失风险。
|
||||
- **GitHub 推送有效解法(2026-08-12 实测)**:报 `LibreSSL SSL_connect: SSL_ERROR_SYSCALL` 时,若 `curl 直连 https://github.com` 能 200(慢 30s 但通),说明是 git 默认 HTTP/2 framing 问题(curl 默认 HTTP/1.1 所以通)→ 执行 `git -C <repo> config http.version HTTP/1.1` 后再 push 即可成功,推完 `config --unset http.version` 撤销。7890 端口进程可能空转(curl -x 走它返回 000),别依赖该代理。
|
||||
|
||||
@@ -105,8 +105,11 @@ Component({
|
||||
|
||||
const cx = w / 2
|
||||
const cy = h / 2
|
||||
// reserve room for the glowing end dot so its shadow isn't clipped
|
||||
const glowPad = 6 * dpr
|
||||
// Reserve room for the glowing end dot's shadow AND the outer set-progress
|
||||
// band (circuit mode). _drawSetArcs derives its band radius from this same
|
||||
// pad, so increasing it here also widens the gap between the countdown
|
||||
// arc and the set band. 18dpr gives a ~10dpr visible gap (size=420).
|
||||
const glowPad = 18 * dpr
|
||||
const radius = (size - ringWidth) / 2 * dpr - glowPad
|
||||
const lw = ringWidth * dpr
|
||||
|
||||
@@ -252,8 +255,9 @@ Component({
|
||||
const cy = (size / 2) * dpr
|
||||
// Same radius math as _draw(): the countdown arc sits at `radius`; the
|
||||
// set band wraps just outside it so the two read as concentric layers.
|
||||
const radius = ((size - ringWidth) / 2) * dpr - 6 * dpr
|
||||
const arcR = radius + (ringWidth / 2) * dpr + 3 * dpr
|
||||
// NOTE: the -18*dpr pad MUST stay in sync with glowPad in _draw().
|
||||
const radius = ((size - ringWidth) / 2) * dpr - 18 * dpr
|
||||
const arcR = radius + (ringWidth / 2) * dpr + 14 * dpr
|
||||
const arcW = 8 * dpr
|
||||
const band = arcW / 2 + 3 * dpr
|
||||
|
||||
@@ -274,6 +278,10 @@ Component({
|
||||
const gap = (2.5 * Math.PI) / 180
|
||||
const startAngle = -Math.PI / 2
|
||||
|
||||
// Pass 1: draw every segment arc. Breathing dots are collected and painted
|
||||
// in pass 2, AFTER all arcs, so a dot always floats ON TOP of its segment
|
||||
// instead of being partially buried under neighbouring round caps.
|
||||
const dots = []
|
||||
for (let i = 1; i <= sets; i++) {
|
||||
const a0 = startAngle + (i - 1) * segAngle + gap / 2
|
||||
const a1 = startAngle + i * segAngle - gap / 2
|
||||
@@ -294,28 +302,38 @@ Component({
|
||||
ctx.stroke()
|
||||
ctx.globalAlpha = 1
|
||||
|
||||
// Glowing endpoint dot on the emphasized segment.
|
||||
if (emphasized) {
|
||||
const dx = cx + arcR * Math.cos(a1)
|
||||
const dy = cy + arcR * Math.sin(a1)
|
||||
const dotR = (arcW / 2) * (paused ? 1 : 0.85 + 0.35 * p)
|
||||
const glowColor = i === active ? primaryColor : successColor
|
||||
const am = (a0 + a1) / 2
|
||||
dots.push({
|
||||
x: cx + arcR * Math.cos(am),
|
||||
y: cy + arcR * Math.sin(am),
|
||||
color: i === active ? primaryColor : successColor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: glowing dot on each emphasized segment, drawn over every arc.
|
||||
// Placed at the segment MIDDLE (not the a1 end): a dot pinned to the
|
||||
// trailing end made that end look visibly thicker than the round-cap
|
||||
// start, so both ends of the arc stay symmetric. Slightly larger than
|
||||
// the band width so it reads as a bead riding on top of the arc.
|
||||
for (const dot of dots) {
|
||||
const dotR = (arcW / 2) * (paused ? 1.3 : 1.15 + 0.25 * p)
|
||||
ctx.save()
|
||||
ctx.shadowColor = glowColor
|
||||
ctx.shadowColor = dot.color
|
||||
ctx.shadowBlur = 8 * dpr
|
||||
ctx.globalAlpha = paused ? 0.9 : 0.55 + 0.45 * p
|
||||
ctx.globalAlpha = paused ? 0.95 : 0.7 + 0.3 * p
|
||||
ctx.beginPath()
|
||||
ctx.arc(dx, dy, dotR, 0, Math.PI * 2)
|
||||
ctx.fillStyle = glowColor
|
||||
ctx.arc(dot.x, dot.y, dotR, 0, Math.PI * 2)
|
||||
ctx.fillStyle = dot.color
|
||||
ctx.fill()
|
||||
ctx.restore()
|
||||
// white core for a "light bulb" feel
|
||||
ctx.beginPath()
|
||||
ctx.arc(dx, dy, dotR * 0.45, 0, Math.PI * 2)
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.85)'
|
||||
ctx.arc(dot.x, dot.y, dotR * 0.45, 0, Math.PI * 2)
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.9)'
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_startSetPulse() {
|
||||
|
||||
@@ -67,6 +67,15 @@ module.exports = {
|
||||
*/
|
||||
caloriesPerSecond: 0.068,
|
||||
|
||||
/**
|
||||
* 最短记录时长(秒)。本次训练有效时长低于该值时:
|
||||
* - 不写入训练记录、不更新连续打卡天数(避免"撑几秒也算一天"的无效记录)
|
||||
* - 完成弹窗会明确提示"本次未计入记录",并隐藏查看记录/分享入口
|
||||
* 循环训练按"有效撑持秒数"(各组时长之和,不含休息)判断。
|
||||
* 改这里即可调整门槛,无需动逻辑代码。
|
||||
*/
|
||||
minRecordSeconds: 15,
|
||||
|
||||
/**
|
||||
* 完成弹窗科学提示(按本次训练时长分段)。
|
||||
* 完成时按实际撑的秒数命中第一段 maxSeconds 大于它的配置,
|
||||
|
||||
+64
-2
@@ -40,6 +40,10 @@ Page({
|
||||
// 组进度改由 progress-ring 外圈分段弧呈现,仅需传 sets/currentSet/resting,
|
||||
// 页面不再维护点阵渲染数据(circuitDots/dotsDone 等已随旧面板一并删除)。
|
||||
celebrationLevel: 'normal', // normal / first / milestone / record
|
||||
// 训练时长低于 config.minRecordSeconds 时置 true:完成弹窗提示"未计入记录",
|
||||
// 隐藏查看记录/分享入口;弹窗内展示门槛值给用户明确反馈
|
||||
completionNotRecorded: false,
|
||||
minRecordSeconds: 20,
|
||||
celebrationMessage: '',
|
||||
confettiPieces: [],
|
||||
// Completion modal (full-screen summary after training ends)
|
||||
@@ -435,7 +439,9 @@ Page({
|
||||
_onCircuitComplete() {
|
||||
this._clearVibrateTimers()
|
||||
const s = storage.getSettings()
|
||||
if (s.voiceGuide !== false) voice.play('complete')
|
||||
// 有效撑持秒数未达最低记录时长时不播完成语音(弹窗走"未计入记录"分支)
|
||||
const minSec = config.minRecordSeconds || 20
|
||||
if (this.data.workTotal >= minSec && s.voiceGuide !== false) voice.play('complete')
|
||||
this._saveAndShowCompletion(this.data.workTotal)
|
||||
},
|
||||
|
||||
@@ -725,8 +731,10 @@ Page({
|
||||
return
|
||||
}
|
||||
// Completion voice (replaces the old auto-onComplete cue). No vibration.
|
||||
// 未达最低记录时长时不播完成语音——弹窗会走"未计入记录"分支,不庆祝。
|
||||
const s = storage.getSettings()
|
||||
if (s.voiceGuide !== false) {
|
||||
const minSec = config.minRecordSeconds || 20
|
||||
if (elapsed >= minSec && s.voiceGuide !== false) {
|
||||
voice.play('complete')
|
||||
}
|
||||
this._saveAndShowCompletion(elapsed)
|
||||
@@ -759,6 +767,15 @@ Page({
|
||||
// Re-entry guard: protects the finishTraining path so the save never
|
||||
// runs twice even if the user double-taps end.
|
||||
if (this._saving) return
|
||||
|
||||
// 未达 config.minRecordSeconds:不写入记录/连胜,弹"未计入记录"提示。
|
||||
// 兜底判断——手动结束(finishTraining)与循环自然完成(_onCircuitComplete)
|
||||
// 两条路径最终都汇聚到这里,保证任何入口都不会把短时训练落库。
|
||||
const minSec = config.minRecordSeconds || 20
|
||||
if (elapsed < minSec) {
|
||||
this._showNotRecorded(elapsed, minSec)
|
||||
return
|
||||
}
|
||||
this._saving = true
|
||||
|
||||
const { level, message } = this._detectCelebrationLevel(elapsed)
|
||||
@@ -797,6 +814,9 @@ Page({
|
||||
// Background effects keep playing behind the modal
|
||||
status: 'completed',
|
||||
isCompleted: true,
|
||||
// 重置未记录标记(上一轮若走了 _showNotRecorded,这里必须回 false,
|
||||
// 否则下次正常完成的弹窗会误显示"未计入记录"且隐藏分享/记录按钮)
|
||||
completionNotRecorded: false,
|
||||
celebrationLevel: level,
|
||||
celebrationMessage: message,
|
||||
confettiPieces,
|
||||
@@ -827,6 +847,46 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 训练时长低于 config.minRecordSeconds:不保存记录、不更新连胜,
|
||||
* 复用完成弹窗(避免系统原生 modal 在 WebView stacking context 下
|
||||
* hit-test 不稳),弹窗内明确提示"本次未计入记录"。
|
||||
* 仍显示本次实际撑持秒数(大数字 countUp),但不放 confetti/连胜/科学提示,
|
||||
* 并隐藏"查看记录/分享"入口(没有记录可看)。
|
||||
*/
|
||||
_showNotRecorded(elapsed, minSec) {
|
||||
if (this._saving) return
|
||||
this._saving = true
|
||||
|
||||
this.setData({
|
||||
status: 'completed',
|
||||
isCompleted: true,
|
||||
completionNotRecorded: true,
|
||||
minRecordSeconds: minSec,
|
||||
showCompletion: true,
|
||||
completionElapsed: 0,
|
||||
completionActualElapsed: elapsed,
|
||||
completionTarget: this.data.duration,
|
||||
completionOvertime: 0,
|
||||
completionStreak: 0,
|
||||
completionLevel: 'normal',
|
||||
completionMessage: '未达最低记录时长',
|
||||
completionTipValue: '',
|
||||
completionTipRisk: '',
|
||||
confettiPieces: []
|
||||
})
|
||||
|
||||
// Animate the big number from 0 to elapsed (same as the normal modal)
|
||||
if (this._completionCountUp) this._completionCountUp.cancel()
|
||||
this._completionCountUp = countUp({
|
||||
from: 0,
|
||||
to: elapsed,
|
||||
duration: 1200,
|
||||
onUpdate: (v) => this.setData({ completionElapsed: v }),
|
||||
onComplete: () => { this._completionCountUp = null }
|
||||
})
|
||||
},
|
||||
|
||||
onCloseCompletion() {
|
||||
if (this._completionCountUp) {
|
||||
this._completionCountUp.cancel()
|
||||
@@ -882,6 +942,8 @@ Page({
|
||||
status: 'idle',
|
||||
isRunning: false,
|
||||
isPaused: false,
|
||||
// 复位未记录标记:若上一轮走了 _showNotRecorded,重练前必须回 false
|
||||
completionNotRecorded: false,
|
||||
remaining: this.data.duration,
|
||||
elapsed: 0,
|
||||
goalReached: false,
|
||||
|
||||
+12
-1
@@ -172,6 +172,15 @@
|
||||
<text class="streak-text">连续打卡 {{completionStreak}} 天</text>
|
||||
</view>
|
||||
|
||||
<!-- 未达最低记录时长:明确提示本次未计入记录,并说明门槛 -->
|
||||
<view class="completion-not-recorded" wx:if="{{completionNotRecorded}}">
|
||||
<view class="completion-not-recorded-main">
|
||||
<image class="completion-not-recorded-icon" src="{{icons.infoFill}}" mode="aspectFit"></image>
|
||||
<text class="completion-not-recorded-title">本次训练未计入记录</text>
|
||||
</view>
|
||||
<text class="completion-not-recorded-sub">撑满 {{minRecordSeconds}} 秒以上才会记录并计入连续打卡</text>
|
||||
</view>
|
||||
|
||||
<!-- 科学提示:价值行 + 弱化风险行(config.scienceTips 按时长选段) -->
|
||||
<view class="completion-tip" wx:if="{{completionTipValue}}">
|
||||
<text class="completion-tip-value">{{completionTipValue}}</text>
|
||||
@@ -185,13 +194,15 @@
|
||||
<view class="completion-btn completion-btn--primary" bindtap="onTrainAgain">
|
||||
<text>再来一次</text>
|
||||
</view>
|
||||
<view class="completion-btn" bindtap="onViewRecords">
|
||||
<!-- 未记录时没有成绩可看/可晒,隐藏查看记录与分享入口 -->
|
||||
<view class="completion-btn" wx:if="{{!completionNotRecorded}}" bindtap="onViewRecords">
|
||||
<text>查看记录</text>
|
||||
</view>
|
||||
<!-- openType="share" 会触发 Page.onShareAppMessage(已实现),
|
||||
没有这个属性 + onShareAppMessage 时分享按钮就是灰色的。
|
||||
ui-btn 透传 openType 到内部原生 button。 -->
|
||||
<ui-btn
|
||||
wx:if="{{!completionNotRecorded}}"
|
||||
variant="ghost"
|
||||
size="md"
|
||||
customStyle="flex:1; height:88rpx;"
|
||||
|
||||
@@ -425,6 +425,46 @@
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
/* 未达最低记录时长提示(完成弹窗内):主色淡底信息块,与科学提示同风格 */
|
||||
.completion-not-recorded {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: rgba(var(--primary-rgb), 0.08);
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
margin-bottom: 28rpx;
|
||||
animation: tipIn 0.4s ease 0.5s both;
|
||||
}
|
||||
|
||||
.completion-not-recorded-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.completion-not-recorded-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.completion-not-recorded-title {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.completion-not-recorded-sub {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.streak-text {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user