#
Junjie
1 天以前 2e7dbd705fc82e8db74b073e55af938d67d8c19f
src/main/webapp/views/index.html
@@ -37,12 +37,25 @@
    }
    .layout-aside {
      position: relative;
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      border-radius: 16px;
      background: linear-gradient(180deg, #16324d 0%, #0d2237 100%);
      box-shadow: 0 14px 32px rgba(22, 50, 77, 0.18);
      transform: translateZ(0);
      backface-visibility: hidden;
      will-change: width;
      transition: width 0.22s cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 0.22s ease;
      -webkit-user-select: none;
      user-select: none;
    }
    .layout-aside.is-animating {
      pointer-events: none;
    }
    .layout-panel {
@@ -55,6 +68,8 @@
      background: #f5f7fa;
      box-shadow: 0 14px 32px rgba(83, 104, 129, 0.12);
      border: 1px solid #e4eaf2;
      transform: translateZ(0);
      backface-visibility: hidden;
    }
    .aside-logo {
@@ -66,6 +81,8 @@
      box-sizing: border-box;
      color: #fff;
      border-bottom: 1px solid rgba(255, 255, 255, 0.08);
      transition: min-height 0.22s cubic-bezier(0.22, 1, 0.36, 1),
      padding 0.22s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .aside-logo-image {
@@ -74,6 +91,8 @@
      max-width: 178px;
      height: auto;
      object-fit: contain;
      transform: translateZ(0);
      transition: max-width 0.22s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .aside-logo.is-collapse {
@@ -90,12 +109,25 @@
      box-sizing: border-box;
    }
    .aside-fade-enter-active,
    .aside-fade-leave-active {
      transition: opacity 0.14s ease, transform 0.18s ease;
    }
    .aside-fade-enter,
    .aside-fade-leave-to {
      opacity: 0;
      transform: translate3d(-10px, 0, 0);
    }
    .aside-search .el-input__inner {
      height: 36px;
      line-height: 36px;
      border: 1px solid rgba(255, 255, 255, 0.10);
      background: rgba(255, 255, 255, 0.08);
      color: #fff;
      -webkit-user-select: text;
      user-select: text;
    }
    .aside-search .el-input__inner::placeholder {
@@ -109,6 +141,7 @@
    .aside-scroll {
      flex: 1;
      min-height: 0;
      transform: translateZ(0);
    }
    .aside-scroll .el-scrollbar__wrap {
@@ -118,6 +151,7 @@
    .side-menu {
      border-right: none;
      background: transparent;
      transform: translateZ(0);
    }
    .side-menu .el-submenu__title,
@@ -127,6 +161,7 @@
      margin: 0 10px 6px;
      border-radius: 10px;
      box-sizing: border-box;
      backface-visibility: hidden;
    }
    .side-menu .el-submenu__title:hover,
@@ -384,6 +419,8 @@
      background: #fff;
      border-bottom: 1px solid #e8edf5;
      box-sizing: border-box;
      -webkit-user-select: none;
      user-select: none;
    }
    .page-tabs {
@@ -402,6 +439,8 @@
    .page-tabs .el-tabs__item {
      height: 38px;
      line-height: 38px;
      -webkit-user-select: none;
      user-select: none;
    }
    .tabs-tools {
@@ -410,6 +449,8 @@
      gap: 8px;
      padding-bottom: 6px;
      flex-shrink: 0;
      -webkit-user-select: none;
      user-select: none;
    }
    .content-main {
@@ -468,45 +509,10 @@
      word-break: break-word;
    }
    @keyframes slideInRight {
      from {
        transform: translate3d(100%, 0, 0);
        opacity: 0;
      }
      to {
        transform: translate3d(0, 0, 0);
        opacity: 1;
      }
    }
    @keyframes slideOutRight {
      from {
        transform: translate3d(0, 0, 0);
        opacity: 1;
      }
      to {
        transform: translate3d(100%, 0, 0);
        opacity: 0;
      }
    }
    @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;
      overflow: hidden;
      animation: slideInRight 0.5s cubic-bezier(0.16, 1, 0.3, 1);
    }
    .ai-drawer-layer-close {
      animation: slideOutRight 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards !important;
    }
    .ai-assistant-btn {
@@ -515,6 +521,87 @@
      bottom: 40px;
      z-index: 9999;
      cursor: pointer;
    }
    .ai-assistant-mask {
      position: fixed;
      inset: 0;
      z-index: 9997;
      background: rgba(15, 23, 42, 0.10);
      backdrop-filter: blur(3px);
    }
    .ai-assistant-drawer {
      position: fixed;
      top: 0;
      right: 0;
      z-index: 9998;
      width: min(600px, 100vw);
      height: 100vh;
      display: flex;
      flex-direction: column;
      background: #fff;
      border-radius: 12px 0 0 12px;
      box-shadow: -10px 0 28px rgba(15, 23, 42, 0.18);
      overflow: hidden;
    }
    .ai-drawer-header {
      height: 54px;
      padding: 0 16px 0 20px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border-bottom: 1px solid rgba(226, 232, 240, 0.92);
      background: rgba(248, 251, 255, 0.96);
      color: #243447;
      font-size: 15px;
      font-weight: 700;
      box-sizing: border-box;
    }
    .ai-drawer-close {
      width: 32px;
      height: 32px;
      border: none;
      border-radius: 10px;
      background: transparent;
      color: #5f7084;
      cursor: pointer;
      font-size: 16px;
    }
    .ai-drawer-close:hover {
      background: rgba(236, 243, 249, 0.92);
      color: #24405c;
    }
    .ai-drawer-frame {
      flex: 1;
      width: 100%;
      border: none;
      background: #fff;
    }
    .ai-mask-enter-active,
    .ai-mask-leave-active {
      transition: opacity .22s ease;
    }
    .ai-mask-enter,
    .ai-mask-leave-to {
      opacity: 0;
    }
    .ai-panel-enter-active,
    .ai-panel-leave-active {
      transition: transform .28s cubic-bezier(0.22, 1, 0.36, 1), opacity .22s ease;
    }
    .ai-panel-enter,
    .ai-panel-leave-to {
      transform: translate3d(100%, 0, 0);
      opacity: 0;
    }
    @media (max-width: 1440px) {
@@ -533,20 +620,25 @@
<body>
<div id="app" v-cloak>
  <el-container class="shell-container">
    <el-aside class="layout-aside" :width="asideWidth">
      <div class="aside-logo" :class="{ 'is-collapse': isCollapse }">
    <el-aside
        class="layout-aside"
        :class="{ 'is-animating': collapseAnimating }"
        :width="asideWidth">
      <div class="aside-logo" :class="{ 'is-collapse': asideCompact }">
        <img class="aside-logo-image" src="../static/images/zy-logo.png" alt="浙江中扬">
      </div>
      <div class="aside-search" v-show="!isCollapse">
        <el-input
            v-model.trim="menuKeyword"
            size="small"
            clearable
            prefix-icon="el-icon-search"
            :placeholder="t('index.searchMenu')">
        </el-input>
      </div>
      <transition name="aside-fade">
        <div class="aside-search" v-show="showAsideExtras">
          <el-input
              v-model.trim="menuKeyword"
              size="small"
              clearable
              prefix-icon="el-icon-search"
              :placeholder="t('index.searchMenu')">
          </el-input>
        </div>
      </transition>
      <el-scrollbar class="aside-scroll">
        <div v-if="menuLoading" class="aside-loading" aria-hidden="true">
@@ -562,7 +654,7 @@
              ref="sideMenu"
              class="side-menu"
              :default-active="activeMenuKey"
              :collapse="isCollapse"
              :collapse="menuCollapse"
              :collapse-transition="false"
              :default-openeds="defaultOpeneds"
              unique-opened
@@ -596,19 +688,21 @@
        </template>
      </el-scrollbar>
      <div class="aside-footer" v-show="!isCollapse">
        <div class="aside-footer-copy">© 2026 {{ t('app.company') }}</div>
        <div class="aside-footer-version">
          <span class="aside-footer-version-text">{{ versionText }}</span>
          <el-tag
              v-if="versionType"
              size="mini"
              :type="versionTagType"
              effect="dark">
            {{ versionTypeTag }}
          </el-tag>
      <transition name="aside-fade">
        <div class="aside-footer" v-show="showAsideExtras">
          <div class="aside-footer-copy">© 2026 {{ t('app.company') }}</div>
          <div class="aside-footer-version">
            <span class="aside-footer-version-text">{{ versionText }}</span>
            <el-tag
                v-if="versionType"
                size="mini"
                :type="versionTagType"
                effect="dark">
              {{ versionTypeTag }}
            </el-tag>
          </div>
        </div>
      </div>
      </transition>
    </el-aside>
    <el-container class="layout-panel">
@@ -728,28 +822,55 @@
    </span>
  </el-dialog>
  <div
      id="ai-assistant-btn"
      class="ai-assistant-btn"
      @mouseenter="showAiTip"
      @mouseleave="hideAiTip"
      @click="openAiAssistant">
  </div>
  <el-tooltip :content="t('common.aiAssistant')" placement="left">
    <div
        id="ai-assistant-btn"
        class="ai-assistant-btn"
        @click="openAiAssistant">
    </div>
  </el-tooltip>
  <transition name="ai-mask">
    <div
        v-if="aiAssistantVisible"
        class="ai-assistant-mask"
        @click="closeAiAssistant">
    </div>
  </transition>
  <transition name="ai-panel">
    <div
        v-if="aiAssistantVisible"
        class="ai-assistant-drawer">
      <div class="ai-drawer-header">
        <span>{{ t('common.aiAssistant') }}</span>
        <button type="button" class="ai-drawer-close" @click="closeAiAssistant">
          <i class="el-icon-close"></i>
        </button>
      </div>
      <iframe
          v-if="aiAssistantMounted"
          class="ai-drawer-frame"
          :src="aiAssistantSrc">
      </iframe>
    </div>
  </transition>
</div>
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../static/js/layer/layer.js"></script>
<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../static/js/common.js"></script>
<script type="text/javascript" src="../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../static/vue/element/element.js"></script>
<script>
  var DASHBOARD_VIEW_VERSION = "20260317-dashboard-network-balanced-columns";
  var HOME_TAB_CONFIG = {
    title: "控制中心",
    url: baseUrl + "/views/watch/console.html",
    title: "系统仪表盘",
    url: baseUrl + "/views/dashboard/dashboard.html?layoutVersion=" + encodeURIComponent(DASHBOARD_VIEW_VERSION),
    home: true,
    group: "实时监控",
    group: "系统概览",
    menuKey: ""
  };
  var LEGACY_HOME_TAB_URL = baseUrl + "/views/watch/console.html";
  var PROFILE_TAB_CONFIG = {
    title: "基本资料",
    url: baseUrl + "/views/detail.html?resourceId=8",
@@ -757,6 +878,8 @@
    group: "账户中心",
    menuKey: ""
  };
  var SIDEBAR_TRANSITION_MS = 220;
  var SIDEBAR_EXPAND_CONTENT_DELAY = 180;
  var TAB_STORAGE_KEY = "wcs-element-home-tabs";
  var USER_STORAGE_KEY = "username";
  var INDEX_I18N_FALLBACKS = {
@@ -770,7 +893,7 @@
    "common.profile": "基本资料",
    "common.logout": "退出登录",
    "common.closeOtherTabs": "关闭其他页签",
    "common.backHome": "返回控制中心",
    "common.backHome": "返回仪表盘",
    "common.aiAssistant": "AI助手",
    "common.workPage": "工作页面",
    "common.businessPage": "业务页面",
@@ -781,8 +904,8 @@
    "index.fakeRunning": "仿真运行中",
    "index.fakeStopped": "仿真未运行",
    "index.licenseExpiring": "许可证即将过期",
    "index.homeTab": "控制中心",
    "index.homeGroup": "实时监控",
    "index.homeTab": "系统仪表盘",
    "index.homeGroup": "系统概览",
    "index.profileGroup": "账户中心",
    "index.versionLoading": "Version loading...",
    "index.licenseExpireAt": "许可证将于 {0} 过期,剩余有效期:{1} 天。",
@@ -800,6 +923,11 @@
    data: function () {
      return {
        isCollapse: false,
        asideCompact: false,
        menuCollapse: false,
        showAsideExtras: true,
        collapseAnimating: false,
        collapseTimer: null,
        menuLoading: true,
        pageLoading: true,
        loadingText: window.WCS_I18N ? window.WCS_I18N.tl("正在加载页面...") : "正在加载页面...",
@@ -822,13 +950,14 @@
        menuSyncVersion: 0,
        menuSyncTimer: null,
        userName: localStorage.getItem(USER_STORAGE_KEY) || (window.WCS_I18N ? window.WCS_I18N.tl("管理员") : "管理员"),
        aiLayerIndex: null,
        aiTipIndex: null
        aiAssistantVisible: false,
        aiAssistantMounted: false,
        aiAssistantSrc: baseUrl + "/views/ai/diagnosis.html"
      };
    },
    computed: {
      asideWidth: function () {
        return this.isCollapse ? "72px" : "248px";
        return this.asideCompact ? "72px" : "248px";
      },
      filteredMenus: function () {
        var keyword = (this.menuKeyword || "").toLowerCase();
@@ -962,9 +1091,9 @@
        clearTimeout(this.menuSyncTimer);
        this.menuSyncTimer = null;
      }
      if (this.aiTipIndex) {
        layer.close(this.aiTipIndex);
        this.aiTipIndex = null;
      if (this.collapseTimer) {
        clearTimeout(this.collapseTimer);
        this.collapseTimer = null;
      }
    },
    methods: {
@@ -1009,17 +1138,7 @@
        PROFILE_TAB_CONFIG.title = profileConfig.title;
        PROFILE_TAB_CONFIG.group = profileConfig.group;
        for (i = 0; i < this.tabs.length; i++) {
          if (this.isHomeTabUrl(this.tabs[i].url)) {
            this.tabs[i].title = homeConfig.title;
            this.tabs[i].group = homeConfig.group;
            this.tabs[i].home = true;
          } else if (this.resolveViewSrc(this.tabs[i].url) === this.resolveViewSrc(profileConfig.url)) {
            this.tabs[i].title = profileConfig.title;
            this.tabs[i].group = profileConfig.group;
          } else {
            this.tabs[i].title = this.translateTabTitle(this.tabs[i].title);
            this.tabs[i].group = this.tl(this.tabs[i].group);
          }
          this.syncTabMeta(this.tabs[i], homeConfig, profileConfig);
        }
        this.updateDocumentTitle(this.activeTabTitle);
        this.persistTabs();
@@ -1057,6 +1176,105 @@
      translateTabTitle: function (title) {
        return this.tl(title);
      },
      findMenuMeta: function (tab) {
        var menuEntry;
        var i;
        var j;
        var group;
        var item;
        if (!tab) {
          return null;
        }
        if (tab.menuKey) {
          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.tabKey === tab.menuKey) {
                return {
                  group: group,
                  item: item
                };
              }
            }
          }
        }
        menuEntry = this.findMenuEntryByUrl(tab.url);
        if (menuEntry) {
          return menuEntry;
        }
        return null;
      },
      normalizeMenuMatchUrl: function (url, stripQuery) {
        var normalized = this.resolveViewSrc(url || "");
        var hashIndex = normalized.indexOf("#");
        var queryIndex;
        if (hashIndex > -1) {
          normalized = normalized.substring(0, hashIndex);
        }
        if (stripQuery) {
          queryIndex = normalized.indexOf("?");
          if (queryIndex > -1) {
            normalized = normalized.substring(0, queryIndex);
          }
        }
        return normalized;
      },
      findMenuEntryByUrl: function (url) {
        var normalized = this.normalizeMenuMatchUrl(url, false);
        var normalizedBase = this.normalizeMenuMatchUrl(url, true);
        var fallback = null;
        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,
                item: item
              };
            }
            if (!fallback && this.normalizeMenuMatchUrl(item.url, true) === normalizedBase) {
              fallback = {
                group: group,
                item: item
              };
            }
          }
        }
        return fallback;
      },
      syncTabMeta: function (tab, homeConfig, profileConfig) {
        var menuMeta;
        if (!tab) {
          return;
        }
        if (this.isHomeTabUrl(tab.url)) {
          tab.title = homeConfig.title;
          tab.group = homeConfig.group;
          tab.home = true;
          return;
        }
        if (this.resolveViewSrc(tab.url) === this.resolveViewSrc(profileConfig.url)) {
          tab.title = profileConfig.title;
          tab.group = profileConfig.group;
          return;
        }
        menuMeta = this.findMenuMeta(tab);
        if (menuMeta) {
          tab.title = menuMeta.item.name;
          tab.group = menuMeta.group.menu;
          tab.menuKey = menuMeta.item.tabKey || tab.menuKey;
          return;
        }
        tab.title = this.translateTabTitle(tab.title);
        tab.group = this.tl(tab.group);
      },
      updateDocumentTitle: function (title) {
        document.title = title + " - " + this.t("app.title");
      },
