This commit is contained in:
zlwu committed 2025-11-28 21:46:23 +08:00
1 parent 375a2c8df7
commit ba7b7b8497
9 files changed
+343 -57

No files matched your search

+1
View File
@@ -37,6 +37,7 @@ export interface UserVO extends BaseEntity {
phonenumber: string;
sex: string;
avatar: string;
avatarUrl?: string;
status: string;
delFlag: string;
loginIp: string;
+14 -1
View File
@@ -75,7 +75,8 @@
<div class="avatar-container">
<el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
<div class="avatar-wrapper">
<img :src="userStore.avatar" class="user-avatar" />
<ImagePreview v-if="userStore.avatar" class="user-avatar" :src="userStore.avatar" :width="24" :height="24" :is-oss-id="avatarIsOssId" />
<img v-else :src="userStore.avatar" class="user-avatar" />
<el-icon><caret-bottom /></el-icon>
</div>
<template #dropdown>
@@ -98,9 +99,12 @@
</template>
<script setup lang="ts">
import { ComponentInternalInstance, computed, getCurrentInstance, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import Hamburger from '@/components/Hamburger/index.vue';
import TopNav from '@/components/TopNav/index.vue';
import SearchMenu from './TopBar/search.vue';
import ImagePreview from '@/components/ImagePreview/index.vue';
import { useAppStore } from '@/store/modules/app';
import { useUserStore } from '@/store/modules/user';
import { useSettingsStore } from '@/store/modules/settings';
@@ -129,6 +133,15 @@ const dynamic = ref(false);
const tenantEnabled = ref(true);
// 搜索菜单
const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
const avatarIsOssId = computed(() => {
const avatar = userStore.avatar;
if (!avatar) {
return false;
}
const isHttp = /^https?:\/\//i.test(avatar);
const isAbsolutePath = avatar.startsWith('/');
return !isHttp && !isAbsolutePath;
});
const openSearchMenu = () => {
searchMenuRef.value?.openSearch();
+7 -1
View File
@@ -13,6 +13,7 @@ export const useUserStore = defineStore('user', () => {
const userId = ref<string | number>('');
const tenantId = ref<string>('');
const avatar = ref('');
const avatarUrl = ref('');
const deptName = ref('');
const phonenumber = ref('');
const roles = ref<Array<string>>([]); // 用户角色编码集合 → 判断路由权限
@@ -40,7 +41,9 @@ export const useUserStore = defineStore('user', () => {
if (res) {
const data = res.data;
const user = data.user;
const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar;
const avatarValue = user.avatar === '' || user.avatar === null || user.avatar === undefined ? '' : String(user.avatar);
const previewUrl = user.avatarUrl || '';
const profile = avatarValue || defAva;
if (data.roles && data.roles.length > 0) {
// 验证返回的roles是否是一个非空数组
@@ -52,6 +55,7 @@ export const useUserStore = defineStore('user', () => {
name.value = user.userName;
nickname.value = user.nickName;
avatar.value = profile;
avatarUrl.value = previewUrl || (avatarValue ? '' : defAva);
userId.value = user.userId;
tenantId.value = user.tenantId;
deptName.value = user.deptName;
@@ -72,6 +76,7 @@ export const useUserStore = defineStore('user', () => {
const setAvatar = (value: string) => {
avatar.value = value;
avatarUrl.value = value;
};
return {
@@ -81,6 +86,7 @@ export const useUserStore = defineStore('user', () => {
name,
nickname,
avatar,
avatarUrl,
deptName,
phonenumber,
roles,
@@ -2,7 +2,7 @@
<div class="user-info-head" @click="editCropper()">
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
<el-dialog v-model="open" :title="title" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-row justify="center">
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper
v-if="visible"
@@ -17,40 +17,23 @@
@real-time="realTime"
/>
</el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div class="avatar-upload-preview">
<img :src="options.previews.url" :style="options.previews.img" />
</div>
</el-col>
</el-row>
<br />
<el-row>
<el-col :lg="2" :md="2">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button>
选择
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
</el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" @click="uploadImg()">提 交</el-button>
</el-col>
</el-row>
<div class="avatar-actions">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button>
选择
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-upload>
<el-button icon="Plus" @click="changeScale(1)"></el-button>
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
<el-button type="primary" @click="uploadImg()">提 交</el-button>
</div>
</el-dialog>
</div>
</template>
@@ -84,13 +67,13 @@ const title = ref('修改头像');
const cropper = ref<any>({});
//图片裁剪数据
const options = reactive<Options>({
img: userStore.avatar,
img: userStore.avatarUrl || userStore.avatar,
autoCrop: true,
autoCropWidth: 200,
autoCropHeight: 200,
fixedBox: true,
outputType: 'png',
fileName: '',
fileName: 'avatar.png',
previews: {},
visible: false
});
@@ -134,14 +117,20 @@ const beforeUpload = (file: UploadRawFile): any => {
/** 上传图片 */
const uploadImg = async () => {
cropper.value.getCropBlob(async (data: any) => {
const formData = new FormData();
formData.append('avatarfile', data, options.fileName);
const res = await uploadAvatar(formData);
open.value = false;
options.img = res.data.imgUrl;
userStore.setAvatar(options.img);
proxy?.$modal.msgSuccess('修改成功');
visible.value = false;
try {
const formData = new FormData();
const fileName = options.fileName || `avatar.${options.outputType || 'png'}`;
formData.append('avatarfile', data, fileName);
const res = await uploadAvatar(formData);
await userStore.getInfo(); // 刷新用户信息以获取最新的oss_id
options.img = res?.data?.imgUrl || userStore.avatarUrl || userStore.avatar;
open.value = false;
proxy?.$modal.msgSuccess('修改成功');
} catch (error) {
proxy?.$modal.msgError('上传失败,请重试');
} finally {
visible.value = false;
}
});
};
/** 实时预览 */
@@ -150,7 +139,7 @@ const realTime = (data: any) => {
};
/** 关闭窗口 */
const closeDialog = () => {
options.img = userStore.avatar;
options.img = userStore.avatarUrl || userStore.avatar;
options.visible = false;
};
</script>
@@ -179,4 +168,12 @@ const closeDialog = () => {
line-height: 110px;
border-radius: 50%;
}
.avatar-actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 12px;
}
</style>