19 Commits

Author SHA1 Message Date
guanj
c2f23aa957 修改测试问题 2026-05-28 20:36:49 +08:00
guanj
9466141bff 去除部门树接口 2026-05-28 15:10:40 +08:00
guanj
faac12615d 提交 2026-05-26 16:23:18 +08:00
guanj
8b80e0678f 提交代码 2026-04-25 15:22:28 +08:00
guanj
7abcdb3a6b 联调程序升级 2026-04-24 09:13:48 +08:00
guanj
c8a42948de 修改台账 2026-04-20 09:28:04 +08:00
guanj
99bc99a6fc 绘制稳态事件配置页面 2026-04-17 08:49:22 +08:00
guanj
01a28d88f3 修改台账 2026-04-15 19:29:36 +08:00
guanj
632a0104fb 调整台账录入页面 2026-04-15 13:44:28 +08:00
guanj
cfcbfc45d6 修改台账 2026-04-13 10:48:32 +08:00
guanj
2601068a55 1 2026-04-08 15:51:46 +08:00
guanj
3ffb11defa 调整台账 2026-04-03 14:47:36 +08:00
guanj
0b9aafc1b5 联调文件管理页面 2026-04-02 09:08:57 +08:00
guanj
762965b1e4 联调设备文件 2026-03-30 09:03:53 +08:00
guanj
a30379ab01 绘制 运维版本管理页面 2026-03-19 11:29:26 +08:00
dk
9f1fbf93cd 新增实时运维页面 2026-03-18 21:06:48 +08:00
dk
b5fc946ce2 测试修改提交 2026-03-17 14:32:14 +08:00
guanj
1171d37a86 添加工程树 2026-03-06 09:36:42 +08:00
guanj
3fdb41c468 修改测试bug 2026-02-04 09:35:24 +08:00
109 changed files with 7666 additions and 4738 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
}) })
} }
// 监测点列表治理 // 监测点列表治理

View File

@@ -79,4 +79,12 @@ export const addProject = (data: any) => {
data: data 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

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

@@ -8,7 +8,7 @@ export function getMakeUpData(data: any) {
}) })
} }
//查询装置目录-文件 //查询设备目录-文件
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}`,

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

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

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

@@ -147,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
@@ -214,18 +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'
tooltip: { // tooltip: {
show: true, // show: true,
trigger: 'item', // trigger: 'item',
formatter: function (params: any) { // formatter: function (params: any) {
return `<strong>可容忍事件</strong><br/> // return `<strong>可容忍事件</strong><br/>
持续时间: ${params.value[0]}s<br/> // 持续时间: ${params.value[0]}s<br/>
特征幅值: ${params.value[1].toFixed(2)}%<br/> // 特征幅值: ${params.value[1].toFixed(2)}%<br/>
发生时间: ${params.value[2] || 'N/A'}` // 发生时间: ${params.value[2] || 'N/A'}`
} // }
} // }
}, },
{ {
name: '不可容忍事件', name: '不可容忍事件',

View File

@@ -47,7 +47,6 @@ const init = () => {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
formatter: (params: any) => { formatter: (params: any) => {
console.log('🚀 ~ init ~ params:', params)
if (!params || params.length === 0) return '' if (!params || params.length === 0) return ''
// 使用第一个项目的轴标签作为时间标题 // 使用第一个项目的轴标签作为时间标题
@@ -90,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 = {
@@ -107,6 +104,7 @@ const initData = async (row: any) => {
return a.phasic.localeCompare(b.phasic) return a.phasic.localeCompare(b.phasic)
}) })
.map((item: any) => { .map((item: any) => {
const xAxisData = item.time.split(',')
const values = xAxisData.map((time: string, index: number) => { const values = xAxisData.map((time: string, index: number) => {
// 将传入的日期与时间拼接成完整的时间字符串 // 将传入的日期与时间拼接成完整的时间字符串
const fullTime = `${row.time} ${time}` const fullTime = `${row.time} ${time}`

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,9 +19,10 @@ 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] },
@@ -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) => {
@@ -118,7 +107,7 @@ const tableStore: any = new TableStore({
field: 'extent', field: 'extent',
minWidth: '70', minWidth: '70',
formatter: (row: any) => { formatter: (row: any) => {
return Math.floor(row.cellValue * 100) / 100 return Math.floor(row.cellValue * 100) / 100
} }
}, },
{ {
@@ -143,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: {
@@ -175,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
} }
] ]
@@ -188,15 +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) { if (column.field == 'maxValue') {
nextTick(() => { if (row.lineId == null) {
dailyTrendChartRef.value.open(row) 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, ""))
})
})
}
} }
} }
@@ -218,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()
@@ -238,6 +243,6 @@ watch(
} }
) )
const addMenu = () => {} const addMenu = () => { }
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -1,15 +1,37 @@
<template> <template>
<div> <div>
<!--治理效果报表 --> <!--治理效果报表 -->
<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
<el-option v-for="item in templateList" :key="item.id" :label="item.excelName" :value="item.id" /> filterable
v-model="tableStore.table.params.tempId"
placeholder="请选择模板策略"
clearable
>
<el-option
v-for="item in templateList"
:key="item.id"
:label="item.excelName"
:value="item.id"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="监测对象"> <el-form-item label="监测对象">
<el-select filterable v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象" clearable> <el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -43,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 }
}) })
@@ -59,13 +81,21 @@ const idList = ref()
// 监测对象 // 监测对象
const initListByIds = () => { const initListByIds = () => {
getListByIds({}).then((res: any) => { getListByIds({}).then((res: any) => {
if (res.data.length > 0) { if (res.data?.length > 0) {
idList.value = res.data idList.value = res.data
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) { if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id tableStore.table.params.sensitiveUserId = idList.value[0].id
} }
templateListData() templateListData()
} else {
querySysExcel({}).then(res => {
templateList.value = res.data.filter(item => item.excelType == 4)
if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
tableStore.table.params.tempId = templateList.value[0].id
}
})
tableStore.table.loading = false
} }
}) })
} }

View File

@@ -93,15 +93,7 @@ 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: '谐波电压越限(%)',
children: loop50('uharm')
},
{
title: '谐波电流越限(%)',
children: loop50('iharm')
},
{
title: '电压偏差越限(%)', title: '电压偏差越限(%)',
field: 'voltageDevOvertime', field: 'voltageDevOvertime',
width: '100', width: '100',
@@ -119,6 +111,15 @@ const tableStore: any = new TableStore({
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
} }
}, },
{
title: '谐波电压越限(%)',
children: loop50('uharm')
},
{
title: '谐波电流越限(%)',
children: loop50('iharm')
},
// { // {
// title: '频率偏差越限(%)', // title: '频率偏差越限(%)',

View File

@@ -186,7 +186,7 @@ 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
} }
}, },
@@ -228,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

@@ -185,7 +185,7 @@ 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
} }
}, },
@@ -227,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

@@ -93,15 +93,7 @@ const tableStore: any = new TableStore({
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: '谐波电压越限(分钟)',
children: loop50('uharm')
},
{
title: '谐波电流越限(分钟)',
children: loop50('iharm')
}, {
title: '电压偏差越限(分钟)', title: '电压偏差越限(分钟)',
field: 'uaberranceOvertime', field: 'uaberranceOvertime',
width: '100', width: '100',
@@ -119,6 +111,14 @@ const tableStore: any = new TableStore({
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
} }
}, },
{
title: '谐波电压越限(分钟)',
children: loop50('uharm')
},
{
title: '谐波电流越限(分钟)',
children: loop50('iharm')
},
// { // {
// title: '频率偏差越限(分钟)', // title: '频率偏差越限(分钟)',

View File

