Skip to content

简单介绍与后台管理系统特点(Day1 1/2) ​

本页对应 day1,共 6 段视频,约 3 小时 5 分。

学习目标 ​

  • 能概括后台管理系统项目的典型特点,包括无注册仅登录、左右布局、增删改查业务、菜单权限与按钮权限,并能举例说明权限控制场景。
  • 能对比 Vue2 与 Vue3 后台管理模板的技术栈、入口文件、路由与状态管理差异,说明基于模板进行二次开发的流程。
  • 能阅读 Vue3 模板源码,定位并解释入口文件、路由配置、Pinia 仓库、全局组件注册、SVG 图标与 Element Plus 图标注册等关键代码。
  • 能按指定结构配置商品管理一级路由及品牌管理、平台属性管理、SPU、SKU 等二级路由,并正确设置 name、path、component、meta 中的 title、icon、hidden 等字段。
  • 能解释路由守卫中 token 判断、用户信息获取、白名单、进度条、页面标题设置及 token 过期处理流程,并说明其与登录鉴权的关系。
  • 能基于模板完成一次可验证的二次开发:新增商品管理模块路由并使其在菜单中正确显示,且通过命名路由和权限元信息验证配置结果。

知识点 ​

课程准备与面试题整理 ​

为什么需要:项目课不只是写业务,还要为毕业面试积累素材;临时背题来不及。
怎么想:把每天讲到的面试题、错误截图、复习点都放进固定文件夹,形成自己的知识库。
怎么做:介绍自己:贾成豪,哈尔滨,微信 jiachenghao0330,有问题可在微信群或课后联系。课上建笔记文件,如 笔记.md,并单独建“面试题总结”,以后遇到 Vue2、Vue3、JS 基础题都归档。
易错点:不要等到毕业前才刷题;让整理的面试题一定要当天或写项目后整理,否则越积越多。

后台管理系统的特点 ​

为什么需要:先看懂项目业务,再写代码,避免一上来陷进细节。
怎么想:后台管理系统通常服务于一家企业,核心是管理员工、角色、菜单、按钮权限和业务数据。
怎么做:结合已完成项目观察几个特点:

  1. 一般没有注册,只有登录。员工账号由老板/超级管理员创建,HR 把账号密码发给员工。
  2. 布局多为左右结构:左侧菜单,右侧内容区;前台项目常见上中下结构。
  3. 首页常有工作报表、数据可视化;复杂场景会做数据大屏。
  4. 业务大量是增删改查,如用户、角色、菜单、品牌、平台属性、SPU、SKU。
  5. 最重要的是权限:菜单权限决定能看到哪些左侧菜单,按钮权限决定能否新增、删除、修改等。
    易错点:不要把后台项目当电商前台写;权限不是只做菜单,还要做按钮级控制。

Vue2 与 Vue3 模板介绍 ​

为什么需要:工作中可能接手 Vue2 或 Vue3 后台项目,需要快速套模板二次开发。
怎么想:Vue2 可参考 vue-element-admin(作者花裤衩),技术栈是 Vue2 + Element UI + axios + vuex + vue-router;Vue3 用封装的模板,技术栈是 Vue3 + Element Plus + Pinia + vue-router + axios。
怎么做:克隆模板后先看 package.json,用淘宝镜像安装依赖,再按脚本运行。Vue2 模板若报缺少 core-js,按报错安装;Vue3 模板运行 npm run dev,可在 dev 后加 --open 自动打开浏览器。
易错点:不要拿 Vue2 模板写 Vue3 项目;依赖数量、运行脚本要以 package.json 为准;遇到红色报错先看底部提示,按提示补依赖。

源码阅读:入口文件与路由 ​

为什么需要:入职后常要接手已有项目,先读入口和路由才能理清业务。
怎么想:从 main.ts 看起:createApp 创建应用,use(router) 注册路由,use(pinia) 注册仓库,use(ElementPlus) 注册组件库;再读 router 和 store。
怎么做:商品管理模块配置为一级路由 /product,组件用 Layout;其下四个二级路由:品牌管理、平台属性、SPU、SKU。每个路由写 name、path、component、meta.title、meta.icon。一级路由要有标题和图标,二级路由也按需配置。
易错点:命名路由必须与一致,大小写和单词不能错,因为菜单权限会用到;meta 不写可能导致标题、图标不显示;路径和组件路径要对应。

路由守卫与 token 过期 ​

