重构
This commit is contained in:
@ -181,6 +181,15 @@ export function createTicketNo(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 告警确认关闭
|
||||
export function closeAlarm(data) {
|
||||
return request({
|
||||
url: `/ems/siteAlarm/closeAlarm`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
function getFieldNameByCode(fieldCode) {
|
||||
const raw = String(fieldCode || '').trim()
|
||||
if (!raw) return ''
|
||||
|
||||
@ -100,6 +100,9 @@ import {mapGetters} from "vuex"
|
||||
onSubmit(){
|
||||
this.$emit('submitSite',this.id)
|
||||
},
|
||||
emitSitesLoaded() {
|
||||
this.$emit('sitesLoaded', this.siteList || [])
|
||||
},
|
||||
setDefaultSite(){
|
||||
const defaultSite = this.defaultSiteId
|
||||
if(defaultSite && this.siteList.find(item=>item.siteId === defaultSite)){
|
||||
@ -112,6 +115,7 @@ import {mapGetters} from "vuex"
|
||||
getList(){
|
||||
return getAllSites().then(response => {
|
||||
this.siteList = response.data || []
|
||||
this.emitSitesLoaded()
|
||||
this.setDefaultSite()
|
||||
}).finally(() => {this.loading=false;this.searchLoading=false})
|
||||
}
|
||||
@ -127,6 +131,7 @@ import {mapGetters} from "vuex"
|
||||
})
|
||||
}else{
|
||||
this.siteList = this.zdList
|
||||
this.emitSitesLoaded()
|
||||
this.loading=false
|
||||
this.searchLoading=false
|
||||
this.setDefaultSite()
|
||||
|
||||
@ -94,13 +94,21 @@
|
||||
<el-table-column
|
||||
label="工单"
|
||||
fixed="right"
|
||||
width="250"
|
||||
width="320"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" v-if="scope.row.ticketNo" @click="toTicket">
|
||||
已生成工单(工单号:{{ scope.row.ticketNo }})
|
||||
</el-button>
|
||||
<el-button type="primary" size="mini" v-else @click="createTicket(scope.row.id)">生成工单</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status !== '1'"
|
||||
type="success"
|
||||
size="mini"
|
||||
style="margin-left: 8px;"
|
||||
@click="closeAlarmRecord(scope.row.id)">
|
||||
确认关闭
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -123,7 +131,7 @@
|
||||
|
||||
|
||||
<script>
|
||||
import {createTicketNo, getAlarmDetailList} from '@/api/ems/dzjk'
|
||||
import {closeAlarm, createTicketNo, getAlarmDetailList} from '@/api/ems/dzjk'
|
||||
import {getDeviceList} from '@/api/ems/site'
|
||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||
import {formatDate} from '@/filters/ems'
|
||||
@ -169,6 +177,23 @@ export default {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
//确认关闭告警
|
||||
closeAlarmRecord(id) {
|
||||
this.$confirm('确认关闭该故障告警吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.loading = true
|
||||
closeAlarm({id}).then(() => {
|
||||
this.$message.success('关闭成功')
|
||||
this.getData()
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
// 判断是否是同一天
|
||||
isSameDay(day1, day2) {
|
||||
const date1 = new Date(day1), date2 = new Date(day2)
|
||||
@ -250,4 +275,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -89,95 +89,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
:data="baseInfo.batteryDataList"
|
||||
class="common-table"
|
||||
max-height="500"
|
||||
stripe
|
||||
style="width: 100%;margin-top:25px;">
|
||||
<el-table-column
|
||||
label="簇号"
|
||||
prop="clusterId">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="簇电压"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'clusterVoltage', '簇电压')">{{ scope.row.clusterVoltage }} V</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="簇电流">
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'clusterCurrent', '簇电流')">{{ scope.row.clusterCurrent }} A</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="簇SOC">
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'currentSoc', '当前SOC')">{{ scope.row.currentSoc }} %</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="单体最高电压"
|
||||
prop="maxVoltage">
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'maxCellVoltage', '最高单体电压')">{{
|
||||
scope.row.maxCellVoltage
|
||||
}} V</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="电池号码"
|
||||
prop="maxCellVoltageId">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="单体最低电压"
|
||||
prop="minVoltage">
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'minCellVoltage', '最低单体电压')">{{
|
||||
scope.row.minCellVoltage
|
||||
}} V</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="电池号码"
|
||||
prop="minCellVoltageId">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="单体最高温度">
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'maxCellTemp', '最高单体温度')">{{
|
||||
scope.row.maxCellTemp
|
||||
}} ℃</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="电池号码"
|
||||
prop="maxCellTempId">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="单体最低温度"
|
||||
prop="minTemperature">
|
||||
<template slot-scope="scope">
|
||||
<span class="pointer"
|
||||
@click="handleClusterFieldClick(scope.row, 'minCellTemp', '最低单体温度')">{{
|
||||
scope.row.minCellTemp
|
||||
}} ℃</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="电池号码"
|
||||
prop="minCellTempId">
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
<el-dialog
|
||||
|
||||
@ -37,8 +37,8 @@
|
||||
>{{ pcsItem.deviceName }}</span
|
||||
>
|
||||
<div class="info">
|
||||
<div>
|
||||
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[pcsItem.communicationStatus] || '-' }}
|
||||
<div v-if="(($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[pcsItem.communicationStatus]">
|
||||
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[pcsItem.communicationStatus] }}
|
||||
</div>
|
||||
<div>数据更新时间:{{ pcsItem.dataUpdateTime }}</div>
|
||||
</div>
|
||||
|
||||
@ -40,6 +40,10 @@
|
||||
prop="siteName"
|
||||
label="站点名称">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="siteShortName"
|
||||
label="站点简称">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="siteAddress"
|
||||
label="站点地址"
|
||||
@ -98,6 +102,13 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="站点简称" prop="siteShortName">
|
||||
<el-input v-model.trim="siteForm.siteShortName" placeholder="请输入站点简称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="运营时间" prop="runningTime">
|
||||
@ -160,6 +171,7 @@ const emptySiteForm = () => ({
|
||||
id: undefined,
|
||||
siteId: '',
|
||||
siteName: '',
|
||||
siteShortName: '',
|
||||
siteAddress: '',
|
||||
runningTime: '',
|
||||
installPower: '',
|
||||
@ -252,6 +264,7 @@ export default {
|
||||
id: row.id,
|
||||
siteId: row.siteId || '',
|
||||
siteName: row.siteName || '',
|
||||
siteShortName: row.siteShortName || '',
|
||||
siteAddress: row.siteAddress || '',
|
||||
runningTime: row.runningTime || '',
|
||||
installPower: row.installPower || '',
|
||||
|
||||
@ -1,95 +1,163 @@
|
||||
|
||||
<template>
|
||||
<div id="zddtChart" style="height: 100%;width:100%"></div>
|
||||
<div class="map-wrapper">
|
||||
<div ref="mapRef" class="map-canvas"></div>
|
||||
<div v-if="!hasPoint" class="map-empty">暂无站点坐标</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
import resize from '@/mixins/ems/resize'
|
||||
import china from '@/data/ems/china.json'//中国地图数据
|
||||
import 'echarts/lib/chart/map';
|
||||
echarts.registerMap('china', { geoJSON: china }); //注册可用地图
|
||||
const TDT_SCRIPT_ID = 'tianditu-js-sdk'
|
||||
let tdtScriptLoading = null
|
||||
|
||||
function loadTdtScript(tk) {
|
||||
if (window.T) return Promise.resolve(window.T)
|
||||
if (tdtScriptLoading) return tdtScriptLoading
|
||||
tdtScriptLoading = new Promise((resolve, reject) => {
|
||||
const oldScript = document.getElementById(TDT_SCRIPT_ID)
|
||||
if (oldScript) {
|
||||
oldScript.addEventListener('load', () => resolve(window.T))
|
||||
oldScript.addEventListener('error', () => reject(new Error('天地图脚本加载失败')))
|
||||
return
|
||||
}
|
||||
const script = document.createElement('script')
|
||||
script.id = TDT_SCRIPT_ID
|
||||
script.src = `https://api.tianditu.gov.cn/api?v=4.0&tk=${tk}`
|
||||
script.async = true
|
||||
script.onload = () => {
|
||||
if (window.T) resolve(window.T)
|
||||
else reject(new Error('天地图对象未初始化'))
|
||||
}
|
||||
script.onerror = () => reject(new Error('天地图脚本加载失败'))
|
||||
document.body.appendChild(script)
|
||||
})
|
||||
return tdtScriptLoading
|
||||
}
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
hasPoint: false,
|
||||
map: null,
|
||||
overlays: [],
|
||||
pendingPayload: null,
|
||||
mapConfig: {
|
||||
zoom: 12,
|
||||
tk: '01e99ab4472430e1c7dbfe4b5db99787'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initChart()
|
||||
})
|
||||
this.initMap()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
this.clearOverlays()
|
||||
this.map = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
// ECharts 默认有提供了一个简单的加载动画。只需要调用 showLoading 方法显示。数据加载完成后再调用 hideLoading 方法隐藏加载动画。
|
||||
this.chart = echarts.init(document.querySelector('#zddtChart'))
|
||||
},
|
||||
setOption(data) {
|
||||
this.chart.setOption({
|
||||
color:['#FFBD00'],
|
||||
backgroundColor: 'transparent', //背景色
|
||||
geo: { //地理坐标系组件 地理坐标系组件用于地图的绘制,支持在地理坐标系上绘制
|
||||
map: 'china', //地图类型 这儿展示的是中国地图
|
||||
aspectScale: 0.85,
|
||||
selectedMode: "single",// 开启单选
|
||||
label: {
|
||||
show: true, //是否显示标签 此处指是否显示地图上的地区名字
|
||||
color: '#ffffff',
|
||||
fontSize: 12
|
||||
},
|
||||
roam: true, //是否开启鼠标缩放和平移漫游
|
||||
itemStyle: {
|
||||
areaColor: "#03365b",
|
||||
borderColor: "#4bf3f9",
|
||||
shadowColor: '#03365b', //阴影颜色
|
||||
shadowOffsetX: 0, //阴影偏移量
|
||||
shadowOffsetY: 0, //阴影偏移量
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
color: '#ffffff',
|
||||
},
|
||||
itemStyle: {
|
||||
areaColor: "#0f5d9d",
|
||||
async initMap() {
|
||||
try {
|
||||
await loadTdtScript(this.mapConfig.tk)
|
||||
if (!this.$refs.mapRef || !window.T) return
|
||||
this.map = new window.T.Map(this.$refs.mapRef)
|
||||
const defaultCenter = new window.T.LngLat(104.1, 35.9)
|
||||
this.map.centerAndZoom(defaultCenter, 5)
|
||||
if (this.pendingPayload) {
|
||||
this.renderPayload(this.pendingPayload)
|
||||
}
|
||||
} catch (e) {
|
||||
// 页面可继续使用,地图只显示空态
|
||||
this.hasPoint = false
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "effectScatter",
|
||||
coordinateSystem: "geo",
|
||||
showEffectOn: "render",
|
||||
data,
|
||||
rippleEffect: {
|
||||
brushType: "stroke",
|
||||
scale: 5,
|
||||
period: 2, // 秒数
|
||||
normalizePoint(site = {}) {
|
||||
const name = site.siteName || site.name || ''
|
||||
const value = site.value || site.siteLocation || []
|
||||
const lonSource = site.longitude !== undefined && site.longitude !== null ? site.longitude : value[0]
|
||||
const latSource = site.latitude !== undefined && site.latitude !== null ? site.latitude : value[1]
|
||||
const lon = Number(lonSource)
|
||||
const lat = Number(latSource)
|
||||
if (!lon || !lat) return null
|
||||
return { name, lon, lat }
|
||||
},
|
||||
symbolSize: 12,
|
||||
clickable: false,
|
||||
zlevel: 1,
|
||||
label: {
|
||||
formatter: "{b}",
|
||||
position: "right",
|
||||
show: true,
|
||||
clearOverlays() {
|
||||
if (!this.map || !this.overlays.length) return
|
||||
this.overlays.forEach(item => this.map.removeOverLay(item))
|
||||
this.overlays = []
|
||||
},
|
||||
renderPayload(payload = {}) {
|
||||
const isArrayPayload = Array.isArray(payload)
|
||||
const selectedRaw = isArrayPayload ? ((payload || [])[0] || {}) : (payload.selected || {})
|
||||
const sitesRaw = isArrayPayload ? [] : (payload.sites || [])
|
||||
const selected = this.normalizePoint(selectedRaw)
|
||||
const points = (Array.isArray(sitesRaw) ? sitesRaw : [])
|
||||
.map(item => this.normalizePoint(item))
|
||||
.filter(Boolean)
|
||||
if (selected && !points.find(item => item.lon === selected.lon && item.lat === selected.lat)) {
|
||||
points.push(selected)
|
||||
}
|
||||
]
|
||||
this.clearOverlays()
|
||||
this.hasPoint = points.length > 0
|
||||
if (!this.map || !points.length || !window.T) return
|
||||
|
||||
const viewPoints = []
|
||||
points.forEach(item => {
|
||||
const lngLat = new window.T.LngLat(item.lon, item.lat)
|
||||
const marker = new window.T.Marker(lngLat)
|
||||
this.map.addOverLay(marker)
|
||||
this.overlays.push(marker)
|
||||
viewPoints.push(lngLat)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (selected && selected.name) {
|
||||
const label = new window.T.Label({
|
||||
text: selected.name,
|
||||
position: new window.T.LngLat(selected.lon, selected.lat),
|
||||
offset: new window.T.Point(8, -34)
|
||||
})
|
||||
this.map.addOverLay(label)
|
||||
this.overlays.push(label)
|
||||
}
|
||||
|
||||
|
||||
if (viewPoints.length === 1) {
|
||||
this.map.centerAndZoom(viewPoints[0], this.mapConfig.zoom)
|
||||
} else {
|
||||
this.map.setViewport(viewPoints)
|
||||
}
|
||||
},
|
||||
setOption(payload = {}) {
|
||||
this.pendingPayload = payload
|
||||
if (!this.map) return
|
||||
this.renderPayload(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.map-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.map-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-empty {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
background: rgba(245, 247, 250, 0.9);
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,32 +3,54 @@
|
||||
<zd-info></zd-info>
|
||||
<div class="ems-content-container">
|
||||
<div class="map-container">
|
||||
<div class="site-cards-wrapper" v-if="allSites.length > 0">
|
||||
<button
|
||||
class="site-cards-arrow site-cards-arrow--left"
|
||||
type="button"
|
||||
:disabled="!canScrollLeft"
|
||||
@click="scrollSiteCards('left')"
|
||||
>
|
||||
<i class="el-icon-arrow-left"></i>
|
||||
</button>
|
||||
<div ref="siteCards" class="site-cards" @scroll="updateScrollButtons">
|
||||
<div
|
||||
v-for="item in allSites"
|
||||
:key="item.siteId"
|
||||
class="site-card"
|
||||
:class="{ active: isSameSite(item.siteId, singleSiteId) }"
|
||||
@click="submitSite(item.siteId)"
|
||||
>
|
||||
<div class="site-card-name">{{ item.siteName || '-' }}</div>
|
||||
<div class="site-card-info-row">
|
||||
<span class="site-card-label">电站位置</span>
|
||||
<span class="site-card-value site-card-value--address">{{ formatSiteCardField(item.siteAddress) }}</span>
|
||||
</div>
|
||||
<div class="site-card-info-row">
|
||||
<span class="site-card-label">投运时间</span>
|
||||
<span class="site-card-value">{{ formatSiteCardDate(item.runningTime) }}</span>
|
||||
</div>
|
||||
<div class="site-card-info-row">
|
||||
<span class="site-card-label">装机功率(MW)</span>
|
||||
<span class="site-card-value">{{ formatSiteCardField(item.installPower) }}</span>
|
||||
</div>
|
||||
<div class="site-card-info-row">
|
||||
<span class="site-card-label">装机容量(MW)</span>
|
||||
<span class="site-card-value">{{ formatSiteCardField(item.installCapacity) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="site-cards-arrow site-cards-arrow--right"
|
||||
type="button"
|
||||
:disabled="!canScrollRight"
|
||||
@click="scrollSiteCards('right')"
|
||||
>
|
||||
<i class="el-icon-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="map-view">
|
||||
<map-chart ref="mapChart"/>
|
||||
</div>
|
||||
<div class="zd-msg-container">
|
||||
<div class="zd-msg-top">
|
||||
<zd-select ref="zdSelect" @submitSite="submitSite"></zd-select>
|
||||
<el-card class="common-card-container">
|
||||
<div slot="header">
|
||||
<span class="card-title">基本信息</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text" size="small" @click="toDzjk">查看详情</el-button>
|
||||
</div>
|
||||
<div class="single-zd-name">{{singleSiteName}}</div>
|
||||
<!-- 四个方块-->
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="12" class="single-square-box-container" v-for="(item,index) in singleZdSqaure" :key="index+'singleSquareBox'">
|
||||
<single-square-box :data="item"></single-square-box>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 基本信息 -->
|
||||
<el-descriptions class="single-zd-info-container" :column="1" >
|
||||
<el-descriptions-item v-for="(item,index) in singleZdInfo" :key="index+'singleZdInfo'" :label="item.title">{{item.value | formatNumber }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<!-- echarts柱状图-->
|
||||
<bar-chart ref="barChart"></bar-chart>
|
||||
</el-card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -36,94 +58,101 @@
|
||||
|
||||
<script>
|
||||
import ZdInfo from '@/components/Ems/ZdBaseInfo/index.vue'
|
||||
import ZdSelect from '@/components/Ems/ZdSelect/index.vue'
|
||||
import SingleSquareBox from '@/components/Ems/SingleSquareBox/index.vue'
|
||||
import BarChart from './BarChart.vue'
|
||||
import MapChart from './MapChart.vue'
|
||||
import {getSingleSiteBaseInfo} from '@/api/ems/zddt'
|
||||
import { getAllSites } from '@/api/ems/zddt'
|
||||
export default {
|
||||
components:{ZdSelect,ZdInfo,SingleSquareBox,BarChart,MapChart},
|
||||
components: { ZdInfo, MapChart },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
singleSiteId: '',
|
||||
singleSiteName: '',
|
||||
singleSiteLocation: [],
|
||||
// 单个电站 四个方块数据
|
||||
singleZdSqaure:[
|
||||
{
|
||||
title:'今日充电(kWh)',
|
||||
value:'',
|
||||
bgColor:'#FFE5E5',
|
||||
attr:'dayChargedCap'
|
||||
},{
|
||||
title:'累计充电(kWh)',
|
||||
value:'',
|
||||
bgColor:'#FFE5E5',
|
||||
attr:'totalChargedCap'
|
||||
},{
|
||||
title:'今日放电(kWh)',
|
||||
value:'',
|
||||
bgColor:'#EEEBFF',
|
||||
attr:'dayDisChargedCap'
|
||||
},{
|
||||
title:'累计放电(kWh)',
|
||||
value:'',
|
||||
bgColor:'#EEEBFF',
|
||||
attr:'totalDisChargedCap'
|
||||
}
|
||||
],
|
||||
|
||||
// 单个电站 基本信息
|
||||
singleZdInfo:[{
|
||||
title:'电站位置',
|
||||
value:'',
|
||||
attr:'siteAddress'
|
||||
},{
|
||||
title:'投运时间',
|
||||
value:'',
|
||||
attr:'runningTime'
|
||||
},{
|
||||
title:'装机功率(MW)',
|
||||
value:'',
|
||||
attr:'installPower'
|
||||
},{
|
||||
title:'装机容量(MW)',
|
||||
value:'',
|
||||
attr:'installCapacity',
|
||||
}]
|
||||
allSites: [],
|
||||
canScrollLeft: false,
|
||||
canScrollRight: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadSites()
|
||||
window.addEventListener('resize', this.updateScrollButtons)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.updateScrollButtons)
|
||||
},
|
||||
methods:{
|
||||
isSameSite(siteId, selectedId) {
|
||||
return String(siteId) === String(selectedId)
|
||||
},
|
||||
formatSiteCardField(value) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-'
|
||||
}
|
||||
return value
|
||||
},
|
||||
formatSiteCardDate(value) {
|
||||
if (!value) {
|
||||
return '-'
|
||||
}
|
||||
const text = String(value)
|
||||
if (text.includes('T')) {
|
||||
return text.slice(0, 10)
|
||||
}
|
||||
return text.length > 10 ? text.slice(0, 10) : text
|
||||
},
|
||||
loadSites() {
|
||||
this.loading = true
|
||||
getAllSites().then(response => {
|
||||
this.allSites = response?.data || []
|
||||
if (this.allSites.length > 0) {
|
||||
this.submitSite(this.allSites[0].siteId)
|
||||
} else {
|
||||
this.updateMapMarkers()
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.updateScrollButtons()
|
||||
})
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
updateScrollButtons() {
|
||||
const container = this.$refs.siteCards
|
||||
if (!container) {
|
||||
this.canScrollLeft = false
|
||||
this.canScrollRight = false
|
||||
return
|
||||
}
|
||||
const maxScrollLeft = container.scrollWidth - container.clientWidth
|
||||
this.canScrollLeft = container.scrollLeft > 0
|
||||
this.canScrollRight = maxScrollLeft > 0 && container.scrollLeft < maxScrollLeft - 1
|
||||
},
|
||||
scrollSiteCards(direction) {
|
||||
const container = this.$refs.siteCards
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
const amount = Math.max(container.clientWidth * 0.8, 240)
|
||||
const delta = direction === 'left' ? -amount : amount
|
||||
container.scrollBy({ left: delta, behavior: 'smooth' })
|
||||
},
|
||||
updateMapMarkers(){
|
||||
this.$refs.mapChart && this.$refs.mapChart.setOption({
|
||||
selected: {name:this.singleSiteName,value:this.singleSiteLocation},
|
||||
sites:this.allSites
|
||||
})
|
||||
},
|
||||
// 站点选中
|
||||
submitSite(id){
|
||||
if(this.singleSiteId === id){return console.log(`点击搜索按钮 搜索相同的站点id= ${id}不再调用获取基本信息接口`)}
|
||||
this.loading=true
|
||||
console.log('点击搜索按钮 选中的站点id',id)
|
||||
this.singleSiteId = id
|
||||
this.$refs.zdSelect.searchLoading = true
|
||||
getSingleSiteBaseInfo(id).then(response => {
|
||||
console.log('单个站点详情数据',response)
|
||||
const res = response?.data || {}
|
||||
this.singleSiteName = res?.siteName || ''//站点名称
|
||||
this.singleSiteLocation = res?.siteLocation || []//站点坐标
|
||||
this.singleZdSqaure.forEach(item=>{
|
||||
item.value =res[item.attr]
|
||||
})
|
||||
this.singleZdInfo.forEach(item=>{
|
||||
item.value = res[item.attr]
|
||||
})
|
||||
this.$refs.barChart.setOption(res?.sevenDayDisChargeStats || [])
|
||||
this.$refs.mapChart.setOption([{name:this.singleSiteName,value:this.singleSiteLocation}])
|
||||
}).finally(() => {this.$refs.zdSelect.searchLoading = false;this.loading=false})
|
||||
},
|
||||
//跳转单站监控页面
|
||||
toDzjk(){
|
||||
this.$router.push({
|
||||
path:'/dzjk',
|
||||
query:{
|
||||
siteId:this.singleSiteId,
|
||||
const currentSite = this.allSites.find(item => this.isSameSite(item.siteId, id)) || {}
|
||||
this.singleSiteName = currentSite.siteName || ''
|
||||
this.singleSiteLocation = currentSite.siteLocation || []
|
||||
if (!this.singleSiteLocation.length) {
|
||||
this.singleSiteLocation = [currentSite.longitude, currentSite.latitude].filter(item => item !== undefined && item !== null)
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.updateMapMarkers()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -134,29 +163,119 @@ export default {
|
||||
.ems-content-container{
|
||||
display: flex;
|
||||
padding:24px;
|
||||
padding-right: 0;
|
||||
.map-container{
|
||||
flex:auto;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
.site-cards-wrapper{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
.site-cards-arrow{
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
transition: all .2s ease;
|
||||
&:hover:not(:disabled){
|
||||
color: #2b74ff;
|
||||
box-shadow: 0 0 0 1px #2b74ff inset;
|
||||
}
|
||||
.zd-msg-container{
|
||||
width: 500px;
|
||||
padding: 24px;
|
||||
.single-zd-name{
|
||||
font-weight: 500;
|
||||
line-height: 23px;
|
||||
color: #FFBD00;
|
||||
font-size: 16px;
|
||||
margin-bottom: 24px;
|
||||
&:disabled{
|
||||
cursor: not-allowed;
|
||||
color: #c0c4cc;
|
||||
box-shadow: 0 0 0 1px #ebeef5 inset;
|
||||
}
|
||||
.single-square-box-container{
|
||||
height: 78px;
|
||||
}
|
||||
}
|
||||
.site-cards{
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 2px;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
&::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
.site-card{
|
||||
flex: 0 0 360px;
|
||||
height: 166px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e4e7ed;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
transition: all .2s ease;
|
||||
.site-card-name{
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.single-zd-info-container{
|
||||
.site-card-info-row{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
color:#666666;
|
||||
line-height: 18px;
|
||||
}
|
||||
.site-card-label{
|
||||
flex: 0 0 84px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.site-card-value{
|
||||
flex: 1;
|
||||
color: #606266;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.site-card-value--address{
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: unset;
|
||||
min-height: 36px;
|
||||
}
|
||||
&:hover{
|
||||
border-color: #c0d3ff;
|
||||
}
|
||||
&.active{
|
||||
border-color: #2b74ff;
|
||||
box-shadow: 0 0 0 1px rgba(43,116,255,.15) inset;
|
||||
background: #f5f9ff;
|
||||
.site-card-name{
|
||||
color: #2b74ff;
|
||||
}
|
||||
.site-card-value{
|
||||
color: #5f8ee3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.map-view{
|
||||
height: 70vh;
|
||||
min-height: 520px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<img :src="loginBg" alt="" srcset="" class="login-bg" />
|
||||
<transition :name="bgTransitionName">
|
||||
<img :key="bgNum" :src="loginBg" alt="" srcset="" class="login-bg" />
|
||||
</transition>
|
||||
<el-form
|
||||
ref="loginForm"
|
||||
:model="loginForm"
|
||||
@ -107,6 +109,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
bgNum: 1,
|
||||
bgTransitionName: "bg-slide",
|
||||
bgTransitionNames: ["bg-slide", "bg-zoom", "bg-blur"],
|
||||
codeUrl: "",
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
@ -148,7 +152,12 @@ export default {
|
||||
this.updateInterval(this.updateBgNum, 5000);
|
||||
},
|
||||
methods: {
|
||||
randomBgTransitionName() {
|
||||
const index = Math.floor(Math.random() * this.bgTransitionNames.length);
|
||||
return this.bgTransitionNames[index];
|
||||
},
|
||||
updateBgNum() {
|
||||
this.bgTransitionName = this.randomBgTransitionName();
|
||||
if (this.bgNum >= 4) this.bgNum = 0;
|
||||
this.bgNum += 1;
|
||||
},
|
||||
@ -210,6 +219,10 @@ export default {
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.login {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding-left: 180px;
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
@ -224,6 +237,60 @@ export default {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
backface-visibility: hidden;
|
||||
will-change: opacity, transform, filter;
|
||||
}
|
||||
.bg-fade-enter-active,
|
||||
.bg-fade-leave-active {
|
||||
transition: opacity 0.9s ease, transform 0.9s ease;
|
||||
}
|
||||
.bg-fade-enter {
|
||||
opacity: 0;
|
||||
transform: scale(1.04);
|
||||
}
|
||||
.bg-fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.bg-slide-enter-active,
|
||||
.bg-slide-leave-active {
|
||||
transition: opacity 0.85s ease, transform 0.85s ease;
|
||||
}
|
||||
.bg-slide-enter {
|
||||
opacity: 0;
|
||||
transform: translate3d(3.5%, 0, 0) scale(1.01);
|
||||
}
|
||||
.bg-slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translate3d(-3.5%, 0, 0) scale(0.99);
|
||||
}
|
||||
.bg-zoom-enter-active,
|
||||
.bg-zoom-leave-active {
|
||||
transition: opacity 1s ease, transform 1s ease;
|
||||
}
|
||||
.bg-zoom-enter {
|
||||
opacity: 0;
|
||||
transform: scale(1.08);
|
||||
}
|
||||
.bg-zoom-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.94);
|
||||
}
|
||||
.bg-blur-enter-active,
|
||||
.bg-blur-leave-active {
|
||||
transition: opacity 0.9s ease, filter 0.9s ease, transform 0.9s ease;
|
||||
}
|
||||
.bg-blur-enter {
|
||||
opacity: 0;
|
||||
filter: blur(8px);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
.bg-blur-leave-to {
|
||||
opacity: 0;
|
||||
filter: blur(6px);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.login-logo {
|
||||
display: block;
|
||||
|
||||
Reference in New Issue
Block a user