| | |
| | | <script setup>
|
| | | import { nextTick, ref, inject, onMounted } from 'vue';
|
| | | 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,
|
| | |
| | | ...Icons,
|
| | | };
|
| | |
|
| | |
|
| | |
|
| | | onMounted(() => {
|
| | | let name = router.currentRoute.value.name;
|
| | | let path = router.currentRoute.value.path;
|
| | |
| | | }
|
| | | })
|
| | |
|
| | |
|
| | | 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)
|
| | | })
|
| | | }
|
| | |
|
| | |
| | | selectedKeys.value = [item.key]
|
| | |
|
| | | // open menu
|
| | | let arr = item.key.split("/");
|
| | | let key = '/' + arr[1];
|
| | | openKeys.value = [key]
|
| | | // let arr = item.key.split("/");
|
| | | // let key = '/' + arr[1];
|
| | | // openKeys.value = [key]
|
| | | }
|
| | |
|
| | | const switchLocale = async (locale) => {
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | 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-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>
|
| | | <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>
|
| | | <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()" />
|
| | | </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>
|
| | | <div class="header-top-right">
|
| | | <div class="trigger" style="color: red;" v-if="licenseDays <= 30">
|
| | | 许可证有效期:{{ licenseDays }}天
|
| | | </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="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 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-arrow-right" @click="handleScroll('right')">
|
| | | <CaretRightOutlined />
|
| | | <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>
|
| | | </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>
|
| | | <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-layout>
|
| | |
|
| | | <AiView />
|
| | | </a-flex>
|
| | | <!--<AiView />-->
|
| | | </template>
|
| | |
|
| | | <style scoped></style>
|
| | | <style scoped>
|
| | | .ant-layout-sider ant-layout-sider-dark main-sider {
|
| | | max-height: 100vh;
|
| | | background: #01101E;
|
| | | }
|
| | |
|
| | | </style>
|