This commit is contained in:
谷成伟 2024-10-24 09:37:59 +08:00
commit c8d77fca95
41 changed files with 2078 additions and 1021 deletions

View File

@ -49,12 +49,12 @@ public class DataService {
//为空查全部 //为空查全部
List<String> sysIotModelFields = sysIotModelFieldMapper.queryAllFiledNames(Long.valueOf(snapshotValueQueryParam.getDeviceId())); List<String> sysIotModelFields = sysIotModelFieldMapper.queryAllFiledNames(Long.valueOf(snapshotValueQueryParam.getDeviceId()));
for (String item : sysIotModelFields) { for (String item : sysIotModelFields) {
String key = String.format("RT:[%s]:[%s]", snapshotValueQueryParam.getDeviceId(), item); String key = String.format("RT:%s:%s", snapshotValueQueryParam.getDeviceId(), item);
keyList.add(key); keyList.add(key);
} }
} else { } else {
for (String item : attributes) { for (String item : attributes) {
String key = String.format("RT:[%s]:[%s]", snapshotValueQueryParam.getDeviceId(), item); String key = String.format("RT:%s:%s", snapshotValueQueryParam.getDeviceId(), item);
keyList.add(key); keyList.add(key);
} }
} }
@ -62,12 +62,10 @@ public class DataService {
List<Object> dataList = adminRedisTemplate.mGet(keyList); List<Object> dataList = adminRedisTemplate.mGet(keyList);
for (int i = 0; i < keyList.size(); i++) { for (int i = 0; i < keyList.size(); i++) {
String key = keyList.get(i); String key = keyList.get(i);
int firstColonIndex = key.indexOf('['); int firstColonIndex = key.indexOf(':');
int firstIndex = key.indexOf(']'); int secondColonIndex = key.indexOf(':', firstColonIndex + 1);
int secondIndex = key.indexOf(']', firstIndex + 1); String deviceId = key.substring(firstColonIndex + 1, secondColonIndex);
int secondColonIndex = key.indexOf('[', firstColonIndex + 1); String fieldName = key.substring(secondColonIndex + 1);
String deviceId = key.substring(firstColonIndex + 1, firstIndex);
String fieldName = key.substring(secondColonIndex + 1, secondIndex);
if (finalResult.get(deviceId) == null) { if (finalResult.get(deviceId) == null) {
Map<String, Object> valueMap = new HashMap<>(); Map<String, Object> valueMap = new HashMap<>();
valueMap.put(fieldName, dataList.get(i)); valueMap.put(fieldName, dataList.get(i));

View File

@ -1,6 +1,7 @@
package com.das.modules.equipment.service.impl; package com.das.modules.equipment.service.impl;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.poi.excel.ExcelReader; import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -25,6 +26,7 @@ import com.das.modules.equipment.mapper.SysIotModelFieldMapper;
import com.das.modules.equipment.mapper.SysIotModelMapper; import com.das.modules.equipment.mapper.SysIotModelMapper;
import com.das.modules.equipment.mapper.SysIotModelServiceMapper; import com.das.modules.equipment.mapper.SysIotModelServiceMapper;
import com.das.modules.equipment.service.SysIotModelService; import com.das.modules.equipment.service.SysIotModelService;
import com.das.modules.node.service.TDEngineService;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -35,15 +37,13 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.*;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Service @Service
@Slf4j @Slf4j
public class SysIotModelServiceImpl implements SysIotModelService { public class SysIotModelServiceImpl implements SysIotModelService {
public static final int COMMIT_COUNT = 1000;
@Autowired @Autowired
private SysIotModelMapper sysIotModelMapper; private SysIotModelMapper sysIotModelMapper;
@ -54,6 +54,8 @@ public class SysIotModelServiceImpl implements SysIotModelService {
@Autowired @Autowired
private SysIotModelServiceMapper sysIotModelServiceMapper; private SysIotModelServiceMapper sysIotModelServiceMapper;
@Autowired
private TDEngineService tdEngineService;
public SysIotModelVo creatSysIotModel(SysIotModelDto sysIotModelDto) { public SysIotModelVo creatSysIotModel(SysIotModelDto sysIotModelDto) {
SysIotModel sysIotModel = new SysIotModel(); SysIotModel sysIotModel = new SysIotModel();
@ -162,6 +164,8 @@ public class SysIotModelServiceImpl implements SysIotModelService {
sysIotModelField.setDataType("tinyint"); sysIotModelField.setDataType("tinyint");
} }
//动态执行创建td超级表字段
createTdStableOrColumn(sysIotModelField);
sysIotModelFieldMapper.insert(sysIotModelField); sysIotModelFieldMapper.insert(sysIotModelField);
SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo(); SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo();
BeanCopyUtils.copy(sysIotModelField, sysIotModelFieldVo); BeanCopyUtils.copy(sysIotModelField, sysIotModelFieldVo);
@ -188,6 +192,15 @@ public class SysIotModelServiceImpl implements SysIotModelService {
} }
sysIotModelFieldMapper.updateById(sysIotModelField); sysIotModelFieldMapper.updateById(sysIotModelField);
SysIotModelField oldSysIotField = sysIotModelFieldMapper.selectById(sysIotModelFieldDto.getId());
if (oldSysIotField == null){
throw new ServiceException("未查找到该条记录");
}
if (!oldSysIotField.getAttributeCode().equals(sysIotModelField.getAttributeCode()) && oldSysIotField.getDataType().equals(sysIotModelField.getDataType()) && Objects.equals(oldSysIotField.getHighSpeed(), sysIotModelField.getHighSpeed())){
//更新td表结构
updateTDStableOrColumn(sysIotModelField,oldSysIotField);
}
SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo(); SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo();
BeanCopyUtils.copy(sysIotModelField, sysIotModelFieldVo); BeanCopyUtils.copy(sysIotModelField, sysIotModelFieldVo);
return sysIotModelFieldVo; return sysIotModelFieldVo;
@ -195,6 +208,8 @@ public class SysIotModelServiceImpl implements SysIotModelService {
@Override @Override
public void deleteSysIotModelField(SysIotModelFieldDto sysIotModelFieldDto) { public void deleteSysIotModelField(SysIotModelFieldDto sysIotModelFieldDto) {
SysIotModelField sysIotModelField = sysIotModelFieldMapper.selectById(sysIotModelFieldDto.getId());
deleteTDStableOrColumn(sysIotModelField);
sysIotModelFieldMapper.deleteById(sysIotModelFieldDto.getId()); sysIotModelFieldMapper.deleteById(sysIotModelFieldDto.getId());
} }
@ -401,11 +416,35 @@ public class SysIotModelServiceImpl implements SysIotModelService {
} }
} }
} }
sysIotModelFieldMapper.insertBatch(sysIotModelFieldList); if (CollectionUtils.isNotEmpty(sysIotModelFieldList)) {
sysIotModelFieldMapper.insertBatch(sysIotModelFieldList);
ListUtil.page(sysIotModelFieldList, COMMIT_COUNT, create -> {
for (SysIotModelField item : create){
createTdStableOrColumn(item);
}
});
}
if (CollectionUtils.isNotEmpty(updateSysIotModelFieldList)) { if (CollectionUtils.isNotEmpty(updateSysIotModelFieldList)) {
ListUtil.page(updateSysIotModelFieldList, COMMIT_COUNT, update -> {
for (SysIotModelField item : update){
SysIotModelField oldSysIotField = sysIotModelFieldMapper.selectById(item.getId());
if (oldSysIotField != null){
if (!oldSysIotField.getAttributeCode().equals(item.getAttributeCode()) && oldSysIotField.getDataType().equals(item.getDataType()) && Objects.equals(oldSysIotField.getHighSpeed(), item.getHighSpeed())){
//更新td表结构
updateTDStableOrColumn(item,oldSysIotField);
}
}
}
});
sysIotModelFieldMapper.updateBatchById(updateSysIotModelFieldList); sysIotModelFieldMapper.updateBatchById(updateSysIotModelFieldList);
} }
if (CollectionUtils.isNotEmpty(delSysIotModelFieldList)) { if (CollectionUtils.isNotEmpty(delSysIotModelFieldList)) {
ListUtil.page(updateSysIotModelFieldList, COMMIT_COUNT, delete -> {
for (SysIotModelField item : delete){
deleteTDStableOrColumn(item);
}
});
sysIotModelFieldMapper.deleteBatchIds(delSysIotModelFieldList); sysIotModelFieldMapper.deleteBatchIds(delSysIotModelFieldList);
} }
} }
@ -428,4 +467,70 @@ public class SysIotModelServiceImpl implements SysIotModelService {
return list; return list;
} }
public void createTdStableOrColumn(SysIotModelField sysIotModelField){
//如果新增的是第一条记录 创建tdengine超级表 分为高频和低频
QueryWrapper<SysIotModelField> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("iot_model_id",sysIotModelField.getIotModelId());
queryWrapper.eq("highspeed",sysIotModelField.getHighSpeed());
List<SysIotModelField> sysIotModelFields = sysIotModelFieldMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(sysIotModelFields)){
if (sysIotModelField.getHighSpeed() == 0){
SysIotModel sysIotModel = sysIotModelMapper.selectById(sysIotModelField.getIotModelId());
Map<String,String> map = new HashMap<>();
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
//创建低频超级表
tdEngineService.createStable(sysIotModel.getIotModelCode(),"l_",map);
}
if (sysIotModelField.getHighSpeed() == 1){
SysIotModel sysIotModel = sysIotModelMapper.selectById(sysIotModelField.getIotModelId());
Map<String,String> map = new HashMap<>();
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
//创建高频超级表
tdEngineService.createStable(sysIotModel.getIotModelCode(),"h_",map);
}
}else {
//stable已经存在,新增stable列
if (sysIotModelField.getHighSpeed() == 0){
SysIotModel sysIotModel = sysIotModelMapper.selectById(sysIotModelField.getIotModelId());
Map<String,String> map = new HashMap<>();
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
//创建低频超级表
tdEngineService.addStableColumn(sysIotModel.getIotModelCode(),"l_",map);
}
if (sysIotModelField.getHighSpeed() == 1){
SysIotModel sysIotModel = sysIotModelMapper.selectById(sysIotModelField.getIotModelId());
Map<String,String> map = new HashMap<>();
map.put(sysIotModelField.getAttributeCode(), sysIotModelField.getDataType());
//创建高频超级表
tdEngineService.addStableColumn(sysIotModel.getIotModelCode(),"h_",map);
}
}
}
public void updateTDStableOrColumn(SysIotModelField sysIotModelField,SysIotModelField oldIotModelField){
deleteTDStableOrColumn(oldIotModelField);
createTdStableOrColumn(sysIotModelField);
}
public void deleteTDStableOrColumn(SysIotModelField sysIotModelField){
String stableName = null;
SysIotModel sysIotModel = sysIotModelMapper.selectById(sysIotModelField.getIotModelId());
if(sysIotModelField.getHighSpeed() == 0) {
stableName = "l_" + sysIotModel.getIotModelCode();
}
if(sysIotModelField.getHighSpeed() == 1) {
stableName = "h_" + sysIotModel.getIotModelCode();
}
QueryWrapper<SysIotModelField> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("iot_model_id",sysIotModelField.getIotModelId());
queryWrapper.eq("highspeed",sysIotModelField.getHighSpeed());
List<SysIotModelField> sysIotModelFields = sysIotModelFieldMapper.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(sysIotModelFields) && sysIotModelFields.size() > 1){
tdEngineService.deleteColumn(stableName,sysIotModelField.getAttributeCode());
}
//物模型属性只剩下最后一个删除表
if (CollectionUtils.isNotEmpty(sysIotModelFields) && sysIotModelFields.size() == 1){
tdEngineService.deleteStable(stableName);
}
}
} }

