196 lines
4.0 KiB
Vue
196 lines
4.0 KiB
Vue
<script lang="ts" setup>
|
|
import { FullScreen } from '@element-plus/icons-vue';
|
|
import { ref, watch } from 'vue';
|
|
|
|
interface NavItem {
|
|
name: string;
|
|
label: string;
|
|
icon?: string;
|
|
}
|
|
|
|
interface Props {
|
|
modelValue: boolean;
|
|
title?: string;
|
|
width?: string;
|
|
navItems: NavItem[];
|
|
defaultActive?: string;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
title: '弹窗标题',
|
|
width: '75%',
|
|
defaultActive: '',
|
|
});
|
|
|
|
const emit = defineEmits(['update:modelValue', 'confirm', 'close', 'nav-change']);
|
|
|
|
const visible = ref(false);
|
|
const activeNav = ref(props.defaultActive || (props.navItems.length > 0 ? props.navItems[0].name : ''));
|
|
const isFullscreen = ref(false);
|
|
|
|
watch(() => props.modelValue, (val) => {
|
|
visible.value = val;
|
|
if (!val) {
|
|
isFullscreen.value = false; // 关闭时重置全屏状态
|
|
}
|
|
});
|
|
|
|
watch(() => props.defaultActive, (val) => {
|
|
if (val) {
|
|
activeNav.value = val;
|
|
}
|
|
});
|
|
|
|
function handleNavSelect(index: string) {
|
|
activeNav.value = index;
|
|
emit('nav-change', index);
|
|
}
|
|
|
|
function handleClose() {
|
|
visible.value = false;
|
|
emit('update:modelValue', false);
|
|
emit('close');
|
|
}
|
|
|
|
function handleConfirm() {
|
|
emit('confirm', activeNav.value);
|
|
handleClose();
|
|
}
|
|
|
|
function toggleFullscreen() {
|
|
isFullscreen.value = !isFullscreen.value;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<el-dialog
|
|
v-model="visible"
|
|
:title="title"
|
|
:width="isFullscreen ? '100%' : width"
|
|
:before-close="handleClose"
|
|
:fullscreen="isFullscreen"
|
|
:top="isFullscreen ? '0' : '5vh'"
|
|
class="nav-dialog"
|
|
>
|
|
<template #header="{ titleId, titleClass }">
|
|
<div class="dialog-header">
|
|
<h4 :id="titleId" :class="titleClass">
|
|
{{ title }}
|
|
</h4>
|
|
<!-- 全屏按钮暂不做 -->
|
|
<div v-if="false" class="header-actions">
|
|
<slot name="extra-actions" />
|
|
<el-button
|
|
circle
|
|
plain
|
|
size="small"
|
|
class="fullscreen-btn"
|
|
:title="isFullscreen ? '退出全屏' : '全屏'"
|
|
@click="toggleFullscreen"
|
|
>
|
|
<el-icon>
|
|
<FullScreen />
|
|
</el-icon>
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="dialog-container">
|
|
<!-- 左侧导航 -->
|
|
<div class="nav-side">
|
|
<el-menu
|
|
:default-active="activeNav"
|
|
class="nav-menu"
|
|
@select="handleNavSelect"
|
|
>
|
|
<el-menu-item
|
|
v-for="item in navItems"
|
|
:key="item.name"
|
|
:index="item.name"
|
|
>
|
|
<template #title>
|
|
<el-icon v-if="item.icon">
|
|
<component :is="item.icon" />
|
|
</el-icon>
|
|
<span>{{ item.label }}</span>
|
|
</template>
|
|
</el-menu-item>
|
|
</el-menu>
|
|
</div>
|
|
|
|
<!-- 右侧内容 -->
|
|
<div class="content-main">
|
|
<slot :name="activeNav" />
|
|
<div v-if="!$slots[activeNav]" class="empty-content">
|
|
<el-empty description="暂无内容" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button type="primary" @click="handleClose">关闭</el-button>
|
|
<el-button v-if="false" type="primary" @click="handleConfirm">确认</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.dialog-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
.fullscreen-btn {
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.fullscreen-btn:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
.dialog-container {
|
|
display: flex;
|
|
height: 70vh;
|
|
min-height: 500px;
|
|
}
|
|
|
|
:deep(.el-dialog.is-fullscreen) .dialog-container {
|
|
height: calc(100vh - 120px);
|
|
}
|
|
|
|
.nav-side {
|
|
width: 200px;
|
|
border-right: 1px solid #e6e6e6;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.nav-menu {
|
|
border-right: none;
|
|
height: 100%;
|
|
}
|
|
|
|
.content-main {
|
|
flex: 1;
|
|
padding: 0 20px;
|
|
overflow: auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
.empty-content {
|
|
display: flex;
|
|
height: 100%;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
</style>
|