第13章 前端Web实战(Tlias案例)

一、开发模式

主流:前后端分离

1、什么是前后端分离开发模式?

前后端开发人员独立开发,独立部署

2、需求的开发流程?

需求分析->接口定义->前后端并行开发->测试->前后端联调测试

二、整体布局

1、准备工作

在vite.config.js目录中

javascript 复制代码
    alias: {
      // 将 @/xxx 映射到 src/xxx,相当于为src目录起别名
      '@': fileURLToPath(new URL('./src', import.meta.url))

src/view/layout/index.vue文件下

html 复制代码
<!-- 左侧菜单 -->
<template>
    <el-container>        
        <el-aside width="200px" class="aside">
          <el-menu>
              
            <el-sub-menu index="1">
              <template #title>
                <el-icon><HomeFilled /></el-icon>班级学员管理
              </template>
              <el-menu-item index="1-1">
                <el-icon><HelpFilled /></el-icon>班级管理
              </el-menu-item>
              <el-menu-item index="1-2">
                <el-icon><UserFilled /></el-icon>学员管理
              </el-menu-item>
            </el-sub-menu>
​
          </el-menu>
        </el-aside>
    <el-container>  
<template>

在main.js中注册icon图标

javascript 复制代码
// 引入icon
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
​
// 注册icon图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
}

2、动态菜单

(1)VueRouter

Vue Router:Vue Router | Vue.js 的官方路由

VueRouter:Vue的官方路由。为Vue提供富有表现力、可配置的、方便的路由。

Vue中的路由:路径与组件的对应关系。

组成:

Router实例:路由实例,基于createRouter函数创建,维护了应用的路由信息。

<router-link>:路由链接组件,浏览器会解析成<a>。

<router-view>:动态视图 组件,用来渲染展示与路由路径对应的组件。

src/view/layout/index.vue文件下,router-link路由链接组件环绕首页菜单并指定访问路径

html 复制代码
<!-- 首页菜单 -->
<router-link to="/index">
​
    <el-menu-item index="/index">
        <el-icon><Promotion /></el-icon> 首页
    </el-menu-item>
​
</router-link>

或者只需在他上级加上 router即可,以el-menu-item作为路径跳转

html 复制代码
<el-menu router>//默认为true
    <!-- 首页菜单 -->
    <el-menu-item index="/index">
        <el-icon><Promotion /></el-icon> 首页
    </el-menu-item>

src/router/index.js下创建Router实例

javascript 复制代码
import { createRouter, createWebHistory } from "vue-router";
​
import IndexView from "@/views/index/index.vue";
import ClazzView from "@/views/clazz/index.vue";
import DeptView from "@/views/dept/index.vue";
import EmpView from "@/views/emp/index.vue";
import LogView from "@/views/log/index.vue";
import StuView from "@/views/stu/index.vue";
import EmpReportView from "@/views/report/emp/index.vue";
import StuReportView from "@/views/report/stu/index.vue";
import LayoutView from "@/views/layout/index.vue";
import LoginView from "@/views/login/index.vue";
​
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    { path: "/index", name: "index", component: IndexView },
    { path: "/clazz", name: "clazz", component: ClazzView },
    { path: "/stu", name: "stu", component: StuView },
    { path: "/dept", name: "dept", component: DeptView },
    { path: "/emp", name: "emp", component: EmpView },
    { path: "/log", name: "log", component: LogView },
    { path: "/empReport", name: "empReport", component: EmpReportView },
    { path: "/stuReport", name: "stuReport", component: StuReportView },
    { path: "/login", name: "index", component: LoginView },
  ],
});
​
export default router;
复制代码

访问地址就会访问路由展示组件处

1.Vue的路由指的是什么?

VueRouter是vue官方的路由 ,描述的是路径与组件的对应关系

2、VueRouter中的三个核心组成部分?

router实例,维护了应用的路由信息(routes)

<router-link>:路由链接组件

<router-view>:动态视图组件

(2)VueRouter嵌套路由

src/router/index.js这样写

