## Day17 Web前端实战(案例)：员工查询、 新增员工
---
* 员工管理-条件分页查询
* 员工管理-新增员工

### 1. 员工管理-条件分页查询
#### 需求分析

<figure><img src="/AiJavaWeb/imgs/jwai17-01.png"><figcaption>图1 需求分析</figcaption></figure>

#### 1.1 页面布局

<figure><img src="/AiJavaWeb/imgs/jwai17-02.png"><figcaption>图2 页面布局</figcaption></figure>

<figure><img src="/AiJavaWeb/imgs/jwai17-03.png"><figcaption>图3 请求参数和返回数据格式</figcaption></figure>

#### 页面布局-搜索表单

<figure><img src="/AiJavaWeb/imgs/jwai17-04.png"><figcaption>图4 搜素表单的数据对象--侦听机制</figcaption></figure>



#### watch侦听

* 作用：侦听一个或多个响应式<mark>数据源</mark>，并在数据源变化时调用传入的<mark>回调函数</mark>。
* 用法：
  * 导入watch函数
  * 执行watch函数，传入要侦听的响应式数据源（ref对象）和回调函数； 

#### watch-侦听1个响应式数据

```js
import { ref, watch } from 'vue'
const a = ref('')
watch(a, (newVal , oldVal) => { //侦听a的变化
  console.log(`a变化后的值：${newVal}，变化前的值：${oldVal}`);
}) 

```

#### watch-侦听1个响应式对象

* 侦听对象的全部属性：

```js
mport { ref, watch } from 'vue'
const user = ref({name:'', age:10})
watch(user, (newVal , oldVal) => {  //侦听user对象中的全部属性的变化
  console.log(`a的值为: newVal: ${newVal}, oldVal: ${oldVal}`);
}, {deep: true}) 

```

* ​	第三个可选参数，常见两个选项：
  * deep（boolean）：是否深度侦听，默认浅层侦听。
  * immediate（boolean）：是否在侦听创建时立即触发回调函数。

* 侦听对象的单个属性：

```js
import { ref, watch } from 'vue'
const user = ref({name:'', age:10})
watch( () => user.value.name , (newVal , oldVal) => {  //侦听user对象中name的变化
  console.log(`变化后的值：${newVal}，变化前的值：${oldVal}`);
}) 
```



#####  src\views\emp\index.vue


```vue
<script setup>
import {ref, watch} from "vue";

//搜索表单对象
const searchEmp = ref({name: "", gender: "", date: [], begin: "", end: ""})

//查询员工列表
const search = () => {
  console.log (searchEmp.value);
}
//清空
const clear = ()=> {  
  searchEmp.value = {name: "", gender: "", date: [], begin: "", end: ""};
  search();
}

//侦听searchEmp的date属性
watch(() => searchEmp.value.date, (newVal, oldVal)=> {
  if (newVal.length== 2){
    searchEmp.value.begin= newVal[0];
    searchEmp.value.end= newVal[1];
  } else {
    searchEmp.value.begin= "";
    searchEmp.value.end= "";
  }
})

// ----------- watch侦听演示 -------------
//1.侦听一个响应式数据
const a = ref('');
watch(a, (newVal,oldVal) => {
  console.log(`侦听一个响应式数据a：变化后的值：${newVal}，变化前的值：${oldVal}`);
})

//2.侦听一个对象（侦听对象的全部属性）
const user = ref({name: '张三',age: 10});
watch(user, (newVal, oldVal)=> {
  console.log (`侦听对象中的全部属性：变化后的姓名：${newVal.name},年龄：${newVal.age}`);
}, {deep: true})//深度侦听

//3.侦听对象中的某一个属性
watch(() => user.value.age, (newVal, oldVal) => {
  console.log(`侦听对象中的一个属性--年龄：变化后的值：${newVal}，变化前的值：${oldVal}`);
})

</script>

<template>
  <h1>员工管理</h1>
  <div class="container"> 
    {{  searchEmp }}
    <el-form :inline="true" :model="searchEmp" class="demo-form-inline">
      <el-form-item label="姓名">
      <el-input v-model="searchEmp.name" placeholder="请输入员工姓名"/>
      </el-form-item>
      <el-form-item label="性别">
        <el-select v-model="searchEmp.gender" placeholder="请选择">
          <el-option label="男" value="1"></el-option>
          <el-option label="女" value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="日期">
        <el-date-picker
          v-model="searchEmp.date"
          type="daterange"
          value-format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="search">查询</el-button>
        <el-button type="info" @click="clear">清空</el-button>
      </el-form-item>
    </el-form>
  </div>

  <div class="container"> 
    <h2>侦听测试(控制台查看输出)</h2>
    a：<input v-model="a" placeholder="请输入内容"/>
    姓名：<input v-model="user.name" placeholder="请输入内容"/>
    年龄：<input v-model="user.age" placeholder="请输入内容"/>
  </div>
  <div class="container"> 
    <el-button type="primary">添加员工</el-button>
    <el-button type="danger">批量删除</el-button>
  </div>

</template>

<style scoped>
  .container{
    margin: 20px 0;
  }
</style>
```

