## Day16 Web前端实战(案例)：整体布局、 部门管理

---

##### 目录
* 开发模式
* 整体布局
* 部门管理
  

#### Tlias智能学习辅助系统
* 开发模式
* 整体布局
* 部门管理
* 员工管理
* 登录/退出
* 班级管理(自己实现)
* 学员管理(自己实现)



<figure><img src="/AiJavaWeb/imgs/jwai16-01.png"><figcaption>图1 Tlias智能学习辅助系统开发步骤</figcaption></figure>



### 1. 开发模式

#### 前后端分离开发


* 当前最为主流的开发模式：前后端分离

<figure><img src="/AiJavaWeb/imgs/jwai16-02.png"><figcaption>图2 前后端分离开发流程</figcaption></figure>

#### 小结

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

* 前后端开发人员独立开发，独立部署

需求的开发流程?

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

<figure><img src="/AiJavaWeb/imgs/jwai16-03.png"><figcaption>图3 小结</figcaption></figure>



### 2. 整体布局

* 准备工作
* 动态菜单

<figure><img src="/AiJavaWeb/imgs/jwai16-04.png"><figcaption>图4 整体布局</figcaption></figure>

#### 2.1 准备工作

* 导入资料中准备的基础工程到VsCode   如将vue-tlias-management.zip复制到G:\workspace\duSSM\vue（个人vue工作目录）下解压
* 启动前端项目，访问该项目

##### ①  index.html  单页面入口文件

 绝对路径G:\workspace\duSSM\vue\vue-tlias-management\index.html，修改其中的title内容

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tlias智能学习辅助系统(杜老师)</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
```

##### ② src\main.js  js入口文件

```javascript
// 引入Vue、根组件、vue路由
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

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

// 引入样式
import './assets/main.css'

// 创建Vue实例并使用路由、ElementPlus、图标、并挂载到#app
const app = createApp(App)
app.use(router)
app.use(ElementPlus, {locale: zhCn})
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.mount('#app')
```

##### ③ src\App.vue 根组件

```vue
<script setup>
//引入views/layout/index.vue命名为Layout
import Layout from "@/views/layout/index.vue";
</script>

<template>
  <Layout></Layout>
</template>

<style scoped>

</style>
```

##### ④ src\views\layout\index.vue  布局组件

修改el-header内容

```vue
<script setup>

</script>

<template>
  <div class="common-layout">
    <el-container>
      <!-- Header 区域 -->
      <el-header class="header">
        <span class="title">Tlias智能学习辅助系统(杜老师)</span>
        <span class="right_tool">
          <a href="">
            <el-icon><EditPen /></el-icon> 修改密码 &nbsp;&nbsp;&nbsp; |  &nbsp;&nbsp;&nbsp;
          </a>
          <a href="">
            <el-icon><SwitchButton /></el-icon> 退出登录
          </a>
        </span>
      </el-header>
      
      <el-container>
        <!-- 左侧菜单 -->
        <el-aside width="200px" class="aside">
          左侧菜单栏
        </el-aside>
        
        <el-main>
          右侧核心展示区域
        </el-main>
      </el-container>
      <el-footer style="text-align: center; height: 60px;display: flex; align-items: center; justify-content: center;" class="footer">
        <span>
          &copy; 2025 杜老师工作室 版权所有
        </span>
      </el-footer>
    </el-container>
  </div>
</template>

<style scoped>
.header, .footer{
  background-image: linear-gradient(to right, #00547d, #007fa4, #00aaa0, #00d072, #a8eb12);
}

.title {
  color: white;
  font-size: 40px;
  font-family: 楷体;
  line-height: 60px;
  font-weight: bolder;
}

.right_tool{
  float: right;
  line-height: 60px;
}

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

.aside {
  width: 220px;
  border-right: 1px solid #ccc;
  height: 800px;
}
</style>
```

##### ⑤ src\views\login\index.vue   登录组件

修改``` <p class="title">```内容

```vue
<script setup>
  import { ref } from 'vue'
  
  let loginForm = ref({username:'', password:''})
  
</script>

<template>
  <div id="container">
    <div class="login-form">
      <el-form label-width="80px">
        <p class="title">Tlias智能学习辅助系统(杜老师)</p>
        <el-form-item label="用户名" prop="username">
          <el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input>
        </el-form-item>

        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="loginForm.password" placeholder="请输入密码"></el-input>
        </el-form-item>

        <el-form-item>
          <el-button class="button" type="primary" @click="">登 录</el-button>
          <el-button class="button" type="info" @click="">重 置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<style scoped>
#container {
  padding: 10%;
  height: 410px;
  background-image: url('../../assets/bg1.jpg');
  background-repeat: no-repeat;
  background-size: cover;
}