View File

@ -61,6 +61,7 @@ public class TDEngineService {
/** /**
* 创建超级表 * 创建超级表
*/ */
@Async
public void createStable(String iotModelCode, String stableType, Map<String, String> fieldNameTypeMap) { public void createStable(String iotModelCode, String stableType, Map<String, String> fieldNameTypeMap) {
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {
@ -94,6 +95,7 @@ public class TDEngineService {
/** /**
* 新增超级表列 * 新增超级表列
*/ */
@Async
public void addStableColumn(String iotModelCode, String stableType, Map<String, String> fieldNameTypeMap) { public void addStableColumn(String iotModelCode, String stableType, Map<String, String> fieldNameTypeMap) {
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {
@ -124,6 +126,7 @@ public class TDEngineService {
/** /**
* 删除超级表列 * 删除超级表列
*/ */
@Async
public void deleteColumn(String stableName, String fieldCode) { public void deleteColumn(String stableName, String fieldCode) {
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {
@ -131,7 +134,7 @@ public class TDEngineService {
sb.setLength(0); sb.setLength(0);
sb.append("ALTER STABLE "); sb.append("ALTER STABLE ");
sb.append(stableName); sb.append(stableName);
sb.append(" DROP COLUMN"); sb.append(" DROP COLUMN ");
; ;
sb.append(fieldCode); sb.append(fieldCode);
sb.append(";"); sb.append(";");
@ -149,6 +152,7 @@ public class TDEngineService {
/** /**
* 删除超级表 * 删除超级表
*/ */
@Async
public void deleteStable(String stableName) { public void deleteStable(String stableName) {
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {

View File

@ -248,9 +248,8 @@ public class DataServiceImpl implements DataService {
Iterator<String> keysHigh = values.fieldNames(); Iterator<String> keysHigh = values.fieldNames();
while (keysHigh.hasNext()) { while (keysHigh.hasNext()) {
String fieldName = keysHigh.next(); String fieldName = keysHigh.next();
String key = String.format("RT:[%s]:[%s]", deviceId, fieldName); String key = String.format("RT:%s:%s", deviceId, fieldName);
keyValueMap.put(key, values.get(fieldName).asDouble()); keyValueMap.put(key,values.get(fieldName));
} }
adminRedisTemplate.mSet(keyValueMap); adminRedisTemplate.mSet(keyValueMap);
// Long dataTime = data.getTime(); // Long dataTime = data.getTime();

View File

@ -6,7 +6,7 @@ VITE_BASE_PATH = './'
# 代理配置(开发使用),必须在一行中 # 代理配置(开发使用),必须在一行中
# 本地 # 本地
# VITE_APP_PROXY=[["/api","http://192.168.130.12:8080/api"]] # VITE_APP_PROXY=[["/api","http://192.168.130.22:8080/api"]]
# 线上 # 线上
VITE_APP_PROXY=[["/api","https://test.jsspisoft.com/api"]] VITE_APP_PROXY=[["/api","https://test.jsspisoft.com/api"]]

View File

@ -34,7 +34,7 @@ export function enumValueAdd(params: object = {}) {
}) })
} }
// 枚举值新增 // 枚举值删除
export function enumValueDelete(params: object = {}) { export function enumValueDelete(params: object = {}) {
return createAxios({ return createAxios({
url: '/api/enum/deleteEnumValues', url: '/api/enum/deleteEnumValues',
@ -42,4 +42,12 @@ export function enumValueDelete(params: object = {}) {
data: params, data: params,
}) })
} }
// 枚举值编辑
export function enumValuepageEdit(params: object = {}) {
return createAxios({
url: '/api/enum/updateEnumValues',
method: 'POST',
data: params,
})
}

View File

@ -1,4 +1,5 @@
import createAxios from '/@/utils/axios' import createAxios from '/@/utils/axios'
import {addDataType, getDataReturnType, getDataType, getTreeDataReturnType} from "/@/views/backend/auth/org/type";
export const url = '/admin/Dashboard/' export const url = '/admin/Dashboard/'
@ -8,3 +9,11 @@ export function index() {
method: 'get', method: 'get',
}) })
} }
export const getInstitutionalListReq = (data: getDataType) => {
return createAxios<addDataType, getDataReturnType<getTreeDataReturnType>>({
url: '/api/org/query',
method: 'post',
data: data,
})
}

View File

@ -142,8 +142,16 @@ export const downloadModelReq = (data: { id: string }) => {
} }
export const getDeviceTypeEnumReq = () => { export const getDeviceTypeEnumReq = () => {
return createAxios<never,RequestReturnType<DeviceType[]>>({ return createAxios<never, RequestReturnType<DeviceType[]>>({
url: '/api/equipment/type', url: '/api/equipment/type',
method: 'post', method: 'post',
}) })
} }
export const getRealValueListReq = (data: { deviceId: string, attributes: string[] }[]) => {
return createAxios<never, RequestReturnType<any>>({
url: '/api/data/snapshot',
method: 'post',
data: data,
})
}

View File

@ -0,0 +1,24 @@
import createAxios from '/@/utils/axios'
import {
RequestReturnType,
RequestReturnRowType,
LinkMonitorTreeType,
GetLinkMonitorTableParam,
LinkMonitorTableType,
} from '/@/views/backend/linkMonitor/type'
// 左侧树节点树结构
export const getNodeListReq = () => {
return createAxios<never, RequestReturnType<LinkMonitorTreeType[]>>({
url: '/api/node/list',
method: 'post',
})
}
// 链路列表
export const getLinkListReq = (data: GetLinkMonitorTableParam) => {
return createAxios<never, RequestReturnRowType<LinkMonitorTableType[]>>({
url: '/api/node/link/list',
method: 'post',
data: data,
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,10 +1,10 @@
<template> <template>
<div class="nav-menus" :class="configStore.layout.layoutMode"> <div class="nav-menus" :class="configStore.layout.layoutMode">
<router-link class="h100" target="_blank" :title="t('Home')" to="/"> <!-- <router-link class="h100" target="_blank" :title="t('Home')" to="/">
<div class="nav-menu-item"> <div class="nav-menu-item">
<Icon :color="configStore.getColorVal('headerBarTabColor')" class="nav-menu-icon" name="el-icon-Monitor" size="18" /> <Icon :color="configStore.getColorVal('headerBarTabColor')" class="nav-menu-icon" name="el-icon-Monitor" size="18" />
</div> </div>
</router-link> </router-link> -->
<!-- <el-dropdown <!-- <el-dropdown
@visible-change="onCurrentNavMenu($event, 'lang')" @visible-change="onCurrentNavMenu($event, 'lang')"
class="h100" class="h100"

View File

@ -70,11 +70,6 @@ const staticRoutes: Array<RouteRecordRaw> = [
name: 'univer', name: 'univer',
component: () => import('/@/views/backend/node/univer.vue'), component: () => import('/@/views/backend/node/univer.vue'),
}, },
{
path: adminBaseRoutePath +'/airBlower',
name: 'airBlower',
component: () => import('/@/views/backend/equipment/airBlower/index.vue'),
}
] ]
const staticFiles: Record<string, Record<string, RouteRecordRaw>> = import.meta.glob('./static/*.ts', { eager: true }) const staticFiles: Record<string, Record<string, RouteRecordRaw>> = import.meta.glob('./static/*.ts', { eager: true })

View File

@ -106,11 +106,12 @@ export const useConfig = defineStore(
const getColorVal = function (name: keyof Layout): string { const getColorVal = function (name: keyof Layout): string {
const colors = layout[name] as string[] const colors = layout[name] as string[]
if (layout.isDark) { // TODO 切换暗黑模式
return colors[1] // if (layout.isDark) {
} else { // return colors[1]
return colors[0] // } else {
} return colors[0]
// }
} }
return { layout, lang, menuWidth, setLang, setLayoutMode, setLayout, getColorVal, onSetLayoutColor } return { layout, lang, menuWidth, setLang, setLayoutMode, setLayout, getColorVal, onSetLayoutColor }

View File

@ -3,26 +3,6 @@
<el-container class="containerPart"> <el-container class="containerPart">
<el-aside class="defaultAside"> <el-aside class="defaultAside">
<el-main class="treeMain"> <el-main class="treeMain">
<!-- <div class="demo-collapse">-->
<!-- <el-collapse v-model="activeName" accordion>-->
<!-- <el-collapse-item name="1">-->
<!-- <template #title>-->
<!-- <span class="collapse-title">枚举类型配置</span>-->
<!-- </template>-->
<!-- <div>-->
<!-- <el-tree-->
<!-- style="max-width: 600px;margin-top: 2.2%"-->
<!-- ref="TypeTree"-->
<!-- :data="TypeData"-->
<!-- :props="defaultProps"-->
<!-- node-key="id"-->
<!-- @node-click="handleNodeClick"-->
<!-- />-->
<!-- </div>-->
<!-- </el-collapse-item>-->
<!-- </el-collapse>-->
<!-- </div>-->
<!-- <label class="treeTitle">枚举类型配置</label>-->
<el-tree <el-tree
style="max-width: 600px;margin-top: 2.2%" style="max-width: 600px;margin-top: 2.2%"
ref="TypeTree" ref="TypeTree"
@ -93,7 +73,7 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="visibleTypeAdd = false"> 取消 </el-button> <el-button @click="visibleTypeAdd = false">取消</el-button>
<el-button type="primary" @click="onSubmitType">提交</el-button> <el-button type="primary" @click="onSubmitType">提交</el-button>
</div> </div>
</template> </template>
@ -118,13 +98,16 @@
<el-input-number :min="0" v-model="formInlineValue.orderNumber" placeholder="" /> <el-input-number :min="0" v-model="formInlineValue.orderNumber" placeholder="" />
</el-form-item> </el-form-item>
<el-form-item label="&nbsp;&nbsp;是否有效:"> <el-form-item label="&nbsp;&nbsp;是否有效:">
<el-switch v-model="formInlineValue.isActive" /> <el-switch @change="addhandleSwitchChange"
:active-value="1"
:inactive-value="0"
v-model="formInlineValue.isActive" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="visibleValueAdd = false"> 取消 </el-button> <el-button @click="visibleValueAdd = false">取消</el-button>
<el-button type="primary" @click="onSubmitvalue">提交</el-button> <el-button type="primary" @click="onSubmitvalue">提交</el-button>
</div> </div>
</template> </template>
@ -139,7 +122,7 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<!--查看枚举值--> <!--编辑枚举值-->
<el-dialog v-model="visibleValueEdit" title="查看枚举值" width="500" :before-close="edithandleClosevalue"> <el-dialog v-model="visibleValueEdit" title="查看枚举值" width="500" :before-close="edithandleClosevalue">
<el-form <el-form
ref="formRef" ref="formRef"
@ -158,14 +141,17 @@
<el-input-number :min="0" v-model="fromUpDate.orderNumber" placeholder="" /> <el-input-number :min="0" v-model="fromUpDate.orderNumber" placeholder="" />
</el-form-item> </el-form-item>
<el-form-item label="&nbsp;&nbsp;是否有效:"> <el-form-item label="&nbsp;&nbsp;是否有效:">
<el-switch v-model="fromUpDate.isActive" /> <el-switch @change="edithandleSwitchChange"
:active-value="1"
:inactive-value="0"
v-model="fromUpDate.isActive" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="visibleValueEdit = false"> 取消 </el-button> <el-button @click="visibleValueEdit = false"> 取消 </el-button>
<!-- <el-button type="primary" @click="onSubmitvalue">提交</el-button>--> <el-button type="primary" @click="EditonSubmitvalue">提交</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -182,13 +168,10 @@ import {
enumTreeQuery, enumTreeQuery,
enumTypeAdd, enumTypeAdd,
enumValueAdd, enumValueAdd,
enumValueDelete enumValueDelete, enumValuepageEdit
} from "/@/api/backend/Enumeration/request"; } from "/@/api/backend/Enumeration/request";
const activeName = ref('1') const activeName = ref('1')
interface Tree { interface Tree {
enumId: number, enumId: number,
@ -260,7 +243,6 @@ const handleNodeClick = (data: any) => {
queryListData.description=queryName.value, queryListData.description=queryName.value,
queryListData.pageNum=currentPage.value, queryListData.pageNum=currentPage.value,
queryListData.pageSize=currentPageSize.value queryListData.pageSize=currentPageSize.value
console.log(queryListData)
queryenumValueMethod(queryListData) queryenumValueMethod(queryListData)
} }
@ -385,13 +367,6 @@ const queryenumValueMethod = (data: any) => {
const onSubmitvalue = () => { const onSubmitvalue = () => {
formRef.value.validate((valid: any) => { formRef.value.validate((valid: any) => {
if (valid) { if (valid) {
if(formInlineValue.isActive==true){
formInlineValue.isActive=1
}else{
formInlineValue.isActive=0
}
console.log(formInlineValue)
enumValueAdd(formInlineValue).then((res: any) => { enumValueAdd(formInlineValue).then((res: any) => {
if (res.code == 200) { if (res.code == 200) {
queryListData.description =queryName.value queryListData.description =queryName.value
@ -459,40 +434,66 @@ const dialogVisibleDelete1 = (done: () => void) => {
} }
}) })
} }
// //
const fromUpDate = reactive({ const fromUpDate = reactive({
id: '', id: '',
enumTypeId: '', enumTypeId: '',
value:'', value:'',
description : '', description : '',
orderNumber: 0, orderNumber: 0,
isActive: false isActive: 0
}) })
const addhandleSwitchChange = (value: any) => {
formInlineValue.isActive = value ? 1 : 0
}
const edithandleSwitchChange = (value: any) => {
fromUpDate.isActive = value ? 1 : 0
}
const visibleValueEdit = ref(false) const visibleValueEdit = ref(false)
const enumValueEdit = (data: any) => { const enumValueEdit = (data: any) => {
visibleValueEdit.value = true visibleValueEdit.value = true
fromUpDate.id = data.row.id fromUpDate.id = data.row.id
fromUpDate.enumTypeId = data.row.enumTypeId fromUpDate.enumTypeId = data.row.enumTypeIdStr
fromUpDate.value = data.row.value fromUpDate.value = data.row.value
fromUpDate.description = data.row.description fromUpDate.description = data.row.description
fromUpDate.orderNumber = data.row.orderNumber fromUpDate.orderNumber = data.row.orderNumber
if(data.row.isActive==1){ fromUpDate.isActive = data.row.isActive
fromUpDate.isActive = true
}else{
fromUpDate.isActive = false
}
} }
const edithandleClosevalue = (done: () => void) => { const edithandleClosevalue = (done: () => void) => {
visibleValueEdit.value = false visibleValueEdit.value = false
} }
const EditonSubmitvalue = () => {
formRef.value.validate((valid: any) => {
if (valid) {
enumValuepageEdit(fromUpDate).then((res: any) => {
if (res.code == 200) {
queryListData.description =queryName.value
queryListData.enumTypeId = enumTypeId.value
queryListData.pageNum=currentPage.value,
queryListData.pageSize=currentPageSize.value
queryenumValueMethod(queryListData)
ElMessage({
message: res.msg,
type: 'success',
})
} else {
ElMessage.error({
message: res.msg,
type: 'error',
})
}
})
visibleValueEdit.value = false
}
})
}
onMounted(() => { onMounted(() => {
enumTreeTypeList() enumTreeTypeList()
const firstNode = TypeTree.value.store.root.childNodes[0];
/*if (firstNode) {
handleNodeClick(firstNode.data, null, TypeTree.value);
}*/
}) })
</script> </script>

View File

@ -11,7 +11,7 @@
</el-form-item> </el-form-item>
<el-form-item prop="iotModelCode" :label="ModelFieldsEnums['iotModelCode']"> <el-form-item prop="iotModelCode" :label="ModelFieldsEnums['iotModelCode']">
<el-input <el-input
:disabled="modelDialogState === ModelDialogTitleStateType['detail']" :disabled="!(modelDialogState === ModelDialogTitleStateType['add'])"
v-model="modelForm.iotModelCode" v-model="modelForm.iotModelCode"
:placeholder="'请输入' + ModelFieldsEnums['iotModelCode']" :placeholder="'请输入' + ModelFieldsEnums['iotModelCode']"
></el-input> ></el-input>

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="controlPage" v-loading="loading" element-loading-text="提交中..."> <div class="controlPage" v-loading="loading" element-loading-text="提交中...">
<h1 class="pageName">设备服务调试</h1> <h1 class="pageName">遥调遥控调试</h1>
<div class="control"> <div class="control">
<el-form :model="serviceType147Form" :rules="validData(147)" ref="serviceType147Ref"> <el-form :model="serviceType147Form" :rules="validData(147)" ref="serviceType147Ref">
<el-row> <el-row>
<el-col :span="1" :offset="2"> <el-col :span="2">
<div class="title">遥控</div> <div class="title">遥控</div>
</el-col> </el-col>
<el-col :span="8" :offset="1"> <el-col :span="10">
<div class="center"> <div class="center">
<el-form-item label="服务名" prop="serviceName"> <el-form-item label="服务名" prop="serviceName">
<el-select v-model="serviceType147Form.serviceName" placeholder="请选择服务名"> <el-select v-model="serviceType147Form.serviceName" placeholder="请选择服务名">
@ -21,7 +21,7 @@
</el-form-item> </el-form-item>
</div> </div>
</el-col> </el-col>
<el-col :span="6" :offset="1"> <el-col :span="7" :offset="1">
<el-form-item label="设定值" prop="opValue"> <el-form-item label="设定值" prop="opValue">
<el-select v-model="serviceType147Form.opValue" placeholder="请选择设定值"> <el-select v-model="serviceType147Form.opValue" placeholder="请选择设定值">
<el-option :value="0"></el-option> <el-option :value="0"></el-option>
@ -29,7 +29,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="4" :offset="1"> <el-col :span="2" :offset="1">
<el-button type="primary" @click="submit(147)">提交</el-button> <el-button type="primary" @click="submit(147)">提交</el-button>
</el-col> </el-col>
</el-row> </el-row>
@ -37,10 +37,10 @@
<br /> <br />
<el-form :model="serviceType146Form" :rules="validData(146)" ref="serviceType146Ref"> <el-form :model="serviceType146Form" :rules="validData(146)" ref="serviceType146Ref">
<el-row> <el-row>
<el-col :span="1" :offset="2"> <el-col :span="2">
<div class="title">遥调</div> <div class="title">遥调</div>
</el-col> </el-col>
<el-col :span="8" :offset="1"> <el-col :span="10">
<div class="center"> <div class="center">
<el-form-item label="服务名" prop="serviceName"> <el-form-item label="服务名" prop="serviceName">
<el-select v-model="serviceType146Form.serviceName" placeholder="请选择服务名"> <el-select v-model="serviceType146Form.serviceName" placeholder="请选择服务名">
@ -54,12 +54,12 @@
</el-form-item> </el-form-item>
</div> </div>
</el-col> </el-col>
<el-col :span="6" :offset="1"> <el-col :span="7" :offset="1">
<el-form-item label="设定值" prop="opValue"> <el-form-item label="设定值" prop="opValue">
<el-input v-model="serviceType146Form.opValue" placeholder="请输入设定值"> </el-input> <el-input v-model="serviceType146Form.opValue" placeholder="请输入设定值"> </el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="4" :offset="1"> <el-col :span="2" :offset="1">
<el-button type="primary" @click="submit(146)">提交</el-button> <el-button type="primary" @click="submit(146)">提交</el-button>
</el-col> </el-col>
</el-row> </el-row>

View File

@ -49,11 +49,13 @@
<el-table-column property="model" label="规格型号" /> <el-table-column property="model" label="规格型号" />
<el-table-column property="address" label="操作"> <el-table-column property="address" label="操作">
<template #default="scope"> <template #default="scope">
<span style="color: #0064aa; cursor: pointer" @click="openControl(scope)">调控 </span> <span style="color: #0064aa; cursor: pointer" @click="openMeasure(scope)">量测</span>
<span style="color: #0064aa"> | </span> <span style="color: #0064aa"> | </span>
<span style="color: #0064aa; cursor: pointer" @click="viewDeviceDetails(scope)">查看 </span> <span style="color: #0064aa; cursor: pointer" @click="openControl(scope)">调控</span>
<span style="color: #0064aa"> | </span> <span style="color: #0064aa"> | </span>
<span style="color: #0064aa; cursor: pointer" @click="deviceDeletion(scope)"> 删除 </span> <span style="color: #0064aa; cursor: pointer" @click="viewDeviceDetails(scope)">查看</span>
<span style="color: #0064aa"> | </span>
<span style="color: #0064aa; cursor: pointer" @click="deviceDeletion(scope)">删除</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -420,16 +422,39 @@
</template> </template>
</el-dialog> </el-dialog>
</el-container> </el-container>
<el-dialog v-model="showControlPage"> <el-dialog v-model="showControlPage" :width="800">
<div class="controlSlot"> <div class="controlSlot">
<ControlPage :deviceId="contorlData.deviceId" :iotModelId="contorlData.iotModelId" :show="showControlPage"></ControlPage> <ControlPage :deviceId="contorlData.deviceId" :iotModelId="contorlData.iotModelId" :show="showControlPage"></ControlPage>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog v-model="showMeasure" title="量测" :width="800">
<template #header>
<div class="measureSlotHeader">
<span style="font-size: 20px">量测</span>
<div class="measureSlotHeaderRight">
<span>自动更新</span>
<el-switch
v-model="measureData.autoUpdate"
class="ml-2"
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
></el-switch>
</div>
</div>
</template>
<div class="measureSlot">
<MeasurementPage
:show="showMeasure"
:deviceId="measureData.deviceId"
:iotModelId="measureData.iotModelId"
:autoUpdate="measureData.autoUpdate"
></MeasurementPage>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, nextTick } from 'vue' import { ref, reactive, onMounted, nextTick, watch } from 'vue'
import { Search, CirclePlusFilled, Upload, Download } from '@element-plus/icons-vue' import { Search, CirclePlusFilled, Upload, Download } from '@element-plus/icons-vue'
import { import {
equipTree, equipTree,
@ -447,6 +472,7 @@ import { ElTable, ElMessage, ElMessageBox } from 'element-plus'
import { useAdminInfo } from '/@/stores/adminInfo' import { useAdminInfo } from '/@/stores/adminInfo'
import { encrypt_aes, generateRandomNumber } from '/@/utils/crypto' import { encrypt_aes, generateRandomNumber } from '/@/utils/crypto'
import ControlPage from './control.vue' import ControlPage from './control.vue'
import MeasurementPage from './measurement.vue'
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
interface Tree { interface Tree {
@ -955,12 +981,34 @@ const contorlData = reactive({
const openControl = (data: any) => { const openControl = (data: any) => {
contorlData.deviceId = data.row.id contorlData.deviceId = data.row.id
contorlData.iotModelId = data.row.iotModelId contorlData.iotModelId = data.row.iotModelId
if(contorlData.iotModelId){ if (contorlData.iotModelId) {
showControlPage.value = true showControlPage.value = true
}else{ } else {
ElMessage.warning('该设备没有绑定物模型!') ElMessage.warning('该设备没有绑定物模型!')
} }
} }
const measureData = reactive({
deviceId: '',
iotModelId: '',
autoUpdate: false,
})
const showMeasure = ref(false)
const openMeasure = (data: any) => {
measureData.deviceId = data.row.id
measureData.iotModelId = data.row.iotModelId
if (measureData.iotModelId) {
showMeasure.value = true
} else {
ElMessage.warning('该设备没有绑定物模型!')
}
}
watch(
showMeasure,
(newVal: boolean) => {
!newVal && (measureData.autoUpdate = false)
}
)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -1052,4 +1100,10 @@ $paginationHeight: 32px;
} }
} }
} }
.measureSlotHeader {
display: flex;
.measureSlotHeaderRight {
margin-left: 20px;
}
}
</style> </style>

View File

@ -0,0 +1,233 @@
<template>
<div class="measurement">
<el-table :columns="tableColumn" :data="tableData" @sort-change="sortChange" max-height="495">
<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"
>
<template #default="scope">
<div v-if="item.prop === 'realTimeValue'">
<el-button @click="openChart(scope.row)" text type="primary">{{ scope.row.realTimeValue }}</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div>
<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 } 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,
},
autoUpdate: {
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'
},
{
label: '子系统',
prop: 'subSystem',
align: 'left',
width: 110,
sortable:false
},
{
label: '实时值',
prop: 'realTimeValue',
width: 140,
align: 'center',
sortable:false
},
]
const tableData = ref<any[]>([])
const getAttributeList = () => {
const requestData: GetModelAttributeType = {
iotModelId: props.iotModelId,
pageNum: pageSetting.current,
pageSize: pageSetting.pageSize,
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] ?? '-'
return {
...item,
realTimeValue: 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()
}
const openChart = (data: any) => {}
watch(
() => props.show,
(newVal) => {
newVal && getCompleteData()
},
{
immediate: true,
}
)
let autoUpdateTimer: any = null
watch(
() => props.autoUpdate,
(newVal) => {
if (newVal) {
if (!autoUpdateTimer) {
autoUpdateTimer = setInterval(() => {
console.log('刷新')
getCompleteData()
}, 2000)
}
} else {
clearInterval(autoUpdateTimer)
autoUpdateTimer = null
}
}
)
</script>
<style scoped></style>

View File

@ -0,0 +1,237 @@
<template>
<div class="FanList-content">
<el-row :gutter="10">
<el-col :span="8" v-for="(item, index) in FanList" style="margin-bottom: 10px;">
<div class="grid-content ep-bg-purple">
<div class="FanList-panel" :class="item.standard==true ? 'wind-mark' : 'wind-default'">
<div class="fanlist-top">
<span :class="item.standard==true ? 'wind-mark-icon' : 'fanlist-icon'">
<img :class="item.standard==true ? '' : 'wind-picture'" src="~assets/dashboard/biaogan.png" alt="">
</span>
<span class="fanlist-name"> {{ item.windTurbine }}</span>
<el-tag class="tag-panel" :class="getItemClass(item)" type="primary">{{ item.windStatus }}</el-tag>
</div>
<div class="fanlist-main">
<el-row>
<el-col :span="24">
<div class="fanlist-pic">
<!-- <div class="mask">
<div class="leafs">
<div class="leaf_1"></div>
<div class="leaf_2"></div>
<div class="leaf_3"></div>
</div>
</div>-->
<img src="~assets/dashboard/fanlist1.png" alt="">
<!-- <img class="fc" src="~assets/dashboard/fanlist1.png" alt=""></img>-->
<!-- <img :style="{ transform: `rotate(${rotationDegrees}deg)` }" src="~assets/dashboard/fanlist1.png" alt="">-->
</div>
<div class="fanlist-pic">
<img src="~assets/dashboard/fanlist2.png" alt="">
</div>
</el-col>
</el-row>
<el-row class="fanlist-data">
<el-col :span="8">
<div class="fanlist-text"><span class="content-number">{{ item.windSpeed }}</span><span>风速m/s</span></div>
</el-col>
<el-col :span="8">
<div class="fanlist-text"><span class="content-number">{{ item.power }}</span><span>功率MW</span></div>
</el-col>
<el-col :span="8">
<div class="fanlist-text"><span class="content-number">{{ item.dayGeneration }}</span><span>日发电量kWh</span></div>
</el-col>
</el-row>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref ,onMounted,onUnmounted } from 'vue'
const FanList = ref([
{windSpeed:'10.34','power':'10.34','dayGeneration':'10.34',windStatus:'并网',windTurbine:'A-001',standard:true},
{windSpeed:'34','power':'10.34','dayGeneration':'10.34',windStatus:'待机',windTurbine:'A-002',standard:false},
{windSpeed:'11','power':'10.34','dayGeneration':'10.34',windStatus:'启动',windTurbine:'A-003',standard:false},
{windSpeed:'45','power':'10.34','dayGeneration':'10.34',windStatus:'解缆',windTurbine:'A-004',standard:false},
{windSpeed:'12','power':'10.34','dayGeneration':'10.34',windStatus:'电网故障停机',windTurbine:'A-005',standard:false},
{windSpeed:'10','power':'10.34','dayGeneration':'10.34',windStatus:'维护',windTurbine:'A-006',standard:false}
])
const getItemClass = (FanList) => {
switch (FanList.windStatus) {
case '并网':
return 'is-primary';
case '并网- AGC限功率':
return 'is-primary';
case '解缆':
return 'is-info';
case '待机':
return 'is-warning';
case '故障':
return 'is-warning';
case '启动':
return 'is-success';
case '电网故障停机':
return 'is-danger';
case '风速超限停机':
return 'is-danger';
case '故障停机':
return 'is-danger';
case '塔基手动停机':
return 'is-danger';
case '远程监控停机':
return 'is-danger';
case 'AGC停机':
return 'is-danger';
case '集控停机':
return 'is-danger';
case '小风停机':
return 'is-danger';
case '维护':
return 'is-maintenance';
default:
return '';
}
return { FanList, getItemClass };
};
const rotationDegrees = ref(0)
const speed = 5; //
function startRotation(speed) {
return setInterval(() => {
rotationDegrees.value += 100;
}, 100); // 100
}
let intervalId;
onMounted(() => {
//
intervalId = startRotation(speed);
});
onUnmounted(() => {
//
clearInterval(intervalId);
});
</script>
<style scoped lang="scss">
.wind-mark{
background-image: linear-gradient(140deg, #FFE3E3 0%, #FFFFFF 93%);
border: 1px solid #FFE3E3;
}
.wind-default{
background-image: linear-gradient(180deg, #F0F6FF 0%, #FFFFFF 50%);
border: 1px solid #E1EDF6;
}
.wind-picture{
display: none;
}
.wind-mark-icon{
background: #FFFFFF;
border: 1px solid #FFE3E3;
border-radius: 8px 0 100px 0;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
vertical-align: middle;
}
.FanList-panel{
border-radius: 8px;
.fanlist-top{
display: flex;
width: 100%;
}
.fanlist-icon{
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
vertical-align: middle;
}
.fanlist-name{
width: 100%;
margin-top: 5px;
}
.tag-panel{
border-radius: 0 8px 0 0;
line-height: 20px;
}
.is-primary{
background: rgba(2,119,179,0.20);
border: 1px solid #0277B3;
color: #0277B3;
}
.is-success{
background: rgba(6,180,41,0.20);
border: 1px solid #06B429;
color: #06B429;
}
.is-info{
background: rgba(48,89,236,0.20);
border: 1px solid #3059EC;
color: #3059EC;
}
.is-warning{
background: rgba(255,126,0,0.20);
border: 1px solid #FF7E00;
color: #FF7E00;
}
.is-danger{
background: rgba(254,55,49,0.20);
border: 1px solid #FE3731;
color: #FE3731;
}
.is-offline{
background: rgba(153,153,153,0.20);
border: 1px solid #999999;
color: #999999;
}
.is-maintenance{
background: rgba(0,160,150,0.20);
border: 1px solid #00A096;
color: #00A096;
}
.fanlist-main{
width: 100%;
display: flex;
padding: 10px;
text-align: center;
.fanlist-pic{
display: flex;
justify-content: center;
align-items: center;
margin-top: -10px;
.mask{
width: 43px;
height: 38px;
}
}
.fanlist-text{
margin-top:30px;
.content-number{
color: #333333;
font-size: 20px;
}
}
.fanlist-text span{
display: inline-block;
}
}
}
</style>

View File

@ -0,0 +1,459 @@
<template>
<div class="linkMonitor">
<el-container class="containerPart">
<el-aside class="asidePart">
<el-header class="asideHeader">
<el-input
v-model="searchLinkTreeInputValue"
:suffix-icon="Search"
clearable
@input="debounceSearchLinkTree"
placeholder="搜索"
class="searchLinkTreeInput"
></el-input>
</el-header>
<el-main class="treeMain">
<el-tree
ref="linkTreeRef"
node-key="id"
:data="treeData"
@node-click="linkTreeNodeClick"
@node-expand="linkTreeNodeExpand"
@node-collapse="linkTreeNodeCollapse"
:props="LinkTreePropReplace"
class="treePart"
highlight-current
:render-content="renderContent"
:default-expanded-keys="linkTreeNodeExpandKeys"
>
<!-- <template #default="{ node, data }">
<span>{{ node.label }}</span>
</template> -->
</el-tree>
</el-main>
</el-aside>
<el-container class="mainContainer">
<el-header class="mainHeader">
<div class="mainHeaderCenter">
<el-input clearable placeholder="请输入链路名称" class="linkMonitorInput" v-model="linkMonitorInputValue"></el-input>
<el-button :icon="Search" type="primary" @click="searchLinkMonitor">查询</el-button>
</div>
</el-header>
<el-main class="mainMain">
<div class="tabsPart">
<el-table :data="linkMonitorTableData" class="tablePart" highlight-current-row>
<el-table-column prop="linkName" :label="LinkMonitorFieldsEnums['linkName']" align="left"> </el-table-column>
<el-table-column prop="status" :label="LinkMonitorFieldsEnums['status']" align="center" width="80">
<template #default="scope">
<div class="status-container">
<span
:class="{
'status-dot-online': !scope.row.status,
'status-dot-offline': scope.row.status,
}"
></span>
</div>
</template>
</el-table-column>
<el-table-column prop="protocolName" :label="LinkMonitorFieldsEnums['protocolName']" align="center">
<template #default="scope"> </template>
</el-table-column>
<!-- <el-table-column prop="remoteRegulation" :label="LinkMonitorFieldsEnums['remoteRegulation']" align="center">
<template #default="scope">
<span style="cursor: pointer; color: #0064aa; font-weight: 600">查看参数</span>
</template>
</el-table-column> -->
<el-table-column label="操作" width="200" align="center">
<template #default="scope">
<div class="tableOperate">
<a @click="opneLog(scope.row)">日志</a>
<a>|</a>
<a @click="startLink(scope.row)">启动</a>
<a>|</a>
<el-popconfirm title="确定停止么?" @confirm="stopLink(scope.row)">
<template #reference>
<a style="color: red">停止</a>
</template>
</el-popconfirm>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="mainFooter">
<el-pagination
v-model:current-page="paginationOptions.current"
v-model:page-size="paginationOptions.pageSize"
:total="paginationOptions.total"
:page-sizes="paginationOptions.pageSizes"
background
:pager-count="7"
layout="prev, pager, next, jumper,sizes,total"
@change="getcurrentPage"
></el-pagination>
</div>
</el-main>
</el-container>
</el-container>
<el-dialog v-model="linkLogVisible" title="日志" @close="closeLinklog" :width="900">
<template #footer> </template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, nextTick } from 'vue'
import { ElContainer, ElAside, ElHeader, ElMain, ElTree, ElInput, ElMessage, ElButton, ElTable, TreeInstance } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { LinkMonitorFieldsEnums, LinkMonitorTreeType, LinkMonitorTableType } from './type'
import { getNodeListReq, getLinkListReq } from '/@/api/backend/linkMonitor/request'
import { debounce, cloneDeep } from 'lodash'
const LinkTreePropReplace = {
label: 'nodeName',
children: 'children',
}
const linkTreeRef = ref<TreeInstance>()
const treeData = ref<LinkMonitorTreeType[]>([])
const originTreeData = ref<LinkMonitorTreeType[]>([])
const curContextMenuTreeData = ref<LinkMonitorTreeType>()
const searchLinkTreeInputValue = ref('')
const getlinkTreeList = () => {
return new Promise((resolve) => {
getNodeListReq()
.then((res) => {
if (res.success) {
originTreeData.value = res.data!
treeData.value = res.data!
// originTreeData.value = res.data!
// const other = [
// {
// id: '5555',
// nodeName: '111112222222222222222222222222222222',
// nodeIp: '',
// orgName: '',
// orgId: '',
// revision: 0,
// children: [
// {
// id: '1111',
// nodeName: '22222',
// nodeIp: '',
// orgName: '',
// orgId: '',
// revision: 0,
// children: [],
// },
// ],
// },
// ]
// originTreeData.value[0].children = [...other]
// originTreeData.value[1] = {
// id: '55654',
// nodeName: '33',
// nodeIp: '',
// orgName: '',
// orgId: '',
// revision: 0,
// children: [
// {
// id: '324232',
// nodeName: '11',
// nodeIp: '',
// orgName: '',
// orgId: '',
// revision: 0,
// children: [],
// },
// ],
// }
// const tree: LinkMonitorTreeType[] = [...originTreeData.value]
// treeData.value = tree
resolve(res.data![0])
} else {
ElMessage.error(res.msg)
}
})
.catch((err) => {
ElMessage.error(err?.response?.data?.msg ?? '查询失败')
})
})
}
getlinkTreeList().then((res) => {
curContextMenuTreeData.value = cloneDeep(res as LinkMonitorTreeType)
linkTreeRef.value?.setCurrentKey(curContextMenuTreeData.value!.id!)
getlinkMonitorList(curContextMenuTreeData.value!.id!, linkMonitorInputValue.value)
})
const searchLinkTree = () => {
if (searchLinkTreeInputValue.value === '') {
treeData.value = originTreeData.value
nextTick(() => {
linkTreeRef.value?.setCurrentKey(curContextMenuTreeData.value!.id!)
})
return
}
treeData.value = filterAndHighlightTreeArray(cloneDeep(originTreeData.value), searchLinkTreeInputValue.value) as LinkMonitorTreeType[]
linkTreeNodeExpandKeys.value = Array.from(new Set([...linkTreeNodeExpandKeys.value, ...getAllIds(treeData.value)]))
nextTick(() => {
linkTreeRef.value?.setCurrentKey(curContextMenuTreeData.value!.id!)
})
}
const getAllIds = (nodes: LinkMonitorTreeType[]) => {
let ids: any = []
nodes.forEach((node) => {
ids.push(node.id)
if (node.children && node.children.length > 0) {
ids = ids.concat(getAllIds(node.children))
}
})
return ids
}
const filterAndHighlightTreeArray = (treeArray: LinkMonitorTreeType[], searchQuery: string) => {
return treeArray.map((tree: LinkMonitorTreeType) => filterAndHighlightTree(tree, searchQuery)).filter(Boolean)
}
const filterAndHighlightTree = (tree: LinkMonitorTreeType, searchQuery: string): (LinkMonitorTreeType & { isMatch?: boolean }) | null => {
const isMatch = tree.nodeName.toLowerCase().includes(searchQuery.toLowerCase())
if (isMatch) {
return {
...tree,
children: tree?.children?.map((child) => filterAndHighlightTree(child, searchQuery)).filter(Boolean) as LinkMonitorTreeType[],
isMatch: true,
}
}
if (tree.children && tree.children.length > 0) {
const filteredChildren = tree.children.map((child) => filterAndHighlightTree(child, searchQuery)).filter(Boolean) as LinkMonitorTreeType[]
if (filteredChildren.length > 0) {
return { ...tree, children: filteredChildren }
}
}
return null
}
const debounceSearchLinkTree = debounce(searchLinkTree, 200)
const linkTreeNodeExpandKeys = ref([] as any)
const linkTreeNodeExpand = (node: any) => {
if (!linkTreeNodeExpandKeys.value.includes(node.id)) {
linkTreeNodeExpandKeys.value.push(node.id)
}
}
const linkTreeNodeCollapse = (node: any) => {
const index = linkTreeNodeExpandKeys.value.indexOf(node.id)
if (index > -1) {
linkTreeNodeExpandKeys.value.splice(index, 1)
}
}
const linkTreeNodeClick = (target: LinkMonitorTreeType) => {
curContextMenuTreeData.value = cloneDeep(target)
getlinkMonitorList(curContextMenuTreeData.value!.id!, linkMonitorInputValue.value)
}
const renderContent = (h: any, { node, data }: any) => {
const label = node.label || ''
const searchTerm = searchLinkTreeInputValue.value.toLowerCase()
const startIndex = label.toLowerCase().indexOf(searchTerm)
if (startIndex === -1) {
return h('span', {}, label)
}
const beforeMatch = label.substring(0, startIndex)
const matchText = label.substring(startIndex, startIndex + searchLinkTreeInputValue.value.length)
const afterMatch = label.substring(startIndex + searchLinkTreeInputValue.value.length)
return h('span', {}, [beforeMatch, h('span', { style: { color: '#f14c4c', fontWeight: 'bold' } }, matchText), afterMatch])
}
const linkMonitorInputValue = ref('')
const searchLinkMonitor = () => {
if (linkMonitorInputValue.value === '') {
getlinkMonitorList(curContextMenuTreeData.value!.id!)
return
}
getlinkMonitorList(curContextMenuTreeData.value!.id!, linkMonitorInputValue.value)
}
const linkMonitorTableData = ref<LinkMonitorTableType[]>([])
//
const paginationOptions = reactive({
current: 1,
pageSize: 10,
total: 0,
pageSizes: [10, 20, 30],
})
const getcurrentPage = () => {
getlinkMonitorList(curContextMenuTreeData.value!.id!, linkMonitorInputValue.value)
}
const protocolList = [
{ label: 'IEC104主 *', value: 8 },
{ label: 'IEC104从 *', value: 9 },
{ label: 'MODBUSRTU主 *', value: 12 },
{ label: 'MODBUSTCP主 *', value: 16 },
]
const getlinkMonitorList = (nodeId: string, linkName?: string) => {
getLinkListReq({ nodeId, linkName, pageNum: paginationOptions.current, pageSize: paginationOptions.pageSize })
.then((res) => {
if (res.rows) {
linkMonitorTableData.value = res.rows.map((item) => {
return {
...item,
protocolName: protocolList.find((i) => i.value === item.protocol)?.label,
}
})
paginationOptions.total = res.total
} else {
ElMessage.error(res.msg ?? '查询失败')
}
})
.catch((err) => {
ElMessage.error(err?.response?.data?.msg ?? '查询失败')
})
}
const linkLogVisible = ref(false)
const closeLinklog = () => {
linkLogVisible.value = false
}
const opneLog = (val: LinkMonitorTableType) => {
// linkLogVisible.value = true
}
const startLink = (val: LinkMonitorTableType) => {
console.log('startLink')
}
const stopLink = (val: LinkMonitorTableType) => {
console.log('stopLink')
}
</script>
<style lang="scss" scoped>
$headerHeight: 60px;
$defaultBackgroundColor: #fff;
$defaultAsideWidth: 260px;
$paginationHeight: 32px;
.linkMonitor {
position: relative;
width: 100%;
height: 100%;
.containerPart {
width: 100%;
height: 100%;
.asidePart {
width: $defaultAsideWidth;
height: 100%;
border-right: 1px solid #eaebed;
.asideHeader {
display: flex;
justify-content: center;
align-items: center;
.searchLinkTreeInput {
height: 40px;
}
}
.treeMain {
height: calc(100% - $headerHeight);
overflow-y: auto;
.treePart {
min-width: 100%;
overflow: auto;
display: inline-block;
background-color: $defaultBackgroundColor;
}
}
}
.mainContainer {
width: calc(100% - $defaultAsideWidth);
height: 100%;
.mainHeader {
display: flex;
justify-content: space-between;
align-items: center;
height: $headerHeight;
padding: 0 20px;
border-bottom: 1px solid #eaebed;
.linkMonitorInput {
height: 40px;
width: 220px;
}
.mainHeaderCenter {
display: flex;
justify-content: space-between;
align-items: center;
width: 320px;
}
}
.mainMain {
height: calc(100% - $headerHeight);
.tabsPart {
height: calc(100% - $paginationHeight);
padding-bottom: 5px;
:deep(.el-tabs__content) {
height: calc(100% - 55px);
.el-tab-pane {
height: 100%;
}
}
.tablePart {
height: 100%;
}
.tableOperate {
display: flex;
justify-content: center;
align-items: center;
a {
margin: 5px;
color: #0064aa;
font-weight: 600;
&:hover {
cursor: pointer;
}
}
}
}
.mainFooter {
display: flex;
justify-content: right;
background-color: #fff;
}
}
}
}
.modelOperate {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
width: 80px;
height: 150px;
.el-button {
margin: 0;
}
}
}
//
.status-container {
display: flex;
justify-content: center;
align-items: center;
}
.status-dot-online,
.status-dot-offline {
width: 10px;
height: 10px;
border-radius: 50%;
}
.status-dot-online {
background-color: #06b429;
}
.status-dot-offline {
background-color: red;
}
.highlight {
background-color: yellow; /* 高亮背景颜色 */
color: red; /* 字体颜色 */
font-weight: bold; /* 加粗字体 */
}
</style>

View File

@ -0,0 +1,45 @@
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 LinkMonitorTreeType = {
id?: string
nodeName: string
nodeIp: string
orgName?: string
orgId: string
revision?: number
children?: LinkMonitorTreeType[]
}
export type LinkMonitorTableType = {
id?: string
linkName: string
protocol?: number
protocolName?: string
params: string
nodeName: string
nodeId: string
revision: number
}
export type GetLinkMonitorTableParam = {
nodeId: string
linkName?: string
pageNum: number
pageSize: number
}
export enum LinkMonitorFieldsEnums {
'id' = 'id',
'linkName' = '链路名称',
'status' = '状态',
'protocolName' = '协议类型',
}

View File

@ -103,7 +103,8 @@ let timer: number
const config = useConfig() const config = useConfig()
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
toggleDark(config.layout.isDark)
// TODO toggleDark(config.layout.isDark)
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const usernameRef = ref<InputInstance>() const usernameRef = ref<InputInstance>()

File diff suppressed because one or more lines are too long