Files
GEAR-OA/gear-ui3/src/views/dashboard/layout/index.vue

165 lines
3.4 KiB
Vue
Raw Normal View History

2025-09-05 09:28:13 +08:00
<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>
2025-09-06 14:36:26 +08:00
<el-dialog v-model="settingVisible" title="图表设置" width="80%">
2025-09-05 09:28:13 +08:00
<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>