.login-form {
  max-width: 400px;
  padding: 30px;
  margin: 0 auto;
  border: 1px solid #e0e0e0;
  border-radius: 10px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  background-color: white;
}

.title {
  font-size: 30px;
  font-family: '楷体';
  text-align: center;
  margin-bottom: 30px;
  font-weight: bold;
}

.button {
  margin-top: 30px;
  width: 120px;
}
</style>
```

##### ⑥ src\router\index.js

```javascript
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    // {
    //   path: '/',
    //   name: 'home',
    //   component: HomeView
    // },
    // {
    //   path: '/about',
    //   name: 'about',
    //   component: () => import('../views/AboutView.vue')
    // }
  ]
})

export default router
```

##### ⑦ 其他组件

src\views\clazz\index.vue，其他组件初始代码类同

```vue
<script setup>

</script>

<template>
  班级管理
</template>

<style scoped>

</style>
```

| 组件                  | 组件相对路径                   | template内容                             |
| --------------------- | ------------------------------ | ---------------------------------------- |
| 首页组件              | src\views\index\index.vue      | ```<img src="../../assets/index.png">``` |
| 班级管理组件          | src\views\clazz\index.vue      | 班级管理                                 |
| 学员管理组件          | src\views\stu\index.vue        | 学员管理                                 |
| 部门管理组件          | src\views\dept\index.vue       | 部门管理                                 |
| 员工管理组件          | src\views\emp\index.vue        | 员工管理                                 |
| 学员信息统计组件      | src\views\report\stu\index.vue | 学员信息统计                             |
| 员工信息统计组件      | src\views\report\emp\index.vue | 员工信息统计                             |
| 日志管理组件          | src\views\log\index.vue        | 日志管理                                 |
|                       |                                |                                          |
| <mark>布局组件</mark> | src\views\layout\index.vue     |                                          |
| <mark>登录组件</mark> | src\views\login\index.vue      |                                          |

<figure><img src="/AiJavaWeb/imgs/jwai16-05.png"><figcaption>图5 源代码目录</figcaption></figure>

#### Container 布局容器

`<el-container>`：外层容器。 

当子元素中包含 `<el-header>` 或 `<el-footer>` 时，全部子元素会<mark>垂直上下排列</mark>， 否则<mark>会水平左右排列</mark>。

`<el-header>`：顶栏容器。

`<el-aside>`：侧边栏容器。

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

`<el-footer>`：底栏容器。

#####  src\views\layout\index.vue  布局组件 主要布局如下


```vue
    <el-container>
      <!-- 上下垂直排列 -->
      <el-header> 顶部标题 </el-header>
      <el-container>
         <!-- 左右水平排列 -->
         <el-aside> 左侧菜单栏 </el-aside>
         <el-main> 右侧核心展示区域 </el-main>
      </el-container>
    </el-container
```


<figure><img src="/AiJavaWeb/imgs/jwai16-06.png"><figcaption>图6 <mark>页面布局验证 </mark></figcaption></figure>

#### 小结

<figure><img src="/AiJavaWeb/imgs/jwai16-07.png"><figcaption>图7 页面布局小结</figcaption></figure>

#### ② 动态菜单

#### 页面布局-左侧菜单

##### src\views\layout\index.vue  左侧菜单栏 替换 下列菜单定义

```vue
<!-- 左侧菜单栏 -->
<el-menu>
  <!-- 首页菜单 -->
  <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>
