138 Commits

Author SHA1 Message Date
2765bcab10 故障告警,搜索栏 告警等级宽度调整 2026-01-29 20:08:33 +08:00
b55d9e2d5c Merge branch 'develop' into single-develop 2026-01-29 20:07:34 +08:00
29ab53056a 故障告警,搜索栏 告警等级宽度调整 2026-01-29 19:57:10 +08:00
0c32439146 Merge branch 'develop' into single-develop 2026-01-28 21:45:43 +08:00
5250db915f Merge branch 'develop' of http://101.43.41.9:13000/xzzn/emsfront into develop 2026-01-28 21:36:14 +08:00
814103c881 统计报表优化 2026-01-28 21:34:58 +08:00
55b7fba021 修改图片 2026-01-27 16:58:50 +08:00
da4ecc4792 修改顺序 2026-01-27 13:45:14 +08:00
498dc117f2 变更图片 2026-01-27 10:43:54 +08:00
058879ec45 Merge branch 'develop' into single-develop 2026-01-26 23:36:47 +08:00
4d29de93a1 大数据图片更新 2026-01-26 23:30:01 +08:00
445e9dfc9f 大数据图片改为切换模式 2026-01-26 22:40:57 +08:00
3a26f6312b 更新注释 2026-01-25 17:02:21 +08:00
138b0753af Merge branch 'develop' into single-develop 2026-01-25 16:56:29 +08:00
ac1d1ae154 更新workStatus状态枚举 2026-01-25 16:45:33 +08:00
825243e741 电池簇备注 2026-01-25 16:36:23 +08:00
0389ed85f3 Merge branch 'develop' of http://101.43.41.9:13000/xzzn/emsfront into develop 2026-01-25 16:33:00 +08:00
aef94f406a 电池簇、电池堆工作状态更新 2026-01-25 16:30:43 +08:00
95d69fb7b1 簇枚举变更 2026-01-25 16:27:35 +08:00
684002ffc8 簇枚举变更 2026-01-25 15:12:07 +08:00
098dfa05f8 簇枚举变更 2026-01-25 14:34:01 +08:00
d6c9310e50 净置改成静置 2026-01-25 13:54:23 +08:00
085f6fd252 Merge branch 'develop' into single-develop 2026-01-24 21:19:27 +08:00
142de3102b 大数据图片更新 2026-01-24 21:14:43 +08:00
d3e51322ab Merge branch 'develop' into single-develop 2026-01-24 10:37:31 +08:00
9b5806a2c0 0或没有返回值隐藏alarmNum的展示 2026-01-24 10:27:43 +08:00
2b6697fa5a pointName改成 dataPointName 2026-01-24 10:02:11 +08:00
3bb859b693 BMS电池簇数据同步 2026-01-23 22:32:12 +08:00
5b3701afd0 BMS总览数据同步 2026-01-23 22:02:02 +08:00
f531075853 电表数据同步 2026-01-23 21:46:02 +08:00
81f8f75d7e pcs开关机状态判断更新 2026-01-23 20:06:47 +08:00
fccddab04a Merge branch 'develop' into single-develop 2026-01-23 19:52:13 +08:00
8cb6fbee3e 大数据展示页面 2026-01-23 18:00:14 +08:00
f454b02c99 pcs、bmszl设备工作状态更新 2026-01-23 16:30:20 +08:00
bfe72cf2c3 首页字段、收益表格 2026-01-23 14:18:22 +08:00
fb90d81bb3 站点首页优化 2026-01-22 17:27:03 +08:00
823c0949d0 文案修改,实时运行图表切换站点数据未更新问题修复,电表未知类型 2026-01-21 18:58:37 +08:00
1b712a70f6 Merge branch 'develop' into single-develop 2026-01-21 14:42:02 +08:00
c1c411e48a 用户归属站点 2026-01-21 14:37:39 +08:00
43153a791d 用户归属站点 2026-01-21 14:05:09 +08:00
5d84972b8a 菜单 2026-01-16 16:08:54 +08:00
b122ab35ab Merge branch 'develop' into single-develop
# Conflicts:
#	src/router/ems.js
2026-01-16 15:59:17 +08:00
11111d035b 优化 2026-01-13 16:38:21 +08:00
80f07876c6 Merge branch 'develop' into single-develop 2026-01-12 14:17:32 +08:00
ab9bb1e85d pcs\bmszl\bmsdcc 标题颜色规则更新 2026-01-12 14:15:21 +08:00
bef8f01d0d pcs开关机接口参数更新 2026-01-08 17:14:57 +08:00
b469190d9f Merge branch 'develop' into single-develop 2026-01-08 16:59:47 +08:00
4079c40e5d workStatus、deviceStatus枚举更新,pcs开关机接口参数更新 2026-01-08 16:59:15 +08:00
e98b4ed287 Merge branch 'develop' into single-develop 2025-12-31 17:25:47 +08:00
4ebd5f0988 样式优化 2025-12-31 17:23:08 +08:00
a0095b4054 pcs设备类型,新增pcs设备配置选项,策略配置时间问题修复 2025-12-31 17:15:26 +08:00
6140ca8f14 Merge branch 'develop' into single-develop 2025-12-26 21:01:54 +08:00
84bc29410a 新增设备:TCP设备新增从站地址
策略配置:新增放电状态、可输入负数、开始、结束时间组件更换
2025-12-26 20:19:20 +08:00
a3d6cf7c55 Merge branch 'develop' into single-develop 2025-12-19 21:51:51 +08:00
f3fda19c64 保护方案 2025-12-19 21:49:19 +08:00
6fb706b528 云下展示pcs开关机按钮 2025-12-18 16:37:59 +08:00
579920e007 Merge branch 'develop' into single-develop 2025-12-18 16:35:34 +08:00
9a8247f833 云上隐藏pcs开关机按钮 2025-12-18 16:26:57 +08:00
d9eeee106f Merge branch 'develop' into single-develop 2025-12-16 13:48:09 +08:00
c4c79aaa64 设备列表顶部下载按钮展示 2025-12-16 13:44:48 +08:00
c90f4f5766 Merge branch 'develop' into single-develop 2025-12-16 11:14:50 +08:00
0966813c25 设备列表,隐藏顶部上传、下载按钮,pcs开机关机按钮显示条件优化 2025-12-16 11:03:59 +08:00
4a4d26f018 生产、云下环境变量配置 2025-12-15 17:41:29 +08:00
1a11c67aad test 环境变量配置 2025-12-15 14:42:27 +08:00
71fd48918d Merge branch 'develop' into single-develop
# Conflicts:
#	src/assets/styles/common.scss
2025-12-12 17:47:57 +08:00
dd4fa36597 优化 2025-12-12 17:38:26 +08:00
9b14d96e24 pcs开关机,设备列表上传、下载 2025-12-11 17:59:34 +08:00
34d9e038e6 合并代码 2025-12-10 15:09:33 +08:00
ac54ce999e ems、箱型图 2025-12-10 15:00:12 +08:00
f49efb5edd ems接口、综合查询 2025-12-09 18:25:36 +08:00
97f9b3ff0e ems 2025-12-08 17:00:18 +08:00
674a0c6c33 ems、收益报表 2025-12-08 16:58:43 +08:00
b4c49c3e58 Merge branch 'develop' into single-develop 2025-12-05 09:49:54 +08:00
7f3d02b8fb 点位、报警点位 2025-12-05 09:46:31 +08:00
9cfce671a6 Merge branch 'develop' into single-develop 2025-12-05 09:16:54 +08:00
cd8871d45a 点位上传、下载 2025-12-04 17:46:11 +08:00
11d44de513 表格内容fong-size调小1px 2025-11-28 17:53:34 +08:00
87e3db9f0f 云下使用深色主题区分云上 2025-11-28 15:21:42 +08:00
4132d8e539 合并develop最新代码 2025-11-28 15:18:27 +08:00
78eb81549b 设备列表上传 2025-11-28 15:11:34 +08:00
65f0e92296 2025-11-20紧急优化内容修复 2025-11-27 15:58:23 +08:00
9a585e97db 2025-11-20紧急优化内容-动环、消防 2025-11-27 15:51:13 +08:00
7fb6d1aa47 2025-11-20紧急优化内容 2025-11-26 17:50:06 +08:00
1378947a9e 设备监控-点位清单 2025-11-25 17:56:12 +08:00
28176ce052 Merge branch 'develop' into single-develop 2025-11-13 16:53:19 +08:00
0b2e7d9b86 mqtt新增站点、qos 2025-11-13 16:49:39 +08:00
fdcf82e343 Merge branch 'develop' into single-develop 2025-11-12 16:38:00 +08:00
87ffaca398 测试环境项目打包文件缺失问题 2025-11-12 16:35:43 +08:00
e4c6f1f798 Merge branch 'develop' into single-develop 2025-11-12 10:59:13 +08:00
06ef1c3d1d Merge branch 'develop' of http://101.43.41.9:13000/xzzn/emsfront into develop 2025-11-12 10:57:14 +08:00
bc5f2b4470 环境变量配置 2025-11-12 10:57:07 +08:00
f24241533b 修改站点首页 2025-11-12 10:50:46 +08:00
87cd27b798 环境配置 2025-11-11 17:53:40 +08:00
f4a42c168f Merge branch 'develop' into single-develop 2025-11-07 14:59:56 +08:00
3fd6f812b9 mqtt 2025-11-07 11:03:00 +08:00
1772c7e0e5 mqtt 2025-11-07 10:59:34 +08:00
eb5abec9ff 点位列表 2025-11-06 14:57:08 +08:00
834bd04d45 点位列表 2025-11-05 18:00:05 +08:00
371a2d8be0 菜单更新,单站监控展示 2025-11-05 14:50:11 +08:00
c403922639 Merge branch 'develop' into single-develop 2025-11-05 14:10:00 +08:00
da0d3d12ef 更新设备状态枚举 2025-11-01 13:02:01 +08:00
1280b7196c 设备保护 2025-10-31 21:34:52 +08:00
567ddf85a5 设备保护 2025-10-28 18:15:10 +08:00
3766b2c0fa 设备保护 2025-10-26 22:44:04 +08:00
695fcd0f8f 电池堆综合查询数据问题 2025-10-22 17:16:29 +08:00
11269efa2d 单体电池综合查询数据问题 2025-10-22 09:44:41 +08:00
4b8380a6a9 样式修复 2025-10-21 17:04:23 +08:00
5c150b0d26 设备列表、电价列表、综合搜索、图表详情优化 2025-10-21 15:42:00 +08:00
96e4f7874f 设备列表、电价列表、综合搜索、图表详情优化 2025-10-21 15:41:45 +08:00
bf833fc763 点位清单参数更新 2025-10-16 16:34:32 +08:00
f4e6821484 综合查询相关接口不再使用中文categoryName统一使用deviceCategory 2025-10-16 16:10:29 +08:00
fb0eda4565 单站监控-设备监控-动态三级菜单,点位清单排序 2025-10-15 14:26:06 +08:00
fef1704cbd 告警红点标志 2025-10-13 17:01:13 +08:00
679f8f2a07 单体电池图表、设备列表点位清单图表 2025-10-12 23:10:15 +08:00
376b50c3b5 电表报表 2025-10-11 17:30:23 +08:00
7d51c37cad 电表报表 2025-10-11 14:02:43 +08:00
ad95debdac 电价配置 2025-10-10 10:49:42 +08:00
cceca2af4f 电价配置 2025-10-09 17:38:26 +08:00
887af476ba 工单状态 2025-09-29 09:29:34 +08:00
28a6e525d8 编辑工单接口新增ticketNo 2025-09-28 16:56:11 +08:00
66e2a3daab 更新电表点位名称 2025-09-28 16:18:34 +08:00
d3558f5ee4 液冷页面优化 2025-09-28 15:52:17 +08:00
4005b921e6 优化 2025-09-28 15:37:01 +08:00
7d2d49b7e2 优化 2025-09-28 14:59:02 +08:00
cc4d6ab347 不展示菜单风格设置菜单 2025-09-28 14:55:22 +08:00
7497c1d1b9 告警页面先生成告警再跳转,更新电表返回值,更新登陆、注册页面表格左边距离 2025-09-28 14:31:24 +08:00
a1fdc958db 样式优化 2025-09-27 17:44:03 +08:00
9dcf2a6c12 综合查询切换站点、设备清空点位 2025-09-27 17:37:23 +08:00
fb4f9d4abc 功率、pcs、电池堆曲线更新为折线图 2025-09-27 17:26:08 +08:00
651a78bceb 站点监控接口没有返回站点菜单时不展示 2025-09-27 17:15:59 +08:00
a4ae820e98 登陆页面背景图片 2025-09-27 15:45:56 +08:00
466d3a14b6 logo更新 2025-09-27 15:19:15 +08:00
b421d11bc2 实时运行图标更新 2025-09-27 14:50:20 +08:00
2a3d2fcf63 Merge branch 'develop' into single-develop 2025-09-22 17:59:25 +08:00
25bb28f77a Merge branch 'develop' into single-develop 2025-09-17 14:57:30 +08:00
3d5d8918d7 合并develop最新代码 2025-08-20 17:53:26 +08:00
fe14089562 在路由权限校验中获取设备列表 2025-07-23 21:35:40 +08:00
b68f2608f3 本地 2025-07-21 23:00:41 +08:00
89 changed files with 9348 additions and 4940 deletions

View File

@ -1,14 +1,15 @@
# 页面标题 # 页面标题
VUE_APP_TITLE = 上动新能源-EMS管理系统 VUE_APP_TITLE = 上动新能源-EMS管理系统
# 开发环境配置
ENV = 'development'
# EMS管理系统/开发环境
VUE_APP_BASE_API = '/dev-api'
# 路由懒加载 # 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true VUE_CLI_BABEL_TRANSPILE_MODULES = true
# 开发环境配置
NODE_ENV = 'development'
# EMS管理系统/开发环境
VUE_APP_BASE_API = '/dev-api'
# EMS管理系统/开发环境 图片拼接地址 # EMS管理系统/开发环境 图片拼接地址
VUE_APP_IMG_URL = '/dev-api' VUE_APP_IMG_URL = '/dev-api'

View File

@ -2,10 +2,12 @@
VUE_APP_TITLE = 上动新能源-EMS管理系统 VUE_APP_TITLE = 上动新能源-EMS管理系统
# 生产环境配置 # 生产环境配置
ENV = 'production' NODE_ENV = 'production'
# EMS管理系统/生产环境 # EMS管理系统/生产环境
VUE_APP_BASE_API = '/dev-api' VUE_APP_BASE_API= 'http://127.0.0.1:8089'
# EMS管理系统/生产环境 图片拼接地址
VUE_APP_IMG_URL = 'http://127.0.0.1:8089'
# EMS管理系统/生产环境 图片拼接地址 todo baseUrl有变更时 请更新
VUE_APP_IMG_URL = 'http://110.40.171.179:8089'

View File

@ -1,12 +1,12 @@
# 页面标题 # 页面标题
VUE_APP_TITLE = 上动新能源-EMS管理系统 VUE_APP_TITLE = 上动新能源-EMS管理系统
BABEL_ENV = production
NODE_ENV = production
# 测试环境配置 # 测试环境配置
ENV = 'staging' NODE_ENV = 'staging'
# EMS管理系统/测试环境 # EMS管理系统/测试环境
VUE_APP_BASE_API = '/stage-api' VUE_APP_BASE_API= 'http://110.40.171.179:8089'
# EMS管理系统/测试环境 图片拼接地址
VUE_APP_IMG_URL = 'http://110.40.171.179:8089'

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>logo-icon.png">
<title><%= webpackConfig.name %></title> <title><%= webpackConfig.name %></title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]--> <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style> <style>

BIN
public/logo-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -10,7 +10,7 @@ import ThemePicker from "@/components/ThemePicker"
export default { export default {
name: "App", name: "App",
components: { ThemePicker } components: { ThemePicker },
} }
</script> </script>
<style scoped> <style scoped>

View File

@ -2,300 +2,383 @@ import request from '@/utils/request'
//获取单个站点的信息 //获取单个站点的信息
export function getDzjkHomeView(siteId) { export function getDzjkHomeView(siteId) {
return request({ return request({
url: `/ems/siteMonitor/homeView?siteId=${siteId}`, url: `/ems/siteMonitor/homeView?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//站点首页 冲放曲线 //站点首页 冲放曲线
export function getSevenChargeData({siteId,startDate,endDate}) { export function getSevenChargeData({siteId, startDate, endDate}) {
return request({ return request({
url: `/ems/siteMap/getSevenChargeData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`, url: `/ems/siteMap/getSevenChargeData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get' method: 'get'
}) })
}
// 获取站点包含的设备种类 用来判断单站监控设备监控的菜单栏展示
export function getSiteAllDeviceCategory(siteId) {
return request({
url: `/ems/siteConfig/getSiteAllDeviceCategory?siteId=${siteId}`,
method: 'get'
})
}
//EMS
export function getEmsDataList(siteId) {
return request({
url: `/ems/siteMonitor/getEmsDataList?siteId=${siteId}`,
method: 'get',
})
} }
//获取pcs、实时运行头部的设备信息 //获取pcs、实时运行头部的设备信息
export function getRunningHeadInfo(siteId) { export function getRunningHeadInfo(siteId) {
return request({ return request({
url: `/ems/siteMonitor/runningHeadInfo?siteId=${siteId}`, url: `/ems/siteMonitor/runningHeadInfo?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//获取pcs列表 //获取pcs列表
export function getPcsDetailInfo(siteId) { export function getPcsDetailInfo(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getPcsDetailInfo?siteId=${siteId}`, url: `/ems/siteMonitor/getPcsDetailInfo?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//获取BMS总览数据 //获取BMS总览数据
export function getBMSOverView(siteId) { export function getBMSOverView(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getBMSOverView?siteId=${siteId}`, url: `/ems/siteMonitor/getBMSOverView?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//获取BMS电池簇总览数据 //获取BMS电池簇总览数据
export function getBMSBatteryCluster(siteId) { export function getBMSBatteryCluster(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getBMSBatteryCluster?siteId=${siteId}`, url: `/ems/siteMonitor/getBMSBatteryCluster?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//获取单体电池 电池堆列表数据 //获取单体电池 电池堆列表数据
export function getStackNameList(siteId) { export function getStackNameList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getStackNameList?siteId=${siteId}`, url: `/ems/siteMonitor/getStackNameList?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//获取单体电池 电池簇列表数据 //获取单体电池 电池簇列表数据
export function getClusterNameList({stackDeviceId,siteId}) { export function getClusterNameList({stackDeviceId, siteId}) {
return request({ return request({
url: `/ems/siteMonitor/getClusterNameList?stackDeviceId=${stackDeviceId}&siteId=${siteId}`, url: `/ems/siteMonitor/getClusterNameList?stackDeviceId=${stackDeviceId}&siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//单体电池表格数据 //单体电池表格数据
export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId,batteryId, pageSize, pageNum}) { export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId, batteryId, pageSize, pageNum}) {
return request({ return request({
url: `/ems/siteMonitor/getClusterDataInfoList?clusterDeviceId=${clusterDeviceId}&siteId=${siteId}&stackDeviceId=${stackDeviceId}&batteryId=${batteryId}&pageSize=${pageSize}&pageNum=${pageNum}`, url: `/ems/siteMonitor/getClusterDataInfoList?clusterDeviceId=${clusterDeviceId}&siteId=${siteId}&stackDeviceId=${stackDeviceId}&batteryId=${batteryId}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get' method: 'get'
}) })
} }
// 单体电池图表 // 单体电池图表
//http://localhost:8089/ems/siteMonitor/getSingleBatteryData?clusterDeviceId=BMSC01&siteId=021_FXX_01&deviceId=001&startDate=2025-07-11&endDate=2025-07-18 //http://localhost:8089/ems/siteMonitor/getSingleBatteryData?clusterDeviceId=BMSC01&siteId=021_FXX_01&deviceId=001&startDate=2025-07-11&endDate=2025-07-18
export function getSingleBatteryData({siteId,deviceId,clusterDeviceId,startDate,endDate}) { export function getSingleBatteryData({siteId, deviceId, clusterDeviceId, startDate, endDate}) {
return request({ return request({
url: `/ems/siteMonitor/getSingleBatteryData?siteId=${siteId}&deviceId=${deviceId}&startDate=${startDate}&endDate=${endDate}&clusterDeviceId=${clusterDeviceId}`, url: `/ems/siteMonitor/getSingleBatteryData?siteId=${siteId}&deviceId=${deviceId}&startDate=${startDate}&endDate=${endDate}&clusterDeviceId=${clusterDeviceId}`,
method: 'get' method: 'get'
}) })
} }
//获取液冷列表数据 //获取液冷列表数据
export function getCoolingDataList(siteId) { export function getCoolingDataList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getCoolingDataList?siteId=${siteId}`, url: `/ems/siteMonitor/getCoolingDataList?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//获取动环数据
export function getDhDataList(siteId) {
return request({
url: `/ems/siteMonitor/getDhDataList?siteId=${siteId}`,
method: 'get'
})
}
//获取消防数据
export function getXfDataList(siteId) {
return request({
url: `/ems/siteMonitor/getXfDataList?siteId=${siteId}`,
method: 'get'
})
}
//获取电表数据 //获取电表数据
export function getAmmeterDataList(siteId) { export function getAmmeterDataList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getAmmeterDataList?siteId=${siteId}`, url: `/ems/siteMonitor/getAmmeterDataList?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
// 故障告警 // 故障告警
export function getAlarmDetailList({status,siteId, deviceId, alarmLevel, alarmStartTime, alarmEndTime,pageSize,pageNum}) { export function getAlarmDetailList({
return request({ status,
url: `/ems/siteAlarm/getAlarmDetailList?siteId=${siteId}&status=${status}&deviceId=${deviceId}&alarmLevel=${alarmLevel}&alarmStartTime=${alarmStartTime}&alarmEndTime=${alarmEndTime}&pageSize=${pageSize}&pageNum=${pageNum}`, siteId,
method: 'get' deviceId,
}) alarmLevel,
alarmStartTime,
alarmEndTime,
pageSize,
pageNum
}) {
return request({
url: `/ems/siteAlarm/getAlarmDetailList?siteId=${siteId}&status=${status}&deviceId=${deviceId}&alarmLevel=${alarmLevel}&alarmStartTime=${alarmStartTime}&alarmEndTime=${alarmEndTime}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get'
})
}
// 告警生成工单
export function createTicketNo(data) {
return request({
url: `/ems/siteAlarm/createTicketNo`,
method: 'post',
data
})
} }
// 概率统计 // 概率统计
//获取概率统计 电量指标接口 //获取概率统计 电量指标接口
export function getElectricData({siteId,startDate,endDate}) { export function getElectricData({siteId, startDate, endDate}) {
return request({ return request({
url: `/ems/statsReport/getElectricData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`, url: `/ems/statsReport/getElectricData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get' method: 'get'
}) })
} }
//获取pcs列表 //获取pcs列表
export function getPcsNameList(siteId) { export function getPcsNameList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getPcsNameList?siteId=${siteId}`, url: `/ems/siteMonitor/getPcsNameList?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//pcs曲线 //pcs曲线
export function getPCSData({siteId,startTime,endTime,dataType}) { export function getPCSData({siteId, startTime, endTime, dataType}) {
return request({ return request({
url: `/ems/statsReport/getPCSData?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}&dataType=${dataType}`, url: `/ems/statsReport/getPCSData?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}&dataType=${dataType}`,
method: 'get' method: 'get'
}) })
} }
//电池堆曲线 //电池堆曲线
export function getStackData({siteId,startTime,endTime,dataType}) { export function getStackData({siteId, startTime, endTime, dataType}) {
return request({ return request({
url: `/ems/statsReport/getStackData?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}&dataType=${dataType}`, url: `/ems/statsReport/getStackData?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}&dataType=${dataType}`,
method: 'get' method: 'get'
}) })
} }
//电池温度 //电池温度
export function getClusterData({siteId,stackId,clusterId,dateTime,pageNum,pageSize}) { export function getClusterData({siteId, stackId, clusterId, dateTime, pageNum, pageSize}) {
return request({ return request({
url: `/ems/statsReport/getClusterData?siteId=${siteId}&stackId=${stackId}&clusterId=${clusterId}&dateTime=${dateTime}&pageNum=${pageNum}&pageSize=${pageSize}`, url: `/ems/statsReport/getClusterData?siteId=${siteId}&stackId=${stackId}&clusterId=${clusterId}&dateTime=${dateTime}&pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get' method: 'get'
}) })
} }
// 实时运行 // 实时运行
//储能 //储能
export function storagePower(siteId) { export function storagePower(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/storagePower?siteId=${siteId}`, url: `/ems/siteMonitor/runningGraph/storagePower?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
method: 'get' method: 'get'
}) })
} }
//poc平均温度
export function stackAveTemp(siteId) { //poc温度
return request({ export function pcsMaxTemp(siteId, startTime, endTime) {
url: `/ems/siteMonitor/runningGraph/stackAveTemp?siteId=${siteId}`, return request({
method: 'get' url: `/ems/siteMonitor/runningGraph/pcsMaxTemp?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
}) method: 'get'
})
} }
// 电池平均soc // 电池平均soc
export function batteryAveSoc(siteId) { export function batteryAveSoc(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/batteryAveSoc?siteId=${siteId}`, url: `/ems/siteMonitor/runningGraph/batteryAveSoc?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
method: 'get' method: 'get'
}) })
} }
// 电池平均温度 // 电池平均温度
export function batteryAveTemp(siteId) { export function batteryAveTemp(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/batteryAveTemp?siteId=${siteId}`, url: `/ems/siteMonitor/runningGraph/batteryAveTemp?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
method: 'get' method: 'get'
}) })
} }
// 功率曲线 // 功率曲线
export function getPowerData({siteId,startDate,endDate}) { export function getPowerData({siteId, startDate, endDate}) {
return request({ return request({
url: `/ems/statsReport/getPowerData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`, url: `/ems/statsReport/getPowerData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get' method: 'get'
}) })
} }
//电表列表 //电表列表
export function getLoadNameList(siteId) { export function getLoadNameList(siteId) {
return request({ return request({
url: `/ems/statsReport/getLoadNameList?siteId=${siteId}`, url: `/ems/statsReport/getLoadNameList?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
}
// 电表报表
export function getAmmeterData({siteId,deviceId,dateTime}) {
return request({
url: `/ems/statsReport/getAmmeterData?siteId=${siteId}&deviceId=${deviceId}&dateTime=${dateTime}`,
method: 'get'
})
} }
// 电表报表
export function getAmmeterData({siteId, startTime, endTime, pageSize, pageNum}) {
return request({
url: `/ems/statsReport/getAmmeterData?siteId=${siteId}&startTime=${startTime}&endTime=${endTime}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get'
})
}
// 电价报表
export function getAmmeterRevenueData(data) {
return request({
url: `/ems/statsReport/getAmmeterRevenueData`,
method: 'get',
params: data
})
}
//策略列表 //策略列表
export function strategyRunningList(siteId) { export function strategyRunningList(siteId) {
return request({ return request({
url: `/system/strategyRunning/list?siteId=${siteId}`, url: `/system/strategyRunning/list?siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//停止策略 //停止策略
export function stopStrategyRunning(id) { export function stopStrategyRunning(id) {
return request({ return request({
url: `/system/strategyRunning/stop?id=${id}`, url: `/system/strategyRunning/stop?id=${id}`,
method: 'get' method: 'get'
}) })
} }
// 获取所有主策略 // 获取所有主策略
export function getMainStrategyList() { export function getMainStrategyList() {
return request({ return request({
url: `/system/strategyRunning/getMainStrategyList`, url: `/system/strategyRunning/getMainStrategyList`,
method: 'get' method: 'get'
}) })
} }
//获取所有辅助策略 //获取所有辅助策略
export function getAuxStrategyList() { export function getAuxStrategyList() {
return request({ return request({
url: `/system/strategyRunning/getAuxStrategyList`, url: `/system/strategyRunning/getAuxStrategyList`,
method: 'get' method: 'get'
}) })
} }
//配置策略 //配置策略
export function configStrategy(data) { export function configStrategy(data) {
return request({ return request({
url: `/system/strategyRunning/configStrategy`, url: `/system/strategyRunning/configStrategy`,
method: 'post', method: 'post',
data data
}) })
} }
//http://localhost:8089/strategy/temp/getTempNameList?strategyId=1&siteId=021_FXX_01 //http://localhost:8089/strategy/temp/getTempNameList?strategyId=1&siteId=021_FXX_01
//获取策略下的所有模板列表 //获取策略下的所有模板列表
export function getTempNameList({siteId,strategyId}) { export function getTempNameList({siteId, strategyId}) {
return request({ return request({
url: `/strategy/temp/getTempNameList?siteId=${siteId}&strategyId=${strategyId}`, url: `/strategy/temp/getTempNameList?siteId=${siteId}&strategyId=${strategyId}`,
method: 'get', method: 'get',
}) })
} }
//获取模板详情 //获取模板详情
///strategy/temp/list?templateId=1 ///strategy/temp/list?templateId=1
export function getStrategyTempDetail(templateId) { export function getStrategyTempDetail(templateId) {
return request({ return request({
url: `/strategy/temp/list?templateId=${templateId}`, url: `/strategy/temp/list?templateId=${templateId}`,
method: 'get', method: 'get',
}) })
} }
//新增模板 //新增模板
export function addStrategyTemp(data) { export function addStrategyTemp(data) {
return request({ return request({
url: `/strategy/temp`, url: `/strategy/temp`,
method: 'post', method: 'post',
data data
}) })
} }
export function editStrategyTemp(data) { export function editStrategyTemp(data) {
return request({ return request({
url: `/strategy/temp`, url: `/strategy/temp`,
method: 'put', method: 'put',
data data
}) })
} }
//http://localhost:8089/strategy/temp/{id} //http://localhost:8089/strategy/temp/{id}
export function deleteStrategyTemp(id) { export function deleteStrategyTemp(id) {
return request({ return request({
url: `/strategy/temp/`+id, url: `/strategy/temp/` + id,
method: 'delete', method: 'delete',
}) })
} }
//http://localhost:8089/strategy/timeConfig/list?strategyId=1&siteId=021_FXX_01 //http://localhost:8089/strategy/timeConfig/list?strategyId=1&siteId=021_FXX_01
export function timeConfigList({siteId,strategyId}) { export function timeConfigList({siteId, strategyId}) {
return request({ return request({
url: `/strategy/timeConfig/list?siteId=${siteId}&strategyId=${strategyId}`, url: `/strategy/timeConfig/list?siteId=${siteId}&strategyId=${strategyId}`,
method: 'get', method: 'get',
}) })
} }
//保存时间配置 //保存时间配置
// http://localhost:8089/strategy/timeConfig // http://localhost:8089/strategy/timeConfig
export function setTimeConfigList(data) { export function setTimeConfigList(data) {
return request({ return request({
url: `/strategy/timeConfig`, url: `/strategy/timeConfig`,
method: 'post', method: 'post',
data data
}) })
} }
// 策略曲线图 // 策略曲线图
//http://localhost:8089/strategy/curve/curveList?strategyId=1&siteId=021_FXX_01 //http://localhost:8089/strategy/curve/curveList?strategyId=1&siteId=021_FXX_01
export function curveList({siteId,strategyId}) { export function curveList({siteId, strategyId}) {
return request({ return request({
url: `/strategy/curve/curveList?siteId=${siteId}&strategyId=${strategyId}`, url: `/strategy/curve/curveList?siteId=${siteId}&strategyId=${strategyId}`,
method: 'get', method: 'get',
}) })
} }
//单站监控 首页 当日功率曲线 //单站监控 首页 当日功率曲线
export function getPointData({siteId,startDate,endDate}) { export function getPointData({siteId, startDate, endDate}) {
return request({ return request({
url: `/ems/siteMonitor/getPointData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`, url: `/ems/siteMonitor/getPointData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get', method: 'get',
}) })
} }

View File

@ -0,0 +1,42 @@
import request from '@/utils/request'
// 新增
export function addPriceConfig(data) {
return request({
url: '/ems/energyPriceConfig',
method: 'post',
data
})
}
//修改
export function editPriceConfig(data) {
return request({
url: '/ems/energyPriceConfig',
method: 'put',
data
})
}
//删除
export function energyPriceConfig(id) {
return request({
url: `/ems/energyPriceConfig/${id}`,
method: 'DELETE',
})
}
//详情
export function detailPriceConfig(id) {
return request({
url: `/ems/energyPriceConfig/${id}`,
method: 'get',
})
}
//列表
export function listPriceConfig({startTime,endTime,pageSize,pageNum,siteId}) {
return request({
url: `/ems/energyPriceConfig/list?startTime=${startTime}&endTime=${endTime}&pageNum=${pageNum}&pageSize=${pageSize}&siteId=${siteId}`,
method: 'get',
})
}

View File

@ -1,72 +1,203 @@
import request from '@/utils/request' import request from '@/utils/request'
// 站点列表 // 站点列表
export function getSiteInfoList({siteName,startTime, endTime,pageSize,pageNum}) { export function getSiteInfoList({siteName, startTime, endTime, pageSize, pageNum}) {
return request({ return request({
url: `/ems/siteConfig/getSiteInfoList?siteName=${siteName}&startTime=${startTime}&endTime=${endTime}&pageSize=${pageSize}&pageNum=${pageNum}`, url: `/ems/siteConfig/getSiteInfoList?siteName=${siteName}&startTime=${startTime}&endTime=${endTime}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get' method: 'get'
}) })
} }
// 设备列表 // 设备列表
export function getDeviceInfoList({siteId,pageSize,pageNum}) { export function getDeviceInfoList(data) {
return request({ return request({
url: `/ems/siteConfig/getDeviceInfoList?siteId=${siteId}&pageSize=${pageSize}&pageNum=${pageNum}`, url: `/ems/siteConfig/getDeviceInfoList`,
method: 'get' method: 'get',
}) params: data
})
} }
// 设备详情 // 设备详情
export function getDeviceDetailInfo(id) { export function getDeviceDetailInfo(id) {
return request({ return request({
url: `/ems/siteConfig/getDeviceDetailInfo?id=${id}`, url: `/ems/siteConfig/getDeviceDetailInfo?id=${id}`,
method: 'get' method: 'get'
}) })
} }
// 获取所有设备类别 // 获取所有设备类别
export function getDeviceCategory() { export function getDeviceCategory() {
return request({ return request({
url: `/ems/siteConfig/getDeviceCategory`, url: `/ems/siteConfig/getDeviceCategory`,
method: 'get' method: 'get'
}) })
} }
// 新增设备 // 新增设备
export function addDevice(data) { export function addDevice(data) {
return request({ return request({
url: `/ems/siteConfig/addDevice`, url: `/ems/siteConfig/addDevice`,
method: 'post', method: 'post',
data data
}) })
} }
// 编辑设备 // 编辑设备
export function updateDevice(data) { export function updateDevice(data) {
return request({ return request({
url: `/ems/siteConfig/updateDevice`, url: `/ems/siteConfig/updateDevice`,
method: 'post', method: 'post',
data data
}) })
} }
// 删除设备 // 删除设备
export function deleteService(id) { export function deleteService(id) {
return request({ return request({
url: `/ems/siteConfig/deleteService/`+id, url: `/ems/siteConfig/deleteService/` + id,
method: 'delete', method: 'delete',
}) })
}
//pcs开、关机
export function updateDeviceStatus(data) {
return request({
url: `/ems/siteConfig/updateDeviceStatus`,
method: 'post',
data
})
}
// 获取上级设备id列表
export function getParentDeviceId({siteId, deviceCategory}) {
return request({
url: `/ems/siteConfig/getParentDeviceId?siteId=${siteId}&deviceCategory=${deviceCategory}`,
method: 'get',
})
} }
//获取所有设备 //获取所有设备
export function getDeviceList(siteId) { export function getDeviceList(siteId) {
return request({ return request({
url: `/ems/siteConfig/getDeviceList?siteId=${siteId}`, url: `/ems/siteConfig/getDeviceList?siteId=${siteId}`,
method: 'get', method: 'get',
}) })
} }
//获取设备点位table //获取设备点位table
export function getDevicePointList({siteId,deviceId,deviceCategory,pageNum,pageSize,dataPointName='',sortMethod,dataPoint,lower,upper}) { export function getDevicePointList(data) {
return request({ return request({
url: `/ems/siteConfig/getDevicePointList?siteId=${siteId}&deviceId=${deviceId}&pageNum=${pageNum}&pageSize=${pageSize}&deviceCategory=${deviceCategory}&dataPointName=${dataPointName}&dataPoint=${dataPoint}&lower=${lower}&upper=${upper}&pageNum=${pageNum}&sortMethod=${sortMethod}`, url: `/ems/siteConfig/getDevicePointList`,
method: 'get', method: 'get',
}) params: data
} })
}
//获取设备类型下面的所有设备列表
export function getDeviceListBySiteAndCategory({siteId, deviceCategory}) {
return request({
url: `/ems/siteConfig/getDeviceListBySiteAndCategory?siteId=${siteId}&deviceCategory=${deviceCategory}`,
method: 'get',
})
}
//新增设备保护
export function addProtectPlan(data) {
return request({
url: `/ems/protectPlan`,
method: 'post',
data
})
}
//修改设备保护
export function updateProtectPlan(data) {
return request({
url: `/ems/protectPlan`,
method: 'put',
data
})
}
//删除设备保护
export function deleteProtectPlan(id) {
return request({
url: `/ems/protectPlan/${id}`,
method: 'delete',
})
}
//设备保护详情
export function getProtectPlan(id) {
return request({
url: `/ems/protectPlan/${id}`,
method: 'get',
})
}
//设备保护详情列表
//http://localhost:8089/ems/protectPlan/list?pageSize=10&pageNum=1&faultName=总压&siteId=021_DDS_01
export function protectPlanList({siteId, faultName, pageSize, pageNum}) {
return request({
url: `/ems/protectPlan/list?siteId=${siteId}&faultName=${faultName}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get',
})
}
// 点位导出
export function exportPointList(data) {
return request({
url: `/ems/pointMatch/export`,
method: 'post',
data
})
}
// 点位导入
export function importPointList(data) {
return request({
url: `/ems/pointMatch/importData`,
method: 'post',
data
})
}
//mqtt
export function getMqttList({pageSize, pageNum, mqttTopic, topicName, siteId}) {
return request({
url: `/ems/mqttConfig/list?pageSize=${pageSize}&pageNum=${pageNum}&mqttTopic=${mqttTopic}&topicName=${topicName}&siteId=${siteId}`,
method: 'get',
})
}
export function getMqttDetail(id) {
return request({
url: `/ems/mqttConfig/${id}`,
method: 'get',
})
}
export function addMqtt(data) {
return request({
url: `/ems/mqttConfig`,
method: 'post',
data
})
}
export function editMqtt(data) {
return request({
url: `/ems/mqttConfig`,
method: 'put',
data
})
}
export function deleteMqtt(id) {
return request({
url: `/ems/mqttConfig/${id}`,
method: 'delete',
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 KiB

View File

@ -5,64 +5,73 @@
//右侧内容区域 //右侧内容区域
//父元素 //父元素
.ems-dashboard-editor-container{ .ems-dashboard-editor-container{
background-color: #F1F5FC; background-color: #FFFFFF;//#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{
background-color: transparent;
}
}
//单站监控 设备监控card公共样式
.sbjk-card-container{
.el-card__header { .el-card__header {
background-color: transparent; background-color: transparent;
padding: 10px 14px; }
}
//单站监控 设备监控card公共样式
.sbjk-card-container {
.el-card__header {
background-color: transparent;
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{
font-size: 20px; .large-title {
font-size: 18px;
font-weight: 500; font-weight: 500;
line-height: 40px; line-height: 40px;
padding: 0 50px 0 11px; padding: 0 50px 0 11px;
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,33 +79,57 @@
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 {
font-size: 22px;
color: #fff;
display: block;
cursor: pointer;
}
} }
} }
//红色背景颜色标题 //红色背景颜色标题
&.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;;
}
} }
} }
@ -105,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;
} }
@ -118,58 +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 {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-left: 1px solid #eee;
border-top: 1px solid #eee;
.device-info-col {
padding: 10px 0;
text-align: center;
font-size: 12px;
color: #666666;
line-height: 14px;
border-bottom: 1px solid #eee;
border-right: 1px solid #eee;
.left {
}
.right {
display: block;
font-weight: 500;
font-size: 16px;
line-height: 18px;
margin-top: 10px;
}
}
}
//公共表格样式 //公共表格样式
.common-table.el-table{ .el-table {
color:#333333; font-size: 13px;
}
.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;
@ -183,31 +260,36 @@
} }
//二、三级菜单栏样式 //二、三级菜单栏样式
.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: 160px;
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:20px;
left:0; left:20px;
.el-menu-item{ .el-menu-item{
line-height: 45px; line-height: 45px;
height: 45px; height: 45px;
@ -215,22 +297,23 @@
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;
@ -239,11 +322,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;
} }

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

View File

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

View File

@ -32,13 +32,13 @@ export default {
attr:'installCapacity' attr:'installCapacity'
},{ },{
title:'总充电量(MWh', title:'总充电量(KWh',
num:'', num:'',
color:'#A696FF', color:'#A696FF',
attr:'totalChargedCap' attr:'totalChargedCap'
},{ },{
title:'总放电量(MWh', title:'总放电量(KWh',
num:'', num:'',
color:'#A696FF', color:'#A696FF',
attr:'totalDischargedCap' attr:'totalDischargedCap'

View File

@ -1,19 +1,24 @@
<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>
@ -32,22 +37,23 @@
</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'
export default { export default {
emits: ['setLayout'], emits: ['setLayout'],
@ -57,7 +63,8 @@ export default {
Hamburger, Hamburger,
Screenfull, Screenfull,
SizeSelect, SizeSelect,
Search Search,
BigDataPopup
}, },
computed: { computed: {
...mapGetters([ ...mapGetters([
@ -78,6 +85,9 @@ export default {
} }
}, },
methods: { methods: {
showBigDataImg() {
this.$refs.bigDataPopup.show = true
},
toggleSideBar() { toggleSideBar() {
this.$store.dispatch('app/toggleSideBar') this.$store.dispatch('app/toggleSideBar')
}, },
@ -93,7 +103,8 @@ export default {
this.$store.dispatch('LogOut').then(() => { this.$store.dispatch('LogOut').then(() => {
location.href = '/index' location.href = '/index'
}) })
}).catch(() => {}) }).catch(() => {
})
} }
} }
} }
@ -105,7 +116,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 +124,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 +150,17 @@ export default {
height: 100%; height: 100%;
line-height: 50px; line-height: 50px;
.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;
} }
@ -168,6 +190,7 @@ export default {
.avatar-wrapper { .avatar-wrapper {
margin-top: 10px; margin-top: 10px;
position: relative; position: relative;
padding-right: 10px;
.user-avatar { .user-avatar {
cursor: pointer; cursor: pointer;
@ -176,7 +199,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;

View File

@ -1,48 +1,60 @@
<template> <template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"> <div
class="sidebar-logo-container"
:class="{ collapse: collapse }"
:style="{
backgroundColor:
sideTheme === 'theme-dark'
? variables.menuBackground
: variables.menuLightBackground,
}"
>
<transition name="sidebarLogoFade"> <transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> <router-link
<img :src="logo" class="sidebar-logo" /> v-if="collapse"
<!-- <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>--> key="collapse"
class="sidebar-logo-link"
to="/"
>
<img :src="logoSmall" class="sidebar-logo" />
<!-- <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>-->
</router-link> </router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/"> <router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img :src="logo" class="sidebar-logo" /> <img :src="logo" class="sidebar-logo" />
<!-- <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>--> <!-- <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>-->
</router-link> </router-link>
</transition> </transition>
</div> </div>
</template> </template>
<script> <script>
import logoImg from '@/assets/logo/logo.png' import variables from "@/assets/styles/variables.scss";
import variables from '@/assets/styles/variables.scss' import logo from "@/assets/images/ems/logo.png";
import logo from '@/assets/images/ems/logo.png' import logoSmall from "@/assets/images/ems/logo-small.png";
import logoLarge from '@/assets/images/ems/logo-large.png'
export default { export default {
name: 'SidebarLogo', name: "SidebarLogo",
props: { props: {
collapse: { collapse: {
type: Boolean, type: Boolean,
required: true required: true,
} },
}, },
computed: { computed: {
variables() { variables() {
return variables return variables;
}, },
sideTheme() { sideTheme() {
return this.$store.state.settings.sideTheme return this.$store.state.settings.sideTheme;
} },
}, },
data() { data() {
return { return {
title: process.env.VUE_APP_TITLE, title: process.env.VUE_APP_TITLE,
// logo: logoImg logo: logo,
logo:logo, logoSmall: logoSmall,
logoLarge:logoLarge };
} },
} };
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -55,3 +55,12 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
::v-deep{
//,.el-submenu.is-active>.el-submenu__title 选中了二级菜单的以及菜单
.el-menu-item.is-active{
background-color: rgba(0,0,0,0.1) !important;
}
}
</style>

View File

@ -6,16 +6,16 @@ const getQuerySiteId= {
} }
}, },
watch: { watch: {
'$route.query':{ '$store.state.ems.zdList':{
handler (newQuery,oldQuery) { handler (newQuery,oldQuery) {
if(!newQuery || newQuery.length === 0){return}
// 参数变化处理 // 参数变化处理
this.$nextTick(() => { this.$nextTick(() => {
const {siteId} =newQuery const {siteId} =newQuery[0]
if(siteId){ this.siteId = siteId
this.siteId = siteId siteId && this.init(newQuery.siteId)
siteId && this.init(newQuery.siteId) console.log('watch站点列表返回数据newQuery=',newQuery)
console.log('mixin=>getQuerySiteId=>页面参数siteId发生了变化,this.siteId=',this.siteId) console.log('设置页面siteIdthis.siteId=',this.siteId)
}
}) })
}, },
immediate: true, immediate: true,

View File

@ -31,6 +31,7 @@ router.beforeEach((to, from, next) => {
// 判断当前用户是否已拉取完user_info信息 // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(() => { store.dispatch('GetInfo').then(() => {
isRelogin.show = false isRelogin.show = false
store.dispatch('getZdList')
store.dispatch('GenerateRoutes').then(accessRoutes => { store.dispatch('GenerateRoutes').then(accessRoutes => {
// 根据roles权限生成可访问的路由表 // 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表

View File

@ -2,164 +2,265 @@ 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',
hidden: true, meta: {title: '单站监控', icon: 'server',},
alwaysShow: false,
name: 'Dzjk',
children: [ children: [
{ {
path: '/dzjk/home', path: '/dzjk/home',
component: () => import('@/views/ems/dzjk/home/index.vue'), component: () => import('@/views/ems/dzjk/home/index.vue'),
name: 'DzjkHome', name: 'DzjkHome',
meta: { title: '站点首页',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkHome' } meta: {title: '站点首页', breadcrumb: false, activeMenu: '/dzjk/home', activeSecondMenuName: 'DzjkHome'}
}, },
{ {
path: '/dzjk/zxlt', path: '/dzjk/zxlt',
component: () => import('@/views/ems/dzjk/zxlt/index.vue'), component: () => import('@/views/ems/dzjk/zxlt/index.vue'),
name: 'DzjkZxlt', name: 'DzjkZxlt',
meta: { title: '主线路图',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkZxlt' } meta: {title: '主线路图', breadcrumb: false, activeMenu: '/dzjk/zxlt', activeSecondMenuName: 'DzjkZxlt'}
}, },
{ {
path: '/dzjk/sbjk', path: '/dzjk/sbjk',
component: () => import('@/views/ems/dzjk/sbjk/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/index.vue'),
name: 'DzjkSbjk', name: 'DzjkSbjk',
meta: { title: '设备监控',breadcrumb: false,activeMenu: '/dzjk'}, alwaysShow: false,
redirect: '/dzjk/sbjk/ssyx', meta: {title: '设备监控', breadcrumb: false, activeMenu: '/dzjk/sbjk'},
children: [ hidden: false,
{ redirect: '/dzjk/sbjk/ssyx',
path: 'ssyx', children: [
component: () => import('@/views/ems/dzjk/sbjk/ssyx/index.vue'), {
name: 'DzjkSbjkSsyx', path: 'ssyx',
meta: { title: '实时运行',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, component: () => import('@/views/ems/dzjk/sbjk/ssyx/index.vue'),
}, name: 'DzjkSbjkSsyx',
{ hidden: true,
path: 'pcs', meta: {
component: () => import('@/views/ems/dzjk/sbjk/pcs/index.vue'), title: '实时运行',
name: 'DzjkSbjkPcs', breadcrumb: false,
meta: { title: 'PCS',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, activeMenu: '/dzjk/sbjk',
}, activeSecondMenuName: 'DzjkSbjk',
{ deviceCategory: 'SSYX'
path: 'bmszl', },
component: () => import('@/views/ems/dzjk/sbjk/bmszl/index.vue'), },
name: 'DzjkSbjkBmszl', {
meta: { title: 'BMS总览',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, path: 'ems',
}, component: () => import('@/views/ems/dzjk/sbjk/ems/index.vue'),
{ name: 'DzjkSbjkEms',
path: 'bmsdcc', hidden: true,
component: () => import('@/views/ems/dzjk/sbjk/bmsdcc/index.vue'), meta: {
name: 'DzjkSbjkBmsdcc', title: 'EMS',
meta: { title: 'BMS电池簇',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, breadcrumb: false,
}, activeMenu: '/dzjk/sbjk',
{ activeSecondMenuName: 'DzjkSbjk',
path: 'dtdc', deviceCategory: 'EMS'
component: () => import('@/views/ems/dzjk/sbjk/dtdc/index.vue'), },
name: 'DzjkSbjkDtdc', },
meta: { title: '单体电池',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, {
}, path: 'pcs',
{ component: () => import('@/views/ems/dzjk/sbjk/pcs/index.vue'),
path: 'db', name: 'DzjkSbjkPcs',
component: () => import('@/views/ems/dzjk/sbjk/db/index.vue'), hidden: true,
name: 'DzjkSbjkDb', meta: {
meta: { title: '电表',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, title: 'PCS',
}, breadcrumb: false,
{ activeMenu: '/dzjk/sbjk',
path: 'yl', activeSecondMenuName: 'DzjkSbjk',
component: () => import('@/views/ems/dzjk/sbjk/yl/index.vue'), deviceCategory: 'PCS'
name: 'DzjkSbjkYl', },
meta: { title: '液冷',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkSbjk'}, },
} {
] path: 'bmszl',
}, component: () => import('@/views/ems/dzjk/sbjk/bmszl/index.vue'),
{ name: 'DzjkSbjkBmszl',
path: '/dzjk/gzgj', hidden: true,
component: () => import('@/views/ems/dzjk/gzgj/index.vue'), meta: {
name: 'DzjkGzgj', title: 'BMS总览',
meta: { title: '故障告警',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkGzgj' } breadcrumb: false,
}, activeMenu: '/dzjk/sbjk',
{ activeSecondMenuName: 'DzjkSbjk',
path: '/dzjk/tjbb', deviceCategory: 'STACK'
component: () => import('@/views/ems/dzjk/tjbb/index.vue'), },
name: 'DzjkTjbb', },
meta: {title: '统计报表', breadcrumb: false, activeMenu: '/dzjk'}, {
redirect: '/dzjk/tjbb/gltj', path: 'bmsdcc',
children: [ component: () => import('@/views/ems/dzjk/sbjk/bmsdcc/index.vue'),
{ name: 'DzjkSbjkBmsdcc',
path: 'gltj', hidden: true,
component: () => import('@/views/ems/dzjk/tjbb/gltj/index.vue'), meta: {
name: 'DzjkTjbbGltj', title: 'BMS电池簇',
meta: { title: '概率统计',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'}, breadcrumb: false,
}, activeMenu: '/dzjk/sbjk',
{ activeSecondMenuName: 'DzjkSbjk',
path: 'glqx', deviceCategory: 'CLUSTER'
component: () => import('@/views/ems/dzjk/tjbb/glqx/index.vue'), },
name: 'DzjkTjbbGlqx', },
meta: { title: '功率曲线',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'}, {
}, path: 'dtdc',
{ component: () => import('@/views/ems/dzjk/sbjk/dtdc/index.vue'),
path: 'pcsqx', name: 'DzjkSbjkDtdc',
component: () => import('@/views/ems/dzjk/tjbb/pcsqx/index.vue'), hidden: true,
name: 'DzjkTjbbPcsqx', meta: {
meta: { title: 'PCS曲线',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'}, title: '单体电池',
}, breadcrumb: false,
{ activeMenu: '/dzjk/sbjk',
path: 'dcdqx', activeSecondMenuName: 'DzjkSbjk',
component: () => import('@/views/ems/dzjk/tjbb/dcdqx/index.vue'), deviceCategory: 'BATTERY'
name: 'DzjkTjbbDcdqx', },
meta: { title: '电池堆曲线',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'}, },
}, {
{ path: 'db',
path: 'dcwd', component: () => import('@/views/ems/dzjk/sbjk/db/index.vue'),
component: () => import('@/views/ems/dzjk/tjbb/dcwd/index.vue'), name: 'DzjkSbjkDb',
name: 'DzjkTjbbDcwd', hidden: true,
meta: { title: '电池温度',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'}, meta: {
}, title: '电表',
{ breadcrumb: false,
path: 'dbbb', activeMenu: '/dzjk/sbjk',
component: () => import('@/views/ems/dzjk/tjbb/dbbb/index.vue'), activeSecondMenuName: 'DzjkSbjk',
name: 'DzjkTjbbDbbb', deviceCategory: 'AMMETER'
meta: { title: '电表报表',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkTjbb'}, },
} },
] {
}, path: 'yl',
{ component: () => import('@/views/ems/dzjk/sbjk/yl/index.vue'),
path: '/dzjk/clpz', name: 'DzjkSbjkYl',
component: () => import('@/views/ems/dzjk/clpz/index.vue'), hidden: true,
name: 'DzjkClpz', meta: {
meta: {title: '策略配置', breadcrumb: false, activeMenu: '/dzjk'}, title: '液冷',
redirect: '/dzjk/clpz/clyx', breadcrumb: false,
children: [ activeMenu: '/dzjk/sbjk',
{ activeSecondMenuName: 'DzjkSbjk',
path: 'clyx', deviceCategory: 'COOLING'
},
},
{
path: 'dh',
component: () => import('@/views/ems/dzjk/sbjk/dh/index.vue'),
name: 'DzjkSbjkDh',
hidden: true,
meta: {
title: '动环',
breadcrumb: false,
activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'DH'
},
},
{
path: 'xf',
component: () => import('@/views/ems/dzjk/sbjk/xf/index.vue'),
name: 'DzjkSbjkXf',
hidden: true,
meta: {
title: '消防',
breadcrumb: false,
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/gzgj', activeSecondMenuName: 'DzjkGzgj'}
},
{
path: '/dzjk/tjbb',
component: () => import('@/views/ems/dzjk/tjbb/index.vue'),
name: 'DzjkTjbb',
alwaysShow: false,
meta: {title: '统计报表', breadcrumb: false, activeMenu: '/dzjk/tjbb'},
hidden: false,
redirect: '/dzjk/tjbb/gltj',
children: [
{
path: 'gltj',
component: () => import('@/views/ems/dzjk/tjbb/gltj/index.vue'),
name: 'DzjkTjbbGltj',
hidden: true,
meta: {
title: '运行统计',
breadcrumb: false,
activeMenu: '/dzjk/tjbb',
activeSecondMenuName: 'DzjkTjbb'
},
},
{
path: 'glqx',
component: () => import('@/views/ems/dzjk/tjbb/glqx/index.vue'),
name: 'DzjkTjbbGlqx',
hidden: true,
meta: {
title: '功率曲线',
breadcrumb: false,
activeMenu: '/dzjk/tjbb',
activeSecondMenuName: 'DzjkTjbb'
},
},
{
path: 'pcsqx',
component: () => import('@/views/ems/dzjk/tjbb/pcsqx/index.vue'),
name: 'DzjkTjbbPcsqx',
hidden: true,
meta: {
title: 'PCS曲线',
breadcrumb: false,
activeMenu: '/dzjk/tjbb',
activeSecondMenuName: 'DzjkTjbb'
},
},
{
path: 'dcdqx',
component: () => import('@/views/ems/dzjk/tjbb/dcdqx/index.vue'),
name: 'DzjkTjbbDcdqx',
hidden: true,
meta: {
title: '电池堆曲线',
breadcrumb: false,
activeMenu: '/dzjk/tjbb',
activeSecondMenuName: 'DzjkTjbb'
},
},
{
path: 'dcwd',
component: () => import('@/views/ems/dzjk/tjbb/dcwd/index.vue'),
name: 'DzjkTjbbDcwd',
hidden: true,
meta: {
title: '电池温度',
breadcrumb: false,
activeMenu: '/dzjk/tjbb',
activeSecondMenuName: 'DzjkTjbb'
},
},
{
path: 'dbbb',
component: () => import('@/views/ems/dzjk/tjbb/dbbb/index.vue'),
name: 'DzjkTjbbDbbb',
hidden: true,
meta: {
title: '电表报表',
breadcrumb: false,
activeMenu: '/dzjk/tjbb',
activeSecondMenuName: 'DzjkTjbb'
},
}
]
},
{
path: '/dzjk/clpz',
component: () => import('@/views/ems/dzjk/clpz/clyx/index.vue'), component: () => import('@/views/ems/dzjk/clpz/clyx/index.vue'),
name: 'DzjkClpzClyx', name: 'DzjkClpz',
meta: { title: '策略运行',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkClpz'}, meta: {title: '策略配置', breadcrumb: false, activeMenu: '/dzjk/clpz'},
}, }
// {
// 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'},
// }
]
}
] ]
} }
]
}
] ]

View File

@ -12,7 +12,7 @@ module.exports = {
/** /**
* 系统布局配置 * 系统布局配置
*/ */
showSettings: true, showSettings: false,
/** /**
* 是否显示顶部导航 * 是否显示顶部导航
@ -52,5 +52,5 @@ module.exports = {
/** /**
* 底部版权文本内容 * 底部版权文本内容
*/ */
footerContent: 'Copyright © 2018-2025 xzzn. All Rights Reserved.' footerContent: 'Copyright © 2025 上动新能源 版权所有'
} }

View File

@ -1,26 +1,81 @@
import {getAllSites} from '@/api/ems/zddt'
import {getAlarmDetailList, getSiteAllDeviceCategory} from '@/api/ems/dzjk'
const ems = { const ems = {
state: { state: {
zdList:[], dzjkAlarmLighting: false,//单站监控 告警统计红点标志
workStatusOptions:{'0':'正常','1':'异常','2':'停止'},//工作状态 zdList: [],
deviceStatusOptions:{'0':'在线','1':'离线','2':'维修中'},//设备状态 zdDeviceCategoryOptions: {},//站点各个站点包含的设备种类 {021_DDS_01:["BATTERY","CLUSTER","STACK", "DH", "AMMETER", "PCS", "XF"],021_DDS_02:[]...}
gridStatusOptions:{'0':'并网','1':'未并网'},//并网状态 CLUSTERWorkStatusOptions: {'0': '静置', '1': '充电', '2': '放电', '3': '待机', '5': '运行', '9': "故障"},//电池簇工作状态
controlModeOptions:{'0':'远程','1':'本地'},//控制模式 PCSWorkStatusOptions: {'0': '运行', '1': '停机', '2': '故障', '3': '待机', '4': '充电', '5': '放电'},//PCS工作状态
warnOptions:{0:'正常', 1:'中断', 2:'不在线',3:'异常'},//告警状态 STACKWorkStatusOptions: {
communicationStatusOptions:{'0':'正常','1':'通讯中断','2':'异常'},//通讯状态 "0": "静置",
workModeOptions:{'0':'正常','1':'停止'},//工作模式 "1": "充电",
alarmLevelOptions:{'A':'提示','B':'一般','C':'严重','D':'紧急'},//告警等级 "2": "放电",
alarmStatusOptions:{'0':'待处理','1':'已处理','2':'处理中'},//告警状态 "3": "浮充",
deviceTypeOptions:{'TCP':'TCP','RTU':'RTU'},//设备类型 '4': '待机',
ticketStatusOptions:{0:'待处理', 1:'已处理', 2:'处理中'},//工单处理状态 '5': '运行',
strategyStatusOptions:{'0':'未启用', '1':'已运行', '2':'已暂停', '3':'禁用', '4':'删除'},//策略状态 '9': "故障"
chargeStatusOptions:{'1':'充电','2':'待机'},//冲放状态 },//STACKBMS总览工作状态
deviceCategoryOptions:{'PCS':'PCS','STACK':'电池堆','CLUSTER':'电池簇','COOLING':'液冷','BATTERY':'单体电池','AMMETER':'电表'},//设备类别 deviceStatusOptions: {'0': '离线', '1': '在线'},//设备状态
}, gridStatusOptions: {'0': '并网', '1': '未并网'},//并网状态
mutations: { controlModeOptions: {'0': '远程', '1': '本地'},//控制模式
SET_ZD_LIST(state, list) { warnOptions: {0: '正常', 1: '中断', 2: '不在线', 3: '异常'},//告警状态
state.zdList = list || [] 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: {
getZdList({commit, state}) {
if (state.zdList.length === 0) {
getAllSites().then(response => {
commit('SET_ZD_LIST', response?.data || [])
console.log('store action getZdList 获取站点数据', state.zdList)
})
}
},
//查询站点的所有待处理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

View File

@ -34,6 +34,13 @@ const permission = {
return new Promise(resolve => { return new Promise(resolve => {
// 向后端请求路由数据 // 向后端请求路由数据
getRouters().then(res => { getRouters().then(res => {
let sysDzjk = -1
if(res?.data){
sysDzjk = res.data.findIndex(i=>{
return i.children && i.children.find(j=>j.path.indexOf('dzjk')>-1)
})
if(sysDzjk>-1) res.data.splice(sysDzjk,1)
}
const sdata = JSON.parse(JSON.stringify(res.data)) const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data)) const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata) const sidebarRoutes = filterAsyncRouter(sdata)
@ -41,6 +48,10 @@ 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(sysDzjk === -1){
const index = constantRoutes.findIndex(i=>i.path.indexOf('dzjk')>-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))
commit('SET_DEFAULT_ROUTES', sidebarRoutes) commit('SET_DEFAULT_ROUTES', sidebarRoutes)
@ -111,7 +122,7 @@ export function filterDynamicRoutes(routes) {
} }
export const loadView = (view) => { export const loadView = (view) => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'staging') {
return (resolve) => require([`@/views/${view}`], resolve) return (resolve) => require([`@/views/${view}`], resolve)
} else { } else {
// 使用 import 实现生产环境的路由懒加载 // 使用 import 实现生产环境的路由懒加载

View File

@ -1,6 +1,6 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading" class="ems-dashboard-editor-container ems-content-container-padding">
<template v-if="!showTemp"> <template v-if="!showTemp">
<el-button v-if="!showTemp" type="primary" plain @click="settingStrategy" style="margin-bottom: 20px;">新增策略</el-button> <el-button v-if="!showTemp" type="primary" plain @click="settingStrategy" style="margin-bottom: 20px;">新增策略</el-button>
<cl-container v-for="(item,index) in list" :key="index+'clContainer'" :info="item" :hide-setting-btn="showTemp" class="contain" @update="init" @showSetting="settingStrategy(item)"> <cl-container v-for="(item,index) in list" :key="index+'clContainer'" :info="item" :hide-setting-btn="showTemp" class="contain" @update="init" @showSetting="settingStrategy(item)">

View File

@ -1,53 +0,0 @@
<template>
<div class="ems-dashboard-editor-container ems-third-menu-container">
<el-menu
class="ems-third-menu"
:default-active="$route.name"
background-color="#ffffff"
text-color="#666666"
active-text-color="#ffffff"
>
<el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'clpzChildrenRoute'">
<router-link style="height: 100%;width: 100%;display: block;" :to="{path:item.path,query:$route.query}">
{{item.meta.title}}
</router-link>
</el-menu-item>
</el-menu>
<div class="ems-content-container ems-content-container-padding clpz-ems-content-container">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</div>
</template>
<script>
import { dzjk } from '@/router/ems'
const childrenRoute = dzjk[0].children[0].children.find(item=> item.name==='DzjkClpz').children.filter(item=>!item?.hidden)//获取到统计报表下面的字路由
console.log('设备监控子路由',childrenRoute)
export default {
name:'DzjkClpz',
data(){
return {
childrenRoute,
activeMenu:''
}
},
mounted() {
console.log('当前统计报表页面路由',this.$route)
}
}
</script>
<style scoped lang="scss">
.clpz-ems-content-container{
margin-top:0;
padding-top:0;
padding-right: 0;
flex: 1;
}
</style>

View File

@ -1,119 +1,140 @@
<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-item label="soc下限" prop="sdcDown"> <!-- <template v-if="formData.sdcLimit === 1">-->
<el-input v-model="formData.sdcDown" placeholder="请输入" clearable :style="{width: '100%'}"></el-input> <el-form-item label="soc下限" prop="sdcDown">
<el-input v-model="formData.sdcDown" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label="soc上限" prop="sdcUp">
<el-input v-model="formData.sdcUp" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
</el-form-item>
<!-- </template>-->
</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>
<el-form-item label="soc上限" prop="sdcUp"> <el-form-item label="冲放功率" prop="chargeDischargePower">
<el-input v-model="formData.sdcUp" placeholder="请输入" clearable :style="{width: '100%'}"></el-input> <el-input v-model="formInline.chargeDischargePower" placeholder="请输入"
: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> </el-table-column>
<el-table-column <el-table-column
prop="endTime" prop="endTime"
label="结束时间"> label="结束时间">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="chargeDischargePower" prop="chargeDischargePower"
label="充放功率kW"> label="充放功率kW">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="chargeStatus" prop="chargeStatus"
label="充电状态"> label="充电状态">
<template slot-scope="scope"> <template slot-scope="scope">
{{chargeStatusOptions[scope.row.chargeStatus]}} {{ chargeStatusOptions[scope.row.chargeStatus] }}
</template> </template>
</el-table-column> </el-table-column>
<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,
@ -127,44 +148,41 @@ export default {
trigger: 'blur' trigger: 'blur'
}], }],
sdcDown: [ sdcDown: [
{required: true,message: '请输入', trigger: 'blur'}, {required: true, message: '请输入', trigger: 'blur'},
{ pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数' } {pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
], ],
sdcUp: [ sdcUp: [
{required: true,message: '请输入', trigger: 'blur'}, {required: true, message: '请输入', trigger: 'blur'},
{ pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数' } {pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
], ],
}, },
showAddTime: false, showAddTime: false,
formInline:{ formInline: {
startTime:'',endTime:'',chargeDischargePower:'',chargeStatus:'' timeRange: range,
chargeDischargePower: '',
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:[{ chargeStatus: [{
required: true, required: true,
message: '请选择充放状态', message: '请选择充放状态',
trigger: ['blur','change'] trigger: ['blur', 'change']
} }
] ]
}, },
tableData:[], tableData: [],
} }
}, },
computed: { computed: {
@ -172,145 +190,136 @@ 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: '', sdcDown: '',
sdcUp: '', sdcUp: '',
} }
this.formInline={ this.formInline = {
startTime:'',endTime:'',chargeDischargePower:'',chargeStatus:'' timeRange: this.secondRange, chargeDischargePower: '', 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, sdcDown, sdcUp} = 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.sdcDown = sdcDown
this.formData.sdcUp=sdcUp this.formData.sdcUp = sdcUp
} }
if(data.length === 1){ if (data.length === 1) {
const {startTime,endTime}=data; const {startTime, endTime} = data;
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: '', 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, chargeStatus} = this.formInline
this.$nextTick(() => {this.cancelAddTime()})
this.tableData.push({startTime, endTime, chargeDischargePower, 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, sdcDown, sdcUp} = this.formData
let status = true const {siteId, updateStrategyId} = this.$home
this.tableData.forEach((outer,outerIndex)=>{ const {tableData} = this
const {startTime, endTime}=outer if (this.mode === 'edit') {
const outerStart = parseInt(startTime),outerEnd = parseInt(endTime) editStrategyTemp({
if(outerStart>outerEnd){ siteId,
status = false strategyId: updateStrategyId,
}else{ templateId: this.editTempId,
this.tableData.forEach((inner,innerIndex)=>{ templateName,
if(innerIndex !== outerIndex){ sdcLimit,
const {startTime:innerStartTime, endTime:innerEndTime}=inner sdcDown,
const innerStart = parseInt(innerStartTime),innerEnd = parseInt(innerEndTime) sdcUp,
if((innerStart<outerStart && innerEnd>outerEnd) || !((innerStart<outerStart && innerEnd<=outerStart) || (innerStart>=outerEnd && innerEnd>outerEnd))){ timeConfigList: tableData
status=false }).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'){ sdcDown,
editStrategyTemp({siteId,strategyId:updateStrategyId,templateId:this.editTempId,templateName,sdcLimit,sdcDown,sdcUp,timeConfigList:tableData}).then(response=>{ sdcUp,
if(response?.code === 200){ timeConfigList: tableData
this.closeDialog() }).then(response => {
this.$emit('update') if (response?.code === 200) {
this.$emit('updateTimeSetting') this.closeDialog()
} this.$emit('update')
}) }
}else{ })
addStrategyTemp({siteId,strategyId:updateStrategyId,templateName,sdcLimit,sdcDown,sdcUp,timeConfigList:tableData}).then(response=>{ }
if(response?.code === 200){
this.closeDialog()
this.$emit('update')
}
})
}
}) })
}, },
closeDialog(){ closeDialog() {
// 清空所有数据 // 清空所有数据
this.$refs.addTempForm.resetFields() this.$refs.addTempForm.resetFields()
this.formData={ this.formData = {
templateName: '', templateName: '',
sdcLimit:0, sdcLimit: 0,
sdcDown: '', sdcDown: '',
sdcUp: '', 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>

View File

@ -2,7 +2,7 @@
<template> <template>
<!-- <cl-container :hideSettingBtn="true">--> <!-- <cl-container :hideSettingBtn="true">-->
<!-- <template v-slot:default>--> <!-- <template v-slot:default>-->
<div> <div class="ems-dashboard-editor-container ems-content-container-padding">
<temp-table ref="tempTable" @updateTimeSetting="updateTimeSetting"/> <temp-table ref="tempTable" @updateTimeSetting="updateTimeSetting"/>
<time-setting ref="timeSetting"/> <time-setting ref="timeSetting"/>
<temp-power-chart ref="tomePowerChart"/> <temp-power-chart ref="tomePowerChart"/>

View File

@ -1,27 +1,30 @@
<template> <template>
<el-card v-loading="loading" gshadow="always" class="common-card-container common-card-container-no-title-bg"> <div v-loading="loading" class="ems-dashboard-editor-container ems-content-container-padding">
<!-- 搜索栏--> <!-- 搜索栏-->
<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,172 +52,196 @@
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' : ''">{{ $store.state.ems.alarmStatusOptions[scope.row.status] }}</span>
</el-table-column> </template>
<el-table-column </el-table-column>
<el-table-column
label="工单" label="工单"
fixed="right" fixed="right"
width="250" width="250"
> >
<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="toTicket">生成工单</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> </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>
</el-card> </div>
</template> </template>
<script> <script>
import {getAlarmDetailList} from'@/api/ems/dzjk' import {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) {
this.loading = true
createTicketNo({id}).then(response => {
response?.data && this.toTicket()
}).finally(() => {
this.loading = false
})
}, },
// 判断是否是同一天 // 判断是否是同一天
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开始搜索
const [alarmStartTime='',alarmEndTime='']=(this.dateRange || [])
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.loading=true this.$store.dispatch('getSiteAlarmNum', this.siteId)
const {deviceId,alarmLevel} = this.search this.loading = true
const {siteId,pageNum,pageSize,activeBtn} =this const {deviceId, alarmLevel} = this.search
const [alarmStartTime='',alarmEndTime='']=(this.dateRange || []) const {siteId, pageNum, pageSize, activeBtn} = this
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];

View File

@ -1,30 +1,29 @@
<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 {getPointData} from '@/api/ems/dzjk'
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},
data() { data() {
return { return {
chart: null, chart: null,
timeRange:[], timeRange: [],
siteId:'', siteId: '',
isInit:true isInit: true
} }
}, },
mounted() { mounted() {
@ -41,23 +40,23 @@ 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() this.showLoading()
const {siteId,timeRange}=this const {siteId, timeRange} = this
getPointData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => { getPointData({siteId, startDate: timeRange[0], endDate: timeRange[1]}).then(response => {
this.setOption(response?.data || []) this.setOption(response?.data || [])
}).finally(()=>this.hideLoading()) }).finally(() => this.hideLoading())
}, },
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,17 +64,17 @@ export default {
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#activeChart')) this.chart = echarts.init(document.querySelector('#activeChart'))
}, },
showLoading(){ showLoading() {
this.chart && this.chart.showLoading() this.chart && this.chart.showLoading()
}, },
hideLoading(){ hideLoading() {
this.chart && this.chart.hideLoading() this.chart && this.chart.hideLoading()
}, },
setOption(data) { setOption(data) {
const source = [['日期','电网功率','负载功率','储能功率','光伏功率','soc平均值','soh平均值','电池平均温度平均值']] const source = [['日期', '电网功率', '负载功率', '储能功率', '光伏功率', 'soc平均值', 'soh平均值', '电池平均温度平均值']]
console.log('source.slice(1)',source[0].slice(1)) console.log('source.slice(1)', source[0].slice(1))
this.chart && data.forEach((item)=>{ this.chart && data.forEach((item) => {
source.push([item.statisDate,item.gridPower,item.loadPower,item.storagePower,item.pvPower,item.avgSoc,item.avgSoh,item.avgTemp]) 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: {
@ -91,8 +90,8 @@ export default {
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
} }
}, },
textStyle:{ textStyle: {
color:"#333333", color: "#333333",
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
@ -105,11 +104,17 @@ export default {
type: 'value', type: 'value',
}, },
], ],
dataset:{source}, dataset: {source},
series: source[0].slice(1).map((item,index)=>{ series: source[0].slice(1).map((item, index) => {
return { return {
type: 'line', type: 'line',//index === 5 ? 'bar' : 'line',
yAxisIndex:index<=4 ? 0 : 1 showSymbol: false,
symbolSize: 2,
smooth: true,
areaStyle: {
opacity: 0.5,
},
yAxisIndex: index <= 4 ? 0 : 1
} }
}) })
}) })

View File

@ -1,12 +1,11 @@
<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>
@ -14,14 +13,15 @@ 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 {getSevenChargeData} from '@/api/ems/dzjk'
export default { export default {
mixins: [resize], mixins: [resize],
components: {DateRangeSelect}, components: {DateRangeSelect},
data() { data() {
return { return {
chart: null, chart: null,
timeRange:[], timeRange: [],
siteId:'', siteId: '',
} }
}, },
mounted() { mounted() {
@ -38,41 +38,40 @@ export default {
}, },
methods: { methods: {
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.timeRange=data this.timeRange = data
this.getWeekKData() this.getWeekKData()
}, },
getWeekKData(){ getWeekKData() {
this.showLoading() this.showLoading()
const {siteId,timeRange}=this const {siteId, timeRange} = this
this.hideLoading() getSevenChargeData({siteId, startDate: timeRange[0], endDate: timeRange[1]}).then(response => {
getSevenChargeData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => {
this.setOption(response?.data || []) this.setOption(response?.data || [])
}).finally(()=>this.hideLoading()) }).finally(() => this.hideLoading())
}, },
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(){ showLoading() {
this.chart && this.chart.showLoading() this.chart && this.chart.showLoading()
}, },
hideLoading(){ hideLoading() {
this.chart && this.chart.hideLoading() this.chart && this.chart.hideLoading()
}, },
setOption(data,unit) { setOption(data, unit) {
const source = [['日期','充电量','放电量']] const source = [['日期', '充电量', '放电量']]
data.forEach(item=>{ data.forEach(item => {
source.push([item.ammeterDate, item.chargedCap,item.disChargedCap]) 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: { // 坐标轴指示器,坐标轴触发有效
@ -88,29 +87,29 @@ export default {
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
name:unit, name: unit,
nameLocation:'center' 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:{ dataset: {
source source
}, },
series: [ series: [
{ {
yAxisIndex:0, yAxisIndex: 0,
type: 'bar', type: 'bar',
}, },
{ {
yAxisIndex:0, yAxisIndex: 0,
type: 'bar', type: 'bar',
}, },
] ]

View File

@ -1,132 +1,168 @@
<template> <template>
<div v-loading="loading"> <div
class="ems-dashboard-editor-container ems-content-container-padding"
v-loading="loading"
>
<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.siteAddress }}</div>
>{{ info[item.attr] | formatNumber }}</el-descriptions-item </div>
> <div class="site-info">
</el-descriptions> <div class="title">
</div> <i class="el-icon-date"></i>
</el-card> </div>
</el-col> <div class="value">{{ info.runningTime || '-' }}</div>
<el-col :xs="24" :sm="24" :lg="8"> </div>
<el-card <!-- 装机功率容量 -->
shadow="always" <el-row :gutter="10" style="margin-top:20px;">
class="common-card-container common-card-container-body-no-padding"
>
<div slot="header">
<span class="card-title">总累计运行数据</span>
</div>
<div
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">装机功率(MW)</div>
{{ runningInfo[item.attr] | formatNumber }} <div class="sjgl-value">
{{ info.installPower | formatNumber }}
</div>
</div>
</el-col>
<el-col
:span="12"
class="sjgl-col power-col"
>
<div class="sjgl-wrapper">
<div class="sjgl-title">装机容量(MW)</div>
<div class="sjgl-value">
{{ info.installCapacity | formatNumber }}
</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">总收入</span>
<span class="value">{{ runningInfo.totalRevenue | formatNumber }}</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 sjglData"
:key="index + 'sjglData'"
class="sjgl-col"
>
<div class="sjgl-wrapper">
<div class="sjgl-title">{{ item.title }}</div>
<div class="sjgl-value" :style="{color:item.color}">
{{ runningInfo[item.attr] | formatNumber }}
</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"/>
</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"/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script>
import { getSingleSiteBaseInfo } from "@/api/ems/zddt"; import {getSingleSiteBaseInfo} from "@/api/ems/zddt";
import { getDzjkHomeView } from "@/api/ems/dzjk"; import {getDzjkHomeView} from "@/api/ems/dzjk";
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: [
{
title: "电站位置",
attr: "siteAddress",
},
{
title: "投运时间",
attr: "runningTime",
},
{
title: "装机功率(MW)",
attr: "installPower",
},
{
title: "装机容量(MW)",
attr: "installCapacity",
},
],
sjglData: [ sjglData: [
{ {
title: "今日充电量kWh", title: "今日充电量kWh",
attr: "dayChargedCap", attr: "dayChargedCap",
color: '#4472c4'
}, },
{ {
title: "今日放电量kWh", title: "今日放电量kWh",
attr: "dayDisChargedCap", attr: "dayDisChargedCap",
color: '#70ad47'
}, },
{ {
title: "总收入", title: "总充电量kWh",
attr: "todo",
},
{
title: "当日实时收入",
attr: "todo",
},
{
title: "总充电量MWh",
attr: "totalChargedCap", attr: "totalChargedCap",
color: '#4472c4'
}, },
{ {
title: "总放电量MWh", title: "今日实时收入(元",
attr: "totalDischargedCap", attr: "dayRevenue",
color: '#f67438'
},
{
title: "昨日充电量kWh",
attr: "yesterdayChargedCap",
color: '#4472c4'
},
{
title: "昨日放电量kWh",
attr: "yesterdayDisChargedCap",
color: '#70ad47'
},
{
title: "总放电量kWh",
attr: "totalDischargedCap",
color: '#70ad47'
},
{
title: "昨日实时收入(元)",
attr: "yesterdayRevenue",
color: '#f67438'
}, },
], ],
info: {}, //基本信息 info: {}, //基本信息
runningInfo: {}, //总累计运行数据+报警表格 runningInfo: {}, //总累计运行数据+报警表格
@ -134,14 +170,13 @@ export default {
}, },
computed: { computed: {
tableData() { tableData() {
console.log(
"this.runningInfo?.siteMonitorHomeAlarmVo ",
this.runningInfo?.siteMonitorHomeAlarmVo
);
return this.runningInfo?.siteMonitorHomeAlarmVo || []; return this.runningInfo?.siteMonitorHomeAlarmVo || [];
}, },
}, },
methods: { methods: {
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 || {};
@ -164,13 +199,80 @@ export default {
this.loading = false; this.loading = false;
}); });
// 一分钟循环一次总累计运行数据 // 一分钟循环一次总累计运行数据
this.updateInterval(this.getRunningInfo) this.updateInterval(this.getRunningInfo);
}, },
}, },
}; };
</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;
}
}
.row-container { .row-container {
& > .el-col { & > .el-col {
margin-bottom: 20px; margin-bottom: 20px;
@ -178,23 +280,47 @@ export default {
} }
//数据概览 //数据概览
.sjgl-data { .sjgl-col {
text-align: center; .sjgl-wrapper {
&:nth-child(1), text-align: left;
&:nth-child(2) { padding: 15px 20px;
margin-bottom: 25px; background-color: #f2f7fb;
} }
&.power-col {
.sjgl-wrapper {
padding: 10px;
.sjgl-value {
color: #c44444;
}
}
}
&:nth-child(4),
&:nth-child(2),
&:nth-child(3),
&:nth-child(4) {
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>
@ -202,10 +328,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;
} }

View File

@ -11,10 +11,10 @@
active-text-color="#ffffff" active-text-color="#ffffff"
mode="horizontal" mode="horizontal"
> >
<el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'dzjkChildrenRoute'"> <el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'dzjkChildrenRoute'" :class="{'lighting':item.path.indexOf('gzgj')>-1 && dzjkAlarmLighting}">
<router-link style="height: 100%;width: 100%;display: block;padding:0 20px;" :to="{path:item.path,query:$route.query}"> <router-link style="height: 100%;width: 100%;display: block;padding:0 20px;" :to="{path:item.path,query:$route.query}">
{{item.meta.title}} {{item.meta.title}}
</router-link> </router-link>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<div class="ems-content-container ems-content-container-padding dzjk-ems-content-container"> <div class="ems-content-container ems-content-container-padding dzjk-ems-content-container">
@ -30,6 +30,7 @@ import { dzjk } from '@/router/ems'
const childrenRoute = dzjk[0].children[0].children//获取到单站监控下面的字路由 const childrenRoute = dzjk[0].children[0].children//获取到单站监控下面的字路由
console.log('childrenRoute',childrenRoute) console.log('childrenRoute',childrenRoute)
import ZdSelect from '@/components/Ems/ZdSelect/index.vue' import ZdSelect from '@/components/Ems/ZdSelect/index.vue'
import {mapState} from "vuex";
export default { export default {
components:{ZdSelect}, components:{ZdSelect},
data(){ data(){
@ -38,14 +39,21 @@ export default {
activeMenu:'' activeMenu:''
} }
}, },
computed:{
...mapState({
dzjkAlarmLighting:state=>state.ems.dzjkAlarmLighting
})
},
methods:{ methods:{
submitSite(id){ submitSite(id){
if(id != this.$route.query.siteId){ if(id !== this.$route.query.siteId){
console.log('单站监控选择了其他的站点id=',id,'并更新页面地址参数') // console.log('单站监控选择了其他的站点id=',id,'并更新页面地址参数')
this.$router.push({query:{...this.$route.query,siteId:id}}) this.$router.push({query:{...this.$route.query,siteId:id}})
}else{ }else{
console.log('单站监控选择了相同的其他的站点id=',id,'页面地址不发生改变') // console.log('单站监控选择了相同的其他的站点id=',id,'页面地址不发生改变')
} }
//获取告警列表数据
this.$store.dispatch('getSiteAlarmNum',id)
} }
}, },
beforeRouteLeave(to,from, next){ beforeRouteLeave(to,from, next){
@ -54,15 +62,27 @@ export default {
this.$store.commit('SET_ZD_LIST',[]) this.$store.commit('SET_ZD_LIST',[])
next() next()
}, },
mounted() {
console.log('单站监控一级页面路由',this.$route)
}
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.dzjk-ems-content-container{ .dzjk-ems-content-container{
margin-top:0; margin-top:0;
min-height: 60vh;
}
.lighting{
position: relative;
z-index: 1;
&::after{
content:"";
display: block;
background-color: red;
height: 10px;
width: 10px;
border-radius: 100%;
position: absolute;
right: -2px;
top: -2px;
}
} }
</style> </style>

View File

@ -10,16 +10,24 @@
append-to-body append-to-body
width="1000px" width="1000px"
class="ems-dialog" class="ems-dialog"
:before-close="handleColsed" :before-close="handleClosed"
> >
<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">
<el-radio-group class="card-title" v-model="dataUnit"> <el-radio-group class="card-title" v-model="dataUnit">
<el-radio :label="1">分钟</el-radio> <el-radio :label="1">分钟</el-radio>
<el-radio :label="2">小时</el-radio> <el-radio :label="2">小时</el-radio>
<el-radio :label="3"></el-radio> <el-radio :label="3"></el-radio>
</el-radio-group> </el-radio-group>
<date-time-select ref="dateTimeSelect" :data-unit="dataUnit" @initDate="((e)=>dataRange=e||[])" @updateDate="updateDate"/> <date-time-select
ref="dateTimeSelect"
:data-unit="dataUnit"
@initDate="(e) => (dataRange = e || [])"
@updateDate="updateDate"
/>
</div> </div>
<div style="height: 350px" id="searchChart"></div> <div style="height: 350px" id="searchChart"></div>
</el-card> </el-card>
@ -31,177 +39,368 @@ 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: {
type: String, type: String,
required: true, required: true,
} },
}, },
computed: { computed: {
isDtdc(){ isDtdc() {
return this.categoryName === '单体电池' return this.deviceCategory === "BATTERY";
}, },
}, },
watch:{ watch: {
show(val) { show(val) {
if(!val){ if (!val) {
this.pointName='' this.pointName = "";
this.categoryName='' this.deviceCategory = "";
this.deviceId='' this.deviceId = "";
this.dataUnit=1 this.dataUnit = 1;
this.child = "";
if (!this.chart) { if (!this.chart) {
return return;
} }
this.hideLoading() this.hideLoading();
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
} }
}, },
dataUnit:{ dataUnit: {
handler(newVal,oldVal){ handler(newVal, oldVal) {
if(!this.show) return if (!this.show) return;
console.log('wacth到了dataUnit的变化',newVal,oldVal) console.log("wacth到了dataUnit的变化", newVal, oldVal);
this.$nextTick(()=>{ this.$nextTick(() => {
this.$refs.dateTimeSelect.init() this.$refs.dateTimeSelect.init();
this.getDate() this.getDate();
}) });
}, },
} },
}, },
data(){ data() {
return{ return {
show:false, show: false,
chart:null, chart: null,
dataUnit:1, dataUnit: 1,
dataRange:[], dataRange: [],
child:[],//单体电池需要数据 暂不删除 child: "", //单体电池需要数据
pointName:'', pointName: "",
categoryName:'', deviceCategory: "",
deviceId:'' deviceId: "",
} };
}, },
methods:{ methods: {
showChart({pointName,categoryName,deviceId}){ showChart({pointName, deviceCategory, deviceId, child = ""}) {
//初始化数据 //初始化数据
this.pointName=pointName this.pointName = pointName;
this.categoryName=categoryName this.deviceCategory = deviceCategory;
this.deviceId=deviceId this.deviceId = deviceId;
this.show = true child && (this.child = child);
this.$nextTick(()=>{ this.show = true;
this.$refs.dateTimeSelect.init() this.$nextTick(() => {
this.initChart() this.$refs.dateTimeSelect.init();
this.getDate() this.initChart();
}) this.getDate();
});
}, },
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#searchChart')) this.chart = echarts.init(document.querySelector("#searchChart"));
}, },
showLoading(){ showLoading() {
this.chart && this.chart.showLoading() this.chart && this.chart.showLoading();
}, },
hideLoading(){ hideLoading() {
this.chart && this.chart.hideLoading() this.chart && this.chart.hideLoading();
}, },
getDate(){ getDate() {
this.showLoading() this.showLoading();
const{dataUnit,dataRange:[start='',end=''],child}=this const {
let siteDeviceMap={} siteId,
child.forEach(([first,second,third])=>{ dataUnit,
if(siteDeviceMap[first]){ dataRange: [start = "", end = ""],
siteDeviceMap[first].push(third) child,
}else{ deviceId,
siteDeviceMap[first]=[] deviceCategory,
siteDeviceMap[first].push(third) pointName,
} } = this;
}) let siteDeviceMap = {};
let startDate,endDate child && (siteDeviceMap[siteId] = child);
if(start && dataUnit===3){ let startDate, endDate;
// startDate= start + `${dataUnit === 2 ? ':00' : ' 00:00:00'}` if (start && dataUnit === 3) {
startDate = start + ' 00:00:00' // startDate= start + `${dataUnit === 2 ? ':00' : ' 00:00:00'}`
}else{ startDate = start + " 00:00:00";
startDate=start } else {
} startDate = start;
if(end && dataUnit===3){ }
// endDate= end + `${dataUnit === 2 ? ':00' : ' 00:00:00'}` if (end && dataUnit === 3) {
endDate = end + ' 00:00:00' // endDate= end + `${dataUnit === 2 ? ':00' : ' 00:00:00'}`
}else{ endDate = end + " 00:00:00";
endDate=end } else {
} endDate = end;
}
getPointValueList({siteIds:[this.siteId],deviceId:this.deviceId,dataUnit,categoryName:this.categoryName,pointName:this.pointName,startDate,endDate,siteDeviceMap:{}}).then(response => { getPointValueList({
this.setOption(response?.data || []) siteIds: [siteId],
}).finally(()=>{ deviceId,
this.hideLoading() dataUnit,
}) deviceCategory,
pointName,
startDate,
endDate,
siteDeviceMap,
})
.then((response) => {
this.setOption(response?.data || []);
})
.finally(() => {
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){ this.$message.warning("暂无数据");
data.forEach((item,index)=>{
item.deviceList.forEach(inner=>{
dataset.push({
name:`${this.isDtdc ? `${inner.parentDeviceId ? inner.parentDeviceId+'-' : ''}` : ''}${this.pointName}`,
type:'line',
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('暂无数据')
} }
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',
// top: '10', // top: '10',
}, },
grid: { grid: {
containLabel: true containLabel: true,
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: "axis",
axisPointer: { // 坐标轴指示器,坐标轴触发有效 axisPointer: {
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' type: 'cross',
} },
// 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",
}, },
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
start: 0, start: 0,
end: 100 end: 100,
}, },
{ {
start: 0, start: 0,
end: 100 end: 100,
} },
], ],
series: dataset series: dataset,
}) });
}, },
updateDate(val){ setBoxOption(data) {
this.dataRange =val || [] let dataset = [];
this.getDate() 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,
});
}, },
handleColsed(done) {
updateDate(val) {
this.dataRange = val || [];
this.getDate();
},
handleClosed(done) {
if (!this.chart) { if (!this.chart) {
return done(); return done();
} }
@ -209,14 +408,13 @@ export default {
this.chart = null; this.chart = null;
done(); done();
}, },
} },
} };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
::v-deep { ::v-deep {
.card-title .el-radio{ .card-title .el-radio {
line-height: 40px; line-height: 40px;
} }
} }
</style>
</style>

View File

@ -1,143 +1,199 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmsdccContainer'" style="margin-bottom:25px;"> <div v-for="(baseInfo,index) in baseInfoList" :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" <el-card shadow="always"
:class="{ class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
'warning-card-container':baseInfo.workStatus && baseInfo.workStatus !== '0', :class="handleCardClass(baseInfo)">
'running-card-container':baseInfo.workStatus === '0'
}">
<div slot="header"> <div slot="header">
<span class="large-title">{{index+1}}#{{baseInfo.parentDeviceName?`${baseInfo.parentDeviceName} —> ` : ''}}{{baseInfo.deviceName}}</span> <span
</div> class="large-title">{{
<div class="descriptions-main"> baseInfo.parentDeviceName ? `${baseInfo.parentDeviceName} -> ` : ''
<el-descriptions direction="vertical" :column="3" :colon="false"> }}{{ baseInfo.deviceName }}</span>
<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> <div class="info">
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" :span="1" label="与PCS通信">{{$store.state.ems.communicationStatusOptions[baseInfo.pcsCommunicationStatus]}}</el-descriptions-item> <div>数据更新时间{{ baseInfo.dataUpdateTime || '-' }}</div>
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" :span="1" label="与EMS通信">{{$store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus]}}</el-descriptions-item>
</el-descriptions>
</div> </div>
<div class="descriptions-main descriptions-main-bg-color"> <div class="alarm">
<el-descriptions direction="vertical" :column="3" :colon="false"> <el-button type="primary" round size="small" style="margin-right:20px;"
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" v-for="(item,index) in infoData" :key="index+'pcsInfoData'" :span="1" :label="item.label"> @click="pointDetail(baseInfo,'point')">详细
<span class="pointer" @click="showChart(item.pointName || '','电池簇',baseInfo.deviceId)"> </el-button>
{{baseInfo[item.attr] | formatNumber}} <span v-if="item.unit" v-html="item.unit"></span> <el-badge :hidden="!baseInfo.alarmNum" :value="baseInfo.alarmNum || 0" class="item">
<i
class="el-icon-message-solid alarm-icon"
@click="pointDetail(baseInfo,'alarmPoint')"
></i>
</el-badge>
</div>
</div>
<div class="descriptions-main">
<el-descriptions direction="vertical" :column="3" :colon="false">
<el-descriptions-item
contentClassName="descriptions-direction work-status"
:span="1" label="工作状态">
{{ CLUSTERWorkStatusOptions[baseInfo.workStatus] }}
</el-descriptions-item>
<el-descriptions-item contentClassName="descriptions-direction"
:span="1" label="与PCS通信">
{{ $store.state.ems.communicationStatusOptions[baseInfo.pcsCommunicationStatus] }}
</el-descriptions-item>
<el-descriptions-item contentClassName="descriptions-direction"
:span="1" label="与EMS通信">
{{ $store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus] }}
</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" @click="showChart(item.pointName || '',baseInfo.deviceId)">
{{ 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>
<!-- 进度--> <!-- 进度-->
<div class="process-container"> <div class="process-container">
<div class="process-line-bg"> <div class="process-line-bg">
<div class="process-line" :style="{height:baseInfo.currentSoc+'%'}"></div> <div class="process-line" :style="{height:baseInfo.currentSoc+'%'}"></div>
</div> </div>
<div class="process pointer" @click="showChart( '当前SOC','电池簇',baseInfo.deviceId)">当前SOC : {{baseInfo.currentSoc}}%</div> <div class="process pointer" @click="showChart( '当前SOC',baseInfo.deviceId)">当前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"
@click="showChart( tablePointNameMap[scope.row.dataName+scope.column.label],baseInfo.deviceId)">{{
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"
@click="showChart( tablePointNameMap[scope.row.dataName+scope.column.label],baseInfo.deviceId)">{{
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 "
@click="showChart( tablePointNameMap[scope.row.dataName+scope.column.label],baseInfo.deviceId)">{{
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> <el-empty v-show="baseInfoList.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"/>
</div> </div>
</template> </template>
<script> <script>
import pointChart from "./../PointChart.vue"; import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import {getBMSBatteryCluster} from '@/api/ems/dzjk' import {getBMSBatteryCluster} 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";
export default { export default {
name:'DzjkSbjkBmsdcc', name: 'DzjkSbjkBmsdcc',
mixins:[getQuerySiteId,intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
components:{pointChart}, components: {PointTable, pointChart},
computed: {
...mapState({
CLUSTERWorkStatusOptions: state => state?.ems?.CLUSTERWorkStatusOptions || {},
})
},
data() { data() {
return { return {
loading:false, loading: false,
unitObj:{ unitObj: {
'电压':'V', '电压': 'V',
'温度':'&#8451;', '温度': '&#8451;',
'SOC':'%' 'SOC': '%'
}, },
tablePointNameMap:{ tablePointNameMap: {
'电压单体最小值':'最低单体电压', '电压单体最小值': '最低单体电压',
'电压单体平均值':'电压平均值', '电压单体平均值': '电压平均值',
'电压单体最大值':'最高单体电压', '电压单体最大值': '最高单体电压',
'温度单体最小值':'最低单体温度', '温度单体最小值': '最低单体温度',
'温度单体平均值':'平均单体温度', '温度单体平均值': '平均单体温度',
'温度单体最大值':'最高单体温度', '温度单体最大值': '最高单体温度',
'SOC单体最小值':'最低单体SOC', 'SOC单体最小值': '最低单体SOC',
'SOC单体平均值':'当前SOC', 'SOC单体平均值': '当前SOC',
'SOC单体最大值':'最高单体SOC', 'SOC单体最大值': '最高单体SOC',
}, },
baseInfoList:[], baseInfoList: [],
infoData:[ infoData: [
{label:'簇电压',attr:'clusterVoltage',unit:'V',pointName:'簇电压'}, {label: '簇电压', attr: 'clusterVoltage', unit: 'V', pointName: '簇电压'},
{label:'可充电量',attr:'chargeableCapacity',unit:'kWh',pointName:'可充电量'}, {label: '可充电量', attr: 'chargeableCapacity', unit: 'kWh', pointName: '可充电量'},
{label:'累计充电量',attr:'totalChargedCapacity',unit:'kWh',pointName:'累计充电量'}, {label: '累计充电量', attr: 'totalChargedCapacity', unit: 'kWh', pointName: '累计充电量'},
{label:'簇电流',attr:'clusterCurrent',unit:'A',pointName:'簇电流'}, {label: '簇电流', attr: 'clusterCurrent', unit: 'A', pointName: '簇电流'},
{label:'可放电量',attr:'dischargeableCapacity',unit:'kWh',pointName:'可放电量'}, {label: '可放电量', attr: 'dischargeableCapacity', unit: 'kWh', pointName: '可放电量'},
{label:'累计放电量',attr:'totalDischargedCapacity',unit:'kWh',pointName:'累计放电量'}, {label: '累计放电量', attr: 'totalDischargedCapacity', unit: 'kWh', pointName: '累计放电量'},
{label:'SOH',attr:'soh',unit:'%',pointName:'SOH'}, {label: 'SOH', attr: 'soh', unit: '%', pointName: 'SOH'},
{label:'平均温度',attr:'averageTemperature',unit:'&#8451;',pointName:'平均温度'}, {label: '平均温度', attr: 'averageTemperature', unit: '&#8451;', pointName: '平均温度'},
{label:'绝缘电阻',attr:'insulationResistance',unit:'&Omega;',pointName:'绝缘电阻'}, {label: '绝缘电阻', attr: 'insulationResistance', unit: '&Omega;', pointName: '绝缘电阻'},
], ],
} }
}, },
methods:{ methods: {
showChart(pointName,categoryName,deviceId){ handleCardClass(item) {
console.log('点击查询图表',pointName,categoryName,deviceId) const {workStatus = ''} = item
pointName && this.$refs.pointChart.showChart({pointName,categoryName,deviceId}) return !(Object.keys(this.CLUSTERWorkStatusOptions).includes(item.workStatus)) ? "timing-card-container" : workStatus === '9' ? 'warning-card-container' : 'running-card-container'
}, },
updateData(){ // 查看设备电位表格
pointDetail(row, dataType) {
const {siteId, deviceId} = row
this.$refs.pointTable.showTable({siteId, deviceId, deviceCategory: 'CLUSTER'}, dataType)
},
showChart(pointName, deviceId) {
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'CLUSTER', deviceId})
},
updateData() {
this.loading = true this.loading = true
getBMSBatteryCluster(this.siteId).then(response => { getBMSBatteryCluster(this.siteId).then(response => {
this.baseInfoList = JSON.parse(JSON.stringify(response?.data || [])); this.baseInfoList = 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)
} }
@ -148,28 +204,32 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
::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;
@ -180,9 +240,10 @@ 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;
} }
} }

View File

@ -1,27 +1,51 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmszlContainer'" style="margin-bottom:25px;"> <div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmszlContainer'" style="margin-bottom:25px;">
<el-card :class="{ <el-card
'warning-card-container':baseInfo.workStatus && baseInfo.workStatus !== '0', :class="handleCardClass(baseInfo)"
'running-card-container':baseInfo.workStatus === '0' class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
}" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg" shadow="always">
shadow="always">
<div slot="header"> <div slot="header">
<span class="large-title">{{index+1}}#{{baseInfo.deviceName}}</span> <span class="large-title">{{ baseInfo.deviceName }}</span>
<div class="info">
<div>数据更新时间{{ baseInfo.dataUpdateTime || '-' }}</div>
</div>
<div class="alarm">
<el-button type="primary" round size="small" style="margin-right:20px;"
@click="pointDetail(baseInfo,'point')">详细
</el-button>
<el-badge :hidden="!baseInfo.alarmNum" :value="baseInfo.alarmNum || 0" class="item">
<i
class="el-icon-message-solid alarm-icon"
@click="pointDetail(baseInfo,'alarmPoint')"
></i>
</el-badge>
</div>
</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">
{{ STACKWorkStatusOptions[baseInfo.workStatus] }}
</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>
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与EMS通信"
labelClassName="descriptions-label">
{{ $store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus] }}
</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="showChart(item.pointName || '',baseInfo.deviceId)">
{{ 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>
@ -30,80 +54,97 @@
<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="showChart('当前SOC',baseInfo.deviceId)">当前SOC :
{{ baseInfo.stackSoc }}%
</div>
</div> </div>
</div> </div>
<el-table <el-table
:data="baseInfo.batteryDataList" :data="baseInfo.batteryDataList"
class="common-table" class="common-table"
max-height="500" max-height="500"
stripe stripe
style="width: 100%;margin-top:25px;"> style="width: 100%;margin-top:25px;">
<el-table-column <el-table-column
label="簇号" label="簇号"
prop="clusterId"> prop="clusterId">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="簇电压" label="簇电压"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<span class="pointer" @click="showChart('簇电压','电池簇',scope.row.clusterId)">{{scope.row.clusterVoltage}} V</span> <span class="pointer"
@click="showChart('簇电压',scope.row.clusterId,'CLUSTER')">{{ scope.row.clusterVoltage }} V</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="pointer" @click="showChart('簇电流','电池簇',scope.row.clusterId)">{{scope.row.clusterCurrent}} A</span> <span class="pointer"
@click="showChart('簇电流',scope.row.clusterId,'CLUSTER')">{{ scope.row.clusterCurrent }} A</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="簇SOC"> label="簇SOC">
<template slot-scope="scope"> <template slot-scope="scope">
<span class="pointer" @click="showChart('当前SOC','电池簇',scope.row.clusterId)">{{scope.row.currentSoc}} %</span> <span class="pointer"
@click="showChart('当前SOC',scope.row.clusterId,'CLUSTER')">{{ scope.row.currentSoc }} %</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="单体最高电压" label="单体最高电压"
prop="maxVoltage"> prop="maxVoltage">
<template slot-scope="scope"> <template slot-scope="scope">
<span class="pointer" @click="showChart('最高单体电压','电池簇',scope.row.clusterId)">{{scope.row.maxCellVoltage}} V</span> <span class="pointer"
@click="showChart('最高单体电压',scope.row.clusterId,'CLUSTER')">{{
scope.row.maxCellVoltage
}} V</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="电池号码" label="电池号码"
prop="maxCellVoltageId"> prop="maxCellVoltageId">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="单体最低电压" label="单体最低电压"
prop="minVoltage"> prop="minVoltage">
<template slot-scope="scope"> <template slot-scope="scope">
<span class="pointer" @click="showChart('最低单体电压','电池簇',scope.row.clusterId)">{{scope.row.minCellVoltage}} V</span> <span class="pointer"
@click="showChart('最低单体电压',scope.row.clusterId,'CLUSTER')">{{
scope.row.minCellVoltage
}} V</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="电池号码" label="电池号码"
prop="minCellVoltageId"> prop="minCellVoltageId">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="单体最高温度"> label="单体最高温度">
<template slot-scope="scope"> <template slot-scope="scope">
<span class="pointer" @click="showChart('最高单体温度','电池簇',scope.row.clusterId)">{{scope.row.maxCellTemp}} &#8451;</span> <span class="pointer"
@click="showChart('最高单体温度',scope.row.clusterId,'CLUSTER')">{{
scope.row.maxCellTemp
}} &#8451;</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="电池号码" label="电池号码"
prop="maxCellTempId"> prop="maxCellTempId">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="单体最低温度" label="单体最低温度"
prop="minTemperature"> prop="minTemperature">
<template slot-scope="scope"> <template slot-scope="scope">
<span class="pointer" @click="showChart('最低单体温度','电池簇',scope.row.clusterId)">{{scope.row.minCellTemp}} &#8451;</span> <span class="pointer"
@click="showChart('最低单体温度',scope.row.clusterId,'CLUSTER')">{{
scope.row.minCellTemp
}} &#8451;</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="电池号码" label="电池号码"
prop="minCellTempId"> prop="minCellTempId">
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -111,6 +152,7 @@
</div> </div>
<el-empty v-show="baseInfoList.length<=0" :image-size="200"></el-empty> <el-empty v-show="baseInfoList.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"/>
</div> </div>
</template> </template>
@ -119,39 +161,58 @@ import {getBMSOverView} 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 pointChart from "./../PointChart.vue"; import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import {mapState} from "vuex";
export default { export default {
name:'DzjkSbjkBmszl', name: 'DzjkSbjkBmszl',
components: {pointChart,}, components: {pointChart, PointTable},
mixins:[getQuerySiteId,intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
computed: {
...mapState({
STACKWorkStatusOptions: state => state?.ems?.STACKWorkStatusOptions || {},
})
},
data() { data() {
return { return {
loading:false, loading: false,
baseInfoList:[], baseInfoList: [],
infoData:[ infoData: [
{label:'电池堆总电压',attr:'stackVoltage',unit:'V',pointName:'电池堆电压'}, {label: '电池堆总电压', attr: 'stackVoltage', unit: 'V', pointName: '电池堆电压'},
{label:'可充电量',attr:'availableChargeCapacity',unit:'kWh',pointName:'可充电量'}, {label: '可充电量', attr: 'availableChargeCapacity', unit: 'kWh', pointName: '可充电量'},
{label:'累计充电量',attr:'totalChargeCapacity',unit:'kWh',pointName:'累计充电量'}, {label: '累计充电量', attr: 'totalChargeCapacity', unit: 'kWh', pointName: '累计充电量'},
{label:'电池堆总电流',attr:'stackCurrent',unit:'A',pointName:'电池堆总电流'}, {label: '电池堆总电流', attr: 'stackCurrent', unit: 'A', pointName: '电池堆总电流'},
{label:'可放电量',attr:'availableDischargeCapacity',unit:'kWh',pointName:'可放电量'}, {label: '可放电量', attr: 'availableDischargeCapacity', unit: 'kWh', pointName: '可放电量'},
{label:'累计放电量',attr:'totalDischargeCapacity',unit:'kWh',pointName:'累计放电量'}, {label: '累计放电量', attr: 'totalDischargeCapacity', unit: 'kWh', pointName: '累计放电量'},
{label:'SOH',attr:'stackSoh',unit:'%',pointName:'SOH'}, {label: 'SOH', attr: 'stackSoh', unit: '%', pointName: 'SOH'},
{label:'平均温度',attr:'operatingTemp',unit:'&#8451;',pointName:'平均温度'}, {label: '平均温度', attr: 'operatingTemp', unit: '&#8451;', pointName: '平均温度'},
{label:'绝缘电阻',attr:'stackInsulationResistance',unit:'&Omega;',pointName:'绝缘电阻'}, {label: '绝缘电阻', attr: 'stackInsulationResistance', unit: '&Omega;', pointName: '绝缘电阻'},
] ]
} }
}, },
methods:{ methods: {
showChart(pointName,categoryName,deviceId){ handleCardClass(item) {
console.log('点击查询图表',pointName,categoryName,deviceId) const {workStatus = ''} = item
pointName && this.$refs.pointChart.showChart({pointName,categoryName,deviceId}) return !Object.keys(this.STACKWorkStatusOptions).find(i => i === workStatus) ? "timing-card-container" : workStatus === '9' ? 'warning-card-container' : 'running-card-container'
}, },
updateData(){
// 查看设备电位表格
pointDetail(row, dataType) {
const {siteId, deviceId} = row
this.$refs.pointTable.showTable({siteId, deviceId, deviceCategory: 'STACK'}, dataType)
},
showChart(pointName, deviceId, deviceCategory = 'STACK') {
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory, deviceId})
},
updateData() {
this.loading = true this.loading = true
getBMSOverView(this.siteId).then(response => { getBMSOverView(this.siteId).then(response => {
this.baseInfoList = JSON.parse(JSON.stringify(response?.data || [])); this.baseInfoList = 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)
} }
@ -171,21 +232,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;
@ -196,9 +260,10 @@ 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;
} }
} }

View File

@ -1,76 +1,213 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<el-card <el-card
v-for="(item,index) in list" v-for="(item,index) in list"
:key="index+'dbList'" :key="index+'dbList'"
shadow="always" shadow="always"
class="sbjk-card-container list" class="sbjk-card-container list"
:class="{ :class="{
'warning-card-container':item.emsCommunicationStatus && item.emsCommunicationStatus !== '0', 'timing-card-container':!['0','2'].includes(item.emsCommunicationStatus),
'warning-card-container':item.emsCommunicationStatus === '2',
'running-card-container':item.emsCommunicationStatus === '0' 'running-card-container':item.emsCommunicationStatus === '0'
}" }"
> >
<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[ communicationStatusOptions[item.emsCommunicationStatus] || '-'
item.emsCommunicationStatus }}
] </div>
}} <div>数据更新时间{{ item.dataUpdateTime || '-' }}</div>
</div> </div>
<div>数据更新时间{{ item.dataUpdateTime }}</div> <div class="alarm">
</div> <el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
</div> 详细
<el-table </el-button>
class="common-table" <el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
:data="item.loadDataDetailInfo" <i
@cell-click="(row,col)=>{handlerCell(item,row,col)}" class="el-icon-message-solid alarm-icon"
stripe @click="pointDetail(item,'alarmPoint')"
style="width: 100%" ></i>
> </el-badge>
<el-table-column prop="category" label="类别"> </el-table-column> </div>
<el-table-column prop="totalKwh" label="/kWh"> </el-table-column> </div>
<el-table-column prop="peakKwh" label="/kWh"> </el-table-column> <el-row class="device-info-row">
<el-table-column prop="highKwh" label="/kWh"> </el-table-column> <el-col v-for="(tempDataItem,tempDataIndex) in (deviceIdTypeMsg[item.deviceId] || otherTypeMsg)"
<el-table-column prop="flatKwh" label="/kWh"> </el-table-column> :key="tempDataIndex+'dbTempData'"
<el-table-column prop="valleyKwh" label="/kWh"> </el-table-column> :span="8" class="device-info-col">
</el-table> <span class="pointer" @click="showChart(tempDataItem.pointName,item.deviceId)">
</el-card> <span class="left">{{ tempDataItem.name }}</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> <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"/>
</div> </div>
</template> </template>
<script> <script>
import pointChart from "./../PointChart.vue"; import pointChart from "./../PointChart.vue";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { getAmmeterDataList } from "@/api/ems/dzjk"; 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 {mapState} from "vuex";
export default { export default {
name: "DzjkSbjkDb", name: "DzjkSbjkDb",
mixins: [getQuerySiteId,intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
components:{pointChart}, components: {PointTable, pointChart},
computed: {
...mapState({
communicationStatusOptions: state => state?.ems?.communicationStatusOptions || {},
})
},
data() { data() {
return { return {
loading: false, loading: false,
list:[], list: [],
deviceIdTypeMsg: {
'LOAD': [
{
name: '正向有功电能',
attr: 'forwardActive',
pointName: '正向有功电能',
unit: 'kWh'
},
{
name: '反向有功电能',
attr: 'reverseActive',
pointName: '反向有功电能',
unit: 'kWh'
},
{
name: '正向无功电能',
attr: 'forwardReactive',
pointName: '正向无功电能',
unit: 'kvarh'
},
{
name: '反向无功电能',
attr: 'reverseReactive',
pointName: '反向无功电能',
unit: 'kvarh'
},
{
name: '有功功率',
attr: 'activePower',
pointName: '总有功功率',
unit: 'kW'
},
{
name: '无功功率',
attr: 'reactivePower',
pointName: '总无功功率',
unit: 'kvar'
}
],
'METE': [
{
name: '正向有功电能',
attr: 'forwardActive',
pointName: '正向有功电能',
unit: 'kWh'
},
{
name: '反向有功电能',
attr: 'reverseActive',
pointName: '反向有功电能',
unit: 'kWh'
},
{
name: '正向无功电能',
attr: 'forwardReactive',
pointName: '正向无功电能',
unit: 'kvarh'
},
{
name: '反向无功电能',
attr: 'reverseReactive',
pointName: '反向无功电能',
unit: 'kvarh'
},
{
name: '有功功率',
attr: 'activePower',
pointName: '总有功功率',
unit: 'kW'
},
{
name: '无功功率',
attr: 'reactivePower',
pointName: '总无功功率',
unit: 'kvar'
}
],
'METEGF': [
{
name: '有功电能',
attr: 'activeEnergy',
pointName: '有功电能',
unit: 'kWh'
},
{
name: '无功电能',
attr: 'reactiveEnergy',
pointName: '无功电能',
unit: 'kvarh'
},
{
name: '有功功率',
attr: 'activePower',
pointName: '总有功功率',
unit: 'kW'
},
{
name: '无功功率',
attr: 'reactivePower',
pointName: '总无功功率',
unit: 'kvar'
}
]
},
otherTypeMsg: [
{
name: '正向有功电能',
attr: 'forwardActive',
pointName: '正向有功电能',
unit: 'kWh'
},
{
name: '反向有功电能',
attr: 'reverseActive',
pointName: '反向有功电能',
unit: 'kWh'
},
{
name: '有功功率',
attr: 'activePower',
pointName: '总有功功率',
unit: 'kW'
},
]
}; };
}, },
methods: { methods: {
handlerCell({deviceId,deviceName},row,column){ // 查看设备电位表格
if(column.label !== '类别'){ pointDetail(row, dataType) {
const arr = row.category.split('') const {deviceId} = row
arr.splice(6,0,column.label[0]) this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'AMMETER'}, dataType)
this.showChart(arr.join(''),deviceName,deviceId)
}
}, },
showChart(pointName,categoryName,deviceId){ showChart(pointName, deviceId) {
console.log('点击查询图表',pointName,categoryName,deviceId) pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'AMMETER', deviceId})
pointName && this.$refs.pointChart.showChart({pointName,categoryName,deviceId})
}, },
updateData(){ updateData() {
this.loading = true; this.loading = true;
getAmmeterDataList(this.siteId) getAmmeterDataList(this.siteId)
.then((response) => { .then((response) => {
@ -85,24 +222,16 @@ export default {
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 {
&.list:not(:last-child){ &.list:not(:last-child) {
margin-bottom: 25px; margin-bottom: 25px;
} }
::v-deep {
.el-table__row td{
&:not(:first-child){
.cell{
cursor: pointer;
}
}
}
}
} }
</style> </style>

View File

@ -0,0 +1,120 @@
<template>
<div v-loading="loading">
<el-card
v-for="(item,index) in list"
:key="index+'ylLise'"
class="sbjk-card-container running-card-container"
shadow="always">
<div slot="header">
<span class="large-title">{{ item.deviceName }}</span>
<div class="info">
<div>数据更新时间{{ item.dataUpdateTime || '-' }}</div>
</div>
<div class="alarm">
<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="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>
</template>
<script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getDhDataList} from '@/api/ems/dzjk'
import intervalUpdate from "@/mixins/ems/intervalUpdate";
import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
export default {
name: 'DzjkSbjkDh',
mixins: [getQuerySiteId, intervalUpdate],
components: {pointChart, PointTable},
data() {
return {
loading: false,
list: [],
tempData: [
{title: '湿度', attr: 'humidity', unit: ''},
{title: '温度', attr: 'temperature', unit: '&#8451;'},
]
}
},
methods: {
// 查看设备电位表格
pointDetail(row, dataType) {
const {deviceId} = row
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'DH'}, dataType)
},
showChart(pointName, deviceId) {
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'DH', deviceId})
},
updateData() {
this.loading = true
getDhDataList(this.siteId).then(response => {
this.list = JSON.parse(JSON.stringify(response?.data || []));
}).finally(() => {
this.loading = false
})
},
init() {
this.updateData()
this.updateInterval(this.updateData)
}
},
mounted() {
}
}
</script>
<style scoped lang="scss">
.sbjk-card-container {
&:not(:last-child) {
margin-bottom: 25px;
}
.el-row {
background-color: #ffffff;
border: 1px solid #eeeeee;
font-size: 14px;
line-height: 16px;
color: #333333;
.el-col {
padding: 12px 0;
text-align: center;
position: relative;
}
.el-col {
border-bottom: 1px solid #eeeeee;
}
.el-col:not(:nth-child(3n)) {
border-right: 1px solid #eeeeee;
}
}
}
</style>

View File

@ -10,13 +10,17 @@
class="ems-dialog chart-detail-dialog" class="ems-dialog chart-detail-dialog"
:before-close="handleColsed" :before-close="handleColsed"
> >
<el-card shadow="always" class="common-card-container time-range-card" style="margin-top:20px"> <el-card
shadow="always"
class="common-card-container time-range-card"
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">
<div id="lineChart" style="height: 310px;"></div> <div id="lineChart" style="height: 310px"></div>
</div> </div>
</el-card> </el-card>
</el-dialog> </el-dialog>
@ -28,7 +32,7 @@ import resize from "@/mixins/ems/resize";
import { getSingleBatteryData } from "@/api/ems/dzjk"; import { getSingleBatteryData } from "@/api/ems/dzjk";
import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue"; import DateRangeSelect from "@/components/Ems/DateRangeSelect/index.vue";
export default { export default {
components: {DateRangeSelect}, components: { DateRangeSelect },
mixins: [resize], mixins: [resize],
data() { data() {
return { return {
@ -48,9 +52,9 @@ export default {
}, },
methods: { methods: {
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.dateRange=data || [] this.dateRange = data || [];
this.getData() this.getData();
}, },
handleColsed(done) { handleColsed(done) {
if (!this.chart) { if (!this.chart) {
@ -94,7 +98,7 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
!this.chart && !this.chart &&
(this.chart = echarts.init(document.querySelector("#lineChart"))); (this.chart = echarts.init(document.querySelector("#lineChart")));
this.$refs.dateRangeSelect.init() this.$refs.dateRangeSelect.init();
}); });
}, },
setOption(data) { setOption(data) {
@ -148,7 +152,6 @@ export default {
}, },
]; ];
} }
this.chart && this.chart &&
this.chart.setOption({ this.chart.setOption({
color: ["#FFBD00", "#3C81FF", "#05AEA3", "#F86F70"], color: ["#FFBD00", "#3C81FF", "#05AEA3", "#F86F70"],
@ -186,10 +189,10 @@ export default {
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.chart-detail-dialog{ .chart-detail-dialog {
::v-deep{ ::v-deep {
.el-dialog__body{ .el-dialog__body {
padding-top:0; padding-top: 0;
} }
} }
} }

View File

@ -10,14 +10,17 @@
v-for="(item, index) in tableData" v-for="(item, index) in tableData"
:key="index + 'dtdcList'" :key="index + 'dtdcList'"
:class="handleListClass(item)" :class="handleListClass(item)"
@click="chartDetail(item)"
> >
<div style="font-size: 10px; font-weight: 600"> <div style="font-size: 10px; font-weight: 600">
{{ item.clusterDeviceId }} {{ item.clusterDeviceId }}
</div> </div>
<div>#{{ item.deviceId }}</div> <div>#{{ item.deviceId }}</div>
<div class="dy">{{ item.voltage }}V</div> <div class="dy pointer" @click="chartDetail(item, '电压 (V)')">
<div class="wd">{{ item.temperature }}</div> {{ item.voltage }}V
</div>
<div class="wd pointer" @click="chartDetail(item, '温度 (℃)')">
{{ item.temperature }}
</div>
</div> </div>
</div> </div>
@ -101,7 +104,7 @@ export default {
//查看表格行图表 //查看表格行图表
chartDetail(row, dataType = "") { chartDetail(row, dataType = "") {
const { clusterDeviceId, deviceId } = row; const { clusterDeviceId, deviceId } = row;
this.$emit("chart", { clusterDeviceId, deviceId, dataType }); this.$emit("chart", { ...row, dataType });
}, },
}, },
}; };
@ -123,7 +126,6 @@ export default {
box-sizing: content-box; box-sizing: content-box;
min-width: 60px; min-width: 60px;
width: auto; width: auto;
cursor: pointer;
&::before { &::before {
display: block; display: block;
content: ""; content: "";

View File

@ -1,61 +1,64 @@
<template> <template>
<div> <div>
<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 prop="deviceId" label="单体编号"> </el-table-column> <el-table-column prop="deviceId" label="单体编号"></el-table-column>
<el-table-column prop="clusterDeviceId" label="簇号"> </el-table-column> <el-table-column prop="clusterDeviceId" label="簇号"></el-table-column>
<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, 'voltage')" @click="chartDetail(scope.row, '电压 (V)')"
type="text" type="text"
size="small" size="small"
> >
{{ scope.row.voltage }} {{ scope.row.voltage }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<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, 'temperature')" @click="chartDetail(scope.row, '温度 (℃)')"
type="text" type="text"
size="small" size="small"
> >
{{ scope.row.temperature }} {{ scope.row.temperature }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<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"
> >
{{ scope.row.soc }} {{ scope.row.soc }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<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"
> >
{{ scope.row.soh }} {{ scope.row.soh }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="曲线图"> <el-table-column label="操作" width="160">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button @click="chartDetail(scope.row)" type="text" size="small"> <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> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -115,8 +118,7 @@ export default {
methods: { methods: {
//查看表格行图表 //查看表格行图表
chartDetail(row, dataType = "") { chartDetail(row, dataType = "") {
const { clusterDeviceId, deviceId } = row; this.$emit("chart", {...row, dataType});
this.$emit("chart", { clusterDeviceId, deviceId, dataType });
}, },
}, },
}; };

View File

@ -88,6 +88,7 @@
: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"
@ -103,6 +104,8 @@
> >
</el-pagination> </el-pagination>
<chart-detail ref="chartDetail" /> <chart-detail ref="chartDetail" />
<point-chart ref="pointChart" :site-id="siteId" />
<point-table ref="pointTable"/>
</el-card> </el-card>
</template> </template>
@ -117,10 +120,19 @@ 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: { BarChart, ChartDetail, DtdcTable: Table, DtdcList: List }, components: {
PointTable,
BarChart,
ChartDetail,
DtdcTable: Table,
DtdcList: List,
pointChart,
},
computed: { computed: {
pointIdList() { pointIdList() {
let obj = {}; let obj = {};
@ -157,17 +169,24 @@ export default {
}; };
}, },
methods: { methods: {
// 查看设备电位表格
pointDetail(row,dataType){
const {deviceId,clusterDeviceId} = row
this.$refs.pointTable.showTable({siteId:this.siteId,deviceId,deviceCategory:'BATTERY',parentId:clusterDeviceId},dataType)
},
changeMenu(menu) { changeMenu(menu) {
const { activeBtn } = this; const { activeBtn } = this;
activeBtn !== menu && (this.activeBtn = menu); activeBtn !== menu && (this.activeBtn = menu);
}, },
//查看表格行图表 //查看表格行图表
chartDetail({ clusterDeviceId, deviceId, dataType = "" }) { chartDetail({ deviceId, clusterDeviceId, dataType = "" }) {
const { siteId } = this; dataType &&
this.$refs.chartDetail.initChart( this.$refs.pointChart.showChart({
{ siteId, clusterDeviceId, deviceId }, pointName: dataType,
dataType deviceCategory:'BATTERY',
); deviceId: clusterDeviceId,
child: [deviceId],
});
}, },
// 分页 // 分页
handleSizeChange(val) { handleSizeChange(val) {

View File

@ -0,0 +1,133 @@
<template>
<div v-loading="loading" class="ems">
<el-card
v-for="(item,index) in list"
:key="index+'emsList'"
class="sbjk-card-container list running-card-container"
shadow="always"
>
<div slot="header">
<span class="large-title">{{ item.deviceName }}</span>
<div class="info">
<div>
EMS控制模式: {{
item.emsStatus === 0 ? '自动' : '手动'
}}
</div>
<div>数据更新时间{{ item.dataUpdateTime }}</div>
</div>
<div class="alarm">
<el-button size="small" round style="margin-right:20px;" type="primary" @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 bmsDataList" :key="tempDataIndex+'bmsTempData'"
:span="6" class="device-info-col">
<span class="pointer" @click="showChart(tempDataItem.name,item.deviceId)">
<span class="left">{{ tempDataItem.name }}</span> <span class="right">{{ item[tempDataItem.attr] }}<span
v-html="tempDataItem.unit"></span></span>
</span>
</el-col>
<el-col v-for="(tempDataItem,tempDataIndex) in pcsDataList" :key="tempDataIndex+'pcsTempData'"
:span="6" class="device-info-col">
<span class="pointer" @click="showChart(tempDataItem.name,item.deviceId)">
<span class="left">{{ tempDataItem.name }}</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>
</template>
<script>
import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getEmsDataList} from "@/api/ems/dzjk";
import intervalUpdate from "@/mixins/ems/intervalUpdate";
export default {
name: "DzjkSbjkEms",
components: {pointChart, PointTable},
mixins: [getQuerySiteId, intervalUpdate],
data() {
return {
loading: false,
list: [],
bmsDataList: [{
name: 'BMS1SOC',
attr: 'bms1Soc'
},
{
name: 'BMS2SOC',
attr: 'bms2Soc'
},
{
name: 'BMS3SOC',
attr: 'bms3Soc'
},
{
name: 'BMS4SOC',
attr: 'bms4Soc'
}],
pcsDataList: [{
name: 'PCS-1有功功率',
attr: 'pcs1Yggl'
},
{
name: 'PCS-2有功功率',
attr: 'pcs2Yggl'
},
{
name: 'PCS-3有功功率',
attr: 'pcs3Yggl'
},
{
name: 'PCS-4有功功率',
attr: 'pcs4Yggl'
}]
};
},
methods: {
// 查看设备电位表格
pointDetail(row, dataType) {
const {deviceId} = row
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'EMS'}, dataType)
},
showChart(pointName, deviceId) {
pointName &&
this.$refs.pointChart.showChart({pointName, deviceCategory: 'EMS', deviceId});
},
getData() {
this.loading = true;
getEmsDataList(this.siteId)
.then((response) => {
const data = response?.data || {};
this.list = JSON.parse(JSON.stringify(data));
})
.finally(() => (this.loading = false));
},
updateData() {
this.getData();
},
init() {
this.updateData();
this.updateInterval(this.updateData);
},
},
};
</script>
<style lang="scss" scoped></style>

View File

@ -1,52 +1,77 @@
<template> <template>
<div class="ems-dashboard-editor-container ems-third-menu-container"> <div class="ems-dashboard-editor-container ems-third-menu-container" v-loading="loading">
<el-menu <el-menu
class="ems-third-menu" class="ems-third-menu"
:default-active="$route.name" :default-active="$route.name"
background-color="#ffffff" background-color="#ffffff"
text-color="#666666" text-color="#666666"
active-text-color="#ffffff" active-text-color="#ffffff"
> >
<el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'dzjkChildrenRoute'"> <el-menu-item :index="item.name" v-for="(item,index) in categoryRouter" :key="index+'dzjkChildrenRoute'">
<router-link style="height: 100%;width: 100%;display: block" :to="{path:item.path,query:$route.query}"> <router-link style="height: 100%;width: 100%;display: block" :to="{path:item.path,query:$route.query}">
{{item.meta.title}} {{item.meta.title}}
</router-link> </router-link>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<div class="ems-content-container ems-content-container-padding sbjk-ems-content-container"> <div class="ems-content-container ems-content-container-padding sbjk-ems-content-container">
<keep-alive> <keep-alive>
<router-view></router-view> <router-view></router-view>
</keep-alive> </keep-alive>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { dzjk } from '@/router/ems' import { dzjk } from '@/router/ems'
const childrenRoute = dzjk[0].children[0].children.find(item=> item.name==='DzjkSbjk').children//获取到单站监控-设备监控下面的字路由 import {mapState} from "vuex";
console.log('设备监控子路由',childrenRoute) const childrenRoute = ((dzjk[0]?.children || []).find(item=> item.name==='DzjkSbjk').children) || []//获取到单站监控-设备监控下面的字路由
export default { export default {
name:'DzjkSbjk', name:'DzjkSbjk',
mixins:[getQuerySiteId],
computed:{
...mapState({
zdDeviceCategoryOptions: state => state.ems.zdDeviceCategoryOptions,
}),
locationSiteCategory(){
return this.zdDeviceCategoryOptions[this.siteId] || []
},
categoryRouter(){
const routeData =this.childrenRoute.filter(item=>this.locationSiteCategory.includes(item.meta.deviceCategory))
if(this.siteId && routeData.length > 0 && this.locationSiteCategory && this.locationSiteCategory.length >1){
const locationPageDeviceCategory = this.$route.meta?.deviceCategory || ''
if(!routeData.some(item=> item.meta.deviceCategory===locationPageDeviceCategory)){
this.$router.replace({path:'/dzjk/sbjk/ssyx',query:this.$route.query})
}
}
return routeData
}
},
data(){ data(){
return { return {
childrenRoute, childrenRoute,
activeMenu:'' activeMenu:'',
loading:false,
} }
}, },
mounted() { methods:{
console.log('当前设备监控页面路由',this.$route) init(){
this.loading=true
this.$store.dispatch('getSiteDeviceCategory',this.siteId).finally(()=>this.loading=false)
}
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.sbjk-ems-content-container{ .sbjk-ems-dashboard-editor-container {
margin-top:0; display: flex;
padding-top:0; background: #ffffff;
}
.sbjk-ems-content-container {
margin-top: 0;
padding-top: 0;
padding-right: 0; padding-right: 0;
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -1,17 +1,21 @@
<template> <template>
<div v-loading="loading" class="pcs-ems-dashboard-editor-container"> <div v-loading="loading" class="pcs-ems-dashboard-editor-container">
<!-- 顶部六个方块--> <!-- 顶部六个方块-->
<real-time-base-info :data="runningHeadData"/> <real-time-base-info :data="runningHeadData"/>
<div v-for="(pcsItem,pcsIndex) in pcsList" :key="pcsIndex+'PcsHome'" style="margin-bottom:25px;"> <div
<el-card :class="{ v-for="(pcsItem, pcsIndex) in pcsList"
'warning-card-container':pcsItem.workStatus === '1', :key="pcsIndex + 'PcsHome'"
'timing-card-container':pcsItem.workStatus === '2', style="margin-bottom: 25px"
'running-card-container':!['1','2'].includes(pcsItem.workStatus) >
}" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg" <el-card
shadow="always"> :class="handleCardClass(pcsItem)"
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">{{pcsIndex+1}}#{{pcsItem.deviceName}}</span> <span class="large-title"
>{{ pcsItem.deviceName }}</span
>
<div class="info"> <div class="info">
<div> <div>
{{ {{
@ -23,116 +27,305 @@
<div>数据更新时间{{ pcsItem.dataUpdateTime }}</div> <div>数据更新时间{{ pcsItem.dataUpdateTime }}</div>
</div> </div>
<div class="alarm"> <div class="alarm">
<el-badge :value="pcsItem.alarmNum || 0" class="item"> <pcs-switch style="margin-right:10px;"
<i class="el-icon-message-solid" style="font-size: 26px;color: #fff;display: block;"></i> :round="true"
size="small"
type="danger"
:data="pcsItem"
@updateSuccess="init"/>
<el-button type="primary" round size="small" style="margin-right:20px;"
@click="pointDetail(pcsItem,'point')">
详细
</el-button>
<el-badge :hidden="!pcsItem.alarmNum" :value="pcsItem.alarmNum || 0" class="item">
<i
class="el-icon-message-solid alarm-icon"
@click="pointDetail(pcsItem,'alarmPoint')"
></i>
</el-badge> </el-badge>
</div> </div>
</div> </div>
<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 :contentClassName="`descriptions-direction ${pcsItem.workStatus === '0' ? 'save' :'danger'}`" :span="1" label="工作状态" labelClassName="descriptions-label">{{$store.state.ems.workStatusOptions[pcsItem.workStatus]}}</el-descriptions-item> <el-descriptions-item
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="并网状态" labelClassName="descriptions-label">{{$store.state.ems.gridStatusOptions[pcsItem.gridStatus]}}</el-descriptions-item> contentClassName="descriptions-direction work-status"
<el-descriptions-item :contentClassName="`descriptions-direction ${pcsItem.deviceStatus === '0' ? 'save' : 'danger'}`" :span="1" label="设备状态" labelClassName="descriptions-label">{{$store.state.ems.deviceStatusOptions[pcsItem.deviceStatus]}}</el-descriptions-item> :span="1"
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="控制模式" labelClassName="descriptions-label">{{$store.state.ems.controlModeOptions[pcsItem.controlMode]}}</el-descriptions-item> label="工作状态"
</el-descriptions> labelClassName="descriptions-label"
</div> >{{
<div class="descriptions-main descriptions-main-bg-color"> PCSWorkStatusOptions[pcsItem.workStatus]
<el-descriptions :colon="false" :column="4" contentClassName="descriptions-direction" direction="vertical" labelClassName="descriptions-label"> }}
<el-descriptions-item v-for="(item,index) in infoData" :key="index+'pcsInfoData'" :label="item.label" :span="1"> </el-descriptions-item
<span class="pointer" @click="showChart(item.pointName || '','PCS',pcsItem.deviceId)"> >
{{pcsItem[item.attr] | formatNumber}} <span v-if="item.unit" v-html="item.unit"></span> <el-descriptions-item
:span="1"
contentClassName="descriptions-direction"
label="并网状态"
labelClassName="descriptions-label"
>{{
$store.state.ems.gridStatusOptions[pcsItem.gridStatus]
}}
</el-descriptions-item
>
<el-descriptions-item
:contentClassName="`descriptions-direction ${
pcsItem.deviceStatus === '1' ? 'save' : 'danger'
}`"
:span="1"
label="设备状态"
labelClassName="descriptions-label"
>{{
$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>
</div>
<div class="descriptions-main descriptions-main-bg-color">
<el-descriptions
:colon="false"
:column="4"
contentClassName="descriptions-direction"
direction="vertical"
labelClassName="descriptions-label"
>
<el-descriptions-item
v-for="(item, index) in infoData"
:key="index + 'pcsInfoData'"
:label="item.label"
:span="1"
>
<span
class="pointer"
@click="
showChart(item.pointName || '', pcsItem.deviceId)
"
>
{{ pcsItem[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>
</div> </div>
<div v-for="(item,index) in pcsItem.pcsBranchInfoList" :key="index+'pcsBranchInfoList'" class="descriptions-main"> <div
<el-descriptions :colon="false" :column="4" contentClassName="descriptions-direction keep" direction="vertical" labelClassName="descriptions-label"> v-for="(item, index) in pcsItem.pcsBranchInfoList"
<el-descriptions-item :label="'支路'+(index+1)" :span="4" contentClassName="descriptions-direction keep" labelClassName="descriptions-label">{{item.dischargeStatus}}</el-descriptions-item> :key="index + 'pcsBranchInfoList'"
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="直流功率" labelClassName="descriptions-label"> class="descriptions-main"
<span class="pointer" @click="showChart('直流功率','PCS分支设备',item.deviceId)">{{item.dcPower}}kW</span> >
</el-descriptions-item> <el-descriptions
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="直流电压" labelClassName="descriptions-label"> :colon="false"
<span class="pointer" @click="showChart('直流电压','PCS分支设备',item.deviceId)">{{item.dcVoltage}}V</span> :column="4"
</el-descriptions-item> contentClassName="descriptions-direction keep"
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="直流电流" labelClassName="descriptions-label"> direction="vertical"
<span class="pointer" @click="showChart('直流电流','PCS分支设备',item.deviceId)">{{item.dcCurrent}}A</span> labelClassName="descriptions-label"
</el-descriptions-item> >
</el-descriptions> <el-descriptions-item
</div> :label="'支路' + (index + 1)"
:span="4"
contentClassName="descriptions-direction keep"
labelClassName="descriptions-label"
>{{ item.dischargeStatus }}
</el-descriptions-item
>
<el-descriptions-item
:span="1"
contentClassName="descriptions-direction"
label="直流功率"
labelClassName="descriptions-label"
>
<span
class="pointer"
@click="showChart('直流功率', item.deviceId,true)"
>{{ item.dcPower }}kW</span
>
</el-descriptions-item>
<el-descriptions-item
:span="1"
contentClassName="descriptions-direction"
label="直流电压"
labelClassName="descriptions-label"
>
<span
class="pointer"
@click="showChart('直流电压', item.deviceId,true)"
>{{ item.dcVoltage }}V</span
>
</el-descriptions-item>
<el-descriptions-item
:span="1"
contentClassName="descriptions-direction"
label="直流电流"
labelClassName="descriptions-label"
>
<span
class="pointer"
@click="showChart('直流电流', item.deviceId,true)"
>{{ item.dcCurrent }}A</span
>
</el-descriptions-item>
</el-descriptions>
</div>
</el-card> </el-card>
</div> </div>
<el-empty v-show="pcsList.length<=0" :image-size="200"></el-empty> <el-empty v-show="pcsList.length <= 0" :image-size="200"></el-empty>
<!-- 电位图表 showChart({pointName:点位名称,categoryName:设备名称})-->
<point-chart ref="pointChart" :site-id="siteId"/> <point-chart ref="pointChart" :site-id="siteId"/>
<point-table ref="pointTable"/>
</div> </div>
</template> </template>
<script> <script>
import pointChart from "./../PointChart.vue"; import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import RealTimeBaseInfo from "./../RealTimeBaseInfo.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 {getPcsDetailInfo, getRunningHeadInfo} from "@/api/ems/dzjk";
import intervalUpdate from "@/mixins/ems/intervalUpdate"; import intervalUpdate from "@/mixins/ems/intervalUpdate";
import PcsSwitch from "@/views/ems/site/sblb/PcsSwitch.vue";
import {mapState} from "vuex";
export default { export default {
name:'DzjkSbjkPcs', name: "DzjkSbjkPcs",
components:{RealTimeBaseInfo,pointChart}, components: {RealTimeBaseInfo, pointChart, PointTable, PcsSwitch},
mixins:[getQuerySiteId,intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
computed: {
...mapState({
PCSWorkStatusOptions: state => state?.ems?.PCSWorkStatusOptions || {},
})
},
data() { data() {
return { return {
loading:false, loading: false,
runningHeadData:{},//运行信息 runningHeadData: {}, //运行信息
pcsList:[], pcsList: [],
infoData:[ infoData: [
{label:'总交流有功电率',attr:'totalActivePower',unit:'kW',pointName:'总交流有功电率'}, {
{label:'当天交流充电量',attr:'dailyAcChargeEnergy',unit:'kWh',pointName:'当天交流充电量 (kWh)'}, label: "总交流有功功率",
{label:'A相电压',attr:'aPhaseVoltage',unit:'V',pointName:''}, attr: "totalActivePower",
{label:'A相电流',attr:'aPhaseCurrent',unit:'A',pointName:'A相电流'}, unit: "kW",
{label:'总交流无功电率',attr:'totalReactivePower',unit:'kVar',pointName:'总交流无功电率'}, pointName: "总交流有功功率",
{label:'当天交流放电量',attr:'dailyAcDischargeEnergy',unit:'kWh',pointName:'当天交流放电量 (kWh)'}, },
{label:'B相电压',attr:'bPhaseVoltage',unit:'V',pointName:''}, {
{label:'B相电流',attr:'bPhaseCurrent',unit:'A',pointName:'B相电流'}, label: "当天交流充电量",
{label:'总交流视在功率',attr:'totalApparentPower',unit:'kVA',pointName:'总交流视在功率'}, attr: "dailyAcChargeEnergy",
{label:'PCS模块温度',attr:'pcsModuleTemperature',unit:'&#8451;',pointName:''}, unit: "kWh",
{label:'C相电压',attr:'cPhaseVoltage',unit:'V',pointName:''}, pointName: "当天交流充电量 (kWh)",
{label:'C相电流',attr:'cPhaseCurrent',unit:'A',pointName:'C相电流'}, },
{label:'总交流功率因数',attr:'totalPowerFactor',unit:'',pointName:'总交流功率因数'}, {label: "A相电压", attr: "aPhaseVoltage", unit: "V", pointName: ""},
{label:'PCS环境温度',attr:'pcsEnvironmentTemperature',unit:'&#8451;',pointName:''}, {
{label:'交流频率',attr:'acFrequency',unit:'Hz',pointName:'交流频率'} label: "A相电流",
attr: "aPhaseCurrent",
unit: "A",
pointName: "A相电流",
},
{
label: "总交流无功功率",
attr: "totalReactivePower",
unit: "kVar",
pointName: "总交流无功功率",
},
{
label: "当天交流放电量",
attr: "dailyAcDischargeEnergy",
unit: "kWh",
pointName: "当天交流放电量 (kWh)",
},
{label: "B相电压", attr: "bPhaseVoltage", unit: "V", pointName: ""},
{
label: "B相电流",
attr: "bPhaseCurrent",
unit: "A",
pointName: "B相电流",
},
{
label: "总交流视在功率",
attr: "totalApparentPower",
unit: "kVA",
pointName: "总交流视在功率",
},
{
label: "PCS模块温度",
attr: "pcsModuleTemperature",
unit: "&#8451;",
pointName: "",
},
{label: "C相电压", attr: "cPhaseVoltage", unit: "V", pointName: ""},
{
label: "C相电流",
attr: "cPhaseCurrent",
unit: "A",
pointName: "C相电流",
},
{
label: "总交流功率因数",
attr: "totalPowerFactor",
unit: "",
pointName: "总交流功率因数",
},
{
label: "PCS环境温度",
attr: "pcsEnvironmentTemperature",
unit: "&#8451;",
pointName: "",
},
{
label: "交流频率",
attr: "acFrequency",
unit: "Hz",
pointName: "交流频率",
},
], ],
pcsBranchList:[],//pcs的支路列表 pcsBranchList: [], //pcs的支路列表
} };
}, },
methods:{ methods: {
//todo 后续需要新增设备id handleCardClass(item) {
showChart(pointName,categoryName,deviceId){ const {workStatus = ''} = item
console.log('点击查询图表',pointName,categoryName,deviceId) return workStatus === '1' || !Object.keys(this.PCSWorkStatusOptions).find(i => i === workStatus) ? "timing-card-container" : workStatus === '2' ? 'warning-card-container' : 'running-card-container'
pointName && this.$refs.pointChart.showChart({pointName,categoryName,deviceId}) },
// 查看设备电位表格
pointDetail(row, dataType) {
const {deviceId} = row
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'PCS'}, dataType)
},
showChart(pointName, deviceId, isBranch = false) {
pointName &&
this.$refs.pointChart.showChart({pointName, deviceCategory: isBranch ? 'BRANCH' : 'PCS', deviceId});
}, },
//6个方块数据 //6个方块数据
getRunningHeadData(){ getRunningHeadData() {
getRunningHeadInfo(this.siteId).then(response => { getRunningHeadInfo(this.siteId).then((response) => {
this.runningHeadData = response?.data || {} this.runningHeadData = response?.data || {};
}) });
}, },
getPcsList(){ getPcsList() {
this.loading = true this.loading = true;
getPcsDetailInfo(this.siteId).then(response => { getPcsDetailInfo(this.siteId)
const data = response?.data || {} .then((response) => {
this.pcsList = JSON.parse(JSON.stringify(data)) const data = response?.data || {};
}).finally(()=>this.loading = false) this.pcsList = JSON.parse(JSON.stringify(data));
})
.finally(() => (this.loading = false));
}, },
updateData(){ updateData() {
this.getRunningHeadData() this.getRunningHeadData();
this.getPcsList() this.getPcsList();
},
init() {
this.updateData();
this.updateInterval(this.updateData);
}, },
init(){
this.updateData()
this.updateInterval(this.updateData)
}
}, },
};
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
</style>

View File

@ -1,8 +1,10 @@
<template> <template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding"> <el-card
shadow="always"
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">PCS有功功率/PCS无功功率</span>
</div> </div>
<div style="height: 360px" id="cnglqxChart"/> <div style="height: 360px" id="cnglqxChart"/>
</el-card> </el-card>
@ -10,98 +12,133 @@
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
<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 {formatDate} from "@/filters/ems"; import {storagePower} from "@/api/ems/dzjk";
import {storagePower} from '@/api/ems/dzjk'
export default { export default {
mixins: [resize], mixins: [resize],
data() { data() {
return { return {
chart: null chart: null,
} };
}, },
mounted() { mounted() {
this.chart = echarts.init(document.querySelector('#cnglqxChart')) this.chart = echarts.init(document.querySelector("#cnglqxChart"));
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
init(siteId){ init(siteId, timeRange) {
this.chart.showLoading() this.chart.showLoading();
const x = [] const [startTime = '', endTime = ''] = timeRange;
const data1 =[],data2 =[] storagePower(siteId, startTime, endTime)
storagePower(siteId).then(response => { .then((response) => {
const source = response?.data?.energyStoragePowList || [] this.setOption(response?.data?.pcsPowerList || []);
source.forEach(item=>{ })
x.push(formatDate(item.createDate,false,true)) .finally(() => {
data1.push(item.pcsTotalActPower) this.chart.hideLoading();
data2.push(item.pcsTotalReactivePower) });
})
this.setOption(x,data1,data2)
}).finally(()=>{
this.chart.hideLoading()
})
}, },
setOption(x,data1,data2) { setOption(data) {
this.chart.setOption({ let xdata = [],
color:['#FFBD00','#3C81FF'], series = [];
data.forEach((element, index) => {
if (index === 0) {
xdata = (element.energyStoragePowList || []).map((i) => i.createDate);
}
series.push(
{
type: "line",
name: `${element.deviceId}有功功率`,
areaStyle: {
// color:'#FFBD00'
},
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 && this.chart.setOption({
legend: { legend: {
left: 'center', left: "center",
top: '10', top: "5",
itemWidth: 10,
itemHeight: 5,
textStyle: {
fontSize: 9,
},
}, },
grid: { grid: {
containLabel: true containLabel: true,
}, },
tooltip: { tooltip: {
trigger: 'axis', show: true,
axisPointer: { // 坐标轴指示器,坐标轴触发有效 trigger: "axis",
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' axisPointer: {
// 坐标轴指示器,坐标轴触发有效
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:x}, xAxis: {type: "category", data: xdata},
yAxis: { yAxis: {
type: 'value', type: "value",
}, },
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
start: 0, start: 0,
end: 100 end: 100,
}, },
{ {
start: 0, start: 0,
end: 100 end: 100,
} },
], ],
series: [ series,
{ }, true);
name:'PCS实时有功功率', },
type: 'line', },
areaStyle: { };
color:'#FFBD00'
},
data: data1,
},{
name:'PCS实时无功功率',
type: 'line',
areaStyle: {
color: '#3C81FF'
},
data: data2
}]
})
}
}
}
</script> </script>

View File

@ -1,99 +1,122 @@
<template> <template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding"> <el-card
shadow="always"
class="common-card-container common-card-container-body-no-padding"
>
<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 style="height: 360px" id="dcpjsocChart" />
</el-card> </el-card>
</template> </template>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
<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 {formatDate} from "@/filters/ems"; import { batteryAveSoc } from "@/api/ems/dzjk";
import {batteryAveSoc} from '@/api/ems/dzjk'
export default { export default {
mixins: [resize], mixins: [resize],
data() { data() {
return { return {
chart: null chart: null,
} };
}, },
mounted() { mounted() {
this.chart = echarts.init(document.querySelector('#dcpjsocChart')) this.chart = echarts.init(document.querySelector("#dcpjsocChart"));
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
init(siteId){ init(siteId,timeRange) {
this.chart.showLoading() this.chart.showLoading();
const x = [] const [startTime='', endTime=''] = timeRange;
const data =[] batteryAveSoc(siteId,startTime,endTime)
batteryAveSoc(siteId).then(response => { .then((response) => {
const source = response?.data?.batteryAveSOCList || [] this.setOption(response?.data?.batteryAveSOCList || []);
source.forEach(item=>{
x.push(formatDate(item.createDate,false,true))
data.push(item.batterySOC)
}) })
this.setOption(x,data) .finally(() => {
}).finally(()=>{ this.chart.hideLoading();
this.chart.hideLoading() });
})
}, },
setOption(x,data) { setOption(data) {
this.chart.setOption({ let xdata = [],
color:['#FFBD00','#3C81FF'], ydata = [];
// legend: { data.forEach((element) => {
// left: 'center', xdata.push(element.createDate);
// bottom: '10', ydata.push({
// }, value:element.batterySOC,
tooltip: { year:element.dateDay,
trigger: 'axis', });
axisPointer: { // 坐标轴指示器,坐标轴触发有效 });
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' this.chart && this.chart.setOption({
} legend: {
left: "center",
top: "5",
itemWidth: 10,
itemHeight: 5,
textStyle: {
fontSize: 9,
},
}, },
grid: { grid: {
containLabel: true containLabel: true,
}, },
textStyle:{ tooltip: {
color:"#333333", show:true,
trigger: "axis",
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
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
}
}, },
xAxis: {type:'category',data:x}, textStyle: {
color: "#333333",
},
xAxis: { type: "category", data: xdata },
yAxis: { yAxis: {
type: 'value', type: "value",
}, },
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
start: 0, start: 0,
end: 100 end: 100,
}, },
{ {
start: 0, start: 0,
end: 100 end: 100,
} },
], ],
series: [ series: [
{ {
name:'电池平均SOC', type: "line",
data: data, name: `平均SOC`,
type: 'line',
areaStyle: { areaStyle: {
color:'#FFBD00' // color:'#FFBD00'
} },
data: ydata,
}] },
}) ],
} },true);
} },
} },
};
</script> </script>

View File

@ -1,99 +1,125 @@
<template> <template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding"> <el-card
shadow="always"
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> </div>
<div style="height: 360px" id="dcpjwdChart"/> <div style="height: 360px" id="dcpjwdChart" />
</el-card> </el-card>
</template> </template>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
<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 {formatDate} from "@/filters/ems"; import { batteryAveTemp } from "@/api/ems/dzjk";
import {batteryAveTemp} from '@/api/ems/dzjk'
export default { export default {
mixins: [resize], mixins: [resize],
data() { data() {
return { return {
chart: null chart: null,
} };
}, },
mounted() { mounted() {
this.chart = echarts.init(document.querySelector('#dcpjwdChart')) this.chart = echarts.init(document.querySelector("#dcpjwdChart"));
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
init(siteId){ init(siteId,timeRange) {
this.chart.showLoading() this.chart.showLoading();
const x = [] const [startTime='', endTime=''] = timeRange;
const data1 =[],data2 =[] batteryAveTemp(siteId,startTime,endTime)
batteryAveTemp(siteId).then(response => { .then((response) => {
const source = response?.data?.batteryAveTempList || [] this.setOption(response?.data?.batteryAveTempList || []);
source.forEach(item=>{
x.push(formatDate(item.createDate,false,true))
data1.push(item.batteryTemp)
}) })
this.setOption(x,data1,data2) .finally(() => {
}).finally(()=>{ this.chart.hideLoading();
this.chart.hideLoading() });
})
}, },
setOption(x,data) { setOption(data) {
this.chart.setOption({ let xdata = [],
color:['#3C81FF'], ydata = [];
// legend: { data.forEach((element) => {
// left: 'center', xdata.push(element.createDate);
// bottom: '10', ydata.push(
// }, {
tooltip: { value: element.batteryTemp,
trigger: 'axis', year: element.dateDay
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
} }
);
});
this.chart && this.chart.setOption({
legend: {
left: "center",
top: "5",
itemWidth: 10,
itemHeight: 5,
textStyle: {
fontSize: 9,
},
}, },
grid: { grid: {
containLabel: true containLabel: true,
}, },
textStyle:{ tooltip: {
color:"#333333", show:true,
trigger: "axis",
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
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
}
}, },
xAxis: {type:'category',data:x}, textStyle: {
color: "#333333",
},
xAxis: { type: "category", data: xdata },
yAxis: { yAxis: {
type: 'value', type: "value",
}, },
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
start: 0, start: 0,
end: 100 end: 100,
}, },
{ {
start: 0, start: 0,
end: 100 end: 100,
} },
], ],
series: [ series: [
{ {
name:'电池平均温度', type: "line",
data: data, name: `电池平均温度`,
type: 'line',
areaStyle: { areaStyle: {
color:'#3C81FF' // color:'#FFBD00'
}, },
}] data: ydata,
}) },
} ],
} },true);
} },
},
};
</script> </script>

View File

@ -1,96 +1,125 @@
<template> <template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding"> <el-card
shadow="always"
class="common-card-container common-card-container-body-no-padding"
>
<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 style="height: 360px" id="pocpjwdChart" />
</el-card> </el-card>
</template> </template>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
<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 {formatDate} from "@/filters/ems"; import { pcsMaxTemp } from "@/api/ems/dzjk";
import {stackAveTemp} from '@/api/ems/dzjk'
export default { export default {
mixins: [resize], mixins: [resize],
data() { data() {
return { return {
chart: null chart: null,
} };
}, },
mounted() { mounted() {
this.chart = echarts.init(document.querySelector('#pocpjwdChart')) this.chart = echarts.init(document.querySelector("#pocpjwdChart"));
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
methods: { methods: {
init(siteId){ init(siteId,timeRange) {
this.chart.showLoading() this.chart.showLoading();
const x = [] const [startTime='', endTime=''] = timeRange;
const data =[] pcsMaxTemp(siteId,startTime,endTime)
stackAveTemp(siteId).then(response => { .then((response) => {
const source = response?.data?.stackAveTempList || [] this.setOption(response?.data?.pcsMaxTempList || []);
source.forEach(item=>{
x.push(formatDate(item.createDate,false,true))
data.push(item.temp)
}) })
this.setOption(x,data) .finally(() => {
}).finally(()=>{ this.chart.hideLoading();
this.chart.hideLoading() });
})
}, },
setOption(x,data) { setOption(data) {
this.chart.setOption({ let xdata = [],
color:['#FFBD00','#3C81FF'], series = [];
tooltip: { data.forEach((element, index) => {
trigger: 'axis', if (index === 0) {
axisPointer: { // 坐标轴指示器,坐标轴触发有效 xdata = (element.maxTempVoList || []).map((i) => i.createDate);
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' }
} series.push({
type: "line",
name: `${element.deviceId}最高温度`,
areaStyle: {
// color:'#FFBD00'
},
data: (element.maxTempVoList || []).map((i) => {
return {
value: i.temp,
year: i.dateDay
}
}),
});
});
this.chart && this.chart.setOption({
legend: {
left: "center",
top: "5",
itemWidth: 10,
itemHeight: 5,
textStyle: {
fontSize: 9,
},
}, },
grid: { grid: {
containLabel: true containLabel: true,
}, },
textStyle:{ tooltip: {
color:"#333333", show:true,
trigger: "axis",
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
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
}
}, },
xAxis: {type:'category',data:x}, textStyle: {
color: "#333333",
},
xAxis: { type: "category", data: xdata },
yAxis: { yAxis: {
type: 'value', type: "value",
}, },
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
start: 0, start: 0,
end: 100 end: 100,
}, },
{ {
start: 0, start: 0,
end: 100 end: 100,
} },
], ],
series: [ series,
{ },true);
name:'PCS平均温度', },
data: data, },
type: 'line', };
areaStyle: {
color:'#FFBD00'
}
}]
})
}
}
}
</script> </script>

View File

@ -3,6 +3,8 @@
<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 :data="runningHeadData"/>
<!-- 时间选择 -->
<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">
@ -27,6 +29,7 @@
</style> </style>
<script> <script>
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'
import PocpjwdChart from './PocpjwdChart.vue' import PocpjwdChart from './PocpjwdChart.vue'
@ -38,11 +41,13 @@ import intervalUpdate from "@/mixins/ems/intervalUpdate";
export default { export default {
name:'DzjkSbjkSsyx', name:'DzjkSbjkSsyx',
components:{RealTimeBaseInfo,CnglqxChart,PocpjwdChart,DcpjwdChart,DcpjsocChart}, components:{RealTimeBaseInfo,CnglqxChart,PocpjwdChart,DcpjwdChart,DcpjsocChart,DateRangeSelect},
mixins:[getQuerySiteId,intervalUpdate], mixins:[getQuerySiteId,intervalUpdate],
data() { data() {
return { return {
runningHeadData:{},//运行信息 runningHeadData:{},//运行信息
timeRange:[],
isInit:true
} }
}, },
methods:{ methods:{
@ -52,17 +57,27 @@ export default {
this.runningHeadData = response?.data || {} this.runningHeadData = response?.data || {}
}) })
}, },
// 更新时间范围 重置图表
updateDate(data){
this.timeRange=data
!this.isInit && this.updateChart()
this.isInit = false
},
updateChart(){
this.$refs.cnglqx.init(this.siteId,this.timeRange||[])
this.$refs.pocpjwd.init(this.siteId,this.timeRange||[])
this.$refs.dcpjsoc.init(this.siteId,this.timeRange||[])
this.$refs.dcpjwd.init(this.siteId,this.timeRange||[])
this.updateInterval(this.updateData)
},
updateData(){ updateData(){
this.getRunningHeadData() this.getRunningHeadData()
this.$refs.cnglqx.init(this.siteId) this.updateChart()
this.$refs.pocpjwd.init(this.siteId)
this.$refs.dcpjsoc.init(this.siteId)
this.$refs.dcpjwd.init(this.siteId)
}, },
init(){ init(){
this.$refs.dateRangeSelect.init(true)
this.$nextTick(()=>{ this.$nextTick(()=>{
this.updateData() this.updateData()
this.updateInterval(this.updateData)
}) })
} }
} }

View File

@ -0,0 +1,134 @@
<template>
<div v-loading="loading">
<el-card
v-for="(item,index) in list"
:key="index+'ylLise'"
class="sbjk-card-container running-card-container"
:class="{
'warning-card-container':item.emsCommunicationStatus && item.emsCommunicationStatus !== '0',
'running-card-container':item.emsCommunicationStatus === '0'
}"
shadow="always">
<div slot="header">
<span class="large-title">{{ item.deviceName }}</span>
<div class="info">
<div>
{{
$store.state.ems.communicationStatusOptions[
item.emsCommunicationStatus
]
}}
</div>
<div>数据更新时间{{ item.dataUpdateTime || '-' }}</div>
</div>
<div class="alarm">
<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="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>
</template>
<script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getXfDataList} from '@/api/ems/dzjk'
import intervalUpdate from "@/mixins/ems/intervalUpdate";
import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue";
export default {
name: 'DzjkSbjkXf',
mixins: [getQuerySiteId, intervalUpdate],
components: {pointChart, PointTable},
data() {
return {
loading: false,
list: [],
tempData: [
{title: '主电源备用电池状态', attr: 'dczt', unit: ''},
{title: '手自动状态延时状态', attr: 'yszt', unit: ''},
{title: '启动喷洒气体喷洒状态', attr: 'pszt', unit: ''},
{title: '压力开关状态电磁阀状态', attr: 'dcfzt', unit: ''},
]
}
},
methods: {
// 查看设备电位表格
pointDetail(row, dataType) {
const {deviceId} = row
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'XF'}, dataType)
},
showChart(pointName, deviceId) {
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'XF', deviceId})
},
updateData() {
this.loading = true
getXfDataList(this.siteId).then(response => {
this.list = JSON.parse(JSON.stringify(response?.data || []));
}).finally(() => {
this.loading = false
})
},
init() {
this.updateData()
this.updateInterval(this.updateData)
}
},
mounted() {
}
}
</script>
<style scoped lang="scss">
.sbjk-card-container {
&:not(:last-child) {
margin-bottom: 25px;
}
.el-row {
background-color: #ffffff;
border: 1px solid #eeeeee;
font-size: 14px;
line-height: 16px;
color: #333333;
.el-col {
padding: 12px 0;
text-align: center;
position: relative;
}
.el-col {
border-bottom: 1px solid #eeeeee;
}
.el-col:not(:nth-child(3n)) {
border-right: 1px solid #eeeeee;
}
}
}
</style>

View File

@ -1,25 +1,42 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<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 common-card-container-body-no-padding common-card-container-no-title-bg" class="sbjk-card-container running-card-container"
shadow="always"> shadow="always">
<div slot="header"> <div slot="header">
<span class="large-title">{{index+1}}#{{item.deviceName}}</span> <span class="large-title">{{ item.deviceName }}</span>
<div class="info">
<div>数据更新时间{{ item.dataUpdateTime || '-' }}</div>
</div> </div>
<el-row> <div class="alarm">
<el-col v-for="(tempDataItem,tempDataIndex) in tempData" :key="tempDataIndex+'ylTempData'" :span="8"> <el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
<span class="pointer" @click="showChart(tempDataItem.title,item.deviceName,item.deviceId)"> 详细
{{tempDataItem.title}}{{item[tempDataItem.attr]}}<span v-html="tempDataItem.unit"></span> </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+'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> </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"/>
</div> <point-table ref="pointTable"/>
</div>
</template> </template>
@ -28,80 +45,59 @@ import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getCoolingDataList} from '@/api/ems/dzjk' import {getCoolingDataList} 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";
export default { export default {
name:'DzjkSbjkYl', name: 'DzjkSbjkYl',
mixins:[getQuerySiteId,intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
components:{pointChart}, components: {pointChart, PointTable},
data() { data() {
return { return {
loading:false, loading: false,
list:[], list: [],
tempData:[ tempData: [
{title:'供水温度',attr:'gsTemp',unit:'&#8451;'}, {title: '供水温度', attr: 'gsTemp', unit: '&#8451;'},
{title:'回水温度',attr:'hsTemp',unit:'&#8451;'}, {title: '回水温度', attr: 'hsTemp', unit: '&#8451;'},
{title:'供水压力',attr:'gsPressure',unit:'bar'}, {title: '供水压力', attr: 'gsPressure', unit: 'bar'},
{title:'回水压力',attr:'hsPressure',unit:'bar'}, {title: '回水压力', attr: 'hsPressure', unit: 'bar'},
{title:'冷源水温度',attr:'lysTemp',unit:'&#8451;'}, {title: '冷源水温度', attr: 'lysTemp', unit: '&#8451;'},
{title:'VB01开度',attr:'vb01Kd',unit:'%'}, {title: 'VB01开度', attr: 'vb01Kd', unit: '%'},
{title:'VB02开度',attr:'vb02Kd',unit:'%'}, {title: 'VB02开度', attr: 'vb02Kd', unit: '%'},
] ]
} }
}, },
methods:{ methods: {
showChart(pointName,deviceName,deviceId){ // 查看设备电位表格
console.log('点击查询图表',pointName,deviceName,deviceId) pointDetail(row, dataType) {
pointName && this.$refs.pointChart.showChart({pointName,deviceName,deviceId}) const {deviceId} = row
this.$refs.pointTable.showTable({siteId: this.siteId, deviceId, deviceCategory: 'COOLING'}, dataType)
}, },
updateData(){ showChart(pointName, deviceId) {
pointName && this.$refs.pointChart.showChart({pointName, deviceCategory: 'COOLING', deviceId})
},
updateData() {
this.loading = true this.loading = true
getCoolingDataList(this.siteId).then(response => { getCoolingDataList(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{
background-color: #ffffff;
border:1px solid #eeeeee;
font-size: 14px;
line-height: 16px;
color: #333333;
.el-col{
padding:12px 0;
text-align: center;
position: relative;
}
.el-col{
border-bottom: 1px solid #eeeeee;
}
.el-col:not(:nth-child(3n)){
border-right: 1px solid #eeeeee;
}
}
}
.yl-warn-item-container{
background-color: #FFF1F0;
.header{
.header-title{
background-color: #FC6B69;
}
.header-values{
color: #FC6B69;
}
}
} }
</style> </style>

View File

@ -1,25 +1,19 @@
<template> <template>
<div style="width:100%" v-loading="loading"> <div style="width:100%" 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="search.deviceId" placeholder="请选择" loading-text="正在加载数据">
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in deviceOptions" :key="index+'dbOptions'"></el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="日报">-->
<!-- <el-select v-model="search.rb" placeholder="请选择" :loading="loading" loading-text="正在加载数据">-->
<!-- <el-option :label="item.name" :value="item.id" v-for="(item,index) in rbOptions" :key="index+'rbOptions'"></el-option>-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item label="时间选择"> <el-form-item label="时间选择">
<el-date-picker <el-date-picker
v-model="search.date" v-model="dateRange"
type="date" type="daterange"
:picker-options="pickerOptions" range-separator=""
:default-value="defaultDate"> start-placeholder="开始日期"
</el-date-picker> end-placeholder="结束日期"
value-format="yyyy-MM-dd"
:clearable="false"
:picker-options="pickerOptions"
:default-value="defaultDateRange"
></el-date-picker>
</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>
@ -27,157 +21,194 @@
<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">
<template slot-scope="scope"> </el-table-column>
<span>{{scope.row.dataTime}}{{scope.row.dataTime === '汇总' ? '' : ':00'}}</span> </el-table-column>
</template> <!--充电量列-->
</el-table-column> <el-table-column label="充电量" align="center">
</el-table-column> <el-table-column
<!--充电量列--> align="center"
<el-table-column label="充电量"> prop="activePeakKwh"
<el-table-column label="尖">
prop="activePeakKwh" </el-table-column>
label="尖"> <el-table-column
</el-table-column> align="center"
<el-table-column prop="activeHighKwh"
prop="activeHighKwh" label="峰">
label="峰"> </el-table-column>
</el-table-column> <el-table-column
<el-table-column align="center"
prop="activeFlatKwh" prop="activeFlatKwh"
label="平"> label="平">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="activeValleyKwh" align="center"
label="谷"> prop="activeValleyKwh"
</el-table-column> label="谷">
<el-table-column </el-table-column>
prop="activeTotalKwh" <el-table-column
label="总"> align="center"
</el-table-column> prop="activeTotalKwh"
</el-table-column> label="总">
<!--充电量列--> </el-table-column>
<el-table-column label="放电量"> </el-table-column>
<el-table-column <!--充电量列-->
prop="reActivePeakKwh" <el-table-column label="放电量" align="center">
label="尖"> <el-table-column
</el-table-column> align="center"
<el-table-column prop="reActivePeakKwh"
prop="reActiveHighKwh" label="尖">
label="峰"> </el-table-column>
</el-table-column> <el-table-column
<el-table-column align="center"
prop="reActiveFlatKwh" prop="reActiveHighKwh"
label=""> label="">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="reActiveValleyKwh" align="center"
label="谷"> prop="reActiveFlatKwh"
</el-table-column> label="平">
<el-table-column </el-table-column>
prop="reActiveTotalKwh" <el-table-column
label="总"> align="center"
</el-table-column> prop="reActiveValleyKwh"
</el-table-column> label="谷">
<!-- 效率--> </el-table-column>
<el-table-column label="效率(%)"> <el-table-column
<el-table-column align="center"
prop="effect"> prop="reActiveTotalKwh"
</el-table-column> label="总">
</el-table-column> </el-table-column>
</el-table> </el-table-column>
<!-- 效率-->
<el-table-column label="效率(%)" align="center">
<el-table-column
align="center"
prop="effect">
</el-table-column>
</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"
>
</el-pagination>
</div> </div>
</template> </template>
<script> <script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { getAmmeterData, getLoadNameList} 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();
}, },
}, },
defaultDate:'',//默认展示的时间 defaultDateRange: [],//默认展示的时间
search:{deviceId:'',date:''}, dateRange: [],
deviceOptions:[], tableData: [],
// rbOptions:[ pageSize: 10,//分页栏当前每个数据总数
// {name:'日报1',id:1}, pageNum: 1,//分页栏当前页数
// {name:'日报2',id:2}, totalSize: 0,//table表格数据总数
// ],
tableData:[]
} }
}, },
methods:{ methods: {
// 导出表格
exportTable() {
if (!this.dateRange?.length) return
const [startTime, endTime] = this.dateRange
this.download('ems/statsReport/exportAmmeterData', {
siteId: this.siteId,
startTime,
endTime,
}, `电表报表_${startTime}-${endTime}.xlsx`)
},
// 搜索 // 搜索
onSearch(){ onSearch() {
this.pageNum = 1//每次搜索从1开始搜索
this.getData() this.getData()
}, },
// 重置 // 重置
onReset(){ onReset() {
this.search.date = '' this.dateRange = this.defaultDateRange
this.pageNum = 1//每次搜索从1开始搜索
this.getData() this.getData()
}, },
// 分页
handleSizeChange(val) {
this.pageSize = val;
this.$nextTick(() => {
this.getData()
})
},
handleCurrentChange(val) {
this.pageNum = val
this.$nextTick(() => {
this.getData()
})
},
// 获取数据 // 获取数据
getData(){ getData() {
if(!this.search.deviceId) return this.loading = true
this.loading=true const {siteId, pageNum, pageSize} = this
getAmmeterData({siteId:this.siteId,deviceId:this.search.deviceId,dateTime:formatDate(this.search.date)}).then(response=>{ const [startTime = '', endTime = ''] = (this.dateRange || [])
this.tableData=response?.data || []; getAmmeterData({siteId: siteId, startTime, endTime, pageSize, pageNum}).then(response => {
}).finally(()=> { this.tableData = response?.rows || [];
this.totalSize = response?.total || 0
}).finally(() => {
this.loading = false this.loading = false
}) })
}, },
getDbList(){ init() {
return getLoadNameList(this.siteId).then(response=>{ this.dateRange = []
this.deviceOptions=response?.data || []; this.tableData = []
this.deviceOptions.length > 0 && (this.search.deviceId = this.deviceOptions[0].id); this.totalSize = 0
}) this.pageSize = 10
}, this.pageNum = 1
init(){ let now = new Date(), lastDay = now.getTime(), firstDay = new Date(now.setDate(1)).getTime();
this.loading = true this.defaultDateRange = [formatDate(firstDay), formatDate(lastDay)];
this.deviceOptions = [] this.dateRange = [formatDate(firstDay), formatDate(lastDay)];
this.search.deviceId='' this.getData()
this.search.date=''
this.tableData=[]
this.getDbList().then(()=>{
if(this.search.deviceId){
this.onReset()
}else{
this.loading = false
}
})
}, },
}, },
mounted(){
this.defaultDate = new Date()
}
} }
</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;
} }
} }

View File

@ -1,165 +1,208 @@
<template> <template>
<el-card shadow="always" class="common-card-container time-range-card" style="margin-top:20px"> <el-card
shadow="always"
class="common-card-container time-range-card"
style="margin-top: 20px"
>
<div slot="header" class="time-range-header"> <div slot="header" class="time-range-header">
<span class="card-title"> <span class="card-title"> </span>
<el-button-group class="ems-btns-group">
<el-button v-for="(item,index) in btnList" :key="index+'dcdqxBtns'" size="mini" :class="{'activeBtn' : activeBtn === item.id}" @click="changeDataType(item.id)">{{item.name}}</el-button>
</el-button-group>
</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">
<div id="dcdEchart" style="height: 310px;"></div> <el-button-group class="ems-btns-group">
<el-button
v-for="(item, index) in btnList"
:key="index + 'dcdqxBtns'"
size="mini"
:class="{ activeBtn: activeBtn === item.id }"
@click="changeDataType(item.id)"
>{{ item.name }}
</el-button
>
</el-button-group>
<div id="dcdEchart" style="height: 310px"></div>
</div> </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 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 {
pickerOptions:{ pickerOptions: {
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
}, },
}, },
dateRange:[], dateRange: [],
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"]},
], ],
} };
}, },
methods: { methods: {
changeDataType(id){ changeDataType(id) {
if(id !== this.activeBtn){ if (id !== this.activeBtn) {
this.activeBtn=id; this.activeBtn = id;
this.getData() this.getData();
} }
}, },
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.dateRange=data || [] this.dateRange = data || [];
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;
getStackData({siteId,startTime:formatDate(start),endTime:formatDate(end),dataType:activeBtn}).then(response => { getStackData({
this.setOption(response?.data || []) siteId,
}).finally(()=>{this.loading=false;}) startTime: formatDate(start),
endTime: formatDate(end),
dataType: activeBtn,
})
.then((response) => {
this.setOption(response?.data || []);
})
.finally(() => {
this.loading = false;
});
}, },
compareDate(date1,date2){ compareDate(date1, date2) {
console.log('比较时间',date1,date2) console.log("比较时间", date1, date2);
// 年2025-09/天2025-09-15/时2025-09-15/10:00 // 年2025-09/天2025-09-15/时2025-09-15/10:00
if(date1.indexOf(':') > -1 && date2.indexOf(':') > -1){ if (date1.indexOf(":") > -1 && date2.indexOf(":") > -1) {
return parseInt(date1) - parseInt(date2) return parseInt(date1) - parseInt(date2);
} }
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 (date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) || (date1_Y === date2_Y && date1_M - date2_M) || date1_Y - date2_Y return (
(date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) ||
(date1_Y === date2_Y && date1_M - date2_M) ||
date1_Y - date2_Y
);
}, },
setOption(data) { setOption(data) {
const ele = this.btnList.find((item)=>{return item.id === this.activeBtn}) const ele = this.btnList.find((item) => {
const sourceBase = JSON.parse(JSON.stringify(ele.source)) return item.id === this.activeBtn;
});
const sourceBase = JSON.parse(JSON.stringify(ele.source));
// sourceBase={name:'堆平均维度',id:'1',attr:['temp'],source:['有功功率']}, // sourceBase={name:'堆平均维度',id:'1',attr:['temp'],source:['有功功率']},
const source=[] const source = [];
const sourceTop = ['日期'] const sourceTop = ["日期"];
let map={},mapArr=[] let map = {},
mapArr = [];
// 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...] // 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...]
data.forEach((item)=>{ data.forEach((item) => {
item.dataList.forEach((inner)=>{ item.dataList.forEach((inner) => {
// 日期格式 // 日期格式
// 年2025-09/天2025-09-15/时2025-09-15/10:00 // 年2025-09/天2025-09-15/时2025-09-15/10:00
// 所有数据的日期格式一致 // 所有数据的日期格式一致
if(!map[inner.statisDate]) { if (!map[inner.statisDate]) {
map[inner.statisDate] = [] map[inner.statisDate] = [];
mapArr.push(inner.statisDate) mapArr.push(inner.statisDate);
} }
}) });
}) });
data.forEach((item,itemIndex)=>{ data.forEach((item, itemIndex) => {
const dataTimeList = item.dataList.map(i =>i.statisDate) const dataTimeList = item.dataList.map((i) => i.statisDate);
const noDataTime = mapArr.filter(i=>!dataTimeList.includes(i)) const noDataTime = mapArr.filter((i) => !dataTimeList.includes(i));
sourceBase.forEach((outer,outerIndex)=>{ sourceBase.forEach((outer, outerIndex) => {
sourceTop.push(`${item.deviceId}-${outer}`) sourceTop.push(`${item.deviceId}-${outer}`);
noDataTime.forEach(i=>map[i].push('')) noDataTime.forEach((i) => map[i].push(""));
item.dataList.forEach((inner,innerIndex)=>{ item.dataList.forEach((inner, innerIndex) => {
map[inner.statisDate].push(inner[ele.attr[outerIndex]]) map[inner.statisDate].push(inner[ele.attr[outerIndex]]);
}) });
}) });
}) });
mapArr = mapArr.sort((a,b)=>this.compareDate(a,b)) mapArr = mapArr.sort((a, b) => this.compareDate(a, b));
mapArr.forEach(item=>{ mapArr.forEach((item) => {
source.push([item,...map[item]]) source.push([item, ...map[item]]);
}) });
source.unshift(sourceTop) source.unshift(sourceTop);
console.log('map=',map) this.chart.setOption(
console.log('mapArr=',mapArr) {
console.log('========',source) grid: {
this.chart.setOption({ containLabel: true,
grid: { },
containLabel: true legend: {
}, left: "center",
legend: { top: "10",
left: 'center', },
bottom: '15', tooltip: {
}, trigger: "axis",
tooltip: { axisPointer: {
trigger: 'axis', // 坐标轴指示器,坐标轴触发有效
axisPointer: { // 坐标轴指示器,坐标轴触发有效 type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' },
} },
}, textStyle: {
textStyle:{ color: "#333333",
color:"#333333", },
}, xAxis: {
xAxis: { type: "category",
type: 'category', },
}, yAxis: {
yAxis: { type: "value",
type: 'value', },
}, dataZoom: [
dataset: {source}, {
series:source[0].slice(1).map(item=>{ type: "inside",
return { start: 0,
type:ele.type || 'scatter' end: 100,
} },
}) {
},true) start: 0,
end: 100,
},
],
dataset: {source},
series: source[0].slice(1).map((item) => {
return {
type: "line",
smooth: true,
areaStyle: {
opacity: 0.7,
},
};
}),
},
true
);
}, },
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#dcdEchart')); this.chart = echarts.init(document.querySelector("#dcdEchart"));
},
init() {
this.$nextTick(() => {
this.initChart();
this.$refs.dateRangeSelect.init(true);
});
}, },
init(){
this.$nextTick(()=>{
this.initChart()
this.$refs.dateRangeSelect.init()
})
}
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
} };
</script> </script>

View File

@ -1,128 +1,168 @@
<template> <template>
<el-card shadow="always" class="common-card-container time-range-card" style="margin-top:20px"> <el-card
shadow="always"
class="common-card-container time-range-card"
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" @reset="resetTime" @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">
<div id="glqxEchart" style="height: 310px;"></div> <div id="glqxEchart" style="height: 310px"></div>
</div> </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 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 {
pickerOptions:{ pickerOptions: {
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
}, },
}, },
dateRange:[], dateRange: [],
loading:false, loading: false,
dateRangeInit:true, };
}
}, },
methods: { methods: {
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.dateRange=data || [] this.dateRange = data || [];
this.getData() this.getData();
}, },
resetTime(){ getData() {
this.dateRangeInit=true; const {siteId} = this;
}, let [start = "", end = ""] = this.dateRange || [];
getData(){
const {siteId}=this;
let [start='',end='']=(this.dateRange || [])
//接口调用完成之后 设置图表、结束loading //接口调用完成之后 设置图表、结束loading
this.loading=true; this.loading = true;
if(this.dateRangeInit){ getPowerData({
start = '' siteId,
end = '' startDate: formatDate(start),
this.dateRangeInit=false endDate: formatDate(end),
} })
getPowerData({siteId,startDate:formatDate(start),endDate:formatDate(end)}).then(response => { .then((response) => {
this.setOption(response?.data || []) this.setOption(response?.data || []);
}).finally(()=>{this.loading=false;}) })
.finally(() => {
this.loading = false;
});
}, },
setOption(data) { setOption(data) {
const source = [['日期','电网功率','负载功率','储能功率','光伏功率']] const source = [["日期", "电网功率", "负载功率", "储能功率", "光伏功率"]];
data.forEach(item=>{ data.forEach((item) => {
source.push([item.statisDate,item.gridPower,item.loadPower,item.storagePower,item.pvPower]) source.push([
}) item.statisDate,
this.chart.setOption({ item.gridPower,
grid: { item.loadPower,
containLabel: true item.storagePower,
}, item.pvPower,
legend: { ]);
left: 'center', });
bottom: '15', this.chart.setOption(
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
textStyle:{
color:"#333333",
},
xAxis: {
type: 'category',
},
yAxis: {
type: 'value',
},
dataset:{source},
series: [
{ {
type: 'scatter', grid: {
containLabel: true,
},
legend: {
left: "center",
top: "10",
},
tooltip: {
trigger: "axis",
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
},
},
textStyle: {
color: "#333333",
},
xAxis: {
type: "category",
},
yAxis: {
type: "value",
},
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
type: 'scatter', );
},
{
type: 'scatter',
},
{
type: 'scatter',
}
]
},true)
}, },
initChart() { initChart() {
if(this.chart) return if (this.chart) return;
this.chart = echarts.init(document.querySelector('#glqxEchart')); this.chart = echarts.init(document.querySelector("#glqxEchart"));
},
init() {
this.$nextTick(() => {
this.initChart();
this.$refs.dateRangeSelect.init();
});
}, },
init(){
this.$nextTick(()=>{
this.dateRangeInit=true;
this.initChart()
this.$refs.dateRangeSelect.init()
})
}
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
} };
</script> </script>

View File

@ -7,47 +7,55 @@
text-color="#666666" text-color="#666666"
active-text-color="#ffffff" active-text-color="#ffffff"
> >
<el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'tjbbChildrenRoute'"> <el-menu-item
<router-link style="height: 100%;width: 100%;display: block;" :to="{path:item.path,query:$route.query}"> :index="item.name"
{{item.meta.title}} v-for="(item, index) in childrenRoute"
:key="index + 'tjbbChildrenRoute'"
>
<router-link
style="height: 100%; width: 100%; display: block"
:to="{ path: item.path, query: $route.query }"
>
{{ item.meta.title }}
</router-link> </router-link>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<div class="ems-content-container ems-content-container-padding tjbb-ems-content-container"> <div
class="ems-content-container ems-content-container-padding tjbb-ems-content-container"
>
<keep-alive> <keep-alive>
<router-view></router-view> <router-view></router-view>
</keep-alive> </keep-alive>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { dzjk } from '@/router/ems' import { dzjk } from "@/router/ems";
const childrenRoute = dzjk[0].children[0].children.find(item=> item.name==='DzjkTjbb').children//获取到统计报表下面的字路由 const childrenRoute = dzjk[0].children.find(
console.log('设备监控子路由',childrenRoute) (item) => item.name === "DzjkTjbb"
).children; //获取到统计报表下面的字路由
console.log("设备监控子路由", childrenRoute);
export default { export default {
name:'DzjkTjbb', name: "DzjkTjbb",
data(){ data() {
return { return {
childrenRoute, childrenRoute,
activeMenu:'' activeMenu: "",
} };
}, },
mounted() { mounted() {
console.log('当前统计报表页面路由',this.$route) console.log("当前统计报表页面路由", this.$route);
} },
};
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.tjbb-ems-content-container{ .tjbb-ems-content-container {
margin-top:0; margin-top: 0;
padding-top:0; padding-top: 0;
padding-right: 0; padding-right: 0;
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -1,167 +1,224 @@
<template> <template>
<el-card shadow="always" class="common-card-container time-range-card" style="margin-top:20px"> <el-card
shadow="always"
class="common-card-container time-range-card"
style="margin-top: 20px"
>
<div slot="header" class="time-range-header"> <div slot="header" class="time-range-header">
<span class="card-title"> <span class="card-title"> </span>
<el-button-group class="ems-btns-group">
<el-button v-for="(item,index) in btnList" :key="index+'flqxcBtns'" size="mini" :class="{'activeBtn' : activeBtn === item.id}" @click="changeDataType(item.id)">{{item.name}}</el-button>
</el-button-group>
</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">
<div id="pcsEchart" style="height: 310px;"></div> <el-button-group class="ems-btns-group">
<el-button
v-for="(item, index) in btnList"
:key="index + 'flqxcBtns'"
size="mini"
:class="{ activeBtn: activeBtn === item.id }"
@click="changeDataType(item.id)"
>{{ item.name }}
</el-button
>
</el-button-group>
<div id="pcsEchart" style="height: 310px"></div>
</div> </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 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 {
pickerOptions:{ pickerOptions: {
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
}, },
}, },
dateRange:[], dateRange: [],
loading:false, loading: false,
activeBtn:'1', activeBtn: "1",
btnList:[ btnList: [
{name:'有功功率',id:'1',attr:['activePower'],source:['有功功率']}, {
{name:'无功功率',id:'2',attr:['reactivePower'],source:['无功功率']}, name: "有功功率",
{name:'三相电流',id:'3',attr:['uCurrent','vCurrent','wCurrent'],source:['u电流','v电流','w电流'],type:'bar'}, id: "1",
attr: ["activePower"],
source: ["有功功率"],
},
{
name: "无功功率",
id: "2",
attr: ["reactivePower"],
source: ["无功功率"],
},
{
name: "三相电流",
id: "3",
attr: ["uCurrent", "vCurrent", "wCurrent"],
source: ["u电流", "v电流", "w电流"],
type: "bar",
},
], ],
};
}
}, },
methods: { methods: {
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();
} }
}, },
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.dateRange=data || [] this.dateRange = data || [];
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
getPCSData({siteId,startTime:formatDate(start),endTime:formatDate(end),dataType:activeBtn}).then(response => { getPCSData({
this.setOption(response?.data || []) siteId,
}).finally(()=>{this.loading=false;}) startTime: formatDate(start),
endTime: formatDate(end),
dataType: activeBtn,
})
.then((response) => {
this.setOption(response?.data || []);
})
.finally(() => {
this.loading = false;
});
}, },
compareDate(date1,date2){ compareDate(date1, date2) {
console.log('比较时间',date1,date2) console.log("比较时间", date1, date2);
// 年2025-09/天2025-09-15/时2025-09-15/10:00 // 年2025-09/天2025-09-15/时2025-09-15/10:00
if(date1.indexOf(':') > -1 && date2.indexOf(':') > -1){ if (date1.indexOf(":") > -1 && date2.indexOf(":") > -1) {
return parseInt(date1) - parseInt(date2) return parseInt(date1) - parseInt(date2);
} }
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 (date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) || (date1_Y === date2_Y && date1_M - date2_M) || date1_Y - date2_Y return (
(date1_Y === date2_Y && date1_M === date2_M && date1_D - date2_D) ||
(date1_Y === date2_Y && date1_M - date2_M) ||
date1_Y - date2_Y
);
}, },
setOption(data) { setOption(data) {
const ele = this.btnList.find((item)=>{return item.id === this.activeBtn}) const ele = this.btnList.find((item) => {
const sourceBase = JSON.parse(JSON.stringify(ele.source)) return item.id === this.activeBtn;
});
const sourceBase = JSON.parse(JSON.stringify(ele.source));
// sourceBase={name:'堆平均维度',id:'1',attr:['temp'],source:['有功功率']}, // sourceBase={name:'堆平均维度',id:'1',attr:['temp'],source:['有功功率']},
const source=[] const source = [];
const sourceTop = ['日期'] const sourceTop = ["日期"];
let map={},mapArr=[] let map = {},
mapArr = [];
// 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...] // 生成所有{日期:[],日期:[]}格式的对象和所有包含所有日期的[日期1,日期2...]
data.forEach((item)=>{ data.forEach((item) => {
item.dataList.forEach((inner)=>{ item.dataList.forEach((inner) => {
// 日期格式 // 日期格式
// 年2025-09/天2025-09-15/时2025-09-15/10:00 // 年2025-09/天2025-09-15/时2025-09-15/10:00
// 所有数据的日期格式一致 // 所有数据的日期格式一致
if(!map[inner.statisDate]) { if (!map[inner.statisDate]) {
map[inner.statisDate] = [] map[inner.statisDate] = [];
mapArr.push(inner.statisDate) mapArr.push(inner.statisDate);
} }
}) });
}) });
data.forEach((item,itemIndex)=>{ data.forEach((item, itemIndex) => {
const dataTimeList = item.dataList.map(i =>i.statisDate) const dataTimeList = item.dataList.map((i) => i.statisDate);
const noDataTime = mapArr.filter(i=>!dataTimeList.includes(i)) const noDataTime = mapArr.filter((i) => !dataTimeList.includes(i));
sourceBase.forEach((outer,outerIndex)=>{ sourceBase.forEach((outer, outerIndex) => {
sourceTop.push(`${item.deviceId}-${outer}`) sourceTop.push(`${item.deviceId}-${outer}`);
noDataTime.forEach(i=>map[i].push('')) noDataTime.forEach((i) => map[i].push(""));
item.dataList.forEach((inner,innerIndex)=>{ item.dataList.forEach((inner, innerIndex) => {
map[inner.statisDate].push(inner[ele.attr[outerIndex]]) map[inner.statisDate].push(inner[ele.attr[outerIndex]]);
}) });
}) });
}) });
mapArr = mapArr.sort((a,b)=>this.compareDate(a,b)) mapArr = mapArr.sort((a, b) => this.compareDate(a, b));
mapArr.forEach(item=>{ mapArr.forEach((item) => {
source.push([item,...map[item]]) source.push([item, ...map[item]]);
}) });
source.unshift(sourceTop) source.unshift(sourceTop);
console.log('map=',map) this.chart.setOption(
console.log('mapArr=',mapArr) {
console.log('========',source) grid: {
this.chart.setOption({ containLabel: true,
grid: { },
containLabel: true legend: {
}, left: "center",
legend: { top: "10",
left: 'center', },
bottom: '15', tooltip: {
}, trigger: "axis",
tooltip: { axisPointer: {
trigger: 'axis', // 坐标轴指示器,坐标轴触发有效
axisPointer: { // 坐标轴指示器,坐标轴触发有效 type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' },
} },
}, textStyle: {
textStyle:{ color: "#333333",
color:"#333333", },
}, xAxis: {
xAxis: { type: "category",
type: 'category', },
}, yAxis: {
yAxis: { type: "value",
type: 'value', },
}, dataZoom: [
dataset: {source}, {
series:source[0].slice(1).map(item=>{ type: "inside",
return { start: 0,
type:ele.type || 'scatter' end: 100,
} },
}) {
start: 0,
},true) end: 100,
},
],
dataset: {source},
series: source[0].slice(1).map((item) => {
return {
type: "line",
smooth: true,
areaStyle: {
opacity: 0.7,
},
};
}),
},
true
);
}, },
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#pcsEchart')); this.chart = echarts.init(document.querySelector("#pcsEchart"));
},
init() {
this.$nextTick(() => {
this.initChart();
this.$refs.dateRangeSelect.init(true);
});
}, },
init(){
this.$nextTick(()=>{
this.initChart()
this.$refs.dateRangeSelect.init()
})
}
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
} };
</script> </script>

View File

@ -0,0 +1,228 @@
<template>
<div style="width:100%" v-loading="loading">
<!-- 搜索栏-->
<el-form :inline="true" class="select-container">
<el-form-item label="时间选择">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
:clearable="false"
:picker-options="pickerOptions"
:default-value="defaultDateRange"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
</el-form-item>
<el-form-item>
<el-button @click="onReset" native-type="button">重置</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="exportTable" native-type="button">导出</el-button>
</el-form-item>
</el-form>
<!--表格-->
<el-table
class="common-table"
:data="tableData"
show-summary
stripe
style="width: 100%;margin-top:25px;">
<!-- 汇总列-->
<el-table-column label="汇总" min-width="100px" align="center">
<el-table-column
prop="dataTime"
label="日期"
min-width="100px" align="center">
</el-table-column>
</el-table-column>
<!--充电量列-->
<el-table-column label="充电价格" align="center">
<el-table-column
align="center"
prop="activePeakPrice"
label="尖">
</el-table-column>
<el-table-column
align="center"
prop="activeHighPrice"
label="峰">
</el-table-column>
<el-table-column
align="center"
prop="activeFlatPrice"
label="平">
</el-table-column>
<el-table-column
align="center"
prop="activeValleyPrice"
label="谷">
</el-table-column>
<el-table-column
align="center"
prop="activeTotalPrice"
label="总">
</el-table-column>
</el-table-column>
<!--充电量列-->
<el-table-column label="放电价格" align="center">
<el-table-column
align="center"
prop="reActivePeakPrice"
label="尖">
</el-table-column>
<el-table-column
align="center"
prop="reActiveHighPrice"
label="峰">
</el-table-column>
<el-table-column
align="center"
prop="reActiveFlatPrice"
label="平">
</el-table-column>
<el-table-column
align="center"
prop="reActiveValleyPrice"
label="谷">
</el-table-column>
<el-table-column
align="center"
prop="reActiveTotalPrice"
label="总">
</el-table-column>
</el-table-column>
<!-- 实际收益-->
<el-table-column label="" align="center" fixed="right">
<el-table-column
prop="actualRevenue"
label="实际收益"
align="center">
</el-table-column>
</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"
>
</el-pagination>
</div>
</template>
<script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getAmmeterRevenueData} from '@/api/ems/dzjk'
import {formatDate} from "@/filters/ems";
export default {
name: 'DzjkTjbbSybb',
mixins: [getQuerySiteId],
data() {
return {
loading: false,
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now();
},
},
defaultDateRange: [],//默认展示的时间
dateRange: [],
tableData: [],
pageSize: 10,//分页栏当前每个数据总数
pageNum: 1,//分页栏当前页数
totalSize: 0,//table表格数据总数
}
},
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() {
this.pageNum = 1//每次搜索从1开始搜索
this.getData()
},
// 重置
onReset() {
this.dateRange = this.defaultDateRange
this.pageNum = 1//每次搜索从1开始搜索
this.getData()
},
// 分页
handleSizeChange(val) {
this.pageSize = val;
this.$nextTick(() => {
this.getData()
})
},
handleCurrentChange(val) {
this.pageNum = val
this.$nextTick(() => {
this.getData()
})
},
// 获取数据
getData() {
this.loading = true
const {siteId, pageNum, pageSize} = this
const [startTime = '', endTime = ''] = (this.dateRange || [])
getAmmeterRevenueData({siteId: siteId, startTime, endTime, pageSize, pageNum}).then(response => {
this.tableData = response?.rows || [];
this.totalSize = response?.total || 0
}).finally(() => {
this.loading = false
})
},
init() {
this.dateRange = []
this.tableData = []
this.totalSize = 0
this.pageSize = 10
this.pageNum = 1
let now = new Date(), lastDay = now.getTime(), firstDay = new Date(now.setDate(1)).getTime();
this.defaultDateRange = [formatDate(firstDay), formatDate(lastDay)];
this.dateRange = [formatDate(firstDay), formatDate(lastDay)];
this.getData()
},
},
}
</script>
<style scoped lang="scss">
::v-deep {
.common-table.el-table {
.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>

View File

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

View File

@ -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">
<!-- bmspcs 下级和上级在一列 -->
<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>
<!-- 没有上下级关系的bmspcs-->
<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>

View File

@ -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.communicationStatus === '0' ? 'status-running' : '' item.deviceStatus === '1' ? 'status-running' : ''
" "
> >
{{ communicationStatusOptions[item.communicationStatus] }} {{ 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.communicationStatus === '0' ? 'status-running' : '' item.deviceStatus === '1' ? 'status-running' : ''
" "
> >
{{ communicationStatusOptions[item.communicationStatus] }} {{ 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.communicationStatus === '0' ? 'status-running' : '' item.deviceStatus === '1' ? 'status-running' : ''
" "
> >
{{ communicationStatusOptions[item.communicationStatus] }} {{ 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.communicationStatus === '0' ? 'status-running' : '' item.deviceStatus === '1' ? 'status-running' : ''
" "
> >
{{ communicationStatusOptions[item.communicationStatus] }} {{ 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].communicationStatus === '0' item.children[0].deviceStatus === '1'
? 'status-running' ? 'status-running'
: '' : ''
" "
> >
{{ {{
communicationStatusOptions[ deviceStatusOptions[
item.children[0].communicationStatus 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],
@ -222,8 +223,8 @@ export default {
}, },
computed: { computed: {
...mapState({ ...mapState({
communicationStatusOptions: (state) => deviceStatusOptions: (state) =>
state.ems.communicationStatusOptions, 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;
});
}, },
}, },
}; };
@ -322,13 +323,14 @@ $lineColor: #86bcc7;
.ems-dashboard-editor-container { .ems-dashboard-editor-container {
background-color: #ffffff; background-color: #ffffff;
padding: 0; padding: 30px;
color: #666666; color: #666666;
.container { .container {
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: "";

View File

@ -1,35 +1,62 @@
<template> <template>
<div v-loading="loading" class="ems-dashboard-editor-container" style="background-color: #ffffff"> <div
v-loading="loading"
class="ems-dashboard-editor-container"
style="background-color: #ffffff"
>
<el-form ref="form" :model="form" label-position="top"> <el-form ref="form" :model="form" label-position="top">
<el-form-item <el-form-item
label="站点" label="站点"
prop="siteIds" prop="siteIds"
:rules="[{ required: true, message: '请选择站点' }]" :rules="[{ required: true, message: '请选择站点' }]"
> >
<el-radio-group v-model="form.siteIds" > <el-radio-group v-model="form.siteIds" @input="changeSiteIds">
<el-radio v-for="(item,index) in siteList" :key="index+'zdListSearch'" :label="item.siteId"> <el-radio
v-for="(item, index) in siteList"
:key="index + 'zdListSearch'"
:label="item.siteId"
>
{{ item.siteName }} {{ item.siteName }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="设备" prop="categoryName" :rules="[{ required: true, message: '请选择设备' }]"> <el-form-item
<el-radio-group v-model="form.categoryName" > label="设备"
<el-radio v-for="(key,index) in deviceCategoryList" :key="index+'sbListSearch'" :label="key"> prop="deviceCategory"
{{ key }} :rules="[{ required: true, message: '请选择设备' }]"
>
<el-radio-group v-model="form.deviceCategory" @input="changeSiteIds">
<el-radio
v-for="(item, index) in deviceCategoryList"
:key="index + 'sbListSearch'"
:label="item.code"
>
{{ item.name }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="isDtdc" label="单体电池(不超过5个)" prop="child" :rules="[{ required: true, message: '请选择单体电池' }]"> <el-form-item
v-if="isDtdc"
label="单体电池(不超过5个)"
prop="child"
:rules="[{ required: true, message: '请选择单体电池' }]"
>
<el-cascader <el-cascader
v-model="form.child" v-model="form.child"
style="width:400px" style="width: 400px"
:props="{ multiple: true }" :props="{ multiple: true }"
:show-all-levels="false" :show-all-levels="false"
:options="childOptions" :options="childOptions"
@change="handleChildChange"></el-cascader> @change="handleChildChange"
></el-cascader>
</el-form-item> </el-form-item>
<div style="display: flex"> <div style="display: flex">
<el-form-item label="点位" prop="pointName" :rules="[{ required: true, message: '请输入点位' }]" style="margin-right: 50px"> <el-form-item
label="点位"
prop="pointName"
:rules="[{ required: true, message: '请输入点位' }]"
style="margin-right: 50px"
>
<el-autocomplete <el-autocomplete
v-model="form.pointName" v-model="form.pointName"
placeholder="请输入点位" placeholder="请输入点位"
@ -38,19 +65,15 @@
@select="handleSelect" @select="handleSelect"
></el-autocomplete> ></el-autocomplete>
</el-form-item> </el-form-item>
<!-- <el-form-item label="横坐标" prop="dataUnit" :rules="[{ required: true, message: '请选择横坐标' }]">-->
<!-- <el-radio-group v-model="form.dataUnit">-->
<!-- <el-radio :label="1">分钟</el-radio>-->
<!-- <el-radio :label="2">小时</el-radio>-->
<!-- <el-radio :label="3"></el-radio>-->
<!-- </el-radio-group>-->
<!-- </el-form-item>-->
</div> </div>
<el-form-item> <el-form-item>
<el-button type="primary" @click="submitForm">生成图表</el-button> <el-button type="primary" @click="submitForm">生成图表</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<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 class="card-title">
<el-radio-group v-model="form.dataUnit"> <el-radio-group v-model="form.dataUnit">
@ -59,7 +82,12 @@
<el-radio :label="3"></el-radio> <el-radio :label="3"></el-radio>
</el-radio-group> </el-radio-group>
</span> </span>
<date-time-select ref="dateTimeSelect" :data-unit="form.dataUnit" @initDate="((e)=>form.dataRange=e||[])" @updateDate="updateDate"/> <date-time-select
ref="dateTimeSelect"
:data-unit="form.dataUnit"
@initDate="(e) => (form.dataRange = e || [])"
@updateDate="updateDate"
/>
</div> </div>
<div style="height: 350px" id="searchChart"></div> <div style="height: 350px" id="searchChart"></div>
</el-card> </el-card>
@ -67,274 +95,478 @@
</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 {getAllSites} from "@/api/ems/zddt"; import {getAllSites} from "@/api/ems/zddt";
import {getAllDeviceCategory,getPointValueList,pointFuzzyQuery,getAllBatteryIdsBySites} from '@/api/ems/search' import {getAllBatteryIdsBySites, getAllDeviceCategory, getPointValueList, pointFuzzyQuery,} from "@/api/ems/search";
import DateTimeSelect from "./DateTimeSelect.vue"; import DateTimeSelect from "./DateTimeSelect.vue";
export default { export default {
name: "Search", name: "Search",
mixins: [resize], mixins: [resize],
components:{DateTimeSelect}, components: {DateTimeSelect},
computed: { computed: {
isDtdc(){ isDtdc() {
return this.form.categoryName === '单体电池' return this.form.deviceCategory === "BATTERY";
}, },
}, },
watch:{ watch: {
'form.siteIds':{ "form.siteIds": {
handler(newVal){ handler(newVal) {
newVal && this.isDtdc && this.getChildList() newVal && this.isDtdc && this.getChildList();
}
},
isDtdc:{
handler(newVal){
newVal && this.form.siteIds && this.getChildList()
!newVal && (this.form.child = [])
}
},
'form.dataUnit':{
handler(newVal,oldVal){
console.log('wacth到了dataUnit的变化',newVal,oldVal)
this.$nextTick(()=>{
this.$refs.dateTimeSelect.init()
this.getDate()
})
// this.submitForm()
}, },
} },
isDtdc: {
handler(newVal) {
newVal && this.form.siteIds && this.getChildList();
!newVal && (this.form.child = []);
},
},
"form.dataUnit": {
handler(newVal, oldVal) {
console.log("wacth到了dataUnit的变化", newVal, oldVal);
this.$nextTick(() => {
this.$refs.dateTimeSelect.init();
this.getDate();
});
},
},
}, },
data() { data() {
return { return {
chart: null, chart: null,
deviceCategoryList:[],//设备列表 deviceCategoryList: [], //设备列表
siteList: [],//站点列表 siteList: [], //站点列表
childOptions:[],//二级设备列表 childOptions: [], //二级设备列表
form: { form: {
dataRange:[],//时间选择范围 dataRange: [], //时间选择范围
child:[], child: [],
siteIds: '',//当前选中的站点id 默认选中第一个站点 siteIds: "", //当前选中的站点id 默认选中第一个站点
categoryName: '',//设备 deviceCategory: "", //设备
pointName: '',//点位 pointName: "", //点位
dataUnit: 1,//横坐标 dataUnit: 1, //横坐标
}, },
loading: false, loading: false,
} };
}, },
methods: { methods: {
getChildList(){ changeSiteIds(val) {
this.childOptions=[] console.log("切换站点或设备清空点位", val);
this.form.child=[] val && (this.form.pointName = "");
const {siteIds} = this.form },
getAllBatteryIdsBySites([siteIds]).then(response=>{ getChildList() {
this.childOptions = [];
this.form.child = [];
const {siteIds} = this.form;
getAllBatteryIdsBySites([siteIds]).then((response) => {
const data = response?.data || {}; const data = response?.data || {};
const base = 50 const base = 50;
let options = [] let options = [];
Object.entries(data).forEach(([key,value],index)=>{ Object.entries(data).forEach(([key, value], index) => {
if(!value) value =[] if (!value) value = [];
options.push({ options.push({
value: key, value: key,
label: this.siteList.find(s=>s.siteId === key)?.siteName || '', label: this.siteList.find((s) => s.siteId === key)?.siteName || "",
children:[] children: [],
}) });
const length = value.length const length = value.length;
const num = Math.ceil(length /base ) const num = Math.ceil(length / base);
if(num === 0) return if (num === 0) return;
for(let i = 1; i <= num; i++){ for (let i = 1; i <= num; i++) {
const start = (i-1)*base+1,end = i*base const start = (i - 1) * base + 1,
end = i * base;
options[index].children.push({ options[index].children.push({
value:i, value: i,
label: `${start}-${end}`, label: `${start}-${end}`,
children:[] children: [],
}) });
for(let s = start;s<=Math.min(length,end);s++){ for (let s = start; s <= Math.min(length, end); s++) {
options[index].children[i-1].children.push({ options[index].children[i - 1].children.push({
value:value[s-1], value: value[s - 1],
label:value[s-1] label: value[s - 1],
}) });
} }
} }
}) });
console.log('二级设备options',options) console.log("二级设备options", options);
this.childOptions = options; this.childOptions = options;
}) });
}, },
handleChildChange(data){ handleChildChange(data) {
console.log('选择二级设备',data) console.log("选择二级设备", data);
this.form.child=data this.form.child = data;
}, },
showLoading(){ showLoading() {
this.chart && this.chart.showLoading() this.chart && this.chart.showLoading();
}, },
hideLoading(){ hideLoading() {
this.chart && this.chart.hideLoading() this.chart && this.chart.hideLoading();
}, },
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#searchChart')) this.chart = echarts.init(document.querySelector("#searchChart"));
}, },
updateDate(val){ updateDate(val) {
this.form.dataRange =val || [] this.form.dataRange = val || [];
this.getDate() this.getDate();
}, },
setOption(data) { setOption(data) {
if(!this.chart) return // 点位类型 dataType 1-瞬时值 2-累计值 仅当值为2展示差值
this.chart.clear() // 图表类型 chartType 1-曲线图2-箱线图
console.log('返回的数据',data) if (!this.chart) return;
let dataset=[] this.chart.clear();
if(data.length>0){ console.log("返回的数据", data);
data.forEach((item,index)=>{ if (!data || data.length <= 0) {
item.deviceList.forEach(inner=>{ this.$message.warning("暂无数据");
dataset.push({
name:`${this.isDtdc ? `${inner.parentDeviceId ? inner.parentDeviceId+'-' : ''}` : ''}${inner.deviceId}`,
type:'line',
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('暂无数据')
} }
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}`,
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',
// top: '10', // top: '10',
}, },
grid: { grid: {
containLabel: true containLabel: true,
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: "axis",
axisPointer: { // 坐标轴指示器,坐标轴触发有效 axisPointer: {
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' type: 'cross',
} },
// 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",
}, },
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: "inside",
start: 0, start: 0,
end: 100 end: 100,
}, },
{ {
start: 0, start: 0,
end: 100 end: 100,
} },
], ],
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}`,
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
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,
});
}, },
submitForm() { submitForm() {
this.getDate() this.getDate();
}, },
handleSelect(data){ handleSelect(data) {
this.form.pointName = data.value this.form.pointName = data.value;
}, },
querySearchAsync(query,cb){ querySearchAsync(query, cb) {
console.log('查询数据',query) console.log("查询数据", query);
if(!this.form.siteIds || !this.form.categoryName){ if (!this.form.siteIds || !this.form.deviceCategory) {
this.$message({ this.$message({
type: 'warning', type: "warning",
message: '请先选择站点和设备', message: "请先选择站点和设备",
}) });
return cb([]) return cb([]);
} }
pointFuzzyQuery({ pointFuzzyQuery({
siteIds:[this.form.siteIds], siteIds: [this.form.siteIds],
categoryName:this.form.categoryName, deviceCategory: this.form.deviceCategory,
pointName:query, pointName: query,
}).then(response => { }).then((response) => {
const data = response?.data || [] const data = response?.data || [];
cb(data.map(item => { cb(
return {name: item, value: item} data.map((item) => {
})) return {name: item, value: item};
}) })
);
});
}, },
// 获取所有设备 // 获取所有设备
getDeviceCategory(){ getDeviceCategory() {
return getAllDeviceCategory().then(response => { return getAllDeviceCategory().then((response) => {
this.deviceCategoryList=response?.data || [] this.deviceCategoryList = response?.data || [];
}) });
}, },
getZdList() { getZdList() {
return getAllSites().then(response => { return getAllSites()
this.siteList = response.data || [] .then((response) => {
}).finally(() => { this.siteList = response.data || [];
}) })
.finally(() => {
});
}, },
getDate(){ getDate() {
this.$refs.form.validate(valid => { this.$refs.form.validate((valid) => {
if(valid){ if (valid) {
if(!this.form.pointName){ if (!this.form.pointName) {
return this.$message.error('请输入正确的点位'); return this.$message.error("请输入正确的点位");
} }
if(this.isDtdc && (this.form.child.length === 0 || this.form.child.length > 5 )){ if (
return this.$message.error('请选择单体电池且不能超过5个'); this.isDtdc &&
(this.form.child.length === 0 || this.form.child.length > 5)
) {
return this.$message.error("请选择单体电池且不能超过5个");
} }
const{siteIds,dataUnit,categoryName,pointName,dataRange:[start='',end=''],child}=this.form const {
if(!start || !end) return this.$message.error('请选择时间'); siteIds,
let siteDeviceMap={} dataUnit,
child.forEach(([first,second,third])=>{ deviceCategory,
if(siteDeviceMap[first]){ pointName,
siteDeviceMap[first].push(third) dataRange: [start = "", end = ""],
}else{ child,
siteDeviceMap[first]=[] } = this.form;
siteDeviceMap[first].push(third) if (!start || !end) return this.$message.error("请选择时间");
} let siteDeviceMap = {};
}) child.forEach(([first, second, third]) => {
let startDate,endDate if (siteDeviceMap[first]) {
if(start && dataUnit===3){ siteDeviceMap[first].push(third);
} else {
siteDeviceMap[first] = [];
siteDeviceMap[first].push(third);
}
});
let startDate, endDate;
if (start && dataUnit === 3) {
// startDate= start + `${dataUnit === 2 ? ':00' : ' 00:00:00'}` // startDate= start + `${dataUnit === 2 ? ':00' : ' 00:00:00'}`
startDate = start + ' 00:00:00' startDate = start + " 00:00:00";
}else{ } else {
startDate=start startDate = start;
} }
if(end && dataUnit===3){ if (end && dataUnit === 3) {
// endDate= end + `${dataUnit === 2 ? ':00' : ' 00:00:00'}` // endDate= end + `${dataUnit === 2 ? ':00' : ' 00:00:00'}`
endDate = end + ' 00:00:00' endDate = end + " 00:00:00";
}else{ } else {
endDate=end endDate = end;
} }
this.loading = true this.loading = true;
getPointValueList({siteIds:[siteIds],dataUnit,categoryName,pointName,startDate,endDate,siteDeviceMap}).then(response => { getPointValueList({
this.setOption(response?.data || []) siteIds: [siteIds],
}).finally(()=>{ dataUnit,
this.loading = false deviceCategory,
pointName,
startDate,
endDate,
siteDeviceMap,
}) })
.then((response) => {
this.setOption(response?.data || []);
})
.finally(() => {
this.loading = false;
});
} }
}) });
}, },
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
return return;
} }
this.chart.dispose() this.chart.dispose();
this.chart = null this.chart = null;
}, },
mounted() { mounted() {
this.loading= true this.loading = true;
this.$nextTick(()=>{ this.$nextTick(() => {
this.initChart() this.initChart();
this.$refs.dateTimeSelect.init() this.$refs.dateTimeSelect.init();
Promise.all([this.getDeviceCategory(), this.getZdList()]).finally(()=>this.loading=false) Promise.all([this.getDeviceCategory(), this.getZdList()]).finally(
}) () => (this.loading = false)
} );
} });
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
</style>

View File

@ -0,0 +1,146 @@
<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="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-item label="站点" prop="siteId">
<el-select v-model="formData.siteId" :disabled="mode === 'edit'" placeholder="请选择站点" :loading="searchLoading" loading-text="正在加载数据">
<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 label="消息等级" prop="qos">
<el-select v-model="formData.qos" placeholder="请选择消息等级">
<el-option :value="1">1</el-option>
<el-option :value="2">2</el-option>
<el-option :value="3">3</el-option>
</el-select>
</el-form-item>
<el-form-item label="订阅topic" prop="mqttTopic">
<el-input v-model="formData.mqttTopic" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="topic描述" prop="topicName">
<el-input v-model="formData.topicName" type="textarea" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
</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 {editMqtt,addMqtt,getMqttDetail} from "@/api/ems/site";
import {getAllSites} from '@/api/ems/zddt'
export default {
data() {
return {
loading:0,
siteList:[],
searchLoading:false,
dialogTableVisible:false,
mode:'',
formData: {
id:'',//设备唯一标识
siteId:'',
qos:'',
topicName:'',
mqttTopic:''
},
rules: {
siteId:[
{ required: true, message: '请选择站点', trigger: 'blur'},
],
qos:[
{ required: true, message: '请选择消息等级', trigger: 'blur'},
],
mqttTopic:[
{ required: true, message: '请输入订阅topic', trigger: 'blur'},
],
topicName:[
{ required: true, message: '请输入topic描述', trigger: 'blur'},
],
},
}
},
methods: {
showDialog(id){
this.dialogTableVisible = true
this.getZdList()
if(id){
this.mode = 'edit'
this.formData.id = id
this.getDetail(id)
}else{
this.mode = 'add'
}
},
//获取站点列表
getZdList(){
this.searchLoading=true
getAllSites().then(response => {
this.siteList = response?.data || []
}).finally(() => {this.searchLoading=false})
},
getDetail(id){
getMqttDetail(id).then(response => {
const {topicName='',mqttTopic='',qos='',siteId=''} = JSON.parse(JSON.stringify(response?.data || {}));
this.formData.mqttTopic=mqttTopic;
this.formData.topicName=topicName;
this.formData.qos=qos;
this.formData.siteId=siteId;
})
},
saveDialog() {
this.$refs.addTempForm.validate(valid => {
if (!valid) return
this.loading+=1
const {
id='',
siteId='',
qos='',
mqttTopic='',//站点ID
topicName='',//设备id
}= this.formData;
if(this.mode === 'add'){
addMqtt( {mqttTopic,topicName,siteId,qos}).then(response => {
if(response.code === 200){
//新增成功
// 关闭弹窗 更新表格
this.$emit('update')
this.closeDialog()
}
}).finally(() => {
this.loading-=1
})
}else{
editMqtt({mqttTopic,topicName,id,siteId,qos}).then(response => {
if(response.code === 200){
//新增成功
// 关闭弹窗 更新表格
this.$emit('update')
this.closeDialog()
}
}).finally(() => {
this.loading-=1
})
}
})
},
closeDialog(){
this.$emit('clear')
// 清空所有数据
this.formData= {
id:'',//设备唯一标识
mqttTopic:'',
topicName:''
}
this.$refs.addTempForm.resetFields()
this.dialogTableVisible=false
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,201 @@
<template>
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
<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-input
v-model="form.mqttTopic"
clearable
placeholder="请输入订阅topic"
style="width: 150px"
></el-input>
</el-form-item>
<el-form-item label="topic描述">
<el-input
v-model="form.topicName"
clearable
placeholder="请输入topic描述"
style="width: 150px"
></el-input>
</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="addPowerConfig('')">新增</el-button>
<el-table
:data="tableData"
class="common-table"
max-height="600px"
stripe
style="width: 100%;margin-top: 25px">
<el-table-column
label="站点"
prop="siteId">
</el-table-column>
<el-table-column
label="消息等级"
prop="qos">
</el-table-column>
<el-table-column
label="订阅topic"
prop="mqttTopic">
</el-table-column>
<el-table-column
label="topic描述"
prop="topicName">
</el-table-column>
<el-table-column
fixed="right"
label="操作">
<template slot-scope="scope">
<el-button
size="mini"
style="margin-top:10px;"
type="warning"
@click="addPowerConfig(scope.row.id)">
编辑
</el-button>
<el-button
size="mini"
style="margin-top:10px;"
type="danger"
@click="deleteMqtt(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"
>
</el-pagination>
<add-mqtt ref="addMqtt" @update="getData"/>
</div>
</template>
<script>
import {deleteMqtt,getMqttList} from '@/api/ems/site'
import {getAllSites} from '@/api/ems/zddt'
import AddMqtt from './AddMqtt.vue'
export default {
name: "Mqtt",
components: {AddMqtt},
computed: { },
data() {
return {
form:{
siteId:"",
topicName:'',
mqttTopic:''
},
siteList:[],
searchLoading:false,
loading:false,
tableData:[],
pageSize:10,//分页栏当前每个数据总数
pageNum:1,//分页栏当前页数
totalSize:0,//table表格数据总数
}
},
methods:{
// 分页
handleSizeChange(val) {
this.pageSize = val;
this.$nextTick(()=>{
this.getData()
})
},
handleCurrentChange(val) {
this.pageNum = val
this.$nextTick(()=>{
this.getData()
})
},
// 搜索
onSearch(){
this.pageNum =1//每次搜索从1开始搜索
this.getData()
},
onReset(){
this.form={
siteId:'',
topicName:'',
mqttTopic:''
}
this.pageNum =1//每次搜索从1开始搜索
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(){
this.loading=true;
const {mqttTopic,topicName,siteId} = this.form;
const {pageNum,pageSize} = this;
getMqttList({pageNum,pageSize,mqttTopic,topicName,siteId}).then(response => {
this.tableData=response?.rows || [];
this.totalSize = response?.total || 0
}).finally(() => {this.loading=false})
},
addPowerConfig(id=''){
this.$refs.addMqtt.showDialog(id);
},
deleteMqtt(row){
this.$confirm(`确认要删除该配置吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
showClose:false,
closeOnClickModal:false,
type: 'warning',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
deleteMqtt(row.id).then(response => {
response.code === 200 && done();
}).finally(() => {
instance.confirmButtonLoading = false;
})
} else {
done();
}
}
}).then(() => {
//只有在废弃成功的情况下会走到这里
this.$message({
type: 'success',
message: '删除成功!'
});
this.getData()
//调用接口 更新表格数据
}).catch(() => {
//取消关机
});
},
},
mounted() {
this.loading=true
this.getZdList()
this.getData()
}
}
</script>

View File

@ -0,0 +1,306 @@
//选择年月 配置尖峰平谷对应的电价 配置24小时选择对应的尖峰平谷
<template>
<div>
<el-dialog v-loading="loading" width="780px" :visible.sync="dialogTableVisible" class="ems-dialog" title="电价配置" :close-on-click-modal="false" :show-close="false">
<div class="items-container">
<div class="item-title">站点:</div>
<div class="item-content">
<el-select v-model="siteId" :disabled="mode === 'edit'" placeholder="请选择站点" :loading="searchLoading" loading-text="正在加载数据">
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
</el-select>
</div>
</div>
<div class="items-container">
<div class="item-title">时间:</div>
<div class="item-content">
<el-date-picker
v-model="powerDate"
format="yyyy-MM"
value-format="yyyy-MM"
type="month"
placeholder="请选择月份"
:disabled="mode === 'edit'"
>
</el-date-picker>
</div>
</div>
<div style="display: flex">
<div class="items-container price-types" v-for="item in priceTypeOptions" :key="item.id">
<div class="item-title">{{item.name}}:</div>
<div class="item-content">
<el-input
placeholder="请输入价格"
v-model="item.price">
</el-input>
</div>
</div>
</div>
<div class="items-container">
<div class="item-title">
<el-button
@click.native.prevent="addRow"
block
type="primary"
size="mini">
新增时间段配置
</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 class="time-lists" v-for="(item,index) in hoursOptions" :key="'hoursOptions'+index">
<div>
<el-time-select
placeholder="开始时间"
v-model="item.startTime"
:picker-options="{
start: '00:00',
step: '01:00',
end: '23:00',
}">
</el-time-select>
</div>
<div>
<el-time-select
placeholder="结束时间"
v-model="item.endTime"
:picker-options="{
start: '00:00',
step: '01:00',
end: '24:00',
minTime: item.startTime
}">
</el-time-select>
</div>
<div>
<el-select v-model="item.costType" placeholder="请选择">
<el-option v-for="(value,key) in priceTypeOptions" :key="key+'priceTypeOptions'" :label="value.name" :value="value.id"></el-option>
</el-select>
</div>
<div>
<el-button
@click.native.prevent="deleteRow(index)"
type="warning"
size="mini">
删除
</el-button>
</div>
</div>
</div>
</div>
</div>
<div slot="footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="saveDialog">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {addPriceConfig,editPriceConfig,detailPriceConfig} from '@/api/ems/powerTariff'
import {getAllSites} from '@/api/ems/zddt'
export default {
data() {
return {
mode:'',
id:'',
searchLoading:false,
siteId:'',
siteList:[],
powerDate:'',//时间
//尖-peak,峰-high,平-flat,谷=valley
priceTypeOptions:[{
id:'peak',
name:'尖',
price:''
},{
id:'high',
name:'峰',
price:''
},{
id:'flat',
name:'平',
price:''
},{
id:'valley',
name:'谷',
price:''
}],
hoursOptions:[],
loading:false,
dialogTableVisible:false,
}
},
methods: {
addRow(){
this.hoursOptions.push({
startTime:'',
endTime:'',
costType:''
})
},
deleteRow(index){
this.hoursOptions.splice(index,1)
},
//获取站点列表
getZdList(){
this.searchLoading=true
getAllSites().then(response => {
this.siteList = response?.data || []
}).finally(() => {this.searchLoading=false})
},
showDialog(id){
this.getZdList()
this.id = id
if(id) {
this.mode='edit'
//获取详情 初始化hoursOptions
this.loading = true
detailPriceConfig(id).then(response => {
const data = response?.data || {}
this.siteId = data?.siteId || ''
this.hoursOptions = data?.range || []
this.powerDate=data?.year && data?.month ? data.year +'-'+data.month : ''
this.priceTypeOptions.forEach(item=>{
item.price = data[item.id]
})
}).finally(()=>this.loading = false)
}else {
this.mode='add'
}
this.dialogTableVisible=true
},
saveDialog() {
if(this.siteId === '') return this.$message.error('请选择站点')
if(this.powerDate === '') return this.$message.error('请选择时间')
let priceArr=[]
this.priceTypeOptions.forEach(item=>{
!['0',0].includes(item.price) && !item.price && priceArr.push(item.name)
})
if(priceArr.length>0) return this.$message.error(`请配置${priceArr.join(',')}的电价`)
if(this.hoursOptions.length<=0) return this.$message.error(`请配置24小时的电价`)
let hours=false,hourDis=false
this.hoursOptions.forEach(item=>{
if(!item.startTime || !item.endTime || !item.costType ) hours=true
if(parseInt(item.startTime)>=parseInt(item.endTime)) hourDis=true
})
if(hours) return this.$message.error(`时间段电价配置错误`)
if(hourDis) return this.$message.error(`结束时间要大于开始时间`)
let hoursMap={}
this.hoursOptions.forEach(item=>{
let s = parseInt(item.startTime),e=parseInt(item.endTime)
for(s;s<e;s++){
if(!hoursMap[s]) hoursMap[s]=1
else hoursMap[s]+=1
}
})
console.log('hoursMap======',hoursMap)
if(Object.values(hoursMap).length<24 || Object.values(hoursMap).find(i=>i>1)) return this.$message.error(`请配置24小时的电价且时间不能重复`)
const {powerDate,priceTypeOptions,hoursOptions,mode,id,siteId} =this
this.loading = true
let params={
year:powerDate.split('-')[0],
month:parseInt(powerDate.split('-')[1]),
range:hoursOptions,
siteId,
}
priceTypeOptions.forEach(item=>{params[item.id]=item.price})
if(mode === 'edit') params.id = id
console.log('参数=======',params)
//调接口传数据 区分新增还是修改 成功之后关闭弹窗 更新表格
if(mode === 'add'){
addPriceConfig(params).then(response => {
if(response.code === 200){
this.$emit('update')
this.closeDialog()
}else{
this.$message.error('新增失败')
}
}).finally(() => {
this.loading = false
})
}else{
editPriceConfig(params).then(response => {
if(response.code === 200){
this.$emit('update')
this.closeDialog()
}else{
this.$message.error('修改失败')
}
}).finally(() => {
this.loading = false
})
}
},
closeDialog(){
// 清空所有数据
this.$emit('clear')
this.mode=''
this.id=''
this.siteId=''
this.siteList=[]
this.searchLoading=false
this.powerDate=''
this.hoursOptions=[]
this.priceTypeOptions.forEach(item=>{
item.price=''
})
this.dialogTableVisible=false
}
},
}
</script>
<style scoped lang="scss">
.items-container{
margin-bottom: 20px;
.item-title{
line-height: 16px;
padding: 10px 0;
color: #000;
}
}
.price-types{
width: 150px;
&:not(:last-child){
margin-right: 20px;
}
}
.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>

View File

@ -0,0 +1,208 @@
<template>
<div class="ems-dashboard-editor-container" style="background-color: #ffffff" v-loading="loading">
<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-date-picker
v-model="defaultYear"
type="year"
:clearable="false"
placeholder="请选择年份"
align="center"
format="yyyy年"
value-format="yyyy"
@change="changeDefaultYear"
>
</el-date-picker>
</el-form-item>
<br>
<el-form-item>
<el-button type="primary" @click="addPowerConfig('')">新增电价配置</el-button>
</el-form-item>
</el-form>
<div class="month-lists-container" v-infinite-scroll="getData" infinite-scroll-immediate="false">
<el-empty v-show="tableData.length<=0" :image-size="200"></el-empty>
<el-card
shadow="always"
class="common-card-container time-range-card"
v-for="item in tableData"
:key="item.id"
>
<div slot="header" class="time-range-header">
<span class="card-title">{{siteList.find(i=>i.siteId===item.siteId).siteName || item.siteId || ''}}-{{item.month}}月电价时段划分</span>
<div>
<el-button type="primary" size="mini" @click="addPowerConfig(item.id)">编辑</el-button>
<el-button type="warning" size="mini" @click="deletePowerConfig(item)">删除</el-button>
</div>
</div>
<div class="price-table-container">
<div class="price-table">
<div class="time-list">
<div class="time"> </div>
<div class="type">时段</div>
<div class="price">电价/kWh</div>
</div>
<div class="time-list" v-for="(rangeItem,rangeIndex) in item.range" :key="rangeIndex+'price'">
<div class="time">{{`${rangeItem.startTime}-${rangeItem.endTime}`}}</div>
<div class="type">{{priceTypeOptions[rangeItem.costType]}}</div>
<div class="price">{{item[rangeItem.costType]}}</div>
</div>
</div>
</div>
</el-card>
</div>
<add-power-tariff ref="addPowerTariff" @update="getData(true)"/>
</div>
</template>
<script>
import {energyPriceConfig,listPriceConfig} from '@/api/ems/powerTariff'
import {getAllSites} from '@/api/ems/zddt'
import AddPowerTariff from './AddPowerTariff.vue'
import DateTimeSelect from "@/views/ems/search/DateTimeSelect.vue";
export default {
name: "PowerTariff",
components: {DateTimeSelect, AddPowerTariff},
computed: { },
data() {
return {
loading:false,
pageNum:1,
pageSize:40,
searchLoading:false,
siteId:'',
siteList:[],
tableData:[],
tableTotal:0,
defaultYear:'',
pickerOptions:{
disabledDate(time) {
return time.getFullYear() >= new Date().getFullYear()+1;
},
},
priceTypeOptions:{
'peak':'尖',
'high':'峰',
'flat':'平',
'valley':'谷'
},
}
},
methods:{
resetTableData(){
this.tableData=[]
this.tableTotal=0
this.pageNum=1
},
// 搜索
onSearch(){
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(){
this.getData(true)
},
getData(reset=false){
reset && this.resetTableData()
if(!reset && this.tableData.length>=this.tableTotal) return
this.loading=true;
const date = new Date(this.defaultYear).getFullYear()
const startTime = date+'-01',endTime = date+'-12'
listPriceConfig({startTime,endTime,pageNum:this.pageNum,pageSize:this.pageSize,siteId:this.siteId}).then(response => {
const data = JSON.parse(JSON.stringify(response?.rows || []))
data.length > 0 && (this.pageNum += 1)
this.tableData.push(...data)
this.tableTotal=response?.total || 0
}).finally(() => {this.loading=false})
},
addPowerConfig(id=''){
this.$refs.addPowerTariff.showDialog(id);
},
deletePowerConfig(row){
this.$confirm(`确认要删除${row.month}月的电价配置吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
showClose:false,
closeOnClickModal:false,
type: 'warning',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
energyPriceConfig(row.id).then(response => {
response.code === 200 && done();
}).finally(() => {
instance.confirmButtonLoading = false;
})
} else {
done();
}
}
}).then(() => {
//只有在废弃成功的情况下会走到这里
this.$message({
type: 'success',
message: '删除成功!'
});
this.getData(true)
//调用接口 更新表格数据
}).catch(() => {
//取消关机
});
},
},
mounted() {
this.defaultYear = new Date()
this.loading=true
this.getZdList().then(()=>{
this.getData(true)
})
}
}
</script>
<style lang="scss" scoped>
.month-lists-container{
max-height:100vh ;
overflow-y: auto;
}
.common-card-container{
width: 100%;
margin-top: 20px;
}
.price-table-container{
overflow-x: auto;
.price-table{
border:1px solid #eee;
width: fit-content;
overflow-x: auto;
display: flex;
.time-list{
&:not(:first-child){
border-left:1px solid #eee;
}
text-align: center;
width: 140px;
&>div{
height: 30px;
font-size: 12px;
line-height: 30px;
color:#000;
&.time,&.type{
border-bottom: 1px solid #eee;
}
}
}
}
}
</style>

View File

@ -0,0 +1,815 @@
<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"
:props="props"
:show-all-levels="false"
:ref="'protectionSettings'+index"
@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"
:props="props"
:ref="'protectionPlan'+index"
@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 {addProtectPlan, getDeviceListBySiteAndCategory, getProtectPlan, updateProtectPlan} 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 {
props: {
// emitPath: false,//在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false则只返回该节点的值
lazy: true,
lazyLoad: (node, resolve) => {
console.log('---------node', node)
this.getDeviceList(node, resolve)
}
},
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 => {
const index = this.childOptions.findIndex(i => i.value === item.deviceCategory)
if (index > -1) {
!this.childOptions[index].children.find(i => i.value === item.deviceId) && this.childOptions[index].children.push({
value: item.deviceId,
label: item.deviceName,
leaf: true
})
}
return Object.assign({}, item, {
deviceId: [item.deviceCategory || '', item.deviceId || ''],
deviceName: [item.categoryName || '', item.deviceName || ''],
})
})
const settings = (JSON.parse(data?.protectionSettings || [])).map(item => {
const index = this.childOptions.findIndex(i => i.value === item.deviceCategory)
if (index > -1) {
!this.childOptions[index].children.find(i => i.value === item.deviceId) && this.childOptions[index].children.push({
value: item.deviceId,
label: item.deviceName,
leaf: true
})
}
return Object.assign({}, item, {
deviceId: [item.deviceCategory || '', item.deviceId || ''],
deviceName: [item.categoryName || '', item.deviceName || ''],
})
})
this.$nextTick(() => {
this.protectionPlan.splice(0, 0, ...plan)
this.protectionSettings.splice(0, 0, ...settings)
})
console.log('获取设备保护详情并初始化', this.formData, this.protectionPlan, this.protectionSettings, this.childOptions)
})
} else {
this.mode = 'add'
}
},
// 新增设备保护前提、设备保护方案
addRow(type) {
const item = type === 'protectionSettings' ? {
deviceId: [],//设备ID
deviceName: [],
deviceCategory: "",//设备类型 英文
categoryName: '',//设备类型名称 中文
point: "",//点位 英文
pointName: "",//点位 中文
faultValue: "",//故障值
releaseValue: "",//释放值
faultOperator: "",//故障值比较关系
releaseOperator: "",//释放值比较关系
relationNext: "",//与下一个点位的关系
} : {
deviceId: [],
deviceName: [],
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 || []);
this.$set(this, 'childOptions', [])
let arr = []
data.forEach((item) => {
arr.push({
value: item.code,
label: item.name,
leaf: false,
children: []
})
})
this.childOptions.splice(0, 0, ...arr)
console.log('获取设备类型', data, this.childOptions)
})
.finally(() => {
this.loading -= 1;
});
},
//获取设备列表-区分站点
getDeviceList(node, resolve) {
if (!this.formData.siteId) {
this.$message({
type: "warning",
message: "请先选择站点",
});
return resolve([])
}
getDeviceListBySiteAndCategory({
siteId: this.formData.siteId,
deviceCategory: node.value
}).then((response) => {
const data = (response?.data || []).map(item => {
return {
label: item.deviceName,
value: item.id,
leaf: true
}
})
resolve(data)
}).catch(() => {
resolve([])
})
},
//更新站点下面的设备列表
updateSiteDeviceList() {
this.childOptions.forEach(item => {
const length = item.children.length
item.children.splice(0, length)
})
},
//选中设备类型、设备
handleChange(data, type, index) {
console.log('设置选中设备类型、设备', data, type, index)
const {label = '', parent = {}} = this.$refs[type + index][0].getCheckedNodes()[0]
console.log('选中设备的名称', parent.label, label)
const item = Object.assign({}, this[type][index], {
deviceId: data,
deviceName: [parent.label, label],
deviceCategory: data[0],
categoryName: parent.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.deviceName = []
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.deviceName = []
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],
deviceName: item.deviceName[1],
})
})
const plan = protectionPlan.map(item => {
return Object.assign({}, item, {
deviceId: item.deviceId[1],
deviceName: item.deviceName[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>

View File

@ -0,0 +1,259 @@
<template>
<div
class="ems-dashboard-editor-container"
style="background-color: #ffffff"
v-loading="loading"
>
<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="故障名称">
<el-input
v-model="form.faultName"
clearable
placeholder="请输入故障名称"
style="width: 150px"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
<el-button @click="onReset" native-type="button">重置</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addDevice" native-type="button"
>新增设备</el-button
>
<el-table
class="common-table"
:data="tableData"
stripe
max-height="600px"
style="width: 100%; margin-top: 25px"
>
<el-table-column prop="siteId" label="站点" width="100"> </el-table-column>
<el-table-column prop="faultName" label="设备保护名称" width="100"> </el-table-column>
<el-table-column prop="faultLevel" label="故障等级" width="100">
<template slot-scope="scope">等级{{scope.row.faultLevel}}</template>
</el-table-column>
<el-table-column prop="isAlert" label="是否告警" width="100">
<template slot-scope="scope">{{scope.row.isAlert === 1 ? '是' : '否'}}</template>
</el-table-column>
<el-table-column prop="description" label="处理方案描述" width="200" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="protectionSettings" label="保护前提" show-overflow-tooltip width="400">
<template slot-scope="scope">
<div v-html="handleProtectionSettings(scope.row.protectionSettings)"></div>
</template>
</el-table-column>
<el-table-column prop="faultDelaySeconds" label="保护前提延时(s)" width="120">
</el-table-column>
<el-table-column prop="protectionPlan" label="保护方案" show-overflow-tooltip width="200">
<template slot-scope="scope">
<div v-html="handleProtectionPlan(scope.row.protectionPlan)"></div>
</template>
</el-table-column>
<el-table-column prop="releaseDelaySeconds" label="保护方案延时(s)" width="120">
</el-table-column>
<el-table-column fixed="right" label="操作" width="150">
<template slot-scope="scope">
<el-button @click="editDevice(scope.row)" type="warning" size="mini">
编辑
</el-button>
<el-button type="danger" @click="deleteDevice(scope.row)" size="mini">
删除
</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"
>
</el-pagination>
<add-device
ref="addDevice"
@update="getData"
/>
</div>
</template>
<script>
import {
protectPlanList,
deleteProtectPlan,
} from "@/api/ems/site";
import { getAllSites } from "@/api/ems/zddt";
import AddDevice from "./AddDevice.vue";
export default {
name: "SBBH",
components: { AddDevice },
data() {
return {
form:{
siteId:'',
faultName:''
},
loading: false,
searchLoading: false,
siteList: [],
tableData: [],
pageSize: 10, //分页栏当前每个数据总数
pageNum: 1, //分页栏当前页数
totalSize: 0, //table表格数据总数
dialogTableVisible: false,
};
},
methods: {
handleProtectionSettings(data){
if(!data || !JSON.parse(data)) return
const arr = JSON.parse(data),
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){
if(!data || !JSON.parse(data)) return
const arr = JSON.parse(data),
str= arr.map((item,index)=>{
const {categoryName='',deviceId='',point='',value=''} = item
return `<div>${index+1}、 <span>${categoryName ? categoryName + '-' : ''}${deviceId ? deviceId + '-' : ''}${ point || ''}</span> <span>故障:=${ value || ''}</span> </div>`
})
return str.join('')
},
// 新增设备 展示弹窗
addDevice() {
this.$refs.addDevice.open()
},
// 编辑设备
editDevice(row) {
this.$refs.addDevice.open(row.id,row.siteId)
},
//删除设备
deleteDevice(row) {
console.log('删除')
this.$confirm(`确认要设备保护${row.faultName}吗?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
showClose: false,
closeOnClickModal: false,
type: "warning",
beforeClose: (action, instance, done) => {
if (action === "confirm") {
instance.confirmButtonLoading = true;
deleteProtectPlan(row.id)
.then((response) => {
response.code === 200 && done();
})
.finally(() => {
instance.confirmButtonLoading = false;
});
} else {
done();
}
},
})
.then(() => {
//只有在废弃成功的情况下会走到这里
this.$message({
type: "success",
message: "删除成功!",
});
this.getData();
//调用接口 更新表格数据
})
.catch(() => {
//取消关机
});
},
// 分页
handleSizeChange(val) {
this.pageSize = val;
this.$nextTick(() => {
this.getData();
});
},
handleCurrentChange(val) {
this.pageNum = val;
this.$nextTick(() => {
this.getData();
});
},
// 搜索
onSearch() {
this.pageNum = 1; //每次搜索从1开始搜索
this.getData();
},
// 重置
onReset() {
this.form={
siteId: "",
faultName: "",
}
this.pageNum = 1; //每次搜索从1开始搜索
this.getData();
},
// 获取数据
getData() {
this.loading = true;
const { pageNum, pageSize } = this,{siteId,faultName=''}=this.form;
protectPlanList({ siteId, faultName,pageNum, pageSize })
.then((response) => {
this.tableData = response?.rows || [];
this.totalSize = response?.total || 0;
})
.finally(() => {
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>
<style scoped lang="scss"></style>

View File

@ -1,77 +1,136 @@
<template> <template>
<el-dialog :visible.sync="dialogTableVisible" :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="100px"> :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%'}"> <div v-loading="loading>0">
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'siteOptions'"></el-option> <el-form v-loading="loading>0" ref="addTempForm" inline :model="formData" :rules="rules" size="medium"
</el-select> label-width="120px" class="device-form">
</el-form-item> <el-form-item label="站点" prop="siteId">
<el-form-item label="设备id" prop="deviceId" > <el-select v-model="formData.siteId" placeholder="请选择" :style="{width: '100%'}" @change="changeType">
<el-input v-model="formData.deviceId" maxlength="60" clearable :style="{width: '100%'}"> <el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList"
</el-input> :key="index+'siteOptions'"></el-option>
</el-form-item> </el-select>
<el-form-item label="设备名称" prop="deviceName"> </el-form-item>
<el-input v-model="formData.deviceName" placeholder="请输入" clearable :style="{width: '100%'}"> <el-form-item label="设备id" prop="deviceId">
</el-input> <el-input v-model="formData.deviceId" placeholder="请输入" maxlength="60" clearable :style="{width: '100%'}">
</el-form-item> </el-input>
<el-form-item label="设备描述" prop="description"> </el-form-item>
<el-input v-model="formData.description" type="textarea" placeholder="请输入" clearable :style="{width: '100%'}"> <el-form-item label="设备名称" prop="deviceName">
</el-input> <el-input v-model="formData.deviceName" placeholder="请输入" clearable :style="{width: '100%'}">
</el-form-item> </el-input>
<el-form-item label="工作状态" prop="communicationStatus"> </el-form-item>
<el-select v-model="formData.communicationStatus" placeholder="请选择" :style="{width: '100%'}"> <el-form-item label="设备描述" prop="description">
<el-option :label="value" :value="key" v-for="(value,key) in communicationStatusOptions" :key="key+'communicationStatusOptions'"></el-option> <el-input v-model="formData.description" type="textarea" placeholder="请输入" clearable
</el-select> :style="{width: '100%'}">
</el-form-item> </el-input>
<el-form-item label="设备类型" prop="deviceType"> </el-form-item>
<el-select v-model="formData.deviceType" placeholder="请选择" :style="{width: '100%'}"> <el-form-item label="工作状态" prop="communicationStatus">
<el-option :label="value" :value="key" v-for="(value,key) in deviceTypeOptions" :key="key+'deviceTypeOptions'"></el-option> <el-select v-model="formData.communicationStatus" placeholder="请选择" :style="{width: '100%'}">
</el-select> <el-option :label="value" :value="key" v-for="(value,key) in communicationStatusOptions"
</el-form-item> :key="key+'communicationStatusOptions'"></el-option>
<el-form-item label="设备类别" prop="deviceCategory"> </el-select>
<el-select v-model="formData.deviceCategory" placeholder="请选择" :style="{width: '100%'}"> </el-form-item>
<el-option :label="item" :value="item" v-for="(item,index) in deviceCategoryList" :key="index+'deviceCategoryList'"></el-option> <el-form-item label="设备类型" prop="deviceType">
</el-select> <el-select v-model="formData.deviceType" placeholder="请选择" :style="{width: '100%'}">
</el-form-item> <el-option :label="value" :value="key" v-for="(value,key) in deviceTypeOptions"
<el-form-item label="上级设备" prop="parentId" v-if="formData.deviceCategory === dccDeviceCategory"> :key="key+'deviceTypeOptions'"></el-option>
<el-select v-model="formData.parentId" :placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先选择站点' : '请选择'" :style="{width: '100%'}"> </el-select>
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList" :key="index+'parentDeviceList'" ></el-option> </el-form-item>
</el-select> <el-form-item label="设备类别" prop="deviceCategory">
</el-form-item> <el-select v-model="formData.deviceCategory" placeholder="请选择" :style="{width: '100%'}"
<el-form-item label="TCP设备的ip地址" prop="ipAddress" v-if="formData.deviceType === 'TCP'"> @change="changeType">
<el-input v-model="formData.ipAddress" placeholder="请输入" clearable :style="{width: '100%'}"> <el-option :label="item.name" :value="item.code" v-for="(item,index) in deviceCategoryList"
</el-input> :key="index+'deviceCategoryList'"></el-option>
</el-form-item> </el-select>
<el-form-item label="TCP设备的端口号" prop="ipPort" v-if="formData.deviceType === 'TCP'"> </el-form-item>
<el-input v-model="formData.ipPort" placeholder="请输入" clearable :style="{width: '100%'}"> <el-form-item label="上级设备" prop="parentId" v-if="dccDeviceCategoryList.includes(formData.deviceCategory)">
</el-input> <el-select v-model="formData.parentId"
</el-form-item> :placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先选择站点' : '请选择'"
:style="{width: '100%'}">
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList"
:key="index+'parentDeviceList'"></el-option>
</el-select>
</el-form-item>
<el-form-item label="TCP设备的ip地址" prop="ipAddress" v-if="formData.deviceType === 'TCP'">
<el-input v-model="formData.ipAddress" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="TCP设备的端口号" prop="ipPort" v-if="formData.deviceType === 'TCP'">
<el-input v-model="formData.ipPort" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="从站地址" prop="slaveId" v-if="formData.deviceType === 'TCP'">
<el-input v-model="formData.slaveId" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="串口路径" prop="serialPort">
<el-input v-model="formData.serialPort" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="波特率" prop="baudRate">
<el-input v-model="formData.baudRate" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="数据位" prop="dataBits">
<el-input v-model="formData.dataBits" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</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-item label="图片" prop="pictureUrl">
<image-upload :limit="1" :drag="false" @input="uploadImage" :value="formData.pictureUrl"/>
</el-form-item>
<el-form-item label="串口路径" prop="serialPort"> </el-form>
<el-input v-model="formData.serialPort" placeholder="请输入" clearable :style="{width: '100%'}"> <!-- pcs配置-->
</el-input> <el-form v-if="isPcs" ref="pcsSettingForm" inline :model="pcsSetting" size="medium"
</el-form-item> label-width="120px" class="device-form" :rules="pcsSettingRules">
<el-form-item label="波特率" prop="baudRate"> <div style="font-size: 14px;padding: 10px 0 20px;font-weight: 600;">PCS配置</div>
<el-input v-model="formData.baudRate" placeholder="请输入" clearable :style="{width: '100%'}"> <el-form-item label="开关机地址" prop="pointAddress">
</el-input> <el-input v-model="pcsSetting.pointAddress" 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="powerAddress">
</el-input> <el-input v-model="pcsSetting.powerAddress" 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="startCommand">
</el-input> <el-input v-model="pcsSetting.startCommand" 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="startPower">
</el-input> <el-input v-model="pcsSetting.startPower" 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="stopCommand">
</el-form-item> <el-input v-model="pcsSetting.stopCommand" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form> </el-form-item>
<el-form-item label="关机目标功率" prop="stopPower">
<el-input v-model="pcsSetting.stopPower" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="电池簇数" prop="clusterNum">
<el-input v-model="pcsSetting.clusterNum" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<br>
<template v-for="index in parseInt(pcsSetting.clusterNum) || 0">
<el-form-item :label="'电池簇'+(index)+'地址'"
prop="clusterPointAddress">
<el-input v-model="pcsSetting.clusterPointAddress[index-1]" placeholder="请输入" clearable
:style="{width: '100%'}">
</el-input>
</el-form-item>
</template>
</el-form>
</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,269 +139,406 @@
</template> </template>
<script> <script>
import {mapState} from "vuex"; import {mapState} from "vuex";
import {getStackNameList} from '@/api/ems/dzjk'
import {getAllSites} from '@/api/ems/zddt' import {getAllSites} from '@/api/ems/zddt'
import {validText} from '@/utils/validate' import {validText} from '@/utils/validate'
import {getDeviceDetailInfo,getDeviceCategory,updateDevice,addDevice} from "@/api/ems/site"; import {addDevice, getDeviceDetailInfo, getParentDeviceId, updateDevice} from "@/api/ems/site";
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();
}
}
return { return {
loading:0, loading: 0,
dccDeviceCategory:'CLUSTER', dccDeviceCategoryList: ['CLUSTER', 'BATTERY'],//需要展示上级设备的设备类型
dialogTableVisible:false, dialogTableVisible: false,
parentDeviceList:[],//上级设备列表 从接口获取数据 parentDeviceList: [],//上级设备列表 从接口获取数据
siteList:[],//站点列表 从接口获取数据 siteList: [],//站点列表 从接口获取数据
deviceCategoryList:[],//设备类别列表 从接口获取数据 deviceCategoryList: [],//设备类别列表 从接口获取数据
formData: { formData: {
id:'',//设备唯一标识 id: '',//设备唯一标识
siteId:'',//站点ID siteId: '',//站点ID
deviceId:'',//设备id deviceId: '',//设备id
deviceName:'',//设备名称 deviceName: '',//设备名称
description:'',//设备描述 description: '',//设备描述
communicationStatus:'',//工作状态 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: '',//校验位
pictureUrl:'',//设备图片 pictureUrl: '',//设备图片
slaveId: '',//从站地址
},
pcsSetting: {
deviceSettingId: '',
powerAddress: '',//功率地址
pointAddress: "",//开关机地址
startCommand: "",//开机指令
stopCommand: "",//关机指令
startPower: '',//开机目标功率
stopPower: '',//关机目标功率
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:[ communicationStatus: [
{ required: true, message: '请选择工作状态', trigger: ['blur','change']} {required: true, message: '请选择工作状态', trigger: ['blur', 'change']}
], ],
deviceType:[ deviceType: [
{ required: true, message: '请选择设备类型', trigger:['blur','change']} {required: true, message: '请选择设备类型', trigger: ['blur', 'change']}
], ],
deviceCategory:[ deviceCategory: [
{ required: true, message: '请选择设备类别', trigger: ['blur','change']} {required: true, message: '请选择设备类别', trigger: ['blur', 'change']}
], ],
ipAddress:[ ipAddress: [
{ validator: validateText, trigger: 'blur' } {validator: validateText, trigger: 'blur'}
], ],
ipPort:[ ipPort: [
{ validator: validateText, trigger: 'blur' } {validator: validateText, trigger: 'blur'}
], ],
serialPort:[ slaveId: [
{ validator: validateText, trigger: 'blur' } {validator: validateNumber, trigger: 'blur'}
], ],
baudRate:[ serialPort: [
{ validator: validateText, trigger: 'blur' } {validator: validateText, trigger: 'blur'}
], ],
dataBits:[ baudRate: [
{ validator: validateText, trigger: 'blur' } {validator: validateText, trigger: 'blur'}
], ],
stopBits:[ dataBits: [
{ validator: validateText, trigger: 'blur' } {validator: validateText, trigger: 'blur'}
], ],
parity:[ stopBits: [
{ validator: validateText, trigger: 'blur' } {validator: validateText, trigger: 'blur'}
],
parity: [
{validator: validateText, trigger: 'blur'}
], ],
// pictureUrl:[ // pictureUrl:[
// { required: true, message: '请上传图片', trigger: ['blur', 'change']} // { 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'}
],
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 || {}, communicationStatusOptions: state => state?.ems?.communicationStatusOptions || {},
deviceTypeOptions:state=>state?.ems?.deviceTypeOptions || {} deviceTypeOptions: state => state?.ems?.deviceTypeOptions || {}
}) }),
isPcs() {
return this.formData.deviceCategory === 'PCS'
}
}, },
watch:{ watch: {
'formData.deviceCategory':{ dialogTableVisible: {
handler(newVal){ handler(newVal) {
// 只有电池簇需要选择上级设备 调用接口获取电池堆列表
if(newVal && newVal === this.dccDeviceCategory){
this.getParentDeviceList()
}
},
immediate:true
},
'formData.siteId':{
handler(newVal,oldVal){
// 只有电池簇需要选择上级设备 调用接口获取电池堆列表
if(newVal && oldVal!== newVal && this.formData.deviceCategory === this.dccDeviceCategory){
this.getParentDeviceList()
}
},
immediate:true
},
dialogTableVisible:{
handler(newVal){
//打开弹窗 //打开弹窗
if(newVal){ if (newVal) {
this.getZdList() this.getZdList()
this.getDeviceCategoryList() this.getDeviceCategoryList()
} }
}, },
immediate: true, immediate: true,
}, },
id:{ id: {
handler(newVal){ handler(newVal) {
if((newVal || newVal===0) && this.mode !== 'add'){ if ((newVal || newVal === 0) && this.mode !== 'add') {
this.loading+=1 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 || {}))
}).finally(() => {this.loading-=1}) 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)
}
}).finally(() => {
this.loading -= 1
})
} }
}, },
immediate: true, immediate: true,
} }
}, },
methods: { methods: {
uploadImage(data){ changeType() {
if (this.dccDeviceCategoryList.includes(this.formData.deviceCategory)) {
this.getParentDeviceList()
}
},
uploadImage(data) {
this.formData.pictureUrl = data this.formData.pictureUrl = data
}, },
//获取站点列表 //获取站点列表
getZdList(){ getZdList() {
this.loading+=1 this.loading += 1
getAllSites().then(response => { getAllSites().then(response => {
this.siteList = response?.data || [] this.siteList = response?.data || []
}).finally(() => {this.loading-=1}) }).finally(() => {
this.loading -= 1
})
}, },
// 获取设备类别 // 获取设备类别
getDeviceCategoryList(){ getDeviceCategoryList() {
this.loading+=1 this.loading += 1
getDeviceCategory().then(response => { getAllDeviceCategory().then(response => {
this.deviceCategoryList = response?.data || [] this.deviceCategoryList = response?.data || []
}).finally(() => {this.loading-=1}) }).finally(() => {
this.loading -= 1
})
}, },
//获取上级id列表 //获取上级id列表
getParentDeviceList(){ getParentDeviceList(init = false) {
if(!this.formData.siteId){ if (!this.formData.siteId) {
return console.log('请先选择站点') return console.log('请先选择站点')
} }
this.formData.parentId='' !init && (this.formData.parentId = '')
this.loading= this.loading+1 this.loading = this.loading + 1
getStackNameList(this.formData.siteId).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 = '',//设备描述
communicationStatus = '',//工作状态
deviceType = '',//设备类型
deviceCategory = '',//设备类别
parentId = '',//上级设备id
ipAddress = '',//TCP设备的ip地址
ipPort = "",//TCP端口号
serialPort = '',//串口路径
baudRate = '',//波特率
dataBits = '',//数据位
stopBits = '',//停止位
parity = '',//校验位
pictureUrl = '',//设备图片
slaveId = '',//从站地址
} = this.formData;
const {
deviceSettingId,
powerAddress,
pointAddress,
startCommand,
stopCommand,
startPower,
stopPower,
clusterNum,
clusterPointAddress
} = this.pcsSetting
let params = {
siteId,
deviceId,
deviceName,
description,
communicationStatus,
deviceType,
deviceCategory,
parentId,
ipAddress,
ipPort,
serialPort,
baudRate,
dataBits,
stopBits,
parity,
pictureUrl,
slaveId,
}
if (this.isPcs) {
params.pcsSetting = {
powerAddress,
pointAddress,
startCommand,
stopCommand,
startPower,
stopPower,
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.$refs.addTempForm.resetFields() this.formData = {
this.formData= { id: '',//设备唯一标识
id:'',//设备唯一标识 siteId: '',//站点ID
siteId:'',//站点ID deviceId: '',//设备id
deviceId:'',//设备id deviceName: '',//设备名称
deviceName:'',//设备名称 description: '',//设备描述
description:'',//设备描述 communicationStatus: '',//工作状态
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:'',//校验位 pictureUrl: '',//设备图片
pictureUrl:'',//设备图片 slaveId: '',//从站地址
} }
this.dialogTableVisible=false this.pcsSetting = {
deviceSettingId: '',
powerAddress: '',//功率地址
pointAddress: "",//开关机地址
startCommand: "",//开机指令
stopCommand: "",//关机指令
startPower: '',//开机目标功率
stopPower: '',//关机目标功率
clusterNum: '',//电池簇数
clusterPointAddress: []//电池簇地址
}
this.$refs.addTempForm.resetFields()
this.$refs?.pcsSettingForm?.resetFields()
this.dialogTableVisible = false
} }
} }
} }
</script> </script>
<style scoped> <style scoped lang="scss">
.device-form {
::v-deep .el-form-item--medium .el-form-item__content {
width: 260px;
}
.el-form-item {
width: 50%;
margin-right: 0;
}
}
</style> </style>

View File

@ -0,0 +1,98 @@
<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
}
},
data() {
return {
runningStatusMap: ['0', '3', '4', '6']
}
},
computed: {
label() {
return this.runningStatusMap.includes(this.data.workStatus) ? '关机' : '开机'
}
},
methods: {
switchStatus() {
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: this.runningStatusMap.includes(workStatus) ? "1" : '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>

View File

@ -1,186 +1,297 @@
<!--电位展示图表--> <!--电位展示图表-->
<template> <template>
<el-dialog <div>
v-loading="loading" <el-dialog
:close-on-click-modal="false" v-loading="loading"
:visible.sync="show" :close-on-click-modal="false"
append-to-body :visible.sync="show"
class="ems-dialog" append-to-body
destroy-on-close class="ems-dialog"
lock-scroll destroy-on-close
show-close lock-scroll
title="点位清单" show-close
width="800px" :title="dataType === 'point' ? '点位清单' : '报警点位'"
> width="950px"
<el-form :inline="true" label-width="85px">
<el-form-item label="点位名称">
<el-input v-model="form.dataPointName" clearable placeholder="请输入点位名称" style="width: 150px"></el-input>
</el-form-item>
<el-form-item label="点位">
<el-input v-model="form.dataPoint" clearable placeholder="请输入点位" style="width: 150px"></el-input>
</el-form-item>
<br>
<el-form-item label="最小值">
<el-input v-model="form.lower" clearable placeholder="请输入最小值" style="width: 150px"></el-input>
</el-form-item>
<el-form-item label="最大值">
<el-input v-model="form.upper" clearable placeholder="请输入最大值" style="width: 150px"></el-input>
</el-form-item>
<el-form-item style="margin-left: 20px">
<el-button type="primary" @click="search">搜索</el-button>
</el-form-item>
</el-form>
<el-table
:data="tableData"
class="common-table"
max-height="400px"
stripe
style="width: 100%;"
:default-sort = "{prop: 'updateTime', order: 'descending'}"
@sort-change="changeSort">
<el-table-column
label="数据点位"
prop="dataPoint">
</el-table-column>
<el-table-column
label="数据点位名称"
prop="dataPointName">
</el-table-column>
<el-table-column
label="最新值"
prop="pointValue">
</el-table-column>
<el-table-column
label="更新时间"
prop="updateTime"
sortable="custom"
>
</el-table-column>
</el-table>
<el-pagination
v-show="tableData.length>0"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:pager-count="5"
:total="totalSize"
background
layout="total, sizes, prev, pager, next, jumper"
small
style="margin-top:15px;text-align: center"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
> >
</el-pagination> <el-form :inline="true" label-width="100px">
</el-dialog> <el-form-item label="点位名称">
<el-input
v-model="form.dataPointName"
clearable
placeholder="请输入点位名称"
style="width: 150px"
></el-input>
</el-form-item>
<el-form-item label="点位">
<el-input
v-model="form.dataPoint"
clearable
placeholder="请输入点位"
style="width: 150px"
></el-input>
</el-form-item>
<!-- <el-form-item label="modbus ip">-->
<!-- <el-input-->
<!-- v-model="form.ipAddress"-->
<!-- clearable-->
<!-- placeholder="请输入ip"-->
<!-- style="width: 150px"-->
<!-- ></el-input>-->
<!-- </el-form-item>-->
<br>
<el-form-item label="最小值">
<el-input
v-model="form.lower"
clearable
placeholder="请输入最小值"
style="width: 150px"
></el-input>
</el-form-item>
<el-form-item label="最大值">
<el-input
v-model="form.upper"
clearable
placeholder="请输入最大值"
style="width: 150px"
></el-input>
</el-form-item>
<!-- <el-form-item label="modbus 端口">-->
<!-- <el-input-->
<!-- v-model="form.ipPort"-->
<!-- clearable-->
<!-- placeholder="请输入端口"-->
<!-- style="width: 150px"-->
<!-- ></el-input>-->
<!-- </el-form-item>-->
<el-form-item style="margin-left: 20px">
<el-button type="primary" @click="search">搜索</el-button>
</el-form-item>
</el-form>
<el-table
:data="tableData"
class="common-table"
max-height="400px"
stripe
style="width: 100%"
:default-sort="defaultSort"
@sort-change="handleSortChange"
>
<el-table-column label="数据点位" prop="dataPoint"></el-table-column>
<el-table-column
label="数据点位名称"
prop="dataPointName"
></el-table-column>
<!-- <el-table-column label="modbus地址">-->
<!-- <template slot-scope="scope">-->
<!-- <span>{{-->
<!-- `${scope.row.ipAddress || ""} ${scope.row.ipPort || ""}`-->
<!-- }}</span>-->
<!-- </template>-->
<!-- </el-table-column>
<el-table-column label="寄存器地址" prop="寄存器地址"></el-table-column>-->
<el-table-column
label="最新值"
prop="pointValue"
align="right"
sortable="custom"
>
<template slot-scope="scope">
<span class="pointer" @click="showChart(scope.row)">{{
scope.row.pointValue
}}</span>
</template>
</el-table-column>
<el-table-column label="单位" prop="dataUnit"></el-table-column>
<el-table-column
label="更新时间"
min-width="160px"
prop="updateTime"
sortable="custom"
>
</el-table-column>
</el-table>
<el-pagination
v-show="tableData.length > 0"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:pager-count="5"
:total="totalSize"
background
layout="total, sizes, prev, pager, next, jumper"
small
style="margin-top: 15px; text-align: center"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</el-dialog>
<point-chart ref="pointChart" :site-id="siteId"/>
</div>
</template> </template>
<script> <script>
import {getDevicePointList} from "@/api/ems/site"; import {getDevicePointList} from "@/api/ems/site";
import pointChart from "@/views/ems/dzjk/sbjk/PointChart.vue";
export default { export default {
components: {pointChart},
watch: { watch: {
show(val) { show(val) {
if (!val) { if (!val) {
this.tableData = [] this.tableData = [];
this.deviceId = '' this.deviceId = "";
this.deviceName = '' this.parentId = "";
this.siteId = '' this.siteId = "";
this.pageSize = 10 this.pageSize = 10;
this.pageNum = 1 this.pageNum = 1;
this.totalSize = 0 this.totalSize = 0;
this.dataType = '';
this.form = { this.form = {
sortMethod: 'desc',//升序不传或者asc、降序desc sortMethod: "desc", //升序不传或者asc、降序desc
dataPointName: '',//点位名称 sortData: this.defaultSort.prop,
dataPoint: '',//点位名称 dataPointName: "", //点位名称
lower: '',// dataPoint: "", //点位名称
upper: '',// lower: "", //
} upper: "", //
this.loading = false // ipAddress: "",
// ipPort: "",
};
this.loading = false;
} }
}, },
}, },
computed: {
isDtdc() {
return this.deviceCategory === "BATTERY";
},
},
data() { data() {
return { return {
// 默认排序
defaultSort: {prop: "updateTime", order: "descending"},
show: false, show: false,
loading: false, loading: false,
dataType: '',//展示的数据类型 point点位/alarmPoint报警点位
form: { form: {
sortMethod: 'desc',//升序不传或者asc、降序desc sortData: "updateTime", //最新值升序不传或者asc、降序desc
dataPointName: '',//点位名称 sortMethod: "desc", //升序不传或者asc、降序desc
dataPoint: '',//点位名称 dataPointName: "", //点位名称
lower: '',// dataPoint: "", //点位名称
upper: '',// lower: "", //
upper: "", //
// ipAddress: "",
// ipPort: "",
}, },
deviceCategory: '', deviceCategory: "",
deviceName: '', deviceId: "",
deviceId: '', parentId: "",
siteId: '', siteId: "",
tableData: [], tableData: [],
pageSize: 10,//分页栏当前每个数据总数 pageSize: 10, //分页栏当前每个数据总数
pageNum: 1,//分页栏当前页数 pageNum: 1, //分页栏当前页数
totalSize: 0,//table表格数据总数 totalSize: 0, //table表格数据总数
} };
}, },
methods: { methods: {
changeSort(c) { showChart({pointName}) {
console.log('切换排序方式', c) if (pointName) {
if(c.prop === "updateTime" && c.order){ const {deviceCategory, deviceId} = this;
this.form.sortMethod = c.order === 'descending' ? 'desc' : 'asc' if (this.isDtdc)
this.getData() this.$refs.pointChart.showChart({
pointName,
deviceCategory,
deviceId: this.parentId,
child: [deviceId],
});
else
this.$refs.pointChart.showChart({
pointName,
deviceCategory,
deviceId,
});
} }
}, },
handleSortChange(column) {
this.form.sortData = column.prop;
this.form.sortMethod = column.order === "descending" ? "desc" : "asc";
console.log("切换排序方式", column, this.form);
this.getData()
},
search() { search() {
this.pageNum = 1 this.pageNum = 1;
this.getData() this.getData()
}, },
showTable({deviceCategory, siteId, deviceId, deviceName}) {
this.deviceCategory = deviceCategory
this.siteId = siteId
this.deviceId = deviceId
this.deviceName = deviceName
this.show = true
this.getData()
},
getData() {
this.loading = true
const {
siteId,
deviceId,
deviceCategory,
pageNum,
pageSize,
form: {sortMethod, dataPointName, dataPoint, lower, upper}
} = this
getDevicePointList({
siteId,
deviceId,
deviceCategory,
pageNum,
pageSize,
sortMethod, dataPointName, dataPoint, lower, upper
}).then(response => {
this.tableData = response?.rows || [];
this.totalSize = response?.total || 0
}).finally(() => {
this.loading = false
})
},
// 分页 // 分页
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()
}) });
}, },
} showTable({deviceCategory, siteId, deviceId, parentId = ""}, dataType) {
} this.dataType = dataType;
this.deviceCategory = deviceCategory;
this.siteId = siteId;
this.deviceId = deviceId;
this.parentId = deviceCategory === "BATTERY" ? parentId : ""; //只有单体电池需要这个值
this.show = true;
this.getData()
},
getData() {
const {
siteId,
deviceId,
deviceCategory,
parentId,
pageNum,
pageSize,
form: {
sortData,
sortMethod,
dataPointName,
dataPoint,
lower,
upper,
// ipAddress,
// ipPort,
},
} = this;
const params = {
siteId,
deviceId,
deviceCategory,
parentId,
pageNum,
pageSize,
sortData,
sortMethod,
dataPointName,
dataPoint,
lower,
upper,
// ipAddress: '',
// ipPort: '',
}
params.isAlarm = this.dataType === 'point' ? 0 : 1
this.loading = true;
getDevicePointList(params)
.then((response) => {
this.tableData = response?.rows || [];
this.totalSize = response?.total || 0;
})
.finally(() => {
this.loading = false;
});
},
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep { ::v-deep {
@ -188,5 +299,4 @@ export default {
line-height: 40px; line-height: 40px;
} }
} }
</style>
</style>

View File

@ -0,0 +1,80 @@
<template>
<el-dialog :visible.sync="show" class="ems-dialog" title="点位上传" width="400px" append-to-body
:close-on-click-modal="false" :close-on-press-escape="false">
<!-- <file-upload :fileType='["xls", "xlsx"]' value="String" :limit="1" :drag="false" @input="updateFile"/>-->
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url"
:data="upload.data" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<span>仅允许导入xlsxlsx格式文件</span>
<!-- <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>-->
</div>
</el-upload>
<div slot="footer">
<el-button @click="handleClosed">取消</el-button>
<el-button type="primary" @click="submitFileForm">确定</el-button>
</div>
</el-dialog>
</template>
<style scoped lang="scss">
</style>
<script>
import {getToken} from "@/utils/auth"
export default {
data() {
return {
show: false,
// 用户导入参数
upload: {
data: {},
// 是否禁用上传
isUploading: false,
// 设置上传的请求头部
headers: {Authorization: "Bearer " + getToken()},
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/ems/pointMatch/importDataByDevice"
},
}
},
methods: {
showDialog({siteId, deviceCategory, deviceId}) {
this.upload.data = {siteId, deviceCategory, deviceId}
this.show = true;
},
//关闭弹窗 重置数据
handleClosed() {
this.$refs.upload.clearFiles()//写在before-closed无效
this.upload.data = {}
this.show = false
},
// updateFile(data){
// console.log('上传文件emit',data)
// },
// saveDialog(){
// this.show=true
// },
// 文件上传中处理 返回参数event, file, fileList
handleFileUploadProgress() {
this.upload.isUploading = true
},
// 文件上传成功处理 返回参数response, file, fileList
handleFileSuccess(response) {
console.log('上传文件结果', response)
this.upload.isUploading = false
if (response.code === 500 && !response.msg) response.msg = '上传失败'
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + (response.msg || '上传成功') + "</div>", "导入结果", {dangerouslyUseHTMLString: true})
this.$emit('update')
this.handleClosed()
},
// 提交上传文件
submitFileForm() {
this.$refs.upload.submit()
}
}
}
</script>

View File

@ -1,175 +1,283 @@
<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="loading" loading-text="正在加载数据" @change="onSearch"> <el-select v-model="siteId" placeholder="请选择换电站名称" :loading="searchLoading" loading-text="正在加载数据"
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option> @change="onSearch" clearable>
</el-select> <el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList"
</el-form-item> :key="index+'zdxeSelect'"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备类型">
<el-select v-model="deviceCategory" placeholder="请选择设备类型" @change="onSearch" clearable>
<el-option
v-for="(item,index) in deviceCategoryList"
:key="index+'deviceCategorySelect'"
:label="item.name"
:value="item.code">
</el-option>
</el-select>
</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-button> <el-button type="primary" @click="addDevice" native-type="button">新增设备</el-button>
<el-dropdown @command="(val)=>downloadPointDetail(val,false)">
<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="(val)=>uploadPointDetail(val,false)">-->
<!-- <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"
stripe stripe
max-height="600px" max-height="600px"
style="width: 100%;margin-top: 25px"> style="width: 100%;margin-top: 25px">
<el-table-column <el-table-column
prop="siteId" prop="siteId"
label="站点ID"> label="站点ID">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="siteName" prop="siteName"
label="站点名称"> label="站点名称">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="deviceId" prop="deviceId"
label="设备ID" label="设备ID"
> >
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="deviceName" prop="deviceName"
label="设备名称"> label="设备名称">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="deviceType" prop="categoryName"
label="设备类"> label="设备类">
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="communicationStatus" prop="deviceStatus"
label="通信状态"> label="在线状态">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{$store.state.ems.communicationStatusOptions[scope.row.communicationStatus]}}</span> <span>{{ $store.state.ems.deviceStatusOptions[scope.row.deviceStatus] }}</span>
<pcs-switch v-if="scope.row.deviceCategory === 'PCS' && ![null,'',undefined].includes(scope.row.workStatus)"
style="margin-left:5px;"
:data="{siteId:scope.row.siteId,workStatus:scope.row.workStatus,deviceId:scope.row.deviceId,deviceName:scope.row.deviceName}"
@updateSuccess="getData"/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
fixed="right" fixed="right"
label="操作" label="操作"
width="260"> width="250">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
@click="pointDetail(scope.row)" @click="pointDetail(scope.row,'point')"
type="primary" type="primary"
size="mini"> size="mini">
点位清单 点位清单
</el-button> </el-button>
<el-button <el-button
@click="editDevice(scope.row)" @click="pointDetail(scope.row,'alarmPoint')"
type="warning" type="primary"
size="mini"> size="mini">
报警点位清单
</el-button>
<br>
<el-button
@click="downloadPointDetail(scope.row,true)"
style="margin-top:10px;"
type="primary"
plain
size="mini">
下载点位清单
</el-button>
<el-button
@click="uploadPointDetail(scope.row,true)"
style="margin-top:10px;"
type="success"
plain
size="mini">
上传点位清单
</el-button>
<br>
<el-button
@click="editDevice(scope.row)"
style="margin-top:10px;"
type="warning"
size="mini">
编辑 编辑
</el-button> </el-button>
<el-button <el-button
type="danger" type="danger"
@click="deleteDevice(scope.row)" style="margin-top:10px;"
size="mini"> @click="deleteDevice(scope.row)"
size="mini">
删除 删除
</el-button> </el-button>
</template> </template>
</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>
<el-dialog :visible.sync="dialogTableVisible" class="ems-dialog" title=" 详细信息" :close-on-click-modal="false" :before-close="handleClosed"> <el-dialog :visible.sync="dialogTableVisible" class="ems-dialog" title="详细信息" :close-on-click-modal="false"
<div class="descriptions-main" style="padding: 0" > :before-close="handleClosed">
<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>
<el-descriptions-item v-for="(item,index) in detailData" :key="index+'descriptions'" labelClassName="descriptions-label" contentClassName="descriptions-direction" :span="1" :label="item.label">{{item.value}}</el-descriptions-item> <el-descriptions-item v-for="(item,index) in detailData" :key="index+'descriptions'"
labelClassName="descriptions-label" contentClassName="descriptions-direction" :span="1"
:label="item.label">{{ item.value }}
</el-descriptions-item>
</el-descriptions> </el-descriptions>
</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-table ref="pointTable"/>
<point-upload ref="pointUpload" @update="getData"/>
</div> </div>
</template> </template>
<script> <script>
import {getDeviceInfoList,getDeviceDetailInfo,deleteService} from'@/api/ems/site' import {deleteService, getDeviceDetailInfo, getDeviceInfoList} from '@/api/ems/site'
import {getAllSites} from '@/api/ems/zddt' import {getAllSites} from '@/api/ems/zddt'
import {formatNumber} from "@/filters/ems"; import {formatNumber} from "@/filters/ems";
import {getAllDeviceCategory} from '@/api/ems/search'
import PointTable from './PointTable.vue' import PointTable from './PointTable.vue'
import AddDevice from "./AddDevice.vue"; import AddDevice from "./AddDevice.vue";
import PointUpload from "./PointUpload.vue";
import PcsSwitch from "./PcsSwitch.vue";
export default { export default {
name: "Sblb", name: "Sblb",
components:{AddDevice,PointTable}, components: {AddDevice, PointTable, PointUpload, PcsSwitch},
data() { data() {
return { return {
loading:false, loading: false,
searchLoading:false, searchLoading: false,
mode:'',//新增、编辑设备 mode: '',//新增、编辑设备
editDeviceId:'',//编辑设备id editDeviceId: '',//编辑设备id
siteId:'', siteId: '',
siteList:[], siteList: [],
tableData:[], deviceCategory: '',//搜索栏设备类型
pageSize:10,//分页栏当前每个数据总数 deviceCategoryList: [],//设备类别
pageNum:1,//分页栏当前页数 tableData: [],
totalSize:0,//table表格数据总数 pageSize: 10,//分页栏当前每个数据总数
dialogTableVisible:false, pageNum: 1,//分页栏当前页数
detailData:[ totalSize: 0,//table表格数据总数
{label:'设备ID',value:'',attr:'deviceId'}, dialogTableVisible: false,
{label:'设备名称',value:'',attr:'deviceName'}, detailData: [
{label:'设备类型',value:'',attr:'deviceType'}, {label: '设备ID', value: '', attr: 'deviceId'},
{label:'通信状态',value:'',attr:'communicationStatus'}, {label: '设备名称', value: '', attr: 'deviceName'},
{label:'TCP设备的IP地址',value:'',attr:'ipAddress'}, {label: '设备类型', value: '', attr: 'deviceType'},
{label:'TCP端口号',value:'',attr:'ipPort'}, {label: '通信状态', value: '', attr: 'communicationStatus'},
{label:'串口路径',value:'',attr:'serialPort'}, {label: 'TCP设备的IP地址', value: '', attr: 'ipAddress'},
{label:'波特率',value:'',attr:'baudRate'}, {label: 'TCP端口号', value: '', attr: 'ipPort'},
{label:'数据位',value:'',attr:'dataBits'}, {label: '串口路径', value: '', attr: 'serialPort'},
{label:'停止位',value:'',attr:'stopBits'}, {label: '波特率', value: '', attr: 'baudRate'},
{label:'校验位',value:'',attr:'parity'}, {label: '数据位', value: '', attr: 'dataBits'},
{label:'设备描述',value:'',attr:'description'}, {label: '停止位', value: '', attr: 'stopBits'},
{label:'设备类别',value:'',attr:'deviceCategory'}, {label: '校验位', value: '', attr: 'parity'},
{label:'上级设备ID',value:'',attr:'parentId'}, {label: '设备描述', value: '', attr: 'description'},
{label:'设备唯一标识符',value:'',attr:'deviceId'}, {label: '设备类别', value: '', attr: 'deviceCategory'},
{label: '上级设备ID', value: '', attr: 'parentId'},
{label: '设备唯一标识符', value: '', attr: 'deviceId'},
], ],
detailformatAttr:{ detailformatAttr: {
'deviceType':'deviceTypeOptions', 'deviceType': 'deviceTypeOptions',
'communicationStatus':'communicationStatusOptions', 'communicationStatus': 'communicationStatusOptions',
} }
} }
}, },
methods:{ methods: {
// 查看设备电位表格 // 获取设备类别
pointDetail(row){ getDeviceCategoryList() {
this.$refs.pointTable.showTable(row) getAllDeviceCategory().then(response => {
this.deviceCategoryList = response?.data || []
})
}, },
clearEditDeviceData(){ // 查看设备电位表格
pointDetail(row, dataType) {
this.$refs.pointTable.showTable(row, dataType)
},
// 下载点位清单
downloadPointDetail(command, isDetail = false) {
const siteId = isDetail ? command.siteId : this.siteId
const deviceCategory = isDetail ? command.deviceCategory : command.code
const categoryName = isDetail ? command.categoryName : command.name
const deviceId = isDetail ? command.deviceId : null
console.log('下载', command, isDetail)
this.download('ems/pointMatch/export', {
siteId,
deviceCategory,
deviceId,
}, `点位清单_${categoryName}_${new Date().getTime()}.xlsx`)
},
// 上传点位清单
uploadPointDetail(command, isDetail = false) {
const siteId = isDetail ? command.siteId : this.siteId
const deviceCategory = isDetail ? command.deviceCategory : command.code
const categoryName = isDetail ? command.categoryName : command.name
const deviceId = isDetail ? command.deviceId : ''
console.log('上传', command, isDetail)
this.$refs.pointUpload.showDialog({siteId, deviceCategory, categoryName, deviceId})
},
clearEditDeviceData() {
this.mode = ''; this.mode = '';
this.editDeviceId='' this.editDeviceId = ''
}, },
// 新增设备 展示弹窗 // 新增设备 展示弹窗
addDevice(){ addDevice() {
this.mode = 'add'; this.mode = 'add';
this.$refs.addDevice.dialogTableVisible = true this.$refs.addDevice.dialogTableVisible = true
}, },
// 编辑设备 // 编辑设备
editDevice(row){ editDevice(row) {
this.mode = 'edit'; this.mode = 'edit';
this.editDeviceId = row.id this.editDeviceId = row.id
this.$refs.addDevice.dialogTableVisible = true; this.$refs.addDevice.dialogTableVisible = true;
}, },
//删除设备 //删除设备
deleteDevice(row){ deleteDevice(row) {
this.$confirm(`确认要设备${row.deviceName}吗?`, { this.$confirm(`确认要设备${row.deviceName}吗?`, {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
showClose:false, showClose: false,
closeOnClickModal:false, closeOnClickModal: false,
type: 'warning', type: 'warning',
beforeClose: (action, instance, done) => { beforeClose: (action, instance, done) => {
if (action === 'confirm') { if (action === 'confirm') {
@ -195,70 +303,84 @@ export default {
//取消关机 //取消关机
}); });
}, },
handleClosed(done){ handleClosed(done) {
this.detailData.forEach((item)=>{item.value = ''}) this.detailData.forEach((item) => {
item.value = ''
})
done() done()
}, },
toDetail(id){ toDetail(id) {
this.loading = true this.loading = true
getDeviceDetailInfo(id).then(response => { getDeviceDetailInfo(id).then(response => {
const data = response?.data || {}; const data = response?.data || {};
this.detailData.forEach(item=>{ this.detailData.forEach(item => {
if(this.detailformatAttr[item.attr]){ if (this.detailformatAttr[item.attr]) {
item.value=formatNumber(this.$store.state.ems[this.detailformatAttr[item.attr]][data[item.attr]]); item.value = formatNumber(this.$store.state.ems[this.detailformatAttr[item.attr]][data[item.attr]]);
}else{ } else {
item.value=formatNumber(data[item.attr]); item.value = formatNumber(data[item.attr]);
} }
}) })
this.dialogTableVisible = true this.dialogTableVisible = true
}).finally(() => {this.loading = false}) }).finally(() => {
this.loading = false
})
}, },
// 分页 // 分页
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.siteId='' this.siteId = ''
this.pageNum =1//每次搜索从1开始搜索 this.pageNum = 1//每次搜索从1开始搜索
this.getData() this.getData()
}, },
// 获取数据 // 获取数据
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 || []; this.tableData = response?.rows || [];
this.totalSize = response?.total || 0 this.totalSize = response?.total || 0
}).finally(() => {this.loading=false}) }).finally(() => {
this.loading = false
})
}, },
//获取站点列表 //获取站点列表
getZdList(){ getZdList() {
this.searchLoading=true this.searchLoading = true
getAllSites().then(response => { return getAllSites().then(response => {
this.siteList = response?.data || [] this.siteList = response?.data || []
}).finally(() => {this.searchLoading=false}) if (this.siteList.length > 0) this.siteId = this.siteList[0].siteId
}).finally(() => {
this.searchLoading = false
})
} }
}, },
mounted() { mounted() {
this.onReset() this.loading = true
this.getZdList() this.siteId = ''
this.pageNum = 1//每次搜索从1开始搜索
this.getDeviceCategoryList()
this.getZdList().then(() => {
this.getData()
})
} }
} }
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog v-loading="loading>0" :visible.sync="dialogTableVisible" class="ems-dialog" :title="mode === 'add'?'新增工单':`编辑工单` " :close-on-click-modal="false" :show-close="false"> <el-dialog v-loading="loading>0" :visible.sync="dialogTableVisible" class="ems-dialog" :title="mode === 'add'?'新增工单':`编辑工单`" :close-on-press-escape="false" :close-on-click-modal="false" :show-close="false">
<el-form ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="120px"> <el-form ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="120px">
<el-form-item label="工单号" prop="ticketNo" v-if="mode !== 'add'"> <el-form-item label="工单号" prop="ticketNo" v-if="mode !== 'add'">
<el-input disabled v-model="formData.ticketNo" clearable :style="{width: '100%'}"> <el-input disabled v-model="formData.ticketNo" clearable :style="{width: '100%'}">
@ -159,7 +159,7 @@ export default {
this.$refs.addTempForm.validate(valid => { this.$refs.addTempForm.validate(valid => {
if (!valid) return if (!valid) return
this.loading+=1 this.loading+=1
const {title='',content='',status='',userId='',workUserId='',id='',expectedCompleteTime=''} = this.formData; const {title='',content='',status='',userId='',workUserId='',id='',expectedCompleteTime='',ticketNo=''} = this.formData;
if(this.mode === 'add'){ if(this.mode === 'add'){
addTicket({title,content,status,userId,workUserId,expectedCompleteTime}).then(response => { addTicket({title,content,status,userId,workUserId,expectedCompleteTime}).then(response => {
if(response.code === 200){ if(response.code === 200){
@ -172,7 +172,7 @@ export default {
this.loading-=1 this.loading-=1
}) })
}else{ }else{
updateTicket({title,content,status,userId,workUserId,id,expectedCompleteTime}).then(response => { updateTicket({title,content,status,userId,workUserId,id,expectedCompleteTime,ticketNo}).then(response => {
if(response.code === 200){ if(response.code === 200){
//新增成功 //新增成功
// 关闭弹窗 更新表格 // 关闭弹窗 更新表格

View File

@ -1,7 +1,18 @@
<template> <template>
<div class="login"> <div class="login">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"> <img :src="loginBg" alt="" srcset="" class="login-bg" />
<h3 class="title">{{title}}</h3> <el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<img
src="./../assets/images/ems/logo.png"
alt=""
srcset=""
class="login-logo"
/>
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="loginForm.username" v-model="loginForm.username"
@ -9,7 +20,11 @@
auto-complete="off" auto-complete="off"
placeholder="账号" placeholder="账号"
> >
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> <svg-icon
slot="prefix"
icon-class="user"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
@ -20,7 +35,11 @@
placeholder="密码" placeholder="密码"
@keyup.enter.native="handleLogin" @keyup.enter.native="handleLogin"
> >
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> <svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="code" v-if="captchaEnabled"> <el-form-item prop="code" v-if="captchaEnabled">
@ -31,152 +50,194 @@
style="width: 63%" style="width: 63%"
@keyup.enter.native="handleLogin" @keyup.enter.native="handleLogin"
> >
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> <svg-icon
slot="prefix"
icon-class="validCode"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
<div class="login-code"> <div class="login-code">
<img :src="codeUrl" @click="getCode" class="login-code-img"/> <img :src="codeUrl" @click="getCode" class="login-code-img" />
</div> </div>
</el-form-item> </el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> <el-checkbox
<el-form-item style="width:100%;"> v-model="loginForm.rememberMe"
style="margin: 0px 0px 25px 0px"
>记住密码</el-checkbox
>
<el-form-item style="width: 100%">
<el-button <el-button
:loading="loading" :loading="loading"
size="medium" size="medium"
type="primary" type="primary"
style="width:100%;" style="width: 100%"
@click.native.prevent="handleLogin" @click.native.prevent="handleLogin"
> >
<span v-if="!loading"> </span> <span v-if="!loading"> </span>
<span v-else> 中...</span> <span v-else> 中...</span>
</el-button> </el-button>
<div style="float: right;" v-if="register"> <div style="float: right" v-if="register">
<router-link class="link-type" :to="'/register'">立即注册</router-link> <router-link class="link-type" :to="'/register'"
>立即注册</router-link
>
</div> </div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 底部 --> <!-- 底部 -->
<div class="el-login-footer"> <div class="el-login-footer">
<span>Copyright © 2025 海电动工具研究所集团有限公司.</span> <span>Copyright © 2025 动新能源 版权所有</span>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { getCodeImg } from "@/api/login" import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie" import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt' import { encrypt, decrypt } from "@/utils/jsencrypt";
import intervalUpdate from "@/mixins/ems/intervalUpdate";
export default { export default {
name: "Login", name: "Login",
mixins: [intervalUpdate],
computed: {
loginBg() {
return require(`./../assets/images/ems/loginBg/${this.bgNum}.png`);
},
},
data() { data() {
return { return {
title: process.env.VUE_APP_TITLE, bgNum: 1,
codeUrl: "", codeUrl: "",
loginForm: { loginForm: {
username: "admin", username: "admin",
password: "admin123", password: "admin123",
rememberMe: false, rememberMe: false,
code: "", code: "",
uuid: "" uuid: "",
}, },
loginRules: { loginRules: {
username: [ username: [
{ required: true, trigger: "blur", message: "请输入您的账号" } { required: true, trigger: "blur", message: "请输入您的账号" },
], ],
password: [ password: [
{ required: true, trigger: "blur", message: "请输入您的密码" } { required: true, trigger: "blur", message: "请输入您的密码" },
], ],
code: [{ required: true, trigger: "change", message: "请输入验证码" }] code: [{ required: true, trigger: "change", message: "请输入验证码" }],
}, },
loading: false, loading: false,
// 验证码开关 // 验证码开关
captchaEnabled: true, captchaEnabled: true,
// 注册开关 // 注册开关
register: false, register: false,
redirect: undefined redirect: undefined,
} };
}, },
watch: { watch: {
$route: { $route: {
handler: function(route) { handler: function (route) {
this.redirect = route.query && route.query.redirect this.redirect = route.query && route.query.redirect;
}, },
immediate: true immediate: true,
} },
}, },
created() { created() {
this.getCode() this.getCode();
this.getCookie() this.getCookie();
},
mounted() {
this.updateInterval(this.updateBgNum, 5000);
}, },
methods: { methods: {
updateBgNum() {
if (this.bgNum >= 4) this.bgNum = 0;
this.bgNum += 1;
},
getCode() { getCode() {
getCodeImg().then(res => { getCodeImg().then((res) => {
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled this.captchaEnabled =
res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (this.captchaEnabled) { if (this.captchaEnabled) {
this.codeUrl = "data:image/gif;base64," + res.img this.codeUrl = "data:image/gif;base64," + res.img;
this.loginForm.uuid = res.uuid this.loginForm.uuid = res.uuid;
} }
}) });
}, },
getCookie() { getCookie() {
const username = Cookies.get("username") const username = Cookies.get("username");
const password = Cookies.get("password") const password = Cookies.get("password");
const rememberMe = Cookies.get('rememberMe') const rememberMe = Cookies.get("rememberMe");
this.loginForm = { this.loginForm = {
username: username === undefined ? this.loginForm.username : username, username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : decrypt(password), password:
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe) password === undefined ? this.loginForm.password : decrypt(password),
} rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
};
}, },
handleLogin() { handleLogin() {
this.$refs.loginForm.validate(valid => { this.$refs.loginForm.validate((valid) => {
if (valid) { if (valid) {
this.loading = true this.loading = true;
if (this.loginForm.rememberMe) { if (this.loginForm.rememberMe) {
Cookies.set("username", this.loginForm.username, { expires: 30 }) Cookies.set("username", this.loginForm.username, { expires: 30 });
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 }) Cookies.set("password", encrypt(this.loginForm.password), {
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 }) expires: 30,
});
Cookies.set("rememberMe", this.loginForm.rememberMe, {
expires: 30,
});
} else { } else {
Cookies.remove("username") Cookies.remove("username");
Cookies.remove("password") Cookies.remove("password");
Cookies.remove('rememberMe') Cookies.remove("rememberMe");
} }
this.$store.dispatch("Login", this.loginForm).then(() => { this.$store
this.$router.push({ path: this.redirect || "/" }).catch(()=>{}) .dispatch("Login", this.loginForm)
}).catch(() => { .then(() => {
this.loading = false this.$router.push({ path: this.redirect || "/" }).catch(() => {});
if (this.captchaEnabled) { })
this.getCode() .catch(() => {
} this.loading = false;
}) if (this.captchaEnabled) {
this.getCode();
}
});
} }
}) });
} },
} },
} };
</script> </script>
<style rel="stylesheet/scss" lang="scss"> <style rel="stylesheet/scss" lang="scss">
.login { .login {
padding-left: 180px;
display: flex; display: flex;
justify-content: center; justify-content: left;
align-items: center; align-items: center;
height: 100%; height: 100%;
background-image: url("../assets/images/ems/login-background.png");
background-size: cover;
} }
.title { .login-bg {
margin: 0px auto 30px auto; position: absolute;
text-align: center; top: 0;
color: #707070; left: 0;
z-index: 1;
display: block;
height: 100%;
width: 100%;
}
.login-logo {
display: block;
width: 70%;
height: auto;
margin: 0 auto;
} }
.login-form { .login-form {
border-radius: 6px; border-radius: 6px;
background: #ffffff; background: #ffffff;
width: 400px; width: 400px;
padding: 25px 25px 5px 25px; padding: 0 25px 5px 25px;
z-index: 1; z-index: 2;
.el-input { .el-input {
height: 38px; height: 38px;
input { input {
@ -204,6 +265,8 @@ export default {
} }
} }
.el-login-footer { .el-login-footer {
margin-left: -180px;
z-index:2;
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
position: fixed; position: fixed;

View File

@ -1,10 +1,30 @@
<template> <template>
<div class="register"> <div class="register">
<el-form ref="registerForm" :model="registerForm" :rules="registerRules" class="register-form"> <img :src="loginBg" alt="" srcset="" class="login-bg" />
<h3 class="title">{{title}}</h3> <el-form
ref="registerForm"
:model="registerForm"
:rules="registerRules"
class="register-form"
>
<img
src="./../assets/images/ems/logo.png"
alt=""
srcset=""
class="login-logo"
/>
<el-form-item prop="username"> <el-form-item prop="username">
<el-input v-model="registerForm.username" type="text" auto-complete="off" placeholder="账号"> <el-input
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> v-model="registerForm.username"
type="text"
auto-complete="off"
placeholder="账号"
>
<svg-icon
slot="prefix"
icon-class="user"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
@ -15,7 +35,11 @@
placeholder="密码" placeholder="密码"
@keyup.enter.native="handleRegister" @keyup.enter.native="handleRegister"
> >
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> <svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="confirmPassword"> <el-form-item prop="confirmPassword">
@ -26,7 +50,11 @@
placeholder="确认密码" placeholder="确认密码"
@keyup.enter.native="handleRegister" @keyup.enter.native="handleRegister"
> >
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> <svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="code" v-if="captchaEnabled"> <el-form-item prop="code" v-if="captchaEnabled">
@ -37,136 +65,190 @@
style="width: 63%" style="width: 63%"
@keyup.enter.native="handleRegister" @keyup.enter.native="handleRegister"
> >
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> <svg-icon
slot="prefix"
icon-class="validCode"
class="el-input__icon input-icon"
/>
</el-input> </el-input>
<div class="register-code"> <div class="register-code">
<img :src="codeUrl" @click="getCode" class="register-code-img"/> <img :src="codeUrl" @click="getCode" class="register-code-img" />
</div> </div>
</el-form-item> </el-form-item>
<el-form-item style="width:100%;"> <el-form-item style="width: 100%">
<el-button <el-button
:loading="loading" :loading="loading"
size="medium" size="medium"
type="primary" type="primary"
style="width:100%;" style="width: 100%"
@click.native.prevent="handleRegister" @click.native.prevent="handleRegister"
> >
<span v-if="!loading"> </span> <span v-if="!loading"> </span>
<span v-else> 中...</span> <span v-else> 中...</span>
</el-button> </el-button>
<div style="float: right;"> <div style="float: right">
<router-link class="link-type" :to="'/login'">使用已有账户登录</router-link> <router-link class="link-type" :to="'/login'"
>使用已有账户登录</router-link
>
</div> </div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 底部 --> <!-- 底部 -->
<div class="el-register-footer"> <div class="el-register-footer">
<span>Copyright © 2018-2025 上海电动工具研究所集团有限公司.</span> <span>Copyright © 2025 上动新能源 版权所有</span>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { getCodeImg, register } from "@/api/login" import { getCodeImg, register } from "@/api/login";
import intervalUpdate from "@/mixins/ems/intervalUpdate";
export default { export default {
name: "Register", name: "Register",
mixins: [intervalUpdate],
computed: {
loginBg() {
return require(`./../assets/images/ems/loginBg/${this.bgNum}.png`);
},
},
data() { data() {
const equalToPassword = (rule, value, callback) => { const equalToPassword = (rule, value, callback) => {
if (this.registerForm.password !== value) { if (this.registerForm.password !== value) {
callback(new Error("两次输入的密码不一致")) callback(new Error("两次输入的密码不一致"));
} else { } else {
callback() callback();
} }
} };
return { return {
title: process.env.VUE_APP_TITLE, bgNum: 1,
codeUrl: "", codeUrl: "",
registerForm: { registerForm: {
username: "", username: "",
password: "", password: "",
confirmPassword: "", confirmPassword: "",
code: "", code: "",
uuid: "" uuid: "",
}, },
registerRules: { registerRules: {
username: [ username: [
{ required: true, trigger: "blur", message: "请输入您的账号" }, { required: true, trigger: "blur", message: "请输入您的账号" },
{ min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur' } {
min: 2,
max: 20,
message: "用户账号长度必须介于 2 和 20 之间",
trigger: "blur",
},
], ],
password: [ password: [
{ required: true, trigger: "blur", message: "请输入您的密码" }, { required: true, trigger: "blur", message: "请输入您的密码" },
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }, {
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" } min: 5,
max: 20,
message: "用户密码长度必须介于 5 和 20 之间",
trigger: "blur",
},
{
pattern: /^[^<>"'|\\]+$/,
message: "不能包含非法字符:< > \" ' \\\ |",
trigger: "blur",
},
], ],
confirmPassword: [ confirmPassword: [
{ required: true, trigger: "blur", message: "请再次输入您的密码" }, { required: true, trigger: "blur", message: "请再次输入您的密码" },
{ required: true, validator: equalToPassword, trigger: "blur" } { required: true, validator: equalToPassword, trigger: "blur" },
], ],
code: [{ required: true, trigger: "change", message: "请输入验证码" }] code: [{ required: true, trigger: "change", message: "请输入验证码" }],
}, },
loading: false, loading: false,
captchaEnabled: true captchaEnabled: true,
} };
}, },
created() { created() {
this.getCode() this.getCode();
},
mounted() {
this.updateInterval(this.updateBgNum, 5000);
}, },
methods: { methods: {
updateBgNum() {
if (this.bgNum >= 4) this.bgNum = 0;
this.bgNum += 1;
},
getCode() { getCode() {
getCodeImg().then(res => { getCodeImg().then((res) => {
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled this.captchaEnabled =
res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (this.captchaEnabled) { if (this.captchaEnabled) {
this.codeUrl = "data:image/gif;base64," + res.img this.codeUrl = "data:image/gif;base64," + res.img;
this.registerForm.uuid = res.uuid this.registerForm.uuid = res.uuid;
} }
}) });
}, },
handleRegister() { handleRegister() {
this.$refs.registerForm.validate(valid => { this.$refs.registerForm.validate((valid) => {
if (valid) { if (valid) {
this.loading = true this.loading = true;
register(this.registerForm).then(res => { register(this.registerForm)
const username = this.registerForm.username .then((res) => {
this.$alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", '系统提示', { const username = this.registerForm.username;
dangerouslyUseHTMLString: true, this.$alert(
type: 'success' "<font color='red'>恭喜你,您的账号 " +
}).then(() => { username +
this.$router.push("/login") " 注册成功!</font>",
}).catch(() => {}) "系统提示",
}).catch(() => { {
this.loading = false dangerouslyUseHTMLString: true,
if (this.captchaEnabled) { type: "success",
this.getCode() }
} )
}) .then(() => {
this.$router.push("/login");
})
.catch(() => {});
})
.catch(() => {
this.loading = false;
if (this.captchaEnabled) {
this.getCode();
}
});
} }
}) });
} },
} },
} };
</script> </script>
<style rel="stylesheet/scss" lang="scss"> <style rel="stylesheet/scss" lang="scss">
.register { .register {
padding-left: 180px;
display: flex; display: flex;
justify-content: center; justify-content: left;
align-items: center; align-items: center;
height: 100%; height: 100%;
background-image: url("../assets/images/ems/login-background.png");
background-size: cover;
} }
.title { .login-bg {
margin: 0px auto 30px auto; position: absolute;
text-align: center; top: 0;
color: #707070; left: 0;
z-index: 1;
display: block;
height: 100%;
width: 100%;
}
.login-logo {
display: block;
width: 70%;
height: auto;
margin: 0 auto;
} }
.register-form { .register-form {
z-index: 2;
border-radius: 6px; border-radius: 6px;
background: #ffffff; background: #ffffff;
width: 400px; width: 400px;
padding: 25px 25px 5px 25px; padding: 0 25px 5px 25px;
.el-input { .el-input {
height: 38px; height: 38px;
input { input {
@ -194,6 +276,8 @@ export default {
} }
} }
.el-register-footer { .el-register-footer {
margin-left: -180px;
z-index:2;
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
position: fixed; position: fixed;

View File

@ -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>仅允许导入xlsxlsx格式文件</span> <span>仅允许导入xlsxlsx格式文件</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()
}, },
// 提交上传文件 // 提交上传文件

View File

@ -2,9 +2,7 @@
<div class="container"> <div class="container">
<div class="left-board"> <div class="left-board">
<div class="logo-wrapper"> <div class="logo-wrapper">
<div class="logo"> <div class="logo"><img :src="logo" alt="logo" /> Form Generator</div>
<img :src="logo" alt="logo"> Form Generator
</div>
</div> </div>
<el-scrollbar class="left-scrollbar"> <el-scrollbar class="left-scrollbar">
<div class="components-list"> <div class="components-list">
@ -21,7 +19,9 @@
@end="onEnd" @end="onEnd"
> >
<div <div
v-for="(element, index) in inputComponents" :key="index" class="components-item" v-for="(element, index) in inputComponents"
:key="index"
class="components-item"
@click="addComponent(element)" @click="addComponent(element)"
> >
<div class="components-body"> <div class="components-body">
@ -58,12 +58,18 @@
<svg-icon icon-class="component" /> 布局型组件 <svg-icon icon-class="component" /> 布局型组件
</div> </div>
<draggable <draggable
class="components-draggable" :list="layoutComponents" class="components-draggable"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent" :list="layoutComponents"
draggable=".components-item" :sort="false" @end="onEnd" :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
> >
<div <div
v-for="(element, index) in layoutComponents" :key="index" class="components-item" v-for="(element, index) in layoutComponents"
:key="index"
class="components-item"
@click="addComponent(element)" @click="addComponent(element)"
> >
<div class="components-body"> <div class="components-body">
@ -81,10 +87,20 @@
<el-button icon="el-icon-download" type="text" @click="download"> <el-button icon="el-icon-download" type="text" @click="download">
导出vue文件 导出vue文件
</el-button> </el-button>
<el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy"> <el-button
class="copy-btn-main"
icon="el-icon-document-copy"
type="text"
@click="copy"
>
复制代码 复制代码
</el-button> </el-button>
<el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty"> <el-button
class="delete-btn"
icon="el-icon-delete"
type="text"
@click="empty"
>
清空 清空
</el-button> </el-button>
</div> </div>
@ -96,7 +112,12 @@
:disabled="formConf.disabled" :disabled="formConf.disabled"
:label-width="formConf.labelWidth + 'px'" :label-width="formConf.labelWidth + 'px'"
> >
<draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup"> <draggable
class="drawing-board"
:list="drawingList"
:animation="340"
group="componentsGroup"
>
<draggable-item <draggable-item
v-for="(element, index) in drawingList" v-for="(element, index) in drawingList"
:key="element.renderKey" :key="element.renderKey"
@ -131,28 +152,38 @@
:show-file-name="showFileName" :show-file-name="showFileName"
@confirm="generate" @confirm="generate"
/> />
<input id="copyNode" type="hidden"> <input id="copyNode" type="hidden" />
</div> </div>
</template> </template>
<script> <script>
import draggable from 'vuedraggable' import draggable from "vuedraggable";
import beautifier from 'js-beautify' import beautifier from "js-beautify";
import ClipboardJS from 'clipboard' import ClipboardJS from "clipboard";
import render from '@/utils/generator/render' import render from "@/utils/generator/render";
import RightPanel from './RightPanel' import RightPanel from "./RightPanel";
import { inputComponents, selectComponents, layoutComponents, formConf } from '@/utils/generator/config' import {
import { beautifierConf, titleCase } from '@/utils/index' inputComponents,
import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html' selectComponents,
import { makeUpJs } from '@/utils/generator/js' layoutComponents,
import { makeUpCss } from '@/utils/generator/css' formConf,
import drawingDefault from '@/utils/generator/drawingDefault' } from "@/utils/generator/config";
import logo from '@/assets/logo/logo.png' import { beautifierConf, titleCase } from "@/utils/index";
import CodeTypeDialog from './CodeTypeDialog' import {
import DraggableItem from './DraggableItem' makeUpHtml,
vueTemplate,
vueScript,
cssStyle,
} from "@/utils/generator/html";
import { makeUpJs } from "@/utils/generator/js";
import { makeUpCss } from "@/utils/generator/css";
import drawingDefault from "@/utils/generator/drawingDefault";
import logo from "@/assets/images/ems/logo-icon.png";
import CodeTypeDialog from "./CodeTypeDialog";
import DraggableItem from "./DraggableItem";
let oldActiveId let oldActiveId;
let tempActiveData let tempActiveData;
export default { export default {
components: { components: {
@ -160,7 +191,7 @@ export default {
render, render,
RightPanel, RightPanel,
CodeTypeDialog, CodeTypeDialog,
DraggableItem DraggableItem,
}, },
data() { data() {
return { return {
@ -179,207 +210,213 @@ export default {
dialogVisible: false, dialogVisible: false,
generateConf: null, generateConf: null,
showFileName: false, showFileName: false,
activeData: drawingDefault[0] activeData: drawingDefault[0],
} };
}, },
created() { created() {
// 防止 firefox 下 拖拽 会新打卡一个选项卡 // 防止 firefox 下 拖拽 会新打卡一个选项卡
document.body.ondrop = event => { document.body.ondrop = (event) => {
event.preventDefault() event.preventDefault();
event.stopPropagation() event.stopPropagation();
} };
}, },
watch: { watch: {
'activeData.label': function (val, oldVal) { "activeData.label": function (val, oldVal) {
if ( if (
this.activeData.placeholder === undefined this.activeData.placeholder === undefined ||
|| !this.activeData.tag !this.activeData.tag ||
|| oldActiveId !== this.activeId oldActiveId !== this.activeId
) { ) {
return return;
} }
this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val this.activeData.placeholder =
this.activeData.placeholder.replace(oldVal, "") + val;
}, },
activeId: { activeId: {
handler(val) { handler(val) {
oldActiveId = val oldActiveId = val;
}, },
immediate: true immediate: true,
} },
}, },
mounted() { mounted() {
const clipboard = new ClipboardJS('#copyNode', { const clipboard = new ClipboardJS("#copyNode", {
text: trigger => { text: (trigger) => {
const codeStr = this.generateCode() const codeStr = this.generateCode();
this.$notify({ this.$notify({
title: '成功', title: "成功",
message: '代码已复制到剪切板,可粘贴。', message: "代码已复制到剪切板,可粘贴。",
type: 'success' type: "success",
}) });
return codeStr return codeStr;
} },
}) });
clipboard.on('error', e => { clipboard.on("error", (e) => {
this.$message.error('代码复制失败') this.$message.error("代码复制失败");
}) });
}, },
methods: { methods: {
activeFormItem(element) { activeFormItem(element) {
this.activeData = element this.activeData = element;
this.activeId = element.formId this.activeId = element.formId;
}, },
onEnd(obj, a) { onEnd(obj, a) {
if (obj.from !== obj.to) { if (obj.from !== obj.to) {
this.activeData = tempActiveData this.activeData = tempActiveData;
this.activeId = this.idGlobal this.activeId = this.idGlobal;
} }
}, },
addComponent(item) { addComponent(item) {
const clone = this.cloneComponent(item) const clone = this.cloneComponent(item);
this.drawingList.push(clone) this.drawingList.push(clone);
this.activeFormItem(clone) this.activeFormItem(clone);
}, },
cloneComponent(origin) { cloneComponent(origin) {
const clone = JSON.parse(JSON.stringify(origin)) const clone = JSON.parse(JSON.stringify(origin));
clone.formId = ++this.idGlobal clone.formId = ++this.idGlobal;
clone.span = formConf.span clone.span = formConf.span;
clone.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件 clone.renderKey = +new Date(); // 改变renderKey后可以实现强制更新组件
if (!clone.layout) clone.layout = 'colFormItem' if (!clone.layout) clone.layout = "colFormItem";
if (clone.layout === 'colFormItem') { if (clone.layout === "colFormItem") {
clone.vModel = `field${this.idGlobal}` clone.vModel = `field${this.idGlobal}`;
clone.placeholder !== undefined && (clone.placeholder += clone.label) clone.placeholder !== undefined && (clone.placeholder += clone.label);
tempActiveData = clone tempActiveData = clone;
} else if (clone.layout === 'rowFormItem') { } else if (clone.layout === "rowFormItem") {
delete clone.label delete clone.label;
clone.componentName = `row${this.idGlobal}` clone.componentName = `row${this.idGlobal}`;
clone.gutter = this.formConf.gutter clone.gutter = this.formConf.gutter;
tempActiveData = clone tempActiveData = clone;
} }
return tempActiveData return tempActiveData;
}, },
AssembleFormData() { AssembleFormData() {
this.formData = { this.formData = {
fields: JSON.parse(JSON.stringify(this.drawingList)), fields: JSON.parse(JSON.stringify(this.drawingList)),
...this.formConf ...this.formConf,
} };
}, },
generate(data) { generate(data) {
const func = this[`exec${titleCase(this.operationType)}`] const func = this[`exec${titleCase(this.operationType)}`];
this.generateConf = data this.generateConf = data;
func && func(data) func && func(data);
}, },
execRun(data) { execRun(data) {
this.AssembleFormData() this.AssembleFormData();
this.drawerVisible = true this.drawerVisible = true;
}, },
execDownload(data) { execDownload(data) {
const codeStr = this.generateCode() const codeStr = this.generateCode();
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' }) const blob = new Blob([codeStr], { type: "text/plain;charset=utf-8" });
this.$download.saveAs(blob, data.fileName) this.$download.saveAs(blob, data.fileName);
}, },
execCopy(data) { execCopy(data) {
document.getElementById('copyNode').click() document.getElementById("copyNode").click();
}, },
empty() { empty() {
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then( this.$confirm("确定要清空所有组件吗?", "提示", { type: "warning" }).then(
() => { () => {
this.drawingList = [] this.drawingList = [];
} }
) );
}, },
drawingItemCopy(item, parent) { drawingItemCopy(item, parent) {
let clone = JSON.parse(JSON.stringify(item)) let clone = JSON.parse(JSON.stringify(item));
clone = this.createIdAndKey(clone) clone = this.createIdAndKey(clone);
parent.push(clone) parent.push(clone);
this.activeFormItem(clone) this.activeFormItem(clone);
}, },
createIdAndKey(item) { createIdAndKey(item) {
item.formId = ++this.idGlobal item.formId = ++this.idGlobal;
item.renderKey = +new Date() item.renderKey = +new Date();
if (item.layout === 'colFormItem') { if (item.layout === "colFormItem") {
item.vModel = `field${this.idGlobal}` item.vModel = `field${this.idGlobal}`;
} else if (item.layout === 'rowFormItem') { } else if (item.layout === "rowFormItem") {
item.componentName = `row${this.idGlobal}` item.componentName = `row${this.idGlobal}`;
} }
if (Array.isArray(item.children)) { if (Array.isArray(item.children)) {
item.children = item.children.map(childItem => this.createIdAndKey(childItem)) item.children = item.children.map((childItem) =>
this.createIdAndKey(childItem)
);
} }
return item return item;
}, },
drawingItemDelete(index, parent) { drawingItemDelete(index, parent) {
parent.splice(index, 1) parent.splice(index, 1);
this.$nextTick(() => { this.$nextTick(() => {
const len = this.drawingList.length const len = this.drawingList.length;
if (len) { if (len) {
this.activeFormItem(this.drawingList[len - 1]) this.activeFormItem(this.drawingList[len - 1]);
} }
}) });
}, },
generateCode() { generateCode() {
const { type } = this.generateConf const { type } = this.generateConf;
this.AssembleFormData() this.AssembleFormData();
const script = vueScript(makeUpJs(this.formData, type)) const script = vueScript(makeUpJs(this.formData, type));
const html = vueTemplate(makeUpHtml(this.formData, type)) const html = vueTemplate(makeUpHtml(this.formData, type));
const css = cssStyle(makeUpCss(this.formData)) const css = cssStyle(makeUpCss(this.formData));
return beautifier.html(html + script + css, beautifierConf.html) return beautifier.html(html + script + css, beautifierConf.html);
}, },
download() { download() {
this.dialogVisible = true this.dialogVisible = true;
this.showFileName = true this.showFileName = true;
this.operationType = 'download' this.operationType = "download";
}, },
run() { run() {
this.dialogVisible = true this.dialogVisible = true;
this.showFileName = false this.showFileName = false;
this.operationType = 'run' this.operationType = "run";
}, },
copy() { copy() {
this.dialogVisible = true this.dialogVisible = true;
this.showFileName = false this.showFileName = false;
this.operationType = 'copy' this.operationType = "copy";
}, },
tagChange(newTag) { tagChange(newTag) {
newTag = this.cloneComponent(newTag) newTag = this.cloneComponent(newTag);
newTag.vModel = this.activeData.vModel newTag.vModel = this.activeData.vModel;
newTag.formId = this.activeId newTag.formId = this.activeId;
newTag.span = this.activeData.span newTag.span = this.activeData.span;
delete this.activeData.tag delete this.activeData.tag;
delete this.activeData.tagIcon delete this.activeData.tagIcon;
delete this.activeData.document delete this.activeData.document;
Object.keys(newTag).forEach(key => { Object.keys(newTag).forEach((key) => {
if (this.activeData[key] !== undefined if (
&& typeof this.activeData[key] === typeof newTag[key]) { this.activeData[key] !== undefined &&
newTag[key] = this.activeData[key] typeof this.activeData[key] === typeof newTag[key]
) {
newTag[key] = this.activeData[key];
} }
}) });
this.activeData = newTag this.activeData = newTag;
this.updateDrawingList(newTag, this.drawingList) this.updateDrawingList(newTag, this.drawingList);
}, },
updateDrawingList(newTag, list) { updateDrawingList(newTag, list) {
const index = list.findIndex(item => item.formId === this.activeId) const index = list.findIndex((item) => item.formId === this.activeId);
if (index > -1) { if (index > -1) {
list.splice(index, 1, newTag) list.splice(index, 1, newTag);
} else { } else {
list.forEach(item => { list.forEach((item) => {
if (Array.isArray(item.children)) this.updateDrawingList(newTag, item.children) if (Array.isArray(item.children))
}) this.updateDrawingList(newTag, item.children);
});
} }
} },
} },
} };
</script> </script>
<style lang='scss'> <style lang="scss">
.editor-tabs{ .editor-tabs {
background: #121315; background: #121315;
.el-tabs__header{ .el-tabs__header {
margin: 0; margin: 0;
border-bottom-color: #121315; border-bottom-color: #121315;
.el-tabs__nav{ .el-tabs__nav {
border-color: #121315; border-color: #121315;
} }
} }
.el-tabs__item{ .el-tabs__item {
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
color: #888a8e; color: #888a8e;
@ -388,15 +425,15 @@ export default {
margin-right: 5px; margin-right: 5px;
user-select: none; user-select: none;
} }
.el-tabs__item.is-active{ .el-tabs__item.is-active {
background: #1e1e1e; background: #1e1e1e;
border-bottom-color: #1e1e1e!important; border-bottom-color: #1e1e1e !important;
color: #fff; color: #fff;
} }
.el-icon-edit{ .el-icon-edit {
color: #f1fa8c; color: #f1fa8c;
} }
.el-icon-document{ .el-icon-document {
color: #a95812; color: #a95812;
} }
} }
@ -412,24 +449,24 @@ export default {
overflow-x: hidden !important; overflow-x: hidden !important;
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.center-tabs{ .center-tabs {
.el-tabs__header{ .el-tabs__header {
margin-bottom: 0!important; margin-bottom: 0 !important;
} }
.el-tabs__item{ .el-tabs__item {
width: 50%; width: 50%;
text-align: center; text-align: center;
} }
.el-tabs__nav{ .el-tabs__nav {
width: 100%; width: 100%;
} }
} }
.reg-item{ .reg-item {
padding: 12px 6px; padding: 12px 6px;
background: #f8f8f8; background: #f8f8f8;
position: relative; position: relative;
border-radius: 4px; border-radius: 4px;
.close-btn{ .close-btn {
position: absolute; position: absolute;
right: -6px; right: -6px;
top: -6px; top: -6px;
@ -444,16 +481,16 @@ export default {
z-index: 1; z-index: 1;
cursor: pointer; cursor: pointer;
font-size: 12px; font-size: 12px;
&:hover{ &:hover {
background: rgba(210, 23, 23, 0.5) background: rgba(210, 23, 23, 0.5);
} }
} }
& + .reg-item{ & + .reg-item {
margin-top: 18px; margin-top: 18px;
} }
} }
.action-bar{ .action-bar {
& .el-button+.el-button { & .el-button + .el-button {
margin-left: 15px; margin-left: 15px;
} }
& i { & i {
@ -464,37 +501,37 @@ export default {
} }
} }
.custom-tree-node{ .custom-tree-node {
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
.node-operation{ .node-operation {
float: right; float: right;
} }
i[class*="el-icon"] + i[class*="el-icon"]{ i[class*="el-icon"] + i[class*="el-icon"] {
margin-left: 6px; margin-left: 6px;
} }
.el-icon-plus{ .el-icon-plus {
color: #409EFF; color: #409eff;
} }
.el-icon-delete{ .el-icon-delete {
color: #157a0c; color: #157a0c;
} }
} }
.left-scrollbar .el-scrollbar__view{ .left-scrollbar .el-scrollbar__view {
overflow-x: hidden; overflow-x: hidden;
} }
.el-rate{ .el-rate {
display: inline-block; display: inline-block;
vertical-align: text-top; vertical-align: text-top;
} }
.el-upload__tip{ .el-upload__tip {
line-height: 1.2; line-height: 1.2;
} }
$selectedColor: #f6f7ff; $selectedColor: #f6f7ff;
$lighterBlue: #409EFF; $lighterBlue: #409eff;
.container { .container {
position: relative; position: relative;
@ -513,14 +550,14 @@ $lighterBlue: #409EFF;
transition: transform 0ms !important; transition: transform 0ms !important;
} }
} }
.components-draggable{ .components-draggable {
padding-bottom: 20px; padding-bottom: 20px;
} }
.components-title{ .components-title {
font-size: 14px; font-size: 14px;
color: #222; color: #222;
margin: 6px 2px; margin: 6px 2px;
.svg-icon{ .svg-icon {
color: #666; color: #666;
font-size: 18px; font-size: 18px;
} }
@ -533,7 +570,7 @@ $lighterBlue: #409EFF;
cursor: move; cursor: move;
border: 1px dashed $selectedColor; border: 1px dashed $selectedColor;
border-radius: 3px; border-radius: 3px;
.svg-icon{ .svg-icon {
color: #777; color: #777;
font-size: 15px; font-size: 15px;
} }
@ -553,7 +590,7 @@ $lighterBlue: #409EFF;
top: 0; top: 0;
height: 100vh; height: 100vh;
} }
.left-scrollbar{ .left-scrollbar {
height: calc(100vh - 42px); height: calc(100vh - 42px);
overflow: hidden; overflow: hidden;
} }
@ -570,7 +607,7 @@ $lighterBlue: #409EFF;
margin: 0 350px 0 260px; margin: 0 350px 0 260px;
box-sizing: border-box; box-sizing: border-box;
} }
.empty-info{ .empty-info {
position: absolute; position: absolute;
top: 46%; top: 46%;
left: 0; left: 0;
@ -580,27 +617,27 @@ $lighterBlue: #409EFF;
color: #ccb1ea; color: #ccb1ea;
letter-spacing: 4px; letter-spacing: 4px;
} }
.action-bar{ .action-bar {
position: relative; position: relative;
height: 42px; height: 42px;
text-align: right; text-align: right;
padding: 0 15px; padding: 0 15px;
box-sizing: border-box;; box-sizing: border-box;
border: 1px solid #f1e8e8; border: 1px solid #f1e8e8;
border-top: none; border-top: none;
border-left: none; border-left: none;
.delete-btn{ .delete-btn {
color: #F56C6C; color: #f56c6c;
} }
} }
.logo-wrapper{ .logo-wrapper {
position: relative; position: relative;
height: 42px; height: 42px;
background: #fff; background: #fff;
border-bottom: 1px solid #f1e8e8; border-bottom: 1px solid #f1e8e8;
box-sizing: border-box; box-sizing: border-box;
} }
.logo{ .logo {
position: absolute; position: absolute;
left: 12px; left: 12px;
top: 6px; top: 6px;
@ -609,16 +646,16 @@ $lighterBlue: #409EFF;
font-weight: 600; font-weight: 600;
font-size: 17px; font-size: 17px;
white-space: nowrap; white-space: nowrap;
> img{ > img {
width: 30px; width: 30px;
height: 30px; height: 30px;
vertical-align: top; vertical-align: top;
} }
.github{ .github {
display: inline-block; display: inline-block;
vertical-align: sub; vertical-align: sub;
margin-left: 15px; margin-left: 15px;
> img{ > img {
height: 22px; height: 22px;
} }
} }
@ -661,32 +698,33 @@ $lighterBlue: #409EFF;
background-color: $selectedColor; background-color: $selectedColor;
} }
.active-from-item { .active-from-item {
& > .el-form-item{ & > .el-form-item {
background: $selectedColor; background: $selectedColor;
border-radius: 6px; border-radius: 6px;
} }
& > .drawing-item-copy, & > .drawing-item-delete{ & > .drawing-item-copy,
& > .drawing-item-delete {
display: initial; display: initial;
} }
& > .component-name{ & > .component-name {
color: $lighterBlue; color: $lighterBlue;
} }
} }
.el-form-item{ .el-form-item {
margin-bottom: 15px; margin-bottom: 15px;
} }
} }
.drawing-item{ .drawing-item {
position: relative; position: relative;
cursor: move; cursor: move;
&.unfocus-bordered:not(.activeFromItem) > div:first-child { &.unfocus-bordered:not(.activeFromItem) > div:first-child {
border: 1px dashed #ccc; border: 1px dashed #ccc;
} }
.el-form-item{ .el-form-item {
padding: 12px 10px; padding: 12px 10px;
} }
} }
.drawing-row-item{ .drawing-row-item {
position: relative; position: relative;
cursor: move; cursor: move;
box-sizing: border-box; box-sizing: border-box;
@ -697,19 +735,19 @@ $lighterBlue: #409EFF;
.drawing-row-item { .drawing-row-item {
margin-bottom: 2px; margin-bottom: 2px;
} }
.el-col{ .el-col {
margin-top: 22px; margin-top: 22px;
} }
.el-form-item{ .el-form-item {
margin-bottom: 0; margin-bottom: 0;
} }
.drag-wrapper{ .drag-wrapper {
min-height: 80px; min-height: 80px;
} }
&.active-from-item{ &.active-from-item {
border: 1px dashed $lighterBlue; border: 1px dashed $lighterBlue;
} }
.component-name{ .component-name {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -719,17 +757,20 @@ $lighterBlue: #409EFF;
padding: 0 6px; padding: 0 6px;
} }
} }
.drawing-item, .drawing-row-item{ .drawing-item,
.drawing-row-item {
&:hover { &:hover {
& > .el-form-item{ & > .el-form-item {
background: $selectedColor; background: $selectedColor;
border-radius: 6px; border-radius: 6px;
} }
& > .drawing-item-copy, & > .drawing-item-delete{ & > .drawing-item-copy,
& > .drawing-item-delete {
display: initial; display: initial;
} }
} }
& > .drawing-item-copy, & > .drawing-item-delete{ & > .drawing-item-copy,
& > .drawing-item-delete {
display: none; display: none;
position: absolute; position: absolute;
top: -10px; top: -10px;
@ -743,23 +784,23 @@ $lighterBlue: #409EFF;
cursor: pointer; cursor: pointer;
z-index: 1; z-index: 1;
} }
& > .drawing-item-copy{ & > .drawing-item-copy {
right: 56px; right: 56px;
border-color: $lighterBlue; border-color: $lighterBlue;
color: $lighterBlue; color: $lighterBlue;
background: #fff; background: #fff;
&:hover{ &:hover {
background: $lighterBlue; background: $lighterBlue;
color: #fff; color: #fff;
} }
} }
& > .drawing-item-delete{ & > .drawing-item-delete {
right: 24px; right: 24px;
border-color: #F56C6C; border-color: #f56c6c;
color: #F56C6C; color: #f56c6c;
background: #fff; background: #fff;
&:hover{ &:hover {
background: #F56C6C; background: #f56c6c;
color: #fff; color: #fff;
} }
} }

View File

@ -9,8 +9,6 @@ const CompressionPlugin = require('compression-webpack-plugin')
const name = process.env.VUE_APP_TITLE || 'EMS管理系统' // 网页标题 const name = process.env.VUE_APP_TITLE || 'EMS管理系统' // 网页标题
const baseUrl = 'http://110.40.171.179:8089' // 后端接口
const port = process.env.port || process.env.npm_config_port || 80 // 端口 const port = process.env.port || process.env.npm_config_port || 80 // 端口
// vue.config.js 配置说明 // vue.config.js 配置说明
@ -34,19 +32,27 @@ module.exports = {
port: port, port: port,
open: true, open: true,
proxy: { proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy // // detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: { // [process.env.VUE_APP_BASE_API]: {
target: baseUrl, // target: baseUrl,
// changeOrigin: true,
// pathRewrite: {
// ['^' + process.env.VUE_APP_BASE_API]: ''
// }
// },
// // springdoc proxy
// '^/v3/api-docs/(.*)': {
// target: baseUrl,
// changeOrigin: true
// }
// 当请求前缀是/dev-api时,使用下面的代理
'/dev-api': {
target: 'http://110.40.171.179:8089',
changeOrigin: true, changeOrigin: true,
pathRewrite: { pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: '' '^/dev-api': ''
} }
}, },
// springdoc proxy
'^/v3/api-docs/(.*)': {
target: baseUrl,
changeOrigin: true
}
}, },
disableHostCheck: true disableHostCheck: true
}, },