@@ -1090,12 +1308,16 @@
        };
      },
      normalizeStoredTab: function (tab) {
        var homeConfig = this.resolveHomeConfig();
        var resolvedUrl = this.resolveViewSrc(tab.url);
        var isLegacyHome = resolvedUrl === this.resolveViewSrc(LEGACY_HOME_TAB_URL);
        var isHome = !!tab.home || isLegacyHome;
        var created = this.createTab({
          title: this.translateTabTitle(tab.title),
          url: this.resolveViewSrc(tab.url),
          home: !!tab.home,
          group: this.tl(tab.group || ""),
          menuKey: tab.menuKey || ""
          title: isHome ? homeConfig.title : this.translateTabTitle(tab.title),
          url: isHome ? homeConfig.url : resolvedUrl,
          home: isHome,
          group: isHome ? homeConfig.group : this.tl(tab.group || ""),
          menuKey: isHome ? homeConfig.menuKey : (tab.menuKey || "")
        });
        created.loaded = false;
        return created;
@@ -1118,6 +1340,9 @@
              }
            }
            active = parsed ? parsed.activeTab : "";
            if (this.resolveViewSrc(active) === this.resolveViewSrc(LEGACY_HOME_TAB_URL)) {
              active = homeTab.name;
            }
          } catch (e) {
            tabs = [];
            active = "";
@@ -1346,7 +1571,7 @@
        script = frameDocument.createElement("script");
        script.id = "wcs-i18n-bridge-script";
        script.type = "text/javascript";
        script.src = baseUrl + "/static/js/common.js?v=20260309_i18n_fix1";
        script.src = baseUrl + "/static/js/common.js";
        script.onload = applyFrameI18n;
        frameDocument.head.appendChild(script);
      },
