DSDSWeb/src/views/statistics/ShipStatistics.vue

547 lines
16 KiB
Vue
Raw Normal View History

<template>
2025-10-31 16:15:28 +08:00
<div class="statistics">
<div class="banner">
2025-10-31 16:15:28 +08:00
<el-dropdown @command="handleCommand">
<span class="titles">{{ currentTitle }}<i class="el-icon-arrow-down el-icon--right" /></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="全部科考船">全部科考船</el-dropdown-item>
<el-dropdown-item
v-for="item in titles"
:key="item.tsy_id"
:command="item.report_name"
>
{{ item.report_name }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="numbers">
<div v-for="(item, index) in numbers" :key="index" class="item">
<div class="value">{{ item.value }}<span class="unit">{{ item.unit }}</span></div>
<div class="label">{{ item.label }}</div>
</div>
</div>
</div>
2025-10-31 16:15:28 +08:00
<!-- 地图板块-->
<div class="map-module">
<div class="bezel">
<div class="map-title">参航单位</div>
2025-11-18 15:33:41 +08:00
<div id="baiduCesiumContainer" ref="baiduCesiumContainer" style="height: 100%;width:100%;" />
</div>
2025-10-31 16:15:28 +08:00
<div class="bezel">
<div class="map-title">航次地图</div>
<el-date-picker
v-model="voyageYear"
type="year"
size="mini"
placeholder="选择年"
class="map-year"
format="yyyy"
@change="selectVoyageYear"
/>
<div id="cesiumContainer" ref="cesiumContainer" style="height: 100%;width:100%;" />
</div>
2025-10-31 16:15:28 +08:00
</div>
<!-- echarts板块-->
<div class="echarts-module">
<div class="bezel">
<BarChart
:title="barData1.title"
:chart-data="barData1.salesData"
:categories="barData1.productCategories"
chart-type="single"
/>
</div>
<div class="bezel">
<BarChart
:title="barData2.title"
:chart-data="barData2.salesData"
:categories="barData2.productCategories"
chart-type="stacked"
stack-name="新增单位数"
/>
</div>
<div class="bezel">
<BarChart
:title="barData3.title"
:chart-data="barData3.salesData"
:categories="barData3.productCategories"
/>
</div>
<div class="bezel">
<BarChart
:title="barData4.title"
:chart-data="barData4.salesData"
:categories="barData4.productCategories"
/>
</div>
<div class="bezel">
<PieChart
:chart-data="pieData"
title="参航单位类型"
chart-type="ring"
/>
</div>
</div>
</div>
</template>
<script>
2025-10-31 16:15:28 +08:00
import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue'
import { setImageryViewModels } from '../../utils/common'
import { reportList, shipTotal, shipUnit, shipUnitYear, shipVoyage } from '../../api/statistics'
import { getYearRange, filterDictItems } from '../../utils/index'
const Cesium = window.Cesium
export default {
components: {
2025-10-31 16:15:28 +08:00
BarChart,
PieChart
},
data() {
return {
2025-10-31 16:15:28 +08:00
currentTitle: '全部科考船',
titles: [],
numbers: [
{
2025-10-31 16:15:28 +08:00
label: '航次',
value: '',
unit: '次'
},
{
2025-10-31 16:15:28 +08:00
label: '航次天数',
value: '',
unit: '天'
},
{
2025-10-31 16:15:28 +08:00
label: '参航单位',
value: '',
unit: '家'
},
{
2025-10-31 16:15:28 +08:00
label: '数据样品个数',
value: '',
unit: '个'
},
{
2025-10-31 16:15:28 +08:00
label: '累计里程',
value: '',
unit: 'KM'
}
],
2025-11-18 15:33:41 +08:00
// Cesium相关 - 参航单位地图
baiduCesiumViewer: null,
baiduCesiumEntities: [], // 存储参航单位标记实体
2025-10-31 16:15:28 +08:00
viewer: null,
// 地图图层基础地址
CesiumHostAddr: `${window.location.protocol}//${window.location.hostname}:${window.location.port}/`,
trackLineLayers: [],
voyageYear: '',
barData1: {},
barData2: {},
barData3: {},
barData4: {},
pieData: []
}
},
2025-10-31 16:15:28 +08:00
created() {
this.getTypeList()
this.getShipList()
this.getShipUnit()
this.gitShipVoyage()
this.gitShipUnitYear()
this.getChartBar({ year: '2025' })
},
mounted() {
2025-10-31 16:15:28 +08:00
this.imageryViewModels = setImageryViewModels(this.CesiumHostAddr)
2025-11-18 15:33:41 +08:00
this.initCesium() // 初始化航次地图
this.initBaiduCesium() // 初始化参航单位地图
},
beforeDestroy() {
// 清理Cesium资源
if (this.baiduCesiumViewer) {
this.baiduCesiumViewer.destroy()
}
if (this.viewer) {
this.viewer.destroy()
}
},
methods: {
2025-10-31 16:15:28 +08:00
// 科考船数据
getTypeList() {
reportList().then(res => {
this.titles = filterDictItems(res.array, '科考船')
})
},
// 选择科考船
handleCommand(command) {
this.currentTitle = command
this.removeSingleLineImg()
if (command === '全部科考船') {
this.getShipList()
this.gitShipVoyage()
this.gitShipUnitYear()
this.getChartBar({ year: '2025' })
} else {
this.getShipList({ ship_name: command })
this.gitShipVoyage({ ship_name: command })
this.gitShipUnitYear({ ship_name: command })
this.getChartBar({ year: '2025', ship_name: command })
}
},
2025-10-31 16:15:28 +08:00
// 汇总统计数据
getShipList(params = {}) {
shipTotal(params).then(res => {
const obj = res.array[0]
this.numbers[0].value = obj.member_total
this.numbers[1].value = obj.days_total
this.numbers[2].value = obj.unit_total
this.numbers[3].value = obj.dataset_total
this.numbers[4].value = obj.mileage_total
})
},
// 参航单位统计
getShipUnit() {
shipUnit().then(res => {
2025-11-18 15:33:41 +08:00
// 清除之前的标记
this.clearBaiduCesiumMarkers()
// 创建参航单位标记
this.createBaiduCesiumMarkers(res.array)
2025-10-31 16:15:28 +08:00
const typeCount = res.array.reduce((acc, unit) => {
const type = unit.unit_type
acc[type] = (acc[type] || 0) + 1 // 如果类型已存在则计数+1否则初始化为1
return acc
}, {})
this.pieData = Object.entries(typeCount).map(([type, count]) => ({
name: type,
value: count
}))
})
},
2025-11-18 15:33:41 +08:00
// 创建参航单位Cesium标记点
createBaiduCesiumMarkers(units) {
if (!this.baiduCesiumViewer) return
const entities = []
units.forEach((item, index) => {
// 创建标记实体
const entity = this.baiduCesiumViewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(item.unit_lon, item.unit_lat),
billboard: {
image: '../../../static/img/01.png', // 正常状态的图标
width: 32,
height: 32,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER
},
label: {
text: item.unit_name,
font: '14pt monospace',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -40), // 标签在标记下方
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK
},
unitData: item, // 存储原始数据
index: index // 存储索引
})
entities.push(entity)
})
this.baiduCesiumEntities = entities
// 调整视角以显示所有标记
if (entities.length > 0) {
this.baiduCesiumViewer.zoomTo(entities)
}
},
// 清除参航单位标记
clearBaiduCesiumMarkers() {
if (this.baiduCesiumViewer && this.baiduCesiumEntities.length > 0) {
this.baiduCesiumEntities.forEach(entity => {
this.baiduCesiumViewer.entities.remove(entity)
})
this.baiduCesiumEntities = []
}
},
2025-10-31 16:15:28 +08:00
// 航次统计
gitShipVoyage(params = {}) {
shipVoyage(params).then(res => {
// 添加轨迹图层
res.array.forEach(item => {
this.addSingleLineImg(item)
})
})
},
2025-11-18 15:33:41 +08:00
// 初始化Cesium - 参航单位地图
initBaiduCesium() {
this.baiduCesiumViewer = new Cesium.Viewer(this.$refs.baiduCesiumContainer, {
sceneMode: Cesium.SceneMode.SCENE2D,
shadows: false,
timeline: false,
baseLayerPicker: false,
imageryProviderViewModels: this.imageryViewModels,
terrainProviderViewModels: [],
fullscreenButton: false,
selectionIndicator: false,
homeButton: false,
sceneModePicker: false,
animation: false,
infoBox: false,
geocoder: false,
navigationHelpButton: false
})
this.baiduCesiumViewer._cesiumWidget._creditContainer.style.display = 'none'
this.baiduCesiumViewer.scene.sun.show = false
this.baiduCesiumViewer.scene.moon.show = false
this.baiduCesiumViewer.scene.fog.enabled = false
this.baiduCesiumViewer.scene.skyAtmosphere.show = false
this.baiduCesiumViewer.scene.sun.show = false
this.baiduCesiumViewer.scene.skyBox.show = false
this.baiduCesiumViewer.scene.globe.enableLighting = false
this.baiduCesiumViewer.shadowMap.darkness = 0.8
this.baiduCesiumViewer.scene._sunBloom = false
this.baiduCesiumViewer.scene.globe.showGroundAtmosphere = false
this.baiduCesiumViewer.scene.postProcessStages.fxaa.enabled = true
this.baiduCesiumViewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(114.4040, 30.5196, 4000000)
2025-10-31 16:15:28 +08:00
})
},
2025-10-31 16:15:28 +08:00
// 初始化cesium
initCesium() {
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkY2Y5NWY2Ni1mODQ1LTQ3YTUtYjc4Zi1jYzhjMGI2YzcxMWYiLCJpZCI6MTI5ODMwLCJpYXQiOjE2Nzk0Njk5NTd9.DTH54ioOH-HLqeNIetBe9hFyrPOX2Vp1AQmZzw8TIZ4'
this.viewer = new Cesium.Viewer('cesiumContainer', {
sceneMode: Cesium.SceneMode.SCENE2D,
shadows: false,
timeline: false,
baseLayerPicker: false,
// 设置自定义底图
imageryProviderViewModels: this.imageryViewModels,
// 关闭默认的地形底图
terrainProviderViewModels: [],
fullscreenButton: false,
selectionIndicator: false,
homeButton: false,
sceneModePicker: false,
animation: false,
infoBox: false,
geocoder: false,
navigationHelpButton: false
})
this.viewer._cesiumWidget._creditContainer.style.display = 'none'
this.viewer.scene.sun.show = false
this.viewer.scene.moon.show = false
this.viewer.scene.fog.enabled = false
this.viewer.scene.skyAtmosphere.show = false
this.viewer.scene.sun.show = false
this.viewer.scene.skyBox.show = false
this.viewer.scene.globe.enableLighting = false
this.viewer.shadowMap.darkness = 0.8
this.viewer.scene._sunBloom = false
this.viewer.scene.globe.showGroundAtmosphere = false
this.viewer.scene.postProcessStages.fxaa.enabled = true
this.viewer.camera.setView({
// 指定相机初始位置
destination: Cesium.Cartesian3.fromDegrees(112, 18, 4000000)
});
},
2025-10-31 16:15:28 +08:00
// 添加轨迹图层
addSingleLineImg(item) {
const trackLine = new Cesium.WebMapTileServiceImageryProvider(
{
url: `${this.CesiumHostAddr}geoserver/gwc/service/wmts/rest/dsds:${item.voyage_name}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png`,
layer: `dsds:${item.voyage_name}`,
style: 'default',
format: 'image/png',
tileMatrixSetID: 'EPSG:4326',
tilingScheme: new Cesium.GeographicTilingScheme()
}
2025-10-31 16:15:28 +08:00
)
const trackLineLayer = this.viewer.imageryLayers.addImageryProvider(trackLine)
this.trackLineLayers.push(trackLineLayer)
},
// 移除轨迹图层
removeSingleLineImg() {
if (this.trackLineLayers.length) {
this.trackLineLayers.forEach(line => {
this.viewer.imageryLayers.remove(line)
})
}
},
2025-10-31 16:15:28 +08:00
// 选择航次地图年份
selectVoyageYear(val) {
const shipName = this.currentTitle === '全部科考船' ? '' : this.currentTitle
if (val) {
this.removeSingleLineImg()
const params = getYearRange(val)
this.gitShipVoyage({ ...params, ship_name: shipName })
} else {
this.gitShipVoyage({ ship_name: shipName })
}
2025-10-31 16:15:28 +08:00
},
// 参航单位年度
gitShipUnitYear(params = {}) {
shipUnitYear(params).then(res => {
const originalData = res.array
const sortedData = originalData.sort((a, b) => a.voyage_year - b.voyage_year)
this.barData2 = {
title: '年度参航单位',
salesData: [
{
name: '参航单位',
data: sortedData.map(item => item.unit_total),
stack: '新增单位数'
},
{
name: '新增单位',
data: sortedData.map(item => item.year_unit_total),
stack: '新增单位数'
}
],
productCategories: sortedData.map(item => item.voyage_year.toString())
}
})
},
// 柱状图数据
getChartBar(params) {
shipTotal(params).then(res => {
const originalData = res.array
const sortedData = originalData.sort((a, b) => a.year - b.year)
this.barData1 = {
title: '年度航次数',
salesData: sortedData.map(item => item.member_total),
productCategories: sortedData.map(item => item.year.toString())
}
this.barData3 = {
title: '样品集个数',
salesData: sortedData.map(item => item.dataset_total),
productCategories: sortedData.map(item => item.year.toString())
}
this.barData4 = {
title: '里程数',
salesData: sortedData.map(item => item.mileage_total),
productCategories: sortedData.map(item => item.year.toString())
}
})
}
}
}
</script>
<style scoped lang="scss">
2025-10-31 16:15:28 +08:00
.statistics {
background-color: #ebf1f7;
.banner {
2025-10-31 16:15:28 +08:00
background-image: url(../../../static/images/bannerny1.jpg);
width: 100%;
2025-10-31 16:15:28 +08:00
height: 480px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
.titles {
margin-bottom: 40px;
font-size: 56px;
color: #fff;
font-weight: bold;
cursor: pointer;
}
2025-10-31 16:15:28 +08:00
.numbers {
display: flex;
2025-10-31 16:15:28 +08:00
justify-content: space-between;
gap: 80px;
.item {
display: flex;
flex-direction: column;
justify-content: center;
2025-10-31 16:15:28 +08:00
align-items: center;
.value {
font-size: 56px;
font-weight: bold;
2025-10-31 16:15:28 +08:00
.unit {
font-size: 26px;
}
}
2025-10-31 16:15:28 +08:00
.label {
font-size: 18px;
}
}
}
}
2025-10-31 16:15:28 +08:00
.map-module {
padding: 50px 50px;
display: flex;
justify-content: space-between;
2025-10-31 16:15:28 +08:00
gap: 50px;
.bezel {
flex: 1;
width: 100%;
height: 520px;
border-radius: 10px;
overflow: hidden;
2025-10-31 16:15:28 +08:00
border: 1px solid #ccc;
position: relative;
.map-title {
position: absolute;
top: 20px;
left: 20px;
2025-10-31 16:15:28 +08:00
color: white;
z-index: 8888;
font-size: 22px;
}
2025-10-31 16:15:28 +08:00
.map-year {
position: absolute;
top: 20px;
right: 20px;
color: white;
z-index: 8888;
width: 100px;
}
}
}
.echarts-module {
padding: 0 50px 50px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 50px;
grid-auto-rows: 300px;
.bezel {
width: 100%;
height: 100%;
border-radius: 10px;
overflow: hidden;
border: 1px solid #ccc;
position: relative;
.map-title {
position: absolute;
top: 20px;
left: 20px;
color: white;
z-index: 8888;
font-size: 22px;
}
}
}
}
2025-10-31 16:15:28 +08:00
.el-icon-arrow-down {
font-size: 12px
}
</style>