703 lines
20 KiB
Vue
703 lines
20 KiB
Vue
<template>
|
|
<div class="station-operation">
|
|
<!-- 左侧列表 -->
|
|
<div :class="'data-container' + (!leftPaneShow ? ' hide' : '')">
|
|
<a-collapse v-model="activeKey" expandIconPosition="right" accordion>
|
|
<template slot="expandIcon">
|
|
<img src="@/assets/images/station-operation/toggle.png" alt="" />
|
|
</template>
|
|
<!-- All Data -->
|
|
<a-collapse-panel key="1">
|
|
<template slot="header">
|
|
<div class="title">
|
|
<div class="title-text">All Data</div>
|
|
<div class="title-rect">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
|
|
<!-- 头部操作栏 -->
|
|
<div class="title-operator">
|
|
<div @click.stop="handleShowSearch">
|
|
<img src="@/assets/images/station-operation/search.png" alt="" />
|
|
</div>
|
|
<div @click.stop="handleShowFilter">
|
|
<img src="@/assets/images/station-operation/filter.png" alt="" />
|
|
</div>
|
|
</div>
|
|
<!-- 头部操作栏结束 -->
|
|
</div>
|
|
</template>
|
|
<ScrollContainer
|
|
ref="scrollContainerRef"
|
|
direction="verticle"
|
|
:scrollContainer="getScrollContainer"
|
|
class="data-list has-search"
|
|
:class="{
|
|
'show-search': searchPlacementVisible
|
|
}"
|
|
>
|
|
<div class="search-filter-placement">
|
|
<!-- 搜索 -->
|
|
<a-input-search
|
|
v-if="searchVisible"
|
|
placeholder="Input Search Text"
|
|
v-model="filter.searchText"
|
|
allow-clear
|
|
@search="onFilterChange"
|
|
>
|
|
<img slot="enterButton" src="@/assets/images/station-operation/search.png" alt="" />
|
|
</a-input-search>
|
|
<!-- 搜索结束 -->
|
|
|
|
<!-- 筛选 -->
|
|
<a-row v-if="filterVisible" :gutter="10" style="width: 100%">
|
|
<a-col :span="12">
|
|
<j-dict-select-tag
|
|
v-model="filter.status"
|
|
:getPopupContainer="getDictSelectTagContainer"
|
|
placeholder="Select Status"
|
|
dictCode="STATION_STATUS"
|
|
style="width: 100%"
|
|
@change="onFilterChange"
|
|
></j-dict-select-tag>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<custom-select
|
|
v-model="filter.type"
|
|
:options="stationTypeList"
|
|
allow-clear
|
|
show-search
|
|
@change="onFilterChange"
|
|
placeholder="Select Type"
|
|
></custom-select>
|
|
</a-col>
|
|
</a-row>
|
|
<!-- 筛选结束 -->
|
|
</div>
|
|
<div class="data-list-content">
|
|
<a-spin v-if="isGettingDataList"></a-spin>
|
|
<custom-empty v-if="!isGettingDataList && !dataList.length" style="margin-top: 40px"></custom-empty>
|
|
<RecycleScroller
|
|
ref="customScrollContainerRef"
|
|
class="scroller"
|
|
:items="dataList"
|
|
:item-size="129"
|
|
key-field="stationId"
|
|
v-slot="{ item }"
|
|
>
|
|
<DataListItem :item="item" @click.native="locateFacility(item)"></DataListItem>
|
|
</RecycleScroller>
|
|
</div>
|
|
<div class="shadow"></div>
|
|
</ScrollContainer>
|
|
</a-collapse-panel>
|
|
<!-- All Data 结束 -->
|
|
<!-- Focus Data -->
|
|
<a-collapse-panel key="2">
|
|
<template slot="header">
|
|
<div class="title">
|
|
<div class="title-text" style="width: 180px">Focus Data</div>
|
|
<div class="title-rect">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<ScrollContainer ref="scrollContainer2Ref" direction="verticle" class="data-list">
|
|
<div class="data-list-content">
|
|
<a-spin v-if="isGettingFollowedDataList"></a-spin>
|
|
<template v-else>
|
|
<DataListItem
|
|
v-for="item of followedDataList"
|
|
:key="item.id"
|
|
:item="item"
|
|
@click.native="locateFacility(item)"
|
|
></DataListItem>
|
|
<custom-empty v-if="!followedDataList.length" style="margin-top: 40px"></custom-empty>
|
|
</template>
|
|
</div>
|
|
<div class="shadow"></div>
|
|
</ScrollContainer>
|
|
</a-collapse-panel>
|
|
<!-- Focus Data 结束 -->
|
|
</a-collapse>
|
|
|
|
<!-- 展开左侧配置栏按钮开始 -->
|
|
<div class="toggle-show-btn" @click="leftPaneShow = !leftPaneShow">
|
|
<a-icon v-if="leftPaneShow" type="menu-fold" />
|
|
<a-icon v-else type="menu-unfold" />
|
|
</div>
|
|
<!-- 展开左侧配置栏按钮结束 -->
|
|
</div>
|
|
<!-- 右侧地图 -->
|
|
<div class="station-operation-map">
|
|
<Map
|
|
ref="mapRef"
|
|
token="AAPK2b935e8bbf564ef581ca3c6fcaa5f2a71ZH84cPqqFvyz3KplFRHP8HyAwJJkh6cnpcQ-qkWh5aiyDQsGJbsXglGx0QM2cPm"
|
|
>
|
|
<MapMarker
|
|
:list="markerList"
|
|
:currList="upDateStationList"
|
|
:orgList="orgStationList"
|
|
:marker-type="markerType"
|
|
:radius="circleRadius"
|
|
@markerClick="onMarkerClick"
|
|
/>
|
|
<MapPane
|
|
ref="mapPane"
|
|
:treeData="treeData"
|
|
@changeMarker="onChangeMarker"
|
|
@filterMarker="onFilterMarker"
|
|
@drawCircle="onDrawCircle"
|
|
/>
|
|
</Map>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import Map from './components/Map.vue'
|
|
import MapMarker from './components/MapMarker.vue'
|
|
import MapPane from './components/MapPane.vue'
|
|
import DataListItem from './components/DataListItem.vue'
|
|
import ScrollContainer from '@/components/ScrollContainer/index.vue'
|
|
import { getAction } from '../../api/manage'
|
|
import { cloneDeep } from 'lodash'
|
|
const key= "updateList"
|
|
export default {
|
|
components: {
|
|
Map,
|
|
MapMarker,
|
|
MapPane,
|
|
ScrollContainer,
|
|
DataListItem
|
|
},
|
|
data() {
|
|
return {
|
|
activeKey: '1',
|
|
|
|
isGettingDataList: false,
|
|
isGettingFollowedDataList: false,
|
|
|
|
dataList: [], // 左侧All Data 列表
|
|
followedDataList: [], // 关注
|
|
markerList: [], // 地图上标记点列表
|
|
markerList_clone: [], // 地图上标记点列表
|
|
upDateStationList: [], // 地图上标记点列表
|
|
orgStationList: [], // 地图上标记点列表
|
|
markerType: 1, // 是否绘制地图上的圆
|
|
circleRadius: 0,
|
|
|
|
searchPlacementVisible: false, // 搜索栏占位是否显示
|
|
|
|
searchVisible: false, // 搜索组件是否显示
|
|
|
|
filter: {
|
|
searchText: undefined,
|
|
status: undefined,
|
|
type: undefined
|
|
},
|
|
|
|
filterVisible: false, // 筛选组件是否显示
|
|
leftPaneShow: true, // 左侧面板是否展示
|
|
|
|
stationTypeList: [],
|
|
|
|
treeData: [], // 台站树列表
|
|
timer: null
|
|
}
|
|
},
|
|
created() {
|
|
this.getStationList()
|
|
this.getFollowedStationList()
|
|
this.getStationTypeList()
|
|
this.getStationTree()
|
|
},
|
|
destroyed () {
|
|
clearInterval(this.timer);
|
|
this.timer = null
|
|
},
|
|
methods: {
|
|
// 获取站点列表
|
|
async getStationList() {
|
|
try {
|
|
this.isGettingDataList = true
|
|
const res = await getAction('/jeecg-station-operation/stationOperation/findList')
|
|
this.originalDataList = res // 保留初始版本
|
|
this.dataList = cloneDeep(res)
|
|
this.markerList = cloneDeep(res)
|
|
this.markerList_clone = cloneDeep(res)
|
|
this.getDataProvisionEfficiency(this.markerList_clone,"one")
|
|
this.timer = setInterval(() => {
|
|
setTimeout(() => {
|
|
this.getDataProvisionEfficiency(this.markerList_clone)
|
|
}, 0);
|
|
}, 15000);
|
|
this.$nextTick(() => {
|
|
this.$refs.scrollContainerRef.checkScrollEnd()
|
|
})
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
this.isGettingDataList = false
|
|
}
|
|
},
|
|
|
|
// 查询台站数据提供率及有效率
|
|
async getDataProvisionEfficiency(arr, str) {
|
|
if (str) {
|
|
this.$message.loading({ content: 'Loading station data, please wait...', key, duration: 0 })
|
|
}
|
|
getAction('/stationOperation/getDataProvisionEfficiency').then(res => {
|
|
if (res.success) {
|
|
this.$message.destroy()
|
|
str&&this.$message.success({ content: 'Loaded!', key, duration: 2 })
|
|
res.result.forEach(item => {
|
|
arr.forEach(el => {
|
|
if (parseInt(item.id) == el.stationId&&el.stationType!="Nuclear Facility"&&el.stationType!="NRL") {
|
|
item.stationType = el.stationType
|
|
item.stationId = el.stationId
|
|
}
|
|
})
|
|
})
|
|
this.orgStationList = res.result
|
|
this.upDateStationList = res.result
|
|
} else {
|
|
this.$message.warning("This operation fails. Contact your system administrator")
|
|
}
|
|
}).catch(error => {
|
|
this.$message.destroy()
|
|
console.error(error)
|
|
})
|
|
},
|
|
|
|
// 获取已关注站点列表
|
|
async getFollowedStationList() {
|
|
try {
|
|
this.isGettingFollowedDataList = true
|
|
const res = await getAction('/jeecg-station-operation/sysUserFocusStation/findList')
|
|
this.followedDataList = res
|
|
|
|
const scrollContainer2Ref = this.$refs.scrollContainer2Ref
|
|
if (scrollContainer2Ref) {
|
|
this.$nextTick(() => {
|
|
scrollContainer2Ref.checkScrollEnd()
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
this.isGettingFollowedDataList = false
|
|
}
|
|
},
|
|
|
|
// 获取站点类型
|
|
async getStationTypeList() {
|
|
try {
|
|
const res = await getAction('/jeecg-station-operation/stationOperation/findStationType')
|
|
this.stationTypeList = res.filter(item => item).map(item => ({ label: item, value: item }))
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
},
|
|
|
|
// 获取 台站树列表
|
|
async getStationTree() {
|
|
try {
|
|
const { success, result, message } = await getAction('/stationOperation/findTree')
|
|
if (success) {
|
|
result.forEach(item => {
|
|
item.stationCode = item.code
|
|
item.stationId = `root_${item.stationId}`
|
|
item.children.forEach(child => {
|
|
child.stationCode = child.code
|
|
child.stationId = `${item.stationId}_parent_${child.stationId}`
|
|
})
|
|
})
|
|
this.treeData = result
|
|
} else {
|
|
this.$message.error(message)
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
},
|
|
|
|
// 显示搜索栏
|
|
handleShowSearch() {
|
|
this.activeKey = 1
|
|
if (this.filterVisible) {
|
|
this.searchVisible = true
|
|
this.filterVisible = false
|
|
this.searchPlacementVisible = true
|
|
} else {
|
|
this.searchPlacementVisible = !this.searchPlacementVisible
|
|
if (this.searchPlacementVisible) {
|
|
this.searchVisible = true
|
|
}
|
|
}
|
|
},
|
|
|
|
// 显示筛选栏
|
|
handleShowFilter() {
|
|
this.activeKey = 1
|
|
if (this.searchVisible) {
|
|
this.searchVisible = false
|
|
this.filterVisible = true
|
|
this.searchPlacementVisible = true
|
|
} else {
|
|
this.searchPlacementVisible = !this.searchPlacementVisible
|
|
if (this.searchPlacementVisible) {
|
|
this.filterVisible = true
|
|
}
|
|
}
|
|
},
|
|
|
|
// 左侧 All Data 筛选
|
|
onFilterChange() {
|
|
this.dataList = this.originalDataList.filter(dataItem => {
|
|
const filterSearchText =
|
|
!this.filter.searchText ||
|
|
-1 !== dataItem.stationName.toLowerCase().indexOf(this.filter.searchText.toLowerCase())
|
|
const filterStatus = !this.filter.status || this.filter.status == dataItem.status
|
|
const filterType = !this.filter.type || this.filter.type == dataItem.stationType
|
|
return filterSearchText && filterStatus && filterType
|
|
})
|
|
|
|
this.$nextTick(() => {
|
|
this.$refs.scrollContainerRef.checkScrollEnd()
|
|
})
|
|
},
|
|
|
|
// 定位台站
|
|
locateFacility(stationItem) {
|
|
const find = this.markerList.find(
|
|
markerItem => markerItem.stationId == stationItem.stationId && markerItem.stationType == stationItem.stationType
|
|
)
|
|
if (!find) {
|
|
// 如果未显示,则不定位
|
|
return
|
|
}
|
|
const { lon, lat, stationType, stationId } = stationItem
|
|
this.$refs.mapRef.panTo([lon, lat])
|
|
|
|
setTimeout(() => {
|
|
// 在panTo结束后执行动画
|
|
const overlays = this.$refs.mapRef.map.getOverlays()
|
|
const currOverlay = overlays.getArray().find(item => item.id == `marker_${stationType}_${stationId}`)
|
|
const innerEle = currOverlay.getElement()
|
|
innerEle.classList.add('ani-bounding')
|
|
|
|
function listener() {
|
|
this.classList.remove('ani-bounding')
|
|
this.removeEventListener('animationend', listener)
|
|
}
|
|
|
|
// 执行动画完毕,移除动画类
|
|
innerEle.addEventListener('animationend', listener)
|
|
}, 1000)
|
|
},
|
|
|
|
// 修改地图上的marker列表
|
|
onChangeMarker(markerList) {
|
|
this.markerList = markerList
|
|
},
|
|
|
|
// 是否绘制圆圈
|
|
onDrawCircle({ markerType, radius }) {
|
|
this.markerType = markerType
|
|
this.circleRadius = radius
|
|
},
|
|
|
|
/**
|
|
* 根据类型筛选地图上的marker列表
|
|
*/
|
|
onFilterMarker({ filterType, filterDataQuality }) {
|
|
console.log(
|
|
'%c [ filterType, filterDataQuality ]-343',
|
|
'font-size:13px; background:pink; color:#bf2c9f;',
|
|
filterType,
|
|
filterDataQuality
|
|
)
|
|
this.markerList = this.originalDataList.filter(item => filterType.includes(item.stationType))
|
|
this.upDateStationList = this.orgStationList.filter(item => filterType.includes(item.stationType))
|
|
console.log("ertyuiodfghj",this.upDateStationList);
|
|
},
|
|
|
|
// 地图图标点击
|
|
onMarkerClick() {
|
|
this.$refs.mapPane.handleOpenAnalyzeModal()
|
|
},
|
|
|
|
getScrollContainer() {
|
|
return this.$refs.customScrollContainerRef.$el
|
|
},
|
|
|
|
getDictSelectTagContainer() {
|
|
return document.body
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style lang="less" scoped>
|
|
.station-operation {
|
|
position: relative;
|
|
height: 100%;
|
|
.data-container {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 348px;
|
|
height: 100%;
|
|
background-color: rgba(2, 26, 29, 0.9);
|
|
padding-left: 5px;
|
|
z-index: 1;
|
|
transition: transform 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
|
|
|
|
&.hide {
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.toggle-show-btn {
|
|
position: absolute;
|
|
right: -40px;
|
|
top: 10px;
|
|
cursor: pointer;
|
|
font-size: 20px;
|
|
width: 30px;
|
|
height: 30px;
|
|
line-height: 30px;
|
|
text-align: center;
|
|
color: #00e9fe;
|
|
background-color: #021a1d;
|
|
}
|
|
|
|
::v-deep {
|
|
.ant-collapse {
|
|
height: 100%;
|
|
padding-top: 10px;
|
|
padding-bottom: 11px;
|
|
|
|
@titleHeight: 45px;
|
|
&-item {
|
|
max-height: calc(100% - @titleHeight);
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
&-active {
|
|
.ant-collapse-arrow {
|
|
transform: translateY(-50%) rotate(-90deg);
|
|
}
|
|
}
|
|
}
|
|
|
|
&-header {
|
|
min-height: @titleHeight;
|
|
border-top: 1px solid rgba(12, 235, 201, 0.3);
|
|
border-bottom: 4px solid rgba(12, 235, 201, 0.2);
|
|
height: auto;
|
|
background-color: rgba(1, 18, 20, 0.6);
|
|
padding-right: 25px;
|
|
|
|
.ant-collapse-arrow {
|
|
right: 9px;
|
|
transition: transform 0.24s;
|
|
}
|
|
}
|
|
|
|
&-content {
|
|
display: flex;
|
|
&-box {
|
|
display: flex;
|
|
width: 100%;
|
|
position: relative;
|
|
}
|
|
&.ant-motion-collapse-legacy {
|
|
.data-list {
|
|
overflow: hidden;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 左侧大标题
|
|
.title {
|
|
line-height: 40px;
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-family: MicrogrammaD-MediExte;
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #0cebc9;
|
|
|
|
&-text {
|
|
padding-left: 20px;
|
|
width: 134px;
|
|
background-color: rgba(12, 235, 201, 0.05);
|
|
user-select: none;
|
|
}
|
|
|
|
&-rect {
|
|
display: flex;
|
|
align-items: center;
|
|
span {
|
|
background-color: rgba(12, 235, 201, 0.2);
|
|
vertical-align: middle;
|
|
&:first-child {
|
|
width: 4px;
|
|
height: 4px;
|
|
}
|
|
&:nth-child(2) {
|
|
width: 6px;
|
|
height: 6px;
|
|
margin-right: 6px;
|
|
}
|
|
&:nth-child(3) {
|
|
width: 4px;
|
|
height: 4px;
|
|
margin-right: 9px;
|
|
}
|
|
&:nth-child(4) {
|
|
width: 1px;
|
|
height: 16px;
|
|
margin-right: 23px;
|
|
}
|
|
&:nth-child(5) {
|
|
width: 2px;
|
|
height: 2px;
|
|
margin-right: 24px;
|
|
}
|
|
&:nth-child(6) {
|
|
width: 4px;
|
|
height: 4px;
|
|
}
|
|
}
|
|
}
|
|
|
|
&-operator {
|
|
position: absolute;
|
|
right: 5px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
user-select: none;
|
|
|
|
div {
|
|
width: 24px;
|
|
height: 24px;
|
|
line-height: 18px;
|
|
text-align: center;
|
|
border: 1px solid #166464;
|
|
background-color: #053842;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 左侧列表内容
|
|
.data-list {
|
|
width: 100%;
|
|
margin-right: 2px;
|
|
padding: 2px 0 10px 7px;
|
|
overflow: auto;
|
|
|
|
@searchHeight: 45px;
|
|
.search-filter-placement {
|
|
height: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
transition: height 0.3s ease-in-out;
|
|
overflow: hidden;
|
|
|
|
.ant-input-search {
|
|
margin-right: 10px;
|
|
}
|
|
}
|
|
|
|
&-content {
|
|
.ant-spin {
|
|
width: 100%;
|
|
height: 100px;
|
|
text-align: center;
|
|
margin-top: 90px;
|
|
}
|
|
}
|
|
|
|
&.has-search {
|
|
.data-list-content {
|
|
transition: height 0.3s ease-in-out;
|
|
height: 100%;
|
|
overflow: auto;
|
|
}
|
|
}
|
|
|
|
&.show-search {
|
|
.search-filter-placement {
|
|
height: @searchHeight;
|
|
}
|
|
|
|
.data-list-content {
|
|
height: calc(100% - @searchHeight);
|
|
}
|
|
}
|
|
|
|
.shadow {
|
|
position: absolute;
|
|
bottom: 0;
|
|
width: calc(100% - 15px);
|
|
height: 50px;
|
|
background: linear-gradient(to top, #021c21 0, transparent 100%);
|
|
transition: opacity 0.24s linear;
|
|
pointer-events: none;
|
|
}
|
|
&.scroll-end {
|
|
.shadow {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 地图
|
|
&-map {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
}
|
|
|
|
.scroller {
|
|
height: 100%;
|
|
}
|
|
</style>
|
|
<style>
|
|
.ani-bounding {
|
|
animation: bounding 1s cubic-bezier(0.075, 0.82, 0.165, 1) 3;
|
|
}
|
|
|
|
@keyframes bounding {
|
|
0% {
|
|
transform: translateY(0);
|
|
}
|
|
50% {
|
|
transform: translateY(-10px);
|
|
}
|
|
100% {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
</style>
|