239 lines
6.7 KiB
Vue
239 lines
6.7 KiB
Vue
<template>
|
|
<a-card :bordered="false">
|
|
<a-space :size="54">
|
|
<a-upload
|
|
:file-list="avatarList"
|
|
accept="image/*"
|
|
:show-file-list="false"
|
|
list-type="picture-card"
|
|
:show-upload-button="true"
|
|
:on-before-upload="handleBeforeUpload"
|
|
>
|
|
<template #upload-button>
|
|
<a-avatar :size="100" class="info-avatar">
|
|
<template #trigger-icon><icon-camera /></template>
|
|
<img
|
|
v-if="avatarList.length"
|
|
:src="avatarList[0].url"
|
|
:alt="$t('userCenter.panel.avatar')"
|
|
/>
|
|
</a-avatar>
|
|
</template>
|
|
</a-upload>
|
|
|
|
<div class="main">
|
|
<a-modal
|
|
:visible="cropperVisible"
|
|
width="40%"
|
|
:footer="false"
|
|
unmount-on-close
|
|
render-to-body
|
|
@cancel="handleCropperCancel"
|
|
>
|
|
<a-row>
|
|
<a-col :span="14">
|
|
<div style="width: 370px; height: 370px">
|
|
<!-- 头像裁剪框 -->
|
|
<vue-cropper
|
|
ref="cropper"
|
|
:info="true"
|
|
:img="options.img"
|
|
:full="options.full"
|
|
:fixed="options.fixed"
|
|
:fixed-box="options.fixedBox"
|
|
:can-move="options.canMove"
|
|
:center-box="options.centerBox"
|
|
:auto-crop="options.autoCrop"
|
|
:auto-crop-width="options.autoCropWidth"
|
|
:auto-crop-height="options.autoCropHeight"
|
|
:output-type="options.outputType"
|
|
:output-size="options.outputSize"
|
|
@realTime="realTime"
|
|
/>
|
|
</div>
|
|
</a-col>
|
|
<a-col :span="6">
|
|
<!-- 实时预览 -->
|
|
<div :style="previewStyle">
|
|
<div :style="previews.div">
|
|
<img :src="previews.url" :style="previews.img" alt="" />
|
|
</div>
|
|
</div>
|
|
</a-col>
|
|
</a-row>
|
|
<br />
|
|
<a-space>
|
|
<a-button type="primary" @click="handleUpload">提交</a-button>
|
|
<a-button type="outline" @click="handleCropperCancel"
|
|
>取消</a-button
|
|
>
|
|
</a-space>
|
|
</a-modal>
|
|
</div>
|
|
|
|
<a-descriptions
|
|
:column="2"
|
|
:label-style="{
|
|
width: '140px',
|
|
fontWeight: 'normal',
|
|
color: 'rgb(var(--gray-8))',
|
|
}"
|
|
:value-style="{
|
|
width: '200px',
|
|
paddingLeft: '8px',
|
|
textAlign: 'left',
|
|
}"
|
|
align="right"
|
|
layout="inline-horizontal"
|
|
>
|
|
<a-descriptions-item :label="$t('userCenter.panel.label.nickname')">{{
|
|
userStore.nickname
|
|
}}</a-descriptions-item>
|
|
<a-descriptions-item :label="$t('userCenter.panel.label.gender')">
|
|
<div v-if="userStore.gender === 1">
|
|
{{ $t('userCenter.panel.male') }}
|
|
<icon-man style="color: #19bbf1" />
|
|
</div>
|
|
<div v-else-if="userStore.gender === 2">
|
|
{{ $t('userCenter.panel.female') }}
|
|
<icon-woman style="color: #fa7fa9" />
|
|
</div>
|
|
<div v-else>{{ $t('userCenter.panel.unknown') }}</div>
|
|
</a-descriptions-item>
|
|
<a-descriptions-item :label="$t('userCenter.panel.label.phone')">{{
|
|
userStore.phone || '暂无'
|
|
}}</a-descriptions-item>
|
|
<a-descriptions-item :label="$t('userCenter.panel.label.email')">{{
|
|
userStore.email || '暂无'
|
|
}}</a-descriptions-item>
|
|
<a-descriptions-item :label="$t('userCenter.panel.label.deptName')">{{
|
|
userStore.deptName
|
|
}}</a-descriptions-item>
|
|
<a-descriptions-item
|
|
:label="$t('userCenter.panel.label.registrationDate')"
|
|
>{{ userStore.registrationDate }}</a-descriptions-item
|
|
>
|
|
</a-descriptions>
|
|
</a-space>
|
|
</a-card>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { reactive, ref, getCurrentInstance } from 'vue';
|
|
import { FileItem } from '@arco-design/web-vue';
|
|
import { uploadAvatar, cropperOptions } from '@/api/system/user-center';
|
|
import getAvatar from '@/utils/avatar';
|
|
import { useUserStore } from '@/store';
|
|
import { VueCropper } from 'vue-cropper';
|
|
import 'vue-cropper/dist/index.css';
|
|
|
|
const fileRef = ref(reactive({ name: 'avatar.png' }));
|
|
const previews: any = ref({});
|
|
const previewStyle: any = ref({});
|
|
const cropperVisible = ref(false);
|
|
const cropper = ref();
|
|
const { proxy } = getCurrentInstance() as any;
|
|
const userStore = useUserStore();
|
|
|
|
const avatar = {
|
|
uid: '-2',
|
|
name: 'avatar.png',
|
|
url: getAvatar(userStore.avatar, userStore.gender),
|
|
};
|
|
const avatarList = ref<FileItem[]>([avatar]);
|
|
|
|
const options: cropperOptions = reactive({
|
|
autoCrop: true,
|
|
autoCropWidth: 200,
|
|
autoCropHeight: 200,
|
|
canMove: true,
|
|
centerBox: true,
|
|
full: false,
|
|
fixed: false,
|
|
fixedBox: false,
|
|
img: '',
|
|
outputSize: 1,
|
|
outputType: 'png',
|
|
});
|
|
|
|
/**
|
|
* 上传前弹出裁剪框
|
|
*
|
|
* @param file 头像
|
|
*/
|
|
const handleBeforeUpload = (file: File): boolean => {
|
|
fileRef.value = file;
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = () => {
|
|
options.img = reader.result;
|
|
};
|
|
cropperVisible.value = true;
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* 关闭裁剪框
|
|
*/
|
|
const handleCropperCancel = () => {
|
|
fileRef.value = { name: '' };
|
|
options.img = '';
|
|
cropperVisible.value = false;
|
|
};
|
|
|
|
/**
|
|
* 上传头像
|
|
*/
|
|
const handleUpload = () => {
|
|
cropper.value.getCropBlob((data: string | Blob) => {
|
|
const formData = new FormData();
|
|
formData.append('avatarFile', data, fileRef.value?.name);
|
|
uploadAvatar(formData).then((res) => {
|
|
userStore.avatar = res.data.avatar;
|
|
avatarList.value[0].url = getAvatar(res.data.avatar, undefined);
|
|
proxy.$message.success(res.msg);
|
|
handleCropperCancel();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 实时预览
|
|
* @param data data
|
|
*/
|
|
const realTime = (data: any) => {
|
|
previewStyle.value = {
|
|
width: `${data.w}px`,
|
|
height: `${data.h}px`,
|
|
overflow: 'hidden',
|
|
margin: '0',
|
|
zoom: 0.8,
|
|
borderRadius: '50%',
|
|
};
|
|
previews.value = data;
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.arco-card {
|
|
padding: 14px 0 4px 4px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
:deep(.arco-avatar-trigger-icon-button) {
|
|
width: 32px;
|
|
height: 32px;
|
|
line-height: 32px;
|
|
background-color: #e8f3ff;
|
|
.arco-icon-camera {
|
|
margin-top: 8px;
|
|
color: rgb(var(--arcoblue-6));
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
.main {
|
|
position: relative;
|
|
}
|
|
</style>
|