42 Commits

Author SHA1 Message Date
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
195 changed files with 19379 additions and 13506 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,18 +1,20 @@
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' method: 'POST',
params
}) })
} }
// 监测点列表治理 // 监测点列表治理
@@ -31,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,5 +1,5 @@
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({
@@ -9,8 +9,6 @@ export function getDeviceDataTrend(data: any) {
}) })
} }
// 波形下载 // 波形下载
export function getFileZip(params: any) { export function getFileZip(params: any) {
return createAxios({ return createAxios({
@@ -27,5 +25,35 @@ export function exportModel(data: any) {
method: 'post', method: 'post',
data: data, data: data,
responseType: 'blob' 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

@@ -65,3 +65,35 @@ export function savePageIdWithUser(data: any) {
params: data 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

@@ -3,12 +3,12 @@ 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}`,
@@ -30,7 +30,14 @@ export function offlineDataUploadMakeUp(data: any) {
export function getListByIds() { export function getListByIds() {
return createAxios({ return createAxios({
url: '/cs-harmonic-boot/pqSensitiveUser/getListByIds', url: '/cs-harmonic-boot/pqSensitiveUser/getListByIds',
method: 'POST', 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,14 +258,42 @@ export function getSimpleLine() {
}) })
} }
export function getLineExport(data: any) {
export function getLineExport(data:any) {
return request({ return request({
url: '/cs-harmonic-boot/eventReport/getLineExport', url: '/cs-harmonic-boot/eventReport/getLineExport',
method: 'post', method: 'post',
data: data, data: data,
responseType: 'blob' 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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 213 KiB

View File

@@ -1,7 +1,14 @@
<template> <template>
<div> <div>
<!--F47曲线 --> <!--F47曲线 -->
<TableHeader ref="TableHeaderRef" :showReset="false" :timeKeyList="prop.timeKey" @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>
@@ -44,7 +51,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -109,8 +116,8 @@ const tableStore: any = new TableStore({
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曲线`
@@ -140,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
@@ -162,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: '%'
} }
], ],
@@ -202,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: '不可容忍事件',
@@ -449,7 +454,7 @@ const handleTolerableEventClick = async (row: any) => {
// 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
@@ -474,7 +479,7 @@ const handleTolerableEventClick = async (row: any) => {
}) })
nextTick(() => { nextTick(() => {
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666) 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

@@ -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: Array as () => string[] }, 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,27 +1,15 @@
<template> <template>
<div> <div>
<!--指标越限程度 --> <!--指标越限程度 -->
<TableHeader <TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" datePicker
ref="TableHeaderRef" :timeKeyList="prop.timeKey" v-if="fullscreen"></TableHeader>
:showReset="false" <my-echart class="tall" :options="echartList"
@selectChange="selectChange" :style="{ width: prop.width, height: `calc(${prop.height} / 2 )` }" />
datePicker <Table ref="tableRef" @cell-click="cellClickEvent"
:timeKeyList="prop.timeKey" :height="`calc(${prop.height} / 2 - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`" isGroup></Table>
v-if="fullscreen"
></TableHeader>
<my-echart
class="tall"
:options="echartList"
: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>
<!-- 指标日趋势图 --> <!-- 指标日趋势图 -->
<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">
@@ -31,15 +19,16 @@ 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 { getTime } from '@/utils/formatTime' import { getTime } from '@/utils/formatTime'
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -47,7 +36,7 @@ const prop = defineProps({
const TableHeaderRef = ref() const TableHeaderRef = ref()
const headerHeight = ref(57) 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) => {
@@ -117,40 +106,24 @@ const tableStore: any = new TableStore({
title: '越限程度(%)', title: '越限程度(%)',
field: 'extent', field: 'extent',
minWidth: '70', 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>`
}
} }
} }
], ],
@@ -159,23 +132,14 @@ const tableStore: any = new TableStore({
}, },
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: {
@@ -191,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
} }
] ]
@@ -204,14 +168,32 @@ 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, ""))
})
})
}
} }
} }
@@ -233,6 +215,14 @@ const setTime = () => {
console.warn('获取时间失败time 不是一个有效数组') console.warn('获取时间失败time 不是一个有效数组')
} }
} }
const dialogFlag = ref(false)
// 谐波弹窗关闭时的回调
const onHarmonicRatioClose = () => {
dialogFlag.value = false
// 重新打开指标越限详情弹窗
}
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
@@ -253,6 +243,6 @@ watch(
} }
) )
const addMenu = () => {} const addMenu = () => { }
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -3,9 +3,9 @@
<!--治理效果报表 --> <!--治理效果报表 -->
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" 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 filterable v-model="tableStore.table.params.tempId" placeholder="请选择报表模板" clearable> <el-select filterable v-model="tableStore.table.params.tempId" placeholder="请选择模板策略" clearable>
<el-option v-for="item in templateList" :key="item.id" :label="item.name" :value="item.id" /> <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="监测对象">
@@ -34,10 +34,10 @@ 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 { getTime } from '@/utils/formatTime' 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] },
@@ -71,8 +71,8 @@ const initListByIds = () => {
} }
const templateListData = () => { const templateListData = () => {
getTemplateList({}).then(res => { querySysExcel({}).then(res => {
templateList.value = res.data.filter(item => item.name === '稳态治理报表') 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
} }
@@ -118,12 +118,15 @@ const tableStore: any = new TableStore({
column: [], column: [],
beforeSearchFun: () => { beforeSearchFun: () => {
setTime() setTime()
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({

View File

@@ -34,7 +34,7 @@
</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 filterable
@@ -42,7 +42,7 @@
<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>
@@ -66,7 +66,7 @@
<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>
@@ -82,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>
@@ -160,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
}) })
@@ -205,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
@@ -287,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 = {
@@ -600,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

@@ -14,7 +14,7 @@
<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>
@@ -85,7 +85,7 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(%)', title: '长时闪变越限(%)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '90', width: '90',
render: 'customTemplate', render: 'customTemplate',
@@ -93,13 +93,14 @@ const tableStore: any = new TableStore({
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: '三相不平衡度越限(%)',
@@ -111,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: () => {
}, },
@@ -139,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

@@ -5,7 +5,8 @@
:showReset="false" :showReset="false"
ref="TableHeaderRef" ref="TableHeaderRef"
@selectChange="selectChange" @selectChange="selectChange"
datePicker :timeKeyList="prop.timeKey" datePicker
:timeKeyList="prop.timeKey"
v-if="fullscreen" v-if="fullscreen"
></TableHeader> ></TableHeader>
<my-echart <my-echart
@@ -41,7 +42,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -87,7 +88,7 @@ const initEcharts = () => {
xAxis: { xAxis: {
// name: '(区域)', // name: '(区域)',
data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡'] data: ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
}, },
yAxis: { yAxis: {
@@ -148,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',
@@ -197,6 +198,7 @@ const tableStore: any = new TableStore({
], ],
beforeSearchFun: () => { beforeSearchFun: () => {
setTime() setTime()
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)`
@@ -211,13 +213,13 @@ 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(() => {

View File

@@ -2,7 +2,14 @@
<div> <div>
<!--指标越限时间分布 <!--指标越限时间分布
--> -->
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" @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" filterable v-model="tableStore.table.params.lineId"> <el-select size="small" filterable v-model="tableStore.table.params.lineId">
@@ -19,10 +26,19 @@
<div v-loading="tableStore.table.loading"> <div v-loading="tableStore.table.loading">
<my-echart <my-echart
class="tall" class="tall"
v-if="lineShow"
:options="echartList1" :options="echartList1"
: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)`
}" }"
/> />
<!-- <my-echart <!-- <my-echart
@@ -49,7 +65,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -61,7 +77,7 @@ const lineList = ref()
const headerHeight = ref(57) const headerHeight = ref(57)
const TableHeaderRef = ref() const TableHeaderRef = ref()
const lineShow = ref(true)
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height headerHeight.value = height
@@ -92,6 +108,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()
@@ -115,7 +136,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)
@@ -146,6 +167,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: {
@@ -162,18 +186,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 隐藏右侧颜色条
@@ -211,7 +228,7 @@ const initProbabilityData = () => {
}, },
zAxis3D: { zAxis3D: {
type: 'value', type: 'value',
name: '越限数', name: '越限数',
nameLocation: 'middle', nameLocation: 'middle',
nameGap: 30, nameGap: 30,
minInterval: 10 minInterval: 10

View File

@@ -1,7 +1,14 @@
<template> <template>
<div> <div>
<!--指标越限概率分布 --> <!--指标越限概率分布 -->
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" @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" filterable v-model="tableStore.table.params.lineId"> <el-select size="small" filterable v-model="tableStore.table.params.lineId">
@@ -17,11 +24,20 @@
</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} - ${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)`
}" }"
/> />
<!-- <my-echart <!-- <my-echart
@@ -48,11 +64,11 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } 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()
@@ -91,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()
@@ -114,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)
@@ -145,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: {
@@ -161,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 隐藏右侧颜色条
@@ -210,7 +227,7 @@ const initProbabilityData = () => {
}, },
zAxis3D: { zAxis3D: {
type: 'value', type: 'value',
name: '越限数', name: '越限数',
nameLocation: 'middle', nameLocation: 'middle',
nameGap: 30, nameGap: 30,
minInterval: 10 minInterval: 10

View File

@@ -66,7 +66,7 @@
<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>
@@ -81,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>
@@ -160,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
}) })
@@ -294,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 = {
@@ -579,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

@@ -14,7 +14,7 @@
<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>
@@ -24,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">
@@ -90,21 +86,21 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(分钟)', title: '越限(分钟)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '90', 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: '三相不平衡度越限(分钟)',
@@ -116,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: () => {
}, },
@@ -144,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(() => {
@@ -180,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" :timeKeyList="prop.timeKey" @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>
@@ -22,7 +29,7 @@ import { ref, onMounted, provide, reactive, watch, nextTick } 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 { getTime } from '@/utils/formatTime' 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'
@@ -32,7 +39,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -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,18 +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: () => {
setTime() setTime()
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.height = `calc(${prop.height} - 80px)` tableStore.table.height = `calc(${prop.height} - 80px)`
@@ -119,31 +132,34 @@ 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 setTime = () => {
const time = getTime( // const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0, // (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey, // prop.timeKey,
fullscreen.value // fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime] // ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue // : prop.timeValue
) // )
if (Array.isArray(time)) { // if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0] // tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1] // tableStore.table.params.searchEndTime = time[1]
// TableHeaderRef.value?.setInterval(time[2] - 0) // // TableHeaderRef.value?.setInterval(time[2] - 0)
// TableHeaderRef.value?.setTimeInterval([time[0], time[1]]) // // TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else { // } else {
console.warn('获取时间失败time 不是一个有效数组') // console.warn('获取时间失败time 不是一个有效数组')
} // }
} }
// 在组件挂载时设置缓存值到 DatePicker // 在组件挂载时设置缓存值到 DatePicker

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

@@ -5,7 +5,7 @@
datePicker datePicker
@selectChange="selectChange" @selectChange="selectChange"
v-if="fullscreen" v-if="fullscreen"
ref="TableHeaderRef" ref="TableHeaderRef"
:timeKeyList="prop.timeKey" :timeKeyList="prop.timeKey"
> >
<template v-slot:select> <template v-slot:select>
@@ -75,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>
@@ -100,7 +109,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -114,7 +123,7 @@ const lineList: any = ref()
const powerList: any = ref() const powerList: 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)
@@ -141,8 +150,14 @@ const indicatorList = 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
initCode()
}) })
} }
@@ -171,7 +186,7 @@ const setEchart = () => {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
formatter: function (params: any) { formatter: function (params: any) {
let result = params[0].name let result = params[0].axisValueLabel
params.forEach((item: any) => { params.forEach((item: any) => {
if (item.seriesName === indicatorName) { if (item.seriesName === indicatorName) {
// 对于电能质量指标格式化Y轴值显示 // 对于电能质量指标格式化Y轴值显示
@@ -186,7 +201,7 @@ const setEchart = () => {
result += `<br/>${item.marker}${item.seriesName}: ${valueText}` result += `<br/>${item.marker}${item.seriesName}: ${valueText}`
} else { } else {
// 对于功率数据,正常显示数值 // 对于功率数据,正常显示数值
result += `<br/>${item.marker}${item.seriesName}: ${item.value[1]}` result += `<br/>${item.marker}${item.seriesName}: ${item.value[1]} ${item.value[2]}`
} }
}) })
return result return result
@@ -277,7 +292,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
] ]
}) })
@@ -287,7 +303,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
] ]
}) })
@@ -432,10 +449,8 @@ watch(
} }
) )
onMounted(() => { onMounted(async () => {
initLineList().then(() => { await initLineList()
initCode()
})
}) })
watch( watch(

View File

@@ -14,7 +14,7 @@
<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>
@@ -34,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)
@@ -85,7 +85,7 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(%)', title: '长时闪变越限(%)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '90', width: '90',
render: 'customTemplate', render: 'customTemplate',
@@ -93,13 +93,14 @@ const tableStore: any = new TableStore({
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: '三相不平衡度越限(%)',
@@ -111,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: () => {
}, },
@@ -139,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
@@ -175,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

@@ -5,9 +5,22 @@
ref="TableHeaderRef" ref="TableHeaderRef"
:showReset="false" :showReset="false"
@selectChange="selectChange" @selectChange="selectChange"
datePicker v-if="fullscreen"
v-if="fullscreen" :timeKeyList="prop.timeKey" :timeKeyList="prop.timeKey"
></TableHeader> >
<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"
@@ -17,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>
@@ -59,7 +80,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -77,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]
} // }
} }
// 计算是否全屏展示 // 计算是否全屏展示
@@ -120,31 +141,76 @@ const tableStore: any = new TableStore({
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: '80' }, {
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: '90', 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: '治理对象', title: '治理对象',
field: 'sensitiveUser', field: 'sensitiveUser',
minWidth: '90' minWidth: '90',
formatter: (row: any) => {
return row.cellValue || '/'
}
}, },
{ {
title: '电压等级', title: '电压等级',
field: 'volGrade', field: 'volGrade',
minWidth: '80' minWidth: '80',
formatter: (row: any) => {
return row.cellValue == 0 ? '/' : row.cellValue + 'kV' || '/'
}
}, },
{ {
title: '是否治理', title: '是否治理',
field: 'govern', field: 'govern',
minWidth: '80' minWidth: '80',
formatter: (row: any) => {
return row.cellValue || '/'
}
}, },
{ {
@@ -160,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: 80, fixed: 'right',
// fixed: 'right', width: 150,
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -187,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
@@ -220,25 +307,25 @@ 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 setTime = () => {
const time = getTime( // const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0, // (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey, // prop.timeKey,
fullscreen.value // fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime] // ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue // : prop.timeValue
) // )
// if (Array.isArray(time)) {
if (Array.isArray(time)) { // tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchBeginTime = time[0] // tableStore.table.params.searchEndTime = time[1]
tableStore.table.params.searchEndTime = time[1] // TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setInterval(time[2] - 0) // TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
TableHeaderRef.value?.setTimeInterval([time[0], time[1]]) // } else {
} else { // console.warn('获取时间失败time 不是一个有效数组')
console.warn('获取时间失败time 不是一个有效数组') // }
}
} }
// 点击行 // 点击行
@@ -253,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) => {
@@ -279,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] // 只保留最新选择的文件
} }
// 处理上传前检查 // 处理上传前检查
@@ -303,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
} }
@@ -315,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 {

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"
@@ -66,7 +66,7 @@
<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>
@@ -82,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>
@@ -111,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)
@@ -160,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
}) })
@@ -205,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
@@ -260,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) => {
@@ -294,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 = {
@@ -399,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 = ''
@@ -600,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 = '谐波电压次数'
} }
}) })
} }
@@ -643,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

