Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
ad37ce4c66
@ -175,10 +175,9 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
if (sysIotModelFieldDto.getAttributeType() == 140) {
|
||||
sysIotModelField.setDataType("tinyint");
|
||||
}
|
||||
|
||||
sysIotModelFieldMapper.insert(sysIotModelField);
|
||||
//动态执行创建td超级表,字段
|
||||
createTdStableOrColumn(sysIotModelField);
|
||||
sysIotModelFieldMapper.insert(sysIotModelField);
|
||||
//新增物模型属性缓存
|
||||
addModelFieldCache(sysIotModelField);
|
||||
SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo();
|
||||
@ -204,12 +203,11 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
throw new ServiceException("物模型属性code已经存在");
|
||||
}
|
||||
}
|
||||
sysIotModelFieldMapper.updateById(sysIotModelField);
|
||||
|
||||
SysIotModelField oldSysIotField = sysIotModelFieldMapper.selectById(sysIotModelFieldDto.getId());
|
||||
if (oldSysIotField == null) {
|
||||
throw new ServiceException("未查找到该条记录");
|
||||
}
|
||||
sysIotModelFieldMapper.updateById(sysIotModelField);
|
||||
if (!oldSysIotField.getAttributeCode().equals(sysIotModelField.getAttributeCode()) || !oldSysIotField.getDataType().equals(sysIotModelField.getDataType()) || Objects.equals(oldSysIotField.getHighSpeed(), sysIotModelField.getHighSpeed())) {
|
||||
//更新td表结构
|
||||
updateTDStableOrColumn(sysIotModelField, oldSysIotField);
|
||||
@ -223,8 +221,9 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
@Override
|
||||
public void deleteSysIotModelField(SysIotModelFieldDto sysIotModelFieldDto) {
|
||||
SysIotModelField sysIotModelField = sysIotModelFieldMapper.selectById(sysIotModelFieldDto.getId());
|
||||
deleteTDStableOrColumn(sysIotModelField);
|
||||
sysIotModelFieldMapper.deleteById(sysIotModelFieldDto.getId());
|
||||
//删除td表结构字段
|
||||
deleteTDStableOrColumn(sysIotModelField);
|
||||
//删除物模型属性缓存
|
||||
deleteModelFieldCache(sysIotModelField);
|
||||
}
|
||||
@ -437,9 +436,10 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(sysIotModelFieldList)) {
|
||||
//创建td表结构
|
||||
//分为高频低频两个list创建
|
||||
List<SysIotModelField> lowCreateList = sysIotModelFieldList.stream().filter(item -> item.getHighSpeed() == 0).collect(Collectors.toList());
|
||||
List<SysIotModelField> highCreateList = sysIotModelFieldList.stream().filter(item -> item.getHighSpeed() == 1).collect(Collectors.toList());
|
||||
//分为高频,低频,计算量三个list创建
|
||||
List<SysIotModelField> lowCreateList = sysIotModelFieldList.stream().filter(item -> item.getHighSpeed() == 0 && item.getAttributeType() != 199).collect(Collectors.toList());
|
||||
List<SysIotModelField> highCreateList = sysIotModelFieldList.stream().filter(item -> item.getHighSpeed() == 1 && item.getAttributeType() != 199).collect(Collectors.toList());
|
||||
List<SysIotModelField> calCreateList = sysIotModelFieldList.stream().filter(item -> item.getAttributeType() == 199).collect(Collectors.toList());
|
||||
for (int i = 0; i < lowCreateList.size(); i++) {
|
||||
if (i == 0) {
|
||||
createTdStableOrColumn(lowCreateList.get(i));
|
||||
@ -452,7 +452,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
//创建低频超级表
|
||||
tdEngineService.addStableColumn(sysIotModel.getIotModelCode(), "l_", map);
|
||||
|
||||
addModelFieldCache(lowCreateList.get(i));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < highCreateList.size(); i++) {
|
||||
@ -467,8 +467,12 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
//创建高频超级表
|
||||
tdEngineService.addStableColumn(sysIotModel.getIotModelCode(), "h_", map);
|
||||
addModelFieldCache(highCreateList.get(i));
|
||||
}
|
||||
|
||||
}
|
||||
for (SysIotModelField item : calCreateList){
|
||||
createTdStableOrColumn(item);
|
||||
addModelFieldCache(item);
|
||||
}
|
||||
//新增pg数据库
|
||||
sysIotModelFieldMapper.insertBatch(sysIotModelFieldList);
|
||||
@ -524,6 +528,12 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
}
|
||||
|
||||
public void createTdStableOrColumn(SysIotModelField sysIotModelField) {
|
||||
//如果type是计算量199,创建单独计算量超级表
|
||||
if (sysIotModelField.getAttributeType() == 199) {
|
||||
String modelCode = dataService.iotModelMap.get(sysIotModelField.getIotModelId().toString());
|
||||
tdEngineService.createCalStable(modelCode, sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
} else {
|
||||
//创建type为138 139 140的超级表
|
||||
//如果新增的是第一条记录 创建tdengine超级表 分为高频和低频
|
||||
QueryWrapper<SysIotModelField> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("iot_model_id", sysIotModelField.getIotModelId());
|
||||
@ -562,6 +572,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTDStableOrColumn(SysIotModelField sysIotModelField, SysIotModelField oldIotModelField) {
|
||||
deleteTDStableOrColumn(oldIotModelField);
|
||||
@ -569,6 +580,11 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
}
|
||||
|
||||
public void deleteTDStableOrColumn(SysIotModelField sysIotModelField) {
|
||||
if (sysIotModelField.getAttributeType() == 199) {
|
||||
Long iotModelId = sysIotModelField.getIotModelId();
|
||||
String modelCode = dataService.iotModelMap.get(iotModelId.toString());
|
||||
tdEngineService.deleteStable("c_" + modelCode +"_"+ sysIotModelField.getAttributeCode());
|
||||
} else {
|
||||
String stableName = null;
|
||||
SysIotModel sysIotModel = sysIotModelMapper.selectById(sysIotModelField.getIotModelId());
|
||||
if (sysIotModelField.getHighSpeed() == 0) {
|
||||
@ -590,18 +606,29 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//新增物模型属性,属性dataType缓存
|
||||
private void addModelFieldCache(SysIotModelField sysIotModelField) {
|
||||
//获取物模型编码
|
||||
String modelCode = dataService.iotModelMap.get(sysIotModelField.getIotModelId().toString());
|
||||
if (sysIotModelField.getAttributeType() == 199) {
|
||||
Map<String, String> map = dataService.calculateIotFieldMap.get(modelCode);
|
||||
if (map == null) {
|
||||
Map<String, String> calMap = new HashMap<>();
|
||||
calMap.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
dataService.calculateIotFieldMap.put(modelCode, calMap);
|
||||
} else {
|
||||
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
}
|
||||
} else {
|
||||
if (sysIotModelField.getHighSpeed() == 0) {
|
||||
Map<String, Object> map = dataService.lowIotFieldMap.get(modelCode);
|
||||
if (map == null) {
|
||||
Map<String, Object> cacheFieldMap = new HashMap<>();
|
||||
cacheFieldMap.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
dataService.lowIotFieldMap.put(modelCode, cacheFieldMap);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
|
||||
}
|
||||
}
|
||||
@ -617,6 +644,8 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//更新物模型属性,属性dataType缓存
|
||||
private void updateModelFieldCache(SysIotModelField newSysIotModelField, SysIotModelField oldSysIotModelField) {
|
||||
//删除旧缓存
|
||||
@ -625,10 +654,14 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
addModelFieldCache(newSysIotModelField);
|
||||
}
|
||||
|
||||
//新增物模型属性,属性dataType缓存
|
||||
//删除物模型属性,删除属性缓存
|
||||
private void deleteModelFieldCache(SysIotModelField sysIotModelField) {
|
||||
//获取物模型编码
|
||||
String modelCode = dataService.iotModelMap.get(sysIotModelField.getIotModelId().toString());
|
||||
if (sysIotModelField.getAttributeType() == 199){
|
||||
Map<String, String> map = dataService.calculateIotFieldMap.get(modelCode);
|
||||
map.remove(sysIotModelField.getAttributeCode());
|
||||
}else {
|
||||
if (sysIotModelField.getHighSpeed() == 0) {
|
||||
Map<String, Object> map = dataService.lowIotFieldMap.get(modelCode);
|
||||
map.remove(sysIotModelField.getAttributeCode());
|
||||
@ -638,6 +671,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
|
||||
map.remove(sysIotModelField.getAttributeCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addModelCache(SysIotModel sysIotModel) {
|
||||
dataService.iotModelMap.put(sysIotModel.getId().toString(), sysIotModel.getIotModelCode());
|
||||
|
@ -91,6 +91,27 @@ public class TDEngineService {
|
||||
}
|
||||
}
|
||||
|
||||
public void createCalStable(String iotmodel, String iotModelField,String dataType) {
|
||||
try (Connection conn = hikariDataSource.getConnection();
|
||||
Statement pstmt = conn.createStatement()) {
|
||||
StringBuilder sb = new StringBuilder(1024 * 1024);
|
||||
sb.setLength(0);
|
||||
sb.append("CREATE STABLE IF NOT EXISTS ");
|
||||
sb.append("c_").append(iotmodel).append("_").append(iotModelField);
|
||||
sb.append(" (`updatetime` TIMESTAMP, datavalue ");
|
||||
sb.append(dataType);
|
||||
sb.append(") TAGS (`deviceid` BIGINT);");
|
||||
try {
|
||||
log.info(sb.toString());
|
||||
pstmt.executeUpdate(sb.toString());
|
||||
} catch (Exception e) {
|
||||
log.error("创建超级表失败,失败原因{}", e);
|
||||
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增超级表列
|
||||
*/
|
||||
@ -170,14 +191,11 @@ public class TDEngineService {
|
||||
|
||||
|
||||
// 遍历所有的物模型存入内存中
|
||||
public void initIotModel(List<IotModelFieldVo> allIotModel, ConcurrentHashMap<String, Map<String, Object>> highIotFieldMap, ConcurrentHashMap<String, Map<String, Object>> lowIotFieldMap) {
|
||||
public void initIotModel(List<IotModelFieldVo> allIotModel, ConcurrentHashMap<String, Map<String, Object>> highIotFieldMap, ConcurrentHashMap<String, Map<String, Object>> lowIotFieldMap, ConcurrentHashMap<String, Map<String, String>> calculateIotFieldMap) {
|
||||
// 创建物模型超级表
|
||||
|
||||
try (Connection conn = hikariDataSource.getConnection();
|
||||
Statement pstmt = conn.createStatement()) {
|
||||
|
||||
ListUtil.page(allIotModel, batchSize, list -> {
|
||||
|
||||
for (IotModelFieldVo info : list) {
|
||||
StringBuilder sb = new StringBuilder(1024 * 1024);
|
||||
sb.setLength(0);
|
||||
@ -187,7 +205,6 @@ public class TDEngineService {
|
||||
sb.append("CREATE STABLE IF NOT EXISTS ");
|
||||
sb.append("h_").append(info.getIotModelCode());
|
||||
sb.append(" (`updatetime` TIMESTAMP");
|
||||
|
||||
// 使用增强的 for 循环遍历键
|
||||
for (String key : map.keySet()) {
|
||||
sb.append(", ");
|
||||
@ -202,7 +219,6 @@ public class TDEngineService {
|
||||
log.error("save yx error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (IotModelFieldVo info : list) {
|
||||
StringBuilder sb = new StringBuilder(1024 * 1024);
|
||||
@ -227,6 +243,16 @@ public class TDEngineService {
|
||||
}
|
||||
}
|
||||
}
|
||||
//初始化创建计算量td超级表
|
||||
for (IotModelFieldVo info : list) {
|
||||
String iotModelCode = info.getIotModelCode();
|
||||
Map<String, String> calFieldMap = calculateIotFieldMap.get(iotModelCode);
|
||||
if (calFieldMap.keySet().size() != 0){
|
||||
for (String key : calFieldMap.keySet()){
|
||||
createCalStable(iotModelCode,key,calFieldMap.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (SQLException ex) {
|
||||
log.error(ex.getMessage());
|
||||
|
@ -71,6 +71,8 @@ public class DataServiceImpl implements DataService {
|
||||
|
||||
public ConcurrentHashMap<String, Map<String, Object>> lowIotFieldMap = new ConcurrentHashMap<>(10000);
|
||||
|
||||
public ConcurrentHashMap<String, Map<String, String>> calculateIotFieldMap = new ConcurrentHashMap<>(10000);
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
//初始化高性能队列
|
||||
@ -218,8 +220,9 @@ public class DataServiceImpl implements DataService {
|
||||
String key = String.valueOf(item.getId());
|
||||
iotModelMap.put(key, item.getIotModelCode());
|
||||
List<SysIotModelField> allIotModelField = sysIotModelMapper.getAllIotModelField(item.getId());
|
||||
Map<String,String> LowModelFieldList = allIotModelField.stream().filter(field -> field.getHighSpeed() == 0).collect(Collectors.toMap(SysIotModelField::getAttributeCode, SysIotModelField::getDataType, (value1, value2) -> value1));
|
||||
Map<String,String> HighModelFieldList = allIotModelField.stream().filter(field -> field.getHighSpeed() == 1).collect(Collectors.toMap(SysIotModelField::getAttributeCode, SysIotModelField::getDataType, (value1, value2) -> value1));
|
||||
Map<String,String> LowModelFieldList = allIotModelField.stream().filter(field -> field.getHighSpeed() == 0 && field.getAttributeType() != 199).collect(Collectors.toMap(SysIotModelField::getAttributeCode, SysIotModelField::getDataType, (value1, value2) -> value1));
|
||||
Map<String,String> HighModelFieldList = allIotModelField.stream().filter(field -> field.getHighSpeed() == 1 && field.getAttributeType() != 199).collect(Collectors.toMap(SysIotModelField::getAttributeCode, SysIotModelField::getDataType, (value1, value2) -> value1));
|
||||
Map<String, String> calculateFieldList = allIotModelField.stream().filter(field -> field.getAttributeType() == 199).collect(Collectors.toMap(SysIotModelField::getAttributeCode, SysIotModelField::getDataType, (value1, value2) -> value1));
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (String field : HighModelFieldList.keySet()) {
|
||||
map.put(field, HighModelFieldList.get(field));
|
||||
@ -230,8 +233,9 @@ public class DataServiceImpl implements DataService {
|
||||
lowMap.put(field, LowModelFieldList.get(field));
|
||||
}
|
||||
lowIotFieldMap.put(item.getIotModelCode(), lowMap);
|
||||
calculateIotFieldMap.put(item.getIotModelCode(),calculateFieldList);
|
||||
}
|
||||
tdEngineService.initIotModel(allIotModel, highIotFieldMap, lowIotFieldMap);
|
||||
tdEngineService.initIotModel(allIotModel, highIotFieldMap, lowIotFieldMap,calculateIotFieldMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,23 +49,19 @@ public class HomeServiceImpl implements HomeService {
|
||||
List<String> attributesList = new ArrayList<>();
|
||||
//风速
|
||||
attributesList.add("iwindspeed");
|
||||
|
||||
//风机状态判断条件所需的字段iturbineoperationmode、iYPLevel、GridLostDetected(可能会调整)
|
||||
//风机状态
|
||||
attributesList.add("iturbineoperationmode");
|
||||
//偏航运行模式
|
||||
attributesList.add("iYPLevel");
|
||||
attributesList.add("iyplevel");
|
||||
//风机电网掉电
|
||||
attributesList.add("GridLostDetected");
|
||||
|
||||
attributesList.add("gridlostdetected");
|
||||
//刹车等级
|
||||
attributesList.add("ibplevel");
|
||||
//有功功率(MW)
|
||||
attributesList.add("igenpower");
|
||||
//当天可利用率
|
||||
//attributesList.add("iavailabillitytoday");
|
||||
//日发电量(kwh)
|
||||
attributesList.add("ikwhthisday");
|
||||
//总发电量(万kwh)
|
||||
//attributesList.add("ikwhoverall");
|
||||
for (SysEquipmentVo item : sysEquipmentVos) {
|
||||
//构建查询属性参数
|
||||
SnapshotValueQueryParam snapshotValueQueryParam = new SnapshotValueQueryParam();
|
||||
|
@ -78,7 +78,7 @@
|
||||
where se.id = #{id}
|
||||
</select>
|
||||
<select id="getAllIotModelField" resultType="com.das.modules.equipment.entity.SysIotModelField">
|
||||
select simf.attribute_code as attributeCode,simf.highspeed as highSpeed,simf.datatype as dataType from sys_iot_model_field simf where simf.iot_model_id = #{id} order by simf.attribute_code
|
||||
select simf.attribute_code as attributeCode,simf.highspeed as highSpeed,simf.datatype as dataType,simf.attribute_type as attributeType from sys_iot_model_field simf where simf.iot_model_id = #{id} order by simf.attribute_code
|
||||
</select>
|
||||
|
||||
|
||||
|
@ -144,7 +144,7 @@
|
||||
<div class="Parameters-item">
|
||||
<img src="~assets/WindBlower/pitch.png" />
|
||||
<p class="Parameters-font">{{ realTimeDataForSingle.ipitchangle }}</p>
|
||||
<p>变桨角度</p>
|
||||
<p>变桨角度(°)</p>
|
||||
</div>
|
||||
<div class="Parameters-item">
|
||||
<img src="~assets/WindBlower/speed.png" />
|
||||
@ -153,8 +153,8 @@
|
||||
</div>
|
||||
<div class="Parameters-item">
|
||||
<img src="~assets/WindBlower/direction.png" />
|
||||
<p class="Parameters-font">98</p>
|
||||
<p>风向</p>
|
||||
<p class="Parameters-font">{{ realTimeDataForSingle.iwinddirection }}</p>
|
||||
<p>风向(°)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -248,10 +248,8 @@
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="24">
|
||||
<el-tabs v-model="trendChartType" class="demo-tabs trend-tabs" @tab-click="tabhandleClick" ref="userTab">
|
||||
<el-tab-pane label="日" name="day">
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="月" name="month">
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="日" name="day"> </el-tab-pane>
|
||||
<el-tab-pane label="月" name="month"> </el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="trend-chart" ref="trendChartRef"></div>
|
||||
</el-col>
|
||||
@ -315,7 +313,7 @@ import { useRoute } from 'vue-router'
|
||||
import Overview from './overview.vue'
|
||||
import { TableInstance } from 'element-plus'
|
||||
import { dayjs } from 'element-plus'
|
||||
import { getRealTimeState } from '/@/views/backend/equipment/airBlower/utils'
|
||||
import { getRealTimeState, getCutDecimalsValue } from '/@/views/backend/equipment/airBlower/utils'
|
||||
const route = useRoute()
|
||||
|
||||
const d = new Date()
|
||||
@ -349,6 +347,7 @@ const realTimeDataForSingle = ref<any>({
|
||||
igenspeed: '',
|
||||
ipitchangle: '',
|
||||
iwindspeed: '',
|
||||
iwinddirection: '',
|
||||
})
|
||||
|
||||
const state: {
|
||||
@ -568,7 +567,7 @@ const TrendDataForMonth = [
|
||||
]
|
||||
const trendChartRef = ref<VNodeRef>()
|
||||
|
||||
const inittrendChart = (type: 'day' | 'month' = 'day') => {
|
||||
const inittrendChart = (type: 'day' | 'month') => {
|
||||
const currentPeriod: any =
|
||||
type === 'day' ? TrendDataForDay.map((item) => item.currentPeriod) : TrendDataForMonth.map((item) => item.currentPeriod)
|
||||
const samePeriod: any = type === 'day' ? TrendDataForDay.map((item) => item.samePeriod) : TrendDataForMonth.map((item) => item.samePeriod)
|
||||
@ -677,7 +676,7 @@ const inittrendChart = (type: 'day' | 'month' = 'day') => {
|
||||
trendChart.setOption(option)
|
||||
state.charts.trendChart = trendChart
|
||||
}
|
||||
const chartsData = [
|
||||
const temperatureChartsData: { name: string; value: string | number }[] = [
|
||||
{
|
||||
name: '环境温度(°C)',
|
||||
value: 56,
|
||||
@ -699,7 +698,7 @@ const initTemperatureChart = () => {
|
||||
const temperatureChart2 = state.charts.temperatureChart2 ?? echarts.init(temperatureChartRef2.value as unknown as HTMLElement)
|
||||
const temperatureChart3 = state.charts.temperatureChart3 ?? echarts.init(temperatureChartRef3.value as unknown as HTMLElement)
|
||||
const options = []
|
||||
for (let i = 0; i < chartsData.length; i++) {
|
||||
for (let i = 0; i < temperatureChartsData.length; i++) {
|
||||
const option = {
|
||||
grid: {
|
||||
top: 30,
|
||||
@ -718,13 +717,13 @@ const initTemperatureChart = () => {
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: chartsData[i].name,
|
||||
data: [chartsData[i].value],
|
||||
name: temperatureChartsData[i].name,
|
||||
data: [temperatureChartsData[i].value],
|
||||
type: 'bar',
|
||||
label: {
|
||||
show: true,
|
||||
align: 'center',
|
||||
formatter: ['{a|{c}}', '{b|{a}}'].join('\n'),
|
||||
formatter: `{a|{c}}\n{b|{a}}`,
|
||||
rich: {
|
||||
a: {
|
||||
color: '#333333',
|
||||
@ -735,7 +734,8 @@ const initTemperatureChart = () => {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
position: ['350%', '50%'],
|
||||
position: 'insideBottom',
|
||||
offset: [62, 0],
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#048bd2',
|
||||
@ -1043,6 +1043,10 @@ const createRealTimeData = async () => {
|
||||
const realData: any = await getRealTimeData()
|
||||
realTimeData.value.iturbineoperationmode = getRealTimeState(realData)
|
||||
|
||||
temperatureChartsData[0].value = getCutDecimalsValue(realData.itempoutdoor_1sec)
|
||||
temperatureChartsData[1].value = getCutDecimalsValue(realData.itempnacelle_1sec)
|
||||
temperatureChartsData[2].value = getCutDecimalsValue(realData.itempoutdoor_1sec)
|
||||
|
||||
const overviewDatakeys: any = Object.keys(overviewData)
|
||||
const sigleDataKeys: any = Object.keys(realTimeDataForSingle.value)
|
||||
|
||||
@ -1058,7 +1062,7 @@ const createRealTimeData = async () => {
|
||||
]
|
||||
modelList.forEach((item: any) => {
|
||||
const realVal = realData[item.attributeCode.toLowerCase()]
|
||||
const val = realVal === 0 ? 0 : realVal ? (realVal % 1 === 0 ? realVal : Math.floor(realVal * 1000) / 1000) : '-'
|
||||
const val = getCutDecimalsValue(realVal)
|
||||
if (sigleDataKeys.includes(item.attributeCode.toLowerCase())) {
|
||||
realTimeDataForSingle.value[item.attributeCode.toLowerCase()] = val === '-' ? val : val
|
||||
}
|
||||
@ -1211,6 +1215,7 @@ const autoUpdate = () => {
|
||||
if (!autoUpdateTimer) {
|
||||
autoUpdateTimer = setInterval(() => {
|
||||
createRealTimeData()
|
||||
initTemperatureChart()
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
@ -1239,7 +1244,7 @@ const getChartData = () => {
|
||||
times[key] = []
|
||||
val[key] = []
|
||||
data[key].times.forEach((item: number, index: number) => {
|
||||
times[key].push(dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
|
||||
times[key].push(dayjs(item).format('HH:mm'))
|
||||
})
|
||||
data[key].values.forEach((item: number) => {
|
||||
val[key].push(item)
|
||||
@ -1254,12 +1259,12 @@ const getChartData = () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
inittrendChart()
|
||||
inittrendChart(trendChartType.value)
|
||||
getChartData().then(() => {
|
||||
initpowerChart()
|
||||
initTemperatureChart()
|
||||
})
|
||||
createScroll()
|
||||
initTemperatureChart()
|
||||
initFrequencyChart()
|
||||
useEventListener(window, 'resize', echartsResize)
|
||||
autoUpdate()
|
||||
|
@ -53,7 +53,7 @@
|
||||
:data="tableData"
|
||||
:header-row-style="tableHaderStyle"
|
||||
@selectionChange="selectTable"
|
||||
height="100%"
|
||||
max-height="100%"
|
||||
>
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<template v-for="item in tableColumn" :key="item.prop">
|
||||
|
@ -15,3 +15,8 @@ export const getRealTimeState = (data: any) => {
|
||||
return 1112
|
||||
}
|
||||
}
|
||||
|
||||
export const getCutDecimalsValue = (data: number, num = 3) => {
|
||||
const n = Math.pow(10, num)
|
||||
return data === 0 ? 0 : data ? (data % 1 === 0 ? data : Math.floor(data * n) / n) : '-'
|
||||
}
|
436
ui/dasadmin/src/views/backend/report/index.vue
Normal file
436
ui/dasadmin/src/views/backend/report/index.vue
Normal file
@ -0,0 +1,436 @@
|
||||
<template>
|
||||
<div class="report">
|
||||
<el-container class="mainContainer">
|
||||
<el-tabs v-model="activeIndex" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="运行报表" name="1" class="runningReport">
|
||||
<!-- <div class="header">
|
||||
<el-space>
|
||||
<div>时间</div>
|
||||
<el-date-picker style="height: 40px" v-model="dateValue" type="date" placeholder="选择日期时间" />
|
||||
<el-button :icon="Search" type="primary">查询</el-button>
|
||||
</el-space>
|
||||
</div> -->
|
||||
运行报表
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="单机报表" name="2" class="singleReport">
|
||||
<div class="topForm">
|
||||
<div class="forms">
|
||||
<el-space>
|
||||
<div style="min-width: 30px">{{ t('statAnalysis.deviceId') }}</div>
|
||||
<el-select
|
||||
v-model="windBlowerValue"
|
||||
@change="selectWindBlower"
|
||||
:placeholder="'请选择' + t('statAnalysis.deviceId')"
|
||||
class="windBlowerSelect commonSelect"
|
||||
>
|
||||
<el-option v-for="v in windBlowerList" :key="v.irn" :label="v.name" :value="v.irn"></el-option>
|
||||
</el-select>
|
||||
<div style="width: 20px"></div>
|
||||
<div style="min-width: 30px">时间</div>
|
||||
<el-date-picker
|
||||
style="height: 40px"
|
||||
v-model="timeRange"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
range-separator="-"
|
||||
:start-placeholder="t('alarm.selectDate')"
|
||||
:end-placeholder="t('alarm.selectDate')"
|
||||
:shortcuts="shortcuts"
|
||||
/>
|
||||
<div style="width: 20px"></div>
|
||||
<div style="min-width: 30px">{{ t('statAnalysis.interval') }}</div>
|
||||
<el-select v-model="interval" :placeholder="'请选择' + t('statAnalysis.interval')" class="commonSelect">
|
||||
<el-option v-for="v in intervals" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
||||
</el-select>
|
||||
</el-space>
|
||||
<div>
|
||||
<el-space style="margin-top: 10px">
|
||||
<div>模板</div>
|
||||
<el-select v-model="template" placeholder="请选择模板" class="commonSelect">
|
||||
<el-option v-for="v in templateList" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
||||
</el-select>
|
||||
<div style="width: 20px"></div>
|
||||
<!-- <div>{{ t('statAnalysis.attributes') }}</div>
|
||||
<el-input
|
||||
style="width: 200px; height: 40px"
|
||||
v-model="currentChoose"
|
||||
:placeholder="'请选择' + t('statAnalysis.attributes')"
|
||||
></el-input>
|
||||
<el-button type="primary" size="small" :icon="Plus" circle @click="chooseMeasurePoint"> </el-button> -->
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<el-button class="button" :icon="Search" @click="queryHistoryData" type="primary">查询</el-button>
|
||||
<el-button class="button" :icon="Upload" type="primary" plain>导出</el-button>
|
||||
<el-button class="button" :icon="Notebook" type="primary" plain>保存为模板</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
class="mainReport"
|
||||
:columns="reportTableColumn"
|
||||
:data="reportTableData"
|
||||
:row-key="(row) => row.id"
|
||||
:row-style="tableRowClassName"
|
||||
v-loading="reportLoading"
|
||||
>
|
||||
<el-table-column prop="time" label="时间" fixed width="120px"> </el-table-column>
|
||||
<el-table-column v-for="item in reportTableColumn" :key="item.prop" :label="item.label" :prop="item.prop">
|
||||
<template #header="scope">
|
||||
<el-space>
|
||||
{{ item.label }}
|
||||
<el-popconfirm v-if="item.label" title="确定删除?" @confirm="removeColumn(scope)">
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small">-</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template #header="scope">
|
||||
<el-button type="primary" size="small" :icon="Plus" @click="chooseMeasurePoint"> </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="多机报表" name="3">多机报表</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-container>
|
||||
<el-dialog v-model="showMeasure" title="测点名称" :width="800" :destroy-on-close="true">
|
||||
<template #header>
|
||||
<div class="measureSlotHeader">
|
||||
<span style="font-size: 20px">测点名称</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="measureSlot">
|
||||
<Measurement ref="measureRef" :show="showMeasure" :iotModelId="iotModelId" @handleSelections="handleSelections"></Measurement>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showMeasure = false">取消</el-button>
|
||||
<el-button type="primary" @click="selectMeasurePoint"> 确认 </el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, nextTick, onMounted } from 'vue'
|
||||
import { ElMessage, TableInstance, TreeInstance } from 'element-plus'
|
||||
import { Search, Upload, Notebook, Plus } from '@element-plus/icons-vue'
|
||||
import { WindBlowerList } from './type'
|
||||
import { queryWindTurbinesPages, historyReq } from '/@/api/backend/statAnalysis/request'
|
||||
import Measurement from './measureList.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
import { useAdminInfo } from '/@/stores/adminInfo'
|
||||
const adminInfo = useAdminInfo()
|
||||
const shortcuts = [
|
||||
{
|
||||
text: '今天',
|
||||
value: () => {
|
||||
const start = getFormattedDate(0) + ' 00:00:00'
|
||||
const end = new Date()
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '昨天',
|
||||
value: () => {
|
||||
const start = getFormattedDate(-1) + ' 00:00:00'
|
||||
const end = getFormattedDate(-1) + ' 23:59:59'
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '前3天',
|
||||
value: () => {
|
||||
const start = getFormattedDate(-3) + ' 00:00:00'
|
||||
const end = getFormattedDate(-1) + ' 23:59:59'
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '本周',
|
||||
value: () => {
|
||||
return getDateRange('week')
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '本月',
|
||||
value: () => {
|
||||
return getDateRange('month')
|
||||
},
|
||||
},
|
||||
]
|
||||
const tableRowClassName = ({ rowIndex }) => {
|
||||
if (rowIndex % 2 === 1) {
|
||||
return { background: '#ebf3f9' }
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
const activeIndex = ref('2')
|
||||
const handleClick = (val) => {
|
||||
activeIndex.value = val.props.name
|
||||
}
|
||||
|
||||
// 运行报表
|
||||
const dateValue = ref('')
|
||||
// 单机报表
|
||||
// 风机
|
||||
const windBlowerValue = ref('')
|
||||
const windBlowerList = ref<WindBlowerList[]>([])
|
||||
// 时间
|
||||
const timeRange = ref([])
|
||||
// 间隔
|
||||
const interval = ref('')
|
||||
const intervals = [
|
||||
{ label: '五分钟', value: '5m' },
|
||||
{ label: '十五分钟', value: '15m' },
|
||||
{ label: '一小时', value: '1h' },
|
||||
{ label: '一天', value: '1d' },
|
||||
{ label: '原始', value: 'NONE' },
|
||||
]
|
||||
// 模板
|
||||
const template = ref('')
|
||||
const templateList = [
|
||||
{ label: '模板一', value: '1' },
|
||||
{ label: '模板二', value: '2' },
|
||||
]
|
||||
// 测点名称
|
||||
const showMeasure = ref(false)
|
||||
const currentChoose = ref('')
|
||||
const currentChooseRows = ref()
|
||||
|
||||
const iotModelId = ref('')
|
||||
const measureRef = ref()
|
||||
const queryWindBlower = () => {
|
||||
queryWindTurbinesPages().then((res) => {
|
||||
if (res.code == 200) {
|
||||
windBlowerList.value = res.data.map((item) => {
|
||||
return {
|
||||
irn: item.irn,
|
||||
name: item.name ?? '-',
|
||||
modelId: item.modelId,
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
const selectWindBlower = (val) => {
|
||||
console.log(val, 9999)
|
||||
}
|
||||
|
||||
const chooseMeasurePoint = () => {
|
||||
if (!windBlowerValue.value) return ElMessage.warning('请先选择风机!')
|
||||
iotModelId.value = windBlowerList.value.find((val) => val.irn == windBlowerValue.value)!.modelId
|
||||
|
||||
showMeasure.value = true
|
||||
}
|
||||
const removeColumn = (val) => {
|
||||
const columnName = val.column.property
|
||||
reportTableColumn.value = reportTableColumn.value.filter((val) => val.prop !== columnName)
|
||||
}
|
||||
const handleSelections = (value) => {
|
||||
currentChooseRows.value = value
|
||||
}
|
||||
// 去重
|
||||
const uniqueByProp = (arr) => {
|
||||
const seen = new Set()
|
||||
return arr.filter((item) => {
|
||||
if (!seen.has(item.prop)) {
|
||||
seen.add(item.prop)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
const selectMeasurePoint = () => {
|
||||
currentChooseRows.value?.forEach((val) => {
|
||||
reportTableColumn.value.push({
|
||||
label: val.attributeName,
|
||||
prop: val.attributeCode,
|
||||
})
|
||||
})
|
||||
reportTableColumn.value = uniqueByProp(reportTableColumn.value)?.filter((item) => item.prop != null && item.prop !== '')
|
||||
showMeasure.value = false
|
||||
}
|
||||
const reportLoading = ref(false)
|
||||
const reportTableColumn = ref([
|
||||
{
|
||||
label: '',
|
||||
prop: '',
|
||||
},
|
||||
])
|
||||
|
||||
const reportTableData = ref([])
|
||||
const idCounter = ref(0)
|
||||
const queryHistoryData = () => {
|
||||
if (!windBlowerValue.value) return ElMessage.warning('请选择风机!')
|
||||
if (!timeRange.value.length) return ElMessage.warning('请选择时间!')
|
||||
if (!interval.value) return ElMessage.warning('请选择间隔!')
|
||||
const attributeCodes = reportTableColumn.value.map((val) => val.prop).filter((item) => item != null && item !== '')
|
||||
if (!attributeCodes.length) return ElMessage.warning('请添加测点!')
|
||||
reportLoading.value = true
|
||||
const requestData = {
|
||||
devices: [
|
||||
{
|
||||
deviceId: windBlowerValue.value,
|
||||
attributes: [...attributeCodes],
|
||||
},
|
||||
],
|
||||
interval: interval.value,
|
||||
startTime: new Date(timeRange.value[0]).getTime(),
|
||||
endTime: new Date(timeRange.value[1]).getTime(),
|
||||
}
|
||||
historyReq(requestData).then((res) => {
|
||||
if (res.code == 200) {
|
||||
const result = res.data
|
||||
if (Object.keys(result)?.length) {
|
||||
const realResult = result[windBlowerValue.value]
|
||||
let tableData = []
|
||||
attributeCodes.forEach((item) => {
|
||||
if (Object.keys(realResult).includes(item)) {
|
||||
tableData.push({
|
||||
name: item,
|
||||
times: realResult[item].times,
|
||||
value: realResult[item].values,
|
||||
})
|
||||
}
|
||||
})
|
||||
const processedData = new Map()
|
||||
idCounter.value = 0
|
||||
if (tableData.length) {
|
||||
tableData.forEach(({ name, times, value }) => {
|
||||
times.forEach((time, index) => {
|
||||
if (!processedData.has(time)) {
|
||||
processedData.set(time, { id: idCounter.value++, time: timestampToTime(time) })
|
||||
}
|
||||
processedData.get(time)[name] = value[index]
|
||||
})
|
||||
})
|
||||
}
|
||||
reportTableData.value = Array.from(processedData.values())
|
||||
reportLoading.value = false
|
||||
} else {
|
||||
ElMessage.warning('查询数据为空!')
|
||||
reportLoading.value = false
|
||||
}
|
||||
} else {
|
||||
reportLoading.value = false
|
||||
ElMessage.warning('查询失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 时间转换
|
||||
const getFormattedDate = (offset) => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() + offset)
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
const getDateRange = (type: 'week' | 'month') => {
|
||||
const today = new Date()
|
||||
if (type === 'week') {
|
||||
const dayOfWeek = today.getDay()
|
||||
const startOfWeek = new Date(today)
|
||||
startOfWeek.setDate(today.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1))
|
||||
startOfWeek.setHours(0, 0, 0, 0)
|
||||
const endOfWeek = new Date(startOfWeek)
|
||||
endOfWeek.setDate(startOfWeek.getDate() + 6)
|
||||
endOfWeek.setHours(23, 59, 59, 999)
|
||||
return [startOfWeek, endOfWeek]
|
||||
}
|
||||
if (type === 'month') {
|
||||
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)
|
||||
startOfMonth.setHours(0, 0, 0, 0)
|
||||
const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0)
|
||||
endOfMonth.setHours(23, 59, 59, 999)
|
||||
return [startOfMonth, endOfMonth]
|
||||
}
|
||||
}
|
||||
const timestampToTime = (timestamp) => {
|
||||
let timestamps = timestamp ? timestamp : null
|
||||
let date = new Date(timestamps)
|
||||
let Y = date.getFullYear() + '-'
|
||||
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||
let D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '
|
||||
let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'
|
||||
let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'
|
||||
let s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||
return Y + M + D + h + m + s
|
||||
}
|
||||
onMounted(() => {
|
||||
queryWindBlower()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$headerHeight: 60px;
|
||||
$defaultBackgroundColor: #fff;
|
||||
$defaultAsideWidth: 260px;
|
||||
$paginationHeight: 32px;
|
||||
.report {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.mainContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
.el-tabs {
|
||||
width: 100%;
|
||||
--el-tabs-header-height: 60px;
|
||||
:deep(.el-tabs__content) {
|
||||
height: calc(100% - 80px);
|
||||
}
|
||||
}
|
||||
.runningReport {
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
.singleReport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.topForm {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
overflow-x: auto;
|
||||
height: 110px;
|
||||
.buttons {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.mainReport {
|
||||
width: 100%;
|
||||
height: calc(100% - 155px);
|
||||
}
|
||||
|
||||
.commonSelect {
|
||||
min-width: 200px;
|
||||
:deep(.el-select__wrapper) {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
213
ui/dasadmin/src/views/backend/report/measureList.vue
Normal file
213
ui/dasadmin/src/views/backend/report/measureList.vue
Normal file
@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div class="measurement">
|
||||
<el-table
|
||||
:columns="tableColumn"
|
||||
:data="tableData"
|
||||
@sort-change="sortChange"
|
||||
max-height="495"
|
||||
:row-key="(row) => row.attributeCode"
|
||||
@selectionChange="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" :reserve-selection="true" />
|
||||
<el-table-column
|
||||
v-for="item in tableColumn"
|
||||
:key="item.prop"
|
||||
:label="item.label"
|
||||
:prop="item.prop"
|
||||
:width="item.width ?? ''"
|
||||
:align="item.align"
|
||||
:sortable="item.sortable"
|
||||
>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mainFooter">
|
||||
<el-pagination
|
||||
v-model:current-page="pageSetting.current"
|
||||
v-model:page-size="pageSetting.pageSize"
|
||||
:total="pageSetting.total"
|
||||
:page-sizes="pageSetting.pageSizes"
|
||||
background
|
||||
:pager-count="7"
|
||||
layout="prev, pager, next, jumper,sizes,total"
|
||||
@change="getcurrentPage"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch, defineEmits } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { ModelAttributeFieldsEnums, GetModelAttributeType } from '/@/views/backend/auth/model/type'
|
||||
import { getModelAttributeListReq, getRealValueListReq } from '/@/api/backend/deviceModel/request'
|
||||
|
||||
const props = defineProps({
|
||||
iotModelId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
deviceId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const tableColumn = [
|
||||
{
|
||||
label: '序号',
|
||||
prop: 'porder',
|
||||
width: 76,
|
||||
align: 'center',
|
||||
sortable: 'custom',
|
||||
},
|
||||
{
|
||||
label: '属性名称',
|
||||
prop: 'attributeName',
|
||||
align: 'left',
|
||||
sortable: 'custom',
|
||||
},
|
||||
{
|
||||
label: '属性编码',
|
||||
prop: 'attributeCode',
|
||||
align: 'left',
|
||||
width: 200,
|
||||
sortable: 'custom',
|
||||
},
|
||||
]
|
||||
const tableData = ref<any[]>([])
|
||||
const emit = defineEmits(['handleSelections'])
|
||||
const selectedRows = ref([])
|
||||
const handleSelectionChange = (selection) => {
|
||||
selectedRows.value = selection
|
||||
emit('handleSelections', selectedRows.value)
|
||||
}
|
||||
|
||||
const getAttributeList = () => {
|
||||
const requestData: GetModelAttributeType = {
|
||||
iotModelId: props.iotModelId,
|
||||
pageNum: pageSetting.current,
|
||||
pageSize: pageSetting.pageSize,
|
||||
attributeType: '138',
|
||||
orderColumn: sortData.orderColumn,
|
||||
orderType: sortData.orderType,
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
getModelAttributeListReq(requestData)
|
||||
.then((res) => {
|
||||
if (res.rows && res.rows.length > 0) {
|
||||
const codeList: any = []
|
||||
const data = res.rows!.map((item) => {
|
||||
codeList.push(item.attributeCode)
|
||||
return {
|
||||
...item,
|
||||
attributeTypeName:
|
||||
item.attributeType === 138
|
||||
? '模拟量'
|
||||
: item.attributeType === 139
|
||||
? '累积量'
|
||||
: item.attributeType === 140
|
||||
? '离散量'
|
||||
: item.attributeType!,
|
||||
}
|
||||
})
|
||||
pageSetting.total = res.total
|
||||
resolve({ data, codeList })
|
||||
} else {
|
||||
if (res.rows && res.rows.length === 0) {
|
||||
tableData.value = []
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err?.response?.data?.msg ?? '查询失败')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const getRealValueList = (data: { deviceId: string; attributes: string[] }, list?: any) => {
|
||||
return new Promise((resolve) => {
|
||||
getRealValueListReq([data]).then((res) => {
|
||||
if (res.success && res.data) {
|
||||
resolve({ realVal: res.data, list })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const getCompleteData = () => {
|
||||
getAttributeList()
|
||||
.then(({ data, codeList }: any) => {
|
||||
return getRealValueList({ deviceId: props.deviceId, attributes: codeList }, data)
|
||||
})
|
||||
.then((realData: any) => {
|
||||
const data = realData.list.map((item: any) => {
|
||||
const realValItem = realData.realVal[props.deviceId]?.[item.attributeCode?.toLowerCase()]
|
||||
return {
|
||||
...item,
|
||||
realTimeValue: realValItem ? (realValItem % 1 === 0 ? realValItem : realValItem.toFixed(3)) : '-',
|
||||
}
|
||||
})
|
||||
tableData.value = data
|
||||
})
|
||||
}
|
||||
|
||||
const sortData = reactive<{ orderColumn?: keyof typeof ModelAttributeFieldsEnums; orderType?: 'asc' | 'desc' }>({
|
||||
orderColumn: 'porder',
|
||||
orderType: 'asc',
|
||||
})
|
||||
|
||||
const sortChange = ({ prop, order }: { prop: keyof typeof ModelAttributeFieldsEnums; order: 'ascending' | 'descending' | null }) => {
|
||||
const propEnums = {
|
||||
attributeCode: 'attribute_code',
|
||||
attributeName: 'attribute_name',
|
||||
attributeTypeName: 'attribute_type',
|
||||
porder: 'porder',
|
||||
serviceCode: 'service_code',
|
||||
serviceName: 'service_name',
|
||||
serviceTypeName: 'service_type',
|
||||
}
|
||||
const orderType = order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : undefined
|
||||
const filed = propEnums[prop as keyof typeof propEnums] as keyof typeof ModelAttributeFieldsEnums
|
||||
sortData.orderColumn = orderType ? filed : undefined
|
||||
sortData.orderType = orderType
|
||||
getCompleteData()
|
||||
}
|
||||
|
||||
const pageSetting = reactive({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
pageSizes: [10, 20, 30],
|
||||
})
|
||||
|
||||
const getcurrentPage = () => {
|
||||
getCompleteData()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getCompleteData()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mainFooter {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
background-color: #fff;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
23
ui/dasadmin/src/views/backend/report/type.ts
Normal file
23
ui/dasadmin/src/views/backend/report/type.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export type RequestReturnType<T = never> = Promise<{
|
||||
code: number
|
||||
msg: string
|
||||
success: boolean
|
||||
data?: T
|
||||
}>
|
||||
|
||||
export type RequestReturnRowType<T = never> = Promise<{
|
||||
code: number
|
||||
msg: string
|
||||
rows: T
|
||||
total: number
|
||||
}>
|
||||
|
||||
// 风机列表
|
||||
export type WindBlowerList = {
|
||||
attributeMap: any
|
||||
belongLine: string
|
||||
irn: string
|
||||
model: string
|
||||
modelId: string
|
||||
name: string
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<el-menu-item v-for="(item, index) in headerList" :index="index" :key="index"> {{ item }} </el-menu-item>
|
||||
</el-menu>
|
||||
<TrendAnalysis v-if="activeIndex == 1"></TrendAnalysis>
|
||||
<TrendComparison v-if="activeIndex == 2"></TrendComparison>
|
||||
<!-- <TrendComparison v-if="activeIndex == 2"></TrendComparison> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@ -12,7 +12,7 @@ import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
import TrendAnalysis from './trendAnalysis.vue'
|
||||
import TrendComparison from './trendComparison.vue'
|
||||
// import TrendComparison from './trendComparison.vue'
|
||||
const config = useConfig()
|
||||
const activeIndex = ref(1)
|
||||
const { t } = useI18n()
|
||||
|
@ -64,15 +64,15 @@
|
||||
<div class="icon">
|
||||
<el-button v-show="index !== 0" type="danger" size="small" :icon="Delete" @click="switchTime(index)" circle></el-button>
|
||||
</div>
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['max']">
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['max'] !== ''">
|
||||
<span>{{ t('statAnalysis.max') }}</span>
|
||||
<span class="max">{{ calculate[index]['max'] }}</span>
|
||||
</div>
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['min']">
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['min'] !== ''">
|
||||
<span>{{ t('statAnalysis.min') }}</span>
|
||||
<span class="min">{{ calculate[index]['min'] }}</span>
|
||||
</div>
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['average']">
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['average'] !== ''">
|
||||
<span>{{ t('statAnalysis.average') }}</span>
|
||||
<span class="average">{{ calculate[index]['average'] }}</span>
|
||||
</div>
|
||||
@ -175,7 +175,7 @@ const selectstatAnalysisAttributes = () => {
|
||||
|
||||
const chartContainer = ref<HTMLElement | null>(null)
|
||||
|
||||
const option = reactive({
|
||||
const option = {
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
@ -206,7 +206,7 @@ const option = reactive({
|
||||
left: '3%',
|
||||
right: '3%',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const statAnalysisSelectOptions = reactive({
|
||||
interval: [
|
||||
@ -316,10 +316,9 @@ const getTimeIntervals = (startTimestamp, endTimestamp) => {
|
||||
return count
|
||||
}
|
||||
const statAnalysisOperate = () => {
|
||||
option.tooltip = {}
|
||||
option.xAxis.data = []
|
||||
option.legend.data = []
|
||||
option.series = []
|
||||
chart.value.setOption(option)
|
||||
chart.value.clear()
|
||||
times.forEach((time, index) => {
|
||||
if (time[0] && time[1]) {
|
||||
const requestData = {
|
||||
@ -347,13 +346,11 @@ const historyDataReq = (data, index) => {
|
||||
if (resData) {
|
||||
const xData = resData['times']
|
||||
const yData = resData['values']
|
||||
|
||||
calculate[index] = {
|
||||
max: Math.floor(Math.max(...yData)),
|
||||
min: Math.floor(Math.min(...yData)),
|
||||
average: Math.floor(yData.reduce((a, b) => a + b, 0) / yData.length),
|
||||
}
|
||||
|
||||
option.tooltip = {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
|
Loading…
Reference in New Issue
Block a user