Files
GEAR-OA/gear-ui3/src/views/dashboard/layout/index.vue
2025-09-06 14:36:26 +08:00

165 lines
3.4 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.

<template>
<div class="dashboard-layout">
<!-- 顶部导航栏通用布局 -->
<header class="layout-header">
<button
class="header-btn back-btn"
@click="handleBack"
aria-label="返回"
>
<el-icon><ArrowLeft /></el-icon>
<span>返回</span>
</button>
<h1 class="layout-title">{{ title }}</h1>
<div style="display: flex;align-items: center;gap: 10px;">
<button class="header-btn" aria-label="设置" @click="settingVisible = true">
<el-icon><Setting /></el-icon>
<span>设置</span>
</button>
<button
class="header-btn refresh-btn"
@click="handleRefresh"
aria-label="刷新"
:disabled="loading"
:class="{ refreshing: isRefreshing }"
>
<el-icon><Refresh /></el-icon>
<span>刷新</span>
</button>
</div>
</header>
<!-- 内容插槽用于插入图表网格 -->
<main class="layout-content" v-loading="loading">
<slot></slot>
</main>
<el-dialog v-model="settingVisible" title="图表设置" width="80%">
<ChartSetting />
</el-dialog>
</div>
</template>
<script setup>
import { ArrowLeft, Refresh, Setting } from '@element-plus/icons-vue';
import ChartSetting from '../grid/setting.vue';
// 接收外部传入的状态与方法props 类型约束)
const props = defineProps({
title: {
type: String,
required: true,
default: '数据可视化大屏'
},
loading: {
type: Boolean,
required: true,
default: false
},
isRefreshing: {
type: Boolean,
required: true,
default: false
},
handleBack: {
type: Function,
required: true
},
handleRefresh: {
type: Function,
required: true
}
});
const settingVisible = ref(false);
</script>
<style scoped>
/* 布局根容器样式 */
.dashboard-layout {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: #0f172a; /* 深色背景统一在布局层定义 */
}
/* 顶部导航样式 */
.layout-header {
height: 60px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
background: linear-gradient(120deg, #1e293b, #0f172a);
border-bottom: 1px solid #334155;
z-index: 10;
}
.layout-title {
font-size: 20px;
font-weight: 600;
color: #f8fafc;
margin: 0;
}
.header-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background-color: #334155;
color: #f8fafc;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.header-btn:hover {
background-color: #475569;
box-shadow: 0 0 8px rgba(255, 255, 255, 0.1);
}
.header-btn:disabled {
background-color: #2d3748;
cursor: not-allowed;
opacity: 0.8;
}
.header-btn i {
font-size: 16px;
}
/* 刷新按钮动画 */
.refreshing el-icon {
animation: spin 0.8s linear;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 内容区域容器(插槽父容器) */
.layout-content {
flex: 1;
overflow: hidden; /* 避免与子组件滚动冲突 */
}
/* 小屏幕适配(导航栏) */
@media (max-width: 768px) {
.layout-title {
font-size: 16px;
}
.header-btn span {
display: none; /* 隐藏按钮文字节省空间 */
}
.header-btn {
padding: 6px 8px;
}
}
</style>