@@ -14,7 +14,7 @@
<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>
@@ -85,7 +85,7 @@ const tableStore: any = new TableStore({
width: '150' width: '150'
}, },
{ {
title: '闪变越限(%)', title: '长时闪变越限(%)',
field: 'flickerOvertime', field: 'flickerOvertime',
width: '90', width: '90',
render: 'customTemplate', render: 'customTemplate',
@@ -93,13 +93,14 @@ const tableStore: any = new TableStore({
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: '三相不平衡度越限(%)',
@@ -111,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: () => {
}, },
@@ -139,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

@@ -89,7 +89,7 @@ const initEcharts = () => {
xAxis: { xAxis: {
// name: '(区域)', // name: '(区域)',
data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡'] data: ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
}, },
yAxis: { yAxis: {
@@ -150,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',
@@ -217,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
) )
} }
} }

View File

@@ -5,9 +5,22 @@
ref="TableHeaderRef" ref="TableHeaderRef"
:showReset="false" :showReset="false"
@selectChange="selectChange" @selectChange="selectChange"
datePicker v-if="fullscreen"
v-if="fullscreen" :timeKeyList="prop.timeKey" :timeKeyList="prop.timeKey"
></TableHeader> >
<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"
@@ -29,7 +42,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })
@@ -44,11 +57,11 @@ 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]
} // }
} }
// 计算是否全屏展示 // 计算是否全屏展示
@@ -65,7 +78,7 @@ 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: [
@@ -94,12 +107,18 @@ const tableStore: any = new TableStore({
{ {
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: () => {
@@ -108,7 +127,7 @@ const tableStore: any = new TableStore({
loadCallback: () => {} loadCallback: () => {}
}) })
tableStore.table.params.searchValue = ''
const tableRef = ref() const tableRef = ref()
provide('tableRef', tableRef) provide('tableRef', tableRef)
@@ -123,22 +142,21 @@ const cellClickEvent = ({ row, column }: any) => {
} }
const setTime = () => { const setTime = () => {
const time = getTime( // const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0, // (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey, // prop.timeKey,
fullscreen.value // fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime] // ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue // : prop.timeValue
) // )
// if (Array.isArray(time)) {
if (Array.isArray(time)) { // tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchBeginTime = time[0] // tableStore.table.params.searchEndTime = time[1]
tableStore.table.params.searchEndTime = time[1] // TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setInterval(time[2] - 0) // TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
TableHeaderRef.value?.setTimeInterval([time[0], time[1]]) // } else {
} else { // console.warn('获取时间失败time 不是一个有效数组')
console.warn('获取时间失败time 不是一个有效数组') // }
}
} }
onMounted(() => { onMounted(() => {

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
@@ -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"
:timeKeyList="prop.timeKey" :timeKeyList="prop.timeKey"
></TableHeader> ></TableHeader>
<el-calendar <el-calendar
v-model="value" v-model="value"
@@ -58,7 +58,7 @@
</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">
@@ -74,7 +74,7 @@ const prop = defineProps({
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: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number }
}) })

View File

@@ -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>
@@ -62,7 +77,11 @@ 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.2).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
@@ -166,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
} }
@@ -200,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

@@ -81,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">
@@ -92,7 +93,8 @@ import { useConfig } from '@/stores/config'
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 { 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] },
@@ -127,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()
} }
}) })
} }
@@ -179,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'])
}) })
// 处理治理后数据 // 处理治理后数据
@@ -188,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'])
}) })
// 构建系列数据 // 构建系列数据
@@ -259,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]
@@ -288,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',
@@ -316,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',
@@ -368,8 +413,8 @@ const tableStore: any = new TableStore({
} }
}) })
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)
@@ -405,7 +450,7 @@ const shouldShowHarmonicCount = () => {
return ( return (
currentIndicator && currentIndicator &&
(currentIndicator.name.includes('电压谐波含有率') || currentIndicator.name.includes('电流谐波含有率')) (currentIndicator.name.includes('幅值') || currentIndicator.name.includes('含有率'))
) )
} }
@@ -414,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 '电流'
} }
} }

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

