ruoyi-plus-soybean/src/layouts/modules/global-header/components/user-avatar.vue

143 lines
3.2 KiB
Vue
Raw Normal View History

2023-11-17 08:45:00 +08:00
<script setup lang="ts">
2025-04-29 00:12:47 +08:00
import { computed } from 'vue';
2023-11-17 08:45:00 +08:00
import type { VNode } from 'vue';
2025-05-09 17:37:24 +08:00
import { useBoolean } from '@sa/hooks';
2023-11-17 08:45:00 +08:00
import { useAuthStore } from '@/store/modules/auth';
import { useRouterPush } from '@/hooks/common/router';
2024-03-24 03:02:08 +08:00
import { useSvgIcon } from '@/hooks/common/icon';
2025-04-28 23:52:26 +08:00
import defaultAvatar from '@/assets/imgs/soybean.jpg';
2023-11-17 08:45:00 +08:00
import { $t } from '@/locales';
defineOptions({
name: 'UserAvatar'
});
const authStore = useAuthStore();
const { routerPushByKey, toLogin } = useRouterPush();
2024-03-24 03:02:08 +08:00
const { SvgIconVNode } = useSvgIcon();
2023-11-17 08:45:00 +08:00
2025-04-29 00:12:47 +08:00
const { bool: avatarError, setTrue: setError, setFalse: clearError } = useBoolean(false);
2025-04-28 23:52:26 +08:00
2023-11-17 08:45:00 +08:00
function loginOrRegister() {
toLogin();
}
2025-04-28 23:52:26 +08:00
function handleAvatarLoad() {
2025-04-29 00:12:47 +08:00
clearError();
2025-04-28 23:52:26 +08:00
}
function handleAvatarError() {
2025-04-29 00:12:47 +08:00
setError();
2025-04-28 23:52:26 +08:00
}
type DropdownKey = 'user-center' | 'logout';
2023-11-17 08:45:00 +08:00
type DropdownOption =
| {
key: DropdownKey;
label: string;
icon?: () => VNode;
}
| {
type: 'divider';
key: string;
};
const options = computed(() => {
const opts: DropdownOption[] = [
2025-04-28 23:52:26 +08:00
{
label: $t('common.userCenter'),
key: 'user-center',
icon: SvgIconVNode({ icon: 'ph:user-circle', fontSize: 18 })
},
{
type: 'divider',
key: 'divider'
},
2023-11-17 08:45:00 +08:00
{
label: $t('common.logout'),
key: 'logout',
icon: SvgIconVNode({ icon: 'ph:sign-out', fontSize: 18 })
}
];
return opts;
});
function logout() {
window.$dialog?.info({
title: $t('common.tip'),
content: $t('common.logoutConfirm'),
positiveText: $t('common.confirm'),
negativeText: $t('common.cancel'),
onPositiveClick: () => {
2024-09-02 09:34:34 +08:00
authStore.logout();
2023-11-17 08:45:00 +08:00
}
});
}
function handleDropdown(key: DropdownKey) {
if (key === 'logout') {
logout();
} else {
routerPushByKey(key);
}
}
</script>
<template>
<NButton v-if="!authStore.isLogin" quaternary @click="loginOrRegister">
{{ $t('page.login.common.loginOrRegister') }}
</NButton>
<NDropdown v-else placement="bottom" trigger="click" :options="options" @select="handleDropdown">
2025-04-29 00:12:47 +08:00
<div class="flex cursor-pointer items-center rounded-md px-2 py-1 transition-colors duration-300 hover:bg-black/6">
2025-05-09 17:37:24 +08:00
<div class="flex items-center gap-2" :class="{ 'opacity-50': avatarError }">
<NAvatar
v-if="authStore.userInfo.user?.avatar"
:size="24"
round
:src="authStore.userInfo.user?.avatar"
@load="handleAvatarLoad"
@error="handleAvatarError"
/>
<NAvatar v-else :size="32" round :src="defaultAvatar" @load="handleAvatarLoad" @error="handleAvatarError" />
<span class="max-w-120px truncate text-14px font-medium">
{{ authStore.userInfo.user?.nickName }}
</span>
</div>
2023-11-17 08:45:00 +08:00
</div>
</NDropdown>
</template>
2025-04-28 23:52:26 +08:00
<style lang="scss" scoped>
.avatar-wrapper {
display: flex;
align-items: center;
padding: 4px 8px;
border-radius: 6px;
transition: all 0.3s ease;
cursor: pointer;
&:hover {
background-color: rgba(0, 0, 0, 0.06);
}
}
.avatar-container {
display: flex;
align-items: center;
gap: 8px;
&.avatar-error {
opacity: 0.5;
}
}
.user-name {
font-size: 14px;
max-width: 120px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>