| | |
| | | } |
| | | |
| | | .layout-aside { |
| | | position: relative; |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | border-radius: 16px; |
| | | background: linear-gradient(180deg, #16324d 0%, #0d2237 100%); |
| | | box-shadow: 0 14px 32px rgba(22, 50, 77, 0.18); |
| | | transform: translateZ(0); |
| | | backface-visibility: hidden; |
| | | will-change: width; |
| | | transition: width 0.22s cubic-bezier(0.22, 1, 0.36, 1), |
| | | box-shadow 0.22s ease; |
| | | } |
| | | |
| | | .layout-aside.is-animating { |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .layout-panel { |
| | |
| | | background: #f5f7fa; |
| | | box-shadow: 0 14px 32px rgba(83, 104, 129, 0.12); |
| | | border: 1px solid #e4eaf2; |
| | | transform: translateZ(0); |
| | | backface-visibility: hidden; |
| | | } |
| | | |
| | | .aside-logo { |
| | |
| | | box-sizing: border-box; |
| | | color: #fff; |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.08); |
| | | transition: min-height 0.22s cubic-bezier(0.22, 1, 0.36, 1), |
| | | padding 0.22s cubic-bezier(0.22, 1, 0.36, 1); |
| | | } |
| | | |
| | | .aside-logo-image { |
| | |
| | | max-width: 178px; |
| | | height: auto; |
| | | object-fit: contain; |
| | | transform: translateZ(0); |
| | | transition: max-width 0.22s cubic-bezier(0.22, 1, 0.36, 1); |
| | | } |
| | | |
| | | .aside-logo.is-collapse { |
| | |
| | | .aside-search { |
| | | padding: 12px 12px 8px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .aside-fade-enter-active, |
| | | .aside-fade-leave-active { |
| | | transition: opacity 0.14s ease, transform 0.18s ease; |
| | | } |
| | | |
| | | .aside-fade-enter, |
| | | .aside-fade-leave-to { |
| | | opacity: 0; |
| | | transform: translate3d(-10px, 0, 0); |
| | | } |
| | | |
| | | .aside-search .el-input__inner { |
| | |
| | | .aside-scroll { |
| | | flex: 1; |
| | | min-height: 0; |
| | | transform: translateZ(0); |
| | | } |
| | | |
| | | .aside-scroll .el-scrollbar__wrap { |
| | |
| | | .side-menu { |
| | | border-right: none; |
| | | background: transparent; |
| | | transform: translateZ(0); |
| | | } |
| | | |
| | | .side-menu .el-submenu__title, |
| | |
| | | margin: 0 10px 6px; |
| | | border-radius: 10px; |
| | | box-sizing: border-box; |
| | | backface-visibility: hidden; |
| | | } |
| | | |
| | | .side-menu .el-submenu__title:hover, |
| | |
| | | color: rgba(255, 255, 255, 0.58); |
| | | } |
| | | |
| | | .aside-loading { |
| | | padding: 4px 12px 12px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .aside-skeleton-item { |
| | | position: relative; |
| | | height: 46px; |
| | | margin: 0 10px 6px; |
| | | overflow: hidden; |
| | | border-radius: 10px; |
| | | background: rgba(255, 255, 255, 0.08); |
| | | } |
| | | |
| | | .aside-skeleton-item::after { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | transform: translateX(-100%); |
| | | background: linear-gradient(90deg, |
| | | rgba(255, 255, 255, 0) 0%, |
| | | rgba(255, 255, 255, 0.16) 48%, |
| | | rgba(255, 255, 255, 0) 100%); |
| | | animation: asideSkeletonShimmer 1.25s ease-in-out infinite; |
| | | } |
| | | |
| | | .aside-skeleton-item:nth-child(3n) { |
| | | width: calc(100% - 28px); |
| | | } |
| | | |
| | | .aside-skeleton-item:nth-child(4n + 2) { |
| | | width: calc(100% - 44px); |
| | | } |
| | | |
| | | .aside-skeleton-item:nth-child(5n) { |
| | | width: calc(100% - 36px); |
| | | } |
| | | |
| | | .aside-footer { |
| | | padding: 14px 16px 16px; |
| | | border-top: 1px solid rgba(255, 255, 255, 0.08); |
| | |
| | | justify-content: flex-end; |
| | | gap: 8px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .lang-select { |
| | | width: 160px; |
| | | } |
| | | |
| | | .header-right .el-tag { |
| | |
| | | } |
| | | } |
| | | |
| | | @keyframes asideSkeletonShimmer { |
| | | 100% { |
| | | transform: translateX(100%); |
| | | } |
| | | } |
| | | |
| | | .ai-drawer-layer { |
| | | box-shadow: -8px 0 24px rgba(0, 0, 0, 0.15) !important; |
| | | border-radius: 8px 0 0 8px !important; |
| | |
| | | <body> |
| | | <div id="app" v-cloak> |
| | | <el-container class="shell-container"> |
| | | <el-aside class="layout-aside" :width="asideWidth"> |
| | | <div class="aside-logo" :class="{ 'is-collapse': isCollapse }"> |
| | | <el-aside |
| | | class="layout-aside" |
| | | :class="{ 'is-animating': collapseAnimating }" |
| | | :width="asideWidth"> |
| | | <div class="aside-logo" :class="{ 'is-collapse': asideCompact }"> |
| | | <img class="aside-logo-image" src="../static/images/zy-logo.png" alt="浙江中扬"> |
| | | </div> |
| | | |
| | | <div class="aside-search" v-show="!isCollapse"> |
| | | <el-input |
| | | v-model.trim="menuKeyword" |
| | | size="small" |
| | | clearable |
| | | prefix-icon="el-icon-search" |
| | | placeholder="搜索菜单"> |
| | | </el-input> |
| | | </div> |
| | | |
| | | <el-scrollbar class="aside-scroll" v-loading="menuLoading"> |
| | | <el-menu |
| | | ref="sideMenu" |
| | | class="side-menu" |
| | | :default-active="activeMenuKey" |
| | | :collapse="isCollapse" |
| | | :collapse-transition="false" |
| | | :default-openeds="defaultOpeneds" |
| | | unique-opened |
| | | background-color="transparent" |
| | | text-color="#c6d1df" |
| | | active-text-color="#ffffff"> |
| | | <el-submenu |
| | | v-for="group in filteredMenus" |
| | | :key="'group-' + group.menuId" |
| | | :index="'group-' + group.menuId"> |
| | | <template slot="title"> |
| | | <i :class="resolveMenuIcon(group.menuCode)"></i> |
| | | <span>{{ group.menu }}</span> |
| | | </template> |
| | | <el-menu-item |
| | | v-for="item in group.subMenu" |
| | | :key="item.tabKey" |
| | | :index="item.tabKey" |
| | | @click="handleMenuSelect(group, item)"> |
| | | {{ item.name }} |
| | | </el-menu-item> |
| | | </el-submenu> |
| | | </el-menu> |
| | | |
| | | <div class="aside-empty" v-if="!menuLoading && filteredMenus.length === 0"> |
| | | <el-empty |
| | | :image-size="80" |
| | | :description="menuKeyword ? '没有匹配菜单' : '当前账号没有可用菜单'"> |
| | | </el-empty> |
| | | <transition name="aside-fade"> |
| | | <div class="aside-search" v-show="showAsideExtras"> |
| | | <el-input |
| | | v-model.trim="menuKeyword" |
| | | size="small" |
| | | clearable |
| | | prefix-icon="el-icon-search" |
| | | :placeholder="t('index.searchMenu')"> |
| | | </el-input> |
| | | </div> |
| | | </transition> |
| | | |
| | | <el-scrollbar class="aside-scroll"> |
| | | <div v-if="menuLoading" class="aside-loading" aria-hidden="true"> |
| | | <div |
| | | v-for="n in 8" |
| | | :key="'menu-skeleton-' + n" |
| | | class="aside-skeleton-item"> |
| | | </div> |
| | | </div> |
| | | |
| | | <template v-else> |
| | | <el-menu |
| | | ref="sideMenu" |
| | | class="side-menu" |
| | | :default-active="activeMenuKey" |
| | | :collapse="menuCollapse" |
| | | :collapse-transition="false" |
| | | :default-openeds="defaultOpeneds" |
| | | unique-opened |
| | | background-color="transparent" |
| | | text-color="#c6d1df" |
| | | active-text-color="#ffffff"> |
| | | <el-submenu |
| | | v-for="group in filteredMenus" |
| | | :key="'group-' + group.menuId" |
| | | :index="'group-' + group.menuId"> |
| | | <template slot="title"> |
| | | <i :class="resolveMenuIcon(group.menuCode)"></i> |
| | | <span>{{ group.menu }}</span> |
| | | </template> |
| | | <el-menu-item |
| | | v-for="item in group.subMenu" |
| | | :key="item.tabKey" |
| | | :index="item.tabKey" |
| | | @click="handleMenuSelect(group, item)"> |
| | | {{ item.name }} |
| | | </el-menu-item> |
| | | </el-submenu> |
| | | </el-menu> |
| | | |
| | | <div class="aside-empty" v-if="filteredMenus.length === 0"> |
| | | <el-empty |
| | | :image-size="80" |
| | | :description="menuKeyword ? t('index.noMatchedMenu') : t('index.noAvailableMenu')"> |
| | | </el-empty> |
| | | </div> |
| | | </template> |
| | | </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> |
| | | <transition name="aside-fade"> |
| | | <div class="aside-footer" v-show="showAsideExtras"> |
| | | <div class="aside-footer-copy">© 2026 {{ t('app.company') }}</div> |
| | | <div class="aside-footer-version"> |
| | | <span class="aside-footer-version-text">{{ versionText }}</span> |
| | | <el-tag |
| | | v-if="versionType" |
| | | size="mini" |
| | | :type="versionTagType" |
| | | effect="dark"> |
| | | {{ versionTypeTag }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </transition> |
| | | </el-aside> |
| | | |
| | | <el-container class="layout-panel"> |
| | |
| | | size="mini" |
| | | :type="licenseTagType" |
| | | effect="dark"> |
| | | 临时许可证有效期:{{ licenseDays }}天 |
| | | {{ t('index.licenseDays', [licenseDays]) }} |
| | | </el-tag> |
| | | <el-tag |
| | | v-if="fakeVisible" |
| | |
| | | :type="fakeRunning ? 'danger' : 'info'" |
| | | effect="dark" |
| | | @click.native="toggleFakeSystem"> |
| | | {{ fakeRunning ? '仿真运行中' : '仿真未运行' }} |
| | | {{ fakeRunning ? t('index.fakeRunning') : t('index.fakeStopped') }} |
| | | </el-tag> |
| | | <el-select |
| | | v-model="currentLocale" |
| | | size="mini" |
| | | class="lang-select" |
| | | @change="handleLocaleChange"> |
| | | <el-option |
| | | v-for="item in localeOptions" |
| | | :key="item.tag" |
| | | :label="item.label" |
| | | :value="item.tag"> |
| | | </el-option> |
| | | </el-select> |
| | | <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> |
| | | |
| | |
| | | <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-item command="profile">{{ t('common.profile') }}</el-dropdown-item> |
| | | <el-dropdown-item command="logout" divided>{{ t('common.logout') }}</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </el-dropdown> |
| | | </div> |
| | |
| | | class="page-tabs" |
| | | v-model="activeTab" |
| | | type="card" |
| | | @tab-click="handleTabClick" |
| | | @tab-remove="removeTab"> |
| | | <el-tab-pane |
| | | v-for="tab in tabs" |
| | |
| | | </el-tabs> |
| | | |
| | | <div class="tabs-tools"> |
| | | <el-tooltip content="关闭其他页签" placement="top"> |
| | | <el-tooltip :content="t('common.closeOtherTabs')" placement="top"> |
| | | <el-button size="mini" icon="el-icon-close" @click="closeOtherTabs"></el-button> |
| | | </el-tooltip> |
| | | <el-tooltip content="返回控制中心" placement="top"> |
| | | <el-tooltip :content="t('common.backHome')" placement="top"> |
| | | <el-button size="mini" type="primary" icon="el-icon-house" @click="openHomeTab"></el-button> |
| | | </el-tooltip> |
| | | </div> |
| | |
| | | v-for="tab in tabs" |
| | | :key="'frame-' + tab.name" |
| | | class="page-frame" |
| | | :data-tab-name="tab.name" |
| | | v-show="activeTab === tab.name" |
| | | :src="tab.currentSrc" |
| | | @load="handleFrameLoad(tab.name)"> |
| | |
| | | </el-container> |
| | | |
| | | <el-dialog |
| | | title="许可证即将过期" |
| | | :title="t('index.licenseExpiring')" |
| | | :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> |
| | | <el-button type="primary" @click="licenseDialogVisible = false">{{ t('common.ok') }}</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | |
| | |
| | | |
| | | <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../static/js/layer/layer.js"></script> |
| | | <script type="text/javascript" src="../static/js/common.js"></script> |
| | | <script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></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> |
| | |
| | | group: "账户中心", |
| | | menuKey: "" |
| | | }; |
| | | var SIDEBAR_TRANSITION_MS = 220; |
| | | var SIDEBAR_EXPAND_CONTENT_DELAY = 180; |
| | | var TAB_STORAGE_KEY = "wcs-element-home-tabs"; |
| | | var USER_STORAGE_KEY = "username"; |
| | | var INDEX_I18N_FALLBACKS = { |
| | | "app.title": "浙江中扬 - 自动化立体仓库 - WCS", |
| | | "app.company": "浙江中扬立库技术有限公司", |
| | | "common.loadingPage": "正在加载页面...", |
| | | "common.loadingTab": "正在加载 “{0}” ...", |
| | | "common.refreshingTab": "正在刷新 “{0}” ...", |
| | | "common.ok": "知道了", |
| | | "common.prompt": "提示", |
| | | "common.profile": "基本资料", |
| | | "common.logout": "退出登录", |
| | | "common.closeOtherTabs": "关闭其他页签", |
| | | "common.backHome": "返回控制中心", |
| | | "common.aiAssistant": "AI助手", |
| | | "common.workPage": "工作页面", |
| | | "common.businessPage": "业务页面", |
| | | "index.searchMenu": "搜索菜单", |
| | | "index.noMatchedMenu": "没有匹配菜单", |
| | | "index.noAvailableMenu": "当前账号没有可用菜单", |
| | | "index.licenseDays": "临时许可证有效期:{0}天", |
| | | "index.fakeRunning": "仿真运行中", |
| | | "index.fakeStopped": "仿真未运行", |
| | | "index.licenseExpiring": "许可证即将过期", |
| | | "index.homeTab": "控制中心", |
| | | "index.homeGroup": "实时监控", |
| | | "index.profileGroup": "账户中心", |
| | | "index.versionLoading": "Version loading...", |
| | | "index.licenseExpireAt": "许可证将于 {0} 过期,剩余有效期:{1} 天。", |
| | | "index.confirmStopFake": "确定要停止仿真模拟吗?", |
| | | "index.confirmStartFake": "确定要启动仿真模拟吗?", |
| | | "index.fakeStoppedSuccess": "仿真模拟已停止", |
| | | "index.fakeStartedSuccess": "仿真模拟已启动", |
| | | "index.operationFailed": "操作失败", |
| | | "index.menuLoadFailed": "菜单加载失败", |
| | | "index.menuLoadFailedDetail": "菜单加载失败,请检查接口状态" |
| | | }; |
| | | |
| | | new Vue({ |
| | | el: "#app", |
| | | data: function () { |
| | | return { |
| | | isCollapse: false, |
| | | menuLoading: false, |
| | | asideCompact: false, |
| | | menuCollapse: false, |
| | | showAsideExtras: true, |
| | | collapseAnimating: false, |
| | | collapseTimer: null, |
| | | menuLoading: true, |
| | | pageLoading: true, |
| | | loadingText: "正在加载页面...", |
| | | loadingText: window.WCS_I18N ? window.WCS_I18N.tl("正在加载页面...") : "正在加载页面...", |
| | | menuKeyword: "", |
| | | menus: [], |
| | | defaultOpeneds: [], |
| | |
| | | licenseDays: null, |
| | | licenseDialogVisible: false, |
| | | licenseDialogText: "", |
| | | currentLocale: window.WCS_I18N ? window.WCS_I18N.getLocale() : "zh-CN", |
| | | localeOptions: [], |
| | | fakeVisible: false, |
| | | fakeRunning: false, |
| | | fakeStatusInterval: null, |
| | | userName: localStorage.getItem(USER_STORAGE_KEY) || "管理员", |
| | | menuSyncVersion: 0, |
| | | menuSyncTimer: null, |
| | | userName: localStorage.getItem(USER_STORAGE_KEY) || (window.WCS_I18N ? window.WCS_I18N.tl("管理员") : "管理员"), |
| | | aiLayerIndex: null, |
| | | aiTipIndex: null |
| | | }; |
| | | }, |
| | | computed: { |
| | | asideWidth: function () { |
| | | return this.isCollapse ? "72px" : "248px"; |
| | | return this.asideCompact ? "72px" : "248px"; |
| | | }, |
| | | filteredMenus: function () { |
| | | var keyword = (this.menuKeyword || "").toLowerCase(); |
| | |
| | | return result; |
| | | }, |
| | | activeTabMeta: function () { |
| | | return this.getTabByName(this.activeTab) || this.createTab(HOME_TAB_CONFIG); |
| | | return this.getTabByName(this.activeTab) || this.createTab(this.resolveHomeConfig()); |
| | | }, |
| | | activeTabTitle: function () { |
| | | return this.activeTabMeta.title; |
| | |
| | | }, |
| | | versionText: function () { |
| | | if (!this.version) { |
| | | return "Version loading..."; |
| | | return this.t("index.versionLoading"); |
| | | } |
| | | return "Version " + this.version; |
| | | }, |
| | |
| | | return "info"; |
| | | }, |
| | | userShortName: function () { |
| | | return (this.userName || "管理员").substring(0, 1); |
| | | return (this.userName || this.tl("管理员")).substring(0, 1); |
| | | } |
| | | }, |
| | | watch: { |
| | | activeTab: function () { |
| | | var tab = this.getTabByName(this.activeTab); |
| | | this.activeMenuKey = tab ? (tab.menuKey || this.findMenuKeyByUrl(tab.url)) : ""; |
| | | this.syncMenuStateByUrl(tab ? tab.url : this.resolveHomeConfig().url); |
| | | this.pageLoading = !!(tab && !tab.loaded); |
| | | if (this.pageLoading) { |
| | | this.loadingText = "正在加载 “" + tab.title + "” ..."; |
| | | this.loadingText = this.t("common.loadingTab", [tab.title]); |
| | | } |
| | | document.title = (tab ? tab.title : HOME_TAB_CONFIG.title) + " - 浙江中扬 - 自动化立体仓库 - WCS"; |
| | | this.updateDocumentTitle(tab ? tab.title : this.resolveHomeConfig().title); |
| | | this.persistTabs(); |
| | | } |
| | | }, |
| | |
| | | } |
| | | |
| | | this.restoreTabs(); |
| | | this.bindI18n(); |
| | | this.installCompatBridge(); |
| | | this.loadSystemVersion(); |
| | | this.loadMenu(); |
| | |
| | | }, |
| | | mounted: function () { |
| | | $("#ai-assistant-btn").html(getAiIconHtml(60, 60)); |
| | | document.title = this.activeTabTitle + " - 浙江中扬 - 自动化立体仓库 - WCS"; |
| | | this.updateDocumentTitle(this.activeTabTitle); |
| | | }, |
| | | beforeDestroy: function () { |
| | | if (this.fakeStatusInterval) { |
| | |
| | | clearInterval(this.userSyncTimer); |
| | | this.userSyncTimer = null; |
| | | } |
| | | if (this.menuSyncTimer) { |
| | | clearTimeout(this.menuSyncTimer); |
| | | this.menuSyncTimer = null; |
| | | } |
| | | if (this.collapseTimer) { |
| | | clearTimeout(this.collapseTimer); |
| | | this.collapseTimer = null; |
| | | } |
| | | if (this.aiTipIndex) { |
| | | layer.close(this.aiTipIndex); |
| | | this.aiTipIndex = null; |
| | | } |
| | | }, |
| | | methods: { |
| | | t: function (key, params) { |
| | | var value = window.WCS_I18N ? window.WCS_I18N.t(key, params) : key; |
| | | if (value !== key) { |
| | | return value; |
| | | } |
| | | value = INDEX_I18N_FALLBACKS[key] || key; |
| | | if (!params) { |
| | | return value; |
| | | } |
| | | if (Object.prototype.toString.call(params) === "[object Array]") { |
| | | for (var i = 0; i < params.length; i++) { |
| | | value = value.replace(new RegExp("\\{" + i + "\\}", "g"), params[i]); |
| | | } |
| | | return value; |
| | | } |
| | | return value; |
| | | }, |
| | | tl: function (text) { |
| | | return window.WCS_I18N ? window.WCS_I18N.tl(text) : text; |
| | | }, |
| | | bindI18n: function () { |
| | | var that = this; |
| | | if (!window.WCS_I18N) { |
| | | return; |
| | | } |
| | | window.WCS_I18N.onReady(function (i18n) { |
| | | that.currentLocale = i18n.getLocale(); |
| | | that.localeOptions = i18n.getLocaleOptions(); |
| | | that.refreshI18nState(); |
| | | }); |
| | | }, |
| | | refreshI18nState: function () { |
| | | var that = this; |
| | | var homeConfig = this.resolveHomeConfig(); |
| | | var profileConfig = this.resolveProfileConfig(); |
| | | var i; |
| | | HOME_TAB_CONFIG.title = homeConfig.title; |
| | | HOME_TAB_CONFIG.group = homeConfig.group; |
| | | PROFILE_TAB_CONFIG.title = profileConfig.title; |
| | | PROFILE_TAB_CONFIG.group = profileConfig.group; |
| | | for (i = 0; i < this.tabs.length; i++) { |
| | | if (this.isHomeTabUrl(this.tabs[i].url)) { |
| | | this.tabs[i].title = homeConfig.title; |
| | | this.tabs[i].group = homeConfig.group; |
| | | this.tabs[i].home = true; |
| | | } else if (this.resolveViewSrc(this.tabs[i].url) === this.resolveViewSrc(profileConfig.url)) { |
| | | this.tabs[i].title = profileConfig.title; |
| | | this.tabs[i].group = profileConfig.group; |
| | | } else { |
| | | this.tabs[i].title = this.translateTabTitle(this.tabs[i].title); |
| | | this.tabs[i].group = this.tl(this.tabs[i].group); |
| | | } |
| | | } |
| | | this.updateDocumentTitle(this.activeTabTitle); |
| | | this.persistTabs(); |
| | | this.updateLicenseDialogText(); |
| | | this.$nextTick(function () { |
| | | if (window.WCS_I18N) { |
| | | window.WCS_I18N.apply(document.body); |
| | | } |
| | | that.syncAllFramesI18n(); |
| | | }); |
| | | }, |
| | | handleLocaleChange: function (locale) { |
| | | if (window.WCS_I18N) { |
| | | window.WCS_I18N.setLocale(locale); |
| | | } |
| | | }, |
| | | resolveHomeConfig: function () { |
| | | return { |
| | | title: this.t("index.homeTab"), |
| | | url: HOME_TAB_CONFIG.url, |
| | | home: true, |
| | | group: this.t("index.homeGroup"), |
| | | menuKey: HOME_TAB_CONFIG.menuKey || "" |
| | | }; |
| | | }, |
| | | resolveProfileConfig: function () { |
| | | return { |
| | | title: this.t("common.profile"), |
| | | url: PROFILE_TAB_CONFIG.url, |
| | | home: false, |
| | | group: this.t("index.profileGroup"), |
| | | menuKey: PROFILE_TAB_CONFIG.menuKey || "" |
| | | }; |
| | | }, |
| | | translateTabTitle: function (title) { |
| | | return this.tl(title); |
| | | }, |
| | | updateDocumentTitle: function (title) { |
| | | document.title = title + " - " + this.t("app.title"); |
| | | }, |
| | | resolveMenuIcon: function (code) { |
| | | var iconMap = { |
| | | index: "el-icon-s-home", |
| | |
| | | title: config.title, |
| | | name: config.url, |
| | | url: config.url, |
| | | currentSrc: config.url, |
| | | currentSrc: this.addNonce(config.url), |
| | | home: !!config.home, |
| | | group: config.group || "", |
| | | menuKey: config.menuKey || "", |
| | |
| | | }, |
| | | normalizeStoredTab: function (tab) { |
| | | var created = this.createTab({ |
| | | title: tab.title, |
| | | title: this.translateTabTitle(tab.title), |
| | | url: this.resolveViewSrc(tab.url), |
| | | home: !!tab.home, |
| | | group: tab.group || "", |
| | | group: this.tl(tab.group || ""), |
| | | menuKey: tab.menuKey || "" |
| | | }); |
| | | created.loaded = false; |
| | | return created; |
| | | }, |
| | | restoreTabs: function () { |
| | | var homeTab = this.createTab(HOME_TAB_CONFIG); |
| | | var homeTab = this.createTab(this.resolveHomeConfig()); |
| | | var raw = localStorage.getItem(TAB_STORAGE_KEY); |
| | | var parsed; |
| | | var tabs = []; |
| | |
| | | |
| | | this.tabs = tabs; |
| | | this.activeTab = this.hasTab(active) ? active : homeTab.name; |
| | | this.loadingText = "正在加载 “" + this.activeTabTitle + "” ..."; |
| | | this.loadingText = this.t("common.loadingTab", [this.activeTabTitle]); |
| | | this.pageLoading = true; |
| | | document.title = this.activeTabTitle + " - 浙江中扬 - 自动化立体仓库 - WCS"; |
| | | this.updateDocumentTitle(this.activeTabTitle); |
| | | }, |
| | | persistTabs: function () { |
| | | var tabs = []; |
| | |
| | | }, |
| | | hasTab: function (name) { |
| | | return !!this.getTabByName(name); |
| | | }, |
| | | isHomeTabUrl: function (url) { |
| | | var homeUrl = this.resolveHomeConfig().url; |
| | | return this.resolveViewSrc(url || homeUrl) === this.resolveViewSrc(homeUrl); |
| | | }, |
| | | getTabByName: function (name) { |
| | | var i; |
| | |
| | | } |
| | | } |
| | | |
| | | this.loadingText = "正在加载 “" + tab.title + "” ..."; |
| | | this.loadingText = this.t("common.loadingTab", [tab.title]); |
| | | this.pageLoading = !tab.loaded; |
| | | this.activeTab = tab.name; |
| | | this.activeMenuKey = tab.menuKey || this.findMenuKeyByUrl(tab.url); |
| | | this.syncMenuStateByUrl(tab.url); |
| | | }, |
| | | openHomeTab: function () { |
| | | this.addOrActivateTab(HOME_TAB_CONFIG); |
| | | this.addOrActivateTab(this.resolveHomeConfig()); |
| | | }, |
| | | openProfileTab: function () { |
| | | this.addOrActivateTab(PROFILE_TAB_CONFIG); |
| | | this.addOrActivateTab(this.resolveProfileConfig()); |
| | | }, |
| | | closeAllTabs: function () { |
| | | this.tabs = [this.createTab(HOME_TAB_CONFIG)]; |
| | | this.activeTab = HOME_TAB_CONFIG.url; |
| | | this.activeMenuKey = ""; |
| | | var homeConfig = this.resolveHomeConfig(); |
| | | this.tabs = [this.createTab(homeConfig)]; |
| | | this.activeTab = homeConfig.url; |
| | | this.syncMenuStateByUrl(homeConfig.url); |
| | | this.pageLoading = true; |
| | | this.loadingText = "正在加载 “控制中心” ..."; |
| | | this.loadingText = this.t("common.loadingTab", [homeConfig.title]); |
| | | this.persistTabs(); |
| | | }, |
| | | closeOtherTabs: function () { |
| | | var active = this.getTabByName(this.activeTab); |
| | | var homeTab = this.createTab(HOME_TAB_CONFIG); |
| | | var homeTab = this.createTab(this.resolveHomeConfig()); |
| | | var result = [homeTab]; |
| | | |
| | | if (active && active.name !== homeTab.name) { |
| | |
| | | }, |
| | | removeTab: function (name) { |
| | | var i; |
| | | var nextTabName = HOME_TAB_CONFIG.url; |
| | | var nextTabName = this.resolveHomeConfig().url; |
| | | |
| | | for (i = 0; i < this.tabs.length; i++) { |
| | | if (this.tabs[i].name === name) { |
| | |
| | | } |
| | | |
| | | if (this.tabs.length === 0) { |
| | | this.tabs.push(this.createTab(HOME_TAB_CONFIG)); |
| | | nextTabName = HOME_TAB_CONFIG.url; |
| | | this.tabs.push(this.createTab(this.resolveHomeConfig())); |
| | | nextTabName = this.resolveHomeConfig().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; |
| | | } |
| | | this.syncFrameI18n(name); |
| | | if (this.activeTab === name) { |
| | | this.pageLoading = false; |
| | | } |
| | |
| | | } |
| | | tab.loaded = false; |
| | | tab.currentSrc = this.addNonce(tab.url); |
| | | this.loadingText = "正在刷新 “" + tab.title + "” ..."; |
| | | this.loadingText = this.t("common.refreshingTab", [tab.title]); |
| | | this.pageLoading = true; |
| | | }, |
| | | addNonce: function (url) { |
| | | return url + (url.indexOf("?") === -1 ? "?" : "&") + "_t=" + new Date().getTime(); |
| | | }, |
| | | findFrameElement: function (name) { |
| | | var frames; |
| | | var i; |
| | | if (!this.$el) { |
| | | return null; |
| | | } |
| | | frames = this.$el.querySelectorAll("iframe[data-tab-name]"); |
| | | for (i = 0; i < frames.length; i++) { |
| | | if (frames[i].getAttribute("data-tab-name") === name) { |
| | | return frames[i]; |
| | | } |
| | | } |
| | | return null; |
| | | }, |
| | | syncFrameI18n: function (name) { |
| | | var that = this; |
| | | var frame = this.findFrameElement(name); |
| | | var frameWindow; |
| | | var frameDocument; |
| | | var script; |
| | | |
| | | function applyFrameI18n() { |
| | | try { |
| | | if (!frameWindow.WCS_I18N || typeof frameWindow.WCS_I18N.onReady !== "function") { |
| | | return; |
| | | } |
| | | frameWindow.WCS_I18N.setLocale(that.currentLocale || "zh-CN", false); |
| | | frameWindow.WCS_I18N.onReady(function (i18n) { |
| | | i18n.apply(frameDocument.body || frameDocument.documentElement); |
| | | if (frameDocument.title) { |
| | | frameDocument.title = i18n.tl(frameDocument.title); |
| | | } |
| | | }); |
| | | } catch (e) { |
| | | } |
| | | } |
| | | |
| | | if (!frame) { |
| | | return; |
| | | } |
| | | try { |
| | | frameWindow = frame.contentWindow; |
| | | frameDocument = frameWindow.document; |
| | | } catch (e) { |
| | | return; |
| | | } |
| | | try { |
| | | frameWindow.localStorage.setItem("wcs_lang", this.currentLocale || "zh-CN"); |
| | | } catch (e) { |
| | | } |
| | | if (frameDocument && frameDocument.documentElement) { |
| | | frameDocument.documentElement.lang = this.currentLocale || "zh-CN"; |
| | | } |
| | | if (frameWindow.WCS_I18N && typeof frameWindow.WCS_I18N.onReady === "function") { |
| | | applyFrameI18n(); |
| | | return; |
| | | } |
| | | if (!frameDocument || !frameDocument.head || frameDocument.getElementById("wcs-i18n-bridge-script")) { |
| | | return; |
| | | } |
| | | script = frameDocument.createElement("script"); |
| | | script.id = "wcs-i18n-bridge-script"; |
| | | script.type = "text/javascript"; |
| | | script.src = baseUrl + "/static/js/common.js?v=20260309_i18n_fix1"; |
| | | script.onload = applyFrameI18n; |
| | | frameDocument.head.appendChild(script); |
| | | }, |
| | | syncAllFramesI18n: function () { |
| | | var i; |
| | | for (i = 0; i < this.tabs.length; i++) { |
| | | this.syncFrameI18n(this.tabs[i].name); |
| | | } |
| | | }, |
| | | clearCollapseTimer: function () { |
| | | if (this.collapseTimer) { |
| | | clearTimeout(this.collapseTimer); |
| | | this.collapseTimer = null; |
| | | } |
| | | }, |
| | | nextFrame: function (callback) { |
| | | if (window.requestAnimationFrame) { |
| | | window.requestAnimationFrame(function () { |
| | | callback(); |
| | | }); |
| | | return; |
| | | } |
| | | setTimeout(callback, 16); |
| | | }, |
| | | finishCollapseStage: function (callback, delay) { |
| | | var that = this; |
| | | this.clearCollapseTimer(); |
| | | this.collapseTimer = setTimeout(function () { |
| | | that.collapseTimer = null; |
| | | callback(); |
| | | }, delay); |
| | | }, |
| | | toggleCollapse: function () { |
| | | this.isCollapse = !this.isCollapse; |
| | | var that = this; |
| | | |
| | | if (this.collapseAnimating) { |
| | | return; |
| | | } |
| | | |
| | | this.collapseAnimating = true; |
| | | |
| | | if (this.isCollapse) { |
| | | this.isCollapse = false; |
| | | this.asideCompact = false; |
| | | this.nextFrame(function () { |
| | | that.finishCollapseStage(function () { |
| | | that.menuCollapse = false; |
| | | that.showAsideExtras = true; |
| | | that.collapseAnimating = false; |
| | | that.$nextTick(function () { |
| | | that.syncMenuStateByUrl(that.activeTabUrl); |
| | | }); |
| | | }, SIDEBAR_EXPAND_CONTENT_DELAY); |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | this.isCollapse = true; |
| | | this.showAsideExtras = false; |
| | | this.menuCollapse = true; |
| | | this.$nextTick(function () { |
| | | that.nextFrame(function () { |
| | | that.asideCompact = true; |
| | | that.finishCollapseStage(function () { |
| | | that.collapseAnimating = false; |
| | | }, SIDEBAR_TRANSITION_MS); |
| | | }); |
| | | }); |
| | | }, |
| | | handleMenuSelect: function (group, item) { |
| | | this.addOrActivateTab({ |
| | |
| | | } |
| | | return ""; |
| | | }, |
| | | findMenuGroupIndexByUrl: function (url) { |
| | | var normalized = this.resolveViewSrc(url); |
| | | var i; |
| | | var j; |
| | | var group; |
| | | var item; |
| | | |
| | | for (i = 0; i < this.menus.length; i++) { |
| | | group = this.menus[i]; |
| | | for (j = 0; j < group.subMenu.length; j++) { |
| | | item = group.subMenu[j]; |
| | | if (item.url === normalized) { |
| | | return "group-" + group.menuId; |
| | | } |
| | | } |
| | | } |
| | | return ""; |
| | | }, |
| | | normalizeMenuData: function (data) { |
| | | var result = []; |
| | | var i; |
| | |
| | | item = group.subMenu[j]; |
| | | subMenu.push({ |
| | | id: item.id, |
| | | name: item.name || item.code || "未命名页面", |
| | | name: item.name || item.code || this.tl("未命名页面"), |
| | | 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 || "未命名分组", |
| | | menu: group.menu || this.tl("未命名分组"), |
| | | menuCode: group.menuCode || "", |
| | | subMenu: subMenu |
| | | }); |
| | |
| | | }, |
| | | resolveViewSrc: function (path) { |
| | | if (!path) { |
| | | return HOME_TAB_CONFIG.url; |
| | | return this.resolveHomeConfig().url; |
| | | } |
| | | if (/^https?:\/\//.test(path) || path.indexOf(baseUrl) === 0) { |
| | | return path; |
| | |
| | | menu.close(openedMenus[i]); |
| | | } |
| | | }, |
| | | syncMenuStateByUrl: function (url) { |
| | | var targetUrl = this.resolveViewSrc(url || this.resolveHomeConfig().url); |
| | | var activeMenuKey = ""; |
| | | var groupIndex = ""; |
| | | var that = this; |
| | | var syncVersion; |
| | | var applyMenuState; |
| | | |
| | | if (!this.isHomeTabUrl(targetUrl)) { |
| | | activeMenuKey = this.findMenuKeyByUrl(targetUrl); |
| | | if (activeMenuKey) { |
| | | groupIndex = this.findMenuGroupIndexByUrl(targetUrl); |
| | | } |
| | | } |
| | | |
| | | this.activeMenuKey = activeMenuKey; |
| | | this.defaultOpeneds = groupIndex ? [groupIndex] : []; |
| | | this.menuSyncVersion += 1; |
| | | syncVersion = this.menuSyncVersion; |
| | | applyMenuState = function () { |
| | | var menu = that.$refs.sideMenu; |
| | | var openedMenus; |
| | | if (syncVersion !== that.menuSyncVersion) { |
| | | return; |
| | | } |
| | | if (!menu) { |
| | | return; |
| | | } |
| | | if (that.menuCollapse) { |
| | | return; |
| | | } |
| | | openedMenus = menu.openedMenus ? menu.openedMenus.slice() : []; |
| | | if (!groupIndex) { |
| | | if (openedMenus.length) { |
| | | that.collapseAllMenus(); |
| | | } |
| | | return; |
| | | } |
| | | if (openedMenus.indexOf(groupIndex) > -1) { |
| | | return; |
| | | } |
| | | if (openedMenus.length) { |
| | | that.collapseAllMenus(); |
| | | } |
| | | menu.open(groupIndex); |
| | | }; |
| | | |
| | | this.$nextTick(function () { |
| | | applyMenuState(); |
| | | if (that.menuSyncTimer) { |
| | | clearTimeout(that.menuSyncTimer); |
| | | that.menuSyncTimer = null; |
| | | } |
| | | that.menuSyncTimer = setTimeout(function () { |
| | | applyMenuState(); |
| | | that.menuSyncTimer = null; |
| | | }, 160); |
| | | }); |
| | | }, |
| | | loadMenu: function () { |
| | | var that = this; |
| | | this.menuLoading = true; |
| | |
| | | that.menuLoading = false; |
| | | if (res.code === 200) { |
| | | that.menus = that.normalizeMenuData(res.data || []); |
| | | that.defaultOpeneds = []; |
| | | that.activeMenuKey = that.findMenuKeyByUrl(that.activeTabUrl); |
| | | that.$nextTick(function () { |
| | | that.collapseAllMenus(); |
| | | }); |
| | | that.syncMenuStateByUrl(that.activeTabUrl); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/login"; |
| | | } else { |
| | | that.$message.error(res.msg || "菜单加载失败"); |
| | | that.$message.error(res.msg || that.t("index.menuLoadFailed")); |
| | | } |
| | | }, |
| | | error: function () { |
| | | that.menuLoading = false; |
| | | that.$message.error("菜单加载失败,请检查接口状态"); |
| | | that.$message.error(that.t("index.menuLoadFailedDetail")); |
| | | } |
| | | }); |
| | | }, |
| | |
| | | showPopup: function (days) { |
| | | var currentDate; |
| | | var expiryDate; |
| | | var formattedDate; |
| | | |
| | | if (days === "" || days === null || typeof days === "undefined") { |
| | | this.hidePopup(); |
| | |
| | | currentDate = new Date(); |
| | | expiryDate = new Date(); |
| | | expiryDate.setDate(currentDate.getDate() + Number(days) + 1); |
| | | this.licenseDialogText = "许可证将于 " + new Intl.DateTimeFormat("zh-CN").format(expiryDate) + |
| | | " 过期,剩余有效期:" + days + " 天。"; |
| | | formattedDate = new Intl.DateTimeFormat(this.currentLocale || "zh-CN").format(expiryDate); |
| | | this.licenseDialogText = this.t("index.licenseExpireAt", [formattedDate, days]); |
| | | this.licenseDialogVisible = true; |
| | | }, |
| | | updateLicenseDialogText: function () { |
| | | if (this.licenseDays === "" || this.licenseDays === null || typeof this.licenseDays === "undefined" || this.licenseDays > 15) { |
| | | return; |
| | | } |
| | | this.showPopup(this.licenseDays); |
| | | }, |
| | | hidePopup: function () { |
| | | this.licenseDialogVisible = false; |
| | |
| | | |
| | | if (this.fakeRunning) { |
| | | url = baseUrl + "/openapi/stopFakeSystem"; |
| | | text = "确定要停止仿真模拟吗?"; |
| | | successMsg = "仿真模拟已停止"; |
| | | text = this.t("index.confirmStopFake"); |
| | | successMsg = this.t("index.fakeStoppedSuccess"); |
| | | running = false; |
| | | } else { |
| | | url = baseUrl + "/openapi/startFakeSystem"; |
| | | text = "确定要启动仿真模拟吗?"; |
| | | successMsg = "仿真模拟已启动"; |
| | | text = this.t("index.confirmStartFake"); |
| | | successMsg = this.t("index.fakeStartedSuccess"); |
| | | running = true; |
| | | } |
| | | |
| | | this.$confirm(text, "提示", { |
| | | this.$confirm(text, this.t("common.prompt"), { |
| | | type: "warning" |
| | | }).then(function () { |
| | | $.ajax({ |
| | |
| | | that.fakeRunning = running; |
| | | that.$message.success(successMsg); |
| | | } else { |
| | | that.$message.error(res.msg || "操作失败"); |
| | | that.$message.error(res.msg || that.t("index.operationFailed")); |
| | | } |
| | | } |
| | | }); |
| | |
| | | }, |
| | | showAiTip: function () { |
| | | this.hideAiTip(); |
| | | this.aiTipIndex = layer.tips("AI助手", "#ai-assistant-btn", { |
| | | this.aiTipIndex = layer.tips(this.t("common.aiAssistant"), "#ai-assistant-btn", { |
| | | tips: [1, "#333"], |
| | | time: -1 |
| | | }); |
| | |
| | | startUserSync: function () { |
| | | var that = this; |
| | | this.userSyncTimer = setInterval(function () { |
| | | that.userName = localStorage.getItem(USER_STORAGE_KEY) || "管理员"; |
| | | that.userName = localStorage.getItem(USER_STORAGE_KEY) || that.tl("管理员"); |
| | | }, 1000); |
| | | }, |
| | | installCompatBridge: function () { |
| | |
| | | }; |
| | | window.admin.changeTheme = window.admin.changeTheme || function () {}; |
| | | window.admin.activeNav = function (url) { |
| | | that.activeMenuKey = that.findMenuKeyByUrl(that.resolveViewSrc(url)); |
| | | that.syncMenuStateByUrl(that.resolveViewSrc(url)); |
| | | }; |
| | | |
| | | window.index = window.index || {}; |
| | |
| | | } |
| | | url = that.resolveViewSrc(param.menuPath); |
| | | that.addOrActivateTab({ |
| | | title: that.stripTags(param.menuName) || "工作页面", |
| | | title: that.stripTags(param.menuName) || that.t("common.workPage"), |
| | | url: url, |
| | | home: false, |
| | | group: "业务页面", |
| | | group: that.t("common.businessPage"), |
| | | menuKey: that.findMenuKeyByUrl(url) |
| | | }); |
| | | }; |
| | |
| | | } |
| | | url = that.resolveViewSrc(param.menuPath); |
| | | that.addOrActivateTab({ |
| | | title: that.stripTags(param.menuName) || HOME_TAB_CONFIG.title, |
| | | title: that.stripTags(param.menuName) || that.resolveHomeConfig().title, |
| | | url: url, |
| | | home: true, |
| | | group: HOME_TAB_CONFIG.group, |
| | | group: that.resolveHomeConfig().group, |
| | | menuKey: that.findMenuKeyByUrl(url) |
| | | }); |
| | | }; |