@@ -266,8 +266,6 @@ self.onmessage = function (e) {
} else if (boxoList.systemType == 'ZL') { } else if (boxoList.systemType == 'ZL') {
titles = titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 项目名称:' +
boxoList.engineeringName +
' 监测点名称:' + ' 监测点名称:' +
boxoList.equipmentName + boxoList.equipmentName +
' 发生时刻:' + ' 发生时刻:' +
@@ -280,8 +278,6 @@ self.onmessage = function (e) {
} else if (boxoList.systemType == 'YPT') { } else if (boxoList.systemType == 'YPT') {
titles = titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 项目名称:' +
boxoList.engineeringName +
' 监测点名称:' + ' 监测点名称:' +
boxoList.lineName + boxoList.lineName +
' 发生时刻:' + ' 发生时刻:' +
@@ -293,15 +289,15 @@ self.onmessage = function (e) {
'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>
@@ -594,10 +594,10 @@ 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} 暂降(骤升)幅值:${(
@@ -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: [
{ {
@@ -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

@@ -127,13 +127,13 @@ self.addEventListener('message', function (e) {
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') {
@@ -141,11 +141,11 @@ self.addEventListener('message', function (e) {
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) + (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') {

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>
@@ -327,11 +327,11 @@ 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} 暂降(骤升)幅值:${(
@@ -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

@@ -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"
@@ -29,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"
> >

View File

@@ -1,6 +1,6 @@
<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">
<div class="mt15 mr10" style="display: flex; justify-content: end"> <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> <el-button type="primary" icon="el-icon-Select" @click="save" :loading="loading">保存</el-button>
</div> </div>
<Icon <Icon
@@ -14,7 +14,7 @@
/> />
<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>
@@ -39,7 +39,7 @@
</div> </div>
<el-tree <el-tree
:style="{ height: 'calc(100vh - 235px)' }" :style="{ height: `calc(100vh - ${height}px)` }"
style="overflow: auto" style="overflow: auto"
ref="treeRef" ref="treeRef"
:props="defaultProps" :props="defaultProps"
@@ -69,9 +69,9 @@
<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({
@@ -82,12 +82,16 @@ interface Props {
width?: string width?: string
canExpand?: boolean canExpand?: boolean
showPush?: boolean showPush?: boolean
showBut?: boolean
height?: number
} }
const loading = ref(false) const loading = ref(false)
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
width: '280px', width: '280px',
canExpand: true, canExpand: true,
showPush: false showPush: false,
showBut: true,
height: 267
}) })
const config = useConfig() const config = useConfig()
const { proxy } = useCurrentInstance() const { proxy } = useCurrentInstance()

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>

View File

@@ -13,18 +13,29 @@
<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 <el-input
maxlength="32" maxlength="32"
show-word-limit
v-model.trim="filterText" v-model.trim="filterText"
autocomplete="off" autocomplete="off"
placeholder="请输入内容" placeholder="请输入内容"
clearable 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 <Icon
@click="onMenuCollapse" @click="onMenuCollapse"
@@ -42,6 +53,8 @@
v-model.trim="activeName" v-model.trim="activeName"
style="flex: 1; height: 100%" style="flex: 1; height: 100%"
@change="changeDevice" @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">
@@ -52,7 +65,7 @@
<el-tree <el-tree
:style="{ :style="{
height: height:
bxsDeviceData.length != 0 treeType.length != 0
? `calc(100vh - 380px - ${props.height}px)` ? `calc(100vh - 380px - ${props.height}px)`
: 'calc(100vh - 278px)' : 'calc(100vh - 278px)'
}" }"
@@ -110,7 +123,7 @@
</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="{ :style="{
height: height:
@@ -142,6 +155,32 @@
</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>
@@ -154,13 +193,14 @@ 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 height?: number
engineering: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -168,8 +208,20 @@ const props = withDefaults(defineProps<Props>(), {
canExpand: true, canExpand: true,
type: '', type: '',
data: [], data: [],
height: 0 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')
@@ -202,7 +254,7 @@ watch(
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) => {
@@ -219,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)
@@ -300,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
@@ -331,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)
} }
} }
//治理 //治理
@@ -355,27 +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, treeRef3 }) 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.length === 0 && bxsDeviceData.value.length === 0) {
activeName.value = '2'
}
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>
@@ -405,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,13 +1,21 @@
<template> <template>
<Tree ref="treRef" :width="width" :showPush="props.showPush" :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 {
@@ -22,143 +30,68 @@ 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 和 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) 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

@@ -15,7 +15,7 @@ import { ref, nextTick, onMounted, defineProps } from 'vue'
import Tree from '../index.vue' import Tree from '../index.vue'
import { getLineTree, objTree } from '@/api/cs-device-boot/csLedger' import { getLineTree, objTree } 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 {
@@ -85,7 +85,7 @@ 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,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

@@ -7,14 +7,18 @@
:data="tree" :data="tree"
:height="props.height" :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'
}) })
@@ -23,11 +27,13 @@ const props = withDefaults(
showCheckbox?: boolean showCheckbox?: boolean
defaultCheckedKeys?: any defaultCheckedKeys?: any
height?: number height?: number
engineering?: boolean
}>(), }>(),
{ {
showCheckbox: false, showCheckbox: false,
defaultCheckedKeys: [], defaultCheckedKeys: [],
height:0 height: 0,
engineering: false
} }
) )
const emit = defineEmits(['init', 'checkChange', 'deviceTypeChange']) const emit = defineEmits(['init', 'checkChange', 'deviceTypeChange'])
@@ -35,114 +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) => { item.children.forEach((item: any) => {
item2.icon = 'el-icon-List' item.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary') item.color = config.getColorVal('elementUiPrimary')
item2.children.forEach((item3: any) => { item.children.forEach((item2: any) => {
item3.icon = 'el-icon-Platform' item2.icon = 'el-icon-Platform'
item3.level = 2 item2.color = config.getColorVal('elementUiPrimary')
item3.color = config.getColorVal('elementUiPrimary') item2.color =
if (item3.comFlag === 1) { item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item3.color = '#e26257 !important' arr4.push(item2)
}
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.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.icon = 'el-icon-Platform'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
arr3.push(item3)
})
})
})
}
})
console.log('🚀 ~ file: deviceTree.vue ~ line 18 ~ getDeviceTree ~ tree:', arr, arr2, arr3)
tree.value = res.data
nextTick(() => {
setTimeout(() => {
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 { } else {
emit('init') if (item.name == '治理设备') {
return 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.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'
// item.disabled =true
item.pName = '便携式设备'
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.pName = '便携式设备'
// 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.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)
})
})
})
}
} }
}, 500) })
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,
@@ -150,4 +210,7 @@ const handleCheckChange = (data: any, checked: any, indeterminate: any) => {
indeterminate indeterminate
}) })
} }
defineExpose({
treRef
})
</script> </script>

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,80 +34,128 @@ 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(() => {
setTimeout(() => { setTimeout(() => {
if (arr1.length > 0) { if (type == '2') {
//初始化选中
treRef.value?.treeRef4.setCurrentKey(arr4[0].id)
// 注册父组件事件
emit('init', {
level: 3,
...arr4[0]
})
changePointType('4', arr4[0])
return
} else if (arr1.length > 0) {
//初始化选中 //初始化选中
treRef.value?.treeRef1.setCurrentKey(arr1[0].id) treRef.value?.treeRef1.setCurrentKey(arr1[0].id)
// 注册父组件事件 // 注册父组件事件
@@ -119,7 +174,6 @@ const info = () => {
}) })
return return
} else if (arr3.length > 0) { } else if (arr3.length > 0) {
treRef.value?.treeRef3?.setCurrentKey(arr3[0].id) treRef.value?.treeRef3?.setCurrentKey(arr3[0].id)
emit('init', { emit('init', {
level: 2, level: 2,
@@ -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

@@ -3,13 +3,7 @@
<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 <el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
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>
@@ -37,7 +31,7 @@
:style="{ color: data.color }" :style="{ color: data.color }"
v-if="data.icon" v-if="data.icon"
/> />
<span style="margin-left: 5px;">{{ node.label }}</span> <span style="margin-left: 5px">{{ node.label }}</span>
</div> </div>
</span> </span>
</template> </template>
@@ -53,7 +47,7 @@ import { getSchemeTree, getTestRecordInfo } from '@/api/cs-device-boot/planData'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import useCurrentInstance from '@/utils/useCurrentInstance' import useCurrentInstance from '@/utils/useCurrentInstance'
import { ElTree } from 'element-plus' import { ElTree } from 'element-plus'
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
defineOptions({ defineOptions({
name: 'govern/schemeTree' name: 'govern/schemeTree'
@@ -157,7 +151,8 @@ const clickNode = (e: anyObj) => {
} }
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)
getTreeList() getTreeList()

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) {
//初始化选中 //初始化选中

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" v-if="props.showPush"> <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'" v-else <!-- <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 - 190px)' }" <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({
@@ -74,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)
}) })
@@ -83,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
} }
@@ -132,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

@@ -13,11 +13,27 @@
/> />
<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 <Icon
@click="onMenuCollapse" @click="onMenuCollapse"
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
@@ -34,6 +50,8 @@
v-model.trim="activeName" v-model.trim="activeName"
style="flex: 1; height: 100%" style="flex: 1; height: 100%"
@change="changeDevice" @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">
@@ -43,7 +61,7 @@
</el-select> </el-select>
<el-tree <el-tree
:style="{ height: bxsDeviceData.length != 0 ? 'calc(100vh - 380px)' : 'calc(100vh - 278px)' }" :style="{ height: treeType.length != 0 ? 'calc(100vh - 380px)' : 'calc(100vh - 278px)' }"
ref="treeRef1" ref="treeRef1"
:props="defaultProps" :props="defaultProps"
highlight-current highlight-current
@@ -93,7 +111,7 @@
</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 - 340px)' : 'calc(100vh - 238px)' }" :style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)' }"
ref="treeRef3" ref="treeRef3"
@@ -120,6 +138,33 @@
</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>
@@ -133,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
@@ -157,12 +202,23 @@ 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,
@@ -180,7 +236,7 @@ watch(
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)
@@ -196,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)
@@ -205,7 +263,7 @@ watch(filterText, val => {
} }
}) })
watch(process, val => { watch(process, val => {
if (val == '' || val == undefined) { if (val == '' || val == undefined) {
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)))
@@ -217,8 +275,6 @@ watch(process, val => {
}) })
const changeDevice = (val: any) => { const changeDevice = (val: any) => {
console.log('🚀 ~ changeDevice ~ val:', val)
let arr1: any = [] let arr1: any = []
//zlDeviceData //zlDeviceData
zlDevList.value.forEach((item: any) => { zlDevList.value.forEach((item: any) => {
@@ -251,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)
@@ -293,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 {
@@ -375,27 +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, treeRef3 }) // 工程
const treeRef4 = ref<InstanceType<typeof ElTree>>()
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
onMounted(() => { onMounted(() => {
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.length === 0 && bxsDeviceData.value.length === 0) {
activeName.value = '2'
}
if (!zlDeviceData.value && !bxsDeviceData.value) {
activeName.value = '2'
}
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>
@@ -426,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

@@ -214,8 +214,8 @@ onMounted(() => {
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
margin-right: var(--ba-main-space); margin-right: var(--ba-main-space);
scrollbar-width: none; // scrollbar-width: none;
// overflow-x: auto;
&::-webkit-scrollbar { &::-webkit-scrollbar {
height: 5px; height: 5px;
} }

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

@@ -25,7 +25,6 @@ export const useNavTabs = defineStore(
}) })
function addTab(route: RouteLocationNormalized) { function addTab(route: RouteLocationNormalized) {
console.log('🚀 ~ addTab ~ route:', route)
if (!route.meta.addtab) return if (!route.meta.addtab) return
for (const key in state.tabsView) { for (const key in state.tabsView) {
if (state.tabsView[key].path === route.path) { if (state.tabsView[key].path === route.path) {
@@ -69,7 +68,7 @@ export const useNavTabs = defineStore(
} }
const setTabsViewRoutes = (data: RouteRecordRaw[]): void => { const setTabsViewRoutes = (data: RouteRecordRaw[]): void => {
state.tabsViewRoutes = encodeRoutesURI(data) state.tabsViewRoutes = encodeRoutesURI(JSON.parse(JSON.stringify(data)))
} }
const setAuthNode = (key: string, data: string[]) => { const setAuthNode = (key: string, data: string[]) => {
@@ -87,9 +86,9 @@ export const useNavTabs = defineStore(
const refresh = () => { const refresh = () => {
// setTimeout(() => { // setTimeout(() => {
// console.log(123, state.tabsViewRoutes) // console.log(123, state.tabsViewRoutes)
let list = matchAndReturnRouteData(state.tabsViewRoutes, state.tabsView) let list = matchAndReturnRouteData(state.tabsViewRoutes, state.tabsView)
state.tabsView = [] state.tabsView = []
list.forEach(item => { list.forEach(item => {
addTab(item) addTab(item)
}) })

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

@@ -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,14 +65,18 @@ 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/csevent/getEventCoords' ||
config.url == '/cs-harmonic-boot/limitRateDetailD/limitTimeProbabilityData'|| config.url == '/cs-harmonic-boot/limitRateDetailD/limitTimeProbabilityData' ||
config.url == '/cs-harmonic-boot/limitRateDetailD/limitProbabilityData'|| config.url == '/cs-harmonic-boot/limitRateDetailD/limitProbabilityData' ||
config.url == '/system-boot/dictTree/queryByCode'|| config.url == '/system-boot/dictTree/queryByCode' ||
config.url == '/system-boot/dictTree/queryByid' ||
config.url == '/system-boot/dictTree/query' config.url == '/system-boot/dictTree/query'
) )
) )
@@ -111,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'
@@ -150,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)
@@ -161,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) // 可根据实际情况调整延迟时间
@@ -179,8 +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 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>

View File

@@ -1,29 +1,43 @@
<template> <template>
<el-dialog class="cn-operate-dialog" width="700px" v-model.trim="dialogVisible" :title="title"> <el-dialog class="cn-operate-dialog" width="700px" v-model.trim="dialogVisible" :title="title">
<el-scrollbar> <el-scrollbar>
<el-form :inline="false" :model="form" label-width="auto" class="form-one"> <el-form :inline="false" :model="form" label-width="auto" ref="formRef" class="form-one" :rules="rules">
<el-form-item label="上级菜单"> <el-form-item label="上级菜单">
<el-cascader v-model.trim="form.pid" :options="tableStore.table.data" :props="cascaderProps" clearable <el-cascader
style="width: 100%" /> v-model.trim="form.pid"
:options="tableStore.table.data"
:props="cascaderProps"
clearable
style="width: 100%"
/>
</el-form-item> </el-form-item>
<el-form-item label="菜单名称"> <el-form-item label="菜单名称" prop="name">
<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="图标">
<IconSelector v-model.trim="form.icon" placeholder="请选择图标" /> <IconSelector v-model.trim="form.icon" placeholder="请选择图标" />
</el-form-item> </el-form-item>
<el-form-item label="菜单路由"> <el-form-item label="菜单路由" prop="path">
<el-input v-model.trim="form.path" placeholder="请输入菜单名称" /> <el-input v-model.trim="form.path" placeholder="请输入菜单名称" />
</el-form-item> </el-form-item>
<el-form-item label="组件路径"> <el-form-item label="组件路径" prop="routeName">
<el-input v-model.trim="form.routeName" placeholder="请输入组件路径,如/src/views/dashboard/index.vue" /> <el-input
v-model.trim="form.routeName"
placeholder="请输入组件路径,如/src/views/dashboard/index.vue"
/>
</el-form-item> </el-form-item>
<el-form-item label="排序"> <el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.trim="form.sort" :min="0" /> <el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
</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
placeholder="请输入描述" /> maxlength="300"
show-word-limit
v-model.trim="form.remark"
:rows="2"
type="textarea"
placeholder="请输入描述"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
@@ -42,10 +56,11 @@ import { reactive } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import IconSelector from '@/components/baInput/components/iconSelector.vue' import IconSelector from '@/components/baInput/components/iconSelector.vue'
import { updateMenu, addMenu } from '@/api/systerm' import { updateMenu, addMenu } from '@/api/systerm'
import { ElMessage } from 'element-plus'
defineOptions({ defineOptions({
name: 'auth/menu/popupMenu' name: 'auth/menu/popupMenu'
}) })
const formRef = ref()
const emits = defineEmits<{ const emits = defineEmits<{
(e: 'init'): void (e: 'init'): void
}>() }>()
@@ -71,7 +86,15 @@ const form: any = reactive({
const dialogVisible = ref(false) const dialogVisible = ref(false)
const title = ref('新增菜单') const title = ref('新增菜单')
const rules = {
name: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }],
path: [{ required: true, message: '请输入菜单路由', trigger: 'blur' }],
icon: [{ required: true, message: '请选择图标', trigger: 'blur' }],
routeName: [{ required: true, message: '请输入组件路径', trigger: 'blur' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
}
const open = (text: string, data: anyObj) => { const open = (text: string, data: anyObj) => {
formRef.value?.resetFields()
title.value = text title.value = text
// 重置表单 // 重置表单
for (let key in form) { for (let key in form) {
@@ -91,17 +114,27 @@ const open = (text: string, data: anyObj) => {
dialogVisible.value = true dialogVisible.value = true
} }
const submit = async () => { const submit = async () => {
if (form.id) { formRef.value.validate(async valid => {
form.pid = form.pid||'0' if (valid) {
await updateMenu(form) if (form.id) {
} else { form.pid = form.pid || '0'
form.code = 'menu' await updateMenu(form).then(() => {
let obj = JSON.parse(JSON.stringify(form)) ElMessage.success('编辑成功!')
delete obj.id })
await addMenu(obj) } else {
} form.code = 'menu'
emits('init') form.pid = form.pid || '0'
dialogVisible.value = false let obj = JSON.parse(JSON.stringify(form))
delete obj.id
await addMenu(obj).then(() => {
ElMessage.success('新增成功!')
})
}
emits('init')
dialogVisible.value = false
}
})
} }
defineExpose({ open }) defineExpose({ open })

View File

@@ -7,16 +7,34 @@
</div> </div>
<Table ref="tableRef" :row-config="{ isCurrent: true, isHover: true }" @currentChange="currentChange" /> <Table ref="tableRef" :row-config="{ isCurrent: true, isHover: true }" @currentChange="currentChange" />
</div> </div>
<Tree <div>
v-if="menuListId" <el-tabs type="border-card">
ref="treeRef" <el-tab-pane label="菜单">
show-checkbox <Tree
width="350px" v-if="menuListId"
:data="menuTree" ref="treeRef"
:checkStrictly="false" show-checkbox
@checkChange="checkChange" width="350px"
></Tree> :data="menuTree"
<el-empty style="width: 350px; padding-top: 300px; box-sizing: border-box" description="请选择角色" v-else /> :checkStrictly="checkStrictly"
@checkChange="checkChange"
></Tree>
<el-empty
style="width: 350px; padding-top: 300px; box-sizing: border-box"
description="请选择角色"
v-else
/>
</el-tab-pane>
<el-tab-pane label="系统">
<div class="mt10 mr10" style="display: flex; justify-content: end">
<el-button type="primary" icon="el-icon-Select" @click="saveSystem">保存</el-button>
</div>
<el-checkbox-group v-model="systemIds" class="md10 system">
<el-checkbox v-for="item in systemList" :label="item.name" :value="item.id" :key="item.id" />
</el-checkbox-group>
</el-tab-pane>
</el-tabs>
</div>
<PopupForm ref="popupRef"></PopupForm> <PopupForm ref="popupRef"></PopupForm>
</div> </div>
</template> </template>
@@ -28,17 +46,20 @@ import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import Tree from '@/components/tree/allocation.vue' import Tree from '@/components/tree/allocation.vue'
import { functionTree } from '@/api/user-boot/function' import { functionTree } from '@/api/user-boot/function'
import { getFunctionsByRoleIndex, updateRoleMenu } from '@/api/user-boot/roleFuction' import { getFunctionsByRoleIndex, updateRoleMenu, getSystemByRoleId, systemAdd } from '@/api/user-boot/roleFuction'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { del } from '@/api/user-boot/role' import { del } from '@/api/user-boot/role'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import PopupForm from './popupForm.vue' import PopupForm from './popupForm.vue'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { useDictData } from '@/stores/dictData'
const dictData = useDictData()
const systemList = dictData.getBasicData('System_Type')
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
defineOptions({ defineOptions({
name: 'auth/role' name: 'auth/role'
}) })
const systemIds = ref([])
const height = mainHeight(20).height const height = mainHeight(20).height
const treeRef = ref() const treeRef = ref()
const menuTree = ref<treeData[]>([]) const menuTree = ref<treeData[]>([])
@@ -69,7 +90,7 @@ const tableStore = new TableStore({
} }
}, },
{ {
title: '操作', title: '操作', fixed: 'right',
align: 'center', align: 'center',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
@@ -141,15 +162,21 @@ const currentChange = (data: any) => {
menuListId.value = data.row.id menuListId.value = data.row.id
getFunctionsByRoleIndex({ id: data.row.id }).then((res: any) => { getFunctionsByRoleIndex({ id: data.row.id }).then((res: any) => {
treeRef.value.treeRef.setCheckedKeys(res.data.map((item: any) => item.id)) treeRef.value.treeRef.setCheckedKeys(res.data.map((item: any) => item.id))
setTimeout(() => {
checkStrictly.value = false
}, 100)
})
getSystemByRoleId({ id: data.row.id }).then((res: any) => {
systemIds.value = res.data.systemIds || []
}) })
} }
const timeout = ref<NodeJS.Timeout>() const timeout = ref<NodeJS.Timeout>()
const checkChange = (data: any) => { const checkChange = (data: any) => {
// if (checkStrictly.value) { if (checkStrictly.value) {
// checkStrictly.value = false checkStrictly.value = false
// return return
// } }
updateRoleMenu({ updateRoleMenu({
id: menuListId.value, id: menuListId.value,
@@ -163,6 +190,19 @@ const checkChange = (data: any) => {
treeRef.value.loading = false treeRef.value.loading = false
}) })
} }
const saveSystem = () => {
systemAdd({
roleId: menuListId.value,
systemIds: systemIds.value
})
.then(() => {
ElMessage.success('操作成功!')
treeRef.value.loading = false
})
.catch(() => {
treeRef.value.loading = false
})
}
onMounted(() => { onMounted(() => {
tableStore.index() tableStore.index()
}) })
@@ -170,3 +210,17 @@ const addRole = () => {
popupRef.value.open('新增角色') popupRef.value.open('新增角色')
} }
</script> </script>
<style lang="scss" scoped>
:deep(.el-tabs--border-card > .el-tabs__content) {
padding: 0px !important;
}
.system {
width: 330px;
height: calc(100vh - 225px);
border: 1px solid var(--el-border-color);
padding: 5px 10px;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -1,74 +1,86 @@
<template> <template>
<el-dialog class="cn-operate-dialog" width="700px" v-model.trim="dialogVisible" :title="title"> <el-dialog class="cn-operate-dialog" width="500px" v-model.trim="dialogVisible" :title="title">
<el-form :inline="false" :model="form" label-width="auto" class="form-one" :rules="rules"> <el-form :inline="false" ref="formRef" :model="form" label-width="auto" class="form-one" :rules="rules">
<el-form-item label="角色名称"> <el-form-item label="角色名称" prop="name">
<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="角色编码" prop="code">
<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
placeholder="请输入描述" /> maxlength="300"
</el-form-item> show-word-limit
</el-form> v-model.trim="form.remark"
:rows="2"
<template #footer> type="textarea"
<span class="dialog-footer"> placeholder="请输入描述"
<el-button @click="dialogVisible = false">取消</el-button> />
<el-button type="primary" @click="submit">确认</el-button> </el-form-item>
</span> </el-form>
</template>
</el-dialog> <template #footer>
</template> <span class="dialog-footer">
<script lang="ts" setup> <el-button @click="dialogVisible = false">取消</el-button>
import { ref, inject } from 'vue' <el-button type="primary" @click="submit">确认</el-button>
import { reactive } from 'vue' </span>
import TableStore from '@/utils/tableStore' </template>
import { ElMessage } from 'element-plus' </el-dialog>
import { add, update } from '@/api/user-boot/role' </template>
import { useAdminInfo } from '@/stores/adminInfo' <script lang="ts" setup>
import { ref, inject } from 'vue'
const adminInfo = useAdminInfo() import { reactive } from 'vue'
const tableStore = inject('tableStore') as TableStore import TableStore from '@/utils/tableStore'
// do not use same name with ref import { ElMessage } from 'element-plus'
const form = reactive({ import { add, update } from '@/api/user-boot/role'
code: '', import { useAdminInfo } from '@/stores/adminInfo'
name: '',
remark: '', const adminInfo = useAdminInfo()
id: '', const tableStore = inject('tableStore') as TableStore
type: 0 // do not use same name with ref
}) const form = reactive({
const rules = { code: '',
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }], name: '',
code: [{ required: true, message: '角色编码不能为空', trigger: 'blur' }] remark: '',
} id: '',
const dialogVisible = ref(false) type: 0
const title = ref('新增菜单') })
const open = (text: string, data?: anyObj) => { const rules = {
title.value = text name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
dialogVisible.value = true code: [{ required: true, message: '请输入角色编码', trigger: 'blur' }]
if (data) { }
for (let key in form) { const dialogVisible = ref(false)
form[key] = data[key] const title = ref('新增菜单')
} const formRef = ref()
} else { const open = (text: string, data?: anyObj) => {
for (let key in form) { formRef.value?.resetFields()
form[key] = '' title.value = text
} dialogVisible.value = true
} if (data) {
} for (let key in form) {
const submit = async () => { form[key] = data[key]
if (form.id) { }
await update(form) } else {
} else { for (let key in form) {
form.type = adminInfo.$state.userType + 1 form[key] = ''
await add(form) }
} }
ElMessage.success('保存成功') }
tableStore.index() const submit = async () => {
dialogVisible.value = false formRef.value.validate(async valid => {
} if (valid) {
if (form.id) {
defineExpose({ open }) await update(form)
</script> } else {
form.type = adminInfo.$state.userType + 1
await add(form)
}
ElMessage.success('保存成功')
tableStore.index()
dialogVisible.value = false
}
})
}
defineExpose({ open })
</script>

View File

@@ -79,10 +79,10 @@ const tableStore = new TableStore({
} }
}, },
{ {
title: '操作', title: '操作', fixed: 'right',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
fixed: 'right',
buttons: [ buttons: [
{ {
name: 'edit', name: 'edit',
@@ -95,6 +95,7 @@ const tableStore = new TableStore({
}, },
click: row => { click: row => {
popupEditRef.value.open('编辑用户', row) popupEditRef.value.open('编辑用户', row)
console.log("🚀 ~ row:", row)
} }
}, },
{ {

View File

@@ -1,245 +1,277 @@
<template> <template>
<el-dialog class="cn-operate-dialog" v-model.trim="dialogVisible" :title="title"> <el-dialog class="cn-operate-dialog" v-model.trim="dialogVisible" :title="title">
<el-form :model="form" ref="formRef" label-width="auto" class="form-two" :rules="rules">
<el-form :model="form" label-width="auto" class="form-two" :rules="rules"> <el-form-item label="用户名" prop="name">
<el-form-item label="用户名" prop="name"> <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="登录名" prop="loginName">
<el-form-item label="登录名" prop="loginName"> <el-input maxlength="32" show-word-limit v-model.trim="form.loginName" placeholder="请输入登录名" />
<el-input maxlength="32" show-word-limit v-model.trim="form.loginName" placeholder="请输入登录名" /> </el-form-item>
</el-form-item> <el-form-item label="默认密码" prop="password" v-if="title === '新增用户'">
<el-form-item label="默认密码" prop="password" v-if="title === '新增用户'"> <el-input
<el-input maxlength="32" show-word-limit v-model.trim="form.password" placeholder="请输入密码" disabled /> maxlength="32"
</el-form-item> show-word-limit
<el-form-item label="权限类型" prop="type"> v-model.trim="form.password"
<el-select v-model.trim="form.type" @change="changeValue" disabled placeholder="请选择权限类型"> placeholder="请输入密码"
<el-option v-for="(item, index) in UserTypeOption" :label="item.label" :value="item.value" disabled
:key="index"></el-option> />
</el-select> </el-form-item>
</el-form-item> <el-form-item label="权限类型" prop="type">
<el-form-item label="用户类型" prop="casualUser"> <el-select v-model.trim="form.type" @change="changeValue" disabled placeholder="请选择权限类型">
<el-select v-model.trim="form.casualUser" placeholder="请选择权限类型"> <el-option
<el-option v-for="(item, index) in TypeOptions" :label="item.label" :value="item.value" v-for="(item, index) in UserTypeOption"
:key="index"></el-option> :label="item.label"
</el-select> :value="item.value"
</el-form-item> :key="index"
<!-- <el-form-item label="所属部门" prop="deptId"> ></el-option>
<Area v-model.trim="form.deptId" /> </el-select>
</el-form-item> --> </el-form-item>
<el-form-item label="角色" prop="role"> <el-form-item label="用户类型" prop="casualUser">
<el-select v-model.trim="form.role" placeholder="请选择角色" multiple collapse-tags> <el-select v-model.trim="form.casualUser" placeholder="请选择权限类型">
<el-option v-for="(item, index) in roleOptions" :label="item.label" :value="item.value" <el-option
:key="index"></el-option> v-for="(item, index) in TypeOptions"
</el-select> :label="item.label"
</el-form-item> :value="item.value"
:key="index"
<el-form-item label="手机号" prop="phone"> ></el-option>
<el-input maxlength="32" show-word-limit v-model.trim="form.phone" placeholder="请输入手机号" /> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="邮箱" prop="email"> <!-- <el-form-item label="所属部门" prop="deptId">
<el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" /> <Area v-model.trim="form.deptId" />
</el-form-item> </el-form-item> -->
<el-form-item label="时间段" prop="limitTime"> <el-form-item label="角色" prop="role">
<el-slider v-model.trim="form.limitTime" style="width: 95%" range show-stops :max="24" /> <el-select v-model.trim="form.role" placeholder="请选择角色" multiple collapse-tags>
</el-form-item> <el-option
<el-form-item label="起始IP" prop="limitIpStart"> v-for="(item, index) in roleOptions"
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" /> :label="item.label"
</el-form-item> :value="item.value"
<el-form-item label="结束IP" prop="limitIpEnd"> :key="index"
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpEnd" placeholder="请输入描述" /> ></el-option>
</el-form-item> </el-select>
</el-form-item>
<el-form-item label="短信通知" prop="smsNotice">
<el-radio-group v-model.trim="form.smsNotice" style="width: 200px"> <el-form-item label="手机号" prop="phone">
<el-radio-button :label="0"></el-radio-button> <el-input maxlength="32" show-word-limit v-model.trim="form.phone" placeholder="请输入手机号" />
<el-radio-button :label="1"></el-radio-button> </el-form-item>
</el-radio-group> <el-form-item label="邮箱" prop="email">
</el-form-item> <el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" />
<el-form-item label="邮件通知" prop="emailNotice"> </el-form-item>
<el-radio-group v-model.trim="form.emailNotice" style="width: 200px"> <el-form-item label="时间段" prop="limitTime">
<el-radio-button :label="0"></el-radio-button> <el-slider v-model.trim="form.limitTime" style="width: 95%" range :max="24" />
<el-radio-button :label="1"></el-radio-button> </el-form-item>
</el-radio-group> <el-form-item label="起始IP" prop="limitIpStart">
</el-form-item> <el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" />
<el-form-item label="用户ID"> </el-form-item>
<div style="display: flex; width: 100%"> <el-form-item label="结束IP" prop="limitIpEnd">
<el-radio-group v-model.trim="useId"> <el-input maxlength="32" show-word-limit v-model.trim="form.limitIpEnd" placeholder="请输入描述" />
<el-radio-button :label="1"></el-radio-button> </el-form-item>
<el-radio-button :label="0"></el-radio-button>
</el-radio-group> <el-form-item label="短信通知" prop="smsNotice">
<el-input maxlength="32" show-word-limit :disabled="title !== '新增用户'" v-model.trim="form.id" <el-radio-group v-model.trim="form.smsNotice" style="width: 200px">
placeholder="请输入用户id" v-if="useId" style="flex: 1;" class="ml10"></el-input> <el-radio-button :label="0"></el-radio-button>
</div> <el-radio-button :label="1"></el-radio-button>
</el-form-item> </el-radio-group>
</el-form> </el-form-item>
<el-form-item label="邮件通知" prop="emailNotice">
<template #footer> <el-radio-group v-model.trim="form.emailNotice" style="width: 200px">
<span class="dialog-footer"> <el-radio-button :label="0"></el-radio-button>
<el-button @click="dialogVisible = false">取消</el-button> <el-radio-button :label="1"></el-radio-button>
<el-button type="primary" @click="submit">确认</el-button> </el-radio-group>
</span> </el-form-item>
</template> <el-form-item label="用户ID">
</el-dialog> <div style="display: flex; width: 100%">
</template> <el-radio-group v-model.trim="useId">
<script lang="ts" setup> <el-radio-button :label="1"></el-radio-button>
import { ref, inject } from 'vue' <el-radio-button :label="0"></el-radio-button>
import { reactive } from 'vue' </el-radio-group>
import TableStore from '@/utils/tableStore' <el-input
import { ElMessage, FormItemRule } from 'element-plus' maxlength="32"
import { roleList } from '@/api/user-boot/role' show-word-limit
import { add, edit } from '@/api/user-boot/user' :disabled="title !== '新增用户'"
import { useAdminInfo } from '@/stores/adminInfo' v-model.trim="form.id"
import Area from '@/components/form/area/index.vue' placeholder="请输入用户id"
import { Arrayable } from 'element-plus/es/utils' v-if="useId"
style="flex: 1"
const adminInfo = useAdminInfo() class="ml10"
const tableStore = inject('tableStore') as TableStore ></el-input>
// do not use same name with ref </div>
const form = reactive({ </el-form-item>
id: '', </el-form>
name: '',
password: '123456', <template #footer>
email: '', <span class="dialog-footer">
limitIpStart: '', <el-button @click="dialogVisible = false">取消</el-button>
deptId: '', <el-button type="primary" @click="submit">确认</el-button>
deptName: '', </span>
casualUser: 1, </template>
loginName: '', </el-dialog>
phone: '', </template>
limitIpEnd: '', <script lang="ts" setup>
limitTime: [1, 2], import { ref, inject } from 'vue'
role: [], import { reactive } from 'vue'
smsNotice: 0, import TableStore from '@/utils/tableStore'
emailNotice: 0, import { ElMessage, FormItemRule } from 'element-plus'
type: 0 import { roleList } from '@/api/user-boot/role'
}) import { add, edit } from '@/api/user-boot/user'
const rules: Partial<Record<string, Arrayable<FormItemRule>>> = { import { useAdminInfo } from '@/stores/adminInfo'
name: [{ required: true, message: '用户名不能为空', trigger: 'blur' }], import Area from '@/components/form/area/index.vue'
role: [{ required: true, message: '角色不能为空', trigger: 'blur' }], import { Arrayable } from 'element-plus/es/utils'
password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }],
loginName: [{ required: true, message: '登录名不能为空', trigger: 'blur' }], const adminInfo = useAdminInfo()
casualUser: [{ required: true, message: '用户类型不能为空', trigger: 'blur' }], const tableStore = inject('tableStore') as TableStore
smsNotice: [{ required: true, message: '短信通知不能为空', trigger: 'blur' }], // do not use same name with ref
emailNotice: [{ required: true, message: '邮件通知不能为空', trigger: 'blur' }], const form = reactive({
email: [ id: '',
{ required: false, message: '邮箱不能为空', trigger: 'blur' }, name: '',
{ password: '123456',
type: 'email', email: '',
message: "'请输入正确的邮箱地址", limitIpStart: '',
trigger: ['blur', 'change'] deptId: '',
} deptName: '',
], casualUser: 1,
phone: [ loginName: '',
{ required: false, message: '手机号不能为空', trigger: 'blur' }, phone: '',
{ limitIpEnd: '',
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, limitTime: [1, 2],
message: '请输入正确的手机号码', role: [],
trigger: 'blur' smsNotice: 0,
} emailNotice: 0,
], type: 0
limitTime: [{ required: true, message: '时间段不能为空', trigger: 'blur' }], })
limitIpStart: [ const formRef = ref()
{ required: true, message: '起始IP不能为空', trigger: 'blur' }, const rules: Partial<Record<string, Arrayable<FormItemRule>>> = {
{ name: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
required: true, role: [{ required: true, message: '请输入角色', trigger: 'blur' }],
validator: (rule: any, value: string, callback: any) => { password: [{ required: true, message: '请输入用户密码', trigger: 'blur' }],
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/ loginName: [{ required: true, message: '请输入登录名', trigger: 'blur' }],
let isCorrect = regexp.test(value) casualUser: [{ required: true, message: '请输入用户类型', trigger: 'blur' }],
if (value == '') { smsNotice: [{ required: true, message: '请输入短信通知', trigger: 'blur' }],
return callback(new Error('请输入IP地址')) emailNotice: [{ required: true, message: '请输入邮件通知', trigger: 'blur' }],
} else if (!isCorrect) { email: [
callback(new Error('请输入正确的IP地址')) { required: false, message: '请输入邮箱', trigger: 'blur' },
} else { {
callback() type: 'email',
} message: "'请输入正确的邮箱地址",
}, trigger: ['blur', 'change']
trigger: 'blur' }
} ],
], phone: [
limitIpEnd: [ { required: false, message: '请输入手机号', trigger: 'blur' },
{ required: true, message: '结束IP不能为空', trigger: 'blur' }, {
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
required: true, message: '请输入正确的手机号码',
validator: (rule: any, value: string, callback: any) => { trigger: 'blur'
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/ }
let isCorrect = regexp.test(value) ],
if (value == '') { limitTime: [{ required: true, message: '请选择时间段', trigger: 'blur' }],
return callback(new Error('请输入IP地址')) limitIpStart: [
} else if (!isCorrect) { { required: true, message: '请输入起始IP', trigger: 'blur' },
callback(new Error('请输入正确的IP地址')) {
} else { required: true,
callback() validator: (rule: any, value: string, callback: any) => {
} let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/
}, let isCorrect = regexp.test(value)
trigger: 'blur' if (value == '') {
} return callback(new Error('请输入IP地址'))
] } else if (!isCorrect) {
} callback(new Error('请输入正确的IP地址'))
const UserTypeOption = [ } else {
{ label: '管理员', value: 1 }, callback()
{ label: '普通用户', value: 2 } }
] },
const TypeOptions = [ trigger: 'blur'
{ label: '临时用户', value: 0 }, }
{ label: '长期用户', value: 1 } ],
] limitIpEnd: [
const useId = ref(1) { required: true, message: '请输入结束IP', trigger: 'blur' },
const roleOptions = ref<treeData>() {
const queryRole = () => { required: true,
roleList(adminInfo.$state.userType).then((res: any) => { validator: (rule: any, value: string, callback: any) => {
roleOptions.value = res.data.map((item: any) => { let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/
return { let isCorrect = regexp.test(value)
label: item.name, if (value == '') {
value: item.id return callback(new Error('请输入IP地址'))
} } else if (!isCorrect) {
}) callback(new Error('请输入正确的IP地址'))
}) } else {
} callback()
queryRole() }
const dialogVisible = ref(false) },
const title = ref('新增菜单') trigger: 'blur'
const open = (text: string, data?: anyObj) => { }
title.value = text ]
dialogVisible.value = true }
if (data) { const UserTypeOption = [
for (let key in form) { { label: '管理员', value: 1 },
form[key] = data[key] { label: '普通用户', value: 2 }
} ]
form.limitTime = data.limitTime.split('-') const TypeOptions = [
form.role = data.roleList { label: '临时用户', value: 0 },
} else { { label: '长期用户', value: 1 }
for (let key in form) { ]
form[key] = '' const useId = ref(1)
} const roleOptions = ref<treeData>()
form.casualUser = 1 const queryRole = () => {
form.limitTime = [0, 24] roleList(adminInfo.$state.userType).then((res: any) => {
form.role = [] roleOptions.value = res.data.map((item: any) => {
form.smsNotice = 0 return {
form.emailNotice = 0 label: item.name,
useId.value = 1 value: item.id
form.id = '' }
form.limitIpStart = '0.0.0.0' })
form.limitIpEnd = '255.255.255.255' })
form.password = '123456' }
} queryRole()
form.type = adminInfo.$state.userType + 1 const dialogVisible = ref(false)
} const title = ref('新增菜单')
const submit = async () => { const open = (text: string, data?: anyObj) => {
let obj = JSON.parse(JSON.stringify(form)) formRef.value?.resetFields()
obj.limitTime = obj.limitTime.join('-') title.value = text
delete obj.password dialogVisible.value = true
if (form.id) {
await edit(obj) if (data) {
ElMessage.success('修改成功') for (let key in form) {
} else { form[key] = data[key]
form.type = adminInfo.$state.userType + 1 }
await add(obj) form.limitTime = data.limitTime.split('-')
ElMessage.success('新增成功') form.role = data.roleList
} } else {
tableStore.index() for (let key in form) {
dialogVisible.value = false form[key] = ''
} }
form.casualUser = 1
const changeValue = () => { } form.limitTime = [0, 24]
defineExpose({ open }) form.role = []
</script> form.smsNotice = 0
form.emailNotice = 0
useId.value = 1
form.id = ''
form.limitIpStart = '0.0.0.0'
form.limitIpEnd = '255.255.255.255'
form.password = '123456'
form.type = adminInfo.$state.userType + 1
}
}
const submit = async () => {
formRef.value.validate(async (valid: any) => {
if (valid) {
let obj = JSON.parse(JSON.stringify(form))
obj.limitTime = obj.limitTime.join('-')
delete obj.password
if (form.id) {
await edit(obj)
ElMessage.success('修改成功')
} else {
form.type = adminInfo.$state.userType + 1
await add(obj)
ElMessage.success('新增成功')
}
tableStore.index()
dialogVisible.value = false
}
})
}
const changeValue = () => {}
defineExpose({ open })
</script>

View File

@@ -9,7 +9,7 @@
@change="sourceChange" @change="sourceChange"
:options="deviceTreeOptions" :options="deviceTreeOptions"
:show-all-levels="false" :show-all-levels="false"
:props="{ checkStrictly: true }" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable clearable
></el-cascader> ></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> --> <!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
@@ -57,17 +57,33 @@ const tabsList = ref([
]) ])
const rankOptions = ref([ const rankOptions = ref([
{ {
value: '1', value: '1,7',
label: '1级' label: '1级(ERROR)'
}, },
{ {
value: '2', value: '2,6',
label: '2级' label: '2级(WARN)'
}, },
{ {
value: '3', value: '3,4,5',
label: '3级' label: '3级(DEBUG,NORMAL)'
} },
// {
// value: '4',
// label: 'DEBUG'
// },
// {
// value: '5',
// label: 'NORMAL'
// },
// {
// value: '6',
// label: 'WARN'
// },
// {
// value: '7',
// label: 'ERROR'
// }
]) ])
const tableStore = new TableStore({ const tableStore = new TableStore({
@@ -83,14 +99,16 @@ const tableStore = 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: 'equipmentName', align: 'center',minWidth: 100 }, { title: '设备名称', field: 'equipmentName', align: 'center', width: 120 },
{ title: '工程名称', field: 'engineeringName', align: 'center',minWidth: 100 }, { title: '监测点名称', field: 'lineName', align: 'center', width: 140 },
{ title: '项目名称', field: 'projectName', align: 'center',minWidth: 100 }, { title: '工程名称', field: 'engineeringName', align: 'center', width: 120 },
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true }, { title: '项目名称', field: 'projectName', align: 'center', width: 120 },
{ title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true },
{ {
title: '模块信息', title: '模块信息',
field: 'moduleNo', field: 'moduleNo',
align: 'center',minWidth: 100 , align: 'center',
width: 100,
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue ? row.cellValue : '/' return row.cellValue ? row.cellValue : '/'
} }
@@ -116,14 +134,24 @@ const tableStore = new TableStore({
width: 100, width: 100,
render: 'tag', render: 'tag',
custom: { custom: {
// 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR
1: 'danger', 1: 'danger',
2: 'warning', 2: 'warning',
3: 'success' 3: 'success',
4: 'warning',
5: 'success',
6: 'warning',
7: 'danger'
}, },
replaceValue: { replaceValue: {
1: '1级', 1: '1级',
2: '2级', 2: '2级',
3: '3级' 3: '3级',
4: 'DEBUG',
5: 'NORMAL',
6: 'WARN',
7: 'ERROR'
} }
} }
// { // {
@@ -135,7 +163,25 @@ const tableStore = new TableStore({
// } // }
], ],
beforeSearchFun: () => {}, beforeSearchFun: () => {},
exportProcessingData: () => {
tableStore.table.allData = tableStore.table.allData.filter(item => {
item.level =
item.level == 1
? '1级'
: item.level == 2
? '2级'
: item.level == 3
? '3级'
: item.level == 4
? 'DEBUG'
: item.level == 5
? 'NORMAL'
: item.level == 6
? 'WARN'
: 'ERROR'
return item
})
}
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -177,6 +223,7 @@ const sourceChange = (e: any) => {
tableStore.table.params.deviceTypeId = e[0] || '' tableStore.table.params.deviceTypeId = e[0] || ''
tableStore.table.params.engineeringid = e[1] || '' tableStore.table.params.engineeringid = e[1] || ''
tableStore.table.params.projectId = e[2] || '' tableStore.table.params.projectId = e[2] || ''
tableStore.table.params.deviceId = e[3] || ''
} }
} }
} }

View File

@@ -1,10 +1,24 @@
<template> <template>
<TableHeader datePicker ref="refheader" showExport> <TableHeader datePicker ref="refheader" showExport>
<template v-slot:select> <template v-slot:select>
<el-form-item label="关键"> <el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入前置服务器名称ip" /> <el-input
maxlength="32"
show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入前置服务器名称ip"
/>
</el-form-item>
<el-form-item label="级别">
<el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable>
<el-option
v-for="item in rankOptions"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item> </el-form-item>
</template> </template>
</TableHeader> </TableHeader>
<!-- <div style="height: 300px;"> --> <!-- <div style="height: 300px;"> -->
@@ -21,7 +35,36 @@ import { mainHeight } from '@/utils/layout'
const props = defineProps(['deviceTree']) const props = defineProps(['deviceTree'])
const refheader = ref() const refheader = ref()
const rankOptions = ref([
{
value: '1,7',
label: '1级(ERROR)'
},
{
value: '2,6',
label: '2级(WARN)'
},
{
value: '3,4,5',
label: '3级(DEBUG,NORMAL)'
},
// {
// value: '4',
// label: 'DEBUG'
// },
// {
// value: '5',
// label: 'NORMAL'
// },
// {
// value: '6',
// label: 'WARN'
// },
// {
// value: '7',
// label: 'ERROR'
// }
])
const tableStore = new TableStore({ const tableStore = new TableStore({
url: '/cs-harmonic-boot/eventUser/frontWarnInfo', url: '/cs-harmonic-boot/eventUser/frontWarnInfo',
method: 'POST', method: 'POST',
@@ -35,34 +78,79 @@ const tableStore = 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: 'lineId', align: 'center' ,minWidth: 120 }, { title: '前置服务器名称', field: 'lineId', align: 'center', width: 120 },
{ title: '前置服务器ip', field: 'wavePath', align: 'center' ,minWidth: 100 }, { title: '前置服务器ip', field: 'wavePath', align: 'center', width: 120 },
{ title: '进程号', field: 'clDid', align: 'center',minWidth: 60 }, { title: '进程号', field: 'clDid', align: 'center', width: 60 },
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true }, { title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true },
{ {
title: '事件描述', title: '事件描述',
field: 'tag', field: 'tag',
minWidth: 350 minWidth: 350
}, },
{ {
title: '告警代码', title: '告警代码',
field: 'code', field: 'code',
align: 'center',minWidth: 100 , align: 'center',
width: 100,
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue ? '\u200B' + row.cellValue : '/' return row.cellValue ? '\u200B' + row.cellValue : '/'
}, },
sortable: true sortable: true
}, },
{
title: '级别',
field: 'level',
width: 100,
render: 'tag',
custom: {
// 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR
1: 'danger',
2: 'warning',
3: 'success',
4: 'warning',
5: 'success',
6: 'warning',
7: 'danger'
},
replaceValue: {
1: '1级',
2: '2级',
3: '3级',
4: 'DEBUG',
5: 'NORMAL',
6: 'WARN',
7: 'ERROR'
}
}
], ],
beforeSearchFun: () => {} beforeSearchFun: () => {},
exportProcessingData: () => {
tableStore.table.allData = tableStore.table.allData.filter(item => {
item.level =
item.level == 1
? '1级'
: item.level == 2
? '2级'
: item.level == 3
? '3级'
: item.level == 4
? 'DEBUG'
: item.level == 5
? 'NORMAL'
: item.level == 6
? 'WARN'
: 'ERROR'
return item
})
}
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
tableStore.table.params.level = ''
const deviceTreeOptions = ref<any>(props.deviceTree) const deviceTreeOptions = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => { deviceTreeOptions.value.map((item: any, index: any) => {
if (item.children.length == 0) { if (item.children.length == 0) {
@@ -76,6 +164,5 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
// tableStore.table.height = mainHeight(200).height as any // tableStore.table.height = mainHeight(200).height as any
}, 0) }, 0)
</script> </script>
<style></style> <style></style>

View File

@@ -9,7 +9,7 @@
@change="sourceChange" @change="sourceChange"
:options="deviceTreeOptions" :options="deviceTreeOptions"
:show-all-levels="false" :show-all-levels="false"
:props="{ checkStrictly: true }" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable clearable
></el-cascader> ></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> --> <!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
@@ -133,6 +133,7 @@ const sourceChange = (e: any) => {
tableStore.table.params.deviceTypeId = e[0] || '' tableStore.table.params.deviceTypeId = e[0] || ''
tableStore.table.params.engineeringid = e[1] || '' tableStore.table.params.engineeringid = e[1] || ''
tableStore.table.params.projectId = e[2] || '' tableStore.table.params.projectId = e[2] || ''
tableStore.table.params.deviceId = e[3] || ''
} }
} }
} }

View File

@@ -1,5 +1,5 @@
<template> <template>
<div ref="refheader" v-if="!isWaveCharts"> <div ref="refheader" v-show="!isWaveCharts" style="width: 100%">
<TableHeader datePicker showExport> <TableHeader datePicker showExport>
<template v-slot:select> <template v-slot:select>
<el-form-item label="数据来源"> <el-form-item label="数据来源">
@@ -10,7 +10,7 @@
v-model.trim="tableStore.table.params.cascader" v-model.trim="tableStore.table.params.cascader"
:options="deviceTreeOptions" :options="deviceTreeOptions"
:show-all-levels="false" :show-all-levels="false"
:props="{ checkStrictly: true }" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable clearable
></el-cascader> ></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> --> <!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
@@ -121,28 +121,31 @@ const tableStore = new TableStore({
method: 'POST', method: 'POST',
publicHeight: 65, publicHeight: 65,
exportName: '暂态事件', exportName: '暂态事件',
column: [ { column: [
{
title: '序号', title: '序号',
width: 80, width: 80,
formatter: (row: any) => { formatter: (row: any) => {
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: 'equipmentName', minWidth: 120,align: 'center' }, { title: '设备名称', field: 'equipmentName', minWidth: 120, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 120,align: 'center' }, { title: '工程名称', field: 'engineeringName', minWidth: 120, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 120,align: 'center' }, { title: '项目名称', field: 'projectName', minWidth: 120, align: 'center' },
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180,sortable: true }, { title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '监测点名称', field: 'lineName', minWidth: 120,align: 'center' }, { title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' },
{ title: '事件描述', field: 'showName', minWidth: 120,align: 'center' }, { title: '事件描述', field: 'showName', minWidth: 120, align: 'center' },
{ title: '事件发生位置', field: 'evtParamPosition',minWidth: 150, align: 'center' }, { title: '事件发生位置', field: 'evtParamPosition', minWidth: 150, align: 'center' },
{ title: '相别', field: 'evtParamPhase',minWidth: 80, align: 'center' }, { title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '持续时间(s)', field: 'evtParamTm',minWidth: 80, align: 'center',sortable: true }, { title: '持续时间(s)', field: 'evtParamTm', minWidth: 80, align: 'center', sortable: true },
{ title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center',sortable: true }, { title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center', sortable: true },
{ {
title: '操作', title: '操作',
fixed: 'right',
align: 'center', align: 'center',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -153,20 +156,21 @@ const tableStore = new TableStore({
render: 'basicButton', render: 'basicButton',
loading: 'loading1', loading: 'loading1',
disabled: row => { disabled: row => {
return !row.wavePath return !row.wavePath
}, },
click: async row => { click: async row => {
row.loading1 = true row.loading1 = true
loading.value = true
isWaveCharts.value = true
await analyseWave(row.id) await analyseWave(row.id)
.then(res => { .then(res => {
row.loading1 = false row.loading1 = false
if (res != undefined) { if (res != undefined) {
loading.value = true
isWaveCharts.value = true
boxoList.value = row boxoList.value = row
boxoList.value.persistTime = row.evtParamTm
boxoList.value.featureAmplitude = boxoList.value.featureAmplitude =
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null row.evtParamVVaDepth != '-' ? (row.evtParamVVaDepth - 0) / 100 : null
boxoList.value.systemType = 'ZL' boxoList.value.systemType = 'YPT'
wp.value = res.data wp.value = res.data
} }
loading.value = false loading.value = false
@@ -234,24 +238,24 @@ const tableStore = new TableStore({
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return row.showName != '未知'; return row.showName != '未知'
} }
}, },
{ {
name: 'edit', name: 'edit',
title: '波形补召', title: '波形补召',
type: 'primary', type: 'primary',
icon: 'el-icon-Check', icon: 'el-icon-Check',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return row.wavePath || row.showName === '未知'; return row.wavePath || row.showName === '未知'
}, },
click: row => { click: row => {
getFileByEventId(row.id).then(res => { getFileByEventId(row.id).then(res => {
ElMessage.success(res.message) ElMessage.success(res.message)
tableStore.index() tableStore.index()
}) })
} }
} }
] ]
} }
@@ -312,8 +316,8 @@ const sourceChange = (e: any) => {
tableStore.table.params.deviceTypeId = e[0] || '' tableStore.table.params.deviceTypeId = e[0] || ''
tableStore.table.params.engineeringid = e[1] || '' tableStore.table.params.engineeringid = e[1] || ''
tableStore.table.params.projectId = e[2] || '' tableStore.table.params.projectId = e[2] || ''
tableStore.table.params.deviceId = e[3] || ''
} }
} }
// tableStore.table.params.engineeringid = e[1] || '' // tableStore.table.params.engineeringid = e[1] || ''

View File

@@ -0,0 +1,129 @@
<template>
<el-dialog class="cn-operate-dialog" draggable v-model="dialogVisible" :title="title" width="500px"
@closed="handleClosed">
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto" class="form-one">
<el-form-item label="方案名称" prop="name">
<el-input v-model.trim="form.name" maxlength="64" show-word-limit placeholder="请输入方案名称" clearable />
</el-form-item>
<el-form-item label="在线率阈值" prop="onlineRateLimit">
<el-input-number v-model="form.onlineRateLimit" :min="0" :max="100" :precision="0" style="width: 100%"
placeholder="0-100" />
</el-form-item>
<el-form-item label="完整性阈值" prop="integrityLimit">
<el-input-number v-model="form.integrityLimit" :min="0" :max="100" :precision="0" style="width: 100%"
placeholder="0-100" />
</el-form-item>
<!-- <el-form-item label="是否启用" prop="active">
<el-select v-model="form.active" placeholder="请选择" style="width: 100%">
<el-option label="启用" value="1" />
<el-option label="停用" value="0" />
</el-select>
</el-form-item> -->
</el-form>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { inject } from 'vue'
import { add, update } from '@/api/cs-system-boot/appinfo'
const emit = defineEmits(['Cancels'])
const dialogVisible = ref(false)
const title = ref('')
const formRef = ref()
const defaultForm = () => ({
id: '',
name: '',
onlineRateLimit: undefined as number | undefined,
integrityLimit: undefined as number | undefined,
active: '0' as string
})
const form = reactive(defaultForm())
const percentRule = (label: string) => [
{ required: true, message: `请输入${label}`, trigger: 'blur' },
{
type: 'number',
min: 0,
max: 100,
message: `${label}范围为 0-100`,
trigger: 'blur'
}
]
const rules: any = {
name: [
{ required: true, message: '请输入方案名称', trigger: 'blur' },
{ min: 1, max: 64, message: '长度 1-64 个字符', trigger: 'blur' }
],
onlineRateLimit: percentRule('在线率阈值'),
integrityLimit: percentRule('完整性阈值'),
active: [{ required: true, message: '请选择是否启用', trigger: 'change' }]
}
const isEdit = () => title.value.includes('修改')
const resetForm = () => {
Object.assign(form, defaultForm())
}
const open = (e: { text: string; row?: any }) => {
formRef.value?.resetFields()
title.value = e.text
dialogVisible.value = true
resetForm()
if (e.row) {
form.id = e.row.id ?? ''
form.name = e.row.name ?? ''
form.onlineRateLimit = e.row.onlineRateLimit != null ? Number(e.row.onlineRateLimit) : undefined
form.integrityLimit = e.row.integrityLimit != null ? Number(e.row.integrityLimit) : undefined
form.active = String(e.row.active ?? '0')
}
}
const handleCancel = () => {
dialogVisible.value = false
emit('Cancels')
}
const handleClosed = () => {
resetForm()
}
const onSubmit = () => {
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
const payload = {
id: form.id,
name: form.name,
onlineRateLimit: form.onlineRateLimit,
integrityLimit: form.integrityLimit,
active: form.active
}
try {
if (isEdit()) {
await update(payload)
ElMessage.success('修改成功')
} else {
await add(payload)
ElMessage.success('新增成功')
}
dialogVisible.value = false
emit('Cancels')
} catch {
/* 请求层一般会统一提示 */
}
})
}
defineExpose({ open })
</script>

View File

@@ -0,0 +1,152 @@
<template>
<div class="default-main">
<TableHeader select :showReset="false" ref="TableHeaderRef">
<template #operation>
<el-button icon="el-icon-Plus" type="primary" @click="add">新增</el-button>
</template>
</TableHeader>
<Table ref="tableRef" />
<Form ref="formRef" @Cancels="tableStore.index()" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, nextTick } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { ElMessage } from 'element-plus'
import { toggleActive, csDelete } from '@/api/cs-system-boot/appinfo'
import Form from './form.vue'
defineOptions({
name: 'govern/alarmConfig'
})
const formTabRef = ref()
const formRef = ref()
const tableStore: any = new TableStore({
url: '/cs-system-boot/csAlarmSet/listAll',
method: 'POST',
showPage: false,
column: [
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'name', title: '名称' },
{ field: 'onlineRateLimit', title: '在线率阈值' },
{ field: 'integrityLimit', title: '完整性阈值' },
{ field: 'updateTime', title: '创建时间' },
{
title: '是否启用',
render: 'switch',
width: 100,
field: 'active',
activeText: '启用',
inactiveText: '停用',
inactiveValue: '0',
activeValue: '1',
onChangeField: (row: any, value: any) => {
if (row.active == 1) {
return ElMessage({ message: '至少需要保留一条启用的配置!', type: 'warning' })
// 至少需要保留一条启用的配置
}
toggleActive({
id: row.id
}).then(res => {
ElMessage({ message: '启用成功!', type: 'success' })
tableStore.index()
})
}
},
{
title: '操作',
fixed: 'right',
width: '180',
render: 'buttons',
buttons: [
// {
// name: 'edit',
// title: '启用 ',
// type: 'primary',
// icon: 'el-icon-Plus',
// render: 'basicButton',
// disabled: row => {
// return row.active == 1
// },
// click: row => {
// toggleActive({ id: row.id }).then(res => {
// ElMessage({
// message: '启用成功!',
// type: 'success'
// })
// tableStore.index()
// })
// }
// },
{
name: 'edit',
title: '修改 ',
type: 'primary',
icon: 'el-icon-Plus',
render: 'basicButton',
click: row => {
setTimeout(() => {
formRef.value.open({
text: '修改配置',
row: row
})
}, 10)
}
},
{
name: 'edit',
title: '删除',
type: 'danger',
icon: 'el-icon-Delete',
render: 'confirmButton',
disabled: row => {
return row.active == 1
},
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定删除吗?'
},
click: row => {
csDelete({ id: row.id }).then(res => {
ElMessage({
message: '删除成功!',
type: 'success'
})
tableStore.index()
})
}
}
]
}
],
loadCallback: () => {
}
})
provide('tableStore', tableStore)
// 新增主题
const add = () => {
setTimeout(() => {
formRef.value.open({
text: '新增配置'
})
}, 10)
}
onMounted(() => {
tableStore.index()
})
</script>

View File

@@ -7,15 +7,15 @@
<el-tab-pane label="前置告警" name="2"> <el-tab-pane label="前置告警" name="2">
<Front v-if="activeName == '2'" :deviceTree="deviceTree" :key="key" /> <Front v-if="activeName == '2'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="稳态越限告警" name="3"> <!-- <el-tab-pane label="稳态越限告警" name="3">
<Steady v-if="activeName == '3'" :deviceTree="deviceTree" :key="key" /> <Steady v-if="activeName == '3'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane> </el-tab-pane> -->
<el-tab-pane label="暂态事件" name="4"> <el-tab-pane label="暂态事件" name="4">
<Transient v-if="activeName == '4'" :deviceTree="deviceTree" :key="key" /> <Transient v-if="activeName == '4'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="异常事件" name="5"> <!-- <el-tab-pane label="异常事件" name="5">
<Abnormal v-if="activeName == '5'" :deviceTree="deviceTree" :key="key" /> <Abnormal v-if="activeName == '5'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane> </el-tab-pane> -->
</el-tabs> </el-tabs>
</div> </div>
</template> </template>
@@ -30,25 +30,27 @@ import { getDeviceTree } from '@/api/cs-device-boot/csLedger'
defineOptions({ defineOptions({
name: 'govern/alarm/index' name: 'govern/alarm/index'
}) })
const deviceTree = ref([]) const deviceTree = ref([])
const activeName = ref('1') const activeName = ref('1')
const key = ref(0) const key = ref(0)
getDeviceTree().then(res => { getDeviceTree().then(res => {
res.data.forEach((item: any) => { // res.data.forEach((item: any) => {
item.value = item.id // item.value = item.id
item.label = item.name // item.label = item.name
item.children.forEach((child: any) => { // item.children.forEach((child: any) => {
child.value = child.id // child.value = child.id
child.label = child.name // child.label = child.name
child.children.forEach((grand: any) => { // child.children.forEach((grand: any) => {
grand.value = grand.id // grand.value = grand.id
grand.label = grand.name // grand.label = grand.name
delete grand.children // delete grand.children
}) // })
}) // })
}) // })
deviceTree.value = res.data deviceTree.value = res.data
key.value += 1 key.value += 1
activeName.value = '1'
}) })
onMounted(() => { }) onMounted(() => { })

View File

@@ -0,0 +1,270 @@
<template>
<el-dialog class="cn-operate-dialog" draggable v-model="dialogVisible" :title="title" width="900px"
@closed="handleClosed">
<el-scrollbar max-height="60vh">
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto" class="form-two">
<el-form-item label="指标名称" prop="indexName">
<el-input v-model.trim="form.indexName" placeholder="请输入指标名称" clearable maxlength="128" />
</el-form-item>
<el-form-item label="指标code" prop="indexCode">
<el-input v-model.trim="form.indexCode" placeholder="请输入指标code" clearable maxlength="64" />
</el-form-item>
<el-form-item label="表名" prop="influxdbTableName">
<el-input v-model.trim="form.influxdbTableName" placeholder="请输入表名" clearable />
</el-form-item>
<el-form-item label="列属性" prop="influxdbColumnName">
<el-input v-model.trim="form.influxdbColumnName" placeholder="实体类属性名" clearable maxlength="128" />
</el-form-item>
<el-form-item label="谐波次数">
<el-slider v-model.trim="form.harmSlider" range :max="50" style="width: 90%" />
</el-form-item>
<el-form-item label="相别" prop="phaseList">
<el-select v-model.trim="form.phaseList" filterable multiple clearable collapse-tags
collapse-tags-tooltip placeholder="请选择相别">
<el-option v-for="item in phaseSelect" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="指标下限" prop="minValue">
<el-input-number v-model="form.minValue" style="width: 100%" placeholder="下限" />
</el-form-item>
<el-form-item label="指标上限" prop="maxValue">
<el-input-number v-model="form.maxValue" style="width: 100%" placeholder="上限" />
</el-form-item>
<el-form-item label="电压等级参与" prop="isVoltage">
<el-radio-group v-model="form.isVoltage">
<el-radio :value="0">不参与</el-radio>
<el-radio :value="1">参与</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Ct变比参与" prop="ctAttendFlag">
<el-radio-group v-model="form.ctAttendFlag">
<el-radio :value="0">不参与</el-radio>
<el-radio :value="1">参与</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="数据来源" prop="dataSource">
<el-select v-model="form.dataSource" placeholder="请选择" clearable style="width: 100%">
<el-option label="InfluxDB" value="InfluxDB" />
<!-- <el-option label="oracle" value="oracle" /> -->
<el-option label="MySql" value="MySql" />
</el-select>
</el-form-item>
<el-form-item label="所属系统" prop="belongingSystem">
<el-select v-model="form.belongingSystem" placeholder="请选择" clearable style="width: 100%">
<el-option label="pqs" value="pqs" />
<el-option label="govern" value="govern" />
</el-select>
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model.trim="form.unit" placeholder="请输入单位" clearable maxlength="32" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :precision="0" style="width: 100%" />
</el-form-item>
<!-- <el-form-item label="状态" prop="state">
<el-select v-model="form.state" placeholder="请选择" style="width: 100%">
<el-option label="正常" :value="1" />
<el-option label="删除" :value="0" />
</el-select>
</el-form-item> -->
<el-form-item label="条件描述" prop="otherAlgorithm" class="form-item-full">
<el-input v-model.trim="form.otherAlgorithm" type="textarea" :rows="2" placeholder="无具体范围时的判断条件描述"
maxlength="500" show-word-limit />
</el-form-item>
<el-form-item label="备注" prop="remark" class="form-item-full">
<el-input v-model.trim="form.remark" type="textarea" :rows="2" placeholder="备注" maxlength="500"
show-word-limit />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { save, update } from '@/api/algorithm-boot/scopeConfig'
const emit = defineEmits(['Cancels'])
const dialogVisible = ref(false)
const title = ref('')
const formRef = ref()
const phaseSelect = [
{
name: 'A相',
id: 'A'
},
{
name: 'B相',
id: 'B'
},
{
name: 'C相',
id: 'C'
},
{
name: '无相别',
id: 'T'
},
{
name: 'AB相',
id: 'AB'
},
{
name: 'BC相',
id: 'BC'
},
{
name: 'CA相',
id: 'CA'
},
// {
// name: '无相别',
// id: 'M'
// },
]
const defaultForm = () => ({
id: '',
indexCode: '',
indexName: '',
harmSlider: [0, 0],
harmStart: undefined as number | undefined,
harmEnd: undefined as number | undefined,
phaseType: '',
phaseList: [] as any,
influxdbTableName: '',
influxdbColumnName: '',
minValue: undefined as number | undefined,
maxValue: undefined as number | undefined,
isVoltage: 0,
ctAttendFlag: 0,
dataSource: '',
otherAlgorithm: '',
remark: '',
unit: '',
sort: 100,
belongingSystem: 'govern',
state: 1
})
const form = reactive(defaultForm())
const validateMinMax = (_rule: any, _value: any, callback: (e?: Error) => void) => {
if (form.minValue == null || form.maxValue == null) {
callback()
return
}
if (form.minValue > form.maxValue) {
callback(new Error('指标下限不能大于指标上限'))
} else {
callback()
}
}
const rules: any = {
indexCode: [{ required: true, message: '请输入指标code', trigger: 'blur' }],
indexName: [{ required: true, message: '请输入指标名称', trigger: 'blur' }],
phaseList: [{ required: true, message: '请输入指标名称', trigger: 'change' }],
influxdbTableName: [{ required: true, message: '请输入表名', trigger: 'blur' }],
influxdbColumnName: [{ required: true, message: '请输入列属性', trigger: 'blur' }],
minValue: [
{ required: true, message: '请输入指标下限', trigger: 'blur' },
{ validator: validateMinMax, trigger: 'change' }
],
maxValue: [
{ required: true, message: '请输入指标上限', trigger: 'blur' },
{ validator: validateMinMax, trigger: 'change' }
],
isVoltage: [{ required: true, message: '请选择电压等级是否参与', trigger: 'change' }],
ctAttendFlag: [{ required: true, message: '请选择Ct变比是否参与', trigger: 'change' }],
dataSource: [{ required: true, message: '请选择数据来源', trigger: 'change' }],
belongingSystem: [{ required: true, message: '请选择所属系统', trigger: 'change' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }],
state: [{ required: true, message: '请选择状态', trigger: 'change' }]
}
const isEdit = () => title.value.includes('修改')
const resetForm = () => {
Object.assign(form, defaultForm())
}
const fillForm = (row: any) => {
const keys = Object.keys(defaultForm()) as (keyof ReturnType<typeof defaultForm>)[]
keys.forEach(key => {
if (row[key] === undefined || row[key] === null) return
if (['harmStart', 'harmEnd', 'sort', 'isVoltage', 'ctAttendFlag', 'state'].includes(key)) {
; (form as any)[key] = Number(row[key])
} else if (['minValue', 'maxValue'].includes(key)) {
; (form as any)[key] = Number(row[key])
} else {
; (form as any)[key] = row[key]
}
})
form.phaseList = form.phaseType.split(',')
form.harmSlider = [form.harmStart || 0, form.harmEnd || 0]
}
const open = (e: { text: string; row?: any }) => {
formRef.value?.resetFields()
title.value = e.text
dialogVisible.value = true
resetForm()
if (e.row) {
fillForm(e.row)
}
}
const handleCancel = () => {
dialogVisible.value = false
emit('Cancels')
}
const handleClosed = () => {
resetForm()
}
const onSubmit = () => {
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
form.harmStart = form.harmSlider[0]
form.harmEnd = form.harmSlider[1]
form.phaseType = form.phaseList.join(',')
const payload = { ...form }
try {
if (isEdit()) {
await update(payload)
ElMessage.success('修改成功!')
} else {
await save({ ...payload, id: '' })
ElMessage.success('新增成功!')
}
dialogVisible.value = false
emit('Cancels')
} catch {
/* 统一错误提示 */
}
})
}
defineExpose({ open })
</script>
<style scoped>
.form-item-full {
grid-column: 1 / -1;
}
</style>

View File

@@ -0,0 +1,193 @@
<template>
<div class="default-main">
<TableHeader ref="TableHeaderRef">
<template #select>
<el-form-item label="数据来源">
<el-select v-model="tableStore.table.params.dataSource" clearable placeholder="请选择数据来源">
<el-option v-for="item in dataSourceOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="系统类型">
<el-select v-model="tableStore.table.params.systemType" clearable placeholder="请选择系统类型">
<el-option v-for="item in systemTypeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="表名">
<el-input v-model.trim="tableStore.table.params.tableName" placeholder="请输入表名" clearable
maxlength="64" />
</el-form-item>
</template>
<template #operation>
<el-button icon="el-icon-Plus" type="primary" @click="add">新增</el-button>
</template>
</TableHeader>
<Table ref="tableRef" />
<Form ref="formRef" @Cancels="tableStore.index()" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, nextTick } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { ElMessage } from 'element-plus'
import { pqDelete } from '@/api/algorithm-boot/scopeConfig'
import Form from './form.vue'
defineOptions({
name: 'govern/alarmConfig'
})
const dataSourceOptions = [
{ label: 'InfluxDB', value: 'InfluxDB' },
// { label: 'oracle', value: 'oracle' },
{ label: 'MySql', value: 'MySql' }
]
const systemTypeOptions = [
{ label: 'pqs', value: 'pqs' },
{ label: 'govern', value: 'govern' }
]
const yesNo = (v: number) => (v === 1 ? '是' : v === 0 ? '否' : '/')
const stateMap: Record<number, string> = { 0: '删除', 1: '正常' }
const formRef = ref()
const tableStore: any = new TableStore({
url: '/algorithm-boot/pqReasonableRange/getData',
method: 'POST',
showPage: false,
column: [
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'indexName', title: '指标名称', minWidth: 200 },
{ field: 'indexCode', title: '指标code', minWidth: 150 },
{ field: 'influxdbTableName', title: '表名', minWidth: 150 },
{ field: 'influxdbColumnName', title: '列属性', minWidth: 150 },
{
field: 'harmStart', title: '谐波次数', width: 90,
formatter: (row: any) => {
return row.cellValue == null ? '/' : row.cellValue + '-' + row.row.harmEnd
}
},
{
field: 'phaseType', title: '相别', width: 100, formatter: (row: any) => {
return row.cellValue == 'T' ? '/' : row.cellValue
}
},
{ field: 'minValue', title: '指标下限', width: 100 },
{ field: 'maxValue', title: '指标上限', width: 100 },
{
field: 'isVoltage',
title: '电压等级参与',
width: 110,
formatter: (row: any) => yesNo(row.cellValue)
},
{
field: 'ctAttendFlag',
title: 'Ct变比参与',
width: 100,
formatter: (row: any) => yesNo(row.cellValue)
},
{ field: 'dataSource', title: '数据来源', width: 100 },
{ field: 'belongingSystem', title: '所属系统', width: 100 },
{
field: 'unit', title: '单位', width: 80,
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{ field: 'sort', title: '排序', width: 70 },
{
field: 'otherAlgorithm', title: '条件描述', minWidth: 200,
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
field: 'remark', title: '备注', minWidth: 200,
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '操作',
fixed: 'right',
width: '180',
render: 'buttons',
buttons: [
{
name: 'edit',
title: '修改 ',
type: 'primary',
icon: 'el-icon-Plus',
render: 'basicButton',
click: row => {
setTimeout(() => {
formRef.value.open({
text: '修改配置',
row: row
})
}, 10)
}
},
{
name: 'edit',
title: '删除',
type: 'danger',
icon: 'el-icon-Delete',
render: 'confirmButton',
disabled: row => {
return row.active == 1
},
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定删除吗?'
},
click: row => {
pqDelete({ id: row.id }).then(res => {
ElMessage({
message: '删除成功!',
type: 'success'
})
tableStore.index()
})
}
}
]
}
],
loadCallback: () => {
}
})
tableStore.table.params.dataSource = ''
tableStore.table.params.tableName = ''
tableStore.table.params.systemType = ''
provide('tableStore', tableStore)
// 新增主题
const add = () => {
setTimeout(() => {
formRef.value.open({
text: '新增配置'
})
}, 10)
}
onMounted(() => {
tableStore.index()
})
</script>

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