| | |
| | | color: rgba(255, 255, 255, 0.58); |
| | | } |
| | | |
| | | .aside-loading { |
| | | padding: 4px 12px 12px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .aside-skeleton-item { |
| | | position: relative; |
| | | height: 46px; |
| | | margin: 0 10px 6px; |
| | | overflow: hidden; |
| | | border-radius: 10px; |
| | | background: rgba(255, 255, 255, 0.08); |
| | | } |
| | | |
| | | .aside-skeleton-item::after { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | transform: translateX(-100%); |
| | | background: linear-gradient(90deg, |
| | | rgba(255, 255, 255, 0) 0%, |
| | | rgba(255, 255, 255, 0.16) 48%, |
| | | rgba(255, 255, 255, 0) 100%); |
| | | animation: asideSkeletonShimmer 1.25s ease-in-out infinite; |
| | | } |
| | | |
| | | .aside-skeleton-item:nth-child(3n) { |
| | | width: calc(100% - 28px); |
| | | } |
| | | |
| | | .aside-skeleton-item:nth-child(4n + 2) { |
| | | width: calc(100% - 44px); |
| | | } |
| | | |
| | | .aside-skeleton-item:nth-child(5n) { |
| | | width: calc(100% - 36px); |
| | | } |
| | | |
| | | .aside-footer { |
| | | padding: 14px 16px 16px; |
| | | border-top: 1px solid rgba(255, 255, 255, 0.08); |
| | |
| | | } |
| | | } |
| | | |
| | | @keyframes asideSkeletonShimmer { |
| | | 100% { |
| | | transform: translateX(100%); |
| | | } |
| | | } |
| | | |
| | | .ai-drawer-layer { |
| | | box-shadow: -8px 0 24px rgba(0, 0, 0, 0.15) !important; |
| | | border-radius: 8px 0 0 8px !important; |
| | |
| | | </el-input> |
| | | </div> |
| | | |
| | | <el-scrollbar class="aside-scroll" v-loading="menuLoading"> |
| | | <el-menu |
| | | ref="sideMenu" |
| | | class="side-menu" |
| | | :default-active="activeMenuKey" |
| | | :collapse="isCollapse" |
| | | :collapse-transition="false" |
| | | :default-openeds="defaultOpeneds" |
| | | unique-opened |
| | | background-color="transparent" |
| | | text-color="#c6d1df" |
| | | active-text-color="#ffffff"> |
| | | <el-submenu |
| | | v-for="group in filteredMenus" |
| | | :key="'group-' + group.menuId" |
| | | :index="'group-' + group.menuId"> |
| | | <template slot="title"> |
| | | <i :class="resolveMenuIcon(group.menuCode)"></i> |
| | | <span>{{ group.menu }}</span> |
| | | </template> |
| | | <el-menu-item |
| | | v-for="item in group.subMenu" |
| | | :key="item.tabKey" |
| | | :index="item.tabKey" |
| | | @click="handleMenuSelect(group, item)"> |
| | | {{ item.name }} |
| | | </el-menu-item> |
| | | </el-submenu> |
| | | </el-menu> |
| | | |
| | | <div class="aside-empty" v-if="!menuLoading && filteredMenus.length === 0"> |
| | | <el-empty |
| | | :image-size="80" |
| | | :description="menuKeyword ? '没有匹配菜单' : '当前账号没有可用菜单'"> |
| | | </el-empty> |
| | | <el-scrollbar class="aside-scroll"> |
| | | <div v-if="menuLoading" class="aside-loading" aria-hidden="true"> |
| | | <div |
| | | v-for="n in 8" |
| | | :key="'menu-skeleton-' + n" |
| | | class="aside-skeleton-item"> |
| | | </div> |
| | | </div> |
| | | |
| | | <template v-else> |
| | | <el-menu |
| | | ref="sideMenu" |
| | | class="side-menu" |
| | | :default-active="activeMenuKey" |
| | | :collapse="isCollapse" |
| | | :collapse-transition="false" |
| | | :default-openeds="defaultOpeneds" |
| | | unique-opened |
| | | background-color="transparent" |
| | | text-color="#c6d1df" |
| | | active-text-color="#ffffff"> |
| | | <el-submenu |
| | | v-for="group in filteredMenus" |
| | | :key="'group-' + group.menuId" |
| | | :index="'group-' + group.menuId"> |
| | | <template slot="title"> |
| | | <i :class="resolveMenuIcon(group.menuCode)"></i> |
| | | <span>{{ group.menu }}</span> |
| | | </template> |
| | | <el-menu-item |
| | | v-for="item in group.subMenu" |
| | | :key="item.tabKey" |
| | | :index="item.tabKey" |
| | | @click="handleMenuSelect(group, item)"> |
| | | {{ item.name }} |
| | | </el-menu-item> |
| | | </el-submenu> |
| | | </el-menu> |
| | | |
| | | <div class="aside-empty" v-if="filteredMenus.length === 0"> |
| | | <el-empty |
| | | :image-size="80" |
| | | :description="menuKeyword ? '没有匹配菜单' : '当前账号没有可用菜单'"> |
| | | </el-empty> |
| | | </div> |
| | | </template> |
| | | </el-scrollbar> |
| | | |
| | | <div class="aside-footer" v-show="!isCollapse"> |
| | |
| | | </div> |
| | | |
| | | <div class="header-right"> |
| | | <el-tag v-if="licenseVisible" size="mini" type="warning" effect="dark"> |
| | | 许可证 {{ licenseDays }} 天游效 |
| | | <el-tag |
| | | v-if="licenseDisplayVisible" |
| | | size="mini" |
| | | :type="licenseTagType" |
| | | effect="dark"> |
| | | 临时许可证有效期:{{ licenseDays }}天 |
| | | </el-tag> |
| | | <el-tag |
| | | v-if="fakeVisible" |
| | |
| | | class="page-tabs" |
| | | v-model="activeTab" |
| | | type="card" |
| | | @tab-click="handleTabClick" |
| | | @tab-remove="removeTab"> |
| | | <el-tab-pane |
| | | v-for="tab in tabs" |
| | |
| | | data: function () { |
| | | return { |
| | | isCollapse: false, |
| | | menuLoading: false, |
| | | menuLoading: true, |
| | | pageLoading: true, |
| | | loadingText: "正在加载页面...", |
| | | menuKeyword: "", |
| | |
| | | fakeVisible: false, |
| | | fakeRunning: false, |
| | | fakeStatusInterval: null, |
| | | menuSyncVersion: 0, |
| | | menuSyncTimer: null, |
| | | userName: localStorage.getItem(USER_STORAGE_KEY) || "管理员", |
| | | aiLayerIndex: null, |
| | | aiTipIndex: null |
| | |
| | | licenseVisible: function () { |
| | | return this.licenseDays !== null && this.licenseDays <= 30; |
| | | }, |
| | | licenseDisplayVisible: function () { |
| | | return this.licenseVisible && this.licenseDays >= 0; |
| | | }, |
| | | licenseTagType: function () { |
| | | if (this.licenseDays !== null && this.licenseDays <= 15) { |
| | | return "danger"; |
| | | } |
| | | if (this.licenseDays !== null && this.licenseDays <= 30) { |
| | | return "warning"; |
| | | } |
| | | return "info"; |
| | | }, |
| | | userShortName: function () { |
| | | return (this.userName || "管理员").substring(0, 1); |
| | | } |
| | |
| | | watch: { |
| | | activeTab: function () { |
| | | var tab = this.getTabByName(this.activeTab); |
| | | this.activeMenuKey = tab ? (tab.menuKey || this.findMenuKeyByUrl(tab.url)) : ""; |
| | | this.syncMenuStateByUrl(tab ? tab.url : HOME_TAB_CONFIG.url); |
| | | this.pageLoading = !!(tab && !tab.loaded); |
| | | if (this.pageLoading) { |
| | | this.loadingText = "正在加载 “" + tab.title + "” ..."; |
| | |
| | | if (this.userSyncTimer) { |
| | | clearInterval(this.userSyncTimer); |
| | | this.userSyncTimer = null; |
| | | } |
| | | if (this.menuSyncTimer) { |
| | | clearTimeout(this.menuSyncTimer); |
| | | this.menuSyncTimer = null; |
| | | } |
| | | if (this.aiTipIndex) { |
| | | layer.close(this.aiTipIndex); |
| | |
| | | hasTab: function (name) { |
| | | return !!this.getTabByName(name); |
| | | }, |
| | | isHomeTabUrl: function (url) { |
| | | return this.resolveViewSrc(url || HOME_TAB_CONFIG.url) === this.resolveViewSrc(HOME_TAB_CONFIG.url); |
| | | }, |
| | | getTabByName: function (name) { |
| | | var i; |
| | | for (i = 0; i < this.tabs.length; i++) { |
| | |
| | | this.loadingText = "正在加载 “" + tab.title + "” ..."; |
| | | this.pageLoading = !tab.loaded; |
| | | this.activeTab = tab.name; |
| | | this.activeMenuKey = tab.menuKey || this.findMenuKeyByUrl(tab.url); |
| | | this.syncMenuStateByUrl(tab.url); |
| | | }, |
| | | openHomeTab: function () { |
| | | this.addOrActivateTab(HOME_TAB_CONFIG); |
| | |
| | | closeAllTabs: function () { |
| | | this.tabs = [this.createTab(HOME_TAB_CONFIG)]; |
| | | this.activeTab = HOME_TAB_CONFIG.url; |
| | | this.activeMenuKey = ""; |
| | | this.syncMenuStateByUrl(HOME_TAB_CONFIG.url); |
| | | this.pageLoading = true; |
| | | this.loadingText = "正在加载 “控制中心” ..."; |
| | | this.persistTabs(); |
| | |
| | | |
| | | this.activeTab = nextTabName; |
| | | this.persistTabs(); |
| | | }, |
| | | handleTabClick: function () { |
| | | this.activeMenuKey = this.findMenuKeyByUrl(this.activeTabUrl); |
| | | }, |
| | | handleFrameLoad: function (name) { |
| | | var tab = this.getTabByName(name); |
| | |
| | | item = group.subMenu[j]; |
| | | if (item.url === normalized) { |
| | | return item.tabKey; |
| | | } |
| | | } |
| | | } |
| | | return ""; |
| | | }, |
| | | findMenuGroupIndexByUrl: function (url) { |
| | | var normalized = this.resolveViewSrc(url); |
| | | var i; |
| | | var j; |
| | | var group; |
| | | var item; |
| | | |
| | | for (i = 0; i < this.menus.length; i++) { |
| | | group = this.menus[i]; |
| | | for (j = 0; j < group.subMenu.length; j++) { |
| | | item = group.subMenu[j]; |
| | | if (item.url === normalized) { |
| | | return "group-" + group.menuId; |
| | | } |
| | | } |
| | | } |
| | |
| | | menu.close(openedMenus[i]); |
| | | } |
| | | }, |
| | | syncMenuStateByUrl: function (url) { |
| | | var targetUrl = this.resolveViewSrc(url || HOME_TAB_CONFIG.url); |
| | | var activeMenuKey = ""; |
| | | var groupIndex = ""; |
| | | var that = this; |
| | | var syncVersion; |
| | | var applyMenuState; |
| | | |
| | | if (!this.isHomeTabUrl(targetUrl)) { |
| | | activeMenuKey = this.findMenuKeyByUrl(targetUrl); |
| | | if (activeMenuKey) { |
| | | groupIndex = this.findMenuGroupIndexByUrl(targetUrl); |
| | | } |
| | | } |
| | | |
| | | this.activeMenuKey = activeMenuKey; |
| | | this.defaultOpeneds = groupIndex ? [groupIndex] : []; |
| | | this.menuSyncVersion += 1; |
| | | syncVersion = this.menuSyncVersion; |
| | | applyMenuState = function () { |
| | | var menu = that.$refs.sideMenu; |
| | | var openedMenus; |
| | | if (syncVersion !== that.menuSyncVersion) { |
| | | return; |
| | | } |
| | | if (!menu) { |
| | | return; |
| | | } |
| | | openedMenus = menu.openedMenus ? menu.openedMenus.slice() : []; |
| | | if (!groupIndex) { |
| | | if (openedMenus.length) { |
| | | that.collapseAllMenus(); |
| | | } |
| | | return; |
| | | } |
| | | if (openedMenus.indexOf(groupIndex) > -1) { |
| | | return; |
| | | } |
| | | if (openedMenus.length) { |
| | | that.collapseAllMenus(); |
| | | } |
| | | menu.open(groupIndex); |
| | | }; |
| | | |
| | | this.$nextTick(function () { |
| | | applyMenuState(); |
| | | if (that.menuSyncTimer) { |
| | | clearTimeout(that.menuSyncTimer); |
| | | that.menuSyncTimer = null; |
| | | } |
| | | that.menuSyncTimer = setTimeout(function () { |
| | | applyMenuState(); |
| | | that.menuSyncTimer = null; |
| | | }, 160); |
| | | }); |
| | | }, |
| | | loadMenu: function () { |
| | | var that = this; |
| | | this.menuLoading = true; |
| | |
| | | that.menuLoading = false; |
| | | if (res.code === 200) { |
| | | that.menus = that.normalizeMenuData(res.data || []); |
| | | that.defaultOpeneds = []; |
| | | that.activeMenuKey = that.findMenuKeyByUrl(that.activeTabUrl); |
| | | that.$nextTick(function () { |
| | | that.collapseAllMenus(); |
| | | }); |
| | | that.syncMenuStateByUrl(that.activeTabUrl); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/login"; |
| | | } else { |
| | |
| | | }; |
| | | window.admin.changeTheme = window.admin.changeTheme || function () {}; |
| | | window.admin.activeNav = function (url) { |
| | | that.activeMenuKey = that.findMenuKeyByUrl(that.resolveViewSrc(url)); |
| | | that.syncMenuStateByUrl(that.resolveViewSrc(url)); |
| | | }; |
| | | |
| | | window.index = window.index || {}; |