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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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-color: #08104C; /* */
color: #fff;
}
}
}
</style>