```

<figure><img src="/AiJavaWeb/imgs/jwai16-08.png"><figcaption>图8 <mark>左侧菜单栏验证 </mark></figcaption></figure>



##### Vue Router

* Vue Router：Vue的官方<mark>路由</mark>。为Vue提供富有表现力、可配置的、方便的<mark>路由</mark>。(https://router.vuejs.org/zh)
* 客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时，URL 会随之更新，但页面不需要从服务器重新加载。
* Vue中的路由：<mark>路径</mark> 与 <mark>组件</mark> 的对应关系。
* 组成：
  * Router实例：路由实例，基于createRouter函数创建，维护了应用的路由信息。
  * ```<router-link>```：路由链接组件，浏览器会解析成<mark>超链接`<a>`</mark>。
  * ```<router-view>```：动态视图组件，用来渲染展示与路由路径对应的组件。

<figure><img src="/AiJavaWeb/imgs/jwai16-09.png"><figcaption>图9 Vue Router三个组成</figcaption></figure>

##### src\views\layout\index.vue  左侧菜单栏 替换 下列菜单定义


```vue
<!-- 左侧菜单栏 -->
<el-menu router>
  <!-- 首页菜单 -->

```

* ```<el-menu router>```：是否启用vue-router模式。启用该模式会在激活导航时以index作为path进行路由跳转

##### src\views\layout\index.vue  修改以下2处

```vue
<el-menu router>
    
<el-main>
    <!-- 右侧核心展示区域 -->
    <router-view></router-view>
</el-main>
```



```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: 'login', component: LoginView}
  ]
})

export default router
```



<figure><img src="/AiJavaWeb/imgs/jwai16-10.png"><figcaption>图10 <mark>Vue Router验证1</mark></figcaption></figure>

#### 小结

Vue的路由指的是什么 ?

* Vue Router是Vue官方的路由，描述的是路径与组件的对应关系

Vue Router中的三个核心组成部分 ?

* router实例，维护了应用的路由信息（routes）
* ```<router-link>```：路由链接组件
* ```<router-view>```：动态视图组件

<figure><img src="/AiJavaWeb/imgs/jwai16-11.png"><figcaption>图11 Vue的路由小结</figcaption></figure>



#### 案例路由实现

<figure><img src="/AiJavaWeb/imgs/jwai16-12.png"><figcaption>图12 嵌套路由</figcaption></figure>

##### src\App.vue

```vue
<script setup>
//引入views/layout/index.vue命名为Layout
//import Layout from "@/views/layout/index.vue";
</script>

<template>
  <!-- <Layout></Layout> -->
  <router-view></router-view>
</template>

<style scoped>

</style>

```

##### 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
```



<figure><img src="/AiJavaWeb/imgs/jwai16-13.png"><figcaption>图13 <mark>Vue Router验证2</mark></figcaption></figure>



<figure><img src="/AiJavaWeb/imgs/jwai16-14.png"><figcaption>图14 嵌套路由</figcaption></figure>



### 3. 部门管理

* 列表查询
* 新增部门
* 修改部门
* 删除部门

#### ① 列表查询

#### 列表查询-页面布局

* 根据页面原型、需求说明、接口文档，先完成页面的基本布局。
  * Button按钮组件
  * Table表格组件

<figure><img src="/AiJavaWeb/imgs/jwai16-15.png"><figcaption>图15 部门管理基本布局</figcaption></figure>

##### src\views\dept\index.vue

```vue
<script setup>
  import { ref, onMounted } from 'vue'
  // 声明列表展示数据
  let 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 type="index" label="序号" width="100" align="center"/>
      <el-table-column prop="name" label="部门名称" width="300" align="center"/>
      <el-table-column prop="updateTime" label="最后修改时间" width="300" align="center"/>
      <el-table-column fixed="right" label="操作" align="center">
        <template #default="{ row }">
          <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 0;
}
</style>

```



<figure><img src="/AiJavaWeb/imgs/jwai16-16.png"><figcaption>图16 <mark>部门管理基本布局验证 </mark></figcaption></figure>

* 根据需求，需要在打开页面之后，需要自动加载全部部门数据，展示在表格中。

* 前后端分离开发模式中，前后端是并行开发的，前端如何进行接口请求测试？
  * Mock.js来生成测试的假数据
  * 基于Apifox提供的Mock服务进行测试

##### src\views\dept\index.vue