<figure><img src="/AiJavaWeb/imgs/jwai17-05.png"><figcaption>图5 <mark>watch侦听测试验证 </mark></figcaption></figure>

#### 小结

watch侦听函数的作用?

* 侦听一个或多个响应式数据源，并在数据源变化时调用所给的回调函数

watch函数调用时传入的参数?

* 参数一：侦听的源，可以是一个ref对象，一个函数等。
* 参数二：侦听的源发生变化时，触发的回调函数
* 参数三：可选，是一个对象(deep、immediate)

能否侦听对象的一个属性或全部属性？

* 可以
* 侦听一个属性：第一个参数传递一个函数 () => user.value.name
* 侦听全部属性：第三个参数传递一个对象，指定deep深度侦听

<figure><img src="/AiJavaWeb/imgs/jwai17-06.png"><figcaption>图6 watch侦听函数小结</figcaption></figure>

##### src\views\emp\index.vue

```vue
<script setup>
import {ref, watch} from "vue";

//搜索表单对象
const searchEmp = ref({name: "", gender: "", date: [], begin: "", end: ""})

//查询员工列表
const search = () => {
  console.log (searchEmp.value);
}
//清空
const clear = ()=> {  
  searchEmp.value = {name: "", gender: "", date: [], begin: "", end: ""};
  search();
}

//侦听searchEmp的date属性
watch(() => searchEmp.value.date, (newVal, oldVal)=> {
  if (newVal.length== 2){
    searchEmp.value.begin= newVal[0];
    searchEmp.value.end= newVal[1];
  } else {
    searchEmp.value.begin= "";
    searchEmp.value.end= "";
  }
})

// ----------- watch侦听演示 -------------
//1.侦听一个响应式数据
// const a = ref('');
// watch(a, (newVal,oldVal) => {
//   console.log(`侦听一个响应式数据a：变化后的值：${newVal}，变化前的值：${oldVal}`);
// })

//2.侦听一个对象（侦听对象的全部属性）
// const user = ref({name: '张三',age: 10});
// watch(user, (newVal, oldVal)=> {
//   console.log (`侦听对象中的全部属性：变化后的姓名：${newVal.name},年龄：${newVal.age}`);
// }, {deep: true})//深度侦听

//3.侦听对象中的某一个属性
// watch(() => user.value.age, (newVal, oldVal) => {
//   console.log(`侦听对象中的一个属性--年龄：变化后的值：${newVal}，变化前的值：${oldVal}`);
// })

//员工列表数据
const empList = ref([
    {
        "id": 1,
        "username": "jinyong",
        "name": "金庸",
        "gender": 1,
        "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg",
        "job": 2,
        "salary": 8000,
        "entryDate": "2015-01-01",
        "deptId": 2,
        "deptName": "教研部",
        "createTime": "2022-09-01T23:06:30",
        "updateTime": "2022-09-02T00:29:04"
      },
      {
        "id": 2,
        "username": "zhangwuji",
        "name": "张无忌",
        "gender": 1,
        "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg",
        "job": 2,
        "salary": 6000,
        "entryDate": "2015-01-01",
        "deptId": 2,
        "deptName": "教研部",
        "createTime": "2022-09-01T23:06:30",
        "updateTime": "2022-09-02T00:29:04"
      }
])
</script>

<template>
  <h1>员工管理</h1>
  <div class="container"> 
    <!-- {{  searchEmp }} -->
    <el-form :inline="true" :model="searchEmp" class="demo-form-inline">
      <el-form-item label="姓名">
      <el-input v-model="searchEmp.name" placeholder="请输入员工姓名"/>
      </el-form-item>
      <el-form-item label="性别">
        <el-select v-model="searchEmp.gender" placeholder="请选择">
          <el-option label="男" value="1"></el-option>
          <el-option label="女" value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="日期">
        <el-date-picker
          v-model="searchEmp.date"
          type="daterange"
          value-format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="search">查询</el-button>
        <el-button type="info" @click="clear">清空</el-button>
      </el-form-item>
    </el-form>
  </div>

<!--   <div class="container"> 
    <h2>侦听测试(控制台查看输出)</h2>
    a：<input v-model="a" placeholder="请输入内容"/>
    姓名：<input v-model="user.name" placeholder="请输入内容"/>
    年龄：<input v-model="user.age" placeholder="请输入内容"/>
  </div> -->

  <div class="container"> 
    <el-button type="primary">+ 添加员工</el-button>
    <el-button type="danger">- 批量删除</el-button>
  </div>
  <div class="container">
    <el-table :data="empList" border style="width: 100">
      <el-table-column type="selection" width="55" align="center"/>
      <el-table-column prop="name" label="姓名" width="120" align="center"/>
      <el-table-column prop="gender" label="性别" width="120" align="center">
        <template #default="scope">
          {{ scope.row.gender === 1 ? '男' : '女' }}
        </template>
      </el-table-column>
      <el-table-column prop="image" label="头像" width="120" align="center">
        <template #default="scope">
          <img :src="scope.row.image" height="40px" />
        </template>
      </el-table-column>
      <el-table-column prop="deptName" label="所属部门" width="120" align="center"/>
      <el-table-column prop="job" label="职位" width="120" align="center">
        <template #default="scope">
          <span v-if="scope.row.job==1">班主任</span>
          <span v-else-if="scope.row.job==2">讲师</span>
          <span v-else-if="scope.row.job==3">学工主管</span>
          <span v-else-if="scope.row.job==4">教研主管</span>
          <span v-else-if="scope.row.job==5">咨询师</span>
          <span v-else>其他</span>
        </template>
      </el-table-column>
      <el-table-column prop="entryDate" label="入职日期" width="180" align="center"/>
      <el-table-column prop="updateTime" label="最后操作时间"width="200" align="center"/>
      <el-table-column label="操作" align="center">
        <template #default="scope">
          <el-button type="primary" size ="small" @click=""><el-icon><EditPen/></el-icon>编辑</el-button>
          <el-button type="danger" size ="small" @click=""><el-icon><Delete/></el-icon> 删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<style scoped>
  .container{
    margin: 10px 0;
  }
</style>
```

