|
<template> |
|
<Transition |
|
name="message-fade" |
|
appear |
|
mode="in-out" |
|
@beforeLeave="emit('close')" |
|
@afterLeave="emit('destroy')" |
|
> |
|
<div class="message" :id="id" v-if="visible"> |
|
<div class="message-container" |
|
@mouseenter="clearTimer()" |
|
@mouseleave="startTimer()" |
|
> |
|
<div class="icons"> |
|
<IconAttention theme="filled" size="18" fill="#faad14" v-if="type === 'warning'" /> |
|
<IconCheckOne theme="filled" size="18" fill="#52c41a" v-if="type === 'success'" /> |
|
<IconCloseOne theme="filled" size="18" fill="#ff4d4f" v-if="type === 'error'" /> |
|
<IconInfo theme="filled" size="18" fill="#1677ff" v-if="type === 'info'" /> |
|
</div> |
|
<div class="content"> |
|
<div class="title" v-if="title">{{ title }}</div> |
|
<div class="description">{{ message }}</div> |
|
</div> |
|
<div class="control" v-if="closable"> |
|
<span |
|
class="close-btn" |
|
@click="close()" |
|
> |
|
<IconCloseSmall /> |
|
</span> |
|
</div> |
|
</div> |
|
</div> |
|
</Transition> |
|
</template> |
|
|
|
<script lang="ts" setup> |
|
import { onMounted, ref, onBeforeMount } from 'vue' |
|
import { icons } from '@/plugins/icon' |
|
|
|
const { |
|
IconAttention, |
|
IconCheckOne, |
|
IconCloseOne, |
|
IconInfo, |
|
IconCloseSmall, |
|
} = icons |
|
|
|
const props = withDefaults(defineProps<{ |
|
id: string |
|
message: string |
|
type?: string |
|
title?: string |
|
duration?: number |
|
closable?: boolean |
|
}>(), { |
|
type: 'success', |
|
title: '', |
|
duration: 3000, |
|
closable: false, |
|
}) |
|
|
|
const emit = defineEmits<{ |
|
(event: 'close'): void |
|
(event: 'destroy'): void |
|
}>() |
|
|
|
const visible = ref(true) |
|
const timer = ref<number | null>(null) |
|
|
|
const startTimer = () => { |
|
if (props.duration <= 0) return |
|
timer.value = setTimeout(close, props.duration) |
|
} |
|
const clearTimer = () => { |
|
if (timer.value) clearTimeout(timer.value) |
|
} |
|
|
|
const close = () => visible.value = false |
|
|
|
onBeforeMount(() => { |
|
clearTimer() |
|
}) |
|
onMounted(() => { |
|
startTimer() |
|
}) |
|
|
|
defineExpose({ |
|
close, |
|
}) |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
.message { |
|
max-width: 600px; |
|
|
|
& + & { |
|
margin-top: 15px; |
|
} |
|
} |
|
.message-container { |
|
min-width: 50px; |
|
display: flex; |
|
align-items: center; |
|
padding: 10px; |
|
font-size: 13px; |
|
overflow: hidden; |
|
border-radius: $borderRadius; |
|
box-shadow: 0 1px 8px rgba(0, 0, 0, .15); |
|
background: #fff; |
|
pointer-events: all; |
|
position: relative; |
|
|
|
.icons { |
|
display: flex; |
|
align-items: center; |
|
margin-right: 10px; |
|
} |
|
.title { |
|
font-size: 14px; |
|
font-weight: 700; |
|
overflow: hidden; |
|
text-overflow: ellipsis; |
|
white-space: nowrap; |
|
} |
|
.content { |
|
width: 100%; |
|
} |
|
.description { |
|
line-height: 1.5; |
|
color: $textColor; |
|
} |
|
.title + .description { |
|
margin-top: 5px; |
|
} |
|
.control { |
|
position: relative; |
|
height: 100%; |
|
margin-left: 10px; |
|
} |
|
.close-btn { |
|
font-size: 15px; |
|
color: #666; |
|
display: flex; |
|
align-items: center; |
|
cursor: pointer; |
|
|
|
&:hover { |
|
color: $themeColor; |
|
} |
|
} |
|
} |
|
|
|
.message-fade-enter-active { |
|
animation: message-fade-in-down .3s; |
|
} |
|
.message-fade-leave-active { |
|
animation: message-fade-out .3s; |
|
} |
|
|
|
@keyframes message-fade-in-down { |
|
0% { |
|
opacity: 0; |
|
transform: translateY(-20px); |
|
} |
|
100% { |
|
opacity: 1; |
|
transform: translateY(0); |
|
} |
|
} |
|
|
|
@keyframes message-fade-out { |
|
0% { |
|
opacity: 1; |
|
margin-top: 0; |
|
} |
|
100% { |
|
opacity: 0; |
|
margin-top: -45px; |
|
} |
|
} |
|
</style> |