前端Web案例-部门管理

YangeIT大约 31 分钟Maven基础框架私服聚合继承依赖传递

前端Web案例-部门管理

相关信息

在前面的课程中,我们学习了Vue工程化的基础内容、TS、ElementPlus,那接下来呢,我们要通过一个案例,加强大家对于Vue项目的理解,并掌握Vue项目的开发。 这个案例呢,就是我们之前所做的Yangeit智能学习辅助系统。

image-20231220210318788
image-20231220210318788

在这个案例中,我们主要完成 部门管理员工管理 的功能开发。 而今天呢,我们先来完成部门管理的功能开发,而在完成部门管理的功能开发之前,先需要完成基础的准备工作。 所以今天的课程安排如下:

  • 前后端分类开发
  • 准备工作
  • 页面布局
  • Vue-Router
  • 部门管理

1. 前后端分离开发

前后端分离开发

在之前的课程中,我们介绍过,现在的企业项目开发有2种开发模式:前后台混合开发前后台分离开发

前后台混合开发,顾名思义就是前台后台代码混在一起开发。这种开发模式有如下缺点:

  • 沟通成本高:后台人员发现前端有问题,需要找前端人员修改,前端修改成功,再交给后台人员使用
  • 分工不明确:后台开发人员需要开发后台代码,也需要开发部分前端代码。很难培养专业人才
  • 不便管理:所有的代码都在一个工程中
  • 难以维护:前端代码更新,和后台无关,但是需要整个工程包括后台一起重新打包部署。

所以我们目前基本都是采用的前后台分离开发方式,如下图所示:

image-20231124150213401

我们将原先的工程分为前端工程和后端工程这2个工程,然后前端工程交给专业的前端人员开发,后端工程交给专业的后端人员开发。

问题1 前端页面需要数据,可以通过发送异步请求,从后台工程获取。但是,我们前后台是分开来开发的,那么前端人员怎么知道后台返回数据的格式呢?

问题2 后端人员开发,怎么知道前端人员需要的数据格式呢?

所以针对这个问题,我们前后台统一制定一套规范!我们前后台开发人员都需要遵循这套规范开发,这就是我们的接口文档。接口文档有离线版和在线版本,接口文档示可以查询今天提供资料/接口文档里面的资料。

问题3 那么接口文档的内容怎么来的呢?

是我们后台开发者根据产品经理提供的产品原型和需求文档所撰写出来的,产品原型示例可以参考今天提供资料/页面原型里面的资料。

那么基于前后台分离开发的模式下,我们后台开发者开发一个功能的具体流程如何呢?如下图所示:

image-20231220212542978
  1. 需求分析 :首先我们需要阅读需求文档,分析需求,理解需求。
  2. 接口定义 :查询接口文档中关于需求的接口的定义,包括地址,参数,响应数据类型等等
  3. 前后台并行开发 :各自按照接口文档进行开发,实现需求
  4. 测试 :前后台开发完了,各自按照接口文档进行测试
  5. 前后段联调测试 :前段工程请求后端工程,测试功能

2. 准备工作

准备工作

image
image

代码操作

1️⃣ 创建Vue项目

在自己工作目录下,运行 cmd 打开命令行,运行如下指令npm init vue@latest,来创建vue项目【这步可以不用操作,直接导入资料中提供的: vue-tlias-management.zip 】。

image
image
2️⃣ 安装依赖如果使用了案例代码,已经安装了,了解即可

1). 在命令行中执行如下命令,为创建好的Vue项目安装 ElementPlus、Axios 的依赖。

npm install element-plus --save

npm install axios
image
image

2). 为创建好的 Vue项目 配置ElementPlus (参照官网),在 main.ts 中引入如下配置信息 【注意:是追加如下内容】:

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

//引入ElementPlus的Icon组件
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(ElementPlus, {locale: zhCn})

app.mount('#app')

 
 
 
 
 
 
 
 
 


如果已经导入yangeit提供的 vue-tlias-management.zip 已经提前配置好了 👍

总结

