当前位置: 首页 > news >正文

后端使用 AI 开发前端速成:第七期:路由、权限与页面骨架

第七期:路由、权限与页面骨架

本期目标:掌握管理后台的"骨架"搭建——侧边栏、路由、登录、权限
核心理念:页面骨架是管理后台的"基础设施",和用户列表页一样重要


目录

  • 第一章:管理后台骨架结构
  • 第二章:Vue Router / React Router
  • 第三章:登录页开发
  • 第四章:路由守卫与权限控制
  • 第五章:布局组件——侧边栏 + 顶部栏
  • 第六章:Axios 封装实战
  • 第七章:课后作业

第一章:管理后台骨架结构

1.1 典型骨架布局

┌─────────────────────────────────────┐ │ Logo 顶部导航栏(面包屑、用户) │ ← Header ├──────────┬──────────────────────────┤ │ │ │ │ 侧边栏 │ 内容区域(路由页面) │ ← Layout │ 菜单 │ │ │ │ │ │ │ │ ├──────────┴──────────────────────────┤ │ Footer │ └─────────────────────────────────────┘

1.2 骨架必备元素

元素用途技术实现
侧边栏菜单页面导航根据路由配置生成
顶部栏面包屑、用户信息、退出独立组件
内容区路由页面渲染<router-view>/<Outlet>
路由系统URL 到页面的映射Vue Router / React Router
路由守卫登录拦截、权限校验beforeEach / loader
Axios 封装统一请求处理拦截器

第二章:Vue Router / React Router

2.1 Vue Router 4

// router/index.tsimport{createRouter,createWebHistory}from'vue-router'constroutes=[{path:'/login',component:()=>import('../views/Login.vue'),meta:{public:true}// 公开页面,不需要登录},{path:'/',component:()=>import('../layouts/MainLayout.vue'),redirect:'/dashboard',children:[{path:'dashboard',component:()=>import('../views/Dashboard.vue'),meta:{title:'首页',icon:'HomeFilled'}},{path:'users',component:()=>import('../views/UserList.vue'),meta:{title:'用户管理',icon:'UserFilled'}},{path:'users/:id/edit',component:()=>import('../views/UserEdit.vue'),meta:{title:'编辑用户',hidden:true}// 不在菜单显示}]},{path:'/:pathMatch(.*)*',redirect:'/404'}]constrouter=createRouter({history:createWebHistory(),routes})exportdefaultrouter

2.2 React Router 6

// router/index.tsx import { createBrowserRouter, RouterProvider } from 'react-router-dom' const router = createBrowserRouter([ { path: '/login', element: <Login />, meta: { public: true } }, { path: '/', element: <MainLayout />, children: [ { path: '', element: <Navigate to="/dashboard" /> }, { path: 'dashboard', element: <Dashboard />, meta: { title: '首页', icon: 'HomeOutlined' } }, { path: 'users', element: <UserList />, meta: { title: '用户管理', icon: 'UserOutlined' } } ] }, { path: '*', element: <Navigate to="/404" /> } ]) export default router

第三章:登录页开发

3.1 Vue 登录页

