UNIVPLMDataIntegration/Web/src/layout/navBars/topBar/user.vue

398 lines
13 KiB
Vue
Raw Normal View History

2024-06-15 13:02:35 +08:00
<template>
<div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
<div class="layout-navbars-breadcrumb-user-icon">
<i class="iconfont icon-ziti" :title="$t('message.user.title0')"></i>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="large" :disabled="state.disabledSize === 'large'">{{ $t('message.user.dropdownLarge') }}</el-dropdown-item>
<el-dropdown-item command="default" :disabled="state.disabledSize === 'default'">{{ $t('message.user.dropdownDefault') }}</el-dropdown-item>
<el-dropdown-item command="small" :disabled="state.disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
<div class="layout-navbars-breadcrumb-user-icon">
<FlagIcon :code="currentCountryCode" square :size="12" :title="$t('message.user.title1')" />
2024-06-15 13:02:35 +08:00
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="(value, key) in languageList" :key="key" :command="key" :disabled="state.disabledI18n === key">
2025-02-27 17:07:51 +08:00
<div class="flex items-center">
<div class="mr-2">
2025-02-27 17:18:26 +08:00
<FlagIcon :code="getCountryCode(key)" :size="18" />
2025-02-27 17:07:51 +08:00
</div>
<div style="margin-left: 10px">
{{ value }}
2025-02-27 17:07:51 +08:00
</div>
</div>
</el-dropdown-item>
2024-06-15 13:02:35 +08:00
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
<el-icon :title="$t('message.user.title2')">
<ele-Search />
</el-icon>
</div>
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
<i class="icon-skin iconfont" :title="$t('message.user.title3')"></i>
</div>
<div class="layout-navbars-breadcrumb-user-icon">
<el-popover placement="bottom" trigger="hover" transition="el-zoom-in-top" :width="300" :persistent="false">
<template #reference>
<el-badge :is-dot="hasUnreadNotice">
<el-icon :title="$t('message.user.title4')">
<ele-Bell />
</el-icon>
</el-badge>
</template>
<UserNews :noticeList="state.noticeList" />
</el-popover>
</div>
<div class="layout-navbars-breadcrumb-user-icon" @click="onScreenfullClick">
<i class="iconfont" :title="state.isScreenfull ? $t('message.user.title6') : $t('message.user.title5')" :class="!state.isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'"></i>
</div>
<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onOnlineUserClick">
2025-02-27 17:07:51 +08:00
<el-icon :title="$t('message.list.onlineUser')">
2024-06-15 13:02:35 +08:00
<ele-User />
</el-icon>
</div>
2025-02-27 17:07:51 +08:00
<el-dropdown :show-timeout="70" :hide-timeout="50" size="large" @command="onHandleCommandClick">
2024-06-15 13:02:35 +08:00
<span class="layout-navbars-breadcrumb-user-link">
<el-tooltip effect="dark" placement="left">
<template #content>
{{ $t('message.list.account') }}{{ userInfos.account }}<br />
{{ $t('message.list.realName') }}{{ userInfos.realName }}<br />
{{ $t('message.list.phone') }}{{ userInfos.phone }}<br />
{{ $t('message.list.email') }}{{ userInfos.email }}<br />
{{ $t('message.list.orgName') }}{{ userInfos.orgName }}<br />
{{ $t('message.list.positionText') }}{{ userInfos.posName }}<br />
2024-06-15 13:02:35 +08:00
</template>
2025-02-27 17:07:51 +08:00
2024-06-15 13:02:35 +08:00
<img :src="userInfos.avatar" class="layout-navbars-breadcrumb-user-link-photo mr5" />
</el-tooltip>
{{ userInfos.realName == '' ? userInfos.account : userInfos.realName }}
<el-icon class="el-icon--right">
<ele-ArrowDown />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<!-- <el-dropdown-item command="/dashboard/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item> -->
<el-dropdown-item :icon="Avatar" command="/system/userCenter">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
2024-07-08 01:37:48 +08:00
<el-dropdown-item :icon="Loading" command="clearCache">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
<el-dropdown-item :icon="Lock" divided command="lockScreen">{{ $t('message.layout.threeIsLockScreen') }}</el-dropdown-item>
<el-dropdown-item :icon="CircleCloseFilled" divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
2024-06-15 13:02:35 +08:00
</el-dropdown-menu>
</template>
</el-dropdown>
2024-09-01 13:07:42 +08:00
2024-06-15 13:02:35 +08:00
<Search ref="searchRef" />
<OnlineUser ref="onlineUserRef" />
2024-09-01 13:07:42 +08:00
<ChangePassword ref="changePasswordRef" />
<UpgradeInfo ref="upgradeInfoRef" />
2024-06-15 13:02:35 +08:00
</div>
</template>
<script setup lang="ts" name="layoutBreadcrumbUser">
import { defineAsyncComponent, ref, computed, reactive, onMounted } from 'vue';
2024-06-15 13:02:35 +08:00
import { useRouter } from 'vue-router';
import { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
import screenfull from 'screenfull';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
2024-07-08 01:37:48 +08:00
import { Local, Session } from '/@/utils/storage';
import { decryptJWT } from '/@/utils/request';
2024-06-15 13:02:35 +08:00
import Push from 'push.js';
import { signalR } from '/@/views/system/onlineUser/signalR';
import { Avatar, CircleCloseFilled, Loading, Lock } from '@element-plus/icons-vue';
2024-06-15 13:02:35 +08:00
import { clearAccessTokens, getAPI } from '/@/utils/axios-utils';
import { SysAuthApi, SysNoticeApi, SysUpgradeApi, SysUserApi } from '/@/api-services/api';
import { languageList, getCountryCode } from '/@/i18n';
import FlagIcon from 'vue3-flag-icons';
import type { CountryCode } from 'vue3-flag-icons';
2024-06-15 13:02:35 +08:00
// 引入组件
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue'));
const Search = defineAsyncComponent(() => import('/@/layout/navBars/topBar/search.vue'));
const OnlineUser = defineAsyncComponent(() => import('/@/views/system/onlineUser/index.vue'));
2024-09-01 13:07:42 +08:00
const ChangePassword = defineAsyncComponent(() => import('/@/views/system/user/component/changePassword.vue'));
const UpgradeInfo = defineAsyncComponent(() => import('/@/views/system/upgrade/component/upgradeInfo.vue'));
2024-06-15 13:02:35 +08:00
// 定义变量内容
const { locale, t } = useI18n();
const router = useRouter();
const stores = useUserInfo();
const storesThemeConfig = useThemeConfig();
const { userInfos } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig);
const searchRef = ref();
const onlineUserRef = ref();
2024-09-01 13:07:42 +08:00
const changePasswordRef = ref();
2025-02-27 17:07:51 +08:00
const upgradeInfoRef = ref();
2025-02-27 17:33:23 +08:00
const currentCountryCode = ref<CountryCode>(getCountryCode(themeConfig.value.globalI18n));
2024-06-15 13:02:35 +08:00
const state = reactive({
isScreenfull: false,
2025-02-27 17:07:51 +08:00
disabledI18n: 'zh-cn',
2024-06-15 13:02:35 +08:00
disabledSize: 'large',
noticeList: [] as any, // 站内信列表
});
// 设置分割样式
const layoutUserFlexNum = computed(() => {
let num: string | number = '';
const { layout, isClassicSplitMenu } = themeConfig.value;
const layoutArr: string[] = ['defaults', 'columns'];
if (layoutArr.includes(layout) || (layout === 'classic' && !isClassicSplitMenu)) num = '1';
else num = '';
return num;
});
// 是否有未读消息
const hasUnreadNotice = computed(() => {
return state.noticeList.some((r: any) => r.readStatus == undefined || r.readStatus == 0);
});
// 全屏点击时
const onScreenfullClick = () => {
if (!screenfull.isEnabled) {
ElMessage.warning('暂不不支持全屏');
return false;
}
screenfull.toggle();
screenfull.on('change', () => {
if (screenfull.isFullscreen) state.isScreenfull = true;
else state.isScreenfull = false;
});
};
// 布局配置 icon 点击时
const onLayoutSetingClick = () => {
mittBus.emit('openSettingsDrawer');
};
// 下拉菜单点击时
const onHandleCommandClick = (path: string) => {
2024-07-08 01:37:48 +08:00
if (path === 'clearCache') {
Local.clear();
Session.clear();
window.location.reload();
} else if (path === 'lockScreen') {
Local.remove('themeConfig');
themeConfig.value.isLockScreen = true;
themeConfig.value.lockScreenTime = 1;
Local.set('themeConfig', themeConfig.value);
2024-07-08 01:37:48 +08:00
} else if (path === 'logOut') {
2024-06-15 13:02:35 +08:00
ElMessageBox({
closeOnClickModal: false,
closeOnPressEscape: false,
title: t('message.user.logOutTitle'),
message: t('message.user.logOutMessage'),
type: 'warning',
showCancelButton: true,
confirmButtonText: t('message.user.logOutConfirm'),
cancelButtonText: t('message.user.logOutCancel'),
// buttonSize: 'default',
2024-06-15 13:02:35 +08:00
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
instance.confirmButtonText = t('message.user.logOutExit');
try {
await getAPI(SysAuthApi).apiSysAuthLogoutPost();
} catch (error) {
console.error(error);
}
2024-06-15 13:02:35 +08:00
instance.confirmButtonLoading = false;
done();
} else {
done();
}
},
})
.then(async () => {
clearAccessTokens();
})
.catch(() => {});
} else {
router.push(path);
}
};
// 菜单搜索点击
const onSearchClick = () => {
searchRef.value.openSearch();
};
// 在线用户列表
const onOnlineUserClick = () => {
onlineUserRef.value.openDrawer();
};
// 组件大小改变
const onComponentSizeChange = (size: string) => {
Local.remove('themeConfig');
themeConfig.value.globalComponentSize = size;
Local.set('themeConfig', themeConfig.value);
initI18nOrSize('globalComponentSize', 'disabledSize');
window.location.reload();
};
// 语言切换
const onLanguageChange = (lang: string) => {
Local.remove('themeConfig');
themeConfig.value.globalI18n = lang;
Local.set('themeConfig', themeConfig.value);
2025-02-27 17:07:51 +08:00
currentCountryCode.value = getCountryCode(lang);
2024-06-15 13:02:35 +08:00
locale.value = lang;
other.useTitle();
initI18nOrSize('globalI18n', 'disabledI18n');
2025-02-27 17:07:51 +08:00
window.location.reload();
2024-06-15 13:02:35 +08:00
};
// 初始化组件大小/i18n
const initI18nOrSize = (value: string, attr: string) => {
(<any>state)[attr] = Local.get('themeConfig')[value];
};
// 页面加载时
onMounted(async () => {
if (Local.get('themeConfig')) {
initI18nOrSize('globalComponentSize', 'disabledSize');
initI18nOrSize('globalI18n', 'disabledI18n');
}
// 手动获取用户桌面通知权限
if (Push.Permission.GRANTED) {
// 判断当前是否有权限,没有则手动获取
Push.Permission.request(null, null);
}
// 监听浏览器 当前系统是否在当前页
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
// 清空关闭消息通知,
Push.clear();
}
});
// 加载未读的站内信
var res = await getAPI(SysNoticeApi).apiSysNoticeUnReadListGet();
state.noticeList = res.data.result ?? [];
// 接收站内信
signalR.on('PublicNotice', receiveNotice);
// // 处理消息已读
// mittBus.on('noticeRead', (id) => {
// const notice = state.noticeList.find((r: any) => r.id == id);
// if (notice == undefined) return;
// // 设置已读
// notice.readStatus = 1;
// });
2024-09-01 13:07:42 +08:00
// 加载系统更新日志
var res1 = await getAPI(SysUpgradeApi).apiSysUpgradeLastUnReadGet();
if (res1.data.result != null) {
upgradeInfoRef.value?.openDialog(res1.data.result);
}
// 是否修改密码
await changePassword();
2024-06-15 13:02:35 +08:00
});
// // 页面卸载时
// onUnmounted(() => {
// mittBus.off('noticeRead', () => {});
// });
const receiveNotice = (msg: any) => {
state.noticeList.unshift(msg);
ElNotification({
title: '提示',
message: '您有一条新消息...',
type: 'info',
position: 'bottom-right',
});
Push.create('提示', {
body: '你有一条新的消息',
icon: 'logo.png', // public目录下的
2024-06-15 13:02:35 +08:00
timeout: 4500, // 通知显示时间,单位为毫秒
});
};
2024-09-01 13:07:42 +08:00
// 修改密码
const changePassword = async () => {
// 若是超管则不进行强制修改密码和有效期验证
let userToken = decryptJWT(Local.get('access-token'));
if (userToken.AccountType == 999) return;
// 开启强制修改密码
var enabledForceChangePassword = themeConfig.value.forceChangePassword ?? true;
if (enabledForceChangePassword) {
if (userInfos.value.lastChangePasswordTime == null || userInfos.value.lastChangePasswordTime == undefined) {
changePasswordRef.value?.openDialog();
2024-12-17 17:39:00 +08:00
return;
}
}
2024-09-01 13:07:42 +08:00
// 验证密码有效期
2024-12-17 17:39:00 +08:00
var passwordExpirationTime = themeConfig.value.passwordExpirationTime ?? 0;
if (passwordExpirationTime > 0) {
var res = await getAPI(SysUserApi).apiSysUserVerifyPwdExpirationTimePost();
if (!res.data.result) {
changePasswordRef.value?.openDialog();
}
2024-09-01 13:07:42 +08:00
}
};
2024-06-15 13:02:35 +08:00
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-user {
display: flex;
align-items: center;
justify-content: flex-end;
&-link {
height: 100%;
display: flex;
align-items: center;
white-space: nowrap;
&-photo {
width: 25px;
height: 25px;
border-radius: 100%;
}
}
&-icon {
padding: 0 10px;
cursor: pointer;
color: var(--next-bg-topBarColor);
height: 50px;
line-height: 50px;
display: flex;
align-items: center;
&:hover {
background: var(--next-color-user-hover);
i {
display: inline-block;
animation: logoAnimation 0.3s ease-in-out;
}
}
}
:deep(.el-dropdown) {
color: var(--next-bg-topBarColor);
}
:deep(.el-badge) {
height: 40px;
line-height: 40px;
display: flex;
align-items: center;
}
:deep(.el-badge__content.is-fixed) {
top: 12px;
}
}
</style>