课堂作业

  1. 根据上述提示,完成准备工作,并运行成功,效果如下🎤
image
image

3. 页面布局

页面布局

elementPlus官网open in new window

我们在制作一个页面的时候,一定是先关注整体的页面布局,然后再关注具体的细节处理 。 所以这一小节,我们就先来完成页面的整体布局。

image-20231220220950569

我们会看到,整个页面分为这么三个部分:

  1. 页头部分
  2. 侧边栏
  3. 主区域

而要完成这样的页面布局,我们其实是可以借助于 ElementPlus 中提供的 Container布局容器 来实现:

image-20231220221807112

Container布局容器,用于布局的容器组件,方便快速搭建页面的基本结构:

<el-container>:外层容器。 当子元素中包含 <el-header><el-footer> 时,全部子元素会垂直上下排列, 否则会水平左右排列。

<el-header>:顶栏容器。

<el-aside>:侧边栏容器。

<el-main>:主要区域容器。

<el-footer>:底栏容器。

而针对于我们当前案例的页面布局,基本的结构如下:

image-20231220220902109

提示:当 <el-container> 子元素中包含 <el-header> <el-footer> 时,全部子元素会垂直上下排列, 否则会水平左右排列。

代码操作

我们可以参照 ElementPlus 的官方网站中的 布局,拷贝其源码,然后对其做一个改造。 具体参照的源码如下:

image-20231220222504658
image-20231220222504658

操作在这!!! 👇 👇

  1. src/views 目录下,再创建一个子目录 layout ,在其中新建一个页面,页面命名为:index.vue
image
image
  1. index.vue准备好基础的组件结构后,就可以将代码直接复制到 <template> </template> 标签中。
image
image
  1. 最终的顶栏布局效果如下所示:
image
image

index.vue完整代码

👇 👇 👇

点击查看index.vue完整代码

代码已经在提供的资料中的index.vue中 image

layout/index.vue 中的内容如下:

<script setup lang="ts">

</script>

<template>
   <div class="common-layout">
    <el-container>
      <!-- 顶栏 - header -->
      <el-header class="header">
        <span class="title">Yangeit智能学习辅助系统</span>
        <span class="right_tool">
          <a href=""><el-icon><EditPen /></el-icon> 修改密码 &nbsp;&nbsp;&nbsp;&nbsp;</a>
          <a href=""><el-icon><SwitchButton /></el-icon> 退出登录&nbsp;&nbsp;&nbsp;&nbsp;</a>
        </span>
      </el-header>
      

      <!-- 左侧菜单 & 主区域 -->
      <el-container>
        <!-- 左侧菜单 -->
        <el-aside width="200px" class="aside">
          <el-scrollbar>
            <el-menu router>
              <!-- 首页菜单 -->
              <el-menu-item index="/index">
                <el-icon><Promotion /></el-icon> 首页
              </el-menu-item>
              
              <!-- 班级管理菜单 -->
              <el-sub-menu index="/manage">
                <template #title>
                  <el-icon><Menu /></el-icon> 班级学员管理
                </template>
                <el-menu-item index="/clazz">
                  <el-icon><HomeFilled /></el-icon>班级管理
                </el-menu-item>
                <el-menu-item index="/stu">
                  <el-icon><UserFilled /></el-icon>学员管理
                </el-menu-item>
              </el-sub-menu>
              
              <!-- 系统信息管理 -->
              <el-sub-menu index="/system">
                <template #title>
                  <el-icon><Tools /></el-icon>系统信息管理
                </template>
                <el-menu-item index="/dept">
                  <el-icon><HelpFilled /></el-icon>部门管理
                </el-menu-item>
                <el-menu-item index="/emp">
                  <el-icon><Avatar /></el-icon>员工管理
                </el-menu-item>
              </el-sub-menu>

              <!-- 数据统计管理 -->
              <el-sub-menu index="/report">
                <template #title>
                  <el-icon><Histogram /></el-icon>数据统计管理
                </template>
                <el-menu-item index="/empReport">
                  <el-icon><InfoFilled /></el-icon>员工信息统计
                </el-menu-item>
                <el-menu-item index="/stuReport">
                  <el-icon><Share /></el-icon>学员信息统计
                </el-menu-item>
                <el-menu-item index="/log">
                  <el-icon><Document /></el-icon>日志信息统计
                </el-menu-item>
              </el-sub-menu>

            </el-menu>
          </el-scrollbar>
        </el-aside>


        <el-main>Main</el-main>
      </el-container>
    </el-container>
  </div>
