HR-08-员工详情模块-腾讯云上传图片

516 阅读7分钟

员工详情

员工详情页

需求:重置密码;展示员工的个人信息和岗位信息(支持图片的上传----上传到第三方平台:腾讯云)

image.png

详情页的基本布局和路由

  • 建立详情页路由 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>

总结:配置路由;准备模板;控制跳转

获取用户信息

  • 发送请求获取数据
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
    }
  }
}

**注意**:这里接口中读取的是后端的密文,我们并不能解密,所以我们不回显原密码! 我们设定了一个临时的字段 newPassword,用它来存储我们的修改值,最后保存的时候,把newPassword 进行提交

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)
    })
},

总结:

  1. 提交表单时,需要提交输入的明文密码
  2. 对象解构赋值之后,可以通过同名的属性覆盖前面的属性

个人组件和岗位组件封装

封装个人详情组件

我们将员工个人信息分为三部分,账户,个人, 岗位,这个小节我们对个人组件和岗位组件进行封装 封装个人组件   **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的数据
<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/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 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 { reqGetUserDetailById } from '@/api/user'

created() {
  this.getUserBaseInfo()
  this.getUserDetailInfo()
},
methods: {
    // 获取员工基本信息(上面的表单)
    async getUserBaseInfo () {
      try {
        const ret = await reqGetUserDetailById(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('更新用户基本信息失败')
      }
    }
}

总结:

  1. 获取个人信息
  2. 保存个人信息

读取保存岗位信息

  • 读取,保存岗位信息  **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('更新岗位信息失败')
      }
    }
}

总结:

  1. 获取岗位信息
  2. 保存岗位信息

配置腾讯云Cos

image.png

账号注册 (实名认证)

**目标**: 配置一个腾讯云cos 由于上课的开发的特殊性,我们不希望把所有的图片都上传到我们自己的官方服务器上, 这里我们可以采用一个腾讯云的图片方案

image.png

上边图的意思就是说,我们找一个可以免费上传图片的服务器,帮我们**代管图片**,我们在自己的数据库里只保存一个地址就行, 这其实也是很多项目的处理方案,会有一个**公共的文件服务器**

第一步,我们必须先拥有一个腾迅云的开发者账号(有时会有腾讯云的广告电话)

请按照腾讯云的注册方式,注册自己的账号

第二步,实名认证

选择个人账户

image.png

填写个人身份信息

image.png

下一步,扫描二维码授权

image.png

手机端授权

image.png

总结:注册腾讯云账号;登录系统;进行实名认证(填充个人相关信息)

开通服务

  • 点击云产品 - 对象存储

image.png

  • 点击开通服务

image.png

创建存储桶

到这一步,账号的部分就操作完毕,接下来,我们需要来创建一个存储图片的存储桶 登录 对象存储控制台创建存储桶。设置存储桶的权限为 **公有读,私有写**

image.png

image.png

设置cors规则

image.png

AllowHeader 需配成*,如下图所示

image.png

因为我们本身没有域名,所以这里设置成*****,仅限于测试,正式环境的话,这里需要配置真实的域名地址

封装上传图片组件分析

**目标** 梳理整个的上传过程

image.png

初始化 cos 对象参数

名称描述
SecretId开发者拥有的项目身份识别 ID,用以身份认证,可在 API 密钥管理
页面获取
SecretKey开发者拥有的项目身份密钥,可在 API 密钥管理
页面获取

注意,上述的参数我们在本次开发过程中,直接将参数放置在前端代码中存储, 但是腾讯云本身是不建议这么做的,因为**敏感信息**放在前端很容易被捕获,一般放在后台, 前端准备参数, 交给后台和腾讯云交互, 交互时, 需要秘钥, 秘钥在后台存着的 由于我们本次是测试研发,所以这个过程可以忽略 正确的做法应该是,通过网站调用接口换取敏感信息

相关文档

实例化 上传sdk (这边还没用到, 先没配)

var cos = new COS({
    SecretId: 'AKIDbw8bAXta6HCsIh45ev0qXWJp9hT7uJ2Y', // 身份识别 ID
    SecretKey: 'Odg90oQ2EDwHhzELG8qidXB2UhHabonc', // 身份密钥
});

到目前为止,我们上传图片准备的内容就已经OK,接下来,我们在**src/componets** 新建一个**ImageUpload** 组件

