员工详情
员工详情页
需求:重置密码;展示员工的个人信息和岗位信息(支持图片的上传----上传到第三方平台:腾讯云)
详情页的基本布局和路由
- 建立详情页路由
router/modules/employees.js
- 配置二级路由,并且控制左侧菜单不显示
- 动态路由参数的获取方式:1、$route.params.id;2、利用路由映射的props:true 配置,组件中通过props获取即可
{
path: 'detail/:id', // 动态路由参数
component: () => import('@/views/employees/detail'),
hidden: true,
meta: {
title: '员工详情'
}
}
- 建立基本架构
<template>
<div class="employees-detail-container">
<div class="app-container">
<el-card>
<el-tabs>
<el-tab-pane label="登录账号设置">
<!-- 放置表单 -->
<el-form label-width="120px" style="margin-left: 120px; margin-top:30px">
<el-form-item label="姓名:">
<el-input style="width:300px" />
</el-form-item>
<el-form-item label="密码:">
<el-input style="width:300px" type="password" />
</el-form-item>
<el-form-item>
<el-button type="primary">更新</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="个人详情">
<!-- 内容 -->
</el-tab-pane>
<el-tab-pane label="岗位信息">
<!-- 内容 -->
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: 'EmployeesDetail'
}
</script>
- 列表跳转到详情页
<el-button type="text" size="small" @click="$router.push(`/employees/detail/${scope.row.id}`)">查看</el-button>
总结:配置路由;准备模板;控制跳转
注意:动态路由的参数传递方式:1、基于$route.parems.id ;2、基于路由映射的props进行控制
获取用户信息
- 发送请求获取数据
import { reqGetUserDetailById } from '@/api/user.js'
export default {
name: 'EmployeesDetail',
data () {
return {
pwdInfo: {
username: '',
password: ''
}
}
},
created () {
this.loadUserInfo()
},
methods: {
// 加载用户名和密码
async loadUserInfo () {
const ret = await getDetailInfo(this.id)
this.pwdInfo.username = ret.data.username
}
}
}
注意
:这里接口中读取的是后端的密文,我们并不能解密,所以我们不回显原密码!
data() {
return {
pwdInfo: {
username: '',
newPassword: ''
},
rules: {
username: [
{ required: true, message: '姓名不能为空', trigger: ['blur', 'change'] }
],
password: [
{ required: true, message: '密码不可以为空', trigger: ['blur', 'change'] },
{ min: 6, max: 9, message: '密码必须是6-9位', trigger: ['blur', 'change'] }
]
}
}
},
<!-- 放置表单 -->
<el-form ref="pwdForm" :model="pwdInfo" :rules="rules" label-width="120px" style="margin-left: 120px; margin-top:30px">
<el-form-item label="姓名:" prop="username">
<el-input v-model="pwdInfo.username" style="width:300px" />
</el-form-item>
<el-form-item label="密码:" prop="password">
<el-input v-model="pwdInfo.password" style="width:300px" type="password" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleUpdate">更新</el-button>
</el-form-item>
</el-form>
总结:调用接口,获取数据,填充表单
注意:密码不需要填充表单(加密密码无法反解)
更新用户名密码
-
保存个人基本信息接口
src/api/employees.js
export function reqSaveUserDetailById(data) {
return request({
method: 'put',
url: `/sys/user/${data.id}`,
data
})
}
-
用户名和密码的修改
src/views/employees/detail.vue
<el-button type="primary" @click="handleSubmit">更新</el-button>
import { reqSaveUserDetailById } from '@/api/employees'
// 重置密码
handleUpdate () {
this.$refs.pwdForm.validate(async valid => {
if (!valid) return
// 调用接口更新
const ret = await reqSaveUserDetailById({
...this.pwdInfo,
id: this.id
})
this.$message.success(ret.message)
})
},
总结:
- 提交表单时,需要提交输入的明文密码
- 对象的解构赋值用法
个人组件和岗位组件封装
封装个人详情组件
我们将员工个人信息分为三部分,账户,个人, 岗位,这个小节我们对个人组件和岗位组件进行封装
封装个人组件
src/views/employees/components/UserInfo.vue
- 基本模板
<template>
<div class="user-info">
<!-- 个人信息 -->
<el-form label-width="220px">
<!-- 工号 入职时间 -->
<el-row class="inline-info">
<el-col :span="12">
<el-form-item label="工号">
<el-input v-model="userInfo.workNumber" class="inputW" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职时间">
<el-date-picker
v-model="userInfo.timeOfEntry"
type="date"
class="inputW"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 姓名 部门 -->
<el-row class="inline-info">
<el-col :span="12">
<el-form-item label="姓名">
<el-input v-model="userInfo.username" class="inputW" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门">
<el-input v-model="userInfo.departmentName" class="inputW" />
</el-form-item>
</el-col>
</el-row>
<!--手机 聘用形式 -->
<el-row class="inline-info">
<el-col :span="12">
<el-form-item label="手机">
<el-input v-model="userInfo.mobile" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="聘用形式">
<el-select v-model="userInfo.formOfEmployment" class="inputW">
<el-option
v-for="item in EmployeeEnum.hireType"
:key="item.id"
:label="item.value"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 员工照片 -->
<el-row class="inline-info">
<el-col :span="12">
<el-form-item label="员工头像">
<!-- 放置上传图片 -->
</el-form-item>
</el-col>
</el-row>
<!-- 保存个人信息 -->
<el-row class="inline-info" type="flex" justify="center">
<el-col :span="12">
<el-button type="primary" @click="saveUser">保存更新</el-button>
<el-button @click="$router.back()">返回</el-button>
</el-col>
</el-row>
</el-form>
<!-- 基础信息 -->
<el-form label-width="220px">
<div class="block">
<div class="title">基础信息</div>
<el-form-item label="最高学历">
<el-select v-model="formData.theHighestDegreeOfEducation" class="inputW2">
<el-option
v-for="item in EmployeeEnum.highestDegree"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 个人头像 -->
<!-- 员工照片 -->
<el-form-item label="员工照片">
<!-- 放置上传图片 -->
</el-form-item>
<el-form-item label="国家/地区">
<el-select v-model="formData.nationalArea" class="inputW2">
<el-option
v-for="item in EmployeeEnum.isOverseas"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="护照号">
<el-input v-model="formData.passportNo" placeholder="正规护照格式" class="inputW" />
</el-form-item>
<el-form-item label="身份证号">
<el-input v-model="formData.idNumber" placeholder="正规身份证格式" class="inputW" />
</el-form-item>
<el-form-item label="籍贯">
<el-input v-model="formData.nativePlace" placeholder="籍贯地址" class="inputW5" />
</el-form-item>
<el-form-item label="民族">
<el-input v-model="formData.nation" placeholder="请输入民族" class="inputW2" />
</el-form-item>
<el-form-item label="婚姻状况">
<el-select v-model="formData.maritalStatus" class="inputW2">
<el-option
v-for="item in EmployeeEnum.maritaStatus"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="生日">
<el-input v-model="formData.birthday" placeholder="示例 0323" class="inputW" />
</el-form-item>
<el-form-item label="年龄">
<el-input v-model="formData.age" type="number" class="inputW2" />
</el-form-item>
<el-form-item label="星座">
<el-select v-model="formData.constellation" class="inputW2">
<el-option
v-for="item in EmployeeEnum.constellation"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="血型">
<el-select v-model="formData.bloodType" class="inputW2">
<el-option
v-for="item in EmployeeEnum.bloodType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="户籍所在地">
<el-input v-model="formData.domicile" class="inputW5" />
</el-form-item>
<el-form-item label="政治面貌">
<el-input v-model="formData.politicalOutlook" class="inputW2" />
</el-form-item>
<el-form-item label="入党时间">
<el-date-picker
v-model="formData.timeToJoinTheParty"
type="date"
placeholder="选择日期"
class="inputW"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="存档机构">
<el-input v-model="formData.archivingOrganization" placeholder="请输入" />
</el-form-item>
<el-form-item label="子女状态">
<el-input v-model="formData.stateOfChildren" placeholder="请输入" />
</el-form-item>
<el-form-item label="子女有无商业险">
<el-radio-group v-model="formData.doChildrenHaveCommercialInsurance">
<el-radio label="1">有</el-radio>
<el-radio label="2">无</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="有无违法违纪状态">
<el-input v-model="formData.isThereAnyViolationOfLawOrDiscipline" placeholder="请输入" />
</el-form-item>
<el-form-item label="有无重大病史">
<el-input v-model="formData.areThereAnyMajorMedicalHistories" placeholder="请输入" />
</el-form-item>
</div>
<!-- 通讯信息 -->
<div class="block">
<div class="title">通讯信息</div>
<el-form-item label="QQ">
<el-input v-model="formData.qq" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="微信">
<el-input v-model="formData.wechat" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="现居住地">
<el-input v-model="formData.placeOfResidence" placeholder="请输入" />
</el-form-item>
<el-form-item label="通讯地址">
<el-input v-model="formData.postalAddress" placeholder="请输入" />
</el-form-item>
<el-form-item label="联系手机">
<el-input v-model="formData.contactTheMobilePhone" placeholder="11位字符" maxlength="11" class="inputW" @change.native="handlePhone(2)" />
</el-form-item>
<el-form-item label="个人邮箱">
<el-input v-model="formData.personalMailbox" placeholder="请输入" type="mail" class="inputW" />
</el-form-item>
<el-form-item label="紧急联系人">
<el-input v-model="formData.emergencyContact" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="紧急联系电话">
<el-input v-model="formData.emergencyContactNumber" placeholder="11位字符" class="inputW" />
</el-form-item>
</div>
<!-- 账号信息 -->
<div class="block">
<div class="title">账号信息</div>
<el-form-item label="社保电脑号">
<el-input v-model="formData.socialSecurityComputerNumber" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="公积金账号">
<el-input v-model="formData.providentFundAccount" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="银行卡号">
<el-input v-model="formData.bankCardNumber" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="开户行">
<el-input v-model="formData.openingBank" placeholder="请输入" class="inputW" />
</el-form-item>
</div>
<!-- 教育信息 -->
<div class="block">
<div class="title">教育信息</div>
<el-form-item label="学历类型">
<el-select v-model="formData.educationalType" placeholder="请选择">
<el-option
v-for="item in EmployeeEnum.educationType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="毕业学校">
<el-input v-model="formData.graduateSchool" placeholder="请输入" class="inputW2" />
</el-form-item>
<el-form-item label="入学时间">
<el-date-picker v-model="formData.enrolmentTime" type="data" placeholder="请输入时间" class="inputW" value-format="yyyy-MM-dd" />
</el-form-item>
<el-form-item label="毕业时间">
<el-date-picker v-model="formData.graduationTime" type="data" placeholder="请输入时间" class="inputW" value-format="yyyy-MM-dd" />
</el-form-item>
<el-form-item label="专业">
<el-input v-model="formData.major" placeholder="请输入" class="inputW" />
</el-form-item>
</div>
<!-- 从业信息 -->
<div class="block">
<div class="title">从业信息</div>
<el-form-item label="上家公司">
<el-input v-model="formData.homeCompany" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="职称">
<el-input v-model="formData.title" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="有无竞业限制">
<el-input v-model="formData.isThereAnyCompetitionRestriction" placeholder="请输入" style="width:80%" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.remarks" type="textarea" placeholder="请输入备注" style="width:80%" />
</el-form-item>
<!-- 保存员工信息 -->
<el-row class="inline-info" type="flex" justify="center">
<el-col :span="12">
<el-button type="primary" @click="savePersonal">保存更新</el-button>
<el-button @click="$router.back()">返回</el-button>
</el-col>
</el-row>
</div>
</el-form>
</div>
</template>
本章节个人数据过于**
繁杂,庞大
**,同学们在开发期间,拷贝代码即可,我们只写关键部位的代码
- 定义user-info的数据
<script>
import EmployeeEnum from '@/api/constant/employees'
export default {
data() {
return {
EmployeeEnum, // 员工枚举数据
userInfo: {
workNumber: '', // 工号
timeOfEntry: '', // 入职时间
username: '', // 姓名
departmentName: '', // 部门
mobile: '', // 手机
formOfEmployment: '' // 聘用形式
},
formData: {
userId: '',
username: '', // 用户名
sex: '', // 性别
mobile: '', // 手机
companyId: '', // 公司id
departmentName: '', // 部门名称
// onTheJobStatus: '', // 在职状态 no
dateOfBirth: '', // 出生日期
timeOfEntry: '', // 入职时间
theHighestDegreeOfEducation: '', // 最高学历
nationalArea: '', // 国家
passportNo: '', // 护照号
idNumber: '', // 身份证号
idCardPhotoPositive: '', // 身份证照正
idCardPhotoBack: '', // 身份证照正
nativePlace: '', // 籍贯
nation: '', // 民族
englishName: '', // 英文名字
maritalStatus: '', // 婚姻状况
staffPhoto: '', // 员工照片
birthday: '', // 生日
zodiac: '', // 属相
age: '', // 年龄
constellation: '', // 星座
bloodType: '', // 血型
domicile: '', // 户籍所在地
politicalOutlook: '', // 政治面貌
timeToJoinTheParty: '', // 入党时间
archivingOrganization: '', // 存档机构
stateOfChildren: '', // 子女状态
doChildrenHaveCommercialInsurance: '1', // 保险状态
isThereAnyViolationOfLawOrDiscipline: '', // 违法违纪状态
areThereAnyMajorMedicalHistories: '', // 重大病史
qq: '', // QQ
wechat: '', // 微信
residenceCardCity: '', // 居住证城市
dateOfResidencePermit: '', // 居住证办理日期
residencePermitDeadline: '', // 居住证截止日期
placeOfResidence: '', // 现居住地
postalAddress: '', // 通讯地址
contactTheMobilePhone: '', // 联系手机
personalMailbox: '', // 个人邮箱
emergencyContact: '', // 紧急联系人
emergencyContactNumber: '', // 紧急联系电话
socialSecurityComputerNumber: '', // 社保电脑号
providentFundAccount: '', // 公积金账号
bankCardNumber: '', // 银行卡号
openingBank: '', // 开户行
educationalType: '', // 学历类型
graduateSchool: '', // 毕业学校
enrolmentTime: '', // 入学时间
graduationTime: '', // 毕业时间
major: '', // 专业
graduationCertificate: '', // 毕业证书
certificateOfAcademicDegree: '', // 学位证书
homeCompany: '', // 上家公司
title: '', // 职称
resume: '', // 简历
isThereAnyCompetitionRestriction: '', // 有无竞业限制
proofOfDepartureOfFormerCompany: '', // 前公司离职证明
remarks: '' // 备注
}
}
},
computed: {
userId() {
return this.$route.params.id
}
},
methods: {
saveUser() {
},
savePersonal() {
}
}
}
</script>
- 在detail.vue组件中,注册并使用
<el-tab-pane label="个人详情">
<!-- 放置个人详情 -->
<user-info />
</el-tab-pane>
import UserInfo from './components/user-info.vue'
components: {
UserInfo
},
总结:准备模板和数据,然后导入组件并使用
封装岗位组件
封装岗位组件**
src/views/employee/components/JobInfo.vue
**
- 基本模板
<template>
<div class="job-info">
<!-- 基础信息 -->
<el-form label-width="220px">
<div class="block">
<div class="title">基础信息</div>
<el-form-item label="岗位">
<el-input v-model="formData.post" placeholder="请输入" class="inputW" />
</el-form-item>
<!-- <el-form-item label="转正日期">
<el-date-picker
v-model="formData.dateOfCorrection"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
/>
</el-form-item> -->
<el-form-item label="转正状态">
<el-select v-model="formData.stateOfCorrection" placeholder="请选择" disabled>
<el-option
v-for="item in EmployeeEnum.stateOfCorrection"
:key="item.value"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="职级">
<el-input v-model="formData.rank" class="inputW" />
</el-form-item>
<el-form-item label="转正评价">
<el-input v-model="formData.correctionEvaluation" type="textarea" placeholder="1-300位字符" />
</el-form-item>
<el-form-item label="汇报对象">
<el-select v-model="formData.reportId" filterable placeholder="请选择" class="inputW">
<el-option v-for="item in list" :key="item.id" :label="item.username" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="HRBP">
<el-select v-model="formData.hrbp" filterable placeholder="请选择" class="inputW">
<el-option v-for="item in list" :key="item.id" :label="item.username" :value="item.id" class="inputW" />
</el-select>
</el-form-item>
<el-form-item class="formInfo" label="调整司龄(天):">
<el-input v-model="formData.adjustmentAgedays" type="number" placeholder="请输入" class="inputW" />
</el-form-item>
<el-form-item label="首次参加工作时间">
<el-date-picker
v-model="formData.workingTimeForTheFirstTime"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="调整工龄">
<el-input v-model="formData.adjustmentOfLengthOfService" placeholder="0.00年" class="inputW" disabled />
</el-form-item>
</div>
<!-- 合同信息 -->
<div class="block">
<div class="title">合同信息</div>
<el-form-item class="formInfo" label="首次合同开始时间:">
<el-date-picker
v-model="formData.initialContractStartTime"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="首次合同结束时间">
<el-date-picker
v-model="formData.firstContractTerminationTime"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="现合同开始时间">
<el-date-picker
v-model="formData.currentContractStartTime"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="现合同结束时间">
<el-date-picker
v-model="formData.closingTimeOfCurrentContract "
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="合同期限">
<el-select v-model="formData.contractPeriod" class="filter-item">
<el-option
v-for="item in EmployeeEnum.contractPeriod"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="续签次数">
<el-select v-model="formData.renewalNumber" class="filter-item">
<el-option
v-for="item in EmployeeEnum.renewalCount"
:key="item.id"
:label="item.value"
:value="item.id"
/>
</el-select>
</el-form-item>
</div>
<!-- 招聘信息 -->
<div class="block">
<div class="title">招聘信息</div>
<el-form-item label="其他招聘渠道">
<el-select v-model="formData.otherRecruitmentChannels" placeholder="请选择">
<el-option
v-for="item in EmployeeEnum.resumeSource"
:key="item.id"
:label="item.value"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="招聘渠道">
<el-select v-model="formData.recruitmentChannels" placeholder="请选择">
<el-option
v-for="item in EmployeeEnum.resumeSource"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="社招/校招">
<el-select v-model="formData.socialRecruitment" placeholder="请选择">
<el-option
v-for="item in EmployeeEnum.hireSourceType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="推荐企业/人">
<el-input v-model="formData.recommenderBusinessPeople" placeholder="请输入" class="infoPosition inputW" />
</el-form-item>
</div>
<!-- 从业信息 -->
<el-form-item>
<el-button type="primary" @click="saveJob">保存更新</el-button>
<el-button @click="$router.back()">返回</el-button>
</el-form-item>
</el-form>
</div>
</template>
- 定义岗位数据
<script>
import EmployeeEnum from '@/api/constant/employees'
export default {
data() {
return {
list: [],
EmployeeEnum,
formData: {
adjustmentAgedays: '', // 调整司龄天
adjustmentOfLengthOfService: '', // 调整工龄天
closingTimeOfCurrentContract: '', // 现合同结束时间
companyId: '', // 公司ID
contractDocuments: '', // 合同文件
contractPeriod: '', // 合同期限
correctionEvaluation: '', // 转正评价
currentContractStartTime: '', // 现合同开始时间
firstContractTerminationTime: '', // 首次合同结束时间
hrbp: '', // HRBP
initialContractStartTime: '', // 首次合同开始时间
otherRecruitmentChannels: '', // 其他招聘渠道
post: '', // 岗位
rank: null, // 职级
recommenderBusinessPeople: '', // 推荐企业人
recruitmentChannels: '', // 招聘渠道
renewalNumber: '', // 续签次数
reportId: '', // 汇报对象
reportName: null, // 汇报对象
socialRecruitment: '', // 社招校招
stateOfCorrection: '', // 转正状态
taxableCity: '', // 纳税城市
userId: '', // 员工ID
workMailbox: '', // 工作邮箱
workingCity: '', // 工作城市
workingTimeForTheFirstTime: '' // 首次参加工作时间
}
}
},
computed: {
userId() {
return this.$route.params.id
}
},
methods: {
saveJob() {
}
}
}
</script>
- 在detail.vue组件中,注册并使用
<el-tab-pane label="岗位详情">
<job-info />
</el-tab-pane>
import JobInfo from './components/job-info.vue'
components: {
JobInfo
},
总结:准备模板和数据并使用组件
员工个人信息和岗位信息
读取保存个人信息
这个环节里面大部分都是繁杂的属性和重复的过程,所以该环节直接将过程代码拷贝到项目中即可
- 封装 读取个人信息 保存个人信息 读取岗位信息 保存岗位信息
/** *
* 读取用户详情的基础信息 (个人详情-下面的接口)
* **/
export function reqGetPersonalDetail(id) {
return request({
method: 'get',
url: `/employees/${id}/personalInfo`
})
}
/** *
* 更新用户详情的基础信息 (个人详情-下面的接口)
* **/
export function reqUpdatePersonal(data) {
return request({
method: 'put',
url: `/employees/${data.userId}/personalInfo`,
data
})
}
/** **
* 获取用户的岗位信息 (岗位信息)
* ****/
export function reqGetJobDetail(id) {
return request({
method: 'get',
url: `/employees/${id}/jobs`
})
}
/**
* 保存岗位信息 (岗位信息)
* ****/
export function reqUpdateJob(data) {
return request({
method: 'put',
url: `/employees/${data.userId}/jobs`,
data
})
}
-
读取,保存个人信息
user-info
需要注意:这里的保存实际上分成了两个接口,这是接口的设计,我们只能遵守
import { reqGetPersonalDetail, reqUpdatePersonal, reqSaveUserDetailById } from '@/api/employees'
import { getDetailInfo } from '@/api/user'
created() {
this.getUserBaseInfo()
this.getUserDetailInfo()
},
methods: {
// 获取员工基本信息(上面的表单)
async getUserBaseInfo () {
try {
const ret = await getDetailInfo(this.userId)
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.userInfo = ret.data
}
} catch {
this.$message.error('获取用户基本信息失败')
}
},
// 获取详细信息(下面的表单)
async getUserDetailInfo () {
try {
const ret = await reqGetPersonalDetail(this.userId)
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.formData = ret.data
}
} catch {
this.$message.error('获取用户详细信息失败')
}
},
async saveUser() {
try {
const ret = await reqSaveUserDetailById(this.userInfo)
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.$message.success(ret.message)
}
} catch {
this.$message.error('更新用户基本信息失败')
}
},
async savePersonal() {
try {
console.log(this.formData, this.userId)
const ret = await reqUpdatePersonal({
...this.formData,
// 当前修改的用户的id
id: this.userId
})
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.$message.success(ret.message)
}
} catch {
this.$message.error('更新用户基本信息失败')
}
}
}
总结:
- 获取个人信息
- 保存个人信息
读取保存岗位信息
-
读取,保存岗位信息
job-info
// 获取员工的简单列表
export function reqGetEmployeeSimple () {
return request({
url: '/sys/user/simple'
})
}
- 功能实现
import { reqGetEmployeeSimple, reqGetJobDetail, reqUpdateJob } from '@/api/employees'
computed: {
userId() {
return this.$route.params.id
}
},
created() {
// 获取岗位信息
this.getJobInfo()
// 获取下拉列表数据
this.getList()
},
methods: {
// 获取岗位信息
async getJobInfo () {
try {
const ret = await reqGetJobDetail(this.userId)
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.formData = ret.data
}
} catch {
this.$message.error('获取岗位信息失败')
}
},
// 获取下拉列表数据
async getList () {
try {
const ret = await reqGetEmployeeSimple()
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.list = ret.data
}
} catch {
this.$message.error('获取岗位信息失败')
}
},
// 报错岗位信息
async saveJob() {
try {
const ret = await reqUpdateJob({
...this.formData,
userId: this.userId
})
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.$message.success('更新岗位信息成功')
}
} catch {
this.$message.error('更新岗位信息失败')
}
}
}
总结:
- 获取岗位信息
- 保存岗位信息
配置腾讯云Cos
账号注册 (实名认证)
目标
: 配置一个腾讯云cos由于上课的开发的特殊性,我们不希望把所有的图片都上传到我们自己的官方服务器上,
这里我们可以采用一个腾讯云的图片方案
上边图的意思就是说,我们找一个可以免费上传图片的服务器,帮我们**
代管图片
,我们在自己的数据库里只保存一个地址就行, 这其实也是很多项目的处理方案,会有一个公共的文件服务器
**
第一步,我们必须先拥有一个腾迅云的开发者账号(有时会有腾讯云的广告电话)
请按照腾讯云的注册方式,注册自己的账号
第二步,实名认证
选择个人账户
填写个人身份信息
下一步,扫描二维码授权
手机端授权
总结:注册腾讯云账号;登录系统;进行实名认证(填充个人相关信息)
开通服务
- 点击云产品 - 对象存储
- 点击开通服务
创建存储桶
到这一步,账号的部分就操作完毕,接下来,我们需要来创建一个存储图片的存储桶
总结:存储桶可以理解为存储图片的一个位置(分组)
注意:选择所属地域;添加自定义存储桶名称;访问权限选择共有读私有写
设置cors规则
AllowHeader 需配成*
,如下图所示
注意:因为我们本身没有域名,所以这里设置成**
*
**,仅限于测试,正式环境的话,这里需要配置真实的域名地址
封装上传图片组件分析
目标
梳理整个的上传过程
初始化 cos 对象参数
名称 | 描述 |
---|---|
SecretId | 开发者拥有的项目身份识别 ID,用以身份认证,可在 API 密钥管理 页面获取 |
SecretKey | 开发者拥有的项目身份密钥,可在 API 密钥管理 页面获取 |
注意,上述的参数我们在本次开发过程中,直接将参数放置在前端代码中存储,
但是腾讯云本身是不建议这么做的,因为**
敏感信息
**放在前端很容易被捕获,一般放在后台, 前端准备参数, 交给后台和腾讯云交互, 交互时, 需要秘钥, 秘钥在后台存着的由于我们本次是测试研发,所以这个过程可以忽略
正确的做法应该是,通过网站调用接口换取敏感信息
实例化 上传sdk (这边还没用到, 先没配)
var cos = new COS({
SecretId: 'AKIDc3E9nAG5dtulOlsiHdljSt3wYtwkIafT', // 身份识别 ID
SecretKey: 'Zrsc8QRZS4PTFkyuZx4uUnqc7PQAcokP', // 身份密钥
});
到目前为止,我们上传图片准备的内容就已经OK,接下来,我们在**
src/componets
** 新建一个**ImageUpload
** 组件
该上传组件需要满足什么要求呢?
- 可以显示传入的图片 (本地预览)
- 可以删除传入的图片
- 可以上传图片到云服务器
- 上传到腾讯云之后,可以返回图片地址,覆盖显示
- 上传成功之后,可以回调成功函数
封装上传组件代码实现
**
目标
**实现上传组件的代码部分JavaScript SDK 需浏览器支持基本的 HTML5 特性(支持 IE10 以上浏览器),
以便支持 ajax 上传文件和计算文件 MD5 值。
新建文件上传组件
- 安装JavaScript SDK
npm i cos-js-sdk-v5
- 新建上传图片组件
src/components/ImageUpload/index.vue
上传组件,我们可以沿用element的el-upload组件,并且采用照片墙的模式
list-type="picture-card"
<template>
<el-upload list-type="picture-card" action="">
<i class="el-icon-plus" />
</el-upload>
</template>
- 全局注册组件
import PageTools from './PageTools'
import UploadExcel from './UploadExcel'
import ImageUpload from './ImageUpload'
export default {
install(Vue) {
Vue.component('PageTools', PageTools) // 注册工具栏组件
Vue.component('UploadExcel', UploadExcel) // 注册导入excel组件
Vue.component('ImageUpload', ImageUpload) // 注册导入上传组件
}
}
总结:新建一个通用上传文件的组件,通过插件进行全局注册,然后在用户详细信息页面使用组件即可。
点击图片进行预览
- 限定上传的图片数量和action
<template>
<div class="upload-box">
<el-upload
:on-preview="preview"
:file-list="fileList"
list-type="picture-card"
:limit="1"
action="#"
>
<i class="el-icon-plus" />
</el-upload>
</div>
</template>
action为什么给#, 因为我们要上传到腾讯云,需要自定义的上传方式,action给个#防止报错
data() {
return {
fileList: [
{ url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100' }
],
showDialog: false, // 控制显示弹层
currentImg: ''
}
},
preview(file) {
// 这里应该弹出一个层 层里是点击的图片地址
this.currentImg = file.url
this.showDialog = true
},
- 预览弹层
<el-dialog width="600px" top="8vh" title="图片预览" :visible.sync="showDialog">
<img width="100%" :src="currentImg" alt="">
</el-dialog>
总结:
- 显示选择的图片列表 file-list
- 预览图片 on-preview
- 弹窗的使用
回顾
- 导入
- 理解文件上传或者导入的基本流程
- 前端直接上传文件给后端:基于xhr2.0新特性FormData
- 前端需要处理选择的文件数据,然后调用接口提交数据
- 文件在前端转换为base64,然后提交该数据
- Excel文件在前端转换为json数据,然后提交该数据
- 请求参数数据格式的转换:逻辑思维(算法)
- 日期的转换
- 理解文件上传或者导入的基本流程
- 导出
- 导出的本质:下载文件
- 直接由后端提供一个下载链接即可,前端只要点击该链接,文件就会下载
- 前端调用接口获取数据,然后把数据写入Excel:基于第三方包实现
- 获取的原始的后端数据需要转换为导出的数据格式:逻辑思维(算法)
- 日期和聘用形式处理
- 导出的本质:下载文件
- 头像上传
- 个人详情页面结构处理:路由;布局;表单回填;表单提交
- 上传文件的两种方案:
- 上传到自己的服务器上
- 上传到第三方云平台中(腾讯云)
- 腾讯云用法
- 注册账号
- 实名认证
- 创建存储桶
- 设置跨域访问的规则CORS
- 如何做到私钥写:生成自己账号的秘钥
- 封装上传头像的组件
- 基本结构封装:基于ElementUI的el-upload组件实现
- 基本展示和图片预览
根据上传数量控制上传按钮
需求:当选中的图片大于等于一张时隐藏加号效果
- 控制上传显示
computed: {
// 设定一个计算属性 判断是否已经上传完了一张
imgLen () {
return this.fileList.length >= 1
}
},
- 模板布局
<el-upload
list-type="picture-card"
:file-list="fileList"
:on-preview="preview"
:limit="1"
action=""
:class='{hideplus: imgLen}'
>
<i class="el-icon-plus" />
</el-upload>
<style scoped lang="scss">
.my-img-upload {
width: 500px;
.hideplus {
::v-deep .el-upload--picture-card {
display: none;
}
}
}
</style>
小拓展: 如果希望能够定制, 图片的限制数量, 而不是永远只是一张, 这里的数字 1, 是可以通过父传子传过来的
注意:scss中的深度选择器需要使用 ::v-deep
删除图片
点击删除按钮, 看似删除成功了, 但是 fileList 数据没有删除, 需要处理
<el-upload
:on-preview="preview"
:on-remove="handleRemove"
:file-list="fileList"
:limit="1"
list-type="picture-card"
action="#"
>
<i class="el-icon-plus" />
</el-upload>
// 控制删除动作
handleRemove (file, fileList) {
// 参数一file表示要删除的文件信息
// 参数二fileList表示删除之后剩余的文件信息
// console.log(fileList)
// file表示当前处理的文件信息
// this.fileList = this.fileList.filter(item => {
// return item.uid !== file.uid
// })
this.fileList = fileList
},
总结:
- 监听删除的动作 on-remove(el-upload组件的选项)
- 删除事件的回调参数一表示要删除的文件信息,参数二表示删除后剩余的图片信息
添加操作
-
默认选中图片时,会自动上传文件到action指向的地址,但是我们希望手动上传
-
点击 + 号, 进行上传动作, 会触发 el-upload的http-request属性配的函数
- 注意:auto-upload=“true”,这样才会触发http-request指向的函数
:http-request="handleUpload"
// 自定义上传动作 有个参数 有个file对象
handleUpload(params) {
// 进行上传操作
console.log(params.file)
}
- 添加文件时, 触发on-change事件函数,函数中将页面的 fileList 同步到 this.fileList (数据中),从而方便进行预览操作
// 添加文件, 用户选了就应该新增文件预览
handleChange(file, fileList) {
// console.log(fileList)
// console.log(fileList.length)
this.fileList = fileList
},
总结
- 监听选中文件的事件 on-change
- 如果想手动上传文件,而不是默认上传给action,那么需要配置 http-request,该配置函数中可以进行手动上传操作(必须保证 auto-upload 值为 true)
- 基于el-upload组件的submit方法触发上传的动作
this.$refs.upload.submit()
<!-- 自动上传:选中图片后,自动把图片文件传递给action指向的url地址 -->
<!-- 手动上传:选中图片后,仅仅触发一个函数,该函数中需要自己写代码上传文件 -->
上传之前校验检查
控制上传图片的类型和上传大小, 如果不满足条件 返回false上传就会停止
:before-upload="beforeUpload"
// 配置上传前的校验, 只要通过校验, 才能进行上传
// 选择文件之前进行验证
beforeUpload (file) {
// 检测文件的类型
const types = ['image/png', 'image/jpeg', 'image/gif']
if (!types.includes(file.type)) {
// 不支持的格式
this.$message.error('必须上传png,jpeg,gif三种类型之一')
return false
}
// 检测文件的大小(限制1M以内)
if (file.size / 1024 / 1024 > 1) {
this.$message.error('图片不可以超过1M')
return false
}
return true
},
总结:选择文件之前进行相关类型检测(文件类型和文件大小)
上传到腾讯云
- 上传动作为el-upload的http-request属性
:http-request="handleUpload"
// 自定义上传动作 有个参数 有个file对象,是我们需要上传到腾讯云服务器的内容
handleUpload(params) {
console.log(params.file)
}
我们需要在该方法中,调用腾讯云的上传方法
- 获取上传腾讯云秘钥腾讯云文档地址
登录 访问管理控制台 ,获取您的项目 SecretId 和 SecretKey。
- 安装腾讯云上传文件sdk包
npm i cos-js-sdk-v5
- 配置上传秘钥并上传
import COS from 'cos-js-sdk-v5' // 导入腾讯云的包(sdk)
const cos = new COS({
SecretId: 'AKIDc3E9nAG5dtulOlsiHdljSt3wYtwkIafT', // 身份识别 ID
SecretKey: 'Zrsc8QRZS4PTFkyuZx4uUnqc7PQAcokP' // 身份密钥
})
// 进行上传操作
handleUpload(params) {
// console.log(params.file)
if (params.file) {
// 执行上传操作
cos.putObject({
Bucket: 'jepson-75-1256203106', /* 存储桶名称 */
Region: 'ap-shanghai', /* 存储桶所在地域,必须字段 */
Key: params.file.name, /* 文件名 */
StorageClass: 'STANDARD', // 上传模式, 标准模式(默认即可)
Body: params.file, // 上传文件对象
onProgress: (progressData) => {
console.log(JSON.stringify(progressData))
}
}, (err, data) => {
console.log(err || data)
})
}
}
上传腾讯云基本流程
- 配置上传的权限
import COS from 'cos-js-sdk-v5' // 导入腾讯云的包(sdk)
const cos = new COS({
SecretId: 'AKIDc3E9nAG5dtulOlsiHdljSt3wYtwkIafT', // 身份识别 ID
SecretKey: 'Zrsc8QRZS4PTFkyuZx4uUnqc7PQAcokP' // 身份密钥
})
- 配置相关的请求参数
handleUpload (params) {
cos.putObject({
// 存储桶名称
Bucket: 'abc-1252553968',
// 存储桶所在地址
Region: 'ap-beijing',
// 上传的文件名称
Key: params.file.name,
// 上传模式
StorageClass: 'STANDARD',
// 上传的文件对象
Body: params.file,
// 监控上传的进度
onProgress: function(progressData) {
console.log(JSON.stringify(progressData))
}
}, function(err, data) {
// 上传完成的回调
console.log(err || data)
})
},
总结
- 这种上传方式仅仅用于测试,不太安全(秘钥容易泄露)
- 正规的流程应该把秘钥存储在后端服务器中
上传成功之后处理返回数据
如何处理返回成功的数据, 应该在上传完图片, 得到图片地址,
- 需要做的事情:
- 更新当前上传的对应图片的 status => 更新成 success
- 更新当前上传的对应图片的 url => 更新成腾讯云拿到的 Location (拼上前缀)
问题: this.fileList 是一个数组, 我怎么知道更新谁呢 ? params.file.uid 就是唯一标识
解决: 通过 params.file.uid 决定修改 this.fileList 中的哪个对象
- 处理返回数据
upload(params) {
if (!params.file) return
// 将文件对象, 上传到腾讯云
cos.putObject({
Bucket: 'jepsonpp-75-1256203106', // 存储桶的名字
Region: 'ap-shanghai', // 存储桶地域
Key: params.file.name, // 上传到存储桶的文件名, 如果希望不重名, 可以对文件名进行处理
StorageClass: 'STANDARD', // 上传模式, 标准模式
Body: params.file, // 上传的文件对象
// 上传的进度, 上传的过程中实时触发onProgress, 可以做进度条的展示
onProgress: progressData => {
// console.log(progressData)
}
}, (err, data) => {
// 上传完成的回调
if (err) {
this.$message.error('上传图片失败')
return
}
// data表示上传成功后后端返回的数据
// 选中图片后,this.fileList中本来已经有了选中的这张图片的信息
// 上传成功后需要把对应的图片的地址修改为腾讯云上传成功的地址,并且修改完成状态
if (data.statusCode === 200) {
const imgInfo = this.fileList.find(item => {
return item.uid === params.file.uid
})
imgInfo.status = 'success'
imgInfo.url = 'https://' + data.Location
} else {
this.$message.error('上传图片失败')
}
})
}
总结
- 腾讯云返回上传的结果后需要更新图片的地址和状态
上传的进度条显示
为了再上传图片过程中显示进度条,我们可以使用element-ui的进度条显示当前的上传进度
- 放置进度条
<el-upload :before-upload="checkUpload" :on-change="onChange" :http-request="handleUpload" :on-remove="onRemove" :class="{hideplus: imgLen}" :on-preview="preview" :file-list="fileList" list-type="picture-card" action="http://baidu.com">
<i class="el-icon-plus" />
</el-upload>
<el-progress v-if="isUploading" class="progress-tip" type="circle" :percentage="percent" />
- 通过腾讯云sdk监听上传进度
// 手动实现文件上传操作
handleUpload (params) {
// console.log(params.file)
// 手动上传文件到腾讯云
if (params.file) {
// 显示进度条
this.isUploading = true
// 执行上传的动作
cos.putObject({
// 存储桶名称
Bucket: 'byte-1252553968',
// 存储桶所在地域,必须字段
Region: 'ap-beijing',
// 文件名称
Key: params.file.name,
// 上传模式, 标准模式(默认即可)
StorageClass: 'STANDARD',
// 上传文件对象
Body: params.file,
// 监听上传的进度
onProgress: (progressData) => {
// 更新进度
this.percent = progressData.percent * 100
}
}, (err, data) => {
// 上传的结果:如果err有值,证明上传失败了,如果err是null表示上传成功
// data表示上传成功后,腾讯云返回的图片地址
// console.log(err || data)
if (err) {
this.isUploading = false
return this.$message.error('上传文件失败')
}
// 上传成功
if (data.statusCode === 200) {
// 获取腾讯云上的图片地址
const fileInfo = this.fileList.find(item => item.uid === params.file.uid)
if (fileInfo) {
// 图片右上角会出现对勾
fileInfo.status = 'success'
// 更新图片地址
fileInfo.url = 'https://' + data.Location
}
} else {
this.$message.error('上传图片失败')
}
setTimeout(() => {
this.isUploading = false
this.percent = 0
}, 500)
})
}
},
总结:
- 基于腾讯云API的回调监控上传的进度 onProgress
- 基于el-progress组件实现进度提示
在员工详情中应用上传组件
配置上传图片组件的数量
- 组件接收参数
props: {
limit: {
type: Number,
default: 1
}
},
computed: {
// 判断是否选中了多张图片
isMultiple () {
return this.fileList.length >= this.limit
}
},
<el-upload
list-type="picture-card"
:file-list="fileList"
:on-preview="preview"
:on-remove="handleRemove"
:on-change="handleChange"
:before-upload="beforeUpload"
:http-request="handleUpload"
:limit="limit"
action=""
:class="{hideplus: isMultiple}"
>
<i class="el-icon-plus" />
</el-upload>
- 父组件传入数量
<image-upload :limit="1"/>
总结:如果有相关信息需要定制,需要抽取组件的属性
- 支持默认图片的显示
- 父组件注入数据
<ImageUpload :default-img="userInfo.staffPhoto" :limit="1" />
- 子组件处理默认图片
export default {
name: 'ImageUpload',
props: {
// 控制上传图片的数量
limit: {
type: Number,
default: 1
},
// 获取默认的图片地址
defaultImg: {
type: String,
default: ''
}
},
},
created () {
this.fileList.push({
url: this.defaultImg
})
},"
总结:支持默认显示已有的头像
员工的头像
在**
user-info.vue
**中放置上传组件
- 模板布局
<!-- 员工照片 -->
<el-row class="inline-info">
<el-col :span="12">
<el-form-item label="员工头像">
<image-upload ref="avatarRef" :default-img='userInfo.staffPhoto' :limit="1" />
</el-form-item>
</el-col>
</el-row>
- 封装的头像上传的组件默认图片的处理不可以使用created,而是要使用侦听器
watch: {
defaultImg (url) {
this.fileList = [{ url }]
}
},
- 当点击保存更新时,获取图片的内容, 进行提交
async saveUser() {
// 去读取 员工上传的头像
const fileList = this.$refs.avatrRef.fileList // 读取上传组件的数据
// 进行提交
...
},
- 判断图片是否上传成功
// 判断图片是否全部上传成功了
const fileList = this.$refs.myAvatar.fileList
const isSuccess = fileList.every(item => {
return item.status === 'success'
})
if (!isSuccess) {
this.$message.error('请等待上传图片成功后再提交表单')
return
}
- 调用接口更新头像
employees/components/user-info.vue
async saveUser() {
try {
// 判断图片是否全部上传成功了
const fileList = this.$refs.myAvatar.fileList
const isSuccess = fileList.every(item => {
return item.status === 'success'
})
if (!isSuccess) {
this.$message.error('请等待上传图片成功后再提交表单')
return
}
const ret = await reqSaveUserDetailById({
...this.userInfo,
// 获取子组件中上传成功的图片的地址
staffPhoto: this.$refs.myAvatar.fileList[0].url
})
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.$message.success(ret.message)
}
} catch {
this.$message.error('更新用户基本信息失败')
}
},
总结:
- 把上传成功的头像更新到自己的服务器中
- 需要初始化原有的默认头像
注意:默认头像的处理需要使用侦听器(默认是没有头像的,调用接口获取到头像后进行更新操作)
员工的照片
- 员工证件照
<el-form-item label="员工照片">
<ImageUpload :default-img="urlList" :limit="3" />
</el-form-item>
- 读取时赋值照片
// 获取详细信息(下面的表单)
async getUserDetailInfo () {
try {
const ret = await reqGetPersonalDetail(this.userId)
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.formData = ret.data
// 获取照片数据进行初始化
if (this.formData.staffPhoto) {
// 获取员工的照片(如果是多张图片,会把多个url地址用;隔开)
if (ret.data.staffPhoto) {
// 获取多张员工照片
this.urlList = ret.data.staffPhoto.split(';')
}
}
}
} catch {
this.$message.error('获取用户详细信息失败')
}
},
- 封装上传成功的判断状态
computed: {
// 判断是否选中了多张图片
isMultiple () {
return this.fileList.length >= this.limit
},
// 判断是否所有的图片都上传成功了
isAllSuccess () {
return this.fileList.every(item => {
return item.status === 'success'
})
}
},
- 保存时读取头像
async savePersonal () {
try {
// 获取最新的员工照片信息
// photos = ['http://abc.com/a.png', 'http://abc.com/b.png']
// photos = 'http://abc.com/a.png;http://abc.com/b.png'
const fileList = this.$refs.myPhoto.fileList
const successList = fileList.filter(item => item.status === 'success')
if (successList.length === 0) {
return this.$message.error('请上传所有照片')
}
// 上传成功
const photos = successList.map(item => item.url).join(';')
const ret = await reqUpdatePersonal({
...this.formData,
// 腾讯云多张图片地址
staffPhoto: photos,
// 当前修改的用户的id
id: this.userId
})
if (!ret.success) {
this.$message.error(ret.message)
} else {
this.$message.success(ret.message)
}
} catch {
this.$message.error('更新用户基本信息失败')
}
}
总结:上传员工的照片,支持多张照片上传
注意:默认照片数据类型是数组
员工列表显示图片
目标
:在员工列表中心显示图片员工的头像可以在列表项中添加一列来进行显示, 处理下图片异常
- 基于ElementUI提供的图片组件定制列表的员工头像
<el-table-column label="头像" prop="staffPhoto" sortable="">
<template v-slot='scope'>
<el-image class='staff' :src='scope.row.staffPhoto'>
<div slot="error">
<img class="staff" :src="defaultImage">
</div>
</el-image>
</template>
</el-table-column>
- 基于自定义指令封装
<el-table-column label="头像" prop="staffPhoto">
<template #default="scope">
<img v-imgerror="defaultImg" class="staff" :src="scope.row.staffPhoto" alt="">
</template>
</el-table-column>
data() {
return {
...
defaultImg: 'https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2146034403,1504718527&fm=26&gp=0.jpg'
}
},
<style lang="scss" scoped>
.employees-container {
.staff {
width: 70px;
height: 70px;
border-radius: 50%;
}
}
</style>
我们尝试用之前的指令来处理图片的异常问题,但是发现只有两三张图片能显示
这是因为有的员工的头像的地址为空(null),给img赋值空的src不能触发错误事件,针对这一特点,我们需要对指令进行升级
插入节点的钩子里面判断空, 然后在组件更新之后的钩子中同样判断空 (发送请求回来, 会赋值给img src, 需要重新错误检测)
https://cn.vuejs.org/v2/guide/custom-directive.html
export const imgerror = {
// 指的是指令所在元素被插入到页面中时执行的一个钩子
// el 指令所在的元素 img标签
// binding 指令相关的参数对象, 指令名, 指令值binding.value
inserted(el, binding) {
// 如果src没有赋值, 给默认src
el.src = el.src || binding.value
// console.log('指令所在元素, 被插入到页面中了')
el.onerror = function() {
// console.log('图片加载失败了, 重新指定一个有效的src')
el.src = binding.value
}
},
// 如果手动把src的值置空(删除头像),显示默认头像
componentUpdated(el, binding) {
// 如果src没有赋值, 给默认src
el.src = el.src || binding.value
}
}
总结:
- 作用域插槽定制列的模板
- 基于ElementUI提供的组件可以处理图片加载失败的情况
- 基于自定义指令方式也可以处理图片加载失败的情况
二维码生成
目标
基于 (图片地址 / 网页地址) 生成二维码二维码功能将来工作中也很常见, 我们需要根据信息 或者 链接地址, 生成一个二维码! 比如: 做地址分享, 做手机图片预览等
- 首先,需要安装生成二维码的插件
npm i qrcode
- qrcode的用法是(把info字符串转换为二维码图片,然后绘制到dom元素中:canvas/svg)
QrCode.toCanvas(dom, info)
dom为一个canvas的dom对象, info为转化二维码的信息
需求:点击头像,打开弹窗,显示二维码,扫码打开图片的链接。
点击图片-显示弹层
- 准备弹层
<!-- 分享展示, 预览的二维码的弹层 -->
<el-dialog title="二维码" :visible="showCodeDialog" @close="showCodeDialog = false">
二维码
</el-dialog>
- 注册点击事件
<el-table-column label="头像" prop="staffPhoto" sortable="">
<template v-slot='scope'>
<el-image @click='showCodeDialog=true' class='staff' :src='scope.row.staffPhoto'>
<div slot="error">
<img class="staff" :src="defaultImage">
</div>
</el-image>
</template>
</el-table-column>
总结:控制二维码弹窗的显示和隐藏。
二维码生成演示
- 我们尝试将canvas标签放到dialog的弹层中
<el-dialog width="300px" title="二维码" :visible="showCodeDialog" @close="showCodeDialog = false">
<el-row type="flex" justify="center">
<canvas ref="myCanvas" />
</el-row>
</el-dialog>
- 在点击员工的图片时,显示弹层,并将图片地址转化成二维码
import QrCode from 'qrcode'
clickShowCodeDialog(url) {
if (!url) return
this.showCodeDialog = true
this.$nextTick(() => {
// 如果这里 url 写的是网址, 就会跳转到对应网址 (二维码分享效果)
QrCode.toCanvas(this.$refs.myCanvas, url)
})
}
总结:
- 二维码的绘制方式(绘制图片到canvas画布上)
- 关于$nextTick用法的理解
打印功能
目标
完成个人信息和工作信息的打印功能
将来能够打印
新建打印页面 - 配置路由
- 配置路由
// 员工打印页
{
path: 'print/:id',
component: () => import('@/views/employees/print'),
hidden: true,
meta: {
title: '员工打印'
}
}
- 获取用户id
computed: {
userId () {
return this.$route.params.id
}
}
- 准备图标按钮
<el-tab-pane label="个人详情">
<el-tooltip class="tooltip-box" content="打印基本个人信息">
<router-link :to="`/employees/print/${userId}?type=personal`">
<i class="el-icon-printer" />
</router-link>
</el-tooltip>
<user-info />
</el-tab-pane>
<el-tab-pane label="岗位信息">
<el-tooltip class="tooltip-box" content="打印基本岗位信息">
<router-link :to="`/employees/print/${userId}?type=job`">
<i class="el-icon-printer" />
</router-link>
</el-tooltip>
<job-info />
</el-tab-pane>
- 样式
<style lang="scss" scoped>
.employees-detail-container {
.el-tab-pane {
padding-top: 10px;
}
.tooltip-box {
position: absolute;
right: 30px;
top: 10px;
z-index: 999;
}
}
</style>
- 创建页面组件 (已准备好)
<template>
<div id="myPrint" class="dashboard-container">
<div class="app-container">
<el-card>
<el-breadcrumb separator="/" class="titInfo ">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>
<router-link :to="{'path':'/employees'}">员工管理</router-link>
</el-breadcrumb-item>
<el-breadcrumb-item>打印</el-breadcrumb-item>
</el-breadcrumb>
<div v-if="type === 'personal'">
<h2 class="centInfo">员工信息表</h2>
<table cellspacing="0" width="100%" class="tableList">
<tr class="title">
<td colspan="8" class="centInfo">基本信息</td>
</tr>
<tr>
<th style="width:10%">姓名</th>
<td colspan="6" style="width:80%">{{ formData.username }}</td>
<td rowspan="5" style="width:10%"><img style="width:155px;height:218px" :src="formData.staffPhoto"></td>
</tr>
<tr>
<th>性别</th>
<td colspan="6">{{ formData.sex }}</td>
</tr>
<tr>
<th>手机</th>
<td colspan="6">{{ formData.mobile }}</td>
</tr>
<tr>
<th>出生日期</th>
<td colspan="6">{{ formData.dateOfBirth | formatDate }}</td>
</tr>
<tr>
<th>最高学历</th>
<td colspan="6">{{ formData.theHighestDegreeOfEducation }}</td>
</tr>
<tr>
<th style="width:10%">是否可编辑</th>
<td style="width:35%">{{ formData.isItEditable }}</td>
<th style="width:10%">是否隐藏号码</th>
<td colspan="5" style="width:45%">{{ formData.doYouHideNumbers }}</td>
</tr>
<tr>
<th>国家地区</th>
<td>{{ formData.nationalArea }}</td>
<th>护照号</th>
<td colspan="5">{{ formData.passportNo }}</td>
</tr>
<tr>
<th>身份证号</th>
<td>{{ formData.idNumber }}</td>
<th>身份证照片</th>
<td colspan="5">{{ formData.iDCardPhoto }}</td>
</tr>
<tr>
<th>籍贯</th>
<td>{{ formData.nativePlace }}</td>
<th>民族</th>
<td colspan="5">{{ formData.nation }}</td>
</tr>
<tr>
<th>英文名</th>
<td>{{ formData.englishName }}</td>
<th>婚姻状况</th>
<td colspan="5">{{ formData.maritalStatus }}</td>
</tr>
<tr>
<th>员工照片</th>
<td>{{ formData.staffPhoto }}</td>
<th>生日</th>
<td colspan="5">{{ formData.birthday }}</td>
</tr>
<tr>
<th>属相</th>
<td>{{ formData.zodiac }}</td>
<th>年龄</th>
<td colspan="5">{{ formData.age }}</td>
</tr>
<tr>
<th>星座</th>
<td>{{ formData.constellation }}</td>
<th>血型</th>
<td colspan="5">{{ formData.bloodType }}</td>
</tr>
<tr>
<th>户籍所在地</th>
<td>{{ formData.domicile }}</td>
<th>政治面貌</th>
<td colspan="5">{{ formData.politicalOutlook }}</td>
</tr>
<tr>
<th>入党时间</th>
<td>{{ formData.timeToJoinTheParty }}</td>
<th>存档机构</th>
<td colspan="5">{{ formData.archivingOrganization }}</td>
</tr>
<tr>
<th>子女状态</th>
<td>{{ formData.stateOfChildren }}</td>
<th>子女有无商业保险</th>
<td colspan="5">{{ formData.doChildrenHaveCommercialInsurance }}</td>
</tr>
<tr>
<th>有无违法违纪行为</th>
<td>{{ formData.isThereAnyViolationOfLawOrDiscipline }}</td>
<th>有无重大病史</th>
<td colspan="5">{{ formData.areThereAnyMajorMedicalHistories }}</td>
</tr>
<tr class="title">
<td colspan="8" class="centInfo">通讯信息</td>
</tr>
<tr>
<th>QQ</th>
<td>{{ formData.qQ }}</td>
<th>微信</th>
<td colspan="5">{{ formData.weChat }}</td>
</tr>
<tr>
<th>居住证城市</th>
<td>{{ formData.residenceCardCity }}</td>
<th>居住证办理日期</th>
<td colspan="5">{{ formData.dateOfResidencePermit }}</td>
</tr>
<tr>
<th>居住证截止日期</th>
<td>{{ formData.residencePermitDeadline }}</td>
<th>现居住地</th>
<td colspan="5">{{ formData.placeOfResidence }}</td>
</tr>
<tr>
<th>通讯地址</th>
<td>{{ formData.postalAddress }}</td>
<th>联系手机</th>
<td colspan="5">{{ formData.contactTheMobilePhone }}</td>
</tr>
<tr>
<th>个人邮箱</th>
<td>{{ formData.personalMailbox }}</td>
<th>紧急联系人</th>
<td colspan="5">{{ formData.emergencyContact }}</td>
</tr>
<tr>
<th>紧急联系电话</th>
<td colspan="7">{{ formData.emergencyContactNumber }}</td>
</tr>
<tr class="title">
<td colspan="8" class="centInfo">账号信息</td>
</tr>
<tr>
<th>社保电脑号</th>
<td>{{ formData.socialSecurityComputerNumber }}</td>
<th>公积金账号</th>
<td colspan="5">{{ formData.providentFundAccount }}</td>
</tr>
<tr>
<th>银行卡号</th>
<td>{{ formData.bankCardNumber }}</td>
<th>开户行</th>
<td colspan="5">{{ formData.openingBank }}</td>
</tr>
<tr class="title">
<td colspan="8" class="centInfo">教育信息</td>
</tr>
<tr>
<th>学历类型</th>
<td>{{ formData.educationalType }}</td>
<th>毕业学校</th>
<td colspan="5">{{ formData.graduateSchool }}</td>
</tr>
<tr>
<th>入学时间</th>
<td>{{ formData.enrolmentTime }}</td>
<th>毕业时间</th>
<td colspan="5">{{ formData.graduationTime }}</td>
</tr>
<tr>
<th>专业</th>
<td>{{ formData.major }}</td>
<th>毕业证书</th>
<td colspan="5">{{ formData.graduationCertificate }}</td>
</tr>
<tr>
<th>学位证书</th>
<td colspan="7">{{ formData.certificateOfAcademicDegree }}</td>
</tr>
<tr class="title">
<td colspan="8" class="centInfo">从业信息</td>
</tr>
<tr>
<th>上家公司</th>
<td>{{ formData.homeCompany }}</td>
<th>职称</th>
<td colspan="5">{{ formData.title }}</td>
</tr>
<tr>
<th>简历</th>
<td>{{ formData.resume }}</td>
<th>有无竞业限制</th>
<td colspan="5">{{ formData.isThereAnyCompetitionRestriction }}</td>
</tr>
<tr>
<th>前公司离职证明</th>
<td>{{ formData.proofOfDepartureOfFormerCompany }}</td>
<th>备注</th>
<td colspan="5">{{ formData.remarks }}</td>
</tr>
</table>
<div class="foot">签字:___________日期:___________</div>
</div>
<div v-else>
<h2 class="centInfo">岗位信息表</h2>
<table cellspacing="0" width="100%" class="tableList">
<tr class="title">
<td colspan="4" class="centInfo">基本信息</td>
</tr>
<tr>
<th style="width:10%">姓名</th>
<td style="width:40%">{{ formData.username }}</td>
<th style="width:10%">入职日期</th>
<td style="width:40%">{{ formData.dateOfEntry }}</td>
</tr>
<tr>
<th>部门</th>
<td>{{ formData.departmentName }}</td>
<th>岗位</th>
<td>{{ formData.post }}</td>
</tr>
<tr>
<th>工作邮箱</th>
<td>{{ formData.workMailbox }}</td>
<th>工号</th>
<td>{{ formData.workNumber }}</td>
</tr>
<tr>
<th>转正日期</th>
<td>{{ formData.dateOfCorrection }}</td>
<th>转正状态</th>
<td>{{ formData.stateOfCorrection }}</td>
</tr>
<tr>
<th>职级</th>
<td>{{ formData.rank }}</td>
<th>汇报对象</th>
<td>{{ formData.reportName }}</td>
</tr>
<tr>
<th>HRBP</th>
<td>{{ formData.hRBP }}</td>
<th>聘用形式</th>
<td>{{ formData.formOfEmployment }}</td>
</tr>
<tr>
<th>管理形式</th>
<td>{{ formData.formOfManagement }}</td>
<th>调整司龄</th>
<td>{{ formData.adjustmentAgedays }}</td>
</tr>
<tr>
<th>司龄</th>
<td>{{ formData.ageOfDivision }}</td>
<th>首次参加工作时间</th>
<td>{{ formData.workingTimeForTheFirstTime }}</td>
</tr>
<tr>
<th>调整工龄天</th>
<td>{{ formData.adjustmentOfLengthOfService }}</td>
<th>工龄</th>
<td>{{ formData.workingYears }}</td>
</tr>
<tr>
<th>纳税城市</th>
<td>{{ formData.taxableCity }}</td>
<th>转正评价</th>
<td>{{ formData.correctionEvaluation }}</td>
</tr>
<tr class="title">
<td colspan="4" class="centInfo">合同信息</td>
</tr>
<tr>
<th>首次合同开始时间</th>
<td>{{ formData.initialContractStartTime }}</td>
<th>首次合同结束时间</th>
<td>{{ formData.firstContractTerminationTime }}</td>
</tr>
<tr>
<th>现合同开始时间</th>
<td>{{ formData.currentContractStartTime }}</td>
<th>现合同结束时间</th>
<td>{{ formData.closingTimeOfCurrentContract }}</td>
</tr>
<tr>
<th>合同期限</th>
<td>{{ formData.contractPeriod }}</td>
<th>合同文件</th>
<td>{{ formData.contractDocuments }}</td>
</tr>
<tr>
<th>续签次数</th>
<td colspan="3">{{ formData.renewalNumber }}</td>
</tr>
<tr class="title">
<td colspan="4" class="centInfo">招聘信息</td>
</tr>
<tr>
<th>其他招聘渠道</th>
<td>{{ formData.otherRecruitmentChannels }}</td>
<th>招聘渠道</th>
<td>{{ formData.recruitmentChannels }}</td>
</tr>
<tr>
<th>社招校招</th>
<td>{{ formData.socialRecruitment }}</td>
<th>推荐企业人</th>
<td>{{ formData.recommenderBusinessPeople }}</td>
</tr>
</table>
<div class="foot">签字:___________日期:___________</div>
</div>
</el-card>
</div>
</div>
</template>
<script>
import { reqGetPersonalDetail, reqGetJobDetail } from '@/api/employees'
import { getDetailInfo as reqGetUserDetailById } from '@/api/user'
export default {
data() {
return {
formData: {}
}
},
computed: {
userId() {
return this.$route.params.id
},
type() {
return this.$route.query.type
}
},
// 创建完毕状态
created() {
if (this.type === 'personal') {
this.getPersonalDetail()
} else {
this.getJobDetail()
}
},
// 组件更新
methods: {
async getPersonalDetail() {
const { data: userInfo } = await reqGetUserDetailById(this.userId) // 获取个人基本信息(顶部)
const { data: detail } = await reqGetPersonalDetail(this.userId) // 获取个人基本信息(底部)
this.formData = { ...detail, ...userInfo }
},
async getJobDetail() {
const { data: userInfo } = await reqGetUserDetailById(this.userId)
const { data: jobInfo } = await reqGetJobDetail(this.userId) // 获取个人基本信息
this.formData = { ...jobInfo, ...userInfo }
}
}
}
</script>
<style lang="scss">
.foot {
padding: 30px 0;
text-align: right;
}
</style>
总结:
- 该页面内容实际上就是读取个人和详情的接口数据,根据传入的type类型决定显示个人还是岗位
- type为**
personal
时显示个人,为job
**时显示岗位注意:路由的参数传递方式
- Restful风格 this.$route.params.id
- 查询字符串风格 this.$route.query.type
利用vue-print-nb打印
- 首先,打印功能我们借助一个比较流行的插件 vue-print-nb
$ npm i vue-print-nb
它的用法是
- 首先注册该插件
import Print from 'vue-print-nb'
Vue.use(Print)
- 给要打印的盒子指定 id
<div id="printbox">
- 使用v-print指令的方式进行打印(指定id是printbox的容器被打印)
<div style="text-align: right; margin-top: 10px;">
<el-button v-print="{ id: 'printbox' }" type="primary" size="small">打印</el-button>
</div>
总结:基于第三方包实现打印功能
注意:第三方包扩展了一个指令 v-print