</template>

<style scoped>
.header {
  background-image: linear-gradient(to right, #e70cc5, #e94dcf, #eb6fd8, #ec8bdf, #eea5e6);
  line-height: 60px;
}

.title {
  color: white;
  font-size: 35px;
  font-family: 楷体;
  
}

.right_tool {
  float: right;
}

a {
  text-decoration: none;
  color: white;
}

.aside {
  border: 1px solid #ccc;
  height: 690px;
  width: 220px;
}
</style>

目前,我们点击左侧的菜单,右侧主区域展示的内容,还不能做到动态变化 。 那应该如何做到动态变化呢 ?

image
image

那要完成这个功能效果,我们就需要用到Vue生态中的路由 Vue-Router

4. Vue Router

4.1 路由介绍和入门

路由介绍和入门

image-20231220230246508
image-20231220230246508
  • Vue Router:Vue的官方路由。 为Vue提供富有表现力、可配置的、方便的路由。
  • Vue中的路由,主要定义的是路径与组件之间的对应关系。

比如,我们打开一个网站(物流项目open in new window),点击左侧菜单,地址栏的地址发生变化。 地址栏地址一旦发生变化,在主区域显示对应的页面组件。

image-20231220230500902
image-20231220230500902
image-20231220230547196
image-20231220230547196

VueRouter主要由以下三个部分组成,如下所示:

image-20231220230746358
image-20231220230746358
  • VueRouter:路由器类,根据路由请求在路由视图中动态渲染选中的组件
  • <router-link>;:请求链接组件,浏览器会解析成<a>;
  • <router-view>;:动态视图组件,用来渲染展示与路由路径对应的组件常用

代码操作

介绍完了VueRouter之后,接下来,我们就通过一个入门程序,来演示一下VueRouter的使用。

1). 安装 vue-router (创建Vue项目时,已经选择,观察node_modules文件夹中,是否存在vue-router文件夹,如果存在就已经安装了 )

npm install vue-router@4

2. 在src下创建router文件,如果存在就不用创建,将已经提供的index.ts路由文件,导入到route文件夹中,具体教程如下:👇 👇

image
image

main.ts 入口文件中进行配置,加入如下配置

import router from './router'
//..... 创建完vue的应用实例后,调用app.use
app.use(router)

3. 在 App.vue 根组件中,定义 <RouterView></RouterView> 标签

该标签将用于显示,访问的请求路径对应的组件。

<script setup lang="ts">

</script>

<template>
  <RouterView></RouterView>
</template>

<style scoped>

</style>
image
image

4. 测试

浏览器访问请求路径 http://127.0.0.1:5173/index,展示如下页面内容(该页面内容,就是我们在 index/index.vue 中定义的页面内容):

image
image

但是,切换左边的条目,绿色区域无变化!!!

5. 分析

这里我们用到了Vue中的嵌套路由,具体定义方式,主要是在配置路由信息时,通过children 来描述。如你所见,children 配置只是另一个路由数组,就像 routes 本身一样。因此,你可以根据自己的需要,不断地嵌套视图。

那为什么没有不断的变化嵌套视图尼?

解决方案:

image
image

6. 在进行测试

image-20231220233620531
image-20231220233620531
image-20231220233646337
image-20231220233646337

已经可以根据点击,切换具体页面了

问题:但是这些页面,是固定的页面,不是表格!!!肿么办!!!

总结

课堂作业

  1. 参考上述步骤,完成项目架子的搭建🎤

4.2 首页制作🎯