javascript 复制代码
import { createRouter, createWebHistory } from "vue-router";
​
import IndexView from "@/views/index/index.vue";
import ClazzView from "@/views/clazz/index.vue";
import DeptView from "@/views/dept/index.vue";
import EmpView from "@/views/emp/index.vue";
import LogView from "@/views/log/index.vue";
import StuView from "@/views/stu/index.vue";
import EmpReportView from "@/views/report/emp/index.vue";
import StuReportView from "@/views/report/stu/index.vue";
import LayoutView from "@/views/layout/index.vue";
import LoginView from "@/views/login/index.vue";
​
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      name: "",
      component: LayoutView,
      redirect: "/index", //重定向
      children: [
        { path: "/index", name: "index", component: IndexView },
        { path: "/clazz", name: "clazz", component: ClazzView },
        { path: "/stu", name: "stu", component: StuView },
        { path: "/dept", name: "dept", component: DeptView },
        { path: "/emp", name: "emp", component: EmpView },
        { path: "/log", name: "log", component: LogView },
        { path: "/empReport", name: "empReport", component: EmpReportView },
        { path: "/stuReport", name: "stuReport", component: StuReportView },
      ],
    },
    { path: "/login", name: "login", component: LoginView },
  ],
});
​
export default router;

三、部门管理

1、列表查询

(1)页面布局

基础页面布局,在src/views/dept/index.vue

html 复制代码
<script setup>
import { ref } from "vue";
const tableData = ref([
  {
    id: 1,
    name: "学工部",
    createTime: "2022-09-01T23:06:29",
    updateTime: "2022-09-01T23:06:29",
  },
]);
</script>
​
<template>
  <h1>部门管理</h1>
  <div class="container">
    <el-button type="primary"> + 新增部门</el-button>
  </div>
​
  <div class="container">
    <el-table :data="tableData" border style="width: 100%">
      <el-table-column prop="id" label="序号" width="100" align="center"/>
      <el-table-column prop="name" label="部门名称" width="260" align="center"/>
      <el-table-column prop="updateTime" label="最后操作时间" width="300" align="center"/>
      <el-table-column prop="address" label="操作" align="center">
        <template #default="scope">
          <el-button type="primary" size="small"><el-icon><EditPen /></el-icon>编辑</el-button>
          <el-button type="danger" size="small"><el-icon><DeleteFilled /></el-icon>删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
​
<style scoped>
.container {
  margin: 10px 0px;
}
</style>
复制代码

(2)加载数据

html 复制代码
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

//钩子函数  页面加载完毕后调用钩子函数,加载其他函数
onMounted(() => {
  search();
});
//查询
const search = async () => {
  const result = await axios.get(
    "https://apifoxmock.com/m1/3128855-1224313-default/depts"
  );
  if (result.data.code) {
    //JS隐式类型转换  0、''、null、undefined都是false;其他都是true
    deptList.value = result.data; //拿到数据->数组
  }
};
const deptList = ref([]);
</script>

<template>
  <h1>部门管理</h1>
  <div class="container">
    <el-button type="primary"> + 新增部门</el-button>
  </div>

  <div class="container">
    <el-table :data="deptList" border style="width: 100%">
      <el-table-column prop="id" label="序号" width="100" align="center" />
      <el-table-column
        prop="name"
        label="部门名称"
        width="260"
        align="center"
      />
      <el-table-column
        prop="updateTime"
        label="最后操作时间"
        width="300"
        align="center"
      />
      <el-table-column prop="address" label="操作" align="center">
        <template #default="scope">
          <el-button type="primary" size="small"
            ><el-icon><EditPen /></el-icon>编辑</el-button
          >
          <el-button type="danger" size="small"
            ><el-icon><DeleteFilled /></el-icon>删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<style scoped>
.container {
  margin: 10px 0px;
}
</style>

(3)程序优化1

为解决上述问题,我们在前端项目开发时,通常会定义一个请求处理工具类-request.js

与服务端进行异步交互 的逻辑,通常会封装到一个单独的api中,如:dept.js。

在src目录下新建utils/request.js,创建请求处理工具类,并导出请求工具

src/api/depts目录下导入工具,并导出查询操作

使用工具的查询操作查询部门

(4)程序优化2

utils/request.js中指定url请求路径的前缀

vite.config.js中配置前端请求服务器信息,配置代理服务器,交给后端8080端口的tomcat处理,并把请求路径的/api替换为空串

这样就做到了前端访问后端

2、新增部门

api/dept.js中

javascript 复制代码
//新增
export const addApi = (dept) =>  request.post('/depts', dept);

src/views/dept/index.vue中

提示: 新增部门和编辑部门可以共用一个dialog对话框。

在ElementPlus提供的form表单组件中,就提供了对应的表单校验的功能。具体步骤如下:

定义表单校验规则,通过rules属性与表单绑定

通过prop属性绑定对应的表单项。

通过ref属性注册表单的引用,表单提交时,校验表单,校验通过,则允许提交表单。

