zhaobing’
1 year ago
40 changed files with 5115 additions and 54 deletions
@ -0,0 +1,636 @@ |
|||||
|
|
||||
|
const moment = require('moment'); |
||||
|
//获取异常识别算法
|
||||
|
async function findAbnMethods(ctx, next) { |
||||
|
let rslt = null; |
||||
|
let error = { name: 'FindError', message: '异常识别算法获取失败' }; |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
let abnMethods = await models.AbnTypes.findAll(); |
||||
|
rslt = abnMethods.map(s => ({ |
||||
|
id: s.id, |
||||
|
name: s.name, |
||||
|
des: s.des |
||||
|
})); |
||||
|
error = null; |
||||
|
} catch (err) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`); |
||||
|
} |
||||
|
if (error) { |
||||
|
ctx.status = 400; |
||||
|
ctx.body = error; |
||||
|
} else { |
||||
|
ctx.status = 200; |
||||
|
ctx.body = rslt; |
||||
|
} |
||||
|
} |
||||
|
//获取异常参数配置
|
||||
|
async function findAbnParamList(ctx) { |
||||
|
const { sensorId } = ctx.query |
||||
|
const id=sensorId.split(',') |
||||
|
let rslt = null; |
||||
|
let error = { name: 'FindError', message: '异常参数配置获取失败' }; |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
let abnParamList = await models.AbnReportParams.findAll({ |
||||
|
where: { sensorId:{$in:id} } |
||||
|
}) |
||||
|
rslt = abnParamList.map(s => ({ |
||||
|
key: s.id, |
||||
|
id: s.id, |
||||
|
sensorId: s.sensorId, |
||||
|
sensorName: s.sensorLocationDescription, |
||||
|
abnType: s.abnTypeId, |
||||
|
enabled: s.enabled, |
||||
|
factorId: s.factorId, |
||||
|
factorName: s.factor, |
||||
|
params: s.params, |
||||
|
itemIndex:s.itemIndex |
||||
|
})); |
||||
|
error = null; |
||||
|
} catch (err) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`); |
||||
|
} |
||||
|
if (error) { |
||||
|
ctx.status = 400; |
||||
|
ctx.body = error; |
||||
|
} else { |
||||
|
ctx.status = 200; |
||||
|
ctx.body = rslt; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//新增异常参数配置
|
||||
|
async function createAbnParam(ctx) { |
||||
|
let error = { name: 'CreateError', message: '异常参数配置新增失败' }; |
||||
|
const models = ctx.fs.dc.models |
||||
|
const data = ctx.request.body |
||||
|
|
||||
|
try { |
||||
|
for (let i = 0; i < data.length; i++) { |
||||
|
let dataItem = data[i]; |
||||
|
if (dataItem && dataItem.params && dataItem.abnType && dataItem.enabled != null && dataItem.sensorId && dataItem.factorId) { |
||||
|
let dataToSave = { |
||||
|
sensorId: dataItem.sensorId, |
||||
|
sensorLocationDescription:dataItem.sensorName, |
||||
|
enabled: dataItem.enabled, |
||||
|
abnTypeId: dataItem.abnType, |
||||
|
factorId: dataItem.factorId, |
||||
|
factor: dataItem.factorName, |
||||
|
itemIndex: dataItem.itemId, |
||||
|
params: dataItem.params |
||||
|
}; |
||||
|
await models.AbnReportParams.create(dataToSave) |
||||
|
|
||||
|
error = null |
||||
|
// // 日志信息
|
||||
|
// ctx.fs.api = ctx.fs.api || {}
|
||||
|
// ctx.fs.api.actionParameter = JSON.stringify(data)
|
||||
|
// ctx.fs.api.actionParameterShow = `新增异常推送配置id:${newId}`
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (err) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`) |
||||
|
} |
||||
|
if (error) { |
||||
|
ctx.status = 400 |
||||
|
ctx.body = error |
||||
|
} else { |
||||
|
ctx.status = 204 |
||||
|
} |
||||
|
} |
||||
|
//
|
||||
|
async function batchSwitch(ctx, next) { |
||||
|
const ids = ctx.params.ids.split(',') |
||||
|
const data = ctx.request.body |
||||
|
let error = { name: 'UpdateError', message: data ? '批量启用异常参数配置失败' : '批量禁用异常参数配置失败' }; |
||||
|
try { |
||||
|
for (let i = 0; i < ids.length; i++) { |
||||
|
let id = ids[i]; |
||||
|
const models = ctx.fs.dc.models; |
||||
|
let abnParam = await models.AbnReportParams.findOne({ where: { id: id } }); |
||||
|
if (abnParam) { |
||||
|
let dataToSave = {}; |
||||
|
if (data.use == 'switch') { |
||||
|
dataToSave.enabled = data.enabled;//批量启用or禁用
|
||||
|
} else { |
||||
|
dataToSave.params = data.paramJson;//批量改参数
|
||||
|
} |
||||
|
if (Object.keys(dataToSave).length) { |
||||
|
await models.AbnReportParams.update(dataToSave, { where: { id } }); |
||||
|
} |
||||
|
error = null; |
||||
|
// // 日志信息
|
||||
|
// ctx.fs.api = ctx.fs.api || {};
|
||||
|
// ctx.fs.api.actionParameter = JSON.stringify(data);
|
||||
|
// ctx.fs.api.actionParameterShow = `异常参数配置id:${id}`;
|
||||
|
} else { |
||||
|
error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` }; |
||||
|
} |
||||
|
} |
||||
|
} catch (err) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`); |
||||
|
} |
||||
|
if (error) { |
||||
|
ctx.status = 400; |
||||
|
ctx.body = error; |
||||
|
} else { |
||||
|
ctx.status = 204; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//删除异常参数配置
|
||||
|
async function deleteAbnParam(ctx, next) { |
||||
|
let error = { name: 'DeleteError', message: '异常参数配置删除失败' }; |
||||
|
const ids = ctx.params.ids.split(','); |
||||
|
//const { id } = ctx.params;
|
||||
|
try { |
||||
|
for (let i = 0; i < ids.length; i++) { |
||||
|
let id = ids[i]; |
||||
|
const models = ctx.fs.dc.models; |
||||
|
let abnParam = await models.AbnReportParams.findOne({ where: { id } }); |
||||
|
if (abnParam) { |
||||
|
await models.AbnReportParams.destroy({ where: { id } }); |
||||
|
error = null; |
||||
|
} else { |
||||
|
error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` }; |
||||
|
} |
||||
|
} |
||||
|
} catch (err) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`); |
||||
|
} |
||||
|
if (error) { |
||||
|
ctx.status = 400; |
||||
|
ctx.body = error; |
||||
|
} else { |
||||
|
ctx.status = 204; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//修改异常推送配置
|
||||
|
async function updateAbnParam(ctx) { |
||||
|
let error = { name: 'UpdateError', message: '异常参数配置修改失败' } |
||||
|
const ids = ctx.params.ids.split(',') |
||||
|
const data = ctx.request.body |
||||
|
|
||||
|
if (data && Object.keys(data).length) { |
||||
|
try { |
||||
|
for (let i = 0; i < ids.length; i++) { |
||||
|
let id = ids[i]; |
||||
|
const models = ctx.fs.dc.models; |
||||
|
let abnParam = await models.AbnReportParams.findOne({ where: { id: id } }); |
||||
|
if (abnParam) { |
||||
|
let dataToSave = {}; |
||||
|
const { abnType, params, enabled } = data; |
||||
|
if (enabled != null && enabled != abnParam.enabled) |
||||
|
dataToSave.enabled = enabled; |
||||
|
//中断
|
||||
|
if (abnType == 1) { |
||||
|
if (params != null && params.thr_int !== abnParam.params.thr_int) { |
||||
|
dataToSave.params = params; |
||||
|
} |
||||
|
} |
||||
|
//毛刺
|
||||
|
if (abnType == 2) { |
||||
|
if (params != null && params.thr_burr !== abnParam.params.thr_burr) { |
||||
|
dataToSave.params = params; |
||||
|
} |
||||
|
} |
||||
|
//趋势
|
||||
|
if (abnType == 3) { |
||||
|
if (params != null && |
||||
|
(params.thr_burr !== abnParam.params.thr_burr || params.win_med !== abnParam.params.win_med |
||||
|
|| params.win_avg !== abnParam.params.win_avg || params.win_grad !== abnParam.params.win_grad |
||||
|
|| params.thr_grad !== abnParam.params.thr_grad || params.thr_der !== abnParam.params.thr_der |
||||
|
|| params.days_Last !== abnParam.params.days_Last)) { |
||||
|
dataToSave.params = params; |
||||
|
} |
||||
|
} |
||||
|
if (Object.keys(dataToSave).length) { |
||||
|
await models.AbnReportParams.update(dataToSave, { where: { id } }); |
||||
|
} |
||||
|
error = null; |
||||
|
} else { |
||||
|
error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` }; |
||||
|
} |
||||
|
} |
||||
|
} catch (err) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`); |
||||
|
} |
||||
|
} |
||||
|
if (error) { |
||||
|
ctx.status = 400; |
||||
|
ctx.body = error; |
||||
|
} else { |
||||
|
ctx.status = 204; |
||||
|
} |
||||
|
} |
||||
|
//异常数据对比
|
||||
|
async function getAbnTaskResult(ctx, next) { |
||||
|
let error = { name: 'TaskError', message: '异常数据对比失败' } |
||||
|
const models = ctx.fs.dc.models |
||||
|
const structId = ctx.params.id |
||||
|
const startTime = ctx.params.start |
||||
|
const endTime = ctx.params.end |
||||
|
const data = ctx.request.body |
||||
|
const stationId = data.station |
||||
|
let factorProto = await models.Factor.findOne({ |
||||
|
where: { id: data.factorId }, |
||||
|
attributes: ['id', 'proto'] |
||||
|
}); |
||||
|
let protoItems = await models.FactorProtoItem.findAll({ |
||||
|
where: { proto: factorProto.proto }, |
||||
|
attributes: ['id', 'name'] |
||||
|
}); |
||||
|
let itemName = await models.FactorProtoItem.findOne({ |
||||
|
where: { id: data.itemId ? data.itemId : protoItems[0].id }, |
||||
|
attributes: ['id', 'field_name', 'name'] |
||||
|
}); |
||||
|
try { |
||||
|
const itemsObj = await findThemeItems(models, data.factorId) |
||||
|
const filter = { |
||||
|
query: { |
||||
|
bool: { |
||||
|
must: [ |
||||
|
{ match: { "sensor": stationId } } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (startTime && endTime) { |
||||
|
filter.query.bool.must.push({ range: { "collect_time": { gte: moment(startTime).toISOString(), lte: moment(endTime).toISOString() } } }); |
||||
|
} |
||||
|
const esThemeData = await findThemeDataFromES(ctx.app.fs.esclient, filter) |
||||
|
const stationsData = esThemeData.reduce((p, c) => { |
||||
|
const { sensor, data, collect_time } = c._source; |
||||
|
p.unshift(Object.assign({}, data, { time: moment(collect_time).format('YYYY-MM-DD HH:mm:ss') })); |
||||
|
return p; |
||||
|
}, []); |
||||
|
//获取前一天的最后一条数据
|
||||
|
const preFilter = { |
||||
|
query: { |
||||
|
bool: { |
||||
|
must: [ |
||||
|
{ match: { "sensor": stationId } }, |
||||
|
{ range: { "collect_time": { gte: moment(startTime).add('days', -1).toISOString(), lte: moment(startTime).toISOString() } } } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
const esPreData = await findThemeDataFromES(ctx.app.fs.esclient, preFilter); |
||||
|
const preOneData = esPreData.reduce((p, c) => { |
||||
|
const { data, collect_time } = c._source; |
||||
|
p.unshift(Object.assign({}, data, { time: moment(collect_time).format('YYYY-MM-DD HH:mm:ss') })); |
||||
|
return p; |
||||
|
}, []); |
||||
|
let one = preOneData && preOneData.length > 0 ? preOneData[preOneData.length - 1] : null; |
||||
|
|
||||
|
let itemKey = itemName.get({ plain: true }).field_name;//监测项名称
|
||||
|
let itemn = itemName.get({ plain: true }).name; |
||||
|
let calcResult = calcAlgorithm(one, stationsData, data, itemKey);//计算
|
||||
|
ctx.status = 200; |
||||
|
ctx.body = { |
||||
|
unit: itemsObj[itemKey].unit, |
||||
|
method: data.abnType, |
||||
|
itemKey: itemKey, |
||||
|
itemName: itemn, |
||||
|
stationData: stationsData, |
||||
|
resultArray: calcResult |
||||
|
}; |
||||
|
} catch (err) { |
||||
|
ctx.fs.logger.error(err); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
name: "FindError", |
||||
|
message: "异常数据识别-数据对比失败" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
let calcAlgorithm = function (dataOne, dataSource, params, itemKey) { |
||||
|
let result; |
||||
|
switch (params.abnType) { |
||||
|
case "interrupt": |
||||
|
result = interrupt(dataOne, dataSource, params, itemKey); |
||||
|
break; |
||||
|
case "burr": |
||||
|
result = burr(dataOne, dataSource, params, itemKey).result; |
||||
|
break; |
||||
|
case "trend": |
||||
|
result = trend(null, dataSource, params, itemKey); |
||||
|
break; |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
//中断
|
||||
|
let interrupt = function (dataOne, dataSource, params, key) { |
||||
|
let result = []; |
||||
|
if (dataSource.length != 0) { |
||||
|
if (dataOne == null) { |
||||
|
result.push({ |
||||
|
type: "interrupt", |
||||
|
hour: 24.00, |
||||
|
time: dataSource[0].time, |
||||
|
value: dataSource[0][key] |
||||
|
});//第一个点中断
|
||||
|
} else { |
||||
|
dataSource.unshift(dataOne); |
||||
|
} |
||||
|
for (let i = 0; i < dataSource.length - 1; i++) { |
||||
|
if (dataSource[i] == null || dataSource[i + 1] == null) continue; |
||||
|
let hour = getHour(dataSource[i + 1].time, dataSource[i].time); |
||||
|
if (hour >= params.params.thr_int) { |
||||
|
result.push({ |
||||
|
type: "interrupt", |
||||
|
hour: hour, |
||||
|
time: dataSource[i + 1].time, |
||||
|
value: dataSource[i + 1][key] |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
//毛刺
|
||||
|
let burr = function (dataOne, dataSource, params, key) { |
||||
|
let burrTv = params.params.thr_burr; |
||||
|
let result = [];//毛刺点
|
||||
|
let dataSAfterBurr = [];//去掉毛刺点的数组
|
||||
|
if (dataSource.length != 0) { |
||||
|
if (dataOne != null) { |
||||
|
dataSource.unshift(dataOne); |
||||
|
} |
||||
|
for (let i = 1; i < dataSource.length - 1; i++) { |
||||
|
if (dataSource[i - 1] == null || dataSource[i] == null || dataSource[i + 1] == null) continue |
||||
|
let gap1 = dataSource[i][key] - dataSource[i - 1][key] |
||||
|
let gap2 = dataSource[i][key] - dataSource[i + 1][key] |
||||
|
|
||||
|
let gap3 = dataSource[i - 1][key] - dataSource[i][key] |
||||
|
let gap4 = dataSource[i + 1][key] - dataSource[i][key] |
||||
|
|
||||
|
let result1 = (gap1 > burrTv && gap2 > burrTv) |
||||
|
let result2 = (gap3 > burrTv && gap4 > burrTv) |
||||
|
|
||||
|
if (i == 1) {//第一个点
|
||||
|
dataSAfterBurr.push(dataSource[0]) |
||||
|
} |
||||
|
if (result1 || result2) { |
||||
|
result.push({ |
||||
|
type: "burr", |
||||
|
burr: result1 ? Math.min(gap1, gap2) : Math.min(gap3, gap4), |
||||
|
time: dataSource[i].time, |
||||
|
value: dataSource[i][key] |
||||
|
}) |
||||
|
} else { |
||||
|
dataSAfterBurr.push(dataSource[i]) |
||||
|
} |
||||
|
if (i == dataSource.length - 2) {//最后一个点
|
||||
|
dataSAfterBurr.push(dataSource[dataSource.length - 1]) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return { result: result, dataSAfterBurr: dataSAfterBurr } |
||||
|
} |
||||
|
//异常趋势
|
||||
|
let trend = function (dataOne, dataSource, params, key) { |
||||
|
let result; |
||||
|
if (dataSource.length != 0) { |
||||
|
//去完毛刺的新数组
|
||||
|
let afterBurr = burr(dataOne, dataSource, params, key).dataSAfterBurr; |
||||
|
//滑动中值
|
||||
|
let arrAfterMedian = []; |
||||
|
for (let i = 0; i < afterBurr.length; i += parseInt(params.params.win_med)) { |
||||
|
let arr = afterBurr.slice(i, i + parseInt(params.params.win_med)) |
||||
|
let oneMedian = calcMedian(arr, key) |
||||
|
arrAfterMedian.push(oneMedian) |
||||
|
} |
||||
|
//滑动均值
|
||||
|
let arrAfterAvg = calcMeanValue(arrAfterMedian, params.params.win_avg, key) |
||||
|
//错位相减,相当于求导
|
||||
|
let arrAfterDe = [] |
||||
|
for (let j = 0; j < arrAfterAvg.length - 1; j++) { |
||||
|
let one = { |
||||
|
value: arrAfterAvg[j + 1].value - arrAfterAvg[j].value, |
||||
|
time: arrAfterAvg[j + 1].time |
||||
|
} |
||||
|
arrAfterDe.push(one); |
||||
|
} |
||||
|
//最后判断
|
||||
|
let finalArray = finalJudge(arrAfterDe, arrAfterMedian, params) |
||||
|
result = { |
||||
|
calcFinal: finalArray, |
||||
|
calcPreprocess: arrAfterAvg//要画预处理+滑动均值完了的曲线
|
||||
|
}; |
||||
|
} |
||||
|
return result |
||||
|
} |
||||
|
let getHour = function (s1, s2) { |
||||
|
s1 = new Date(s1.replace(/-/g, '/')) |
||||
|
s2 = new Date(s2.replace(/-/g, '/')) |
||||
|
let ms = Math.abs(s1.getTime() - s2.getTime()) |
||||
|
return ms / 1000 / 60 / 60; |
||||
|
} |
||||
|
//计算一组数据的中值
|
||||
|
let calcMedian = function (array, key) { |
||||
|
let result; |
||||
|
if (array != null || array.length > 0) { |
||||
|
array.sort((a, b) => { return a[key] - b[key]}) |
||||
|
if (array.length % 2 == 0) {//偶数
|
||||
|
let index1 = array.length / 2; |
||||
|
result = { |
||||
|
value: (array[index1][key] + array[index1 - 1][key]) / 2, |
||||
|
time: array[index1].time |
||||
|
} |
||||
|
|
||||
|
} else {//奇数
|
||||
|
let index = (array.length - 1) / 2 |
||||
|
result = { |
||||
|
value: array[index][key], |
||||
|
time: array[index].time |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
//计算一组数据的均值
|
||||
|
let calcMeanValue = function (array, coef, key) { |
||||
|
let result = []; |
||||
|
let sum = 0; |
||||
|
if (array != null || array.length > 0) { |
||||
|
for (let i = 0; i < array.length; i++) { |
||||
|
let value; |
||||
|
if (i < parseInt(coef)) { |
||||
|
sum = sum + array[i].value |
||||
|
value = sum / (i + 1) |
||||
|
} else { |
||||
|
let arr = array.slice(i - parseInt(coef) + 1, i + 1) |
||||
|
let ssum = 0; |
||||
|
for (let s = 0; s < arr.length; s++) { |
||||
|
ssum = ssum + arr[s].value |
||||
|
} |
||||
|
value = ssum / parseInt(coef) |
||||
|
} |
||||
|
let one = { |
||||
|
value: value, |
||||
|
time: array[i].time |
||||
|
} |
||||
|
result.push(one) |
||||
|
} |
||||
|
} |
||||
|
return result |
||||
|
} |
||||
|
|
||||
|
let finalJudge = function (array, original, params) { |
||||
|
let ups = 1, downs = 1; |
||||
|
let tempUp = [], tempDown = []; |
||||
|
let point = params.params.win_grad;//渐变点个数
|
||||
|
let deTv = params.params.thr_der;//导数阈值
|
||||
|
let greTv = params.params.thr_grad;//渐变阈值
|
||||
|
let finalArray = []; |
||||
|
for (let i = 0; i < array.length; i++)//对最新数组作阈值判断
|
||||
|
{ |
||||
|
if (array[i].value > deTv) { |
||||
|
ups = ups + 1 |
||||
|
if (ups == 2) { |
||||
|
tempUp.push(original[i]) |
||||
|
} |
||||
|
tempUp.push(original[i + 1]) |
||||
|
if (tempDown.length >= point) { |
||||
|
let bbb = tempDown[tempDown.length - 1].value - tempDown[0].Value |
||||
|
if (downs >= point && bbb < -greTv) { |
||||
|
let one = { |
||||
|
startTime: tempDown[0].time, |
||||
|
endTime: tempDown[tempDown.length - 1].time, |
||||
|
startValue: tempDown[0].value, |
||||
|
endValue: tempDown[tempDown.length - 1].value, |
||||
|
value: bbb, |
||||
|
des: "异常下降" |
||||
|
}; |
||||
|
finalArray.push(one) |
||||
|
} |
||||
|
} |
||||
|
downs = 1 |
||||
|
tempDown = [] |
||||
|
} else if (array[i].value < -deTv) { |
||||
|
downs = downs + 1; |
||||
|
if (downs == 2) { |
||||
|
tempDown.push(original[i]) |
||||
|
} |
||||
|
tempDown.push(original[i + 1]) |
||||
|
if (tempUp.length >= point) { |
||||
|
let aaa = tempUp[tempUp.length - 1].value - tempUp[0].value |
||||
|
if (ups >= point && aaa > greTv) { |
||||
|
let one = { |
||||
|
startTime: tempUp[0].time, |
||||
|
endTime: tempUp[tempUp.length - 1].time, |
||||
|
startValue: tempUp[0].value, |
||||
|
endValue: tempUp[tempUp.length - 1].value, |
||||
|
value: aaa, |
||||
|
des: "异常上升" |
||||
|
}; |
||||
|
finalArray.push(one) |
||||
|
} |
||||
|
} |
||||
|
ups = 1; |
||||
|
tempUp = [] |
||||
|
} |
||||
|
} |
||||
|
if (ups >= point) { |
||||
|
let ccc = tempUp[tempUp.length - 1].value - tempUp[0].value |
||||
|
if (ccc > greTv) { |
||||
|
let one = { |
||||
|
startTime: tempUp[0].time, |
||||
|
endTime: tempUp[tempUp.length - 1].time, |
||||
|
startValue: tempUp[0].value, |
||||
|
endValue: tempUp[tempUp.length - 1].value, |
||||
|
value: ccc, |
||||
|
des: "异常上升" |
||||
|
}; |
||||
|
finalArray.push(one) |
||||
|
} |
||||
|
} |
||||
|
if (downs >= point) { |
||||
|
let ddd = tempDown[tempDown.length - 1].value - tempDown[0].value |
||||
|
if (ddd < -greTv) { |
||||
|
let one = { |
||||
|
startTime: tempDown[0].time, |
||||
|
endTime: tempDown[tempDown.length - 1].time, |
||||
|
startValue: tempDown[0].value, |
||||
|
endValue: tempDown[tempDown.length - 1].value, |
||||
|
value: ddd, |
||||
|
des: "异常下降" |
||||
|
}; |
||||
|
finalArray.push(one); |
||||
|
} |
||||
|
} |
||||
|
return finalArray |
||||
|
} |
||||
|
async function findThemeItems(models, factor) { |
||||
|
try { |
||||
|
let factorProto = await models.Factor.findOne({ |
||||
|
where: { id: factor }, |
||||
|
attributes: ['id', 'proto'] |
||||
|
}); |
||||
|
let protoItems = await models.FactorProtoItem.findAll({ |
||||
|
where: { proto: factorProto.proto }, |
||||
|
attributes: ['name', 'fieldName'], |
||||
|
include: [{ |
||||
|
model: models.ItemUnit, |
||||
|
where: { default: true }, |
||||
|
required: true, |
||||
|
attributes: ['name'] |
||||
|
}] |
||||
|
}); |
||||
|
let itemsObj = protoItems.reduce((p, c) => { |
||||
|
p[c.fieldName] = { |
||||
|
name: c.name, |
||||
|
unit: c.itemUnits[0] ? c.itemUnits[0].name : null |
||||
|
} |
||||
|
return p |
||||
|
}, {}) |
||||
|
return itemsObj |
||||
|
} catch (err) { |
||||
|
throw err |
||||
|
} |
||||
|
}; |
||||
|
async function findThemeDataFromES(esclient, filter, limit, _source) { |
||||
|
try { |
||||
|
let rslt = [] |
||||
|
const client = esclient[THEME_DATA] |
||||
|
let params = { |
||||
|
index: client.config.index, |
||||
|
type: client.config.type, |
||||
|
body: filter |
||||
|
} |
||||
|
|
||||
|
params.size = limit |
||||
|
if (limit == null) { |
||||
|
const countRes = await client.count(params) |
||||
|
params.size = countRes.count > 10000 ? 10000 : countRes.count |
||||
|
} |
||||
|
|
||||
|
params._source = _source || ["sensor", "collect_time", "data"] |
||||
|
params.body.sort = { "collect_time": { "order": "desc" } } |
||||
|
let res = await client.search(params) |
||||
|
rslt = res.hits.hits |
||||
|
return rslt |
||||
|
} catch (err) { |
||||
|
throw err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
module.exports = { |
||||
|
findAbnMethods, |
||||
|
findAbnParamList, |
||||
|
createAbnParam, |
||||
|
updateAbnParam, |
||||
|
batchSwitch, |
||||
|
deleteAbnParam, |
||||
|
getAbnTaskResult |
||||
|
}; |
@ -0,0 +1,192 @@ |
|||||
|
|
||||
|
const moment = require('moment'); |
||||
|
|
||||
|
async function getStructures (ctx) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
const { clickHouse } = ctx.app.fs |
||||
|
const { pomsProjectId } = ctx.query |
||||
|
let bindRes=[] |
||||
|
//选择全局就是查询所有项目下的结构物,有选择项目就只查询对应项目的结构物
|
||||
|
if(pomsProjectId){ |
||||
|
bindRes = await models.ProjectCorrelation.findAll({where:{id:{ $in: pomsProjectId.split(',') }}}) |
||||
|
}else{ |
||||
|
bindRes = await models.ProjectCorrelation.findAll() |
||||
|
} |
||||
|
let anxinProjectIds = new Set() |
||||
|
for (let b of bindRes) { |
||||
|
if (b.anxinProjectId.length) { |
||||
|
for (let aid of b.anxinProjectId) { |
||||
|
anxinProjectIds.add(aid) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
let undelStrucRes=[] |
||||
|
// if (bindRes) {
|
||||
|
// undelStrucRes = anxinProjectIds.size ?
|
||||
|
// await clickHouse.anxinyun.query(
|
||||
|
// `
|
||||
|
// SELECT
|
||||
|
// t_structure.id AS strucId,
|
||||
|
// t_structure.name AS strucName
|
||||
|
|
||||
|
// FROM
|
||||
|
// t_project
|
||||
|
// LEFT JOIN
|
||||
|
// t_project_structure
|
||||
|
// ON t_project_structure.project = t_project.id
|
||||
|
// LEFT JOIN
|
||||
|
// t_project_structuregroup
|
||||
|
// ON t_project_structuregroup.project = t_project.id
|
||||
|
// LEFT JOIN
|
||||
|
// t_structuregroup_structure
|
||||
|
// ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup
|
||||
|
// LEFT JOIN
|
||||
|
// t_project_construction
|
||||
|
// ON t_project_construction.project = t_project.id
|
||||
|
// LEFT JOIN
|
||||
|
// t_structure_site
|
||||
|
// ON t_structure_site.siteid = t_project_construction.construction
|
||||
|
// RIGHT JOIN
|
||||
|
// t_structure
|
||||
|
// ON t_structure.id = t_project_structure.structure
|
||||
|
// OR t_structure.id = t_structuregroup_structure.structure
|
||||
|
// OR t_structure.id = t_structure_site.structid
|
||||
|
|
||||
|
// WHERE
|
||||
|
// project_state != -1
|
||||
|
// AND
|
||||
|
// t_project.id IN (${[...anxinProjectIds].join(',')}, -1)
|
||||
|
// ORDER BY strucId
|
||||
|
// `
|
||||
|
// ).toPromise() :
|
||||
|
// []
|
||||
|
// }
|
||||
|
undelStrucRes.push({strucId:4036,strucName:'象山港大桥'}) |
||||
|
undelStrucRes.push({strucId:1,strucName:'象山港大'}) |
||||
|
ctx.status = 200; |
||||
|
ctx.body = undelStrucRes |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: error`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function getFactors (ctx) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
const { clickHouse } = ctx.app.fs |
||||
|
const { structId,cacl } = ctx.query |
||||
|
let list=[] |
||||
|
if(cacl){ |
||||
|
const factorList=await clickHouse.alarmLocal.query(`select distinct SafetyFactorTypeId, SafetyFactorTypeName from sensors where PlatformStructureId =${structId}`).toPromise() |
||||
|
let fList=factorList.map(m=>m.SafetyFactorTypeId) |
||||
|
list=await clickHouse.alarmLocal.query(`select distinct FactorID, Name,Items,ItemNames from factors where FactorID in (${[...fList,-1].join(',')})`).toPromise() |
||||
|
}else{ |
||||
|
list=await clickHouse.alarmLocal.query(`select distinct SafetyFactorTypeId, SafetyFactorTypeName from sensors where PlatformStructureId =${structId}`).toPromise() |
||||
|
} |
||||
|
ctx.body=list |
||||
|
ctx.status=200 |
||||
|
}catch(error){ |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: error`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//查询设备
|
||||
|
async function getSensors (ctx) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
const { clickHouse } = ctx.app.fs |
||||
|
const { structId,SafetyFactorTypeId } = ctx.query |
||||
|
const list=await clickHouse.alarmLocal.query(` |
||||
|
select distinct SensorId,SensorLocationDescription,Project |
||||
|
from sensors where PlatformStructureId =${structId} |
||||
|
and SafetyFactorTypeId=${SafetyFactorTypeId}`).toPromise()
|
||||
|
ctx.body=list |
||||
|
ctx.status=200 |
||||
|
}catch(error){ |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: error`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//根据设备id和监测因素id查询监测数据
|
||||
|
async function getMonitorData (ctx) { |
||||
|
try{ |
||||
|
const { clickHouse } = ctx.app.fs |
||||
|
const { factorId,sensorId,startTime, endTime,limit,page } = ctx.query |
||||
|
const offset=page*limit |
||||
|
const factorsList=await clickHouse.alarmLocal.query(`SELECT FactorID,Items,ItemNames,ItemUnits FROM factors
|
||||
|
WHERE FactorID=${factorId} |
||||
|
`).toPromise()||[]
|
||||
|
const monitorData=await clickHouse.alarmLocal.query(`SELECT SensorId,CollectTime,Values FROM themes
|
||||
|
WHERE SensorId in ('${sensorId}') |
||||
|
AND CollectTime >= toDate('${startTime}') |
||||
|
AND CollectTime <= toDate('${endTime}') |
||||
|
LIMIT ${limit} OFFSET ${offset} |
||||
|
`).toPromise()||[]
|
||||
|
console.log('vmonitorDatamonitorDatamonitorData',monitorData) |
||||
|
const id=sensorId&&sensorId.replace('-',':') |
||||
|
const sensor=await clickHouse.alarmLocal.query(`SELECT distinct SensorId,SensorLocationDescription FROM sensors WHERE ID='${id}'`).toPromise() |
||||
|
//监测项
|
||||
|
let items={} |
||||
|
if(factorsList&&factorsList.length>0){ |
||||
|
//因素解释
|
||||
|
let factors=[] |
||||
|
//因素名词
|
||||
|
let factorNames=[] |
||||
|
factorsList.map(item=>{ |
||||
|
factors=item.ItemNames.split(',') |
||||
|
factorNames=item.Items.split(',') |
||||
|
factors.map(child=>{ |
||||
|
factorNames.map(p=>{ |
||||
|
items[p]={name:child,unit:item.ItemUnits} |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
//设备数据+数据
|
||||
|
let sensors=[] |
||||
|
if(monitorData&&monitorData.length>0){ |
||||
|
monitorData.map(data => { |
||||
|
const values = {}; |
||||
|
Object.keys(items).forEach(key => { |
||||
|
const index = Object.keys(items).indexOf(key); |
||||
|
values[key] = data.Values[index]; |
||||
|
}); |
||||
|
sensors.push({ values, time: moment(data.CollectTime).format('YYYY-MM-DD HH:mm:ss') }); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
if(sensor&&sensor.length){ |
||||
|
ctx.body={items,sensors:[{data:sensors,id:sensor[0].SensorId,name:sensor[0].SensorLocationDescription}]} |
||||
|
ctx.status=200 |
||||
|
}else{ |
||||
|
ctx.body={items,sensors:[]} |
||||
|
ctx.status=200 |
||||
|
} |
||||
|
}catch(error){ |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: error`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
module.exports = { |
||||
|
getStructures,getFactors,getSensors,getMonitorData |
||||
|
} |
@ -0,0 +1,101 @@ |
|||||
|
/* eslint-disable*/ |
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = dc => { |
||||
|
const DataTypes = dc.ORM; |
||||
|
const sequelize = dc.orm; |
||||
|
const AbnReportParams = sequelize.define("abnReportParams", { |
||||
|
id: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: true, |
||||
|
field: "id", |
||||
|
autoIncrement: true |
||||
|
}, |
||||
|
sensorId: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "sensor_id", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
sensorLocationDescription: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "sensor_location_description", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
factor: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "factor", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
factorId: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "factor_id", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
abnTypeId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "abn_type", |
||||
|
autoIncrement: false, |
||||
|
references: { |
||||
|
key: "id", |
||||
|
model: "abnType" |
||||
|
} |
||||
|
}, |
||||
|
params: { |
||||
|
type: DataTypes.JSONB, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "params", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
enabled: { |
||||
|
type: DataTypes.BOOLEAN, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "enabled", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
itemIndex: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "item_index", |
||||
|
autoIncrement: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: "abn_report_params", |
||||
|
comment: "", |
||||
|
indexes: [] |
||||
|
}); |
||||
|
dc.models.AbnReportParams = AbnReportParams; |
||||
|
return AbnReportParams; |
||||
|
}; |
@ -0,0 +1,31 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = (dc) => { |
||||
|
const AbnTypes = dc.orm.define('abnTypes', |
||||
|
{ |
||||
|
id: { |
||||
|
field: 'id', |
||||
|
type: dc.ORM.INTEGER, |
||||
|
primaryKey: true, |
||||
|
unique: true, |
||||
|
allowNull: false, |
||||
|
autoIncrement: true |
||||
|
}, |
||||
|
name: { |
||||
|
field: 'name', |
||||
|
type: dc.ORM.STRING, |
||||
|
allowNull: false |
||||
|
}, |
||||
|
des: { |
||||
|
field: 'description', |
||||
|
type: dc.ORM.STRING, |
||||
|
allowNull: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: 'abn_type' |
||||
|
}); |
||||
|
|
||||
|
dc.models.AbnTypes = AbnTypes; |
||||
|
|
||||
|
return AbnTypes; |
||||
|
}; |
@ -0,0 +1,28 @@ |
|||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
const dataCacl = require('../../controllers/dataCacl'); |
||||
|
|
||||
|
module.exports = function (app, router, opts) { |
||||
|
app.fs.api.logAttr['GET/abnormal/methods'] = { content: '获取异常识别算法', visible: true } |
||||
|
router.get('/abnormal/methods', dataCacl.findAbnMethods) |
||||
|
|
||||
|
app.fs.api.logAttr['GET/struct/abnormal/params'] = { content: '获取结构物下异常识别参数配置列表', visible: false } |
||||
|
router.get('/struct/abnormal/params', dataCacl.findAbnParamList) |
||||
|
|
||||
|
app.fs.api.logAttr['POST/struct/abnormal/params'] = { content: '新增异常识别参数配置', visible: true } |
||||
|
router.post('/struct/abnormal/params', dataCacl.createAbnParam) |
||||
|
|
||||
|
app.fs.api.logAttr['PUT/batch/switch/:ids'] = { content: '异常参数批量配置', visible: true } |
||||
|
router.put('/batch/switch/:ids', dataCacl.batchSwitch) |
||||
|
|
||||
|
app.fs.api.logAttr['DELETE/delete/abnormal/params/:ids'] = { content: '删除异常识别参数配置', visible: true } |
||||
|
router.del('delete/abnormal/params/:ids', dataCacl.deleteAbnParam) |
||||
|
|
||||
|
app.fs.api.logAttr['PUT/edit/abnormal/params/:ids'] = { content: '修改异常识别参数配置', visible: true } |
||||
|
router.put('/edit/abnormal/params/:ids', dataCacl.updateAbnParam) |
||||
|
|
||||
|
app.fs.api.logAttr['POST/struct/:id/abnTask/result/:start/:end'] = { content: '获取异常数据识别任务结果', visible: true } |
||||
|
router.post('/struct/:id/abnTask/result/:start/:end', dataCacl.getAbnTaskResult) |
||||
|
|
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
const monitor = require('../../controllers/monitor'); |
||||
|
|
||||
|
module.exports = function (app, router, opts) { |
||||
|
|
||||
|
app.fs.api.logAttr['GET/project/allStructures'] = { content: '获取对应项目的结构物', visible: true }; |
||||
|
router.get('/project/allStructures', monitor.getStructures); |
||||
|
|
||||
|
|
||||
|
app.fs.api.logAttr['GET/structure/factors'] = { content: '获取对应的监测因素', visible: true }; |
||||
|
router.get('structure/factors', monitor.getFactors); |
||||
|
|
||||
|
app.fs.api.logAttr['GET/structure/factors/sensors'] = { content: '获取对应的点位', visible: true }; |
||||
|
router.get('structure/factors/sensors', monitor.getSensors); |
||||
|
|
||||
|
app.fs.api.logAttr['GET/structure/factors/sensors/data'] = { content: '获取对应点位的设备', visible: true }; |
||||
|
router.get('structure/factors/sensors/data', monitor.getMonitorData); |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
create table abn_type |
||||
|
( |
||||
|
id serial |
||||
|
primary key, |
||||
|
name varchar(50) not null, |
||||
|
description varchar(50) not null |
||||
|
); |
||||
|
INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'interrupt'::varchar(50), '数据中断'::varchar(50)); |
||||
|
INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'burr'::varchar(50), '毛刺'::varchar(50)); |
||||
|
INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'trend'::varchar(50), '趋势'::varchar(50)) |
||||
|
|
||||
|
|
@ -0,0 +1,15 @@ |
|||||
|
create table abn_report_params |
||||
|
( |
||||
|
id serial |
||||
|
primary key, |
||||
|
sensor_id varchar(300) not null, |
||||
|
sensor_location_description varchar(300) not null, |
||||
|
factor varchar(30) not null, |
||||
|
factor_id varchar(30) not null, |
||||
|
abn_type integer |
||||
|
constraint t_abn_report_params_t_abn_type_id_fk |
||||
|
references abn_type, |
||||
|
params jsonb not null, |
||||
|
enabled boolean default true not null, |
||||
|
item_index integer not null |
||||
|
); |
@ -1,7 +1,7 @@ |
|||||
'use strict'; |
'use strict'; |
||||
|
|
||||
import * as dataQuery from './dataQuery' |
import * as dataQuery from './dataQuery' |
||||
|
import * as monitor from './monitor' |
||||
export default { |
export default { |
||||
...dataQuery |
...dataQuery,...monitor |
||||
} |
} |
@ -0,0 +1,54 @@ |
|||||
|
/* |
||||
|
* @Author: zhaobing |
||||
|
* @Date: 2023-11-02 11:31:56 |
||||
|
*/ |
||||
|
import { ApiTable, basicAction } from '$utils' |
||||
|
|
||||
|
export function getProjectAllStructures (query) {//获取对应项目下所有结构物
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_PROJECT_ALL_STRUCTURES", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getProjectAllStructures}`, |
||||
|
msg: { option: "获取对应项目下的所有结构物" }, |
||||
|
reducer: { name: "ProjectAllStructures", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export function getFactors (query) {//获取对应结构物下的检测因素
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_FACTORS", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getFactors}`, |
||||
|
msg: { option: "获取对应结构物下的检测因素" }, |
||||
|
reducer: { name: "Factors", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function getSensors (query) {//获取对应检测因素下的设备
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_SENSORS", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getSensors}`, |
||||
|
msg: { option: "获取对应检测因素下的设备" }, |
||||
|
reducer: { name: "Sensors", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function getMonitorData (query) {//获取对应检测因素下的设备的数据(最终的数据)
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_MONITOR_DATA", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getMonitorData}`, |
||||
|
msg: { option: "获取对应检测因素下的设备的数据(最终的数据)" }, |
||||
|
reducer: { name: "SensorsData", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
@ -0,0 +1,359 @@ |
|||||
|
|
||||
|
'use strict'; |
||||
|
import React, { Component } from 'react'; |
||||
|
// 引入 echarts 主模块。 |
||||
|
import * as echarts from 'echarts/lib/echarts'; |
||||
|
// 引入折线图 |
||||
|
import 'echarts/lib/chart/line'; |
||||
|
//引入标签组件 |
||||
|
import ReactEcharts from 'echarts-for-react'; |
||||
|
|
||||
|
let chartDownload = ""; |
||||
|
class LineTimeChart extends Component { |
||||
|
constructor(props) { |
||||
|
super(props); |
||||
|
this.sliderId = 'sliderId' + Math.floor(Math.random() * 1000000000); |
||||
|
this.chartId = 'chartId' + Math.floor(Math.random() * 1000000000); |
||||
|
this.legendId = 'legendId' + Math.floor(Math.random() * 1000000000); |
||||
|
this.legendListId = 'legendListId' + Math.floor(Math.random() * 1000000000); |
||||
|
this.tooltipId = 'tooltipId' + Math.floor(Math.random() * 1000000000); |
||||
|
this.state = { |
||||
|
option: {}, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
componentDidMount() { |
||||
|
let that = this; |
||||
|
this.renderChart(); |
||||
|
this.reSetLegendStyle(that) |
||||
|
window.addEventListener('resize', function () { |
||||
|
let a = setInterval(function () { that.reSetLegendStyle(that) }, 100); |
||||
|
setTimeout(function () { clearInterval(a) }, 3500) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
reSetLegendStyle = (that) => { |
||||
|
// Ps.initialize(document.getElementById(that.legendId)); |
||||
|
// Ps.initialize(document.getElementById(that.tooltipId)); |
||||
|
// let legendListDom = document.getElementById(that.legendListId) |
||||
|
// legendListDom.style.textAlign = 'left'; |
||||
|
// legendListDom.style.maxWidth = '115px' |
||||
|
// document.getElementById(that.legendId).style.left = '10px' |
||||
|
} |
||||
|
|
||||
|
download = () => { |
||||
|
this.chartDownload.downloadImage(); |
||||
|
} |
||||
|
|
||||
|
renderChart = () => { |
||||
|
const { options } = this.props; |
||||
|
const props = this.props; |
||||
|
let height = props.height || 300; |
||||
|
let showSlider = options && options.sliderStart; |
||||
|
let padding = [20, 135, 30, 140] |
||||
|
let startTime, endTime, ds, dv, view; |
||||
|
|
||||
|
// let chart = new G2.Chart({ |
||||
|
// container: this.chartId, |
||||
|
// forceFit: true, |
||||
|
// height: height, |
||||
|
// padding: padding, |
||||
|
// }); |
||||
|
// chart.source(props.data); |
||||
|
// if (showSlider) { |
||||
|
// startTime = options.sliderStart; |
||||
|
// endTime = options.sliderEnd; |
||||
|
// ds = new DataSet({ |
||||
|
// state: { |
||||
|
// start: startTime, |
||||
|
// end: endTime, |
||||
|
// } |
||||
|
// }); |
||||
|
// dv = ds.createView(); |
||||
|
// dv.source(props.data) |
||||
|
// .transform({ // !!! 根据状态量设置数据过滤规则, |
||||
|
// type: 'filter', |
||||
|
// callback: obj => { |
||||
|
// return obj.time <= ds.state.end && obj.time >= ds.state.start; |
||||
|
// } |
||||
|
// }); |
||||
|
// } |
||||
|
// chart.scale({ |
||||
|
// 'time': { |
||||
|
// range: [0, 1], |
||||
|
// type: 'time', |
||||
|
// alias: '时间', |
||||
|
// mask: 'YYYY-MM-DD HH:mm:ss', |
||||
|
// }, |
||||
|
// }); |
||||
|
// chart.on('tooltip:change', function (ev) { |
||||
|
// let item = ev.items[0]; |
||||
|
// item.title = item.title; |
||||
|
// }); |
||||
|
// chart.axis('value', { |
||||
|
// position: 'right' |
||||
|
// }); |
||||
|
// chart.tooltip(true, { |
||||
|
// enterable: true, |
||||
|
// offset: 3, |
||||
|
// crosshairs: { |
||||
|
// type: 'cross', |
||||
|
// }, |
||||
|
// inPlot: false, |
||||
|
// containerTpl: ` |
||||
|
// <div class="g2-tooltip"> |
||||
|
// <div id="${this.tooltipId}" style="max-height:${height - 30}px;min-width:130px;overflow:auto;position:relative;padding-right:12px"> |
||||
|
// <div class="g2-tooltip-title" style="margin-bottom: 4px;"></div> |
||||
|
// <ul class="g2-tooltip-list"></ul> |
||||
|
// </div> |
||||
|
// </div>`, |
||||
|
// }); |
||||
|
// chart.legend(true, { |
||||
|
// marker: 'circle', |
||||
|
// useHtml: true, |
||||
|
// position: 'left', |
||||
|
// containerTpl: `<div class="g2-legend" id="${this.legendId}" style="word-wrap:break-word;position:absolute;top:20px;left:0"> |
||||
|
// <h4 class="g2-legend-title"></h4> |
||||
|
// <ul class="g2-legend-list" id="${this.legendListId}" style="word-wrap:break-word;text-align: left;height:${height - 50}px;width:${130}px"></ul> |
||||
|
// </div>`, |
||||
|
// }) |
||||
|
// let guideOption = { |
||||
|
// position: ['min', 'max'], |
||||
|
// content: options.yAlias, |
||||
|
// offsetY: -10 |
||||
|
// } |
||||
|
// if (showSlider) { |
||||
|
// view = chart.view(); |
||||
|
// view.source(dv); // !!! 注意数据源是 ds 创建 DataView 对象 |
||||
|
// view.guide().text(guideOption); |
||||
|
// view.line().position('time*value').color('name').shape('line').size(2); |
||||
|
// } else { |
||||
|
// chart.guide().text(guideOption); |
||||
|
// chart.line().position('time*value').color('name').shape('line').size(2); |
||||
|
// } |
||||
|
// chart.render(); |
||||
|
// if (showSlider) { |
||||
|
// // 创建滑动条 |
||||
|
// let slider = new Slider({ |
||||
|
// container: this.sliderId, |
||||
|
// padding: [35, 135, 30, 140], |
||||
|
// xAxis: 'time', |
||||
|
// yAxis: 'value', |
||||
|
// start: startTime, |
||||
|
// end: endTime, |
||||
|
// data: props.data, |
||||
|
// scales: { |
||||
|
// 'time': { |
||||
|
// type: 'time', |
||||
|
// mask: "YYYY-MM-DD HH:mm:ss" |
||||
|
// } |
||||
|
// }, |
||||
|
// onChange: ({ startValue, endValue, startText, endText }) => { |
||||
|
// ds.setState('start', startText); |
||||
|
// ds.setState('end', endText); |
||||
|
// } // 更新数据状态量的回调函数 |
||||
|
// }); |
||||
|
// slider.render(); |
||||
|
// } |
||||
|
// chart.changeData(props.data); |
||||
|
|
||||
|
//组织dataset数据 |
||||
|
// let data = this.props.data; |
||||
|
// let dataset = [], name = [], x = ['product']; |
||||
|
// data.map(d => { |
||||
|
// x.push(d.time); |
||||
|
// let value = []; |
||||
|
// if (!name.includes(d.name)) { |
||||
|
// name.push(d.name); |
||||
|
// value.push(d.name); |
||||
|
// value.push(d.value); |
||||
|
// dataset.push(value); |
||||
|
// } else { |
||||
|
// dataset.map(set => { |
||||
|
// if (set[0] == d.name) { |
||||
|
// set.push(d.value); |
||||
|
// } |
||||
|
// }) |
||||
|
// } |
||||
|
|
||||
|
// }); |
||||
|
// dataset.unshift(x); |
||||
|
//组织后端数据组织data |
||||
|
let data = this.props.data; |
||||
|
let dataArray = [], types = {}; |
||||
|
let xAxisValue = props.xAxis || 'time'; |
||||
|
let yAxisValue = props.yAxis || 'value'; |
||||
|
data.map(dd => { |
||||
|
if (!types[dd.name]) { |
||||
|
types[dd.name] = Object.assign({ |
||||
|
name: dd.name, data: [] |
||||
|
}, { |
||||
|
type: 'line', |
||||
|
//smooth: true |
||||
|
}); |
||||
|
} |
||||
|
types[dd.name].data.push({ |
||||
|
name: dd.time, |
||||
|
value: [dd[xAxisValue], dd[yAxisValue]] |
||||
|
}) |
||||
|
|
||||
|
}); |
||||
|
for (let t in types) { |
||||
|
dataArray.push(types[t]); |
||||
|
} |
||||
|
// //根据dataset数据数量给出足够的线空间 |
||||
|
// let line, lines = []; |
||||
|
// line = { type: 'line', smooth: true, seriesLayoutBy: 'row' } |
||||
|
// dataset.map(data => { |
||||
|
// lines.push(line); |
||||
|
// }); |
||||
|
|
||||
|
let eOption = {} |
||||
|
//解决只有一条数据时图例和线颜色不同 |
||||
|
// if (dataset.length == 2) { |
||||
|
// eOption.color = ['#000000'] |
||||
|
// } |
||||
|
//鼠标移入提示 |
||||
|
eOption.tooltip = { |
||||
|
// axisPointer:{ |
||||
|
// axis:{} |
||||
|
// } |
||||
|
trigger: 'axis', |
||||
|
//格式化 tooltip |
||||
|
formatter: function (params, ticket, callback) { |
||||
|
let htmlStr = ""; |
||||
|
for (let i = 0; i < params.length; i++) { |
||||
|
let param = params[i]; |
||||
|
let xName = param.name;//x轴的名称 |
||||
|
let seriesName = param.seriesName.split("series");//图例名称 |
||||
|
let value = param.value;//y轴值 |
||||
|
let color = param.color;//图例颜色 |
||||
|
let data = param.value[1] ? param.value[1] : '无数据'; |
||||
|
if (value[1] && seriesName.length == 1) { |
||||
|
htmlStr += '<div><span>' + xName + '</span> ' + seriesName[0] + ' <span style="margin-right:5px;display:inline-block;width:10px;height:10px;border-radius:5px;' |
||||
|
+ 'background-color:' + color + ';"></span>' + data + '</div>'; |
||||
|
} |
||||
|
} |
||||
|
return htmlStr; |
||||
|
} |
||||
|
} |
||||
|
//X轴 |
||||
|
eOption.xAxis = { |
||||
|
//type: 'category', |
||||
|
type: 'time', |
||||
|
// min: new Date(xxxxx), |
||||
|
// //结束时间 |
||||
|
// max: new Date(xxxxx), |
||||
|
// axisLabel: { |
||||
|
// interale: 0, |
||||
|
// rotate: -40, |
||||
|
// formatter: function (value) {//在这里写你需要的时间格式 |
||||
|
// var t_date = new Date(value); |
||||
|
// return [t_date.getFullYear(), t_date.getMonth() + 1, t_date.getDate()].join('-') + " " |
||||
|
// + [t_date.getHours(), t_date.getMinutes()].join(':'); |
||||
|
|
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
|
||||
|
} |
||||
|
//Y轴 |
||||
|
eOption.yAxis = { |
||||
|
type: 'value', |
||||
|
min: 'dataMin', |
||||
|
max: 'dataMax', |
||||
|
name: options.yAlias, |
||||
|
axisLabel: { |
||||
|
formatter: function (value, index) { |
||||
|
if (value < 0.01) { |
||||
|
if(value<0.001){ |
||||
|
return value?value.toFixed(4):0; |
||||
|
} |
||||
|
return value.toFixed(3); |
||||
|
} |
||||
|
return value.toFixed(2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
//toolbox 设置导出图片 |
||||
|
eOption.toolbox = { |
||||
|
feature: { |
||||
|
saveAsImage: {} |
||||
|
} |
||||
|
} |
||||
|
//缩放 |
||||
|
eOption.dataZoom = [ |
||||
|
{ |
||||
|
show: true, |
||||
|
realtime: true, |
||||
|
start: 0, |
||||
|
end: 100 |
||||
|
}, |
||||
|
{ |
||||
|
type: 'inside', |
||||
|
realtime: true, |
||||
|
start: 0, |
||||
|
end: 100 |
||||
|
} |
||||
|
] |
||||
|
//颜色标识 |
||||
|
eOption.legend = { |
||||
|
left: '0', |
||||
|
orient: 'vertical', |
||||
|
type: 'scroll', |
||||
|
icon: 'circle', |
||||
|
//文字过长可以使用下面方法截取字符串 |
||||
|
formatter: function (name) { |
||||
|
if (!name) return ''; |
||||
|
if (name.length > 8) { |
||||
|
if (name.length > 16) { |
||||
|
name = name.slice(0, 8) + `\n` + name.slice(0, 7) + "..."; |
||||
|
} else { |
||||
|
name = name.slice(0, 8) + `\n` + name.slice(8); |
||||
|
} |
||||
|
return name; |
||||
|
} else { |
||||
|
return name |
||||
|
} |
||||
|
}, |
||||
|
tooltip: { |
||||
|
show: true |
||||
|
} |
||||
|
} |
||||
|
// //数据源 |
||||
|
// eOption.dataset = { |
||||
|
// source: dataset |
||||
|
// } |
||||
|
//props.data; |
||||
|
eOption.series = dataArray |
||||
|
this.setState({ option: eOption }); |
||||
|
|
||||
|
//this.chartDownload = chart; |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { data, width, height, options, slider } = this.props; |
||||
|
const showSlider = options && options.sliderStart; |
||||
|
return ( |
||||
|
<div style={{ position: 'relative', paddingTop: 20, marginBottom: 24 }}> |
||||
|
{/* <div style={{ position: 'absolute', right: '20px', top: '20px', zIndex: 2000 }} > |
||||
|
<Button onClick={this.download} size="default">导出</Button> |
||||
|
</div> */} |
||||
|
<div> |
||||
|
{/* <div id={this.chartId} style={{ textAlign: 'left' }}></div> */} |
||||
|
<ReactEcharts |
||||
|
option={this.state.option} |
||||
|
/> |
||||
|
{/* <div id="echarts" style={{ textAlign: 'left' }}></div> */} |
||||
|
{/* { |
||||
|
showSlider ? [ |
||||
|
<div style={{ width: '100%' }} id={this.sliderId}></div> |
||||
|
] : null |
||||
|
} */} |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
export default LineTimeChart; |
@ -0,0 +1,122 @@ |
|||||
|
'use strict' |
||||
|
|
||||
|
|
||||
|
import React from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import moment from 'moment'; |
||||
|
import { Form, Button, Row, Col, DatePicker, Cascader, Select, Spin, Banner } from '@douyinfe/semi-ui'; |
||||
|
import LineTimeChart from '../components/lineTimeChartTemplate'; |
||||
|
|
||||
|
|
||||
|
const FormItem = Form.Item; |
||||
|
const { RangePicker } = DatePicker; |
||||
|
const Option = Select.Option; |
||||
|
const createForm = Form.create; |
||||
|
|
||||
|
const DataComponent =(props)=>{ |
||||
|
const {isRequesting,dataList}=props |
||||
|
const {items,sensors}=dataList |
||||
|
|
||||
|
const renderChart = () => { |
||||
|
// const { dataAcquisitionPointList, dataAcquisitionList, isRequesting, dataType, aggregationData } =props; |
||||
|
|
||||
|
if (isRequesting) { |
||||
|
return <Banner |
||||
|
type="info" |
||||
|
description="查询数据中,请稍候..."/> |
||||
|
} |
||||
|
if (JSON.stringify(dataList) == "{}") { |
||||
|
return <Banner description="无数据" type="info"/> |
||||
|
} |
||||
|
let chartContent = []; |
||||
|
let deviceItems ={pitstate: {name: "坑位状态", unit: null}} |
||||
|
let deviceSensors =[{id: "8d59eeef-147f-40e4-9516-768f8270b3fd", name: "女坑1",data:[{values: {pitstate: "1"}, time: "2023-11-06 03:03:58"},{values: {pitstate: "1"}, time: "2022-11-05 03:03:58"}]}]; |
||||
|
for (let deviceItem in items) { |
||||
|
let chartDataD = []; |
||||
|
let startD = ""; |
||||
|
let endD = ""; |
||||
|
for (let d = 0; d < sensors.length; d++) { |
||||
|
let isHas = false; |
||||
|
for (let s = 0; s < sensors[d].data.length; s++) { |
||||
|
let cdataR = {}; |
||||
|
let dataDS = sensors[d].data[s]; |
||||
|
cdataR.time = moment(dataDS.time).format('YYYY-MM-DD HH:mm:ss'); |
||||
|
if (startD == "") { |
||||
|
startD = dataDS.time; |
||||
|
endD = dataDS.time; |
||||
|
} else { |
||||
|
if (moment(startD) >= moment(dataDS.time)) |
||||
|
startD = dataDS.time; |
||||
|
if (moment(endD) <= moment(dataDS.time)) |
||||
|
endD = dataDS.time; |
||||
|
} |
||||
|
cdataR.name = sensors[d].name; |
||||
|
let deviceItemValue = Number(dataDS.values[deviceItem]); |
||||
|
if (!isNaN(deviceItemValue)) { |
||||
|
cdataR.value = deviceItemValue; |
||||
|
chartDataD.push(cdataR); |
||||
|
} |
||||
|
// if (deviceItemValue !== undefined) { |
||||
|
// cdataR.value = typeof deviceItemValue == 'number' ? Number(deviceItemValue) : null; |
||||
|
// chartDataD.push(cdataR); |
||||
|
// } |
||||
|
} |
||||
|
} |
||||
|
chartDataD.reverse(); |
||||
|
if (chartDataD.length > 0) { |
||||
|
const optionsD = { |
||||
|
"xArray": [], |
||||
|
"yAlias": deviceItems[deviceItem].name + '(' + deviceItems[deviceItem].unit + ')', |
||||
|
"sliderStart": moment(startD).format('YYYY-MM-DD HH:mm:ss'),//默认数据源第一个,时间毫秒数 |
||||
|
"sliderEnd": moment(endD).format('YYYY-MM-DD HH:mm:ss'),//默认数据源最后一个,时间毫秒数 |
||||
|
"mask": "yyyy-mm-dd HH:MM:ss",//时间格式 |
||||
|
} |
||||
|
chartContent.push( |
||||
|
<LineTimeChart |
||||
|
key={"slider-device-" + deviceItem} |
||||
|
data={chartDataD} |
||||
|
width={1000} |
||||
|
height={300} |
||||
|
slider={"slider-device-" + deviceItem} |
||||
|
options={optionsD} |
||||
|
/>) |
||||
|
} |
||||
|
} |
||||
|
if (chartContent.length == 0) { |
||||
|
return <Banner message="无数据" |
||||
|
type="info" |
||||
|
/> |
||||
|
} else { |
||||
|
return chartContent.map(p => { |
||||
|
return p; |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//渲染图表 |
||||
|
return (<div> |
||||
|
<span>{renderChart()}</span> |
||||
|
</div>) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
const mapStateToProps = (state) => { |
||||
|
const { auth, global, dataContinuityType, dataContinuity,pepProjectId,SensorsData} = state; |
||||
|
return { |
||||
|
user: auth.user, |
||||
|
actions: global.actions, |
||||
|
dataContinuityType: dataContinuityType.data || [], |
||||
|
dataContinuity: dataContinuity.data || [], |
||||
|
pepProjectId:global.pepProjectId, |
||||
|
isRequesting:SensorsData.isRequesting |
||||
|
}; |
||||
|
} |
||||
|
export default connect(mapStateToProps)(DataComponent); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,284 @@ |
|||||
|
import React, { useEffect, useState, useRef } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import moment from 'moment'; |
||||
|
import { exportWord } from 'mhtml-to-word' |
||||
|
import { saveAs } from 'file-saver'; |
||||
|
import { SkeletonScreen, } from "$components"; |
||||
|
import DataTableComponent from './dataTableComponent'; |
||||
|
import DataComponent from './dataComponent.jsx'; |
||||
|
import { IconSearch,IconLineChartStroked,IconStar,IconUser,IconBookOpenStroked} from '@douyinfe/semi-icons'; |
||||
|
import { Divider,Form, Button, Skeleton, Table, Pagination, Space, Row, Col, DatePicker, Cascader, Select, Spin, Alert, message, Menu, Icon,Nav,Tabs, TabPane} from '@douyinfe/semi-ui'; |
||||
|
import '../style.less' |
||||
|
|
||||
|
const DataDetail = (props) => { |
||||
|
const { dispatch, actions, user, dataContinuityType, dataContinuity,pepProjectId } = props |
||||
|
const { data } = actions |
||||
|
const [queryPage, setQueryPage] = useState({ limit: 10, page: 0 }); //页码信息 |
||||
|
const [limits, setLimits] = useState(0)//每页实际条数 |
||||
|
const [loading, setLoading] = useState(true); |
||||
|
const [params, setParams] = useState({}) |
||||
|
const [checkVis, setCheckVis] = useState(false) |
||||
|
const [checkData, setCheckData] = useState({}) |
||||
|
const [structList,setStructList]=useState([])//结构物筛选列表 |
||||
|
const [structId,setStructId]=useState(null)//结构物id(根据结构物下拉框赋值) |
||||
|
const [factosList,setFactorsList]=useState([])//监测因素列表 |
||||
|
const [factorId,setFactorId]=useState(null) |
||||
|
const [sensorList,setSensorList]=useState([])//设备列表 |
||||
|
const [sensorId,setSensorId]=useState([])//设备数组id |
||||
|
const [project,setProject]=useState(null)//project(eg:{projcet:'nbjj'}) |
||||
|
const form = useRef(); |
||||
|
//初始化 |
||||
|
useEffect(() => { |
||||
|
if(factorId&&project){ |
||||
|
let query={ |
||||
|
factorId, |
||||
|
sensorId:sensorId&&sensorId.length>0?sensorId.map(item=>`${project}-${item}`).join(','):[-11], |
||||
|
startTime:form.current.getValue('createTimes')[0], |
||||
|
endTime:form.current.getValue('createTimes')[1], |
||||
|
limit:queryPage.limit, |
||||
|
page:queryPage.page |
||||
|
} |
||||
|
queryData(query) |
||||
|
} |
||||
|
|
||||
|
// dispatch(sectionData.getContinuityType()) |
||||
|
// form.current.setValue('createTimes',[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]) |
||||
|
|
||||
|
}, [factorId,project,sensorId]) |
||||
|
|
||||
|
//监听变化 |
||||
|
useEffect(() => { |
||||
|
setStructList([]) |
||||
|
form.current.reset() |
||||
|
getData() |
||||
|
}, [pepProjectId]) |
||||
|
//监听结构物变化,查询监测因素 |
||||
|
useEffect(()=>{ |
||||
|
setLoading(true); |
||||
|
let queryParams = {structId} |
||||
|
if(structId){ |
||||
|
dispatch(data.getFactors({...queryParams})).then(res => { |
||||
|
if(res.success){ |
||||
|
const list=res.payload.data?.map(item=>{ |
||||
|
return { |
||||
|
value: item.SafetyFactorTypeId, |
||||
|
label: item.SafetyFactorTypeName, |
||||
|
} |
||||
|
}) |
||||
|
form.current.setValue('factor',list[0]?.value) |
||||
|
setFactorsList(list) |
||||
|
setFactorId(list[0]?.value) |
||||
|
setLoading(false); |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
},[structId]) |
||||
|
useEffect(()=>{ |
||||
|
let queryParams = {structId,SafetyFactorTypeId:factorId} |
||||
|
if(factorId){ |
||||
|
dispatch(data.getSensors({...queryParams})).then(res => { |
||||
|
if(res.success){ |
||||
|
const list=res.payload.data?.map(item=>{ |
||||
|
return { |
||||
|
value: item.SensorId, |
||||
|
label: item.SensorLocationDescription, |
||||
|
} |
||||
|
}) |
||||
|
form.current.setValue('point',[list[0]?.value]) |
||||
|
setSensorList(list) |
||||
|
setLoading(false) |
||||
|
setProject(res.payload.data[0].Project) |
||||
|
setSensorId([list[0]?.value]) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
},[factorId]) |
||||
|
const getData = (queryParams = {pomsProjectId:pepProjectId}) => { |
||||
|
setLoading(true); |
||||
|
dispatch(data.getProjectAllStructures({...queryParams})).then(res => |
||||
|
{ |
||||
|
if(res.success){ |
||||
|
const uniqueIds = new Set(); |
||||
|
const list=res.payload.data?.filter(item => { |
||||
|
const duplicate = uniqueIds.has(item.strucId); |
||||
|
uniqueIds.add(item.strucId); |
||||
|
return !duplicate |
||||
|
})?.map(item => ({ |
||||
|
value: item.strucId, |
||||
|
label: item.strucName, |
||||
|
project: item.Project, |
||||
|
})) |
||||
|
form.current.setValue('struct',list[0]?.value) |
||||
|
setStructId(list[0]?.value) |
||||
|
setStructList(list) |
||||
|
setLoading(false) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
) |
||||
|
} |
||||
|
|
||||
|
const downloadWord = (html = '', name = '数据监测 ' + moment().format('YYYY-MM-DD HH:mm:ss')) => { |
||||
|
exportWord({ |
||||
|
mhtml: html, |
||||
|
filename: name, |
||||
|
}) |
||||
|
// let blob = new Blob([html], { type: "application/msword;charset=utf-8" }); |
||||
|
// saveAs(blob, name + '.doc'); |
||||
|
} |
||||
|
//结构物筛选变化回调函数 |
||||
|
const structChange = (value) => { |
||||
|
setStructId(value) |
||||
|
form.current.setValue('point','') |
||||
|
form.current.setValue('factor','') |
||||
|
|
||||
|
// setStructName(structList.find(item=>item.value===value)?.label) |
||||
|
} |
||||
|
//结构物筛选发生变化回调函数 |
||||
|
const factorChange=(value)=>{ |
||||
|
form.current.setValue('point','') |
||||
|
setFactorId(value) |
||||
|
} |
||||
|
//点位筛选发生改变的回调函数 |
||||
|
const pointChange=(value)=>{ |
||||
|
setSensorId(value) |
||||
|
// setProject(structList.find(item=>item.value===structId)?.project) |
||||
|
} |
||||
|
//查询按钮回调函数 |
||||
|
const queryData=(query)=>{ |
||||
|
dispatch(data.getMonitorData(query)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
setCheckData(res.payload.data) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
const searchHandler=(values) => { |
||||
|
form.current.validate().then(rs=>{ |
||||
|
let query={ |
||||
|
factorId, |
||||
|
sensorId:sensorId&&sensorId.length>0?sensorId.map(item=>`${project}-${item}`).join(','):[-11], |
||||
|
startTime:moment(rs.createTimes[0]).format('YYYY-MM-DD HH:mm:ss'), |
||||
|
endTime:moment(rs.createTimes[1]).format('YYYY-MM-DD HH:mm:ss'), |
||||
|
limit:queryPage.limit, |
||||
|
page:queryPage.page |
||||
|
} |
||||
|
queryData(query) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}> |
||||
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> |
||||
|
<div style={{ display: 'flex', alignItems: 'center' }}> |
||||
|
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div> |
||||
|
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>数据详情</div> |
||||
|
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>DATA DETAIL</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<Form |
||||
|
getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}} |
||||
|
labelPosition='left' |
||||
|
layout="horizontal" |
||||
|
style={{ position: "relative", width: "100%", flex: 1 }} |
||||
|
// onSubmit={formSubmit} |
||||
|
> |
||||
|
<Row> |
||||
|
<Col span={12} > |
||||
|
<Form.Select |
||||
|
value={structId} |
||||
|
filter |
||||
|
labelWidth="75px" |
||||
|
label="结构物" |
||||
|
field="struct" |
||||
|
placeholder="结构物" |
||||
|
optionList={structList} |
||||
|
style={{ width: 260 }} |
||||
|
showClear |
||||
|
onChange={structChange} |
||||
|
></Form.Select> |
||||
|
</Col> |
||||
|
<Col span={12} > |
||||
|
<Form.Select |
||||
|
filter |
||||
|
label="监测因素" |
||||
|
field="factor" |
||||
|
placeholder="监测因素" |
||||
|
style={{ width: 260, marginLeft: 12, marginRight: 12 }} |
||||
|
showClear |
||||
|
optionList={factosList} |
||||
|
onChange={factorChange} |
||||
|
></Form.Select> |
||||
|
<Form.Select |
||||
|
filter |
||||
|
label="点位" |
||||
|
field="point" |
||||
|
placeholder="点位" |
||||
|
multiple |
||||
|
optionList={sensorList} |
||||
|
style={{ width: 260, marginLeft: 12, marginRight: 12 }} |
||||
|
showClear |
||||
|
onChange={pointChange} |
||||
|
></Form.Select> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
|
||||
|
<Row> |
||||
|
<Col span={12}> |
||||
|
<Form.DatePicker |
||||
|
// labelWidth="75px" |
||||
|
field="createTimes" |
||||
|
label="查询时间" |
||||
|
initValue={[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]} |
||||
|
// style={{ width: 268 }} |
||||
|
type="dateTimeRange" density="compact" |
||||
|
/> |
||||
|
<Col span={6}> |
||||
|
<Button theme='solid' type="primary" htmlType="submit" onClick={searchHandler}> |
||||
|
查询 |
||||
|
</Button> |
||||
|
</Col> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</Form> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<Row> |
||||
|
<Col> |
||||
|
<Tabs type="button"> |
||||
|
<TabPane tab={<span><IconLineChartStroked />趋势图</span>} itemKey="1"> |
||||
|
<Divider></Divider> |
||||
|
<DataComponent dataList={checkData}/> |
||||
|
</TabPane> |
||||
|
<TabPane tab={<span><IconBookOpenStroked />数据</span>} itemKey="2"> |
||||
|
<Divider></Divider> |
||||
|
|
||||
|
<DataTableComponent dataList={checkData}/> |
||||
|
</TabPane> |
||||
|
</Tabs> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</div> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { auth, global, dataContinuityType, dataContinuity,pepProjectId} = state; |
||||
|
return { |
||||
|
user: auth.user, |
||||
|
actions: global.actions, |
||||
|
dataContinuityType: dataContinuityType.data || [], |
||||
|
dataContinuity: dataContinuity.data || [], |
||||
|
pepProjectId:global.pepProjectId |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(DataDetail); |
@ -0,0 +1,137 @@ |
|||||
|
/* |
||||
|
* @Author: zhaobing |
||||
|
* @Date: 2023-11-02 09:32:25 |
||||
|
*/ |
||||
|
import React, { useState, useEffect } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Table, Button, Alert, Spin } from '@douyinfe/semi-ui'; |
||||
|
// import { ApiTable } from '../../../../utils/webapi'; |
||||
|
import FileSaver from 'file-saver'; |
||||
|
import moment from 'moment'; |
||||
|
|
||||
|
const DataTableComponent = (props) => { |
||||
|
const {dataList}=props |
||||
|
const [currentPage, setCurrentPage] = useState(1); |
||||
|
const [exportingFlag, setExportingFlag] = useState(false); |
||||
|
const [isRequesting, setIsRequesting] = useState(false); |
||||
|
const [arr,setArr]=useState(dataList.items) |
||||
|
const [data,setData]=useState([]) |
||||
|
const [columns,setColumns]=useState([]) |
||||
|
//初始化 |
||||
|
|
||||
|
useEffect(()=>{ |
||||
|
|
||||
|
// let arr=[{readingNumber:{name: '电表示数', unit: 'kWh'}}] |
||||
|
// let dataSource = [{values: {total: 24138.97}, time: "2023-11-03 14:18:27.000"},{values: {total: 24138.97}, time: "2023-11-03 14:18:27.000"}] |
||||
|
let columns = [] |
||||
|
columns.push({ |
||||
|
title: '设备位置', |
||||
|
dataIndex: 'position', |
||||
|
key: 'position', |
||||
|
}); |
||||
|
for (let index in arr) { |
||||
|
columns.push({ |
||||
|
title: arr[index].name + '(' + arr[index].unit + ')', |
||||
|
dataIndex: index, |
||||
|
sorter: (a, b) => b[index] - a[index], |
||||
|
key: index, |
||||
|
}) |
||||
|
}; |
||||
|
columns.push({ |
||||
|
title: '采集时间', |
||||
|
dataIndex: 'acqTime', |
||||
|
sorter: (a, b) => b['realTime'] - a['realTime'], |
||||
|
key: 'acqTime', |
||||
|
}); |
||||
|
//设备 |
||||
|
const data1=[] |
||||
|
for (let i = 0; i < dataList.length; i++) { |
||||
|
for (let k = 0; k < dataList[i].data.length; k++) { |
||||
|
let cdataT={} |
||||
|
let startT = ""; |
||||
|
let endT = ""; |
||||
|
let dataTS = dataList[i].data[k]; |
||||
|
cdataT.key = `station-${dataList[i].id}-${k}`; |
||||
|
cdataT.position = dataList[i].name; |
||||
|
cdataT.acqTime = moment(dataTS.time).format('YYYY-MM-DD HH:mm:ss'); |
||||
|
cdataT.realTime = moment(dataTS.time).valueOf(); |
||||
|
if (startT == "") { |
||||
|
startT = dataTS.time; |
||||
|
endT = dataTS.time; |
||||
|
} else { |
||||
|
if (moment(startT) >= moment(dataTS.time)) |
||||
|
startT = dataTS.time; |
||||
|
if (moment(endT) <= moment(dataTS.time)) |
||||
|
endT = dataTS.time; |
||||
|
} |
||||
|
//动态列的值 |
||||
|
for (let themeItem in arr) { |
||||
|
cdataT[themeItem] = dataTS.values[themeItem]; |
||||
|
} |
||||
|
data1.push(cdataT) |
||||
|
} |
||||
|
} |
||||
|
setData(data1) |
||||
|
setColumns(columns) |
||||
|
},[]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
const exporting = () => { |
||||
|
setIsRequesting(true); |
||||
|
}; |
||||
|
|
||||
|
const pageChange = (page, pageSize) => { |
||||
|
setCurrentPage(page); |
||||
|
}; |
||||
|
|
||||
|
const exportCsv = (data, title, filename) => { |
||||
|
setExportingFlag(true); |
||||
|
}; |
||||
|
|
||||
|
const exportVibrationCsv = (data, title, filename) => { |
||||
|
setExportingFlag(true); |
||||
|
}; |
||||
|
|
||||
|
const renderViberationChart = () => { |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
setCurrentPage(1); |
||||
|
}, [props]); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
return ( |
||||
|
<div> |
||||
|
<Spin spinning={isRequesting}> |
||||
|
<div> |
||||
|
{/* <p style={{ textAlign: 'right', margin: 8 }}> |
||||
|
{ |
||||
|
data.length ? <Button type="ghost" onClick={exporting}>导出</Button> : null |
||||
|
} |
||||
|
</p> */} |
||||
|
<Table dataSource={data} columns={columns} pagination={{ current: currentPage, onChange:pageChange }} /> |
||||
|
</div> |
||||
|
</Spin> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const mapStateToProps = (state) => { |
||||
|
const { auth, global, dataContinuityType, dataContinuity,pepProjectId} = state; |
||||
|
return { |
||||
|
user: auth.user, |
||||
|
actions: global.actions, |
||||
|
dataContinuityType: dataContinuityType.data || [], |
||||
|
dataContinuity: dataContinuity.data || [], |
||||
|
pepProjectId:global.pepProjectId |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
export default connect(mapStateToProps)(DataTableComponent); |
@ -0,0 +1,10 @@ |
|||||
|
.semi-form-horizontal{ |
||||
|
display: block; |
||||
|
} |
||||
|
.fontStyle{ |
||||
|
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; |
||||
|
} |
||||
|
.semi-col-12 { |
||||
|
display: flex; |
||||
|
|
||||
|
} |
@ -0,0 +1,117 @@ |
|||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
import { ApiTable, basicAction } from '$utils' |
||||
|
|
||||
|
export function getAbnMethods (query) {//获取异常识别算法
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_ABN_METHODS", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getAbnMethods}`, |
||||
|
msg: { option: "获取异常识别算法" }, |
||||
|
reducer: { name: "abnMethods", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export function getAbnParamList (query) {//获取异常参数配置
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_ABN_PARAM_LIST", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getAbnParamList}`, |
||||
|
msg: { option: "获取异常参数配置" }, |
||||
|
reducer: { name: "abnParam", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function addAbnParam (data) {//新增异常参数配置
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "post", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "ADD_ABN_PARAM", |
||||
|
data, |
||||
|
url: `${ApiTable.addAbnParamList}`, |
||||
|
msg: { option: "新增异常参数配置" }, |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export function batchCfgAbnParams (ids, enable) {//批量启用or禁用
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "put", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "BATCH_CFG_ABNPARAMS", |
||||
|
data:enable, |
||||
|
url: `${ApiTable.batchSwitch.replace("{ids}", ids)}`, |
||||
|
msg: { option: enable?.enabled?"批量启用异常参数配置" : "批量禁用异常参数配置" }, |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export function deleteAbnParams (id) {//删除配置
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "delete", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "DELETE_ABN_PARAMS", |
||||
|
url: `${ApiTable.deleteAbnParams.replace("{id}", id)}`, |
||||
|
msg: { option: '删除异常参数配置' }, |
||||
|
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function editAbnParams (id, pushData) {//编辑配置
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "put", |
||||
|
data:pushData, |
||||
|
dispatch: dispatch, |
||||
|
actionType: "EDIT_ABN_PARAMS", |
||||
|
url: `${ApiTable.editAbnParams.replace("{id}", id)}`, |
||||
|
msg: { option: '修改异常参数配置' }, |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//中断数据对比
|
||||
|
export function getItemAbnResult_int (structId, start, end, pushData) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "post", |
||||
|
data:pushData, |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_ITEM_ABN_RESULT_INT", |
||||
|
url: `${ApiTable.getAbnTaskResult.replace("{structId}", structId).replace("{start}", start).replace("{end}", end)}`, |
||||
|
msg: { option: '中断数据对比' }, |
||||
|
reducer: { name: "abnItemState_int", params: { noClear: true } }, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//异常趋势数据对比
|
||||
|
export function getItemAbnResult_tr (structId, start, end, pushData) {//
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "post", |
||||
|
data:pushData, |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_ITEM_ABN_RESULT_TR", |
||||
|
url: `${ApiTable.getAbnTaskResult.replace("{structId}", structId).replace("{start}", start).replace("{end}", end)}`, |
||||
|
msg: { option: '异常趋势数据对比' }, |
||||
|
reducer: { name: "abnItemState_tr", params: { noClear: true } }, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//毛刺数据对比
|
||||
|
export function getItemAbnResult_burr (structId, start, end, pushData) {//
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "post", |
||||
|
data:pushData, |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_ITEM_ABN_RESULT_BURR", |
||||
|
url: `${ApiTable.getAbnTaskResult.replace("{structId}", structId).replace("{start}", start).replace("{end}", end)}`, |
||||
|
msg: { option: '毛刺数据对比' }, |
||||
|
reducer: { name: "abnItemState_burr", params: { noClear: true } }, |
||||
|
}) |
||||
|
} |
@ -0,0 +1,168 @@ |
|||||
|
import React, { Component } from 'react'; |
||||
|
import createG2 from 'g2-react'; |
||||
|
|
||||
|
import G2, { Plugin } from 'g2'; |
||||
|
//import G2 from '@antv/g2' |
||||
|
//import DataSet from '@antv/data-set'; |
||||
|
|
||||
|
import 'g2-plugin-slider'; |
||||
|
//import Slider from '@antv/g2-plugin-slider'; |
||||
|
|
||||
|
import './theme'; |
||||
|
import { Button } from '@douyinfe/semi-ui'; |
||||
|
import FileSaver from 'file-saver'; |
||||
|
/** |
||||
|
* props: |
||||
|
* |
||||
|
* data: [{ |
||||
|
* name: 图例名称, |
||||
|
* value: y轴数值, |
||||
|
* time: x轴时间值 |
||||
|
* }], // 数组或封装后的frame |
||||
|
* height: 图表高度, |
||||
|
* configs: { |
||||
|
* slider: { |
||||
|
* start: 初始开始点, |
||||
|
* end: 初始结束点 |
||||
|
* } |
||||
|
* }, |
||||
|
* yAxis: 修改默认y轴值对应的键 |
||||
|
*/ |
||||
|
|
||||
|
let chartDownload = ""; |
||||
|
class TimeAbnValueLineChart extends Component { |
||||
|
constructor(props) { |
||||
|
super(props); |
||||
|
|
||||
|
this.sliderId = 'range' + Math.floor(Math.random() * 1000000000); |
||||
|
const Chart = createG2((chart, configs) => { |
||||
|
chart.source(props.data, { |
||||
|
'value': { |
||||
|
formatter: val => { |
||||
|
if (configs.unit) { |
||||
|
return val + configs.unit |
||||
|
} else { |
||||
|
return val |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
'time': { |
||||
|
range: [0, 1], |
||||
|
type: 'time', |
||||
|
alias: '时间', |
||||
|
mask: 'yyyy-mm-dd HH:MM:ss' |
||||
|
}, |
||||
|
'days': { |
||||
|
type: 'linear', |
||||
|
alias: '', |
||||
|
formatter: val => { |
||||
|
return val + '天' |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
chart.axis(props.xAxis || 'time', { |
||||
|
title: null, |
||||
|
line: { |
||||
|
//stroke: '#C0C0C0', |
||||
|
}, label: { |
||||
|
textStyle: { |
||||
|
//fill: '#C0C0C0', // 文本的颜色 |
||||
|
} |
||||
|
} |
||||
|
//mask: 'yyyy-mm-dd HH:MM:ss' |
||||
|
}); |
||||
|
chart.axis(props.yAxis || 'value', { |
||||
|
title: null, |
||||
|
}); |
||||
|
chart.legend({ |
||||
|
title: null, |
||||
|
position: 'right', // 设置图例的显示位置 |
||||
|
itemWrap: true, |
||||
|
//dx: -30, |
||||
|
height: 120, |
||||
|
}); |
||||
|
chart.tooltip(true, { |
||||
|
map: { |
||||
|
|
||||
|
}, |
||||
|
}); |
||||
|
chart.on('tooltipchange', function (ev) { |
||||
|
//let items = ev.items; |
||||
|
|
||||
|
}); |
||||
|
chart.line().position(`${props.xAxis || 'time'}*${props.yAxis || 'value'}`).color('name').shape('line').size(2); |
||||
|
// chart.point().position(`${props.xAxis || 'time'}*${props.yAxis || 'value'}`).color('name', (name) => name != this.props.itemName).size(4).shape('circle').style({ |
||||
|
// stroke: '#fff', |
||||
|
// lineWidth: 1 |
||||
|
// }); |
||||
|
if (this.props.contentType != 'trend') { |
||||
|
chart.point().position(`${props.xAxis || 'time'}*${props.yAxis || 'value'}`).color('name').size(4).shape('circle').style({ |
||||
|
stroke: '#fff', |
||||
|
lineWidth: 1 |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (!configs || !configs.slider) { |
||||
|
chart.render(); |
||||
|
} else { |
||||
|
// 创建滑动条 |
||||
|
//Plugin.slider.ATTRS.textAttr = { fill: '#fff' }; |
||||
|
var slider = new Plugin.slider({ |
||||
|
domId: this.sliderId, |
||||
|
height: 30, |
||||
|
charts: chart, |
||||
|
xDim: props.xAxis || 'time', |
||||
|
yDim: props.yAxis || 'value', |
||||
|
start: configs.slider.start, // 滑块开始值,用户未定义则使用默认值 |
||||
|
end: configs.slider.end // 滑块结束值,用户未定义则使用默认值 |
||||
|
}); |
||||
|
slider.render(); |
||||
|
} |
||||
|
this.chartDownload = chart; |
||||
|
}); |
||||
|
this.Chart = Chart; |
||||
|
} |
||||
|
download = () => { |
||||
|
//this.chartDownload.downloadImage(); |
||||
|
const dataurl = this.chartDownload.toImage(); |
||||
|
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], |
||||
|
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); |
||||
|
while (n--) { |
||||
|
u8arr[n] = bstr.charCodeAt(n); |
||||
|
} |
||||
|
let blob = new Blob([u8arr], { type: mime }); |
||||
|
|
||||
|
FileSaver.saveAs(blob, "chart.png"); |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { unit, data, width, height, configs, xAxis } = this.props; |
||||
|
|
||||
|
let margin = [20, 350, 30, 60]; |
||||
|
const showSlider = configs && configs.slider; |
||||
|
if (showSlider) { |
||||
|
//margin = [20, 145, 30, 138]; |
||||
|
margin = [20, 335, 80, 138]; |
||||
|
} |
||||
|
return ( |
||||
|
<div style={{ position: 'relative', paddingTop: 20, marginBottom: 24 }}> |
||||
|
<div style={{ position: 'absolute', right: '20px', top: '20px', zIndex: 2 }} > |
||||
|
<Button onClick={this.download} size="default">导出</Button> |
||||
|
</div> |
||||
|
<div> |
||||
|
<this.Chart |
||||
|
data={data} width={width} height={height || 500} xAxis={xAxis} |
||||
|
plotCfg={{ margin: margin }} forceFit={true} ref="myChart" configs={configs} /> |
||||
|
{ |
||||
|
showSlider ? [ |
||||
|
<div className="chart-inner-divider"></div>, |
||||
|
<div className="chart-slider" id={this.sliderId}></div> |
||||
|
] : null |
||||
|
} |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default TimeAbnValueLineChart; |
@ -0,0 +1,288 @@ |
|||||
|
|
||||
|
import React, { useState, useEffect,useRef } from "react"; |
||||
|
import { connect } from "react-redux"; |
||||
|
import moment from 'moment'; |
||||
|
import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row } from "@douyinfe/semi-ui"; |
||||
|
import {IconLineChartStroked} from "@douyinfe/semi-icons" |
||||
|
import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
const TrendModal = (props) => { |
||||
|
const { modalData,dispatch,actions,sensorId, closeModal, visible,project } = props |
||||
|
const{install}=actions |
||||
|
const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const form = useRef() |
||||
|
//初始化 |
||||
|
useEffect(() => { |
||||
|
}, []) |
||||
|
const checkPoint = (rule, value) => { |
||||
|
const pattern = /^[1-9]*[1-9][0-9]*$/; |
||||
|
if (pattern.test(value) && value != 1) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
const checkNumber = (rule, val) => { |
||||
|
const strRegex = /^-?\d+(\.\d+)?$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
const btnFormSubmit = () => { |
||||
|
let data = modalData |
||||
|
form.current.validate().then(value=>{ |
||||
|
let paramJson = { |
||||
|
"thr_burr": Number(value.burr),//毛刺阈值 |
||||
|
"win_med": Number(value.ws),//滑动中值 |
||||
|
"win_avg": Number(value.rc),//滑动均值 |
||||
|
"thr_der": Number(value.dv), //导数阈值 |
||||
|
"win_grad": Number(value.pn),//渐变点个数 |
||||
|
"thr_grad": Number(value.gv),//渐变阈值 |
||||
|
"days_Last": Number(value.timeRange),//分析时长 |
||||
|
} |
||||
|
let pushData = { |
||||
|
station: data.sensorId,//测点 |
||||
|
factorId: data.factorId, |
||||
|
itemId: data.itemIndex, |
||||
|
abnType: 3,//异常趋势 |
||||
|
enabled: value.checked, |
||||
|
params: paramJson |
||||
|
}; |
||||
|
dispatch(install.editAbnParams(data.id,pushData)).then(res => { |
||||
|
if (res.success) { |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
closeModal() |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
const checkInterger = (rule, val) => { |
||||
|
// if(val){ |
||||
|
const strRegex = /^[1-9]*[1-9][0-9]*$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
// } |
||||
|
|
||||
|
}; |
||||
|
const timeOnChange = (dates, dateStrings) => { |
||||
|
if (dates.length == 0) { |
||||
|
setCurrStartTime(null) |
||||
|
setCurrEndTime(null) |
||||
|
return Notification.warning({ |
||||
|
content: `请选择时间`, |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss')) |
||||
|
setCurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss')) |
||||
|
} |
||||
|
//数据对比 |
||||
|
// const dataCompare = () => { |
||||
|
// this.props.form.validateFields((err, values) => { |
||||
|
// if (!err) { |
||||
|
// let paramJson = { |
||||
|
// "thr_burr": values.burr,//毛刺阈值 |
||||
|
// "win_med": values.ws,//滑动中值 |
||||
|
// "win_avg": values.rc,//滑动均值 |
||||
|
// "thr_der": values.dv, //导数阈值 |
||||
|
// "win_grad": values.pn,//渐变点个数 |
||||
|
// "thr_grad": values.gv,//渐变阈值 |
||||
|
// "days_Last": values.timeRange,//分析时长 |
||||
|
// } |
||||
|
// let data = { |
||||
|
// station: this.props.modalData.stationId, |
||||
|
// factorId: this.props.modalData.factorId, |
||||
|
// itemId: this.props.modalData.itemId, |
||||
|
// abnType: 'trend',//算法类型 |
||||
|
// enabled: true, |
||||
|
// params: paramJson |
||||
|
// }; |
||||
|
// let start = this.state.currStartTime; |
||||
|
// let end = this.state.currEndTime; |
||||
|
// this.props.dispatch(getItemAbnResult_tr(this.props.structId, start, end, data)).then(res => { |
||||
|
// }) |
||||
|
// } |
||||
|
// }); |
||||
|
// } |
||||
|
|
||||
|
// let originalData = ['还没查询'], calcArray = [], key, itemName, start, end; |
||||
|
// let stationsData = []; |
||||
|
// if (abnItemCompData) { |
||||
|
// originalData = abnItemCompData.stationData; |
||||
|
// calcArray = abnItemCompData.resultArray; |
||||
|
// key = abnItemCompData.itemKey; |
||||
|
// itemName = abnItemCompData.itemName; |
||||
|
// start = originalData && originalData.length > 0 ? originalData[0].time : ''; |
||||
|
// end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : ''; |
||||
|
|
||||
|
// for (let i = 0; i < originalData.length; i++) { |
||||
|
// let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// if (calcArray) { |
||||
|
// let preLineData = calcArray.calcPreprocess; |
||||
|
// let abnTrends = calcArray.calcFinal; |
||||
|
// for (let j = 0; j < preLineData.length; j++) { |
||||
|
// let one = { name: "预处理+滑动均值后数据", value: preLineData[j].value, time: preLineData[j].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// for (let t = 0; t < abnTrends.length; t++) { |
||||
|
// let name = abnTrends[t].startTime + "至" + abnTrends[t].endTime + abnTrends[t].des + ",渐变差值:" + abnTrends[t].value.toFixed(2) + abnItemCompData.unit; |
||||
|
// let start = { name: name, value: abnTrends[t].startValue, time: abnTrends[t].startTime }; |
||||
|
// let end = { name: name, value: abnTrends[t].endValue, time: abnTrends[t].endTime }; |
||||
|
// stationsData.push(start); |
||||
|
// stationsData.push(end); |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
TrendModal.propTypes = { |
||||
|
visible: PropTypes.bool.isRequired, |
||||
|
closeModal: PropTypes.func.isRequired |
||||
|
}; |
||||
|
|
||||
|
return <div> |
||||
|
<Modal title="异常参数编辑" maskClosable={false} visible={visible} onCancel={closeModal} width={1000} |
||||
|
footer={[<div> |
||||
|
<Button type="primary" theme='borderless' htmlType="submit" onClick={btnFormSubmit}>保存</Button> |
||||
|
<Button key="cancel" onClick={closeModal}>关闭</Button> |
||||
|
</div>]}> |
||||
|
<Form getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}} labelPosition='left' |
||||
|
|
||||
|
> |
||||
|
<Row> |
||||
|
<Form.Input label='测点位置' field='stationName' |
||||
|
rules={[{ required: true, message: '测点位置' }]} |
||||
|
disabled={true} |
||||
|
initValue={modalData.sensorName} |
||||
|
> |
||||
|
</Form.Input> |
||||
|
<Form.Input label='监测因素' field='factorName' |
||||
|
rules={[{ required: true, message: '监测因素' }]} |
||||
|
disabled={true} |
||||
|
initValue={modalData.factorName} |
||||
|
> |
||||
|
</Form.Input> |
||||
|
<Form.Switch label='是否启用' field='checked' |
||||
|
rules={[{ required: true, message: '是否启用' }]} |
||||
|
initValue={modalData.enabled} |
||||
|
> |
||||
|
</Form.Switch> |
||||
|
|
||||
|
<Form.Select showSearch filter label="分析时长" field="timeRange" |
||||
|
placeholder="请选择分析时长" style={{ width: 127, marginBottom: 0 }} |
||||
|
initValue={modalData.params.days_Last} rules={[{ |
||||
|
required: true, message: "请选择分析时长" |
||||
|
}]} |
||||
|
> |
||||
|
<Select.Option value="1">1个月</Select.Option> |
||||
|
<Select.Option value="2">2个月</Select.Option> |
||||
|
<Select.Option value="3">3个月</Select.Option> |
||||
|
</Form.Select> |
||||
|
</Row> |
||||
|
|
||||
|
<Row style={{ marginTop: 18 }}> |
||||
|
<Form.Input |
||||
|
initValue={ modalData.params.thr_burr} |
||||
|
field='burr' |
||||
|
placeholder="毛刺阈值:数字" label="毛刺阈值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} t |
||||
|
trigger='blur' /> |
||||
|
<Form.Input |
||||
|
initValue={ modalData.params.win_med} |
||||
|
field='ws' |
||||
|
placeholder="滑动中值:正值" label="滑动中值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} |
||||
|
trigger='blur' /> |
||||
|
<Form.Input |
||||
|
field='rc' |
||||
|
initValue={ modalData.params.win_avg} |
||||
|
placeholder="滑动均值:正值" label="滑动均值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} |
||||
|
trigger='blur' /> |
||||
|
</Row> |
||||
|
<Row style={{ marginTop: 18 }}> |
||||
|
<Form.Input |
||||
|
field='dv' |
||||
|
initValue={modalData.params.thr_der} |
||||
|
placeholder="导数阈值:数字" label="导数阈值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} |
||||
|
trigger='blur' /> |
||||
|
<Form.Input |
||||
|
field='pn' |
||||
|
initValue={modalData.params.win_grad} |
||||
|
placeholder="渐变点个数:正值" label="渐变点数" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入大于1的正整数", validator: checkPoint |
||||
|
}]} |
||||
|
trigger='blur' /> |
||||
|
|
||||
|
<Form.Input |
||||
|
field='gv' |
||||
|
initValue={modalData.params.thr_grad} |
||||
|
placeholder="渐变阈值:数字" label="渐变阈值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} trigger='blur' |
||||
|
/> |
||||
|
|
||||
|
</Row> |
||||
|
<Row style={{ marginTop: 28 }}> |
||||
|
<Form.DatePicker |
||||
|
placeholder={['开始时间', '结束时间']} |
||||
|
field="timeSelected" |
||||
|
label="查询时间" |
||||
|
initValue={[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]} |
||||
|
type="dateTimeRange" density="compact" |
||||
|
onChange={timeOnChange} |
||||
|
/> |
||||
|
|
||||
|
{/* <Button size='default' type="primary" onClick={dataCompare}>数据对比</Button> */} |
||||
|
</Row> |
||||
|
|
||||
|
{/* <div className="data-chart-container"> |
||||
|
{originalData && originalData[0] == '还没查询' ? |
||||
|
<div style={{ margin: '30px 0' }}><IconLineChartStroked/> 输入参数,点击数据对比展示数据对比图</div> |
||||
|
: |
||||
|
originalData && originalData[0] != '还没查询' && originalData.length > 0 ? |
||||
|
<TimeAbnValueLineChart contentType={'trend'} data={stationsData} width={300} height={300} |
||||
|
itemName={itemName} configs={{ slider: { start: start, end: end } }} /> |
||||
|
: |
||||
|
<div style={{ margin: '30px 0' }}><IconLineChartStroked/> 没有查询到任何有效数据!</div>} |
||||
|
</div> */} |
||||
|
|
||||
|
</Form> |
||||
|
</Modal> |
||||
|
</div>; |
||||
|
} |
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { abnItemState_tr, global} = state; |
||||
|
return { |
||||
|
abnItemCompData: abnItemState_tr.items, |
||||
|
actions:global.actions |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(TrendModal) |
@ -0,0 +1,214 @@ |
|||||
|
import React, { useRef,useState,useEffect } from "react"; |
||||
|
import { connect } from "react-redux"; |
||||
|
import moment from 'moment'; |
||||
|
import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row, IconLineChartStroked } from "@douyinfe/semi-ui"; |
||||
|
import {IconInfoCircle} from "@douyinfe/semi-icons" |
||||
|
import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
|
||||
|
const BurrModal = (props) => { |
||||
|
const form = useRef() |
||||
|
const {dispatch, modalData, closeModal, visible, structId,item,actions,sensorId,project } = props |
||||
|
const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const{install}=actions |
||||
|
useEffect(()=>{ |
||||
|
// form.current.setValues({stationName: modalData.sensorName, |
||||
|
// factorName: modalData.factorName + "/" + modalData.item, |
||||
|
// burr: modalData.params.thr_burr, |
||||
|
// isEnable: modalData.enabled}) |
||||
|
},[]) |
||||
|
//表单类 |
||||
|
const btnFormSubmit = () => { |
||||
|
let data = modalData; |
||||
|
form.current.validate().then(value=>{ |
||||
|
let paramJson = { "thr_burr": Number(value.burr) }; |
||||
|
let pushData = { |
||||
|
station: data.sensorId,//测点 |
||||
|
factorId: data.factorId, |
||||
|
itemId: data.itemIndex, |
||||
|
abnType: 2,//毛刺 |
||||
|
enabled: value.checked, |
||||
|
params: paramJson |
||||
|
} |
||||
|
dispatch(install.editAbnParams(data.id,pushData)).then(res => { |
||||
|
if (res.success) { |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
closeModal() |
||||
|
} |
||||
|
}) |
||||
|
// this.props.dispatch(editAbnParams(data.id, pushData)).then(_ => { |
||||
|
// this.props.dispatch(getAbnParamList(this.props.structId)).then(() => { |
||||
|
// this.props.closeModal(); |
||||
|
// }); |
||||
|
// }); |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
const checkInterger = (rule, value, callback) => { |
||||
|
if (!value) { |
||||
|
callback(); |
||||
|
} else { |
||||
|
const pattern = /^[1-9]*[1-9][0-9]*$/; |
||||
|
if (pattern.test(value)) { |
||||
|
callback(); |
||||
|
} else { |
||||
|
callback(new Error('请输入正整数')); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
const timeOnChange = (dates, dateStrings) => { |
||||
|
if (dates.length == 0) { |
||||
|
setCurrStartTime(null) |
||||
|
setcurrEndTime(null) |
||||
|
return message.warning('请选择时间'); |
||||
|
} |
||||
|
setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss')) |
||||
|
setcurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss')) |
||||
|
} |
||||
|
//数据对比 |
||||
|
// const dataCompare = () => { |
||||
|
// form.current.validate().then(res=>{ |
||||
|
// if (res.success) { |
||||
|
// let paramJson = { "thr_burr": values.burr } |
||||
|
// let data = { |
||||
|
// station: modalData.stationId, |
||||
|
// factorId: modalData.factorId, |
||||
|
// itemId: modalData.itemId, |
||||
|
// abnType: 'burr',//算法类型 |
||||
|
// enabled: true, |
||||
|
// params: paramJson |
||||
|
// }; |
||||
|
// let start = this.state.currStartTime |
||||
|
// let end = this.state.currEndTime |
||||
|
|
||||
|
// // this.props.dispatch(getItemAbnResult_burr(this.props.structId, start, end, data)).then(res => { |
||||
|
// // }) |
||||
|
// } |
||||
|
// }) |
||||
|
// } |
||||
|
const checkNumber = (rule, val) => { |
||||
|
const strRegex = /^-?\d+(\.\d+)?$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
// let originalData = ['还没查询'], calcArray = [], key, itemName, start, end; |
||||
|
// let stationsData = []; |
||||
|
// if (abnItemCompData) { |
||||
|
// originalData = abnItemCompData.stationData; |
||||
|
// calcArray = abnItemCompData.resultArray; |
||||
|
// key = abnItemCompData.itemKey; |
||||
|
// itemName = abnItemCompData.itemName; |
||||
|
// start = originalData && originalData.length > 0 ? originalData[0].time : ''; |
||||
|
// end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : ''; |
||||
|
// for (let i = 0; i < originalData.length; i++) { |
||||
|
// let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// for (let j = 0; j < calcArray.length; j++) { |
||||
|
// let one = { |
||||
|
// name: "毛刺点:" + calcArray[j].time + "(突变大小:" + calcArray[j].burr.toFixed(2) + abnItemCompData.unit + ")" |
||||
|
// , value: calcArray[j].value, time: calcArray[j].time |
||||
|
// }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// } |
||||
|
BurrModal.propTypes = { |
||||
|
visible: PropTypes.bool.isRequired, |
||||
|
closeModal: PropTypes.func.isRequired |
||||
|
} |
||||
|
return (<> |
||||
|
<Modal maskClosable={false} title="异常参数编辑" visible={visible} onCancel={closeModal} width={900} |
||||
|
footer={[<div> |
||||
|
<Button type="primary" theme='borderless' htmlType="submit" onClick={btnFormSubmit}>保存</Button> |
||||
|
<Button key="cancel" onClick={closeModal}>关闭</Button> |
||||
|
</div>]}> |
||||
|
<Form |
||||
|
labelPosition='left' |
||||
|
// initialValues={{'stationName': modalData.sensorName, |
||||
|
// 'factorName': modalData.factorName + "/" + item, |
||||
|
// 'burr': modalData.params.thr_burr, |
||||
|
// 'checked': modalData.enabled}} |
||||
|
getFormApi={formApi => { form.current = formApi}}> |
||||
|
<Row> |
||||
|
<Form.Input label='测点位置' field='stationName' |
||||
|
initValue={modalData.sensorName} |
||||
|
rules={[{ required: true, message: '测点位置' }]} |
||||
|
disabled={true} |
||||
|
> |
||||
|
</Form.Input> |
||||
|
<Form.Input label='监测因素' field='factorName' |
||||
|
initValue={ modalData.factorName + "/" + item} |
||||
|
rules={[{ required: true, message: '监测因素' }]} |
||||
|
disabled={true} |
||||
|
> |
||||
|
</Form.Input> |
||||
|
<Form.Switch label='是否启用' field='checked' |
||||
|
initValue={modalData.enabled} |
||||
|
rules={[{ required: true, message: '是否启用' }]} |
||||
|
|
||||
|
> |
||||
|
</Form.Switch> |
||||
|
<Form.Input |
||||
|
field='burr' |
||||
|
placeholder="毛刺阈值:数字" label="毛刺阈值" |
||||
|
initValue={modalData.params.thr_burr} |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} |
||||
|
trigger='blur' /> |
||||
|
</Row> |
||||
|
|
||||
|
<Row style={{ marginTop: 15 }}> |
||||
|
<Form.DatePicker |
||||
|
placeholder={['开始时间', '结束时间']} |
||||
|
field="timeSelected" |
||||
|
label="查询时间" |
||||
|
initValue={[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]} |
||||
|
type="dateTimeRange" density="compact" |
||||
|
onChange={timeOnChange} |
||||
|
|
||||
|
/> |
||||
|
|
||||
|
{/* <Button size='default' type="primary" onClick={dataCompare}>数据对比</Button> */} |
||||
|
</Row> |
||||
|
|
||||
|
{/* <div className="data-chart-container"> |
||||
|
{originalData && originalData[0] == '还没查询' ? |
||||
|
<div style={{ margin: '30px 0' }}><IconInfoCircle /> 输入参数,点击数据对比展示数据对比图</div> |
||||
|
: |
||||
|
originalData && originalData[0] != '还没查询' && originalData.length > 0 ? |
||||
|
<TimeAbnValueLineChart contentType={'burr'} data={stationsData} width={300} height={300} |
||||
|
itemName={itemName} configs={{ slider: { start: start, end: end } }} /> |
||||
|
: |
||||
|
<div style={{ margin: '30px 0' }}><IconInfoCircle/> 没有查询到任何有效数据!</div>} |
||||
|
</div> */} |
||||
|
|
||||
|
</Form> |
||||
|
</Modal> |
||||
|
|
||||
|
</>) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { abnItemState_burr,global } = state; |
||||
|
return { |
||||
|
abnItemCompData: abnItemState_burr.items, |
||||
|
actions:global.actions, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(BurrModal) |
@ -0,0 +1,187 @@ |
|||||
|
import React, { useRef,useState,useEffect } from "react"; |
||||
|
import { connect } from "react-redux"; |
||||
|
import moment from 'moment'; |
||||
|
import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row } from "@douyinfe/semi-ui"; |
||||
|
import { IconLineChartStroked } from "@douyinfe/semi-icons" |
||||
|
import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart' |
||||
|
import PropTypes from 'prop-types'; |
||||
|
|
||||
|
const InterruptModal = (props) => { |
||||
|
const form = useRef() |
||||
|
const {modalData,actions,dispatch, closeModal, visible, structId,item,sensorId,project}=props |
||||
|
const {install}=actions |
||||
|
const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss')) |
||||
|
|
||||
|
|
||||
|
//表单类 |
||||
|
const btnFormSubmit = () => { |
||||
|
let data = modalData |
||||
|
form.current.validate().then(value=>{ |
||||
|
let paramJson = { "thr_int": Number(value.thr_int) } |
||||
|
let pushData = { |
||||
|
station: data.sensorId,//测点 |
||||
|
factorId: data.factorId, |
||||
|
itemId: null, |
||||
|
abnType: 1,//中断 |
||||
|
enabled: value.checked, |
||||
|
params: paramJson |
||||
|
} |
||||
|
dispatch(install.editAbnParams(data.id,pushData)).then(res => { |
||||
|
if (res.success) { |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
closeModal() |
||||
|
} |
||||
|
}) |
||||
|
// this.props.dispatch(editAbnParams(data.id, pushData)).then(_ => { |
||||
|
// this.props.dispatch(getAbnParamList(this.props.structId)).then(() => { |
||||
|
// this.props.closeModal(); |
||||
|
// }); |
||||
|
// }); |
||||
|
|
||||
|
}) |
||||
|
// this.props.form.validateFieldsAndScroll((err, values) => { |
||||
|
|
||||
|
// }); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const checkInterger = (rule, val) => { |
||||
|
// if(val){ |
||||
|
const strRegex = /^[1-9]*[1-9][0-9]*$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
// } |
||||
|
|
||||
|
}; |
||||
|
const timeOnChange = (dates, dateStrings) => { |
||||
|
if (dates.length == 0) { |
||||
|
setCurrStartTime(null) |
||||
|
setcurrEndTime(null) |
||||
|
return message.warning('请选择时间'); |
||||
|
} |
||||
|
setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss')) |
||||
|
setcurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss')) |
||||
|
} |
||||
|
//数据对比 |
||||
|
// const dataCompare = () => { |
||||
|
// form.current.validate().then(_=>{ |
||||
|
// let paramJson = { "thr_int": values.interrupt } |
||||
|
// let data = { |
||||
|
// station: modalData.stationId, |
||||
|
// factorId: modalData.factorId, |
||||
|
// itemId: modalData.itemId, |
||||
|
// abnType: 'interrupt',//算法类型 |
||||
|
// enabled: true, |
||||
|
// params: paramJson |
||||
|
// }; |
||||
|
// let start = this.state.currStartTime; |
||||
|
// let end = this.state.currEndTime; |
||||
|
// this.props.dispatch(getItemAbnResult_int(this.props.structId, start, end, data)).then(res => { |
||||
|
// }) |
||||
|
// }) |
||||
|
// } |
||||
|
// let originalData = ['还没查询'], calcArray = [], key, itemName, start, end; |
||||
|
// let stationsData = []; |
||||
|
// if (abnItemCompData) { |
||||
|
// originalData = abnItemCompData.stationData; |
||||
|
// calcArray = abnItemCompData.resultArray; |
||||
|
// key = abnItemCompData.itemKey; |
||||
|
// itemName = abnItemCompData.itemName; |
||||
|
// start = originalData && originalData.length > 0 ? originalData[0].time : ''; |
||||
|
// end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : ''; |
||||
|
// for (let i = 0; i < originalData.length; i++) { |
||||
|
// let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// for (let j = 0; j < calcArray.length; j++) { |
||||
|
// let one = { name: "中断点:" + calcArray[j].time + "(中断时长:" + calcArray[j].hour.toFixed(2) + "h)", value: calcArray[j].value, time: calcArray[j].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
// InterruptModal.propTypes = { |
||||
|
// visible: PropTypes.bool.isRequired, |
||||
|
// closeModal: PropTypes.func.isRequired |
||||
|
// } |
||||
|
return <div> |
||||
|
<Modal maskClosable={false} title="异常参数编辑" visible={visible} onCancel={closeModal} width={900} |
||||
|
footer={[<div> |
||||
|
<Button type="primary" theme='borderless' htmlType="submit" onClick={btnFormSubmit}>保存</Button> |
||||
|
<Button key="cancel" onClick={closeModal}>关闭</Button> |
||||
|
</div>]}> |
||||
|
<Form labelPosition="left" getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}}> |
||||
|
<Row> |
||||
|
<Form.Input label='测点位置' field='stationName' |
||||
|
rules={[{ required: true, message: '测点位置' }]} |
||||
|
disabled={true} |
||||
|
initValue={modalData.sensorName} |
||||
|
> |
||||
|
</Form.Input> |
||||
|
<Form.Input label='监测因素' field='factorName' |
||||
|
initValue={modalData.factorName} |
||||
|
rules={[{ required: true, message: '监测因素' }]} |
||||
|
disabled={true} |
||||
|
> |
||||
|
</Form.Input> |
||||
|
<Form.Switch label='是否启用' field='checked' |
||||
|
initValue={modalData.enabled} |
||||
|
rules={[{ required: true, message: '是否启用' }]} |
||||
|
> |
||||
|
</Form.Switch> |
||||
|
<Form.Input |
||||
|
field='thr_int' |
||||
|
placeholder="中断阈值:正整数" label="中断阈值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur' |
||||
|
initValue={modalData.params.thr_int} |
||||
|
/> |
||||
|
<Row style={{ marginTop: 15 }}> |
||||
|
<Form.DatePicker |
||||
|
placeholder={['开始时间', '结束时间']} |
||||
|
field="timeSelected" |
||||
|
label="查询时间" |
||||
|
initValue={[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]} |
||||
|
type="dateTimeRange" density="compact" |
||||
|
onChange={timeOnChange} |
||||
|
|
||||
|
/> |
||||
|
|
||||
|
{/* <Button size='default' type="primary" onClick={dataCompare}>数据对比</Button> */} |
||||
|
</Row> |
||||
|
{/* <Button size='default' type="primary" onClick={dataCompare}>数据对比</Button> */} |
||||
|
|
||||
|
{/* <div className="data-chart-container"> |
||||
|
{originalData && originalData[0] == '还没查询' ? |
||||
|
<div style={{ margin: '30px 0' }}><IconLineChartStroked /> 输入参数,点击数据对比展示数据对比图(中断仅展示第一个监测项)</div> |
||||
|
: |
||||
|
originalData && originalData[0] != '还没查询' && originalData.length > 0 ? |
||||
|
<TimeAbnValueLineChart contentType={'interrupt'} data={stationsData} width={300} height={300} |
||||
|
itemName={itemName} configs={{ slider: { start: start, end: end } }} /> |
||||
|
: |
||||
|
<div style={{ margin: '30px 0' }}><IconLineChartStroked /> 没有查询到任何有效数据!</div>} |
||||
|
</div> */} |
||||
|
</Row> |
||||
|
</Form> |
||||
|
</Modal> |
||||
|
</div> |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { global } = state; |
||||
|
return { |
||||
|
// abnItemCompData: abnItemState_int.items, |
||||
|
// abnItemCompData:[], |
||||
|
actions:global.actions |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(InterruptModal) |
@ -0,0 +1,474 @@ |
|||||
|
import G2 from 'g2'; |
||||
|
|
||||
|
const DEFAULT_COLOR = '#ffea00'; |
||||
|
const FONT_FAMILY = '"Microsoft YaHei", "微软雅黑", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", SimSun, "sans-serif"'; |
||||
|
const theme = { |
||||
|
defaultColor: DEFAULT_COLOR, // 默认主题色 |
||||
|
plotCfg: { |
||||
|
margin: [20, 80, 60, 80] |
||||
|
}, |
||||
|
facetCfg: { |
||||
|
type: 'rect', |
||||
|
margin: 10, |
||||
|
facetTitle: { |
||||
|
titleOffset: 16, |
||||
|
colDimTitle: { |
||||
|
title: { |
||||
|
fontSize: 14, |
||||
|
textAlign: 'center', |
||||
|
fill: '#999' |
||||
|
} |
||||
|
}, |
||||
|
colTitle: { |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
textAlign: 'center', |
||||
|
fill: '#999' |
||||
|
} |
||||
|
}, |
||||
|
rowTitle: { |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
textAlign: 'center', |
||||
|
rotate: 90, |
||||
|
fill: '#999' |
||||
|
} |
||||
|
}, |
||||
|
rowDimTitle: { |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
textAlign: 'center', |
||||
|
rotate: 90, |
||||
|
fill: '#999' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
binWidth: 0.03, // bin 统计的默认值 |
||||
|
fontFamily: FONT_FAMILY, |
||||
|
colors: { |
||||
|
'default': ['#ffea00', '#0de3ff', '#fe8501', '#00ff36', '#0097ff', '#fe4c9b', '#9bff00', '#bd67ff', '#f6ba33', '#978cff'], |
||||
|
intervalStack: ['#ffea00', '#0de3ff', '#fe8501', '#00ff36', '#0097ff', '#fe4c9b', '#9bff00', '#bd67ff', '#f6ba33', '#978cff'] |
||||
|
}, |
||||
|
shapes: { |
||||
|
point: ['hollowCircle', 'hollowSquare', 'hollowDiamond', 'hollowBowtie', 'hollowTriangle', |
||||
|
'hollowHexagon', 'cross', 'tick', 'plus', 'hyphen', 'line'], |
||||
|
line: ['line', 'dash', 'dot'], |
||||
|
area: ['area'] |
||||
|
}, |
||||
|
hues: ['red', 'yellow', 'green', 'blue', 'orange', 'purple', 'pink', 'brown', 'white', 'gray', 'black'], |
||||
|
axis: { |
||||
|
top: { |
||||
|
position: 'top', |
||||
|
titleOffset: 30, |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
fill: '#999' |
||||
|
}, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2', |
||||
|
fontSize: 12 |
||||
|
} |
||||
|
}, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc', |
||||
|
value: 5 |
||||
|
} |
||||
|
}, |
||||
|
bottom: { |
||||
|
position: 'bottom', |
||||
|
titleOffset: 45, |
||||
|
labelOffset: 20, |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
textAlign: 'center', |
||||
|
fill: '#999' |
||||
|
}, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2', |
||||
|
fontSize: 12 |
||||
|
} |
||||
|
}, |
||||
|
line: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc', |
||||
|
value: 5 |
||||
|
} |
||||
|
}, |
||||
|
left: { |
||||
|
position: 'left', |
||||
|
titleOffset: 60, |
||||
|
labelOffset: 13, |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
fill: '#999' |
||||
|
}, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2' |
||||
|
} |
||||
|
}, |
||||
|
line: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc', |
||||
|
value: 5 |
||||
|
}, |
||||
|
grid: { |
||||
|
line: { |
||||
|
stroke: '#495f7a', |
||||
|
lineWidth: 1, |
||||
|
lineDash: [2, 2] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
right: { |
||||
|
position: 'right', |
||||
|
titleOffset: 60, |
||||
|
labelOffset: 13, |
||||
|
title: { |
||||
|
fontSize: 12, |
||||
|
fill: '#999' |
||||
|
}, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2' |
||||
|
} |
||||
|
}, |
||||
|
line: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc', |
||||
|
value: 5 |
||||
|
} |
||||
|
}, |
||||
|
circle: { |
||||
|
labelOffset: 5, |
||||
|
line: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
grid: { |
||||
|
line: { |
||||
|
stroke: '#d9d9d9', |
||||
|
lineWidth: 1, |
||||
|
lineDash: [1, 3] |
||||
|
} |
||||
|
}, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
gauge: { |
||||
|
grid: null, |
||||
|
labelOffset: 5, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
value: -20, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
subTick: 5, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
clock: { |
||||
|
grid: null, |
||||
|
labelOffset: 5, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
value: -20, |
||||
|
stroke: '#C0D0E0' |
||||
|
}, |
||||
|
subTick: 5, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
radius: { |
||||
|
titleOffset: 45, |
||||
|
labels: { |
||||
|
label: { |
||||
|
fill: '#a2adc2' |
||||
|
} |
||||
|
}, |
||||
|
line: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
grid: { |
||||
|
line: { |
||||
|
stroke: '#d9d9d9', |
||||
|
lineWidth: 1, |
||||
|
lineDash: [2, 2] |
||||
|
}, |
||||
|
type: 'circle' |
||||
|
} |
||||
|
}, |
||||
|
helix: { |
||||
|
grid: null, |
||||
|
labels: { |
||||
|
label: null |
||||
|
}, |
||||
|
line: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#ccc' |
||||
|
}, |
||||
|
tickLine: { |
||||
|
lineWidth: 1, |
||||
|
value: 5, |
||||
|
stroke: '#ccc' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
labels: { |
||||
|
offset: 14, |
||||
|
label: { |
||||
|
fill: '#666', |
||||
|
fontSize: 12 |
||||
|
} |
||||
|
}, |
||||
|
treemapLabels: { |
||||
|
offset: 10, |
||||
|
label: { |
||||
|
fill: '#fff', |
||||
|
fontSize: 14, |
||||
|
textBaseline: 'top', |
||||
|
fontStyle: 'bold' |
||||
|
} |
||||
|
}, |
||||
|
innerLabels: { |
||||
|
label: { |
||||
|
fill: '#fff', |
||||
|
fontSize: 12 |
||||
|
} |
||||
|
}, // 在theta坐标系下的饼图文本内部的样式 |
||||
|
thetaLabels: { |
||||
|
labelLine: { |
||||
|
lineWidth: 1 |
||||
|
}, |
||||
|
labelHeight: 14, |
||||
|
offset: 30 |
||||
|
}, // 在theta坐标系下的饼图文本的样式 |
||||
|
legend: { |
||||
|
right: { |
||||
|
position: 'right', |
||||
|
back: null, |
||||
|
title: { |
||||
|
fill: '#a2adc2' |
||||
|
}, |
||||
|
spacingX: 10, |
||||
|
spacingY: 12, |
||||
|
markerAlign: 'center', |
||||
|
wordSpaceing: 12, |
||||
|
word: { |
||||
|
fill: '#a2adc2' |
||||
|
}, |
||||
|
width: 20, |
||||
|
height: 156 |
||||
|
}, |
||||
|
left: { |
||||
|
position: 'left', |
||||
|
back: null, |
||||
|
title: { |
||||
|
fill: '#a2adc2' |
||||
|
}, |
||||
|
spacingX: 10, |
||||
|
spacingY: 12, |
||||
|
markerAlign: 'center', |
||||
|
wordSpaceing: 12, |
||||
|
word: { |
||||
|
fill: '#a2adc2' |
||||
|
}, |
||||
|
width: 20, |
||||
|
height: 156 |
||||
|
}, |
||||
|
top: { |
||||
|
position: 'top', |
||||
|
title: null, |
||||
|
back: null, |
||||
|
spacingX: 16, |
||||
|
spacingY: 10, |
||||
|
markerAlign: 'center', |
||||
|
wordSpaceing: 12, |
||||
|
word: { |
||||
|
fill: '#a2adc2' |
||||
|
}, |
||||
|
width: 156, |
||||
|
height: 20 |
||||
|
}, |
||||
|
bottom: { |
||||
|
position: 'bottom', |
||||
|
title: null, |
||||
|
back: null, |
||||
|
spacingX: 16, |
||||
|
spacingY: 10, |
||||
|
markerAlign: 'center', |
||||
|
wordSpaceing: 12, |
||||
|
word: { |
||||
|
fill: '#a2adc2' |
||||
|
}, |
||||
|
width: 156, |
||||
|
height: 20 |
||||
|
} |
||||
|
}, |
||||
|
tooltip: { |
||||
|
crosshairs: false, |
||||
|
offset: 15, |
||||
|
crossLine: { |
||||
|
stroke: '#666' |
||||
|
}, |
||||
|
wordSpaceing: 6, |
||||
|
markerCfg: { |
||||
|
symbol: 'circle', |
||||
|
radius: 3 |
||||
|
} |
||||
|
}, |
||||
|
activeShape: { |
||||
|
point: { |
||||
|
radius: 5, |
||||
|
fillOpacity: 0.7 |
||||
|
}, |
||||
|
hollowPoint: { |
||||
|
lineWidth: 2, |
||||
|
radius: 4 |
||||
|
}, |
||||
|
interval: { |
||||
|
fillOpacity: 0.7 |
||||
|
}, |
||||
|
hollowInterval: { |
||||
|
lineWidth: 2 |
||||
|
}, |
||||
|
area: { |
||||
|
fillOpacity: 0.85 |
||||
|
}, |
||||
|
hollowArea: { |
||||
|
lineWidth: 2 |
||||
|
}, |
||||
|
line: { |
||||
|
lineWidth: 2 |
||||
|
}, |
||||
|
polygon: { |
||||
|
fillOpacity: 0.75 |
||||
|
} |
||||
|
}, // 图形激活时,鼠标移动到上面 |
||||
|
shape: { |
||||
|
point: { |
||||
|
lineWidth: 1, |
||||
|
fill: DEFAULT_COLOR, |
||||
|
radius: 4 |
||||
|
}, |
||||
|
hollowPoint: { |
||||
|
fill: '#fff', |
||||
|
lineWidth: 1, |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
radius: 3 |
||||
|
}, |
||||
|
interval: { |
||||
|
lineWidth: 0, |
||||
|
fill: DEFAULT_COLOR, |
||||
|
fillOpacity: 0.85 |
||||
|
}, |
||||
|
pie: { |
||||
|
lineWidth: 1, |
||||
|
stroke: '#fff' |
||||
|
}, |
||||
|
hollowInterval: { |
||||
|
fill: '#fff', |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
fillOpacity: 0, |
||||
|
lineWidth: 1 |
||||
|
}, |
||||
|
area: { |
||||
|
lineWidth: 0, |
||||
|
fill: DEFAULT_COLOR, |
||||
|
fillOpacity: 0.6 |
||||
|
}, |
||||
|
polygon: { |
||||
|
lineWidth: 0, |
||||
|
fill: DEFAULT_COLOR, |
||||
|
fillOpacity: 1 |
||||
|
}, |
||||
|
hollowPolygon: { |
||||
|
fill: '#fff', |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
fillOpacity: 0, |
||||
|
lineWidth: 1 |
||||
|
}, |
||||
|
hollowArea: { |
||||
|
fill: '#fff', |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
fillOpacity: 0, |
||||
|
lineWidth: 1 |
||||
|
}, |
||||
|
line: { |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
lineWidth: 1, |
||||
|
fill: null |
||||
|
} |
||||
|
}, |
||||
|
guide: { |
||||
|
text: { |
||||
|
fill: '#666', |
||||
|
fontSize: 12 |
||||
|
}, |
||||
|
line: { |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
lineDash: [0, 2, 2] |
||||
|
}, |
||||
|
rect: { |
||||
|
lineWidth: 0, |
||||
|
fill: DEFAULT_COLOR, |
||||
|
fillOpacity: 0.1 |
||||
|
}, |
||||
|
tag: { |
||||
|
line: { |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
lineDash: [0, 2, 2] |
||||
|
}, |
||||
|
text: { |
||||
|
fill: '#666', |
||||
|
fontSize: 12, |
||||
|
textAlign: 'center' |
||||
|
}, |
||||
|
rect: { |
||||
|
lineWidth: 0, |
||||
|
fill: DEFAULT_COLOR, |
||||
|
fillOpacity: 0.1 |
||||
|
} |
||||
|
}, |
||||
|
html: { |
||||
|
align: 'cc' |
||||
|
} |
||||
|
}, |
||||
|
tooltipMarker: { |
||||
|
fill: '#fff', |
||||
|
symbol: 'circle', |
||||
|
lineWidth: 2, |
||||
|
stroke: DEFAULT_COLOR, |
||||
|
radius: 4 |
||||
|
} // 提示信息在折线图、区域图上形成点的样式 |
||||
|
}; |
||||
|
|
||||
|
var Theme = G2.Util.mix(true, {}, G2.Theme, theme); |
||||
|
|
||||
|
G2.Global.setTheme(Theme); |
@ -0,0 +1,106 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import React, { } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Row,Col, Card,Icon } from '@douyinfe/semi-ui'; |
||||
|
import { IconSearch,IconLineChartStroked,IconStar,IconUser} from '@douyinfe/semi-icons'; |
||||
|
import Trend from './trend'; |
||||
|
import Burr from './Burr'; |
||||
|
import Interrupt from './Interrupt'; |
||||
|
|
||||
|
const AbnRecognize = (props) => { |
||||
|
// const {abnCompareData}=props |
||||
|
const {calcMethod,abnCompareData,factorId,structId,strunctChange,itName,sensorId,project}=props |
||||
|
// let originalData = ['还没查询'], calcArray = [], key, itemName, start, end; |
||||
|
// let stationsData = []; |
||||
|
// if (abnCompareData &&calcMethod == abnCompareData.method) { |
||||
|
// originalData = abnCompareData.stationData; |
||||
|
// calcArray = abnCompareData.resultArray; |
||||
|
// key = abnCompareData.itemKey; |
||||
|
// itemName = abnCompareData.itemName; |
||||
|
// start = originalData && originalData.length > 0 ? originalData[0].time : ''; |
||||
|
// end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : ''; |
||||
|
// for (let i = 0; i < originalData.length; i++) { |
||||
|
// let one = { name: itemName + "(单位:" + abnCompareData.unit + ")", value: originalData[i][key], time: originalData[i].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// if (calcMethod == 'interrupt') { |
||||
|
// for (let j = 0; j < calcArray.length; j++) { |
||||
|
// let one = { name: "中断点:" + calcArray[j].time + "(中断时长:" + calcArray[j].hour.toFixed(2) + "h)", value: calcArray[j].value, time: calcArray[j].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// } else if (calcMethod == 'burr') { |
||||
|
// for (let j = 0; j < calcArray.length; j++) { |
||||
|
// let one = { |
||||
|
// name: "毛刺点:" + calcArray[j].time + "(突变大小:" + calcArray[j].burr.toFixed(2) + abnCompareData.unit + ")", |
||||
|
// value: calcArray[j].value, time: calcArray[j].time |
||||
|
// }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// } else { |
||||
|
// if (calcArray) { |
||||
|
// let preLineData = calcArray.calcPreprocess; |
||||
|
// let abnTrends = calcArray.calcFinal; |
||||
|
// for (let j = 0; j < preLineData.length; j++) { |
||||
|
// let one = { name: "预处理+滑动均值后数据", value: preLineData[j].value, time: preLineData[j].time }; |
||||
|
// stationsData.push(one); |
||||
|
// } |
||||
|
// for (let t = 0; t < abnTrends.length; t++) { |
||||
|
// let name = abnTrends[t].startTime + "至" + abnTrends[t].endTime + abnTrends[t].des + ",渐变差值:" + abnTrends[t].value.toFixed(2) + abnCompareData.unit; |
||||
|
// let start = { name: name, value: abnTrends[t].startValue, time: abnTrends[t].startTime }; |
||||
|
// let end = { name: name, value: abnTrends[t].endValue, time: abnTrends[t].endTime }; |
||||
|
// stationsData.push(start); |
||||
|
// stationsData.push(end); |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
|
||||
|
const renderContent=()=> { |
||||
|
const SubContent = { |
||||
|
'interrupt': <Interrupt project={project} sensorId={sensorId} structId={structId} factorId={factorId} />, |
||||
|
'burr': <Burr project={project} sensorId={sensorId} structId={structId} factorId={factorId} itName={itName} />, |
||||
|
'trend': <Trend project={project} sensorId={sensorId} structId={structId} factorId={factorId} itName={itName}/>, |
||||
|
}; |
||||
|
return SubContent[calcMethod]; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
return (<> |
||||
|
<hr style={{ borderTopWidth: 0, marginBottom: 18 }} /> |
||||
|
{/* <Row> |
||||
|
<Col> |
||||
|
<Card title={<div><IconLineChartStroked/><span style={{ marginLeft: 6 }}>数据对比</span></div>}> |
||||
|
<div bodyStyle={{ padding: 0 }}> |
||||
|
<div className="data-chart-container"> |
||||
|
{originalData && originalData[0] == '还没查询' ? |
||||
|
<div style={{ margin: '20px 0' }}><IconLineChartStroked/> 输入参数,点击数据对比展示数据对比图(默认显示选择的第一个传感器)</div> |
||||
|
: |
||||
|
originalData && originalData[0] != '还没查询' && originalData.length > 0 ? |
||||
|
<timeChart contentType={calcMethod} data={stationsData} width={300} height={300} |
||||
|
itemName={itemName} configs={{ slider: { start: start, end: end } }} /> |
||||
|
: |
||||
|
<div style={{ margin: '20px 0' }}><IconLineChartStroked/> 没有查询到任何有效数据!</div>} |
||||
|
</div> |
||||
|
</div> |
||||
|
</Card> |
||||
|
</Col> |
||||
|
</Row> */} |
||||
|
{renderContent()} |
||||
|
|
||||
|
</>) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { auth, global, OrganizationDeps } = state; |
||||
|
return { |
||||
|
// loading: OrganizationDeps.isRequesting, |
||||
|
// user: auth.user, |
||||
|
// actions: global.actions, |
||||
|
}; |
||||
|
} |
||||
|
export default connect(mapStateToProps)(AbnRecognize) |
@ -0,0 +1,267 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import React, { useState,useRef } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag,Notification, Form } from '@douyinfe/semi-ui'; |
||||
|
import BurrModal from '../components/burrModal'; |
||||
|
import { useEffect } from 'react'; |
||||
|
|
||||
|
const Burr = (props) => { |
||||
|
const {factorId,abnParam,structId,dispatch,actions,itName,sensorId,project}=props |
||||
|
const form = useRef() |
||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]) |
||||
|
const [filterFunc, setFilterFunc] = useState({}) |
||||
|
const [modalVisible, setModalVisible] = useState(false) |
||||
|
const [bvBatch, setBvBatch] = useState('') |
||||
|
const [modalData, setModalData] = useState(null) |
||||
|
const {install}=actions |
||||
|
const filterSet = { sensorName: null, factorName: null } |
||||
|
|
||||
|
useEffect(()=>{ |
||||
|
},[]) |
||||
|
const onSelectChange = (selectedRowKeys) => { |
||||
|
setSelectedRowKeys(selectedRowKeys); |
||||
|
} |
||||
|
const compareAndEdit = (e, record) => { |
||||
|
setModalVisible(true) |
||||
|
setModalData(record) |
||||
|
} |
||||
|
const modalCancel = e => { |
||||
|
setModalVisible(false) |
||||
|
}; |
||||
|
const removeItem = e => { |
||||
|
dispatch(install.deleteAbnParams(e)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
} |
||||
|
}) |
||||
|
}; |
||||
|
const handleInputChange = e => { |
||||
|
if (e != null) { |
||||
|
const value = e |
||||
|
let func = (ep => (s => (s.sensorName).search(ep) > -1))(value); |
||||
|
filterSet.sensorName = func; |
||||
|
func = (ep => (s => (s.factorName).search(ep) > -1))(value); |
||||
|
filterSet.factorName = func; |
||||
|
} else { |
||||
|
filterSet.sensorName = null; |
||||
|
filterSet.factorName = null; |
||||
|
} |
||||
|
setFilterFunc(filterSet) |
||||
|
} |
||||
|
//批量启用or禁用 |
||||
|
const onSwitchChange = (e) => { |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(','); |
||||
|
let data = { |
||||
|
enabled: e, |
||||
|
use: 'switch' |
||||
|
}; |
||||
|
//批量启用or禁用逻辑 |
||||
|
dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id })) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
//批量删除 |
||||
|
const batchDelete = () => { |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(','); |
||||
|
dispatch(install.deleteAbnParams(ids)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id })) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// this.props.dispatch(deleteAbnParams(ids)).then(_ => { |
||||
|
// this.props.dispatch(getAbnParamList(this.props.structId)); |
||||
|
// }); |
||||
|
} else { |
||||
|
Notification.warning({ |
||||
|
content: '您尚未勾选任何参数配置!', |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
//批量保存 |
||||
|
const batchSave = () => { |
||||
|
form.current.validate().then(res=>{ |
||||
|
let dataSource = abnParam.filter(a => a.abnType == 2 && a.factorId == factorId) |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(','); |
||||
|
let data = { |
||||
|
paramJson: { "thr_burr": bvBatch }, |
||||
|
use: 'notSwitch', |
||||
|
}; |
||||
|
//批量保存逻辑 |
||||
|
dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id })) |
||||
|
} |
||||
|
}) |
||||
|
} else if (dataSource.length != 0) { |
||||
|
Notification.warning({ |
||||
|
content: '您尚未勾选任何参数配置!', |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
const batchBurr = (value) => { |
||||
|
setBvBatch(value) |
||||
|
} |
||||
|
const checkNumber = (rule, val) => { |
||||
|
const strRegex = /^-?\d+(\.\d+)?$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
let dataSource = abnParam.filter(a => a.abnType == 2 && a.factorId == factorId) |
||||
|
const rowSelection = { |
||||
|
selectedRowKeys: selectedRowKeys, |
||||
|
onChange: onSelectChange, |
||||
|
}; |
||||
|
let filterData = dataSource |
||||
|
let flag = false |
||||
|
let keyArr = [] |
||||
|
let tmpds = [] |
||||
|
let dataPush = [] |
||||
|
Object.keys(filterFunc).forEach(key => { |
||||
|
const filter = filterFunc[key] |
||||
|
filterData = dataSource |
||||
|
if (filter != null) { |
||||
|
flag = true |
||||
|
filterData = filterData.filter(filter) |
||||
|
if (filterData.length > 0) { |
||||
|
filterData.map(s => { |
||||
|
if (keyArr.indexOf(s.id) == -1) { |
||||
|
keyArr.push(s.id) |
||||
|
dataPush.push(s) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id) |
||||
|
let columns = [ |
||||
|
{ |
||||
|
title: "测点位置", |
||||
|
dataIndex: "sensorName", |
||||
|
key: "sensorName", |
||||
|
width: '20%', |
||||
|
}, |
||||
|
{ |
||||
|
title: "监测项", |
||||
|
dataIndex: "factorName", |
||||
|
key: "factorName", |
||||
|
width: '20%', |
||||
|
render: (text, record) => ( |
||||
|
record.factorName + "/" + itName |
||||
|
), |
||||
|
}, |
||||
|
{ |
||||
|
title: "毛刺阈值", |
||||
|
dataIndex: "params", |
||||
|
key: "params", |
||||
|
width: '20%', |
||||
|
render: text => text.thr_burr |
||||
|
}, |
||||
|
{ |
||||
|
title: "启用状态", |
||||
|
dataIndex: "enabled", |
||||
|
key: "enabled", |
||||
|
width: '20%', |
||||
|
render: (text, record) => ( |
||||
|
record.enabled ? <Tag color="blue">已启用</Tag> : <Tag>已禁用</Tag> |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
title: "操作", |
||||
|
key: "action", |
||||
|
width: '20%', |
||||
|
render: (text, record) => ( |
||||
|
<span> |
||||
|
<Button theme='borderless' type='primary' onClick={(e) => compareAndEdit(e, record)}>编辑</Button> |
||||
|
<span className="ant-divider"></span> |
||||
|
<Popconfirm title="确认删除该参数配置?" id={record.id} onConfirm={() => {removeItem(record.id) }}> |
||||
|
<Button theme='borderless' type='danger'>删除</Button> |
||||
|
</Popconfirm> |
||||
|
</span> |
||||
|
), |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
return (<Card style={{ marginTop: 15 }}> |
||||
|
<Form style={{ marginBottom: 15 }} |
||||
|
getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}} |
||||
|
labelPosition='left' |
||||
|
|
||||
|
> |
||||
|
<Row style={{ display: 'flex',alignItems:'center'}}> |
||||
|
<Col span={5}> |
||||
|
<Form.Input field='keywords' noLabel={true} style={{ marginBottom: 0,width: '95%' }} placeholder="关键词:测点位置、监测项" onChange={handleInputChange}> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Form.Switch field='switch' label='批量启用' style={{ marginBottom: 0 }} checked onChange={onSwitchChange}> |
||||
|
</Form.Switch> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input field='bv1' noLabel={true} style={{ marginBottom: 0,width: '93%' }} |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} trigger='blur' |
||||
|
onChange={e =>batchBurr(e)} placeholder="毛刺阈值:数字"> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Button style={{ marginBottom: 0 }} type='primary' theme='borderless' onClick={batchSave}>批量保存</Button> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Popconfirm title="确认批量删除选中的参数配置?" onConfirm={batchDelete}> |
||||
|
<Button style={{ fontSize: 13 }} theme='borderless' type='danger'>批量删除</Button> |
||||
|
</Popconfirm> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</Form> |
||||
|
<Table rowSelection={rowSelection} columns={columns} dataSource={tmpds} /> |
||||
|
{modalVisible ? <BurrModal |
||||
|
project={project} |
||||
|
structId={structId} |
||||
|
visible={true} |
||||
|
closeModal={modalCancel} |
||||
|
modalData={modalData} |
||||
|
item={itName} |
||||
|
sensorId={sensorId} |
||||
|
/> : ''} |
||||
|
</Card> |
||||
|
) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { abnParam ,global} = state; |
||||
|
// let isRequesting = abnParamState?.isRequesting; |
||||
|
|
||||
|
return { |
||||
|
isRequesting: false,//请求状态 |
||||
|
abnParam: abnParam?.data||[], |
||||
|
actions:global.actions |
||||
|
} |
||||
|
} |
||||
|
export default connect(mapStateToProps)(Burr) |
||||
|
|
@ -0,0 +1,496 @@ |
|||||
|
'use strict' |
||||
|
|
||||
|
import React, { useEffect, useState, useRef } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Row, Form, Col, Select, Button, Notification } from '@douyinfe/semi-ui'; |
||||
|
import moment from 'moment'; |
||||
|
import AbnRecognize from './AbnRecognize' |
||||
|
const methodType = { |
||||
|
'interrupt': 1, |
||||
|
'burr': 2, |
||||
|
'trend': 3, |
||||
|
} |
||||
|
const methodDes = { |
||||
|
'interrupt': "中断", |
||||
|
'burr': "毛刺", |
||||
|
'trend': "异常趋势", |
||||
|
} |
||||
|
|
||||
|
const Calc = (props) => { |
||||
|
const { actions, dispatch, pepProjectId, abnMethods, abnParam } = props |
||||
|
const { install, data } = actions |
||||
|
const [structId, setStructId] = useState(null) |
||||
|
const [methodType, setMethodType] = useState('interrupt') |
||||
|
const [loading, setLoading] = useState(false) |
||||
|
const [structList, setStructList] = useState([])//结构物筛选列表 |
||||
|
const [project, setProject] = useState('')//project(eg:{projcet:'nbjj'}) |
||||
|
const [reCalcFactorId, setReCalcFactorId] = useState(null); |
||||
|
const [msg, setMsg] = useState('') |
||||
|
const [factosList, setFactorsList] = useState([])//监测因素列表 |
||||
|
const [factorId, setFactorId] = useState([]) |
||||
|
const [sensorList, setSensorList] = useState([])//设备列表 |
||||
|
const [sensorId, setSensorId] = useState([])//设备id' |
||||
|
const [itemId, setItemId] = useState(null)//监测项索引 |
||||
|
const [itemName, setItemName] = useState('')//检测项name |
||||
|
const form = useRef() |
||||
|
const form2 = useRef() |
||||
|
const method = { |
||||
|
'interrupt': 1, |
||||
|
'burr': 2, |
||||
|
'trend': 3, |
||||
|
} |
||||
|
//初始化 |
||||
|
useEffect(() => { |
||||
|
// getData() |
||||
|
dispatch(install.getAbnMethods()) |
||||
|
}, []) |
||||
|
useEffect(() => { |
||||
|
if(project){ |
||||
|
const id = [...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId: id })) |
||||
|
} |
||||
|
}, [sensorId, factorId,project]) |
||||
|
|
||||
|
//监听变化 |
||||
|
useEffect(() => { |
||||
|
setStructList([]) |
||||
|
form.current.reset() |
||||
|
form2.current.reset() |
||||
|
form2.current.setValue('method', methodType) |
||||
|
getData() |
||||
|
}, [pepProjectId]) |
||||
|
const checkInterger = (rule, val) => { |
||||
|
// if(val){ |
||||
|
const strRegex = /^[1-9]*[1-9][0-9]*$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
// } |
||||
|
|
||||
|
}; |
||||
|
const checkNumber = (rule, val) => { |
||||
|
const strRegex = /^-?\d+(\.\d+)?$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
const checkPoint = (rule, value) => { |
||||
|
const pattern = /^[1-9]*[1-9][0-9]*$/; |
||||
|
if (pattern.test(value) && value != 1) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
const getJson = (values) => { |
||||
|
let paramJson; |
||||
|
switch (methodType) { |
||||
|
case 'interrupt': |
||||
|
return paramJson = { "thr_int": Number(values.iv) }; |
||||
|
case 'burr': |
||||
|
return paramJson = { "thr_burr": Number(values.bv1) } |
||||
|
case 'trend': |
||||
|
return paramJson = { |
||||
|
"thr_burr": Number(values.bv2),//毛刺阈值 |
||||
|
"win_med": Number(values.ws),//滑动中值窗口 |
||||
|
"win_avg": Number(values.rc),//滑动均值窗口 |
||||
|
"win_grad": Number(values.pn),//渐变点个数 |
||||
|
"thr_grad": Number(values.gv),//渐变阈值 |
||||
|
"thr_der": Number(values.dv),//导数阈值 |
||||
|
"days_Last": Number(values.time),//时间跨度 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
const getStationstoSave = () => { |
||||
|
let toSave = [], toSaveName = [], notToSave = [], notToSaveName = [] |
||||
|
let selectIds = sensorId |
||||
|
for (let i = 0; i < selectIds.length; i++) { |
||||
|
let seStaName = sensorList.find(a => a.value == selectIds[i]).label |
||||
|
let cfg |
||||
|
if (methodType == "interrupt") { |
||||
|
cfg = abnParam.find(a => a.abnType == 1 && a.factorId == factorId |
||||
|
&& a.sensorId == selectIds[i]) |
||||
|
} else { |
||||
|
let type = methodType == "burr" ? 2 : 3 |
||||
|
cfg = abnParam.find(a => a.abnType == type && a.factorId == factorId |
||||
|
&& a.sensorId == selectIds[i]) |
||||
|
} |
||||
|
if (!cfg) { |
||||
|
toSave.push(selectIds[i]) |
||||
|
toSaveName.push(seStaName) |
||||
|
} else { |
||||
|
notToSave.push(selectIds[i]) |
||||
|
notToSaveName.push(seStaName) |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
toSave: toSave, toSaveName: toSaveName, |
||||
|
notToSave: notToSave, notToSaveName: notToSaveName |
||||
|
}; |
||||
|
} |
||||
|
//保存配置 |
||||
|
const handleSave = () => { |
||||
|
setMsg('') |
||||
|
form2.current.validate().then(res => { |
||||
|
if (sensorId.length != 0) { |
||||
|
let ids = getStationstoSave() |
||||
|
if (ids.toSave.length != 0) { |
||||
|
let paramJson = getJson(res) |
||||
|
let data = { |
||||
|
sensorId:ids.toSave,//测点 |
||||
|
sensorName: ids.toSaveName, |
||||
|
factorId: factorId, |
||||
|
factorName: factosList.find(a => a.value == factorId).label, |
||||
|
abnType: method[methodType],//算法类型 |
||||
|
enabled: true, |
||||
|
params: paramJson, |
||||
|
itemId: itemId, |
||||
|
|
||||
|
} |
||||
|
let pushData = data.sensorId.map(d => { |
||||
|
return { |
||||
|
sensorId: project+'-'+d,//测点 |
||||
|
sensorName: sensorList.find(a => a.value == d).label, |
||||
|
factorId: factorId, |
||||
|
factorName: factosList.find(a => a.value == factorId).label, |
||||
|
abnType: method[methodType],//算法类型 |
||||
|
enabled: true, |
||||
|
params: paramJson, |
||||
|
itemId: methodType == 'interrupt' ? null : itemId |
||||
|
|
||||
|
} |
||||
|
}) |
||||
|
dispatch(install.addAbnParam(pushData)).then(res => { |
||||
|
if (res.success) { |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId: id })) |
||||
|
} |
||||
|
}) |
||||
|
if (ids.notToSave.length != 0) { |
||||
|
let fact = methodType == 'interrupt' ? "因素" : "子项" |
||||
|
Notification.warning({ |
||||
|
content: `已存在传感器: ${ids.notToSaveName.join(',')} 在当前监测${fact}下的${methodDes[methodType]}参数配置,本次保存的传感器为:${ids.toSaveName.join(',')}`, |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
} else { |
||||
|
let sg = methodType == 'interrupt' ? "因素下的中断" |
||||
|
: methodType == 'burr' ? "子项下的毛刺" : "子项下的异常趋势" |
||||
|
Notification.warning({ |
||||
|
content: `已存在选定测点在当前监测 ${sg} 参数配置,本次无可保存的参数配置`, |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
} else { |
||||
|
Notification.warning({ |
||||
|
content: `请选择测点`, |
||||
|
duration: 3, |
||||
|
}) |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
//数据对比 |
||||
|
const dataCompare = () => { |
||||
|
// this.setState({ msg: '' }); |
||||
|
// this.props.form.validateFields((err, values) => { |
||||
|
// if (!err) { |
||||
|
// if (this.props.stationsValue.length != 0) { |
||||
|
// let paramJson = this.getJson(values); |
||||
|
// let data = { |
||||
|
// station: this.props.stationsValue[0],//第一个测点 |
||||
|
// factorId: this.props.factorId, |
||||
|
// itemId: this.props.itemId, |
||||
|
// abnType: this.props.methodType,//算法类型 |
||||
|
// enabled: true, |
||||
|
// params: paramJson |
||||
|
// }; |
||||
|
// this.props.dataCompara(data); |
||||
|
// } else { |
||||
|
// message.error('请选择测点!'); |
||||
|
// return false; |
||||
|
// } |
||||
|
// } |
||||
|
// }); |
||||
|
} |
||||
|
|
||||
|
//结构物筛选发生变化回调函数 |
||||
|
const reCalcFactorChange = (value) => { |
||||
|
setItemName(factosList?.find(item => item.value === value[0])?.children?.find(p => p.value === value[1])?.label) |
||||
|
setItemId(value[1]) |
||||
|
// form2.current.setValue('point', '') |
||||
|
setFactorId(value[0]) |
||||
|
} |
||||
|
//点位筛选发生改变的回调函数 |
||||
|
const pointChange = (value) => { |
||||
|
setSensorId(value) |
||||
|
// setProject(structList.find(item => item.value == structId)?.project) |
||||
|
} |
||||
|
//监听结构物变化,查询监测因素 |
||||
|
useEffect(() => { |
||||
|
setLoading(true); |
||||
|
let queryParams = { structId, cacl: 1 } |
||||
|
if (structId) { |
||||
|
dispatch(data.getFactors({ ...queryParams })).then(res => { |
||||
|
if (res.success) { |
||||
|
const list = res.payload.data?.map(item => { |
||||
|
const itemList = item.ItemNames && item.ItemNames.split(',') || [] |
||||
|
let child = [] |
||||
|
itemList && itemList.length && itemList.forEach((i, index) => { |
||||
|
child.push({ label: i, value: index }) |
||||
|
}) |
||||
|
return { |
||||
|
label: item.Name, |
||||
|
value: item.FactorID, |
||||
|
children: child |
||||
|
|
||||
|
} |
||||
|
|
||||
|
}) |
||||
|
form2.current.setValue('factor', [list[0]?.value, list[0]?.children[0]?.value]) |
||||
|
setFactorsList(list) |
||||
|
setFactorId(list[0]?.value) |
||||
|
setItemId(list[0]?.children[0]?.value) |
||||
|
setItemName(list[0]?.children[0]?.label) |
||||
|
setLoading(false) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
}, [structId]) |
||||
|
useEffect(() => { |
||||
|
let queryParams = { structId, SafetyFactorTypeId: factorId } |
||||
|
if (factorId && structId) { |
||||
|
dispatch(data.getSensors({ ...queryParams })).then(res => { |
||||
|
if (res.success) { |
||||
|
const list = res.payload.data?.map(item => { |
||||
|
return { |
||||
|
value: item.SensorId, |
||||
|
label: item.SensorLocationDescription, |
||||
|
} |
||||
|
}) |
||||
|
form2.current.setValue('point', [list[0]?.value]) |
||||
|
setSensorId([list[0]?.value]) |
||||
|
setSensorList(list) |
||||
|
setLoading(false); |
||||
|
setProject(res.payload.data[0].Project) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}, [factorId]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
//获取结构物函数 |
||||
|
const getData = (queryParams = { pomsProjectId: pepProjectId }) => { |
||||
|
setLoading(true); |
||||
|
dispatch(data.getProjectAllStructures({ ...queryParams })).then(res => { |
||||
|
if (res.success) { |
||||
|
const uniqueIds = new Set(); |
||||
|
const list = res.payload.data?.filter(item => { |
||||
|
const duplicate = uniqueIds.has(item.strucId); |
||||
|
uniqueIds.add(item.strucId); |
||||
|
return !duplicate |
||||
|
})?.map(item => ({ |
||||
|
value: item.strucId, |
||||
|
label: item.strucName, |
||||
|
project: item.Project, |
||||
|
})) |
||||
|
form.current.setValue('struct', list[0]?.value) |
||||
|
setStructId(list[0]?.value) |
||||
|
setStructList(list) |
||||
|
setLoading(false) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
) |
||||
|
} |
||||
|
const changeMethod = (value) => { |
||||
|
setMethodType(value) |
||||
|
} |
||||
|
const clearFactorAndSensor = () => { |
||||
|
form2.current.setValue('point', '') |
||||
|
form2.current.setValue('factor', '') |
||||
|
} |
||||
|
const structChange = (value) => { |
||||
|
setStructId(value) |
||||
|
clearFactorAndSensor() |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const renderParamsConfig = () => { |
||||
|
switch (methodType) { |
||||
|
case 'interrupt': |
||||
|
return ( |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='iv' placeholder="中断阈值:正整数" label="中断阈值" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur'> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
); |
||||
|
case 'burr': |
||||
|
return ( |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='bv1' placeholder="毛刺阈值:数字" label="Burr阈值" rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} trigger='blur'> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
); |
||||
|
case 'trend': |
||||
|
return ( |
||||
|
<Col span={18}> |
||||
|
<Row gutter={10}> |
||||
|
<Col span={4} > |
||||
|
<Form.Input field='bv2' placeholder="毛刺阈值:数字" label="中断阈值" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur' /> |
||||
|
</Col> |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='ws' placeholder="滑动中值:正值" label="滑动中值" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur' /> |
||||
|
</Col> |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='rc' placeholder="滑动均值:正值" label="滑动均值" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur' /> |
||||
|
</Col> |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='dv' placeholder="导数阈值:数字" label="导数阈值" rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} trigger='blur' /> |
||||
|
</Col> |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='pn' placeholder="渐变点个数:正值" label="渐变点个数" rules={[{ |
||||
|
required: true, message: "请输入大于1的正整数", validator: checkPoint |
||||
|
}]} trigger='blur' /> |
||||
|
</Col> |
||||
|
<Col span={4}> |
||||
|
<Form.Input field='gv' placeholder="渐变阈值:数字" label="渐变阈值" rules={[{ |
||||
|
required: true, message: "请输入数字", validator: checkNumber |
||||
|
}]} trigger='blur' /> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</Col>); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return (<> |
||||
|
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}> |
||||
|
<div style={{ display: 'flex', alignItems: 'center' }}> |
||||
|
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div> |
||||
|
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>数据计算</div> |
||||
|
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>DATA CACL</div> |
||||
|
</div> |
||||
|
<div style={{ margin: '20px 0 16px 0' }}> |
||||
|
<Form labelPosition='left' getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}} style={{ textAlign: 'left', position: "relative", width: "100%", flex: 1 }}> |
||||
|
<Row> |
||||
|
<Col span={12} > |
||||
|
<Form.Select onChange={structChange} optionList={structList} option labelWidth="75px" label="结构物" field="struct" placeholder="请选择结构物" showSearch filter> |
||||
|
</Form.Select> |
||||
|
</Col> |
||||
|
{/* <Col span={12} > |
||||
|
<Form.Select label="监测因素" field="factorId" value={reCalcFactorId} onChange={reCalcFactorChange} |
||||
|
placeholder="请选择监测因素" style={{ width: 160 }} size="large"> |
||||
|
</Form.Select> |
||||
|
</Col> */} |
||||
|
<Col span={12} > |
||||
|
<Form.DatePicker |
||||
|
labelWidth="75px" |
||||
|
field="timeRange" |
||||
|
label="查询时间" |
||||
|
initValue={[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]} |
||||
|
type="dateTimeRange" density="compact" |
||||
|
/> |
||||
|
</Col> |
||||
|
|
||||
|
</Row> |
||||
|
</Form> |
||||
|
<div style={{ marginTop: 15 }}> |
||||
|
<hr style={{ borderTopWidth: 0, marginBottom: 18 }} /> |
||||
|
<Form labelPosition='left' style={{ textAlign: 'left', marginTop: 15, position: "relative", width: "100%" }} |
||||
|
getFormApi={formApi => { form2.current = formApi }}> |
||||
|
<Row gutter={10}> |
||||
|
<Col span={6} > |
||||
|
<Form.Cascader label="监测因素" field="factor" value={reCalcFactorId} onChange={reCalcFactorChange} |
||||
|
treeData={factosList} filter |
||||
|
placeholder="请选择监测因素" style={{ textAlign: 'left', width: 274 }} > |
||||
|
</Form.Cascader> |
||||
|
</Col> |
||||
|
<Col span={6} > |
||||
|
<Form.Select showSearch filter multiple iSelect label="设备" field="point" value={sensorId} onChange={pointChange} |
||||
|
optionList={sensorList} placeholder="请选择设备" style={{ textAlign: 'left', width: 274 }} > |
||||
|
</Form.Select> |
||||
|
</Col> |
||||
|
<Col span={6} > |
||||
|
<Form.Select showSearch filter label="异常识别算法" field="method" onChange={changeMethod} |
||||
|
placeholder="请选择异常识别算法" style={{ textAlign: 'left', width: 127 }} > |
||||
|
{abnMethods?.map(item => { |
||||
|
return <Form.Select.Option value={item.name} label={item.des}></Form.Select.Option>; |
||||
|
})} |
||||
|
</Form.Select> |
||||
|
</Col> |
||||
|
<Col span={3} > |
||||
|
{methodType === 'trend' ? <Form.Select showSearch filter label="分析时长" field="time" |
||||
|
rules={[{ |
||||
|
required: true, message: "请选择分析时长" |
||||
|
}]} |
||||
|
placeholder="请选择分析时长" style={{ width: 127 }} > |
||||
|
<Select.Option value="1" label='1个月'></Select.Option> |
||||
|
<Select.Option value="2" label='2个月'></Select.Option> |
||||
|
<Select.Option value="3" label='3个月'></Select.Option> |
||||
|
</Form.Select> : ''} |
||||
|
</Col> |
||||
|
</Row> |
||||
|
<Row style={{ display: 'flex',alignItems:'center'}}> |
||||
|
{renderParamsConfig()} |
||||
|
<Col span={6}> |
||||
|
{/* <Button style={{ marginRight: 10, marginLeft: 10 }} type="tertiary">数据对比</Button> */} |
||||
|
<Button theme='borderless' type='secondary' style={{ marginRight: 10,marginLeft: 10 }} onClick={handleSave}>保存配置</Button> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</Form> |
||||
|
|
||||
|
<AbnRecognize project={project} sensorId={sensorId} calcMethod={methodType} factorId={factorId} structId={structId} itName={itemName} /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</>) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { auth, global, OrganizationDeps, abnMethods, abnParam } = state; |
||||
|
return { |
||||
|
loading: OrganizationDeps.isRequesting, |
||||
|
user: auth.user, |
||||
|
actions: global.actions, |
||||
|
pepProjectId: global.pepProjectId, |
||||
|
abnMethods: abnMethods?.data || [], |
||||
|
abnParam: abnParam?.data || [] |
||||
|
}; |
||||
|
} |
||||
|
export default connect(mapStateToProps)(Calc); |
||||
|
|
||||
|
|
@ -0,0 +1,266 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import React, { useRef, useState, useEffect } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag, Notification, Form } from '@douyinfe/semi-ui'; |
||||
|
import InterruptModal from '../components/InterruptModal'; |
||||
|
import '../style.less' |
||||
|
|
||||
|
|
||||
|
const Interrupt = (props) => { |
||||
|
const {dispatch, abnParam,structId,factorId,actions,sensorId,project} = props |
||||
|
const form = useRef() |
||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]) |
||||
|
const [filterFunc, setFilterFunc] = useState({}) |
||||
|
const [modalVisible, setModalVisible] = useState(false) |
||||
|
const [ivBatch, setIvBatch] = useState('') |
||||
|
const [modalData, setModalData] = useState(null) |
||||
|
const filterSet = { sensorName: null, factorName: null } |
||||
|
const {install}=actions |
||||
|
const onSelectChange = (selectedRowKeys) => { |
||||
|
setSelectedRowKeys(selectedRowKeys); |
||||
|
} |
||||
|
const compareAndEdit = (e, record) => { |
||||
|
setModalVisible(true) |
||||
|
setModalData(record) |
||||
|
} |
||||
|
const modalCancel = e => { |
||||
|
setModalVisible(false) |
||||
|
} |
||||
|
const removeItem = e => { |
||||
|
dispatch(install.deleteAbnParams(e)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
const handleInputChange = e => { |
||||
|
if (e!= null) { |
||||
|
const value = e |
||||
|
let func = (ep => (s => (s.sensorName).search(ep) > -1))(value); |
||||
|
filterSet.sensorName = func |
||||
|
func = (ep => (s => (s.factorName).search(ep) > -1))(value); |
||||
|
filterSet.factorName = func |
||||
|
} else { |
||||
|
filterSet.sensorName = null |
||||
|
filterSet.factorName = null |
||||
|
} |
||||
|
setFilterFunc(filterSet) |
||||
|
} |
||||
|
//批量启用or禁用 |
||||
|
const onSwitchChange = (e) => { |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(',') |
||||
|
let data = { |
||||
|
enabled: e, |
||||
|
use: 'switch' |
||||
|
} |
||||
|
dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id })) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
//批量删除 |
||||
|
const batchDelete = () => { |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(',') |
||||
|
dispatch(install.deleteAbnParams(ids)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
} |
||||
|
}) |
||||
|
} else { |
||||
|
Notification.warning({ |
||||
|
content: '您尚未勾选任何参数配置!', |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
//批量保存 |
||||
|
const batchSave = () => { |
||||
|
form.current.validate().then(_ => { |
||||
|
let dataSource = abnParam.filter(a => a.abnType == 1 && a.factorId == factorId) |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(','); |
||||
|
let data = { |
||||
|
paramJson: { "thr_int": ivBatch }, |
||||
|
use: 'notSwitch', |
||||
|
} |
||||
|
|
||||
|
dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id })) |
||||
|
} |
||||
|
}) |
||||
|
} else if (dataSource.length != 0) { |
||||
|
Notification.warning({ |
||||
|
content: '您尚未勾选任何参数配置!', |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
const batchInterrupt = (value) => { |
||||
|
setIvBatch(value) |
||||
|
} |
||||
|
const checkInterger = (rule, val) => { |
||||
|
// if(val){ |
||||
|
const strRegex = /^[1-9]*[1-9][0-9]*$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
// } |
||||
|
|
||||
|
}; |
||||
|
let dataSource = abnParam.filter(a => a.abnType == 1 && a.factorId == factorId) |
||||
|
const rowSelection = { |
||||
|
selectedRowKeys: selectedRowKeys, |
||||
|
onChange: onSelectChange, |
||||
|
}; |
||||
|
let filterData = dataSource |
||||
|
let flag = false |
||||
|
let keyArr = [] |
||||
|
let tmpds = [] |
||||
|
let dataPush = [] |
||||
|
Object.keys(filterFunc).forEach(key => { |
||||
|
const filter = filterFunc[key] |
||||
|
filterData = dataSource |
||||
|
if (filter != null) { |
||||
|
flag = true |
||||
|
filterData = filterData.filter(filter) |
||||
|
if (filterData.length > 0) { |
||||
|
filterData.map(s => { |
||||
|
if (keyArr.indexOf(s.id) == -1) { |
||||
|
keyArr.push(s.id) |
||||
|
dataPush.push(s) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id); |
||||
|
let columns = [ |
||||
|
{ |
||||
|
title: "测点位置", |
||||
|
dataIndex: "sensorName", |
||||
|
key: "stationName", |
||||
|
width: '20%', |
||||
|
}, |
||||
|
{ |
||||
|
title: "监测因素", |
||||
|
dataIndex: "factorName", |
||||
|
key: "factorName", |
||||
|
width: '20%', |
||||
|
}, |
||||
|
{ |
||||
|
title: "中断阈值", |
||||
|
dataIndex: "params", |
||||
|
key: "params", |
||||
|
width: '20%', |
||||
|
render: text => text.thr_int |
||||
|
// render: text => { |
||||
|
// return (<Input style={{ width: 121 }} defaultValue={text.thr_int} placeholder="正整数" />) |
||||
|
// } |
||||
|
}, |
||||
|
{ |
||||
|
title: "启用状态", |
||||
|
dataIndex: "enabled", |
||||
|
key: "enabled", |
||||
|
width: '20%', |
||||
|
render: (text, record) => ( |
||||
|
record.enabled ? <Tag color="blue">已启用</Tag> : <Tag>已禁用</Tag> |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
title: "操作", |
||||
|
key: "action", |
||||
|
width: '20%', |
||||
|
render: (text, record) => ( |
||||
|
<span> |
||||
|
<Button theme='borderless' type='primary' onClick={(e) => compareAndEdit(e, record)}>编辑</Button> |
||||
|
<span className="ant-divider"></span> |
||||
|
<Popconfirm title="确认删除该参数配置?" id={record.id} onConfirm={() => { removeItem(record.id) }}> |
||||
|
<Button theme='borderless' type='danger'>删除</Button> |
||||
|
</Popconfirm> |
||||
|
</span> |
||||
|
), |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
return (<Card style={{ marginTop: 15 }}> |
||||
|
<Form style={{ marginBottom: 15 }} |
||||
|
getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}} |
||||
|
labelPosition='left' |
||||
|
> |
||||
|
<Row style={{ display: 'flex',alignItems:'center'}}> |
||||
|
<Col span={5}> |
||||
|
<Form.Input field='keywords' noLabel={true} style={{ marginBottom: 0, width: '95%' }} placeholder="关键词:测点位置、监测项" onChange={handleInputChange}> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Form.Switch label='批量启用' field='switch' style={{ marginBottom: 0 }} checked onChange={onSwitchChange}> |
||||
|
</Form.Switch> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input field='iv' noLabel={true} style={{ marginBottom: 0, width: '93%' }} |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur' |
||||
|
onChange={e => batchInterrupt(e)} placeholder="毛中断阈值:正整数"> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Button style={{ marginBottom: 0 }} onClick={batchSave} type='primary' theme='borderless'>批量保存</Button> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Popconfirm title="确认批量删除选中的参数配置?" onConfirm={batchDelete}> |
||||
|
<Button style={{ fontSize: 13 }} theme='borderless' type='danger'>批量删除</Button> |
||||
|
</Popconfirm> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
</Form> |
||||
|
<Table rowSelection={rowSelection} columns={columns} dataSource={tmpds} /> |
||||
|
{modalVisible ? <InterruptModal |
||||
|
structId={structId} |
||||
|
visible={true} |
||||
|
project={project} |
||||
|
sensorId={sensorId} |
||||
|
closeModal={modalCancel} |
||||
|
modalData={modalData} |
||||
|
/> : ''} |
||||
|
</Card> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { abnParam,global } = state; |
||||
|
// let isRequesting = abnParamState?.isRequesting; |
||||
|
|
||||
|
return { |
||||
|
isRequesting: false,//请求状态 |
||||
|
abnParam: abnParam?.data || [], |
||||
|
actions:global.actions, |
||||
|
} |
||||
|
} |
||||
|
export default connect(mapStateToProps)(Interrupt) |
@ -0,0 +1,425 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import React, { Component, useRef,useState } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag, Notification, Form, Select } from '@douyinfe/semi-ui' |
||||
|
import {IconPlus} from "@douyinfe/semi-icons" |
||||
|
import TrendModal from '../components/TrendModal'; |
||||
|
|
||||
|
|
||||
|
|
||||
|
const Trend = (props) => { |
||||
|
const { abnParam, factorId,itName,actions,dispatch,sensorId,project } = props |
||||
|
const form = useRef() |
||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]) |
||||
|
const [filterFunc, setFilterFunc] = useState({}) |
||||
|
const [modalVisible, setModalVisible] = useState(false) |
||||
|
const [showBatchConfig, setShowBatchConfig] = useState(false) |
||||
|
const [timeRange, setTimeRange] = useState('') |
||||
|
const [bvBatch, setBvBatch] = useState('') |
||||
|
const [winSize, setWinSize] = useState('') |
||||
|
const [reCoef, setReCoef] = useState('') |
||||
|
const [deValue, setDeValue] = useState('') |
||||
|
const [graPoint, setGraPoint] = useState('') |
||||
|
const [graValue, setGraValue] = useState('') |
||||
|
const [modalData, setModalData] = useState(null) |
||||
|
const filterSet = { sensorName: null, factorName: null } |
||||
|
const {install}=actions |
||||
|
const onSelectChange = (selectedRowKeys) => { |
||||
|
setSelectedRowKeys(selectedRowKeys) |
||||
|
} |
||||
|
const compareAndEdit = (e, record) => { |
||||
|
setModalVisible(true) |
||||
|
setModalData(record) |
||||
|
} |
||||
|
const modalCancel = e => { |
||||
|
setModalVisible(false) |
||||
|
}; |
||||
|
const removeItem = e => { |
||||
|
dispatch(install.deleteAbnParams(e)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
const handleInputChange = e => { |
||||
|
if (e != null) { |
||||
|
const value = e |
||||
|
let func = (ep => (s => (s.sensorName).search(ep) > -1))(value) |
||||
|
filterSet.sensorName = func |
||||
|
func = (ep => (s => (s.factorName).search(ep) > -1))(value) |
||||
|
filterSet.factorName = func |
||||
|
} else { |
||||
|
filterSet.sensorName = null |
||||
|
filterSet.factorName = null |
||||
|
} |
||||
|
setFilterFunc(filterSet) |
||||
|
} |
||||
|
//批量启用or禁用 |
||||
|
const onSwitchChange = (e) => { |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(','); |
||||
|
let data = { |
||||
|
enabled: e, |
||||
|
use: 'switch' |
||||
|
}; |
||||
|
//启用和禁用接口 |
||||
|
dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
//批量删除 |
||||
|
const batchDelete = () => { |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(','); |
||||
|
//删除接口 |
||||
|
dispatch(install.deleteAbnParams(ids)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id })) |
||||
|
} |
||||
|
}) |
||||
|
} else { |
||||
|
Notification.warning({ |
||||
|
content: '您尚未勾选任何参数配置!', |
||||
|
duration: 3, |
||||
|
}) } |
||||
|
} |
||||
|
//批量保存 |
||||
|
const batchSave = () => { |
||||
|
form.current.validate().then(value=>{ |
||||
|
let dataSource = abnParam.filter(a => a.abnType == 3 && a.factorId == factorId) |
||||
|
if (selectedRowKeys.length != 0) { |
||||
|
let ids = selectedRowKeys.join(',') |
||||
|
let data = { |
||||
|
paramJson: { |
||||
|
"thr_der": deValue, //导数阈值 |
||||
|
"win_avg": reCoef,//滑动均值 |
||||
|
"win_med": winSize,//滑动中值 |
||||
|
"thr_burr": bvBatch,//毛刺阈值 |
||||
|
"thr_grad": graValue,//渐变阈值 |
||||
|
"win_grad": graPoint,//渐变点个数 |
||||
|
"days_Last": timeRange//分析时长 |
||||
|
}, |
||||
|
use: 'notSwitch', |
||||
|
}; |
||||
|
|
||||
|
dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{ |
||||
|
if(res.success){ |
||||
|
const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',') |
||||
|
dispatch(install.getAbnParamList({ sensorId:id})) |
||||
|
} |
||||
|
}) |
||||
|
} else if (dataSource.length != 0) { |
||||
|
Notification.warning({ |
||||
|
content: '您尚未勾选任何参数配置!', |
||||
|
duration: 3, |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
const iconClick = () => { |
||||
|
if (!showBatchConfig) { |
||||
|
setShowBatchConfig(true); |
||||
|
} else { |
||||
|
setShowBatchConfig(false); |
||||
|
} |
||||
|
} |
||||
|
const rangeChange = (value) => { |
||||
|
setTimeRange(value) |
||||
|
} |
||||
|
const bvValueBatch = (value) => { |
||||
|
setBvBatch(value) |
||||
|
} |
||||
|
const winSizeBatch = (value) => { |
||||
|
setWinSize(value) |
||||
|
} |
||||
|
const reCoefBatch = (value) => { |
||||
|
setReCoef(value) |
||||
|
} |
||||
|
const deValueBatch = (value) => { |
||||
|
setDeValue(value) |
||||
|
} |
||||
|
const graPointBatch = (value) => { |
||||
|
setGraPoint(value) |
||||
|
} |
||||
|
const graValueBatch = (value) => { |
||||
|
setGraValue(value) |
||||
|
} |
||||
|
const checkInterger = (rule, val) => { |
||||
|
// if(val){ |
||||
|
const strRegex = /^[1-9]*[1-9][0-9]*$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
// } |
||||
|
|
||||
|
}; |
||||
|
const checkPoint = (rule, value) => { |
||||
|
const pattern = /^[1-9]*[1-9][0-9]*$/; |
||||
|
if (pattern.test(value) && value != 1) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
const checkNumber = (rule, val) => { |
||||
|
const strRegex = /^-?\d+(\.\d+)?$/ |
||||
|
if (strRegex.test(val)) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
let dataSource = abnParam.filter(a => a.abnType == 3 && a.factorId == factorId) |
||||
|
const rowSelection = { |
||||
|
selectedRowKeys: selectedRowKeys, |
||||
|
onChange: onSelectChange, |
||||
|
}; |
||||
|
let filterData = dataSource; |
||||
|
let flag = false; |
||||
|
let keyArr = []; |
||||
|
let tmpds = []; |
||||
|
let dataPush = []; |
||||
|
Object.keys(filterFunc).forEach(key => { |
||||
|
const filter = filterFunc[key]; |
||||
|
filterData = dataSource; |
||||
|
if (filter != null) { |
||||
|
flag = true; |
||||
|
filterData = filterData.filter(filter) |
||||
|
if (filterData.length > 0) { |
||||
|
filterData.map(s => { |
||||
|
if (keyArr.indexOf(s.id) == -1) { |
||||
|
keyArr.push(s.id); |
||||
|
dataPush.push(s); |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id); |
||||
|
let columns = [ |
||||
|
{ |
||||
|
title: "测点位置", |
||||
|
dataIndex: "sensorName", |
||||
|
key: "sensorName", |
||||
|
width: '9%' |
||||
|
}, |
||||
|
{ |
||||
|
title: "监测项", |
||||
|
dataIndex: "factorName", |
||||
|
key: "factorName", |
||||
|
width: '10%', |
||||
|
render: (text, record) => ( |
||||
|
record.factorName + "/" + itName |
||||
|
), |
||||
|
}, |
||||
|
{ |
||||
|
title: "分析时段", |
||||
|
dataIndex: "params", |
||||
|
key: "params1", |
||||
|
width: '9%', |
||||
|
render: text => text.days_Last + "个月" |
||||
|
}, |
||||
|
{ |
||||
|
title: "毛刺阈值", |
||||
|
dataIndex: "params", |
||||
|
key: "params2", |
||||
|
width: '9%', |
||||
|
render: text => text.thr_burr |
||||
|
}, |
||||
|
{ |
||||
|
title: "窗口数", |
||||
|
dataIndex: "params", |
||||
|
key: "params3", |
||||
|
width: '9%', |
||||
|
render: text => text.win_med |
||||
|
}, |
||||
|
{ |
||||
|
title: "回归系数", |
||||
|
dataIndex: "params", |
||||
|
key: "params4", |
||||
|
width: '9%', |
||||
|
render: text => text.win_avg |
||||
|
}, |
||||
|
{ |
||||
|
title: "导数阈值", |
||||
|
dataIndex: "params", |
||||
|
key: "params5", |
||||
|
width: '9%', |
||||
|
render: text => text.thr_der |
||||
|
}, |
||||
|
{ |
||||
|
title: "渐变点个数", |
||||
|
dataIndex: "params", |
||||
|
key: "params6", |
||||
|
width: '9%', |
||||
|
render: text => text.win_grad |
||||
|
}, |
||||
|
{ |
||||
|
title: "渐变阈值", |
||||
|
dataIndex: "params", |
||||
|
key: "params7", |
||||
|
width: '9%', |
||||
|
render: text => text.thr_grad |
||||
|
}, |
||||
|
{ |
||||
|
title: "启用状态", |
||||
|
dataIndex: "enabled", |
||||
|
key: "enabled", |
||||
|
width: '9%', |
||||
|
render: (text, record) => ( |
||||
|
record.enabled ? <Tag color="blue">已启用</Tag> : <Tag>已禁用</Tag> |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
title: "操作", |
||||
|
key: "action", |
||||
|
width: '9%', |
||||
|
render: (text, record) => ( |
||||
|
<span> |
||||
|
<Button theme='borderless' type='primary' onClick={(e) => compareAndEdit(e, record)}>编辑</Button> |
||||
|
<span className="ant-divider"></span> |
||||
|
<Popconfirm title="确认删除该参数配置?" id={record.id} onConfirm={() => { removeItem(record.id) }}> |
||||
|
<Button theme='borderless' type='danger' >删除</Button> |
||||
|
</Popconfirm> |
||||
|
</span> |
||||
|
), |
||||
|
} |
||||
|
] |
||||
|
return (<Card style={{ marginTop: 15 }}> |
||||
|
<Form style={{ marginBottom: 15 }} labelPosition='left' getFormApi={formApi => { |
||||
|
form.current = formApi |
||||
|
}}> |
||||
|
<Row style={{ display: 'flex',alignItems:'center'}}> |
||||
|
<Col span={5}> |
||||
|
<Form.Input field='keywords' noLabel={true} style={{ marginBottom: 0, width: '95%' }} placeholder="关键词:测点位置、监测因素" onChange={handleInputChange}> |
||||
|
</Form.Input> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Form.Switch label='是否启用' field='switch' style={{ marginBottom: 0 }} checked onChange={onSwitchChange} > |
||||
|
</Form.Switch> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Popconfirm style={{ marginBottom: 0 }} title="确认批量删除选中的参数配置?" onConfirm={batchDelete}> |
||||
|
<Button style={{ fontSize: 13 }} theme='borderless' type='danger'>批量删除</Button> |
||||
|
</Popconfirm> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<IconPlus style={{ paddingTop: 9 }} onClick={iconClick} /> |
||||
|
</Col> |
||||
|
</Row> |
||||
|
{showBatchConfig ? |
||||
|
<Row style={{ display: 'flex',alignItems:'center',marginTop: 10}}> |
||||
|
<Col span={3}> |
||||
|
<Form.Select showSearch filter label="分析时长" field="timeRange" onChange={rangeChange} |
||||
|
placeholder="请选择分析时长" style={{ width: 127, marginBottom: 0 }}> |
||||
|
<Select.Option value="1">1个月</Select.Option> |
||||
|
<Select.Option value="2">2个月</Select.Option> |
||||
|
<Select.Option value="3">3个月</Select.Option> |
||||
|
</Form.Select> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input style={{ marginBottom: 0, width: '93%' }} |
||||
|
field='bv' |
||||
|
placeholder="毛刺阈值:数字" label="毛刺阈值" |
||||
|
onChange={e => bvValueBatch(e)} |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} trigger='blur'/> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input |
||||
|
style={{ marginBottom: 0, width: '93%' }} |
||||
|
field='ws' |
||||
|
placeholder="滑动中值:正值" |
||||
|
label="滑动中值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} |
||||
|
onChange={e => winSizeBatch(e)} |
||||
|
trigger='blur'/> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input |
||||
|
style={{ marginBottom: 0, width: '93%' }} |
||||
|
field='rc' |
||||
|
placeholder="滑动均值:正值" |
||||
|
label="滑动均值" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkInterger |
||||
|
}]} |
||||
|
onChange={e => reCoefBatch(e)} |
||||
|
trigger='blur'/> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input |
||||
|
style={{ marginBottom: 0, width: '93%' }} |
||||
|
field='dv' |
||||
|
placeholder="导数阈值:数字" |
||||
|
label="导数阈值" |
||||
|
rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkNumber |
||||
|
}]} |
||||
|
onChange={e => deValueBatch(e)} |
||||
|
trigger='blur'/> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input |
||||
|
style={{ marginBottom: 0, width: '93%' }} |
||||
|
field='pn' |
||||
|
placeholder="渐变点个数:正值" |
||||
|
label="渐变点个数" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkPoint |
||||
|
}]} |
||||
|
onChange={e => graPointBatch(e)} |
||||
|
trigger='blur'/> |
||||
|
</Col> |
||||
|
<Col span={3}> |
||||
|
<Form.Input |
||||
|
style={{ marginBottom: 0, width: '93%' }} |
||||
|
field='gv' |
||||
|
placeholder="渐变阈值:数字" |
||||
|
label="渐变阈值" rules={[{ |
||||
|
required: true, message: "请输入正整数", validator: checkNumber |
||||
|
}]} |
||||
|
onChange={e => graValueBatch(e)} |
||||
|
trigger='blur'/> |
||||
|
</Col> |
||||
|
<Col span={2}> |
||||
|
<Button onClick={batchSave} theme='solid' type='primary'>批量保存</Button> |
||||
|
</Col> |
||||
|
</Row> : ''} |
||||
|
</Form> |
||||
|
<Table rowSelection={rowSelection} columns={columns} dataSource={tmpds} /> |
||||
|
{modalVisible ? <TrendModal |
||||
|
// structId={structId} |
||||
|
visible={true} |
||||
|
project={ project} |
||||
|
closeModal={modalCancel} |
||||
|
modalData={modalData} |
||||
|
sensorId={sensorId} |
||||
|
/> : ''} |
||||
|
</Card> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { abnParam,global } = state |
||||
|
// let isRequesting = abnParamState?.isRequesting; |
||||
|
|
||||
|
return { |
||||
|
isRequesting: false,//请求状态 |
||||
|
abnParam: abnParam?.data||[], |
||||
|
actions:global.actions |
||||
|
} |
||||
|
} |
||||
|
export default connect(mapStateToProps)(Trend) |
||||
|
|
Loading…
Reference in new issue