Vue 3 + SCSS 实现全屏系统门户:五栏展开与灯光联动

0 阅读5分钟

Vue 3 + SCSS 实现全屏系统门户:五栏展开与灯光联动

技术栈:Vue 3 + SCSS + Vite

最近在实现一个全屏系统门户页面:五个业务模块横向铺满屏幕,鼠标移入某个模块后,模块展开、背景高亮、子模块上滑出现,同时边缘灯光会向相邻模块延伸。

这篇文章不讨论登录、权限、接口和弹窗,只拆解这套视觉交互如何实现。

默认五栏状态

综合管理中心展开状态

一、整体结构

页面分为顶部导航和五栏主体:

数字综合门户
├── 数据资源中心
├── 生产运营中心
├── 综合管理中心
├── 服务门户
└── 平台支撑中心

每个模块内部包含:

<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:激活状态下显示的系统标题、子模块和简介按钮。

二、用数据驱动模块

五个模块的标题、英文名、背景图和子模块都可以通过配置生成:

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:

function onSysMouseEnter(type) {
  activeSys.value = type
}

function onSysMouseLeave() {
  activeSys.value = ""
}

模板根据状态添加 class:

<div
  class="sys-item"
  :class="[
    item.class,
    activeSys === item.type && 'sys-active',
    activeSys === 'management' && 'center-active'
  ]"
>

这里有两个关键状态:

  • sys-active:控制当前模块展开;
  • center-active:综合管理中心比较特殊,激活后还需要调整左右相邻模块的位置。

四、五栏伸缩

五栏使用 Flex 平分布局:

.app-screen__sys {
  display: flex;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.sys-item {
  position: relative;
  flex: 1;
  height: 100%;
  cursor: pointer;
}

激活后放大比例:

.sys-item.sys-active {
  flex: 1.5;
  transition: all .5s ease-in-out;
}

数据资源中心和平台支撑中心的视觉权重更大,因此单独设置:

.sys-datacenter,
.sys-support {
  flex: 2;

  &.sys-active {
    flex: 2.5;
  }
}

从实际效果看,不建议直接动画 width,使用 flex-grow 更容易维持五栏的整体比例。

五、默认遮罩与高亮背景

每个模块由背景图和遮罩层组成:

.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);
  }
}

默认状态下用深色遮罩压暗背景,激活时隐藏遮罩:

.sys-item.sys-active {
  .mask-layer::before {
    display: none;
  }
}

同时 JavaScript 切换普通背景和高亮背景:

bg.src = active ? item.lightBg : item.bg

这样既有背景图的细节,也能明显区分当前模块。

六、斜切面板

中间三个模块使用旋转实现斜切分栏:

.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);
  }
}

外层容器负责倾斜和留白,背景图反向旋转,保证画面中的建筑和城市纹理不会歪斜。

左侧数据资源中心和右侧平台支撑中心使用正向面板,通过宽度和高度参与整体构图。

七、子模块展开

子模块默认位于面板下方,并保持透明:

.child-wrap {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  height: 0;
  padding-left: 3.125rem;
  opacity: 0;
  transition: top .7s ease;
}

激活时移动到可视区域:

.sys-item.sys-active .child-wrap {
  top: 8.75rem;
  opacity: 1;
}

子模块不显示图标,只保留纯文字列表:

.module-item {
  display: flex;
  align-items: center;
  font-size: 1.25rem;
  line-height: 2.5rem;
  cursor: pointer;

  &:hover {
    color: #0a39c2;
  }
}

纯文字列表的好处是视觉更克制,不会和背景图争夺注意力。

八、综合管理中心的特殊联动

综合管理中心处于画面中间,激活时需要同时推动左右模块。

.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,让“中央模块激活”成为独立状态,而不是在每个模块里重复判断。

九、灯光联动

分割线本身是一条渐变线:

.line::before {
  position: absolute;
  top: 0;
  width: 2px;
  height: 100%;
  content: "";
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(#7d88ac, .6),
    transparent
  );
}

灯光图片默认宽高为 0,激活时展开:

.sys-item.sys-active .line .light {
  width: 100%;
  height: 100%;
}

右侧模块的灯光联动可以通过相邻选择器完成:

.sys-production.sys-active ~ .sys-management .line .light {
  width: 100%;
  height: 100%;
}

左侧反向联动则使用 :has():

.sys-service:has(~ .sys-support.sys-active) .line .light {
  width: 100%;
  height: 100%;
}

这一层是页面视觉联动的关键:用户激活一个模块时,不只是当前模块变化,相邻模块的边缘也会同步亮起。

十、动画和性能细节

建议只对必要属性做过渡:

.sys-item {
  transition:
    flex-grow .5s ease,
    transform .5s ease,
    opacity .5s ease;
}

不要随意使用:

transition: all .5s;

同时增加减少动画模式:

@media (prefers-reduced-motion: reduce) {
  .sys-item,
  .child-wrap,
  .line .light {
    transition: none !important;
  }
}

背景图建议提前预加载,普通背景和高亮背景的尺寸保持一致,避免切换时发生明显跳动。

总结

这个门户页面的核心不是业务逻辑,而是状态驱动的视觉变化:

activeSys
  → 当前模块 flex 放大
  → 遮罩隐藏
  → 普通背景切换为高亮背景
  → 子模块上滑出现
  → 相邻模块灯光联动

实现时重点处理四件事:

  1. 用数据描述五个模块;
  2. 用 Flex 控制模块宽度变化;
  3. 用遮罩、旋转和背景图建立视觉层次;
  4. 用兄弟选择器和 :has() 完成跨模块灯光联动。

这套结构不依赖具体业务数据和弹窗功能,后续只需要替换模块名称、背景图和字体资源,就能复用到其他全屏门户页面。