Files
xk-admin/apps/web-antd/src/views/notice/scenes/NoticeSceneShell.vue
李琦 b82754e36b feat:
1. 更新框架
2. 修复框架更新后导致的问题
3. 消息系统升级
2026-08-10 20:08:50 +08:00

261 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
/**
* 消息专用场景弹层外壳(非 VbenModal
* 遮罩 + 类型色调面板 + 关闭;内容由各场景组件填充
*/
import { onMounted, onUnmounted, watch } from 'vue';
import { X } from 'lucide-vue-next';
import type { NoticeSceneSize, NoticeSceneTone } from './meta';
const props = withDefaults(
defineProps<{
open: boolean;
title?: string;
kicker?: string;
tone?: NoticeSceneTone;
size?: NoticeSceneSize;
bareHeader?: boolean;
}>(),
{
title: '消息详情',
kicker: 'NOTICE',
tone: 'default',
size: 'md',
bareHeader: false,
},
);
const emit = defineEmits<{
'update:open': [boolean];
close: [];
}>();
function close() {
emit('update:open', false);
emit('close');
}
function onKeydown(e: KeyboardEvent) {
if (e.key === 'Escape' && props.open) {
close();
}
}
watch(
() => props.open,
(v) => {
document.body.style.overflow = v ? 'hidden' : '';
},
);
onMounted(() => window.addEventListener('keydown', onKeydown));
onUnmounted(() => {
window.removeEventListener('keydown', onKeydown);
document.body.style.overflow = '';
});
</script>
<template>
<Teleport to="body">
<Transition name="ns">
<div
v-if="open"
class="ns-root"
role="presentation"
>
<div class="ns-mask" @click="close" />
<div
class="ns-panel"
:class="[`tone-${tone}`, `size-${size}`, { bare: bareHeader }]"
role="dialog"
aria-modal="true"
:aria-label="title"
@click.stop
>
<button
type="button"
class="ns-close"
aria-label="关闭"
@click="close"
>
<X class="size-4" />
</button>
<header v-if="!bareHeader" class="ns-head">
<div class="ns-kicker">{{ kicker }}</div>
<h2 class="ns-title">{{ title }}</h2>
</header>
<div class="ns-body">
<slot />
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
.ns-root {
position: fixed;
inset: 0;
z-index: 1200;
display: grid;
place-items: center;
padding: 24px 16px;
}
.ns-mask {
position: absolute;
inset: 0;
background: hsl(0 0% 0% / 0.48);
backdrop-filter: blur(6px);
}
.ns-panel {
position: relative;
z-index: 1;
width: min(720px, 96vw);
max-height: min(88vh, 920px);
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: 20px;
border: 1px solid hsl(var(--border));
background: hsl(var(--card, var(--background)));
box-shadow:
0 24px 64px hsl(0 0% 0% / 0.28),
0 0 0 1px hsl(var(--foreground) / 0.03);
}
.ns-panel.size-lg {
width: min(860px, 96vw);
}
.ns-panel.size-xl {
width: min(960px, 96vw);
}
.ns-panel.tone-vip {
border-color: hsl(160 50% 40% / 0.35);
box-shadow:
0 24px 64px hsl(160 40% 12% / 0.45),
0 0 40px hsl(160 60% 40% / 0.12);
}
.ns-panel.tone-danger {
border-color: hsl(0 72% 50% / 0.35);
box-shadow:
0 24px 64px hsl(0 40% 10% / 0.4),
0 0 36px hsl(0 72% 50% / 0.12);
}
.ns-panel.tone-money {
border-color: hsl(var(--primary) / 0.35);
}
.ns-panel.tone-report {
border-color: hsl(160 60% 40% / 0.3);
}
.ns-panel.tone-invoice {
border-color: hsl(250 60% 55% / 0.3);
}
.ns-panel.tone-audit {
border-color: hsl(190 70% 42% / 0.28);
}
.ns-close {
position: absolute;
top: 14px;
right: 14px;
z-index: 3;
display: grid;
place-items: center;
width: 32px;
height: 32px;
border: 0;
border-radius: 999px;
cursor: pointer;
color: hsl(var(--muted-foreground));
background: hsl(var(--muted) / 0.55);
transition:
background 0.15s ease,
color 0.15s ease,
transform 0.15s ease;
}
.ns-close:hover {
color: hsl(var(--foreground));
background: hsl(var(--muted));
transform: scale(1.04);
}
.ns-head {
flex-shrink: 0;
padding: 20px 48px 0 22px;
}
.ns-kicker {
font-size: 11px;
font-weight: 750;
letter-spacing: 0.12em;
color: hsl(var(--primary));
}
.tone-danger .ns-kicker {
color: hsl(0 72% 52%);
}
.tone-vip .ns-kicker {
color: hsl(160 55% 42%);
}
.tone-invoice .ns-kicker {
color: hsl(250 60% 55%);
}
.ns-title {
margin: 6px 0 0;
font-size: 18px;
font-weight: 720;
letter-spacing: -0.02em;
color: hsl(var(--foreground));
}
.ns-body {
flex: 1;
overflow: auto;
padding: 16px 22px 22px;
}
.ns-panel.bare .ns-body {
padding-top: 18px;
}
/* enter / leave */
.ns-enter-active,
.ns-leave-active {
transition: opacity 0.2s ease;
}
.ns-enter-active .ns-panel,
.ns-leave-active .ns-panel {
transition:
transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
opacity 0.22s ease;
}
.ns-enter-from,
.ns-leave-to {
opacity: 0;
}
.ns-enter-from .ns-panel,
.ns-leave-to .ns-panel {
opacity: 0;
transform: translateY(12px) scale(0.98);
}
</style>