@@ -73,15 +73,7 @@ const tableStore: any = new TableStore({
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,7 +87,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

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

@@ -150,6 +150,7 @@ const indicatorList = ref()
const initLineList = async () => { const initLineList = async () => {
cslineList({}).then(res => { cslineList({}).then(res => {
setTime()
if (res.data.length == 0) { if (res.data.length == 0) {
lineShow.value = false lineShow.value = false
return (tableStore.table.loading = false) return (tableStore.table.loading = false)

View File

@@ -93,15 +93,7 @@ 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: '谐波电压越限(%)',
children: loop50('uharm')
},
{
title: '谐波电流越限(%)',
children: loop50('iharm')
},
{
title: '电压偏差越限(%)', title: '电压偏差越限(%)',
field: 'voltageDevOvertime', field: 'voltageDevOvertime',
width: '100', width: '100',
@@ -119,6 +111,15 @@ const tableStore: any = new TableStore({
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
} }
}, },
{
title: '谐波电压越限(%)',
children: loop50('uharm')
},
{
title: '谐波电流越限(%)',
children: loop50('iharm')
},
// { // {
// title: '频率偏差越限(%)', // title: '频率偏差越限(%)',

View File

@@ -5,10 +5,22 @@
ref="TableHeaderRef" ref="TableHeaderRef"
:showReset="false" :showReset="false"
@selectChange="selectChange" @selectChange="selectChange"
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"
@@ -189,7 +201,7 @@ const tableStore: any = new TableStore({
field: 'volGrade', field: 'volGrade',
minWidth: '80', minWidth: '80',
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue==0?'/': row.cellValue+'kV' || '/' return row.cellValue == 0 ? '/' : row.cellValue + 'kV' || '/'
} }
}, },
{ {
@@ -236,7 +248,8 @@ const tableStore: any = new TableStore({
} }
}, },
{ {
title: '操作', fixed: 'right', title: '操作',
fixed: 'right',
width: 150, width: 150,
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
@@ -294,6 +307,7 @@ 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 = () => {
@@ -304,7 +318,6 @@ const setTime = () => {
// ? [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]

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"
@@ -107,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)
@@ -268,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) => {
@@ -407,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 = ''
@@ -651,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

@@ -92,14 +92,6 @@ const tableStore: any = new TableStore({
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: '谐波电压越限(%)',
children: loop50('uharm')
},
{
title: '谐波电流越限(%)',
children: loop50('iharm')
}, },
{ {
title: '电压偏差越限(%)', title: '电压偏差越限(%)',
@@ -119,7 +111,16 @@ const tableStore: any = new TableStore({
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>` return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
} }
}, },
{
title: '谐波电压越限(%)',
children: loop50('uharm')
},
{
title: '谐波电流越限(%)',
children: loop50('iharm')
},
// { // {
// title: '频率偏差越限(%)', // title: '频率偏差越限(%)',
// field: 'freqDevOvertime', // field: 'freqDevOvertime',

View File

@@ -5,10 +5,22 @@
ref="TableHeaderRef" ref="TableHeaderRef"
:showReset="false" :showReset="false"
@selectChange="selectChange" @selectChange="selectChange"
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"
@@ -66,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: [
@@ -115,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)
@@ -137,7 +149,6 @@ const setTime = () => {
// ? [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]

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
@@ -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
@@ -201,9 +220,10 @@ const tableStore: any = new TableStore({
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => {} loadCallback: () => {}
}) })
tableStore.table.params.eventType = ''
provide('tableStore', tableStore) provide('tableStore', tableStore)
const open = async (row: any, searchBeginTime: any, searchEndTime: any) => { const open = async (row: any, searchBeginTime: any, searchEndTime: any) => {
tableStore.table.params.eventType = ''
dialogVisible.value = true dialogVisible.value = true
getSimpleLineList() getSimpleLineList()
tableStore.table.params.lineId = row.id tableStore.table.params.lineId = row.id

View File

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

@@ -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.2 || 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,
@@ -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
@@ -1105,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

@@ -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',
@@ -512,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: [
{ {
@@ -789,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',
@@ -820,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

@@ -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="mt10 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 - 267px)' }" :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()
@@ -183,7 +187,5 @@ defineExpose({ treeRef, loading })
.custom-tree-node { .custom-tree-node {
display: flex; display: flex;
align-items: center; align-items: center;
} }
</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>

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)'
}" }"
@@ -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')
@@ -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)
@@ -362,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>
@@ -412,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,11 +1,19 @@
<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 { querySysExcel } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
@@ -22,136 +30,61 @@ 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 = () => {

View File

@@ -1,11 +1,19 @@
<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"
:data="tree"
default-expand-all
@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,lineTree } from '@/api/cs-device-boot/csLedger' import { getLineTree, lineTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
@@ -22,27 +30,26 @@ 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[] = []
lineTree().then(res => { lineTree().then(res => {
try { try {
// 检查响应数据结构 // 检查响应数据结构
let rootData = null; let rootData = null
if (Array.isArray(res.data)) { if (Array.isArray(res.data)) {
// 旧的数据结构 - 数组 // 旧的数据结构 - 数组
rootData = res.data.find((item: any) => item.name == '监测设备'); rootData = res.data.find((item: any) => item.name == '监测设备')
} else if (res.data && res.data.name == '监测设备') { } else if (res.data && res.data.name == '监测设备') {
// 新的数据结构 - 单个对象 // 新的数据结构 - 单个对象
rootData = res.data; rootData = res.data
} }
// 治理设备 // 治理设备
@@ -66,7 +73,7 @@ const info = (selectedNodeId?: string) => {
item2.icon = 'el-icon-List' item2.icon = 'el-icon-List'
item2.level = 2 item2.level = 2
item2.color = config.getColorVal('elementUiPrimary') item2.color = config.getColorVal('elementUiPrimary')
// 确保 children 是数组 // 确保 children 是数组
if (!Array.isArray(item2.children)) { if (!Array.isArray(item2.children)) {
item2.children = [] item2.children = []
@@ -76,12 +83,12 @@ const info = (selectedNodeId?: string) => {
item3.level = 3 item3.level = 3
item3.color = item3.color =
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important' item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
// 确保 children 是数组 // 确保 children 是数组
if (!Array.isArray(item3.children)) { if (!Array.isArray(item3.children)) {
item3.children = [] item3.children = []
} }
item3.children.forEach((item4: any) => { item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Platform' item4.icon = 'el-icon-Platform'
item4.level = 4 item4.level = 4
@@ -97,50 +104,51 @@ const info = (selectedNodeId?: string) => {
tree.value = [] tree.value = []
} }
nextTick(() => { nextTick(() => {
if (arr1.length) { if (arr1.length) {
// 安全检查 treRef 和 treeRef 是否存在 // 安全检查 treRef 和 treeRef 是否存在
console.log("🚀 ~ info ~ treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey:", treRef.value && treRef.value.treeRef1 && treRef.value.treeRef1.setCurrentKey) 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) { if (treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey) {
// 如果传入了要选中的节点ID则选中该节点否则选中第一个节点 // 如果传入了要选中的节点ID则选中该节点否则选中第一个节点
console.log('selectedNodeId:', selectedNodeId); console.log('selectedNodeId:', selectedNodeId)
if (selectedNodeId) { if (selectedNodeId) {
treRef.value.treeRef.setCurrentKey(selectedNodeId); treRef.value.treeRef.setCurrentKey(selectedNodeId)
// 查找对应的节点数据并触发事件 // 查找对应的节点数据并触发事件
let selectedNode = null; let selectedNode = null
const findNode = (nodes: any[]) => { const findNode = (nodes: any[]) => {
for (const node of nodes) { for (const node of nodes) {
if (node.id === selectedNodeId) { if (node.id === selectedNodeId) {
selectedNode = node; selectedNode = node
return true; return true
} }
if (node.children && findNode(node.children)) { if (node.children && findNode(node.children)) {
return true; return true
} }
} }
return false; return false
}; }
findNode(tree.value); findNode(tree.value)
if (selectedNode) { if (selectedNode) {
emit('init', { emit('init', {
level: selectedNode.level, level: selectedNode.level,
...selectedNode ...selectedNode
}); })
} }
} else { } else {
// 初始化选中第一个节点 // 初始化选中第一个节点
treRef.value.treeRef.setCurrentKey(arr1[0].id); treRef.value.treeRef.setCurrentKey(arr1[0].id)
emit('init', { emit('init', {
level: 2, level: 2,
...arr1[0] ...arr1[0]
}); })
} }
} }
} else { } else {
} }
}) })
} catch (error) { } catch (error) {
@@ -149,7 +157,6 @@ const info = (selectedNodeId?: string) => {
}) })
} }
const changePointType = (val: any, obj: any) => { const changePointType = (val: any, obj: any) => {
emit('pointTypeChange', val, obj) emit('pointTypeChange', val, obj)
} }
@@ -158,7 +165,8 @@ const onAdd = () => {
emit('onAdd') emit('onAdd')
} }
if (props.template) { if (props.template) {
querySysExcel({ 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

@@ -7,6 +7,8 @@
:data="tree" :data="tree"
:height="props.height" :height="props.height"
@changeDeviceType="changeDeviceType" @changeDeviceType="changeDeviceType"
@changeTreeType="info"
:engineering="props.engineering"
/> />
</template> </template>
@@ -16,6 +18,7 @@ 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' import { throttle } from 'lodash'
defineOptions({ defineOptions({
name: 'govern/deviceTree' name: 'govern/deviceTree'
}) })
@@ -24,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'])
@@ -38,112 +43,149 @@ const treRef = ref()
const changeDeviceType = (val: any, obj: any) => { const changeDeviceType = (val: any, obj: any) => {
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.pName = '治理设备' item2.icon = 'el-icon-Platform'
item3.icon = 'el-icon-Platform' item2.color = config.getColorVal('elementUiPrimary')
item3.level = 2 item2.color =
item3.color = config.getColorVal('elementUiPrimary') item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
if (item3.comFlag === 1) { arr4.push(item2)
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)
})
})
})
}
})
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( throttle(

View File

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

View File

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

View File

@@ -1,5 +1,12 @@
<template> <template>
<Tree ref="treRef" :width="width" :data="tree" default-expand-all @changePointType="changePointType" /> <Tree
ref="treRef"
:width="width"
:data="tree"
default-expand-all
@changePointType="changePointType"
@changeTreeType="info"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -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) {
querySysExcel({ 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>
@@ -157,7 +151,8 @@ const clickNode = (e: anyObj) => {
} }
if (props.template) { if (props.template) {
querySysExcel({ 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

@@ -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
@@ -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,6 +202,17 @@ 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>([])
@@ -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)
@@ -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) => {
@@ -299,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 {
@@ -381,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>
@@ -432,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>

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

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

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

View File

@@ -1,5 +1,5 @@
<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='角色名称'>

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

View File

@@ -4,7 +4,7 @@ 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
@@ -13,7 +13,7 @@ const loadingInstance: LoadingInstance = {
target: null, target: null,
count: 0 count: 0
} }
const navTabs = useNavTabs()
/** /**
* 根据运行环境获取基础请求URL * 根据运行环境获取基础请求URL
*/ */
@@ -76,6 +76,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
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'
) )
) )
@@ -164,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) // 可根据实际情况调整延迟时间

View File

@@ -57,7 +57,7 @@
<el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" /> <el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" />
</el-form-item> </el-form-item>
<el-form-item label="时间段" prop="limitTime"> <el-form-item label="时间段" prop="limitTime">
<el-slider v-model.trim="form.limitTime" style="width: 95%" range show-stops :max="24" /> <el-slider v-model.trim="form.limitTime" style="width: 95%" range :max="24" />
</el-form-item> </el-form-item>
<el-form-item label="起始IP" prop="limitIpStart"> <el-form-item label="起始IP" prop="limitIpStart">
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" /> <el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" />

View File

@@ -100,6 +100,7 @@ const tableStore = new TableStore({
} }
}, },
{ title: '设备名称', field: 'equipmentName', align: 'center', width: 120 }, { title: '设备名称', field: 'equipmentName', align: 'center', width: 120 },
{ title: '监测点名称', field: 'lineName', align: 'center', width: 140 },
{ title: '工程名称', field: 'engineeringName', align: 'center', width: 120 }, { title: '工程名称', field: 'engineeringName', align: 'center', width: 120 },
{ title: '项目名称', field: 'projectName', align: 'center', width: 120 }, { title: '项目名称', field: 'projectName', align: 'center', width: 120 },
{ title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true }, { title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true },

View File

@@ -1,5 +1,5 @@
<template> <template>
<div ref="refheader" v-show="!isWaveCharts" style="width: 100%;"> <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="数据来源">
@@ -141,10 +141,11 @@ const tableStore = new TableStore({
{ title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center', sortable: true }, { title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center', sortable: true },
{ {
title: '操作', fixed: 'right', title: '操作',
fixed: 'right',
align: 'center', align: 'center',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -159,12 +160,12 @@ const tableStore = new TableStore({
}, },
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.persistTime = row.evtParamTm
boxoList.value.featureAmplitude = boxoList.value.featureAmplitude =

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,8 +30,9 @@ 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('0') 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) => {

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>

View File

@@ -0,0 +1,440 @@
<template>
<div class="default-main" style="display: flex" :style="height">
<div style="width: 400px; overflow: hidden">
<div class="custom-table-header">
<div class="title">方案列表</div>
<el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增</el-button>
</div>
<Table ref="tableRef" :row-config="{ isCurrent: true, isHover: true }" @currentChange="currentChange" />
</div>
<div style="flex: 1; overflow: hidden">
<div class="custom-table-header">
<div class="title">指标配置</div>
<!-- <el-button :icon="Select" type="primary" @click="saveIndicator" class="ml10">保存</el-button> -->
</div>
<div class="pd10 borderBox" :style="height1" style="overflow-y: auto">
<H4 class="mt10">基础指标</H4>
<el-checkbox-group v-model="indicator">
<el-checkbox label="频率偏差" value="频率偏差" />
<el-checkbox label="电压偏差" value="电压偏差" />
<el-checkbox label="三相电压不平衡度" value="三相电压不平衡度" />
<el-checkbox label="闪变" value="闪变" />
<el-checkbox label="电压总谐波畸变率" value="电压总谐波畸变率" />
<el-checkbox label="负序电流" value="负序电流" />
</el-checkbox-group>
<H4 class="mt10">谐波电压</H4>
<div class="df">
<div class="title">谐波电压含有率:</div>
<div style="flex: 1">
<el-checkbox v-model="checkAll1" @change="handleCheckAllChange1">全选</el-checkbox>
<el-checkbox-group v-model="indicator" @change="handleHarmonicVoltageChange">
<el-checkbox
v-for="num in 24"
:key="`harmonicVoltage_${num + 1}`"
:label="`${num + 1}次`"
:value="`${num + 1}次谐波电压含有率`"
/>
</el-checkbox-group>
</div>
</div>
<H4 class="mt10">谐波电流</H4>
<div class="df">
<div class="title">谐波电流有效值:</div>
<div style="flex: 1">
<el-checkbox v-model="checkAll2" @change="handleCheckAllChange2">全选</el-checkbox>
<el-checkbox-group v-model="indicator" @change="handleHarmonicCurrentChange">
<el-checkbox
v-for="num in 24"
:key="`harmonicCurrent_${num + 1}`"
:label="`${num + 1}次`"
:value="`${num + 1}次谐波电流有效值`"
/>
</el-checkbox-group>
</div>
</div>
<H4 class="mt10">间谐波电压</H4>
<div class="df">
<div class="title">间谐波电压含有率:</div>
<div style="flex: 1">
<el-checkbox v-model="checkAll3" @change="handleCheckAllChange3">全选</el-checkbox>
<el-checkbox-group v-model="indicator" @change="handleInterharmonicVoltageChange">
<el-checkbox
v-for="num in 16"
:key="`interharmonicVoltage_${num}`"
:label="`${num - 0.5}次`"
:value="`${num - 0.5}次间谐波电压含有率`"
/>
</el-checkbox-group>
</div>
</div>
</div>
</div>
<div style="width: 370px">
<div class="custom-table-header">
<div class="title">监测点绑定</div>
<el-button :icon="Select" type="primary" @click="saveIndicator" class="ml10" :loading="loading">
保存
</el-button>
</div>
<!-- <steadyStateTree /> -->
<Tree
class="borderBox"
ref="treeRef"
show-checkbox
:showBut="false"
width="370px"
:height="260"
:data="menuTree"
:checkStrictly="checkStrictly"
></Tree>
</div>
<!-- 新增/编辑弹框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="400px" :before-close="handleClose">
<el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px">
<el-form-item label="方案名称" prop="name">
<el-input v-model.trim="formData.name" placeholder="请输入方案名称" clearable />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.number="formData.sort" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { Plus, Select } from '@element-plus/icons-vue'
import { ref, onMounted, provide, computed } from 'vue'
import TableStore from '@/utils/tableStore'
import { getLineTree, getCldTree } from '@/api/cs-device-boot/csLedger'
import Table from '@/components/table/index.vue'
import { save, deletePlan, update, getById } from '@/api/cs-harmonic-boot/mxgraph'
import { mainHeight } from '@/utils/layout'
import { ElMessage } from 'element-plus'
import steadyStateTree from '@/components/tree/govern/steadyStateTree.vue'
import { useConfig } from '@/stores/config'
import Tree from '@/components/tree/allocation.vue'
import { id } from 'element-plus/es/locale'
defineOptions({
name: 'govern/steadyStateEvent'
})
const height = mainHeight(20)
const height1 = mainHeight(90)
const config = useConfig()
const tableRef = ref()
const checkAll1 = ref(false)
const checkAll2 = ref(false)
const checkAll3 = ref(false)
const menuTree: any = ref([])
const checkStrictly = ref(false)
const indicator: any = ref([])
const lineList: any = ref([])
const copyRow: any = ref({})
const treeRef = ref()
const loading = ref(false)
// 弹框相关
const dialogVisible = ref(false)
const dialogTitle = ref('新增方案')
const formRef = ref()
const formData = ref({
id: '',
sort: 0,
lineList: [],
harmonicTarget: '',
name: ''
})
const formRules = {
name: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
}
// 生成各分组的指标值数组
const harmonicVoltageValues = computed(() => Array.from({ length: 24 }, (_, i) => `${i + 2}次谐波电压含有率`))
const harmonicCurrentValues = computed(() => Array.from({ length: 24 }, (_, i) => `${i + 2}次谐波电流有效值`))
const interharmonicVoltageValues = computed(() => Array.from({ length: 16 }, (_, i) => `${i + 0.5}次间谐波电压含有率`))
// 谐波电压全选/取消全选
const handleCheckAllChange1 = (val: any) => {
if (val) {
const newValues = harmonicVoltageValues.value.filter(v => !indicator.value.includes(v))
indicator.value = [...indicator.value, ...newValues]
} else {
indicator.value = indicator.value.filter(v => !harmonicVoltageValues.value.includes(v))
}
}
// 谐波电流全选/取消全选
const handleCheckAllChange2 = (val: any) => {
if (val) {
const newValues = harmonicCurrentValues.value.filter(v => !indicator.value.includes(v))
indicator.value = [...indicator.value, ...newValues]
} else {
indicator.value = indicator.value.filter(v => !harmonicCurrentValues.value.includes(v))
}
}
// 间谐波电压全选/取消全选
const handleCheckAllChange3 = (val: any) => {
if (val) {
const newValues = interharmonicVoltageValues.value.filter(v => !indicator.value.includes(v))
indicator.value = [...indicator.value, ...newValues]
} else {
indicator.value = indicator.value.filter(v => !interharmonicVoltageValues.value.includes(v))
}
}
// 谐波电压分组内值变化时,更新全选状态
const handleHarmonicVoltageChange = () => {
const selectedInGroup = harmonicVoltageValues.value.filter(v => indicator.value.includes(v))
checkAll1.value =
selectedInGroup.length == harmonicVoltageValues.value.length && harmonicVoltageValues.value.length > 0
}
// 谐波电流分组内值变化时,更新全选状态
const handleHarmonicCurrentChange = () => {
const selectedInGroup = harmonicCurrentValues.value.filter(v => indicator.value.includes(v))
checkAll2.value =
selectedInGroup.length == harmonicCurrentValues.value.length && harmonicCurrentValues.value.length > 0
}
// 间谐波电压分组内值变化时,更新全选状态
const handleInterharmonicVoltageChange = () => {
const selectedInGroup = interharmonicVoltageValues.value.filter(v => indicator.value.includes(v))
checkAll3.value =
selectedInGroup.length == interharmonicVoltageValues.value.length && interharmonicVoltageValues.value.length > 0
}
const tableStore = new TableStore({
showPage: false,
url: '/cs-harmonic-boot/csHarmonicPlan/list',
method: 'GET',
publicHeight: 70,
column: [
{ title: '方案名称', field: 'name', align: 'center' },
{
title: '操作',
fixed: 'right',
align: 'center',
width: '100',
render: 'buttons',
buttons: [
{
name: 'edit',
title: '编辑',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
openDialog('edit', row)
}
},
{
name: 'del',
title: '删除',
type: 'danger',
icon: 'el-icon-Delete',
render: 'confirmButton',
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定删除该角色吗?'
},
click: row => {
deletePlan([row.id]).then(() => {
ElMessage.success('删除成功')
tableStore.index()
})
}
}
]
}
],
beforeSearchFun: () => {},
loadCallback: () => {
tableRef.value.getRef().setCurrentRow(tableStore.table.data[0])
currentChange({
row: tableStore.table.data[0]
})
}
})
tableStore.table.params.searchValue = ''
getCldTree().then(res => {
res.data?.children.map((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item: any) => {
item.icon = 'el-icon-List'
item.color = config.getColorVal('elementUiPrimary')
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 = item3.comFlag == 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
})
})
})
})
menuTree.value = filterTreeKeepPath(res.data.children)
})
provide('tableStore', tableStore)
const filterTreeKeepPath = (treeData: any) => {
const result = []
for (const node of treeData) {
// 递归处理子节点
const filteredChildren: any = node.children?.length ? filterTreeKeepPath(node.children) : []
// 如果当前节点 level == 3或者子节点中有符合条件的数据
if (node.level == 3 || filteredChildren.length > 0) {
result.push({
...node,
children: filteredChildren
})
}
}
return result
}
// 打开弹框
const openDialog = (type: string, row?: any) => {
if (type == 'add') {
dialogTitle.value = '新增方案'
formData.value = {
id: '',
name: '',
lineList: [],
harmonicTarget: '',
sort: 0
}
} else if (type == 'edit') {
dialogTitle.value = '编辑方案'
formData.value = {
id: row.id,
lineList: row.lineList,
harmonicTarget: row.harmonicTarget,
sort: row.sort,
name: row.name
}
}
dialogVisible.value = true
}
// 关闭弹框
const handleClose = () => {
dialogVisible.value = false
formRef.value?.resetFields()
}
// 提交表单
const submitForm = async () => {
await formRef.value?.validate()
if (dialogTitle.value == '新增方案') {
await save({
harmonicTarget: '',
id: '',
lineList: [],
name: formData.value.name,
sort: formData.value.sort
}).then((res: any) => {
ElMessage.success(`${dialogTitle.value}成功`)
dialogVisible.value = false
tableStore.index() // 刷新列表
})
} else {
await update({
harmonicTarget: formData.value.harmonicTarget,
id: formData.value.id,
lineList: formData.value.lineList,
name: formData.value.name,
sort: formData.value.sort
}).then((res: any) => {
ElMessage.success(`${dialogTitle.value}成功`)
dialogVisible.value = false
tableStore.index() // 刷新列表
})
}
// 模拟保存成功
}
// 保存指标
const saveIndicator = () => {
// TODO: 调用保存指标接口
loading.value = true
update({
harmonicTarget: indicator.value.join(','),
id: copyRow.value.id,
lineList: treeRef.value.treeRef
.getCheckedNodes(false, true)
.filter(item => item.level == 3)
.map((node: any) => node.id),
name: copyRow.value.name,
sort: copyRow.value.sort
})
.then((res: any) => {
ElMessage.success(`保存成功`)
loading.value = false
tableStore.index()
})
.catch(() => {
loading.value = false
})
}
const currentChange = (data: any) => {
getById({ id: data.row.id }).then((res: any) => {
checkStrictly.value = true
indicator.value = res.data.harmonicTarget.split(',')
lineList.value = res.data.lineList || []
copyRow.value = res.data
treeRef.value.treeRef.setCheckedKeys(lineList.value)
setTimeout(() => {
handleInterharmonicVoltageChange()
handleHarmonicCurrentChange()
handleHarmonicVoltageChange()
checkStrictly.value = false
}, 100)
})
}
onMounted(() => {
tableStore.index()
})
const addRole = () => {
openDialog('add')
}
</script>
<style lang="scss" scoped>
:deep(.el-tabs--border-card > .el-tabs__content) {
padding: 0px !important;
}
.df {
display: flex;
.title {
width: 125px;
}
:deep(.el-checkbox) {
width: 70px;
}
}
.custom-table-header {
height: 60px;
}
.borderBox {
border: 1px solid var(--el-border-color);
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,298 +1,297 @@
<template> <template>
<div class="device-control-detail child-router"> <div class="device-control-detail child-router">
<TableHeader :showSearch="false"> <TableHeader :showSearch="false">
<template #select> <template #select>
<el-form-item label="日期"> <el-form-item label="日期">
<DatePicker ref="datePickerRef"></DatePicker> <DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> </el-form-item>
<el-form-item label="值类型"> <el-form-item label="值类型">
<el-select v-model.trim="form.dataLevel" :disabled="props.dataLevel == 'Primary'"> <el-select v-model.trim="form.dataLevel" :disabled="props.dataLevel == 'Primary'">
<el-option value="Primary" label="一次值"></el-option> <el-option value="Primary" label="一次值"></el-option>
<el-option value="Secondary" label="二次值"></el-option> <el-option value="Secondary" label="二次值"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="数据类型" label-width="80px"> <el-form-item label="数据类型" label-width="80px">
<el-select v-model.trim="form.statMethod" placeholder="请选择数据类型"> <el-select v-model.trim="form.statMethod" placeholder="请选择数据类型">
<el-option v-for="item in typeOptions" :key="item.id" :label="item.name" <el-option v-for="item in typeOptions" :key="item.id" :label="item.name"
:value="item.id"></el-option> :value="item.id"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</template> </template>
<template #operation> <template #operation>
<el-button type="primary" icon="el-icon-Search" @click="init">查询</el-button> <el-button type="primary" icon="el-icon-Search" @click="init">查询</el-button>
<el-button icon="el-icon-Back" @click="$emit('close')">返回</el-button> <el-button icon="el-icon-Back" @click="$emit('close')">返回</el-button>
</template> </template>
</TableHeader> </TableHeader>
<MyEchart :options="echartsData" v-if="echartsData" style="flex: 1" class="mt10" /> <MyEchart :options="echartsData" v-if="echartsData" style="flex: 1" class="mt10" />
<el-empty description="暂无数据" v-else style="flex: 1" v-loading="loading"></el-empty> <el-empty description="暂无数据" v-else style="flex: 1" v-loading="loading"></el-empty>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, inject, nextTick, onMounted } from 'vue' import { ref, inject, nextTick, onMounted } from 'vue'
import { reactive } from 'vue' import { reactive } from 'vue'
import DatePicker from '@/components/form/datePicker/index.vue' import DatePicker from '@/components/form/datePicker/index.vue'
import { getDeviceDataTrend } from '@/api/cs-harmonic-boot/datatrend' import { getDeviceDataTrend } from '@/api/cs-harmonic-boot/datatrend'
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 { yMethod, exportCSV } from '@/utils/echartMethod' import { yMethod, exportCSV } from '@/utils/echartMethod'
import { ITEM_RENDER_EVT } from 'element-plus/es/components/virtual-list/src/defaults' import { ITEM_RENDER_EVT } from 'element-plus/es/components/virtual-list/src/defaults'
interface Props { interface Props {
detail: anyObj detail: anyObj
dataLevel: string dataLevel: string
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
detail: () => { detail: () => {
return {} return {}
}, },
dataLevel: () => { dataLevel: () => {
return '' return ''
} }
}) })
const datePickerRef = ref() const datePickerRef = ref()
const form: any = reactive({ const form: any = reactive({
code: '', code: '',
icon: '', icon: '',
id: '', id: '',
name: '', name: '',
path: '', path: '',
pid: '0', pid: '0',
remark: '', remark: '',
routeName: '', routeName: '',
sort: 100, sort: 100,
dataLevel: '', dataLevel: '',
statMethod: 'avg' statMethod: 'avg'
}) })
const typeOptions = [ const typeOptions = [
{ {
name: '平均值', name: '平均值',
id: 'avg' id: 'avg'
}, },
{ {
name: '最大值', name: '最大值',
id: 'max' id: 'max'
}, },
{ {
name: '最小值', name: '最小值',
id: 'min' id: 'min'
}, },
{ {
name: 'CP95值', name: 'CP95值',
id: 'cp95' id: 'cp95'
} }
] ]
const echartsData = ref<any>(null) const echartsData = ref<any>(null)
const dialogVisible = ref(false) const dialogVisible = ref(false)
const loading = ref(true) const loading = ref(true)
onMounted(() => { onMounted(() => {
if (props.dataLevel == 'Secondary') { if (props.dataLevel == 'Secondary') {
form.dataLevel = 'Primary' form.dataLevel = 'Primary'
} else { } else {
form.dataLevel = props.dataLevel form.dataLevel = props.dataLevel
} }
init() init()
}) })
const init = () => { const init = () => {
echartsData.value = null echartsData.value = null
loading.value = true loading.value = true
// console.log(props.detail.children, 'props.detail.children') // console.log(props.detail.children, 'props.detail.children')
let statisticalParams = props.detail.children let statisticalParams = props.detail.children
statisticalParams[0].statMethod = form.statMethod statisticalParams[0].statMethod = form.statMethod
getDeviceDataTrend({ getDeviceDataTrend({
devId: props.detail.devId, devId: props.detail.devId,
endTime: datePickerRef.value.timeValue[1], endTime: datePickerRef.value.timeValue[1],
lineId: props.detail.lineId, lineId: props.detail.lineId,
startTime: datePickerRef.value.timeValue[0], startTime: datePickerRef.value.timeValue[0],
statisticalParams: statisticalParams, statisticalParams: statisticalParams,
dataLevel: form.dataLevel, dataLevel: form.dataLevel,
statMethod: form.statMethod statMethod: form.statMethod
}).then(res => { }).then(res => {
if (res.data.length && res.data[0].length) { if (res.data.length && res.data[0].length) {
let arr: any[] = [] let arr: any[] = []
res.data.forEach((item: any[]) => { res.data.forEach((item: any[]) => {
arr.push(...item) arr.push(...item)
}) })
let [min, max] = yMethod(arr.map((item: any) => item.statisticalData.toFixed(2))) let [min, max] = yMethod(arr.map((item: any) => item.statisticalData.toFixed(2)))
echartsData.value = { echartsData.value = {
options: { options: {
legend: { legend: {
right: 70, right: 70,
top: 5, top: 5,
data: res.data.map((item: any[]) => { data: res.data.map((item: any[]) => {
return item[0]?.anotherName return item[0]?.anotherName
}) })
}, },
grid: { grid: {
top: '60px', top: '60px',
left: '10px', left: '10px',
right: '20px', right: '20px',
bottom: '40px', bottom: '40px',
containLabel: true containLabel: true
}, },
series: res.data.map((item: any) => { series: res.data.map((item: any) => {
return { return {
data: item.map((item: any, i: any) => { data: item.map((item: any, i: any) => {
return [res.data[0][i]?.time, item.statisticalData.toFixed(2)] return [res.data[0][i]?.time, item.statisticalData.toFixed(2)]
}), }),
// data: [ // data: [
// [1584086222000, '573'], // [1584086222000, '573'],
// [1584086342000, '57'], // [1584086342000, '57'],
// [1584086462000, '56'] // [1584086462000, '56']
// ], // ],
// markPoint: { // markPoint: {
// symbol: 'circle', // symbol: 'circle',
// symbolSize: 0, // symbolSize: 0,
// label: { // label: {
// show: false // show: false
// }, // },
// data: [ // data: [
// { type: 'max', name: 'Max' }, // { type: 'max', name: 'Max' },
// { type: 'min', name: 'Min' } // { type: 'min', name: 'Min' }
// ] // ]
// }, // },
type: 'line', type: 'line',
showSymbol: false, showSymbol: false,
smooth: true, smooth: true,
name: item[0]?.anotherName name: item[0]?.anotherName
} }
}) })
}, },
title: { title: {
text: props.detail.name?.split('(')[0], text: props.detail.name?.split('(')[0],
}, },
tooltip: { tooltip: {
axisPointer: { axisPointer: {
type: 'cross', type: 'cross',
label: { label: {
color: '#fff', color: '#fff',
fontSize: 16 fontSize: 16
} }
}, },
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontStyle: 'normal', fontStyle: 'normal',
opacity: 0.35, opacity: 0.35,
fontSize: 14 fontSize: 14
}, },
backgroundColor: 'rgba(0,0,0,0.55)', borderWidth: 0
borderWidth: 0 },
},
yAxis: {
yAxis: { name: `单位:(${arr[0].unit == null ? ' / ' : arr[0].unit})`,
name: `单位:(${arr[0].unit == null ? ' / ' : arr[0].unit})`, type: 'value',
type: 'value',
min: min,
min: min, max: max,
max: max, // interval:interval,
// interval:interval,
// min: 134,
// min: 134, // max: 500,
// max: 500, // min: Math.ceil(
// min: Math.ceil( // arr
// arr // .map((item: any) => item.statisticalData)
// .map((item: any) => item.statisticalData) // .sort((a, b) => {
// .sort((a, b) => { // return a - b
// return a - b // })[0]
// })[0] // ),
// ), // max: Math.floor(
// max: Math.floor( // arr
// arr // .map(item => item.statisticalData)
// .map(item => item.statisticalData) // .sort((a, b) => {
// .sort((a, b) => { // return b - a
// return b - a // })[0]
// })[0] // ),
// ), // interval: (Math.floor(
// interval: (Math.floor( // arr
// arr // .map(item => item.statisticalData)
// .map(item => item.statisticalData) // .sort((a, b) => {
// .sort((a, b) => { // return b - a
// return b - a // })[0]
// })[0] // ) - Math.ceil(
// ) - Math.ceil( // arr
// arr // .map((item: any) => item.statisticalData)
// .map((item: any) => item.statisticalData) // .sort((a, b) => {
// .sort((a, b) => { // return a - b
// return a - b // })[0]
// })[0] // )) / 5,
// )) / 5,
},
},
xAxis: {
xAxis: { type: 'time',
type: 'time', axisLabel: {
axisLabel: { formatter: {
formatter: { day: '{MM}-{dd}',
day: '{MM}-{dd}', month: '{MM}',
month: '{MM}', year: '{yyyy}',
year: '{yyyy}', }
} }
} // data: res.data[0].map((item: any) => {
// data: res.data[0].map((item: any) => { // return item.time
// return item.time // }),
// }),
// axisLabel: {
// axisLabel: { // formatter: function (value: string) {
// formatter: function (value: string) { // return value.split(' ').join('\n')
// return value.split(' ').join('\n') // }
// } // }
// } },
}, toolbox: {
toolbox: { featureProps: {
featureProps: { myTool1: {
myTool1: { show: true,
show: true, title: '下载csv',
title: '下载csv',
icon: 'path://M588.8 551.253333V512H352v39.253333h236.373333z m0 78.933334v-39.253334H352v39.253334h236.373333z m136.533333 78.933333V334.933333l-157.866666-157.866666H273.066667A59.306667 59.306667 0 0 0 213.333333 236.373333v551.253334a59.306667 59.306667 0 0 0 59.306667 59.306666h274.773333v42.666667H853.333333v-180.48zM568.746667 234.666667l100.266666 100.693333h-81.066666a20.053333 20.053333 0 0 1-19.626667-20.053333z m-20.48 573.013333H273.066667a19.2 19.2 0 0 1-17.493334-19.626667V236.373333a19.2 19.2 0 0 1 19.626667-19.626666h256v98.133333a58.88 58.88 0 0 0 58.88 59.306667h96.426667v334.933333h-98.133334v-39.68H352v39.68h196.266667z m100.266666 23.04a37.973333 37.973333 0 0 1-32 15.786667 38.826667 38.826667 0 0 1-32.426666-15.786667 53.76 53.76 0 0 1-10.24-32.853333 42.666667 42.666667 0 0 1 42.666666-47.786667 35.84 35.84 0 0 1 37.546667 29.866667h-12.8a23.893333 23.893333 0 0 0-24.746667-19.2c-17.066667 0-29.013333 14.08-29.013333 35.84s11.52 37.546667 28.586667 37.546666a26.453333 26.453333 0 0 0 26.453333-25.6h12.8a39.253333 39.253333 0 0 1-7.253333 22.186667z m59.733334 15.786667a35.84 35.84 0 0 1-40.106667-34.56H682.666667a23.893333 23.893333 0 0 0 26.88 23.04c12.8 0 22.613333-6.4 22.613333-15.786667s-4.266667-11.52-14.506667-13.653333l-21.333333-5.12c-17.066667-4.266667-24.32-11.52-24.32-23.893334s12.8-26.453333 34.133333-26.453333a31.573333 31.573333 0 0 1 35.413334 30.293333h-13.653334a19.626667 19.626667 0 0 0-22.613333-18.773333c-12.8 0-20.48 5.12-20.48 12.8s5.12 11.093333 17.066667 13.653333l14.933333 2.986667a42.666667 42.666667 0 0 1 20.906667 8.96 23.893333 23.893333 0 0 1 7.68 17.92c-0.426667 17.066667-14.506667 28.16-37.12 28.16z m88.746666 0h-14.506666l-32.426667-92.16h14.08l19.626667 59.733333 6.4 20.053333c0-9.386667 3.413333-12.8 5.546666-20.053333l19.2-59.733333h14.08z',
icon: 'path://M588.8 551.253333V512H352v39.253333h236.373333z m0 78.933334v-39.253334H352v39.253334h236.373333z m136.533333 78.933333V334.933333l-157.866666-157.866666H273.066667A59.306667 59.306667 0 0 0 213.333333 236.373333v551.253334a59.306667 59.306667 0 0 0 59.306667 59.306666h274.773333v42.666667H853.333333v-180.48zM568.746667 234.666667l100.266666 100.693333h-81.066666a20.053333 20.053333 0 0 1-19.626667-20.053333z m-20.48 573.013333H273.066667a19.2 19.2 0 0 1-17.493334-19.626667V236.373333a19.2 19.2 0 0 1 19.626667-19.626666h256v98.133333a58.88 58.88 0 0 0 58.88 59.306667h96.426667v334.933333h-98.133334v-39.68H352v39.68h196.266667z m100.266666 23.04a37.973333 37.973333 0 0 1-32 15.786667 38.826667 38.826667 0 0 1-32.426666-15.786667 53.76 53.76 0 0 1-10.24-32.853333 42.666667 42.666667 0 0 1 42.666666-47.786667 35.84 35.84 0 0 1 37.546667 29.866667h-12.8a23.893333 23.893333 0 0 0-24.746667-19.2c-17.066667 0-29.013333 14.08-29.013333 35.84s11.52 37.546667 28.586667 37.546666a26.453333 26.453333 0 0 0 26.453333-25.6h12.8a39.253333 39.253333 0 0 1-7.253333 22.186667z m59.733334 15.786667a35.84 35.84 0 0 1-40.106667-34.56H682.666667a23.893333 23.893333 0 0 0 26.88 23.04c12.8 0 22.613333-6.4 22.613333-15.786667s-4.266667-11.52-14.506667-13.653333l-21.333333-5.12c-17.066667-4.266667-24.32-11.52-24.32-23.893334s12.8-26.453333 34.133333-26.453333a31.573333 31.573333 0 0 1 35.413334 30.293333h-13.653334a19.626667 19.626667 0 0 0-22.613333-18.773333c-12.8 0-20.48 5.12-20.48 12.8s5.12 11.093333 17.066667 13.653333l14.933333 2.986667a42.666667 42.666667 0 0 1 20.906667 8.96 23.893333 23.893333 0 0 1 7.68 17.92c-0.426667 17.066667-14.506667 28.16-37.12 28.16z m88.746666 0h-14.506666l-32.426667-92.16h14.08l19.626667 59.733333 6.4 20.053333c0-9.386667 3.413333-12.8 5.546666-20.053333l19.2-59.733333h14.08z', onclick: (e) => {
onclick: (e) => {
let list = echartsData.value.options.series.map(item => item.data)
let list = echartsData.value.options.series.map(item => item.data) let dataList = list[0].map((item, index) => {
let dataList = list[0].map((item, index) => { const value1 = list[1] && list[1][index] ? list[1][index][1] : null;
const value1 = list[1] && list[1][index] ? list[1][index][1] : null; const value2 = list[2] && list[2][index] ? list[2][index][1] : null;
const value2 = list[2] && list[2][index] ? list[2][index][1] : null; const value3 = list[3] && list[3][index] ? list[3][index][1] : null;
const value3 = list[3] && list[3][index] ? list[3][index][1] : null;
return [item[0], item[1], value1, value2, value3];
return [item[0], item[1], value1, value2, value3]; });
}); exportCSV(echartsData.value.options.series.map(item => item.name), dataList, echartsData.value.title.text + '.csv')
exportCSV(echartsData.value.options.series.map(item => item.name), dataList, echartsData.value.title.text + '.csv')
}
} }
} }
} }
} }
} if ((echartsData.value.legend = ['A相', 'B相', 'C相'])) {
if ((echartsData.value.legend = ['A相', 'B相', 'C相'])) { echartsData.value.color = ['#DAA520', '#2E8B57', '#A52a2a']
echartsData.value.color = ['#DAA520', '#2E8B57', '#A52a2a'] }
}
} else {
} else { echartsData.value = null
echartsData.value = null }
} loading.value = false
loading.value = false })
}) }
}
defineExpose({ open })
defineExpose({ open }) </script>
</script> <style lang="scss">
<style lang="scss"> .device-control-detail {
.device-control-detail { padding-bottom: 10px;
padding-bottom: 10px; display: flex;
display: flex; flex-direction: column;
flex-direction: column; }
}
.el-form--inline .el-form-item {
.el-form--inline .el-form-item { margin-bottom: 10px !important;
margin-bottom: 10px !important; }
} </style>
</style>

View File

@@ -141,7 +141,8 @@
v-show=" v-show="
dataSet.includes('_items') || dataSet.includes('_items') ||
dataSet.indexOf('_history') != -1 || dataSet.indexOf('_history') != -1 ||
dataSet.indexOf('_moduleData') != -1 dataSet.indexOf('_moduleData') != -1 ||
dataSet.indexOf('_devRunTrend') != -1
" "
> >
<DatePicker ref="datePickerRef"></DatePicker> <DatePicker ref="datePickerRef"></DatePicker>
@@ -165,7 +166,11 @@
</el-select> --> </el-select> -->
<el-radio-group <el-radio-group
v-model.trim="formInline.dataLevel" v-model.trim="formInline.dataLevel"
v-if="!dataSet.includes('_moduleData') && TrendList?.lineType == 1" v-if="
!dataSet.includes('_devRunTrend') &&
!dataSet.includes('_moduleData') &&
TrendList?.lineType == 1
"
:disabled="TrendList?.lineType != 1" :disabled="TrendList?.lineType != 1"
@change="handleClick" @change="handleClick"
> >
@@ -226,7 +231,7 @@
<el-button <el-button
type="primary" type="primary"
:disabled="tableLoading" :disabled="tableLoading"
v-if="realTimeFlag" v-if="showButton"
:icon="DataLine" :icon="DataLine"
@click="handleTrend" @click="handleTrend"
> >
@@ -234,13 +239,22 @@
</el-button> </el-button>
<el-button <el-button
type="primary" type="primary"
v-if="realTimeFlag" v-if="showButton"
:icon="TrendCharts" :icon="TrendCharts"
@click="handleHarmonicSpectrum" @click="handleHarmonicSpectrum"
:disabled="tableLoading" :disabled="tableLoading"
> >
实时趋势 实时趋势
</el-button> </el-button>
<el-button
type="primary"
v-if="showButton"
:icon="Download"
@click="downloadTxt"
:disabled="tableLoading"
>
下载数据
</el-button>
</template> </template>
</TableHeader> </TableHeader>
<div <div
@@ -525,6 +539,14 @@
> >
<moduleData ref="moduleDataRef" @onSubmit="handleClick" /> <moduleData ref="moduleDataRef" @onSubmit="handleClick" />
</div> </div>
<!-- 运行趋势 -->
<div
style="height: calc(100vh - 395px)"
v-if="dataSet.indexOf('_devRunTrend') != -1"
v-loading="tableLoading"
>
<operatingTrend ref="operatingTrendRef" @onSubmit="handleClick" />
</div>
<div v-if="!tableData" style="height: 42px"></div> <div v-if="!tableData" style="height: 42px"></div>
</el-tabs> </el-tabs>
</div> </div>
@@ -548,7 +570,8 @@ import {
getOverLimitData, getOverLimitData,
queryDictType, queryDictType,
getById, getById,
allModelData allModelData,
getRawData
} from '@/api/cs-device-boot/EquipmentDelivery' } from '@/api/cs-device-boot/EquipmentDelivery'
import { deviceHisData, deviceRtData, realTimeData, getTestData } from '@/api/cs-device-boot/csGroup' import { deviceHisData, deviceRtData, realTimeData, getTestData } from '@/api/cs-device-boot/csGroup'
import { ref, reactive, onMounted, onUnmounted, inject, nextTick, onBeforeUnmount } from 'vue' import { ref, reactive, onMounted, onUnmounted, inject, nextTick, onBeforeUnmount } from 'vue'
@@ -557,6 +580,7 @@ import DatePicker from '@/components/form/datePicker/index.vue'
import Trend from './tabs/trend.vue' //趋势数据 import Trend from './tabs/trend.vue' //趋势数据
import realTime from './tabs/realtime.vue' //实时数据-主界面 import realTime from './tabs/realtime.vue' //实时数据-主界面
import realTrend from './tabs/components/realtrend.vue' //实时数据-实时趋势 import realTrend from './tabs/components/realtrend.vue' //实时数据-实时趋势
import operatingTrend from './tabs/operatingTrend.vue' //运行趋势
import harmonicSpectrum from './tabs/components/harmonicSpectrum.vue' //实时数据-谐波频谱子页面 import harmonicSpectrum from './tabs/components/harmonicSpectrum.vue' //实时数据-谐波频谱子页面
import recordWoves from './tabs/components/recordwoves.vue' //实时数据-实时录波子页面 import recordWoves from './tabs/components/recordwoves.vue' //实时数据-实时录波子页面
import offLineDataImport from './offLineDataImport/index.vue' import offLineDataImport from './offLineDataImport/index.vue'
@@ -569,7 +593,7 @@ import { useRouter } from 'vue-router'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { import {
Histogram, Download,
TrendCharts, TrendCharts,
DataLine, DataLine,
DataAnalysis, DataAnalysis,
@@ -581,6 +605,7 @@ import {
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import analysisList from './analysisList/index.vue' import analysisList from './analysisList/index.vue'
import mqtt from 'mqtt' import mqtt from 'mqtt'
import { json } from 'node:stream/consumers'
defineOptions({ defineOptions({
name: 'govern/device/control' name: 'govern/device/control'
}) })
@@ -660,6 +685,8 @@ const volConTypeList = dictData.getBasicData('Dev_Connect')
// } // }
//谐波频谱 //谐波频谱
const realTrendRef = ref() const realTrendRef = ref()
const txtContent = ref('')
const operatingTrendRef = ref()
const changeTrendType = (val: any) => { const changeTrendType = (val: any) => {
trendDataTime.value = '' trendDataTime.value = ''
activeTrendName.value = val * 1 activeTrendName.value = val * 1
@@ -669,7 +696,7 @@ const changeTrendType = (val: any) => {
const activeTrendName: any = ref(0) const activeTrendName: any = ref(0)
const trendTimer: any = ref() const trendTimer: any = ref()
const trendDataTime: any = ref() const trendDataTime: any = ref()
const showButton = ref(false)
//谐波频谱方法 //谐波频谱方法
const handleTrend = async () => { const handleTrend = async () => {
realTimeFlag.value = false realTimeFlag.value = false
@@ -685,7 +712,7 @@ const handleTrend = async () => {
.then((res: any) => { .then((res: any) => {
if (res.code == 'A0000') { if (res.code == 'A0000') {
trendDataTime.value = '' trendDataTime.value = ''
ElMessage.success('装置应答成功') ElMessage.success('设备应答成功')
//每隔30s调用一下接口通知后台推送mqtt消息 //每隔30s调用一下接口通知后台推送mqtt消息
trendTimer.value = window.setInterval(() => { trendTimer.value = window.setInterval(() => {
if (!dataSet.value.includes('_realtimedata')) return if (!dataSet.value.includes('_realtimedata')) return
@@ -709,7 +736,7 @@ const handleTrend = async () => {
// } // }
}) })
} else { } else {
ElMessage.warning('装置应答失败') ElMessage.warning('设备应答失败')
} }
}) })
.catch(e => { .catch(e => {
@@ -741,9 +768,9 @@ const handleHarmonicSpectrum = async () => {
// getRealDataMqttMsg() // getRealDataMqttMsg()
await getBasicRealData(lineId.value).then((res: any) => { await getBasicRealData(lineId.value).then((res: any) => {
if (res.code == 'A0000') { if (res.code == 'A0000') {
ElMessage.success('装置应答成功') ElMessage.success('设备应答成功')
// mqttMessage.value = {} // mqttMessage.value = {}
showButton.value = true
realDataTimer.value = window.setInterval(() => { realDataTimer.value = window.setInterval(() => {
if (!dataSet.value.includes('_realtimedata')) return if (!dataSet.value.includes('_realtimedata')) return
@@ -771,7 +798,8 @@ const handleReturn = async () => {
tableLoading.value = true tableLoading.value = true
await getBasicRealData(lineId.value).then((res: any) => { await getBasicRealData(lineId.value).then((res: any) => {
if (res.code == 'A0000') { if (res.code == 'A0000') {
ElMessage.success('装置应答成功') ElMessage.success('设备应答成功')
showButton.value = true
// mqttMessage.value = {} // mqttMessage.value = {}
realDataTimer.value = window.setInterval(() => { realDataTimer.value = window.setInterval(() => {
if (!dataSet.value.includes('_realtimedata')) return if (!dataSet.value.includes('_realtimedata')) return
@@ -920,6 +948,10 @@ const nodeClick = async (e: anyObj, node: any) => {
if (item.type === 'moduleData') { if (item.type === 'moduleData') {
item.id = item.id + '_moduleData' item.id = item.id + '_moduleData'
} }
// 模块数据
if (item.type === 'devRunTrend') {
item.id = item.id + '_devRunTrend'
}
}) })
res.data.dataSetList = res.data.dataSetList.filter((item: any) => item.name != '历史统计数据') res.data.dataSetList = res.data.dataSetList.filter((item: any) => item.name != '历史统计数据')
//便携式设备默认二次值 //便携式设备默认二次值
@@ -982,9 +1014,9 @@ const getRealDataMqttMsg = async () => {
await getBasicRealData(lineId.value) await getBasicRealData(lineId.value)
.then((res: any) => { .then((res: any) => {
if (res.code == 'A0000') { if (res.code == 'A0000') {
ElMessage.success('装置应答成功') ElMessage.success('设备应答成功')
mqttMessage.value = {} mqttMessage.value = {}
showButton.value = true
realDataTimer.value = window.setInterval(async () => { realDataTimer.value = window.setInterval(async () => {
if (!dataSet.value.includes('_realtimedata')) return if (!dataSet.value.includes('_realtimedata')) return
await getBasicRealData(lineId.value).then((res: any) => { await getBasicRealData(lineId.value).then((res: any) => {
@@ -1113,6 +1145,7 @@ const getRealDataMqttMsg = async () => {
} }
if (obj.hasOwnProperty('pA') && obj.hasOwnProperty('pB')) { if (obj.hasOwnProperty('pA') && obj.hasOwnProperty('pB')) {
mqttMessage.value = obj mqttMessage.value = obj
txtContent.value = JSON.stringify(obj)
//更新实时数据主页面值 //更新实时数据主页面值
realTimeFlag.value && realTimeFlag.value &&
@@ -1146,7 +1179,7 @@ const getRealDataMqttMsg = async () => {
console.log('mqtt客户端已断开连接.....') console.log('mqtt客户端已断开连接.....')
}) })
} else { } else {
ElMessage.success('装置应答失败') ElMessage.success('设备应答失败')
tableLoading.value = false tableLoading.value = false
} }
}) })
@@ -1162,6 +1195,7 @@ const realDataTimer: any = ref()
const mqttMessage = ref<any>({}) const mqttMessage = ref<any>({})
const handleClick = async (tab?: any) => { const handleClick = async (tab?: any) => {
tableLoading.value = true tableLoading.value = true
showButton.value = false
if (realDataTimer.value) { if (realDataTimer.value) {
window.clearInterval(realDataTimer.value) window.clearInterval(realDataTimer.value)
} }
@@ -1316,6 +1350,36 @@ const handleClick = async (tab?: any) => {
}, 0) }, 0)
}, 0) }, 0)
} }
//运行趋势
if (dataSet.value.includes('_devRunTrend')) {
setTimeout(async () => {
if (tab.props != undefined) await (datePickerRef.value && datePickerRef.value?.setInterval(5))
let obj = {
// devId: deviceId.value, //e.id
lineId: [deviceId.value], //e.pid
startTime: datePickerRef.value && datePickerRef.value.timeValue[0],
endTime: datePickerRef.value && datePickerRef.value.timeValue[1]
}
await setTimeout(() => {
getRawData(obj)
.then((res: any) => {
tableLoading.value = false
setTimeout(() => {
operatingTrendRef.value?.setData(res.data)
}, 500)
setTimeout(() => {
loading.value = false
}, 1500)
})
.catch(e => {
setTimeout(() => {
tableLoading.value = false
}, 1500)
})
}, 0)
}, 0)
}
//查询当前指标 //查询当前指标
if (!dataSet.value.includes('_')) { if (!dataSet.value.includes('_')) {
@@ -1435,6 +1499,21 @@ const queryList: any = ref([])
const echoName = (value: any, arr: any[]) => { const echoName = (value: any, arr: any[]) => {
return value ? arr.find(item => item.value == value)?.label : '/' return value ? arr.find(item => item.value == value)?.label : '/'
} }
// 下载txt
const downloadTxt = () => {
const content = txtContent.value // 文件内容
const blob = new Blob([content], { type: 'text/plain' }) // 创建 Blob 对象
const url = URL.createObjectURL(blob) // 生成下载链接
// 创建 <a> 标签并触发下载
const link = document.createElement('a')
link.href = url
link.download = '实时数据.txt' // 文件名
link.click()
// 释放 URL 对象
URL.revokeObjectURL(url)
}
onMounted(() => {}) onMounted(() => {})
onBeforeUnmount(() => { onBeforeUnmount(() => {

View File

@@ -241,7 +241,7 @@ const tableStore = new TableStore({
width: '180', width: '180',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
//直连装置注册 //直连设备注册
{ {
title: '注册', title: '注册',
type: 'primary', type: 'primary',

View File

@@ -20,14 +20,8 @@
<el-button @click="handleBack" :icon="Back">返回</el-button> <el-button @click="handleBack" :icon="Back">返回</el-button>
</div> </div>
<!-- v-loading="loading" --> <!-- v-loading="loading" -->
<el-tabs <el-tabs class="home_body" type="border-card" v-model.trim="activeName1" @tab-click="handleClick">
class="home_body"
type="border-card"
v-model.trim="activeName1"
@tab-click="handleClick"
>
<el-tab-pane label="瞬时波形" name="ssbx" :style="'height:' + bxecharts + ';overflow-y: auto;'"> <el-tab-pane label="瞬时波形" name="ssbx" :style="'height:' + bxecharts + ';overflow-y: auto;'">
<shushiboxi <shushiboxi
v-if="isWp && wp && activeName == 'ssbx' && showBoxi" v-if="isWp && wp && activeName == 'ssbx' && showBoxi"
@@ -110,9 +104,15 @@ const value = ref(1)
const isWp = ref(false) const isWp = ref(false)
const boxoList: any = ref([]) const boxoList: any = ref([])
const getWpData = (val: any, list: any) => { const getWpData = (val: any, list: any) => {
wp.value = val wp.value = {}
isWp.value = true isWp.value = false
boxoList.value = list boxoList.value = []
setTimeout(() => {
wp.value = val
isWp.value = true
boxoList.value = list
}, 100)
} }
const changeView = () => { const changeView = () => {
showBoxi.value = false showBoxi.value = false
@@ -140,7 +140,6 @@ const handleBack = () => {
emit('handleHideCharts') emit('handleHideCharts')
} }
const setHeight = (h: any, vh: any, num = 1) => { const setHeight = (h: any, vh: any, num = 1) => {
console.log('🚀 ~ setHeight ~ h:', h)
if (h != false) { if (h != false) {
parentHeight.value = h parentHeight.value = h
} }

View File

@@ -182,7 +182,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-Check', icon: 'el-icon-Check',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return (props.deviceType === '2' && row.wavePath) || row.showName === '未知' return !(props.deviceType == '2' && row.wavePath == null)
}, },
click: row => { click: row => {
getFileByEventId(row.id).then(res => { getFileByEventId(row.id).then(res => {
@@ -219,6 +219,8 @@ const handleBack = async () => {
} }
defineExpose({ getTableParams }) defineExpose({ getTableParams })
onMounted(() => { onMounted(() => {
console.log('🚀 ~ props.deviceType:', props.deviceType)
tableStore.index() tableStore.index()
}) })
</script> </script>

View File

@@ -0,0 +1,165 @@
<template>
<div :style="height">
<MyEchart v-if="list.length != 0" :options="options1" style="height: 100%; width: 100%" class="pt10" />
<el-empty description="暂无数据" style="width: 100%; height: 100%" v-else></el-empty>
</div>
</template>
<script setup lang="ts">
import MyEchart from '@/components/echarts/MyEchart.vue'
import { ref, reactive } from 'vue'
import { mainHeight } from '@/utils/layout'
import { max } from 'lodash'
const height = ref(mainHeight(290))
const list = ref([])
const options1 = ref({})
const setData = (data: any) => {
// list.value = [...fillDataFromFirstTime(data),...data]
list.value = data
init()
}
/**
* 补全离散数据严格从数组第一个time开始最后一个time结束
* @param {Array} data 原始离散数据数组
* @param {number} interval 补全时间间隔默认10秒
* @returns {Array} 补全后的连续数据
*/
function fillDataFromFirstTime(data, interval = 10) {
// 1. 基础校验
if (!Array.isArray(data) || data.length === 0) {
console.error('原始数据必须是非空数组')
return []
}
// 2. 锁定起始时间直接取数组第一个元素的time不做任何偏移
const firstItem = data[0]
const startTime = new Date(firstItem.time)
const startTimestamp = startTime.getTime()
// 3. 锁定结束时间取数组最后一个元素的time
const lastItem = data[data.length - 1]
const endTime = new Date(lastItem.time)
const endTimestamp = endTime.getTime()
// 4. 预处理:按时间排序(防止原始数据乱序)
const sortedData = [...data].sort((a, b) => new Date(a.time) - new Date(b.time))
// 5. 初始化状态继承第一个数据的type
let currentType = firstItem.type
let currentDesc = firstItem.description
let nextStatusIndex = 1 // 指向待切换的下一个状态点
const filledData = []
// 6. 核心循环从第一个time开始按间隔补全到最后一个time
for (let ts = startTimestamp; ts <= endTimestamp; ts += interval * 1000) {
const currentDate = new Date(ts)
// 格式化时间为和原始数据一致的 "YYYY-MM-DD HH:mm:ss" 格式
const formattedTime = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(
2,
'0'
)}-${String(currentDate.getDate()).padStart(2, '0')} ${String(currentDate.getHours()).padStart(
2,
'0'
)}:${String(currentDate.getMinutes()).padStart(2, '0')}:${String(currentDate.getSeconds()).padStart(2, '0')}`
// 7. 检查是否到达下一个状态切换点
if (nextStatusIndex < sortedData.length) {
const nextStatusTime = new Date(sortedData[nextStatusIndex].time).getTime()
if (ts >= nextStatusTime) {
currentType = sortedData[nextStatusIndex].type
currentDesc = sortedData[nextStatusIndex].description
nextStatusIndex++
}
}
// 8. 生成补全数据项(结构与原始数据完全一致)
filledData.push({
time: formattedTime,
devId: firstItem.devId,
description: currentDesc,
type: currentType
})
}
return filledData
}
const init = () => {
options1.value = {
title: {
text: '运行状态'
},
legend: {
show: false
},
tooltip: {
formatter: function (params: any) {
var res = params[0].data[0] + '<br/>运行状态:'
var texts = ''
if (params[0].data[1] === 1 || params[0].data[1] === '1') {
texts = '中断'
} else if (params[0].data[1] === 10 || params[0].data[1] === '10') {
texts = '正常'
}
res = res + texts
return res
}
},
xAxis: {
// type: 'category',
// data: data.updateTime
type: 'time',
name: '时间',
//
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
yAxis: {
name: '',
type: 'value',
max: 11,
interval: 1,
splitLine: {
lineStyle: {
// 使用深浅的间隔色
color: ['#ccc'],
type: 'dashed',
opacity: 0
}
},
axisLabel: {
// 这里重新定义就可以
formatter: function (value: number) {
var texts = []
if (value === 1) {
texts.push('中断')
} else if (value === 10) {
texts.push('正常')
}
return texts
}
}
},
series: [
{
name: '运行状态',
data: list.value.map((item: any, index: number) => [item.time, item.type == 0 ? 1 : 10]),
type: 'line',
showSymbol: false,
step: 'end'
}
]
}
}
defineExpose({
setData
})
</script>
<style lang="scss" scoped></style>

View File

@@ -1,12 +1,10 @@
<template> <template>
<div class=" device-manage" :style="{ height: pageHeight.height }" v-loading="loading"> <div class="device-manage" :style="{ height: pageHeight.height }" v-loading="loading">
<GetMarketList @node-click="selectUser" @selectUser="selectUser"></GetMarketList> <GetMarketList @node-click="selectUser" @selectUser="selectUser"></GetMarketList>
<div class="device-manage-right" :style="{ height: pageHeight.height }"> <div class="device-manage-right" :style="{ height: pageHeight.height }">
<el-descriptions title="用户基本信息" class="mb10" :column="2" border> <el-descriptions title="用户基本信息" class="mb10" :column="2" border>
<template #extra> <template #extra>
<el-button type="primary" icon="el-icon-Sort" @click="getMarketEnginner"> <el-button type="primary" icon="el-icon-Sort" @click="getMarketEnginner">绑定工程</el-button>
绑定工程
</el-button>
</template> </template>
<el-descriptions-item label="名称"> <el-descriptions-item label="名称">
{{ user.name }} {{ user.name }}
@@ -29,18 +27,36 @@
</vxe-table> </vxe-table>
</div> </div>
</div> </div>
<el-dialog v-model.trim="dialogVisible" title="绑定工程" class="cn-operate-dialog" :close-on-click-modal="false"> <el-dialog
<el-input maxlength="32" show-word-limit v-model.trim="filterText" icon="el-icon-Search" placeholder="请输入内容" v-model.trim="dialogVisible"
clearable style="margin-bottom: 10px"> title="绑定工程"
class="cn-operate-dialog"
:close-on-click-modal="false"
>
<el-input
maxlength="32"
show-word-limit
v-model.trim="filterText"
icon="el-icon-Search"
placeholder="请输入内容"
clearable
style="margin-bottom: 10px"
>
<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>
<vxe-table ref="tableRef" v-bind="defaultAttribute" :data="tableData2.filter((item: any) => { <vxe-table
ref="tableRef"
v-bind="defaultAttribute"
:data="tableData2.filter((item: any) => {
return item.name.includes(filterText) return item.name.includes(filterText)
}) })
" height="500px" style="width: 100%"> "
height="500px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column> <vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="工程名称"></vxe-column> <vxe-column field="name" title="工程名称"></vxe-column>
</vxe-table> </vxe-table>
@@ -77,6 +93,7 @@ const filterText = ref('')
const tableRef = ref() const tableRef = ref()
const selectUser = (e: any) => { const selectUser = (e: any) => {
if (e == undefined) return (loading.value = false)
user.value = e user.value = e
loading.value = true loading.value = true
queryByUseId({ queryByUseId({

View File

@@ -2,7 +2,7 @@
<template> <template>
<div class="default-main main" :style="{ height: pageHeight.height }"> <div class="default-main main" :style="{ height: pageHeight.height }">
<div class="main_left"> <div class="main_left">
<DeviceTree @node-click="nodeClick" @init="nodeClick"></DeviceTree> <DeviceTree @node-click="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree>
</div> </div>
<div class="main_right" v-loading="loading"> <div class="main_right" v-loading="loading">
<div class="right_nav"> <div class="right_nav">
@@ -19,7 +19,7 @@
</el-breadcrumb> </el-breadcrumb>
</div> </div>
<!-- <el-button :icon="Refresh" @click="handleRestartDevice" type="primary" :loading="deviceRestartLoading"> <!-- <el-button :icon="Refresh" @click="handleRestartDevice" type="primary" :loading="deviceRestartLoading">
装置重启 设备重启
</el-button> --> </el-button> -->
</div> </div>
@@ -197,22 +197,28 @@
import DeviceTree from '@/components/tree/govern/deviceTree.vue' import DeviceTree from '@/components/tree/govern/deviceTree.vue'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { ref, watch, onMounted, onBeforeUnmount, h, inject } from 'vue' import { ref, watch, onMounted, onBeforeUnmount, h, inject } from 'vue'
import { ElMessage, ElMessageBox, ElInput } from 'element-plus' import { ElMessage, ElMessageBox, ElInput, ElSegmented } from 'element-plus'
import { import {
getFileServiceFileOrDir, getFileServiceFileOrDir,
uploadDeviceFile, uploadDeviceFile,
reStartDevice, reStartDevice,
addDeviceDir, addDeviceDir,
delDeviceDir delDeviceDir,
} from '@/api/cs-device-boot/fileService.ts' listDir,
downloadFileFromFrontr,
deleteCld,
uploadFileToFront,
mkdir
} from '@/api/cs-device-boot/fileService'
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
import { Delete, Download, Upload, Plus, Refresh, Search } from '@element-plus/icons-vue' import { Delete, Download, Upload, Plus, Refresh, Search } from '@element-plus/icons-vue'
import popup from './popup.vue' import popup from './popup.vue'
import mqtt from 'mqtt' import mqtt from 'mqtt'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { passwordConfirm } from '@/api/user-boot/user' import { passwordConfirm } from '@/api/user-boot/user'
import { downLoadFile } from '@/utils/downloadFile.ts'
defineOptions({ defineOptions({
name: 'govern/device/fileService' name: 'govern/device/fileService/index'
}) })
const pageHeight = mainHeight(20) const pageHeight = mainHeight(20)
const tableHeight = mainHeight(130) const tableHeight = mainHeight(130)
@@ -220,31 +226,53 @@ const adminInfo = useAdminInfo()
const loading = ref(false) const loading = ref(false)
//nDid //nDid
const nDid = ref<string>('') const nDid = ref<string>('')
const devId = ref<string>('')
//当前目录 //当前目录
const activePath = ref<string>('') const activePath = ref<string>('')
//判断是否是根目录 //判断是否是根目录
const isRoot = ref<boolean>(true) const isRoot = ref<boolean>(true)
//储存所有点击过的目录 //储存所有点击过的目录
const activePathList: any = ref([]) const activePathList: any = ref([])
const devConType = ref<string>('')
const deviceTypeChange = (val: any, obj: any) => {
nodeClick(obj)
}
const nodeClick = (e: any) => { const nodeClick = (e: any) => {
if (e && (e.level == 2 || e.type == 'device')) { if (e && (e.level == 2 || e.type == 'device')) {
loading.value = true loading.value = true
nDid.value = e.ndid nDid.value = e.ndid
devId.value = e.id
dirList.value = [] dirList.value = []
activePathList.value = [] activePathList.value = []
activePath.value = '/' activePath.value = '/'
getFileServiceFileOrDir({ nDid: nDid.value, name: activePath.value, type: 'dir' }) devConType.value = e.devConType
.then((resp: any) => { if (devConType.value == 'CLD') {
if (resp.code == 'A0000') { listDir({ devId: devId.value, filePath: activePath.value })
dirList.value = resp.data .then((resp: any) => {
currentDirList.value = resp.data if (resp.code == 'A0000') {
activePathList.value = [{ path: activePath.value }] dirList.value = resp.data
currentDirList.value = resp.data
activePathList.value = [{ path: activePath.value }]
loading.value = false
}
})
.catch(e => {
loading.value = false loading.value = false
} })
}) } else {
.catch(e => { getFileServiceFileOrDir({ nDid: nDid.value, name: activePath.value, type: 'dir' })
loading.value = false .then((resp: any) => {
}) if (resp.code == 'A0000') {
dirList.value = resp.data
currentDirList.value = resp.data
activePathList.value = [{ path: activePath.value }]
loading.value = false
}
})
.catch(e => {
loading.value = false
})
}
} }
} }
//搜索文件或文件夹 //搜索文件或文件夹
@@ -291,11 +319,11 @@ const vNode = () => {
]) ])
} }
//装置重启 //设备重启
const deviceRestartLoading = ref<boolean>(false) const deviceRestartLoading = ref<boolean>(false)
const handleRestartDevice = () => { const handleRestartDevice = () => {
deviceRestartLoading.value = true deviceRestartLoading.value = true
ElMessageBox.prompt('二次校验密码确认', '装置重启', { ElMessageBox.prompt('二次校验密码确认', '设备重启', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
customClass: 'customInput', customClass: 'customInput',
@@ -360,21 +388,41 @@ const handleIntoDir = (row: any) => {
if (activePathList.value.indexOf(obj.name) == -1) { if (activePathList.value.indexOf(obj.name) == -1) {
activePathList.value.push({ path: obj.name }) activePathList.value.push({ path: obj.name })
} }
getFileServiceFileOrDir(obj)
.then(res => { if (devConType.value == 'CLD') {
dirList.value = res.data listDir({ devId: devId.value, filePath: row.prjDataPath })
loading.value = false .then((resp: any) => {
currentDirList.value = res.data if (resp.code == 'A0000') {
activePathList.value.map((item: any, index: any) => { dirList.value = resp.data
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) { currentDirList.value = resp.data
activePathList.value.splice(index, 1) activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
loading.value = false
} }
}) })
isRoot.value = false .catch(e => {
}) loading.value = false
.catch(e => { })
loading.value = false } else {
}) getFileServiceFileOrDir(obj)
.then(res => {
dirList.value = res.data
loading.value = false
currentDirList.value = res.data
activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
isRoot.value = false
})
.catch(e => {
loading.value = false
})
}
} }
//处理导航栏路径 //处理导航栏路径
@@ -406,19 +454,38 @@ const handleIntoByPath = async (val: any) => {
} }
activePath.value = val.path activePath.value = val.path
loading.value = true loading.value = true
getFileServiceFileOrDir(obj) if (devConType.value == 'CLD') {
.then(res => { listDir({ devId: devId.value, filePath: val.path })
dirList.value = res.data .then((resp: any) => {
activePathList.value.map((item: any, index: any) => { if (resp.code == 'A0000') {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) { dirList.value = resp.data
activePathList.value.splice(index, 1)
activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
loading.value = false
} }
}) })
loading.value = false .catch(e => {
}) loading.value = false
.catch(e => { })
loading.value = false } else {
}) getFileServiceFileOrDir(obj)
.then(res => {
dirList.value = res.data
activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
loading.value = false
})
.catch(e => {
loading.value = false
})
}
} }
const form = ref({ const form = ref({
path: '' path: ''
@@ -437,45 +504,84 @@ const formRef = ref()
//重新加载当前页面菜单 //重新加载当前页面菜单
const reloadCurrentMenu = (msg: string) => { const reloadCurrentMenu = (msg: string) => {
loading.value = true loading.value = true
getFileServiceFileOrDir({ nDid: nDid.value, name: activePath.value, type: 'dir' })
.then((resp: any) => {
if (resp.code == 'A0000') {
loading.value = false
dirList.value = resp.data
currentDirList.value = resp.data
activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
loading.value = false
if (!msg) return if (devConType.value == 'CLD') {
ElMessage({ message: msg, type: 'success', duration: 5000 }) listDir({ devId: devId.value, filePath: activePath.value })
} .then((resp: any) => {
}) if (resp.code == 'A0000') {
.catch(e => { dirList.value = resp.data
loading.value = false currentDirList.value = resp.data
}) activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
loading.value = false
if (!msg) return
ElMessage({ message: msg, type: 'success', duration: 5000 })
}
})
.catch(e => {
loading.value = false
})
} else {
getFileServiceFileOrDir({ nDid: nDid.value, name: activePath.value, type: 'dir' })
.then((resp: any) => {
if (resp.code == 'A0000') {
loading.value = false
dirList.value = resp.data
currentDirList.value = resp.data
activePathList.value.map((item: any, index: any) => {
if (item.path.includes(activePath.value) && item.path.length > activePath.value.length) {
activePathList.value.splice(index, 1)
}
})
loading.value = false
if (!msg) return
ElMessage({ message: msg, type: 'success', duration: 5000 })
}
})
.catch(e => {
loading.value = false
})
}
} }
//新建文件夹 //新建文件夹
const submitDeviceDir = () => { const submitDeviceDir = () => {
formRef.value.validate((valid: any) => { formRef.value.validate((valid: any) => {
if (valid) { if (valid) {
let obj = { if (devConType.value == 'CLD') {
nDid: nDid.value, let obj = {
path: devId: devId.value,
activePath.value == '/' filePath:
? activePath.value + form.value.path activePath.value == '/'
: activePath.value + '/' + form.value.path ? activePath.value + form.value.path
} : activePath.value + '/' + form.value.path
loading.value = true
addDeviceDir(obj).then((res: any) => {
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
addDeviceDirOpen.value = false
} }
}) loading.value = true
mkdir(obj).then((res: any) => {
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
addDeviceDirOpen.value = false
}
})
} else {
let obj = {
nDid: nDid.value,
path:
activePath.value == '/'
? activePath.value + form.value.path
: activePath.value + '/' + form.value.path
}
loading.value = true
addDeviceDir(obj).then((res: any) => {
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
addDeviceDirOpen.value = false
}
})
}
} }
}) })
} }
@@ -507,13 +613,30 @@ const handleDelDirOrFile = (row: any) => {
passwordConfirm(value) passwordConfirm(value)
.then((resp: any) => { .then((resp: any) => {
if (resp.code == 'A0000') { if (resp.code == 'A0000') {
delDeviceDir({ nDid: nDid.value, path: row.prjDataPath }).then((res: any) => { if (devConType.value == 'CLD') {
if (res.code == 'A0000') { deleteCld({
reloadCurrentMenu(res.message) devId: devId.value,
filePath: row.prjDataPath
// ElMessage({ message: res.message, type: 'success', duration: 5000 }) })
} .then((res: any) => {
}) if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
}
})
.catch(e => {
loading.value = false
})
} else {
delDeviceDir({ nDid: nDid.value, path: row.prjDataPath })
.then((res: any) => {
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
}
})
.catch(e => {
loading.value = false
})
}
} }
}) })
.catch(e => { .catch(e => {
@@ -526,11 +649,21 @@ const changeType = ref<any>('')
//下载文件 //下载文件
const fileRef = ref() const fileRef = ref()
const handleDownLoad = async (row: any) => { const handleDownLoad = async (row: any) => {
;(await nDid.value) && fileRef.value && fileRef.value.open(row, nDid.value) if (devConType.value == 'CLD') {
// fileName.value = row?.prjDataPath.split('/')[row?.prjDataPath.split('/').length - 1] ElMessage.info('下载中,请稍等...')
// localStorage.setItem('fileName', fileName.value) downloadFileFromFrontr({
changeType.value = 'download' devId: devId.value,
localStorage.setItem('changeType', changeType.value) filePath: row.prjDataPath
}).then(res => {
downLoadFile(row.name, row.name, res)
})
} else {
;(await nDid.value) && fileRef.value && fileRef.value.open(row, nDid.value)
// fileName.value = row?.prjDataPath.split('/')[row?.prjDataPath.split('/').length - 1]
// localStorage.setItem('fileName', fileName.value)
changeType.value = 'download'
localStorage.setItem('changeType', changeType.value)
}
} }
//上传文件 //上传文件
const fileName = ref<any>('') const fileName = ref<any>('')
@@ -540,17 +673,32 @@ const handleUpload = (e: any, fileList: any, row: any) => {
localStorage.setItem('fileName', fileName.value) localStorage.setItem('fileName', fileName.value)
changeType.value = 'upload' changeType.value = 'upload'
localStorage.setItem('changeType', changeType.value) localStorage.setItem('changeType', changeType.value)
const obj = {
id: nDid.value, if (devConType.value == 'CLD') {
file: e.raw, const obj = {
filePath: row || row.prjDataPath devId: devId.value,
} file: e.raw,
uploadDeviceFile(obj).then((res: any) => { dirPath: row || row.prjDataPath
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
status.value = 100
} }
}) uploadFileToFront(obj).then((res: any) => {
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
status.value = 100
}
})
} else {
const obj = {
id: nDid.value,
file: e.raw,
filePath: row || row.prjDataPath
}
uploadDeviceFile(obj).then((res: any) => {
if (res.code == 'A0000') {
reloadCurrentMenu(res.message)
status.value = 100
}
})
}
} }
watch( watch(
() => activePathList.value, () => activePathList.value,
@@ -598,26 +746,28 @@ const mqttMessage = ref<any>({})
const status: any = ref() const status: any = ref()
function parseStringToObject(str: string) { function parseStringToObject(str: string) {
const content = str.replace(/^{|}$/g, '') const content = str.replace(/^{|}$/g, '')
const pairs = content.split(',')
const result: any = {} const result: any = {}
pairs.forEach(pair => {
const [key, value] = pair.split(':') // 正则匹配:key:value 格式,支持 value 里带 : / 等字符
// 尝试将数字转换为Number类型 const regex = /([^,:]+):([^,]+)(?=,|$)/g
result[key.trim()] = isNaN(Number(value)) ? value.trim() : Number(value) let match
})
while ((match = regex.exec(content)) !== null) {
const key = match[1].trim()
const value = match[2].trim()
// 数字自动转 Number
result[key] = isNaN(Number(value)) ? value : Number(value)
}
return result return result
} }
mqttRef.value.on('message', (topic: any, message: any) => { mqttRef.value.on('message', (topic: any, message: any) => {
// console.log('mqtt接收到消息', JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))) // console.log('mqtt接收到消息', JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message)))))
let str = JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message)))) let str = JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
let regex = /fileName:(.*?),allStep/
let regex1 = /allStep:(.*?),nowStep/
let regex2 = /nowStep:(.*?),userId/
let regex3 = /userId:(.*?)}/
mqttMessage.value = parseStringToObject(str) mqttMessage.value = parseStringToObject(str)
if (adminInfo.id != mqttMessage.value.userId) return
// console.log("🚀 ~ str.match(regex3)[1]:", str.match(regex3)[1]) // console.log("🚀 ~ str.match(regex3)[1]:", str.match(regex3)[1])
status.value = parseInt(Number((mqttMessage.value.nowStep / mqttMessage.value.allStep) * 100)) status.value = parseInt(Number((mqttMessage.value.nowStep / mqttMessage.value.allStep) * 100))
fileRef.value.setStatus(mqttMessage.value) fileRef.value.setStatus(mqttMessage.value)

View File

@@ -3,6 +3,7 @@
<div :class="downLoading ? 'all_disabled' : ''"> <div :class="downLoading ? 'all_disabled' : ''">
<el-dialog v-model.trim="dialogVisible" title="文件信息" width="50%" @closed="handleClose"> <el-dialog v-model.trim="dialogVisible" title="文件信息" width="50%" @closed="handleClose">
<div v-loading="loading"> <div v-loading="loading">
<div <div
class="download_progress" class="download_progress"
v-if="mqttFileName.includes(fileNameInfoMation) && status != 0 && status != 100" v-if="mqttFileName.includes(fileNameInfoMation) && status != 0 && status != 100"
@@ -57,7 +58,7 @@ import {
getFileServiceFileOrDir, getFileServiceFileOrDir,
downLoadDeviceFile, downLoadDeviceFile,
downLoadDeviceFilePath downLoadDeviceFilePath
} from '@/api/cs-device-boot/fileService.ts' } from '@/api/cs-device-boot/fileService'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { downLoadFile } from '@/api/cs-system-boot/manage.ts' import { downLoadFile } from '@/api/cs-system-boot/manage.ts'

View File

@@ -20,7 +20,7 @@
type="primary" type="primary"
:loading="deviceRestartLoading" :loading="deviceRestartLoading"
> >
装置重启 设备重启
</el-button> </el-button>
</template> </template>
<el-descriptions-item label="名称"> <el-descriptions-item label="名称">
@@ -237,11 +237,11 @@ const openGroup = () => {
}) })
}) })
} }
//装置重启 //设备重启
const deviceRestartLoading = ref<boolean>(false) const deviceRestartLoading = ref<boolean>(false)
const handleRestartDevice = () => { const handleRestartDevice = () => {
deviceRestartLoading.value = true deviceRestartLoading.value = true
ElMessageBox.prompt('二次校验密码确认', '装置重启', { ElMessageBox.prompt('二次校验密码确认', '设备重启', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
customClass: 'customInput', customClass: 'customInput',

View File

@@ -1,141 +1,159 @@
<template> <template>
<div class=" device-manage" :style="{ height: pageHeight.height }" v-loading="loading"> <div class="device-manage" :style="{ height: pageHeight.height }" v-loading="loading">
<OfficialUserTree @node-click="selectUser" @selectUser="selectUser"></OfficialUserTree> <OfficialUserTree @node-click="selectUser" @selectUser="selectUser"></OfficialUserTree>
<div class="device-manage-right" :style="{ height: pageHeight.height }"> <div style="flex: 1" class="pt10 pr10">
<div class="el-descriptions__header"> <div class="el-descriptions__header">
<el-button type="primary" icon="el-icon-Sort" @click="getEnginnerDev">绑定工程 / 设备</el-button> <div class="el-descriptions__title">工程 / 设备列表</div>
</div> <el-button type="primary" icon="el-icon-Sort" @click="getEnginnerDev">绑定工程 / 设备</el-button>
</div>
<!-- 使用flex布局平均分配高度 --> <!-- 使用flex布局平均分配高度 -->
<div class="tables-container"> <div class="tables-container">
<div class="table-wrapper"> <div class="table-wrapper" :style="{ height: pageHeight1.height }">
<vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%"> <vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%">
<vxe-column field="name" title="工程名称"></vxe-column> <vxe-column field="name" title="工程名称"></vxe-column>
<vxe-column title="操作" width="200px"> <vxe-column title="操作" width="200px">
<template v-slot:default="scoped"> <template v-slot:default="scoped">
<el-button link size="small" type="danger" @click="deleteEngineering(scoped.row)"> <el-button link size="small" type="danger" @click="deleteEngineering(scoped.row)">
移除 移除
</el-button> </el-button>
</template> </template>
</vxe-column> </vxe-column>
</vxe-table> </vxe-table>
</div>
</div>
</div>
<!-- 短信配置 -->
<div style="width: 400px" class="pt10 pr10">
<div class="el-descriptions__header">
<div class="el-descriptions__title">短信配置</div>
<el-button type="primary" icon="el-icon-Select" @click="saveConfiguration">保存</el-button>
</div>
<!-- 使用flex布局平均分配高度 -->
<div :style="{ height: pageHeight1.height }" class="border">
<el-tree
ref="treeRef"
class="mt10"
node-key="id"
default-expand-all
:data="tableData"
:props="defaultProps"
show-checkbox
>
<template #default="{ node, data }">
<span>{{ data.name.replace(/\([^)]*\)/, '') }}</span>
</template>
</el-tree>
</div>
</div> </div>
<!-- 新增设备列表 --> <!-- 对话框为左右布局 -->
<div class="device-list-section table-wrapper"> <el-dialog
<vxe-table v-bind="defaultAttribute" :data="deviceTableData" height="auto" style="width: 100%"> v-model.trim="dialogVisible"
<vxe-column field="name" title="设备名称"></vxe-column> draggable
<vxe-column title="操作" width="200px"> title="绑定工程 / 设备"
<template v-slot:default="scoped"> class="cn-operate-dialog"
<el-button link size="small" type="danger" @click="deleteDevice(scoped.row)"> :close-on-click-modal="false"
移除 >
</el-button> <div class="dialog-content">
</template> <!-- 工程部分 -->
</vxe-column> <div class="dialog-section">
</vxe-table> <div class="section-header">
</div> <span>工程列表</span>
</div> <el-input
maxlength="32"
show-word-limit
v-model.trim="filterText"
placeholder="搜索工程"
clearable
class="search-input"
>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<vxe-table
ref="tableRef"
v-bind="defaultAttribute"
:data="tableData2.filter((item: any) => {
return item.name.includes(filterText)
})"
height="400px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="工程名称"></vxe-column>
</vxe-table>
</div>
<!-- 设备部分 -->
<div class="dialog-section">
<div class="section-header">
<span>设备列表</span>
<el-input
maxlength="32"
show-word-limit
v-model.trim="deviceFilterText"
placeholder="搜索设备"
clearable
class="search-input"
>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<vxe-table
ref="deviceTableRef"
v-bind="defaultAttribute"
:data="deviceTableData2.filter((item: any) => {
return item.name.includes(deviceFilterText)
})"
height="400px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="便携式设备名称"></vxe-column>
</vxe-table>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="addData"> </el-button>
</span>
</template>
</el-dialog>
</div> </div>
<!-- 对话框为左右布局 -->
<el-dialog
v-model.trim="dialogVisible"
draggable
title="绑定工程 / 设备"
class="cn-operate-dialog"
:close-on-click-modal="false"
>
<div class="dialog-content">
<!-- 工程部分 -->
<div class="dialog-section">
<div class="section-header">
<span>工程列表</span>
<el-input
maxlength="32"
show-word-limit
v-model.trim="filterText"
placeholder="搜索工程"
clearable
class="search-input"
>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<vxe-table
ref="tableRef"
v-bind="defaultAttribute"
:data="tableData2.filter((item: any) => {
return item.name.includes(filterText)
})"
height="400px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="工程名称"></vxe-column>
</vxe-table>
</div>
<!-- 设备部分 -->
<div class="dialog-section">
<div class="section-header">
<span>设备列表</span>
<el-input
maxlength="32"
show-word-limit
v-model.trim="deviceFilterText"
placeholder="搜索设备"
clearable
class="search-input"
>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<vxe-table
ref="deviceTableRef"
v-bind="defaultAttribute"
:data="deviceTableData2.filter((item: any) => {
return item.name.includes(deviceFilterText)
})"
height="400px"
style="width: 100%"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="name" title="设备名称"></vxe-column>
</vxe-table>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="addData"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
defineOptions({ defineOptions({
name: 'govern/officialUser/index' name: 'govern/officialUser/index'
}) })
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
import OfficialUserTree from '@/components/tree/govern/officialUserTree.vue' import OfficialUserTree from '@/components/tree/govern/officialUserTree.vue'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { queryRunPortableDevByUseId ,queryUnlinkEngineeringByUseId} from '@/api/cs-device-boot/user' import { queryRunPortableDevByUseId, queryUnlinkEngineeringByUseId } from '@/api/cs-device-boot/user'
import { add, removeUserDev, queryDevByUseId } from '@/api/cs-system-boot/official' import {
add,
removeUserDev,
queryDevByUseId,
addUserDevices,
queryDeviceIdsByUserId
} from '@/api/cs-system-boot/official'
import { ref } from 'vue' import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
const pageHeight = mainHeight(60) const pageHeight = mainHeight(60)
const pageHeight1 = mainHeight(125)
const loading = ref(true) const loading = ref(true)
const user: any = ref({}) const user: any = ref({})
@@ -148,185 +166,222 @@ const filterText = ref('') // 工程搜索文本
const deviceFilterText = ref('') // 设备搜索文本 const deviceFilterText = ref('') // 设备搜索文本
const tableRef = ref() const tableRef = ref()
const deviceTableRef = ref() // 设备表格引用 const deviceTableRef = ref() // 设备表格引用
const defaultProps = {
children: 'devList',
label: 'name'
}
const selectUser = (e: any) => { const selectUser = (e: any) => {
user.value = e if (e == undefined) return (loading.value = false)
loading.value = true user.value = e
loading.value = true
queryDevByUseId({ userId: e.id }).then((engineeringRes) => { queryDevByUseId({ userId: e.id })
loading.value = false .then(engineeringRes => {
tableData.value = engineeringRes.data.engineeringList || [] loading.value = false
deviceTableData.value = engineeringRes.data.portableDevList || []
}).catch(() => { tableData.value = engineeringRes.data.engineeringList || []
loading.value = false deviceTableData.value = engineeringRes.data.portableDevList || []
}) queryDeviceIdsByUserId({
userId: user.value.id
}).then(res => {
treeRef.value!.setCheckedKeys(res.data, false)
})
})
.catch(() => {
loading.value = false
})
} }
const getEnginnerDev = () => { const getEnginnerDev = () => {
filterText.value = '' filterText.value = ''
deviceFilterText.value = '' deviceFilterText.value = ''
// 同时获取工程和设备数据 // 同时获取工程和设备数据
Promise.all([ Promise.all([
queryUnlinkEngineeringByUseId({ userId: user.value.id }), queryUnlinkEngineeringByUseId({ userId: user.value.id }),
queryRunPortableDevByUseId({ userId: user.value.id }) queryRunPortableDevByUseId({ userId: user.value.id })
]).then(([engineeringRes, deviceRes]) => { ]).then(([engineeringRes, deviceRes]) => {
tableData2.value = engineeringRes.data || [] tableData2.value = engineeringRes.data || []
deviceTableData2.value = deviceRes.data || [] deviceTableData2.value = deviceRes.data || []
dialogVisible.value = true dialogVisible.value = true
setTimeout(() => { setTimeout(() => {
tableRef.value?.clearCheckboxRow() tableRef.value?.clearCheckboxRow()
deviceTableRef.value?.clearCheckboxRow() deviceTableRef.value?.clearCheckboxRow()
}, 0) }, 0)
}) })
} }
const deleteEngineering = (row: any) => { const deleteEngineering = (row: any) => {
ElMessageBox.confirm('是否移出该工程?', '请确认', { ElMessageBox.confirm('是否移出该工程?', '请确认', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// 只移除工程,设备列表为空 // 只移除工程,设备列表为空
const form = { const form = {
engineeringList: [row.id], // 要移除的工程ID engineeringList: [row.id], // 要移除的工程ID
portableDevList: [], // 设备列表为空 portableDevList: [], // 设备列表为空
userId: user.value.id userId: user.value.id
} }
removeUserDev(form).then((res: any) => { removeUserDev(form).then((res: any) => {
ElMessage.success(res.message) ElMessage.success(res.message)
selectUser(user.value) selectUser(user.value)
})
}) })
})
} }
// 删除设备方法 // 删除设备方法
const deleteDevice = (row: any) => { const deleteDevice = (row: any) => {
console.log('删除设备', row) console.log('删除设备', row)
ElMessageBox.confirm('是否移出该设备?', '请确认', { ElMessageBox.confirm('是否移出该设备?', '请确认', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// 只移除设备,工程列表为空 // 只移除设备,工程列表为空
const form = { const form = {
engineeringList: [], // 工程列表为空 engineeringList: [], // 工程列表为空
portableDevList: [row.id], // 要移除的设备ID portableDevList: [row.id], // 要移除的设备ID
userId: user.value.id userId: user.value.id
} }
removeUserDev(form).then((res: any) => { removeUserDev(form).then((res: any) => {
ElMessage.success(res.message) ElMessage.success(res.message)
selectUser(user.value) selectUser(user.value)
})
}) })
})
} }
const addData = () => { const addData = () => {
const selectedEngineers = tableRef.value.getCheckboxRecords() const selectedEngineers = tableRef.value.getCheckboxRecords()
const selectedDevices = deviceTableRef.value.getCheckboxRecords() const selectedDevices = deviceTableRef.value.getCheckboxRecords()
// 如果没有选择任何项,则提示 // 如果没有选择任何项,则提示
if (selectedEngineers.length === 0 && selectedDevices.length === 0) { if (selectedEngineers.length === 0 && selectedDevices.length === 0) {
ElMessage.warning('请至少选择一项工程或设备') ElMessage.warning('请至少选择一项工程或设备')
return return
} }
// 构造请求参数对象 // 构造请求参数对象
const form = { const form = {
engineeringList: [] as any[], engineeringList: [] as any[],
portableDevList: [] as any[], portableDevList: [] as any[],
userId: user.value.id userId: user.value.id
} }
// // 处理已有的工程数据 // // 处理已有的工程数据
// tableData.value.forEach((item: any) => { // tableData.value.forEach((item: any) => {
// form.engineeringList.push(item.id) // form.engineeringList.push(item.id)
// }) // })
// // 处理已有的设备数据 // // 处理已有的设备数据
// deviceTableData.value.forEach((item: any) => { // deviceTableData.value.forEach((item: any) => {
// form.portableDevList.push(item.id) // form.portableDevList.push(item.id)
// }) // })
// 添加新选择的工程 // 添加新选择的工程
selectedEngineers.forEach((item: any) => { selectedEngineers.forEach((item: any) => {
form.engineeringList.push( item.id) form.engineeringList.push(item.id)
}) })
// 添加新选择的设备 // 添加新选择的设备
selectedDevices.forEach((item: any) => { selectedDevices.forEach((item: any) => {
form.portableDevList.push(item.id) form.portableDevList.push(item.id)
}) })
// 发送请求 // 发送请求
add(form).then((res: any) => { add(form).then((res: any) => {
ElMessage.success(res.message) ElMessage.success(res.message)
selectUser(user.value) selectUser(user.value)
dialogVisible.value = false dialogVisible.value = false
}) })
}
const treeRef = ref()
// 保存短信配置
const saveConfiguration = () => {
// treeRef.value!.getCheckedNodes(false, false)
let deviceIds = treeRef
.value!.getCheckedNodes(false, false)
.filter((item: any) => item.devList == null)
.map((item: any) => item.id)
if (deviceIds.length == 0) {
return ElMessage.warning('请选择设备!')
}
addUserDevices({
deviceIds: deviceIds,
userId: user.value.id
}).then((res: any) => {
return ElMessage.success('短信配置成功!')
})
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.device-manage { .device-manage {
display: flex;
&-right {
overflow: hidden;
flex: 1;
padding: 10px 10px 10px 0;
display: flex; display: flex;
flex-direction: column;
.el-descriptions__header { &-right {
height: 27px; overflow: hidden;
display: flex;
justify-content: flex-end; /* 靠右显示 */
align-items: center;
}
.tables-container {
flex: 1;
display: flex;
flex-direction: column;
.table-wrapper {
flex: 1; flex: 1;
min-height: 0; padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
&:first-child { .el-descriptions__header {
margin-bottom: 10px; // height: 27px;
display: flex;
justify-content: flex-end;
/* 靠右显示 */
align-items: center;
}
.tables-container {
flex: 1;
display: flex;
flex-direction: column;
.table-wrapper {
flex: 1;
min-height: 0;
&:first-child {
// margin-bottom: 10px;
}
}
} }
}
} }
} }
:deep(.el-descriptions__header) {
margin-bottom: 10px !important;
} }
.dialog-content { .dialog-content {
display: flex; display: flex;
gap: 20px; gap: 20px;
.dialog-section { .dialog-section {
flex: 1; flex: 1;
.section-header { .section-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 10px;
span { span {
font-weight: bold; font-weight: bold;
font-size: 16px; font-size: 16px;
} }
.search-input { .search-input {
width: 200px; width: 200px;
} }
}
} }
}
} }
</style> .border {
border: 1px solid var(--el-border-color);
}
</style>

View File

@@ -1,399 +1,399 @@
<template> <template>
<div> <div>
<div :style="{ width: menuCollapse ? '40px' : '280px' }" style=" overflow: hidden; height: 100%"> <div :style="{ width: menuCollapse ? '40px' : '280px' }" style=" overflow: hidden; height: 100%">
<Icon v-show="menuCollapse" @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <Icon v-show="menuCollapse" @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 mt20 menu-collapse" :class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 mt20 menu-collapse"
style="cursor: pointer" /> style="cursor: pointer" />
<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> <template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" /> <Icon name="el-icon-Search" style="font-size: 16px" />
</template> </template>
</el-input> </el-input>
<el-tooltip placement="bottom" :hide-after="0"> <el-tooltip placement="bottom" :hide-after="0">
<template #content> <template #content>
<span>新增方案</span> <span>新增方案</span>
</template> </template>
<Icon name="el-icon-Plus" size="18" class="fold ml10 menu-collapse" style="cursor: pointer" <Icon name="el-icon-Plus" size="18" class="fold ml10 menu-collapse" style="cursor: pointer"
@click="onAdd" /> @click="onAdd" />
</el-tooltip> </el-tooltip>
<Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" <Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse" :class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse"
style="cursor: pointer" /> style="cursor: pointer" />
</div> </div>
<el-tree style="flex: 1; overflow: auto" :props="defaultProps" highlight-current <el-tree style="flex: 1; overflow: auto" :props="defaultProps" highlight-current
:filter-node-method="filterNode" node-key="id" v-bind="$attrs" default-expand-all :data="tree" :filter-node-method="filterNode" node-key="id" v-bind="$attrs" default-expand-all :data="tree"
ref="treRef" @node-click="clickNode" :expand-on-click-node="false"> ref="treRef" @node-click="clickNode" :expand-on-click-node="false">
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<div class="left"> <div class="left">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }" <Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
v-if="data.icon" /> v-if="data.icon" />
<span>{{ node.label }}</span> <span>{{ node.label }}</span>
</div> </div>
<div class="right"> <div class="right">
<a :style="{ marginRight: '0.5rem' }" v-if="data?.children"> <a :style="{ marginRight: '0.5rem' }" v-if="data?.children">
<el-icon :style="{ color: '#0000FF' }"> <el-icon :style="{ color: '#0000FF' }">
<el-tooltip placement="bottom" :hide-after="0"> <el-tooltip placement="bottom" :hide-after="0">
<template #content> <template #content>
<span>新增测试项</span> <span>新增测试项</span>
</template> </template>
<Plus @click.stop="add(node, data)" /> <Plus @click.stop="add(node, data)" />
</el-tooltip> </el-tooltip>
</el-icon> </el-icon>
</a> </a>
<a :style="{ marginRight: '0.5rem' }" v-else> <a :style="{ marginRight: '0.5rem' }" v-else>
<el-icon :style="{ color: '#0000FF' }"> <el-icon :style="{ color: '#0000FF' }">
<el-tooltip placement="bottom" :hide-after="0"> <el-tooltip placement="bottom" :hide-after="0">
<template #content> <template #content>
<span>数据绑定</span> <span>数据绑定</span>
</template> </template>
<SetUp @click.stop="bind(node, data)" /> <SetUp @click.stop="bind(node, data)" />
</el-tooltip> </el-tooltip>
</el-icon> </el-icon>
</a> </a>
<a :style="{ marginRight: '0.5rem' }"> <a :style="{ marginRight: '0.5rem' }">
<el-icon :style="{ color: '#0000FF' }"> <el-icon :style="{ color: '#0000FF' }">
<el-tooltip placement="bottom" :hide-after="0"> <el-tooltip placement="bottom" :hide-after="0">
<template #content> <template #content>
<span> {{ data.pid ? '修改测试项' : ' 修改测试方案' }}</span> <span> {{ data.pid ? '修改测试项' : ' 修改测试方案' }}</span>
</template> </template>
<Edit @click.stop="edit(node, data)" /> <Edit @click.stop="edit(node, data)" />
</el-tooltip> </el-tooltip>
</el-icon> </el-icon>
</a> </a>
<a :style="{ marginRight: '0.5rem' }"> <a :style="{ marginRight: '0.5rem' }">
<el-icon :style="{ color: '#DA3434' }"> <el-icon :style="{ color: '#DA3434' }">
<Delete @click.stop="del(node, data)" /> <Delete @click.stop="del(node, data)" />
</el-icon> </el-icon>
</a> </a>
</div> </div>
</span> </span>
</template> </template>
</el-tree> </el-tree>
</div> </div>
</div> </div>
<popup ref="dialogRef" @onSubmit="getTreeList"></popup> <popup ref="dialogRef" @onSubmit="getTreeList"></popup>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, nextTick, watch, defineProps, defineEmits } from 'vue' import { ref, nextTick, watch, defineProps, defineEmits } from 'vue'
import { getSchemeTree, getTestRecordInfo } from '@/api/cs-device-boot/planData' 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 { Plus, Edit, Delete, SetUp } from '@element-plus/icons-vue' import { Plus, Edit, Delete, SetUp } from '@element-plus/icons-vue'
import { delRecord } from '@/api/cs-device-boot/planData' import { delRecord } from '@/api/cs-device-boot/planData'
import popup from './popup.vue' import popup from './popup.vue'
import { getDeviceList } from '@/api/cs-device-boot/planData' import { getDeviceList } from '@/api/cs-device-boot/planData'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
defineOptions({ defineOptions({
name: 'govern/schemeTree' name: 'govern/schemeTree'
}) })
interface Props { interface Props {
width?: string width?: string
canExpand?: boolean canExpand?: boolean
} }
const visible1 = ref(false) const visible1 = ref(false)
const visible2 = ref(false) const visible2 = ref(false)
const visible3 = ref(false) const visible3 = ref(false)
const visible4 = ref(false) const visible4 = ref(false)
const { proxy } = useCurrentInstance() const { proxy } = useCurrentInstance()
const menuCollapse = ref(false) const menuCollapse = ref(false)
const filterText = ref('') const filterText = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>() const treeRef = ref<InstanceType<typeof ElTree>>()
watch(filterText, val => { watch(filterText, val => {
treRef.value!.filter(val) treRef.value!.filter(val)
}) })
const onMenuCollapse = () => { const onMenuCollapse = () => {
menuCollapse.value = !menuCollapse.value menuCollapse.value = !menuCollapse.value
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) => {
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)
} }
} }
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
} }
const level = node.level const level = node.level
// 如果传入的节点本身就是一级节点就不用校验了 // 如果传入的节点本身就是一级节点就不用校验了
if (level === 1) { if (level === 1) {
return false return false
} }
// 先取当前节点的父节点 // 先取当前节点的父节点
let parentData = node.parent let parentData = node.parent
// 遍历当前节点的父节点 // 遍历当前节点的父节点
let index = 0 let index = 0
while (index < level - 1) { while (index < level - 1) {
// 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤 // 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤
if (parentData.data.name.indexOf(value) !== -1) { if (parentData.data.name.indexOf(value) !== -1) {
return true return true
} }
// 否则的话再往上一层做匹配 // 否则的话再往上一层做匹配
parentData = parentData.parent parentData = parentData.parent
index++ index++
} }
// 没匹配到返回false // 没匹配到返回false
return false return false
} }
// 新增方案 // 新增方案
const onAdd = () => { const onAdd = () => {
emit('onAdd') emit('onAdd')
} }
// 绑定数据 // 绑定数据
const bind = (node: any, data: any) => { const bind = (node: any, data: any) => {
emit('bind', data) emit('bind', data)
} }
/** 树形结构数据 */ /** 树形结构数据 */
const defaultProps = { const defaultProps = {
children: 'children', children: 'children',
label: 'name', label: 'name',
value: 'id' value: 'id'
} }
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
showCheckbox?: boolean showCheckbox?: boolean
defaultCheckedKeys?: any defaultCheckedKeys?: any
}>(), }>(),
{ {
showCheckbox: false, showCheckbox: false,
defaultCheckedKeys: [] defaultCheckedKeys: []
} }
) )
const emit = defineEmits(['init', 'checkChange', 'nodeChange', 'editNode', 'getChart', 'onAdd', 'bind']) const emit = defineEmits(['init', 'checkChange', 'nodeChange', 'editNode', 'getChart', 'onAdd', 'bind'])
const config = useConfig() const config = useConfig()
const tree = ref() const tree = ref()
const treRef = ref() const treRef = ref()
const id: any = ref(null) const id: any = ref(null)
const treeData = ref({}) const treeData = ref({})
//获取方案树形数据 //获取方案树形数据
const getTreeList = () => { const getTreeList = () => {
getSchemeTree().then(res => { getSchemeTree().then(res => {
let arr: any[] = [] let arr: any[] = []
res.data.forEach((item: any) => { res.data.forEach((item: any) => {
item.icon = 'el-icon-Menu' item.icon = 'el-icon-Menu'
item.color = config.getColorVal('elementUiPrimary') item.color = config.getColorVal('elementUiPrimary')
item?.children.forEach((item2: any) => { item?.children.forEach((item2: any) => {
item2.icon = 'el-icon-Document' item2.icon = 'el-icon-Document'
item2.color = config.getColorVal('elementUiPrimary') item2.color = config.getColorVal('elementUiPrimary')
arr.push(item2) arr.push(item2)
}) })
}) })
tree.value = res.data tree.value = res.data
nextTick(() => { nextTick(() => {
if (arr.length) { if (arr.length) {
treRef.value.setCurrentKey(id.value || arr[0].id) treRef.value.setCurrentKey(id.value || arr[0].id)
let list = id.value ? arr.find((item: any) => item.id == id.value) : arr[0] let list = id.value ? arr.find((item: any) => item.id == id.value) : arr[0]
// 注册父组件事件 // 注册父组件事件
emit('init', { emit('init', {
level: 2, level: 2,
...list ...list
}) })
} else { } else {
emit('init') emit('init')
} }
}) })
}) })
} }
getTreeList() getTreeList()
const dialogRef = ref() const dialogRef = ref()
const handleOpen = (val: any, id: any) => { const handleOpen = (val: any, id: any) => {
dialogRef.value.open(val, id) dialogRef.value.open(val, id)
} }
//方案id //方案id
const planId: any = ref('') const planId: any = ref('')
//测试项id //测试项id
const monitorId: any = ref('') const monitorId: any = ref('')
const planData: any = ref({}) const planData: any = ref({})
const getPlanData = (row: any) => { const getPlanData = (row: any) => {
planData.value = {} planData.value = {}
planData.value = JSON.parse(JSON.stringify(row)) planData.value = JSON.parse(JSON.stringify(row))
} }
/** 添加树节点 */ /** 添加树节点 */
// 0 新增方案 1 修改方案 2 新增测试项 3 修改测试项 4 设备信息 // 0 新增方案 1 修改方案 2 新增测试项 3 修改测试项 4 设备信息
const add = (node: any, data: any) => { const add = (node: any, data: any) => {
planId.value = data.id planId.value = data.id
//添加测试项 //添加测试项
if (data?.children) { if (data?.children) {
dialogRef.value.detailsType('tree') dialogRef.value.detailsType('tree')
handleOpen(2, planId.value) handleOpen(2, planId.value)
} }
} }
/** 编辑树节点 */ /** 编辑树节点 */
const edit = async (node: Node, data: any) => { const edit = async (node: Node, data: any) => {
planId.value = data.id planId.value = data.id
await getTestRecordInfo(planId.value) await getTestRecordInfo(planId.value)
.then(res => { .then(res => {
//修改方案 //修改方案
if (data?.children) { if (data?.children) {
dialogRef.value.detailsType('') dialogRef.value.detailsType('')
dialogRef.value.details(res.data) dialogRef.value.details(res.data)
handleOpen(1, planId.value) handleOpen(1, planId.value)
} }
//修改测试项 //修改测试项
else { else {
monitorId.value = data.id monitorId.value = data.id
dialogRef.value.detailsType('tree') dialogRef.value.detailsType('tree')
dialogRef.value.details(res.data.records[0]) dialogRef.value.details(res.data.records[0])
handleOpen(3, planId.value) handleOpen(3, planId.value)
} }
}) })
.catch(e => { }) .catch(e => { })
} }
/** 删除树节点 */ /** 删除树节点 */
const del = async (node: Node, data: any) => { const del = async (node: Node, data: any) => {
let titleList = '' let titleList = ''
planId.value = data.id planId.value = data.id
await getDeviceList({ await getDeviceList({
id: data.id, id: data.id,
isTrueFlag: 1 isTrueFlag: 1
}).then(res => { }).then(res => {
if (res.data.length > 0) { if (res.data.length > 0) {
titleList = '已绑定数据_' titleList = '已绑定数据_'
} }
}) })
//删除方案/测试项 //删除方案/测试项
ElMessageBox.confirm(titleList + '是否确认删除?', { ElMessageBox.confirm(titleList + '是否确认删除?', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}) })
.then(() => { .then(() => {
delRecord({ id: data.id }).then((res: any) => { delRecord({ id: data.id }).then((res: any) => {
if (res.code == 'A0000') { if (res.code == 'A0000') {
ElMessage.success('删除成功') ElMessage.success('删除成功')
id.value = null id.value = null
getTreeList() getTreeList()
} }
}) })
}) })
.catch(() => { .catch(() => {
ElMessage({ ElMessage({
type: 'info', type: 'info',
message: '已取消' message: '已取消'
}) })
}) })
} }
//取消删除 //取消删除
const cancelDel = () => { } const cancelDel = () => { }
const clickNode = (e: anyObj) => { const clickNode = (e: anyObj) => {
e?.children ? (planId.value = e.id) : (planId.value = e.pid) e?.children ? (planId.value = e.id) : (planId.value = e.pid)
id.value = e.id id.value = e.id
emit('nodeChange', e) emit('nodeChange', e)
} }
const setCheckedNode = (e: anyObj) => { const setCheckedNode = (e: anyObj) => {
// console.log('🚀 ~ setCheckedNode ~ e:', e) // console.log('🚀 ~ setCheckedNode ~ e:', e)
id.value = e id.value = e
treRef.value.setCurrentKey(e) treRef.value.setCurrentKey(e)
} }
watch( watch(
() => planData.value, () => planData.value,
(val, oldVal) => { (val, oldVal) => {
if (val && dialogRef.value) { if (val && dialogRef.value) {
const obj = JSON.parse(JSON.stringify(val)) const obj = JSON.parse(JSON.stringify(val))
obj.records = [ obj.records = [
val.records.find(item => { val.records.find(item => {
return item.id == monitorId.value return item.id == monitorId.value
}) })
] ]
dialogRef.value.details(obj) dialogRef.value.details(obj)
} }
}, },
{ {
immediate: true, immediate: true,
deep: true deep: true
} }
) )
defineExpose({ treeRef, getPlanData, getTreeList, setCheckedNode }) defineExpose({ treeRef, getPlanData, getTreeList, setCheckedNode })
</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%;
:deep(.el-tree) { :deep(.el-tree) {
border: 1px solid var(--el-border-color); border: 1px solid var(--el-border-color);
} }
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) { :deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
background-color: var(--el-color-primary-light-7); background-color: var(--el-color-primary-light-7);
} }
.menu-collapse { .menu-collapse {
color: var(--el-color-primary); color: var(--el-color-primary);
} }
} }
.ml10 { .ml10 {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.add_plan { .add_plan {
width: 100%; width: 100%;
height: 40px; height: 40px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
} }
.custom-tree-node { .custom-tree-node {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
.left, .left,
.right { .right {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.left { .left {
span { span {
margin-left: 2px; margin-left: 2px;
} }
} }
.right { .right {
a { a {
margin-left: 2px; margin-left: 2px;
} }
} }
} }
</style> </style>

View File

@@ -6,12 +6,13 @@
:default-checked-keys="defaultCheckedKeys" :default-checked-keys="defaultCheckedKeys"
@checkChange="checkChange" @checkChange="checkChange"
:height="35" :height="35"
:engineering="true"
></DeviceTree> ></DeviceTree>
<div class="device-manage-right" :style="{ height: pageHeight.height }"> <div class="device-manage-right" :style="{ height: pageHeight.height }">
<vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%"> <vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%">
<vxe-column field="enginerName" title="工程名称"></vxe-column> <vxe-column field="enginerName" title="工程名称"></vxe-column>
<vxe-column field="projectName" title="项目名称"></vxe-column> <vxe-column field="projectName" title="项目名称"></vxe-column>
<vxe-column field="deviceName" title="装置名称"></vxe-column> <vxe-column field="deviceName" title="设备名称"></vxe-column>
</vxe-table> </vxe-table>
</div> </div>
</div> </div>
@@ -27,6 +28,7 @@ import { mainHeight } from '@/utils/layout'
import { getVisitorConfig, updateVisitorConfig } from '@/api/cs-device-boot/user' import { getVisitorConfig, updateVisitorConfig } from '@/api/cs-device-boot/user'
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { debounce } from 'lodash-es'
const pageHeight = mainHeight(60) const pageHeight = mainHeight(60)
const loading = ref(true) const loading = ref(true)
const defaultCheckedKeys: any = ref([]) const defaultCheckedKeys: any = ref([])
@@ -34,6 +36,7 @@ const tableData = ref([])
const treeRef = ref(null) const treeRef = ref(null)
const ignoreCheckChange = ref(false) const ignoreCheckChange = ref(false)
const checkChange = (data: any) => { const checkChange = (data: any) => {
if (data == undefined) return (loading.value = false)
if (data.data.pName == '便携式设备') { if (data.data.pName == '便携式设备') {
if (ignoreCheckChange.value) { if (ignoreCheckChange.value) {
ignoreCheckChange.value = false // 清除标记,不影响后续正常触发 ignoreCheckChange.value = false // 清除标记,不影响后续正常触发
@@ -54,7 +57,7 @@ const checkChange = (data: any) => {
updateVisitorConfigs() updateVisitorConfigs()
} }
} }
const updateVisitorConfigs = () => { const updateVisitorConfigs = debounce(() => {
const result = Array.from(new Set(defaultCheckedKeys.value)) const result = Array.from(new Set(defaultCheckedKeys.value))
updateVisitorConfig( updateVisitorConfig(
result.map(item => { result.map(item => {
@@ -67,7 +70,7 @@ const updateVisitorConfigs = () => {
getVisitorConfigs() getVisitorConfigs()
} }
}) })
} }, 500)
const getVisitorConfigs = () => { const getVisitorConfigs = () => {
getVisitorConfig().then((res: any) => { getVisitorConfig().then((res: any) => {
if (res.code === 'A0000') { if (res.code === 'A0000') {

View File

@@ -3,47 +3,22 @@
<TableHeader> <TableHeader>
<template #select> <template #select>
<el-form-item label="数据分类"> <el-form-item label="数据分类">
<el-select <el-select v-model.trim="tableStore.table.params.dataType" multiple filterable collapse-tags
v-model.trim="tableStore.table.params.dataType" clearable placeholder="请选择数据分类">
multiple <el-option v-for="item in DataTypeSelect" :key="item.id" :label="item.name"
filterable :value="item.id"></el-option>
collapse-tags
clearable
placeholder="请选择数据分类"
>
<el-option
v-for="item in DataTypeSelect"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="数据存储"> <el-form-item label="数据存储">
<el-select <el-select v-model.trim="tableStore.table.params.classId" multiple filterable collapse-tags
v-model.trim="tableStore.table.params.classId" clearable placeholder="请选择数据存储">
multiple <el-option v-for="item in DataSelect" :key="item.id" :label="item.name"
filterable :value="item.id"></el-option>
collapse-tags
clearable
placeholder="请选择数据存储"
>
<el-option
v-for="item in DataSelect"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="关键字筛选"> <el-form-item label="关键字筛选">
<el-input <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
maxlength="32" placeholder="数据名称、别名、展示名称" clearable></el-input>
show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="数据名称、别名、展示名称"
clearable
></el-input>
</el-form-item> </el-form-item>
</template> </template>
<template #operation> <template #operation>
@@ -99,8 +74,9 @@ const tableStore = new TableStore({
? row.cellValue == '/' ? row.cellValue == '/'
? '/' ? '/'
: row.cellValue == 'M' : row.cellValue == 'M'
? '无相别' ? '/' : row.cellValue == 'T'
: row.cellValue + '' ? '/'
: row.cellValue + '相'
: '/' : '/'
} }
}, },
@@ -114,7 +90,7 @@ const tableStore = new TableStore({
title: '操作', fixed: 'right', title: '操作', fixed: 'right',
align: 'center', align: 'center',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {

View File

@@ -1,10 +1,10 @@
<template> <template>
<el-dialog class="cn-operate-dialog" draggable v-model.trim="dialogVisible" :title="title" @close="emit('close')"> <el-dialog class="cn-operate-dialog" draggable v-model.trim="dialogVisible" width="900px" :title="title" @close="emit('close')">
<el-scrollbar> <el-scrollbar>
<div style="padding-left: 50px"> <div style="padding-left: 50px">
<el-divider content-position="center">基础数据</el-divider> <el-divider content-position="center">基础数据</el-divider>
</div> </div>
<el-form :model="form" label-width="140px" ref="formRef" class="form-two" :rules="rules"> <el-form :model="form" label-width="130px" ref="formRef" class="form-two" :rules="rules">
<el-form-item label="数据分类:" prop="dataType"> <el-form-item label="数据分类:" prop="dataType">
<el-select v-model.trim="form.dataType" filterable clearable placeholder="请选择数据分类"> <el-select v-model.trim="form.dataType" filterable clearable placeholder="请选择数据分类">
<el-option <el-option
@@ -71,7 +71,7 @@
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="开始结束次数:"> <el-form-item label="开始结束次数:">
<el-slider v-model.trim="form.harm" range show-stops :max="50" style="width: 95%" /> <el-slider v-model.trim="form.harm" range :max="50" style="width: 90%" />
</el-form-item> </el-form-item>
<el-form-item label="统计方法:"> <el-form-item label="统计方法:">
<el-select <el-select
@@ -155,7 +155,7 @@
<div style="padding-left: 50px"> <div style="padding-left: 50px">
<el-divider content-position="center">拓展数据</el-divider> <el-divider content-position="center">拓展数据</el-divider>
</div> </div>
<el-form class="form-two" :model="form" label-width="140px" ref="formRef2"> <el-form class="form-two" :model="form" label-width="130px" ref="formRef2">
<el-form-item label="告警码(缺省值):"> <el-form-item label="告警码(缺省值):">
<el-input <el-input
maxlength="32" maxlength="32"
@@ -282,7 +282,7 @@ const phaseSelect = [
id: 'C' id: 'C'
}, },
{ {
name: 'T相', name: '无相别',
id: 'T' id: 'T'
}, },
{ {
@@ -297,10 +297,10 @@ const phaseSelect = [
name: 'CA相', name: 'CA相',
id: 'CA' id: 'CA'
}, },
{ // {
name: '无相别', // name: '无相别',
id: 'M' // id: 'M'
}, // },
] ]
const StatMethodSelect = [ const StatMethodSelect = [
{ {

View File

@@ -1,115 +1,55 @@
<template> <template>
<el-dialog <el-dialog draggable class="cn-operate-dialog" width="800px" v-model.trim="dialogVisible" :title="title"
class="cn-operate-dialog" @close="emit('closePopup')">
width="800px"
v-model.trim="dialogVisible"
:title="title"
@close="emit('closePopup')"
>
<el-form class="form-two" :model="form" label-width="100px" ref="formRef" :rules="rules"> <el-form class="form-two" :model="form" label-width="100px" ref="formRef" :rules="rules">
<el-form-item label="装置类型:" prop="type"> <el-form-item label="设备类型:" prop="type">
<!-- <el-select v-model.trim="form.type" filterable placeholder="请选择" @change="typeChange">
<el-option v-for="item in TypeOptions" :key="item.id" :label="item.name" <el-select v-model.trim="form.type" placeholder="请选择设备类型" @change="formDevTypeChange" clearable>
:value="item.id"></el-option> <el-option v-for="item in devTypeOptions" :key="item.value" :label="item.label"
</el-select> --> :value="item.value"></el-option>
<el-select v-model.trim="form.type" placeholder="请选择装置类型" @change="formDevTypeChange" clearable>
<el-option
v-for="item in devTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="装置型号:" prop="devType"> <el-form-item label="设备型号:" prop="devType">
<!-- <el-select v-model.trim="form.devType" placeholder="请选择" filterable @change="devTypeChange"> <!-- <el-select v-model.trim="form.devType" placeholder="请选择" filterable @change="devTypeChange">
<el-option v-for="item in DevTypeOptions" :key="item.id" :label="item.name" <el-option v-for="item in DevTypeOptions" :key="item.id" :label="item.name"
:value="item.id"></el-option> :value="item.id"></el-option>
</el-select> --> </el-select> -->
<el-select <el-select v-model.trim="form.devType" filterable placeholder="请选择设备型号" clearable
v-model.trim="form.devType" @change="devTypeChange">
filterable <el-option v-for="item in formDevModelOptionsFilter" :key="item.value" :label="item.label"
placeholder="请选择装置型号" :value="item.value"></el-option>
clearable
@change="devTypeChange"
>
<el-option
v-for="item in formDevModelOptionsFilter"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="版本号:" prop="versionNo"> <el-form-item label="版本号:" prop="versionNo">
<el-input <el-input maxlength="32" show-word-limit v-model.trim="form.versionNo" autocomplete="off"
maxlength="32" placeholder="请输入版本号"></el-input>
show-word-limit
v-model.trim="form.versionNo"
autocomplete="off"
placeholder="请输入版本号"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="版本类型:" prop="versionType"> <el-form-item label="版本类型:" prop="versionType">
<el-input <el-input maxlength="32" show-word-limit v-model.trim="form.versionType" autocomplete="off"
maxlength="32" placeholder="填写特殊类型(不填默认通用类型)"></el-input>
show-word-limit
v-model.trim="form.versionType"
autocomplete="off"
placeholder="填写特殊类型(不填默认通用类型)"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="版本协议:" prop="versionAgreement"> <el-form-item label="协议版本:" prop="versionAgreement">
<el-input <el-input maxlength="32" show-word-limit v-model.trim="form.versionAgreement" autocomplete="off"
maxlength="32" placeholder="请输入协议版本"></el-input>
show-word-limit
v-model.trim="form.versionAgreement"
autocomplete="off"
placeholder="请输入版本协议"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="版本日期:" prop="versionDate"> <el-form-item label="版本日期:" prop="versionDate">
<el-date-picker <el-date-picker v-model.trim="form.versionDate" type="date" value-format="YYYY-MM-DD"
v-model.trim="form.versionDate" placeholder="选择日期时间" style="width: 100%"></el-date-picker>
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期时间"
style="width: 100%"
></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="CRC校验:" prop="crcInfo"> <el-form-item label="CRC校验:" prop="crc">
<el-input <el-input maxlength="32" show-word-limit v-model.trim="form.crc" autocomplete="off"
maxlength="32" placeholder="请输入CRC校验"></el-input>
show-word-limit
v-model.trim="form.crcInfo"
autocomplete="off"
placeholder="请输入CRC校验"
></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-form :model="form" label-width="100px" class="mt10 form-one"> <el-form :model="form" label-width="100px" class="mt10 form-one">
<el-form-item label="描述:" prop="description"> <el-form-item label="描述:" prop="description">
<el-input <el-input maxlength="300" :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" show-word-limit
maxlength="300" v-model.trim="form.description" autocomplete="off" placeholder="请输入描述"></el-input>
:autosize="{ minRows: 2, maxRows: 4 }"
type="textarea"
show-word-limit
v-model.trim="form.description"
autocomplete="off"
placeholder="请输入描述"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="升级文件:" prop="file"> <el-form-item label="升级文件:" prop="file">
<el-upload <el-upload :limit="1" accept=".bin" :auto-upload="false" :on-change="fileChange" :on-exceed="fileExceed"
:limit="1" :on-remove="fileRemove" :file-list="fileList">
accept=".bin"
:auto-upload="false"
:on-change="fileChange"
:on-exceed="fileExceed"
:on-remove="fileRemove"
:file-list="fileList"
>
<el-button type="primary">点击上传</el-button> <el-button type="primary">点击上传</el-button>
</el-upload> </el-upload>
</el-form-item> </el-form-item>
@@ -146,7 +86,7 @@ const form = reactive<any>({
versionAgreement: null, versionAgreement: null,
versionNo: null, versionNo: null,
versionType: null, versionType: null,
crcInfo: null, crc: null,
description: null, description: null,
versionDate: null, versionDate: null,
type: null, type: null,
@@ -154,14 +94,14 @@ const form = reactive<any>({
file: [] file: []
}) })
const rules = { const rules = {
type: [{ required: true, message: '请选择装置类型', trigger: 'change' }], type: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
devType: [{ required: true, message: '请选择装置型号', trigger: 'change' }], devType: [{ required: true, message: '请选择设备型号', trigger: 'change' }],
versionNo: [{ required: true, message: '请输入版本号', trigger: 'blur' }], versionNo: [{ required: true, message: '请输入版本号', trigger: 'blur' }],
versionType: [{ required: true, message: '请输入版本类型', trigger: 'blur' }], versionType: [{ required: true, message: '请输入版本类型', trigger: 'blur' }],
versionAgreement: [{ required: true, message: '请输入版本协议', trigger: 'blur' }], versionAgreement: [{ required: true, message: '请输入协议版本', trigger: 'blur' }],
versionDate: [{ required: true, message: '请输入版本日期', trigger: 'blur' }], versionDate: [{ required: true, message: '请输入版本日期', trigger: 'blur' }],
description: [{ required: true, message: '请输入描述', trigger: 'blur' }], description: [{ required: true, message: '请输入描述', trigger: 'blur' }],
crcInfo: [{ required: true, message: '请输入CRC校验', trigger: 'blur' }], crc: [{ required: true, message: '请输入CRC校验', trigger: 'blur' }],
file: [{ required: true, message: '请输入升级文件', trigger: 'blur' }] file: [{ required: true, message: '请输入升级文件', trigger: 'blur' }]
} }
const typeChange = () => { const typeChange = () => {
@@ -176,7 +116,7 @@ const devTypeChange = (e: any) => {
// console.log(e) // console.log(e)
form.devTypeName = formDevModelOptionsFilter.value.filter((item: any) => item.value == e)[0].label form.devTypeName = formDevModelOptionsFilter.value.filter((item: any) => item.value == e)[0].label
} }
queryByCode('Device_Type').then(res => { queryByCode('Device_Type').then(async res => {
const id = res.data.id const id = res.data.id
queryCsDictTree(id).then(res => { queryCsDictTree(id).then(res => {
devTypeOptions.value = res.data.map((item: any) => { devTypeOptions.value = res.data.map((item: any) => {
@@ -203,6 +143,14 @@ queryByCode('Device_Type').then(res => {
}) })
// tableStore.index() // tableStore.index()
}) })
queryByCode('Direct_Connected_Device').then(res => {
queryCsDictTree(res.data.id).then(res => {
TypeOptions.value = res.data
})
queryByid(res.data.id).then(res => {
DevTypeAllOptions.value = res.data
})
})
const formDevModelOptionsFilter = computed(() => { const formDevModelOptionsFilter = computed(() => {
return devModelOptions.value.filter((item: any) => { return devModelOptions.value.filter((item: any) => {
if (form.type) { if (form.type) {
@@ -246,7 +194,7 @@ const formDevTypeChange = (e: any) => {
} }
const dialogVisible = ref(false) const dialogVisible = ref(false)
const title = ref('新增版本') const title = ref('新增版本')
const open = (text: string, data?: anyObj) => { const open = async (text: string, data?: anyObj) => {
title.value = text title.value = text
dialogVisible.value = true dialogVisible.value = true
if (data) { if (data) {
@@ -264,14 +212,7 @@ const open = (text: string, data?: anyObj) => {
form[key] = null form[key] = null
} }
} }
queryByCode('Direct_Connected_Device').then(res => {
queryCsDictTree(res.data.id).then(res => {
TypeOptions.value = res.data
})
queryByid(res.data.id).then(res => {
DevTypeAllOptions.value = res.data
})
})
} }
const submit = () => { const submit = () => {
// console.log(formRef) // console.log(formRef)

View File

@@ -11,7 +11,7 @@
placeholder="请输入名称" placeholder="请输入名称"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="装置型号"> <el-form-item label="设备型号">
<el-select v-model.trim="tableStore.table.params.devType" placeholder="请选择" clearable> <el-select v-model.trim="tableStore.table.params.devType" placeholder="请选择" clearable>
<el-option <el-option
v-for="item in DevTypeOptions" v-for="item in DevTypeOptions"
@@ -71,7 +71,7 @@ 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: 'devTypeName' }, { title: '设备型号', field: 'devTypeName' },
{ title: '模板名称', field: 'name' }, { title: '模板名称', field: 'name' },
{ title: '版本号', field: 'versionNo' }, { title: '版本号', field: 'versionNo' },
{ title: '版本时间', field: 'versionDate', sortable: true }, { title: '版本时间', field: 'versionDate', sortable: true },

View File

@@ -2,19 +2,17 @@
<div class="default-main"> <div class="default-main">
<TableHeader ref="tableHeaderRef" :showReset="false" showExport> <TableHeader ref="tableHeaderRef" :showReset="false" showExport>
<template #select> <template #select>
<el-form-item label="装置型号:"> <el-form-item label="设备型号:">
<el-select v-model.trim="tableStore.table.params.devType" placeholder="请选择装置型号" clearable> <el-select v-model.trim="tableStore.table.params.devType" filterable placeholder="请选择设备型号"
<el-option clearable>
v-for="item in DevTypeOptions" <el-option v-for="item in DevTypeOptions" :key="item.id" :label="item.name"
:key="item.id" :value="item.id"></el-option>
:label="item.name"
:value="item.id"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</template> </template>
<template #operation> <template #operation>
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增版本</el-button> <el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增版本</el-button>
<el-button :icon="Back" @click="go(-1)">返回</el-button>
</template> </template>
</TableHeader> </TableHeader>
<Table ref="tableRef" /> <Table ref="tableRef" />
@@ -27,13 +25,15 @@ 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 { delCsDictData } from '@/api/system-boot/csDictData' import { delCsDictData } from '@/api/system-boot/csDictData'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { queryByCode, queryByid } from '@/api/system-boot/dictTree' import { queryByCode, queryByid } from '@/api/system-boot/dictTree'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import PopupVersion from '@/views/govern/manage/basic/popupVersion.vue' import PopupVersion from '@/views/govern/manage/basic/popupVersion.vue'
import { Plus } from '@element-plus/icons-vue' import { Plus, Back } from '@element-plus/icons-vue'
import { auditEdData } from '@/api/cs-device-boot/edData' import { auditEdData } from '@/api/cs-device-boot/edData'
import { activateUser, deluser, passwordConfirm } from '@/api/user-boot/user'
import { getFileUrl, downLoadFile } from '@/api/cs-system-boot/manage' import { getFileUrl, downLoadFile } from '@/api/cs-system-boot/manage'
import { useRouter } from 'vue-router'
defineOptions({ defineOptions({
name: 'govern/manage/basic/version' name: 'govern/manage/basic/version'
}) })
@@ -42,12 +42,12 @@ const dictData = useDictData()
const showPopup = ref(false) const showPopup = ref(false)
const DevTypeOptions = ref() const DevTypeOptions = ref()
const tableHeaderRef = ref() const tableHeaderRef = ref()
const { push, go } = useRouter()
const tableStore = new TableStore({ const tableStore = new TableStore({
url: '/cs-device-boot/edData/queryEdDataPage', url: '/cs-device-boot/edData/queryEdDataPage',
method: 'POST', method: 'POST',
column: [ column: [
{ {
field: 'index', field: 'index',
title: '序号', title: '序号',
width: '80', width: '80',
@@ -55,28 +55,74 @@ 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: 'devTypeName' ,minWidth: '100'}, { title: '设备型号', field: 'devTypeName', minWidth: '100' },
{ title: '版本号', field: 'versionNo' ,minWidth: '100'}, { title: '版本号', field: 'versionNo', minWidth: '100' },
{ title: '版本协议', field: 'versionAgreement' ,minWidth: '100'}, { title: '协议版本', field: 'versionAgreement', minWidth: '100' },
{ title: '版本日期', field: 'versionDate' ,minWidth: '100'}, { title: '版本日期', field: 'versionDate', minWidth: '100' },
{ title: '归档日期', field: 'updateTime',minWidth: '150' }, { title: '归档日期', field: 'updateTime', minWidth: '150' },
{ title: '描述', field: 'description',minWidth: '200' }, { title: '描述', field: 'description', minWidth: '200' },
// {
// title: '状态',
// field: 'status',
// render: 'tag',
// minWidth: '80',
// custom: {
// 0: 'danger',
// 1: 'success'
// },
// replaceValue: {
// 0: '禁用',
// 1: '启用'
// }
// },
{ {
title: '状态', title: '使用状态',
render: 'switch',
width: 100,
field: 'status', field: 'status',
render: 'tag', activeText: '启用',
minWidth: '80', inactiveText: '禁用',
custom: { inactiveValue: '0',
0: 'error', activeValue: '1',
1: 'success' onChangeField: (row: any, value: any) => {
}, // console.log("🚀 ~ row:", row)
replaceValue: { ElMessageBox.prompt('二次校验密码确认', '', {
0: '禁用', confirmButtonText: '确认',
1: '启用' cancelButtonText: '取消',
customClass: 'customInput',
inputType: 'text',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
if (instance.inputValue == null) {
return ElMessage.warning('请输入密码')
} else if (instance.inputValue?.length > 32) {
return ElMessage.warning(
'密码长度不能超过32位,当前密码长度为' + instance.inputValue.length
)
} else {
done()
}
} else {
done()
}
}
}).then(({ value }) => {
passwordConfirm(value).then(res => {
auditEdData({
id: row.id,
status: row.status == 0 ? 1 : 0
}).then((res: any) => {
ElMessage.success(row.status == 0 ? '启用成功!' : '禁用成功!')
tableStore.index()
})
})
})
} }
}, },
{ {
title: '操作', fixed: 'right', title: '操作',
fixed: 'right',
align: 'center', align: 'center',
width: '180', width: '180',
render: 'buttons', render: 'buttons',
@@ -104,50 +150,7 @@ const tableStore = new TableStore({
}, 100) }, 100)
} }
}, },
{
name: 'edit',
title: '启用',
type: 'primary',
icon: 'el-icon-Open',
render: 'basicButton',
disabled: row => {
return row.status == 1
},
click: row => {
auditEdData({
id: row.id,
status: 1
}).then((res: any) => {
ElMessage.success('启用成功')
tableStore.index()
})
}
},
{
name: 'del',
title: '禁用',
type: 'danger',
icon: 'el-icon-SwitchButton',
render: 'confirmButton',
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定禁用吗?'
},
disabled: row => {
return row.status == 0
},
click: row => {
auditEdData({
id: row.id,
status: 0
}).then((res: any) => {
ElMessage.success('禁用成功')
tableStore.index()
})
}
}
] ]
} }
], ],
@@ -177,7 +180,6 @@ queryByCode('Device_Type').then(res => {
// 下载报告 // 下载报告
const downloadTheReport = (name: string) => { const downloadTheReport = (name: string) => {
getFileUrl(name).then((res: any) => { getFileUrl(name).then((res: any) => {
downLoadFile(res.data).then(res => { downLoadFile(res.data).then(res => {
let blob = new Blob([res], { let blob = new Blob([res], {
type: 'application/octet-stream' type: 'application/octet-stream'
@@ -209,3 +211,10 @@ const closePopup = () => {
showPopup.value = false showPopup.value = false
} }
</script> </script>
<style lang="scss">
.customInput {
.el-input__inner {
-webkit-text-security: disc !important;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<el-dialog draggable class="cn-operate-dialog" width="520px" v-model.trim="dialogVisible" :title="title"> <el-dialog draggable class="cn-operate-dialog" width="540px" v-model.trim="dialogVisible" :title="title">
<el-form :inline="false" ref="formRef" :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="项目名称" 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="请输入项目名称" />
@@ -23,14 +23,9 @@
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.number="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="拓扑图" prop="sort"> <el-form-item label="拓扑图">
<div class="image-radio-group"> <div class="image-radio-group">
<div <div class="image-radio-item" v-for="item in images" :key="item.id" @click="handleSelect(item.id)">
class="image-radio-item"
v-for="item in images"
:key="item.id"
@click="handleSelect(item.id)"
>
<el-radio :label="item.id" v-model="form.topoIds" class="hidden-radio"> <el-radio :label="item.id" v-model="form.topoIds" class="hidden-radio">
{{ item.name }} {{ item.name }}
</el-radio> </el-radio>
@@ -64,7 +59,7 @@ import TableStore from '@/utils/tableStore'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { add, update } from '@/api/user-boot/role' import { add, update } from '@/api/user-boot/role'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { addProject, updateProject } from '@/api/cs-device-boot/edData' import { addProject, updateProjects } from '@/api/cs-device-boot/edData'
import { getTopoTemplate } from '@/api/cs-device-boot/topologyTemplate' import { getTopoTemplate } from '@/api/cs-device-boot/topologyTemplate'
import { getFileUrl } from '@/api/system-boot/file' import { getFileUrl } from '@/api/system-boot/file'
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
@@ -123,14 +118,15 @@ const open = (text: string, List: any, data?: anyObj, id?: string) => {
} }
} }
const submit = async () => { const submit = async () => {
if (form.topoIds == '') {
return ElMessage.warning('请选择拓扑图!')
}
formRef.value.validate(async valid => { formRef.value.validate(async valid => {
console.log("🚀 ~ submit ~ form.topoIds:", form.topoIds)
if (valid) { if (valid) {
if (form.id) { if (form.id) {
await updateProject({ ...form ,topoIds:[form.topoIds]}) await updateProjects({ ...form, topoIds: [form.topoIds] })
} else { } else {
await addProject({ ...form,topoIds:[form.topoIds] }) await addProject({ ...form, topoIds: [form.topoIds] })
} }
ElMessage.success('保存成功') ElMessage.success('保存成功')
tableStore.index() tableStore.index()
@@ -150,7 +146,11 @@ const getImageList = async () => {
} }
const handleSelect = (filePath: any) => { const handleSelect = (filePath: any) => {
form.topoIds = filePath if (form.topoIds == filePath) {
form.topoIds = ''
} else {
form.topoIds = filePath
}
} }
defineExpose({ open }) defineExpose({ open })
</script> </script>

View File

@@ -1,14 +1,24 @@
<template> <template>
<div class="default-main"> <div class="default-main">
<div style="display: flex"> <el-row v-loading="tableStore.table.loading">
<div style="width: 800px"> <el-col :span="11">
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">工程列表</div> <div class="title">
工程列表
<el-input
class="ml10"
v-model="searchValue"
placeholder="请输入工程名称"
clearable
style="width: 240px"
@input="inpChaange"
/>
</div>
<el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增工程</el-button> <el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增工程</el-button>
</div> </div>
<Table ref="tableRef" @currentChange="currentChange" /> <Table ref="tableRef" @currentChange="currentChange" />
</div> </el-col>
<div style="flex: 1"> <el-col :span="13">
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">項目列表</div> <div class="title">項目列表</div>
@@ -25,17 +35,19 @@
<vxe-column field="projectName" title="项目名称"></vxe-column> <vxe-column field="projectName" title="项目名称"></vxe-column>
<vxe-column field="projectArea" title="地址"></vxe-column> <vxe-column field="projectArea" title="地址"></vxe-column>
<vxe-column title="拓扑图"> <vxe-column field="projectRemark" title="备注"></vxe-column>
<vxe-column title="拓扑图" width="100px">
<template #default="{ row }"> <template #default="{ row }">
<el-image <el-image
:hide-on-click-modal="true" :hide-on-click-modal="true"
:preview-teleported="true" :preview-teleported="true"
v-if="row.topologyInfo"
:preview-src-list="[row.topologyImageUrl]" :preview-src-list="[row.topologyImageUrl]"
:src="getUrl(row) ? row.topologyImageUrl : ''" :src="row.topologyImageUrl"
></el-image> ></el-image>
<span v-else>/</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="projectRemark" title="备注"></vxe-column>
<vxe-column field="projectSort" title="排序" width="80px"></vxe-column> <vxe-column field="projectSort" title="排序" width="80px"></vxe-column>
<vxe-column title="操作" width="180px"> <vxe-column title="操作" width="180px">
<template #default="{ row }"> <template #default="{ row }">
@@ -64,8 +76,8 @@
</vxe-column> </vxe-column>
</vxe-table> </vxe-table>
</div> </div>
</div> </el-col>
</div> </el-row>
<!-- 新增工程 --> <!-- 新增工程 -->
<popup ref="popupRef" @save="save" /> <popup ref="popupRef" @save="save" />
<!-- 新增項目 --> <!-- 新增項目 -->
@@ -78,7 +90,7 @@ import { Plus } from '@element-plus/icons-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 { delTemplate, deleteSysExcel, bandRelation, queryList } from '@/api/harmonic-boot/luckyexcel' import { delTemplate, deleteSysExcel, bandRelation, queryList } from '@/api/harmonic-boot/luckyexcel'
import { deleteProject ,deleteEngineering} from '@/api/cs-device-boot/edData' import { deleteProject, deleteEngineering } from '@/api/cs-device-boot/edData'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
@@ -92,13 +104,16 @@ defineOptions({
const height = mainHeight(80) const height = mainHeight(80)
const volConTypeList: any = dictData.getBasicData('Dev_Connect') const volConTypeList: any = dictData.getBasicData('Dev_Connect')
import { getFileUrl } from '@/api/system-boot/file' import { getFileUrl } from '@/api/system-boot/file'
const popupRef = ref() const popupRef = ref()
const tableRef = ref() const tableRef = ref()
const tableRef1 = ref() const tableRef1 = ref()
const itemList = ref([]) const searchValue = ref('')
const itemList: any = ref([])
const menuListId = ref([]) const menuListId = ref([])
const itemAddRef = ref() const itemAddRef = ref()
const engineeringId = ref('') const engineeringId = ref('')
const tableList = ref([])
const tableStore: any = new TableStore({ const tableStore: any = new TableStore({
url: '/cs-device-boot/engineeringProjectRelation/list', url: '/cs-device-boot/engineeringProjectRelation/list',
method: 'POST', method: 'POST',
@@ -161,17 +176,35 @@ const tableStore: any = new TableStore({
], ],
loadCallback: () => { loadCallback: () => {
if (engineeringId.value == '') { tableList.value = JSON.parse(JSON.stringify(tableStore.table.data))
engineeringId.value = tableStore.table.data[0].engineeringId setTableRow()
}
let list = tableStore.table.data.filter((item: any) => item.engineeringId == engineeringId.value)
tableRef.value.getRef().setCurrentRow(list[0])
itemList.value = list[0].projectInfoList
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
// 过滤
const inpChaange = (val: any) => {
engineeringId.value = ''
if (val == '') {
tableStore.table.data = tableList.value
} else {
tableStore.table.data = tableList.value.filter((item: any) => item.engineeringName.includes(val))
}
setTableRow()
}
const setTableRow = () => {
if (engineeringId.value == '') {
engineeringId.value = tableStore.table.data[0].engineeringId
}
let list = tableStore.table.data.filter((item: any) => item.engineeringId == engineeringId.value)
tableRef.value.getRef().setCurrentRow(list[0] ?? {})
itemList.value = list?.[0]?.projectInfoList ?? []
if (itemList.value.length > 0) {
itemList.value.forEach((item: any) => {
item.topologyImageUrl = getUrl(item)
})
}
}
// 修改模版 // 修改模版
const itemModification = (row: any) => { const itemModification = (row: any) => {
itemAddRef.value.open('新增项目', tableStore.table.data, row, engineeringId.value) itemAddRef.value.open('新增项目', tableStore.table.data, row, engineeringId.value)
@@ -195,6 +228,11 @@ const add = () => {
const currentChange = (data: any) => { const currentChange = (data: any) => {
engineeringId.value = data.row.engineeringId engineeringId.value = data.row.engineeringId
itemList.value = data.row.projectInfoList itemList.value = data.row.projectInfoList
if (itemList.value.length > 0) {
itemList.value.forEach((item: any) => {
item.topologyImageUrl = getUrl(item)
})
}
} }
// 保存模版 // 保存模版
const save = () => { const save = () => {
@@ -210,14 +248,11 @@ const save = () => {
const imgList = ref([]) const imgList = ref([])
// 获取拓扑图 // 获取拓扑图
const getUrl = async (row: any) => { const getUrl = async (row: any) => {
if (!row.topologyInfo || row.topologyInfo === '/') return false if (!row.topologyInfo || row.topologyInfo === '/') return ''
try {
const res = await getFileUrl({ filePath: row.topologyInfo }) const res = await getFileUrl({ filePath: row.topologyInfo })
row.topologyImageUrl = res.data row.topologyImageUrl = res.data
return true return res.data
} catch (error) {
return false
}
} }
onMounted(() => { onMounted(() => {

View File

@@ -10,13 +10,13 @@
placeholder="请输入设备名称/网络设备ID" placeholder="请输入设备名称/网络设备ID"
/> />
</el-form-item> </el-form-item>
<el-form-item label="流程阶段"> <!-- <el-form-item label="流程阶段">
<el-select v-model.trim="tableStore.table.params.process" clearable placeholder="请选择状态"> <el-select v-model.trim="tableStore.table.params.process" clearable placeholder="请选择状态">
<el-option label="功能调试" :value="2"></el-option> <el-option label="功能调试" :value="2"></el-option>
<el-option label="出厂调试" :value="3"></el-option> <el-option label="出厂调试" :value="3"></el-option>
<el-option label="正式投运" :value="4"></el-option> <el-option label="正式投运" :value="4"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<el-form-item label="物联状态"> <el-form-item label="物联状态">
<el-select <el-select
v-model.trim="tableStore.table.params.connectStatus" v-model.trim="tableStore.table.params.connectStatus"
@@ -151,7 +151,7 @@
<el-form-item label="设备型号:" prop="devModel" class="top"> <el-form-item label="设备型号:" prop="devModel" class="top">
<el-select v-model.trim="form.devModel" filterable placeholder="请选择" clearable> <el-select v-model.trim="form.devModel" filterable placeholder="请选择" clearable>
<el-option <el-option
v-for="item in formDevModelOptionsFilter " v-for="item in formDevModelOptionsFilter"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
@@ -164,6 +164,15 @@
<el-option label="CLD" value="CLD"></el-option> <el-option label="CLD" value="CLD"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="关联项目:" class="top">
<el-cascader
v-model.trim="form.association"
filterable
:options="engineeringList"
:props="props"
clearable
/>
</el-form-item>
<el-form-item label="排序:" class="top" prop="sort"> <el-form-item label="排序:" class="top" 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.trim="form.sort" :min="0" />
</el-form-item> </el-form-item>
@@ -198,7 +207,7 @@ import TableHeader from '@/components/table/header/index.vue'
import { queryByCode, queryByid, queryCsDictTree } from '@/api/system-boot/dictTree' import { queryByCode, queryByid, queryCsDictTree } from '@/api/system-boot/dictTree'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { activateUser, deluser, passwordConfirm } from '@/api/user-boot/user' import { activateUser, deluser, passwordConfirm } from '@/api/user-boot/user'
import { downLoadFile } from '@/api/cs-system-boot/manage.ts' import { downLoadFile } from '@/api/cs-system-boot/manage'
import { import {
addEquipmentDelivery, addEquipmentDelivery,
deleteEquipmentDelivery, deleteEquipmentDelivery,
@@ -208,7 +217,10 @@ import {
governDeviceRegister, governDeviceRegister,
portableDeviceRegister, portableDeviceRegister,
portableDeviceAccess, portableDeviceAccess,
getExcelTemplate getExcelTemplate,
engineeringProject,
onlineRegister,
resetFactory
} from '@/api/cs-system-boot/device' } from '@/api/cs-system-boot/device'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import { fullUrl } from '@/utils/common' import { fullUrl } from '@/utils/common'
@@ -221,17 +233,27 @@ const showQrCode = ref(false)
const devTypeOptions: any = ref([]) const devTypeOptions: any = ref([])
const devTypeOptions2: any = ref([]) const devTypeOptions2: any = ref([])
const devModelOptions2: any = ref([]) const devModelOptions2: any = ref([])
const engineeringList: any = ref([])
const deivce: any = ref({}) const deivce: any = ref({})
const ruleFormRef = ref() const ruleFormRef = ref()
const form = reactive({ const form: any = reactive({
cntractNo: '', cntractNo: '',
devAccessMethod: 'MQTT', devAccessMethod: 'MQTT',
devModel: '', devModel: '',
devType: '', devType: '',
name: '', name: '',
ndid: '', ndid: '',
associatedEngineering: '',
associatedProject: '',
association: [],
sort: 0 sort: 0
}) })
const props = {
label: 'projectName',
value: 'projectId',
children: 'projectInfoList'
}
const rules = reactive({ const rules = reactive({
name: [{ required: true, message: '请输入设备名', trigger: 'blur' }], name: [{ required: true, message: '请输入设备名', trigger: 'blur' }],
ndid: [{ required: true, message: '请输入网络设备id', trigger: 'blur' }], ndid: [{ required: true, message: '请输入网络设备id', trigger: 'blur' }],
@@ -248,19 +270,19 @@ const dialogTitle = ref('新增设备')
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const devModelOptions: any = ref([]) const devModelOptions: any = ref([])
const queryTheDictionary = () => { const queryTheDictionary = () => {
queryByCode('DEV_CLD').then(res => { queryByCode('DEV_CLD')
.then(res => {
devTypeOptions2.value = res.data devTypeOptions2.value = res.data
return queryCsDictTree(res.data.id).then(res => { queryCsDictTree(res.data.id).then(res => {
devModelOptions2.value = res.data.map((item: any) => { devModelOptions2.value = res.data.map((item: any) => {
return { return {
value: item.id, value: item.id,
label: item.name, label: item.name,
...item ...item
} }
}) })
}) })
}).then(() => { queryByCode('Device_Type').then(async res => {
queryByCode('Device_Type').then(async res => {
const id = res.data.id const id = res.data.id
await queryCsDictTree(id).then(res => { await queryCsDictTree(id).then(res => {
devTypeOptions.value = res.data.map((item: any) => { devTypeOptions.value = res.data.map((item: any) => {
@@ -270,7 +292,7 @@ const queryTheDictionary = () => {
...item ...item
} }
}) })
// let index = devTypeOptions.value.findIndex((item: any) => { // let index = devTypeOptions.value.findIndex((item: any) => {
// return item.name == '网关' // return item.name == '网关'
// }) // })
@@ -292,10 +314,11 @@ const queryTheDictionary = () => {
}) })
await tableStore.index() await tableStore.index()
}) })
}).catch(error => { })
.catch(error => {
console.error('查询过程中出现错误:', error) console.error('查询过程中出现错误:', error)
}) })
} }
const devModelOptionsFilter = computed(() => { const devModelOptionsFilter = computed(() => {
return devModelOptions.value.filter((item: any) => { return devModelOptions.value.filter((item: any) => {
@@ -308,7 +331,7 @@ const devModelOptionsFilter = computed(() => {
}) })
const formDevModelOptionsFilter = computed(() => { const formDevModelOptionsFilter = computed(() => {
if(form.devAccessMethod === 'CLD'){ if (form.devAccessMethod === 'CLD') {
return devModelOptions2.value.filter((item: any) => { return devModelOptions2.value.filter((item: any) => {
if (form.devType) { if (form.devType) {
return item.pid == form.devType return item.pid == form.devType
@@ -316,18 +339,17 @@ const formDevModelOptionsFilter = computed(() => {
return true return true
} }
}) })
}else{ } else {
return devModelOptions.value.filter((item: any) => { return devModelOptions.value.filter((item: any) => {
if (form.devType) { if (form.devType) {
return item.pid == form.devType return item.pid == form.devType
} else { } else {
return true return true
} }
}) })
} }
}) })
const formDevTypeOptions = computed(() => { const formDevTypeOptions = computed(() => {
// 如果协议是CLD使用devTypeOptions2否则使用devTypeOptions // 如果协议是CLD使用devTypeOptions2否则使用devTypeOptions
if (form.devAccessMethod === 'CLD') { if (form.devAccessMethod === 'CLD') {
@@ -362,13 +384,13 @@ const tableStore = new TableStore({
field: 'devType', field: 'devType',
formatter: row => { formatter: row => {
if (row.row.devAccessMethod === 'CLD') { if (row.row.devAccessMethod === 'CLD') {
// 遍历devTypeOptions2查找匹配 // 遍历devTypeOptions2查找匹配
return devTypeOptions2.value.name return devTypeOptions2.value.name
} }
// 如果是MQTT协议使用devTypeOptions查找 // 如果是MQTT协议使用devTypeOptions查找
else if (row.row.devAccessMethod === 'MQTT') { else if (row.row.devAccessMethod === 'MQTT') {
const item = devTypeOptions.value.find((item: any) => item.value == row.cellValue); const item = devTypeOptions.value.find((item: any) => item.value == row.cellValue)
return item ? item.label : '/'; return item ? item.label : '/'
} }
}, },
minWidth: 120 minWidth: 120
@@ -378,8 +400,8 @@ const tableStore = new TableStore({
field: 'devModel', field: 'devModel',
formatter: row => { formatter: row => {
const options = row.row.devAccessMethod === 'MQTT' ? devModelOptions.value : devModelOptions2.value const options = row.row.devAccessMethod === 'MQTT' ? devModelOptions.value : devModelOptions2.value
const item = options.find((item: any) => item.value == row.cellValue); const item = options.find((item: any) => item.value == row.cellValue)
return item ? item.label : '/'; return item ? item.label : '/'
}, },
minWidth: 120 minWidth: 120
}, },
@@ -488,12 +510,13 @@ const tableStore = new TableStore({
minWidth: 80 minWidth: 80
}, },
{ {
title: '操作', fixed: 'right', title: '操作',
fixed: 'right',
align: 'center', align: 'center',
width: 220, width: 220,
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
//直连装置注册 //直连设备注册
// { // {
// title: '注册', // title: '注册',
// type: 'primary', // type: 'primary',
@@ -589,12 +612,51 @@ const tableStore = new TableStore({
// .catch(e => {}) // .catch(e => {})
// } // }
// }, // },
// 在线设备注册
{
title: '接入',
type: 'primary',
icon: 'el-icon-Grid',
render: 'basicButton',
loading: 'loading1',
disabled: row => {
return !(
row.devType == '123202e523be2b8defc8d0c2f118f232' &&
row.associatedProject != null &&
row.status != 3
)
},
click: row => {
// 便携式设备手动接入
ElMessageBox.confirm('确定接入该设备吗?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
row.loading1 = true
onlineRegister({
projectId: row.associatedProject,
nDid: row.ndid
})
.then(res => {
ElMessage.success(res.message)
row.loading1 = false
tableStore.index()
})
.catch(() => {
row.loading1 = false
})
})
}
},
//便携式设备手动接入 //便携式设备手动接入
{ {
title: '接入', title: '接入',
type: 'primary', type: 'primary',
icon: 'el-icon-Grid', icon: 'el-icon-Grid',
render: 'basicButton', render: 'basicButton',
loading: 'loading1',
disabled: row => { disabled: row => {
return ( return (
(row.devType != '8b45cf6b7f5266e777d07c166ad5fa77' && (row.devType != '8b45cf6b7f5266e777d07c166ad5fa77' &&
@@ -610,14 +672,18 @@ const tableStore = new TableStore({
type: 'warning' type: 'warning'
}) })
.then(() => { .then(() => {
row.loading1 = true
portableDeviceAccess({ portableDeviceAccess({
nDid: row.ndid nDid: row.ndid
}).then(res => { }).then(res => {
ElMessage.success(res.message) ElMessage.success(res.message)
row.loading1 = false
tableStore.index() tableStore.index()
}) })
}) })
.catch(e => {}) .catch(e => {
row.loading1 = false
})
} }
}, },
{ {
@@ -625,6 +691,7 @@ const tableStore = new TableStore({
type: 'primary', type: 'primary',
icon: 'el-icon-Grid', icon: 'el-icon-Grid',
render: 'basicButton', render: 'basicButton',
loading: 'loading1',
disabled: row => { disabled: row => {
return ( return (
(row.devType != '8b45cf6b7f5266e777d07c166ad5fa77' && (row.devType != '8b45cf6b7f5266e777d07c166ad5fa77' &&
@@ -640,24 +707,20 @@ const tableStore = new TableStore({
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}) }).then(() => {
.then(() => { row.loading1 = true
loading.value = true portableDeviceRegister({
portableDeviceRegister({ nDid: row.ndid
nDid: row.ndid })
.then(res => {
row.loading1 = false
ElMessage.success(res.message)
tableStore.index()
}) })
.then(res => { .catch(e => {
loading.value = false row.loading1 = false
ElMessage.success(res.message) })
tableStore.index() })
})
.catch(e => {
loading.value = false
})
})
.catch(e => {
loading.value = false
})
} }
}, },
{ {
@@ -677,7 +740,6 @@ const tableStore = new TableStore({
useCORS: true useCORS: true
}).then(canvas => { }).then(canvas => {
let url = canvas.toDataURL('image/png') let url = canvas.toDataURL('image/png')
console.log('🚀 ~ html2canvas ~ url:', url)
// 下载图片 // 下载图片
let a = document.createElement('a') let a = document.createElement('a')
let event = new MouseEvent('click') let event = new MouseEvent('click')
@@ -701,9 +763,35 @@ const tableStore = new TableStore({
for (let key in form) { for (let key in form) {
form[key] = row[key] form[key] = row[key]
} }
form.association = row.associatedProject
? [row.associatedEngineering, row.associatedProject]
: []
form.id = row.id form.id = row.id
} }
}, },
{
name: 'edit',
title: '重置',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: async row => {
ElMessageBox.confirm('确定重置该设备吗?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
resetFactory({
nDid: row.ndid
}).then(res => {
ElMessage.success('重置成功!')
tableStore.onTableAction('search', {})
})
})
}
},
{ {
name: 'del', name: 'del',
title: '删除', title: '删除',
@@ -741,14 +829,13 @@ const tableStore = new TableStore({
: '' : ''
} }
}) })
console.log('🚀 ~ loadCallback ~ tableStore.table:', tableStore.table)
tableKey.value += 1 tableKey.value += 1
} }
}) })
tableStore.table.params.orderBy = 'desc' tableStore.table.params.orderBy = 'desc'
tableStore.table.params.devType = '' tableStore.table.params.devType = ''
tableStore.table.params.devModel = '' tableStore.table.params.devModel = ''
tableStore.table.params.process = 2 // tableStore.table.params.process = 2
tableStore.table.params.devAccessMethod = '' tableStore.table.params.devAccessMethod = ''
tableStore.table.params.status = '' tableStore.table.params.status = ''
tableStore.table.params.sortBy = '' tableStore.table.params.sortBy = ''
@@ -877,6 +964,9 @@ const add = () => {
const onSubmit = () => { const onSubmit = () => {
ruleFormRef.value.validate((valid: any) => { ruleFormRef.value.validate((valid: any) => {
if (valid) { if (valid) {
form.associatedEngineering = form.association ? form.association[0] : ''
form.associatedProject = form.association ? form.association[1] : ''
if (dialogTitle.value == '新增设备') { if (dialogTitle.value == '新增设备') {
addEquipmentDelivery(form).then(res => { addEquipmentDelivery(form).then(res => {
ElMessage.success('新增成功') ElMessage.success('新增成功')
@@ -923,6 +1013,9 @@ const resetForm = () => {
devModel: '', devModel: '',
devType: '', devType: '',
name: '', name: '',
associatedEngineering: '',
associatedProject: '',
association: [],
ndid: '' ndid: ''
} }
Object.assign(form, user) Object.assign(form, user)
@@ -939,6 +1032,15 @@ provide('tableStore', tableStore)
onMounted(() => { onMounted(() => {
queryTheDictionary() queryTheDictionary()
engineeringProject().then(res => {
engineeringList.value = res.data.filter(item => {
item.projectName = item.engineeringName
item.projectId = item.engineeringId
item.disabled = item.projectInfoList ? false : true
return item
})
})
setTimeout(() => {}, 100) setTimeout(() => {}, 100)
}) })

View File

@@ -9,13 +9,13 @@
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="流程阶段"> <!-- <el-form-item label="流程阶段">
<el-select v-model.trim="tableStore.table.params.process" clearable placeholder="请选择"> <el-select v-model.trim="tableStore.table.params.process" clearable placeholder="请选择">
<el-option label="功能调试" :value="2"></el-option> <el-option label="功能调试" :value="2"></el-option>
<el-option label="出厂调试" :value="3"></el-option> <el-option label="出厂调试" :value="3"></el-option>
<el-option label="正式投运" :value="4"></el-option> <el-option label="正式投运" :value="4"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item> -->
</template> </template>
<template v-slot:operation> <template v-slot:operation>
<el-button type="primary" icon="el-icon-Download" @click="exportTab">导出</el-button> <el-button type="primary" icon="el-icon-Download" @click="exportTab">导出</el-button>
@@ -147,7 +147,6 @@ const tableStore = new TableStore({
item.process = item.process == 2 ? '功能调试' : item.process == 3 ? '出厂调试' : '正式投运' item.process = item.process == 2 ? '功能调试' : item.process == 3 ? '出厂调试' : '正式投运'
return item return item
}) })
console.log('🚀 ~ tableStore.table.allData:', tableStore.table.allData)
}, },
loadCallback: () => { loadCallback: () => {
let name = tableStore.table.params.name let name = tableStore.table.params.name
@@ -176,7 +175,7 @@ const tableStore = new TableStore({
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.process = 4 // tableStore.table.params.process = 4
tableStore.table.params.name = '' tableStore.table.params.name = ''
const tableRef = ref() const tableRef = ref()
const exportTab = () => { const exportTab = () => {

View File

@@ -0,0 +1,94 @@
<template>
<el-dialog v-model="dialogVisible" :title="title" width="800" :close-on-click-modal="false"
:before-close="handleClose">
<!-- 表格区域 -->
<div class="table-area" :style="layout1">
<vxe-table v-bind="defaultAttribute" :data="tableData" v-loading="loading" height="auto">
<vxe-column type="seq" title="序号" width="80" align="center" />
<vxe-column field="version" title="版本" min-width="120" align="center" />
<vxe-column field="result" title="升级结果" width="120" align="center">
<template #default="{ row }">
<el-tag :type="getResultTagType(row.result)" size="small">
{{ getResultText(row.result) }}
</el-tag>
</template>
</vxe-column>
<vxe-column field="upgradeTime" title="升级时间" width="180" align="center" />
<vxe-column field="upgradeUser" title="升级人" width="120" align="center" />
</vxe-table>
</div>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { ElMessage } from 'element-plus'
import { mainHeight } from '@/utils/layout'
import { getByDevId } from '@/api/cs-device-boot/cloudDeviceEntry'
const layout1 = mainHeight(100, 2)
// 弹框显示控制
const dialogVisible = ref(false)
// 表格数据
const tableData = ref([])
// 加载状态
const loading = ref(false)
const title = ref('升级日志')
// 升级结果标签类型映射
const getResultTagType = (result) => {
const map = {
success: 'success',
fail: 'danger',
processing: 'warning'
}
return map[result] || 'info'
}
// 升级结果文本映射
const getResultText = (result) => {
const map = {
success: '成功',
fail: '失败',
processing: '进行中'
}
return map[result] || result
}
// 打开弹框
const open = (row) => {
title.value = `${row.name}_升级日志`
dialogVisible.value = true
getByDevId({
devId: row.id
}).then((res) => {
tableData = res.data
})
}
// 加载表格数据
const loadTableData = async (deviceId) => {
loading.value = true
}
// 关闭弹框
const handleClose = () => {
dialogVisible.value = false
// 重置数据
tableData.value = []
}
// 暴露方法给父组件
defineExpose({
open
})
</script>
<style scoped></style>

View File

@@ -0,0 +1,114 @@
<template>
<el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title">
<el-form :model="form" class="form-one" label-width="auto" ref="formRef" :rules="rules">
<el-form-item label="版本:" prop="edDataId">
<el-select v-model.trim="form.edDataId" placeholder="请选择版本" @change="selectChange">
<el-option v-for="item in devList" :disabled="version == item.versionNo"
:label="`${item.devTypeName}_${item.versionNo}_${item.versionAgreement}_${item.versionDate}`"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="描述:">
<el-input v-model.trim="versionDesc" disabled type="textarea" :rows="3"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submit" :loading="loading">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, inject, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { findByDevTypeId, upgrade } from '@/api/system-boot/dictTree'
const formRef = ref()
const dialogVisible = ref(false)
const title = ref('程序版本')
const devList: any = ref([])
const versionDesc = ref('')
const version = ref('')
// 表单数据(新增了版本类型、版本描述字段)
const form = reactive<any>({
devId: [],
edDataId: '' // 新增:下拉选择版本
})
const loading = ref(false)
// 校验规则(新增两个字段的必填校验)
const rules = {
// 新增校验
edDataId: [{ required: true, message: '请选择版本', trigger: 'change' }]
}
// 打开弹窗
const open = (data: any) => {
loading.value = false
version.value = data.version
form.devId = []
form.edDataId = ''
versionDesc.value = ''
getFindByDev(data.devModel)
formRef.value?.resetFields()
dialogVisible.value = true
form.devId = data.id
}
// 获取版本
const getFindByDev = (devModel: string) => {
findByDevTypeId({
devType: devModel, //'45afe3ad9970785090cd7da27f489c37',
pageNum: 1,
pageSize: 10000
}).then((res: any) => {
devList.value = res.data.records
if (devList.value.length == 0) {
ElMessage.warning('该设备没有可升级的版本!')
return
} else if (devList.value.length == 1 && devList.value[0].versionNo == version.value) {
ElMessage.warning('该设备没有可升级的版本!')
}
})
}
// 提交
const submit = () => {
formRef.value.validate(async (valid: boolean) => {
if (valid) {
try {
ElMessage.info('升级中,请稍等...')
loading.value = true
upgrade({
devId: form.devId.join(),
edDataId: form.edDataId
})
.then(res => {
ElMessage.success('升级成功')
loading.value = false
dialogVisible.value = false
})
.catch(error => {
loading.value = false
})
} catch (error) {
loading.value = false
ElMessage.error('升级失败')
}
}
})
}
const selectChange = e => {
devList.value.forEach(item => {
if (item.id === e) {
versionDesc.value = item.description
}
})
}
defineExpose({ open })
</script>

View File

@@ -0,0 +1,217 @@
<template>
<div class="default-main">
<TableHeader ref="tableHeaderRef" showExport>
<template #select>
<el-form-item label="关键字筛选">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入设备名称"
style="width:200px" />
</el-form-item>
<el-form-item label="通讯状态">
<el-select v-model.trim="tableStore.table.params.connectStatus" filterable placeholder="请选择通讯状态"
clearable>
<el-option label="中断" :value="0"></el-option>
<el-option label="正常" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备型号">
<el-select v-model.trim="tableStore.table.params.devModel" filterable placeholder="请选择设备型号"
clearable>
<el-option v-for="item in DevTypeOptions" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="icd模型">
<el-select v-model.trim="tableStore.table.params.icd" filterable placeholder="请选择设备系列" clearable>
<el-option v-for="item in icdList" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="升级设备筛选">
<el-select v-model.trim="tableStore.table.params.upgrade" filterable placeholder="请选择升级设备筛选"
clearable>
<el-option label="可升级" :value="1"></el-option>
<el-option label="不可升级" :value="0"></el-option>
</el-select>
</el-form-item>
</template>
<template #operation>
<el-button :icon="Document" type="primary" class="ml10" @click="maintenance">版本维护</el-button>
<!-- <el-button :icon="Share" type="primary" class="ml10" @click="upgradeVersion">批量升级</el-button> -->
</template>
</TableHeader>
<Table :checkbox-config="checkboxConfig" ref="tableRef" />
<upgrade ref="upgradeRef" />
<deviceLog ref="deviceLogRef" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide } 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 { queryByCode, queryByid } from '@/api/system-boot/dictTree'
import { useDictData } from '@/stores/dictData'
import { Share, Document } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'
import upgrade from './comp/upgrade.vue'
import deviceLog from './comp/deviceLog.vue'
defineOptions({
name: 'govern/manage/programVersion'
})
const upgradeRef = ref()
const deviceLogRef = ref()
const dictData = useDictData()
const DevTypeOptions = ref()
const icdList = ref()
const tableHeaderRef = ref()
const { push, options, currentRoute } = useRouter()
const tableStore: any = new TableStore({
url: '/cs-device-boot/EquipmentDelivery/version/page',
method: 'POST',
column: [
// { type: 'checkbox', width: '60', },
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '设备名称', field: 'name', minWidth: '150' },
{ title: '版本号', field: 'version', minWidth: '100', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '协议版本', field: 'protocolVersion', minWidth: '100', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '版本日期', field: 'versionDate', minWidth: '150', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '设备型号', field: 'devModelName', minWidth: '120', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: 'icd模型', field: 'icd', minWidth: '120', formatter: (row: any) => { return icdList.value.filter(item => item.id == row.cellValue)[0]?.name || '/' } },
{ title: '所属工程', field: 'associatedEngineering', minWidth: '120', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '所属项目', field: 'associatedProject', minWidth: '120', formatter: (row: any) => { return row.cellValue || '/' } },
{
title: '状态',
field: 'runStatus',
render: 'tag',
minWidth: '80',
custom: {
1: 'danger',
2: 'success'
},
replaceValue: {
1: '离线',
2: '在线'
}
},
{ title: '更新时间', field: 'updateTime', minWidth: '150', formatter: (row: any) => { return row.cellValue || '/' }, sortable: true },
{ title: '修改人员', field: 'updateByName', minWidth: '100', formatter: (row: any) => { return row.cellValue || '/' } },
{
title: '操作',
fixed: 'right',
align: 'center',
width: '130',
render: 'buttons',
buttons: [
{
name: 'productSetting',
title: '升级',
type: 'primary',
icon: 'el-icon-EditPen',
disabled: (row: any) => {
return row.upgrade == 0
},
render: 'basicButton',
click: (row: any) => {
upgradeRef.value.open({
id: [row.id],
devModel: row.devModel,
version: row.version
})
}
},
{
name: 'productSetting',
title: '日志',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: (row: any) => {
deviceLogRef.value.open(row)
}
}
]
}
],
loadCallback: () => { }
})
const checkboxConfig = reactive({
checkMethod: ({ row }) => {
return row.upgrade == 1
}
})
const getQuery = async () => {
queryByCode('Device_Type').then(res => {
const id = res.data.id
queryByid(id).then(res => {
res.data.map((item: any, index: any) => {
if (item.pid == id) {
res.data.splice(index, 1)
}
})
DevTypeOptions.value = res.data
})
})
queryByCode('Icd_Model').then(res => {
const id = res.data.id
queryByid(id).then(res => {
icdList.value = res.data
})
})
}
// 禁止点击
// 升级版本
const upgradeVersion = () => {
if (!tableStore.table.selection.length) {
ElMessage.warning('请选择需要升级的设备!')
return
}
// 获取第一个设备的 devModel
const firstDevModel = tableStore.table.selection[0].devModel
// 检查是否所有设备的 devModel 都相同
const allSame = tableStore.table.selection.every(device => device.devModel === firstDevModel)
if (!allSame) {
return ElMessage.warning('请选择相同设备型号的设备进行批量升级!')
}
upgradeRef.value.open(
{
id: tableStore.table.selection.map(item => item.id),
devModel: firstDevModel
}
)
}
// 版本维护
const maintenance = () => {
push({
// path: '/versionMaintenance'
path: '/version'
})
}
tableStore.table.params.searchValue = ''
tableStore.table.params.connectStatus = ''
tableStore.table.params.devModel = ''
tableStore.table.params.icd = ''
tableStore.table.params.upgrade = ''
provide('tableStore', tableStore)
onMounted(() => {
getQuery()
tableStore.index()
})
</script>

View File

@@ -0,0 +1,263 @@
<template>
<div class="default-main manage-realTime" :style="{ height: pageHeight.height }">
<DeviceTree @node-click="nodeClick" @init="" @deviceTypeChange=""></DeviceTree>
<div class="manage-realTime-right">
<div class="time-container">
<div>
<div>系统时间{{ realTime }}</div>
<div>终端时间{{ deviceTime }}</div>
</div>
<el-button icon="el-icon-RefreshLeft" type="primary" @click="synchronizeTime">对时</el-button>
</div>
<div :style="echartHeight" class="pl10 pr10">
<MyEchart :options="echartsData" />
</div>
<div class="pl10 pr10">
<el-table :data="tableData" border stripe :show-header="false" class="custom-table">
<el-table-column prop="label1" />
<el-table-column prop="label2" />
<el-table-column prop="value1" />
<el-table-column prop="label3" />
<el-table-column prop="value2" />
<el-table-column prop="label4" />
<el-table-column prop="value3" />
</el-table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { mainHeight } from '@/utils/layout'
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { formatToDateTime } from '@/utils/dateUtil'
defineOptions({
name: 'govern/manage/realTime'
})
//页面属性
const pageHeight = mainHeight(20)
const echartHeight = mainHeight(180)
const realTime = ref<string>('')
const deviceTime = ref<string>('')
const timer = ref<any>(null)
const echartsData = ref<any>({
title: {
text: '终端性能'
},
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
},
legend: {
bottom: 0,
data: ['CPU1使用率', 'CPU2使用率', '内存使用率', '磁盘使用率']
},
xAxis: {
type: 'time',
name: '时间',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
yAxis: {
name: '%',
type: 'value',
min: 0,
max: 100
},
series: [
{
name: 'CPU1使用率',
type: 'line',
showSymbol: false,
smooth: true,
data: [
['2025-01-01 08:00:00', 10],
['2025-01-01 09:00:00', 10],
['2025-01-01 010:00:00', 10]
]
},
{
name: 'CPU2使用率',
type: 'line',
showSymbol: false,
smooth: true,
data: [
['2025-01-01 08:00:00', 11],
['2025-01-01 09:00:00', 11],
['2025-01-01 010:00:00', 11]
]
},
{
name: '内存使用率',
type: 'line',
showSymbol: false,
smooth: true,
data: [
['2025-01-01 08:00:00', 12],
['2025-01-01 09:00:00', 12],
['2025-01-01 010:00:00', 12]
]
},
{
name: '磁盘使用率',
type: 'line',
showSymbol: false,
smooth: true,
data: [
['2025-01-01 08:00:00', 13],
['2025-01-01 09:00:00', 13],
['2025-01-01 010:00:00', 13]
]
}
]
})
const tableData = ref([
{
label1: 'cpu1负载(%)',
label2: '使用:',
value1: '43',
label3: '剩余:',
value2: '57',
label4: '',
value3: ''
},
{
label1: 'cpu2负载(%)',
label2: '使用:',
value1: '/',
label3: '剩余:',
value2: '/',
label4: '',
value3: ''
},
{
label1: '终端内存详情(MB)',
label2: '内存总量:',
value1: '489',
label3: '已使用:',
value2: '143',
label4: '未使用:',
value3: '346.00'
},
{
label1: '主存储器详情(GB)',
label2: '主存储器总量:',
value1: '6.44',
label3: '已使用:',
value2: '6.03',
label4: '未使用:',
value3: '0.41'
},
{
label1: '其余信息',
label2: '最后对时对标:',
value1: '1970-01-01 08:00:00',
label3: '信号强度(dBm):',
value2: '-',
label4: '',
value3: ''
}
])
const nodeClick = async (e: anyObj) => {
console.log('点击设备树节点')
}
const handleClose = () => {
console.log('close')
}
const synchronizeTime = async () => {
console.log('对时')
}
onMounted(() => {
timer.value = setInterval(() => {
realTime.value = formatToDateTime(new Date())
}, 1000)
})
// 在组件卸载时清除定时器
onUnmounted(() => {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
})
</script>
<style scoped lang="scss">
.manage-realTime {
display: flex;
&-right {
height: 100%;
overflow: hidden;
flex: 1;
padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
border: 1px solid #ebeef5; // 加个边框方便查看效果
.time-container {
display: flex;
justify-content: flex-end; // 让内容靠右
align-items: center; // 让内容垂直居中(避免高度不一致时偏移)
* {
margin-right: 15px;
}
.el-button * {
margin-right: 0;
}
}
:deep(.el-table__cell) {
text-align: center;
}
/* 1. 加深表格边框线条 */
:deep(.custom-table.el-table) {
border: 1px solid #dcdfe6;
width: auto;
}
:deep(.custom-table .el-table__cell) {
border: 0.5px solid #dcdfe6;
}
/* 2. 第 1、2、4、6 列显示深色背景 */
:deep(.custom-table .el-table__row .el-table__cell:nth-child(1)) {
background-color: #f5f7fa;
font-weight: bold;
}
/* 第 3、5、7 列显示白色背景 */
:deep(.custom-table .el-table__row .el-table__cell:nth-child(3)),
:deep(.custom-table .el-table__row .el-table__cell:nth-child(5)),
:deep(.custom-table .el-table__row .el-table__cell:nth-child(7)) {
// background-color: #fff;
}
}
}
</style>

View File

@@ -16,9 +16,8 @@ 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 { Setting } from '@element-plus/icons-vue' import { Setting } from '@element-plus/icons-vue'
import { eventRecall, fileRecall, logRecall } from '@/api/cs-device-boot/recall' import { eventRecall, fileRecall, } from '@/api/cs-device-boot/recall'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { el } from 'element-plus/es/locale'
const props = defineProps({ const props = defineProps({
checkedNodes: { checkedNodes: {
@@ -147,9 +146,11 @@ const recall1 = async () => {
await eventRecall({ await eventRecall({
startTime: tableStore.table.params.startTime, startTime: tableStore.table.params.startTime,
endTime: tableStore.table.params.endTime, endTime: tableStore.table.params.endTime,
lineList: props.checkedNodes.map((node: any) => node.id) lineList: props.checkedNodes.map((node: any) => node.id),
}).then((res: any) => { }).then((res: any) => {
ElMessage.success('补召事件成功') ElMessage.success('补召事件成功')
tableStore.index()
}) })
} }

View File

@@ -16,7 +16,7 @@ import Table from '@/components/table/index.vue'
import { ElMessage, ElTree } from 'element-plus' import { ElMessage, ElTree } from 'element-plus'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { Setting } from '@element-plus/icons-vue' import { Setting } from '@element-plus/icons-vue'
import { eventRecall, fileRecall } from '@/api/cs-device-boot/recall'
const props = defineProps({ const props = defineProps({
checkedNodes: { checkedNodes: {
type: Array, type: Array,
@@ -68,23 +68,23 @@ const tableStore: any = new TableStore({
2: '解析失败', 2: '解析失败',
3: '文件不存在' 3: '文件不存在'
} }
},
{
title: '操作',
fixed: 'right',
width: '100',
render: 'buttons',
buttons: [
{
name: 'edit',
title: '详情',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {}
}
]
} }
// {
// title: '操作',
// fixed: 'right',
// width: '100',
// render: 'buttons',
// buttons: [
// {
// name: 'edit',
// title: '详情',
// type: 'primary',
// icon: 'el-icon-EditPen',
// render: 'basicButton',
// click: row => {}
// }
// ]
// }
], ],
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => {} loadCallback: () => {}
@@ -92,8 +92,22 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore) provide('tableStore', tableStore)
const exportTab = () => { const exportTab = async () => {
ElMessage.info('稳态补召功能暂未开发!') // ElMessage.info('稳态补召功能暂未开发!')
if (!props.checkedNodes || props.checkedNodes.length === 0) {
ElMessage.warning('请先勾选监测点')
return
}
await eventRecall({
startTime: tableStore.table.params.startTime,
endTime: tableStore.table.params.endTime,
lineList: props.checkedNodes.map((node: any) => node.id),
bzType: 0
}).then((res: any) => {
ElMessage.success('补召成功')
tableStore.index()
})
} }
//获取请求参数 //获取请求参数
const getTableParams = (val: any) => { const getTableParams = (val: any) => {

View File

@@ -1,349 +1,326 @@
// import { createCellPos } from './translateNumToLetter' // import { createCellPos } from './translateNumToLetter'
import Excel from 'exceljs' import Excel from 'exceljs'
import FileSaver from 'file-saver' import FileSaver from 'file-saver'
const exportExcel = function(luckysheet, value) { const exportExcel = function (luckysheet, value) {
// 参数为luckysheet.getluckysheetfile()获取的对象 // 参数为luckysheet.getluckysheetfile()获取的对象
// 1.创建工作簿,可以为工作簿添加属性 // 1.创建工作簿,可以为工作簿添加属性
const workbook = new Excel.Workbook() const workbook = new Excel.Workbook()
// 2.创建表格,第二个参数可以配置创建什么样的工作表 // 2.创建表格,第二个参数可以配置创建什么样的工作表
if (Object.prototype.toString.call(luckysheet) === '[object Object]') { if (Object.prototype.toString.call(luckysheet) === '[object Object]') {
luckysheet = [luckysheet] luckysheet = [luckysheet]
} }
luckysheet.forEach(function(table) { luckysheet.forEach(function (table) {
if (table.data.length === 0) return true if (table.data.length === 0) return true
// ws.getCell('B2').fill = fills. // ws.getCell('B2').fill = fills.
const worksheet = workbook.addWorksheet(table.name) const worksheet = workbook.addWorksheet(table.name)
const merge = (table.config && table.config.merge) || {} const merge = (table.config && table.config.merge) || {}
const borderInfo = (table.config && table.config.borderInfo) || {} const borderInfo = (table.config && table.config.borderInfo) || {}
// 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值 // 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值
setStyleAndValue(table.data, worksheet) setStyleAndValue(table.data, worksheet)
setMerge(merge, worksheet) setMerge(merge, worksheet)
setBorder(borderInfo, worksheet) setBorder(borderInfo, worksheet)
return true return true
}) })
// return // return
// 4.写入 buffer // 4.写入 buffer
const buffer = workbook.xlsx.writeBuffer().then(data => { const buffer = workbook.xlsx.writeBuffer().then(data => {
// console.log('data', data) // console.log('data', data)
const blob = new Blob([data], { const blob = new Blob([data], {
type: 'application/vnd.ms-excel;charset=utf-8' type: 'application/vnd.ms-excel;charset=utf-8'
}) })
console.log("导出成功!") console.log('导出成功!')
FileSaver.saveAs(blob, `${value}.xlsx`) FileSaver.saveAs(blob, `${value}.xlsx`)
}) })
return buffer return buffer
} }
var setMerge = function(luckyMerge = {}, worksheet) { var setMerge = function (luckyMerge = {}, worksheet) {
const mergearr = Object.values(luckyMerge) const mergearr = Object.values(luckyMerge)
mergearr.forEach(function(elem) { mergearr.forEach(function (elem) {
// elem格式{r: 0, c: 0, rs: 1, cs: 2} // elem格式{r: 0, c: 0, rs: 1, cs: 2}
// 按开始行,开始列,结束行,结束列合并(相当于 K10:M12 // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12
worksheet.mergeCells( worksheet.mergeCells(elem.r + 1, elem.c + 1, elem.r + elem.rs, elem.c + elem.cs)
elem.r + 1, })
elem.c + 1, }
elem.r + elem.rs,
elem.c + elem.cs var setBorder = function (luckyBorderInfo, worksheet) {
) if (!Array.isArray(luckyBorderInfo)) return
}) // console.log('luckyBorderInfo', luckyBorderInfo)
} luckyBorderInfo.forEach(function (elem) {
// 现在只兼容到borderType 为range的情况
var setBorder = function(luckyBorderInfo, worksheet) { // console.log('ele', elem)
if (!Array.isArray(luckyBorderInfo)) return if (elem.rangeType === 'range') {
// console.log('luckyBorderInfo', luckyBorderInfo) let border = borderConvert(elem.borderType, elem.style, elem.color)
luckyBorderInfo.forEach(function(elem) { let rang = elem.range[0]
// 现在只兼容到borderType 为range的情况 // console.log('range', rang)
// console.log('ele', elem) let row = rang.row
if (elem.rangeType === 'range') { let column = rang.column
let border = borderConvert(elem.borderType, elem.style, elem.color) for (let i = row[0] + 1; i < row[1] + 2; i++) {
let rang = elem.range[0] for (let y = column[0] + 1; y < column[1] + 2; y++) {
// console.log('range', rang) worksheet.getCell(i, y).border = border
let row = rang.row }
let column = rang.column }
for (let i = row[0] + 1; i < row[1] + 2; i++) { }
for (let y = column[0] + 1; y < column[1] + 2; y++) { if (elem.rangeType === 'cell') {
worksheet.getCell(i, y).border = border // col_index: 2
} // row_index: 1
} // b: {
} // color: '#d0d4e3'
if (elem.rangeType === 'cell') { // style: 1
// col_index: 2 // }
// row_index: 1 const { col_index, row_index } = elem.value
// b: { const borderData = Object.assign({}, elem.value)
// color: '#d0d4e3' delete borderData.col_index
// style: 1 delete borderData.row_index
// } let border = addborderToCell(borderData, row_index, col_index)
const { col_index, row_index } = elem.value // console.log('bordre', border, borderData)
const borderData = Object.assign({}, elem.value) worksheet.getCell(row_index + 1, col_index + 1).border = border
delete borderData.col_index }
delete borderData.row_index // console.log(rang.column_focus + 1, rang.row_focus + 1)
let border = addborderToCell(borderData, row_index, col_index) // worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border
// console.log('bordre', border, borderData) })
worksheet.getCell(row_index + 1, col_index + 1).border = border }
} var setStyleAndValue = function (cellArr, worksheet) {
// console.log(rang.column_focus + 1, rang.row_focus + 1) if (!Array.isArray(cellArr)) return
// worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border cellArr.forEach(function (row, rowid) {
}) row.every(function (cell, columnid) {
} if (!cell) return true
var setStyleAndValue = function(cellArr, worksheet) { let fill = fillConvert(cell.bg)
if (!Array.isArray(cellArr)) return
cellArr.forEach(function(row, rowid) { let font = fontConvert(cell.ff, cell.fc, cell.bl, cell.it, cell.fs, cell.cl, cell.ul)
row.every(function(cell, columnid) { let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr)
if (!cell) return true let value = ''
let fill = fillConvert(cell.bg)
if (cell.f) {
let font = fontConvert( value = { formula: cell.f, result: cell.v }
cell.ff, } else if (!cell.v && cell.ct && cell.ct.s) {
cell.fc, // xls转为xlsx之后内部存在不同的格式都会进到富文本里即值不存在与cell.v而是存在于cell.ct.s之后
cell.bl, // value = cell.ct.s[0].v
cell.it, cell.ct.s.forEach(arr => {
cell.fs, value += arr.v
cell.cl, })
cell.ul } else {
) value = cell.v
let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr) }
let value = '' // style 填入到_value中可以实现填充色
let letter = createCellPos(columnid)
if (cell.f) { let target = worksheet.getCell(letter + (rowid + 1))
value = { formula: cell.f, result: cell.v } // console.log('1233', letter + (rowid + 1))
} else if (!cell.v && cell.ct && cell.ct.s) { for (const key in fill) {
// xls转为xlsx之后内部存在不同的格式都会进到富文本里即值不存在与cell.v而是存在于cell.ct.s之后 target.fill = fill
// value = cell.ct.s[0].v break
cell.ct.s.forEach(arr => { }
value += arr.v target.font = font
}) target.alignment = alignment
} else { target.value = value
value = cell.v
} return true
// style 填入到_value中可以实现填充色 })
let letter = createCellPos(columnid) })
let target = worksheet.getCell(letter + (rowid + 1)) }
// console.log('1233', letter + (rowid + 1))
for (const key in fill) { var fillConvert = function (bg) {
target.fill = fill if (!bg) {
break return {}
} }
target.font = font // const bgc = bg.replace('#', '')
target.alignment = alignment let fill = {
target.value = value type: 'pattern',
pattern: 'solid',
return true fgColor: { argb: bg.replace('#', '') }
}) }
}) return fill
} }
var fillConvert = function(bg) { var fontConvert = function (ff = 0, fc = '#000000', bl = 0, it = 0, fs = 10, cl = 0, ul = 0) {
if (!bg) { // luckysheetff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)
return {} const luckyToExcel = {
} 0: '微软雅黑',
// const bgc = bg.replace('#', '') 1: '宋体Song',
let fill = { 2: '黑体ST Heiti',
type: 'pattern', 3: '楷体ST Kaiti',
pattern: 'solid', 4: '仿宋ST FangSong',
fgColor: { argb: bg.replace('#', '') } 5: '新宋体ST Song',
} 6: '华文新魏',
return fill 7: '华文行楷',
} 8: '华文隶书',
9: 'Arial',
var fontConvert = function( 10: 'Times New Roman ',
ff = 0, 11: 'Tahoma ',
fc = '#000000', 12: 'Verdana',
bl = 0, num2bl: function (num) {
it = 0, return num === 0 ? false : true
fs = 10, }
cl = 0, }
ul = 0 // 出现Bug导入的时候ff为luckyToExcel的val
) {
// luckysheetff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线) let font = {
const luckyToExcel = { name: typeof ff === 'number' ? luckyToExcel[ff] : ff,
0: '微软雅黑', family: 1,
1: '宋体Song', size: fs,
2: '黑体ST Heiti', color: { argb: fc.replace('#', '') },
3: '楷体ST Kaiti', bold: luckyToExcel.num2bl(bl),
4: '仿宋ST FangSong', italic: luckyToExcel.num2bl(it),
5: '新宋体ST Song', underline: luckyToExcel.num2bl(ul),
6: '华文新魏', strike: luckyToExcel.num2bl(cl)
7: '华文行楷', }
8: '华文隶书',
9: 'Arial', return font
10: 'Times New Roman ', }
11: 'Tahoma ',
12: 'Verdana', var alignmentConvert = function (vt = 'default', ht = 'default', tb = 'default', tr = 'default') {
num2bl: function(num) { // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)
return num === 0 ? false : true const luckyToExcel = {
} vertical: {
} 0: 'middle',
// 出现Bug导入的时候ff为luckyToExcel的val 1: 'top',
2: 'bottom',
let font = { default: 'top'
name: typeof ff === 'number' ? luckyToExcel[ff] : ff, },
family: 1, horizontal: {
size: fs, 0: 'center',
color: { argb: fc.replace('#', '') }, 1: 'left',
bold: luckyToExcel.num2bl(bl), 2: 'right',
italic: luckyToExcel.num2bl(it), default: 'left'
underline: luckyToExcel.num2bl(ul), },
strike: luckyToExcel.num2bl(cl) wrapText: {
} 0: false,
1: false,
return font 2: true,
} default: false
},
var alignmentConvert = function( textRotation: {
vt = 'default', 0: 0,
ht = 'default', 1: 45,
tb = 'default', 2: -45,
tr = 'default' 3: 'vertical',
) { 4: 90,
// luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转) 5: -90,
const luckyToExcel = { default: 0
vertical: { }
0: 'middle', }
1: 'top',
2: 'bottom', let alignment = {
default: 'top' vertical: luckyToExcel.vertical[vt],
}, horizontal: luckyToExcel.horizontal[ht],
horizontal: { wrapText: luckyToExcel.wrapText[tb],
0: 'center', textRotation: luckyToExcel.textRotation[tr]
1: 'left', }
2: 'right', return alignment
default: 'left' }
},
wrapText: { var borderConvert = function (borderType, style = 1, color = '#000') {
0: false, // 对应luckysheet的config中borderinfo的的参数
1: false, if (!borderType) {
2: true, return {}
default: false }
}, const luckyToExcel = {
textRotation: { type: {
0: 0, 'border-all': 'all',
1: 45, 'border-top': 'top',
2: -45, 'border-right': 'right',
3: 'vertical', 'border-bottom': 'bottom',
4: 90, 'border-left': 'left'
5: -90, },
default: 0 style: {
} 0: 'none',
} 1: 'thin',
2: 'hair',
let alignment = { 3: 'dotted',
vertical: luckyToExcel.vertical[vt], 4: 'dashDot', // 'Dashed',
horizontal: luckyToExcel.horizontal[ht], 5: 'dashDot',
wrapText: luckyToExcel.wrapText[tb], 6: 'dashDotDot',
textRotation: luckyToExcel.textRotation[tr] 7: 'double',
} 8: 'medium',
return alignment 9: 'mediumDashed',
} 10: 'mediumDashDot',
11: 'mediumDashDotDot',
var borderConvert = function(borderType, style = 1, color = '#000') { 12: 'slantDashDot',
// 对应luckysheet的config中borderinfo的的参数 13: 'thick'
if (!borderType) { }
return {} }
} let template = {
const luckyToExcel = { style: luckyToExcel.style[style],
type: { color: { argb: color.replace('#', '') }
'border-all': 'all', }
'border-top': 'top', let border = {}
'border-right': 'right', if (luckyToExcel.type[borderType] === 'all') {
'border-bottom': 'bottom', border['top'] = template
'border-left': 'left' border['right'] = template
}, border['bottom'] = template
style: { border['left'] = template
0: 'none', } else {
1: 'thin', border[luckyToExcel.type[borderType]] = template
2: 'hair', }
3: 'dotted', // console.log('border', border)
4: 'dashDot', // 'Dashed', return border
5: 'dashDot', }
6: 'dashDotDot',
7: 'double', function addborderToCell(borders, row_index, col_index) {
8: 'medium', let border = {}
9: 'mediumDashed', const luckyExcel = {
10: 'mediumDashDot', type: {
11: 'mediumDashDotDot', l: 'left',
12: 'slantDashDot', r: 'right',
13: 'thick' b: 'bottom',
} t: 'top'
} },
let template = { style: {
style: luckyToExcel.style[style], 0: 'none',
color: { argb: color.replace('#', '') } 1: 'thin',
} 2: 'hair',
let border = {} 3: 'dotted',
if (luckyToExcel.type[borderType] === 'all') { 4: 'dashDot', // 'Dashed',
border['top'] = template 5: 'dashDot',
border['right'] = template 6: 'dashDotDot',
border['bottom'] = template 7: 'double',
border['left'] = template 8: 'medium',
} else { 9: 'mediumDashed',
border[luckyToExcel.type[borderType]] = template 10: 'mediumDashDot',
} 11: 'mediumDashDotDot',
// console.log('border', border) 12: 'slantDashDot',
return border 13: 'thick'
} }
}
function addborderToCell(borders, row_index, col_index) { // console.log('borders', borders)
let border = {} for (const bor in borders) {
const luckyExcel = { const borderItem = borders[bor]
type: { // 同时判断边框对象 和 color 属性都存在
l: 'left', if (!borderItem || !borderItem.color) {
r: 'right', continue
b: 'bottom', }
t: 'top'
}, if (borderItem.color.indexOf('rgb') === -1) {
style: { border[luckyExcel.type[bor]] = {
0: 'none', style: luckyExcel.style[borderItem.style],
1: 'thin', color: { argb: borderItem.color.replace('#', '') }
2: 'hair', }
3: 'dotted', } else {
4: 'dashDot', // 'Dashed', border[luckyExcel.type[bor]] = {
5: 'dashDot', style: luckyExcel.style[borderItem.style],
6: 'dashDotDot', color: { argb: borderItem.color }
7: 'double', }
8: 'medium', }
9: 'mediumDashed', }
10: 'mediumDashDot',
11: 'mediumDashDotDot', return border
12: 'slantDashDot', }
13: 'thick'
} function createCellPos(n) {
} let ordA = 'A'.charCodeAt(0)
// console.log('borders', borders)
for (const bor in borders) { let ordZ = 'Z'.charCodeAt(0)
// console.log(bor) let len = ordZ - ordA + 1
if (borders[bor].color.indexOf('rgb') === -1) { let s = ''
border[luckyExcel.type[bor]] = { while (n >= 0) {
style: luckyExcel.style[borders[bor].style], s = String.fromCharCode((n % len) + ordA) + s
color: { argb: borders[bor].color.replace('#', '') }
} n = Math.floor(n / len) - 1
} else { }
border[luckyExcel.type[bor]] = { return s
style: luckyExcel.style[borders[bor].style], }
color: { argb: borders[bor].color }
} export { exportExcel }
}
}
return border
}
function createCellPos(n) {
let ordA = 'A'.charCodeAt(0)
let ordZ = 'Z'.charCodeAt(0)
let len = ordZ - ordA + 1
let s = ''
while (n >= 0) {
s = String.fromCharCode((n % len) + ordA) + s
n = Math.floor(n / len) - 1
}
return s
}
export {
exportExcel
}

View File

@@ -1,14 +1,14 @@
<template> <template>
<div class="default-main"> <div class="default-main">
<div v-show="show && lookShow" style="display: flex"> <el-row v-show="show && lookShow">
<div style="width: 600px"> <el-col :span="10">
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">报表列表</div> <div class="title">报表列表</div>
<el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增报表</el-button> <el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增报表</el-button>
</div> </div>
<Table ref="tableRef" @currentChange="currentChange" /> <Table ref="tableRef" @currentChange="currentChange" />
</div> </el-col>
<div style="flex: 1"> <el-col :span="14">
<div class="custom-table-header"> <div class="custom-table-header">
<div class="title">模版绑定</div> <div class="title">模版绑定</div>
@@ -61,8 +61,9 @@
</vxe-column> </vxe-column>
</vxe-table> </vxe-table>
</div> </div>
</div> </el-col>
</div> </el-row>
<luckysheet ref="luckysheetRef" v-if="!show" @shutDown="shutDown" /> <luckysheet ref="luckysheetRef" v-if="!show" @shutDown="shutDown" />
<!-- 查看 --> <!-- 查看 -->
<look ref="lookRef" v-if="!lookShow" @shutDown="shutDown" /> <look ref="lookRef" v-if="!lookShow" @shutDown="shutDown" />

View File

@@ -1,5 +1,5 @@
<template> <template>
<el-dialog class="cn-operate-dialog" width="600px" v-model.trim="dialogVisible" :title="title"> <el-dialog class="cn-operate-dialog" width="500px" v-model.trim="dialogVisible" :title="title">
<el-form :model="form" class="form-one" label-width="auto" ref="formRef" :rules="rules"> <el-form :model="form" class="form-one" label-width="auto" ref="formRef" :rules="rules">
<el-form-item label="所属厂站名称" prop="substationName"> <el-form-item label="所属厂站名称" prop="substationName">
<el-input v-model.trim="form.substationName" placeholder="请输入所属厂站名称"></el-input> <el-input v-model.trim="form.substationName" placeholder="请输入所属厂站名称"></el-input>
@@ -8,7 +8,7 @@
<el-input v-model.trim="form.name" placeholder="请输入敏感用户名称"></el-input> <el-input v-model.trim="form.name" placeholder="请输入敏感用户名称"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="敏感负荷类型" prop="loadType"> <el-form-item label="敏感负荷类型" prop="loadType">
<el-select v-model.trim="form.loadType" filterable clearable placeholder="请选择数据分类"> <el-select v-model.trim="form.loadType" filterable clearable placeholder="请选择敏感负荷类型">
<el-option <el-option
v-for="item in DataTypeSelect" v-for="item in DataTypeSelect"
:key="item.id" :key="item.id"

View File

@@ -1,6 +1,18 @@
<template> <template>
<div class="default-main"> <div class="default-main">
<TableHeader :showReset="false" showExport> <TableHeader :showReset="false" showExport>
<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>
<template #operation> <template #operation>
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button> <el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button>
</template> </template>

View File

@@ -60,7 +60,7 @@ const tableStore = new TableStore({
{ {
title: '类型', title: '类型',
field: 'type', field: 'type',
width: 200, width: 250,
formatter: row => { formatter: row => {
return options.value.filter((item: any) => item.value == row.cellValue)[0]?.name return options.value.filter((item: any) => item.value == row.cellValue)[0]?.name
} }

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