@@ -1356,8 +1581,65 @@
          this.syncFrameI18n(this.tabs[i].name);
        }
      },
      clearCollapseTimer: function () {
        if (this.collapseTimer) {
          clearTimeout(this.collapseTimer);
          this.collapseTimer = null;
        }
      },
      nextFrame: function (callback) {
        if (window.requestAnimationFrame) {
          window.requestAnimationFrame(function () {
            callback();
          });
          return;
        }
        setTimeout(callback, 16);
      },
      finishCollapseStage: function (callback, delay) {
        var that = this;
        this.clearCollapseTimer();
        this.collapseTimer = setTimeout(function () {
          that.collapseTimer = null;
          callback();
        }, delay);
      },
      toggleCollapse: function () {
        this.isCollapse = !this.isCollapse;
        var that = this;
        if (this.collapseAnimating) {
          return;
        }
        this.collapseAnimating = true;
        if (this.isCollapse) {
          this.isCollapse = false;
          this.asideCompact = false;
          this.nextFrame(function () {
            that.finishCollapseStage(function () {
              that.menuCollapse = false;
              that.showAsideExtras = true;
              that.collapseAnimating = false;
              that.$nextTick(function () {
                that.syncMenuStateByUrl(that.activeTabUrl);
              });
            }, SIDEBAR_EXPAND_CONTENT_DELAY);
          });
          return;
        }
        this.isCollapse = true;
        this.showAsideExtras = false;
        this.menuCollapse = true;
        this.$nextTick(function () {
          that.nextFrame(function () {
            that.asideCompact = true;
            that.finishCollapseStage(function () {
              that.collapseAnimating = false;
            }, SIDEBAR_TRANSITION_MS);
          });
        });
      },
      handleMenuSelect: function (group, item) {
        this.addOrActivateTab({
@@ -1369,40 +1651,55 @@
        });
      },
      findMenuKeyByUrl: 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 item.tabKey;
            }
          }
        var entry = this.findMenuEntryByUrl(url);
        if (entry && entry.item) {
          return entry.item.tabKey || "";
        }
        return "";
      },
      findMenuGroupIndexByUrl: function (url) {
        var normalized = this.resolveViewSrc(url);
        var entry = this.findMenuEntryByUrl(url);
        if (entry && entry.group) {
          return "group-" + entry.group.menuId;
        }
        return "";
      },
      injectDashboardMenu: function (menus) {
        var homeConfig = this.resolveHomeConfig();
        var dashboardUrl = this.resolveViewSrc(homeConfig.url);
        var i;
        var j;
        var group;
        var item;
        for (i = 0; i < this.menus.length; i++) {
          group = this.menus[i];
        for (i = 0; i < menus.length; i++) {
          group = menus[i];
          for (j = 0; j < group.subMenu.length; j++) {
            item = group.subMenu[j];
            if (item.url === normalized) {
              return "group-" + group.menuId;
            if (item.url === dashboardUrl) {
              item.name = homeConfig.title;
              group.menu = homeConfig.group;
              group.menuCode = group.menuCode || "index";
              HOME_TAB_CONFIG.menuKey = item.tabKey || dashboardUrl;
              return menus;
            }
          }
        }
        return "";
        HOME_TAB_CONFIG.menuKey = dashboardUrl;
        menus.unshift({
          menuId: "dashboard-home",
          menu: homeConfig.group,
          menuCode: "index",
          subMenu: [{
            id: "dashboard-home-tab",
            name: homeConfig.title,
            code: "dashboard/dashboard.html",
            url: dashboardUrl,
            tabKey: dashboardUrl
          }]
        });
        return menus;
      },
      normalizeMenuData: function (data) {
        var result = [];
@@ -1435,7 +1732,7 @@
          });
        }
        return result;
        return this.injectDashboardMenu(result);
      },
      buildMenuSrc: function (code, resourceId) {
        var normalized = code || "";
@@ -1496,11 +1793,9 @@
        var syncVersion;
        var applyMenuState;
        if (!this.isHomeTabUrl(targetUrl)) {
          activeMenuKey = this.findMenuKeyByUrl(targetUrl);
          if (activeMenuKey) {
            groupIndex = this.findMenuGroupIndexByUrl(targetUrl);
          }
        activeMenuKey = this.findMenuKeyByUrl(targetUrl);
        if (activeMenuKey) {
          groupIndex = this.findMenuGroupIndexByUrl(targetUrl);
        }
        this.activeMenuKey = activeMenuKey;
@@ -1514,6 +1809,9 @@
            return;
          }
          if (!menu) {
            return;
          }
          if (that.menuCollapse) {
            return;
          }
          openedMenus = menu.openedMenus ? menu.openedMenus.slice() : [];
@@ -1555,6 +1853,7 @@
            that.menuLoading = false;
            if (res.code === 200) {
              that.menus = that.normalizeMenuData(res.data || []);
              that.refreshI18nState();
              that.syncMenuStateByUrl(that.activeTabUrl);
            } else if (res.code === 403) {
              top.location.href = baseUrl + "/login";
@@ -1712,72 +2011,14 @@
          document.exitFullscreen();
        }
      },
      showAiTip: function () {
        this.hideAiTip();
        this.aiTipIndex = layer.tips(this.t("common.aiAssistant"), "#ai-assistant-btn", {
          tips: [1, "#333"],
          time: -1
        });
      },
      hideAiTip: function () {
        if (this.aiTipIndex) {
          layer.close(this.aiTipIndex);
          this.aiTipIndex = null;
        }
      },
      openAiAssistant: function () {
        var that = this;
        var $layero;
        var $shade;
        this.hideAiTip();
        if (this.aiLayerIndex !== null && $("#layui-layer" + this.aiLayerIndex).length > 0) {
          $layero = $("#layui-layer" + this.aiLayerIndex);
          $shade = $("#layui-layer-shade" + this.aiLayerIndex);
          $shade.show().css("opacity", 0.1);
          $layero.show();
          $layero.removeClass("ai-drawer-layer-close");
          $layero.removeClass("ai-drawer-layer");
          void $layero.get(0).offsetWidth;
          $layero.addClass("ai-drawer-layer");
          return;
        if (!this.aiAssistantMounted) {
          this.aiAssistantMounted = true;
        }
        layer.open({
          type: 2,
          title: false,
          closeBtn: 0,
          shadeClose: false,
          shade: 0.1,
          area: ["600px", "100%"],
          offset: "r",
          anim: -1,
          isOutAnim: false,
          skin: "ai-drawer-layer",
          content: "ai/diagnosis.html",
          success: function (layero, index) {
            var shadeId;
            var $shadeEl;
            that.aiLayerIndex = index;
            shadeId = layero.attr("id").replace("layui-layer", "layui-layer-shade");
            $shadeEl = $("#" + shadeId);
            $shadeEl.css({
              "backdrop-filter": "blur(3px)",
              transition: "opacity 0.8s"
            });
            $shadeEl.off("click.aiAssistant").on("click.aiAssistant", function () {
              layero.addClass("ai-drawer-layer-close");
              $shadeEl.css("opacity", 0);
              setTimeout(function () {
                layero.hide();
                $shadeEl.hide();
              }, 400);
            });
          }
        });
        this.aiAssistantVisible = true;
      },
      closeAiAssistant: function () {
        this.aiAssistantVisible = false;
      },
      handleUserCommand: function (command) {
        if (command === "profile") {
@@ -1813,31 +2054,35 @@
        window.index = window.index || {};
        window.index.loadView = function (param) {
          var url;
          var menuEntry;
          if (!param || !param.menuPath) {
            return;
          }
          url = that.resolveViewSrc(param.menuPath);
          menuEntry = that.findMenuEntryByUrl(url);
          that.addOrActivateTab({
            title: that.stripTags(param.menuName) || that.t("common.workPage"),
            url: url,
            title: that.stripTags(param.menuName) || (menuEntry && menuEntry.item ? menuEntry.item.name : that.t("common.workPage")),
            url: menuEntry && menuEntry.item ? menuEntry.item.url : url,
            home: false,
            group: that.t("common.businessPage"),
            menuKey: that.findMenuKeyByUrl(url)
            group: menuEntry && menuEntry.group ? menuEntry.group.menu : that.t("common.businessPage"),
            menuKey: menuEntry && menuEntry.item ? menuEntry.item.tabKey : that.findMenuKeyByUrl(url)
          });
        };
        window.index.loadHome = function (param) {
          var url;
          var menuEntry;
          if (!param || !param.menuPath) {
            that.openHomeTab();
            return;
          }
          url = that.resolveViewSrc(param.menuPath);
          menuEntry = that.findMenuEntryByUrl(url);
          that.addOrActivateTab({
            title: that.stripTags(param.menuName) || that.resolveHomeConfig().title,
            url: url,
            title: that.stripTags(param.menuName) || (menuEntry && menuEntry.item ? menuEntry.item.name : that.resolveHomeConfig().title),
            url: menuEntry && menuEntry.item ? menuEntry.item.url : url,
            home: true,
            group: that.resolveHomeConfig().group,
            menuKey: that.findMenuKeyByUrl(url)
            group: menuEntry && menuEntry.group ? menuEntry.group.menu : that.resolveHomeConfig().group,
            menuKey: menuEntry && menuEntry.item ? menuEntry.item.tabKey : that.findMenuKeyByUrl(url)
          });
        };