为什么需要:后台页面不能随便访问,必须判断登录和权限。
怎么想:permission.ts 中引入 router、Pinia 用户仓库、NProgress。全局前置守卫开始进度条,后置守卫结束进度条。有 token 访问 /login 时跳首页;无 token 时判断是否在白名单,白名单放行,否则跳登录并带 redirect。有 token 但没有用户信息时,发请求获取用户信息再放行;获取失败说明 token 过期或异常,清空用户数据并跳登录。
怎么做:调试时可在浏览器 Application 中删掉 token 或改坏 token,刷新观察是否重新登录;登录成功后再访问原目标页。
易错点:Pinia 数据默认非持久化,刷新会丢,所以每次刷新可能重新获取用户信息;token 过期不是“重新交学费”,而是清空本地用户数据、重新登录拿新 token;白名单、redirect、进度条结束位置都容易漏。

后台管理系统的四个典型特点 ​

后台管理系统通常是企业内部使用的系统,服务于老板、管理员和员工。它和前台商城项目最大的不同,是“人”和“权限”更复杂。本节先抓住四个特点:一般没有注册只有登录;项目结构多为左右布局;核心业务是增删改查;最重要的是用户访问权限,分为菜单权限和按钮权限。

为什么需要:后台系统不是面向陌生用户开放注册的,账号由公司统一分配;界面要长期高频操作,所以布局要稳定;业务数据要维护,所以离不开增删改查;不同岗位能看到的内容不同,所以必须有权限控制。

怎么想:先想“账号从哪来”,再想“页面怎么排”,再想“数据怎么管”,最后想“谁能看、谁能点”。

怎么做:学习时按这四个特点去读项目源码和配路由,不要一上来就陷进某个组件。

易错点:不要把前台商城的“注册+登录”照搬到后台;不要以为权限只是菜单,按钮级权限同样关键。

账号来源:没有注册,只有登录 ​

后台系统一般没有注册入口,只有登录。员工账号和密码由老板或超级管理员创建,HR 再通过邮件等方式发给员工。老板账号相当于超级管理员,可以管理员工、角色和权限。

为什么需要:如果开放注册,任何人都能进入企业后台,安全风险极高。

怎么想:把“注册”这件事前移到管理员端:管理员新增员工时,同时设置账号和密码。

怎么做:登录成功后保存 token;后续请求携带 token;管理员在用户管理里新增用户、分配角色。

易错点:不要把“新增员工”理解成“员工自己注册”;不要把超级管理员和普通员工混为一谈。

布局与业务:左右结构、增删改查 ​

后台项目常见左右结构:左侧是菜单,右侧是内容展示区;前台商城常见上中下结构,中间是路由出口。后台核心业务大量是增删改查,例如用户管理、角色管理、品牌管理、平台属性管理、SPU/SKU 管理等。

为什么需要:左侧菜单稳定,右侧内容切换,适合长期办公操作;企业数据需要持续维护。

怎么想:先看一级路由和 Layout 骨架,再看二级路由如何嵌到右侧内容区。

怎么做:用模板的 Layout 做外层骨架,把业务路由配成它的子路由;新增、删除、修改、查询分别对应弹窗、确认框、表单和表格。

易错点:一级路由组件通常用 Layout,不要误写成业务页面;路由层级和菜单展示要对应。

权限:菜单权限与按钮权限 ​

后台系统最重要的特点是用户访问权限。不同职位看到的左侧菜单不同,甚至同一个页面里的按钮也不同。例如超级管理员能看到“权限管理”“商品管理”,普通员工可能只能看首页。菜单权限控制“能不能进页面”,按钮权限控制“能不能点新增、删除、修改”。

为什么需要:公司岗位不同,职责不同,不能让人看到或操作越权内容。

怎么想:权限最终落到“当前用户有什么角色、角色有什么菜单和按钮”。

怎么做:管理员给用户分配角色;角色管理里维护岗位;权限管理里勾选菜单和按钮;前端路由和按钮按权限渲染。路由命名要稳定,因为做菜单权限时会用到命名路由。

易错点:路由 name 不要随意改,转写稿特别强调要和模板约定一致;不要只做菜单隐藏而忘了按钮权限。

项目模板:Vue2 与 Vue3 ​

笔记给了两个模板地址,学习时先克隆已完成的模板看业务,再用模板做二次开发。

Vue2 后台管理模板: https://gitee.com/panjiachen/vue-admin-template.git

Vue3 后台管理模板: https://gitee.com/jch1011/vue3_admin_project.git

Vue3 模板讲解视频: https://www.bilibili.com/video/BV1Xh411V7b5/?spm_id_from=333.337.search-card.all.click

为什么需要:企业项目常基于成熟模板二次开发,快速获得布局、路由、权限、请求封装等基础能力。

