Files

298 lines
6.1 KiB
Vue

<template>
<el-container class="layout-container">
<el-header class="layout-header">
<layout-header />
</el-header>
<el-scrollbar :style="calcHeight(60)" class="main-scrollbar" ref="mainScrollbarRef">
<div class="layout-main">
<div v-if="memberStore.token || !configStore.siteSetting.require_login" class="layout-content">
<slot></slot>
</div>
<div v-else class="auth-required">
<div class="auth-required-card">
<Icon icon="ant-design:lock-outlined" size="34" color="var(--el-color-primary)" />
<h2>请先登录</h2>
<p>登录后才能访问 Web 端页面和生产运营数据。</p>
<el-button type="primary" @click="memberStore.logOpen">立即登录</el-button>
</div>
</div>
<layout-footer />
</div>
</el-scrollbar>
</el-container>
</template>
<script lang="ts" setup>
import { useTemplateRef, watch, provide, onMounted } from 'vue';
import layoutHeader from "./components/header.vue";
import layoutFooter from "./components/footer.vue";
import { useMemberStore } from '@/stores/member';
import { useConfigStore } from '@/stores/config';
import { Icon } from '~/components/icon';
const route = useRoute();
const mainScrollbarRef = useTemplateRef('mainScrollbarRef');
const memberStore = useMemberStore();
const configStore = useConfigStore();
// 计算滚动区域高度
const calcHeight = (headerHeight: number) => {
return {
height: `calc(100vh - ${headerHeight}px)`,
maxHeight: `calc(100vh - ${headerHeight}px)`
};
};
// 路由切换时滚动条滚动至顶部
watch(
() => route.fullPath,
() => {
if (!route.meta.disableScrollTo) {
mainScrollbarRef.value?.scrollTo(0, 0);
}
}
);
// 将滚动条的 ref provide 给子级组件
provide('mainScrollbarRef', mainScrollbarRef);
</script>
<style scoped lang="scss">
.layout-container {
width: 100%;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
.layout-header {
position: relative;
z-index: 1000;
background-color: var(--ma-bg-color-overlay);
height: 60px;
flex-shrink: 0;
}
.main-scrollbar {
flex: 1;
overflow: hidden;
.layout-main {
width: 100%;
max-width: 100%;
display: flex;
flex-direction: column;
min-height: 100%;
.layout-content {
flex: 1;
min-height: var(--content-min-height, 500px);
display: flex;
flex-direction: column;
width: 100%;
max-width: 100%;
overflow-x: visible;
padding: 0;
}
.layout-footer {
flex-shrink: 0;
margin-top: auto;
width: 100%;
max-width: 100%;
}
}
}
}
/* 全局重置 */
:global(html, body) {
margin: 0;
padding: 0;
width: 100%;
}
/* 响应式断点 */
@media screen and (max-width: 768px) {
.layout-container {
.layout-main {
.layout-content {
--content-min-height: 300px;
}
}
}
}
@media screen and (max-width: 1024px) {
.layout-container {
.layout-main {
.layout-content {
--content-min-height: 400px;
}
}
}
}
/* 组件样式适配 */
.layout-header {
position: fixed;
width: 100%;
z-index: 1000;
left: 0;
right: 0;
:deep(.header-logo) {
span {
padding-left: 4px;
}
}
:deep(.frontend-header-menu) {
background: transparent;
.el-menu-item,
.el-sub-menu .el-sub-menu__title {
&.is-active {
color: var(--el-color-primary) !important;
}
&:hover {
background-color: transparent !important;
color: var(--el-menu-hover-text-color) !important;
}
}
}
}
.layout-footer {
color: var(--el-text-color-secondary);
background-color: transparent !important;
position: relative;
bottom: auto;
width: 100%;
left: 0;
right: 0;
}
.auth-required {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
min-height: var(--content-min-height, 500px);
padding: 32px 20px;
background: var(--el-bg-color-page);
}
.auth-required-card {
display: flex;
flex-direction: column;
align-items: center;
width: min(420px, 100%);
padding: 42px 28px;
border: 1px solid var(--el-border-color-light);
border-radius: 16px;
background: var(--el-bg-color);
box-shadow: 0 8px 28px rgb(31 56 88 / 8%);
text-align: center;
}
.auth-required-card h2 {
margin: 18px 0 8px;
color: var(--el-text-color-primary);
font-size: 22px;
}
.auth-required-card p {
margin: 0 0 24px;
color: var(--el-text-color-secondary);
font-size: 14px;
}
/* 页面特定样式 */
.page-index {
.layout-content {
/* 首页特定样式 */
}
}
.page-member {
.layout-content {
/* 会员页面特定样式 */
}
}
/* 滚动条样式优化 */
.main-scrollbar {
:deep(.el-scrollbar__thumb) {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 4px;
&:hover {
background-color: rgba(0, 0, 0, 0.3);
}
}
:deep(.el-scrollbar__track) {
background-color: rgba(0, 0, 0, 0.05);
}
}
/* 深色模式滚动条 */
@at-root html.dark {
.main-scrollbar {
:deep(.el-scrollbar__thumb) {
background-color: rgba(255, 255, 255, 0.2);
&:hover {
background-color: rgba(255, 255, 255, 0.3);
}
}
:deep(.el-scrollbar__track) {
background-color: rgba(255, 255, 255, 0.05);
}
}
.page-index {
background: url(~/assets/images/bg-dark.jpg) repeat;
}
}
/* 响应式适配 */
@media screen and (max-width: 1024px) {
.layout-container {
.main-scrollbar {
.layout-main {
.layout-content {
padding: 0 15px;
--content-min-height: 400px;
}
}
}
}
}
@media screen and (max-width: 768px) {
.layout-container {
.main-scrollbar {
.layout-main {
.layout-content {
padding: 0 10px;
--content-min-height: 300px;
}
}
}
}
}
@media screen and (max-height: 650px) {
.layout-container {
.main-scrollbar {
.layout-main {
.layout-content {
--content-min-height: 200px;
}
}
}
}
}
</style>