You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

417 lines
11 KiB

<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>