光伏功能页面

This commit is contained in:
huangjp
2025-11-12 10:55:55 +08:00
parent d5c01162f6
commit 2ff46c5cbc
57 changed files with 12168 additions and 93 deletions

View File

@ -64,7 +64,6 @@ export default {
aioPower: 'AIO Power',
loadPower: 'Load Power',
powerFactor: 'Fa-Power',
stationName: 'Name of Power Station',
stationType: 'Type of Power Station',
stationLocation: 'Location of Power Station',
@ -81,7 +80,6 @@ export default {
tree: 'Tree',
discabinet: 'Distribution cabinet',
chargingPile: 'Charging pile',
chargingDischarging: 'Cumulative charge and discharge',
sevenDay: 'Nearly Seven Days',
monthDay: 'Nearly a Month',
@ -108,7 +106,6 @@ export default {
energyStorage: 'Energy Storage',
energyStorageMeter: 'Energy Storage Meter',
crane: 'Crane',
realtimeAlarm: 'Real-Time Alarms',
accessPoint: 'Device Access Point',
environmentalData: 'Environmental Control Data',
@ -120,27 +117,23 @@ export default {
refSetting: 'Refrigeration Setting',
shutPoint: 'Fan Shutdown Point',
heatSetting: 'Heating Setting',
cabinetTem: 'Temperature of Energy Storage Cabinet',
cabinetHum: 'Humidity of Energy Storage Cabinet',
dieselTem: 'Diesel Generator Temperature',
dieselLevel: 'Diesel Generator Oil Level',
dieselDenerators: 'Diesel Generator',
runCurve: 'Run Time Curve',
runData: 'Run Time Data',
power: 'Power',
air: 'Air Conditioning',
device: 'Device',
chargingandDischarging: 'Electricity',
alarmDevices: 'Alarm Devices',
NormalDevices: 'Normal Devices',
DeviceNo: 'Device No',
DeviceType: 'Device Type',
DeviceState: 'Device State',
AlarmTime: 'Alarm time',
oneControl: 'One-click Control',
col: 'Indicators',
state: 'State',
@ -163,12 +156,10 @@ export default {
issusedMsg: 'This operation will send data items in sequence. Do you confirm the sending?',
cancel: 'Cancel',
sure: 'Sure',
Loadpower: 'Load Active Power',
pvpower: 'Photovoltaic Active Power',
PCSpower: 'PCS Active Power',
Dieselpower: 'Diesel Generator Power',
waterSuper: 'Water Supply Temperature',
ambientTem: 'Ambient Temperature',
returnTemperature: 'Return Water Temperature',
@ -177,7 +168,6 @@ export default {
engineV: 'Diesel Generator voltage',
powerfrequency: 'Generate Electricity Frequency',
engineC: 'Diesel Generator Current',
onepcsPower: '1-PCS Active Power',
twopcsPower: '2-PCS Active Power',
onebmsSoc: '1-BMS-SOC',
@ -224,6 +214,16 @@ export default {
generation: 'Generation',
yhl: 'Fuel',
jsfs: 'Count method',
saveSuccess: 'Save successful'
saveSuccess: 'Save successful',
todayElectricityGeneration: "Today's Electricity Generation",
totalPowerGeneration: 'Cumulative Power Generation',
totalRevenue: 'Cumulative Earnings',
ratedPowerofInverter: 'Rated power of inverter',
energyTrend: 'Energy Trend',
month: 'month',
year: 'year',
powerGenerationCapacity: 'power generation capacity',
powerGeneration: 'power generation',
revenueTrend: 'Revenue Trend'
}
}

View File

@ -13,7 +13,6 @@ export default {
totalDischarge: '总放电量',
dailyCharge: '日充电量',
dailyDischarge: '日放电量',
day: '天',
frequency: '频率',
rechPower: '可充功率',
disRechPower: '可放功率',
@ -64,7 +63,6 @@ export default {
aioPower: '一体机功率',
loadPower: '负载功率',
refluxAmmeter: '防逆流电表',
stationName: '电站名称',
stationType: '电站类型',
stationLocation: '电站位置',
@ -78,7 +76,6 @@ export default {
coal: '等效节约煤',
income: '等效经济收入',
tree: '棵',
chargingDischarging: '累计充放电量',
sevenDay: '近七天',
monthDay: '近一月',
@ -106,7 +103,6 @@ export default {
energyStorageMeter: '储能电表',
crane: '塔吊',
powerFactor: '功率因数',
realtimeAlarm: '实时告警',
accessPoint: '设备接入点',
environmentalData: '环控数据',
@ -118,27 +114,23 @@ export default {
refSetting: '制冷设定',
shutPoint: '风机停机点',
heatSetting: '制热设定',
cabinetTem: '储能柜温度',
cabinetHum: '储能柜湿度',
dieselTem: '柴发温度',
dieselLevel: '柴发油位',
dieselDenerators: '柴发',
runCurve: '运行曲线',
runData: '运行数据',
power: '功率',
air: '空调',
device: '设备',
chargingandDischarging: '充放电量',
alarmDevices: '告警设备',
NormalDevices: '正常设备',
DeviceNo: '设备编号',
DeviceType: '设备类型',
DeviceState: '设备状态',
AlarmTime: '告警时间',
oneControl: '一键控制',
col: '指标',
state: '状态',
@ -161,12 +153,10 @@ export default {
issusedMsg: '此操作将会按顺序下发数据项,是否确认下发?',
cancel: '取消',
sure: '确认',
Loadpower: '负载有功功率',
pvpower: '光伏有功功率',
PCSpower: 'PCS有功功率',
Dieselpower: '柴发有功功率',
waterSuper: '供水温度',
ambientTem: '环境温度',
returnTemperature: '回水温度',
@ -175,7 +165,6 @@ export default {
engineV: '柴发电压',
powerfrequency: '发电频率',
engineC: '柴发电流',
onepcsPower: '1-PCS有功功率',
twopcsPower: '2-PCS有功功率',
onebmsSoc: '1-BMS-SOC',
@ -222,6 +211,17 @@ export default {
generation: '发电量',
yhl: '油耗量',
jsfs: '计算方式',
saveSuccess: '保存成功'
saveSuccess: '保存成功',
todayElectricityGeneration: '当日发电量',
totalPowerGeneration: '累计发电量',
totalRevenue: '累计收益',
ratedPowerofInverter: '逆变器额定功率',
energyTrend: '能量趋势',
day: '日',
month: '月',
year: '年',
powerGenerationCapacity: '发电功率',
powerGeneration: '发电量',
revenueTrend: '收益趋势'
}
}

View File

@ -78,7 +78,6 @@ export default {
acRea: 'Active/Reactive',
active: 'Active',
reactive: 'Reactive',
eqptRunState: 'Eqpt Run State',
cumCharge: 'Cumulative charge',
cumDischarge: 'Cumulative discharge',
@ -103,10 +102,17 @@ export default {
rightHum: 'Right Humidity',
leftTem: 'Left Temperature',
rightTem: 'Right Temperature',
dataDisplay: 'Data Display',
airTopo: 'Air Conditioning Topology',
airTem: 'Air Conditioning Temperature'
airTem: 'Air Conditioning Temperature',
batVoltage: 'BAT voltage',
batCurrent: 'BAT current',
busVoltage: 'BUS voltage',
busCurrent: 'BUS current',
dc: 'DC',
dcVoltage: 'DC voltage',
operatingPower: 'operating power',
powerGenerationCapacity: 'power generation capacity',
powerGeneration: 'Power Generation'
}
}

View File

@ -103,10 +103,17 @@ export default {
rightHum: '右侧湿度',
leftTem: '左侧温度',
rightTem: '右侧温度',
dataDisplay: '数据展示',
airTopo: '空调拓扑图',
airTem: '空调温度'
airTem: '空调温度',
batVoltage: 'BAT电压',
batCurrent: 'BAT电流',
busVoltage: 'BAT电压',
busCurrent: 'BAT电流',
dc: '直流电流',
dcVoltage: '直流电压',
operatingPower: '运行功率',
powerGenerationCapacity: '发电功率',
powerGeneration: '发电'
}
}

View File

@ -522,6 +522,7 @@ export default {
hint: 'Hint',
delSuccess: 'Delete Success',
addSuccess: 'Add Success',
editSuccess: 'Edit Success', repairRecord: 'repairRecord' }
editSuccess: 'Edit Success', repairRecord: 'repairRecord'
}
}

View File

@ -174,7 +174,21 @@ export default {
computeding: 'Computing,The current progress is:',
laterQuery: '%,Please enquire later.',
earningReport: 'Earning Report',
bill: 'Bill'
bill: 'Bill',
powerGenerationStatus: 'power Generation Status',
totalStringCapacity: 'Total string capacity',
currentMonthlyPowerGeneration: 'Current monthly power generation',
monthlyPowerGeneration: 'Monthly power generation',
cumulativePowerGeneration: 'cumulative power generation',
equivalentPowerGenerationTime: 'Equivalent power generation time',
peakACpower: 'Peak AC power',
gridConnectedDuration: 'grid-connected duration',
projectRevenueSituation: 'Project revenue situation',
monthlyPVpowerGeneration: 'Monthly PV power generation',
monthlyInverterPowerGeneration: 'Monthly inverter power generation',
monthlyIncome: 'Monthly income',
powerGeneration: 'Power Generation'
},
region: {
regionName: 'Region Name',

View File

@ -134,7 +134,6 @@ export default {
dayHighPrice: '日最高电价',
dayLowtPrice: '日最低电价',
close: '关闭'
},
state: {
month: '月份',
@ -173,7 +172,21 @@ export default {
computeding: '计算中,当前进度为',
laterQuery: '%,请稍后查询。',
earningReport: '收益报表',
bill: '账单'
bill: '账单',
powerGenerationStatus: '项目发电情况',
totalStringCapacity: '组串总容量',
currentMonthlyPowerGeneration: '本月发电量',
cumulativePowerGeneration: '累计发电量',
equivalentPowerGenerationTime: '等价发电时',
peakACpower: '峰值交流功率',
monthlyPowerGeneration: '月发电量',
projectRevenueSituation: '项目收益情况',
monthlyPVpowerGeneration: '本月PV发电量',
monthlyInverterPowerGeneration: '本月逆变器发电量',
monthlyIncome: '本月收益',
gridConnectedDuration: '并网时长',
powerGeneration: '发电量'
},
region: {
regionName: '区域名称',

View File

@ -53,6 +53,14 @@ export default {
ammeter: 'PA',
cabinet: 'Cabinet',
diesel: 'Diesel',
qtScreenTitle: 'New smart energy operation platform'
qtScreenTitle: 'New smart energy operation platform',
pvScreenTitle: 'Zetatech Smart PV management and control platform',
todayEarning: 'Today Earning',
dailyPowerGeneration: 'Daily Power Generation',
yearlyPowerGeneration: 'Yearly Power Generation',
cumulativePowerGeneration: 'Cumulative Power Generation',
powerGenerationEarningRanking: 'Power generation earnings ranking',
powerGenerationRanking: 'Power generation ranking',
powerGeneration: 'power generation'
}
}

View File

@ -53,6 +53,15 @@ export default {
ammeter: '电表',
cabinet: '储能柜',
diesel: '柴发',
qtScreenTitle: '新一代智慧能源运营平台'
qtScreenTitle: '新一代智慧能源运营平台',
pvScreenTitle: '智慧光伏管控平台',
todayEarning: '今日收益',
dailyPowerGeneration: '日发电量',
yearlyPowerGeneration: '年发电量',
cumulativePowerGeneration: '累计发电量',
powerGenerationEarningRanking: '发电收益排名',
powerGenerationRanking: '发电量排名',
powerGeneration: '发电'
}
}

View File

@ -262,7 +262,7 @@ export default {
policyTemCurve: 'Policy Template Curve/kW',
exitSaveExit: 'Please save or exit the edit first',
distributeSuccess: 'Distribute Succeeded',
numRange: 'The number range is -99999999-99999999到99999999之间'
numRange: 'Between -99999999 and 99999999'
},
loadForecast: {
conditions: 'Current operating conditions',

View File

@ -35,7 +35,8 @@ export default {
maxValue: 'You can select up to',
point: 'points',
pointError: 'Only one point can be selected for ordinary points or cell dismantling'
}, deviceUpgrade: {
},
deviceUpgrade: {
powerStation: 'PowerStation',
device: 'Device',
deviceType: 'DeviceType',
@ -80,5 +81,6 @@ export default {
pleaseSelectTime: 'Please Select Time',
pleaseFillinUpgradeReason: 'Please Fill in Upgrade Reason',
pleaseUploadUpgradeFile: 'Please Upload Upgrade File',
uploadSuccessful: 'Upload Successful' }
uploadSuccessful: 'Upload Successful'
}
}

View File

@ -14,6 +14,7 @@ const whiteList = [
'/screen',
'/new-screen',
'/new-screen-zz',
'/new-screen-zz-pv',
'/common-screen',
'/app-privacy-en.html'
] // no redirect whitelist

View File

@ -87,6 +87,12 @@ export const constantRoutes = [
name: 'new-screen-zz',
meta: { title: '管控大屏' }
},
{
path: '/new-screen-zz-pv',
component: () => import('@/views/new-screen-zz-pv/index.vue'),
name: 'new-screen-zz-pv',
meta: { title: '光伏大屏' }
},
{
path: '/dashboard-test',
component: Layout,
@ -99,6 +105,18 @@ export const constantRoutes = [
}
]
},
{
path: '/dashboard-pv',
component: Layout,
children: [
{
path: 'dashboard-pv',
component: () => import('@/views/dashboard-pv/index.vue'),
name: 'dashboard-pv',
meta: { title: 'dashboard-pv', icon: 'dashboard', affix: true }
}
]
},
{
path: '/dashboard-zhongzi',
component: Layout,

View File

@ -33,8 +33,13 @@ const revenueRouter = {
component: () => import('@/views/revenue-management/proxy-price/index.vue'),
name: 'proxy-price',
meta: { title: 'proxy-price' }
},
{
path: 'pv-earnings-statement',
component: () => import('@/views/revenue-management/pv-earnings-statement/index.vue'),
name: 'pv-earnings-statement',
meta: { title: '光伏收益报表' }
}
]
}

View File

@ -0,0 +1,269 @@
<template>
<div class="bottom-left-wrapper">
<ItemBox :title="$t('dashboard.energyTrend')">
<div slot="header">
<div class="header-right-box">
<div
class="header-title"
:class="{ active: currentType === 'day' }"
@click="selectTime('day')"
>
{{ $t("dashboard.day") }}
</div>
<div
class="header-title"
:class="{ active: currentType === 'month' }"
@click="selectTime('month')"
>
{{ $t("dashboard.month") }}
</div>
<div
class="header-title"
:class="{ active: currentType === 'year' }"
@click="selectTime('year')"
>
{{ $t("dashboard.year") }}
</div>
</div>
</div>
<div v-loading="loading" class="charts-box">
<Chart
ref="chart"
:key="key"
:options="powerOptions"
:class-name="'chart'"
/>
</div>
</ItemBox>
</div>
</template>
<script>
// import * as echarts from 'echarts'
import { GetPCSElecData } from '@/api/home-page/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: undefined
}
},
data() {
return {
powerOptions: undefined,
currentType: 'day',
color: ['#9A66E4'],
loading: false,
key: 0
}
},
created() {},
mounted() {
const timeData = []
const totalPoints = (24 * 60) / 5
const currentTime = Date.now()
for (let i = 0; i < totalPoints; i++) {
const pointTimestamp = currentTime - i * 5 * 60 * 1000
const date = new Date(pointTimestamp)
const formattedTime = `${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date
.getHours()
.toString()
.padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
timeData.push(formattedTime)
}
timeData.reverse()
const currentData = []
for (let i = 0; i < timeData.length; i++) {
currentData.push({
date: timeData[i],
chargeElec: Number((Math.random() * 1500 + 1400).toFixed(2))
})
}
this.initCharts(currentData, 1)
},
methods: {
selectTime(type) {
this.currentType = type
this.getData()
},
getData() {
this.getElecData()
// this.getIncomeData()
},
async getElecData() {
this.loading = true
const params = {
stationId: this.stationId,
type: this.currentType
}
try {
const res = await GetPCSElecData(params)
console.log(res)
// this.initCharts(res.data, 1)
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
initCharts(val, type) {
const x_data = []
const charge_data = []
// const discharge_data = []
// let benefit_data = [0, 0, 0, 0, 0, 0, 0]
if (val && val.length > 0) {
val.forEach((item) => {
x_data.push(item.date)
charge_data.push(item.chargeElec)
// discharge_data.push(item.dischargeElec)
})
}
this.powerOptions = {
grid: {
top: '25%',
left: '5%',
right: '5%',
bottom: '5%',
containLabel: true
},
legend: {},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: (params) => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].name}</p></div>`
params.forEach((item) => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${this.color[0]};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${item.data}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
xAxis: [
{
type: 'category',
data: x_data,
axisLine: {
show: true,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
}
],
yAxis: [
{
type: 'value',
name: `kWh`,
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
},
splitLine: {
show: true, // 强制显示分割线(默认已开启,确保不被隐藏)
lineStyle: {
type: 'dashed', // 线型设为虚线
color: 'rgba(255,255,255,0.2)', // 虚线颜色(可自定义,如 #999、rgba(0,0,0,0.1)
width: 1, // 虚线宽度
dashOffset: 5 // 虚线偏移量(可选,调整虚线起始位置)
}
}
}
],
series: [
{
name: `${this.$t('dashboard.powerGeneration')}`,
type: 'line',
symbol: 'none',
data: charge_data,
barWidth: 14, // 柱状图的宽度
color: '#9A66E4'
}
]
}
}
}
}
</script>
<style lang="scss" scoped>
.bottom-left-wrapper {
width: 100%;
height: 100%;
.header-right-box {
display: flex;
flex-direction: row;
align-items: center;
.header-title {
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
white-space: nowrap;
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active {
color: #ffffff;
background: linear-gradient(
90deg,
rgba(0, 148, 255, 0) 0%,
rgba(0, 148, 255, 0.43) 51%,
rgba(0, 148, 255, 0) 99%
);
}
}
}
}
.charts-box {
width: 100%;
height: 100%;
color: rgba(154, 102, 228, 1);
}
</style>

View File

@ -0,0 +1,307 @@
<template>
<div class="bottom-left-wrapper">
<ItemBox :title="$t('dashboard.revenueTrend')">
<div v-loading="loading" class="charts-box">
<Chart :options="runOptions" :class-name="'chart'" />
</div>
</ItemBox>
</div>
</template>
<script>
import { GetRealtimeCurve } from '@/api/home-page/index'
import * as echarts from 'echarts'
import { chartYIndex } from '@/utils/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
runOptions: undefined,
serviceList: [],
nameArr: [],
color: [],
color2: [
[
{
offset: 0.0,
color: 'rgba(206, 235, 255, 0.1)'
},
{
offset: 1,
color: 'rgba(206, 235, 255, 0.2)'
}
],
[
{
offset: 0.0,
color: 'rgba(255, 184, 0, 0.1)'
},
{
offset: 1,
color: 'rgba(255, 184, 0, 0.2)'
}
]
],
loading: false
}
},
computed: {},
watch: {},
created() {},
mounted() {
// this.initCharts()
},
methods: {
async getData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const res = await GetRealtimeCurve(params)
this.initCharts(res.data)
} catch (error) {
// console.log(error)
} finally {
this.loading = false
}
},
initCharts(val) {
let x_data = []
const valueArr = []
this.nameArr = []
if (val && val.length > 0) {
val.forEach((item, index) => {
valueArr.push(item.list)
this.nameArr.push(item.deviceName + item.name)
if (item.list.length) {
x_data = []
item.list.forEach((el) => {
x_data.push(el.date)
})
}
})
// val[0].
const valueArr2 = []
valueArr.forEach((item, index) => {
const arr = []
item.forEach((item2, index2) => {
arr.push(item2.digital)
valueArr2[index] = arr
})
})
this.serviceList = []
valueArr2.forEach((item, index) => {
this.serviceList.push({
data: item,
type: 'line',
name: this.nameArr[index],
showSymbol: false,
yAxisIndex: chartYIndex(val[index]),
color: this.color[index],
lineStyle: {
color: this.color[index]
},
areaStyle: {
color: new echarts.graphic.LinearGradient(
0,
1,
0,
0,
this.color2[index]
)
}
})
})
} else {
this.runOptions = {
title: {
text: `${this.$t('dashboard.noData')}`,
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
fontWeight: 'normal'
}
}
}
}
this.runOptions = {
grid: {
top: '25%',
left: '5%',
right: '3%',
bottom: '5%',
containLabel: true
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: (params) => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].name}</p></div>`
params.forEach((item) => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${
item.color
};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${
item.data === null ? '' : item.data
}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
legend: {
type: 'scroll',
top: 20,
bottom: 20,
padding: [0, 180],
icon: 'circle',
align: 'left',
itemWidth: 10,
itemHeight: 8,
itemGap: 15,
textStyle: {
color: '#fff',
rich: {
name: {
fontSize: 12,
color: '#ffffff',
padding: [0, 10, 0, 0]
},
num: {
fontSize: 16,
color: '#ffffff'
},
unit: {
fontSize: 16,
color: '#ffffff'
}
}
}
},
xAxis: {
data: x_data,
axisLine: {
show: true,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
},
yAxis: [
{
name: `${this.$t('dashboard.power')}kW`,
type: 'value',
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
},
min: function(value) {
return Math.floor(
(Math.abs(value.min) < value.max
? -value.max * 1.05
: value.min * 1.05
).toFixed(2)
)
},
max: function(value) {
return Math.ceil(
(Math.abs(value.min) < value.max
? value.max * 1.05
: -value.min * 1.05
).toFixed(2)
)
}
},
{
name: 'SOC(%)',
type: 'value',
max: 100,
min: -100,
ming: 0,
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
}
],
series: this.serviceList
}
}
}
}
</script>
<style lang="scss" scoped>
.bottom-left-wrapper {
width: 100%;
height: 100%;
}
.charts-box {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,284 @@
<template>
<div class="bottom-left-wrapper">
<ItemBox
:title="$t('dashboard.revenueTrend')"
@handleSetting="handleSetting"
>
<!-- <ItemBox :title="$t('dashboard.revenueTrend')"
:show-curve-setting="true"
@handleSetting="handleSetting"> -->
<div slot="header">
<div class="header-right-box">
<div
class="header-title"
:class="{ active: currentType === 'month' }"
@click="selectTime('month')"
>
{{ $t("dashboard.month") }}
</div>
<div
class="header-title"
:class="{ active: currentType === 'year' }"
@click="selectTime('year')"
>
{{ $t("dashboard.year") }}
</div>
</div>
</div>
<div v-loading="loading" class="charts-box">
<Chart :options="runOptions" :class-name="'chart'" />
</div>
</ItemBox>
</div>
</template>
<script>
import { GetDynamicRealtimeCurve } from '@/api/home-page/index'
import * as echarts from 'echarts'
// import { chartYIndex } from '@/utils/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
},
permissionId: {
type: Number,
default: null
}
},
data() {
return {
currentType: 'month',
runOptions: undefined,
serviceList: [],
nameArr: [],
color: ['#FF9900'],
color2: [
[
{
offset: 0.0,
color: 'rgba(206, 235, 255, 0.1)'
},
{
offset: 1,
color: 'rgba(206, 235, 255, 0.2)'
}
],
[
{
offset: 0.0,
color: 'rgba(255, 184, 0, 0.1)'
},
{
offset: 1,
color: 'rgba(255, 184, 0, 0.2)'
}
]
],
loading: false
}
},
computed: {},
watch: {},
created() {},
mounted() {
const currentData = []
for (let i = 1; i < 32; i++) {
currentData.push({
date: i,
chargeElec: Number((Math.random() * 9000 + 1000).toFixed(2))
})
}
this.initCharts(currentData, 1)
},
methods: {
selectTime(type) {
this.currentType = type
this.getData()
},
handleSetting() {
this.$emit('handleSetting')
},
async getData() {
this.loading = true
const params = {
stationId: this.stationId,
pageLocation: 'runChart',
permissionId: this.permissionId
}
try {
const res = await GetDynamicRealtimeCurve(params)
if (res.data) {
// this.initCharts(res.data)
}
} catch (error) {
// console.log(error)
} finally {
this.loading = false
}
},
// 新增代码
initCharts(val, type) {
const x_data = []
const charge_data = []
// const discharge_data = []
// let benefit_data = [0, 0, 0, 0, 0, 0, 0]
if (val && val.length > 0) {
val.forEach((item) => {
x_data.push(item.date)
charge_data.push(item.chargeElec)
// discharge_data.push(item.dischargeElec)
})
}
this.runOptions = {
grid: {
top: '25%',
left: '5%',
right: '5%',
bottom: '5%',
containLabel: true
},
legend: {},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: (params) => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].name}</p></div>`
params.forEach((item) => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${this.color[0]};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${item.data}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
xAxis: [
{
type: 'category',
data: x_data,
axisLine: {
show: true,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
}
],
yAxis: [
{
type: 'value',
name: this.$t('glance.wRMB'),
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
},
splitLine: {
show: true, // 强制显示分割线(默认已开启,确保不被隐藏)
lineStyle: {
type: 'dashed', // 线型设为虚线
color: 'rgba(255,255,255,0.2)', // 虚线颜色(可自定义,如 #999、rgba(0,0,0,0.1)
width: 1, // 虚线宽度
dashOffset: 5 // 虚线偏移量(可选,调整虚线起始位置)
}
}
}
],
series: [
{
name: `${this.$t('state.powerGeneration')}`,
type: 'bar',
data: charge_data,
barWidth: 14, // 柱状图的宽度
color: '#ff9900',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 153, 0,0.90)' },
{ offset: 1, color: 'rgba(255, 153, 0, 0.0)' }
])
}
}
]
}
}
}
}
</script>
<style lang="scss" scoped>
.bottom-left-wrapper {
width: 100%;
height: 100%;
.header-right-box {
display: flex;
flex-direction: row;
align-items: center;
.header-title {
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
white-space: nowrap;
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active {
color: #ffffff;
background: linear-gradient(
90deg,
rgba(0, 148, 255, 0) 0%,
rgba(0, 148, 255, 0.43) 51%,
rgba(0, 148, 255, 0) 99%
);
}
}
}
}
.charts-box {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,297 @@
<template>
<div class="bottom-left-wrapper">
<ItemBox :title="$t('dashboard.revenueTrend')">
<div v-loading="loading" class="charts-box">
<Chart :options="runOptions" :class-name="'chart'" />
</div>
</ItemBox>
</div>
</template>
<script>
import { GetRealtimeCurve } from '@/api/home-page/index'
import * as echarts from 'echarts'
import { chartYIndex } from '@/utils/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
runOptions: undefined,
serviceList: [],
nameArr: [],
color: [],
color2: [[{
offset: 0.0,
color: 'rgba(206, 235, 255, 0.1)'
},
{
offset: 1,
color: 'rgba(206, 235, 255, 0.2)'
}], [
{
offset: 0.0,
color: 'rgba(255, 184, 0, 0.1)'
},
{
offset: 1,
color: 'rgba(255, 184, 0, 0.2)'
}
]],
loading: false
}
},
computed: {
language() {
return this.$store.getters.language || undefined
}
},
watch: {
language: {
handler(val) {
this.getData()
}
},
deep: true
},
created() {},
mounted() {
// this.initCharts()
},
methods: {
async getData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const res = await GetRealtimeCurve(params)
this.initCharts(res.data)
} catch (error) {
// console.log(error)
} finally {
this.loading = false
}
},
initCharts(val) {
let x_data = []
const valueArr = []
this.nameArr = []
if (val && val.length > 0) {
val.forEach((item, index) => {
valueArr.push(item.list.length ? item.list : [''])
this.nameArr.push(item.deviceName + item.name)
if (item.list.length) {
x_data = []
item.list.forEach((el) => {
x_data.push(el.date)
})
}
})
const valueArr2 = []
valueArr.forEach((item, index) => {
const arr = []
item.forEach((item2, index2) => {
arr.push(item2.digital)
valueArr2[index] = arr
})
})
this.serviceList = []
valueArr2.forEach((item, index) => {
this.serviceList.push({
data: item,
type: 'line',
name: this.nameArr[index],
showSymbol: false,
yAxisIndex: chartYIndex(val[index]),
color: this.color[index],
lineStyle: {
color: this.color[index]
},
// areaStyle: {
// color: '#CEEBFF',
// opacity: 0.1
// },
// 设置面积区域为渐变效果
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, this.color2[index])
}
})
})
} else {
this.runOptions = {
title: {
text: `${this.$t('dashboard.noData')}`,
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
fontWeight: 'normal'
}
}
}
}
this.runOptions = {
grid: {
top: '25%',
left: '5%',
right: '3%',
bottom: '5%',
containLabel: true
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: params => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].name}</p></div>`
params.forEach(item => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${item.color};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${item.data === null ? '' : item.data}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
legend: {
type: 'scroll',
top: 20,
bottom: 20,
padding: [0, 180],
icon: 'circle',
align: 'left',
itemWidth: 10,
itemHeight: 8,
itemGap: 15,
textStyle: {
color: '#fff',
rich: {
name: {
fontSize: 12,
color: '#ffffff',
padding: [0, 10, 0, 0]
},
num: {
fontSize: 16,
color: '#ffffff'
},
unit: {
fontSize: 16,
color: '#ffffff'
}
}
}
},
xAxis: {
data: x_data,
axisLine: {
show: true,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
},
yAxis: [{
name: `${this.$t('dashboard.power')}kW`,
type: 'value',
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
},
min: function(value) {
return Math.floor((Math.abs(value.min) < value.max ? -value.max * 1.05 : value.min * 1.05).toFixed(2))
},
max: function(value) {
return Math.ceil((Math.abs(value.min) < value.max ? value.max * 1.05 : -value.min * 1.05).toFixed(2))
}
},
{
name: 'SOC(%)',
type: 'value',
max: 100,
min: -100,
ming: 0,
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
}],
series: this.serviceList
}
}
}
}
</script>
<style lang="scss" scoped>
.bottom-left-wrapper{
width: 100%;
height: 100%;
}
.charts-box{
width: 100%;
height: 100%;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,182 @@
<template>
<div class="huankong-wrap">
<ItemBox :title="$t('dashboard.environmentalData')">
<div v-if="airArr.length" v-loading="loading" class="box">
<template v-for="(item,index) in airArr">
<div :key="index" class="air-box">
<div class="value">{{ item.value }}</div>
<div class="label">
<span class="air-left-jt " />
<span class="air-icon left" />
<span />
<span class="name">{{ item.name }}</span>
<span class="air-icon right" />
<span class="air-right-jt " />
</div>
</div>
</template>
</div>
<div v-else class="box">
<div class="empty">{{ $t("dashboard.noData") }}</div>
</div>
</ItemBox>
</div>
</template>
<script>
import { GetAirConfig } from '@/api/home-page/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
hkList: [],
statusData: {
dischargCapacity: '',
rechargeCapacity: '',
grid: '',
loading: false
},
airArr: []
}
},
computed: {
},
watch: {},
created() {
},
mounted() { },
methods: {
async getData() {
this.loading = true
try {
const res = await GetAirConfig({ stationId: this.stationId })
this.airArr = res.data
// console.log(this.airArr)
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
async GetStatusMonitor() {
// this.loading = true
// const params = {
// stationId: this.stationId
// }
// try {
// const { data } = await GetCircleCtr(params)
// this.hkList = data
// } catch (error) {
// // console.log(error);
// } finally {
// this.loading = false
// }
}
}
}
</script>
<style lang="scss" scoped>
.huankong-wrap {
width: 100%;
overflow: hidden;
}
.box {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
padding: 10px;
height: 302px;
overflow: auto;
.empty {
width: 100%;
height: 280px;
display: flex;
justify-content: center;
align-items: center;
font-family: Source Han Sans CN;
font-size: 16px;
color: rgba(206, 235, 255, 0.7);
}
.air-box {
min-width: 120px;
width: 47%;
height: 60px;
margin: 10px 0 0 0;
background: url(../../../../assets/images/air-bg.png);
background-size: 100% 100%;
display: flex;
flex-direction: column;
align-items: center;
.label {
min-width: 86px;
width: 100%;
flex: 1;
font-family: Source Han Sans CN;
font-size: 14px;
color: rgba(206, 235, 255, 0.7);
display: flex;
align-items: center;
justify-content: center;
.name{
text-align: center;
// min-width: 88px;
}
.air-icon {
display: inline-block;
width: 10px;
height: 10px;
background: url(../../../../assets/images/air-icon.png);
background-size: 100% 100%;
}
.air-left-jt {
display: inline-block;
width: 8px;
height: 8px;
line-height: 0px;
background: url(../../../../assets/images/air-left.png);
background-size: 100% 100%;
margin-right: 10px;
}
.air-right-jt {
display: inline-block;
width: 8px;
height: 8px;
line-height: 0px;
background: url(../../../../assets/images/air-right.png);
background-size: 100% 100%;
margin-left: 10px;
}
.left {
margin-right: 10px;
}
.right {
margin-left: 10px;
}
}
.value {
width: 100%;
flex: 1;
font-family: DIN;
font-size: 22px;
color: #ffffff;
text-shadow: 0px 0px 10px 0px #0094ff;
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>

View File

@ -0,0 +1,179 @@
<template>
<div class="huankong-wrap">
<ItemBox :title="$t('dashboard.environmentalData')" :show-point-setting="true" @handleSetting="handleSetting">
<div v-if="airArr.length" v-loading="loading" class="box">
<div v-for="(item,index) in airArr" :key="index" class="air-box">
<div class="value">{{ item.value }}</div>
<div class="label">
<span class="air-left-jt " />
<span class="air-icon left" />
<span />
<span class="name">{{ item.name }}</span>
<span class="air-icon right" />
<span class="air-right-jt " />
</div>
</div>
</div>
<div v-else class="box">
<div class="empty">{{ $t("dashboard.noData") }}</div>
</div>
</ItemBox>
</div>
</template>
<script>
import { DynamicConfigPoint } from '@/api/home-page/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
},
permissionId: {
type: Number,
default: 0
}
},
data() {
return {
hkList: [],
statusData: {
dischargCapacity: '',
rechargeCapacity: '',
grid: '',
loading: false,
permissionId: null
},
airArr: []
}
},
computed: {
},
watch: {},
created() {
},
mounted() { },
methods: {
handleSetting() {
this.$emit('handleSetting')
},
async getData() {
this.loading = true
try {
const res = await DynamicConfigPoint({
pageLocation: 'airData',
permissionId: this.permissionId,
stationId: this.stationId })
this.airArr = res.data
// console.log(this.airArr)
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.huankong-wrap {
width: 100%;
overflow: hidden;
}
.box {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
padding: 10px;
height: 302px;
overflow: auto;
.empty {
width: 100%;
height: 280px;
display: flex;
justify-content: center;
align-items: center;
font-family: Source Han Sans CN;
font-size: 16px;
color: rgba(206, 235, 255, 0.7);
}
.air-box {
min-width: 120px;
width: 47%;
height: 60px;
margin: 10px 0 0 0;
background: url(../../../../assets/images/air-bg.png);
background-size: 100% 100%;
display: flex;
flex-direction: column;
align-items: center;
.label {
min-width: 86px;
width: 100%;
flex: 1;
font-family: Source Han Sans CN;
font-size: 14px;
color: rgba(206, 235, 255, 0.7);
display: flex;
align-items: center;
justify-content: center;
.name{
text-align: center;
// min-width: 88px;
}
.air-icon {
display: inline-block;
width: 10px;
height: 10px;
background: url(../../../../assets/images/air-icon.png);
background-size: 100% 100%;
}
.air-left-jt {
display: inline-block;
width: 8px;
height: 8px;
line-height: 0px;
background: url(../../../../assets/images/air-left.png);
background-size: 100% 100%;
margin-right: 10px;
}
.air-right-jt {
display: inline-block;
width: 8px;
height: 8px;
line-height: 0px;
background: url(../../../../assets/images/air-right.png);
background-size: 100% 100%;
margin-left: 10px;
}
.left {
margin-right: 10px;
}
.right {
margin-left: 10px;
}
}
.value {
width: 100%;
flex: 1;
font-family: DIN;
font-size: 22px;
color: #ffffff;
text-shadow: 0px 0px 10px 0px #0094ff;
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>

View File

@ -0,0 +1,334 @@
<template>
<div class="huankong-wrap">
<ItemBox :title="$t('dashboard.environmentalData')">
<div v-loading="loading" class="box">
<div class="zhengxiang">
<div class="left">
<el-tooltip :content="$t('dashboard.indoorTem') + '(℃)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.indoorTem') }}()</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionInnerTemperature) }}</div>
<el-tooltip :content="$t('dashboard.heatBackLash') + '(℃)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.heatBackLash') }}()</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionReturnHot) }}</div>
</div>
<div class="center">
<div class="label">{{ $t('dashboard.air') }}</div>
</div>
<div class="right">
<el-tooltip :content="$t('dashboard.condensation') + '(℃)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.condensation') }}()</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionCondensation) }}</div>
<el-tooltip :content="$t('dashboard.coolBackLash') + '(℃)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.coolBackLash') }}()</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionReturnCold) }}</div>
</div>
</div>
<div class="zhengxiang">
<div class="left">
<el-tooltip :content="$t('dashboard.indoorHum') + '(%)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.indoorHum') }}(%)</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionInnerHumidity) }}</div>
<el-tooltip :content="$t('dashboard.shutPoint')" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.shutPoint') }}</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionFanStopPoint) }}</div>
</div>
<div class="center">
<!-- <div class="value">1</div> -->
<div class="label">{{ $t('dashboard.air') }}</div>
<!-- <div class="label">(kWh)</div> -->
</div>
<div class="right">
<el-tooltip :content="$t('dashboard.refSetting') + '(℃)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.refSetting') }}()</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionSetCold) }}</div>
<el-tooltip :content="$t('dashboard.heatSetting') + '(℃)'" placement="top" effect="dark">
<div class="label">{{ $t('dashboard.heatSetting') }}()</div>
</el-tooltip>
<div class="value">{{ toFix(formModel.airconditionSetHot) }}</div>
</div>
</div>
</div>
<!-- <div v-else class="box">
<div class="empty">暂无数据</div>
</div> -->
</ItemBox>
</div>
</template>
<script>
import { GetShanghaiKJYAirData } from '@/api/homePage-integrated/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
hkList: [],
formModel: {},
loading: false,
colList: ['airconditionInnerTemperature', 'airconditionReturnHot', 'airconditionCondensation', 'airconditionReturnCold', 'airconditionInnerHumidity', 'airconditionFanStopPoint', 'airconditionSetCold', 'airconditionSetHot']
}
},
computed: {
},
watch: {},
created() {
},
mounted() {
},
methods: {
toFix(val) {
if (val) {
const result = Number(val).toFixed(2)
return result
} else if (val === 0) {
return 0
} else {
return ''
}
},
async getData() {
this.loading = true
const params = {
stationId: this.stationId,
colList: this.colList
}
try {
const { data } = await GetShanghaiKJYAirData(params)
this.formModel = data
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.huankong-wrap{
width: 100%;
}
.box {
.empty{
width: 100%;
height: 302px;
display: flex;
justify-content: center;
align-items: center;
font-family: Source Han Sans CN;
font-size: 16px;
color: rgba(206, 235, 255, 0.7);
}
// padding-top: 25px;
// padding-bottom: 20px;
.zhengxiang {
width: 100%;
// min-width: 362px;
height: 135px;
// background: url(../../../../../assets/images/dianbiao-zxdn-bg.png) no-repeat;
// background:url(../../../../assets/images/dianbiao-zxdn-bg.png);
// background-size: 100% 100%;
margin: 10px 0;
display: flex;
.left {
flex: 1;
height: 100%;
background:url(../../../../assets/images/yuan-left.png);
background-size: 100% 100%;
display: flex;
flex-direction: column;
// padding-left: 10%;
.label {
margin-left: 10px;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 86px;
// width: 100%;
// flex: 1;
font-family: Source Han Sans CN;
font-size: 14px;
color: rgba(206, 235, 255, 0.7);
// display: flex;
text-align: center;
height: 35px;
line-height: 40px;
// align-items: center;
// justify-content: center;
}
.value {
width: 100%;
flex: 1;
font-family: DIN;
font-size: 22px;
color: #FFFFFF;
text-shadow: 0px 0px 10px 0px #0094FF;
display: flex;
align-items: center;
justify-content: center;
}
}
.center {
background:url(../../../../assets/images/yuan-center.png);
background-size: 100% 100%;
width: 220px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.label {
width: 100%;
font-family: Source Han Sans CN;
font-size: 14px;
color: rgba(206, 235, 255, 0.7);
text-align: center;
}
.value {
width: 100%;
font-family: DIN;
font-size: 30px;
color: #FFFFFF;
text-align: center;
text-shadow: 0px 0px 10px 0px #0094FF;
}
}
.right {
flex: 1;
// padding-right: 10%;
height: 100%;
background:url(../../../../assets/images/yuan-right.png);
background-size: 100% 100%;
display: flex;
flex-direction: column;
text-align: right;
.label {
margin-right: 10px;
max-width: 113px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 86px;
// width: 100%;
// flex: 1;
font-family: Source Han Sans CN;
font-size: 14px;
color: rgba(206, 235, 255, 0.7);
text-align: center;
height: 35px;
line-height: 40px;
// justify-content: center;
}
.value {
width: 100%;
flex: 1;
font-family: DIN;
font-size: 22px;
color: #FFFFFF;
text-shadow: 0px 0px 10px 0px #0094FF;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.yougonglv-wrap {
width: 100%;
min-width: 367px;
min-height: 67px;
background: url(../../../../assets/images/zongyougong-bg.png) no-repeat;
background-size: 100% auto;
margin: 10px 0;
.top {
height: 50%;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 20%;
width: 100%;
.name {
display: inline-block;
color: #fff;
}
.top-value {
display: inline-block;
font-size: 22px;
padding-right: 20px;
color: #fff;
.value {
padding-right: 10px;
}
.unit {
color: #CEEBFF;
font-size: 14px;
}
}
}
.bottom {
height: 50%;
display: flex;
align-items: center;
padding-left: 20%;
.item {
display: inline-block;
flex: 1;
display: flex;
align-items: center;
.name {
color: #CEEBFF
}
.value {
display: inline-block;
padding-left: 5px;
min-width: 35px;
background: url(../../../../assets/images/jb.png) no-repeat;
background-size: cover;
color: #CEEBFF;
text-align: center;
}
}
}
}
}
</style>

View File

@ -0,0 +1,207 @@
<template>
<div class="item-box">
<div class="item-top">
<el-popover
placement="bottom"
width="240"
trigger="click"
@show="changeTitle = title"
@hide="handleSaveTitle"
>
<el-input v-model="changeTitle" maxlength="25" />
<div slot="reference" class="top-title">{{ title }}</div>
</el-popover>
<div class="header-right">
<slot name="header" />
<i
v-if="showPointSetting"
slot="reference"
v-permission="['business:dynamicConfig:addPointList']"
class="el-icon-setting"
style="
font-size: 22px;
cursor: pointer;
color: #ceebff;
margin-right: 3px;
"
@click="handleSetting"
/>
<i
v-if="showCurveSetting"
slot="reference"
v-permission="['business:dynamicConfig:addCurveList']"
class="el-icon-setting"
style="
font-size: 22px;
cursor: pointer;
color: #ceebff;
margin-right: 3px;
"
@click="handleSetting"
/>
<svg-icon
v-if="showScreen"
:style="svgStyle"
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
@click="click"
/>
</div>
</div>
<div class="item-con">
<slot />
</div>
</div>
</template>
<script>
import screenfull from 'screenfull'
export default {
name: 'Index',
components: {},
props: {
title: {
type: String,
default: ''
},
titleId: {
type: [String, Number],
default: ''
},
showPointSetting: {
type: Boolean,
default: false
},
showCurveSetting: {
type: Boolean,
default: false
},
showScreen: {
type: Boolean,
default: false
}
},
data() {
return {
svgStyle: {
display: 'inline-block',
cursor: 'pointer',
fill: '#ceebff',
width: '16px',
height: '16px',
verticalAlign: '10px',
marginRight: '5px'
},
isFullscreen: false,
changeTitle: ''
}
},
computed: {},
watch: {},
created() {},
mounted() {
this.init()
},
methods: {
handleSaveTitle() {
this.$emit('handleSaveTitle', this.changeTitle, this.titleId)
},
handleSetting() {
this.$emit('handleSetting')
},
change() {
this.isFullscreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
screenfull.off('change', this.change)
}
},
click() {
this.$emit('screenFull', this.isFullscreen)
const element = document.getElementById('app-main') // 指定全屏区域元素
element.style.background =
'url(' + require('../../../../assets/images/dashboardBg.png') + ')'
if (screenfull.isEnabled) {
screenfull.request(element)
}
screenfull.toggle(element)
}
}
}
</script>
<style lang="scss" scoped>
.item-box {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
.item-top {
width: 100%;
height: 40px;
line-height: 40px;
opacity: 1;
box-sizing: border-box;
// border: 1px solid;
// background: #003235;
background-image: var(--item-header-bg);
background-size: 100% 40px;
// border-image: linear-gradient(
// 11deg,
// rgba(0, 148, 255, 0.5) 37%,
// rgba(0, 148, 255, 0.5) 37%,
// rgba(0, 148, 255, 0) 44%
// )
// 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.15);
// background:var(--table-bg);
display: flex;
flex-direction: row;
align-items: center;
.top-title {
// width: 100%;
height: 40px;
line-height: 40px;
font-size: 20px;
font-weight: normal;
letter-spacing: 0.2em;
color: #ceebff;
text-shadow: 0px 0px 10px #0094ff;
font-family: var(--font-family);
padding: 0 10px;
// background: linear-gradient(180deg, #c9fff3 0%, #c3fff2 13%, #32ffd2 63%);
}
.header-right {
display: flex;
align-items: center;
margin-left: auto;
}
}
.item-con {
flex: 1;
margin-top: 5px;
width: 100%;
background:var(--table-bg);
box-sizing: border-box;
border: 1px solid;
border-image: linear-gradient(
108deg,
#ffffff -7%,
rgba(201, 255, 243, 0) 16%,
rgba(201, 255, 243, 0) 75%,
rgba(201, 255, 243, 0) 81%,
#ffffff 102%,
#ffffff 102%
)
1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.15);
}
}
</style>

View File

@ -0,0 +1,244 @@
<template>
<div class="top-center-wrapper">
<ItemBox :title="$t('dashboard.runCurve')">
<div v-loading="loading" class="charts-box">
<Chart
ref="chart"
:key="key"
:options="powerOptions"
:class-name="'chart'"
/>
</div>
</ItemBox>
</div>
</template>
<script>
// import * as echarts from 'echarts'
import { GetPCSElecData } from '@/api/home-page/index'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: undefined
}
},
data() {
return {
powerOptions: undefined,
currentType: 'day',
color: ['#00A0E9'],
loading: false,
key: 0
}
},
created() {},
mounted() {
const timeData = []
const totalPoints = (24 * 60) / 5
const currentTime = Date.now()
for (let i = 0; i < totalPoints; i++) {
const pointTimestamp = currentTime - i * 5 * 60 * 1000
const date = new Date(pointTimestamp)
const formattedTime = `${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date
.getHours()
.toString()
.padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
timeData.push(formattedTime)
}
timeData.reverse()
const currentData = []
for (let i = 0; i < timeData.length; i++) {
currentData.push({
date: timeData[i],
chargeElec: Number((Math.random() * 1500 + 1400).toFixed(2))
})
}
this.initCharts(currentData, 1)
},
methods: {
selectTime(type) {
this.currentType = type
this.getData()
},
getData() {
this.getElecData()
// this.getIncomeData()
},
async getElecData() {
this.loading = true
const params = {
stationId: this.stationId,
type: this.currentType
}
try {
const res = await GetPCSElecData(params)
console.log(res)
// this.initCharts(res.data, 1)
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
// 新增代码
initCharts(val, type) {
const x_data = []
const charge_data = []
// const discharge_data = []
// let benefit_data = [0, 0, 0, 0, 0, 0, 0]
if (val && val.length > 0) {
val.forEach((item) => {
x_data.push(item.date)
charge_data.push(item.chargeElec)
// discharge_data.push(item.dischargeElec)
})
}
this.powerOptions = {
grid: {
top: '25%',
left: '5%',
right: '5%',
bottom: '5%',
containLabel: true
},
legend: {},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: (params) => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].name}</p></div>`
params.forEach((item) => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${this.color[0]};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${item.data}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
xAxis: [
{
type: 'category',
data: x_data,
axisLine: {
show: true,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
}
],
yAxis: [
{
type: 'value',
name: `kWh`,
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
},
splitLine: {
show: true, // 强制显示分割线(默认已开启,确保不被隐藏)
lineStyle: {
type: 'dashed', // 线型设为虚线
color: 'rgba(255,255,255,0.2)', // 虚线颜色(可自定义,如 #999、rgba(0,0,0,0.1)
width: 1, // 虚线宽度
dashOffset: 5 // 虚线偏移量(可选,调整虚线起始位置)
}
}
}
],
series: [
{
name: `${this.$t('dashboard.powerGenerationCapacity')}`,
type: 'line',
smooth: true,
symbol: 'none',
data: charge_data,
barWidth: 14, // 柱状图的宽度
color: '#00C8FF'
}
]
}
}
}
}
</script>
<style lang="scss" scoped>
.top-center-wrapper {
width: 100%;
height: 100%;
.header-right-box {
display: flex;
flex-direction: row;
align-items: center;
.header-title {
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
white-space: nowrap;
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active {
color: #ffffff;
background: linear-gradient(
90deg,
rgba(0, 148, 255, 0) 0%,
rgba(0, 148, 255, 0.43) 51%,
rgba(0, 148, 255, 0) 99%
);
}
}
}
.charts-box {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,508 @@
<template>
<div class="top-left-box">
<ItemBox :title="$t('dashboard.stationData')">
<div slot="header">
<div class="header-right-box">
<div class="header-title" :class="{'active':currentIndex === 0}" @click="changeType(0)">{{ $t('dashboard.dataOverView') }}</div>
<div class="header-title" :class="{'active':currentIndex === 1}" @click="changeType(1)">{{ $t('dashboard.stationInfo') }}</div>
</div>
</div>
<div v-if="currentIndex === 0" v-loading="loading" class="box">
<StationData :table-data="overflowData" />
</div>
<div v-else v-loading="loading" class="box">
<StationData :table-data="totalData" />
</div>
<el-dialog
:append-to-body="true"
title="日充放统计规则"
:visible.sync="ruleShow"
center
width="30%"
:close-on-click-modal="false"
>
<el-form :model="timeForm" label-width="120px">
<el-form-item label="前一日" class="select-box">
<el-select
v-model="timeForm.beforeTime"
style="width:100%"
placeholder="请选择"
@change="selectRuleTime"
>
<template slot="prefix">
<span style="padding-left: 5px;">
<i class="el-icon-time" />
</span>
</template>
<el-option
v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="今日">
<el-time-picker
v-model="timeForm.nowDayTime"
style="width:100%"
:clearable="false"
readonly
placeholder="请选择时间"
value-format="HH:mm:ss"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelRule"> </el-button>
<el-button
type="primary"
:loading="timeSubmitLoading"
@click="sureRule"
> </el-button>
</div>
</el-dialog>
</ItemBox>
</div>
</template>
<script>
import { GetPcsStationData, addElecMeterConfig,
updateElecMeterConfig } from '@/api/home-page/index'
import { GetRAPcsTotalData } from '@/api/homePage-integrated/index'
import { ToDegrees } from '@/utils/index'
import dayImg from '../../../../assets/station-data/run-day.png'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
dayImg,
currentIndex: 0,
overflowData: [{
name: this.$t('dashboard.safeDays'),
id: 'operationDays',
picture: dayImg,
unit: this.$t('dashboard.day'),
value: 0
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: dayImg,
unit: 'kWh',
value: 0
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: dayImg,
unit: '%',
value: 0
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: dayImg,
unit: 'kW',
value: 0
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: dayImg,
unit: 'MWh',
value: 0
}, {
name: this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: dayImg,
unit: 'MWh',
value: 0
}, {
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: dayImg,
unit: 'MWh',
value: 0,
click: true
}, {
name: this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: dayImg,
unit: 'MWh',
value: 0
}],
totalData: [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: dayImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: dayImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: dayImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: dayImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: dayImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: dayImg,
value: 0
}],
stationType: [],
loading: false,
ruleShow: false,
timeForm: {
beforeTime: undefined,
nowDayTime: undefined
},
timeSubmitLoading: false,
timeOperate: 1, // 1 新增 2 编辑
timeList: []
}
},
computed: {
stationTypeList() {
return this.$store.getters.dicts['stationType'] || []
},
language() {
return this.$store.getters.language || undefined
}
},
watch: {
dailyTime: {
handler(val) {
if (val) {
this.timeForm = val
this.timeOperate = val.timeOperate
this.GetPcsTotalData()
}
},
deep: true,
immediate: true
},
language: {
handler() {
this.overflowData = [{
name: this.$t('dashboard.safeDays'),
id: 'operationDays',
picture: dayImg,
unit: this.$t('dashboard.day'),
value: 0
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: dayImg,
unit: 'kWh',
value: 0
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: dayImg,
unit: '%',
value: 0
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: dayImg,
unit: 'kW',
value: 0
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: dayImg,
unit: 'MWh',
value: 0
}, {
name: this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: dayImg,
unit: 'MWh',
value: 0
}, {
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: dayImg,
unit: 'MWh',
value: 0,
click: true
}, {
name: this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: dayImg,
unit: 'MWh',
value: 0
}]
this.totalData = [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: dayImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: dayImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: dayImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: dayImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: dayImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: dayImg,
value: 0
}]
},
deep: true
}
},
created() {
// console.log(this.stationTypeList)
this.getTimeList()
},
mounted() { },
methods: {
getStationTypeName(item) {
const typeIndex = this.stationTypeList.findIndex(t => Number(t.value) === Number(item))
return this.stationTypeList[typeIndex].label
},
changeType(val) {
this.currentIndex = val
},
getData() {
this.GetPcsStationData()
this.GetPcsTotalData()
},
async GetPcsStationData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetPcsStationData(params)
this.totalData.forEach((item) => {
item.value = data[item.id]
if (item.id === 'type') {
item.value = this.getStationTypeName(item.value)
}
if (item.id === 'longitude') {
item.value = ToDegrees(item.value, 1)
}
if (item.id === 'latitude') {
item.value = ToDegrees(item.value, 2)
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
async GetPcsTotalData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetRAPcsTotalData(params)
this.overflowData.forEach((item) => {
if (data[item.id]) {
item.value = data[item.id]
}
if (item.id === 'systemEfficiency') {
item.value = (item.value * 100).toFixed(2)
}
if (item.id === 'totalChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'totalDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'currentPower' && item.value >= 1E4) {
item.unit = 'MW'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
getTimeList() {
for (let i = 0; i < 24; i++) {
this.timeList.push({
label: `${i < 10 ? '0' + i : i}:00:00`,
value: `${i < 10 ? '0' + i : i}:00:00`
})
}
},
setRule() {
this.ruleShow = true
},
selectRuleTime(val) {
this.timeForm.nowDayTime = this.getEightTime('2020-07-28 ' + val)
},
cancelRule() {
this.ruleShow = false
},
sureRule() {
if (this.timeOperate === 1) {
// 新增
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime
}
addElecMeterConfig(param)
.then((res) => {
this.$message.success('新增成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
} else {
// 编辑
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime,
id: this.timeForm.id
}
updateElecMeterConfig(param)
.then((res) => {
this.$message.success('更新成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
}
},
/**
* 指定时间的多少小时或多少分钟之后的时间
* @param {*} str 2020-07-28 15:00:00
*/
getEightTime(str) {
const t = new Date(str).getTime() + 24 * 60 * 60 * 1000 - 1 // 24小时 * 60分钟 * 60秒 * 1000
const d = new Date(t)
let theMonth = d.getMonth() + 1
let theDate = d.getDate()
let theHours = d.getHours()
let theMinutes = d.getMinutes()
let theSecond = d.getSeconds()
if (theMonth < 10) {
theMonth = '0' + theMonth
}
if (theDate < 10) {
theDate = '0' + theDate
}
if (theHours < 10) {
theHours = '0' + theHours
}
if (theMinutes < 10) {
theMinutes = '0' + theMinutes
}
if (theSecond < 10) {
theSecond = '0' + theSecond
}
// let date = d.getFullYear() + '-' + theMonth + '-' + theDate
const time = theHours + ':' + theMinutes + ':' + theSecond
// let Spare = date + ' ' + time
// console.log(date)
// console.log(time)
// console.log(Spare)
return time
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .item-con{
overflow:auto;
}
.top-left-box{
width: 100%;
height: 100%;
overflow: hidden;
.header-right-box {
display: flex;
align-items: center;
.header-title {
white-space: nowrap;
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active{
color: #ffffff;
background: linear-gradient(90deg, rgba(0,148,255,0.00) 0%, rgba(0,148,255,0.43) 51%, rgba(0,148,255,0.00) 99%);
}
}
}
}
.box{
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 0;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -0,0 +1,509 @@
<template>
<div class="top-left-box">
<ItemBox :title="$t('dashboard.stationData')">
<div slot="header">
<div class="header-right-box">
<div class="header-title" :class="{'active':currentIndex === 0}" @click="changeType(0)">{{ $t('dashboard.dataOverView') }}</div>
<div class="header-title" :class="{'active':currentIndex === 1}" @click="changeType(1)">{{ $t('dashboard.stationInfo') }}</div>
</div>
</div>
<div v-if="currentIndex === 0" v-loading="loading" class="box">
<StationData :table-data="overflowData" />
</div>
<div v-else v-loading="loading" class="box">
<StationData :table-data="totalData" />
</div>
<el-dialog
:append-to-body="true"
title="日充放统计规则"
:visible.sync="ruleShow"
center
width="30%"
:close-on-click-modal="false"
>
<el-form :model="timeForm" label-width="120px">
<el-form-item label="前一日" class="select-box">
<el-select
v-model="timeForm.beforeTime"
style="width:100%"
placeholder="请选择"
@change="selectRuleTime"
>
<template slot="prefix">
<span style="padding-left: 5px;">
<i class="el-icon-time" />
</span>
</template>
<el-option
v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="今日">
<el-time-picker
v-model="timeForm.nowDayTime"
style="width:100%"
:clearable="false"
readonly
placeholder="请选择时间"
value-format="HH:mm:ss"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelRule"> </el-button>
<el-button
type="primary"
:loading="timeSubmitLoading"
@click="sureRule"
> </el-button>
</div>
</el-dialog>
</ItemBox>
</div>
</template>
<script>
import { GetPcsStationData, addElecMeterConfig,
updateElecMeterConfig } from '@/api/home-page/index'
import { GetRAPcsTotalData } from '@/api/homePage-integrated/index'
import { ToDegrees } from '@/utils/index'
import disImg from '../../../../assets/station-data/total-discharge.png'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
disImg,
currentIndex: 0,
totalData: [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}],
overflowData: [{
name: this.$t('dashboard.safeDays'),
id: 'operationDays',
picture: disImg,
value: 0,
unit: this.$t('dashboard.day')
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: disImg,
value: 0,
unit: '%'
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: disImg,
value: 0,
unit: 'kW'
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: disImg,
value: 0,
unit: 'kWh',
click: true
}, {
name: this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}],
stationType: [],
loading: false,
ruleShow: false,
timeForm: {
beforeTime: undefined,
nowDayTime: undefined
},
timeSubmitLoading: false,
timeOperate: 1, // 1 新增 2 编辑
timeList: []
}
},
computed: {
stationTypeList() {
return this.$store.getters.dicts['stationType'] || []
},
language() {
return this.$store.getters.language || undefined
}
},
watch: {
dailyTime: {
handler(val) {
if (val) {
this.timeForm = val
this.timeOperate = val.timeOperate
this.GetPcsTotalData()
}
},
deep: true,
immediate: true
},
language: {
handler() {
this.totalData = [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}]
this.overflowData = [{
name: this.$t('dashboard.safeDays') + `(${this.$t('dashboard.day')})`,
id: 'operationDays',
picture: disImg,
value: 0,
unit: this.$t('dashboard.day')
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: disImg,
value: 0,
unit: '%'
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: disImg,
value: 0,
unit: 'kW'
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: disImg,
value: 0,
unit: 'kWh',
click: true
}, {
name: this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}]
},
deep: true
}
},
created() {
// console.log(this.stationTypeList)
this.getTimeList()
},
mounted() { },
methods: {
getStationTypeName(item) {
const typeIndex = this.stationTypeList.findIndex(t => Number(t.value) === Number(item))
return this.stationTypeList[typeIndex].label
},
changeType(val) {
this.currentIndex = val
},
getData() {
this.GetPcsStationData()
this.GetPcsTotalData()
},
async GetPcsStationData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetPcsStationData(params)
this.totalData.forEach((item) => {
item.value = data[item.id]
if (item.id === 'type') {
item.value = this.getStationTypeName(item.value)
}
if (item.id === 'longitude') {
item.value = ToDegrees(item.value, 1)
}
if (item.id === 'latitude') {
item.value = ToDegrees(item.value, 2)
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
async GetPcsTotalData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetRAPcsTotalData(params)
this.overflowData.forEach((item) => {
if (data[item.id]) {
item.value = data[item.id]
}
if (item.id === 'systemEfficiency') {
item.value = (item.value * 100).toFixed(2)
}
if (item.id === 'totalChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'totalDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'currentPower' && item.value >= 1E4) {
item.unit = 'MW'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
getTimeList() {
for (let i = 0; i < 24; i++) {
this.timeList.push({
label: `${i < 10 ? '0' + i : i}:00:00`,
value: `${i < 10 ? '0' + i : i}:00:00`
})
}
},
setRule() {
this.ruleShow = true
},
selectRuleTime(val) {
this.timeForm.nowDayTime = this.getEightTime('2020-07-28 ' + val)
},
cancelRule() {
this.ruleShow = false
},
sureRule() {
if (this.timeOperate === 1) {
// 新增
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime
}
addElecMeterConfig(param)
.then((res) => {
this.$message.success('新增成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
} else {
// 编辑
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime,
id: this.timeForm.id
}
updateElecMeterConfig(param)
.then((res) => {
this.$message.success('更新成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
}
},
/**
* 指定时间的多少小时或多少分钟之后的时间
* @param {*} str 2020-07-28 15:00:00
*/
getEightTime(str) {
const t = new Date(str).getTime() + 24 * 60 * 60 * 1000 - 1 // 24小时 * 60分钟 * 60秒 * 1000
const d = new Date(t)
let theMonth = d.getMonth() + 1
let theDate = d.getDate()
let theHours = d.getHours()
let theMinutes = d.getMinutes()
let theSecond = d.getSeconds()
if (theMonth < 10) {
theMonth = '0' + theMonth
}
if (theDate < 10) {
theDate = '0' + theDate
}
if (theHours < 10) {
theHours = '0' + theHours
}
if (theMinutes < 10) {
theMinutes = '0' + theMinutes
}
if (theSecond < 10) {
theSecond = '0' + theSecond
}
// let date = d.getFullYear() + '-' + theMonth + '-' + theDate
const time = theHours + ':' + theMinutes + ':' + theSecond
// let Spare = date + ' ' + time
// console.log(date)
// console.log(time)
// console.log(Spare)
return time
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .item-con{
overflow:auto;
}
.top-left-box{
width: 100%;
height: 100%;
overflow: hidden;
.header-right-box {
display: flex;
align-items: center;
.header-title {
white-space: nowrap;
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active{
color: #ffffff;
background: linear-gradient(90deg, rgba(0,148,255,0.00) 0%, rgba(0,148,255,0.43) 51%, rgba(0,148,255,0.00) 99%);
}
}
}
}
.box{
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 0;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -0,0 +1,378 @@
<template>
<div class="top-left-box">
<ItemBox :title="$t('dashboard.stationData')" :show-point-setting="true" @handleSetting="handleSetting">
<div slot="header">
<div class="header-right-box">
<div class="header-title" :class="{'active':currentIndex === 0}" @click="changeType(0)">{{ $t('dashboard.dataOverView') }}</div>
<div class="header-title" :class="{'active':currentIndex === 1}" @click="changeType(1)">{{ $t('dashboard.stationInfo') }}</div>
</div>
</div>
<div v-if="currentIndex === 0" v-loading="loading" class="box">
<StationData
:table-data=" stationId !== 1069 ? [ ...overflowData,...dynamicData] : [ ...dynamicData]"
/>
</div>
<div v-else v-loading="loading" class="box">
<StationData :table-data="totalData" />
</div>
</ItemBox>
<el-dialog :title="$t('dashboard.pzsjy')" :visible.sync="configVisible" width="30%" @close="closeConfig">
<el-form ref="configForm" :model="configForm" :rules="rules" label-width="135px">
<el-form-item :label="$t('dashboard.time')" prop="time">
<el-date-picker v-model="configForm.time" style="width: 100%" type="date" :picker-options="pickerOptionsStart" value-format="yyyy-MM-dd" :placeholder="$t('remote.pleaseSelect')" />
</el-form-item>
<el-form-item :label="$t('dashboard.generation') + '(kWh)'" prop="param1">
<el-input-number v-model="configForm.param1" style="width: 100%" :max="9999999" :min="-9999999" :controls="false" :placeholder="$t('remote.pleaseInput')" />
</el-form-item>
<el-form-item :label="$t('dashboard.yhl') + '(L)'" prop="param2">
<el-input-number v-model="configForm.param2" style="width: 100%" :max="9999999" :min="-9999999" :controls="false" :placeholder="$t('remote.pleaseInput')" />
</el-form-item>
<el-form-item :label="$t('dashboard.jsfs')" prop="countMethod">
<el-select v-model="configForm.countMethod" style="width:100%" :placeholder="$t('remote.pleaseSelect')">
<el-option v-for="item in countMethod" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="closeConfig">{{ $t('dashboard.cancel') }}</el-button>
<el-button type="primary" :loading="sureLoading" @click="sureConfig">{{ $t('dashboard.sure') }}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { ToDegrees } from '@/utils/index'
import { GetPcsStationData, DynamicConfigPoint, SetDataConfig } from '@/api/home-page/index'
import { GetRAPcsTotalData } from '@/api/homePage-integrated/index'
import dayImg from '../../../../assets/station-data/run-day.png'
import capImg from '../../../../assets/station-data/Installed-capacity.png'
import disImg from '../../../../assets/station-data/total-discharge.png'
import otherImg from '../../../../assets/station-data/station-data.png'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
},
permissionId: {
type: Number,
default: 0
}
},
data() {
return {
currentIndex: 0,
dayImg,
capImg,
disImg,
otherImg,
overflowData: [{
name: this.$t('dashboard.safeDays') + `(${this.$t('dashboard.day')})`,
id: 'operationDays',
picture: dayImg,
value: 0
}, {
name: this.$t('dashboard.totalCapacity') + `(kWh)`,
id: 'totalCapacity',
picture: capImg,
value: 0
}, {
name: this.$t('dashboard.systemConversionEfficiency') + `(%)`,
id: 'systemEfficiency',
picture: disImg,
value: 0
}],
totalData: [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}],
stationType: [],
loading: false,
timeForm: {
beforeTime: undefined,
nowDayTime: undefined
},
dynamicData: [],
configVisible: false,
configForm: {
countMethod: '4'
},
sureLoading: false,
selectItem: {},
rules: {
time: [{ required: true, message: this.$t('remote.pleaseSelect'), trigger: 'change' }],
param1: [{ required: true, message: this.$t('remote.pleaseInput'), trigger: 'blur' }],
param2: [{ required: true, message: this.$t('remote.pleaseInput'), trigger: 'blur' }],
countMethod: [{ required: true, message: this.$t('remote.pleaseSelect'), trigger: 'change' }]
},
pickerOptionsStart: {
// 时间不能大于当前时间
disabledDate: (time) => {
return time.getTime() > Date.now()
}
}
}
},
computed: {
stationTypeList() {
return this.$store.getters.dicts['stationType'] || []
},
countMethod() {
return this.$store.getters.dicts['commonCountSymbol'] || []
},
permission() {
return this.$store.getters.permissions
},
language() {
return this.$store.getters.language || undefined
}
},
watch: {
language: {
handler() {
this.overflowData = [{
name: this.$t('dashboard.safeDays') + `(${this.$t('dashboard.day')})`,
id: 'operationDays',
picture: dayImg,
value: 0
}, {
name: this.$t('dashboard.totalCapacity') + `(kWh)`,
id: 'totalCapacity',
picture: capImg,
value: 0
}, {
name: this.$t('dashboard.systemConversionEfficiency') + `(%)`,
id: 'systemEfficiency',
picture: disImg,
value: 0
}]
this.totalData = [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}]
},
deep: true
}
},
mounted() { },
methods: {
configData(item) {
if (item.isConfig && this.permission.includes('business:DataConfig:configData')) {
this.selectItem = item
this.configVisible = true
this.$nextTick(() => {
this.$refs.configForm.clearValidate()
})
}
},
sureConfig() {
this.$refs.configForm.validate(async(valid) => {
if (valid) {
this.sureLoading = true
try {
await SetDataConfig({
...this.configForm,
stationId: this.selectItem.stationId,
srcId: this.selectItem.srcId,
deviceType: this.selectItem.deviceType,
col: this.selectItem.col
})
this.$message.success(this.$t('dashboard.saveSuccess'))
this.getData()
this.closeConfig()
} finally {
this.sureLoading = false
}
}
})
},
closeConfig() {
this.configForm = {
countMethod: '4'
}
this.configVisible = false
},
handleSetting() {
this.$emit('handleSetting')
},
getStationTypeName(item) {
const typeIndex = this.stationTypeList.findIndex(t => Number(t.value) === Number(item))
return this.stationTypeList[typeIndex].label
},
changeType(val) {
this.currentIndex = val
},
getData() {
this.GetPcsStationData()
this.GetPcsTotalData()
},
async GetPcsStationData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetPcsStationData(params)
this.totalData.forEach((item) => {
item.value = data[item.id]
if (item.id === 'type') {
item.value = this.getStationTypeName(item.value)
}
if (item.id === 'longitude') {
item.value = ToDegrees(item.value, 1)
}
if (item.id === 'latitude') {
item.value = ToDegrees(item.value, 2)
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
async GetPcsTotalData() {
this.loading = true
const params = {
stationId: this.stationId,
pageLocation: 'stationData',
permissionId: this.permissionId
}
try {
const { data } = await GetRAPcsTotalData(params)
const res = await DynamicConfigPoint(params)
this.overflowData.forEach((item) => {
if (data[item.id]) {
item.value = data[item.id]
}
if (item.id === 'systemEfficiency') {
item.value = (item.value * 100).toFixed(2)
}
})
this.dynamicData = res.data
this.dynamicData.forEach((item) => {
item.picture = otherImg
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .item-con {
overflow: auto;
}
.top-left-box {
width: 100%;
height: 100%;
overflow: hidden;
.header-right-box {
display: flex;
align-items: center;
.header-title {
white-space: nowrap;
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active {
color: #ffffff;
background: linear-gradient(
90deg,
rgba(0, 148, 255, 0) 0%,
rgba(0, 148, 255, 0.43) 51%,
rgba(0, 148, 255, 0) 99%
);
}
}
}
}
.box {
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 0;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -0,0 +1,498 @@
<template>
<div class="top-left-box">
<ItemBox :title="$t('dashboard.stationData')">
<div slot="header">
<div class="header-right-box">
<div class="header-title" :class="{ 'active': currentIndex === 0 }" @click="changeType(0)">{{ $t('dashboard.dataOverView') }}</div>
<div class="header-title" :class="{ 'active': currentIndex === 1 }" @click="changeType(1)">{{ $t('dashboard.stationInfo') }}</div>
</div>
</div>
<div v-if="currentIndex === 0" v-loading="loading" class="box">
<StationData :table-data="overflowData" />
</div>
<div v-else v-loading="loading" class="box">
<StationData :table-data="totalData" />
</div>
<el-dialog
:append-to-body="true"
title="日充放统计规则"
:visible.sync="ruleShow"
center
width="30%"
:close-on-click-modal="false"
>
<el-form :model="timeForm" label-width="120px">
<el-form-item label="前一日" class="select-box">
<el-select
v-model="timeForm.beforeTime"
style="width:100%"
placeholder="请选择"
@change="selectRuleTime"
>
<template slot="prefix">
<span style="padding-left: 5px;">
<i class="el-icon-time" />
</span>
</template>
<el-option
v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="今日">
<el-time-picker
v-model="timeForm.nowDayTime"
style="width:100%"
:clearable="false"
readonly
placeholder="请选择时间"
value-format="HH:mm:ss"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelRule"> </el-button>
<el-button
type="primary"
:loading="timeSubmitLoading"
@click="sureRule"
> </el-button>
</div>
</el-dialog>
</ItemBox>
</div>
</template>
<script>
import { GetPcsStationData, addElecMeterConfig,
updateElecMeterConfig } from '@/api/home-page/index'
import { GetRAPcsTotalData } from '@/api/homePage-integrated/index'
import { ToDegrees } from '@/utils/index'
import disImg from '../../../../assets/station-data/total-discharge.png'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
currentIndex: 0,
disImg,
totalData: [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}],
overflowData: [{
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: disImg,
value: 0,
unit: 'kW'
}, {
name: this.$t('dashboard.todayElectricityGeneration'),
id: 'todayElecGeneration',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.totalPowerGeneration'),
id: 'totalPowerGeneration',
picture: disImg,
value: 0,
unit: 'kWh'
},
{
name: this.$t('dashboard.totalRevenue'),
id: 'totalRevenue',
picture: disImg,
value: 0,
unit: 'kWh',
click: true
}, {
name: this.$t('dashboard.ratedPowerofInverter'),
id: 'ratedPowerofInverter',
picture: disImg,
value: 0,
unit: 'kW'
}],
stationType: [],
loading: false,
ruleShow: false,
timeForm: {
beforeTime: undefined,
nowDayTime: undefined
},
timeSubmitLoading: false,
timeOperate: 1, // 1 新增 2 编辑
timeList: []
}
},
computed: {
stationTypeList() {
return this.$store.getters.dicts['stationType'] || []
},
language() {
return this.$store.getters.language || undefined
}
},
watch: {
dailyTime: {
handler(val) {
if (val) {
this.timeForm = val
this.timeOperate = val.timeOperate
this.GetPcsTotalData()
}
},
deep: true,
immediate: true
},
language: {
handler() {
this.totalData = [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}]
this.overflowData = [{
name: this.$t('dashboard.safeDays'),
id: 'operationDays',
picture: disImg,
value: 0,
unit: this.$t('dashboard.day')
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: disImg,
value: 0,
unit: '%'
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: disImg,
value: 0,
unit: 'kW'
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.stationId === 753 ? this.$t('dashboard.capacityIncrease') : this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
},
{
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: disImg,
value: 0,
unit: 'kWh',
click: true
}, {
name: this.stationId === 753 ? this.$t('dashboard.DailycapacityIncrease') : this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}]
},
deep: true
}
},
created() {
// console.log(this.stationTypeList)
this.getTimeList()
},
mounted() { },
methods: {
getStationTypeName(item) {
if (item && item !== '') {
const typeIndex = this.stationTypeList.findIndex(t => Number(t.value) === Number(item))
return this.stationTypeList[typeIndex].label
}
},
changeType(val) {
this.currentIndex = val
},
getData() {
this.GetPcsStationData()
this.GetPcsTotalData()
},
async GetPcsStationData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetPcsStationData(params)
this.totalData.forEach((item) => {
item.value = data[item.id]
if (item.id === 'type') {
item.value = this.getStationTypeName(item.value)
}
if (item.id === 'longitude') {
item.value = ToDegrees(item.value, 1)
}
if (item.id === 'latitude') {
item.value = ToDegrees(item.value, 2)
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
async GetPcsTotalData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetRAPcsTotalData(params)
this.overflowData.forEach((item) => {
if (data[item.id]) {
item.value = data[item.id]
}
if (item.id === 'systemEfficiency') {
item.value = (item.value * 100).toFixed(2)
}
if (item.id === 'totalCapacity' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'totalChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'totalDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'currentPower' && item.value >= 1E4) {
item.unit = 'MW'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
getTimeList() {
for (let i = 0; i < 24; i++) {
this.timeList.push({
label: `${i < 10 ? '0' + i : i}:00:00`,
value: `${i < 10 ? '0' + i : i}:00:00`
})
}
},
setRule() {
this.ruleShow = true
},
selectRuleTime(val) {
this.timeForm.nowDayTime = this.getEightTime('2020-07-28 ' + val)
},
cancelRule() {
this.ruleShow = false
},
sureRule() {
if (this.timeOperate === 1) {
// 新增
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime
}
addElecMeterConfig(param)
.then((res) => {
this.$message.success('新增成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
} else {
// 编辑
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime,
id: this.timeForm.id
}
updateElecMeterConfig(param)
.then((res) => {
this.$message.success('更新成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
}
},
/**
* 指定时间的多少小时或多少分钟之后的时间
* @param {*} str 2020-07-28 15:00:00
*/
getEightTime(str) {
const t = new Date(str).getTime() + 24 * 60 * 60 * 1000 - 1 // 24小时 * 60分钟 * 60秒 * 1000
const d = new Date(t)
let theMonth = d.getMonth() + 1
let theDate = d.getDate()
let theHours = d.getHours()
let theMinutes = d.getMinutes()
let theSecond = d.getSeconds()
if (theMonth < 10) {
theMonth = '0' + theMonth
}
if (theDate < 10) {
theDate = '0' + theDate
}
if (theHours < 10) {
theHours = '0' + theHours
}
if (theMinutes < 10) {
theMinutes = '0' + theMinutes
}
if (theSecond < 10) {
theSecond = '0' + theSecond
}
// let date = d.getFullYear() + '-' + theMonth + '-' + theDate
const time = theHours + ':' + theMinutes + ':' + theSecond
// let Spare = date + ' ' + time
// console.log(date)
// console.log(time)
// console.log(Spare)
return time
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .item-con{
overflow:auto;
}
.top-left-box{
width: 100%;
height: 100%;
overflow: hidden;
.header-right-box {
display: flex;
align-items: center;
.header-title {
white-space: nowrap;
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active{
color: #ffffff;
background: linear-gradient(90deg, rgba(0,148,255,0.00) 0%, rgba(0,148,255,0.43) 51%, rgba(0,148,255,0.00) 99%);
}
}
}
}
.box{
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 0;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -0,0 +1,540 @@
<template>
<div class="top-left-box">
<ItemBox :title="$t('dashboard.stationData')">
<div slot="header">
<div class="header-right-box">
<div class="header-title" :class="{'active':currentIndex === 0}" @click="changeType(0)">{{ $t('dashboard.dataOverView') }}</div>
<div class="header-title" :class="{'active':currentIndex === 1}" @click="changeType(1)">{{ $t('dashboard.stationInfo') }}</div>
</div>
</div>
<div v-if="currentIndex === 0" v-loading="loading" class="box">
<StationData :table-data="overflowData" />
</div>
<div v-else v-loading="loading" class="box">
<StationData :table-data="totalData" />
</div>
<el-dialog
:append-to-body="true"
title="日充放统计规则"
:visible.sync="ruleShow"
center
width="30%"
:close-on-click-modal="false"
>
<el-form :model="timeForm" label-width="120px">
<el-form-item label="前一日" class="select-box">
<el-select
v-model="timeForm.beforeTime"
style="width:100%"
placeholder="请选择"
@change="selectRuleTime"
>
<template slot="prefix">
<span style="padding-left: 5px;">
<i class="el-icon-time" />
</span>
</template>
<el-option
v-for="item in timeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="今日">
<el-time-picker
v-model="timeForm.nowDayTime"
style="width:100%"
:clearable="false"
readonly
placeholder="请选择时间"
value-format="HH:mm:ss"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelRule"> </el-button>
<el-button
type="primary"
:loading="timeSubmitLoading"
@click="sureRule"
> </el-button>
</div>
</el-dialog>
</ItemBox>
</div>
</template>
<script>
import { GetPcsStationData, addElecMeterConfig,
updateElecMeterConfig } from '@/api/home-page/index'
import { GetRAPcsTotalData, GetZZHBday } from '@/api/homePage-integrated/index'
import { ToDegrees } from '@/utils/index'
import disImg from '../../../../assets/station-data/total-discharge.png'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
disImg,
currentIndex: 0,
totalData: [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}],
overflowData: [{
name: this.$t('dashboard.safeDays'),
id: 'operationDays',
picture: disImg,
value: 0,
unit: this.$t('dashboard.day')
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: disImg,
value: 0,
unit: '%'
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: disImg,
value: 0,
unit: 'kW'
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dayPhotovoltaic'),
id: 'cumulativePowerGeneration',
picture: disImg,
value: 0,
unit: 'kWh'
}],
stationType: [],
loading: false,
ruleShow: false,
timeForm: {
beforeTime: undefined,
nowDayTime: undefined
},
timeSubmitLoading: false,
timeOperate: 1, // 1 新增 2 编辑
timeList: []
}
},
computed: {
stationTypeList() {
return this.$store.getters.dicts['stationType'] || []
},
language() {
return this.$store.getters.language || undefined
}
},
watch: {
dailyTime: {
handler(val) {
if (val) {
this.timeForm = val
this.timeOperate = val.timeOperate
this.GetPcsTotalData()
}
},
deep: true,
immediate: true
},
language: {
handler() {
this.totalData = [{
name: this.$t('dashboard.stationName'),
id: 'name',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationType'),
id: 'type',
picture: disImg,
value: 0
}, {
name: this.$t('dashboard.stationLocation'),
id: 'address',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.commTime'),
id: 'createTime',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.log'),
id: 'longitude',
picture: disImg,
value: 0
},
{
name: this.$t('dashboard.lat'),
id: 'latitude',
picture: disImg,
value: 0
}]
this.overflowData = [{
name: this.$t('dashboard.safeDays'),
id: 'operationDays',
picture: disImg,
value: 0,
unit: this.$t('dashboard.day')
}, {
name: this.$t('dashboard.totalCapacity'),
id: 'totalCapacity',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.systemConversionEfficiency'),
id: 'systemEfficiency',
picture: disImg,
value: 0,
unit: '%'
}, {
name: this.$t('dashboard.currentPower'),
id: 'currentPower',
picture: disImg,
value: 0,
unit: 'kW'
}, {
name: this.$t('dashboard.totalCharge'),
id: 'totalChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.totalDischarge'),
id: 'totalDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dailyCharge'),
id: 'dailyChargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dailyDischarge'),
id: 'dailyDischargeElec',
picture: disImg,
value: 0,
unit: 'kWh'
}, {
name: this.$t('dashboard.dayPhotovoltaic'),
id: 'cumulativePowerGeneration',
picture: disImg,
value: 0,
unit: 'kWh'
}]
},
deep: true
}
},
created() {
this.getTimeList()
},
mounted() { },
methods: {
getStationTypeName(item) {
const typeIndex = this.stationTypeList.findIndex(t => Number(t.value) === Number(item))
return this.stationTypeList[typeIndex].label
},
changeType(val) {
this.currentIndex = val
},
getData() {
this.GetPcsStationData()
this.GetPcsTotalData()
this.GetZZHBdayInverter()
},
async GetZZHBdayInverter() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetZZHBday(params)
this.totalData = Object.assign(this.totalData, data)
} finally {
this.loading = false
}
},
async GetPcsStationData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetPcsStationData(params)
this.totalData.forEach((item) => {
item.value = data[item.id]
if (item.id === 'type') {
item.value = this.getStationTypeName(item.value)
}
if (item.id === 'longitude') {
item.value = ToDegrees(item.value, 1)
}
if (item.id === 'latitude') {
item.value = ToDegrees(item.value, 2)
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
async GetPcsTotalData() {
this.loading = true
const params = {
stationId: this.stationId
}
try {
const { data } = await GetRAPcsTotalData(params)
// this.totalData = Object.assign(this.totalData, data)
this.overflowData.forEach((item) => {
if (data[item.id]) {
item.value = data[item.id]
}
if (item.id === 'systemEfficiency') {
item.value = (item.value * 100).toFixed(2)
}
if (item.id === 'totalCapacity' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'totalChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'totalDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyDischargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'dailyChargeElec' && item.value >= 1E4) {
item.unit = 'MWh'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'currentPower' && item.value >= 1E4) {
item.unit = 'MW'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
if (item.id === 'cumulativePowerGeneration' && item.value >= 1E4) {
item.unit = 'MW'
item.value = (Number(item.value) / 1E3).toFixed(2) + ''
}
})
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
getTimeList() {
for (let i = 0; i < 24; i++) {
this.timeList.push({
label: `${i < 10 ? '0' + i : i}:00:00`,
value: `${i < 10 ? '0' + i : i}:00:00`
})
}
},
setRule() {
this.ruleShow = true
},
selectRuleTime(val) {
this.timeForm.nowDayTime = this.getEightTime('2020-07-28 ' + val)
},
cancelRule() {
this.ruleShow = false
},
sureRule() {
if (this.timeOperate === 1) {
// 新增
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime
}
addElecMeterConfig(param)
.then((res) => {
this.$message.success('新增成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
} else {
// 编辑
this.timeSubmitLoading = true
const param = {
stationId: this.stationId,
beginTime: this.timeForm.beforeTime,
endTime: this.timeForm.nowDayTime,
id: this.timeForm.id
}
updateElecMeterConfig(param)
.then((res) => {
this.$message.success('更新成功')
this.ruleShow = false
this.$emit('updateTime')
})
.finally(() => {
this.timeSubmitLoading = false
})
}
},
/**
* 指定时间的多少小时或多少分钟之后的时间
* @param {*} str 2020-07-28 15:00:00
*/
getEightTime(str) {
const t = new Date(str).getTime() + 24 * 60 * 60 * 1000 - 1 // 24小时 * 60分钟 * 60秒 * 1000
const d = new Date(t)
let theMonth = d.getMonth() + 1
let theDate = d.getDate()
let theHours = d.getHours()
let theMinutes = d.getMinutes()
let theSecond = d.getSeconds()
if (theMonth < 10) {
theMonth = '0' + theMonth
}
if (theDate < 10) {
theDate = '0' + theDate
}
if (theHours < 10) {
theHours = '0' + theHours
}
if (theMinutes < 10) {
theMinutes = '0' + theMinutes
}
if (theSecond < 10) {
theSecond = '0' + theSecond
}
// let date = d.getFullYear() + '-' + theMonth + '-' + theDate
const time = theHours + ':' + theMinutes + ':' + theSecond
// let Spare = date + ' ' + time
// console.log(date)
// console.log(time)
// console.log(Spare)
return time
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .item-con{
overflow:auto;
}
.top-left-box{
width: 100%;
height: 100%;
overflow: hidden;
.header-right-box {
display: flex;
align-items: center;
.header-title {
white-space: nowrap;
font-size: 14px;
font-weight: 500;
letter-spacing: 0em;
color: rgba(206, 235, 255, 0.5);
padding: 0 10px;
cursor: pointer;
font-family: Source Han Sans CN;
&.active{
color: #ffffff;
background: linear-gradient(90deg, rgba(0,148,255,0.00) 0%, rgba(0,148,255,0.43) 51%, rgba(0,148,255,0.00) 99%);
}
}
}
}
.box{
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 0;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -0,0 +1,571 @@
<template>
<div>
<ItemBox :title="$t('dashboard.oneControl')">
<div v-loading="loading" class="con-box">
<!-- <div class="mode">
<span class="mode-title">工作模式</span>
<template v-if="!currentWorkInfo.noWork">
<span class="mode-title">{{ currentWorkInfo.label }}</span>
<el-button style="margin-left: 10px" circle type="primary" icon="el-icon-edit" @click="sureMode" />
</template>
<span v-else class="noWork"> 请配置关联点</span>
</div> -->
<div class="control-box">
<div v-for="(item, index) in controlList" :key="index" class="control-item-box" @click="handleOpen(item)">
<div class="control-item">
<el-tooltip :content="item.controlName" placement="bottom" effect="dark">
<div>{{ item.controlName }}</div>
</el-tooltip>
</div>
</div>
</div>
</div>
</ItemBox>
<el-dialog
:append-to-body="true"
:show-close="false"
center
:close-on-click-modal="false"
:title="controlInfo.controlName"
:visible="controlShow"
width="40%"
>
<div class="control-box">
<el-steps :space="140" direction="vertical" :active="activeControlStatus">
<el-step v-for="(item, index) in controlInfo.list" :key="index" :title="item.colName">
<div slot="description">
<div class="description-box">
<div class="description-item">
<span class="label">{{ $t('dashboard.col') }}:</span>
<span class="value">{{ item.col }}</span>
</div>
<div class="description-item">
<span class="label">{{ $t('dashboard.state') }}:</span>
<span
v-if="item.statusLabel === `${$t('dashboard.orderTimeOut')}` ||
item.statusLabel === `${$t('dashboard.orderFail')}`
"
class="value"
style="color: red"
>{{ item.statusLabel }}</span>
<span v-else class="value" style="color: #0bbd87">{{
item.statusLabel
}}</span>
</div>
<div class="description-item">
<span class="label">{{ $t('dashboard.orderValue') }}:</span>
<span class="value">{{ item.setValue }}</span>
</div>
</div>
</div>
</el-step>
</el-steps>
</div>
<span slot="footer">
<el-button type="primary" @click="closeControl">{{ $t('dashboard.close') }}</el-button>
</span>
</el-dialog>
<el-dialog
:append-to-body="true"
:close-on-click-modal="false"
:title="$t('dashboard.workingMode')"
:visible="dispatchVisiable"
width="40%"
center
@close="closeModel"
>
<div>
<el-form ref="workInfo" :model="workInfo" :rules="workRules" label-width="210px" label-position="right">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="$t('dashboard.remoteControl')">
<el-select v-model="workInfo.value" style="width: 100%" :placeholder="$t('dashboard.selectMode')" @change="changeWorkInfo">
<el-option v-for="item in modeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col v-if="workInfo.value === 1" :span="24">
<el-form-item :label="$t('dashboard.remoteControlActive') + (kW)" prop="p">
<el-input-number
v-model="workInfo.p"
style="width: 100%"
:controls="false"
:placeholder="$t('dashboard.inputValue')"
class="input-with-select"
>
<template slot="append">kW</template>
</el-input-number>
</el-form-item>
</el-col>
<el-col v-if="workInfo.value === 1" :span="24">
<el-form-item :label="$t('dashboard.remoteControlreActive') + (kVar)" prop="q">
<el-input-number
v-model="workInfo.q"
style="width: 100%"
:controls="false"
:placeholder="$t('dashboard.inputValue')"
class="input-with-select"
>
<template slot="append">kVar</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="closeModel">{{ $t('dashboard.back') }}</el-button>
<el-button type="primary" :loading="issueLoading" @click="confirmIssue">{{ $t('dashboard.issused') }}</el-button>
</span>
</el-dialog>
<el-dialog
:append-to-body="true"
:title=" $t('dashboard.tips')"
:visible.sync="messageShow"
width="20%"
center
@close="messageShow = false"
>
<span style="color: #fff;">{{ $t('dashboard.issusedMsg') }}</span>
<div class="dialog-footer">
<el-button plain @click="messageShow = false"> {{ $t('dashboard.cancel') }} </el-button>
<el-button v-preventReClick type="primary" @click="handleToOrder">
{{ $t('dashboard.sure') }}
</el-button>
</div>
<!-- <span slot="footer">
<el-button @click="messageShow = false">取消</el-button>
<el-button type="primary" @click="handleToOrder">确定</el-button>
</span> -->
</el-dialog>
</div>
</template>
<script>
import {
SendPlanControl,
GetprogressBar
} from '@/api/strategic-management/strategy.js'
import { GetControlList } from '@/api/strategic-management/chronological-control'
import {
OneKeyControl, GetTabModel
} from '@/api/strategic-management/policyOverview'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
const valueRule = (rule, value, callback) => {
if (value === null || value === '' || value === undefined) {
callback(new Error(this.$t('dashboard.inputValue')))
} else {
if (value > 65535 || value < -65535) {
callback(new Error(this.$t('dashboard.dataRange') - '65535~65535'))
} else {
return callback()
}
}
}
return {
workRules: {
p: [{ validator: valueRule, required: true, trigger: 'blur' }],
q: [{ validator: valueRule, required: true, trigger: 'blur' }]
},
currentWorkInfo: {
id: undefined,
value: '',
p: undefined,
q: undefined,
noWork: false
},
workInfo: {
id: undefined,
value: '',
p: undefined,
q: undefined,
noWork: false
},
controlList: [],
loading: false,
controlInfo: {
controlName: '',
list: []
},
modeOptions: [
{ label: `${this.$t('dashboard.psvf')}`, value: 0 },
{ label: `${this.$t('dashboard.reakSchedu')}`, value: 1 }
],
controlTimer: null,
activeControlStatus: 0,
activeIndex: 0, // 当前下发的index
controlShow: false,
dispatchVisiable: false,
issueLoading: false,
messageShow: false,
orderItem: {}
}
},
computed: {
},
created() {
},
mounted() {
},
methods: {
changeWorkInfo(val) {
this.workInfo = {
value: val,
p: undefined,
q: undefined
}
},
async confirmIssue() {
this.$refs.workInfo.validate((valid) => {
if (valid) {
try {
this.$confirm(
this.$t('dashboard.sureDispatch'),
this.$t('dashboard.tips'),
{
confirmButtonText: this.$t('dashboard.sure'),
cancelButtonText: this.$t('dashboard.cancel'),
type: 'warning',
showClose: false,
center: true
}
)
.then(async() => {
this.issueLoading = true
const res = await SendPlanControl({
...this.workInfo,
stationId: this.stationId
})
this.getprogressBar(res.data)
})
.catch(() => { })
} catch (err) {
this.issueLoading = false
}
}
})
},
async getprogressBar(val) {
this.issueLoading = true
const timer = setInterval(async() => {
const { data } = await GetprogressBar(val)
if (data.isEnd === '1' || data.isEnd === 1) {
this.$message.success(data.msg)
clearInterval(timer)
this.issueLoading = false
this.getTabModel()
}
if (data.isEnd === '2' || data.isEnd === 2) {
this.$message.warning(data.msg)
clearInterval(timer)
this.issueLoading = false
}
}, 1000)
},
async getTabModel() {
const res = await GetTabModel({ stationId: this.stationId })
if (
res.data.planControlModel !== undefined &&
res.data.planControlModel !== null
) {
this.currentWorkInfo.value = res.data.planControlModel
this.currentWorkInfo.p = res.data?.planControlP
this.currentWorkInfo.q = res.data?.planControlQ
this.currentWorkInfo.label = this.modeOptions.find(
(item) => item.value === res.data?.planControlModel
)?.label
this.currentWorkInfo.noWork = false
this.workInfo.value = res.data.planControlModel
this.workInfo.p = res.data?.planControlP
this.workInfo.q = res.data?.planControlQ
this.workInfo.label = this.modeOptions.find(
(item) => item.value === res.data?.planControlModel
)?.label
this.workInfo.noWork = false
} else {
this.workInfo.noWork = true
this.currentWorkInfo.noWork = true
}
},
closeControl() {
this.controlShow = false
if (this.controlTimer) {
clearInterval(this.controlTimer)
}
},
sureMode() {
this.dispatchVisiable = true
},
async getControlList() {
const res = await GetControlList({
stationId: this.stationId
})
this.controlList = res.data
},
getData() {
this.loading = true
Promise.all([this.getTabModel(), this.getControlList()]).finally(() => {
this.loading = false
})
},
closeModel() {
this.dispatchVisiable = false
this.workInfo = JSON.parse(JSON.stringify(this.currentWorkInfo))
},
handleOpen(item) {
this.orderItem = item
this.messageShow = true
},
handleToOrder() {
this.controlInfo = this.orderItem
this.controlInfo.list = this.orderItem.list.map((i) => {
i.statusLabel = '未下发'
return i
})
this.activeIndex = 0
this.activeControlStatus = 0
this.toIssueByOrder()
this.controlShow = true
},
toIssueByOrder() {
this.messageShow = false
if (this.activeIndex <= this.controlInfo.list.length - 1) {
this.getGroupIssueProgress()
this.controlTimer = setInterval(() => {
this.getGroupIssueProgress()
}, 1000)
} else {
clearInterval(this.controlTimer)
}
},
async getGroupIssueProgress() {
try {
const res = await OneKeyControl([
{
...this.controlInfo.list[this.activeIndex],
isLast:
this.activeIndex === this.controlInfo.list.length - 1 ? 1 : 0
}
])
if (
res.data.isEnd === '1' ||
res.data.isEnd === '2' ||
res.data.isEnd === '3' ||
res.data.isEnd === 1 ||
res.data.isEnd === 2 ||
res.data.isEnd === 3
) {
if (res.data.isEnd === '1' || res.data.isEnd === 1) {
this.$set(this.controlInfo.list, this.activeIndex, {
...this.controlInfo.list[this.activeIndex],
statusLabel: this.$t('dashboard.orderSuccess')
})
this.activeIndex += 1
this.activeControlStatus += 1
if (this.controlTimer) {
clearInterval(this.controlTimer)
}
this.toIssueByOrder()
this.$message.success(`${this.$t('dashboard.orderSuccess')}`)
}
if (res.data.isEnd === '2' || res.data.isEnd === 2) {
this.$set(this.controlInfo.list, this.activeIndex, {
...this.controlInfo.list[this.activeIndex],
statusLabel: this.$t('dashboard.orderFail')
})
if (this.controlTimer) {
clearInterval(this.controlTimer)
}
}
if (res.data.isEnd === '3' || res.data.isEnd === 3) {
this.$set(this.controlInfo.list, this.activeIndex, {
...this.controlInfo.list[this.activeIndex],
statusLabel: this.$t('dashboard.orderTimeOut')
})
if (this.controlTimer) {
clearInterval(this.controlTimer)
}
}
} else if (res.data.isEnd === '0' || res.data.isEnd === 0) {
this.$set(this.controlInfo.list, this.activeIndex, {
...this.controlInfo.list[this.activeIndex],
statusLabel: this.$t('dashboard.orderLoading')
})
} else if (res.data.isEnd === '') {
if (this.controlTimer) {
clearInterval(this.controlTimer)
}
}
} catch (error) {
if (this.controlTimer) {
clearInterval(this.controlTimer)
}
}
}
}
}
</script>
<style lang="scss" scoped>
.con-box {
width: 100%;
height: 169px;
padding: 10px;
display: flex;
flex-direction: column;
}
.mode {
width: 100%;
.mode-title {
padding-right: 10px;
color: #fff;
font-size: 14px;
}
}
.control-box {
flex: 1;
width: 100%;
overflow-y: auto;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
justify-content: space-evenly;
.control-item-box {
margin-top: 15px;
}
.control-item {
width: 190px;
height: 40px;
background-image: url("../../../../assets/images/controlbg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
box-sizing: border-box;
font-size: 16px;
color: #ffffff;
text-align: center;
line-height: 40px;
word-break: break-all;
padding-left: 10px;
cursor: pointer;
div {
width: 150px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
.noWork {
font-size: 14px;
color: #bfc6ce;
}
.description-box {
display: flex;
flex-direction: column;
.description-item {
display: flex;
flex-direction: row;
color: #fff !important;
font-size: 14px;
.label {
width: 60px;
}
}
}
::v-deep .el-step__title.is-process {
color: #ffffff !important;
}
.case-progress {
::v-deep .el-progress-bar__outer {
background-color: #023b61;
}
}
// 默认当前激活步骤显示的是黑色,这里修改显示为蓝色
::v-deep .el-step__head.is-process {
color: #ffffff !important; // 圆圈中数字
border-color: #0bbd87 !important; // 圆圈
.el-step__icon {
background-color: #0bbd87 !important;
}
}
::v-deep .el-step__title.is-process {
color: #0bbd87 !important; // 圆圈下的标题文本
}
::v-deep .el-step__head.is-finish {
color: #ffffff !important; // 圆圈中数字
border-color: #409eff !important; // 圆圈
.el-step__icon {
background-color: #409eff !important;
}
}
::v-deep .el-step__title.is-finish {
color: #409eff !important; // 圆圈下的标题文本
}
::v-deep .el-dialog {
display: flex;
flex-direction: column;
margin: 0 !important;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-height: calc(100% - 30px);
max-width: calc(100% - 30px);
}
::v-deep .el-dialog .el-dialog__body {
flex: 1;
overflow: auto;
}
.dialog-footer{
text-align:center;
margin-top:20px;
}
</style>

View File

@ -0,0 +1,331 @@
<template>
<div class="top-right-box">
<ItemBox :title="$t('dashboard.realtimeAlarm')">
<div v-loading="loading" class="box">
<div class="alarm-list">
<template v-if="listData.length">
<div v-for="(item, index) in listData" :key="index" :class="index % 2 ? 'item1' : 'item'">
<div class="alarm-title">
<div class="dev">
<span class="title">{{ $t('dashboard.device') }}</span>
<span class="value">{{ item.deviceName }}</span>
</div>
<div class="time-box">
<div class="time">{{ item.createTime }}</div>
</div>
<div class="leve">
<span :class="item.name === '异常' ? 'leve-value' : 'leve-value2'">{{ item.name }}</span>
</div>
</div>
<div class="bottom">
<span class="content">{{ item.description }}</span>
</div>
</div>
</template>
<div v-else style="height:100%;text-align:center;font-size:14px;color:#999;display: flex;justify-content: center;align-items: center;">
{{ $t('screen.runSuccess' ) }}
</div>
</div>
</div>
</ItemBox>
</div>
</template>
<script>
import { GetAlarmList } from '@/api/alarm/realtime'
export default {
name: 'Index',
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
config: {
header: ['等级', '内容', '时间'],
data: [],
columnWidth: [80],
align: ['center', 'center', 'center', 'center'],
headerBGC: '#062b40',
oddRowBGC: 'rgba(255, 255, 255, 0)',
evenRowBGC: 'rgba(255, 255, 255, 0.05)'
},
pageflag: false,
listData: [],
loading: false
}
},
computed: {
eventLevels() {
return this.$store.getters.dicts['eventLevel'] || []
},
eventTypes() {
return this.$store.getters.dicts['eventType'] || []
}
},
watch: {},
created() {
// this.GetAlarmList()
},
mounted() {
// this.getData()
},
methods: {
oldsortData(data) {
const result = data.sort(function(a, b) {
return b.createTime - a.createTime
})
return result
},
async getData() {
this.loading = true
this.pageflag = false
const params = {
pageNum: 1,
pageSize: 50,
status: 0,
stationIds: this.stationId ? [this.stationId] : [],
deviceTypeList: [],
description: '',
eventLevels: ['1', '2', '3'],
eventTypes: ['2', '3', '4', '5', '6']
}
this.config.data = []
try {
const res = await GetAlarmList(params)
// const sortDataRes = this.oldsortData(res.data.list)
const chartData = []
res.data.list.forEach(item => {
const eventLevel = this.getEventLevelsname(item.eventLevel)
const lastIndex = item.timeStamp.lastIndexOf('.')
const result = item.timeStamp.slice(0, lastIndex)
item.timeStamp = result
const i = {
name: eventLevel,
deviceName: item.name,
description: item.description,
createTime: item.timeStamp
}
chartData.push(i)
})
// console.log(chartData)
this.listData = chartData
this.pageflag = true
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
getEventLevelsname(item) {
return (
this.eventLevels.find(level => Number(level.value) === item).label || ''
)
}
}
}
</script>
<style lang="scss" scoped>
.top-right-box{
width: 100%;
height: 100%;
overflow: hidden;
.box {
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 0;
overflow-y: auto;
overflow-x: hidden;
font-family: Source Han Sans CN;
.top-box {
width: 100%;
height: 30px;
line-height: 30px;
}
.alarm-list {
width: 100%;
height: 100%;
margin: 0 auto;
overflow: auto;
.alarm-title {
display: flex;
width: 100%;
flex-direction: row;
align-items: center;
.dev {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
.title {
display: inline-block;
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
text-align: center;
letter-spacing: 0px;
color: rgba(206, 235, 255, 0.7);
}
.value {
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
letter-spacing: 0px;
color: rgba(206, 235, 255, 0.7);
width: calc(100% - 45px);
display: -webkit-box; //将盒子转换为弹性盒子
-webkit-box-orient: vertical; //文本显示方式,默认水平
-webkit-line-clamp: 1; //设置显示多少行
overflow: hidden;
}
.time {
/* 12:33:17 */
opacity: 1;
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
letter-spacing: 0px;
color: #ffffff;
z-index: 1;
}
}
.time-box {
min-width: 117px;
height: 100%;
/* 设备PCS */
display: flex;
flex-direction: row;
align-items: center;
margin-right: 5px;
.title {
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
text-align: center;
letter-spacing: 0px;
flex-shrink: 0;
color: rgba(206, 235, 255, 0.7);
}
.time {
/* 12:33:17 */
opacity: 1;
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
letter-spacing: 0px;
color: rgba(206, 235, 255, 0.7);
z-index: 1;
display: -webkit-box; //将盒子转换为弹性盒子
-webkit-box-orient: vertical; //文本显示方式,默认水平
-webkit-line-clamp: 1; //设置显示多少行
overflow: hidden;
}
}
.leve {
// width: 20%;
margin-left: auto;
height: 100%;
/* 异常 */
text-align: center;
opacity: 1;
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
letter-spacing: 0em;
color: #ffeeee;
text-shadow: 0px 0px 5px 0px #ff0000, 0px 0px 5px 0px #ffb800;
.leve-value {
display: inline-block;
width: 40px;
background: url(../../../../assets/images/alarm-error.png) no-repeat;
background-size: 100% 100%;
}
.leve-value2 {
display: inline-block;
width: 40px;
background: url(../../../../assets/images/alarm-error-2.png) no-repeat;
background-size: 100% 100%;
}
}
}
.bottom {
width: 100%;
margin-top: 10px;
padding: 5px;
display: flex;
align-items: center;
background-image: url('../../../../assets/images/alarm-con-bg.png');
background-size: 100% 100%;
.content {
/* 这是内容这是内容这是内容这是内容这是内容这 */
opacity: 1;
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: normal;
letter-spacing: 0px;
color: rgba(255, 255, 255, 1);
display: inline-block;
}
}
.item {
width: 100%;
padding: 5px 10px;
background-color: #0c3650;
background-size: 100% 100%;
margin-bottom: 10px;
}
.item1 {
width: 100%;
padding: 5px 10px;
margin-bottom: 10px;
background-color: transparent;
background-size: 100% 100%;
}
}
}
}
</style>

View File

@ -0,0 +1,509 @@
<template>
<div class="home-container">
<el-row class="top-box" :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
<div class="component-wrapper">
<component
:is="topLeftComponent"
ref="topLeft"
:permission-id="permissionId"
:station-id="stationId"
:daily-time="timeForm"
@handleSetting="handlePointSetting('stationData')"
/>
</div>
</el-col>
<el-col
:xs="24"
:sm="24"
:md="24"
:lg="11"
:xl="11"
class="hidden-md-only hidden-xs-only hidden-sm-only"
>
<div v-if="clientWidth >= 1200" class="tuopu-box">
<component
:is="topCenterComponent"
ref="topCenter"
:station-id="stationId"
:station-type="stationType"
@screenFull="screenFull"
/>
</div>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="7" :xl="7">
<div class="component-wrapper">
<component
:is="topRightComponent"
ref="topRight"
:station-id="stationId"
/>
<!-- <component
:is="centerRightComponent"
ref="centerRight"
:station-id="stationId"
:permission-id="permissionId"
class="center-right"
@handleSetting="handlePointSetting('airData')"
/> -->
</div>
</el-col>
<el-col
:xs="24"
:sm="24"
:md="24"
:lg="11"
:xl="11"
class="hidden-xl-only hidden-lg-only"
>
<div v-if="clientWidth < 1200" class="tuopu-box">
<component
:is="topCenterComponent"
ref="topCenter1"
:station-id="stationId"
:station-type="stationType"
/>
</div>
</el-col>
</el-row>
<el-row class="bottom-box" :gutter="10">
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
<div class="component-bottom">
<!-- <div class="component-bottom" :style="{ height: bottomHeight }"> -->
<component
:is="bottomLeftComponent"
ref="bottomLeft"
:station-id="stationId"
:daily-time="timeForm"
@handleSetting="handlePointSetting('barLeftBottom')"
/>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
<div class="component-bottom">
<!-- <div
class="component-bottom"
:style="{ height: bottomHeight }"
@contextmenu.prevent="onContextmenu"
> -->
<component
:is="bottomRightComponent"
ref="bottomRight"
:permission-id="permissionId"
:station-id="stationId"
@handleSetting="handleSetting('runChart')"
/>
</div>
</el-col>
</el-row>
<!-- 配置曲线 -->
<DispositionChartData :page-location="chartLocation" :disposition-show="show_dispostion" @getData="getDynamicData" @close="close" />
<!-- 配置点 -->
<DispositionPointData :page-location="pageLocation" :disposition-show="show_point_dispostion" @getData="getDynamicPointData" @close="closePoint" />
<!-- 右键菜单 -->
<!-- <rightMenu
:class-index="0"
:rightclick-info="rightclickInfo"
@copy="copy"
/> -->
</div>
</template>
<script>
import rightMenu from '@/components/RightMenu/index.vue'
import commonTopLeft from './components/top-left/common.vue'
import standard215TopLeft from './components/top-left/standard-215.vue'
import zzhbTopLeft from './components/top-left/zzhb.vue'
import commonMWhTopLeft from './components/top-left/common-mwh.vue'
import dispositionTopLeft from './components/top-left/disposition.vue'
import leftBottom_215 from './components/bottom-left/standard-215.vue'
import topRight from './components/top-right/index.vue'
import controlTopRight from './components/top-right/control.vue'
import pvCurve from './components/top-center/pv-operating-curve.vue'
// import fourthTopCenter from './components/top-center/fourth.vue'
// import topCenter_215 from './components/top-center/standard-215.vue'
// import triadTopCenter from './components/top-center/triad.vue'
// import secondTopCenter from './components/top-center/second.vue'
// import fifthTopCenter from './components/top-center/fifth.vue'
// import sixthTopCenter from './components/top-center/sixth.vue'
// import seventhTopCenter from './components/top-center/seventh.vue'
// import eighthTopCenter from './components/top-center/eighth.vue'
// import ninthTopCenter from './components/top-center/ninth.vue'
// import secondDispositionTopCenter from './components/top-center/second-disposition.vue'
// import triadDispositionTopCenter from './components/top-center/triad-disposition.vue'
// import fourthDispositionTopCenter from './components/top-center/fourth-disposition.vue'
// import tenthTopCenter from './components/top-center/ten.vue'
// import onceTopCenter from './components/top-center/once.vue'
import rightCenter_215 from './components/center-right/standard-215.vue'
import commonRightCenter from './components/center-right/common.vue'
import dispositionRightCenter from './components/center-right/disposition.vue'
import commonBottom from './components/bottom-right/common.vue'
import bottom_215 from './components/bottom-right/standard-215.vue'
import dispositionBottomRight from './components/bottom-right/disposition.vue'
import { getDashboard } from '@/api/station/maintain'
import { queryElecMeterConfig } from '@/api/home-page/index'
// import pv1AndStorage_261 from './components/top-center/pv1storage261.vue'
// import pv2AndStorage_261 from './components/top-center/pv2storage261.vue'
export default {
name: 'Index',
components: {
commonMWhTopLeft,
zzhbTopLeft,
leftBottom_215,
commonRightCenter,
commonTopLeft,
standard215TopLeft,
rightCenter_215,
topRight,
commonBottom,
bottom_215,
rightMenu,
dispositionTopLeft,
dispositionRightCenter,
dispositionBottomRight,
controlTopRight,
pvCurve
},
props: {},
data() {
return {
stationId: undefined,
topLeftComponent: '',
bottomLeftComponent: '',
topCenterComponent: '',
topRightComponent: '',
centerRightComponent: '',
bottomRightComponent: '',
stationType: 0,
chartKey: 0,
bottomHeight: '230px',
interval: null,
timeForm: null,
clientWidth: 1920,
centerStatus: 1,
cancalDebounce: undefined,
rightclickInfo: {},
show_dispostion: false,
show_point_dispostion: false,
pageLocation: null,
chartLocation: 'runChart',
permissionId: null
}
},
computed: {
currentStation() {
return this.$store.getters.currentStation || undefined
},
stationStatus() {
return this.$store.getters.stationStatus || undefined
},
language() {
return this.$store.getters.language || undefined
}
},
watch: {
currentStation: {
handler(val) {
if (val && val.id) {
const self = this
if (self.interval) {
clearInterval(self.interval)
self.interval = null
}
this.stationId = val.id
this.stationType = val.type
this.$nextTick(() => {
this.handleSite()
this.clientWidth = document.body.clientWidth
this.centerStatus = this.clientWidth >= 1200 ? 1 : 0
this.getCenterData()
// this.getRangeTime()
})
this.init(this.stationId, this.permissionId)
}
},
deep: true,
immediate: true
},
language: {
handler(val) {
this.$refs.bottomLeft.getData()
this.$refs.bottomRight.getData()
},
deep: true
},
permissionId: {
handler(val) {
if (val) {
this.init(this.stationId, val)
}
},
deep: true
}
},
created() {
const self = this
const timer = setInterval(() => {
if (self.$store.getters.menuList.length) {
self.getPermissionId()
clearInterval(timer)
}
}, 500)
window.addEventListener('resize', this.handleResize)
const getWindowInfo = () => {
if (window.innerHeight > 1000) {
this.bottomHeight = window.innerHeight - 620 + 'px'
} else {
this.bottomHeight = 230 + 'px'
}
}
getWindowInfo()
const debounce = (fn, delay) => {
let timer
return function() {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn()
}, delay)
}
}
this.cancalDebounce = debounce(getWindowInfo, 100)
window.addEventListener('resize', this.cancalDebounce)
},
beforeDestroy() {
clearInterval(this.interval)
this.interval = null
window.removeEventListener('resize', this.handleResize)
window.removeEventListener('resize', this.cancalDebounce)
},
mounted() {
},
methods: {
getPermissionId() {
this.permissionId = this.$store.getters.menuList.find(item => {
return item.url === this.$route.path
})?.id
},
init(stationId, permissionId) {
if (stationId && permissionId) {
setTimeout(() => {
this.getAllData()
}, 100)
if (this.stationId === 1069) {
self.interval = setInterval(() => {
this.getAllData()
this.getCenterData()
}, 10000)
} else {
self.interval = setInterval(() => {
this.getAllData()
this.getCenterData()
}, 1000 * 60 * 2)
}
}
},
screenFull(val) {
this.$nextTick(() => {
setTimeout(() => {
if (!val) {
this.bottomHeight = window.innerHeight - 620 + 'px'
} else {
this.bottomHeight = window.innerHeight - 680 + 'px'
}
}, 200)
})
},
getDynamicData() {
this.$refs.bottomRight.getData()
this.$refs.centerRight.getData()
},
getDynamicPointData() {
this.$refs.bottomLeft.getData()
this.$refs.topLeft.getData()
this.$refs.centerRight.getData()
},
handleSetting(val) {
this.chartLocation = val
this.show_dispostion = true
},
handlePointSetting(val) {
this.pageLocation = val
this.show_point_dispostion = true
},
copy(params) {
this.show_dispostion = true
},
close() {
this.show_dispostion = false
},
closePoint() {
this.show_point_dispostion = false
},
// 普通dom右键
onContextmenu(e) {
this.rightclickInfo = {
position: {
x: e.clientX,
y: e.clientY
},
menulists: [
{
fnName: 'copy',
params: '配置',
icoName: 'el-icon-setting',
btnName: '配置'
}
]
}
},
handleResize() {
this.clientWidth = document.body.clientWidth
this.getCenterData()
},
getCenterData() {
if (this.clientWidth < 1200) {
if (!this.centerStatus) {
this.centerStatus = 1
setTimeout(() => {
this.$refs.topCenter1?.getData()
}, 800)
}
} else if (this.clientWidth >= 1200) {
if (this.centerStatus) {
this.centerStatus = 0
setTimeout(() => {
this.$refs.topCenter?.getData()
}, 800)
}
}
},
async handleSite() {
const res = await getDashboard(this.stationId)
this.topLeftComponent = res.data[0]?.topLeft
? res.data[0].topLeft
: 'standard215TopLeft'
this.bottomLeftComponent = res.data[0]?.leftBottom
? res.data[0].leftBottom
: 'leftBottom_215'
this.topCenterComponent = 'pvCurve'
this.centerRightComponent = res.data[0]?.rightCenter
? res.data[0].rightCenter
: 'rightCenter_215'
this.bottomRightComponent = res.data[0]?.bottom
? res.data[0].bottom
: 'bottom_215'
this.topRightComponent = res.data[0]?.rightTop
? res.data[0].rightTop
: 'topRight'
},
updateTime() {
this.getRangeTime()
},
getRangeTime() {
const param = {
stationId: this.stationId
}
queryElecMeterConfig(param).then((res) => {
if (res.data.beginTime) {
this.timeForm = {
beforeTime: res.data.beginTime,
nowDayTime: res.data.endTime,
id: res.data.id,
stationId: res.data.stationId,
timeOperate: 2
}
} else {
this.timeForm = {
beforeTime: '22:00:00',
nowDayTime: '21:59:59',
timeOperate: 1
}
}
})
},
getAllData() {
setTimeout(() => {
this.getPermissionId()
this.$refs.topLeft?.getData()
this.$refs.bottomLeft?.getData()
this.$refs.bottomRight?.getData()
this.$refs.topCenter?.getData()
this.$refs.centerRight?.getData()
this.$refs.topRight?.getData()
}, 800)
}
}
}
</script>
<style lang="scss" scoped>
.home-container {
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: column;
.top-box{
height: 60%;
.el-col{
height: 100%;
}
}
.bottom-box{
height: 40%;
.el-col{
height: 100%;
}
}
.component-wrapper {
width: 100%;
height: calc(100% - 10px);
margin-bottom: 10px;
}
.tuopu-box {
width: 100%;
height: calc(100% - 10px);
margin-bottom: 10px;
}
.component-bottom {
width: 100%;
height: calc(100% - 10px);
// min-height: 235px;
margin-bottom: 10px;
}
}
::v-deep .icon-font{
cursor: auto!important;
}
</style>

View File

@ -530,12 +530,10 @@
<text x="730" y="460" fill="#FFB800" font-size="14">{{ pcsData_9.length > 1 ? pcsData_9[1].value : '' }}</text>
<text x="730" y="480" fill="#FFB800" font-size="14">{{ pcsData_9.length > 2 ? pcsData_9[2].value : '' }}</text>
<text x="730" y="500" fill="#FFB800" font-size="14">{{ pcsData_9.length > 3 ? pcsData_9[3].value : '' }}</text>
</svg>
</div>
</div>
<DispositionPointData :disposition-show="show_point_dispostion" :page-location="pageLocation" @close="closePoint" @getData="getDynamicPointData" />
</ItemBox>
</template>

View File

@ -54,30 +54,6 @@
stroke="#0094FF"
stroke-dasharray="3 3"
/>
<!-- <rect
x="154"
y="78"
width="231"
height="4"
:style="{ fill: 'rgba(100,100,100,0)', stroke: 'transparent',cursor: 'pointer' }"
@click="lookDeviceDetail('line-top-left')"
/> -->
<!-- <polyline
points="385,80 600,80"
fill="none"
class="g-rect-path"
stroke="#0094FF"
stroke-dasharray="3 3"
/> -->
<!-- <rect
x="385"
y="78"
width="215"
height="4"
:style="{ fill: 'rgba(100,100,100,0)', stroke: 'transparent',cursor: 'pointer' }"
@click="lookDeviceDetail('line-top-right')"
/> -->
<!-- center -->
<polyline
points="385,50 385,417"
@ -414,15 +390,12 @@
popper-class="svg-tooltip"
>
<span style="font-size: 14px;font-family: sans-serif;color: #ffffff;max-width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis">
{{ truncateText(item.name, 100) }}
{{ truncateText(item.name, 90) }}
</span>
</el-tooltip>
</div>
</foreignObject>
<!-- <text x="200" :y="450 + ( 20 * index)" fill="#ffffff" font-size="14">
{{ item.name }}
</text> -->
<text :x="calculateValueX(item.name, 185)" :y="450 + ( 20 * index)" fill="#FFB800" font-size="14">
<text :x="calculateValueX(item.name, 205)" :y="450 + ( 20 * index)" fill="#FFB800" font-size="14">
{{ item.value }}
</text>
</g>
@ -445,7 +418,7 @@
popper-class="svg-tooltip"
>
<span style="font-size: 14px;font-family: sans-serif;color: #ffffff;max-width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis">
{{ truncateText(item.name, 100) }}
{{ truncateText(item.name, 90) }}
</span>
</el-tooltip>
</div>
@ -453,7 +426,7 @@
<!-- <text x="410" :y="450 + ( 20 * index)" fill="#ffffff" font-size="14">
{{ item.name }}
</text> -->
<text :x="calculateValueX(item.name, 395)" :y="450 + ( 20 * index)" fill="#FFB800" font-size="14">
<text :x="calculateValueX(item.name, 410)" :y="450 + ( 20 * index)" fill="#FFB800" font-size="14">
{{ item.value }}
</text>
</g>
@ -570,20 +543,35 @@ export default {
},
mounted() {},
methods: {
truncateText(text, maxWidth) {
if (!text) return ''
const width = this.measureTextWidth(text)
if (width <= maxWidth) return text
let truncated = text
while (this.measureTextWidth(truncated + '...') > maxWidth && truncated.length > 0) {
truncated = truncated.slice(0, -1)
}
return truncated + (truncated.length < text.length ? '...' : '')
isChineseDominant(text) {
const chineseRegex = /[\u4e00-\u9fa5]/g
const chineseCount = (text.match(chineseRegex) || []).length
return chineseCount / text.length > 0.5
},
// 精确测量文本渲染宽度(像素)
// 修改截断逻辑:按字符类型统一视觉宽度
truncateText(text, baseMaxWidth = 90) {
if (!text) return ''
// 1. 按文本类型设置“最大可显示字符数”(视觉宽度相近)
const isChinese = this.isChineseDominant(text)
const maxChars = isChinese ? 8 : 15 // 中文8字 ≈ 英文15字母14px sans-serif下
// 2. 先按字符数截断(避免过长文本循环过多)
const truncated = text.slice(0, maxChars)
let finalText = truncated
// 3. 精确测量:如果截断后加...仍超宽,继续缩减
while (this.measureTextWidth(finalText + '...') > baseMaxWidth && finalText.length > 0) {
finalText = finalText.slice(0, -1)
}
// 4. 补充...(如果有截断)
return finalText + (finalText.length < text.length ? '...' : '')
},
// 精确测量文本渲染宽度(像素)- 保持不变
measureTextWidth(text, font = '14px sans-serif') {
// 创建或复用一个 canvas避免重复创建
if (!this._textMeasurementCanvas) {
this._textMeasurementCanvas = document.createElement('canvas')
}
@ -592,11 +580,12 @@ export default {
return ctx.measureText(text).width
},
// 计算 value 的 x 坐标baseX + name 的实际宽度 + 一点间距
calculateValueX(name, baseX = 200) {
const truncatedName = this.truncateText(name, 120) // 限制 name 最大宽度
const width = this.measureTextWidth(truncatedName)
return baseX + width + 6 // +6 是 name 和 value 之间的小空隙
// 计算value的x坐标基于统一后的截断文本宽度
calculateValueX(name, baseX = 205) {
const truncatedName = this.truncateText(name) // 无需传maxWidth内部统一处理
const nameWidth = this.measureTextWidth(truncatedName)
const spacing = 6 // 固定间距(因截断文本视觉宽度一致,间距会统一)
return baseX + nameWidth + spacing
},
countChineseAndEnglishCharacters(str, x) {
var chineseCount = str.match(/[\u4e00-\u9fa5]/g) ? str.match(/[\u4e00-\u9fa5]/g).length : 0

View File

@ -0,0 +1,265 @@
<template>
<div
class="table-wrapper"
:style="{
width: 'calc(' + widths.reduce((i, j) => `${i} + ${j}`) + ` + ${(widths.length - 1) * 6}px)`,
height: `calc(${contentHeight}px + 48px)`
}"
>
<div v-if="showHeader" class="table-header">
<span v-show="isSort" :style="{ width: '60px' }">序号</span>
<span v-for="(it, i) in titles" :key="i" :style="{ width: widths[i] === 'auto' ? 'auto' : widths[i] + 'px' }" :class="{'span-auto': widths[i] === 'auto'}">{{ titles[i] }}</span>
</div>
<div
class="table-content"
:style="{height:contentHeight+'px'}"
>
<swiper v-if="data.length > 0" ref="myBotSwiper" class="swiper" :options="swiperOption">
<swiper-slide v-for="(row, rowIndex) in data" :key="rowIndex">
<div
class="table-row"
:class="{ stripe: rowIndex % 2 === 1}"
:style="{ height: tabelHeight }"
@click="handleRowClick(row, rowIndex)"
>
<span
v-show="isSort"
:style="{ width: '60px', height: tabelHeight }"
>{{ rowIndex+1 }}</span>
<span
v-for="(column, columnIndex) in columns"
:key="columnIndex"
:title="row[columnIndex]"
:style="{ width: widths[columnIndex] === 'auto' ? 'auto' : widths[columnIndex] + 'px', height: tabelHeight }"
:class="{'span-auto': widths[columnIndex] === 'auto','color-blue':column === 'efficiencyValue'}"
>{{ row[column] }}</span>
</div>
</swiper-slide>
</swiper>
<div v-if="data.length === 0" style="height:100%;text-align:center; margin-top:40px;font-size:14px;color:#999">
暂无数据
</div>
</div>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
name: 'SwiperTable',
components: {
swiper,
swiperSlide
},
props: {
// 传入的表格数据
data: {
type: Array,
default: () => []
},
// 传入的表格头
titles: {
type: Array,
default: () => ['事件标题', '创建时间', '信息来源', '分类', '状态', '上报人']
},
// 表格的列宽
widths: {
type: Array,
default: () => ['246px', '348px', '224px', '214px', '214px', '214px']
},
// 表格的高度
contentHeight: {
type: Number,
default: 200
},
// 是否展示表格头
showHeader: {
type: Boolean,
default: true
},
isSort: {
type: Boolean,
default: true
},
// 表格的行高
tabelHeight: {
type: String,
default: '32px'
},
// 字段
columns: {
type: Array,
default: () => ['title', 'createTime', 'info', 'type', 'status', 'upper']
}
},
data() {
return {
currentActiveRow: 0,
swiperOption: {
autoHeight: true,
direction: 'vertical',
spaceBetween: 0,
autoplay: {
delay: 2500,
disableOnInteraction: false,
autoplayDisableOnInteraction: false
},
slidesPerView: 'auto',
grabCursor: true,
autoplayDisableOnInteraction: false,
mousewheelControl: true
}
}
},
computed: {
myBotSwiper() {
return this.$refs.myBotSwiper.$swiper
}
},
watch: {
data: {
handler(val) {
this.currentActiveRow = 0
// if (val && val.length > 0) {
// this.columns = []
// for (const key in val[0]) {
// this.columns.push(key)
// }
// // this.swiperStart()
// }
},
deep: true,
immediate: true
}
},
methods: {
handleRowClick(it, i) {
this.currentActiveRow = i
const currentIt = this.data[i]
this.$emit('change', currentIt)
},
// 控制表格停止滚动
swiperStop() {
this.myBotSwiper.autoplay.stop()
},
// 控制表格开始滚动
swiperStart() {
this.myBotSwiper.autoplay.start()
}
}
}
</script>
<style lang="scss" scoped>
.table-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
span {
box-sizing: border-box;
}
.table-header {
$height: 32px;
width: 100%;
height: $height;
display: flex;
align-items: center;
background: transparent;
span {
display: flex;
align-items: center;
justify-content: center;
color: #9AD1FD;
font-size: 14px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;
font-weight: 400;
line-height: $height;
&:nth-child(2) {
text-align: center;
}
}
.span-auto {
flex: 1;
}
span + span {
margin-left: 2px;
}
}
.table-content {
width: 100%;
margin-top: 2px;
.swiper {
width: 100%;
height: 100%;
.table-row {
width: 100%;
height: 36px;
cursor: pointer;
display: flex;
align-items: center;
&.active {
border: 2px solid rgba(0, 148, 255, 0.00);
background: linear-gradient(90deg, rgba(0, 51, 81, 0.00) 0%, #003351 51.3%, rgba(0, 51, 81, 0.00) 100%);
span {
color: #32AAFF;
font-size: 14px;
font-weight: bold;
}
}
&.stripe span {
background:#00131B;
}
span {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #E6F4FE;
line-height: 36px;
}
.span-auto {
flex: 1;
}
// span + span {
// margin-left: 2px;
// }
}
}
}
span {
height: 100%;
line-height: 100%;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 6px;
&:nth-child(1) {
width: 246px;
}
&:nth-child(2) {
width: 348px;
padding-left: 13px;
}
&:nth-child(3) {
text-align: center;
width: 214px;
}
&:nth-child(4) {
text-align: center;
width: 214px;
}
&:nth-child(5) {
width: 214px;
}
}
}
.color-blue{
color: #0089FB !important;
}
</style>

View File

@ -0,0 +1,292 @@
<template>
<div
class="table-wrapper"
:style="{
width: 'calc(' + widths.reduce((i, j) => `${i} + ${j}`) + ` + ${(widths.length - 1) * 6}px)`,
}"
>
<div v-if="showHeader" class="table-header">
<span v-show="isSort" :style="{ width: '60px' }">序号</span>
<span v-for="(it, i) in titles" :key="i" :style="{ width: widths[i] === 'auto' ? 'auto' : widths[i] + 'px' }" :class="{'span-auto': widths[i] === 'auto'}">{{ titles[i] }}</span>
</div>
<div
class="table-content"
:style="{height:contentHeight+'px'}"
>
<swiper v-if="data.length > 0" ref="myBotSwiper" class="swiper" :options="swiperOption">
<swiper-slide v-for="(row, rowIndex) in data" :key="rowIndex">
<div
class="table-row "
:style="{ height: tabelHeight,'border-color':alarmList.find(i=>i.level === row.eventLevel)?alarmList.find(i=>i.level === row.eventLevel).color :'#fff'}"
@click="handleRowClick(row, rowIndex)"
>
<div class="tip-box" :style="{color:alarmList.find(i=>i.level === row.eventLevel) ? alarmList.find(i=>i.level === row.eventLevel).color :'#fff'}">{{ alarmList.find(i=>i.level === row.eventLevel)? alarmList.find(i=>i.level === row.eventLevel).label :'' }}</div>
<div
v-for="(column, columnIndex) in columns"
:key="columnIndex"
:title="row[columnIndex]"
:style="{ width: widths[columnIndex] === 'auto' ? 'auto' : widths[columnIndex] + 'px', height: tabelHeight }"
:class="{'span-auto': widths[columnIndex] === 'auto'}"
>{{ column === 'timeStamp' ?parseTime( row[column]) : row[column] }}</div>
</div>
</swiper-slide>
</swiper>
<div v-if="data.length === 0" style="height:100%;text-align:center; margin-top:40px;font-size:14px;color:#999">
暂无数据
</div>
</div>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.css'
import { parseTime } from '@/utils/index'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
name: 'SwiperTable',
components: {
swiper,
swiperSlide
},
props: {
// 传入的表格数据
data: {
type: Array,
default: () => []
},
// 传入的表格头
titles: {
type: Array,
default: () => ['事件标题', '创建时间', '信息来源', '分类', '状态', '上报人']
},
// 表格的列宽
widths: {
type: Array,
default: () => ['246px', '348px', '224px', '214px', '214px', '214px']
},
// 表格的高度
contentHeight: {
type: Number,
default: 200
},
// 是否展示表格头
showHeader: {
type: Boolean,
default: true
},
isSort: {
type: Boolean,
default: true
},
// 表格的行高
tabelHeight: {
type: String,
default: '40px'
},
// 字段
columns: {
type: Array,
default: () => ['title', 'createTime', 'info', 'type', 'status', 'upper']
}
},
data() {
return {
alarmList: [{
level: 1,
label: '事故',
color: '#CF0000'
},
{
level: 2,
label: '故障',
color: '#FDA934'
},
{
level: 3,
label: '告警',
color: '#0089FB'
},
{
level: 4,
label: '提示',
color: '#0089FB'
},
{
level: 5,
label: '告知',
color: '#0089FB'
}
],
currentActiveRow: 0,
swiperOption: {
autoHeight: true,
direction: 'vertical',
spaceBetween: 0,
autoplay: {
delay: 2500,
disableOnInteraction: false,
autoplayDisableOnInteraction: false
},
slidesPerView: 'auto',
grabCursor: true,
autoplayDisableOnInteraction: false,
mousewheelControl: true
}
}
},
computed: {
myBotSwiper() {
return this.$refs.myBotSwiper.$swiper
}
},
watch: {
data: {
handler(val) {
this.currentActiveRow = 0
// if (val && val.length > 0) {
// this.columns = []
// for (const key in val[0]) {
// this.columns.push(key)
// }
// // this.swiperStart()
// }
},
deep: true,
immediate: true
}
},
methods: {
parseTime,
handleRowClick(it, i) {
this.currentActiveRow = i
const currentIt = this.data[i]
this.$emit('change', currentIt)
},
// 控制表格停止滚动
swiperStop() {
this.myBotSwiper.autoplay.stop()
},
// 控制表格开始滚动
swiperStart() {
this.myBotSwiper.autoplay.start()
}
}
}
</script>
<style lang="scss" scoped>
.table-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
span {
box-sizing: border-box;
}
.table-header {
$height: 32px;
width: 100%;
height: $height;
display: flex;
align-items: center;
background: transparent;
span {
display: flex;
align-items: center;
justify-content: center;
color: #9AD1FD;
font-size: 14px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;
font-weight: 400;
line-height: $height;
&:nth-child(2) {
text-align: center;
}
}
.span-auto {
flex: 1;
}
span + span {
margin-left: 2px;
}
}
.table-content {
width: 100%;
margin-top: 2px;
.swiper {
width: 100%;
height: 100%;
.table-row {
width: calc(100% - 2px);
height: 40px;
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 20px;
padding: 0 10px;
border-width: 1px;
border-style: solid;
box-sizing: border-box;
div {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #E6F4FE;
line-height: 36px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.span-auto {
flex: 1;
}
div + div {
margin-left: 2px;
}
}
}
}
span {
height: 100%;
line-height: 100%;
display: inline-block;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 6px;
&:nth-child(1) {
width: 246px;
}
&:nth-child(2) {
width: 348px;
padding-left: 13px;
}
&:nth-child(3) {
width: 214px;
}
&:nth-child(4) {
width: 214px;
}
&:nth-child(5) {
width: 214px;
}
}
}
.tip-box{
font-size: 12px !important;
font-style: normal;
font-weight: 400;
line-height: 16px !important; /* 133.333% */
letter-spacing: 0.6px;
writing-mode: tb-rl !important;
margin-right: 10px;
}
</style>

View File

@ -0,0 +1,296 @@
<!-- eslint-disable vue/no-parsing-error -->
<template>
<div
class="table-wrapper"
:style="{
width: 'calc(' + widths.reduce((i, j) => `${i} + ${j}`) + ` + ${(widths.length - 1) * 6}px)`,
}"
>
<div v-if="showHeader" class="table-header">
<span v-show="isSort" :style="{ width: '60px' }">序号</span>
<span v-for="(it, i) in titles" :key="i" :style="{ width: widths[i] === 'auto' ? 'auto' : widths[i] + 'px' }" :class="{'span-auto': widths[i] === 'auto'}">{{ titles[i] }}</span>
</div>
<div
class="table-content"
:style="{height:contentHeight+'px'}"
>
<swiper v-if="data.length > 0" ref="myBotSwiper" class="swiper" :options="swiperOption">
<swiper-slide v-for="(row, rowIndex) in data" :key="rowIndex">
<div
class="table-row"
:style="{ height: tabelHeight }"
@click="handleRowClick(row, rowIndex)"
>
<img :src="screenRightCenterBg" class="row-bg">
<div
v-show="isSort"
class="sort-box"
:style="{ width: '60px', height: tabelHeight,'margin-left':'20px' }"
>
<img v-if="rowIndex === 0" :src="one">
<img v-else-if="rowIndex === 1" :src="two">
<img v-else-if="rowIndex === 2" :src="three">
<div v-else class="sort-box" :style="{ width: '100%', height: tabelHeight }"> {{ rowIndex < 9 ?'0' :'' }}{{ rowIndex +1 }}</div>
</div>
<span
v-for="(column, columnIndex) in columns"
:key="columnIndex"
:title="row[columnIndex]"
:style="{ width: widths[columnIndex] === 'auto' ? 'auto' : widths[columnIndex] + 'px' }"
:class="{'span-auto': widths[columnIndex] === 'auto'}"
>{{ row[column] }}</span>
</div>
</swiper-slide>
</swiper>
<div v-if="data.length === 0" style="height:100%;text-align:center; margin-top:40px;font-size:14px;color:#999">
暂无数据
</div>
</div>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.css'
import one from '@/assets/new-screen/one.png'
import two from '@/assets/new-screen/two.png'
import three from '@/assets/new-screen/three.png'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import screenRightCenterBg from '@/assets/new-screen/screenRightCenterBg.png'
export default {
name: 'SwiperTable',
components: {
swiper,
swiperSlide
},
props: {
// 传入的表格数据
data: {
type: Array,
default: () => []
},
// 传入的表格头
titles: {
type: Array,
default: () => ['事件标题', '创建时间', '信息来源', '分类', '状态', '上报人']
},
// 表格的列宽
widths: {
type: Array,
default: () => ['246px', '348px', '224px', '214px', '214px', '214px']
},
// 表格的高度
contentHeight: {
type: Number,
default: 200
},
// 是否展示表格头
showHeader: {
type: Boolean,
default: true
},
isSort: {
type: Boolean,
default: true
},
// 表格的行高
tabelHeight: {
type: String,
default: '32px'
},
// 字段
columns: {
type: Array,
default: () => ['title', 'createTime', 'info', 'type', 'status', 'upper']
}
},
data() {
return {
one,
two,
three,
screenRightCenterBg,
currentActiveRow: 0,
swiperOption: {
autoHeight: true,
direction: 'vertical',
spaceBetween: 0,
autoplay: {
delay: 2500,
disableOnInteraction: false,
autoplayDisableOnInteraction: false
},
slidesPerView: 'auto',
grabCursor: true,
autoplayDisableOnInteraction: false,
mousewheelControl: true
}
}
},
computed: {
myBotSwiper() {
return this.$refs.myBotSwiper.$swiper
}
},
watch: {
data: {
handler(val) {
this.currentActiveRow = 0
// if (val && val.length > 0) {
// this.columns = []
// for (const key in val[0]) {
// this.columns.push(key)
// }
// // this.swiperStart()
// }
},
deep: true,
immediate: true
}
},
methods: {
handleRowClick(it, i) {
this.currentActiveRow = i
const currentIt = this.data[i]
this.$emit('change', currentIt)
},
// 控制表格停止滚动
swiperStop() {
this.myBotSwiper.autoplay.stop()
},
// 控制表格开始滚动
swiperStart() {
this.myBotSwiper.autoplay.start()
}
}
}
</script>
<style lang="scss" scoped>
.table-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
span {
box-sizing: border-box;
}
.table-header {
$height: 32px;
width: 100%;
height: $height;
display: flex;
align-items: center;
background: transparent;
span {
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 14px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;
font-weight: 400;
line-height: $height;
&:nth-child(2) {
// text-align: center;
}
}
.span-auto {
flex: 1;
}
span + span {
margin-left: 2px;
}
}
.table-content {
width: 100%;
margin-top: 2px;
.swiper {
width: 100%;
height: 100%;
.table-row {
width: 100%;
height: 36px;
cursor: pointer;
display: flex;
align-items: center;
position: relative;
.row-bg{
position: absolute;
bottom: 0;
width: 386px;
height: auto;
}
&.active {
border: 2px solid rgba(0, 148, 255, 0.00);
background: linear-gradient(90deg, rgba(0, 51, 81, 0.00) 0%, #003351 51.3%, rgba(0, 51, 81, 0.00) 100%);
span {
color: #32AAFF;
font-size: 14px;
font-weight: bold;
}
}
&.stripe span {
background: rgba(2, 74, 123, 0.2);
}
span {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #E6F4FE;
line-height: 36px;
}
.span-auto {
flex: 1;
}
// span + span {
// margin-left: 2px;
// }
}
}
}
span {
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
margin-bottom: 6px;
&:nth-child(1) {
width: 246px;
}
&:nth-child(2) {
width: 348px;
padding-left: 13px;
text-align: center;
}
&:nth-child(3) {
text-align: center;
width: 214px;
}
&:nth-child(4) {
width: 214px;
}
&:nth-child(5) {
width: 214px;
}
}
}
.sort-box{
color: #FAD733 !important;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
text-shadow: 0px 0px 1px #FAD733 !important;
font-size: 16px !important;
font-weight: 400;
line-height: 36px;
}
</style>

View File

@ -0,0 +1,192 @@
<template>
<div class="center-bottom-warp">
<item :title="$t('screen.dailyPowerGeneration')">
<dv-loading v-if="Loading">Loading...</dv-loading>
<Chart v-else :options="options" />
</item>
</div>
</template>
<script>
import item from './item-warp.vue'
import { GetChargeDailyChart } from '@/api/screen/zzScreen'
import dayChargeIcon from '../../../assets/new-screen/dayCharge-icon.png'
// import dayDischargeIcon from '../../../assets/new-screen/dayDischarge-icon.png'
export default {
components: { item },
data() {
return {
options: {},
Loading: true
}
},
computed: {
lang() {
return this.$store.getters.language
}
},
created() {},
mounted() {},
methods: {
async getData(deptId) {
try {
this.Loading = true
const { data } = await GetChargeDailyChart({
deptId: deptId
})
this.initChart(data)
} catch (error) {
console.log(error)
} finally {
this.Loading = false
}
// data.splice(0, 3)
},
initChart(data) {
if (data.length) {
const x_data = []
const dayCharge = []
data.forEach((el) => {
x_data.push(el.time)
dayCharge.push(el.dayCharge)
})
this.options = {
tooltip: {
trigger: 'axis'
},
legend: {
orient: 'horizontal',
right: 30,
itemWidth: 14,
data: [
{
icon: 'image://' + dayChargeIcon,
name: this.$t('screen.powerGeneration')
}
]
},
grid: {
left: '5%',
top: '13%',
right: '5%',
bottom: '10%'
},
xAxis: [
{
data: x_data,
axisLabel: {
textStyle: {
color: '#6E7174'
},
margin: 10 // 刻度标签与轴线之间的距离。
},
axisLine: {
show: false // 不显示x轴
},
axisTick: {
show: false // 不显示刻度
},
boundaryGap: true,
splitLine: {
show: false,
width: 0.08,
lineStyle: {
type: 'solid',
color: '#03202E'
}
}
}
],
yAxis: [
{
name: 'kWh',
nameTextStyle: {
color: '#6E7174',
fontSize: 12,
padding: [0, 0, 0, -40]
},
splitLine: {
show: false,
lineStyle: {
color: '#eee',
type: 'solid'
}
},
axisTick: {
show: false
},
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#6E7174'
}
}
}
],
series: [
// 柱体
{
name: this.$t('screen.powerGeneration'),
type: 'bar',
barGap: '20%',
barCategoryGap: '50%' /* 多个并排柱子设置柱子之间的间距*/,
itemStyle: {
normal: {
color: {
x: 0,
y: 0,
x2: 0,
y2: 1,
type: 'linear',
global: false,
colorStops: [
{
// 第一节下面
offset: 0,
color: 'rgba(0, 243, 253, 1)'
},
{
offset: 1,
color: 'rgba(0, 243, 253, 0.2)'
}
]
}
}
},
data: dayCharge
}
]
}
} else {
this.options = {
title: {
text: this.$t('screen.noData'),
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
color: 'rgb(153, 153, 153)',
fontWeight: 'normal'
}
}
}
}
}
}
}
</script>
<style lang="scss" scoped>
.center-bottom-warp {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,119 @@
<template>
<div class="center-top-warp">
<div class="income">
<div class="title">
<span class="square" />
<span>{{ $t('screen.todayEarning') }}</span>
</div>
<div class="value">
<template v-for="(item,index) in info.yestProfit">
<div :key="index" class="number">{{ item }}</div>
</template>
<div class="unit">{{ $t('screen.mRMB') }}</div>
</div>
</div>
<div class="income">
<div class="title">
<span class="square" />
<span>{{ $t('screen.annualEarning') }}</span>
</div>
<div class="value">
<template v-for="(item,index) in info.yearProfit">
<div :key="index" class="number">{{ item }}</div>
</template>
<div class="unit">{{ $t('screen.mRMB') }}</div>
</div>
</div>
<div class="income">
<div class="title">
<span class="square" />
<span>{{ $t('screen.totalEarning') }}</span>
</div>
<div class="value">
<template v-for="(item,index) in info.totalProfit">
<div :key="index" class="number">{{ item }}</div>
</template>
<div class="unit">{{ $t('screen.mRMB') }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
info: {
type: Object,
default: () => null
}
},
data() {
return {
number: '1234.5'
}
},
created() {
},
mounted() {
this.getData()
},
methods: {
getData() {}
}
}
</script>
<style lang="scss" scoped>
.center-top-warp {
width: 100%;
height: 100%;
display: flex;
justify-content: space-around;
.income {
height: 100%;
padding-top: 15px;
z-index: 99999;
.title {
display: flex;
align-items: center;
.square {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(0, 137, 251, 1);
margin-right: 10px;
}
}
.value {
display: flex;
align-items: center;
.number {
width: 36px;
height: 44px;
line-height: 44px;
background: url(../../../assets/new-screen/num-bg.png);
color: #fff;
font-family: LCD;
font-size: 32px;
text-align: center;
font-style: normal;
font-weight: 700;
margin-right: 5px;
margin-top: 10px;
}
.unit{
margin-top: 10px;
height: 44px;
line-height: 77px;
}
}
}
}
</style>

View File

@ -0,0 +1,51 @@
<template>
<div class="item-warp">
<div class="item-bg">
<div class="item-title">{{ title }}</div>
</div>
<div class="item-content">
<slot />
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '集团数据'
}
}
}
</script>
<style lang="scss" scoped>
.item-warp {
width: 100%;
height: 100%;
.item-bg {
width: 385px;
height: 40px;
background: url(../../../assets/new-screen/item-bg.png) no-repeat;
background-size: 100% 75%;
background-position:0 10px;
padding-left: 12px;
.item-title{
font-size: 16px;
color: #CCE8FE;
}
}
.item-content {
width: 100%;
height: calc(100% - 41px);
display: flex;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,136 @@
<template>
<div class="left-bottom-warp">
<item :title="$t('screen.energySaving')">
<dv-loading v-if="loading">Loading...</dv-loading>
<div v-else class="top-all-box">
<div class="top-con-box">
<div class="top-item-box">
<div class="data">{{ info.treePlanting }}<span class="unit">{{ $t('screen.tree') }}</span></div>
<div class="title">{{ $t('screen.planted') }}</div>
<img :src="screenItemBg" class="item-bg-img">
</div>
<div class="top-item-box">
<div class="data">{{ info.reductionCO2 | kgFormat }}<span class="unit">{{ info.reductionCO2 | KgUnitFormat }}</span></div>
<div class="title">{{ $t('screen.co2') }}</div>
<img :src="screenItemBg" class="item-bg-img">
</div>
</div>
<div class="top-con-box">
<div class="top-item-box">
<div class="data">{{ info.equivalentCoal | kgFormat }}<span class="unit">{{ info.equivalentCoal | KgUnitFormat }}</span></div>
<div class="title">{{ $t('screen.coal') }}</div>
<img :src="screenItemBg" class="item-bg-img">
</div>
<div class="top-item-box">
<div class="data">{{ info.income | moneyFormat }}<span class="unit">{{ info.income | moneyUnitFormat }}</span></div>
<div class="title">{{ $t('screen.income') }}</div>
<img :src="screenItemBg" class="item-bg-img">
</div>
</div>
</div>
</item>
</div>
</template>
<script>
import item from './item-warp.vue'
import screenItemBg from '@/assets/new-screen/screen-item-bg.png'
import { GetEnergySaving } from '@/api/screen/zzScreen'
export default {
components: { item },
data() {
return {
screenItemBg,
loading: true,
info: {
treePlanting: null,
reductionCO2: null,
equivalentCoal: null,
income: null
}
}
},
created() {
},
mounted() {
},
methods: {
async getData(deptId) {
try {
this.loading = true
const { data } = await GetEnergySaving({
deptId: deptId
})
this.info = data
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.left-bottom-warp {
width: 100%;
height: 100%;
.top-all-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 100%;
height: 100%;
padding: 10px 0;
.top-con-box {
display: flex;
flex-direction: row;
justify-content: space-around;
width: 100%;
.top-item-box {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
min-height: 110px;
.data {
color: #F5FAFF;
font-family: DIN;
font-size: 24px;
font-weight: 500;
line-height: normal;
}
.unit {
color: #F5FAFF;
font-family: DIN;
font-size: 12px;
line-height: normal;
}
.title {
color: #B7C1C9;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.item-bg-img {
width: 90px;
height: auto;
position: absolute;
top: 38px;
}
}
}
}
}</style>

View File

@ -0,0 +1,202 @@
<template>
<div class="left-center-warp">
<item :title="$t('screen.regionStation')">
<dv-loading v-if="loading">Loading...</dv-loading>
<div v-else class="chart-box">
<Chart :options="options" :class-name="'chart'" />
</div></item>
</div>
</template>
<script>
import item from './item-warp.vue'
import { GetRegionalDistribution } from '@/api/screen/zzScreen'
export default {
components: { item },
data() {
return {
options: {},
loading: true
}
},
methods: {
async getData(deptId) {
try {
this.loading = true
const res = await GetRegionalDistribution({
deptId: deptId
})
this.initChart(res.data)
} catch (error) {
console.log(error)
} finally {
this.loading = false
}
},
initChart(val) {
if (val.length) {
val.sort((a, b) => b.regionValue - a.regionValue)
var ydata = []
var legend = []
val.forEach(i => {
const param = {
name: i.addName,
value: i.regionValue
}
legend.push(param.name)
ydata.push(param)
})
const count = this.arrCount(ydata)
this.options = {
title: [{
text: this.$t('screen.totalStation'),
subtext: count,
subtextStyle: {
color: '#fff',
fontSize: 37,
fontWeight: 500
},
left: '35%',
top: '40%',
padding: [0, 0],
textStyle: {
color: '#fff',
fontSize: 18
},
textAlign: 'center'
}],
tooltip: {},
legend: {
type: 'scroll',
orient: 'vertical',
right: 10,
top: 20,
bottom: 20,
padding: [0, 10],
data: legend,
icon: 'circle',
align: 'left',
itemWidth: 8,
itemHeight: 8,
itemGap: 15,
textStyle: {
color: '#fff',
rich: {
name: {
fontSize: 12,
color: '#ffffff',
padding: [0, 10, 0, 0]
},
num: {
fontSize: 16,
color: '#ffffff'
},
unit: {
fontSize: 16,
color: '#ffffff'
}
}
},
formatter: function(name) {
const val = ydata.filter(item => {
return item.name === name
})
const num = (((val[0].value / count)) * 100).toFixed(2)
return [
`{name|${name.length > 5 ? name.slice(0, 2) : name}}`,
`{num|${num}}{unit|%}`
].join(' ')
}
},
series: [{
name: '电站个数',
type: 'pie',
clockwise: false, // 饼图的扇区是否是顺时针排布
minAngle: 20, // 最小的扇区角度0 ~ 360
radius: ['60%', '75%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: { // 图形样式
normal: {
borderColor: '#070A0C',
borderWidth: 5
}
},
label: {
normal: {
show: false
},
emphasis: {
show: false,
position: 'outer',
alignTo: 'edge',
margin: 10,
formatter: '{text|{b}}\n{value|{d}%}',
rich: {
text: {
fontSize: 14,
align: 'center',
verticalAlign: 'middle',
padding: 5
},
value: {
fontSize: 24,
align: 'center',
verticalAlign: 'middle'
}
}
}
},
data: ydata
}]
}
} else {
this.options = {
title: {
text: '暂无数据',
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
color: 'rgb(153, 153, 153)',
fontWeight: 'normal'
}
}
}
}
},
arrCount(arr) {
let count = 0
arr.forEach(item => {
count = count + item.value
})
return count
}
}
}
</script>
<style lang="scss" scoped>
.left-center-warp {
width: 100%;
height: 100%;
.chart-box {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,180 @@
<template>
<div class="left-top-warp">
<item :title="$t('screen.groupData')">
<dv-loading v-if="loading">Loading...</dv-loading>
<div v-else class="top-all-box">
<div class="value-box">
<el-tooltip class="item" effect="dark" :content="$t('screen.capacity')" placement="top">
<div class="title">{{ $t('screen.capacity') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.capacity |kwhFormat" placement="top">
<div class="value">{{ info.capacity |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.capacity | kwhUnitFormat }}</div>
</div>
<div class="value-box">
<el-tooltip class="item" effect="dark" :content="$t('screen.stationNum')" placement="top">
<div class="title">{{ $t('screen.stationNum') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.stationNumber" placement="top">
<div class="value">{{ info.stationNumber }}</div>
</el-tooltip>
<div class="unit">{{ lang === 'zh'? '个':'' }}</div>
</div>
<div class="value-box" style="width:100%">
<el-tooltip class="item" effect="dark" :content="$t('screen.dailyPowerGeneration')" placement="top">
<div class="title" style="min-width:170px;max-width:170px;">{{ $t('screen.dailyPowerGeneration') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.dayCharge |kwhFormat" placement="top">
<div class="value" style="min-width:170px;max-width:170px;">{{ info.dayCharge |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.dayCharge |kwhUnitFormat }}</div>
</div>
<div class="value-box" style="width:100%">
<el-tooltip class="item" effect="dark" :content="$t('screen.yearlyPowerGeneration')" placement="top">
<div class="title" style="min-width:170px;max-width:170px;">{{ $t('screen.yearlyPowerGeneration') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.dayDischarge |kwhFormat" placement="top">
<div class="value" style="min-width:170px;max-width:170px;">{{ info.dayDischarge |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.dayDischarge |kwhUnitFormat }}</div>
</div>
<div class="value-box" style="width:100%">
<el-tooltip class="item" effect="dark" :content="$t('screen.cumulativePowerGeneration')" placement="top">
<div class="title" style="min-width:170px;max-width:170px;">{{ $t('screen.cumulativePowerGeneration') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.yearCharge |kwhFormat" placement="top">
<div class="value" style="min-width:170px;max-width:170px;">{{ info.yearCharge |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.yearCharge |kwhUnitFormat }}</div>
</div>
<!-- <div class="value-box">
<el-tooltip class="item" effect="dark" :content="$t('screen.yearDisCharge')" placement="top">
<div class="title">{{ $t('screen.yearDisCharge') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.yearDischarge |kwhFormat" placement="top">
<div class="value">{{ info.yearDischarge |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.yearDischarge |kwhUnitFormat }}</div>
</div>
<div class="value-box">
<el-tooltip class="item" effect="dark" :content="$t('screen.totalCharge')" placement="top">
<div class="title">{{ $t('screen.totalCharge') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.totalCharge |kwhFormat" placement="top">
<div class="value">{{ info.totalCharge |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.totalCharge |kwhUnitFormat }}</div>
</div>
<div class="value-box">
<el-tooltip class="item" effect="dark" :content="$t('screen.totalDisCharge')" placement="top">
<div class="title">{{ $t('screen.totalDisCharge') }}</div>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="info.totalDischarge |kwhFormat" placement="top">
<div class="value">{{ info.totalDischarge |kwhFormat }}</div>
</el-tooltip>
<div class="unit">{{ info.totalDischarge |kwhUnitFormat }}</div>
</div> -->
</div>
</item>
</div>
</template>
<script>
import item from './item-warp.vue'
export default {
components: { item },
props: {
info: {
type: Object,
default: () => null
}
},
data() {
return {
loading: true
}
},
computed: {
lang() {
return this.$store.getters.language
}
},
watch: {
info: {
handler(val) {
if (val) {
this.loading = false
} else {
this.loading = true
}
},
immediate: true,
deep: true
}
},
created() {
},
mounted() {
this.getData()
},
methods: {
getData() {
}
}
}
</script>
<style lang="scss" scoped>
.left-top-warp {
width: 100%;
height: 100%;
.top-all-box {
display: flex;
align-items: center;
justify-content: space-around;
flex-wrap: wrap;
width: 100%;
height: 100%;
padding: 10px 0;
.value-box{
width: 50%;
height: 32px;
background: url(../../../assets/new-screen/top-left-bg.png)no-repeat;
background-size: 120% 100%;
display: flex;
align-items: center;
.title{
color: rgba(179, 221, 253, 1);
overflow: hidden;
text-overflow: ellipsis;
min-width: 90px;
max-width: 90px;
padding-left: 16px;
white-space: nowrap;
}
.value{
color: rgba(255, 184, 0, 1);
padding-left: 10px;
min-width: 70px;
max-width: 70px;
text-align: right;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.unit{
color: rgba(183, 193, 201, 1);
padding-right: 20px;
padding-left: 5px;
font-size: 12px;
}
}
}
}
</style>

View File

@ -0,0 +1,394 @@
<template>
<div class="map-warp">
<dv-loading v-if="loading">Loading...</dv-loading>
<Chart v-else ref="chart" :options="options" @mapClick="mapClick" />
<div class="map-left-box">
<template v-for="(item,index) in tableData">
<div :key="index" class="box-value">
<span class="num">{{ index + 1 }}</span>
<span class="name">{{ item.addName }}</span>
<span class="value"><span style="min-width: 25px;display: inline-block;">{{ (item.stationCapacity / 1000).toFixed(2) }}</span>MWh</span>
</div>
</template>
</div>
</div>
</template>
<script>
// import chinaMap from '@/assets/mapJson/chinaMap.json'
import worldJson from 'echarts/map/json/world.json'
// import { chinaMapOutline } from '@/assets/mapJson/chinaMapOut.js'
import blue from '../../../assets/new-screen/map-blue.png'
import { GetStationInfo, GetCapacity, GetRegionalDistribution } from '@/api/screen/zzScreen'
export default {
data() {
return {
options: {},
pointData: [],
pointIndex: 0,
timer: null,
currentToolTip: {},
loading: true,
tableData: [],
mapData: [],
stationNum: []
}
},
destroyed() {
clearInterval(this.timer)
this.timer = null
},
methods: {
mapClick(params) {
if (params.seriesType === 'effectScatter' || params.name === '四川') {
const routeParams = {
url: '/dashboard-test/dashboard-test',
urlName: 'dashboard-test',
data: {
id: params.data.id
}
}
this.$store.dispatch('user/menuChange', routeParams)
}
},
async getStationNum(deptId) {
const { data } = await GetRegionalDistribution({
deptId: deptId
})
this.mapData = data
this.mapData.map((el) => {
el.name = el.addName
el.value = el.regionValue
this.stationNum.push(el.value)
})
},
async getLeftData(deptId) {
try {
const { data } = await GetCapacity({
deptId: deptId
})
this.tableData = data
} catch (error) {
console.log(error)
}
},
async getData(deptId) {
this.loading = true
try {
const { data } = await GetStationInfo({
deptId: deptId
})
console.log('GetStationInfo', data)
const province = []
if (data.length) {
data.forEach((el) => {
el.value = [el.longitude, el.latitude]
el.symbol = 'image://' + blue
el.cityCode = 1000
province.push(el)
})
this.pointData = province
} else {
this.pointData = [{ symbol: 'image://' + blue, cityCode: 1000, value: [30.787045, 103.923008], name: 'Sichuan' }]
}
} finally {
this.loading = false
}
this.getInitMap()
},
getInitMap() {
this.$echarts.registerMap('world', worldJson)
var series = [
{
name: '国家',
map: 'world',
type: 'map',
roam: false,
zoom: 1.1,
label: {
normal: {
show: false,
textStyle: {
color: '#fff'
}
},
emphasis: {
textStyle: {
color: '#fff'
}
}
},
top: '10%',
tooltip: {
show: false
},
itemStyle: {
normal: {
areaColor: 'transparent',
borderColor: 'rgba(21,138,246,100%)',
borderWidth: 1
},
emphasis: {
areaColor: '#0B63B0',
textStyle: {
color: 'red'
}
}
},
data: this.mapData
},
{
name: '散点',
type: 'effectScatter',
coordinateSystem: 'geo',
rippleEffect: {
color: 'purple', // 涟漪颜色,默认为散点自身颜色
brushType: 'fill', // 动画方式,全填充或只有线条,'stroke'
period: 4, // 动画周期
scale: '0.5' // 涟漪规模
},
itemStyle: {
normal: {
color: '#F4E925',
shadowColor: '#333'
}
},
symbolSize: 24,
symbolOffset: [0, '-50%'], // 或 [0, -12] 更精确
data: [this.pointData[this.pointIndex]],
showEffectOn: 'render' // 加载完毕显示特效
}
]
this.options = {
visualMap: {
min: 0,
max: Math.max(...this.stationNum),
right: 'right',
top: 'bottom',
text: [this.$t('screen.high') + '(' + this.$t('screen.stationNum') + ')', this.$t('screen.low') + '(' + this.$t('screen.stationNum') + ')'],
calculable: true,
inRange: {
// color: ['#5edfd6', '#37d4cf', '#14c9c9', '#0da5aa', '#07828b']
// color: ['#fcf26b', '#fbe842', '#fadc19', '#cfaf0f', '#a38408']
color: ['#6aa1ff', '#4080ff', '#165dff', '#0e42d2', '#072ca6', '#031a79']
// color: ['#8d4eda', '#722ed1', '#551db0', '#3c108f', '#27066e']
// color: ['#e13edb', '#d91ad9', '#b010b6', '#8a0993', '#650370']
// color: ['#23c343', '#00b42a', '#009a29', '#008026', '#006622']
},
textStyle: {
color: '#F5FAFF'
},
seriesIndex: [0]
},
tooltip: {
trigger: 'item',
alwaysShowContent: true,
backgroundColor: 'transparent',
position: 'bottom',
triggerOn: 'click',
enterable: true,
formatter: params => {
// 获取xAxis data中的数据
let dataStr = `<div></div>`
dataStr += `<div>
<span style="padding-left:20px;font-weight:bold;margin-left:20px;margin-top:5px;font-size="14px">${params.name}</span>
</div>`
dataStr += `<div style="margin-top:15px">
<span style="padding-left:10px;">${this.$t('screen.comTime')}</span>
<span style="margin-left:5px;margin-right:10px;color:rgba(245, 221, 0, 1);font-family:DIN;">${params.data.createTime}</span>
</div>`
dataStr += `<div style="margin-top:2px;">
<span style="padding-left:10px;">${this.$t('screen.capacity')}</span>
<span style="margin-left:5px;margin-right:5px;color:rgba(245, 221, 0, 1);font-family:DIN;">${params.data.capacity}</span>
<span style="color:rgba(245, 221, 0, 1);">kWh</span>
</div>`
const div = `<div style='height:85px;
background-image:url(${require('../../../assets/new-screen/tooltip-bg.png')});background-repeat: no-repeat;background-size:100% 100%;' >${dataStr}</div>`
return div
}
},
color: ['#34c6bb'],
geo: [
{
silent: true,
map: 'world',
zoom: 1.1,
label: {
normal: {
show: false,
textStyle: {
color: '#fff'
}
},
emphasis: {
textStyle: {
color: '#fff'
}
}
},
roam: false,
itemStyle: {
normal: {
areaColor: 'rgba(3,56,100,50%)',
borderColor: 'transparent',
borderWidth: 1.5,
shadowColor: 'transparent',
shadowOffsetX: 0,
shadowOffsetY: 4,
shadowBlur: 10
},
emphasis: {
areaColor: 'transparent', // 悬浮背景
textStyle: {
color: '#fff'
}
}
}
},
{
silent: true,
map: 'chinaMapOutline',
zoom: 1.2,
top: '7%',
label: {
normal: {
show: false,
textStyle: {
color: '#fff'
}
},
emphasis: {
textStyle: {
color: '#fff'
}
}
},
roam: false,
itemStyle: {
normal: {
areaColor: 'rgba(1,28,50,100%)',
borderColor: '#0089FB',
borderWidth: 1.5,
shadowColor: '#0089FB',
shadowOffsetX: 0,
shadowOffsetY: 4,
shadowBlur: 15
},
emphasis: {
areaColor: 'transparent', // 悬浮背景
textStyle: {
color: '#fff'
}
}
}
}],
series: series
}
setTimeout(() => {
this.$refs.chart?.chart.dispatchAction({
type: 'showTip',
seriesIndex: 1, // 针对series下第几个数据
dataIndex: 0 // 第几个数据
})
}, 0)
if (!this.timer) {
this.timer = setInterval(() => {
this.pointIndex++
if (this.pointIndex === this.pointData.length) {
this.pointIndex = 0
}
this.getInitMap()
}, 5000)
}
}
}
}
</script>
<style lang="scss" scoped>
.map-warp {
width: 100%;
height: 100%;
position: relative;
.map-left-box{
width: 220px;
height: 180px;
// border: 1px solid;
z-index: 9999999;
position: absolute;
left: 0;
bottom: 10px;
overflow: auto;
margin-right: 10px;
.box-value{
width: 100%;
height: 24px;
line-height: 24px;
background-size: 100% 100%;
background: url(../../../assets/new-screen/nomal-bg.png)no-repeat;
background-size: 100% 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
.num{
width: 20px;
font-size: 12px;
padding-left: 20px;
font-family: DIN;
height: 24px;
line-height: 24px;
text-align: center;
}
.name{
font-size: 12px;
text-align: center;
height: 24px;
line-height: 24px;
}
.value{
font-size: 12px;
width: 80px;
line-height: 24px;
height: 24px;
background: url(../../../assets/new-screen/select-bg.png)no-repeat;
background-size: 100% 100%;
text-align: center;
}
}
}
.map-right-box{
width: 200px;
height: 200px;
border: 1px solid;
z-index: 9999999;
position: absolute;
right: 0;
bottom: 0;
}
}
.box{
width: 100px;
height: 100px;
}
::v-deep *::-webkit-scrollbar{
background-color: transparent!important;
}
::v-deep *::-webkit-scrollbar-thumb{
background-color: transparent!important;
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<div class="left-bottom-warp">
<item :title="$t('screen.powerGenerationRanking')">
<dv-loading v-if="tableLoading">Loading...</dv-loading>
<div v-else class="con-box">
<SwiperTable
:titles="tableTitles"
:widths="widths"
:content-height="contentHeight"
:data="tableData"
:columns="tableColumns"
:is-sort="false"
/>
</div>
</item>
</div>
</template>
<script>
import SwiperTable from './SwiperTable.vue'
import { GetEfficiencyDate } from '@/api/screen/zzScreen'
import item from './item-warp.vue'
export default {
components: { item, SwiperTable },
data() {
return {
tableLoading: false,
tableTitles: [this.$t('screen.stationName'), this.$t('screen.cap')],
tableData: [],
widths: ['auto', 'auto', 'auto', 'auto'],
contentHeight: 200,
tableColumns: [
'stationName',
'stationCapacity',
'efficiencyValue'
]
}
},
mounted() {
},
methods: {
async getData(deptId) {
try {
this.tableLoading = true
const res = await GetEfficiencyDate({
deptId: deptId
})
this.tableData = res.data
} finally {
this.tableLoading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.left-bottom-warp{
width: 100%;
height: 100%;
.con-box{
width: 100%;
padding-top: 10px;
}
}
.class1{
color: #F4E925;
}
</style>

View File

@ -0,0 +1,68 @@
<template>
<div class="right-top-warp">
<item :title="$t('screen.powerGenerationEarningRanking')">
<dv-loading v-if="tableLoading">Loading...</dv-loading>
<div v-else class="con-box">
<SwiperTableCenter
:titles="tableTitles"
:widths="widths"
:content-height="contentHeight"
:data="tableData"
:columns="tableColumns"
:show-header="false"
tabel-height="53px"
/>
</div>
</item>
</div>
</template>
<script>
import item from './item-warp.vue'
import SwiperTableCenter from './SwiperTableCenter.vue'
import { GetRegionalIncomeDate } from '@/api/screen/zzScreen'
export default {
components: { item, SwiperTableCenter },
data() {
return {
tableLoading: false,
tableTitles: ['电站名称', '容量'],
tableData: [],
widths: ['auto', 'auto', 'auto', 'auto'],
contentHeight: 220,
tableColumns: [
'stationName',
'incomeValue'
]
}
},
mounted() {
},
methods: {
async getData(deptId) {
try {
this.tableLoading = true
const res = await GetRegionalIncomeDate({
deptId: deptId
})
this.tableData = res.data
this.tableData.map((el) => {
el.incomeValue = el.incomeValue + ' ' + this.$t('screen.mRMB')
})
} finally {
this.tableLoading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.right-top-warp {
width: 100%;
height: 100%;
.con-box{
width: 100%;
}
}
</style>

View File

@ -0,0 +1,244 @@
<template>
<div class="right-top-warp">
<item :title="$t('screen.groupEarning')">
<div class="box">
<div class="button-box">
<el-tabs
v-model="activeName"
tab-position="top"
@tab-click="handleClick"
>
<el-tab-pane :label="$t('screen.daily30')" name="day" />
<el-tab-pane :label="$t('screen.monthly')" name="month" />
<el-tab-pane :label="$t('screen.yearly')" name="year" />
</el-tabs>
</div>
<div class="chart-box">
<dv-loading v-if="loading">Loading...</dv-loading>
<Chart v-else :options="options" />
</div>
</div>
</item>
</div>
</template>
<script>
import item from './item-warp.vue'
import { GetIncomeCurve } from '@/api/screen/zzScreen'
export default {
components: { item },
props: {},
data() {
return {
loading: false,
options: {},
deptId: null,
type: 'day',
activeName: 'day'
}
},
mounted() {
// this.getData()
},
methods: {
handleClick() {
this.getData(this.deptId)
},
async getData(deptId) {
this.deptId = deptId
try {
this.loading = true
const { data } = await GetIncomeCurve({ type: this.activeName, deptId: deptId })
this.initChart(data)
} catch (error) {
console.log(error)
} finally {
this.loading = false
}
},
initChart(val) {
if (val.length) {
const x_data = []
const profit_data = []
val.forEach((el) => {
x_data.push(el.time)
profit_data.push(el.profit)
})
this.options = {
// backgroundColor: '#072685',
grid: {
left: '10%',
top: '15%',
right: '5%',
bottom: '10%'
},
tooltip: {
trigger: 'axis'
},
xAxis: [
{
data: x_data,
axisLabel: {
textStyle: {
color: '#6E7174'
},
margin: 10 // 刻度标签与轴线之间的距离。
},
axisLine: {
show: false // 不显示x轴
},
axisTick: {
show: false // 不显示刻度
},
boundaryGap: true,
splitLine: {
show: false,
width: 0.08,
lineStyle: {
type: 'solid',
color: '#03202E'
}
}
}
],
yAxis: [
{
type: 'value',
name: this.$t('screen.mRMB'),
nameTextStyle: {
color: '#6E7174',
fontSize: 12,
padding: [0, 0, 0, -40]
},
splitLine: {
show: false,
lineStyle: {
color: '#eee',
type: 'solid'
}
},
axisTick: {
show: false
},
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#6E7174'
}
}
}
],
series: [
{
name: this.$t('screen.earning'),
type: 'line',
data: profit_data,
smooth: true,
symbolSize: 5,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(245, 209, 164, 1)' // 折线点的颜色
}
},
lineStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(245, 209, 164, 1)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(236, 168, 80, 1)' // 100% 处的颜色
}
],
global: false // 缺省为 false
},
// shadowColor: 'rgba(255, 120, 0,1)',
shadowBlur: 8
}
}
]
}
} else {
this.options = {
title: {
text: this.$t('screen.noData'),
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
color: 'rgb(153, 153, 153)',
fontWeight: 'normal'
}
}
}
}
}
}
}
</script>
<style lang="scss" scoped>
.right-top-warp {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.box {
width: 100%;
height: 100%;
.button-box {
width: 100%;
height: 25px;
padding-top: 5px;
text-align: right;
}
.chart-box {
height: calc(100% - 25px);
width: 100%;
}
}
::v-deep .el-tabs {
height: 100% !important;
}
::v-deep .el-tabs__header {
height: 100% !important;
}
::v-deep .el-tabs__nav-wrap {
height: 100% !important;
}
::v-deep .el-tabs__nav-scroll {
height: 100% !important;
display: flex;
justify-content: right;
}
::v-deep .el-tabs__nav {
transform: translate(0, -12px) !important;
}
::v-deep .el-tabs__active-bar {
bottom: -15px !important;
height: 6px !important;
transform: skewX(-45deg);
}
::v-deep .el-tabs__nav-wrap::after {
display: none;
}
::v-deep .el-tabs__item {
padding: 0 10px !important;
}
}
</style>

