添加行车静态页面;添加菜单
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 621 B |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 382 KiB |
|
After Width: | Height: | Size: 230 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 422 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 279 B |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 307 KiB |
|
|
@ -21,4 +21,4 @@ app.use(ElementPlus)
|
|||
|
||||
app.mount('#app')
|
||||
|
||||
globalScale()
|
||||
// globalScale()
|
||||
|
|
|
|||
|
|
@ -33,6 +33,18 @@ const routes = [
|
|||
name: 'Traffic',
|
||||
component: () => import('@/views/traffic/index.vue'),
|
||||
meta: { title: '客运', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'driving',
|
||||
name: 'Driving',
|
||||
component: () => import('@/views/driving/index.vue'),
|
||||
meta: { title: '行车', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'construction',
|
||||
name: 'Construction',
|
||||
component: () => import('@/views/construction/index.vue'),
|
||||
meta: { title: '施工', requiresAuth: true }
|
||||
}
|
||||
],
|
||||
meta: { requiresAuth: true }
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ html, body, #app {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
// overflow: scroll;
|
||||
overflow: scroll;
|
||||
font-family: 'Alibaba PuHuiTi 3.0', 'Microsoft YaHei', 'PingFang SC', sans-serif;
|
||||
background: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<div class="construction-container" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.construction-container {
|
||||
width: 4800px;
|
||||
height: 1514px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
<template>
|
||||
<section class="driving-1">
|
||||
<div class="column-select">
|
||||
<MetroLineSelect v-model="activeLine" />
|
||||
</div>
|
||||
|
||||
<div class="vehicle-table">
|
||||
<div class="table-header table-grid">
|
||||
<span>车站</span>
|
||||
<span>图号</span>
|
||||
<span>用车数</span>
|
||||
<span>当前计划用车数</span>
|
||||
<span>行车间隔</span>
|
||||
</div>
|
||||
|
||||
<el-scrollbar class="table-body">
|
||||
<div class="table-body-inner">
|
||||
<div v-for="item in vehicles" :key="item.id" class="table-row table-grid">
|
||||
<span class="station">{{ item.station }}</span>
|
||||
<span class="diagram">{{ item.diagram }}</span>
|
||||
<span class="vehicles">{{ item.vehicles }}</span>
|
||||
<span class="plan">
|
||||
{{ item.plan }}<b :class="item.change > 0 ? 'rise' : 'fall'">{{ formatChange(item.change) }}</b>
|
||||
</span>
|
||||
<el-popover placement="top" trigger="hover" :width="560" :offset="34" :fallback-placements="[]"
|
||||
:teleported="false" :show-after="150" :hide-after="80" popper-class="driving-row-popover">
|
||||
<template #reference>
|
||||
<div class="intervals">
|
||||
<span class="interval-route">{{ item.interval.section }}</span>
|
||||
<div class="interval-directions">
|
||||
<div class="interval-direction">
|
||||
<img src="/imgs/driving/1-item-1.png" alt="上行" />
|
||||
<span>{{ item.interval.up }}</span>
|
||||
</div>
|
||||
<div class="interval-direction">
|
||||
<img src="/imgs/driving/1-item-2.png" alt="下行" />
|
||||
<span>{{ item.interval.down }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="row-popover-content">
|
||||
<div class="popover-overview">{{ intervalPopover.overview }}</div>
|
||||
<div v-for="row in intervalPopover.rows" :key="row.section" class="popover-interval">
|
||||
<span class="popover-section">{{ row.section }}</span>
|
||||
<span class="popover-time"><img src="/imgs/driving/1-item-1.png" alt="上行" />{{ row.up }}</span>
|
||||
<span class="popover-time"><img src="/imgs/driving/1-item-2.png" alt="下行" />{{ row.down }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import MetroLineSelect from './MetroLineSelect.vue'
|
||||
|
||||
const activeLine = ref(7)
|
||||
|
||||
const vehicles = [
|
||||
{ id: 1, station: '上海南站', diagram: '1706-2', vehicles: 54, plan: 4, change: 2, interval: { section: '东川路站~奉贤新城站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 2, station: '石龙路', diagram: '1706-2', vehicles: 54, plan: 4, change: 1, interval: { section: '嘉定新城站~南翔站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 3, station: '龙漕路', diagram: '1706-2', vehicles: 54, plan: 4, change: -1, interval: { section: '三林站~罗山路站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 4, station: '漕溪路', diagram: '1706-2', vehicles: 54, plan: 4, change: 2, interval: { section: '花桥站~嘉定新城站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 5, station: '龙漕路', diagram: '1706-2', vehicles: 54, plan: 4, change: -1, interval: { section: '嘉定北站~嘉定新城站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 6, station: '漕溪路', diagram: '1706-2', vehicles: 54, plan: 4, change: 2, interval: { section: '罗山路站~迪士尼站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 7, station: '龙漕路', diagram: '1706-2', vehicles: 54, plan: 4, change: -1, interval: { section: '东川路站~闵行开发区站', up: '7分30秒', down: '5分20秒' } },
|
||||
{ id: 8, station: '漕溪路', diagram: '1706-2', vehicles: 54, plan: 4, change: 2, interval: { section: '嘉定新城站~南翔站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 9, station: '漕溪路', diagram: '1706-2', vehicles: 54, plan: 4, change: 2, interval: { section: '嘉定新城站~南翔站', up: '3分45秒', down: '2分13秒' } },
|
||||
{ id: 10, station: '漕溪路', diagram: '1706-2', vehicles: 54, plan: 4, change: 2, interval: { section: '嘉定新城站~南翔站', up: '3分45秒', down: '2分13秒' } }
|
||||
]
|
||||
|
||||
const intervalPopover = {
|
||||
overview: '东川路站~闵行开发区站:平均7分30秒(3分~5分)',
|
||||
rows: [
|
||||
{ section: '嘉定新城站~南翔站:', up: '平均3分45秒', down: '平均2分13秒' },
|
||||
{ section: '三林站~罗山路站:', up: '平均3分45秒', down: '平均2分13秒' },
|
||||
{ section: '花桥站~嘉定新城站:', up: '平均3分45秒', down: '平均2分13秒' },
|
||||
{ section: '嘉定北~嘉定新城站:\n罗山路站~迪士尼站:', up: '平均3分45秒', down: '平均2分13秒' }
|
||||
]
|
||||
}
|
||||
|
||||
const formatChange = value => `${value > 0 ? '+' : '-'}${Math.abs(value)}`
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-1 {
|
||||
position: absolute;
|
||||
left: 11px;
|
||||
top: 123.99px;
|
||||
width: 1255.52px;
|
||||
height: 895.21px;
|
||||
color: #d9ebff;
|
||||
background: url('/imgs/driving/1-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.column-select {
|
||||
position: absolute;
|
||||
top: 61px;
|
||||
right: 27px;
|
||||
z-index: 2;
|
||||
|
||||
}
|
||||
|
||||
.vehicle-table {
|
||||
position: absolute;
|
||||
top: 115px;
|
||||
right: 17px;
|
||||
bottom: 27px;
|
||||
left: 17px;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 220px 145px 115px 145px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
height: 46px;
|
||||
padding: 0 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 24px;
|
||||
|
||||
text-align: center;
|
||||
|
||||
background: linear-gradient(90deg, #0094ff00 -7%, #0085ff4d 50%, #0047ff00 99%), rgba(0, 87, 255, 0.05);
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.table-body {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-body-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-top: 16px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
flex: 0 0 74px;
|
||||
height: 66px;
|
||||
line-height: 66px;
|
||||
padding: 0 15px;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.station {
|
||||
background: linear-gradient(114deg, #3485ff33 4%, #3e8bff20 44%, #2eadff10 104%);
|
||||
}
|
||||
|
||||
.diagram {
|
||||
font-size: 23px;
|
||||
color: #19CFD6;
|
||||
background: linear-gradient(130deg, #3485ff32 3%, #3e8bff26 47%, #2eadff1d 112%);
|
||||
}
|
||||
|
||||
.vehicles {
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
background: linear-gradient(131deg, #3485ff32 3%, #3e8bff26 47%, #2eadff1d 113%);
|
||||
}
|
||||
|
||||
.plan {
|
||||
font-size: 25px;
|
||||
background: linear-gradient(131deg, #3485ff32 3%, #3e8bff26 47%, #2eadff1d 113%);
|
||||
}
|
||||
|
||||
.rise,
|
||||
.fall {
|
||||
color: #FF0000;
|
||||
}
|
||||
|
||||
.intervals {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 170px;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
padding: 0 12px 0 16px;
|
||||
cursor: default;
|
||||
text-align: left;
|
||||
|
||||
background: linear-gradient(99deg, #3485ff32 5%, #3e8bff26 43%, #2eadff0e 101%);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(90deg, rgba(35, 113, 211, .96), rgba(24, 91, 178, .88));
|
||||
box-shadow: inset 0 0 20px rgba(58, 174, 255, .22);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.interval-route {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.interval-directions {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.interval-direction {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
white-space: nowrap;
|
||||
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.el-popper.driving-row-popover {
|
||||
padding: 13px 31px 15px;
|
||||
border: 1px solid #087bce;
|
||||
border-radius: 9px;
|
||||
color: #dce9fa;
|
||||
background: rgba(2, 22, 61, .98);
|
||||
box-shadow: inset 0 0 18px rgba(0, 106, 213, .1), 0 5px 14px rgba(0, 8, 32, .32);
|
||||
|
||||
.el-popper__arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&[data-popper-placement^='top']::before,
|
||||
&[data-popper-placement^='top']::after {
|
||||
position: absolute;
|
||||
left: 77px;
|
||||
content: '';
|
||||
clip-path: polygon(0 0, 100% 0, 0 100%);
|
||||
}
|
||||
|
||||
&[data-popper-placement^='top']::before {
|
||||
bottom: -40px;
|
||||
width: 103px;
|
||||
height: 41px;
|
||||
background: #087bce;
|
||||
}
|
||||
|
||||
&[data-popper-placement^='top']::after {
|
||||
bottom: -37px;
|
||||
left: 78px;
|
||||
width: 99px;
|
||||
height: 38px;
|
||||
background: #02163d;
|
||||
}
|
||||
}
|
||||
|
||||
.row-popover-content {
|
||||
font-size: 17px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.popover-overview {
|
||||
height: 31px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.popover-interval {
|
||||
display: grid;
|
||||
grid-template-columns: 200px 149px 149px;
|
||||
min-height: 43px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popover-section {
|
||||
padding-right: 10px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.popover-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
img {
|
||||
flex: 0 0 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 9px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<section class="driving-2">
|
||||
<div class="column-select">
|
||||
<MetroLineSelect v-model="activeLine" />
|
||||
</div>
|
||||
|
||||
<div class="station-table">
|
||||
<div class="table-header table-grid">
|
||||
<span>车站</span>
|
||||
<span>供电</span>
|
||||
<span>车辆</span>
|
||||
<span>车队</span>
|
||||
<span>通号</span>
|
||||
<span>保护区</span>
|
||||
<span>轨行区</span>
|
||||
</div>
|
||||
|
||||
<el-scrollbar class="table-body">
|
||||
<div class="table-body-inner">
|
||||
<div v-for="item in stationRanks" :key="item.id" class="table-row table-grid">
|
||||
<span class="station">{{ item.station }}</span>
|
||||
<span>{{ item.power }}</span>
|
||||
<span>{{ item.vehicle }}</span>
|
||||
<span>{{ item.fleet }}</span>
|
||||
<span>{{ item.signal }}</span>
|
||||
<span>{{ item.protection }}</span>
|
||||
<span>{{ item.track }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import MetroLineSelect from './MetroLineSelect.vue'
|
||||
|
||||
const activeLine = ref(7)
|
||||
|
||||
const stationRanks = [
|
||||
{ id: 1, station: '上海南站', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 2, station: '石龙路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 3, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 4, station: '漕溪路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 5, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 6, station: '虹桥火车站', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 7, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 8, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 9, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 10, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 11, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 12, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 },
|
||||
{ id: 13, station: '龙漕路', power: 23, vehicle: 56, fleet: 45, signal: 7, protection: 46, track: 5 }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-2 {
|
||||
position: absolute;
|
||||
left: 1277.92px;
|
||||
top: 123.99px;
|
||||
width: 865.4px;
|
||||
height: 894px;
|
||||
color: #d9ebff;
|
||||
background: url('/imgs/driving/2-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.column-select {
|
||||
position: absolute;
|
||||
top: 61px;
|
||||
right: 27px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.station-table {
|
||||
position: absolute;
|
||||
top: 115px;
|
||||
right: 13px;
|
||||
bottom: 22px;
|
||||
left: 13px;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 130px repeat(6, minmax(0, 1fr));
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
flex: 0 0 49px;
|
||||
padding: 0 11px;
|
||||
color: #FFFFFF;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
background: linear-gradient(90deg, #0094ff00 -7%, #0085ff4d 50%, #0047ff00 99%), rgba(0, 87, 255, 0.05);
|
||||
|
||||
span:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.table-body {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-body-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
flex: 0 0 48px;
|
||||
height: 48px;
|
||||
padding: 0 11px;
|
||||
border-bottom: 1px solid rgba(0, 138, 255, .72);
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
background: linear-gradient(90deg, #0094ff00 -7%, #0085ff4d 50%, #0047ff00 99%), rgba(0, 87, 255, 0.05);
|
||||
|
||||
}
|
||||
|
||||
.station {
|
||||
overflow: hidden;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<section class="driving-3">
|
||||
<img class="rotating-visual" src="/imgs/driving/3-item-1.png" alt="" />
|
||||
<span v-for="item in smallBallCounts" :key="item.name" class="small-ball-count"
|
||||
:style="{ left: `${item.left}px`, top: `${item.top}px`, fontSize: `${item.fontSize}px` }">{{
|
||||
constructionStats[item.key] }}</span>
|
||||
<div class="total-count">
|
||||
<span>总施工量</span>
|
||||
<strong>{{ formatNumber(constructionStats.total) }}</strong>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const constructionStats = reactive({
|
||||
protection: 6,
|
||||
database: 6,
|
||||
signal: 183,
|
||||
generalAffairs: 19,
|
||||
power: 82,
|
||||
vehicle: 1,
|
||||
track: 20,
|
||||
fleet: 30,
|
||||
total: 63935
|
||||
})
|
||||
|
||||
const smallBallCounts = [
|
||||
{ name: '保护区', key: 'protection', left: 125, top: 105, fontSize: 30 },
|
||||
{ name: '数据库', key: 'database', left: 354, top: 121, fontSize: 38 },
|
||||
{ name: '通号', key: 'signal', left: 541, top: 158, fontSize: 34 },
|
||||
{ name: '总务', key: 'generalAffairs', left: 787, top: 108, fontSize: 30 },
|
||||
{ name: '供电', key: 'power', left: 190, top: 278, fontSize: 38 },
|
||||
{ name: '车辆', key: 'vehicle', left: 700, top: 256, fontSize: 38 },
|
||||
{ name: '轨行区', key: 'track', left: 117, top: 486, fontSize: 34 },
|
||||
{ name: '车队', key: 'fleet', left: 765, top: 480, fontSize: 36 }
|
||||
]
|
||||
|
||||
const formatNumber = value => Number(value ?? 0).toLocaleString('en-US')
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-3 {
|
||||
position: absolute;
|
||||
left: 2155.13px;
|
||||
top: 123.99px;
|
||||
width: 866.87px;
|
||||
height: 926.8px;
|
||||
opacity: 1;
|
||||
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: url('/imgs/driving/3-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.rotating-visual {
|
||||
position: absolute;
|
||||
top: 328px;
|
||||
left: 250px;
|
||||
width: 380px;
|
||||
height: 379px;
|
||||
object-fit: contain;
|
||||
transform-origin: center;
|
||||
animation: driving-3-rotate 18s linear infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.small-ball-count {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
text-shadow: 0 2px 4px rgba(0, 38, 118, .7);
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.total-count {
|
||||
position: absolute;
|
||||
top: 451px;
|
||||
left: 250px;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
width: 380px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
|
||||
span {
|
||||
font-size: 42px;
|
||||
line-height: 52px;
|
||||
text-shadow: 0px 0px 10px rgba(30, 121, 255, 0.8);
|
||||
}
|
||||
|
||||
strong {
|
||||
margin-top: 18px;
|
||||
font-size: 50px;
|
||||
font-weight: 600;
|
||||
line-height: 62px;
|
||||
text-shadow: 0px 1px 4px rgba(7, 44, 137, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes driving-3-rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<DrivingChartPanel
|
||||
v-model:line="line"
|
||||
v-model:date="date"
|
||||
:chart-data="chartData"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import DrivingChartPanel from './DrivingChartPanel.vue'
|
||||
|
||||
const line = ref(7)
|
||||
const date = ref('2026-06-30')
|
||||
const chartData = {
|
||||
dates: ['01/21', '01/22', '01/23', '01/24', '01/25', '01/26'],
|
||||
current: [98.8, 100.15, 101.3, 99.95, 100.42, 99.35],
|
||||
previous: [98.55, 99.7, 100.08, 99.68, 100.88, 99.32]
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
<template>
|
||||
<section class="driving-5">
|
||||
<div class="metric-cards">
|
||||
<article v-for="card in metricCards" :key="card.key" class="metric-card"
|
||||
:class="`metric-card--${card.type}`">
|
||||
<h3>{{ card.title }}</h3>
|
||||
|
||||
<div class="primary-value">
|
||||
<strong>{{ card.value }}</strong>
|
||||
<span v-if="card.unit">{{ card.unit }}</span>
|
||||
</div>
|
||||
|
||||
<div class="secondary-value">
|
||||
<span>{{ card.secondaryLabel }}</span>
|
||||
<strong>{{ card.secondaryValue }}</strong>
|
||||
<small v-if="card.secondaryUnit">{{ card.secondaryUnit }}</small>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const metricCards = reactive([
|
||||
{
|
||||
key: 'assignedMileage',
|
||||
type: 'basic',
|
||||
title: '配属车辆',
|
||||
value: '774',
|
||||
unit: '公里',
|
||||
secondaryLabel: '车站数',
|
||||
secondaryValue: '460',
|
||||
secondaryUnit: '座'
|
||||
},
|
||||
{
|
||||
key: 'assignedVehicles',
|
||||
type: 'basic',
|
||||
title: '配属车辆',
|
||||
value: '7154',
|
||||
unit: '辆',
|
||||
secondaryLabel: '列车数',
|
||||
secondaryValue: '1116',
|
||||
secondaryUnit: '座'
|
||||
},
|
||||
{
|
||||
key: 'headway',
|
||||
type: 'basic',
|
||||
title: '行车间隔',
|
||||
value: '1′50″',
|
||||
unit: '',
|
||||
secondaryLabel: '中心城区',
|
||||
secondaryValue: '1′50″',
|
||||
secondaryUnit: ''
|
||||
},
|
||||
{
|
||||
key: 'dailyPassengerFlow',
|
||||
type: 'gauge',
|
||||
title: '日均客流',
|
||||
value: '965.5',
|
||||
unit: '万人',
|
||||
secondaryLabel: '目标值',
|
||||
secondaryValue: '915',
|
||||
secondaryUnit: '万人'
|
||||
},
|
||||
{
|
||||
key: 'fiveMinuteDelay',
|
||||
type: 'gauge',
|
||||
title: "5′晚点",
|
||||
value: '940',
|
||||
unit: '万车公里',
|
||||
secondaryLabel: '目标值',
|
||||
secondaryValue: '750',
|
||||
secondaryUnit: ''
|
||||
},
|
||||
{
|
||||
key: 'punctualityRate',
|
||||
type: 'gauge',
|
||||
title: '正点率',
|
||||
value: '99.9',
|
||||
unit: '%',
|
||||
secondaryLabel: '目标值',
|
||||
secondaryValue: '99.7',
|
||||
secondaryUnit: '%'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-5 {
|
||||
position: absolute;
|
||||
left: 3856.58px;
|
||||
top: 128px;
|
||||
width: 920.56px;
|
||||
height: 717.6px;
|
||||
color: #fff;
|
||||
font-family: 'AlibabaPuHuiTi', 'Microsoft YaHei', sans-serif;
|
||||
background: url('/imgs/driving/5-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.metric-cards {
|
||||
position: absolute;
|
||||
top: 128px;
|
||||
left: 46px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 234px);
|
||||
grid-auto-rows: 229px;
|
||||
gap: 54px 63px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
position: relative;
|
||||
width: 234px;
|
||||
height: 229px;
|
||||
text-align: center;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 100% 100%;
|
||||
|
||||
&--basic {
|
||||
background-image: url('/imgs/driving/5-item-1.png');
|
||||
}
|
||||
|
||||
&.metric-card--gauge {
|
||||
background-image: url('/imgs/driving/5-item-2.png');
|
||||
|
||||
h3 {
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
.primary-value {
|
||||
top: 118px;
|
||||
}
|
||||
|
||||
.secondary-value {
|
||||
top: 170px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
position: absolute;
|
||||
top: 17px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
line-height: normal;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.primary-value {
|
||||
position: absolute;
|
||||
top: 91px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
|
||||
strong {
|
||||
font-size: 42px;
|
||||
font-weight: 700;
|
||||
line-height: 48px;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 3px 5px rgba(0, 22, 79, .68);
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 3px;
|
||||
font-size: 20px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.secondary-value {
|
||||
position: absolute;
|
||||
top: 158px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
line-height: normal;
|
||||
white-space: nowrap;
|
||||
|
||||
strong {
|
||||
margin-left: 13px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
small {
|
||||
margin-left: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<section class="driving-6">
|
||||
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-6 {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 1052px;
|
||||
width: 3009px;
|
||||
height: 535px;
|
||||
opacity: 1;
|
||||
|
||||
|
||||
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: url('/imgs/driving/6-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<section class="driving-7">
|
||||
<DrivingChartPanel
|
||||
class="driving-7-panel"
|
||||
v-model:line="line"
|
||||
v-model:date="date"
|
||||
:chart-data="chartData"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import DrivingChartPanel from './DrivingChartPanel.vue'
|
||||
|
||||
const line = ref(7)
|
||||
const date = ref('2026-06-30')
|
||||
const chartData = {
|
||||
dates: ['01/21', '01/22', '01/23', '01/24', '01/25', '01/26'],
|
||||
current: [98.8, 100.15, 101.3, 99.95, 100.42, 99.35],
|
||||
previous: [98.55, 99.7, 100.08, 99.68, 100.88, 99.32]
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-7 {
|
||||
position: absolute;
|
||||
left: 3035px;
|
||||
top: 860.63px;
|
||||
width: 805px;
|
||||
height: 726.37px;
|
||||
opacity: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: url('/imgs/driving/7-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.driving-7-panel {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
<template>
|
||||
<section class="driving-8">
|
||||
<header class="toolbar"><span>对比日期:</span><select v-model="state.date">
|
||||
<option>{{ state.date }}</option>
|
||||
</select><select v-model="state.period">
|
||||
<option>{{ state.period }}</option>
|
||||
</select></header>
|
||||
<div class="summary">
|
||||
<div class="row"><b>最大满载区间</b><span>{{ state.maxSection }} <i>{{ state.loadRate }}%</i></span></div>
|
||||
<div class="row"><b>运行图图号</b><span>实际 <i>{{ state.actualTrips }}列</i><em>|</em> 计划 <strong>{{
|
||||
state.planTrips }}列</strong></span></div>
|
||||
<div class="row"><b>备车情况</b><span>{{ state.spare }}</span></div>
|
||||
<div class="row"><b>行车间隔</b><span>高峰: <span style="margin-right: 20px;">{{ state.peakInterval }}min
|
||||
</span>平峰: <span>{{
|
||||
state.offPeakInterval }}min</span></span></div>
|
||||
</div>
|
||||
<div class="route">
|
||||
<div class="route-line">
|
||||
<label>西岑</label>
|
||||
<span class="station mid">淀山湖大道</span>
|
||||
<span class="station end">虹桥火车站</span>
|
||||
<small class="time t1">6分钟</small>
|
||||
<small class="time t2">3分钟</small>
|
||||
</div>
|
||||
<div class="flow f1">{{ state.flowLeft }}人/小时</div>
|
||||
<div class="flow f2">{{ state.flowRight }}人/小时</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const state = reactive({ date: '20251113', period: '早高峰时段', maxSection: '虹桥火车站-国家会展中心', loadRate: 110, actualTrips: 23, planTrips: 24, spare: '东方绿洲/淀山湖', peakInterval: 2.5, offPeakInterval: 4, flowLeft: 225600, flowRight: 225600 })
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$panel-bg: rgba(4, 124, 235, 0.1);
|
||||
$panel-radius: 6px;
|
||||
$route-color: #00e9ed;
|
||||
$tag-bg: rgba(34, 71, 122, 0.92);
|
||||
$tag-border: #8d7c4e;
|
||||
|
||||
@mixin flex($align: center, $justify: flex-start, $direction: row) {
|
||||
display: flex;
|
||||
flex-direction: $direction;
|
||||
align-items: $align;
|
||||
justify-content: $justify;
|
||||
}
|
||||
|
||||
@mixin glass-panel {
|
||||
background: $panel-bg;
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: $panel-radius;
|
||||
}
|
||||
|
||||
@mixin route-tag($top) {
|
||||
position: absolute;
|
||||
top: $top;
|
||||
z-index: 2;
|
||||
padding: 4px 16px;
|
||||
color: inherit;
|
||||
white-space: nowrap;
|
||||
background: linear-gradient(130deg, #f5ff3442 2%, #ffb23e31 36%, #ff932e27 116%);
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(90deg, #ffd343b3 -3%, #ffc85b 51%, #ffcd43b3 102%) 1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@mixin time-tag($top) {
|
||||
position: absolute;
|
||||
top: $top;
|
||||
z-index: 2;
|
||||
padding: 4px 16px;
|
||||
color: inherit;
|
||||
white-space: nowrap;
|
||||
background: linear-gradient(122deg, #28426a 4%, #0d4061 108%);
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(90deg, #43aaffb3 -3%, #5bb5ff 51%, #43aaffb3 102%) 1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.driving-8 {
|
||||
@include flex(stretch, flex-start, column);
|
||||
|
||||
position: absolute;
|
||||
top: 860.63px;
|
||||
left: 3856.58px;
|
||||
width: 920.56px;
|
||||
height: 726.37px;
|
||||
padding: 8px 32px 0;
|
||||
color: #FFFFFF;
|
||||
font: 22px;
|
||||
background: url('/imgs/driving/8-bg.png') no-repeat center / cover;
|
||||
|
||||
.toolbar {
|
||||
@include flex(center, flex-end);
|
||||
|
||||
height: 33px;
|
||||
margin-top: 70px;
|
||||
gap: 12px;
|
||||
color: #dcecff;
|
||||
font-size: 16px;
|
||||
|
||||
select {
|
||||
height: 26px;
|
||||
padding: 0 24px 0 10px;
|
||||
appearance: none;
|
||||
color: #e4f2ff;
|
||||
font-size: 17px;
|
||||
background-color: #2166a8;
|
||||
background-image: linear-gradient(45deg, transparent 50%, #b8d9f7 50%),
|
||||
linear-gradient(135deg, #b8d9f7 50%, transparent 50%);
|
||||
background-repeat: no-repeat;
|
||||
background-position: calc(100% - 13px) 10px, calc(100% - 8px) 10px;
|
||||
background-size: 5px 5px;
|
||||
border: 1px solid #4b9ee5;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.summary {
|
||||
@include glass-panel;
|
||||
@include flex(stretch, space-evenly, column);
|
||||
|
||||
height: 280px;
|
||||
padding: 0 18px;
|
||||
|
||||
.row {
|
||||
@include flex(center, space-between);
|
||||
|
||||
height: 53px;
|
||||
padding: 0 18px;
|
||||
margin-bottom: 1px;
|
||||
background: linear-gradient(90deg, #0085ff00 0%, #00a3ff48 40%, #00a2ff48 70%, #0075ff0a 100%);
|
||||
|
||||
b {
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
// font-weight: 600;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
i,
|
||||
strong {
|
||||
padding: 3px 10px;
|
||||
margin-left: 10px;
|
||||
color: white;
|
||||
font-style: normal;
|
||||
background: #D72929;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
strong {
|
||||
background: #008CFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
em {
|
||||
margin: 0 10px;
|
||||
color: #d6e6fa;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.route {
|
||||
@include glass-panel;
|
||||
|
||||
position: relative;
|
||||
height: 290px;
|
||||
margin-top: 24px;
|
||||
|
||||
.route-line {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 44px;
|
||||
width: 760px;
|
||||
height: 187px;
|
||||
border: 3px solid $route-color;
|
||||
border-radius: 100px;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 238px;
|
||||
width: 512px;
|
||||
height: 85px;
|
||||
content: '';
|
||||
border: 3px solid $route-color;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
@include route-tag(73px);
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
.station {
|
||||
@include route-tag(73px);
|
||||
|
||||
&.mid,
|
||||
&.end {
|
||||
width: 125px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
left: 270px;
|
||||
}
|
||||
|
||||
&.end {
|
||||
right: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
@include time-tag(169px);
|
||||
padding: 3px 20px;
|
||||
|
||||
&.t1 {
|
||||
left: 190px;
|
||||
}
|
||||
|
||||
&.t2 {
|
||||
left: 487px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flow {
|
||||
@include time-tag(242px);
|
||||
font-size: 18px;
|
||||
|
||||
&.f1 {
|
||||
left: 198px;
|
||||
}
|
||||
|
||||
&.f2 {
|
||||
left: 492px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,413 @@
|
|||
<template>
|
||||
<section class="driving-4">
|
||||
<div class="filters">
|
||||
<MetroLineSelect v-model="activeLine" />
|
||||
<div class="date-select-shell">
|
||||
<el-config-provider :locale="zhCn">
|
||||
<el-date-picker
|
||||
v-model="selectedDate"
|
||||
class="date-select"
|
||||
popper-class="driving-date-popper"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:clearable="false"
|
||||
:editable="false"
|
||||
/>
|
||||
</el-config-provider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="chart-unit">单位:100%</span>
|
||||
|
||||
<img
|
||||
class="current-highlight"
|
||||
src="/imgs/driving/4-item-1.png"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
:style="highlightStyle"
|
||||
/>
|
||||
<div class="current-value-labels" :style="highlightValueStyle">
|
||||
<strong class="current-value-labels__current">{{ chartData.currentCount }}</strong>
|
||||
<strong class="current-value-labels__previous">{{ chartData.previousCount }}</strong>
|
||||
</div>
|
||||
|
||||
<v-chart class="punctuality-chart" :option="chartOption" autoresize @click="handleChartClick" />
|
||||
|
||||
<div class="chart-legend" aria-hidden="true">
|
||||
<span class="legend-item legend-item--current">当前数据</span>
|
||||
<span class="legend-item legend-item--previous">去年同期</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import MetroLineSelect from './MetroLineSelect.vue'
|
||||
|
||||
const props = defineProps({
|
||||
line: { type: Number, default: 7 },
|
||||
date: { type: String, default: '2026-06-30' },
|
||||
chartData: { type: Object, default: () => ({}) }
|
||||
})
|
||||
const emit = defineEmits(['update:line', 'update:date'])
|
||||
const activeLine = computed({ get: () => props.line, set: value => emit('update:line', value) })
|
||||
const selectedDate = computed({ get: () => props.date, set: value => emit('update:date', value) })
|
||||
const currentIndex = ref(2)
|
||||
|
||||
const chartData = reactive({
|
||||
dates: ['01/21', '01/22', '01/23', '01/24', '01/25', '01/26'],
|
||||
current: [98.8, 100.15, 101.3, 99.95, 100.42, 99.35],
|
||||
previous: [98.55, 99.7, 100.08, 99.68, 100.88, 99.32],
|
||||
currentCount: 418,
|
||||
previousCount: 101
|
||||
})
|
||||
Object.assign(chartData, props.chartData)
|
||||
|
||||
const summary = reactive({
|
||||
current: '99.46',
|
||||
month: '99.86',
|
||||
year: '99.86',
|
||||
yearTotal: '94.86'
|
||||
})
|
||||
|
||||
const handleChartClick = params => {
|
||||
if (!['series', 'xAxis'].includes(params.componentType)) return
|
||||
const index = typeof params.dataIndex === 'number'
|
||||
? params.dataIndex
|
||||
: chartData.dates.indexOf(params.value)
|
||||
if (index >= 0) currentIndex.value = index
|
||||
}
|
||||
|
||||
const highlightStyle = computed(() => ({
|
||||
left: `${74 + currentIndex.value * 135 - 28.5}px`
|
||||
}))
|
||||
const highlightValueStyle = computed(() => ({
|
||||
left: `${74 + currentIndex.value * 135 - 36}px`
|
||||
}))
|
||||
|
||||
const chartOption = computed(() => ({
|
||||
animationDuration: 900,
|
||||
backgroundColor: 'transparent',
|
||||
grid: {
|
||||
top: 78,
|
||||
right: 56,
|
||||
bottom: 82,
|
||||
left: 74
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
showContent: false,
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: { color: 'rgba(39, 220, 255, .75)', width: 1 }
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
triggerEvent: true,
|
||||
data: chartData.dates,
|
||||
axisLine: {
|
||||
lineStyle: { color: '#d4e8ff', width: 1.5 }
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
margin: 18,
|
||||
color: '#fff',
|
||||
fontSize: 19,
|
||||
fontWeight: 600,
|
||||
formatter: (value, index) => index === currentIndex.value ? `{active|${value}}\n{current|当前}` : value,
|
||||
rich: {
|
||||
active: { color: '#158dff', fontSize: 19, fontWeight: 700, lineHeight: 26 },
|
||||
current: { color: '#158dff', fontSize: 18, fontWeight: 600, lineHeight: 28 }
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 97,
|
||||
max: 101.5,
|
||||
interval: 1,
|
||||
axisLine: {
|
||||
show: true,
|
||||
symbol: ['none', 'arrow'],
|
||||
symbolSize: [8, 10],
|
||||
lineStyle: { color: '#d4e8ff', width: 1.5 }
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
fontWeight: 600,
|
||||
margin: 16,
|
||||
formatter: value => Number.isInteger(value) ? value : ''
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(190, 218, 255, .32)', width: 1.5 }
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '当前数据',
|
||||
type: 'line',
|
||||
data: chartData.current,
|
||||
smooth: 0.45,
|
||||
showSymbol: false,
|
||||
z: 5,
|
||||
lineStyle: {
|
||||
color: '#28bfff',
|
||||
width: 4,
|
||||
shadowBlur: 8,
|
||||
shadowColor: 'rgba(40, 191, 255, .5)'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(33, 184, 255, .58)' },
|
||||
{ offset: 1, color: 'rgba(20, 107, 203, .08)' }
|
||||
])
|
||||
},
|
||||
markPoint: {
|
||||
silent: true,
|
||||
symbol: 'none',
|
||||
z: 20,
|
||||
data: [
|
||||
{
|
||||
coord: [currentIndex.value, 101.22],
|
||||
value: chartData.currentCount,
|
||||
label: { color: '#00caff', fontSize: 34, fontWeight: 700, offset: [-36, -28] }
|
||||
},
|
||||
{
|
||||
coord: [currentIndex.value, 99.2],
|
||||
value: chartData.previousCount,
|
||||
label: { color: '#ffd20b', fontSize: 34, fontWeight: 700, offset: [-36, 0] }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '去年同期',
|
||||
type: 'line',
|
||||
data: chartData.previous,
|
||||
smooth: 0.45,
|
||||
showSymbol: false,
|
||||
z: 4,
|
||||
lineStyle: { color: '#f2cb08', width: 4 },
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(238, 202, 15, .42)' },
|
||||
{ offset: 1, color: 'rgba(238, 202, 15, .06)' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '日期选择区域',
|
||||
type: 'bar',
|
||||
data: chartData.dates.map(() => 101.5),
|
||||
barWidth: '100%',
|
||||
silent: false,
|
||||
z: 10,
|
||||
tooltip: { show: false },
|
||||
itemStyle: { color: 'transparent' },
|
||||
emphasis: { itemStyle: { color: 'transparent' } }
|
||||
}
|
||||
]
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-4 {
|
||||
position: absolute;
|
||||
left: 3035px;
|
||||
top: 123.99px;
|
||||
width: 805px;
|
||||
height: 717.6px;
|
||||
color: #fff;
|
||||
background: url('/imgs/driving/4-bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filters {
|
||||
position: absolute;
|
||||
top: 68px;
|
||||
right: 24px;
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.date-select-shell {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 43px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(90deg, rgba(51, 99, 164, .9), rgba(9, 38, 92, .66));
|
||||
--el-input-bg-color: transparent;
|
||||
--el-fill-color-blank: transparent;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 13px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url('/imgs/driving/arrow.png') no-repeat center / contain;
|
||||
content: '';
|
||||
pointer-events: none;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.driving-date-popper.el-picker__popper) {
|
||||
border: 1px solid rgba(42, 154, 239, .7) !important;
|
||||
border-radius: 4px;
|
||||
background: rgba(4, 27, 72, .98) !important;
|
||||
box-shadow: 0 10px 28px rgba(0, 8, 30, .55);
|
||||
}
|
||||
|
||||
:global(.driving-date-popper .el-picker-panel),
|
||||
:global(.driving-date-popper .el-date-picker__header),
|
||||
:global(.driving-date-popper .el-picker-panel__content) {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
:global(.driving-date-popper .el-date-picker__header-label),
|
||||
:global(.driving-date-popper .el-picker-panel__icon-btn) {
|
||||
color: #e8f4ff !important;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
:global(.driving-date-popper .el-date-table th) {
|
||||
color: #75b9ed !important;
|
||||
border-bottom-color: rgba(117, 185, 237, .2) !important;
|
||||
}
|
||||
|
||||
:global(.driving-date-popper .el-date-table-cell__text) {
|
||||
color: #dcecff !important;
|
||||
}
|
||||
|
||||
:global(.driving-date-popper .el-date-table td.available:hover .el-date-table-cell__text),
|
||||
:global(.driving-date-popper .el-date-table td.current:not(.disabled) .el-date-table-cell__text) {
|
||||
color: #fff !important;
|
||||
background: #1677d6 !important;
|
||||
}
|
||||
|
||||
:global(.driving-date-popper .el-date-table td.today .el-date-table-cell__text) {
|
||||
color: #39c5ff !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
// Element Plus applies the date editor outline on the outer input wrapper.
|
||||
:global(.driving-4 .date-select.el-date-editor),
|
||||
:global(.driving-4 .date-select.el-date-editor .el-input__wrapper),
|
||||
:global(.driving-4 .date-select.el-date-editor.is-focus .el-input__wrapper),
|
||||
:global(.driving-4 .date-select.el-date-editor:hover .el-input__wrapper) {
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:global(.driving-4 .date-select .el-input__prefix) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
:global(.driving-4 .date-select-shell .el-input),
|
||||
:global(.driving-4 .date-select-shell .el-input__wrapper),
|
||||
:global(.driving-4 .date-select-shell .el-input__inner) {
|
||||
background-color: transparent !important;
|
||||
background-image: none !important;
|
||||
height: 44px !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
.chart-unit {
|
||||
position: absolute;
|
||||
top: 147px;
|
||||
left: 33px;
|
||||
z-index: 3;
|
||||
font-size: 18px;
|
||||
|
||||
}
|
||||
|
||||
.punctuality-chart {
|
||||
position: absolute;
|
||||
top: 128px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 530px;
|
||||
}
|
||||
|
||||
.current-highlight {
|
||||
position: absolute;
|
||||
top: 219px;
|
||||
z-index: 2;
|
||||
width: 57px;
|
||||
height: 364px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.current-value-labels {
|
||||
position: absolute;
|
||||
top: 182px;
|
||||
z-index: 6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 123px;
|
||||
width: 72px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.current-value-labels strong { font-size: 34px; line-height: 1; }
|
||||
.current-value-labels__current { color: #12bfff; }
|
||||
.current-value-labels__previous { color: #ffd20b; }
|
||||
|
||||
.chart-legend {
|
||||
position: absolute;
|
||||
bottom: 38px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 38px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
position: relative;
|
||||
padding-left: 37px;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 29px;
|
||||
border-top: 4px solid;
|
||||
content: '';
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&--current {
|
||||
color: #28bfff;
|
||||
}
|
||||
|
||||
&--previous {
|
||||
color: #f2cb08;
|
||||
|
||||
&::before {
|
||||
border-top-style: dotted;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<div ref="root" class="metro-line-select">
|
||||
<button class="select-trigger" type="button" aria-haspopup="listbox" :aria-expanded="open" @click="open = !open"
|
||||
@keydown.down.prevent="moveSelection(1)" @keydown.up.prevent="moveSelection(-1)" @keydown.esc="open = false">
|
||||
<span class="selected-line" :style="lineStyle(selectedLine)">
|
||||
<strong v-if="isNumberedLine(selectedLine)">{{ selectedLine.id }}</strong>
|
||||
<span>{{ isNumberedLine(selectedLine) ? '号线' : selectedLine.name }}</span>
|
||||
</span>
|
||||
<img class="arrow" :class="{ open }" src="/imgs/driving/arrow.png" alt="" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<Transition name="line-options">
|
||||
<ul v-if="open" class="options" role="listbox">
|
||||
<li v-for="line in lineOptions" :key="line.id" :class="{ active: line.id === modelValue }" role="option"
|
||||
:aria-selected="line.id === modelValue" @click="selectLine(line.id)">
|
||||
<span class="line-swatch" :style="lineStyle(line)">{{ line.name }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { METRO_LINES } from '@/utils/const.js'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Number, default: 7 }
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
const root = ref(null)
|
||||
const open = ref(false)
|
||||
const lineOptions = Object.entries(METRO_LINES).map(([id, theme]) => ({
|
||||
id: Number(id),
|
||||
...theme
|
||||
}))
|
||||
const selectedLine = computed(() => (
|
||||
lineOptions.find(line => line.id === props.modelValue) || lineOptions[0]
|
||||
))
|
||||
|
||||
const lineStyle = line => ({ backgroundColor: line.bg, color: line.color })
|
||||
const isNumberedLine = line => line.id >= 1 && line.id <= 18
|
||||
|
||||
const selectLine = id => {
|
||||
emit('update:modelValue', id)
|
||||
emit('change', id)
|
||||
open.value = false
|
||||
}
|
||||
|
||||
const moveSelection = offset => {
|
||||
open.value = true
|
||||
const currentIndex = lineOptions.findIndex(line => line.id === props.modelValue)
|
||||
const nextIndex = (currentIndex + offset + lineOptions.length) % lineOptions.length
|
||||
selectLine(lineOptions[nextIndex].id)
|
||||
}
|
||||
|
||||
const closeOnOutsideClick = event => {
|
||||
if (!root.value?.contains(event.target)) open.value = false
|
||||
}
|
||||
|
||||
onMounted(() => document.addEventListener('pointerdown', closeOnOutsideClick))
|
||||
onBeforeUnmount(() => document.removeEventListener('pointerdown', closeOnOutsideClick))
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.metro-line-select {
|
||||
position: relative;
|
||||
width: 154px;
|
||||
}
|
||||
|
||||
.select-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 43px;
|
||||
padding: 0 13px 0 18px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(90deg, rgba(51, 99, 164, .9), rgba(9, 38, 92, .66));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected-line {
|
||||
display: inline-flex;
|
||||
// align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
min-width: 78px;
|
||||
height: 33px;
|
||||
padding: 0 9px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
white-space: nowrap;
|
||||
|
||||
strong {
|
||||
font-size: 30px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 12px;
|
||||
object-fit: contain;
|
||||
transition: transform .18s ease;
|
||||
|
||||
&.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
position: absolute;
|
||||
top: 47px;
|
||||
right: 0;
|
||||
z-index: 20;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 6px;
|
||||
width: 238px;
|
||||
max-height: 330px;
|
||||
margin: 0;
|
||||
padding: 9px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid rgba(47, 156, 255, .7);
|
||||
border-radius: 4px;
|
||||
list-style: none;
|
||||
background: rgba(3, 24, 68, .98);
|
||||
box-shadow: 0 8px 24px rgba(0, 8, 30, .55);
|
||||
}
|
||||
|
||||
.options li {
|
||||
padding: 2px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
border-color: #60d9ff;
|
||||
}
|
||||
}
|
||||
|
||||
.line-swatch {
|
||||
display: block;
|
||||
height: 30px;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.line-options-enter-active,
|
||||
.line-options-leave-active {
|
||||
transition: opacity .15s ease, transform .15s ease;
|
||||
}
|
||||
|
||||
.line-options-enter-from,
|
||||
.line-options-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<script setup lang="ts">
|
||||
import Driving1 from './components/Driving1.vue';
|
||||
import Driving2 from './components/Driving2.vue';
|
||||
import Driving3 from './components/Driving3.vue';
|
||||
import Driving4 from './components/Driving4.vue';
|
||||
import Driving5 from './components/Driving5.vue';
|
||||
import Driving6 from './components/Driving6.vue';
|
||||
import Driving7 from './components/Driving7.vue';
|
||||
import Driving8 from './components/Driving8.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="driving-container">
|
||||
|
||||
<Driving1 />
|
||||
<Driving2 />
|
||||
<Driving3 />
|
||||
<Driving4 />
|
||||
<Driving5 />
|
||||
<Driving6 />
|
||||
<Driving7 />
|
||||
<Driving8 />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.driving-container {
|
||||
width: 4800px;
|
||||
height: 1620px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,30 +1,18 @@
|
|||
<template>
|
||||
<div class="dashboard-header">
|
||||
<nav class="layout-menu" aria-label="页面导航">
|
||||
<RouterLink v-for="item in menuItems" :key="item.to" :to="item.to" class="layout-menu__item">
|
||||
<span>{{ item.label }}</span>
|
||||
</RouterLink>
|
||||
<div class="weather-info">
|
||||
<el-icon class="weather-icon"><Pouring /></el-icon>
|
||||
<span class="weather-temperature">{{ temperatureRange }}</span>
|
||||
<span class="weather-name">{{ weatherInfo.weather || '--' }}</span>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="header-title">
|
||||
<img src="/imgs/title.png" alt="title" class="title-img" />
|
||||
</div>
|
||||
<div class="weather-info">
|
||||
<div class="weather-item">
|
||||
<span class="label">天气:</span>
|
||||
<span class="value">{{ weatherInfo.weather || '--' }}</span>
|
||||
</div>
|
||||
<div class="weather-item">
|
||||
<span class="label">温度:</span>
|
||||
<span class="value">{{ weatherInfo.tem || '--' }}℃</span>
|
||||
</div>
|
||||
<div class="weather-item">
|
||||
<span class="label">风速:</span>
|
||||
<span class="value">{{ weatherInfo.wind_speed || '--' }}m/s</span>
|
||||
</div>
|
||||
<div class="weather-item">
|
||||
<span class="label">湿度:</span>
|
||||
<span class="value">{{ weatherInfo.rh || '--' }}%</span>
|
||||
</div>
|
||||
<div class="weather-item">
|
||||
<span class="label">能见度:</span>
|
||||
<span class="value">{{ weatherInfo.vis || '--' }}km</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-time">{{ currentTime }}</div>
|
||||
<div class="notice-marquee" v-if="noticeList.length > 0">
|
||||
<div class="marquee-content" :class="{ 'marquee-scroll': noticeList.length > 1 }">
|
||||
|
|
@ -38,6 +26,7 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||
import { Pouring } from '@element-plus/icons-vue'
|
||||
import server from '@/utils/service'
|
||||
|
||||
const currentTime = ref('')
|
||||
|
|
@ -47,11 +36,25 @@ const currentNoticeIndex = ref(0)
|
|||
let noticeTimer = null
|
||||
let timeTimer = null
|
||||
|
||||
const menuItems = [
|
||||
{ label: '常规', to: '/home' },
|
||||
{ label: '客运', to: '/traffic' },
|
||||
{ label: '行车', to: '/driving' },
|
||||
{ label: '施工', to: '/construction' }
|
||||
]
|
||||
|
||||
const displayNoticeList = computed(() => {
|
||||
if (noticeList.value.length === 0) return []
|
||||
return [noticeList.value[currentNoticeIndex.value]]
|
||||
})
|
||||
|
||||
const temperatureRange = computed(() => {
|
||||
const min = weatherInfo.value.tem_min ?? weatherInfo.value.min_tem ?? weatherInfo.value.temp_min ?? 21
|
||||
const max = weatherInfo.value.tem_max ?? weatherInfo.value.max_tem ?? weatherInfo.value.temp_max
|
||||
?? (weatherInfo.value.tem ? Math.round(Number(weatherInfo.value.tem)) : 27)
|
||||
return `${min}°~${max}°`
|
||||
})
|
||||
|
||||
const updateTime = () => {
|
||||
const now = new Date()
|
||||
currentTime.value = now.toLocaleString('zh-CN', {
|
||||
|
|
@ -114,92 +117,138 @@ onUnmounted(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.dashboard-header {
|
||||
width: 4800px;
|
||||
height: 106px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: url('/imgs/top.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
// border-bottom: 2px solid rgba(0, 212, 255, 0.5);
|
||||
|
||||
.header-title {
|
||||
.title-img {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.header-time {
|
||||
.dashboard-header {
|
||||
position: absolute;
|
||||
// right: 40px;
|
||||
top: 110px;
|
||||
left: 50%;
|
||||
font-family: BlackOpsOne, sans-serif;
|
||||
font-size: 44px;
|
||||
color: #19EBFF;
|
||||
text-align: center;
|
||||
// 居中显示
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.notice-marquee {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 55px;
|
||||
width: 1300px;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
|
||||
.marquee-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.notice-item {
|
||||
font-size: 40px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
white-space: nowrap;
|
||||
animation: marquee 20s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes marquee {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.weather-info {
|
||||
position: absolute;
|
||||
left: 200px;
|
||||
top: 70%;
|
||||
transform: translateY(-50%);
|
||||
left: -2px;
|
||||
top: -36px;
|
||||
width: 4802px;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: url('/imgs/top.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
// border-bottom: 2px solid rgba(0, 212, 255, 0.5);
|
||||
|
||||
.weather-item {
|
||||
.layout-menu {
|
||||
position: absolute;
|
||||
top: 82px;
|
||||
left: 42px;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
line-height: 45px;
|
||||
|
||||
.label {
|
||||
font-size: 36px;
|
||||
color: #FFF;
|
||||
}
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 214px;
|
||||
height: 58px;
|
||||
padding-bottom: 8px;
|
||||
background: url('/imgs/layout/menu-bg.png') no-repeat center / 100% 100%;
|
||||
text-decoration: none;
|
||||
|
||||
.value {
|
||||
font-size: 36px;
|
||||
color: #fff;
|
||||
margin-left: -10px;
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
font-size: 36px;
|
||||
font-weight: 500;
|
||||
background: linear-gradient(180deg, #ffffff 53%, #8dc2ff 69%, #66b5ff 75%), #FFFFFF;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-fill-color: transparent;
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.15);
|
||||
}
|
||||
|
||||
&.router-link-active {
|
||||
background-image: url('/imgs/layout/menu-bg-active.png');
|
||||
color: #eafaff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
.title-img {
|
||||
margin-top: 18px;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.header-time {
|
||||
position: absolute;
|
||||
// right: 40px;
|
||||
top: 148px;
|
||||
left: 50%;
|
||||
font-family: BlackOpsOne, sans-serif;
|
||||
font-size: 44px;
|
||||
color: #19EBFF;
|
||||
text-align: center;
|
||||
// 居中显示
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.notice-marquee {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 55px;
|
||||
width: 1300px;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
|
||||
.marquee-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.notice-item {
|
||||
font-size: 40px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
white-space: nowrap;
|
||||
animation: marquee 20s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes marquee {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.weather-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 58px;
|
||||
margin-left: 46px;
|
||||
gap: 14px;
|
||||
white-space: nowrap;
|
||||
color: #fff;
|
||||
|
||||
.weather-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: #82f6ff;
|
||||
font-size: 40px;
|
||||
filter: drop-shadow(0 0 6px rgba(70, 224, 255, 0.75));
|
||||
}
|
||||
|
||||
.weather-temperature,
|
||||
.weather-name {
|
||||
font-size: 30px;
|
||||
font-weight: 500;
|
||||
line-height: 58px;
|
||||
text-shadow: 0px 0px 10px rgba(30, 198, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,19 +12,21 @@ import DashboardHeader from '@/views/home/components/DashboardHeader.vue'
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-container {
|
||||
width: 4800px;
|
||||
height: 1620px;
|
||||
position: relative;
|
||||
background: url('/imgs/mainBg.webp') no-repeat center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
.layout-container {
|
||||
width: 4800px;
|
||||
height: 1620px;
|
||||
position: relative;
|
||||
background: url('/imgs/mainBg.webp') no-repeat center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.route-content {
|
||||
width: 4800px;
|
||||
height: 1514px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.route-content {
|
||||
width: 4800px;
|
||||
height: 1514px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||