首页制作

其实首页,我们只需要展示一张图片即可。 直接在 index/index.vue 中引入一张图片即可,具体代码如下:

将图片放到assets文件夹中(模版工程中存在)

<script setup lang="ts">

</script>

<template>
    <img src="../../assets/index.png" />
</template>

<style scoped>

</style>

最终效果如下:

image
image

总结

课堂作业

  1. 根据上述提示,完成首页制作🎤

5. 部门管理

部门管理的页面内容,写在 src/views/dept/index.vue 中。

5.1 部门列表

部门列表

最终效果:

image
image

步骤:

  1. 基本布局
  2. 加载数据
  3. 程序优化

代码操作

基本布局

首先,根据页面原型、需求说明、接口文档,先完成页面的基本布局 。 可以参考 ElementPlus 中的组件,拷贝过来适当做一个改造。

image-20231221105804349

部门管理组件 src/views/dept/index.vue 具体的页面布局代码如下:

<script setup lang="ts">
import {ref} from 'vue'
import type { DeptModelArray } from '@/api/model/model'

//声明列表展示数据
let tableData = ref<DeptModelArray>([])
</script>

<template>
  <h1>部门管理</h1>
  <el-button type="primary" style="float: right" @click="">+ 新增</el-button>
  <br><br>

  <!-- 部门数据表格 -->
  <el-table :data="tableData" border style="width: 100%">
    <el-table-column type="index" label="序号"  width="80"  align="center"/>
    <el-table-column prop="name" label="部门名称" width="250"  align="center"/>
    <el-table-column prop="updateTime" label="最后操作时间" width="300"  align="center"/>
    <el-table-column label="操作"  align="center">
      <template #default="scope">
        <el-button size="small" type="primary" @click="">修改</el-button>
        <el-button size="small" type="danger"  @click="">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<style scoped>

</style>

表格中每一列展示的属性 prop 都是根据接口文档来的,接口文档返回什么样的数据,我们就安装对应的数据格式进行解析。

image
image

总结

课堂作业

  1. 根据上述提示,完成部门列表的书写,细心 细心 细心

注意:参考步骤完成上述的前端代码,多敲几遍。

5.2 新增部门

新增部门

接下来,我们再来完成新增部门的功能实现。

image
image

代码操作

1). 在 src/views/dept/index.vue 中完成页面布局,并编写交互逻辑,完成数据绑定。

完整代码如下:

<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray, DeptModel } from '@/api/model/model'
import {queryAllApi, addApi} from '@/api/dept'
import { ElMessage } from 'element-plus';

//声明列表展示数据
let tableData = ref<DeptModelArray>([])

//动态加载数据-查询部门
const queryAll = async () => {
  const result = await queryAllApi()
  tableData.value = result.data
}

//钩子函数
onMounted(() => {
  queryAll()
})


//新增部门
const dialogFormVisible = ref<boolean>(false) 
const deptForm = ref<DeptModel>({name: ''})
const formTitle = ref<string>('')

//点击新增按钮触发的函数
const add = () => {
  formTitle.value = '新增部门'
  dialogFormVisible.value = true
  deptForm.value = {name: ''}
}

//点击保存按钮-发送异步请求
const save = async () => {
  const result = await addApi(deptForm.value)
  if(result.code){
    ElMessage.success('操作成功')
  }else{
    ElMessage.error(result.msg)
  }
  dialogFormVisible.value = false
  queryAll()
}

</script>