View File

@ -0,0 +1,389 @@
<template>
<ScaleScreen :width="1920" :height="1080" class="scale-wrap" :self-adaption="true">
<div class="bg">
<!-- <dv-loading v-show="loading">Loading...</dv-loading> -->
<div class="host-body">
<!-- 头部 s -->
<div class="title_wrap">
<div class="title">
<span :class="lang === 'zh'? 'left-title' : 'left-title-en'" />
<span :class="lang === 'zh'? 'title-text' : 'en-eitle-text'">{{ $t('screen.pvScreenTitle') }}</span>
<div class="right-title">
<span>{{ time }}</span>
<!-- <span class="weather">{{ weatherInfo ? weatherInfo.skyCon : '' }} {{ weatherInfo? weatherInfo.minTemperature :'' }} ~ {{ weatherInfo ? weatherInfo.maxTemperature : '' }}</span> -->
</div>
</div>
</div>
<div class="box">
<div class="left">
<div class="left-top">
<LeftTop ref="LeftTopRef" :info="leftTopInfo" />
</div>
<div class="left-center">
<LeftCenter ref="LeftCenterRef" />
</div>
<div class="left-bottom">
<LeftBottom ref="LeftBottomRef" />
</div>
</div>
<div class="center">
<div class="center-top">
<CenterTop ref="CenterTopRef" :info="centerTopInfo" />
</div>
<div class="center-map">
<MapCenter ref="MapCenterRef" />
</div>
<div class="center-bottom">
<CenterBottom ref="CenterBottomRef" />
</div>
</div>
<div class="right">
<div class="right-top">
<RightTop ref="RightTopRef" />
</div>
<div class="right-center">
<RightCenter ref="RightCenterRef" />
</div>
<div class="right-bottom">
<RightRight ref="RightRightRef" />
</div>
</div>
</div>
</div>
</div>
</ScaleScreen>
</template>
<script>
import ScaleScreen from '@/components/scale-screen/scale-screen.vue'
import MapCenter from './components/map-center.vue'
import LeftTop from './components/left-top.vue'
import LeftCenter from './components/left-center.vue'
import LeftBottom from './components/left-bottom.vue'
import RightTop from './components/right-top.vue'
import RightCenter from './components/right-center.vue'
import RightRight from './components/right-bottom.vue'
import CenterBottom from './components/center-bottom.vue'
import CenterTop from './components/center-top.vue'
import { GetOverviewData, GetNewWeather, GetDeptIdByStationId } from '@/api/screen/zzScreen'
export default {
components: {
// MapCenter, CenterTop,
ScaleScreen, MapCenter, LeftTop, LeftCenter, LeftBottom, RightTop, RightCenter, RightRight, CenterBottom, CenterTop
},
data() {
return {
time: '',
leftTopInfo: null,
leftBottomInfo: null,
deptId: null,
interval: null,
centerTopInfo: {
totalProfit: 0,
yearProfit: 0,
yestProfit: 0
},
weatherInfo: {
skyCon: '',
minTemperature: '',
maxTemperature: ''
}
}
},
computed: {
lang() {
return this.$store.getters.language
}
},
created() {
this.changeFavicon(`./zhongzi.ico`)
setInterval(() => {
this.time = this.getCurrentFormattedTime()
}, 1000)
},
mounted() {
this.getDeptIdByStationId()
},
destroyed() {
clearInterval(this.interval)
this.interval = null
},
methods: {
// 修改Favicon的方法
changeFavicon(link) {
let $favicon = document.querySelector('link[rel="icon"]')
if ($favicon !== null) {
$favicon.href = link
} else {
$favicon = document.createElement('link')
$favicon.rel = 'icon'
$favicon.href = link
document.head.appendChild($favicon)
}
},
getCurrentFormattedTime() {
const now = new Date()
const year = now.getFullYear().toString().padStart(4, '0')
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = now.getDate().toString().padStart(2, '0')
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
const seconds = now.getSeconds().toString().padStart(2, '0')
const formattedTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
return formattedTime
},
async getData() {
try {
this.$refs.LeftCenterRef.getData(this.deptId)
this.$refs.LeftBottomRef.getData(this.deptId)
this.$refs.RightTopRef.getData(this.deptId)
this.$refs.RightCenterRef.getData(this.deptId)
this.$refs.RightRightRef.getData(this.deptId)
// this.getWeatherInfo() 国外大屏不展示天气
this.$refs.MapCenterRef.getStationNum(this.deptId)
this.$refs.MapCenterRef.getData(this.deptId)
this.$refs.MapCenterRef.getLeftData(this.deptId)
this.$refs.CenterBottomRef.getData(this.deptId)
const res = await GetOverviewData({
deptId: this.deptId
})
this.leftTopInfo = {
capacity: res.data.capacity,
stationNumber: res.data.stationNumber,
yearCharge: res.data.yearCharge,
yearDischarge: res.data.yearDischarge,
totalCharge: res.data.totalCharge,
totalDischarge: res.data.totalDischarge,
dayCharge: res.data.dayCharge,
dayDischarge: res.data.dayDischarge
}
this.centerTopInfo = {
totalProfit: (Number(res.data.totalProfit) / 1E4).toFixed(2),
yearProfit: (Number(res.data.yearProfit) / 1E4).toFixed(2),
yestProfit: (Number(res.data.yestProfit) / 1E4).toFixed(2)
}
} catch (err) {
this.leftTopInfo = {
capacity: 0,
stationNumber: 0,
yearCharge: 0,
yearDischarge: 0,
totalCharge: 0,
totalDischarge: 0,
dayCharge: 0,
dayDischarge: 0
}
this.centerTopInfo = {
totalProfit: 0,
yearProfit: 0,
yestProfit: 0
}
}
},
getWeatherInfo() {
GetNewWeather().then(res => {
this.weatherInfo = res.data
})
},
getDeptIdByStationId() {
GetDeptIdByStationId({
stationId: null
}).then(res => {
this.deptId = res.data?.deptId
this.getData()
this.interval = setInterval(() => {
this.getData()
}, 600000)
})
}
}
}
</script>
<style lang="scss" scoped>
// 左右两侧图表宽度
$lrWidth: 385px;
$cTopHeight: 85px;
$lTopHeight: 313px;
$lBottomHeight: 300px;
$rTopHeight: 313px;
$rBottomHeight: 300px;
$margin: 16px;
.scale-wrap {
color: #d3d6dd;
width: 1920px;
height: 1080px;
overflow: hidden;
.bg {
width: 100%;
height: 100%;
box-sizing: border-box;
background-color: #000c18;
.box{
height: 100%;
display: flex;
.left {
width: $lrWidth;
height: calc(100% - 100px);
display: flex;
margin-left: $margin;
flex-direction: column;
.left-top {
width: 100%;
height: $lTopHeight;
margin-bottom: $margin;
}
.left-center {
width: 100%;
flex: 1;
margin-bottom: $margin;
}
.left-bottom {
width: 100%;
margin-bottom: $margin;
height: $lBottomHeight;
}
}
.center{
flex: 1;
height: calc(100% - 100px);
display: flex;
flex-direction: column;
margin: 0 $margin 0 $margin;
.center-top{
height: 80px;
}
.center-map{
flex: 1;
margin-bottom: $margin;
}
.center-bottom{
height: $lBottomHeight;
margin-bottom: $margin;
}
}
.right {
width: $lrWidth;
height: calc(100% - 100px);
display: flex;
margin-right: $margin;
flex-direction: column;
.right-top {
width: 100%;
height: $rTopHeight;
margin-bottom: $margin;
}
.right-center {
width: 100%;
margin-bottom: $margin;
flex: 1;
}
.right-bottom {
width: 100%;
margin-bottom: $margin;
height: $rBottomHeight;
}
}
}
}
.host-body {
height: 100%;
background-color: #070A0C;
background-size: 100% 100%;
.title_wrap {
height: 100px;
background-image: url("../../assets/new-screen/top-bg.png");
background-size: cover;
background-position: center center;
position: relative;
margin-bottom: 4px;
}
.title {
position: relative;
text-align: center;
background-size: cover;
color: transparent;
height: 127px;
line-height: 100px;
.left-title {
width: 200px;
height: 52px;
background: url(../../assets/new-screen/zhongzi.png) no-repeat;
background-size: 100% 100%;
color: #fff;
font-size: 16px;
position: absolute;
left: $margin;
top: 30px;
}
.left-title-en {
width: 200px;
height: 52px;
background: url(../../assets/new-screen/zhongzien.png) no-repeat;
background-size: 100% 100%;
color: #fff;
font-size: 16px;
position: absolute;
left: $margin;
top: 30px;
}
.right-title {
font-family: LCD;
color: #fff;
position: absolute;
right: $margin;
top: 10px;
font-size: 22px;
.weather{
margin-left: 16px;
}
}
.en-eitle-text{
font-size: 18px;
letter-spacing: 1px;
font-weight: 900;
width: 100%;
background: linear-gradient(92deg, #fff, #fff 48.85254%, #fff);
font-family: PangMenNumber;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.title-text {
font-size: 38px;
font-weight: 900;
letter-spacing: 6px;
width: 100%;
background: linear-gradient(92deg,
#fff 0%,
#fff 48.8525390625%,
#fff 100%);
font-family: PangMenNumber;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
}
}</style>

View File

@ -0,0 +1,89 @@
<template>
<div>
<div class="loader">
<div id="ld4">
<div />
<div />
<div />
<div />
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Loading',
components: {},
props: {},
data() {
return {}
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {}
}
</script>
<style lang="scss" scoped>
.loader {
width: 60px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0 10px;
}
#ld4 {
position: relative;
display: flex;
width: 100%;
justify-content: space-between;
}
#ld4 div {
height: 8px;
width: 8px;
border-radius: 50%;
background: #409eff;
}
#ld4 div:nth-child(1) {
animation: ld4 3s linear infinite 0s;
}
#ld4 div:nth-child(2) {
animation: ld4 3s linear infinite 0.15s;
}
#ld4 div:nth-child(3) {
animation: ld4 3s linear infinite 0.3s;
}
#ld4 div:nth-child(4) {
animation: ld4 3s linear infinite 0.45s;
}
@keyframes ld4 {
0% {
opacity: 0;
transform: scale(0.3);
// background: #409eff;
}
25% {
opacity: 1;
transform: scale(1.8);
// background: #0072bb;
}
50% {
opacity: 0;
transform: scale(0.3);
// background: #fe4a49;
}
75% {
opacity: 1;
transform: scale(1.8);
// background: #fed766;
}
100% {
opacity: 0;
}
}
</style>

