电信网络运维平台(OSS)通常具有业务模块繁多、数据可视化要求高、交互逻辑复杂等特点。如何构建一个既具备高性能,又易于维护、支持多主题切换和细粒度权限控制的前端架构,是工程化落地的核心挑战。
本文将分享如何基于 Vue 3 + TypeScript + Vite 技术栈,结合 Spring Cloud Alibaba 后端生态,构建一个模块化、可扩展的大型前端应用。我们将重点探讨如何利用 CSS Variables 实现动态主题切换,以及如何设计基于 RBAC 模型的菜单与按钮权限控制系统。
一、 总体架构设计
1. 技术选型
- 核心框架:Vue 3 (Composition API) + TypeScript。TS 的类型系统能有效约束复杂的业务数据结构(如基站信息、告警对象),提升代码健壮性。
- 构建工具:Vite。利用其原生 ESM 特性,实现秒级冷启动和热更新,显著提升开发体验。
- 状态管理:Pinia。替代 Vuex,提供更简洁的 API 和完美的 TS 支持,用于管理用户信息、主题配置、全局字典等。
- UI 组件库:Element Plus。提供丰富的基础组件,满足后台管理系统的基本需求。
- 路由管理:Vue Router 4。支持动态路由添加,是实现权限控制的关键。
2. 目录结构规范
采用功能模块化的目录结构,便于大型团队协作:
src/
├── api/ # 接口请求封装(按微服务模块划分:perf, jom, netmgr...)
├── assets/ # 静态资源
├── components/ # 公共组件(BusinessChart, MapContainer...)
├── layout/ # 布局组件(Sidebar, Navbar, AppMain)
├── router/ # 路由配置(静态路由 + 动态路由逻辑)
├── stores/ # Pinia Store(user, theme, permission)
├── styles/ # 全局样式与主题变量
├── utils/ # 工具函数(auth, request, validate)
├── views/ # 页面视图(按业务域划分:PerfAnalysis, WorkOrder...)
└── App.vue
二、 动态主题切换实现
借鉴原系统中 Content/Theme/Blue 和 Red 的多主题设计理念,我们在 Vue 3 中通过 CSS Variables (CSS 自定义属性) 实现无刷新动态换肤。
1. 定义主题变量
在 src/styles/variables.css 中定义两套主题的核心色值:
css
:root {
/* 默认蓝色主题 */
--primary-color: #409eff;
--bg-color: #f0f2f5;
--sidebar-bg: #304156;
--text-color: #303133;
}
[data-theme='red'] {
/* 红色主题 */
--primary-color: #f56c6c;
--bg-color: #fff0f0;
--sidebar-bg: #5e3030;
--text-color: #2c3e50;
}
2. Pinia 管理主题状态
创建 themeStore.ts:
css
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useThemeStore = defineStore('theme', () => {
const currentTheme = ref<'blue' | 'red'>('blue');
function setTheme(theme: 'blue' | 'red') {
currentTheme.value = theme;
// 动态修改 html 标签的 data-theme 属性
document.documentElement.setAttribute('data-theme', theme);
// 持久化到 localStorage
localStorage.setItem('app-theme', theme);
}
// 初始化时读取本地存储
function initTheme() {
const savedTheme = localStorage.getItem('app-theme') as 'blue' | 'red' || 'blue';
setTheme(savedTheme);
}
return { currentTheme, setTheme, initTheme };
});
3. 组件中使用变量
在 Vue 组件的 <style> 中直接使用变量,确保样式随主题自动切换:
css
.sidebar {
background-color: var(--sidebar-bg);
color: #fff;
}
.el-button--primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
三、 基于角色的权限控制 (RBAC)
电信运维系统涉及不同角色(如:监控员、优化专家、管理员),需要精确控制菜单可见性和按钮操作权限。
1. 后端接口设计
Spring Cloud Gateway 配合 SSO 服务,在用户登录成功后返回该用户的权限标识列表(Permission Codes)和菜单树(Menu Tree)。
css
// /api/user/info 响应示例
{
"roles": ["OPTIMIZER"],
"permissions": [
"perf:view",
"perf:export",
"jom:create_order",
"netmgr:edit_cell"
],
"menus": [
{
"path": "/perf",
"name": "PerfAnalysis",
"component": "Layout",
"children": [...]
}
]
}
2. 动态路由生成
在 permissionStore.ts 中,根据后端返回的菜单树,利用 router.addRoute 动态挂载路由。
TypeScript
import { useRouter } from 'vue-router';
import Layout from '@/layout/index.vue';
// 映射组件路径
const modules = import.meta.glob('@/views/**/*.vue');
function generateRoutes(menuList: any[]) {
return menuList.map(menu => {
const route: any = {
path: menu.path,
name: menu.name,
meta: { title: menu.title, icon: menu.icon }
};
if (menu.component === 'Layout') {
route.component = Layout;
} else {
// 动态导入组件
const componentPath = `./views/${menu.component}.vue`;
route.component = modules[componentPath];
}
if (menu.children && menu.children.length > 0) {
route.children = generateRoutes(menu.children);
}
return route;
});
}
3. 按钮级权限指令
为了控制页面内具体按钮(如"导出"、"派单")的显隐,我们自定义一个 Vue 指令 v-auth。
注册指令 (directives/auth.ts):
TypeScript
import { DirectiveBinding } from 'vue';
import { useUserStore } from '@/stores/user';
export const auth = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding;
const userStore = useUserStore();
const permissions = userStore.permissions;
if (value && !permissions.includes(value)) {
el.parentNode?.removeChild(el);
}
}
};
在模板中使用:
html
<template>
<!-- 只有拥有 perf:export 权限的用户才能看到导出按钮 -->
<el-button v-auth="'perf:export'" type="primary">导出数据</el-button>
<!-- 只有拥有 jom:create_order 权限的用户才能看到派单按钮 -->
<el-button v-auth="'jom:create_order'" @click="handleDispatch">一键派单</el-button>
</template>
四、 网络请求与微服务适配
前端通过 Axios 拦截器统一处理请求,适配 Spring Cloud Gateway 的路由规则。
TypeScript
// utils/request.ts
import axios from 'axios';
import { useUserStore } from '@/stores/user';
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, // 网关地址
timeout: 15000
});
// 请求拦截器:携带 Token
service.interceptors.request.use(config => {
const userStore = useUserStore();
if (userStore.token) {
config.headers['Authorization'] = `Bearer ${userStore.token}`;
}
return config;
});
// 响应拦截器:统一错误处理
service.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
// Token 过期,跳转登录页
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default service;
五、 总结
通过 Vue 3 + TypeScript 的现代化架构,我们成功构建了一个灵活、高效的电信运维前端平台:
- 动态主题:利用 CSS Variables 和 Pinia,实现了蓝/红主题的无缝切换,满足了不同运维场景下的视觉偏好。
- 精细权限:通过动态路由和自定义指令,实现了从菜单到按钮的全链路权限控制,保障了系统数据的安全性。
- 工程化规范:TypeScript 的类型约束和 Vite 的快速构建,显著提升了代码质量和开发效率。
这种架构不仅适用于当前的运维业务,也为未来引入 GIS 可视化、实时信令追踪等复杂功能奠定了坚实的基础。
互动环节
💬 你们公司的动态指标计算引擎是怎么实现的?遇到过哪些难题?欢迎在评论区分享!
⭐ 如果觉得这篇文章有帮助,欢迎点赞、收藏、转发!
🔔 关注我,下一篇将分享《Web GIS 最佳实践:Vue 集成 Leaflet/OpenLayers 实现基站海量点位渲染》
版权声明:本文为原创文章,转载请注明出处。商业转载请联系作者获得授权。
作者简介:系统架构 师,专注于电信大数据平台架构设计与运维。目前负责日均处理2亿条消息的ucp平台,擅长分布式系统设计、消息中间件运维和高可用架构