```vue
<script setup>
  import { ref, onMounted } from 'vue'
  import axios from 'axios';

  //查询
  const search = async () => {
    const result = await axios.get('https://apifoxmock.com/m1/3128855-1224313-default/depts');
    if(result.data.code)//Js隐式类型转换 0- false，其他数字- true; '' false，其他都是true；null，undefined--false
      deptList.value = result.data.data;
  }

  onMounted(()=>{
    search();
  })
  // 声明列表展示数据
  let 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 type="index" label="序号" width="100" align="center"/>
      <el-table-column prop="name" label="部门名称" width="300" align="center"/>
      <el-table-column prop="updateTime" label="最后修改时间" width="300" align="center"/>
      <el-table-column fixed="right" label="操作" align="center">
        <template #default="{ row }">
          <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 0;
}
</style>

```

<figure><img src="/AiJavaWeb/imgs/jwai16-17.png"><figcaption>图17 <mark>自动加载全部部门数据验证 </mark></figcaption></figure>

#### 小结

动态加载部门列表数据 

* 钩子函数 onMounted
* Axios发送异步请求



#### 列表查询-程序优化

<figure><img src="/AiJavaWeb/imgs/jwai16-18.png"><figcaption>图18 查询优化问题</figcaption></figure>

* 为解决上述问题，我们在前端项目开发时，通常会定义一个请求处理的工具类 – request.js 。
* 与服务端进行异步交互的逻辑，通常会封装到一个单独的api中，如：dept.js 。

<figure><img src="/AiJavaWeb/imgs/jwai16-19.png"><figcaption>图19 列表查询-程序优化</figcaption></figure>

##### src\utils\request.js

```javascript
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '../router'

//创建axios实例对象
const request = axios.create({
  baseURL: 'https://apifoxmock.com/m1/3128855-1224313-default/', //'/api',
  timeout: 600000
})

//axios的响应 response 拦截器
request.interceptors.response.use(
  (response) => { //成功回调
    return response.data
  },
  (error) => { //失败回调
    return Promise.reject(error)
  }
)

export default request
```

##### src\api\dept.js

```js
import request from '@/utils/request'
//部门列表查询
export const queryAllApi = () => request.get('/depts');

//部门添加

//部门删除

//部门详情查询

//部门修改

```

##### src\views\dept\index.vue

```vue
<script setup>
  import { ref, onMounted } from 'vue'
  //import axios from 'axios';
  import { queryAllApi } from '@/api/dept'

  //查询
  const search = async () => {
    //const result = await axios.get('https://apifoxmock.com/m1/3128855-1224313-default/depts');
    //if(result.data.code)
    //Js隐式类型转换 0- false，其他数字- true; '' false，其他都是true；null，undefined--false
    //deptList.value = result.data.data; */
    const result = await queryAllApi()
    deptList.value = result.data
  }
  //钩子函数
  onMounted(()=>{
    search();
  })
  // 声明列表展示数据
  let 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 type="index" label="序号" width="100" align="center"/>
      <el-table-column prop="name" label="部门名称" width="300" align="center"/>
      <el-table-column prop="updateTime" label="最后修改时间" width="300" align="center"/>
      <el-table-column fixed="right" label="操作" align="center">
        <template #default="{ row }">
          <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 0;
}
</style>
```



<figure><img src="/AiJavaWeb/imgs/jwai16-20.png"><figcaption>图20 配置前端代理服务器</figcaption></figure>

##### vite.config.js  添加代理服务器配置

```js
 server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        secure: false,
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
```

##### src\utils\request.js  修改baseURL

```js
  baseURL: '/api',
```

启动后台服务器(停止令牌检验过滤器、拦截器)后，前端测试

<figure><img src="/AiJavaWeb/imgs/jwai16-21.png"><figcaption>图21  <mark>启动后台服务器、前端代理服务器验证 </mark></figcaption></figure>

#### 小结

请求处理工具类

* request.js：封装axios异步请求的基本信息(拦截器)
* api/xxx.js: 封装模块异步交互的代码
* vite.config.js: 配置代理规则



#### ② 新增部门

#### 新增部门

