2026-06-14 16:49:18 +08:00
|
|
|
|
<!-- =============================================================================
|
|
|
|
|
|
企微IT智能服务台 — H5用户端「呼叫坐席」动画弹窗
|
|
|
|
|
|
=============================================================================
|
|
|
|
|
|
流程(简化版):
|
|
|
|
|
|
1. 按钮出现后点击 → 弹出此弹窗
|
|
|
|
|
|
2. 立即随机播放趣味动画 + 话术,同时发 shake 请求
|
|
|
|
|
|
3. 发送成功 → 显示成功提示,3秒后自动关闭
|
|
|
|
|
|
|
|
|
|
|
|
注意:问题描述已在聊天中完成(AI 实质性回复 >= 3 轮),弹窗不需要再录入。
|
|
|
|
|
|
|
|
|
|
|
|
七种动画场景(随机选一种):
|
|
|
|
|
|
1. 🙋 招手 — "看这里!看我这里...我有个问题!"
|
|
|
|
|
|
2. 🪑 拍桌子 — "快快快!我等不及了!"
|
|
|
|
|
|
3. 💀 劈稻草人 — "这个问题不解决我就要原地爆炸了💥"
|
|
|
|
|
|
4. 🍉 砍西瓜 — "IT!救我!这个问题卡住了🍉"
|
|
|
|
|
|
5. 🔔 摇铃铛 — "叮叮叮!有人吗!IT 在线吗!"
|
|
|
|
|
|
6. 💣 大炮发射 — "开炮!这个问题必须解决了!"
|
|
|
|
|
|
7. 🚀 导弹发射 — "发射!紧急呼叫 IT 特种部队!"
|
|
|
|
|
|
============================================================================= -->
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<Teleport to="body">
|
|
|
|
|
|
<Transition name="modal-fade">
|
|
|
|
|
|
<div v-if="visible" class="call-modal__overlay" @click.self="handleClose">
|
|
|
|
|
|
<Transition name="modal-zoom" appear>
|
|
|
|
|
|
<div class="call-modal call-modal--compact" v-if="visible">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ========== 动画场景 + 话术 ========== -->
|
|
|
|
|
|
<div class="call-modal__step">
|
|
|
|
|
|
<div class="call-modal__header">
|
|
|
|
|
|
<span class="call-modal__icon">🔔</span>
|
2026-06-16 10:07:42 +08:00
|
|
|
|
<h3>呼叫人工坐席帮助...</h3>
|
2026-06-14 16:49:18 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="call-modal__body call-modal__body--center">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景SVG动画区域(根据 selectedScene 渲染对应场景) -->
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景1:招手 🙋 -->
|
|
|
|
|
|
<div v-if="selectedScene === 1" class="scene scene--hand">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="140" width="200" height="40" fill="#E8EAF6"/>
|
|
|
|
|
|
<rect x="45" y="75" width="110" height="65" rx="10" fill="#ECEFF1"/>
|
|
|
|
|
|
<rect x="50" y="80" width="100" height="55" rx="8" fill="#CFD8DC"/>
|
|
|
|
|
|
<rect x="55" y="85" width="40" height="25" rx="3" fill="#90CAF9"/>
|
|
|
|
|
|
<rect x="100" y="85" width="40" height="25" rx="3" fill="#90CAF9"/>
|
|
|
|
|
|
<circle cx="135" cy="70" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<circle cx="130" cy="67" r="2" fill="#333"/>
|
|
|
|
|
|
<circle cx="140" cy="67" r="2" fill="#333"/>
|
|
|
|
|
|
<path d="M130 75 Q135 79 140 75" stroke="#333" stroke-width="1.5" fill="none"/>
|
|
|
|
|
|
<rect x="12" y="72" width="30" height="50" rx="8" fill="#F5C6A0"/>
|
|
|
|
|
|
<g class="hand-arm">
|
|
|
|
|
|
<line x1="42" y1="80" x2="55" y2="55" stroke="#F5C6A0" stroke-width="8" stroke-linecap="round"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="bubble-float">
|
|
|
|
|
|
<rect x="55" y="36" width="100" height="24" rx="12" fill="#FF9800"/>
|
|
|
|
|
|
<text x="105" y="52" text-anchor="middle" fill="white" font-size="11" font-weight="bold">看这里!🙋</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景2:拍桌子 🪑 -->
|
|
|
|
|
|
<div v-else-if="selectedScene === 2" class="scene scene--pound">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="140" width="200" height="40" fill="#D7CCC8"/>
|
|
|
|
|
|
<rect x="30" y="105" width="140" height="35" rx="6" fill="#8D6E63"/>
|
|
|
|
|
|
<rect x="35" y="110" width="130" height="25" rx="4" fill="#A1887F"/>
|
|
|
|
|
|
<rect x="20" y="75" width="35" height="50" rx="8" fill="#795548"/>
|
|
|
|
|
|
<circle cx="37" cy="60" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<g class="pound-fists">
|
|
|
|
|
|
<circle cx="75" cy="98" r="9" fill="#F5C6A0"/>
|
|
|
|
|
|
<circle cx="105" cy="98" r="9" fill="#F5C6A0"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="splash-lines">
|
|
|
|
|
|
<line x1="68" y1="90" x2="62" y2="82" stroke="#FF5722" stroke-width="2"/>
|
|
|
|
|
|
<line x1="80" y1="88" x2="80" y2="78" stroke="#FF5722" stroke-width="2"/>
|
|
|
|
|
|
<line x1="90" y1="88" x2="95" y2="78" stroke="#FF5722" stroke-width="2"/>
|
|
|
|
|
|
<line x1="110" y1="90" x2="110" y2="80" stroke="#FF5722" stroke-width="2"/>
|
|
|
|
|
|
<line x1="120" y1="90" x2="126" y2="82" stroke="#FF5722" stroke-width="2"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="bubble-float">
|
|
|
|
|
|
<rect x="90" y="36" width="100" height="24" rx="12" fill="#FF5722"/>
|
|
|
|
|
|
<text x="140" y="52" text-anchor="middle" fill="white" font-size="11" font-weight="bold">快快快!🪑</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景3-7略(同原版,保留全部7个场景的SVG) -->
|
|
|
|
|
|
<!-- 场景3:劈稻草人 💀 -->
|
|
|
|
|
|
<div v-else-if="selectedScene === 3" class="scene scene--scarecrow">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="140" width="200" height="40" fill="#C8E6C9"/>
|
|
|
|
|
|
<rect x="145" y="100" width="6" height="40" fill="#795548"/>
|
|
|
|
|
|
<rect x="133" y="95" width="30" height="5" rx="2" fill="#795548"/>
|
|
|
|
|
|
<circle cx="148" cy="85" r="14" fill="#FFE082"/>
|
|
|
|
|
|
<line x1="148" y1="74" x2="148" y2="70" stroke="#333" stroke-width="1.5"/>
|
|
|
|
|
|
<rect x="25" y="72" width="30" height="50" rx="8" fill="#1565C0"/>
|
|
|
|
|
|
<circle cx="40" cy="57" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<g class="slash-blade">
|
|
|
|
|
|
<rect x="55" y="40" width="6" height="50" rx="2" fill="#B0BEC5"/>
|
|
|
|
|
|
<rect x="52" y="38" width="12" height="8" rx="2" fill="#90A4AE"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="explosion-effect">
|
|
|
|
|
|
<circle cx="148" cy="85" r="10" fill="#FF5722" opacity="0"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="bubble-float">
|
|
|
|
|
|
<rect x="5" y="5" width="105" height="24" rx="12" fill="#D32F2F"/>
|
|
|
|
|
|
<text x="57" y="21" text-anchor="middle" fill="white" font-size="10" font-weight="bold">我要爆炸了💥</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景4:砍西瓜 🍉 -->
|
|
|
|
|
|
<div v-else-if="selectedScene === 4" class="scene scene--watermelon">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="140" width="200" height="40" fill="#C8E6C9"/>
|
|
|
|
|
|
<g class="melon">
|
|
|
|
|
|
<ellipse cx="140" cy="125" rx="25" ry="18" fill="#4CAF50"/>
|
|
|
|
|
|
<ellipse cx="140" cy="122" rx="18" ry="10" fill="#F44336"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<rect x="25" y="75" width="30" height="47" rx="8" fill="#1565C0"/>
|
|
|
|
|
|
<circle cx="40" cy="60" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<g class="knife">
|
|
|
|
|
|
<rect x="55" y="42" width="6" height="45" rx="2" fill="#B0BEC5"/>
|
|
|
|
|
|
<rect x="52" y="40" width="12" height="8" rx="2" fill="#90A4AE"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="juice-splash">
|
|
|
|
|
|
<circle cx="130" cy="110" r="2" fill="#F44336" opacity="0"/>
|
|
|
|
|
|
<circle cx="145" cy="105" r="2.5" fill="#F44336" opacity="0"/>
|
|
|
|
|
|
<circle cx="155" cy="112" r="1.5" fill="#F44336" opacity="0"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="bubble-float">
|
|
|
|
|
|
<rect x="5" y="5" width="105" height="24" rx="12" fill="#43A047"/>
|
|
|
|
|
|
<text x="57" y="21" text-anchor="middle" fill="white" font-size="10" font-weight="bold">IT救我!🍉</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景5:摇传菜铃 🔔 -->
|
|
|
|
|
|
<div v-else-if="selectedScene === 5" class="scene scene--bell">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="140" width="200" height="40" fill="#FFF3E0"/>
|
|
|
|
|
|
<rect x="25" y="70" width="30" height="52" rx="8" fill="#FF9800"/>
|
|
|
|
|
|
<circle cx="40" cy="55" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<g class="bell-left">
|
|
|
|
|
|
<path d="M52 72 Q52 58 60 50 Q68 42 72 50 Q76 58 74 72" fill="#FFD54F" stroke="#FFA000" stroke-width="1.5"/>
|
|
|
|
|
|
<ellipse cx="63" cy="72" rx="9" ry="3" fill="#FFA000"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="bell-right">
|
|
|
|
|
|
<path d="M92 72 Q92 58 100 50 Q108 42 112 50 Q116 58 114 72" fill="#FFD54F" stroke="#FFA000" stroke-width="1.5"/>
|
|
|
|
|
|
<ellipse cx="103" cy="72" rx="9" ry="3" fill="#FFA000"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="sound-waves">
|
|
|
|
|
|
<circle cx="160" cy="55" r="10" fill="none" stroke="#FF9800" stroke-width="1.5" opacity="0"/>
|
|
|
|
|
|
<circle cx="160" cy="55" r="16" fill="none" stroke="#FF9800" stroke-width="1.5" opacity="0"/>
|
|
|
|
|
|
<circle cx="160" cy="55" r="22" fill="none" stroke="#FF9800" stroke-width="1" opacity="0"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="bubble-float">
|
|
|
|
|
|
<rect x="5" y="5" width="105" height="24" rx="12" fill="#FF9800"/>
|
|
|
|
|
|
<text x="57" y="21" text-anchor="middle" fill="white" font-size="10" font-weight="bold">叮叮叮!有人吗</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景6:大炮发射 💣 -->
|
|
|
|
|
|
<div v-else-if="selectedScene === 6" class="scene scene--cannon">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="145" width="200" height="35" fill="#D7CCC8"/>
|
|
|
|
|
|
<rect x="55" y="132" width="50" height="16" rx="3" fill="#5D4037"/>
|
|
|
|
|
|
<circle cx="80" cy="138" r="9" fill="#4E342E"/>
|
|
|
|
|
|
<g class="cannon-barrel">
|
|
|
|
|
|
<rect x="58" y="110" width="55" height="14" rx="4" fill="#616161"/>
|
|
|
|
|
|
<rect x="108" y="108" width="12" height="18" rx="3" fill="#757575"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="fuse">
|
|
|
|
|
|
<path d="M58 117 Q52 115 48 118 Q44 121 40 117" stroke="#FF9800" stroke-width="2" fill="none"/>
|
|
|
|
|
|
<circle cx="38" cy="116" r="3" fill="#FF5722" class="fuse-spark"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="cannonball">
|
|
|
|
|
|
<circle cx="120" cy="105" r="7" fill="#37474F"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="explosion">
|
|
|
|
|
|
<circle cx="170" cy="60" r="18" fill="#FF5722" opacity="0" class="blast-main"/>
|
|
|
|
|
|
<circle cx="170" cy="60" r="12" fill="#FFC107" opacity="0" class="blast-inner"/>
|
|
|
|
|
|
<line x1="170" y1="40" x2="170" y2="30" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-1"/>
|
|
|
|
|
|
<line x1="185" y1="48" x2="192" y2="42" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-2"/>
|
|
|
|
|
|
<line x1="188" y1="60" x2="196" y2="60" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-3"/>
|
|
|
|
|
|
<line x1="185" y1="72" x2="192" y2="78" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-4"/>
|
|
|
|
|
|
<line x1="170" y1="80" x2="170" y2="90" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-5"/>
|
|
|
|
|
|
<line x1="155" y1="72" x2="148" y2="78" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-6"/>
|
|
|
|
|
|
<line x1="152" y1="60" x2="144" y2="60" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-7"/>
|
|
|
|
|
|
<line x1="155" y1="48" x2="148" y2="42" stroke="#FF5722" stroke-width="3" opacity="0" class="blast-ray ray-8"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="target">
|
|
|
|
|
|
<circle cx="170" cy="55" r="14" fill="none" stroke="#F44336" stroke-width="2"/>
|
|
|
|
|
|
<circle cx="170" cy="55" r="8" fill="none" stroke="#F44336" stroke-width="1.5"/>
|
|
|
|
|
|
<circle cx="170" cy="55" r="2" fill="#F44336"/>
|
|
|
|
|
|
<text x="170" y="38" text-anchor="middle" fill="#F44336" font-size="9" font-weight="bold">BUG</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<rect x="5" y="75" width="30" height="57" rx="7" fill="#795548"/>
|
|
|
|
|
|
<circle cx="20" cy="60" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<circle cx="15" cy="57" r="2" fill="#333"/>
|
|
|
|
|
|
<circle cx="25" cy="57" r="2" fill="#333"/>
|
|
|
|
|
|
<path d="M14 66 Q20 70 26 66" stroke="#333" stroke-width="1.5" fill="none"/>
|
|
|
|
|
|
<line x1="35" y1="85" x2="48" y2="100" stroke="#F5C6A0" stroke-width="7" stroke-linecap="round" class="gunner-arm"/>
|
|
|
|
|
|
<g class="bubble-float">
|
|
|
|
|
|
<rect x="5" y="5" width="95" height="24" rx="12" fill="#FF5722"/>
|
|
|
|
|
|
<text x="52" y="21" text-anchor="middle" fill="white" font-size="11" font-weight="bold">开炮!💣</text>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 场景7:导弹发射 🚀 -->
|
|
|
|
|
|
<div v-else-if="selectedScene === 7" class="scene scene--missile">
|
|
|
|
|
|
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg" class="scene__svg">
|
|
|
|
|
|
<rect x="0" y="0" width="200" height="140" fill="#E3F2FD"/>
|
|
|
|
|
|
<rect x="0" y="140" width="200" height="40" fill="#BDBDBD"/>
|
|
|
|
|
|
<rect x="85" y="130" width="30" height="12" rx="2" fill="#616161"/>
|
|
|
|
|
|
<rect x="88" y="142" width="8" height="10" fill="#757575"/>
|
|
|
|
|
|
<rect x="104" y="142" width="8" height="10" fill="#757575"/>
|
|
|
|
|
|
<g class="missile-body">
|
|
|
|
|
|
<rect x="94" y="50" width="12" height="65" rx="4" fill="#E0E0E0"/>
|
|
|
|
|
|
<path d="M94 50 L100 35 L106 50 Z" fill="#F44336"/>
|
|
|
|
|
|
<polygon points="94,108 86,115 94,112" fill="#F44336"/>
|
|
|
|
|
|
<polygon points="106,108 114,115 106,112" fill="#F44336"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="rocket-flame">
|
|
|
|
|
|
<ellipse cx="100" cy="118" rx="4" ry="10" fill="#FF9800" class="flame-outer"/>
|
|
|
|
|
|
<ellipse cx="100" cy="116" rx="2" ry="6" fill="#FFEB3B" class="flame-inner"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="smoke">
|
|
|
|
|
|
<circle cx="88" cy="145" r="8" fill="#CFD8DC" opacity="0" class="smoke-puff puff-1"/>
|
|
|
|
|
|
<circle cx="112" cy="145" r="8" fill="#CFD8DC" opacity="0" class="smoke-puff puff-2"/>
|
|
|
|
|
|
<circle cx="100" cy="155" r="10" fill="#CFD8DC" opacity="0" class="smoke-puff puff-3"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<g class="trail">
|
|
|
|
|
|
<path d="M100 130 L100 145" stroke="#FFC107" stroke-width="2" opacity="0" class="trail-line"/>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
<rect x="15" y="80" width="28" height="52" rx="7" fill="#1565C0"/>
|
|
|
|
|
|
<circle cx="29" cy="65" r="16" fill="#F5C6A0"/>
|
|
|
|
|
|
<rect x="18" y="58" width="22" height="8" rx="3" fill="#333" opacity="0.7"/>
|
|
|
|
|
|
<circle cx="24" cy="62" r="1.5" fill="#FFF"/>
|
|
|
|
|
|
<circle cx="34" cy="62" r="1.5" fill="#FFF"/>
|
|
|
|
|
|
<ellipse cx="29" cy="72" rx="4" ry="3" fill="#333"/>
|
|
|
|
|
|
<line x1="43" y1="90" x2="60" y2="100" stroke="#F5C6A0" stroke-width="7" stroke-linecap="round"/>
|
|
|
|
|
|
<rect x="55" y="105" width="15" height="20" rx="3" fill="#424242"/>
|
|
|
|
|
|
<circle cx="62.5" cy="112" r="3" fill="#F44336" class="launch-button"/>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 话术文字 -->
|
|
|
|
|
|
<div class="call-modal__speech-text">
|
|
|
|
|
|
<span class="call-modal__speech-emoji">{{ sceneEmoji }}</span>
|
|
|
|
|
|
<span class="call-modal__speech-content">{{ sceneText }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 发送状态 -->
|
|
|
|
|
|
<div v-if="sending" class="call-modal__sending">
|
|
|
|
|
|
<span class="call-modal__dot-flashing"></span>
|
|
|
|
|
|
<span>正在通知 IT 坐席...</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="sendSuccess" class="call-modal__success">
|
|
|
|
|
|
✅ 呼叫成功!坐席马上就来~
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 底部:关闭按钮 -->
|
|
|
|
|
|
<div class="call-modal__footer call-modal__footer--center">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-if="sendSuccess"
|
|
|
|
|
|
class="call-modal__btn call-modal__btn--primary call-modal__btn--large"
|
|
|
|
|
|
@click="handleClose"
|
|
|
|
|
|
>好的,返回聊天</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-else
|
|
|
|
|
|
class="call-modal__btn call-modal__btn--cancel"
|
|
|
|
|
|
@click="handleClose"
|
|
|
|
|
|
>取消</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Transition>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Transition>
|
|
|
|
|
|
</Teleport>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
/**
|
|
|
|
|
|
* CallAgentModal — 「呼叫坐席」动画弹窗(简化版)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 流程:打开 → 随机播放趣味动画 + 发 shake 请求 → 关闭
|
|
|
|
|
|
* 前提:用户已在聊天中描述问题,AI 已回复 >= 3 轮
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { ref, computed, watch } from 'vue'
|
|
|
|
|
|
import { useConversationStore } from '@/stores/conversation'
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
visible: boolean
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
(e: 'update:visible', value: boolean): void
|
|
|
|
|
|
(e: 'call-success'): void
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const store = useConversationStore()
|
|
|
|
|
|
|
|
|
|
|
|
// ── 状态 ──
|
|
|
|
|
|
const selectedScene = ref<number>(1)
|
|
|
|
|
|
const sending = ref<boolean>(false)
|
|
|
|
|
|
const sendSuccess = ref<boolean>(false)
|
|
|
|
|
|
|
|
|
|
|
|
// ── 场景配置 ──
|
|
|
|
|
|
const scenes = [
|
|
|
|
|
|
{ id: 1, emoji: '🙋', text: '看这里!看我这里...我有个问题!', weight: 3 },
|
|
|
|
|
|
{ id: 2, emoji: '🪑', text: '快快快!我等不及了!', weight: 3 },
|
|
|
|
|
|
{ id: 3, emoji: '💀', text: '这个问题不解决我就要原地爆炸了💥', weight: 1.5 },
|
|
|
|
|
|
{ id: 4, emoji: '🍉', text: 'IT!救我!这个问题卡住我了🍉', weight: 1.5 },
|
|
|
|
|
|
{ id: 5, emoji: '🔔', text: '叮叮叮!有人吗!IT 在线吗!', weight: 1 },
|
|
|
|
|
|
{ id: 6, emoji: '💣', text: '开炮!💣 这个问题必须解决了!', weight: 1.5 },
|
|
|
|
|
|
{ id: 7, emoji: '🚀', text: '发射!🚀 紧急呼叫 IT 特种部队!', weight: 1.5 },
|
|
|
|
|
|
] as const
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 固定使用场景5:摇铃铛 🔔
|
|
|
|
|
|
* 不再随机选择,回归统一的摇铃呼叫体验
|
|
|
|
|
|
*/
|
|
|
|
|
|
function pickScene(): number {
|
|
|
|
|
|
return 5
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const sceneText = computed(() => scenes.find(s => s.id === selectedScene.value)?.text ?? '')
|
|
|
|
|
|
const sceneEmoji = computed(() => scenes.find(s => s.id === selectedScene.value)?.emoji ?? '🙋')
|
|
|
|
|
|
|
|
|
|
|
|
// ── 弹窗打开时自动发起呼叫 ──
|
|
|
|
|
|
watch(() => props.visible, (newVal) => {
|
|
|
|
|
|
if (newVal) {
|
|
|
|
|
|
startCall()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
async function startCall(): Promise<void> {
|
|
|
|
|
|
selectedScene.value = pickScene()
|
|
|
|
|
|
sending.value = true
|
|
|
|
|
|
sendSuccess.value = false
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await store.shakeAgent()
|
|
|
|
|
|
sendSuccess.value = true
|
|
|
|
|
|
emit('call-success')
|
|
|
|
|
|
|
|
|
|
|
|
// 3秒后自动关闭
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (sendSuccess.value) handleClose()
|
|
|
|
|
|
}, 4000)
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
// 发送失败,关闭弹窗
|
|
|
|
|
|
handleClose()
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
sending.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleClose(): void {
|
|
|
|
|
|
emit('update:visible', false)
|
|
|
|
|
|
// 重置状态
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
sending.value = false
|
|
|
|
|
|
sendSuccess.value = false
|
|
|
|
|
|
}, 300)
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
弹窗容器
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.call-modal__overlay {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
background: rgba(0,0,0,0.5);
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal {
|
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
|
width: 88vw;
|
|
|
|
|
|
max-width: 360px;
|
|
|
|
|
|
max-height: 85vh;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal--compact {
|
|
|
|
|
|
max-width: 340px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__header {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 20px 16px 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__icon { font-size: 32px; }
|
|
|
|
|
|
.call-modal__header h3 {
|
|
|
|
|
|
margin: 8px 0 4px;
|
|
|
|
|
|
font-size: 17px;
|
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__body { padding: 12px 16px; }
|
|
|
|
|
|
.call-modal__body--center {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__footer {
|
|
|
|
|
|
padding: 12px 16px 20px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__footer--center { justify-content: center; }
|
|
|
|
|
|
.call-modal__btn {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
padding: 10px 24px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__btn--cancel { background: var(--bg-tertiary); color: var(--text-secondary); }
|
|
|
|
|
|
.call-modal__btn--primary { background: var(--color-warning); color: var(--bg-secondary); }
|
|
|
|
|
|
.call-modal__btn--large { padding: 12px 48px; font-size: 15px; }
|
|
|
|
|
|
.call-modal__btn:active { transform: scale(0.96); }
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
话术文字
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.call-modal__speech-text {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__speech-emoji { font-size: 22px; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
发送状态
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.call-modal__sending {
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: var(--text-tertiary);
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__dot-flashing {
|
|
|
|
|
|
width: 8px;
|
|
|
|
|
|
height: 8px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: var(--color-warning);
|
|
|
|
|
|
animation: dot-flash 0.6s infinite alternate;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes dot-flash {
|
|
|
|
|
|
0% { opacity: 0.2; }
|
|
|
|
|
|
100% { opacity: 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.call-modal__success {
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
SVG 场景容器
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.scene { margin-bottom: 4px; }
|
|
|
|
|
|
.scene__svg { width: 180px; height: 150px; display: block; margin: 0 auto; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景1:招手动画
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.hand-arm {
|
|
|
|
|
|
animation: hand-wave 0.6s ease-in-out infinite;
|
|
|
|
|
|
transform-origin: 42px 80px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes hand-wave {
|
|
|
|
|
|
0%, 100% { transform: rotate(-5deg); }
|
|
|
|
|
|
50% { transform: rotate(-30deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.bubble-float { animation: bubble-updown 2s ease-in-out infinite; }
|
|
|
|
|
|
@keyframes bubble-updown {
|
|
|
|
|
|
0%, 100% { transform: translateY(0); }
|
|
|
|
|
|
50% { transform: translateY(-6px); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景2:拍桌子动画
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.pound-fists {
|
|
|
|
|
|
animation: desk-pound 0.4s ease-in-out infinite alternate;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes desk-pound {
|
|
|
|
|
|
0% { transform: translateY(0); }
|
|
|
|
|
|
100% { transform: translateY(-8px); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.scene--pound {
|
|
|
|
|
|
animation: desk-shake 0.4s ease-in-out infinite alternate;
|
|
|
|
|
|
transform-origin: center bottom;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes desk-shake {
|
|
|
|
|
|
0%, 100% { transform: translateX(0); }
|
|
|
|
|
|
25% { transform: translateX(-3px); }
|
|
|
|
|
|
75% { transform: translateX(3px); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.splash-lines line:nth-child(1) { animation: splash 0.6s ease-out infinite; }
|
|
|
|
|
|
.splash-lines line:nth-child(2) { animation: splash 0.6s ease-out 0.1s infinite; }
|
|
|
|
|
|
.splash-lines line:nth-child(3) { animation: splash 0.6s ease-out 0.2s infinite; }
|
|
|
|
|
|
.splash-lines line:nth-child(4) { animation: splash 0.6s ease-out 0.15s infinite; }
|
|
|
|
|
|
.splash-lines line:nth-child(5) { animation: splash 0.6s ease-out 0.25s infinite; }
|
|
|
|
|
|
@keyframes splash {
|
|
|
|
|
|
0% { opacity: 1; transform: translate(0,0); }
|
|
|
|
|
|
100% { opacity: 0; transform: translate(var(--sx, 0), -12px); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景3:劈稻草人
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.slash-blade {
|
|
|
|
|
|
animation: blade-slash 1.0s ease-in-out infinite;
|
|
|
|
|
|
transform-origin: 58px 38px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes blade-slash {
|
|
|
|
|
|
0%, 15% { transform: rotate(-60deg); }
|
|
|
|
|
|
30%, 55% { transform: rotate(20deg); }
|
|
|
|
|
|
70%, 100% { transform: rotate(-60deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.explosion-effect circle {
|
|
|
|
|
|
animation: boom-flash 1.0s ease-in-out infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes boom-flash {
|
|
|
|
|
|
0%, 50% { opacity: 0; transform: scale(0.5); }
|
|
|
|
|
|
55% { opacity: 0.9; transform: scale(2); }
|
|
|
|
|
|
65% { opacity: 0; transform: scale(3); }
|
|
|
|
|
|
100% { opacity: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景4:砍西瓜
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.knife {
|
|
|
|
|
|
animation: knife-chop 0.8s ease-in-out infinite;
|
|
|
|
|
|
transform-origin: 58px 40px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes knife-chop {
|
|
|
|
|
|
0%, 20% { transform: rotate(-50deg); }
|
|
|
|
|
|
40%, 60% { transform: rotate(15deg); }
|
|
|
|
|
|
80%, 100% { transform: rotate(-50deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.juice-splash circle:nth-child(1) { animation: juice-splash 0.8s ease-out 0.4s infinite; }
|
|
|
|
|
|
.juice-splash circle:nth-child(2) { animation: juice-splash 0.8s ease-out 0.45s infinite; }
|
|
|
|
|
|
.juice-splash circle:nth-child(3) { animation: juice-splash 0.8s ease-out 0.5s infinite; }
|
|
|
|
|
|
@keyframes juice-splash {
|
|
|
|
|
|
0% { opacity: 0; transform: translate(0,0) scale(0); }
|
|
|
|
|
|
50% { opacity: 1; transform: translate(-5px, -10px) scale(1); }
|
|
|
|
|
|
100% { opacity: 0; transform: translate(-10px, -20px) scale(0.5); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.melon {
|
|
|
|
|
|
animation: melon-shake 0.8s ease-in-out infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes melon-shake {
|
|
|
|
|
|
0%, 37% { transform: translate(0,0); }
|
|
|
|
|
|
42%, 58% { transform: translate(-3px, 2px); }
|
|
|
|
|
|
62% { transform: translate(0,0); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景5:摇传菜铃
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.bell-left {
|
|
|
|
|
|
animation: bell-ring-left 0.5s ease-in-out infinite;
|
|
|
|
|
|
transform-origin: 63px 58px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bell-right {
|
|
|
|
|
|
animation: bell-ring-right 0.5s ease-in-out 0.25s infinite;
|
|
|
|
|
|
transform-origin: 103px 58px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes bell-ring-left {
|
|
|
|
|
|
0%, 100% { transform: rotate(0); }
|
|
|
|
|
|
50% { transform: rotate(-12deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes bell-ring-right {
|
|
|
|
|
|
0%, 100% { transform: rotate(0); }
|
|
|
|
|
|
50% { transform: rotate(12deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.sound-waves circle:nth-child(1) { animation: wave-expand 1s ease-out infinite; }
|
|
|
|
|
|
.sound-waves circle:nth-child(2) { animation: wave-expand 1s ease-out 0.3s infinite; }
|
|
|
|
|
|
.sound-waves circle:nth-child(3) { animation: wave-expand 1s ease-out 0.6s infinite; }
|
|
|
|
|
|
@keyframes wave-expand {
|
|
|
|
|
|
0% { opacity: 1; stroke-width: 2; }
|
|
|
|
|
|
100% { opacity: 0; stroke-width: 0.5; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景6:大炮发射
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.cannon-barrel {
|
|
|
|
|
|
animation: cannon-recoil 1.2s ease-in-out infinite;
|
|
|
|
|
|
transform-origin: 80px 117px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes cannon-recoil {
|
|
|
|
|
|
0%, 15% { transform: translateX(0); }
|
|
|
|
|
|
20%, 25% { transform: translateX(-8px); }
|
|
|
|
|
|
30%, 100% { transform: translateX(0); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.fuse-spark {
|
|
|
|
|
|
animation: spark-flicker 0.15s ease-in-out infinite alternate;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes spark-flicker {
|
|
|
|
|
|
0% { r: 2; fill: #FF5722; opacity: 0.6; }
|
|
|
|
|
|
100% { r: 3.5; fill: #FFEB3B; opacity: 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.cannonball {
|
|
|
|
|
|
animation: cannonball-fly 1.2s ease-in-out infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes cannonball-fly {
|
|
|
|
|
|
0%, 25% { transform: translate(0, 0); opacity: 0; }
|
|
|
|
|
|
28% { transform: translate(15px, -20px); opacity: 1; }
|
|
|
|
|
|
50% { transform: translate(50px, -48px); opacity: 1; }
|
|
|
|
|
|
100% { transform: translate(50px, -48px); opacity: 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.blast-main { animation: blast-appear 1.2s ease-in-out infinite; }
|
|
|
|
|
|
@keyframes blast-appear {
|
|
|
|
|
|
0%, 55% { opacity: 0; transform: scale(0); }
|
|
|
|
|
|
58% { opacity: 1; transform: scale(1.5); }
|
|
|
|
|
|
65% { opacity: 0.9; transform: scale(1.2); }
|
|
|
|
|
|
75% { opacity: 0; transform: scale(1.8); }
|
|
|
|
|
|
100% { opacity: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.blast-inner { animation: blast-inner 1.2s ease-in-out infinite; }
|
|
|
|
|
|
@keyframes blast-inner {
|
|
|
|
|
|
0%, 58% { opacity: 0; transform: scale(0); }
|
|
|
|
|
|
62% { opacity: 1; transform: scale(1); }
|
|
|
|
|
|
68% { opacity: 0.7; transform: scale(1.3); }
|
|
|
|
|
|
75% { opacity: 0; }
|
|
|
|
|
|
100% { opacity: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.blast-ray { animation: blast-ray 1.2s ease-in-out infinite; }
|
|
|
|
|
|
.ray-1, .ray-5 { animation-delay: 0.55s; }
|
|
|
|
|
|
.ray-2, .ray-6 { animation-delay: 0.56s; }
|
|
|
|
|
|
.ray-3, .ray-7 { animation-delay: 0.57s; }
|
|
|
|
|
|
.ray-4, .ray-8 { animation-delay: 0.58s; }
|
|
|
|
|
|
@keyframes blast-ray {
|
|
|
|
|
|
0%, 60% { opacity: 0; }
|
|
|
|
|
|
62% { opacity: 1; }
|
|
|
|
|
|
70% { opacity: 0; }
|
|
|
|
|
|
100% { opacity: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.target { animation: target-shake 1.2s ease-in-out infinite; }
|
|
|
|
|
|
@keyframes target-shake {
|
|
|
|
|
|
0%, 56% { transform: translate(0,0) rotate(0); }
|
|
|
|
|
|
60% { transform: translate(3px,-3px) rotate(5deg); }
|
|
|
|
|
|
64% { transform: translate(-3px,2px) rotate(-5deg); }
|
|
|
|
|
|
68% { transform: translate(0,0) rotate(0); }
|
|
|
|
|
|
100% { transform: translate(0,0) rotate(0); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.gunner-arm {
|
|
|
|
|
|
animation: gunner-arm 1.2s ease-in-out infinite;
|
|
|
|
|
|
transform-origin: 35px 85px;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes gunner-arm {
|
|
|
|
|
|
0%, 25% { transform: rotate(0deg); }
|
|
|
|
|
|
28% { transform: rotate(15deg); }
|
|
|
|
|
|
35% { transform: rotate(0deg); }
|
|
|
|
|
|
100% { transform: rotate(0deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
场景7:导弹发射
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.missile-body { animation: missile-launch 1.5s ease-in-out infinite; }
|
|
|
|
|
|
@keyframes missile-launch {
|
|
|
|
|
|
0%, 10% { transform: translateY(0); }
|
|
|
|
|
|
15%, 70% { transform: translateY(-80px); }
|
|
|
|
|
|
80%, 100% { transform: translateY(-80px); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.rocket-flame { animation: missile-launch 1.5s ease-in-out infinite; }
|
|
|
|
|
|
.flame-outer { animation: flame-pulse 0.2s ease-in-out infinite alternate; }
|
|
|
|
|
|
.flame-inner { animation: flame-pulse 0.15s ease-in-out infinite alternate; }
|
|
|
|
|
|
@keyframes flame-pulse {
|
|
|
|
|
|
0% { transform: scaleY(1); opacity: 0.8; }
|
|
|
|
|
|
100% { transform: scaleY(1.3); opacity: 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.smoke-puff { animation: smoke-burst 1.5s ease-out infinite; }
|
|
|
|
|
|
.puff-1 { animation-delay: 0.2s; }
|
|
|
|
|
|
.puff-2 { animation-delay: 0.3s; }
|
|
|
|
|
|
.puff-3 { animation-delay: 0.4s; }
|
|
|
|
|
|
@keyframes smoke-burst {
|
|
|
|
|
|
0% { opacity: 0; transform: scale(0.5) translateY(0); }
|
|
|
|
|
|
15% { opacity: 0.7; transform: scale(1) translateY(0); }
|
|
|
|
|
|
100% { opacity: 0; transform: scale(2.5) translateY(-20px); }
|
|
|
|
|
|
}
|
|
|
|
|
|
.trail-line { animation: trail-glow 1.5s ease-out infinite; }
|
|
|
|
|
|
@keyframes trail-glow {
|
|
|
|
|
|
0%, 15% { opacity: 0; }
|
|
|
|
|
|
20% { opacity: 0.8; }
|
|
|
|
|
|
70% { opacity: 0.6; }
|
|
|
|
|
|
100% { opacity: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.launch-button { animation: button-blink 0.5s ease-in-out infinite alternate; }
|
|
|
|
|
|
@keyframes button-blink {
|
|
|
|
|
|
0% { fill: #F44336; }
|
|
|
|
|
|
100% { fill: #FFEB3B; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
|
|
过渡动画
|
|
|
|
|
|
========================================================================== */
|
|
|
|
|
|
.modal-fade-enter-active,
|
|
|
|
|
|
.modal-fade-leave-active { transition: opacity 0.25s ease; }
|
|
|
|
|
|
.modal-fade-enter-from,
|
|
|
|
|
|
.modal-fade-leave-to { opacity: 0; }
|
|
|
|
|
|
.modal-zoom-enter-active { transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
|
|
|
|
|
|
.modal-zoom-leave-active { transition: all 0.2s ease-in; }
|
|
|
|
|
|
.modal-zoom-enter-from { opacity: 0; transform: scale(0.8); }
|
|
|
|
|
|
.modal-zoom-leave-to { opacity: 0; transform: scale(0.9); }
|
|
|
|
|
|
</style>
|