<template> <div class="login-page"> <el-card class="login-card"> <h2>管理后台</h2> <el-form ref="formRef" :model="form" :rules="rules" @keyup.enter="handleLogin" > <el-form-item prop="username"> <el-input v-model="form.username" placeholder="用户名" prefix-icon="User" /> </el-form-item> <el-form-item prop="password"> <el-input v-model="form.password" type="password" placeholder="密码" prefix-icon="Lock" show-password /> </el-form-item> <el-form-item> <el-button type="primary" :loading="loading" style="width: 100%" @click="handleLogin" > 登录 </el-button> </el-form-item> </el-form> </el-card> </div> </template> <script setup lang="ts"> import { ref, reactive } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import { useUserStore } from '@/stores/user' import request from '@/utils/request' const router = useRouter() const userStore = useUserStore() const loading = ref(false) const formRef = ref() const form = reactive({ username: '', password: '' }) const rules = { username: [{ required: true, message: '请输入用户名' }], password: [{ required: true, message: '请输入密码' }] } const handleLogin = async () => { await formRef.value?.validate() loading.value = true try { const { data } = await request.post('/api/login', form) userStore.setToken(data.token) userStore.setUserInfo(data.userInfo) ElMessage.success('登录成功') router.push('/') } catch (error) { ElMessage.error('登录失败') } finally { loading.value = false } } </script> <style scoped> .login-page { height: 100vh; display: flex; justify-content: center; align-items: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .login-card { width: 360px; } </style>

第四章:路由守卫与权限控制

4.1 Vue 路由守卫

// router/index.tsimport{useUserStore}from'@/stores/user'// 路由守卫(类似后端拦截器)router.beforeEach((to,from,next)=>{constuserStore=useUserStore()consttoken=userStore.token// 不需要登录的页面直接放行if(to.meta?.public){next()return}// 未登录跳转登录页if(!token){next('/login')return}// 已登录但不能访问该页面(权限校验)constrequiredRoles=to.meta?.rolesasstring[]if(requiredRoles&&!requiredRoles.includes(userStore.userInfo?.role)){next('/403')return}next()})

4.2 React 路由守卫

// 使用 Outlet + 条件渲染 import { Navigate, Outlet } from 'react-router-dom' import { useUserStore } from '@/stores/userStore' function AuthGuard() { const { isLoggedIn } = useUserStore() return isLoggedIn ? <Outlet /> : <Navigate to="/login" replace /> } function PermissionGuard({ roles }: { roles: string[] }) { const { userInfo } = useUserStore() const hasPermission = roles.includes(userInfo?.role) return hasPermission ? <Outlet /> : <Navigate to="/403" replace /> } // 路由配置 { path: '/', element: <AuthGuard />, // 先检查登录 children: [ { path: 'admin', element: <PermissionGuard roles={['admin']} />, // 再检查权限 children: [ { path: '', element: <AdminPage /> } ] } ] }

第五章:布局组件——侧边栏 + 顶部栏

5.1 Vue 布局组件

<template> <el-container class="layout"> <!-- 侧边栏 --> <el-aside width="200px" class="sidebar"> <div class="logo">管理后台</div> <el-menu :default-active="activeMenu" router background-color="#001529" text-color="#fff" active-text-color="#409EFF" > <el-menu-item index="/dashboard"> <el-icon><HomeFilled /></el-icon> <span>首页</span> </el-menu-item> <el-menu-item index="/users"> <el-icon><UserFilled /></el-icon> <span>用户管理</span> </el-menu-item> </el-menu> </el-aside> <el-container> <!-- 顶部栏 --> <el-header class="header"> <breadcrumb /> <div class="user-info"> <span>{{ userStore.userName }}</span> <el-button type="text" @click="handleLogout">退出</el-button> </div> </el-header> <!-- 内容区 --> <el-main class="main"> <router-view /> </el-main> </el-container> </el-container> </template> <script setup> import { computed } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useUserStore } from '@/stores/user' const route = useRoute() const router = useRouter() const userStore = useUserStore() const activeMenu = computed(() => route.path) const handleLogout = () => { userStore.logout() router.push('/login') } </script>

5.2 动态菜单

// 根据路由配置生成菜单constmenuRoutes=router.getRoutes().filter(r=>r.meta?.title&&!r.meta?.hidden).map(r=>({path:r.path,title:r.meta.title,icon:r.meta.icon}))

第六章:Axios 封装实战

6.1 请求封装

// utils/request.tsimportaxiosfrom'axios'import{ElMessage}from'element-plus'import{useUserStore}from'@/stores/user'importrouterfrom'@/router'constrequest=axios.create({baseURL:import.meta.env.VITE_API_BASE_URL||'/api',timeout:10000})// 请求拦截器request.interceptors.request.use((config)=>{consttoken=useUserStore().tokenif(token){config.headers.Authorization=`Bearer${token}`}returnconfig},(error)=>Promise.reject(error))// 响应拦截器request.interceptors.response.use((response)=>{const{code,data,message}=response.dataif(code===200){returndata}ElMessage.error(message||'请求失败')returnPromise.reject(newError(message))},(error)=>{if(error.response?.status===401){useUserStore().logout()router.push('/login')ElMessage.error('登录已过期')}else{ElMessage.error(error.response?.data?.message||'网络错误')}returnPromise.reject(error)})exportdefaultrequest

6.2 React 版本

// utils/request.tsimportaxiosfrom'axios'import{message}from'antd'import{useUserStore}from'@/stores/userStore'constrequest=axios.create({baseURL:import.meta.env.VITE_API_BASE_URL,timeout:10000})request.interceptors.request.use((config)=>{consttoken=useUserStore.getState().tokenif(token){config.headers.Authorization=`Bearer${token}`}returnconfig})request.interceptors.response.use((response)=>{const{code,data,message:msg}=response.dataif(code===200)returndata message.error(msg||'请求失败')returnPromise.reject(newError(msg))},(error)=>{if(error.response?.status===401){useUserStore.getState().logout()window.location.href='/login'message.error('登录已过期')}else{message.error(error.response?.data?.message||'网络错误')}returnPromise.reject(error)})exportdefaultrequest