该上传组件需要满足什么要求呢?

  1. 可以显示传入的图片 (本地预览)
  2. 可以删除传入的图片
  3. 可以上传图片到云服务器
  4. 上传到腾讯云之后,可以返回图片地址,覆盖显示
  5. 上传成功之后,可以回调成功函数

封装上传组件代码实现

**目标**实现上传组件的代码部分 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, // 控制显示弹层
    imgUrl: ''
  }
},
preview(file) {
  // 这里应该弹出一个层 层里是点击的图片地址
  this.imgUrl = file.url
  this.showDialog = true
},
  • 预览弹层
<el-dialog width="600px" top="8vh" title="图片预览" :visible.sync="showDialog">
  <img width="100%" :src="imgUrl" alt="">
</el-dialog>

总结:

  1. 显示选择的图片列表 file-list
  2. 预览图片 on-preview

根据上传数量控制上传按钮

需求:当选中的图片大于等于一张时隐藏加号效果

  • 控制上传显示
computed: {
  // 设定一个计算属性 判断是否已经上传完了一张
  isMultiple () {
    return this.fileList.length >= 1
  }
},
  • 模板布局
<el-upload
  list-type="picture-card"
  :file-list="fileList"
  :on-preview="preview"
  :limit="1"
  action=""
  :class='{"hide-plus": isMultiple}'
>
  <i class="el-icon-plus" />
</el-upload>

<style lang="scss" scoped>
.hide-plus {
  ::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"
  :class="{disabled: fileComputed }"
  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
},

总结:

  1. 监听删除的动作 on-remove
  2. 删除事件的回调参数一表示要删除的文件信息

添加操作

  • 点击 + 号,  进行上传动作,  会触发 el-upload的http-request属性配的函数
:http-request="upload"

// 自定义上传动作 有个参数 有个file对象
handleUpload(params) {
  // 进行上传操作
  console.log(params.file)
}
  • 添加文件时, 将页面的 fileList 同步到 this.fileList (数据中)
// 添加文件, 用户选了就应该新增文件预览
handleChange(file, fileList) {
  // console.log(fileList)
  // console.log(fileList.length)
  this.fileList = fileList
},

总结

  1. 监听选中文件的事件 on-change
  2. 如果想手动上传文件,而不是默认上传给action,那么需要配置 http-request,该配置函数中可以进行手动上传操作

上传之前校验检查

控制上传图片的类型和上传大小, 如果不满足条件 返回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)
}

我们需要在该方法中,调用腾讯云的上传方法

腾讯云文档地址

身份ID和密钥可以通过腾讯云平台获取

登录 访问管理控制台 ,获取您的项目 SecretId 和 SecretKey。

image.png

import COS from 'cos-js-sdk-v5' // 导入腾讯云的包(sdk)
const cos = new COS({
  SecretId: 'AKIDe8LHxOcZHhqjLglZ6OREk0zra5MOAhyy', // 身份识别ID
  SecretKey: 'lFUUDfqqa8PQAdokPnulLegRwA8qITN0' // 身份秘钥
})


// 进行上传操作
handleUpload(params) {
  // console.log(params.file)
  if (params.file) {
    // 执行上传操作
    cos.putObject({
     Bucket: 'wh-1306446112', /* 存储桶 */
     Region: 'ap-beijing', /* 存储桶所在地域,必须字段 */
      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: 'AKIDbw8bAXta6HCsIh45ev0qXWJp9hT7uJ2Y', // 身份识别 ID
  SecretKey: 'Odg90oQ2EDwHhzELG8qidXB2UhHabonc' // 身份密钥
})
  • 配置相关的请求参数
handleUpload (params) {
  cos.putObject({
    // 存储桶名称
   Bucket: 'wh-1306446112',
    // 存储桶所在地址
    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)
  })
},

总结

  1. 这种上传方式仅仅用于测试,不太安全(秘钥容易泄露)
  2. 正规的流程应该把秘钥存储在后端服务器中

上传成功之后处理返回数据

如何处理返回成功的返回数据, 应该在上传完图片, 得到图片地址之后,

  • 需要做的事情:
  1. 更新当前上传的对应图片的 status  => 更新成 success
  2. 更新当前上传的对应图片的 url        => 更新成腾讯云拿到的  Location (拼上前缀)

问题: this.fileList 是一个数组, 我怎么知道更新谁呢 ?  params.file.uid 就是唯一标识 解决: 通过 params.file.uid 决定修改 this.fileList 中的哪个对象

  • 处理返回数据
 handleUpload (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('上传图片失败')
    }
  })
}

总结

  1. 腾讯云返回上传的结果后需要更新图片的地址和状态
  2. 提交到我们的服务端