<template>
  <h1>部门管理</h1>
  <el-button type="primary" style="float: right" @click="add">+ 新增</el-button>
  <br><br>

  <!-- 部门数据表格 -->
  <el-table :data="tableData" border style="width: 100%">
    <el-table-column type="index" label="序号"  width="80"  align="center"/>
    <el-table-column prop="name" label="部门名称" width="250"  align="center"/>
    <el-table-column prop="updateTime" label="最后操作时间" width="300"  align="center"/>
    <el-table-column label="操作"  align="center">
      <template #default="scope">
        <el-button size="small" type="primary" @click="">修改</el-button>
        <el-button size="small" type="danger"  @click="">删除</el-button>
      </template>
    </el-table-column>
  </el-table>

  <!-- 新增部门 / 修改部门对话框 -->
  <el-dialog v-model="dialogFormVisible" :title="formTitle" width="30%">
    <el-form :model="deptForm">
      <el-form-item label="部门名称" label-width="80px">
        <el-input v-model="deptForm.name" autocomplete="off" />
      </el-form-item>
    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取消</el-button>
        <el-button type="primary" @click="save">确定</el-button>
      </span>
    </template>
  </el-dialog>

</template>

<style scoped>

</style>


 
 
 

















 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 





















 
 
 
 
 
 
 
 
 
 
 
 
 
 
 






2). 在 src/api/dept.ts 中增加如下代码

//添加部门
export const addApi = (dept:DeptModel) => request.post<any, ResultModel>('/depts', dept)

目前 src/api/dept.ts 文件中完整代码如下:

import request from "@/utils/request"
import type { DeptModel, ResultModel } from "./model/model"

//列表查询
export const queryAllApi = () => request.get<any, ResultModel>('/depts')

//添加部门
export const addApi = (dept:DeptModel) => request.post<any, ResultModel>('/depts', dept)

打开浏览器进行测试,效果如下:

image-20231221123733634
image-20231221123733634
image-20231221123743792
image-20231221123743792

总结

课堂作业

  1. 按照上述的步骤,一步一步的完成以便,记得按照步骤哦!! 先写效果,在关注数据交互🎤

5.3 修改部门

修改部门

对于修改操作,通常会分为两步进行:

  1. 查询回显
  2. 保存修改
image-20231221141144901

交互逻辑:

  1. 点击 编辑 按钮,根据ID进行查询,弹出对话框,完成页面回显展示。(查询回显)
  2. 点击 确定 按钮,保存修改后的数据,完成数据更新操作。(保存修改)

代码操作

查询回显

1). 在 src/api/dept.ts 中定义根据id查询的请求

//根据ID查询
export const queryInfoApi = (id:number) => request.get(`/depts/${id}`)

2). 在 src/views/dept/index.vue 中添加根据ID查询回显的逻辑

为修改按钮绑定事件 <template></template>:

<el-button size="small" type="primary" @click="update(scope.row.id)">修改</el-button>

<script> </script> 添加JS逻辑:

//修改部门-查询回显
const update = async (id:number) => {
  formTitle.value = '修改部门'
  dialogFormVisible.value = true
  deptForm.value = {name: ''}

  const result = await queryInfoApi(id)
  deptForm.value = result.data
}

回显效果:image

到目前为止,完整的 src/views/dept/index.vue 代码如下:

阴影部分是发生修改的部分

<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray, DeptModel } from '@/api/model/model'
import {queryAllApi, addApi, queryInfoApi} from '@/api/dept'
import { ElMessage } from 'element-plus';

//声明列表展示数据
let tableData = ref<DeptModelArray>([])

//动态加载数据-查询部门
const queryAll = async () => {
  const result = await queryAllApi()
  tableData.value = result.data
}

//钩子函数
onMounted(() => {
  queryAll()
})


//新增部门
const dialogFormVisible = ref<boolean>(false) 
const deptForm = ref<DeptModel>({name: ''})
const formTitle = ref<string>('')

//点击新增按钮触发的函数
const add = () => {
  formTitle.value = '新增部门'
  dialogFormVisible.value = true
  deptForm.value = {name: ''}
}

//点击保存按钮-发送异步请求
const save = async () => {
  const result = await addApi(deptForm.value)
  if(result.code){
    ElMessage.success('操作成功')
  }else{
    ElMessage.error(result.msg)
  }
  dialogFormVisible.value = false
  queryAll()
}

//修改部门-查询回显
const update = async (id:number) => {
  formTitle.value = '修改部门'
  dialogFormVisible.value = true
  deptForm.value = {name: ''}

  const result = await queryInfoApi(id)
  deptForm.value = result.data
}