* 根据页面原型、需求说明、接口文档，先完成页面的基本布局。
  * 新增部门 和 编辑部门可以共用一个Dialog对话框。

<figure><img src="/AiJavaWeb/imgs/jwai16-22.png"><figcaption>图22 新增和修改部门对话框</figcaption></figure>

##### src\views\dept\index.vue  添加或修改如下代码

```vue
<script setup>
  import { queryAllApi, addApi } from '@/api/dept'
  import { ElMessage } from 'element-plus'
    
  // 打开新增部门对话框
  const addDept = () => {
    dialogFormVisible.value = true;
    formTitle.value = '新增部门';
    dept.value = { name: '' };
  }
  
  // 保存新增部门
  const save = async () => {
    const result = await addApi(dept.value);
    if(result.code){
      //提示信息
       ElMessage.success('新增成功');
      //关闭对话框
      dialogFormVisible.value = false;
      //查询
      search();
    } else {
      ElMessage.error(result.msg);
    }
  }
</script>

    <el-button type="primary" @click="addDept"> + 新增部门</el-button>


  <!--  Dialog对话框 -->
  <el-dialog v-model="dialogFormVisible" :title="formTitle" width="500">
    <el-form :model="dept">
    <el-form-item label="部门名称"label-width="80px">
      <el-input v-model="dept.name" placeholder="请输入部门名称，长度为2-10位" />
    </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="dialogFormVisible = false" >取消</el-button>
        <el-button type="primary" @click="save">确定</el-button>
      </div>
    </template>
  </el-dialog>
```



##### src\api\dept.js 添加下列代码

```js
//部门添加
export const addApi = (dept) => request.post('/depts',dept);
```



#### 新增部门-表单校验

* 在ElementPlus提供的form表单组件中，就提供了对应的表单校验的功能。 具体步骤如下：
  * 定义表单校验规则，通过rules属性与表单绑定
  * 通过prop属性绑定对应的表单项。
  * 通过ref属性注册表单的引用，表单提交时，校验表单，校验通过，则允许提交表单。

<figure><img src="/AiJavaWeb/imgs/jwai16-23.png"><figcaption>图23 新增部门-表单校验</figcaption></figure>

##### src\views\dept\index.vue  添加或修改如下代码

```vue
  <!--  Dialog对话框 -->
  <el-dialog v-model="dialogFormVisible" :title="formTitle" width="500">
    <el-form :model="dept" :rules="rules" ref="deptFormRef">
    <el-form-item label="部门名称"label-width="80px" prop="name">
      <el-input v-model="dept.name" placeholder="请输入部门名称，长度为2-10位" />
    </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="dialogFormVisible = false" >取消</el-button>
        <el-button type="primary" @click="save">确定</el-button>
      </div>
    </template>
  </el-dialog>

<script>
  // 打开新增部门对话框
  const addDept = () => {
    ...
    //重置表单的校验规则-提示信息
    if (deptFormRef.value){
      deptFormRef.value.resetFields();
    }
  }
    
  //表单校验
  const deptFormRef = ref()
  const rules = ref({
    name: [
      { required: true, message: '部门名称是必填项', trigger: 'blur' },
      { min: 2, max: 10, message: '部门名称的长度应该在2-10位', trigger: 'blur' }
    ]
  })

  // 保存新增部门
  const save = async () => {
    //表单校验
    if(!deptFormRef.value) return;
    deptFormRef.value.validate(async(valid)=>{//valid表示是否校验通过：true通过/false不通过
      if(valid){//通过
        const result = await addApi(dept.value);
        if(result.code){
          //提示信息
          ElMessage.success('新增成功');
          //关闭对话框
          dialogFormVisible.value = false;
          //查询
          search();
        } else {
          ElMessage.error(result.msg);
        }
      }else {//不通过
        //提示信息
        ElMessage.error('表单校验未通过');
      }
    })
  }
</script>
```



<figure><img src="/AiJavaWeb/imgs/jwai16-24.png"><figcaption>图24 <mark>新增部门验证</mark></figcaption></figure>

#### ③ 修改部门

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

<figure><img src="/AiJavaWeb/imgs/jwai16-25.png"><figcaption>图25 </figcaption></figure>