javascript 复制代码
//Dialog对话框
const dialogFormVisible = ref(false);//控制弹窗显示 / 隐藏的响应式变量
const formTitle = ref('');//弹窗的标题文字
const dept = ref({name: ''});//保存弹窗表单里部门的表单数据对象。
javascript 复制代码
//新增部门
const addDept = () => {
  dialogFormVisible.value = true;
  formTitle.value = '新增部门';
  dept.value = {name: ''};//再次打开清空

  //重置表单的校验规则-提示信息
  if (deptFormRef.value){
    deptFormRef.value.resetFields();
  }
  
}

3、修改部门

对于修改操作,通常会分为两步进行:①查询回显、②保存修改。

javascript 复制代码
//根据ID查询
export const queryByIdApi = (id) =>  request.get(`/depts/${id}`);//路径参数传输,使用模板字符串

//修改
export const updateApi = (dept) =>  request.put('/depts', dept);

交互逻辑:

点击编辑按钮,根据ID进行查询,弹出对话框,完成页面回显展示。(查询回显)

javascript 复制代码
//编辑
const edit = async (id) => {
  formTitle.value = '修改部门';
  //重置表单的校验规则-提示信息
  if (deptFormRef.value){
    deptFormRef.value.resetFields();
  }

  const result = await queryByIdApi(id);
  if(result.code){
    dialogFormVisible.value = true;
    dept.value = result.data;
  }
}

点击确定按钮,保存修改后的数据,完成数据更新操作。(保存修改)

javascript 复制代码
let result ;
      if(dept.value.id){ //修改
        result = await updateApi(dept.value);
      }else{ //新增
        result = await addApi(dept.value);
      }

4、删除部门

点击删除按钮,需要删除当前这条数据,删除完成之后,刷新页面,展示出最新的数据。 由于删除是一个比较危险的操作,为避免误操作,通常会在点击删除之后,弹出确认框进行确认。

四、员工管理-列表查询

1、页面布局(搜索栏)

2、页面布局(搜索栏-watch侦听)

如果表单封装的数据较多,建议绑定到一个对象中

作用:侦听一个或多个响应式数据源 ,并在数据源变化时 调用传入的回调函数

用法:

1、导入watch函数

2、执行watch函数,传入要侦听的响应式数据源(ref对象)和回调函数

侦听对象

javascript 复制代码
// 1. 侦听一个响应式数据
const a = ref('');
watch(a, (newVal, oldVal) => {
  console.log(`变化后的值: ${newVal}, 变化前的值: ${oldVal}`);
})
// 2. 侦听一个对象(侦听对象的全部属性)
const user = ref({name: '', age: 10});
watch(user, (newVal, oldVal) => {
  console.log(newVal);
}, {deep: true}) //深度侦听
// 3. 侦听对象中的某一个属性
const user2 = ref({name: '', age: 10});
watch(() => user2.value.age, (newVal, oldVal) => {
  console.log(`变化后的值: ${newVal}, 变化前的值: ${oldVal}`);
}) 

小结

1、watch侦听函数的作用?

侦听一个或多个响应式数据源 ,并在数据源变化时调所给的回调函数

2、watch函数调用时传入的参数?

参数一:侦听的源,可以是一个ref对象,一个函数等。

参数二:侦听的源发生变化时,触发的回调函数

参数三:可选,是一个对象(deep、immediate)

3、能否侦听对象的一个属性或全部属性?

可以

侦听一个属性:第一个参数传递一个函数()=>user.value.name

侦听全部属性:第三个参数传递一个对象 ,指定deep深度侦听

3、页面布局(表格)

4、页面布局(分页条)

5、页面交互

五、员工管理-新增员工

1、页面布局(引入)

2、页面布局(展示优化)

3、页面布局(Upload组件)

4、页面交互(工作经历)

5、页面交互(工作经历-watch监听)

6、页面交互(保存员工)

7、页面交互(表单校验)

六、员工管理-修改员工

1、查询回显

2、修改数据

七、员工管理-删除员工

1、删除单个

2、批量删除

八、登录退出

1、登录功能实现

2、携带令牌访问服务端

3、携带令牌访问服务端(Upload组件处理)

九、登录-响应401跳转登录页面

十、退出

1、展示当前登录员工

2、功能实现

十一、Tlias案例-打包部署

1、生成打包文件

2、安装Nginx代理服务器

介绍:Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强,在各大型互联网公司都有非常广泛的使用。

官网:nginx

3、部署Nginx服务器

将上面的打包文件放到html目录下,双击可执行文件启动

