国庆节10月1日
--


第 16 章 Tlias 前端实战的开头两节——先复习前后端分离的开发模式与五步流程,再把资料里的基础工程跑起来,用 Container 布局容器搭出顶栏加侧边栏加主区域的骨架,最后用 Vue Router 的嵌套路由把左侧菜单和八个页面组件连起来

97 篇把 ElementPlus 的常用组件过了一遍,练手时都是一个页面;从这一篇开始,PPT 进入第 16 章——用 Vue3 + ElementPlus 把 Tlias 这个项目的前端真正做出来。第一个要解决的问题不是”某个组件怎么写”,而是”一个多页面的后台系统,骨架是怎么搭起来的”。
本章 PPT 一共 39 页(另外三节还有后续章节),我们按目录分成三篇笔记:
| PPT 页 | 内容 | 本篇对应小节 |
|---|---|---|
| 1~9 | 案例全景、开发模式(前后端分离) | Tlias 前端实战要做什么、前后端分离开发 |
| 10~14 | 整体布局:准备工作、页面布局(左侧菜单) | 准备工作、页面布局 |
| 15~22 | 整体布局:动态菜单(Vue Router、嵌套路由) | 动态菜单、案例路由实现 |
| 23~31 | 部门管理:列表查询 | 99 篇 |
| 32~38 | 部门管理:新增、修改、删除 | 100 篇(后续) |
PPT 第 1~2 页是这一章的封面(Web开发(AI) → Web前端实战(案例) · Tlias智能学习辅助系统),从第 3 页起正式进入案例。PPT 第 5、6、10、11、15 页反复出现的那几张目录卡片(开发模式 / 整体布局 / 部门管理 / 员工管理 / 登录退出)就是本章的路线图:每讲完一块,卡片就点亮一张(第 6 页”开发模式 01”、第 11 页”整体布局 02”这类带序号的页就是分节页)。本篇负责前两张——开发模式和整体布局。
PPT 第 4 页把整个案例拆成了七块:
| 模块 | 说明 |
|---|---|
| 开发模式 | 前后端分离(复习 + 本章怎么落地) |
| 整体布局 | 顶栏 + 左侧菜单 + 右侧主区域这套”壳” |
| 部门管理 | 列表查询、新增、修改、删除(本章的重头戏) |
| 员工管理 | 同样的套路,再做一遍 |
| 登录/退出 | 登录页 + 退出登录 |
| 班级管理 | (自己实现) |
| 学员管理 | (自己实现) |
前五块 PPT 会带着做一遍,最后两块标着”自己实现”——学完本章就是照着前面做过的套路把它补全,这是本章留的作业。
PPT 第 3~4 页贴的系统界面就是”做出来之后长什么样”:

注意它的布局特点:顶栏横贯整页,下面左窄右宽——这不是靠”手写 CSS 定位”拼出来的,而是 ElementPlus 的布局容器给的(PPT 第 13 页讲)。这个界面在 12 篇见过一次,当时是用 HTML + CSS 手写的;现在换成”组件 + 路由”的工程做法,同一个界面就”活”起来了(能点菜单切页面)。
PPT 第 7 页的标题就是答案,配了一张三方协作的图:
当前最为主流的开发模式:前后端分离
图里有三方角色和两组箭头:


两张图中间夹着接口文档,并且两侧各写着”阅读-开发”:
1 原型 + 需求 接口文档2 │ │3 ▼ ▼4 ┌───────────┐ 请求 ┌───────────┐5 │ 前端开发 │ ───────────▶ │ 后端开发 │6 │(页面/交互)│ ◀─────────── │(接口/数据)│7 └───────────┘ 响应 └───────────┘8 ▲ ▲9 └──────── 接口文档 ────────────┘10 (两边都"阅读-开发")两边的共同依据就是接口文档:前端照着接口文档发请求、按文档里规定的结构解析响应;后端照着接口文档定义路径、参数、返回格式。接口文档一改,两边都要跟着改——所以它才是”合同”。
PPT 第 8 页在上一张图的基础上,把时间线补了上去,就是前后端分离下的开发流程:
1需求分析 ──▶ 接口定义 ──▶ 前后端并行开发 ──▶ 测试 ──▶ 前后端联调测试| 阶段 | 谁在做 | 产出 |
|---|---|---|
| 需求分析 | 产品/开发一起 | 原型 + 需求说明 |
| 接口定义 | 前后端一起定 | 接口文档(路径、参数、响应结构) |
| 前后端并行开发 | 前端、后端同时做 | 前端照着文档写页面、后端照着文档写接口(前端这时用 Mock 假数据就能开工,99 篇细讲) |
| 测试 | 各自测 | 前端测交互、后端测接口 |
| 前后端联调测试 | 前后端一起 | 把真前端接到真后端上跑通(59 篇干过这件事) |
这条链子里最关键的是中间那步”接口定义”——它一定下来,前后端就可以同时开工,不用互相等:后端的接口还没写完,前端也能照文档先把页面和数据流做出来(99 篇会讲这时候怎么用假数据顶一阵)。
PPT 第 9 页是这一节的问答页,两道题必须能立刻答出来:
| 问题 | 答案 |
|---|---|
| 什么是前后端分离开发模式? | 前后端开发人员独立开发,独立部署 |
| 需求的开发流程? | 需求分析 → 接口定义 → 前后端并行开发 → 测试 → 前后端联调测试 |
“独立开发、独立部署”这八个字里其实藏着两个”独立”:开发时两边各写各的工程(前端跑自己的 dev server、后端跑自己的服务),部署时也是两套——后端把 jar 包扔到服务器上,前端把打好的静态文件托管出去(第 19 章项目部署会见到)。所以本章的前端工程从头到尾不需要把后端代码放进自己的目录,只需要一个”地址”就能拿到数据。
PPT 第 12 页只有两句话,这一节的准备工作就这两步:
- 导入资料中准备的基础工程到 VsCode
- 启动前端项目,访问该项目
基础工程就是 资料\03. 基础工程\vue-tlias-management.zip,解压后用 VSCode 打开(04 篇装过的 VSCode + Volar 插件这时候就派上用场了)。里面依赖已经写好,解压后装依赖、起服务:
1npm install # 装依赖(本机实测用了国内镜像 npm install --registry=https://registry.npmmirror.com 快一些)2npm run dev # 启动开发服务器本机实测启动后命令行输出:
1 VITE v3.2.10 ready in 469 ms2
3 ➜ Local: http://localhost:5173/浏览器打开 http://localhost:5173,页面上只有顶栏是齐的:

