页面结构
树形组件
<el-tree :data="depts" :props="defaultProps" :default-expand-all="true" />
记录节点
设置变量以存储查询参数
// 存储查询参数
queryParams: {
departmentId: null
}
找到节点——>记录节点——>调用setCurrentKey方法选中节点
初始化时,找到首个节点记录并且选中(因为我们设置完树形之后立刻选中首个节点,此时更新还没有完成,必须等待更新完成后,再去选中首个节点,所以需要使用$nextTick)
methods: {
async getDepartment() {
// 调用封装好的转换树的方法
this.depts = transListToTreeData(await getDepartment(), 0)
this.queryParams.departmentId = this.depts[0].id
this.$nextTick(() => {
// 此时意味着树渲染完毕
//选中
this.$refs.deptTree.setCurrentKey(this.queryParams.departmentId)
}
)
}
}
}
选中时,记录节点
<el-tree
ref="deptTree"
:data="depts"
:props="defaultProps"
:default-expand-all="true"
node-key="id"
@current-change="selectNode"
/>
// 记录节点
selectNode(node) {
this.queryParams.departmentId = node.id
}
员工列表结构:
表格样式
<!-- 表格组件 -->
<el-table>
<el-table-column alien="center" label="头像" />
<el-table-column label="姓名" />
<el-table-column label="手机号" sortable />
<el-table-column label="工号" sortable />
<el-table-column label="聘用形势" />
<el-table-column label="入职时间" sotrable />
<el-table-column label="操作" width="280px">
<template>
<button size="mini" type="text">查看</button>
<button size="mini" type="text">角色</button>
<button size="mini" type="text">删除</button>
</template>
</el-table-column>
</el-table>
分页模式
<!-- 分页 -->
<el-row type="flex" style="height: 60px;" align="middle" justify="end">
<el-pagination layout="total,prev, pager, next" :total="1000" />
</el-row>
数据渲染
1.首次加载
1.1 封装api,封装获取数据的方法,list已经提取定义好
// 获取员工列表的方法
async getEmployeeList() {
const { rows } = await getEmployeeList(this.queryParams)
this.list = rows
}
}
1.2给表格绑定数据
<el-table :data="list">
1.3给每一列指定数据
<el-table-column alien="center" prop="staffPhoto" label="头像" />
<el-table-column label="姓名" paop="username" />
1.4 调用方法渲染
async getDepartment() {
// 调用封装好的转换树的方法
this.depts = transListToTreeData(await getDepartment(), 0)
this.queryParams.departmentId = this.depts[0].id
// 设置选中节点
// 树组件渲染是异步的 等到更新完毕
this.$nextTick(() => {
// 此时意味着树渲染完毕
this.$refs.deptTree.setCurrentKey(this.queryParams.departmentId)
}
)
// 这个时候参数 记录了id
// 调用了获取员工列表的方法
this.getEmployeeList()
},
2.切换节点重新渲染
2.1 节点id更新之后又重新调方法即可
this.$nextTick(() => {
// 此时意味着树渲染完毕
this.$refs.deptTree.setCurrentKey(this.queryParams.departmentId)
}
)
// 这个时候参数 记录了id
// 调用了获取员工列表的方法
this.getEmployeeList()
},
// 记录节点
selectNode(node) {
this.queryParams.departmentId = node.id
this.getEmployeeList()
},
对显示出来的数据进行处理
1.头像
row指的是当前行的数据
<el-table-column alien="center" prop="staffPhoto" label="头像">
<template v-slot="{row}">
<el-avatar v-if="row.staffPhoto" :src="row.staffPhoto" :size="30" />
<span v-else class="username">{{ row.username.charAt(0) }}</span>
</template>
2.聘用形势
<el-table-column label="聘用形势" />
<template v-slot="{row}">
<span v-if="row.formOfEmployment === 1">正式</span>
<span v-else-if="row.formOfEmployment === 2">非正式</span>
<span v-else>无</span>
</template>
3.总结
分页
先获取total数据,并且赋值给设定好的变量,再赋值给分页组件
async getEmployeeList() {
const { rows, total } = await getEmployeeList(this.queryParams)
this.list = rows
this.total = total
}
}
list: [],
total: '0'
<!-- 分页 -->
<el-row type="flex" style="height: 60px;" align="middle" justify="end">
<el-pagination layout="total,prev, pager, next" :total="total" />
</el-row>
在queryParams里加入两个分页参数
// 存储查询参数
queryParams: {
departmentId: null,
page: 1,
pagesize: 10
}
把添加的参数绑定到分页组件上,同时绑定change事件
<el-row type="flex" style="height: 60px;" align="middle" justify="end">
<el-pagination layout="total,prev, pager, next" :total="total" :current-page="queryParams.page" @current-change="changePage"/>
</el-row>
change方法的逻辑
changePage(newPage) {
this.queryParams.page = newPage// 赋值新页码
this.getEmployeeList()// 查询数据
}
切换节点时,重置为第一页
// 记录节点
selectNode(node) {
this.queryParams.departmentId = node.id
this.queryParams.page = 1// 设置第一页
this.getEmployeeList()
}
中文版 element-ui
// set ElementUI lang to EN
Vue.use(ElementUI, { locale })
// 如果想要中文版 element-ui,按如下方式声明
// Vue.use(ElementUI)
这个引用也要注释
// import locale from 'element-ui/lib/locale/lang/en' // lang i18n
实现员工查询
1.设置参数
// 存储查询参数
queryParams: {
departmentId: null,
page: 1,
pagesize: 10,
keyword: ''
}
2.给输入框绑定参数和事件(输入框内容一变化就触发)
<el-input
v-model="queryParams.keyword"
style="margin-bottom:10px"
type="text"
prefix-icon="el-icon-search"
size="small"
placeholder="输入员工姓名全员搜索"
@input="changeValue"
/>
3.事件逻辑(防抖处理)
changeValue() {
// 单位时间内只会执行最后一次
// this的实例上赋值了一个timer的属性
clearTimeout(this.timer)// 清楚上一次的定时器
this.timer = setTimeout(() => {
// 先把页数设置为第一页
this.queryParams.page = 1
// 调用方法重新渲染
getEmployeeList()
}, 300)
}
员工导出excel
1.封装导excel接口:注意返回的字符形式需要转换
// 导出员工的excel
export function exportEmployee() {
return request({
url: '/sys/user/export',
// 改变接收数据的类型
responseType: 'blob'// 使用blob接收二进制文件流
})
}
2.响应拦截器判断是否是blob格式
// 响应拦截器
service.interceptors.response.use((response) => {
// 判断是不是Blob
if (response.data instanceof Blob) return response.data// 返回了Blob对象
const { data, message, success } = response.data
if (success) {
return data
} else {
Message({ type: 'error', message })
return Promise.reject(new Error(message))// 这里没有传入error,所以新建了一个Error对象,在传信息
}
}
3.下载文件
命令台
npm i file-saver
async exportEmployee() {
// 调用接口
const result = await exportEmployee()
// 使用一个npm包,直接将blob文件下载到本地
// 之后引入这个包
// 调用方法保存下来
// 引入的包名.saveAs(blob对象,文件名称)
FileSaver.saveAs(result, '员工信息表.xlsx')
}
excel导入组件
1.创建组件,把组件模板粘贴过来
<template>
<el-dialog
width="500px"
title="员工导入"
:visible="showExcelDialog"
@close="$emit('update:showExcelDialog', false)"
>
<el-row type="flex" justify="center">
<div class="upload-excel">
<input
ref="excel-upload-input"
class="excel-upload-input"
type="file"
accept=".xlsx, .xls"
>
<div class="drop">
<i class="el-icon-upload" />
<el-button type="text">下载导入模板</el-button>
<span>将文件拖到此处或
<el-button type="text">点击上传</el-button>
</span>
</div>
</div>
</el-row>
<el-row type="flex" justify="end">
<!-- update:props属性名,值 直接修改 .sync修饰符的属性值 -->
<el-button size="mini" type="primary" @click="$emit('update:showExcelDialog', false)">取消</el-button>
</el-row>
</el-dialog>
</template>
<script>
export default {
props: {
showExcelDialog: {
type: Boolean,
default: false
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.upload-excel {
display: flex;
justify-content: center;
margin: 20px;
width: 360px;
height: 180px;
align-items: center;
color: #697086;
.excel-upload-input {
display: none;
z-index: -9999;
}
.btn-upload,
.drop {
border: 1px dashed #dcdfe6;
width: 100%;
height: 100%;
text-align: center;
line-height: 160px;
border-radius: 8px;
display: flex;
flex-direction: column;
justify-content: center;
}
.drop {
line-height: 40px;
color: #bbb;
i {
font-size: 60px;
display: block;
color: #c0c4cc;
}
}
}
</style>
2.在index.vue里引入组件,并注册
import ImportExcel from './components/import-excel.vue'
export default {
name: 'Employee',
components
data() {
return {
depts: [],
components:{
ImportExcel
},
3.index里放置组件
<!--放置导入组件-->
<import-excel></import-excel>
4.数据准备
list: [],
total: '0',
showExcelDialog:false//控制弹层的显示和隐藏
}
5.给组件绑定事件
可以通过:show-excel-dialog.sync这个属性接收显示Excel对话框的状态(例如布尔值),也可以通过发出一个事件来更新父组件中的showExcelDialog状态。
.sync:当子组件import-excel需要更新父组件中的showExcelDialog状态时,它会通过.sync修饰符自动同步这些变更,无需父组件手动处理这些更新。
</div>
<!--放置导入组件-->
<import-excel :show-excel-dialog.sync = "showExcelDialog"/>
</div>
6.点击按钮显示弹层
<el-button size="mini" @click="showExcelDialog=true">excel导入</el-button>
excel下载导入模板
1.封装接口:返回的是Blob要加responseType
// 下载导入员工模板
export function getExportTemplate() {
return request({
url: '/sys/user/import/template',
responseType: 'blob'// 二进制文件流
})
}
2.调用方法,获取Blob,使用第三方包FileSaver来saveAs
methods: {
async getTemplate() {
const data = await getExportTemplate()
FileSaver.saveAs(data, '员工导入模板.xlsx')
}
}
excel导入
1.利用input弹出文件选择器:click()函数是这个DOM元素的原生方法,用于模拟用户对该元素的点击动作。
<el-button type="text" @click="handleUpload">点击上传</el-button>
handleUpload() {
// 弹出文件选择题-只有一种方式,通过input file
this.$refs['excel-upload-input'].click()
// this.$refs.属性名 和 this.$refs[属性名] 等价
}
<input
ref="excel-upload-input"
class="excel-upload-input"
type="file"
accept=".xlsx, .xls"
@change="uploadChange"
>
封装接口:这里是formdata类型,需要 在组件里 const data = new FormData()
// 上传用户的excel
export function uploadExcel(data
) {
return request({
url: '/sys/user/import',
method: 'post',
data // form-data类型 因为要上传文件类型
})
}
方法里调用接口与处理
async uploadChange(event) {
// @change="uploadChange"传过来的event
// 调用接口
const files = event.target.files// input的文件列表
if (files.length > 0) {
// 大于0,说明有文件要上传
const data = new FormData()
// file:file类型
data.append('file', files[0])
try {
// 把data传给接口
await uploadExcel(data)
// 处理成功和失败
// 成功后关闭弹层
this.$emit('uploadSuccess')// 通知父组件 我上传成功
this.$emit('update:showExcelDialog', false)// 关闭弹层
} catch (error) {
// 捕获失败
} finally {
// 无论成功或失败都会执行finally的清空
// 因为名字是excel-upload-input,所以使用了this.$refs[属性名]
this.$refs['excel-upload-input'].value = ''
}
}
}
父组件监听uploadSuccess事件,@uploadSuccess是一个事件监听器,用来监听文件上传成功的事件。它会在文件上传成功时被触发。
<import-excel :show-excel-dialog.sync="showExcelDialog" @uploadSuccess="getEmployeeList" />
删除员工
弹窗
<button size="mini" type="text">查看</button>
<button size="mini" type="text">角色</button>
<el-popconfirm
title="Are you sure to delete this?"
@OnConfirm="confirmDel(row.id)"
>
删除员工的方法
async confirmDel(id) {
await delEmployee(id)
if(this.list.length === 1 && this.queryParams.page > 1){
this.queryParams.page--
this.getEmployeeList()
this.$message.success('成功')
}
}
封装的接口
// 删除员工的方法
export function delEmployee(id) {
return request({
url: `/sys/user/${id}`,
method: 'delete'
})
}