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.

1095 lines
27 KiB

1 month ago
<template>
<div>
<PublicMapComponents ref="mapComponentRef" class="map-base" />
1 month ago
<div class="head">
<div class="head-title">
<span>曹妃甸港区船舶岸电监管平台</span>
</div>
1 month ago
<el-button class="view-btn" size="small" type="success" @click="handleOverviewClick"
1 month ago
style="margin-right: 10px;">概览视角</el-button>
<div class="head-group">
<div v-for="item in headGroupList" :key="item.value" class="head-group-item"
:class="{ 'active': activeHeadGroup === item.value }" @click="selectHeadGroup(item.value)">
{{ item.label }}
</div>
</div>
<div class="head-time">{{ currentTime }}</div>
</div>
<!-- 港区概览 -->
<template v-if="activeHeadGroup === 0">
4 weeks ago
<PortOverview :ship-status-data="shipStatusData" :shore-power-status-data="shorePowerStatusData"
4 weeks ago
@item-click="handleSwitch" :realtime-device-data="realtimeDeviceData" />
1 month ago
</template>
<!-- 港口岸电使用情况 -->
4 weeks ago
<!-- <template v-if="activeHeadGroup === 1"> -->
<ShorePowerUsage v-show="activeHeadGroup === 1" :realtime-device-data="realtimeDeviceData"
:active-head-group="activeHeadGroup" />
<!-- </template> -->
1 month ago
<!-- 港口企业岸电使用 -->
<template v-if="activeHeadGroup === 2">
1 month ago
<CompanyShorePower :companyComparisonData="companyComparisonData" />
1 month ago
</template>
<!-- 船舶岸电使用情况 -->
<template v-if="activeHeadGroup === 3">
4 weeks ago
<ShipShorePower :ship-data="shipStatusData" :selected-ship="selectedShip" @item-click="handleSwitch" />
1 month ago
</template>
1 month ago
<template v-if="activeHeadGroup === 4">
<div class="right" style="width: 500px;">
<div class="card digital-twin-card--deep-blue">
<div class="card-title">
<div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">岸电箱详情</span>
<el-button class="close-btn" size="small" @click="closeElectricalBoxInfo"
style="margin-left: auto;">×</el-button>
</div>
<div class="card-content">
<div class="ship-detail">
<div class="detail-item">
<span class="label">名称:</span>
<span class="value">{{ selectedElectricalBox?.name || '无' }}</span>
</div>
<div class="detail-item">
<span class="label">位置:</span>
<span class="value">{{ selectedElectricalBox?.location || '曹妃甸港区华能码头' }}</span>
</div>
<div class="detail-item">
<span class="label">状态:</span>
<span class="value">{{ selectedElectricalBox?.status || '在线' }}</span>
</div>
<!-- 当前使用数据 -->
<div class="section-title">当前使用数据</div>
<div class="detail-item">
<span class="label">当前使用功率:</span>
<span class="value">{{ selectedElectricalBox?.currentPower || '120' }} kW</span>
</div>
<div class="detail-item">
<span class="label">当前使用电量:</span>
<span class="value">{{ selectedElectricalBox?.currentEnergy || '45.5' }} kWh</span>
</div>
<!-- 累计历史数据 -->
<div class="section-title">累计历史数据</div>
<div class="detail-item">
<span class="label">累计历史用电:</span>
<span class="value">{{ selectedElectricalBox?.totalEnergy || '1250.8' }} kWh</span>
</div>
<div class="detail-item">
<span class="label">累计服务次数:</span>
<span class="value">{{ selectedElectricalBox?.serviceCount || '86' }} </span>
</div>
</div>
</div>
</div>
1 month ago
1 month ago
</div>
</template>
1 month ago
</div>
</template>
<script setup lang="ts">
import PublicMapComponents from './components/cesiumMap.vue'
import PortOverview from './components/PortOverview.vue'
import ShorePowerUsage from './components/ShorePowerUsage.vue'
import CompanyShorePower from './components/CompanyShorePower.vue'
import ShipShorePower from './components/ShipShorePower.vue'
1 month ago
import dayjs from 'dayjs'
import { onMounted, onUnmounted, ref, computed, watch } from 'vue'
import { MapApi } from "@/api/shorepower/map";
4 weeks ago
import { RealtimeDeviceData } from '@/types/shorepower'
1 month ago
defineOptions({ name: 'PublicMap' })
4 weeks ago
let getRealtimeIntervalId: NodeJS.Timeout | null = null
1 month ago
const headGroupList = ref<{ value: number, label: string }[]>([
{ value: 0, label: '港区概览' },
{ value: 1, label: '港口岸电使用情况' },
{ value: 2, label: '港口企业岸电使用' },
{ value: 3, label: '船舶岸电使用情况' },
])
const activeHeadGroup = ref<number>(-1)
// 定义子组件类型
interface PublicMapComponentsType {
switchView: (viewName: string) => void
dataWithModels: any[]
1 month ago
switchModelView: (data: any) => void
setElectricalBoxClickCallback: (callback: (data: any) => void) => void
1 month ago
}
const mapComponentRef = ref<PublicMapComponentsType | null>(null)
const selectHeadGroup = async (value: number) => {
if (value === activeHeadGroup.value) {
activeHeadGroup.value = -1;
return
}
activeHeadGroup.value = value
if (value === 1) {
const deviceStatus = await MapApi.getDeviceStatusByIds(totalPowerDeviceId.value)
console.log('deviceStatus', deviceStatus)
}
}
4 weeks ago
1 month ago
// 实时时间显示
const currentTime = ref<string>(dayjs().format('YYYY-MM-DD HH:mm:ss'))
4 weeks ago
// 实时设备采集值
const realtimeDeviceData = ref<RealtimeDeviceData[]>([])
1 month ago
// 自动滚动相关变量
const scrollContainerRef = ref<HTMLElement | null>(null)
let scrollTimer: NodeJS.Timeout | null = null
let scrollTimeout: NodeJS.Timeout | null = null
let userScrollHandler: ((event: Event) => void) | null = null
const scrollSpeed = ref<number>(20) // 滚动速度,越小越快
// 更新时间的函数
const updateTime = () => {
currentTime.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
}
const selectedShip = ref(null)
1 month ago
const selectedElectricalBox = ref(null)
1 month ago
1 month ago
/* 回到初始视角 */
const handleOverviewClick = () => {
mapComponentRef.value?.switchView('overview')
}
/* 切换至模型视角 */
1 month ago
const handleSwitch = (item) => {
console.log(item)
4 weeks ago
// selectedShip.value = item
1 month ago
mapComponentRef.value?.switchModelView(item.modelInstance)
}
1 month ago
// 处理岸电箱点击事件
const handleElectricalBoxClick = (data) => {
activeHeadGroup.value = 4
console.log('岸电箱被点击:', data);
// 设置选中的岸电箱数据
selectedElectricalBox.value = data;
// 这里可以添加更多的处理逻辑,比如切换到特定的视图等
};
1 month ago
1 month ago
// 关闭岸电箱详情面板
const closeElectricalBoxInfo = () => {
activeHeadGroup.value = -1;
selectedElectricalBox.value = null;
};
1 month ago
// 启动定时器更新时间
let timer: NodeJS.Timeout
onMounted(() => {
timer = setInterval(updateTime, 1000)
1 month ago
// 设置岸电箱点击回调
if (mapComponentRef.value) {
mapComponentRef.value.setElectricalBoxClickCallback(handleElectricalBoxClick);
}
1 month ago
})
// 组件卸载时清除所有资源
onUnmounted(() => {
console.log('Unmounting component, cleaning up resources')
// 清除时间更新定时器
if (timer) {
clearInterval(timer)
timer = null
}
// 清除自动滚动定时器
if (scrollTimer) {
clearInterval(scrollTimer)
scrollTimer = null
}
// 清除用户滚动超时
if (scrollTimeout) {
clearTimeout(scrollTimeout)
scrollTimeout = null
}
// 移除用户滚动事件监听器
if (scrollContainerRef.value && userScrollHandler) {
scrollContainerRef.value.removeEventListener('scroll', userScrollHandler)
userScrollHandler = null
}
console.log('All resources cleaned up')
})
// 自动滚动功能
const startAutoScroll = () => {
if (!scrollContainerRef.value) {
console.log('scrollContainerRef is null')
return
}
const container = scrollContainerRef.value
const content = container.querySelector('.ship-data-table')
if (!content) {
console.log('ship-data-table not found')
return
}
const contentHeight = content.offsetHeight
const containerHeight = container.offsetHeight
console.log('Container height:', containerHeight)
console.log('Content height:', contentHeight)
// 如果内容高度小于等于容器高度,不需要滚动
if (contentHeight <= containerHeight) {
console.log('Content height is less than container height, no need to scroll')
return
}
// 清除之前的定时器
if (scrollTimer) {
clearInterval(scrollTimer)
scrollTimer = null
}
if (scrollTimeout) {
clearTimeout(scrollTimeout)
scrollTimeout = null
}
// 移除之前的事件监听器
if (userScrollHandler) {
container.removeEventListener('scroll', userScrollHandler)
userScrollHandler = null
}
// 监听滚动事件,当用户手动滚动时暂停自动滚动
let isUserScrolling = false
userScrollHandler = (event) => {
console.log('User scrolling detected')
isUserScrolling = true
// 清除自动滚动定时器
if (scrollTimer) {
clearInterval(scrollTimer)
scrollTimer = null
}
// 清除之前的超时
if (scrollTimeout) {
clearTimeout(scrollTimeout)
scrollTimeout = null
}
// 3秒后恢复自动滚动
scrollTimeout = setTimeout(() => {
console.log('Resuming auto scroll after user interaction')
isUserScrolling = false
startAutoScroll()
}, 3000)
}
// 添加事件监听器
container.addEventListener('scroll', userScrollHandler)
// 启动自动滚动
scrollTimer = setInterval(() => {
if (isUserScrolling) return
// 计算当前滚动位置
const currentScrollTop = container.scrollTop
const maxScrollTop = contentHeight - containerHeight
// 检查内容是否有变化
if (content.offsetHeight !== contentHeight) {
console.log('Content height changed, restarting scroll')
startAutoScroll()
return
}
console.log('Auto scrolling - Current scroll:', currentScrollTop, 'Max scroll:', maxScrollTop)
if (currentScrollTop >= maxScrollTop) {
// 滚动到顶部
console.log('Reached bottom, scrolling to top')
container.scrollTop = 0
} else {
// 平滑滚动
container.scrollTop += 1
}
}, scrollSpeed.value)
console.log('Auto scroll started')
}
4 weeks ago
const handleGetRealtimeAllData = async () => {
const data = await MapApi.getRealtimeAllData({})
const arrayOfObjects = Object.values(data) as RealtimeDeviceData[]
realtimeDeviceData.value = arrayOfObjects
// console.log('getRealtimeAllData', arrayOfObjects)
}
// 监听组件挂载
onMounted(() => {
// 延迟启动滚动,确保DOM已渲染
handleGetRealtimeAllData()
getRealtimeIntervalId = setInterval(() => {
handleGetRealtimeAllData()
}, 5000)
})
// 监听组件卸载
onUnmounted(() => {
if (getRealtimeIntervalId) {
clearInterval(getRealtimeIntervalId)
getRealtimeIntervalId = null
}
})
1 month ago
// 监听组件挂载
onMounted(() => {
timer = setInterval(updateTime, 1000)
// 延迟启动滚动,确保DOM已渲染
setTimeout(() => {
startAutoScroll()
}, 500)
})
// 监听活跃头部组变化,当切换到第3组时重新启动滚动
watch(
() => activeHeadGroup,
(newValue) => {
if (newValue === 3) {
// 延迟启动滚动,确保DOM已重新渲染
setTimeout(() => {
startAutoScroll()
}, 300)
}
}
)
// 监听子组件数据变化
watch(
() => mapComponentRef.value?.dataWithModels,
(newData) => {
console.log('子组件数据变化:', newData)
},
{ deep: true }
)
// 三个公司的岸电使用对比数据
const companyComparisonData = ref([
{
name: '华能码头',
value: 234277.33,
children: [
{ name: '1泊位', value: 80000 },
{ name: '2泊位', value: 60000 },
{ name: '3泊位', value: 40000 },
{ name: '4泊位', value: 30000 },
{ name: '5泊位', value: 24277.33 }
]
},
{
name: '国投码头',
value: 417203.39,
children: [
{ name: '101泊位', value: 150000 },
{ name: '102泊位', value: 100000 },
{ name: '103泊位', value: 80000 },
{ name: '104泊位', value: 50000 },
{ name: '105泊位', value: 37203.39 },
{ name: '106泊位', value: 5433 },
{ name: '107泊位', value: 34567 },
{ name: '108泊位', value: 50000 },
{ name: '109泊位', value: 50000 },
{ name: '110泊位', value: 50000 },
]
},
{
name: '华电码头(储运)',
value: 191340.14,
children: [
{ name: '11泊位', value: 80000 },
{ name: '12泊位', value: 60000 },
{ name: '13泊位', value: 40000 },
{ name: '14泊位', value: 30000 },
{ name: '15泊位', value: 24277.33 }
]
}
])
// 下拉框选中的公司
const selectedCompany = ref('华能码头')
// 饼图数据
const pieChartData = ref<Array<{ name: string; value: number }>>([])
// 初始化饼图数据
onMounted(async () => {
// 默认选中华能码头的数据
const defaultCompany = companyComparisonData.value.find(company => company.name === selectedCompany.value)
if (defaultCompany && defaultCompany.children) {
pieChartData.value = defaultCompany.children
}
/* console.log('totalPowerDeviceId', totalPowerDeviceId.value)
const deviceStatus = await MapApi.getDeviceStatusByIds(totalPowerDeviceId.value)
console.log('deviceStatus', deviceStatus) */
})
const totalPowerDeviceId = computed(() => {
if (!mapComponentRef.value || !mapComponentRef.value.dataWithModels) {
return []
}
const ids = mapComponentRef.value.dataWithModels
.filter(item => item.modelType === 'ship')
.map(item => item.shorePower?.totalPowerDeviceId)
.filter(id => id !== undefined && id !== null)
// 去重处理
return [...new Set(ids)]
})
4 weeks ago
// 从子组件获取的岸电箱状态数据
const shorePowerStatusData = computed(() => {
if (!mapComponentRef.value || !mapComponentRef.value.dataWithModels) {
return []
}
// 过滤出轮船类的数据
return mapComponentRef.value.dataWithModels
.filter(item => item.modelType === 'electrical_box')
.map((item, index) => {
// 解析数据中的状态信息
let status = ['正常'] // 默认状态
try {
if (item.data && typeof item.data === 'string') {
const dataObj = JSON.parse(item.data)
// 根据实际数据结构提取状态信息
// 这里假设dataObj中有status字段,如果没有则使用默认值
if (dataObj.status) {
status = Array.isArray(dataObj.status) ? dataObj.status : [dataObj.status]
}
} else if (item.data && item.data.status) {
status = Array.isArray(item.data.status) ? item.data.status : [item.data.status]
}
} catch (error) {
console.error('解析船舶状态失败:', error)
}
return {
id: item.id || index + 1,
...item
}
})
})
1 month ago
// 从子组件获取的船舶数据
const shipStatusData = computed(() => {
if (!mapComponentRef.value || !mapComponentRef.value.dataWithModels) {
return []
}
// 过滤出轮船类的数据
return mapComponentRef.value.dataWithModels
.filter(item => item.modelType === 'ship')
.map((item, index) => {
// 解析数据中的状态信息
let status = ['正常'] // 默认状态
try {
if (item.data && typeof item.data === 'string') {
const dataObj = JSON.parse(item.data)
// 根据实际数据结构提取状态信息
// 这里假设dataObj中有status字段,如果没有则使用默认值
if (dataObj.status) {
status = Array.isArray(dataObj.status) ? dataObj.status : [dataObj.status]
}
} else if (item.data && item.data.status) {
status = Array.isArray(item.data.status) ? item.data.status : [item.data.status]
}
} catch (error) {
console.error('解析船舶状态失败:', error)
}
return {
id: item.id || index + 1,
...item
}
})
})
</script>
<style lang="scss">
.cesium-viewer-bottom {
display: none !important;
}
.digital-twin-card--deep-blue {
/* 深空蓝背景 */
background: rgba(8, 15, 30, 0.7);
/* 接近纯黑但带蓝调 */
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
padding: 20px;
border-radius: 2px;
position: relative;
overflow: hidden;
/* 外边框:霓虹蓝 */
border: 1px solid rgba(30, 120, 255, 0.4);
box-shadow:
0 4px 20px rgba(0, 0, 0, 0.5),
inset 0 0 0 1px rgba(30, 120, 255, 0.1);
/* 文字颜色:冷白/青白 */
color: #e6f2ff;
font-family: 'Segoe UI', 'Helvetica Neue', 'IBM Plex Mono', monospace;
}
/* 内发光效果 */
.digital-twin-card--deep-blue::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 16px;
box-shadow: inset 0 0 16px rgba(30, 120, 255, 0.25);
pointer-events: none;
}
/* 悬停:增强光效 + 轻微上浮 */
.digital-twin-card--deep-blue:hover {
border-color: rgba(64, 158, 255, 0.8);
background: rgba(10, 20, 40, 0.85);
transition: all 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
}
/* 可选:顶部高光条(增强“面板”感) */
.digital-twin-card--deep-blue::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, #00b0ff, #0066ff);
opacity: 0.7;
border-radius: 16px 16px 0 0;
}
.card {
// background-color: rgba(10, 130, 170, 0.1);
// padding: 12px;
// border-radius: 4px;
// border: 1px solid rgb(10, 130, 170);
flex: 1;
display: flex;
flex-direction: column;
.search-container {
height: 100%;
width: 200px;
border-radius: 4px;
border: 1px solid rgb(10, 130, 170);
padding: 0 10px;
margin-bottom: 8px;
background-color: rgba(255, 255, 255, 0.1);
}
1 month ago
.card-title {
font-size: 18px;
font-weight: 600;
color: #FFF;
display: flex;
align-items: center;
margin-bottom: 8px;
.vertical-line {
width: 2px;
height: 20px;
background-color: #1296db;
margin-right: 6px;
}
.title-icon {
width: 20px;
height: 20px;
margin-right: 6px;
}
.title-text {
font-size: 18px;
1 month ago
color: #FFF;
font-weight: 500;
}
}
.card-content {
flex: 1;
min-height: 0;
width: 100%;
// padding: 10px;
1 month ago
}
}
/* 船舶数据表格样式 */
.ship-data-table-container .card-content {
1 month ago
height: 100%;
padding: 10px;
}
.ship-data-table-container {
height: 100%;
overflow-y: auto;
position: relative;
scroll-behavior: smooth;
}
/* 自定义滚动条样式 */
.ship-data-table-container::-webkit-scrollbar {
width: 6px;
}
.ship-data-table-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.ship-data-table-container::-webkit-scrollbar-thumb {
background: rgba(17, 138, 237, 0.7);
border-radius: 3px;
}
.ship-data-table-container::-webkit-scrollbar-thumb:hover {
background: rgba(17, 138, 237, 1);
}
.ship-data-table {
width: 100%;
padding: 5px 0;
}
.ship-data-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
transition: background-color 0.3s ease;
}
.ship-data-row:hover {
background-color: rgba(17, 138, 237, 0.2);
}
.ship-data-cell {
font-size: 14px;
color: #fff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ship-name {
width: 20%;
}
.wharf-name {
width: 25%;
}
.berth-number {
width: 20%;
}
.shore-power-status {
width: 35%;
}
.head {
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
width: 100vw;
height: 72px;
top: 0px;
right: 0px;
padding: 12px 24px;
.head-title {
width: 420px;
font-size: 28px;
color: rgba(20, 200, 255, 1);
text-align: center;
font-weight: bold;
text-align: left;
}
.head-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
.head-group-item {
padding: 8px 16px;
background-color: rgba(50, 50, 50, 0.5);
color: #ccc;
border: 1px solid rgb(122, 122, 122);
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
}
.head-group-item.active,
.head-group-item:hover {
color: #FFF;
background-color: rgba(17, 138, 237, 0.4);
}
}
.head-time {
width: 420px;
font-size: 18px;
font-weight: bold;
color: #FFF;
text-align: right;
text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
margin-top: 10px;
}
}
.left {
width: 420px;
padding: 12px;
height: calc(100vh - 72px);
position: absolute;
top: 72px;
left: 0px;
display: flex;
flex-direction: column;
gap: 8px;
1 month ago
overflow-y: auto;
}
.right {
width: 420px;
padding: 12px;
height: calc(100vh - 72px);
position: absolute;
top: 72px;
right: 0px;
display: flex;
flex-direction: column;
gap: 8px;
1 month ago
overflow-y: auto;
}
/* 船舶状态表格样式 */
.ship-table {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 8px;
overflow: hidden;
}
/* 船舶详情样式 */
.ship-detail {
display: flex;
flex-direction: column;
gap: 1px;
}
.detail-item {
display: flex;
justify-content: space-between;
padding: 4px 0;
border-bottom: 1px solid rgba(30, 120, 255, 0.2);
font-size: 24px;
1 month ago
}
.detail-item:last-child {
border-bottom: none;
}
.detail-item .label {
font-weight: 500;
color: #ccc;
flex: 0 0 120px;
}
.detail-item .value {
flex: 1;
text-align: right;
color: #e6f2ff;
word-break: break-all;
}
.no-selection {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #ccc;
font-size: 16px;
}
.ship-table-header {
display: flex;
padding: 10px 15px;
background-color: rgba(17, 138, 237, 0.3);
border-bottom: 1px solid rgba(30, 120, 255, 0.4);
font-weight: bold;
font-size: 18px;
1 month ago
}
.ship-table-body {
flex: 1;
overflow-y: auto;
padding: 5px 0;
}
.ship-table-row {
display: flex;
cursor: pointer;
padding: 8px 8px;
1 month ago
border-bottom: 1px solid rgba(30, 120, 255, 0.2);
transition: background-color 0.2s ease;
}
.ship-table-row:hover {
background-color: rgba(30, 120, 255, 0.1);
}
.ship-table-column {
display: flex;
align-items: center;
}
.ship-name-header {
width: 180px;
}
.ship-status-header {
flex: 1;
justify-content: flex-start;
}
.ship-name {
width: 180px;
gap: 10px;
}
.ship-icon {
font-size: 20px;
flex-shrink: 0;
}
.ship-name-text {
font-size: 18px;
1 month ago
color: #e6f2ff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ship-status {
flex: 1;
gap: 8px;
flex-wrap: wrap;
justify-content: flex-start;
}
.status-tag {
padding: 2px 4px;
border-radius: 4px;
font-size: 16px;
1 month ago
font-weight: 500;
color: #fff;
text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}
.status-normal {
background-color: #4CAF50;
box-shadow: 0 0 5px rgba(76, 175, 80, 0.5);
}
/* 空闲状态 */
.status-idle {
background-color: #2196F3;
/* 蓝色 */
box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
}
/* 故障状态 */
.status-fault {
background-color: #F44336;
/* 红色 */
box-shadow: 0 0 5px rgba(244, 67, 54, 0.5);
}
1 month ago
.status-abnormal {
background-color: #F44336;
box-shadow: 0 0 5px rgba(244, 67, 54, 0.5);
}
.status-maintenance {
background-color: #FF9800;
box-shadow: 0 0 5px rgba(255, 152, 0, 0.5);
}
.status-shorepower {
background-color: #2196F3;
box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
}
/* .status-fault {
1 month ago
background-color: #9C27B0;
box-shadow: 0 0 5px rgba(156, 39, 176, 0.5);
} */
1 month ago
.status-default {
background-color: #607D8B;
box-shadow: 0 0 5px rgba(96, 125, 139, 0.5);
}
/* 滚动条样式 */
.ship-table-body::-webkit-scrollbar {
width: 6px;
}
.ship-table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
}
.ship-table-body::-webkit-scrollbar-thumb {
background: rgba(30, 120, 255, 0.5);
border-radius: 3px;
}
.ship-table-body::-webkit-scrollbar-thumb:hover {
background: rgba(30, 120, 255, 0.7);
}
/* 淡入淡出过渡效果 */
.fade-left-enter-active,
.fade-left-leave-active,
.fade-right-enter-active,
.fade-right-leave-active {
transition: opacity 0.5s ease, transform 0.5s ease;
}
.fade-left-enter-from,
.fade-left-leave-to {
opacity: 0;
transform: translateX(-20px);
}
.fade-right-enter-from,
.fade-right-leave-to {
opacity: 0;
transform: translateX(20px);
}
1 month ago
.overview-grid {
display: grid;
grid-template-columns: 1fr 1fr;
4 weeks ago
gap: 2px;
1 month ago
padding: 2px;
height: 100%;
}
.overview-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
4 weeks ago
// padding: 4px;
1 month ago
background-color: rgba(0, 0, 0, 0.2);
border-radius: 8px;
}
.overview-value {
4 weeks ago
font-size: 24px;
1 month ago
font-weight: bold;
color: #1296db;
text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
4 weeks ago
margin-bottom: 0px;
1 month ago
}
.overview-label {
text-align: center;
4 weeks ago
font-size: 14px;
1 month ago
color: #ccc;
}
/* 状态样式 */
.status-using {
color: #00ff00;
}
.status-no-equipment {
color: #ff6600;
}
.status-damaged {
color: #ff3300;
}
.status-cable {
color: #ffcc00;
}
/* 岸电箱详情样式 */
.section-title {
font-size: 18px;
font-weight: bold;
color: #1296db;
margin: 15px 0 8px 0;
padding-bottom: 5px;
border-bottom: 1px solid rgba(18, 150, 219, 0.3);
}
.detail-item {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
padding: 8px 12px;
border-radius: 6px;
background-color: rgba(0, 0, 0, 0.15);
}
.label {
color: #ddd;
font-size: 16px;
}
.value {
color: #fff;
font-size: 16px;
font-weight: 600;
}
1 month ago
</style>