You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.6 KiB
Vue

3 months ago
<template>
<!-- 全局容器 -->
<div class="app-container">
<!-- 路由视图容器 -->
<router-view v-slot="{ Component, route }">
<!-- 智能缓存仅缓存带有 meta.keepAlive 的路由 -->
<keep-alive :include="cachedRoutes">
<component
:is="Component"
:key="route.fullPath"
class="router-view-container"
:class="{ 'has-header': showHeader }"
/>
</keep-alive>
</router-view>
<!-- 大屏适配容器用于缩放内容 -->
<div
v-if="isDashboardPage"
ref="scaleContainer"
class="scale-container"
:style="scaleStyle"
>
<!-- 实际内容会被挂载到这里 -->
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const scaleContainer = ref<HTMLElement>()
const scaleValue = ref(1)
// 需要缓存的组件名称列表
const cachedRoutes = ref<string[]>([])
// 是否显示头部(根据路由配置)
const showHeader = computed(() => route.meta.showHeader ?? true)
// 是否大屏页面(需要特殊适配)
const isDashboardPage = computed(() => route.meta.isDashboard ?? false)
// 缩放样式计算
const scaleStyle = computed(() => ({
transform: `scale(${scaleValue.value}) translate(-50%, -50%)`,
width: `${(1 / scaleValue.value) * 100}%`,
height: `${(1 / scaleValue.value) * 100}%`
}))
// 自适应计算函数
const calculateScale = () => {
if (!isDashboardPage.value || !scaleContainer.value) return
const baseWidth = 1920 // 设计稿基准宽度
const baseHeight = 1080 // 设计稿基准高度
const currentWidth = window.innerWidth
const currentHeight = window.innerHeight
// 计算缩放比例(考虑宽高比)
const widthRatio = currentWidth / baseWidth
const heightRatio = currentHeight / baseHeight
scaleValue.value = Math.min(widthRatio, heightRatio)
}
// 监听路由变化
watch(
() => route.path,
(newVal) => {
// 更新缓存路由列表
if (route.meta.keepAlive && route.name) {
cachedRoutes.value = [...new Set([...cachedRoutes.value, route.name as string])]
}
// 大屏页面特殊处理
if (isDashboardPage.value) {
nextTick(() => {
// 将路由视图移动到缩放容器
const view = document.querySelector('.router-view-container')
if (view && scaleContainer.value) {
scaleContainer.value.appendChild(view)
}
calculateScale()
})
}
}
)
// 窗口resize监听带防抖
let resizeTimer: number
const onResize = () => {
clearTimeout(resizeTimer)
resizeTimer = setTimeout(calculateScale, 300)
}
onMounted(() => {
window.addEventListener('resize', onResize)
calculateScale()
})
onUnmounted(() => {
window.removeEventListener('resize', onResize)
})
</script>
<style lang="scss">
/* 全局样式重置 */
html, body, #app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 禁用全局滚动条 */
}
.app-container {
position: relative;
width: 100%;
height: 100%;
/* 路由视图基础样式 */
.router-view-container {
width: 100%;
height: 100%;
}
/* 大屏适配容器样式 */
.scale-container {
position: fixed;
top: 50%;
left: 50%;
transform-origin: 0 0;
transition: transform 0.3s;
/* 大屏页面特殊处理 */
.router-view-container {
background: #030409; /* 深色背景 */
color: #fff;
}
}
}
</style>