</script>

<template>
  <h1>部门管理</h1>
  <el-button type="primary" style="float: right" @click="add">+ 新增</el-button>
  <br><br>

  <!-- 部门数据表格 -->
  <el-table :data="tableData" border style="width: 100%">
    <el-table-column type="index" label="序号"  width="80"  align="center"/>
    <el-table-column prop="name" label="部门名称" width="250"  align="center"/>
    <el-table-column prop="updateTime" label="最后操作时间" width="300"  align="center"/>
    <el-table-column label="操作"  align="center">
      <template #default="scope">
        <el-button size="small" type="primary" @click="update(scope.row.id)">修改</el-button>
        <el-button size="small" type="danger"  @click="">删除</el-button>
      </template>
    </el-table-column>
  </el-table>

  <!-- 新增部门 / 修改部门对话框 -->
  <el-dialog v-model="dialogFormVisible" :title="formTitle" width="30%">
    <el-form :model="deptForm">
      <el-form-item label="部门名称" label-width="80px">
        <el-input v-model="deptForm.name" autocomplete="off" />
      </el-form-item>
    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取消</el-button>
        <el-button type="primary" @click="save">确定</el-button>
      </span>
    </template>
  </el-dialog>

</template>

<style scoped>

</style>



 









































 
 
 
 
 
 
 
 
 















 


























总结

课堂作业

  1. 根据上述的步骤提示完成修改操作🎤

5.4 删除部门

删除部门

image
image

代码操作

1). 在 src/api/dept.ts 中增加如下删除部门的请求

//删除部门
export const deleteApi = (id:number) => request.delete<any, ResultModel>(`/depts?id=${id}`)

2). 在 src/views/dept/index.vue 中为什么 删除 按钮绑定事件

<el-button size="small" type="danger"  @click="deleteById(scope.row.id)">删除</el-button>

3). 在 src/views/dept/index.vue 编写根据ID删除数据的函数

//删除部门
const deleteById =async (id:number) => {
  //弹出确认框
  ElMessageBox.confirm('您确认删除此部门吗? ', '确认删除').then( async () => {
    let result = await deleteApi(id)
    if(result.code){ //成功
      ElMessage.success('删除成功')
      queryAll()
    }else {
      ElMessage.error(result.msg)
    }
  }).catch(() => {
    ElMessage.info('取消删除')
  })
}

打开浏览器做一个测试:

image
image

至此dept/index.vue的完整代码:

点击查看完整代码
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray,DeptModel  } from '@/api/model/model'
import {queryAllApi,addApi,queryInfoApi,updateApi,deleteApi} from '@/api/dept'

import { ElMessage,ElMessageBox } from 'element-plus';

//声明列表展示数据
let tableData = ref<DeptModelArray>([])

//动态加载数据-查询部门
const queryAll = async () => {
  const result = await queryAllApi()
  tableData.value = result.data
}

//钩子函数
onMounted(() => {
  queryAll()
})

//新增部门
const dialogFormVisible = ref<boolean>(false) 
const deptForm = ref<DeptModel>({name: ''})
const formTitle = ref<string>('')

//点击新增按钮触发的函数
const add = () => {
  formTitle.value = '新增部门'
  dialogFormVisible.value = true
  deptForm.value = {name: ''}
}


//点击保存按钮-发送异步请求
const save = async () => {
  let result = null;
  if(deptForm.value.id){
    result = await updateApi(deptForm.value) //有id, 执行修改操作
  }else {
    result = await addApi(deptForm.value) //没有id, 执行新增操作
  }

  if(result.code){
    ElMessage.success('操作成功')
  }else{
    ElMessage.error(result.msg)
  }
  dialogFormVisible.value = false
  queryAll()
}

//修改部门-查询回显
const update = async (id:number) => {
  formTitle.value = '修改部门'
  dialogFormVisible.value = true
  deptForm.value = {name: ''}

  const result = await queryInfoApi(id)
  deptForm.value = result.data
}