View File

@ -0,0 +1,988 @@
<template>
<div class="earnings-warp">
<el-form id="searchForm">
<el-row :gutter="5" class="search-row">
<el-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7">
<el-form-item :label="$t('state.month') + ':'" label-width="90px">
<el-date-picker
v-model="month"
type="month"
value-format="yyyy-MM"
:placeholder="$t('state.selectMonth')"
:clearable="false"
@change="changeTime"
/>
</el-form-item>
</el-col>
<el-col
:xs="24"
:sm="24"
:md="17"
:lg="17"
:xl="17"
style="text-align: right"
>
<el-button
v-permission="['business:earningsCalculate:oneKeyComputation']"
style="margin-left: auto"
type="primary"
class="reset-btn"
@click="handleComputeReport"
>{{ $t("state.onButtonComputed") }}</el-button>
<el-button
type="primary"
class="reset-btn"
:loading="downLoadingReport"
@click="handleExportReport"
>{{ $t("state.exportBill") }}</el-button>
<el-button
type="primary"
class="reset-btn"
:loading="downLoading"
@click="handleExportTempData"
>{{ $t("state.exportReport") }}</el-button>
</el-col>
</el-row>
</el-form>
<el-row class="top-search">
<el-col :span="24" class="report-title">{{
totalData.stationName
}}</el-col>
</el-row>
<div class="top">
<ItemBox :title="$t('state.powerGenerationStatus')">
<div class="box">
<div class="value-box">
<div class="top-box">
<span class="d" />
<el-tooltip
:content="$t('state.totalStringCapacity')"
placement="top"
effect="dark"
>
<span class="title">{{ $t("state.totalStringCapacity") }}</span>
</el-tooltip>
<span class="d" />
</div>
<div class="center-line" />
<div class="bottom-value">
{{ totalData.capacity }}
<span class="unit">kWp</span>
</div>
</div>
<div class="value-box">
<div class="top-box">
<span class="d" />
<el-tooltip
:content="$t('state.currentMonthlyPowerGeneration')"
placement="top"
effect="dark"
>
<span class="title">{{
$t("state.currentMonthlyPowerGeneration")
}}</span>
</el-tooltip>
<span class="d" />
</div>
<div class="center-line" />
<div class="bottom-value">
203{{ totalData.totalChargeElec }}.55
<span class="unit">kWh</span>
</div>
</div>
<div class="value-box">
<div class="top-box">
<span class="d" />
<el-tooltip
:content="$t('state.cumulativePowerGeneration')"
placement="top"
effect="dark"
>
<span class="title">{{
$t("state.cumulativePowerGeneration")
}}</span>
</el-tooltip>
<span class="d" />
</div>
<div class="center-line" />
<div class="bottom-value">
237{{ totalData.totalCharge }}.12
<span class="unit">kWh</span>
</div>
</div>
<div class="value-box">
<div class="top-box">
<span class="d" />
<el-tooltip
:content="$t('state.equivalentPowerGenerationTime')"
placement="top"
effect="dark"
>
<span class="title">{{
$t("state.equivalentPowerGenerationTime")
}}</span>
</el-tooltip>
<span class="d" />
</div>
<div class="center-line" />
<div class="bottom-value">
9.{{ totalData.totalDischargeElec }}6
<span class="unit">kWh/kWp</span>
</div>
</div>
<div class="value-box">
<div class="top-box">
<span class="d" />
<el-tooltip
:content="$t('state.peakACpower')"
placement="top"
effect="dark"
>
<span class="title">{{ $t("state.peakACpower") }}</span>
</el-tooltip>
<span class="d" />
</div>
<div class="center-line" />
<div class="bottom-value">
6{{ totalData.totalDischarge }}.87
<span class="unit">kW</span>
</div>
</div>
<div class="value-box">
<div class="top-box">
<span class="d" />
<el-tooltip
:content="$t('state.gridConnectedDuration')"
placement="top"
effect="dark"
>
<span class="title">{{
$t("state.gridConnectedDuration")
}}</span>
</el-tooltip>
<span class="d" />
</div>
<div class="center-line" />
<div class="bottom-value">
0
<span class="unit">h</span>
</div>
</div>
</div>
</ItemBox>
</div>
<div class="center">
<ItemBox :title="$t('state.monthlyPowerGeneration')">
<div v-loading="loading" class="charts-box">
<Chart
ref="chart"
:key="key"
:options="powerOptions"
:class-name="'chart'"
/>
</div>
</ItemBox>
<!-- <el-row :gutter="10" style="width: 100%">
<el-col
:xs="24"
:sm="12"
:md="12"
:lg="12"
:xl="12"
class="center-left"
>
<ItemBox :title="$t('state.charge')">
<el-table
v-loading="load_data"
:header-cell-style="{
color: '#CEEBFF',
textAlign: 'center',
}"
:data="chargeArr"
style="width: 100%"
:row-style="{ height: '40px' }"
height="175"
>
<el-table-column
align="center"
prop="rateType"
:label="$t('state.time')"
/>
<el-table-column
align="center"
prop="elec"
:label="$t('state.ele')"
/>
<el-table-column
align="center"
prop="digital"
:label="$t('state.expend')"
/>
</el-table>
</ItemBox>
</el-col>
<el-col
:xs="24"
:sm="12"
:md="12"
:lg="12"
:xl="12"
class="center-right"
>
<ItemBox :title="$t('state.disCharge')">
<div class="box">
<el-table
v-loading="load_data"
:header-cell-style="{
color: '#CEEBFF',
textAlign: 'center',
}"
:data="dischargeArr"
style="width: 100%"
:row-style="{ height: '40px' }"
height="175"
>
<el-table-column
align="center"
prop="rateType"
:label="$t('state.time')"
/>
<el-table-column
align="center"
prop="elec"
:label="$t('state.ele')"
/>
<el-table-column
align="center"
prop="digital"
:label="$t('state.earnings') + `(${$t('state.rmb')})`"
/>
</el-table>
</div>
</ItemBox>
</el-col>
</el-row> -->
</div>
<div class="bottom">
<ItemBox :title="$t('state.projectRevenue')">
<div class="box">
<div class="value-box">
<div class="title">{{ $t("state.monthlyPVpowerGeneration") }}</div>
<div class="bottom-value">
<div class="value">
2031.{{ totalData.totalChargePrice }}7
<span class="unit">kWh</span>
</div>
</div>
</div>
<div class="value-box">
<div class="title">
{{ $t("state.monthlyInverterPowerGeneration") }}
</div>
<div class="bottom-value">
<div class="value">
237{{ totalData.totalDisChargePrice }}.53
<span class="unit">kWh</span>
</div>
</div>
</div>
<div class="value-box">
<div class="title">{{ $t("state.monthlyIncome") }}</div>
<div class="bottom-value">
<div class="value">
45{{ totalData.income }}8.3
<span class="unit">{{ $t("state.rmb") }}</span>
</div>
</div>
</div>
</div>
</ItemBox>
</div>
<el-dialog
:append-to-body="false"
:title="$t('state.earningsRecalculation')"
center
:close-on-click-modal="false"
:visible.sync="computedShow"
width="30%"
@close="closeComputed"
>
<template v-if="!conformLoading">
<el-form :model="filter">
<el-row class="search-row">
<el-col v-if="timeShow" :span="24">
<el-form-item
:label="$t('state.computedTime') + ':'"
label-width="120px"
>
<el-date-picker
v-model="filter.time"
style="width: 90%"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="~"
:start-placeholder="$t('state.startTime')"
:end-placeholder="$t('state.endTime')"
:disabled="progressShow"
/>
</el-form-item>
</el-col>
<el-col v-if="progressShow" :span="24">
<el-form-item
:label="$t('state.conputedProgress') + ':'"
label-width="150px"
>
<el-progress
:text-inside="true"
:stroke-width="20"
style="margin-top: 8px"
:percentage="percentage"
:format="progressFormat"
class="case-progress"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeComputed">{{ $t("state.close") }}</el-button>
<el-button v-if="sureShow" type="primary" @click="sureComputed">{{
$t("state.sure")
}}</el-button>
</span>
</template>
<loading v-show="conformLoading" />
</el-dialog>
</div>
</template>
<script>
import * as echarts from 'echarts'
import {
GetTotal,
OneKeyComputation,
SureOneKeyComputation
} from '@/api/revenue-management/earnings-statement'
import loading from './components/loading'
import { handleDownExcel } from '@/utils'
export default {
components: { loading },
data() {
return {
month: '',
downLoadingReport: false,
downLoading: false,
totalData: {},
load_data: false,
chargeArr: [],
dischargeArr: [],
computedTime: [],
computedShow: false,
computedLoading: false,
filter: {
time: []
},
percentage: 0,
finish: '',
timeShow: true,
progressShow: false,
sureShow: true,
timer: undefined,
confirmTimer: undefined,
conformLoading: false,
powerOptions: undefined,
currentType: 'day',
color: ['#4197FF'],
key: 0,
dontClick: true
}
},
computed: {
currentStation() {
return this.$store.getters.currentStation || undefined
}
},
watch: {
currentStation: {
handler(val) {
if (val && val.id) {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0') // 月份从0开始所以要加1然后用padStart确保是两位数
this.month = `${year}-${month}`
this.stationId = val.id
this.get_table_data()
}
},
deep: true,
immediate: true
}
},
mounted() {
const currentData = []
for (let i = 1; i < 32; i++) {
currentData.push({
date: i,
chargeElec: Number((Math.random() * 50 + 30).toFixed(2))
})
}
this.initCharts(currentData, 1)
},
methods: {
closeComputed() {
if (this.dontClick) {
return
}
this.computedShow = false
if (this.timer) {
clearInterval(this.timer)
}
if (this.confirmTimer) {
clearInterval(this.confirmTimer)
}
setTimeout(() => {
this.filter.time = []
this.sureShow = true
}, 500)
},
getProgress() {
const that = this
const params = {
stationId: this.stationId
}
this.conformLoading = true
this.timer = setInterval(async() => {
try {
const res = await SureOneKeyComputation(params)
if (res.data?.finish === 1) {
// 计算完成
that.sureShow = false
this.percentage = +res.data.progress
clearInterval(this.timer)
}
if (res.data?.finish === '') {
// 未计算
that.progressShow = false
that.timeShow = true
that.sureShow = true
clearInterval(this.timer)
}
if (res.data?.finish !== 1 && res.data?.finish !== '') {
// 计算中
that.progressShow = true
that.timeShow = false
that.sureShow = false
this.percentage = +res.data.progress
}
this.conformLoading = false
} catch (error) {
console.log(error)
clearInterval(this.timer)
this.$message.warning(this.$t('state.computedFail'))
this.conformLoading = false
}
}, 1000)
},
progressFormat(percentage) {
return percentage === 100 ? this.$t('state.finish') : `${percentage}%`
},
async sureComputed() {
if (this.dontClick) {
return
}
const that = this
if (this.filter.time.length === 0) {
this.$message.warning(this.$t('state.selectComputedTime'))
return
}
this.computedLoading = true
const params = {
stationId: this.stationId,
startTime: this.filter.time[0],
endTime: this.filter.time[1]
}
let res = await OneKeyComputation(params)
this.sureShow = false
this.confirmTimer = setInterval(async() => {
try {
res = await SureOneKeyComputation(params)
// that.sureShow = false
this.timeShow = true
this.progressShow = true
this.percentage = +res.data.progress
if (res.data.finish === 1) {
// 计算完成
// this.sureShow = true
that.computedLoading = false
this.percentage = +res.data.progress
clearInterval(this.confirmTimer)
}
if (res.data?.finish === '') {
// 未计算
that.computedLoading = false
// res = await OneKeyComputation(params)
clearInterval(this.confirmTimer)
}
if (res.data.finish !== 1 && res.data.finish !== '') {
// 计算中
this.percentage = +res.data.progress
}
} catch (error) {
clearInterval(this.confirmTimer)
this.$message.warning(this.$t('state.computedFail'))
}
}, 3000)
},
changeTime() {
this.get_table_data()
},
async get_table_data() {
this.load_data = true
const params = {
stationId: this.stationId,
time: this.month
}
try {
const res = await GetTotal(params)
this.totalData = res.data
this.chargeArr = []
this.dischargeArr = []
if (res.data.finish === 0) {
this.$message.warning(
`${this.$t('state.computeding')}${res.data.progress}${this.$t(
'state.laterQuery'
)}`
)
}
if (res.data.list.length) {
res.data.list.forEach((el) => {
if (+el.type === 0) {
this.chargeArr.push(el)
}
if (+el.type === 1) {
this.dischargeArr.push(el)
}
})
} else {
this.chargeArr = []
this.dischargeArr = []
}
} catch (error) {
// console.log(error);
} finally {
this.load_data = false
}
},
handleExportReport() {
if (this.dontClick) {
return
}
this.downLoadingReport = true
const params = {
title: this.$t('state.bill'),
stationId: this.stationId,
time: this.month
}
handleDownExcel(
'/business/earningsCalculate/exportReport',
params,
(callback) => {
console.log(callback)
this.downLoadingReport = false
}
)
},
handleExportTempData() {
if (this.dontClick) {
return
}
this.downLoading = true
const params = {
title: this.$t('state.earningReport'),
stationId: this.stationId,
time: this.month
}
handleDownExcel(
'/business/earningsCalculate/export',
params,
(callback) => {
console.log(callback)
this.downLoading = false
}
)
},
handleComputeReport() {
if (this.dontClick) {
return
}
this.computedShow = true
this.getProgress()
},
// 新增代码
initCharts(val, type) {
const x_data = []
const charge_data = []
// const discharge_data = []
// let benefit_data = [0, 0, 0, 0, 0, 0, 0]
if (val && val.length > 0) {
val.forEach((item) => {
x_data.push(item.date)
charge_data.push(item.chargeElec)
// discharge_data.push(item.dischargeElec)
})
}
this.powerOptions = {
grid: {
top: '25%',
left: '5%',
right: '5%',
bottom: '5%',
containLabel: true
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: (params) => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${this.month}-${params[0].name}</p></div>`
params.forEach((item) => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${this.color[0]};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${item.data}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
xAxis: [
{
type: 'category',
data: x_data,
axisLine: {
show: true,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
}
}
],
yAxis: [
{
type: 'value',
name: `kWh`,
nameTextStyle: {
color: ' rgba(255, 255, 255, 0.5)'
},
axisLine: {
show: false,
lineStyle: {
color: '#90e9d8'
}
},
axisLabel: {
show: true,
color: '#CEEBFF'
},
axisTick: {
show: false
},
splitLine: {
show: true, // 强制显示分割线(默认已开启,确保不被隐藏)
lineStyle: {
type: 'dashed', // 线型设为虚线
color: 'rgba(255,255,255,0.2)', // 虚线颜色(可自定义,如 #999、rgba(0,0,0,0.1)
width: 1, // 虚线宽度
dashOffset: 5 // 虚线偏移量(可选,调整虚线起始位置)
}
}
}
],
series: [
{
name: `${this.$t('state.powerGeneration')}`,
type: 'bar',
data: charge_data,
barWidth: 14, // 柱状图的宽度
color: '#00C8FF',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0,200,255,0.90)' },
{ offset: 1, color: 'rgba(0, 200, 255, 0.0)' }
])
}
}
]
}
}
}
}
</script>
<style lang="scss" scoped>
.earnings-warp {
width: 100%;
height: 100%;
background: var(--table-bg);
padding: 10px;
overflow: auto;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.15);
.top {
// height: 180px;
width: 100%;
.box {
width: 100%;
height: 100%;
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
padding: 20px;
.value-box {
background: url(../../../assets/images/yxsj.png) no-repeat;
background-size: 100% 100%;
width: 190px;
height: 80px;
display: flex;
flex-direction: column;
justify-content: space-around;
.top-box {
width: 100%;
display: flex;
align-items: center;
height: 30px;
justify-content: space-around;
.d {
display: flex;
align-items: center;
justify-content: center;
display: inline-block;
width: 4px;
height: 4px;
background: #ffb800;
border-radius: 1px;
margin-left: 10px;
margin-right: 10px;
}
.title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #ceebff;
font-size: 16px;
}
}
.center-line {
width: 80%;
text-align: center;
background: url(../../../assets/images/xt.png) no-repeat;
height: 5px;
margin-left: 10%;
background-size: 100% 100%;
}
.bottom-value {
height: 30px;
width: 100%;
color: #fff;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
.unit {
margin-left: 2px;
height: 28px;
line-height: 30px;
color: #fff;
font-size: 12px;
}
}
}
}
}
.center {
width: 100%;
// height: 220px;
display: flex;
margin-top: 10px;
justify-content: space-between;
.box {
width: 100%;
height: 175px;
}
}
.bottom {
margin-top: 10px;
width: 100%;
height: calc(100% - 220px - 300px);
.box {
width: 100%;
height: 100%;
display: flex;
justify-content: space-around;
align-items: center;
.value-box {
background: url(../../../assets/images/report-bottom.png) no-repeat;
background-size: 100% 100%;
width: 240px;
height: 212px;
.title {
text-align: center;
width: 100%;
height: 40px;
line-height: 40px;
font-size: 20px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;
font-weight: 400;
color: #ffffff;
letter-spacing: 1px;
background: linear-gradient(180deg, #ffffff 37%, #5bbaf2 88%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.bottom-value {
height: calc(212px - 40px);
padding-top: 22px;
display: flex;
.value {
width: 100%;
height: 49px;
font-size: 40px;
text-align: center;
font-weight: bold;
color: #e3e9f5;
line-height: 47px;
// text-shadow: 0px 6px 6px rgba(206, 235, 255, 0.2);
color: #e3e9f5;
text-align: center;
text-shadow: 0px 6.400000095367432px 6.400000095367432px
rgba(206, 235, 255, 0.2);
font-family: DIN;
font-size: 40px;
font-style: normal;
font-weight: 700;
line-height: normal;
.unit {
color: #e3e9f5;
font-family: DIN;
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
}
}
}
}
}
.top-search {
width: 100%;
height: 50px;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 10px;
.report-title {
text-align: center;
font-size: 24px;
white-space: nowrap;
color: #fff;
margin-bottom: 10px;
}
.serach-item {
display: flex;
align-items: center;
color: $text-color;
.input-box {
width: 60%;
}
}
.btns-box {
text-align: right;
.upload-btn {
background: $tiffany;
}
.download-btn {
background: $green;
}
}
}
/deep/ .el-table__header-wrapper {
background-color: #025086 !important;
}
}
.case-progress {
::v-deep .el-progress-bar__outer {
background-color: #023b61;
}
}
.charts-box {
width: 100%;
height: 175px;
}
</style>

View File

@ -0,0 +1,255 @@
<template>
<div class="left-bottom-wrap">
<ItemBox :title="$t('pcs.powerGenerationCapacity')">
<div slot="title-right">
<div class="title-right">
<div class="title-content">
{{ $t("pcs.timeGranula") }}
<el-select
v-model="timeLidu"
style="width: 120px"
@change="changTime"
>
<el-option
v-for="item in times"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</div>
<div v-loading="loading" class="top-bottom-container">
<Chart :options="options" :class-name="'chart'" />
</div>
</ItemBox>
</div>
</template>
<script>
import { GetPcsCurve } from '@/api/surveillance/pcs'
import ItemBox from '../../item-kuang.vue'
export default {
name: 'Index',
components: { ItemBox },
data() {
return {
timeLidu: 1,
times: [
{ label: 1 + this.$t('pcs.min'), value: 1 },
{ label: 5 + this.$t('pcs.min'), value: 5 },
{ label: 10 + this.$t('pcs.min'), value: 10 },
{ label: 15 + this.$t('pcs.min'), value: 15 },
{ label: 20 + this.$t('pcs.min'), value: 20 },
{ label: 30 + this.$t('pcs.min'), value: 30 }
],
options: {
title: {
text: this.$t('pcs.noData'),
x: 'center',
y: 'center',
textStyle: {
fontSize: 14,
fontWeight: 'normal'
}
}
},
stationId: '',
srcId: '',
loading: false
}
},
computed: {
language() {
return this.$store.getters.language || undefined
}
},
watch: {
language: {
handler(val) {
if (this.stationId) {
this.times = [
{ label: 1 + this.$t('pcs.min'), value: 1 },
{ label: 5 + this.$t('pcs.min'), value: 5 },
{ label: 10 + this.$t('pcs.min'), value: 10 },
{ label: 15 + this.$t('pcs.min'), value: 15 },
{ label: 20 + this.$t('pcs.min'), value: 20 },
{ label: 30 + this.$t('pcs.min'), value: 30 }
]
this.getData(this.stationId, this.srcId)
}
},
deep: true
}
},
created() {
// this.getChatData()
},
mounted() {},
methods: {
changTime() {
this.getData(this.stationId, this.srcId)
},
async getData(stationId, srcId) {
this.loading = true
this.stationId = stationId
this.srcId = srcId
const params = {
sampleTime: this.timeLidu,
stationId: stationId,
srcId: srcId
}
try {
const res = await GetPcsCurve(params)
this.getChatData(res.data)
} catch (error) {
// console.log(error);
} finally {
this.loading = false
}
},
getChatData(val) {
const gonglv = []
const xAxis = []
val.forEach((v) => {
xAxis.push(v.data)
gonglv.push(Number((Math.random() * 50 + 100).toFixed(2)))
})
this.options = {
color: ['#00C8FF', '#FBBB11'],
legend: {},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0,)',
borderColor: 'rgba(0,0,0,0,);',
borderWidth: 0,
textStyle: {
width: 160,
height: 250,
lineHeight: 24,
color: '#ffffff',
fontSize: '14',
fontFamily: 'SourceHanSansCN-Normal'
},
formatter: (params) => {
// 获取xAxis data中的数据
let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].name}</p></div>`
params.forEach((item) => {
dataStr += `<div>
<div style="margin: 0 8px;">
<span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${item.color};"></span>
<span>${item.seriesName}</span>
<span style="float:right;color:#00C8FF;margin-left:20px;">${item.data}</span>
</div>
</div>`
})
const div = `<div style='border: 1px solid ;
border-image: linear-gradient(130deg, #FFFFFF 0%, rgba(201,255,243,0.00) 22%, rgba(201,255,243,0.00) 75%, rgba(201,255,243,0.00) 80%, #FFFFFF 99%, #FFFFFF 99%) 1;
box-shadow: inset 0px 2px 16px 0px rgba(0, 148, 255, 0.4);' >${dataStr}</div>`
return div
}
},
grid: {
top: '15%',
left: '5%',
right: '5%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: xAxis,
splitLine: {
show: false
}
},
yAxis: [
{
type: 'value',
axisLabel: {
color: '#00C8FF'
},
nameTextStyle: {
color: '#00C8FF'
}
// min: function(value) {
// return Math.floor(
// (Math.abs(value.min) < value.max
// ? value.max
// : value.min
// ).toFixed(2)
// )
// },
// max: function(value) {
// return Math.ceil(
// (Math.abs(value.min) < value.max
// ? value.max
// : value.min
// ).toFixed(2)
// )
// }
}
],
dataZoom: [
{
type: 'inside',
start: 0,
end: 100
},
{
start: 0,
height: 20,
bottom: 10,
end: 100
}
],
series: [
{
name: this.$t('pcs.powerGenerationCapacity') + '(kW)',
type: 'line',
symbol: 'none',
lineStyle: {
color: '#00A0E9'
},
data: gonglv
}
]
}
}
}
}
</script>
<style lang="scss" scoped>
.left-bottom-wrap {
width: 100%;
height: 100%;
.title-right {
display: flex;
align-items: center;
.title-content {
white-space: nowrap;
padding: 0 10px;
font-size: 16px;
letter-spacing: 0.2em;
color: #ceebff;
font-family: Source Han Sans CN;
display: flex;
}
}
.top-bottom-container {
width: 100%;
height: 100%;
overflow: hidden;
.chat {
width: 100%;
height: 100%;
// color: rgb(0, 200, 255)
}
}
}
</style>

