247 lines
4.5 KiB
Vue
247 lines
4.5 KiB
Vue
|
<template>
|
|||
|
<teleport to="body">
|
|||
|
<transition name="emergency-modal">
|
|||
|
<div class="modal-mask" @click.self="$emit('close')">
|
|||
|
<div class="emergency-modal" :class="{ 'emergency-pulse': show }">
|
|||
|
<div class="glow"></div>
|
|||
|
<div class="modal-header">
|
|||
|
<div class="warning-icon">⚠️</div>
|
|||
|
<h2>{{ title }}</h2>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="modal-content">
|
|||
|
<p>{{ content }}</p>
|
|||
|
</div>
|
|||
|
|
|||
|
<button class="emergency-button" @click="handleAudioEnd">
|
|||
|
<span class="button-text">立即处理</span>
|
|||
|
<div class="button-glow"></div>
|
|||
|
</button>
|
|||
|
|
|||
|
<!-- <audio ref="audioPlayer" autoplay loop>
|
|||
|
<source src="../assets/audio/alarm.mp3" type="video/mp3" />
|
|||
|
</audio> -->
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</transition>
|
|||
|
</teleport>
|
|||
|
</template>
|
|||
|
|
|||
|
<script setup lang="ts">
|
|||
|
import { onMounted, ref } from "vue";
|
|||
|
const audioPlayer = ref(null);
|
|||
|
|
|||
|
defineProps({
|
|||
|
show: Boolean,
|
|||
|
title: {
|
|||
|
type: String,
|
|||
|
default: "紧急警报!",
|
|||
|
},
|
|||
|
content: {
|
|||
|
type: String,
|
|||
|
default: "检测到危险操作,请立即处理!",
|
|||
|
},
|
|||
|
});
|
|||
|
|
|||
|
defineEmits(["close", "confirm"]);
|
|||
|
|
|||
|
const handleAudioEnd = () => {
|
|||
|
if (audioPlayer.value) {
|
|||
|
audioPlayer.value.play().catch((error) => {
|
|||
|
console.error("自动播放被阻止:", error);
|
|||
|
});
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
onMounted(() => {
|
|||
|
// 确保在DOM完全加载后播放音频
|
|||
|
// if (audioPlayer.value) {
|
|||
|
// setTimeout(() => {
|
|||
|
// audioPlayer.value.play();
|
|||
|
// });
|
|||
|
// audioPlayer.value.play();
|
|||
|
// // 设置音频静音,浏览器允许自动播放
|
|||
|
// audioPlayer.value
|
|||
|
// .play()
|
|||
|
// .then(() => {
|
|||
|
// // 自动播放成功后取消静音
|
|||
|
// audioPlayer.value.muted = false;
|
|||
|
// })
|
|||
|
// .catch((error) => {
|
|||
|
// console.log("自动播放失败", error);
|
|||
|
// });
|
|||
|
// }
|
|||
|
});
|
|||
|
</script>
|
|||
|
|
|||
|
<style scoped>
|
|||
|
.modal-mask {
|
|||
|
position: fixed;
|
|||
|
z-index: 9998;
|
|||
|
top: 0;
|
|||
|
left: 0;
|
|||
|
width: 100vw;
|
|||
|
height: 100vh;
|
|||
|
background-color: rgba(0, 0, 0, 0.6);
|
|||
|
backdrop-filter: blur(4px);
|
|||
|
display: flex;
|
|||
|
justify-content: center;
|
|||
|
align-items: center;
|
|||
|
}
|
|||
|
|
|||
|
.emergency-modal {
|
|||
|
position: relative;
|
|||
|
width: 300px;
|
|||
|
padding: 2rem;
|
|||
|
background: #2a0a0a;
|
|||
|
border-radius: 12px;
|
|||
|
border: 2px solid #ff4444;
|
|||
|
box-shadow: 0 0 40px rgba(255, 68, 68, 0.3);
|
|||
|
transform-origin: center;
|
|||
|
overflow: hidden;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
align-items: center;
|
|||
|
}
|
|||
|
|
|||
|
.glow {
|
|||
|
position: absolute;
|
|||
|
top: 0;
|
|||
|
left: 0;
|
|||
|
width: 100%;
|
|||
|
height: 100%;
|
|||
|
background: radial-gradient(circle at 50% 50%, rgba(255, 68, 68, 0.3) 0%, rgba(255, 68, 68, 0) 70%);
|
|||
|
animation: glow-pulse 2s infinite;
|
|||
|
}
|
|||
|
|
|||
|
.modal-header {
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
margin-bottom: 1.5rem;
|
|||
|
}
|
|||
|
|
|||
|
.warning-icon {
|
|||
|
font-size: 3rem;
|
|||
|
margin-right: 1rem;
|
|||
|
animation: shake 0.8s ease infinite;
|
|||
|
}
|
|||
|
|
|||
|
h2 {
|
|||
|
color: #ff4444;
|
|||
|
font-size: 1.8rem;
|
|||
|
margin: 0;
|
|||
|
}
|
|||
|
|
|||
|
.modal-content {
|
|||
|
color: #ffaaaa;
|
|||
|
margin-bottom: 2rem;
|
|||
|
line-height: 1.5;
|
|||
|
}
|
|||
|
|
|||
|
.emergency-button {
|
|||
|
position: relative;
|
|||
|
padding: 1rem 2rem;
|
|||
|
background: linear-gradient(45deg, #ff4444, #ff0000);
|
|||
|
border: none;
|
|||
|
border-radius: 8px;
|
|||
|
color: white;
|
|||
|
font-weight: bold;
|
|||
|
cursor: pointer;
|
|||
|
overflow: hidden;
|
|||
|
transition: transform 0.2s;
|
|||
|
}
|
|||
|
|
|||
|
.button-glow {
|
|||
|
position: absolute;
|
|||
|
top: 0;
|
|||
|
left: -100%;
|
|||
|
width: 200%;
|
|||
|
height: 100%;
|
|||
|
background: linear-gradient(45deg, transparent 25%, rgba(255, 255, 255, 0.2) 50%, transparent 75%);
|
|||
|
animation: slide 1.5s infinite;
|
|||
|
}
|
|||
|
|
|||
|
.emergency-button:hover {
|
|||
|
transform: scale(1.05);
|
|||
|
}
|
|||
|
|
|||
|
.emergency-button:active {
|
|||
|
transform: scale(0.95);
|
|||
|
}
|
|||
|
|
|||
|
/* 动画效果 */
|
|||
|
@keyframes emergency-pulse {
|
|||
|
0% {
|
|||
|
transform: scale(1);
|
|||
|
}
|
|||
|
50% {
|
|||
|
transform: scale(1.02);
|
|||
|
}
|
|||
|
100% {
|
|||
|
transform: scale(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@keyframes shake {
|
|||
|
0% {
|
|||
|
transform: translateX(0);
|
|||
|
}
|
|||
|
25% {
|
|||
|
transform: translateX(-5px);
|
|||
|
}
|
|||
|
50% {
|
|||
|
transform: translateX(5px);
|
|||
|
}
|
|||
|
75% {
|
|||
|
transform: translateX(-3px);
|
|||
|
}
|
|||
|
100% {
|
|||
|
transform: translateX(0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@keyframes glow-pulse {
|
|||
|
0% {
|
|||
|
opacity: 0.6;
|
|||
|
}
|
|||
|
50% {
|
|||
|
opacity: 1;
|
|||
|
}
|
|||
|
100% {
|
|||
|
opacity: 0.6;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@keyframes slide {
|
|||
|
to {
|
|||
|
left: 100%;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.emergency-modal-enter-active {
|
|||
|
animation: emergency-modal-in 0.3s;
|
|||
|
}
|
|||
|
|
|||
|
.emergency-modal-leave-active {
|
|||
|
animation: emergency-modal-in 0.3s reverse;
|
|||
|
}
|
|||
|
|
|||
|
@keyframes emergency-modal-in {
|
|||
|
0% {
|
|||
|
transform: scale(0.5);
|
|||
|
opacity: 0;
|
|||
|
}
|
|||
|
60% {
|
|||
|
transform: scale(1.05);
|
|||
|
}
|
|||
|
100% {
|
|||
|
transform: scale(1);
|
|||
|
opacity: 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.emergency-pulse {
|
|||
|
animation: emergency-pulse 1.5s infinite;
|
|||
|
}
|
|||
|
</style>
|