From 0e66217cdb07d5e6bd337228e0eb39d67f877d0d Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期五, 29 五月 2026 18:16:16 +0800
Subject: [PATCH] 系统架构图,及质量报表自适应
---
src/views/systemArchitecture/index.vue | 523 ++++++++++++---------------------------------------------
1 files changed, 112 insertions(+), 411 deletions(-)
diff --git a/src/views/systemArchitecture/index.vue b/src/views/systemArchitecture/index.vue
index 1eb37bb..9f87a06 100644
--- a/src/views/systemArchitecture/index.vue
+++ b/src/views/systemArchitecture/index.vue
@@ -1,436 +1,137 @@
<template>
- <div class="architecture-page">
- <section ref="canvasRef" class="canvas">
- <svg
- v-if="linkPath"
- class="link-overlay"
- :width="svgSize.width"
- :height="svgSize.height"
- aria-hidden="true"
- >
- <defs>
- <marker
- id="erp-link-arrow"
- markerWidth="10"
- markerHeight="10"
- refX="7"
- refY="3.5"
- orient="auto"
- markerUnits="strokeWidth"
- >
- <path d="M0,0 L0,7 L7,3.5 z" fill="#94a3b8" />
- </marker>
- </defs>
- <path
- :d="linkPath"
- class="link-overlay__path"
- marker-end="url(#erp-link-arrow)"
- />
- </svg>
-
- <section
- v-for="row in architectureRows"
- :key="row.key"
- class="lane"
- >
- <aside class="lane__aside">
- <svg-icon :icon-class="row.icon" class="lane__icon" />
- <h2>{{ row.label }}</h2>
- <span class="lane__arrow"></span>
- </aside>
-
- <div class="lane__flow">
- <div
- v-for="(item, index) in row.items"
- :key="item.name"
- class="node-wrap"
- >
- <article
- class="node"
- :class="{ 'node--accent': item.accent }"
- :ref="setNodeRef(item)"
- >
- <span class="node__mark">
- <span class="node__cap"></span>
- <span class="node__base"></span>
- <svg-icon :icon-class="item.icon" class="node__icon" />
- </span>
- <h3>{{ item.name }}</h3>
- </article>
- <span v-if="index < row.items.length - 1" class="flow-arrow"></span>
- </div>
+ <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>
- </section>
- </section>
+ <div class="image-wrapper">
+ <img :src="architectureImg"
+ alt="绯荤粺鏋舵瀯鍥�"
+ class="architecture-img" />
+ </div>
+ </div>
+ </div>
</div>
</template>
<script setup>
-import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue'
+ import { ref, onMounted, onBeforeUnmount, nextTick } from "vue";
+ import architectureImg from "@/assets/images/xitongjiagou.svg";
-const architectureRows = [
- {
- key: 'basic',
- label: '鍩虹閰嶇疆',
- icon: 'system',
- items: [
- { name: '瑙掕壊鐢ㄦ埛绠$悊', icon: 'user' },
- { name: '浜у搧缁存姢', icon: 'monitor' },
- { name: '瀹℃壒绠$悊', icon: 'tree', accent: true }
- ]
- },
- {
- key: 'sale',
- label: '閿�鍞�',
- icon: 'chart',
- items: [
- { name: '瀹㈡埛妗f', icon: 'peoples' },
- { name: '閿�鍞姤浠�', icon: 'form' },
- { name: '閿�鍞彴璐�', icon: 'table' },
- { name: '鍙戣揣鍙拌处', icon: 'clipboard', accent: true, linkSource: true },
- { name: '閿�鍞��璐�', icon: 'nested' },
- { name: '瀹㈡埛寰�鏉�', icon: 'money' },
- { name: '鎸囨爣缁熻', icon: 'chart' }
- ]
- },
- {
- key: 'purchase',
- label: '閲囪喘',
- icon: 'shopping',
- items: [
- { name: '渚涘簲鍟嗘。妗�', icon: 'people' },
- { name: '閲囪喘鍙拌处', icon: 'table', accent: true },
- { name: '閲囪喘閫�璐�', icon: 'nested' },
- { name: '渚涘簲鍟嗗線鏉�', icon: 'money' },
- { name: '閲囪喘鎶ヨ〃', icon: 'chart' }
- ]
- },
- {
- key: 'produce',
- label: '鐢熶骇',
- icon: 'build',
- items: [
- { name: '宸ュ簭', icon: 'tree' },
- { name: 'BOM', icon: 'list' },
- { name: '宸ヨ壓璺嚎', icon: 'guide' },
- { name: '鐢熶骇璁㈠崟', icon: 'form' },
- { name: '鐢熶骇鎺掍骇', icon: 'date' },
- { name: '鐢熶骇鎶ュ伐', icon: 'edit' },
- { name: '鎶ュ伐鍙拌处', icon: 'clipboard' },
- { name: '鐢熶骇鏍哥畻', icon: 'money', accent: true }
- ]
- },
- {
- key: 'store',
- label: '浠撳偍鐗╂祦',
- icon: 'redis',
- items: [
- { name: '鍏ュ簱绠$悊', icon: 'download', accent: true },
- { name: '鍑哄簱绠$悊', icon: 'upload', accent: true, linkTarget: true },
- { name: '搴撳瓨绠$悊', icon: 'redis-list' }
- ]
- }
-]
+ // 缂╂斁姣斾緥
+ const scaleRatio = ref(1);
+ // 璁捐灏哄锛堝熀鍑嗗昂瀵革級
+ const designWidth = 1920;
+ const designHeight = 980;
-const canvasRef = ref(null)
-const sourceNodeRef = ref(null)
-const targetNodeRef = ref(null)
-const linkPath = ref('')
-const svgSize = ref({ width: 0, height: 0 })
-let resizeObserver = null
+ // 璁$畻缂╂斁姣斾緥
+ const calculateScale = () => {
+ const container = document.querySelector(".scale-container");
+ if (!container) return;
-function setNodeRef(item) {
- return (el) => {
- if (item.linkSource) sourceNodeRef.value = el
- if (item.linkTarget) targetNodeRef.value = el
- }
-}
+ // 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
+ const containerWidth = container.clientWidth;
-function resetLine() {
- linkPath.value = ''
-}
+ if (containerWidth === 0) {
+ // 濡傛灉杩樻病娓叉煋鍑烘潵锛屽欢杩熶竴涓嬪啀璁$畻
+ setTimeout(calculateScale, 100);
+ return;
+ }
-function updateLinkLine() {
- if (!canvasRef.value || !sourceNodeRef.value || !targetNodeRef.value || window.innerWidth <= 768) {
- resetLine()
- return
- }
+ // 璁$畻瀹藉害缂╂斁姣斾緥锛屼娇鍏跺缁堝崰婊″搴�
+ const scaleX = containerWidth / designWidth;
+ scaleRatio.value = scaleX;
+ };
- const canvasRect = canvasRef.value.getBoundingClientRect()
- const sourceRect = sourceNodeRef.value.getBoundingClientRect()
- const targetRect = targetNodeRef.value.getBoundingClientRect()
+ // 绐楀彛澶у皬鍙樺寲澶勭悊
+ const handleResize = () => {
+ calculateScale();
+ };
- svgSize.value = {
- width: Math.ceil(canvasRef.value.scrollWidth),
- height: Math.ceil(canvasRef.value.scrollHeight)
- }
+ // 鐢熷懡鍛ㄦ湡
+ onMounted(() => {
+ // 璁$畻鍒濆缂╂斁姣斾緥
+ nextTick(() => {
+ calculateScale();
+ });
- const startX = sourceRect.left + sourceRect.width / 2 - canvasRect.left
- const startY = sourceRect.bottom - canvasRect.top + 2
- const endX = targetRect.left + targetRect.width / 2 - canvasRect.left
- const endY = targetRect.top - canvasRect.top - 6
- const middleY = startY + Math.max(24, (endY - startY) / 2)
+ // 鐩戝惉绐楀彛澶у皬鍙樺寲
+ window.addEventListener("resize", handleResize);
+ });
- linkPath.value = `M ${startX} ${startY} L ${startX} ${middleY} L ${endX} ${middleY} L ${endX} ${endY}`
-}
-
-function handleResize() {
- requestAnimationFrame(() => {
- requestAnimationFrame(updateLinkLine)
- })
-}
-
-onMounted(async () => {
- await nextTick()
- handleResize()
- window.addEventListener('resize', handleResize)
- if (window.ResizeObserver && canvasRef.value) {
- resizeObserver = new ResizeObserver(handleResize)
- resizeObserver.observe(canvasRef.value)
- }
-})
-
-onUpdated(() => {
- nextTick(handleResize)
-})
-
-onBeforeUnmount(() => {
- window.removeEventListener('resize', handleResize)
- if (resizeObserver) resizeObserver.disconnect()
-})
+ onBeforeUnmount(() => {
+ window.removeEventListener("resize", handleResize);
+ });
</script>
-<style scoped>
-.architecture-page {
- min-height: calc(100vh - 84px);
- padding: 18px;
- background: #f6f7f9;
-}
-
-.canvas {
- position: relative;
- display: grid;
- gap: 10px;
-}
-
-.lane {
- display: grid;
- grid-template-columns: 94px 1fr;
- gap: 10px;
- align-items: stretch;
-}
-
-.lane__aside {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- padding: 8px 6px;
-}
-
-.lane__icon {
- font-size: 18px;
- color: #2563eb;
- flex-shrink: 0;
-}
-
-.lane__aside h2 {
- margin: 0;
- width: 34px;
- font-size: 12px;
- line-height: 1.15;
- font-weight: 600;
- color: #1f2937;
- text-align: center;
-}
-
-.lane__arrow {
- position: relative;
- width: 16px;
- height: 1px;
- background: #cbd5e1;
- flex-shrink: 0;
-}
-
-.lane__arrow::after {
- content: '';
- position: absolute;
- top: 50%;
- right: -1px;
- width: 6px;
- height: 6px;
- border-top: 1px solid #cbd5e1;
- border-right: 1px solid #cbd5e1;
- transform: translateY(-50%) rotate(45deg);
-}
-
-.lane__flow {
- position: relative;
- z-index: 2;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 8px;
- padding: 12px 14px;
- background: #f1f3f5;
-}
-
-.node-wrap {
- display: flex;
- align-items: center;
- gap: 8px;
- position: relative;
-}
-
-.node {
- width: 100px;
- min-height: 72px;
- padding: 4px 2px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 6px;
-}
-
-.node__mark {
- position: relative;
- width: 34px;
- height: 28px;
- display: flex;
- align-items: flex-start;
- justify-content: center;
-}
-
-.node__cap {
- position: absolute;
- top: 0;
- width: 18px;
- height: 14px;
- border-radius: 4px 4px 3px 3px;
- background: linear-gradient(180deg, #60a5fa, #2563eb);
- box-shadow: 0 3px 6px rgba(37, 99, 235, 0.16);
-}
-
-.node__base {
- position: absolute;
- bottom: 0;
- width: 30px;
- height: 12px;
- border-radius: 4px;
- background: linear-gradient(180deg, #ffffff, #e8edf3);
- border: 1px solid #d7dee7;
-}
-
-.node__icon {
- position: relative;
- z-index: 1;
- margin-top: 2px;
- font-size: 15px;
- color: #ffffff;
-}
-
-.node--accent .node__cap {
- background: linear-gradient(180deg, #2dd4bf, #0f766e);
- box-shadow: 0 3px 6px rgba(15, 118, 110, 0.16);
-}
-
-.node h3 {
- margin: 0;
- font-size: 12px;
- line-height: 1.3;
- font-weight: 600;
- color: #111827;
- text-align: center;
-}
-
-.flow-arrow {
- width: 18px;
- height: 1px;
- background: #b6c1ce;
- position: relative;
- flex-shrink: 0;
-}
-
-.flow-arrow::after {
- content: '';
- position: absolute;
- top: 50%;
- right: -1px;
- width: 6px;
- height: 6px;
- border-top: 1px solid #94a3b8;
- border-right: 1px solid #94a3b8;
- transform: translateY(-50%) rotate(45deg);
-}
-
-.link-overlay {
- position: absolute;
- inset: 0;
- z-index: 20;
- pointer-events: none;
- overflow: visible;
-}
-
-.link-overlay__path {
- fill: none;
- stroke: #94a3b8;
- stroke-width: 1;
- stroke-dasharray: 4 4;
- stroke-linecap: round;
- stroke-linejoin: round;
-}
-
-@media (max-width: 1180px) {
- .lane {
- grid-template-columns: 1fr;
- gap: 6px;
- }
-
- .lane__aside {
- justify-content: flex-start;
- }
-}
-
-@media (max-width: 768px) {
- .architecture-page {
- padding: 12px;
- }
-
- .lane__flow {
- display: grid;
- gap: 8px;
- padding: 12px;
- }
-
- .node-wrap {
- display: grid;
- gap: 8px;
- }
-
- .node {
+<style scoped lang="scss">
+ .scale-container {
+ position: relative;
width: 100%;
- min-height: 52px;
- flex-direction: row;
- justify-content: flex-start;
- gap: 10px;
+ height: calc(100vh - 84px);
+ background-color: #f5f7fa;
+ overflow: hidden;
}
- .node h3 {
- text-align: left;
+ .architecture-scaling-container {
+ position: relative;
+ width: 1920px;
+ height: 980px;
+ transform-origin: left top;
+ background-color: #f5f7fa;
+ transition: transform 0.3s;
}
- .flow-arrow {
- width: 1px;
- height: 16px;
- margin: 0 auto;
- }
+ .architecture-container {
+ padding: 20px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
- .flow-arrow::after {
- top: auto;
- bottom: -1px;
- right: 50%;
- transform: translateX(50%) rotate(135deg);
+ .header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 20px;
+ padding: 10px 20px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+
+ .title-bar {
+ width: 4px;
+ height: 20px;
+ background-color: #409eff;
+ margin-right: 12px;
+ border-radius: 2px;
+ }
+
+ .title-text {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 600;
+ color: #303133;
+ }
+ }
+
+ .image-wrapper {
+ flex: 1;
+ background: #fff;
+ border-radius: 8px;
+ padding: 20px 40px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+
+ .architecture-img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+ }
+ }
}
-}
</style>
--
Gitblit v1.9.3