feat: 顶部右侧内容移除冗余代码,封装为组件

This commit is contained in:
TsMask
2025-06-12 10:43:47 +08:00
parent 4d171b0d06
commit 4e4b4bc2b7
8 changed files with 376 additions and 264 deletions

View File

@@ -5,7 +5,7 @@ import {
clearMenuItem,
MenuDataItem,
} from 'antdv-pro-layout';
import RightContent from './components/RightContent.vue';
import HeaderContentRight from './components/HeaderContentRight/HeaderContentRight.vue';
import Tabs from './components/Tabs.vue';
import GlobalMask from '@/components/GlobalMask/index.vue';
import ForcePasswdChange from '@/components/ForcePasswdChange/index.vue';
@@ -68,19 +68,6 @@ watch(
);
// 动态路由添加到菜单面板
// const rootRoute = router.getRoutes().find(r => r.name === 'Root');
// if (rootRoute) {
// const children = routerStore.setRootRouterData(rootRoute.children);
// const buildRouterData = routerStore.buildRouterData;
// if (buildRouterData.length > 0) {
// rootRoute.children = children.concat(buildRouterData);
// } else {
// rootRoute.children = children;
// }
// }
//
// const { menuData } = getMenuData(clearMenuItem(router.getRoutes()));
//
const menuData = computed(() => {
const rootRoute = router.getRoutes().find(r => r.name === 'Root');
if (rootRoute) {
@@ -151,10 +138,13 @@ tabsStore.clear();
// LOGO地址
const logoUrl = computed(() => {
let url =
appStore.logoType === 'brand'
? parseUrlPath(appStore.filePathBrand)
: parseUrlPath(appStore.filePathIcon);
let url = parseUrlPath(appStore.filePathIcon);
if (appStore.logoType === 'brand') {
url = parseUrlPath(appStore.filePathBrand);
}
if (layoutState.collapsed) {
url = parseUrlPath(appStore.filePathIcon);
}
if (url.indexOf('{language}') === -1) {
return url;
@@ -294,13 +284,17 @@ onUnmounted(() => {
:alt="appStore.appName"
:title="appStore.appName"
/>
<h1 class="app-name" :title="appStore.appName">
<h1
class="app-name"
:title="appStore.appName"
v-show="!layoutState.collapsed"
>
<span class="marquee app-name_scrollable">
{{ appStore.appName }}
</span>
</h1>
</template>
<template v-if="appStore.logoType === 'brand'">
<template v-else-if="appStore.logoType === 'brand'">
<img
class="logo-brand"
:src="logoUrl"
@@ -316,7 +310,7 @@ onUnmounted(() => {
<!--插槽-渲染顶部内容右端区域-->
<template #headerContentRightRender>
<RightContent />
<HeaderContentRight />
</template>
<!--插槽-导航标签项-->

View File

@@ -0,0 +1,124 @@
<script setup lang="ts">
import svgLight from '@/assets/svg/light.svg';
import svgDark from '@/assets/svg/dark.svg';
import { viewTransitionTheme } from 'antdv-pro-layout';
import { useRouter } from 'vue-router';
import { useFullscreen } from '@vueuse/core';
import { hasPermissions } from '@/plugins/auth-user';
import useI18n from '@/hooks/useI18n';
import useLayoutStore from '@/store/modules/layout';
import useAppStore from '@/store/modules/app';
import useAlarmStore from '@/store/modules/alarm';
import LockScreen from './components/LockScreen.vue';
import UserProfile from './components/UserProfile.vue';
import NetCoreSelect from './components/NetCoreSelect.vue';
const { isFullscreen, toggle } = useFullscreen();
const { t, changeLocale, optionsLocale, currentLocale } = useI18n();
const layoutStore = useLayoutStore();
const appStore = useAppStore();
const router = useRouter();
/**告警数按钮提示跳转 */
function fnClickAlarm() {
router.push({ name: 'ActiveAlarm_2088' });
}
/**改变主题色 */
function fnClickTheme(e: any) {
viewTransitionTheme(isDarkMode => {
layoutStore.changeConf('theme', isDarkMode ? 'light' : 'dark');
}, e);
}
/**改变多语言 */
function fnChangeLocale(e: any) {
changeLocale(e.key);
}
</script>
<template>
<a-space :size="12" align="center">
<!-- 切换核心网 -->
<NetCoreSelect></NetCoreSelect>
<!-- 告警气泡 -->
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.alarm') }}</template>
<a-button type="text" style="color: inherit" @click="fnClickAlarm">
<template #icon>
<a-badge
:count="useAlarmStore().activeAlarmTotal"
:overflow-count="99"
status="warning"
style="color: inherit"
>
<BellOutlined />
</a-badge>
</template>
</a-button>
</a-tooltip>
<!-- 锁屏操作 -->
<LockScreen></LockScreen>
<!-- 全屏操作 -->
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.fullscreen') }}</template>
<a-button type="text" style="color: inherit" @click="toggle">
<template #icon>
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</template>
</a-button>
</a-tooltip>
<!-- 明暗主题操作 -->
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.theme') }}</template>
<a-button type="text" @click="fnClickTheme">
<template #icon>
<img
v-if="layoutStore.proConfig.theme === 'dark'"
:src="svgDark"
class="theme-icon"
/>
<img v-else :src="svgLight" class="theme-icon" />
</template>
</a-button>
</a-tooltip>
<!-- 多语言操作 -->
<a-dropdown
placement="bottomRight"
trigger="click"
v-if="appStore.i18nOpen && hasPermissions(['system:setting:i18n'])"
>
<a-button type="text" style="color: inherit">
<template #icon> <TranslationOutlined /> </template>
</a-button>
<template #overlay>
<a-menu @click="fnChangeLocale">
<a-menu-item
v-for="opt in optionsLocale"
:key="opt.value"
:disabled="opt.value == currentLocale"
>
{{ opt.label }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<!-- 用户信息操作 -->
<UserProfile></UserProfile>
</a-space>
</template>
<style lang="css" scoped>
.theme-icon {
width: 18px;
height: 18px;
margin-bottom: 4px;
color: inherit;
}
</style>

View File

@@ -0,0 +1,71 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ProModal } from 'antdv-pro-modal';
import useMaskStore from '@/store/modules/mask';
import useI18n from '@/hooks/useI18n';
const maskStore = useMaskStore();
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
/**锁屏确认 */
const lockConfirm = ref<boolean>(false);
/**锁屏密码 */
const lockPasswd = ref<string>('');
/**锁屏按钮提示 */
function fnClickLock() {
lockConfirm.value = true;
lockPasswd.value = '';
maskStore.lockPasswd = '';
}
/**锁屏确认跳转锁屏页面 */
function fnClickLockToPage() {
lockConfirm.value = false;
maskStore.lockPasswd = lockPasswd.value;
maskStore.handleMaskType('lock');
router.push({ name: 'LockScreen', query: { redirect: route.path } });
}
</script>
<template>
<span v-perms:has="['system:setting:lock']">
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.lock') }}</template>
<a-button type="text" style="color: inherit" @click="fnClickLock()">
<template #icon>
<LockOutlined />
</template>
</a-button>
<ProModal
:drag="true"
:center-y="true"
:width="400"
:minHeight="200"
:mask-closable="false"
v-model:open="lockConfirm"
:title="t('loayouts.rightContent.lockTip')"
@ok="fnClickLockToPage()"
>
<a-space>
{{ t('loayouts.rightContent.lockPasswd') }}
<a-input-password
v-model:value="lockPasswd"
:placeholder="t('common.inputPlease')"
>
<template #prefix>
<a-tooltip
:title="t('loayouts.rightContent.lockPasswdTip')"
placement="topLeft"
>
<UnlockOutlined />
</a-tooltip>
</template>
</a-input-password>
</a-space>
</ProModal>
</a-tooltip>
</span>
</template>

View File

@@ -0,0 +1,50 @@
<script lang="ts" setup>
import { useRouter } from 'vue-router';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import useCoreStore from '@/store/modules/core';
import useI18n from '@/hooks/useI18n';
import { handleError, ref } from 'vue';
const coreStore = useCoreStore();
const router = useRouter();
const { t } = useI18n();
const coreValue = ref(coreStore.current);
const coreName = ref();
function handleSearch(v: string) {
coreValue.value = v;
coreStore.setCurrent(v);
}
</script>
<template>
<a-select
v-model:value="coreValue"
:options="coreStore.getCoreOptions"
style="width: 200px"
>
<template #option="{ value, label }">
<span>{{ label }} - {{ value }}</span>
</template>
<template #dropdownRender="{ menuNode: menu }">
<div style="display: flex; justify-content: space-between">
<span style="color: currentColor; font-weight: 700">Core</span>
<a-button type="text" size="small" @click="handleSearch('Global')">
<template #icon> <HomeOutlined /> </template>
Global
</a-button>
</div>
<a-input-search
v-model:value="coreName"
placeholder="Search Core Name"
style="width: 100%; margin: 4px 0"
enter-button
@search="handleSearch"
/>
<a-divider style="margin: 4px 0" />
<component :is="menu" />
</template>
</a-select>
</template>
<style lang="css" scoped></style>

View File

@@ -0,0 +1,83 @@
<script lang="ts" setup>
import { useRouter } from 'vue-router';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import useUserStore from '@/store/modules/user';
import useI18n from '@/hooks/useI18n';
const userStore = useUserStore();
const router = useRouter();
const { t } = useI18n();
/**头像展开项点击 */
function fnClick({ key }: MenuInfo) {
switch (key) {
case 'settings':
router.push({ name: 'Settings' });
break;
case 'profile':
router.push({ name: 'Profile' });
break;
case 'logout':
userStore.fnLogOut().finally(() => router.push({ name: 'Login' }));
break;
}
}
</script>
<template>
<a-dropdown placement="bottomRight" trigger="click">
<div class="user">
<a-avatar
shape="circle"
size="default"
:src="userStore.getAvatar"
:alt="userStore.userName"
></a-avatar>
<span class="nick">
{{ userStore.nickName }}
</span>
</div>
<template #overlay>
<a-menu @click="fnClick">
<!-- <a-menu-item key="profile">
<template #icon>
<UserOutlined />
</template>
<span>{{ t('loayouts.rightContent.profile') }}</span>
</a-menu-item> -->
<a-menu-item key="settings">
<template #icon>
<SettingOutlined />
</template>
<span>{{ t('loayouts.rightContent.settings') }}</span>
</a-menu-item>
<a-menu-divider />
<a-menu-item key="logout">
<template #icon>
<LogoutOutlined />
</template>
<span>{{ t('loayouts.rightContent.logout') }}</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
<style lang="less" scoped>
.user {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
cursor: pointer;
.nick {
padding-left: 8px;
padding-right: 16px;
font-size: 16px;
max-width: 164px;
white-space: nowrap;
text-align: start;
text-overflow: ellipsis;
overflow: hidden;
}
}
</style>

View File

@@ -1,241 +0,0 @@
<script setup lang="ts">
import svgLight from '@/assets/svg/light.svg';
import svgDark from '@/assets/svg/dark.svg';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { viewTransitionTheme } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useFullscreen } from '@vueuse/core';
import { hasPermissions } from '@/plugins/auth-user';
import useI18n from '@/hooks/useI18n';
import useLayoutStore from '@/store/modules/layout';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
import useAlarmStore from '@/store/modules/alarm';
import useMaskStore from '@/store/modules/mask';
const { isFullscreen, toggle } = useFullscreen();
const { t, changeLocale, optionsLocale } = useI18n();
const layoutStore = useLayoutStore();
const maskStore = useMaskStore();
const userStore = useUserStore();
const appStore = useAppStore();
const route = useRoute();
const router = useRouter();
/**头像展开项点击 */
function fnClick({ key }: MenuInfo) {
switch (key) {
case 'settings':
router.push({ name: 'Settings' });
break;
case 'profile':
router.push({ name: 'Profile' });
break;
case 'logout':
userStore.fnLogOut().finally(() => router.push({ name: 'Login' }));
break;
}
}
/**锁屏确认 */
const lockConfirm = ref<boolean>(false);
/**锁屏密码 */
const lockPasswd = ref<string>('');
/**锁屏按钮提示 */
function fnClickLock() {
lockConfirm.value = true;
lockPasswd.value = '';
maskStore.lockPasswd = '';
}
/**锁屏确认跳转锁屏页面 */
function fnClickLockToPage() {
lockConfirm.value = false;
maskStore.lockPasswd = lockPasswd.value;
maskStore.handleMaskType('lock');
router.push({ name: 'LockScreen', query: { redirect: route.path } });
}
/**告警数按钮提示跳转 */
function fnClickAlarm() {
router.push({ name: 'ActiveAlarm_2088' });
}
/**改变主题色 */
function fnClickTheme(e: any) {
viewTransitionTheme(isDarkMode => {
layoutStore.changeConf('theme', isDarkMode ? 'light' : 'dark');
}, e);
}
/**改变多语言 */
function fnChangeLocale(e: any) {
changeLocale(e.key);
}
</script>
<template>
<a-space :size="12" align="center">
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.alarm') }}</template>
<a-button type="text" style="color: inherit" @click="fnClickAlarm">
<template #icon>
<a-badge
:count="useAlarmStore().activeAlarmTotal"
:overflow-count="99"
status="warning"
style="color: inherit"
>
<BellOutlined />
</a-badge>
</template>
</a-button>
</a-tooltip>
<!-- 锁屏操作 -->
<span v-perms:has="['system:setting:lock']">
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.lock') }}</template>
<a-button type="text" style="color: inherit" @click="fnClickLock()">
<template #icon>
<LockOutlined />
</template>
</a-button>
<ProModal
:drag="true"
:center-y="true"
:width="400"
:minHeight="200"
:mask-closable="false"
v-model:open="lockConfirm"
:title="t('loayouts.rightContent.lockTip')"
@ok="fnClickLockToPage()"
>
<a-space>
{{ t('loayouts.rightContent.lockPasswd') }}
<a-input-password
v-model:value="lockPasswd"
:placeholder="t('common.inputPlease')"
>
<template #prefix>
<a-tooltip
:title="t('loayouts.rightContent.lockPasswdTip')"
placement="topLeft"
>
<UnlockOutlined />
</a-tooltip>
</template>
</a-input-password>
</a-space>
</ProModal>
</a-tooltip>
</span>
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.fullscreen') }}</template>
<a-button type="text" style="color: inherit" @click="toggle">
<template #icon>
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</template>
</a-button>
</a-tooltip>
<a-tooltip placement="bottomRight">
<template #title>{{ t('loayouts.rightContent.theme') }}</template>
<a-button type="text" @click="fnClickTheme">
<template #icon>
<img
v-if="layoutStore.proConfig.theme === 'dark'"
:src="svgDark"
class="theme-icon"
/>
<img v-else :src="svgLight" class="theme-icon" />
</template>
</a-button>
</a-tooltip>
<a-dropdown
placement="bottomRight"
trigger="click"
v-if="appStore.i18nOpen && hasPermissions(['system:setting:i18n'])"
>
<a-button type="text" style="color: inherit">
<template #icon> <TranslationOutlined /> </template>
</a-button>
<template #overlay>
<a-menu @click="fnChangeLocale">
<a-menu-item v-for="opt in optionsLocale" :key="opt.value">
{{ opt.label }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<a-dropdown placement="bottomRight" trigger="click">
<div class="user">
<a-avatar
shape="circle"
size="default"
:src="userStore.getAvatar"
:alt="userStore.userName"
></a-avatar>
<span class="nick">
{{ userStore.nickName }}
</span>
</div>
<template #overlay>
<a-menu @click="fnClick">
<!-- <a-menu-item key="profile">
<template #icon>
<UserOutlined />
</template>
<span>{{ t('loayouts.rightContent.profile') }}</span>
</a-menu-item> -->
<a-menu-item key="settings">
<template #icon>
<SettingOutlined />
</template>
<span>{{ t('loayouts.rightContent.settings') }}</span>
</a-menu-item>
<a-menu-divider />
<a-menu-item key="logout">
<template #icon>
<LogoutOutlined />
</template>
<span>{{ t('loayouts.rightContent.logout') }}</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-space>
</template>
<style lang="less" scoped>
.user {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
cursor: pointer;
.nick {
padding-left: 8px;
padding-right: 16px;
font-size: 16px;
max-width: 164px;
white-space: nowrap;
text-align: start;
text-overflow: ellipsis;
overflow: hidden;
}
}
.theme-icon {
width: 18px;
height: 18px;
margin-bottom: 4px;
color: inherit;
}
</style>

31
src/store/modules/core.ts Normal file
View File

@@ -0,0 +1,31 @@
import { defineStore } from 'pinia';
/**核心网信息类型 */
type Core = {
/**当前选择 */
current: string;
/**核心网选择 */
coreOptions: Record<string, any>[];
};
const useCoreStore = defineStore('core', {
state: (): Core => ({
current: 'Global',
coreOptions: [
{ label: 'Core', value: 'Core' },
{ label: 'Core2', value: 'Core2' },
],
}),
getters: {
getCoreOptions(): Record<string, any>[] {
return this.coreOptions;
},
},
actions: {
setCurrent(value: string = 'Global') {
this.current = value;
},
},
});
export default useCoreStore;

View File

@@ -70,9 +70,9 @@ const proRender = (render: any) => (render === false ? false : undefined);
const proConfigLocal: LayoutStore['proConfig'] = localGetJSON(
CACHE_LOCAL_PROCONFIG
) || {
layout: 'mix',
layout: 'side',
theme: 'light',
menuTheme: 'light',
menuTheme: 'dark',
fixSiderbar: true,
fixedHeader: true,
splitMenus: true,