问题
-
file-input 如果选了同一个文件 不会触发 chang 事件 解决办法就是每次使用完毕 把他的 value 清空
this.$refs.file.value = '' -
上传头像请求参数传递:接口要求Content-Type 为 multipart/form-data
// 注意这里请求头的 Content-Type 是 multipart/form-data const formData = new FormData() formData.append('photo', blob) const { data } = await uploadUserPhoto(formData)
点击头像:选择文件 这样写,其中选择文件的事件通过: @change 绑定
头像上传弹出层:
第三方库:如果是纯客户端的图片裁切,则使用:getCroppedCanvas 方法
如果是基于服务端的裁切,则使用:getData 方法,该方法得到裁切的区域参数。
-
npm install cropperjs
-
加载css js
import 'cropperjs/dist/cropper.css'; import Cropper from 'cropperjs'; -
将图片img标签 放到一个div容器中
-
官方要求设置图片样式:是这个
img { display: block; /* This rule is very important, please don't ignore this */ max-width: 100%; } -
图片裁切:写在mounted中
import 'cropperjs/dist/cropper.css'; import Cropper from 'cropperjs'; mounted() { const image = this.$refs.image; this.cropper = new Cropper(image, { viewMode: 1, dragMode: 'move', aspectRatio: 1, autoCropArea: 1, cropBoxMovable: false, cropBoxResizable: false, background: false, movable: true }) },
父组件完整代码:
<template>
<div class="user-profile">
<van-nav-bar title="个人信息" class="page-nav-bar" left-arrow @click-left="$router.back()"></van-nav-bar>
<!-- 上传头像 -->
<input type="file" ref="file" hidden @change="onFileChange">
<van-cell title="头像" is-link @click="$refs.file.click()">
<van-image class="head" fit="cover" round :src="user.photo" />
</van-cell>
<van-cell title="昵称" :value="user.name" is-link @click="isShowNickName=true" />
<van-cell title="性别" :value="user.gender === 0 ? '男' : '女'" is-link @click="isShowGender=true" />
<van-cell title="生日" :value="user.birthday" is-link @click="isShowBirthday=true" />
<!-- 昵称弹出层 -->
<van-popup v-model="isShowNickName" style="height:100%" position="bottom">
<update-name v-if="isShowNickName" v-model="user.name" @close="isShowNickName = false"> </update-name>
</van-popup>
<!-- 性别弹出层 -->
<van-popup v-model="isShowGender" position="bottom">
<update-gender v-if="isShowGender" v-model="user.gender" @close="isShowGender = false"> </update-gender>
</van-popup>
<!-- 生日弹出层 -->
<van-popup v-model="isShowBirthday" position="bottom">
<!-- <update-birthday v-if="isShowBirthday" @close="isShowBirthday = false" v-model="user.birthday"> </update-birthday> -->
<update-birthday v-if="isShowBirthday" @close="isShowBirthday = false" v-model="user.birthday" />
</van-popup>
<!-- 头像弹出层 -->
<van-popup v-model="isShowPhoto" position="bottom" style="height:100%">
<!-- <update-birthday v-if="isShowBirthday" @close="isShowBirthday = false" v-model="user.birthday"> </update-birthday> -->
<update-photo v-if="isShowPhoto" :img="img" @close="isShowPhoto=false" @update-photo="user.photo=$event" />
</van-popup>
</div>
</template>
<script>
import { getUserProfile } from '@/api/user'
import UpdateName from './components/update-name'
import UpdateGender from './components/update-gender'
import UpdateBirthday from './components/update-birthday'
import UpdatePhoto from './components/update-photo'
export default {
name: "UserProfile",
components: { UpdateName, UpdateGender, UpdateBirthday, UpdatePhoto },
data() {
return {
user: {},
isShowNickName: false,
isShowGender: false,
isShowBirthday: false,
isShowPhoto: false,
img: null
}
},
created() {
this.loadUserProfile()
},
methods: {
async loadUserProfile() {
try {
const { data } = await getUserProfile()
console.log(data);
this.user = data.data
} catch (error) {
}
},
onFileChange() {
// 获取文件对象
const file = this.$refs.file.files[0]
// 基于问斩搞对象获取blob数据
this.img = window.URL.createObjectURL(file)
this.isShowPhoto = true
// change 事件问题 :file-input 如果选了同一个文件 不会触发 chang 事件
// 解决办法就是每次使用完毕 把他的 value 清空
this.$refs.file.value = ''
}
}
}
</script>
<style scoped lang="less">
.van-popup {
background-color: #f1f1f1;
}
.head {
width: 60px;
height: 60px;
}
</style>
子组件:完整代码:
<template>
<div class="update-photo">
<img class="img" :src="img" ref="image">
<div class="toolbar">
<div class="cancel" @click="$emit('close')">取消</div>
<div class="confirm" @click="onConfirm">完成</div>
</div>
</div>
</template>
<script>
import 'cropperjs/dist/cropper.css';
import Cropper from 'cropperjs';
import { uploadUserPhoto } from '@/api/user'
export default {
name: "update-photo",
props: {
img: {
type: [Object, String],
required: true
}
},
data() {
return {
cropper: null
}
},
mounted() {
const image = this.$refs.image;
this.cropper = new Cropper(image, {
viewMode: 1,
dragMode: 'move',
aspectRatio: 1,
autoCropArea: 1,
cropBoxMovable: false,
cropBoxResizable: false,
background: false,
movable: true
})
},
methods: {
onConfirm() {
// 基于服务端的裁切使用 getData 方法
// console.log(this.cropper.getData());
this.cropper.getCroppedCanvas().toBlob(blob => {
// console.log(blob);
// 发送请求
this.uploadUserPhoto(blob)
}
)
},
async uploadUserPhoto(blob) {
this.$toast.loading({
message: '上传中...',
forbidClick: true, // 禁用背景点击
duration: 0, // 持续展示 toast
})
try {
// 注意这里请求头的 Content-Type 是 multipart/form-data
const formData = new FormData()
formData.append('photo', blob)
const { data } = await uploadUserPhoto(formData)
// console.log(data);
// 更新图片
this.$emit('update-photo', data.data.photo)
// 关闭图层
this.$emit('close')
// 提示更新成功
this.$toast('头像更新成功')
} catch (error) {
this.$toast.fail('更新失败')
}
}
}
}
</script>
<style scoped lang="less">
.update-photo {
height: 100%;
background-color: #000;
.toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: space-between;
.cancel,
.confirm {
display: flex;
width: 90px;
height: 90px;
font-size: 30px;
justify-content: center;
align-items: center;
color: #fff;
}
}
}
.img {
display: block;
max-width: 100%;
}
</style>