283 lines
7.7 KiB
Vue
283 lines
7.7 KiB
Vue
<template>
|
|
<a-dropdown trigger="hover" placement="bottomRight" popup-class="user-account-dropdown">
|
|
<div class="user-dropdown-trigger">
|
|
<span class="user-name">{{ displayName }}</span>
|
|
<IconSettings class="user-icon" />
|
|
</div>
|
|
<template #content>
|
|
<a-doption v-if="isCustomerPortal" @click="() => onMenuClick({ key: 'userinfo' })">
|
|
<span class="user-menu-item">
|
|
<span class="user-menu-item__icon"><i class="fa fa-user" /></span>
|
|
<span class="user-menu-item__label">{{ t('menu.user.profile') }}</span>
|
|
</span>
|
|
</a-doption>
|
|
<a-doption v-if="!isCustomerPortal" @click="() => onMenuClick({ key: 'settings' })">
|
|
<span class="user-menu-item">
|
|
<span class="user-menu-item__icon"><i class="fa fa-cog" /></span>
|
|
<span class="user-menu-item__label">页面配置</span>
|
|
</span>
|
|
</a-doption>
|
|
<a-doption @click="() => onMenuClick({ key: 'changePassword' })">
|
|
<span class="user-menu-item">
|
|
<span class="user-menu-item__icon"><i class="fa fa-lock" /></span>
|
|
<span class="user-menu-item__label">{{ t('profile.changePassword') }}</span>
|
|
</span>
|
|
</a-doption>
|
|
<a-doption v-if="isCustomerPortal" @click="() => onMenuClick({ key: 'settings' })">
|
|
<span class="user-menu-item">
|
|
<span class="user-menu-item__icon"><i class="fa fa-cog" /></span>
|
|
<span class="user-menu-item__label">页面配置</span>
|
|
</span>
|
|
</a-doption>
|
|
<a-doption @click="() => onMenuClick({ key: 'logout' })">
|
|
<span class="user-menu-item is-danger">
|
|
<span class="user-menu-item__icon"><i class="fa fa-sign-in" /></span>
|
|
<span class="user-menu-item__label">退出登录</span>
|
|
</span>
|
|
</a-doption>
|
|
</template>
|
|
</a-dropdown>
|
|
|
|
<a-modal
|
|
v-model:visible="changePasswordVisible"
|
|
:title="t('profile.changePassword')"
|
|
:mask-closable="false"
|
|
:confirm-loading="changePasswordLoading"
|
|
@ok="handleChangePasswordSubmit"
|
|
@cancel="handleChangePasswordCancel"
|
|
>
|
|
<a-form
|
|
ref="pwdFormRef"
|
|
:model="pwdForm"
|
|
:label-col-props="{ span: 6 }"
|
|
:wrapper-col-props="{ span: 16 }"
|
|
>
|
|
<a-form-item
|
|
field="oldPassword"
|
|
:label="t('profile.oldPassword')"
|
|
:rules="[{ required: true, message: t('profile.oldPassword') }]"
|
|
>
|
|
<a-input-password
|
|
v-model="pwdForm.oldPassword"
|
|
:placeholder="t('profile.oldPassword')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item
|
|
field="newPassword"
|
|
:label="t('profile.newPassword')"
|
|
:rules="[{ required: true, message: t('profile.newPassword') }]"
|
|
>
|
|
<a-input-password
|
|
v-model="pwdForm.newPassword"
|
|
:placeholder="t('profile.newPassword')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item
|
|
field="confirmPassword"
|
|
:label="t('profile.confirmPassword')"
|
|
:rules="[
|
|
{ required: true, message: t('profile.confirmPassword') },
|
|
{ validator: validateConfirmPassword },
|
|
]"
|
|
>
|
|
<a-input-password
|
|
v-model="pwdForm.confirmPassword"
|
|
:placeholder="t('profile.confirmPassword')"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
</a-form>
|
|
</a-modal>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed, reactive, ref } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { Message } from '@arco-design/web-vue';
|
|
import { useUserStore, useAppStore } from '@/store';
|
|
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
|
import { AuthService } from '@/api/rbac';
|
|
import { IconSettings } from '@arco-design/web-vue/es/icon';
|
|
import { resolvePostLogoutLogin } from '@/utils/login-portal';
|
|
|
|
const { t } = useI18n();
|
|
const router = useRouter();
|
|
const userStore = useUserStore();
|
|
const appStore = useAppStore();
|
|
const rbacUserStore = useRbacUserStore();
|
|
|
|
const isCustomerPortal = computed(() => rbacUserStore.isCustomerPortal());
|
|
|
|
const displayName = computed(() => {
|
|
return userStore.name || '--';
|
|
});
|
|
|
|
const changePasswordVisible = ref(false);
|
|
const changePasswordLoading = ref(false);
|
|
const pwdFormRef = ref();
|
|
const pwdForm = reactive({
|
|
oldPassword: '',
|
|
newPassword: '',
|
|
confirmPassword: '',
|
|
});
|
|
|
|
function validateConfirmPassword(
|
|
_rule: unknown,
|
|
value: string,
|
|
callback: (error?: string) => void
|
|
) {
|
|
if (value !== pwdForm.newPassword) {
|
|
callback(t('profile.passwordMismatch'));
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
function resetPasswordForm() {
|
|
pwdForm.oldPassword = '';
|
|
pwdForm.newPassword = '';
|
|
pwdForm.confirmPassword = '';
|
|
}
|
|
|
|
function openChangePasswordModal() {
|
|
resetPasswordForm();
|
|
changePasswordVisible.value = true;
|
|
}
|
|
|
|
function handleChangePasswordCancel() {
|
|
changePasswordVisible.value = false;
|
|
resetPasswordForm();
|
|
}
|
|
|
|
async function handleChangePasswordSubmit() {
|
|
try {
|
|
await pwdFormRef.value?.validate();
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
changePasswordLoading.value = true;
|
|
try {
|
|
await AuthService.postRbacAuthChangePassword({
|
|
oldPassword: pwdForm.oldPassword,
|
|
newPassword: pwdForm.newPassword,
|
|
});
|
|
Message.success(t('profile.changePasswordSuccess'));
|
|
changePasswordVisible.value = false;
|
|
resetPasswordForm();
|
|
const loginType = rbacUserStore.resolveLoginTypeForRedirect();
|
|
const currentPath = router.currentRoute.value.fullPath;
|
|
rbacUserStore.logout();
|
|
userStore.logout();
|
|
const target = resolvePostLogoutLogin({ loginType, path: currentPath });
|
|
if (target.path) {
|
|
router.replace({ path: target.path, query: target.query });
|
|
} else {
|
|
router.replace({ name: target.name, query: target.query });
|
|
}
|
|
} catch (err: any) {
|
|
if (err?.errors || err?.errorFields) return;
|
|
Message.error(err?.message || '修改密码失败');
|
|
} finally {
|
|
changePasswordLoading.value = false;
|
|
}
|
|
}
|
|
|
|
const onMenuClick = async (event: { key: string }) => {
|
|
const { key } = event;
|
|
|
|
if (key === 'logout') {
|
|
const loginType = rbacUserStore.resolveLoginTypeForRedirect();
|
|
const currentPath = router.currentRoute.value.fullPath;
|
|
rbacUserStore.logout();
|
|
userStore.logout();
|
|
const target = resolvePostLogoutLogin({ loginType, path: currentPath });
|
|
if (target.path) {
|
|
router.push({ path: target.path, query: target.query });
|
|
} else {
|
|
router.push({ name: target.name, query: target.query });
|
|
}
|
|
} else if (key === 'userinfo') {
|
|
router.push({ name: 'UserProfile' });
|
|
} else if (key === 'changePassword') {
|
|
openChangePasswordModal();
|
|
} else if (key === 'settings') {
|
|
appStore.updateSettings({ globalSettings: true });
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.user-dropdown-trigger {
|
|
display: flex;
|
|
align-items: center;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
padding: 0 8px;
|
|
flex-shrink: 0;
|
|
color: inherit;
|
|
text-decoration: none;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.user-dropdown-trigger:hover {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.user-name {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 150px;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
.user-icon {
|
|
font-size: 16px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.user-menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
width: 132px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.user-menu-item__icon {
|
|
width: 18px;
|
|
flex-shrink: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 10px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.user-menu-item__label {
|
|
flex: 1;
|
|
white-space: nowrap;
|
|
font-weight: 500;
|
|
text-align: left;
|
|
}
|
|
|
|
.user-menu-item.is-danger .user-menu-item__label {
|
|
color: rgb(var(--red-6));
|
|
}
|
|
</style>
|
|
|
|
<style lang="less">
|
|
.user-account-dropdown {
|
|
.arco-dropdown {
|
|
min-width: 156px;
|
|
}
|
|
|
|
.arco-dropdown-option-content {
|
|
padding: 8px 12px;
|
|
}
|
|
}
|
|
</style>
|