| | |
| | | <!DOCTYPE html> |
| | | <html lang="en"> |
| | | |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <title>浙江中扬 - 自动化立体仓库 - WCS</title> |
| | |
| | | <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); |
| | |
| | | .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> |
| | | |
| | | <!-- 右下角SVG动画 --> |
| | | <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 === 'prd' ? '' : ' (' + systemVersionType + ')'; |
| | | var versionTypeColor = systemVersionType === 'prd' ? 'rgb(25,190,107)' : 'rgb(245,166,35)'; |
| | | new Vue({ |
| | | el: "#app", |
| | | data: function () { |
| | | return { |
| | | isCollapse: false, |
| | | menuLoading: false, |
| | | pageLoading: true, |
| | | loadingText: "正在加载页面...", |
| | | 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 = "正在加载 “" + 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, // 禁用默认动画,使用CSS动画 |
| | | 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 = "正在加载 “" + 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 = "正在加载 “" + 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 = "正在加载 “控制中心” ..."; |
| | | 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 = "正在刷新 “" + 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("菜单加载失败,请检查接口状态"); |
| | | } |
| | | }); |
| | | }, |
| | | 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> <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> |
| | | </html> |