| | |
| | | <template> |
| | | <div class="scale-container"> |
| | | <div class="architecture-scaling-container" |
| | | :style="{ transform: `scale(${scaleRatio})` }"> |
| | | <div class="architecture-container"> |
| | | <div class="header"> |
| | | <span class="title-bar"></span> |
| | | <h1 class="title-text">系统架构图</h1> |
| | | </div> |
| | | <div class="image-wrapper"> |
| | | <div class="hotspot-container"> |
| | | <img :src="architectureImg" |
| | | alt="系统架构图" |
| | | class="architecture-img" /> |
| | | <!-- 热点区域 --> |
| | | <div v-for="spot in hotspots" |
| | | :key="spot.id" |
| | | class="hotspot" |
| | | :style="{ top: spot.top, left: spot.left, width: spot.width, height: spot.height }" |
| | | @click="handleSpotClick(spot)" |
| | | :title="'跳转至: ' + spot.name"> |
| | | <div class="scroll-content" |
| | | :style="{ |
| | | height: `${designHeight * scaleRatio}px`, |
| | | width: `${designWidth * scaleRatio}px` |
| | | }"> |
| | | <div class="architecture-scaling-container" |
| | | :style="{ transform: `scale(${scaleRatio})` }"> |
| | | <div class="architecture-container"> |
| | | <div class="header"> |
| | | <span class="title-bar"></span> |
| | | <h1 class="title-text">系统架构图</h1> |
| | | </div> |
| | | <div class="image-wrapper"> |
| | | <div class="hotspot-container"> |
| | | <img :src="architectureImg" |
| | | alt="系统架构图" |
| | | class="architecture-img" /> |
| | | <!-- 热点区域 --> |
| | | <div v-for="spot in hotspots" |
| | | :key="spot.id" |
| | | class="hotspot" |
| | | :style="{ top: spot.top, left: spot.left, width: spot.width, height: spot.height }" |
| | | @click="handleSpotClick(spot)" |
| | | :title="'跳转至: ' + spot.name"> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | |
| | | // 获取容器的实际尺寸 |
| | | const containerWidth = container.clientWidth; |
| | | const containerHeight = container.clientHeight; |
| | | |
| | | if (containerWidth === 0) { |
| | | if (containerWidth === 0 || containerHeight === 0) { |
| | | // 如果还没渲染出来,延迟一下再计算 |
| | | setTimeout(calculateScale, 100); |
| | | return; |
| | | } |
| | | |
| | | // 计算宽度缩放比例,使其始终占满宽度 |
| | | // 计算缩放比例,取宽高比例中的较大值,确保始终撑满整个容器 |
| | | const scaleX = containerWidth / designWidth; |
| | | scaleRatio.value = scaleX; |
| | | const scaleY = containerHeight / designHeight; |
| | | scaleRatio.value = Math.max(scaleX, scaleY); |
| | | }; |
| | | |
| | | // 窗口大小变化处理 |
| | |
| | | }; |
| | | |
| | | // 生命周期 |
| | | let resizeObserver = null; |
| | | onMounted(() => { |
| | | // 计算初始缩放比例 |
| | | nextTick(() => { |
| | |
| | | |
| | | // 监听窗口大小变化 |
| | | window.addEventListener("resize", handleResize); |
| | | |
| | | // 使用 ResizeObserver 监听容器尺寸变化(解决侧边栏切换问题) |
| | | const container = document.querySelector(".scale-container"); |
| | | if (container && window.ResizeObserver) { |
| | | resizeObserver = new ResizeObserver(() => { |
| | | calculateScale(); |
| | | }); |
| | | resizeObserver.observe(container); |
| | | } |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener("resize", handleResize); |
| | | if (resizeObserver) { |
| | | resizeObserver.disconnect(); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | |
| | | width: 100%; |
| | | height: calc(100vh - 84px); |
| | | background-color: #f5f7fa; |
| | | overflow: hidden; |
| | | overflow: auto; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .scroll-content { |
| | | position: relative; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .architecture-scaling-container { |
| | | position: relative; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 1920px; |
| | | height: 980px; |
| | | transform-origin: left top; |