<figure><img src="/AiJavaWeb/imgs/jwai17-07.png"><figcaption>图7 <mark>页面布局验证 </mark></figcaption></figure>



##### src\views\emp\index.vue 增加下列2段代码

```vue
<script setup>
  //分页相关
  const currentPage = ref(1);
  const pagesize = ref(5);
  const background = ref(true);
  const total = ref(10);
    
  //每页展示记录数变化
  const handleSizeChange = (val) => {
    console.log(`每页展示${val}条记录`);
  }

  //页码变化时触发
  const handleCurrentChange = (val) => {
    console.log(`当前页码：${val}`);
  }

</script>

  <!--分页条-->
  <div class="container">
    <el-pagination
      v-model:current-page="currentPage"
      v-model:page-size="pagesize"
      :page-sizes="[5,10,20,30,50,75,100]"
      :background="background"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total"
      @size-change="handlesizeChange"
      @current-change="handlecurrentchange"
    />
  </div>
```

<figure><img src="/AiJavaWeb/imgs/jwai17-08.png"><figcaption>图8 <mark>页面布局验证2</mark></figcaption></figure>



#### 1.2 页面交互

<figure><img src="/AiJavaWeb/imgs/jwai17-09.png"><figcaption>图9 页面交互需求</figcaption></figure>

* 页面加载完毕后，查询员工信息列表。
* 点击查询按钮，查询员工信息列表。
* 当页码、每页展示记录数发生变化时，查询员工信息列表。

##### src\api\emp.js

