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.
189 lines
4.7 KiB
189 lines
4.7 KiB
|
1 month ago
|
<template>
|
||
|
|
<div class="ship-shore-power">
|
||
|
|
<div class="left" style="width: 800px;">
|
||
|
|
<div class="card digital-twin-card--deep-blue " style="flex: 1;">
|
||
|
|
<div class="card-title">
|
||
|
|
<div class="vertical-line"></div>
|
||
|
|
<img src="@/assets/svgs/data.svg" class="title-icon" />
|
||
|
|
<span class="title-text">概览</span>
|
||
|
|
</div>
|
||
|
|
<div class="card-content">
|
||
|
|
<div class="overview-grid">
|
||
|
|
<div class="overview-item">
|
||
|
|
<div class="overview-value">{{ berthingShips }}</div>
|
||
|
|
<div class="overview-label">在泊船舶数量</div>
|
||
|
|
</div>
|
||
|
|
<div class="overview-item">
|
||
|
|
<div class="overview-value">{{ shorePowerShips }}</div>
|
||
|
|
<div class="overview-label">使用岸电船舶数量</div>
|
||
|
|
</div>
|
||
|
|
<div class="overview-item">
|
||
|
|
<div class="overview-value">{{ noShorePowerShips }}</div>
|
||
|
|
<div class="overview-label">未使用岸电船舶数量</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card digital-twin-card--deep-blue " style="flex: 3;">
|
||
|
|
<div class="card-title">
|
||
|
|
<div class="vertical-line"></div>
|
||
|
|
<img src="@/assets/svgs/data.svg" class="title-icon" />
|
||
|
|
<span class="title-text">数据</span>
|
||
|
|
</div>
|
||
|
|
<div class="card-content">
|
||
|
|
<div class="ship-data-table-container" ref="scrollContainerRef">
|
||
|
|
<div class="ship-data-table">
|
||
|
|
<div v-for="(ship, index) in shipData" :key="index" class="ship-data-row">
|
||
|
|
<div class="ship-data-cell ship-name">{{ ship.name }}</div>
|
||
|
|
<div class="ship-data-cell wharf-name">{{ ship.wharf }}</div>
|
||
|
|
<div class="ship-data-cell berth-number">{{ ship.berth }}</div>
|
||
|
|
<div class="ship-data-cell shore-power-status" :class="ship.statusClass">{{ ship.status }}</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup lang="ts">
|
||
|
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
||
|
|
|
||
|
|
// 定义组件属性
|
||
|
|
interface ShipDataItem {
|
||
|
|
name: string;
|
||
|
|
wharf: string;
|
||
|
|
berth: string;
|
||
|
|
status: string;
|
||
|
|
statusClass: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
berthingShips: number;
|
||
|
|
shorePowerShips: number;
|
||
|
|
noShorePowerShips: number;
|
||
|
|
shipData: ShipDataItem[];
|
||
|
|
}
|
||
|
|
|
||
|
|
const props = defineProps<Props>()
|
||
|
|
|
||
|
|
// 滚动容器引用
|
||
|
|
const scrollContainerRef = ref<HTMLElement | null>(null)
|
||
|
|
|
||
|
|
// 滚动相关变量
|
||
|
|
let scrollInterval: NodeJS.Timeout | null = null
|
||
|
|
const scrollSpeed = ref<number>(20) // 滚动速度,越小越快
|
||
|
|
|
||
|
|
// 开始滚动
|
||
|
|
const startScroll = () => {
|
||
|
|
if (!scrollContainerRef.value) return
|
||
|
|
|
||
|
|
const container = scrollContainerRef.value
|
||
|
|
const content = container.querySelector('.ship-data-table')
|
||
|
|
if (!content) return
|
||
|
|
|
||
|
|
// 计算滚动距离
|
||
|
|
const scrollDistance = content.offsetHeight - container.clientHeight
|
||
|
|
if (scrollDistance <= 0) return // 内容高度小于容器高度,不需要滚动
|
||
|
|
|
||
|
|
let currentScroll = 0
|
||
|
|
let direction = 1 // 1 向下滚动,-1 向上滚动
|
||
|
|
|
||
|
|
// 清除可能存在的旧定时器
|
||
|
|
if (scrollInterval) {
|
||
|
|
clearInterval(scrollInterval)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 创建新的滚动定时器
|
||
|
|
scrollInterval = setInterval(() => {
|
||
|
|
if (!scrollContainerRef.value) return
|
||
|
|
|
||
|
|
currentScroll += direction * scrollSpeed.value
|
||
|
|
|
||
|
|
// 检查是否到达边界
|
||
|
|
if (currentScroll >= scrollDistance) {
|
||
|
|
currentScroll = scrollDistance
|
||
|
|
direction = -1
|
||
|
|
} else if (currentScroll <= 0) {
|
||
|
|
currentScroll = 0
|
||
|
|
direction = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// 应用滚动
|
||
|
|
scrollContainerRef.value.scrollTop = currentScroll
|
||
|
|
}, 100)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 停止滚动
|
||
|
|
const stopScroll = () => {
|
||
|
|
if (scrollInterval) {
|
||
|
|
clearInterval(scrollInterval)
|
||
|
|
scrollInterval = null
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 监听activeHeadGroup变化
|
||
|
|
watch(
|
||
|
|
() => props.shipData,
|
||
|
|
() => {
|
||
|
|
// 数据变化时重新开始滚动
|
||
|
|
stopScroll()
|
||
|
|
startScroll()
|
||
|
|
},
|
||
|
|
{ deep: true }
|
||
|
|
)
|
||
|
|
|
||
|
|
// 组件挂载时开始滚动
|
||
|
|
onMounted(() => {
|
||
|
|
// 延迟启动滚动,确保DOM已经渲染完成
|
||
|
|
setTimeout(() => {
|
||
|
|
startScroll()
|
||
|
|
}, 500)
|
||
|
|
})
|
||
|
|
|
||
|
|
// 组件销毁前停止滚动
|
||
|
|
onBeforeUnmount(() => {
|
||
|
|
stopScroll()
|
||
|
|
})
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.ship-shore-power {
|
||
|
|
display: flex;
|
||
|
|
height: 100%;
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ship-data-table-container {
|
||
|
|
height: 100%;
|
||
|
|
overflow-y: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ship-data-table {
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ship-data-row {
|
||
|
|
display: flex;
|
||
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.ship-data-cell {
|
||
|
|
flex: 1;
|
||
|
|
padding: 10px;
|
||
|
|
color: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
.shore-power-status {
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
.shore-power-status.status-on {
|
||
|
|
color: #4CAF50;
|
||
|
|
}
|
||
|
|
|
||
|
|
.shore-power-status.status-off {
|
||
|
|
color: #F44336;
|
||
|
|
}
|
||
|
|
</style>
|