* 交互逻辑：
  * 点击 编辑 按钮，根据ID进行查询，弹出对话框，完成页面回显展示。（查询回显）
  * 点击 确定 按钮，保存修改后的数据，完成数据更新操作。（保存修改）

#### 查询回显

##### src\api\dept.js 添加下列代码

```js
//根部门id查询部门详情
export const queryByIdApi = (id) => request.get('/depts/'+id);
```

##### src\views\dept\index.vue  添加或修改如下代码

```vue
<script setup>
  import { queryAllApi, addApi, queryByIdApi } from '@/api/dept'
    
  //打开编辑部门对话框 回显数据
  const editDept = async (id) => {
    dialogFormVisible.value = true;
    formTitle.value='修改部门';
    //重置表单的校验规则-提示信息
    if (deptFormRef.value){
      deptFormRef.value.resetFields();
    }
    const result = await queryByIdApi(id);
    if(result.code){
      dept.value = result.data;
    }
  }
</script>

        <template #default="scope">
          <el-button type="primary" size="small" @click="editDept(scope.row.id)"><el-icon><EditPen /></el-icon>修改</el-button>
          <el-button type="danger" size="small" ><el-icon><DeleteFilled /></el-icon>删除</el-button>
        </template>
```


<figure><img src="/AiJavaWeb/imgs/jwai16-26.png"><figcaption>图26 <mark>修改部门数据回显验证</mark></figcaption></figure>

##### src\api\dept.js 添加下列代码

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

##### src\views\dept\index.vue  修改如下js代码

```js
  import { queryAllApi, addApi, queryByIdApi, updateApi } from '@/api/dept'


  // 保存新增、修改部门
  const save = async () => {
    //表单校验
    if(!deptFormRef.value) return;
    deptFormRef.value.validate(async(valid)=>{//valid表示是否校验通过：true通过/false不通过
      if(valid){//通过
        //保存新增、修改部门
        let result;
        if(dept.value.id){//修改
          result = await updateApi(dept.value);
        } else { //新增
          result = await addApi(dept.value);
        }
        if(result.code){
          //提示信息
          ElMessage.success(`${formTitle.value}成功`);
          //关闭对话框
          dialogFormVisible.value = false;
          //查询
          search();
        } else { //失败
          ElMessage.error(result.msg);
        }
      }else {//不通过
        //提示信息
        ElMessage.error('表单校验未通过');
      }
    })
  }
```



<figure><img src="/AiJavaWeb/imgs/jwai16-27.png"><figcaption>图27 <mark>修改部门保存数据验证</mark></figcaption></figure>



#### ④ 删除部门

<figure><img src="/AiJavaWeb/imgs/jwai16-28.png"><figcaption>图28 删除部门需求</figcaption></figure>

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

##### src\api\dept.js 添加下列代码

```js
//根部门id删除部门
export const deleteByIdApi = (id) => request.delete(`/depts?id=${id}`);
```

##### src\views\dept\index.vue  修改如下代码

```vue
<script>
  import { queryAllApi, addApi, queryByIdApi, updateApi, deleteByIdApi } from '@/api/dept'
  import { ElMessage, ElMessageBox } from 'element-plus'

   //删除
  const delById = async (id,name) => {
    //弹出确认框
    ElMessageBox.confirm('您确认删除【' + name + '】部门吗？','提示',
      {confirmButtonText:'确认',cancelButtonText:'取消',type:'warning'}
    ).then(async () =>  {//确认
      const result = await deleteByIdApi(id);
      if(result.code){
        ElMessage.success('删除成功')
        search();
      }else{
        ElMessage.error(result.msg)
    }).catch(() =>  {//取消
      ElMessage.info('您已取消删除')
    })
  }
</script>
          <el-button type="primary" size="small" @click="editDept(scope.row.id)"><el-icon><EditPen /></el-icon>修改</el-button>
          <el-button type="danger" size="small" @click="delById(scope.row.id,scope.row.name)"><el-icon><DeleteFilled /></el-icon>删除</el-button>

```



<figure><img src="/AiJavaWeb/imgs/jwai16-29.png"><figcaption>图29 <mark>删除部门验证</mark></figcaption></figure>