//删除部门
const deleteById =async (id:number) => {
  //弹出确认框
  ElMessageBox.confirm('您确认删除此部门吗? ', '确认删除').then( async () => {
    let result = await deleteApi(id)
    if(result.code){ //成功
      ElMessage.success('删除成功')
      queryAll()
    }else {
      ElMessage.error(result.msg)
    }
  }).catch(() => {
    ElMessage.info('取消删除')
  })
}

</script>

<template>
  <h1>部门管理</h1>
  <el-button type="primary" style="float: right" @click="add">+ 新增</el-button>
  <br><br>

  <!-- 部门数据表格 -->
  <el-table :data="tableData" border style="width: 100%">
    <el-table-column type="index" label="序号"  width="80"  align="center"/>
    <el-table-column prop="name" label="部门名称" width="250"  align="center"/>
    <el-table-column prop="updateTime" label="最后操作时间" width="300"  align="center"/>
    <el-table-column label="操作"  align="center">
      <template #default="scope">
        <el-button size="small" type="primary" @click="update(scope.row.id)">修改</el-button>
        <el-button size="small" type="danger"  @click="deleteById(scope.row.id)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>


    <!-- 新增部门 / 修改部门对话框 -->
    <el-dialog v-model="dialogFormVisible" :title="formTitle" width="30%">
    <el-form :model="deptForm">
      <el-form-item label="部门名称" label-width="80px">
        <el-input v-model="deptForm.name" autocomplete="off" />
      </el-form-item>
    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取消</el-button>
        <el-button type="primary" @click="save">确定</el-button>
      </span>
    </template>
  </el-dialog>

</template>

<style scoped>

</style>

总结

课堂作业

  1. 根据删除的步骤,完成删除逻辑🎤

确保后端代码没有错误哦,如果前端出错了,请检查后端代码的日志信息

5.5 表单校验

表单校验

目前,我们已经基本完成了部门管理的增删改查操作。 接下来,我们对部门管理的功能进行,最后一块完善工作,增加表单校验。 从页面原型中,我们可以看到,新增部门的时候部门名称,不能为空,而且长度得在2-10之间。

image-20231221143448443

5.5.1 ElementPlus 参考

Form 组件允许你验证用户的输入是否符合规范,来帮助你找到和纠正错误。Form 组件提供了表单验证的功能,只需为 rules 属性传入约定的验证规则,并将 form-Itemprop 属性设置为需要验证的特殊键值即可。

image-20231221144745755
image-20231221144745755

代码操作

实现

1). 定义表单校验规则

//定义表单校验规则
const deptFormRef = ref<FormInstance>()
const rules = ref<FormRules<DeptModel>>({
  name: [
    { required: true, message: '部门名称不能为空', trigger: 'blur' },
    { min: 2, max: 10, message: '部门名称长度在2-10个字之间', trigger: 'blur' },
  ]
})

2). 将表单校验规则与表单绑定

为表单 <el-form> 绑定 rules 属性绑定表单校验规则 。 为每一个表单项,指定 prop 属性,设置为需要验证的属性名。

image-20231221160100967
image-20231221160100967

3). 表单提交时,校验表单,校验通过,则允许提交表单。

修改save方法的逻辑,需要加入表单校验的逻辑。

//点击保存按钮-发送异步请求
const save = async (form:FormInstance | undefined) => {
  if(!form) return;
  await form.validate(async (valid) => {
    if (valid) {
      let result = null;
      if(deptForm.value.id){
        result = await updateApi(deptForm.value)
      }else {
        result = await addApi(deptForm.value)
      }

      if(result.code){
        ElMessage.success('操作成功')
      }else{
        ElMessage.error(result.msg)
      }
      dialogFormVisible.value = false
      queryAll()
    }
  })
}

4). 重置表单校验结果

//重置表单校验结果
const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}

然后在点击 "新增" / "修改" 按钮的时候,调用 resetForm 函数,重置表单校验结果。

image-20231221160717585
image-20231221160717585