怎么想:先跑起来看效果,再读 package.json 看技术栈,最后从入口文件读源码。

怎么做:安装依赖并运行:

bash
npm install
npm run dev

Vue3 模板可在 dev 后加 --open 自动打开浏览器:

bash
npm run dev --open

Vue2 模板运行时如果报缺少 core-js,按报错安装后再运行。转写稿中 Vue2 模板跑在 9528 端口,Vue3 模板跑在 3001 端口。

易错点:不要用 npm 安装时忽略报错;不要拿 Vue2 模板的依赖数量去判断 Vue3 模板;运行命令看 package.json 的 scripts,不一定是 serve。

源码阅读与路由配置 ​

读模板先从入口文件开始:createApp 创建应用,注册 Pinia 大仓库,注册 Element Plus、SVG 图标等全局组件。路由里通常有登录、404、Layout、首页和任意匹配等一级路由;Layout 下再挂业务二级路由。

配商品管理时,一级路由 product 使用 Layout,下面挂品牌管理、平台属性、SPU、SKU 四个二级路由。一级路由配 meta.title 和 meta.icon 后菜单才会显示;如果一级下只有一个二级,模板通常不显示折叠效果,至少两个二级才容易看到折叠。

为什么需要:模板已经封装好 Layout 骨架,配好路由菜单就能出现。

怎么想:先确定一级/二级层级,再确定组件路径和命名路由。

怎么做:一级用 Layout,二级用业务组件;每个一级、二级都写 name;菜单标题和图标写在 meta 里。

易错点:路径拼写错误不会立刻报错,可能查很久;meta 不写可能导致菜单不展示或报错。

路由守卫与 token 处理 ​

路由守卫控制“这个路由页面能不能访问”。模板里常见:有 token 去登录页会重定向首页;没有 token 且访问的不是白名单页面,跳登录并带 redirect;有 token 但没有用户信息,就发请求获取用户信息,成功后再放行;获取失败通常说明 token 过期,要清空用户仓库和本地存储,跳回登录页。进度条用 NProgress,前置守卫开始,后置守卫结束;页面标题根据路由 meta.title 和项目标题拼接。

为什么需要:不登录不能进后台;刷新后 Pinia 数据会丢,需要重新获取用户信息;token 会过期。

怎么想:先判断 token,再判断目标路由,再判断用户信息,最后放行或跳登录。

怎么做:在全局前置守卫里写判断;请求用户信息成功后 next();失败清空并跳登录;后置守卫关闭进度条。

易错点:token 过期不是“重新注册”,而是重新登录拿新 token;刷新后 Pinia 非持久化,用户信息会丢,必须重新请求。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

代码 1:入口文件 src/main.ts + 全局 SVG 图标组件(对应“模版源码阅读 2/3”) ​

ts
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import SvgIcon from '@/components/SvgIcon/index.vue'
import '@/styles/index.scss'
import './permission' // 路由鉴权(守卫)必须引入才会生效

const app = createApp(App) // Vue3 创建应用实例,Vue2 是 new Vue()

// 1. 把 Element Plus 的全部图标注册成全局组件,统一加 ELE- 前缀
const icons = ElementPlusIconsVue // 这是一个对象:key=图标名,value=组件
Object.keys(icons).forEach((key) => {
  app.component(`ELE-${key}`, icons[key])
})

// 2. 把阿里 SVG 图标注册成一个全局组件 svg-icon
app.component('svg-icon', SvgIcon)

app.use(router)
app.use(pinia)
app.use(ElementPlus)
app.mount('#app')
vue
<!-- src/components/SvgIcon/index.vue -->
<template>
  <svg :style="{ width: size + 'px', height: size + 'px', color }">
    <use :xlink:href="name" :fill="color" />
  </svg>
</template>

<script setup lang="ts">
defineProps({
  name: { type: String, required: true },
  size: { type: [Number, String], default: 14 }, // 默认 14
  color: { type: String, default: '#fff' }
})
</script>

关键行与易错点

  • createApp(App):Vue3 才有,Vue2 是 new Vue({ render: h => h(App) })。
  • Object.keys(icons).forEach(...):icons 是对象不是数组,直接 forEach 会报错,必须先 Object.keys()。
  • 组件名前缀 ELE- 必须和模板里写的完全一致(<ELE-Plus />、<ELE-Edit />),大小写错了就是“组件未注册”。
  • svg-icon 注册成全局组件后,任何地方直接 <svg-icon name="#icon-xxx" :size="20" color="red" />,不需要再 import。
  • import './permission' 只是执行文件,没有导出,但不引入守卫就不会跑。