底下这两句占位文字在工程里的位置一找就着:src/views/layout/index.vue 的模板里,el-aside 里写着”左侧菜单栏”、el-main 里写着”右侧核心展示区域”(下面会看到原样代码)。
本机实测的一个真实小坑(排查”我看到的页面怎么不对”时先看这里):Vite 默认端口是 5173,但如果 5173 已经被别的工程占着,它会自动换一个端口(本机第一次启动时命令行出现过 Port 5173 is in use, trying another one...,然后跑到了 5174)。所以打开页面前先看命令行打印的 Local: 地址是哪个端口,别守着 5173 刷半天旧的页面。
工程里已经有的东西(包在基础工程里就装好了,package.json 里能看到):vue、vue-router、element-plus、@element-plus/icons-vue、axios。其中 vue-router 就是这一篇后半段的主角。
PPT 第 13 页给出的就是 ElementPlus 的布局容器,五个组件分工明确:
| 组件 | PPT 原文 | 作用 |
|---|---|---|
<el-container> | 外层容器 | 包住整页(或包住左 + 右那一行) |
<el-header> | 顶栏容器 | 顶部一条(Tlias 那条渐变标题栏) |
<el-aside> | 侧边栏容器 | 左侧竖条(放菜单) |
<el-main> | 主要区域容器 | 右侧主内容(页面切换就换这里) |
<el-footer> | 底栏容器 | 底部一条(Tlias 案例没用) |
PPT 第 13 页把五个标签画在了”一个页面”上,同时配了实际页面的样子:

