<template>
|
|
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
|
|
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
|
<!-- Desktop keeps the vertical navigation visible alongside the new horizontal bar. -->
|
|
<sidebar
|
|
v-if="device !== 'mobile' || sidebar.opened"
|
|
class="sidebar-container"
|
|
:class="{ 'sidebar-wide': sidebarWide }"
|
|
@menu-depth-change="setSidebarWide"
|
|
/>
|
|
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
|
|
<div class="layout-header" :class="{'fixed-header':fixedHeader}">
|
|
<navbar />
|
|
<!-- 横向导航条:位置始终保留;仅从汇聚门户跳转进入时显示导航按钮 -->
|
|
<div class="module-subbar">
|
|
<div class="module-links">
|
|
<button
|
|
v-for="item in moduleLinks"
|
|
:key="item.key"
|
|
type="button"
|
|
class="module-link"
|
|
:class="{ active: activeModule === item.key }"
|
|
@click="goModule(item.path)"
|
|
>{{ item.label }}</button>
|
|
</div>
|
|
<!-- 汇聚态站名筛选:接口与首页一致(/agg/station/all),默认全部场站 -->
|
|
<el-select
|
|
v-if="fromPortal"
|
|
v-model="stationCode"
|
|
class="module-station-select"
|
|
size="mini"
|
|
placeholder="全部场站"
|
|
clearable
|
|
>
|
|
<el-option label="全部场站" value="" />
|
|
<el-option
|
|
v-for="station in stationOptions"
|
|
:key="station.stationCode"
|
|
:label="station.stationName"
|
|
:value="station.stationCode"
|
|
/>
|
|
</el-select>
|
|
</div>
|
|
<tags-view v-if="needTagsView"/>
|
|
</div>
|
|
<app-main :class="{hasTagsView: needTagsView, patrolWorkspace: isPatrolWorkspace}" />
|
|
<right-panel v-if="!isPatrolWorkspace">
|
|
<settings/>
|
|
</right-panel>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import RightPanel from '@/components/RightPanel'
|
|
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
|
|
import ResizeMixin from './mixin/ResizeHandler'
|
|
import { mapState, mapGetters } from 'vuex'
|
|
import variables from '@/assets/styles/variables.scss'
|
|
import axios from 'axios'
|
|
import { getStationList } from '@/api/portal'
|
|
|
|
export default {
|
|
name: 'Layout',
|
|
components: {
|
|
AppMain,
|
|
Navbar,
|
|
RightPanel,
|
|
Settings,
|
|
Sidebar,
|
|
TagsView
|
|
},
|
|
data() {
|
|
return {
|
|
// 侧边栏宽度档位:0=160px,1=200px,2=240px,3=280px,随子菜单展开层级加深
|
|
sidebarWidthLevel: 0,
|
|
// 汇聚态站名筛选选项
|
|
stationOptions: [],
|
|
stationRequestSource: null
|
|
}
|
|
},
|
|
mixins: [ResizeMixin],
|
|
computed: {
|
|
...mapState({
|
|
theme: state => state.settings.theme,
|
|
sideTheme: state => state.settings.sideTheme,
|
|
sidebar: state => state.app.sidebar,
|
|
device: state => state.app.device,
|
|
needTagsView: state => state.settings.tagsView,
|
|
fixedHeader: state => state.settings.fixedHeader
|
|
}),
|
|
classObj() {
|
|
return {
|
|
hideSidebar: !this.sidebar.opened,
|
|
openSidebar: this.sidebar.opened,
|
|
withoutAnimation: this.sidebar.withoutAnimation,
|
|
mobile: this.device === 'mobile',
|
|
patrolWorkspace: this.isPatrolWorkspace,
|
|
sidebarWide: this.sidebarWidthLevel >= 1,
|
|
sidebarWide2: this.sidebarWidthLevel >= 2,
|
|
sidebarWide3: this.sidebarWidthLevel >= 3
|
|
}
|
|
},
|
|
isPatrolWorkspace() {
|
|
return this.$route.matched.some(route => route.meta && route.meta.patrolWorkspace)
|
|
},
|
|
// 侧边栏宽度档位 ≥1 时给侧边栏容器加宽(修复模板引用未定义属性的告警)
|
|
sidebarWide() {
|
|
return this.sidebarWidthLevel >= 1
|
|
},
|
|
...mapGetters(['fromPortal', 'workspaceStationCode']),
|
|
// 选中值存于 store(sessionStorage 持久化),跨页面保持
|
|
stationCode: {
|
|
get() {
|
|
return this.workspaceStationCode || ''
|
|
},
|
|
set(value) {
|
|
this.$store.dispatch('siteContext/setWorkspaceStation', value)
|
|
}
|
|
},
|
|
// 从门户跳转进入时展示:首页回门户,动环巡检为当前单站系统
|
|
moduleLinks() {
|
|
if (!this.fromPortal) return []
|
|
return [
|
|
{ key: 'home', label: '首页', path: '/portal' },
|
|
{ key: 'patrol', label: '动环巡检', path: '/index' },
|
|
// { key: 'system', label: '系统管理', path: '/system/user' }
|
|
// { key: 'system', label: '系统管理', path: '' }
|
|
]
|
|
},
|
|
activeModule() {
|
|
if (this.$route.path.startsWith('/system')) return 'system'
|
|
return 'patrol'
|
|
},
|
|
variables() {
|
|
return variables;
|
|
}
|
|
},
|
|
mounted() {
|
|
if (this.fromPortal) this.loadStationOptions()
|
|
},
|
|
beforeDestroy() {
|
|
this.cancelStationOptions()
|
|
},
|
|
methods: {
|
|
cancelStationOptions() {
|
|
if (!this.stationRequestSource) return
|
|
this.stationRequestSource.cancel('layout station list cancelled')
|
|
this.stationRequestSource = null
|
|
},
|
|
// 站名下拉选项:与汇聚首页同款接口
|
|
loadStationOptions() {
|
|
this.cancelStationOptions()
|
|
this.stationRequestSource = axios.CancelToken.source()
|
|
getStationList({}, { cancelToken: this.stationRequestSource.token }).then(response => {
|
|
this.stationRequestSource = null
|
|
this.stationOptions = Array.isArray(response && response.data) ? response.data : []
|
|
}).catch((error) => {
|
|
if (axios.isCancel(error)) return
|
|
this.stationRequestSource = null
|
|
this.stationOptions = []
|
|
})
|
|
},
|
|
goModule(path) {
|
|
if (this.$route.path !== path) this.$router.push({ path }).catch(() => undefined)
|
|
},
|
|
setSidebarWide(level) {
|
|
this.sidebarWidthLevel = Number(level) || 0
|
|
},
|
|
handleClickOutside() {
|
|
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "~@/assets/styles/mixin.scss";
|
|
@import "~@/assets/styles/variables.scss";
|
|
|
|
.app-wrapper {
|
|
@include clearfix;
|
|
position: relative;
|
|
height: 100%;
|
|
width: 100%;
|
|
|
|
&.mobile.openSidebar {
|
|
position: fixed;
|
|
top: 0;
|
|
}
|
|
}
|
|
|
|
.drawer-bg {
|
|
background: #000;
|
|
opacity: 0.3;
|
|
width: 100%;
|
|
top: 0;
|
|
height: 100%;
|
|
position: absolute;
|
|
z-index: 999;
|
|
}
|
|
|
|
.layout-header {
|
|
margin-left: -$base-sidebar-width;
|
|
width: calc(100% + #{$base-sidebar-width});
|
|
|
|
.tags-view-container {
|
|
width: calc(100% - #{$base-sidebar-width});
|
|
margin-left: $base-sidebar-width;
|
|
transition: width 0.28s, margin-left 0.28s;
|
|
}
|
|
}
|
|
|
|
.module-subbar {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 40px;
|
|
padding: 2px 18px;
|
|
box-sizing: border-box;
|
|
background: linear-gradient(180deg, #151b5b 0%, #111753 58%, #0e1244 100%);
|
|
// Keep the lower edge soft while replacing the former hard top line with a fade.
|
|
box-shadow: inset 0 -1px 0 rgba(0, 13, 63, .55);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.module-subbar::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 6px;
|
|
pointer-events: none;
|
|
background: linear-gradient(180deg, rgba(1, 8, 27, .62) 0%, rgba(5, 18, 52, .28) 48%, rgba(5, 18, 52, 0) 100%);
|
|
}
|
|
|
|
.module-links {
|
|
height: 36px;
|
|
margin-left:120px;
|
|
display: flex;
|
|
align-items: stretch;
|
|
}
|
|
|
|
/* 汇聚态站名筛选:靠行最右 */
|
|
.module-station-select {
|
|
width: 150px;
|
|
|
|
::v-deep .el-input__inner {
|
|
height: 28px;
|
|
line-height: 28px;
|
|
background: rgba(16, 30, 84, 0.65);
|
|
border-color: rgba(84, 130, 255, 0.35);
|
|
color: #eef4ff;
|
|
font-size: 12px;
|
|
|
|
&::placeholder {
|
|
color: rgba(159, 180, 232, 0.65);
|
|
}
|
|
}
|
|
|
|
::v-deep .el-input__suffix {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 28px;
|
|
}
|
|
|
|
::v-deep .el-input__icon {
|
|
line-height: 28px;
|
|
}
|
|
}
|
|
|
|
.module-link {
|
|
position: relative;
|
|
min-width: 90px;
|
|
height: 36px;
|
|
padding: 0 24px;
|
|
border: 0;
|
|
border-right: 0;
|
|
color: #c9dcf8;
|
|
background: transparent;
|
|
font: inherit;
|
|
font-size: 14px;
|
|
font-weight: 900;
|
|
letter-spacing: 1px;
|
|
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
line-height: 36px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: color .2s, background-color .2s;
|
|
}
|
|
|
|
.module-link:not(:last-child)::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 11%;
|
|
right: 0;
|
|
width: 1px;
|
|
height: 78%;
|
|
background: linear-gradient(180deg, rgba(90, 133, 197, 0) 0%, rgba(90, 133, 197, .42) 50%, rgba(90, 133, 197, 0) 100%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.module-link:hover {
|
|
color: #54ffff;
|
|
background: transparent;
|
|
}
|
|
|
|
.module-link.active {
|
|
position: relative;
|
|
color: #3FFFFE;
|
|
font-size: 14px;
|
|
letter-spacing: 1px;
|
|
font-weight: 900;
|
|
// 选中聚光灯:自下而上的光晕(静态,无呼吸动画)
|
|
background: radial-gradient(ellipse 90% 78% at 50% 108%, rgba(63, 255, 254, 0.28), rgba(63, 255, 254, 0.08) 52%, transparent 76%);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.module-link.active::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 14%;
|
|
right: 14%;
|
|
bottom: 0;
|
|
height: 2px;
|
|
border-radius: 2px;
|
|
background: linear-gradient(90deg, rgba(63, 255, 254, 0) 0%, #3ffef0 50%, rgba(63, 255, 254, 0) 100%);
|
|
box-shadow: 0 0 14px rgba(63, 255, 254, 0.85);
|
|
}
|
|
|
|
.fixed-header {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 1002;
|
|
margin-left: 0;
|
|
width: 100%;
|
|
transition: none;
|
|
}
|
|
|
|
.hideSidebar .layout-header {
|
|
margin-left: -54px;
|
|
width: calc(100% + 54px);
|
|
|
|
.tags-view-container {
|
|
width: calc(100% - 54px);
|
|
margin-left: 54px;
|
|
}
|
|
|
|
}
|
|
|
|
.sidebarWide .layout-header {
|
|
margin-left: -$base-sidebar-wide-width;
|
|
width: calc(100% + #{$base-sidebar-wide-width});
|
|
|
|
.tags-view-container {
|
|
width: calc(100% - #{$base-sidebar-wide-width});
|
|
margin-left: $base-sidebar-wide-width;
|
|
}
|
|
}
|
|
|
|
.sidebarWide2 .layout-header {
|
|
margin-left: -$base-sidebar-wide2-width;
|
|
width: calc(100% + #{$base-sidebar-wide2-width});
|
|
|
|
.tags-view-container {
|
|
width: calc(100% - #{$base-sidebar-wide2-width});
|
|
margin-left: $base-sidebar-wide2-width;
|
|
}
|
|
}
|
|
|
|
.sidebarWide3 .layout-header {
|
|
margin-left: -$base-sidebar-wide3-width;
|
|
width: calc(100% + #{$base-sidebar-wide3-width});
|
|
|
|
.tags-view-container {
|
|
width: calc(100% - #{$base-sidebar-wide3-width});
|
|
margin-left: $base-sidebar-wide3-width;
|
|
}
|
|
}
|
|
|
|
.hideSidebar .module-links {
|
|
margin-left: 54px;
|
|
}
|
|
|
|
.hideSidebar .fixed-header {
|
|
margin-left: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.sidebarHide .layout-header,
|
|
.mobile .layout-header {
|
|
margin-left: 0;
|
|
width: 100%;
|
|
|
|
.tags-view-container {
|
|
width: 100%;
|
|
margin-left: 0;
|
|
}
|
|
|
|
}
|
|
|
|
.sidebarHide .module-links,
|
|
.mobile .module-links {
|
|
margin-left: 12px;
|
|
}
|
|
|
|
.app-wrapper.patrolWorkspace {
|
|
::v-deep .sidebar-container {
|
|
top: 0 !important;
|
|
height: 100% !important;
|
|
}
|
|
}
|
|
</style>
|