代码 2:商品管理模块路由配置 src/router/index.ts(对应“商品管理模块路由的配置”) ​

ts
// src/router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router'

export const constantRoutes = [
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  },
  {
    path: '/',
    component: () => import('@/layout/index.vue'), // 一级路由用 layout 骨架
    children: [
      {
        path: '',
        name: 'Home',
        component: () => import('@/views/home/index.vue'),
        meta: { title: '首页', icon: 'ELE-HomeFilled' }
      }
    ]
  },
  {
    // 商品管理:一级路由,组件仍然是 layout
    path: '/product',
    component: () => import('@/layout/index.vue'),
    meta: { title: '商品管理', icon: 'ELE-Goods' },
    children: [
      {
        path: 'trademark', // 二级不要加 /
        name: 'Trademark',
        component: () => import('@/views/product/trademark/index.vue'),
        meta: { title: '品牌管理', icon: 'ELE-PriceTag' }
      },
      {
        path: 'attr',
        name: 'Attr',
        component: () => import('@/views/product/attr/index.vue'),
        meta: { title: '平台属性管理', icon: 'ELE-Setting' }
      },
      {
        path: 'spu',
        name: 'Spu',
        component: () => import('@/views/product/spu/index.vue'),
        meta: { title: 'SPU管理', icon: 'ELE-Grid' }
      },
      {
        path: 'sku',
        name: 'Sku',
        component: () => import('@/views/product/sku/index.vue'),
        meta: { title: 'SKU管理', icon: 'ELE-List' }
      }
    ]
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/404/index.vue'),
    meta: { hidden: true }
  },
  {
    path: '/:pathMatch(.*)*', // 任意路由,必须放最后
    redirect: '/404',
    meta: { hidden: true }
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes,
  scrollBehavior: () => ({ left: 0, top: 0 })
})

export default router

关键行与易错点

  • 一级路由 path: '/product' 带 /,二级 path: 'trademark' 不带 /,写错会变成 /trademark 而不是 /product/trademark。
  • 一级路由的 component 用的是 layout/index.vue,不是具体页面——菜单和内容区都由 layout 撑起来,所以“只配路由,窗口就出来了”。
  • 每个一级、二级都必须写 name,且首字母大写、和讲师给的单词一模一样,因为后面做菜单权限/按钮权限要靠 name 匹配。
  • 一级下至少要有 2 个二级才会出现折叠效果;只有一个二级时模板会直接不折叠。
  • meta 不写就会出 undefined(“假报错”那种),title 决定菜单文字,icon 决定菜单小图标(写 Element Plus 图标名,如 ELE-xxx),hidden 决定菜单是否隐藏。
  • 任意路由 /:pathMatch(.*)* 必须放在最后,否则会把正常路由也吞掉。

代码 3:路由守卫 src/permission.ts(对应“模版源码阅读 3”) ​

ts
// src/permission.ts
import router from '@/router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { ElMessage } from 'element-plus'
import { useUserInfoStore } from '@/store/modules/user'
import { getPageTitle } from '@/utils/getPageTitle'
import setting from '@/setting'

NProgress.configure({ showSpinner: false }) // 关掉右上角转圈小球

const whiteList = ['/login'] // 白名单:不登录也能访问

router.beforeEach(async (to, from, next) => {
  NProgress.start() // 前置守卫:进度条开始
  document.title = getPageTitle(to.meta.title as string)

  const userInfoStore = useUserInfoStore()
  const token = userInfoStore.token

  if (token) {
    // 已登录
    if (to.path === '/login') {
      next('/') // 已登录还想去登录页 → 回首页
    } else {
      if (userInfoStore.name) {
        next() // 有用户信息,直接放行
      } else {
        try {
          await userInfoStore.getUserInfo() // 刷新后 pinia 没了,重新拉用户信息
          next()
        } catch (error) {
          // token 过期或被人手改:清空用户相关数据,回登录页并记住原地址
          userInfoStore.reset()
          ElMessage.error('登录已过期,请重新登录')
          next('/login?redirect=' + to.path)
        }
      }
    }
  } else {
    // 未登录
    if (whiteList.indexOf(to.path) !== -1) {
      next() // 白名单放行
    } else {
      next('/login?redirect=' + to.path) // 其余全部踢回登录页
    }
  }
})

router.afterEach(() => {
  NProgress.done() // 后置守卫:进度条结束
})
ts
// src/utils/getPageTitle.ts
import setting from '@/setting'

export const getPageTitle = (title?: string) => {
  if (title) {
    return `${title} - ${setting.title}` // 有 meta.title 就拼接
  }
  return setting.title // 没有就用项目默认名
}

