当前位置: 首页 > 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.jsqmd.com/news/956203/

相关文章:

  • Redcar插件开发实战:如何创建自定义扩展
  • 2026 义乌厨卫楼顶地下室漏水测评,吉修匠五星高分稳居榜首 - 吉修匠
  • 半导体分销商如何以技术驱动创造需求:科汇集团模式深度解析
  • 从原理到实战:拆解Fluxion钓鱼WiFi的每一步,理解802.11协议与Deauth攻击(Kali Linux环境)
  • 终极XPath定位神器:3分钟掌握xpath-helper-plus完整使用指南
  • 保姆级教程:用SolidWorks 2023插件为六轴机械臂一键生成URDF文件(附Innfos模型)
  • 【Sora 2比特率优化实战白皮书】:20年视频编码专家首度公开4大降码率不损画质的核心公式
  • 保姆级教程:用VMware Workstation Pro桥接模式,5分钟搞定三台CentOS7虚拟机上网
  • 混合检索方案:融合传统倒排索引,与语义向量以提升 Milvus 分区分片精准度
  • Zotero Style插件架构解析:从版本兼容性问题到现代化扩展开发实践
  • 从技术天才到商业博弈:李一男与华为的恩怨启示录
  • 单片机与嵌入式系统:工程师职业路径选择与核心技术深度剖析
  • 2026年LED路灯哪家好?从光源技术到工程落地的选型逻辑 - 深度智识库
  • Craftable完全指南:如何用Laravel快速构建专业级管理面板
  • 2026邢台市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐.txt
  • Cursor Free VIP:终极解决方案,让AI编程助手永久免费使用
  • 【计算机毕业设计案例】基于微信小程序的图像识别智能垃圾分类系统django基于图像识别的智能垃圾分类系统设计与实现(程序+文档+讲解+定制)
  • Jetpack Compose拖拽排序实战指南:Reorderable库深度解析与高效应用
  • 2026年企业邮箱系统哪家好?企业邮箱系统选型全指南 - U-Mail邮件系统
  • 信号测量核心:带宽与上升时间公式BW=0.35/Tr的工程应用
  • 一站式直饮水价格:平台整理报价干货亲测实用 - 19120507004
  • FunClip架构深度解析:大语言模型驱动的智能视频剪辑技术方案
  • Floci支持的AWS服务清单:50+服务的完整兼容性指南
  • 招聘网站优选盘点,高性价比求职就业平台推荐 - 讲清楚了
  • 别再只盯着Webshell了:CVE-2016-3088漏洞的三种高阶利用思路与防御绕过思考
  • 三分钟美化foobar2000!foobox-cn让你拥有专业级音乐播放器界面
  • 2W+程序员收藏!LikeShop与CRMEB选型对比,2026最新版全解析
  • 3步解锁苹果平方字体:解决跨平台中文显示难题的完整方案
  • 5分钟掌握PDF补丁丁:无需安装的强大PDF编辑工具使用全攻略
  • 如何用OpenCore Legacy Patcher让旧Mac重获新生:终极硬件兼容性修复指南