```js
import request from '@/utils/request'

//查询员工列表数据
export const queryPageApi = (name, gender, begin, end, page, pageSize) =>
  request.get(`/emps?name=${name}&gender=${gender}&begin=${begin}&end=${end}&page=${page}&pageSize=${pageSize}`)

//新增

//根据ID查询

//修改

//删除

```

##### src\views\emp\index.vue

```vue
<script setup>
  import {ref, watch, onMounted} from "vue";
  import {queryPageApi} from "@/api/emp";

  //搜索表单对象
  const searchEmp = ref({name: "", gender: "", date: [], begin: "", end: ""})

  //查询员工列表
  const search = async () => {
    //console.log (searchEmp.value);
    const result = await queryPageApi(searchEmp.value.name, searchEmp.value.gender,
            searchEmp.value.begin, searchEmp.value.end, currentPage.value, pageSize.value);
    if(result.code){ 
      empList.value = result.data.rows;
      total.value = result.data.total;
    } else {
      ElMessage.error(result.msg);  
    }
  }
  //清空
  const clear = ()=> {  
    searchEmp.value = {name: "", gender: "", date: [], begin: "", end: ""};
    search();
  }

  //侦听searchEmp的date属性
  watch(() => searchEmp.value.date, (newVal, oldVal)=> {
    if (newVal.length== 2){
      searchEmp.value.begin= newVal[0];
      searchEmp.value.end= newVal[1];
    } else {
      searchEmp.value.begin= "";
      searchEmp.value.end= "";
    }
  })

  //初始化查询
  onMounted(() => {
    search();
  })

// ----------- watch侦听演示 -------------
//1.侦听一个响应式数据
// const a = ref('');
// watch(a, (newVal,oldVal) => {
//   console.log(`侦听一个响应式数据a：变化后的值：${newVal}，变化前的值：${oldVal}`);
// })

//2.侦听一个对象（侦听对象的全部属性）
// const user = ref({name: '张三',age: 10});
// watch(user, (newVal, oldVal)=> {
//   console.log (`侦听对象中的全部属性：变化后的姓名：${newVal.name},年龄：${newVal.age}`);
// }, {deep: true})//深度侦听

//3.侦听对象中的某一个属性
// watch(() => user.value.age, (newVal, oldVal) => {
//   console.log(`侦听对象中的一个属性--年龄：变化后的值：${newVal}，变化前的值：${oldVal}`);
// })

  //员工列表数据
  const empList = ref([])

  //分页相关
  const currentPage = ref(1);
  const pageSize = ref(5);
  const background = ref(true);
  const total = ref(0);

  //每页展示记录数变化
  const handleSizeChange = (val) => {
    //console.log(`每页展示${val}条记录`);
    search();
  }

  //页码变化时触发
  const handleCurrentChange = (val) => {
    //console.log(`当前页码：${val}`);
    search();
  }

</script>

<template>
  <h1>员工管理</h1>
  <div class="container"> 
    <!-- {{  searchEmp }} -->
    <el-form :inline="true" :model="searchEmp" class="demo-form-inline">
      <el-form-item label="姓名">
      <el-input v-model="searchEmp.name" placeholder="请输入员工姓名"/>
      </el-form-item>
      <el-form-item label="性别">
        <el-select v-model="searchEmp.gender" placeholder="请选择">
          <el-option label="男" value="1"></el-option>
          <el-option label="女" value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="日期">
        <el-date-picker
          v-model="searchEmp.date"
          type="daterange"
          value-format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="search">查询</el-button>
        <el-button type="info" @click="clear">清空</el-button>
      </el-form-item>
    </el-form>
  </div>

<!--   <div class="container"> 
    <h2>侦听测试(控制台查看输出)</h2>
    a：<input v-model="a" placeholder="请输入内容"/>
    姓名：<input v-model="user.name" placeholder="请输入内容"/>
    年龄：<input v-model="user.age" placeholder="请输入内容"/>
  </div> -->

  <div class="container"> 
    <el-button type="primary">+ 添加员工</el-button>
    <el-button type="danger">- 批量删除</el-button>
  </div>
  <div class="container">
    <el-table :data="empList" border style="width: 100">
      <el-table-column type="selection" width="55" align="center"/>
      <el-table-column prop="name" label="姓名" width="120" align="center"/>
      <el-table-column prop="gender" label="性别" width="120" align="center">
        <template #default="scope">
          {{ scope.row.gender === 1 ? '男' : '女' }}
        </template>
      </el-table-column>
      <el-table-column prop="image" label="头像" width="120" align="center">
        <template #default="scope">
          <img :src="scope.row.image" height="40px" />
        </template>
      </el-table-column>
      <el-table-column prop="deptName" label="所属部门" width="120" align="center"/>
      <el-table-column prop="job" label="职位" width="120" align="center">
        <template #default="scope">
          <span v-if="scope.row.job==1">班主任</span>
          <span v-else-if="scope.row.job==2">讲师</span>
          <span v-else-if="scope.row.job==3">学工主管</span>
          <span v-else-if="scope.row.job==4">教研主管</span>
          <span v-else-if="scope.row.job==5">咨询师</span>
          <span v-else>其他</span>
        </template>
      </el-table-column>
      <el-table-column prop="entryDate" label="入职日期" width="180" align="center"/>
      <el-table-column prop="updateTime" label="最后操作时间"width="200" align="center"/>
      <el-table-column label="操作" align="center">
        <template #default="scope">
          <el-button type="primary" size ="small" @click=""><el-icon><EditPen/></el-icon>编辑</el-button>
          <el-button type="danger" size ="small" @click=""><el-icon><Delete/></el-icon> 删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>

  <!--分页条-->
  <div class="container">
    <!-- {{ currentPage }}: {{ pageSize }} -->
    <el-pagination
      v-model:current-page="currentPage"
      v-model:page-size="pageSize"
      :page-sizes="[5,10,20,30,50,75,100]"
      :background="background"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<style scoped>
  .container{
    margin: 10px 0;
  }
</style>

```



