1、登录流程
- 进入登录页,获取验证码
// 文件位置
// src\pages\Login\Login.vue
mounted() {
this.setIdentifierObj();
}
private async setIdentifierObj() {
const res = await getIdentifierImg();
if (res) {
const { img, cToken } = res;
if (img && cToken) {
this.identifierObj.imgUrl = img;
this.identifierObj.cToken = cToken; // 比对验证码是否正确
}
}
}
- 点击登录,首先因为平台使用第三方库crypto-js对登录密码进行加密。所以首先我们会调用接口,请求密钥。
// 文件位置
// src\pages\Login\Login.vue
const skRes = await userApi.getSk();
if(skRes) {
const { data } = skRes;
const dataArr = data.split('');
dataArr.splice(0, 1, 'E');
dataArr.splice(7, 1, 'D');
dataArr.splice(15, 1, 'U');
const md5Str: string = dataArr.join('');
}
- 拿到密钥后对密码进行加密
// 文件位置
// src\pages\Login\Login.vue
const aesKey = {
key: md5Str,
iv: '',
modeType: ''
}
JsCrypto.setDataAndAesKey(loginFormModal.password, aesKey);
- 调用登录接口,验证登录
// 文件位置
// src\pages\Login\Login.vue
const res = await userApi.login({
...loginFormModal,
cToken: this.identifierObj.cToken,
textCode: this.identifier
});
- 登录成功,拿到返回的token,并存储token,localstorage和cookie中各存一份。目前看存储到cookie没啥太大意义还徒增请求头体积。
// 文件位置
// src\pages\Login\Login.vue
this.$cookies.set('access_token', accessToken);
localStorage.userToken = accessToken;
- 关于记住密码,存储到cookie有问题,请求的时候会携带cookie徒增请求头体积,第二不持久,因该存储到localstorage里
// 文件位置
// src\pages\Login\Login.vue
if (rememberPassword) {
const { aesPassword, aesKey } = userInfo;
this.setUserInfoToCookie({
password: aesPassword,
account: this.loginFormObj.account,
aesKey: aesKey
});
} else {
this.$cookies.remove('accountInfo');
}
- 后续请求,携带token
// 文件位置
// src\http\interceptors.ts
this.instance.interceptors.request.use((config) => {
// .......
const token = localStorage.userToken
if (token) {
config.headers.Authorization = token
}
// .......
});
2、路由权限与路由跳转
- 登录成功后,会跳转到路由:/home/atlasManage/home/structure,此为三级路由
// 文件位置
// src\pages\Login\Login.vue
function loginSuccess(res, rememberPassword?, userInfo?) {
this.$router.push({
path: `/home/atlasManage/home/structure?${strArr.join('&')}`
})
}
- 访问路由,进入路由前置首守卫,在路由守卫里做判断,比如如果没有token,跳转到登录页,随后调用接口i请求当前用户下所有的路由菜单**(根据token里的用户基本信息判断该返回哪些路由)**。
// 文件位置
// src\router\index.ts
const res = await userApi.getMunuList({
system: 'jk'
});
- 具体的路由返回是根据角色配置的,在角色管理页面给角色分配路由,再给用户分配角色,这样用户就有了对应的路由。以内容合规审核员为例,给内容合规审核员分配路由权限
- 获取到所有的路由数据后,会对路由数据做处理转成符合VueRouter路由的配置项的格式。同时我们回想路由的第一层push一个路由配置
// 文件位置
// src\router\index.ts
const routerObj: Array<any> = [
{
path: '/home',
component: () => import('@/pages/Layout/Layout.vue'),
children: []
}
];
// 递归函数处理路由数据
recursionFun(menuList, routerArr);
routerObj[0].children = routerArr;
- 此时App.vue组件的结构如下,TopNav是顶部导航栏,而对应的router-view渲染我们的一级路由/home,也就是Layout.vue组件,具体结构图如下。
<template>
<div id="app">
<TopNav></TopNav>
<router-view />
</div>
</template>
- 在TopNav.vue组件内部如下,左侧为侧边栏,右侧为router-view将渲染的二级路由内容
<template>
<Layout class="main-container" :class="{ 'show-side-nav': showSideNav }">
<SideNav
:parentId="$store.state.common.currentMenuId"
v-show="showSideNav"
:class="{ 'is-expand': $store.state.common.sideExpand }"
></SideNav>
<router-view class="page-content" :class="{'not-padding':notPadding}" />
</Layout>
</template>
- 我们会将二级路由的路径全部绑定到左侧的侧边栏,所以当我们点击侧边栏的时候,会push到二级的路由,也就是渲染右侧的二级路由内容。点击左侧侧边栏即可切换路由,切换右侧的内容。
public menuClick (name: string): void {
const urlLink: string = name + this.getQueryParam(name);
this.$router.push({
path: urlLink
});
}
- 切换顶部导航栏,首先是改变了currentMenuId,随后跳转路由到/home/authManage
public menuClick (menuData: any): void {
this.$store.commit('changeCurrentId', this.activeId);
this.$router.push({
path: urlLink
})
}
- 首先左侧侧边栏监听了currentMenuId,会根据它做筛选,不同的Id展示不同的侧边栏,侧边栏对应不同的路由路径。其次右侧的内容会变为/home/authManage对应的组件:/Auth/index.vue
<template>
<div class="auth-manage-container">
<div class="right-container">
<div class="header-container common-header-container">
<span class="title">{{ headerTitle }}</span>
</div>
<router-view></router-view>
</div>
</div>
</template>
- 同时在前置路由守卫里给/home/authManage路由增加了一个路由配置,它对应的就是/home/authManage/user路由对应的/Auth/UserVue.vue
if (item.link === '/home/authManage') {
obj.children.unshift({
path: '/',
component: obj.children[0].component
});
}
- 关于标注平台,是通过微服务qiankun框架注册的子应用。在main.ts中有
// 文件位置
// src\main.ts
// 引入子应用配置
getQianKunConfig().then((result) => {
(window as any).qiankun.registerMicroApps(result.subApps, {
})
}).catch((err) => {
console.log(err);
});
- getQianKunConfig返回的格式为
{
"subApps": [
{
"name":"qianKunMarkManage",
"entry":"https://fxm-graph-jk.iflyresearch.com/qianKunMarkManage/",
"activeRule":"#/qianKunMarkManage",
"container": "#sunIframe",
"props": {
"baseUrl":"https://fxm-graph-jk.iflyresearch.com/qianKunMarkManage/"
}
}
]
}
- 最终渲染在App.vue
<template>
<div id="app">
<div id="sunIframe" :class="{ 'is-qiankun': isQiankun }"></div>
</div>
</template>
3、功能权限(按钮权限)
- 权限管理 -> 功能列表增加功能权限,以增加一个删除资源为例,英文名称为:btn_deleteResource
- 新增完之后在菜单管理给路由增加这个新增删除的功能,这里需要注意:
如果功能权限里什么都不配置,那么说明这个路由下不设置按钮权限;
如果配置了,则说明这个路由下的这些按钮就会被赋予权限,不经过进一步的处理是看不见的;
- 这里给试卷管理配置了按钮权限后,需要回到角色管理,给角色增加按钮权限。比如给内容合规审核员的试卷管理路由配置展示编辑标签按钮,但是不展示资源变更按钮。
- 这个时候我们进入到内容合规审核员的账号
- 具体的实现是搭配自定义指令v-has实现,在试卷管理的列表页,我们使用v-has绑定了需要增加权限的按钮
<!-- 文件位置 -->
<!-- src\pages\AtlasManage\ResourceManage\list.vue -->
<Button v-if="canShowChangeResource(item)"
size="small"
:disabled="getResult(item, btnItem.code, !btnItem.canHandle)"
@click="handleBtnClick(btnItem.code, item, index)"
v-has:code="'btn_' + btnItem.code"
>{{ setBtnName(item, btnItem.name) }}
</Button>
- 然后在自定义指令里代码如下,判断如果绑定了v-has并且没有出现在路由配置里,则不渲染。按钮是和路由对应的看下数据结构即可。
// 文件位置
// src\customDirective.ts
if (unionPromissions.length > 0 && currentBtnValue !== 'btn_undefined') {
if (unionPromissions && unionPromissions.indexOf(currentBtnValue) < 0) {
// 开发过程中去掉这里todo
(el as any).parentNode.removeChild(el);
}
}