Compare commits
51 Commits
b4c49c3e58
...
develop-cl
| Author | SHA1 | Date | |
|---|---|---|---|
| f88e9bedc2 | |||
| 9272a0162a | |||
| c7c1b416ee | |||
| 41a3ab45b3 | |||
| 50c72d6989 | |||
| 7fdb6e2ad3 | |||
| 29ab53056a | |||
| 5250db915f | |||
| 814103c881 | |||
| 55b7fba021 | |||
| da4ecc4792 | |||
| 498dc117f2 | |||
| 4d29de93a1 | |||
| 445e9dfc9f | |||
| ac1d1ae154 | |||
| 825243e741 | |||
| 0389ed85f3 | |||
| aef94f406a | |||
| 95d69fb7b1 | |||
| 684002ffc8 | |||
| 098dfa05f8 | |||
| d6c9310e50 | |||
| 142de3102b | |||
| 9b5806a2c0 | |||
| 2b6697fa5a | |||
| 3bb859b693 | |||
| 5b3701afd0 | |||
| f531075853 | |||
| 8cb6fbee3e | |||
| f454b02c99 | |||
| bfe72cf2c3 | |||
| fb90d81bb3 | |||
| 823c0949d0 | |||
| c1c411e48a | |||
| 43153a791d | |||
| 11111d035b | |||
| ab9bb1e85d | |||
| 4079c40e5d | |||
| 4ebd5f0988 | |||
| a0095b4054 | |||
| 84bc29410a | |||
| f3fda19c64 | |||
| 9a8247f833 | |||
| c4c79aaa64 | |||
| 0966813c25 | |||
| dd4fa36597 | |||
| 9b14d96e24 | |||
| ac54ce999e | |||
| f49efb5edd | |||
| 97f9b3ff0e | |||
| 674a0c6c33 |
File diff suppressed because it is too large
Load Diff
@ -29,4 +29,12 @@ export function getAllBatteryIdsBySites(data) {
|
|||||||
url: `/ems/generalQuery/getAllBatteryIdsBySites/${data}`,
|
url: `/ems/generalQuery/getAllBatteryIdsBySites/${data}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 综合查询-按站点获取配置设备列表
|
||||||
|
export function getGeneralQueryDeviceList(siteId) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/getDeviceList?siteId=${siteId}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -8,11 +8,39 @@ export function getSiteInfoList({siteName, startTime, endTime, pageSize, pageNum
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设备列表
|
// 手动同步站点天气(收益报表)
|
||||||
export function getDeviceInfoList({siteId, pageSize, pageNum}) {
|
export function syncSiteWeatherByDateRange({siteId, startTime, endTime}) {
|
||||||
return request({
|
return request({
|
||||||
url: `/ems/siteConfig/getDeviceInfoList?siteId=${siteId}&pageSize=${pageSize}&pageNum=${pageNum}`,
|
url: `/ems/statsReport/syncWeatherByDateRange`,
|
||||||
method: 'get'
|
method: 'post',
|
||||||
|
params: {siteId, startTime, endTime}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增站点
|
||||||
|
export function addSite(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/addSite`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑站点
|
||||||
|
export function updateSite(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/updateSite`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备列表
|
||||||
|
export function getDeviceInfoList(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/getDeviceInfoList`,
|
||||||
|
method: 'get',
|
||||||
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +86,15 @@ export function deleteService(id) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pcs开、关机
|
||||||
|
export function updateDeviceStatus(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/updateDeviceStatus`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 获取上级设备id列表
|
// 获取上级设备id列表
|
||||||
export function getParentDeviceId({siteId, deviceCategory}) {
|
export function getParentDeviceId({siteId, deviceCategory}) {
|
||||||
return request({
|
return request({
|
||||||
@ -92,6 +129,46 @@ export function getDeviceListBySiteAndCategory({siteId, deviceCategory}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取单站监控项目点位映射
|
||||||
|
export function getSingleMonitorProjectPointMapping(siteId) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/getSingleMonitorProjectPointMapping?siteId=${siteId}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存单站监控项目点位映射
|
||||||
|
export function saveSingleMonitorProjectPointMapping(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/saveSingleMonitorProjectPointMapping`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: {
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取单站监控工作状态枚举映射(PCS)
|
||||||
|
export function getSingleMonitorWorkStatusEnumMappings(siteId) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/getSingleMonitorWorkStatusEnumMappings?siteId=${siteId}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存单站监控工作状态枚举映射(PCS)
|
||||||
|
export function saveSingleMonitorWorkStatusEnumMappings(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/siteConfig/saveSingleMonitorWorkStatusEnumMappings`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: {
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
//新增设备保护
|
//新增设备保护
|
||||||
export function addProtectPlan(data) {
|
export function addProtectPlan(data) {
|
||||||
return request({
|
return request({
|
||||||
@ -153,6 +230,234 @@ export function importPointList(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按站点导入模板点位配置
|
||||||
|
export function importPointTemplateBySite(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/importTemplateBySite`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSV导入点位配置
|
||||||
|
export function importPointConfigCsv(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/importCsv`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点位配置列表
|
||||||
|
export function getPointMatchList(params) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/list`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点位配置详情
|
||||||
|
export function getPointMatchDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增点位配置
|
||||||
|
export function addPointMatch(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑点位配置
|
||||||
|
export function updatePointMatch(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除点位配置
|
||||||
|
export function deletePointMatch(ids) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点位配置-批量获取最新值(新接口)
|
||||||
|
export function getPointConfigLatestValues(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/latestValues`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: {
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点位配置-曲线数据(新接口)
|
||||||
|
export function getPointConfigCurve(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/curve`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: {
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点位配置-生成最近7天数据
|
||||||
|
export function generatePointConfigRecent7Days(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointConfig/generateRecent7Days`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: {
|
||||||
|
repeatSubmit: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算点配置列表
|
||||||
|
export function getPointCalcConfigList(params) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointCalcConfig/list`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算点配置详情
|
||||||
|
export function getPointCalcConfigDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointCalcConfig/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增计算点配置
|
||||||
|
export function addPointCalcConfig(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointCalcConfig`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑计算点配置
|
||||||
|
export function updatePointCalcConfig(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointCalcConfig`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除计算点配置
|
||||||
|
export function deletePointCalcConfig(ids) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/pointCalcConfig/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据修正列表(ems_daily_energy_data)
|
||||||
|
export function getDailyEnergyDataList(params) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyEnergyData/list`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据修正详情
|
||||||
|
export function getDailyEnergyDataDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyEnergyData/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增数据修正
|
||||||
|
export function addDailyEnergyData(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyEnergyData`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑数据修正
|
||||||
|
export function updateDailyEnergyData(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyEnergyData`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除数据修正
|
||||||
|
export function deleteDailyEnergyData(ids) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyEnergyData/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充放电修正列表(ems_daily_charge_data)
|
||||||
|
export function getDailyChargeDataList(params) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyChargeData/list`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充放电修正详情
|
||||||
|
export function getDailyChargeDataDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyChargeData/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增充放电修正
|
||||||
|
export function addDailyChargeData(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyChargeData`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑充放电修正
|
||||||
|
export function updateDailyChargeData(data) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyChargeData`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除充放电修正
|
||||||
|
export function deleteDailyChargeData(ids) {
|
||||||
|
return request({
|
||||||
|
url: `/ems/dailyChargeData/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
//mqtt
|
//mqtt
|
||||||
export function getMqttList({pageSize, pageNum, mqttTopic, topicName, siteId}) {
|
export function getMqttList({pageSize, pageNum, mqttTopic, topicName, siteId}) {
|
||||||
|
|||||||
BIN
src/assets/images/ems/bigData-1.png
Normal file
BIN
src/assets/images/ems/bigData-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
BIN
src/assets/images/ems/bigData-2.png
Normal file
BIN
src/assets/images/ems/bigData-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
BIN
src/assets/images/ems/bigData-3.png
Normal file
BIN
src/assets/images/ems/bigData-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 MiB |
@ -4,58 +4,66 @@
|
|||||||
|
|
||||||
//右侧内容区域
|
//右侧内容区域
|
||||||
//父元素
|
//父元素
|
||||||
.ems-dashboard-editor-container{
|
.ems-dashboard-editor-container {
|
||||||
background-color: #F1F5FC;
|
background-color: #F1F5FC;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
//除去顶部信息(如搜索栏、站点基本信息等)外的 白色背景内容区域
|
//除去顶部信息(如搜索栏、站点基本信息等)外的 白色背景内容区域
|
||||||
.ems-content-container{
|
.ems-content-container {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
//需要设置内padding的白色背景区域
|
//需要设置内padding的白色背景区域
|
||||||
.ems-content-container-padding{
|
.ems-content-container-padding {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
//card通用样式 标题、body
|
//card通用样式 标题、body
|
||||||
.common-card-container{
|
.common-card-container {
|
||||||
.el-card__header{
|
.el-card__header {
|
||||||
padding:14px;
|
padding: 14px;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
background: #F1F5FB ;
|
background: #F1F5FB;
|
||||||
position: relative;
|
position: relative;
|
||||||
.card-title{
|
|
||||||
font-weight: 500;
|
|
||||||
color:#333333;
|
|
||||||
}
|
|
||||||
.el-button--text{
|
|
||||||
color: #666666;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333333;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.common-card-container-body-no-padding{
|
.el-button--text {
|
||||||
.el-card__body{
|
color: #666666;
|
||||||
padding:0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.common-card-container-body-no-padding {
|
||||||
|
.el-card__body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.common-card-container-no-title-bg {
|
.common-card-container-no-title-bg {
|
||||||
.el-card__header{
|
.el-card__header {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//单站监控 设备监控card公共样式
|
//单站监控 设备监控card公共样式
|
||||||
.sbjk-card-container{
|
.sbjk-card-container {
|
||||||
.el-card__header {
|
.el-card__header {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding: 5px 14px;
|
padding: 5px 14px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 5px 5px 0 0;
|
border-radius: 5px 5px 0 0;
|
||||||
.large-title{
|
|
||||||
|
.large-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
@ -63,6 +71,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -70,15 +79,18 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
.el-button--text{
|
|
||||||
|
.el-button--text {
|
||||||
color: #666666;
|
color: #666666;
|
||||||
}
|
}
|
||||||
.alarm{
|
|
||||||
|
.alarm {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 25px;
|
right: 25px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
.alarm-icon{
|
|
||||||
|
.alarm-icon {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: block;
|
display: block;
|
||||||
@ -86,23 +98,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//红色背景颜色标题
|
//红色背景颜色标题
|
||||||
&.warning-card-container{
|
&.warning-card-container {
|
||||||
.el-card__header {
|
.el-card__header {
|
||||||
background-color: #fc6b69;
|
background-color: #b64040; //#fc6b69;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-status {
|
||||||
|
color: #b64040 !important;;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//绿色背景颜色标题
|
//绿色背景颜色标题
|
||||||
&.running-card-container {
|
&.running-card-container {
|
||||||
.el-card__header {
|
.el-card__header {
|
||||||
background-color: #05aea3;
|
background-color: #40b6a5; //#05aea3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-status {
|
||||||
|
color: #40b6a5 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//灰色背景颜色标题
|
//灰色背景颜色标题
|
||||||
&.timing-card-container {
|
&.timing-card-container {
|
||||||
.el-card__header {
|
.el-card__header {
|
||||||
background-color: #666666;
|
background-color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.work-status {
|
||||||
|
color: #666666 !important;;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,11 +138,13 @@
|
|||||||
&.common-card-container .el-card__header {
|
&.common-card-container .el-card__header {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
|
|
||||||
.time-range-header {
|
.time-range-header {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
}
|
}
|
||||||
@ -124,86 +153,100 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//描述样式 PCS、BMS总览、BMS电池簇页面公共样式
|
//描述样式 PCS、BMS总览、BMS电池簇页面公共样式
|
||||||
.descriptions-main{
|
.descriptions-main {
|
||||||
padding:24px;
|
padding: 24px;
|
||||||
position: relative;
|
position: relative;
|
||||||
&.descriptions-main-bg-color{
|
|
||||||
background-color:#f1f5fc ;
|
&.descriptions-main-bg-color {
|
||||||
.el-descriptions__body{
|
background-color: #f1f5fc;
|
||||||
background-color:#f1f5fc ;
|
|
||||||
|
.el-descriptions__body {
|
||||||
|
background-color: #f1f5fc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.el-descriptions-item__cell[colspan='1']{
|
|
||||||
width:25%
|
.el-descriptions-item__cell[colspan='1'] {
|
||||||
|
width: 25%
|
||||||
}
|
}
|
||||||
.el-descriptions__body .el-descriptions__table{
|
|
||||||
.descriptions-direction{
|
.el-descriptions__body .el-descriptions__table {
|
||||||
|
.descriptions-direction {
|
||||||
line-height: 19px;
|
line-height: 19px;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.descriptions-label{
|
|
||||||
|
.descriptions-label {
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
.danger{
|
|
||||||
color:#FC6B69;
|
.danger {
|
||||||
|
color: #FC6B69;
|
||||||
}
|
}
|
||||||
.save{
|
|
||||||
color:#09ADA3;
|
.save {
|
||||||
|
color: #09ADA3;
|
||||||
}
|
}
|
||||||
.keep{
|
|
||||||
color:#3C81FF;
|
.keep {
|
||||||
|
color: #3C81FF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//电表、液冷公共样式
|
//电表、液冷公共样式
|
||||||
.device-info-row{
|
.device-info-row {
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
border-left: 1px solid #eee;
|
border-left: 1px solid #eee;
|
||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
.device-info-col{
|
|
||||||
|
.device-info-col {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size:12px;
|
font-size: 12px;
|
||||||
color:#666666;
|
color: #666666;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
border-right: 1px solid #eee;
|
border-right: 1px solid #eee;
|
||||||
.left{
|
|
||||||
|
.left {
|
||||||
}
|
}
|
||||||
.right{
|
|
||||||
|
.right {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size:16px;
|
font-size: 16px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
margin-top:10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//公共表格样式
|
//公共表格样式
|
||||||
.el-table{
|
.el-table {
|
||||||
font-size:13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
.common-table.el-table{
|
|
||||||
color:#333333;
|
.common-table.el-table {
|
||||||
|
color: #333333;
|
||||||
|
|
||||||
.el-table__header-wrapper th, .el-table__fixed-header-wrapper th {
|
.el-table__header-wrapper th, .el-table__fixed-header-wrapper th {
|
||||||
background: #f1f5fc;
|
background: #f1f5fc;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
|
||||||
.table-head {
|
.table-head {
|
||||||
color: #515a6e;
|
color: #515a6e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.warning-status{
|
|
||||||
color:#FC6B69;
|
.warning-status {
|
||||||
|
color: #FC6B69;
|
||||||
|
|
||||||
&.circle::before {
|
&.circle::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -217,54 +260,61 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//二、三级菜单栏样式
|
//二、三级菜单栏样式
|
||||||
.ems-second-menu{
|
.ems-second-menu {
|
||||||
width:fit-content;
|
width: fit-content;
|
||||||
.el-menu-item{
|
|
||||||
|
.el-menu-item {
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding:0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
&.el-menu--horizontal > .el-menu-item.is-active,&.el-menu--horizontal > .el-menu-item{
|
|
||||||
border-bottom:none!important;
|
&.el-menu--horizontal > .el-menu-item.is-active, &.el-menu--horizontal > .el-menu-item {
|
||||||
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
.el-menu-item.is-active{
|
|
||||||
background: #0366c1!important;
|
.el-menu-item.is-active {
|
||||||
|
background: #0366c1 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ems-third-menu-container{
|
|
||||||
|
.ems-third-menu-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 140px;
|
padding-left: 140px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
.ems-third-menu{
|
|
||||||
|
.ems-third-menu {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top:0;
|
top: 0;
|
||||||
left:0;
|
left: 0;
|
||||||
.el-menu-item{
|
|
||||||
|
.el-menu-item {
|
||||||
line-height: 45px;
|
line-height: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
width: 125px;
|
width: 125px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.el-menu-item:hover{
|
|
||||||
background: #67b1ff!important;
|
.el-menu-item:hover {
|
||||||
color:#ffffff!important;
|
background: #67b1ff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
}
|
}
|
||||||
.el-menu-item.is-active{
|
|
||||||
background: #409eff!important;
|
.el-menu-item.is-active {
|
||||||
|
background: #409eff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//按钮栏 选中样式
|
//按钮栏 选中样式
|
||||||
.ems-btns-group{
|
.ems-btns-group {
|
||||||
.activeBtn{
|
.activeBtn {
|
||||||
background-color: #0366c1;
|
background-color: #0366c1;
|
||||||
border-color: #0366c1;
|
border-color: #0366c1;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
@ -273,11 +323,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//搜索栏样式
|
//搜索栏样式
|
||||||
.select-container.el-form--inline .el-form-item{
|
.select-container.el-form--inline .el-form-item {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
//红色背景颜色按钮
|
//红色背景颜色按钮
|
||||||
.alarm-btn,.alarm-btn:hover, .alarm-btn:focus{
|
.alarm-btn, .alarm-btn:hover, .alarm-btn:focus {
|
||||||
background-color: #FC6B69;
|
background-color: #FC6B69;
|
||||||
border-color: #FC6B69;
|
border-color: #FC6B69;
|
||||||
}
|
}
|
||||||
|
|||||||
137
src/components/BigDataPopup/index.vue
Normal file
137
src/components/BigDataPopup/index.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:fullscreen="true"
|
||||||
|
:append-to-body="true"
|
||||||
|
:visible.sync="show"
|
||||||
|
:show-close="false"
|
||||||
|
top="0"
|
||||||
|
custom-class="big-data-dialog"
|
||||||
|
>
|
||||||
|
<div class="swiper-container">
|
||||||
|
<div class="swiper-icon left-icon" v-show="imgIndex > 0">
|
||||||
|
<i class="el-icon-d-arrow-left icon" @click="toLeft"></i>
|
||||||
|
</div>
|
||||||
|
<div v-show="showRightIcon" class="swiper-icon right-icon">
|
||||||
|
<i class="el-icon-d-arrow-right icon" @click="toRight"></i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="img-container"
|
||||||
|
:style="{ transform: 'translateX(' + imgIndex * -100 + 'vw)' }"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-for="index in maxImgNumber"
|
||||||
|
:key="'swiperImg' + index"
|
||||||
|
:src="require(`@/assets/images/ems/bigData-${index}.png`)"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="close-btn" @click="show = false">
|
||||||
|
<i class="el-icon-close"></i>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 10px;
|
||||||
|
font-size: 23px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: rgba(217, 242, 255, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.swiper-container {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
.swiper-icon {
|
||||||
|
color: rgba(217, 242, 255, 1);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 20;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background: transparent;
|
||||||
|
&.left-icon {
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
&.right-icon {
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
transition: all 0.6s;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.img-container {
|
||||||
|
height: 100%;
|
||||||
|
transition: all 1s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
z-index: 0;
|
||||||
|
img {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.big-data-dialog {
|
||||||
|
.el-dialog__header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
imgIndex: 0,
|
||||||
|
maxImgNumber: 3,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
showRightIcon() {
|
||||||
|
return this.imgIndex < this.maxImgNumber - 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show: {
|
||||||
|
handler(newValue) {
|
||||||
|
if (!newValue) this.imgIndex = 0;
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toLeft() {
|
||||||
|
if (this.imgIndex === 0) return;
|
||||||
|
this.imgIndex -= 1;
|
||||||
|
},
|
||||||
|
toRight() {
|
||||||
|
if (this.imgIndex >= this.maxImgNumber - 1) return;
|
||||||
|
this.imgIndex += 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -1,36 +1,68 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="time-range">
|
<div class="time-range">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="dateRange"
|
v-model="dateRange"
|
||||||
type="daterange"
|
:class="miniTimePicker ? 'mini-date-picker' : ''"
|
||||||
range-separator="至"
|
type="daterange"
|
||||||
start-placeholder="开始时间"
|
range-separator="至"
|
||||||
value-format="yyyy-MM-dd"
|
start-placeholder="开始时间"
|
||||||
:clearable="false"
|
value-format="yyyy-MM-dd"
|
||||||
:picker-options="pickerOptions"
|
:clearable="false"
|
||||||
end-placeholder="结束时间">
|
:picker-options="pickerOptions"
|
||||||
</el-date-picker>
|
end-placeholder="结束时间">
|
||||||
<el-button size="mini" style="margin-left: 10px;" :loading="loading" @click="reset">重置</el-button>
|
</el-date-picker>
|
||||||
<el-button type="primary" size="mini" :loading="loading" @click="search">搜索</el-button>
|
<template v-if="!showIcon">
|
||||||
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('before')">上一时段</el-button>
|
<el-button size="mini" style="margin-left: 10px;" :loading="loading" @click="reset">重置</el-button>
|
||||||
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('next')" :disabled="disabledNextBtn">下一时段</el-button>
|
<el-button type="primary" size="mini" :loading="loading" @click="search">搜索</el-button>
|
||||||
</div>
|
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('before')">上一时段</el-button>
|
||||||
|
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('next')" :disabled="disabledNextBtn">
|
||||||
|
下一时段
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-button class="btn-icon" icon="el-icon-refresh-right" circle size="mini" style="margin-left: 8px;"
|
||||||
|
:loading="loading"
|
||||||
|
@click="reset"></el-button>
|
||||||
|
<el-button class="btn-icon" type="primary" size="mini" icon="el-icon-search" circle :loading="loading"
|
||||||
|
@click="search"></el-button>
|
||||||
|
<el-button class="btn-icon" type="primary" size="mini" icon="el-icon-d-arrow-left" circle :loading="loading"
|
||||||
|
@click="timeLine('before')"></el-button>
|
||||||
|
<el-button class="btn-icon" type="primary" size="mini" icon="el-icon-d-arrow-right" circle :loading="loading"
|
||||||
|
@click="timeLine('next')"
|
||||||
|
:disabled="disabledNextBtn"></el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {formatDate} from '@/filters/ems'
|
import {formatDate} from '@/filters/ems'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed:{
|
props: {
|
||||||
disabledNextBtn(){
|
showIcon: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
miniTimePicker: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
disabledNextBtn() {
|
||||||
return new Date(this.dateRange[1]) >= new Date(this.defaultDateRange[1])
|
return new Date(this.dateRange[1]) >= new Date(this.defaultDateRange[1])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
dateRange:[],
|
dateRange: [],
|
||||||
defaultDateRange:[],
|
defaultDateRange: [],
|
||||||
pickerOptions:{
|
pickerOptions: {
|
||||||
disabledDate(time) {
|
disabledDate(time) {
|
||||||
return time.getTime() > Date.now();
|
return time.getTime() > Date.now();
|
||||||
},
|
},
|
||||||
@ -38,64 +70,81 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init(today=false){
|
init(today = false) {
|
||||||
const now = new Date(),formatNow = formatDate(now);
|
const now = new Date(), formatNow = formatDate(now);
|
||||||
const weekAgo = formatDate(today ? new Date(now.getTime()) : new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000))
|
const weekAgo = formatDate(today ? new Date(now.getTime()) : new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000))
|
||||||
this.dateRange = [weekAgo, formatNow];
|
this.dateRange = [weekAgo, formatNow];
|
||||||
this.defaultDateRange=[weekAgo, formatNow];
|
this.defaultDateRange = [weekAgo, formatNow];
|
||||||
this.$emit('updateDate',this.dateRange)
|
this.$emit('updateDate', this.dateRange)
|
||||||
},
|
},
|
||||||
showBtnLoading(status){
|
showBtnLoading(status) {
|
||||||
this.loading = status
|
this.loading = status
|
||||||
},
|
},
|
||||||
resetDate(){
|
resetDate() {
|
||||||
this.dateRange = this.defaultDateRange
|
this.dateRange = this.defaultDateRange
|
||||||
},
|
},
|
||||||
//重置 设置时间范围为初始化时间段
|
//重置 设置时间范围为初始化时间段
|
||||||
reset(){
|
reset() {
|
||||||
this.resetDate()
|
this.resetDate()
|
||||||
this.$emit('reset')
|
this.$emit('reset')
|
||||||
this.$emit('updateDate',this.dateRange)
|
this.$emit('updateDate', this.dateRange)
|
||||||
},
|
},
|
||||||
// 搜索
|
// 搜索
|
||||||
search(){
|
search() {
|
||||||
this.$emit('updateDate',this.dateRange)
|
this.$emit('updateDate', this.dateRange)
|
||||||
},
|
},
|
||||||
timeLine(type){
|
timeLine(type) {
|
||||||
if(!this.dateRange || !this.dateRange[0] || !this.dateRange[1]) return
|
if (!this.dateRange || !this.dateRange[0] || !this.dateRange[1]) return
|
||||||
const nowStartTimes = new Date(this.dateRange[0]).getTime(),nowEndTimes = new Date(this.dateRange[1]).getTime(),maxTime = new Date(this.defaultDateRange[1]).getTime()
|
const nowStartTimes = new Date(this.dateRange[0]).getTime(), nowEndTimes = new Date(this.dateRange[1]).getTime(),
|
||||||
|
maxTime = new Date(this.defaultDateRange[1]).getTime()
|
||||||
const nowDis = nowEndTimes - nowStartTimes//用户当前选择时间差 可能=0
|
const nowDis = nowEndTimes - nowStartTimes//用户当前选择时间差 可能=0
|
||||||
//baseTime,maxTime 毫秒数
|
//baseTime,maxTime 毫秒数
|
||||||
const baseDis = 24 * 60 * 60 * 1000
|
const baseDis = 24 * 60 * 60 * 1000
|
||||||
const calcDis = nowDis === 0 ? baseDis : nowDis
|
const calcDis = nowDis === 0 ? baseDis : nowDis
|
||||||
let start = type === 'before' ? nowStartTimes - calcDis : nowStartTimes + calcDis
|
let start = type === 'before' ? nowStartTimes - calcDis : nowStartTimes + calcDis
|
||||||
if(start>maxTime) start=maxTime
|
if (start > maxTime) start = maxTime
|
||||||
let end = type === 'before' ? nowEndTimes - calcDis : nowEndTimes + calcDis
|
let end = type === 'before' ? nowEndTimes - calcDis : nowEndTimes + calcDis
|
||||||
if(end>maxTime) end=maxTime
|
if (end > maxTime) end = maxTime
|
||||||
this.dateRange = [formatDate(start),formatDate(end)]
|
this.dateRange = [formatDate(start), formatDate(end)]
|
||||||
this.$emit('updateDate',this.dateRange)
|
this.$emit('updateDate', this.dateRange)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.time-range{
|
.time-range {
|
||||||
display: flex;
|
display: flex;
|
||||||
::v-deep {
|
|
||||||
.el-range-editor--medium .el-range__icon, .el-range-editor--medium .el-range__close-icon{
|
::v-deep {
|
||||||
line-height: 22px;
|
|
||||||
}
|
.el-range-editor--medium .el-range__icon, .el-range-editor--medium .el-range__close-icon {
|
||||||
.el-range-editor--medium.el-input__inner{
|
line-height: 22px;
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
.el-range-editor--medium .el-range-separator{
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
.el-button--mini{
|
|
||||||
padding:3px 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-range-editor--medium.el-input__inner {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-range-editor--medium .el-range-separator {
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--mini {
|
||||||
|
padding: 3px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 展示icon的小组件
|
||||||
|
.btn-icon.el-button--mini {
|
||||||
|
padding: 3px 8px;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
//小宽度时间选择框
|
||||||
|
.mini-date-picker {
|
||||||
|
width: 250px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card shadow="always" class="single-square-box" :style="{background: 'linear-gradient(180deg, '+data.bgColor+' 0%,rgba(255,255,255,0) 100%)'}">
|
<el-card shadow="always" class="single-square-box" :style="{background: 'linear-gradient(180deg, '+data.bgColor+' 0%,rgba(255,255,255,0) 100%)'}">
|
||||||
<div class="single-square-box-title">{{ data.title }}</div>
|
<div class="single-square-box-title">{{ data.title }}</div>
|
||||||
<div class="single-square-box-value">{{ data.value | formatNumber }}</div>
|
<div class="single-square-box-value">
|
||||||
|
<i v-if="data.loading" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ data.value | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,18 +17,29 @@
|
|||||||
color:#666666;
|
color:#666666;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
.single-square-box-title{
|
.single-square-box-title{
|
||||||
font-size: 12px;
|
font-size: 10px;
|
||||||
line-height: 12px;
|
line-height: 10px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
.single-square-box-value{
|
.single-square-box-value{
|
||||||
font-size: 26px;
|
font-size: 18px;
|
||||||
line-height: 26px;
|
line-height: 18px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
::v-deep .el-card__body{
|
.point-loading-icon{
|
||||||
padding: 12px 10px;
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
}
|
}
|
||||||
|
::v-deep .el-card__body{
|
||||||
|
padding: 8px 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -4,7 +4,10 @@
|
|||||||
<el-row type="flex" >
|
<el-row type="flex" >
|
||||||
<el-card shadow="hover" class="card common-card-container-body-no-padding" v-for="(item,index) in data" :key="index+'zdInfo'" :style="{borderBottomColor:item.color}">
|
<el-card shadow="hover" class="card common-card-container-body-no-padding" v-for="(item,index) in data" :key="index+'zdInfo'" :style="{borderBottomColor:item.color}">
|
||||||
<div class="info">{{ item.title }}</div>
|
<div class="info">{{ item.title }}</div>
|
||||||
<div class="num">{{item.num | formatNumber}}</div>
|
<div class="num">
|
||||||
|
<i v-if="item.loading" class="el-icon-loading"></i>
|
||||||
|
<span v-else>{{item.num | formatNumber}}</span>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
@ -18,30 +21,35 @@ export default {
|
|||||||
title:'站点总数(座)',
|
title:'站点总数(座)',
|
||||||
num:'',
|
num:'',
|
||||||
color:'#FFBD00',
|
color:'#FFBD00',
|
||||||
attr:'siteNum'
|
attr:'siteNum',
|
||||||
|
loading: true
|
||||||
},{
|
},{
|
||||||
title:'装机功率(MW)',
|
title:'装机功率(MW)',
|
||||||
num:'',
|
num:'',
|
||||||
color:'#3C81FF',
|
color:'#3C81FF',
|
||||||
attr:'installPower'
|
attr:'installPower',
|
||||||
|
loading: true
|
||||||
|
|
||||||
},{
|
},{
|
||||||
title:'装机容量(MW)',
|
title:'装机容量(MW)',
|
||||||
num:'',
|
num:'',
|
||||||
color:'#5AC7C0',
|
color:'#5AC7C0',
|
||||||
attr:'installCapacity'
|
attr:'installCapacity',
|
||||||
|
loading: true
|
||||||
|
|
||||||
},{
|
},{
|
||||||
title:'总充电量(MWh)',
|
title:'总充电量(KWh)',
|
||||||
num:'',
|
num:'',
|
||||||
color:'#A696FF',
|
color:'#A696FF',
|
||||||
attr:'totalChargedCap'
|
attr:'totalChargedCap',
|
||||||
|
loading: true
|
||||||
|
|
||||||
},{
|
},{
|
||||||
title:'总放电量(MWh)',
|
title:'总放电量(KWh)',
|
||||||
num:'',
|
num:'',
|
||||||
color:'#A696FF',
|
color:'#A696FF',
|
||||||
attr:'totalDischargedCap'
|
attr:'totalDischargedCap',
|
||||||
|
loading: true
|
||||||
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@ -50,6 +58,7 @@ export default {
|
|||||||
setData(res = {}){
|
setData(res = {}){
|
||||||
this.data.forEach((item)=>{
|
this.data.forEach((item)=>{
|
||||||
item.num =res[item.attr]
|
item.num =res[item.attr]
|
||||||
|
item.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,8 +2,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="zd-select-container">
|
<div class="zd-select-container">
|
||||||
<el-form :inline="true">
|
<el-form :inline="true">
|
||||||
<el-form-item label="站点选择">
|
<el-form-item :label="showLabel ? '站点选择' : ''" :class="{'no-label': !showLabel}">
|
||||||
<el-select v-model="id" placeholder="请选择换电站名称" :loading="loading" loading-text="正在加载数据" @change="onSubmit">
|
<el-select
|
||||||
|
v-model="id"
|
||||||
|
:size="size"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:loading="loading"
|
||||||
|
loading-text="正在加载数据"
|
||||||
|
:style="{width: selectWidth}"
|
||||||
|
@change="onSubmit"
|
||||||
|
>
|
||||||
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
|
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -15,7 +23,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.zd-select-container {
|
||||||
|
.el-form {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.no-label ::v-deep .el-form-item__label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.no-label ::v-deep .el-form-item__content {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
import {getAllSites} from '@/api/ems/zddt'
|
||||||
@ -31,6 +53,26 @@ import {mapGetters} from "vuex"
|
|||||||
type:String,
|
type:String,
|
||||||
default:'',
|
default:'',
|
||||||
required:false
|
required:false
|
||||||
|
},
|
||||||
|
showLabel: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'medium',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择换电站名称',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
selectWidth: {
|
||||||
|
type: String,
|
||||||
|
default: '220px',
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -44,10 +86,23 @@ import {mapGetters} from "vuex"
|
|||||||
computed:{
|
computed:{
|
||||||
...mapGetters(["zdList"]),
|
...mapGetters(["zdList"]),
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
defaultSiteId(newVal) {
|
||||||
|
if (!newVal || !this.siteList || this.siteList.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.siteList.find(item => item.siteId === newVal) && this.id !== newVal) {
|
||||||
|
this.id = newVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods:{
|
methods:{
|
||||||
onSubmit(){
|
onSubmit(){
|
||||||
this.$emit('submitSite',this.id)
|
this.$emit('submitSite',this.id)
|
||||||
},
|
},
|
||||||
|
emitSitesLoaded() {
|
||||||
|
this.$emit('sitesLoaded', this.siteList || [])
|
||||||
|
},
|
||||||
setDefaultSite(){
|
setDefaultSite(){
|
||||||
const defaultSite = this.defaultSiteId
|
const defaultSite = this.defaultSiteId
|
||||||
if(defaultSite && this.siteList.find(item=>item.siteId === defaultSite)){
|
if(defaultSite && this.siteList.find(item=>item.siteId === defaultSite)){
|
||||||
@ -60,7 +115,7 @@ import {mapGetters} from "vuex"
|
|||||||
getList(){
|
getList(){
|
||||||
return getAllSites().then(response => {
|
return getAllSites().then(response => {
|
||||||
this.siteList = response.data || []
|
this.siteList = response.data || []
|
||||||
console.log("获取站点列表返回数据",response,this.siteList)
|
this.emitSitesLoaded()
|
||||||
this.setDefaultSite()
|
this.setDefaultSite()
|
||||||
}).finally(() => {this.loading=false;this.searchLoading=false})
|
}).finally(() => {this.loading=false;this.searchLoading=false})
|
||||||
}
|
}
|
||||||
@ -71,15 +126,14 @@ import {mapGetters} from "vuex"
|
|||||||
this.$nextTick(()=>{
|
this.$nextTick(()=>{
|
||||||
if(this.getListByStore){
|
if(this.getListByStore){
|
||||||
if(this.zdList.length === 0){
|
if(this.zdList.length === 0){
|
||||||
this.getList().then(()=>{
|
this.getList().then(()=>{
|
||||||
this.$store.commit('SET_ZD_LIST', this.siteList)
|
this.$store.commit('SET_ZD_LIST', this.siteList)
|
||||||
console.log("从store中获取站点列表数据,但是store中的zdList=[],所以从接口获取数据",this.zdList,this.siteList)
|
|
||||||
})
|
})
|
||||||
}else{
|
}else{
|
||||||
this.siteList = this.zdList
|
this.siteList = this.zdList
|
||||||
|
this.emitSitesLoaded()
|
||||||
this.loading=false
|
this.loading=false
|
||||||
this.searchLoading=false
|
this.searchLoading=false
|
||||||
console.log("从store中获取站点列表数据",this.zdList,this.siteList)
|
|
||||||
this.setDefaultSite()
|
this.setDefaultSite()
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@ -1,22 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container"
|
||||||
|
@toggleClick="toggleSideBar"/>
|
||||||
|
|
||||||
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" />
|
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container"/>
|
||||||
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" />
|
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container"/>
|
||||||
|
|
||||||
<div class="right-menu">
|
<div class="right-menu">
|
||||||
<template v-if="device!=='mobile'">
|
<template v-if="device!=='mobile'">
|
||||||
<search id="header-search" class="right-menu-item" />
|
<div class="big-data-container">
|
||||||
|
<i class="el-icon-s-marketing big-data-icon" @click.stop="showBigDataImg"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<search id="header-search" class="right-menu-item"/>
|
||||||
|
|
||||||
|
|
||||||
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
<screenfull id="screenfull" class="right-menu-item hover-effect"/>
|
||||||
|
|
||||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
<size-select id="size-select" class="right-menu-item hover-effect"/>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
<div v-if="device!=='mobile'" class="site-select-wrap">
|
||||||
|
<zd-select
|
||||||
|
:get-list-by-store="true"
|
||||||
|
:show-label="false"
|
||||||
|
size="mini"
|
||||||
|
select-width="220px"
|
||||||
|
:default-site-id="$route.query.siteId"
|
||||||
|
@submitSite="onSiteChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover">
|
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover">
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
@ -32,22 +47,24 @@
|
|||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
<div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting">
|
<div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting">
|
||||||
<svg-icon icon-class="more-up" />
|
<svg-icon icon-class="more-up"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<BigDataPopup ref="bigDataPopup"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import {mapGetters} from 'vuex'
|
||||||
import Breadcrumb from '@/components/Breadcrumb'
|
import Breadcrumb from '@/components/Breadcrumb'
|
||||||
import TopNav from '@/components/TopNav'
|
import TopNav from '@/components/TopNav'
|
||||||
import Hamburger from '@/components/Hamburger'
|
import Hamburger from '@/components/Hamburger'
|
||||||
import Screenfull from '@/components/Screenfull'
|
import Screenfull from '@/components/Screenfull'
|
||||||
import SizeSelect from '@/components/SizeSelect'
|
import SizeSelect from '@/components/SizeSelect'
|
||||||
import Search from '@/components/HeaderSearch'
|
import Search from '@/components/HeaderSearch'
|
||||||
|
import BigDataPopup from '@/components/BigDataPopup'
|
||||||
|
import ZdSelect from '@/components/Ems/ZdSelect/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['setLayout'],
|
emits: ['setLayout'],
|
||||||
@ -57,7 +74,9 @@ export default {
|
|||||||
Hamburger,
|
Hamburger,
|
||||||
Screenfull,
|
Screenfull,
|
||||||
SizeSelect,
|
SizeSelect,
|
||||||
Search
|
Search,
|
||||||
|
BigDataPopup,
|
||||||
|
ZdSelect
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
@ -78,6 +97,28 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onSiteChange(id) {
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const currentSite = (this.$store.getters.zdList || []).find(item => item.siteId === id)
|
||||||
|
const siteName = currentSite && currentSite.siteName ? currentSite.siteName : ''
|
||||||
|
localStorage.setItem('global_site_id', id)
|
||||||
|
if (id !== this.$route.query.siteId || siteName !== (this.$route.query.siteName || '')) {
|
||||||
|
this.$router.push({
|
||||||
|
path: this.$route.path,
|
||||||
|
query: {
|
||||||
|
...this.$route.query,
|
||||||
|
siteId: id,
|
||||||
|
siteName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$store.dispatch('getSiteAlarmNum', id)
|
||||||
|
},
|
||||||
|
showBigDataImg() {
|
||||||
|
this.$refs.bigDataPopup.show = true
|
||||||
|
},
|
||||||
toggleSideBar() {
|
toggleSideBar() {
|
||||||
this.$store.dispatch('app/toggleSideBar')
|
this.$store.dispatch('app/toggleSideBar')
|
||||||
},
|
},
|
||||||
@ -93,7 +134,8 @@ export default {
|
|||||||
this.$store.dispatch('LogOut').then(() => {
|
this.$store.dispatch('LogOut').then(() => {
|
||||||
location.href = '/index'
|
location.href = '/index'
|
||||||
})
|
})
|
||||||
}).catch(() => {})
|
}).catch(() => {
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +147,7 @@ export default {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||||
|
|
||||||
.hamburger-container {
|
.hamburger-container {
|
||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
@ -113,7 +155,7 @@ export default {
|
|||||||
float: left;
|
float: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background .3s;
|
transition: background .3s;
|
||||||
-webkit-tap-highlight-color:transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, .025)
|
background: rgba(0, 0, 0, .025)
|
||||||
@ -139,6 +181,33 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
|
|
||||||
|
.site-select-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 10px 0 14px;
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
::v-deep .el-form-item__content {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-input__inner {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-data-container {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #5a5e66;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
@ -177,7 +246,7 @@ export default {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-nickname{
|
.user-nickname {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -193,6 +262,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -10,11 +10,16 @@ import { isRelogin } from '@/utils/request'
|
|||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
|
|
||||||
const whiteList = ['/login', '/register']
|
const whiteList = ['/login', '/register']
|
||||||
|
const GLOBAL_SITE_STORAGE_KEY = 'global_site_id'
|
||||||
|
|
||||||
const isWhiteList = (path) => {
|
const isWhiteList = (path) => {
|
||||||
return whiteList.some(pattern => isPathMatch(pattern, path))
|
return whiteList.some(pattern => isPathMatch(pattern, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shouldAppendSiteId = (path) => {
|
||||||
|
return !['/login', '/register', '/404', '/401'].includes(path) && !path.startsWith('/redirect')
|
||||||
|
}
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
if (getToken()) {
|
if (getToken()) {
|
||||||
@ -26,6 +31,24 @@ router.beforeEach((to, from, next) => {
|
|||||||
} else if (isWhiteList(to.path)) {
|
} else if (isWhiteList(to.path)) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
|
const routeSiteId = to.query?.siteId
|
||||||
|
if (routeSiteId) {
|
||||||
|
localStorage.setItem(GLOBAL_SITE_STORAGE_KEY, routeSiteId)
|
||||||
|
} else {
|
||||||
|
const globalSiteId = localStorage.getItem(GLOBAL_SITE_STORAGE_KEY)
|
||||||
|
if (globalSiteId && shouldAppendSiteId(to.path)) {
|
||||||
|
next({
|
||||||
|
path: to.path,
|
||||||
|
query: {
|
||||||
|
...to.query,
|
||||||
|
siteId: globalSiteId
|
||||||
|
},
|
||||||
|
hash: to.hash,
|
||||||
|
replace: true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if (store.getters.roles.length === 0) {
|
if (store.getters.roles.length === 0) {
|
||||||
isRelogin.show = true
|
isRelogin.show = true
|
||||||
// 判断当前用户是否已拉取完user_info信息
|
// 判断当前用户是否已拉取完user_info信息
|
||||||
|
|||||||
@ -2,183 +2,323 @@ import Layout from "@/layout/index.vue";
|
|||||||
|
|
||||||
//单站监控
|
//单站监控
|
||||||
// todo 本地设置了 hidden:true,不会显示在侧边栏,需要在系统管理、菜单管理中手动添加菜单后才会展示在侧边栏
|
// todo 本地设置了 hidden:true,不会显示在侧边栏,需要在系统管理、菜单管理中手动添加菜单后才会展示在侧边栏
|
||||||
export const dzjk=[
|
export const dzjk = [
|
||||||
{
|
{
|
||||||
path: '/dzjk',
|
path: '/dzjk',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/dzjk/home',
|
|
||||||
meta: { title: '单站监控', icon: 'dashboard',},
|
|
||||||
alwaysShow: false,
|
|
||||||
name:'Dzjk',
|
|
||||||
hidden:true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: () => import('@/views/ems/dzjk/index'),
|
|
||||||
name: 'Dzjk',
|
|
||||||
redirect: '/dzjk/home',
|
redirect: '/dzjk/home',
|
||||||
|
meta: {title: '单站监控', icon: 'dashboard',},
|
||||||
|
alwaysShow: false,
|
||||||
|
name: 'DzjkLocal',
|
||||||
hidden: true,
|
hidden: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/dzjk/home',
|
path: '',
|
||||||
component: () => import('@/views/ems/dzjk/home/index.vue'),
|
component: () => import('@/views/ems/dzjk/index'),
|
||||||
name: 'DzjkHome',
|
name: 'DzjkRoot',
|
||||||
meta: { title: '站点首页',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkHome' }
|
redirect: '/dzjk/home',
|
||||||
},
|
hidden: true,
|
||||||
{
|
children: [
|
||||||
path: '/dzjk/zxlt',
|
{
|
||||||
component: () => import('@/views/ems/dzjk/zxlt/index.vue'),
|
path: '/dzjk/home',
|
||||||
name: 'DzjkZxlt',
|
component: () => import('@/views/ems/dzjk/home/index.vue'),
|
||||||
meta: { title: '主线路图',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkZxlt' }
|
name: 'DzjkHome',
|
||||||
},
|
meta: {
|
||||||
{
|
title: '站点首页',
|
||||||
path: '/dzjk/sbjk',
|
breadcrumb: false,
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/index.vue'),
|
activeMenu: '/dzjk',
|
||||||
name: 'DzjkSbjk',
|
activeSecondMenuName: 'DzjkHome'
|
||||||
meta: { title: '设备监控',breadcrumb: false,activeMenu: '/dzjk'},
|
}
|
||||||
redirect: '/dzjk/sbjk/ssyx',
|
},
|
||||||
children: [
|
{
|
||||||
{
|
path: '/dzjk/zxlt',
|
||||||
path: 'ssyx',
|
component: () => import('@/views/ems/dzjk/zxlt/index.vue'),
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/ssyx/index.vue'),
|
name: 'DzjkZxlt',
|
||||||
name: 'DzjkSbjkSsyx',
|
meta: {
|
||||||
meta: { title: '实时运行',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'SSYX'},
|
title: '主线路图',
|
||||||
},
|
breadcrumb: false,
|
||||||
{
|
activeMenu: '/dzjk',
|
||||||
path: 'pcs',
|
activeSecondMenuName: 'DzjkZxlt'
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/pcs/index.vue'),
|
}
|
||||||
name: 'DzjkSbjkPcs',
|
},
|
||||||
meta: { title: 'PCS',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'PCS'},
|
{
|
||||||
},
|
path: '/dzjk/sbjk',
|
||||||
{
|
component: () => import('@/views/ems/dzjk/sbjk/index.vue'),
|
||||||
path: 'bmszl',
|
name: 'DzjkSbjk',
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/bmszl/index.vue'),
|
meta: {title: '设备监控', breadcrumb: false, activeMenu: '/dzjk'},
|
||||||
name: 'DzjkSbjkBmszl',
|
redirect: '/dzjk/sbjk/ssyx',
|
||||||
meta: { title: 'BMS总览',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk', deviceCategory:'STACK'},
|
children: [
|
||||||
},
|
{
|
||||||
{
|
path: 'ssyx',
|
||||||
path: 'bmsdcc',
|
component: () => import('@/views/ems/dzjk/sbjk/ssyx/index.vue'),
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/bmsdcc/index.vue'),
|
name: 'DzjkSbjkSsyx',
|
||||||
name: 'DzjkSbjkBmsdcc',
|
meta: {
|
||||||
meta: { title: 'BMS电池簇',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'CLUSTER'},
|
title: '实时运行',
|
||||||
},
|
breadcrumb: false,
|
||||||
{
|
activeMenu: '/dzjk',
|
||||||
path: 'dtdc',
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/dtdc/index.vue'),
|
deviceCategory: 'SSYX'
|
||||||
name: 'DzjkSbjkDtdc',
|
},
|
||||||
meta: { title: '单体电池',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'BATTERY'},
|
},
|
||||||
},
|
{
|
||||||
{
|
path: 'ems',
|
||||||
path: 'db',
|
component: () => import('@/views/ems/dzjk/sbjk/ems/index.vue'),
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/db/index.vue'),
|
name: 'DzjkSbjkEms',
|
||||||
name: 'DzjkSbjkDb',
|
meta: {
|
||||||
meta: { title: '电表',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'AMMETER'},
|
title: 'EMS',
|
||||||
},
|
breadcrumb: false,
|
||||||
{
|
activeMenu: '/dzjk',
|
||||||
path: 'yl',
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/yl/index.vue'),
|
deviceCategory: 'EMS'
|
||||||
name: 'DzjkSbjkYl',
|
},
|
||||||
meta: { title: '液冷',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'COOLING'},
|
},
|
||||||
},
|
{
|
||||||
{
|
path: 'pcs',
|
||||||
path: 'dh',
|
component: () => import('@/views/ems/dzjk/sbjk/pcs/index.vue'),
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/dh/index.vue'),
|
name: 'DzjkSbjkPcs',
|
||||||
name: 'DzjkSbjkDh',
|
meta: {
|
||||||
meta: { title: '动环',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'DH'},
|
title: 'PCS',
|
||||||
},
|
breadcrumb: false,
|
||||||
{
|
activeMenu: '/dzjk',
|
||||||
path: 'xf',
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
component: () => import('@/views/ems/dzjk/sbjk/xf/index.vue'),
|
deviceCategory: 'PCS'
|
||||||
name: 'DzjkSbjkXf',
|
},
|
||||||
meta: { title: '消防',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk',deviceCategory:'XF'},
|
},
|
||||||
}
|
{
|
||||||
]
|
path: 'bmszl',
|
||||||
},
|
component: () => import('@/views/ems/dzjk/sbjk/bmszl/index.vue'),
|
||||||
{
|
name: 'DzjkSbjkBmszl',
|
||||||
path: '/dzjk/gzgj',
|
meta: {
|
||||||
component: () => import('@/views/ems/dzjk/gzgj/index.vue'),
|
title: 'BMS总览',
|
||||||
name: 'DzjkGzgj',
|
breadcrumb: false,
|
||||||
meta: { title: '故障告警',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkGzgj' }
|
activeMenu: '/dzjk',
|
||||||
},
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
{
|
deviceCategory: 'STACK'
|
||||||
path: '/dzjk/tjbb',
|
},
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/index.vue'),
|
},
|
||||||
name: 'DzjkTjbb',
|
{
|
||||||
meta: {title: '统计报表', breadcrumb: false, activeMenu: '/dzjk'},
|
path: 'bmsdcc',
|
||||||
redirect: '/dzjk/tjbb/gltj',
|
component: () => import('@/views/ems/dzjk/sbjk/bmsdcc/index.vue'),
|
||||||
children: [
|
name: 'DzjkSbjkBmsdcc',
|
||||||
{
|
meta: {
|
||||||
path: 'gltj',
|
title: 'BMS电池簇',
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/gltj/index.vue'),
|
breadcrumb: false,
|
||||||
name: 'DzjkTjbbGltj',
|
activeMenu: '/dzjk',
|
||||||
meta: { title: '概率统计',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
},
|
deviceCategory: 'CLUSTER'
|
||||||
{
|
},
|
||||||
path: 'glqx',
|
},
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/glqx/index.vue'),
|
{
|
||||||
name: 'DzjkTjbbGlqx',
|
path: 'dtdc',
|
||||||
meta: { title: '功率曲线',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
component: () => import('@/views/ems/dzjk/sbjk/dtdc/index.vue'),
|
||||||
},
|
name: 'DzjkSbjkDtdc',
|
||||||
{
|
meta: {
|
||||||
path: 'pcsqx',
|
title: 'BMS单体电池',
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/pcsqx/index.vue'),
|
breadcrumb: false,
|
||||||
name: 'DzjkTjbbPcsqx',
|
activeMenu: '/dzjk',
|
||||||
meta: { title: 'PCS曲线',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
},
|
deviceCategory: 'BATTERY'
|
||||||
{
|
},
|
||||||
path: 'dcdqx',
|
},
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/dcdqx/index.vue'),
|
{
|
||||||
name: 'DzjkTjbbDcdqx',
|
path: 'db',
|
||||||
meta: { title: '电池堆曲线',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
component: () => import('@/views/ems/dzjk/sbjk/db/index.vue'),
|
||||||
},
|
name: 'DzjkSbjkDb',
|
||||||
{
|
meta: {
|
||||||
path: 'dcwd',
|
title: '电表',
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/dcwd/index.vue'),
|
breadcrumb: false,
|
||||||
name: 'DzjkTjbbDcwd',
|
activeMenu: '/dzjk',
|
||||||
meta: { title: '电池温度',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
},
|
deviceCategory: 'AMMETER'
|
||||||
{
|
},
|
||||||
path: 'dbbb',
|
},
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/dbbb/index.vue'),
|
{
|
||||||
name: 'DzjkTjbbDbbb',
|
path: 'yl',
|
||||||
meta: { title: '电表报表',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
component: () => import('@/views/ems/dzjk/sbjk/yl/index.vue'),
|
||||||
},
|
name: 'DzjkSbjkYl',
|
||||||
{
|
meta: {
|
||||||
path: 'sybb',
|
title: '冷却',
|
||||||
component: () => import('@/views/ems/dzjk/tjbb/sybb/index.vue'),
|
breadcrumb: false,
|
||||||
name: 'DzjkTjbbSybb',
|
activeMenu: '/dzjk',
|
||||||
meta: { title: '收益报表',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'},
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
}
|
deviceCategory: 'COOLING'
|
||||||
]
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dzjk/clpz',
|
path: 'dh',
|
||||||
component: () => import('@/views/ems/dzjk/clpz/index.vue'),
|
component: () => import('@/views/ems/dzjk/sbjk/dh/index.vue'),
|
||||||
name: 'DzjkClpz',
|
name: 'DzjkSbjkDh',
|
||||||
meta: {title: '策略配置', breadcrumb: false, activeMenu: '/dzjk'},
|
meta: {
|
||||||
redirect: '/dzjk/clpz/clyx',
|
title: '动环',
|
||||||
children: [
|
breadcrumb: false,
|
||||||
{
|
activeMenu: '/dzjk',
|
||||||
path: 'clyx',
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
component: () => import('@/views/ems/dzjk/clpz/clyx/index.vue'),
|
deviceCategory: 'DH'
|
||||||
name: 'DzjkClpzClyx',
|
},
|
||||||
meta: { title: '策略运行',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkClpz'},
|
},
|
||||||
},
|
{
|
||||||
// {
|
path: 'xf',
|
||||||
// path: 'xftg',
|
component: () => import('@/views/ems/dzjk/sbjk/xf/index.vue'),
|
||||||
// component: () => import('@/views/ems/dzjk/clpz/xftg/index.vue'),
|
name: 'DzjkSbjkXf',
|
||||||
// hidden:true,
|
meta: {
|
||||||
// breadcrumb: false,
|
title: '消防',
|
||||||
// name: 'DzjkClpzXftg',
|
breadcrumb: false,
|
||||||
// meta: { title: '削峰填谷',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkClpz'},
|
activeMenu: '/dzjk',
|
||||||
// }
|
activeSecondMenuName: 'DzjkSbjk',
|
||||||
]
|
deviceCategory: 'XF'
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dzjk/gzgj',
|
||||||
|
component: () => import('@/views/ems/dzjk/gzgj/index.vue'),
|
||||||
|
name: 'DzjkGzgj',
|
||||||
|
meta: {
|
||||||
|
title: '故障告警',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkGzgj'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dzjk/tjbb',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/index.vue'),
|
||||||
|
name: 'DzjkTjbb',
|
||||||
|
meta: {title: '统计报表', breadcrumb: false, activeMenu: '/dzjk'},
|
||||||
|
redirect: '/dzjk/tjbb/gltj',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'gltj',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/gltj/index.vue'),
|
||||||
|
name: 'DzjkTjbbGltj',
|
||||||
|
meta: {
|
||||||
|
title: '运行统计',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'glqx',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/glqx/index.vue'),
|
||||||
|
name: 'DzjkTjbbGlqx',
|
||||||
|
meta: {
|
||||||
|
title: '功率曲线',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pcsqx',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/pcsqx/index.vue'),
|
||||||
|
name: 'DzjkTjbbPcsqx',
|
||||||
|
meta: {
|
||||||
|
title: 'PCS曲线',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dcdqx',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/dcdqx/index.vue'),
|
||||||
|
name: 'DzjkTjbbDcdqx',
|
||||||
|
meta: {
|
||||||
|
title: '电池堆曲线',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dcwd',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/dcwd/index.vue'),
|
||||||
|
name: 'DzjkTjbbDcwd',
|
||||||
|
meta: {
|
||||||
|
title: '电池温度',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dbbb',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/dbbb/index.vue'),
|
||||||
|
name: 'DzjkTjbbDbbb',
|
||||||
|
meta: {
|
||||||
|
title: '电表报表',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'sybb',
|
||||||
|
component: () => import('@/views/ems/dzjk/tjbb/sybb/index.vue'),
|
||||||
|
name: 'DzjkTjbbSybb',
|
||||||
|
meta: {
|
||||||
|
title: '收益报表',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkTjbb'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dzjk/clpz',
|
||||||
|
component: () => import('@/views/ems/dzjk/clpz/index.vue'),
|
||||||
|
name: 'DzjkClpz',
|
||||||
|
meta: {title: '策略配置', breadcrumb: false, activeMenu: '/dzjk'},
|
||||||
|
redirect: '/dzjk/clpz/clyx',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'clyx',
|
||||||
|
component: () => import('@/views/ems/dzjk/clpz/clyx/index.vue'),
|
||||||
|
name: 'DzjkClpzClyx',
|
||||||
|
meta: {
|
||||||
|
title: '策略运行',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkClpz'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'runtimeParam',
|
||||||
|
component: () => import('@/views/ems/dzjk/clpz/runtimeParam/index.vue'),
|
||||||
|
name: 'DzjkClpzRuntimeParam',
|
||||||
|
meta: {
|
||||||
|
title: '运行参数',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkClpz'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'sbbh',
|
||||||
|
component: () => import('@/views/ems/site/sbbh/index.vue'),
|
||||||
|
name: 'DzjkClpzSbbh',
|
||||||
|
meta: {
|
||||||
|
title: '设备保护',
|
||||||
|
breadcrumb: false,
|
||||||
|
activeMenu: '/dzjk',
|
||||||
|
activeSecondMenuName: 'DzjkClpz'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: 'xftg',
|
||||||
|
// component: () => import('@/views/ems/dzjk/clpz/xftg/index.vue'),
|
||||||
|
// hidden:true,
|
||||||
|
// breadcrumb: false,
|
||||||
|
// name: 'DzjkClpzXftg',
|
||||||
|
// meta: { title: '削峰填谷',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkClpz'},
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -89,6 +89,19 @@ export const constantRoutes = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/ems/site/zdlb',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'monitor-point-mapping',
|
||||||
|
component: () => import('@/views/ems/site/zdlb/MonitorPointMapping.vue'),
|
||||||
|
name: 'MonitorPointMapping',
|
||||||
|
meta: { title: '单站监控项目点位配置', activeMenu: '/dzjk/clpz/sbbh' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
// EMS管理系统routers
|
// EMS管理系统routers
|
||||||
...dzjk
|
...dzjk
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,53 +1,72 @@
|
|||||||
import {getAlarmDetailList,getSiteAllDeviceCategory} from'@/api/ems/dzjk'
|
import {getAlarmDetailList, getSiteAllDeviceCategory} from '@/api/ems/dzjk'
|
||||||
const ems = {
|
|
||||||
state: {
|
|
||||||
dzjkAlarmLighting:false,//单站监控 告警统计红点标志
|
|
||||||
zdList:[],
|
|
||||||
zdDeviceCategoryOptions:{},//站点各个站点包含的设备种类 {021_DDS_01:["BATTERY","CLUSTER","STACK", "DH", "AMMETER", "PCS", "XF"],021_DDS_02:[]...}
|
|
||||||
workStatusOptions:{'0':'正常','1':'异常','2':'停止'},//工作状态
|
|
||||||
deviceStatusOptions:{'0':'离线','1':'待机','2':'运行','3':'故障','4':'停机'},//设备状态
|
|
||||||
gridStatusOptions:{'0':'并网','1':'未并网'},//并网状态
|
|
||||||
controlModeOptions:{'0':'远程','1':'本地'},//控制模式
|
|
||||||
warnOptions:{0:'正常', 1:'中断', 2:'不在线',3:'异常'},//告警状态
|
|
||||||
communicationStatusOptions:{'0':'正常','1':'通讯中断','2':'异常'},//通讯状态
|
|
||||||
workModeOptions:{'0':'正常','1':'停止'},//工作模式
|
|
||||||
alarmLevelOptions:{'A':'提示','B':'一般','C':'严重','D':'紧急'},//告警等级
|
|
||||||
alarmStatusOptions:{'0':'待处理','1':'已处理','2':'处理中'},//告警状态
|
|
||||||
deviceTypeOptions:{'TCP':'TCP','RTU':'RTU'},//设备类型
|
|
||||||
ticketStatusOptions:{1:'待处理', 2:'处理中', 3:'已处理'},//工单处理状态
|
|
||||||
strategyStatusOptions:{'0':'未启用', '1':'已运行', '2':'已暂停', '3':'禁用', '4':'删除'},//策略状态
|
|
||||||
chargeStatusOptions:{'1':'充电','2':'待机'},//冲放状态
|
|
||||||
deviceCategoryOptions:{'PCS':'PCS','STACK':'电池堆','CLUSTER':'电池簇','COOLING':'液冷','BATTERY':'单体电池','AMMETER':'电表'},//设备类别
|
|
||||||
comparisonOperatorOptions:{'>':'>','<':'<','=':'=','>=':'>=','<=':'<='},
|
|
||||||
relationWithPoint:{'||':'||','&&':'&&'}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
SET_ZD_LIST(state, list) {
|
|
||||||
state.zdList = list || []
|
|
||||||
},
|
|
||||||
SET_DZJK_ALARM_LIGHTING(state, status) {
|
|
||||||
state.dzjkAlarmLighting = status
|
|
||||||
},
|
|
||||||
SET_ZD_DEVICE_CATEGORY_OPTIONS(state,{siteId,data}){
|
|
||||||
state.zdDeviceCategoryOptions = Object.assign({}, state.zdDeviceCategoryOptions, {[siteId]:data})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
//查询站点的所有待处理0的告警 存在展示红点标志
|
|
||||||
getSiteAlarmNum({state,commit},siteId){
|
|
||||||
getAlarmDetailList({status:0,siteId,pageSize:10,pageNum:1,deviceId:'',alarmLevel:'',alarmStartTime:'',alarmEndTime:''}).then(response=>{
|
|
||||||
commit('SET_DZJK_ALARM_LIGHTING',!!response?.total || false)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getSiteDeviceCategory({state,commit},siteId){
|
|
||||||
getSiteAllDeviceCategory(siteId).then(response=>{
|
|
||||||
let data = response?.data || [];
|
|
||||||
data.unshift('SSYX');
|
|
||||||
commit('SET_ZD_DEVICE_CATEGORY_OPTIONS',{siteId,data})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
const ems = {
|
||||||
|
state: {
|
||||||
|
dzjkAlarmLighting: false,//单站监控 告警统计红点标志
|
||||||
|
zdList: [],
|
||||||
|
zdDeviceCategoryOptions: {},//站点各个站点包含的设备种类 {021_DDS_01:["BATTERY","CLUSTER","STACK", "DH", "AMMETER", "PCS", "XF"],021_DDS_02:[]...}
|
||||||
|
CLUSTERWorkStatusOptions: {'0': '静置', '1': '充电', '2': '放电', '3': '待机', '5': '运行', '9': "故障"},//电池簇工作状态
|
||||||
|
PCSWorkStatusOptions: {'0': '运行', '1': '停机', '2': '故障', '3': '待机', '4': '充电', '5': '放电'},//PCS工作状态
|
||||||
|
STACKWorkStatusOptions: {
|
||||||
|
"0": "静置",
|
||||||
|
"1": "充电",
|
||||||
|
"2": "放电",
|
||||||
|
"3": "浮充",
|
||||||
|
'4': '待机',
|
||||||
|
'5': '运行',
|
||||||
|
'9': "故障"
|
||||||
|
},//STACKBMS总览工作状态
|
||||||
|
deviceStatusOptions: {'0': '离线', '1': '在线'},//设备状态
|
||||||
|
gridStatusOptions: {'0': '并网', '1': '未并网'},//并网状态
|
||||||
|
controlModeOptions: {'0': '远程', '1': '本地'},//控制模式
|
||||||
|
warnOptions: {0: '正常', 1: '中断', 2: '不在线', 3: '异常'},//告警状态
|
||||||
|
communicationStatusOptions: {'0': '正常', '1': '通讯中断', '2': '异常'},//通讯状态
|
||||||
|
workModeOptions: {'0': '正常', '1': '停止'},//工作模式
|
||||||
|
alarmLevelOptions: {'A': '提示', 'B': '一般', 'C': '严重', 'D': '紧急'},//告警等级
|
||||||
|
alarmStatusOptions: {'0': '待处理', '1': '已处理', '2': '处理中'},//告警状态
|
||||||
|
deviceTypeOptions: {'TCP': 'TCP', 'RTU': 'RTU'},//设备类型
|
||||||
|
ticketStatusOptions: {1: '待处理', 2: '处理中', 3: '已处理'},//工单处理状态
|
||||||
|
strategyStatusOptions: {'0': '未启用', '1': '已运行', '2': '已暂停', '3': '禁用', '4': '删除'},//策略状态
|
||||||
|
chargeStatusOptions: {'1': '充电', '2': '待机', '3': '放电'},//冲放状态
|
||||||
|
comparisonOperatorOptions: {'>': '>', '<': '<', '=': '=', '>=': '>=', '<=': '<='},
|
||||||
|
relationWithPoint: {'||': '||', '&&': '&&'}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_ZD_LIST(state, list) {
|
||||||
|
state.zdList = list || []
|
||||||
|
},
|
||||||
|
SET_DZJK_ALARM_LIGHTING(state, status) {
|
||||||
|
state.dzjkAlarmLighting = status
|
||||||
|
},
|
||||||
|
SET_ZD_DEVICE_CATEGORY_OPTIONS(state, {siteId, data}) {
|
||||||
|
state.zdDeviceCategoryOptions = Object.assign({}, state.zdDeviceCategoryOptions, {[siteId]: data})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
//查询站点的所有待处理0的告警 存在展示红点标志
|
||||||
|
getSiteAlarmNum({state, commit}, siteId) {
|
||||||
|
getAlarmDetailList({
|
||||||
|
status: 0,
|
||||||
|
siteId,
|
||||||
|
pageSize: 10,
|
||||||
|
pageNum: 1,
|
||||||
|
deviceId: '',
|
||||||
|
alarmLevel: '',
|
||||||
|
alarmStartTime: '',
|
||||||
|
alarmEndTime: ''
|
||||||
|
}).then(response => {
|
||||||
|
commit('SET_DZJK_ALARM_LIGHTING', !!response?.total || false)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getSiteDeviceCategory({state, commit}, siteId) {
|
||||||
|
getSiteAllDeviceCategory(siteId).then(response => {
|
||||||
|
let data = response?.data || [];
|
||||||
|
data.unshift('SSYX');
|
||||||
|
commit('SET_ZD_DEVICE_CATEGORY_OPTIONS', {siteId, data})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ems
|
export default ems
|
||||||
|
|||||||
@ -47,9 +47,12 @@ const permission = {
|
|||||||
const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
|
const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
|
||||||
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
|
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
|
||||||
router.addRoutes(asyncRoutes)
|
router.addRoutes(asyncRoutes)
|
||||||
if(!hasDzjk){
|
// 后端已下发 dzjk 菜单时,移除本地静态 dzjk 路由,避免重名/重复注册
|
||||||
|
if (hasDzjk) {
|
||||||
const index = constantRoutes.findIndex(i=>i.path.indexOf('dzjk')>-1)
|
const index = constantRoutes.findIndex(i=>i.path.indexOf('dzjk')>-1)
|
||||||
constantRoutes.splice(index,1)
|
if (index > -1) {
|
||||||
|
constantRoutes.splice(index, 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
commit('SET_ROUTES', rewriteRoutes)
|
commit('SET_ROUTES', rewriteRoutes)
|
||||||
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
|
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
|
||||||
@ -121,11 +124,16 @@ export function filterDynamicRoutes(routes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadView = (view) => {
|
export const loadView = (view) => {
|
||||||
|
const normalizedView = String(view || '')
|
||||||
|
.replace(/^\.\/+/, '')
|
||||||
|
.replace(/^\/+/, '')
|
||||||
|
.replace(/^@\/views\//, '')
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'staging') {
|
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'staging') {
|
||||||
return (resolve) => require([`@/views/${view}`], resolve)
|
return (resolve) => require([`@/views/${normalizedView}`], resolve)
|
||||||
} else {
|
} else {
|
||||||
// 使用 import 实现生产环境的路由懒加载
|
// 使用 import 实现生产环境的路由懒加载
|
||||||
return () => import(`@/views/${view}`)
|
return () => import(`@/views/${normalizedView}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,4 +50,3 @@ export default {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
248
src/views/ems/dzjk/clpz/runtimeParam/index.vue
Normal file
248
src/views/ems/dzjk/clpz/runtimeParam/index.vue
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ems-dashboard-editor-container" v-loading="loading">
|
||||||
|
<el-card shadow="never" class="common-card-container">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span class="card-title">运行参数配置</span>
|
||||||
|
<span class="site-tag">站点:{{ siteId || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="150px">
|
||||||
|
<el-row :gutter="20" class="runtime-grid">
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="group-card">
|
||||||
|
<div class="group-title">SOC上下限</div>
|
||||||
|
<el-form-item label="SOC下限(%)" prop="socDown">
|
||||||
|
<el-input-number v-model="form.socDown" :min="0" :max="100" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SOC上限(%)" prop="socUp">
|
||||||
|
<el-input-number v-model="form.socUp" :min="0" :max="100" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="group-card">
|
||||||
|
<div class="group-title">防逆流参数</div>
|
||||||
|
<el-form-item label="防逆流阈值(kW)" prop="antiReverseThreshold">
|
||||||
|
<el-input-number v-model="form.antiReverseThreshold" :min="0" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="阈值上浮比例(%)" prop="antiReverseRangePercent">
|
||||||
|
<el-input-number v-model="form.antiReverseRangePercent" :min="0" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="恢复上限(kW)" prop="antiReverseUp">
|
||||||
|
<el-input-number v-model="form.antiReverseUp" :min="0" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="降功率比例(%)" prop="antiReversePowerDownPercent">
|
||||||
|
<el-input-number v-model="form.antiReversePowerDownPercent" :min="0" :max="100" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="硬停阈值(kW)" prop="antiReverseHardStopThreshold">
|
||||||
|
<el-input-number v-model="form.antiReverseHardStopThreshold" :min="0" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="group-card">
|
||||||
|
<div class="group-title">功率与保护</div>
|
||||||
|
<el-form-item label="设定功率倍率" prop="powerSetMultiplier">
|
||||||
|
<el-input-number v-model="form.powerSetMultiplier" :min="0.0001" :step="0.1" :precision="4" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="保护介入" prop="protectInterveneEnable">
|
||||||
|
<el-switch v-model="form.protectInterveneEnable" :active-value="1" :inactive-value="0" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="一级降额比例(%)" prop="protectL1DeratePercent">
|
||||||
|
<el-input-number v-model="form.protectL1DeratePercent" :min="0" :max="100" :step="1" :precision="2" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="释放稳定时长(s)" prop="protectRecoveryStableSeconds">
|
||||||
|
<el-input-number v-model="form.protectRecoveryStableSeconds" :min="0" :max="3600" :step="1" :precision="0" :controls="false" style="width: 160px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="三级锁存" prop="protectL3LatchEnable">
|
||||||
|
<el-switch v-model="form.protectL3LatchEnable" :active-value="1" :inactive-value="0" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="冲突策略" prop="protectConflictPolicy">
|
||||||
|
<el-select v-model="form.protectConflictPolicy" style="width: 160px;">
|
||||||
|
<el-option label="最高等级优先" value="MAX_LEVEL_WIN" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="action-row">
|
||||||
|
<el-button type="primary" :loading="saveLoading" @click="handleSave">保存</el-button>
|
||||||
|
<el-button @click="init">重置</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import getQuerySiteId from '@/mixins/ems/getQuerySiteId'
|
||||||
|
import { getStrategyRuntimeConfig, saveStrategyRuntimeConfig } from '@/api/ems/dzjk'
|
||||||
|
|
||||||
|
const emptyForm = () => ({
|
||||||
|
siteId: '',
|
||||||
|
socDown: 0,
|
||||||
|
socUp: 100,
|
||||||
|
antiReverseThreshold: 30,
|
||||||
|
powerSetMultiplier: 10,
|
||||||
|
antiReverseRangePercent: 20,
|
||||||
|
antiReverseUp: 100,
|
||||||
|
antiReversePowerDownPercent: 10,
|
||||||
|
antiReverseHardStopThreshold: 20,
|
||||||
|
protectInterveneEnable: 1,
|
||||||
|
protectL1DeratePercent: 50,
|
||||||
|
protectRecoveryStableSeconds: 5,
|
||||||
|
protectL3LatchEnable: 1,
|
||||||
|
protectConflictPolicy: 'MAX_LEVEL_WIN'
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DzjkClpzRuntimeParam',
|
||||||
|
mixins: [getQuerySiteId],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
saveLoading: false,
|
||||||
|
form: emptyForm(),
|
||||||
|
rules: {
|
||||||
|
socDown: [
|
||||||
|
{ required: true, message: '请输入SOC下限', trigger: 'change' }
|
||||||
|
],
|
||||||
|
socUp: [
|
||||||
|
{ required: true, message: '请输入SOC上限', trigger: 'change' }
|
||||||
|
],
|
||||||
|
antiReverseThreshold: [
|
||||||
|
{ required: true, message: '请输入防逆流阈值', trigger: 'change' }
|
||||||
|
],
|
||||||
|
powerSetMultiplier: [
|
||||||
|
{ required: true, message: '请输入设定功率倍率', trigger: 'change' }
|
||||||
|
],
|
||||||
|
antiReverseRangePercent: [
|
||||||
|
{ required: true, message: '请输入防逆流阈值上浮比例', trigger: 'change' }
|
||||||
|
],
|
||||||
|
antiReverseUp: [
|
||||||
|
{ required: true, message: '请输入防逆流恢复上限', trigger: 'change' }
|
||||||
|
],
|
||||||
|
antiReversePowerDownPercent: [
|
||||||
|
{ required: true, message: '请输入防逆流降功率比例', trigger: 'change' }
|
||||||
|
],
|
||||||
|
antiReverseHardStopThreshold: [
|
||||||
|
{ required: true, message: '请输入防逆流硬停阈值', trigger: 'change' }
|
||||||
|
],
|
||||||
|
protectInterveneEnable: [
|
||||||
|
{ required: true, message: '请选择是否启用保护介入', trigger: 'change' }
|
||||||
|
],
|
||||||
|
protectL1DeratePercent: [
|
||||||
|
{ required: true, message: '请输入一级降额比例', trigger: 'change' }
|
||||||
|
],
|
||||||
|
protectRecoveryStableSeconds: [
|
||||||
|
{ required: true, message: '请输入释放稳定时长', trigger: 'change' }
|
||||||
|
],
|
||||||
|
protectL3LatchEnable: [
|
||||||
|
{ required: true, message: '请选择三级锁存开关', trigger: 'change' }
|
||||||
|
],
|
||||||
|
protectConflictPolicy: [
|
||||||
|
{ required: true, message: '请选择冲突策略', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
if (!this.siteId) {
|
||||||
|
this.form = emptyForm()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
getStrategyRuntimeConfig(this.siteId).then(response => {
|
||||||
|
const data = response?.data || {}
|
||||||
|
this.form = {
|
||||||
|
...emptyForm(),
|
||||||
|
...data,
|
||||||
|
siteId: this.siteId
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSave() {
|
||||||
|
if (!this.siteId) {
|
||||||
|
this.$message.error('缺少站点ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (!valid) return
|
||||||
|
if (Number(this.form.socDown) > Number(this.form.socUp)) {
|
||||||
|
this.$message.error('SOC下限不能大于SOC上限')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (Number(this.form.powerSetMultiplier) <= 0) {
|
||||||
|
this.$message.error('设定功率倍率必须大于0')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (Number(this.form.protectL1DeratePercent) < 0 || Number(this.form.protectL1DeratePercent) > 100) {
|
||||||
|
this.$message.error('一级降额比例必须在0~100之间')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (Number(this.form.protectRecoveryStableSeconds) < 0) {
|
||||||
|
this.$message.error('释放稳定时长不能小于0')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.saveLoading = true
|
||||||
|
saveStrategyRuntimeConfig({ ...this.form, siteId: this.siteId }).then(response => {
|
||||||
|
if (response?.code === 200) {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.saveLoading = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.site-tag {
|
||||||
|
float: right;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.common-card-container {
|
||||||
|
border: none;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.runtime-grid {
|
||||||
|
max-width: 1180px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.runtime-grid > .el-col {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 14px 14px 2px;
|
||||||
|
min-height: 330px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,124 +1,221 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog :visible.sync="dialogTableVisible" class="ems-dialog" :title="mode === 'add'?'新增模板':`编辑模板` ">
|
<el-dialog :visible.sync="dialogTableVisible" class="ems-dialog add-template-dialog"
|
||||||
<el-form ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="100px">
|
:title="mode === 'add'?'新增模板':`编辑模板` ">
|
||||||
<el-form-item label="模板名称" prop="templateName">
|
<el-form ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="100px">
|
||||||
<el-input v-model="formData.templateName" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-form-item label="模板名称" prop="templateName">
|
||||||
</el-input>
|
<el-input v-model="formData.templateName" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item label="soc限制" prop="sdcLimit" required>
|
</el-form-item>
|
||||||
<el-switch :active-value="1" :inactive-value="0" v-model="formData.sdcLimit"></el-switch>
|
<el-form-item label="soc限制" prop="sdcLimit" required>
|
||||||
</el-form-item>
|
<el-switch :active-value="1" :inactive-value="0" v-model="formData.sdcLimit"></el-switch>
|
||||||
<!-- <template v-if="formData.sdcLimit === 1">-->
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-button type="primary" size="mini" @click="addTime">新增</el-button>
|
||||||
|
<!-- 新增时间段表单-->
|
||||||
|
<el-collapse-transition>
|
||||||
|
<el-card v-show="showAddTime" shadow="always" class="common-card-container" style="margin-top:25px;">
|
||||||
|
<el-form class="add-time-form transition-box" ref="addTimeForm" :model="formInline" :rules="formInlineRule"
|
||||||
|
label-width="100px" style="margin-top:25px">
|
||||||
|
<!-- <el-form-item label="开始时间" prop="startTime">-->
|
||||||
|
<!-- <el-time-select-->
|
||||||
|
<!-- placeholder="开始时间"-->
|
||||||
|
<!-- v-model="formInline.startTime"-->
|
||||||
|
<!-- :picker-options="{-->
|
||||||
|
<!-- start: '00:00',-->
|
||||||
|
<!-- step: '00:01',-->
|
||||||
|
<!-- end: '23:00',-->
|
||||||
|
<!-- }">-->
|
||||||
|
<!-- </el-time-select>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- <el-form-item label="结束时间" prop="endTime">-->
|
||||||
|
<!-- <el-time-select-->
|
||||||
|
<!-- placeholder="结束时间"-->
|
||||||
|
<!-- v-model="formInline.endTime"-->
|
||||||
|
<!-- :picker-options="{-->
|
||||||
|
<!-- start: '00:00',-->
|
||||||
|
<!-- step: '00:01',-->
|
||||||
|
<!-- end: '23:00',-->
|
||||||
|
<!-- minTime: formInline.startTime-->
|
||||||
|
<!-- }">-->
|
||||||
|
<!-- </el-time-select>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<el-form-item label="时间范围" prop="timeRange">
|
||||||
|
<el-time-picker
|
||||||
|
is-range
|
||||||
|
v-model="formInline.timeRange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
placeholder="选择时间范围"
|
||||||
|
format="HH:mm"
|
||||||
|
value-format="HH:mm"
|
||||||
|
:style="{width: '100%'}">
|
||||||
|
</el-time-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="冲放功率" prop="chargeDischargePower">
|
||||||
|
<el-input v-model="formInline.chargeDischargePower" placeholder="请输入"
|
||||||
|
:style="{width: '100%'}"></el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="soc下限" prop="sdcDown">
|
<el-form-item label="soc下限" prop="sdcDown">
|
||||||
<el-input v-model="formData.sdcDown" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
|
<el-input v-model="formInline.sdcDown" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="soc上限" prop="sdcUp">
|
<el-form-item label="soc上限" prop="sdcUp">
|
||||||
<el-input v-model="formData.sdcUp" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
|
<el-input v-model="formInline.sdcUp" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- </template>-->
|
<el-form-item label="充电状态" prop="chargeStatus">
|
||||||
</el-form>
|
<el-select v-model="formInline.chargeStatus" placeholder="请选择" :style="{width: '100%'}">
|
||||||
<el-button type="primary" size="mini" @click="addTime">新增</el-button>
|
<el-option v-for="(value,key) in chargeStatusOptions" :key="key+'chargeStatusOptions'" :label="value"
|
||||||
<!-- 新增时间段表单-->
|
:value="key"></el-option>
|
||||||
<el-collapse-transition>
|
</el-select>
|
||||||
<el-card v-show="showAddTime" shadow="always" class="common-card-container" style="margin-top:25px;">
|
</el-form-item>
|
||||||
<el-form class="add-time-form transition-box" ref="addTimeForm" :model="formInline" :rules="formInlineRule" label-width="100px" style="margin-top:25px">
|
<el-form-item>
|
||||||
<el-form-item label="开始时间" prop="startTime">
|
<el-button type="primary" size="mini" @click="saveTime">保存</el-button>
|
||||||
<el-time-select
|
<el-button size="mini" @click="cancelAddTime">取消</el-button>
|
||||||
placeholder="开始时间"
|
</el-form-item>
|
||||||
v-model="formInline.startTime"
|
</el-form>
|
||||||
:picker-options="{
|
</el-card>
|
||||||
start: '00:00',
|
</el-collapse-transition>
|
||||||
step: '01:00',
|
<el-table
|
||||||
end: '23:00',
|
|
||||||
}">
|
|
||||||
</el-time-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="结束时间" prop="endTime">
|
|
||||||
<el-time-select
|
|
||||||
placeholder="结束时间"
|
|
||||||
v-model="formInline.endTime"
|
|
||||||
:picker-options="{
|
|
||||||
start: '00:00',
|
|
||||||
step: '01:00',
|
|
||||||
end: '23:00',
|
|
||||||
minTime: formInline.startTime
|
|
||||||
}">
|
|
||||||
</el-time-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="冲放功率" prop="chargeDischargePower">
|
|
||||||
<el-input v-model="formInline.chargeDischargePower" placeholder="请输入"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="充电状态" prop="chargeStatus">
|
|
||||||
<el-select v-model="formInline.chargeStatus" placeholder="请选择">
|
|
||||||
<el-option v-for="(value,key) in chargeStatusOptions" :key="key+'chargeStatusOptions'" :label="value" :value="key"></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" size="mini" @click="saveTime">保存</el-button>
|
|
||||||
<el-button size="mini" @click="cancelAddTime">取消</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
</el-collapse-transition>
|
|
||||||
<el-table
|
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
border
|
border
|
||||||
style="width: 100%;margin-top:25px">
|
style="width: 100%;margin-top:25px">
|
||||||
<!-- todo 如果要在span-method中使用column.property 在表格中必须定义prop="xxx"属性-->
|
<el-table-column
|
||||||
<el-table-column
|
|
||||||
prop="startTime"
|
prop="startTime"
|
||||||
label="开始时间">
|
label="开始时间">
|
||||||
</el-table-column>
|
<template slot-scope="scope">
|
||||||
<el-table-column
|
<el-time-select
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
v-model="scope.row.startTime"
|
||||||
|
placeholder="开始时间"
|
||||||
|
:picker-options="{
|
||||||
|
start: '00:00',
|
||||||
|
step: '00:01',
|
||||||
|
end: '23:59'
|
||||||
|
}"
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.startTime || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
prop="endTime"
|
prop="endTime"
|
||||||
label="结束时间">
|
label="结束时间">
|
||||||
</el-table-column>
|
<template slot-scope="scope">
|
||||||
<el-table-column
|
<el-time-select
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
v-model="scope.row.endTime"
|
||||||
|
placeholder="结束时间"
|
||||||
|
:picker-options="{
|
||||||
|
start: '00:00',
|
||||||
|
step: '00:01',
|
||||||
|
end: '23:59',
|
||||||
|
minTime: scope.row.startTime
|
||||||
|
}"
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.endTime || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
prop="chargeDischargePower"
|
prop="chargeDischargePower"
|
||||||
label="充放功率(kW)">
|
label="充放功率(kW)">
|
||||||
</el-table-column>
|
<template slot-scope="scope">
|
||||||
<el-table-column
|
<el-input
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
v-model.trim="scope.row.chargeDischargePower"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.chargeDischargePower || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="sdcDown"
|
||||||
|
label="SOC下限">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
v-model.trim="scope.row.sdcDown"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.sdcDown === null || scope.row.sdcDown === undefined || scope.row.sdcDown === '' ? '-' : scope.row.sdcDown + '%' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="sdcUp"
|
||||||
|
label="SOC上限">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
v-model.trim="scope.row.sdcUp"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.sdcUp === null || scope.row.sdcUp === undefined || scope.row.sdcUp === '' ? '-' : scope.row.sdcUp + '%' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
prop="chargeStatus"
|
prop="chargeStatus"
|
||||||
label="充电状态">
|
label="充电状态">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{chargeStatusOptions[scope.row.chargeStatus]}}
|
<el-select
|
||||||
</template>
|
v-if="mode === 'edit'"
|
||||||
</el-table-column>
|
v-model="scope.row.chargeStatus"
|
||||||
<el-table-column
|
placeholder="请选择"
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(value,key) in chargeStatusOptions"
|
||||||
|
:key="key+'chargeStatusEditOptions'"
|
||||||
|
:label="value"
|
||||||
|
:value="key"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<span v-else>{{ chargeStatusOptions[scope.row.chargeStatus] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
fixed="right"
|
fixed="right"
|
||||||
label="操作"
|
label="操作"
|
||||||
width="120">
|
width="120">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@click.native.prevent="deleteRow(scope.$index, tableData)"
|
@click.native.prevent="deleteRow(scope.$index, tableData)"
|
||||||
type="warning"
|
type="warning"
|
||||||
size="mini">
|
size="mini">
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<el-button @click="closeDialog">取消</el-button>
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
<el-button type="primary" @click="saveDialog">确定</el-button>
|
<el-button type="primary" @click="saveDialog">确定</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
import {addStrategyTemp,editStrategyTemp,getStrategyTempDetail} from '@/api/ems/dzjk'
|
import {addStrategyTemp, editStrategyTemp, getStrategyTempDetail} from '@/api/ems/dzjk'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject:['$home'],
|
inject: ['$home'],
|
||||||
data() {
|
data() {
|
||||||
|
const now = new Date()
|
||||||
|
const year = now.getFullYear(), month = now.getMonth(), day = now.getDate()
|
||||||
|
const range = [new Date(year, month, day, 0), new Date(year, month, day, 23)]
|
||||||
return {
|
return {
|
||||||
mode:'',
|
mode: '',
|
||||||
editTempId:'',
|
editTempId: '',
|
||||||
dialogTableVisible:false,
|
dialogTableVisible: false,
|
||||||
|
secondRange: range,
|
||||||
formData: {
|
formData: {
|
||||||
templateName: '',
|
templateName: '',
|
||||||
sdcLimit: false,
|
sdcLimit: false,
|
||||||
sdcDown: '',
|
|
||||||
sdcUp: '',
|
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
templateName: [{
|
templateName: [{
|
||||||
@ -126,45 +223,44 @@ export default {
|
|||||||
message: '请输入',
|
message: '请输入',
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}],
|
}],
|
||||||
sdcDown: [
|
|
||||||
{required: true,message: '请输入', trigger: 'blur'},
|
|
||||||
{ pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数' }
|
|
||||||
],
|
|
||||||
sdcUp: [
|
|
||||||
{required: true,message: '请输入', trigger: 'blur'},
|
|
||||||
{ pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数' }
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
showAddTime: false,
|
showAddTime: false,
|
||||||
formInline:{
|
formInline: {
|
||||||
startTime:'',endTime:'',chargeDischargePower:'',chargeStatus:''
|
timeRange: range,
|
||||||
|
chargeDischargePower: '',
|
||||||
|
sdcDown: '',
|
||||||
|
sdcUp: '',
|
||||||
|
chargeStatus: ''
|
||||||
},
|
},
|
||||||
formInlineRule:{
|
formInlineRule: {
|
||||||
startTime: [{
|
timeRange: [{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择开始时间',
|
message: '请选择时间范围',
|
||||||
trigger: 'change'
|
|
||||||
}],
|
|
||||||
endTime: [{
|
|
||||||
required: true,
|
|
||||||
message: '请选择结束时间',
|
|
||||||
trigger: 'change'
|
trigger: 'change'
|
||||||
}],
|
}],
|
||||||
chargeDischargePower: [{
|
chargeDischargePower: [{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入冲放功率',
|
message: '请输入冲放功率',
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
},
|
},
|
||||||
{ pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数' }
|
{pattern: /^-?\d*\.?\d*$/, message: '请输入合法数字或小数'}
|
||||||
],
|
],
|
||||||
chargeStatus:[{
|
sdcDown: [
|
||||||
|
{required: true, message: '请输入', trigger: 'blur'},
|
||||||
|
{pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
|
||||||
|
],
|
||||||
|
sdcUp: [
|
||||||
|
{required: true, message: '请输入', trigger: 'blur'},
|
||||||
|
{pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
|
||||||
|
],
|
||||||
|
chargeStatus: [{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择充放状态',
|
message: '请选择充放状态',
|
||||||
trigger: ['blur','change']
|
trigger: ['blur', 'change']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
tableData:[],
|
tableData: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -172,145 +268,183 @@ export default {
|
|||||||
chargeStatusOptions: state => state?.ems?.chargeStatusOptions || {},
|
chargeStatusOptions: state => state?.ems?.chargeStatusOptions || {},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
"formInline.startTime":{
|
|
||||||
handler(newVal){
|
|
||||||
if(newVal && this.formInline.endTime){
|
|
||||||
const endTime = parseInt((this.formInline.endTime).split(':')[0] || 0)
|
|
||||||
const startTime =parseInt(newVal.split(':')[0])
|
|
||||||
if(endTime<=startTime){
|
|
||||||
this.formInline.endTime = `${startTime+1 <=9 ? '0'+(startTime+1) : startTime+1}:00`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep:true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
changeSiteId(){
|
changeSiteId() {
|
||||||
this.dialogTableVisible=false
|
this.dialogTableVisible = false
|
||||||
this.mode=''
|
this.mode = ''
|
||||||
this.editTempId=''
|
this.editTempId = ''
|
||||||
this.formData={
|
this.formData = {
|
||||||
templateName: '',
|
templateName: '',
|
||||||
sdcLimit: false,
|
sdcLimit: false,
|
||||||
sdcDown: '',
|
|
||||||
sdcUp: '',
|
|
||||||
}
|
|
||||||
this.formInline={
|
|
||||||
startTime:'',endTime:'',chargeDischargePower:'',chargeStatus:''
|
|
||||||
}
|
}
|
||||||
|
this.formInline = {
|
||||||
|
timeRange: this.secondRange, chargeDischargePower: '', sdcDown: '', sdcUp: '', chargeStatus: ''
|
||||||
|
}//startTime: '', endTime: '',
|
||||||
this.showAddTime = false
|
this.showAddTime = false
|
||||||
this.tableData=[]
|
this.tableData = []
|
||||||
},
|
},
|
||||||
show({mode = 'add', editTempId = ''}){
|
show({mode = 'add', editTempId = ''}) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.dialogTableVisible = true
|
this.dialogTableVisible = true
|
||||||
this.mode = mode
|
this.mode = mode
|
||||||
this.editTempId=editTempId
|
this.editTempId = editTempId
|
||||||
if(mode === 'edit' && editTempId){
|
if (mode === 'edit' && editTempId) {
|
||||||
getStrategyTempDetail(this.editTempId).then(response => {
|
getStrategyTempDetail(this.editTempId).then(response => {
|
||||||
const data=JSON.parse(JSON.stringify(response?.data || []));
|
const data = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
if(data.length>0){
|
if (data.length > 0) {
|
||||||
const {templateName,sdcLimit,sdcDown,sdcUp} =JSON.parse(JSON.stringify( data[0]));
|
const {templateName, sdcLimit} = JSON.parse(JSON.stringify(data[0]));
|
||||||
this.formData.templateName=templateName
|
this.formData.templateName = templateName
|
||||||
this.formData.sdcLimit=sdcLimit
|
this.formData.sdcLimit = sdcLimit
|
||||||
this.formData.sdcDown=sdcDown
|
|
||||||
this.formData.sdcUp=sdcUp
|
|
||||||
}
|
}
|
||||||
if(data.length === 1){
|
if (data.length === 1) {
|
||||||
const {startTime,endTime}=data;
|
const {startTime, endTime} = data[0];
|
||||||
if(!startTime || !endTime){
|
if (!startTime || !endTime) {
|
||||||
this.tableData = []
|
this.tableData = []
|
||||||
}else{
|
} else {
|
||||||
this.tableData= data
|
this.tableData = data
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
this.tableData= data
|
this.tableData = data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
addTime(){
|
addTime() {
|
||||||
this.showAddTime=true
|
this.showAddTime = true
|
||||||
},
|
},
|
||||||
cancelAddTime(){
|
cancelAddTime() {
|
||||||
this.$refs.addTimeForm.resetFields()
|
this.$refs.addTimeForm.resetFields()
|
||||||
this.showAddTime=false
|
this.showAddTime = false
|
||||||
this.formInline = {startTime:'',endTime:'',chargeDischargePower:'',chargeStatus:''}
|
this.formInline = {timeRange: this.secondRange, chargeDischargePower: '', sdcDown: '', sdcUp: '', chargeStatus: ''}//startTime: '', endTime: '',
|
||||||
},
|
},
|
||||||
saveTime(){
|
saveTime() {
|
||||||
//表单校验,校验成功,添加到tableData里
|
//表单校验,校验成功,添加到tableData里
|
||||||
this.$refs.addTimeForm.validate(valid => {
|
this.$refs.addTimeForm.validate(valid => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
this.tableData.push(JSON.parse(JSON.stringify(this.formInline)));
|
const {timeRange: [startTime, endTime], chargeDischargePower, sdcDown, sdcUp, chargeStatus} = this.formInline
|
||||||
this.$nextTick(() => {this.cancelAddTime()})
|
|
||||||
|
this.tableData.push({startTime, endTime, chargeDischargePower, sdcDown, sdcUp, chargeStatus})
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.cancelAddTime()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteRow(index){
|
deleteRow(index) {
|
||||||
this.tableData.splice(index,1)
|
this.tableData.splice(index, 1)
|
||||||
},
|
},
|
||||||
saveDialog() {
|
saveDialog() {
|
||||||
this.$refs.addTempForm.validate(valid => {
|
this.$refs.addTempForm.validate(valid => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
//校验时间选择范围是否冲突
|
const {templateName, sdcLimit} = this.formData
|
||||||
let status = true
|
const {siteId, updateStrategyId} = this.$home
|
||||||
this.tableData.forEach((outer,outerIndex)=>{
|
const tableData = this.tableData.map(item => ({
|
||||||
const {startTime, endTime}=outer
|
...item,
|
||||||
const outerStart = parseInt(startTime),outerEnd = parseInt(endTime)
|
sdcDown: this.normalizeSocValue(item.sdcDown),
|
||||||
if(outerStart>outerEnd){
|
sdcUp: this.normalizeSocValue(item.sdcUp)
|
||||||
status = false
|
}))
|
||||||
}else{
|
if (!this.validateTableData(tableData)) return
|
||||||
this.tableData.forEach((inner,innerIndex)=>{
|
if (this.mode === 'edit') {
|
||||||
if(innerIndex !== outerIndex){
|
editStrategyTemp({
|
||||||
const {startTime:innerStartTime, endTime:innerEndTime}=inner
|
siteId,
|
||||||
const innerStart = parseInt(innerStartTime),innerEnd = parseInt(innerEndTime)
|
strategyId: updateStrategyId,
|
||||||
if((innerStart<outerStart && innerEnd>outerEnd) || !((innerStart<outerStart && innerEnd<=outerStart) || (innerStart>=outerEnd && innerEnd>outerEnd))){
|
templateId: this.editTempId,
|
||||||
status=false
|
templateName,
|
||||||
}
|
sdcLimit,
|
||||||
}
|
timeConfigList: tableData
|
||||||
})
|
}).then(response => {
|
||||||
|
if (response?.code === 200) {
|
||||||
|
this.closeDialog()
|
||||||
|
this.$emit('update')
|
||||||
|
this.$emit('updateTimeSetting')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(!status){
|
} else {
|
||||||
return this.$message.error('时间选择范围冲突');
|
addStrategyTemp({
|
||||||
}
|
siteId,
|
||||||
const {templateName,sdcLimit,sdcDown,sdcUp} = this.formData
|
strategyId: updateStrategyId,
|
||||||
const {siteId,updateStrategyId} =this.$home
|
templateName,
|
||||||
const {tableData} = this
|
sdcLimit,
|
||||||
if(this.mode==='edit'){
|
timeConfigList: tableData
|
||||||
editStrategyTemp({siteId,strategyId:updateStrategyId,templateId:this.editTempId,templateName,sdcLimit,sdcDown,sdcUp,timeConfigList:tableData}).then(response=>{
|
}).then(response => {
|
||||||
if(response?.code === 200){
|
if (response?.code === 200) {
|
||||||
this.closeDialog()
|
this.closeDialog()
|
||||||
this.$emit('update')
|
this.$emit('update')
|
||||||
this.$emit('updateTimeSetting')
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}else{
|
|
||||||
addStrategyTemp({siteId,strategyId:updateStrategyId,templateName,sdcLimit,sdcDown,sdcUp,timeConfigList:tableData}).then(response=>{
|
|
||||||
if(response?.code === 200){
|
|
||||||
this.closeDialog()
|
|
||||||
this.$emit('update')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
closeDialog(){
|
normalizeSocValue(value) {
|
||||||
|
if (value === null || value === undefined) return null
|
||||||
|
const normalized = String(value).replace('%', '').trim()
|
||||||
|
return normalized === '' ? null : normalized
|
||||||
|
},
|
||||||
|
toMinutes(timeValue) {
|
||||||
|
if (!timeValue || String(timeValue).indexOf(':') < 0) return -1
|
||||||
|
const [h, m] = String(timeValue).split(':')
|
||||||
|
const hour = Number(h), minute = Number(m)
|
||||||
|
if (!Number.isInteger(hour) || !Number.isInteger(minute)) return -1
|
||||||
|
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return -1
|
||||||
|
return hour * 60 + minute
|
||||||
|
},
|
||||||
|
validateTableData(list = []) {
|
||||||
|
const numberPattern = /^-?\d+(\.\d+)?$/
|
||||||
|
const socPattern = /^(0|[1-9]\d*)(\.\d+)?$/
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const row = list[i]
|
||||||
|
const rowNo = i + 1
|
||||||
|
if (!row.startTime || !row.endTime) {
|
||||||
|
this.$message.error(`第${rowNo}行:开始时间和结束时间不能为空`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const startMinute = this.toMinutes(row.startTime)
|
||||||
|
const endMinute = this.toMinutes(row.endTime)
|
||||||
|
if (startMinute < 0 || endMinute < 0 || startMinute >= endMinute) {
|
||||||
|
this.$message.error(`第${rowNo}行:时间范围不合法`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!numberPattern.test(String(row.chargeDischargePower ?? '').trim())) {
|
||||||
|
this.$message.error(`第${rowNo}行:充放功率格式不正确`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!socPattern.test(String(row.sdcDown ?? '').trim())) {
|
||||||
|
this.$message.error(`第${rowNo}行:SOC下限格式不正确`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!socPattern.test(String(row.sdcUp ?? '').trim())) {
|
||||||
|
this.$message.error(`第${rowNo}行:SOC上限格式不正确`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (Number(row.sdcDown) > Number(row.sdcUp)) {
|
||||||
|
this.$message.error(`第${rowNo}行:SOC下限不能大于SOC上限`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (row.chargeStatus === undefined || row.chargeStatus === null || row.chargeStatus === '') {
|
||||||
|
this.$message.error(`第${rowNo}行:请选择充电状态`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
closeDialog() {
|
||||||
// 清空所有数据
|
// 清空所有数据
|
||||||
this.$refs.addTempForm.resetFields()
|
this.$refs.addTempForm.resetFields()
|
||||||
this.formData={
|
this.formData = {
|
||||||
templateName: '',
|
templateName: '',
|
||||||
sdcLimit:0,
|
sdcLimit: 0,
|
||||||
sdcDown: '',
|
|
||||||
sdcUp: '',
|
|
||||||
}
|
}
|
||||||
this.tableData=[]
|
this.tableData = []
|
||||||
this.cancelAddTime()
|
this.cancelAddTime()
|
||||||
this.dialogTableVisible=false
|
this.dialogTableVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.add-template-dialog {
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -39,14 +39,14 @@
|
|||||||
prop="sdcDown"
|
prop="sdcDown"
|
||||||
label="SOC下限">
|
label="SOC下限">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{scope.row.sdcDown ? scope.row. sdcDown + '%' : '-'}}
|
{{scope.row.sdcDown === null || scope.row.sdcDown === undefined || scope.row.sdcDown === '' ? '-' : scope.row.sdcDown + '%'}}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="sdcUp"
|
prop="sdcUp"
|
||||||
label="SOC上限">
|
label="SOC上限">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{scope.row.sdcUp ? scope.row.sdcUp + '%' : '-'}}
|
{{scope.row.sdcUp === null || scope.row.sdcUp === undefined || scope.row.sdcUp === '' ? '-' : scope.row.sdcUp + '%'}}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@ -92,7 +92,7 @@ export default {
|
|||||||
activeBtn:'',
|
activeBtn:'',
|
||||||
tempList:[],
|
tempList:[],
|
||||||
tableData:[],
|
tableData:[],
|
||||||
mixinPrototype:['templateName','sdcLimit','sdcDown','sdcUp']
|
mixinPrototype:['templateName','sdcLimit']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed:{
|
computed:{
|
||||||
|
|||||||
@ -1,27 +1,30 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-card v-loading="loading" gshadow="always" class="common-card-container common-card-container-no-title-bg">
|
<el-card v-loading="loading" gshadow="always" class="common-card-container common-card-container-no-title-bg">
|
||||||
<!-- 搜索栏-->
|
<!-- 搜索栏-->
|
||||||
<el-form :inline="true" class="select-container">
|
<el-form :inline="true" class="select-container">
|
||||||
<el-form-item label="设备清单">
|
<el-form-item label="设备清单">
|
||||||
<el-select v-model="search.deviceId" clearable placeholder="请选择" :loading="loading" loading-text="正在加载数据">
|
<el-select v-model="search.deviceId" clearable placeholder="请选择" :loading="loading"
|
||||||
<el-option :label="item.deviceName" :value="item.deviceId" v-for="(item,key) in deviceOptions" :key="key+'deviceIdOptions'"></el-option>
|
loading-text="正在加载数据">
|
||||||
|
<el-option :label="item.deviceName" :value="item.deviceId" v-for="(item,key) in deviceOptions"
|
||||||
|
:key="key+'deviceIdOptions'"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="告警等级">
|
<el-form-item label="告警等级">
|
||||||
<el-select v-model="search.alarmLevel" clearable placeholder="请选择" :loading="loading" loading-text="正在加载数据">
|
<el-select v-model="search.alarmLevel" clearable placeholder="请选择" :loading="loading"
|
||||||
<el-option :label="value" :value="key" v-for="(value,key) in $store.state.ems.alarmLevelOptions" :key="key+'alarmLevelOptions'"></el-option>
|
loading-text="正在加载数据" style="width: 130px">
|
||||||
|
<el-option :label="value" :value="key" v-for="(value,key) in $store.state.ems.alarmLevelOptions"
|
||||||
|
:key="key+'alarmLevelOptions'"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="时间选择">
|
<el-form-item label="时间选择">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="dateRange"
|
v-model="dateRange"
|
||||||
type="daterange"
|
type="daterange"
|
||||||
range-separator="至"
|
range-separator="至"
|
||||||
start-placeholder="开始时间"
|
start-placeholder="开始时间"
|
||||||
:picker-options="pickerOptions"
|
:picker-options="pickerOptions"
|
||||||
:default-value="defaultDateRange"
|
:default-value="defaultDateRange"
|
||||||
end-placeholder="结束时间">
|
end-placeholder="结束时间">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
@ -36,7 +39,9 @@
|
|||||||
<el-row style="">
|
<el-row style="">
|
||||||
<el-col :xs="24" :sm="24" :lg="24">
|
<el-col :xs="24" :sm="24" :lg="24">
|
||||||
<el-button-group class="ems-btns-group">
|
<el-button-group class="ems-btns-group">
|
||||||
<el-button v-for="(item,index) in btnList" :key="index+'dtdcBtns'" :class="{'activeBtn' : activeBtn === item.id}" @click="changeDataType(item.id)">{{item.name}}</el-button>
|
<el-button v-for="(item,index) in btnList" :key="index+'dtdcBtns'"
|
||||||
|
:class="{'activeBtn' : activeBtn === item.id}" @click="changeDataType(item.id)">{{ item.name }}
|
||||||
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -47,64 +52,77 @@
|
|||||||
stripe
|
stripe
|
||||||
max-height="500"
|
max-height="500"
|
||||||
style="width: 100%;margin-top:25px;">
|
style="width: 100%;margin-top:25px;">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="deviceName"
|
prop="deviceName"
|
||||||
label="设备名称">
|
label="设备名称">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="告警等级"
|
label="告警等级"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{$store.state.ems.alarmLevelOptions[scope.row.alarmLevel]}}</span>
|
<span>{{ $store.state.ems.alarmLevelOptions[scope.row.alarmLevel] }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="alarmContent"
|
prop="alarmContent"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
label="告警内容">
|
label="告警内容">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="alarmStartTime"
|
prop="alarmStartTime"
|
||||||
label="告警发生时间">
|
label="告警发生时间">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{formatDate(scope.row.alarmStartTime,true)}}</span>
|
<span>{{ formatDate(scope.row.alarmStartTime, true) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="alarmEndTime"
|
prop="alarmEndTime"
|
||||||
label="告警结束时间">
|
label="告警结束时间">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{formatDate(scope.row.alarmEndTime,true)}}</span>
|
<span>{{ formatDate(scope.row.alarmEndTime, true) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="状态">
|
label="状态">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span :class="['0','2'].includes(scope.row.status) ? 'warning-status' : ''">{{$store.state.ems.alarmStatusOptions[scope.row.status]}}</span>
|
<span
|
||||||
</template>
|
:class="['0','2'].includes(scope.row.status) ? 'warning-status' : ''">{{
|
||||||
</el-table-column>
|
$store.state.ems.alarmStatusOptions[scope.row.status]
|
||||||
<el-table-column
|
}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
label="工单"
|
label="工单"
|
||||||
fixed="right"
|
fixed="right"
|
||||||
width="250"
|
width="320"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" size="mini" v-if="scope.row.ticketNo" @click="toTicket">已生成工单(工单号:{{scope.row.ticketNo}})</el-button>
|
<el-button type="text" size="mini" v-if="scope.row.ticketNo" @click="toTicket">
|
||||||
<el-button type="primary" size="mini" v-else @click="createTicket(scope.row.id)">生成工单</el-button>
|
已生成工单(工单号:{{ scope.row.ticketNo }})
|
||||||
</template>
|
</el-button>
|
||||||
</el-table-column>
|
<el-button type="primary" size="mini" v-else @click="createTicket(scope.row.id)">生成工单</el-button>
|
||||||
</el-table>
|
<el-button
|
||||||
|
v-if="scope.row.status !== '1'"
|
||||||
|
type="success"
|
||||||
|
size="mini"
|
||||||
|
style="margin-left: 8px;"
|
||||||
|
@click="closeAlarmRecord(scope.row.id)">
|
||||||
|
确认关闭
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-show="tableData.length>0"
|
v-show="tableData.length>0"
|
||||||
background
|
background
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
:current-page="pageNum"
|
:current-page="pageNum"
|
||||||
:page-size="pageSize"
|
:page-size="pageSize"
|
||||||
:page-sizes="[10, 20, 30, 40]"
|
:page-sizes="[10, 20, 30, 40]"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
:total="totalSize"
|
:total="totalSize"
|
||||||
style="margin-top:15px;text-align: center"
|
style="margin-top:15px;text-align: center"
|
||||||
>
|
>
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
@ -113,117 +131,147 @@
|
|||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getAlarmDetailList,createTicketNo} from'@/api/ems/dzjk'
|
import {closeAlarm, createTicketNo, getAlarmDetailList} from '@/api/ems/dzjk'
|
||||||
import {getDeviceList} from'@/api/ems/site'
|
import {getDeviceList} from '@/api/ems/site'
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { formatDate } from '@/filters/ems'
|
import {formatDate} from '@/filters/ems'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkGzgj',
|
name: 'DzjkGzgj',
|
||||||
mixins:[getQuerySiteId],
|
mixins: [getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
btnList:[
|
btnList: [
|
||||||
{name:'未处理告警',id:'today'},
|
{name: '未处理告警', id: 'today'},
|
||||||
{name:'历史告警',id:'history'},
|
{name: '历史告警', id: 'history'},
|
||||||
],
|
],
|
||||||
deviceOptions:[],//设备列表
|
deviceOptions: [],//设备列表
|
||||||
pickerOptions:{
|
pickerOptions: {
|
||||||
disabledDate(time) {
|
disabledDate(time) {
|
||||||
return time.getTime() > Date.now();
|
return time.getTime() > Date.now();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultDateRange:[],//默认展示的时间
|
defaultDateRange: [],//默认展示的时间
|
||||||
dateRange:[],//alarmStartTime,alarmEndTime
|
dateRange: [],//alarmStartTime,alarmEndTime
|
||||||
activeBtn:'today',
|
activeBtn: 'today',
|
||||||
search:{deviceId:'',alarmLevel:''},
|
search: {deviceId: '', alarmLevel: ''},
|
||||||
// 表格、分页
|
// 表格、分页
|
||||||
tableData:[],
|
tableData: [],
|
||||||
pageSize:10,//分页栏当前每个数据总数
|
pageSize: 10,//分页栏当前每个数据总数
|
||||||
pageNum:1,//分页栏当前页数
|
pageNum: 1,//分页栏当前页数
|
||||||
totalSize:0,//table表格数据总数
|
totalSize: 0,//table表格数据总数
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
formatDate,
|
formatDate,
|
||||||
toTicket(){
|
toTicket() {
|
||||||
this.$router.push({path:'/ticket'})
|
this.$router.push({path: '/ticket'})
|
||||||
},
|
},
|
||||||
//生成工单
|
//生成工单
|
||||||
createTicket(id){
|
createTicket(id) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
createTicketNo({id}).then(response=>{
|
createTicketNo({id}).then(response => {
|
||||||
response?.data && this.toTicket()
|
response?.data && this.toTicket()
|
||||||
}).finally(()=>{this.loading = false})
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
//确认关闭告警
|
||||||
|
closeAlarmRecord(id) {
|
||||||
|
this.$confirm('确认关闭该故障告警吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.loading = true
|
||||||
|
closeAlarm({id}).then(() => {
|
||||||
|
this.$message.success('关闭成功')
|
||||||
|
this.getData()
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// 判断是否是同一天
|
// 判断是否是同一天
|
||||||
isSameDay(day1, day2) {
|
isSameDay(day1, day2) {
|
||||||
const date1 = new Date(day1),date2 = new Date(day2)
|
const date1 = new Date(day1), date2 = new Date(day2)
|
||||||
return date1.getFullYear() === date2.getFullYear() &&
|
return date1.getFullYear() === date2.getFullYear() &&
|
||||||
date1.getMonth() === date2.getMonth() &&
|
date1.getMonth() === date2.getMonth() &&
|
||||||
date1.getDate() === date2.getDate();
|
date1.getDate() === date2.getDate();
|
||||||
},
|
},
|
||||||
// 分页
|
// 分页
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCurrentChange(val) {
|
handleCurrentChange(val) {
|
||||||
this.pageNum = val
|
this.pageNum = val
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 搜索
|
// 搜索
|
||||||
onSearch(){
|
onSearch() {
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 重置
|
// 重置
|
||||||
onReset(){
|
onReset() {
|
||||||
this.search={deviceId:'',alarmLevel:''}
|
this.search = {deviceId: '', alarmLevel: ''}
|
||||||
this.dateRange=[]
|
this.dateRange = []
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 切换今日、历史告警
|
// 切换今日、历史告警
|
||||||
changeDataType(id){
|
changeDataType(id) {
|
||||||
if(id !== this.activeBtn){
|
if (id !== this.activeBtn) {
|
||||||
console.log('点击了不同的菜单,更新数据')
|
console.log('点击了不同的菜单,更新数据')
|
||||||
this.activeBtn=id;
|
this.activeBtn = id;
|
||||||
this.getData()
|
this.getData()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 获取数据
|
// 获取数据
|
||||||
getData(){
|
getData() {
|
||||||
this.$store.dispatch('getSiteAlarmNum',this.siteId)
|
this.$store.dispatch('getSiteAlarmNum', this.siteId)
|
||||||
this.loading=true
|
this.loading = true
|
||||||
const {deviceId,alarmLevel} = this.search
|
const {deviceId, alarmLevel} = this.search
|
||||||
const {siteId,pageNum,pageSize,activeBtn} =this
|
const {siteId, pageNum, pageSize, activeBtn} = this
|
||||||
const [alarmStartTime='',alarmEndTime='']=(this.dateRange || [])
|
const [alarmStartTime = '', alarmEndTime = ''] = (this.dateRange || [])
|
||||||
let status = activeBtn === 'today' ? '0' : '1,2'
|
let status = activeBtn === 'today' ? '0' : '1,2'
|
||||||
getAlarmDetailList({status,deviceId,alarmLevel,siteId,pageSize,pageNum,alarmStartTime:formatDate(alarmStartTime),alarmEndTime:formatDate(alarmEndTime)}).then(response => {
|
getAlarmDetailList({
|
||||||
this.tableData=response?.rows || [];
|
status,
|
||||||
|
deviceId,
|
||||||
|
alarmLevel,
|
||||||
|
siteId,
|
||||||
|
pageSize,
|
||||||
|
pageNum,
|
||||||
|
alarmStartTime: formatDate(alarmStartTime),
|
||||||
|
alarmEndTime: formatDate(alarmEndTime)
|
||||||
|
}).then(response => {
|
||||||
|
this.tableData = response?.rows || [];
|
||||||
this.totalSize = response?.total || 0
|
this.totalSize = response?.total || 0
|
||||||
}).finally(() => {this.loading=false})
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
getDeviceOptions(){
|
getDeviceOptions() {
|
||||||
getDeviceList(this.siteId).then(response => {
|
getDeviceList(this.siteId).then(response => {
|
||||||
this.deviceOptions = JSON.parse(JSON.stringify(response?.data || []))
|
this.deviceOptions = JSON.parse(JSON.stringify(response?.data || []))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
init(){
|
init() {
|
||||||
this.getDeviceOptions()
|
this.getDeviceOptions()
|
||||||
this.onReset()
|
this.onReset()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||||
this.defaultDateRange = [lastMonth, now];
|
this.defaultDateRange = [lastMonth, now];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,30 +1,42 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
|
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<span class="card-title">当日功率曲线</span>
|
<span class="card-title">当日功率曲线</span>
|
||||||
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/>
|
<date-range-select ref="dateRangeSelect" :showIcon="true" :mini-time-picker="true" @updateDate="updateDate"/>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 310px" id="activeChart"></div>
|
<div style="height: 310px" id="activeChart"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import resize from '@/mixins/ems/resize'
|
import resize from '@/mixins/ems/resize'
|
||||||
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
|
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
|
||||||
import { getPointData } from '@/api/ems/dzjk'
|
import {getPointConfigCurve} from '@/api/ems/site'
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize,intervalUpdate],
|
mixins: [resize, intervalUpdate],
|
||||||
components: {DateRangeSelect},
|
components: {DateRangeSelect},
|
||||||
|
props: {
|
||||||
|
displayData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
chart: null,
|
||||||
timeRange:[],
|
timeRange: [],
|
||||||
siteId:'',
|
siteId: '',
|
||||||
isInit:true
|
isInit: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
displayData() {
|
||||||
|
if (this.siteId && this.timeRange.length === 2) {
|
||||||
|
this.getGVQXData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -41,23 +53,46 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 更新时间范围 重置图表
|
// 更新时间范围 重置图表
|
||||||
updateDate(data){
|
updateDate(data) {
|
||||||
this.timeRange=data
|
this.timeRange = data
|
||||||
!this.isInit && this.getGVQXData()
|
!this.isInit && this.getGVQXData()
|
||||||
this.isInit = false
|
this.isInit = false
|
||||||
},
|
},
|
||||||
getGVQXData(){
|
getGVQXData() {
|
||||||
this.showLoading()
|
const {siteId, timeRange} = this
|
||||||
const {siteId,timeRange}=this
|
const displayData = this.displayData || []
|
||||||
getPointData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => {
|
const sectionRows = displayData.filter(item =>
|
||||||
this.setOption(response?.data || [])
|
item && item.sectionName === '当日功率曲线' && item.useFixedDisplay !== 1 && item.dataPoint
|
||||||
}).finally(()=>this.hideLoading())
|
)
|
||||||
|
const tasks = sectionRows.map(row => {
|
||||||
|
const pointId = String(row.dataPoint || '').trim()
|
||||||
|
if (!pointId) return Promise.resolve(null)
|
||||||
|
return getPointConfigCurve({
|
||||||
|
siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: 'data',
|
||||||
|
rangeType: 'custom',
|
||||||
|
startTime: this.normalizeDateTime(timeRange[0], false),
|
||||||
|
endTime: this.normalizeDateTime(timeRange[1], true)
|
||||||
|
}).then(curveResponse => {
|
||||||
|
const list = curveResponse?.data || []
|
||||||
|
return {
|
||||||
|
name: row.fieldName || row.fieldCode || pointId,
|
||||||
|
data: list
|
||||||
|
.map(item => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
|
||||||
|
.filter(item => item[0] && !Number.isNaN(item[1]))
|
||||||
|
}
|
||||||
|
}).catch(() => null)
|
||||||
|
})
|
||||||
|
Promise.all(tasks).then(series => {
|
||||||
|
this.setOption((series || []).filter(Boolean))
|
||||||
|
})
|
||||||
},
|
},
|
||||||
init(siteId){
|
init(siteId) {
|
||||||
//初始化 清空数据
|
//初始化 清空数据
|
||||||
this.siteId = siteId
|
this.siteId = siteId
|
||||||
this.isInit = true
|
this.isInit = true
|
||||||
this.timeRange=[]
|
this.timeRange = []
|
||||||
this.$refs.dateRangeSelect.init(true)
|
this.$refs.dateRangeSelect.init(true)
|
||||||
this.getGVQXData()
|
this.getGVQXData()
|
||||||
this.updateInterval(this.getGVQXData)
|
this.updateInterval(this.getGVQXData)
|
||||||
@ -65,18 +100,18 @@ export default {
|
|||||||
initChart() {
|
initChart() {
|
||||||
this.chart = echarts.init(document.querySelector('#activeChart'))
|
this.chart = echarts.init(document.querySelector('#activeChart'))
|
||||||
},
|
},
|
||||||
showLoading(){
|
normalizeDateTime(value, endOfDay) {
|
||||||
this.chart && this.chart.showLoading()
|
const raw = String(value || '').trim()
|
||||||
|
if (!raw) return ''
|
||||||
|
if (raw.includes(' ')) return raw
|
||||||
|
return `${raw} ${endOfDay ? '23:59:59' : '00:00:00'}`
|
||||||
},
|
},
|
||||||
hideLoading(){
|
parseToTimestamp(value) {
|
||||||
this.chart && this.chart.hideLoading()
|
if (!value) return null
|
||||||
|
const t = new Date(value).getTime()
|
||||||
|
return Number.isNaN(t) ? null : t
|
||||||
},
|
},
|
||||||
setOption(data) {
|
setOption(seriesData = []) {
|
||||||
const source = [['日期','电网功率','负载功率','储能功率','光伏功率','soc平均值','soh平均值','电池平均温度平均值']]
|
|
||||||
console.log('source.slice(1)',source[0].slice(1))
|
|
||||||
this.chart && data.forEach((item)=>{
|
|
||||||
source.push([item.statisDate,item.gridPower,item.loadPower,item.storagePower,item.pvPower,item.avgSoc,item.avgSoh,item.avgTemp])
|
|
||||||
})
|
|
||||||
this.chart.setOption({
|
this.chart.setOption({
|
||||||
grid: {
|
grid: {
|
||||||
containLabel: true
|
containLabel: true
|
||||||
@ -87,29 +122,28 @@ export default {
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { // 坐标轴指示器,坐标轴触发有效
|
axisPointer: { type: 'cross' }
|
||||||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
textStyle:{
|
textStyle: {
|
||||||
color:"#333333",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'time',
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [{
|
||||||
{
|
type: 'value',
|
||||||
type: 'value',
|
}],
|
||||||
},
|
series: seriesData.map((item) => {
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
dataset:{source},
|
|
||||||
series: source[0].slice(1).map((item,index)=>{
|
|
||||||
return {
|
return {
|
||||||
|
name: item.name,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
yAxisIndex:index<=4 ? 0 : 1
|
showSymbol: false,
|
||||||
|
symbolSize: 2,
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.5,
|
||||||
|
},
|
||||||
|
data: item.data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -119,4 +153,3 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,27 +1,40 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
|
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<span class="card-title">一周充放曲线</span>
|
<span class="card-title">一周充放曲线</span>
|
||||||
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/>
|
<date-range-select ref="dateRangeSelect" :showIcon="true" :mini-time-picker="true" @updateDate="updateDate"/>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 310px" id="weekChart"></div>
|
<div style="height: 310px" id="weekChart"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import resize from '@/mixins/ems/resize'
|
import resize from '@/mixins/ems/resize'
|
||||||
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
|
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
|
||||||
import {getSevenChargeData} from '@/api/ems/dzjk'
|
import {getPointConfigCurve} from '@/api/ems/site'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize],
|
mixins: [resize],
|
||||||
components: {DateRangeSelect},
|
components: {DateRangeSelect},
|
||||||
|
props: {
|
||||||
|
displayData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
chart: null,
|
||||||
timeRange:[],
|
timeRange: [],
|
||||||
siteId:'',
|
siteId: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
displayData() {
|
||||||
|
if (this.siteId && this.timeRange.length === 2) {
|
||||||
|
this.getWeekKData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -38,46 +51,67 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 更新时间范围 重置图表
|
// 更新时间范围 重置图表
|
||||||
updateDate(data){
|
updateDate(data) {
|
||||||
this.timeRange=data
|
this.timeRange = data
|
||||||
this.getWeekKData()
|
this.getWeekKData()
|
||||||
},
|
},
|
||||||
getWeekKData(){
|
getWeekKData() {
|
||||||
this.showLoading()
|
const {siteId, timeRange} = this
|
||||||
const {siteId,timeRange}=this
|
const displayData = this.displayData || []
|
||||||
this.hideLoading()
|
const sectionRows = displayData.filter(item =>
|
||||||
getSevenChargeData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => {
|
item && item.sectionName === '一周充放曲线' && item.useFixedDisplay !== 1 && item.dataPoint
|
||||||
this.setOption(response?.data || [])
|
)
|
||||||
}).finally(()=>this.hideLoading())
|
const tasks = sectionRows.map(row => {
|
||||||
|
const pointId = String(row.dataPoint || '').trim()
|
||||||
|
if (!pointId) return Promise.resolve(null)
|
||||||
|
return getPointConfigCurve({
|
||||||
|
siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: 'data',
|
||||||
|
rangeType: 'custom',
|
||||||
|
startTime: this.normalizeDateTime(timeRange[0], false),
|
||||||
|
endTime: this.normalizeDateTime(timeRange[1], true)
|
||||||
|
}).then(curveResponse => {
|
||||||
|
const list = curveResponse?.data || []
|
||||||
|
return {
|
||||||
|
name: row.fieldName || row.fieldCode || pointId,
|
||||||
|
data: list
|
||||||
|
.map(item => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
|
||||||
|
.filter(item => item[0] && !Number.isNaN(item[1]))
|
||||||
|
}
|
||||||
|
}).catch(() => null)
|
||||||
|
})
|
||||||
|
Promise.all(tasks).then(series => {
|
||||||
|
this.setOption((series || []).filter(Boolean))
|
||||||
|
})
|
||||||
},
|
},
|
||||||
init(siteId){
|
init(siteId) {
|
||||||
//初始化 清空数据
|
//初始化 清空数据
|
||||||
this.siteId = siteId
|
this.siteId = siteId
|
||||||
this.timeRange=[]
|
this.timeRange = []
|
||||||
this.deviceId=''
|
this.deviceId = ''
|
||||||
this.$refs.dateRangeSelect.init()
|
this.$refs.dateRangeSelect.init()
|
||||||
},
|
},
|
||||||
initChart() {
|
initChart() {
|
||||||
this.chart = echarts.init(document.querySelector('#weekChart'))
|
this.chart = echarts.init(document.querySelector('#weekChart'))
|
||||||
},
|
},
|
||||||
showLoading(){
|
normalizeDateTime(value, endOfDay) {
|
||||||
this.chart && this.chart.showLoading()
|
const raw = String(value || '').trim()
|
||||||
|
if (!raw) return ''
|
||||||
|
if (raw.includes(' ')) return raw
|
||||||
|
return `${raw} ${endOfDay ? '23:59:59' : '00:00:00'}`
|
||||||
},
|
},
|
||||||
hideLoading(){
|
parseToTimestamp(value) {
|
||||||
this.chart && this.chart.hideLoading()
|
if (!value) return null
|
||||||
|
const t = new Date(value).getTime()
|
||||||
|
return Number.isNaN(t) ? null : t
|
||||||
},
|
},
|
||||||
setOption(data,unit) {
|
setOption(seriesData = []) {
|
||||||
const source = [['日期','充电量','放电量']]
|
|
||||||
data.forEach(item=>{
|
|
||||||
source.push([item.ammeterDate, item.chargedCap,item.disChargedCap])
|
|
||||||
})
|
|
||||||
this.chart && this.chart.setOption({
|
this.chart && this.chart.setOption({
|
||||||
color:['#4472c4','#70ad47'],//所有充放电颜色保持统一
|
color: ['#4472c4', '#70ad47'],//所有充放电颜色保持统一
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: { // 坐标轴指示器,坐标轴触发有效
|
axisPointer: { type: 'cross' }
|
||||||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
containLabel: true
|
containLabel: true
|
||||||
@ -87,33 +121,24 @@ export default {
|
|||||||
bottom: '15',
|
bottom: '15',
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'time'
|
||||||
name:unit,
|
|
||||||
nameLocation:'center'
|
|
||||||
},
|
},
|
||||||
yAxis: [{
|
yAxis: [{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
name:'充电量/放电量kWh',
|
name: '充电量/放电量kWh',
|
||||||
axisLine: {
|
axisLine: {
|
||||||
lineStyle:{
|
lineStyle: {
|
||||||
color: '#333333',
|
color: '#333333',
|
||||||
},
|
},
|
||||||
onZero:false
|
onZero: false
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
dataset:{
|
series: seriesData.map(item => ({
|
||||||
source
|
name: item.name,
|
||||||
},
|
yAxisIndex: 0,
|
||||||
series: [
|
type: 'bar',
|
||||||
{
|
data: item.data
|
||||||
yAxisIndex:0,
|
}))
|
||||||
type: 'bar',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
yAxisIndex:0,
|
|
||||||
type: 'bar',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,167 +1,552 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<el-row style="background: #fff" class="row-container" :gutter="15">
|
<el-row style="background: #fff" class="row-container" :gutter="15">
|
||||||
<el-col v-if="tableData.length > 0" :xs="24" :sm="24" :lg="24">
|
<el-col :xs="24" :sm="24" :lg="5">
|
||||||
<alarm-table :tableData="tableData" />
|
<!-- 站点信息-->
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="24" :lg="6">
|
|
||||||
<el-card
|
<el-card
|
||||||
shadow="always"
|
shadow="always"
|
||||||
class="common-card-container common-card-container-body-no-padding"
|
class="common-card-container common-card-container-body-no-padding"
|
||||||
>
|
>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="card-title">站点信息</span>
|
<span class="card-title">站点信息</span>
|
||||||
|
<div class="alarm-msg" v-if="tableData.length > 0" @click="toAlarm">
|
||||||
|
<i class="el-icon-message-solid"></i> 设备告警
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style="box-sizing: border-box; height: 250px; padding: 20px 15px"
|
style="box-sizing: border-box; height: 218px; padding: 20px 15px"
|
||||||
>
|
>
|
||||||
<el-descriptions class="home-normal-info" :column="1">
|
<!-- 地址、运行时间-->
|
||||||
<el-descriptions-item
|
<div class="site-info site-info-address">
|
||||||
size="mini"
|
<div class="title">
|
||||||
v-for="(item, index) in singleZdInfo"
|
<i class="el-icon-location"></i>
|
||||||
:key="index + 'singleZdInfo'"
|
</div>
|
||||||
:label="item.title"
|
<div class="value">
|
||||||
>{{ info[item.attr] | formatNumber }}</el-descriptions-item
|
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
|
||||||
>
|
<span v-else>{{ info.siteAddress || '-' }}</span>
|
||||||
</el-descriptions>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
<div class="site-info">
|
||||||
</el-col>
|
<div class="title">
|
||||||
<el-col :xs="24" :sm="24" :lg="8">
|
<i class="el-icon-date"></i>
|
||||||
<el-card
|
</div>
|
||||||
shadow="always"
|
<div class="value">
|
||||||
class="common-card-container common-card-container-body-no-padding"
|
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
|
||||||
>
|
<span v-else>{{ info.runningTime || '-' }}</span>
|
||||||
<div slot="header">
|
</div>
|
||||||
<span class="card-title">总累计运行数据</span>
|
</div>
|
||||||
</div>
|
<!-- 装机功率、容量 -->
|
||||||
<div
|
<el-row :gutter="10" style="margin-top:20px;">
|
||||||
style="box-sizing: border-box; height: 250px; padding: 20px 15px"
|
|
||||||
>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col
|
<el-col
|
||||||
:span="12"
|
:span="12"
|
||||||
v-for="(item, index) in sjglData"
|
class="sjgl-col power-col"
|
||||||
:key="index + 'sjglData'"
|
|
||||||
class="sjgl-data"
|
|
||||||
>
|
>
|
||||||
<div class="sjgl-title">{{ item.title }}</div>
|
<div class="sjgl-wrapper">
|
||||||
<div class="sjgl-value">
|
<div class="sjgl-title">装机功率(MWh)</div>
|
||||||
{{ runningInfo[item.attr] | formatNumber }}
|
<div class="sjgl-value">
|
||||||
|
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
|
||||||
|
<span v-else>{{ info.installPower | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
class="sjgl-col power-col"
|
||||||
|
>
|
||||||
|
<div class="sjgl-wrapper">
|
||||||
|
<div class="sjgl-title">装机容量(MWh)</div>
|
||||||
|
<div class="sjgl-value">
|
||||||
|
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
|
||||||
|
<span v-else>{{ info.installCapacity | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="24" :lg="10">
|
<!-- 总累计运行数据-->
|
||||||
<cl-info :info="runningInfo.strategyTempInfo" />
|
<el-col :xs="24" :sm="24" :lg="19">
|
||||||
|
<el-card
|
||||||
|
shadow="always"
|
||||||
|
class="common-card-container common-card-container-body-no-padding"
|
||||||
|
>
|
||||||
|
<div slot="header">
|
||||||
|
<span class="card-title">总累计运行数据</span>
|
||||||
|
<div class="total-count">
|
||||||
|
<span class="title pointer-field" @click="handleTotalRevenueClick">总收入</span>
|
||||||
|
<span
|
||||||
|
class="value pointer-field"
|
||||||
|
:class="{ 'field-disabled': !hasPointId(totalRevenueDisplayItem) }"
|
||||||
|
@click="handleTotalRevenueClick"
|
||||||
|
>
|
||||||
|
<i v-if="isRunningInfoLoading" class="el-icon-loading"></i>
|
||||||
|
<span v-else>{{ totalRevenueDisplayValue | formatNumber }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="unit">元</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="box-sizing: border-box; height: 218px; padding: 20px 15px"
|
||||||
|
>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col
|
||||||
|
:span="6"
|
||||||
|
v-for="(item, index) in runningDataCards"
|
||||||
|
:key="index + 'sjglData'"
|
||||||
|
class="sjgl-col"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sjgl-wrapper pointer-field"
|
||||||
|
:class="{ 'field-disabled': !hasPointId(item.raw) }"
|
||||||
|
@click="handleRunningFieldClick(item)"
|
||||||
|
>
|
||||||
|
<div class="sjgl-title">{{ item.title }}</div>
|
||||||
|
<div class="sjgl-value" :style="{color:item.color}">
|
||||||
|
<i v-if="item.loading" class="el-icon-loading"></i>
|
||||||
|
<span v-else>{{ item.value | formatNumber }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="24" :lg="24">
|
<el-col :xs="24" :sm="24" :lg="12">
|
||||||
<week-chart ref="weekChart" />
|
<week-chart ref="weekChart" :display-data="runningDisplayData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="24" :lg="24">
|
<el-col :xs="24" :sm="24" :lg="12">
|
||||||
<active-chart ref="activeChart" />
|
<active-chart ref="activeChart" :display-data="runningDisplayData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getSingleSiteBaseInfo } from "@/api/ems/zddt";
|
import * as echarts from "echarts";
|
||||||
import { getDzjkHomeView } from "@/api/ems/dzjk";
|
import {getSingleSiteBaseInfo} from "@/api/ems/zddt";
|
||||||
|
import {getDzjkHomeTotalView, getProjectDisplayData} from "@/api/ems/dzjk";
|
||||||
|
import {getPointConfigCurve} from "@/api/ems/site";
|
||||||
import WeekChart from "./WeekChart.vue";
|
import WeekChart from "./WeekChart.vue";
|
||||||
import ActiveChart from "./ActiveChart.vue";
|
import ActiveChart from "./ActiveChart.vue";
|
||||||
import AlarmTable from "./AlarmTable.vue";
|
import AlarmTable from "./AlarmTable.vue";
|
||||||
import ClInfo from "./ClInfo.vue";
|
import ClInfo from "./ClInfo.vue";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkSbjkHome",
|
name: "DzjkSbjkHome",
|
||||||
components: { WeekChart, ActiveChart, AlarmTable, ClInfo },
|
components: {WeekChart, ActiveChart, AlarmTable, ClInfo},
|
||||||
mixins: [getQuerySiteId, intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
singleZdInfo: [
|
baseInfoLoading: false,
|
||||||
{
|
runningInfoLoading: false,
|
||||||
title: "电站位置",
|
runningUpdateSpinning: false,
|
||||||
attr: "siteAddress",
|
runningUpdateTimer: null,
|
||||||
},
|
curveDialogVisible: false,
|
||||||
{
|
curveDialogTitle: "点位曲线",
|
||||||
title: "投运时间",
|
curveChart: null,
|
||||||
attr: "runningTime",
|
curveLoading: false,
|
||||||
},
|
curveCustomRange: [],
|
||||||
{
|
curveQuery: {
|
||||||
title: "装机功率(MW)",
|
siteId: "",
|
||||||
attr: "installPower",
|
pointId: "",
|
||||||
},
|
pointType: "data",
|
||||||
{
|
rangeType: "custom",
|
||||||
title: "装机容量(MW)",
|
startTime: "",
|
||||||
attr: "installCapacity",
|
endTime: "",
|
||||||
},
|
},
|
||||||
],
|
fallbackSjglData: [
|
||||||
sjglData: [
|
|
||||||
{
|
{
|
||||||
title: "今日充电量(kWh)",
|
title: "今日充电量(kWh)",
|
||||||
attr: "dayChargedCap",
|
attr: "dayChargedCap",
|
||||||
|
color: '#4472c4'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "今日放电量(kWh)",
|
title: "今日放电量(kWh)",
|
||||||
attr: "dayDisChargedCap",
|
attr: "dayDisChargedCap",
|
||||||
|
color: '#70ad47'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "总充电量(kWh)",
|
title: "总充电量(kWh)",
|
||||||
attr: "totalChargedCap",
|
attr: "totalChargedCap",
|
||||||
|
color: '#4472c4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "今日实时收入(元)",
|
||||||
|
attr: "dayRevenue",
|
||||||
|
color: '#f67438'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "昨日充电量(kWh)",
|
||||||
|
attr: "yesterdayChargedCap",
|
||||||
|
color: '#4472c4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "昨日放电量(kWh)",
|
||||||
|
attr: "yesterdayDisChargedCap",
|
||||||
|
color: '#70ad47'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "总放电量(kWh)",
|
title: "总放电量(kWh)",
|
||||||
attr: "totalDischargedCap",
|
attr: "totalDischargedCap",
|
||||||
|
color: '#70ad47'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "总收入(元)",
|
title: "昨日实时收入(元)",
|
||||||
attr: "totalRevenue",
|
attr: "yesterdayRevenue",
|
||||||
},
|
color: '#f67438'
|
||||||
{
|
|
||||||
title: "当日实时收入(元)",
|
|
||||||
attr: "dayRevenue",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
info: {}, //基本信息
|
info: {}, //基本信息
|
||||||
runningInfo: {}, //总累计运行数据+报警表格
|
runningInfo: {}, //总累计运行数据+报警表格
|
||||||
|
runningDisplayData: [], //单站监控项目配置展示数据
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isBaseInfoLoading() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
isRunningInfoLoading() {
|
||||||
|
const state = this.$data || {};
|
||||||
|
return !!(state.runningInfoLoading || state.runningUpdateSpinning || state.loading);
|
||||||
|
},
|
||||||
tableData() {
|
tableData() {
|
||||||
console.log(
|
|
||||||
"this.runningInfo?.siteMonitorHomeAlarmVo ",
|
|
||||||
this.runningInfo?.siteMonitorHomeAlarmVo
|
|
||||||
);
|
|
||||||
return this.runningInfo?.siteMonitorHomeAlarmVo || [];
|
return this.runningInfo?.siteMonitorHomeAlarmVo || [];
|
||||||
},
|
},
|
||||||
|
totalRunningSectionData() {
|
||||||
|
return (this.runningDisplayData || []).filter(item => item.sectionName === "总累计运行数据");
|
||||||
|
},
|
||||||
|
totalRevenueDisplayItem() {
|
||||||
|
const sectionData = this.totalRunningSectionData || [];
|
||||||
|
const byFieldCode = sectionData.find(item => item.fieldCode === "totalRevenue");
|
||||||
|
if (byFieldCode) {
|
||||||
|
return byFieldCode;
|
||||||
|
}
|
||||||
|
return sectionData.find(item => item.fieldName === "总收入");
|
||||||
|
},
|
||||||
|
totalRevenueDisplayValue() {
|
||||||
|
return this.totalRevenueDisplayItem ? this.totalRevenueDisplayItem.fieldValue : this.runningInfo.totalRevenue;
|
||||||
|
},
|
||||||
|
runningDataCards() {
|
||||||
|
const sectionData = this.totalRunningSectionData || [];
|
||||||
|
if (sectionData.length > 0) {
|
||||||
|
const revenueFieldCode = this.totalRevenueDisplayItem ? this.totalRevenueDisplayItem.fieldCode : "";
|
||||||
|
return sectionData
|
||||||
|
.filter(item => item.fieldCode !== revenueFieldCode)
|
||||||
|
.map((item, index) => ({
|
||||||
|
title: item.fieldName,
|
||||||
|
value: item.fieldValue,
|
||||||
|
color: this.getCardColor(index),
|
||||||
|
loading: this.isRunningInfoLoading,
|
||||||
|
raw: item,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return this.fallbackSjglData.map(item => ({
|
||||||
|
title: item.title,
|
||||||
|
value: this.runningInfo[item.attr],
|
||||||
|
color: item.color,
|
||||||
|
loading: this.isRunningInfoLoading,
|
||||||
|
raw: item,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.runningUpdateTimer) {
|
||||||
|
clearTimeout(this.runningUpdateTimer);
|
||||||
|
this.runningUpdateTimer = null;
|
||||||
|
}
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
hasPointId(item) {
|
||||||
|
return !!String(item?.dataPoint || "").trim();
|
||||||
|
},
|
||||||
|
handleTotalRevenueClick() {
|
||||||
|
const item = this.totalRevenueDisplayItem;
|
||||||
|
const pointId = String(item?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openCurveDialog({
|
||||||
|
pointId,
|
||||||
|
title: item?.fieldName || "总收入",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleRunningFieldClick(card) {
|
||||||
|
const item = card?.raw || {};
|
||||||
|
const pointId = String(item?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openCurveDialog({
|
||||||
|
pointId,
|
||||||
|
title: card?.title || item?.fieldName || item?.fieldCode || pointId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openCurveDialog({pointId, title}) {
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || pointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const pad = (num) => String(num).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query).then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
}).catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
}).finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map(item => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map(item => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setBaseInfoLoading(loading) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.$data, "baseInfoLoading")) {
|
||||||
|
this.baseInfoLoading = loading;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$set(this.$data, "baseInfoLoading", loading);
|
||||||
|
},
|
||||||
|
setRunningInfoLoading(loading) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.$data, "runningInfoLoading")) {
|
||||||
|
this.runningInfoLoading = loading;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$set(this.$data, "runningInfoLoading", loading);
|
||||||
|
},
|
||||||
|
getCardColor(index) {
|
||||||
|
const colors = ['#4472c4', '#70ad47', '#4472c4', '#f67438', '#4472c4', '#70ad47', '#70ad47', '#f67438'];
|
||||||
|
return colors[index % colors.length];
|
||||||
|
},
|
||||||
|
toAlarm() {
|
||||||
|
this.$router.push({path: "/dzjk/gzgj", query: this.$route.query});
|
||||||
|
},
|
||||||
getBaseInfo() {
|
getBaseInfo() {
|
||||||
return getSingleSiteBaseInfo(this.siteId).then((response) => {
|
return getSingleSiteBaseInfo(this.siteId).then((response) => {
|
||||||
this.info = response?.data || {};
|
this.info = response?.data || {};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getRunningInfo() {
|
getRunningInfo() {
|
||||||
return getDzjkHomeView(this.siteId).then((response) => {
|
const hasOldData = Object.keys(this.runningInfo || {}).length > 0 || (this.runningDisplayData || []).length > 0;
|
||||||
this.runningInfo = response?.data || {};
|
if (!hasOldData) {
|
||||||
|
this.setRunningInfoLoading(true);
|
||||||
|
}
|
||||||
|
return Promise.all([
|
||||||
|
getDzjkHomeTotalView(this.siteId),
|
||||||
|
getProjectDisplayData(this.siteId),
|
||||||
|
]).then(([homeResponse, displayResponse]) => {
|
||||||
|
const nextRunningInfo = homeResponse?.data || {};
|
||||||
|
const nextRunningDisplayData = displayResponse?.data || [];
|
||||||
|
const changed = hasOldData && this.hasTotalRunningChanged(nextRunningInfo, nextRunningDisplayData);
|
||||||
|
this.runningInfo = nextRunningInfo;
|
||||||
|
this.runningDisplayData = nextRunningDisplayData;
|
||||||
|
if (changed) {
|
||||||
|
this.triggerRunningUpdateSpinner();
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
if (!hasOldData) {
|
||||||
|
this.setRunningInfoLoading(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
triggerRunningUpdateSpinner() {
|
||||||
|
if (this.runningUpdateTimer) {
|
||||||
|
clearTimeout(this.runningUpdateTimer);
|
||||||
|
}
|
||||||
|
this.runningUpdateSpinning = true;
|
||||||
|
this.runningUpdateTimer = setTimeout(() => {
|
||||||
|
this.runningUpdateSpinning = false;
|
||||||
|
this.runningUpdateTimer = null;
|
||||||
|
}, 800);
|
||||||
|
},
|
||||||
|
hasTotalRunningChanged(nextRunningInfo, nextRunningDisplayData) {
|
||||||
|
const oldSnapshot = this.getTotalRunningSnapshot(this.runningInfo, this.runningDisplayData);
|
||||||
|
const newSnapshot = this.getTotalRunningSnapshot(nextRunningInfo, nextRunningDisplayData);
|
||||||
|
return JSON.stringify(oldSnapshot) !== JSON.stringify(newSnapshot);
|
||||||
|
},
|
||||||
|
getTotalRunningSnapshot(runningInfo, runningDisplayData) {
|
||||||
|
const snapshot = {};
|
||||||
|
const sectionData = (runningDisplayData || []).filter(item => item.sectionName === "总累计运行数据");
|
||||||
|
if (sectionData.length > 0) {
|
||||||
|
sectionData.forEach(item => {
|
||||||
|
const key = item.fieldCode || item.fieldName;
|
||||||
|
if (!key) return;
|
||||||
|
snapshot[`cfg:${key}`] = this.normalizeRunningCompareValue(item.fieldValue);
|
||||||
|
});
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
this.fallbackSjglData.forEach(item => {
|
||||||
|
snapshot[`fallback:${item.attr}`] = this.normalizeRunningCompareValue(runningInfo?.[item.attr]);
|
||||||
|
});
|
||||||
|
snapshot["fallback:totalRevenue"] = this.normalizeRunningCompareValue(runningInfo?.totalRevenue);
|
||||||
|
return snapshot;
|
||||||
|
},
|
||||||
|
normalizeRunningCompareValue(value) {
|
||||||
|
if (value === null || value === undefined) return "";
|
||||||
|
if (typeof value === "number") return Number.isNaN(value) ? "" : value;
|
||||||
|
const text = String(value).trim();
|
||||||
|
if (text === "") return "";
|
||||||
|
const num = Number(text);
|
||||||
|
return Number.isNaN(num) ? text : num;
|
||||||
|
},
|
||||||
init() {
|
init() {
|
||||||
this.loading = true;
|
|
||||||
// 功率曲线
|
// 功率曲线
|
||||||
this.$refs.activeChart.init(this.siteId);
|
this.$refs.activeChart.init(this.siteId);
|
||||||
// 一周冲放曲线
|
// 一周冲放曲线
|
||||||
this.$refs.weekChart.init(this.siteId);
|
this.$refs.weekChart.init(this.siteId);
|
||||||
// 静态信息 this.getBaseInfo()
|
// 静态信息 this.getBaseInfo()
|
||||||
// 总累计运行数据+故障告警 this.getRunningInfo()
|
// 总累计运行数据+故障告警 this.getRunningInfo()
|
||||||
Promise.all([this.getBaseInfo(), this.getRunningInfo()]).finally(() => {
|
Promise.all([this.getBaseInfo(), this.getRunningInfo()]);
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
// 一分钟循环一次总累计运行数据
|
// 一分钟循环一次总累计运行数据
|
||||||
this.updateInterval(this.getRunningInfo);
|
this.updateInterval(this.getRunningInfo);
|
||||||
},
|
},
|
||||||
@ -170,6 +555,89 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
//设备告警
|
||||||
|
.alarm-msg {
|
||||||
|
background: #ff4949;
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bolder;
|
||||||
|
border-radius: 3px;
|
||||||
|
line-height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
//基本信息-地址 运行️时间
|
||||||
|
.site-info {
|
||||||
|
display: flex;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.site-info-address {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #1791ff;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #000;
|
||||||
|
font-size: 12px;
|
||||||
|
max-height: 40px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//总收入
|
||||||
|
.total-count {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: #333;
|
||||||
|
line-height: 14px;
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: #ed2f1d;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pointer-field {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.row-container {
|
.row-container {
|
||||||
& > .el-col {
|
& > .el-col {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@ -177,25 +645,47 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//数据概览
|
//数据概览
|
||||||
.sjgl-data {
|
.sjgl-col {
|
||||||
text-align: center;
|
.sjgl-wrapper {
|
||||||
&:nth-child(1),
|
text-align: left;
|
||||||
|
padding: 15px 20px;
|
||||||
|
background-color: #f2f7fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.power-col {
|
||||||
|
.sjgl-wrapper {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.sjgl-value {
|
||||||
|
color: #c44444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(4),
|
||||||
&:nth-child(2),
|
&:nth-child(2),
|
||||||
&:nth-child(3),
|
&:nth-child(3),
|
||||||
&:nth-child(4) {
|
&:nth-child(4) {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sjgl-title {
|
.sjgl-title {
|
||||||
color: #666666;
|
color: #717171;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sjgl-value {
|
.sjgl-value {
|
||||||
color: rgba(51, 51, 51, 1);
|
color: rgba(51, 51, 51, 1);
|
||||||
font-size: 26px;
|
font-size: 22px;
|
||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
font-weight: 500;
|
font-weight: bolder;
|
||||||
|
font-style: italic;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
word-wrap: break-word;
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -203,10 +693,12 @@ export default {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.home-normal-info {
|
.home-normal-info {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
.el-descriptions-item__container {
|
.el-descriptions-item__container {
|
||||||
.el-descriptions-item__label {
|
.el-descriptions-item__label {
|
||||||
color: #666666;
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-descriptions-item__content {
|
.el-descriptions-item__content {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container">
|
<div class="ems-dashboard-editor-container">
|
||||||
<zd-select :get-list-by-store="true" :default-site-id="$route.query.siteId" @submitSite="submitSite"/>
|
|
||||||
<el-menu
|
<el-menu
|
||||||
class="ems-second-menu"
|
class="ems-second-menu"
|
||||||
:default-active="$route.meta.activeSecondMenuName"
|
:default-active="$route.meta.activeSecondMenuName"
|
||||||
@ -28,11 +27,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import { dzjk } from '@/router/ems'
|
import { dzjk } from '@/router/ems'
|
||||||
const childrenRoute = dzjk[0].children[0].children//获取到单站监控下面的字路由
|
const childrenRoute = dzjk[0].children[0].children//获取到单站监控下面的字路由
|
||||||
console.log('childrenRoute',childrenRoute)
|
|
||||||
import ZdSelect from '@/components/Ems/ZdSelect/index.vue'
|
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
export default {
|
export default {
|
||||||
components:{ZdSelect},
|
|
||||||
data(){
|
data(){
|
||||||
return {
|
return {
|
||||||
childrenRoute,
|
childrenRoute,
|
||||||
@ -44,18 +40,6 @@ export default {
|
|||||||
dzjkAlarmLighting:state=>state.ems.dzjkAlarmLighting
|
dzjkAlarmLighting:state=>state.ems.dzjkAlarmLighting
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods:{
|
|
||||||
submitSite(id){
|
|
||||||
if(id !== this.$route.query.siteId){
|
|
||||||
// console.log('单站监控选择了其他的站点id=',id,'并更新页面地址参数')
|
|
||||||
this.$router.push({query:{...this.$route.query,siteId:id}})
|
|
||||||
}else{
|
|
||||||
// console.log('单站监控选择了相同的其他的站点id=',id,'页面地址不发生改变')
|
|
||||||
}
|
|
||||||
//获取告警列表数据
|
|
||||||
this.$store.dispatch('getSiteAlarmNum',id)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeRouteLeave(to,from, next){
|
beforeRouteLeave(to,from, next){
|
||||||
//从单站监控下面的所有子页面跳出时会触发
|
//从单站监控下面的所有子页面跳出时会触发
|
||||||
// 清空store中的zdList 保障下次进入到单站监控会重新调用接口获取数据
|
// 清空store中的zdList 保障下次进入到单站监控会重新调用接口获取数据
|
||||||
@ -66,6 +50,9 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.ems-dashboard-editor-container{
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
.dzjk-ems-content-container{
|
.dzjk-ems-content-container{
|
||||||
margin-top:0;
|
margin-top:0;
|
||||||
min-height: 60vh;
|
min-height: 60vh;
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<!--电位展示图表-->
|
<!--电位展示图表-->
|
||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:visible.sync="show"
|
:visible.sync="show"
|
||||||
:title="pointName"
|
:title="pointName"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
show-close
|
show-close
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
lock-scroll
|
lock-scroll
|
||||||
append-to-body
|
append-to-body
|
||||||
width="1000px"
|
width="1000px"
|
||||||
class="ems-dialog"
|
class="ems-dialog"
|
||||||
:before-close="handleClosed"
|
:before-close="handleClosed"
|
||||||
>
|
>
|
||||||
<el-card
|
<el-card
|
||||||
shadow="always"
|
shadow="always"
|
||||||
class="common-card-container common-card-container-body-no-padding time-range-card"
|
class="common-card-container common-card-container-body-no-padding time-range-card"
|
||||||
>
|
>
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<el-radio-group class="card-title" v-model="dataUnit">
|
<el-radio-group class="card-title" v-model="dataUnit">
|
||||||
@ -23,10 +23,10 @@
|
|||||||
<el-radio :label="3">天</el-radio>
|
<el-radio :label="3">天</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<date-time-select
|
<date-time-select
|
||||||
ref="dateTimeSelect"
|
ref="dateTimeSelect"
|
||||||
:data-unit="dataUnit"
|
:data-unit="dataUnit"
|
||||||
@initDate="(e) => (dataRange = e || [])"
|
@initDate="(e) => (dataRange = e || [])"
|
||||||
@updateDate="updateDate"
|
@updateDate="updateDate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 350px" id="searchChart"></div>
|
<div style="height: 350px" id="searchChart"></div>
|
||||||
@ -37,10 +37,11 @@
|
|||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import DateTimeSelect from "@/views/ems/search/DateTimeSelect.vue";
|
import DateTimeSelect from "@/views/ems/search/DateTimeSelect.vue";
|
||||||
import { getPointValueList } from "@/api/ems/search";
|
import {getPointValueList} from "@/api/ems/search";
|
||||||
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { DateRangeSelect, DateTimeSelect },
|
components: {DateRangeSelect, DateTimeSelect},
|
||||||
mixins: [resize],
|
mixins: [resize],
|
||||||
props: {
|
props: {
|
||||||
siteId: {
|
siteId: {
|
||||||
@ -93,7 +94,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showChart({ pointName, deviceCategory, deviceId, child = "" }) {
|
showChart({pointName, deviceCategory, deviceId, child = ""}) {
|
||||||
//初始化数据
|
//初始化数据
|
||||||
this.pointName = pointName;
|
this.pointName = pointName;
|
||||||
this.deviceCategory = deviceCategory;
|
this.deviceCategory = deviceCategory;
|
||||||
@ -152,84 +153,94 @@ export default {
|
|||||||
endDate,
|
endDate,
|
||||||
siteDeviceMap,
|
siteDeviceMap,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data || []);
|
this.setOption(response?.data || []);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.hideLoading();
|
this.hideLoading();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setOption(data) {
|
setOption(data) {
|
||||||
if (!this.chart) return;
|
if (!this.chart) return;
|
||||||
this.chart.clear();
|
this.chart.clear();
|
||||||
console.log("返回的数据", data);
|
console.log("返回的数据", data);
|
||||||
let dataset = [];
|
if (!data || data.length <= 0) {
|
||||||
if (data.length > 0) {
|
|
||||||
data.forEach((item, index) => {
|
|
||||||
item.deviceList.forEach((inner) => {
|
|
||||||
dataset.push({
|
|
||||||
name: `${
|
|
||||||
this.isDtdc
|
|
||||||
? `${inner.parentDeviceId ? inner.parentDeviceId + "-" : ""}${inner.deviceId}`
|
|
||||||
: `${inner.deviceId}`
|
|
||||||
}`,
|
|
||||||
type: "line",
|
|
||||||
markPoint: {
|
|
||||||
symbolSize: 30,
|
|
||||||
emphasis: {
|
|
||||||
disabled:false//打开 鼠标高亮
|
|
||||||
},
|
|
||||||
data: [//最大值、最小值
|
|
||||||
{
|
|
||||||
// type: 'max',
|
|
||||||
name: `最大值`,
|
|
||||||
coord:[inner.maxDate,inner.maxValue],
|
|
||||||
relativeTo:'coordinate',
|
|
||||||
label: {
|
|
||||||
position: "top",
|
|
||||||
formatter: item.dataType === 2 ? ([
|
|
||||||
`最大值:${inner.maxValue}`,
|
|
||||||
// `平均值:${inner.avgValue}`,
|
|
||||||
`差值:${inner.diffValue}`,
|
|
||||||
]).join('\n') : ([
|
|
||||||
`最大值:${inner.maxValue}`,
|
|
||||||
// `平均值:${inner.avgValue}`,
|
|
||||||
]).join('\n'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// type: 'min',
|
|
||||||
name: `最小值`,
|
|
||||||
coord:[inner.minDate,inner.minValue],
|
|
||||||
relativeTo:'coordinate',
|
|
||||||
label: {
|
|
||||||
position: "top",
|
|
||||||
formatter: item.dataType === 2 ? ([
|
|
||||||
`最小值:${inner.minValue}`,
|
|
||||||
// `平均值:${inner.avgValue}`,
|
|
||||||
`差值:${inner.diffValue}`,
|
|
||||||
]).join('\n') : ([
|
|
||||||
`最小值:${inner.minValue}`,
|
|
||||||
// `平均值:${inner.avgValue}`,
|
|
||||||
]).join('\n'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
xdata: [],
|
|
||||||
data: [],
|
|
||||||
});
|
|
||||||
const length = dataset.length;
|
|
||||||
inner.pointValueList.forEach((value) => {
|
|
||||||
dataset[length - 1].xdata.push(value.valueDate);
|
|
||||||
dataset[length - 1].data.push(value.pointValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$message.warning("暂无数据");
|
this.$message.warning("暂无数据");
|
||||||
}
|
}
|
||||||
console.log("图表数据", dataset);
|
console.log('展示的图表类型chartType', data[0].chartType)
|
||||||
|
|
||||||
|
if (data[0].chartType === 2) {
|
||||||
|
// 箱型图
|
||||||
|
this.setBoxOption(data)
|
||||||
|
} else {
|
||||||
|
//折线图
|
||||||
|
this.setLineOption(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setLineOption(data) {
|
||||||
|
let dataset = [];
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
item.deviceList.forEach((inner) => {
|
||||||
|
dataset.push({
|
||||||
|
name: `${
|
||||||
|
this.isDtdc
|
||||||
|
? `${inner.parentDeviceId ? inner.parentDeviceId + "-" : ""}${inner.deviceId}`
|
||||||
|
: `${inner.deviceId}`
|
||||||
|
}`,
|
||||||
|
type: "line",
|
||||||
|
markPoint: {
|
||||||
|
symbolSize: 30,
|
||||||
|
emphasis: {
|
||||||
|
disabled: false//打开 鼠标高亮
|
||||||
|
},
|
||||||
|
data: [//最大值、最小值
|
||||||
|
{
|
||||||
|
// type: 'max',
|
||||||
|
name: `最大值`,
|
||||||
|
coord: [inner.maxDate, inner.maxValue],
|
||||||
|
relativeTo: 'coordinate',
|
||||||
|
label: {
|
||||||
|
position: "top",
|
||||||
|
formatter: item.dataType === 2 ? ([
|
||||||
|
`最大值:${inner.maxValue}`,
|
||||||
|
// `平均值:${inner.avgValue}`,
|
||||||
|
`差值:${inner.diffValue}`,
|
||||||
|
]).join('\n') : ([
|
||||||
|
`最大值:${inner.maxValue}`,
|
||||||
|
// `平均值:${inner.avgValue}`,
|
||||||
|
]).join('\n'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// type: 'min',
|
||||||
|
name: `最小值`,
|
||||||
|
coord: [inner.minDate, inner.minValue],
|
||||||
|
relativeTo: 'coordinate',
|
||||||
|
label: {
|
||||||
|
position: "top",
|
||||||
|
formatter: item.dataType === 2 ? ([
|
||||||
|
`最小值:${inner.minValue}`,
|
||||||
|
// `平均值:${inner.avgValue}`,
|
||||||
|
`差值:${inner.diffValue}`,
|
||||||
|
]).join('\n') : ([
|
||||||
|
`最小值:${inner.minValue}`,
|
||||||
|
// `平均值:${inner.avgValue}`,
|
||||||
|
]).join('\n'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
xdata: [],
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
const length = dataset.length;
|
||||||
|
inner.pointValueList.forEach((value) => {
|
||||||
|
dataset[length - 1].xdata.push(value.valueDate);
|
||||||
|
dataset[length - 1].data.push(value.pointValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log("折线图图表数据", dataset);
|
||||||
this.chart.setOption({
|
this.chart.setOption({
|
||||||
legend: {
|
legend: {
|
||||||
// left: 'center',
|
// left: 'center',
|
||||||
@ -241,14 +252,17 @@ export default {
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
type: 'cross',
|
||||||
type: "cross", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
},
|
||||||
|
// axisPointer: {
|
||||||
|
// // 坐标轴指示器,坐标轴触发有效
|
||||||
|
// type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#333333",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
xAxis: { type: "category", data: dataset?.[0]?.xdata || [] },
|
xAxis: {type: "category", data: dataset?.[0]?.xdata || []},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
},
|
},
|
||||||
@ -266,6 +280,122 @@ export default {
|
|||||||
series: dataset,
|
series: dataset,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setBoxOption(data) {
|
||||||
|
let dataset = [];
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
item.deviceList.forEach((inner) => {
|
||||||
|
dataset.push({
|
||||||
|
name: `${
|
||||||
|
this.isDtdc
|
||||||
|
? `${inner.parentDeviceId ? inner.parentDeviceId + "-" : ""}${inner.deviceId}`
|
||||||
|
: `${inner.deviceId}`
|
||||||
|
}`,
|
||||||
|
type: "boxplot",
|
||||||
|
// markPoint: {
|
||||||
|
// symbolSize: 30,
|
||||||
|
// emphasis: {
|
||||||
|
// disabled: false//打开 鼠标高亮
|
||||||
|
// },
|
||||||
|
// data: [//最大值、最小值
|
||||||
|
// {
|
||||||
|
// // type: 'max',
|
||||||
|
// name: `最大值`,
|
||||||
|
// coord: [inner.maxDate, inner.maxValue],
|
||||||
|
// relativeTo: 'coordinate',
|
||||||
|
// label: {
|
||||||
|
// position: "top",
|
||||||
|
// formatter: item.dataType === 2 ? ([
|
||||||
|
// `最大值:${inner.maxValue}`,
|
||||||
|
// // `平均值:${inner.avgValue}`,
|
||||||
|
// `差值:${inner.diffValue}`,
|
||||||
|
// ]).join('\n') : ([
|
||||||
|
// `最大值:${inner.maxValue}`,
|
||||||
|
// // `平均值:${inner.avgValue}`,
|
||||||
|
// ]).join('\n'),
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// // type: 'min',
|
||||||
|
// name: `最小值`,
|
||||||
|
// coord: [inner.minDate, inner.minValue],
|
||||||
|
// relativeTo: 'coordinate',
|
||||||
|
// label: {
|
||||||
|
// position: "top",
|
||||||
|
// formatter: item.dataType === 2 ? ([
|
||||||
|
// `最小值:${inner.minValue}`,
|
||||||
|
// // `平均值:${inner.avgValue}`,
|
||||||
|
// `差值:${inner.diffValue}`,
|
||||||
|
// ]).join('\n') : ([
|
||||||
|
// `最小值:${inner.minValue}`,
|
||||||
|
// // `平均值:${inner.avgValue}`,
|
||||||
|
// ]).join('\n'),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
xdata: [],
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
const length = dataset.length;
|
||||||
|
inner.pointValueList.forEach((value) => {
|
||||||
|
const {valueDate, min, q1, median, q3, max} = value
|
||||||
|
// const mid = (max - min) / 2, minLine = min + Math.abs(median / 2),
|
||||||
|
// maxLine = max - Math.abs(median / 2)
|
||||||
|
dataset[length - 1].xdata.push(valueDate);
|
||||||
|
dataset[length - 1].data.push([min, q1, median, q3, max]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log("箱型图图表数据", dataset);
|
||||||
|
this.chart.setOption({
|
||||||
|
legend: {
|
||||||
|
// left: 'center',
|
||||||
|
// top: '10',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function (params) {
|
||||||
|
let data = params.data;
|
||||||
|
let result = params.marker + params.name + ' ' + params.seriesName + '<br/>';
|
||||||
|
result += '最小值: ' + data[1] + '<br/>';
|
||||||
|
result += '平均值: ' + data[3] + '<br/>';
|
||||||
|
result += '最大值: ' + data[5];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// trigger: "axis",
|
||||||
|
// axisPointer: {
|
||||||
|
// type: 'cross',
|
||||||
|
// },
|
||||||
|
// axisPointer: {
|
||||||
|
// // 坐标轴指示器,坐标轴触发有效
|
||||||
|
// type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {type: "category", data: dataset?.[0]?.xdata || []},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: dataset,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateDate(val) {
|
updateDate(val) {
|
||||||
this.dataRange = val || [];
|
this.dataRange = val || [];
|
||||||
this.getDate();
|
this.getDate();
|
||||||
|
|||||||
@ -2,8 +2,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 6个方块-->
|
<!-- 6个方块-->
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :xs="12" :sm="8" :lg="4" style="margin-bottom: 10px;" class="single-square-box-container" v-for="(item,index) in singleZdSqaure" :key="index+'singleSquareBox'">
|
<el-col :xs="12" :sm="8" :lg="4" style="margin-bottom: 10px;" class="single-square-box-container" v-for="(item,index) in displaySquares" :key="index+'singleSquareBox'">
|
||||||
<single-square-box :data="{...item,value:formatNumber(data[item.attr])}" ></single-square-box>
|
<div
|
||||||
|
class="square-click-wrapper"
|
||||||
|
:class="{ 'field-disabled': !item.pointId }"
|
||||||
|
@click="handleSquareClick(item)"
|
||||||
|
>
|
||||||
|
<single-square-box :data="{...item,value:item.value,loading:item.valueLoading}" ></single-square-box>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
@ -11,52 +17,74 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SingleSquareBox from "@/components/Ems/SingleSquareBox/index.vue";
|
import SingleSquareBox from "@/components/Ems/SingleSquareBox/index.vue";
|
||||||
import {formatNumber} from '@/filters/ems'
|
|
||||||
export default {
|
export default {
|
||||||
components:{SingleSquareBox},
|
components:{SingleSquareBox},
|
||||||
props:{
|
props:{
|
||||||
data:{
|
displayData: {
|
||||||
type:Object,
|
type: Array,
|
||||||
required:false,
|
required: false,
|
||||||
default:()=>{return {}}
|
default: () => [],
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods:{formatNumber},
|
computed: {
|
||||||
data() {
|
displaySquares() {
|
||||||
return {
|
const sourceList = (this.displayData || []).filter((item) => {
|
||||||
// 单个电站 四个方块数据
|
if (!item) return false;
|
||||||
singleZdSqaure:[{
|
return item.menuCode === "SBJK_SSYX" || item.sectionName === "运行概览";
|
||||||
title:'实时有功功率(kW)',
|
});
|
||||||
value:'',
|
const sourceMap = {};
|
||||||
bgColor:'#FFF2CB',
|
sourceList.forEach((item) => {
|
||||||
attr:'totalActivePower'
|
if (!item) return;
|
||||||
},{
|
const key = this.getFieldName(item.fieldCode);
|
||||||
title:'实时无功功率(kVar)',
|
if (key) {
|
||||||
value:'',
|
sourceMap[key] = item;
|
||||||
bgColor:'#CBD6FF',
|
}
|
||||||
attr:'totalReactivePower'
|
});
|
||||||
},{
|
const defaults = [
|
||||||
title:'电池堆SOC',
|
{fieldCode: "totalActivePower", fieldName: "实时有功功率(kW)"},
|
||||||
value:'',
|
{fieldCode: "totalReactivePower", fieldName: "实时无功功率(kVar)"},
|
||||||
bgColor:'#DCCBFF',
|
{fieldCode: "soc", fieldName: "电池堆SOC"},
|
||||||
attr:'soc'
|
{fieldCode: "soh", fieldName: "电池堆SOH"},
|
||||||
},{
|
{fieldCode: "dayChargedCap_rt", fieldName: "今日充电量(kWh)"},
|
||||||
title:'电池堆SOH',
|
{fieldCode: "dayDisChargedCap_rt", fieldName: "今日放电量(kWh)"},
|
||||||
value:'',
|
];
|
||||||
bgColor:'#FFD4CB',
|
return defaults.map((def, index) => {
|
||||||
attr:'soh'
|
const row = sourceMap[def.fieldCode] || {};
|
||||||
},{
|
const pointId = String(row.dataPoint || "").trim();
|
||||||
title:'今日充电量(kWh)',
|
return {
|
||||||
value:'',
|
title: row.fieldName || def.fieldName,
|
||||||
bgColor:'#FFD6F8',
|
value: row.fieldValue,
|
||||||
attr:'dayChargedCap'
|
valueLoading: this.loading && this.isEmptyValue(row.fieldValue),
|
||||||
},{
|
bgColor: this.getBgColor(index),
|
||||||
title:'今日放电量(kWh)',
|
pointId,
|
||||||
value:'',
|
fieldCode: row.fieldCode || def.fieldCode,
|
||||||
bgColor:'#E1FFCA',
|
raw: row,
|
||||||
attr:'dayDisChargedCap'
|
};
|
||||||
}]
|
});
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSquareClick(item) {
|
||||||
|
this.$emit("field-click", item || {});
|
||||||
|
},
|
||||||
|
getFieldName(fieldCode) {
|
||||||
|
const raw = String(fieldCode || "").trim();
|
||||||
|
if (!raw) return "";
|
||||||
|
const index = raw.lastIndexOf("__");
|
||||||
|
return index >= 0 ? raw.slice(index + 2) : raw;
|
||||||
|
},
|
||||||
|
getBgColor(index) {
|
||||||
|
const bgColors = ['#FFF2CB', '#CBD6FF', '#DCCBFF', '#FFD4CB', '#FFD6F8', '#E1FFCA'];
|
||||||
|
return bgColors[index % bgColors.length];
|
||||||
|
},
|
||||||
|
isEmptyValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" || value === "-";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -70,4 +98,13 @@ export default {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.square-click-wrapper {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-click-wrapper.field-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,20 +1,44 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmsdccContainer'" style="margin-bottom:25px;">
|
<div class="pcs-tags">
|
||||||
<el-card shadow="always" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
|
<el-tag
|
||||||
:class="{
|
size="small"
|
||||||
'warning-card-container':baseInfo.workStatus && baseInfo.workStatus !== '0',
|
:type="selectedClusterId ? 'info' : 'primary'"
|
||||||
'running-card-container':baseInfo.workStatus === '0'
|
:effect="selectedClusterId ? 'plain' : 'dark'"
|
||||||
}">
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick('')"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-for="(item, index) in clusterDeviceList"
|
||||||
|
:key="index + 'clusterTag'"
|
||||||
|
size="small"
|
||||||
|
:type="selectedClusterId === (item.deviceId || item.id) ? 'primary' : 'info'"
|
||||||
|
:effect="selectedClusterId === (item.deviceId || item.id) ? 'dark' : 'plain'"
|
||||||
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick(item.deviceId || item.id || '')"
|
||||||
|
>
|
||||||
|
{{ item.deviceName || item.name || item.deviceId || item.id || 'BMS电池簇' }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<div v-for="(baseInfo,index) in filteredBaseInfoList" :key="index+'bmsdccContainer'" style="margin-bottom:25px;">
|
||||||
|
<el-card shadow="always"
|
||||||
|
class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
|
||||||
|
:class="handleCardClass(baseInfo)">
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="large-title">{{baseInfo.parentDeviceName?`${baseInfo.parentDeviceName} -> ` : ''}}{{baseInfo.deviceName}}</span>
|
<span
|
||||||
|
class="large-title">{{
|
||||||
|
baseInfo.parentDeviceName ? `${baseInfo.parentDeviceName} -> ` : ''
|
||||||
|
}}{{ baseInfo.deviceName }}</span>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div>数据更新时间:{{ baseInfo.dataUpdateTime || '-'}}</div>
|
<div>数据更新时间:{{ baseInfo.dataUpdateTime || '-' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alarm">
|
<div class="alarm">
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(baseInfo,'point')">详细</el-button>
|
<el-button type="primary" round size="small" style="margin-right:20px;"
|
||||||
<el-badge :value="baseInfo.alarmNum || 0" class="item">
|
@click="pointDetail(baseInfo,'point')">详细
|
||||||
|
</el-button>
|
||||||
|
<el-badge :hidden="!baseInfo.alarmNum" :value="baseInfo.alarmNum || 0" class="item">
|
||||||
<i
|
<i
|
||||||
class="el-icon-message-solid alarm-icon"
|
class="el-icon-message-solid alarm-icon"
|
||||||
@click="pointDetail(baseInfo,'alarmPoint')"
|
@click="pointDetail(baseInfo,'alarmPoint')"
|
||||||
@ -22,172 +46,615 @@
|
|||||||
</el-badge>
|
</el-badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="descriptions-main">
|
<div class="descriptions-main">
|
||||||
<el-descriptions direction="vertical" :column="3" :colon="false">
|
<el-descriptions direction="vertical" :column="3" :colon="false">
|
||||||
<el-descriptions-item labelClassName="descriptions-label" :contentClassName="`descriptions-direction ${baseInfo.workStatus === '0' ? 'save' :'danger'}`" :span="1" label="工作状态" >{{$store.state.ems.workStatusOptions[baseInfo.workStatus]}}</el-descriptions-item>
|
<el-descriptions-item
|
||||||
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" :span="1" label="与PCS通信">{{$store.state.ems.communicationStatusOptions[baseInfo.pcsCommunicationStatus]}}</el-descriptions-item>
|
contentClassName="descriptions-direction work-status"
|
||||||
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" :span="1" label="与EMS通信">{{$store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus]}}</el-descriptions-item>
|
:span="1" label="工作状态">
|
||||||
</el-descriptions>
|
<span
|
||||||
</div>
|
class="pointer"
|
||||||
<div class="descriptions-main descriptions-main-bg-color">
|
:class="{ 'field-disabled': !hasFieldPointId(baseInfo, 'workStatus') }"
|
||||||
<el-descriptions direction="vertical" :column="3" :colon="false">
|
@click="handleFieldClick(baseInfo, 'workStatus', '工作状态')"
|
||||||
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" v-for="(item,index) in infoData" :key="index+'pcsInfoData'" :span="1" :label="item.label">
|
>
|
||||||
<span class="pointer" @click="showChart(item.pointName || '',baseInfo.deviceId)">
|
{{ CLUSTERWorkStatusOptions[baseInfo.workStatus] || '-' }}
|
||||||
{{baseInfo[item.attr] | formatNumber}} <span v-if="item.unit" v-html="item.unit"></span>
|
|
||||||
</span>
|
</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
<el-descriptions-item contentClassName="descriptions-direction"
|
||||||
<!-- 进度-->
|
:span="1" label="与PCS通信">
|
||||||
<div class="process-container">
|
<span
|
||||||
<div class="process-line-bg">
|
class="pointer"
|
||||||
<div class="process-line" :style="{height:baseInfo.currentSoc+'%'}"></div>
|
:class="{ 'field-disabled': !hasFieldPointId(baseInfo, 'pcsCommunicationStatus') }"
|
||||||
</div>
|
@click="handleFieldClick(baseInfo, 'pcsCommunicationStatus', '与PCS通信')"
|
||||||
<div class="process pointer" @click="showChart( '当前SOC',baseInfo.deviceId)">当前SOC : {{baseInfo.currentSoc}}%</div>
|
>
|
||||||
|
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[baseInfo.pcsCommunicationStatus] || '-' }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item contentClassName="descriptions-direction"
|
||||||
|
:span="1" label="与EMS通信">
|
||||||
|
<span
|
||||||
|
class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasFieldPointId(baseInfo, 'emsCommunicationStatus') }"
|
||||||
|
@click="handleFieldClick(baseInfo, 'emsCommunicationStatus', '与EMS通信')"
|
||||||
|
>
|
||||||
|
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[baseInfo.emsCommunicationStatus] || '-' }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
<div class="descriptions-main descriptions-main-bg-color">
|
||||||
|
<el-descriptions direction="vertical" :column="3" :colon="false">
|
||||||
|
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction"
|
||||||
|
v-for="(item,index) in infoData" :key="index+'pcsInfoData'" :span="1"
|
||||||
|
:label="item.label">
|
||||||
|
<span
|
||||||
|
class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasFieldPointId(baseInfo, item.attr) }"
|
||||||
|
@click="handleFieldClick(baseInfo, item.attr, item.label)"
|
||||||
|
>
|
||||||
|
<i v-if="isPointLoading(baseInfo[item.attr])" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(baseInfo[item.attr]) | formatNumber }}</span>
|
||||||
|
<span v-if="item.unit" v-html="item.unit"></span>
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<!-- 进度-->
|
||||||
|
<div class="process-container">
|
||||||
|
<div class="process-line-bg">
|
||||||
|
<div class="process-line" :style="{height:baseInfo.currentSoc+'%'}"></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="process pointer"
|
||||||
|
:class="{ 'field-disabled': !hasFieldPointId(baseInfo, 'currentSoc') }"
|
||||||
|
@click="handleFieldClick(baseInfo, 'currentSoc', '当前SOC')"
|
||||||
|
>当前SOC :
|
||||||
|
{{ baseInfo.currentSoc }}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
class="common-table"
|
class="common-table"
|
||||||
:data="baseInfo.batteryDataList"
|
:data="baseInfo.batteryDataList"
|
||||||
stripe
|
stripe
|
||||||
style="width: 100%;margin-top:25px;">
|
style="width: 100%;margin-top:25px;">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="dataName"
|
prop="dataName"
|
||||||
label="名称">
|
label="名称">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-html="scope.row.dataName+'('+unitObj[scope.row.dataName]+')'"></span>
|
<span v-html="scope.row.dataName+'('+unitObj[scope.row.dataName]+')'"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="avgData"
|
prop="avgData"
|
||||||
label="单体平均值"
|
label="单体平均值"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="pointer" @click="showChart( tablePointNameMap[scope.row.dataName+scope.column.label],baseInfo.deviceId)">{{scope.row.avgData}}</span>
|
<span class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasTableFieldPointId(baseInfo, scope.row.dataName, scope.column.label) }"
|
||||||
|
@click="handleTableFieldClick(baseInfo, scope.row.dataName, scope.column.label)">{{
|
||||||
|
scope.row.avgData
|
||||||
|
}}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="minData"
|
prop="minData"
|
||||||
label="单体最小值">
|
label="单体最小值">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="pointer" @click="showChart( tablePointNameMap[scope.row.dataName+scope.column.label],baseInfo.deviceId)">{{scope.row.minData}}</span>
|
<span class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasTableFieldPointId(baseInfo, scope.row.dataName, scope.column.label) }"
|
||||||
|
@click="handleTableFieldClick(baseInfo, scope.row.dataName, scope.column.label)">{{
|
||||||
|
scope.row.minData
|
||||||
|
}}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="minDataID"
|
prop="minDataID"
|
||||||
label="单体最小值ID">
|
label="单体最小值ID">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="maxData"
|
prop="maxData"
|
||||||
label="单体最大值">
|
label="单体最大值">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="pointer " @click="showChart( tablePointNameMap[scope.row.dataName+scope.column.label],baseInfo.deviceId)">{{scope.row.maxData}}</span>
|
<span class="pointer "
|
||||||
|
:class="{ 'field-disabled': !hasTableFieldPointId(baseInfo, scope.row.dataName, scope.column.label) }"
|
||||||
|
@click="handleTableFieldClick(baseInfo, scope.row.dataName, scope.column.label)">{{
|
||||||
|
scope.row.maxData
|
||||||
|
}}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="maxDataID"
|
prop="maxDataID"
|
||||||
label="单体最大值ID">
|
label="单体最大值ID">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-show="baseInfoList.length<=0" :image-size="200"></el-empty>
|
|
||||||
<point-chart ref="pointChart" :site-id="siteId"/>
|
|
||||||
<point-table ref="pointTable"/>
|
<point-table ref="pointTable"/>
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import pointChart from "./../PointChart.vue";
|
import * as echarts from "echarts";
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
||||||
import {getBMSBatteryCluster} from '@/api/ems/dzjk'
|
import {getProjectDisplayData, getStackNameList, getClusterNameList} from '@/api/ems/dzjk'
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
import {getPointConfigCurve} from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkSbjkBmsdcc',
|
name: 'DzjkSbjkBmsdcc',
|
||||||
mixins:[getQuerySiteId,intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
components:{PointTable, pointChart},
|
components: {PointTable},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
CLUSTERWorkStatusOptions: state => state?.ems?.CLUSTERWorkStatusOptions || {},
|
||||||
|
}),
|
||||||
|
filteredBaseInfoList() {
|
||||||
|
if (!this.selectedClusterId) {
|
||||||
|
return this.baseInfoList || [];
|
||||||
|
}
|
||||||
|
return (this.baseInfoList || []).filter(item => item.deviceId === this.selectedClusterId);
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
unitObj:{
|
displayData: [],
|
||||||
'电压':'V',
|
clusterDeviceList: [],
|
||||||
'温度':'℃',
|
selectedClusterId: "",
|
||||||
'SOC':'%'
|
curveDialogVisible: false,
|
||||||
|
curveDialogTitle: "点位曲线",
|
||||||
|
curveChart: null,
|
||||||
|
curveLoading: false,
|
||||||
|
curveCustomRange: [],
|
||||||
|
curveQuery: {
|
||||||
|
siteId: "",
|
||||||
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
},
|
},
|
||||||
tablePointNameMap:{
|
unitObj: {
|
||||||
'电压单体最小值':'最低单体电压',
|
'电压': 'V',
|
||||||
'电压单体平均值':'电压平均值',
|
'温度': '℃',
|
||||||
'电压单体最大值':'最高单体电压',
|
'SOC': '%'
|
||||||
'温度单体最小值':'最低单体温度',
|
|
||||||
'温度单体平均值':'平均单体温度',
|
|
||||||
'温度单体最大值':'最高单体温度',
|
|
||||||
'SOC单体最小值':'最低单体SOC',
|
|
||||||
'SOC单体平均值':'当前SOC',
|
|
||||||
'SOC单体最大值':'最高单体SOC',
|
|
||||||
},
|
},
|
||||||
baseInfoList:[],
|
tablePointNameMap: {
|
||||||
infoData:[
|
'电压单体最小值': '最低单体电压',
|
||||||
{label:'簇电压',attr:'clusterVoltage',unit:'V',pointName:'簇电压'},
|
'电压单体平均值': '电压平均值',
|
||||||
{label:'可充电量',attr:'chargeableCapacity',unit:'kWh',pointName:'可充电量'},
|
'电压单体最大值': '最高单体电压',
|
||||||
{label:'累计充电量',attr:'totalChargedCapacity',unit:'kWh',pointName:'累计充电量'},
|
'温度单体最小值': '最低单体温度',
|
||||||
{label:'簇电流',attr:'clusterCurrent',unit:'A',pointName:'簇电流'},
|
'温度单体平均值': '平均单体温度',
|
||||||
{label:'可放电量',attr:'dischargeableCapacity',unit:'kWh',pointName:'可放电量'},
|
'温度单体最大值': '最高单体温度',
|
||||||
{label:'累计放电量',attr:'totalDischargedCapacity',unit:'kWh',pointName:'累计放电量'},
|
'SOC单体最小值': '最低单体SOC',
|
||||||
{label:'SOH',attr:'soh',unit:'%',pointName:'SOH'},
|
'SOC单体平均值': '当前SOC',
|
||||||
{label:'平均温度',attr:'averageTemperature',unit:'℃',pointName:'平均温度'},
|
'SOC单体最大值': '最高单体SOC',
|
||||||
{label:'绝缘电阻',attr:'insulationResistance',unit:'Ω',pointName:'绝缘电阻'},
|
},
|
||||||
|
baseInfoList: [{
|
||||||
|
siteId: "",
|
||||||
|
deviceId: "",
|
||||||
|
parentDeviceName: "",
|
||||||
|
deviceName: "BMS电池簇",
|
||||||
|
dataUpdateTime: "-",
|
||||||
|
alarmNum: 0,
|
||||||
|
batteryDataList: [],
|
||||||
|
}],
|
||||||
|
infoData: [
|
||||||
|
{label: '簇电压', attr: 'clusterVoltage', unit: 'V', pointName: '簇电压'},
|
||||||
|
{label: '可充电量', attr: 'chargeableCapacity', unit: 'kWh', pointName: '可充电量'},
|
||||||
|
{label: '累计充电量', attr: 'totalChargedCapacity', unit: 'kWh', pointName: '累计充电量'},
|
||||||
|
{label: '簇电流', attr: 'clusterCurrent', unit: 'A', pointName: '簇电流'},
|
||||||
|
{label: '可放电量', attr: 'dischargeableCapacity', unit: 'kWh', pointName: '可放电量'},
|
||||||
|
{label: '累计放电量', attr: 'totalDischargedCapacity', unit: 'kWh', pointName: '累计放电量'},
|
||||||
|
{label: 'SOH', attr: 'soh', unit: '%', pointName: 'SOH'},
|
||||||
|
{label: '平均温度', attr: 'averageTemperature', unit: '℃', pointName: '平均温度'},
|
||||||
|
{label: '绝缘电阻', attr: 'insulationResistance', unit: 'Ω', pointName: '绝缘电阻'},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
|
handleCardClass(item) {
|
||||||
|
const {workStatus = ''} = item
|
||||||
|
return !(Object.keys(this.CLUSTERWorkStatusOptions).includes(item.workStatus)) ? "timing-card-container" : workStatus === '9' ? 'warning-card-container' : 'running-card-container'
|
||||||
|
},
|
||||||
// 查看设备电位表格
|
// 查看设备电位表格
|
||||||
pointDetail(row,dataType){
|
pointDetail(row, dataType) {
|
||||||
const {siteId,deviceId} = row
|
const {siteId, deviceId} = row
|
||||||
this.$refs.pointTable.showTable({siteId,deviceId,deviceCategory:'CLUSTER'},dataType)
|
this.$refs.pointTable.showTable({siteId, deviceId, deviceCategory: 'CLUSTER'}, dataType)
|
||||||
},
|
},
|
||||||
showChart(pointName,deviceId){
|
hasFieldPointId(baseInfo, fieldName) {
|
||||||
pointName && this.$refs.pointChart.showChart({pointName,deviceCategory:'CLUSTER',deviceId})
|
const row = this.getFieldRow(baseInfo, fieldName);
|
||||||
|
return !!String(row?.dataPoint || "").trim();
|
||||||
},
|
},
|
||||||
updateData(){
|
hasTableFieldPointId(baseInfo, dataName, columnLabel) {
|
||||||
this.loading = true
|
const pointName = this.tablePointNameMap[String(dataName || "") + String(columnLabel || "")];
|
||||||
getBMSBatteryCluster(this.siteId).then(response => {
|
if (!pointName) {
|
||||||
this.baseInfoList = JSON.parse(JSON.stringify(response?.data || []));
|
return false;
|
||||||
}).finally(() => {this.loading = false})
|
}
|
||||||
|
return this.hasFieldPointId(baseInfo, pointName);
|
||||||
},
|
},
|
||||||
init(){
|
getFieldRow(baseInfo, fieldName) {
|
||||||
|
const key = String(fieldName || "").trim();
|
||||||
|
const map = baseInfo?._fieldRowMap || {};
|
||||||
|
return map[key] || null;
|
||||||
|
},
|
||||||
|
handleFieldClick(baseInfo, fieldName, title) {
|
||||||
|
const row = this.getFieldRow(baseInfo, fieldName);
|
||||||
|
const pointId = String(row?.dataPoint || "").trim();
|
||||||
|
this.openCurveDialogByPointId(pointId, title || fieldName);
|
||||||
|
},
|
||||||
|
handleTableFieldClick(baseInfo, dataName, columnLabel) {
|
||||||
|
const pointName = this.tablePointNameMap[String(dataName || "") + String(columnLabel || "")];
|
||||||
|
if (!pointName) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.handleFieldClick(baseInfo, pointName, pointName);
|
||||||
|
},
|
||||||
|
openCurveDialogByPointId(pointId, title) {
|
||||||
|
const normalizedPointId = String(pointId || "").trim();
|
||||||
|
if (!normalizedPointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || normalizedPointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId: normalizedPointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query).then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
}).catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
}).finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map(item => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map(item => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTagClick(deviceId) {
|
||||||
|
this.selectedClusterId = deviceId || "";
|
||||||
|
},
|
||||||
|
init() {
|
||||||
this.updateData()
|
this.updateData()
|
||||||
this.updateInterval(this.updateData)
|
this.updateInterval(this.updateData)
|
||||||
|
},
|
||||||
|
getModuleRows(menuCode, sectionName) {
|
||||||
|
return (this.displayData || []).filter(item => item.menuCode === menuCode && item.sectionName === sectionName);
|
||||||
|
},
|
||||||
|
getFieldName(fieldCode) {
|
||||||
|
const raw = String(fieldCode || "").trim();
|
||||||
|
if (!raw) return "";
|
||||||
|
const index = raw.lastIndexOf("__");
|
||||||
|
return index >= 0 ? raw.slice(index + 2) : raw;
|
||||||
|
},
|
||||||
|
getFieldMap(rows = [], deviceId = "") {
|
||||||
|
const rowMap = this.getFieldRowMap(rows, deviceId);
|
||||||
|
return Object.keys(rowMap).reduce((acc, fieldName) => {
|
||||||
|
const row = rowMap[fieldName];
|
||||||
|
if (acc[fieldName] === undefined) {
|
||||||
|
acc[fieldName] = row?.fieldValue;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
getFieldRowMap(rows = [], deviceId = "") {
|
||||||
|
const map = {};
|
||||||
|
const targetDeviceId = String(deviceId || "");
|
||||||
|
rows.forEach(item => {
|
||||||
|
if (!item || !item.fieldCode) return;
|
||||||
|
const itemDeviceId = String(item.deviceId || "");
|
||||||
|
if (itemDeviceId !== targetDeviceId) return;
|
||||||
|
const fieldName = this.getFieldName(item.fieldCode);
|
||||||
|
map[fieldName] = item;
|
||||||
|
const displayName = String(item.fieldName || "").trim();
|
||||||
|
if (displayName && !map[displayName]) {
|
||||||
|
map[displayName] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rows.forEach(item => {
|
||||||
|
if (!item || !item.fieldCode) return;
|
||||||
|
const itemDeviceId = String(item.deviceId || "");
|
||||||
|
if (itemDeviceId !== "") return;
|
||||||
|
const fieldName = this.getFieldName(item.fieldCode);
|
||||||
|
if (map[fieldName] === undefined || map[fieldName] === null || map[fieldName] === "") {
|
||||||
|
map[fieldName] = item;
|
||||||
|
}
|
||||||
|
const displayName = String(item.fieldName || "").trim();
|
||||||
|
if (displayName && !map[displayName]) {
|
||||||
|
map[displayName] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
getLatestTime(menuCode) {
|
||||||
|
const times = (this.displayData || [])
|
||||||
|
.filter(item => item.menuCode === menuCode && item.valueTime)
|
||||||
|
.map(item => new Date(item.valueTime).getTime())
|
||||||
|
.filter(ts => !isNaN(ts));
|
||||||
|
if (times.length === 0) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
const date = new Date(Math.max(...times));
|
||||||
|
const p = (n) => String(n).padStart(2, '0');
|
||||||
|
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`;
|
||||||
|
},
|
||||||
|
getClusterDeviceList() {
|
||||||
|
return getStackNameList(this.siteId)
|
||||||
|
.then(response => {
|
||||||
|
const stackList = response?.data || [];
|
||||||
|
if (!stackList.length) {
|
||||||
|
this.clusterDeviceList = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const requests = stackList.map(stack => {
|
||||||
|
const stackDeviceId = stack.deviceId || stack.id || '';
|
||||||
|
return getClusterNameList({stackDeviceId, siteId: this.siteId})
|
||||||
|
.then(clusterResponse => {
|
||||||
|
const clusterList = clusterResponse?.data || [];
|
||||||
|
return clusterList.map(cluster => ({
|
||||||
|
...cluster,
|
||||||
|
parentDeviceName: stack.deviceName || stack.name || stackDeviceId || '',
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch(() => []);
|
||||||
|
});
|
||||||
|
return Promise.all(requests).then(results => {
|
||||||
|
this.clusterDeviceList = results.flat();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.clusterDeviceList = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buildBaseInfoList() {
|
||||||
|
const devices = (this.clusterDeviceList && this.clusterDeviceList.length > 0)
|
||||||
|
? this.clusterDeviceList
|
||||||
|
: [{deviceId: this.siteId, deviceName: 'BMS电池簇', parentDeviceName: ''}];
|
||||||
|
this.baseInfoList = devices.map(device => ({
|
||||||
|
...(() => {
|
||||||
|
const id = device.deviceId || device.id || this.siteId;
|
||||||
|
const infoMap = this.getFieldMap(this.getModuleRows('SBJK_BMSDCC', '簇信息'), id);
|
||||||
|
const statusMap = this.getFieldMap(this.getModuleRows('SBJK_BMSDCC', '状态'), id);
|
||||||
|
const currentSoc = Number(infoMap.currentSoc);
|
||||||
|
return {
|
||||||
|
...infoMap,
|
||||||
|
workStatus: statusMap.workStatus,
|
||||||
|
pcsCommunicationStatus: statusMap.pcsCommunicationStatus,
|
||||||
|
emsCommunicationStatus: statusMap.emsCommunicationStatus,
|
||||||
|
currentSoc: isNaN(currentSoc) ? 0 : currentSoc,
|
||||||
|
_fieldRowMap: {
|
||||||
|
...this.getFieldRowMap(this.getModuleRows('SBJK_BMSDCC', '簇信息'), id),
|
||||||
|
...this.getFieldRowMap(this.getModuleRows('SBJK_BMSDCC', '状态'), id),
|
||||||
|
...this.getFieldRowMap(this.getModuleRows('SBJK_BMSDCC', '单体数据'), id),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
siteId: this.siteId,
|
||||||
|
deviceId: device.deviceId || device.id || this.siteId,
|
||||||
|
parentDeviceName: device.parentDeviceName || '',
|
||||||
|
deviceName: device.deviceName || device.name || device.deviceId || device.id || 'BMS电池簇',
|
||||||
|
dataUpdateTime: this.getLatestTime('SBJK_BMSDCC'),
|
||||||
|
alarmNum: 0,
|
||||||
|
batteryDataList: [],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
updateData() {
|
||||||
|
this.loading = true
|
||||||
|
// 先渲染卡片框架,字段值走单点位 loading
|
||||||
|
this.buildBaseInfoList();
|
||||||
|
Promise.all([
|
||||||
|
getProjectDisplayData(this.siteId),
|
||||||
|
this.getClusterDeviceList(),
|
||||||
|
]).then(([response]) => {
|
||||||
|
this.displayData = response?.data || [];
|
||||||
|
this.buildBaseInfoList();
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.pcs-tags {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tag-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep {
|
::v-deep {
|
||||||
//描述列表样式
|
//描述列表样式
|
||||||
.descriptions-main{
|
.descriptions-main {
|
||||||
padding:24px 300px 24px 24px;
|
padding: 24px 300px 24px 24px;
|
||||||
}
|
}
|
||||||
.descriptions-main-bottom{
|
|
||||||
padding:14px 300px 14px 24px;
|
.descriptions-main-bottom {
|
||||||
|
padding: 14px 300px 14px 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 进度条样式
|
// 进度条样式
|
||||||
.process-container{
|
.process-container {
|
||||||
width:100px;
|
width: 100px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right:70px;
|
right: 70px;
|
||||||
top:50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
.process-line-bg{
|
|
||||||
|
.process-line-bg {
|
||||||
position: relative;
|
position: relative;
|
||||||
width:100%;
|
width: 100%;
|
||||||
height: 110px;
|
height: 110px;
|
||||||
background-color:#fff2cb ;
|
background-color: #fff2cb;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 0 10px #fff2cb, 0 0 0 rgba(255, 242, 203, 0.5);
|
box-shadow: 0 0 10px #fff2cb, 0 0 0 rgba(255, 242, 203, 0.5);
|
||||||
.process-line{
|
|
||||||
|
.process-line {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -198,10 +665,23 @@ export default {
|
|||||||
box-shadow: 0 0 10px #ffbf14, 0 0 0 rgba(255, 191, 20, 0.5);
|
box-shadow: 0 0 10px #ffbf14, 0 0 0 rgba(255, 191, 20, 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.process{
|
|
||||||
margin-top:15px;
|
.process {
|
||||||
color:#666666;
|
margin-top: 15px;
|
||||||
|
color: #666666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,20 +1,42 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmszlContainer'" style="margin-bottom:25px;">
|
<div class="pcs-tags">
|
||||||
<el-card :class="{
|
<el-tag
|
||||||
'warning-card-container':baseInfo.workStatus && baseInfo.workStatus !== '0',
|
size="small"
|
||||||
'running-card-container':baseInfo.workStatus === '0'
|
:type="selectedStackId ? 'info' : 'primary'"
|
||||||
}" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
|
:effect="selectedStackId ? 'plain' : 'dark'"
|
||||||
shadow="always">
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick('')"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-for="(item, index) in stackDeviceList"
|
||||||
|
:key="index + 'stackTag'"
|
||||||
|
size="small"
|
||||||
|
:type="selectedStackId === (item.deviceId || item.id) ? 'primary' : 'info'"
|
||||||
|
:effect="selectedStackId === (item.deviceId || item.id) ? 'dark' : 'plain'"
|
||||||
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick(item.deviceId || item.id || '')"
|
||||||
|
>
|
||||||
|
{{ item.deviceName || item.name || item.deviceId || item.id || 'BMS总览' }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<div v-for="(baseInfo,index) in filteredBaseInfoList" :key="index+'bmszlContainer'" style="margin-bottom:25px;">
|
||||||
|
<el-card
|
||||||
|
:class="handleCardClass(baseInfo)"
|
||||||
|
class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
|
||||||
|
shadow="always">
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="large-title">{{baseInfo.deviceName}}</span>
|
<span class="large-title">{{ baseInfo.deviceName }}</span>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div>数据更新时间:{{ baseInfo.dataUpdateTime || '-'}}</div>
|
<div>数据更新时间:{{ baseInfo.dataUpdateTime || '-' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alarm">
|
<div class="alarm">
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(baseInfo,'point')">详细</el-button>
|
<el-button type="primary" round size="small" style="margin-right:20px;"
|
||||||
<el-badge :value="baseInfo.alarmNum || 0" class="item">
|
@click="pointDetail(baseInfo,'point')">详细
|
||||||
|
</el-button>
|
||||||
|
<el-badge :hidden="!baseInfo.alarmNum" :value="baseInfo.alarmNum || 0" class="item">
|
||||||
<i
|
<i
|
||||||
class="el-icon-message-solid alarm-icon"
|
class="el-icon-message-solid alarm-icon"
|
||||||
@click="pointDetail(baseInfo,'alarmPoint')"
|
@click="pointDetail(baseInfo,'alarmPoint')"
|
||||||
@ -24,16 +46,36 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="descriptions-main">
|
<div class="descriptions-main">
|
||||||
<el-descriptions :colon="false" :column="3" direction="vertical">
|
<el-descriptions :colon="false" :column="3" direction="vertical">
|
||||||
<el-descriptions-item :contentClassName="`descriptions-direction ${baseInfo.workStatus === '0' ? 'save' :'danger'}`" :span="1" label="工作状态" labelClassName="descriptions-label" >{{$store.state.ems.workStatusOptions[baseInfo.workStatus]}}</el-descriptions-item>
|
<el-descriptions-item
|
||||||
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与PCS通信" labelClassName="descriptions-label">{{$store.state.ems.communicationStatusOptions[baseInfo.pcsCommunicationStatus]}}</el-descriptions-item>
|
contentClassName="descriptions-direction work-status"
|
||||||
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与EMS通信" labelClassName="descriptions-label">{{$store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus]}}</el-descriptions-item>
|
label="工作状态" labelClassName="descriptions-label">
|
||||||
|
<span class="pointer" @click="handleStatusFieldClick(baseInfo, 'workStatus', '工作状态')">
|
||||||
|
{{ STACKWorkStatusOptions[baseInfo.workStatus] || '-' }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与PCS通信"
|
||||||
|
labelClassName="descriptions-label">
|
||||||
|
<span class="pointer" @click="handleStatusFieldClick(baseInfo, 'pcsCommunicationStatus', '与PCS通信')">
|
||||||
|
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[baseInfo.pcsCommunicationStatus] || '-' }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与EMS通信"
|
||||||
|
labelClassName="descriptions-label">
|
||||||
|
<span class="pointer" @click="handleStatusFieldClick(baseInfo, 'emsCommunicationStatus', '与EMS通信')">
|
||||||
|
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[baseInfo.emsCommunicationStatus] || '-' }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div class="descriptions-main descriptions-main-bg-color">
|
<div class="descriptions-main descriptions-main-bg-color">
|
||||||
<el-descriptions :colon="false" :column="3" direction="vertical">
|
<el-descriptions :colon="false" :column="3" direction="vertical">
|
||||||
<el-descriptions-item v-for="(item,index) in infoData" :key="index+'pcsInfoData'" :label="item.label" :span="1" contentClassName="descriptions-direction" labelClassName="descriptions-label">
|
<el-descriptions-item v-for="(item,index) in infoData" :key="index+'pcsInfoData'" :label="item.label"
|
||||||
<span class="pointer" @click="showChart(item.pointName || '',baseInfo.deviceId)">
|
:span="1" contentClassName="descriptions-direction"
|
||||||
{{baseInfo[item.attr] | formatNumber}}<span v-if="item.unit" v-html="item.unit"></span>
|
labelClassName="descriptions-label">
|
||||||
|
<span class="pointer" @click="handleStackFieldClick(baseInfo, item)">
|
||||||
|
<i v-if="isPointLoading(baseInfo[item.attr])" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(baseInfo[item.attr]) | formatNumber }}</span>
|
||||||
|
<span v-if="item.unit" v-html="item.unit"></span>
|
||||||
</span>
|
</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
@ -42,143 +84,449 @@
|
|||||||
<div class="process-line-bg">
|
<div class="process-line-bg">
|
||||||
<div :style="{height:baseInfo.stackSoc+'%'}" class="process-line"></div>
|
<div :style="{height:baseInfo.stackSoc+'%'}" class="process-line"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="process pointer" @click="showChart('当前SOC',baseInfo.deviceId)">当前SOC : {{baseInfo.stackSoc}}%</div>
|
<div class="process pointer" @click="handleStackSocClick(baseInfo)">当前SOC :
|
||||||
|
{{ baseInfo.stackSoc }}%
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-table
|
|
||||||
:data="baseInfo.batteryDataList"
|
|
||||||
class="common-table"
|
|
||||||
max-height="500"
|
|
||||||
stripe
|
|
||||||
style="width: 100%;margin-top:25px;">
|
|
||||||
<el-table-column
|
|
||||||
label="簇号"
|
|
||||||
prop="clusterId">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="簇电压"
|
|
||||||
>
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('簇电压',scope.row.clusterId,'CLUSTER')">{{scope.row.clusterVoltage}} V</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="簇电流">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('簇电流',scope.row.clusterId,'CLUSTER')">{{scope.row.clusterCurrent}} A</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="簇SOC">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('当前SOC',scope.row.clusterId,'CLUSTER')">{{scope.row.currentSoc}} %</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="单体最高电压"
|
|
||||||
prop="maxVoltage">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('最高单体电压',scope.row.clusterId,'CLUSTER')">{{scope.row.maxCellVoltage}} V</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="电池号码"
|
|
||||||
prop="maxCellVoltageId">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="单体最低电压"
|
|
||||||
prop="minVoltage">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('最低单体电压',scope.row.clusterId,'CLUSTER')">{{scope.row.minCellVoltage}} V</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="电池号码"
|
|
||||||
prop="minCellVoltageId">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="单体最高温度">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('最高单体温度',scope.row.clusterId,'CLUSTER')">{{scope.row.maxCellTemp}} ℃</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="电池号码"
|
|
||||||
prop="maxCellTempId">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="单体最低温度"
|
|
||||||
prop="minTemperature">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="pointer" @click="showChart('最低单体温度',scope.row.clusterId,'CLUSTER')">{{scope.row.minCellTemp}} ℃</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="电池号码"
|
|
||||||
prop="minCellTempId">
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-show="baseInfoList.length<=0" :image-size="200"></el-empty>
|
<el-dialog
|
||||||
<point-chart ref="pointChart" :site-id="siteId"/>
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
<point-table ref="pointTable"/>
|
<point-table ref="pointTable"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getBMSOverView} from '@/api/ems/dzjk'
|
import * as echarts from "echarts";
|
||||||
|
import {getProjectDisplayData, getStackNameList} from '@/api/ems/dzjk'
|
||||||
|
import {getPointConfigCurve} from "@/api/ems/site";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
import pointChart from "./../PointChart.vue";
|
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkSbjkBmszl',
|
name: 'DzjkSbjkBmszl',
|
||||||
components: {pointChart,PointTable},
|
components: {PointTable},
|
||||||
mixins:[getQuerySiteId,intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
STACKWorkStatusOptions: state => state?.ems?.STACKWorkStatusOptions || {},
|
||||||
|
}),
|
||||||
|
filteredBaseInfoList() {
|
||||||
|
if (!this.selectedStackId) {
|
||||||
|
return this.baseInfoList || [];
|
||||||
|
}
|
||||||
|
return (this.baseInfoList || []).filter(item => item.deviceId === this.selectedStackId);
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
baseInfoList:[],
|
displayData: [],
|
||||||
infoData:[
|
stackDeviceList: [],
|
||||||
{label:'电池堆总电压',attr:'stackVoltage',unit:'V',pointName:'电池堆电压'},
|
selectedStackId: "",
|
||||||
{label:'可充电量',attr:'availableChargeCapacity',unit:'kWh',pointName:'可充电量'},
|
curveDialogVisible: false,
|
||||||
{label:'累计充电量',attr:'totalChargeCapacity',unit:'kWh',pointName:'累计充电量'},
|
curveDialogTitle: "点位曲线",
|
||||||
{label:'电池堆总电流',attr:'stackCurrent',unit:'A',pointName:'电池堆总电流'},
|
curveChart: null,
|
||||||
{label:'可放电量',attr:'availableDischargeCapacity',unit:'kWh',pointName:'可放电量'},
|
curveLoading: false,
|
||||||
{label:'累计放电量',attr:'totalDischargeCapacity',unit:'kWh',pointName:'累计放电量'},
|
curveCustomRange: [],
|
||||||
{label:'SOH',attr:'stackSoh',unit:'%',pointName:'SOH'},
|
curveQuery: {
|
||||||
{label:'平均温度',attr:'operatingTemp',unit:'℃',pointName:'平均温度'},
|
siteId: "",
|
||||||
{label:'绝缘电阻',attr:'stackInsulationResistance',unit:'Ω',pointName:'绝缘电阻'},
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
},
|
||||||
|
baseInfoList: [{
|
||||||
|
siteId: "",
|
||||||
|
deviceId: "",
|
||||||
|
deviceName: "BMS总览",
|
||||||
|
dataUpdateTime: "-",
|
||||||
|
alarmNum: 0,
|
||||||
|
batteryDataList: [],
|
||||||
|
}],
|
||||||
|
infoData: [
|
||||||
|
{label: '电池堆总电压', attr: 'stackVoltage', unit: 'V'},
|
||||||
|
{label: '可充电量', attr: 'availableChargeCapacity', unit: 'kWh'},
|
||||||
|
{label: '累计充电量', attr: 'totalChargeCapacity', unit: 'kWh'},
|
||||||
|
{label: '电池堆总电流', attr: 'stackCurrent', unit: 'A'},
|
||||||
|
{label: '可放电量', attr: 'availableDischargeCapacity', unit: 'kWh'},
|
||||||
|
{label: '累计放电量', attr: 'totalDischargeCapacity', unit: 'kWh'},
|
||||||
|
{label: 'SOH', attr: 'stackSoh', unit: '%'},
|
||||||
|
{label: '平均温度', attr: 'operatingTemp', unit: '℃'},
|
||||||
|
{label: '绝缘电阻', attr: 'stackInsulationResistance', unit: 'Ω'},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
|
handleCardClass(item) {
|
||||||
|
const {workStatus = ''} = item
|
||||||
|
return !Object.keys(this.STACKWorkStatusOptions).find(i => i === workStatus) ? "timing-card-container" : workStatus === '9' ? 'warning-card-container' : 'running-card-container'
|
||||||
|
},
|
||||||
|
|
||||||
// 查看设备电位表格
|
// 查看设备电位表格
|
||||||
pointDetail(row,dataType){
|
pointDetail(row, dataType) {
|
||||||
const {siteId,deviceId} = row
|
const {siteId, deviceId} = row
|
||||||
this.$refs.pointTable.showTable({siteId,deviceId,deviceCategory:'STACK'},dataType)
|
this.$refs.pointTable.showTable({siteId, deviceId, deviceCategory: 'STACK'}, dataType)
|
||||||
},
|
},
|
||||||
showChart(pointName,deviceId,deviceCategory = 'STACK'){
|
handleStatusFieldClick(baseInfo, fieldKey, title) {
|
||||||
pointName && this.$refs.pointChart.showChart({pointName,deviceCategory,deviceId})
|
const pointId = this.resolvePointId(baseInfo, fieldKey, "status");
|
||||||
|
this.openCurveDialogByPointId(pointId, title || fieldKey);
|
||||||
},
|
},
|
||||||
updateData(){
|
handleStackFieldClick(baseInfo, item) {
|
||||||
this.loading = true
|
const fieldKey = item?.attr || "";
|
||||||
getBMSOverView(this.siteId).then(response => {
|
const pointId = this.resolvePointId(baseInfo, fieldKey, "info");
|
||||||
this.baseInfoList = JSON.parse(JSON.stringify(response?.data || []));
|
this.openCurveDialogByPointId(pointId, item?.label || fieldKey);
|
||||||
}).finally(() => {this.loading = false})
|
|
||||||
},
|
},
|
||||||
init(){
|
handleStackSocClick(baseInfo) {
|
||||||
|
const pointId = this.resolvePointId(baseInfo, "stackSoc", "info");
|
||||||
|
this.openCurveDialogByPointId(pointId, "当前SOC");
|
||||||
|
},
|
||||||
|
handleClusterFieldClick(row = {}, fieldKey = "", title = "") {
|
||||||
|
const directKeys = [
|
||||||
|
"pointId",
|
||||||
|
"dataPoint",
|
||||||
|
`${fieldKey}PointId`,
|
||||||
|
`${fieldKey}DataPoint`,
|
||||||
|
];
|
||||||
|
let pointId = "";
|
||||||
|
directKeys.some((key) => {
|
||||||
|
const value = String(row?.[key] || "").trim();
|
||||||
|
if (value) {
|
||||||
|
pointId = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (!pointId && row?.pointIdMap && fieldKey) {
|
||||||
|
pointId = String(row.pointIdMap[fieldKey] || "").trim();
|
||||||
|
}
|
||||||
|
this.openCurveDialogByPointId(pointId, title || fieldKey);
|
||||||
|
},
|
||||||
|
resolvePointId(baseInfo = {}, fieldKey = "", source = "info") {
|
||||||
|
const mapKey = source === "status" ? "statusPointIdMap" : "pointIdMap";
|
||||||
|
return String(baseInfo?.[mapKey]?.[fieldKey] || "").trim();
|
||||||
|
},
|
||||||
|
openCurveDialogByPointId(pointId, title) {
|
||||||
|
const normalizedPointId = String(pointId || "").trim();
|
||||||
|
if (!normalizedPointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || normalizedPointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId: normalizedPointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query).then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
}).catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
}).finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map((item) => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map((item) => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init() {
|
||||||
this.updateData()
|
this.updateData()
|
||||||
this.updateInterval(this.updateData)
|
this.updateInterval(this.updateData)
|
||||||
}
|
},
|
||||||
|
getModuleRows(menuCode, sectionName) {
|
||||||
|
return (this.displayData || []).filter(item => item.menuCode === menuCode && item.sectionName === sectionName);
|
||||||
|
},
|
||||||
|
getFieldName(fieldCode) {
|
||||||
|
const raw = String(fieldCode || "").trim();
|
||||||
|
if (!raw) return "";
|
||||||
|
const index = raw.lastIndexOf("__");
|
||||||
|
return index >= 0 ? raw.slice(index + 2) : raw;
|
||||||
|
},
|
||||||
|
isEmptyValue(value) {
|
||||||
|
return value === undefined || value === null || value === "";
|
||||||
|
},
|
||||||
|
getFieldRowMap(rows = [], deviceId = "") {
|
||||||
|
const map = {};
|
||||||
|
const targetDeviceId = String(deviceId || "");
|
||||||
|
rows.forEach(item => {
|
||||||
|
if (!item || !item.fieldCode) return;
|
||||||
|
const itemDeviceId = String(item.deviceId || "");
|
||||||
|
if (itemDeviceId !== targetDeviceId) return;
|
||||||
|
map[this.getFieldName(item.fieldCode)] = item;
|
||||||
|
});
|
||||||
|
rows.forEach(item => {
|
||||||
|
if (!item || !item.fieldCode) return;
|
||||||
|
const itemDeviceId = String(item.deviceId || "");
|
||||||
|
if (itemDeviceId !== "") return;
|
||||||
|
const fieldName = this.getFieldName(item.fieldCode);
|
||||||
|
const existRow = map[fieldName];
|
||||||
|
if (!existRow || this.isEmptyValue(existRow.fieldValue)) {
|
||||||
|
map[fieldName] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
getFieldMap(rowMap = {}) {
|
||||||
|
const map = {};
|
||||||
|
Object.keys(rowMap || {}).forEach((fieldName) => {
|
||||||
|
map[fieldName] = rowMap[fieldName]?.fieldValue;
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
getPointIdMap(rowMap = {}) {
|
||||||
|
const map = {};
|
||||||
|
Object.keys(rowMap || {}).forEach((fieldName) => {
|
||||||
|
map[fieldName] = String(rowMap[fieldName]?.dataPoint || "").trim();
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
getLatestTime(menuCode) {
|
||||||
|
const times = (this.displayData || [])
|
||||||
|
.filter(item => item.menuCode === menuCode && item.valueTime)
|
||||||
|
.map(item => new Date(item.valueTime).getTime())
|
||||||
|
.filter(ts => !isNaN(ts));
|
||||||
|
if (times.length === 0) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
const date = new Date(Math.max(...times));
|
||||||
|
const p = (n) => String(n).padStart(2, '0');
|
||||||
|
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`;
|
||||||
|
},
|
||||||
|
handleTagClick(deviceId) {
|
||||||
|
this.selectedStackId = deviceId || "";
|
||||||
|
},
|
||||||
|
getStackDeviceList() {
|
||||||
|
return getStackNameList(this.siteId).then(response => {
|
||||||
|
this.stackDeviceList = response?.data || [];
|
||||||
|
}).catch(() => {
|
||||||
|
this.stackDeviceList = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buildBaseInfoList() {
|
||||||
|
const devices = (this.stackDeviceList && this.stackDeviceList.length > 0)
|
||||||
|
? this.stackDeviceList
|
||||||
|
: [{deviceId: this.siteId, deviceName: 'BMS总览'}];
|
||||||
|
this.baseInfoList = devices.map(device => ({
|
||||||
|
...(() => {
|
||||||
|
const id = device.deviceId || device.id || this.siteId;
|
||||||
|
const infoRowMap = this.getFieldRowMap(this.getModuleRows('SBJK_BMSZL', '堆信息'), id);
|
||||||
|
const statusRowMap = this.getFieldRowMap(this.getModuleRows('SBJK_BMSZL', '状态'), id);
|
||||||
|
const infoMap = this.getFieldMap(infoRowMap);
|
||||||
|
const statusMap = this.getFieldMap(statusRowMap);
|
||||||
|
const stackSoc = Number(infoMap.stackSoc);
|
||||||
|
return {
|
||||||
|
...infoMap,
|
||||||
|
workStatus: statusMap.workStatus,
|
||||||
|
pcsCommunicationStatus: statusMap.pcsCommunicationStatus,
|
||||||
|
emsCommunicationStatus: statusMap.emsCommunicationStatus,
|
||||||
|
stackSoc: isNaN(stackSoc) ? 0 : stackSoc,
|
||||||
|
pointIdMap: this.getPointIdMap(infoRowMap),
|
||||||
|
statusPointIdMap: this.getPointIdMap(statusRowMap),
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
siteId: this.siteId,
|
||||||
|
deviceId: device.deviceId || device.id || this.siteId,
|
||||||
|
deviceName: device.deviceName || device.name || device.deviceId || device.id || 'BMS总览',
|
||||||
|
dataUpdateTime: this.getLatestTime('SBJK_BMSZL'),
|
||||||
|
alarmNum: 0,
|
||||||
|
batteryDataList: [],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
updateData() {
|
||||||
|
this.loading = true
|
||||||
|
// 先渲染卡片框架,字段值走单点位 loading
|
||||||
|
this.buildBaseInfoList();
|
||||||
|
Promise.all([
|
||||||
|
getProjectDisplayData(this.siteId),
|
||||||
|
this.getStackDeviceList(),
|
||||||
|
]).then(([displayResponse]) => {
|
||||||
|
this.displayData = displayResponse?.data || [];
|
||||||
|
this.buildBaseInfoList();
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.pcs-tags {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tag-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep {
|
::v-deep {
|
||||||
//描述列表样式
|
//描述列表样式
|
||||||
.descriptions-main {
|
.descriptions-main {
|
||||||
@ -189,21 +537,24 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 进度条样式
|
// 进度条样式
|
||||||
.process-container{
|
.process-container {
|
||||||
width:100px;
|
width: 100px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right:70px;
|
right: 70px;
|
||||||
top:50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
.process-line-bg{
|
|
||||||
|
.process-line-bg {
|
||||||
position: relative;
|
position: relative;
|
||||||
width:100%;
|
width: 100%;
|
||||||
height: 110px;
|
height: 110px;
|
||||||
background-color:#fff2cb ;
|
background-color: #fff2cb;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 0 10px #fff2cb, 0 0 0 rgba(255, 242, 203, 0.5);
|
box-shadow: 0 0 10px #fff2cb, 0 0 0 rgba(255, 242, 203, 0.5);
|
||||||
.process-line{
|
|
||||||
|
.process-line {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -214,10 +565,23 @@ export default {
|
|||||||
box-shadow: 0 0 10px rgb(252 108 108), 0 0 0 rgba(252, 108, 108, 0.5);
|
box-shadow: 0 0 10px rgb(252 108 108), 0 0 0 rgba(252, 108, 108, 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.process{
|
|
||||||
margin-top:15px;
|
.process {
|
||||||
color:#666666;
|
margin-top: 15px;
|
||||||
|
color: #666666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,188 +1,497 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<el-card
|
<div class="pcs-tags">
|
||||||
v-for="(item,index) in list"
|
<el-tag
|
||||||
:key="index+'dbList'"
|
size="small"
|
||||||
shadow="always"
|
:type="selectedSectionKey ? 'info' : 'primary'"
|
||||||
class="sbjk-card-container list"
|
:effect="selectedSectionKey ? 'plain' : 'dark'"
|
||||||
:class="{
|
class="pcs-tag-item"
|
||||||
'warning-card-container':item.emsCommunicationStatus && item.emsCommunicationStatus !== '0',
|
@click="handleTagClick('')"
|
||||||
'running-card-container':item.emsCommunicationStatus === '0'
|
>
|
||||||
}"
|
全部
|
||||||
>
|
</el-tag>
|
||||||
<div slot="header">
|
<el-tag
|
||||||
<span class="large-title">{{ item.deviceName }}</span>
|
v-for="(group, index) in sectionGroups"
|
||||||
<div class="info">
|
:key="index + 'dbTag'"
|
||||||
<div>
|
size="small"
|
||||||
{{
|
:type="selectedSectionKey === group.sectionKey ? 'primary' : 'info'"
|
||||||
$store.state.ems.communicationStatusOptions[
|
:effect="selectedSectionKey === group.sectionKey ? 'dark' : 'plain'"
|
||||||
item.emsCommunicationStatus
|
class="pcs-tag-item"
|
||||||
]
|
@click="handleTagClick(group.sectionKey)"
|
||||||
}}
|
>
|
||||||
</div>
|
{{ group.displayName || "电表" }}
|
||||||
<div>数据更新时间:{{ item.dataUpdateTime }}</div>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="alarm">
|
<el-card
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">详细</el-button>
|
v-for="(group, index) in filteredSectionGroups"
|
||||||
<el-badge :value="item.alarmNum || 0" class="item">
|
:key="index + 'dbSection'"
|
||||||
<i
|
class="sbjk-card-container list running-card-container"
|
||||||
class="el-icon-message-solid alarm-icon"
|
shadow="always"
|
||||||
@click="pointDetail(item,'alarmPoint')"
|
>
|
||||||
></i>
|
<div slot="header">
|
||||||
</el-badge>
|
<span class="large-title">{{ group.displayName || "电表" }}</span>
|
||||||
</div>
|
<div class="info">
|
||||||
</div>
|
<div>状态:{{ group.statusText }}</div>
|
||||||
<el-row class="device-info-row">
|
<div>数据更新时间:{{ group.updateTimeText }}</div>
|
||||||
<el-col v-for="(tempDataItem,tempDataIndex) in deviceIdTypeMsg[item.deviceId]" :key="tempDataIndex+'dbTempData'" :span="8" class="device-info-col">
|
</div>
|
||||||
<span class="pointer" @click="showChart(tempDataItem.pointName,item.deviceId)">
|
</div>
|
||||||
<span class="left">{{tempDataItem.name}}</span> <span class="right">{{item[tempDataItem.attr]}}<span v-html="tempDataItem.unit"></span></span>
|
<el-row class="device-info-row">
|
||||||
|
<el-col
|
||||||
|
v-for="(item, dataIndex) in group.items"
|
||||||
|
:key="dataIndex + 'dbField'"
|
||||||
|
:span="8"
|
||||||
|
class="device-info-col"
|
||||||
|
:class="{ 'field-disabled': !item.pointId }"
|
||||||
|
>
|
||||||
|
<div class="field-click-wrapper" @click="handleFieldClick(item)">
|
||||||
|
<span class="left">{{ item.fieldName }}</span>
|
||||||
|
<span class="right">
|
||||||
|
<i v-if="isPointLoading(item.fieldValue)" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(item.fieldValue) | formatNumber }}</span>
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</div>
|
||||||
</el-row>
|
</el-col>
|
||||||
</el-card>
|
</el-row>
|
||||||
<el-empty v-show="list.length<=0" :image-size="200"></el-empty>
|
</el-card>
|
||||||
<point-chart ref="pointChart" :site-id="siteId"/>
|
|
||||||
<point-table ref="pointTable"/>
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import pointChart from "./../PointChart.vue";
|
import * as echarts from "echarts";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getAmmeterDataList } from "@/api/ems/dzjk";
|
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import { getProjectDisplayData } from "@/api/ems/dzjk";
|
||||||
|
import { getDeviceList, getPointConfigCurve } from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkSbjkDb",
|
name: "DzjkSbjkDb",
|
||||||
mixins: [getQuerySiteId,intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
components:{PointTable, pointChart},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
list:[],
|
displayData: [],
|
||||||
deviceIdTypeMsg:{
|
selectedSectionKey: "",
|
||||||
'LOAD':[
|
ammeterDeviceList: [],
|
||||||
{
|
curveDialogVisible: false,
|
||||||
name:'正向有功电能',
|
curveDialogTitle: "点位曲线",
|
||||||
attr:'forwardActive',
|
curveChart: null,
|
||||||
pointName:'正向有功电能'
|
curveLoading: false,
|
||||||
},
|
curveCustomRange: [],
|
||||||
{
|
curveQuery: {
|
||||||
name:'反向有功电能',
|
siteId: "",
|
||||||
attr:'reverseActive',
|
pointId: "",
|
||||||
pointName:'反向有功电能'
|
pointType: "data",
|
||||||
},
|
rangeType: "custom",
|
||||||
{
|
startTime: "",
|
||||||
name:'正向无功电能',
|
endTime: "",
|
||||||
attr:'forwardReactive',
|
},
|
||||||
pointName:'正向无功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'反向无功电能',
|
|
||||||
attr:'reverseReactive',
|
|
||||||
pointName:'反向无功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'有功功率',
|
|
||||||
attr:'activePower',
|
|
||||||
pointName:'总有功功率'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'无功功率',
|
|
||||||
attr:'reactivePower',
|
|
||||||
pointName:'总无功功率'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'METE':[
|
|
||||||
{
|
|
||||||
name:'正向有功电能',
|
|
||||||
attr:'forwardActive',
|
|
||||||
pointName:'正向有功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'反向有功电能',
|
|
||||||
attr:'reverseActive',
|
|
||||||
pointName:'反向有功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'正向无功电能',
|
|
||||||
attr:'forwardReactive',
|
|
||||||
pointName:'正向无功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'反向无功电能',
|
|
||||||
attr:'reverseReactive',
|
|
||||||
pointName:'反向无功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'有功功率',
|
|
||||||
attr:'activePower',
|
|
||||||
pointName:'总有功功率'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'无功功率',
|
|
||||||
attr:'reactivePower',
|
|
||||||
pointName:'总无功功率'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'METEGF':[
|
|
||||||
{
|
|
||||||
name:'有功电能',
|
|
||||||
attr:'activeEnergy',
|
|
||||||
pointName:'有功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'无功电能',
|
|
||||||
attr:'reactiveEnergy',
|
|
||||||
pointName:'无功电能'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'有功功率',
|
|
||||||
attr:'activePower',
|
|
||||||
pointName:'总有功功率'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name:'无功功率',
|
|
||||||
attr:'reactivePower',
|
|
||||||
pointName:'总无功功率'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
computed: {
|
||||||
// 查看设备电位表格
|
moduleDisplayData() {
|
||||||
pointDetail(row,dataType){
|
return (this.displayData || []).filter((item) => item.menuCode === "SBJK_DB");
|
||||||
const {deviceId} = row
|
|
||||||
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'AMMETER'},dataType)
|
|
||||||
},
|
},
|
||||||
showChart(pointName,deviceId){
|
dbTemplateFields() {
|
||||||
pointName && this.$refs.pointChart.showChart({pointName,deviceCategory:'AMMETER',deviceId})
|
const source = this.moduleDisplayData || [];
|
||||||
|
const result = [];
|
||||||
|
const seen = new Set();
|
||||||
|
source.forEach((item) => {
|
||||||
|
const fieldName = String(item?.fieldName || "").trim();
|
||||||
|
if (!fieldName || seen.has(fieldName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(fieldName);
|
||||||
|
result.push(fieldName);
|
||||||
|
});
|
||||||
|
return result.length > 0 ? result : this.fallbackFields;
|
||||||
},
|
},
|
||||||
updateData(){
|
sectionGroups() {
|
||||||
this.loading = true;
|
const source = this.moduleDisplayData || [];
|
||||||
getAmmeterDataList(this.siteId)
|
const devices = (this.ammeterDeviceList || []).length > 0
|
||||||
.then((response) => {
|
? this.ammeterDeviceList
|
||||||
this.list = response?.data || []
|
: [{ deviceId: "", deviceName: "电表" }];
|
||||||
})
|
|
||||||
.finally(() => {
|
return devices.map((device, index) => {
|
||||||
this.loading = false;
|
const deviceId = String(device?.deviceId || device?.id || "").trim();
|
||||||
});
|
const sectionKey = deviceId || `AMMETER_${index}`;
|
||||||
|
const displayName = String(device?.deviceName || device?.name || deviceId || `电表${index + 1}`).trim();
|
||||||
|
const exactRows = source.filter((item) => String(item?.deviceId || "").trim() === deviceId);
|
||||||
|
const fallbackRows = source.filter((item) => !String(item?.deviceId || "").trim());
|
||||||
|
|
||||||
|
const exactValueMap = {};
|
||||||
|
exactRows.forEach((item) => {
|
||||||
|
const key = String(item?.fieldName || "").trim();
|
||||||
|
if (key) {
|
||||||
|
exactValueMap[key] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const fallbackValueMap = {};
|
||||||
|
fallbackRows.forEach((item) => {
|
||||||
|
const key = String(item?.fieldName || "").trim();
|
||||||
|
if (key && fallbackValueMap[key] === undefined) {
|
||||||
|
fallbackValueMap[key] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = (this.dbTemplateFields || []).map((fieldName) => {
|
||||||
|
const row = exactValueMap[fieldName] || fallbackValueMap[fieldName] || {};
|
||||||
|
return {
|
||||||
|
fieldName,
|
||||||
|
fieldValue: row.fieldValue,
|
||||||
|
valueTime: row.valueTime,
|
||||||
|
pointId: String(row?.dataPoint || "").trim(),
|
||||||
|
raw: row,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusItem = (items || []).find((it) => String(it.fieldName || "").includes("状态"));
|
||||||
|
const timestamps = [...exactRows, ...fallbackRows]
|
||||||
|
.map((it) => new Date(it?.valueTime).getTime())
|
||||||
|
.filter((ts) => !isNaN(ts));
|
||||||
|
|
||||||
|
return {
|
||||||
|
sectionName: displayName,
|
||||||
|
sectionKey,
|
||||||
|
displayName,
|
||||||
|
deviceId,
|
||||||
|
items,
|
||||||
|
statusText: this.displayValue(statusItem ? statusItem.fieldValue : "-"),
|
||||||
|
updateTimeText: timestamps.length > 0 ? this.formatDate(new Date(Math.max(...timestamps))) : "-",
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
init() {
|
displaySectionGroups() {
|
||||||
this.updateData()
|
if (this.sectionGroups.length > 0) {
|
||||||
this.updateInterval(this.updateData)
|
return this.sectionGroups;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sectionName: "电参量",
|
||||||
|
sectionKey: "电参量",
|
||||||
|
displayName: "电表",
|
||||||
|
items: this.fallbackFields.map((fieldName) => ({ fieldName, fieldValue: "-" })),
|
||||||
|
statusText: "-",
|
||||||
|
updateTimeText: "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
filteredSectionGroups() {
|
||||||
|
const groups = this.displaySectionGroups || [];
|
||||||
|
if (!this.selectedSectionKey) {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
return groups.filter((group) => group.sectionKey === this.selectedSectionKey);
|
||||||
|
},
|
||||||
|
fallbackFields() {
|
||||||
|
return [
|
||||||
|
"正向有功电能",
|
||||||
|
"反向有功电能",
|
||||||
|
"正向无功电能",
|
||||||
|
"反向无功电能",
|
||||||
|
"有功功率",
|
||||||
|
"无功功率",
|
||||||
|
];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {},
|
methods: {
|
||||||
|
handleFieldClick(item) {
|
||||||
|
const pointId = String(item?.pointId || item?.raw?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openCurveDialog({
|
||||||
|
pointId,
|
||||||
|
title: item?.fieldName || pointId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openCurveDialog({ pointId, title }) {
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || pointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query)
|
||||||
|
.then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map((item) => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map((item) => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTagClick(sectionKey) {
|
||||||
|
this.selectedSectionKey = sectionKey || "";
|
||||||
|
},
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
|
formatDate(date) {
|
||||||
|
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(
|
||||||
|
date.getMinutes()
|
||||||
|
)}:${p(date.getSeconds())}`;
|
||||||
|
},
|
||||||
|
resolveDbDisplayName(sectionName) {
|
||||||
|
const key = String(sectionName || "").trim();
|
||||||
|
if (!key) {
|
||||||
|
return "电表";
|
||||||
|
}
|
||||||
|
const list = this.ammeterDeviceList || [];
|
||||||
|
const matched = list.find((item) => {
|
||||||
|
const deviceId = String(item.deviceId || item.id || "").trim();
|
||||||
|
const deviceName = String(item.deviceName || item.name || "").trim();
|
||||||
|
return key === deviceId || key === deviceName;
|
||||||
|
});
|
||||||
|
if (matched) {
|
||||||
|
return matched.deviceName || matched.name || key;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
},
|
||||||
|
getAmmeterDeviceList() {
|
||||||
|
return getDeviceList(this.siteId)
|
||||||
|
.then((response) => {
|
||||||
|
const list = response?.data || [];
|
||||||
|
this.ammeterDeviceList = list.filter((item) => item.deviceCategory === "AMMETER");
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.ammeterDeviceList = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateData() {
|
||||||
|
this.loading = true;
|
||||||
|
Promise.all([getProjectDisplayData(this.siteId), this.getAmmeterDeviceList()])
|
||||||
|
.then(([response]) => {
|
||||||
|
this.displayData = response?.data || [];
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.updateData();
|
||||||
|
this.updateInterval(this.updateData);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.sbjk-card-container {
|
.sbjk-card-container {
|
||||||
&.list:not(:last-child){
|
&.list:not(:last-child) {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pcs-tags {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tag-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info-col {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-click-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info-col.field-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,38 +1,44 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<el-card
|
<el-card
|
||||||
v-for="(item,index) in list"
|
v-for="(item,index) in list"
|
||||||
:key="index+'ylLise'"
|
:key="index+'ylLise'"
|
||||||
class="sbjk-card-container running-card-container"
|
class="sbjk-card-container running-card-container"
|
||||||
shadow="always">
|
shadow="always">
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="large-title">{{item.deviceName}}</span>
|
<span class="large-title">{{ item.deviceName }}</span>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div>数据更新时间:{{ item.dataUpdateTime || '-'}}</div>
|
<div>数据更新时间:{{ item.dataUpdateTime || '-' }}</div>
|
||||||
</div>
|
|
||||||
<div class="alarm">
|
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">详细</el-button>
|
|
||||||
<el-badge :value="item.alarmNum || 0" class="item">
|
|
||||||
<i
|
|
||||||
class="el-icon-message-solid alarm-icon"
|
|
||||||
@click="pointDetail(item,'alarmPoint')"
|
|
||||||
></i>
|
|
||||||
</el-badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<el-row class="device-info-row">
|
<div class="alarm">
|
||||||
<el-col v-for="(tempDataItem,tempDataIndex) in tempData" :key="tempDataIndex+'hdTempData'" :span="12" class="device-info-col">
|
<el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
|
||||||
|
详细
|
||||||
|
</el-button>
|
||||||
|
<el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
|
||||||
|
<i
|
||||||
|
class="el-icon-message-solid alarm-icon"
|
||||||
|
@click="pointDetail(item,'alarmPoint')"
|
||||||
|
></i>
|
||||||
|
</el-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row class="device-info-row">
|
||||||
|
<el-col v-for="(tempDataItem,tempDataIndex) in tempData" :key="tempDataIndex+'hdTempData'" :span="12"
|
||||||
|
class="device-info-col">
|
||||||
<span class="pointer" @click="showChart(tempDataItem.title,item.deviceId)">
|
<span class="pointer" @click="showChart(tempDataItem.title,item.deviceId)">
|
||||||
<span class="left">{{tempDataItem.title}}</span> <span class="right">{{item[tempDataItem.attr]}}<span v-html="tempDataItem.unit"></span></span>
|
<span class="left">{{ tempDataItem.title }}</span>
|
||||||
|
<span class="right">
|
||||||
|
<i v-if="isPointLoading(item[tempDataItem.attr])" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(item[tempDataItem.attr]) }}<span v-html="tempDataItem.unit"></span></span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-empty v-show="list.length<=0" :image-size="200"></el-empty>
|
<el-empty v-show="list.length<=0" :image-size="200"></el-empty>
|
||||||
<point-chart ref="pointChart" :site-id="siteId"/>
|
<point-chart ref="pointChart" :site-id="siteId"/>
|
||||||
<point-table ref="pointTable"/>
|
<point-table ref="pointTable"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@ -42,68 +48,93 @@ import {getDhDataList} from '@/api/ems/dzjk'
|
|||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
import pointChart from "./../PointChart.vue";
|
import pointChart from "./../PointChart.vue";
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkSbjkDh',
|
name: 'DzjkSbjkDh',
|
||||||
mixins:[getQuerySiteId,intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
components:{pointChart,PointTable},
|
components: {pointChart, PointTable},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
list:[],
|
list: [],
|
||||||
tempData:[
|
tempData: [
|
||||||
{title:'湿度',attr:'humidity',unit:'℃'},
|
{title: '湿度', attr: 'humidity', unit: ''},
|
||||||
{title:'温度',attr:'temperature',unit:''},
|
{title: '温度', attr: 'temperature', unit: '℃'},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
// 查看设备电位表格
|
// 查看设备电位表格
|
||||||
pointDetail(row,dataType){
|
pointDetail(row, dataType) {
|
||||||
const {deviceId} = row
|
const {deviceId} = row
|
||||||
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'DH'},dataType)
|
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'DH'}, dataType)
|
||||||
},
|
},
|
||||||
showChart(pointName,deviceId){
|
showChart(pointName, deviceId) {
|
||||||
pointName && this.$refs.pointChart.showChart({pointName,deviceCategory:'DH',deviceId})
|
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'DH', deviceId})
|
||||||
},
|
},
|
||||||
updateData(){
|
updateData() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
getDhDataList(this.siteId).then(response => {
|
getDhDataList(this.siteId).then(response => {
|
||||||
this.list = JSON.parse(JSON.stringify(response?.data || []));
|
this.list = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
}).finally(() => {this.loading = false})
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
init(){
|
init() {
|
||||||
this.updateData()
|
this.updateData()
|
||||||
this.updateInterval(this.updateData)
|
this.updateInterval(this.updateData)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.sbjk-card-container{
|
.sbjk-card-container {
|
||||||
&:not(:last-child){
|
&:not(:last-child) {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
.el-row{
|
|
||||||
|
.el-row {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border:1px solid #eeeeee;
|
border: 1px solid #eeeeee;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
.el-col{
|
|
||||||
padding:12px 0;
|
.el-col {
|
||||||
|
padding: 12px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.el-col{
|
|
||||||
|
.el-col {
|
||||||
border-bottom: 1px solid #eeeeee;
|
border-bottom: 1px solid #eeeeee;
|
||||||
}
|
}
|
||||||
.el-col:not(:nth-child(3n)){
|
|
||||||
|
.el-col:not(:nth-child(3n)) {
|
||||||
border-right: 1px solid #eeeeee;
|
border-right: 1px solid #eeeeee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -15,10 +15,10 @@
|
|||||||
{{ item.clusterDeviceId }}
|
{{ item.clusterDeviceId }}
|
||||||
</div>
|
</div>
|
||||||
<div>#{{ item.deviceId }}</div>
|
<div>#{{ item.deviceId }}</div>
|
||||||
<div class="dy pointer" @click="chartDetail(item, '电压 (V)')">
|
<div class="dy pointer" @click="chartDetail(item, 'voltage')">
|
||||||
{{ item.voltage }}V
|
{{ item.voltage }}V
|
||||||
</div>
|
</div>
|
||||||
<div class="wd pointer" @click="chartDetail(item, '温度 (℃)')">
|
<div class="wd pointer" @click="chartDetail(item, 'temperature')">
|
||||||
{{ item.temperature }}℃
|
{{ item.temperature }}℃
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -102,9 +102,8 @@ export default {
|
|||||||
return className;
|
return className;
|
||||||
},
|
},
|
||||||
//查看表格行图表
|
//查看表格行图表
|
||||||
chartDetail(row, dataType = "") {
|
chartDetail(row, fieldKey = "") {
|
||||||
const { clusterDeviceId, deviceId } = row;
|
this.$emit("chart", { ...row, fieldKey });
|
||||||
this.$emit("chart", { ...row, dataType });
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<el-table-column prop="voltage" label="电压 (V)">
|
<el-table-column prop="voltage" label="电压 (V)">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@click="chartDetail(scope.row, '电压 (V)')"
|
@click="chartDetail(scope.row, 'voltage')"
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
<el-table-column prop="temperature" label="温度 (℃)">
|
<el-table-column prop="temperature" label="温度 (℃)">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@click="chartDetail(scope.row, '温度 (℃)')"
|
@click="chartDetail(scope.row, 'temperature')"
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
<el-table-column prop="soc" label="SOC (%)">
|
<el-table-column prop="soc" label="SOC (%)">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@click="chartDetail(scope.row, 'SOC (%)')"
|
@click="chartDetail(scope.row, 'soc')"
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<el-table-column prop="soh" label="SOH (%)">
|
<el-table-column prop="soh" label="SOH (%)">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@click="chartDetail(scope.row, 'SOH (%)')"
|
@click="chartDetail(scope.row, 'soh')"
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@ -52,16 +52,6 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="160">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button @click="$emit('pointDetail',scope.row,'point')" type="text" size="small">
|
|
||||||
详细
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="$emit('pointDetail',scope.row,'alarmPoint')" type="text" size="small">
|
|
||||||
报警点位详细
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- <el-pagination
|
<!-- <el-pagination
|
||||||
v-show="tableData.length > 0"
|
v-show="tableData.length > 0"
|
||||||
@ -117,8 +107,8 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
//查看表格行图表
|
//查看表格行图表
|
||||||
chartDetail(row, dataType = "") {
|
chartDetail(row, fieldKey = "") {
|
||||||
this.$emit("chart", {...row, dataType});
|
this.$emit("chart", {...row, fieldKey});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -88,7 +88,6 @@
|
|||||||
:totalSize="totalSize"
|
:totalSize="totalSize"
|
||||||
:pointIdList="pointIdList"
|
:pointIdList="pointIdList"
|
||||||
@chart="chartDetail"
|
@chart="chartDetail"
|
||||||
@pointDetail="pointDetail"
|
|
||||||
></component>
|
></component>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-show="tableData.length > 0"
|
v-show="tableData.length > 0"
|
||||||
@ -104,34 +103,55 @@
|
|||||||
>
|
>
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
<chart-detail ref="chartDetail" />
|
<chart-detail ref="chartDetail" />
|
||||||
<point-chart ref="pointChart" :site-id="siteId" />
|
<el-dialog
|
||||||
<point-table ref="pointTable"/>
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import * as echarts from "echarts";
|
||||||
import BarChart from "./BarChart";
|
import BarChart from "./BarChart";
|
||||||
import {
|
import {
|
||||||
getStackNameList,
|
|
||||||
getClusterNameList,
|
|
||||||
getClusterDataInfoList,
|
getClusterDataInfoList,
|
||||||
|
getClusterNameList,
|
||||||
|
getStackNameList,
|
||||||
} from "@/api/ems/dzjk";
|
} from "@/api/ems/dzjk";
|
||||||
|
import { getPointConfigCurve } from "@/api/ems/site";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import ChartDetail from "./ChartDetail.vue";
|
import ChartDetail from "./ChartDetail.vue";
|
||||||
import Table from "./Table.vue";
|
import Table from "./Table.vue";
|
||||||
import List from "./List.vue";
|
import List from "./List.vue";
|
||||||
import pointChart from "./../PointChart.vue";
|
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkSbjkDtdc",
|
name: "DzjkSbjkDtdc",
|
||||||
mixins: [getQuerySiteId],
|
mixins: [getQuerySiteId],
|
||||||
components: {
|
components: {
|
||||||
PointTable,
|
|
||||||
BarChart,
|
BarChart,
|
||||||
ChartDetail,
|
ChartDetail,
|
||||||
DtdcTable: Table,
|
DtdcTable: Table,
|
||||||
DtdcList: List,
|
DtdcList: List,
|
||||||
pointChart,
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
pointIdList() {
|
pointIdList() {
|
||||||
@ -166,27 +186,190 @@ export default {
|
|||||||
pageNum: 1, //分页栏当前页数
|
pageNum: 1, //分页栏当前页数
|
||||||
totalSize: 0, //table表格数据总数
|
totalSize: 0, //table表格数据总数
|
||||||
activeBtn: "table",
|
activeBtn: "table",
|
||||||
|
curveDialogVisible: false,
|
||||||
|
curveDialogTitle: "点位曲线",
|
||||||
|
curveChart: null,
|
||||||
|
curveLoading: false,
|
||||||
|
curveCustomRange: [],
|
||||||
|
curveQuery: {
|
||||||
|
siteId: "",
|
||||||
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 查看设备电位表格
|
getFieldPointConfig(fieldKey) {
|
||||||
pointDetail(row,dataType){
|
const pointMap = {
|
||||||
const {deviceId,clusterDeviceId} = row
|
voltage: { pointIdKey: "voltagePointId", title: "电压 (V)" },
|
||||||
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'BATTERY',parentId:clusterDeviceId},dataType)
|
temperature: { pointIdKey: "temperaturePointId", title: "温度 (℃)" },
|
||||||
|
soc: { pointIdKey: "socPointId", title: "SOC (%)" },
|
||||||
|
soh: { pointIdKey: "sohPointId", title: "SOH (%)" },
|
||||||
|
};
|
||||||
|
return pointMap[String(fieldKey || "").trim()] || null;
|
||||||
|
},
|
||||||
|
openCurveDialogByPointId(pointId, title) {
|
||||||
|
const normalizedPointId = String(pointId || "").trim();
|
||||||
|
if (!normalizedPointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || normalizedPointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId: normalizedPointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(
|
||||||
|
d.getHours()
|
||||||
|
)}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query)
|
||||||
|
.then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map((item) => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map((item) => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
changeMenu(menu) {
|
changeMenu(menu) {
|
||||||
const { activeBtn } = this;
|
const { activeBtn } = this;
|
||||||
activeBtn !== menu && (this.activeBtn = menu);
|
activeBtn !== menu && (this.activeBtn = menu);
|
||||||
},
|
},
|
||||||
//查看表格行图表
|
//查看表格行图表
|
||||||
chartDetail({ deviceId, clusterDeviceId, dataType = "" }) {
|
chartDetail(row = {}) {
|
||||||
dataType &&
|
const config = this.getFieldPointConfig(row.fieldKey);
|
||||||
this.$refs.pointChart.showChart({
|
if (!config) return;
|
||||||
pointName: dataType,
|
const pointId = row[config.pointIdKey];
|
||||||
deviceCategory:'BATTERY',
|
this.openCurveDialogByPointId(pointId, config.title);
|
||||||
deviceId: clusterDeviceId,
|
|
||||||
child: [deviceId],
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
// 分页
|
// 分页
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
@ -224,6 +407,9 @@ export default {
|
|||||||
);
|
);
|
||||||
this.search.clusterId = "";
|
this.search.clusterId = "";
|
||||||
this.getClusterList();
|
this.getClusterList();
|
||||||
|
} else {
|
||||||
|
this.search.clusterId = "";
|
||||||
|
this.clusterOptions = [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//表格数据
|
//表格数据
|
||||||
@ -254,19 +440,26 @@ export default {
|
|||||||
},
|
},
|
||||||
getStackList() {
|
getStackList() {
|
||||||
getStackNameList(this.siteId).then((response) => {
|
getStackNameList(this.siteId).then((response) => {
|
||||||
this.stackOptions = JSON.parse(JSON.stringify(response?.data || []));
|
const list = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
|
this.stackOptions = list;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getClusterList() {
|
getClusterList() {
|
||||||
|
const { stackId } = this.search;
|
||||||
|
if (!stackId) {
|
||||||
|
this.clusterOptions = [];
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
this.clusterloading = true;
|
this.clusterloading = true;
|
||||||
getClusterNameList({
|
const currentStackId = String(stackId);
|
||||||
stackDeviceId: this.search.stackId,
|
return getClusterNameList({
|
||||||
|
stackDeviceId: stackId,
|
||||||
siteId: this.siteId,
|
siteId: this.siteId,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.clusterOptions = JSON.parse(
|
// 避免用户快速切换电池堆时旧请求覆盖新数据
|
||||||
JSON.stringify(response?.data || [])
|
if (String(this.search.stackId || "") !== currentStackId) return;
|
||||||
);
|
this.clusterOptions = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.clusterloading = false;
|
this.clusterloading = false;
|
||||||
|
|||||||
481
src/views/ems/dzjk/sbjk/ems/index.vue
Normal file
481
src/views/ems/dzjk/sbjk/ems/index.vue
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="pcs-tags">
|
||||||
|
<el-tag
|
||||||
|
size="small"
|
||||||
|
:type="selectedSectionKey ? 'info' : 'primary'"
|
||||||
|
:effect="selectedSectionKey ? 'plain' : 'dark'"
|
||||||
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick('')"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-for="(group, index) in sectionGroups"
|
||||||
|
:key="index + 'emsTag'"
|
||||||
|
size="small"
|
||||||
|
:type="selectedSectionKey === group.sectionKey ? 'primary' : 'info'"
|
||||||
|
:effect="selectedSectionKey === group.sectionKey ? 'dark' : 'plain'"
|
||||||
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick(group.sectionKey)"
|
||||||
|
>
|
||||||
|
{{ group.displayName || group.sectionName || "EMS" }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-card
|
||||||
|
v-for="(group, index) in filteredSectionGroups"
|
||||||
|
:key="index + 'emsSection'"
|
||||||
|
class="sbjk-card-container list running-card-container"
|
||||||
|
shadow="always"
|
||||||
|
>
|
||||||
|
<div slot="header">
|
||||||
|
<span class="large-title">{{ group.displayName || group.sectionName || "EMS" }}</span>
|
||||||
|
<div class="info">
|
||||||
|
<div>状态:{{ group.statusText }}</div>
|
||||||
|
<div>数据更新时间:{{ group.updateTimeText }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row class="device-info-row">
|
||||||
|
<el-col
|
||||||
|
v-for="(item, dataIndex) in group.items"
|
||||||
|
:key="dataIndex + 'emsField'"
|
||||||
|
:span="6"
|
||||||
|
class="device-info-col"
|
||||||
|
:class="{ 'field-disabled': !item.pointId }"
|
||||||
|
>
|
||||||
|
<div class="field-click-wrapper" @click="handleFieldClick(item)">
|
||||||
|
<span class="left">{{ item.fieldName }}</span>
|
||||||
|
<span class="right">
|
||||||
|
<i v-if="isPointLoading(item.fieldValue)" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(item.fieldValue) | formatNumber }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
|
import { getProjectDisplayData } from "@/api/ems/dzjk";
|
||||||
|
import { getDeviceList, getPointConfigCurve } from "@/api/ems/site";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DzjkSbjkEms",
|
||||||
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
displayData: [],
|
||||||
|
selectedSectionKey: "",
|
||||||
|
emsDeviceList: [],
|
||||||
|
curveDialogVisible: false,
|
||||||
|
curveDialogTitle: "点位曲线",
|
||||||
|
curveChart: null,
|
||||||
|
curveLoading: false,
|
||||||
|
curveCustomRange: [],
|
||||||
|
curveQuery: {
|
||||||
|
siteId: "",
|
||||||
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
moduleDisplayData() {
|
||||||
|
return (this.displayData || []).filter((item) => item.menuCode === "SBJK_EMS");
|
||||||
|
},
|
||||||
|
emsTemplateFields() {
|
||||||
|
const source = this.moduleDisplayData || [];
|
||||||
|
const result = [];
|
||||||
|
const seen = new Set();
|
||||||
|
source.forEach((item) => {
|
||||||
|
const fieldName = String(item?.fieldName || "").trim();
|
||||||
|
if (!fieldName || seen.has(fieldName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(fieldName);
|
||||||
|
result.push(fieldName);
|
||||||
|
});
|
||||||
|
return result.length > 0 ? result : this.fallbackFields;
|
||||||
|
},
|
||||||
|
sectionGroups() {
|
||||||
|
const source = this.moduleDisplayData || [];
|
||||||
|
const devices = (this.emsDeviceList || []).length > 0
|
||||||
|
? this.emsDeviceList
|
||||||
|
: [{ deviceId: "", deviceName: "EMS" }];
|
||||||
|
|
||||||
|
return devices.map((device, index) => {
|
||||||
|
const deviceId = String(device?.deviceId || device?.id || "").trim();
|
||||||
|
const sectionKey = deviceId || `EMS_${index}`;
|
||||||
|
const displayName = String(device?.deviceName || device?.name || deviceId || `EMS${index + 1}`).trim();
|
||||||
|
const exactRows = source.filter((item) => String(item?.deviceId || "").trim() === deviceId);
|
||||||
|
const fallbackRows = source.filter((item) => !String(item?.deviceId || "").trim());
|
||||||
|
|
||||||
|
const exactValueMap = {};
|
||||||
|
exactRows.forEach((item) => {
|
||||||
|
const key = String(item?.fieldName || "").trim();
|
||||||
|
if (key) {
|
||||||
|
exactValueMap[key] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const fallbackValueMap = {};
|
||||||
|
fallbackRows.forEach((item) => {
|
||||||
|
const key = String(item?.fieldName || "").trim();
|
||||||
|
if (key && fallbackValueMap[key] === undefined) {
|
||||||
|
fallbackValueMap[key] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = (this.emsTemplateFields || []).map((fieldName) => {
|
||||||
|
const row = exactValueMap[fieldName] || fallbackValueMap[fieldName] || {};
|
||||||
|
return {
|
||||||
|
fieldName,
|
||||||
|
fieldValue: row.fieldValue,
|
||||||
|
valueTime: row.valueTime,
|
||||||
|
pointId: String(row?.dataPoint || "").trim(),
|
||||||
|
raw: row,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusItem = (items || []).find((it) => String(it.fieldName || "").includes("状态"));
|
||||||
|
const timestamps = [...exactRows, ...fallbackRows]
|
||||||
|
.map((it) => new Date(it?.valueTime).getTime())
|
||||||
|
.filter((ts) => !isNaN(ts));
|
||||||
|
|
||||||
|
return {
|
||||||
|
sectionName: displayName,
|
||||||
|
sectionKey,
|
||||||
|
displayName,
|
||||||
|
deviceId,
|
||||||
|
items,
|
||||||
|
statusText: this.displayValue(statusItem ? statusItem.fieldValue : "-"),
|
||||||
|
updateTimeText: timestamps.length > 0 ? this.formatDate(new Date(Math.max(...timestamps))) : "-",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
displaySectionGroups() {
|
||||||
|
if (this.sectionGroups.length > 0) {
|
||||||
|
return this.sectionGroups;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sectionName: "EMS",
|
||||||
|
items: this.fallbackFields.map((fieldName) => ({ fieldName, fieldValue: "-" })),
|
||||||
|
statusText: "-",
|
||||||
|
updateTimeText: "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
filteredSectionGroups() {
|
||||||
|
const groups = this.displaySectionGroups || [];
|
||||||
|
if (!this.selectedSectionKey) {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
return groups.filter((group) => group.sectionKey === this.selectedSectionKey);
|
||||||
|
},
|
||||||
|
fallbackFields() {
|
||||||
|
return [
|
||||||
|
"BMS1SOC",
|
||||||
|
"BMS2SOC",
|
||||||
|
"BMS3SOC",
|
||||||
|
"BMS4SOC",
|
||||||
|
"PCS-1有功功率",
|
||||||
|
"PCS-2有功功率",
|
||||||
|
"PCS-3有功功率",
|
||||||
|
"PCS-4有功功率",
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleFieldClick(item) {
|
||||||
|
const pointId = String(item?.pointId || item?.raw?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openCurveDialog({
|
||||||
|
pointId,
|
||||||
|
title: item?.fieldName || pointId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openCurveDialog({ pointId, title }) {
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || pointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query)
|
||||||
|
.then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map((item) => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map((item) => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTagClick(sectionKey) {
|
||||||
|
this.selectedSectionKey = sectionKey || "";
|
||||||
|
},
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
|
formatDate(date) {
|
||||||
|
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(
|
||||||
|
date.getMinutes()
|
||||||
|
)}:${p(date.getSeconds())}`;
|
||||||
|
},
|
||||||
|
getEmsDeviceList() {
|
||||||
|
return getDeviceList(this.siteId)
|
||||||
|
.then((response) => {
|
||||||
|
const list = response?.data || [];
|
||||||
|
this.emsDeviceList = list.filter((item) => item.deviceCategory === "EMS");
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.emsDeviceList = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateData() {
|
||||||
|
this.loading = true;
|
||||||
|
Promise.all([getProjectDisplayData(this.siteId), this.getEmsDeviceList()])
|
||||||
|
.then(([response]) => {
|
||||||
|
this.displayData = response?.data || [];
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.updateData();
|
||||||
|
this.updateInterval(this.updateData);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.sbjk-card-container {
|
||||||
|
&.list:not(:last-child) {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tags {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tag-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info-col {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-click-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info-col.field-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -72,4 +72,3 @@ export default {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@ -1,41 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading" class="pcs-ems-dashboard-editor-container">
|
<div class="pcs-ems-dashboard-editor-container">
|
||||||
<!-- 顶部六个方块-->
|
<div class="pcs-tags">
|
||||||
<real-time-base-info :data="runningHeadData" />
|
<el-tag
|
||||||
|
size="small"
|
||||||
|
:type="selectedPcsId ? 'info' : 'primary'"
|
||||||
|
:effect="selectedPcsId ? 'plain' : 'dark'"
|
||||||
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick('')"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-for="(item, index) in pcsDeviceList"
|
||||||
|
:key="index + 'pcsTag'"
|
||||||
|
size="small"
|
||||||
|
:type="selectedPcsId === (item.deviceId || item.id) ? 'primary' : 'info'"
|
||||||
|
:effect="selectedPcsId === (item.deviceId || item.id) ? 'dark' : 'plain'"
|
||||||
|
class="pcs-tag-item"
|
||||||
|
@click="handleTagClick(item.deviceId || item.id || '')"
|
||||||
|
>
|
||||||
|
{{ item.deviceName || item.deviceId || item.id || 'PCS' }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="(pcsItem, pcsIndex) in pcsList"
|
v-for="(pcsItem, pcsIndex) in filteredPcsList"
|
||||||
:key="pcsIndex + 'PcsHome'"
|
:key="pcsIndex + 'PcsHome'"
|
||||||
style="margin-bottom: 25px"
|
style="margin-bottom: 25px"
|
||||||
>
|
>
|
||||||
<el-card
|
<el-card
|
||||||
:class="{
|
:class="handleCardClass(pcsItem)"
|
||||||
'warning-card-container': pcsItem.workStatus === '1',
|
class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
|
||||||
'timing-card-container': pcsItem.workStatus === '2',
|
shadow="always"
|
||||||
'running-card-container': !['1', '2'].includes(pcsItem.workStatus),
|
|
||||||
}"
|
|
||||||
class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
|
|
||||||
shadow="always"
|
|
||||||
>
|
>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="large-title"
|
<span class="large-title"
|
||||||
>{{ pcsItem.deviceName }}</span
|
>{{ pcsItem.deviceName }}</span
|
||||||
>
|
>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div>
|
<div v-if="(($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[pcsItem.communicationStatus]">
|
||||||
{{
|
{{ (($store.state.ems && $store.state.ems.communicationStatusOptions) || {})[pcsItem.communicationStatus] }}
|
||||||
$store.state.ems.communicationStatusOptions[
|
|
||||||
pcsItem.communicationStatus
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
<div>数据更新时间:{{ pcsItem.dataUpdateTime }}</div>
|
<div>数据更新时间:{{ pcsItem.dataUpdateTime }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alarm">
|
<div class="alarm">
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(pcsItem,'point')">详细</el-button>
|
<el-badge :hidden="!pcsItem.alarmNum" :value="pcsItem.alarmNum || 0" class="item">
|
||||||
<el-badge :value="pcsItem.alarmNum || 0" class="item">
|
|
||||||
<i
|
<i
|
||||||
class="el-icon-message-solid alarm-icon"
|
class="el-icon-message-solid alarm-icon"
|
||||||
@click="pointDetail(pcsItem,'alarmPoint')"
|
@click="pointDetail(pcsItem,'alarmPoint')"
|
||||||
></i>
|
></i>
|
||||||
</el-badge>
|
</el-badge>
|
||||||
</div>
|
</div>
|
||||||
@ -43,160 +54,243 @@
|
|||||||
<div class="descriptions-main">
|
<div class="descriptions-main">
|
||||||
<el-descriptions :colon="false" :column="4" direction="vertical">
|
<el-descriptions :colon="false" :column="4" direction="vertical">
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
:contentClassName="`descriptions-direction ${
|
contentClassName="descriptions-direction work-status"
|
||||||
pcsItem.workStatus === '0' ? 'save' : 'danger'
|
:span="1"
|
||||||
|
label="工作状态"
|
||||||
|
labelClassName="descriptions-label"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasFieldPointId(pcsItem, 'workStatus') }"
|
||||||
|
@click="handlePcsFieldClick(pcsItem, 'workStatus', '工作状态')"
|
||||||
|
>
|
||||||
|
{{ formatDictValue((PCSWorkStatusOptions || {}), pcsItem.workStatus) }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item
|
||||||
|
>
|
||||||
|
<el-descriptions-item
|
||||||
|
:span="1"
|
||||||
|
contentClassName="descriptions-direction"
|
||||||
|
label="并网状态"
|
||||||
|
labelClassName="descriptions-label"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasFieldPointId(pcsItem, 'gridStatus') }"
|
||||||
|
@click="handlePcsFieldClick(pcsItem, 'gridStatus', '并网状态')"
|
||||||
|
>
|
||||||
|
{{ formatDictValue((($store.state.ems && $store.state.ems.gridStatusOptions) || {}), pcsItem.gridStatus) }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item
|
||||||
|
>
|
||||||
|
<el-descriptions-item
|
||||||
|
:contentClassName="`descriptions-direction ${
|
||||||
|
pcsItem.deviceStatus === '1' ? 'save' : 'danger'
|
||||||
}`"
|
}`"
|
||||||
:span="1"
|
:span="1"
|
||||||
label="工作状态"
|
label="设备状态"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>{{
|
>
|
||||||
$store.state.ems.workStatusOptions[pcsItem.workStatus]
|
<span
|
||||||
}}</el-descriptions-item
|
class="pointer"
|
||||||
|
:class="{ 'field-disabled': !hasFieldPointId(pcsItem, 'deviceStatus') }"
|
||||||
|
@click="handlePcsFieldClick(pcsItem, 'deviceStatus', '设备状态')"
|
||||||
|
>
|
||||||
|
{{ formatDictValue((($store.state.ems && $store.state.ems.deviceStatusOptions) || {}), pcsItem.deviceStatus) }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item
|
||||||
>
|
>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
:span="1"
|
:span="1"
|
||||||
contentClassName="descriptions-direction"
|
contentClassName="descriptions-direction"
|
||||||
label="并网状态"
|
label="控制模式"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>{{
|
|
||||||
$store.state.ems.gridStatusOptions[pcsItem.gridStatus]
|
|
||||||
}}</el-descriptions-item
|
|
||||||
>
|
>
|
||||||
<el-descriptions-item
|
<span
|
||||||
:contentClassName="`descriptions-direction ${
|
class="pointer"
|
||||||
pcsItem.deviceStatus === '2' ? 'save' : 'danger'
|
:class="{ 'field-disabled': !hasFieldPointId(pcsItem, 'controlMode') }"
|
||||||
}`"
|
@click="handlePcsFieldClick(pcsItem, 'controlMode', '控制模式')"
|
||||||
:span="1"
|
>
|
||||||
label="设备状态"
|
{{ formatDictValue((($store.state.ems && $store.state.ems.controlModeOptions) || {}), pcsItem.controlMode) }}
|
||||||
labelClassName="descriptions-label"
|
</span>
|
||||||
>{{
|
</el-descriptions-item
|
||||||
$store.state.ems.deviceStatusOptions[pcsItem.deviceStatus]
|
|
||||||
}}</el-descriptions-item
|
|
||||||
>
|
|
||||||
<el-descriptions-item
|
|
||||||
:span="1"
|
|
||||||
contentClassName="descriptions-direction"
|
|
||||||
label="控制模式"
|
|
||||||
labelClassName="descriptions-label"
|
|
||||||
>{{
|
|
||||||
$store.state.ems.controlModeOptions[pcsItem.controlMode]
|
|
||||||
}}</el-descriptions-item
|
|
||||||
>
|
>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div class="descriptions-main descriptions-main-bg-color">
|
<div class="descriptions-main descriptions-main-bg-color">
|
||||||
<el-descriptions
|
<el-descriptions
|
||||||
:colon="false"
|
:colon="false"
|
||||||
:column="4"
|
:column="4"
|
||||||
contentClassName="descriptions-direction"
|
contentClassName="descriptions-direction"
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>
|
>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
v-for="(item, index) in infoData"
|
v-for="(item, index) in infoData"
|
||||||
:key="index + 'pcsInfoData'"
|
:key="index + 'pcsInfoData'"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:span="1"
|
:span="1"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
@click="
|
:class="{ 'field-disabled': !hasFieldPointId(pcsItem, item.attr) }"
|
||||||
showChart(item.pointName || '', pcsItem.deviceId)
|
@click="handlePcsFieldClick(pcsItem, item.attr, item.label)"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ pcsItem[item.attr] | formatNumber }}
|
<i v-if="isPointLoading(pcsItem[item.attr])" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(pcsItem[item.attr]) | formatNumber }}</span>
|
||||||
<span v-if="item.unit" v-html="item.unit"></span>
|
<span v-if="item.unit" v-html="item.unit"></span>
|
||||||
</span>
|
</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in pcsItem.pcsBranchInfoList"
|
v-for="(item, index) in pcsItem.pcsBranchInfoList"
|
||||||
:key="index + 'pcsBranchInfoList'"
|
:key="index + 'pcsBranchInfoList'"
|
||||||
class="descriptions-main"
|
class="descriptions-main"
|
||||||
>
|
>
|
||||||
<el-descriptions
|
<el-descriptions
|
||||||
:colon="false"
|
:colon="false"
|
||||||
:column="4"
|
:column="4"
|
||||||
contentClassName="descriptions-direction keep"
|
contentClassName="descriptions-direction keep"
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>
|
>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
:label="'支路' + (index + 1)"
|
:label="'支路' + (index + 1)"
|
||||||
:span="4"
|
:span="4"
|
||||||
contentClassName="descriptions-direction keep"
|
contentClassName="descriptions-direction keep"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>{{ item.dischargeStatus }}</el-descriptions-item
|
>{{ item.dischargeStatus }}
|
||||||
|
</el-descriptions-item
|
||||||
>
|
>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
:span="1"
|
:span="1"
|
||||||
contentClassName="descriptions-direction"
|
contentClassName="descriptions-direction"
|
||||||
label="直流功率"
|
label="直流功率"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
@click="showChart('直流功率', item.deviceId,true)"
|
:class="{ 'field-disabled': !item.dcPowerPointId }"
|
||||||
>{{ item.dcPower }}kW</span
|
@click="openCurveDialogByPointId(item.dcPowerPointId, '直流功率')"
|
||||||
|
>{{ item.dcPower }}kW</span
|
||||||
>
|
>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
:span="1"
|
:span="1"
|
||||||
contentClassName="descriptions-direction"
|
contentClassName="descriptions-direction"
|
||||||
label="直流电压"
|
label="直流电压"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
@click="showChart('直流电压', item.deviceId,true)"
|
:class="{ 'field-disabled': !item.dcVoltagePointId }"
|
||||||
>{{ item.dcVoltage }}V</span
|
@click="openCurveDialogByPointId(item.dcVoltagePointId, '直流电压')"
|
||||||
|
>{{ item.dcVoltage }}V</span
|
||||||
>
|
>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
:span="1"
|
:span="1"
|
||||||
contentClassName="descriptions-direction"
|
contentClassName="descriptions-direction"
|
||||||
label="直流电流"
|
label="直流电流"
|
||||||
labelClassName="descriptions-label"
|
labelClassName="descriptions-label"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
@click="showChart('直流电流', item.deviceId,true)"
|
:class="{ 'field-disabled': !item.dcCurrentPointId }"
|
||||||
>{{ item.dcCurrent }}A</span
|
@click="openCurveDialogByPointId(item.dcCurrentPointId, '直流电流')"
|
||||||
|
>{{ item.dcCurrent }}A</span
|
||||||
>
|
>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-show="pcsList.length <= 0" :image-size="200"></el-empty>
|
|
||||||
<point-chart ref="pointChart" :site-id="siteId" />
|
|
||||||
<point-table ref="pointTable"/>
|
<point-table ref="pointTable"/>
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import pointChart from "./../PointChart.vue";
|
import * as echarts from "echarts";
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
||||||
import RealTimeBaseInfo from "./../RealTimeBaseInfo.vue";
|
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getRunningHeadInfo, getPcsDetailInfo } from "@/api/ems/dzjk";
|
import {getPcsNameList, getProjectDisplayData} from "@/api/ems/dzjk";
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
import {getPointConfigCurve} from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkSbjkPcs",
|
name: "DzjkSbjkPcs",
|
||||||
components: { RealTimeBaseInfo, pointChart ,PointTable},
|
components: {PointTable},
|
||||||
mixins: [getQuerySiteId, intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
PCSWorkStatusOptions: state => state?.ems?.PCSWorkStatusOptions || {},
|
||||||
|
}),
|
||||||
|
filteredPcsList() {
|
||||||
|
if (!this.selectedPcsId) {
|
||||||
|
return this.pcsList || [];
|
||||||
|
}
|
||||||
|
return (this.pcsList || []).filter(item => item.deviceId === this.selectedPcsId);
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
runningHeadData: {}, //运行信息
|
displayData: [],
|
||||||
pcsList: [],
|
pcsDeviceList: [],
|
||||||
|
selectedPcsId: "",
|
||||||
|
curveDialogVisible: false,
|
||||||
|
curveDialogTitle: "点位曲线",
|
||||||
|
curveChart: null,
|
||||||
|
curveLoading: false,
|
||||||
|
curveCustomRange: [],
|
||||||
|
curveQuery: {
|
||||||
|
siteId: "",
|
||||||
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
},
|
||||||
|
pcsList: [{
|
||||||
|
deviceId: "",
|
||||||
|
deviceName: "PCS",
|
||||||
|
dataUpdateTime: "-",
|
||||||
|
alarmNum: 0,
|
||||||
|
pcsBranchInfoList: [],
|
||||||
|
}],
|
||||||
infoData: [
|
infoData: [
|
||||||
{
|
{
|
||||||
label: "总交流有功电率",
|
label: "总交流有功功率",
|
||||||
attr: "totalActivePower",
|
attr: "totalActivePower",
|
||||||
unit: "kW",
|
unit: "kW",
|
||||||
pointName: "总交流有功电率",
|
pointName: "总交流有功功率",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "当天交流充电量",
|
label: "当天交流充电量",
|
||||||
@ -204,7 +298,7 @@ export default {
|
|||||||
unit: "kWh",
|
unit: "kWh",
|
||||||
pointName: "当天交流充电量 (kWh)",
|
pointName: "当天交流充电量 (kWh)",
|
||||||
},
|
},
|
||||||
{ label: "A相电压", attr: "aPhaseVoltage", unit: "V", pointName: "" },
|
{label: "A相电压", attr: "aPhaseVoltage", unit: "V", pointName: ""},
|
||||||
{
|
{
|
||||||
label: "A相电流",
|
label: "A相电流",
|
||||||
attr: "aPhaseCurrent",
|
attr: "aPhaseCurrent",
|
||||||
@ -212,10 +306,10 @@ export default {
|
|||||||
pointName: "A相电流",
|
pointName: "A相电流",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "总交流无功电率",
|
label: "总交流无功功率",
|
||||||
attr: "totalReactivePower",
|
attr: "totalReactivePower",
|
||||||
unit: "kVar",
|
unit: "kVar",
|
||||||
pointName: "总交流无功电率",
|
pointName: "总交流无功功率",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "当天交流放电量",
|
label: "当天交流放电量",
|
||||||
@ -223,7 +317,7 @@ export default {
|
|||||||
unit: "kWh",
|
unit: "kWh",
|
||||||
pointName: "当天交流放电量 (kWh)",
|
pointName: "当天交流放电量 (kWh)",
|
||||||
},
|
},
|
||||||
{ label: "B相电压", attr: "bPhaseVoltage", unit: "V", pointName: "" },
|
{label: "B相电压", attr: "bPhaseVoltage", unit: "V", pointName: ""},
|
||||||
{
|
{
|
||||||
label: "B相电流",
|
label: "B相电流",
|
||||||
attr: "bPhaseCurrent",
|
attr: "bPhaseCurrent",
|
||||||
@ -242,7 +336,7 @@ export default {
|
|||||||
unit: "℃",
|
unit: "℃",
|
||||||
pointName: "",
|
pointName: "",
|
||||||
},
|
},
|
||||||
{ label: "C相电压", attr: "cPhaseVoltage", unit: "V", pointName: "" },
|
{label: "C相电压", attr: "cPhaseVoltage", unit: "V", pointName: ""},
|
||||||
{
|
{
|
||||||
label: "C相电流",
|
label: "C相电流",
|
||||||
attr: "cPhaseCurrent",
|
attr: "cPhaseCurrent",
|
||||||
@ -268,44 +362,348 @@ export default {
|
|||||||
pointName: "交流频率",
|
pointName: "交流频率",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pcsBranchList: [], //pcs的支路列表
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
|
normalizeDictKey(value) {
|
||||||
|
const raw = String(value == null ? "" : value).trim();
|
||||||
|
if (!raw) return "";
|
||||||
|
if (/^-?\d+(\.0+)?$/.test(raw)) {
|
||||||
|
return String(parseInt(raw, 10));
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
},
|
||||||
|
formatDictValue(options, value) {
|
||||||
|
const dict = (options && typeof options === "object") ? options : {};
|
||||||
|
const key = this.normalizeDictKey(value);
|
||||||
|
if (!key) return "-";
|
||||||
|
return dict[key] || key;
|
||||||
|
},
|
||||||
|
normalizeDeviceId(value) {
|
||||||
|
return String(value == null ? "" : value).trim().toUpperCase();
|
||||||
|
},
|
||||||
|
handleCardClass(item) {
|
||||||
|
const workStatus = this.normalizeDictKey((item && item.workStatus) || "");
|
||||||
|
const statusOptions = (this.PCSWorkStatusOptions && typeof this.PCSWorkStatusOptions === 'object')
|
||||||
|
? this.PCSWorkStatusOptions
|
||||||
|
: {};
|
||||||
|
const hasStatus = Object.prototype.hasOwnProperty.call(statusOptions, workStatus);
|
||||||
|
return workStatus === '1' || !hasStatus
|
||||||
|
? "timing-card-container"
|
||||||
|
: workStatus === '2'
|
||||||
|
? 'warning-card-container'
|
||||||
|
: 'running-card-container';
|
||||||
|
},
|
||||||
// 查看设备电位表格
|
// 查看设备电位表格
|
||||||
pointDetail(row,dataType){
|
pointDetail(row, dataType) {
|
||||||
const {deviceId} = row
|
const {deviceId} = row
|
||||||
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'PCS'},dataType)
|
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'PCS'}, dataType)
|
||||||
},
|
},
|
||||||
showChart(pointName, deviceId,isBranch=false) {
|
hasFieldPointId(pcsItem, fieldName) {
|
||||||
pointName &&
|
const row = this.getFieldRow(pcsItem, fieldName);
|
||||||
this.$refs.pointChart.showChart({ pointName,deviceCategory:isBranch ? 'BRANCH' : 'PCS', deviceId });
|
return !!String(row?.dataPoint || "").trim();
|
||||||
},
|
},
|
||||||
//6个方块数据
|
getFieldRow(pcsItem, fieldName) {
|
||||||
getRunningHeadData() {
|
const key = String(fieldName || "").trim();
|
||||||
getRunningHeadInfo(this.siteId).then((response) => {
|
const map = pcsItem?._fieldRowMap || {};
|
||||||
this.runningHeadData = response?.data || {};
|
return map[key] || null;
|
||||||
|
},
|
||||||
|
handlePcsFieldClick(pcsItem, fieldName, title) {
|
||||||
|
const row = this.getFieldRow(pcsItem, fieldName);
|
||||||
|
const pointId = String(row?.dataPoint || "").trim();
|
||||||
|
this.openCurveDialogByPointId(pointId, title || fieldName);
|
||||||
|
},
|
||||||
|
openCurveDialogByPointId(pointId, title) {
|
||||||
|
const normalizedPointId = String(pointId || "").trim();
|
||||||
|
if (!normalizedPointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || normalizedPointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId: normalizedPointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query).then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
}).catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
}).finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getPcsList() {
|
renderCurveChart(rows = []) {
|
||||||
this.loading = true;
|
if (!this.curveChart) return;
|
||||||
getPcsDetailInfo(this.siteId)
|
const xData = rows.map(item => this.formatCurveTime(item.dataTime));
|
||||||
.then((response) => {
|
const yData = rows.map(item => item.pointValue);
|
||||||
const data = response?.data || {};
|
this.curveChart.clear();
|
||||||
this.pcsList = JSON.parse(JSON.stringify(data));
|
this.curveChart.setOption({
|
||||||
})
|
legend: {},
|
||||||
.finally(() => (this.loading = false));
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTagClick(deviceId) {
|
||||||
|
this.selectedPcsId = deviceId || "";
|
||||||
|
},
|
||||||
|
getModuleRows(menuCode, sectionName) {
|
||||||
|
return (this.displayData || []).filter(item => item.menuCode === menuCode && item.sectionName === sectionName);
|
||||||
|
},
|
||||||
|
getFieldName(fieldCode) {
|
||||||
|
if (!fieldCode) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const index = fieldCode.lastIndexOf("__");
|
||||||
|
return index >= 0 ? fieldCode.slice(index + 2) : fieldCode;
|
||||||
|
},
|
||||||
|
getFieldMap(rows = [], deviceId = "") {
|
||||||
|
const rowMap = this.getFieldRowMap(rows, deviceId);
|
||||||
|
return Object.keys(rowMap).reduce((acc, fieldName) => {
|
||||||
|
const row = rowMap[fieldName] || {};
|
||||||
|
acc[fieldName] = row.fieldValue;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
getFieldRowMap(rows = [], deviceId = "") {
|
||||||
|
const map = {};
|
||||||
|
const targetDeviceId = this.normalizeDeviceId(deviceId || "");
|
||||||
|
// 设备维度优先:先吃 device_id 对应值,再用默认值(空 device_id)补齐
|
||||||
|
rows.forEach(item => {
|
||||||
|
if (!item || !item.fieldCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const itemDeviceId = this.normalizeDeviceId(item.deviceId || "");
|
||||||
|
if (itemDeviceId !== targetDeviceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
map[this.getFieldName(item.fieldCode)] = item;
|
||||||
|
});
|
||||||
|
rows.forEach(item => {
|
||||||
|
if (!item || !item.fieldCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const itemDeviceId = this.normalizeDeviceId(item.deviceId || "");
|
||||||
|
if (itemDeviceId !== "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fieldName = this.getFieldName(item.fieldCode);
|
||||||
|
if (map[fieldName] === undefined || map[fieldName] === null || map[fieldName] === "") {
|
||||||
|
map[fieldName] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
getLatestTime(menuCode) {
|
||||||
|
const times = (this.displayData || [])
|
||||||
|
.filter(item => item.menuCode === menuCode && item.valueTime)
|
||||||
|
.map(item => new Date(item.valueTime).getTime())
|
||||||
|
.filter(ts => !isNaN(ts));
|
||||||
|
if (times.length === 0) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
const date = new Date(Math.max(...times));
|
||||||
|
const p = (n) => String(n).padStart(2, '0');
|
||||||
|
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`;
|
||||||
|
},
|
||||||
|
getPcsDeviceList() {
|
||||||
|
return getPcsNameList(this.siteId).then((response) => {
|
||||||
|
this.pcsDeviceList = response?.data || [];
|
||||||
|
}).catch(() => {
|
||||||
|
this.pcsDeviceList = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buildPcsList() {
|
||||||
|
const devices = (this.pcsDeviceList && this.pcsDeviceList.length > 0)
|
||||||
|
? this.pcsDeviceList
|
||||||
|
: [{deviceId: this.siteId, deviceName: 'PCS'}];
|
||||||
|
this.pcsList = devices.map((device) => ({
|
||||||
|
...this.getFieldMap(this.getModuleRows('SBJK_PCS', '电参量'), device.deviceId || device.id || this.siteId),
|
||||||
|
deviceId: device.deviceId || device.id || this.siteId,
|
||||||
|
deviceName: device.deviceName || device.name || device.deviceId || device.id || 'PCS',
|
||||||
|
...this.getFieldMap(this.getModuleRows('SBJK_PCS', '状态'), device.deviceId || device.id || this.siteId),
|
||||||
|
_fieldRowMap: {
|
||||||
|
...this.getFieldRowMap(this.getModuleRows('SBJK_PCS', '电参量'), device.deviceId || device.id || this.siteId),
|
||||||
|
...this.getFieldRowMap(this.getModuleRows('SBJK_PCS', '状态'), device.deviceId || device.id || this.siteId),
|
||||||
|
},
|
||||||
|
dataUpdateTime: this.getLatestTime('SBJK_PCS'),
|
||||||
|
alarmNum: 0,
|
||||||
|
pcsBranchInfoList: [],
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
updateData() {
|
updateData() {
|
||||||
this.getRunningHeadData();
|
this.loading = true;
|
||||||
this.getPcsList();
|
// 先渲染卡片框架,字段值走单点位 loading
|
||||||
|
this.buildPcsList();
|
||||||
|
Promise.all([
|
||||||
|
getProjectDisplayData(this.siteId),
|
||||||
|
this.getPcsDeviceList(),
|
||||||
|
]).then(([displayResponse]) => {
|
||||||
|
this.displayData = displayResponse?.data || [];
|
||||||
|
this.buildPcsList();
|
||||||
|
}).finally(() => (this.loading = false));
|
||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
this.updateData();
|
this.updateData();
|
||||||
this.updateInterval(this.updateData);
|
this.updateInterval(this.updateData);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.pcs-tags {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
.pcs-tag-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card
|
<el-card
|
||||||
shadow="always"
|
shadow="always"
|
||||||
class="common-card-container common-card-container-body-no-padding"
|
class="common-card-container common-card-container-body-no-padding"
|
||||||
>
|
>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="card-title">PCS有功功率/PCS无功功率</span>
|
<span class="card-title">PCS有功功率/PCS无功功率</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 360px" id="cnglqxChart" />
|
<div ref="chartRef" style="height: 360px" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,17 +14,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import { storagePower } from "@/api/ems/dzjk";
|
import { getPointConfigCurve } from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize],
|
mixins: [resize],
|
||||||
|
props: {
|
||||||
|
displayData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
chart: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.chart = echarts.init(document.querySelector("#cnglqxChart"));
|
this.chart = echarts.init(this.$refs.chartRef);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (!this.chart) {
|
if (!this.chart) {
|
||||||
@ -35,57 +41,62 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init(siteId, timeRange) {
|
init(siteId, timeRange) {
|
||||||
this.chart.showLoading();
|
const [startTime = "", endTime = ""] = timeRange;
|
||||||
const [startTime='', endTime=''] = timeRange;
|
const query = {
|
||||||
storagePower(siteId,startTime,endTime)
|
rangeType: "custom",
|
||||||
.then((response) => {
|
startTime: this.normalizeDateTime(startTime, false),
|
||||||
this.setOption(response?.data?.pcsPowerList || []);
|
endTime: this.normalizeDateTime(endTime, true),
|
||||||
|
siteId
|
||||||
|
};
|
||||||
|
const rows = (this.displayData || []).filter(
|
||||||
|
(item) =>
|
||||||
|
item &&
|
||||||
|
item.useFixedDisplay !== 1 &&
|
||||||
|
[
|
||||||
|
"SBJK_SSYX__curvePcsActivePower",
|
||||||
|
"SBJK_SSYX__curvePcsReactivePower"
|
||||||
|
].includes(item.fieldCode) &&
|
||||||
|
item.dataPoint
|
||||||
|
);
|
||||||
|
const tasks = rows.map((row) => {
|
||||||
|
const pointId = String(row.dataPoint || "").trim();
|
||||||
|
if (!pointId) return Promise.resolve(null);
|
||||||
|
return getPointConfigCurve({
|
||||||
|
...query,
|
||||||
|
pointId
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.then((response) => {
|
||||||
this.chart.hideLoading();
|
const list = response?.data || [];
|
||||||
|
return {
|
||||||
|
name: row.fieldName || row.fieldCode || pointId,
|
||||||
|
data: list
|
||||||
|
.map((item) => [
|
||||||
|
this.parseToTimestamp(item.dataTime),
|
||||||
|
Number(item.pointValue)
|
||||||
|
])
|
||||||
|
.filter((item) => item[0] && !Number.isNaN(item[1]))
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
});
|
||||||
|
Promise.all(tasks)
|
||||||
|
.then((series) => {
|
||||||
|
this.setOption((series || []).filter(Boolean));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setOption(data) {
|
normalizeDateTime(value, endOfDay) {
|
||||||
let xdata = [],
|
const raw = String(value || "").trim();
|
||||||
series = [];
|
if (!raw) return "";
|
||||||
data.forEach((element, index) => {
|
if (raw.includes(" ")) return raw;
|
||||||
if (index === 0) {
|
return `${raw} ${endOfDay ? "23:59:59" : "00:00:00"}`;
|
||||||
xdata = (element.energyStoragePowList || []).map((i) => i.createDate);
|
},
|
||||||
}
|
parseToTimestamp(value) {
|
||||||
series.push(
|
if (!value) return null;
|
||||||
{
|
const t = new Date(value).getTime();
|
||||||
type: "line",
|
return Number.isNaN(t) ? null : t;
|
||||||
name: `${element.deviceId}有功功率`,
|
},
|
||||||
areaStyle: {
|
setOption(seriesData = []) {
|
||||||
// color:'#FFBD00'
|
this.chart && this.chart.setOption({
|
||||||
},
|
|
||||||
data: (element.energyStoragePowList || []).map(
|
|
||||||
(i) => {
|
|
||||||
return {
|
|
||||||
value:i.pcsTotalActPower,
|
|
||||||
year:i.dateDay || ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "line",
|
|
||||||
name: `${element.deviceId}无功功率`,
|
|
||||||
areaStyle: {
|
|
||||||
// color:'#FFBD00'
|
|
||||||
},
|
|
||||||
data: (element.energyStoragePowList || []).map(
|
|
||||||
(i) => {
|
|
||||||
return {
|
|
||||||
value: i.pcsTotalReactivePower,
|
|
||||||
year:i.dateDay || ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
this.chart.setOption({
|
|
||||||
legend: {
|
legend: {
|
||||||
left: "center",
|
left: "center",
|
||||||
top: "5",
|
top: "5",
|
||||||
@ -99,29 +110,16 @@ export default {
|
|||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show:true,
|
show: true,
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
type: "cross",
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
|
||||||
formatter :(params)=>{
|
|
||||||
if(params.length <= 0) return
|
|
||||||
let result = (params[0].data.year || '')+' '+ params[0].name + '<div>'
|
|
||||||
params.forEach(item=>{
|
|
||||||
const {color,seriesName,value} = item
|
|
||||||
result += `<div style="position: relative;padding-left:20px;line-height: 20px;">
|
|
||||||
<div style="position: absolute;top:50%;left:0;width:12px;height:12px;border-radius:100%;background: ${color};transform: translateY(-50%)"></div>
|
|
||||||
<span>${seriesName}</span><span style="margin-left:20px;font-weight: 700">${value}</span></div>`
|
|
||||||
})
|
|
||||||
result+='</div>'
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#333333",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
xAxis: { type: "category", data: xdata },
|
xAxis: { type: "time" },
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
},
|
},
|
||||||
@ -136,8 +134,17 @@ export default {
|
|||||||
end: 100,
|
end: 100,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series,
|
series: seriesData.map((item) => ({
|
||||||
});
|
type: "line",
|
||||||
|
name: item.name,
|
||||||
|
showSymbol: false,
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.35
|
||||||
|
},
|
||||||
|
data: item.data
|
||||||
|
})),
|
||||||
|
}, true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="card-title">平均SOC</span>
|
<span class="card-title">平均SOC</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 360px" id="dcpjsocChart" />
|
<div ref="chartRef" style="height: 360px" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,16 +14,22 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import { batteryAveSoc } from "@/api/ems/dzjk";
|
import { getPointConfigCurve } from "@/api/ems/site";
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize],
|
mixins: [resize],
|
||||||
|
props: {
|
||||||
|
displayData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
chart: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.chart = echarts.init(document.querySelector("#dcpjsocChart"));
|
this.chart = echarts.init(this.$refs.chartRef);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (!this.chart) {
|
if (!this.chart) {
|
||||||
@ -34,27 +40,48 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init(siteId,timeRange) {
|
init(siteId,timeRange) {
|
||||||
this.chart.showLoading();
|
|
||||||
const [startTime='', endTime=''] = timeRange;
|
const [startTime='', endTime=''] = timeRange;
|
||||||
batteryAveSoc(siteId,startTime,endTime)
|
const row = (this.displayData || []).find(
|
||||||
|
(item) =>
|
||||||
|
item &&
|
||||||
|
item.fieldCode === "SBJK_SSYX__curveBatteryAveSoc" &&
|
||||||
|
item.useFixedDisplay !== 1 &&
|
||||||
|
item.dataPoint
|
||||||
|
);
|
||||||
|
const pointId = String(row?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.setOption([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getPointConfigCurve({
|
||||||
|
siteId,
|
||||||
|
pointId,
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.normalizeDateTime(startTime, false),
|
||||||
|
endTime: this.normalizeDateTime(endTime, true)
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data?.batteryAveSOCList || []);
|
const list = response?.data || [];
|
||||||
})
|
this.setOption(
|
||||||
.finally(() => {
|
list
|
||||||
this.chart.hideLoading();
|
.map((item) => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
|
||||||
|
.filter((item) => item[0] && !Number.isNaN(item[1]))
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setOption(data) {
|
normalizeDateTime(value, endOfDay) {
|
||||||
let xdata = [],
|
const raw = String(value || "").trim();
|
||||||
ydata = [];
|
if (!raw) return "";
|
||||||
data.forEach((element) => {
|
if (raw.includes(" ")) return raw;
|
||||||
xdata.push(element.createDate);
|
return `${raw} ${endOfDay ? "23:59:59" : "00:00:00"}`;
|
||||||
ydata.push({
|
},
|
||||||
value:element.batterySOC,
|
parseToTimestamp(value) {
|
||||||
year:element.dateDay,
|
if (!value) return null;
|
||||||
});
|
const t = new Date(value).getTime();
|
||||||
});
|
return Number.isNaN(t) ? null : t;
|
||||||
xdata = this.chart.setOption({
|
},
|
||||||
|
setOption(data = []) {
|
||||||
|
this.chart && this.chart.setOption({
|
||||||
legend: {
|
legend: {
|
||||||
left: "center",
|
left: "center",
|
||||||
top: "5",
|
top: "5",
|
||||||
@ -71,26 +98,13 @@ export default {
|
|||||||
show:true,
|
show:true,
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
type: "cross",
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
|
||||||
formatter :(params)=>{
|
|
||||||
if(params.length <= 0) return
|
|
||||||
let result = (params[0].data.year || '')+' '+params[0].name + '<div>'
|
|
||||||
params.forEach(item=>{
|
|
||||||
const {color,seriesName,value} = item
|
|
||||||
result += `<div style="position: relative;padding-left:20px;line-height: 20px;">
|
|
||||||
<div style="position: absolute;top:50%;left:0;width:12px;height:12px;border-radius:100%;background: ${color};transform: translateY(-50%)"></div>
|
|
||||||
<span>${seriesName}</span><span style="margin-left:20px;font-weight: 700">${value}</span></div>`
|
|
||||||
})
|
|
||||||
result+='</div>'
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#333333",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
xAxis: { type: "category", data: xdata },
|
xAxis: { type: "time" },
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
},
|
},
|
||||||
@ -109,13 +123,15 @@ export default {
|
|||||||
{
|
{
|
||||||
type: "line",
|
type: "line",
|
||||||
name: `平均SOC`,
|
name: `平均SOC`,
|
||||||
|
showSymbol: false,
|
||||||
|
smooth: true,
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
// color:'#FFBD00'
|
opacity: 0.35
|
||||||
},
|
},
|
||||||
data: ydata,
|
data,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
},true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="card-title">电池平均温度</span>
|
<span class="card-title">电池平均温度</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 360px" id="dcpjwdChart" />
|
<div ref="chartRef" style="height: 360px" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,17 +14,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import { batteryAveTemp } from "@/api/ems/dzjk";
|
import { getPointConfigCurve } from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize],
|
mixins: [resize],
|
||||||
|
props: {
|
||||||
|
displayData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
chart: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.chart = echarts.init(document.querySelector("#dcpjwdChart"));
|
this.chart = echarts.init(this.$refs.chartRef);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (!this.chart) {
|
if (!this.chart) {
|
||||||
@ -35,29 +41,48 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init(siteId,timeRange) {
|
init(siteId,timeRange) {
|
||||||
this.chart.showLoading();
|
|
||||||
const [startTime='', endTime=''] = timeRange;
|
const [startTime='', endTime=''] = timeRange;
|
||||||
batteryAveTemp(siteId,startTime,endTime)
|
const row = (this.displayData || []).find(
|
||||||
|
(item) =>
|
||||||
|
item &&
|
||||||
|
item.fieldCode === "SBJK_SSYX__curveBatteryAveTemp" &&
|
||||||
|
item.useFixedDisplay !== 1 &&
|
||||||
|
item.dataPoint
|
||||||
|
);
|
||||||
|
const pointId = String(row?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.setOption([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getPointConfigCurve({
|
||||||
|
siteId,
|
||||||
|
pointId,
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.normalizeDateTime(startTime, false),
|
||||||
|
endTime: this.normalizeDateTime(endTime, true)
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data?.batteryAveTempList || []);
|
const list = response?.data || [];
|
||||||
})
|
this.setOption(
|
||||||
.finally(() => {
|
list
|
||||||
this.chart.hideLoading();
|
.map((item) => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
|
||||||
|
.filter((item) => item[0] && !Number.isNaN(item[1]))
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setOption(data) {
|
normalizeDateTime(value, endOfDay) {
|
||||||
let xdata = [],
|
const raw = String(value || "").trim();
|
||||||
ydata = [];
|
if (!raw) return "";
|
||||||
data.forEach((element) => {
|
if (raw.includes(" ")) return raw;
|
||||||
xdata.push(element.createDate);
|
return `${raw} ${endOfDay ? "23:59:59" : "00:00:00"}`;
|
||||||
ydata.push(
|
},
|
||||||
{
|
parseToTimestamp(value) {
|
||||||
value: element.batteryTemp,
|
if (!value) return null;
|
||||||
year: element.dateDay
|
const t = new Date(value).getTime();
|
||||||
}
|
return Number.isNaN(t) ? null : t;
|
||||||
);
|
},
|
||||||
});
|
setOption(data = []) {
|
||||||
xdata = this.chart.setOption({
|
this.chart && this.chart.setOption({
|
||||||
legend: {
|
legend: {
|
||||||
left: "center",
|
left: "center",
|
||||||
top: "5",
|
top: "5",
|
||||||
@ -74,26 +99,13 @@ export default {
|
|||||||
show:true,
|
show:true,
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
type: "cross",
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
|
||||||
formatter :(params)=>{
|
|
||||||
if(params.length <= 0) return
|
|
||||||
let result = (params[0].data.year || '')+' '+params[0].name + '<div>'
|
|
||||||
params.forEach(item=>{
|
|
||||||
const {color,seriesName,value} = item
|
|
||||||
result += `<div style="position: relative;padding-left:20px;line-height: 20px;">
|
|
||||||
<div style="position: absolute;top:50%;left:0;width:12px;height:12px;border-radius:100%;background: ${color};transform: translateY(-50%)"></div>
|
|
||||||
<span>${seriesName}</span><span style="margin-left:20px;font-weight: 700">${value}</span></div>`
|
|
||||||
})
|
|
||||||
result+='</div>'
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#333333",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
xAxis: { type: "category", data: xdata },
|
xAxis: { type: "time" },
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
},
|
},
|
||||||
@ -112,13 +124,15 @@ export default {
|
|||||||
{
|
{
|
||||||
type: "line",
|
type: "line",
|
||||||
name: `电池平均温度`,
|
name: `电池平均温度`,
|
||||||
|
showSymbol: false,
|
||||||
|
smooth: true,
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
// color:'#FFBD00'
|
opacity: 0.35
|
||||||
},
|
},
|
||||||
data: ydata,
|
data,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
},true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="card-title">PCS最高温度</span>
|
<span class="card-title">PCS最高温度</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 360px" id="pocpjwdChart" />
|
<div ref="chartRef" style="height: 360px" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,17 +14,24 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import { pcsMaxTemp } from "@/api/ems/dzjk";
|
import { getPointConfigCurve } from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize],
|
mixins: [resize],
|
||||||
|
props: {
|
||||||
|
displayData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
chart: null,
|
||||||
|
seriesName: "PCS最高温度"
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.chart = echarts.init(document.querySelector("#pocpjwdChart"));
|
this.chart = echarts.init(this.$refs.chartRef);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (!this.chart) {
|
if (!this.chart) {
|
||||||
@ -35,38 +42,49 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init(siteId,timeRange) {
|
init(siteId,timeRange) {
|
||||||
this.chart.showLoading();
|
|
||||||
const [startTime='', endTime=''] = timeRange;
|
const [startTime='', endTime=''] = timeRange;
|
||||||
pcsMaxTemp(siteId,startTime,endTime)
|
const row = (this.displayData || []).find(
|
||||||
|
(item) =>
|
||||||
|
item &&
|
||||||
|
item.fieldCode === "SBJK_SSYX__curvePcsMaxTemp" &&
|
||||||
|
item.useFixedDisplay !== 1 &&
|
||||||
|
item.dataPoint
|
||||||
|
);
|
||||||
|
const pointId = String(row?.dataPoint || "").trim();
|
||||||
|
this.seriesName = row?.fieldName || "PCS最高温度";
|
||||||
|
if (!pointId) {
|
||||||
|
this.setOption([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getPointConfigCurve({
|
||||||
|
siteId,
|
||||||
|
pointId,
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.normalizeDateTime(startTime, false),
|
||||||
|
endTime: this.normalizeDateTime(endTime, true)
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data?.pcsMaxTempList || []);
|
const list = response?.data || [];
|
||||||
})
|
this.setOption(
|
||||||
.finally(() => {
|
list
|
||||||
this.chart.hideLoading();
|
.map((item) => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
|
||||||
|
.filter((item) => item[0] && !Number.isNaN(item[1]))
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setOption(data) {
|
normalizeDateTime(value, endOfDay) {
|
||||||
let xdata = [],
|
const raw = String(value || "").trim();
|
||||||
series = [];
|
if (!raw) return "";
|
||||||
data.forEach((element, index) => {
|
if (raw.includes(" ")) return raw;
|
||||||
if (index === 0) {
|
return `${raw} ${endOfDay ? "23:59:59" : "00:00:00"}`;
|
||||||
xdata = (element.maxTempVoList || []).map((i) => i.createDate);
|
},
|
||||||
}
|
parseToTimestamp(value) {
|
||||||
series.push({
|
if (!value) return null;
|
||||||
type: "line",
|
const t = new Date(value).getTime();
|
||||||
name: `${element.deviceId}最高温度`,
|
return Number.isNaN(t) ? null : t;
|
||||||
areaStyle: {
|
},
|
||||||
// color:'#FFBD00'
|
setOption(data = []) {
|
||||||
},
|
this.chart && this.chart.setOption({
|
||||||
data: (element.maxTempVoList || []).map((i) => {
|
|
||||||
return {
|
|
||||||
value: i.temp,
|
|
||||||
year: i.dateDay
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.chart.setOption({
|
|
||||||
legend: {
|
legend: {
|
||||||
left: "center",
|
left: "center",
|
||||||
top: "5",
|
top: "5",
|
||||||
@ -83,26 +101,13 @@ export default {
|
|||||||
show:true,
|
show:true,
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
type: "cross",
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
|
||||||
formatter :(params)=>{
|
|
||||||
if(params.length <= 0) return
|
|
||||||
let result = (params[0].data.year || '')+' '+params[0].name + '<div>'
|
|
||||||
params.forEach(item=>{
|
|
||||||
const {color,seriesName,value} = item
|
|
||||||
result += `<div style="position: relative;padding-left:20px;line-height: 20px;">
|
|
||||||
<div style="position: absolute;top:50%;left:0;width:12px;height:12px;border-radius:100%;background: ${color};transform: translateY(-50%)"></div>
|
|
||||||
<span>${seriesName}</span><span style="margin-left:20px;font-weight: 700">${value}</span></div>`
|
|
||||||
})
|
|
||||||
result+='</div>'
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#333333",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
xAxis: { type: "category", data: xdata },
|
xAxis: { type: "time" },
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
},
|
},
|
||||||
@ -117,8 +122,19 @@ export default {
|
|||||||
end: 100,
|
end: 100,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series,
|
series: [
|
||||||
});
|
{
|
||||||
|
type: "line",
|
||||||
|
name: this.seriesName,
|
||||||
|
showSymbol: false,
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.35
|
||||||
|
},
|
||||||
|
data
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,33 +2,65 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ssyx-ems-dashboard-editor-container">
|
<div class="ssyx-ems-dashboard-editor-container">
|
||||||
<!-- 6个方块-->
|
<!-- 6个方块-->
|
||||||
<real-time-base-info :data="runningHeadData"/>
|
<real-time-base-info :display-data="runningDisplayData" :loading="runningHeadLoading" @field-click="handleHeadFieldClick"/>
|
||||||
<!-- 时间选择 -->
|
<!-- 时间选择 -->
|
||||||
<date-range-select ref="dateRangeSelect" @updateDate="updateDate" style="margin-top:20px;"/>
|
<date-range-select ref="dateRangeSelect" @updateDate="updateDate" style="margin-top:20px;"/>
|
||||||
<!-- echart图表-->
|
<!-- echart图表-->
|
||||||
<el-row :gutter="32" style="background:#fff;margin:30px 0;">
|
<el-row :gutter="32" style="background:#fff;margin:30px 0;">
|
||||||
<el-col :xs="24" :sm="12" :lg="12">
|
<el-col :xs="24" :sm="12" :lg="12">
|
||||||
<cnglqx-chart ref='cnglqx'/>
|
<cnglqx-chart ref='cnglqx' :display-data="runningDisplayData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :lg="12">
|
<el-col :xs="24" :sm="12" :lg="12">
|
||||||
<pocpjwd-chart ref='pocpjwd'/>
|
<pocpjwd-chart ref='pocpjwd' :display-data="runningDisplayData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="32" style="margin:30px 0;">
|
<el-row :gutter="32" style="margin:30px 0;">
|
||||||
<el-col :xs="24" :sm="12" :lg="12">
|
<el-col :xs="24" :sm="12" :lg="12">
|
||||||
<dcpjsoc-chart ref="dcpjsoc"/>
|
<dcpjsoc-chart ref="dcpjsoc" :display-data="runningDisplayData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :lg="12">
|
<el-col :xs="24" :sm="12" :lg="12">
|
||||||
<dcpjwd-chart ref="dcpjwd"/>
|
<dcpjwd-chart ref="dcpjwd" :display-data="runningDisplayData"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
import * as echarts from "echarts";
|
||||||
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
|
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
|
||||||
import RealTimeBaseInfo from "./../RealTimeBaseInfo.vue";
|
import RealTimeBaseInfo from "./../RealTimeBaseInfo.vue";
|
||||||
import CnglqxChart from './CnglqxChart.vue'
|
import CnglqxChart from './CnglqxChart.vue'
|
||||||
@ -36,7 +68,8 @@ import PocpjwdChart from './PocpjwdChart.vue'
|
|||||||
import DcpjwdChart from './DcpjwdChart.vue'
|
import DcpjwdChart from './DcpjwdChart.vue'
|
||||||
import DcpjsocChart from './DcpjsocChart.vue'
|
import DcpjsocChart from './DcpjsocChart.vue'
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import {getRunningHeadInfo} from '@/api/ems/dzjk'
|
import {getProjectDisplayData} from '@/api/ems/dzjk'
|
||||||
|
import {getPointConfigCurve} from "@/api/ems/site";
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -45,16 +78,183 @@ export default {
|
|||||||
mixins:[getQuerySiteId,intervalUpdate],
|
mixins:[getQuerySiteId,intervalUpdate],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
runningHeadData:{},//运行信息
|
runningDisplayData: [], //单站监控项目配置展示数据
|
||||||
timeRange:[],
|
timeRange:[],
|
||||||
isInit:true
|
isInit:true,
|
||||||
|
runningHeadLoading: false,
|
||||||
|
curveDialogVisible: false,
|
||||||
|
curveDialogTitle: "点位曲线",
|
||||||
|
curveChart: null,
|
||||||
|
curveLoading: false,
|
||||||
|
curveCustomRange: [],
|
||||||
|
curveQuery: {
|
||||||
|
siteId: "",
|
||||||
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
handleHeadFieldClick(item) {
|
||||||
|
const pointId = String(item?.pointId || item?.raw?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openCurveDialog({
|
||||||
|
pointId,
|
||||||
|
title: item?.title || item?.raw?.fieldName || pointId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openCurveDialog({pointId, title}) {
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || pointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const pad = (num) => String(num).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query).then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
}).catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
}).finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map(item => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map(item => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
//6个方块数据
|
//6个方块数据
|
||||||
getRunningHeadData(){
|
getRunningHeadData(){
|
||||||
getRunningHeadInfo(this.siteId).then(response => {
|
this.runningHeadLoading = true
|
||||||
this.runningHeadData = response?.data || {}
|
return getProjectDisplayData(this.siteId).then((displayResponse) => {
|
||||||
|
this.runningDisplayData = displayResponse?.data || []
|
||||||
|
}).finally(() => {
|
||||||
|
this.runningHeadLoading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 更新时间范围 重置图表
|
// 更新时间范围 重置图表
|
||||||
@ -71,8 +271,9 @@ export default {
|
|||||||
this.updateInterval(this.updateData)
|
this.updateInterval(this.updateData)
|
||||||
},
|
},
|
||||||
updateData(){
|
updateData(){
|
||||||
this.getRunningHeadData()
|
this.getRunningHeadData().finally(() => {
|
||||||
this.updateChart()
|
this.updateChart()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
init(){
|
init(){
|
||||||
this.$refs.dateRangeSelect.init(true)
|
this.$refs.dateRangeSelect.init(true)
|
||||||
|
|||||||
@ -1,49 +1,55 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<el-card
|
<el-card
|
||||||
v-for="(item,index) in list"
|
v-for="(item,index) in list"
|
||||||
:key="index+'ylLise'"
|
:key="index+'ylLise'"
|
||||||
class="sbjk-card-container running-card-container"
|
class="sbjk-card-container running-card-container"
|
||||||
:class="{
|
:class="{
|
||||||
'warning-card-container':item.emsCommunicationStatus && item.emsCommunicationStatus !== '0',
|
'warning-card-container':item.emsCommunicationStatus && item.emsCommunicationStatus !== '0',
|
||||||
'running-card-container':item.emsCommunicationStatus === '0'
|
'running-card-container':item.emsCommunicationStatus === '0'
|
||||||
}"
|
}"
|
||||||
shadow="always">
|
shadow="always">
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span class="large-title">{{item.deviceName}}</span>
|
<span class="large-title">{{ item.deviceName }}</span>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div>
|
<div>
|
||||||
{{
|
{{
|
||||||
$store.state.ems.communicationStatusOptions[
|
$store.state.ems.communicationStatusOptions[
|
||||||
item.emsCommunicationStatus
|
item.emsCommunicationStatus
|
||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
</div>
|
|
||||||
<div>数据更新时间:{{ item.dataUpdateTime || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="alarm">
|
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">详细</el-button>
|
|
||||||
<el-badge :value="item.alarmNum || 0" class="item">
|
|
||||||
<i
|
|
||||||
class="el-icon-message-solid alarm-icon"
|
|
||||||
@click="pointDetail(item,'alarmPoint')"
|
|
||||||
></i>
|
|
||||||
</el-badge>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>数据更新时间:{{ item.dataUpdateTime || '-' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<el-row class="device-info-row">
|
<div class="alarm">
|
||||||
<el-col v-for="(tempDataItem,tempDataIndex) in tempData" :key="tempDataIndex+'hdTempData'" :span="12" class="device-info-col">
|
<el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
|
||||||
|
详细
|
||||||
|
</el-button>
|
||||||
|
<el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
|
||||||
|
<i
|
||||||
|
class="el-icon-message-solid alarm-icon"
|
||||||
|
@click="pointDetail(item,'alarmPoint')"
|
||||||
|
></i>
|
||||||
|
</el-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row class="device-info-row">
|
||||||
|
<el-col v-for="(tempDataItem,tempDataIndex) in tempData" :key="tempDataIndex+'hdTempData'" :span="12"
|
||||||
|
class="device-info-col">
|
||||||
<span class="pointer" @click="showChart(tempDataItem.title,item.deviceId)">
|
<span class="pointer" @click="showChart(tempDataItem.title,item.deviceId)">
|
||||||
<span class="left">{{tempDataItem.title}}</span> <span class="right">{{item[tempDataItem.attr] || ''}}<span v-html="tempDataItem.unit"></span></span>
|
<span class="left">{{ tempDataItem.title }}</span>
|
||||||
|
<span class="right">
|
||||||
|
<i v-if="isPointLoading(item[tempDataItem.attr])" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(item[tempDataItem.attr]) }}<span v-html="tempDataItem.unit"></span></span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-empty v-show="list.length<=0" :image-size="200"></el-empty>
|
<el-empty v-show="list.length<=0" :image-size="200"></el-empty>
|
||||||
<point-chart ref="pointChart" :site-id="siteId"/>
|
<point-chart ref="pointChart" :site-id="siteId"/>
|
||||||
<point-table ref="pointTable"/>
|
<point-table ref="pointTable"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@ -53,70 +59,95 @@ import {getXfDataList} from '@/api/ems/dzjk'
|
|||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
import pointChart from "./../PointChart.vue";
|
import pointChart from "./../PointChart.vue";
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkSbjkXf',
|
name: 'DzjkSbjkXf',
|
||||||
mixins:[getQuerySiteId,intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
components:{pointChart,PointTable},
|
components: {pointChart, PointTable},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
list:[],
|
list: [],
|
||||||
tempData:[
|
tempData: [
|
||||||
{title:'主电源备用电池状态',attr:'dczt',unit:''},
|
{title: '主电源备用电池状态', attr: 'dczt', unit: ''},
|
||||||
{title:'手自动状态延时状态',attr:'yszt',unit:''},
|
{title: '手自动状态延时状态', attr: 'yszt', unit: ''},
|
||||||
{title:'启动喷洒气体喷洒状态',attr:'pszt',unit:''},
|
{title: '启动喷洒气体喷洒状态', attr: 'pszt', unit: ''},
|
||||||
{title:'压力开关状态电磁阀状态',attr:'dcfzt',unit:''},
|
{title: '压力开关状态电磁阀状态', attr: 'dcfzt', unit: ''},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
// 查看设备电位表格
|
// 查看设备电位表格
|
||||||
pointDetail(row,dataType){
|
pointDetail(row, dataType) {
|
||||||
const {deviceId} = row
|
const {deviceId} = row
|
||||||
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'XF'},dataType)
|
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'XF'}, dataType)
|
||||||
},
|
},
|
||||||
showChart(pointName,deviceId){
|
showChart(pointName, deviceId) {
|
||||||
pointName && this.$refs.pointChart.showChart({pointName,deviceCategory:'XF',deviceId})
|
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'XF', deviceId})
|
||||||
},
|
},
|
||||||
updateData(){
|
updateData() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
getXfDataList(this.siteId).then(response => {
|
getXfDataList(this.siteId).then(response => {
|
||||||
this.list = JSON.parse(JSON.stringify(response?.data || []));
|
this.list = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
}).finally(() => {this.loading = false})
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
init(){
|
init() {
|
||||||
this.updateData()
|
this.updateData()
|
||||||
this.updateInterval(this.updateData)
|
this.updateInterval(this.updateData)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.sbjk-card-container{
|
.sbjk-card-container {
|
||||||
&:not(:last-child){
|
&:not(:last-child) {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
.el-row{
|
|
||||||
|
.el-row {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border:1px solid #eeeeee;
|
border: 1px solid #eeeeee;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
.el-col{
|
|
||||||
padding:12px 0;
|
.el-col {
|
||||||
|
padding: 12px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.el-col{
|
|
||||||
|
.el-col {
|
||||||
border-bottom: 1px solid #eeeeee;
|
border-bottom: 1px solid #eeeeee;
|
||||||
}
|
}
|
||||||
.el-col:not(:nth-child(3n)){
|
|
||||||
|
.el-col:not(:nth-child(3n)) {
|
||||||
border-right: 1px solid #eeeeee;
|
border-right: 1px solid #eeeeee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,96 +1,471 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div>
|
||||||
<el-card
|
<div class="pcs-tags">
|
||||||
v-for="(item,index) in list"
|
<el-tag
|
||||||
:key="index+'ylLise'"
|
size="small"
|
||||||
class="sbjk-card-container running-card-container"
|
:type="selectedSectionKey ? 'info' : 'primary'"
|
||||||
shadow="always">
|
:effect="selectedSectionKey ? 'plain' : 'dark'"
|
||||||
<div slot="header">
|
class="pcs-tag-item"
|
||||||
<span class="large-title">{{item.deviceName}}</span>
|
@click="handleTagClick('')"
|
||||||
<div class="info">
|
>
|
||||||
<div>数据更新时间:{{ item.dataUpdateTime || '-'}}</div>
|
全部
|
||||||
</div>
|
</el-tag>
|
||||||
<div class="alarm">
|
<el-tag
|
||||||
<el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">详细</el-button>
|
v-for="(group, index) in sectionGroups"
|
||||||
<el-badge :value="item.alarmNum || 0" class="item">
|
:key="index + 'ylTag'"
|
||||||
<i
|
size="small"
|
||||||
class="el-icon-message-solid alarm-icon"
|
:type="selectedSectionKey === group.sectionKey ? 'primary' : 'info'"
|
||||||
@click="pointDetail(item,'alarmPoint')"
|
:effect="selectedSectionKey === group.sectionKey ? 'dark' : 'plain'"
|
||||||
></i>
|
class="pcs-tag-item"
|
||||||
</el-badge>
|
@click="handleTagClick(group.sectionKey)"
|
||||||
</div>
|
>
|
||||||
</div>
|
{{ group.displayName || group.sectionName || "冷却" }}
|
||||||
<el-row class="device-info-row">
|
</el-tag>
|
||||||
<el-col v-for="(tempDataItem,tempDataIndex) in tempData" :key="tempDataIndex+'ylTempData'" :span="8" class="device-info-col">
|
|
||||||
<span class="pointer" @click="showChart(tempDataItem.title,item.deviceId)">
|
|
||||||
<span class="left">{{tempDataItem.title}}</span> <span class="right">{{item[tempDataItem.attr]}}<span v-html="tempDataItem.unit"></span></span>
|
|
||||||
</span>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
<el-empty v-show="list.length<=0" :image-size="200"></el-empty>
|
|
||||||
<point-chart ref="pointChart" :site-id="siteId"/>
|
|
||||||
<point-table ref="pointTable"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<el-card
|
||||||
|
v-for="(group, index) in filteredSectionGroups"
|
||||||
|
:key="index + 'ylSection'"
|
||||||
|
class="sbjk-card-container list running-card-container"
|
||||||
|
shadow="always"
|
||||||
|
>
|
||||||
|
<div slot="header">
|
||||||
|
<span class="large-title">{{ group.displayName || group.sectionName || "冷却" }}</span>
|
||||||
|
<div class="info">
|
||||||
|
<div>状态:{{ group.statusText }}</div>
|
||||||
|
<div>数据更新时间:{{ group.updateTimeText }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row class="device-info-row">
|
||||||
|
<el-col
|
||||||
|
v-for="(item, dataIndex) in group.items"
|
||||||
|
:key="dataIndex + 'ylField'"
|
||||||
|
:span="8"
|
||||||
|
class="device-info-col"
|
||||||
|
:class="{ 'field-disabled': !item.pointId }"
|
||||||
|
>
|
||||||
|
<div class="field-click-wrapper" @click="handleFieldClick(item)">
|
||||||
|
<span class="left">{{ item.fieldName }}</span>
|
||||||
|
<span class="right">
|
||||||
|
<i v-if="isPointLoading(item.fieldValue)" class="el-icon-loading point-loading-icon"></i>
|
||||||
|
<span v-else>{{ displayValue(item.fieldValue) | formatNumber }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="curveDialogVisible"
|
||||||
|
:title="curveDialogTitle"
|
||||||
|
width="1000px"
|
||||||
|
append-to-body
|
||||||
|
class="ems-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
@opened="handleCurveDialogOpened"
|
||||||
|
@closed="handleCurveDialogClosed"
|
||||||
|
>
|
||||||
|
<div class="curve-tools">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="curveCustomRange"
|
||||||
|
type="datetimerange"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
style="width: 440px"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="mini" :loading="curveLoading" @click="loadCurveData">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-loading="curveLoading" ref="curveChartRef" style="height: 380px;"></div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import * as echarts from "echarts";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import {getCoolingDataList} from '@/api/ems/dzjk'
|
|
||||||
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
import intervalUpdate from "@/mixins/ems/intervalUpdate";
|
||||||
import pointChart from "./../PointChart.vue";
|
import { getProjectDisplayData } from "@/api/ems/dzjk";
|
||||||
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
|
import { getDeviceList, getPointConfigCurve } from "@/api/ems/site";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkSbjkYl',
|
name: "DzjkSbjkYl",
|
||||||
mixins:[getQuerySiteId,intervalUpdate],
|
mixins: [getQuerySiteId, intervalUpdate],
|
||||||
components:{pointChart,PointTable},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
list:[],
|
displayData: [],
|
||||||
tempData:[
|
selectedSectionKey: "",
|
||||||
{title:'供水温度',attr:'gsTemp',unit:'℃'},
|
coolingDeviceList: [],
|
||||||
{title:'回水温度',attr:'hsTemp',unit:'℃'},
|
curveDialogVisible: false,
|
||||||
{title:'供水压力',attr:'gsPressure',unit:'bar'},
|
curveDialogTitle: "点位曲线",
|
||||||
{title:'回水压力',attr:'hsPressure',unit:'bar'},
|
curveChart: null,
|
||||||
{title:'冷源水温度',attr:'lysTemp',unit:'℃'},
|
curveLoading: false,
|
||||||
{title:'VB01开度',attr:'vb01Kd',unit:'%'},
|
curveCustomRange: [],
|
||||||
{title:'VB02开度',attr:'vb02Kd',unit:'%'},
|
curveQuery: {
|
||||||
]
|
siteId: "",
|
||||||
}
|
pointId: "",
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
methods:{
|
computed: {
|
||||||
// 查看设备电位表格
|
moduleDisplayData() {
|
||||||
pointDetail(row,dataType){
|
return (this.displayData || []).filter((item) => item.menuCode === "SBJK_YL");
|
||||||
const {deviceId} = row
|
|
||||||
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'COOLING'},dataType)
|
|
||||||
},
|
},
|
||||||
showChart(pointName,deviceId){
|
ylTemplateFields() {
|
||||||
pointName && this.$refs.pointChart.showChart({pointName,deviceCategory:'COOLING',deviceId})
|
const source = this.moduleDisplayData || [];
|
||||||
|
const result = [];
|
||||||
|
const seen = new Set();
|
||||||
|
source.forEach((item) => {
|
||||||
|
const fieldName = String(item?.fieldName || "").trim();
|
||||||
|
if (!fieldName || seen.has(fieldName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(fieldName);
|
||||||
|
result.push(fieldName);
|
||||||
|
});
|
||||||
|
return result.length > 0 ? result : this.fallbackFields;
|
||||||
},
|
},
|
||||||
updateData(){
|
sectionGroups() {
|
||||||
this.loading = true
|
const source = this.moduleDisplayData || [];
|
||||||
getCoolingDataList(this.siteId).then(response => {
|
const devices = (this.coolingDeviceList || []).length > 0
|
||||||
this.list = JSON.parse(JSON.stringify(response?.data || []));
|
? this.coolingDeviceList
|
||||||
}).finally(() => {this.loading = false})
|
: [{ deviceId: "", deviceName: "冷却" }];
|
||||||
},
|
return devices.map((device, index) => {
|
||||||
init(){
|
const deviceId = String(device?.deviceId || device?.id || "").trim();
|
||||||
this.updateData()
|
const sectionKey = deviceId || `COOLING_${index}`;
|
||||||
this.updateInterval(this.updateData)
|
const displayName = String(device?.deviceName || device?.name || deviceId || `冷却${index + 1}`).trim();
|
||||||
}
|
const exactRows = source.filter((item) => String(item?.deviceId || "").trim() === deviceId);
|
||||||
},
|
const fallbackRows = source.filter((item) => !String(item?.deviceId || "").trim());
|
||||||
mounted(){
|
|
||||||
|
|
||||||
}
|
const exactValueMap = {};
|
||||||
}
|
exactRows.forEach((item) => {
|
||||||
|
const key = String(item?.fieldName || "").trim();
|
||||||
|
if (key) {
|
||||||
|
exactValueMap[key] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const fallbackValueMap = {};
|
||||||
|
fallbackRows.forEach((item) => {
|
||||||
|
const key = String(item?.fieldName || "").trim();
|
||||||
|
if (key && fallbackValueMap[key] === undefined) {
|
||||||
|
fallbackValueMap[key] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = (this.ylTemplateFields || []).map((fieldName) => {
|
||||||
|
const row = exactValueMap[fieldName] || fallbackValueMap[fieldName] || {};
|
||||||
|
return {
|
||||||
|
fieldName,
|
||||||
|
fieldValue: row.fieldValue,
|
||||||
|
valueTime: row.valueTime,
|
||||||
|
pointId: String(row?.dataPoint || "").trim(),
|
||||||
|
raw: row,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusItem = (items || []).find((it) => String(it.fieldName || "").includes("状态"));
|
||||||
|
const timestamps = [...exactRows, ...fallbackRows]
|
||||||
|
.map((it) => new Date(it?.valueTime).getTime())
|
||||||
|
.filter((ts) => !isNaN(ts));
|
||||||
|
|
||||||
|
return {
|
||||||
|
sectionName: displayName,
|
||||||
|
sectionKey,
|
||||||
|
displayName,
|
||||||
|
deviceId,
|
||||||
|
items,
|
||||||
|
statusText: this.displayValue(statusItem ? statusItem.fieldValue : "-"),
|
||||||
|
updateTimeText: timestamps.length > 0 ? this.formatDate(new Date(Math.max(...timestamps))) : "-",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
displaySectionGroups() {
|
||||||
|
if (this.sectionGroups.length > 0) {
|
||||||
|
return this.sectionGroups;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sectionName: "冷却参数",
|
||||||
|
items: this.fallbackFields.map((fieldName) => ({ fieldName, fieldValue: "-" })),
|
||||||
|
statusText: "-",
|
||||||
|
updateTimeText: "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
filteredSectionGroups() {
|
||||||
|
const groups = this.displaySectionGroups || [];
|
||||||
|
if (!this.selectedSectionKey) {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
return groups.filter((group) => group.sectionKey === this.selectedSectionKey);
|
||||||
|
},
|
||||||
|
fallbackFields() {
|
||||||
|
return ["供水温度", "回水温度", "供水压力", "回水压力", "冷源水温度", "VB01开度", "VB02开度"];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleFieldClick(item) {
|
||||||
|
const pointId = String(item?.pointId || item?.raw?.dataPoint || "").trim();
|
||||||
|
if (!pointId) {
|
||||||
|
this.$message.warning("该字段未配置点位,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openCurveDialog({
|
||||||
|
pointId,
|
||||||
|
title: item?.fieldName || pointId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openCurveDialog({ pointId, title }) {
|
||||||
|
const range = this.getDefaultCurveRange();
|
||||||
|
this.curveCustomRange = range;
|
||||||
|
this.curveDialogTitle = `点位曲线 - ${title || pointId}`;
|
||||||
|
this.curveQuery = {
|
||||||
|
siteId: this.siteId,
|
||||||
|
pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: range[0],
|
||||||
|
endTime: range[1],
|
||||||
|
};
|
||||||
|
this.curveDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleCurveDialogOpened() {
|
||||||
|
if (!this.curveChart && this.$refs.curveChartRef) {
|
||||||
|
this.curveChart = echarts.init(this.$refs.curveChartRef);
|
||||||
|
}
|
||||||
|
this.loadCurveData();
|
||||||
|
},
|
||||||
|
handleCurveDialogClosed() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
this.curveLoading = false;
|
||||||
|
},
|
||||||
|
getDefaultCurveRange() {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
return [this.formatDateTime(start), this.formatDateTime(end)];
|
||||||
|
},
|
||||||
|
formatDateTime(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
||||||
|
},
|
||||||
|
formatCurveTime(value) {
|
||||||
|
if (value === undefined || value === null || value === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const raw = String(value).trim();
|
||||||
|
const normalized = raw
|
||||||
|
.replace("T", " ")
|
||||||
|
.replace(/\.\d+/, "")
|
||||||
|
.replace(/Z$/, "")
|
||||||
|
.replace(/([+-]\d{2}:?\d{2})$/, "")
|
||||||
|
.trim();
|
||||||
|
const matched = normalized.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/);
|
||||||
|
if (matched) {
|
||||||
|
return `${matched[1]} ${matched[2]}`;
|
||||||
|
}
|
||||||
|
return normalized.slice(0, 16);
|
||||||
|
},
|
||||||
|
loadCurveData() {
|
||||||
|
if (!this.curveQuery.siteId || !this.curveQuery.pointId) {
|
||||||
|
this.$message.warning("点位信息不完整,无法查询曲线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.curveCustomRange || this.curveCustomRange.length !== 2) {
|
||||||
|
this.$message.warning("请选择查询时间范围");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.curveQuery.startTime = this.curveCustomRange[0];
|
||||||
|
this.curveQuery.endTime = this.curveCustomRange[1];
|
||||||
|
const query = {
|
||||||
|
siteId: this.curveQuery.siteId,
|
||||||
|
pointId: this.curveQuery.pointId,
|
||||||
|
pointType: "data",
|
||||||
|
rangeType: "custom",
|
||||||
|
startTime: this.curveQuery.startTime,
|
||||||
|
endTime: this.curveQuery.endTime,
|
||||||
|
};
|
||||||
|
this.curveLoading = true;
|
||||||
|
getPointConfigCurve(query)
|
||||||
|
.then((response) => {
|
||||||
|
const rows = response?.data || [];
|
||||||
|
this.renderCurveChart(rows);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.renderCurveChart([]);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.curveLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderCurveChart(rows = []) {
|
||||||
|
if (!this.curveChart) return;
|
||||||
|
const xData = rows.map((item) => this.formatCurveTime(item.dataTime));
|
||||||
|
const yData = rows.map((item) => item.pointValue);
|
||||||
|
this.curveChart.clear();
|
||||||
|
this.curveChart.setOption({
|
||||||
|
legend: {},
|
||||||
|
grid: {
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: this.curveDialogTitle,
|
||||||
|
type: "line",
|
||||||
|
data: yData,
|
||||||
|
connectNulls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
this.$message.warning("当前时间范围暂无曲线数据");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTagClick(sectionKey) {
|
||||||
|
this.selectedSectionKey = sectionKey || "";
|
||||||
|
},
|
||||||
|
displayValue(value) {
|
||||||
|
return value === undefined || value === null || value === "" ? "-" : value;
|
||||||
|
},
|
||||||
|
isPointLoading(value) {
|
||||||
|
return this.loading && (value === undefined || value === null || value === "" || value === "-");
|
||||||
|
},
|
||||||
|
formatDate(date) {
|
||||||
|
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
const p = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(
|
||||||
|
date.getMinutes()
|
||||||
|
)}:${p(date.getSeconds())}`;
|
||||||
|
},
|
||||||
|
getCoolingDeviceList() {
|
||||||
|
return getDeviceList(this.siteId)
|
||||||
|
.then((response) => {
|
||||||
|
const list = response?.data || [];
|
||||||
|
this.coolingDeviceList = list.filter((item) => item.deviceCategory === "COOLING");
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.coolingDeviceList = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateData() {
|
||||||
|
this.loading = true;
|
||||||
|
Promise.all([getProjectDisplayData(this.siteId), this.getCoolingDeviceList()])
|
||||||
|
.then(([response]) => {
|
||||||
|
this.displayData = response?.data || [];
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.updateData();
|
||||||
|
this.updateInterval(this.updateData);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.curveChart) {
|
||||||
|
this.curveChart.dispose();
|
||||||
|
this.curveChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.sbjk-card-container{
|
.sbjk-card-container {
|
||||||
&:not(:last-child){
|
&.list:not(:last-child) {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tags {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-tag-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info-col {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-click-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info-col.field-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.curve-tools {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-loading-icon {
|
||||||
|
color: #409eff;
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pointLoadingSpinPulse 1.1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes pointLoadingSpinPulse {
|
||||||
|
0% { opacity: 0.45; transform: rotate(0deg) scale(0.9); }
|
||||||
|
50% { opacity: 1; transform: rotate(180deg) scale(1.08); }
|
||||||
|
100% { opacity: 0.45; transform: rotate(360deg) scale(0.9); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="width:100%" v-loading="loading">
|
<div style="width:100%" v-loading="loading">
|
||||||
<!-- 搜索栏-->
|
<!-- 搜索栏-->
|
||||||
@ -22,172 +21,185 @@
|
|||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="onReset" native-type="button">重置</el-button>
|
<el-button @click="onReset" native-type="button">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="exportTable" native-type="button">导出</el-button>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<!--表格-->
|
<!--表格-->
|
||||||
<el-table
|
<el-table
|
||||||
class="common-table"
|
class="common-table"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
stripe
|
stripe
|
||||||
style="width: 100%;margin-top:25px;">
|
style="width: 100%;margin-top:25px;">
|
||||||
<!-- 汇总列-->
|
<!-- 汇总列-->
|
||||||
<el-table-column label="汇总">
|
<el-table-column label="汇总">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="dataTime"
|
prop="dataTime"
|
||||||
label="日期"
|
label="日期"
|
||||||
width="120">
|
width="120">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!--充电量列-->
|
<!--充电量列-->
|
||||||
<el-table-column label="充电量" align="center">
|
<el-table-column label="充电量" align="center">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="activePeakKwh"
|
prop="activePeakKwh"
|
||||||
label="尖">
|
label="尖">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="activeHighKwh"
|
prop="activeHighKwh"
|
||||||
label="峰">
|
label="峰">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="activeFlatKwh"
|
prop="activeFlatKwh"
|
||||||
label="平">
|
label="平">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="activeValleyKwh"
|
prop="activeValleyKwh"
|
||||||
label="谷">
|
label="谷">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="activeTotalKwh"
|
prop="activeTotalKwh"
|
||||||
label="总">
|
label="总">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!--充电量列-->
|
<!--充电量列-->
|
||||||
<el-table-column label="放电量" align="center">
|
<el-table-column label="放电量" align="center">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="reActivePeakKwh"
|
prop="reActivePeakKwh"
|
||||||
label="尖">
|
label="尖">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="reActiveHighKwh"
|
prop="reActiveHighKwh"
|
||||||
label="峰">
|
label="峰">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="reActiveFlatKwh"
|
prop="reActiveFlatKwh"
|
||||||
label="平">
|
label="平">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="reActiveValleyKwh"
|
prop="reActiveValleyKwh"
|
||||||
label="谷">
|
label="谷">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="reActiveTotalKwh"
|
prop="reActiveTotalKwh"
|
||||||
label="总">
|
label="总">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- 效率-->
|
<!-- 效率-->
|
||||||
<el-table-column label="效率(%)" align="center">
|
<el-table-column label="效率(%)" align="center">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
align="center"
|
align="center"
|
||||||
prop="effect">
|
prop="effect">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-show="tableData.length>0"
|
v-show="tableData.length>0"
|
||||||
background
|
background
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
:current-page="pageNum"
|
:current-page="pageNum"
|
||||||
:page-size="pageSize"
|
:page-size="pageSize"
|
||||||
:page-sizes="[10, 20, 30, 40]"
|
:page-sizes="[10, 20, 30, 40]"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
:total="totalSize"
|
:total="totalSize"
|
||||||
style="margin-top:15px;text-align: center"
|
style="margin-top:15px;text-align: center"
|
||||||
>
|
>
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getAmmeterData} from '@/api/ems/dzjk'
|
import {getAmmeterData} from '@/api/ems/dzjk'
|
||||||
import {formatDate} from "@/filters/ems";
|
import {formatDate} from "@/filters/ems";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkTjbbDbbb',
|
name: 'DzjkTjbbDbbb',
|
||||||
mixins: [getQuerySiteId],
|
mixins: [getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
pickerOptions:{
|
pickerOptions: {
|
||||||
disabledDate(time) {
|
disabledDate(time) {
|
||||||
return time.getTime() > Date.now();
|
return time.getTime() > Date.now();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultDateRange:[],//默认展示的时间
|
defaultDateRange: [],//默认展示的时间
|
||||||
dateRange:[],
|
dateRange: [],
|
||||||
tableData:[],
|
tableData: [],
|
||||||
pageSize:10,//分页栏当前每个数据总数
|
pageSize: 10,//分页栏当前每个数据总数
|
||||||
pageNum:1,//分页栏当前页数
|
pageNum: 1,//分页栏当前页数
|
||||||
totalSize:0,//table表格数据总数
|
totalSize: 0,//table表格数据总数
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
|
// 导出表格
|
||||||
|
exportTable() {
|
||||||
|
if (!this.dateRange?.length) return
|
||||||
|
const [startTime, endTime] = this.dateRange
|
||||||
|
this.download('ems/statsReport/exportAmmeterDataFromDaily', {
|
||||||
|
siteId: this.siteId,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
}, `电表报表_${startTime}-${endTime}.xlsx`)
|
||||||
|
},
|
||||||
// 搜索
|
// 搜索
|
||||||
onSearch(){
|
onSearch() {
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 重置
|
// 重置
|
||||||
onReset(){
|
onReset() {
|
||||||
this.dateRange=this.defaultDateRange
|
this.dateRange = this.defaultDateRange
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 分页
|
// 分页
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCurrentChange(val) {
|
handleCurrentChange(val) {
|
||||||
this.pageNum = val
|
this.pageNum = val
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 获取数据
|
// 获取数据
|
||||||
getData(){
|
getData() {
|
||||||
this.loading=true
|
this.loading = true
|
||||||
const {siteId,pageNum,pageSize} =this
|
const {siteId, pageNum, pageSize} = this
|
||||||
const [startTime='',endTime='']=(this.dateRange || [])
|
const [startTime = '', endTime = ''] = (this.dateRange || [])
|
||||||
getAmmeterData({siteId:siteId,startTime,endTime,pageSize,pageNum}).then(response=>{
|
getAmmeterData({siteId: siteId, startTime, endTime, pageSize, pageNum}).then(response => {
|
||||||
this.tableData=response?.rows || [];
|
this.tableData = response?.rows || [];
|
||||||
this.totalSize = response?.total || 0
|
this.totalSize = response?.total || 0
|
||||||
}).finally(()=> {
|
}).finally(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
init(){
|
init() {
|
||||||
this.dateRange=[]
|
this.dateRange = []
|
||||||
this.tableData=[]
|
this.tableData = []
|
||||||
this.totalSize=0
|
this.totalSize = 0
|
||||||
this.pageSize=10
|
this.pageSize = 10
|
||||||
this.pageNum = 1
|
this.pageNum = 1
|
||||||
const now = new Date().getTime();
|
let now = new Date(), lastDay = now.getTime(), firstDay = new Date(now.setDate(1)).getTime();
|
||||||
const lastMonth = new Date(now-30 * 24 * 60 * 60 * 1000).getTime();
|
this.defaultDateRange = [formatDate(firstDay), formatDate(lastDay)];
|
||||||
this.defaultDateRange = [formatDate(lastMonth), formatDate(now)];
|
this.dateRange = [formatDate(firstDay), formatDate(lastDay)];
|
||||||
this.dateRange=[formatDate(lastMonth), formatDate(now)];
|
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -195,8 +207,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
::v-deep{
|
::v-deep {
|
||||||
.common-table.el-table .el-table__header-wrapper th, .common-table.el-table .el-table__fixed-header-wrapper th{
|
.common-table.el-table .el-table__header-wrapper th, .common-table.el-table .el-table__fixed-header-wrapper th {
|
||||||
border-bottom: 1px solid #dfe6ec;
|
border-bottom: 1px solid #dfe6ec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card
|
<el-card
|
||||||
shadow="always"
|
shadow="always"
|
||||||
class="common-card-container time-range-card"
|
class="common-card-container time-range-card"
|
||||||
style="margin-top: 20px"
|
style="margin-top: 20px"
|
||||||
>
|
>
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<span class="card-title"> </span>
|
<span class="card-title"> </span>
|
||||||
<date-range-select ref="dateRangeSelect" @updateDate="updateDate" />
|
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-main" v-loading="loading">
|
<div class="card-main" v-loading="loading">
|
||||||
<el-button-group class="ems-btns-group">
|
<el-button-group class="ems-btns-group">
|
||||||
<el-button
|
<el-button
|
||||||
v-for="(item, index) in btnList"
|
v-for="(item, index) in btnList"
|
||||||
:key="index + 'dcdqxBtns'"
|
:key="index + 'dcdqxBtns'"
|
||||||
size="mini"
|
size="mini"
|
||||||
:class="{ activeBtn: activeBtn === item.id }"
|
:class="{ activeBtn: activeBtn === item.id }"
|
||||||
@click="changeDataType(item.id)"
|
@click="changeDataType(item.id)"
|
||||||
>{{ item.name }}</el-button
|
>{{ item.name }}
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
<div id="dcdEchart" style="height: 310px"></div>
|
<div id="dcdEchart" style="height: 310px"></div>
|
||||||
@ -28,12 +29,13 @@
|
|||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getStackData, getStackNameList } from "@/api/ems/dzjk";
|
import {getStackData} from "@/api/ems/dzjk";
|
||||||
import { formatDate } from "@/filters/ems";
|
import {formatDate} from "@/filters/ems";
|
||||||
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkTjbbDcdqx",
|
name: "DzjkTjbbDcdqx",
|
||||||
components: { DateRangeSelect },
|
components: {DateRangeSelect},
|
||||||
mixins: [resize, getQuerySiteId],
|
mixins: [resize, getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -46,10 +48,10 @@ export default {
|
|||||||
loading: false,
|
loading: false,
|
||||||
activeBtn: "1",
|
activeBtn: "1",
|
||||||
btnList: [
|
btnList: [
|
||||||
{ name: "堆平均维度", id: "1", attr: ["temp"], source: ["有功功率"] },
|
{name: "堆平均维度", id: "1", attr: ["temp"], source: ["有功功率"]},
|
||||||
{ name: "堆电压", id: "2", attr: ["voltage"], source: ["堆电压"] },
|
{name: "堆电压", id: "2", attr: ["voltage"], source: ["堆电压"]},
|
||||||
{ name: "堆电流", id: "3", attr: ["current"], source: ["堆电流"] },
|
{name: "堆电流", id: "3", attr: ["current"], source: ["堆电流"]},
|
||||||
{ name: "堆soc", id: "4", attr: ["soc"], source: ["堆soc"] },
|
{name: "堆soc", id: "4", attr: ["soc"], source: ["堆soc"]},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -66,7 +68,7 @@ export default {
|
|||||||
this.getData();
|
this.getData();
|
||||||
},
|
},
|
||||||
getData() {
|
getData() {
|
||||||
const { siteId, activeBtn } = this;
|
const {siteId, activeBtn} = this;
|
||||||
const [start = "", end = ""] = this.dateRange || [];
|
const [start = "", end = ""] = this.dateRange || [];
|
||||||
//接口调用完成之后 设置图表、结束loading
|
//接口调用完成之后 设置图表、结束loading
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
@ -76,12 +78,12 @@ export default {
|
|||||||
endTime: formatDate(end),
|
endTime: formatDate(end),
|
||||||
dataType: activeBtn,
|
dataType: activeBtn,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data || []);
|
this.setOption(response?.data || []);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
compareDate(date1, date2) {
|
compareDate(date1, date2) {
|
||||||
console.log("比较时间", date1, date2);
|
console.log("比较时间", date1, date2);
|
||||||
@ -92,9 +94,9 @@ export default {
|
|||||||
const [date1_Y = "", date1_M = "", date1_D = ""] = date1.split("-"); //根据空格区分[年月日,小时]
|
const [date1_Y = "", date1_M = "", date1_D = ""] = date1.split("-"); //根据空格区分[年月日,小时]
|
||||||
const [date2_Y = "", date2_M = "", date2_D = ""] = date2.split("-"); //根据空格区分[年月日,小时]
|
const [date2_Y = "", date2_M = "", date2_D = ""] = date2.split("-"); //根据空格区分[年月日,小时]
|
||||||
return (
|
return (
|
||||||
(date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) ||
|
(date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) ||
|
||||||
(date1_Y === date2_Y && date1_M - date2_M) ||
|
(date1_Y === date2_Y && date1_M - date2_M) ||
|
||||||
date1_Y - date2_Y
|
date1_Y - date2_Y
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
setOption(data) {
|
setOption(data) {
|
||||||
@ -106,7 +108,7 @@ export default {
|
|||||||
const source = [];
|
const source = [];
|
||||||
const sourceTop = ["日期"];
|
const sourceTop = ["日期"];
|
||||||
let map = {},
|
let map = {},
|
||||||
mapArr = [];
|
mapArr = [];
|
||||||
// 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...]
|
// 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...]
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
item.dataList.forEach((inner) => {
|
item.dataList.forEach((inner) => {
|
||||||
@ -136,38 +138,53 @@ export default {
|
|||||||
});
|
});
|
||||||
source.unshift(sourceTop);
|
source.unshift(sourceTop);
|
||||||
this.chart.setOption(
|
this.chart.setOption(
|
||||||
{
|
{
|
||||||
grid: {
|
grid: {
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
left: "center",
|
|
||||||
bottom: "15",
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
axisPointer: {
|
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
},
|
||||||
|
legend: {
|
||||||
|
left: "center",
|
||||||
|
top: "10",
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器,坐标轴触发有效
|
||||||
|
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dataset: {source},
|
||||||
|
series: source[0].slice(1).map((item) => {
|
||||||
|
return {
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
textStyle: {
|
true
|
||||||
color: "#333333",
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: "value",
|
|
||||||
},
|
|
||||||
dataset: { source },
|
|
||||||
series: source[0].slice(1).map((item) => {
|
|
||||||
return {
|
|
||||||
type: "line",
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
initChart() {
|
initChart() {
|
||||||
@ -176,7 +193,7 @@ export default {
|
|||||||
init() {
|
init() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.initChart();
|
this.initChart();
|
||||||
this.$refs.dateRangeSelect.init();
|
this.$refs.dateRangeSelect.init(true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -147,6 +147,9 @@ export default {
|
|||||||
if(val){
|
if(val){
|
||||||
this.search.clusterId=''
|
this.search.clusterId=''
|
||||||
this.getClusterList()
|
this.getClusterList()
|
||||||
|
} else {
|
||||||
|
this.search.clusterId=''
|
||||||
|
this.clusterOptions=[]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//表格数据
|
//表格数据
|
||||||
@ -170,8 +173,15 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async getClusterList(){
|
async getClusterList(){
|
||||||
|
const currentStackId = String(this.search.stackId || '')
|
||||||
|
if (!currentStackId) {
|
||||||
|
this.clusterOptions = []
|
||||||
|
this.search.clusterId = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
this.clusterloading =true
|
this.clusterloading =true
|
||||||
await getClusterNameList({stackDeviceId: this.search.stackId, siteId: this.siteId}).then(response => {
|
await getClusterNameList({stackDeviceId: this.search.stackId, siteId: this.siteId}).then(response => {
|
||||||
|
if (String(this.search.stackId || '') !== currentStackId) return
|
||||||
const data = JSON.parse(JSON.stringify(response?.data || []))
|
const data = JSON.parse(JSON.stringify(response?.data || []))
|
||||||
this.clusterOptions = data
|
this.clusterOptions = data
|
||||||
this.search.clusterId = data.length > 0 ? data[0].id : ''
|
this.search.clusterId = data.length > 0 ? data[0].id : ''
|
||||||
@ -200,4 +210,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card
|
<el-card
|
||||||
shadow="always"
|
shadow="always"
|
||||||
class="common-card-container time-range-card"
|
class="common-card-container time-range-card"
|
||||||
style="margin-top: 20px"
|
style="margin-top: 20px"
|
||||||
>
|
>
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<span class="card-title">功率曲线</span>
|
<span class="card-title">功率曲线</span>
|
||||||
<date-range-select
|
<date-range-select
|
||||||
ref="dateRangeSelect"
|
ref="dateRangeSelect"
|
||||||
@reset="resetTime"
|
@updateDate="updateDate"
|
||||||
@updateDate="updateDate"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-main" v-loading="loading">
|
<div class="card-main" v-loading="loading">
|
||||||
@ -22,12 +21,13 @@
|
|||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getPcsNameList, getPowerData } from "@/api/ems/dzjk";
|
import {getPowerData} from "@/api/ems/dzjk";
|
||||||
import { formatDate } from "@/filters/ems";
|
import {formatDate} from "@/filters/ems";
|
||||||
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkTjbbGlqx",
|
name: "DzjkTjbbGlqx",
|
||||||
components: { DateRangeSelect },
|
components: {DateRangeSelect},
|
||||||
mixins: [resize, getQuerySiteId],
|
mixins: [resize, getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -38,7 +38,6 @@ export default {
|
|||||||
},
|
},
|
||||||
dateRange: [],
|
dateRange: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
dateRangeInit: true,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -47,30 +46,22 @@ export default {
|
|||||||
this.dateRange = data || [];
|
this.dateRange = data || [];
|
||||||
this.getData();
|
this.getData();
|
||||||
},
|
},
|
||||||
resetTime() {
|
|
||||||
this.dateRangeInit = true;
|
|
||||||
},
|
|
||||||
getData() {
|
getData() {
|
||||||
const { siteId } = this;
|
const {siteId} = this;
|
||||||
let [start = "", end = ""] = this.dateRange || [];
|
let [start = "", end = ""] = this.dateRange || [];
|
||||||
//接口调用完成之后 设置图表、结束loading
|
//接口调用完成之后 设置图表、结束loading
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
if (this.dateRangeInit) {
|
|
||||||
start = "";
|
|
||||||
end = "";
|
|
||||||
this.dateRangeInit = false;
|
|
||||||
}
|
|
||||||
getPowerData({
|
getPowerData({
|
||||||
siteId,
|
siteId,
|
||||||
startDate: formatDate(start),
|
startDate: formatDate(start),
|
||||||
endDate: formatDate(end),
|
endDate: formatDate(end),
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data || []);
|
this.setOption(response?.data || []);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setOption(data) {
|
setOption(data) {
|
||||||
const source = [["日期", "电网功率", "负载功率", "储能功率", "光伏功率"]];
|
const source = [["日期", "电网功率", "负载功率", "储能功率", "光伏功率"]];
|
||||||
@ -84,47 +75,74 @@ export default {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
this.chart.setOption(
|
this.chart.setOption(
|
||||||
{
|
{
|
||||||
grid: {
|
grid: {
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
left: "center",
|
|
||||||
bottom: "15",
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
axisPointer: {
|
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
},
|
||||||
},
|
legend: {
|
||||||
textStyle: {
|
left: "center",
|
||||||
color: "#333333",
|
top: "10",
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: "value",
|
|
||||||
},
|
|
||||||
dataset: { source },
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: "line",
|
|
||||||
},
|
},
|
||||||
{
|
tooltip: {
|
||||||
type: "line",
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器,坐标轴触发有效
|
||||||
|
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
textStyle: {
|
||||||
type: "line",
|
color: "#333333",
|
||||||
},
|
},
|
||||||
{
|
xAxis: {
|
||||||
type: "line",
|
type: "category",
|
||||||
},
|
},
|
||||||
],
|
yAxis: {
|
||||||
},
|
type: "value",
|
||||||
true
|
},
|
||||||
|
dataset: {source},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
true
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
initChart() {
|
initChart() {
|
||||||
@ -133,7 +151,6 @@ export default {
|
|||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.dateRangeInit = true;
|
|
||||||
this.initChart();
|
this.initChart();
|
||||||
this.$refs.dateRangeSelect.init();
|
this.$refs.dateRangeSelect.init();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -125,6 +125,12 @@ export default {
|
|||||||
this.totalChargedCap=totalChargedCap
|
this.totalChargedCap=totalChargedCap
|
||||||
this.totalDisChargedCap=totalDisChargedCap
|
this.totalDisChargedCap=totalDisChargedCap
|
||||||
this.efficiency=efficiency
|
this.efficiency=efficiency
|
||||||
|
}).catch(() => {
|
||||||
|
this.setOption([], '')
|
||||||
|
this.totalChargedCap=''
|
||||||
|
this.totalDisChargedCap=''
|
||||||
|
this.efficiency=''
|
||||||
|
// 错误提示由全局请求拦截器处理,这里兜底避免出现 Uncaught (in promise)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading=false;
|
this.loading=false;
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,22 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card
|
<el-card
|
||||||
shadow="always"
|
shadow="always"
|
||||||
class="common-card-container time-range-card"
|
class="common-card-container time-range-card"
|
||||||
style="margin-top: 20px"
|
style="margin-top: 20px"
|
||||||
>
|
>
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<span class="card-title"> </span>
|
<span class="card-title"> </span>
|
||||||
<date-range-select ref="dateRangeSelect" @updateDate="updateDate" />
|
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-main" v-loading="loading">
|
<div class="card-main" v-loading="loading">
|
||||||
<el-button-group class="ems-btns-group">
|
<el-button-group class="ems-btns-group">
|
||||||
<el-button
|
<el-button
|
||||||
v-for="(item, index) in btnList"
|
v-for="(item, index) in btnList"
|
||||||
:key="index + 'flqxcBtns'"
|
:key="index + 'flqxcBtns'"
|
||||||
size="mini"
|
size="mini"
|
||||||
:class="{ activeBtn: activeBtn === item.id }"
|
:class="{ activeBtn: activeBtn === item.id }"
|
||||||
@click="changeDataType(item.id)"
|
@click="changeDataType(item.id)"
|
||||||
>{{ item.name }}</el-button
|
>{{ item.name }}
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
<div id="pcsEchart" style="height: 310px"></div>
|
<div id="pcsEchart" style="height: 310px"></div>
|
||||||
@ -28,12 +29,13 @@
|
|||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import resize from "@/mixins/ems/resize";
|
import resize from "@/mixins/ems/resize";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getPCSData, getPcsNameList } from "@/api/ems/dzjk";
|
import {getPCSData} from "@/api/ems/dzjk";
|
||||||
import { formatDate } from "@/filters/ems";
|
import {formatDate} from "@/filters/ems";
|
||||||
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkTjbbPcsqx",
|
name: "DzjkTjbbPcsqx",
|
||||||
components: { DateRangeSelect },
|
components: {DateRangeSelect},
|
||||||
mixins: [resize, getQuerySiteId],
|
mixins: [resize, getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -82,7 +84,7 @@ export default {
|
|||||||
this.getData();
|
this.getData();
|
||||||
},
|
},
|
||||||
getData() {
|
getData() {
|
||||||
const { siteId, activeBtn } = this;
|
const {siteId, activeBtn} = this;
|
||||||
const [start = "", end = ""] = this.dateRange || [];
|
const [start = "", end = ""] = this.dateRange || [];
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
//接口调用完成之后 设置图表、结束loading
|
//接口调用完成之后 设置图表、结束loading
|
||||||
@ -92,12 +94,12 @@ export default {
|
|||||||
endTime: formatDate(end),
|
endTime: formatDate(end),
|
||||||
dataType: activeBtn,
|
dataType: activeBtn,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setOption(response?.data || []);
|
this.setOption(response?.data || []);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
compareDate(date1, date2) {
|
compareDate(date1, date2) {
|
||||||
console.log("比较时间", date1, date2);
|
console.log("比较时间", date1, date2);
|
||||||
@ -108,9 +110,9 @@ export default {
|
|||||||
const [date1_Y = "", date1_M = "", date1_D = ""] = date1.split("-"); //根据空格区分[年月日,小时]
|
const [date1_Y = "", date1_M = "", date1_D = ""] = date1.split("-"); //根据空格区分[年月日,小时]
|
||||||
const [date2_Y = "", date2_M = "", date2_D = ""] = date2.split("-"); //根据空格区分[年月日,小时]
|
const [date2_Y = "", date2_M = "", date2_D = ""] = date2.split("-"); //根据空格区分[年月日,小时]
|
||||||
return (
|
return (
|
||||||
(date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) ||
|
(date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) ||
|
||||||
(date1_Y === date2_Y && date1_M - date2_M) ||
|
(date1_Y === date2_Y && date1_M - date2_M) ||
|
||||||
date1_Y - date2_Y
|
date1_Y - date2_Y
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
setOption(data) {
|
setOption(data) {
|
||||||
@ -122,7 +124,7 @@ export default {
|
|||||||
const source = [];
|
const source = [];
|
||||||
const sourceTop = ["日期"];
|
const sourceTop = ["日期"];
|
||||||
let map = {},
|
let map = {},
|
||||||
mapArr = [];
|
mapArr = [];
|
||||||
// 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...]
|
// 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...]
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
item.dataList.forEach((inner) => {
|
item.dataList.forEach((inner) => {
|
||||||
@ -152,38 +154,53 @@ export default {
|
|||||||
});
|
});
|
||||||
source.unshift(sourceTop);
|
source.unshift(sourceTop);
|
||||||
this.chart.setOption(
|
this.chart.setOption(
|
||||||
{
|
{
|
||||||
grid: {
|
grid: {
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
left: "center",
|
|
||||||
bottom: "15",
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
axisPointer: {
|
|
||||||
// 坐标轴指示器,坐标轴触发有效
|
|
||||||
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
},
|
},
|
||||||
|
legend: {
|
||||||
|
left: "center",
|
||||||
|
top: "10",
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
// 坐标轴指示器,坐标轴触发有效
|
||||||
|
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: "#333333",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "inside",
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dataset: {source},
|
||||||
|
series: source[0].slice(1).map((item) => {
|
||||||
|
return {
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
textStyle: {
|
true
|
||||||
color: "#333333",
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: "value",
|
|
||||||
},
|
|
||||||
dataset: { source },
|
|
||||||
series: source[0].slice(1).map((item) => {
|
|
||||||
return {
|
|
||||||
type: "line",
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
initChart() {
|
initChart() {
|
||||||
@ -192,7 +209,7 @@ export default {
|
|||||||
init() {
|
init() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.initChart();
|
this.initChart();
|
||||||
this.$refs.dateRangeSelect.init();
|
this.$refs.dateRangeSelect.init(true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="width:100%" v-loading="loading">
|
<div style="width:100%" v-loading="loading">
|
||||||
<!-- 搜索栏-->
|
<!-- 搜索栏-->
|
||||||
@ -22,172 +21,199 @@
|
|||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="onReset" native-type="button">重置</el-button>
|
<el-button @click="onReset" native-type="button">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="exportTable" native-type="button">导出</el-button>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<!--表格-->
|
<!--表格-->
|
||||||
<el-table
|
<el-table
|
||||||
class="common-table"
|
class="common-table"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
stripe
|
show-summary
|
||||||
style="width: 100%;margin-top:25px;">
|
stripe
|
||||||
<!-- 汇总列-->
|
style="width: 100%;margin-top:25px;">
|
||||||
<el-table-column label="汇总">
|
<!-- 汇总列-->
|
||||||
<el-table-column
|
<el-table-column label="汇总" min-width="100px" align="center">
|
||||||
prop="dataTime"
|
<el-table-column
|
||||||
label="日期"
|
prop="dataTime"
|
||||||
width="120">
|
label="日期"
|
||||||
</el-table-column>
|
min-width="100px" align="center">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!--充电量列-->
|
<el-table-column
|
||||||
<el-table-column label="充电价格" align="center">
|
prop="dayType"
|
||||||
<el-table-column
|
label="日期类型"
|
||||||
align="center"
|
min-width="100px" align="center">
|
||||||
prop="activePeakKwh"
|
</el-table-column>
|
||||||
label="尖">
|
<el-table-column
|
||||||
</el-table-column>
|
prop="weatherDesc"
|
||||||
<el-table-column
|
label="天气情况"
|
||||||
align="center"
|
min-width="180px"
|
||||||
prop="activeHighKwh"
|
align="center">
|
||||||
label="峰">
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<!--充电量列-->
|
||||||
align="center"
|
<el-table-column label="充电价格" align="center">
|
||||||
prop="activeFlatKwh"
|
<el-table-column
|
||||||
label="平">
|
align="center"
|
||||||
</el-table-column>
|
prop="activePeakPrice"
|
||||||
<el-table-column
|
label="尖">
|
||||||
align="center"
|
</el-table-column>
|
||||||
prop="activeValleyKwh"
|
<el-table-column
|
||||||
label="谷">
|
align="center"
|
||||||
</el-table-column>
|
prop="activeHighPrice"
|
||||||
<el-table-column
|
label="峰">
|
||||||
align="center"
|
</el-table-column>
|
||||||
prop="activeTotalKwh"
|
<el-table-column
|
||||||
label="总">
|
align="center"
|
||||||
</el-table-column>
|
prop="activeFlatPrice"
|
||||||
</el-table-column>
|
label="平">
|
||||||
<!--充电量列-->
|
</el-table-column>
|
||||||
<el-table-column label="放电价格" align="center">
|
<el-table-column
|
||||||
<el-table-column
|
align="center"
|
||||||
align="center"
|
prop="activeValleyPrice"
|
||||||
prop="reActivePeakKwh"
|
label="谷">
|
||||||
label="尖">
|
</el-table-column>
|
||||||
</el-table-column>
|
<el-table-column
|
||||||
<el-table-column
|
align="center"
|
||||||
align="center"
|
prop="activeTotalPrice"
|
||||||
prop="reActiveHighKwh"
|
label="总">
|
||||||
label="峰">
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<!--充电量列-->
|
||||||
align="center"
|
<el-table-column label="放电价格" align="center">
|
||||||
prop="reActiveFlatKwh"
|
<el-table-column
|
||||||
label="平">
|
align="center"
|
||||||
</el-table-column>
|
prop="reActivePeakPrice"
|
||||||
<el-table-column
|
label="尖">
|
||||||
align="center"
|
</el-table-column>
|
||||||
prop="reActiveValleyKwh"
|
<el-table-column
|
||||||
label="谷">
|
align="center"
|
||||||
</el-table-column>
|
prop="reActiveHighPrice"
|
||||||
<el-table-column
|
label="峰">
|
||||||
align="center"
|
</el-table-column>
|
||||||
prop="reActiveTotalKwh"
|
<el-table-column
|
||||||
label="总">
|
align="center"
|
||||||
</el-table-column>
|
prop="reActiveFlatPrice"
|
||||||
</el-table-column>
|
label="平">
|
||||||
<!-- 效率-->
|
</el-table-column>
|
||||||
<!-- <el-table-column label="效率(%)" align="center">-->
|
<el-table-column
|
||||||
<!-- <el-table-column-->
|
align="center"
|
||||||
<!-- align="center"-->
|
prop="reActiveValleyPrice"
|
||||||
<!-- prop="effect">-->
|
label="谷">
|
||||||
<!-- </el-table-column>-->
|
</el-table-column>
|
||||||
<!-- </el-table-column>-->
|
<el-table-column
|
||||||
</el-table>
|
align="center"
|
||||||
<el-pagination
|
prop="reActiveTotalPrice"
|
||||||
v-show="tableData.length>0"
|
label="总">
|
||||||
background
|
</el-table-column>
|
||||||
@size-change="handleSizeChange"
|
</el-table-column>
|
||||||
@current-change="handleCurrentChange"
|
<!-- 实际收益-->
|
||||||
:current-page="pageNum"
|
<el-table-column label="" align="center" fixed="right">
|
||||||
:page-size="pageSize"
|
<el-table-column
|
||||||
:page-sizes="[10, 20, 30, 40]"
|
prop="actualRevenue"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
label="实际收益"
|
||||||
:total="totalSize"
|
align="center">
|
||||||
style="margin-top:15px;text-align: center"
|
</el-table-column>
|
||||||
>
|
</el-table-column>
|
||||||
</el-pagination>
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
v-show="tableData.length>0"
|
||||||
|
background
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pageNum"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 30, 40]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="totalSize"
|
||||||
|
style="margin-top:15px;text-align: center"
|
||||||
|
>
|
||||||
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { getAmmeterData} from '@/api/ems/dzjk'
|
import {getAmmeterRevenueData} from '@/api/ems/dzjk'
|
||||||
import {formatDate} from "@/filters/ems";
|
import {formatDate} from "@/filters/ems";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name:'DzjkTjbbSybb',
|
name: 'DzjkTjbbSybb',
|
||||||
mixins: [getQuerySiteId],
|
mixins: [getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
pickerOptions:{
|
pickerOptions: {
|
||||||
disabledDate(time) {
|
disabledDate(time) {
|
||||||
return time.getTime() > Date.now();
|
return time.getTime() > Date.now();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultDateRange:[],//默认展示的时间
|
defaultDateRange: [],//默认展示的时间
|
||||||
dateRange:[],
|
dateRange: [],
|
||||||
tableData:[],
|
tableData: [],
|
||||||
pageSize:10,//分页栏当前每个数据总数
|
pageSize: 10,//分页栏当前每个数据总数
|
||||||
pageNum:1,//分页栏当前页数
|
pageNum: 1,//分页栏当前页数
|
||||||
totalSize:0,//table表格数据总数
|
totalSize: 0,//table表格数据总数
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
|
// 导出表格
|
||||||
|
exportTable() {
|
||||||
|
if (!this.dateRange?.length) return
|
||||||
|
const [startTime, endTime] = this.dateRange
|
||||||
|
this.download('ems/statsReport/exportAmmeterRevenueData', {
|
||||||
|
siteId: this.siteId,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
}, `收益报表_${startTime}-${endTime}.xlsx`)
|
||||||
|
},
|
||||||
// 搜索
|
// 搜索
|
||||||
onSearch(){
|
onSearch() {
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 重置
|
// 重置
|
||||||
onReset(){
|
onReset() {
|
||||||
this.dateRange=this.defaultDateRange
|
this.dateRange = this.defaultDateRange
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 分页
|
// 分页
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCurrentChange(val) {
|
handleCurrentChange(val) {
|
||||||
this.pageNum = val
|
this.pageNum = val
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 获取数据
|
// 获取数据
|
||||||
getData(){
|
getData() {
|
||||||
this.loading=true
|
this.loading = true
|
||||||
const {siteId,pageNum,pageSize} =this
|
const {siteId, pageNum, pageSize} = this
|
||||||
const [startTime='',endTime='']=(this.dateRange || [])
|
const [startTime = '', endTime = ''] = (this.dateRange || [])
|
||||||
getAmmeterData({siteId:siteId,startTime,endTime,pageSize,pageNum}).then(response=>{
|
getAmmeterRevenueData({siteId: siteId, startTime, endTime, pageSize, pageNum}).then(response => {
|
||||||
this.tableData=response?.rows || [];
|
const rows = response?.rows || [];
|
||||||
this.totalSize = response?.total || 0
|
this.totalSize = response?.total || 0
|
||||||
}).finally(()=> {
|
this.tableData = rows
|
||||||
|
}).finally(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
init(){
|
init() {
|
||||||
this.dateRange=[]
|
this.dateRange = []
|
||||||
this.tableData=[]
|
this.tableData = []
|
||||||
this.totalSize=0
|
this.totalSize = 0
|
||||||
this.pageSize=10
|
this.pageSize = 10
|
||||||
this.pageNum = 1
|
this.pageNum = 1
|
||||||
const now = new Date().getTime();
|
let now = new Date(), lastDay = now.getTime(), firstDay = new Date(now.setDate(1)).getTime();
|
||||||
const lastMonth = new Date(now-30 * 24 * 60 * 60 * 1000).getTime();
|
this.defaultDateRange = [formatDate(firstDay), formatDate(lastDay)];
|
||||||
this.defaultDateRange = [formatDate(lastMonth), formatDate(now)];
|
this.dateRange = [formatDate(firstDay), formatDate(lastDay)];
|
||||||
this.dateRange=[formatDate(lastMonth), formatDate(now)];
|
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -195,10 +221,20 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
::v-deep{
|
::v-deep {
|
||||||
.common-table.el-table .el-table__header-wrapper th, .common-table.el-table .el-table__fixed-header-wrapper th{
|
.common-table.el-table {
|
||||||
border-bottom: 1px solid #dfe6ec;
|
.el-table__header-wrapper th, .common-table.el-table .el-table__fixed-header-wrapper th {
|
||||||
|
border-bottom: 1px solid #dfe6ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__footer-wrapper {
|
||||||
|
tbody td.el-table__cell {
|
||||||
|
color: #000;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,577 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="ems-dashboard-editor-container" v-loading="loading">
|
|
||||||
<div class="container" v-show="!empty">
|
|
||||||
<!-- 云、电脑 -->
|
|
||||||
<div class="top">
|
|
||||||
<div class="cloud-container">
|
|
||||||
<div class="cloud">
|
|
||||||
<span style="z-index: 2; position: relative">云</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="double-arrows">
|
|
||||||
<div class="top-arrows"></div>
|
|
||||||
<div class="bottom-arrows"></div>
|
|
||||||
</div>
|
|
||||||
<div class="computer">
|
|
||||||
<img src="@/assets/images/ems/computer.png" alt="" />
|
|
||||||
<span style="z-index: 2; position: relative">ems</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="outer-border">
|
|
||||||
<!-- 电表-->
|
|
||||||
<div class="row-lists-container" v-if="showDb">
|
|
||||||
<div class="row-title">电表({{ db.length }})</div>
|
|
||||||
<div class="row-lists">
|
|
||||||
<div v-for="item in db" :key="item.deviceId" class="row-items">
|
|
||||||
<div
|
|
||||||
class="status"
|
|
||||||
:class="
|
|
||||||
item.communicationStatus === '0' ? 'status-running' : ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ communicationStatusOptions[item.communicationStatus] }}
|
|
||||||
</div>
|
|
||||||
<div class="row-items-img">
|
|
||||||
<img
|
|
||||||
class="img-db"
|
|
||||||
:src="require('@/assets/images/ems/db.png')"
|
|
||||||
/>
|
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 液冷-->
|
|
||||||
<div class="row-lists-container" v-if="showLq">
|
|
||||||
<div class="row-title">冷却({{ lq.length }})</div>
|
|
||||||
<div class="row-lists">
|
|
||||||
<div v-for="item in lq" :key="item.deviceId" class="row-items">
|
|
||||||
<div
|
|
||||||
class="status"
|
|
||||||
:class="
|
|
||||||
item.communicationStatus === '0' ? 'status-running' : ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ communicationStatusOptions[item.communicationStatus] }}
|
|
||||||
</div>
|
|
||||||
<div class="row-items-img">
|
|
||||||
<img
|
|
||||||
class="img-lq"
|
|
||||||
:src="require('@/assets/images/ems/lq.png')"
|
|
||||||
/>
|
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- PCS-->
|
|
||||||
<div class="row-lists-container" v-if="showPcs">
|
|
||||||
<div class="row-lists">
|
|
||||||
<div class="row-title">PCS({{ pcs.length }})</div>
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in pcs"
|
|
||||||
:key="item.deviceId"
|
|
||||||
class="row-items row-items-pcs"
|
|
||||||
>
|
|
||||||
<!-- pcs -->
|
|
||||||
<div class="parent-dash">
|
|
||||||
<div
|
|
||||||
class="status"
|
|
||||||
:class="
|
|
||||||
item.communicationStatus === '0' ? 'status-running' : ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ communicationStatusOptions[item.communicationStatus] }}
|
|
||||||
</div>
|
|
||||||
<div class="row-items-img">
|
|
||||||
<img
|
|
||||||
class="img-pcs"
|
|
||||||
:src="require('@/assets/images/ems/pcs.png')"
|
|
||||||
/>
|
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 子设备 bms -->
|
|
||||||
<div
|
|
||||||
v-if="item.children && item.children.length > 0"
|
|
||||||
class="children-dash"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="row-children-title"
|
|
||||||
v-if="bmsHasParentLength > 0 && index === 0"
|
|
||||||
>
|
|
||||||
BMS({{ bmsHasParentLength }})
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="(childrenItem, childrenIndex) in item.children"
|
|
||||||
:key="childrenIndex + 'childrenBms'"
|
|
||||||
class="children-dash-items"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="status"
|
|
||||||
:class="
|
|
||||||
childrenItem.communicationStatus === '0'
|
|
||||||
? 'status-running'
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
communicationStatusOptions[
|
|
||||||
childrenItem.communicationStatus
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<div class="row-items-img">
|
|
||||||
<img
|
|
||||||
class="img-pcs"
|
|
||||||
:src="require('@/assets/images/ems/bms.png')"
|
|
||||||
/>
|
|
||||||
<div class="name">{{ childrenItem.deviceName }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row-lists-container" v-if="showPcs">
|
|
||||||
<div class="row-title">PCS({{ pcs.length }})</div>
|
|
||||||
<div class="row-lists">
|
|
||||||
<div
|
|
||||||
v-for="item in bmsNoParent"
|
|
||||||
:key="item.deviceId"
|
|
||||||
class="row-items row-items-pcs"
|
|
||||||
>
|
|
||||||
<!-- pcs -->
|
|
||||||
<div class="parent-dash">
|
|
||||||
<div
|
|
||||||
class="status"
|
|
||||||
:class="
|
|
||||||
item.communicationStatus === '0' ? 'status-running' : ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ communicationStatusOptions[item.communicationStatus] }}
|
|
||||||
</div>
|
|
||||||
<div class="row-items-img">
|
|
||||||
<img :src="require('@/assets/images/ems/bms.png')" />
|
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- bms没有上级设备-->
|
|
||||||
<div class="row-lists-container" v-if="bmsNoParent.length > 0">
|
|
||||||
<div class="row-title">BMS({{ bmsNoParent.length }})</div>
|
|
||||||
<div class="row-lists">
|
|
||||||
<div
|
|
||||||
v-for="item in bmsNoParent"
|
|
||||||
:key="item.deviceId"
|
|
||||||
class="row-items row-items-pcs"
|
|
||||||
>
|
|
||||||
<!-- pcs -->
|
|
||||||
<div class="parent-dash">
|
|
||||||
<div
|
|
||||||
class="status"
|
|
||||||
:class="
|
|
||||||
item.communicationStatus === '0' ? 'status-running' : ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ communicationStatusOptions[item.communicationStatus] }}
|
|
||||||
</div>
|
|
||||||
<div class="row-items-img">
|
|
||||||
<img :src="require('@/assets/images/ems/bms.png')" />
|
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-empty v-show="empty" :image-size="200"></el-empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getDeviceList } from "@/api/ems/site";
|
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
|
||||||
import { mapState } from "vuex";
|
|
||||||
export default {
|
|
||||||
name: "DzjkZxlt",
|
|
||||||
mixins: [getQuerySiteId],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
pcs: [],
|
|
||||||
bms: [],
|
|
||||||
db: [],
|
|
||||||
lq: [],
|
|
||||||
pcsHasChildren: [],
|
|
||||||
pcsNoChildren: [],
|
|
||||||
bmsNoParent: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
communicationStatusOptions: (state) =>
|
|
||||||
state.ems.communicationStatusOptions,
|
|
||||||
}),
|
|
||||||
|
|
||||||
showPcs() {
|
|
||||||
return this.pcs.length > 0;
|
|
||||||
},
|
|
||||||
showBms() {
|
|
||||||
return this.bms.length > 0;
|
|
||||||
},
|
|
||||||
showDb() {
|
|
||||||
return this.db.length > 0;
|
|
||||||
},
|
|
||||||
showLq() {
|
|
||||||
return this.lq.length > 0;
|
|
||||||
},
|
|
||||||
bmsHasParentLength() {
|
|
||||||
let count = 0;
|
|
||||||
this.pcs.forEach((item) => (count += item.children.length));
|
|
||||||
return count;
|
|
||||||
},
|
|
||||||
empty() {
|
|
||||||
return !this.showBms && !this.showPcs && !this.showDb && !this.showLq;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
init() {
|
|
||||||
this.pcs = [];
|
|
||||||
this.bms = [];
|
|
||||||
this.lq = [];
|
|
||||||
this.db = [];
|
|
||||||
this.bmsNoParent = [];
|
|
||||||
this.loading = true;
|
|
||||||
getDeviceList(this.siteId)
|
|
||||||
.then((response) => {
|
|
||||||
const data = JSON.parse(JSON.stringify(response?.data || []));
|
|
||||||
let pcs = [],
|
|
||||||
bms = [],
|
|
||||||
db = [],
|
|
||||||
lq = [],
|
|
||||||
bmsNoParent = [];
|
|
||||||
data.forEach((item) => {
|
|
||||||
// 电表
|
|
||||||
if (item.deviceCategory === "AMMETER") {
|
|
||||||
db.push({ ...item, children: [] });
|
|
||||||
} else if (item.deviceCategory === "PCS") {
|
|
||||||
// pcs
|
|
||||||
pcs.push({ ...item, children: [] });
|
|
||||||
} else if (item.deviceCategory === "STACK") {
|
|
||||||
// bms
|
|
||||||
bms.push({ ...item, children: [] });
|
|
||||||
} else if (item.deviceCategory === "COOLING") {
|
|
||||||
// 液冷
|
|
||||||
lq.push({ ...item, children: [] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
bms.forEach((item, index) => {
|
|
||||||
if (item.parentId) {
|
|
||||||
pcs
|
|
||||||
.find((pcsItem) => pcsItem.deviceId === item.parentId)
|
|
||||||
.children.push(item);
|
|
||||||
} else {
|
|
||||||
bmsNoParent.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.pcs = pcs;
|
|
||||||
this.bms = bms;
|
|
||||||
this.lq = lq;
|
|
||||||
this.db = db;
|
|
||||||
this.pcsHasChildren = pcs.filter((item) => item.children.length > 0);
|
|
||||||
this.pcsNoChildren = pcs.filter((item) => item.children.length === 0);
|
|
||||||
this.bmsNoParent = bmsNoParent;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$sqDistance: 30px;
|
|
||||||
$borderColor: #174a8e;
|
|
||||||
$lineColor: #86bcc7;
|
|
||||||
|
|
||||||
.ems-dashboard-editor-container {
|
|
||||||
background-color: #ffffff;
|
|
||||||
padding: 0;
|
|
||||||
color: #666666;
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
//云 、计算机 、箭头
|
|
||||||
.top {
|
|
||||||
z-index: 2;
|
|
||||||
width: fit-content;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
// position: absolute;
|
|
||||||
// top: 50%;
|
|
||||||
// left: 0;
|
|
||||||
// transform: translateY(-50%);
|
|
||||||
//云 样式
|
|
||||||
.cloud-container {
|
|
||||||
margin: 0 auto;
|
|
||||||
.cloud {
|
|
||||||
width: 60px;
|
|
||||||
height: 26px;
|
|
||||||
background: #cbebfd;
|
|
||||||
border-radius: 100px;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 26px;
|
|
||||||
}
|
|
||||||
.cloud:before,
|
|
||||||
.cloud:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
background: #cbebfd;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
.cloud:before {
|
|
||||||
top: -9px;
|
|
||||||
left: 8px;
|
|
||||||
}
|
|
||||||
.cloud:after {
|
|
||||||
top: -6px;
|
|
||||||
right: 9px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//双箭头
|
|
||||||
.double-arrows {
|
|
||||||
height: fit-content;
|
|
||||||
margin: 0 10px;
|
|
||||||
text-align: center;
|
|
||||||
.top-arrows,
|
|
||||||
.bottom-arrows {
|
|
||||||
height: 4px;
|
|
||||||
width: 30px;
|
|
||||||
background-color: #5ea9df;
|
|
||||||
margin: 0 10px;
|
|
||||||
position: relative;
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.top-arrows {
|
|
||||||
vertical-align: super;
|
|
||||||
}
|
|
||||||
.top-arrows::after {
|
|
||||||
top: -4px;
|
|
||||||
border-bottom: 6px solid transparent;
|
|
||||||
border-left: 6px solid transparent;
|
|
||||||
border-right: 6px solid #5ea9df;
|
|
||||||
border-top: 6px solid transparent;
|
|
||||||
left: -11px;
|
|
||||||
}
|
|
||||||
.bottom-arrows {
|
|
||||||
margin-top: 8px;
|
|
||||||
&::after {
|
|
||||||
top: -4px;
|
|
||||||
border-top: 6px solid transparent;
|
|
||||||
border-left: 6px solid #5ea9df;
|
|
||||||
border-right: 6px solid transparent;
|
|
||||||
border-bottom: 6px solid transparent;
|
|
||||||
right: -11px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//电脑
|
|
||||||
.computer {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
position: relative;
|
|
||||||
background: #fff;
|
|
||||||
img {
|
|
||||||
width: 80px;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.outer-border {
|
|
||||||
position: relative;
|
|
||||||
width: fit-content;
|
|
||||||
border: 1.5px solid $borderColor;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding-left: 120px;
|
|
||||||
padding-right: 20px;
|
|
||||||
margin-left: -40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设备列表
|
|
||||||
.row-lists-container {
|
|
||||||
font-size: 10px;
|
|
||||||
position: relative;
|
|
||||||
padding: 10px;
|
|
||||||
.row-title {
|
|
||||||
position: absolute;
|
|
||||||
left: -$sqDistance - 30px;
|
|
||||||
top: calc(50% + 10px);
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: #000;
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
.row-lists {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
.row-items {
|
|
||||||
position: relative;
|
|
||||||
padding: 5px 0;
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-left: $sqDistance; //和外层父元素上下padding一致
|
|
||||||
}
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
height: 3px;
|
|
||||||
width: $sqDistance - 2px;
|
|
||||||
background: $lineColor;
|
|
||||||
position: absolute;
|
|
||||||
left: -$sqDistance;
|
|
||||||
top: calc(50% + 10px);
|
|
||||||
transform: scale(1, 0.4);
|
|
||||||
}
|
|
||||||
// 一列 第一个设备最上面的线
|
|
||||||
&:first-child {
|
|
||||||
&::before {
|
|
||||||
width: $sqDistance + 20px;
|
|
||||||
// top: -$sqDistance - 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 一列 最后一个设备最下面的线
|
|
||||||
// &:last-child {
|
|
||||||
// &::after {
|
|
||||||
// content: "";
|
|
||||||
// display: block;
|
|
||||||
// width: 3px;
|
|
||||||
// height: $sqDistance - 2px;
|
|
||||||
// background: $lineColor;
|
|
||||||
// position: absolute;
|
|
||||||
// bottom: -$sqDistance;
|
|
||||||
// left: 50%;
|
|
||||||
// transform: scale(0.4, 1);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// 设备状态
|
|
||||||
.status {
|
|
||||||
margin: 0 auto 4px;
|
|
||||||
width: fit-content;
|
|
||||||
height: 18px;
|
|
||||||
padding: 0 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 8px;
|
|
||||||
line-height: 18px;
|
|
||||||
border: 1px solid #08ffff;
|
|
||||||
border-radius: 2px;
|
|
||||||
background: #aaaaaa;
|
|
||||||
color: #ffffff;
|
|
||||||
&.status-running {
|
|
||||||
background: #00c69c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 图片+设备名称
|
|
||||||
.row-items-img {
|
|
||||||
position: relative;
|
|
||||||
padding-top: 12px;
|
|
||||||
img {
|
|
||||||
width: 80px;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
&.img-lq {
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
&.img-pcs {
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
&.img-db {
|
|
||||||
width: 56px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.name {
|
|
||||||
position: absolute;
|
|
||||||
top: 1px;
|
|
||||||
left: 0;
|
|
||||||
color: #666;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//子设备
|
|
||||||
.row-lists-container-children {
|
|
||||||
margin: 10px 0 0 $sqDistance;
|
|
||||||
.parent-dash {
|
|
||||||
position: relative;
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
height: 40px;
|
|
||||||
width: 3px;
|
|
||||||
background: #ec7f8c;
|
|
||||||
position: absolute;
|
|
||||||
left: 20%;
|
|
||||||
top: -40px;
|
|
||||||
transform: scale(0.4, 1) rotate(-40deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.parent-dash {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
.children-dash {
|
|
||||||
margin: $sqDistance 0 0 $sqDistance;
|
|
||||||
position: relative;
|
|
||||||
.row-children-title {
|
|
||||||
position: absolute;
|
|
||||||
left: -$sqDistance - 30px;
|
|
||||||
top: calc(50% + 10px);
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: #000;
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
.children-dash-items {
|
|
||||||
position: relative;
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
height: $sqDistance;
|
|
||||||
width: 3px;
|
|
||||||
background: #ec7f8c;
|
|
||||||
position: absolute;
|
|
||||||
left: 20%;
|
|
||||||
top: -$sqDistance;
|
|
||||||
transform: scale(0.4, 1) rotate(-40deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,614 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="ems-dashboard-editor-container" v-loading="loading" >
|
|
||||||
<div class="container" v-show="!empty">
|
|
||||||
<div class="top">
|
|
||||||
<div class="cloud-container">
|
|
||||||
<div class="cloud">
|
|
||||||
<span style="z-index:2;position: relative;">云</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="double-arrows">
|
|
||||||
<div class="top-arrows"></div>
|
|
||||||
<div class="bottom-arrows"></div>
|
|
||||||
</div>
|
|
||||||
<div class="computer">
|
|
||||||
<img src="@/assets/images/ems/computer.png" alt="">
|
|
||||||
<span style="z-index:2;position: relative;">ems</span>
|
|
||||||
</div>
|
|
||||||
<div class="arrow"></div>
|
|
||||||
</div>
|
|
||||||
<div class="bottom">
|
|
||||||
<!-- 四列设备-->
|
|
||||||
<div class="zxlt-row">
|
|
||||||
<!-- bms、pcs 下级和上级在一列 -->
|
|
||||||
<div class="row-lists pcs-row-lists" v-if="showPcsAndBms">
|
|
||||||
<div class="item-square">
|
|
||||||
<div class="row-lists-title" v-if="showPcs">PCS({{pcs.length}})</div>
|
|
||||||
<div class="row-lists-title" v-if="showBms">BMS({{bms.length}})</div>
|
|
||||||
</div>
|
|
||||||
<!-- 上下级块 class区分-->
|
|
||||||
<div class="item-square pcs-has-children-item-square" :class="{'no-bms-list':!showBms}" v-for="(item,index) in pcsHasChildren" :key="index+'pcsHasChildren'">
|
|
||||||
<!-- 左边的上级 上级只有一个-->
|
|
||||||
<div class="item-lists parent-item-lists">
|
|
||||||
<!-- 上级设备-->
|
|
||||||
<div class="items normal-items-arrow">
|
|
||||||
<div class="items-inner">
|
|
||||||
<div style="text-align: center;margin-bottom:10px;">
|
|
||||||
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<img v-if="item.pictureUrl" :src="item.pictureUrl">
|
|
||||||
<img v-else :src="require('@/assets/images/ems/pcs.png')"/>
|
|
||||||
<div class="name">{{item.deviceName}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 右边的下级 下级有多个-->
|
|
||||||
<div class="item-lists children-item-lists">
|
|
||||||
<!-- 下级设备 循环生成-->
|
|
||||||
<div class="items children-items-arrow bms-children-arrow" v-for="children in item.children" :key="children.deviceId">
|
|
||||||
<div class="items-inner">
|
|
||||||
<div style="text-align: center;margin-bottom:10px;">
|
|
||||||
<div class="status" :class="children.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[children.communicationStatus] || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<img v-if="children.pictureUrl" :src="children.pictureUrl">
|
|
||||||
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
|
|
||||||
<div class="name">{{children.deviceName}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- 没有上下级关系的bms、pcs-->
|
|
||||||
<div class="item-square" :class="{'no-bms-list':!showBms}">
|
|
||||||
<!-- 左边没有下级的pcs-->
|
|
||||||
<div class="item-lists">
|
|
||||||
<div class="items normal-items-arrow" v-for="item in pcsNoChildren" :key="item.deviceId">
|
|
||||||
<div class="items-inner">
|
|
||||||
<div style="text-align: center;margin-bottom:10px;">
|
|
||||||
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<img v-if="item.pictureUrl" :src="item.pictureUrl">
|
|
||||||
<img v-else :src="require('@/assets/images/ems/pcs.png')"/>
|
|
||||||
<div class="name">{{item.deviceName}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 右边没有上级的bms-->
|
|
||||||
<div class="item-lists">
|
|
||||||
<!-- 下级设备 循环生成-->
|
|
||||||
<div class="items children-items-arrow" v-for="item in bmsNoParent" :key="item.deviceId">
|
|
||||||
<div class="items-inner">
|
|
||||||
<div style="text-align: center;margin-bottom:10px;">
|
|
||||||
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<img v-if="item.pictureUrl" :src="item.pictureUrl">
|
|
||||||
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
|
|
||||||
<div class="name">{{item.deviceName}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 电表-->
|
|
||||||
<div class="row-lists" v-if="showDb">
|
|
||||||
<div class="item-square">
|
|
||||||
<div class="row-lists-title" style="width:100%;">电表({{db.length}})</div>
|
|
||||||
</div>
|
|
||||||
<div class="item-square">
|
|
||||||
<!-- 左边的下级 下级有多个-->
|
|
||||||
<div class="item-lists">
|
|
||||||
<!-- 下级设备 循环生成-->
|
|
||||||
<div class="items normal-items-arrow" v-for="item in db" :key="item.deviceId">
|
|
||||||
<div class="items-inner">
|
|
||||||
<div style="text-align: center;margin-bottom:10px;">
|
|
||||||
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<img v-if="item.pictureUrl" :src="item.pictureUrl">
|
|
||||||
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
|
|
||||||
<div class="name">{{item.deviceName}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!--冷却-->
|
|
||||||
<div class="row-lists" v-if="showLq">
|
|
||||||
<div class="item-square">
|
|
||||||
<div class="row-lists-title" style="width:100%;">冷却({{lq.length}})</div>
|
|
||||||
</div>
|
|
||||||
<div class="item-square">
|
|
||||||
<div class="item-lists">
|
|
||||||
<div class="items normal-items-arrow" v-for="item in lq" :key="item.deviceId">
|
|
||||||
<div class="items-inner">
|
|
||||||
<div style="text-align: center;margin-bottom:10px;">
|
|
||||||
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
|
|
||||||
</div>
|
|
||||||
<img v-if="item.pictureUrl" :src="item.pictureUrl">
|
|
||||||
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
|
|
||||||
<div class="name">{{item.deviceName}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-empty v-show="empty" :image-size="200"></el-empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import {getDeviceList} from'@/api/ems/site'
|
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
|
||||||
import {mapState} from "vuex";
|
|
||||||
export default {
|
|
||||||
name: 'DzjkZxlt',
|
|
||||||
mixins: [getQuerySiteId],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading:false,
|
|
||||||
pcs :[],
|
|
||||||
bms:[],
|
|
||||||
db:[],
|
|
||||||
lq:[],
|
|
||||||
pcsHasChildren:[],
|
|
||||||
pcsNoChildren:[],
|
|
||||||
bmsNoParent:[]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed:{
|
|
||||||
...mapState({
|
|
||||||
communicationStatusOptions:(state)=>state.ems.communicationStatusOptions
|
|
||||||
}),
|
|
||||||
|
|
||||||
showPcs(){
|
|
||||||
return this.pcs.length>0
|
|
||||||
},
|
|
||||||
showBms(){
|
|
||||||
return this.bms.length>0
|
|
||||||
},
|
|
||||||
showDb(){
|
|
||||||
return this.db.length>0
|
|
||||||
},
|
|
||||||
showLq(){
|
|
||||||
return this.lq.length>0
|
|
||||||
},
|
|
||||||
showPcsAndBms(){
|
|
||||||
return this.showPcs || this.showBms
|
|
||||||
},
|
|
||||||
empty(){
|
|
||||||
return !this.showBms && !this.showPcs && !this.showDb && !this.showLq
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
init(){
|
|
||||||
this.pcs = []
|
|
||||||
this.bms = []
|
|
||||||
this.lq=[]
|
|
||||||
this.db=[]
|
|
||||||
this.bmsNoParent=[]
|
|
||||||
this.loading = true
|
|
||||||
getDeviceList(this.siteId).then(response => {
|
|
||||||
const data =JSON.parse(JSON.stringify(response?.data || []))
|
|
||||||
let pcs = [],bms=[],db=[],lq=[],bmsNoParent=[]
|
|
||||||
data.forEach(item=>{
|
|
||||||
// 电表
|
|
||||||
if(item.deviceCategory === 'AMMETER'){
|
|
||||||
db.push({...item,children:[]})
|
|
||||||
}else if(item.deviceCategory === 'PCS'){
|
|
||||||
// pcs
|
|
||||||
pcs.push({...item,children:[]})
|
|
||||||
}else if(item.deviceCategory === 'STACK'){
|
|
||||||
// bms
|
|
||||||
bms.push({...item,children:[]})
|
|
||||||
}else if(item.deviceCategory === 'COOLING'){
|
|
||||||
// 液冷
|
|
||||||
lq.push({...item,children:[]})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
bms.forEach((item,index)=>{
|
|
||||||
if(item.parentId){
|
|
||||||
pcs.find(pcsItem=>pcsItem.deviceId === item.parentId).children.push(item)
|
|
||||||
}else{
|
|
||||||
bmsNoParent.push(item)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.pcs = pcs
|
|
||||||
this.bms = bms
|
|
||||||
this.lq=lq
|
|
||||||
this.db=db
|
|
||||||
this.pcsHasChildren = pcs.filter(item=>item.children.length > 0)
|
|
||||||
this.pcsNoChildren = pcs.filter(item=>item.children.length === 0)
|
|
||||||
this.bmsNoParent = bmsNoParent
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$distance:60px;
|
|
||||||
$arrowDistance:80px;//margin:60+quare的padding10
|
|
||||||
$arrowColoe:#5ea9df;
|
|
||||||
$lineColoe:#5ea9df;
|
|
||||||
.ems-dashboard-editor-container {
|
|
||||||
background-color: #ffffff;
|
|
||||||
padding:0;
|
|
||||||
.container{
|
|
||||||
position: relative;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
//云 、计算机 、箭头
|
|
||||||
.top{
|
|
||||||
width: 280px;
|
|
||||||
font-size: 30px;
|
|
||||||
line-height: 40px;
|
|
||||||
font-weight: 500;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
//云 样式
|
|
||||||
.cloud-container{
|
|
||||||
padding-top:40px;
|
|
||||||
margin:0 auto;
|
|
||||||
.cloud {
|
|
||||||
width: 150px;
|
|
||||||
height: 60px;
|
|
||||||
background: #cbebfd;
|
|
||||||
border-radius: 200px;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
color:#666666;
|
|
||||||
}
|
|
||||||
.cloud:before, .cloud:after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
background:#cbebfd;
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.cloud:before {
|
|
||||||
top: -28px;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
.cloud:after {
|
|
||||||
top: -31px;
|
|
||||||
right: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//双箭头
|
|
||||||
.double-arrows {
|
|
||||||
height: 50px;
|
|
||||||
margin:20px 0;
|
|
||||||
text-align: center;
|
|
||||||
.top-arrows,.bottom-arrows{
|
|
||||||
height: 100%;
|
|
||||||
width: 6px;
|
|
||||||
background-color: $arrowColoe;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: super;
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left:0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.top-arrows{
|
|
||||||
vertical-align: super;
|
|
||||||
}
|
|
||||||
.top-arrows::after {
|
|
||||||
bottom: -24px;
|
|
||||||
border-bottom: 12px solid transparent;
|
|
||||||
border-left: 12px solid transparent;
|
|
||||||
border-right: 12px solid transparent;
|
|
||||||
border-top: 14px solid $arrowColoe;
|
|
||||||
left: -9px;
|
|
||||||
}
|
|
||||||
.bottom-arrows{
|
|
||||||
margin-top:12px;
|
|
||||||
&::after {
|
|
||||||
top: -24px;
|
|
||||||
border-top: 12px solid transparent;
|
|
||||||
border-left: 12px solid transparent;
|
|
||||||
border-right: 12px solid transparent;
|
|
||||||
border-bottom: 14px solid $arrowColoe;
|
|
||||||
left: -9px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//电脑
|
|
||||||
.computer{
|
|
||||||
margin:20px auto;
|
|
||||||
text-align: center;
|
|
||||||
color:#666666;
|
|
||||||
position: relative;
|
|
||||||
img {
|
|
||||||
width: auto;
|
|
||||||
height: 100px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.arrow{
|
|
||||||
height: 50px;
|
|
||||||
width: 30px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: $arrowColoe;
|
|
||||||
position: relative;
|
|
||||||
margin:0 auto;
|
|
||||||
&::after{
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
left: -9px;
|
|
||||||
border-top: 24px solid $arrowColoe;
|
|
||||||
border-left: 24px solid transparent;
|
|
||||||
border-bottom: 24px solid transparent;
|
|
||||||
border-right: 24px solid transparent;
|
|
||||||
bottom: -44px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.bottom{
|
|
||||||
z-index:1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-top:50px;
|
|
||||||
.zxlt-row{
|
|
||||||
display: flex;
|
|
||||||
padding:20px $distance;
|
|
||||||
position: relative;
|
|
||||||
width: fit-content;
|
|
||||||
&:before{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width:calc(100% - 100px);
|
|
||||||
height:1px;
|
|
||||||
background-color: $lineColoe;
|
|
||||||
position:absolute;
|
|
||||||
top:0;
|
|
||||||
left: $distance/2;
|
|
||||||
}
|
|
||||||
.row-lists{
|
|
||||||
height: fit-content;
|
|
||||||
position: relative;
|
|
||||||
&:before{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
width: 1px;
|
|
||||||
position: absolute;
|
|
||||||
left:-($distance/2);
|
|
||||||
top:-20px;
|
|
||||||
background-color: $lineColoe;
|
|
||||||
}
|
|
||||||
//pcs列 bms右侧的边框
|
|
||||||
&.pcs-row-lists{
|
|
||||||
&:after{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
width: 1px;
|
|
||||||
position: absolute;
|
|
||||||
right:-(($distance/2) + 1);
|
|
||||||
top:-20px;
|
|
||||||
background-color: $lineColoe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not(:last-child){
|
|
||||||
margin-right: $distance;
|
|
||||||
}
|
|
||||||
.item-square{
|
|
||||||
//左右 两列
|
|
||||||
display: flex;
|
|
||||||
vertical-align: middle;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding:10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
&:not(:last-child){
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
.row-lists-title{
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
color: #333333;
|
|
||||||
font-weight: 500;
|
|
||||||
text-align: center;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.item-lists{
|
|
||||||
position: relative;
|
|
||||||
&:not(:last-child){
|
|
||||||
margin-right:$distance;
|
|
||||||
}
|
|
||||||
//每个设备
|
|
||||||
.items{
|
|
||||||
background-color: #cbebfd;
|
|
||||||
position: relative;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 10px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1), 0 0 0 rgba(0, 0, 0, 0.5);
|
|
||||||
//普通设备 箭头方向
|
|
||||||
&.normal-items-arrow{
|
|
||||||
&:before{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width:($arrowDistance/2) - 15;
|
|
||||||
height: 4px;
|
|
||||||
background-color: $arrowColoe;
|
|
||||||
position: absolute;
|
|
||||||
top:50%;
|
|
||||||
left: -($arrowDistance/2);
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
&:after{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
border-left: 10px solid #5ea9df;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: -15px;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//下级的箭头
|
|
||||||
&.children-items-arrow{
|
|
||||||
&:before{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width:($arrowDistance/2) - 15;
|
|
||||||
height: 4px;
|
|
||||||
background-color: $arrowColoe;
|
|
||||||
position: absolute;
|
|
||||||
top:50%;
|
|
||||||
right: -($arrowDistance/2);
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
&:after{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
border-right: 10px solid #5ea9df;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: -15px;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not(:last-child){
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.items-inner{
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding:10px;
|
|
||||||
width:130px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
img{
|
|
||||||
width: 80px;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
z-index:2;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.name{
|
|
||||||
text-align: center;
|
|
||||||
margin-top:10px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
z-index:2;
|
|
||||||
}
|
|
||||||
.status{
|
|
||||||
z-index:2;
|
|
||||||
margin-top:10px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
position: relative;
|
|
||||||
padding-left:20px;
|
|
||||||
display: inline;
|
|
||||||
&.status-normal {
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #05AEA3;
|
|
||||||
position: absolute;
|
|
||||||
top:50%;
|
|
||||||
left:0;
|
|
||||||
transform: translate(0,-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.status-warn{
|
|
||||||
&:before{
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #FC6B69;
|
|
||||||
position: absolute;
|
|
||||||
top:50%;
|
|
||||||
left:0;
|
|
||||||
transform: translate(0,-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.children-item-lists{
|
|
||||||
//todo 手动修改
|
|
||||||
&:before{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width:40px;
|
|
||||||
height: 4px;
|
|
||||||
background-color: $arrowColoe;
|
|
||||||
position: absolute;
|
|
||||||
top:50%;
|
|
||||||
left: -50px;
|
|
||||||
transform:translateY(-50%);
|
|
||||||
}
|
|
||||||
&:after{
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
border-left: 10px solid #5ea9df;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: -14px;
|
|
||||||
transform:translateY(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.pcs-has-children-item-square{
|
|
||||||
vertical-align: middle;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #ffefad;
|
|
||||||
}
|
|
||||||
.no-bms-list{
|
|
||||||
.item-lists{
|
|
||||||
&:not(:last-child){
|
|
||||||
margin-right:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<div class="bottom-arrows"></div>
|
<div class="bottom-arrows"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="computer">
|
<div class="computer">
|
||||||
<img src="@/assets/images/ems/computer.png" alt="" />
|
<img src="@/assets/images/ems/computer.png" alt=""/>
|
||||||
<span style="z-index: 2; position: relative">ems</span>
|
<span style="z-index: 2; position: relative">ems</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -22,27 +22,27 @@
|
|||||||
<div class="row-lists-container" v-if="showDb">
|
<div class="row-lists-container" v-if="showDb">
|
||||||
<div class="row-title">电表({{ db.length }})</div>
|
<div class="row-title">电表({{ db.length }})</div>
|
||||||
<div
|
<div
|
||||||
class="row-lists"
|
class="row-lists"
|
||||||
v-for="outter in Math.ceil(db.length / 3)"
|
v-for="outter in Math.ceil(db.length / 3)"
|
||||||
:key="outter + 'row'"
|
:key="outter + 'row'"
|
||||||
>
|
>
|
||||||
<template v-for="(item, index) in handlerList('db', outter)">
|
<template v-for="(item, index) in handlerList('db', outter)">
|
||||||
<div :key="index" class="row-items">
|
<div :key="index" class="row-items">
|
||||||
<div
|
<div
|
||||||
style="position: relative; z-index: 2; background-color: #fff"
|
style="position: relative; z-index: 2; background-color: #fff"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="status"
|
class="status"
|
||||||
:class="
|
:class="
|
||||||
item.runningStatus === '2' ? 'status-running' : ''
|
item.deviceStatus === '1' ? 'status-running' : ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ deviceStatusOptions[item.runningStatus] }}
|
{{ deviceStatusOptions[item.deviceStatus] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="row-items-img">
|
<div class="row-items-img">
|
||||||
<img
|
<img
|
||||||
class="img-db"
|
class="img-db"
|
||||||
:src="require('@/assets/images/ems/db.png')"
|
:src="require('@/assets/images/ems/db.png')"
|
||||||
/>
|
/>
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
<div class="name">{{ item.deviceName }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -56,27 +56,27 @@
|
|||||||
<div class="row-lists-container" v-if="showLq">
|
<div class="row-lists-container" v-if="showLq">
|
||||||
<div class="row-title">冷却({{ lq.length }})</div>
|
<div class="row-title">冷却({{ lq.length }})</div>
|
||||||
<div
|
<div
|
||||||
class="row-lists"
|
class="row-lists"
|
||||||
v-for="outter in Math.ceil(lq.length / 3)"
|
v-for="outter in Math.ceil(lq.length / 3)"
|
||||||
:key="outter + 'row'"
|
:key="outter + 'row'"
|
||||||
>
|
>
|
||||||
<template v-for="(item, index) in handlerList('lq', outter)">
|
<template v-for="(item, index) in handlerList('lq', outter)">
|
||||||
<div :key="index" class="row-items">
|
<div :key="index" class="row-items">
|
||||||
<div
|
<div
|
||||||
style="position: relative; z-index: 2; background-color: #fff"
|
style="position: relative; z-index: 2; background-color: #fff"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="status"
|
class="status"
|
||||||
:class="
|
:class="
|
||||||
item.runningStatus === '2' ? 'status-running' : ''
|
item.deviceStatus === '1' ? 'status-running' : ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ deviceStatusOptions[item.runningStatus] }}
|
{{ deviceStatusOptions[item.deviceStatus] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="row-items-img">
|
<div class="row-items-img">
|
||||||
<img
|
<img
|
||||||
class="img-lq"
|
class="img-lq"
|
||||||
:src="require('@/assets/images/ems/lq.png')"
|
:src="require('@/assets/images/ems/lq.png')"
|
||||||
/>
|
/>
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
<div class="name">{{ item.deviceName }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -88,37 +88,37 @@
|
|||||||
|
|
||||||
<!-- 没有上级设备的bms -->
|
<!-- 没有上级设备的bms -->
|
||||||
<div
|
<div
|
||||||
class="row-lists-container"
|
class="row-lists-container"
|
||||||
v-if="bmsHasParentLength !== bms.length"
|
v-if="bmsHasParentLength !== bms.length"
|
||||||
>
|
>
|
||||||
<div class="row-title">BMS({{ bmsNoParent.length }})</div>
|
<div class="row-title">BMS({{ bmsNoParent.length }})</div>
|
||||||
<div
|
<div
|
||||||
class="row-lists"
|
class="row-lists"
|
||||||
v-for="outter in Math.ceil(bmsNoParent.length / 3)"
|
v-for="outter in Math.ceil(bmsNoParent.length / 3)"
|
||||||
:key="outter + 'row'"
|
:key="outter + 'row'"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(item, index) in handlerList('bmsNoParent', outter)"
|
v-for="(item, index) in handlerList('bmsNoParent', outter)"
|
||||||
>
|
>
|
||||||
<div :key="index" class="row-items">
|
<div :key="index" class="row-items">
|
||||||
<div
|
<div
|
||||||
style="position: relative; z-index: 2; background-color: #fff"
|
style="position: relative; z-index: 2; background-color: #fff"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="status"
|
class="status"
|
||||||
:class="
|
:class="
|
||||||
item.runningStatus === '2' ? 'status-running' : ''
|
item.deviceStatus === '1' ? 'status-running' : ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ deviceStatusOptions[item.runningStatus] }}
|
{{ deviceStatusOptions[item.deviceStatus] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="row-items-img row-items-img-bms">
|
<div class="row-items-img row-items-img-bms">
|
||||||
<div style="position:relative;">
|
<div style="position:relative;">
|
||||||
<img
|
<img
|
||||||
class="img-bms"
|
class="img-bms"
|
||||||
:src="require('@/assets/images/ems/bms.png')"
|
:src="require('@/assets/images/ems/bms.png')"
|
||||||
/>
|
/>
|
||||||
<div class="num">{{item.batteryNum || 0}}</div>
|
<div class="num">{{ item.batteryNum || 0 }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
<div class="name">{{ item.deviceName }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -130,8 +130,8 @@
|
|||||||
|
|
||||||
<!-- pcs -->
|
<!-- pcs -->
|
||||||
<div
|
<div
|
||||||
class="row-lists-container row-lists-container-with-children"
|
class="row-lists-container row-lists-container-with-children"
|
||||||
v-if="showPcs"
|
v-if="showPcs"
|
||||||
>
|
>
|
||||||
<div class="row-title">
|
<div class="row-title">
|
||||||
PCS({{ pcs.length }}){{
|
PCS({{ pcs.length }}){{
|
||||||
@ -139,56 +139,56 @@
|
|||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="row-lists"
|
class="row-lists"
|
||||||
v-for="outter in Math.ceil(pcs.length / 3)"
|
v-for="outter in Math.ceil(pcs.length / 3)"
|
||||||
:key="outter + 'row'"
|
:key="outter + 'row'"
|
||||||
>
|
>
|
||||||
<template v-for="(item, index) in handlerList('pcs', outter)">
|
<template v-for="(item, index) in handlerList('pcs', outter)">
|
||||||
<div :key="index" class="row-items">
|
<div :key="index" class="row-items">
|
||||||
<!-- 上级设备 -->
|
<!-- 上级设备 -->
|
||||||
<div class="parent-dash">
|
<div class="parent-dash">
|
||||||
<div
|
<div
|
||||||
class="status"
|
class="status"
|
||||||
:class="
|
:class="
|
||||||
item.runningStatus === '2' ? 'status-running' : ''
|
item.deviceStatus === '1' ? 'status-running' : ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ deviceStatusOptions[item.runningStatus] }}
|
{{ deviceStatusOptions[item.deviceStatus] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="row-items-img">
|
<div class="row-items-img">
|
||||||
<img
|
<img
|
||||||
class="img-pcs"
|
class="img-pcs"
|
||||||
:src="require('@/assets/images/ems/pcs.png')"
|
:src="require('@/assets/images/ems/pcs.png')"
|
||||||
/>
|
/>
|
||||||
<div class="name">{{ item.deviceName }}</div>
|
<div class="name">{{ item.deviceName }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 下级设备 -->
|
<!-- 下级设备 -->
|
||||||
<div
|
<div
|
||||||
class="children-dash"
|
class="children-dash"
|
||||||
v-if="item.children && item.children.length > 0"
|
v-if="item.children && item.children.length > 0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="status"
|
class="status"
|
||||||
:class="
|
:class="
|
||||||
item.children[0].runningStatus === '2'
|
item.children[0].deviceStatus === '1'
|
||||||
? 'status-running'
|
? 'status-running'
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
deviceStatusOptions[
|
deviceStatusOptions[
|
||||||
item.children[0].runningStatus
|
item.children[0].deviceStatus
|
||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div class="row-items-img row-items-img-bms">
|
<div class="row-items-img row-items-img-bms">
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
<img
|
<img
|
||||||
class="img-bms"
|
class="img-bms"
|
||||||
:src="require('@/assets/images/ems/bms.png')"
|
:src="require('@/assets/images/ems/bms.png')"
|
||||||
/>
|
/>
|
||||||
<div class="num">{{item.batteryNum || 0}}</div>
|
<div class="num">{{ item.batteryNum || 0 }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="name">{{ item.children[0].deviceName }}</div>
|
<div class="name">{{ item.children[0].deviceName }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -204,9 +204,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getDeviceList } from "@/api/ems/site";
|
import {getDeviceList} from "@/api/ems/site";
|
||||||
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
import { mapState } from "vuex";
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DzjkZxlt",
|
name: "DzjkZxlt",
|
||||||
mixins: [getQuerySiteId],
|
mixins: [getQuerySiteId],
|
||||||
@ -223,7 +224,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
deviceStatusOptions: (state) =>
|
deviceStatusOptions: (state) =>
|
||||||
state.ems.deviceStatusOptions,
|
state.ems.deviceStatusOptions,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
showPcs() {
|
showPcs() {
|
||||||
@ -245,7 +246,7 @@ export default {
|
|||||||
},
|
},
|
||||||
pcsHasChildren() {
|
pcsHasChildren() {
|
||||||
return this.pcs.filter(
|
return this.pcs.filter(
|
||||||
(item) => item.children && item.children.length > 0
|
(item) => item.children && item.children.length > 0
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
empty() {
|
empty() {
|
||||||
@ -270,46 +271,46 @@ export default {
|
|||||||
this.bmsNoParent = [];
|
this.bmsNoParent = [];
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
getDeviceList(this.siteId)
|
getDeviceList(this.siteId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const data = JSON.parse(JSON.stringify(response?.data || []));
|
const data = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
let pcs = [],
|
let pcs = [],
|
||||||
bms = [],
|
bms = [],
|
||||||
db = [],
|
db = [],
|
||||||
lq = [],
|
lq = [],
|
||||||
bmsNoParent = [];
|
bmsNoParent = [];
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
// 电表
|
// 电表
|
||||||
if (item.deviceCategory === "AMMETER") {
|
if (item.deviceCategory === "AMMETER") {
|
||||||
db.push({ ...item, children: [] });
|
db.push({...item, children: []});
|
||||||
} else if (item.deviceCategory === "PCS") {
|
} else if (item.deviceCategory === "PCS") {
|
||||||
// pcs
|
// pcs
|
||||||
pcs.push({ ...item, children: [] });
|
pcs.push({...item, children: []});
|
||||||
} else if (item.deviceCategory === "STACK") {
|
} else if (item.deviceCategory === "STACK") {
|
||||||
// bms
|
// bms
|
||||||
bms.push({ ...item, children: [] });
|
bms.push({...item, children: []});
|
||||||
} else if (item.deviceCategory === "COOLING") {
|
} else if (item.deviceCategory === "COOLING") {
|
||||||
// 液冷
|
// 液冷
|
||||||
lq.push({ ...item, children: [] });
|
lq.push({...item, children: []});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
bms.forEach((item, index) => {
|
||||||
|
if (item.parentId) {
|
||||||
|
pcs
|
||||||
|
.find((pcsItem) => pcsItem.deviceId === item.parentId)
|
||||||
|
.children.push(item);
|
||||||
|
} else {
|
||||||
|
bmsNoParent.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.pcs = pcs;
|
||||||
|
this.bms = bms;
|
||||||
|
this.lq = lq;
|
||||||
|
this.db = db;
|
||||||
|
this.bmsNoParent = bmsNoParent;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
});
|
});
|
||||||
bms.forEach((item, index) => {
|
|
||||||
if (item.parentId) {
|
|
||||||
pcs
|
|
||||||
.find((pcsItem) => pcsItem.deviceId === item.parentId)
|
|
||||||
.children.push(item);
|
|
||||||
} else {
|
|
||||||
bmsNoParent.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.pcs = pcs;
|
|
||||||
this.bms = bms;
|
|
||||||
this.lq = lq;
|
|
||||||
this.db = db;
|
|
||||||
this.bmsNoParent = bmsNoParent;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -329,6 +330,7 @@ $lineColor: #86bcc7;
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
//云 、计算机 、箭头
|
//云 、计算机 、箭头
|
||||||
.top {
|
.top {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@ -343,6 +345,7 @@ $lineColor: #86bcc7;
|
|||||||
//云 样式
|
//云 样式
|
||||||
.cloud-container {
|
.cloud-container {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
.cloud {
|
.cloud {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
@ -354,6 +357,7 @@ $lineColor: #86bcc7;
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cloud:before,
|
.cloud:before,
|
||||||
.cloud:after {
|
.cloud:after {
|
||||||
content: "";
|
content: "";
|
||||||
@ -363,20 +367,24 @@ $lineColor: #86bcc7;
|
|||||||
height: 30px;
|
height: 30px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cloud:before {
|
.cloud:before {
|
||||||
top: -9px;
|
top: -9px;
|
||||||
left: 8px;
|
left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cloud:after {
|
.cloud:after {
|
||||||
top: -6px;
|
top: -6px;
|
||||||
right: 9px;
|
right: 9px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//双箭头
|
//双箭头
|
||||||
.double-arrows {
|
.double-arrows {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.top-arrows,
|
.top-arrows,
|
||||||
.bottom-arrows {
|
.bottom-arrows {
|
||||||
height: 4px;
|
height: 4px;
|
||||||
@ -384,6 +392,7 @@ $lineColor: #86bcc7;
|
|||||||
background-color: #5ea9df;
|
background-color: #5ea9df;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -392,9 +401,11 @@ $lineColor: #86bcc7;
|
|||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-arrows {
|
.top-arrows {
|
||||||
vertical-align: super;
|
vertical-align: super;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-arrows::after {
|
.top-arrows::after {
|
||||||
top: -4px;
|
top: -4px;
|
||||||
border-bottom: 6px solid transparent;
|
border-bottom: 6px solid transparent;
|
||||||
@ -403,8 +414,10 @@ $lineColor: #86bcc7;
|
|||||||
border-top: 6px solid transparent;
|
border-top: 6px solid transparent;
|
||||||
left: -11px;
|
left: -11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-arrows {
|
.bottom-arrows {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
top: -4px;
|
top: -4px;
|
||||||
border-top: 6px solid transparent;
|
border-top: 6px solid transparent;
|
||||||
@ -425,6 +438,7 @@ $lineColor: #86bcc7;
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: auto;
|
height: auto;
|
||||||
@ -432,6 +446,7 @@ $lineColor: #86bcc7;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.outer-border {
|
.outer-border {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
@ -448,11 +463,11 @@ $lineColor: #86bcc7;
|
|||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to top,
|
to top,
|
||||||
transparent 0%,
|
transparent 0%,
|
||||||
transparent 50%,
|
transparent 50%,
|
||||||
#ccc 50%,
|
#ccc 50%,
|
||||||
#ccc 100%
|
#ccc 100%
|
||||||
);
|
);
|
||||||
background-size: 1px 10px;
|
background-size: 1px 10px;
|
||||||
background-repeat: repeat-y;
|
background-repeat: repeat-y;
|
||||||
@ -467,8 +482,10 @@ $lineColor: #86bcc7;
|
|||||||
color: #000;
|
color: #000;
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-lists {
|
.row-lists {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.row-items {
|
.row-items {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
@ -503,17 +520,21 @@ $lineColor: #86bcc7;
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background: #aaaaaa;
|
background: #aaaaaa;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
|
||||||
&.status-running {
|
&.status-running {
|
||||||
background: #00c69c;
|
background: #00c69c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图片+设备名称
|
// 图片+设备名称
|
||||||
.row-items-img {
|
.row-items-img {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
&.row-items-img-bms{
|
|
||||||
|
&.row-items-img-bms {
|
||||||
padding-top: 14px;
|
padding-top: 14px;
|
||||||
.num{
|
|
||||||
|
.num {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
right: -2px;
|
right: -2px;
|
||||||
@ -526,21 +547,26 @@ $lineColor: #86bcc7;
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: auto;
|
height: auto;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.img-lq {
|
&.img-lq {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.img-pcs {
|
&.img-pcs {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.img-db {
|
&.img-db {
|
||||||
width: 56px;
|
width: 56px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
@ -559,19 +585,24 @@ $lineColor: #86bcc7;
|
|||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: 30px;
|
margin-right: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-items {
|
.row-items {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parent-dash {
|
.parent-dash {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: inherit;
|
position: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
content: "";
|
content: "";
|
||||||
@ -585,16 +616,19 @@ $lineColor: #86bcc7;
|
|||||||
transform: scale(0.4, 1);
|
transform: scale(0.4, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.children-dash {
|
.children-dash {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
margin: -20px 0 0 26px;
|
margin: -20px 0 0 26px;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: inherit;
|
position: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
content: "";
|
content: "";
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container" v-loading="loading">
|
<div class="ems-dashboard-editor-container">
|
||||||
<zd-info></zd-info>
|
<zd-info></zd-info>
|
||||||
<div class="ems-content-container ems-content-container-padding">
|
<div class="ems-content-container ems-content-container-padding">
|
||||||
<div class="content-title">数据概览</div>
|
<div class="content-title">数据概览</div>
|
||||||
<el-row :gutter="15" style="background:#fff;margin:30px 0;">
|
<el-row :gutter="15" style="background:#fff;margin:30px 0;">
|
||||||
<el-col :xs="24" :sm="12" :lg="12">
|
<el-col :xs="24" :sm="12" :lg="12" v-loading="chartLoading.dlzbchart">
|
||||||
<dlzb-chart ref="dlzbchart"/>
|
<dlzb-chart ref="dlzbchart"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :lg="12">
|
<el-col :xs="24" :sm="12" :lg="12" v-loading="chartLoading.xtxlchart">
|
||||||
<xtxl-chart ref="xtxlchart"/>
|
<xtxl-chart ref="xtxlchart"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="15" style="background:#fff;margin:0;">
|
<el-row :gutter="15" style="background:#fff;margin:0;">
|
||||||
<el-col :xs="24" :sm="8" :lg="8">
|
<el-col :xs="24" :sm="8" :lg="8" v-loading="chartLoading.gjqsChart">
|
||||||
<gjqs-chart ref="gjqsChart"/>
|
<gjqs-chart ref="gjqsChart"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="8" :lg="8">
|
<el-col :xs="24" :sm="8" :lg="8" v-loading="chartLoading.sbgjzbChart">
|
||||||
<sbgjzb-chart ref="sbgjzbChart"/>
|
<sbgjzb-chart ref="sbgjzbChart"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="8" :lg="8">
|
<el-col :xs="24" :sm="8" :lg="8" v-loading="chartLoading.gjdjfbChart">
|
||||||
<gjdjfb-chart ref="gjdjfbChart"/>
|
<gjdjfb-chart ref="gjdjfbChart"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -47,24 +47,45 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
chartLoading: {
|
||||||
|
dlzbchart: false,
|
||||||
|
xtxlchart: false,
|
||||||
|
gjqsChart: false,
|
||||||
|
sbgjzbChart: false,
|
||||||
|
gjdjfbChart: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setChartLoading(loading) {
|
||||||
|
this.chartLoading = {
|
||||||
|
dlzbchart: loading,
|
||||||
|
xtxlchart: loading,
|
||||||
|
gjqsChart: loading,
|
||||||
|
sbgjzbChart: loading,
|
||||||
|
gjdjfbChart: loading
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideChartLoading(key) {
|
||||||
|
this.$set(this.chartLoading, key, false)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loading = true
|
this.setChartLoading(true)
|
||||||
dataList().then(response => {
|
dataList().then(response => {
|
||||||
const data = JSON.parse(JSON.stringify(response?.data || {}))
|
const data = JSON.parse(JSON.stringify(response?.data || {}))
|
||||||
this.$refs.dlzbchart.initChart(data?.elecDataList || [])
|
this.$refs.dlzbchart.initChart(data?.elecDataList || [])
|
||||||
|
this.hideChartLoading('dlzbchart')
|
||||||
this.$refs.xtxlchart.initChart(data?.sysEfficList || [])
|
this.$refs.xtxlchart.initChart(data?.sysEfficList || [])
|
||||||
|
this.hideChartLoading('xtxlchart')
|
||||||
this.$refs.gjqsChart.initChart(data?.alarmDataList || [])
|
this.$refs.gjqsChart.initChart(data?.alarmDataList || [])
|
||||||
|
this.hideChartLoading('gjqsChart')
|
||||||
this.$refs.sbgjzbChart.initChart(data?.deviceAlarmList || [])
|
this.$refs.sbgjzbChart.initChart(data?.deviceAlarmList || [])
|
||||||
|
this.hideChartLoading('sbgjzbChart')
|
||||||
this.$refs.gjdjfbChart.initChart(data?.alarmLevelList || [])
|
this.$refs.gjdjfbChart.initChart(data?.alarmLevelList || [])
|
||||||
|
this.hideChartLoading('gjdjfbChart')
|
||||||
}).finally(() => {
|
}).catch(() => {
|
||||||
this.loading = false
|
this.setChartLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
183
src/views/ems/site/dataCorrection/AddChargeDataCorrection.vue
Normal file
183
src/views/ems/site/dataCorrection/AddChargeDataCorrection.vue
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:show-close="false"
|
||||||
|
destroy-on-close
|
||||||
|
lock-scroll
|
||||||
|
append-to-body
|
||||||
|
width="760px"
|
||||||
|
class="ems-dialog"
|
||||||
|
:title="mode === 'add' ? '新增充放电收益修正' : '编辑充放电收益修正'"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
v-loading="loading"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
size="medium"
|
||||||
|
class="form-grid"
|
||||||
|
>
|
||||||
|
<el-form-item label="站点" prop="siteId">
|
||||||
|
<el-input v-model="formData.siteId" placeholder="请先在顶部选择站点" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据日期" prop="dateTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.dateTime"
|
||||||
|
type="date"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
placeholder="请选择数据日期"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="总充电量" prop="totalChargeData">
|
||||||
|
<el-input-number v-model="formData.totalChargeData" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="总放电量" prop="totalDischargeData">
|
||||||
|
<el-input-number v-model="formData.totalDischargeData" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="当日充电量" prop="chargeData">
|
||||||
|
<el-input-number v-model="formData.chargeData" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="当日放电量" prop="dischargeData">
|
||||||
|
<el-input-number v-model="formData.dischargeData" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="累计收益" prop="totalRevenue">
|
||||||
|
<el-input-number v-model="formData.totalRevenue" :controls="false" :min="-999999999" :max="999999999" :step="0.0001" :precision="4" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="当日收益" prop="dayRevenue">
|
||||||
|
<el-input-number v-model="formData.dayRevenue" :controls="false" :min="-999999999" :max="999999999" :step="0.0001" :precision="4" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注" prop="remark" class="full-row">
|
||||||
|
<el-input v-model="formData.remark" type="textarea" :rows="3" maxlength="300" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="saving" @click="saveDialog">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
addDailyChargeData,
|
||||||
|
getDailyChargeDataDetail,
|
||||||
|
updateDailyChargeData,
|
||||||
|
} from '@/api/ems/site'
|
||||||
|
|
||||||
|
const buildEmptyForm = () => ({
|
||||||
|
id: '',
|
||||||
|
siteId: '',
|
||||||
|
dateTime: '',
|
||||||
|
totalChargeData: null,
|
||||||
|
totalDischargeData: null,
|
||||||
|
chargeData: null,
|
||||||
|
dischargeData: null,
|
||||||
|
totalRevenue: null,
|
||||||
|
dayRevenue: null,
|
||||||
|
remark: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddChargeDataCorrection',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
mode: 'add',
|
||||||
|
loading: false,
|
||||||
|
saving: false,
|
||||||
|
formData: buildEmptyForm(),
|
||||||
|
rules: {
|
||||||
|
siteId: [{ required: true, message: '请先在顶部选择站点', trigger: 'blur' }],
|
||||||
|
dateTime: [{ required: true, message: '请选择数据日期', trigger: 'change' }],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getRouteSiteId() {
|
||||||
|
const siteId = this.$route?.query?.siteId
|
||||||
|
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
|
||||||
|
},
|
||||||
|
showDialog(id, siteId = '') {
|
||||||
|
this.dialogVisible = true
|
||||||
|
if (id) {
|
||||||
|
this.mode = 'edit'
|
||||||
|
this.formData.id = id
|
||||||
|
this.fetchDetail(id)
|
||||||
|
} else {
|
||||||
|
this.mode = 'add'
|
||||||
|
this.formData = buildEmptyForm()
|
||||||
|
this.formData.siteId = siteId || this.getRouteSiteId()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchDetail(id) {
|
||||||
|
this.loading = true
|
||||||
|
getDailyChargeDataDetail(id)
|
||||||
|
.then((res) => {
|
||||||
|
this.formData = Object.assign(buildEmptyForm(), res?.data || {})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveDialog() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.saving = true
|
||||||
|
const request = this.mode === 'add' ? addDailyChargeData : updateDailyChargeData
|
||||||
|
const payload = {
|
||||||
|
id: this.formData.id,
|
||||||
|
siteId: this.formData.siteId,
|
||||||
|
dateTime: this.formData.dateTime,
|
||||||
|
totalChargeData: this.formData.totalChargeData,
|
||||||
|
totalDischargeData: this.formData.totalDischargeData,
|
||||||
|
chargeData: this.formData.chargeData,
|
||||||
|
dischargeData: this.formData.dischargeData,
|
||||||
|
totalRevenue: this.formData.totalRevenue,
|
||||||
|
dayRevenue: this.formData.dayRevenue,
|
||||||
|
remark: this.formData.remark,
|
||||||
|
}
|
||||||
|
request(payload)
|
||||||
|
.then((res) => {
|
||||||
|
if (res?.code === 200) {
|
||||||
|
this.$emit('update')
|
||||||
|
this.closeDialog()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.saving = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeDialog() {
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.formData = buildEmptyForm()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.formRef && this.$refs.formRef.resetFields()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
column-gap: 16px;
|
||||||
|
|
||||||
|
.full-row {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
218
src/views/ems/site/dataCorrection/AddDataCorrection.vue
Normal file
218
src/views/ems/site/dataCorrection/AddDataCorrection.vue
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:show-close="false"
|
||||||
|
destroy-on-close
|
||||||
|
lock-scroll
|
||||||
|
append-to-body
|
||||||
|
width="760px"
|
||||||
|
class="ems-dialog"
|
||||||
|
:title="mode === 'add' ? '新增数据修正' : '编辑数据修正'"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
v-loading="loading"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
size="medium"
|
||||||
|
class="form-grid"
|
||||||
|
>
|
||||||
|
<el-form-item label="站点" prop="siteId">
|
||||||
|
<el-input v-model="formData.siteId" placeholder="请先在顶部选择站点" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据日期" prop="dataDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.dataDate"
|
||||||
|
type="date"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
placeholder="请选择数据日期"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据小时" prop="dataHour">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.dataHour"
|
||||||
|
:controls="false"
|
||||||
|
:min="0"
|
||||||
|
:max="23"
|
||||||
|
:step="1"
|
||||||
|
:precision="0"
|
||||||
|
placeholder="0-23"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="尖充电差值" prop="peakChargeDiff">
|
||||||
|
<el-input-number v-model="formData.peakChargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="尖放电差值" prop="peakDischargeDiff">
|
||||||
|
<el-input-number v-model="formData.peakDischargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="峰充电差值" prop="highChargeDiff">
|
||||||
|
<el-input-number v-model="formData.highChargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="峰放电差值" prop="highDischargeDiff">
|
||||||
|
<el-input-number v-model="formData.highDischargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="平充电差值" prop="flatChargeDiff">
|
||||||
|
<el-input-number v-model="formData.flatChargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="平放电差值" prop="flatDischargeDiff">
|
||||||
|
<el-input-number v-model="formData.flatDischargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="谷充电差值" prop="valleyChargeDiff">
|
||||||
|
<el-input-number v-model="formData.valleyChargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="谷放电差值" prop="valleyDischargeDiff">
|
||||||
|
<el-input-number v-model="formData.valleyDischargeDiff" :controls="false" :min="-999999999" :max="999999999" :step="0.001" :precision="3" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="计算时间" prop="calcTime" class="full-row">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.calcTime"
|
||||||
|
type="datetime"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
placeholder="请选择计算时间"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注" prop="remark" class="full-row">
|
||||||
|
<el-input v-model="formData.remark" type="textarea" :rows="3" maxlength="300" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="saving" @click="saveDialog">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
addDailyEnergyData,
|
||||||
|
getDailyEnergyDataDetail,
|
||||||
|
updateDailyEnergyData,
|
||||||
|
} from '@/api/ems/site'
|
||||||
|
|
||||||
|
const buildEmptyForm = () => ({
|
||||||
|
id: '',
|
||||||
|
siteId: '',
|
||||||
|
dataDate: '',
|
||||||
|
dataHour: null,
|
||||||
|
peakChargeDiff: null,
|
||||||
|
peakDischargeDiff: null,
|
||||||
|
highChargeDiff: null,
|
||||||
|
highDischargeDiff: null,
|
||||||
|
flatChargeDiff: null,
|
||||||
|
flatDischargeDiff: null,
|
||||||
|
valleyChargeDiff: null,
|
||||||
|
valleyDischargeDiff: null,
|
||||||
|
calcTime: '',
|
||||||
|
remark: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddDataCorrection',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
mode: 'add',
|
||||||
|
loading: false,
|
||||||
|
saving: false,
|
||||||
|
formData: buildEmptyForm(),
|
||||||
|
rules: {
|
||||||
|
siteId: [{ required: true, message: '请先在顶部选择站点', trigger: 'blur' }],
|
||||||
|
dataDate: [{ required: true, message: '请选择数据日期', trigger: 'change' }],
|
||||||
|
dataHour: [
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === '' || value === null || value === undefined) {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (Number.isInteger(value) && value >= 0 && value <= 23) {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(new Error('数据小时需为 0-23 的整数'))
|
||||||
|
},
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getRouteSiteId() {
|
||||||
|
const siteId = this.$route?.query?.siteId
|
||||||
|
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
|
||||||
|
},
|
||||||
|
showDialog(id, siteId = '') {
|
||||||
|
this.dialogVisible = true
|
||||||
|
if (id) {
|
||||||
|
this.mode = 'edit'
|
||||||
|
this.formData.id = id
|
||||||
|
this.fetchDetail(id)
|
||||||
|
} else {
|
||||||
|
this.mode = 'add'
|
||||||
|
this.formData = buildEmptyForm()
|
||||||
|
this.formData.siteId = siteId || this.getRouteSiteId()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchDetail(id) {
|
||||||
|
this.loading = true
|
||||||
|
getDailyEnergyDataDetail(id)
|
||||||
|
.then((res) => {
|
||||||
|
this.formData = Object.assign(buildEmptyForm(), res?.data || {})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveDialog() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
this.saving = true
|
||||||
|
const request = this.mode === 'add' ? addDailyEnergyData : updateDailyEnergyData
|
||||||
|
request(this.formData)
|
||||||
|
.then((res) => {
|
||||||
|
if (res?.code === 200) {
|
||||||
|
this.$emit('update')
|
||||||
|
this.closeDialog()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.saving = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeDialog() {
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.formData = buildEmptyForm()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.formRef && this.$refs.formRef.resetFields()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
column-gap: 16px;
|
||||||
|
|
||||||
|
.full-row {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
187
src/views/ems/site/dataCorrection/DailyChargeDataTab.vue
Normal file
187
src/views/ems/site/dataCorrection/DailyChargeDataTab.vue
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<div style="background-color: #ffffff" v-loading="loading">
|
||||||
|
<el-form :inline="true" class="select-container" @submit.native.prevent>
|
||||||
|
<el-form-item label="数据日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.dateTime"
|
||||||
|
type="date"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择日期"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button native-type="button" type="primary" @click="onSearch">搜索</el-button>
|
||||||
|
<el-button native-type="button" @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-button type="primary" @click="openDialog('')">新增</el-button>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
class="common-table"
|
||||||
|
max-height="620px"
|
||||||
|
stripe
|
||||||
|
style="width: 100%; margin-top: 20px"
|
||||||
|
>
|
||||||
|
<el-table-column label="站点" prop="siteId" min-width="120" />
|
||||||
|
<el-table-column label="数据日期" prop="dateTime" width="120" />
|
||||||
|
<el-table-column label="总充电量" prop="totalChargeData" min-width="110" />
|
||||||
|
<el-table-column label="总放电量" prop="totalDischargeData" min-width="110" />
|
||||||
|
<el-table-column label="当日充电量" prop="chargeData" min-width="110" />
|
||||||
|
<el-table-column label="当日放电量" prop="dischargeData" min-width="110" />
|
||||||
|
<el-table-column label="累计收益" prop="totalRevenue" min-width="110" />
|
||||||
|
<el-table-column label="当日收益" prop="dayRevenue" min-width="110" />
|
||||||
|
<el-table-column label="备注" prop="remark" min-width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column fixed="right" label="操作" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="warning" @click="openDialog(scope.row.id)">编辑</el-button>
|
||||||
|
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
v-show="tableData.length > 0"
|
||||||
|
background
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pageNum"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 30, 40]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="totalSize"
|
||||||
|
style="margin-top: 15px; text-align: center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<add-charge-data-correction ref="addChargeDataCorrection" @update="getData" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
deleteDailyChargeData,
|
||||||
|
getDailyChargeDataList,
|
||||||
|
} from '@/api/ems/site'
|
||||||
|
import AddChargeDataCorrection from './AddChargeDataCorrection.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DailyChargeDataTab',
|
||||||
|
components: { AddChargeDataCorrection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
form: {
|
||||||
|
siteId: '',
|
||||||
|
dateTime: '',
|
||||||
|
},
|
||||||
|
tableData: [],
|
||||||
|
pageSize: 10,
|
||||||
|
pageNum: 1,
|
||||||
|
totalSize: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query.siteId'(newSiteId) {
|
||||||
|
const normalizedSiteId = this.normalizeSiteId(newSiteId)
|
||||||
|
if (normalizedSiteId === this.form.siteId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.form.siteId = normalizedSiteId
|
||||||
|
this.onSearch()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.form.siteId = this.normalizeSiteId(this.$route.query.siteId)
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
normalizeSiteId(siteId) {
|
||||||
|
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.pageNum = 1
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.pageNum = val
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
onSearch() {
|
||||||
|
this.pageNum = 1
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
onReset() {
|
||||||
|
this.form = {
|
||||||
|
siteId: this.form.siteId,
|
||||||
|
dateTime: '',
|
||||||
|
}
|
||||||
|
this.pageNum = 1
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
getData() {
|
||||||
|
if (!this.form.siteId) {
|
||||||
|
this.tableData = []
|
||||||
|
this.totalSize = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
const params = {
|
||||||
|
pageNum: this.pageNum,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
siteId: this.form.siteId,
|
||||||
|
}
|
||||||
|
if (this.form.dateTime) {
|
||||||
|
params.dateTime = this.form.dateTime
|
||||||
|
}
|
||||||
|
getDailyChargeDataList(params)
|
||||||
|
.then((res) => {
|
||||||
|
this.tableData = res?.rows || []
|
||||||
|
this.totalSize = res?.total || 0
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openDialog(id) {
|
||||||
|
this.$refs.addChargeDataCorrection.showDialog(id, this.form.siteId)
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认要删除该条数据吗?', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
showClose: false,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
type: 'warning',
|
||||||
|
beforeClose: (action, instance, done) => {
|
||||||
|
if (action === 'confirm') {
|
||||||
|
instance.confirmButtonLoading = true
|
||||||
|
deleteDailyChargeData(row.id)
|
||||||
|
.then((res) => {
|
||||||
|
if (res?.code === 200) {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
instance.confirmButtonLoading = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success('删除成功!')
|
||||||
|
this.getData()
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
191
src/views/ems/site/dataCorrection/DailyEnergyDataTab.vue
Normal file
191
src/views/ems/site/dataCorrection/DailyEnergyDataTab.vue
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
<template>
|
||||||
|
<div style="background-color: #ffffff" v-loading="loading">
|
||||||
|
<el-form :inline="true" class="select-container" @submit.native.prevent>
|
||||||
|
<el-form-item label="数据日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.dataDate"
|
||||||
|
type="date"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择日期"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button native-type="button" type="primary" @click="onSearch">搜索</el-button>
|
||||||
|
<el-button native-type="button" @click="onReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-button type="primary" @click="openDialog('')">新增</el-button>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
class="common-table"
|
||||||
|
max-height="620px"
|
||||||
|
stripe
|
||||||
|
style="width: 100%; margin-top: 20px"
|
||||||
|
>
|
||||||
|
<el-table-column label="站点" prop="siteId" width="130" />
|
||||||
|
<el-table-column label="数据日期" prop="dataDate" width="120" />
|
||||||
|
<el-table-column label="data_hour" prop="dataHour" width="100" />
|
||||||
|
<el-table-column label="尖充" prop="peakChargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="尖放" prop="peakDischargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="峰充" prop="highChargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="峰放" prop="highDischargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="平充" prop="flatChargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="平放" prop="flatDischargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="谷充" prop="valleyChargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="谷放" prop="valleyDischargeDiff" min-width="90" />
|
||||||
|
<el-table-column label="计算时间" prop="calcTime" min-width="170" />
|
||||||
|
<el-table-column label="备注" prop="remark" min-width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column fixed="right" label="操作" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="warning" @click="openDialog(scope.row.id)">编辑</el-button>
|
||||||
|
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
v-show="tableData.length > 0"
|
||||||
|
background
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pageNum"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 30, 40]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="totalSize"
|
||||||
|
style="margin-top: 15px; text-align: center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<add-data-correction ref="addDataCorrection" @update="getData" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
deleteDailyEnergyData,
|
||||||
|
getDailyEnergyDataList,
|
||||||
|
} from '@/api/ems/site'
|
||||||
|
import AddDataCorrection from './AddDataCorrection.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DailyEnergyDataTab',
|
||||||
|
components: { AddDataCorrection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
form: {
|
||||||
|
siteId: '',
|
||||||
|
dataDate: '',
|
||||||
|
},
|
||||||
|
tableData: [],
|
||||||
|
pageSize: 10,
|
||||||
|
pageNum: 1,
|
||||||
|
totalSize: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query.siteId'(newSiteId) {
|
||||||
|
const normalizedSiteId = this.normalizeSiteId(newSiteId)
|
||||||
|
if (normalizedSiteId === this.form.siteId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.form.siteId = normalizedSiteId
|
||||||
|
this.onSearch()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.form.siteId = this.normalizeSiteId(this.$route.query.siteId)
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
normalizeSiteId(siteId) {
|
||||||
|
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.pageNum = 1
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.pageNum = val
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
onSearch() {
|
||||||
|
this.pageNum = 1
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
onReset() {
|
||||||
|
this.form = {
|
||||||
|
siteId: this.form.siteId,
|
||||||
|
dataDate: '',
|
||||||
|
}
|
||||||
|
this.pageNum = 1
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
getData() {
|
||||||
|
if (!this.form.siteId) {
|
||||||
|
this.tableData = []
|
||||||
|
this.totalSize = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
const params = {
|
||||||
|
pageNum: this.pageNum,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
siteId: this.form.siteId,
|
||||||
|
}
|
||||||
|
if (this.form.dataDate) {
|
||||||
|
params.dataDate = this.form.dataDate
|
||||||
|
}
|
||||||
|
getDailyEnergyDataList(params)
|
||||||
|
.then((res) => {
|
||||||
|
this.tableData = res?.rows || []
|
||||||
|
this.totalSize = res?.total || 0
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openDialog(id) {
|
||||||
|
this.$refs.addDataCorrection.showDialog(id, this.form.siteId)
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认要删除该条数据吗?', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
showClose: false,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
type: 'warning',
|
||||||
|
beforeClose: (action, instance, done) => {
|
||||||
|
if (action === 'confirm') {
|
||||||
|
instance.confirmButtonLoading = true
|
||||||
|
deleteDailyEnergyData(row.id)
|
||||||
|
.then((res) => {
|
||||||
|
if (res?.code === 200) {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
instance.confirmButtonLoading = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success('删除成功!')
|
||||||
|
this.getData()
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
30
src/views/ems/site/dataCorrection/index.vue
Normal file
30
src/views/ems/site/dataCorrection/index.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ems-dashboard-editor-container" style="background-color: #ffffff">
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="充放电量修正" name="energy" lazy>
|
||||||
|
<daily-energy-data-tab />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="充放电收益修正" name="charge" lazy>
|
||||||
|
<daily-charge-data-tab />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DailyChargeDataTab from './DailyChargeDataTab.vue'
|
||||||
|
import DailyEnergyDataTab from './DailyEnergyDataTab.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DataCorrection',
|
||||||
|
components: { DailyEnergyDataTab, DailyChargeDataTab },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeTab: 'energy',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
@ -2,9 +2,7 @@
|
|||||||
<el-dialog :visible.sync="dialogTableVisible" :close-on-press-escape="false" :close-on-click-modal="false" :show-close="false" destroy-on-close lock-scroll append-to-body width="400px" class="ems-dialog" :title="mode === 'add'?'新增配置':`编辑配置` " >
|
<el-dialog :visible.sync="dialogTableVisible" :close-on-press-escape="false" :close-on-click-modal="false" :show-close="false" destroy-on-close lock-scroll append-to-body width="400px" class="ems-dialog" :title="mode === 'add'?'新增配置':`编辑配置` " >
|
||||||
<el-form v-loading="loading>0" ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="140px">
|
<el-form v-loading="loading>0" ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="140px">
|
||||||
<el-form-item label="站点" prop="siteId">
|
<el-form-item label="站点" prop="siteId">
|
||||||
<el-select v-model="formData.siteId" :disabled="mode === 'edit'" placeholder="请选择站点" :loading="searchLoading" loading-text="正在加载数据">
|
<el-input v-model="formData.siteId" placeholder="请先在顶部选择站点" disabled />
|
||||||
<el-option v-for="(item,index) in siteList" :key="index+'zdxeSelect'" :label="item.siteName" :value="item.siteId" ></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="消息等级" prop="qos">
|
<el-form-item label="消息等级" prop="qos">
|
||||||
<el-select v-model="formData.qos" placeholder="请选择消息等级">
|
<el-select v-model="formData.qos" placeholder="请选择消息等级">
|
||||||
@ -30,13 +28,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import {editMqtt,addMqtt,getMqttDetail} from "@/api/ems/site";
|
import {editMqtt,addMqtt,getMqttDetail} from "@/api/ems/site";
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:0,
|
loading:0,
|
||||||
siteList:[],
|
|
||||||
searchLoading:false,
|
|
||||||
dialogTableVisible:false,
|
dialogTableVisible:false,
|
||||||
mode:'',
|
mode:'',
|
||||||
formData: {
|
formData: {
|
||||||
@ -48,7 +43,7 @@ export default {
|
|||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
siteId:[
|
siteId:[
|
||||||
{ required: true, message: '请选择站点', trigger: 'blur'},
|
{ required: true, message: '请先在顶部选择站点', trigger: 'blur'},
|
||||||
],
|
],
|
||||||
qos:[
|
qos:[
|
||||||
{ required: true, message: '请选择消息等级', trigger: 'blur'},
|
{ required: true, message: '请选择消息等级', trigger: 'blur'},
|
||||||
@ -63,23 +58,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showDialog(id){
|
getRouteSiteId() {
|
||||||
|
const siteId = this.$route?.query?.siteId
|
||||||
|
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
|
||||||
|
},
|
||||||
|
showDialog(id, siteId = ''){
|
||||||
this.dialogTableVisible = true
|
this.dialogTableVisible = true
|
||||||
this.getZdList()
|
|
||||||
if(id){
|
if(id){
|
||||||
this.mode = 'edit'
|
this.mode = 'edit'
|
||||||
this.formData.id = id
|
this.formData.id = id
|
||||||
this.getDetail(id)
|
this.getDetail(id)
|
||||||
}else{
|
}else{
|
||||||
this.mode = 'add'
|
this.mode = 'add'
|
||||||
|
this.formData.siteId = siteId || this.getRouteSiteId()
|
||||||
}
|
}
|
||||||
},
|
|
||||||
//获取站点列表
|
|
||||||
getZdList(){
|
|
||||||
this.searchLoading=true
|
|
||||||
getAllSites().then(response => {
|
|
||||||
this.siteList = response?.data || []
|
|
||||||
}).finally(() => {this.searchLoading=false})
|
|
||||||
},
|
},
|
||||||
getDetail(id){
|
getDetail(id){
|
||||||
getMqttDetail(id).then(response => {
|
getMqttDetail(id).then(response => {
|
||||||
@ -132,6 +124,8 @@ export default {
|
|||||||
// 清空所有数据
|
// 清空所有数据
|
||||||
this.formData= {
|
this.formData= {
|
||||||
id:'',//设备唯一标识
|
id:'',//设备唯一标识
|
||||||
|
siteId:'',
|
||||||
|
qos:'',
|
||||||
mqttTopic:'',
|
mqttTopic:'',
|
||||||
topicName:''
|
topicName:''
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
||||||
<el-form :inline="true" class="select-container">
|
<el-form :inline="true" class="select-container">
|
||||||
<el-form-item label="站点选择">
|
|
||||||
<el-select v-model="form.siteId" placeholder="请选择换电站名称" :loading="searchLoading" loading-text="正在加载数据" clearable>
|
|
||||||
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="订阅topic">
|
<el-form-item label="订阅topic">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.mqttTopic"
|
v-model="form.mqttTopic"
|
||||||
@ -90,12 +85,21 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {deleteMqtt,getMqttList} from '@/api/ems/site'
|
import {deleteMqtt,getMqttList} from '@/api/ems/site'
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
|
||||||
import AddMqtt from './AddMqtt.vue'
|
import AddMqtt from './AddMqtt.vue'
|
||||||
export default {
|
export default {
|
||||||
name: "Mqtt",
|
name: "Mqtt",
|
||||||
components: {AddMqtt},
|
components: {AddMqtt},
|
||||||
computed: { },
|
computed: { },
|
||||||
|
watch: {
|
||||||
|
'$route.query.siteId'(newSiteId) {
|
||||||
|
const normalizedSiteId = this.hasValidSiteId(newSiteId) ? String(newSiteId).trim() : ''
|
||||||
|
if (normalizedSiteId === this.form.siteId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.form.siteId = normalizedSiteId
|
||||||
|
this.onSearch()
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form:{
|
form:{
|
||||||
@ -103,8 +107,6 @@ export default {
|
|||||||
topicName:'',
|
topicName:'',
|
||||||
mqttTopic:''
|
mqttTopic:''
|
||||||
},
|
},
|
||||||
siteList:[],
|
|
||||||
searchLoading:false,
|
|
||||||
loading:false,
|
loading:false,
|
||||||
tableData:[],
|
tableData:[],
|
||||||
pageSize:10,//分页栏当前每个数据总数
|
pageSize:10,//分页栏当前每个数据总数
|
||||||
@ -113,6 +115,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
hasValidSiteId(siteId) {
|
||||||
|
return !!(siteId !== undefined && siteId !== null && String(siteId).trim())
|
||||||
|
},
|
||||||
// 分页
|
// 分页
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
@ -133,22 +138,19 @@ export default {
|
|||||||
},
|
},
|
||||||
onReset(){
|
onReset(){
|
||||||
this.form={
|
this.form={
|
||||||
siteId:'',
|
siteId:this.form.siteId,
|
||||||
topicName:'',
|
topicName:'',
|
||||||
mqttTopic:''
|
mqttTopic:''
|
||||||
}
|
}
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
this.pageNum =1//每次搜索从1开始搜索
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
//获取站点列表
|
|
||||||
getZdList(){
|
|
||||||
this.searchLoading=true
|
|
||||||
return getAllSites().then(response => {
|
|
||||||
this.siteList = response?.data || []
|
|
||||||
// if( this.siteList.length>0 ) this.siteId = this.siteList[0].siteId
|
|
||||||
}).finally(() => {this.searchLoading=false})
|
|
||||||
},
|
|
||||||
getData(){
|
getData(){
|
||||||
|
if (!this.form.siteId) {
|
||||||
|
this.tableData = []
|
||||||
|
this.totalSize = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
this.loading=true;
|
this.loading=true;
|
||||||
const {mqttTopic,topicName,siteId} = this.form;
|
const {mqttTopic,topicName,siteId} = this.form;
|
||||||
const {pageNum,pageSize} = this;
|
const {pageNum,pageSize} = this;
|
||||||
@ -158,7 +160,7 @@ export default {
|
|||||||
}).finally(() => {this.loading=false})
|
}).finally(() => {this.loading=false})
|
||||||
},
|
},
|
||||||
addPowerConfig(id=''){
|
addPowerConfig(id=''){
|
||||||
this.$refs.addMqtt.showDialog(id);
|
this.$refs.addMqtt.showDialog(id, this.form.siteId);
|
||||||
},
|
},
|
||||||
deleteMqtt(row){
|
deleteMqtt(row){
|
||||||
this.$confirm(`确认要删除该配置吗?`, {
|
this.$confirm(`确认要删除该配置吗?`, {
|
||||||
@ -193,8 +195,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loading=true
|
this.form.siteId = this.hasValidSiteId(this.$route.query.siteId) ? String(this.$route.query.siteId).trim() : ''
|
||||||
this.getZdList()
|
|
||||||
this.getData()
|
this.getData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1800
src/views/ems/site/pointConfig/index.vue
Normal file
1800
src/views/ems/site/pointConfig/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,7 @@
|
|||||||
<div class="items-container">
|
<div class="items-container">
|
||||||
<div class="item-title">站点:</div>
|
<div class="item-title">站点:</div>
|
||||||
<div class="item-content">
|
<div class="item-content">
|
||||||
<el-select v-model="siteId" :disabled="mode === 'edit'" placeholder="请选择站点" :loading="searchLoading" loading-text="正在加载数据">
|
<el-input v-model="siteId" placeholder="请先在顶部选择站点" disabled />
|
||||||
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="items-container">
|
<div class="items-container">
|
||||||
@ -103,15 +101,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import {addPriceConfig,editPriceConfig,detailPriceConfig} from '@/api/ems/powerTariff'
|
import {addPriceConfig,editPriceConfig,detailPriceConfig} from '@/api/ems/powerTariff'
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
mode:'',
|
mode:'',
|
||||||
id:'',
|
id:'',
|
||||||
searchLoading:false,
|
|
||||||
siteId:'',
|
siteId:'',
|
||||||
siteList:[],
|
|
||||||
powerDate:'',//时间
|
powerDate:'',//时间
|
||||||
//尖-peak,峰-high,平-flat,谷=valley
|
//尖-peak,峰-high,平-flat,谷=valley
|
||||||
priceTypeOptions:[{
|
priceTypeOptions:[{
|
||||||
@ -137,6 +132,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getRouteSiteId() {
|
||||||
|
const siteId = this.$route?.query?.siteId
|
||||||
|
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
|
||||||
|
},
|
||||||
addRow(){
|
addRow(){
|
||||||
this.hoursOptions.push({
|
this.hoursOptions.push({
|
||||||
startTime:'',
|
startTime:'',
|
||||||
@ -147,15 +146,7 @@ export default {
|
|||||||
deleteRow(index){
|
deleteRow(index){
|
||||||
this.hoursOptions.splice(index,1)
|
this.hoursOptions.splice(index,1)
|
||||||
},
|
},
|
||||||
//获取站点列表
|
showDialog(id, siteId = ''){
|
||||||
getZdList(){
|
|
||||||
this.searchLoading=true
|
|
||||||
getAllSites().then(response => {
|
|
||||||
this.siteList = response?.data || []
|
|
||||||
}).finally(() => {this.searchLoading=false})
|
|
||||||
},
|
|
||||||
showDialog(id){
|
|
||||||
this.getZdList()
|
|
||||||
this.id = id
|
this.id = id
|
||||||
if(id) {
|
if(id) {
|
||||||
this.mode='edit'
|
this.mode='edit'
|
||||||
@ -172,11 +163,12 @@ export default {
|
|||||||
}).finally(()=>this.loading = false)
|
}).finally(()=>this.loading = false)
|
||||||
}else {
|
}else {
|
||||||
this.mode='add'
|
this.mode='add'
|
||||||
|
this.siteId = siteId || this.getRouteSiteId()
|
||||||
}
|
}
|
||||||
this.dialogTableVisible=true
|
this.dialogTableVisible=true
|
||||||
},
|
},
|
||||||
saveDialog() {
|
saveDialog() {
|
||||||
if(this.siteId === '') return this.$message.error('请选择站点')
|
if(this.siteId === '') return this.$message.error('请先在顶部选择站点')
|
||||||
if(this.powerDate === '') return this.$message.error('请选择时间')
|
if(this.powerDate === '') return this.$message.error('请选择时间')
|
||||||
let priceArr=[]
|
let priceArr=[]
|
||||||
this.priceTypeOptions.forEach(item=>{
|
this.priceTypeOptions.forEach(item=>{
|
||||||
@ -244,8 +236,6 @@ export default {
|
|||||||
this.mode=''
|
this.mode=''
|
||||||
this.id=''
|
this.id=''
|
||||||
this.siteId=''
|
this.siteId=''
|
||||||
this.siteList=[]
|
|
||||||
this.searchLoading=false
|
|
||||||
this.powerDate=''
|
this.powerDate=''
|
||||||
this.hoursOptions=[]
|
this.hoursOptions=[]
|
||||||
this.priceTypeOptions.forEach(item=>{
|
this.priceTypeOptions.forEach(item=>{
|
||||||
@ -303,4 +293,4 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
||||||
<el-form :inline="true" class="select-container">
|
<el-form :inline="true" class="select-container">
|
||||||
<el-form-item label="站点选择">
|
|
||||||
<el-select v-model="siteId" placeholder="请选择换电站名称" :loading="searchLoading" loading-text="正在加载数据" @change="onSearch" clearable>
|
|
||||||
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="年份选择">
|
<el-form-item label="年份选择">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="defaultYear"
|
v-model="defaultYear"
|
||||||
@ -33,7 +28,7 @@
|
|||||||
:key="item.id"
|
:key="item.id"
|
||||||
>
|
>
|
||||||
<div slot="header" class="time-range-header">
|
<div slot="header" class="time-range-header">
|
||||||
<span class="card-title">{{siteList.find(i=>i.siteId===item.siteId).siteName || item.siteId || ''}}-{{item.month}}月电价时段划分</span>
|
<span class="card-title">{{item.siteId || ''}}-{{item.month}}月电价时段划分</span>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" size="mini" @click="addPowerConfig(item.id)">编辑</el-button>
|
<el-button type="primary" size="mini" @click="addPowerConfig(item.id)">编辑</el-button>
|
||||||
<el-button type="warning" size="mini" @click="deletePowerConfig(item)">删除</el-button>
|
<el-button type="warning" size="mini" @click="deletePowerConfig(item)">删除</el-button>
|
||||||
@ -61,21 +56,28 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {energyPriceConfig,listPriceConfig} from '@/api/ems/powerTariff'
|
import {energyPriceConfig,listPriceConfig} from '@/api/ems/powerTariff'
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
|
||||||
import AddPowerTariff from './AddPowerTariff.vue'
|
import AddPowerTariff from './AddPowerTariff.vue'
|
||||||
import DateTimeSelect from "@/views/ems/search/DateTimeSelect.vue";
|
import DateTimeSelect from "@/views/ems/search/DateTimeSelect.vue";
|
||||||
export default {
|
export default {
|
||||||
name: "PowerTariff",
|
name: "PowerTariff",
|
||||||
components: {DateTimeSelect, AddPowerTariff},
|
components: {DateTimeSelect, AddPowerTariff},
|
||||||
computed: { },
|
computed: { },
|
||||||
|
watch: {
|
||||||
|
'$route.query.siteId'(newSiteId) {
|
||||||
|
const normalizedSiteId = this.hasValidSiteId(newSiteId) ? String(newSiteId).trim() : ''
|
||||||
|
if (normalizedSiteId === this.siteId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.siteId = normalizedSiteId
|
||||||
|
this.onSearch()
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading:false,
|
||||||
pageNum:1,
|
pageNum:1,
|
||||||
pageSize:40,
|
pageSize:40,
|
||||||
searchLoading:false,
|
|
||||||
siteId:'',
|
siteId:'',
|
||||||
siteList:[],
|
|
||||||
tableData:[],
|
tableData:[],
|
||||||
tableTotal:0,
|
tableTotal:0,
|
||||||
defaultYear:'',
|
defaultYear:'',
|
||||||
@ -93,6 +95,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
hasValidSiteId(siteId) {
|
||||||
|
return !!(siteId !== undefined && siteId !== null && String(siteId).trim())
|
||||||
|
},
|
||||||
resetTableData(){
|
resetTableData(){
|
||||||
this.tableData=[]
|
this.tableData=[]
|
||||||
this.tableTotal=0
|
this.tableTotal=0
|
||||||
@ -102,19 +107,16 @@ export default {
|
|||||||
onSearch(){
|
onSearch(){
|
||||||
this.getData(true)
|
this.getData(true)
|
||||||
},
|
},
|
||||||
//获取站点列表
|
|
||||||
getZdList(){
|
|
||||||
this.searchLoading=true
|
|
||||||
return getAllSites().then(response => {
|
|
||||||
this.siteList = response?.data || []
|
|
||||||
if( this.siteList.length>0 ) this.siteId = this.siteList[0].siteId
|
|
||||||
}).finally(() => {this.searchLoading=false})
|
|
||||||
},
|
|
||||||
changeDefaultYear(){
|
changeDefaultYear(){
|
||||||
this.getData(true)
|
this.getData(true)
|
||||||
},
|
},
|
||||||
getData(reset=false){
|
getData(reset=false){
|
||||||
reset && this.resetTableData()
|
reset && this.resetTableData()
|
||||||
|
if (!this.siteId) {
|
||||||
|
this.tableData = []
|
||||||
|
this.tableTotal = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
if(!reset && this.tableData.length>=this.tableTotal) return
|
if(!reset && this.tableData.length>=this.tableTotal) return
|
||||||
this.loading=true;
|
this.loading=true;
|
||||||
const date = new Date(this.defaultYear).getFullYear()
|
const date = new Date(this.defaultYear).getFullYear()
|
||||||
@ -127,7 +129,7 @@ export default {
|
|||||||
}).finally(() => {this.loading=false})
|
}).finally(() => {this.loading=false})
|
||||||
},
|
},
|
||||||
addPowerConfig(id=''){
|
addPowerConfig(id=''){
|
||||||
this.$refs.addPowerTariff.showDialog(id);
|
this.$refs.addPowerTariff.showDialog(id, this.siteId);
|
||||||
},
|
},
|
||||||
deletePowerConfig(row){
|
deletePowerConfig(row){
|
||||||
this.$confirm(`确认要删除${row.month}月的电价配置吗?`, {
|
this.$confirm(`确认要删除${row.month}月的电价配置吗?`, {
|
||||||
@ -163,10 +165,8 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.defaultYear = new Date()
|
this.defaultYear = new Date()
|
||||||
this.loading=true
|
this.siteId = this.hasValidSiteId(this.$route.query.siteId) ? String(this.$route.query.siteId).trim() : ''
|
||||||
this.getZdList().then(()=>{
|
this.getData(true)
|
||||||
this.getData(true)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,766 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-dialog
|
|
||||||
v-loading="loading"
|
|
||||||
width="90%"
|
|
||||||
:visible.sync="dialogTableVisible"
|
|
||||||
class="ems-dialog"
|
|
||||||
title="保护方案"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
:show-close="false"
|
|
||||||
>
|
|
||||||
<el-form
|
|
||||||
v-loading="loading > 0"
|
|
||||||
ref="addTempForm"
|
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
size="medium"
|
|
||||||
label-width="140px"
|
|
||||||
>
|
|
||||||
<el-form-item label="站点" prop="siteId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.siteId"
|
|
||||||
placeholder="请选择"
|
|
||||||
:style="{ width: '50%' }"
|
|
||||||
@change="changeType"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
:label="item.siteName"
|
|
||||||
:value="item.siteId"
|
|
||||||
v-for="(item, index) in siteList"
|
|
||||||
:key="index + 'siteOptions'"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="设备保护名称" prop="faultName">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.faultName"
|
|
||||||
placeholder="请输入"
|
|
||||||
clearable
|
|
||||||
:style="{ width: '50%' }"
|
|
||||||
>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="处理方案描述" prop="description">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.description"
|
|
||||||
type="textarea"
|
|
||||||
:rows="2"
|
|
||||||
placeholder="请输入"
|
|
||||||
clearable
|
|
||||||
:style="{ width: '50%' }"
|
|
||||||
>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="是否告警" prop="isAlert">
|
|
||||||
<el-checkbox
|
|
||||||
v-model="formData.isAlert"
|
|
||||||
:true-label="1"
|
|
||||||
:false-label="0"
|
|
||||||
></el-checkbox>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="告警等级" prop="faultLevel">
|
|
||||||
<el-radio-group v-model="formData.faultLevel" :style="{ width: '50%' }" :disabled="mode === 'edit'">
|
|
||||||
<el-radio :label="1">等级1</el-radio>
|
|
||||||
<el-radio :label="2">等级2</el-radio>
|
|
||||||
<el-radio :label="3">等级3</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<div class="items-container">
|
|
||||||
<div class="item-title">
|
|
||||||
保护前提:
|
|
||||||
<div style="display: inline-block; margin-left: 20px">
|
|
||||||
<el-form-item label="延时" prop="faultDelaySeconds">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.faultDelaySeconds"
|
|
||||||
placeholder="请输入"
|
|
||||||
clearable
|
|
||||||
:style="{ width: '200px', display: 'inline-block' }"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button
|
|
||||||
@click.native.prevent="addRow('protectionSettings')"
|
|
||||||
block
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
style="margin-bottom: 20px"
|
|
||||||
>
|
|
||||||
新增保护前提
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="item-content">
|
|
||||||
<div class="time-lists-container">
|
|
||||||
<div class="time-lists time-lists-title">
|
|
||||||
<div>设备类型</div>
|
|
||||||
<div>点位</div>
|
|
||||||
<div>故障值比较符号</div>
|
|
||||||
<div>故障值</div>
|
|
||||||
<div>释放值比较符号</div>
|
|
||||||
<div>释放值</div>
|
|
||||||
<div>关系</div>
|
|
||||||
<div>操作</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="time-lists"
|
|
||||||
v-for="(item, index) in protectionSettings"
|
|
||||||
:key="'protectionSettings' + index"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<el-cascader
|
|
||||||
v-model="item.deviceId"
|
|
||||||
:options="childOptions"
|
|
||||||
:show-all-levels="false"
|
|
||||||
@change="(v)=>handleChange(v,'protectionSettings',index)"
|
|
||||||
></el-cascader>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-autocomplete
|
|
||||||
v-model="item.point"
|
|
||||||
placeholder="请输入点位"
|
|
||||||
clearable
|
|
||||||
:fetch-suggestions="
|
|
||||||
(q, c) =>
|
|
||||||
querySearchAsync(q, c, index, 'protectionSettings')
|
|
||||||
"
|
|
||||||
@select="(v) => handleSelect(v, index, 'protectionSettings')"
|
|
||||||
></el-autocomplete>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<el-select v-model="item.faultOperator" placeholder="请选择">
|
|
||||||
<el-option
|
|
||||||
v-for="(value, key) in comparisonOperatorOptions"
|
|
||||||
:key="key + 'faultOperator'"
|
|
||||||
:label="key"
|
|
||||||
:value="value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-input placeholder="请输入故障值" v-model="item.faultValue">
|
|
||||||
</el-input>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-select v-model="item.releaseOperator" placeholder="请选择">
|
|
||||||
<el-option
|
|
||||||
v-for="(value, key) in comparisonOperatorOptions"
|
|
||||||
:key="key + 'releaseOperator'"
|
|
||||||
:label="key"
|
|
||||||
:value="value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-input
|
|
||||||
placeholder="请输入释放值"
|
|
||||||
v-model="item.releaseValue"
|
|
||||||
>
|
|
||||||
</el-input>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-select v-model="item.relationNext" placeholder="请选择">
|
|
||||||
<el-option
|
|
||||||
v-for="(value, key) in relationWithPoint"
|
|
||||||
:key="key + 'relation'"
|
|
||||||
:label="key"
|
|
||||||
:value="value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button
|
|
||||||
@click.native.prevent="deleteRow(index,'protectionSettings')"
|
|
||||||
type="warning"
|
|
||||||
size="mini"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="items-container">
|
|
||||||
<div class="item-title">
|
|
||||||
保护方案:
|
|
||||||
<div style="display: inline-block; margin-left: 20px">
|
|
||||||
<el-form-item label="延时" prop="releaseDelaySeconds">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.releaseDelaySeconds"
|
|
||||||
placeholder="请输入"
|
|
||||||
clearable
|
|
||||||
:style="{ width: '200px', display: 'inline-block' }"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button
|
|
||||||
@click.native.prevent="addRow('protectionPlan')"
|
|
||||||
block
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
style="margin-bottom: 20px"
|
|
||||||
>
|
|
||||||
新增保护方案
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="item-content">
|
|
||||||
<div class="time-lists-container">
|
|
||||||
<div class="time-lists time-lists-title">
|
|
||||||
<div>设备类型</div>
|
|
||||||
<div>点位</div>
|
|
||||||
<div>故障值比较符号</div>
|
|
||||||
<div>故障值</div>
|
|
||||||
<div>操作</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="time-lists"
|
|
||||||
v-for="(item, index) in protectionPlan"
|
|
||||||
:key="'protectionPlan' + index"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<el-cascader
|
|
||||||
v-model="item.deviceId"
|
|
||||||
:show-all-levels="false"
|
|
||||||
:options="childOptions"
|
|
||||||
@change="(v)=>handleChange(v,'protectionPlan',index)"
|
|
||||||
></el-cascader>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-autocomplete
|
|
||||||
v-model="item.point"
|
|
||||||
placeholder="请输入点位"
|
|
||||||
clearable
|
|
||||||
:fetch-suggestions="
|
|
||||||
(q, c) => querySearchAsync(q, c, index, 'protectionPlan')
|
|
||||||
"
|
|
||||||
@select="(v) => handleSelect(v, index, 'protectionPlan')"
|
|
||||||
></el-autocomplete>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>=</div>
|
|
||||||
<div>
|
|
||||||
<el-input placeholder="请输入故障值" v-model="item.value">
|
|
||||||
</el-input>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button
|
|
||||||
@click.native.prevent="deleteRow(index,'protectionPlan')"
|
|
||||||
type="warning"
|
|
||||||
size="mini"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer">
|
|
||||||
<el-button @click="closeDialog">取消</el-button>
|
|
||||||
<el-button type="primary" @click="saveDialog">确定</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import { mapState } from "vuex";
|
|
||||||
import { getAllSites } from "@/api/ems/zddt";
|
|
||||||
import { validText } from "@/utils/validate";
|
|
||||||
import {
|
|
||||||
updateProtectPlan,
|
|
||||||
addProtectPlan,
|
|
||||||
getProtectPlan,
|
|
||||||
getDeviceListBySiteAndCategory
|
|
||||||
} from "@/api/ems/site";
|
|
||||||
import { getAllDeviceCategory, pointFuzzyQuery } from "@/api/ems/search";
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
const validateText = (rule, value, callback) => {
|
|
||||||
if (value !== "" && !validText(value)) {
|
|
||||||
callback(new Error("只能输入中文、英文、数字和特殊字符!"));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
mode:'',
|
|
||||||
loading: 0,
|
|
||||||
childOptions:[],
|
|
||||||
protectionSettings: [],
|
|
||||||
protectionPlan: [],
|
|
||||||
dialogTableVisible: false,
|
|
||||||
siteList: [], //站点列表 从接口获取数据
|
|
||||||
formData: {
|
|
||||||
id: "", //设备唯一标识
|
|
||||||
siteId: "", //站点ID
|
|
||||||
faultName: "", //设备保护名称
|
|
||||||
isAlert: 0, //是否告警
|
|
||||||
faultLevel: 1, //告警等级
|
|
||||||
faultDelaySeconds: "", //故障延时
|
|
||||||
releaseDelaySeconds: "", //释放延时
|
|
||||||
description:'',//方案描述
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
siteId: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "请选择站点",
|
|
||||||
trigger: ["blur", "change"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
faultName: [
|
|
||||||
{ required: true, message: "请输入设备保护名称", trigger: "blur" },
|
|
||||||
],
|
|
||||||
isAlert: [
|
|
||||||
{ required: true, message: "请选择是否告警", trigger: "blur" },
|
|
||||||
],
|
|
||||||
description: [
|
|
||||||
{ required: true, message: "请输入设备描述", trigger: "blur" },
|
|
||||||
{ validator: validateText, trigger: "blur" },
|
|
||||||
],
|
|
||||||
faultDelaySeconds: [
|
|
||||||
{ required: true, message: "请输入保护前提延时", trigger: "blur" },
|
|
||||||
{ validator: validateText, trigger: "blur" },
|
|
||||||
],
|
|
||||||
releaseDelaySeconds: [
|
|
||||||
{ required: true, message: "请输入保护方案延时", trigger: "blur" },
|
|
||||||
{ validator: validateText, trigger: "blur" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
communicationStatusOptions: (state) =>
|
|
||||||
state?.ems?.communicationStatusOptions || {},
|
|
||||||
deviceTypeOptions: (state) => state?.ems?.deviceTypeOptions || {},
|
|
||||||
comparisonOperatorOptions: (state) =>
|
|
||||||
state?.ems?.comparisonOperatorOptions || {},
|
|
||||||
relationWithPoint: (state) => state?.ems?.relationWithPoint || {},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
open(id,siteId){
|
|
||||||
this.dialogTableVisible=true
|
|
||||||
this.getZdList();
|
|
||||||
this.getDeviceCategoryList().then(()=>{
|
|
||||||
if(id && siteId) {
|
|
||||||
this.getDeviceList('PCS',siteId)
|
|
||||||
this.getDeviceList('STACK',siteId)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(id){
|
|
||||||
this.formData.id = id
|
|
||||||
this.mode = 'edit'
|
|
||||||
getProtectPlan(id).then(response => {
|
|
||||||
const data = response?.data || {}
|
|
||||||
this.formData = {
|
|
||||||
id,
|
|
||||||
siteId: data?.siteId || '', //站点ID
|
|
||||||
faultName: data?.faultName || '', //设备保护名称
|
|
||||||
isAlert: data?.isAlert || 0, //是否告警
|
|
||||||
faultLevel: data?.faultLevel || 1, //告警等级
|
|
||||||
faultDelaySeconds: data?.faultDelaySeconds || "", //故障延时
|
|
||||||
releaseDelaySeconds: data?.releaseDelaySeconds ||"", //释放延时
|
|
||||||
description: data?.description ||'',//方案描述
|
|
||||||
}
|
|
||||||
const plan =(JSON.parse(data?.protectionPlan || [])).map(item=>{
|
|
||||||
return Object.assign({},item,{
|
|
||||||
deviceId:[item.deviceCategory || '',item.deviceId || ''],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const settings =(JSON.parse(data?.protectionSettings || [])).map(item=>{
|
|
||||||
return Object.assign({},item,{
|
|
||||||
deviceId:[item.deviceCategory || '',item.deviceId || ''],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.$nextTick(()=>{
|
|
||||||
this.protectionPlan.splice(0,0,...plan)
|
|
||||||
this.protectionSettings.splice(0,0,...settings)
|
|
||||||
})
|
|
||||||
console.log('获取设备保护详情并初始化',this.formData,this.protectionPlan,this.protectionSettings)
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
this.mode = 'add'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 新增设备保护前提、设备保护方案
|
|
||||||
addRow(type) {
|
|
||||||
const item = type === 'protectionSettings' ? {
|
|
||||||
deviceId:[],//设备ID
|
|
||||||
deviceCategory: "",//设备类型 英文
|
|
||||||
categoryName:'',//设备类型名称 中文
|
|
||||||
point: "",//点位 英文
|
|
||||||
pointName:"",//点位 中文
|
|
||||||
faultValue: "",//故障值
|
|
||||||
releaseValue: "",//释放值
|
|
||||||
faultOperator: "",//故障值比较关系
|
|
||||||
releaseOperator: "",//释放值比较关系
|
|
||||||
relationNext: "",//与下一个点位的关系
|
|
||||||
} : {
|
|
||||||
deviceId:[],
|
|
||||||
deviceCategory: "",//设备类型 英文
|
|
||||||
categoryName:'',//设备类型名称 中文
|
|
||||||
point: "",
|
|
||||||
pointName:"",
|
|
||||||
value: "",//设置值
|
|
||||||
}
|
|
||||||
// this.$set(this[type], this[type].length, item);
|
|
||||||
this[type].splice(this[type].length,0,item)
|
|
||||||
console.log('新增设备保护前提、方案',type,this[type])
|
|
||||||
},
|
|
||||||
// 删除设备保护前提、设备保护方案
|
|
||||||
deleteRow(index, type) {
|
|
||||||
this[type].splice(index, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 设备保护前提、设备保护方案点位选择
|
|
||||||
querySearchAsync(query, cb, index, type) {
|
|
||||||
console.log("查询数据", query, index);
|
|
||||||
if (!this.formData.siteId || !this[type][index].deviceCategory) {
|
|
||||||
this.$message({
|
|
||||||
type: "warning",
|
|
||||||
message: "请先选择站点和设备",
|
|
||||||
});
|
|
||||||
return cb([]);
|
|
||||||
}
|
|
||||||
pointFuzzyQuery({
|
|
||||||
siteIds: [this.formData.siteId],
|
|
||||||
deviceCategory: this[type][index].deviceCategory,
|
|
||||||
pointName: query,
|
|
||||||
}).then((response) => {
|
|
||||||
const data = response?.data || [];
|
|
||||||
cb(
|
|
||||||
data.map((item) => {
|
|
||||||
return { name: item, value: item };
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 点位选择
|
|
||||||
handleSelect(data, index, type) {
|
|
||||||
console.log('选择点位',data,index,type)
|
|
||||||
// this.$set(this[type], index, Object.assign({},this[type][index],{
|
|
||||||
// point:data.value,
|
|
||||||
// pointName:data.value,
|
|
||||||
// }));
|
|
||||||
let line = Object.assign({},this[type][index],{
|
|
||||||
point:data.value,
|
|
||||||
pointName:data.value,
|
|
||||||
})
|
|
||||||
this[type].splice(index,1,line);
|
|
||||||
console.log('选择点位配置完成',this[type][index])
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取设备类别-不区分站点
|
|
||||||
getDeviceCategoryList() {
|
|
||||||
this.loading += 1;
|
|
||||||
return getAllDeviceCategory()
|
|
||||||
.then((response) => {
|
|
||||||
const data = (response?.data || []).filter(item => ['PCS','STACK'].includes(item.code));
|
|
||||||
// this.childOptions=[]
|
|
||||||
this.$set(this,'childOptions',[])
|
|
||||||
let arr =[]
|
|
||||||
data.forEach((item) => {
|
|
||||||
arr.push({
|
|
||||||
value: item.code,
|
|
||||||
label: item.name,
|
|
||||||
children:[]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.childOptions.splice(0,0,...arr)
|
|
||||||
console.log('获取设备类型',data,this.childOptions)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading -= 1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//获取设备列表-区分站点
|
|
||||||
getDeviceList(deviceCategory,siteId){
|
|
||||||
this.$nextTick(()=>{
|
|
||||||
getDeviceListBySiteAndCategory({siteId:siteId || this.formData.siteId,deviceCategory}).then((response) => {
|
|
||||||
const data = (response?.data || []).map(item => {
|
|
||||||
return {
|
|
||||||
label: item.deviceName,
|
|
||||||
value: item.id,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const index = this.childOptions.findIndex(item=>item.value === deviceCategory)
|
|
||||||
if(index>-1){
|
|
||||||
const length = this.childOptions[index].children.length
|
|
||||||
this.childOptions[index].children.splice(0,length,...data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
//更新站点下面的设备列表
|
|
||||||
updateSiteDeviceList(){
|
|
||||||
this.childOptions.forEach(item => {
|
|
||||||
const length = item.children.length
|
|
||||||
item.children.splice(0,length)
|
|
||||||
})
|
|
||||||
this.getDeviceList('PCS')
|
|
||||||
this.getDeviceList('STACK')
|
|
||||||
},
|
|
||||||
//选中设备类型、设备
|
|
||||||
handleChange(data,type,index){
|
|
||||||
const deviceCategory = data[0],deviceId=data[1]
|
|
||||||
console.log('设置选中设备类型、设备',deviceCategory,deviceId,type,index)
|
|
||||||
const item = Object.assign({},this[type][index],{
|
|
||||||
deviceId:data,
|
|
||||||
deviceCategory,
|
|
||||||
categoryName : this.childOptions.find(i=>i.value === deviceCategory).label,
|
|
||||||
pointName:'',
|
|
||||||
point:''
|
|
||||||
})
|
|
||||||
this.$nextTick(()=>{
|
|
||||||
// this.$set(this[type], index, item);
|
|
||||||
this[type].splice(index,1,item);
|
|
||||||
})
|
|
||||||
console.log('设置选中设备类型、设备配置完成',this[type][index])
|
|
||||||
},
|
|
||||||
|
|
||||||
//获取站点列表
|
|
||||||
getZdList() {
|
|
||||||
this.loading += 1;
|
|
||||||
getAllSites()
|
|
||||||
.then((response) => {
|
|
||||||
this.siteList = response?.data || [];
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading -= 1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 切换站点
|
|
||||||
// 重新获取设备列表
|
|
||||||
// 清空选中的设备、点位信息
|
|
||||||
changeType() {
|
|
||||||
//获取当前站点下的pcs和bms
|
|
||||||
this.updateSiteDeviceList()
|
|
||||||
if(this.protectionSettings.length>0){
|
|
||||||
const list =this.protectionSettings
|
|
||||||
list.forEach((item) => {
|
|
||||||
item.point = ""
|
|
||||||
item.pointName = ""
|
|
||||||
item.deviceId=[]
|
|
||||||
item.categoryName=''
|
|
||||||
item.deviceCategory=''
|
|
||||||
});
|
|
||||||
// this.$set(this,'protectionSettings',list)
|
|
||||||
this.$nextTick(()=>{
|
|
||||||
this.protectionSettings.splice(0,this.protectionSettings.length,...list)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if(this.protectionPlan.length>0){
|
|
||||||
const list =this.protectionPlan
|
|
||||||
list.forEach((item) => {
|
|
||||||
item.point = ""
|
|
||||||
item.pointName = ""
|
|
||||||
item.deviceId=[]
|
|
||||||
item.categoryName=''
|
|
||||||
item.deviceCategory=''
|
|
||||||
});
|
|
||||||
// this.$set(this,'protectionPlan',list)
|
|
||||||
this.$nextTick(()=>{
|
|
||||||
this.protectionPlan.splice(0,this.protectionPlan.length,...list)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
saveDialog() {
|
|
||||||
function getToastMsg(name,type,index){
|
|
||||||
return {
|
|
||||||
protectionSettings:{
|
|
||||||
deviceId:`请选择保护前提第${index}行的设备`,//设备ID
|
|
||||||
deviceCategory: `请选择保护前提第${index}行的设备类型`,//设备类型 英文
|
|
||||||
categoryName:`请选择保护前提第${index}行的设备类型`,//设备类型名称 中文
|
|
||||||
point: `请选择保护前提第${index}行的点位`,//点位 英文
|
|
||||||
pointName:`请选择保护前提第${index}行的点位`,//点位 中文
|
|
||||||
faultValue: `请输入保护前提第${index}行的故障值`,//故障值
|
|
||||||
releaseValue: `请输入保护前提第${index}行的释放值`,//释放值
|
|
||||||
faultOperator: `请选择保护前提第${index}行的故障值比较关系`,//故障值比较关系
|
|
||||||
releaseOperator: `请选择保护前提第${index}行的释放值比较关系`,//释放值比较关系
|
|
||||||
relationNext: `请选择保护前提第${index}行与下一个点位的关系`,//与下一个点位的关系
|
|
||||||
},
|
|
||||||
protectionPlan :{
|
|
||||||
deviceId:`请选择保护方案第${index}行的设备`,
|
|
||||||
deviceCategory: `请选择保护方案第${index}行的设备类型`,//设备类型 英文
|
|
||||||
categoryName:`请选择保护方案第${index}行的设备类型`,//设备类型名称 中文
|
|
||||||
point: `请选择保护方案第${index}行的点位`,
|
|
||||||
pointName:`请选择保护方案第${index}行的点位`,
|
|
||||||
value: `请输入保护方案第${index}行的故障值`,//设置值
|
|
||||||
}
|
|
||||||
}[type][name]
|
|
||||||
}
|
|
||||||
this.$refs.addTempForm.validate((valid) => {
|
|
||||||
if (!valid) return;
|
|
||||||
const {
|
|
||||||
id = "", //设备唯一标识
|
|
||||||
siteId = "", //站点ID
|
|
||||||
faultName = "", //设备保护名称
|
|
||||||
isAlert = 0, //是否告警
|
|
||||||
faultLevel = 1, //告警等级
|
|
||||||
faultDelaySeconds = "", //故障延时
|
|
||||||
releaseDelaySeconds = "", //释放延时
|
|
||||||
description="",//方案描述
|
|
||||||
} = this.formData;
|
|
||||||
const {protectionSettings,protectionPlan} = this
|
|
||||||
let protectionSettingsValidateStatus= true , protectionPlanValidateStatus= true
|
|
||||||
for(let i = 0;i<protectionSettings.length;i++){
|
|
||||||
let valueMap = Object.entries(protectionSettings[i]);
|
|
||||||
for(let inner = 0;inner < valueMap.length;inner++){
|
|
||||||
const key =valueMap[inner][0],value =valueMap[inner][1]
|
|
||||||
if(key === 'relationNext'){
|
|
||||||
if(protectionSettings[i+1] && !value){//有下一个点位
|
|
||||||
this.$message.error(getToastMsg(key,'protectionSettings',i+1))
|
|
||||||
protectionSettingsValidateStatus=false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(![0,'0'].includes(value) && !value){
|
|
||||||
this.$message.error(getToastMsg(key,'protectionSettings',i+1))
|
|
||||||
protectionSettingsValidateStatus=false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!protectionSettingsValidateStatus) break
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let i = 0;i<protectionPlan.length;i++){
|
|
||||||
let valueMap = Object.entries(protectionPlan[i]);
|
|
||||||
for(let inner = 0;inner < valueMap.length;inner++){
|
|
||||||
const key =valueMap[inner][0],value =valueMap[inner][1]
|
|
||||||
if(key === 'relationNext'){
|
|
||||||
if(protectionPlan[i+1] && !value){//有下一个点位
|
|
||||||
this.$message.error(getToastMsg(key,'protectionPlan',i+1))
|
|
||||||
protectionPlanValidateStatus=false
|
|
||||||
break
|
|
||||||
}else{
|
|
||||||
// protectionPlan[i][key] = ''//清空选择的关系
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(![0,'0'].includes(value) && !value){
|
|
||||||
this.$message.error(getToastMsg(key,'protectionPlan',i+1))
|
|
||||||
protectionPlanValidateStatus=false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!protectionPlanValidateStatus) break
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!protectionSettingsValidateStatus || !protectionPlanValidateStatus) return
|
|
||||||
const settings = protectionSettings.map(item=>{
|
|
||||||
return Object.assign({},item,{
|
|
||||||
deviceId:item.deviceId[1],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const plan = protectionPlan.map(item=>{
|
|
||||||
return Object.assign({},item,{
|
|
||||||
deviceId:item.deviceId[1],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.loading += 1;
|
|
||||||
const params= {
|
|
||||||
siteId,
|
|
||||||
faultName,
|
|
||||||
isAlert,
|
|
||||||
faultLevel,
|
|
||||||
faultDelaySeconds,
|
|
||||||
releaseDelaySeconds,
|
|
||||||
description,
|
|
||||||
protectionSettings:JSON.stringify(settings),
|
|
||||||
protectionPlan:JSON.stringify(plan),
|
|
||||||
}
|
|
||||||
if (this.mode === "add") {
|
|
||||||
addProtectPlan(params)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
//新增成功
|
|
||||||
// 关闭弹窗 更新表格
|
|
||||||
this.$emit("update");
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading -= 1;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
params.id = id
|
|
||||||
updateProtectPlan(params)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
//新增成功
|
|
||||||
// 关闭弹窗 更新表格
|
|
||||||
this.$emit("update");
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading -= 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
closeDialog() {
|
|
||||||
this.$emit("clear");
|
|
||||||
// 清空所有数据
|
|
||||||
for(let key in this.formData) {
|
|
||||||
this.formData[key] = key === 'isAlert' ? 0 : key === 'faultLevel' ? 1 : ''
|
|
||||||
}
|
|
||||||
this.$refs.addTempForm.resetFields();
|
|
||||||
this.$set(this,'protectionSettings',[])
|
|
||||||
this.$set(this,'protectionPlan',[])
|
|
||||||
this.$set(this,'childOptions',[])
|
|
||||||
this.dialogTableVisible = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.items-container {
|
|
||||||
margin-top: 40px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
.item-title {
|
|
||||||
line-height: 16px;
|
|
||||||
padding: 10px 0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.time-lists-container {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
.time-lists {
|
|
||||||
&:not(:last-child) {
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
display: flex;
|
|
||||||
& > div {
|
|
||||||
width: 16%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px 15px;
|
|
||||||
&:not(:last-child) {
|
|
||||||
width: 28%;
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
}
|
|
||||||
.el-date-editor.el-input,
|
|
||||||
.el-date-editor.el-input__inner {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.time-lists-title {
|
|
||||||
color: #000;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
1090
src/views/ems/site/sbbh/AddPlan.vue
Normal file
1090
src/views/ems/site/sbbh/AddPlan.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,160 +1,185 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="protect-plan-page" v-loading="loading">
|
||||||
class="ems-dashboard-editor-container"
|
<el-card class="query-card" shadow="never">
|
||||||
style="background-color: #ffffff"
|
<div class="query-head">
|
||||||
v-loading="loading"
|
<div class="query-title">设备保护方案</div>
|
||||||
>
|
<el-button type="primary" @click="addPlan" native-type="button">新增方案</el-button>
|
||||||
<el-form :inline="true" class="select-container">
|
</div>
|
||||||
<el-form-item label="站点选择">
|
<el-form :inline="true" class="query-form" @submit.native.prevent>
|
||||||
<el-select
|
<el-form-item label="故障名称">
|
||||||
v-model="form.siteId"
|
<el-input
|
||||||
placeholder="请选择换电站名称"
|
|
||||||
:loading="searchLoading"
|
|
||||||
loading-text="正在加载数据"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
:label="item.siteName"
|
|
||||||
:value="item.siteId"
|
|
||||||
v-for="(item, index) in siteList"
|
|
||||||
:key="index + 'zdxeSelect'"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="故障名称">
|
|
||||||
<el-input
|
|
||||||
v-model="form.faultName"
|
v-model="form.faultName"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入故障名称"
|
placeholder="请输入故障名称"
|
||||||
style="width: 150px"
|
style="width: 220px"
|
||||||
></el-input>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
|
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
|
||||||
<el-button @click="onReset" native-type="button">重置</el-button>
|
<el-button @click="onReset" native-type="button">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-button type="primary" @click="addDevice" native-type="button"
|
</el-card>
|
||||||
>新增设备</el-button
|
|
||||||
>
|
<el-card class="table-card" shadow="never">
|
||||||
<el-table
|
<el-table class="common-table" :data="tableData" stripe max-height="620px">
|
||||||
class="common-table"
|
<el-table-column prop="faultName" label="设备保护名称" min-width="140" />
|
||||||
:data="tableData"
|
<el-table-column prop="faultLevel" label="故障等级" width="100">
|
||||||
stripe
|
<template slot-scope="scope">等级{{ scope.row.faultLevel }}</template>
|
||||||
max-height="600px"
|
</el-table-column>
|
||||||
style="width: 100%; margin-top: 25px"
|
<el-table-column prop="isAlert" label="是否告警" width="100">
|
||||||
>
|
<template slot-scope="scope">{{ scope.row.isAlert === 1 ? '是' : '否' }}</template>
|
||||||
<el-table-column prop="siteId" label="站点" width="100"> </el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="faultName" label="设备保护名称" width="100"> </el-table-column>
|
<el-table-column prop="description" label="处理方案描述" min-width="180" show-overflow-tooltip />
|
||||||
<el-table-column prop="faultLevel" label="故障等级" width="100">
|
<el-table-column prop="protectionSettings" label="故障/释放保护" min-width="360" show-overflow-tooltip>
|
||||||
<template slot-scope="scope">等级{{scope.row.faultLevel}}</template>
|
<template slot-scope="scope">
|
||||||
</el-table-column>
|
<div class="rich-lines" v-html="handleProtectionSettings(scope.row.protectionSettings)"></div>
|
||||||
<el-table-column prop="isAlert" label="是否告警" width="100">
|
</template>
|
||||||
<template slot-scope="scope">{{scope.row.isAlert === 1 ? '是' : '否'}}</template>
|
</el-table-column>
|
||||||
</el-table-column>
|
<el-table-column prop="faultDelaySeconds" label="前提延时(s)" width="110" />
|
||||||
<el-table-column prop="description" label="处理方案描述" width="200" show-overflow-tooltip>
|
<el-table-column prop="protectionPlan" label="执行保护" min-width="260" show-overflow-tooltip>
|
||||||
</el-table-column>
|
<template slot-scope="scope">
|
||||||
<el-table-column prop="protectionSettings" label="保护前提" show-overflow-tooltip width="400">
|
<div class="rich-lines" v-html="handleProtectionPlan(scope.row.protectionPlan)"></div>
|
||||||
<template slot-scope="scope">
|
</template>
|
||||||
<div v-html="handleProtectionSettings(scope.row.protectionSettings)"></div>
|
</el-table-column>
|
||||||
</template>
|
<el-table-column prop="releaseDelaySeconds" label="方案延时(s)" width="110" />
|
||||||
</el-table-column>
|
<el-table-column fixed="right" label="操作" width="150">
|
||||||
<el-table-column prop="faultDelaySeconds" label="保护前提延时(s)" width="120">
|
<template slot-scope="scope">
|
||||||
</el-table-column>
|
<el-button @click="editDevice(scope.row)" type="warning" size="mini">编辑</el-button>
|
||||||
<el-table-column prop="protectionPlan" label="保护方案" show-overflow-tooltip width="200">
|
<el-button type="danger" @click="deleteDevice(scope.row)" size="mini">删除</el-button>
|
||||||
<template slot-scope="scope">
|
</template>
|
||||||
<div v-html="handleProtectionPlan(scope.row.protectionPlan)"></div>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
</el-table-column>
|
<el-pagination
|
||||||
<el-table-column prop="releaseDelaySeconds" label="保护方案延时(s)" width="120">
|
v-show="tableData.length > 0"
|
||||||
</el-table-column>
|
background
|
||||||
<el-table-column fixed="right" label="操作" width="150">
|
@size-change="handleSizeChange"
|
||||||
<template slot-scope="scope">
|
@current-change="handleCurrentChange"
|
||||||
<el-button @click="editDevice(scope.row)" type="warning" size="mini">
|
:current-page="pageNum"
|
||||||
编辑
|
:page-size="pageSize"
|
||||||
</el-button>
|
:page-sizes="[10, 20, 30, 40]"
|
||||||
<el-button type="danger" @click="deleteDevice(scope.row)" size="mini">
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
删除
|
:total="totalSize"
|
||||||
</el-button>
|
class="pager"
|
||||||
</template>
|
/>
|
||||||
</el-table-column>
|
</el-card>
|
||||||
</el-table>
|
|
||||||
<el-pagination
|
<add-plan ref="addPlan" @update="getData" />
|
||||||
v-show="tableData.length > 0"
|
|
||||||
background
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
:current-page="pageNum"
|
|
||||||
:page-size="pageSize"
|
|
||||||
:page-sizes="[10, 20, 30, 40]"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:total="totalSize"
|
|
||||||
style="margin-top: 15px; text-align: center"
|
|
||||||
>
|
|
||||||
</el-pagination>
|
|
||||||
<add-device
|
|
||||||
ref="addDevice"
|
|
||||||
@update="getData"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { protectPlanList, deleteProtectPlan } from "@/api/ems/site";
|
||||||
protectPlanList,
|
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
|
||||||
deleteProtectPlan,
|
import AddPlan from "./AddPlan.vue";
|
||||||
} from "@/api/ems/site";
|
|
||||||
import { getAllSites } from "@/api/ems/zddt";
|
|
||||||
import AddDevice from "./AddDevice.vue";
|
|
||||||
export default {
|
export default {
|
||||||
name: "SBBH",
|
name: "SBBH",
|
||||||
components: { AddDevice },
|
components: { AddPlan },
|
||||||
|
mixins: [getQuerySiteId],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form:{
|
form: {
|
||||||
siteId:'',
|
faultName: "",
|
||||||
faultName:''
|
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
searchLoading: false,
|
|
||||||
siteList: [],
|
|
||||||
tableData: [],
|
tableData: [],
|
||||||
pageSize: 10, //分页栏当前每个数据总数
|
pageSize: 10,
|
||||||
pageNum: 1, //分页栏当前页数
|
pageNum: 1,
|
||||||
totalSize: 0, //table表格数据总数
|
totalSize: 0,
|
||||||
dialogTableVisible: false,
|
dialogTableVisible: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleProtectionSettings(data){
|
init() {
|
||||||
if(!data || !JSON.parse(data)) return
|
this.pageNum = 1;
|
||||||
const arr = JSON.parse(data),
|
this.getData();
|
||||||
str= arr.map((item,index)=>{
|
|
||||||
const {categoryName='',deviceId='',point='',faultOperator='',faultValue='',releaseOperator='',releaseValue='',relationNext=''} = item
|
|
||||||
return `<div>${index+1}、 <span>${categoryName ? categoryName + '-' : ''}${deviceId ? deviceId + '-' : ''}${ point || ''}</span> <span>故障:${faultOperator || ''}${ faultValue || ''}</span> <span>释放:${releaseOperator || ''}${releaseValue || ''}</span> ${arr[index+1] ? '<span>关系:'+(relationNext || '')+'</span>' : ''}</div>`
|
|
||||||
})
|
|
||||||
return str.join('')
|
|
||||||
},
|
},
|
||||||
handleProtectionPlan(data){
|
handleProtectionSettings(data) {
|
||||||
if(!data || !JSON.parse(data)) return
|
if (!data) return;
|
||||||
const arr = JSON.parse(data),
|
let parsed = null;
|
||||||
str= arr.map((item,index)=>{
|
try {
|
||||||
const {categoryName='',deviceId='',point='',value=''} = item
|
parsed = JSON.parse(data);
|
||||||
return `<div>${index+1}、 <span>${categoryName ? categoryName + '-' : ''}${deviceId ? deviceId + '-' : ''}${ point || ''}</span> <span>故障:=${ value || ''}</span> </div>`
|
} catch (e) {
|
||||||
})
|
return "";
|
||||||
return str.join('')
|
}
|
||||||
|
const faultSettings = Array.isArray(parsed) ? parsed : parsed?.faultSettings || [];
|
||||||
|
const releaseSettings = Array.isArray(parsed) ? parsed : parsed?.releaseSettings || [];
|
||||||
|
|
||||||
|
const buildLine = (item, index, total, key, value, relationKey) => {
|
||||||
|
const {
|
||||||
|
categoryName = "",
|
||||||
|
deviceId = "",
|
||||||
|
point = "",
|
||||||
|
[key]: operator = "",
|
||||||
|
[value]: val = "",
|
||||||
|
relationNext = "",
|
||||||
|
} = item;
|
||||||
|
return `<div>${index + 1}、 <span>${categoryName ? categoryName + "-" : ""}${
|
||||||
|
deviceId ? deviceId + "-" : ""
|
||||||
|
}${point || ""}</span> <span>${relationKey}:${operator || ""}${val || ""}</span> ${
|
||||||
|
total[index + 1] ? "<span>关系:" + (relationNext || "") + "</span>" : ""
|
||||||
|
}</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const faultStr = faultSettings.map((item, index) =>
|
||||||
|
buildLine(item, index, faultSettings, "faultOperator", "faultValue", "故障")
|
||||||
|
);
|
||||||
|
const releaseStr = releaseSettings.map((item, index) =>
|
||||||
|
buildLine(item, index, releaseSettings, "releaseOperator", "releaseValue", "释放")
|
||||||
|
);
|
||||||
|
const groups = [];
|
||||||
|
if (faultStr.length) {
|
||||||
|
groups.push(`<div><strong>故障保护</strong></div>${faultStr.join("")}`);
|
||||||
|
}
|
||||||
|
if (releaseStr.length) {
|
||||||
|
groups.push(`<div><strong>释放保护</strong></div>${releaseStr.join("")}`);
|
||||||
|
}
|
||||||
|
return groups.join("");
|
||||||
},
|
},
|
||||||
// 新增设备 展示弹窗
|
handleProtectionPlan(data) {
|
||||||
addDevice() {
|
if (!data) return;
|
||||||
this.$refs.addDevice.open()
|
let arr = [];
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(data);
|
||||||
|
arr = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];
|
||||||
|
} catch (e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const actionLabelMap = {
|
||||||
|
derate: "降功率",
|
||||||
|
shutdown: "关机/停机/切断",
|
||||||
|
forbid_charge: "禁止充电",
|
||||||
|
allow_discharge: "允许放电",
|
||||||
|
forbid_discharge: "禁止放电",
|
||||||
|
allow_charge: "允许充电",
|
||||||
|
forbid_charge_discharge: "禁止充放电",
|
||||||
|
standby: "待机",
|
||||||
|
};
|
||||||
|
const str = arr.map((item, index) => {
|
||||||
|
const action = item?.action || "";
|
||||||
|
const point = item?.point || "";
|
||||||
|
const pointName = item?.pointName || "";
|
||||||
|
const actionName = item?.actionName || actionLabelMap[action] || pointName || point || "未配置";
|
||||||
|
const value = item?.value;
|
||||||
|
if ((action === "derate" || actionName.includes("降功率")) && value !== null && value !== undefined && value !== "") {
|
||||||
|
return `<div>${index + 1}、 <span>动作:${actionName}</span> <span>比例:${value}%</span></div>`;
|
||||||
|
}
|
||||||
|
return `<div>${index + 1}、 <span>动作:${actionName}</span></div>`;
|
||||||
|
});
|
||||||
|
return str.join("");
|
||||||
|
},
|
||||||
|
addPlan() {
|
||||||
|
if (!this.siteId) {
|
||||||
|
this.$message.warning("请先在顶部选择站点");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$refs.addPlan.open("", this.siteId);
|
||||||
},
|
},
|
||||||
// 编辑设备
|
|
||||||
editDevice(row) {
|
editDevice(row) {
|
||||||
this.$refs.addDevice.open(row.id,row.siteId)
|
this.$refs.addPlan.open(row.id, this.siteId);
|
||||||
},
|
},
|
||||||
//删除设备
|
|
||||||
deleteDevice(row) {
|
deleteDevice(row) {
|
||||||
console.log('删除')
|
|
||||||
this.$confirm(`确认要设备保护${row.faultName}吗?`, {
|
this.$confirm(`确认要设备保护${row.faultName}吗?`, {
|
||||||
confirmButtonText: "确定",
|
confirmButtonText: "确定",
|
||||||
cancelButtonText: "取消",
|
cancelButtonText: "取消",
|
||||||
@ -177,19 +202,14 @@ export default {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
//只有在废弃成功的情况下会走到这里
|
|
||||||
this.$message({
|
this.$message({
|
||||||
type: "success",
|
type: "success",
|
||||||
message: "删除成功!",
|
message: "删除成功!",
|
||||||
});
|
});
|
||||||
this.getData();
|
this.getData();
|
||||||
//调用接口 更新表格数据
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {});
|
||||||
//取消关机
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
// 分页
|
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@ -202,25 +222,27 @@ export default {
|
|||||||
this.getData();
|
this.getData();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 搜索
|
|
||||||
onSearch() {
|
onSearch() {
|
||||||
this.pageNum = 1; //每次搜索从1开始搜索
|
this.pageNum = 1;
|
||||||
this.getData();
|
this.getData();
|
||||||
},
|
},
|
||||||
// 重置
|
|
||||||
onReset() {
|
onReset() {
|
||||||
this.form={
|
this.form = {
|
||||||
siteId: "",
|
|
||||||
faultName: "",
|
faultName: "",
|
||||||
}
|
};
|
||||||
this.pageNum = 1; //每次搜索从1开始搜索
|
this.pageNum = 1;
|
||||||
this.getData();
|
this.getData();
|
||||||
},
|
},
|
||||||
// 获取数据
|
|
||||||
getData() {
|
getData() {
|
||||||
|
if (!this.siteId) {
|
||||||
|
this.tableData = [];
|
||||||
|
this.totalSize = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const { pageNum, pageSize } = this,{siteId,faultName=''}=this.form;
|
const { pageNum, pageSize } = this;
|
||||||
protectPlanList({ siteId, faultName,pageNum, pageSize })
|
const { faultName = "" } = this.form;
|
||||||
|
protectPlanList({ siteId: this.siteId, faultName, pageNum, pageSize })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.tableData = response?.rows || [];
|
this.tableData = response?.rows || [];
|
||||||
this.totalSize = response?.total || 0;
|
this.totalSize = response?.total || 0;
|
||||||
@ -229,31 +251,50 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//获取站点列表
|
|
||||||
getZdList() {
|
|
||||||
this.searchLoading = true;
|
|
||||||
return getAllSites()
|
|
||||||
.then((response) => {
|
|
||||||
this.siteList = response?.data || [];
|
|
||||||
if (this.siteList.length > 0) this.form.siteId = this.siteList[0].siteId;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.searchLoading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.loading = true;
|
|
||||||
this.form = {
|
|
||||||
siteId: "",
|
|
||||||
faultName: "",
|
|
||||||
};
|
|
||||||
this.pageNum = 1; //每次搜索从1开始搜索
|
|
||||||
this.getZdList().then(() => {
|
|
||||||
this.getData();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss">
|
||||||
|
.protect-plan-page {
|
||||||
|
padding: 16px;
|
||||||
|
background: linear-gradient(180deg, #f5f8ff 0%, #f7f9fc 100%);
|
||||||
|
|
||||||
|
.query-card,
|
||||||
|
.table-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e7edf7;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1d2a3a;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: -18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-lines {
|
||||||
|
color: #3e4b5a;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pager {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,77 +1,127 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog :visible.sync="dialogTableVisible" :close-on-press-escape="false" :close-on-click-modal="false" :show-close="false" destroy-on-close lock-scroll append-to-body width="600px" class="ems-dialog" :title="mode === 'add'?'新增设备':`编辑设备` " >
|
<el-dialog :visible.sync="dialogTableVisible" :close-on-press-escape="false" :close-on-click-modal="false"
|
||||||
<el-form v-loading="loading>0" ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="140px">
|
:show-close="false" destroy-on-close lock-scroll append-to-body width="800px" class="ems-dialog"
|
||||||
<el-form-item label="站点" prop="siteId">
|
:title="mode === 'add'?'新增设备':`编辑设备` ">
|
||||||
<el-select v-model="formData.siteId" placeholder="请选择" :style="{width: '100%'}" @change="changeType">
|
<div v-loading="loading>0">
|
||||||
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'siteOptions'"></el-option>
|
<div class="form-layout" :class="{ 'has-pcs': isPcs }">
|
||||||
</el-select>
|
<el-form v-loading="loading>0" ref="addTempForm" inline :model="formData" :rules="rules" size="medium"
|
||||||
</el-form-item>
|
label-width="120px" class="device-form base-form">
|
||||||
<el-form-item label="设备id" prop="deviceId" >
|
<el-form-item label="站点" prop="siteId">
|
||||||
<el-input v-model="formData.deviceId" placeholder="请输入" maxlength="60" clearable :style="{width: '100%'}">
|
<el-input
|
||||||
</el-input>
|
v-model="formData.siteId"
|
||||||
</el-form-item>
|
placeholder="请先在顶部选择站点"
|
||||||
<el-form-item label="设备名称" prop="deviceName">
|
disabled
|
||||||
<el-input v-model="formData.deviceName" placeholder="请输入" clearable :style="{width: '100%'}">
|
:style="{width: '100%'}"
|
||||||
</el-input>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设备描述" prop="description">
|
<el-form-item label="设备id" prop="deviceId">
|
||||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-input v-model="formData.deviceId" placeholder="请输入" maxlength="60" clearable :style="{width: '100%'}">
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="工作状态" prop="communicationStatus">
|
<el-form-item label="设备名称" prop="deviceName">
|
||||||
<el-select v-model="formData.communicationStatus" placeholder="请选择" :style="{width: '100%'}">
|
<el-input v-model="formData.deviceName" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
<el-option :label="value" :value="key" v-for="(value,key) in communicationStatusOptions" :key="key+'communicationStatusOptions'"></el-option>
|
</el-input>
|
||||||
</el-select>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item label="设备描述" prop="description">
|
||||||
<el-form-item label="设备类型" prop="deviceType">
|
<el-input v-model="formData.description" type="textarea" placeholder="请输入" clearable
|
||||||
<el-select v-model="formData.deviceType" placeholder="请选择" :style="{width: '100%'}">
|
:style="{width: '100%'}">
|
||||||
<el-option :label="value" :value="key" v-for="(value,key) in deviceTypeOptions" :key="key+'deviceTypeOptions'"></el-option>
|
</el-input>
|
||||||
</el-select>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item label="设备类型" prop="deviceType">
|
||||||
<el-form-item label="设备类别" prop="deviceCategory">
|
<el-select v-model="formData.deviceType" placeholder="请选择" :style="{width: '100%'}">
|
||||||
<el-select v-model="formData.deviceCategory" placeholder="请选择" :style="{width: '100%'}" @change="changeType">
|
<el-option :label="value" :value="key" v-for="(value,key) in deviceTypeOptions"
|
||||||
<el-option :label="item.name" :value="item.code" v-for="(item,index) in deviceCategoryList" :key="index+'deviceCategoryList'"></el-option>
|
:key="key+'deviceTypeOptions'"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="上级设备" prop="parentId" v-if="dccDeviceCategoryList.includes(formData.deviceCategory)">
|
<el-form-item label="设备类别" prop="deviceCategory">
|
||||||
<el-select v-model="formData.parentId" :placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先选择站点' : '请选择'" :style="{width: '100%'}">
|
<el-select v-model="formData.deviceCategory" placeholder="请选择" :style="{width: '100%'}"
|
||||||
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList" :key="index+'parentDeviceList'" ></el-option>
|
@change="changeType">
|
||||||
</el-select>
|
<el-option :label="item.name" :value="item.code" v-for="(item,index) in deviceCategoryList"
|
||||||
</el-form-item>
|
:key="index+'deviceCategoryList'"></el-option>
|
||||||
<el-form-item label="TCP设备的ip地址" prop="ipAddress" v-if="formData.deviceType === 'TCP'">
|
</el-select>
|
||||||
<el-input v-model="formData.ipAddress" placeholder="请输入" clearable :style="{width: '100%'}">
|
</el-form-item>
|
||||||
</el-input>
|
<el-form-item label="上级设备" prop="parentId" v-if="dccDeviceCategoryList.includes(formData.deviceCategory)">
|
||||||
</el-form-item>
|
<el-select v-model="formData.parentId"
|
||||||
<el-form-item label="TCP设备的端口号" prop="ipPort" v-if="formData.deviceType === 'TCP'">
|
:placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先在顶部选择站点' : '请选择'"
|
||||||
<el-input v-model="formData.ipPort" placeholder="请输入" clearable :style="{width: '100%'}">
|
:style="{width: '100%'}">
|
||||||
</el-input>
|
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList"
|
||||||
</el-form-item>
|
:key="index+'parentDeviceList'"></el-option>
|
||||||
|
</el-select>
|
||||||
<el-form-item label="串口路径" prop="serialPort">
|
</el-form-item>
|
||||||
<el-input v-model="formData.serialPort" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-form-item label="TCP设备的ip地址" prop="ipAddress" v-if="formData.deviceType === 'TCP'">
|
||||||
</el-input>
|
<el-input v-model="formData.ipAddress" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item label="波特率" prop="baudRate">
|
</el-form-item>
|
||||||
<el-input v-model="formData.baudRate" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-form-item label="TCP设备的端口号" prop="ipPort" v-if="formData.deviceType === 'TCP'">
|
||||||
</el-input>
|
<el-input v-model="formData.ipPort" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item label="数据位" prop="dataBits">
|
</el-form-item>
|
||||||
<el-input v-model="formData.dataBits" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-form-item label="从站地址" prop="slaveId" v-if="formData.deviceType === 'TCP'">
|
||||||
</el-input>
|
<el-input v-model="formData.slaveId" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item label="停止位" prop="stopBits">
|
</el-form-item>
|
||||||
<el-input v-model="formData.stopBits" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-form-item label="串口路径" prop="serialPort">
|
||||||
</el-input>
|
<el-input v-model="formData.serialPort" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item label="校验位" prop="parity">
|
</el-form-item>
|
||||||
<el-input v-model="formData.parity" placeholder="请输入" clearable :style="{width: '100%'}">
|
<el-form-item label="波特率" prop="baudRate">
|
||||||
</el-input>
|
<el-input v-model="formData.baudRate" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item label="图片" prop="pictureUrl">
|
</el-form-item>
|
||||||
<image-upload :limit="1" :drag="false" @input="uploadImage" :value="formData.pictureUrl"/>
|
<el-form-item label="数据位" prop="dataBits">
|
||||||
</el-form-item>
|
<el-input v-model="formData.dataBits" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
|
</el-input>
|
||||||
</el-form>
|
</el-form-item>
|
||||||
|
<el-form-item label="停止位" prop="stopBits">
|
||||||
|
<el-input v-model="formData.stopBits" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="校验位" prop="parity">
|
||||||
|
<el-input v-model="formData.parity" placeholder="请输入" clearable :style="{width: '100%'}">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<!-- pcs配置-->
|
||||||
|
<el-form v-if="isPcs" ref="pcsSettingForm" :model="pcsSetting" size="medium"
|
||||||
|
label-position="top" class="pcs-form" :rules="pcsSettingRules">
|
||||||
|
<div class="pcs-form__title">PCS配置</div>
|
||||||
|
<div class="pcs-form__grid">
|
||||||
|
<el-form-item label="开关机地址" prop="pointAddress" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.pointAddress" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="功率地址" prop="powerAddress" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.powerAddress" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开机指令" prop="startCommand" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.startCommand" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关机指令" prop="stopCommand" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.stopCommand" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开机目标功率" prop="startPower" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.startPower" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关机目标功率" prop="stopPower" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.stopPower" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="倍率" prop="powerMultiplier" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.powerMultiplier" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="电池簇数" prop="clusterNum" class="pcs-form__item">
|
||||||
|
<el-input v-model="pcsSetting.clusterNum" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div v-if="(parseInt(pcsSetting.clusterNum) || 0) > 0" class="pcs-form__cluster">
|
||||||
|
<div class="pcs-form__cluster-title">电池簇地址</div>
|
||||||
|
<template v-for="index in parseInt(pcsSetting.clusterNum) || 0">
|
||||||
|
<el-form-item :key="'clusterAddress' + index" :label="'电池簇' + index + '地址'" prop="clusterPointAddress">
|
||||||
|
<el-input v-model="pcsSetting.clusterPointAddress[index - 1]" placeholder="请输入" clearable :style="{width: '100%'}" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<el-button @click="closeDialog">取消</el-button>
|
<el-button @click="closeDialog">取消</el-button>
|
||||||
<el-button type="primary" @click="saveDialog">确定</el-button>
|
<el-button type="primary" @click="saveDialog">确定</el-button>
|
||||||
@ -80,259 +130,517 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
|
||||||
import {validText} from '@/utils/validate'
|
import {validText} from '@/utils/validate'
|
||||||
import {getDeviceDetailInfo,updateDevice,addDevice,getParentDeviceId} from "@/api/ems/site";
|
import {addDevice, getDeviceDetailInfo, getParentDeviceId, updateDevice} from "@/api/ems/site";
|
||||||
import {getAllDeviceCategory} from '@/api/ems/search'
|
import {getAllDeviceCategory} from '@/api/ems/search'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props:{
|
props: {
|
||||||
mode:{
|
mode: {
|
||||||
type:String,
|
type: String,
|
||||||
default:"add"
|
default: "add"
|
||||||
},
|
},
|
||||||
id:{
|
id: {
|
||||||
type:String|Number,
|
type: String | Number,
|
||||||
required:false
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const validateText=(rule, value, callback) =>{
|
const validateText = (rule, value, callback) => {
|
||||||
if (value !== '' && !validText(value)) {
|
if (value !== '' && !validText(value)) {
|
||||||
callback(new Error('只能输入中文、英文、数字和特殊字符!'));
|
callback(new Error('只能输入中文、英文、数字和特殊字符!'));
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const validateDeviceId=(rule, value, callback) =>{
|
const validateDeviceId = (rule, value, callback) => {
|
||||||
if (value !== '' && !/^[a-zA-Z0-9]+$/.test(value)) {
|
if (value !== '' && !/^[a-zA-Z0-9]+$/.test(value)) {
|
||||||
callback(new Error('只能输入英文和数字!'));
|
callback(new Error('只能输入英文和数字!'));
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const validateNumber = (rule, value, callback) => {
|
||||||
|
if (value !== '' && !/^[0-9]+$/.test(value)) {
|
||||||
|
callback(new Error('只能输入数字!'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const validateDecimal = (rule, value, callback) => {
|
||||||
|
if (value !== '' && !/^(0|[1-9]\d*)(\.\d+)?$/.test(value)) {
|
||||||
|
callback(new Error('只能输入非负数字!'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
loading:0,
|
loading: 0,
|
||||||
dccDeviceCategoryList:['CLUSTER','BATTERY'],//需要展示上级设备的设备类型
|
dccDeviceCategoryList: ['CLUSTER', 'BATTERY'],//需要展示上级设备的设备类型
|
||||||
dialogTableVisible:false,
|
dialogTableVisible: false,
|
||||||
parentDeviceList:[],//上级设备列表 从接口获取数据
|
parentDeviceList: [],//上级设备列表 从接口获取数据
|
||||||
siteList:[],//站点列表 从接口获取数据
|
deviceCategoryList: [],//设备类别列表 从接口获取数据
|
||||||
deviceCategoryList:[],//设备类别列表 从接口获取数据
|
|
||||||
formData: {
|
formData: {
|
||||||
id:'',//设备唯一标识
|
id: '',//设备唯一标识
|
||||||
siteId:'',//站点ID
|
siteId: '',//站点ID
|
||||||
deviceId:'',//设备id
|
deviceId: '',//设备id
|
||||||
deviceName:'',//设备名称
|
deviceName: '',//设备名称
|
||||||
description:'',//设备描述
|
description: '',//设备描述
|
||||||
communicationStatus:'',//工作状态
|
deviceType: '',//设备类型
|
||||||
deviceType:'',//设备类型
|
deviceCategory: '',//设备类别
|
||||||
deviceCategory:'',//设备类别
|
parentId: '',//上级设备id
|
||||||
parentId:'',//上级设备id
|
ipAddress: '',//TCP设备的ip地址
|
||||||
ipAddress:'',//TCP设备的ip地址
|
ipPort: "",//TCP端口号
|
||||||
ipPort:"",//TCP端口号
|
serialPort: '',//串口路径
|
||||||
serialPort:'',//串口路径
|
baudRate: '',//波特率
|
||||||
baudRate:'',//波特率
|
dataBits: '',//数据位
|
||||||
dataBits:'',//数据位
|
stopBits: '',//停止位
|
||||||
stopBits:'',//停止位
|
parity: '',//校验位
|
||||||
parity:'',//校验位
|
slaveId: '',//从站地址
|
||||||
pictureUrl:'',//设备图片
|
},
|
||||||
|
pcsSetting: {
|
||||||
|
deviceSettingId: '',
|
||||||
|
powerAddress: '',//功率地址
|
||||||
|
pointAddress: "",//开关机地址
|
||||||
|
startCommand: "",//开机指令
|
||||||
|
stopCommand: "",//关机指令
|
||||||
|
startPower: '',//开机目标功率
|
||||||
|
stopPower: '',//关机目标功率
|
||||||
|
powerMultiplier: '',//目标功率倍率
|
||||||
|
clusterNum: '',//电池簇数
|
||||||
|
clusterPointAddress: []//电池簇地址
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
siteId:[
|
siteId: [
|
||||||
{ required: true, message: '请选择站点', trigger: ['blur','change']}
|
{required: true, message: '请先在顶部选择站点', trigger: ['blur', 'change']}
|
||||||
],
|
],
|
||||||
deviceId:[
|
deviceId: [
|
||||||
{ required: true, message: '请输入设备id', trigger: 'blur'},
|
{required: true, message: '请输入设备id', trigger: 'blur'},
|
||||||
{ validator: validateDeviceId, trigger: 'blur' }
|
{validator: validateDeviceId, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
deviceName:[
|
deviceName: [
|
||||||
{ required: true, message: '请输入设备名称', trigger: 'blur'},
|
{required: true, message: '请输入设备名称', trigger: 'blur'},
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
description:[
|
description: [
|
||||||
{ required: true, message: '请输入设备描述', trigger: 'blur'},
|
{required: true, message: '请输入设备描述', trigger: 'blur'},
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
communicationStatus:[
|
deviceType: [
|
||||||
{ required: true, message: '请选择工作状态', trigger: ['blur','change']}
|
{required: true, message: '请选择设备类型', trigger: ['blur', 'change']}
|
||||||
],
|
],
|
||||||
deviceType:[
|
deviceCategory: [
|
||||||
{ required: true, message: '请选择设备类型', trigger:['blur','change']}
|
{required: true, message: '请选择设备类别', trigger: ['blur', 'change']}
|
||||||
],
|
],
|
||||||
deviceCategory:[
|
ipAddress: [
|
||||||
{ required: true, message: '请选择设备类别', trigger: ['blur','change']}
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
ipAddress:[
|
ipPort: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
ipPort:[
|
slaveId: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateNumber, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
serialPort:[
|
serialPort: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
baudRate:[
|
baudRate: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
dataBits:[
|
dataBits: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
stopBits:[
|
stopBits: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
parity:[
|
parity: [
|
||||||
{ validator: validateText, trigger: 'blur' }
|
{validator: validateText, trigger: 'blur'}
|
||||||
],
|
],
|
||||||
// pictureUrl:[
|
|
||||||
// { required: true, message: '请上传图片', trigger: ['blur', 'change']}
|
|
||||||
// ],
|
|
||||||
},
|
},
|
||||||
|
pcsSettingRules: {
|
||||||
|
pointAddress: [
|
||||||
|
{required: true, message: '请输入开关机地址', trigger: 'blur'},
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
powerAddress: [
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
startCommand: [
|
||||||
|
{required: true, message: '请输入开机指令', trigger: 'blur'},
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
startPower: [
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
stopCommand: [
|
||||||
|
{required: true, message: '请输入关机指令', trigger: 'blur'},
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
stopPower: [
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
powerMultiplier: [
|
||||||
|
{validator: validateDecimal, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
clusterNum: [
|
||||||
|
{required: true, message: '请输入电池簇数', trigger: 'blur'},
|
||||||
|
{validator: validateNumber, trigger: 'blur'}
|
||||||
|
],
|
||||||
|
clusterPointAddress: [
|
||||||
|
{required: true, message: '请输入电池簇地址', trigger: 'blur'},
|
||||||
|
{validator: validateText, trigger: 'blur'}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
communicationStatusOptions: state => state?.ems?.communicationStatusOptions || {},
|
deviceTypeOptions: state => state?.ems?.deviceTypeOptions || {}
|
||||||
deviceTypeOptions:state=>state?.ems?.deviceTypeOptions || {}
|
}),
|
||||||
})
|
isPcs() {
|
||||||
|
return this.formData.deviceCategory === 'PCS'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch:{
|
watch: {
|
||||||
dialogTableVisible:{
|
dialogTableVisible: {
|
||||||
handler(newVal){
|
handler(newVal) {
|
||||||
//打开弹窗
|
//打开弹窗
|
||||||
if(newVal){
|
if (newVal) {
|
||||||
this.getZdList()
|
if (this.mode === 'add') {
|
||||||
|
this.syncSiteFromRoute(true)
|
||||||
|
}
|
||||||
this.getDeviceCategoryList()
|
this.getDeviceCategoryList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
id:{
|
'$route.query.siteId': {
|
||||||
handler(newVal){
|
handler() {
|
||||||
if((newVal || newVal===0) && this.mode !== 'add'){
|
if (!this.dialogTableVisible || this.mode !== 'add') {
|
||||||
this.loading+=1
|
return
|
||||||
|
}
|
||||||
|
this.syncSiteFromRoute(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
handler(newVal) {
|
||||||
|
if ((newVal || newVal === 0) && this.mode !== 'add') {
|
||||||
|
this.loading += 1
|
||||||
getDeviceDetailInfo(newVal).then(response => {
|
getDeviceDetailInfo(newVal).then(response => {
|
||||||
this.formData = JSON.parse(JSON.stringify(response?.data || {}));
|
const {pcsSetting, ...data} = JSON.parse(JSON.stringify(response?.data || {}))
|
||||||
if(this.dccDeviceCategoryList.includes(this.formData.deviceCategory)){
|
this.formData = data;
|
||||||
|
if (pcsSetting && JSON.stringify(pcsSetting) !== '{}') {
|
||||||
|
this.pcsSetting = JSON.parse(JSON.stringify({
|
||||||
|
...pcsSetting,
|
||||||
|
clusterPointAddress: JSON.parse(pcsSetting.clusterPointAddress || [])
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (this.dccDeviceCategoryList.includes(this.formData.deviceCategory)) {
|
||||||
this.getParentDeviceList(true)
|
this.getParentDeviceList(true)
|
||||||
}
|
}
|
||||||
}).finally(() => {this.loading-=1})
|
}).finally(() => {
|
||||||
|
this.loading -= 1
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeType(){
|
syncSiteFromRoute(force = false) {
|
||||||
if(this.dccDeviceCategoryList.includes(this.formData.deviceCategory)){
|
const routeSiteId = this.$route?.query?.siteId
|
||||||
|
const normalizedSiteId = routeSiteId === undefined || routeSiteId === null ? '' : String(routeSiteId).trim()
|
||||||
|
if (!normalizedSiteId) {
|
||||||
|
if (force) {
|
||||||
|
this.formData.siteId = ''
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (force || !this.formData.siteId) {
|
||||||
|
this.formData.siteId = normalizedSiteId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeType() {
|
||||||
|
if (this.dccDeviceCategoryList.includes(this.formData.deviceCategory)) {
|
||||||
this.getParentDeviceList()
|
this.getParentDeviceList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
uploadImage(data){
|
|
||||||
this.formData.pictureUrl = data
|
|
||||||
},
|
|
||||||
//获取站点列表
|
|
||||||
getZdList(){
|
|
||||||
this.loading+=1
|
|
||||||
getAllSites().then(response => {
|
|
||||||
this.siteList = response?.data || []
|
|
||||||
}).finally(() => {this.loading-=1})
|
|
||||||
},
|
|
||||||
// 获取设备类别
|
// 获取设备类别
|
||||||
getDeviceCategoryList(){
|
getDeviceCategoryList() {
|
||||||
this.loading+=1
|
this.loading += 1
|
||||||
getAllDeviceCategory().then(response => {
|
getAllDeviceCategory().then(response => {
|
||||||
this.deviceCategoryList = response?.data || []
|
this.deviceCategoryList = response?.data || []
|
||||||
}).finally(() => {this.loading-=1})
|
}).finally(() => {
|
||||||
|
this.loading -= 1
|
||||||
|
})
|
||||||
},
|
},
|
||||||
//获取上级id列表
|
//获取上级id列表
|
||||||
getParentDeviceList(init=false){
|
getParentDeviceList(init = false) {
|
||||||
if(!this.formData.siteId){
|
if (!this.formData.siteId) {
|
||||||
return console.log('请先选择站点')
|
this.$message.warning('请先在顶部选择站点')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
!init && (this.formData.parentId='')
|
!init && (this.formData.parentId = '')
|
||||||
this.loading= this.loading+1
|
this.loading = this.loading + 1
|
||||||
getParentDeviceId({siteId:this.formData.siteId,deviceCategory:this.formData.deviceCategory}).then(response => {
|
getParentDeviceId({siteId: this.formData.siteId, deviceCategory: this.formData.deviceCategory}).then(response => {
|
||||||
this.parentDeviceList = JSON.parse(JSON.stringify(response?.data || []));
|
this.parentDeviceList = JSON.parse(JSON.stringify(response?.data || []));
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading=this.loading -1
|
this.loading = this.loading - 1
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
saveData() {
|
||||||
|
this.loading += 1
|
||||||
|
const {
|
||||||
|
id = '',
|
||||||
|
siteId = '',//站点ID
|
||||||
|
deviceId = '',//设备id
|
||||||
|
deviceName = '',//设备名称
|
||||||
|
description = '',//设备描述
|
||||||
|
deviceType = '',//设备类型
|
||||||
|
deviceCategory = '',//设备类别
|
||||||
|
parentId = '',//上级设备id
|
||||||
|
ipAddress = '',//TCP设备的ip地址
|
||||||
|
ipPort = "",//TCP端口号
|
||||||
|
serialPort = '',//串口路径
|
||||||
|
baudRate = '',//波特率
|
||||||
|
dataBits = '',//数据位
|
||||||
|
stopBits = '',//停止位
|
||||||
|
parity = '',//校验位
|
||||||
|
slaveId = '',//从站地址
|
||||||
|
} = this.formData;
|
||||||
|
const {
|
||||||
|
deviceSettingId,
|
||||||
|
powerAddress,
|
||||||
|
pointAddress,
|
||||||
|
startCommand,
|
||||||
|
stopCommand,
|
||||||
|
startPower,
|
||||||
|
stopPower,
|
||||||
|
powerMultiplier,
|
||||||
|
clusterNum,
|
||||||
|
clusterPointAddress
|
||||||
|
} = this.pcsSetting
|
||||||
|
let params = {
|
||||||
|
siteId,
|
||||||
|
deviceId,
|
||||||
|
deviceName,
|
||||||
|
description,
|
||||||
|
deviceType,
|
||||||
|
deviceCategory,
|
||||||
|
parentId,
|
||||||
|
ipAddress,
|
||||||
|
ipPort,
|
||||||
|
serialPort,
|
||||||
|
baudRate,
|
||||||
|
dataBits,
|
||||||
|
stopBits,
|
||||||
|
parity,
|
||||||
|
slaveId,
|
||||||
|
}
|
||||||
|
if (this.isPcs) {
|
||||||
|
params.pcsSetting = {
|
||||||
|
powerAddress,
|
||||||
|
pointAddress,
|
||||||
|
startCommand,
|
||||||
|
stopCommand,
|
||||||
|
startPower,
|
||||||
|
stopPower,
|
||||||
|
powerMultiplier,
|
||||||
|
clusterNum,
|
||||||
|
clusterPointAddress: JSON.stringify(!clusterNum ? [] : (clusterPointAddress || []).slice(0, clusterNum))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.mode === 'add') {
|
||||||
|
addDevice(params).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
//新增成功
|
||||||
|
// 关闭弹窗 更新表格
|
||||||
|
this.$emit('update')
|
||||||
|
this.closeDialog()
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading -= 1
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
params.id = id
|
||||||
|
params.pcsSetting && (params.pcsSetting.deviceSettingId = deviceSettingId)
|
||||||
|
updateDevice(params).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
//新增成功
|
||||||
|
// 关闭弹窗 更新表格
|
||||||
|
this.$emit('update')
|
||||||
|
this.closeDialog()
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading -= 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
saveDialog() {
|
saveDialog() {
|
||||||
this.$refs.addTempForm.validate(valid => {
|
this.$refs.addTempForm.validate(valid => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
this.loading+=1
|
if (this.isPcs) {
|
||||||
const {
|
this.$refs.pcsSettingForm.validate(pcsValidate => {
|
||||||
id='',
|
if (!pcsValidate) return
|
||||||
siteId='',//站点ID
|
this.saveData()
|
||||||
deviceId='',//设备id
|
|
||||||
deviceName='',//设备名称
|
|
||||||
description='',//设备描述
|
|
||||||
communicationStatus='',//工作状态
|
|
||||||
deviceType='',//设备类型
|
|
||||||
deviceCategory='',//设备类别
|
|
||||||
parentId='',//上级设备id
|
|
||||||
ipAddress='',//TCP设备的ip地址
|
|
||||||
ipPort="",//TCP端口号
|
|
||||||
serialPort='',//串口路径
|
|
||||||
baudRate='',//波特率
|
|
||||||
dataBits='',//数据位
|
|
||||||
stopBits='',//停止位
|
|
||||||
parity='',//校验位
|
|
||||||
pictureUrl='',//设备图片
|
|
||||||
}= this.formData;
|
|
||||||
if(this.mode === 'add'){
|
|
||||||
addDevice({siteId,deviceId,deviceName,description,communicationStatus,deviceType,deviceCategory,parentId,ipAddress,ipPort,serialPort,baudRate,dataBits,stopBits,parity,pictureUrl}).then(response => {
|
|
||||||
if(response.code === 200){
|
|
||||||
//新增成功
|
|
||||||
// 关闭弹窗 更新表格
|
|
||||||
this.$emit('update')
|
|
||||||
this.closeDialog()
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading-=1
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
updateDevice({id,siteId,deviceId,deviceName,description,communicationStatus,deviceType,deviceCategory,parentId,ipAddress,ipPort,serialPort,baudRate,dataBits,stopBits,parity,pictureUrl}).then(response => {
|
|
||||||
if(response.code === 200){
|
|
||||||
//新增成功
|
|
||||||
// 关闭弹窗 更新表格
|
|
||||||
this.$emit('update')
|
|
||||||
this.closeDialog()
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading-=1
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
this.saveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
closeDialog(){
|
closeDialog() {
|
||||||
this.$emit('clear')
|
this.$emit('clear')
|
||||||
// 清空所有数据
|
// 清空所有数据
|
||||||
this.formData= {
|
this.formData = {
|
||||||
id:'',//设备唯一标识
|
id: '',//设备唯一标识
|
||||||
siteId:'',//站点ID
|
siteId: '',//站点ID
|
||||||
deviceId:'',//设备id
|
deviceId: '',//设备id
|
||||||
deviceName:'',//设备名称
|
deviceName: '',//设备名称
|
||||||
description:'',//设备描述
|
description: '',//设备描述
|
||||||
communicationStatus:'',//工作状态
|
deviceType: '',//设备类型
|
||||||
deviceType:'',//设备类型
|
deviceCategory: '',//设备类别
|
||||||
deviceCategory:'',//设备类别
|
parentId: '',//上级设备id
|
||||||
parentId:'',//上级设备id
|
ipAddress: '',//TCP设备的ip地址
|
||||||
ipAddress:'',//TCP设备的ip地址
|
ipPort: "",//TCP端口号
|
||||||
ipPort:"",//TCP端口号
|
serialPort: '',//串口路径
|
||||||
serialPort:'',//串口路径
|
baudRate: '',//波特率
|
||||||
baudRate:'',//波特率
|
dataBits: '',//数据位
|
||||||
dataBits:'',//数据位
|
stopBits: '',//停止位
|
||||||
stopBits:'',//停止位
|
parity: '',//校验位
|
||||||
parity:'',//校验位
|
slaveId: '',//从站地址
|
||||||
pictureUrl:'',//设备图片
|
}
|
||||||
|
this.parentDeviceList = []
|
||||||
|
this.pcsSetting = {
|
||||||
|
deviceSettingId: '',
|
||||||
|
powerAddress: '',//功率地址
|
||||||
|
pointAddress: "",//开关机地址
|
||||||
|
startCommand: "",//开机指令
|
||||||
|
stopCommand: "",//关机指令
|
||||||
|
startPower: '',//开机目标功率
|
||||||
|
stopPower: '',//关机目标功率
|
||||||
|
powerMultiplier: '',//目标功率倍率
|
||||||
|
clusterNum: '',//电池簇数
|
||||||
|
clusterPointAddress: []//电池簇地址
|
||||||
}
|
}
|
||||||
this.$refs.addTempForm.resetFields()
|
this.$refs.addTempForm.resetFields()
|
||||||
this.dialogTableVisible=false
|
this.$refs?.pcsSettingForm?.resetFields()
|
||||||
|
this.dialogTableVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
|
.form-layout {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-layout.has-pcs {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-form {
|
||||||
|
::v-deep .el-form-item--medium .el-form-item__content {
|
||||||
|
width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
width: 50%;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: -360px;
|
||||||
|
width: 340px;
|
||||||
|
max-height: 520px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form__title {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form__item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form__cluster {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form__cluster-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcs-form {
|
||||||
|
::v-deep .el-form-item {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item__label {
|
||||||
|
line-height: 20px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item__content {
|
||||||
|
width: 100%;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-input__inner {
|
||||||
|
height: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item__error {
|
||||||
|
position: static;
|
||||||
|
line-height: 1.2;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ems-dialog {
|
||||||
|
::v-deep .el-dialog,
|
||||||
|
::v-deep .el-dialog__body {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
94
src/views/ems/site/sblb/PcsSwitch.vue
Normal file
94
src/views/ems/site/sblb/PcsSwitch.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<el-button :size="size" :type="type" :round="round" @click="switchStatus"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import {updateDeviceStatus} from "@/api/ems/site";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'mini',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
round: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'primary',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
workStatus: null,
|
||||||
|
deviceId: null,
|
||||||
|
deviceName: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
label() {
|
||||||
|
return this.data.workStatus === '0' ? '关机' : '开机'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
switchStatus() {
|
||||||
|
console.log(this.data, 11111111)
|
||||||
|
const {workStatus, deviceId, deviceName, siteId} = this.data
|
||||||
|
this.$confirm(`确认要${this.label}设备${deviceName || ''}吗?`, {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
showClose: false,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
type: "warning",
|
||||||
|
beforeClose: (action, instance, done) => {
|
||||||
|
if (action === "confirm") {
|
||||||
|
instance.confirmButtonLoading = true;
|
||||||
|
//做开关机操作,更新成功后刷新表格
|
||||||
|
updateDeviceStatus({
|
||||||
|
siteId,
|
||||||
|
workStatus: workStatus === '0' ? "1" : '0',
|
||||||
|
deviceId
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
response.code === 200 && done();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
instance.confirmButtonLoading = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
//只有在废弃成功的情况下会走到这里
|
||||||
|
this.$message({
|
||||||
|
type: "success",
|
||||||
|
message: `${deviceName}${this.label}成功!`,
|
||||||
|
});
|
||||||
|
this.$emit('updateSuccess')
|
||||||
|
//调用接口 更新表格数据
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
//取消关机
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
@ -80,7 +80,7 @@
|
|||||||
<el-table-column label="数据点位" prop="dataPoint"></el-table-column>
|
<el-table-column label="数据点位" prop="dataPoint"></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="数据点位名称"
|
label="数据点位名称"
|
||||||
prop="pointName"
|
prop="dataPointName"
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<!-- <el-table-column label="modbus地址">-->
|
<!-- <el-table-column label="modbus地址">-->
|
||||||
<!-- <template slot-scope="scope">-->
|
<!-- <template slot-scope="scope">-->
|
||||||
@ -88,8 +88,8 @@
|
|||||||
<!-- `${scope.row.ipAddress || ""} ${scope.row.ipPort || ""}`-->
|
<!-- `${scope.row.ipAddress || ""} ${scope.row.ipPort || ""}`-->
|
||||||
<!-- }}</span>-->
|
<!-- }}</span>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </el-table-column>-->
|
<!-- </el-table-column>
|
||||||
<el-table-column label="寄存器地址" prop="寄存器地址"></el-table-column>
|
<el-table-column label="寄存器地址" prop="寄存器地址"></el-table-column>-->
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="最新值"
|
label="最新值"
|
||||||
prop="pointValue"
|
prop="pointValue"
|
||||||
@ -110,6 +110,11 @@
|
|||||||
sortable="custom"
|
sortable="custom"
|
||||||
>
|
>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column v-if="selectable" label="操作" width="90" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click="selectPoint(scope.row)">选择</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-show="tableData.length > 0"
|
v-show="tableData.length > 0"
|
||||||
@ -147,6 +152,7 @@ export default {
|
|||||||
this.pageNum = 1;
|
this.pageNum = 1;
|
||||||
this.totalSize = 0;
|
this.totalSize = 0;
|
||||||
this.dataType = '';
|
this.dataType = '';
|
||||||
|
this.selectable = false;
|
||||||
this.form = {
|
this.form = {
|
||||||
sortMethod: "desc", //升序不传或者asc、降序desc)
|
sortMethod: "desc", //升序不传或者asc、降序desc)
|
||||||
sortData: this.defaultSort.prop,
|
sortData: this.defaultSort.prop,
|
||||||
@ -191,9 +197,14 @@ export default {
|
|||||||
pageSize: 10, //分页栏当前每个数据总数
|
pageSize: 10, //分页栏当前每个数据总数
|
||||||
pageNum: 1, //分页栏当前页数
|
pageNum: 1, //分页栏当前页数
|
||||||
totalSize: 0, //table表格数据总数
|
totalSize: 0, //table表格数据总数
|
||||||
|
selectable: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
selectPoint(row) {
|
||||||
|
this.$emit('select-point', row || {})
|
||||||
|
this.show = false
|
||||||
|
},
|
||||||
showChart({pointName}) {
|
showChart({pointName}) {
|
||||||
if (pointName) {
|
if (pointName) {
|
||||||
const {deviceCategory, deviceId} = this;
|
const {deviceCategory, deviceId} = this;
|
||||||
@ -235,12 +246,13 @@ export default {
|
|||||||
this.getData()
|
this.getData()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showTable({deviceCategory, siteId, deviceId, parentId = ""}, dataType) {
|
showTable({deviceCategory, siteId, deviceId, parentId = ""}, dataType, options = {}) {
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
this.deviceCategory = deviceCategory;
|
this.deviceCategory = deviceCategory;
|
||||||
this.siteId = siteId;
|
this.siteId = siteId;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.parentId = deviceCategory === "BATTERY" ? parentId : ""; //只有单体电池需要这个值
|
this.parentId = deviceCategory === "BATTERY" ? parentId : ""; //只有单体电池需要这个值
|
||||||
|
this.selectable = !!options.selectable
|
||||||
this.show = true;
|
this.show = true;
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
|
|||||||
@ -42,8 +42,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showDialog({siteId, code}) {
|
showDialog({siteId, deviceCategory, deviceId}) {
|
||||||
this.upload.data = {siteId, deviceCategory: code}
|
this.upload.data = {siteId, deviceCategory, deviceId}
|
||||||
this.show = true;
|
this.show = true;
|
||||||
},
|
},
|
||||||
//关闭弹窗 重置数据
|
//关闭弹窗 重置数据
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
||||||
<el-form :inline="true" class="select-container">
|
<el-form :inline="true" class="select-container">
|
||||||
<el-form-item label="站点选择">
|
<el-form-item label="设备类型">
|
||||||
<el-select v-model="siteId" placeholder="请选择换电站名称" :loading="searchLoading" loading-text="正在加载数据"
|
<el-select v-model="deviceCategory" placeholder="请选择设备类型" @change="onSearch" clearable>
|
||||||
@change="onSearch" clearable>
|
<el-option
|
||||||
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList"
|
v-for="(item,index) in deviceCategoryList"
|
||||||
:key="index+'zdxeSelect'"></el-option>
|
:key="index+'deviceCategorySelect'"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.code">
|
||||||
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
@ -14,36 +17,6 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-button type="primary" @click="addDevice" native-type="button">新增设备</el-button>
|
<el-button type="primary" @click="addDevice" native-type="button">新增设备</el-button>
|
||||||
<el-dropdown @command="downloadPointDetail">
|
|
||||||
<el-button
|
|
||||||
style="margin-left:10px;"
|
|
||||||
type="primary"
|
|
||||||
plain>
|
|
||||||
下载点位清单
|
|
||||||
</el-button>
|
|
||||||
<el-dropdown-menu slot="dropdown">
|
|
||||||
<el-dropdown-item v-for="(item,index) in deviceCategoryList" :key="index+'deviceCategoryList'"
|
|
||||||
:command="item">
|
|
||||||
{{ item.name }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</el-dropdown>
|
|
||||||
<el-dropdown @command="uploadPointDetail">
|
|
||||||
<el-button
|
|
||||||
style="margin-left:10px;"
|
|
||||||
type="success"
|
|
||||||
plain>
|
|
||||||
上传点位清单
|
|
||||||
</el-button>
|
|
||||||
<el-dropdown-menu slot="dropdown">
|
|
||||||
<el-dropdown-item v-for="(item,index) in deviceCategoryList" :key="index+'deviceCategoryList'"
|
|
||||||
:command="item">
|
|
||||||
{{ item.name }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</el-dropdown>
|
|
||||||
|
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
class="common-table"
|
class="common-table"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
@ -54,10 +27,6 @@
|
|||||||
prop="siteId"
|
prop="siteId"
|
||||||
label="站点ID">
|
label="站点ID">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
prop="siteName"
|
|
||||||
label="站点名称">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="deviceId"
|
prop="deviceId"
|
||||||
label="设备ID"
|
label="设备ID"
|
||||||
@ -71,31 +40,11 @@
|
|||||||
prop="categoryName"
|
prop="categoryName"
|
||||||
label="设备类别">
|
label="设备类别">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
prop="runningStatus"
|
|
||||||
label="在线状态">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span>{{ $store.state.ems.deviceStatusOptions[scope.row.runningStatus] }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
fixed="right"
|
fixed="right"
|
||||||
label="操作"
|
label="操作"
|
||||||
width="250">
|
width="180">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
|
||||||
@click="pointDetail(scope.row,'point')"
|
|
||||||
type="primary"
|
|
||||||
size="mini">
|
|
||||||
点位清单
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
@click="pointDetail(scope.row,'alarmPoint')"
|
|
||||||
type="primary"
|
|
||||||
size="mini">
|
|
||||||
报警点位清单
|
|
||||||
</el-button>
|
|
||||||
<br>
|
|
||||||
<el-button
|
<el-button
|
||||||
@click="editDevice(scope.row)"
|
@click="editDevice(scope.row)"
|
||||||
style="margin-top:10px;"
|
style="margin-top:10px;"
|
||||||
@ -126,7 +75,7 @@
|
|||||||
style="margin-top:15px;text-align: center"
|
style="margin-top:15px;text-align: center"
|
||||||
>
|
>
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
<el-dialog :visible.sync="dialogTableVisible" class="ems-dialog" title=" 详细信息" :close-on-click-modal="false"
|
<el-dialog :visible.sync="dialogTableVisible" class="ems-dialog" title="详细信息" :close-on-click-modal="false"
|
||||||
:before-close="handleClosed">
|
:before-close="handleClosed">
|
||||||
<div class="descriptions-main" style="padding: 0">
|
<div class="descriptions-main" style="padding: 0">
|
||||||
<el-descriptions direction="vertical" :column="2" :colon="false" border>
|
<el-descriptions direction="vertical" :column="2" :colon="false" border>
|
||||||
@ -138,33 +87,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<add-device ref="addDevice" :mode="mode" :id="editDeviceId" @update="getData" @clear="clearEditDeviceData"/>
|
<add-device ref="addDevice" :mode="mode" :id="editDeviceId" @update="getData" @clear="clearEditDeviceData"/>
|
||||||
<point-table ref="pointTable"/>
|
|
||||||
<point-upload ref="pointUpload" @update="getData"/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {deleteService, getDeviceDetailInfo, getDeviceInfoList} from '@/api/ems/site'
|
import {deleteService, getDeviceDetailInfo, getDeviceInfoList} from '@/api/ems/site'
|
||||||
import {getAllSites} from '@/api/ems/zddt'
|
|
||||||
import {formatNumber} from "@/filters/ems";
|
import {formatNumber} from "@/filters/ems";
|
||||||
import {getAllDeviceCategory} from '@/api/ems/search'
|
import {getAllDeviceCategory} from '@/api/ems/search'
|
||||||
import PointTable from './PointTable.vue'
|
|
||||||
import AddDevice from "./AddDevice.vue";
|
import AddDevice from "./AddDevice.vue";
|
||||||
import PointUpload from "./PointUpload.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Sblb",
|
name: "Sblb",
|
||||||
components: {AddDevice, PointTable, PointUpload},
|
components: {AddDevice},
|
||||||
|
watch: {
|
||||||
|
'$route.query.siteId'(newSiteId) {
|
||||||
|
const normalizedSiteId = this.hasValidSiteId(newSiteId) ? String(newSiteId).trim() : ''
|
||||||
|
if (normalizedSiteId === this.siteId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.siteId = normalizedSiteId
|
||||||
|
this.onSearch()
|
||||||
|
},
|
||||||
|
'$route.query.siteName'(newSiteName) {
|
||||||
|
const normalizedSiteName = this.getSelectedSiteName(newSiteName)
|
||||||
|
if (normalizedSiteName === this.selectedSiteName) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.selectedSiteName = normalizedSiteName
|
||||||
|
this.tableData = (this.tableData || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
siteName: normalizedSiteName
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
searchLoading: false,
|
|
||||||
mode: '',//新增、编辑设备
|
mode: '',//新增、编辑设备
|
||||||
editDeviceId: '',//编辑设备id
|
editDeviceId: '',//编辑设备id
|
||||||
siteId: '',
|
siteId: '',
|
||||||
siteList: [],
|
selectedSiteName: '',
|
||||||
tableData: [],
|
deviceCategory: '',//搜索栏设备类型
|
||||||
deviceCategoryList: [],//设备类别
|
deviceCategoryList: [],//设备类别
|
||||||
|
tableData: [],
|
||||||
pageSize: 10,//分页栏当前每个数据总数
|
pageSize: 10,//分页栏当前每个数据总数
|
||||||
pageNum: 1,//分页栏当前页数
|
pageNum: 1,//分页栏当前页数
|
||||||
totalSize: 0,//table表格数据总数
|
totalSize: 0,//table表格数据总数
|
||||||
@ -193,27 +158,26 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
hasValidSiteId(siteId) {
|
||||||
|
return !!(siteId !== undefined && siteId !== null && String(siteId).trim())
|
||||||
|
},
|
||||||
|
getSelectedSiteName(routeSiteName) {
|
||||||
|
const name = routeSiteName === undefined || routeSiteName === null ? '' : String(routeSiteName).trim()
|
||||||
|
if (name) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
const matchedSite = (this.$store.getters.zdList || []).find(item => item.siteId === this.siteId)
|
||||||
|
if (matchedSite && matchedSite.siteName) {
|
||||||
|
return matchedSite.siteName
|
||||||
|
}
|
||||||
|
return this.siteId || ''
|
||||||
|
},
|
||||||
// 获取设备类别
|
// 获取设备类别
|
||||||
getDeviceCategoryList() {
|
getDeviceCategoryList() {
|
||||||
getAllDeviceCategory().then(response => {
|
getAllDeviceCategory().then(response => {
|
||||||
this.deviceCategoryList = response?.data || []
|
this.deviceCategoryList = response?.data || []
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 查看设备电位表格
|
|
||||||
pointDetail(row, dataType) {
|
|
||||||
this.$refs.pointTable.showTable(row, dataType)
|
|
||||||
},
|
|
||||||
// 下载点位清单
|
|
||||||
downloadPointDetail(command) {
|
|
||||||
this.download('ems/pointMatch/export', {
|
|
||||||
siteId: this.siteId,
|
|
||||||
deviceCategory: command.code
|
|
||||||
}, `点位清单_${command.name}_${new Date().getTime()}.xlsx`)
|
|
||||||
},
|
|
||||||
// 上传点位清单
|
|
||||||
uploadPointDetail(command) {
|
|
||||||
this.$refs.pointUpload.showDialog({...command, siteId: this.siteId})
|
|
||||||
},
|
|
||||||
clearEditDeviceData() {
|
clearEditDeviceData() {
|
||||||
this.mode = '';
|
this.mode = '';
|
||||||
this.editDeviceId = ''
|
this.editDeviceId = ''
|
||||||
@ -312,33 +276,26 @@ export default {
|
|||||||
// 获取数据
|
// 获取数据
|
||||||
getData() {
|
getData() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const {siteId, pageNum, pageSize} = this
|
const {siteId, deviceCategory, pageNum, pageSize} = this
|
||||||
getDeviceInfoList({siteId, pageNum, pageSize}).then(response => {
|
getDeviceInfoList({siteId, deviceCategory, pageNum, pageSize}).then(response => {
|
||||||
this.tableData = response?.rows || [];
|
const selectedSiteName = this.getSelectedSiteName(this.$route.query.siteName)
|
||||||
|
this.selectedSiteName = selectedSiteName
|
||||||
|
this.tableData = (response?.rows || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
siteName: selectedSiteName
|
||||||
|
}));
|
||||||
this.totalSize = response?.total || 0
|
this.totalSize = response?.total || 0
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
|
||||||
//获取站点列表
|
|
||||||
getZdList() {
|
|
||||||
this.searchLoading = true
|
|
||||||
return getAllSites().then(response => {
|
|
||||||
this.siteList = response?.data || []
|
|
||||||
if (this.siteList.length > 0) this.siteId = this.siteList[0].siteId
|
|
||||||
}).finally(() => {
|
|
||||||
this.searchLoading = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loading = true
|
this.siteId = this.hasValidSiteId(this.$route.query.siteId) ? String(this.$route.query.siteId).trim() : ''
|
||||||
this.siteId = ''
|
this.selectedSiteName = this.getSelectedSiteName(this.$route.query.siteName)
|
||||||
this.pageNum = 1//每次搜索从1开始搜索
|
this.pageNum = 1//每次搜索从1开始搜索
|
||||||
this.getDeviceCategoryList()
|
this.getDeviceCategoryList()
|
||||||
this.getZdList().then(() => {
|
this.getData()
|
||||||
this.getData()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
1697
src/views/ems/site/zdlb/MonitorPointMapping.vue
Normal file
1697
src/views/ems/site/zdlb/MonitorPointMapping.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
|
||||||
<el-form :inline="true" class="select-container">
|
<el-form :inline="true" class="select-container">
|
||||||
@ -24,6 +23,7 @@
|
|||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
|
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
|
||||||
<el-button @click="onReset" native-type="button">重置</el-button>
|
<el-button @click="onReset" native-type="button">重置</el-button>
|
||||||
|
<el-button type="success" @click="openAddDialog" native-type="button">新增站点</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-table
|
<el-table
|
||||||
@ -40,6 +40,10 @@
|
|||||||
prop="siteName"
|
prop="siteName"
|
||||||
label="站点名称">
|
label="站点名称">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="siteShortName"
|
||||||
|
label="站点简称">
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="siteAddress"
|
prop="siteAddress"
|
||||||
label="站点地址"
|
label="站点地址"
|
||||||
@ -57,6 +61,21 @@
|
|||||||
prop="installCapacity"
|
prop="installCapacity"
|
||||||
label="装机容量">
|
label="装机容量">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="操作"
|
||||||
|
width="220"
|
||||||
|
fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="small" @click="openEditDialog(scope.row)">编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
:loading="syncingSiteId === scope.row.siteId"
|
||||||
|
:disabled="syncingSiteId === scope.row.siteId"
|
||||||
|
@click="syncWeather(scope.row)"
|
||||||
|
>同步天气</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-show="tableData.length>0"
|
v-show="tableData.length>0"
|
||||||
@ -71,67 +90,250 @@
|
|||||||
style="margin-top:15px;text-align: center"
|
style="margin-top:15px;text-align: center"
|
||||||
>
|
>
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:title="isEdit ? '编辑站点' : '新增站点'"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="640px"
|
||||||
|
:close-on-click-modal="false">
|
||||||
|
<el-form ref="siteForm" :model="siteForm" :rules="siteRules" label-width="100px">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="站点ID" prop="siteId">
|
||||||
|
<el-input v-model.trim="siteForm.siteId" :disabled="isEdit" placeholder="仅支持字母/数字/下划线" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="站点名称" prop="siteName">
|
||||||
|
<el-input v-model.trim="siteForm.siteName" placeholder="请输入站点名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="站点简称" prop="siteShortName">
|
||||||
|
<el-input v-model.trim="siteForm.siteShortName" placeholder="请输入站点简称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="运营时间" prop="runningTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="siteForm.runningTime"
|
||||||
|
type="datetime"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
placeholder="请选择运营时间"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="站点地址" prop="siteAddress">
|
||||||
|
<el-input v-model.trim="siteForm.siteAddress" placeholder="请输入站点地址" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="装机功率" prop="installPower">
|
||||||
|
<el-input v-model="siteForm.installPower" placeholder="请输入装机功率" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="装机容量" prop="installCapacity">
|
||||||
|
<el-input v-model="siteForm.installCapacity" placeholder="请输入装机容量" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="纬度" prop="latitude">
|
||||||
|
<el-input v-model="siteForm.latitude" placeholder="请输入纬度" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="经度" prop="longitude">
|
||||||
|
<el-input v-model="siteForm.longitude" placeholder="请输入经度" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model.trim="siteForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="submitSite">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getSiteInfoList} from '@/api/ems/site'
|
import {addSite, getSiteInfoList, syncSiteWeatherByDateRange, updateSite} from '@/api/ems/site'
|
||||||
import { formatDate } from '@/filters/ems'
|
import { formatDate } from '@/filters/ems'
|
||||||
|
|
||||||
|
const emptySiteForm = () => ({
|
||||||
|
id: undefined,
|
||||||
|
siteId: '',
|
||||||
|
siteName: '',
|
||||||
|
siteShortName: '',
|
||||||
|
siteAddress: '',
|
||||||
|
runningTime: '',
|
||||||
|
installPower: '',
|
||||||
|
installCapacity: '',
|
||||||
|
latitude: '',
|
||||||
|
longitude: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Zdlb",
|
name: 'Zdlb',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
siteName:"",
|
submitLoading: false,
|
||||||
pickerOptions:{
|
siteName: '',
|
||||||
|
pickerOptions: {
|
||||||
disabledDate(time) {
|
disabledDate(time) {
|
||||||
return time.getTime() > Date.now();
|
return time.getTime() > Date.now();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultDateRange:[],//默认展示的时间
|
defaultDateRange: [],
|
||||||
dateRange:[],//startTime,endTime
|
dateRange: [],
|
||||||
tableData:[],
|
tableData: [],
|
||||||
pageSize:10,//分页栏当前每个数据总数
|
pageSize: 10,
|
||||||
pageNum:1,//分页栏当前页数
|
pageNum: 1,
|
||||||
totalSize:0,//table表格数据总数
|
totalSize: 0,
|
||||||
|
syncingSiteId: '',
|
||||||
|
dialogVisible: false,
|
||||||
|
isEdit: false,
|
||||||
|
siteForm: emptySiteForm(),
|
||||||
|
siteRules: {
|
||||||
|
siteId: [
|
||||||
|
{ required: true, message: '请输入站点ID', trigger: 'blur' },
|
||||||
|
{ pattern: /^[A-Za-z0-9_]+$/, message: '站点ID仅支持字母、数字、下划线', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
siteName: [
|
||||||
|
{ required: true, message: '请输入站点名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
runningTime: [
|
||||||
|
{ required: true, message: '请选择运营时间', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
// 分页
|
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCurrentChange(val) {
|
handleCurrentChange(val) {
|
||||||
this.pageNum = val
|
this.pageNum = val
|
||||||
this.$nextTick(()=>{
|
this.$nextTick(() => {
|
||||||
this.getData()
|
this.getData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 搜索
|
onSearch() {
|
||||||
onSearch(){
|
this.pageNum = 1
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 重置
|
onReset() {
|
||||||
onReset(){
|
this.siteName = ''
|
||||||
this.siteName=''
|
this.dateRange = []
|
||||||
this.dateRange=[]
|
this.pageNum = 1
|
||||||
this.pageNum =1//每次搜索从1开始搜索
|
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
// 获取数据
|
getSyncDateRange() {
|
||||||
getData(){
|
const now = new Date()
|
||||||
this.loading=true
|
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||||
const {siteName,pageNum,pageSize} =this
|
return [formatDate(monthStart), formatDate(now)]
|
||||||
const [startTime='',endTime='']=(this.dateRange || [])
|
},
|
||||||
getSiteInfoList({siteName,pageSize,pageNum,startTime:formatDate(startTime),endTime:formatDate(endTime)}).then(response => {
|
syncWeather(row) {
|
||||||
this.tableData=response?.rows || [];
|
const siteId = row?.siteId
|
||||||
|
if (!siteId) {
|
||||||
|
this.$message.warning('站点ID为空,无法同步天气')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const [startTime, endTime] = this.getSyncDateRange()
|
||||||
|
this.$confirm(`将同步站点 ${siteId} 在 ${startTime} 至 ${endTime} 的天气数据,是否继续?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.syncingSiteId = siteId
|
||||||
|
return syncSiteWeatherByDateRange({siteId, startTime, endTime})
|
||||||
|
}).then((response) => {
|
||||||
|
const result = response?.data || {}
|
||||||
|
const successDays = result.successDays ?? 0
|
||||||
|
const totalDays = result.totalDays ?? 0
|
||||||
|
this.$message.success(`天气同步完成(${successDays}/${totalDays}天)`)
|
||||||
|
}).catch((err) => {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
this.$message.error('天气同步失败')
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.syncingSiteId = ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getData() {
|
||||||
|
this.loading = true
|
||||||
|
const {siteName, pageNum, pageSize} = this
|
||||||
|
const [startTime = '', endTime = ''] = (this.dateRange || [])
|
||||||
|
getSiteInfoList({siteName, pageSize, pageNum, startTime: formatDate(startTime), endTime: formatDate(endTime)}).then(response => {
|
||||||
|
this.tableData = response?.rows || [];
|
||||||
this.totalSize = response?.total || 0
|
this.totalSize = response?.total || 0
|
||||||
}).finally(() => {this.loading=false})
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
openAddDialog() {
|
||||||
|
this.isEdit = false
|
||||||
|
this.siteForm = emptySiteForm()
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.siteForm && this.$refs.siteForm.clearValidate()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEditDialog(row) {
|
||||||
|
this.isEdit = true
|
||||||
|
this.siteForm = {
|
||||||
|
id: row.id,
|
||||||
|
siteId: row.siteId || '',
|
||||||
|
siteName: row.siteName || '',
|
||||||
|
siteShortName: row.siteShortName || '',
|
||||||
|
siteAddress: row.siteAddress || '',
|
||||||
|
runningTime: row.runningTime || '',
|
||||||
|
installPower: row.installPower || '',
|
||||||
|
installCapacity: row.installCapacity || '',
|
||||||
|
latitude: row.latitude || '',
|
||||||
|
longitude: row.longitude || '',
|
||||||
|
remark: row.remark || ''
|
||||||
|
}
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.siteForm && this.$refs.siteForm.clearValidate()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitSite() {
|
||||||
|
this.$refs.siteForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.submitLoading = true
|
||||||
|
const request = this.isEdit ? updateSite : addSite
|
||||||
|
request(this.siteForm).then(() => {
|
||||||
|
this.$message.success(this.isEdit ? '编辑成功' : '新增成功')
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.getData()
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.onReset()
|
this.onReset()
|
||||||
|
|||||||
@ -1,95 +1,188 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="zddtChart" style="height: 100%;width:100%"></div>
|
<div class="map-wrapper">
|
||||||
|
<div ref="mapRef" class="map-canvas"></div>
|
||||||
|
<div v-if="selectedAddress" class="map-center-address">{{ selectedAddress }}</div>
|
||||||
|
<div v-if="!hasPoint" class="map-empty">暂无站点坐标</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as echarts from 'echarts'
|
const TDT_SCRIPT_ID = 'tianditu-js-sdk'
|
||||||
import resize from '@/mixins/ems/resize'
|
let tdtScriptLoading = null
|
||||||
import china from '@/data/ems/china.json'//中国地图数据
|
|
||||||
import 'echarts/lib/chart/map';
|
function loadTdtScript(tk) {
|
||||||
echarts.registerMap('china', { geoJSON: china }); //注册可用地图
|
if (window.T) return Promise.resolve(window.T)
|
||||||
|
if (tdtScriptLoading) return tdtScriptLoading
|
||||||
|
tdtScriptLoading = new Promise((resolve, reject) => {
|
||||||
|
const oldScript = document.getElementById(TDT_SCRIPT_ID)
|
||||||
|
if (oldScript) {
|
||||||
|
oldScript.addEventListener('load', () => resolve(window.T))
|
||||||
|
oldScript.addEventListener('error', () => reject(new Error('天地图脚本加载失败')))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.id = TDT_SCRIPT_ID
|
||||||
|
script.src = `https://api.tianditu.gov.cn/api?v=4.0&tk=${tk}`
|
||||||
|
script.async = true
|
||||||
|
script.onload = () => {
|
||||||
|
if (window.T) resolve(window.T)
|
||||||
|
else reject(new Error('天地图对象未初始化'))
|
||||||
|
}
|
||||||
|
script.onerror = () => reject(new Error('天地图脚本加载失败'))
|
||||||
|
document.body.appendChild(script)
|
||||||
|
})
|
||||||
|
return tdtScriptLoading
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [resize],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
chart: null,
|
hasPoint: false,
|
||||||
|
selectedAddress: '',
|
||||||
|
map: null,
|
||||||
|
overlays: [],
|
||||||
|
pendingPayload: null,
|
||||||
|
mapConfig: {
|
||||||
|
zoom: 12,
|
||||||
|
selectedZoom: 15,
|
||||||
|
tk: '01e99ab4472430e1c7dbfe4b5db99787'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => {
|
this.initMap()
|
||||||
this.initChart()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (!this.chart) {
|
this.clearOverlays()
|
||||||
return
|
this.map = null
|
||||||
}
|
|
||||||
this.chart.dispose()
|
|
||||||
this.chart = null
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initChart() {
|
async initMap() {
|
||||||
// ECharts 默认有提供了一个简单的加载动画。只需要调用 showLoading 方法显示。数据加载完成后再调用 hideLoading 方法隐藏加载动画。
|
try {
|
||||||
this.chart = echarts.init(document.querySelector('#zddtChart'))
|
await loadTdtScript(this.mapConfig.tk)
|
||||||
|
if (!this.$refs.mapRef || !window.T) return
|
||||||
|
this.map = new window.T.Map(this.$refs.mapRef)
|
||||||
|
const defaultCenter = new window.T.LngLat(104.1, 35.9)
|
||||||
|
this.map.centerAndZoom(defaultCenter, 5)
|
||||||
|
if (this.pendingPayload) {
|
||||||
|
this.renderPayload(this.pendingPayload)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 页面可继续使用,地图只显示空态
|
||||||
|
this.hasPoint = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setOption(data) {
|
normalizePoint(site = {}) {
|
||||||
this.chart.setOption({
|
const name = site.siteName || site.name || ''
|
||||||
color:['#FFBD00'],
|
const address = site.siteAddress || site.address || ''
|
||||||
backgroundColor: 'transparent', //背景色
|
const value = site.value || site.siteLocation || []
|
||||||
geo: { //地理坐标系组件 地理坐标系组件用于地图的绘制,支持在地理坐标系上绘制
|
const lonSource = site.longitude !== undefined && site.longitude !== null ? site.longitude : value[0]
|
||||||
map: 'china', //地图类型 这儿展示的是中国地图
|
const latSource = site.latitude !== undefined && site.latitude !== null ? site.latitude : value[1]
|
||||||
aspectScale: 0.85,
|
const lon = Number(lonSource)
|
||||||
selectedMode: "single",// 开启单选
|
const lat = Number(latSource)
|
||||||
label: {
|
if (!lon || !lat) return null
|
||||||
show: true, //是否显示标签 此处指是否显示地图上的地区名字
|
return { name, address, lon, lat }
|
||||||
color: '#ffffff',
|
},
|
||||||
fontSize: 12
|
clearOverlays() {
|
||||||
},
|
if (!this.map || !this.overlays.length) return
|
||||||
roam: true, //是否开启鼠标缩放和平移漫游
|
this.overlays.forEach(item => this.map.removeOverLay(item))
|
||||||
itemStyle: {
|
this.overlays = []
|
||||||
areaColor: "#03365b",
|
},
|
||||||
borderColor: "#4bf3f9",
|
renderPayload(payload = {}) {
|
||||||
shadowColor: '#03365b', //阴影颜色
|
const isArrayPayload = Array.isArray(payload)
|
||||||
shadowOffsetX: 0, //阴影偏移量
|
const selectedRaw = isArrayPayload ? ((payload || [])[0] || {}) : (payload.selected || {})
|
||||||
shadowOffsetY: 0, //阴影偏移量
|
const sitesRaw = isArrayPayload ? [] : (payload.sites || [])
|
||||||
},
|
const selected = this.normalizePoint(selectedRaw)
|
||||||
emphasis: {
|
const points = (Array.isArray(sitesRaw) ? sitesRaw : [])
|
||||||
label: {
|
.map(item => this.normalizePoint(item))
|
||||||
show: true,
|
.filter(Boolean)
|
||||||
color: '#ffffff',
|
this.selectedAddress = selected?.address || ''
|
||||||
},
|
if (selected && !points.find(item => item.lon === selected.lon && item.lat === selected.lat)) {
|
||||||
itemStyle: {
|
points.push(selected)
|
||||||
areaColor: "#0f5d9d",
|
}
|
||||||
}
|
this.clearOverlays()
|
||||||
}
|
this.hasPoint = points.length > 0
|
||||||
},
|
if (!this.map || !points.length || !window.T) return
|
||||||
series: [
|
|
||||||
{
|
const viewPoints = []
|
||||||
type: "effectScatter",
|
points.forEach(item => {
|
||||||
coordinateSystem: "geo",
|
const lngLat = new window.T.LngLat(item.lon, item.lat)
|
||||||
showEffectOn: "render",
|
const marker = new window.T.Marker(lngLat)
|
||||||
data,
|
this.map.addOverLay(marker)
|
||||||
rippleEffect: {
|
this.overlays.push(marker)
|
||||||
brushType: "stroke",
|
viewPoints.push(lngLat)
|
||||||
scale: 5,
|
|
||||||
period: 2, // 秒数
|
|
||||||
},
|
|
||||||
symbolSize: 12,
|
|
||||||
clickable: false,
|
|
||||||
zlevel: 1,
|
|
||||||
label: {
|
|
||||||
formatter: "{b}",
|
|
||||||
position: "right",
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (selected && selected.name) {
|
||||||
|
const label = new window.T.Label({
|
||||||
|
text: selected.name,
|
||||||
|
position: new window.T.LngLat(selected.lon, selected.lat),
|
||||||
|
offset: new window.T.Point(8, -34)
|
||||||
|
})
|
||||||
|
this.map.addOverLay(label)
|
||||||
|
this.overlays.push(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
this.map.centerAndZoom(new window.T.LngLat(selected.lon, selected.lat), this.mapConfig.selectedZoom)
|
||||||
|
} else if (viewPoints.length === 1) {
|
||||||
|
this.map.centerAndZoom(viewPoints[0], this.mapConfig.zoom)
|
||||||
|
} else {
|
||||||
|
this.map.setViewport(viewPoints)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setOption(payload = {}) {
|
||||||
|
this.pendingPayload = payload
|
||||||
|
if (!this.map) return
|
||||||
|
this.renderPayload(payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.map-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-empty {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
background: rgba(245, 247, 250, 0.9);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-center-address {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, calc(-50% - 26px));
|
||||||
|
max-width: min(70%, 520px);
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: #ffffff;
|
||||||
|
background: rgba(0, 0, 0, 0.65);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
||||||
|
text-align: center;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,33 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ems-dashboard-editor-container" v-loading="loading">
|
<div class="ems-dashboard-editor-container" v-loading="loading">
|
||||||
<zd-info></zd-info>
|
<zd-info></zd-info>
|
||||||
<div class="ems-content-container ">
|
<div class="ems-content-container">
|
||||||
<div class="map-container">
|
<div class="map-container">
|
||||||
<map-chart ref="mapChart"/>
|
<div class="site-cards-wrapper" v-if="allSites.length > 0">
|
||||||
</div>
|
<button
|
||||||
<div class="zd-msg-container">
|
class="site-cards-arrow site-cards-arrow--left"
|
||||||
<div class="zd-msg-top">
|
type="button"
|
||||||
<zd-select ref="zdSelect" @submitSite="submitSite"></zd-select>
|
:disabled="!canScrollLeft"
|
||||||
<el-card class="common-card-container">
|
@click="scrollSiteCards('left')"
|
||||||
<div slot="header">
|
>
|
||||||
<span class="card-title">基本信息</span>
|
<i class="el-icon-arrow-left"></i>
|
||||||
<el-button style="float: right; padding: 3px 0" type="text" size="small" @click="toDzjk">查看详情</el-button>
|
</button>
|
||||||
|
<div ref="siteCards" class="site-cards" @scroll="updateScrollButtons">
|
||||||
|
<div
|
||||||
|
v-for="item in allSites"
|
||||||
|
:key="item.siteId"
|
||||||
|
class="site-card"
|
||||||
|
:class="{ active: isSameSite(item.siteId, singleSiteId) }"
|
||||||
|
@click="submitSite(item.siteId)"
|
||||||
|
>
|
||||||
|
<div class="site-card-name">{{ item.siteName || '-' }}</div>
|
||||||
|
<div class="site-card-info-row">
|
||||||
|
<span class="site-card-label">电站位置</span>
|
||||||
|
<span class="site-card-value site-card-value--address">{{ formatSiteCardField(item.siteAddress) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="site-card-info-row">
|
||||||
|
<span class="site-card-label">投运时间</span>
|
||||||
|
<span class="site-card-value">{{ formatSiteCardDate(item.runningTime) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="site-card-info-row">
|
||||||
|
<span class="site-card-label">装机功率(MWh)</span>
|
||||||
|
<span class="site-card-value">{{ formatSiteCardField(item.installPower) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="site-card-info-row">
|
||||||
|
<span class="site-card-label">装机容量(MWh)</span>
|
||||||
|
<span class="site-card-value">{{ formatSiteCardField(item.installCapacity) }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="single-zd-name">{{singleSiteName}}</div>
|
</div>
|
||||||
<!-- 四个方块-->
|
<button
|
||||||
<el-row :gutter="14">
|
class="site-cards-arrow site-cards-arrow--right"
|
||||||
<el-col :span="12" class="single-square-box-container" v-for="(item,index) in singleZdSqaure" :key="index+'singleSquareBox'">
|
type="button"
|
||||||
<single-square-box :data="item"></single-square-box>
|
:disabled="!canScrollRight"
|
||||||
</el-col>
|
@click="scrollSiteCards('right')"
|
||||||
</el-row>
|
>
|
||||||
<!-- 基本信息 -->
|
<i class="el-icon-arrow-right"></i>
|
||||||
<el-descriptions class="single-zd-info-container" :column="1" >
|
</button>
|
||||||
<el-descriptions-item v-for="(item,index) in singleZdInfo" :key="index+'singleZdInfo'" :label="item.title">{{item.value | formatNumber }}</el-descriptions-item>
|
</div>
|
||||||
</el-descriptions>
|
<div class="map-view">
|
||||||
<!-- echarts柱状图-->
|
<map-chart ref="mapChart"/>
|
||||||
<bar-chart ref="barChart"></bar-chart>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,94 +58,103 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ZdInfo from '@/components/Ems/ZdBaseInfo/index.vue'
|
import ZdInfo from '@/components/Ems/ZdBaseInfo/index.vue'
|
||||||
import ZdSelect from '@/components/Ems/ZdSelect/index.vue'
|
|
||||||
import SingleSquareBox from '@/components/Ems/SingleSquareBox/index.vue'
|
|
||||||
import BarChart from './BarChart.vue'
|
|
||||||
import MapChart from './MapChart.vue'
|
import MapChart from './MapChart.vue'
|
||||||
import {getSingleSiteBaseInfo} from '@/api/ems/zddt'
|
import { getAllSites } from '@/api/ems/zddt'
|
||||||
export default {
|
export default {
|
||||||
components:{ZdSelect,ZdInfo,SingleSquareBox,BarChart,MapChart},
|
components: { ZdInfo, MapChart },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading:false,
|
loading: false,
|
||||||
singleSiteId:'',
|
singleSiteId: '',
|
||||||
singleSiteName:'',
|
singleSiteName: '',
|
||||||
singleSiteLocation:[],
|
singleSiteAddress: '',
|
||||||
// 单个电站 四个方块数据
|
singleSiteLocation: [],
|
||||||
singleZdSqaure:[
|
allSites: [],
|
||||||
{
|
canScrollLeft: false,
|
||||||
title:'今日充电(kWh)',
|
canScrollRight: false
|
||||||
value:'',
|
|
||||||
bgColor:'#FFE5E5',
|
|
||||||
attr:'dayChargedCap'
|
|
||||||
},{
|
|
||||||
title:'累计充电(kWh)',
|
|
||||||
value:'',
|
|
||||||
bgColor:'#FFE5E5',
|
|
||||||
attr:'totalChargedCap'
|
|
||||||
},{
|
|
||||||
title:'今日放电(kWh)',
|
|
||||||
value:'',
|
|
||||||
bgColor:'#EEEBFF',
|
|
||||||
attr:'dayDisChargedCap'
|
|
||||||
},{
|
|
||||||
title:'累计放电(kWh)',
|
|
||||||
value:'',
|
|
||||||
bgColor:'#EEEBFF',
|
|
||||||
attr:'totalDisChargedCap'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// 单个电站 基本信息
|
|
||||||
singleZdInfo:[{
|
|
||||||
title:'电站位置',
|
|
||||||
value:'',
|
|
||||||
attr:'siteAddress'
|
|
||||||
},{
|
|
||||||
title:'投运时间',
|
|
||||||
value:'',
|
|
||||||
attr:'runningTime'
|
|
||||||
},{
|
|
||||||
title:'装机功率(MW)',
|
|
||||||
value:'',
|
|
||||||
attr:'installPower'
|
|
||||||
},{
|
|
||||||
title:'装机容量(MW)',
|
|
||||||
value:'',
|
|
||||||
attr:'installCapacity',
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadSites()
|
||||||
|
window.addEventListener('resize', this.updateScrollButtons)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('resize', this.updateScrollButtons)
|
||||||
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
isSameSite(siteId, selectedId) {
|
||||||
|
return String(siteId) === String(selectedId)
|
||||||
|
},
|
||||||
|
formatSiteCardField(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
formatSiteCardDate(value) {
|
||||||
|
if (!value) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
const text = String(value)
|
||||||
|
if (text.includes('T')) {
|
||||||
|
return text.slice(0, 10)
|
||||||
|
}
|
||||||
|
return text.length > 10 ? text.slice(0, 10) : text
|
||||||
|
},
|
||||||
|
loadSites() {
|
||||||
|
this.loading = true
|
||||||
|
getAllSites().then(response => {
|
||||||
|
this.allSites = response?.data || []
|
||||||
|
if (this.allSites.length > 0) {
|
||||||
|
this.submitSite(this.allSites[0].siteId)
|
||||||
|
} else {
|
||||||
|
this.updateMapMarkers()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateScrollButtons()
|
||||||
|
})
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateScrollButtons() {
|
||||||
|
const container = this.$refs.siteCards
|
||||||
|
if (!container) {
|
||||||
|
this.canScrollLeft = false
|
||||||
|
this.canScrollRight = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const maxScrollLeft = container.scrollWidth - container.clientWidth
|
||||||
|
this.canScrollLeft = container.scrollLeft > 0
|
||||||
|
this.canScrollRight = maxScrollLeft > 0 && container.scrollLeft < maxScrollLeft - 1
|
||||||
|
},
|
||||||
|
scrollSiteCards(direction) {
|
||||||
|
const container = this.$refs.siteCards
|
||||||
|
if (!container) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const amount = Math.max(container.clientWidth * 0.8, 240)
|
||||||
|
const delta = direction === 'left' ? -amount : amount
|
||||||
|
container.scrollBy({ left: delta, behavior: 'smooth' })
|
||||||
|
},
|
||||||
|
updateMapMarkers(){
|
||||||
|
this.$refs.mapChart && this.$refs.mapChart.setOption({
|
||||||
|
selected: {name:this.singleSiteName,address:this.singleSiteAddress,value:this.singleSiteLocation},
|
||||||
|
sites:this.allSites
|
||||||
|
})
|
||||||
|
},
|
||||||
// 站点选中
|
// 站点选中
|
||||||
submitSite(id){
|
submitSite(id){
|
||||||
if(this.singleSiteId === id){return console.log(`点击搜索按钮 搜索相同的站点id= ${id}不再调用获取基本信息接口`)}
|
|
||||||
this.loading=true
|
|
||||||
console.log('点击搜索按钮 选中的站点id',id)
|
|
||||||
this.singleSiteId = id
|
this.singleSiteId = id
|
||||||
this.$refs.zdSelect.searchLoading = true
|
const currentSite = this.allSites.find(item => this.isSameSite(item.siteId, id)) || {}
|
||||||
getSingleSiteBaseInfo(id).then(response => {
|
this.singleSiteName = currentSite.siteName || ''
|
||||||
console.log('单个站点详情数据',response)
|
this.singleSiteAddress = currentSite.siteAddress || ''
|
||||||
const res = response?.data || {}
|
this.singleSiteLocation = currentSite.siteLocation || []
|
||||||
this.singleSiteName = res?.siteName || ''//站点名称
|
if (!this.singleSiteLocation.length) {
|
||||||
this.singleSiteLocation = res?.siteLocation || []//站点坐标
|
this.singleSiteLocation = [currentSite.longitude, currentSite.latitude].filter(item => item !== undefined && item !== null)
|
||||||
this.singleZdSqaure.forEach(item=>{
|
}
|
||||||
item.value =res[item.attr]
|
this.$nextTick(() => {
|
||||||
})
|
this.updateMapMarkers()
|
||||||
this.singleZdInfo.forEach(item=>{
|
|
||||||
item.value = res[item.attr]
|
|
||||||
})
|
|
||||||
this.$refs.barChart.setOption(res?.sevenDayDisChargeStats || [])
|
|
||||||
this.$refs.mapChart.setOption([{name:this.singleSiteName,value:this.singleSiteLocation}])
|
|
||||||
}).finally(() => {this.$refs.zdSelect.searchLoading = false;this.loading=false})
|
|
||||||
},
|
|
||||||
//跳转单站监控页面
|
|
||||||
toDzjk(){
|
|
||||||
this.$router.push({
|
|
||||||
path:'/dzjk',
|
|
||||||
query:{
|
|
||||||
siteId:this.singleSiteId,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -134,29 +165,119 @@ export default {
|
|||||||
.ems-content-container{
|
.ems-content-container{
|
||||||
display: flex;
|
display: flex;
|
||||||
padding:24px;
|
padding:24px;
|
||||||
padding-right: 0;
|
|
||||||
.map-container{
|
.map-container{
|
||||||
flex:auto;
|
flex:auto;
|
||||||
}
|
min-width: 0;
|
||||||
.zd-msg-container{
|
display: flex;
|
||||||
width: 500px;
|
flex-direction: column;
|
||||||
padding: 24px;
|
gap: 12px;
|
||||||
.single-zd-name{
|
.site-cards-wrapper{
|
||||||
font-weight: 500;
|
display: flex;
|
||||||
line-height: 23px;
|
align-items: center;
|
||||||
color: #FFBD00;
|
gap: 8px;
|
||||||
font-size: 16px;
|
.site-cards-arrow{
|
||||||
margin-bottom: 24px;
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||||
|
color: #606266;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: all .2s ease;
|
||||||
|
&:hover:not(:disabled){
|
||||||
|
color: #2b74ff;
|
||||||
|
box-shadow: 0 0 0 1px #2b74ff inset;
|
||||||
|
}
|
||||||
|
&:disabled{
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: #c0c4cc;
|
||||||
|
box-shadow: 0 0 0 1px #ebeef5 inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.single-square-box-container{
|
.site-cards{
|
||||||
height: 78px;
|
display: flex;
|
||||||
box-sizing: border-box;
|
gap: 10px;
|
||||||
margin-bottom: 10px;
|
overflow-x: auto;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
&::-webkit-scrollbar{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.site-card{
|
||||||
|
flex: 0 0 360px;
|
||||||
|
height: 166px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .2s ease;
|
||||||
|
.site-card-name{
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 22px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.site-card-info-row{
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
.site-card-label{
|
||||||
|
flex: 0 0 84px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.site-card-value{
|
||||||
|
flex: 1;
|
||||||
|
color: #606266;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.site-card-value--address{
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-all;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
text-overflow: unset;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
&:hover{
|
||||||
|
border-color: #c0d3ff;
|
||||||
|
}
|
||||||
|
&.active{
|
||||||
|
border-color: #2b74ff;
|
||||||
|
box-shadow: 0 0 0 1px rgba(43,116,255,.15) inset;
|
||||||
|
background: #f5f9ff;
|
||||||
|
.site-card-name{
|
||||||
|
color: #2b74ff;
|
||||||
|
}
|
||||||
|
.site-card-value{
|
||||||
|
color: #5f8ee3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.single-zd-info-container{
|
.map-view{
|
||||||
font-size: 12px;
|
height: 70vh;
|
||||||
margin-top: 10px;
|
min-height: 520px;
|
||||||
color:#666666;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<img :src="loginBg" alt="" srcset="" class="login-bg" />
|
<transition :name="bgTransitionName">
|
||||||
|
<img :key="bgNum" :src="loginBg" alt="" srcset="" class="login-bg" />
|
||||||
|
</transition>
|
||||||
<el-form
|
<el-form
|
||||||
ref="loginForm"
|
ref="loginForm"
|
||||||
:model="loginForm"
|
:model="loginForm"
|
||||||
@ -107,6 +109,8 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
bgNum: 1,
|
bgNum: 1,
|
||||||
|
bgTransitionName: "bg-slide",
|
||||||
|
bgTransitionNames: ["bg-slide", "bg-zoom", "bg-blur"],
|
||||||
codeUrl: "",
|
codeUrl: "",
|
||||||
loginForm: {
|
loginForm: {
|
||||||
username: "admin",
|
username: "admin",
|
||||||
@ -148,7 +152,12 @@ export default {
|
|||||||
this.updateInterval(this.updateBgNum, 5000);
|
this.updateInterval(this.updateBgNum, 5000);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
randomBgTransitionName() {
|
||||||
|
const index = Math.floor(Math.random() * this.bgTransitionNames.length);
|
||||||
|
return this.bgTransitionNames[index];
|
||||||
|
},
|
||||||
updateBgNum() {
|
updateBgNum() {
|
||||||
|
this.bgTransitionName = this.randomBgTransitionName();
|
||||||
if (this.bgNum >= 4) this.bgNum = 0;
|
if (this.bgNum >= 4) this.bgNum = 0;
|
||||||
this.bgNum += 1;
|
this.bgNum += 1;
|
||||||
},
|
},
|
||||||
@ -210,6 +219,10 @@ export default {
|
|||||||
|
|
||||||
<style rel="stylesheet/scss" lang="scss">
|
<style rel="stylesheet/scss" lang="scss">
|
||||||
.login {
|
.login {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
padding-left: 180px;
|
padding-left: 180px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: left;
|
justify-content: left;
|
||||||
@ -224,6 +237,60 @@ export default {
|
|||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
will-change: opacity, transform, filter;
|
||||||
|
}
|
||||||
|
.bg-fade-enter-active,
|
||||||
|
.bg-fade-leave-active {
|
||||||
|
transition: opacity 0.9s ease, transform 0.9s ease;
|
||||||
|
}
|
||||||
|
.bg-fade-enter {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.04);
|
||||||
|
}
|
||||||
|
.bg-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
.bg-slide-enter-active,
|
||||||
|
.bg-slide-leave-active {
|
||||||
|
transition: opacity 0.85s ease, transform 0.85s ease;
|
||||||
|
}
|
||||||
|
.bg-slide-enter {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(3.5%, 0, 0) scale(1.01);
|
||||||
|
}
|
||||||
|
.bg-slide-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(-3.5%, 0, 0) scale(0.99);
|
||||||
|
}
|
||||||
|
.bg-zoom-enter-active,
|
||||||
|
.bg-zoom-leave-active {
|
||||||
|
transition: opacity 1s ease, transform 1s ease;
|
||||||
|
}
|
||||||
|
.bg-zoom-enter {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.08);
|
||||||
|
}
|
||||||
|
.bg-zoom-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.94);
|
||||||
|
}
|
||||||
|
.bg-blur-enter-active,
|
||||||
|
.bg-blur-leave-active {
|
||||||
|
transition: opacity 0.9s ease, filter 0.9s ease, transform 0.9s ease;
|
||||||
|
}
|
||||||
|
.bg-blur-enter {
|
||||||
|
opacity: 0;
|
||||||
|
filter: blur(8px);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
.bg-blur-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
filter: blur(6px);
|
||||||
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
.login-logo {
|
.login-logo {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@ -6,30 +6,39 @@
|
|||||||
<pane size="16">
|
<pane size="16">
|
||||||
<el-col>
|
<el-col>
|
||||||
<div class="head-container">
|
<div class="head-container">
|
||||||
<el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" />
|
<el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small"
|
||||||
|
prefix-icon="el-icon-search" style="margin-bottom: 20px"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="head-container">
|
<div class="head-container">
|
||||||
<el-tree :data="deptOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current @node-click="handleNodeClick" />
|
<el-tree :data="deptOptions" :props="defaultProps" :expand-on-click-node="false"
|
||||||
|
:filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current
|
||||||
|
@node-click="handleNodeClick"/>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</pane>
|
</pane>
|
||||||
<!--用户数据-->
|
<!--用户数据-->
|
||||||
<pane size="84">
|
<pane size="84">
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
|
||||||
|
label-width="68px">
|
||||||
<el-form-item label="用户名称" prop="userName">
|
<el-form-item label="用户名称" prop="userName">
|
||||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
|
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px"
|
||||||
|
@keyup.enter.native="handleQuery"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="手机号码" prop="phonenumber">
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
|
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px"
|
||||||
|
@keyup.enter.native="handleQuery"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
|
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
|
||||||
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label"
|
||||||
|
:value="dict.value"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="创建时间">
|
<el-form-item label="创建时间">
|
||||||
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
|
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange"
|
||||||
|
range-separator="-" start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"></el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
@ -39,33 +48,48 @@
|
|||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:user:add']">新增</el-button>
|
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||||
|
v-hasPermi="['system:user:add']">新增
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['system:user:edit']">修改</el-button>
|
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
|
||||||
|
v-hasPermi="['system:user:edit']">修改
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:user:remove']">删除</el-button>
|
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple"
|
||||||
|
@click="handleDelete" v-hasPermi="['system:user:remove']">删除
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['system:user:import']">导入</el-button>
|
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport"
|
||||||
|
v-hasPermi="['system:user:import']">导入
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['system:user:export']">导出</el-button>
|
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
|
||||||
|
v-hasPermi="['system:user:export']">导出
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection" width="50" align="center" />
|
<el-table-column type="selection" width="50" align="center"/>
|
||||||
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
|
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible"/>
|
||||||
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
|
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible"
|
||||||
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
|
:show-overflow-tooltip="true"/>
|
||||||
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
|
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible"
|
||||||
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
|
:show-overflow-tooltip="true"/>
|
||||||
|
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible"
|
||||||
|
:show-overflow-tooltip="true"/>
|
||||||
|
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber"
|
||||||
|
v-if="columns[4].visible" width="120"/>
|
||||||
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
|
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
|
||||||
|
@change="handleStatusChange(scope.row)"></el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
|
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
|
||||||
@ -75,20 +99,30 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope" v-if="scope.row.userId !== 1">
|
<template slot-scope="scope" v-if="scope.row.userId !== 1">
|
||||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']">修改</el-button>
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']">删除</el-button>
|
v-hasPermi="['system:user:edit']">修改
|
||||||
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
|
</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||||
|
v-hasPermi="['system:user:remove']">删除
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)"
|
||||||
|
v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
|
||||||
<el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
|
<el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="handleResetPwd" icon="el-icon-key" v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
|
<el-dropdown-item command="handleResetPwd" icon="el-icon-key"
|
||||||
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check" v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
|
v-hasPermi="['system:user:resetPwd']">重置密码
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check"
|
||||||
|
v-hasPermi="['system:user:edit']">分配角色
|
||||||
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||||
|
:limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</pane>
|
</pane>
|
||||||
</splitpanes>
|
</splitpanes>
|
||||||
@ -100,36 +134,38 @@
|
|||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="用户昵称" prop="nickName">
|
<el-form-item label="用户昵称" prop="nickName">
|
||||||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
|
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="归属部门" prop="deptId">
|
<el-form-item label="归属部门" prop="deptId">
|
||||||
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择归属部门" />
|
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true"
|
||||||
|
placeholder="请选择归属部门"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="手机号码" prop="phonenumber">
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="邮箱" prop="email">
|
<el-form-item label="邮箱" prop="email">
|
||||||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
|
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
|
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
|
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20"
|
||||||
|
show-password/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -137,14 +173,17 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="用户性别">
|
<el-form-item label="用户性别">
|
||||||
<el-select v-model="form.sex" placeholder="请选择性别">
|
<el-select v-model="form.sex" placeholder="请选择性别">
|
||||||
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label"
|
||||||
|
:value="dict.value"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-radio-group v-model="form.status">
|
<el-radio-group v-model="form.status">
|
||||||
<el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
|
<el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">
|
||||||
|
{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -153,14 +192,33 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="岗位">
|
<el-form-item label="岗位">
|
||||||
<el-select v-model="form.postIds" multiple placeholder="请选择岗位">
|
<el-select v-model="form.postIds" multiple placeholder="请选择岗位">
|
||||||
<el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId" :disabled="item.status == 1" ></el-option>
|
<el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId"
|
||||||
|
:disabled="item.status == 1"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="角色">
|
<el-form-item label="角色">
|
||||||
<el-select v-model="form.roleIds" multiple placeholder="请选择角色">
|
<el-select v-model="form.roleIds" multiple placeholder="请选择角色">
|
||||||
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1"></el-option>
|
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId"
|
||||||
|
:disabled="item.status == 1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="归属站点" name="belongSite">
|
||||||
|
<el-select
|
||||||
|
v-model="form.belongSite"
|
||||||
|
multiple
|
||||||
|
collapse-tags
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width:100%"
|
||||||
|
@change="selectBelongSite">
|
||||||
|
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList"
|
||||||
|
:disabled="item.siteId!== 'all' && (form.belongSite || []).includes('all')"
|
||||||
|
:key="index+'zdxeSelect'"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -181,15 +239,20 @@
|
|||||||
|
|
||||||
<!-- 用户导入对话框 -->
|
<!-- 用户导入对话框 -->
|
||||||
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
|
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
|
||||||
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
|
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers"
|
||||||
|
:action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading"
|
||||||
|
:on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
|
||||||
<i class="el-icon-upload"></i>
|
<i class="el-icon-upload"></i>
|
||||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
<div class="el-upload__tip text-center" slot="tip">
|
<div class="el-upload__tip text-center" slot="tip">
|
||||||
<div class="el-upload__tip" slot="tip">
|
<div class="el-upload__tip" slot="tip">
|
||||||
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
|
<el-checkbox v-model="upload.updateSupport"/>
|
||||||
|
是否更新已经存在的用户数据
|
||||||
</div>
|
</div>
|
||||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
|
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline"
|
||||||
|
@click="importTemplate">下载模板
|
||||||
|
</el-link>
|
||||||
</div>
|
</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
@ -201,17 +264,27 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/system/user"
|
import {
|
||||||
import { getToken } from "@/utils/auth"
|
addUser,
|
||||||
|
changeUserStatus,
|
||||||
|
delUser,
|
||||||
|
deptTreeSelect,
|
||||||
|
getUser,
|
||||||
|
listUser,
|
||||||
|
resetUserPwd,
|
||||||
|
updateUser
|
||||||
|
} from "@/api/system/user"
|
||||||
|
import {getAllSites} from '@/api/ems/zddt'
|
||||||
|
import {getToken} from "@/utils/auth"
|
||||||
import Treeselect from "@riophae/vue-treeselect"
|
import Treeselect from "@riophae/vue-treeselect"
|
||||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
|
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
|
||||||
import { Splitpanes, Pane } from "splitpanes"
|
import {Pane, Splitpanes} from "splitpanes"
|
||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "User",
|
name: "User",
|
||||||
dicts: ['sys_normal_disable', 'sys_user_sex'],
|
dicts: ['sys_normal_disable', 'sys_user_sex'],
|
||||||
components: { Treeselect, Splitpanes, Pane },
|
components: {Treeselect, Splitpanes, Pane},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
@ -228,6 +301,8 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
// 用户表格数据
|
// 用户表格数据
|
||||||
userList: null,
|
userList: null,
|
||||||
|
// 站点列表数据
|
||||||
|
siteList: [],
|
||||||
// 弹出层标题
|
// 弹出层标题
|
||||||
title: "",
|
title: "",
|
||||||
// 所有部门树选项
|
// 所有部门树选项
|
||||||
@ -263,7 +338,7 @@ export default {
|
|||||||
// 是否更新已经存在的用户数据
|
// 是否更新已经存在的用户数据
|
||||||
updateSupport: 0,
|
updateSupport: 0,
|
||||||
// 设置上传的请求头部
|
// 设置上传的请求头部
|
||||||
headers: { Authorization: "Bearer " + getToken() },
|
headers: {Authorization: "Bearer " + getToken()},
|
||||||
// 上传的地址
|
// 上传的地址
|
||||||
url: process.env.VUE_APP_BASE_API + "/system/user/importData"
|
url: process.env.VUE_APP_BASE_API + "/system/user/importData"
|
||||||
},
|
},
|
||||||
@ -278,27 +353,27 @@ export default {
|
|||||||
},
|
},
|
||||||
// 列信息
|
// 列信息
|
||||||
columns: [
|
columns: [
|
||||||
{ key: 0, label: `用户编号`, visible: true },
|
{key: 0, label: `用户编号`, visible: true},
|
||||||
{ key: 1, label: `用户名称`, visible: true },
|
{key: 1, label: `用户名称`, visible: true},
|
||||||
{ key: 2, label: `用户昵称`, visible: true },
|
{key: 2, label: `用户昵称`, visible: true},
|
||||||
{ key: 3, label: `部门`, visible: true },
|
{key: 3, label: `部门`, visible: true},
|
||||||
{ key: 4, label: `手机号码`, visible: true },
|
{key: 4, label: `手机号码`, visible: true},
|
||||||
{ key: 5, label: `状态`, visible: true },
|
{key: 5, label: `状态`, visible: true},
|
||||||
{ key: 6, label: `创建时间`, visible: true }
|
{key: 6, label: `创建时间`, visible: true}
|
||||||
],
|
],
|
||||||
// 表单校验
|
// 表单校验
|
||||||
rules: {
|
rules: {
|
||||||
userName: [
|
userName: [
|
||||||
{ required: true, message: "用户名称不能为空", trigger: "blur" },
|
{required: true, message: "用户名称不能为空", trigger: "blur"},
|
||||||
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
|
{min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur'}
|
||||||
],
|
],
|
||||||
nickName: [
|
nickName: [
|
||||||
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
|
{required: true, message: "用户昵称不能为空", trigger: "blur"}
|
||||||
],
|
],
|
||||||
password: [
|
password: [
|
||||||
{ required: true, message: "用户密码不能为空", trigger: "blur" },
|
{required: true, message: "用户密码不能为空", trigger: "blur"},
|
||||||
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
|
{min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur'},
|
||||||
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
|
{pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur"}
|
||||||
],
|
],
|
||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
@ -313,7 +388,8 @@ export default {
|
|||||||
message: "请输入正确的手机号码",
|
message: "请输入正确的手机号码",
|
||||||
trigger: "blur"
|
trigger: "blur"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -324,6 +400,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.getZdList()
|
||||||
this.getList()
|
this.getList()
|
||||||
this.getDeptTree()
|
this.getDeptTree()
|
||||||
this.getConfigKey("sys.user.initPassword").then(response => {
|
this.getConfigKey("sys.user.initPassword").then(response => {
|
||||||
@ -331,14 +408,37 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
selectBelongSite(data) {
|
||||||
|
console.log('选中的站点', data)
|
||||||
|
if (data.includes("all")) {
|
||||||
|
this.form.belongSite = ['all']
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.siteList.length && data.length === (this.siteList.length - 1)) {
|
||||||
|
this.form.belongSite = ['all']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//获取站点列表
|
||||||
|
getZdList() {
|
||||||
|
return getAllSites().then(response => {
|
||||||
|
this.siteList = response?.data || []
|
||||||
|
if (this.siteList.length > 0) {
|
||||||
|
this.siteList.unshift({
|
||||||
|
id: 'all',
|
||||||
|
siteId: "all",
|
||||||
|
siteName: "全部"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
/** 查询用户列表 */
|
/** 查询用户列表 */
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
|
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
|
||||||
this.userList = response.rows
|
this.userList = response.rows
|
||||||
this.total = response.total
|
this.total = response.total
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
/** 查询部门下拉树结构 */
|
/** 查询部门下拉树结构 */
|
||||||
@ -373,11 +473,11 @@ export default {
|
|||||||
// 用户状态修改
|
// 用户状态修改
|
||||||
handleStatusChange(row) {
|
handleStatusChange(row) {
|
||||||
let text = row.status === "0" ? "启用" : "停用"
|
let text = row.status === "0" ? "启用" : "停用"
|
||||||
this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function() {
|
this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
|
||||||
return changeUserStatus(row.userId, row.status)
|
return changeUserStatus(row.userId, row.status)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$modal.msgSuccess(text + "成功")
|
this.$modal.msgSuccess(text + "成功")
|
||||||
}).catch(function() {
|
}).catch(function () {
|
||||||
row.status = row.status === "0" ? "1" : "0"
|
row.status = row.status === "0" ? "1" : "0"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -400,7 +500,8 @@ export default {
|
|||||||
status: "0",
|
status: "0",
|
||||||
remark: undefined,
|
remark: undefined,
|
||||||
postIds: [],
|
postIds: [],
|
||||||
roleIds: []
|
roleIds: [],
|
||||||
|
belongSite: []
|
||||||
}
|
}
|
||||||
this.resetForm("form")
|
this.resetForm("form")
|
||||||
},
|
},
|
||||||
@ -455,6 +556,7 @@ export default {
|
|||||||
this.form = response.data
|
this.form = response.data
|
||||||
this.postOptions = response.posts
|
this.postOptions = response.posts
|
||||||
this.roleOptions = response.roles
|
this.roleOptions = response.roles
|
||||||
|
this.$set(this.form, "belongSite", response?.data?.belongSite ? JSON.parse(response.data.belongSite) : [])
|
||||||
this.$set(this.form, "postIds", response.postIds)
|
this.$set(this.form, "postIds", response.postIds)
|
||||||
this.$set(this.form, "roleIds", response.roleIds)
|
this.$set(this.form, "roleIds", response.roleIds)
|
||||||
this.open = true
|
this.open = true
|
||||||
@ -475,29 +577,30 @@ export default {
|
|||||||
return "不能包含非法字符:< > \" ' \\\ |"
|
return "不能包含非法字符:< > \" ' \\\ |"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}).then(({ value }) => {
|
}).then(({value}) => {
|
||||||
resetUserPwd(row.userId, value).then(response => {
|
resetUserPwd(row.userId, value).then(response => {
|
||||||
this.$modal.msgSuccess("修改成功,新密码是:" + value)
|
this.$modal.msgSuccess("修改成功,新密码是:" + value)
|
||||||
})
|
})
|
||||||
}).catch(() => {})
|
}).catch(() => {
|
||||||
|
})
|
||||||
},
|
},
|
||||||
/** 分配角色操作 */
|
/** 分配角色操作 */
|
||||||
handleAuthRole: function(row) {
|
handleAuthRole: function (row) {
|
||||||
const userId = row.userId
|
const userId = row.userId
|
||||||
this.$router.push("/system/user-auth/role/" + userId)
|
this.$router.push("/system/user-auth/role/" + userId)
|
||||||
},
|
},
|
||||||
/** 提交按钮 */
|
/** 提交按钮 */
|
||||||
submitForm: function() {
|
submitForm: function () {
|
||||||
this.$refs["form"].validate(valid => {
|
this.$refs["form"].validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
if (this.form.userId != undefined) {
|
if (this.form.userId != undefined) {
|
||||||
updateUser(this.form).then(response => {
|
updateUser({...this.form, belongSite: JSON.stringify(this.form.belongSite)}).then(response => {
|
||||||
this.$modal.msgSuccess("修改成功")
|
this.$modal.msgSuccess("修改成功")
|
||||||
this.open = false
|
this.open = false
|
||||||
this.getList()
|
this.getList()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
addUser(this.form).then(response => {
|
addUser({...this.form, belongSite: JSON.stringify(this.form.belongSite)}).then(response => {
|
||||||
this.$modal.msgSuccess("新增成功")
|
this.$modal.msgSuccess("新增成功")
|
||||||
this.open = false
|
this.open = false
|
||||||
this.getList()
|
this.getList()
|
||||||
@ -509,12 +612,13 @@ export default {
|
|||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const userIds = row.userId || this.ids
|
const userIds = row.userId || this.ids
|
||||||
this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function() {
|
this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function () {
|
||||||
return delUser(userIds)
|
return delUser(userIds)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.getList()
|
this.getList()
|
||||||
this.$modal.msgSuccess("删除成功")
|
this.$modal.msgSuccess("删除成功")
|
||||||
}).catch(() => {})
|
}).catch(() => {
|
||||||
|
})
|
||||||
},
|
},
|
||||||
/** 导出按钮操作 */
|
/** 导出按钮操作 */
|
||||||
handleExport() {
|
handleExport() {
|
||||||
@ -529,8 +633,7 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 下载模板操作 */
|
/** 下载模板操作 */
|
||||||
importTemplate() {
|
importTemplate() {
|
||||||
this.download('system/user/importTemplate', {
|
this.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`)
|
||||||
}, `user_template_${new Date().getTime()}.xlsx`)
|
|
||||||
},
|
},
|
||||||
// 文件上传中处理
|
// 文件上传中处理
|
||||||
handleFileUploadProgress(event, file, fileList) {
|
handleFileUploadProgress(event, file, fileList) {
|
||||||
@ -541,7 +644,7 @@ export default {
|
|||||||
this.upload.open = false
|
this.upload.open = false
|
||||||
this.upload.isUploading = false
|
this.upload.isUploading = false
|
||||||
this.$refs.upload.clearFiles()
|
this.$refs.upload.clearFiles()
|
||||||
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true })
|
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", {dangerouslyUseHTMLString: true})
|
||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
// 提交上传文件
|
// 提交上传文件
|
||||||
|
|||||||
@ -47,7 +47,7 @@ module.exports = {
|
|||||||
// }
|
// }
|
||||||
// 当请求前缀是/dev-api时,使用下面的代理
|
// 当请求前缀是/dev-api时,使用下面的代理
|
||||||
'/dev-api': {
|
'/dev-api': {
|
||||||
target: 'http://110.40.171.179:8089',
|
target: 'http://localhost:8089',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: {
|
pathRewrite: {
|
||||||
'^/dev-api': ''
|
'^/dev-api': ''
|
||||||
|
|||||||
Reference in New Issue
Block a user