本文最后更新于:2 个月前
                  
                
              
            
            
              
                
                框架构建 安装 | Vue CLI (vuejs.org) 
Arco Design Vue 
1 2 3 4 5 6 7 8 9 10 11 12 13 <template>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template>
1 2 3 4 5 6 7 8 9 10 11 12 <template>
路由跳转 router 下的 index.ts:
1 2 3 4 5 6 7 8 9 10 import  { createRouter, createWebHistory } from  "vue-router" ;import  { routes } from  "@/router/routes" ;const  router = createRouter ({history : createWebHistory (process.env .BASE_URL ),export  default  router;
router 下的 router.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  { RouteRecordRaw  } from  "vue-router" ;import  HomeView  from  "@/views/HomeView.vue" ;export  const  routes : Array <RouteRecordRaw > = [path : "/" ,name : "首页" ,component : HomeView ,path : "/about" ,name : "关于" ,component : () => import ( "../views/AboutView.vue" ),
动态导航栏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <a-menu
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  { ref } from  "vue" ;import  { useRouter } from  "vue-router" ;import  { routes } from  "@/router/routes" ;const  router = useRouter ();const  selectedKeys = ref (["/" ]);afterEach ((to, from , failure ) =>  {value  = [to.path ];const  doMenuClick  = (key: string  ) => {push ({path : key,
效果如下: 
全局状态管理 vuex/examples/classic/shopping-cart/store/index.js at main · vuejs/vuex (github.com) 
开始 | Vuex (vuejs.org) 
store 下的 user.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import  { StoreOptions  } from  "vuex" ;export  default  {namespaced : true ,state : () =>  ({loginUser : {userName : "未登录" ,actions : {async  getLoginUser ({ commit, state }, payload ) {commit ("updateUser" , { userName : "memory"  });mutations : {updateUser (state, payload ) {loginUser  = payload;as  StoreOptions <any >;
index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 import  { createStore } from  "vuex" ;import  user from  "@/store/user" ;export  default  createStore ({state : {},getters : {},mutations : {},actions : {},modules : {
main.ts
1 2 3 4 import  store from  "./store" ;createApp (App ).use (ArcoVue ).use (store).use (router).mount ("#app" );
store 原理(执行流程) 
1 2 3 4 5 6 setTimeout (() =>  {dispatch ("user/getLoginUser" , {userName : "回忆如初" ,3000 );
这样引入 store,执行 dispatch方法,根据 actions 下的路径,提供参数,执行 mutations 下的方法。
1 2 3 4 5 actions : {async  getLoginUser ({ commit, state }, payload ) {commit ("updateUser" , payload);
1 2 3 4 5 mutations : {updateUser (state, payload ) {loginUser  = payload;
mutations 改变了 state 的值,根据传入的参数改变了。
我们尝试在页面获取 store 值,并展示:
1 2 3 4 5 <a-col flex="100px">
效果如下:
另外,action 这里可以写死,不接受参数:
1 2 3 4 5 actions : {async  getLoginUser ({ commit, state }, payload ) {commit ("updateUser" , { userName : "memory"  });
全局权限管理 关键在于这段逻辑,App.vue 下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import  router from  "@/router" ;import  store from  "@/store" ;beforeEach ((to, from , next ) =>  {if  (to.meta ?.access  === "canAdmin" ) {if  (store.state .user ?.loginUser ?.role  !== "admin" ) {next ("/noAuth" );return ;next ();
设置路由访问权限,必须为管理员可见:
1 2 3 4 5 6 7 8 {path : "/auth" ,name : "管理员可见" ,component : AuthView ,meta : {access : "canAdmin" ,
默认用户权限,测试用:
1 2 3 4 5 6 state : () =>  ({loginUser : {userName : "未登录" ,role : "admin" ,
我们发现能正常跳转页面,但这样做:
1 2 3 4 5 6 7 setTimeout (() =>  {dispatch ("user/getLoginUser" , {userName : "回忆如初" ,role : "noAdmin" ,3000 );
三秒过后,该用户不是管理员权限,访问一个管理员可见的页面,直接重定向:
隐藏菜单 构造通用的导航栏组件,根据配置控制菜单栏的显隐
1 2 3 4 5 6 7 8 {path : "/hide" ,name : "隐藏页面" ,component : noAuthView,meta : {hideInMenu : true ,
过滤,仅展示显示在菜单上的路由数组
1 2 3 <a-menu-item v-for="item in visibleRoutes" :key="item.path">
1 2 3 4 5 6 import  { routes } from  "@/router/routes" ;const  visibleRoutes = routes.filter ((item, index ) =>  {return  !item.meta ?.hideInmenu ;
除了 根据配置权限隐藏菜单 ,还需要根据用户权限,只有具有相关权限的用户,才能看到该菜单。
检测用户权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import  ACCESS_ENUM  from  "@/access/accessEnum" ;const  checkAccess  = (loginUser: any , needAccess = ACCESS_ENUM.NOT_LOGIN ) => {const  loginUserAccess = loginUser?.userRole  ?? ACCESS_ENUM .NOT_LOGIN ;if  (needAccess === ACCESS_ENUM .NOT_LOGIN ) {return  true ;if  (needAccess === ACCESS_ENUM .USER ) {if  (loginUserAccess === ACCESS_ENUM .NOT_LOGIN ) {return  false ;if  (needAccess === ACCESS_ENUM .ADMIN ) {if  (loginUserAccess !== ACCESS_ENUM .ADMIN ) {return  false ;return  true ;export  default  checkAccess;
使用计算属性,使得用户信息发生变更时,触发菜单栏的重新渲染,。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const  visibleRoutes = computed (() =>  {return  routes.filter ((item, index ) =>  {if  (item.meta ?.hideInmenu ) {return  false ;if  (checkAccess (store.state .user ?.loginUser , item.meta ?.access  as  string )return  false ;return  true ;
全局项目入口 1 2 3 4 5 6 7 const  doInit  = (console .log ("项目全局入口" );onMounted (() =>  {doInit ();
根据后端生成接口 后端接口文档:
我们根据后端接口文档,一键生成前端 HTTP 请求接口:
官方文档:
ferdikoomen/openapi-typescript-codegen: NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification (github.com) 
安装:
1 npm install openapi-typescript-codegen --save-dev
执行命令生成代码:
1 openapi --input http://localhost:8121/api/v2/api-docs?group=memory-oj --output ./generated --client axios
如上,执行成功,成功生成 HTTP 请求接口
MarkDown 编辑器 代码编辑器