|  |  | 
 |  |  | <script setup> | 
 |  |  | import { nextTick, ref, inject, onMounted } from 'vue'; | 
 |  |  | import { useRouter } from "vue-router"; | 
 |  |  | import { get, post, postForm } from '@/utils/request.js' | 
 |  |  | import { logout } from '@/config.js'; | 
 |  |  | import * as Icons from "@ant-design/icons-vue"; | 
 |  |  | import { message } from 'ant-design-vue'; | 
 |  |  | import { | 
 |  |  |   MenuUnfoldOutlined, | 
 |  |  |   MenuFoldOutlined, | 
 |  |  |   HomeOutlined, | 
 |  |  |   CloseOutlined, | 
 |  |  |   RedoOutlined, | 
 |  |  |   UserOutlined, | 
 |  |  |   TranslationOutlined, | 
 |  |  |   ApartmentOutlined, | 
 |  |  |   CaretLeftOutlined, | 
 |  |  |   CaretRightOutlined, | 
 |  |  | } from "@ant-design/icons-vue"; | 
 |  |  | import { formatMessage, loadData } from '@/utils/localeUtils.js'; | 
 |  |  | import AiView from '@/components/ai/index.vue' | 
 |  |  |  | 
 |  |  | const globalState = inject('globalState'); | 
 |  |  | const selectedKeys = ref([]); | 
 |  |  | let openKeys = ref([]); | 
 |  |  | const collapsed = ref(false); | 
 |  |  | const router = useRouter(); | 
 |  |  | let routerCache = ref([]); | 
 |  |  | let routerCacheList = ref([]); | 
 |  |  | let currentCache = ref(null); | 
 |  |  | let isRouterAlive = ref(true); | 
 |  |  | const menuCache = ref([]); | 
 |  |  | const hostList = ref([]); | 
 |  |  | const tabsContent = ref(null); | 
 |  |  |  | 
 |  |  | const components = { | 
 |  |  |   ...Icons, | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | onMounted(() => { | 
 |  |  |   let name = router.currentRoute.value.name; | 
 |  |  |   let path = router.currentRoute.value.path; | 
 |  |  |   if (currentCache.value == null && path != '/') { | 
 |  |  |     get('/api/menu/get/route', { | 
 |  |  |       route: path | 
 |  |  |     }).then((resp) => { | 
 |  |  |       let result = resp.data; | 
 |  |  |       let data = result.data; | 
 |  |  |       if (result.code == 200) { | 
 |  |  |         currentCache.value = name; | 
 |  |  |         routerCache.value.push(name) | 
 |  |  |         routerCacheList.value.push({ | 
 |  |  |           key: path, | 
 |  |  |           languageId: data.languageId, | 
 |  |  |           name: name, | 
 |  |  |         }) | 
 |  |  |       } | 
 |  |  |     }) | 
 |  |  |   } | 
 |  |  | }) | 
 |  |  |  | 
 |  |  | getMenu() | 
 |  |  | function getMenu() { | 
 |  |  |   get('/api/auth/menu', {}).then((result) => { | 
 |  |  |     menuCache.value = result.data.data; | 
 |  |  |   }) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | function menuSelect(item) { | 
 |  |  |   router.push({ | 
 |  |  |     path: item.key | 
 |  |  |   }) | 
 |  |  |  | 
 |  |  |   let name = item.item.name; | 
 |  |  |   currentCache.value = name; | 
 |  |  |  | 
 |  |  |   if (name != undefined && routerCache.value.indexOf(name) == -1) { | 
 |  |  |     routerCache.value.push(item.item.name) | 
 |  |  |     routerCacheList.value.push({ | 
 |  |  |       key: item.key, | 
 |  |  |       languageId: item.item.languageId, | 
 |  |  |       name: item.item.name, | 
 |  |  |     }) | 
 |  |  |   } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | function closeTabs(param) { | 
 |  |  |   let name = param.name; | 
 |  |  |   let tmp = [] | 
 |  |  |   let tmpList = []; | 
 |  |  |   routerCache.value.forEach((item) => { | 
 |  |  |     if (item != name) { | 
 |  |  |       tmp.push(item); | 
 |  |  |     } | 
 |  |  |   }) | 
 |  |  |  | 
 |  |  |   routerCacheList.value.forEach((item) => { | 
 |  |  |     if (item.name != name) { | 
 |  |  |       tmpList.push(item); | 
 |  |  |     } | 
 |  |  |   }) | 
 |  |  |  | 
 |  |  |   if (tmp == 0) { | 
 |  |  |     router.push({ | 
 |  |  |       path: '/' | 
 |  |  |     }) | 
 |  |  |     routerCache.value.push('home') | 
 |  |  |     routerCacheList.value.push({ | 
 |  |  |       key: '/', | 
 |  |  |       languageId: 'common.home', | 
 |  |  |       name: '主页', | 
 |  |  |     }) | 
 |  |  |     selectedKeys.value = ['/'] | 
 |  |  |   } else { | 
 |  |  |     switchTabs(tmpList[0]); | 
 |  |  |   } | 
 |  |  |   routerCache.value = tmp; | 
 |  |  |   routerCacheList.value = tmpList; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | function reloadTabs() { | 
 |  |  |   const hide = message.loading(formatMessage('common.loading', '加载中')); | 
 |  |  |   try { | 
 |  |  |     isRouterAlive.value = false; | 
 |  |  |     nextTick(() => { | 
 |  |  |       isRouterAlive.value = true; | 
 |  |  |       // message.success(formatMessage('common.success', '加载成功')); | 
 |  |  |     }) | 
 |  |  |   } catch (error) { | 
 |  |  |     message.error(formatMessage('common.fail', '加载失败')); | 
 |  |  |   } finally { | 
 |  |  |     hide(); | 
 |  |  |   } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | function closeAllTabs() { | 
 |  |  |   routerCache.value = []; | 
 |  |  |   routerCacheList.value = []; | 
 |  |  |   router.push({ | 
 |  |  |     path: '/' | 
 |  |  |   }) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | function switchTabs(item) { | 
 |  |  |   router.push({ | 
 |  |  |     path: item.key | 
 |  |  |   }) | 
 |  |  |  | 
 |  |  |   currentCache.value = item.name; | 
 |  |  |   selectedKeys.value = [item.key] | 
 |  |  |  | 
 |  |  |   // open menu | 
 |  |  |   let arr = item.key.split("/"); | 
 |  |  |   let key = '/' + arr[1]; | 
 |  |  |   openKeys.value = [key] | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const switchLocale = async (locale) => { | 
 |  |  |   globalState.locale = locale; | 
 |  |  |   localStorage.setItem('locale', locale) | 
 |  |  |   loadData(locale); | 
 |  |  |   reloadTabs() | 
 |  |  | } | 
 |  |  |  | 
 |  |  | getHostList() | 
 |  |  | function getHostList() { | 
 |  |  |   post('/api/show/host.action', {}).then((resp) => { | 
 |  |  |     let result = resp.data; | 
 |  |  |     let data = result.data; | 
 |  |  |     let hostId = data.hostId; | 
 |  |  |     if (data.root) { | 
 |  |  |       post('/api/host/list', {}).then((resp) => { | 
 |  |  |         let result = resp.data; | 
 |  |  |         let data = result.data; | 
 |  |  |         hostList.value = data; | 
 |  |  |         data.forEach((item) => { | 
 |  |  |           if (item.id == hostId) { | 
 |  |  |             globalState.currentHost = item; | 
 |  |  |           } | 
 |  |  |         }) | 
 |  |  |       }) | 
 |  |  |     } | 
 |  |  |   }) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const licenseDays = ref(365); | 
 |  |  | getLicenseDays(); | 
 |  |  | function getLicenseDays() { | 
 |  |  |   post('/api/license/getLicenseDays', {}).then((resp) => { | 
 |  |  |     let result = resp.data; | 
 |  |  |     let data = result.data; | 
 |  |  |     if (result.code == 200) { | 
 |  |  |       licenseDays.value = data; | 
 |  |  |     } | 
 |  |  |   }) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const switchHost = (item) => { | 
 |  |  |   globalState.currentHost = item; | 
 |  |  |   postForm('/api/root/change/host/auth', { | 
 |  |  |     hostId: item.id | 
 |  |  |   }).then((resp) => { | 
 |  |  |     let result = resp.data; | 
 |  |  |     if (result.code == 200) { | 
 |  |  |       window.location.reload(); | 
 |  |  |     } else { | 
 |  |  |       message.error(formatMessage('common.fail', '加载失败')); | 
 |  |  |     } | 
 |  |  |   }) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const windowReload = () => { | 
 |  |  |   window.location.reload(); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const handleScroll = (data) => { | 
 |  |  |   let position = tabsContent.value.scrollLeft; | 
 |  |  |   let offset = position * 0.1 + 30; | 
 |  |  |   if (data == 'left') { | 
 |  |  |     tabsContent.value.scrollLeft = position - offset; | 
 |  |  |   } else { | 
 |  |  |     tabsContent.value.scrollLeft = position + offset; | 
 |  |  |   } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | </script> | 
 |  |  |  | 
 |  |  | <template> | 
 |  |  |   <a-layout class="main"> | 
 |  |  |     <a-layout-sider class="main-sider" v-model:collapsed="collapsed" :trigger="null" collapsible theme="dark"> | 
 |  |  |       <div class="logo" /> | 
 |  |  |       <a-menu v-model:openKeys="openKeys" v-model:selectedKeys="selectedKeys" @select="menuSelect" theme="dark" | 
 |  |  |         mode="inline"> | 
 |  |  |         <div> | 
 |  |  |           <a-menu-item key="/" name="主页"> | 
 |  |  |             <HomeOutlined /> {{ formatMessage('common.home', '主页') }} | 
 |  |  |           </a-menu-item> | 
 |  |  |         </div> | 
 |  |  |  | 
 |  |  |         <div v-for="(item, index) in menuCache" :key="index"> | 
 |  |  |           <a-sub-menu :key="item.route" v-if="item.type == 0"> | 
 |  |  |             <template #title> | 
 |  |  |               <span> | 
 |  |  |                 <component :is="components[ref(item.icon).value]" /> | 
 |  |  |                 {{ formatMessage(item.languageId, item.name) }} | 
 |  |  |               </span> | 
 |  |  |             </template> | 
 |  |  |             <div v-for="(child, idx) in item.children"> | 
 |  |  |               <a-menu-item v-if="child.status == 1" :key="child.route" :name="child.name" | 
 |  |  |                 :languageId="child.languageId"> | 
 |  |  |                 {{ formatMessage(child.languageId, child.name) }} | 
 |  |  |               </a-menu-item> | 
 |  |  |             </div> | 
 |  |  |           </a-sub-menu> | 
 |  |  |         </div> | 
 |  |  |       </a-menu> | 
 |  |  |  | 
 |  |  |     </a-layout-sider> | 
 |  |  |     <a-layout> | 
 |  |  |       <a-layout-header style="background: #fff; padding: 0;"> | 
 |  |  |         <div class="header-top"> | 
 |  |  |           <div class="header-top-left"> | 
 |  |  |             <MenuUnfoldOutlined v-if="collapsed" class="trigger triggerLarge" @click="() => (collapsed = !collapsed)" /> | 
 |  |  |             <MenuFoldOutlined v-else class="trigger" @click="() => (collapsed = !collapsed)" /> | 
 |  |  |             <RedoOutlined class="trigger" @click="windowReload()" /> | 
 |  |  |           </div> | 
 |  |  |           <div class="header-top-right"> | 
 |  |  |             <div class="trigger" style="color: red;" v-if="licenseDays <= 30"> | 
 |  |  |               许可证有效期:{{ licenseDays }}天 | 
 |  |  |             </div> | 
 |  |  |             <div class="trigger" v-if="globalState.currentHost"> | 
 |  |  |               <a-dropdown> | 
 |  |  |                 <div> | 
 |  |  |                   <ApartmentOutlined /> | 
 |  |  |                   {{ globalState.currentHost?.name }} | 
 |  |  |                 </div> | 
 |  |  |                 <template #overlay> | 
 |  |  |                   <a-menu> | 
 |  |  |                     <a-menu-item v-for="(item, index) in hostList" :key="index" @click="switchHost(item)" | 
 |  |  |                       :class="globalState.currentHost?.id == item.id ? 'active' : ''">{{ item.name }}</a-menu-item> | 
 |  |  |                   </a-menu> | 
 |  |  |                 </template> | 
 |  |  |               </a-dropdown> | 
 |  |  |             </div> | 
 |  |  |             <div class="trigger"> | 
 |  |  |               <a-dropdown> | 
 |  |  |                 <div> | 
 |  |  |                   <TranslationOutlined /> | 
 |  |  |                   {{ globalState.localeList[globalState.locale]?.desc }} | 
 |  |  |                 </div> | 
 |  |  |                 <template #overlay> | 
 |  |  |                   <a-menu> | 
 |  |  |                     <div v-for="(item, key) in globalState.localeList" :key="key"> | 
 |  |  |                       <a-menu-item @click="switchLocale(key)" :class="globalState.locale == key ? 'active' : ''">{{ | 
 |  |  |                         item.desc }}</a-menu-item> | 
 |  |  |                     </div> | 
 |  |  |                   </a-menu> | 
 |  |  |                 </template> | 
 |  |  |               </a-dropdown> | 
 |  |  |             </div> | 
 |  |  |             <div> | 
 |  |  |               <a-dropdown> | 
 |  |  |                 <a class="header-user" @click.prevent> | 
 |  |  |                   <UserOutlined /> | 
 |  |  |                   <span>{{ globalState.user.username }}</span> | 
 |  |  |                 </a> | 
 |  |  |                 <template #overlay> | 
 |  |  |                   <a-menu @click="logout"> | 
 |  |  |                     <a-menu-item key="logout">{{ formatMessage('common.account.logout', '退出') }}</a-menu-item> | 
 |  |  |                   </a-menu> | 
 |  |  |                 </template> | 
 |  |  |               </a-dropdown> | 
 |  |  |             </div> | 
 |  |  |           </div> | 
 |  |  |         </div> | 
 |  |  |       </a-layout-header> | 
 |  |  |       <a-layout-content class="content-view"> | 
 |  |  |         <div class="tabs-fixed"> | 
 |  |  |           <div class="tabs-arrow-left" @click="handleScroll('left')"> | 
 |  |  |             <CaretLeftOutlined /> | 
 |  |  |           </div> | 
 |  |  |  | 
 |  |  |           <div class="tabs-content" ref="tabsContent"> | 
 |  |  |             <div class="tabs-content-item"> | 
 |  |  |               <div v-for="(item, index) in routerCacheList" :key="index" @click="switchTabs(item)" class="tabs-item" | 
 |  |  |                 :class="currentCache == item.name ? 'tabs-item-active' : ''"> | 
 |  |  |                 <div :class="currentCache == item.name ? '' : 'tabs-item-reload-none'" @click="reloadTabs" @click.stop> | 
 |  |  |                   <RedoOutlined /> | 
 |  |  |                 </div> | 
 |  |  |                 <div>{{ formatMessage(item.languageId, item.name) }}</div> | 
 |  |  |                 <div @click="closeTabs(item)" @click.stop> | 
 |  |  |                   <CloseOutlined /> | 
 |  |  |                 </div> | 
 |  |  |               </div> | 
 |  |  |             </div> | 
 |  |  |           </div> | 
 |  |  |  | 
 |  |  |           <div class="tabs-arrow-right" @click="handleScroll('right')"> | 
 |  |  |             <CaretRightOutlined /> | 
 |  |  |           </div> | 
 |  |  |         </div> | 
 |  |  |  | 
 |  |  |         <router-view v-slot="{ Component, route }" v-if="isRouterAlive"> | 
 |  |  |           <keep-alive :include="routerCache"> | 
 |  |  |             <component :is="Component" @pageReload="reloadTabs" /> | 
 |  |  |           </keep-alive> | 
 |  |  |         </router-view> | 
 |  |  |       </a-layout-content> | 
 |  |  |     </a-layout> | 
 |  |  |   </a-layout> | 
 |  |  |  | 
 |  |  |   <AiView /> | 
 |  |  | </template> | 
 |  |  |  | 
 |  |  | <style scoped></style> | 
 |  |  | <script setup>
 | 
 |  |  | import { nextTick, ref, inject, onMounted, h, reactive } from 'vue';
 | 
 |  |  | import { useRouter } from "vue-router";
 | 
 |  |  | import { get, post, postForm } from '@/utils/request.js'
 | 
 |  |  | import { logout } from '@/config.js';
 | 
 |  |  | import * as Icons from "@ant-design/icons-vue";
 | 
 |  |  | import { message } from 'ant-design-vue';
 | 
 |  |  | // import type { MenuMode, MenuTheme } from 'ant-design-vue';
 | 
 |  |  | // import { ItemType } from 'ant-design-vue';
 | 
 |  |  | 
 | 
 |  |  | import {
 | 
 |  |  |   MenuUnfoldOutlined,
 | 
 |  |  |   MenuFoldOutlined,
 | 
 |  |  |   HomeOutlined,
 | 
 |  |  |   CloseOutlined,
 | 
 |  |  |   RedoOutlined,
 | 
 |  |  |   UserOutlined,
 | 
 |  |  |   TranslationOutlined,
 | 
 |  |  |   ApartmentOutlined,
 | 
 |  |  |   CaretLeftOutlined,
 | 
 |  |  |   CaretRightOutlined,
 | 
 |  |  | } from "@ant-design/icons-vue";
 | 
 |  |  | import { formatMessage, loadData } from '@/utils/localeUtils.js';
 | 
 |  |  | import AiView from '@/components/ai/index.vue'
 | 
 |  |  | 
 | 
 |  |  | const globalState = inject('globalState');
 | 
 |  |  | const selectedKeys = ref([]);
 | 
 |  |  | let openKeys = ref([]);
 | 
 |  |  | const collapsed = ref(false);
 | 
 |  |  | const router = useRouter();
 | 
 |  |  | let routerCache = ref([]);
 | 
 |  |  | let routerCacheList = ref([]);
 | 
 |  |  | let currentCache = ref(null);
 | 
 |  |  | let isRouterAlive = ref(true);
 | 
 |  |  | const menuCache = ref([]);
 | 
 |  |  | const hostList = ref([]);
 | 
 |  |  | const tabsContent = ref(null);
 | 
 |  |  | 
 | 
 |  |  | const components = {
 | 
 |  |  |   ...Icons,
 | 
 |  |  | };
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | onMounted(() => {
 | 
 |  |  |   let name = router.currentRoute.value.name;
 | 
 |  |  |   let path = router.currentRoute.value.path;
 | 
 |  |  |   if (currentCache.value == null && path != '/') {
 | 
 |  |  |     get('/api/menu/get/route', {
 | 
 |  |  |       route: path
 | 
 |  |  |     }).then((resp) => {
 | 
 |  |  |       let result = resp.data;
 | 
 |  |  |       let data = result.data;
 | 
 |  |  |       if (result.code == 200) {
 | 
 |  |  |         currentCache.value = name;
 | 
 |  |  |         routerCache.value.push(name)
 | 
 |  |  |         routerCacheList.value.push({
 | 
 |  |  |           key: path,
 | 
 |  |  |           languageId: data.languageId,
 | 
 |  |  |           name: name,
 | 
 |  |  |         })
 | 
 |  |  |       }
 | 
 |  |  |     })
 | 
 |  |  |   }
 | 
 |  |  | })
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | const state = reactive({
 | 
 |  |  |     rootSubmenuKeys: [],
 | 
 |  |  |     openKeys: ["/"],
 | 
 |  |  |     selectedKeys: [],
 | 
 |  |  | });
 | 
 |  |  | 
 | 
 |  |  | getMenu()
 | 
 |  |  | function getMenu() {
 | 
 |  |  |   get('/api/auth/menu', {}).then((result) => {
 | 
 |  |  |     menuCache.value = result.data.data;
 | 
 |  |  |     let routes = menuCache.value.map(item =>{
 | 
 |  |  |         return item.route
 | 
 |  |  |     })
 | 
 |  |  |       routes.unshift('/')
 | 
 |  |  |       state.rootSubmenuKeys = routes
 | 
 |  |  |       console.log(state.rootSubmenuKeys)
 | 
 |  |  |   })
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | function menuSelect(item) {
 | 
 |  |  |   router.push({
 | 
 |  |  |     path: item.key
 | 
 |  |  |   })
 | 
 |  |  | 
 | 
 |  |  |   let name = item.item.name;
 | 
 |  |  |   currentCache.value = name;
 | 
 |  |  | 
 | 
 |  |  |   if (name != undefined && routerCache.value.indexOf(name) == -1) {
 | 
 |  |  |     routerCache.value.push(item.item.name)
 | 
 |  |  |     routerCacheList.value.push({
 | 
 |  |  |       key: item.key,
 | 
 |  |  |       languageId: item.item.languageId,
 | 
 |  |  |       name: item.item.name,
 | 
 |  |  |     })
 | 
 |  |  |   }
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | function closeTabs(param) {
 | 
 |  |  |   let name = param.name;
 | 
 |  |  |   let tmp = []
 | 
 |  |  |   let tmpList = [];
 | 
 |  |  |   routerCache.value.forEach((item) => {
 | 
 |  |  |     if (item != name) {
 | 
 |  |  |       tmp.push(item);
 | 
 |  |  |     }
 | 
 |  |  |   })
 | 
 |  |  | 
 | 
 |  |  |   routerCacheList.value.forEach((item) => {
 | 
 |  |  |     if (item.name != name) {
 | 
 |  |  |       tmpList.push(item);
 | 
 |  |  |     }
 | 
 |  |  |   })
 | 
 |  |  | 
 | 
 |  |  |   if (tmp == 0) {
 | 
 |  |  |     router.push({
 | 
 |  |  |       path: '/'
 | 
 |  |  |     })
 | 
 |  |  |     routerCache.value.push('home')
 | 
 |  |  |     routerCacheList.value.push({
 | 
 |  |  |       key: '/',
 | 
 |  |  |       languageId: 'common.home',
 | 
 |  |  |       name: '主页',
 | 
 |  |  |     })
 | 
 |  |  |     selectedKeys.value = ['/']
 | 
 |  |  |   } else {
 | 
 |  |  |     switchTabs(tmpList[0]);
 | 
 |  |  |   }
 | 
 |  |  |   routerCache.value = tmp;
 | 
 |  |  |   routerCacheList.value = tmpList;
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | function reloadTabs() {
 | 
 |  |  |   const hide = message.loading(formatMessage('common.loading', '加载中'));
 | 
 |  |  |   try {
 | 
 |  |  |     isRouterAlive.value = false;
 | 
 |  |  |     nextTick(() => {
 | 
 |  |  |       isRouterAlive.value = true;
 | 
 |  |  |       // message.success(formatMessage('common.success', '加载成功'));
 | 
 |  |  |     })
 | 
 |  |  |   } catch (error) {
 | 
 |  |  |     message.error(formatMessage('common.fail', '加载失败'));
 | 
 |  |  |   } finally {
 | 
 |  |  |     hide();
 | 
 |  |  |   }
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | function closeAllTabs() {
 | 
 |  |  |   routerCache.value = [];
 | 
 |  |  |   routerCacheList.value = [];
 | 
 |  |  |   router.push({
 | 
 |  |  |     path: '/'
 | 
 |  |  |   })
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | function switchTabs(item) {
 | 
 |  |  |   router.push({
 | 
 |  |  |     path: item.key
 | 
 |  |  |   })
 | 
 |  |  | 
 | 
 |  |  |   currentCache.value = item.name;
 | 
 |  |  |   selectedKeys.value = [item.key]
 | 
 |  |  | 
 | 
 |  |  |   // open menu
 | 
 |  |  |   // let arr = item.key.split("/");
 | 
 |  |  |   // let key = '/' + arr[1];
 | 
 |  |  |   // openKeys.value = [key]
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | const switchLocale = async (locale) => {
 | 
 |  |  |   globalState.locale = locale;
 | 
 |  |  |   localStorage.setItem('locale', locale)
 | 
 |  |  |   loadData(locale);
 | 
 |  |  |   reloadTabs()
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | getHostList()
 | 
 |  |  | function getHostList() {
 | 
 |  |  |   post('/api/show/host.action', {}).then((resp) => {
 | 
 |  |  |     let result = resp.data;
 | 
 |  |  |     let data = result.data;
 | 
 |  |  |     let hostId = data.hostId;
 | 
 |  |  |     if (data.root) {
 | 
 |  |  |       post('/api/host/list', {}).then((resp) => {
 | 
 |  |  |         let result = resp.data;
 | 
 |  |  |         let data = result.data;
 | 
 |  |  |         hostList.value = data;
 | 
 |  |  |         data.forEach((item) => {
 | 
 |  |  |           if (item.id == hostId) {
 | 
 |  |  |             globalState.currentHost = item;
 | 
 |  |  |           }
 | 
 |  |  |         })
 | 
 |  |  |       })
 | 
 |  |  |     }
 | 
 |  |  |   })
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | const licenseDays = ref(365);
 | 
 |  |  | getLicenseDays();
 | 
 |  |  | function getLicenseDays() {
 | 
 |  |  |   post('/api/license/getLicenseDays', {}).then((resp) => {
 | 
 |  |  |     let result = resp.data;
 | 
 |  |  |     let data = result.data;
 | 
 |  |  |     if (result.code == 200) {
 | 
 |  |  |       licenseDays.value = data;
 | 
 |  |  |     }
 | 
 |  |  |   })
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | const switchHost = (item) => {
 | 
 |  |  |   globalState.currentHost = item;
 | 
 |  |  |   postForm('/api/root/change/host/auth', {
 | 
 |  |  |     hostId: item.id
 | 
 |  |  |   }).then((resp) => {
 | 
 |  |  |     let result = resp.data;
 | 
 |  |  |     if (result.code == 200) {
 | 
 |  |  |       window.location.reload();
 | 
 |  |  |     } else {
 | 
 |  |  |       message.error(formatMessage('common.fail', '加载失败'));
 | 
 |  |  |     }
 | 
 |  |  |   })
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | const windowReload = () => {
 | 
 |  |  |   window.location.reload();
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | const handleScroll = (data) => {
 | 
 |  |  |   let position = tabsContent.value.scrollLeft;
 | 
 |  |  |   let offset = position * 0.1 + 30;
 | 
 |  |  |   if (data == 'left') {
 | 
 |  |  |     tabsContent.value.scrollLeft = position - offset;
 | 
 |  |  |   } else {
 | 
 |  |  |     tabsContent.value.scrollLeft = position + offset;
 | 
 |  |  |   }
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | const onOpenChange = (openKeys) => {
 | 
 |  |  |     const latestOpenKey = openKeys.find(
 | 
 |  |  |         (key) => state.openKeys.indexOf(key) === -1
 | 
 |  |  |     );
 | 
 |  |  |     if (state.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
 | 
 |  |  |         state.openKeys = openKeys
 | 
 |  |  |     } else {
 | 
 |  |  |         state.openKeys = latestOpenKey ? [latestOpenKey] : [];
 | 
 |  |  |     }
 | 
 |  |  |     console.log(state.openKeys)
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | </script>
 | 
 |  |  | 
 | 
 |  |  | <template>
 | 
 |  |  |   <a-flex gap="middle" horizontal>
 | 
 |  |  |       <div class="sider-style">
 | 
 |  |  |         <a-layout-sider class="main-sider" v-model:collapsed="collapsed" :trigger="null"  theme="dark">
 | 
 |  |  |         <div class="logo" />
 | 
 |  |  |         <a-menu v-model:openKeys="state.openKeys" v-model:selectedKeys="state.selectedKeys"  @select="menuSelect"  theme="dark"
 | 
 |  |  |                 mode="inline" @openChange="onOpenChange">
 | 
 |  |  |           <div>
 | 
 |  |  |             <a-menu-item key="/" name="主页">
 | 
 |  |  |               <HomeOutlined /> {{ formatMessage('common.home', '主页') }}
 | 
 |  |  |             </a-menu-item>
 | 
 |  |  |           </div>
 | 
 |  |  | 
 | 
 |  |  |           <div v-for="(item, index) in menuCache" :key="index">
 | 
 |  |  |             <a-sub-menu :key="item.route" v-if="item.type == 0">
 | 
 |  |  |               <template #title>
 | 
 |  |  |               <span>
 | 
 |  |  |                 <component :is="components[ref(item.icon).value]" />
 | 
 |  |  |                 {{ formatMessage(item.languageId, item.name) }}
 | 
 |  |  |               </span>
 | 
 |  |  |               </template>
 | 
 |  |  |               <div v-for="(child, idx) in item.children">
 | 
 |  |  |                 <a-menu-item v-if="child.status == 1" :key="child.route" :name="child.name"
 | 
 |  |  |                              :languageId="child.languageId">
 | 
 |  |  |                   {{ formatMessage(child.languageId, child.name) }}
 | 
 |  |  |                 </a-menu-item>
 | 
 |  |  |               </div>
 | 
 |  |  |             </a-sub-menu>
 | 
 |  |  |           </div>
 | 
 |  |  |         </a-menu>
 | 
 |  |  |       </a-layout-sider>
 | 
 |  |  |       </div>
 | 
 |  |  |     <a-layout class="main">
 | 
 |  |  |       <a-layout>
 | 
 |  |  |         <a-layout-header style="background: #fff; padding: 0;">
 | 
 |  |  |           <div class="header-top">
 | 
 |  |  |             <div class="header-top-left">
 | 
 |  |  |               <MenuUnfoldOutlined v-if="collapsed" class="trigger triggerLarge" @click="() => (collapsed = !collapsed)" />
 | 
 |  |  |               <MenuFoldOutlined v-else class="trigger" @click="() => (collapsed = !collapsed)" />
 | 
 |  |  |               <RedoOutlined class="trigger" @click="windowReload()" />
 | 
 |  |  |             </div>
 | 
 |  |  |             <div class="header-top-right">
 | 
 |  |  |               <div class="trigger" style="color: red;" v-if="licenseDays <= 30">
 | 
 |  |  |                 许可证有效期:{{ licenseDays }}天
 | 
 |  |  |               </div>
 | 
 |  |  |               <div class="trigger" v-if="globalState.currentHost">
 | 
 |  |  |                 <a-dropdown>
 | 
 |  |  |                   <div>
 | 
 |  |  |                     <ApartmentOutlined />
 | 
 |  |  |                     {{ globalState.currentHost?.name }}
 | 
 |  |  |                   </div>
 | 
 |  |  |                   <template #overlay>
 | 
 |  |  |                     <a-menu>
 | 
 |  |  |                       <a-menu-item v-for="(item, index) in hostList" :key="index" @click="switchHost(item)"
 | 
 |  |  |                                    :class="globalState.currentHost?.id == item.id ? 'active' : ''">{{ item.name }}</a-menu-item>
 | 
 |  |  |                     </a-menu>
 | 
 |  |  |                   </template>
 | 
 |  |  |                 </a-dropdown>
 | 
 |  |  |               </div>
 | 
 |  |  |               <div class="trigger">
 | 
 |  |  |                 <a-dropdown>
 | 
 |  |  |                   <div>
 | 
 |  |  |                     <TranslationOutlined />
 | 
 |  |  |                     {{ globalState.localeList[globalState.locale]?.desc }}
 | 
 |  |  |                   </div>
 | 
 |  |  |                   <template #overlay>
 | 
 |  |  |                     <a-menu>
 | 
 |  |  |                       <div v-for="(item, key) in globalState.localeList" :key="key">
 | 
 |  |  |                         <a-menu-item @click="switchLocale(key)" :class="globalState.locale == key ? 'active' : ''">{{
 | 
 |  |  |                           item.desc }}</a-menu-item>
 | 
 |  |  |                       </div>
 | 
 |  |  |                     </a-menu>
 | 
 |  |  |                   </template>
 | 
 |  |  |                 </a-dropdown>
 | 
 |  |  |               </div>
 | 
 |  |  |               <div>
 | 
 |  |  |                 <a-dropdown>
 | 
 |  |  |                   <a class="header-user" @click.prevent>
 | 
 |  |  |                     <UserOutlined />
 | 
 |  |  |                     <span>{{ globalState.user.username }}</span>
 | 
 |  |  |                   </a>
 | 
 |  |  |                   <template #overlay>
 | 
 |  |  |                     <a-menu @click="logout">
 | 
 |  |  |                       <a-menu-item key="logout">{{ formatMessage('common.account.logout', '退出') }}</a-menu-item>
 | 
 |  |  |                     </a-menu>
 | 
 |  |  |                   </template>
 | 
 |  |  |                 </a-dropdown>
 | 
 |  |  |               </div>
 | 
 |  |  |             </div>
 | 
 |  |  |           </div>
 | 
 |  |  |         </a-layout-header>
 | 
 |  |  |         <a-layout-content class="content-view">
 | 
 |  |  |           <div class="tabs-fixed">
 | 
 |  |  |             <div class="tabs-arrow-left" @click="handleScroll('left')">
 | 
 |  |  |               <CaretLeftOutlined />
 | 
 |  |  |             </div>
 | 
 |  |  | 
 | 
 |  |  |             <div class="tabs-content" ref="tabsContent">
 | 
 |  |  |               <div class="tabs-content-item">
 | 
 |  |  |                 <div v-for="(item, index) in routerCacheList" :key="index" @click="switchTabs(item)" class="tabs-item"
 | 
 |  |  |                      :class="currentCache == item.name ? 'tabs-item-active' : ''">
 | 
 |  |  |                   <div :class="currentCache == item.name ? '' : 'tabs-item-reload-none'" @click="reloadTabs" @click.stop>
 | 
 |  |  |                     <RedoOutlined />
 | 
 |  |  |                   </div>
 | 
 |  |  |                   <div>{{ formatMessage(item.languageId, item.name) }}</div>
 | 
 |  |  |                   <div @click="closeTabs(item)" @click.stop>
 | 
 |  |  |                     <CloseOutlined />
 | 
 |  |  |                   </div>
 | 
 |  |  |                 </div>
 | 
 |  |  |               </div>
 | 
 |  |  |             </div>
 | 
 |  |  | 
 | 
 |  |  |             <div class="tabs-arrow-right" @click="handleScroll('right')">
 | 
 |  |  |               <CaretRightOutlined />
 | 
 |  |  |             </div>
 | 
 |  |  |           </div>
 | 
 |  |  | 
 | 
 |  |  |           <router-view v-slot="{ Component, route }" v-if="isRouterAlive">
 | 
 |  |  |             <keep-alive :include="routerCache">
 | 
 |  |  |               <component :is="Component" @pageReload="reloadTabs" />
 | 
 |  |  |             </keep-alive>
 | 
 |  |  |           </router-view>
 | 
 |  |  |         </a-layout-content>
 | 
 |  |  |       </a-layout>
 | 
 |  |  |     </a-layout>
 | 
 |  |  |   </a-flex>
 | 
 |  |  |   <!--<AiView />-->
 | 
 |  |  | </template>
 | 
 |  |  | 
 | 
 |  |  | <style scoped>
 | 
 |  |  |   .ant-layout-sider ant-layout-sider-dark main-sider {
 | 
 |  |  |      max-height: 100vh;
 | 
 |  |  |     background: #01101E;
 | 
 |  |  |   }
 | 
 |  |  | 
 | 
 |  |  | </style>
 |