<figure><img src="/AiJavaWeb/imgs/jwai17-10.png"><figcaption>图10 <mark>员工分页显示验证</mark></figcaption></figure>




### 2. 员工管理-新增员工
#### 2.1 页面布局

<figure><img src="/AiJavaWeb/imgs/jwai17-11.png"><figcaption>图11 新增员工页面布局</figcaption></figure>

#### 表单项数据动态展示

* 性别（男/女）
* 职位（班主任/讲师/学工主管/教研主管/...）
* 所属部门（动态查询）

<figure><img src="/AiJavaWeb/imgs/jwai17-12.png"><figcaption>图12 表单项数据动态展示</figcaption></figure>

#####  src\views\emp\index.vue  添加或修改如下代码


```vue
<script setup>
  import {ElMessage} from "element-plus";
  import {queryAllApi as queryAllDeptApi} from "@/api/dept";

  //职位列表数据
  const jobs = ref([{ name: '班主任', value: 1 },{ name: '讲师', value: 2 },{ name: '学工主管', value: 3 },{ name: '教研主管', value: 4 },{ name: '咨询师', value: 5 },{ name: '其他', value: 6 }])
  //性别列表数据
  const genders = ref([{ name: '男', value: 1 }, { name: '女', value: 2 }])
  //部门列表数据
  const depts = ref([])

  //查询所有部门数据
  const queryAllDepts = async () => {
    const result = await queryAllDeptApi();
    if(result.code)
      depts.value = result.data;
  }
  
  //初始化查询   添加queryAllDepts();
  onMounted(() => {
    search();
    queryAllDepts();
  })

  //新增/修改表单
  const employee = ref({
    username: '',
    name: '',
    gender: '',
    phone: '',
    job: '',
    salary: '',
    deptId: '',
    entryDate: '',
    image: '',
    exprList: []
  })

  // 控制弹窗显示
  const dialogVisible = ref(false)
  const dialogTitle = ref('新增员工')

  // 添加员工
  const addEmp = () => {
    dialogVisible.value = true
    dialogTitle.value = '添加员工'
    employee.value = {
      username: '',
      name: '',
      gender: '',
      phone: '',
      job: '',
      salary: '',
      deptId: '',
      entryDate: '',
      image: '',
      exprList: []
    }
  }

  //文件上传
  // 图片上传成功后触发
  const handleAvatarSuccess = (response) => {
    //console.log(response);
    employee.value.image = response.data
  }
  // 文件上传之前触发
  const beforeAvatarUpload = (rawFile) => {
    if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
      ElMessage.error('只支持上传图片')
      return false
    } else if (rawFile.size / 1024 / 1024 > 10) {
      ElMessage.error('只能上传10M以内图片')
      return false
    }
    return true
  }

</script>

<template>
  <h1>员工管理</h1>

  <div class="container"> 
    <el-button type="primary" @click="addEmp">+ 添加员工</el-button>
    <el-button type="danger">- 批量删除</el-button>
  </div>

  <!-- 新增/修改员工的对话框 -->
  <el-dialog v-model="dialogVisible" :title="dialogTitle">
    <el-form :model="employee" label-width="80px">
      <!-- 基本信息 -->
      <!-- 第一行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="用户名">
            <el-input v-model="employee.username" placeholder="请输入员工用户名，2-20个字"></el-input>
          </el-form-item>
        </el-col>
        
        <el-col :span="12">
          <el-form-item label="姓名">
            <el-input v-model="employee.name" placeholder="请输入员工姓名，2-10个字"></el-input>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第二行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="性别">
            <el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
              <el-option v-for="item in genders" :label="item.name" :value="item.value" :key="item.value"></el-option>
            </el-select>
          </el-form-item>
        </el-col>

        <el-col :span="12">
          <el-form-item label="手机号">
            <el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第三行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="职位">
            <el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
              <el-option v-for="item in jobs" :label="item.name" :value="item.value" :key="item.value"></el-option>
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="薪资">
            <el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第四行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="所属部门">
            <el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
              <el-option v-for="item in depts" :label="item.name" :value="item.id" :key="item.id"></el-option> 
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="入职日期">
            <el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第五行 -->
      <el-row :gutter="20">
        <el-col :span="24">
          <el-form-item label="头像">
            <el-upload
              class="avatar-uploader"
              action="/api/upload"
              :show-file-list="false"
              :on-success="handleAvatarSuccess"
              :before-upload="beforeAvatarUpload"
              >
              <img v-if="employee.image" :src="employee.image" class="avatar" />
              <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
            </el-upload>
          </el-form-item>
        </el-col>
      </el-row>
      

      <!-- 工作经历 -->
      <!-- 第六行 -->
      <el-row :gutter="10">
        <el-col :span="24">
          <el-form-item label="工作经历">
            <el-button type="success" size="small" @click="">+ 添加工作经历</el-button>
          </el-form-item>
        </el-col>
      </el-row>
      
      <!-- 第七行 ...  工作经历 -->
      <el-row :gutter="3">
        <el-col :span="10">
          <el-form-item size="small" label="时间" label-width="80px">
            <el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item size="small" label="公司" label-width="60px">
            <el-input placeholder="请输入公司名称"></el-input>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item size="small" label="职位" label-width="60px">
            <el-input placeholder="请输入职位"></el-input>
          </el-form-item>
        </el-col>

        <el-col :span="2">
          <el-form-item size="small" label-width="0px">
            <el-button type="danger" >- 删除</el-button>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
      
    <!-- 底部按钮 -->
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="">保存</el-button>
      </span>
    </template>
  </el-dialog>

</template>

<style scoped>
  。。。
  .avatar {
    height: 40px;
  }
  .avatar-uploader .avatar {
    width: 78px;
    height: 78px;
    display: block;
  }
  .avatar-uploader .el-upload {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);
  }

  .avatar-uploader .el-upload:hover {
    border-color: var(--el-color-primary);
  }

  .el-icon.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 78px;
    height: 78px;
    text-align: center;
    border-radius: 10px;
    /* 添加灰色的虚线边框 */
    border: 1px dashed var(--el-border-color);
  }
</style>
```

