Vue 3 + SCSS 实现全屏系统门户:五栏展开与灯光联动
技术栈:Vue 3 + SCSS + Vite
最近在实现一个全屏系统门户页面:五个业务模块横向铺满屏幕,鼠标移入某个模块后,模块展开、背景高亮、子模块上滑出现,同时边缘灯光会向相邻模块延伸。
这篇文章不讨论登录、权限、接口和弹窗,只拆解这套视觉交互如何实现。


一、整体结构
页面分为顶部导航和五栏主体:
text
数字综合门户
├── 数据资源中心
├── 生产运营中心
├── 综合管理中心
├── 服务门户
└── 平台支撑中心
每个模块内部包含:
html
<div class="sys-item">
<div class="mask-layer">
<img class="bg" src="module-bg.png" />
</div>
<div class="line">
<img class="light light--left" />
<img class="light light--right" />
</div>
<div class="child-wrap">
<div class="sys-name">模块标题</div>
<div class="sys-module">纯文字子模块列表</div>
<div class="sys-intro">系统简介</div>
</div>
<div class="name">默认竖排标题</div>
</div>
注意这里有两个标题区域:
.name:默认状态下显示的竖排标题;.child-wrap:激活状态下显示的系统标题、子模块和简介按钮。
二、用数据驱动模块
五个模块的标题、英文名、背景图和子模块都可以通过配置生成:
js
const systems = [
{
type: "datacenter",
zh: "数据资源中心",
en: "DATA RESOURCE CENTER",
class: "sys-datacenter",
bg: "./assets/portal_bg_01.png",
lightBg: "./assets/portal_bg_light_01.png",
children: ["资产目录", "标准规范", "指标平台"]
},
{
type: "management",
zh: "综合管理中心",
en: "MANAGEMENT CENTER",
class: "sys-management",
bg: "./assets/portal_bg_03.png",
lightBg: "./assets/portal_bg_light_03.png",
children: ["组织中心", "流程中心"]
}
]
每个模块的 class 用于挂载不同的斜切和位置规则,type 用于控制当前激活状态。
三、激活状态
鼠标进入模块时记录 activeSys:
js
function onSysMouseEnter(type) {
activeSys.value = type
}
function onSysMouseLeave() {
activeSys.value = ""
}
模板根据状态添加 class:
html
<div
class="sys-item"
:class="[
item.class,
activeSys === item.type && 'sys-active',
activeSys === 'management' && 'center-active'
]"
>
这里有两个关键状态:
sys-active:控制当前模块展开;center-active:综合管理中心比较特殊,激活后还需要调整左右相邻模块的位置。
四、五栏伸缩
五栏使用 Flex 平分布局:
scss
.app-screen__sys {
display: flex;
width: 100%;
height: 100%;
overflow: hidden;
}
.sys-item {
position: relative;
flex: 1;
height: 100%;
cursor: pointer;
}
激活后放大比例:
scss
.sys-item.sys-active {
flex: 1.5;
transition: all .5s ease-in-out;
}
数据资源中心和平台支撑中心的视觉权重更大,因此单独设置:
scss
.sys-datacenter,
.sys-support {
flex: 2;
&.sys-active {
flex: 2.5;
}
}
从实际效果看,不建议直接动画 width,使用 flex-grow 更容易维持五栏的整体比例。
五、默认遮罩与高亮背景
每个模块由背景图和遮罩层组成:
scss
.mask-layer {
position: absolute;
inset: 0;
overflow: hidden;
.bg {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
}
&::before {
position: absolute;
inset: 0;
content: "";
background-color: rgba(#2f3038, .8);
}
}
默认状态下用深色遮罩压暗背景,激活时隐藏遮罩:
scss
.sys-item.sys-active {
.mask-layer::before {
display: none;
}
}
同时 JavaScript 切换普通背景和高亮背景:
js
bg.src = active ? item.lightBg : item.bg
这样既有背景图的细节,也能明显区分当前模块。
六、斜切面板
中间三个模块使用旋转实现斜切分栏:
scss
.sys-production,
.sys-management,
.sys-service {
.mask-layer {
top: -10%;
left: -57%;
width: 133%;
height: 120%;
transform: rotate(15deg);
}
.bg {
transform: translateX(-50%) rotate(-15deg);
}
}
外层容器负责倾斜和留白,背景图反向旋转,保证画面中的建筑和城市纹理不会歪斜。
左侧数据资源中心和右侧平台支撑中心使用正向面板,通过宽度和高度参与整体构图。
七、子模块展开
子模块默认位于面板下方,并保持透明:
scss
.child-wrap {
position: absolute;
top: 100%;
left: 0;
width: 100%;
height: 0;
padding-left: 3.125rem;
opacity: 0;
transition: top .7s ease;
}
激活时移动到可视区域:
scss
.sys-item.sys-active .child-wrap {
top: 8.75rem;
opacity: 1;
}
子模块不显示图标,只保留纯文字列表:
scss
.module-item {
display: flex;
align-items: center;
font-size: 1.25rem;
line-height: 2.5rem;
cursor: pointer;
&:hover {
color: #0a39c2;
}
}
纯文字列表的好处是视觉更克制,不会和背景图争夺注意力。
八、综合管理中心的特殊联动
综合管理中心处于画面中间,激活时需要同时推动左右模块。
scss
.sys-item.center-active {
&.sys-production {
.mask-layer { left: -68%; }
.name { left: -4%; }
.line { left: -66%; }
}
&.sys-service {
.mask-layer { left: 26%; }
.line { left: 28%; }
}
}
这里把状态统一命名成 center-active,让"中央模块激活"成为独立状态,而不是在每个模块里重复判断。
九、灯光联动
分割线本身是一条渐变线:
scss
.line::before {
position: absolute;
top: 0;
width: 2px;
height: 100%;
content: "";
background: linear-gradient(
to bottom,
transparent,
rgba(#7d88ac, .6),
transparent
);
}
灯光图片默认宽高为 0,激活时展开:
scss
.sys-item.sys-active .line .light {
width: 100%;
height: 100%;
}
右侧模块的灯光联动可以通过相邻选择器完成:
scss
.sys-production.sys-active ~ .sys-management .line .light {
width: 100%;
height: 100%;
}
左侧反向联动则使用 :has():
scss
.sys-service:has(~ .sys-support.sys-active) .line .light {
width: 100%;
height: 100%;
}
这一层是页面视觉联动的关键:用户激活一个模块时,不只是当前模块变化,相邻模块的边缘也会同步亮起。
十、动画和性能细节
建议只对必要属性做过渡:
scss
.sys-item {
transition:
flex-grow .5s ease,
transform .5s ease,
opacity .5s ease;
}
不要随意使用:
scss
transition: all .5s;
同时增加减少动画模式:
scss
@media (prefers-reduced-motion: reduce) {
.sys-item,
.child-wrap,
.line .light {
transition: none !important;
}
}
背景图建议提前预加载,普通背景和高亮背景的尺寸保持一致,避免切换时发生明显跳动。
总结
这个门户页面的核心不是业务逻辑,而是状态驱动的视觉变化:
text
activeSys
→ 当前模块 flex 放大
→ 遮罩隐藏
→ 普通背景切换为高亮背景
→ 子模块上滑出现
→ 相邻模块灯光联动
实现时重点处理四件事:
- 用数据描述五个模块;
- 用 Flex 控制模块宽度变化;
- 用遮罩、旋转和背景图建立视觉层次;
- 用兄弟选择器和
:has()完成跨模块灯光联动。
这套结构不依赖具体业务数据和弹窗功能,后续只需要替换模块名称、背景图和字体资源,就能复用到其他全屏门户页面。