4、访问前后端

访问 http://localhost:80,并运行后端

注意:Nginx默认占用80端口号,如果80端口号被占用,可以在Nginx.conf中修改端口号。使用新的nginx.conf文件,进行反向代理

nginx目录下cmd,重新加载nginx的配置文件

停止nginx服务器

十二、关键字总结

1、导入组件

(1)vite.config.js 配置文件

javascript 复制代码
// 转成电脑真实磁盘路径字符串;拼装文件路径地址;*
import { fileURLToPath, URL } from 'node:url'

//写代码时有代码提示、语法校验*
import { defineConfig } from 'vite'

//让 vite 能识别.vue单文件组件*
import vue from '@vitejs/plugin-vue'

(2)main.js 主入口

javascript 复制代码
//Vue3 的创建应用实例的函数。
import { createApp } from 'vue'

//创建 Pinia 实例,用于全局共享状态数据(登录用户信息、全局变量等)。
import { createPinia } from 'pinia'

//导入根组件
import App from './App.vue'
import router from './router'

//导入 Element‑Plus UI 组件库(vue3 版本),提供 el‑button、el‑table、el‑dialog、el‑form 等全套组件。
import ElementPlus from 'element-plus'

//导入 Element‑Plus 的全部 css 样式文件。
//⚠️必须导入!不导入,所有 element 组件没有样式,页面样式错乱。
import 'element-plus/dist/index.css'

// 把组件默认英文改成中文
import zhCn from 'element-plus/es/locale/lang/zh-cn'

//把图标库全部图标一次性导入
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

// 导入项目全局自定义 css
import './assets/main.css'

(3)request.js 携带token

javascript 复制代码
//axios 网络请求库,用来发 http 请求。
import axios from 'axios'

//消息提示组件
import { ElMessage } from 'element-plus'

//vue‑router 路由对象;可以做页面跳转,比如没登录跳转到登录页。
import router from '../router'

(4)conter.js 全局仓库容器

javascript 复制代码
//Vue3 响应式 API,定义基础类型响应式数据(数字、字符串、布尔)。
//依赖别的响应式数据,自动算出新值;依赖的数据变了,它自动更新,不能直接手动赋值。
import { ref, computed } from 'vue'

//pinia 提供的函数,用来创建全局仓库 store。
import { defineStore } from 'pinia'

(5)api包 发送请求,交接后端

javascript 复制代码
//导入根组件
//自带请求拦截器(自动带 token)、响应拦截器。所有接口都使用这个 request 发请求,不用原生 axios。
import request from '@/utils/request'

(6)views包 主布局页面

javascript 复制代码
//定义响应式数据。简单类型(字符串、数字、布尔)或者对象数组,用 ref 包裹,页面才会数据驱动,数据变页面自动更新。
//生命周期钩子函数。组件渲染完成(页面挂载完毕)自动执行,一般在这里加载页面初始数据,比如一进来就查询部门列表。
import { ref, onMounted } from 'vue'

// ElMessage:轻提示消息,成功 / 失败提示
// ElMessageBox:确认弹窗(删除前弹框确认)
import { ElMessage, ElMessageBox } from 'element-plus'

// 前面讲的部门接口文件,导入已经封装好的后端请求函数。
import { queryAllApi, addDeptApi, queryInfoApi, updateDeptApi, deleteDeptApi } from '@/api/dept'
相关推荐
aircrushin1 小时前
Claude 5 之后,上下文工程该做减法了
前端·人工智能·后端
huabuyu1 小时前
CLS 自动化归因及自愈:让系统自己定位根因、修复并验证
前端·javascript
用户6919026813391 小时前
React 组件间通信全解析:从父子到跨级
前端·javascript
echoVic1 小时前
同一个 bug,我在 WebGPU 渲染器里犯了两轮:一帧内多次写同一块 buffer
前端·webgl·图形学
胡萝卜术1 小时前
数据主权与渲染防线:从受控组件到性能优化的完整图景
前端·javascript·面试
DFT计算杂谈1 小时前
FeSe超薄膜在CaF2衬底上的电子结构DFT研究
java·服务器·前端
mONESY1 小时前
React 组件通信进化史:从 Props 层层搬运到 Context 跨层级直达
javascript
BreezeJiang1 小时前
父组件一更新,子组件就必须跟着更新吗?从 memo 走到 useCallback
前端·javascript
胡萝卜术1 小时前
复用与并行:从自定义 Hooks 封装状态逻辑,到 Web Worker 的多线程计算
前端·javascript·面试