关键行与易错点

  • beforeEach 里 NProgress.start()、afterEach 里 NProgress.done(),必须成对,漏了 done() 进度条会一直卡着。
  • useUserInfoStore() 建议写在守卫函数内部,写在文件顶层容易因为 pinia 还没 app.use 而报错。
  • whiteList.indexOf(to.path) !== -1:indexOf 找不到返回 -1,!== -1 才表示“在白名单里”,符号写反就全放行或全拦截。
  • 判断顺序是“先看 token,再看 name”:有 token 没 name 说明是刷新页面(pinia 不持久化,一刷新就没了),必须重新 getUserInfo() 再 next()。
  • catch 里的 reset() 就是清空仓库和本地存储里用户相关的数据,然后 next('/login?redirect=' + to.path),登录成功后可以回到原来想去的页面。
  • document.title = getPageTitle(to.meta.title):to 是要跳过去的路由对象,meta.title 就是上面路由里配的标题,所以路由里的 meta 千万不能漏。

常见错误 ​

  1. 现象:商品管理路由配完后左侧菜单不显示或没有折叠效果。→ 原因:一级/二级层级写错,或一级下只配了一个二级。→ 修法:按模板核对一级用 Layout、二级挂到对应目录,折叠菜单至少配两个二级。
  2. 现象:菜单标题、图标缺失,控制台出现异常。→ 原因:路由 meta 未写全或字段拼错。→ 修法:补齐 meta.title、meta.icon,逐项核对 hidden、title、icon 字段。
  3. 现象:后续做菜单权限时匹配不到路由。→ 原因:路由 name 大小写、拼写与模板约定不一致。→ 修法:严格按模板命名,先对照已有路由再新增,单词逐个核对。
  4. 现象:npm run dev 启动模板报缺依赖。→ 原因:依赖未安装完整,如 core-js 等缺失。→ 修法:按终端报错补装依赖,统一使用一种包管理器并重新安装。
  5. 现象:刷新页面后用户信息丢失、反复请求或直接跳登录。→ 原因:Pinia 数据非持久化,守卫中未按 token 重新获取用户信息。→ 修法:有 token 时重新拉取用户信息再放行,异常时清空并跳登录。
  6. 现象:token 过期后只跳登录,登录回来不能回到原页面。→ 原因:未清空用户相关数据,或跳转未带 redirect。→ 修法:清空 store 与 localStorage,跳 login 时携带 redirect,登录后回原目标页。
  7. 现象:Element Plus 图标或 SVG 图标在组件中用不了。→ 原因:未按模板全局注册,或组件传参名写错。→ 修法:按模板注册全局图标组件,使用正确的 name/size/color 传参。
  8. 现象:模板启动后浏览器不自动打开。→ 原因:dev 脚本未加 --open。→ 修法:在 package.json 的 dev 命令后加 --open,再重启。

练习 ​

  1. 按模板新增“商品管理”一级路由及品牌管理、平台属性管理、SPU、SKU 四个二级路由,要求菜单可折叠、命名与模板一致。
  2. 阅读 router 与 permission 相关文件,画出“未登录 / 有 token 但无用户信息 / token 失效”三条访问分支。
  3. 将 Element Plus 图标和 SVG 图标注册为全局组件,并在菜单或按钮中使用,验证 name、size、color 生效。
  4. 模拟 npm run dev 报缺 core-js,写出定位报错、补装依赖、重新启动的排查步骤。
  5. 对比 Pinia 与 Vuex 的核心概念及持久化缺陷,说明本项目 token 存在哪里、刷新后如何恢复用户信息。

原视频对照 ​

#视频时长
1项目介绍26:10
2vue2与vue3模版的介绍27:46
3模版的源码阅读146:26
4商品管理模块路由的配置20:03
5模版的源码阅读222:42
6模版源码阅读342:19

小结 ​

  1. 后台管理系统核心是登录与权限,权限重点看菜单权限和按钮权限。
  2. 模板二次开发先读入口文件、router、store、permission,再动业务代码。
  3. 路由配置直接决定菜单展示,name 与 meta 必须规范且与模板一致。
  4. 路由守卫围绕 token 和用户信息展开,刷新后要能重新获取用户信息。
  5. token 过期处理要清空仓库与本地存储,并携带 redirect 回登录页。
  6. Element Plus 与 SVG 图标按模板全局注册,能减少重复引入和传参错误。
  7. 依赖缺失先看终端报错再补装,启动命令以 package.json 为准。
  8. 面试题要及时归档,重点整理 H5 存储、Pinia/Vuex、路由守卫和 token 处理。