feat: 后台头像及编辑

This commit is contained in:
From-wh 2023-04-27 11:38:55 +08:00
parent ff1d202bea
commit 3f4396598a
6 changed files with 332 additions and 4 deletions

View File

@ -60,6 +60,7 @@
"vue-awesome-swiper": "^4.1.1",
"vue-clipboard2": "^0.3.3",
"vue-codemirror": "^4.0.6",
"vue-cropper": "^0.5.11",
"vue-happy-scroll": "^2.1.1",
"vue-i18n": "^7.8.0",
"vue-pickers": "^2.5.3",

View File

@ -9,6 +9,7 @@
// +----------------------------------------------------------------------
import request from '@/libs/request';
import { getCookies } from '@/libs/util';
/**
* @description 设置 系统设置 应用设置头部
@ -1122,3 +1123,17 @@ export function codeCrud(data) {
data,
});
}
/**
* @description 图片上传
*/
export function fileUpload(data) {
return request({
url: `file/upload`,
method: 'post',
headers: {
'Authori-zation': 'Bearer ' + getCookies('token'),
'content-type': 'multipart/form-data;' + 'Bearer ' + getCookies('token'),
},
data,
});
}

View File

@ -0,0 +1,280 @@
<template>
<div class="cropper-content">
<div class="cropper-box">
<div class="cropper">
<vue-cropper
ref="cropper"
:img="option.img"
:outputSize="option.outputSize"
:outputType="option.outputType"
:info="option.info"
:canScale="option.canScale"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixed="option.fixed"
:fixedNumber="option.fixedNumber"
:full="option.full"
:fixedBox="option.fixedBox"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:centerBox="option.centerBox"
:height="option.height"
:infoTrue="option.infoTrue"
:maxImgSize="option.maxImgSize"
:enlarge="option.enlarge"
:mode="option.mode"
@realTime="realTime"
@imgLoad="imgLoad"
>
</vue-cropper>
</div>
<!--底部操作工具按钮-->
<div class="footer-btn">
<div class="scope-btn">
<input
type="file"
id="uploads"
style="position: absolute; clip: rect(0 0 0 0)"
accept="image/png, image/jpeg, image/gif, image/jpg"
@change="selectImg($event)"
/>
<el-button size="mini" type="danger" plain icon="el-icon-zoom-in" @click="changeScale(1)">放大</el-button>
<el-button size="mini" type="danger" plain icon="el-icon-zoom-out" @click="changeScale(-1)">缩小</el-button>
<el-button size="mini" type="danger" plain @click="rotateLeft">↺ 左旋转</el-button>
<el-button size="mini" type="danger" plain @click="rotateRight">↻ 右旋转</el-button>
</div>
</div>
</div>
<!--预览效果图-->
<div class="show-preview">
<div class="preview">
<img :src="previews.url" :style="previews.img" />
</div>
<div class="upload-btn">
<label class="btn" for="uploads">选择图片</label>
<el-button size="mini" type="success" @click="uploadImg()">确认上传</el-button>
</div>
</div>
</div>
</template>
<script>
import { VueCropper } from 'vue-cropper';
// import { updateAvatar } from ''; //这里为文件上传的接口换成自己的文件
import { fileUpload } from '@/api/setting';
export default {
name: 'CropperImage',
components: {
VueCropper,
},
props: ['Name'],
data() {
return {
name: this.Name,
resImg: '',
previews: {},
option: {
img: this.Name, //裁剪图片的地址
outputSize: 1, //裁剪生成图片的质量(可选0.1 - 1)
outputType: 'png', //裁剪生成图片的格式(jpeg || png || webp)
info: true, //图片大小信息
canScale: true, //图片是否允许滚轮缩放
autoCrop: true, //是否默认生成截图框
autoCropWidth: 200, //默认生成截图框宽度
autoCropHeight: 200, //默认生成截图框高度
fixed: false, //是否开启截图框宽高固定比例
fixedNumber: [1.53, 1], //截图框的宽高比例
full: false, //false按原比例裁切图片,不失真
fixedBox: false, //固定截图框大小,不允许改变
canMove: true, //上传图片是否可以移动
canMoveBox: true, //截图框能否拖动
original: false, //上传图片按照原始比例渲染
centerBox: true, //截图框是否被限制在图片里面
height: false, //是否按照设备的dpr 输出等比例图片
infoTrue: false, //true为展示真实输出图片宽高,false展示看到的截图框宽高
maxImgSize: 3000, //限制图片最大宽度和高度
enlarge: 1, //图片根据截图框输出比例倍数
mode: '300px 300px', //图片默认渲染方式
},
};
},
methods: {
//初始化函数
imgLoad(msg) {
console.log('工具初始化函数=====' + msg);
},
//图片缩放
changeScale(num) {
num = num || 1;
this.$refs.cropper.changeScale(num);
},
//向左旋转
rotateLeft() {
this.$refs.cropper.rotateLeft();
},
//向右旋转
rotateRight() {
this.$refs.cropper.rotateRight();
},
// //实时预览函数
realTime(data) {
let that = this;
that.previews = data;
this.$refs.cropper.getCropBlob((data) => {
this.blobToDataURI(data, function (res) {
that.previewImg = res;
});
});
},
blobToDataURI(blob, callback) {
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = function (e) {
callback(e.target.result);
};
},
//选择图片
selectImg(e) {
let file = e.target.files[0];
if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
this.$message({
message: '图片类型要求:jpeg、jpg、png',
type: 'error',
});
return false;
}
//转化为blob
let reader = new FileReader();
reader.onload = (e) => {
let data;
if (typeof e.target.result === 'object') {
data = window.URL.createObjectURL(new Blob([e.target.result]));
} else {
data = e.target.result;
}
this.option.img = data;
};
//转化为base64
reader.readAsDataURL(file);
},
base64ImgtoFile(dataurl, filename = 'file') {
//将base64格式分割:['data:image/png;base64','XXXX']
const arr = dataurl.split(',');
// .*? 表示匹配任意字符到下一个符合条件的字符 刚好匹配到:
// image/png
const mime = arr[0].match(/:(.*?);/)[1]; //image/png
//[image,png] 获取图片类型后缀
const suffix = mime.split('/')[1]; //png
const bstr = atob(arr[1]); //atob() 方法用于解码使用 base-64 编码的字符串
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime,
});
},
uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
fileUpload(formData).then((res) => {
console.log(res);
if (res.status == 200) {
this.$emit('uploadImgSuccess', res.data);
} else {
this.$message({
message: '上传失败',
type: 'error',
duration: 1000,
});
}
});
},
//上传图片
uploadImg() {
this.$refs.cropper.getCropData((data) => {
this.resImg = this.base64ImgtoFile(data);
this.uploadFile(this.resImg);
});
},
},
};
</script>
<style scoped lang="scss">
.btn {
outline: none;
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
-webkit-transition: 0.1s;
transition: 0.1s;
font-weight: 500;
padding: 8px 15px;
font-size: 12px;
border-radius: 3px;
color: #fff;
background-color: #409eff;
border-color: #409eff;
margin-right: 10px;
}
.cropper-content {
display: flex;
display: -webkit-flex;
justify-content: flex-end;
.cropper-box {
flex: 1;
width: 100%;
.cropper {
width: auto;
height: 300px;
}
}
.show-preview {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
.preview {
overflow: hidden;
height: 200px;
width: 200px;
background: #cccccc;
transform: scale(0.8);
border-radius: 50%;
}
}
}
.footer-btn {
margin-top: 30px;
display: flex;
display: -webkit-flex;
justify-content: space-around;
.scope-btn {
display: flex;
display: -webkit-flex;
justify-content: space-between;
padding-right: 10px;
}
.upload-btn {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
}
}
</style>

