| | |
| | | <script lang="ts" setup> |
| | | import type { NotificationItem } from '@vben/layouts'; |
| | | |
| | | import type { SystemTenantApi } from '#/api/system/tenant'; |
| | | |
| | | import { computed, onMounted, ref, watch } from 'vue'; |
| | | |
| | | import { useAccess } from '@vben/access'; |
| | | import { AuthenticationLoginExpiredModal, useVbenModal } from '@vben/common-ui'; |
| | | import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; |
| | | import { isTenantEnable, useTabs, useWatermark } from '@vben/hooks'; |
| | | import { useWatermark } from '@vben/hooks'; |
| | | import { |
| | | AntdProfileOutlined, |
| | | BookOpenText, |
| | |
| | | Help, |
| | | LockScreen, |
| | | Notification, |
| | | TenantDropdown, |
| | | UserDropdown, |
| | | } from '@vben/layouts'; |
| | | import { preferences, usePreferences } from '@vben/preferences'; |
| | | import { useAccessStore, useUserStore } from '@vben/stores'; |
| | | import { formatDateTime, openWindow } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | getUnreadNotifyMessageCount, |
| | |
| | | updateAllNotifyMessageRead, |
| | | updateNotifyMessageRead, |
| | | } from '#/api/system/notify/message'; |
| | | import { getSimpleTenantList } from '#/api/system/tenant'; |
| | | import { $t } from '#/locales'; |
| | | import { router } from '#/router'; |
| | | import { useAuthStore } from '#/store'; |
| | | import LoginForm from '#/views/_core/authentication/login.vue'; |
| | | import PortalBrandText from '#/components/portal/portal-brand-text.vue'; |
| | | |
| | | const userStore = useUserStore(); |
| | | const authStore = useAuthStore(); |
| | | const accessStore = useAccessStore(); |
| | | const { hasAccessByCodes } = useAccess(); |
| | | const { destroyWatermark, updateWatermark } = useWatermark(); |
| | | const { closeOtherTabs, refreshTab } = useTabs(); |
| | | |
| | | const notifications = ref<NotificationItem[]>([]); |
| | | const unreadCount = ref(0); |
| | |
| | | }, |
| | | icon: AntdProfileOutlined, |
| | | text: $t('ui.widgets.profile'), |
| | | }, |
| | | { |
| | | handler: () => router.push({ name: 'Profile', query: { section: 'resetPwd' } }), |
| | | icon: 'lucide:shield-check', |
| | | text: '密码设置', |
| | | }, |
| | | // { |
| | | // handler: () => { |
| | |
| | | handleNotificationGetUnreadCount(); |
| | | } |
| | | |
| | | // 租户列表 |
| | | const tenants = ref<SystemTenantApi.Tenant[]>([]); |
| | | const tenantEnable = computed( |
| | | () => hasAccessByCodes(['system:tenant:visit']) && isTenantEnable(), |
| | | ); |
| | | |
| | | /** 获取租户列表 */ |
| | | async function handleGetTenantList() { |
| | | if (tenantEnable.value) { |
| | | tenants.value = await getSimpleTenantList(); |
| | | } |
| | | } |
| | | |
| | | /** 处理租户切换 */ |
| | | async function handleTenantChange(tenant: SystemTenantApi.Tenant) { |
| | | if (!tenant || !tenant.id) { |
| | | message.error('切换租户失败'); |
| | | return; |
| | | } |
| | | // 设置访问租户 ID |
| | | accessStore.setVisitTenantId(tenant.id as number); |
| | | // 关闭其他标签页,只保留当前页 |
| | | await closeOtherTabs(); |
| | | // 刷新当前页面 |
| | | await refreshTab(); |
| | | // 提示切换成功 |
| | | message.success(`切换当前租户为: ${tenant.name}`); |
| | | } |
| | | |
| | | // ========== 初始化 ========== |
| | | onMounted(() => { |
| | | // 首次加载未读数量 |
| | | handleNotificationGetUnreadCount(); |
| | | // 获取租户列表 |
| | | handleGetTenantList(); |
| | | // 轮询刷新未读数量 |
| | | setInterval( |
| | | () => { |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicLayout @clear-preferences-and-logout="handleLogout"> |
| | | <BasicLayout class="supply-portal-shell" @clear-preferences-and-logout="handleLogout" @click-logo="router.push('/analytics')"> |
| | | <template #logo-text> |
| | | <PortalBrandText /> |
| | | </template> |
| | | <template #user-dropdown> |
| | | <UserDropdown |
| | | show-text |
| | | :avatar |
| | | :menus |
| | | :text="userStore.userInfo?.nickname" |
| | |
| | | @logout="handleLogout" |
| | | @clear-preferences-and-logout="handleLogout" |
| | | /> |
| | | </template> |
| | | <template #header-right-165> |
| | | <button type="button" class="portal-personal-entry" aria-label="个人中心" title="个人中心" :class="{ 'is-current': router.currentRoute.value.name === 'Profile' }" @click="router.push({ name: 'Profile' })"><IconifyIcon icon="lucide:user-round" /><span>个人中心</span></button> |
| | | </template> |
| | | <template #notification> |
| | | <Notification |
| | |
| | | @on-click="handleClick" |
| | | /> |
| | | </template> |
| | | <!-- <template #header-right-1> |
| | | <div v-if="tenantEnable"> |
| | | <TenantDropdown |
| | | class="mr-2" |
| | | :tenant-list="tenants" |
| | | :visit-tenant-id="accessStore.visitTenantId" |
| | | @success="handleTenantChange" |
| | | /> |
| | | </div> |
| | | </template> --> |
| | | <template #header-right-170> |
| | | <template v-if="router.hasRoute('MesWorkbench') && accessStore.accessMenus.some((menu) => /生产|MES/i.test(menu.name))" #header-right-170> |
| | | <button |
| | | type="button" |
| | | class="flex cursor-pointer items-center gap-1.5 rounded-md border-none bg-primary/15 px-3 py-1.5 text-sm font-medium text-primary transition-colors hover:bg-primary/25" |