fix: 修复 VoiceController Map.of 兼容性 + ExploreController 参数不匹配

- VoiceController: Map.of() -> Collections.singletonMap() 兼容 Java 8
- ExploreController: 补齐 takeoutService.roll() 缺失的 taste/priceRange/allergies 参数

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
王鹏
2026-05-08 20:02:27 +08:00
commit 802b4ba229
98 changed files with 5761 additions and 0 deletions

View File

@@ -0,0 +1,304 @@
/*
* 开盒动效 · 三幕分镜
* 参考 doc/box.md
*
* Act 1 (0400ms): 光晕扩散 + 盒子震动 + 背景变暗
* Act 2 (4001000ms): 盒盖飞起 + 金色光芒 + 粒子溅射 + 白色闪屏
* Act 3 (10001800ms): 内容弹性弹出 + 彩带纸屑
*
* 总时长约 1.82.0s
*/
/* ═══ 覆盖层 ═══ */
.animation-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.animation-overlay.visible {
pointer-events: auto;
}
/* ═══ 背景变暗 ═══ */
.bg-dimmer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.55);
opacity: 0;
transition: opacity 0.4s ease-out;
}
.bg-dimmer.active {
opacity: 1;
}
/* ════════════════════════
Act 1 · 光晕扩散 (0400ms)
════════════════════════ */
.act1-glow-ring {
position: absolute;
width: 320rpx;
height: 320rpx;
border-radius: 50%;
opacity: 0;
transform: scale(0.15);
}
.act1-glow-ring.active {
animation: glowExpand 0.5s ease-out forwards;
}
.glow-inner {
width: 100%;
height: 100%;
border-radius: 50%;
}
/* 模式颜色 */
.box-takeout .glow-inner {
background: radial-gradient(circle, #E8693B 0%, rgba(232,105,59,0.4) 35%, transparent 70%);
}
.box-fridge .glow-inner {
background: radial-gradient(circle, #4CAF50 0%, rgba(76,175,80,0.4) 35%, transparent 70%);
}
.box-explore .glow-inner {
background: radial-gradient(circle, #7C3AED 0%, rgba(124,58,237,0.4) 35%, transparent 70%);
}
@keyframes glowExpand {
0% { opacity: 0.9; transform: scale(0.15); }
50% { opacity: 0.6; transform: scale(1.5); }
100% { opacity: 0; transform: scale(3.2); }
}
/* ════════════════════════
Act 1 · 盒子震动
════════════════════════ */
.box-stage {
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
opacity: 0;
transform: scale(0.85);
transition: opacity 0.15s ease, transform 0.15s ease;
}
.box-stage.active {
opacity: 1;
transform: scale(1);
}
/* ── 盒盖 ── */
.box-lid {
position: relative;
z-index: 3;
margin-bottom: -12rpx;
transition: transform 0.35s ease-in, opacity 0.35s ease-in;
transition-delay: 0.05s;
}
.lid-top {
width: 120rpx;
height: 40rpx;
background: linear-gradient(180deg, #F5A623 0%, #E8961A 100%);
border-radius: 20rpx 20rpx 4rpx 4rpx;
box-shadow: 0 4rpx 16rpx rgba(200, 120, 20, 0.4);
}
.box-lid.active {
transform: translateY(-80rpx) rotate(-8deg);
opacity: 0;
}
/* ── 金色光芒 ── */
.golden-light {
position: absolute;
z-index: 1;
width: 200rpx;
height: 200rpx;
border-radius: 50%;
opacity: 0;
transform: scale(0.3);
}
.box-takeout .golden-light {
background: radial-gradient(circle, #FFD54F 0%, rgba(255, 180, 50, 0.6) 30%, transparent 65%);
}
.box-fridge .golden-light {
background: radial-gradient(circle, #A5D6A7 0%, rgba(76, 175, 80, 0.5) 30%, transparent 65%);
}
.box-explore .golden-light {
background: radial-gradient(circle, #CE93D8 0%, rgba(124, 58, 237, 0.5) 30%, transparent 65%);
}
.golden-light.active {
animation: goldenBurst 0.6s ease-out forwards;
}
@keyframes goldenBurst {
0% { opacity: 0; transform: scale(0.3); }
30% { opacity: 1; transform: scale(1.8); }
100% { opacity: 0; transform: scale(3.5); }
}
/* ── 盒体Act2 压缩反弹) ── */
.box-body {
position: relative;
z-index: 2;
transition: transform 0.25s ease-out;
}
.box-body.compressed {
animation: bodyCompress 0.3s ease-out;
}
.box-emoji {
font-size: 160rpx;
display: block;
}
@keyframes bodyCompress {
0% { transform: scaleY(1); }
40% { transform: scaleY(0.85); }
100% { transform: scaleY(1); }
}
/* ── 粒子溅射Act2 ── */
.spark-particles {
position: absolute;
z-index: 0;
top: 50%; left: 50%;
width: 0; height: 0;
pointer-events: none;
}
.spark-particles.active .spark {
animation: sparkBurst 0.7s ease-out forwards;
}
.spark {
position: absolute;
font-size: 32rpx;
opacity: 0;
}
.spark.s1 { animation-delay: 0s !important; }
.spark.s2 { animation-delay: 0.06s !important; }
.spark.s3 { animation-delay: 0.12s !important; }
.spark.s4 { animation-delay: 0.18s !important; }
@keyframes sparkBurst {
0% { opacity: 1; transform: translate(0, 0) scale(0.5); }
100% { opacity: 0; transform: translate(var(--sx, 60rpx), var(--sy, -80rpx)) scale(1.2); }
}
.box-takeout .spark { --sx: 70rpx; --sy: -90rpx; }
.box-takeout .spark.s2 { --sx: -60rpx; --sy: -70rpx; }
.box-takeout .spark.s3 { --sx: 50rpx; --sy: -100rpx; }
.box-takeout .spark.s4 { --sx: -80rpx; --sy: -80rpx; }
.box-fridge .spark { --sx: 70rpx; --sy: -90rpx; }
.box-fridge .spark.s2 { --sx: -60rpx; --sy: -70rpx; }
.box-fridge .spark.s3 { --sx: 50rpx; --sy: -100rpx; }
.box-fridge .spark.s4 { --sx: -80rpx; --sy: -80rpx; }
.box-explore .spark { --sx: 70rpx; --sy: -90rpx; }
.box-explore .spark.s2 { --sx: -60rpx; --sy: -70rpx; }
.box-explore .spark.s3 { --sx: 50rpx; --sy: -100rpx; }
.box-explore .spark.s4 { --sx: -80rpx; --sy: -80rpx; }
/* ════════════════════════
Act 2 · 白色闪屏 (6001000ms)
════════════════════════ */
.act2-mask {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: #FFFFFF;
opacity: 0;
pointer-events: none;
}
.act2-mask.active {
animation: maskFlash 0.5s ease-in-out forwards;
animation-delay: 0.2s;
}
@keyframes maskFlash {
0% { opacity: 0; }
45% { opacity: 0.92; }
100% { opacity: 0; }
}
/* ════════════════════════
Act 3 · 内容弹性弹出 (10001800ms)
════════════════════════ */
.act3-content {
position: relative;
z-index: 20;
opacity: 0;
transform: scale(0.85) translateY(30rpx);
}
.act3-content.active {
animation: contentReveal 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
.content-inner {
display: flex;
flex-direction: column;
align-items: center;
}
@keyframes contentReveal {
0% { opacity: 0; transform: scale(0.85) translateY(30rpx); }
100% { opacity: 1; transform: scale(1) translateY(0); }
}
/* ════════════════════════
Act 3 · 彩带纸屑 (1600ms+)
════════════════════════ */
.confetti-stage {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
overflow: hidden;
}
.confetti-stage.active .confetti {
animation: confettiDrop 1.4s ease-in forwards;
}
.confetti {
position: absolute;
top: -20rpx;
left: var(--x, 20%);
width: 14rpx;
height: 14rpx;
border-radius: 3rpx;
background: var(--c, #E8693B);
opacity: 0;
animation-delay: var(--d, 0s);
}
@keyframes confettiDrop {
0% { opacity: 1; transform: translateY(0) rotate(0deg) scale(1); }
30% { opacity: 0.9; transform: translateY(280rpx) rotate(180deg) scale(0.7); }
60% { opacity: 0.5; transform: translateY(600rpx) rotate(400deg) scale(0.4); }
100% { opacity: 0; transform: translateY(1000rpx) rotate(720deg) scale(0.1); }
}