perf: tenant-dropdown
							parent
							
								
									56b4751be1
								
							
						
					
					
						commit
						5b4846e93d
					
				|  | @ -1,11 +1,13 @@ | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import type { NotificationItem } from '@vben/layouts'; | import type { NotificationItem } from '@vben/layouts'; | ||||||
| 
 | 
 | ||||||
|  | import type { SystemTenantApi } from '#/api/system/tenant'; | ||||||
|  | 
 | ||||||
| import { computed, onMounted, ref, watch } from 'vue'; | import { computed, onMounted, ref, watch } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { AuthenticationLoginExpiredModal, useVbenModal } from '@vben/common-ui'; | import { AuthenticationLoginExpiredModal, useVbenModal } from '@vben/common-ui'; | ||||||
| import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; | import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; | ||||||
| import { useWatermark } from '@vben/hooks'; | import { useTabs, useWatermark } from '@vben/hooks'; | ||||||
| import { | import { | ||||||
|   AntdProfileOutlined, |   AntdProfileOutlined, | ||||||
|   BookOpenText, |   BookOpenText, | ||||||
|  | @ -16,30 +18,37 @@ import { | ||||||
|   BasicLayout, |   BasicLayout, | ||||||
|   LockScreen, |   LockScreen, | ||||||
|   Notification, |   Notification, | ||||||
|  |   TenantDropdown, | ||||||
|   UserDropdown, |   UserDropdown, | ||||||
| } from '@vben/layouts'; | } from '@vben/layouts'; | ||||||
| import { preferences } from '@vben/preferences'; | import { preferences } from '@vben/preferences'; | ||||||
| import { useAccessStore, useUserStore } from '@vben/stores'; | import { useAccessStore, useUserStore } from '@vben/stores'; | ||||||
| import { formatDateTime, openWindow } from '@vben/utils'; | import { formatDateTime, openWindow } from '@vben/utils'; | ||||||
| 
 | 
 | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
| import { | import { | ||||||
|   getUnreadNotifyMessageCount, |   getUnreadNotifyMessageCount, | ||||||
|   getUnreadNotifyMessageList, |   getUnreadNotifyMessageList, | ||||||
|   updateAllNotifyMessageRead, |   updateAllNotifyMessageRead, | ||||||
|   updateNotifyMessageRead, |   updateNotifyMessageRead, | ||||||
| } from '#/api/system/notify/message'; | } from '#/api/system/notify/message'; | ||||||
|  | import { getSimpleTenantList } from '#/api/system/tenant'; | ||||||
| import { $t } from '#/locales'; | import { $t } from '#/locales'; | ||||||
| import { router } from '#/router'; | import { router } from '#/router'; | ||||||
| import { useAuthStore } from '#/store'; | import { useAuthStore } from '#/store'; | ||||||
| import LoginForm from '#/views/_core/authentication/login.vue'; | import LoginForm from '#/views/_core/authentication/login.vue'; | ||||||
| 
 | 
 | ||||||
| import Help from './components/help.vue'; | import Help from './components/help.vue'; | ||||||
| import TenantDropdown from './components/tenant-dropdown.vue'; | 
 | ||||||
|  | // 租户列表 | ||||||
|  | const tenants = ref<SystemTenantApi.Tenant[]>([]); | ||||||
| 
 | 
 | ||||||
| const userStore = useUserStore(); | const userStore = useUserStore(); | ||||||
| const authStore = useAuthStore(); | const authStore = useAuthStore(); | ||||||
| const accessStore = useAccessStore(); | const accessStore = useAccessStore(); | ||||||
| const { destroyWatermark, updateWatermark } = useWatermark(); | const { destroyWatermark, updateWatermark } = useWatermark(); | ||||||
|  | const { closeOtherTabs, refreshTab } = useTabs(); | ||||||
| 
 | 
 | ||||||
| const notifications = ref<NotificationItem[]>([]); | const notifications = ref<NotificationItem[]>([]); | ||||||
| const unreadCount = ref(0); | const unreadCount = ref(0); | ||||||
|  | @ -148,10 +157,33 @@ function handleNotificationOpen(open: boolean) { | ||||||
|   handleNotificationGetUnreadCount(); |   handleNotificationGetUnreadCount(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** 获取租户列表 */ | ||||||
|  | async function handleGetTenantList() { | ||||||
|  |   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(() => { | onMounted(() => { | ||||||
|   // 首次加载未读数量 |   // 首次加载未读数量 | ||||||
|   handleNotificationGetUnreadCount(); |   handleNotificationGetUnreadCount(); | ||||||
|  |   // 获取租户列表 | ||||||
|  |   handleGetTenantList(); | ||||||
|   // 轮询刷新未读数量 |   // 轮询刷新未读数量 | ||||||
|   setInterval( |   setInterval( | ||||||
|     () => { |     () => { | ||||||
|  | @ -204,7 +236,14 @@ watch( | ||||||
|       /> |       /> | ||||||
|     </template> |     </template> | ||||||
|     <template #header-right-1> |     <template #header-right-1> | ||||||
|       <TenantDropdown class="mr-2 w-44" /> |       <div v-access:code="['system:tenant:visit']"> | ||||||
|  |         <TenantDropdown | ||||||
|  |           class="mr-2" | ||||||
|  |           :tenant-list="tenants" | ||||||
|  |           :visit-tenant-id="accessStore.visitTenantId" | ||||||
|  |           @success="handleTenantChange" | ||||||
|  |         /> | ||||||
|  |       </div> | ||||||
|     </template> |     </template> | ||||||
|     <template #extra> |     <template #extra> | ||||||
|       <AuthenticationLoginExpiredModal |       <AuthenticationLoginExpiredModal | ||||||
|  |  | ||||||
|  | @ -1,63 +0,0 @@ | ||||||
| <script lang="ts" setup> |  | ||||||
| import type { SelectValue } from 'ant-design-vue/es/select'; |  | ||||||
| 
 |  | ||||||
| import type { SystemTenantApi } from '#/api/system/tenant'; |  | ||||||
| 
 |  | ||||||
| import { onMounted, ref } from 'vue'; |  | ||||||
| 
 |  | ||||||
| import { useAccess } from '@vben/access'; |  | ||||||
| import { isTenantEnable, useTabs } from '@vben/hooks'; |  | ||||||
| import { useAccessStore } from '@vben/stores'; |  | ||||||
| 
 |  | ||||||
| import { message, Select } from 'ant-design-vue'; |  | ||||||
| 
 |  | ||||||
| import { getSimpleTenantList } from '#/api/system/tenant'; |  | ||||||
| import { $t } from '#/locales'; |  | ||||||
| 
 |  | ||||||
| const { closeOtherTabs, refreshTab } = useTabs(); |  | ||||||
| 
 |  | ||||||
| const { hasAccessByCodes } = useAccess(); |  | ||||||
| const accessStore = useAccessStore(); |  | ||||||
| 
 |  | ||||||
| const tenantEnable = isTenantEnable(); |  | ||||||
| 
 |  | ||||||
| // 当前访问的租户 ID |  | ||||||
| const value = ref<number | undefined>(accessStore.visitTenantId ?? undefined); |  | ||||||
| // 租户列表 |  | ||||||
| const tenants = ref<SystemTenantApi.Tenant[]>([]); |  | ||||||
| 
 |  | ||||||
| async function handleChange(id: SelectValue) { |  | ||||||
|   // 设置访问租户 ID |  | ||||||
|   accessStore.setVisitTenantId(id as number); |  | ||||||
|   // 关闭其他标签页,只保留当前页 |  | ||||||
|   await closeOtherTabs(); |  | ||||||
|   // 刷新当前页面 |  | ||||||
|   await refreshTab(); |  | ||||||
|   // 提示切换成功 |  | ||||||
|   const tenant = tenants.value.find((item) => item.id === id); |  | ||||||
|   if (tenant) { |  | ||||||
|     message.success(`切换当前租户为: ${tenant.name}`); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| onMounted(async () => { |  | ||||||
|   if (!tenantEnable) { |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   tenants.value = await getSimpleTenantList(); |  | ||||||
| }); |  | ||||||
| </script> |  | ||||||
| <template> |  | ||||||
|   <div v-if="tenantEnable && hasAccessByCodes(['system:tenant:visit'])"> |  | ||||||
|     <Select |  | ||||||
|       v-model:value="value" |  | ||||||
|       :field-names="{ label: 'name', value: 'id' }" |  | ||||||
|       :options="tenants" |  | ||||||
|       :placeholder="$t('page.tenant.placeholder')" |  | ||||||
|       :dropdown-style="{ position: 'fixed', zIndex: 1666 }" |  | ||||||
|       allow-clear |  | ||||||
|       class="w-40" |  | ||||||
|       @change="handleChange" |  | ||||||
|     /> |  | ||||||
|   </div> |  | ||||||
| </template> |  | ||||||
|  | @ -7,5 +7,6 @@ export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; | ||||||
| export * from './lock-screen'; | export * from './lock-screen'; | ||||||
| export * from './notification'; | export * from './notification'; | ||||||
| export * from './preferences'; | export * from './preferences'; | ||||||
|  | export * from './tenant-dropdown'; | ||||||
| export * from './theme-toggle'; | export * from './theme-toggle'; | ||||||
| export * from './user-dropdown'; | export * from './user-dropdown'; | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | export { default as TenantDropdown } from './tenant-dropdown.vue'; | ||||||
|  | @ -0,0 +1,76 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { computed } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { isTenantEnable } from '@vben/hooks'; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   Button, | ||||||
|  |   DropdownMenu, | ||||||
|  |   DropdownMenuContent, | ||||||
|  |   DropdownMenuGroup, | ||||||
|  |   DropdownMenuItem, | ||||||
|  |   DropdownMenuTrigger, | ||||||
|  | } from '@vben-core/shadcn-ui'; | ||||||
|  | 
 | ||||||
|  | interface Tenant { | ||||||
|  |   id?: number; | ||||||
|  |   name: string; | ||||||
|  |   packageId: number; | ||||||
|  |   contactName: string; | ||||||
|  |   contactMobile: string; | ||||||
|  |   accountCount: number; | ||||||
|  |   expireTime: Date; | ||||||
|  |   website: string; | ||||||
|  |   status: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | defineOptions({ | ||||||
|  |   name: 'TenantDropdown', | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  |   tenantList?: Tenant[]; | ||||||
|  |   visitTenantId?: null | number; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | 
 | ||||||
|  | const tenantEnable = isTenantEnable(); | ||||||
|  | 
 | ||||||
|  | // 租户列表 | ||||||
|  | const tenants = computed(() => props.tenantList ?? []); | ||||||
|  | 
 | ||||||
|  | async function handleChange(id: number | undefined) { | ||||||
|  |   if (!id) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   const tenant = tenants.value.find((item) => item.id === id); | ||||||
|  | 
 | ||||||
|  |   emit('success', tenant); | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <DropdownMenu v-if="tenantEnable"> | ||||||
|  |     <DropdownMenuTrigger as-child> | ||||||
|  |       <Button | ||||||
|  |         variant="outline" | ||||||
|  |         class="hover:bg-accent ml-1 mr-2 h-8 w-24 cursor-pointer rounded-full p-1.5" | ||||||
|  |       > | ||||||
|  |         {{ tenants.find((item) => item.id === visitTenantId)?.name }} | ||||||
|  |       </Button> | ||||||
|  |     </DropdownMenuTrigger> | ||||||
|  |     <DropdownMenuContent class="w-56 p-0 pb-1"> | ||||||
|  |       <DropdownMenuGroup> | ||||||
|  |         <DropdownMenuItem | ||||||
|  |           v-for="tenant in tenants" | ||||||
|  |           :key="tenant.id" | ||||||
|  |           :disabled="tenant.id === visitTenantId" | ||||||
|  |           class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8" | ||||||
|  |           @click="handleChange(tenant.id)" | ||||||
|  |         > | ||||||
|  |           {{ tenant.name }} | ||||||
|  |         </DropdownMenuItem> | ||||||
|  |       </DropdownMenuGroup> | ||||||
|  |     </DropdownMenuContent> | ||||||
|  |   </DropdownMenu> | ||||||
|  | </template> | ||||||
		Loading…
	
		Reference in New Issue
	
	 xingyu4j
						xingyu4j