员工详情页创建和布局
目标
:创建员工详情的主要布局页面和基本布局
详情页的基本布局和路由
建立详情页路由
{ path: 'detail/:id', // query传参 动态路由传参 component: () => import('@/views/employees/detail'), hidden: true, // 不在左侧菜单显示 meta: { title: '员工详情' // 标记当前路由规则的中文名称 后续在做左侧菜单时 使用 } }
建立基本架构
<div class="dashboard-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 label="岗位信息" /> </el-tabs> </el-card> </div> </div>
列表跳转到详情
<el-button type="text" size="small" @click="$router.push(`/employees/detail/${ row.id}`)">查看</el-button>
读取和保存用户信息的接口
加载个人基本信息 > 该接口已经在之前提供过了 src/api/user.js
/** * * 获取某个用户的基本信息 * * ***/ export function getUserDetailById(id) { return request({ url: `/sys/user/${id}` }) }
保存个人基本信息 src/api/employees.js
/** * * * 保存员工的基本信息 * **/ export function saveUserDetailById(data) { return request({ url: `/sys/user/${data.id}`, method: 'put', data }) }
实现用户名和密码的修改
注意
:这里有个缺陷,接口中读取的是后端的密文,我们并不能解密,所以当我们没有任何修改就保存时,会校验失败,因为密文超过了规定的12位长度,为了真的修改密码,我们设定了一个临时的字段 password2,用它来存储我们的修改值,最后保存的时候,把password2传给password
用户名和密码的修改 src/views/employees/detail.vue
import { getUserDetailById } from '@/api/user' import { saveUserDetailById } from '@/api/employees' export default { data() { return { userId: this.$route.params.id, // 这样可以后面直接通过 this.userId进行获取数据 userInfo: { // 专门存放基本信息 username: '', password2: '' }, rules: { username: [{ required: true, message: '姓名不能为空', trigger: 'blur' }], password2: [{ required: true, message: '密码不能为空', trigger: 'blur' }, { min: 6, max: 9, message: '密码长度6-9位', trigger: 'blur' }] } } }, created() { this.getUserDetailById() }, methods: { async getUserDetailById() { this.userInfo = await getUserDetailById(this.userId) }, async saveUser() { try { // 校验 await this.$refs.userForm.validate() await saveUserDetailById({ ...this.userInfo, password: this.userInfo.password2 }) // 将新密码的值替换原密码的值 this.$message.success('保存成功') } catch (error) { console.log(error) } } } }
绑定表单数据
<!-- 放置表单 --> <el-form ref="userForm" :model="userInfo" :rules="rules" label-width="120px" style="margin-left: 120px; margin-top:30px"> <el-form-item label="姓名:" prop="username"> <el-input v-model="userInfo.username" style="width:300px" /> </el-form-item> <el-form-item label="新密码:" prop="password2"> <el-input v-model="userInfo.password2" style="width:300px" type="password" /> </el-form-item> <el-form-item> <el-button type="primary" @click="saveUser">更新</el-button> </el-form-item> </el-form>
提交代码
个人组件和岗位组件封装
封装个人详情组件
封装个人组件 src/views/employees/components/user-info.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的数据
import EmployeeEnum from '@/api/constant/employees' export default { data() { return { userId: this.$route.params.id, EmployeeEnum, // 员工枚举数据 userInfo: {}, 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: '' // 备注 } } } }
在detail.vue组件中,导入注册并使用
<el-tab-pane label="个人详情"> <!-- 放置个人详情 --> <component :is="userComponent" /> <!-- <user-info /> --> </el-tab-pane>
在以上代码中,我们使用了动态组件component,它通过 is
属性来绑定需要显示在该位置的组件,is属性可以直接为注册组件
的组件名称即可
import UserInfo from './components/user-info' // 导入 components: { UserInfo // 注册 } data(){ return { ..., // 其他变量 userComponent:'UserInfo' } }
封装岗位组件
同理,封装岗位组件
封装岗位组件 src/views/employee/components/job-info.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 depts" :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 depts" :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>
定义岗位数据
import EmployeeEnum from '@/api/constant/employees' export default { data() { return { userId: this.$route.params.id, depts: [], 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: '' // 首次参加工作时间 } } } }
在detail.vue组件中,注册并使用
<el-tab-pane label="岗位详情"> <!-- 放置岗位详情 --> <component :is="JobInfo" /> </el-tab-pane>
import JobInfo from './components/job-info' // 导入 components: { JobInfo // 注册 } data(){ return { ..., // 其他变量 JobInfo:'JobInfo' } }
本节任务
:完成个人组件和岗位组件封装
员工个人信息和岗位信息-读取-保存
目标
:实现个人信息的岗位信息的读取和校验,保存
读取个人保存个人信息
封装 读取个人信息 保存个人信息 读取岗位信息 保存岗位信息
/** * * 读取用户详情的基础信息 * **/ export function getPersonalDetail(id) { return request({ url: `/employees/${id}/personalInfo` }) } /** * * 更新用户详情的基础信息 * **/ export function updatePersonal(data) { return request({ url: `/employees/${data.userId}/personalInfo`, method: 'put', data }) } /** ** * 获取用户的岗位信息 * * ****/ export function getJobDetail(id) { return request({ url: `/employees/${id}/jobs` }) } /** * 保存岗位信息 * ****/ export function updateJob(data) { return request({ url: `/employees/${data.userId}/jobs`, method: 'put', data }) }
读取,保存个人信息 user-info
需要注意:这里的保存实际上分成了两个接口,这是接口的设计,我们只能遵守
import { getPersonalDetail, updatePersonal, saveUserDetailById } from '@/api/employees' import { getUserDetailById } from '@/api/user' created() { this.getPersonalDetail() this.getUserDetailById() }, methods: { async getPersonalDetail() { this.formData = await getPersonalDetail(this.userId) // 获取员工数据 }, async savePersonal() { await updatePersonal({ ...this.formData, id: this.userId }) this.$message.success('保存成功') }, async saveUser() { // 调用父组件 await saveUserDetailById(this.userInfo) this.$message.success('保存成功') }, async getUserDetailById() { this.userInfo = await getUserDetailById(this.userId) } }
读取保存岗位信息
读取,保存岗位信息 job-info
import { getEmployeeSimple, updateJob, getJobDetail } from '@/api/employees' created() { this.getJobDetail() this.getEmployeeSimple() }, methods: { async getJobDetail() { this.formData = await getJobDetail(this.userId) }, // 获取员工列表 async getEmployeeSimple() { this.depts = await getEmployeeSimple() }, // 保存岗位信息 async saveJob() { await updateJob(this.formData) this.$message.success('保存岗位信息成功') } }
提交代码
本节任务
实现个人信息的岗位信息的读取和校验,保存
配置腾讯云Cos
目标
: 配置一个腾讯云cos
第一步,我们必须先拥有一个腾迅云的开发者账号(小心腾讯云的广告电话)
第二步,实名认证
填写个人身份信息
下一步,扫描二维码授权
手机端授权
点击领取免费产品
选择对象存储COS
点击0元试用,开通服务
到这一步,账号的部分就操作完毕,接下来,我们需要来创建一个存储图片的存储桶
登录 对象存储控制台 ,创建存储桶。设置存储桶的权限为 公有读,私有写
设置cors规则
AllowHeader 需配成*
,如下图所示。
到这里,我们的腾讯云存储桶就设置好了。
封装上传图片组件-上传组件需求分析
目标
梳理整个的上传过程
初始化cos对象参数
名称 | 描述 |
---|---|
SecretId | 开发者拥有的项目身份识别 ID,用以身份认证,可在 API 密钥管理 页面获取 |
SecretKey | 开发者拥有的项目身份密钥,可在 API 密钥管理 页面获取 |
相关文档
实例化 上传sdk
var cos = new COS({ SecretId: 'COS_SECRETID', // 身份识别 ID SecretKey: 'COS_SECRETKEY', // 身份密钥 });
该组件需要满足什么要求呢?
-
可以显示传入的图片地址
-
可以删除传入的图片地址
-
可以上传图片到云服务器
-
上传到腾讯云之后,可以返回图片地址,显示
-
上传成功之后,可以回调成功函数
这个上传组件简单吗?
no ! ! !
看似需求很明确,但是它真正的实现很复杂,我们通过一个图来看一下
从上图中,我们可以看到,实际上是有两种场景的,本地场景和已经上传的场景
下个章节,针对这个场景我们进行开发
封装上传组件-代码实现
目标
实现上传组件的代码部分
新建文件上传组件
安装JavaScript SDK
$ npm i cos-js-sdk-v5 --save
新建上传图片组件 src/components/ImageUpload/index.vue
放置el-upload组件
<template> <el-upload list-type="picture-card"> <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> <el-upload list-type="picture-card" :limit="1" action="#"> </el-upload> </template>
预览
data() { return { fileList: [], // 图片地址设置为数组 showDialog: false, // 控制显示弹层 imgUrl: '' } },
preview(file) { // 这里应该弹出一个层 层里是点击的图片地址 this.imgUrl = file.url this.showDialog = true },
预览弹层
<el-dialog title="图片" :visible.sync="showDialog"> <img :src="imgUrl" style="width:100%" alt=""> </el-dialog>
根据上传数量控制上传按钮
控制上传显示
computed: { // 设定一个计算属性 判断是否已经上传完了一张 fileComputed() { return this.fileList.length === 1 } },
<el-upload :on-preview="preview" :on-remove="handleRemove" :on-change="changeFile" :file-list="fileList" list-type="picture-card" action="#" :limit="1" :class="{disabled: fileComputed }" > <style> .disabled .el-upload--picture-card { display: none } </style>
删除图片和添加图片
删除文件
handleRemove(file) { // file是点击删除的文件 // 将原来的文件给排除掉了 剩下的就是最新的数组了 this.fileList = this.fileList.filter(item => item.uid !== file.uid) },
添加文件
// 修改文件时触发 // 此时可以用fileList 因为该方法会进来很多遍 不能每次都去push // fileList因为fileList参数是当前传进来的最新参数 我们只需要将其转化成数组即可 需要转化成一个新的数组 // [] => [...fileList] [] => fileList.map() // 上传成功之后 还会进来 需要实现上传代码的逻辑 这里才会成功 changeFile(file, fileList) { this.fileList = fileList.map(item => item) }
上传之前检查
beforeUpload(file) { // 要开始做文件上传的检查了 // 文件类型 文件大小 const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png'] if (!types.includes(file.type)) { this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!') return false } // 检查大小 const maxSize = 5 * 1024 * 1024 if (maxSize < file.size) { this.$message.error('图片大小最大不能超过5M') return false } return true }
上传动作调用上传腾讯云
上传动作为el-upload的http-request属性
:http-request="upload" // 自定义上传动作 有个参数 有个file对象,是我们需要上传到腾讯云服务器的内容 upload(params) { console.log(params.file) }
腾讯云文档地址
身份ID和密钥可以通过腾讯云平台获取
登录 访问管理控制台 ,获取您的项目 SecretId 和 SecretKey。
实现代码
// 进行上传操作 upload(params) { // console.log(params.file) if (params.file) { // 执行上传操作 cos.putObject({ Bucket: 'shuiruohanyu-106-1302806742', // 存储桶 Region: 'ap-beijing', // 地域 Key: params.file.name, // 文件名 Body: params.file, // 要上传的文件对象 StorageClass: 'STANDARD' // 上传的模式类型 直接默认 标准模式即可 // 上传到腾讯云 =》 哪个存储桶 哪个地域的存储桶 文件 格式 名称 回调 }, function(err, data) { // data返回数据之后 应该如何处理 console.log(err || data) }) } }
上传成功之后处理返回数据
确定要上传记录id
beforeUpload(file) { // 先检查文件类型 const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png'] if (!types.some(item => item === file.type)) { // 如果不存在 this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!') return false // 上传终止 } // 检查文件大小 5M 1M = 1024KB 1KB = 1024B const maxSize = 5 * 1024 * 1024 if (file.size > maxSize) { // 超过了限制的文件大小 this.$message.error('上传的图片大小不能大于5M') return false } // 已经确定当前上传的就是当前的这个file了 this.currentFileUid = file.uid // 【新增】 return true // 最后一定要return true },
// 进行上传操作 upload(params) { // console.log(params.file) if (params.file) { // 执行上传操作 cos.putObject({ Bucket: 'shuiruohanyu-106-1302806742', // 存储桶 Region: 'ap-beijing', // 地域 Key: params.file.name, // 文件名 Body: params.file, // 要上传的文件对象 StorageClass: 'STANDARD' // 上传的模式类型 直接默认 标准模式即可 // 上传到腾讯云 =》 哪个存储桶 哪个地域的存储桶 文件 格式 名称 回调 }, (err, data) => { // data返回数据之后 应该如何处理 console.log(err || data) // data中有一个statusCode === 200 的时候说明上传成功 if (!err && data.statusCode === 200) { // 此时说明文件上传成功 要获取成功的返回地址 // fileList才能显示到上传组件上 此时我们要将fileList中的数据的url地址变成 现在上传成功的地址 // 目前虽然是一张图片 但是请注意 我们的fileList是一个数组 // 需要知道当前上传成功的是哪一张图片 this.fileList = this.fileList.map(item => { // 去找谁的uid等于刚刚记录下来的id if (item.uid === this.currentFileUid) { // 将成功的地址赋值给原来的url属性 return { url: 'http://' + data.Location, upload: true } // upload 为true 表示这张图片已经上传完毕 这个属性要为我们后期应用的时候做标记 // 保存 => 图片有大有小 => 上传速度有快又慢 =>要根据有没有upload这个标记来决定是否去保存 } return item }) // 将上传成功的地址 回写到了fileList中 fileList变化 =》 upload组件 就会根据fileList的变化而去渲染视图 } }) } }
上传的进度条显示
放置进度条
<el-progress v-if="showPercent" style="width: 180px" :percentage="percent" />
data定义变量
percent: 0, showPercent: false // 默认不显示进度条
通过腾讯云sdk监听上传进度
cos.putObject({ // 配置 Bucket: 'laogao-1302806742', // 存储桶名称 Region: 'ap-guangzhou', // 存储桶地域 Key: params.file.name, // 文件名作为key StorageClass: 'STANDARD', // 此类写死 Body: params.file, // 将本地的文件赋值给腾讯云配置 // 进度条 onProgress: (params) => { this.percent = params.percent * 100 } }
完整代码
<template> <div> <!-- 放置一个上传组件 --> <!-- action这里不写上传地址 因为我们是调用腾讯云cos 不是一个地址可以搞定的 要用自定义的上传 --> <el-upload :on-preview="preview" :on-remove="handleRemove" :on-change="changeFile" :before-upload="beforeUpload" :file-list="fileList" :http-request="upload" list-type="picture-card" action="#" :limit="1" :class="{disabled: fileComputed }" > <i class="el-icon-plus" /> </el-upload> <!-- 进度条 --> <el-progress v-if="showPercent" style="width: 180px" :percentage="percent" /> <!-- 放置一个弹层 --> <!-- sync修饰符自动将弹层关闭了 --> <el-dialog title="图片" :visible.sync="showDialog"> <img :src="imgUrl" style="width:100%" alt=""> </el-dialog> </div> </template> <script> import COS from 'cos-js-sdk-v5' // 引入腾讯云的包 // 需要实例化 const cos = new COS({ SecretId: 'AKID0mqfEWqlUzIbeSkGRL6c7ML6c0B93To9', SecretKey: 'JFwNZdeRF2iOp03FFsGNDm44vWFitmNF' }) // 实例化的包 已经具有了上传的能力 可以上传到该账号里面的存储桶了 export default { data() { return { fileList: [], showDialog: false, // 控制图片的显示或者隐藏 imgUrl: '', // 存储点击的图片地址 currentFileUid: '', // 用一个变量 记住当前上传的图片id percent: 0, showPercent: false // 默认不显示进度条 } }, computed: { // 设定一个计算属性 判断是否已经上传完了一张 fileComputed() { return this.fileList.length === 1 } }, methods: { preview(file) { // 这里应该弹出一个层 层里是点击的图片地址 this.imgUrl = file.url this.showDialog = true }, handleRemove(file) { // file是点击删除的文件 // 将原来的文件给排除掉了 剩下的就是最新的数组了 this.fileList = this.fileList.filter(item => item.uid !== file.uid) }, // 修改文件时触发 // 此时可以用fileList 因为该方法会进来很多遍 不能每次都去push // fileList因为fileList参数是当前传进来的最新参数 我们只需要将其转化成数组即可 需要转化成一个新的数组 // [] => [...fileList] [] => fileList.map() // 上传成功之后 还会进来 需要实现上传代码的逻辑 这里才会成功 changeFile(file, fileList) { this.fileList = fileList.map(item => item) }, beforeUpload(file) { // 要开始做文件上传的检查了 // 文件类型 文件大小 const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png'] if (!types.includes(file.type)) { this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!') return false } // 检查大小 const maxSize = 5 * 1024 * 1024 if (maxSize < file.size) { this.$message.error('图片大小最大不能超过5M') return false } // file.uid this.currentFileUid = file.uid // 记住当前的uid this.showPercent = true return true }, // 自定义上传动作 有个参数 有个file对象,是我们需要上传到腾讯云服务器的内容 upload(params) { if (params.file) { // 上传文件到腾讯云 COS.putObject({ // 配置 Bucket: 'laogao-1302806742', // 存储桶名称 Region: 'ap-guangzhou', // 存储桶地域 Key: params.file.name, // 文件名作为key StorageClass: 'STANDARD', // 此类写死 Body: params.file, // 将本地的文件赋值给腾讯云配置 // 进度条 onProgress: (params) => { this.percent = params.percent * 100 } }, (err, data) => { // 需要判断错误与成功 if (!err && data.statusCode === 200) { // 如果没有失败表示成功了 // 此时认为上传成功了 // this.currentFileUid // 仍然有个小问题, 比如此时我们正在上传,但是调用了保存,保存在上传过程中进行, // 此时上传还没有完成 此时可以这样做 : 给所有上传成功的图片 加一个属性 upload: true this.fileList = this.fileList.map(item => { if (item.uid === this.currentFileUid) { // upload为true表示 该图片已经成功上传到服务器,地址已经是腾讯云的地址了 就不可以执行保存了 return { url: 'http://' + data.Location, upload: true } // 将本地的地址换成腾讯云地址 } return item }) setTimeout(() => { this.showPercent = false // 隐藏进度条 this.percent = 0 // 进度归0 }, 2000) // 将腾讯云地址写入到fileList上 ,保存的时候 就可以从fileList中直接获取图片地址 // 此时注意,我们应该记住 当前上传的是哪个图片 上传成功之后,将图片的地址赋值回去 } }) } } } } </script> <style> .disabled .el-upload--picture-card { display: none } </style>
通过上面的代码,我们会发现,我们把上传之后的图片信息都给了fileList数据
,那么在应用时,就可以直接获取该实例的fileList数据即可
提交代码
本节任务
完成上传组件的封装
在员工详情中应用上传组件
目标
:应用封装好的上传组件