|
|
@@ -2,7 +2,15 @@
|
|
|
import type { IContextMenuItem } from '@velofex-core/tabs-ui';
|
|
|
import type { MenuProps } from 'antdv-next';
|
|
|
|
|
|
-import { computed, h, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
|
+import {
|
|
|
+ computed,
|
|
|
+ defineComponent,
|
|
|
+ h,
|
|
|
+ onBeforeUnmount,
|
|
|
+ onMounted,
|
|
|
+ ref,
|
|
|
+ watch,
|
|
|
+} from 'vue';
|
|
|
|
|
|
import {
|
|
|
loadLocaleMessages,
|
|
|
@@ -21,13 +29,25 @@ import defaultAvatar from '@/assets/image/user.png';
|
|
|
import { resolveEnterpriseCodeFromLocation } from '@/router/guard';
|
|
|
import { Dropdown, Menu } from 'antdv-next';
|
|
|
|
|
|
+import HomeDashboardTab from '#/components/home-dashboard-tab.vue';
|
|
|
import SelectLang from '#/components/select-lang.vue';
|
|
|
import { $t } from '#/locales';
|
|
|
|
|
|
+const HOME_TAB_KEY = '__home__';
|
|
|
+const HomeTabIcon = defineComponent({
|
|
|
+ name: 'HomeTabIcon',
|
|
|
+ setup(_, { attrs }) {
|
|
|
+ return () =>
|
|
|
+ h('i', {
|
|
|
+ ...attrs,
|
|
|
+ class: ['home-tab-icon', 'icon-dashboard', attrs.class],
|
|
|
+ });
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
const leftMenuItems = ref<MenuProps['items']>([]);
|
|
|
const selectedKeys = ref<string[]>([]);
|
|
|
const userMenuItems = computed<MenuProps['items']>(() => [
|
|
|
- { key: 'ChangeInformation', label: $t('home.userMenu.changeInformation') },
|
|
|
{ key: 'Logout', label: $t('home.userMenu.logout') },
|
|
|
]);
|
|
|
const openMenuKeys = ref<string[]>([]);
|
|
|
@@ -42,9 +62,10 @@ const iframeTabs = ref<
|
|
|
Array<{ crumb: string; iframeSrc: string; key: string; title: string }>
|
|
|
>([]);
|
|
|
const activeTabKey = ref('');
|
|
|
+const homeRefreshStamp = ref(Date.now());
|
|
|
const companyLogoSrc = ref('/Content/Images/company-logo.png');
|
|
|
const avatarSrc = computed(() => {
|
|
|
- const avatar = userInfo.value?.avatar;
|
|
|
+ const avatar = userInfo.value?.avatarFileId;
|
|
|
return avatar ? `/File/Download?fileId=${avatar}` : defaultAvatar;
|
|
|
});
|
|
|
const contentTabs = computed(() => {
|
|
|
@@ -52,7 +73,8 @@ const contentTabs = computed(() => {
|
|
|
return {
|
|
|
fullPath: tab.key,
|
|
|
meta: {
|
|
|
- tabClosable: true,
|
|
|
+ icon: tab.key === HOME_TAB_KEY ? HomeTabIcon : undefined,
|
|
|
+ tabClosable: tab.key !== HOME_TAB_KEY,
|
|
|
title: tab.title,
|
|
|
},
|
|
|
name: tab.title,
|
|
|
@@ -60,6 +82,13 @@ const contentTabs = computed(() => {
|
|
|
} as any;
|
|
|
});
|
|
|
});
|
|
|
+const homeTabMeta = computed(() => ({
|
|
|
+ crumb: $t('home.dashboard.crumb'),
|
|
|
+ iframeSrc: '',
|
|
|
+ icon: HomeTabIcon,
|
|
|
+ key: HOME_TAB_KEY,
|
|
|
+ title: $t('home.dashboard.tab'),
|
|
|
+}));
|
|
|
|
|
|
function handleCompanyLogoError() {
|
|
|
companyLogoSrc.value = defaultCompanyLogo;
|
|
|
@@ -195,8 +224,27 @@ function clearActiveTab() {
|
|
|
pageCrumb.value = '';
|
|
|
}
|
|
|
|
|
|
+function ensureHomeTab() {
|
|
|
+ const nextHomeTab = homeTabMeta.value;
|
|
|
+ const index = iframeTabs.value.findIndex((tab) => tab.key === HOME_TAB_KEY);
|
|
|
+
|
|
|
+ if (index === -1) {
|
|
|
+ iframeTabs.value.unshift(nextHomeTab);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ iframeTabs.value.splice(index, 1, nextHomeTab);
|
|
|
+}
|
|
|
+
|
|
|
function setActiveTab(key: string) {
|
|
|
activeTabKey.value = key;
|
|
|
+ if (key === HOME_TAB_KEY) {
|
|
|
+ selectedKeys.value = [];
|
|
|
+ pageTitle.value = homeTabMeta.value.title;
|
|
|
+ pageCrumb.value = homeTabMeta.value.crumb;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
selectedKeys.value = [key];
|
|
|
applyMenuMeta(key);
|
|
|
}
|
|
|
@@ -241,6 +289,10 @@ async function loadLeftMenuFromB() {
|
|
|
|
|
|
menuMetaByKey.value = nextMetaMap;
|
|
|
iframeTabs.value = iframeTabs.value.flatMap((tab) => {
|
|
|
+ if (tab.key === HOME_TAB_KEY) {
|
|
|
+ return [homeTabMeta.value];
|
|
|
+ }
|
|
|
+
|
|
|
const meta = nextMetaMap[tab.key];
|
|
|
if (!meta?.iframeSrc) {
|
|
|
return [];
|
|
|
@@ -255,10 +307,10 @@ async function loadLeftMenuFromB() {
|
|
|
},
|
|
|
];
|
|
|
});
|
|
|
+ ensureHomeTab();
|
|
|
|
|
|
leftMenuItems.value = items;
|
|
|
if (firstLeafPath.length > 0) {
|
|
|
- const defaultKey = firstLeafPath[firstLeafPath.length - 1]!;
|
|
|
openMenuKeys.value = items
|
|
|
.filter(
|
|
|
(item) =>
|
|
|
@@ -273,13 +325,11 @@ async function loadLeftMenuFromB() {
|
|
|
setActiveTab(activeTabKey.value);
|
|
|
} else if (iframeTabs.value.length > 0) {
|
|
|
setActiveTab(iframeTabs.value[0]!.key);
|
|
|
- } else {
|
|
|
- openIframeTab(defaultKey);
|
|
|
}
|
|
|
} else {
|
|
|
openMenuKeys.value = [];
|
|
|
- iframeTabs.value = [];
|
|
|
- clearActiveTab();
|
|
|
+ ensureHomeTab();
|
|
|
+ setActiveTab(HOME_TAB_KEY);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -381,9 +431,63 @@ function handleLeftMenuClick({ key }: { key: string }) {
|
|
|
closeMobileSidebar();
|
|
|
}
|
|
|
|
|
|
+function findMenuPathByKey(
|
|
|
+ items: MenuProps['items'],
|
|
|
+ targetKey: string,
|
|
|
+ parentPath: string[] = [],
|
|
|
+): null | string[] {
|
|
|
+ for (const item of items ?? []) {
|
|
|
+ if (!item) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentKey = String((item as any).key ?? '');
|
|
|
+ if (!currentKey) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentPath = [...parentPath, currentKey];
|
|
|
+ if (currentKey === targetKey) {
|
|
|
+ return currentPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ const childItems = (item as any).children as MenuProps['items'];
|
|
|
+ if (!childItems || childItems.length === 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const matchedPath = findMenuPathByKey(childItems, targetKey, currentPath);
|
|
|
+ if (matchedPath) {
|
|
|
+ return matchedPath;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+}
|
|
|
+
|
|
|
+function handleDashboardMenuOpen(key: string) {
|
|
|
+ const normalizedKey = String(key ?? '');
|
|
|
+ if (!normalizedKey) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!menuMetaByKey.value[normalizedKey]?.iframeSrc) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const menuPath = findMenuPathByKey(leftMenuItems.value, normalizedKey);
|
|
|
+ if (menuPath && menuPath.length > 1) {
|
|
|
+ const parentKeys = menuPath.slice(0, -1);
|
|
|
+ openMenuKeys.value = [...new Set([...openMenuKeys.value, ...parentKeys])];
|
|
|
+ }
|
|
|
+
|
|
|
+ openIframeTab(normalizedKey);
|
|
|
+ closeMobileSidebar();
|
|
|
+}
|
|
|
+
|
|
|
function handleTabChange(key: string) {
|
|
|
const normalizedKey = String(key);
|
|
|
- if (!menuMetaByKey.value[normalizedKey]) {
|
|
|
+ if (normalizedKey !== HOME_TAB_KEY && !menuMetaByKey.value[normalizedKey]) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -392,6 +496,10 @@ function handleTabChange(key: string) {
|
|
|
|
|
|
function handleTabClose(key: string) {
|
|
|
const normalizedKey = String(key);
|
|
|
+ if (normalizedKey === HOME_TAB_KEY) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const currentIndex = iframeTabs.value.findIndex(
|
|
|
(tab) => tab.key === normalizedKey,
|
|
|
);
|
|
|
@@ -452,6 +560,12 @@ function addTabRefreshStamp(url: string) {
|
|
|
|
|
|
function refreshTabByKey(key: string) {
|
|
|
const normalizedKey = String(key);
|
|
|
+ if (normalizedKey === HOME_TAB_KEY) {
|
|
|
+ homeRefreshStamp.value = Date.now();
|
|
|
+ setActiveTab(HOME_TAB_KEY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const index = iframeTabs.value.findIndex((tab) => tab.key === normalizedKey);
|
|
|
if (index === -1) {
|
|
|
return;
|
|
|
@@ -476,24 +590,33 @@ function closeOtherTabsByKey(key: string) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- iframeTabs.value = [targetTab];
|
|
|
+ iframeTabs.value =
|
|
|
+ normalizedKey === HOME_TAB_KEY
|
|
|
+ ? [homeTabMeta.value]
|
|
|
+ : [homeTabMeta.value, targetTab];
|
|
|
setActiveTab(normalizedKey);
|
|
|
}
|
|
|
|
|
|
function closeAllTabs() {
|
|
|
- iframeTabs.value = [];
|
|
|
- clearActiveTab();
|
|
|
+ iframeTabs.value = [homeTabMeta.value];
|
|
|
+ setActiveTab(HOME_TAB_KEY);
|
|
|
}
|
|
|
|
|
|
function createTabContextMenus(tab: { key?: string }) {
|
|
|
const tabKey = String(tab?.key ?? '');
|
|
|
const hasTabs = iframeTabs.value.length > 0;
|
|
|
- const isCurrentTabClosable =
|
|
|
+ const isCurrentTabRefreshable =
|
|
|
hasTabs && tabKey && iframeTabs.value.some((item) => item.key === tabKey);
|
|
|
+ const isHomeTab = tabKey === HOME_TAB_KEY;
|
|
|
+ const isCurrentTabClosable =
|
|
|
+ hasTabs &&
|
|
|
+ !isHomeTab &&
|
|
|
+ tabKey &&
|
|
|
+ iframeTabs.value.some((item) => item.key === tabKey);
|
|
|
|
|
|
const menus: IContextMenuItem[] = [
|
|
|
{
|
|
|
- disabled: !isCurrentTabClosable,
|
|
|
+ disabled: !isCurrentTabRefreshable,
|
|
|
handler: () => {
|
|
|
refreshTabByKey(tabKey);
|
|
|
},
|
|
|
@@ -509,7 +632,7 @@ function createTabContextMenus(tab: { key?: string }) {
|
|
|
text: '关闭当前',
|
|
|
},
|
|
|
{
|
|
|
- disabled: !isCurrentTabClosable || iframeTabs.value.length <= 1,
|
|
|
+ disabled: !tabKey || iframeTabs.value.length <= 1,
|
|
|
handler: () => {
|
|
|
closeOtherTabsByKey(tabKey);
|
|
|
},
|
|
|
@@ -536,16 +659,8 @@ function closeMobileSidebar() {
|
|
|
}
|
|
|
|
|
|
function handleUserMenuClick({ key }: { key: string }) {
|
|
|
- if (key === 'ChangeInformation' && window.top && window.top !== window) {
|
|
|
- window.top.location.href = '/user-profile';
|
|
|
- }
|
|
|
-
|
|
|
if (key === 'Logout') {
|
|
|
const enterpriseCode = resolveEnterpriseCodeFromLocation();
|
|
|
- // if (enterpriseCode) {
|
|
|
- // localStorage.removeItem(`token_${enterpriseCode}`);
|
|
|
- // }
|
|
|
-
|
|
|
const logoutUrl = `/Account/Logout?enterpriseCode=${enterpriseCode}`;
|
|
|
try {
|
|
|
if (window.top && window.top !== window) {
|
|
|
@@ -557,6 +672,25 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
window.location.href = logoutUrl;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+function handleIframeLoad(event: Event) {
|
|
|
+ const iframe = event.target as HTMLIFrameElement | null;
|
|
|
+ if (!iframe) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const doc = iframe.contentDocument;
|
|
|
+ if (!doc) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ doc.documentElement.style.background = 'transparent';
|
|
|
+ if (doc.body) {
|
|
|
+ doc.body.style.background = 'transparent';
|
|
|
+ }
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
@@ -627,14 +761,17 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
</div>
|
|
|
</header>
|
|
|
|
|
|
- <div class="content-placeholder">
|
|
|
+ <div
|
|
|
+ :class="{ 'home-active': activeTabKey === HOME_TAB_KEY }"
|
|
|
+ class="content-placeholder"
|
|
|
+ >
|
|
|
<template v-if="iframeTabs.length > 0">
|
|
|
<div class="content-tabs-wrap">
|
|
|
<TabsView
|
|
|
:active="activeTabKey"
|
|
|
:context-menus="createTabContextMenus"
|
|
|
:draggable="preferences.tabbar.draggable"
|
|
|
- :show-icon="false"
|
|
|
+ :show-icon="true"
|
|
|
:style-type="preferences.tabbar.styleType"
|
|
|
:tabs="contentTabs"
|
|
|
:wheelable="preferences.tabbar.wheelable"
|
|
|
@@ -644,14 +781,26 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
- <div class="content-iframe-stack">
|
|
|
+ <div
|
|
|
+ :class="{ 'home-active': activeTabKey === HOME_TAB_KEY }"
|
|
|
+ class="content-iframe-stack"
|
|
|
+ >
|
|
|
+ <HomeDashboardTab
|
|
|
+ v-show="activeTabKey === HOME_TAB_KEY"
|
|
|
+ :key="homeRefreshStamp"
|
|
|
+ :is-super-admin="userInfo?.isSuperAdmin"
|
|
|
+ class="h-full"
|
|
|
+ @open-menu="handleDashboardMenuOpen"
|
|
|
+ />
|
|
|
+
|
|
|
<iframe
|
|
|
v-for="tab in iframeTabs"
|
|
|
- v-show="tab.key === activeTabKey"
|
|
|
+ v-show="tab.key === activeTabKey && tab.key !== HOME_TAB_KEY"
|
|
|
:key="tab.key"
|
|
|
:src="tab.iframeSrc"
|
|
|
border="0"
|
|
|
class="content-iframe"
|
|
|
+ @load="handleIframeLoad"
|
|
|
></iframe>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -719,13 +868,6 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
height: 100vh;
|
|
|
min-height: 100vh;
|
|
|
overflow: hidden;
|
|
|
- background: radial-gradient(
|
|
|
- circle at 78% 88%,
|
|
|
- rgb(193 233 255 / 35%),
|
|
|
- transparent 36%
|
|
|
- ),
|
|
|
- radial-gradient(circle at 70% 92%, rgb(255 216 199 / 30%), transparent 30%),
|
|
|
- #f2f0f3;
|
|
|
}
|
|
|
|
|
|
.enterprise-shell {
|
|
|
@@ -733,7 +875,7 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
height: 100vh;
|
|
|
min-height: 100vh;
|
|
|
overflow: hidden;
|
|
|
- background: #f8f6f8;
|
|
|
+ background: linear-gradient(180deg, #f8f6f8 0%, #fdf2f7 100%);
|
|
|
box-shadow: 0 10px 30px rgb(60 34 51 / 8%);
|
|
|
}
|
|
|
|
|
|
@@ -762,7 +904,7 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
min-height: 0;
|
|
|
padding: 24px 16px;
|
|
|
overflow: hidden;
|
|
|
- background: #f8f6f8;
|
|
|
+ background: linear-gradient(180deg, #f8f6f8 0%, #fdf2f7 100%);
|
|
|
border-right: none;
|
|
|
}
|
|
|
|
|
|
@@ -975,6 +1117,19 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
fill: rgb(139 22 72 / 10%) !important;
|
|
|
}
|
|
|
|
|
|
+:deep(.content-tabs-wrap .home-tab-icon) {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ line-height: 1;
|
|
|
+ color: #111;
|
|
|
+ vertical-align: middle;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.content-tabs-wrap .is-active .home-tab-icon) {
|
|
|
+ color: #111 !important;
|
|
|
+}
|
|
|
+
|
|
|
:deep(.overflow-y-hidden) {
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
@@ -987,7 +1142,6 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
padding: 28px 32px;
|
|
|
overflow: hidden;
|
|
|
border-radius: 50px 0 0 50px;
|
|
|
- box-shadow: 0 8px 20px rgb(95 67 84 / 10%);
|
|
|
}
|
|
|
|
|
|
.right-panel::before {
|
|
|
@@ -995,7 +1149,11 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
inset: 0;
|
|
|
z-index: 0;
|
|
|
content: '';
|
|
|
- background: #fff;
|
|
|
+ background-color: #fff;
|
|
|
+ background-image: url('@/assets/image/bg.png');
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-position: center;
|
|
|
+ background-size: cover;
|
|
|
}
|
|
|
|
|
|
.right-panel > * {
|
|
|
@@ -1047,7 +1205,11 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
min-height: 0;
|
|
|
margin-top: 12px;
|
|
|
overflow: hidden;
|
|
|
- background: #fff;
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.content-placeholder.home-active {
|
|
|
+ overflow: visible;
|
|
|
}
|
|
|
|
|
|
.content-tabs-wrap {
|
|
|
@@ -1063,12 +1225,18 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
position: relative;
|
|
|
flex: 1;
|
|
|
min-height: 0;
|
|
|
+ overflow: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.content-iframe-stack.home-active {
|
|
|
+ overflow: visible;
|
|
|
}
|
|
|
|
|
|
.content-iframe {
|
|
|
display: block;
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
+ background: transparent;
|
|
|
border: 0;
|
|
|
}
|
|
|
|