This commit is contained in:
2026-02-17 21:44:12 +08:00
parent c7c1b416ee
commit 9272a0162a
9 changed files with 507 additions and 291 deletions

View File

@ -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 ''

View File

@ -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()

View File

@ -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>

View File

@ -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
}} &#8451;</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
}} &#8451;</span>
</template>
</el-table-column>
<el-table-column
label="电池号码"
prop="minCellTempId">
</el-table-column>
</el-table>
</el-card>
</div>
<el-dialog

View File

@ -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>

View File

@ -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 || '',

View File

@ -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'))
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
}
},
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",
}
}
},
series: [
{
type: "effectScatter",
coordinateSystem: "geo",
showEffectOn: "render",
data,
rippleEffect: {
brushType: "stroke",
scale: 5,
period: 2, // 秒数
},
symbolSize: 12,
clickable: false,
zlevel: 1,
label: {
formatter: "{b}",
position: "right",
show: true,
},
}
]
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 }
},
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>

View File

@ -1,33 +1,55 @@
<template>
<div class="ems-dashboard-editor-container" v-loading="loading">
<zd-info></zd-info>
<div class="ems-content-container ">
<div class="ems-content-container">
<div class="map-container">
<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 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 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>
<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>
</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',
}]
loading: false,
singleSiteId: '',
singleSiteName: '',
singleSiteLocation: [],
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;
}
.zd-msg-container{
width: 500px;
padding: 24px;
.single-zd-name{
font-weight: 500;
line-height: 23px;
color: #FFBD00;
font-size: 16px;
margin-bottom: 24px;
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;
}
&:disabled{
cursor: not-allowed;
color: #c0c4cc;
box-shadow: 0 0 0 1px #ebeef5 inset;
}
}
}
.single-square-box-container{
height: 78px;
box-sizing: border-box;
margin-bottom: 10px;
.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;
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;
}
.site-card-info-row{
display: flex;
align-items: flex-start;
gap: 8px;
margin-top: 6px;
font-size: 12px;
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;
}
}
}
}
.single-zd-info-container{
font-size: 12px;
margin-top: 10px;
color:#666666;
.map-view{
height: 70vh;
min-height: 520px;
}
}
}

View File

@ -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;