<figure><img src="/AiJavaWeb/imgs/jwai17-13.png"><figcaption>图13 <mark>添加员工页面布局验证 </mark></figcaption></figure>

#### 2.2 页面交互

#### 工作经历

* 添加工作经历：下方就会增加一个工作经历信息
* 删除工作经历：删除当前这一个工作经历信息

<figure><img src="/AiJavaWeb/imgs/jwai17-14.png"><figcaption>图14 工作经历编程思路</figcaption></figure>

##### src\views\emp\index.vue  添加或修改如下代码

```vue
<script setup>
  ......

  //添加工作经历
  const addExprItem = () => {
    employee.value.exprList.push({company:'',job:"",begin:"", end:"",exprDate:[]})  
  }
  //删除工作经历
  const delExprItem = (index) => {
    employee.value.exprList.splice(index,1)
  }
  //侦听-employee员工对象中的工作经历信息
  watch(() => employee.value.exprList, (newval,oldval)=>{
    if(employee.value.exprList && employee.value.exprList.length > 0){
      employee.value.exprList.forEach((expr)=>{
        expr.begin=expr.exprDate[0];
        expr.end= expr.exprDate[1];
      })
    }
  },{deep:true})//深度侦听

</script>

<template>
  <h1>员工管理</h1>
  ...

  <!-- 新增/修改员工的对话框 -->
  <el-dialog v-model="dialogVisible" :title="dialogTitle">
    {{ employee }}
    <el-form :model="employee" label-width="80px">
      <!-- 基本信息 -->
      ....
      <!-- 工作经历 -->
      <!-- 第六行 -->
      <el-row :gutter="10">
        <el-col :span="24">
          <el-form-item label="工作经历">
            <el-button type="success" size="small" @click="addExprItem">+ 添加工作经历</el-button>
          </el-form-item>
        </el-col>
      </el-row>
      
      <!-- 第七行 ...  工作经历 -->
      <el-row :gutter="3" v-for="(expr, index) in employee.exprList" :key="index">
        <el-col :span="10">
          <el-form-item size="small" label="时间" label-width="80px">
            <el-date-picker type="daterange" v-model="expr.exprDate" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item size="small" label="公司" label-width="60px">
            <el-input placeholder="请输入公司名称" v-model="expr.company"></el-input>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item size="small" label="职位" label-width="60px">
            <el-input placeholder="请输入职位" v-model="expr.job"></el-input>
          </el-form-item>
        </el-col>

        <el-col :span="2">
          <el-form-item size="small" label-width="0px">
            <el-button type="danger" @click="delExprItem(index)">- 删除</el-button>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
      
    <!-- 底部按钮 -->
    ....
  </el-dialog>

</template>
```



