style(entry): 训练浮动入口美化,移除 DEV 标签

主按钮:
- 移除右上角红色 DEV 标签
- 单字"练" 48rpx 粗字号居中,不再 emoji+文字双层
- emerald 色由 #34d399 → #047857 渐变,跟节拍器主色统一
- 加双层立体阴影 + inset 高光,看起来像凸起的玻璃按钮
- 加 2 圈错相位呼吸光环(2.4s 周期),暗示可点击
- 关闭态用 CSS 几何 X 图标,旋转 135° 动画过渡

展开菜单:
- 圆角药丸卡片,带 backdrop-blur 玻璃质感,层级阴影
- "练一练"圆形 icon emerald 渐变 + 💪 emoji
- "节拍器"图标改成 3 根高低柱 CSS 模拟节拍/均衡器,
  琥珀色渐变跟节拍器页面重音色呼应
- 右侧加细 › 箭头暗示可跳转
- 入场动画从 scale(0.92) 弹性放大,更顺滑

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-25 18:24:35 +08:00
co-authored by Cursor
parent 5aa0cda252
commit 769d912eef
@@ -1,26 +1,42 @@
<template> <template>
<view v-if="visible" class="dev-entry-wrap" :style="{ bottom: props.bottom + 'rpx' }"> <view v-if="visible" class="dev-entry-wrap" :style="{ bottom: props.bottom + 'rpx' }">
<!-- 展开后的菜单卡片(从下往上动画) -->
<view v-if="expanded" class="menu-list" @click.stop> <view v-if="expanded" class="menu-list" @click.stop>
<view class="menu-item" @click="goto('/pages/training/index')"> <view class="menu-item" @click="goto('/pages/training/index')">
<view class="menu-icon">💪</view> <view class="menu-icon menu-icon--train">
<text class="menu-icon-text">💪</text>
</view>
<view class="menu-info"> <view class="menu-info">
<text class="menu-title">练一练</text> <text class="menu-title">练一练</text>
<text class="menu-sub">器械跟练</text> <text class="menu-sub">器械跟练</text>
</view> </view>
<view class="menu-arrow"></view>
</view> </view>
<view class="menu-item" @click="goto('/pages/training/metronome')"> <view class="menu-item" @click="goto('/pages/training/metronome')">
<view class="menu-icon metronome-icon">🎵</view> <view class="menu-icon menu-icon--metro">
<view class="metro-bar metro-bar-1" />
<view class="metro-bar metro-bar-2" />
<view class="metro-bar metro-bar-3" />
</view>
<view class="menu-info"> <view class="menu-info">
<text class="menu-title">节拍器</text> <text class="menu-title">节拍器</text>
<text class="menu-sub">独立工具</text> <text class="menu-sub">健走配速</text>
</view> </view>
<view class="menu-arrow"></view>
</view> </view>
</view> </view>
<view class="entry-fab" :class="{ expanded }" @click="toggle"> <!-- 主浮动按钮(单字"练" + 呼吸光环) -->
<text class="fab-icon">{{ expanded ? '✕' : '💪' }}</text> <view class="fab-container">
<text v-if="!expanded" class="fab-text"></text> <view v-if="!expanded" class="fab-pulse" />
<view v-if="!expanded" class="fab-tag">DEV</view> <view v-if="!expanded" class="fab-pulse fab-pulse-2" />
<view class="entry-fab" :class="{ expanded }" @click="toggle">
<view v-if="expanded" class="icon-close">
<view class="cross-bar bar-1" />
<view class="cross-bar bar-2" />
</view>
<text v-else class="fab-text"></text>
</view>
</view> </view>
</view> </view>
</template> </template>
@@ -36,7 +52,7 @@ const props = withDefaults(
) )
const isDevMode = (): boolean => { const isDevMode = (): boolean => {
// HBuilderX 工程:发行(release)模式下 NODE_ENV === 'production' /* HBuilderX 工程:发行(release)模式下 NODE_ENV === 'production' */
try { try {
return process.env.NODE_ENV !== 'production' return process.env.NODE_ENV !== 'production'
} catch (_) { } catch (_) {
@@ -58,6 +74,10 @@ const goto = (url: string) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$brand: #10b981;
$brand-deep: #047857;
$brand-light: #34d399;
.dev-entry-wrap { .dev-entry-wrap {
position: fixed; position: fixed;
right: 24rpx; right: 24rpx;
@@ -65,70 +85,120 @@ const goto = (url: string) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-end; align-items: flex-end;
gap: 16rpx; gap: 18rpx;
}
/* ============================================================
* 主浮动按钮
* ============================================================ */
.fab-container {
position: relative;
width: 108rpx;
height: 108rpx;
} }
.entry-fab { .entry-fab {
width: 110rpx; position: absolute;
height: 110rpx; inset: 0;
border-radius: 50%; border-radius: 50%;
background: linear-gradient(135deg, #22c55e, #16a34a); background: linear-gradient(140deg, $brand-light 0%, $brand-deep 100%);
box-shadow: 0 6rpx 20rpx rgba(34, 197, 94, 0.4); box-shadow:
0 8rpx 20rpx rgba(16, 185, 129, 0.42),
0 2rpx 6rpx rgba(15, 23, 42, 0.12),
inset 0 -8rpx 16rpx rgba(0, 0, 0, 0.12),
inset 0 8rpx 16rpx rgba(255, 255, 255, 0.18);
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s;
transition: transform 0.2s, background 0.2s;
&:active { &:active {
transform: scale(0.92); transform: scale(0.92);
} }
&.expanded { &.expanded {
background: linear-gradient(135deg, #64748b, #334155); background: linear-gradient(140deg, #94a3b8 0%, #475569 100%);
transform: rotate(90deg); transform: rotate(135deg);
} box-shadow:
0 6rpx 16rpx rgba(15, 23, 42, 0.2),
.fab-icon { inset 0 -6rpx 14rpx rgba(0, 0, 0, 0.12),
font-size: 36rpx; inset 0 6rpx 14rpx rgba(255, 255, 255, 0.12);
line-height: 1;
} }
.fab-text { .fab-text {
font-size: 18rpx; font-size: 48rpx;
font-weight: 800;
color: #fff; color: #fff;
margin-top: 4rpx;
line-height: 1; line-height: 1;
} letter-spacing: 2rpx;
text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.18);
.fab-tag {
position: absolute;
top: -8rpx;
right: -8rpx;
background: #ef4444;
color: #fff;
font-size: 16rpx;
padding: 2rpx 8rpx;
border-radius: 8rpx;
line-height: 1.4;
} }
} }
/* 关闭态 X 图标 (CSS 几何) */
.icon-close {
position: relative;
width: 36rpx;
height: 36rpx;
.cross-bar {
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 5rpx;
background: #fff;
border-radius: 3rpx;
transform-origin: center;
}
.bar-1 {
transform: translateY(-50%) rotate(45deg);
}
.bar-2 {
transform: translateY(-50%) rotate(-45deg);
}
}
/* 呼吸光环(2 圈错相位扩散) */
.fab-pulse {
position: absolute;
inset: 0;
border-radius: 50%;
background: rgba(16, 185, 129, 0.25);
animation: fab-ring 2.4s ease-out infinite;
pointer-events: none;
}
.fab-pulse-2 {
animation-delay: 1.2s;
}
@keyframes fab-ring {
0% {
transform: scale(0.95);
opacity: 0.7;
}
100% {
transform: scale(1.55);
opacity: 0;
}
}
/* ============================================================
* 展开菜单
* ============================================================ */
.menu-list { .menu-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12rpx; gap: 14rpx;
animation: slide-up 0.2s ease-out; animation: slide-up 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
} }
@keyframes slide-up { @keyframes slide-up {
from { from {
opacity: 0; opacity: 0;
transform: translateY(20rpx); transform: translateY(20rpx) scale(0.92);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0) scale(1);
} }
} }
@@ -136,45 +206,88 @@ const goto = (url: string) => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16rpx; gap: 16rpx;
background: #fff; background: rgba(255, 255, 255, 0.96);
padding: 16rpx 24rpx; padding: 14rpx 20rpx 14rpx 14rpx;
border-radius: 32rpx; border-radius: 999rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08); box-shadow:
min-width: 240rpx; 0 8rpx 24rpx rgba(15, 23, 42, 0.1),
0 1rpx 2rpx rgba(15, 23, 42, 0.06);
min-width: 280rpx;
transition: transform 0.15s;
backdrop-filter: blur(12rpx);
&:active { &:active {
transform: scale(0.97);
background: #f0fdf4; background: #f0fdf4;
} }
}
.menu-icon { .menu-icon {
width: 64rpx; width: 64rpx;
height: 64rpx; height: 64rpx;
border-radius: 50%; border-radius: 50%;
background: linear-gradient(135deg, #22c55e, #16a34a); display: flex;
display: flex; align-items: center;
align-items: center; justify-content: center;
justify-content: center; flex-shrink: 0;
font-size: 32rpx; box-shadow: 0 2rpx 8rpx rgba(16, 185, 129, 0.32);
&.metronome-icon { &--train {
background: linear-gradient(135deg, #6366f1, #4f46e5); background: linear-gradient(140deg, $brand-light, $brand-deep);
}
} }
&--metro {
.menu-info { background: linear-gradient(140deg, #fbbf24, #d97706);
display: flex; box-shadow: 0 2rpx 8rpx rgba(245, 158, 11, 0.36);
flex-direction: column;
gap: 4rpx; gap: 4rpx;
} }
}
.menu-title { .menu-icon-text {
font-size: 28rpx; font-size: 32rpx;
color: #111827; line-height: 1;
font-weight: 600; }
}
.menu-sub { /* 节拍器小 icon: 3 根高低柱模拟均衡器/节拍 */
font-size: 20rpx; .metro-bar {
color: #9ca3af; width: 4rpx;
} background: #fff;
border-radius: 2rpx;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18);
}
.metro-bar-1 {
height: 16rpx;
}
.metro-bar-2 {
height: 28rpx;
}
.metro-bar-3 {
height: 22rpx;
}
.menu-info {
display: flex;
flex-direction: column;
gap: 2rpx;
flex: 1;
}
.menu-title {
font-size: 28rpx;
color: #0f172a;
font-weight: 700;
letter-spacing: 1rpx;
}
.menu-sub {
font-size: 20rpx;
color: #94a3b8;
letter-spacing: 0.5rpx;
}
.menu-arrow {
font-size: 32rpx;
color: #cbd5e1;
font-weight: 300;
line-height: 1;
margin-left: 4rpx;
} }
</style> </style>