View File

@ -54,7 +54,7 @@
</div>
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onDropdownCommand">
<span class="layout-navbars-breadcrumb-user-link">
<!-- <img :src="getUserInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" /> -->
<img :src="getUserInfos.head_pic" class="layout-navbars-breadcrumb-user-link-photo mr5" />
{{ getUserInfos.account === '' ? 'test' : getUserInfos.account }}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
@ -262,8 +262,8 @@ export default {
align-items: center;
white-space: nowrap;
&-photo {
width: 25px;
height: 25px;
width: 30px;
height: 30px;
border-radius: 100%;
}
}

View File

@ -2,6 +2,12 @@
<div>
<Card :bordered="false" dis-hover class="ivu-mt">
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="160" label-position="right">
<FormItem label="头像">
<div class="avatar" @click="avatarMoadl = true">
<img v-if="formValidate.head_pic" :src="formValidate.head_pic" alt="" />
<img v-else src="../../../assets/images/f.png" alt="" />
</div>
</FormItem>
<FormItem label="账号" prop="">
<Input type="text" v-model="account" :disabled="true" class="input"></Input>
</FormItem>
@ -22,14 +28,19 @@
</FormItem>
</Form>
</Card>
<Modal v-model="avatarMoadl" title="头像上传" width="700">
<CropperImg v-if="avatarMoadl" @uploadImgSuccess="uploadImgSuccess"></CropperImg>
</Modal>
</div>
</template>
<script>
import { updtaeAdmin } from '@/api/user';
import { mapState } from 'vuex';
import CropperImg from '@/components/cropperImg';
export default {
name: 'setting_user',
components: { CropperImg },
computed: {
...mapState('media', ['isMobile']),
...mapState('userLevel', ['categoryId']),
@ -43,7 +54,9 @@ export default {
data() {
return {
account: '',
avatarMoadl: false,
formValidate: {
avatar: '',
real_name: '',
pwd: '',
new_pwd: '',
@ -59,15 +72,21 @@ export default {
},
mounted() {
this.account = this.$store.state.userInfo.userInfo.account;
this.formValidate.head_pic = this.$store.state.userInfo.userInfo.head_pic;
this.formValidate.real_name = this.$store.state.userInfo.userInfo.real_name;
},
methods: {
uploadImgSuccess(data) {
this.avatarMoadl = false;
this.formValidate.head_pic = data.src;
},
handleSubmit(name) {
this.$refs[name].validate((valid) => {
if (valid) {
updtaeAdmin(this.formValidate)
.then((res) => {
this.$store.commit('userInfo/userRealName', this.formValidate.real_name);
this.$store.commit('userInfo/userRealHeadPic', this.formValidate.head_pic);
this.$Message.success(res.msg);
})
.catch((res) => {
@ -84,8 +103,18 @@ export default {
};
</script>
<style scoped>
<style lang="scss" scoped>
.input {
width: 400px;
}
.avatar {
width: 80px;
height: 80px;
img {
width: 100%;
height: 100%;
border-radius: 50%;
border: 1px solid #f2f2f2;
}
}
</style>

View File

@ -59,6 +59,9 @@ export default {
userRealName(state, realName) {
state.userInfo.real_name = realName;
},
userRealHeadPic(state, headPic) {
state.userInfo.head_pic = headPic;
},
uniqueAuth(state, uniqueAuth) {
state.uniqueAuth = uniqueAuth;
},