<figure><img src="/AiJavaWeb/imgs/jwai17-15.png"><figcaption>图15 <mark>新增员工信息录入验证</mark></figcaption></figure>



#### 保存员工

* 点击保存之后，发送异步请求到服务端，提交数据。
* 保存完毕之后，如果成功，关闭对话框，重新加载列表数据。
* 保存完毕之后，如果失败，提示错误信息。

##### src\api\emp.js  添加下列代码

```js
//新增员工
export const addApi = (emp) => request.post('/emps',emp);
```

##### src\views\emp\index.vue  添加或修改如下代码

```vue
<script setup>
  。。。
  import {queryPageApi, addApi} from "@/api/emp";
  。。。

  //保存员工
  const save = async () => { 
    const result = await addApi(employee.value);
    if(result.code){//成功
      ElMessage.success('保存成功');
      dialogVisible.value = false;
      search();
    } else {
      ElMessage.error(result.msg);
    }
  }

</script>

去掉{{ employee }}
    <!-- 底部按钮 -->
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="save">保存</el-button>
      </span>
    </template>
```



<figure><img src="/AiJavaWeb/imgs/jwai17-16.png"><figcaption>图16 <mark>添加员工验证</mark></figcaption></figure>



#### 表单校验

表单校验规则：

* 用户名：必填，长度2-20
* 姓名：必填，长度2-10
* 性别：必填
* 手机号：必填，符合手机号规则

<figure><img src="/AiJavaWeb/imgs/jwai17-17.png"><figcaption>图17 表单校验规则</figcaption></figure>



##### src\views\emp\index.vue  添加或修改如下代码

