From 8a5e373add078ed9aa11d4e406a9f68638e1b42e Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期六, 07 三月 2026 14:06:11 +0800
Subject: [PATCH] #

---
 src/main/webapp/views/index.html | 1809 +++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 1,411 insertions(+), 398 deletions(-)

diff --git a/src/main/webapp/views/index.html b/src/main/webapp/views/index.html
index f970799..b9bf30f 100644
--- a/src/main/webapp/views/index.html
+++ b/src/main/webapp/views/index.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
-<html lang="en">
-
+<html lang="zh-CN">
 <head>
   <meta charset="utf-8">
   <title>娴欐睙涓壃 - 鑷姩鍖栫珛浣撲粨搴� - WCS</title>
@@ -8,43 +7,425 @@
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
   <meta name="viewport"
         content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
-  <link rel="stylesheet" href="../static/layui/css/layui.css" media="all">
-  <link rel="stylesheet" href="../static/css/admin.css?v=318" media="all">
-  <link rel="stylesheet" href="../static/css/loader.css" media="all">
+  <link rel="stylesheet" href="../static/vue/element/element.css">
   <style>
-    .layui-logo img {
-      width: 25px;
+    [v-cloak] {
+      display: none;
     }
 
-    .layui-logo cite {
-      font-size: 18px;
-      font-weight: 400;
-      /*margin-left: 5px;*/
-    }
-
-    /* 寮圭獥鏍峰紡 */
-    .popup {
-      position: fixed;
-      top: 0;
-      left: 0;
+    html,
+    body,
+    #app {
       width: 100%;
       height: 100%;
-      background-color: rgba(0, 0, 0, 0.5);
-      display: none;
-      justify-content: center;
-      align-items: center;
-      z-index: 9999;
+      margin: 0;
+      overflow: hidden;
     }
 
-    .popup-content {
-      background-color: #fff;
-      padding: 20px;
-      border-radius: 5px;
-      box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.3);
+    body {
+      font-family: "Helvetica Neue", "PingFang SC", "Microsoft YaHei", sans-serif;
+      background: linear-gradient(180deg, #f3f6fb 0%, #e9eef6 100%);
+      color: #1f2d3d;
+    }
+
+    .shell-container {
+      height: 100%;
+      padding: 12px;
+      box-sizing: border-box;
+      gap: 12px;
+      background: transparent;
+    }
+
+    .layout-aside {
+      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);
+    }
+
+    .layout-panel {
+      display: flex;
+      flex-direction: column;
+      min-width: 0;
+      height: 100%;
+      overflow: hidden;
+      border-radius: 16px;
+      background: #f5f7fa;
+      box-shadow: 0 14px 32px rgba(83, 104, 129, 0.12);
+      border: 1px solid #e4eaf2;
+    }
+
+    .aside-logo {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      min-height: 78px;
+      padding: 18px 16px 16px;
+      box-sizing: border-box;
+      color: #fff;
+      border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+    }
+
+    .aside-logo-image {
+      display: block;
+      width: 100%;
+      max-width: 178px;
+      height: auto;
+      object-fit: contain;
+    }
+
+    .aside-logo.is-collapse {
+      min-height: 64px;
+      padding: 16px 10px 14px;
+    }
+
+    .aside-logo.is-collapse .aside-logo-image {
+      max-width: 42px;
+    }
+
+    .aside-search {
+      padding: 12px 12px 8px;
+      box-sizing: border-box;
+    }
+
+    .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;
+    }
+
+    .aside-search .el-input__inner::placeholder {
+      color: rgba(255, 255, 255, 0.48);
+    }
+
+    .aside-search .el-input__prefix {
+      color: rgba(255, 255, 255, 0.58);
+    }
+
+    .aside-scroll {
+      flex: 1;
+      min-height: 0;
+    }
+
+    .aside-scroll .el-scrollbar__wrap {
+      overflow-x: hidden;
+    }
+
+    .side-menu {
+      border-right: none;
+      background: transparent;
+    }
+
+    .side-menu .el-submenu__title,
+    .side-menu .el-menu-item {
+      height: 46px;
+      line-height: 46px;
+      margin: 0 10px 6px;
+      border-radius: 10px;
+      box-sizing: border-box;
+    }
+
+    .side-menu .el-submenu__title:hover,
+    .side-menu .el-menu-item:hover {
+      background: rgba(255, 255, 255, 0.08) !important;
+      color: #fff !important;
+    }
+
+    .side-menu .el-menu-item.is-active {
+      background: linear-gradient(90deg, #3a7afe 0%, #2867e0 100%) !important;
+      color: #fff !important;
+      box-shadow: 0 8px 18px rgba(40, 103, 224, 0.26);
+    }
+
+    .side-menu .el-submenu .el-menu {
+      background: transparent;
+    }
+
+    .side-menu.el-menu--collapse .el-submenu__title,
+    .side-menu.el-menu--collapse .el-menu-item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 0 !important;
       text-align: center;
     }
 
