海南系统详细设计
海南系统详细设计
技术选型
vue3+ts+vite+element plus
初始化项目基本需求
- 动态路由权限验证
- 按钮权限
- 接口代理转发
- 响应请求拦截处理
- ui框架处理
- 侧栏菜单以及面包屑
- 通用组件封装
技术选型
充分发挥了 Vite 的模块化和快速热更新的特性,结合 Vue 3 和 TypeScript 构建了一个基于单文件组件和组合式 API 的前端架构。这种结构允许我们充分利用 Vite 的即时开发体验,并使用 TypeScript 强大的类型检查功能,提高了代码的可维护性和可读性
动态路由设计
1 | router |

动态路由主要为动态添加路由的处理
动态路由在设计的时候是有两种方案:
- 通过在前端注册路由表的方式,通过请求接口拿到权限路由,之后通过逐级查找路由表获取到真实的路由,将路由进行注册
- 后端直接返回前端路由表,通过请求接口直接拿到真实的路由表,前端直接进行注册
第二种方式可以保证路由表更加真实安全,但是也会存在问题
- 需要额外维护一个权限页面
- 路由表存放在数据库中,数据需要与前端工程目录一一对应
- 修改页面路径文件名需要同步修改数据库中的对应内容
- 整体路由结构前端不可见,后续维护可能会更加麻烦
因为本系统安全性需求并没有那么高,所以最后还是采用可以更加灵活开发的方案一
动态路由有几个需要处理的问题:
动态路由跳转白屏
在首次跳转的时候,还没有进行添加动态路由,此时可以通过next({ ...to, replace: true });的方式避免没有获取路由的情况页面刷新404问题
刷新是会重新执行路由守卫的,所以此时动态路由还没有进行注册,所以会跳转到404页面,解决方法就是将404页面的路由注册也改为动态路由,在请求的路由菜单全部注册完成之后,在注册404页面的路由,这样就可以保证路由在解析的时候可以拿到正确的路由菜单
如何设计路由表
根据业务的模块对前端路由进行按文件进行划分,将路由地址作为键,功能模块路由文件作为值,存放在map对象的集合当中,之后拿从接口获取的路由url去匹配真实路由,再进行添加,通过路由表的形式,实现路由的懒加载
按钮权限
实现按钮级权限有三种方式:
- 通过注册vue的自定义指令实现对按钮的显示和隐藏
- 通过二次封装buttone的方式,将权限字符串作为参数传入,由组件内部进行权限判断控制显示隐藏
- 通过封装hooks的方式,在权限页面将hooks进行引入,同时传入权限字符串,通过vif进行显示隐藏
三种方式各有优势,最后还是采用第一种自定义指令的方式,一是为了与其他项目逻辑保持一致,另外通过自定义方式使用起来相对来说会更加简便。

公共组件layout

layout基本由左侧菜单栏,头部的占位区,中间为router-view,后续的页面都渲染在这里
navMenu实现:

根据菜单表进行判断,如果没有子菜单,那么就是功能菜单,此时需要添加点击跳转的功能,如果有子菜单,那么就是父级菜单,可以点击进行折叠子菜单,没有跳转功能。
因为所有页面都依赖这个组件,所以使用嵌套路由,所有的业务功能页面都将通过路由匹配渲染在这个组件中
1 | const route: RouteRecordRaw = { |
将功能页面作为children的一部分父级路由都为layout,因此可以在初始化路由的时候,统一使用lay组件来渲染页面内容。
通用table组件
由于后台管理项目中存在很多的table表格,还有很多带标签的表格页,所以对表格进行二次封装,便于快速创建出表格页
配置项:
| config | 类型 | 描述 |
|---|---|---|
| tableConfig | array |
存放table的配置内容 |
| tableData | array | table的展示数据 |
| isShowRightExtend | boolean | 是否展示表格右侧扩展区 |
| tagName | string | 展示右侧tag需要的name,需要开启 isShowRightExtend |
| tableConfig | 类型 | 描述 |
|---|---|---|
| type | enum | COLUMN_TYPES.COMMON,COLUMN_TYPES.EXTENDS,COLUMN_TYPES.INDEX,COLUMN_TYPES.SELECTION有普通列,序号列,多选框列,扩展列四种类型 |
| label | string | 对应el-table的label,用于展示名称 |
| prop | string | 对应el-table的prop,匹配列内容的字段名 |
| width | string | 对应el-table的width,用于控制控制该列的宽度 |
| 列表类型 | 描述 |
|---|---|
| COMMON | 普通列表 |
| SELECTION | 列表中带有多选框如果开启了表格右侧扩展区,可以开启点击表格列表项选中当前行功能 |
| EXTENDS | 扩展列表,这种类型可以对列表数据进行改造后展示 |
| INDEX | 序号列,可以展示当前序号 |
关于表格的selection优化点
当使用element的selection多选框的时候,在element ui的表格中并没有提供相应的方法区获取已经选中的表格项,但是在源码中可以看到,element 是有对已选的表格项进行处理的,table的实例的selection属性就记录着用户已经勾选的表格项,所以可以通过ref去获取table的实例,之后调用selection就可以,不需要再进行手动维护一个已选项的数组和添加删除逻辑了
在element plus中,官方正式开放了获取已选表格的方法,可以通过getSelectionRows()进行获取
通用表格组件的二次封装
考虑到部分页面会存在大量布局设计相同,但不只是有表格的页面,所以会有场景需要对表格组件再次进行封装。以我封装的带标签页tabs的表格为例
该组件需要解决的问题有:如何进行深层次进行插槽内容的传递、如何在切换标签时对用户操作进行拦截,校验本页信息是否正确
- 深层次插槽内容传递:
通过动态插槽名+动态插槽实现

1 | <tab-table :tab-config="configData" ref="tabDialogRef" @tab-click="handleTabClick($event, 'dialog')" |
1 | <common-table :config="pane" class="table-container" ref="tableRef" :loading="props.loading"> |
1 | <el-table-column v-else-if="column.type === COLUMN_TYPES.EXTENDS" :label="column.label" :prop="column.prop" |
因为插槽需要通过template进行包裹,所以子组件采用template根据插槽配置数组进行循环渲染,通过vue3的模板引入设置动态的插槽name,之后设置插槽存放父组件传递过来的插槽内容
在孙子组件中对对应的插槽内容进行匹配,渲染dom
拦截标签切换
标签拦截通过使用代理模式,通过js中的proxy对象对标签绑定的activeName进行代理,代理模式允许在访问或修改目标对象的属性时进行拦截,并且可以在拦截器中执行一些额外的逻辑。
1
proxy = new Proxy(this.isOpenAutoActivateParams, this.getAutoActivateHandler())
1
2
3
4
5
6
7
8getAutoActivateHandler() {
return {
get: Reflect.get,
set: (target, property, value) => {
// ...
},
};
}get拦截器使用了Reflect.get,表示直接从目标对象中获取属性值。set拦截器在属性值发生变化时进行拦截和处理。
使用观察者模式的思想,当属性变化时触发更新前的额外操作,进行拦截操作