第七章:实战

7.1 必做实战

实战 1:搭建完整的管理后台骨架

创建一个包含以下功能的骨架项目:

  1. 登录页(用户名 + 密码)
  2. 路由系统(首页、用户管理、404)
  3. 路由守卫(未登录拦截)
  4. 布局组件(侧边栏 + 顶部栏)
  5. Axios 封装(自动加 Token + 401 处理)

实战 2:添加权限控制

在骨架基础上:

  1. 增加角色字段(admin / user)
  2. 某些菜单只有 admin 能看到
  3. 某些操作按钮按角色显示/隐藏

7.2 FAQ

Q:路由守卫和后端拦截器有什么区别?

路由守卫是前端的"客户端校验",可以被绕过(直接访问 URL)。后端拦截器才是真正的安全防线。

Q:Token 过期了怎么处理?

在 Axios 响应拦截器中捕获 401,清除本地 Token,跳转到登录页。不需要刷新 Token 的复杂逻辑(简单的管理后台)。


下一期预告:对接真实后端接口 —— 打通前后端联调全流程

http://www.zskr.cn/news/1467830.html

相关文章:

  • 终极XPath定位神器:3分钟掌握xpath-helper-plus完整使用指南
  • 2026 义乌厨卫楼顶地下室漏水测评,吉修匠五星高分稳居榜首 - 吉修匠
  • 混合检索方案:融合传统倒排索引,与语义向量以提升 Milvus 分区分片精准度
  • 半导体分销商如何以技术驱动创造需求:科汇集团模式深度解析
  • 2026年LED路灯哪家好?从光源技术到工程落地的选型逻辑 - 深度智识库
  • Jetpack Compose拖拽排序实战指南:Reorderable库深度解析与高效应用
  • 【计算机毕业设计案例】基于微信小程序的图像识别智能垃圾分类系统django基于图像识别的智能垃圾分类系统设计与实现(程序+文档+讲解+定制)
  • 2W+程序员收藏!LikeShop与CRMEB选型对比,2026最新版全解析
  • 别再只盯着Webshell了:CVE-2016-3088漏洞的三种高阶利用思路与防御绕过思考
  • Floci支持的AWS服务清单:50+服务的完整兼容性指南
  • 抖音评论批量采集终极指南:三步获取完整用户反馈数据
  • 全托管商用直饮水怎么买,好评较多套餐放心选购 - 17329971652
  • 5个理由告诉你为什么Gaggiuino能彻底改变你的咖啡制作体验
  • 如何用AsrTools实现批量音频转文字:智能语音识别工具全面解析
  • 深入解析TI DSP中断系统:IER与IFR寄存器原理与cregister关键字应用
  • 终极零代码知识图谱构建工具:3步将Excel表格转化为智能对话系统
  • LabWindows/CVI开发实战:性能调优、多线程与系统集成疑难解析
  • 用Python处理FY4A雷电数据(LMI):从netCDF文件读取到Cartopy地图可视化的保姆级教程
  • 告别Windows卡顿与繁琐配置:这款工具如何让你30分钟搞定系统优化?
  • 阳光房遮阳帘厂家常见问题解答(2026专家版) - 资讯纵览
  • 告别盲扫!深入理解PNG/BMP/GIF文件结构,手把手教你用010Editor模板破解CTF图片隐写
  • 工程与工业摄影测量笔记(超长完整版)
  • 3分钟掌握rcedit:Windows可执行文件资源编辑的终极指南
  • 从寻呼到高速下载:5G PDSCH的MCS与TBSize如何随场景‘智能’切换?
  • TensorFlow语音增强与去混响全流程代码包:含噪声模拟、TFRecords构建、ResNet-RCE训练、PESQ评估及波形重建
  • DDrawCompat完整教程:让Windows 11完美运行DirectX老游戏的终极方案
  • 北京汉堡品牌加盟哪家靠谱,无隐形收费透明签约安心投资开店 - 19120507004
  • Umi-OCR终极指南:3个简单技巧让你轻松掌握免费离线文字识别
  • Logisim-evolution:从虚拟仿真到物理实现的数字逻辑设计革命
  • 海岛海洋可再生能源多能互补发电系统储能装置的运行与控制策略【附仿真】