Merge branch 'main' of https://git.jsspisoft.com/ry-das
This commit is contained in:
commit
8cae89c876
@ -9,5 +9,17 @@ public class IotFieldInfoCache {
|
||||
private Integer attributeType;
|
||||
private Integer porder;
|
||||
private Integer highspeed;
|
||||
private Integer datatype;
|
||||
private String datatype;
|
||||
|
||||
public boolean isHighSpeed() {
|
||||
return highspeed.equals(1) && attributeType.equals(138);
|
||||
}
|
||||
|
||||
public boolean isLowSpeed() {
|
||||
return highspeed.equals(0) && attributeType.equals(138);
|
||||
}
|
||||
|
||||
public boolean isCalculate() {
|
||||
return attributeType.equals(139);
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,4 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class IotModelInfoCache {
|
||||
private Long iotModelId;
|
||||
private String iodModelCode;
|
||||
private ConcurrentHashMap<String,IotFieldInfoCache> fieldInfoCache;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.das.modules.cache.service;
|
||||
|
||||
public interface IotModelCache {
|
||||
|
||||
public boolean isHighSpeed(Long modelId, String attr);
|
||||
public boolean isLowSpeed(Long modelId, String attr);
|
||||
public boolean isCalculate(Long modelId, String attr);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.das.modules.cache.service.impl;
|
||||
|
||||
import com.das.modules.cache.domain.IotFieldInfoCache;
|
||||
import com.das.modules.cache.domain.IotModelInfoCache;
|
||||
import com.das.modules.cache.service.IotModelCache;
|
||||
import com.das.modules.equipment.mapper.SysIotModelMapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@ -7,18 +9,70 @@ import jakarta.annotation.PreDestroy;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
@Service
|
||||
public class IotModelCacheImpl implements IotModelCache {
|
||||
|
||||
@Autowired
|
||||
SysIotModelMapper sysIotModelMapper;
|
||||
private ConcurrentHashMap<String, IotFieldInfoCache> iotFieldsMap = new ConcurrentHashMap<>();
|
||||
private ConcurrentHashMap<Long, IotModelInfoCache> iotModelInfoIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
//缓存模型
|
||||
sysIotModelMapper.getAllIotModel().forEach(item -> {
|
||||
IotModelInfoCache info = new IotModelInfoCache();
|
||||
info.setIotModelId(item.getId());
|
||||
info.setIodModelCode(item.getIotModelCode());
|
||||
iotModelInfoIdMap.put(item.getId(), info);
|
||||
});
|
||||
//缓存模型属性
|
||||
iotModelInfoIdMap.forEach((k,v)->{
|
||||
sysIotModelMapper.queryFieldByModelId(k).forEach(item -> {
|
||||
IotFieldInfoCache fieldInfoCache = new IotFieldInfoCache();
|
||||
fieldInfoCache.setAttributeCode(item.getAttributeCode());
|
||||
fieldInfoCache.setPorder(item.getPorder());
|
||||
fieldInfoCache.setAttributeName(item.getAttributeName());
|
||||
fieldInfoCache.setAttributeType(item.getAttributeType());
|
||||
fieldInfoCache.setHighspeed(item.getHighSpeed());
|
||||
fieldInfoCache.setDatatype(item.getDataType());
|
||||
iotFieldsMap.put(String.format("%d_%s", k, item.getAttributeCode()), fieldInfoCache);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHighSpeed(Long modelId, String attr) {
|
||||
IotFieldInfoCache fieldInfoCache = iotFieldsMap.get(String.format("%d_%s", modelId, attr));
|
||||
if (fieldInfoCache == null) {
|
||||
return false;
|
||||
}
|
||||
return fieldInfoCache.isHighSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLowSpeed(Long modelId, String attr) {
|
||||
IotFieldInfoCache fieldInfoCache = iotFieldsMap.get(String.format("%d_%s", modelId, attr));
|
||||
if (fieldInfoCache == null) {
|
||||
return false;
|
||||
}
|
||||
return fieldInfoCache.isLowSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCalculate(Long modelId, String attr) {
|
||||
IotFieldInfoCache fieldInfoCache = iotFieldsMap.get(String.format("%d_%s", modelId, attr));
|
||||
if (fieldInfoCache == null) {
|
||||
return false;
|
||||
}
|
||||
return fieldInfoCache.isCalculate();
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,12 @@ import com.das.modules.cache.domain.DeviceInfoCache;
|
||||
import com.das.modules.cache.service.CacheService;
|
||||
import com.das.modules.data.service.DataService;
|
||||
import com.das.modules.node.domain.bo.CalculateRTData;
|
||||
import com.googlecode.aviator.exception.StandardError;
|
||||
import com.googlecode.aviator.runtime.function.AbstractVariadicFunction;
|
||||
import com.googlecode.aviator.runtime.function.FunctionUtils;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
@ -56,6 +58,7 @@ public class FunctionSaveCalcData extends AbstractVariadicFunction {
|
||||
// return AviatorRuntimeJavaType.valueOf(result);
|
||||
// }
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public AviatorObject variadicCall(Map<String, Object> env, AviatorObject... args) {
|
||||
if (args.length < 4) { return AviatorRuntimeJavaType.valueOf(1);}
|
||||
@ -63,6 +66,9 @@ public class FunctionSaveCalcData extends AbstractVariadicFunction {
|
||||
//deviceCode
|
||||
String code = (String)args[0].getValue(env);
|
||||
DeviceInfoCache deviceInfoCache = cacheService.getEquipmentCache().getDeviceInfoCacheByCode(code);
|
||||
if(deviceInfoCache == null) {
|
||||
throw new StandardError(String.format("设备%s不存在", code));
|
||||
}
|
||||
List<CalculateRTData> dataList = new ArrayList<>();
|
||||
for (int i = 1; i < args.length; i+=3) {
|
||||
Date date = (Date)FunctionUtils.getJavaObject(args[i], env);
|
||||
|
@ -77,8 +77,20 @@ public class FunctionTopValue extends AbstractFunction {
|
||||
return AviatorRuntimeJavaType.valueOf(cacheValue.value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cacheValue = new CacheValue();
|
||||
}
|
||||
Date startTime = DateUtil.beginOfDay(curTimeValue);
|
||||
Date endTime = DateUtil.endOfDay(curTimeValue);
|
||||
Double value = dataService.getTimeTopValue(deviceInfoCache.getDeviceId(), attrName, startTime.getTime(), endTime.getTime());
|
||||
if (value == null){
|
||||
return AviatorNil.NIL;
|
||||
}
|
||||
cacheValue.setValue(value);
|
||||
cacheValue.setCurTimeValue(curTimeValue);
|
||||
cacheValues.put(key, cacheValue);
|
||||
//未找到缓存,查询时序API获取数据
|
||||
return AviatorRuntimeJavaType.valueOf(1);
|
||||
return AviatorRuntimeJavaType.valueOf(value);
|
||||
}
|
||||
|
||||
@Data
|
||||
|
@ -261,11 +261,18 @@ public class DataServiceImpl implements DataService {
|
||||
*/
|
||||
@Override
|
||||
public Double getTimeTopValue(Long devcieId, String attr, long startTime, long endTime){
|
||||
DeviceInfoCache deviceInfoCacheById = cacheService.getEquipmentCache().getDeviceInfoCacheById(devcieId);
|
||||
if (deviceInfoCacheById == null) {
|
||||
DeviceInfoCache deviceInfoCache = cacheService.getEquipmentCache().getDeviceInfoCacheById(devcieId);
|
||||
if (deviceInfoCache == null) {
|
||||
return null;
|
||||
}
|
||||
String tableName = "";
|
||||
if (cacheService.getIotModelCache().isHighSpeed(deviceInfoCache.getIotModelId(), attr)){
|
||||
tableName = String.format("h_%s", deviceInfoCache.getDeviceId());
|
||||
} else if (cacheService.getIotModelCache().isLowSpeed(deviceInfoCache.getIotModelId(), attr)){
|
||||
tableName = String.format("l_%s", deviceInfoCache.getDeviceId());
|
||||
} else if (cacheService.getIotModelCache().isCalculate(deviceInfoCache.getIotModelId(), attr)){
|
||||
tableName = String.format("c_%s", deviceInfoCache.getDeviceId());
|
||||
}
|
||||
return tdEngineService.getTimeTopValue(tableName, attr, startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,12 @@
|
||||
<div class="statAnalysis">
|
||||
<el-menu :default-active="activeIndex" class="headerList" mode="horizontal" @select="handleSelect">
|
||||
<el-menu-item index="0" key="0"> {{ headerList[0] }} </el-menu-item>
|
||||
<el-popover placement="top-start" :width="200" popper-class="admin-info-box" trigger="hover" content="同一测点,不同时间段对比">
|
||||
<template #reference>
|
||||
<el-menu-item index="1" key="1"> {{ headerList[1] }} </el-menu-item>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-popover placement="top-start" :width="200" popper-class="admin-info-box" trigger="hover" content="同一时间段,不同测点对比">
|
||||
<template #reference>
|
||||
<el-menu-item index="2" key="2"> {{ headerList[2] }} </el-menu-item>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-tooltip class="box-item" effect="dark" content="同一测点,不同时间段对比" placement="right-start">
|
||||
<el-menu-item index="1" key="1"> {{ headerList[1] }} </el-menu-item>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="box-item" effect="dark" content="同一时间段,不同测点对比" placement="right-start">
|
||||
<el-menu-item index="2" key="2"> {{ headerList[2] }} </el-menu-item>
|
||||
</el-tooltip>
|
||||
</el-menu>
|
||||
|
||||
<keep-alive>
|
||||
|
@ -1,92 +1,96 @@
|
||||
<template>
|
||||
<div class="contain">
|
||||
<div class="left">
|
||||
<div class="headerPart">
|
||||
<div class="topLeft">
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.deviceId') }}</span>
|
||||
<el-select
|
||||
v-model="statAnalysisSelect.deviceId"
|
||||
@change="deviceIdChange"
|
||||
:placeholder="'请选择' + t('statAnalysis.deviceId')"
|
||||
class="statAnalysisSelect"
|
||||
>
|
||||
<el-option v-for="v in statAnalysisSelectOptions.deviceId" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.attributes') }}</span>
|
||||
<el-input
|
||||
class="statAnalysisSelect"
|
||||
v-model="statAnalysisSelect.attributes"
|
||||
@click="attributesChange"
|
||||
:placeholder="'请选择' + t('statAnalysis.attributes')"
|
||||
></el-input>
|
||||
</div>
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.interval') }}</span>
|
||||
<el-select
|
||||
v-model="statAnalysisSelect.interval"
|
||||
:placeholder="'请选择' + t('statAnalysis.interval')"
|
||||
class="statAnalysisSelect"
|
||||
>
|
||||
<el-option v-for="v in statAnalysisSelectOptions.interval" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="headerPart">
|
||||
<div class="topLeft">
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.deviceId') }}</span>
|
||||
<el-select
|
||||
v-model="statAnalysisSelect.deviceId"
|
||||
@change="deviceIdChange"
|
||||
:placeholder="'请选择' + t('statAnalysis.deviceId')"
|
||||
class="statAnalysisSelect"
|
||||
>
|
||||
<el-option v-for="v in statAnalysisSelectOptions.deviceId" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="topRight">
|
||||
<el-button type="primary" :icon="Plus" @click="addTime()"> 增加</el-button>
|
||||
<el-button type="primary" :loading="isLoading" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
|
||||
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.attributes') }}</span>
|
||||
<el-input
|
||||
class="statAnalysisSelect"
|
||||
v-model="statAnalysisSelect.attributes"
|
||||
@click="attributesChange"
|
||||
:placeholder="'请选择' + t('statAnalysis.attributes')"
|
||||
></el-input>
|
||||
</div>
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.interval') }}</span>
|
||||
<el-select v-model="statAnalysisSelect.interval" :placeholder="'请选择' + t('statAnalysis.interval')" class="statAnalysisSelect">
|
||||
<el-option v-for="v in statAnalysisSelectOptions.interval" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button class="addline" type="primary" :icon="Plus" @click="addTime()"> 增加</el-button>
|
||||
</div>
|
||||
|
||||
<div class="timeColumns">
|
||||
<div class="moduleRow" v-for="(time, index) in times" :key="index">
|
||||
<div class="item">
|
||||
<el-icon v-show="index !== 0" class="removeModule" @click="switchTime(index)">
|
||||
<Close />
|
||||
</el-icon>
|
||||
<div class="selectPart">
|
||||
<span>曲线名称</span>
|
||||
<el-input v-model="customName[index]" class="customName"></el-input>
|
||||
</div>
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.time') }}</span>
|
||||
<el-date-picker
|
||||
class="datetime-picker"
|
||||
v-model="times[index]"
|
||||
:type="statAnalysisSelect.interval == '1d' ? 'daterange' : 'datetimerange'"
|
||||
:value-format="statAnalysisSelect.interval == '1d' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||
:teleported="false"
|
||||
:shortcuts="shortcuts"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]"
|
||||
@change="timechange(index)"
|
||||
/>
|
||||
</div>
|
||||
<div class="topLeft">
|
||||
<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'] !== ''">
|
||||
<span>{{ t('statAnalysis.min') }}</span>
|
||||
<span class="min">{{ calculate[index]['min'] }}</span>
|
||||
</div>
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['average'] !== ''">
|
||||
<span>{{ t('statAnalysis.average') }}</span>
|
||||
<span class="average">{{ calculate[index]['average'] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="topRight">
|
||||
<el-button type="primary" :loading="isLoading" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
|
||||
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div
|
||||
ref="chartContainer"
|
||||
style="position: absolute; top: 127px; width: calc(100% - 430px); height: 400px; border: 1px solid rgb(217, 217, 217)"
|
||||
></div>
|
||||
<div class="main">
|
||||
<div class="left">
|
||||
<div class="timeColumns">
|
||||
<div class="moduleRow" v-for="(time, index) in times" :key="index">
|
||||
<div class="item">
|
||||
<el-icon v-show="index !== 0" class="removeModule" @click="switchTime(index)">
|
||||
<Close />
|
||||
</el-icon>
|
||||
<div class="selectPart">
|
||||
<span>曲线名称</span>
|
||||
<el-input v-model="customName[index]" class="customName"></el-input>
|
||||
</div>
|
||||
<div class="selectPart">
|
||||
<span>{{ t('statAnalysis.time') }}</span>
|
||||
<el-date-picker
|
||||
class="datetime-picker"
|
||||
v-model="times[index]"
|
||||
:type="statAnalysisSelect.interval == '1d' ? 'daterange' : 'datetimerange'"
|
||||
:value-format="statAnalysisSelect.interval == '1d' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||
:teleported="false"
|
||||
:shortcuts="shortcuts"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]"
|
||||
@change="timechange(index)"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="topLeft">
|
||||
<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'] !== ''">
|
||||
<span>{{ t('statAnalysis.min') }}</span>
|
||||
<span class="min">{{ calculate[index]['min'] }}</span>
|
||||
</div>
|
||||
<div class="selectPart" v-if="calculate[index] && calculate[index]['average'] !== ''">
|
||||
<span>{{ t('statAnalysis.average') }}</span>
|
||||
<span class="average">{{ calculate[index]['average'] }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div
|
||||
ref="chartContainer"
|
||||
style="
|
||||
position: absolute;
|
||||
top: 127px;
|
||||
width: calc(100% - 430px);
|
||||
height: calc(100% - 187px);
|
||||
border: 1px solid rgb(217, 217, 217);
|
||||
margin: 30px 0;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog v-model="showMeasure" title="测点名称" :width="800">
|
||||
@ -124,8 +128,12 @@ const statAnalysisSelect = reactive({
|
||||
})
|
||||
const times: any = reactive([{ time: '' }])
|
||||
const addTime = () => {
|
||||
times.push({ time: '' })
|
||||
customName.push(statAnalysisSelect.attributes + String(times.length))
|
||||
if (times.length < 4) {
|
||||
times.push({ time: '' })
|
||||
customName.push(statAnalysisSelect.attributes + String(times.length))
|
||||
} else {
|
||||
ElMessage.info('最多可添加四条查询曲线!')
|
||||
}
|
||||
}
|
||||
const switchTime = (index: number) => {
|
||||
times.splice(index, 1)
|
||||
@ -421,7 +429,7 @@ const historyDataReq = (promises: any) => {
|
||||
const fillData = fillMissingData(alltimes, resData)
|
||||
const xData = fillData['times']
|
||||
const yData = fillData['values']
|
||||
calculate.value[index] = calculateStats(resData['values'])
|
||||
// calculate.value[index] = calculateStats(resData['values'])
|
||||
xDatas.push({
|
||||
series: String(customName[index]),
|
||||
data: xData,
|
||||
@ -564,48 +572,63 @@ const timestampToTime = (timestamp: any) => {
|
||||
height: 100%;
|
||||
.contain {
|
||||
height: calc(100% - 60px);
|
||||
display: flex;
|
||||
.right {
|
||||
width: calc(100% - 418px);
|
||||
}
|
||||
}
|
||||
.headerPart {
|
||||
padding: 20px;
|
||||
.topLeft {
|
||||
.icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.dialog-footer button:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.topRight {
|
||||
// display: flex;
|
||||
.main {
|
||||
display: flex;
|
||||
margin: 12px 0;
|
||||
.el-button {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
height: calc(100% - 60px);
|
||||
|
||||
.right {
|
||||
width: calc(100% - 418px);
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.headerPart {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
height: 60px;
|
||||
.addline {
|
||||
margin: 4px 20px;
|
||||
}
|
||||
.topLeft {
|
||||
display: flex;
|
||||
.icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.dialog-footer button:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.topRight {
|
||||
display: flex;
|
||||
margin: 12px 0;
|
||||
.el-button {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeColumns {
|
||||
.moduleRow {
|
||||
.item {
|
||||
position: relative;
|
||||
margin: 10px 8px;
|
||||
width: 402px;
|
||||
min-height: 170px;
|
||||
padding: 20px 5px 30px 5px;
|
||||
margin: 0px 12px 12px;
|
||||
padding: 12px 12px 8px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
||||
.topLeft {
|
||||
display: flex;
|
||||
}
|
||||
.selectPart {
|
||||
width: 370px;
|
||||
width: 388px;
|
||||
justify-content: left;
|
||||
margin: 4px;
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -614,15 +637,17 @@ const timestampToTime = (timestamp: any) => {
|
||||
}
|
||||
.removeModule {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 5px;
|
||||
top: 6px;
|
||||
right: 8px;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
cursor: pointer;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
&:hover {
|
||||
.removeModule {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
border: 1px solid rgba(0, 0, 0, 0.55);
|
||||
border-radius: 50px;
|
||||
}
|
||||
}
|
||||
.headerPart {
|
||||
@ -635,8 +660,6 @@ const timestampToTime = (timestamp: any) => {
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
margin-right: 20px;
|
||||
width: 340px;
|
||||
margin: 4px 0;
|
||||
span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user