91 Commits

Author SHA1 Message Date
guanj
c2f23aa957 修改测试问题 2026-05-28 20:36:49 +08:00
guanj
9466141bff 去除部门树接口 2026-05-28 15:10:40 +08:00
guanj
faac12615d 提交 2026-05-26 16:23:18 +08:00
guanj
8b80e0678f 提交代码 2026-04-25 15:22:28 +08:00
guanj
7abcdb3a6b 联调程序升级 2026-04-24 09:13:48 +08:00
guanj
c8a42948de 修改台账 2026-04-20 09:28:04 +08:00
guanj
99bc99a6fc 绘制稳态事件配置页面 2026-04-17 08:49:22 +08:00
guanj
01a28d88f3 修改台账 2026-04-15 19:29:36 +08:00
guanj
632a0104fb 调整台账录入页面 2026-04-15 13:44:28 +08:00
guanj
cfcbfc45d6 修改台账 2026-04-13 10:48:32 +08:00
guanj
2601068a55 1 2026-04-08 15:51:46 +08:00
guanj
3ffb11defa 调整台账 2026-04-03 14:47:36 +08:00
guanj
0b9aafc1b5 联调文件管理页面 2026-04-02 09:08:57 +08:00
guanj
762965b1e4 联调设备文件 2026-03-30 09:03:53 +08:00
guanj
a30379ab01 绘制 运维版本管理页面 2026-03-19 11:29:26 +08:00
dk
9f1fbf93cd 新增实时运维页面 2026-03-18 21:06:48 +08:00
dk
b5fc946ce2 测试修改提交 2026-03-17 14:32:14 +08:00
guanj
1171d37a86 添加工程树 2026-03-06 09:36:42 +08:00
guanj
3fdb41c468 修改测试bug 2026-02-04 09:35:24 +08:00
guanj
dd0dab7643 添加工程信息管理 页面 2026-02-02 13:56:50 +08:00
guanj
cf4291ed9a 修改报表 2026-01-28 10:16:05 +08:00
guanj
46124f0ea5 修改全局报表功能 2026-01-27 16:32:33 +08:00
guanj
def48e9c84 修改测试bug 2026-01-23 09:24:13 +08:00
guanj
823d5f4475 修改问题 2026-01-20 14:39:13 +08:00
guanj
c09e6f54dd 修改测试问题 2026-01-16 15:54:16 +08:00
guanj
5ceb9be9e2 修改测试问题 2026-01-15 15:59:13 +08:00
guanj
054d84778b 优化表格 2026-01-14 13:30:23 +08:00
guanj
63433aa6dc 云平台自测问题修改 2026-01-13 14:27:23 +08:00
guanj
e9d7231a75 修改测试用例1 2026-01-12 11:06:54 +08:00
guanj
08afdddc51 修改数据来源 2026-01-08 20:08:26 +08:00
guanj
e21ae50e51 修改数据来源 2026-01-08 19:51:43 +08:00
guanj
4cbd2e43cb 修改告警级别 2026-01-08 19:20:32 +08:00
guanj
4c9b677e81 修改监测点列表 2026-01-08 14:09:43 +08:00
guanj
0affb17e3a 修改监测列表页面 2026-01-08 13:48:40 +08:00
guanj
c2d0faea08 修改在线设备 2026-01-08 11:33:40 +08:00
guanj
0d155c8680 优化页面 2026-01-08 11:32:01 +08:00
guanj
3db01fe32d 修改驾驶舱组件重复绑定问题 2026-01-08 10:08:51 +08:00
guanj
545e3836d1 修改测试问题 2026-01-07 21:01:28 +08:00
guanj
02a95c1dcd 修改测试bug,优化页面 2026-01-07 13:14:26 +08:00
guanj
7a81c008c3 修改组件页面 2026-01-06 15:42:33 +08:00
guanj
5d3d16f8ec 修改测试bug 2026-01-06 11:35:11 +08:00
guanj
d25f16bcc7 添加系统绑的功能 2026-01-05 16:34:42 +08:00
guanj
75987c0c6f 修改测试问题 2026-01-05 11:31:50 +08:00
guanj
a765cdf9ee 修改测试bug 优化页面 2026-01-04 14:55:31 +08:00
guanj
cc0f8bc8b6 优化驾驶舱页面 2025-12-20 23:44:46 +08:00
guanj
7e4db9d4cd 修改菜单 2025-12-17 17:41:35 +08:00
sjl
67e2fa57d0 在线设备录入校验 2025-12-17 16:47:11 +08:00
stt
ad1fc11e61 删除打印 2025-12-10 13:33:06 +08:00
stt
6824864db2 没有波形图的时候显示暂无波形 2025-12-10 13:21:48 +08:00
stt
37ed693cea 实时数据页面样式修改 2025-12-10 10:30:32 +08:00
stt
0419af8e50 指标越限程度宽度修改 2025-12-09 15:21:42 +08:00
stt
5268b93dd0 监测点管理页面 2025-12-09 14:56:33 +08:00
guanj
4e6bd55089 修改报表样式 2025-12-09 13:58:37 +08:00
stt
4e0db29ab1 复位按钮隐藏 2025-12-09 11:36:52 +08:00
stt
9b0fd76f48 修改"下一个"按钮的状态 2025-12-08 15:23:38 +08:00
stt
f92b07c555 修改"下一个"按钮的状态 2025-12-08 14:21:27 +08:00
guanj
a77db278ac 修改驾驶舱时间问题 2025-12-08 13:30:46 +08:00
stt
51a0ae49a9 zoom缩放导致echarts偏移问题 2025-12-08 11:39:39 +08:00
stt
814e9917d6 弹框显示越限和不越限,不显示数值 2025-12-08 10:55:24 +08:00
stt
21f1c41196 宽度调整 2025-12-08 10:35:31 +08:00
stt
c188446e76 样式修改 2025-12-08 10:32:11 +08:00
stt
e2a5d084a5 下拉框添加filterable属性 2025-12-08 09:42:08 +08:00
stt
4ae27a9d6d 所以下拉框加filterable属性 2025-12-08 09:31:16 +08:00
stt
7783569f91 不越限样式调整 2025-12-08 09:01:00 +08:00
stt
f1ac67070f 指标越限明显样式调整 2025-12-08 08:55:36 +08:00
stt
77a9a2adfc Merge branch 'main' of http://192.168.1.22:3000/Web/admin-govern 2025-12-08 08:54:48 +08:00
stt
accc1f30f6 暂态事件样式调整 2025-12-08 08:54:44 +08:00
guanj
94649b3348 微调 2025-12-08 08:37:07 +08:00
stt
e3de350dc5 指标拟合图y轴展示修改 2025-12-05 16:18:48 +08:00
stt
4963dd495a 样式修改 2025-12-05 16:06:39 +08:00
stt
40fa6eba20 暂降方向统计页面联调 2025-12-05 14:55:32 +08:00
stt
f32934e0e6 Merge branch 'main' of http://192.168.1.22:3000/Web/admin-govern 2025-12-05 11:05:08 +08:00
stt
460962cead 删除多余代码 2025-12-05 11:05:05 +08:00
guanj
fa75fc2923 联调实时数据 2025-12-05 10:44:35 +08:00
stt
f953b560c7 暂态电能质量分析时间修改 2025-12-04 20:27:05 +08:00
stt
8f3426eb1f 稳态治理效果分析时间修改 2025-12-04 20:08:37 +08:00
stt
c2a2a4afd6 稳态电能质量分析时间修改 2025-12-04 19:47:31 +08:00
stt
d2357d4ad2 稳态电能质量分析时间修改 2025-12-04 19:11:21 +08:00
stt
1b23355134 实时数据页面提交 2025-12-04 16:29:46 +08:00
guanj
ce9caa8729 Merge branch 'main' of http://192.168.1.22:3000/Web/admin-govern 2025-12-04 15:25:31 +08:00
guanj
2d0349c1b6 微调 2025-12-04 15:25:22 +08:00
stt
8355fc6aed Merge branch 'main' of http://192.168.1.22:3000/Web/admin-govern 2025-12-04 14:51:25 +08:00
stt
23bc2d8f05 组件查询时间加必填校验 2025-12-04 14:51:21 +08:00
guanj
43caddffa3 修改 echart样式 2025-12-04 10:33:48 +08:00
stt
3accaf3079 日期下拉默认修改 2025-12-04 10:30:19 +08:00
guanj
5687367602 Merge branch 'main' of http://192.168.1.22:3000/Web/admin-govern 2025-12-04 09:37:58 +08:00
guanj
b8ee530557 修改驾驶舱zoom缩放问题 2025-12-04 09:37:38 +08:00
stt
0518127792 公共时间修改 2025-12-03 16:30:42 +08:00
guanj
5db43cd4b1 微调 2025-12-03 15:37:08 +08:00
guanj
bf0657cbbc 在线设备录入添加参数
修改组件管理时间线配置
2025-12-03 14:56:57 +08:00
stt
bcb1535d4d 日历只月的时候调接口 2025-12-03 13:26:03 +08:00
217 changed files with 27817 additions and 18566 deletions

2
.gitignore vendored
View File

@@ -23,3 +23,5 @@ dist-ssr
*.sln *.sln
*.sw? *.sw?
pnpm-lock.yaml pnpm-lock.yaml
#test

View File

@@ -0,0 +1,42 @@
import createAxios from '@/utils/request'
/**
* 删除数据合理范围
**/
export const pqDelete = (data: any) => {
return createAxios({
url: '/algorithm-boot/pqReasonableRange/delete',
method: 'post',
params: data
})
}
/**
* 按条件获取数据合理范围
**/
export const getData = (data: any) => {
return createAxios({
url: '/algorithm-boot/pqReasonableRange/getData',
method: 'post',
data
})
}
/**
* 新增数据合理范围
**/
export const save = (data: any) => {
return createAxios({
url: '/algorithm-boot/pqReasonableRange/save',
method: 'post',
data
})
}
/**
* 更新数据合理范围
**/
export const update = (data: any) => {
return createAxios({
url: '/algorithm-boot/pqReasonableRange/update',
method: 'post',
data
})
}

View File

@@ -1,144 +1,152 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 装置基础数据和模板数据 // 设备基础数据和模板数据
export function getDeviceData(deviceId: string, type: string, lineId: string) { export function getDeviceData(deviceId: string, type: string, lineId: string) {
let form = new FormData() let form = new FormData()
form.append('deviceId', deviceId) form.append('deviceId', deviceId)
form.append('lineId', lineId) form.append('lineId', lineId)
form.append('type', type) form.append('type', type)
return createAxios({ return createAxios({
url: '/cs-device-boot/EquipmentDelivery/deviceData', url: '/cs-device-boot/EquipmentDelivery/deviceData',
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}, },
data: form data: form
}) })
} }
//获取趋势数据、暂态数据、实时数据 //获取趋势数据、暂态数据、实时数据
export function getTabsDataByType(data: any) { export function getTabsDataByType(data: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/deviceDataByType', url: '/cs-device-boot/csGroup/deviceDataByType',
method: 'POST', method: 'POST',
data data
}) })
} }
/**** 获取基础实施数据 ****/ /**** 获取基础实施数据 ****/
export function getBasicRealData(id: any) { export function getBasicRealData(id: any) {
return createAxios({ return createAxios({
url: `/cs-harmonic-boot/realData/getBaseRealData?lineId=${id}`, url: `/cs-harmonic-boot/realData/getBaseRealData?lineId=${id}`,
method: 'POST' method: 'POST'
}) })
} }
/**** 获取谐波实时数据 ****/ /**** 获取谐波实时数据 ****/
export function getHarmRealData(id: any, target: any) { export function getHarmRealData(id: any, target: any) {
return createAxios({ return createAxios({
url: `/cs-harmonic-boot/realData/getHarmRealData?lineId=${id}&target=${target}`, url: `/cs-harmonic-boot/realData/getHarmRealData?lineId=${id}&target=${target}`,
method: 'POST' method: 'POST'
}) })
} }
/**** 获取国标限值 ****/ /**** 获取国标限值 ****/
export function getOverLimitData(id: any) { export function getOverLimitData(id: any) {
return createAxios({ return createAxios({
url: `/cs-device-boot/csline/getOverLimitData?id=${id}`, url: `/cs-device-boot/csline/getOverLimitData?id=${id}`,
method: 'POST' method: 'POST'
}) })
} }
//获取实时数据列表数据 //获取实时数据列表数据
export function getRealTimeTableList() { export function getRealTimeTableList() {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/getGroupPortableStatistical', url: '/cs-device-boot/csGroup/getGroupPortableStatistical',
method: 'GET' method: 'GET'
}) })
} }
//离线数据导入 //离线数据导入
export function uploadOffLineDataFile(data: any) { export function uploadOffLineDataFile(data: any) {
return createAxios({ return createAxios({
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
}, },
url: '/cs-device-boot/portableOfflLog/importEquipment', url: '/cs-device-boot/portableOfflLog/importEquipment',
method: 'POST', method: 'POST',
data data
}) })
} }
//查询实时数据中实时趋势中指标分组 //查询实时数据中实时趋势中指标分组
export function getDeviceTrendDataGroup() { export function getDeviceTrendDataGroup() {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/getDeviceTrendDataGroup', url: '/cs-device-boot/csGroup/getDeviceTrendDataGroup',
method: 'GET' method: 'GET'
}) })
} }
//根据指标分组查询实时数据中实时趋势 //根据指标分组查询实时数据中实时趋势
export function getDeviceTrendData(query: any) { export function getDeviceTrendData(query: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/getDeviceTrendData', url: '/cs-device-boot/csGroup/getDeviceTrendData',
method: 'GET', method: 'GET',
params: query params: query
}) })
} }
//查询实时数据-谐波频谱-稳态指标 //查询实时数据-谐波频谱-稳态指标
export function getGroupPortableStatistical() { export function getGroupPortableStatistical() {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/getGroupPortableStatistical', url: '/cs-device-boot/csGroup/getGroupPortableStatistical',
method: 'GET' method: 'GET'
}) })
} }
//查询实时数据-谐波频谱 //查询实时数据-谐波频谱
export function getDeviceHarmonicSpectrumData(data: any) { export function getDeviceHarmonicSpectrumData(data: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/getDeviceHarmonicSpectrumData', url: '/cs-device-boot/csGroup/getDeviceHarmonicSpectrumData',
method: 'POST', method: 'POST',
data: data data: data
}) })
} }
//获取指标类型-谐波频谱 //获取指标类型-谐波频谱
export function queryDictType(data?: any) { export function queryDictType(data?: any) {
return createAxios({ return createAxios({
url: '/system-boot/dictTree/queryDictType', url: '/system-boot/dictTree/queryDictType',
method: 'GET', method: 'GET',
params: data params: data
}) })
} }
//根据监测点id获取监测点详情 //根据监测点id获取监测点详情
export function getById(data?: any) { export function getById(data?: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csline/getById', url: '/cs-device-boot/csline/getById',
method: 'POST', method: 'POST',
params: data params: data
}) })
} }
//测试项日志修改 //测试项日志修改
export function updateRecordData(data?: any) { export function updateRecordData(data?: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/wlRecord/updateRecordData', url: '/cs-device-boot/wlRecord/updateRecordData',
method: 'POST', method: 'POST',
data data
}) })
} }
//模块数据 //模块数据
export function allModelData(data?: any) { export function allModelData(data?: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/data/allModelData', url: '/cs-harmonic-boot/data/allModelData',
method: 'POST', method: 'POST',
data data
}) })
} }
//刷新状态 //刷新状态
export function getModuleState(data?: any) { export function getModuleState(data?: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/data/getModuleState', url: '/cs-harmonic-boot/data/getModuleState',
method: 'POST', method: 'POST',
params: data params: data
}) })
} }
//获取运行取数
export function getRawData(data?: any) {
return createAxios({
url: '/cs-device-boot/pqsCommunicate/getRawData',
method: 'POST',
data
})
}

View File

@@ -1,4 +1,4 @@
import createAxios from "@/utils/request"; import createAxios from '@/utils/request'
//根据Id获取台账信息 //根据Id获取台账信息
export function getInfoById(id: any) { export function getInfoById(id: any) {
@@ -11,7 +11,6 @@ export function getInfoById(id: any) {
}) })
} }
//工程查询通过id获取 //工程查询通过id获取
export function getEngineerById(id: any) { export function getEngineerById(id: any) {
let form = new FormData() let form = new FormData()
@@ -23,7 +22,6 @@ export function getEngineerById(id: any) {
}) })
} }
//项目查询通过id获取 //项目查询通过id获取
export function getProjectById(id: any) { export function getProjectById(id: any) {
let form = new FormData() let form = new FormData()
@@ -53,7 +51,7 @@ export function getById(id: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csline/getById', url: '/cs-device-boot/csline/getById',
method: 'POST', method: 'POST',
data: form data: form
}) })
} }
@@ -75,13 +73,15 @@ export function addLedger(data: any) {
} }
//修改-删除项目 //修改-删除项目
export function deleteProject(id: any,name:any,area:any,description:any,status:any) { export function deleteProject(id: any, name: any, area: any, description: any, status: any, sort: any, topoIds: any) {
let form = new FormData() let form = new FormData()
form.append('id', id) form.append('id', id)
form.append('name', name) form.append('name', name)
form.append('area', area) form.append('area', area)
form.append('description', description) form.append('description', description)
form.append('status', status) form.append('status', status)
form.append('sort', sort)
form.append('topoIds', topoIds)
return createAxios({ return createAxios({
url: '/cs-device-boot/project/auditAppProject', url: '/cs-device-boot/project/auditAppProject',
method: 'post', method: 'post',
@@ -105,7 +105,7 @@ export const deleteLine = (id: any) => {
let form = new FormData() let form = new FormData()
form.append('id', id) form.append('id', id)
return createAxios({ return createAxios({
url: '/cs-device-boot/csline/delCldLine', url: '/cs-device-boot/csline/delCldLine',
method: 'POST', method: 'POST',
data: form data: form
}) })
@@ -120,7 +120,6 @@ export function updateEquipment(data: any) {
}) })
} }
//修改监测点 //修改监测点
export function updateLine(data: any) { export function updateLine(data: any) {
return createAxios({ return createAxios({
@@ -134,8 +133,7 @@ export function updateLine(data: any) {
export function pushLog() { export function pushLog() {
return createAxios({ return createAxios({
url: '/cs-device-boot/csTerminalLogs/pushCldInfo', url: '/cs-device-boot/csTerminalLogs/pushCldInfo',
method: 'post', method: 'post'
}) })
} }
@@ -143,7 +141,14 @@ export function pushLog() {
export function queryPushResult() { export function queryPushResult() {
return createAxios({ return createAxios({
url: '/cs-device-boot/csTerminalReply/queryData', url: '/cs-device-boot/csTerminalReply/queryData',
method: 'post', method: 'post'
}) })
} }
//查询升级日志
export function getByDevId(data: any) {
return createAxios({
url: '/cs-device-boot/csUpgradeLogs/getByDevId',
method: 'get',
params:data
})
}

View File

@@ -1,86 +1,86 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 查询分组 // 查询分组
export function getGroup(dataSet: string) { export function getGroup(dataSet: string) {
let form = new FormData() let form = new FormData()
form.append('dataSet', dataSet) form.append('dataSet', dataSet)
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/getGroup', url: '/cs-device-boot/csGroup/getGroup',
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}, },
data: form data: form
}) })
} }
// 装置分组实时数据 // 设备分组实时数据
export function deviceHisData(data: any) { export function deviceHisData(data: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/deviceHistoryData', url: '/cs-device-boot/csGroup/deviceHistoryData',
method: 'POST', method: 'POST',
data: Object.assign( data: Object.assign(
{ {
endTime: '', endTime: '',
id: '', id: '',
lineId: '', lineId: '',
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
startTime: '' startTime: ''
}, },
data data
) )
}) })
} }
// 装置分组历史数据 // 设备分组历史数据
export function deviceRtData(data: any) { export function deviceRtData(data: any) {
let form = new FormData() let form = new FormData()
form.append('id', data.id) form.append('id', data.id)
form.append('lineId', data.lineId) form.append('lineId', data.lineId)
form.append('pageNum', data.pageNum) form.append('pageNum', data.pageNum)
form.append('pageSize', data.pageSize) form.append('pageSize', data.pageSize)
form.append('searchValue', data.searchValue) form.append('searchValue', data.searchValue)
form.append('dataLevel', data.dataLevel) form.append('dataLevel', data.dataLevel)
return createAxios({ return createAxios({
url: '/cs-device-boot/csGroup/deviceRtData', url: '/cs-device-boot/csGroup/deviceRtData',
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}, },
data: form data: form
}) })
} }
// 装置分组历史数据 // 设备分组历史数据
export function realTimeData(data: any) { export function realTimeData(data: any) {
let form = new FormData() let form = new FormData()
form.append('id', data.id) form.append('id', data.id)
form.append('lineId', data.lineId) form.append('lineId', data.lineId)
form.append('pageNum', data.pageNum) form.append('pageNum', data.pageNum)
form.append('pageSize', data.pageSize) form.append('pageSize', data.pageSize)
form.append('searchValue', data.searchValue) form.append('searchValue', data.searchValue)
form.append('targetType', data.targetType) form.append('targetType', data.targetType)
form.append('dataLevel', data.dataLevel) form.append('dataLevel', data.dataLevel)
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/data/realTimeData', url: '/cs-harmonic-boot/data/realTimeData',
method: 'POST', method: 'POST',
data data
}) })
} }
// 设备监控-》测试项数据 // 设备监控-》测试项数据
export function getTestData(data: any) { export function getTestData(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/data/getTestData', url: '/cs-harmonic-boot/data/getTestData',
method: 'POST', method: 'POST',
data data
}) })
} }
// 设备监控-删除装置测试项 // 设备监控-删除设备测试项
export function deleteItem(data: any) { export function deleteItem(data: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/wlRecord/deleteItem', url: '/cs-device-boot/wlRecord/deleteItem',
method: 'POST', method: 'POST',
params: data params: data
}) })
} }

View File

@@ -1,17 +1,26 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 设备列表 // 设备列表
export function getDeviceTree() { export function getDeviceTree(params?: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csLedger/deviceTree', url: '/cs-device-boot/csLedger/deviceTree',
method: 'POST' method: 'POST',
params
}) })
} }
// 监测点列表 // 监测点列表
export function getLineTree() { export function getLineTree(params?: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csLedger/lineTree', url: '/cs-device-boot/csLedger/lineTree',
method: 'POST',
params
})
}
// 监测点列表治理
export function objTree() {
return createAxios({
url: '/cs-device-boot/csLedger/objTree',
method: 'POST' method: 'POST'
}) })
} }
@@ -24,4 +33,11 @@ export function getCldTree() {
method: 'POST' method: 'POST'
}) })
} }
//报表树
export function lineTree() {
return createAxios({
url: '/cs-device-boot/csLedger/lineTree',
method: 'POST'
})
}

View File

@@ -1,41 +1,90 @@
import request from '@/utils/request' import request from '@/utils/request'
// 新增程序版本 // 新增程序版本
export const addEdData = (data) => { export const addEdData = data => {
return request({ return request({
url: '/cs-device-boot/edData/addEdData', url: '/cs-device-boot/edData/addEdData',
method: 'post', method: 'post',
headers: { headers: {
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data'
}, },
data: data, data: data
}) })
} }
export const auditEdData = (data) => { export const auditEdData = data => {
return request({ return request({
url: '/cs-device-boot/edData/auditEdData', url: '/cs-device-boot/edData/auditEdData',
method: 'post', method: 'post',
headers: { headers: {
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data'
}, },
data: data, data: data
}) })
} }
// 修改-删除工程 // 修改-删除工程
export const auditEngineering = (data:any)=> { export const auditEngineering = (data: any) => {
return request({ return request({
url: '/cs-device-boot/engineering/auditEngineering', url: '/cs-device-boot/engineering/auditEngineering',
method: 'post', method: 'post',
data: data, data: data
}) })
} }
// 修改项目 // 修改项目
export const updateProject = (data:any) => { export const updateProject = (data: any) => {
return request({ return request({
url: '/cs-device-boot/project/updateProject', url: '/cs-device-boot/project/updateProject',
method: 'post', method: 'post',
data: data, data: data
}) })
} }
// 新增工程
export const addEngineering = (data: any) => {
return request({
url: '/cs-device-boot/engineeringProjectRelation/addEngineering',
method: 'post',
data: data
})
}
// 修改工程
export const updateEngineering = (data: any) => {
return request({
url: '/cs-device-boot/engineeringProjectRelation/updateEngineering',
method: 'post',
data: data
})
}
// 刪除工程
export const deleteEngineering = (data: any) => {
return request({
url: '/cs-device-boot/engineeringProjectRelation/deleteEngineering',
method: 'post',
params: data
})
}
// 刪除項目
export const deleteProject = (data: any) => {
return request({
url: '/cs-device-boot/engineeringProjectRelation/deleteProject',
method: 'post',
params: data
})
}
// 新增项目
export const addProject = (data: any) => {
return request({
url: '/cs-device-boot/engineeringProjectRelation/addProject',
method: 'post',
data: data
})
}
// 修改项目
export const updateProjects = (data: any) => {
return request({
url: '/cs-device-boot/engineeringProjectRelation/updateProject',
method: 'post',
data: data
})
}

View File

@@ -1,93 +1,140 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 设备文件根目录查询 // 设备文件根目录查询
export function getDeviceRootPath(nDid) { export function getDeviceRootPath(nDid) {
return createAxios({ return createAxios({
url: '/cs-device-boot/deviceFile/askDeviceRootPath?nDid=' + nDid, url: '/cs-device-boot/deviceFile/askDeviceRootPath?nDid=' + nDid,
method: 'POST' method: 'POST'
}) })
} }
// 设备文件-目录信息询问 // 设备文件-目录信息询问
export function getFileServiceFileOrDir(data) { export function getFileServiceFileOrDir(data) {
return createAxios({ return createAxios({
url: `cs-device-boot/deviceFile/askDeviceFileOrDir?nDid=${data.nDid}&name=${data.name}&type=${data.type}`, url: `cs-device-boot/deviceFile/askDeviceFileOrDir?nDid=${data.nDid}&name=${data.name}&type=${data.type}`,
method: 'POST' method: 'POST'
}) })
} }
// 监测设备-目录信息询问
//设备文件下载 export function listDir(data) {
export function downLoadDeviceFile(data) { return createAxios({
return createAxios({ url: `/zl-event-boot/file/listDir`,
url: `/cs-device-boot/deviceFile/downloadFile?nDid=${data.nDid}&name=${data.name}&fileCheck=${data.fileCheck}&size=${data.size}`, method: 'POST',
method: 'POST' data: data
}) })
} }
// 下载文件
//获取下载文件的文件路径地址 export function downloadFileFromFrontr(data: any) {
export function downLoadDeviceFilePath(obj) { return createAxios({
let form = new FormData() url: `/zl-event-boot/file/downloadFileFromFront`,
form.append('name', obj.name) method: 'POST',
form.append('nDid', obj.nDid) data: data,
return createAxios({ responseType: 'blob'
url: `/cs-device-boot/deviceFile/getDownloadFilePath`, })
method: 'POST', }
headers: { // 删除文件
'Content-Type': 'application/x-www-form-urlencoded' export function deleteCld(data: any) {
}, return createAxios({
data: form url: `/zl-event-boot/file/delete`,
}) method: 'POST',
} data: data
//装置重启 })
export function reStartDevice(data) { }
return createAxios({ // 新建文件
url: `/cs-device-boot/EquipmentDelivery/rebootDevice?nDid=${data.nDid}`, export function mkdir(data: any) {
method: 'POST' return createAxios({
}) url: `/zl-event-boot/file/mkdir`,
} method: 'POST',
data: data
//上传文件至装置 })
export function uploadDeviceFile(data) { }
let form = new FormData() // 上传文件
form.append('file', data.file) export function uploadFileToFront(obj: any) {
form.append('filePath', data.filePath) let form = new FormData()
form.append('id', data.id) form.append('file', obj.file)
return createAxios({ form.append('devId', obj.devId)
url: `/access-boot/analyzeModel/uploadDevFile`, form.append('dirPath', obj.dirPath)
method: 'POST', return createAxios({
headers: { url: `/zl-event-boot/file/uploadFileToFront`,
'Content-Type': 'application/x-www-form-urlencoded' method: 'POST',
}, headers: {
data: form 'Content-Type': 'application/x-www-form-urlencoded'
}) },
} data: form
})
//新建文件夹目录 }
export function addDeviceDir(data) { //设备文件下载
let form = new FormData() export function downLoadDeviceFile(data) {
form.append('nDid', data.nDid) return createAxios({
form.append('path', data.path) url: `/cs-device-boot/deviceFile/downloadFile?nDid=${data.nDid}&name=${data.name}&fileCheck=${data.fileCheck}&size=${data.size}`,
return createAxios({ method: 'POST'
url: `/access-boot/askDeviceData/createFolder`, })
method: 'POST', }
headers: {
'Content-Type': 'application/x-www-form-urlencoded' //获取下载文件的文件路径地址
}, export function downLoadDeviceFilePath(obj) {
data: form let form = new FormData()
}) form.append('name', obj.name)
} form.append('nDid', obj.nDid)
return createAxios({
//删除文件/文件夹 url: `/cs-device-boot/deviceFile/getDownloadFilePath`,
export function delDeviceDir(data) { method: 'POST',
let form = new FormData() headers: {
form.append('nDid', data.nDid) 'Content-Type': 'application/x-www-form-urlencoded'
form.append('path', data.path) },
return createAxios({ data: form
url: `/access-boot/askDeviceData/deleteFolder`, })
method: 'POST', }
headers: { //设备重启
'Content-Type': 'application/x-www-form-urlencoded' export function reStartDevice(data) {
}, return createAxios({
data: form url: `/cs-device-boot/EquipmentDelivery/rebootDevice?nDid=${data.nDid}`,
}) method: 'POST'
} })
}
//上传文件至设备
export function uploadDeviceFile(data) {
let form = new FormData()
form.append('file', data.file)
form.append('filePath', data.filePath)
form.append('id', data.id)
return createAxios({
url: `/access-boot/analyzeModel/uploadDevFile`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: form
})
}
//新建文件夹目录
export function addDeviceDir(data) {
let form = new FormData()
form.append('nDid', data.nDid)
form.append('path', data.path)
return createAxios({
url: `/access-boot/askDeviceData/createFolder`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: form
})
}
//删除文件/文件夹
export function delDeviceDir(data) {
let form = new FormData()
form.append('nDid', data.nDid)
form.append('path', data.path)
return createAxios({
url: `/access-boot/askDeviceData/deleteFolder`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: form
})
}

View File

@@ -1,22 +1,59 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
import { genFileId, ElMessage, ElNotification } from 'element-plus'
// 查询设备数据趋势 // 查询设备数据趋势
export function getDeviceDataTrend(data: any) { export function getDeviceDataTrend(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/datatrend/querydatatrend', url: '/cs-harmonic-boot/datatrend/querydatatrend',
method: 'POST', method: 'POST',
data data
}) })
} }
// 波形下载
export function getFileZip(params: any) {
// 波形下载 return createAxios({
export function getFileZip(params: any) { url: '/cs-harmonic-boot/event/getFileZip',
return createAxios({ method: 'get',
url: '/cs-harmonic-boot/event/getFileZip', params,
method: 'get', responseType: 'blob'
params, })
responseType: 'blob' }
})
} export function exportModel(data: any) {
return createAxios({
url: '/cs-harmonic-boot/exportmodel/exportModel',
method: 'post',
data: data,
responseType: 'blob'
}).then(async res => {
let load: any = await readJsonBlob(res)
if (load.code) {
if (load.data.code == 'A0011') {
ElMessage.warning('下载失败!')
} else {
ElMessage.warning(load.data.message)
}
} else {
return res
}
})
}
async function readJsonBlob(blob) {
try {
// 1. Blob.text() 读取二进制 → 直接转为 字符串(自动处理编码)
const jsonStr = await blob.text()
// 2. JSON.parse 解析字符串 → 得到可用的 JS 对象/数组
const jsonData = JSON.parse(jsonStr)
// 3. 拿到数据,后续随便用
return {
code: true,
data: jsonData
}
} catch (err) {
return {
code: false,
data: {}
}
// console.error('解析Blob的JSON数据失败', err)
}
}

View File

@@ -1,50 +1,99 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
//新增组态项目 //新增组态项目
export function add(data: any) { export function add(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/csconfiguration/add', url: '/cs-harmonic-boot/csconfiguration/add',
method: 'post', method: 'post',
data data
}) })
} }
//组态项目分页查询 //组态项目分页查询
export function coFqueryPage(data: any) { export function coFqueryPage(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/csconfiguration/queryPage', url: '/cs-harmonic-boot/csconfiguration/queryPage',
method: 'post', method: 'post',
data data
}) })
} }
//修改组态项目 //修改组态项目
export function audit(data: any) { export function audit(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/csconfiguration/audit', url: '/cs-harmonic-boot/csconfiguration/audit',
method: 'post', method: 'post',
data data
}) })
} }
//组态页面分页查询 //组态页面分页查询
export function queryPageData(data: any) { export function queryPageData(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/cspage/queryPage', url: '/cs-harmonic-boot/cspage/queryPage',
method: 'post', method: 'post',
data data
}) })
} }
//查询工程列表 //查询工程列表
export function deviceTree(data: any) { export function deviceTree(data: any) {
return createAxios({ return createAxios({
url: '/cs-device-boot/csLedger/deviceTree', url: '/cs-device-boot/csLedger/deviceTree',
method: 'post', method: 'post',
data data
}) })
} }
//三层设备树(项目层根节点为治理设备和便携式设备组态) //三层设备树(项目层根节点为治理设备和便携式设备组态)
export function getztProjectTree() { export function getztProjectTree() {
return createAxios({ return createAxios({
url: '/cs-device-boot/csLedger/getztProjectTree', url: '/cs-device-boot/csLedger/getztProjectTree',
method: 'post', method: 'post',
}) })
} }
//根据用户id获取组件信息
export function getByUserId(data: any) {
return createAxios({
url: '/cs-harmonic-boot/cspage/getByUserId',
method: 'post',
params: data
})
}
//c保存组态界面与用户的关系
export function savePageIdWithUser(data: any) {
return createAxios({
url: '/cs-harmonic-boot/cspage/savePageIdWithUser',
method: 'post',
params: data
})
}
//新增稳态指标方案
export function save(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/save',
method: 'post',
data: data
})
}
//修改稳态指标方案
export function update(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/update',
method: 'post',
data: data
})
}
//新增稳态指标方案
export function deletePlan(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/delete',
method: 'post',
data: data
})
}
//根据ID查询稳态指标方案
export function getById(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/getById',
method: 'GET',
params: data
})
}

View File

@@ -1,27 +1,43 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 获取设备补召页面数据 // 获取设备补召页面数据
export function getMakeUpData(data: any) { export function getMakeUpData(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/offlineDataUpload/makeUpData?lineId='+data, url: '/cs-harmonic-boot/offlineDataUpload/makeUpData?lineId=' + data,
method: 'POST' method: 'POST'
}) })
} }
//查询装置目录-文件 //查询设备目录-文件
export function getAskDirOrFile(data: any) { export function getAskDirOrFile(data: any) {
return createAxios({ return createAxios({
url: `/cs-harmonic-boot/offlineDataUpload/askDirOrFile?fileType=${data.fileType}&nDid=${data.nDid}&path=${data.path}&prjName=${data.prjName}`, url: `/cs-harmonic-boot/offlineDataUpload/askDirOrFile?fileType=${data.fileType}&nDid=${data.nDid}&path=${data.path}&prjName=${data.prjName}`,
method: 'POST' method: 'POST'
}) })
} }
//设备补召操作 //设备补召操作
// 获取设备补召页面数据 // 获取设备补召页面数据
export function offlineDataUploadMakeUp(data: any) { export function offlineDataUploadMakeUp(data: any) {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/offlineDataUpload/makeUp', url: '/cs-harmonic-boot/offlineDataUpload/makeUp',
method: 'POST', method: 'POST',
data data
}) })
} }
//设备补召操作
// 根据id集合获取敏感负荷用户列表
export function getListByIds() {
return createAxios({
url: '/cs-harmonic-boot/pqSensitiveUser/getListByIds',
method: 'POST'
})
}
// 根据id集合获取敏感负荷用户列表
export function getList(data) {
return createAxios({
url: '/cs-harmonic-boot/pqSensitiveUser/getList',
method: 'POST',
data
})
}

View File

@@ -1,30 +1,69 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
/** /**
* 查询app个人中心信息详情 * 查询app个人中心信息详情
* @param id * @param id
*/ */
export const queryAppInfo = (type: string) => { export const queryAppInfo = (type: string) => {
let form = new FormData() let form = new FormData()
form.append('type', type) form.append('type', type)
return createAxios({ return createAxios({
url: '/cs-system-boot/appinfo/queryAppInfoByType', url: '/cs-system-boot/appinfo/queryAppInfoByType',
method: 'post', method: 'post',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}, },
data: form data: form
}) })
} }
/**
/** * 新增app基础信息
* 新增app基础信息 **/
**/ export const addAppInfo = (data: { type: string; content: string }) => {
export const addAppInfo = (data: { type: string, content: string }) => { return createAxios({
return createAxios({ url: '/cs-system-boot/appinfo/addAppInfo',
url: '/cs-system-boot/appinfo/addAppInfo', method: 'post',
method: 'post', data: data
data: data })
}) }
} /**
* 切换告警配置启用状态
**/
export const toggleActive = (data: any) => {
return createAxios({
url: '/cs-system-boot/csAlarmSet/toggleActive',
method: 'post',
params: data
})
}
/**
* 切换告警配置启用状态
**/
export const csDelete = (data: any) => {
return createAxios({
url: '/cs-system-boot/csAlarmSet/delete',
method: 'post',
params: data
})
}
/**
* 新增告警配置
**/
export const add = (data: any) => {
return createAxios({
url: '/cs-system-boot/csAlarmSet/add',
method: 'post',
data: data
})
}
/**
* 修改告警配置
**/
export const update = (data: any) => {
return createAxios({
url: '/cs-system-boot/csAlarmSet/update',
method: 'post',
data: data
})
}

View File

@@ -1,98 +1,121 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 新增出厂设备 // 新增出厂设备
export const addEquipmentDelivery = (data: any) => { export const addEquipmentDelivery = (data: any) => {
return createAxios({ return createAxios({
url: '/cs-device-boot/EquipmentDelivery/addEquipmentDelivery', url: '/cs-device-boot/EquipmentDelivery/addEquipmentDelivery',
method: 'POST', method: 'POST',
data: data data: data
}) })
} }
// 删除出厂设备 // 删除出厂设备
export const deleteEquipmentDelivery = (id: any) => { export const deleteEquipmentDelivery = (id: any) => {
let form = new FormData() let form = new FormData()
form.append('id', id) form.append('id', id)
return createAxios({ return createAxios({
url: '/cs-device-boot/EquipmentDelivery/AuditEquipmentDelivery', url: '/cs-device-boot/EquipmentDelivery/AuditEquipmentDelivery',
method: 'POST', method: 'POST',
data: form data: form
}) })
} }
// 恢复出厂设置 // 恢复出厂设置
export const resetEquipmentDelivery = (id: any) => { export const resetEquipmentDelivery = (id: any) => {
let form = new FormData() let form = new FormData()
form.append('nDid', id) form.append('nDid', id)
return createAxios({ return createAxios({
url: '/access-boot/device/resetFactory', url: '/access-boot/device/resetFactory',
method: 'POST', method: 'POST',
data: form data: form
}) })
} }
// 编辑出厂设备 // 编辑出厂设备
export const editEquipmentDelivery = (data: any) => { export const editEquipmentDelivery = (data: any) => {
return createAxios({ return createAxios({
url: '/cs-device-boot/EquipmentDelivery/updateEquipmentDelivery', url: '/cs-device-boot/EquipmentDelivery/updateEquipmentDelivery',
method: 'POST', method: 'POST',
data: data data: data
}) })
} }
// 上传拓扑图 // 上传拓扑图
export const uploadTopo = (file: any) => { export const uploadTopo = (file: any) => {
let form = new FormData() let form = new FormData()
form.append('file', file) form.append('file', file)
return createAxios({ return createAxios({
url: '/cs-device-boot/topologyTemplate/uploadImage', url: '/cs-device-boot/topologyTemplate/uploadImage',
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
}, },
data: form data: form
}) })
} }
// 批量导入设备 // 批量导入设备
export const batchImportDevice = (file: any) => { export const batchImportDevice = (file: any) => {
let form = new FormData() let form = new FormData()
form.append('file', file) form.append('file', file)
return createAxios({ return createAxios({
url: '/cs-device-boot/EquipmentDelivery/importEquipment', url: '/cs-device-boot/EquipmentDelivery/importEquipment',
method: 'POST', method: 'POST',
responseType: 'blob', responseType: 'blob',
data: form data: form
}) })
} }
// 直连设备注册接入 // 直连设备注册接入
export const governDeviceRegister = (data: any) => { export const governDeviceRegister = (data: any) => {
return createAxios({ return createAxios({
url: `/access-boot/device/register?nDid=${data.nDid}&type=${data.type}`, url: `/access-boot/device/register?nDid=${data.nDid}&type=${data.type}`,
method: 'POST' method: 'POST'
}) })
} }
// 便携式设备注册 // 便携式设备注册
export const portableDeviceRegister = (params: any) => { export const portableDeviceRegister = (params: any) => {
return createAxios({ return createAxios({
url: `/access-boot/device/wlRegister`, url: `/access-boot/device/wlRegister`,
method: 'POST', method: 'POST',
params params
}) })
} }
// 便携式设备接入 // 便携式设备接入
export const portableDeviceAccess = (data: any) => { export const portableDeviceAccess = (data: any) => {
return createAxios({ return createAxios({
url: `/access-boot/device/wlAccess?nDid=${data.nDid}`, url: `/access-boot/device/wlAccess?nDid=${data.nDid}`,
method: 'POST', method: 'POST'
}) })
} }
// 下载模版 // 下载模版
export function getExcelTemplate() { export function getExcelTemplate() {
return createAxios({ return createAxios({
url: '/cs-device-boot/EquipmentDelivery/getExcelTemplate', url: '/cs-device-boot/EquipmentDelivery/getExcelTemplate',
method: 'get', method: 'get',
responseType: 'blob' responseType: 'blob'
}) })
} }
// 查询工程信息列表
export function engineeringProject() {
return createAxios({
url: '/cs-device-boot/engineeringProjectRelation/list',
method: 'post'
})
}
//监测设备接入
export function onlineRegister(data: any) {
return createAxios({
url: '/access-boot/device/onlineRegister',
method: 'post',
params: data
})
}
//重启设备
export function resetFactory(data: any) {
return createAxios({
url: '/access-boot/device/resetFactory',
method: 'post',
params: data
})
}

View File

@@ -1,20 +1,30 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 更新问题状态 // 更新问题状态
export function auditFeedBack(data: any) { export function auditFeedBack(data: any) {
return createAxios({ return createAxios({
url: '/cs-system-boot/feedback/auditFeedBack', url: '/cs-system-boot/feedback/auditFeedBack',
method: 'post', method: 'post',
params: data params: data
}) })
} }
//下载文件 //下载文件
export function downLoadFile(filePath: any) { export function downLoadFile(filePath: any) {
return createAxios({ return createAxios({
url: '/system-boot/file/download', url: '/system-boot/file/download',
method: 'get', method: 'get',
responseType: 'blob', responseType: 'blob',
params: { filePath: filePath } params: { filePath: filePath }
}) })
}
//获取文件的一个短期url
export function getFileUrl(filePath: any) {
return createAxios({
url: '/system-boot/file/getFileUrl',
method: 'get',
// responseType: 'blob',
params: { filePath: filePath }
})
} }

View File

@@ -27,3 +27,23 @@ export const removeUserDev = (data: any) => {
data: data data: data
}) })
} }
/**
* 短信配置
*/
export const addUserDevices = (data: any) => {
return createAxios({
url: '/cs-system-boot/appMsgSet/addUserDevices',
method: 'post',
data: data
})
}
/**
* 短信配置
*/
export const queryDeviceIdsByUserId = (data: any) => {
return createAxios({
url: '/cs-system-boot/appMsgSet/queryDeviceIdsByUserId',
method: 'post',
params: data
})
}

View File

@@ -1,5 +1,5 @@
import request from '@/utils/request' import request from '@/utils/request'
import { genFileId, ElMessage, ElNotification } from 'element-plus'
// 主要监测点列表查询>>分页 // 主要监测点列表查询>>分页
export function mainLineList(data: any) { export function mainLineList(data: any) {
return request({ return request({
@@ -115,7 +115,6 @@ export function limitProbabilityData(data: any) {
}) })
} }
// 电网侧指标越限统计列表 // 电网侧指标越限统计列表
export function gridSideLimitStatisticsList(data: any) { export function gridSideLimitStatisticsList(data: any) {
return request({ return request({
@@ -152,7 +151,6 @@ export function getListByIds(data: any) {
}) })
} }
// 上传治理报告 // 上传治理报告
export function uploadReport(data: any) { export function uploadReport(data: any) {
return request({ return request({
@@ -260,5 +258,42 @@ export function getSimpleLine() {
}) })
} }
export function getLineExport(data: any) {
return request({
url: '/cs-harmonic-boot/eventReport/getLineExport',
method: 'post',
data: data,
responseType: 'blob'
}).then(async res => {
let load: any = await readJsonBlob(res)
if (load.code) {
if (load.data.code == 'A0011') {
ElMessage.warning('下载失败!')
} else {
ElMessage.warning(load.data.message)
}
} else {
return res
}
})
}
async function readJsonBlob(blob) {
try {
// 1. Blob.text() 读取二进制 → 直接转为 字符串(自动处理编码)
const jsonStr = await blob.text()
// 2. JSON.parse 解析字符串 → 得到可用的 JS 对象/数组
const jsonData = JSON.parse(jsonStr)
// 3. 拿到数据,后续随便用
return {
code: true,
data: jsonData
}
} catch (err) {
return {
code: false,
data: {}
}
// console.error('解析Blob的JSON数据失败', err)
}
}

View File

@@ -103,6 +103,14 @@ export function getTemplateByDept(params) {
params params
}) })
} }
// 获取模版
export function querySysExcel(params) {
return createAxios({
url: '/cs-harmonic-boot/sysExcel/querySysExcel',
method: 'post',
params
})
}
//资源管理 查询数据 //资源管理 查询数据
export function queryData(data) { export function queryData(data) {
return createAxios({ return createAxios({
@@ -168,3 +176,43 @@ export function terminalChooseTree() {
method: 'get' method: 'get'
}) })
} }
//新增模版
export function addSysExcel(data:any) {
return createAxios({
url: '/cs-harmonic-boot/sysExcel/addSysExcel',
method: 'post',
data
})
}
//修改模版
export function updateSysExcel(data:any) {
return createAxios({
url: '/cs-harmonic-boot/sysExcel/updateSysExcel',
method: 'post',
data
})
}
//删除模版
export function deleteSysExcel(params:any) {
return createAxios({
url: '/cs-harmonic-boot/sysExcel/deleteSysExcel',
method: 'post',
params
})
}
//查詢綁定
export function queryList(params:any) {
return createAxios({
url: '/cs-harmonic-boot/sysExcelRelation/queryList',
method: 'post',
params
})
}
//綁定
export function bandRelation(data:any) {
return createAxios({
url: '/cs-harmonic-boot/sysExcelRelation/bandRelation',
method: 'post',
data
})
}

View File

@@ -1,118 +1,126 @@
import request from '@/utils/request' import request from '@/utils/request'
// 新增字典数据 // 新增字典数据
export const addCsDictData = (data: any) => { export const addCsDictData = (data: any) => {
return request({ return request({
url: '/system-boot/csDictData/add', url: '/system-boot/csDictData/add',
method: 'post', method: 'post',
data: data data: data
}) })
} }
// 查询字典数据 // 查询字典数据
export const queryCsDictDataPage = (data: any) => { export const queryCsDictDataPage = (data: any) => {
return request({ return request({
url: '/system-boot/csDictData/list', url: '/system-boot/csDictData/list',
method: 'post', method: 'post',
data: Object.assign( data: Object.assign(
{ {
orderBy: '', orderBy: '',
pageNum: 0, pageNum: 0,
pageSize: 0, pageSize: 0,
searchBeginTime: '', searchBeginTime: '',
searchEndTime: '', searchEndTime: '',
searchState: 0, searchState: 0,
searchValue: '', searchValue: '',
dataType: '', dataType: '',
sortBy: '' sortBy: ''
}, },
data data
) )
}) })
} }
//删除字典数据 //删除字典数据
export const delCsDictData = (id: string) => { export const delCsDictData = (id: string) => {
let form = new FormData() let form = new FormData()
form.append('id', id) form.append('id', id)
return request({ return request({
url: '/system-boot/csDictData/delete', url: '/system-boot/csDictData/delete',
method: 'post', method: 'post',
data: form, data: form,
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
} }
}) })
} }
// 修改字典数据 // 修改字典数据
export const updateCsDictData = (data: any) => { export const updateCsDictData = (data: any) => {
return request({ return request({
url: '/system-boot/csDictData/update', url: '/system-boot/csDictData/update',
method: 'post', method: 'post',
data: data data: data
}) })
} }
// 执行算法 // 执行算法
export const timerRun = (params: any) => { export const timerRun = (params: any) => {
return request({ return request({
url: '/system-boot/timer/run', url: '/system-boot/timer/run',
method: 'GET', method: 'GET',
params params
}) })
} }
// 任务表达式 // 任务表达式
export const getActionClasses = () => { export const getActionClasses = () => {
return request({ return request({
url: '/system-boot/timer/getActionClasses', url: '/system-boot/timer/getActionClasses',
method: 'GET' method: 'GET'
}) })
} }
// 新增任务 // 新增任务
export const addTimer = (data: any) => { export const addTimer = (data: any) => {
return request({ return request({
url: '/system-boot/timer/add', url: '/system-boot/timer/add',
method: 'POST', method: 'POST',
data data
}) })
} }
// 修改任务 // 修改任务
export const updateTimer = (data: any) => { export const updateTimer = (data: any) => {
return request({ return request({
url: '/system-boot/timer/update', url: '/system-boot/timer/update',
method: 'POST', method: 'POST',
data data
}) })
} }
// 补招配置 // 补招配置
export const runTimer = (data: any) => { export const runTimer = (data: any) => {
return request({ return request({
url: '/system-boot/timer/run', url: '/system-boot/timer/run',
method: 'GET', method: 'GET',
params: data params: data
}) })
} }
// 删除任务 // 删除任务
export const deleteTimer = (data: any) => { export const deleteTimer = (data: any) => {
return request({ return request({
url: '/system-boot/timer/delete', url: '/system-boot/timer/delete',
method: 'POST', method: 'POST',
data: data data: data
}) })
} }
// 关闭任务 // 关闭任务
export const stop = (params: any) => { export const stop = (params: any) => {
return request({ return request({
url: '/system-boot/timer/stop', url: '/system-boot/timer/stop',
method: 'get', method: 'get',
params params
}) })
} }
// 启动任务 // 启动任务
export const start = (params: any) => { export const start = (params: any) => {
return request({ return request({
url: '/system-boot/timer/start', url: '/system-boot/timer/start',
method: 'get', method: 'get',
params params
}) })
}
// 查询监测对象类型
export const getDicDataByTypeCode = (params: any) => {
return request({
url: '/system-boot/dictData/getDicDataByTypeCode',
method: 'get',
params
})
} }

View File

@@ -54,6 +54,14 @@ export const activatePage = (params: any) => {
params params
}) })
} }
// 全局的驾驶舱页面
export const scopePage = (params: any) => {
return createAxios({
url: '/system-boot/dashboard/scopePage',
method: 'post',
params
})
}
// 查询激活的驾驶舱页面 // 查询激活的驾驶舱页面
export const queryActivatePage = () => { export const queryActivatePage = () => {
return createAxios({ return createAxios({

View File

@@ -94,3 +94,19 @@ export function codeDicTree(data: any) {
params: data params: data
}) })
} }
// 根据装置型号获取装置类型
export function findByDevTypeId(data: any) {
return createAxios({
url: '/cs-device-boot/edData/queryEdDataPage',
method: 'post',
data
})
}
// 装置升级
export function upgrade(params: any) {
return createAxios({
url: '/zl-event-boot/device/upgrade',
method: 'get',
params
})
}

View File

@@ -1,21 +1,33 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
export function getFunctionsByRoleIndex(data) { export function getFunctionsByRoleIndex(data) {
return createAxios({ return createAxios({
url: '/user-boot/roleFunction/getFunctionsByRoleIndex', url: '/user-boot/roleFunction/getFunctionsByRoleIndex',
method: 'post', method: 'post',
params: data params: data
}) })
} }
export function updateRoleMenu(data:any) { export function updateRoleMenu(data: any) {
return createAxios({ return createAxios({
url: '/user-boot/function/assignFunctionByRoleIndexes', url: '/user-boot/function/assignFunctionByRoleIndexes',
method: 'post', method: 'post',
data: data data: data
// params: roleIndex,functionIndexList })
// data:{ }
// roleIndex,functionIndexList // 新增角色与系统关系
// } export function systemAdd(data: any) {
}) return createAxios({
} url: '/user-boot/sysRoleSystem/add',
method: 'post',
data: data
})
}
// 根据角色id获取系统信息
export function getSystemByRoleId(params: any) {
return createAxios({
url: '/user-boot/sysRoleSystem/getSystemByRoleId',
method: 'get',
params
})
}

BIN
src/assets/img/jss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@@ -1,7 +1,14 @@
<template> <template>
<div> <div>
<!--F47曲线 --> <!--F47曲线 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
ref="TableHeaderRef"
:showReset="false"
:timeKeyList="prop.timeKey"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
<el-descriptions class="mt2" direction="vertical" :column="4" border> <el-descriptions class="mt2" direction="vertical" :column="4" border>
<el-descriptions-item align="center" label="名称">{{ data.name }}</el-descriptions-item> <el-descriptions-item align="center" label="名称">{{ data.name }}</el-descriptions-item>
<el-descriptions-item align="center" label="事件总数">{{ data.gs }}</el-descriptions-item> <el-descriptions-item align="center" label="事件总数">{{ data.gs }}</el-descriptions-item>
@@ -22,7 +29,6 @@
<el-dialog v-model="isWaveCharts" v-if="isWaveCharts" draggable :title="dialogTitle" append-to-body width="70%"> <el-dialog v-model="isWaveCharts" v-if="isWaveCharts" draggable :title="dialogTitle" append-to-body width="70%">
<waveFormAnalysis <waveFormAnalysis
v-loading="loading" v-loading="loading"
ref="waveFormAnalysisRef" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" @handleHideCharts="isWaveCharts = false"
:wp="wp" :wp="wp"
@@ -38,15 +44,20 @@ import waveFormAnalysis from '@/views/govern/device/control/tabs/components/wave
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { analyseWave } from '@/api/common' import { analyseWave } from '@/api/common'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const TableHeaderRef = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
const dialogTitle = ref('波形分析') const dialogTitle = ref('波形分析')
@@ -100,14 +111,13 @@ const tableStore: any = new TableStore({
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
const gongData = gongfunction(tableStore.table.data) const gongData = gongfunction(tableStore.table.data)
data.gs = tableStore.table.data.length data.gs = tableStore.table.data.length
data.krr = gongData.pointI.length data.krr = gongData.pointF.length
data.bkrr = gongData.pointIun.length data.bkrr = gongData.pointFun.length
echartList.value = { echartList.value = {
title: { title: {
text: `F47曲线` text: `F47曲线`
@@ -137,8 +147,9 @@ const tableStore: any = new TableStore({
backgroundColor: 'rgba(0,0,0,0.55)', backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0, borderWidth: 0,
formatter: function (a: any) { formatter: function (a: any) {
var relVal = '' var relVal = `<strong>${a.seriesName}</strong><br/>`
relVal = "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>' relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>'
relVal += "<font style='color:" + "'>特征幅值:" + a.value[1].toFixed(2) + '%</font>' relVal += "<font style='color:" + "'>特征幅值:" + a.value[1].toFixed(2) + '%</font>'
return relVal return relVal
@@ -159,11 +170,16 @@ const tableStore: any = new TableStore({
yAxis: [ yAxis: [
{ {
type: 'value', type: 'value',
max: function (value: any) { // max: function (value: any) {
return value.max + 20 // return value.max + 20
// },
max: function (value) {
// 先取原始最大值+20再向上取整到最近的10的倍数确保刻度够用且规整
return Math.ceil((value.max + 20) / 10) * 10
}, },
splitNumber: 10, // splitNumber: 10,
minInterval: 0.1, // interval: 10,
// minInterval: 10,
name: '%' name: '%'
} }
], ],
@@ -199,26 +215,18 @@ const tableStore: any = new TableStore({
// [0.2, 10, '2023-01-01 10:00:00'], // [0.2, 10, '2023-01-01 10:00:00'],
// [0.4, 50, '2023-01-01 11:00:00'] // [0.4, 50, '2023-01-01 11:00:00']
// ], // ],
legendSymbol: 'circle', legendSymbol: 'circle'
emphasis: {
focus: 'series', // tooltip: {
itemStyle: { // show: true,
borderColor: '#fff', // trigger: 'item',
borderWidth: 2, // formatter: function (params: any) {
shadowBlur: 10, // return `<strong>可容忍事件</strong><br/>
shadowColor: 'rgba(0, 0, 0, 0.5)' // 持续时间: ${params.value[0]}s<br/>
} // 特征幅值: ${params.value[1].toFixed(2)}%<br/>
}, // 发生时间: ${params.value[2] || 'N/A'}`
tooltip: { // }
show: true, // }
trigger: 'item',
formatter: function (params: any) {
return `<strong>可容忍事件</strong><br/>
持续时间: ${params.value[0]}s<br/>
特征幅值: ${params.value[1].toFixed(2)}%<br/>
发生时间: ${params.value[2] || 'N/A'}`
}
}
}, },
{ {
name: '不可容忍事件', name: '不可容忍事件',
@@ -238,6 +246,25 @@ const tableRef = ref()
provide('tableRef', tableRef) provide('tableRef', tableRef)
provide('tableStore', tableStore) provide('tableStore', tableStore)
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
function gongfunction(arr: any) { function gongfunction(arr: any) {
let standI = 0 let standI = 0
let unstandI = 0 let unstandI = 0
@@ -424,10 +451,10 @@ const handleTolerableEventClick = async (row: any) => {
nextTick(() => { nextTick(() => {
if (waveFormAnalysisRef.value) { if (waveFormAnalysisRef.value) {
//waveFormAnalysisRef.value.setHeight(false, 360) //waveFormAnalysisRef.value.setHeight(false, 360)
waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666) // waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
} }
}) })
const messageInstance = ElMessage.info(`正在加载,请稍等...`) const messageInstance = ElMessage.info(`正在加载,请稍等...`)
await analyseWave(row.value[3]) //eventId await analyseWave(row.value[3]) //eventId
.then(res => { .then(res => {
row.loading1 = false row.loading1 = false
@@ -452,6 +479,7 @@ const handleTolerableEventClick = async (row: any) => {
}) })
nextTick(() => { nextTick(() => {
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
waveFormAnalysisRef.value && waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true) waveFormAnalysisRef.value && waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
}) })
} }

View File

@@ -0,0 +1,308 @@
<template>
<div>
<!--暂态越限时间分布 -->
<TableHeader
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
:showReset="false"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
<my-echart
class="tall"
:options="echartList1"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
<!-- <my-echart
class="mt10"
:options="echartList1"
:style="{
width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
}"
/> -->
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, reactive, watch } from 'vue'
import TableStore from '@/utils/tableStore'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { useConfig } from '@/stores/config'
import TableHeader from '@/components/table/header/index.vue'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
})
const TableHeaderRef = ref()
const headerHeight = ref(57)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
}
}
// 计算是否全屏展示
const fullscreen = computed(() => {
const w = Number(prop.w)
const h = Number(prop.h)
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
// 执行相应逻辑
return true
} else {
return false
}
})
const config = useConfig()
const echartList = ref({})
const echartList1 = ref({})
const processDataForChart = (rawData: any[]) => {
// 将后端返回的扁平数据转换为 ECharts 需要的三维坐标格式 [x, y, z]
const chartData = rawData.map(item => [item.x, item.y, item.z])
return chartData
}
const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/csevent/getEventCoords',
method: 'POST',
showPage: false,
column: [],
beforeSearchFun: () => {
setTime()
},
loadCallback: () => {
const processedData = processDataForChart(tableStore.table.data.innerList || [])
const trendList = tableStore.table.data.trendList || []
const xlist = tableStore.table.data.xlist || []
// 处理趋势图数据
const seriesData = trendList.map((item: any) => {
// 根据接口返回的name字段确定系列名称和颜色
let name = ''
let color = ''
switch (item.name) {
case 'Evt_Sys_DipStr':
name = '电压暂降'
color = '#FFBF00'
break
case 'Evt_Sys_IntrStr':
name = '电压中断'
color = '#FF9100'
break
case 'Evt_Sys_SwlStr':
name = '电压暂升'
color = config.layout.elementUiPrimary[0]
break
default:
name = item.name
color = '#000000'
}
return {
name: name,
type: 'line',
showSymbol: false,
color: color,
data: item.trendList?.map((value: number, index: number) => [xlist[index], value]) || []
}
})
// 获取x轴和y轴的标签值
const xLabels = [
'0-10%',
'10%-20%',
'20%-30%',
'30%-40%',
'40%-50%',
'50%-60%',
'60%-70%',
'70%-80%',
'80%-90%',
'90%-100%'
]
const yLabels = ['0-0.01s', '0.01s-0.1s', '0.1s-1s', '1s-10s', '10s']
echartList.value = {
options: {
xAxis: null,
yAxis: null,
dataZoom: null,
backgroundColor: '#fff',
tooltip: {
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter: function (params: any) {
var tips = ''
tips += '持续时间: ' + yLabels[params.value[1]] + '</br>'
tips += '特征幅值: ' + xLabels[params.value[0]] + '</br>'
tips += '事件次数: ' + params.value[2] + '</br>'
return tips
}
},
title: {
text: '暂态事件概率分布',
x: 'center'
},
visualMap: {
max: 500,
show: false,
inRange: {
color: ['#313695', '#00BB00', '#ff8000', '#a50026']
}
},
xAxis3D: {
type: 'category',
name: '特征幅值',
data: xLabels,
nameGap: 40
},
yAxis3D: {
type: 'category',
name: '持续时间',
data: yLabels,
nameGap: 40,
splitLine: {
lineStyle: {
type: 'dashed',
opacity: 0.5
}
}
},
zAxis3D: {
type: 'value',
minInterval: 10,
name: '暂态事件次数',
nameGap: 30
},
grid3D: {
viewControl: {
projection: 'perspective',
distance: 260
},
boxWidth: 200,
boxDepth: 80,
light: {
main: {
intensity: 1.2
},
ambient: {
intensity: 0.3
}
}
},
series: [
{
type: 'bar3D',
data: processedData,
shading: 'realistic',
label: {
show: false,
textStyle: {
fontSize: 16,
borderWidth: 1
}
}
}
]
}
}
echartList1.value = {
title: {
text: '暂态越限时间概率分布'
},
xAxis: {
type: 'category',
data: xlist,
axisLabel: {
formatter: '{value}'
}
},
yAxis: {
name: '次'
},
grid: {
left: '10px',
right: '20px'
},
series: seriesData
}
}
})
const tableRef = ref()
provide('tableRef', tableRef)
provide('tableStore', tableStore)
onMounted(() => {
tableStore.index()
})
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch(
() => prop.timeKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
(newVal, oldVal) => {
tableStore.index()
},
{
deep: true
}
)
</script>
<style lang="scss" scoped></style>

View File

@@ -1,35 +1,42 @@
<template> <template>
<div> <div>
<!--暂降方向统计 --> <!--暂降方向统计 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
<my-echart class="tall" :options="echartList" :style="{ width: prop.width, height: `calc(${prop.height} )` }" /> ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
datePicker
:timeKeyList="prop.timeKey"
v-if="fullscreen"
></TableHeader>
<my-echart
v-loading="tableStore.table.loading"
class="tall"
:options="echartList"
:style="{ width: prop.width, height: `calc(${prop.height} )` }"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, h } from 'vue' import { ref, onMounted, provide, reactive, watch, h } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
import { useDictData } from '@/stores/dictData'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getTimeOfTheMonth } from '@/utils/formatTime'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useRoute } from 'vue-router' import { getTime } from '@/utils/formatTime'
import { useTimeCacheStore } from '@/stores/timeCache'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
const route = useRoute() const TableHeaderRef = ref()
const timeCacheStore = useTimeCacheStore()
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
@@ -53,85 +60,99 @@ const fullscreen = computed(() => {
} }
}) })
const data = [ const echartList = ref({})
{
name: '来自电网',
value: 4
},
{
name: '来自负荷',
value: 41
}
]
const echartList = ref({
title: {},
tooltip: { // const data = [
trigger: 'item' // {
}, // name: '来自电网',
legend: { // value: 4
orient: 'vertical', // },
top: 'center', // {
right: '5%', // name: '来自负荷',
formatter: function (e: any) { // value: 41
return e + ' ' + data.filter(item => item.name == e)[0].value + '次' // }
} // ]
},
xAxis: {
show: false
},
yAxis: {
show: false
},
grid: {
left: '10px',
right: '20px'
},
options: {
dataZoom: null,
title: [
{
text: '暂降方向统计',
left: 'center'
},
{
text: data[0].value + data[1].value + '次',
left: 'center',
top: 'center'
}
],
series: [
{
type: 'pie',
center: 'center',
radius: ['55%', '75%'],
label: {
show: false,
position: 'outside',
textStyle: {
//数值样式
}
},
name: '事件统计',
data: data
}
]
}
})
const OverLimitDetailsRef = ref() const OverLimitDetailsRef = ref()
const tableStore: any = new TableStore({ const tableStore: any = new TableStore({
url: '/user-boot/dept/deptTree', url: '/cs-harmonic-boot/csevent/getEventDirectionData',
method: 'POST', method: 'POST',
showPage: false, showPage: false,
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => {} loadCallback: () => {
if (!tableStore.table.data || !Array.isArray(tableStore.table.data)) {
return []
}
const chartData = ref(
tableStore.table.data.map((item: any) => ({
name: item.source === 'load' ? '来自负荷' : '来自电网',
value: item.times
}))
)
const total = chartData.value.reduce((sum: any, item: any) => sum + item.value, 0)
echartList.value = {
title: {},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
top: '50',
right: '10',
formatter: function (name: string) {
const item = chartData.value.find((i: any) => i.name === name)
return item ? `${name} ${item.value}` : name
}
},
xAxis: {
show: false
},
yAxis: {
show: false
},
grid: {
left: '10px',
right: '20px'
},
options: {
dataZoom: null,
title: [
{
text: '暂降方向统计',
left: 'center'
},
{
text: total + '次',
left: 'center',
top: 'center'
}
],
series: [
{
type: 'pie',
center: 'center',
radius: ['55%', '75%'],
label: {
show: false,
position: 'outside',
textStyle: {
//数值样式
}
},
name: '事件统计',
data: chartData.value
}
]
}
}
}
}) })
const tableRef = ref() const tableRef = ref()
@@ -139,10 +160,28 @@ provide('tableRef', tableRef)
provide('tableStore', tableStore) provide('tableStore', tableStore)
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
// 点击行 // 点击行
const cellClickEvent = ({ row, column }: any) => { const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name') { if (column.field != 'name') {
console.log(row)
OverLimitDetailsRef.value.open(row) OverLimitDetailsRef.value.open(row)
} }
} }
@@ -159,12 +198,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -17,7 +17,7 @@ import { yMethod } from '@/utils/echartMethod'
const prop = defineProps({ const prop = defineProps({
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object }
}) })
@@ -89,8 +89,6 @@ const initData = async (row: any) => {
let [min, max] = yMethod(res.data.map((item: any) => item.value.split(',')).flat()) let [min, max] = yMethod(res.data.map((item: any) => item.value.split(',')).flat())
// 从第一条数据中提取时间作为x轴数据 // 从第一条数据中提取时间作为x轴数据
const firstItem = res.data[0]
const xAxisData = firstItem.time.split(',')
// 定义相位颜色映射 // 定义相位颜色映射
const phaseColors: any = { const phaseColors: any = {
@@ -100,28 +98,34 @@ const initData = async (row: any) => {
} }
// 处理每条相位数据 // 处理每条相位数据
const seriesData = res.data.map((item: any) => { const seriesData = res.data
const values = xAxisData.map((time: string, index: number) => { .filter(item => item.valueType == 'max')
// 将传入的日期与时间拼接成完整的时间字符串 .sort((a, b) => {
const fullTime = `${row.time} ${time}` return a.phasic.localeCompare(b.phasic)
const value = parseFloat(item.value.split(',')[index]) || 0
return [fullTime, value]
}) })
.map((item: any) => {
const xAxisData = item.time.split(',')
const values = xAxisData.map((time: string, index: number) => {
// 将传入的日期与时间拼接成完整的时间字符串
const fullTime = `${row.time} ${time}`
const value = parseFloat(item.value.split(',')[index]) || 0
return [fullTime, value]
})
return { return {
name: `${item.phasic}`, name: `${item.phasic}`,
type: 'line', type: 'line',
showSymbol: false, showSymbol: false,
smooth: true, smooth: true,
data: values, data: values,
itemStyle: { itemStyle: {
normal: { normal: {
// 根据相位设置对应颜色 // 根据相位设置对应颜色
color: phaseColors[item.phasic] || config.layout.elementUiPrimary[0] color: phaseColors[item.phasic] || config.layout.elementUiPrimary[0]
}
} }
} }
} })
})
echartList.value.yAxis.max = max echartList.value.yAxis.max = max
echartList.value.yAxis.min = min echartList.value.yAxis.min = min
// 更新图表配置 // 更新图表配置
@@ -135,7 +139,7 @@ onMounted(() => {})
const open = async (row: any) => { const open = async (row: any) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = row.name + '日趋势图' dialogTitle.value = row.name + '日趋势图'
dialogText.value = `监测点名称:${row.lineName}_越限时间:${row.time}_指标名称:${row.name}` dialogText.value = `监测点名称:${row.lineName} 越限时间:${row.time} 指标名称:${row.name}`
nextTick(() => { nextTick(() => {
initData(row) initData(row)
}) })

View File

@@ -1,20 +1,15 @@
<template> <template>
<div> <div>
<!--指标越限程度 --> <!--指标越限程度 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" datePicker
<my-echart :timeKeyList="prop.timeKey" v-if="fullscreen"></TableHeader>
class="tall" <my-echart class="tall" :options="echartList"
:options="echartList" :style="{ width: prop.width, height: `calc(${prop.height} / 2 )` }" />
:style="{ width: prop.width, height: `calc(${prop.height} / 2 )` }" <Table ref="tableRef" @cell-click="cellClickEvent"
/> :height="`calc(${prop.height} / 2 - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`" isGroup></Table>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} / 2 - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`"
isGroup
></Table>
<!-- 指标日趋势图 --> <!-- 指标日趋势图 -->
<DailyTrendChart v-if="dialogTrendChart" ref="dailyTrendChartRef" @close="dialogTrendChart = false" /> <HarmonicRatio ref="harmonicRatioRef" v-if="dialogFlag" @close="onHarmonicRatioClose" :showIndex="false" />
<!-- <DailyTrendChart v-if="dialogTrendChart" ref="dailyTrendChartRef" @close="dialogTrendChart = false" /> -->
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -24,21 +19,24 @@ import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
import { getTimeOfTheMonth } from '@/utils/formatTime' import { getTimeOfTheMonth } from '@/utils/formatTime'
import { ElMessage, ElMessageBox } from 'element-plus'
import DailyTrendChart from '@/components/cockpit/exceedanceLevel/components/dailyTrendChart.vue' import DailyTrendChart from '@/components/cockpit/exceedanceLevel/components/dailyTrendChart.vue'
import { useRoute } from 'vue-router' import { getTime } from '@/utils/formatTime'
import { useTimeCacheStore } from '@/stores/timeCache' import HarmonicRatio from '@/components/cockpit/overLimitStatistics/components/harmonicRatio.vue'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const TableHeaderRef = ref()
const headerHeight = ref(57)
const harmonicRatioRef: any = ref(null)
const dialogTrendChart = ref(false) const dialogTrendChart = ref(false)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
@@ -89,7 +87,7 @@ const tableStore: any = new TableStore({
{ {
title: '越限最大值', title: '越限最大值',
field: 'maxValue', field: 'maxValue',
minWidth: '60', minWidth: '70',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
const extentValue = const extentValue =
@@ -107,67 +105,41 @@ const tableStore: any = new TableStore({
{ {
title: '越限程度(%)', title: '越限程度(%)',
field: 'extent', field: 'extent',
minWidth: '60', minWidth: '70',
render: 'customTemplate', formatter: (row: any) => {
customTemplate: (row: any) => { return Math.floor(row.cellValue * 100) / 100
// 保留两个小数
const extentValue =
row.extent !== null && row.extent !== undefined && row.extent !== ''
? Math.floor(row.extent * 100) / 100
: '/'
return `<span>${extentValue}</span>`
} }
}, },
{ {
title: '越限时间', title: '越限时间',
field: 'time', field: 'time',
minWidth: '60', minWidth: '60',
render: 'customTemplate', formatter: (row: any) => {
customTemplate: (row: any) => { return row.cellValue || '/'
if (row.time !== null && row.time !== undefined && row.time !== '') {
return `<span>${row.time}</span>`
} else {
return `<span>/</span>`
}
} }
}, },
{ {
title: '越限最高监测点', title: '越限最高监测点',
field: 'lineName', field: 'lineName',
minWidth: '90', minWidth: '90',
render: 'customTemplate', formatter: (row: any) => {
customTemplate: (row: any) => { return row.cellValue || '/'
if (row.lineName !== null && row.lineName !== undefined && row.lineName !== '') {
return `<span>${row.lineName}</span>`
} else {
return `<span>/</span>`
}
} }
} }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
// 定义 x 轴标签顺序 // 定义 x 轴标签顺序
const xAxisLabels = ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
// 根据指标名称顺序提取对应的 extent 数据
const chartData = xAxisLabels.map(label => {
// 在表格数据中查找对应指标名称的数据项
const item = tableStore.table.data.find((row: any) => row.name === label)
// 如果找到对应项,则返回 extent 值,否则返回 0并保留两位小数
const extentValue = item ? item.extent || 0 : 0
return Math.round(extentValue * 100) / 100
})
echartList.value = { echartList.value = {
title: { title: {
text: '指标越限严重度' text: '指标越限严重度'
}, },
xAxis: { xAxis: {
data: xAxisLabels data: tableStore.table.data.map((item: any) => item.name)
}, },
yAxis: { yAxis: {
@@ -183,7 +155,7 @@ const tableStore: any = new TableStore({
{ {
type: 'bar', type: 'bar',
name: '越限占比', name: '越限占比',
data: chartData, data: tableStore.table.data.map((item: any) => Math.floor(item.extent * 100) / 100),
barMaxWidth: 30 barMaxWidth: 30
} }
] ]
@@ -196,17 +168,62 @@ const tableRef = ref()
provide('tableRef', tableRef) provide('tableRef', tableRef)
provide('tableStore', tableStore) provide('tableStore', tableStore)
const codeMap = [
{ key: '闪变', code: 'flickerOvertime' },
{ key: '电压偏差', code: 'voltageDevOvertime' },
{ key: '三相', code: 'ubalanceOvertime' },
{ key: '谐波电压', code: 'uharm' },
{ key: '谐波电流', code: 'iharm' },
];
// 点击行 // 点击行
const cellClickEvent = ({ row, column }: any) => { const cellClickEvent = ({ row, column }: any) => {
dialogTrendChart.value = true dialogTrendChart.value = true
if (column.field == 'maxValue' && row.lineId) {
nextTick(() => { if (column.field == 'maxValue') {
dailyTrendChartRef.value.open(row) if (row.lineId == null) {
}) ElMessage.info('暂无越限监测点!')
} else {
nextTick(() => {
// dailyTrendChartRef.value.open(row)
dialogFlag.value = true
nextTick(() => {
const code = codeMap.find(item => row.name.includes(item.key))?.code || '';
harmonicRatioRef.value.openDialog(row, code, column.title.replace(/次/g, ""))
})
})
}
} }
} }
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
const dialogFlag = ref(false)
// 谐波弹窗关闭时的回调
const onHarmonicRatioClose = () => {
dialogFlag.value = false
// 重新打开指标越限详情弹窗
}
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
@@ -219,18 +236,13 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true
} }
) )
const addMenu = () => {} const addMenu = () => { }
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -1,21 +1,43 @@
<template> <template>
<div> <div>
<!--治理效果报表 --> <!--治理效果报表 -->
<TableHeader :showReset="false" datePicker @selectChange="selectChange" v-if="fullscreen"> <TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
ref="TableHeaderRef"
datePicker
@selectChange="selectChange"
v-if="fullscreen"
>
<template v-slot:select> <template v-slot:select>
<el-form-item label="报表模板"> <el-form-item label="模板策略">
<el-select v-model="tableStore.table.params.tempId" placeholder="请选择报表模板" clearable> <el-select
<el-option v-for="item in templateList" :key="item.id" :label="item.name" :value="item.id" /> filterable
v-model="tableStore.table.params.tempId"
placeholder="请选择模板策略"
clearable
>
<el-option
v-for="item in templateList"
:key="item.id"
:label="item.excelName"
:value="item.id"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="监测对象"> <el-form-item label="监测对象">
<el-select v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象" clearable> <el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</template> </template>
<template v-slot:operation> <template v-slot:operation>
<el-button @click="downloadExcel" class="" type="primary" icon="el-icon-Download">导出excel</el-button> <el-button @click="downloadExcel" class="" type="primary" icon="el-icon-Download">导出</el-button>
</template> </template>
</TableHeader> </TableHeader>
<div style="display: flex"> <div style="display: flex">
@@ -34,19 +56,22 @@ import { ref, onMounted, provide, reactive, watch, h, computed, nextTick } from
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import { exportExcel } from '@/views/govern/reportForms/export.js' import { exportExcel } from '@/views/govern/reportForms/export.js'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { getTemplateList } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit' import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import Json from './index.json' import { getTime } from '@/utils/formatTime'
import { ElMessage } from 'element-plus'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const TableHeaderRef = ref()
// 报表模板列表 // 报表模板列表
const templateList = ref() const templateList = ref()
@@ -56,20 +81,28 @@ const idList = ref()
// 监测对象 // 监测对象
const initListByIds = () => { const initListByIds = () => {
getListByIds({}).then((res: any) => { getListByIds({}).then((res: any) => {
if (res.data.length > 0) { if (res.data?.length > 0) {
idList.value = res.data idList.value = res.data
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) { if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id tableStore.table.params.sensitiveUserId = idList.value[0].id
} }
templateListData() templateListData()
} else {
querySysExcel({}).then(res => {
templateList.value = res.data.filter(item => item.excelType == 4)
if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
tableStore.table.params.tempId = templateList.value[0].id
}
})
tableStore.table.loading = false
} }
}) })
} }
const templateListData = () => { const templateListData = () => {
getTemplateList({}).then(res => { querySysExcel({}).then(res => {
templateList.value = res.data templateList.value = res.data.filter(item => item.excelType == 4)
if (!tableStore.table.params.tempId && templateList.value?.length > 0) { if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
tableStore.table.params.tempId = templateList.value[0].id tableStore.table.params.tempId = templateList.value[0].id
} }
@@ -114,14 +147,16 @@ const tableStore: any = new TableStore({
exportName: '治理效果报表', exportName: '治理效果报表',
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1] // if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) { // tableStore.table.params.sensitiveUserId = idList.value[0].id
tableStore.table.params.sensitiveUserId = idList.value[0].id // }
} // if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
if (!tableStore.table.params.tempId && templateList.value?.length > 0) { // tableStore.table.params.tempId = templateList.value[0].id
tableStore.table.params.tempId = templateList.value[0].id // }
} // if( !tableStore.table.params.tempId){
// return ElMessage.warning('请选择模板')
// }
}, },
loadCallback: () => { loadCallback: () => {
luckysheet.create({ luckysheet.create({
@@ -142,6 +177,27 @@ provide('tableRef', tableRef)
provide('tableStore', tableStore) provide('tableStore', tableStore)
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
tableStore.table.params.startTime = time[0]
tableStore.table.params.endTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {
@@ -151,12 +207,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -16,6 +16,7 @@
v-model="searchForm.index" v-model="searchForm.index"
placeholder="请选择统计指标" placeholder="请选择统计指标"
@change="onIndexChange($event)" @change="onIndexChange($event)"
filterable
> >
<el-option <el-option
v-for="item in indexOptions" v-for="item in indexOptions"
@@ -33,14 +34,15 @@
</el-form-item> </el-form-item>
<el-form-item label="统计类型"> <el-form-item label="统计类型">
<el-select <el-select
style="min-width: 120px !important" style="min-width: 90px !important"
placeholder="请选择" placeholder="请选择"
v-model="searchForm.valueType" v-model="searchForm.valueType"
filterable
> >
<el-option value="max" label="最大值"></el-option> <el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option> <el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option> <el-option value="avg" label="平均值"></el-option>
<el-option value="cp95" label="cp95"></el-option> <el-option value="cp95" label="CP95"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -59,11 +61,12 @@
placeholder="请选择谐波次数" placeholder="请选择谐波次数"
style="width: 100px" style="width: 100px"
class="mr20" class="mr20"
filterable
> >
<el-option <el-option
v-for="vv in item.countOptions" v-for="vv in item.countOptions"
:key="vv" :key="vv"
:label="vv" :label="item.name.includes('间谐波') ? vv - 0.5 : vv"
:value="vv" :value="vv"
></el-option> ></el-option>
</el-select> </el-select>
@@ -79,11 +82,7 @@
</TableHeader> </TableHeader>
</div> </div>
<div class="history_chart" :style="pageHeight" v-loading="loading"> <div class="history_chart" :style="pageHeight" v-loading="loading">
<MyEchart <MyEchart ref="historyChart" :options="echartsData" v-if="showEchart" />
ref="historyChart"
:options="echartsData"
v-if="showEchart"
/>
<el-empty :style="pageHeight" v-else description="暂无数据" /> <el-empty :style="pageHeight" v-else description="暂无数据" />
</div> </div>
</el-dialog> </el-dialog>
@@ -157,28 +156,40 @@ const countOptions: any = ref([])
const legendDictList: any = ref([]) const legendDictList: any = ref([])
const initCode = (field: string, title: string) => { const initCode = (field: string, title: string) => {
queryByCode('steady_state_limit_trend').then(res => { queryByCode('gridSide_exceedTheLimit').then(res => {
queryCsDictTree(res.data.id).then(item => { queryCsDictTree(res.data.id).then(item => {
//排序 //排序
indexOptions.value = item.data.sort((a: any, b: any) => { indexOptions.value = item.data.sort((a: any, b: any) => {
return a.sort - b.sort return a.sort - b.sort
}) })
const titleMap: Record<string, number> = { let codeKey = field.includes('flickerOvertime')
flickerOvertime: 0, ? '闪变'
uaberranceOvertime: 3, : field.includes('uharm')
ubalanceOvertime: 4, ? '谐波电压'
freqDevOvertime: 5 : field.includes('iharm')
} ? '谐波电流'
: field.includes('voltageDevOvertime')
? '电压偏差'
: field.includes('ubalanceOvertime')
? '不平衡'
: ''
let defaultIndex = 0 // 默认值 // const titleMap: Record<string, number> = {
// flickerOvertime: 0,
// uaberranceOvertime: 3,
// ubalanceOvertime: 4,
// freqDevOvertime: 5
// }
if (field in titleMap) { // let defaultIndex = 0 // 默认值
defaultIndex = titleMap[field] let defaultIndex = indexOptions.value.findIndex((item: any) => item.name.includes(codeKey)) || 0
} else if (field.includes('uharm')) { // if (field in titleMap) {
defaultIndex = 1 // defaultIndex = titleMap[field]
} else if (field.includes('iharm')) { // } else if (field.includes('uharm')) {
defaultIndex = 2 // defaultIndex = indexOptions.value.findIndex((item: any) => item.code === 'uharm')
} // } else if (field.includes('iharm')) {
// defaultIndex = indexOptions.value.findIndex((item: any) => item.code === 'iharm')
// }
searchForm.value.index[0] = indexOptions.value[defaultIndex].id searchForm.value.index[0] = indexOptions.value[defaultIndex].id
}) })
@@ -202,7 +213,7 @@ const initCode = (field: string, title: string) => {
if (kk.harmStart && kk.harmEnd) { if (kk.harmStart && kk.harmEnd) {
range(0, 0, 0) range(0, 0, 0)
if (kk.showName == '间谐波电压含有率') { if (kk.showName.includes('间谐波电压')) {
countDataCopy.value[index].countOptions = range(kk.harmStart, kk.harmEnd, 1).map( countDataCopy.value[index].countOptions = range(kk.harmStart, kk.harmEnd, 1).map(
(item: any) => { (item: any) => {
return item - 0.5 return item - 0.5
@@ -284,14 +295,15 @@ const init = async () => {
let lists: any = [] let lists: any = []
let frequencys: any = null let frequencys: any = null
countData.value.map((item: any, index: any) => { countData.value.map((item: any, index: any) => {
if (item.name.includes('谐波含有率')) { if (item.name.includes('谐波')) {
frequencys = item.count frequencys = item.count
} else { } else {
frequencys = '' frequencys = ''
} }
lists[index] = { lists[index] = {
statisticalId: item.index, statisticalId: item.index,
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : '' frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
} }
}) })
let obj = { let obj = {
@@ -597,12 +609,12 @@ const formatCountOptions = () => {
}) })
countData.value.map((item: any, key: any) => { countData.value.map((item: any, key: any) => {
if (item.name == '谐波电流有效值') { if (item.name.includes('谐波电压')) {
item.name = '谐波电流次数'
} else if (item.name == '谐波电压含有率') {
item.name = '谐波电压次数'
} else if (item.name == '间谐波电压含有率') {
item.name = '间谐波电压次数' item.name = '间谐波电压次数'
} else if (item.name.includes('谐波电流')) {
item.name = '谐波电流次数'
} else if (item.name.includes('谐波电压')) {
item.name = '谐波电压次数'
} }
}) })
} }

View File

@@ -9,11 +9,12 @@
v-model="tableStore.table.params.lineId" v-model="tableStore.table.params.lineId"
placeholder="请选择监测点" placeholder="请选择监测点"
style="width: 150px" style="width: 150px"
filterable
> >
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.lineId" :key="item.lineId"
:label="item.name" :label="item.lineName"
:value="item.lineId" :value="item.lineId"
/> />
</el-select> </el-select>
@@ -48,7 +49,7 @@ const loop50 = (key: string) => {
list.push({ list.push({
title: i + '次', title: i + '次',
field: key + i + 'Overtime', field: key + i + 'Overtime',
width: '80', width: '60',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
@@ -84,21 +85,22 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(%)', title: '长时闪变越限(%)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '80', width: '90',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
} }
}, },
{ {
title: '谐波电压越限(%)', title: '电压偏差越限(%)',
children: loop50('uharm') field: 'voltageDevOvertime',
}, width: '100',
{ render: 'customTemplate',
title: '谐波电流越限(%)', customTemplate: (row: any) => {
children: loop50('iharm') return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '三相不平衡度越限(%)', title: '三相不平衡度越限(%)',
@@ -110,23 +112,24 @@ const tableStore: any = new TableStore({
} }
}, },
{ {
title: '电压偏差越限(%)', title: '谐波电压越限(%)',
field: 'voltageDevOvertime', children: loop50('uharm')
width: '100',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '频率偏差越限(%)', title: '谐波电流越限(%)',
field: 'freqDevOvertime', children: loop50('iharm')
width: '100', },
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>` // {
} // title: '频率偏差越限(%)',
} // field: 'freqDevOvertime',
// width: '100',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
// }
// }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
}, },
@@ -138,16 +141,20 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.sortBy = '' tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = '' tableStore.table.params.orderBy = ''
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => { const open = async (row: any,searchBeginTime:any,searchEndTime:any,interval:any,list:any) => {
dialogVisible.value = true dialogVisible.value = true
initCSlineList() options.value = list
// initCSlineList()
tableStore.table.params.lineId = row.lineId tableStore.table.params.lineId = row.lineId
nextTick(() => { nextTick(() => {
tableHeaderRef.value.setTimeInterval([searchBeginTime, searchEndTime]) tableHeaderRef.value.setInterval(interval)
setTimeout(() => {
tableHeaderRef.value.setTimeInterval([searchBeginTime, searchEndTime])
tableStore.table.params.searchBeginTime =searchBeginTime tableStore.table.params.searchBeginTime =searchBeginTime
tableStore.table.params.searchEndTime = searchEndTime tableStore.table.params.searchEndTime = searchEndTime
tableStore.index() tableStore.index()
},100)
}) })
} }

View File

@@ -1,7 +1,14 @@
<template> <template>
<div> <div>
<!--电网侧指标越限统计 --> <!--电网侧指标越限统计 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
:showReset="false"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
:timeKeyList="prop.timeKey"
v-if="fullscreen"
></TableHeader>
<my-echart <my-echart
class="tall" class="tall"
:options="echartList" :options="echartList"
@@ -27,23 +34,25 @@ import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
import OverLimitDetails from '@/components/cockpit/gridSideStatistics/components/overLimitDetails.vue' import OverLimitDetails from '@/components/cockpit/gridSideStatistics/components/overLimitDetails.vue'
import { useRoute } from 'vue-router'
import { useTimeCacheStore } from '@/stores/timeCache'
import { gridSideLimitStatisticsData } from '@/api/harmonic-boot/cockpit/cockpit' import { gridSideLimitStatisticsData } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
const echartList = ref({}) const echartList = ref({})
const TableHeaderRef = ref()
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
@@ -79,7 +88,7 @@ const initEcharts = () => {
xAxis: { xAxis: {
// name: '(区域)', // name: '(区域)',
data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡'] data: ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
}, },
yAxis: { yAxis: {
@@ -140,7 +149,7 @@ const tableStore: any = new TableStore({
title: '越限占比(%)', title: '越限占比(%)',
children: [ children: [
{ {
title: '闪变', title: '长时闪变',
field: 'flicker', field: 'flicker',
minWidth: '70', minWidth: '70',
render: 'customTemplate', render: 'customTemplate',
@@ -188,8 +197,8 @@ const tableStore: any = new TableStore({
} }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1] tableStore.table.params.interval = TableHeaderRef.value?.datePickerRef?.interval || 3
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.height = `calc(${prop.height} - 80px)` tableStore.table.height = `calc(${prop.height} - 80px)`
@@ -204,18 +213,37 @@ provide('tableStore', tableStore)
// 点击行 // 点击行
const cellClickEvent = ({ row, column }: any) => { const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name') { OverLimitDetailsRef.value.open(
OverLimitDetailsRef.value.open( row,
row, tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
tableStore.table.params.searchBeginTime || prop.timeValue?.[0], tableStore.table.params.searchEndTime || prop.timeValue?.[1],
tableStore.table.params.searchEndTime || prop.timeValue?.[1] tableStore.table.params.interval || prop.interval,
) tableStore.table.data
} )
} }
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {
@@ -226,12 +254,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -0,0 +1,480 @@
<template>
<div>
<!--指标越限时间分布
-->
<TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
>
<template v-slot:select>
<el-form-item label="监测点">
<el-select size="small" filterable v-model="tableStore.table.params.lineId">
<el-option
v-for="item in lineList"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
</el-select>
</el-form-item>
</template>
</TableHeader>
<div v-loading="tableStore.table.loading">
<my-echart
class="tall"
v-if="lineShow"
:options="echartList1"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
<el-empty
v-else
description="暂无监测点"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
<!-- <my-echart
class="mt10"
:options="echartList1"
:style="{
width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
}"
/> -->
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, h } from 'vue'
import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { limitProbabilityData, cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
})
// const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
const lineList = ref()
const headerHeight = ref(57)
const TableHeaderRef = ref()
const lineShow = ref(true)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
}
}
// 计算是否全屏展示
const fullscreen = computed(() => {
const w = Number(prop.w)
const h = Number(prop.h)
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
// 执行相应逻辑
return true
} else {
return false
}
})
const echartList = ref()
const echartList1 = ref()
const probabilityData = ref()
const initLineList = async () => {
cslineList({}).then(res => {
if (res.data.length == 0) {
lineShow.value = false
return (tableStore.table.loading = false)
}
lineShow.value = true
lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId
tableStore.index()
})
}
// 越限程度概率分布
const initProbabilityData = () => {
// 只有当 lineList 已加载且有数据时才设置默认 lineId
if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) {
tableStore.table.params.lineId = lineList.value[0].lineId
}
const params = {
searchBeginTime: tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
searchEndTime: tableStore.table.params.searchEndTime || prop.timeValue?.[1],
lineId: tableStore.table.params.lineId
}
limitProbabilityData(params).then((res: any) => {
probabilityData.value = res.data
// 处理接口返回的数据,转换为图表所需格式
if (res.data && Array.isArray(res.data)) {
// 定义指标类型顺序
const indicatorOrder = ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相电压不平衡度', '频率偏差']
// 按照指定顺序排序数据
const sortedData = [...res.data].sort((a, b) => {
return indicatorOrder.indexOf(a.indexName) - indicatorOrder.indexOf(b.indexName)
})
// 构造 series 数据
const seriesData: any = []
let maxValue: any = 0 // 用于存储数据中的最大值
// 遍历每个越限程度区间0-20%, 20-40%, 40-60%, 60-80%, 80-100%
for (let xIndex = 0; xIndex < 5; xIndex++) {
// 遍历每个指标类型
sortedData.forEach((item, yIndex) => {
// 从 extentGrades 中获取对应区间的值
const extentGrade = item.extentGrades[xIndex]
const value = extentGrade ? (Object.values(extentGrade)[0] as number) : 0
seriesData.push([xIndex, yIndex, value])
// 更新最大值
if (value > maxValue) {
maxValue = value
}
})
}
// 计算 z 轴最大值(最大值加 5
const zAxisMax = Math.ceil(maxValue) + 5
// 构造 yAxis 数据(指标类型名称)
const yAxisData = sortedData.map(item => item.indexName)
echartList.value = {
title: {
text: '指标越限概率分布'
},
options: {
backgroundColor: '#fff',
tooltip: {
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter: function (params: any) {
var yIndex = params.value[1] //获取y轴索引
var tips = ''
tips += '指标类型: ' + yAxisData[yIndex] + '</br>'
tips += '越限程度: ' + params.seriesName + '</br>'
tips += '越限天数: ' + params.value[2] + '</br>'
return tips
}
},
// 移除或隐藏 visualMap 组件
visualMap: {
show: false, // 设置为 false 隐藏右侧颜色条
min: 0,
// max: 100,
max: zAxisMax, // 使用计算出的最大值加5
inRange: {
color: ['#313695', '#00BB00', '#ff8000', '#d73027', '#a50026']
}
},
// 添加 legend 配置并设置为不显示
legend: {
show: false // 隐藏图例
},
xAxis3D: {
type: 'category',
name: '越限程度',
nameLocation: 'middle',
nameGap: 50,
data: ['0-20%', '20-40%', '40-60%', '60-80%', '80-100%']
},
yAxis3D: {
type: 'category',
name: '指标类型',
nameLocation: 'middle',
nameGap: 50,
data: yAxisData,
splitLine: {
lineStyle: {
type: 'dashed',
opacity: 0.5
}
}
},
zAxis3D: {
type: 'value',
name: '越限天数',
nameLocation: 'middle',
nameGap: 30,
minInterval: 10
// max: 100
},
grid3D: {
viewControl: {
projection: 'perspective',
distance: 260,
rotateSensitivity: 10,
zoomSensitivity: 2
},
boxWidth: 150,
boxDepth: 100,
boxHeight: 100,
light: {
main: {
intensity: 1.2
},
ambient: {
intensity: 0.4
}
}
},
series: [
{
type: 'bar3D',
name: '0-20%',
data: seriesData.filter((item: any) => item[0] === 0),
shading: 'realistic',
label: {
show: false
},
itemStyle: {
opacity: 0.9
},
emphasis: {
label: {
show: true,
textStyle: {
fontSize: 14,
color: '#000'
}
},
itemStyle: {
color: '#ff8000'
}
}
},
{
type: 'bar3D',
name: '20-40%',
data: seriesData.filter((item: any) => item[0] === 1),
shading: 'realistic',
label: {
show: false
},
itemStyle: {
opacity: 0.9
},
emphasis: {
label: {
show: true,
textStyle: {
fontSize: 14,
color: '#000'
}
},
itemStyle: {
color: '#ff8000'
}
}
},
{
type: 'bar3D',
name: '40-60%',
data: seriesData.filter((item: any) => item[0] === 2),
shading: 'realistic',
label: {
show: false
},
itemStyle: {
opacity: 0.9
},
emphasis: {
label: {
show: true,
textStyle: {
fontSize: 14,
color: '#000'
}
},
itemStyle: {
color: '#ff8000'
}
}
},
{
type: 'bar3D',
name: '60-80%',
data: seriesData.filter((item: any) => item[0] === 3),
shading: 'realistic',
label: {
show: false
},
itemStyle: {
opacity: 0.9
},
emphasis: {
label: {
show: true,
textStyle: {
fontSize: 14,
color: '#000'
}
},
itemStyle: {
color: '#ff8000'
}
}
},
{
type: 'bar3D',
name: '80-100%',
data: seriesData.filter((item: any) => item[0] === 4),
shading: 'realistic',
label: {
show: false
},
itemStyle: {
opacity: 0.9
},
emphasis: {
label: {
show: true,
textStyle: {
fontSize: 14,
color: '#000'
}
},
itemStyle: {
color: '#ff8000'
}
}
}
]
}
}
}
})
}
const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/limitRateDetailD/limitTimeProbabilityData',
method: 'POST',
showPage: false,
column: [],
beforeSearchFun: () => {
setTime()
// 只有当 lineList 已加载且有数据时才设置默认 lineId
if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) {
tableStore.table.params.lineId = lineList.value[0].lineId
}
},
loadCallback: () => {
// 处理返回的数据,将其转换为图表所需格式
const indexNames: any = [...new Set(tableStore.table.data.map((item: any) => item.indexName))]
const timePeriods = [...new Set(tableStore.table.data.map((item: any) => item.timePeriod))]
// 构建系列数据
const seriesData = indexNames.map((indexName: string) => {
const dataIndex = tableStore.table.data.filter((item: any) => item.indexName === indexName)
return {
name: indexName,
type: 'line',
symbol: 'none',
data: dataIndex.map((item: any) => [item.timePeriod, item.times])
}
})
echartList1.value = {
title: {
text: '指标越限时间概率分布'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: indexNames
},
xAxis: {
type: 'category',
name: '时间段',
data: timePeriods
},
yAxis: {
type: 'value'
// name: '次数'
},
series: seriesData
}
initProbabilityData()
}
})
provide('tableStore', tableStore)
onMounted(() => {
initLineList()
})
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch(
() => prop.timeKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
(newVal, oldVal) => {
tableStore.index()
},
{
deep: true
}
)
const addMenu = () => {}
</script>
<style lang="scss" scoped></style>

View File

@@ -5,9 +5,9 @@
:showReset="false" :showReset="false"
ref="TableHeaderRef" ref="TableHeaderRef"
@selectChange="selectChange" @selectChange="selectChange"
datePicker datePicker
v-if="fullscreen" v-if="fullscreen"
:timeCacheFlag="false" :timeKeyList="prop.timeKey"
></TableHeader> ></TableHeader>
<el-calendar <el-calendar
v-model="value" v-model="value"
@@ -32,7 +32,7 @@
<template #content> <template #content>
<span v-html="getTextForDate(data.day)"></span> <span v-html="getTextForDate(data.day)"></span>
</template> </template>
<div class="details" v-html="getTextForDate(data.day)"></div> <div class="details" v-html="fullscreen ? getTextForDate(data.day) : '有越限'"></div>
</el-tooltip> </el-tooltip>
</div> </div>
</template> </template>
@@ -44,14 +44,16 @@ import { ref, onMounted, provide, reactive, watch, h } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { dayjs } from 'element-plus' import { dayjs } from 'element-plus'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -96,10 +98,7 @@ const tableStore: any = new TableStore({
showPage: false, showPage: false,
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
if (!fullscreen.value && prop.timeValue && Array.isArray(prop.timeValue)) { setTime()
tableStore.table.params.searchBeginTime = prop.timeValue[0]
tableStore.table.params.searchEndTime = prop.timeValue[1]
}
}, },
loadCallback: () => { loadCallback: () => {
value.value = tableStore.table.params.searchBeginTime value.value = tableStore.table.params.searchBeginTime
@@ -144,35 +143,34 @@ provide('tableStore', tableStore)
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
if (TableHeaderRef.value && typeof TableHeaderRef.value.setDatePicker === 'function') {
TableHeaderRef.value.setDatePicker([{ label: '月份', value: 3 }])
}
if (fullscreen.value) {
TableHeaderRef.value.setInterval(3)
}
tableStore.index() tableStore.index()
}) })
}) })
watch(
() => prop.timeKey, const setTime = () => {
val => { const time = getTime(
tableStore.index() (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
} }
) }
watch( watch(
() => prop.timeValue, () => prop.timeValue,
// (newVal, oldVal) => { (newVal, oldVal) => {
// // 当外部时间值变化时,更新表格的时间参数
// if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
// tableStore.table.params.searchBeginTime = newVal[0]
// tableStore.table.params.searchEndTime = newVal[1]
// tableStore.index()
// }
// },
val => {
tableStore.index() tableStore.index()
}, },
{ {
deep: true deep: true
} }

View File

@@ -1,10 +1,17 @@
<template> <template>
<div> <div>
<!--指标越限概率分布 --> <!--指标越限概率分布 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"> <TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
>
<template v-slot:select> <template v-slot:select>
<el-form-item label="监测点"> <el-form-item label="监测点">
<el-select size="small" v-model="tableStore.table.params.lineId"> <el-select size="small" filterable v-model="tableStore.table.params.lineId">
<el-option <el-option
v-for="item in lineList" v-for="item in lineList"
:key="item.lineId" :key="item.lineId"
@@ -17,21 +24,30 @@
</TableHeader> </TableHeader>
<div v-loading="tableStore.table.loading"> <div v-loading="tableStore.table.loading">
<my-echart <my-echart
v-if="lineShow"
class="tall" class="tall"
:options="echartList" :options="echartList"
:style="{ :style="{
width: prop.width, width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )` height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}" }"
/> />
<my-echart <el-empty
v-else
description="暂无监测点"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
<!-- <my-echart
class="mt10" class="mt10"
:options="echartList1" :options="echartList1"
:style="{ :style="{
width: prop.width, width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )` height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
}" }"
/> /> -->
</div> </div>
</div> </div>
</template> </template>
@@ -41,22 +57,26 @@ import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
import { limitProbabilityData, cslineList } from '@/api/harmonic-boot/cockpit/cockpit' import { limitProbabilityData, cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const lineShow = ref(true)
// const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]')) // const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
const lineList = ref() const lineList = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
const TableHeaderRef = ref()
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
@@ -87,6 +107,11 @@ const probabilityData = ref()
const initLineList = async () => { const initLineList = async () => {
cslineList({}).then(res => { cslineList({}).then(res => {
if (res.data.length == 0) {
lineShow.value = false
return (tableStore.table.loading = false)
}
lineShow.value = true
lineList.value = res.data lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId tableStore.table.params.lineId = lineList.value[0].lineId
tableStore.index() tableStore.index()
@@ -110,7 +135,7 @@ const initProbabilityData = () => {
// 处理接口返回的数据,转换为图表所需格式 // 处理接口返回的数据,转换为图表所需格式
if (res.data && Array.isArray(res.data)) { if (res.data && Array.isArray(res.data)) {
// 定义指标类型顺序 // 定义指标类型顺序
const indicatorOrder = ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相电压不平衡度', '频率偏差'] const indicatorOrder = ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相电压不平衡度', '频率偏差']
// 按照指定顺序排序数据 // 按照指定顺序排序数据
const sortedData = [...res.data].sort((a, b) => { const sortedData = [...res.data].sort((a, b) => {
return indicatorOrder.indexOf(a.indexName) - indicatorOrder.indexOf(b.indexName) return indicatorOrder.indexOf(a.indexName) - indicatorOrder.indexOf(b.indexName)
@@ -141,6 +166,9 @@ const initProbabilityData = () => {
const yAxisData = sortedData.map(item => item.indexName) const yAxisData = sortedData.map(item => item.indexName)
echartList.value = { echartList.value = {
title: {
text: '指标越限概率分布'
},
options: { options: {
backgroundColor: '#fff', backgroundColor: '#fff',
tooltip: { tooltip: {
@@ -157,18 +185,11 @@ const initProbabilityData = () => {
var tips = '' var tips = ''
tips += '指标类型: ' + yAxisData[yIndex] + '</br>' tips += '指标类型: ' + yAxisData[yIndex] + '</br>'
tips += '越限程度: ' + params.seriesName + '</br>' tips += '越限程度: ' + params.seriesName + '</br>'
tips += '越限数: ' + params.value[2] + '</br>' tips += '越限数: ' + params.value[2] + '</br>'
return tips return tips
} }
}, },
title: {
text: '指标越限概率分布',
x: 'center',
textStyle: {
fontSize: 16,
fontWeight: 'normal'
}
},
// 移除或隐藏 visualMap 组件 // 移除或隐藏 visualMap 组件
visualMap: { visualMap: {
show: false, // 设置为 false 隐藏右侧颜色条 show: false, // 设置为 false 隐藏右侧颜色条
@@ -187,43 +208,18 @@ const initProbabilityData = () => {
type: 'category', type: 'category',
name: '越限程度', name: '越限程度',
nameLocation: 'middle', nameLocation: 'middle',
nameGap: 30, nameGap: 50,
data: ['0-20%', '20-40%', '40-60%', '60-80%', '80-100%'], data: ['0-20%', '20-40%', '40-60%', '60-80%', '80-100%']
axisLine: {
lineStyle: {
color: '#111'
}
},
axisLabel: {
color: '#111',
margin: 15
},
nameTextStyle: {
color: '#111'
}
}, },
yAxis3D: { yAxis3D: {
type: 'category', type: 'category',
name: '指标类型', name: '指标类型',
nameLocation: 'middle', nameLocation: 'middle',
nameGap: 30, nameGap: 50,
data: yAxisData, data: yAxisData,
nameTextStyle: {
color: '#111'
},
axisLine: {
show: true,
lineStyle: {
color: '#111'
}
},
axisLabel: {
color: '#111',
margin: 15
},
splitLine: { splitLine: {
lineStyle: { lineStyle: {
color: ['#111'],
type: 'dashed', type: 'dashed',
opacity: 0.5 opacity: 0.5
} }
@@ -231,28 +227,17 @@ const initProbabilityData = () => {
}, },
zAxis3D: { zAxis3D: {
type: 'value', type: 'value',
name: '越限数', name: '越限数',
nameLocation: 'middle', nameLocation: 'middle',
nameGap: 30, nameGap: 30,
nameTextStyle: { minInterval: 10
color: '#111'
},
axisLine: {
lineStyle: {
color: '#111'
}
},
axisLabel: {
color: '#111'
},
min: 0,
max: zAxisMax // 使用计算出的最大值加5
// max: 100 // max: 100
}, },
grid3D: { grid3D: {
viewControl: { viewControl: {
projection: 'perspective', projection: 'perspective',
distance: 250, distance: 260,
rotateSensitivity: 10, rotateSensitivity: 10,
zoomSensitivity: 2 zoomSensitivity: 2
}, },
@@ -402,8 +387,7 @@ const tableStore: any = new TableStore({
showPage: false, showPage: false,
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
// 只有当 lineList 已加载且有数据时才设置默认 lineId // 只有当 lineList 已加载且有数据时才设置默认 lineId
if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) { if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) {
tableStore.table.params.lineId = lineList.value[0].lineId tableStore.table.params.lineId = lineList.value[0].lineId
@@ -427,7 +411,7 @@ const tableStore: any = new TableStore({
echartList1.value = { echartList1.value = {
title: { title: {
text: '越限时间概率分布' text: '指标越限时间概率分布'
}, },
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis'
@@ -455,6 +439,25 @@ provide('tableStore', tableStore)
onMounted(() => { onMounted(() => {
initLineList() initLineList()
}) })
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {
@@ -464,12 +467,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -16,6 +16,7 @@
v-model="searchForm.index" v-model="searchForm.index"
placeholder="请选择统计指标" placeholder="请选择统计指标"
@change="onIndexChange($event)" @change="onIndexChange($event)"
filterable
> >
<el-option <el-option
v-for="item in indexOptions" v-for="item in indexOptions"
@@ -36,6 +37,7 @@
style="min-width: 120px !important" style="min-width: 120px !important"
placeholder="请选择" placeholder="请选择"
v-model="searchForm.valueType" v-model="searchForm.valueType"
filterable
> >
<el-option value="max" label="最大值"></el-option> <el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option> <el-option value="min" label="最小值"></el-option>
@@ -59,11 +61,12 @@
placeholder="请选择谐波次数" placeholder="请选择谐波次数"
style="width: 100px" style="width: 100px"
class="mr20" class="mr20"
filterable
> >
<el-option <el-option
v-for="vv in item.countOptions" v-for="vv in item.countOptions"
:key="vv" :key="vv"
:label="vv" :label="item.name.includes('间谐波') ? vv - 0.5 : vv"
:value="vv" :value="vv"
></el-option> ></el-option>
</el-select> </el-select>
@@ -78,12 +81,8 @@
</template> </template>
</TableHeader> </TableHeader>
</div> </div>
<div class="history_chart" :style="pageHeight" v-loading="loading"> <div class="history_chart" :style="pageHeight" v-loading="loading">
<MyEchart <MyEchart ref="historyChart" :options="echartsData" v-if="showEchart" />
ref="historyChart"
:options="echartsData"
v-if="showEchart"
/>
<el-empty :style="pageHeight" v-else description="暂无数据" /> <el-empty :style="pageHeight" v-else description="暂无数据" />
</div> </div>
</el-dialog> </el-dialog>
@@ -157,28 +156,40 @@ const countOptions: any = ref([])
const legendDictList: any = ref([]) const legendDictList: any = ref([])
const initCode = (field: string, title: string) => { const initCode = (field: string, title: string) => {
queryByCode('steady_state_limit_trend').then(res => { queryByCode('gridSide_exceedTheLimit').then(res => {
queryCsDictTree(res.data.id).then(item => { queryCsDictTree(res.data.id).then(item => {
//排序 //排序
indexOptions.value = item.data.sort((a: any, b: any) => { indexOptions.value = item.data.sort((a: any, b: any) => {
return a.sort - b.sort return a.sort - b.sort
}) })
const titleMap: Record<string, number> = { // const titleMap: Record<string, number> = {
flickerOvertime: 0, // flickerOvertime: 0,
uaberranceOvertime: 3, // uaberranceOvertime: 3,
ubalanceOvertime: 4, // ubalanceOvertime: 4,
freqDevOvertime: 5 // freqDevOvertime: 5
} // }
let defaultIndex = 0 // 默认值 // let defaultIndex = 0 // 默认值
if (field in titleMap) { // if (field in titleMap) {
defaultIndex = titleMap[field] // defaultIndex = titleMap[field]
} else if (field.includes('uharm')) { // } else if (field.includes('uharm')) {
defaultIndex = 1 // defaultIndex = 1
} else if (field.includes('iharm')) { // } else if (field.includes('iharm')) {
defaultIndex = 2 // defaultIndex = 2
} // }
let codeKey = field.includes('flickerOvertime')
? '闪变'
: field.includes('uharm')
? '谐波电压'
: field.includes('iharm')
? '谐波电流'
: field.includes('voltageDevOvertime')
? '电压偏差'
: field.includes('ubalanceOvertime')
? '不平衡'
: ''
let defaultIndex = indexOptions.value.findIndex((item: any) => item.name.includes(codeKey)) || 0
searchForm.value.index[0] = indexOptions.value[defaultIndex].id searchForm.value.index[0] = indexOptions.value[defaultIndex].id
}) })
@@ -291,7 +302,7 @@ const init = async () => {
} }
lists[index] = { lists[index] = {
statisticalId: item.index, statisticalId: item.index,
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : '' frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
} }
}) })
let obj = { let obj = {
@@ -576,7 +587,6 @@ const setTimeControl = () => {
} }
const selectChange = (flag: boolean, height: any) => { const selectChange = (flag: boolean, height: any) => {
pageHeight.value = mainHeight(height * 1.6, 1.6) pageHeight.value = mainHeight(height * 1.6, 1.6)
} }
//导出 //导出

View File

@@ -9,11 +9,12 @@
v-model="tableStore.table.params.lineId" v-model="tableStore.table.params.lineId"
placeholder="请选择监测点" placeholder="请选择监测点"
style="width: 150px" style="width: 150px"
filterable
> >
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.lineId" :key="item.lineId"
:label="item.name" :label="item.lineName"
:value="item.lineId" :value="item.lineId"
/> />
</el-select> </el-select>
@@ -23,11 +24,7 @@
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table> <Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
</el-dialog> </el-dialog>
<!-- 谐波电流谐波电压占有率 --> <!-- 谐波电流谐波电压占有率 -->
<HarmonicRatio <HarmonicRatio ref="harmonicRatioRef" @close="onHarmonicRatioClose" v-if="dialogFlag" />
ref="harmonicRatioRef"
@close="onHarmonicRatioClose"
v-if="dialogFlag"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -53,7 +50,7 @@ const loop50 = (key: string) => {
list.push({ list.push({
title: i + '次', title: i + '次',
field: key + i + 'Overtime', field: key + i + 'Overtime',
width: '80', width: '60',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
@@ -89,21 +86,21 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(分钟)', title: '越限(分钟)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '80', width: '90',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
} }
}, }, {
{ title: '电压偏差越限(分钟)',
title: '谐波电压越限(分钟)', field: 'uaberranceOvertime',
children: loop50('uharm') width: '100',
}, render: 'customTemplate',
{ customTemplate: (row: any) => {
title: '谐波电流越限(分钟)', return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
children: loop50('iharm') }
}, },
{ {
title: '三相不平衡度越限(分钟)', title: '三相不平衡度越限(分钟)',
@@ -115,23 +112,23 @@ const tableStore: any = new TableStore({
} }
}, },
{ {
title: '电压偏差越限(分钟)', title: '谐波电压越限(分钟)',
field: 'uaberranceOvertime', children: loop50('uharm')
width: '100',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '频率偏差越限(分钟)', title: '谐波电流越限(分钟)',
field: 'freqDevOvertime', children: loop50('iharm')
width: '100', },
render: 'customTemplate',
customTemplate: (row: any) => { // {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>` // title: '频率偏差越限(分钟)',
} // field: 'freqDevOvertime',
} // width: '100',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
// }
// }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
}, },
@@ -143,9 +140,10 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.sortBy = '' tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = '' tableStore.table.params.orderBy = ''
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => { const open = async (row: any,searchBeginTime:any,searchEndTime:any,data:any=[]) => {
dialogVisible.value = true dialogVisible.value = true
initCSlineList() // initCSlineList()
options.value = data
tableStore.table.params.lineId = row.lineId tableStore.table.params.lineId = row.lineId
nextTick(() => { nextTick(() => {
@@ -179,8 +177,8 @@ const onHarmonicRatioClose = () => {
} }
const initCSlineList = async () => { const initCSlineList = async () => {
const res = await cslineList({}) // const res = await cslineList({})
options.value = res.data // options.value = res.data
} }

View File

@@ -1,10 +1,17 @@
<template> <template>
<div> <div>
<!--主要监测点列表 --> <!--主要监测点列表 -->
<TableHeader :showReset="false" @selectChange="selectChange" v-if="fullscreen" datePicker ref="tableHeaderRef"> <TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
@selectChange="selectChange"
v-if="fullscreen"
ref="TableHeaderRef"
>
<template v-slot:select> <template v-slot:select>
<el-form-item label="关键"> <el-form-item label="关键字筛选">
<el-input v-model="tableStore.table.params.keywords" clearable placeholder="请输关键字" /> <el-input v-model="tableStore.table.params.keywords" clearable placeholder="请输入监测点名称" />
</el-form-item> </el-form-item>
</template> </template>
</TableHeader> </TableHeader>
@@ -26,15 +33,15 @@ import { getTimeOfTheMonth } from '@/utils/formatTime'
import OverLimitDetails from '@/components/cockpit/indicatorFittingChart/components/overLimitDetails.vue' import OverLimitDetails from '@/components/cockpit/indicatorFittingChart/components/overLimitDetails.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useTimeCacheStore } from '@/stores/timeCache' import { useTimeCacheStore } from '@/stores/timeCache'
import { log } from 'console'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const OverLimitDetailsRef = ref() const OverLimitDetailsRef = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -42,7 +49,7 @@ const headerHeight = ref(57)
const route = useRoute() const route = useRoute()
const timeCacheStore = useTimeCacheStore() const timeCacheStore = useTimeCacheStore()
const tableHeaderRef = ref() const TableHeaderRef = ref()
// 计算是否全屏展示 // 计算是否全屏展示
const fullscreen = computed(() => { const fullscreen = computed(() => {
@@ -59,11 +66,11 @@ const fullscreen = computed(() => {
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) { // if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数 // // 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0] // tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1] // tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
} // }
} }
const tableStore: any = new TableStore({ const tableStore: any = new TableStore({
@@ -93,19 +100,24 @@ const tableStore: any = new TableStore({
{ {
title: '监测对象类型', title: '监测对象类型',
field: 'objType', field: 'objType',
minWidth: '90' minWidth: '90',
formatter: (row: any) => {
return row.cellValue || '/'
}
}, },
{ {
title: '是否治理', title: '是否治理',
field: 'govern', field: 'govern',
minWidth: '70' minWidth: '80',
formatter: (row: any) => {
return row.cellValue || '/'
}
}, },
{ title: '主要存在的电能质量问题', field: 'problems', minWidth: '150', showOverflow: true } { title: '主要存在的电能质量问题', field: 'problems', minWidth: '150', showOverflow: true }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.height = `calc(${prop.height} - 80px)` tableStore.table.height = `calc(${prop.height} - 80px)`
@@ -120,33 +132,45 @@ provide('tableStore', tableStore)
// 点击行 // 点击行
const cellClickEvent = ({ row, column }: any) => { const cellClickEvent = ({ row, column }: any) => {
if (column.field == 'lineName') { if (column.field == 'lineName') {
let time = getTimeOfTheMonth('3');
OverLimitDetailsRef.value.open( OverLimitDetailsRef.value.open(
row, row,
tableStore.table.params.searchBeginTime || prop.timeValue?.[0], time[0],
tableStore.table.params.searchEndTime || prop.timeValue?.[1] time[1],
tableStore.table.data
) )
} }
} }
const setTime = () => {
// const time = getTime(
// (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
// prop.timeKey,
// fullscreen.value
// ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
// : prop.timeValue
// )
// if (Array.isArray(time)) {
// tableStore.table.params.searchBeginTime = time[0]
// tableStore.table.params.searchEndTime = time[1]
// // TableHeaderRef.value?.setInterval(time[2] - 0)
// // TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
// } else {
// console.warn('获取时间失败time 不是一个有效数组')
// }
}
// 在组件挂载时设置缓存值到 DatePicker // 在组件挂载时设置缓存值到 DatePicker
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
watch(
() => prop.timeKey,
val => {
tableStore.index()
}
)
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -69,19 +69,11 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(分钟)', title: '长时闪变越限(分钟)',
field: 'flicker', field: 'flicker',
width: '80' width: '80'
}, },
{ {
title: '谐波电压越限(分钟)',
children: loop50('voltage')
},
{
title: '谐波电流越限(分钟)',
children: loop50('harmonicCurrent')
},
{
title: '三相不平衡度越限(分钟)', title: '三相不平衡度越限(分钟)',
field: 'flicker', field: 'flicker',
width: '100' width: '100'
@@ -95,30 +87,20 @@ const tableStore: any = new TableStore({
title: '频率偏差越限(分钟)', title: '频率偏差越限(分钟)',
field: 'flicker', field: 'flicker',
width: '100' width: '100'
} },
{
title: '谐波电压越限(分钟)',
children: loop50('voltage')
},
{
title: '谐波电流越限(分钟)',
children: loop50('harmonicCurrent')
},
], ],
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => { loadCallback: () => {
tableStore.table.data = [
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0',
},
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0',
},
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0',
},
]
} }
}) })

View File

@@ -84,7 +84,7 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(分钟)', title: '长时闪变越限(分钟)',
field: 'flicker', field: 'flicker',
width: '80', width: '80',
render: 'customTemplate', render: 'customTemplate',
@@ -92,15 +92,7 @@ const tableStore: any = new TableStore({
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flicker}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.flicker}</span>`
} }
}, },
{ {
title: '谐波电压越限(分钟)',
children: loop50('voltage')
},
{
title: '谐波电流越限(分钟)',
children: loop50('harmonicCurrent')
},
{
title: '三相不平衡度越限(分钟)', title: '三相不平衡度越限(分钟)',
field: 'flicker', field: 'flicker',
width: '100' width: '100'
@@ -114,7 +106,16 @@ const tableStore: any = new TableStore({
title: '频率偏差越限(分钟)', title: '频率偏差越限(分钟)',
field: 'flicker', field: 'flicker',
width: '100' width: '100'
} },
{
title: '谐波电压越限(分钟)',
children: loop50('voltage')
},
{
title: '谐波电流越限(分钟)',
children: loop50('harmonicCurrent')
},
], ],
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => { loadCallback: () => {

View File

@@ -1,10 +1,16 @@
<template> <template>
<div> <div>
<!--指标拟合图 --> <!--指标拟合图 -->
<TableHeader datePicker @selectChange="selectChange" v-if="fullscreen"> <TableHeader
datePicker
@selectChange="selectChange"
v-if="fullscreen"
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
>
<template v-slot:select> <template v-slot:select>
<el-form-item label="监测点"> <el-form-item label="监测点">
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable> <el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable>
<el-option <el-option
v-for="item in lineList" v-for="item in lineList"
:key="item.lineId" :key="item.lineId"
@@ -14,7 +20,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="用户功率"> <el-form-item label="用户功率">
<el-select v-model="tableStore.table.params.power" placeholder="请选择用户功率" clearable> <el-select
filterable
v-model="tableStore.table.params.power"
placeholder="请选择用户功率"
clearable
>
<el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -23,6 +34,7 @@
style="min-width: 120px !important" style="min-width: 120px !important"
placeholder="请选择" placeholder="请选择"
v-model="tableStore.table.params.valueType" v-model="tableStore.table.params.valueType"
filterable
> >
<el-option value="max" label="最大值"></el-option> <el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option> <el-option value="min" label="最小值"></el-option>
@@ -31,7 +43,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="电能质量指标"> <el-form-item label="电能质量指标">
<el-select v-model="tableStore.table.params.indicator" placeholder="请选择电能质量指标" clearable> <el-select
filterable
v-model="tableStore.table.params.indicator"
placeholder="请选择电能质量指标"
clearable
>
<el-option v-for="item in indicatorList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in indicatorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -42,6 +59,7 @@
v-model="tableStore.table.params.harmonicCount" v-model="tableStore.table.params.harmonicCount"
placeholder="请选择谐波次数" placeholder="请选择谐波次数"
style="min-width: 80px !important" style="min-width: 80px !important"
filterable
> >
<el-option <el-option
v-for="num in harmonicCountOptions" v-for="num in harmonicCountOptions"
@@ -57,12 +75,21 @@
<div v-loading="tableStore.table.loading"> <div v-loading="tableStore.table.loading">
<my-echart <my-echart
class="tall" class="tall"
v-if="lineShow"
:options="echartList" :options="echartList"
:style="{ :style="{
width: prop.width, width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )` height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`
}" }"
/> />
<el-empty
v-else
description="暂无监测点"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
</div> </div>
</div> </div>
</template> </template>
@@ -75,26 +102,28 @@ import { useConfig } from '@/stores/config'
import { cslineList, fittingData } from '@/api/harmonic-boot/cockpit/cockpit' import { cslineList, fittingData } from '@/api/harmonic-boot/cockpit/cockpit'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree' import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const TableHeaderRef = ref()
const config = useConfig() const config = useConfig()
const lineList: any = ref() const lineList: any = ref()
const powerList: any = ref() const powerList: any = ref()
const countData: any = ref([])
const chartsList = ref<any>([]) const chartsList = ref<any>([])
const lineShow = ref(true)
// 计算是否全屏展示 // 计算是否全屏展示
const fullscreen = computed(() => { const fullscreen = computed(() => {
const w = Number(prop.w) const w = Number(prop.w)
@@ -121,8 +150,15 @@ const indicatorList = ref()
const initLineList = async () => { const initLineList = async () => {
cslineList({}).then(res => { cslineList({}).then(res => {
setTime()
if (res.data.length == 0) {
lineShow.value = false
return (tableStore.table.loading = false)
}
lineShow.value = true
lineList.value = res.data lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId tableStore.table.params.lineId = lineList.value[0].lineId
initCode()
}) })
} }
@@ -131,7 +167,6 @@ const echartList = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) { if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数 // 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0] tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
@@ -149,6 +184,30 @@ const setEchart = () => {
title: { title: {
text: `${indicatorName}${powerName}负荷曲线拟合图` text: `${indicatorName}${powerName}负荷曲线拟合图`
}, },
tooltip: {
trigger: 'axis',
formatter: function (params: any) {
let result = params[0].axisValueLabel
params.forEach((item: any) => {
if (item.seriesName === indicatorName) {
// 对于电能质量指标格式化Y轴值显示
let valueText = ''
if (item.value[1] == 0) {
valueText = '不越限'
} else if (item.value[1] == 1) {
valueText = '越限'
} else {
valueText = item.value[1]
}
result += `<br/>${item.marker}${item.seriesName}: ${valueText}`
} else {
// 对于功率数据,正常显示数值
result += `<br/>${item.marker}${item.seriesName}: ${item.value[1]} ${item.value[2]}`
}
})
return result
}
},
xAxis: { xAxis: {
type: 'time', type: 'time',
axisLabel: { axisLabel: {
@@ -159,7 +218,25 @@ const setEchart = () => {
} }
} }
}, },
yAxis: [{}, {}], yAxis: [
{},
indicatorName
? {
min: 0,
max: 1,
axisLabel: {
formatter: function (value: number) {
if (value === 0) {
return '不越限'
} else if (value === 1) {
return '越限'
}
return value
}
}
}
: {}
],
grid: { grid: {
left: '10px', left: '10px',
right: '20px' right: '20px'
@@ -186,6 +263,7 @@ const setEchart = () => {
{ {
name: indicatorName, // 动态设置指标名称 name: indicatorName, // 动态设置指标名称
type: 'line', type: 'line',
step: 'end',
showSymbol: false, showSymbol: false,
// smooth: true, // smooth: true,
data: [], data: [],
@@ -215,7 +293,8 @@ const setEchart = () => {
item.time, item.time,
item.statisticalData !== null && item.statisticalData !== undefined item.statisticalData !== null && item.statisticalData !== undefined
? parseFloat(item.statisticalData.toFixed(2)) ? parseFloat(item.statisticalData.toFixed(2))
: null : null,
item.unit
] ]
}) })
@@ -225,7 +304,8 @@ const setEchart = () => {
item.time, item.time,
item.statisticalData !== null && item.statisticalData !== undefined item.statisticalData !== null && item.statisticalData !== undefined
? parseFloat(item.statisticalData.toFixed(2)) ? parseFloat(item.statisticalData.toFixed(2))
: null : null,
item.unit
] ]
}) })
@@ -255,6 +335,7 @@ const initCode = () => {
tableStore.table.params.power = powerList.value[0].id tableStore.table.params.power = powerList.value[0].id
tableStore.table.params.indicator = indicatorList.value[0].id tableStore.table.params.indicator = indicatorList.value[0].id
nextTick(() => { nextTick(() => {
// setTime()
tableStore.index() tableStore.index()
}) })
}) })
@@ -268,10 +349,7 @@ const tableStore: any = new TableStore({
exportName: '主要监测点列表', exportName: '主要监测点列表',
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
// 设置时间参数 setTime()
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0]
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
// 只有当 lineList 已加载且有数据时才设置默认 lineId // 只有当 lineList 已加载且有数据时才设置默认 lineId
if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) { if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) {
tableStore.table.params.lineId = lineList.value[0].lineId tableStore.table.params.lineId = lineList.value[0].lineId
@@ -372,33 +450,44 @@ watch(
} }
) )
onMounted(() => { onMounted(async () => {
initLineList().then(() => { await initLineList()
initCode()
})
}) })
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {
tableStore.index() tableStore.index()
} }
) )
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true
} }
) )
const addMenu = () => {}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// :deep(.el-select) { // :deep(.el-select) {

View File

@@ -9,11 +9,12 @@
v-model="tableStore.table.params.lineId" v-model="tableStore.table.params.lineId"
placeholder="请选择监测点" placeholder="请选择监测点"
style="width: 150px" style="width: 150px"
filterable
> >
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.lineId" :key="item.lineId"
:label="item.name" :label="item.lineName"
:value="item.lineId" :value="item.lineId"
/> />
</el-select> </el-select>
@@ -33,7 +34,7 @@ import TableHeader from '@/components/table/header/index.vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import HarmonicRatio from '@/components/cockpit/gridSideStatistics/components/harmonicRatio.vue' import HarmonicRatio from '@/components/cockpit/gridSideStatistics/components/harmonicRatio.vue'
import { cslineList } from '@/api/harmonic-boot/cockpit/cockpit' import { cslineList ,governLineList} from '@/api/harmonic-boot/cockpit/cockpit'
const dialogVisible: any = ref(false) const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null) const harmonicRatioRef: any = ref(null)
@@ -48,7 +49,7 @@ const loop50 = (key: string) => {
list.push({ list.push({
title: i + '次', title: i + '次',
field: key + i + 'Overtime', field: key + i + 'Overtime',
width: '80', width: '60',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
@@ -84,21 +85,22 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(%)', title: '长时闪变越限(%)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '80', width: '90',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
} }
}, },
{ {
title: '谐波电压越限(%)', title: '电压偏差越限(%)',
children: loop50('uharm') field: 'voltageDevOvertime',
}, width: '100',
{ render: 'customTemplate',
title: '谐波电流越限(%)', customTemplate: (row: any) => {
children: loop50('iharm') return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '三相不平衡度越限(%)', title: '三相不平衡度越限(%)',
@@ -110,23 +112,24 @@ const tableStore: any = new TableStore({
} }
}, },
{ {
title: '电压偏差越限(%)', title: '谐波电压越限(%)',
field: 'voltageDevOvertime', children: loop50('uharm')
width: '100',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '频率偏差越限(%)', title: '谐波电流越限(%)',
field: 'freqDevOvertime', children: loop50('iharm')
width: '100', },
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>` // {
} // title: '频率偏差越限(%)',
} // field: 'freqDevOvertime',
// width: '100',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
// }
// }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
}, },
@@ -138,8 +141,10 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.sortBy = '' tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = '' tableStore.table.params.orderBy = ''
const time:any=ref([])
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => { const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
dialogVisible.value = true dialogVisible.value = true
time.value=[searchBeginTime,searchEndTime]
initCSlineList() initCSlineList()
tableStore.table.params.lineId = row.lineId tableStore.table.params.lineId = row.lineId
@@ -174,8 +179,19 @@ const onHarmonicRatioClose = () => {
} }
const initCSlineList = async () => { const initCSlineList = async () => {
const res = await cslineList({}) // const res = await cslineList({})
options.value = res.data const res = await governLineList({
endTime:time.value[1],
keywords:"",
pageNum:1,
pageSize:10000,
searchBeginTime:time.value[0],
searchEndTime:time.value[1],
startTime:time.value[0],
timeFlag:1
})
options.value = res.data.records
} }

View File

@@ -1,7 +1,26 @@
<template> <template>
<div> <div>
<!-- 监测点列表 --> <!-- 监测点列表 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
:timeKeyList="prop.timeKey"
>
<template #select>
<el-form-item label="关键字筛选">
<el-input
maxlength="32"
show-word-limit
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable
placeholder="请输入监测点名称"
/>
</el-form-item>
</template>
</TableHeader>
<Table <Table
ref="tableRef" ref="tableRef"
@cell-click="cellClickEvent" @cell-click="cellClickEvent"
@@ -11,27 +30,35 @@
<OverLimitDetails ref="OverLimitDetailsRef" /> <OverLimitDetails ref="OverLimitDetailsRef" />
<!-- 上传对话框 --> <!-- 上传对话框 -->
<el-dialog v-model="uploadDialogVisible" title="上传报告" width="500px" @closed="handleDialogClosed"> <el-dialog
v-model="uploadDialogVisible"
title="上传报告"
append-to-body
width="500px"
@closed="handleDialogClosed"
>
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
class="upload-demo" class="upload-demo"
:auto-upload="true" action=""
accept=".doc,.docx,.PDF"
:on-change="handleChange" :on-change="handleChange"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:http-request="handleUpload"
:limit="1" :limit="1"
:auto-upload="false"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:on-remove="handleRemove"
:file-list="fileList" :file-list="fileList"
> >
<el-button type="primary">点击上传</el-button> <el-button type="primary">点击上传</el-button>
<template #tip> <template #tip>
<div class="el-upload__tip">只能上传Word或PDF文件且不超过10MB</div> <div class="el-upload__tip">上传Word或PDF文件</div>
</template> </template>
</el-upload> </el-upload>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="uploadDialogVisible = false">取消</el-button> <el-button @click="uploadDialogVisible = false">取消</el-button>
<el-button type="primary" @click="uploadDialogVisible = false">确定</el-button> <el-button type="primary" @click="handleUpload">确定</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@@ -46,18 +73,22 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import OverLimitDetails from '@/components/cockpit/monitoringPointList/components/overLimitDetails.vue' import OverLimitDetails from '@/components/cockpit/monitoringPointList/components/overLimitDetails.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { uploadReport, getReportUrl } from '@/api/harmonic-boot/cockpit/cockpit' import { uploadReport, getReportUrl } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
const TableHeaderRef = ref()
// 上传相关 // 上传相关
const uploadDialogVisible = ref(false) const uploadDialogVisible = ref(false)
const currentUploadRow = ref<any>(null) const currentUploadRow = ref<any>(null)
@@ -67,11 +98,11 @@ const fileList = ref([])
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) { // if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数 // // 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0] // tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1] // tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
} // }
} }
// 计算是否全屏展示 // 计算是否全屏展示
@@ -101,51 +132,87 @@ const tableStore: any = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{
title: '治理对象',
field: 'sensitiveUser',
minWidth: '80'
},
{
title: '电压等级',
field: 'volGrade',
minWidth: '70'
},
{
title: '是否治理',
field: 'govern',
minWidth: '70'
},
// {
// title: '治理前报告',
// field: 'reportFileName',
// minWidth: '80',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.reportFileName}</span>`
// }
// },
{ {
title: '监测点名称', title: '监测点名称',
field: 'lineName', field: 'lineName',
minWidth: '70', minWidth: '120',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.lineName}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.lineName}</span>`
} }
}, },
{ title: '监测类型', field: 'position', minWidth: '60' }, {
title: '监测类型',
field: 'position',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue || '/'
}
},
// {
// title: '监测点状态',
// field: 'runStatus',
// minWidth: '90',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='color: ${row.runStatus === '中断' ? '#FF0000' : ''}'>${row.runStatus==null?'/':row.runStatus}</span>`
// }
// },
{ {
title: '监测点状态', title: '监测点状态',
field: 'runStatus', field: 'runStatus',
minWidth: '60', render: 'tag',
render: 'customTemplate',
customTemplate: (row: any) => { width: 100,
return `<span style='color: ${row.runStatus === '中断' ? '#FF0000' : ''}'>${row.runStatus}</span>` custom: {
停运: 'danger',
退运: 'danger',
运行: 'success',
在线: 'success',
中断: 'warning',
离线: 'danger',
检修: 'warning',
调试: 'warning',
null: 'info'
},
replaceValue: {
运行: '运行',
在线: '在线',
退运: '退运',
停运: '停运',
中断: '中断',
检修: '检修',
离线: '离线',
调试: '调试',
null: '/'
} }
}, },
{
title: '治理对象',
field: 'sensitiveUser',
minWidth: '90',
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '电压等级',
field: 'volGrade',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue == 0 ? '/' : row.cellValue + 'kV' || '/'
}
},
{
title: '是否治理',
field: 'govern',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{ {
title: '最新数据时间', title: '最新数据时间',
field: 'latestTime', field: 'latestTime',
@@ -159,10 +226,31 @@ const tableStore: any = new TableStore({
} }
} }
}, },
// {
// title: '报告',
// field: 'reportFilePath',
// minWidth: '120',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return row.reportFilePath == null
// ? '/'
// : `<span style='cursor: pointer;text-decoration: underline;'>${row.reportFilePath
// .split('/')
// .pop()}</span>`
// }
// },
{
title: '报告',
field: 'reportFilePath',
minWidth: '120',
formatter: (row: any) => {
return row.cellValue == null ? '/' : row.cellValue.split('/').pop()
}
},
{ {
title: '操作', title: '操作',
minWidth: 120, fixed: 'right',
// fixed: 'right', width: 150,
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -186,7 +274,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-EditPen', icon: 'el-icon-EditPen',
render: 'basicButton', render: 'basicButton',
click: row => { click: row => {
downloadTheReport(row.lineId) downloadTheReport(row.lineId, row.reportFilePath)
}, },
disabled: row => { disabled: row => {
return row.reportFilePath == null || row.reportFilePath.length == 0 return row.reportFilePath == null || row.reportFilePath.length == 0
@@ -209,8 +297,7 @@ const tableStore: any = new TableStore({
} }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.height = `calc(${prop.height} - 80px)` tableStore.table.height = `calc(${prop.height} - 80px)`
@@ -220,8 +307,27 @@ const tableStore: any = new TableStore({
const tableRef = ref() const tableRef = ref()
provide('tableRef', tableRef) provide('tableRef', tableRef)
tableStore.table.params.keywords = '' tableStore.table.params.keywords = ''
tableStore.table.params.searchValue = ''
provide('tableStore', tableStore) provide('tableStore', tableStore)
const setTime = () => {
// const time = getTime(
// (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
// prop.timeKey,
// fullscreen.value
// ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
// : prop.timeValue
// )
// if (Array.isArray(time)) {
// tableStore.table.params.searchBeginTime = time[0]
// tableStore.table.params.searchEndTime = time[1]
// TableHeaderRef.value?.setInterval(time[2] - 0)
// TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
// } else {
// console.warn('获取时间失败time 不是一个有效数组')
// }
}
// 点击行 // 点击行
const cellClickEvent = ({ row, column }: any) => { const cellClickEvent = ({ row, column }: any) => {
if (column.field == 'lineName') { if (column.field == 'lineName') {
@@ -234,16 +340,43 @@ const cellClickEvent = ({ row, column }: any) => {
} }
// 下载报告 // 下载报告
const downloadTheReport = (lineId: string) => { const downloadTheReport = (lineId: string, name: string) => {
getReportUrl({ lineId: lineId }).then((res: any) => { getReportUrl({ lineId: lineId }).then((res: any) => {
const link = document.createElement('a') forceDownloadPdf(res.data, name.split('/').pop() || '')
link.href = res.data
link.download = '治理报告'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}) })
} }
const forceDownloadPdf = async (pdfUrl, fileName = '文件.pdf') => {
try {
// 1. 请求 PDF 并转为 Blob关键绕开浏览器直接解析
const response = await fetch(pdfUrl, {
method: 'GET'
// 若需要鉴权,添加请求头(如 token
})
// 校验响应是否成功
if (!response.ok) throw new Error(`请求失败:${response.status}`)
// 2. 将响应转为 Blob指定类型为 PDF确保兼容性
const blob = await response.blob()
const pdfBlob = new Blob([blob], { type: 'application/pdf' })
// 3. 创建临时 URL 并触发下载
const blobUrl = URL.createObjectURL(pdfBlob)
const a = document.createElement('a')
a.href = blobUrl
a.download = fileName // 此时 Blob URL 是同源的download 必生效
a.style.display = 'none'
document.body.appendChild(a)
a.click() // 触发下载
// 4. 清理资源(避免内存泄漏)
document.body.removeChild(a)
URL.revokeObjectURL(blobUrl)
} catch (error) {
console.error('PDF 下载失败:', error)
// ElMessage.error('文件下载失败,请检查网络或文件地址') // 适配 Element Plus
}
}
// 上传报告 // 上传报告
const uploadReportRow = (row: any) => { const uploadReportRow = (row: any) => {
@@ -260,14 +393,18 @@ const handleDialogClosed = () => {
} }
// 处理文件超出限制 // 处理文件超出限制
const handleExceed = (files: any, fileList: any) => { const handleExceed = (files: any) => {
ElMessage.warning('只能上传一个文件,请先删除已选择的文件') ElMessage.warning('只能上传一个文件,请先删除已选择的文件')
} }
const handleRemove = (files: any) => {
fileList.value = []
}
// 文件变更处理函数 // 文件变更处理函数
const handleChange = (file: any, fileList: any) => { const handleChange = (file: any) => {
// 在这里直接处理文件上传逻辑 // 在这里直接处理文件上传逻辑
beforeUpload(file.raw) // 注意使用 file.raw 获取原始文件对象 // beforeUpload(file.raw) // 注意使用 file.raw 获取原始文件对象
fileList.value = [file] // 只保留最新选择的文件
} }
// 处理上传前检查 // 处理上传前检查
@@ -284,11 +421,7 @@ const beforeUpload = (file: any) => {
const isLt10M = file.size / 1024 / 1024 < 10 const isLt10M = file.size / 1024 / 1024 < 10
if (!isValidType) { if (!isValidType) {
ElMessage.error('上传文件只能是 Word 文档(.doc/.docx) 或 PDF 文件(.pdf)!') ElMessage.error('上传(.doc/.docx/.pdf)格式文件!')
return false
}
if (!isLt10M) {
ElMessage.error('上传文件大小不能超过 10MB!')
return false return false
} }
@@ -296,10 +429,11 @@ const beforeUpload = (file: any) => {
return true return true
} }
const handleUpload = async (options: any) => { const handleUpload = async () => {
const { file } = options // return
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', fileList.value[0]?.raw)
formData.append('lineId', currentUploadRow.value?.lineId || currentUploadRow.value?.id || '') formData.append('lineId', currentUploadRow.value?.lineId || currentUploadRow.value?.id || '')
try { try {
@@ -326,12 +460,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -1,5 +1,5 @@
<template> <template>
<el-dialog draggable title="趋势图" v-model="dialogVisible" append-to-body width="70%"> <el-dialog draggable :title="titles" v-model="dialogVisible" append-to-body width="70%">
<!-- 总体指标占比详情谐波含有率 --> <!-- 总体指标占比详情谐波含有率 -->
<div> <div>
<TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange"> <TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange">
@@ -7,7 +7,7 @@
<el-form-item> <el-form-item>
<DatePicker ref="datePickerRef"></DatePicker> <DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> </el-form-item>
<el-form-item label="统计指标" label-width="80px"> <el-form-item label="统计指标" label-width="80px" v-if="props.showIndex">
<el-select <el-select
multiple multiple
:multiple-limit="2" :multiple-limit="2"
@@ -16,6 +16,7 @@
v-model="searchForm.index" v-model="searchForm.index"
placeholder="请选择统计指标" placeholder="请选择统计指标"
@change="onIndexChange($event)" @change="onIndexChange($event)"
filterable
> >
<el-option <el-option
v-for="item in indexOptions" v-for="item in indexOptions"
@@ -36,6 +37,7 @@
style="min-width: 120px !important" style="min-width: 120px !important"
placeholder="请选择" placeholder="请选择"
v-model="searchForm.valueType" v-model="searchForm.valueType"
filterable
> >
<el-option value="max" label="最大值"></el-option> <el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option> <el-option value="min" label="最小值"></el-option>
@@ -59,11 +61,12 @@
placeholder="请选择谐波次数" placeholder="请选择谐波次数"
style="width: 100px" style="width: 100px"
class="mr20" class="mr20"
filterable
> >
<el-option <el-option
v-for="vv in item.countOptions" v-for="vv in item.countOptions"
:key="vv" :key="vv"
:label="vv" :label="item.name.includes('间谐波') ? vv - 0.5 : vv"
:value="vv" :value="vv"
></el-option> ></el-option>
</el-select> </el-select>
@@ -79,11 +82,7 @@
</TableHeader> </TableHeader>
</div> </div>
<div class="history_chart" :style="pageHeight" v-loading="loading"> <div class="history_chart" :style="pageHeight" v-loading="loading">
<MyEchart <MyEchart ref="historyChart" :options="echartsData" v-if="showEchart" />
ref="historyChart"
:options="echartsData"
v-if="showEchart"
/>
<el-empty :style="pageHeight" v-else description="暂无数据" /> <el-empty :style="pageHeight" v-else description="暂无数据" />
</div> </div>
</el-dialog> </el-dialog>
@@ -108,9 +107,14 @@ defineOptions({
const props = defineProps({ const props = defineProps({
TrendList: { TrendList: {
type: Array type: Array
},
showIndex:{
type: Boolean,
default: true
} }
}) })
const titles = ref('趋势图')
const dialogVisible: any = ref(false) const dialogVisible: any = ref(false)
// console.log("🚀 ~ props:", props.TrendList) // console.log("🚀 ~ props:", props.TrendList)
const showEchart = ref(true) const showEchart = ref(true)
@@ -157,28 +161,40 @@ const countOptions: any = ref([])
const legendDictList: any = ref([]) const legendDictList: any = ref([])
const initCode = (field: string, title: string) => { const initCode = (field: string, title: string) => {
queryByCode('steady_state_limit_trend').then(res => { queryByCode('gridSide_exceedTheLimit').then(res => {
queryCsDictTree(res.data.id).then(item => { queryCsDictTree(res.data.id).then(item => {
//排序 //排序
indexOptions.value = item.data.sort((a: any, b: any) => { indexOptions.value = item.data.sort((a: any, b: any) => {
return a.sort - b.sort return a.sort - b.sort
}) })
const titleMap: Record<string, number> = { // const titleMap: Record<string, number> = {
flickerOvertime: 0, // flickerOvertime: 0,
uaberranceOvertime: 3, // uaberranceOvertime: 3,
ubalanceOvertime: 4, // ubalanceOvertime: 4,
freqDevOvertime: 5 // freqDevOvertime: 5
} // }
let defaultIndex = 0 // 默认值 // let defaultIndex = 0 // 默认值
if (field in titleMap) { // if (field in titleMap) {
defaultIndex = titleMap[field] // defaultIndex = titleMap[field]
} else if (field.includes('uharm')) { // } else if (field.includes('uharm')) {
defaultIndex = 1 // defaultIndex = 1
} else if (field.includes('iharm')) { // } else if (field.includes('iharm')) {
defaultIndex = 2 // defaultIndex = 2
} // }
let codeKey = field.includes('flickerOvertime')
? '闪变'
: field.includes('uharm')
? '谐波电压'
: field.includes('iharm')
? '谐波电流'
: field.includes('voltageDevOvertime')
? '电压偏差'
: field.includes('ubalanceOvertime')
? '不平衡'
: ''
let defaultIndex = indexOptions.value.findIndex((item: any) => item.name.includes(codeKey)) || 0
searchForm.value.index[0] = indexOptions.value[defaultIndex].id searchForm.value.index[0] = indexOptions.value[defaultIndex].id
}) })
@@ -202,7 +218,7 @@ const initCode = (field: string, title: string) => {
if (kk.harmStart && kk.harmEnd) { if (kk.harmStart && kk.harmEnd) {
range(0, 0, 0) range(0, 0, 0)
if (kk.showName == '间谐波电压含有率') { if (kk.showName.includes('间谐波电压')) {
countDataCopy.value[index].countOptions = range(kk.harmStart, kk.harmEnd, 1).map( countDataCopy.value[index].countOptions = range(kk.harmStart, kk.harmEnd, 1).map(
(item: any) => { (item: any) => {
return item - 0.5 return item - 0.5
@@ -257,6 +273,7 @@ const lineStyle = [{ type: 'solid' }, { type: 'dashed' }, { type: 'dotted' }]
const init = async () => { const init = async () => {
loading.value = true loading.value = true
// 选择指标的时候切换legend内容和data数据 // 选择指标的时候切换legend内容和data数据
echartsData.value = {}
let list: any = [] let list: any = []
legendDictList.value?.selectedList?.map((item: any) => { legendDictList.value?.selectedList?.map((item: any) => {
searchForm.value.index.map((vv: any) => { searchForm.value.index.map((vv: any) => {
@@ -291,7 +308,7 @@ const init = async () => {
} }
lists[index] = { lists[index] = {
statisticalId: item.index, statisticalId: item.index,
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : '' frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
} }
}) })
let obj = { let obj = {
@@ -396,6 +413,7 @@ const setEchart = () => {
formatter(params: any) { formatter(params: any) {
const xname = params[0].value[0] const xname = params[0].value[0]
let str = `${xname}<br>` let str = `${xname}<br>`
params.forEach((el: any, index: any) => { params.forEach((el: any, index: any) => {
let marker = '' let marker = ''
@@ -597,12 +615,12 @@ const formatCountOptions = () => {
}) })
countData.value.map((item: any, key: any) => { countData.value.map((item: any, key: any) => {
if (item.name == '谐波电流有效值') { if (item.name.includes('谐波电压')) {
item.name = '谐波电流次数'
} else if (item.name == '谐波电压含有率') {
item.name = '谐波电压次数'
} else if (item.name == '间谐波电压含有率') {
item.name = '间谐波电压次数' item.name = '间谐波电压次数'
} else if (item.name.includes('谐波电流')) {
item.name = '谐波电流次数'
} else if (item.name.includes('谐波电压')) {
item.name = '谐波电压次数'
} }
}) })
} }
@@ -640,6 +658,7 @@ watch(
) )
const openDialog = async (row: any, field: any, title: any) => { const openDialog = async (row: any, field: any, title: any) => {
titles.value = `${row?.lineName ? `${row.lineName}_` : ''}趋势图`
dialogVisible.value = true dialogVisible.value = true
trendRequestData.value = row trendRequestData.value = row

View File

@@ -9,11 +9,12 @@
v-model="tableStore.table.params.lineId" v-model="tableStore.table.params.lineId"
placeholder="请选择监测点" placeholder="请选择监测点"
style="width: 150px" style="width: 150px"
filterable
> >
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.lineId" :key="item.lineId"
:label="item.name" :label="item.lineName"
:value="item.lineId" :value="item.lineId"
/> />
</el-select> </el-select>
@@ -48,7 +49,7 @@ const loop50 = (key: string) => {
list.push({ list.push({
title: i + '次', title: i + '次',
field: key + i + 'Overtime', field: key + i + 'Overtime',
width: '80', width: '60',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
@@ -84,21 +85,22 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(%)', title: '长时闪变越限(%)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '80', width: '90',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
} }
}, },
{ {
title: '谐波电压越限(%)', title: '电压偏差越限(%)',
children: loop50('uharm') field: 'voltageDevOvertime',
}, width: '100',
{ render: 'customTemplate',
title: '谐波电流越限(%)', customTemplate: (row: any) => {
children: loop50('iharm') return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '三相不平衡度越限(%)', title: '三相不平衡度越限(%)',
@@ -110,23 +112,24 @@ const tableStore: any = new TableStore({
} }
}, },
{ {
title: '电压偏差越限(%)', title: '谐波电压越限(%)',
field: 'voltageDevOvertime', children: loop50('uharm')
width: '100',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
}
}, },
{ {
title: '频率偏差越限(%)', title: '谐波电流越限(%)',
field: 'freqDevOvertime', children: loop50('iharm')
width: '100', },
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>` // {
} // title: '频率偏差越限(%)',
} // field: 'freqDevOvertime',
// width: '100',
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
// }
// }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
}, },
@@ -138,9 +141,10 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.sortBy = '' tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = '' tableStore.table.params.orderBy = ''
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => { const open = async (row: any,searchBeginTime:any,searchEndTime:any,data: any) => {
dialogVisible.value = true dialogVisible.value = true
initCSlineList() // initCSlineList()
options.value = data
tableStore.table.params.lineId = row.lineId tableStore.table.params.lineId = row.lineId
nextTick(() => { nextTick(() => {

View File

@@ -1,7 +1,13 @@
<template> <template>
<div> <div>
<!--总体指标越限统计 --> <!--总体指标越限统计 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
:showReset="false"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
v-if="fullscreen" :timeKeyList="prop.timeKey"
></TableHeader>
<my-echart <my-echart
class="tall" class="tall"
:options="echartList" :options="echartList"
@@ -30,20 +36,21 @@ import OverLimitDetails from '@/components/cockpit/overLimitStatistics/component
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useTimeCacheStore } from '@/stores/timeCache' import { useTimeCacheStore } from '@/stores/timeCache'
import { totalLimitStatisticsData } from '@/api/harmonic-boot/cockpit/cockpit' import { totalLimitStatisticsData } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
const route = useRoute() const TableHeaderRef = ref()
const timeCacheStore = useTimeCacheStore()
const echartList = ref({}) const echartList = ref({})
@@ -82,7 +89,7 @@ const initEcharts = () => {
xAxis: { xAxis: {
// name: '(区域)', // name: '(区域)',
data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡'] data: ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
}, },
yAxis: { yAxis: {
@@ -143,7 +150,7 @@ const tableStore: any = new TableStore({
title: '越限占比(%)', title: '越限占比(%)',
children: [ children: [
{ {
title: '闪变', title: '长时闪变',
field: 'flicker', field: 'flicker',
minWidth: '70', minWidth: '70',
render: 'customTemplate', render: 'customTemplate',
@@ -191,8 +198,7 @@ const tableStore: any = new TableStore({
} }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.height = `calc(${prop.height} - 80px)` tableStore.table.height = `calc(${prop.height} - 80px)`
@@ -211,7 +217,8 @@ const cellClickEvent = ({ row, column }: any) => {
OverLimitDetailsRef.value.open( OverLimitDetailsRef.value.open(
row, row,
tableStore.table.params.searchBeginTime || prop.timeValue?.[0], tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
tableStore.table.params.searchEndTime || prop.timeValue?.[1] tableStore.table.params.searchEndTime || prop.timeValue?.[1],
tableStore.table.data
) )
} }
} }
@@ -219,6 +226,26 @@ const cellClickEvent = ({ row, column }: any) => {
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {
@@ -229,12 +256,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -1,8 +1,32 @@
<template> <template>
<div> <div>
<!--敏感负荷列表 --> <!--敏感负荷列表 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
<Table ref="tableRef" @cell-click="cellClickEvent" :height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`" isGroup></Table> ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
:timeKeyList="prop.timeKey"
>
<template #select>
<el-form-item label="关键字筛选">
<el-input
maxlength="32"
show-word-limit
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable
placeholder="请输入敏感负荷名称"
/>
</el-form-item>
</template>
</TableHeader>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`"
isGroup
></Table>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -10,33 +34,34 @@ import { ref, onMounted, provide, reactive, watch, h } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useRoute } from 'vue-router'
import { useTimeCacheStore } from '@/stores/timeCache'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number]}, w: { type: [String, Number] },
h: { type: [String, Number]}, h: { type: [String, Number] },
width: { type: [String, Number]}, width: { type: [String, Number] },
height: { type: [String, Number]}, height: { type: [String, Number] },
timeKey: { type: [String, Number]}, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
const dictData = useDictData() const TableHeaderRef = ref()
const sensitiveUserType = dictData.getBasicData('Sensitive_User_Type')
const dictData = useDictData()
const sensitiveUserType = dictData.getBasicData('Interference_Source')
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) { // if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数 // // 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0] // tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1] // tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
} // }
} }
// 计算是否全屏展示 // 计算是否全屏展示
@@ -51,10 +76,9 @@ const fullscreen = computed(() => {
} }
}) })
const OverLimitDetailsRef = ref() const OverLimitDetailsRef = ref()
const tableStore: any = new TableStore({ const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/pqSensitiveUser/getList', url: '/cs-harmonic-boot/pqSensitiveUser/getListByUser',
method: 'POST', method: 'POST',
showPage: fullscreen.value ? true : false, showPage: fullscreen.value ? true : false,
column: [ column: [
@@ -76,30 +100,34 @@ const tableStore: any = new TableStore({
title: '敏感负荷类型', title: '敏感负荷类型',
field: 'loadType', field: 'loadType',
minWidth: '70', minWidth: '70',
formatter: row => { formatter: row => {
return sensitiveUserType.filter(item => item.id == row.cellValue)[0]?.name return sensitiveUserType.filter(item => item.id == row.cellValue)[0]?.name
} }
}, },
{ {
title: '是否监测', title: '是否监测',
field: 'isMonitor', field: 'isMonitor',
minWidth: '80' minWidth: '80',
formatter: (row: any) => {
return row.cellValue || '/'
}
}, },
{ {
title: '是否治理', title: '是否治理',
field: 'isGovern', field: 'isGovern',
minWidth: '80' minWidth: '80',
formatter: (row: any) => {
return row.cellValue || '/'
}
} }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {}
}
}) })
tableStore.table.params.searchValue = ''
const tableRef = ref() const tableRef = ref()
provide('tableRef', tableRef) provide('tableRef', tableRef)
@@ -113,6 +141,24 @@ const cellClickEvent = ({ row, column }: any) => {
} }
} }
const setTime = () => {
// const time = getTime(
// (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
// prop.timeKey,
// fullscreen.value
// ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
// : prop.timeValue
// )
// if (Array.isArray(time)) {
// tableStore.table.params.searchBeginTime = time[0]
// tableStore.table.params.searchEndTime = time[1]
// TableHeaderRef.value?.setInterval(time[2] - 0)
// TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
// } else {
// console.warn('获取时间失败time 不是一个有效数组')
// }
}
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
@@ -127,17 +173,11 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true
} }
) )
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -4,8 +4,8 @@
<el-dialog draggable title="暂态事件详情 " v-model="dialogVisible" append-to-body width="70%"> <el-dialog draggable title="暂态事件详情 " v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange"> <TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange">
<template v-slot:select> <template v-slot:select>
<el-form-item label="监测点"> <el-form-item label="监测点" v-if="props.showLine">
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称"> <el-select v-model="tableStore.table.params.lineId" filterable placeholder="请选择监测点名称">
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.value" :key="item.value"
@@ -14,6 +14,21 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="暂态类型">
<el-select
v-model="tableStore.table.params.eventType"
style="min-width: 150px"
clearable
placeholder="请选择暂态类型"
>
<el-option
v-for="item in eventList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template> </template>
</TableHeader> </TableHeader>
<Table ref="tableRef" isGroup :height="heightRef"></Table> <Table ref="tableRef" isGroup :height="heightRef"></Table>
@@ -46,6 +61,13 @@ import { mainHeight } from '@/utils/layout'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue' import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import { analyseWave } from '@/api/common' import { analyseWave } from '@/api/common'
import { getSimpleLine } from '@/api/harmonic-boot/cockpit/cockpit' import { getSimpleLine } from '@/api/harmonic-boot/cockpit/cockpit'
interface Props {
showLine?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showLine: true
})
const dialogVisible: any = ref(false) const dialogVisible: any = ref(false)
const waveFormAnalysisRef: any = ref(null) const waveFormAnalysisRef: any = ref(null)
@@ -62,7 +84,11 @@ const heightRef = ref(mainHeight(168, 2.1).height)
const selectChange = (flag: boolean, h: any) => { const selectChange = (flag: boolean, h: any) => {
heightRef.value = mainHeight(h, 2.1).height heightRef.value = mainHeight(h, 2.1).height
} }
const eventList = [
{ label: '电压暂降', value: '1' },
{ label: '电压中断', value: '2' },
{ label: '电压暂升', value: '3' }
]
const getSimpleLineList = async () => { const getSimpleLineList = async () => {
const res = await getSimpleLine() const res = await getSimpleLine()
options.value = res.data options.value = res.data
@@ -132,7 +158,7 @@ const tableStore: any = new TableStore({
}, },
{ {
title: '波形', title: '波形',
width: '100', minWidth: '100',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -166,8 +192,8 @@ const tableStore: any = new TableStore({
// ...row, // ...row,
// duration: row.persistTime // 将 persistTime 值赋给 duration // duration: row.persistTime // 将 persistTime 值赋给 duration
// } // }
boxoList.value.featureAmplitude = boxoList.value.featureAmplitude = (row.amplitude - 0) / 100
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null // row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
boxoList.value.systemType = 'YPT' boxoList.value.systemType = 'YPT'
wp.value = res.data wp.value = res.data
} }
@@ -200,9 +226,10 @@ const tableStore: any = new TableStore({
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => {} loadCallback: () => {}
}) })
tableStore.table.params.eventType = ''
provide('tableStore', tableStore) provide('tableStore', tableStore)
const open = async (time: any) => { const open = async (time: any) => {
tableStore.table.params.eventType = ''
dialogVisible.value = true dialogVisible.value = true
getSimpleLineList() getSimpleLineList()
tableStore.table.params.lineId = '' tableStore.table.params.lineId = ''

View File

@@ -7,7 +7,7 @@
@selectChange="selectChange" @selectChange="selectChange"
datePicker datePicker
v-if="fullscreen" v-if="fullscreen"
:timeCacheFlag="false" :timeKeyList="prop.timeKey"
></TableHeader> ></TableHeader>
<el-calendar <el-calendar
v-model="value" v-model="value"
@@ -43,16 +43,22 @@
v-for="item in list?.filter((item:any) => item.name == data.day)" v-for="item in list?.filter((item:any) => item.name == data.day)"
@click="descentClick(item)" @click="descentClick(item)"
> >
<div>电压暂降:{{ item.eventDown || 0 }}</div> <!-- <div>电压暂降:{{ item.eventDown || 0 }}</div>
<div>电压中断:{{ item.eventOff || 0 }}</div> <div>电压中断:{{ item.eventOff || 0 }}</div>
<div>电压暂升:{{ item.eventUp || 0 }}</div> <div>电压暂升:{{ item.eventUp || 0 }}</div> -->
<template v-if="fullscreen">
<div>电压暂降:{{ item.eventDown || 0 }}</div>
<div>电压中断:{{ item.eventOff || 0 }}</div>
<div>电压暂升:{{ item.eventUp || 0 }}</div>
</template>
<template v-else>暂态事件</template>
</div> </div>
</el-tooltip> </el-tooltip>
</div> </div>
</template> </template>
</el-calendar> </el-calendar>
<!-- 暂态事件列表 --> <!-- 暂态事件列表 -->
<TransientList ref="transientListRef" /> <TransientList ref="transientListRef" :showLine="false" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -61,14 +67,16 @@ import TableStore from '@/utils/tableStore'
import { dayjs } from 'element-plus' import { dayjs } from 'element-plus'
import TransientList from './components/transientList.vue' import TransientList from './components/transientList.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -119,10 +127,7 @@ const tableStore: any = new TableStore({
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
if (!fullscreen.value && prop.timeValue && Array.isArray(prop.timeValue)) { setTime()
tableStore.table.params.searchBeginTime = prop.timeValue[0]
tableStore.table.params.searchEndTime = prop.timeValue[1]
}
}, },
loadCallback: () => { loadCallback: () => {
@@ -148,42 +153,41 @@ provide('tableStore', tableStore)
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
if (TableHeaderRef.value && typeof TableHeaderRef.value.setDatePicker === 'function') {
TableHeaderRef.value.setDatePicker([{ label: '月份', value: 3 }])
}
if (fullscreen.value) {
TableHeaderRef.value.setInterval(3)
}
tableStore.index() tableStore.index()
}) })
}) })
watch(
() => prop.timeKey, const setTime = () => {
val => { const time = getTime(
tableStore.index() (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
} }
) }
watch( watch(
() => prop.timeValue, () => prop.timeValue,
// (newVal, oldVal) => { (newVal, oldVal) => {
// // 当外部时间值变化时,更新表格的时间参数
// if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
// tableStore.table.params.searchBeginTime = newVal[0]
// tableStore.table.params.searchEndTime = newVal[1]
// tableStore.index()
// }
// },
val => {
tableStore.index() tableStore.index()
}, },
{ {
deep: true deep: true
} }
) )
// 电压暂降点击事件 // 电压暂降点击事件
const descentClick = (item:any) => { const descentClick = (item: any) => {
transientListRef.value.open(item.name) transientListRef.value.open(item.name)
} }
</script> </script>

View File

@@ -1,23 +1,30 @@
<template> <template>
<div> <div>
<!--暂态事件概率分布 --> <!--暂态事件概率分布 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
:showReset="false"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
<my-echart <my-echart
class="tall" class="tall"
:options="echartList" :options="echartList"
:style="{ :style="{
width: prop.width, width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )` height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}" }"
/> />
<my-echart <!-- <my-echart
class="mt10" class="mt10"
:options="echartList1" :options="echartList1"
:style="{ :style="{
width: prop.width, width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )` height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
}" }"
/> /> -->
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -26,16 +33,20 @@ import TableStore from '@/utils/tableStore'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const TableHeaderRef = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
@@ -66,135 +77,6 @@ const echartList = ref({})
const echartList1 = ref({}) const echartList1 = ref({})
// const echartList1 = ref({
// title: {
// text: '越限时间概率分布'
// },
// xAxis: {
// // name: '时间',
// // data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
// type: 'time',
// axisLabel: {
// formatter: {
// day: '{MM}-{dd}',
// month: '{MM}',
// year: '{yyyy}'
// }
// }
// },
// yAxis: {
// name: '次' // 给X轴加单位
// },
// grid: {
// left: '10px',
// right: '20px'
// },
// options: {
// series: [
// {
// type: 'line',
// showSymbol: false,
// // smooth: true,
// name: '电压中断',
// color: '#FF9100',
// data: [
// ['2025-10-16 07:00:00', 10],
// ['2025-10-16 07:15:00', 10],
// ['2025-10-16 07:30:00', 10],
// ['2025-10-16 07:45:00', 10],
// ['2025-10-16 08:00:00', 30],
// ['2025-10-16 08:15:00', 50],
// ['2025-10-16 08:30:00', 60],
// ['2025-10-16 08:45:00', 70],
// ['2025-10-16 09:00:00', 100],
// ['2025-10-16 09:15:00', 120],
// ['2025-10-16 09:30:00', 130],
// ['2025-10-16 09:45:00', 140],
// ['2025-10-16 10:00:00', 160],
// ['2025-10-16 10:15:00', 160],
// ['2025-10-16 10:30:00', 130],
// ['2025-10-16 10:45:00', 120],
// ['2025-10-16 11:00:00', 140],
// ['2025-10-16 11:15:00', 80],
// ['2025-10-16 11:30:00', 70],
// ['2025-10-16 11:45:00', 90],
// ['2025-10-16 12:00:00', 60],
// ['2025-10-16 12:15:00', 60],
// ['2025-10-16 12:30:00', 60],
// ['2025-10-16 12:45:00', 60]
// ]
// },
// {
// type: 'line',
// showSymbol: false,
// // smooth: true,
// color: '#FFBF00',
// name: '电压暂降',
// data: [
// ['2025-10-16 07:00:00', 1],
// ['2025-10-16 07:15:00', 1],
// ['2025-10-16 07:30:00', 1],
// ['2025-10-16 07:45:00', 1],
// ['2025-10-16 08:00:00', 3],
// ['2025-10-16 08:15:00', 5],
// ['2025-10-16 08:30:00', 6],
// ['2025-10-16 08:45:00', 7],
// ['2025-10-16 09:00:00', 10],
// ['2025-10-16 09:15:00', 12],
// ['2025-10-16 09:30:00', 13],
// ['2025-10-16 09:45:00', 14],
// ['2025-10-16 10:00:00', 16],
// ['2025-10-16 10:15:00', 16],
// ['2025-10-16 10:30:00', 13],
// ['2025-10-16 10:45:00', 12],
// ['2025-10-16 11:00:00', 14],
// ['2025-10-16 11:15:00', 8],
// ['2025-10-16 11:30:00', 7],
// ['2025-10-16 11:45:00', 9],
// ['2025-10-16 12:00:00', 6],
// ['2025-10-16 12:15:00', 6],
// ['2025-10-16 12:30:00', 6],
// ['2025-10-16 12:45:00', 6]
// ]
// },
// {
// type: 'line',
// showSymbol: false,
// // smooth: true,
// name: '电压暂升',
// color: config.layout.elementUiPrimary[0],
// data: [
// ['2025-10-16 07:00:00', 19],
// ['2025-10-16 07:15:00', 19],
// ['2025-10-16 07:30:00', 19],
// ['2025-10-16 07:45:00', 19],
// ['2025-10-16 08:00:00', 39],
// ['2025-10-16 08:15:00', 59],
// ['2025-10-16 08:30:00', 69],
// ['2025-10-16 08:45:00', 79],
// ['2025-10-16 09:00:00', 109],
// ['2025-10-16 09:15:00', 129],
// ['2025-10-16 09:30:00', 139],
// ['2025-10-16 09:45:00', 149],
// ['2025-10-16 10:00:00', 169],
// ['2025-10-16 10:15:00', 169],
// ['2025-10-16 10:30:00', 139],
// ['2025-10-16 10:45:00', 129],
// ['2025-10-16 11:00:00', 149],
// ['2025-10-16 11:15:00', 89],
// ['2025-10-16 11:30:00', 79],
// ['2025-10-16 11:45:00', 99],
// ['2025-10-16 12:00:00', 69],
// ['2025-10-16 12:15:00', 69],
// ['2025-10-16 12:30:00', 69],
// ['2025-10-16 12:45:00', 69]
// ]
// }
// ]
// }
// })
const processDataForChart = (rawData: any[]) => { const processDataForChart = (rawData: any[]) => {
// 将后端返回的扁平数据转换为 ECharts 需要的三维坐标格式 [x, y, z] // 将后端返回的扁平数据转换为 ECharts 需要的三维坐标格式 [x, y, z]
const chartData = rawData.map(item => [item.x, item.y, item.z]) const chartData = rawData.map(item => [item.x, item.y, item.z])
@@ -208,8 +90,7 @@ const tableStore: any = new TableStore({
showPage: false, showPage: false,
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
const processedData = processDataForChart(tableStore.table.data.innerList || []) const processedData = processDataForChart(tableStore.table.data.innerList || [])
@@ -302,34 +183,16 @@ const tableStore: any = new TableStore({
type: 'category', type: 'category',
name: '特征幅值', name: '特征幅值',
data: xLabels, data: xLabels,
axisLine: { nameGap: 40
lineStyle: {
color: '#111'
}
},
axisLabel: {
color: '#111'
}
}, },
yAxis3D: { yAxis3D: {
type: 'category', type: 'category',
name: '持续时间', name: '持续时间',
data: yLabels, data: yLabels,
nameTextStyle: { nameGap: 40,
color: '#111'
},
axisLine: {
show: true,
lineStyle: {
color: '#111'
}
},
axisLabel: {
color: '#111'
},
splitLine: { splitLine: {
lineStyle: { lineStyle: {
color: ['#111'],
type: 'dashed', type: 'dashed',
opacity: 0.5 opacity: 0.5
} }
@@ -337,25 +200,28 @@ const tableStore: any = new TableStore({
}, },
zAxis3D: { zAxis3D: {
type: 'value', type: 'value',
splitNumber: 10,
minInterval: 10, minInterval: 10,
name: '暂态事件次数' name: '暂态事件次数',
nameGap: 30
}, },
grid3D: { grid3D: {
viewControl: { viewControl: {
projection: 'perspective', projection: 'perspective',
distance: 250 distance: 260,
}, rotateSensitivity: 10,
boxWidth: 200, zoomSensitivity: 2
boxDepth: 80,
light: {
main: {
intensity: 1.2
}, },
ambient: { boxWidth: 150,
intensity: 0.3 boxDepth: 100,
boxHeight: 100,
light: {
main: {
intensity: 1.2
},
ambient: {
intensity: 0.4
}
} }
}
}, },
series: [ series: [
{ {
@@ -368,20 +234,6 @@ const tableStore: any = new TableStore({
fontSize: 16, fontSize: 16,
borderWidth: 1 borderWidth: 1
} }
},
itemStyle: {
opacity: 1
},
emphasis: {
label: {
textStyle: {
fontSize: 20,
color: '#900'
}
},
itemStyle: {
color: '#900'
}
} }
} }
] ]
@@ -419,26 +271,41 @@ provide('tableStore', tableStore)
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {
tableStore.index() tableStore.index()
} }
) )
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true
} }
) )
const addMenu = () => {}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -5,9 +5,24 @@
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange"> <TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange">
<template v-slot:select> <template v-slot:select>
<el-form-item label="监测点"> <el-form-item label="监测点">
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称"> <el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称">
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
</el-select>
</el-form-item>
<el-form-item label="暂态类型">
<el-select
v-model="tableStore.table.params.eventType"
style="min-width: 150px"
clearable
placeholder="请选择暂态类型"
>
<el-option
v-for="item in eventList"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
@@ -58,17 +73,20 @@ const boxoList: any = ref({})
const tableHeaderRef = ref() const tableHeaderRef = ref()
const options = ref() const options = ref()
const heightRef = ref(mainHeight(168, 2.1).height) const heightRef = ref(mainHeight(168, 2.2).height)
const selectChange = (flag: boolean, h: any) => { const selectChange = (flag: boolean, h: any) => {
heightRef.value = mainHeight(h, 2.1).height heightRef.value = mainHeight(h, 2.2).height
} }
const eventList = [
{ label: '电压暂降', value: '1' },
{ label: '电压中断', value: '2' },
{ label: '电压暂升', value: '3' }
]
const getSimpleLineList = async () => { const getSimpleLineList = async () => {
const res = await getSimpleLine() const res = await getSimpleLine()
options.value = res.data options.value = res.data
} }
const tableStore: any = new TableStore({ const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/event/pageEvent', url: '/cs-harmonic-boot/event/pageEvent',
method: 'POST', method: 'POST',
@@ -86,27 +104,27 @@ const tableStore: any = new TableStore({
{ {
title: '暂态时间', title: '暂态时间',
field: 'startTime', field: 'startTime',
minWidth: '150' minWidth: '180'
}, },
{ {
title: '测点名称', title: '测点名称',
field: 'lineName', field: 'lineName',
width: '150' minWidth: '150'
}, },
{ {
title: '暂态类型', title: '暂态类型',
field: 'tag', field: 'tag',
width: '100' minWidth: '100'
}, },
{ {
title: '特征幅值(%)', title: '特征幅值(%)',
field: 'amplitude', field: 'amplitude',
width: '100' minWidth: '100'
}, },
{ {
title: '暂降深度(%)', title: '暂降深度(%)',
field: 'depth', field: 'depth',
width: '100', minWidth: '100',
formatter: (row: any) => { formatter: (row: any) => {
// 当暂态类型不是电压暂升时,计算暂降深度 = 100 - 特征幅值 // 当暂态类型不是电压暂升时,计算暂降深度 = 100 - 特征幅值
if (row.row.tag !== '电压暂升') { if (row.row.tag !== '电压暂升') {
@@ -124,16 +142,16 @@ const tableStore: any = new TableStore({
{ {
title: '持续时间(S)', title: '持续时间(S)',
field: 'persistTime', field: 'persistTime',
width: '100' minWidth: '100'
}, },
{ {
title: '严重度', title: '严重度',
field: 'severity', field: 'severity',
width: '80' minWidth: '80'
}, },
{ {
title: '波形', title: '波形',
width: '100', width: '90',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -167,8 +185,9 @@ const tableStore: any = new TableStore({
// ...row, // ...row,
// duration: row.persistTime // 将 persistTime 值赋给 duration // duration: row.persistTime // 将 persistTime 值赋给 duration
// } // }
boxoList.value.featureAmplitude = // boxoList.value.featureAmplitude =
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null // row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
boxoList.value.featureAmplitude = (row.amplitude - 0) / 100
boxoList.value.systemType = 'YPT' boxoList.value.systemType = 'YPT'
wp.value = res.data wp.value = res.data
} }
@@ -192,7 +211,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return !(!row.wavePath && row.evtParamTm < 20) return !!row.wavePath
} }
} }
] ]
@@ -201,9 +220,10 @@ const tableStore: any = new TableStore({
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => {} loadCallback: () => {}
}) })
tableStore.table.params.eventType = ''
provide('tableStore', tableStore) provide('tableStore', tableStore)
const open = async (row: any, searchBeginTime: any, searchEndTime: any) => { const open = async (row: any, searchBeginTime: any, searchEndTime: any) => {
tableStore.table.params.eventType = ''
dialogVisible.value = true dialogVisible.value = true
getSimpleLineList() getSimpleLineList()
tableStore.table.params.lineId = row.id tableStore.table.params.lineId = row.id

View File

@@ -1,7 +1,13 @@
<template> <template>
<div> <div>
<!--暂态事件统计 --> <!--暂态事件统计 -->
<TableHeader :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader> <TableHeader
ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
datePicker
v-if="fullscreen" :timeKeyList="prop.timeKey"
></TableHeader>
<my-echart <my-echart
class="tall" class="tall"
:options="echartList" :options="echartList"
@@ -28,18 +34,23 @@ import { useConfig } from '@/stores/config'
import TransientStatisticsDetail from '@/components/cockpit/transientStatistics/components/transientStatisticsDetail.vue' import TransientStatisticsDetail from '@/components/cockpit/transientStatistics/components/transientStatisticsDetail.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { netEventEcharts } from '@/api/harmonic-boot/cockpit/cockpit' import { netEventEcharts } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const TableHeaderRef = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
@@ -96,8 +107,8 @@ const eventEcharts = () => {
}, },
legend: { legend: {
orient: 'vertical', orient: 'vertical',
top: 'center', top: '50',
right: '5%', right: '10',
formatter: function (e: any) { formatter: function (e: any) {
return e + ' ' + data.value.filter((item: any) => item.name == e)[0].value + '次' return e + ' ' + data.value.filter((item: any) => item.name == e)[0].value + '次'
} }
@@ -199,8 +210,7 @@ const tableStore: any = new TableStore({
} }
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
}, },
loadCallback: () => { loadCallback: () => {
eventEcharts() eventEcharts()
@@ -223,6 +233,25 @@ const cellClickEvent = ({ row, column }: any) => {
} }
} }
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
@@ -237,12 +266,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true

View File

@@ -1,10 +1,22 @@
<template> <template>
<div> <div>
<!--趋势对比 --> <!--趋势对比 -->
<TableHeader datePicker :showReset="false" @selectChange="selectChange" v-if="fullscreen"> <TableHeader
datePicker
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
>
<template v-slot:select> <template v-slot:select>
<el-form-item label="监测对象"> <el-form-item label="监测对象">
<el-select v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象" clearable> <el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -69,6 +81,7 @@
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)` height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}" }"
/> />
<!-- <el-empty description="暂无数据" /> -->
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -77,26 +90,24 @@ import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { useRoute } from 'vue-router'
import { useTimeCacheStore } from '@/stores/timeCache'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree' import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit' import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
import { yMethod, exportCSV } from '@/utils/echartMethod'
import { max } from 'lodash'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object } timeValue: { type: Object },
interval: { type: Number }
}) })
const route = useRoute() const TableHeaderRef = ref()
const timeCacheStore = useTimeCacheStore()
const config = useConfig() const config = useConfig()
const lineIdList = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
// 计算是否全屏展示 // 计算是否全屏展示
const fullscreen = computed(() => { const fullscreen = computed(() => {
const w = Number(prop.w) const w = Number(prop.w)
@@ -118,14 +129,16 @@ const echartList = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
// 监测对象 // 监测对象
const idList = ref() const idList = ref([])
// 监测对象 // 监测对象
const initListByIds = () => { const initListByIds = () => {
getListByIds({}).then((res: any) => { getListByIds({}).then((res: any) => {
if (res.data.length > 0) { if (res.data?.length > 0) {
idList.value = res.data idList.value = res.data
initCode() initCode()
} else {
tableStore.index()
} }
}) })
} }
@@ -170,7 +183,7 @@ const setEchart = () => {
if (!beforeGroupedByPhase[phase]) { if (!beforeGroupedByPhase[phase]) {
beforeGroupedByPhase[phase] = [] beforeGroupedByPhase[phase] = []
} }
beforeGroupedByPhase[phase].push([item.time, item.statisticalData]) beforeGroupedByPhase[phase].push([item.time, item.statisticalData, item.unit, 'solid'])
}) })
// 处理治理后数据 // 处理治理后数据
@@ -179,7 +192,7 @@ const setEchart = () => {
if (!afterGroupedByPhase[phase]) { if (!afterGroupedByPhase[phase]) {
afterGroupedByPhase[phase] = [] afterGroupedByPhase[phase] = []
} }
afterGroupedByPhase[phase].push([item.time, item.statisticalData]) afterGroupedByPhase[phase].push([item.time, item.statisticalData, item.unit, 'dotted'])
}) })
// 构建系列数据 // 构建系列数据
@@ -231,7 +244,7 @@ const setEchart = () => {
data: afterGroupedByPhase[phase], data: afterGroupedByPhase[phase],
itemStyle: { itemStyle: {
normal: { normal: {
color:color color: color
} }
}, },
lineStyle: { lineStyle: {
@@ -250,6 +263,11 @@ const setEchart = () => {
titleText = afterData[0].anotherName titleText = afterData[0].anotherName
} }
// statisticalData
// chartsListBefore.value.map((item: any) => item.statisticalData)
// chartsListAfter.value = tableStore.table.data.after
// 构建图例数据 // 构建图例数据
const legendData = series.map((item: any, index: number) => { const legendData = series.map((item: any, index: number) => {
let color = config.layout.elementUiPrimary[0] let color = config.layout.elementUiPrimary[0]
@@ -279,11 +297,45 @@ const setEchart = () => {
} }
} }
}) })
let [min, max] = yMethod(
[...chartsListBefore.value.map((item: any) => item.statisticalData),
...chartsListAfter.value.map((item: any) => item.statisticalData)]
)
echartList.value = { echartList.value = {
title: { title: {
text: titleText text: titleText
}, },
tooltip: {
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter(params: any) {
const xname = params[0].value[0]
let str = `${xname}<br>`
params.forEach((el: any, index: any) => {
let marker = ''
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
str += `${marker}${el.seriesName.split('(')[0]}${
el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == null ? '' : el.value[2]) : '-'
}<br>`
})
return str
}
},
legend: { legend: {
data: legendData, data: legendData,
icon: 'rect', icon: 'rect',
@@ -307,7 +359,9 @@ const setEchart = () => {
} }
}, },
yAxis: { yAxis: {
name: beforeData.length > 0 ? beforeData[0].unit : afterData.length > 0 ? afterData[0].unit : '' name: beforeData.length > 0 ? beforeData[0].unit : afterData.length > 0 ? afterData[0].unit : '',
max: max,
min: min,
}, },
grid: { grid: {
left: '10px', left: '10px',
@@ -324,8 +378,7 @@ const tableStore: any = new TableStore({
exportName: '趋势对比', exportName: '趋势对比',
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.searchBeginTime = tableStore.table.params.searchBeginTime || prop.timeValue?.[0] setTime()
tableStore.table.params.searchEndTime = tableStore.table.params.searchEndTime || prop.timeValue?.[1]
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) { if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id tableStore.table.params.sensitiveUserId = idList.value[0].id
} }
@@ -356,12 +409,12 @@ const tableStore: any = new TableStore({
chartsListBefore.value = tableStore.table.data.before chartsListBefore.value = tableStore.table.data.before
chartsListAfter.value = tableStore.table.data.after chartsListAfter.value = tableStore.table.data.after
setEchart() setEchart()
} }
} }
}) })
tableStore.table.params.indicator = '1' tableStore.table.params.indicator = ''
tableStore.table.params.exceedingTheLimit = '1' tableStore.table.params.exceedingTheLimit = ''
tableStore.table.params.dataLevel = 'Primary' tableStore.table.params.dataLevel = 'Primary'
tableStore.table.params.valueType = 'avg' tableStore.table.params.valueType = 'avg'
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -370,6 +423,25 @@ onMounted(() => {
initListByIds() initListByIds()
}) })
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
// 判断是否应该显示谐波次数选择框 // 判断是否应该显示谐波次数选择框
const shouldShowHarmonicCount = () => { const shouldShowHarmonicCount = () => {
if (!tableStore.table.params.indicator || !indicatorList.value) return false if (!tableStore.table.params.indicator || !indicatorList.value) return false
@@ -378,7 +450,7 @@ const shouldShowHarmonicCount = () => {
return ( return (
currentIndicator && currentIndicator &&
(currentIndicator.name.includes('电压谐波含有率') || currentIndicator.name.includes('电流谐波含有率')) (currentIndicator.name.includes('幅值') || currentIndicator.name.includes('含有率'))
) )
} }
@@ -387,9 +459,9 @@ const getHarmonicTypeName = () => {
const currentIndicator = indicatorList.value.find((item: any) => item.id === tableStore.table.params.indicator) const currentIndicator = indicatorList.value.find((item: any) => item.id === tableStore.table.params.indicator)
if (currentIndicator) { if (currentIndicator) {
if (currentIndicator.name.includes('电压谐波含有率')) { if (currentIndicator.name.includes('电压')) {
return '电压' return '电压'
} else if (currentIndicator.name.includes('电流谐波含有率')) { } else if (currentIndicator.name.includes('电流')) {
return '电流' return '电流'
} }
} }
@@ -405,12 +477,7 @@ watch(
watch( watch(
() => prop.timeValue, () => prop.timeValue,
(newVal, oldVal) => { (newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数 tableStore.index()
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
}, },
{ {
deep: true deep: true
@@ -432,8 +499,6 @@ watch(
} }
} }
) )
const addMenu = () => {}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// :deep(.el-select) { // :deep(.el-select) {

View File

@@ -106,13 +106,15 @@ const initChart = () => {
start: 0, start: 0,
bottom: '20px', bottom: '20px',
end: 100 end: 100,
filterMode: 'none'
}, },
{ {
start: 0, start: 0,
height: 13, height: 13,
bottom: '20px', bottom: '20px',
end: 100 end: 100,
filterMode: 'none'
} }
// { // {
// show: true, // show: true,

View File

@@ -252,50 +252,52 @@ self.onmessage = function (e) {
let titles = '' let titles = ''
if (boxoList.systemType == 'pms') { if (boxoList.systemType == 'pms') {
titles = titles =
'变电站名称' + '变电站名称:' +
boxoList.powerStationName + boxoList.powerStationName +
' 监测点名称' + ' 监测点名称:' +
boxoList.measurementPointName + boxoList.measurementPointName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + (boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间' + '%  持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'
} else if (boxoList.systemType == 'ZL') { } else if (boxoList.systemType == 'ZL') {
titles = titles =
' 监测点名称' + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.equipmentName + boxoList.equipmentName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
boxoList.evtParamVVaDepth + boxoList.evtParamVVaDepth +
' 持续时间' + '% 持续时间:' +
boxoList.evtParamTm + boxoList.evtParamTm +
's' 's'
} else if (boxoList.systemType == 'YPT') { } else if (boxoList.systemType == 'YPT') {
titles = titles =
' 监测点名称' + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.lineName + boxoList.lineName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + (boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间' + '% 持续时间:' +
boxoList.persistTime + boxoList.persistTime +
's' 's'
} else { } else {
titles = titles =
'变电站名称' + ' 变电站名称:' +
boxoList.subName + boxoList.subName +
' 监测点名称' + ' 监测点名称:' +
boxoList.lineName + boxoList.lineName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + (boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间' + '% 持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'
} }

View File

@@ -1,8 +1,8 @@
<template> <template>
<div v-loading="loading" style="position: relative; height: 100%"> <div v-loading="loading" style="position: relative; height: 100%">
<div id="boxr"> <div id="boxr">
<div id="rmsp" :style="`height:${vh};overflow: hidden;`"> <div id="rmsp" :style="`height:${vh};overflow: hidden;min-height: 200px;`">
<div class="bx" id="rms"></div> <div class="bx" id="rms" style="min-height: 200px"></div>
</div> </div>
</div> </div>
</div> </div>
@@ -109,7 +109,7 @@ const myChartess5 = ref<echarts.ECharts | null>(null)
const vh = computed(() => { const vh = computed(() => {
if (props.parentHeight == 999) { if (props.parentHeight == 999) {
return '310px' return `calc((60vh - 150px) / 2 )`
} else if (props.parentHeight != 0) { } else if (props.parentHeight != 0) {
return mainHeight(props.parentHeight, 2).height return mainHeight(props.parentHeight, 2).height
} }
@@ -594,13 +594,13 @@ const initWave = (
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) { for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
const rmsId = 'rms' + step const rmsId = 'rms' + step
const newDivRms = $( const newDivRms = $(
`<div style="height:${vh.value};overflow: hidden;"><div class='bx' id='${rmsId}'></div></div>` `<div style="height:${vh.value};overflow: hidden;min-height: 200px;"><div class='bx' id='${rmsId}'></div></div>`
) )
newDivRms.insertAfter($('#rmsp')) newDivRms.insertAfter($('#rmsp'))
$(`#${rmsId}`).css('height', picHeight).css('width', vw.value) $(`#${rmsId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
} }
} else { } else {
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压${( titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降(骤升)幅值${(
Number(eventValue.value) * 1 Number(eventValue.value) * 1
).toFixed(0)}% 持续时间:${persistTime.value}s` ).toFixed(0)}% 持续时间:${persistTime.value}s`
} }
@@ -748,8 +748,10 @@ const initWave = (
rotation: 0, rotation: 0,
y: -10 y: -10
}, },
max: rmscm[0]?.[1] * 1.06 || 0, // max: rmscm[0]?.[1] * 1.06 || 0,
min: rmscu[0]?.[1] - rmscu[0]?.[1] * 0.04 || 0, // min: rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0,
max: Math.floor((rmscm[0]?.[1] * 1.06 || 0) * 1.1 * 10) / 10,
min: Math.floor((rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0) * 10) / 10,
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
opposite: false, opposite: false,
@@ -768,7 +770,7 @@ const initWave = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
return (value - 0).toFixed(2) return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -780,11 +782,11 @@ const initWave = (
} }
}, },
grid: { grid: {
left: '1%', left: '60px',
right: '45px', right: '45px',
bottom: '40px', bottom: '40px',
top: '60px', top: '60px'
containLabel: true // containLabel: true
}, },
dataZoom: [ dataZoom: [
{ {
@@ -837,7 +839,7 @@ const initWave = (
data: rmscu data: rmscu
}, },
{ {
name: '最小残余电压', name: '最小暂降(骤升)幅值',
type: 'scatter', type: 'scatter',
symbol: 'image://' + url2, symbol: 'image://' + url2,
itemStyle: { width: 45, height: 45 }, itemStyle: { width: 45, height: 45 },
@@ -1077,6 +1079,8 @@ const drawPics = (
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
opposite: false, opposite: false,
// max: Math.floor((rmscm[0]?.[1] * 1.06 || 0) * 1.1 * 10) / 10,
// min: Math.floor((rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0) * 10) / 10,
nameTextStyle: { nameTextStyle: {
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor color: props.DColor ? '#000' : echartsColor.WordColor
@@ -1092,7 +1096,8 @@ const drawPics = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
return (value - 0).toFixed(2) // return (value - 0).toFixed(2)
return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -1104,11 +1109,11 @@ const drawPics = (
} }
}, },
grid: { grid: {
left: '1%', left: '60px',
right: '45px', right: '45px',
bottom: '40px', bottom: '40px',
top: '60px', top: '60px'
containLabel: true // containLabel: true
}, },
dataZoom: [ dataZoom: [
{ {

View File

@@ -125,50 +125,52 @@ self.addEventListener('message', function (e) {
let titles = '' let titles = ''
if (boxoList.systemType == 'pms') { if (boxoList.systemType == 'pms') {
titles = titles =
'变电站名称' + '变电站名称:' +
boxoList.powerStationName + boxoList.powerStationName +
' 监测点名称' + ' 监测点名称:' +
boxoList.measurementPointName + boxoList.measurementPointName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + (boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间' + '% 持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'
} else if (boxoList.systemType == 'ZL') { } else if (boxoList.systemType == 'ZL') {
titles = titles =
' 监测点名称' + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.equipmentName + boxoList.equipmentName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
boxoList.evtParamVVaDepth + boxoList.evtParamVVaDepth +
' 持续时间' + '% 持续时间:' +
boxoList.evtParamTm + boxoList.evtParamTm +
's' 's'
} else if (boxoList.systemType == 'YPT') { } else if (boxoList.systemType == 'YPT') {
titles = titles =
' 监测点名称' + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.lineName + boxoList.lineName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + (boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间' + '% 持续时间:' +
boxoList.persistTime + boxoList.persistTime +
's' 's'
} else { } else {
titles = titles =
'变电站名称' + '变电站名称:' +
boxoList.subName + boxoList.subName +
' 监测点名称' + ' 监测点名称:' +
boxoList.lineName + boxoList.lineName +
' 发生时刻' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 残余电压:' + ' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + (boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间' + '% 持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'
} }

View File

@@ -1,8 +1,8 @@
<template> <template>
<div v-loading="loading" class="boxbx" style="position: relative; height: 100%"> <div v-loading="loading" class="boxbx" style="position: relative; height: 100%">
<div id="boxsj"> <div id="boxsj">
<div id="shushi" :style="`height:${vh};overflow: hidden;`"> <div id="shushi" :style="`height:${vh};overflow: hidden;min-height: 200px;`">
<div class="bx" id="wave"></div> <div class="bx" id="wave" style="min-height: 200px"></div>
</div> </div>
</div> </div>
</div> </div>
@@ -88,7 +88,7 @@ const myChartess5 = ref<echarts.ECharts | null>(null)
const vh = computed(() => { const vh = computed(() => {
if (props.parentHeight == 999) { if (props.parentHeight == 999) {
return '310px' return `calc((60vh - 150px) / 2 )`
} else if (props.parentHeight != 0) { } else if (props.parentHeight != 0) {
return mainHeight(props.parentHeight, 2).height return mainHeight(props.parentHeight, 2).height
} }
@@ -327,14 +327,14 @@ const initWave = (
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) { for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
const waveId = 'wave' + step const waveId = 'wave' + step
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;"> const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;min-height: 200px;">
<div class='bx1' id='${waveId}'></div> <div class='bx1' id='${waveId}'></div>
</div>`) </div>`)
newDivShunshi.insertAfter($('#shushi')) newDivShunshi.insertAfter($('#shushi'))
$(`#${waveId}`).css('height', picHeight).css('width', vw.value) $(`#${waveId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
} }
} else { } else {
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压${( titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降(骤升)幅值${(
Number(eventValue.value) * 1 Number(eventValue.value) * 1
).toFixed(0)}% 持续时间:${persistTime.value}s` ).toFixed(0)}% 持续时间:${persistTime.value}s`
} }
@@ -481,8 +481,10 @@ const initWave = (
}, },
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
max: max.toFixed(2) * 1.1, // max: max.toFixed(2) * 1.1,
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1, // min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
max: Math.floor(max.toFixed(2) * 1.1 * 10) / 10,
min: Math.floor(min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1 * 10) / 10 ,
opposite: false, opposite: false,
nameTextStyle: { nameTextStyle: {
fontSize: '12px', fontSize: '12px',
@@ -499,7 +501,8 @@ const initWave = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
return (value - 0).toFixed(2) // return (value - 0).toFixed(2)
return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -511,11 +514,11 @@ const initWave = (
} }
}, },
grid: { grid: {
left: '1%', left: '60px',
right: '45px', right: '45px',
bottom: '40px', bottom: '40px',
top: '60px', top: '60px'
containLabel: true // containLabel: true
}, },
dataZoom: [ dataZoom: [
{ {
@@ -591,7 +594,7 @@ const initWave = (
const drawPics = ( const drawPics = (
waveDataTemp: WaveData, waveDataTemp: WaveData,
picHeight: string, picHeight: any,
step: number, step: number,
show: boolean, show: boolean,
myChartes1: echarts.ECharts, myChartes1: echarts.ECharts,
@@ -788,8 +791,8 @@ const drawPics = (
}, },
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
max: max.toFixed(2) * 1.1, max: Math.floor(max.toFixed(2) * 1.1 * 10) / 10,
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1, min: Math.floor(min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1 * 10) / 10 ,
opposite: false, opposite: false,
nameTextStyle: { nameTextStyle: {
fontSize: '12px', fontSize: '12px',
@@ -806,7 +809,8 @@ const drawPics = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
return (value - 0).toFixed(2) // return (value - 0).toFixed(2)
return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -818,11 +822,11 @@ const drawPics = (
} }
}, },
grid: { grid: {
left: '1%', left: '60px',
right: '45px', right: '45px',
bottom: '40px', bottom: '40px',
top: '60px', top: '60px'
containLabel: true // containLabel: true
}, },
dataZoom: [ dataZoom: [
{ {

View File

@@ -5,7 +5,7 @@
style="min-width: 90px; width: 90px; margin-right: 10px" style="min-width: 90px; width: 90px; margin-right: 10px"
@change="timeChange" @change="timeChange"
> >
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in filteredTimeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-date-picker <el-date-picker
v-model.trim="timeValue" v-model.trim="timeValue"
@@ -20,6 +20,7 @@
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
:shortcuts="shortcuts" :shortcuts="shortcuts"
/> />
<el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button> <el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button>
<el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button> <el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button>
<el-button :disabled="preDisabled" type="primary" :icon="DArrowRight" @click="next"></el-button> <el-button :disabled="preDisabled" type="primary" :icon="DArrowRight" @click="next"></el-button>
@@ -35,13 +36,15 @@ interface Props {
theCurrentTime?: boolean theCurrentTime?: boolean
initialInterval?: number initialInterval?: number
initialTimeValue?: any initialTimeValue?: any
timeKeyList?: string[] //日期下拉
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
nextFlag: false, nextFlag: false,
theCurrentTime: true, theCurrentTime: true,
initialInterval: 3, initialInterval: 3,
initialTimeValue: undefined initialTimeValue: undefined,
timeKeyList: () => []
}) })
const emit = defineEmits(['change']) const emit = defineEmits(['change'])
@@ -89,6 +92,16 @@ const shortcuts = [
} }
} }
] ]
// 计算过滤后的 timeOptions
const filteredTimeOptions = computed(() => {
if (!props.timeKeyList || props.timeKeyList.length === 0) {
return timeOptions.value
}
return timeOptions.value.filter((option: any) => props.timeKeyList.includes(option.value.toString()))
})
onMounted(() => { onMounted(() => {
// 使用传入的初始值 // 使用传入的初始值
if (props.initialInterval !== undefined) { if (props.initialInterval !== undefined) {
@@ -113,9 +126,13 @@ const checkInitialButtonStatus = () => {
const endTime = timeValue.value[1] const endTime = timeValue.value[1]
const currentDate = window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd') const currentDate = window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd')
// 如果结束时间小于当前日期,则不禁用"下一个"按钮 // 只有当 props.nextFlag 为 false 时才应用限制
if (new Date(endTime + ' 00:00:00').getTime() < new Date(currentDate + ' 00:00:00').getTime()) { if (!props.nextFlag) {
preDisabled.value = false // 如果结束时间早于当前日期则按钮可用preDisabled = false
// 如果结束时间晚于或等于当前日期则按钮禁用preDisabled = true
const endDateTime = new Date(endTime).getTime()
const currentDateTime = new Date(currentDate).getTime()
preDisabled.value = endDateTime >= currentDateTime
} }
} }
} }
@@ -164,30 +181,15 @@ const timeChange = (e: number) => {
timeFlag.value = 1 timeFlag.value = 1
} }
// 检查按钮状态 nextTick(() => {
checkButtonStatus() // 检查按钮状态
checkInitialButtonStatus()
})
// 触发 change 事件 // 触发 change 事件
emitChange() emitChange()
} }
// 添加按钮状态检查方法
const checkButtonStatus = () => {
if (timeValue.value && timeValue.value.length >= 2) {
const endTime = timeValue.value[1]
const currentDate = window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd')
// 如果结束时间大于等于当前日期,且 nextFlag 为 false则禁用"下一个"按钮
if (!props.nextFlag) {
if (new Date(endTime + ' 00:00:00').getTime() >= new Date(currentDate + ' 00:00:00').getTime()) {
preDisabled.value = true
} else {
preDisabled.value = false
}
}
}
}
// 当前 // 当前
const nowTime = () => { const nowTime = () => {
// console.log(interval.value, '000000000') // console.log(interval.value, '000000000')

View File

@@ -1,35 +1,36 @@
<template> <template>
<div class="mac-address-input" :class="{ disabled: disabled }"> <div class="mac-address-input" :class="{ disabled: disabled }">
<el-input <el-input
ref="inputRef" ref="inputRef"
v-model="macValue" placeholder="请输入设备mac地址"
type="text" v-model="macValue"
maxlength="17" type="text"
:disabled="disabled" maxlength="17"
@input="handleInput" :disabled="disabled"
@keydown="handleKeydown" @input="handleInput"
@focus="handleFocus" @keydown="handleKeydown"
@blur="handleBlur" @focus="handleFocus"
@paste="handlePaste" @blur="handleBlur"
/> @paste="handlePaste"
</div> />
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
interface Props { interface Props {
modelValue?: string modelValue?: string
disabled?: boolean disabled?: boolean
} }
interface Emits { interface Emits {
(e: 'update:modelValue', value: string): void (e: 'update:modelValue', value: string): void
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: '', modelValue: '',
disabled: false disabled: false
}) })
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
@@ -42,35 +43,35 @@ const macValue = ref<string>('')
// 解析传入的MAC地址 // 解析传入的MAC地址
const parseMacAddress = (mac: string): string => { const parseMacAddress = (mac: string): string => {
if (!mac) return '' if (!mac) return ''
// 移除非十六进制字符并转为大写 // 移除非十六进制字符并转为大写
const cleanMac = mac.replace(/[^0-9a-fA-F]/g, '').toUpperCase() const cleanMac = mac.replace(/[^0-9a-fA-F]/g, '').toUpperCase()
// 按每2个字符分割并用冒号连接 // 按每2个字符分割并用冒号连接
let result = '' let result = ''
for (let i = 0; i < cleanMac.length; i += 2) { for (let i = 0; i < cleanMac.length; i += 2) {
if (i > 0) result += ':' if (i > 0) result += ':'
result += cleanMac.substr(i, 2) result += cleanMac.substr(i, 2)
} }
return result.substring(0, 17) // 最多17个字符 (12个数字+5个冒号) return result.substring(0, 17) // 最多17个字符 (12个数字+5个冒号)
} }
// 格式化MAC地址 - 改进版 // 格式化MAC地址 - 改进版
const formatMac = (value: string): string => { const formatMac = (value: string): string => {
// 移除所有冒号 // 移除所有冒号
const cleanValue = value.replace(/:/g, '') const cleanValue = value.replace(/:/g, '')
// 只保留十六进制字符并转为大写 // 只保留十六进制字符并转为大写
const hexOnly = cleanValue.replace(/[^0-9a-fA-F]/g, '').toUpperCase() const hexOnly = cleanValue.replace(/[^0-9a-fA-F]/g, '').toUpperCase()
// 按每两个字符添加冒号最多6段 // 按每两个字符添加冒号最多6段
let formatted = '' let formatted = ''
for (let i = 0; i < Math.min(hexOnly.length, 12); i += 2) { for (let i = 0; i < Math.min(hexOnly.length, 12); i += 2) {
if (i > 0) formatted += ':' if (i > 0) formatted += ':'
formatted += hexOnly.substr(i, 2) formatted += hexOnly.substr(i, 2)
} }
return formatted return formatted
} }
// 当前聚焦的输入框索引 // 当前聚焦的输入框索引
@@ -78,88 +79,86 @@ const focusedIndex = ref<number | null>(null)
// 处理输入事件 // 处理输入事件
const handleInput = (value: string) => { const handleInput = (value: string) => {
const formatted = formatMac(value) const formatted = formatMac(value)
macValue.value = formatted macValue.value = formatted
// 发出不带冒号的纯净值 // 发出不带冒号的纯净值
emit('update:modelValue', formatted.replace(/:/g, '')) emit('update:modelValue', formatted.replace(/:/g, ''))
} }
// 处理键盘事件 // 处理键盘事件
const handleKeydown = (event: KeyboardEvent) => { const handleKeydown = (event: KeyboardEvent) => {
const target = event.target as HTMLInputElement const target = event.target as HTMLInputElement
// 处理退格键 // 处理退格键
if (event.key === 'Backspace') { if (event.key === 'Backspace') {
// 处理在冒号前删除的情况 // 处理在冒号前删除的情况
const cursorPos = target.selectionStart || 0 const cursorPos = target.selectionStart || 0
if (cursorPos > 0 && macValue.value[cursorPos - 1] === ':' && if (cursorPos > 0 && macValue.value[cursorPos - 1] === ':' && target.selectionStart === target.selectionEnd) {
target.selectionStart === target.selectionEnd) { event.preventDefault()
event.preventDefault() // 删除冒号前的两个字符
// 删除冒号前的两个字符 const newValue = macValue.value.substring(0, cursorPos - 3) + macValue.value.substring(cursorPos)
const newValue = macValue.value.substring(0, cursorPos - 3) + macValue.value = newValue
macValue.value.substring(cursorPos) // 设置光标位置
macValue.value = newValue setTimeout(() => {
// 设置光标位置 if (target.setSelectionRange) {
setTimeout(() => { target.setSelectionRange(cursorPos - 3, cursorPos - 3)
if (target.setSelectionRange) { }
target.setSelectionRange(cursorPos - 3, cursorPos - 3) }, 0)
emit('update:modelValue', newValue.replace(/:/g, ''))
} }
}, 0)
emit('update:modelValue', newValue.replace(/:/g, ''))
} }
}
} }
// 处理焦点事件 // 处理焦点事件
const handleFocus = () => { const handleFocus = () => {
focusedIndex.value = 0 focusedIndex.value = 0
} }
// 处理失焦事件 // 处理失焦事件
const handleBlur = () => { const handleBlur = () => {
focusedIndex.value = null focusedIndex.value = null
} }
// 处理粘贴事件 // 处理粘贴事件
const handlePaste = (event: ClipboardEvent) => { const handlePaste = (event: ClipboardEvent) => {
event.preventDefault() event.preventDefault()
const pastedText = event.clipboardData?.getData('text') || '' const pastedText = event.clipboardData?.getData('text') || ''
// 清理粘贴的文本 // 清理粘贴的文本
const cleanPastedText = pastedText.replace(/[^0-9a-fA-F]/g, '').toUpperCase() const cleanPastedText = pastedText.replace(/[^0-9a-fA-F]/g, '').toUpperCase()
const formatted = formatMac(cleanPastedText) const formatted = formatMac(cleanPastedText)
macValue.value = formatted macValue.value = formatted
emit('update:modelValue', formatted.replace(/:/g, '')) emit('update:modelValue', formatted.replace(/:/g, ''))
} }
// 监听modelValue变化 // 监听modelValue变化
watch( watch(
() => props.modelValue, () => props.modelValue,
(newVal) => { newVal => {
const cleanNewVal = (newVal || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase() const cleanNewVal = (newVal || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
const currentCleanValue = macValue.value.replace(/:/g, '') const currentCleanValue = macValue.value.replace(/:/g, '')
if (cleanNewVal !== currentCleanValue) { if (cleanNewVal !== currentCleanValue) {
macValue.value = parseMacAddress(cleanNewVal) macValue.value = parseMacAddress(cleanNewVal)
} }
}, },
{ immediate: true } { immediate: true }
) )
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.mac-address-input { .mac-address-input {
width: 100%; width: 100%;
&.disabled { &.disabled {
opacity: 0.7; opacity: 0.7;
} }
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
input { input {
text-transform: uppercase; text-transform: uppercase;
font-family: inherit; // 使用继承的字体而不是等宽字体 font-family: inherit; // 使用继承的字体而不是等宽字体
}
} }
}
} }
</style> </style>

View File

@@ -21,8 +21,8 @@
<el-image <el-image
:hide-on-click-modal="true" :hide-on-click-modal="true"
:preview-teleported="true" :preview-teleported="true"
:preview-src-list="[fieldValue]" :preview-src-list="[imgList[fieldValue]]"
:src="fieldValue.length > 100 ? fieldValue : getUrl(fieldValue)" :src="fieldValue.length > 100 ? fieldValue : getUrl(fieldValue) ? imgList[fieldValue] : ''"
></el-image> ></el-image>
</div> </div>
@@ -226,10 +226,12 @@ const handlerCommand = (item: OptButton) => {
break break
} }
} }
const imgList: any = ref({})
const getUrl = (url: string) => { const getUrl = (url: string) => {
getFileUrl({ filePath: url }).then(res => { getFileUrl({ filePath: url }).then(res => {
return res.data imgList.value[url] = res.data
}) })
return true
} }
</script> </script>

View File

@@ -2,7 +2,7 @@
<div ref="tableHeader" class="cn-table-header"> <div ref="tableHeader" class="cn-table-header">
<div class="table-header ba-scroll-style" :key="num"> <div class="table-header ba-scroll-style" :key="num">
<el-form <el-form
style="flex: 1; height: 34px; margin-right: 20px; display: flex; flex-wrap: wrap" style="flex: 1; height: 34px; margin-right: 0px; display: flex; flex-wrap: wrap"
ref="headerForm" ref="headerForm"
@submit.prevent="" @submit.prevent=""
@keyup.enter="onComSearch" @keyup.enter="onComSearch"
@@ -15,6 +15,7 @@
:nextFlag="nextFlag" :nextFlag="nextFlag"
:theCurrentTime="theCurrentTime" :theCurrentTime="theCurrentTime"
@change="handleDatePickerChange" @change="handleDatePickerChange"
:timeKeyList="props.timeKeyList"
></DatePicker> ></DatePicker>
</el-form-item> </el-form-item>
@@ -28,12 +29,27 @@
<Icon size="14" name="el-icon-ArrowUp" style="color: #fff" v-if="showSelect" /> <Icon size="14" name="el-icon-ArrowUp" style="color: #fff" v-if="showSelect" />
<Icon size="14" name="el-icon-ArrowDown" style="color: #fff" v-else /> <Icon size="14" name="el-icon-ArrowDown" style="color: #fff" v-else />
</el-button> </el-button>
<el-button @click="onComSearch" v-if="showSearch" type="primary" :icon="Search">查询</el-button> <el-button
<el-button @click="onResetForm" v-if="showSearch && showReset" :icon="RefreshLeft">重置</el-button> @click="onComSearch"
v-if="showSearch"
:loading="tableStore.table.loading"
type="primary"
:icon="Search"
>
查询
</el-button>
<el-button
@click="onResetForm"
v-if="showSearch && showReset"
:loading="tableStore.table.loading"
:icon="RefreshLeft"
>
重置
</el-button>
<el-button <el-button
@click="onExport" @click="onExport"
v-if="showExport" v-if="showExport"
:loading="tableStore.table.loading" :loading="tableStore.table.exportLoading"
type="primary" type="primary"
icon="el-icon-Download" icon="el-icon-Download"
> >
@@ -89,6 +105,7 @@ interface Props {
showReset?: boolean //是否显示重置 showReset?: boolean //是否显示重置
showExport?: boolean //导出控制 showExport?: boolean //导出控制
timeCacheFlag?: boolean //是否取缓存时间 timeCacheFlag?: boolean //是否取缓存时间
timeKeyList?: string[] //日期下拉列表
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -99,7 +116,8 @@ const props = withDefaults(defineProps<Props>(), {
theCurrentTime: true, theCurrentTime: true,
showReset: true, showReset: true,
showExport: false, showExport: false,
timeCacheFlag: true timeCacheFlag: true,
timeKeyList: () => ['1', '2', '3', '4', '5'] // 修改为箭头函数返回空数组
}) })
// 处理 DatePicker 值变化事件 // 处理 DatePicker 值变化事件

View File

@@ -0,0 +1,191 @@
<template>
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="transition: all 0.3s; overflow: hidden">
<div class="mt10 mr10" style="display: flex; justify-content: end" v-if="showBut">
<el-button type="primary" icon="el-icon-Select" @click="save" :loading="loading">保存</el-button>
</div>
<Icon
v-show="menuCollapse"
@click="onMenuCollapse"
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''"
size="18"
class="fold ml10 mt20 menu-collapse"
style="cursor: pointer"
/>
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
<el-tooltip placement="bottom" :hide-after="0" v-if="props.showPush">
<template #content>
<span>台账推送</span>
</template>
<Icon
name="el-icon-Promotion"
size="20"
class="fold ml10 menu-collapse"
style="cursor: pointer"
:style="{ color: config.getColorVal('elementUiPrimary') }"
@click="onAdd"
/>
</el-tooltip>
<!-- <Icon @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" v-else
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
style='cursor: pointer' v-if='props.canExpand' /> -->
</div>
<el-tree
:style="{ height: `calc(100vh - ${height}px)` }"
style="overflow: auto"
ref="treeRef"
:props="defaultProps"
highlight-current
:default-expand-all="false"
@check-change="checkTreeNodeChange"
:filter-node-method="filterNode"
node-key="id"
v-bind="$attrs"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon
:name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
</template>
<script lang="ts" setup>
import useCurrentInstance from '@/utils/useCurrentInstance'
import { ElTree } from 'element-plus'
import { ref, watch } from 'vue'
import { useConfig } from '@/stores/config'
defineOptions({
name: 'govern/tree'
})
interface Props {
width?: string
canExpand?: boolean
showPush?: boolean
showBut?: boolean
height?: number
}
const loading = ref(false)
const props = withDefaults(defineProps<Props>(), {
width: '280px',
canExpand: true,
showPush: false,
showBut: true,
height: 267
})
const config = useConfig()
const { proxy } = useCurrentInstance()
const menuCollapse = ref(false)
const filterText = ref('')
const defaultProps = {
label: 'name',
value: 'id'
}
const emit = defineEmits(['checkTreeNodeChange', 'onAdd', 'checkChange'])
watch(filterText, val => {
treeRef.value!.filter(val)
})
const onMenuCollapse = () => {
menuCollapse.value = !menuCollapse.value
proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
}
const save = () => {
loading.value = true
emit('checkChange')
}
const filterNode = (value: string, data: any, node: any) => {
console.log(value, data, node, 'filterNode')
if (!value) return true
// return data.name.includes(value)
if (data.name) {
return chooseNode(value, data, node)
}
}
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配则返回该节点以及其下的所有子节点如果参数是子节点则返回该节点的父节点。name是中文字符enName是英文字符.
const chooseNode = (value: string, data: any, node: any) => {
if (data.name.indexOf(value) !== -1) {
return true
}
const level = node.level
// 如果传入的节点本身就是一级节点就不用校验了
if (level === 1) {
return false
}
// 先取当前节点的父节点
let parentData = node.parent
// 遍历当前节点的父节点
let index = 0
while (index < level - 1) {
// 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤
if (parentData.data.name.indexOf(value) !== -1) {
return true
}
// 否则的话再往上一层做匹配
parentData = parentData.parent
index++
}
// 没匹配到返回false
return false
}
const checkTreeNodeChange = () => {
// console.log(treeRef.value?.getCheckedNodes(), "ikkkkkiisiiisis");
emit('checkTreeNodeChange', treeRef.value?.getCheckedNodes())
}
const onAdd = () => {
emit('onAdd')
}
const treeRef = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef, loading })
</script>
<style lang="scss" scoped>
.cn-tree {
flex-shrink: 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 10px;
height: 100%;
width: 100%;
:deep(.el-tree) {
border: 1px solid var(--el-border-color);
}
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
background-color: var(--el-color-primary-light-7);
}
.menu-collapse {
color: var(--el-color-primary);
}
}
.custom-tree-node {
display: flex;
align-items: center;
}
</style>

View File

@@ -5,7 +5,7 @@
style='cursor: pointer' /> style='cursor: pointer' />
<div class='cn-tree' :style='{ opacity: menuCollapse ? 0 : 1 }'> <div class='cn-tree' :style='{ opacity: menuCollapse ? 0 : 1 }'>
<div style='display: flex; align-items: center' class='mb10'> <div style='display: flex; align-items: center' class='mb10'>
<el-input maxlength="32" show-word-limit v-model.trim='filterText' placeholder='请输入内容' clearable> <el-input maxlength="32" v-model.trim='filterText' placeholder='请输入内容' clearable>
<template #prefix> <template #prefix>
<Icon name='el-icon-Search' style='font-size: 16px' /> <Icon name='el-icon-Search' style='font-size: 16px' />
</template> </template>
@@ -14,7 +14,7 @@
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse' :class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
style='cursor: pointer' v-if='props.canExpand' /> --> style='cursor: pointer' v-if='props.canExpand' /> -->
</div> </div>
<el-tree :style="{ height: 'calc(100vh - 110px)' }" <el-tree :style="{ height: 'calc(100vh - 230px)' }"
style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false" style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false"
@check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'> @check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'>
<template #default='{ node, data }'> <template #default='{ node, data }'>
@@ -32,7 +32,6 @@
<script lang='ts' setup> <script lang='ts' setup>
import useCurrentInstance from '@/utils/useCurrentInstance' import useCurrentInstance from '@/utils/useCurrentInstance'
import { ElTree } from 'element-plus' import { ElTree } from 'element-plus'
import { emit } from 'process';
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
defineOptions({ defineOptions({

View File

@@ -1,27 +1,61 @@
<!-- 设备管理使用折叠面板渲染多个tree --> <!-- 设备管理使用折叠面板渲染多个tree -->
<template> <template>
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="display: flex; overflow: hidden"> <div :style="{ width: menuCollapse ? '40px' : props.width }" style="display: flex; overflow: hidden">
<Icon v-show="menuCollapse" @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <Icon
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 mt20 menu-collapse" v-show="menuCollapse"
style="cursor: pointer" /> @click="onMenuCollapse"
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''"
size="18"
class="fold ml10 mt20 menu-collapse"
style="cursor: pointer"
/>
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }"> <div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
<div style="display: flex; align-items: center" class="mb10"> <div style="display: flex; align-items: center" class="mb10">
<!-- <el-form-item> --> <!-- <el-form-item> -->
<el-input maxlength="32" show-word-limit v-model.trim="filterText" autocomplete="off"
placeholder="请输入内容" clearable> <el-input
maxlength="32"
v-model.trim="filterText"
autocomplete="off"
placeholder="请输入内容"
clearable
>
<template #prepend>
<el-select v-model="treeType" @change="changeTreeType" style="min-width: 75px">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template #prefix> <template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" /> <Icon name="el-icon-Search" style="font-size: 16px" />
</template> </template>
</el-input> </el-input>
<!-- </el-form-item> --> <!-- </el-form-item> -->
<Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <Icon
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse" @click="onMenuCollapse"
style="cursor: pointer" v-if="props.canExpand" /> :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''"
size="18"
class="fold ml10 menu-collapse"
style="cursor: pointer"
v-if="props.canExpand"
/>
</div> </div>
<el-collapse :accordion="true" v-model.trim="activeName" style="flex: 1; height: 100%" <el-collapse
@change="changeDevice"> :accordion="true"
v-model.trim="activeName"
style="flex: 1; height: 100%"
@change="changeDevice"
v-if="treeType == '1'"
v-loading="loading"
>
<el-collapse-item title="治理设备" name="0" v-if="zlDeviceData.length != 0"> <el-collapse-item title="治理设备" name="0" v-if="zlDeviceData.length != 0">
<el-select v-model.trim="process" clearable placeholder="请选择状态" class="mb10"> <el-select v-model.trim="process" clearable placeholder="请选择状态" class="mb10">
<el-option label="功能调试" value="2"></el-option> <el-option label="功能调试" value="2"></el-option>
@@ -29,13 +63,30 @@
<el-option label="正式投运" value="4"></el-option> <el-option label="正式投运" value="4"></el-option>
</el-select> </el-select>
<el-tree <el-tree
:style="{ height: bxsDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 278px)' }" :style="{
ref="treeRef1" :props="defaultProps" highlight-current :filter-node-method="filterNode" height:
node-key="id" :default-expand-all="false" v-bind="$attrs" :data="zlDevList" style="overflow: auto"> treeType.length != 0
? `calc(100vh - 380px - ${props.height}px)`
: 'calc(100vh - 278px)'
}"
ref="treeRef1"
:props="defaultProps"
highlight-current
:filter-node-method="filterNode"
node-key="id"
:default-expand-all="false"
v-bind="$attrs"
:data="zlDevList"
style="overflow: auto"
>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon
v-if="data.icon" /> :name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span> <span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
@@ -43,35 +94,93 @@
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length != 0"> <el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length != 0">
<el-tree <el-tree
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 280px)' : 'calc(100vh - 238px)' }" :style="{
ref="treeRef2" :props="defaultProps" highlight-current :default-expand-all="false" height:
:filter-node-method="filterNode" node-key="id" :data="bxsDeviceData" v-bind="$attrs" zlDeviceData.length != 0
style="overflow: auto"> ? `calc(100vh - 340px - ${props.height}px)`
: 'calc(100vh - 238px)'
}"
ref="treeRef2"
:props="defaultProps"
highlight-current
:default-expand-all="false"
:filter-node-method="filterNode"
node-key="id"
:data="bxsDeviceData"
v-bind="$attrs"
style="overflow: auto"
>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon
v-if="data.icon" /> :name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span> <span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="在线设备" name="2" v-if="frontDeviceData.length != 0"> <el-collapse-item title="监测设备" name="2" v-if="frontDeviceData.length != 0">
<el-tree <el-tree
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 280px)' : 'calc(100vh - 238px)' }" :style="{
ref="treeRef3" :props="defaultProps" highlight-current :default-expand-all="false" height:
:filter-node-method="filterNode" node-key="id" :data="frontDeviceData" v-bind="$attrs" zlDeviceData.length != 0
style="overflow: auto"> ? `calc(100vh - 340px - ${props.height}px)`
: 'calc(100vh - 238px)'
}"
ref="treeRef3"
:props="defaultProps"
highlight-current
:default-expand-all="false"
:filter-node-method="filterNode"
node-key="id"
:data="frontDeviceData"
v-bind="$attrs"
style="overflow: auto"
>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon
v-if="data.icon" /> :name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span> <span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
<div v-if="treeType == '2'" v-loading="loading">
<el-tree
:style="{ height: `calc(100vh - 188px - ${props.height}px )` }"
ref="treeRef4"
:props="defaultProps"
highlight-current
:filter-node-method="filterNode"
node-key="id"
v-bind="$attrs"
:data="data"
style="overflow: auto"
:default-expand-all="false"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon
:name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -84,20 +193,35 @@ import { ref, watch, defineEmits, onMounted, nextTick } from 'vue'
defineOptions({ defineOptions({
name: 'govern/tree' name: 'govern/tree'
}) })
const emit = defineEmits(['changeDeviceType']) const emit = defineEmits(['changeDeviceType', 'changeTreeType'])
interface Props { interface Props {
width?: string width?: string
canExpand?: boolean canExpand?: boolean
type?: string type?: string
data?: any data?: any
height?: number
engineering: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
width: '280px', width: '280px',
canExpand: true, canExpand: true,
type: '', type: '',
data: [] data: [],
height: 0,
engineering: false
}) })
const treeType = ref('1')
const options = [
{
label: '设备',
value: '1'
},
{
label: '工程',
value: '2'
}
]
const { proxy } = useCurrentInstance() const { proxy } = useCurrentInstance()
const menuCollapse = ref(false) const menuCollapse = ref(false)
const activeName = ref('0') const activeName = ref('0')
@@ -124,19 +248,18 @@ watch(
item.children.map((vv: any) => { item.children.map((vv: any) => {
zlDeviceData.value.push(vv) zlDeviceData.value.push(vv)
}) })
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value)) zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
} else if (item.name == '便携式设备') { } else if (item.name == '便携式设备') {
bxsDeviceData.value = [] bxsDeviceData.value = []
item.children.map((vv: any) => { item.children.map((vv: any) => {
bxsDeviceData.value.push(vv) bxsDeviceData.value.push(vv)
}) })
}else if (item.name == '在线设备') { } else if (item.name == '监测设备') {
frontDeviceData.value = [] frontDeviceData.value = []
item.children.map((vv: any) => { item.children.map((vv: any) => {
frontDeviceData.value.push(vv) frontDeviceData.value.push(vv)
}) })
} }
}) })
} }
@@ -148,7 +271,9 @@ watch(
) )
watch(filterText, val => { watch(filterText, val => {
if (activeName.value == '0') { if (treeType.value == '2') {
treeRef4.value!.filter(val)
} else if (activeName.value == '0') {
treeRef1.value!.filter(val) treeRef1.value!.filter(val)
} else if (activeName.value == '1') { } else if (activeName.value == '1') {
treeRef2.value!.filter(val) treeRef2.value!.filter(val)
@@ -157,8 +282,9 @@ watch(filterText, val => {
} }
}) })
watch(process, val => { watch(process, val => {
if (val == '') { if (val == '' || val == undefined) {
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value)) zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
console.log('🚀 ~ zlDevList.value:', zlDeviceData.value)
} else { } else {
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value))) zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
} }
@@ -176,7 +302,7 @@ function filterProcess(nodes: any) {
const children = node.children ? filterProcess(node.children) : [] const children = node.children ? filterProcess(node.children) : []
// 如果当前节点的process=4或者有子节点满足条件则保留当前节点 // 如果当前节点的process=4或者有子节点满足条件则保留当前节点
if ( node.process == process.value || children.length > 0) { if (node.process == process.value || children.length > 0) {
return { return {
...node, ...node,
children: children children: children
@@ -228,7 +354,6 @@ const chooseNode = (value: string, data: any, node: any) => {
} }
const changeDevice = (val: any) => { const changeDevice = (val: any) => {
console.log('changeDevice', val)
let arr1: any = [] let arr1: any = []
//zlDeviceData //zlDeviceData
@@ -259,22 +384,30 @@ const changeDevice = (val: any) => {
arr2.map((item: any) => { arr2.map((item: any) => {
item.checked = false item.checked = false
}) })
treeRef1.value && treeRef1.value.setCurrentKey(arr1[0]?.id)
emit('changeDeviceType', activeName.value, arr1[0]) emit('changeDeviceType', activeName.value, arr1[0])
setTimeout(() => {
treeRef1.value?.setCurrentKey(arr1[0]?.id)
}, 100)
} }
if (val == '1') { if (val == '1') {
arr1.map((item: any) => { arr1.map((item: any) => {
item.checked = false item.checked = false
}) })
treeRef2.value && treeRef2.value.setCurrentKey(arr2[0]?.id)
emit('changeDeviceType', activeName.value, arr2[0]) emit('changeDeviceType', activeName.value, arr2[0])
setTimeout(() => {
treeRef2.value?.setCurrentKey(arr2[0]?.id)
}, 100)
} }
if (val == '2') { if (val == '2') {
arr3.map((item: any) => { arr3.map((item: any) => {
item.checked = false item.checked = false
}) })
treeRef3.value && treeRef3.value.setCurrentKey(arr3[0]?.id)
emit('changeDeviceType', activeName.value, arr3[0]) emit('changeDeviceType', activeName.value, arr3[0])
setTimeout(() => {
treeRef3.value?.setCurrentKey(arr3[0]?.id)
}, 100)
} }
} }
//治理 //治理
@@ -283,24 +416,44 @@ const treeRef1 = ref<InstanceType<typeof ElTree>>()
const treeRef2 = ref<InstanceType<typeof ElTree>>() const treeRef2 = ref<InstanceType<typeof ElTree>>()
//前置 //前置
const treeRef3 = ref<InstanceType<typeof ElTree>>() const treeRef3 = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef1, treeRef2 }) const treeRef4 = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
onMounted(() => { onMounted(() => {
treeType.value = props.engineering ? '2' : '1'
setTimeout(() => { setTimeout(() => {
if (zlDeviceData.value.length != 0) { setActiveName()
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
activeName.value = '0'
}
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length != 0) {
activeName.value = '1'
}
if (!zlDeviceData.value && !bxsDeviceData.value) {
activeName.value = ''
}
nextTick(() => {
changeDevice(activeName.value)
})
}, 500) }, 500)
}) })
const setActiveName = () => {
if (zlDeviceData.value.length != 0) {
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
activeName.value = '0'
}
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length != 0) {
activeName.value = '1'
}
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length === 0) {
activeName.value = '2'
}
if (!zlDeviceData.value && !bxsDeviceData.value) {
activeName.value = ''
}
nextTick(() => {
changeDevice(activeName.value)
})
}
const loading = ref(false)
const changeTreeType = (val: string) => {
loading.value = true
emit('changeTreeType', val)
if (val == '1') {
setActiveName()
}
setTimeout(() => {
loading.value = false
}, 1000)
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -330,4 +483,7 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
} }
:deep(.el-input-group__prepend) {
background-color: var(--el-fill-color-blank);
}
</style> </style>

View File

@@ -1,159 +1,97 @@
<template> <template>
<Tree ref="treRef" :width="width" :data="tree" default-expand-all @changePointType="changePointType" @onAdd="onAdd"/> <Tree
ref="treRef"
:width="width"
:showPush="props.showPush"
:expand-on-click-node="false"
:data="tree"
@changePointType="changePointType"
@onAdd="onAdd"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, nextTick, onMounted, defineProps } from 'vue' import { ref, nextTick, onMounted, defineProps } from 'vue'
import Tree from '../index.vue' import Tree from '../index.vue'
import { getLineTree,getCldTree } from '@/api/cs-device-boot/csLedger' import { getLineTree, getCldTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
interface Props { interface Props {
template?: boolean template?: boolean
showPush?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
template: false template: false,
showPush: false
}) })
defineOptions({ defineOptions({
name: 'govern/deviceTree' name: 'govern/deviceTree'
}) })
const emit = defineEmits(['init', 'checkChange', 'pointTypeChange', 'Policy','onAdd']) const emit = defineEmits(['init', 'checkChange', 'pointTypeChange', 'Policy', 'onAdd'])
const config = useConfig() const config = useConfig()
const tree = ref() const tree = ref()
const dictData = useDictData() const dictData = useDictData()
const treRef = ref() const treRef = ref()
const width = ref('') const width = ref('')
const info = (selectedNodeId?: string) => { const info = (selectedNodeId?: string) => {
tree.value = [] tree.value = []
let arr1: any[] = [] let arr1: any[] = []
getCldTree().then(res => { getCldTree().then(res => {
try { res.data.icon = 'el-icon-Menu'
// 检查响应数据结构 res.data.color = config.getColorVal('elementUiPrimary')
let rootData = null; res.data?.children.map((item: any) => {
if (Array.isArray(res.data)) { item.icon = 'el-icon-HomeFilled'
// 旧的数据结构 - 数组 item.color = config.getColorVal('elementUiPrimary')
rootData = res.data.find((item: any) => item.name == '在线设备'); item.children.forEach((item: any) => {
} else if (res.data && res.data.name == '在线设备') { item.icon = 'el-icon-List'
// 新的数据结构 - 单个对象 item.color = config.getColorVal('elementUiPrimary')
rootData = res.data; item.children.forEach((item2: any) => {
} // item2.icon = 'el-icon-List'
// item2.color = config.getColorVal('elementUiPrimary')
// 治理设备 item2.icon = 'el-icon-Platform'
if (rootData) { item2.level = 2
rootData.icon = 'el-icon-Menu' item2.color = item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
rootData.level = 0 item2.children.forEach((item3: any) => {
rootData.color = config.getColorVal('elementUiPrimary') item3.icon = 'el-icon-Platform'
// 确保根节点的 children 是数组 item3.color =
if (!Array.isArray(rootData.children)) { item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
rootData.children = [] arr1.push(item3)
}
rootData.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.level = 1
item.color = config.getColorVal('elementUiPrimary')
// 确保 children 是数组
if (!Array.isArray(item.children)) {
item.children = []
}
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.level = 2
item2.color = config.getColorVal('elementUiPrimary')
// 确保 children 是数组
if (!Array.isArray(item2.children)) {
item2.children = []
}
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.level = 3
item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
// 确保 children 是数组
if (!Array.isArray(item3.children)) {
item3.children = []
}
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Platform'
item4.level = 4
item4.color =
item4.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
arr1.push(item4)
})
})
}) })
}) })
tree.value = [rootData] // 确保 tree.value 是数组
} else {
tree.value = []
}
nextTick(() => {
if (arr1.length) {
// 安全检查 treRef 和 treeRef1 是否存在
if (treRef.value && treRef.value.treeRef1 && treRef.value.treeRef1.setCurrentKey) {
// 如果传入了要选中的节点ID则选中该节点否则选中第一个节点
console.log('selectedNodeId:', selectedNodeId);
if (selectedNodeId) {
treRef.value.treeRef1.setCurrentKey(selectedNodeId);
// 查找对应的节点数据并触发事件
let selectedNode = null;
const findNode = (nodes: any[]) => {
for (const node of nodes) {
if (node.id === selectedNodeId) {
selectedNode = node;
return true;
}
if (node.children && findNode(node.children)) {
return true;
}
}
return false;
};
findNode(tree.value);
if (selectedNode) {
emit('init', {
level: selectedNode.level,
...selectedNode
});
}
} else {
// 初始化选中第一个节点
treRef.value.treeRef1.setCurrentKey(arr1[0].id);
emit('init', {
level: 2,
...arr1[0]
});
}
}
} else {
}
}) })
} catch (error) { })
console.error('Error in processing getCldTree response:', error) tree.value = [res.data]
}
nextTick(() => {
setTimeout(() => {
//初始化选中
treRef.value?.treeRef.setCurrentKey(arr1[0].id)
// 注册父组件事件
emit('init', {
level: 3,
...arr1[0]
})
changePointType('4', arr1[0])
return
}, 500)
})
}) })
} }
const changePointType = (val: any, obj: any) => { const changePointType = (val: any, obj: any) => {
emit('pointTypeChange', val, obj) // emit('pointTypeChange', val, obj)
} }
const onAdd = () => { const onAdd = () => {
emit('onAdd') emit('onAdd')
} }
if (props.template) { if (props.template) {
getTemplateByDept({ id: dictData.state.area[0].id }) querySysExcel({ id: dictData.state.area[0]?.id })
.then((res: any) => { .then((res: any) => {
emit('Policy', res.data) emit('Policy', res.data)
info() info()

View File

@@ -0,0 +1,106 @@
<template>
<Tree
ref="treRef"
:width="width"
:showPush="props.showPush"
:data="tree"
default-expand-all
@changePointType="changePointType"
@onAdd="onAdd"
/>
</template>
<script lang="ts" setup>
import { ref, nextTick, onMounted, defineProps } from 'vue'
import Tree from '../index.vue'
import { getLineTree, objTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config'
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData'
interface Props {
template?: boolean
showPush?: boolean
}
const props = withDefaults(defineProps<Props>(), {
template: false,
showPush: false
})
defineOptions({
name: 'govern/deviceTree'
})
const emit = defineEmits(['init', 'checkChange', 'pointTypeChange', 'Policy', 'onAdd'])
const config = useConfig()
const tree = ref()
const dictData = useDictData()
const treRef = ref()
const width = ref('')
const info = (selectedNodeId?: string) => {
tree.value = []
let arr1: any[] = []
objTree().then(res => {
try {
res.data.map((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.level = 1
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item: any) => {
item.icon = 'el-icon-List'
item.level = 2
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
arr1.push(item2)
item2.icon = 'el-icon-Platform'
item2.level = 3
item2.color = config.getColorVal('elementUiPrimary')
})
})
})
tree.value = res.data
nextTick(() => {
if (arr1.length) {
//初始化选中
treRef.value.treeRef.setCurrentKey(arr1[0].id)
// 注册父组件事件
emit('init', arr1[0])
return
} else {
emit('init')
return
}
})
} catch (error) {
console.error('Error in processing getCldTree response:', error)
}
})
}
const changePointType = (val: any, obj: any) => {
emit('pointTypeChange', val, obj)
}
const onAdd = () => {
emit('onAdd')
}
if (props.template) {
querySysExcel({ id: dictData.state.area[0]?.id })
.then((res: any) => {
emit('Policy', res.data)
info()
})
.catch(err => {
info()
})
} else {
info()
}
// 暴露 info 方法给父组件调用
defineExpose({
info
})
onMounted(() => {})
</script>

View File

@@ -0,0 +1,187 @@
<template>
<Tree
ref="treRef"
:width="width"
:showPush="props.showPush"
:data="tree"
default-expand-all
@changePointType="changePointType"
@onAdd="onAdd"
/>
</template>
<script lang="ts" setup>
import { ref, nextTick, onMounted, defineProps } from 'vue'
import Tree from '../index.vue'
import { getLineTree, lineTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config'
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData'
interface Props {
template?: boolean
showPush?: boolean
}
const props = withDefaults(defineProps<Props>(), {
template: false,
showPush: false
})
defineOptions({
name: 'govern/deviceTree'
})
const emit = defineEmits(['init', 'checkChange', 'pointTypeChange', 'Policy', 'onAdd'])
const config = useConfig()
const tree = ref()
const dictData = useDictData()
const treRef = ref()
const width = ref('')
const info = (selectedNodeId?: string) => {
tree.value = []
let arr1: any[] = []
lineTree().then(res => {
try {
// 检查响应数据结构
let rootData = null
if (Array.isArray(res.data)) {
// 旧的数据结构 - 数组
rootData = res.data.find((item: any) => item.name == '监测设备')
} else if (res.data && res.data.name == '监测设备') {
// 新的数据结构 - 单个对象
rootData = res.data
}
// 治理设备
if (rootData) {
rootData.icon = 'el-icon-Menu'
rootData.level = 0
rootData.color = config.getColorVal('elementUiPrimary')
// 确保根节点的 children 是数组
if (!Array.isArray(rootData.children)) {
rootData.children = []
}
rootData.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.level = 1
item.color = config.getColorVal('elementUiPrimary')
// 确保 children 是数组
if (!Array.isArray(item.children)) {
item.children = []
}
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.level = 2
item2.color = config.getColorVal('elementUiPrimary')
// 确保 children 是数组
if (!Array.isArray(item2.children)) {
item2.children = []
}
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.level = 3
item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
// 确保 children 是数组
if (!Array.isArray(item3.children)) {
item3.children = []
}
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Platform'
item4.level = 4
item4.color =
item4.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
arr1.push(item4)
})
})
})
})
tree.value = [rootData] // 确保 tree.value 是数组
} else {
tree.value = []
}
nextTick(() => {
if (arr1.length) {
// 安全检查 treRef 和 treeRef 是否存在
console.log(
'🚀 ~ info ~ treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey:',
treRef.value && treRef.value.treeRef1 && treRef.value.treeRef1.setCurrentKey
)
if (treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey) {
// 如果传入了要选中的节点ID则选中该节点否则选中第一个节点
console.log('selectedNodeId:', selectedNodeId)
if (selectedNodeId) {
treRef.value.treeRef.setCurrentKey(selectedNodeId)
// 查找对应的节点数据并触发事件
let selectedNode = null
const findNode = (nodes: any[]) => {
for (const node of nodes) {
if (node.id === selectedNodeId) {
selectedNode = node
return true
}
if (node.children && findNode(node.children)) {
return true
}
}
return false
}
findNode(tree.value)
if (selectedNode) {
emit('init', {
level: selectedNode.level,
...selectedNode
})
}
} else {
// 初始化选中第一个节点
treRef.value.treeRef.setCurrentKey(arr1[0].id)
emit('init', {
level: 2,
...arr1[0]
})
}
}
} else {
}
})
} catch (error) {
console.error('Error in processing getCldTree response:', error)
}
})
}
const changePointType = (val: any, obj: any) => {
emit('pointTypeChange', val, obj)
}
const onAdd = () => {
emit('onAdd')
}
if (props.template) {
// id: dictData.state.area[0]?.id
querySysExcel({})
.then((res: any) => {
emit('Policy', res.data)
info()
})
.catch(err => {
info()
})
} else {
info()
}
// 暴露 info 方法给父组件调用
defineExpose({
info
})
onMounted(() => {})
</script>

View File

@@ -5,15 +5,20 @@
:default-checked-keys="defaultCheckedKeys" :default-checked-keys="defaultCheckedKeys"
:show-checkbox="props.showCheckbox" :show-checkbox="props.showCheckbox"
:data="tree" :data="tree"
:height="props.height"
@changeDeviceType="changeDeviceType" @changeDeviceType="changeDeviceType"
@changeTreeType="info"
:engineering="props.engineering"
/> />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, nextTick, defineEmits } from 'vue' import { ref, nextTick } from 'vue'
import Tree from '../device.vue' import Tree from '../device.vue'
import { getDeviceTree } from '@/api/cs-device-boot/csLedger' import { getDeviceTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { throttle } from 'lodash'
defineOptions({ defineOptions({
name: 'govern/deviceTree' name: 'govern/deviceTree'
}) })
@@ -21,10 +26,14 @@ const props = withDefaults(
defineProps<{ defineProps<{
showCheckbox?: boolean showCheckbox?: boolean
defaultCheckedKeys?: any defaultCheckedKeys?: any
height?: number
engineering?: boolean
}>(), }>(),
{ {
showCheckbox: false, showCheckbox: false,
defaultCheckedKeys: [] defaultCheckedKeys: [],
height: 0,
engineering: false
} }
) )
const emit = defineEmits(['init', 'checkChange', 'deviceTypeChange']) const emit = defineEmits(['init', 'checkChange', 'deviceTypeChange'])
@@ -32,115 +41,168 @@ const config = useConfig()
const tree = ref() const tree = ref()
const treRef = ref() const treRef = ref()
const changeDeviceType = (val: any, obj: any) => { const changeDeviceType = (val: any, obj: any) => {
console.log("🚀 ~ changeDeviceType ~ val:", val,obj)
emit('deviceTypeChange', val, obj) emit('deviceTypeChange', val, obj)
} }
getDeviceTree().then(res => {
let arr: any[] = [] const info = (type?: string) => {
let arr2: any[] = [] getDeviceTree({ type: type == '2' ? 'engineering' : '' }).then(res => {
let arr3: any[] = [] let arr: any[] = []
//治理设备 let arr2: any[] = []
res.data.map((item: any) => { let arr3: any[] = []
if (item.name == '治理设备') { let arr4: any[] = []
item.children.forEach((item: any) => { //治理设备
res.data.map((item: any) => {
if (type == '2') {
item.icon = 'el-icon-HomeFilled' item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary') item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.level = 2
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
arr.push(item3)
})
})
})
} else if (item.name == '便携式设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-Platform'
item.color = config.getColorVal('elementUiPrimary')
item.color = '#e26257 !important'
item.color = item.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
if (item.type == 'device') {
arr2.push(item)
}
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-Platform'
item2.color = item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
// item2.children.forEach((item3: any) => {
// item3.icon = 'el-icon-Platform'
// item3.color = config.getColorVal('elementUiPrimary')
// if (item3.comFlag === 1) {
// item3.color = '#e26257 !important'
// }
// arr.push(item3)
// })
})
})
}else if (item.name == '在线设备') {
item.children.forEach((item: any) => { item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled' item.icon = 'el-icon-List'
item.color = config.getColorVal('elementUiPrimary') item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => { item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List' item2.icon = 'el-icon-Platform'
item2.color = config.getColorVal('elementUiPrimary') item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => { item2.color =
item3.icon = 'el-icon-Platform' item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item3.color = config.getColorVal('elementUiPrimary') arr4.push(item2)
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
arr3.push(item3)
}) })
}) })
}) } else {
} if (item.name == '治理设备') {
}) item.children.forEach((item: any) => {
console.log("🚀 ~ file: deviceTree.vue ~ line 18 ~ getDeviceTree ~ tree:", arr,arr2,arr3) item.icon = 'el-icon-HomeFilled'
tree.value = res.data item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
nextTick(() => { item2.icon = 'el-icon-List'
if (arr.length) { item2.color = config.getColorVal('elementUiPrimary')
treRef.value.treeRef1.setCurrentKey(arr[0].id) item2.children.forEach((item3: any) => {
// 注册父组件事件 item3.pName = '治理设备'
emit('init', { item3.icon = 'el-icon-Platform'
level: 2, item3.level = 2
...arr[0] item3.color = config.getColorVal('elementUiPrimary')
}) if (item3.comFlag === 1) {
return item3.color = '#e26257 !important'
} }
if (arr2.length) { arr.push(item3)
treRef.value.treeRef2.setCurrentKey(arr2[0].id) })
// 注册父组件事件 })
emit('init', { })
level: 2, } else if (item.name == '便携式设备') {
...arr2[0] item.children.forEach((item: any) => {
}) item.icon = 'el-icon-Platform'
return item.color = config.getColorVal('elementUiPrimary')
} item.color = '#e26257 !important'
console.log("🚀 ~ file: deviceTree.vue ~ line 33 ~ getDeviceTree ~ tree:", arr3.length) item.color = item.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
if (arr3.length) { // item.disabled =true
console.log("🚀 ~ file: deviceTree.vue ~ line 33 ~ getDeviceTree ~ tree:", arr3) item.pName = '便携式设备'
treRef.value.treeRef3.setCurrentKey(arr3[0].id) if (item.type == 'device') {
// 注册父组件事件 arr2.push(item)
emit('init', { }
level: 2, item.children.forEach((item2: any) => {
...arr3[0] item2.icon = 'el-icon-Platform'
}) item2.color =
return item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
} item2.pName = '便携式设备'
else { // item2.children.forEach((item3: any) => {
emit('init') // item3.icon = 'el-icon-Platform'
return // item3.color = config.getColorVal('elementUiPrimary')
} // if (item3.comFlag === 1) {
// item3.color = '#e26257 !important'
// }
// arr.push(item3)
// })
})
})
} else if (item.name == '监测设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => {
item3.pName = '监测设备'
item3.icon = 'el-icon-Platform'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
arr3.push(item3)
})
})
})
}
}
})
tree.value = res.data
nextTick(() => {
setTimeout(() => {
if (type == '2') {
//初始化选中
treRef.value?.treeRef4.setCurrentKey(arr4[0]?.id)
// 注册父组件事件
emit('init', {
level: 2,
...arr4[0]
})
// changePointType('4', arr4[0])
return
}
if (arr.length > 0) {
treRef.value.treeRef1.setCurrentKey(arr[0]?.id)
// 注册父组件事件
emit('init', {
level: 2,
...arr[0]
})
return
} else if (arr2.length > 0) {
treRef.value.treeRef2.setCurrentKey(arr2[0]?.id)
// 注册父组件事件
emit('init', {
level: 2,
...arr2[0]
})
return
} else if (arr3.length > 0) {
console.log('🚀 ~ arr3:', arr3)
treRef.value.treeRef3.setCurrentKey(arr3[0].id)
// 注册父组件事件
emit('init', {
level: 2,
...arr3[0]
})
return
} else {
emit('init')
return
}
}, 500)
})
}) })
}
onMounted(() => {
info(props.engineering ? '2' : '1')
}) })
throttle(
(data: any, checked: any, indeterminate: any) => {
emit('checkChange', {
data,
checked,
indeterminate
})
},
300,
{
leading: true, // 首次触发立即执行(可选,默认 true
trailing: false // 节流结束后是否执行最后一次(可选,默认 true根据需求调整
}
)
const handleCheckChange = (data: any, checked: any, indeterminate: any) => { const handleCheckChange = (data: any, checked: any, indeterminate: any) => {
emit('checkChange', { emit('checkChange', {
data, data,
@@ -148,4 +210,7 @@ const handleCheckChange = (data: any, checked: any, indeterminate: any) => {
indeterminate indeterminate
}) })
} }
defineExpose({
treRef
})
</script> </script>

View File

@@ -19,9 +19,11 @@ getMarketList().then((res: any) => {
color: 'royalblue' color: 'royalblue'
} }
}) })
console.log("🚀 ~ royalblue:")
emit('selectUser', tree.value[0]) emit('selectUser', tree.value[0])
nextTick(() => { nextTick(() => {
treRef.value.treeRef.setCurrentKey(tree.value[0].id) treRef.value.treeRef.setCurrentKey(tree.value[0]?.id)
}) })
} }
}) })

View File

@@ -21,7 +21,7 @@ getFormalUserList().then((res: any) => {
}) })
emit('selectUser', tree.value[0]) emit('selectUser', tree.value[0])
nextTick(() => { nextTick(() => {
treRef.value.treeRef.setCurrentKey(tree.value[0].id) treRef.value.treeRef.setCurrentKey(tree.value[0]?.id)
}) })
} }
}) })

View File

@@ -1,5 +1,12 @@
<template> <template>
<Tree ref="treRef" :width="width" :data="tree" default-expand-all @changePointType="changePointType" /> <Tree
ref="treRef"
:width="width"
:data="tree"
default-expand-all
@changePointType="changePointType"
@changeTreeType="info"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -7,7 +14,7 @@ import { ref, nextTick, onMounted, defineProps } from 'vue'
import Tree from '../point.vue' import Tree from '../point.vue'
import { getLineTree } from '@/api/cs-device-boot/csLedger' import { getLineTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
// const props = defineProps(['template']) // const props = defineProps(['template'])
interface Props { interface Props {
@@ -27,110 +34,157 @@ const dictData = useDictData()
const treRef = ref() const treRef = ref()
const width = ref('') const width = ref('')
const info = () => { const info = (type?: string) => {
tree.value = [] tree.value = []
let arr1: any[] = [] let arr1: any[] = []
let arr2: any[] = [] let arr2: any[] = []
let arr3: any[] = [] let arr3: any[] = []
getLineTree().then(res => { let arr4: any[] = []
getLineTree({ type: type == '2' ? 'engineering' : '' }).then(res => {
//治理设备 //治理设备
res.data.map((item: any) => { res.data.map((item: any) => {
if (item.name == '治理设备') { if (type == '2') {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item: any) => { item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled' item.icon = 'el-icon-List'
item.level = 1
item.color = config.getColorVal('elementUiPrimary') item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => { item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List' // item2.icon = 'el-icon-List'
item2.level = 1 // item2.color = config.getColorVal('elementUiPrimary')
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.level = 2
item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Platform'
item4.color =
item4.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
// item4.color = '#e26257 !important'
arr1.push(item4)
})
})
})
})
} else if (item.name == '便携式设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-Platform'
item.color = config.getColorVal('elementUiPrimary')
item.color = item.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-Platform' item2.icon = 'el-icon-Platform'
item2.level = 2
item2.color = item2.color =
item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important' item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
arr2.push(item2)
})
})
} else if (item.name == '在线设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.level = 1
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.level = 1
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => { item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform' item3.icon = 'el-icon-Platform'
item3.level = 1
item3.color = item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important' item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item3.children.forEach((item4: any) => { arr4.push(item3)
item4.icon = 'el-icon-Platform' // item3.children.forEach((item4: any) => {
item4.color = // item4.icon = 'el-icon-Platform'
item4.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important' // item4.color =
// item4.color = '#e26257 !important' // item4.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
arr3.push(item4)
}) // })
}) })
}) })
}) })
} else {
if (item.name == '治理设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.level = 1
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.level = 1
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.level = 2
item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Platform'
item4.color =
item4.comFlag === 2
? config.getColorVal('elementUiPrimary')
: '#e26257 !important'
// item4.color = '#e26257 !important'
arr1.push(item4)
})
})
})
})
} else if (item.name == '便携式设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-Platform'
item.color = config.getColorVal('elementUiPrimary')
item.color = item.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-Platform'
item2.color =
item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
arr2.push(item2)
})
})
} else if (item.name == '监测设备') {
item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.level = 1
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-List'
item2.level = 1
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.level = 1
item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Platform'
item4.color =
item4.comFlag === 2
? config.getColorVal('elementUiPrimary')
: '#e26257 !important'
// item4.color = '#e26257 !important'
arr3.push(item4)
})
})
})
})
}
} }
}) })
tree.value = res.data tree.value = res.data
nextTick(() => { nextTick(() => {
if (arr1.length) { setTimeout(() => {
//初始化选中 if (type == '2') {
treRef.value.treeRef1.setCurrentKey(arr1[0].id) //初始化选中
// 注册父组件事件
emit('init', { treRef.value?.treeRef4.setCurrentKey(arr4[0].id)
level: 2, // 注册父组件事件
...arr1[0] emit('init', {
}) level: 3,
return ...arr4[0]
} })
if (arr2.length) { changePointType('4', arr4[0])
//初始化选中 return
treRef.value.treeRef2.setCurrentKey(arr2[0].id) } else if (arr1.length > 0) {
// 注册父组件事件 //初始化选中
emit('init', { treRef.value?.treeRef1.setCurrentKey(arr1[0].id)
level: 2, // 注册父组件事件
...arr2[0] emit('init', {
}) level: 2,
return ...arr1[0]
} })
if(arr3.length){ return
treRef.value.treeRef3.setCurrentKey(arr3[0].id) } else if (arr2.length > 0) {
emit('init', { //初始化选中
level: 2, treRef.value?.treeRef2.setCurrentKey(arr2[0].id)
...arr3[0] // 注册父组件事件
}) emit('init', {
return level: 2,
} ...arr2[0]
else { })
emit('init') return
return } else if (arr3.length > 0) {
} treRef.value?.treeRef3?.setCurrentKey(arr3[0].id)
emit('init', {
level: 2,
...arr3[0]
})
return
} else {
emit('init')
return
}
}, 500)
}) })
}) })
} }
@@ -138,7 +192,8 @@ const changePointType = (val: any, obj: any) => {
emit('pointTypeChange', val, obj) emit('pointTypeChange', val, obj)
} }
if (props.template) { if (props.template) {
getTemplateByDept({ id: dictData.state.area[0].id }) // id: dictData.state.area[0]?.id
querySysExcel({})
.then((res: any) => { .then((res: any) => {
emit('Policy', res.data) emit('Policy', res.data)
info() info()

View File

@@ -1,181 +1,191 @@
<template> <template>
<div> <div>
<div style="transition: all 0.3s; overflow: hidden; height: 100%"> <div style="transition: all 0.3s; overflow: hidden; height: 100%">
<div class="cn-tree">
<div class="cn-tree"> <div style="display: flex; align-items: center" class="mb10">
<div style="display: flex; align-items: center" class="mb10"> <el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<el-input maxlength="32" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable> <template #prefix>
<template #prefix> <Icon name="el-icon-Search" style="font-size: 16px" />
<Icon name="el-icon-Search" style="font-size: 16px" /> </template>
</template> </el-input>
</el-input> </div>
</div> <el-tree
<el-tree style="flex: 1; overflow: auto" :props="defaultProps" highlight-current style="flex: 1; overflow: auto"
:filter-node-method="filterNode" node-key="id" v-bind="$attrs" default-expand-all :data="tree" :props="defaultProps"
ref="treRef" @node-click="clickNode" :expand-on-click-node="false"> highlight-current
<template #default="{ node, data }"> :filter-node-method="filterNode"
<span class="custom-tree-node"> node-key="id"
<div class="left"> v-bind="$attrs"
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" default-expand-all
v-if="data.icon" /> :data="tree"
<span>{{ node.label }}</span> ref="treRef"
</div> @node-click="clickNode"
:expand-on-click-node="false"
</span> >
</template> <template #default="{ node, data }">
</el-tree> <span class="custom-tree-node">
</div> <div class="left" style="display: flex; align-items: center">
</div> <Icon
</div> :name="data.icon"
</template> style="font-size: 16px"
:style="{ color: data.color }"
<script lang="ts" setup> v-if="data.icon"
import { ref, nextTick, watch, defineProps, defineEmits } from 'vue' />
import { getSchemeTree, getTestRecordInfo } from '@/api/cs-device-boot/planData' <span style="margin-left: 5px">{{ node.label }}</span>
import { useConfig } from '@/stores/config' </div>
import useCurrentInstance from '@/utils/useCurrentInstance' </span>
import { ElTree } from 'element-plus' </template>
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel' </el-tree>
import { useDictData } from '@/stores/dictData' </div>
defineOptions({ </div>
name: 'govern/schemeTree' </div>
}) </template>
interface Props { <script lang="ts" setup>
template?: boolean import { ref, nextTick, watch, defineProps, defineEmits } from 'vue'
import { getSchemeTree, getTestRecordInfo } from '@/api/cs-device-boot/planData'
} import { useConfig } from '@/stores/config'
const dictData = useDictData() import useCurrentInstance from '@/utils/useCurrentInstance'
const props = withDefaults(defineProps<Props>(), { import { ElTree } from 'element-plus'
template: false, import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData'
}) defineOptions({
const filterText = ref('') name: 'govern/schemeTree'
watch(filterText, val => { })
treRef.value!.filter(val)
}) interface Props {
template?: boolean
const filterNode = (value: string, data: any, node: any) => { }
if (!value) return true const dictData = useDictData()
// return data.name.includes(value) const props = withDefaults(defineProps<Props>(), {
if (data.name) { template: false
return chooseNode(value, data, node) })
} const filterText = ref('')
} watch(filterText, val => {
const chooseNode = (value: string, data: any, node: any) => { treRef.value!.filter(val)
if (data.name.indexOf(value) !== -1) { })
return true
} const filterNode = (value: string, data: any, node: any) => {
const level = node.level if (!value) return true
// 如果传入的节点本身就是一级节点就不用校验了 // return data.name.includes(value)
if (level === 1) { if (data.name) {
return false return chooseNode(value, data, node)
} }
// 先取当前节点的父节点 }
let parentData = node.parent const chooseNode = (value: string, data: any, node: any) => {
// 遍历当前节点的父节点 if (data.name.indexOf(value) !== -1) {
let index = 0 return true
while (index < level - 1) { }
// 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤 const level = node.level
if (parentData.data.name.indexOf(value) !== -1) { // 如果传入的节点本身就是一级节点就不用校验了
return true if (level === 1) {
} return false
// 否则的话再往上一层做匹配 }
parentData = parentData.parent // 先取当前节点的父节点
index++ let parentData = node.parent
} // 遍历当前节点的父节点
// 没匹配到返回false let index = 0
return false while (index < level - 1) {
} // 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤
/** 树形结构数据 */ if (parentData.data.name.indexOf(value) !== -1) {
const defaultProps = { return true
children: 'children', }
label: 'name', // 否则的话再往上一层做匹配
value: 'id' parentData = parentData.parent
} index++
}
// 没匹配到返回false
const emit = defineEmits(['init', 'checkChange', 'nodeChange', 'editNode', 'getChart', 'Policy']) return false
const config = useConfig() }
const tree = ref() /** 树形结构数据 */
const treRef = ref() const defaultProps = {
const id: any = ref(null) children: 'children',
const treeData = ref({}) label: 'name',
//获取方案树形数据 value: 'id'
const getTreeList = () => { }
getSchemeTree().then(res => {
let arr: any[] = [] const emit = defineEmits(['init', 'checkChange', 'nodeChange', 'editNode', 'getChart', 'Policy'])
const config = useConfig()
res.data.forEach((item: any) => { const tree = ref()
item.icon = 'el-icon-Menu' const treRef = ref()
item.color = config.getColorVal('elementUiPrimary') const id: any = ref(null)
item?.children.forEach((item2: any) => { const treeData = ref({})
item2.icon = 'el-icon-Document' //获取方案树形数据
item2.color = config.getColorVal('elementUiPrimary') const getTreeList = () => {
arr.push(item2) getSchemeTree().then(res => {
}) let arr: any[] = []
})
tree.value = res.data res.data.forEach((item: any) => {
nextTick(() => { item.icon = 'el-icon-Menu'
if (arr.length) { item.color = config.getColorVal('elementUiPrimary')
treRef.value.setCurrentKey(id.value || arr[0].id) item?.children.forEach((item2: any) => {
let list = id.value ? arr.find((item: any) => item.id == id.value) : arr[0] item2.icon = 'el-icon-Document'
// 注册父组件事件 item2.color = config.getColorVal('elementUiPrimary')
emit('init', { arr.push(item2)
level: 2, })
...list })
}) tree.value = res.data
} else { nextTick(() => {
emit('init') if (arr.length) {
} treRef.value.setCurrentKey(id.value || arr[0].id)
}) let list = id.value ? arr.find((item: any) => item.id == id.value) : arr[0]
}) // 注册父组件事件
} emit('init', {
level: 2,
//方案id ...list
const planId: any = ref('') })
} else {
const clickNode = (e: anyObj) => { emit('init')
e?.children ? (planId.value = e.id) : (planId.value = e.pid) }
id.value = e.id })
emit('nodeChange', e) })
} }
//方案id
if (props.template) { const planId: any = ref('')
getTemplateByDept({ id: dictData.state.area[0].id }).then((res: any) => {
emit('Policy', res.data) const clickNode = (e: anyObj) => {
getTreeList() e?.children ? (planId.value = e.id) : (planId.value = e.pid)
}).catch(err => { id.value = e.id
getTreeList() emit('nodeChange', e)
}) }
} else {
getTreeList() if (props.template) {
} // id: dictData.state.area[0]?.id
querySysExcel({})
</script> .then((res: any) => {
<style lang="scss" scoped> emit('Policy', res.data)
.cn-tree { getTreeList()
flex-shrink: 0; })
display: flex; .catch(err => {
flex-direction: column; getTreeList()
box-sizing: border-box; })
padding: 10px; } else {
height: 100%; getTreeList()
width: 100%; }
height: calc(100vh - 125px); </script>
overflow-y: auto; <style lang="scss" scoped>
.cn-tree {
:deep(.el-tree) { flex-shrink: 0;
border: 1px solid var(--el-border-color); display: flex;
} flex-direction: column;
box-sizing: border-box;
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) { padding: 10px;
background-color: var(--el-color-primary-light-7); height: 100%;
} width: 100%;
height: calc(100vh - 125px);
.menu-collapse { overflow-y: auto;
color: var(--el-color-primary);
} :deep(.el-tree) {
} border: 1px solid var(--el-border-color);
</style> }
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
background-color: var(--el-color-primary-light-7);
}
.menu-collapse {
color: var(--el-color-primary);
}
}
</style>

View File

@@ -33,7 +33,7 @@ const info = () => {
getLineTree().then(res => { getLineTree().then(res => {
//治理设备 //治理设备
res.data.map((item: any) => { res.data.map((item: any) => {
if (item.name == '在线设备') { if (item.name == '监测设备') {
item.children.forEach((item: any) => { item.children.forEach((item: any) => {
item.icon = 'el-icon-HomeFilled' item.icon = 'el-icon-HomeFilled'
item.level = 1 item.level = 1
@@ -59,7 +59,7 @@ const info = () => {
}) })
} }
}) })
tree.value = res.data.filter(item => item.name == '在线设备') tree.value = res.data.filter(item => item.name == '监测设备')
nextTick(() => { nextTick(() => {
if (arr3.length) { if (arr3.length) {
//初始化选中 //初始化选中
@@ -87,7 +87,7 @@ const handleCheckedNodesChange = (nodes: any[]) => {
if (props.template) { if (props.template) {
getTemplateByDept({ id: dictData.state.area[0].id }) getTemplateByDept({ id: dictData.state.area[0]?.id })
.then((res: any) => { .then((res: any) => {
emit('Policy', res.data) emit('Policy', res.data)
info() info()

View File

@@ -1,41 +1,61 @@
<template> <template>
<div :style="{ width: menuCollapse ? '40px' : props.width }" style='transition: all 0.3s; overflow: hidden;'> <div :style="{ width: menuCollapse ? '40px' : props.width }" style="transition: all 0.3s; overflow: hidden">
<Icon v-show='menuCollapse' @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <Icon
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 mt20 menu-collapse' v-show="menuCollapse"
style='cursor: pointer' /> @click="onMenuCollapse"
<div class='cn-tree' :style='{ opacity: menuCollapse ? 0 : 1 }'> :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
<div style='display: flex; align-items: center' class='mb10'> :class="menuCollapse ? 'unfold' : ''"
<el-input maxlength="32" show-word-limit v-model.trim='filterText' placeholder='请输入内容' clearable> size="18"
class="fold ml10 mt20 menu-collapse"
style="cursor: pointer"
/>
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix> <template #prefix>
<Icon name='el-icon-Search' style='font-size: 16px' /> <Icon name="el-icon-Search" style="font-size: 16px" />
</template> </template>
</el-input> </el-input>
<el-tooltip placement="bottom" :hide-after="0"> <el-tooltip placement="bottom" :hide-after="0" v-if="props.showPush">
<template #content> <template #content>
<span>台账推送</span> <span>台账推送</span>
</template> </template>
<Icon <Icon
name="el-icon-Promotion" name="el-icon-Promotion"
size="20" size="20"
class="fold ml10 menu-collapse" class="fold ml10 mr10 menu-collapse"
style="cursor: pointer;" style="cursor: pointer"
:style="{ color: config.getColorVal('elementUiPrimary') }" :style="{ color: config.getColorVal('elementUiPrimary') }"
@click="onAdd" /> @click="onAdd"
/>
</el-tooltip> </el-tooltip>
<!-- <Icon @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <!-- <Icon @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" v-else
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse' :class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
style='cursor: pointer' v-if='props.canExpand' /> --> style='cursor: pointer' v-if='props.canExpand' /> -->
</div> </div>
<el-tree :style="{ height: 'calc(100vh - 200px)' }" <el-tree
style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false" :style="{ height: 'calc(100vh - 190px)' }"
@check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'> style="overflow: auto"
<template #default='{ node, data }'> ref="treeRef"
<span class='custom-tree-node'> :props="defaultProps"
<Icon :name='data.icon' style='font-size: 16px' :style='{ color: data.color }' highlight-current
v-if='data.icon' /> :default-expand-all="false"
<span style='margin-left: 4px'>{{ node.label }}</span> @check-change="checkTreeNodeChange"
:filter-node-method="filterNode"
node-key="id"
v-bind="$attrs"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon
:name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
@@ -43,12 +63,10 @@
</div> </div>
</template> </template>
<script lang='ts' setup> <script lang="ts" setup>
import useCurrentInstance from '@/utils/useCurrentInstance' import useCurrentInstance from '@/utils/useCurrentInstance'
import { ElTree } from 'element-plus' import { ElTree } from 'element-plus'
import { emit } from 'process';
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { t } from 'vxe-table';
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
defineOptions({ defineOptions({
@@ -58,11 +76,13 @@ defineOptions({
interface Props { interface Props {
width?: string width?: string
canExpand?: boolean canExpand?: boolean
showPush?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
width: '280px', width: '280px',
canExpand: true canExpand: true,
showPush: false
}) })
const config = useConfig() const config = useConfig()
const { proxy } = useCurrentInstance() const { proxy } = useCurrentInstance()
@@ -72,7 +92,7 @@ const defaultProps = {
label: 'name', label: 'name',
value: 'id' value: 'id'
} }
const emit = defineEmits(['checkTreeNodeChange','onAdd']) const emit = defineEmits(['checkTreeNodeChange', 'onAdd'])
watch(filterText, val => { watch(filterText, val => {
treeRef.value!.filter(val) treeRef.value!.filter(val)
}) })
@@ -81,18 +101,16 @@ const onMenuCollapse = () => {
proxy.eventBus.emit('cnTreeCollapse', menuCollapse) proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
} }
const filterNode = (value: string, data: any, node: any) => { const filterNode = (value: string, data: any, node: any) => {
console.log(value, data, node, 'filterNode'); console.log(value, data, node, 'filterNode')
if (!value) return true if (!value) return true
// return data.name.includes(value) // return data.name.includes(value)
if (data.name) { if (data.name) {
return chooseNode(value, data, node) return chooseNode(value, data, node)
} }
} }
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配则返回该节点以及其下的所有子节点如果参数是子节点则返回该节点的父节点。name是中文字符enName是英文字符. // 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配则返回该节点以及其下的所有子节点如果参数是子节点则返回该节点的父节点。name是中文字符enName是英文字符.
const chooseNode = (value: string, data: any, node: any) => { const chooseNode = (value: string, data: any, node: any) => {
if (data.name.indexOf(value) !== -1) { if (data.name.indexOf(value) !== -1) {
return true return true
} }
@@ -130,13 +148,13 @@ const treeRef = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef }) defineExpose({ treeRef })
</script> </script>
<style lang='scss' scoped> <style lang="scss" scoped>
.cn-tree { .cn-tree {
flex-shrink: 0; flex-shrink: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
padding: 10px; // padding: 10px;
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@@ -1,23 +1,58 @@
<!-- 设备监控使用折叠面板渲染多个tree --> <!-- 设备监控使用折叠面板渲染多个tree -->
<template> <template>
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="display: flex; overflow: hidden"> <div :style="{ width: menuCollapse ? '40px' : props.width }" style="display: flex; overflow: hidden">
<Icon v-show="menuCollapse" @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <Icon
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 mt20 menu-collapse" style="cursor: pointer" v-show="menuCollapse"
v-if="route.path != '/admin/govern/reportCore/statistics/index'" /> @click="onMenuCollapse"
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''"
size="18"
class="fold ml10 mt20 menu-collapse"
style="cursor: pointer"
v-if="route.path != '/admin/govern/reportCore/statistics/index'"
/>
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1, display: menuCollapse ? 'none' : '' }"> <div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1, display: menuCollapse ? 'none' : '' }">
<div style="display: flex; align-items: center" class="mb10"> <div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable> <!-- <el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input> -->
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prepend>
<el-select v-model="treeType" @change="changeTreeType" style="min-width: 75px">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template #prefix> <template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" /> <Icon name="el-icon-Search" style="font-size: 16px" />
</template> </template>
</el-input> </el-input>
<Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <!-- -->
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse" <Icon
@click="onMenuCollapse"
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''"
size="18"
class="fold ml10 menu-collapse"
style="cursor: pointer" style="cursor: pointer"
v-if="props.canExpand && route.path != '/admin/govern/reportCore/statistics/index'" /> v-if="props.canExpand && route.path != '/admin/govern/reportCore/statistics/index'"
/>
</div> </div>
<el-collapse :accordion="true" v-model.trim="activeName" style="flex: 1; height: 100%"
@change="changeDevice"> <el-collapse
:accordion="true"
v-model.trim="activeName"
style="flex: 1; height: 100%"
@change="changeDevice"
v-if="treeType == '1'"
v-loading="loading"
>
<el-collapse-item title="治理设备" name="0" v-if="zlDeviceData.length != 0"> <el-collapse-item title="治理设备" name="0" v-if="zlDeviceData.length != 0">
<el-select v-model.trim="process" clearable placeholder="请选择状态" class="mb10"> <el-select v-model.trim="process" clearable placeholder="请选择状态" class="mb10">
<el-option label="功能调试" value="2"></el-option> <el-option label="功能调试" value="2"></el-option>
@@ -26,14 +61,25 @@
</el-select> </el-select>
<el-tree <el-tree
:style="{ height: bxsDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 278px)' }" :style="{ height: treeType.length != 0 ? 'calc(100vh - 380px)' : 'calc(100vh - 278px)' }"
ref="treeRef1" :props="defaultProps" highlight-current :filter-node-method="filterNode" ref="treeRef1"
node-key="id" v-bind="$attrs" :data="zlDevList" style="overflow: auto" :props="defaultProps"
:default-expand-all="false"> highlight-current
:filter-node-method="filterNode"
node-key="id"
v-bind="$attrs"
:data="zlDevList"
style="overflow: auto"
:default-expand-all="false"
>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon
v-if="data.icon" /> :name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span> <span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
@@ -41,35 +87,84 @@
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length != 0"> <el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length != 0">
<el-tree <el-tree
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 330px)' : 'calc(100vh - 238px)' }" :style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)' }"
ref="treeRef2" :props="defaultProps" highlight-current :default-expand-all="false" ref="treeRef2"
:filter-node-method="filterNode" node-key="id" :data="bxsDeviceData" v-bind="$attrs" :props="defaultProps"
style="overflow: auto" > highlight-current
:default-expand-all="false"
:filter-node-method="filterNode"
node-key="id"
:data="bxsDeviceData"
v-bind="$attrs"
style="overflow: auto"
>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon
v-if="data.icon" /> :name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span> <span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="在线设备" name="2" v-if="yqfDeviceData.length != 0"> <el-collapse-item title="监测设备" name="2" v-if="yqfDeviceData.length != 0">
<el-tree <el-tree
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 330px)' : 'calc(100vh - 238px)' }" :style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)' }"
ref="treeRef3" :props="defaultProps" highlight-current :default-expand-all="false" ref="treeRef3"
:filter-node-method="filterNode" node-key="id" :data="yqfDeviceData" v-bind="$attrs" :props="defaultProps"
style="overflow: auto"> highlight-current
:default-expand-all="false"
:filter-node-method="filterNode"
node-key="id"
:data="yqfDeviceData"
v-bind="$attrs"
style="overflow: auto"
>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon
v-if="data.icon" /> :name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span> <span style="margin-left: 4px">{{ node.label }}</span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
<div v-if="treeType == '2'" v-loading="loading">
<el-tree
:style="{ height: 'calc(100vh - 188px)' }"
class="pt10"
ref="treeRef4"
:props="defaultProps"
highlight-current
:filter-node-method="filterNode"
node-key="id"
v-bind="$attrs"
:data="data"
style="overflow: auto"
:default-expand-all="false"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon
:name="data.icon"
style="font-size: 16px"
:style="{ color: data.color }"
v-if="data.icon"
/>
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -83,7 +178,7 @@ import { useRoute } from 'vue-router'
defineOptions({ defineOptions({
name: 'govern/tree' name: 'govern/tree'
}) })
const emit = defineEmits(['changePointType']) const emit = defineEmits(['changePointType', 'changeTreeType'])
interface Props { interface Props {
width?: string width?: string
canExpand?: boolean canExpand?: boolean
@@ -107,38 +202,47 @@ const defaultProps = {
label: 'name', label: 'name',
value: 'id' value: 'id'
} }
const treeType = ref('1')
const options = [
{
label: '设备',
value: '1'
},
{
label: '工程',
value: '2'
}
]
//治理设备数据 //治理设备数据
const zlDeviceData = ref<any>([]) const zlDeviceData = ref<any>([])
const zlDevList = ref<any>([]) const zlDevList = ref<any>([])
//便携式设备数据 //便携式设备数据
const bxsDeviceData = ref<any>([]) const bxsDeviceData = ref<any>([])
//在线设备数据 //监测设备数据
const yqfDeviceData = ref<any>([]) const yqfDeviceData = ref<any>([])
watch( watch(
() => props.data, () => props.data,
(val, oldVal) => { (val, oldVal) => {
if (val && val.length != 0) { if (val && val.length != 0) {
val.map((item: any) => { val.map((item: any) => {
if (item.name == '治理设备') { if (item.name == '治理设备') {
zlDeviceData.value = [] zlDeviceData.value = []
item.children.map((vv: any) => { item.children.map((vv: any) => {
zlDeviceData.value.push(vv) zlDeviceData.value.push(vv)
}) })
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value)) zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
} else if (item.name == '便携式设备') { } else if (item.name == '便携式设备') {
bxsDeviceData.value = [] bxsDeviceData.value = []
item.children.map((vv: any) => { item.children.map((vv: any) => {
bxsDeviceData.value.push(vv) bxsDeviceData.value.push(vv)
}) })
}else if (item.name == '在线设备') { } else if (item.name == '监测设备') {
yqfDeviceData.value = [] yqfDeviceData.value = []
item.children.map((vv: any) => { item.children.map((vv: any) => {
yqfDeviceData.value.push(vv) yqfDeviceData.value.push(vv)
}) })
} }
}) })
} }
}, },
{ {
@@ -148,7 +252,9 @@ watch(
) )
watch(filterText, val => { watch(filterText, val => {
if (activeName.value == '0') { if (treeType.value == '2') {
treeRef4.value!.filter(val)
} else if (activeName.value == '0') {
treeRef1.value!.filter(val) treeRef1.value!.filter(val)
} else if (activeName.value == '1') { } else if (activeName.value == '1') {
treeRef2.value!.filter(val) treeRef2.value!.filter(val)
@@ -157,12 +263,10 @@ watch(filterText, val => {
} }
}) })
watch(process, val => { watch(process, val => {
if (val == '' || val == undefined) {
if (val == '') {
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value)) zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
} else { } else {
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value))) zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
} }
setTimeout(() => { setTimeout(() => {
@@ -170,10 +274,7 @@ watch(process, val => {
}, 0) }, 0)
}) })
const changeDevice = (val: any) => { const changeDevice = (val: any) => {
let arr1: any = [] let arr1: any = []
//zlDeviceData //zlDeviceData
zlDevList.value.forEach((item: any) => { zlDevList.value.forEach((item: any) => {
@@ -206,22 +307,28 @@ const changeDevice = (val: any) => {
arr2.map((item: any) => { arr2.map((item: any) => {
item.checked = false item.checked = false
}) })
treeRef1?.value && treeRef1.value.setCurrentKey(arr1[0]?.id)
emit('changePointType', activeName.value, arr1[0]) emit('changePointType', activeName.value, arr1[0])
setTimeout(() => {
treeRef1.value?.setCurrentKey(arr1[0]?.id)
}, 100)
} }
if (val == '1') { if (val == '1') {
arr1.map((item: any) => { arr1.map((item: any) => {
item.checked = false item.checked = false
}) })
treeRef2?.value && treeRef2.value.setCurrentKey(arr2[0]?.id)
emit('changePointType', activeName.value, arr2[0]) emit('changePointType', activeName.value, arr2[0])
setTimeout(() => {
treeRef2.value?.setCurrentKey(arr2[0]?.id)
}, 100)
} }
if (val == '2') { if (val == '2') {
arr3.map((item: any) => { arr3.map((item: any) => {
item.checked = false item.checked = false
}) })
treeRef3?.value && treeRef3.value.setCurrentKey(arr3[0]?.id)
emit('changePointType', activeName.value, arr3[0]) emit('changePointType', activeName.value, arr3[0])
setTimeout(() => {
treeRef3.value?.setCurrentKey(arr3[0]?.id)
}, 100)
} }
// if(activeName.value){ // if(activeName.value){
// emit('changePointType', activeName.value) // emit('changePointType', activeName.value)
@@ -248,7 +355,7 @@ function filterProcess(nodes: any) {
// 递归处理子节点 // 递归处理子节点
const children = node.children ? filterProcess(node.children) : [] const children = node.children ? filterProcess(node.children) : []
// 对于装置层级level=2只保留 process 值匹配的节点 // 对于设备层级level=2只保留 process 值匹配的节点
if (node.level === 2) { if (node.level === 2) {
if (node.process == process.value) { if (node.process == process.value) {
return { return {
@@ -263,8 +370,7 @@ function filterProcess(nodes: any) {
// 1. 如果有满足条件的子节点则保留 // 1. 如果有满足条件的子节点则保留
// 2. 如果本身 process 值匹配则保留 // 2. 如果本身 process 值匹配则保留
// 3. 如果是叶子节点也保留(监测点通常没有子节点) // 3. 如果是叶子节点也保留(监测点通常没有子节点)
if (children.length > 0 || node.process == process.value || if (children.length > 0 || node.process == process.value || !node.children || node.children.length === 0) {
(!node.children || node.children.length === 0)) {
return { return {
...node, ...node,
children: children children: children
@@ -276,7 +382,6 @@ function filterProcess(nodes: any) {
.filter(Boolean) // 移除null节点 .filter(Boolean) // 移除null节点
} }
// function filterProcess(nodes: any) { // function filterProcess(nodes: any) {
// if (process.value == '') { // if (process.value == '') {
// return nodes // return nodes
@@ -332,26 +437,43 @@ const treeRef1 = ref<InstanceType<typeof ElTree>>()
const treeRef2 = ref<InstanceType<typeof ElTree>>() const treeRef2 = ref<InstanceType<typeof ElTree>>()
//在线 //在线
const treeRef3 = ref<InstanceType<typeof ElTree>>() const treeRef3 = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef1, treeRef2 }) // 工程
const treeRef4 = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {
setActiveName()
if (zlDeviceData.value.length != 0) {
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
activeName.value = '0'
}
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length != 0) {
activeName.value = '1'
}
if (!zlDeviceData.value && !bxsDeviceData.value) {
activeName.value = ''
}
nextTick(() => {
changeDevice(activeName.value)
})
}, 500) }, 500)
}) })
const setActiveName = () => {
if (zlDeviceData.value.length != 0) {
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
activeName.value = '0'
}
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length != 0) {
activeName.value = '1'
}
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length === 0) {
activeName.value = '2'
}
if (!zlDeviceData.value && !bxsDeviceData.value) {
activeName.value = '2'
}
nextTick(() => {
changeDevice(activeName.value)
})
}
const loading = ref(false)
const changeTreeType = (val: string) => {
loading.value = true
emit('changeTreeType', val)
if (val == '1') {
setActiveName()
}
setTimeout(() => {
loading.value = false
}, 1000)
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -382,4 +504,7 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
} }
:deep(.el-input-group__prepend) {
background-color: var(--el-fill-color-blank);
}
</style> </style>

View File

@@ -5,7 +5,7 @@
style='cursor: pointer' /> style='cursor: pointer' />
<div class='cn-tree' :style='{ opacity: menuCollapse ? 0 : 1 }'> <div class='cn-tree' :style='{ opacity: menuCollapse ? 0 : 1 }'>
<div style='display: flex; align-items: center' class='mb10'> <div style='display: flex; align-items: center' class='mb10'>
<el-input maxlength="32" show-word-limit v-model.trim='filterText' placeholder='请输入内容' clearable> <el-input maxlength="32" v-model.trim='filterText' placeholder='请输入内容' clearable>
<template #prefix> <template #prefix>
<Icon name='el-icon-Search' style='font-size: 16px' /> <Icon name='el-icon-Search' style='font-size: 16px' />
</template> </template>
@@ -218,11 +218,12 @@ const updateNodeCheckStatus = (currentCount: number) => {
const allNodes = treeRef.value.store._getAllNodes() const allNodes = treeRef.value.store._getAllNodes()
allNodes.forEach((node: any) => { allNodes.forEach((node: any) => {
if (node.level === 3) { // 监测点层级 if (node.level === 3) { // 监测点层级
// 如果已达到最大数量且该节点未被选中,则禁用勾选 // 如果已达到最大数量且该节点未被选中,则禁用勾选
if (isMaxSelected && !node.checked) { if (isMaxSelected && !node.data.checked) {
node.disabled = true node.data.disabled = true
} else { } else {
node.disabled = false node.data.disabled = false
} }
} }
}) })

View File

@@ -1,359 +1,359 @@
<template> <template>
<div class="layout-config-drawer"> <div class="layout-config-drawer">
<el-drawer :model-value="configStore.layout.showDrawer" title="布局配置" size="310px" @close="onCloseDrawer"> <el-drawer :model-value="configStore.layout.showDrawer" title="布局配置" size="310px" @close="onCloseDrawer">
<el-scrollbar class="layout-mode-style-scrollbar"> <el-scrollbar class="layout-mode-style-scrollbar">
<el-form ref="formRef" :model="configStore.layout"> <el-form ref="formRef" :model="configStore.layout">
<div class="layout-mode-styles-box"> <div class="layout-mode-styles-box">
<el-divider border-style="dashed">全局</el-divider> <el-divider border-style="dashed">全局</el-divider>
<div class="layout-config-global"> <div class="layout-config-global">
<el-form-item label="后台页面切换动画"> <el-form-item label="后台页面切换动画">
<el-select @change="onCommitState($event, 'mainAnimation')" <el-select @change="onCommitState($event, 'mainAnimation')"
:model-value="configStore.layout.mainAnimation" :model-value="configStore.layout.mainAnimation"
:placeholder="'layouts.Please select an animation name'"> :placeholder="'layouts.Please select an animation name'">
<el-option label="slide-right" value="slide-right"></el-option> <el-option label="slide-right" value="slide-right"></el-option>
<el-option label="slide-left" value="slide-left"></el-option> <el-option label="slide-left" value="slide-left"></el-option>
<el-option label="el-fade-in-linear" value="el-fade-in-linear"></el-option> <el-option label="el-fade-in-linear" value="el-fade-in-linear"></el-option>
<el-option label="el-fade-in" value="el-fade-in"></el-option> <el-option label="el-fade-in" value="el-fade-in"></el-option>
<el-option label="el-zoom-in-center" value="el-zoom-in-center"></el-option> <el-option label="el-zoom-in-center" value="el-zoom-in-center"></el-option>
<el-option label="el-zoom-in-top" value="el-zoom-in-top"></el-option> <el-option label="el-zoom-in-top" value="el-zoom-in-top"></el-option>
<el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option> <el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="组件主题色"> <el-form-item label="组件主题色">
<el-color-picker @change="onCommitColorState($event, 'elementUiPrimary')" <el-color-picker @change="onCommitColorState($event, 'elementUiPrimary')"
:model-value="configStore.getColorVal('elementUiPrimary')" /> :model-value="configStore.getColorVal('elementUiPrimary')" />
</el-form-item> </el-form-item>
<el-form-item label="表格标题栏背景颜色"> <el-form-item label="表格标题栏背景颜色">
<el-color-picker @change="onCommitColorState($event, 'tableHeaderBackground')" <el-color-picker @change="onCommitColorState($event, 'tableHeaderBackground')"
:model-value="configStore.getColorVal('tableHeaderBackground')" /> :model-value="configStore.getColorVal('tableHeaderBackground')" />
</el-form-item> </el-form-item>
<el-form-item label="表格标题栏文字颜色"> <el-form-item label="表格标题栏文字颜色">
<el-color-picker @change="onCommitColorState($event, 'tableHeaderColor')" <el-color-picker @change="onCommitColorState($event, 'tableHeaderColor')"
:model-value="configStore.getColorVal('tableHeaderColor')" /> :model-value="configStore.getColorVal('tableHeaderColor')" />
</el-form-item> </el-form-item>
<el-form-item label="表格激活栏颜色"> <el-form-item label="表格激活栏颜色">
<el-color-picker @change="onCommitColorState($event, 'tableCurrent')" <el-color-picker @change="onCommitColorState($event, 'tableCurrent')"
:model-value="configStore.getColorVal('tableCurrent')" /> :model-value="configStore.getColorVal('tableCurrent')" />
</el-form-item> </el-form-item>
</div> </div>
<el-divider border-style="dashed">侧边栏</el-divider> <el-divider border-style="dashed">侧边栏</el-divider>
<div class="layout-config-aside"> <div class="layout-config-aside">
<el-form-item label="侧边菜单栏背景色"> <el-form-item label="侧边菜单栏背景色">
<el-color-picker @change="onCommitColorState($event, 'menuBackground')" <el-color-picker @change="onCommitColorState($event, 'menuBackground')"
:model-value="configStore.getColorVal('menuBackground')" /> :model-value="configStore.getColorVal('menuBackground')" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单文字颜色"> <el-form-item label="侧边菜单文字颜色">
<el-color-picker @change="onCommitColorState($event, 'menuColor')" <el-color-picker @change="onCommitColorState($event, 'menuColor')"
:model-value="configStore.getColorVal('menuColor')" /> :model-value="configStore.getColorVal('menuColor')" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单激活项背景色"> <el-form-item label="侧边菜单激活项背景色">
<el-color-picker @change="onCommitColorState($event, 'menuActiveBackground')" <el-color-picker @change="onCommitColorState($event, 'menuActiveBackground')"
:model-value="configStore.getColorVal('menuActiveBackground')" /> :model-value="configStore.getColorVal('menuActiveBackground')" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单激活项文字色"> <el-form-item label="侧边菜单激活项文字色">
<el-color-picker @change="onCommitColorState($event, 'menuActiveColor')" <el-color-picker @change="onCommitColorState($event, 'menuActiveColor')"
:model-value="configStore.getColorVal('menuActiveColor')" /> :model-value="configStore.getColorVal('menuActiveColor')" />
</el-form-item> </el-form-item>
<el-form-item label="显示侧边菜单顶栏(LOGO栏)"> <el-form-item label="显示侧边菜单顶栏(LOGO栏)">
<el-switch @change="onCommitState($event, 'menuShowTopBar')" <el-switch @change="onCommitState($event, 'menuShowTopBar')"
:model-value="configStore.layout.menuShowTopBar"></el-switch> :model-value="configStore.layout.menuShowTopBar"></el-switch>
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单顶栏背景色"> <el-form-item label="侧边菜单顶栏背景色">
<el-color-picker @change="onCommitColorState($event, 'menuTopBarBackground')" <el-color-picker @change="onCommitColorState($event, 'menuTopBarBackground')"
:model-value="configStore.getColorVal('menuTopBarBackground')" /> :model-value="configStore.getColorVal('menuTopBarBackground')" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单宽度(展开时)"> <el-form-item label="侧边菜单宽度(展开时)">
<el-input maxlength="32" show-word-limit @input="onCommitState($event, 'menuWidth')" <el-input maxlength="32" show-word-limit @input="onCommitState($event, 'menuWidth')"
type="number" :step="10" :model-value="configStore.layout.menuWidth"> type="number" :step="10" :model-value="configStore.layout.menuWidth">
<template #append>px</template> <template #append>px</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<!-- <el-form-item label="侧边菜单默认图标">--> <!-- <el-form-item label="侧边菜单默认图标">-->
<!-- <IconSelector--> <!-- <IconSelector-->
<!-- @change="onCommitMenuDefaultIcon($event, 'menuDefaultIcon')"--> <!-- @change="onCommitMenuDefaultIcon($event, 'menuDefaultIcon')"-->
<!-- :model-value="configStore.layout.menuDefaultIcon"--> <!-- :model-value="configStore.layout.menuDefaultIcon"-->
<!-- />--> <!-- />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="侧边菜单水平折叠">--> <!-- <el-form-item label="侧边菜单水平折叠">-->
<!-- <el-switch--> <!-- <el-switch-->
<!-- @change="onCommitState($event, 'menuCollapse')"--> <!-- @change="onCommitState($event, 'menuCollapse')"-->
<!-- :model-value="configStore.layout.menuCollapse"--> <!-- :model-value="configStore.layout.menuCollapse"-->
<!-- ></el-switch>--> <!-- ></el-switch>-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="侧边菜单手风琴">--> <!-- <el-form-item label="侧边菜单手风琴">-->
<!-- <el-switch--> <!-- <el-switch-->
<!-- @change="onCommitState($event, 'menuUniqueOpened')"--> <!-- @change="onCommitState($event, 'menuUniqueOpened')"-->
<!-- :model-value="configStore.layout.menuUniqueOpened"--> <!-- :model-value="configStore.layout.menuUniqueOpened"-->
<!-- ></el-switch>--> <!-- ></el-switch>-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
</div> </div>
<el-divider border-style="dashed">顶栏</el-divider> <el-divider border-style="dashed">顶栏</el-divider>
<div class="layout-config-aside"> <div class="layout-config-aside">
<el-form-item label="顶栏背景色"> <el-form-item label="顶栏背景色">
<el-color-picker @change="onCommitColorState($event, 'headerBarBackground')" <el-color-picker @change="onCommitColorState($event, 'headerBarBackground')"
:model-value="configStore.getColorVal('headerBarBackground')" /> :model-value="configStore.getColorVal('headerBarBackground')" />
</el-form-item> </el-form-item>
<el-form-item label="顶栏文字色"> <el-form-item label="顶栏文字色">
<el-color-picker @change="onCommitColorState($event, 'headerBarTabColor')" <el-color-picker @change="onCommitColorState($event, 'headerBarTabColor')"
:model-value="configStore.getColorVal('headerBarTabColor')" /> :model-value="configStore.getColorVal('headerBarTabColor')" />
</el-form-item> </el-form-item>
<!-- <el-form-item label="顶栏悬停时背景色">--> <!-- <el-form-item label="顶栏悬停时背景色">-->
<!-- <el-color-picker--> <!-- <el-color-picker-->
<!-- @change="onCommitColorState($event, 'headerBarHoverBackground')"--> <!-- @change="onCommitColorState($event, 'headerBarHoverBackground')"-->
<!-- :model-value="configStore.getColorVal('headerBarHoverBackground')"--> <!-- :model-value="configStore.getColorVal('headerBarHoverBackground')"-->
<!-- />--> <!-- />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="顶栏菜单激活项背景色">--> <!-- <el-form-item label="顶栏菜单激活项背景色">-->
<!-- <el-color-picker--> <!-- <el-color-picker-->
<!-- @change="onCommitColorState($event, 'headerBarTabActiveBackground')"--> <!-- @change="onCommitColorState($event, 'headerBarTabActiveBackground')"-->
<!-- :model-value="configStore.getColorVal('headerBarTabActiveBackground')"--> <!-- :model-value="configStore.getColorVal('headerBarTabActiveBackground')"-->
<!-- />--> <!-- />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="顶栏菜单激活项文字色">--> <!-- <el-form-item label="顶栏菜单激活项文字色">-->
<!-- <el-color-picker--> <!-- <el-color-picker-->
<!-- @change="onCommitColorState($event, 'headerBarTabActiveColor')"--> <!-- @change="onCommitColorState($event, 'headerBarTabActiveColor')"-->
<!-- :model-value="configStore.getColorVal('headerBarTabActiveColor')"--> <!-- :model-value="configStore.getColorVal('headerBarTabActiveColor')"-->
<!-- />--> <!-- />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
</div> </div>
<el-popconfirm @confirm="restoreDefault" title="确定要恢复全部配置到默认值吗?"> <el-popconfirm @confirm="restoreDefault" title="确定要恢复全部配置到默认值吗?">
<template #reference> <template #reference>
<div class="ba-center"> <div class="ba-center">
<el-button class="w80" type="info">恢复默认</el-button> <el-button class="w80" type="info">恢复默认</el-button>
</div> </div>
</template> </template>
</el-popconfirm> </el-popconfirm>
</div> </div>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
</el-drawer> </el-drawer>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { useNavTabs } from '@/stores/navTabs' import { useNavTabs } from '@/stores/navTabs'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import IconSelector from '@/components/baInput/components/iconSelector.vue' import IconSelector from '@/components/baInput/components/iconSelector.vue'
import { STORE_CONFIG } from '@/stores/constant/cacheKey' import { STORE_CONFIG } from '@/stores/constant/cacheKey'
import { Local, Session } from '@/utils/storage' import { Local, Session } from '@/utils/storage'
import type { Layout } from '@/stores/interface' import type { Layout } from '@/stores/interface'
const configStore = useConfig() const configStore = useConfig()
const navTabs = useNavTabs() const navTabs = useNavTabs()
const router = useRouter() const router = useRouter()
const onCommitState = (value: any, name: any) => { const onCommitState = (value: any, name: any) => {
configStore.setLayout(name, value) configStore.setLayout(name, value)
} }
const onCommitColorState = (value: string | null, name: keyof Layout) => { const onCommitColorState = (value: string | null, name: keyof Layout) => {
if (value === null) return if (value === null) return
const colors = configStore.layout[name] as string[] const colors = configStore.layout[name] as string[]
if (configStore.layout.isDark) { if (configStore.layout.isDark) {
colors[1] = value colors[1] = value
} else { } else {
colors[0] = value colors[0] = value
} }
configStore.setLayout(name, colors) configStore.setLayout(name, colors)
} }
const setLayoutMode = (mode: string) => { const setLayoutMode = (mode: string) => {
configStore.setLayoutMode(mode) configStore.setLayoutMode(mode)
} }
// 修改默认菜单图标 // 修改默认菜单图标
const onCommitMenuDefaultIcon = (value: any, name: any) => { const onCommitMenuDefaultIcon = (value: any, name: any) => {
configStore.setLayout(name, value) configStore.setLayout(name, value)
const menus = navTabs.state.tabsViewRoutes const menus = navTabs.state.tabsViewRoutes
navTabs.setTabsViewRoutes([]) navTabs.setTabsViewRoutes([])
setTimeout(() => { setTimeout(() => {
navTabs.setTabsViewRoutes(menus) navTabs.setTabsViewRoutes(menus)
}, 200) }, 200)
} }
const onCloseDrawer = () => { const onCloseDrawer = () => {
configStore.setLayout('showDrawer', false) configStore.setLayout('showDrawer', false)
} }
const restoreDefault = () => { const restoreDefault = () => {
Local.remove(STORE_CONFIG) Local.remove(STORE_CONFIG)
router.go(0) router.go(0)
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.layout-config-drawer :deep(.el-input__inner) { .layout-config-drawer :deep(.el-input__inner) {
padding: 0 0 0 6px; padding: 0 0 0 6px;
} }
.layout-config-drawer :deep(.el-input-group__append) { .layout-config-drawer :deep(.el-input-group__append) {
padding: 0 10px; padding: 0 10px;
} }
.layout-config-drawer :deep(.el-drawer__header) { .layout-config-drawer :deep(.el-drawer__header) {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.layout-config-drawer :deep(.el-drawer__body) { .layout-config-drawer :deep(.el-drawer__body) {
padding: 0; padding: 0;
} }
.layout-mode-styles-box { .layout-mode-styles-box {
padding: 20px; padding: 20px;
} }
.layout-mode-box-style-row { .layout-mode-box-style-row {
margin-bottom: 15px; margin-bottom: 15px;
} }
.layout-mode-style { .layout-mode-style {
position: relative; position: relative;
height: 100px; height: 100px;
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
border-radius: var(--el-border-radius-small); border-radius: var(--el-border-radius-small);
&:hover, &:hover,
&.active { &.active {
border: 1px solid var(--el-color-primary); border: 1px solid var(--el-color-primary);
} }
.layout-mode-style-name { .layout-mode-style-name {
position: absolute; position: absolute;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: var(--el-color-primary-light-5); color: var(--el-color-primary-light-5);
border-radius: 50%; border-radius: 50%;
height: 50px; height: 50px;
width: 50px; width: 50px;
border: 1px solid var(--el-color-primary-light-3); border: 1px solid var(--el-color-primary-light-3);
} }
.layout-mode-style-box { .layout-mode-style-box {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
&.default { &.default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.layout-mode-style-aside { .layout-mode-style-aside {
width: 18%; width: 18%;
height: 90%; height: 90%;
background-color: var(--el-border-color-lighter); background-color: var(--el-border-color-lighter);
} }
.layout-mode-style-container-box { .layout-mode-style-container-box {
width: 68%; width: 68%;
height: 90%; height: 90%;
margin-left: 4%; margin-left: 4%;
.layout-mode-style-header { .layout-mode-style-header {
width: 100%; width: 100%;
height: 10%; height: 10%;
background-color: var(--el-border-color-lighter); background-color: var(--el-border-color-lighter);
} }
.layout-mode-style-container { .layout-mode-style-container {
width: 100%; width: 100%;
height: 85%; height: 85%;
background-color: var(--el-border-color-extra-light); background-color: var(--el-border-color-extra-light);
margin-top: 5%; margin-top: 5%;
} }
} }
} }
&.classic { &.classic {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.layout-mode-style-aside { .layout-mode-style-aside {
width: 18%; width: 18%;
height: 100%; height: 100%;
background-color: var(--el-border-color-lighter); background-color: var(--el-border-color-lighter);
} }
.layout-mode-style-container-box { .layout-mode-style-container-box {
width: 82%; width: 82%;
height: 100%; height: 100%;
.layout-mode-style-header { .layout-mode-style-header {
width: 100%; width: 100%;
height: 10%; height: 10%;
background-color: var(--el-border-color); background-color: var(--el-border-color);
} }
.layout-mode-style-container { .layout-mode-style-container {
width: 100%; width: 100%;
height: 90%; height: 90%;
background-color: var(--el-border-color-extra-light); background-color: var(--el-border-color-extra-light);
} }
} }
} }
&.streamline { &.streamline {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.layout-mode-style-container-box { .layout-mode-style-container-box {
width: 100%; width: 100%;
height: 100%; height: 100%;
.layout-mode-style-header { .layout-mode-style-header {
width: 100%; width: 100%;
height: 10%; height: 10%;
background-color: var(--el-border-color); background-color: var(--el-border-color);
} }
.layout-mode-style-container { .layout-mode-style-container {
width: 100%; width: 100%;
height: 90%; height: 90%;
background-color: var(--el-border-color-extra-light); background-color: var(--el-border-color-extra-light);
} }
} }
} }
&.double { &.double {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.layout-mode-style-aside { .layout-mode-style-aside {
width: 18%; width: 18%;
height: 100%; height: 100%;
background-color: var(--el-border-color); background-color: var(--el-border-color);
} }
.layout-mode-style-container-box { .layout-mode-style-container-box {
width: 82%; width: 82%;
height: 100%; height: 100%;
.layout-mode-style-header { .layout-mode-style-header {
width: 100%; width: 100%;
height: 10%; height: 10%;
background-color: var(--el-border-color); background-color: var(--el-border-color);
} }
.layout-mode-style-container { .layout-mode-style-container {
width: 100%; width: 100%;
height: 90%; height: 90%;
background-color: var(--el-border-color-extra-light); background-color: var(--el-border-color-extra-light);
} }
} }
} }
} }
.w80 { .w80 {
width: 90%; width: 90%;
} }
</style> </style>

View File

@@ -1,81 +1,81 @@
<template> <template>
<el-scrollbar ref="verticalMenusRef" class="vertical-menus-scrollbar"> <el-scrollbar ref="verticalMenusRef" class="vertical-menus-scrollbar">
<el-menu <el-menu
class="layouts-menu-vertical" class="layouts-menu-vertical"
:collapse-transition="false" :collapse-transition="false"
:unique-opened="config.layout.menuUniqueOpened" :unique-opened="config.layout.menuUniqueOpened"
:default-active="state.defaultActive" :default-active="state.defaultActive"
:collapse="config.layout.menuCollapse" :collapse="config.layout.menuCollapse"
> >
<MenuTree :menus="navTabs.state.tabsViewRoutes" /> <MenuTree :menus="navTabs.state.tabsViewRoutes" />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, onMounted, reactive, ref } from 'vue' import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import MenuTree from '@/layouts/admin/components/menus/menuTree.vue' import MenuTree from '@/layouts/admin/components/menus/menuTree.vue'
import { useRoute, onBeforeRouteUpdate, type RouteLocationNormalizedLoaded } from 'vue-router' import { useRoute, onBeforeRouteUpdate, type RouteLocationNormalizedLoaded } from 'vue-router'
import type { ScrollbarInstance } from 'element-plus' import type { ScrollbarInstance } from 'element-plus'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { useNavTabs } from '@/stores/navTabs' import { useNavTabs } from '@/stores/navTabs'
const config = useConfig() const config = useConfig()
const navTabs = useNavTabs() const navTabs = useNavTabs()
const route = useRoute() const route = useRoute()
const verticalMenusRef = ref<ScrollbarInstance>() const verticalMenusRef = ref<ScrollbarInstance>()
const state = reactive({ const state = reactive({
defaultActive: '', defaultActive: '',
}) })
const verticalMenusScrollbarHeight = computed(() => { const verticalMenusScrollbarHeight = computed(() => {
let menuTopBarHeight = 0 let menuTopBarHeight = 0
if (config.layout.menuShowTopBar) { if (config.layout.menuShowTopBar) {
menuTopBarHeight = 50 menuTopBarHeight = 50
} }
if (config.layout.layoutMode == 'Default') { if (config.layout.layoutMode == 'Default') {
return 'calc(100vh - ' + (32 + menuTopBarHeight) + 'px)' return 'calc(100vh - ' + (32 + menuTopBarHeight) + 'px)'
} else { } else {
return 'calc(100vh - ' + menuTopBarHeight + 'px)' return 'calc(100vh - ' + menuTopBarHeight + 'px)'
} }
}) })
// 激活当前路由的菜单 // 激活当前路由的菜单
const currentRouteActive = (currentRoute: RouteLocationNormalizedLoaded) => { const currentRouteActive = (currentRoute: RouteLocationNormalizedLoaded) => {
state.defaultActive = currentRoute.path state.defaultActive = currentRoute.path
} }
// 滚动条滚动到激活菜单所在位置 // 滚动条滚动到激活菜单所在位置
const verticalMenusScroll = () => { const verticalMenusScroll = () => {
nextTick(() => { nextTick(() => {
let activeMenu: HTMLElement | null = document.querySelector('.el-menu.layouts-menu-vertical li.is-active') let activeMenu: HTMLElement | null = document.querySelector('.el-menu.layouts-menu-vertical li.is-active')
if (!activeMenu) return false if (!activeMenu) return false
verticalMenusRef.value?.setScrollTop(activeMenu.offsetTop) verticalMenusRef.value?.setScrollTop(activeMenu.offsetTop)
}) })
} }
onMounted(() => { onMounted(() => {
currentRouteActive(route) currentRouteActive(route)
verticalMenusScroll() verticalMenusScroll()
}) })
onBeforeRouteUpdate((to) => { onBeforeRouteUpdate((to) => {
currentRouteActive(to) currentRouteActive(to)
}) })
</script> </script>
<style> <style>
.vertical-menus-scrollbar { .vertical-menus-scrollbar {
height: v-bind(verticalMenusScrollbarHeight); height: v-bind(verticalMenusScrollbarHeight);
background-color: v-bind('config.getColorVal("menuBackground")'); background-color: v-bind('config.getColorVal("menuBackground")');
} }
.layouts-menu-vertical { .layouts-menu-vertical {
border: 0; border: 0;
padding-bottom: 30px; padding-bottom: 30px;
--el-menu-bg-color: v-bind('config.getColorVal("menuBackground")'); --el-menu-bg-color: v-bind('config.getColorVal("menuBackground")');
--el-menu-text-color: v-bind('config.getColorVal("menuColor")'); --el-menu-text-color: v-bind('config.getColorVal("menuColor")');
--el-menu-active-color: v-bind('config.getColorVal("menuActiveColor")'); --el-menu-active-color: v-bind('config.getColorVal("menuActiveColor")');
--el-menu-hover-color: v-bind('config.getColorVal("menuActiveBackground")'); --el-menu-hover-color: v-bind('config.getColorVal("menuActiveBackground")');
} }
</style> </style>

View File

@@ -1,242 +1,246 @@
<template> <template>
<div class="nav-tabs" ref="tabScrollbarRef"> <div class="nav-tabs" ref="tabScrollbarRef">
<div
v-for="(item, idx) in navTabs.state.tabsView" <div
@click="onTab(item)" v-for="(item, idx) in navTabs.state.tabsView"
@contextmenu.prevent="onContextmenu(item, $event)" @click="onTab(item)"
class="ba-nav-tab" @contextmenu.prevent="onContextmenu(item, $event)"
:class="navTabs.state.activeIndex == idx ? 'active' : ''" class="ba-nav-tab"
:ref="tabsRefs.set" :class="navTabs.state.activeIndex == idx ? 'active' : ''"
:key="idx" :ref="tabsRefs.set"
> :key="idx"
{{ item.meta.title }} >
<transition @after-leave="selectNavTab(tabsRefs[navTabs.state.activeIndex])" name="el-fade-in"> {{ item.meta.title }}
<Icon <transition @after-leave="selectNavTab(tabsRefs[navTabs.state.activeIndex])" name="el-fade-in">
v-if="navTabs.state.tabsView.length > 1" <Icon
class="close-icon" v-if="navTabs.state.tabsView.length > 1"
@click.stop="closeTab(item)" class="close-icon"
size="15" @click.stop="closeTab(item)"
name="el-icon-Close" size="15"
/> name="el-icon-Close"
</transition> />
</div> </transition>
<!-- <div :style='activeBoxStyle' class='nav-tabs-active-box'></div>--> </div>
</div> <!-- <div :style='activeBoxStyle' class='nav-tabs-active-box'></div>-->
<Contextmenu ref="contextmenuRef" :items="state.contextmenuItems" @contextmenuItemClick="onContextmenuItem" /> </div>
</template> <Contextmenu ref="contextmenuRef" :items="state.contextmenuItems" @contextmenuItemClick="onContextmenuItem" />
</template>
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref } from 'vue' <script setup lang="ts">
import { useRoute, useRouter, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router' import { nextTick, onMounted, reactive, ref } from 'vue'
import { useConfig } from '@/stores/config' import { useRoute, useRouter, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'
import { useNavTabs } from '@/stores/navTabs' import { useConfig } from '@/stores/config'
import { useTemplateRefsList } from '@vueuse/core' import { useNavTabs } from '@/stores/navTabs'
import type { ContextMenuItem, ContextmenuItemClickEmitArg } from '@/components/contextmenu/interface' import { useTemplateRefsList } from '@vueuse/core'
import useCurrentInstance from '@/utils/useCurrentInstance' import type { ContextMenuItem, ContextmenuItemClickEmitArg } from '@/components/contextmenu/interface'
import Contextmenu from '@/components/contextmenu/index.vue' import useCurrentInstance from '@/utils/useCurrentInstance'
import horizontalScroll from '@/utils/horizontalScroll' import Contextmenu from '@/components/contextmenu/index.vue'
import { getFirstRoute, routePush } from '@/utils/router' import horizontalScroll from '@/utils/horizontalScroll'
import { adminBaseRoutePath } from '@/router/static' import { getFirstRoute, routePush } from '@/utils/router'
import { adminBaseRoutePath } from '@/router/static'
const route = useRoute()
const router = useRouter() const route = useRoute()
const config = useConfig() const router = useRouter()
const navTabs = useNavTabs() const config = useConfig()
const navTabs = useNavTabs()
const { proxy } = useCurrentInstance()
const tabScrollbarRef = ref() const { proxy } = useCurrentInstance()
const tabsRefs = useTemplateRefsList<HTMLDivElement>() const tabScrollbarRef = ref()
const tabsRefs = useTemplateRefsList<HTMLDivElement>()
const contextmenuRef = ref()
const contextmenuRef = ref()
const state: {
contextmenuItems: ContextMenuItem[] const state: {
} = reactive({ contextmenuItems: ContextMenuItem[]
contextmenuItems: [ } = reactive({
{ name: 'refresh', label: '重新加载', icon: 'fa fa-refresh' }, contextmenuItems: [
{ name: 'close', label: '关闭标签', icon: 'fa fa-times' }, { name: 'refresh', label: '重新加载', icon: 'fa fa-refresh' },
{ name: 'fullScreen', label: '当前标签全屏', icon: 'el-icon-FullScreen' }, { name: 'close', label: '关闭标签', icon: 'fa fa-times' },
{ name: 'closeOther', label: '关闭其他标签', icon: 'fa fa-minus' }, { name: 'fullScreen', label: '当前标签全屏', icon: 'el-icon-FullScreen' },
{ name: 'closeAll', label: '关闭全部标签', icon: 'fa fa-stop' } { name: 'closeOther', label: '关闭其他标签', icon: 'fa fa-minus' },
] { name: 'closeAll', label: '关闭全部标签', icon: 'fa fa-stop' }
}) ]
})
const activeBoxStyle = reactive({
width: '0', const activeBoxStyle = reactive({
transform: 'translateX(0px)' width: '0',
}) transform: 'translateX(0px)'
})
const onTab = (menu: RouteLocationNormalized) => {
router.push(menu) const onTab = (menu: RouteLocationNormalized) => {
} router.push(menu)
}
const onContextmenu = (menu: RouteLocationNormalized, el: MouseEvent) => {
// 禁用刷新 const onContextmenu = (menu: RouteLocationNormalized, el: MouseEvent) => {
state.contextmenuItems[0].disabled = route.path !== menu.path
// 禁用关闭其他和关闭全部 // 禁用刷新
state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled = state.contextmenuItems[0].disabled = route.path !== menu.path
navTabs.state.tabsView.length == 1 ? true : false
const { clientX, clientY } = el // 禁用关闭其他和关闭全部
contextmenuRef.value.onShowContextmenu(menu, { state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled =
x: clientX, navTabs.state.tabsView.length == 1 ? true : false
y: clientY
}) const { clientX, clientY } = el
} contextmenuRef.value.onShowContextmenu(menu, {
x: clientX,
// tab 激活状态切换 y: clientY
const selectNavTab = function (dom: HTMLDivElement) { })
if (!dom) { }
return false
} // tab 激活状态切换
activeBoxStyle.width = dom.clientWidth + 'px' const selectNavTab = function (dom: HTMLDivElement) {
activeBoxStyle.transform = `translateX(${dom.offsetLeft}px)` if (!dom) {
return false
let scrollLeft = dom.offsetLeft + dom.clientWidth - tabScrollbarRef.value.clientWidth }
if (dom.offsetLeft < tabScrollbarRef.value.scrollLeft) { activeBoxStyle.width = dom.clientWidth + 'px'
tabScrollbarRef.value.scrollTo(dom.offsetLeft, 0) activeBoxStyle.transform = `translateX(${dom.offsetLeft}px)`
} else if (scrollLeft > tabScrollbarRef.value.scrollLeft) {
tabScrollbarRef.value.scrollTo(scrollLeft, 0) let scrollLeft = dom.offsetLeft + dom.clientWidth - tabScrollbarRef.value.clientWidth
} if (dom.offsetLeft < tabScrollbarRef.value.scrollLeft) {
} tabScrollbarRef.value.scrollTo(dom.offsetLeft, 0)
} else if (scrollLeft > tabScrollbarRef.value.scrollLeft) {
const toLastTab = () => { tabScrollbarRef.value.scrollTo(scrollLeft, 0)
const lastTab = navTabs.state.tabsView.slice(-1)[0] }
if (lastTab) { }
router.push(lastTab)
} else { const toLastTab = () => {
router.push(adminBaseRoutePath) const lastTab = navTabs.state.tabsView.slice(-1)[0]
} if (lastTab) {
} router.push(lastTab)
} else {
const closeTab = (route: RouteLocationNormalized) => { router.push(adminBaseRoutePath)
navTabs.closeTab(route) }
proxy.eventBus.emit('onTabViewClose', route) }
if (navTabs.state.activeRoute?.path === route.path) {
toLastTab() const closeTab = (route: RouteLocationNormalized) => {
} else { navTabs.closeTab(route)
navTabs.setActiveRoute(navTabs.state.activeRoute!) proxy.eventBus.emit('onTabViewClose', route)
nextTick(() => { if (navTabs.state.activeRoute?.path === route.path) {
selectNavTab(tabsRefs.value[navTabs.state.activeIndex]) toLastTab()
}) } else {
} navTabs.setActiveRoute(navTabs.state.activeRoute!)
nextTick(() => {
contextmenuRef.value.onHideContextmenu() selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
} })
}
const closeOtherTab = (menu: RouteLocationNormalized) => {
navTabs.closeTabs(menu) contextmenuRef.value.onHideContextmenu()
navTabs.setActiveRoute(menu) }
if (navTabs.state.activeRoute?.path !== route.path) {
router.push(menu!.path) const closeOtherTab = (menu: RouteLocationNormalized) => {
} navTabs.closeTabs(menu)
} navTabs.setActiveRoute(menu)
if (navTabs.state.activeRoute?.path !== route.path) {
const closeAllTab = (menu: RouteLocationNormalized) => { router.push(menu!.path)
let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes) }
if (firstRoute && firstRoute.path == menu.path) { }
return closeOtherTab(menu)
} const closeAllTab = (menu: RouteLocationNormalized) => {
if (firstRoute && firstRoute.path == navTabs.state.activeRoute?.path) { let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
return closeOtherTab(navTabs.state.activeRoute) if (firstRoute && firstRoute.path == menu.path) {
} return closeOtherTab(menu)
navTabs.closeTabs(false) }
if (firstRoute) routePush(firstRoute.path) if (firstRoute && firstRoute.path == navTabs.state.activeRoute?.path) {
} return closeOtherTab(navTabs.state.activeRoute)
}
const onContextmenuItem = async (item: ContextmenuItemClickEmitArg) => { navTabs.closeTabs(false)
const { name, menu } = item if (firstRoute) routePush(firstRoute.path)
if (!menu) return }
switch (name) {
case 'refresh': const onContextmenuItem = async (item: ContextmenuItemClickEmitArg) => {
proxy.eventBus.emit('onTabViewRefresh', menu) const { name, menu } = item
break if (!menu) return
case 'close': switch (name) {
closeTab(menu) case 'refresh':
break proxy.eventBus.emit('onTabViewRefresh', menu)
case 'closeOther': break
closeOtherTab(menu) case 'close':
break closeTab(menu)
case 'closeAll': break
closeAllTab(menu) case 'closeOther':
break closeOtherTab(menu)
case 'fullScreen': break
if (route.path !== menu?.path) { case 'closeAll':
router.push(menu?.path as string) closeAllTab(menu)
} break
navTabs.setFullScreen(true) case 'fullScreen':
break if (route.path !== menu?.path) {
} router.push(menu?.path as string)
} }
navTabs.setFullScreen(true)
const updateTab = function (newRoute: RouteLocationNormalized) { break
// 添加tab }
navTabs.addTab(newRoute) }
// 激活当前tab
navTabs.setActiveRoute(newRoute) const updateTab = function (newRoute: RouteLocationNormalized) {
// 添加tab
nextTick(() => { navTabs.addTab(newRoute)
selectNavTab(tabsRefs.value[navTabs.state.activeIndex]) // 激活当前tab
}) navTabs.setActiveRoute(newRoute)
}
nextTick(() => {
onBeforeRouteUpdate(async to => { selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
})
}
updateTab(to)
onBeforeRouteUpdate(async to => {
})
onMounted(() => { updateTab(to)
updateTab(router.currentRoute.value)
new horizontalScroll(tabScrollbarRef.value) })
})
</script> onMounted(() => {
updateTab(router.currentRoute.value)
<style scoped lang="scss"> new horizontalScroll(tabScrollbarRef.value)
.dark { })
.close-icon { </script>
color: v-bind('config.getColorVal("headerBarTabColor")') !important;
} <style scoped lang="scss">
.dark {
.ba-nav-tab.active { .close-icon {
.close-icon { color: v-bind('config.getColorVal("headerBarTabColor")') !important;
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important; }
}
} .ba-nav-tab.active {
} .close-icon {
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;
.nav-tabs { }
overflow-x: auto; }
overflow-y: hidden; }
margin-right: var(--ba-main-space);
scrollbar-width: none; .nav-tabs {
overflow-x: auto;
&::-webkit-scrollbar { overflow-y: hidden;
height: 5px; margin-right: var(--ba-main-space);
} // scrollbar-width: none;
// overflow-x: auto;
// &::-webkit-scrollbar {
//&::-webkit-scrollbar-thumb { height: 5px;
// background: #eaeaea; }
// border-radius: var(--el-border-radius-base);
// box-shadow: none; //
// -webkit-box-shadow: none; //&::-webkit-scrollbar-thumb {
//} // background: #eaeaea;
// // border-radius: var(--el-border-radius-base);
//&::-webkit-scrollbar-track { // box-shadow: none;
// background: v-bind('config.layout.layoutMode == "Default" ? "none":config.getColorVal("headerBarBackground")'); // -webkit-box-shadow: none;
//} //}
// //
//&:hover { //&::-webkit-scrollbar-track {
// &::-webkit-scrollbar-thumb:hover { // background: v-bind('config.layout.layoutMode == "Default" ? "none":config.getColorVal("headerBarBackground")');
// background: #c8c9cc; //}
// } //
//} //&:hover {
} // &::-webkit-scrollbar-thumb:hover {
// background: #c8c9cc;
.ba-nav-tab { // }
white-space: nowrap; //}
height: 40px; }
}
</style> .ba-nav-tab {
white-space: nowrap;
height: 40px;
}
</style>

View File

@@ -1,238 +1,241 @@
<template> <template>
<div class="nav-menus" :class="configStore.layout.layoutMode"> <div class="nav-menus" :class="configStore.layout.layoutMode">
<div @click="savePng" class="nav-menu-item"> <!-- <div @click="savePng" class="nav-menu-item">
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon" class="nav-menu-icon"
name="el-icon-Camera" name="el-icon-Camera"
size="18" size="18"
/> />
</div> </div>
<div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''"> <div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''">
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon" class="nav-menu-icon"
v-if="state.isFullScreen" v-if="state.isFullScreen"
name="fa-solid fa-compress" name="fa-solid fa-compress"
size="18" size="18"
/> />
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon" class="nav-menu-icon"
v-else v-else
name="fa-solid fa-expand" name="fa-solid fa-expand"
size="18" size="18"
/> />
</div> </div> -->
<el-dropdown style="height: 100%" @command="handleCommand"> <el-dropdown style="height: 100%" @command="handleCommand">
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''"> <div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
<el-avatar :size="25" fit="fill"> <el-avatar :size="25" fit="fill">
<img src="@/assets/avatar.png" alt="" /> <img src="@/assets/avatar.png" alt="" />
</el-avatar> </el-avatar>
<div class="admin-name">{{ adminInfo.nickname }}</div> <div class="admin-name">{{ adminInfo.nickname }}</div>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="adminInfo">个人资料</el-dropdown-item> <el-dropdown-item command="adminInfo">个人资料</el-dropdown-item>
<el-dropdown-item command="changePwd">修改密码</el-dropdown-item> <el-dropdown-item command="changePwd">修改密码</el-dropdown-item>
<el-dropdown-item command="layout">退出登录</el-dropdown-item> <el-dropdown-item command="layout">退出登录</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<!-- <div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item"> <!-- <div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item">
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon" class="nav-menu-icon"
name="fa fa-cogs" name="fa fa-cogs"
size="18" size="18"
/> />
</div> -->" </div> -->
"
<Config />
<PopupPwd ref="popupPwd" /> <Config />
<AdminInfo ref="popupAdminInfo" /> <PopupPwd ref="popupPwd" />
<!-- <TerminalVue /> --> <AdminInfo ref="popupAdminInfo" />
</div> <!-- <TerminalVue /> -->
</template> </div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue' <script lang="ts" setup>
import screenfull from 'screenfull' import { reactive, ref } from 'vue'
import { useConfig } from '@/stores/config' import screenfull from 'screenfull'
import { ElMessage } from 'element-plus' import { useConfig } from '@/stores/config'
import Config from './config.vue' import { ElMessage } from 'element-plus'
import { useAdminInfo } from '@/stores/adminInfo' import Config from './config.vue'
import router from '@/router' import { useAdminInfo } from '@/stores/adminInfo'
import { routePush } from '@/utils/router' import router from '@/router'
import { fullUrl } from '@/utils/common' import { routePush } from '@/utils/router'
import html2canvas from 'html2canvas' import { fullUrl } from '@/utils/common'
import PopupPwd from './popup/password.vue' import html2canvas from 'html2canvas'
import AdminInfo from './popup/adminInfo.vue' import PopupPwd from './popup/password.vue'
import { useNavTabs } from '@/stores/navTabs' import AdminInfo from './popup/adminInfo.vue'
import { useNavTabs } from '@/stores/navTabs'
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const navTabs = useNavTabs() const navTabs = useNavTabs()
const configStore = useConfig() const configStore = useConfig()
const popupPwd = ref() const popupPwd = ref()
const popupAdminInfo = ref() const popupAdminInfo = ref()
const state = reactive({ const state = reactive({
isFullScreen: false, isFullScreen: false,
currentNavMenu: '', currentNavMenu: '',
showLayoutDrawer: false, showLayoutDrawer: false,
showAdminInfoPopover: false showAdminInfoPopover: false
}) })
const savePng = () => { const savePng = () => {
html2canvas(document.body, { html2canvas(document.body, {
scale: 1, scale: 1,
useCORS: true useCORS: true
}).then(function (canvas) { }).then(function (canvas) {
var link = document.createElement('a') var link = document.createElement('a')
link.href = canvas.toDataURL('image/png') link.href = canvas.toDataURL('image/png')
link.download = 'screenshot.png' link.download = 'screenshot.png'
link.click() link.click()
}) })
} }
const onFullScreen = () => { const onFullScreen = () => {
if (!screenfull.isEnabled) { if (!screenfull.isEnabled) {
ElMessage.warning('layouts.Full screen is not supported') ElMessage.warning('layouts.Full screen is not supported')
return false return false
} }
screenfull.toggle() screenfull.toggle()
screenfull.onchange(() => { screenfull.onchange(() => {
state.isFullScreen = screenfull.isFullscreen state.isFullScreen = screenfull.isFullscreen
}) })
} }
const handleCommand = (key: string) => { const handleCommand = async (key: string) => {
// console.log(key) // console.log(key)
switch (key) { switch (key) {
case 'adminInfo': case 'adminInfo':
popupAdminInfo.value.open() popupAdminInfo.value.open()
break break
case 'changePwd': case 'changePwd':
popupPwd.value.open() popupPwd.value.open()
break break
case 'layout': case 'layout':
navTabs.closeTabs() await window.location.reload()
window.localStorage.clear() setTimeout(() => {
adminInfo.reset() navTabs.closeTabs()
router.push({ name: 'login' }) window.localStorage.clear()
break adminInfo.reset()
default: router.push({ name: 'login' })
break }, 0)
} break
} default:
</script> break
}
<style scoped lang="scss"> }
.nav-menus.Default { </script>
border-radius: var(--el-border-radius-base);
box-shadow: var(--el-box-shadow-light); <style scoped lang="scss">
} .nav-menus.Default {
border-radius: var(--el-border-radius-base);
.nav-menus { box-shadow: var(--el-box-shadow-light);
display: flex; }
align-items: center;
height: 100%; .nav-menus {
margin-left: auto; display: flex;
background-color: v-bind('configStore.getColorVal("headerBarBackground")'); align-items: center;
height: 100%;
.nav-menu-item { margin-left: auto;
height: 100%; background-color: v-bind('configStore.getColorVal("headerBarBackground")');
width: 40px;
display: flex; .nav-menu-item {
align-items: center; height: 100%;
justify-content: center; width: 40px;
cursor: pointer; display: flex;
align-items: center;
.nav-menu-icon { justify-content: center;
box-sizing: content-box; cursor: pointer;
color: v-bind('configStore.getColorVal("headerBarTabColor")');
} .nav-menu-icon {
box-sizing: content-box;
&:hover { color: v-bind('configStore.getColorVal("headerBarTabColor")');
.icon { }
animation: twinkle 0.3s ease-in-out;
} &:hover {
} .icon {
} animation: twinkle 0.3s ease-in-out;
}
.admin-info { }
display: flex; }
height: 100%;
padding: 0 10px; .admin-info {
align-items: center; display: flex;
cursor: pointer; height: 100%;
user-select: none; padding: 0 10px;
color: v-bind('configStore.getColorVal("headerBarTabColor")'); align-items: center;
cursor: pointer;
&:hover { user-select: none;
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")'); color: v-bind('configStore.getColorVal("headerBarTabColor")');
}
} &:hover {
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")');
.admin-name { }
padding-left: 6px; }
white-space: nowrap;
} .admin-name {
padding-left: 6px;
.nav-menu-item:hover, white-space: nowrap;
.admin-info:hover, }
.nav-menu-item.hover,
.admin-info.hover { .nav-menu-item:hover,
background: v-bind('configStore.getColorVal("headerBarHoverBackground")'); .admin-info:hover,
.nav-menu-item.hover,
.nav-menu-icon { .admin-info.hover {
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important; background: v-bind('configStore.getColorVal("headerBarHoverBackground")');
}
} .nav-menu-icon {
} color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important;
}
.dropdown-menu-box :deep(.el-dropdown-menu__item) { }
justify-content: center; }
}
.dropdown-menu-box :deep(.el-dropdown-menu__item) {
.admin-info-base { justify-content: center;
display: flex; }
justify-content: center;
flex-wrap: wrap; .admin-info-base {
padding-top: 10px; display: flex;
justify-content: center;
.admin-info-other { flex-wrap: wrap;
display: block; padding-top: 10px;
width: 100%;
text-align: center; .admin-info-other {
padding: 10px 0; display: block;
width: 100%;
.admin-info-name { text-align: center;
font-size: var(--el-font-size-large); padding: 10px 0;
}
} .admin-info-name {
} font-size: var(--el-font-size-large);
}
.admin-info-footer { }
padding: 10px 0; }
margin: 0 -12px -12px -12px;
display: flex; .admin-info-footer {
justify-content: space-around; padding: 10px 0;
} margin: 0 -12px -12px -12px;
display: flex;
.pt2 { justify-content: space-around;
padding-top: 2px; }
}
.pt2 {
@keyframes twinkle { padding-top: 2px;
0% { }
transform: scale(0);
} @keyframes twinkle {
80% { 0% {
transform: scale(1.2); transform: scale(0);
} }
100% { 80% {
transform: scale(1); transform: scale(1.2);
} }
} 100% {
</style> transform: scale(1);
}
}
</style>

View File

@@ -1,51 +1,51 @@
<template> <template>
<el-dialog draggable width="600px" v-model.trim="dialogVisible" :title="title"> <el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title">
<el-form :inline="false" :model="form" label-width="auto" class="form-one"> <el-form :inline="false" :model="form" label-width="auto" class="form-one">
<el-form-item label="用户名称:"> <el-form-item label="用户名称:">
<el-input v-model.trim="form.name" :disabled="true"></el-input> <el-input v-model.trim="form.name" :disabled="true"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="登录名称:" class="top"> <el-form-item label="登录名称:" class="top">
<el-input v-model.trim="form.loginName" :disabled="true"></el-input> <el-input v-model.trim="form.loginName" :disabled="true"></el-input>
</el-form-item> </el-form-item>
<!-- <el-form-item label="归属部门名称:" class="top"> <!-- <el-form-item label="归属部门名称:" class="top">
<el-input v-model.trim="form.deptName" :disabled="true"></el-input> <el-input v-model.trim="form.deptName" :disabled="true"></el-input>
</el-form-item> --> </el-form-item> -->
<el-form-item label="拥有的角色:" class="top"> <el-form-item label="拥有的角色:" class="top">
<el-input v-model.trim="form.role" :disabled="true"></el-input> <el-input v-model.trim="form.role" :disabled="true"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="电话号码:" class="top"> <el-form-item label="电话号码:" class="top">
<el-input v-model.trim="form.phone" :disabled="true"></el-input> <el-input v-model.trim="form.phone" :disabled="true"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="电子邮箱:" class="top"> <el-form-item label="电子邮箱:" class="top">
<el-input v-model.trim="form.email" :disabled="true"></el-input> <el-input v-model.trim="form.email" :disabled="true"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-dialog> </el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, inject } from 'vue' import { ref, inject } from 'vue'
import { reactive } from 'vue' import { reactive } from 'vue'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
const dialogVisible = ref(false) const dialogVisible = ref(false)
const title = ref('用户信息') const title = ref('用户信息')
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const formRef = ref() const formRef = ref()
const form = reactive({ const form = reactive({
name: '', name: '',
deptName: '', deptName: '',
phone: '', phone: '',
email: '', email: '',
role: '', role: '',
loginName: '' loginName: ''
}) })
const open = () => { const open = () => {
dialogVisible.value = true dialogVisible.value = true
for (const key in form) { for (const key in form) {
form[key] = Array.isArray(adminInfo.$state[key]) ? adminInfo.$state[key].join(',') : adminInfo.$state[key] form[key] = Array.isArray(adminInfo.$state[key]) ? adminInfo.$state[key].join(',') : adminInfo.$state[key]
} }
} }
defineExpose({ open }) defineExpose({ open })
</script> </script>

View File

@@ -1,109 +1,123 @@
<template> <template>
<el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title"> <el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title">
<el-scrollbar> <el-scrollbar>
<el-form :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one" ref="formRef"> <el-form :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one" ref="formRef">
<el-form-item label="校验密码:" prop="password"> <el-form-item label="校验密码:" prop="password">
<el-input v-model.trim="form.password" type="password" placeholder="请输入校验密码" show-password /> <el-input v-model.trim="form.password" type="password" placeholder="请输入校验密码" show-password />
</el-form-item> </el-form-item>
<el-form-item label="新密码:" prop="newPwd"> <el-form-item label="新密码:" prop="newPwd">
<el-input v-model.trim="form.newPwd" type="password" placeholder="请输入新密码" show-password /> <el-input v-model.trim="form.newPwd" type="password" placeholder="请输入新密码" show-password />
</el-form-item> </el-form-item>
<el-form-item label="确认密码:" prop="confirmPwd"> <el-form-item label="确认密码:" prop="confirmPwd">
<el-input v-model.trim="form.confirmPwd" type="password" placeholder="请输入确认密码" show-password /> <el-input
</el-form-item> v-model.trim="form.confirmPwd"
</el-form> type="password"
</el-scrollbar> placeholder="请输入确认密码"
<template #footer> show-password
<span class="dialog-footer"> />
<el-button @click="dialogVisible = false">取消</el-button> </el-form-item>
<el-button type="primary" @click="submit">确认</el-button> </el-form>
</span> </el-scrollbar>
</template> <template #footer>
</el-dialog> <span class="dialog-footer">
</template> <el-button @click="dialogVisible = false">取消</el-button>
<script lang="ts" setup> <el-button type="primary" @click="submit">确认</el-button>
import { ref, inject } from 'vue' </span>
import { reactive } from 'vue' </template>
import { ElMessage } from 'element-plus' </el-dialog>
import { passwordConfirm, updatePassword } from '@/api/user-boot/user' </template>
import { validatePwd } from '@/utils/common' <script lang="ts" setup>
import { useAdminInfo } from '@/stores/adminInfo' import { ref, inject } from 'vue'
import { reactive } from 'vue'
const adminInfo = useAdminInfo() import { ElMessage } from 'element-plus'
const dialogVisible = ref(false) import { passwordConfirm, updatePassword } from '@/api/user-boot/user'
const title = ref('修改密码') import { validatePwd } from '@/utils/common'
const formRef = ref() import { useAdminInfo } from '@/stores/adminInfo'
// 注意不要和表单ref的命名冲突 import router from '@/router'
const form = reactive({ import { useNavTabs } from '@/stores/navTabs'
password: '', const adminInfo = useAdminInfo()
newPwd: '', const navTabs = useNavTabs()
confirmPwd: '' const dialogVisible = ref(false)
}) const title = ref('修改密码')
const rules = { const formRef = ref()
password: [ // 注意不要和表单ref的命名冲突
{ required: true, message: '请输入校验密码', trigger: 'blur' }, const form = reactive({
{ password: '',
min: 6, newPwd: '',
max: 16, confirmPwd: ''
message: '长度在 6 到 16 个字符', })
trigger: 'blur' const rules = {
} password: [
], { required: true, message: '请输入校验密码', trigger: 'blur' },
newPwd: [ {
{ required: true, message: '请输入密码', trigger: 'blur' }, min: 6,
{ max: 16,
min: 6, message: '长度在 6 到 16 个字符',
max: 16, trigger: 'blur'
message: '长度在 6 到 16 个字符', }
trigger: 'blur' ],
}, newPwd: [
{ validator: validatePwd, trigger: 'blur' } { required: true, message: '请输入密码', trigger: 'blur' },
], {
confirmPwd: [ min: 6,
{ required: true, message: '请确认密码', trigger: 'blur' }, max: 16,
{ message: '长度在 6 到 16 个字符',
min: 6, trigger: 'blur'
max: 16, },
message: '长度在 6 到 16 个字符', { validator: validatePwd, trigger: 'blur' }
trigger: 'blur' ],
}, confirmPwd: [
{ { required: true, message: '请确认密码', trigger: 'blur' },
validator: (rule: any, value: string, callback: any) => { {
if (value === '') { min: 6,
callback(new Error('请再次输入密码')) max: 16,
} else if (value !== form.newPwd) { message: '长度在 6 到 16 个字符',
callback(new Error('两次输入密码不一致!')) trigger: 'blur'
} else { },
callback() {
} validator: (rule: any, value: string, callback: any) => {
}, if (value === '') {
trigger: 'blur', callback(new Error('请再次输入密码'))
required: true } else if (value !== form.newPwd) {
} callback(new Error('两次输入密码不一致!'))
] } else {
} callback()
}
const open = () => { },
dialogVisible.value = true trigger: 'blur',
form.password = '' required: true
form.newPwd = '' }
form.confirmPwd = '' ]
} }
const submit = () => {
formRef.value.validate(async (valid: boolean) => { const open = () => {
if (valid) { dialogVisible.value = true
passwordConfirm(form.password).then(res => { form.password = ''
updatePassword({ form.newPwd = ''
id: adminInfo.$state.userIndex, form.confirmPwd = ''
newPassword: form.newPwd }
}).then((res: any) => { const submit = () => {
ElMessage.success('密码修改成功') formRef.value.validate(async (valid: boolean) => {
dialogVisible.value = false if (valid) {
}) passwordConfirm(form.password).then(res => {
}) updatePassword({
} id: adminInfo.$state.userIndex,
}) newPassword: form.newPwd
} }).then(async (res: any) => {
ElMessage.success('密码修改成功')
defineExpose({ open }) dialogVisible.value = false
</script>
setTimeout(() => {
navTabs.closeTabs()
window.localStorage.clear()
adminInfo.reset()
router.push({ name: 'login' })
}, 0)
})
})
}
})
}
defineExpose({ open })
</script>

View File

@@ -51,11 +51,11 @@ onBeforeMount(() => {
}) })
const init = async () => { const init = async () => {
await Promise.all([getAreaList(), dictDataCache(), getUserById(), areaSelect(),getAllUserSimpleList()]).then(res => { await Promise.all([ dictDataCache(), getUserById(), areaSelect(),getAllUserSimpleList()]).then(res => {
dictData.state.area = res[0].data // dictData.state.area = res[0].data
dictData.state.basic = res[1].data dictData.state.basic = res[0].data
// dictData.state.userList=res[4].data // dictData.state.userList=res[4].data
adminInfo.dataFill(res[2].data) adminInfo.dataFill(res[1].data)
// dictData.state.areaTree = res[3].data // dictData.state.areaTree = res[3].data
}) })
/** /**

View File

@@ -1,47 +1,47 @@
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import staticRoutes from '@/router/static' import staticRoutes from '@/router/static'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import { loading } from '@/utils/loading' import { loading } from '@/utils/loading'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes: staticRoutes routes: staticRoutes
}) })
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
NProgress.start() NProgress.start()
if (!window.existLoading) { if (!window.existLoading) {
loading.show() loading.show()
window.existLoading = true window.existLoading = true
} }
if (to.path == '/login' || to.path == '/404'|| to.path == '/policy'|| to.path == '/agreement') { if (to.path == '/login' || to.path == '/404'|| to.path == '/policy'|| to.path == '/agreement') {
// 登录或者注册才可以往下进行 // 登录或者注册才可以往下进行
next() next()
} else { } else {
// 获取 token // 获取 token
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const token = adminInfo.getToken() const token = adminInfo.getToken()
// token 不存在 // token 不存在
if (token === null || token === '') { if (token === null || token === '') {
ElMessage.error('您还没有登录,请先登录') // ElMessage.error('您还没有登录,请先登录')
next('/login') next('/login')
} else { } else {
next() next()
} }
} }
// next() // next()
}) })
// 路由加载后 // 路由加载后
router.afterEach(() => { router.afterEach(() => {
if (window.existLoading) { if (window.existLoading) {
loading.hide() loading.hide()
} }
NProgress.done() NProgress.done()
}) })
export default router export default router

View File

@@ -35,6 +35,16 @@ export const adminBaseRoute = {
title: pageTitle('router.supplementaryRecruitment') title: pageTitle('router.supplementaryRecruitment')
} }
}, },
{
// 版本维护
path: '/version',
name: 'version',
component: () => import('@/views/govern/manage/basic/version.vue'),
meta: {
title: pageTitle('router.version')
}
},
{ {
path: 'cockpit', path: 'cockpit',
name: '项目管理', name: '项目管理',

View File

@@ -1,108 +1,168 @@
import { reactive } from 'vue' import { reactive } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { STORE_TAB_VIEW_CONFIG } from '@/stores/constant/cacheKey' import { STORE_TAB_VIEW_CONFIG } from '@/stores/constant/cacheKey'
import type { NavTabs } from '@/stores/interface/index' import type { NavTabs } from '@/stores/interface/index'
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router' import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
import { adminBaseRoutePath } from '@/router/static' import { adminBaseRoutePath } from '@/router/static'
import { set } from 'lodash'
export const useNavTabs = defineStore(
'navTabs', export const useNavTabs = defineStore(
() => { 'navTabs',
const state: NavTabs = reactive({ () => {
// 激活tab的index const state: NavTabs = reactive({
activeIndex: 0, // 激活tab的index
// 激活的tab activeIndex: 0,
activeRoute: null, // 激活的tab
// tab列表 activeRoute: null,
tabsView: [], // tab列表
// 当前tab是否全屏 tabsView: [],
tabFullScreen: false, // 当前tab是否全屏
// 从后台加载到的菜单路由列表 tabFullScreen: false,
tabsViewRoutes: [], // 从后台加载到的菜单路由列表
// 按钮权限节点 tabsViewRoutes: [],
authNode: new Map(), // 按钮权限节点
}) authNode: new Map()
})
function addTab(route: RouteLocationNormalized) {
if (!route.meta.addtab) return function addTab(route: RouteLocationNormalized) {
for (const key in state.tabsView) { if (!route.meta.addtab) return
if (state.tabsView[key].path === route.path) { for (const key in state.tabsView) {
state.tabsView[key].params = route.params ? route.params : state.tabsView[key].params if (state.tabsView[key].path === route.path) {
state.tabsView[key].query = route.query ? route.query : state.tabsView[key].query state.tabsView[key].params = route.params ? route.params : state.tabsView[key].params
return state.tabsView[key].query = route.query ? route.query : state.tabsView[key].query
} state.tabsView[key].meta = route.query ? route.meta : state.tabsView[key].meta
} return
state.tabsView.push(route) }
} }
state.tabsView.push(route)
function closeTab(route: RouteLocationNormalized) { }
state.tabsView.map((v, k) => {
if (v.path == route.path) { function closeTab(route: RouteLocationNormalized) {
state.tabsView.splice(k, 1) state.tabsView.map((v, k) => {
return if (v.path == route.path) {
} state.tabsView.splice(k, 1)
}) return
} }
})
/** }
* 关闭多个标签
* @param retainMenu 需要保留的标签,否则关闭全部标签 /**
*/ * 关闭多个标签
const closeTabs = (retainMenu: RouteLocationNormalized | false = false) => { * @param retainMenu 需要保留的标签,否则关闭全部标签
if (retainMenu) { */
state.tabsView = [retainMenu] const closeTabs = (retainMenu: RouteLocationNormalized | false = false) => {
} else { if (retainMenu) {
state.tabsView = [] state.tabsView = [retainMenu]
} } else {
} state.tabsView = []
}
const setActiveRoute = (route: RouteLocationNormalized): void => { }
const currentRouteIndex: number = state.tabsView.findIndex((item: RouteLocationNormalized) => {
return item.path === route.path const setActiveRoute = (route: RouteLocationNormalized): void => {
}) const currentRouteIndex: number = state.tabsView.findIndex((item: RouteLocationNormalized) => {
if (currentRouteIndex === -1) return return item.path === route.path
state.activeRoute = route })
state.activeIndex = currentRouteIndex if (currentRouteIndex === -1) return
} state.activeRoute = route
state.activeIndex = currentRouteIndex
const setTabsViewRoutes = (data: RouteRecordRaw[]): void => { }
state.tabsViewRoutes = encodeRoutesURI(data)
} const setTabsViewRoutes = (data: RouteRecordRaw[]): void => {
state.tabsViewRoutes = encodeRoutesURI(JSON.parse(JSON.stringify(data)))
const setAuthNode = (key: string, data: string[]) => { }
state.authNode.set(key, data)
} const setAuthNode = (key: string, data: string[]) => {
state.authNode.set(key, data)
const fillAuthNode = (data: Map<string, string[]>) => { }
state.authNode = data
} const fillAuthNode = (data: Map<string, string[]>) => {
state.authNode = data
const setFullScreen = (fullScreen: boolean): void => { }
state.tabFullScreen = fullScreen
} const setFullScreen = (fullScreen: boolean): void => {
state.tabFullScreen = fullScreen
return { state, addTab, closeTab, closeTabs, setActiveRoute, setTabsViewRoutes, setAuthNode, fillAuthNode, setFullScreen } }
},
{ const refresh = () => {
persist: { // setTimeout(() => {
key: STORE_TAB_VIEW_CONFIG, // console.log(123, state.tabsViewRoutes)
paths: ['state.tabFullScreen'],
}, let list = matchAndReturnRouteData(state.tabsViewRoutes, state.tabsView)
} state.tabsView = []
) list.forEach(item => {
addTab(item)
/** })
* 对iframe的url进行编码 // }, 1000)
*/ }
function encodeRoutesURI(data: RouteRecordRaw[]) { return {
data.forEach((item) => { state,
if (item.meta?.menu_type == 'iframe') { addTab,
item.path = adminBaseRoutePath + '/iframe/' + encodeURIComponent(item.path) closeTab,
} closeTabs,
setActiveRoute,
if (item.children && item.children.length) { setTabsViewRoutes,
item.children = encodeRoutesURI(item.children) setAuthNode,
} fillAuthNode,
}) setFullScreen,
return data refresh
} }
},
{
persist: {
key: STORE_TAB_VIEW_CONFIG,
paths: ['state.tabFullScreen']
}
}
)
/**
* 核心逻辑:
* 1. 递归遍历树形菜单筛选出与routeList中name匹配的节点
* 2. 将匹配到的节点格式转换为routeList的结构并返回
*/
function matchAndReturnRouteData(tree, routeList) {
// 1. 构建路由name映射name -> 完整路由对象)
const routeMap = new Map()
routeList.forEach(route => {
if (route.name) {
routeMap.set(route.name, route)
}
})
// 2. 递归遍历树形菜单,收集匹配的节点
const matchedNodes = []
function recursion(node) {
// 匹配当前节点
if (routeMap.has(node.name)) {
// 深度克隆路由对象,避免修改原数据
const matchedRoute = JSON.parse(JSON.stringify(routeMap.get(node.name)))
matchedNodes.push(matchedRoute)
}
// 递归处理子节点
if (node.children && node.children.length) {
node.children.forEach(child => recursion(child))
}
}
// 遍历所有顶级节点
tree.forEach(node => recursion(node))
// 3. 返回匹配后的第二个数据格式和routeList结构一致
return matchedNodes
}
/**
* 对iframe的url进行编码
*/
function encodeRoutesURI(data: RouteRecordRaw[]) {
data.forEach(item => {
if (item.meta?.menu_type == 'iframe') {
item.path = adminBaseRoutePath + '/iframe/' + encodeURIComponent(item.path)
}
if (item.children && item.children.length) {
item.children = encodeRoutesURI(item.children)
}
})
return data
}

58
src/styles/vxeTable.css Normal file
View File

@@ -0,0 +1,58 @@
.vxe-header--row {
background: var(--vxe-table-header-background-color);
color: var(--vxe-table-header-font-color);
}
.is--checked.vxe-checkbox,
.is--checked.vxe-checkbox .vxe-checkbox--icon,
.is--checked.vxe-custom--checkbox-option,
.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,
.is--checked.vxe-export--panel-column-option,
.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,
.is--checked.vxe-table--filter-option,
.is--checked.vxe-table--filter-option .vxe-checkbox--icon,
.is--indeterminate.vxe-checkbox,
.is--indeterminate.vxe-checkbox .vxe-checkbox--icon,
.is--indeterminate.vxe-custom--checkbox-option,
.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,
.is--indeterminate.vxe-export--panel-column-option,
.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,
.is--indeterminate.vxe-table--filter-option,
.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,
.vxe-table--render-default .is--checked.vxe-cell--checkbox,
.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,
.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon {
color: var(--el-color-primary-light-3);
}
.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,
.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon {
color: var(--el-color-primary-light-5);
}
.vxe-table--render-default .vxe-body--row.row--current,
.vxe-table--render-default .vxe-body--row.row--current:hover {
background-color: var(--el-color-primary-light-8);
}
.vxe-table--tooltip-wrapper {
z-index: 10000 !important;
}
.is--disabled {
background-color: var(--vxe-input-disabled-color);
}
.vxe-modal--wrapper {
z-index: 5000 !important;
}

1
src/styles/vxeTable.min.css vendored Normal file
View File

@@ -0,0 +1 @@
.vxe-header--row{background:var(--vxe-table-header-background-color);color:var(--vxe-table-header-font-color)}.is--checked.vxe-checkbox,.is--checked.vxe-checkbox .vxe-checkbox--icon,.is--checked.vxe-custom--checkbox-option,.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,.is--checked.vxe-export--panel-column-option,.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,.is--checked.vxe-table--filter-option,.is--checked.vxe-table--filter-option .vxe-checkbox--icon,.is--indeterminate.vxe-checkbox,.is--indeterminate.vxe-checkbox .vxe-checkbox--icon,.is--indeterminate.vxe-custom--checkbox-option,.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,.is--indeterminate.vxe-export--panel-column-option,.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,.is--indeterminate.vxe-table--filter-option,.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,.vxe-table--render-default .is--checked.vxe-cell--checkbox,.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon{color:var(--el-color-primary-light-3)}.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon{color:var(--el-color-primary-light-5)}.vxe-table--render-default .vxe-body--row.row--current,.vxe-table--render-default .vxe-body--row.row--current:hover{background-color:var(--el-color-primary-light-8)}.vxe-table--tooltip-wrapper{z-index:10000 !important}.is--disabled{background-color:var(--vxe-input-disabled-color)}.vxe-modal--wrapper{z-index:5000 !important}

View File

@@ -1,84 +1,87 @@
// .vxe-table--body-wrapper, // .vxe-table--body-wrapper,
// .boxbx { // .boxbx {
// &::-webkit-scrollbar { // &::-webkit-scrollbar {
// width: 10px; // width: 10px;
// height: 10px; // height: 10px;
// } // }
// &::-webkit-scrollbar-thumb { // &::-webkit-scrollbar-thumb {
// border-radius: 5px; // border-radius: 5px;
// height: 3px; // height: 3px;
// background-color: var(--el-color-primary) !important; // background-color: var(--el-color-primary) !important;
// border-radius: 30px !important; // border-radius: 30px !important;
// } // }
// } // }
// .vxe-table--body-wrapper::-webkit-scrollbar-thumb { // .vxe-table--body-wrapper::-webkit-scrollbar-thumb {
// border-radius: 5px; // border-radius: 5px;
// height: 3px; // height: 3px;
// background-color: var(--el-color-primary) !important; // background-color: var(--el-color-primary) !important;
// border-radius: 30px !important; // border-radius: 30px !important;
// } // }
.vxe-header--row { .vxe-header--row {
background: var(--vxe-table-header-background-color); background: var(--vxe-table-header-background-color);
color: var(--vxe-table-header-font-color); color: var(--vxe-table-header-font-color);
} }
.is--checked.vxe-checkbox, .is--checked.vxe-checkbox,
.is--checked.vxe-checkbox .vxe-checkbox--icon, .is--checked.vxe-checkbox .vxe-checkbox--icon,
.is--checked.vxe-custom--checkbox-option, .is--checked.vxe-custom--checkbox-option,
.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon, .is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,
.is--checked.vxe-export--panel-column-option, .is--checked.vxe-export--panel-column-option,
.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon, .is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,
.is--checked.vxe-table--filter-option, .is--checked.vxe-table--filter-option,
.is--checked.vxe-table--filter-option .vxe-checkbox--icon, .is--checked.vxe-table--filter-option .vxe-checkbox--icon,
.is--indeterminate.vxe-checkbox, .is--indeterminate.vxe-checkbox,
.is--indeterminate.vxe-checkbox .vxe-checkbox--icon, .is--indeterminate.vxe-checkbox .vxe-checkbox--icon,
.is--indeterminate.vxe-custom--checkbox-option, .is--indeterminate.vxe-custom--checkbox-option,
.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon, .is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,
.is--indeterminate.vxe-export--panel-column-option, .is--indeterminate.vxe-export--panel-column-option,
.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon, .is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,
.is--indeterminate.vxe-table--filter-option, .is--indeterminate.vxe-table--filter-option,
.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon, .is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,
.vxe-table--render-default .is--checked.vxe-cell--checkbox, .vxe-table--render-default .is--checked.vxe-cell--checkbox,
.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon, .vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox, .vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon, .vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,
.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon { .vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon {
color: var(--el-color-primary-light-3); color: var(--el-color-primary-light-3);
} }
.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon, .vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon, .vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon, .vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon, .vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon, .vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-radio:not(.is--disabled):hover .vxe-radio--icon, .vxe-radio:not(.is--disabled):hover .vxe-radio--icon,
.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon, .vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon, .vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon, .vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon { .vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon {
color: var(--el-color-primary-light-5); color: var(--el-color-primary-light-5);
} }
.vxe-table--render-default .vxe-body--row.row--current,
// .vxe-table--render-default .is--disabled.vxe-cell--checkbox .vxe-checkbox--icon{ .vxe-table--render-default .vxe-body--row.row--current:hover {
// color: #fff0; background-color: var(--el-color-primary-light-8);
// } }
.vxe-table--tooltip-wrapper { // .vxe-table--render-default .is--disabled.vxe-cell--checkbox .vxe-checkbox--icon{
z-index: 10000 !important; // color: #fff0;
} // }
.is--disabled { .vxe-table--tooltip-wrapper {
background-color: var(--vxe-input-disabled-color); z-index: 10000 !important;
} }
.is--disabled {
.vxe-modal--wrapper { background-color: var(--vxe-input-disabled-color);
z-index: 5000 !important; }
}
.vxe-table--body .vxe-body--row:nth-child(even) { .vxe-modal--wrapper {
background-color: #f9f9f9; z-index: 5000 !important;
// background-color: var(--el-color-primary-light-9); }
} // .vxe-table--body .vxe-body--row:nth-child(even) {
// background-color: #f9f9f9;
.vxe-table--body .vxe-body--row:nth-child(odd) { // // background-color: var(--el-color-primary-light-9);
background-color: #ffffff; // }
}
// .vxe-table--body .vxe-body--row:nth-child(odd) {
// background-color: #ffffff;
// }

View File

@@ -1,79 +1,79 @@
<template> <template>
<el-dialog width="600px" v-model.trim='dialogVisible' :title='title'> <el-dialog width="500px" v-model.trim='dialogVisible' :title='title'>
<el-scrollbar> <el-scrollbar>
<el-form :inline='false' :model='form' label-width='auto' class="form-one" :rules='rules' ref='formRef'> <el-form :inline='false' :model='form' label-width='auto' class="form-one" :rules='rules' ref='formRef'>
<el-form-item label='角色名称'> <el-form-item label='角色名称'>
<el-input maxlength="32" show-word-limit v-model.trim='form.name' placeholder='请输入菜单名称' /> <el-input maxlength="32" show-word-limit v-model.trim='form.name' placeholder='请输入菜单名称' />
</el-form-item> </el-form-item>
<el-form-item label='角色编码'> <el-form-item label='角色编码'>
<el-input maxlength="32" show-word-limit v-model.trim='form.code' placeholder='请输入菜单名称' /> <el-input maxlength="32" show-word-limit v-model.trim='form.code' placeholder='请输入菜单名称' />
</el-form-item> </el-form-item>
<el-form-item label='角色描述'> <el-form-item label='角色描述'>
<el-input maxlength="300" show-word-limit v-model.trim='form.remark' :rows='2' type='textarea' <el-input maxlength="300" show-word-limit v-model.trim='form.remark' :rows='2' type='textarea'
placeholder='请输入描述' /> placeholder='请输入描述' />
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
<template #footer> <template #footer>
<span class='dialog-footer'> <span class='dialog-footer'>
<el-button @click='dialogVisible = false'>取消</el-button> <el-button @click='dialogVisible = false'>取消</el-button>
<el-button type='primary' @click='submit'>确认</el-button> <el-button type='primary' @click='submit'>确认</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { ref, inject } from 'vue' import { ref, inject } from 'vue'
import { reactive } from 'vue' import { reactive } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import TableStore from '@/utils/tableStore' // 若不是列表页面弹框可删除 import TableStore from '@/utils/tableStore' // 若不是列表页面弹框可删除
const dialogVisible = ref(false) const dialogVisible = ref(false)
const title = ref('') const title = ref('')
const tableStore = inject('tableStore') as TableStore const tableStore = inject('tableStore') as TableStore
const formRef = ref() const formRef = ref()
// 注意不要和表单ref的命名冲突 // 注意不要和表单ref的命名冲突
const form = reactive({ const form = reactive({
code: '', code: '',
name: '', name: '',
remark: '', remark: '',
id: '' id: ''
}) })
const rules = { const rules = {
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
code: [{ required: true, message: '角色编码不能为空', trigger: 'blur' }] code: [{ required: true, message: '请输入角色编码', trigger: 'blur' }]
} }
const open = (text: string, data?: anyObj) => { const open = (text: string, data?: anyObj) => {
title.value = text title.value = text
dialogVisible.value = true dialogVisible.value = true
if (data) { if (data) {
// 表单赋值 // 表单赋值
for (let key in form) { for (let key in form) {
form[key] = data[key] form[key] = data[key]
} }
} else { } else {
// 在此处恢复默认表单 // 在此处恢复默认表单
for (let key in form) { for (let key in form) {
form[key] = '' form[key] = ''
} }
} }
} }
const submit = () => { const submit = () => {
formRef.value.validate(async (valid) => { formRef.value.validate(async (valid) => {
if (valid) { if (valid) {
if (form.id) { if (form.id) {
// await update(form) // await update(form)
} else { } else {
// await create(form) // await create(form)
} }
ElMessage.success('保存成功') ElMessage.success('保存成功')
tableStore.index() tableStore.index()
dialogVisible.value = false dialogVisible.value = false
} }
}) })
} }
defineExpose({ open }) defineExpose({ open })
</script> </script>

103
src/utils/downloadFile.ts Normal file
View File

@@ -0,0 +1,103 @@
import { ElMessage, ElMessageBox, ElInput, ElSegmented } from 'element-plus'
export const downLoadFile = (name: string, key: string, res: any) => {
let blob = new Blob([res], {
type: getFileType(key)
})
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a') // 创建a标签
link.href = url
link.download = name // 设置下载的文件名
document.body.appendChild(link)
link.click() //执行下载
document.body.removeChild(link)
ElMessage.success('下载成功')
}
const getFileType = (url: string) => {
const ext = url.split('.').pop()?.toLowerCase() || ''
const mimeMap: Record<string, string> = {
// Excel
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// CSV
csv: 'text/csv',
// Word
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
// PDF
pdf: 'application/pdf',
// PowerPoint
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
// 图片
png: 'image/png',
gif: 'image/gif',
jpeg: 'image/jpeg',
jpg: 'image/jpeg',
bmp: 'image/bmp',
ico: 'image/vnd.microsoft.icon',
tif: 'image/tiff',
tiff: 'image/tiff',
svg: 'image/svg+xml',
webp: 'image/webp',
// 音频
mp3: 'audio/mpeg',
aac: 'audio/aac',
mid: 'audio/midi',
midi: 'audio/midi',
oga: 'audio/ogg',
wav: 'audio/wav',
weba: 'audio/webm',
// 视频
avi: 'video/x-msvideo',
mpeg: 'video/mpeg',
ogv: 'video/ogg',
webm: 'video/webm',
'3gp': 'video/3gpp',
'3g2': 'video/3gpp2',
// 网页/代码
html: 'text/html',
css: 'text/css',
js: 'text/javascript',
mjs: 'text/javascript',
json: 'application/json',
jsonld: 'application/ld+json',
xhtml: 'application/xhtml+xml',
xml: 'application/xml',
xul: 'application/vnd.mozilla.xul+xml',
// 文档
abw: 'application/x-abiword',
odp: 'application/vnd.oasis.opendocument.presentation',
ods: 'application/vnd.oasis.opendocument.spreadsheet',
odt: 'application/vnd.oasis.opendocument.text',
rtf: 'application/rtf',
txt: 'text/plain',
vsd: 'application/vnd.visio',
// 字体
otf: 'font/otf',
ttf: 'font/ttf',
woff: 'font/woff',
woff2: 'font/woff2',
eot: 'application/vnd.ms-fontobject',
// 压缩/归档
arc: 'application/x-freearc',
bz: 'application/x-bzip',
bz2: 'application/x-bzip2',
rar: 'application/x-rar-compressed',
tar: 'application/x-tar',
zip: 'application/zip',
'7z': 'application/x-7z-compressed',
// 其他
bin: 'application/octet-stream',
csh: 'application/x-csh',
epub: 'application/epub+zip',
azw: 'application/vnd.amazon.ebook',
ics: 'text/calendar',
jar: 'application/java-archive',
mpkg: 'application/vnd.apple.installer+xml',
ogx: 'application/ogg',
sh: 'application/x-sh',
swf: 'application/x-shockwave-flash'
}
return mimeMap[ext] || ''
}

View File

@@ -1,4 +1,3 @@
import { number } from 'vue-types'
const dataProcessing = (arr: any[]) => { const dataProcessing = (arr: any[]) => {
return arr return arr
@@ -44,7 +43,8 @@ const calculateValue = (o: number, value: number, num: number, isMin: boolean) =
} }
if (base === 0.1) { if (base === 0.1) {
return parseFloat(calculatedValue.toFixed(1)) // return parseFloat(calculatedValue.toFixed(1))
return Math.ceil(calculatedValue * 10) / 10
} else if (isMin) { } else if (isMin) {
return Math.floor(calculatedValue / base) * base return Math.floor(calculatedValue / base) * base
} else { } else {

View File

@@ -352,7 +352,9 @@ export function getTimeOfTheMonth(key: any): [string, string] {
const dayOfWeek = now.getDay() // 0是周日 const dayOfWeek = now.getDay() // 0是周日
const diff = now.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1) // 调整为周一 const diff = now.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1) // 调整为周一
const weekStart = new Date(year, month, diff) const weekStart = new Date(year, month, diff)
return [formatDate(weekStart, 'YYYY-MM-DD'), formatDate(now, 'YYYY-MM-DD')] const weekEnd = new Date(weekStart)
weekEnd.setDate(weekEnd.getDate() + 6)
return [formatDate(weekStart, 'YYYY-MM-DD'), formatDate(weekEnd, 'YYYY-MM-DD')]
case '5': // 日 case '5': // 日
return [formatDate(now, 'YYYY-MM-DD'), formatDate(now, 'YYYY-MM-DD')] return [formatDate(now, 'YYYY-MM-DD'), formatDate(now, 'YYYY-MM-DD')]
@@ -361,3 +363,23 @@ export function getTimeOfTheMonth(key: any): [string, string] {
throw new Error('Invalid key') throw new Error('Invalid key')
} }
} }
/**
* 获取当月时间
* @param interval 组件外部时间 1 年 2 季 3 月 4 周 5 日
* @param timeList 驾驶舱里面组件勾选时间 []
* @param externalTime //外部传入时间
* @param fullscreen // 全屏是否全屏
*/
export function getTime(interval: number | 3, timeList: any, externalTime: any) {
// console.log('🚀 ~ getTime ~ timeList:', timeList)
// 1、先匹配时间
// 检查 interval 是否在 timeList 中
if (timeList && timeList.includes(interval.toString())) {
return [externalTime[0], externalTime[1], interval]
} else {
if (timeList && timeList.length > 0) {
return [...getTimeOfTheMonth(timeList[0]), timeList[0]]
}
}
}

View File

@@ -4,16 +4,16 @@ import { ElLoading, ElMessage, ElNotification, type LoadingOptions } from 'eleme
import { refreshToken } from '@/api/user-boot/user' import { refreshToken } from '@/api/user-boot/user'
import router from '@/router/index' import router from '@/router/index'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { useNavTabs } from '@/stores/navTabs'
window.requests = [] window.requests = []
window.tokenRefreshing = false window.tokenRefreshing = false
let loginExpireTimer:any=null let loginExpireTimer: any = null
const pendingMap = new Map() const pendingMap = new Map()
const loadingInstance: LoadingInstance = { const loadingInstance: LoadingInstance = {
target: null, target: null,
count: 0 count: 0
} }
const navTabs = useNavTabs()
/** /**
* 根据运行环境获取基础请求URL * 根据运行环境获取基础请求URL
*/ */
@@ -65,10 +65,19 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
if ( if (
!( !(
config.url == '/system-boot/file/upload' || config.url == '/system-boot/file/upload' ||
config.url == '/user-boot/sysRoleSystem/getSystemByRoleId' ||
config.url == '/system-boot/file/getFileUrl' ||
config.url == '/cs-harmonic-boot/realData/getBaseRealData' ||
config.url == '/harmonic-boot/grid/getAssessOverview' || config.url == '/harmonic-boot/grid/getAssessOverview' ||
config.url == '/harmonic-boot/gridDiagram/getGridDiagramAreaData' || config.url == '/harmonic-boot/gridDiagram/getGridDiagramAreaData' ||
config.url == '/cs-device-boot/csline/list' || config.url == '/cs-device-boot/csline/list' ||
config.url == '/cs-harmonic-boot/pqSensitiveUser/getListByIds' config.url == '/cs-harmonic-boot/pqSensitiveUser/getListByIds' ||
config.url == '/cs-harmonic-boot/csevent/getEventCoords' ||
config.url == '/cs-harmonic-boot/limitRateDetailD/limitTimeProbabilityData' ||
config.url == '/cs-harmonic-boot/limitRateDetailD/limitProbabilityData' ||
config.url == '/system-boot/dictTree/queryByCode' ||
config.url == '/system-boot/dictTree/queryByid' ||
config.url == '/system-boot/dictTree/query'
) )
) )
removePending(config) removePending(config)
@@ -106,11 +115,11 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
response => { response => {
removePending(response.config) removePending(response.config)
options.loading && closeLoading(options) // 关闭loading options.loading && closeLoading(options) // 关闭loading
if ( if (
response.data.code === 'A0000' || response.data.code === 'A0000' ||
response.data.type === 'application/json' || response.data.type === 'application/json' ||
Array.isArray(response.data) || Array.isArray(response.data) ||
response.data.size response.data.size
// || // ||
// response.data.type === 'application/octet-stream' || // response.data.type === 'application/octet-stream' ||
// response.data.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // response.data.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@@ -145,7 +154,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
}) })
} }
} else if (response.data.code == 'A0024') { } else if (response.data.code == 'A0024') {
// // 登录失效 // // 登录失效
// 清除上一次的定时器 // 清除上一次的定时器
if (loginExpireTimer) { if (loginExpireTimer) {
clearTimeout(loginExpireTimer) clearTimeout(loginExpireTimer)
@@ -156,6 +165,9 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
message: response.data.message message: response.data.message
}) })
adminInfo.removeToken() adminInfo.removeToken()
navTabs.closeTabs()
window.localStorage.clear()
adminInfo.reset()
router.push({ name: 'login' }) router.push({ name: 'login' })
loginExpireTimer = null // 执行后清空定时器 loginExpireTimer = null // 执行后清空定时器
}, 100) // 可根据实际情况调整延迟时间 }, 100) // 可根据实际情况调整延迟时间
@@ -174,6 +186,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
setTimeout(() => { setTimeout(() => {
ElMessage.error(response.data.message || '未知错误') ElMessage.error(response.data.message || '未知错误')
}, 6000) }, 6000)
} else if (response.config.url == '/cs-harmonic-boot/cspage/getByUserId') {
} else { } else {
ElMessage.error(response.data.message || '未知错误') ElMessage.error(response.data.message || '未知错误')
} }

View File

@@ -304,7 +304,7 @@ export const getMenu = () => {
} }
handlerMenu(res.data) handlerMenu(res.data)
handleAdminRoute(res.data) handleAdminRoute(res.data)
if (route.params.to) { if (route?.params?.to) {
const lastRoute = JSON.parse(route.params.to as string) const lastRoute = JSON.parse(route.params.to as string)
if (lastRoute.path != adminBaseRoutePath) { if (lastRoute.path != adminBaseRoutePath) {
let query = !isEmpty(lastRoute.query) ? lastRoute.query : {} let query = !isEmpty(lastRoute.query) ? lastRoute.query : {}

View File

@@ -45,6 +45,7 @@ export default class TableStore {
pageSize: 20 pageSize: 20
}, },
loading: true, loading: true,
exportLoading: false,
column: [], column: [],
loadCallback: null, loadCallback: null,
resetCallback: null, resetCallback: null,
@@ -196,6 +197,7 @@ export default class TableStore {
[ [
'export', 'export',
() => { () => {
this.table.exportLoading = true
// this.index() // this.index()
let params = { ...this.table.params, pageNum: 1, pageSize: this.table.total } let params = { ...this.table.params, pageNum: 1, pageSize: this.table.total }
createAxios( createAxios(
@@ -206,11 +208,16 @@ export default class TableStore {
}, },
requestPayload(this.method, params, this.paramsPOST) requestPayload(this.method, params, this.paramsPOST)
) )
).then(res => { )
this.table.allData = filtration(res.data.records || res.data) .then(res => {
this.table.exportProcessingData && this.table.exportProcessingData() this.table.allData = filtration(res.data.records || res.data)
this.table.allFlag = data.showAllFlag || true this.table.exportProcessingData && this.table.exportProcessingData()
}) this.table.allFlag = data.showAllFlag || true
this.table.exportLoading = false
})
.catch(() => {
this.table.exportLoading = false
})
} }
] ]
]) ])

View File

@@ -1,136 +1,136 @@
<template> <template>
<div class="default-main"> <div class="default-main">
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">待审核用户</div> <div class="title">待审核用户</div>
<el-button :icon="Check" type="primary" @click="addRole" class="ml10">审核通过</el-button> <el-button :icon="Check" type="primary" @click="addRole" class="ml10">审核通过</el-button>
</div> </div>
<Table ref="tableRef" /> <Table ref="tableRef" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Check } from '@element-plus/icons-vue' import { Check } from '@element-plus/icons-vue'
import { ref, onMounted, provide } from 'vue' import { ref, onMounted, provide } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { checkUser } from '@/api/user-boot/user' import { checkUser } from '@/api/user-boot/user'
defineOptions({ defineOptions({
name: 'auth/audit' name: 'auth/audit'
}) })
const tableStore = new TableStore({ const tableStore = new TableStore({
showPage: false, showPage: false,
method: 'POST', method: 'POST',
url: '/user-boot/user/checkUserList', url: '/user-boot/user/checkUserList',
column: [ column: [
// { width: '60', type: 'checkbox' }, { width: '60', type: 'checkbox' },
{ title: '名称', field: 'name' }, { title: '名称', field: 'name' },
{ title: '登录名', field: 'loginName' }, { title: '登录名', field: 'loginName' },
{ title: '角色', field: 'roleName' }, { title: '角色', field: 'roleName' },
// { title: '部门', field: 'deptId' }, // { title: '部门', field: 'deptId' },
{ title: '电话', field: 'phoneShow' }, { title: '电话', field: 'phoneShow' },
{ title: '注册时间', field: 'registerTime', sortable: true }, { title: '注册时间', field: 'registerTime', sortable: true },
{ title: '类型', field: 'casualUserName' }, { title: '类型', field: 'casualUserName' },
{ title: '状态', field: 'stateName' }, { title: '状态', field: 'stateName' },
{ {
title: '操作', title: '操作', fixed: 'right',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
name: 'edit', name: 'edit',
title: '审核通过', title: '审核通过',
type: 'primary', type: 'primary',
icon: 'el-icon-Check', icon: 'el-icon-Check',
render: 'basicButton', render: 'basicButton',
click: row => { click: row => {
checkUser([row.id]).then(res => { checkUser([row.id]).then(res => {
tableStore.index() tableStore.index()
ElMessage.success('操作成功') ElMessage.success('操作成功')
}) })
} }
}, },
{ {
name: 'del', name: 'del',
title: '审核不通过', title: '审核不通过',
type: 'danger', type: 'danger',
icon: 'el-icon-Close', icon: 'el-icon-Close',
render: 'confirmButton', render: 'confirmButton',
popconfirm: { popconfirm: {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
confirmButtonType: 'danger', confirmButtonType: 'danger',
title: '确定不通过该角色吗?' title: '确定不通过该角色吗?'
}, },
click: row => { click: row => {
ElMessage.warning('功能尚未实现') ElMessage.warning('功能尚未实现')
} }
} }
] ]
} }
], ],
loadCallback: () => { loadCallback: () => {
tableStore.table.data.forEach((item: any) => { tableStore.table.data.forEach((item: any) => {
item.deptId = item.deptId || '/' item.deptId = item.deptId || '/'
item.phoneShow = item.phone || '/' item.phoneShow = item.phone || '/'
item.roleName = item.role.length ? item.role : '/' item.roleName = item.role.length ? item.role : '/'
switch (item.casualUser) { switch (item.casualUser) {
case 0: case 0:
item.casualUserName = '临时用户' item.casualUserName = '临时用户'
break break
case 1: case 1:
item.casualUserName = '长期用户' item.casualUserName = '长期用户'
break break
default: default:
item.casualUserName = '/' item.casualUserName = '/'
break break
} }
switch (item.state) { switch (item.state) {
case 0: case 0:
item.stateName = '注销' item.stateName = '注销'
break break
case 1: case 1:
item.stateName = '正常' item.stateName = '正常'
break break
case 2: case 2:
item.stateName = '锁定' item.stateName = '锁定'
break break
case 3: case 3:
item.stateName = '待审核' item.stateName = '待审核'
break break
case 4: case 4:
item.stateName = '休眠' item.stateName = '休眠'
break break
case 5: case 5:
item.stateName = '密码过期' item.stateName = '密码过期'
break break
default: default:
item.stateName = '/' item.stateName = '/'
break break
} }
}) })
} }
}) })
tableStore.table.params.casualUser = 0 tableStore.table.params.casualUser = 0
tableStore.table.params.searchState = 0 tableStore.table.params.searchState = 0
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
tableStore.table.params.searchBeginTime = '' tableStore.table.params.searchBeginTime = ''
tableStore.table.params.searchEndTime = '' tableStore.table.params.searchEndTime = ''
tableStore.table.params.sortBy = '' tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = '' tableStore.table.params.orderBy = ''
provide('tableStore', tableStore) provide('tableStore', tableStore)
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
const addRole = () => { const addRole = () => {
if (!tableStore.table.selection.length) { if (!tableStore.table.selection.length) {
ElMessage.warning('请选择用户') ElMessage.warning('请选择用户')
return return
} }
checkUser(tableStore.table.selection.map((item: any) => item.id)).then(res => { checkUser(tableStore.table.selection.map((item: any) => item.id)).then(res => {
tableStore.index() tableStore.index()
ElMessage.success('操作成功') ElMessage.success('操作成功')
}) })
} }
</script> </script>

View File

@@ -1,116 +1,136 @@
<template> <template>
<div> <div>
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">接口权限列表</div> <div class="title">接口权限列表</div>
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" <el-input
style="width: 240px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" /> maxlength="32"
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10" :disabled="!props.id">新增</el-button> show-word-limit
</div> v-model.trim="tableStore.table.params.searchValue"
<Table ref="tableRef" /> style="width: 240px"
<popupApi ref="popupRef" @init="tableStore.index()"></popupApi> placeholder="请输入菜单名称"
</div> class="ml10"
</template> clearable
<script setup lang="ts"> @input="search"
import { Plus } from '@element-plus/icons-vue' />
import { ref, Ref, inject, provide, watch } from 'vue' <el-button :icon="Plus" type="primary" @click="addMenu" class="ml10" :disabled="!props.id">新增</el-button>
import TableStore from '@/utils/tableStore' </div>
import Table from '@/components/table/index.vue' <Table ref="tableRef" />
import popupApi from './popupApi.vue' <popupApi ref="popupRef" @init="tableStore.index()"></popupApi>
import { deleteMenu } from '@/api/user-boot/function' </div>
</template>
defineOptions({ <script setup lang="ts">
name: 'auth/menu/api' import { Plus } from '@element-plus/icons-vue'
}) import { ref, Ref, inject, provide, watch } from 'vue'
const emits = defineEmits<{ import TableStore from '@/utils/tableStore'
(e: 'init'): void import Table from '@/components/table/index.vue'
}>() import popupApi from './popupApi.vue'
const props = defineProps({ import { deleteMenu } from '@/api/user-boot/function'
id: { import { ElMessage } from 'element-plus'
type: String, defineOptions({
default: '' name: 'auth/menu/api'
} })
}) const emits = defineEmits<{
const tableRef = ref() (e: 'init'): void
const popupRef = ref() }>()
const apiList = ref([]) const props = defineProps({
const tableStore = new TableStore({ id: {
showPage: false, type: String,
url: '/user-boot/function/getButtonById', default: ''
publicHeight: 60, }
column: [ })
{ title: '普通接口/接口名称', field: 'name' }, const tableRef = ref()
{ const popupRef = ref()
title: '接口类型', const apiList = ref([])
field: 'type', const tableStore = new TableStore({
formatter: row => { showPage: false,
return row.cellValue == 1 ? '普通接口' : '公用接口' url: '/user-boot/function/getButtonById',
} publicHeight: 60,
}, column: [
{ title: 'URL接口路径', field: 'path' }, {
{ field: 'index',
title: '操作', title: '序号',
align: 'center', width: '80',
width: '180', formatter: (row: any) => {
render: 'buttons', return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
buttons: [ }
{ },
name: 'edit', { title: '普通接口/接口名称', field: 'name', minWidth: '180' },
title: '编辑', {
type: 'primary', title: '接口类型',
icon: 'el-icon-EditPen', field: 'type',
render: 'basicButton', minWidth: '140',
click: row => { formatter: row => {
popupRef.value.open('编辑接口权限', row) return row.cellValue == 1 ? '普通接口' : '公用接口'
} }
}, },
{ { title: 'URL接口路径', field: 'path', minWidth: '200' },
name: 'del', {
title: '删除', title: '操作',
type: 'danger', fixed: 'right',
icon: 'el-icon-Delete', align: 'center',
render: 'confirmButton', width: '140',
popconfirm: { render: 'buttons',
confirmButtonText: '确认', buttons: [
cancelButtonText: '取消', {
confirmButtonType: 'danger', name: 'edit',
title: '确定删除该菜单吗?' title: '编辑',
}, type: 'primary',
click: row => { icon: 'el-icon-EditPen',
deleteMenu(row.id).then(() => { render: 'basicButton',
tableStore.index() click: row => {
}) popupRef.value.open('编辑接口权限', row)
} }
} },
] {
} name: 'del',
], title: '删除',
loadCallback: () => { type: 'danger',
apiList.value = tableStore.table.data icon: 'el-icon-Delete',
search() render: 'confirmButton',
} popconfirm: {
}) confirmButtonText: '确认',
tableStore.table.loading = false cancelButtonText: '取消',
watch( confirmButtonType: 'danger',
() => props.id, title: '确定删除该菜单吗?'
() => { },
tableStore.table.params.id = props.id click: row => {
tableStore.index() deleteMenu(row.id).then(() => {
} ElMessage.success('删除成功!')
)
provide('tableStore', tableStore) tableStore.index()
})
const addMenu = () => { }
console.log(popupRef) }
popupRef.value.open('新增接口权限', { ]
pid: props.id }
}) ],
} loadCallback: () => {
const search = () => { apiList.value = tableStore.table.data
tableStore.table.data = apiList.value.filter( search()
(item: any) => }
!tableStore.table.params.searchValue || })
item.name.indexOf(tableStore.table.params.searchValue) !== -1 || tableStore.table.loading = false
item.path.indexOf(tableStore.table.params.searchValue) !== -1 watch(
) () => props.id,
} () => {
</script> tableStore.table.params.id = props.id
tableStore.index()
}
)
provide('tableStore', tableStore)
const addMenu = () => {
console.log(popupRef)
popupRef.value.open('新增接口权限', {
pid: props.id
})
}
const search = () => {
tableStore.table.data = apiList.value.filter(
(item: any) =>
!tableStore.table.params.searchValue ||
item.name.indexOf(tableStore.table.params.searchValue) !== -1 ||
item.path.indexOf(tableStore.table.params.searchValue) !== -1
)
}
</script>

View File

@@ -1,140 +1,142 @@
<template> <template>
<div> <div>
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">菜单列表</div> <div class="title">菜单列表</div>
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
style="width: 310px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" /> style="width: 310px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" />
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button> <el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button>
</div> </div>
<Table @currentChange="currentChange" /> <Table @currentChange="currentChange" />
<popupMenu ref="popupRef" @init="emits('init')"></popupMenu> <popupMenu ref="popupRef" @init="emits('init')"></popupMenu>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { ref, nextTick, inject, provide, watch } from 'vue' import { ref, nextTick, inject, provide, watch } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
import popupMenu from './popupMenu.vue' import popupMenu from './popupMenu.vue'
import { delMenu } from '@/api/systerm' import { delMenu } from '@/api/systerm'
import { ElMessage } from 'element-plus'
defineOptions({ defineOptions({
name: 'auth/menu/menu' name: 'auth/menu/menu'
}) })
const emits = defineEmits<{ const emits = defineEmits<{
(e: 'init'): void (e: 'init'): void
(e: 'select', row: any): void (e: 'select', row: any): void
}>() }>()
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
menuData: treeData[] menuData: treeData[]
}>(), }>(),
{ {
menuData: () => { menuData: () => {
return [] return []
} }
} }
) )
const popupRef = ref() const popupRef = ref()
const tableStore = new TableStore({ const tableStore = new TableStore({
showPage: false, showPage: false,
url: '/user-boot/function/functionTree', url: '/user-boot/function/functionTree',
publicHeight: 60, publicHeight: 60,
column: [ column: [
{ title: '菜单名称', field: 'title', align: 'left', treeNode: true }, { title: '菜单名称', field: 'title', align: 'left', treeNode: true },
{ {
title: '图标', title: '图标',
field: 'icon', field: 'icon',
align: 'center', align: 'center',
width: '60', width: '60',
render: 'icon' render: 'icon'
}, },
{ {
title: '操作', title: '操作', fixed: 'right',
align: 'center', align: 'center',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
name: 'edit', name: 'edit',
text: '新增', text: '新增',
type: 'primary', type: 'primary',
icon: 'el-icon-Plus', icon: 'el-icon-Plus',
render: 'basicButton', render: 'basicButton',
click: row => { click: row => {
popupRef.value.open('新增菜单', { pid: row.id }) popupRef.value.open('新增菜单', { pid: row.id })
} }
}, },
{ {
name: 'edit', name: 'edit',
text: '编辑', text: '编辑',
type: 'primary', type: 'primary',
icon: 'el-icon-EditPen', icon: 'el-icon-EditPen',
render: 'basicButton', render: 'basicButton',
click: row => { click: row => {
popupRef.value.open('编辑菜单', row) popupRef.value.open('编辑菜单', row)
} }
}, },
{ {
name: 'del', name: 'del',
text: '删除', text: '删除',
type: 'danger', type: 'danger',
icon: 'el-icon-Delete', icon: 'el-icon-Delete',
render: 'confirmButton', render: 'confirmButton',
popconfirm: { popconfirm: {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
confirmButtonType: 'danger', confirmButtonType: 'danger',
title: '确定删除该菜单吗?' title: '确定删除该菜单吗?'
}, },
click: row => { click: row => {
delMenu(row.id).then(() => { delMenu(row.id).then(() => {
emits('init') ElMessage.success('删除成功!')
})
} emits('init')
} })
] }
} }
] ]
}) }
tableStore.table.loading = false ]
watch( })
() => props.menuData, tableStore.table.loading = false
() => { watch(
search() () => props.menuData,
} () => {
) search()
provide('tableStore', tableStore) }
)
const addMenu = () => { provide('tableStore', tableStore)
popupRef.value.open('新增菜单', {})
} const addMenu = () => {
const currentChange = (newValue: any) => { popupRef.value.open('新增菜单', {})
emits('select', newValue.row.id) }
} const currentChange = (newValue: any) => {
const search = () => { emits('select', newValue.row.id)
tableStore.table.data = filterData(JSON.parse(JSON.stringify(props.menuData))) }
if (tableStore.table.params.searchValue) { const search = () => {
nextTick(() => { tableStore.table.data = filterData(JSON.parse(JSON.stringify(props.menuData)))
tableStore.table.ref?.setAllTreeExpand(true) if (tableStore.table.params.searchValue) {
}) nextTick(() => {
} tableStore.table.ref?.setAllTreeExpand(true)
} })
}
// 过滤 }
const filterData = (arr: treeData[]): treeData[] => {
if (!tableStore.table.params.searchValue) { // 过滤
return arr const filterData = (arr: treeData[]): treeData[] => {
} if (!tableStore.table.params.searchValue) {
return arr.filter((item: treeData) => { return arr
if (item.title.includes(tableStore.table.params.searchValue)) { }
return true return arr.filter((item: treeData) => {
} else if (item.children?.length > 0) { if (item.title.includes(tableStore.table.params.searchValue)) {
item.children = filterData(item.children) return true
return item.children.length > 0 } else if (item.children?.length > 0) {
} else { item.children = filterData(item.children)
return false return item.children.length > 0
} } else {
}) return false
} }
</script> })
}
</script>

View File

@@ -1,104 +1,134 @@
<template> <template>
<el-dialog width="700px" v-model.trim="dialogVisible" :title="title"> <el-dialog width="700px" v-model.trim="dialogVisible" :title="title">
<el-scrollbar> <el-scrollbar>
<el-form :mode="form" :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one"> <el-form
<el-form-item prop="name" label="接口/按钮名称"> :mode="form"
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入接口名称" /> :inline="false"
</el-form-item> ref="formRef"
<el-form-item prop="code" label="接口/按钮标识"> :model="form"
<el-input maxlength="32" show-word-limit v-model.trim="form.code" placeholder="请输入英文接口标识" /> label-width="120px"
</el-form-item> :rules="rules"
<el-form-item prop="path" label="接口路径"> class="form-one"
<el-input v-model.trim="form.path" placeholder="请输入接口路径" /> >
</el-form-item> <el-form-item prop="name" label="接口/按钮名称">
<el-form-item prop="type" label="接口类型"> <el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入接口名称" />
<el-radio-group v-model.trim="form.type"> </el-form-item>
<el-radio :label="1">普通接口</el-radio> <el-form-item prop="code" label="接口/按钮标识">
<el-radio :label="2">公用接口</el-radio> <el-input
</el-radio-group> maxlength="32"
</el-form-item> show-word-limit
<el-form-item prop="sort" label="排序"> v-model.trim="form.code"
<el-input maxlength="32" show-word-limit-number v-model.trim="form.sort" :min="0" /> placeholder="请输入英文接口标识"
</el-form-item> />
<el-form-item prop="remark" label="接口/按钮描述"> </el-form-item>
<el-input maxlength="300" show-word-limit v-model.trim="form.remark" :rows="2" type="textarea" <el-form-item prop="path" label="接口路径">
placeholder="请输入描述" /> <el-input v-model.trim="form.path" placeholder="请输入接口路径" />
</el-form-item> </el-form-item>
</el-form> <el-form-item prop="type" label="接口类型">
</el-scrollbar> <el-radio-group v-model.trim="form.type">
<el-radio :label="1">普通接口</el-radio>
<template #footer> <el-radio :label="2">公用接口</el-radio>
<span class="dialog-footer"> </el-radio-group>
<el-button @click="dialogVisible = false">取消</el-button> </el-form-item>
<el-button type="primary" @click="submit">确认</el-button> <el-form-item prop="sort" label="排序">
</span> <el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
</template> </el-form-item>
</el-dialog> <el-form-item prop="remark" label="接口/按钮描述">
</template> <el-input
<script lang="ts" setup> maxlength="300"
import { ref, inject } from 'vue' show-word-limit
import { reactive } from 'vue' v-model.trim="form.remark"
import { update, add } from '@/api/user-boot/function' :rows="2"
type="textarea"
defineOptions({ placeholder="请输入描述"
name: 'auth/menu/popupApi' />
}) </el-form-item>
const emits = defineEmits<{ </el-form>
(e: 'init'): void </el-scrollbar>
}>()
const form: any = reactive({ <template #footer>
id: '', <span class="dialog-footer">
pid: '0', <el-button @click="dialogVisible = false">取消</el-button>
code: '', <el-button type="primary" @click="submit">确认</el-button>
name: '', </span>
path: '', </template>
type: 1, </el-dialog>
sort: 100, </template>
remark: '' <script lang="ts" setup>
}) import { ref, inject } from 'vue'
const rules = { import { reactive } from 'vue'
code: [ import { update, add } from '@/api/user-boot/function'
{ required: true, message: '标识不能为空', trigger: 'blur' }, import { ElMessage } from 'element-plus'
{ defineOptions({
pattern: /^[a-zA-Z_]{1}[a-zA-Z0-9_]{2,20}$/, name: 'auth/menu/popupApi'
message: '请输入至少3-20位英文', })
min: 3, const emits = defineEmits<{
max: 20, (e: 'init'): void
trigger: 'blur' }>()
} const formRef = ref()
], const form: any = reactive({
name: [{ required: true, message: '请输入接口名称', trigger: 'blur' }], id: '',
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }], pid: '0',
path: [{ required: true, message: '请输入接口路径', trigger: 'blur' }] code: '',
} name: '',
const dialogVisible = ref(false) path: '',
const title = ref('新增菜单') type: 1,
const open = (text: string, data: anyObj) => { sort: 100,
title.value = text remark: ''
// 重置表单 })
for (let key in form) { const rules = {
form[key] = '' code: [
} { required: true, message: '请输入标识', trigger: 'blur' },
form.type = 1 {
form.pid = data.pid pattern: /^[a-zA-Z_]{1}[a-zA-Z0-9_]{2,20}$/,
if (data.id) { message: '请输入至少3-20位英文',
for (let key in form) { min: 3,
form[key] = data[key] || '' max: 20,
} trigger: 'blur'
} }
dialogVisible.value = true ],
} name: [{ required: true, message: '请输入接口名称', trigger: 'blur' }],
const submit = async () => { sort: [{ required: true, message: '请输入排序', trigger: 'blur' }],
if (form.id) { path: [{ required: true, message: '请输入接口路径', trigger: 'blur' }]
await update(form) }
} else { const dialogVisible = ref(false)
let obj = JSON.parse(JSON.stringify(form)) const title = ref('新增菜单')
delete obj.id const open = (text: string, data: anyObj) => {
await add(obj) formRef.value?.resetFields()
} title.value = text
emits('init') // 重置表单
dialogVisible.value = false for (let key in form) {
} form[key] = ''
}
defineExpose({ open }) form.type = 1
</script> form.sort = 100
form.pid = data.pid
if (data.id) {
for (let key in form) {
form[key] = data[key] || ''
}
}
dialogVisible.value = true
}
const submit = async () => {
formRef.value.validate(async valid => {
if (valid) {
if (form.id) {
await update(form).then(() => {
ElMessage.success('修改成功!')
})
} else {
let obj = JSON.parse(JSON.stringify(form))
delete obj.id
await add(obj).then(() => {
ElMessage.success('新增成功!')
})
}
emits('init')
dialogVisible.value = false
}
})
}
defineExpose({ open })
</script>

Some files were not shown because too many files have changed in this diff Show More