```vue
<script>
  。。。
  // 添加员工弹窗-打开
  const addEmp = () => {
    dialogVisible.value = true
    dialogTitle.value = '添加员工'
    employee.value = {
      username: '',
      name: '',
      gender: '',
      phone: '',
      job: '',
      salary: '',
      deptId: '',
      entryDate: '',
      image: '',
      exprList: []
    }
    //重置表单的校验规则-提示信息
    if (empFormRef.value){
      empFormRef.value.resetFields();
    }
  }

    
  //表单引用
  const empFormRef = ref();

  //保存员工
  const save = async () => { 
    //表单校验
    if(!empFormRef.value) return;
    empFormRef.value.validate(async(valid)=>{//valid表示是否校验通过：true通过/false不通过
      if(valid){//通过
        const result = await addApi(employee.value);
        if(result.code){//成功
          ElMessage.success('保存成功');
          dialogVisible.value = false;
          search();
        } else {
          ElMessage.error(result.msg);
        }
      }else {//不通过
        ElMessage.error('表单校验未通过');
      }
    })
  }

  //表单校验规则
  const rules = ref({
    username: [
      { required: true, message: '请输入用户名', trigger: 'blur' },
      { min: 2, max: 20, message: '用户名长度应在2到20个字符之间', trigger: 'blur' }
    ],
    name: [
      { required: true, message: '请输入姓名', trigger: 'blur' },
      { min: 2, max: 10, message: '姓名长度应在2到10个字符之间', trigger: 'blur' }
    ],
    gender: [
      { required: true, message: '请选择性别', trigger: 'change' }
    ],
    phone: [
      { required: true, message: '请输入手机号', trigger: 'blur' },
      /**
       * 正则表达式：/..../; ^：以..开始; $：以...结束
       * [3-9]：范围3-9之间
       * \d：数字，[0-9]
       * *{9}： 量词
      */
      { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号', trigger: 'blur' }
    ]
  });
    
</script>

    <el-form :model="employee" label-width="80px" :rules="rules" ref="empFormRef">
      <!-- 基本信息 -->
      <!-- 第一行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="用户名" prop="username">
            <el-input v-model="employee.username" placeholder="请输入员工用户名，2-20个字"></el-input>
          </el-form-item>
        </el-col>
        
        <el-col :span="12">
          <el-form-item label="姓名" prop="name">
            <el-input v-model="employee.name" placeholder="请输入员工姓名，2-10个字"></el-input>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第二行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="性别" prop="gender">
            <el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
              <el-option v-for="item in genders" :label="item.name" :value="item.value" :key="item.value"></el-option>
            </el-select>
          </el-form-item>
        </el-col>

        <el-col :span="12">
          <el-form-item label="手机号" prop="phone">
            <el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第三行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="职位">
            <el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
              <el-option v-for="item in jobs" :label="item.name" :value="item.value" :key="item.value"></el-option>
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="薪资">
            <el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第四行 -->
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="所属部门">
            <el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
              <el-option v-for="item in depts" :label="item.name" :value="item.id" :key="item.id"></el-option> 
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="入职日期">
            <el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第五行 -->
      <el-row :gutter="20">
        <el-col :span="24">
          <el-form-item label="头像">
            <el-upload
              class="avatar-uploader"
              action="/api/upload"
              :show-file-list="false"
              :on-success="handleAvatarSuccess"
              :before-upload="beforeAvatarUpload"
              >
              <img v-if="employee.image" :src="employee.image" class="avatar" />
              <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
            </el-upload>
          </el-form-item>
        </el-col>
      </el-row>
      

      <!-- 工作经历 -->
      <!-- 第六行 -->
      <el-row :gutter="10">
        <el-col :span="24">
          <el-form-item label="工作经历">
            <el-button type="success" size="small" @click="addExprItem">+ 添加工作经历</el-button>
          </el-form-item>
        </el-col>
      </el-row>
      
      <!-- 第七行 ...  工作经历 -->
      <el-row :gutter="3" v-for="(expr, index) in employee.exprList" :key="index">
        <el-col :span="10">
          <el-form-item size="small" label="时间" label-width="80px">
            <el-date-picker type="daterange" v-model="expr.exprDate" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item size="small" label="公司" label-width="60px">
            <el-input placeholder="请输入公司名称" v-model="expr.company"></el-input>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item size="small" label="职位" label-width="60px">
            <el-input placeholder="请输入职位" v-model="expr.job"></el-input>
          </el-form-item>
        </el-col>

        <el-col :span="2">
          <el-form-item size="small" label-width="0px">
            <el-button type="danger" @click="delExprItem(index)">- 删除</el-button>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>

```



<figure><img src="/AiJavaWeb/imgs/jwai17-18.png"><figcaption>图18 <mark>新增员工表单校验验证 </mark></figcaption></figure>