-    /* AI鍔╂墜鎶藉眽鍔ㄧ敾 */
+    .side-menu.el-menu--collapse .el-submenu__title > i:not(.el-submenu__icon-arrow),
+    .side-menu.el-menu--collapse .el-menu-item [class^="el-icon-"],
+    .side-menu.el-menu--collapse .el-menu-item [class*=" el-icon-"] {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      height: 100%;
+      margin: 0 !important;
+      width: 100%;
+      font-size: 22px;
+      line-height: 1;
+      text-align: center;
+    }
+
+    .side-menu.el-menu--collapse .el-submenu__icon-arrow {
+      display: none !important;
+    }
+
+    .el-menu--popup {
+      min-width: 188px;
+      padding: 8px;
+      border: none;
+      border-radius: 14px;
+      background: linear-gradient(180deg, #16324d 0%, #0d2237 100%) !important;
+      box-shadow: 0 16px 32px rgba(13, 34, 55, 0.28);
+    }
+
+    .el-menu--popup .el-menu-item,
+    .el-menu--popup .el-submenu__title {
+      height: 44px;
+      line-height: 44px;
+      margin: 0 0 6px;
+      border-radius: 10px;
+      color: #c6d1df;
+      background: transparent !important;
+    }
+
+    .el-menu--popup .el-menu-item:last-child,
+    .el-menu--popup .el-submenu__title:last-child {
+      margin-bottom: 0;
+    }
+
+    .el-menu--popup .el-menu-item:hover,
+    .el-menu--popup .el-submenu__title:hover {
+      color: #fff !important;
+      background: rgba(255, 255, 255, 0.08) !important;
+    }
+
+    .el-menu--popup .el-menu-item.is-active {
+      color: #fff !important;
+      background: linear-gradient(90deg, #3a7afe 0%, #2867e0 100%) !important;
+      box-shadow: 0 8px 18px rgba(40, 103, 224, 0.26);
+    }
+
+    .el-menu--popup .el-submenu__icon-arrow,
+    .el-menu--popup [class^="el-icon-"],
+    .el-menu--popup [class*=" el-icon-"] {
+      color: inherit;
+    }
+
+    .side-menu .el-submenu__icon-arrow,
+    .side-menu [class^="el-icon-"],
+    .side-menu [class*=" el-icon-"] {
+      color: inherit;
+    }
+
+    .aside-empty {
+      padding: 24px 12px;
+    }
+
+    .aside-empty .el-empty__description p {
+      color: rgba(255, 255, 255, 0.58);
+    }
+
+    .aside-footer {
+      padding: 14px 16px 16px;
+      border-top: 1px solid rgba(255, 255, 255, 0.08);
+      color: rgba(255, 255, 255, 0.72);
+      font-size: 12px;
+      line-height: 1.7;
+    }
+
+    .aside-footer-copy {
+      margin-bottom: 8px;
+      color: rgba(255, 255, 255, 0.62);
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .aside-footer-version {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      color: #fff;
+    }
+
+    .aside-footer-version-text {
+      flex: 1;
+      min-width: 0;
+      font-size: 13px;
+      font-weight: 500;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .header-bar {
+      height: 60px !important;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: 12px;
+      padding: 0 16px;
+      box-sizing: border-box;
+      background: #fff;
+      border-bottom: 1px solid #e8edf5;
+    }
+
+    .header-left {
+      min-width: 0;
+      display: flex;
+      align-items: center;
+      gap: 12px;
+    }
+
+    .header-title-group {
+      min-width: 0;
+    }
+
+    .header-title {
+      color: #303133;
+      font-size: 18px;
+      font-weight: 600;
+      line-height: 1.2;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .header-right {
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      gap: 8px;
+      flex-shrink: 0;
+    }
+
+    .header-right .el-tag {
+      border-radius: 999px;
+    }
+
+    .header-tag-clickable {
+      cursor: pointer;
+    }
+
+    .user-dropdown {
+      display: inline-flex;
+      align-items: center;
+      gap: 8px;
+      height: 38px;
+      padding: 0 10px 0 6px;
+      border: 1px solid #dce3ed;
+      border-radius: 999px;
+      background: #fff;
+      cursor: pointer;
+      user-select: none;
+    }
+
+    .user-name {
+      display: inline-block;
+      max-width: 96px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      color: #303133;
+      font-size: 13px;
+    }
+
+    .tabs-row {
+      display: flex;
+      align-items: flex-end;
+      gap: 10px;
+      flex-shrink: 0;
+      padding: 0 12px;
+      background: #fff;
+      border-bottom: 1px solid #e8edf5;
+      box-sizing: border-box;
+    }
+
+    .page-tabs {
+      flex: 1;
+      min-width: 0;
+    }
+
+    .page-tabs .el-tabs__header {
+      margin: 0;
+    }
+
+    .page-tabs .el-tabs__nav-wrap::after {
+      height: 0;
+    }
+
+    .page-tabs .el-tabs__item {
+      height: 38px;
+      line-height: 38px;
+    }
+
+    .tabs-tools {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding-bottom: 6px;
+      flex-shrink: 0;
+    }
+
+    .content-main {
+      display: flex;
+      flex: 1;
+      padding: 12px;
+      box-sizing: border-box;
+      background: #f5f7fa;
+      min-height: 0;
+      overflow: hidden;
+    }
+
+    .frame-wrapper {
+      flex: 1;
+      position: relative;
+      min-height: 0;
+      overflow: hidden;
+      border-radius: 12px;
+      border: 1px solid #e5ebf3;
+      background: #fff;
+    }
+
+    .page-frame {
+      width: 100%;
+      height: 100%;
+      border: 0;
+      display: block;
+      background: #fff;
+    }
+
+    .page-loading {
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+      z-index: 10;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 10px;
+      background: rgba(255, 255, 255, 0.92);
+      color: #606266;
+      font-size: 14px;
+    }
+
+    .page-loading i {
+      font-size: 18px;
+      color: #409eff;
+    }
+
+    .license-dialog-text {
+      color: #303133;
+      font-size: 15px;
+      line-height: 1.8;
+      word-break: break-word;
+    }
+
     @keyframes slideInRight {
       from {
         transform: translate3d(100%, 0, 0);
@@ -79,405 +460,1037 @@
     .ai-drawer-layer-close {
       animation: slideOutRight 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards !important;
     }
+
+    .ai-assistant-btn {
+      position: fixed;
+      right: 20px;
+      bottom: 40px;
+      z-index: 9999;
+      cursor: pointer;
+    }
+
+    @media (max-width: 1440px) {
+      .user-name {
+        max-width: 72px;
+      }
+    }
+
+    @media (max-width: 1280px) {
+      .header-right .el-tag {
+        display: none;
+      }
+    }
   </style>
 </head>
+<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 }">
+        <img class="aside-logo-image" src="../static/images/zy-logo.png" alt="娴欐睙涓壃">
+      </div>
 
-<body class="layui-layout-body">
-<div class="layui-layout layui-layout-admin">
-  <!-- 澶撮儴 -->
-  <div class="layui-header">
-    <div class="layui-logo">
-      <img src="../static/images/zy-logo-dark.png" style="display: inline-block; width: 60%;height: auto">
-      <!--          <span style="margin-top: 0; letter-spacing: 10px">涓壃绔嬪簱</span>-->
-      <!--          <img src="../static/image/logo.svg"/>-->
-      <!--          <cite>涓壃 - Zoneyung</cite>-->
-    </div>
+      <div class="aside-search" v-show="!isCollapse">
+        <el-input
+            v-model.trim="menuKeyword"
+            size="small"
+            clearable
+            prefix-icon="el-icon-search"
+            placeholder="鎼滅储鑿滃崟">
+        </el-input>
+      </div>
 
-    <ul class="layui-nav layui-layout-left">
-      <li class="layui-nav-item" lay-unselect>
-        <a ew-event="flexible" title="渚ц竟浼哥缉"><i class="layui-icon layui-icon-shrink-right"></i></a>
-      </li>
-      <li class="layui-nav-item" lay-unselect>
-        <a ew-event="refresh" title="鍒锋柊"><i class="layui-icon layui-icon-refresh-3"></i></a>
-      </li>
-    </ul>
-    <ul class="layui-nav layui-layout-right">
-      <!--      <li class="layui-nav-item" lay-unselect>-->
-      <!--        <a ew-event="note" title="渚跨"><i class="layui-icon layui-icon-note"></i></a>-->
-      <!--      </li>-->
-      <li class="layui-nav-item" lay-unselect id="fakeShow"
-          style="display: none;user-select: none;margin-right: 10px;">
-        <div style="color: red;" id="fakeShowText">浠跨湡妯℃嫙杩愯涓�</div>
-      </li>
-      <li class="layui-nav-item" lay-unselect id="licenseShow" style="display: none;user-select: none;">
-        <div style="color: red;">涓存椂璁稿彲璇佹湁鏁堟湡锛�<span id="licenseDays">29</span>澶�</div>
-      </li>
-      <li class="layui-nav-item layui-hide-xs" lay-unselect>
-        <a ew-event="fullScreen" title="鍏ㄥ睆"><i class="layui-icon layui-icon-screen-full"></i></a>
-      </li>
-      <li class="layui-nav-item" lay-unselect>
-        <a>
-          <cite id="username" style="margin-right: 5px">绠$悊鍛�</cite>
-        </a>
-        <dl class="layui-nav-child">
-          <dd lay-unselect><a ew-href="detail.html?resourceId=8">鍩烘湰璧勬枡</a></dd>
-          <hr>
-          <dd lay-unselect><a id="logout">閫�鍑�</a></dd>
-        </dl>
-      </li>
-      <li class="layui-nav-item" lay-unselect>
-        <a ew-event="theme" title="涓婚"><i class="layui-icon layui-icon-more-vertical"></i></a>
-      </li>
-    </ul>
+      <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>
+        </div>
+      </el-scrollbar>
+
+      <div class="aside-footer" v-show="!isCollapse">
+        <div class="aside-footer-copy">漏 2026 娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙�</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>
+    </el-aside>
+
+    <el-container class="layout-panel">
+      <el-header class="header-bar">
+        <div class="header-left">
+          <el-button
+              circle
+              size="mini"
+              :icon="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
+              @click="toggleCollapse">
+          </el-button>
+          <div class="header-title-group">
+            <div class="header-title">{{ activeTabTitle }}</div>
+          </div>
+        </div>
+
+        <div class="header-right">
+          <el-tag
+              v-if="licenseDisplayVisible"
+              size="mini"
+              :type="licenseTagType"
+              effect="dark">
+            涓存椂璁稿彲璇佹湁鏁堟湡锛歿{ licenseDays }}澶�
+          </el-tag>
+          <el-tag
+              v-if="fakeVisible"
+              class="header-tag-clickable"
+              size="mini"
+              :type="fakeRunning ? 'danger' : 'info'"
+              effect="dark"
+              @click.native="toggleFakeSystem">
+            {{ fakeRunning ? '浠跨湡杩愯涓�' : '浠跨湡鏈繍琛�' }}
+          </el-tag>
+          <el-button circle size="mini" icon="el-icon-refresh" @click="refreshActiveTab"></el-button>
+          <el-button circle size="mini" icon="el-icon-full-screen" @click="toggleFullScreen"></el-button>
+
+          <el-dropdown trigger="click" @command="handleUserCommand">
+            <span class="user-dropdown">
+              <el-avatar size="small">{{ userShortName }}</el-avatar>
+              <span class="user-name" id="person-username">{{ userName }}</span>
+              <i class="el-icon-arrow-down"></i>
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="profile">鍩烘湰璧勬枡</el-dropdown-item>
+              <el-dropdown-item command="logout" divided>閫�鍑虹櫥褰�</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </div>
+      </el-header>
+
+      <div class="tabs-row">
+        <el-tabs
+            class="page-tabs"
+            v-model="activeTab"
+            type="card"
+            @tab-click="handleTabClick"
+            @tab-remove="removeTab">
+          <el-tab-pane
+              v-for="tab in tabs"
+              :key="tab.name"
+              :label="tab.title"
+              :name="tab.name"
+              :closable="!tab.home">
+          </el-tab-pane>
+        </el-tabs>
+
+        <div class="tabs-tools">
+          <el-tooltip content="鍏抽棴鍏朵粬椤电" placement="top">
+            <el-button size="mini" icon="el-icon-close" @click="closeOtherTabs"></el-button>
+          </el-tooltip>
+          <el-tooltip content="杩斿洖鎺у埗涓績" placement="top">
+            <el-button size="mini" type="primary" icon="el-icon-house" @click="openHomeTab"></el-button>
+          </el-tooltip>
+        </div>
+      </div>
+
+      <el-main class="content-main">
+        <div class="frame-wrapper">
+          <div class="page-loading" v-if="pageLoading">
+            <i class="el-icon-loading"></i>
+            <span>{{ loadingText }}</span>
+          </div>
+
+          <iframe
+              v-for="tab in tabs"
+              :key="'frame-' + tab.name"
+              class="page-frame"
+              v-show="activeTab === tab.name"
+              :src="tab.currentSrc"
+              @load="handleFrameLoad(tab.name)">
+          </iframe>
+        </div>
+      </el-main>
+    </el-container>
+  </el-container>
+
+  <el-dialog
+      title="璁稿彲璇佸嵆灏嗚繃鏈�"
+      :visible.sync="licenseDialogVisible"
+      width="420px"
+      :close-on-click-modal="false">
+    <div class="license-dialog-text">{{ licenseDialogText }}</div>
+    <span slot="footer">
+      <el-button type="primary" @click="licenseDialogVisible = false">鐭ラ亾浜�</el-button>
+    </span>
+  </el-dialog>
+
+  <div
+      id="ai-assistant-btn"
+      class="ai-assistant-btn"
+      @mouseenter="showAiTip"
+      @mouseleave="hideAiTip"
+      @click="openAiAssistant">
   </div>
-
-  <!-- 渚ц竟鏍� -->
-  <div class="layui-side">
-    <div class="layui-side-scroll">
-      <ul id="menu-main" class="layui-nav layui-nav-tree arrow2" lay-filter="admin-side-nav" lay-shrink="_all">
-      </ul>
-    </div>
-  </div>
-
-  <!-- 涓讳綋閮ㄥ垎 -->
-  <div class="layui-body"></div>
-  <!-- 搴曢儴 -->
-  <div class="layui-footer layui-text">
-    copyright 漏 2026 娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙� all rights reserved.
-    <span class="pull-right" id="system-version">Version loading...</span>
-  </div>
-
-</div>
-
-<!--鍒濆鍖栧姞杞藉眰-->
-<div class="layuimini-loader">
-  <div class="layuimini-loader-inner"></div>
-</div>
-
-<!-- 寮圭獥鍐呭 -->
-<div class="popup" id="popup">
-  <div class="popup-content">
-    <h2 style="font-size: 28px;margin-bottom: 10px;">璁稿彲璇佸嵆灏嗚繃鏈�</h2>
-    <div id="popup-text" style="font-size: 28px;color: red"></div>
-    <button
-            style="background-color: #007bff;color: #fff;border: none;padding: 10px 20px;border-radius: 5px;cursor: pointer;font-size: 16px;"
-            onclick="hidePopup()">鍏抽棴</button>
-  </div>
-</div>
-
-<!-- 鍙充笅瑙扴VG鍔ㄧ敾 -->
-<div id="ai-assistant-btn" style="position: fixed; bottom: 40px; right: 20px; z-index: 9999; cursor: pointer;">
 </div>
 
 <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../static/layui/layui.js"></script>
-<script type="text/javascript" src="../static/js/handlebars/handlebars-v4.5.3.js"></script>
+<script type="text/javascript" src="../static/js/layer/layer.js"></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 systemVersion = '...';
-  var systemVersionType = '';
+  var HOME_TAB_CONFIG = {
+    title: "鎺у埗涓績",
+    url: baseUrl + "/views/watch/console.html",
+    home: true,
+    group: "瀹炴椂鐩戞帶",
+    menuKey: ""
+  };
+  var PROFILE_TAB_CONFIG = {
+    title: "鍩烘湰璧勬枡",
+    url: baseUrl + "/views/detail.html?resourceId=8",
+    home: false,
+    group: "璐︽埛涓績",
+    menuKey: ""
+  };
+  var TAB_STORAGE_KEY = "wcs-element-home-tabs";
+  var USER_STORAGE_KEY = "username";
 
-  // 鍔犺浇绯荤粺鐗堟湰淇℃伅
-  function loadSystemVersion() {
-    $.ajax({
-      url: baseUrl + "/openapi/getSystemVersion",
-      headers: { 'token': localStorage.getItem('token') },
-      method: 'GET',
-      success: function (res) {
-        if (res.code === 200) {
-          systemVersion = res.data.version;
-          systemVersionType = res.data.versionType;
-          var versionTypeLabel = systemVersionType === 'stable' ? '' : ' (' + systemVersionType + ')';
-          var versionTypeColor = systemVersionType === 'stable' ? 'rgb(25,190,107)' : 'rgb(245,166,35)';
+  new Vue({
+    el: "#app",
+    data: function () {
+      return {
+        isCollapse: false,
+        menuLoading: false,
+        pageLoading: true,
+        loadingText: "姝e湪鍔犺浇椤甸潰...",
+        menuKeyword: "",
+        menus: [],
+        defaultOpeneds: [],
+        activeMenuKey: "",
+        tabs: [],
+        activeTab: HOME_TAB_CONFIG.url,
+        version: "",
+        versionType: "",
+        licenseDays: null,
+        licenseDialogVisible: false,
+        licenseDialogText: "",
+        fakeVisible: false,
+        fakeRunning: false,
+        fakeStatusInterval: null,
+        userName: localStorage.getItem(USER_STORAGE_KEY) || "绠$悊鍛�",
+        aiLayerIndex: null,
+        aiTipIndex: null
+      };
+    },
+    computed: {
+      asideWidth: function () {
+        return this.isCollapse ? "72px" : "248px";
+      },
+      filteredMenus: function () {
+        var keyword = (this.menuKeyword || "").toLowerCase();
+        var result = [];
+        var i;
+        var j;
+        var group;
+        var items;
+        var item;
+        var subMenu;
+        var groupMatched;
+        var text;
 
-          // 鏇存柊椤佃剼鐗堟湰鏄剧ず
-          $('#system-version').html('Version ' + systemVersion + '<span style="margin-left:5px;padding:1px 6px;font-size:12px;border-radius:3px;background-color:' + versionTypeColor + ';color:#fff;">' + systemVersionType + '</span>');
-
-          // 鎺у埗鍙拌緭鍑虹増鏈俊鎭�
-          console.log('%c 涓壃绔嬪簱骞冲彴 %c ' + systemVersion + ' %c ' + systemVersionType + ' ', 'background-color:rgb(53,73,94);color: #fff;border-radius:2px 0 0 2px;padding:2px 4px;', 'background-color:rgb(25,190,107);color: #fff;padding:2px 4px;font: 9pt "Apercu Regular", Georgia, "Times New Roman", Times, serif;', 'background-color:' + versionTypeColor + ';color: #fff;border-radius:0 2px 2px 0;padding:2px 4px;font: 9pt "Apercu Regular", Georgia, "Times New Roman", Times, serif;');
+        if (!keyword) {
+          return this.menus;
         }
-      }
-    });
-  }
 
-  $(function () {
-    // 娉ㄥ叆AI鍔╂墜鍥炬爣
-    $('#ai-assistant-btn').html(getAiIconHtml(60, 60));
+        for (i = 0; i < this.menus.length; i++) {
+          group = this.menus[i];
+          subMenu = [];
+          groupMatched = (group.menu || "").toLowerCase().indexOf(keyword) > -1;
+          items = group.subMenu || [];
 
-    if ("" === localStorage.getItem('token')) {
-      top.location.href = baseUrl + "/login";
-    }
-
-    // 鍔犺浇鐗堟湰淇℃伅
-    loadSystemVersion();
-  });
-
-  // 鏄剧ず寮圭獥
-  function showPopup(res) {
-    document.getElementById('popup').style.display = 'block';
-    // 鑾峰彇寮瑰嚭绐楀彛鍐呭鐨勫鍣ㄥ厓绱�
-    var popupText = document.getElementById('popup-text');
-    // 鍋囪鍚庡彴杩斿洖鐨勫瓧绗︿覆涓� responseString
-    if (res !== "") {
-      // 鑾峰彇褰撳墠鏃ユ湡
-      const currentDate = new Date();
-      // 鍒涘缓鏂版棩鏈熷璞″苟娣诲姞澶╂暟
-      const newDate = new Date();
-      newDate.setDate(currentDate.getDate() + res + 1);
-      // 灏嗗瓧绗︿覆璁剧疆涓哄脊绐楀唴瀹圭殑鏂囨湰
-      popupText.textContent = "璁稿彲璇佸皢浜�" + new Intl.DateTimeFormat('zh-CN').format(newDate) + "杩囨湡锛屽墿浣欐湁鏁堟湡:" + res + "澶╋紒";
-    } else {
-      document.getElementById('popup').style.display = 'none';
-    }
-  }
-
-  // 闅愯棌寮圭獥
-  function hidePopup() {
-    document.getElementById('popup').style.display = 'none';
-  }
-
-  layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-  }).extend({
-    notice: 'notice/notice',
-  }).use(['index', 'element', 'layer', 'admin', 'notice'], function () {
-    var $ = layui.jquery;
-    var index = layui.index;
-    var element = layui.element;
-    var layer = layui.layer;
-    var admin = layui.admin;
-    var notice = layui.notice;
-
-    var easywebIframeMsg = localStorage.getItem("easyweb-iframe");
-    if (!isEmpty(easywebIframeMsg)) {
-      var easywebIframeObj = JSON.parse(easywebIframeMsg);
-      if (easywebIframeObj.defaultTheme === undefined) {
-        admin.changeTheme("theme-colorful");
-      }
-    }
-
-    let fakeRunning = false
-    let fakeStatusInterval = null
-    function checkFakeStatus() {
-      $.ajax({
-        url: baseUrl + "/openapi/getFakeSystemRunStatus",
-        headers: { 'token': localStorage.getItem('token') },
-        method: 'GET',
-        success: function (res) {
-          if (res.code === 200) {
-            if (res.data.isFake) {
-              $("#fakeShow").show()
-              let running = res.data.running
-              if (running) {
-                $("#fakeShowText").text("浠跨湡妯℃嫙杩愯涓�")
-              } else {
-                $("#fakeShowText").text("浠跨湡妯℃嫙鏈繍琛�")
-              }
-              fakeRunning = running
-              if (!fakeStatusInterval) {
-                fakeStatusInterval = setInterval(checkFakeStatus, 1000);
-              }
-            } else {
-              $("#fakeShow").hide()
-              if (fakeStatusInterval) {
-                clearInterval(fakeStatusInterval);
-                fakeStatusInterval = null;
-              }
+          for (j = 0; j < items.length; j++) {
+            item = items[j];
+            text = ((item.name || "") + " " + (item.code || "")).toLowerCase();
+            if (groupMatched || text.indexOf(keyword) > -1) {
+              subMenu.push(item);
             }
-          } else {
-            top.location.href = baseUrl + "/login";
+          }
+
+          if (subMenu.length > 0) {
+            result.push({
+              menuId: group.menuId,
+              menu: group.menu,
+              menuCode: group.menuCode,
+              subMenu: subMenu
+            });
           }
         }
-      });
-    }
-    checkFakeStatus();
 
-    $("#fakeShow").on("click", function () {
-      if (fakeRunning) {
-        layer.confirm('纭畾瑕佸仠姝豢鐪熸ā鎷熷悧锛�', function (index) {
-          layer.close(index);
-          $.ajax({
-            url: baseUrl + "/openapi/stopFakeSystem",
-            headers: { 'token': localStorage.getItem('token') },
-            method: 'POST',
-            success: function (res) {
-              if (res.code === 200) {
-                layer.msg("浠跨湡妯℃嫙宸插仠姝�", { icon: 1 });
-                $("#fakeShowText").text("浠跨湡妯℃嫙鏈繍琛�")
-              } else {
-                layer.msg(res.msg, { icon: 2 });
-              }
-            }
-          });
-        });
-      } else {
-        layer.confirm('纭畾瑕佸惎鍔ㄤ豢鐪熸ā鎷熷悧锛�', function (index) {
-          layer.close(index);
-          $.ajax({
-            url: baseUrl + "/openapi/startFakeSystem",
-            headers: { 'token': localStorage.getItem('token') },
-            method: 'POST',
-            success: function (res) {
-              if (res.code === 200) {
-                layer.msg("浠跨湡妯℃嫙宸插惎鍔�", { icon: 1 });
-                $("#fakeShowText").text("浠跨湡妯℃嫙杩愯涓�")
-              } else {
-                layer.msg(res.msg, { icon: 2 });
-              }
-            }
-          });
-        });
-      }
-    });
-
-    $.ajax({
-      url: baseUrl + "/menu/auth",
-      headers: { 'token': localStorage.getItem('token') },
-      method: 'POST',
-      // async: false,
-      success: function (res) {
-        // 鍏抽棴鍔犺浇鍔ㄧ敾
-        $('.layuimini-loader').fadeOut();
-        if (res.code === 200) {
-          var tpl = $('#menuTpl').html();
-          var template = Handlebars.compile(tpl);
-          var html = template(res);
-          $("#menu-main").html(html);
-          element.init();
-        } else if (res.code === 403) {
-          top.location.href = baseUrl + "/login";
-        } else {
-          layer.msg(res.msg, { icon: 2 });
+        return result;
+      },
+      activeTabMeta: function () {
+        return this.getTabByName(this.activeTab) || this.createTab(HOME_TAB_CONFIG);
+      },
+      activeTabTitle: function () {
+        return this.activeTabMeta.title;
+      },
+      activeTabUrl: function () {
+        return this.activeTabMeta.url;
+      },
+      versionText: function () {
+        if (!this.version) {
+          return "Version loading...";
         }
-      }
-    });
-
-    $.ajax({
-      url: baseUrl + "/license/getLicenseDays",
-      headers: { 'token': localStorage.getItem('token') },
-      method: 'POST',
-      success: function (res) {
-        if (res.code == 200) {
-          let days = res.data
-          if (days <= 30) {
-            $("#licenseShow").show()
-            $("#licenseDays").html(days)
-          }
-
-          if (days <= 15) {
-            showPopup(days)
-          }
-
-          if (days < 0) {
-            top.location.href = baseUrl + "/login";
-          }
-        } else {
-          top.location.href = baseUrl + "/login";
+        return "Version " + this.version;
+      },
+      versionTypeTag: function () {
+        return (this.versionType || "").toUpperCase();
+      },
+      versionTagType: function () {
+        if (this.versionType === "prd") {
+          return "success";
         }
+        if (this.versionType === "dev") {
+          return "warning";
+        }
+        return "info";
+      },
+      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);
       }
-    });
-
-    // 榛樿鍔犺浇涓婚〉
-    index.loadHome({
-      menuPath: baseUrl + '/views/watch/console.html',
-      menuName: '<i class="layui-icon layui-icon-home"></i>'
-    });
-
-    $('#username').text(localStorage.getItem('username'));
-
-    $(document).on('click', '#logout', function () {
-      window.location.href = "login.html";
-      localStorage.removeItem('token');
-      localStorage.removeItem('username');
-      admin.closeAllTabs();
-    });
-
-    // 鏇挎崲閫�鍑烘寜閽彉閲�
-    var logout = document.getElementById('logout');
-    var url = logout.getAttribute('href');
-    logout.setAttribute('href', baseUrl + "/login");
-
-    // AI鍔╂墜寮圭獥绱㈠紩
-    var aiLayerIndex = null;
-
-    // AI鍔╂墜鍥炬爣鎮诞鎻愮ず
-    $('#ai-assistant-btn').on('mouseenter', function () {
-      this.index = layer.tips('AI鍔╂墜', this, {
-        tips: [1, '#333'], // 涓婃柟鏄剧ず锛屾繁鑹茶儗鏅�
-        time: -1 // 涓嶈嚜鍔ㄥ叧闂�
-      });
-    }).on('mouseleave', function () {
-      layer.close(this.index);
-    }).on('click', function () {
-      // 濡傛灉宸茬粡鎵撳紑杩囦笖鏈攢姣侊紝鐩存帴鏄剧ず
-      if (aiLayerIndex !== null && $('#layui-layer' + aiLayerIndex).length > 0) {
-        var $layero = $('#layui-layer' + aiLayerIndex);
-        var $shade = $('#layui-layer-shade' + 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');
+    },
+    watch: {
+      activeTab: function () {
+        var tab = this.getTabByName(this.activeTab);
+        this.activeMenuKey = tab ? (tab.menuKey || this.findMenuKeyByUrl(tab.url)) : "";
+        this.pageLoading = !!(tab && !tab.loaded);
+        if (this.pageLoading) {
+          this.loadingText = "姝e湪鍔犺浇 鈥�" + tab.title + "鈥� ...";
+        }
+        document.title = (tab ? tab.title : HOME_TAB_CONFIG.title) + " - 娴欐睙涓壃 - 鑷姩鍖栫珛浣撲粨搴� - WCS";
+        this.persistTabs();
+      }
+    },
+    created: function () {
+      if (!localStorage.getItem("token")) {
+        top.location.href = baseUrl + "/login";
         return;
       }
 
-      layer.open({
-        type: 2,
-        title: false, // 闅愯棌榛樿鏍囬鏍忥紝鏇寸畝娲�
-        closeBtn: 0, // 闅愯棌鍏抽棴鎸夐挳锛岀偣鍑婚伄缃╁叧闂�
-        shadeClose: false, // 鏀逛负鎵嬪姩鎺у埗鍏抽棴锛屼互渚挎挱鏀惧姩鐢�
-        shade: 0.1,
-        area: ['600px', '100%'],
-        offset: 'r', // 鍙充晶鎮诞
-        anim: -1, // 绂佺敤榛樿鍔ㄧ敾锛屼娇鐢–SS鍔ㄧ敾
-        isOutAnim: false,
-        skin: 'ai-drawer-layer', // 鑷畾涔夌毊鑲�
-        content: 'ai/diagnosis.html',
-        success: function (layero, index) {
-          aiLayerIndex = index; // 璁板綍绱㈠紩
+      this.restoreTabs();
+      this.installCompatBridge();
+      this.loadSystemVersion();
+      this.loadMenu();
+      this.loadLicenseDays();
+      this.checkFakeStatus();
+      this.startUserSync();
+    },
+    mounted: function () {
+      $("#ai-assistant-btn").html(getAiIconHtml(60, 60));
+      document.title = this.activeTabTitle + " - 娴欐睙涓壃 - 鑷姩鍖栫珛浣撲粨搴� - WCS";
+    },
+    beforeDestroy: function () {
+      if (this.fakeStatusInterval) {
+        clearInterval(this.fakeStatusInterval);
+        this.fakeStatusInterval = null;
+      }
+      if (this.userSyncTimer) {
+        clearInterval(this.userSyncTimer);
+        this.userSyncTimer = null;
+      }
+      if (this.aiTipIndex) {
+        layer.close(this.aiTipIndex);
+        this.aiTipIndex = null;
+      }
+    },
+    methods: {
+      resolveMenuIcon: function (code) {
+        var iconMap = {
+          index: "el-icon-s-home",
+          system: "el-icon-setting",
+          set: "el-icon-s-tools",
+          merchant: "el-icon-user",
+          develop: "el-icon-monitor",
+          stock: "el-icon-box",
+          logReport: "el-icon-document",
+          ioWork: "el-icon-s-operation",
+          workFlow: "el-icon-connection",
+          base: "el-icon-collection-tag",
+          erp: "el-icon-s-order",
+          sensor: "el-icon-cpu"
+        };
+        return iconMap[code] || "el-icon-menu";
+      },
+      createTab: function (config) {
+        return {
+          title: config.title,
+          name: config.url,
+          url: config.url,
+          currentSrc: config.url,
+          home: !!config.home,
+          group: config.group || "",
+          menuKey: config.menuKey || "",
+          loaded: false
+        };
+      },
+      normalizeStoredTab: function (tab) {
+        var created = this.createTab({
+          title: tab.title,
+          url: this.resolveViewSrc(tab.url),
+          home: !!tab.home,
+          group: tab.group || "",
+          menuKey: tab.menuKey || ""
+        });
+        created.loaded = false;
+        return created;
+      },
+      restoreTabs: function () {
+        var homeTab = this.createTab(HOME_TAB_CONFIG);
+        var raw = localStorage.getItem(TAB_STORAGE_KEY);
+        var parsed;
+        var tabs = [];
+        var active;
+        var i;
+        var exists;
 
-          // 鑳屾櫙妯$硦鏁堟灉
-          var shadeId = layero.attr('id').replace('layui-layer', 'layui-layer-shade');
-          var $shade = $('#' + shadeId);
-          $shade.css({
-            'backdrop-filter': 'blur(3px)',
-            'transition': 'opacity 0.8s'
-          });
+        if (raw) {
+          try {
+            parsed = JSON.parse(raw);
+            if (parsed && parsed.tabs && parsed.tabs.length) {
+              for (i = 0; i < parsed.tabs.length; i++) {
+                tabs.push(this.normalizeStoredTab(parsed.tabs[i]));
+              }
+            }
+            active = parsed ? parsed.activeTab : "";
+          } catch (e) {
+            tabs = [];
+            active = "";
+          }
+        }
 
-          // 鐐瑰嚮閬僵鍏抽棴锛堝甫鍔ㄧ敾锛�
-          $shade.on('click', function () {
-            layero.addClass('ai-drawer-layer-close');
-            $shade.css('opacity', 0);
-            setTimeout(function () {
-              // layer.close(index); // 涓嶉攢姣侊紝鏀逛负闅愯棌
-              layero.hide();
-              $shade.hide();
-            }, 400);
+        exists = false;
+        for (i = 0; i < tabs.length; i++) {
+          if (tabs[i].name === homeTab.name) {
+            tabs[i].home = true;
+            tabs[i].title = homeTab.title;
+            exists = true;
+            break;
+          }
+        }
+        if (!exists) {
+          tabs.unshift(homeTab);
+        }
+
+        if (tabs.length === 0) {
+          tabs = [homeTab];
+        }
+
+        this.tabs = tabs;
+        this.activeTab = this.hasTab(active) ? active : homeTab.name;
+        this.loadingText = "姝e湪鍔犺浇 鈥�" + this.activeTabTitle + "鈥� ...";
+        this.pageLoading = true;
+        document.title = this.activeTabTitle + " - 娴欐睙涓壃 - 鑷姩鍖栫珛浣撲粨搴� - WCS";
+      },
+      persistTabs: function () {
+        var tabs = [];
+        var i;
+        for (i = 0; i < this.tabs.length; i++) {
+          tabs.push({
+            title: this.tabs[i].title,
+            url: this.tabs[i].url,
+            home: this.tabs[i].home,
+            group: this.tabs[i].group,
+            menuKey: this.tabs[i].menuKey
           });
         }
-      });
-    });
+        localStorage.setItem(TAB_STORAGE_KEY, JSON.stringify({
+          tabs: tabs,
+          activeTab: this.activeTab
+        }));
+      },
+      hasTab: function (name) {
+        return !!this.getTabByName(name);
+      },
+      getTabByName: function (name) {
+        var i;
+        for (i = 0; i < this.tabs.length; i++) {
+          if (this.tabs[i].name === name) {
+            return this.tabs[i];
+          }
+        }
+        return null;
+      },
+      addOrActivateTab: function (config) {
+        var url = this.resolveViewSrc(config.url);
+        var tab = this.getTabByName(url);
 
+        if (!tab) {
+          tab = this.createTab({
+            title: config.title,
+            url: url,
+            home: !!config.home,
+            group: config.group,
+            menuKey: config.menuKey || this.findMenuKeyByUrl(url)
+          });
+          this.tabs.push(tab);
+        } else {
+          tab.title = config.title || tab.title;
+          tab.group = config.group || tab.group;
+          tab.menuKey = config.menuKey || tab.menuKey;
+          if (config.home) {
+            tab.home = true;
+          }
+        }
+
+        this.loadingText = "姝e湪鍔犺浇 鈥�" + tab.title + "鈥� ...";
+        this.pageLoading = !tab.loaded;
+        this.activeTab = tab.name;
+        this.activeMenuKey = tab.menuKey || this.findMenuKeyByUrl(tab.url);
+      },
+      openHomeTab: function () {
+        this.addOrActivateTab(HOME_TAB_CONFIG);
+      },
+      openProfileTab: function () {
+        this.addOrActivateTab(PROFILE_TAB_CONFIG);
+      },
+      closeAllTabs: function () {
+        this.tabs = [this.createTab(HOME_TAB_CONFIG)];
+        this.activeTab = HOME_TAB_CONFIG.url;
+        this.activeMenuKey = "";
+        this.pageLoading = true;
+        this.loadingText = "姝e湪鍔犺浇 鈥滄帶鍒朵腑蹇冣�� ...";
+        this.persistTabs();
+      },
+      closeOtherTabs: function () {
+        var active = this.getTabByName(this.activeTab);
+        var homeTab = this.createTab(HOME_TAB_CONFIG);
+        var result = [homeTab];
+
+        if (active && active.name !== homeTab.name) {
+          result.push(active);
+        }
+
+        this.tabs = result;
+        this.persistTabs();
+      },
+      removeTab: function (name) {
+        var i;
+        var nextTabName = HOME_TAB_CONFIG.url;
+
+        for (i = 0; i < this.tabs.length; i++) {
+          if (this.tabs[i].name === name) {
+            if (this.activeTab === name) {
+              if (this.tabs[i + 1]) {
+                nextTabName = this.tabs[i + 1].name;
+              } else if (this.tabs[i - 1]) {
+                nextTabName = this.tabs[i - 1].name;
+              }
+            }
+            this.tabs.splice(i, 1);
+            break;
+          }
+        }
+
+        if (this.tabs.length === 0) {
+          this.tabs.push(this.createTab(HOME_TAB_CONFIG));
+          nextTabName = HOME_TAB_CONFIG.url;
+        }
+
+        this.activeTab = nextTabName;
+        this.persistTabs();
+      },
+      handleTabClick: function () {
+        this.activeMenuKey = this.findMenuKeyByUrl(this.activeTabUrl);
+      },
+      handleFrameLoad: function (name) {
+        var tab = this.getTabByName(name);
+        if (tab) {
+          tab.loaded = true;
+        }
+        if (this.activeTab === name) {
+          this.pageLoading = false;
+        }
+      },
+      refreshActiveTab: function () {
+        var tab = this.getTabByName(this.activeTab);
+        if (!tab) {
+          return;
+        }
+        tab.loaded = false;
+        tab.currentSrc = this.addNonce(tab.url);
+        this.loadingText = "姝e湪鍒锋柊 鈥�" + tab.title + "鈥� ...";
+        this.pageLoading = true;
+      },
+      addNonce: function (url) {
+        return url + (url.indexOf("?") === -1 ? "?" : "&") + "_t=" + new Date().getTime();
+      },
+      toggleCollapse: function () {
+        this.isCollapse = !this.isCollapse;
+      },
+      handleMenuSelect: function (group, item) {
+        this.addOrActivateTab({
+          title: item.name,
+          url: item.url,
+          home: false,
+          group: group.menu,
+          menuKey: item.tabKey
+        });
+      },
+      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;
+            }
+          }
+        }
+        return "";
+      },
+      normalizeMenuData: function (data) {
+        var result = [];
+        var i;
+        var j;
+        var group;
+        var item;
+        var subMenu;
+
+        for (i = 0; i < data.length; i++) {
+          group = data[i];
+          subMenu = [];
+
+          for (j = 0; j < (group.subMenu || []).length; j++) {
+            item = group.subMenu[j];
+            subMenu.push({
+              id: item.id,
+              name: item.name || item.code || "鏈懡鍚嶉〉闈�",
+              code: item.code || "",
+              url: this.buildMenuSrc(item.code, item.id),
+              tabKey: this.buildMenuSrc(item.code, item.id)
+            });
+          }
+
+          result.push({
+            menuId: group.menuId,
+            menu: group.menu || "鏈懡鍚嶅垎缁�",
+            menuCode: group.menuCode || "",
+            subMenu: subMenu
+          });
+        }
+
+        return result;
+      },
+      buildMenuSrc: function (code, resourceId) {
+        var normalized = code || "";
+        var hash = "";
+        var hashIndex = normalized.indexOf("#");
+        var resolved;
+
+        if (hashIndex > -1) {
+          hash = normalized.substring(hashIndex);
+          normalized = normalized.substring(0, hashIndex);
+        }
+
+        resolved = this.resolveViewSrc(normalized);
+        if (resourceId && resolved.indexOf("resourceId=") === -1) {
+          resolved += (resolved.indexOf("?") === -1 ? "?" : "&") + "resourceId=" + encodeURIComponent(resourceId);
+        }
+        return resolved + hash;
+      },
+      resolveViewSrc: function (path) {
+        if (!path) {
+          return HOME_TAB_CONFIG.url;
+        }
+        if (/^https?:\/\//.test(path) || path.indexOf(baseUrl) === 0) {
+          return path;
+        }
+        if (path.indexOf("/views/") === 0) {
+          return baseUrl + path;
+        }
+        if (path.indexOf("views/") === 0) {
+          return baseUrl + "/" + path;
+        }
+        return baseUrl + "/views/" + path.replace(/^\/+/, "");
+      },
+      stripTags: function (value) {
+        var div = document.createElement("div");
+        div.innerHTML = value || "";
+        return (div.textContent || div.innerText || "").trim();
+      },
+      collapseAllMenus: function () {
+        var menu = this.$refs.sideMenu;
+        var openedMenus;
+        var i;
+
+        if (!menu || !menu.openedMenus || !menu.openedMenus.length) {
+          return;
+        }
+
+        openedMenus = menu.openedMenus.slice();
+        for (i = 0; i < openedMenus.length; i++) {
+          menu.close(openedMenus[i]);
+        }
+      },
+      loadMenu: function () {
+        var that = this;
+        this.menuLoading = true;
+        $.ajax({
+          url: baseUrl + "/menu/auth",
+          headers: { token: localStorage.getItem("token") },
+          method: "POST",
+          success: function (res) {
+            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();
+              });
+            } else if (res.code === 403) {
+              top.location.href = baseUrl + "/login";
+            } else {
+              that.$message.error(res.msg || "鑿滃崟鍔犺浇澶辫触");
+            }
+          },
+          error: function () {
+            that.menuLoading = false;
+            that.$message.error("鑿滃崟鍔犺浇澶辫触锛岃妫�鏌ユ帴鍙g姸鎬�");
+          }
+        });
+      },
+      loadSystemVersion: function () {
+        var that = this;
+        $.ajax({
+          url: baseUrl + "/openapi/getSystemVersion",
+          headers: { token: localStorage.getItem("token") },
+          method: "GET",
+          success: function (res) {
+            if (res.code === 200) {
+              that.version = res.data.version || "";
+              that.versionType = res.data.versionType || "";
+            }
+          }
+        });
+      },
+      loadLicenseDays: function () {
+        var that = this;
+        $.ajax({
+          url: baseUrl + "/license/getLicenseDays",
+          headers: { token: localStorage.getItem("token") },
+          method: "POST",
+          success: function (res) {
+            var days;
+            if (res.code !== 200) {
+              top.location.href = baseUrl + "/login";
+              return;
+            }
+
+            days = Number(res.data);
+            that.licenseDays = days;
+
+            if (days <= 15) {
+              that.showPopup(days);
+            }
+
+            if (days < 0) {
+              top.location.href = baseUrl + "/login";
+            }
+          }
+        });
+      },
+      showPopup: function (days) {
+        var currentDate;
+        var expiryDate;
+
+        if (days === "" || days === null || typeof days === "undefined") {
+          this.hidePopup();
+          return;
+        }
+
+        currentDate = new Date();
+        expiryDate = new Date();
+        expiryDate.setDate(currentDate.getDate() + Number(days) + 1);
+        this.licenseDialogText = "璁稿彲璇佸皢浜� " + new Intl.DateTimeFormat("zh-CN").format(expiryDate) +
+          " 杩囨湡锛屽墿浣欐湁鏁堟湡锛�" + days + " 澶┿��";
+        this.licenseDialogVisible = true;
+      },
+      hidePopup: function () {
+        this.licenseDialogVisible = false;
+      },
+      checkFakeStatus: function () {
+        var that = this;
+        $.ajax({
+          url: baseUrl + "/openapi/getFakeSystemRunStatus",
+          headers: { token: localStorage.getItem("token") },
+          method: "GET",
+          success: function (res) {
+            if (res.code !== 200) {
+              top.location.href = baseUrl + "/login";
+              return;
+            }
+
+            if (res.data.isFake) {
+              that.fakeVisible = true;
+              that.fakeRunning = !!res.data.running;
+              if (!that.fakeStatusInterval) {
+                that.fakeStatusInterval = setInterval(function () {
+                  that.checkFakeStatus();
+                }, 1000);
+              }
+            } else {
+              that.fakeVisible = false;
+              if (that.fakeStatusInterval) {
+                clearInterval(that.fakeStatusInterval);
+                that.fakeStatusInterval = null;
+              }
+            }
+          }
+        });
+      },
+      toggleFakeSystem: function () {
+        var that = this;
+        var url;
+        var text;
+        var successMsg;
+        var running;
+
+        if (!this.fakeVisible) {
+          return;
+        }
+
+        if (this.fakeRunning) {
+          url = baseUrl + "/openapi/stopFakeSystem";
+          text = "纭畾瑕佸仠姝豢鐪熸ā鎷熷悧锛�";
+          successMsg = "浠跨湡妯℃嫙宸插仠姝�";
+          running = false;
+        } else {
+          url = baseUrl + "/openapi/startFakeSystem";
+          text = "纭畾瑕佸惎鍔ㄤ豢鐪熸ā鎷熷悧锛�";
+          successMsg = "浠跨湡妯℃嫙宸插惎鍔�";
+          running = true;
+        }
+
+        this.$confirm(text, "鎻愮ず", {
+          type: "warning"
+        }).then(function () {
+          $.ajax({
+            url: url,
+            headers: { token: localStorage.getItem("token") },
+            method: "POST",
+            success: function (res) {
+              if (res.code === 200) {
+                that.fakeRunning = running;
+                that.$message.success(successMsg);
+              } else {
+                that.$message.error(res.msg || "鎿嶄綔澶辫触");
+              }
+            }
+          });
+        }).catch(function () {});
+      },
+      toggleFullScreen: function () {
+        if (!document.fullscreenElement) {
+          document.documentElement.requestFullscreen();
+        } else {
+          document.exitFullscreen();
+        }
+      },
+      showAiTip: function () {
+        this.hideAiTip();
+        this.aiTipIndex = layer.tips("AI鍔╂墜", "#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;
+        }
+
+        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);
+            });
+          }
+        });
+      },
+      handleUserCommand: function (command) {
+        if (command === "profile") {
+          this.openProfileTab();
+        } else if (command === "logout") {
+          this.logout();
+        }
+      },
+      logout: function () {
+        localStorage.removeItem("token");
+        localStorage.removeItem(USER_STORAGE_KEY);
+        localStorage.removeItem(TAB_STORAGE_KEY);
+        window.location.href = baseUrl + "/login";
+      },
+      startUserSync: function () {
+        var that = this;
+        this.userSyncTimer = setInterval(function () {
+          that.userName = localStorage.getItem(USER_STORAGE_KEY) || "绠$悊鍛�";
+        }, 1000);
+      },
+      installCompatBridge: function () {
+        var that = this;
+
+        window.admin = window.admin || {};
+        window.admin.closeAllTabs = function () {
+          that.closeAllTabs();
+        };
+        window.admin.changeTheme = window.admin.changeTheme || function () {};
+        window.admin.activeNav = function (url) {
+          that.activeMenuKey = that.findMenuKeyByUrl(that.resolveViewSrc(url));
+        };
+
+        window.index = window.index || {};
+        window.index.loadView = function (param) {
+          var url;
+          if (!param || !param.menuPath) {
+            return;
+          }
+          url = that.resolveViewSrc(param.menuPath);
+          that.addOrActivateTab({
+            title: that.stripTags(param.menuName) || "宸ヤ綔椤甸潰",
+            url: url,
+            home: false,
+            group: "涓氬姟椤甸潰",
+            menuKey: that.findMenuKeyByUrl(url)
+          });
+        };
+        window.index.loadHome = function (param) {
+          var url;
+          if (!param || !param.menuPath) {
+            that.openHomeTab();
+            return;
+          }
+          url = that.resolveViewSrc(param.menuPath);
+          that.addOrActivateTab({
+            title: that.stripTags(param.menuName) || HOME_TAB_CONFIG.title,
+            url: url,
+            home: true,
+            group: HOME_TAB_CONFIG.group,
+            menuKey: that.findMenuKeyByUrl(url)
+          });
+        };
+
+        window.showPopup = function (days) {
+          that.showPopup(days);
+        };
+        window.hidePopup = function () {
+          that.hidePopup();
+        };
+      }
+    }
   });
 </script>
-<script type="text/html" id="menuTpl">
-  {{#each data}}
-  <li class="layui-nav-item">
-    <a><i class="layui-icon {{this.menuIcon}}"></i>&emsp;<cite>{{this.menu}}</cite></a>
-    <dl class="layui-nav-child">
-      {{#each this.subMenu}}
-      <dd><a lay-href="{{this.code}}?resourceId={{this.id}}">{{this.name}}</a></dd>
-      {{/each}}
-    </dl>
-  </li>
-  {{/each}}
-</script>
 </body>
-
-</html>
\ No newline at end of file
+</html>

--
Gitblit v1.9.1