01. 页面原型里截的)——顶栏(黑马 tlias 标题 + 修改密码/退出登录)、左侧菜单(首页 / 班级学员管理 / 系统信息管理 / 数据统计管理,每组能展开)、右侧主区域(教学后台首页图)。“整体布局”指的就是这副骨架;左侧菜单栏那个位置原来是几个字,现在要换成真正的菜单el-container 有个贴心规则:里面直接放的是 el-header / el-footer 时,它自动竖着排;放的是 el-aside / el-main 时,自动横着排。案例里正是用这条规则——外层 el-container 套一个 el-header(竖排:上顶栏、下主体),主体再套一层 el-container(横排:左菜单、右主区域)。
src/views/layout/index.vue 就是这套骨架的落点(课程工程的真实代码,加中文注释):
1<template>2 <div class="common-layout">3 <el-container> <!-- 外层容器:包住整页 -->4 <!-- 顶栏区域 -->5 <el-header class="header">6 <span class="title">Tlias智能学习辅助系统</span>7 <span class="right_tool">8 <a href="">9 <el-icon><EditPen /></el-icon> 修改密码 | 10 </a>11 <a href="">12 <el-icon><SwitchButton /></el-icon> 退出登录13 </a>14 </span>15 </el-header>16
17 <el-container> <!-- 内层容器:横向排"侧边栏 + 主区域" -->18 <!-- 左侧菜单 -->19 <el-aside width="200px" class="aside">20 <!-- 左侧菜单栏(下一节往里放 el-menu) -->21 </el-aside>22
23 <el-main>24 <!-- 右侧核心展示区域(下一节往里放 router-view) -->25 </el-main>26 </el-container>27
28 </el-container>29 </div>30</template>配的样式(同样是课程代码,都在 <style scoped> 里):
1.header {2 background-image: linear-gradient(to right, #00547d, #007fa4, #00aaa0, #00d072, #a8eb12);3}4
5.title {6 color: white;7 font-size: 40px;8 font-family: 楷体;9 line-height: 60px;10 font-weight: bolder;11}12
13.right_tool {14 float: right; /* 右侧那一组工具链接靠右 */15 line-height: 60px;16}17
18a {19 color: white;20 text-decoration: none; /* 去掉下划线 */21}22
23.aside {24 width: 220px;25 border-right: 1px solid #ccc; /* 菜单和主区域之间那条竖线 */26 height: 730px;27}顶栏那条渐变就是 linear-gradient(06 篇讲的背景色 + 渐变);<el-icon><EditPen /></el-icon> 用的是 ElementPlus 的图标组件——图标能在模板里直接写(<EditPen />),是因为 main.js 里把整套图标全局注册了:
1import * as ElementPlusIconsVue from '@element-plus/icons-vue'2
3// 把图标库里的每个图标都注册成全局组件,模板里就能直接写 <EditPen />4for (const [key, component] of Object.entries(ElementPlusIconsVue)) {5 app.component(key, component)6}(97 篇讲的 ElementPlus 快速入门只管了组件库本体,这段图标注册是基础工程里额外加的,看到 <Promotion />、<Avatar /> 这类标签不认识的图标名,回来查这里即可。)
PPT 第 14 页叫”页面布局-左侧菜单”,就是把上面 el-aside 里的占位文字换成一个菜单组件;PPT 第 22 页又把它和路由连了起来。菜单的完整代码(资料\04. 基础代码\01. 左侧菜单栏.vue,直接放进 el-aside 里):
1<!-- 左侧菜单栏 -->2<el-menu router> <!-- router:点菜单项 = 跳到它 index 写的路径 -->3 <!-- 首页菜单 -->4 <el-menu-item index="/index">5 <el-icon><Promotion /></el-icon> 首页6 </el-menu-item>7
8 <!-- 班级管理菜单(二级菜单组) -->9 <el-sub-menu index="/manage">10 <template #title> <!-- 一级标题插槽 -->11 <el-icon><Menu /></el-icon> 班级学员管理12 </template>13 <el-menu-item index="/clazz">14 <el-icon><HomeFilled /></el-icon>班级管理15 </el-menu-item>16 <el-menu-item index="/stu">17 <el-icon><UserFilled /></el-icon>学员管理18 </el-menu-item>19 </el-sub-menu>20
21 <!-- 系统信息管理 -->22 <el-sub-menu index="/system">23 <template #title>24 <el-icon><Tools /></el-icon>系统信息管理25 </template>26 <el-menu-item index="/dept">27 <el-icon><HelpFilled /></el-icon>部门管理28 </el-menu-item>29 <el-menu-item index="/emp">30 <el-icon><Avatar /></el-icon>员工管理31 </el-menu-item>32 </el-sub-menu>33
34 <!-- 数据统计管理 -->35 <el-sub-menu index="/report">36 <template #title>37 <el-icon><Histogram /></el-icon>数据统计管理38 </template>39 <el-menu-item index="/empReport">40 <el-icon><InfoFilled /></el-icon>员工信息统计41 </el-menu-item>42 <el-menu-item index="/stuReport">43 <el-icon><Share /></el-icon>学员信息统计44 </el-menu-item>45 <el-menu-item index="/log">46 <el-icon><Document /></el-icon>日志信息统计47 </el-menu-item>48 </el-sub-menu>49</el-menu>三个要点:
<el-menu-item> 是一个菜单项,index 属性写的是路径(/dept、/emp);<el-sub-menu> 是带子项的分组(左侧带折叠箭头),它的名字写在 <template #title> 里;<el-menu> 上的 router 属性是关键:加上它之后,点菜单项就会跳转到 index 对应的路径(内部相当于 router.push),不用自己写点击事件。本机实测这一条菜单的最终样子:一级菜单共四项(首页 / 班级学员管理 / 系统信息管理 / 数据统计管理),后三项展开后分别是 班级管理·学员管理、部门管理·员工管理、员工信息统计·学员信息统计·日志信息统计——和上面代码一一对应。
PPT 第 17 页给了两句话:
Vue Router:Vue 的官方路由。为 Vue 提供富有表现力、可配置的、方便的路由。(https://router.vuejs.org/zh)
Vue 中的路由:路径 与 组件 的对应关系。
“路由”这个词在 Vue 里的含义就这么朴素:什么路径,显示什么组件。PPT 第 16~17 页配了三张浏览器截图,正好是”三个路径 → 三个组件”的现场:

localhost:5173/dept,左侧菜单里”部门管理”高亮,右侧主区域是部门管理页。路径 /dept 与”部门管理组件”是对应关系
localhost:5173/emp,菜单高亮换到”员工管理”,主区域换成员工管理页(这一章它还是空的,显示”员工管理”三个字)。地址变了,显示的内容就跟着变,这就是路由;而且顶栏和左侧菜单没有重新加载——因为它们不属于会变的那部分注意截图的另一个细节:只有右侧主区域在变。这解释了为什么”布局壳”要单独拎出来(layout/index.vue)——它是所有页面的”公共外壳”,变的只是壳里那一块。
PPT 第 18 页的原图是一张示意图,用 /stu、/emp、/dept 三个例子说明三者怎么配合:
1 ┌──────────────── Router 实例(createRouter)────────────────┐2 │ routes: │3 │ /stu → /views/stu/index.vue │4 │ /emp → /views/emp/index.vue │5 │ /dept → /views/dept/index.vue │6 └──────────────────────────────────────────────────────────────┘7 ▲ 更新8 路由请求 │9 <router-link to='/emp'>员工管理</router-link> ──▶ <router-view></router-view>10 (路由链接组件,浏览器会解析成 <a>) (动态视图组件,按路径渲染对应组件)| 组成部分 | PPT 原文 | 性质 |
|---|---|---|
| Router 实例 | 路由实例,基于 createRouter 函数创建,维护了应用的路由信息 | 一张”路径 → 组件”的对照表(routes) |
<router-link> | 路由链接组件,浏览器会解析成 <a> | 页面上的”链接”,点了就发路由请求 |
<router-view> | 动态视图组件,用来渲染展示与路由路径对应的组件 | 占位符:路由说该显示谁,它就在这儿把谁渲染出来 |
PPT 第 18 页还贴了一张命令行截图(npm create vue@3.3.4 建工程时的问答),其中 “Add Vue Router for Single Page Application development?” 这一问选的是 No——意思是脚手架不会白送路由。本章用的基础工程里已经装好了 vue-router(package.json 里是 ^4.1.5),所以不用再装;自己从零建工程时,要么在这儿选 Yes,要么事后 npm install vue-router@4。

npm create vue@3.3.4 的交互式问答列表(TypeScript、JSX、Router、Pinia……全选了 No)。它说明的一件事:路由是工程的”加选项”,不选就没有 src/router/index.js本机实测:课程的案例工程里,链接组件 <router-link> 其实没有出现——左侧菜单用的是更省事的 el-menu router(PPT 第 22 页那个方案),点菜单项直接跳路径,效果等价。三种东西里,<router-view> 和 Router 实例是必须的。
| 问题 | 答案 |
|---|---|
| Vue 的路由指的是什么? | Vue Router 是 Vue 官方的路由,描述的是路径与组件的对应关系 |
| Vue Router 中的三个核心组成部分? | Router 实例(维护了应用的路由信息 routes);<router-link>(路由链接组件);<router-view>(动态视图组件) |
PPT 第 20~22 页把上面那套东西装进案例工程,一共四处改动:根组件、布局壳、路由表、左侧菜单。
<router-view>#基础工程里的 src/App.vue 长这样(“改之前”的样子):
1<script setup>2//引入views/layout/index.vue命名为Layout3import Layout from "@/views/layout/index.vue";4</script>5
6<template>7 <Layout></Layout> <!-- 直接写死:整站永远显示这一个布局组件 -->8</template>问题很明显:整站被写死成了一个组件,不管什么地址都显示”布局壳 + 占位文字”,页面切换无从谈起。PPT 第 21 页给出的第一个改动就是把它换成动态视图组件:
1<!-- src/App.vue(改完之后) -->2<script setup>3
4</script>5
6<template>7 <router-view></router-view> <!-- 这里显示谁,由当前路径决定 -->8</template>9
10<style scoped>11
12</style>el-main 里再放一个 <router-view>(嵌套路由)#布局壳(views/layout/index.vue)本身就是”某个路径对应的组件”了,所以它的 el-main 里还能再放一个 <router-view>——用来渲染”壳里面那一块”该显示的页面:
1<el-aside width="200px" class="aside">2 <!-- 左侧菜单栏(el-menu) -->3</el-aside>4
5<el-main>6 <router-view></router-view> <!-- 子页面在这里渲染 -->7</el-main>这就叫嵌套路由(PPT 第 21 页的关键词):两层 router-view 套着用——
1App.vue 的 <router-view> ← 第一层:路径 '/' → 渲染 LayoutView(布局壳)2 └─ LayoutView 的 <router-view> ← 第二层:子路径 '/dept' → 渲染 dept/index.vue3 (顶栏、左侧菜单是外层,不受影响)PPT 第 21 页给它配的画面是”壳 + 叶子页面”的组合,其中 views/dept/index.vue(子页面)现在只是一个占位:

views/dept/index.vue)——整页只有一个词”部门管理”。路由先把它接进 el-main 里,内容留到 99 篇再填;能显示这个词,就说明嵌套路由通了PPT 第 21 页还贴了另一个页面——登录页,它是”不套壳”的例子:

views/login/index.vue,灰色几何背景 + 居中白卡片 + 用户名/密码/登录/重置)。它没有顶栏也没有左侧菜单——因为它在路由表里是独立的一级路由 /login,不挂在布局壳下面。有登录页这个反例,嵌套路由的意思就清楚了:“套壳”还是”不套壳”,由它在 routes 里挂在哪儿决定路由实例在 src/router/index.js(课程工程的真实代码):
1import { createRouter, createWebHistory } from 'vue-router'2
3import IndexView from '@/views/index/index.vue'4import ClazzView from '@/views/clazz/index.vue'5import DeptView from '@/views/dept/index.vue'6import EmpView from '@/views/emp/index.vue'7import LogView from '@/views/log/index.vue'8import StuView from '@/views/stu/index.vue'9import EmpReportView from '@/views/report/emp/index.vue'10import StuReportView from '@/views/report/stu/index.vue'11import LayoutView from '@/views/layout/index.vue'12import LoginView from '@/views/login/index.vue'13
14const router = createRouter({15 history: createWebHistory(import.meta.env.BASE_URL),16 routes: [17 {18 path: '/', // 一级路径:整个布局壳19 name: '',20 component: LayoutView,21 redirect: '/index', // 打开 '/' 时重定向到 '/index'(首页)22 children: [ // 子路由:它们渲染在 LayoutView 的 <router-view> 里23 { path: 'index', name: 'index', component: IndexView },24 { path: 'clazz', name: 'clazz', component: ClazzView },25 { path: 'stu', name: 'stu', component: StuView },26 { path: 'dept', name: 'dept', component: DeptView },27 { path: 'emp', name: 'emp', component: EmpView },28 { path: 'log', name: 'log', component: LogView },29 { path: 'empReport', name: 'empReport', component: EmpReportView },30 { path: 'stuReport', name: 'stuReport', component: StuReportView },31 ]32 },33 { path: '/login', name: 'login', component: LoginView } // 独立路由:不套布局壳34 ]35})36
37export default router
routes 写法——path: '/' 的组件是 Layout(布局壳),它下面用 children 挂了一串子路由(index / clazz / stu / dept / emp / empReport / stuReport / log)。父子关系就是”壳”与”壳里那块”的关系,这也是本机实测工程 src/router/index.js 的实际内容几个必须点明的细节:
| 写法 | 含义 |
|---|---|
createRouter({ ... }) | 创建 Router 实例(PPT 第 18 页说的那个”维护路由信息”的家伙) |
routes | 路由表:路径 → 组件的对照关系 |
import ... from '@/views/...' | 把每个页面组件先 import 进来再写进路由表(@ = src 的别名,vite.config.js 里配的) |
children | 子路由:它们的路径会拼在父路径后面('dept' + '/' = /dept),并且渲染在父组件的 <router-view> 里 |
redirect: '/index' | 重定向:访问 / 时自动跳到 /index(所以打开首页看到的是 IndexView,不是空白) |
{ path: '/login', ... } | 独立路由(不在 children 里):不套布局壳,整页显示登录页 |
路由表写完还差一步——把 Router 实例”装”到应用上。这一步在 main.js 里(基础工程已配好):app.use(router)。有了它,<router-view> 才有”路由信息”可渲染。
路由表里的路径,就是菜单项 index 属性的值:

<el-menu-item index="/dept">…部门管理</el-menu-item> 和 <el-menu-item index="/emp">…员工管理</el-menu-item>。index 里写的 /dept、/emp 和路由表里的子路径同名,点菜单项就等于跳转到那个路径(再靠 <el-menu router> 的属性自动完成跳转)到这里,“动态菜单”这个名字就能解释了:菜单不是写死的八行字,而是”路径的入口”——每一项指向一个路由,路由指向一个组件;菜单点谁,el-main 里的 <router-view> 就换成谁。要加一个新页面,就是”加一个组件 + 在 routes 里加一条 + 在菜单里加一项”,三处对应。
本机实测(前端 dev server 跑在 5173,后端课程工程跑在 8081,/api 由 Vite 代理转发——这三个端口 99 篇会讲,这里是它的前置环境):
http://localhost:5173/ → 左侧一级菜单四项:首页 / 班级学员管理 / 系统信息管理 / 数据统计管理;首页内容显示出来了,说明 / 的 redirect: '/index' 生效了;http://localhost:5173/dept 也能独立打开部门管理页(菜单高亮自动落到”部门管理”)——说明”路径与组件的对应关系”真的在工作,不是只有点菜单才能进去;src/router/index.js 里,/ 指向带 <router-view> 的 LayoutView,children 里挂着 index/clazz/stu/dept/emp/log/empReport/stuReport 八个子路由——这就是 PPT 第 21 页说的嵌套路由结构,实测工程里就是这个写法。判断”嵌套路由有没有配错”有个笨办法但很快:把 layout/index.vue 的 el-main 里那行 <router-view> 临时删掉,再点菜单——地址栏会照常变,但主区域永远是空的。地址变、主区域不变,说明路由通了而”渲染出口”没了;反过来,如果地址栏都不变,那要查的是菜单项的 index 或 el-menu 的 router 属性。
| 问题 | 答案 |
|---|---|
| 本章的 Tlias 前端实战包括哪些模块? | 开发模式、整体布局、部门管理、员工管理、登录/退出(班级管理、学员管理标着”自己实现”) |
| 什么是前后端分离开发? | 前后端开发人员独立开发、独立部署;两边靠接口文档协作(原型+需求 → 前端,接口文档 → 后端),之间是”请求 / 响应” |
| 前后端分离的开发流程? | 需求分析 → 接口定义 → 前后端并行开发 → 测试 → 前后端联调测试;接口定义完就可以并行开工 |
| 准备工作两步? | 导入资料里的基础工程到 VSCode、启动前端项目并访问(本机实测 npm install + npm run dev → http://localhost:5173) |
| Container 布局容器有哪五个? | el-container 外层容器、el-header 顶栏容器、el-aside 侧边栏容器、el-main 主要区域容器、el-footer 底栏容器;案例结构是”外层 container 套 header + 内层 container(aside + main)” |
| 左侧菜单用什么搭? | el-menu(加 router 属性就支持点菜单跳转)+ el-menu-item(一个菜单项,index 写路径)+ el-sub-menu(可折叠的分组,标题放 <template #title>) |
| Vue 的路由是什么? | 路径与组件的对应关系;Vue Router 是 Vue 官方路由 |
| Vue Router 三个核心组成部分? | Router 实例(createRouter 创建,维护 routes)、<router-link>(路由链接组件,会解析成 <a>)、<router-view>(动态视图组件,按路径渲染对应组件) |
| 什么是嵌套路由?案例里在哪两层? | 两级 <router-view> 套着用:App.vue 里的渲染布局壳(/ → LayoutView),layout/index.vue 的 el-main 里那个渲染子页面(children 里的 /dept、/emp…);顶栏和菜单是外层,主区域随路径变 |
| 本机实测验证了什么? | 一级菜单四项;/ 自动重定向到 /index;直接访问 /dept 能独立打开;routes 里 / + children(8 个子路由)+ 独立的 /login 就是嵌套路由结构 |
npm install + npm run dev,跑在 http://localhost:5173(Vite 3.2.10)el-container 外层容器、el-header 顶栏容器、el-aside 侧边栏容器、el-main 主要区域容器、el-footer 底栏容器(案例没用 footer)el-container 里放 el-header,再套一层 el-container 放 el-aside + el-main”:顶栏横贯整页,下面左菜单右主区域el-menu(带 router 属性 → 点菜单项跳转到 index 里写的路径)+ el-menu-item(index 属性写路径)+ el-sub-menu(可折叠分组,标题写在 <template #title> 里);菜单图标(<Promotion /> 等)是 main.js 里全局注册的 ElementPlus 图标库createRouter 创建、维护 routes)、<router-link>(路由链接组件,解析成 <a>)、<router-view>(动态视图组件,按路径渲染组件)App.vue 的 <router-view> 渲染布局壳(/ → LayoutView),layout/index.vue 的 el-main 里的 <router-view> 渲染子页面(children 里的 /dept、/emp 等);redirect: '/index' 让访问 / 时跳到首页;/login 不在 children 里,所以不套布局壳、整页显示http://localhost:5173/dept 能独立打开部门管理页;工程 src/router/index.js 里 / + children(index、clazz、stu、dept、emp、log、empReport、stuReport 八个子路由)+ 独立 /login,就是这个嵌套结构;Vite 的 5173 被占用时会自动换端口,排查”页面不是我改的那个”先看命令行打印的 Local: 地址2-1 用布局容器搭一个”顶栏 + 左菜单 + 右主区域”的三段骨架 要求:页面顶部是一条顶栏(左边写”Tlias智能学习辅助系统”,右边写”修改密码 | 退出登录”,整条用渐变色做背景);下面分成左右两块——左边一条固定宽度的侧边栏(先只放一句”左侧菜单栏”占位),右边是主区域(先只放一句”右侧核心展示区域”占位)。主区域要能自动占满剩下的一整行宽度。
一级 · 思路:别自己算宽度——用布局容器:外层容器负责”上下”、内层容器负责”左右”。顶栏容器直接扔进外层容器里,侧边栏和主区域容器扔进内层容器里
二级 · 方法:<el-container> 会根据里面装的是”顶栏/底栏”还是”侧边栏/主区域”自动决定竖排还是横排;<el-aside> 用 width="200px" 指定宽度;样式写在 <style scoped> 里
三级 · 骨架:<el-container> → <el-header class="header">…</el-header> → 再一层 <el-container> → <el-aside width="200px" class="aside">____</el-aside> + <el-main>____</el-main>;顶栏渐变用 background-image: linear-gradient(to right, #00547d, …)
1<script setup>2
3</script>4
5<template>6 <div class="common-layout">7 <el-container> <!-- 外层:上下排 -->8 <el-header class="header"> <!-- 顶栏 -->9 <span class="title">Tlias智能学习辅助系统</span>10 <span class="right_tool">修改密码 | 退出登录</span>11 </el-header>12
13 <el-container> <!-- 内层:左右排 -->14 <el-aside width="200px" class="aside"> <!-- 侧边栏:固定宽 -->15 左侧菜单栏16 </el-aside>17 <el-main> <!-- 主区域:占满剩余宽度 -->18 右侧核心展示区域19 </el-main>20 </el-container>21 </el-container>22 </div>23</template>24
25<style scoped>26.header {27 background-image: linear-gradient(to right, #00547d, #007fa4, #00aaa0, #00d072, #a8eb12);28}29.title { color: white; font-size: 40px; font-family: 楷体; font-weight: bolder; }30.right_tool { float: right; line-height: 60px; color: white; }31.aside { width: 220px; border-right: 1px solid #ccc; height: 730px; }32</style>对照 PPT 第 13~14 页和课程工程 src/views/layout/index.vue。检查点:① 顶栏横贯整页(不是只占左边一块);② 主区域自动占满右侧宽度;③ 把 <el-aside> 挪到内层 el-container 外面,布局立刻乱掉——验证”内层容器管左右”这件事。
2-2 写一份带二级分组的左侧菜单
要求:菜单分三组——第一组是单独的”首页”(路径 /index);第二组叫”班级学员管理”(下面两项:班级管理 /clazz、学员管理 /stu);第三组叫”系统信息管理”(下面两项:部门管理 /dept、员工管理 /emp);点任意一项能跳到对应路径。
一级 · 思路:单个项用”菜单项”组件,分组用”子菜单”组件;“点了能跳转”这件事不用自己写事件,菜单组件有个属性打开它就行
二级 · 方法:<el-menu router>;<el-menu-item index="/路径">;分组用 <el-sub-menu index="/随便一个分组标识">,分组标题放 <template #title> 里;图标可加可不加(<el-icon><Promotion /></el-icon>)
三级 · 骨架:<el-menu ____> / <el-menu-item ____="/index">首页</el-menu-item> / <el-sub-menu index="/manage"><template #____>班级学员管理</template><el-menu-item index="/clazz">____</el-menu-item>…</el-sub-menu>
1<template>2 <el-menu router>3 <!-- 第一组:单个项 -->4 <el-menu-item index="/index">5 <el-icon><Promotion /></el-icon> 首页6 </el-menu-item>7
8 <!-- 第二组:可折叠分组 -->9 <el-sub-menu index="/manage">10 <template #title>11 <el-icon><Menu /></el-icon> 班级学员管理12 </template>13 <el-menu-item index="/clazz">14 <el-icon><HomeFilled /></el-icon>班级管理15 </el-menu-item>16 <el-menu-item index="/stu">17 <el-icon><UserFilled /></el-icon>学员管理18 </el-menu-item>19 </el-sub-menu>20
21 <!-- 第三组 -->22 <el-sub-menu index="/system">23 <template #title>24 <el-icon><Tools /></el-icon>系统信息管理25 </template>26 <el-menu-item index="/dept">27 <el-icon><HelpFilled /></el-icon>部门管理28 </el-menu-item>29 <el-menu-item index="/emp">30 <el-icon><Avatar /></el-icon>员工管理31 </el-menu-item>32 </el-sub-menu>33 </el-menu>34</template>对照 PPT 第 14、22 页。检查点:① 三组显示正确、分组能折叠;② 地址栏随点击变成 /dept、/emp(说明 router 属性生效);③ 把 router 属性删掉,点击地址栏就不再变了——这正是”菜单跳转”的原理。
2-3 写一份嵌套路由表
要求:有个布局壳组件(views/layout/index.vue)和四个页面组件(views/index/index.vue、views/dept/index.vue、views/emp/index.vue、views/login/index.vue);要求——打开 / 时进入布局壳并自动跳到 /index;布局壳里能切换 /index、/dept、/emp 三个页面;登录页 /login 不套布局壳、整页显示。
一级 · 思路:把”套壳的页面”写成布局壳的子路由(它们渲染在壳内部的出口里),把”不套壳的”写成和壳平级的一级路由
二级 · 方法:createRouter + createWebHistory;壳路由用 component + children + redirect 三件套;子路由的 path 不带斜杠(拼接用);页面组件先 import 再写进 routes
三级 · 骨架:import { ____, createWebHistory } from 'vue-router' / const router = ____({ history: createWebHistory(), routes: [ { path: '/', component: ____, redirect: '/index', ____: [ {path:'index', component: ____} … ] }, {path:'/login', component: ____} ] }) / export default ____
1import { createRouter, createWebHistory } from 'vue-router'2
3import LayoutView from '@/views/layout/index.vue' // 布局壳4import IndexView from '@/views/index/index.vue'5import DeptView from '@/views/dept/index.vue'6import EmpView from '@/views/emp/index.vue'7import LoginView from '@/views/login/index.vue'8
9const router = createRouter({10 history: createWebHistory(import.meta.env.BASE_URL),11 routes: [12 {13 path: '/', // 布局壳(一级路由)14 component: LayoutView,15 redirect: '/index', // 打开 '/' 自动跳首页16 children: [ // 子路由:渲染在 LayoutView 的 <router-view> 里17 { path: 'index', name: 'index', component: IndexView },18 { path: 'dept', name: 'dept', component: DeptView },19 { path: 'emp', name: 'emp', component: EmpView },20 ]21 },22 { path: '/login', name: 'login', component: LoginView } // 独立路由:不套壳23 ]24})25
26export default router配套的两处”出口”:App.vue 的模板写 <router-view></router-view>;layout/index.vue 的 <el-main> 里再写一个 <router-view></router-view>。检查点:① 打开 / 地址栏变成 /index;② 直接访问 /dept、/emp 都能进(顶栏还在);③ 访问 /login 时没有顶栏和菜单。
2-4 排查:菜单点了没反应 / 主区域永远是空的
下面这几种”症状”分别说明哪里有问题?各改一句话就好。
① 点左侧菜单,地址栏完全没变;
② 地址栏变成 /dept 了,但右侧主区域一直是”右侧核心展示区域”这句占位文字;
③ 访问 /dept 整页变成一片白,顶栏和菜单都不见了。
一级 · 思路:三句话对三个原因——“跳不跳”看菜单组件有没有开跳转开关;“变了不变”看壳里有没有渲染出口;“整页白”看这个路径挂在哪一级(挂错了就不套壳、也就没有顶栏),以及组件有没有 import 错
二级 · 方法:① 给 <el-menu> 加 router 属性(或检查 el-menu-item 的 index 写没写路径);② 在 layout/index.vue 的 <el-main> 里放 <router-view></router-view>(把占位文字顶掉);③ 检查 routes 里 /dept 是不是写成了一级路由(应当写在壳的 children 里),顺带确认组件 import 路径没写错
三级 · 骨架:① <el-menu ____>;② <el-main>____</el-main>;③ children: [ { path: '____', component: ____ } ]
| 症状 | 原因 | 改法 |
|---|---|---|
| ① 地址栏不变 | <el-menu> 少了 router 属性(菜单项只是”显示项”,不会自己跳转) | 写成 <el-menu router> |
| ② 地址变了、主区域还是占位文字 | 布局壳的 <el-main> 里没有 <router-view>(子路由没有”渲染出口”) | 把占位文字换成 <router-view></router-view> |
| ③ 整页白(顶栏菜单都没了) | /dept 被写成了一级路由(和 /login 平级),于是整页只渲染部门管理组件,不套布局壳;另外要确认组件 import 的路径拼写正确 | 把这条路由挪进壳路由的 children 里(path: 'dept',不带斜杠) |
一句话总结:“能不能跳”看菜单的 router,“跳到哪儿显示”看 <router-view>,“要不要顶栏菜单”看路由挂在 children 里还是平级。
3-1 照着课程案例做一遍”整体布局 + 动态菜单” 分步做(顺序和 PPT 第 12~22 页一致):
src/views/layout/index.vue,确认它的骨架是”外层容器装顶栏,内层容器装侧边栏和主区域”(这一段基础工程已经写好了,读懂它)src/views 下准备好每个菜单对应的页面组件(先用只写一句文字的占位组件就行,比如”部门管理”)App.vue 里”写死的布局组件”换成动态视图组件/ 指向布局壳并重定向到 /index;八个页面写成它的子路由;登录页写成独立路由/dept 也能直接进;访问 /login 没有顶栏和菜单一级 · 思路:整题只有两条线——“壳”决定不变的部分,“路由”决定变的部分。壳的出口是主区域里那个 <router-view>;路由表决定”哪个路径由谁渲染”
二级 · 方法:布局容器 el-container / el-header / el-aside / el-main;菜单 el-menu router + el-menu-item index + el-sub-menu;出口 <router-view>(两处:根组件、布局壳);路由表 createRouter + createWebHistory + routes + children + redirect
三级 · 骨架:App.vue 的模板只留 <router-view></router-view>;布局壳主区域放 <router-view></router-view>;路由表 { path:'/', component: LayoutView, redirect:'/index', children:[ {path:'index',…}, {path:'dept',…} … ] } + { path:'/login', … }
① src/App.vue:
1<script setup>2
3</script>4
5<template>6 <router-view></router-view>7</template>8
9<style scoped>10</style>② src/views/layout/index.vue 的关键两行(其余照基础工程原样保留):
1<el-aside width="200px" class="aside">2 <el-menu router> <!-- 第 3 步的菜单整段放这儿 -->3 <!-- …八个菜单项… -->4 </el-menu>5</el-aside>6
7<el-main>8 <router-view></router-view> <!-- 子页面的渲染出口 -->9</el-main>③ src/router/index.js:
1import { createRouter, createWebHistory } from 'vue-router'2
3import IndexView from '@/views/index/index.vue'4import ClazzView from '@/views/clazz/index.vue'5import StuView from '@/views/stu/index.vue'6import DeptView from '@/views/dept/index.vue'7import EmpView from '@/views/emp/index.vue'8import LogView from '@/views/log/index.vue'9import EmpReportView from '@/views/report/emp/index.vue'10import StuReportView from '@/views/report/stu/index.vue'11import LayoutView from '@/views/layout/index.vue'12import LoginView from '@/views/login/index.vue'13
14const router = createRouter({15 history: createWebHistory(import.meta.env.BASE_URL),16 routes: [17 {18 path: '/',19 component: LayoutView,20 redirect: '/index',21 children: [22 { path: 'index', name: 'index', component: IndexView },23 { path: 'clazz', name: 'clazz', component: ClazzView },24 { path: 'stu', name: 'stu', component: StuView },25 { path: 'dept', name: 'dept', component: DeptView },26 { path: 'emp', name: 'emp', component: EmpView },27 { path: 'log', name: 'log', component: LogView },28 { path: 'empReport', name: 'empReport', component: EmpReportView },29 { path: 'stuReport', name: 'stuReport', component: StuReportView },30 ]31 },32 { path: '/login', name: 'login', component: LoginView }33 ]34})35
36export default router④ 占位页面组件(每个目录下都这么写,改文字即可):
1<script setup>2
3</script>4
5<template>6 部门管理7</template>8
9<style scoped>10</style>检查点:
/index,主区域显示首页(课程工程里是一张 index.png 图);/dept、主区域显示”部门管理”——顶栏和菜单不闪不重载(这就是嵌套路由的效果);http://localhost:5173/emp 也能直接进去;/login:整页只有登录卡片,没有顶栏和菜单;index 值临时改成一个路由表里不存在的路径(比如 /deptt)再点它——地址栏会照常变、主区域却空白。这正好说明分工:菜单只管”路径”,路由表才管”路径对应谁”(试完记得改回来)。
做完这一题,99 篇就往 /dept 这个占位页面里填真正的内容(表格 + 数据)。如果你喜欢,那么欢迎来到我的世界!
了解更多暂未播放



"所有的相遇都是久别重逢。"—— 未知