上传的进度条显示

为了再上传图片过程中显示进度条,我们可以使用element-ui的进度条显示当前的上传进度

  • 放置进度条
<!-- 进度条组件 -->
<div v-if="isShow">上传进度:<el-progress style="width: 180px;" :percentage="percent" /></div>
  • 通过腾讯云sdk监听上传进度
// 执行上传操作
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))
      // 显示进度条
      this.isShow = true
      this.percent = progressData.percent * 100
      if (progressData.percent === 1) {
        // 进度完成
        this.isShow = false
        this.percent = 0
      }
    }
}
  • 最后上传完, 延迟500毫秒关闭
handleUpload(params) {
  if (params.file) {
    this.isShow = true
    // 执行上传操作
    ...
    }, (err, data) => {
      ...
      setTimeout(() => {
        this.isShow = false
        this.percent = 0
      }, 500)
    })
  }
}

总结:基于腾讯云API的回调监控上传的进度

在员工详情中应用上传组件

配置上传图片组件的数量

  • 组件接收参数
  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="{'hide-plus': isMultiple}"
>
  <i class="el-icon-plus" />
</el-upload>
  • 父组件传入数量
<image-upload :limit="1"/>

总结:如果有相关信息需要定制,需要抽取组件的属性

员工的头像

**user-info.vue**中放置上传组件

  • 模板布局
<!-- 员工照片 -->
<el-row class="inline-info">
  <el-col :span="12">
    <el-form-item label="员工头像">
      <image-upload ref="myAvatar" :limit="1" />
    </el-form-item>
  </el-col>
</el-row>
  • 读取时初始化头像
// 获取员工基本信息(上面的表单)
async getUserBaseInfo () {
  try {
    const ret = await reqGetUserDetailById(this.userId)
    if (!ret.success) {
      this.$message.error(ret.message)
    } else {
      this.userInfo = ret.data
      if (this.userInfo.staffPhoto) {
        // 证明原来有头像数据,初始化头像效果
        this.$refs.myAvatar.fileList = [{
          url: this.userInfo.staffPhoto
        }]
      }
    }
  } catch {
    this.$message.error('获取用户基本信息失败')
  }
},
  • 当点击保存更新时,获取图片的内容,  进行提交
async saveUser() {
  // 去读取 员工上传的头像
  const fileList = this.$refs.staffPhoto.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="员工照片">
  <!-- ref不要重名 -->
  <image-upload ref="myPhoto" :default-img="formData.staffPhoto" :limit="2" />
</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) {
        const photos = this.formData.staffPhoto.split(';')
        if (photos.length > 0) {
          this.$refs.myPhoto.fileList = photos.map(item => {
            return {
              url: item
            }
          })
        }
      }
    }
  } 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 {
        if (!this.$refs.myPhoto.isAllSuccess) {
          this.$message.error('请等待上传图片成功后再提交表单')
          return
        }
        // 处理选中的员工照片
        const fileList = this.$refs.myPhoto.fileList
        const photos = fileList.map(item => {
          return item.url
        })
        const ret = await reqUpdatePersonal({
          ...this.formData,
          // 定制照片数据格式
          staffPhoto: photos.join(';'),
          // 当前修改的用户的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, 需要重新错误检测)

cn.vuejs.org/v2/guide/cu…

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
  }
}

总结:

  1. 作用域插槽定制列的模板
  2. 基于ElementUI提供的组件可以处理图片加载失败的情况
  3. 基于自定义指令方式也可以处理图片加载失败的情况

二维码生成

**目标** 基于 (图片地址 /  网页地址) 生成二维码 二维码功能将来工作中也很常见, 我们需要根据信息 或者 链接地址, 生成一个二维码! 比如: 做地址分享, 做手机图片预览等

image.png

  • 首先,需要安装生成二维码的插件
npm i qrcode
  • qrcode的用法是(把info字符串转换为二维码)
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='previewImg(scope.row.staffPhoto)' 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'

previewImg (url) {
    if (!url) return
    this.showCodeDialog = true
    // 把url转换为二维码并绘制到canvas画布中
    // 注意:这里无法直接获取this.$refs.myCanvas元素的
    this.$nextTick(() => {
      // 如果这里url写的是网址,就会跳转到对应的网址(二维码分享效果)
      QrCode.toCanvas(this.$refs.myCanvas, url)
    })
  },

总结:

  1. 二维码的绘制方式
  2. 关于$nextTick用法的理解