View File

@ -0,0 +1,950 @@
<template>
<div class="center-top-wrap">
<ItemBox :title="$t('pcs.pcstopo')">
<div slot="title-right">
<div class="title-right">
<div class="title-content">
<!-- <el-select v-model="value" placeholder="请选择" style="width:200px">
<el-option
v-for="item in times"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> -->
</div>
</div>
</div>
<div class="center-top-container">
<div class="left">
<div v-loading="left_top_loading" class="left-first">
<div
v-for="item in pcsLeftTopData"
:key="item.label"
class="box-title"
>
<span v-if="language === 'en'" class="title">{{ item.englabel }}</span>
<span v-else class="title">{{ item.label }}</span>
<span class="value">{{ item.value }}</span>
<span class="unit">{{ item.unit }}</span>
</div>
</div>
<div
v-loading="left_bottom_loading"
class="left-first"
style="margin-top: 10%"
>
<div
v-for="item in pcsLeftBottomData"
:key="item.label"
class="box-title"
>
<span v-if="language === 'en'" class="title">{{ item.englabel }}</span>
<span v-else class="title">{{ item.label }}</span>
<span class="value">{{ item.value }}</span>
<span class="unit">{{ item.unit }}</span>
</div>
<!-- <div class="box-title">
<span class="title">Idc</span><span class="value">0.00</span><span class="unit">A</span>
</div>
<div class="box-title">
<span class="title">Pdc</span><span class="value">0.00</span><span class="unit">kW</span>
</div> -->
</div>
</div>
<div class="svg-box">
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:space="preserve"
class="circle-load-rect-svg"
viewBox="0 0 400 450"
>
<defs>
<defs>
<radialGradient id="RadialGradient1">
<stop offset="0%" stop-color="#fff" />
<stop offset="30%" stop-color="#0171c1" />
<stop offset="50%" stop-color="#035088" />
<stop offset="100%" stop-color="#02395a" />
</radialGradient>
</defs>
<marker
id="markerCircle"
markerWidth="15"
markerHeight="15"
refX="5"
refY="5"
>
<circle
cx="5"
cy="5"
r="5"
style="stroke: none; fill: url(#RadialGradient1)"
/>
</marker>
<marker
id="markerArrow"
markerWidth="13"
markerHeight="13"
refX="2"
refY="6"
orient="auto"
>
<path d="M2,2 L2,11 L10,6 L2,2" style="fill: black" />
</marker>
</defs>
<polyline
:points="line.top"
fill="none"
class="g-rect-path"
stroke="#01b4e7"
/>
<!-- 直线 -->
<polyline
:points="line.center"
fill="none"
class="g-rect-path"
stroke="#01b4e7"
/>
<polyline
v-if="
newPcsCenterDataStatus.stateCharging ||
newPcsCenterDataStatus.stateDischarging
"
:points="line.center"
fill="none"
class="g-rect-fill-two"
stroke="#00e9f9"
/>
<polyline
v-if="
newPcsCenterDataStatus.stateCharging ||
newPcsCenterDataStatus.stateDischarging
"
:points="line.center"
fill="none"
class="g-rect-fill-two-delay-1"
stroke="#00e9f9"
/>
<polyline
v-if="
newPcsCenterDataStatus.stateCharging ||
newPcsCenterDataStatus.stateDischarging
"
:points="line.center"
fill="none"
class="g-rect-fill-two-delay-2"
stroke="#00e9f9"
/>
<polyline
v-if="
newPcsCenterDataStatus.stateCharging ||
newPcsCenterDataStatus.stateDischarging
"
:points="line.center"
fill="none"
class="g-rect-fill-two-delay-3"
stroke="#00e9f9"
/>
<!-- <text x="100" y="320" fill="#ffffff" style="font-size: 16px">
{{ $t('pcs.dcbreaker' ) }}
</text> -->
<!-- <rect
x="190"
y="300"
width="20"
height="30"
style="fill: #ff3232"
/> -->
<!-- <rect v-if="newPcsCenterDataStatus.dcBreaker" x="190" y="300" width="20" height="30" style="fill:#ff3232;" />
<rect v-else x="190" y="300" width="20" height="30" style="fill:rgba(4, 48, 80, 0.6);stroke:#fff" /> -->
<image
:xlink:href="frameImg"
x="160"
y="300"
width="100"
height="100"
style="cursor: pointer;"
@click="lookDeviceDetail('triad-pcs-left')"
/>
<rect
x="160"
y="160"
width="80"
height="80"
style="stroke: #ceebff; fill: #4d91da"
/>
<polyline
:points="line.rect"
fill="none"
class="g-rect-path"
stroke="#CEEBFF"
/>
<text x="180" y="190" fill="#ffffff" style="font-size: 18px">
DC
</text>
<text x="200" y="220" fill="#ffffff" style="font-size: 18px">
DC
</text>
</svg>
<!-- <div class="battary-box">
<div class="container">
<div class="header" />
<div class="battery" />
<div class="battery-copy">
<div
class="g-wave"
:style="{
bottom: newPcsCenterDataStatus.soc
? `${(newPcsCenterDataStatus.soc / 100) * 55}px`
: '0px',
}"
/>
<div
class="g-wave"
:style="{
bottom: newPcsCenterDataStatus.soc
? `${(newPcsCenterDataStatus.soc / 100) * 55}px`
: '0px',
}"
/>
<div
class="g-wave"
:style="{
bottom: newPcsCenterDataStatus.soc
? `${(newPcsCenterDataStatus.soc / 100) * 55}px`
: '0px',
}"
/>
</div>
<div class="battery-num">
{{
newPcsCenterDataStatus.soc ? newPcsCenterDataStatus.soc : 0
}}%
</div>
</div>
<div class="soc">SOC</div>
</div> -->
</div>
<div v-loading="right_loding" class="right">
<div class="left-first">
<div class="box-title">
<span class="title">{{ $t('pcs.runState') }}</span><span class="value">{{
workStatus(newPcsCenterDataStatus.runState)
}}</span>
</div>
<div
v-if="newPcsCenterDataStatus.remoteInPlace === 0"
class="box-title"
>
<span class="title">{{ $t('pcs.rsState') }}</span><span class="value">{{ $t('pcs.local') }}</span>
</div>
<div
v-else-if="newPcsCenterDataStatus.remoteInPlace === 1"
class="box-title"
>
<span class="title">{{ $t('pcs.rsState') }}</span><span class="value">{{ $t('pcs.distance') }}</span>
</div>
<!-- <div v-if="newPcsCenterDataStatus.onGrid" class="box-title">
<span class="title">{{ $t('pcs.gridMode') }}</span><span class="value">{{ $t('pcs.grid') }}</span>
</div>
<div v-else class="box-title">
<span class="title">{{ $t('pcs.gridMode') }}</span><span class="value">{{ $t('pcs.offGrid') }}</span>
</div> -->
<div class="box-title-radio">
<div class="title-radio">{{ $t('pcs.deviceState') }}</div>
<div class="radio">
<div class="radio-box">
<div class="radio-value">
<span :class=" newPcsCenterDataStatus.deviceStateStand? 'sq': 'sq-transparent'" />
<span class="sq-value">{{ $t('pcs.standby') }}</span>
</div>
<div class="radio-value">
<span :class=" newPcsCenterDataStatus.deviceStateFault? 'sq': 'sq-transparent'" />
<span class="sq-value">{{ $t('pcs.fault') }}</span>
</div>
<template v-if="!hnStationId.includes(stationId)">
<!-- <div class="radio-value">
<span :class="isChongdian ? 'sq' : 'sq-transparent'" />
<span class="sq-value">{{ $t('pcs.charge') }}</span>
</div> -->
<div class="radio-value">
<span :class="isFangdian ? 'sq' : 'sq-transparent'" />
<span class="sq-value">{{ $t('pcs.powerGeneration') }}</span>
</div>
</template>
<template v-else>
<div class="radio-value">
<span
:class="
newPcsCenterDataStatus.stateCharging
? 'sq'
: 'sq-transparent'
"
/>
<span class="sq-value">{{ $t('pcs.charge') }}</span>
</div>
<div class="radio-value">
<span
:class="
newPcsCenterDataStatus.stateDischarging
? 'sq'
: 'sq-transparent'
"
/>
<span class="sq-value">{{ $t('pcs.discharge') }}</span>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
</ItemBox>
</div>
</template>
<script>
import frameImg from '@/assets/images/wxjd/frame.png'
import {
pcsLeftTopData,
pcsCenterDataStatus,
pcsLeftBottomData1,
pcsLeftBottomData2
} from '../config/config'
import { GetNewValue } from '@/api/surveillance/pcs/index'
import ItemBox from '../../item-kuang.vue'
export default {
name: 'Index',
components: { ItemBox },
props: {},
data() {
return {
frameImg,
value: 'XX直流舱',
times: [
{ label: 'XX直流舱', value: 'XX直流舱' },
{ label: 'XX1直流舱', value: 'XX1直流舱' },
{ label: 'XX2直流舱', value: 'XX2直流舱' },
{ label: 'XX3直流舱', value: 'XX3直流舱' }
],
config: {
header: ['告警'],
data: [],
columnWidth: [],
align: ['center', 'center', 'center', 'center'],
headerBGC: '#062b40',
oddRowBGC: 'rgba(255, 255, 255, 0)',
evenRowBGC: 'rgba(255, 255, 255, 0.05)'
},
pageflag: false,
isChongdian: false,
isFangdian: false,
line: {
top: '50,20,350,20',
center: '200,20 200,320',
rect: '161,239 239,161'
},
radio: 3,
pcsLeftTopData: pcsLeftTopData,
pcsCenterDataStatus: pcsCenterDataStatus,
pcsLeftBottomData: [],
pcsLeftBottomData1: pcsLeftBottomData1,
pcsLeftBottomData2: pcsLeftBottomData2,
newPcsCenterDataStatus: {},
left_top_loading: false,
left_bottom_loading: false,
right_loding: false,
stationId: null,
// 对海南4个电站处理和
hnStationId: [417, 398, 416, 415, 405, 485],
ksStationId: [526],
jlShow: [417, 398, 416, 415, 405, 485, 526, 528, 514, 527, 529]// 控制交流断路器的显示
}
},
computed: {
language() {
return this.$store.getters.language || undefined
}
},
watch: {},
created() {
// this.getChatData()
},
mounted() {},
methods: {
workStatus(val) {
if (val === 0) {
return this.$t('pcs.shutdown')
} else if (val === 1) {
return this.$t('pcs.run')
} else if (val === 2) {
return this.$t('pcs.standby')
}
},
async getData(stationId, srcId) {
this.stationId = stationId
this.getLeftTop(stationId, srcId)
this.getLeftBottom(stationId, srcId)
this.getCeterTop(stationId, srcId)
},
async getLeftTop(stationId, srcId) {
const self = this
this.left_top_loading = true
const colArr = []
this.pcsLeftTopData.forEach((el) => {
colArr.push(el.field)
})
const params = {
stationId,
srcId,
colList: colArr
}
try {
const res = await GetNewValue(params)
this.pcsLeftTopData.forEach((el) => {
if (Object.keys(res.data).length !== 0) {
el.value = res.data[el.field].value
if (!self.hnStationId.includes(this.stationId)) {
if (el.field === 'outputPower') {
if (res.data.flowDirection === 2) {
// if (+val > +1) {
// return '放电'
// }
if (+el.value > 1) {
self.isFangdian = true
self.isChongdian = false
} else if (+el.value < -1) {
self.isChongdian = true
self.isFangdian = false
} else {
self.isChongdian = false
self.isFangdian = false
}
} else {
if (+el.value > 1) {
self.isFangdian = false
self.isChongdian = true
} else if (+el.value < -1) {
self.isChongdian = false
self.isFangdian = true
} else {
self.isChongdian = false
self.isFangdian = false
}
}
}
}
}
})
} catch (error) {
// console.log(error)
} finally {
this.left_top_loading = false
}
},
async getLeftBottom(stationId, srcId) {
this.left_bottom_loading = true
const colArr = []
if (stationId === 719 || stationId === 728) {
this.pcsLeftBottomData = pcsLeftBottomData2
} else {
this.pcsLeftBottomData = pcsLeftBottomData1
}
this.pcsLeftBottomData.forEach((el) => {
colArr.push(el.field)
})
const params = {
stationId,
srcId,
colList: colArr
}
try {
const res = await GetNewValue(params)
this.pcsLeftBottomData.forEach((el) => {
if (Object.keys(res.data).length !== 0) {
el.value = res.data[el.field].value
}
})
} catch (error) {
// console.log(error)
} finally {
this.left_bottom_loading = false
}
},
async getCeterTop(stationId, srcId) {
this.right_loding = true
const colArr = []
this.pcsCenterDataStatus.forEach((el) => {
colArr.push(el.field)
})
const params = {
stationId,
srcId,
colList: colArr
}
try {
const res = await GetNewValue(params)
this.pcsCenterDataStatus.forEach((el) => {
if (Object.keys(res.data).length !== 0) {
el.value = res.data[el.field]?.value
this.newPcsCenterDataStatus[el.field] = res.data[el.field]?.value
}
})
// if (this.hnStationId.includes(this.stationId)) {
// 充电拓扑图流向
if (this.newPcsCenterDataStatus.stateCharging) {
this.line = {
top: '50,20,350,20',
center: '200,400,200,20',
rect: '161,239 239,161'
}
}
// 放电拓扑图流向
if (this.newPcsCenterDataStatus.stateDischarging) {
this.line = {
top: '50,20,350,20',
center: '200,20 200,400',
rect: '161,239 239,161'
}
}
// }
} catch (error) {
// console.log(error)
} finally {
this.$forceUpdate()
this.right_loding = false
}
}
}
}
</script>
<style lang="scss" scoped>
.center-top-wrap {
width: 100%;
height: 100%;
.title-right {
display: flex;
align-items: center;
.title-content {
padding: 0 10px;
font-size: 16px;
letter-spacing: 0.2em;
color: #ceebff;
font-family: Source Han Sans CN;
display: flex;
}
}
.center-top-container {
width: 100%;
min-width: 700px;
height: 100%;
display: flex;
align-items: center;
justify-content: space-around;
.left {
width: 25%;
padding-top: 10%;
padding-left: 3%;
align-self: flex-start;
.left-first {
width: 210px;
// width: 80%;
padding: 10px;
padding-bottom: 0;
background: url(../../../../../../assets/images/item-border.png)
no-repeat;
background-size: 100% 100%;
.box-title {
padding-bottom: 10px;
display: flex;
align-items: center;
.title {
// width: 100px;
text-align: left;
white-space: nowrap;
font-family: Source Han Sans CN;
color: #fff;
font-size: 14px;
}
.value {
flex: 1;
text-align: right;
font-family: DIN;
color: #ffb800;
font-size: 14px;
}
.unit {
width: 40px;
padding-left: 5px;
color: #ffff;
font-size: 14px;
}
}
}
}
.right {
width: 30%;
padding-top: 10%;
padding-right: 1%;
align-self: flex-start;
.left-first {
min-width: 217px;
padding: 10px;
padding-bottom: 0;
background: url(../../../../../../assets/images/item-border.png)
no-repeat;
background-size: 100% 100%;
.box-title {
padding-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-around;
.title {
width: 160px;
text-align: left;
color: #fff;
font-size: 14px;
font-family: Source Han Sans CN;
}
.value {
flex: 1;
color: #ffb800;
font-size: 14px;
font-family: DIN;
}
.unit {
width: 40px;
text-align: right;
padding-left: 5px;
color: #ffff;
font-size: 14px;
font-family: DIN;
}
}
.box-title-radio {
display: flex;
padding-bottom: 10px;
width: 100%;
.title-radio {
width: 140px;
text-align: left;
color: #fff;
font-size: 14px;
font-family: Source Han Sans CN;
}
.radio {
// border: 1px solid;
display: flex;
flex-wrap: wrap;
width: 100%;
.radio-box {
display: flex;
flex-wrap: wrap;
.radio-value {
width: 45%;
display: flex;
align-items: center;
margin-right: 5px;
margin-bottom: 5px;
.sq {
display: inline-block;
min-width: 14px;
height: 14px;
background-color: #32aaff;
border-radius: 50%;
border: 1px solid #6d93ac;
}
.sq-transparent {
display: inline-block;
min-width: 14px;
height: 14px;
background-color: transparent;
border-radius: 50%;
border: 1px solid #6d93ac;
}
.sq-value {
font-family: Source Han Sans CN;
padding-left: 3px;
display: flex;
font-size: 14px;
color: #fff;
}
}
}
// /deep/.el-radio{
// width: 45%;
// margin: 0;
// // margin-right: 8px;
// }
// /deep/.el-radio-group{
// display: flex;
// flex-wrap: wrap;
// }
// /deep/ .el-radio__label{
// padding-left: 0;
// }
}
}
}
}
.svg-box {
width: 400px;
height: 450px;
position: relative;
svg {
position: absolute;
top: 0;
}
.circle-load-rect-svg {
width: 100%;
height: 450px;
// margin: 10px;
}
.g-rect-path {
fill: none;
stroke-width: 3;
stroke-linejoin: round;
stroke-linecap: round;
}
/*
$color: 圆点背景颜色
$path: 圆点行走路径
$duration跑完需要多久时间
$delay延迟多久开始跑
*/
@mixin move-round($duration, $delay) {
fill: none;
stroke-width: 5;
stroke-linejoin: round;
stroke-linecap: round;
stroke-dasharray: 3, 900;
stroke-dashoffset: 0;
animation: lineMove $duration $delay cubic-bezier(0, 0, 0.74, 0.74)
infinite;
}
@keyframes lineMove {
0% {
stroke-dashoffset: -850;
}
100% {
stroke-dashoffset: -0;
}
}
.g-rect-fill-two {
@include move-round(3.5s, 1s);
}
.g-rect-fill-two-delay-1 {
@include move-round(3.5s, 1.5s);
}
.g-rect-fill-two-delay-2 {
@include move-round(3.5s, 2s);
}
.g-rect-fill-two-delay-3 {
@include move-round(3.5s, 2.5s);
}
}
}
}
.jl-box {
border: 1px solid;
color: #fff;
position: absolute;
}
.zl-box {
position: absolute;
top: 90px;
left: 100px;
border: 1px solid;
color: #fff;
}
.battary-box {
position: absolute;
bottom: 10px;
left: 45%;
.soc {
position: absolute;
top: 20px;
right: -40px;
font-size: 16px;
color: #fff;
}
}
.container {
position: relative;
width: 40px;
margin: auto;
}
.battery-num {
width: 100%;
position: absolute;
text-align: center;
top: 30px;
-webkit-z-index: 666;
-moz-z-index: 666;
-ms-z-index: 666;
-o-z-index: 666;
z-index: 666;
font-size: 14px;
color: #ffb800;
}
.shandianImg {
position: absolute;
left: 7px;
top: 10px;
z-index: 2;
}
.header {
position: absolute;
width: 16px;
height: 5px;
left: 50%;
top: 0;
transform: translate(-50%, -5px);
border-radius: 5px 5px 0 0;
background: rgba(255, 255, 255, 0.88);
}
.battery-copy {
position: absolute;
top: 0;
left: 0;
height: 60px;
width: 40px;
border-radius: 10px 10px 5px 5px;
overflow: hidden;
}
.battery {
position: relative;
height: 60px;
box-sizing: border-box;
border-radius: 15px 15px 5px 5px;
box-shadow: 0 0 5px 2px rgba(255, 255, 255, 0.22);
background: #fff;
z-index: 1;
&::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 5px;
background: linear-gradient(to bottom, #877925 0%, #877925 100%);
border-radius: 0px 0px 5px 5px;
box-shadow: 0 14px 28px rgba(33, 150, 243, 0),
0 10px 10px rgba(9, 188, 215, 0.08);
// animation: charging 10s linear infinite;
// filter: hue-rotate(90deg);
}
}
.g-wave {
position: absolute;
width: 300px;
height: 300px;
background: rgba(255, 255, 255, 0.8);
border-radius: 45% 47% 44% 42%;
bottom: 55px;
left: 50%;
transform: translate(-50%, 0);
z-index: 1;
animation: move 10s linear infinite;
}
// .g-wave:nth-child(2) {
// border-radius: 38% 46% 43% 47%;
// transform: translate(-50%, 0) rotate(-135deg);
// }
// .g-wave:nth-child(3) {
// border-radius: 42% 46% 37% 40%;
// transform: translate(-50%, 0) rotate(135deg);
// }
@keyframes charging {
50% {
box-shadow: 0 14px 28px rgba(0, 150, 136, 0.83),
0px 4px 10px rgba(9, 188, 215, 0.4);
}
95% {
top: 5%;
filter: hue-rotate(0deg);
border-radius: 0 0 5px 5px;
box-shadow: 0 14px 28px rgba(4, 188, 213, 0.2),
0 10px 10px rgba(9, 188, 215, 0.08);
}
100% {
top: 0%;
filter: hue-rotate(0deg);
border-radius: 15px 15px 5px 5px;
box-shadow: 0 14px 28px rgba(4, 188, 213, 0),
0 10px 10px rgba(9, 188, 215, 0.4);
}
}
@keyframes move {
100% {
transform: translate(-50%, 0px) rotate(720deg);
}
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<div class="right-wrap">
<div class="left-top-wrap">
<ItemBox :title="$t('pcs.faultSign')">
<div slot="title-right" />
<ColListData v-loading="loading" :col-list-data="pcsRightData" :label-key="language === 'en' ? 'englabel' : 'label'" />
</ItemBox>
</div>
</div>
</template>
<script>
import { pcsRightData } from '../config/config'
import { GetPCSAlarm } from '@/api/surveillance/pcs/index'
import ItemBox from '../../item-kuang.vue'
export default {
name: 'Index',
components: { ItemBox },
props: {
stationId: {
type: Number,
default: 0
}
},
data() {
return {
currentIndex: 0,
totalData: {
operationDays: '',
ratePower: '',
totalChargeElec: '',
totalDischargeElec: '',
dailyChargeElec: '',
dailyDischargeElec: '',
currentPower: ''
},
stationData: {
name: '1',
type: '1',
address: '1',
createTime: '1',
longitude: '1',
latitude: '1'
},
stationType: [],
pcsRightData: pcsRightData,
loading: false
}
},
computed: {
language() {
return this.$store.getters.language || undefined
}
},
created() {
},
mounted() {
},
methods: {
async getData(stationId, srcId) {
this.loading = true
const colArr = []
this.pcsRightData.forEach((el) => {
colArr.push(el.field)
})
try {
const params = {
stationId, srcId, colList: colArr
}
const res = await GetPCSAlarm(params)
this.pcsRightData.forEach((el) => {
if (Object.keys(res.data).length !== 0) {
el.value = res.data[el.field].value
}
})
} catch (error) {
// console.log(error)
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.left-top-wrap {
width: 100%;
height: 100%;
.title-right {
.setting-title {
cursor: pointer;
margin-top: 5px;
background: url(../../../../../../assets/images/setting.png) no-repeat;
width: 30px;
height: 30px;
background-size: cover;
}
}
}
</style>

View File

@ -0,0 +1,439 @@
export const pcsRightData = [
{
label: '总报警状态',
englabel: 'PCS Total Alarm Status',
field: 'YX0012',
value: ''
},
{
label: '单元直压故障',
englabel: 'Unit direct pressure failure',
field: 'YX0007',
value: ''
},
{
label: '开关电源欠压',
englabel: 'Switching power supply is undervoltaged',
field: 'YX0010',
value: ''
},
{
label: '过温故障',
englabel: 'Overtemperature failure',
field: 'YX0015',
value: ''
},
{
label: '系统相序错误',
englabel: 'The system phase sequence is incorrect',
field: 'YX0031',
value: ''
},
{
label: '直流极性反接',
englabel: 'DC polarity reversed',
field: 'YX0032',
value: ''
},
{
label: '直流母线软件过压',
englabel: 'DC bus software overvoltage',
field: 'YX0033',
value: ''
},
{
label: '直流母线软件欠压',
englabel: 'DC bus software undervoltage',
field: 'YX0034',
value: ''
},
{
label: '系统过频率',
englabel: 'The frequency of the system is over-frequent',
field: 'YX0035',
value: ''
},
{
label: '系统欠频率',
englabel: 'System underfrequency',
field: 'YX0036',
value: ''
},
{
label: '直流充电过流',
englabel: 'DC charging overcurrent',
field: 'YX0037',
value: ''
},
{
label: '直流放电过流',
englabel: 'DC discharge overcurrent',
field: 'YX0038',
value: ''
},
{
label: '孤岛保护',
englabel: 'Island protection',
field: 'YX0039',
value: ''
},
{
label: '直流主接合闸故障',
englabel: 'DC main connection and closing fault',
field: 'YX0045',
value: ''
},
{
label: '直流主接分闸故障',
englabel: 'DC main connection opening fault',
field: 'YX0046',
value: ''
},
{
label: '直流软启合闸故障',
englabel: 'DC soft start and close fault',
field: 'YX0047',
value: ''
},
{
label: '直流软启分闸故障',
englabel: 'DC soft start opening fault',
field: 'YX0048',
value: ''
},
{
label: '直流软起失败',
englabel: 'DC soft start failed',
field: 'YX0050',
value: ''
},
{
label: '起机条件不满足',
englabel: 'The starting conditions are not met',
field: 'YX0053',
value: ''
},
{
label: '运行中开关故障',
englabel: 'Faulty switch during operation',
field: 'YX0054',
value: ''
},
{
label: '逆变启动超时',
englabel: 'The inverter startup timed out',
field: 'YX0055',
value: ''
},
{
label: '参数下发设置错误',
englabel: 'The parameter delivery setting is incorrect',
field: 'YX0056',
value: ''
},
{
label: '通讯故障',
englabel: 'Communication failures',
field: 'YX0057',
value: ''
},
{
label: '温度异常',
englabel: 'Abnormal temperature',
field: 'YX0058',
value: ''
},
{
label: '跳机',
englabel: 'Jump',
field: 'YX0059',
value: ''
},
{
label: 'DCDC通讯故障',
englabel: 'DCDC communication failure',
field: 'YX0061',
value: ''
},
{
label: 'EMS通讯故障',
englabel: 'EMS communication failure',
field: 'YX0062',
value: ''
},
{
label: '急停故障',
englabel: 'Emergency stop or melt core failure',
field: 'YX0063',
value: ''
},
{
label: '母线不平衡异常',
englabel: 'The bus bar is unbalanced and abnormal',
field: 'YX0068',
value: ''
},
{
label: '母线半直压过压',
englabel: 'The bus bar is semi-directly pressed and overpressed',
field: 'YX0069',
value: ''
},
{
label: '启动超时',
englabel: 'Startup timed out',
field: 'YX0070',
value: ''
}
]
export const pcsLeftTopData = [
{
label: 'BAT电压',
englabel: 'BAT voltage',
field: 'outputPower',
value: '0',
unit: 'V'
},
{
label: 'BAT电流',
field: 'reactivePowerPCS',
englabel: 'BAT current',
value: '0',
unit: 'A'
},
{
label: 'BUS电压',
englabel: 'BUS voltage',
field: 'grid',
value: '0',
unit: 'V'
},
{
label: 'BUS电流',
englabel: 'BUS current',
field: 'volA',
value: '0',
unit: 'A'
}
]
// export const pcsLeftTopData = [
// {
// label: 'Pac',
// field: 'outputPower',
// value: '0',
// unit: 'kW'
// },
// {
// label: 'Qac',
// field: 'reactivePowerPCS',
// value: '0',
// unit: 'kVar'
// },
// {
// label: 'Freq',
// field: 'grid',
// value: '0',
// unit: 'Hz'
// },
// {
// label: 'Uab',
// field: 'volA',
// value: '0',
// unit: 'V'
// },
// {
// label: 'Ubc',
// field: 'volB',
// value: '0',
// unit: 'V'
// },
// {
// label: 'Uca',
// field: 'volC',
// value: '0',
// unit: 'V'
// },
// {
// label: 'Ia',
// field: 'currentA',
// value: '0',
// unit: 'A'
// },
// {
// label: 'Ib',
// field: 'currentB',
// value: '0',
// unit: 'A'
// },
// {
// label: 'Ic',
// field: 'currentC',
// value: '0',
// unit: 'A'
// }
// ]
// export const pcsLeftBottomData = [
// {
// label: 'Udc',
// field: 'dcPower',
// value: '0',
// unit: 'kW'
// },
// {
// label: 'Idc',
// field: 'dcCurrent',
// value: '0',
// unit: 'A'
// },
// {
// label: 'Pdc',
// field: 'dcInputVol',
// value: '0',
// unit: 'V'
// }
// ]
export const pcsLeftBottomData1 = [
{
label: '运行功率',
englabel: 'operating power',
field: 'dcPower',
value: '0',
unit: 'kW'
},
{
label: '直流电压',
englabel: 'DC voltage',
field: 'dcInputVol',
value: '0',
unit: 'V'
},
{
label: '直流电流',
englabel: 'DC current',
field: 'dcCurrent',
value: '0',
unit: 'A'
}
]
export const pcsLeftBottomData2 = [
{
label: '直流功率',
englabel: 'DC power',
field: 'dcPower',
value: '0',
unit: 'kW'
},
{
label: '直流电压',
englabel: 'DC voltage',
field: 'dcInputVol',
value: '0',
unit: 'V'
},
{
label: '直流电流',
englabel: 'DC current',
field: 'dcCurrent',
value: '0',
unit: 'A'
},
{
label: 'pcs模块温度',
englabel: 'PCS module temperature',
field: 'radiatorTemperature',
value: '0',
unit: '℃'
}
]
export const pcsCenterDataStatus = [
{
label: '运行状态',
englabel: 'Run state',
field: 'runState',
value: ''
},
{
label: '远方/就地状态',
englabel: 'Distance/Local State',
field: 'remoteInPlace',
value: ''
},
{
label: '并网',
englabel: 'Grid',
field: 'onGrid',
value: ''
},
{
label: '离网',
englabel: 'Off-grid',
field: 'offGrid',
value: ''
},
{
label: '充电状态',
englabel: 'Charge state',
field: 'stateCharging',
value: ''
},
{
label: '放电状态',
englabel: 'Discharge state',
field: 'stateDischarging',
value: ''
}, {
label: '待机',
englabel: 'Standby',
field: 'deviceStateStand',
value: ''
}, {
label: '故障',
englabel: 'Fault',
field: 'deviceStateFault',
value: ''
},
{
label: '充满',
englabel: 'Full',
field: 'deviceStateFull',
value: ''
},
{
label: '放空',
englabel: 'Empty',
field: 'deviceStateEmpty',
value: ''
},
{
label: '直流断路器',
englabel: 'DC breaker',
field: 'dcBreaker',
value: ''
},
{
label: '紧急停机',
englabel: 'Emergent shutdown',
field: 'eStop',
value: ''
},
{
label: 'SOC',
field: 'soc',
value: ''
}
]

View File

@ -0,0 +1,121 @@
<template>
<div class="pcs-wrap">
<div class="center-wrap">
<div class="center-top">
<PCSCenterTop ref="PCScenterTop" />
</div>
<div class="center-bottom">
<PCSCenterBottom ref="PCScenterBottom" />
</div>
</div>
<div class="right-wrap">
<PCSRight ref="PCSright" class="right-wrap" />
</div>
</div>
</template>
<script>
// PCS
import PCSCenterTop from './components/center-top.vue'
import PCSCenterBottom from './components/center-bottom.vue'
import PCSRight from './components/right.vue'
export default {
components: { PCSCenterTop, PCSCenterBottom, PCSRight },
props: {},
data() {
return {
stationId: undefined
}
},
computed: {
currentStation() {
return this.$store.getters.currentStation || undefined
}
},
watch: {
currentStation: {
handler(val) {
if (val && val.id) {
this.stationId = val.id
// this.$nextTick(() => {
// this.$refs.left.getTreeData(this.stationId)
// this.$refs.left.expandedKeys = []
// })
}
},
deep: true,
immediate: true
}
},
created() { },
mounted() {
// this.getData()
},
methods: {
getData(srcId) {
this.$refs.PCScenterTop.getData(this.stationId, srcId)
this.$refs.PCScenterBottom.getData(this.stationId, srcId)
this.$refs.PCSright.getData(this.stationId, srcId)
}
}
}
</script>
<style lang="scss" scoped>
.pcs-wrap {
width: 100%;
height: 100%;
display: flex;
.center-wrap {
flex: 2.5;
display: flex;
flex-direction: column;
.center-top {
width: 100%;
flex: 2;
padding: 0 20px;
overflow: hidden;
}
.center-bottom {
width: 100%;
flex: 1;
padding: 0 20px;
}
}
}
.right-wrap {
flex: 1;
height: 100%;
display: flex;
min-width: 300px;
flex-direction: column;
.right-top {
width: 100%;
flex: 2;
display: flex;
flex-direction: column;
.right-top-top {
width: 100%;
}
.right-top-bottom {
width: 100%;
flex: 1;
margin-top: 20px;
}
}
.right-bottom {
width: 100%;
flex: 1;
margin-top: 20px;
}
}</style>

View File

@ -18,6 +18,7 @@
// 电池堆
import Left from './components/left.vue'
import PCS from './components/pcs/index.vue'
import DCDCMPPT from './components/dcdcmppt/index.vue'
import RingMainUnit from './components/ring-main-unit/index.vue'
import CLUSTER from './components/cluster/index.vue'
@ -33,7 +34,7 @@ import OPTICALSTORAGE from './components/optical-storage/index.vue'
import DIESELGENERATORS from './components/diesel-generators/index.vue'
import NABATTERY from './components/na-battery/index.vue'
export default {
components: { Left, PCS, LIQUIDCLUSTER, DIESELGENERATORS, RingMainUnit, CLUSTER, STACK, AIRCONDITION, STORAGEFIRE, ElectricityMeter, GROUP, LIQUID, FIRE, OPTICALSTORAGE, NABATTERY },
components: { Left, PCS, DCDCMPPT, LIQUIDCLUSTER, DIESELGENERATORS, RingMainUnit, CLUSTER, STACK, AIRCONDITION, STORAGEFIRE, ElectricityMeter, GROUP, LIQUID, FIRE, OPTICALSTORAGE, NABATTERY },
props: {},
data() {
return {
@ -85,12 +86,19 @@ export default {
methods: {
getDeviceNode(node) {
const self = this
console.log(node.deviceType)
if (node.deviceType) {
if (node.deviceType.includes('pcs') && !this.isBoShi(node.stationId)) {
this.currentComponent = 'PCS'
self.$nextTick(() => {
self.$refs[self.currentComponent].getData(node.srcId)
})
} else if (node.deviceType.includes('mttp') || node.deviceType.includes('mppt')) {
this.currentComponent = 'DCDCMPPT'
self.$nextTick(() => {
self.$refs[self.currentComponent].getData(node.srcId)
})
} else if (node.deviceType.includes('emu')) {
this.currentComponent = 'RingMainUnit'
self.$nextTick(() => {

View File

@ -6,7 +6,7 @@ function resolve(dir) {
return path.join(__dirname, dir)
}
const name = defaultSettings.title || '中自能源' // page title
const name = defaultSettings.title || '中自能源' // page title
// If your port is set to 80,
// use administrator privileges to execute the command line.