duyufeng 8 months ago
parent
commit
d1189bcae9
  1. BIN
      hx-ai-intelligent/public/asset/file/emissionSource.xlsx
  2. BIN
      hx-ai-intelligent/public/asset/file/energyConsumption.xlsx
  3. 10
      hx-ai-intelligent/src/api/coldAndHeatSources.ts
  4. 13
      hx-ai-intelligent/src/api/waterSystem.ts
  5. 38
      hx-ai-intelligent/src/router/equipmentControl.ts
  6. 164
      hx-ai-intelligent/src/util/ExcelUtil.js
  7. 5
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonAssets/carbonAssetsDetail/index.vue
  8. 3
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/carbonEmissions/index.vue
  9. 10
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/config.ts
  10. 4
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/energyConsumption/index.vue
  11. 80
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/quickCalculation/index.vue
  12. 16
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonInventoryCheck/fillInPage/index.vue
  13. 37
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonInventoryCheck/index.vue
  14. 12
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonPlanning/category/categoryDeatil.vue
  15. 2
      hx-ai-intelligent/src/view/carbonEmissionManage/carbonPlanning/category/index.vue
  16. 1
      hx-ai-intelligent/src/view/equipmentControl/airConditionControlSystem/tabs3.vue
  17. 542
      hx-ai-intelligent/src/view/equipmentControl/coldAndHeatSources/index.vue
  18. 111
      hx-ai-intelligent/src/view/equipmentControl/coldAndHeatSources/position.ts
  19. 6
      hx-ai-intelligent/src/view/equipmentControl/image/coldAndHeatSources/arrow.svg
  20. BIN
      hx-ai-intelligent/src/view/equipmentControl/image/coldAndHeatSources/sensor.png
  21. 1
      hx-ai-intelligent/src/view/equipmentControl/lightingManage/tabs3.vue
  22. 10
      hx-ai-intelligent/src/view/equipmentControl/planToAdd/index.vue
  23. 4
      hx-ai-intelligent/src/view/equipmentControl/ventilationSystem/components/fanControl.vue
  24. 1
      hx-ai-intelligent/src/view/equipmentControl/ventilationSystem/components/fanLog.vue
  25. 429
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/component/logTab.vue
  26. 381
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/component/planTab.vue
  27. 282
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/device.ts
  28. 150
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/deviceInfo.vue
  29. 291
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/deviceItem.vue
  30. 30
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/deviceLine.vue
  31. BIN
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/images/open.png
  32. 663
      hx-ai-intelligent/src/view/equipmentControl/waterSystem/index.vue
  33. 4
      hx-ai-intelligent/src/view/monitor/deviceMonitor/page.vue
  34. 5
      hx-ai-intelligent/src/view/monitor/deviceMonitor/table/index.vue
  35. 22
      hx-ai-intelligent/src/view/monitor/deviceMonitor/tree/index.vue
  36. 150
      hx-ai-intelligent/src/view/monitor/energyMonitor/analysisTable/index.vue
  37. 5
      hx-ai-intelligent/src/view/monitor/energyMonitor/graphTable/index.vue
  38. 26
      hx-ai-intelligent/src/view/monitor/energyMonitor/page.vue
  39. 26
      hx-ai-intelligent/src/view/monitor/energyMonitor/tree/index.vue
  40. 5
      hx-ai-intelligent/src/view/monitor/environmentMonitor/averageData/index.vue
  41. 6
      hx-ai-intelligent/src/view/monitor/environmentMonitor/historyData/index.vue
  42. 4
      lib/component/tree/tree-api.vue
  43. 4
      lib/util/xlsx-util.ts

BIN
hx-ai-intelligent/public/asset/file/emissionSource.xlsx

Binary file not shown.

BIN
hx-ai-intelligent/public/asset/file/energyConsumption.xlsx

Binary file not shown.

10
hx-ai-intelligent/src/api/coldAndHeatSources.ts

@ -0,0 +1,10 @@
import { BASE_URL } from './index';
export enum coldAndHeatSourcesApi {
getUserWaterPumpState = `${BASE_URL}/api/tempSysCtrl/getUserWaterPumpState`, // 用户水泵查询最新状态
getLandWaterPumpState = `${BASE_URL}/api/tempSysCtrl/getLandWaterPumpState`, // 地源水泵查询最新状态
getLandHeatPumpState = `${BASE_URL}/api/tempSysCtrl/getLandHeatPumpState`, //螺旋式地源热泵 - 查询最新状态
getEnergyTankState = `${BASE_URL}/api/tempSysCtrl/getEnergyTankState`, //冷热水双蓄储能罐 - 查询最新状态
getCoolPumpState = `${BASE_URL}/api/tempSysCtrl/getCoolPumpState`, //释冷泵 - 查询最新状态
getAirHeatPumpState = `${BASE_URL}/api/tempSysCtrl/getAirHeatPumpState`, //空气源热泵 - 查询最新状态
}

13
hx-ai-intelligent/src/api/waterSystem.ts

@ -2,6 +2,8 @@
const prefix = '/carbon-smart/api';
// 通风系统相关接口
export enum waterSys {
// 首页 ====================================================
// 获得污水池状态
getPool1 = prefix + '/waterSysCtrl/getSewagePoolState',
// 获得阀门状态
@ -10,4 +12,15 @@ export enum waterSys {
getPool2 = prefix + '/waterSysCtrl/getCollectPoolState',
// 获得水泵状态
getPump = prefix + '/waterSysCtrl/getPumpState',
// 提交场景模式修改
submitList = prefix + '/waterSysCtrl/changeToSceneMode',
// 计划 tab1 ===============================================
submitTableData = prefix + '/waterSysCtrl/refreshPlanStatus',
// 日志 tab2 ===============================================
// 获得设备日志
getLog = prefix + '/waterSysInfo/pageAbleLog',
// 获得日志详情
getLogDetail = prefix + '/waterSysInfo/fullLog',
}

38
hx-ai-intelligent/src/router/equipmentControl.ts

@ -119,25 +119,25 @@ const equipmentControl = {
},
],
},
// {
// path: 'waterSystem',
// name: 'waterSystem',
// meta: { title: '给排水系统', hideChildren: true, icon: 'shebeiqunkong' },
// component: Base,
// redirect: { name: 'waternControlSystemIndex' },
// children: [
// {
// path: 'index',
// name: 'waternControlSystemIndex',
// component: () => import('/@/view/equipmentControl/waterSystem/index.vue'),
// meta: {
// title: '给排水系统',
// keepAlive: false,
// // backApi: [],
// },
// },
// ],
// },
{
path: 'waterSystem',
name: 'waterSystem',
meta: { title: '给排水系统', hideChildren: true, icon: 'shebeiqunkong' },
component: Base,
redirect: { name: 'waternControlSystemIndex' },
children: [
{
path: 'index',
name: 'waternControlSystemIndex',
component: () => import('/@/view/equipmentControl/waterSystem/index.vue'),
meta: {
title: '给排水系统',
keepAlive: false,
// backApi: [],
},
},
],
},
{
path: 'planToAdd',
name: 'planToAdd',

164
hx-ai-intelligent/src/util/ExcelUtil.js

@ -10,10 +10,43 @@ import FileSaver from 'file-saver';
// export default exportExcel;
// 导出excel文件
export function exportExcel (tableColumns,data,fillName,isMerge,start,end) {
/**
*
* @param {*} tableColumns 表头
* @param {*} data 数据
* @param {*} fillName 文件名
* @param {*} isMerge 是否合并单元格
* @param {*} firstKey 第一个字段的key用来判断序号列是否合并
* @param {*} start 合并单元格开始列
* @param {*} end 合并单元格结束列
* @returns
*/
export function exportExcel (tableColumns,data,fillName,isMerge = false, firstKey = '',start = 0, end = 0) {
debugger
if (!data || data.length == 0) {
return;
}
if (isMerge) {
// 需要合并序号
for (let i = 0; i < data.length; i++) {
// 自定义单元格内容,这里返回序号
if (i == 0) {
data[i].index = 1;
// return 1;
} else if (data[i - 1][firstKey] == data[i][firstKey]) {
data[i].index = data[i - 1].index;
// return data.value[index].index;
} else {
data[i].index = data[i - 1].index + 1;
}
}
} else {
// 不需要合并序号
for (let i = 0; i < data.length; i++) {
data[i].index = i + 1;
}
}
// 创建工作簿
const workbook = new ExcelJS.Workbook();
// 添加工作表,名为sheet1
@ -33,27 +66,22 @@ export function exportExcel (tableColumns,data,fillName,isMerge,start,end) {
columns.push({
header: tableColumns[i].title,
key: tableColumns[i].dataIndex,
width: tableColumns[i].width / 5,
width: tableColumns[i].width ? tableColumns[i].width / 5 : 20,
});
}
}
//传入的数据
const list = data;
//格式化数据
const datas = formatJson(filterVal, list);
// // 导出数据列表
// const data = [
// { 姓名: '张三', 年龄: 18, 身高: 175, 体重: 74 },
// { 姓名: '李四', 年龄: 22, 身高: 177, 体重: 84 },
// { 姓名: '王五', 年龄: 53, 身高: 155, 体重: 64 },
// ];
// 获取表头所有键
// const headers = Object.keys(data[0]);
// 获取表头
sheet1.columns = columns;
// // 将标题写入第一行
// sheet1.addRow(tHeader);
// 将数据写入工作表
datas.forEach((row) => {
// const values = Object.values(row);
@ -62,80 +90,30 @@ export function exportExcel (tableColumns,data,fillName,isMerge,start,end) {
// 判断是否合并单元格
if (isMerge) {
debugger
// 遍历列,从第一列到 end 列
// 遍历列,从 start 列到 end 列
for (let col = start; col <= end; col++) {
// let mergeStartRow = 2; // 每次新列开始时,重置起始行
for (let row = 3; row <= datas.length + 1; row++) {
const currentCellValue = sheet1.getCell(row, col).value;
const previousCellValue = sheet1.getCell(row - 1, col).value;
let currentCellValue_1 = ''
let previousCellValue_1 = ''
// 从第二列开始就要看前一列是否已经合并
let isMerged = true
if (col > 1) {
currentCellValue_1 = sheet1.getCell(row, col-1).value;
previousCellValue_1 = sheet1.getCell(row - 1, col-1).value;
isMerged = ifMerged(sheet1,row - 1, col-1,row, col-1)
}
if (currentCellValue === previousCellValue && currentCellValue_1 === previousCellValue_1) {
// 当前列有变化,检查是否需要合并前面的单元格
// if (mergeStartRow < row - 1) {
// 只有在前面的行有超过1个时才合并
sheet1.mergeCells(row, col, row - 1, col);
// }
// 更新起始行
// mergeStartRow = row;
if (currentCellValue === previousCellValue && isMerged) {
// 检查是上边需要合并的单元格是否已经合并
const mergeInfo = getMergeInfo(sheet1, row - 1, col)
if ( mergeInfo.isMerged ) {
sheet1.unMergeCells( mergeInfo.startRow, col, row - 1, col);
sheet1.mergeCells(mergeInfo.startRow, col, row, col);
} else {
sheet1.mergeCells(row, col, row - 1, col);
}
}
// // 如果是最后一行,检查是否需要合并
// if (row === datas.length + 1 && mergeStartRow < row) {
// sheet1.mergeCells(mergeStartRow, col, row, col);
// }
}
}
// 从第一列开始逐列检查,前提是前面的列已合并
// for (let col = start; col <= end; col++) {
// let startRow = 2; // 从数据开始的第二行开始检查
// let endRow = 2;
// while (endRow <= sheet1.rowCount) {
// let currentValue = sheet1.getCell(endRow, col).value;
// let prevValue = sheet1.getCell(endRow - 1, col).value;
// // 如果当前值等于前一个值,且前面的列是合并的,则继续合并
// if (currentValue === prevValue) {
// let mergeAllowed = true;
// for (let prevCol = 1; prevCol < col; prevCol++) {
// let range = sheet1.getCell(endRow - 1, prevCol).master;
// if (range && range.row !== startRow) {
// mergeAllowed = false;
// break;
// }
// }
// if (mergeAllowed) {
// endRow++;
// } else {
// // 不允许合并,直接移动起始行到当前行
// startRow = endRow;
// endRow++;
// }
// } else {
// // 当前值不等于前一个值或合并不允许,进行合并操作
// if (startRow < endRow - 1) {
// sheet1.mergeCells(startRow, col, endRow - 1, col);
// }
// startRow = endRow;
// endRow++;
// }
// }
// // 处理最后一段相同的单元格
// if (startRow < endRow - 1) {
// sheet1.mergeCells(startRow, col, endRow - 1, col);
// }
// }
}
@ -208,3 +186,43 @@ function formatJson (filterVal, jsonData) {
return jsonData.map((v) => filterVal.map((j) => v[j]));
};
/**
* 获取给定行和列的单元格是否为合并单元格并返回合并起始行
* @param {Worksheet} worksheet - ExcelJS 工作表对象
* @param {number} row - 单元格的行号 ( 1 开始)
* @param {number} col - 单元格的列号 ( 1 开始)
* @returns {Object} - 返回一个对象包含 isMerged startRow 属性
*/
function getMergeInfo(worksheet, row, col) {
// 遍历所有的合并范围
for (const mergeRange in worksheet._merges) {
if (worksheet._merges.hasOwnProperty(mergeRange)) {
const { top, left, bottom, right } = worksheet._merges[mergeRange];
// 检查行列是否在当前合并范围内
if (row >= top && row <= bottom && col >= left && col <= right) {
return { isMerged: true, startRow: top }; // 找到合并范围,返回合并信息
}
}
}
return { isMerged: false, startRow: null }; // 单元格不在任何合并范围内
}
// 函数:检查两个单元格是否属于同一个合并区域
function ifMerged(worksheet, row1, col1, row2, col2) {
const merges = worksheet._merges; // 获取所有的合并区域
for (let mergeAddress in merges) {
const mergeRange = merges[mergeAddress];
const { top, left, bottom, right } = mergeRange;
const isCell1InRange = (row1 >= top && row1 <= bottom && col1 >= left && col1 <= right);
const isCell2InRange = (row2 >= top && row2 <= bottom && col2 >= left && col2 <= right);
if (isCell1InRange && isCell2InRange) {
return true;
}
}
return false;
}

5
hx-ai-intelligent/src/view/carbonEmissionManage/carbonAssets/carbonAssetsDetail/index.vue

@ -48,7 +48,7 @@
</a-form>
</a-card>
</div>
<div style="display: flex; margin-top: 20px; height: calc(84% - 20px)">
<div style="display: flex; margin-top: 20px; height: calc(85% - 20px)">
<div class="detailTable">
<ns-view-list-table v-bind="tableConfig" :model="data" ref="mainRef" :scroll="{ x: 1280 }">
<template #bodyCell="{ column, text, record }">
@ -523,6 +523,7 @@
{
title: '更新时间',
width: 150,
ellipsis: true,
dataIndex: 'updateTime',
},
],
@ -826,7 +827,7 @@
padding: 16px;
}
.search {
height: 16%;
height: 15%;
}
.detailTable {
width: 70%;

3
hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/carbonEmissions/index.vue

@ -13,6 +13,9 @@
<a-button type="primary" ghost style="margin-left: 6px" @click="reset">重置</a-button>
</span>
</template>
<template #bodyCell="{ column, text }">
<span>{{ text || '-' }}</span>
</template>
</a-table>
<!-- <a-pagination
:current="queryParams.pageNum"

10
hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/config.ts

@ -146,7 +146,7 @@ export const drawerColumns = [
dataIndex: 'dataSources',
},
];
export const setFactorConfig = (orgId) => {
export const setFactorConfig = (orgId, treeId, tableId) => {
return ref({
api: carbonEmissionFactorLibrary.getTableList,
params: { orgId, pageNum: 1, pageSize: 9999, emissionList: [0] },
@ -155,7 +155,8 @@ export const setFactorConfig = (orgId) => {
icon: 'deviceType',
title: '排放分类',
},
params: { orgId},
selectedKeys: treeId,
params: { orgId },
dynamicParams: { emissionList: 'id[]' },
defaultExpandAll: true,
// checkable:true,
@ -175,7 +176,10 @@ export const setFactorConfig = (orgId) => {
],
},
},
rowSelection: { type: 'radio' },
rowSelection: {
type: 'radio',
selectedRowKeys: tableId,
},
columns: [
{
title: '序号',

4
hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/energyConsumption/index.vue

@ -297,9 +297,7 @@
indexName: '能源种类', //
message: [
{ label: '1、若必填项未填写,则不能进行导入操作' },
{ label: `2、当重复时,则更新数据。` },
{ label: '3、数据将从模版的第五行进行导入。' },
{ label: '4、文件导入勿超过5MB。' },
{ label: '2、文件导入勿超过5MB。' },
],
},
},

80
hx-ai-intelligent/src/view/carbonEmissionManage/carbonEmissionStatistics/quickCalculation/index.vue

@ -21,20 +21,22 @@
:auto-expand-parent="autoExpandParent"
:selectedKeys="selectedKeys"
:tree-data="gData"
:show-line="{ showLeafIcon: false }"
show-line
@expand="onExpand"
@select="onSelect"
style="padding: 0 16px !important">
<template #title="data">
<span
<!-- <span
v-if="data.energyType && searchValue && data.energyType.indexOf(searchValue) > -1">
{{ data.energyType.substring(0, data.energyType.indexOf(searchValue)) }}
<span style="color: #f50">{{ searchValue }}</span>
{{
data.energyType.substring(data.energyType.indexOf(searchValue) + searchValue.length)
}}
</span>
<span v-else>{{ data.energyType }}</span>
</span> -->
<span v-if="data.code">{{ truncatedName(data.energyType + data.code) }}</span>
<span v-else>{{ truncatedName(data.energyType) }}</span>
</template>
</a-tree>
</div>
@ -130,8 +132,8 @@
</template>
<script lang="ts" setup>
import { ref, watch, toRaw, defineExpose } from 'vue';
import type { TreeProps } from 'ant-design-vue';
import { ref, watch, toRaw, defineExpose, nextTick } from 'vue';
import { message, TreeProps } from 'ant-design-vue';
import { Pagination, Modal } from 'ant-design-vue';
import { columns, drawerColumns } from '../config';
import { http } from '/nerv-lib/util/http';
@ -225,6 +227,12 @@
expandedKeys.value = keys;
autoExpandParent.value = false;
};
const truncatedName = (name) => {
if (name.length > 8) {
return name.substring(0, 8) + '...';
}
return name;
};
//
const energyType = ref();
const onSelect = (selectedKey: string[], info: any) => {
@ -245,16 +253,22 @@
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
expandedKeys.value = expanded;
// expandedKeys.value = expanded;
searchValue.value = value;
autoExpandParent.value = true;
});
//
const onSearchTreeData = () => {};
const getTreeQuery = ref({
orgId: orgId.value,
});
const onSearchTreeData = () => {
getTreeQuery.value.energyType = searchValue.value;
getTreeData();
};
const statsId = ref();
//
const getTreeData = () => {
fetch(quickCalculation.carbonQuickTree, { orgId: orgId.value }).then((res) => {
fetch(quickCalculation.carbonQuickTree, getTreeQuery.value).then((res) => {
gData.value = res.data;
energyType.value = gData.value[0].children[0].id;
statsId.value = gData.value[0].children[0].id;
@ -271,9 +285,12 @@
});
const tableData = ref([]);
const emissionSources = ref();
const treeId = ref([]);
const tableId = ref([]);
const tableConfig = ref({
title: '排放因子库',
api: quickCalculation.queryCarbonEmissionPage,
rowSelection: null,
params: {
orgId,
energyType,
@ -307,12 +324,6 @@
dataIndex: 'carbonEmissionSuffix',
},
{
title: '更新时间',
className: 'updateTime',
dataIndex: 'updateTime',
ellipsis: true,
},
{
title: '启用时间',
className: 'startTime',
dataIndex: 'startTime',
@ -327,6 +338,12 @@
className: 'dataSources',
dataIndex: 'dataSources',
},
{
title: '更新时间',
className: 'updateTime',
dataIndex: 'updateTime',
ellipsis: true,
},
],
columnActions: {
title: '操作',
@ -342,8 +359,10 @@
formState.value.factorId = record.factorId;
text.value = '编辑';
visible.value = true;
emissionSources.value = record.factorId; //todo
queryData.value.factorId = emissionSources.value; //todo
emissionSources.value = record.factorId;
queryData.value.factorId = emissionSources.value;
treeId.value = [record.treeId];
tableId.value = [record.factorId];
getNewTable();
},
},
@ -450,6 +469,7 @@
selectedRowKeys.value = [];
formState.value = {};
formRef.value.resetFields();
message.success('操作成功!');
mainRef.value?.nsTableRef.reload();
});
} else {
@ -458,6 +478,7 @@
selectedRowKeys.value = [];
formState.value = {};
formRef.value.resetFields();
message.success('操作成功!');
mainRef.value?.nsTableRef.reload();
});
}
@ -487,9 +508,15 @@
};
const openVisible = ref(false);
const setFactorRef = ref();
const config = setFactorConfig(orgId.value);
const config = setFactorConfig(orgId.value, treeId.value, tableId.value);
const selectFactor = () => {
openVisible.value = true;
nextTick(() => {
setFactorRef.value.nsTableRef.params.emissionList = treeId.value;
setFactorRef.value.nsTableRef.treeElRef.selectedKeys = treeId.value;
setFactorRef.value.nsTableRef.rowSelection.selectedRowKeys = tableId.value;
setFactorRef.value.nsTableRef.reload();
});
};
const btnClick = () => {
let selectRowKeys = setFactorRef.value?.nsTableRef.tableState.selectedRowKeys;
@ -624,6 +651,25 @@
:deep(.ant-modal-footer) {
border-top: 10px solid #f0f0f0 !important;
}
:deep(.ns-table-container) {
background: white;
}
:deep(.ns-part-tree) {
border-radius: 8px;
background: rgba(255, 255, 255, 1);
box-shadow: 0px 2px 20px rgb(69 123 234 / 20%);
}
:deep(.ns-table-search) {
border-radius: 8px;
background: rgba(255, 255, 255, 1);
box-shadow: 0px 2px 20px rgb(69 123 234 / 20%);
}
:deep(.ns-table-main) {
margin-top: 20px !important;
border-radius: 8px;
background: rgba(255, 255, 255, 1);
box-shadow: 0px 2px 20px rgb(69 123 234 / 20%);
}
</style>
<style scoped>
th.column-money,

16
hx-ai-intelligent/src/view/carbonEmissionManage/carbonInventoryCheck/fillInPage/index.vue

@ -1193,11 +1193,11 @@
return regex.test(filename);
};
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
const filename = file.name;
if (!isValidFileName(filename)) {
message.error('文件名不符合规则');
return Upload.LIST_IGNORE; //
}
// const filename = file.name;
// if (!isValidFileName(filename)) {
// message.error('');
// return Upload.LIST_IGNORE; //
// }
return false;
};
const handleChange = (info: UploadChangeParam) => {
@ -1284,14 +1284,16 @@
NsMessage.warn('请选择因子');
return;
} else {
if (newTableData.value.emissionFactorUnits === carbonEmission.value) {
if (newTableData.value[0].emissionFactorUnits === carbonEmission.value) {
newTableData.value = setFactorRef.value?.nsTableRef.tableState.selectedRows;
selectedRowKeysEdit.value = setFactorRef.value?.nsTableRef.tableState.selectedRowKeys;
editFormState.value.emissionFactors = newTableData.value[0].emissionFactors;
editFormState.value.factorId = selectedRowKeysEdit.value[0];
openVisible.value = false;
} else {
NsMessage.warn('因子值单位不统一,请重新选择!');
NsMessage.warn(
'因子值单位与当前因子值单位(' + carbonEmission.value + ')不统一,请重新选择!',
);
}
}
};

37
hx-ai-intelligent/src/view/carbonEmissionManage/carbonInventoryCheck/index.vue

@ -39,11 +39,7 @@
placeholder="请输入报告名称" />
</a-form-item>
<a-form-item ref="name" label="报告年度" name="reportYear">
<a-date-picker
v-model:value="formState.reportYear"
@openChange="openChange"
picker="year"
valueFormat="YYYY" />
<a-date-picker v-model:value="formState.reportYear" picker="year" valueFormat="YYYY" />
</a-form-item>
<a-form-item ref="name" label="适用标准" name="genericStandard">
<a-input
@ -64,7 +60,6 @@
<a-form-item ref="name" label="报告范围" name="reportScope">
<a-range-picker
v-model:value="formState.reportScope"
:defaultPickerValue="defaultPickerValue"
picker="month"
:disabledDate="disabledDate"
valueFormat="YYYY-MM" />
@ -111,20 +106,22 @@
const selectChange = (value) => {
formState.value.reportScope = '';
};
const defaultPickerValue = ref([
dayjs('2020'), //
dayjs('2020'), //
]);
const openChange = (status) => {
if (status === false) {
if (formState.value.reportYear) {
defaultPickerValue.value = [
dayjs(formState.value.reportYear),
dayjs(formState.value.reportYear),
];
}
}
};
// const defaultPickerValue = ref([]);
// const open = ref(false);
// const openChange = (status) => {
// debugger;
// open.value = status;
// if (formState.value.reportYear) {
// defaultPickerValue.value = [
// dayjs(formState.value.reportYear),
// dayjs(formState.value.reportYear),
// ];
// open.value = false;
// nextTick(() => {
// open.value = status; //
// });
// }
// };
// form
const rules: Record<string, Rule[]> = {
reportName: [{ required: true, message: '请输入报告名称', trigger: 'change' }],

12
hx-ai-intelligent/src/view/carbonEmissionManage/carbonPlanning/category/categoryDeatil.vue

@ -417,7 +417,6 @@
const editData = (record) => {
open.value = true;
if (record.isLastYear !== undefined) {
formState.value.ids = [record.id];
if (record.lastYear === '是') {
formState.value.isLastYear = 1;
disabled.value = false;
@ -425,10 +424,12 @@
formState.value.isLastYear = 0;
disabled.value = true;
}
formState.value.conversionRate = record.conversionRate;
formState.value.lastYearList = [record.lastYearActualUsage];
formState.value.budget = record.budget;
}
formState.value.ids = [record.id];
formState.value.conversionRate = record.conversionRate;
formState.value.lastYearList = [record.lastYearActualUsage];
formState.value.ids = [record.id];
formState.value.budget = record.budget;
};
const disabled = ref(true);
const selectChange = (value) => {
@ -995,6 +996,9 @@
:deep(.ant-card-bordered) {
border: unset;
}
:deep(.ant-input-number-handler-wrap){
display: none;
}
</style>
<style scoped>
.editable-row-operations a {

2
hx-ai-intelligent/src/view/carbonEmissionManage/carbonPlanning/category/index.vue

@ -674,7 +674,7 @@
}
.contant {
width: 100%;
height: calc(94% - 5vh);
height: calc(94% - 5vh - 24px);
overflow-y: auto;
padding: 12px;
.chartsPart {

1
hx-ai-intelligent/src/view/equipmentControl/airConditionControlSystem/tabs3.vue

@ -30,6 +30,7 @@
v-model:pageSize="pagination.pageSize"
show-size-changer
:total="pagination.total"
show-less-items
@change="getTable(true)" />
<div style="width: 100%; height: 40px"></div>

542
hx-ai-intelligent/src/view/equipmentControl/coldAndHeatSources/index.vue

@ -16,14 +16,38 @@
<div style="width: 100%; height: 20px; color: rgb(128, 255, 255)">
{{ item.deviceInfoName }}
</div>
<div v-if="item.autoStatus" style="width: 100%; height: 20px">
模式: <span style="color: #fff">{{ item.autoStatus.label }}</span>
</div>
<div v-if="item.temp" style="width: 100%; height: 20px">
设定温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
<img
style="position: absolute; width: 135px; height: 130px; left: -20px; top: 40px"
:src="item.url" />
</div>
</template>
<!-- 空气源 - 传感器 -->
<template v-for="(item, index) in airSourceSensor" :key="index">
<div
style="
width: 135px;
height: 200px;
position: relative;
font-size: 12px;
position: absolute;
color: #ffff80;
z-index: 2;
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<div style="width: 100%; height: 20px">
模式: <span style="color: #fff">{{ item.type }}</span>
出水温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
<div style="width: 100%; height: 20px">
设定温度: <span style="color: #fff">{{ item.number }}</span>
流量: <span style="color: #fff">{{ item.traffic }} {{ item.trafficUnit }}</span>
</div>
<img
style="position: absolute; width: 135px; height: 130px; left: -20px; top: 40px"
style="position: absolute; width: 28px; height: 28px; left: -20px; top: 40px"
:src="item.url" />
</div>
</template>
@ -37,7 +61,7 @@
bottom: 50%;
position: absolute;
">
<a-switch
<!-- <a-switch
:checked="selectAllCheckbox === 1 ? true : false"
size="small"
:disabled="true"
@ -46,10 +70,34 @@
'blue-background': selectAllCheckbox === 1 ? true : false,
'grey-background': selectAllCheckbox === 1 ? false : true,
}"
@change="toggleAllSelection" />
@change="toggleAllSelection" /> -->
<img style="display: flex; width: 111px; height: 100px" :src="waterPumpSrc" />
<div style="width: 100%; height: 20px; color: rgb(128, 255, 255)"> 水泵 </div>
</div>
<!-- 水泵 传感器 -->
<template v-for="(item, index) in waterSensor" :key="index">
<div
style="
width: 135px;
height: 200px;
position: relative;
font-size: 12px;
position: absolute;
color: #ffff80;
z-index: 2;
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<div style="width: 100%; height: 20px">
温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
<div style="width: 100%; height: 20px">
流量: <span style="color: #fff">{{ item.traffic }} {{ item.trafficUnit }}</span>
</div>
<img
style="position: absolute; width: 28px; height: 28px; left: -20px; top: 40px"
:src="item.url" />
</div>
</template>
<!-- 螺杆式地源热泵 -->
<template v-for="(item, index) in screwGeothermalHeatPump" :key="index">
<div
@ -69,11 +117,11 @@
<div style="width: 100%; height: 20px; color: rgb(128, 255, 255); z-index: 2">
{{ item.deviceInfoName }}
</div>
<div style="width: 100%; height: 20px">
模式: <span style="color: #fff">{{ item.type }}</span>
<div v-if="item.autoStatus" style="width: 100%; height: 20px">
模式: <span style="color: #fff">{{ item.autoStatus.label }}</span>
</div>
<div style="width: 100%; height: 20px">
设定温度: <span style="color: #fff">{{ item.number }}</span>
<div v-if="item.temp" style="width: 100%; height: 20px">
设定温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
</div>
</template>
@ -90,15 +138,17 @@
z-index: 2;
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<div style="width: 100%; height: 20px; color: rgb(128, 255, 255)">
<div
style="
width: 100%;
height: 20px;
color: rgb(128, 255, 255);
position: absolute;
top: 40px;
left: 5px;
">
{{ item.deviceInfoName }}
</div>
<div style="width: 100%; height: 20px">
出水温度: <span style="color: #fff">{{ item.number }}</span>
</div>
<div style="width: 100%; height: 20px">
流量: <span style="color: #fff">{{ item.lNumber }}</span>
</div>
<img
style="position: absolute; width: 117.42px; height: 106.31px; left: -20px; top: 60px"
:src="item.url" />
@ -117,17 +167,14 @@
z-index: 2;
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<div style="width: 100%; height: 20px; color: rgb(128, 255, 255)">
<div style="width: 100%; height: 20px; color: rgb(128, 255, 255); margin-top: 20px">
{{ item.deviceInfoName }}
</div>
<div style="width: 100%; height: 20px">
出水温度: <span style="color: #fff">{{ item.number }}</span>
</div>
<div style="width: 100%; height: 20px">
容量: <span style="color: #fff">{{ item.rNumber }}</span>
<div v-if="item.temp" style="width: 100%; height: 20px">
出水温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
<div style="width: 100%; height: 20px">
: <span style="color: #fff">{{ item.lNumber }}</span>
<div v-if="item.temp" style="width: 100%; height: 20px">
容量: <span style="color: #fff">{{ item.vol }} {{ item.volUnit }}</span>
</div>
<img
style="position: absolute; width: 110px; height: 110px; left: -20px; top: 80px"
@ -146,13 +193,14 @@
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<a-switch
:checked="item.user === 1 ? true : false"
v-if="item.switchStatus"
:checked="item?.switchStatus?.value === 1 ? true : false"
size="small"
:disabled="true"
style="position: absolute; left: 30px; bottom: 0px; z-index: 2"
:class="{
'blue-background': item.user === 1 ? true : false,
'grey-background': item.user === 1 ? false : true,
'blue-background': item?.switchStatus?.value === 1 ? true : false,
'grey-background': item?.switchStatus?.value === 1 ? false : true,
}" />
<img
style="
@ -168,7 +216,7 @@
font-size: 12px;
position: absolute;
left: -40px;
top: 30px;
top: 40px;
transform: rotateZ(-24deg);
"
>{{ item.deviceInfoName }}</div
@ -192,21 +240,43 @@
style="width: 226.19px; height: 176.19px; transform: rotateY(13deg)"
:src="manifoldSrc" />
</div>
<!-- 集水器 传感器 -->
<template v-for="(item, index) in manifoldSensor" :key="index">
<div
style="
width: 135px;
height: 200px;
position: relative;
font-size: 12px;
position: absolute;
color: #ffff80;
z-index: 2;
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<div style="width: 100%; height: 20px">
回水温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
<div style="width: 100%; height: 20px">
流量: <span style="color: #fff">{{ item.traffic }} {{ item.trafficUnit }}</span>
</div>
<img
style="
position: absolute;
width: 28px;
height: 28px;
left: -20px;
top: 40px;
transform: rotateX(-4deg) rotateY(180deg) rotateZ(1deg);
"
:src="item.url" />
</div>
</template>
<!-- 定压补水装置 -->
<template v-for="(item, index) in pressureWater" :key="index">
<div
style="width: 137px; height: 137px; position: relative; position: absolute; z-index: 2"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<img style="width: 137px; height: 127px; transform: rotateY(157deg)" :src="item.url" />
<a-switch
:checked="item.user === 1 ? true : false"
size="small"
:disabled="true"
style="position: absolute; left: 40px; bottom: 0px"
:class="{
'blue-background': item.user === 1 ? true : false,
'grey-background': item.user === 1 ? false : true,
}" />
<div
style="
width: 100%;
@ -217,9 +287,6 @@
font-size: 12px;
">
<div> {{ item.deviceInfoName }}</div>
<div style="width: 100%; height: 20px; color: #ffff80">
压差: <span style="color: #fff">{{ item.yc }}</span>
</div>
</div>
</div>
</template>
@ -294,13 +361,14 @@
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<a-switch
:checked="item.user === 1 ? true : false"
v-if="item.switchStatus"
:checked="item.switchStatus.value === 1 ? true : false"
size="small"
:disabled="true"
style="position: absolute; left: 30px; bottom: 0px; z-index: 2"
:class="{
'blue-background': item.user === 1 ? true : false,
'grey-background': item.user === 1 ? false : true,
'blue-background': item.switchStatus.value === 1 ? true : false,
'grey-background': item.switchStatus.value === 1 ? false : true,
}" />
<img
style="
@ -356,6 +424,30 @@
>
<img style="width: 290.75px; height: 215.29px" :src="soilCouplerSrc" />
</div>
<!-- 土壤 传感器 -->
<template v-for="(item, index) in soilCouplerSensor" :key="index">
<div
style="
width: 135px;
height: 80px;
position: relative;
font-size: 12px;
position: absolute;
color: #ffff80;
z-index: 2;
"
:style="{ left: item.style.mLeft, bottom: item.style.mBottom }">
<div style="width: 100%; height: 20px">
供水温度: <span style="color: #fff">{{ item.temp }} {{ item.tempUnit }}</span>
</div>
<div style="width: 100%; height: 20px">
流量: <span style="color: #fff">{{ item.traffic }} {{ item.trafficUnit }}</span>
</div>
<img
style="position: absolute; width: 28px; height: 28px; left: -20px; top: 40px"
:src="item.url" />
</div>
</template>
<!-- 线 -->
<template v-for="(item, index) in line" :key="index">
<div
@ -370,11 +462,27 @@
}">
</div>
</template>
<!-- 箭头 -->
<template v-for="(item, index) in arrow" :key="index">
<div
style="width: 50px; height: 20px; position: absolute"
:style="{
left: item.style.mLeft,
bottom: item.style.mBottom,
transform: item.style.transform,
}">
<img style="width: 50px; height: 20px" :src="item.url" />
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { http } from '/nerv-lib/util';
import { coldAndHeatSourcesApi } from '/@/api/coldAndHeatSources';
import { ventilating } from '/@/api/ventilatingSystem';
//
import airSourceThermalCollapseSrc from '../image/coldAndHeatSources/airSourceThermalCollapse.png';
import waterPumpSrc from '../image/coldAndHeatSources/waterPump.png';
@ -386,10 +494,24 @@
import softenedWaterTankSrc from '../image/coldAndHeatSources/softenedWaterTank.png';
import waterProcessorSrc from '../image/coldAndHeatSources/waterProcessor.png';
import soilCouplerSrc from '../image/coldAndHeatSources/soilCoupler.png';
import sensorSrc from '../image/coldAndHeatSources/sensor.png';
import blueGif from '../image/coldAndHeatSources/blue.gif';
import bluePng from '../image/coldAndHeatSources/blue.png';
import greenGif from '../image/coldAndHeatSources/green.gif';
import greenPng from '../image/coldAndHeatSources/green.png';
import arrowSrc from '../image/coldAndHeatSources/arrow.svg';
//
import {
userWaterPumpPosition,
waterPumpPosition,
airSourceThermalCollapsePosition,
screwGeothermalHeatPumpPosition,
airSourceSensorPosition,
} from './position';
//
import { items } from '/@/store/item';
//
const state = items();
const line = ref([
//线-
{
@ -759,9 +881,6 @@
]);
const airSourceThermalCollapse = ref([
{
deviceInfoName: '1#空气源热泵',
type: '制热',
number: '40℃',
style: {
mLeft: '17%',
mBottom: '54%',
@ -769,9 +888,6 @@
url: airSourceThermalCollapseSrc,
},
{
deviceInfoName: '2#空气源热泵',
type: '制热',
number: '40℃',
style: {
mLeft: '24%',
mBottom: '59%',
@ -779,9 +895,6 @@
url: airSourceThermalCollapseSrc,
},
{
deviceInfoName: '3#空气源热泵',
type: '制热',
number: '40℃',
style: {
mLeft: '31%',
mBottom: '66%',
@ -789,9 +902,6 @@
url: airSourceThermalCollapseSrc,
},
{
deviceInfoName: '4#空气源热泵',
type: '制热',
number: '40℃',
style: {
mLeft: '38%',
mBottom: '73%',
@ -802,9 +912,6 @@
//
const screwGeothermalHeatPump = ref([
{
deviceInfoName: '1#螺杆式地源热泵',
type: '制热',
number: '40℃',
style: {
mLeft: '9.5%',
mBottom: '22.5%',
@ -812,9 +919,6 @@
url: screwGeothermalHeatPumpSrc,
},
{
deviceInfoName: '2#螺杆式地源热泵',
type: '制热',
number: '40℃',
style: {
mLeft: '18.5%',
mBottom: '31.5%',
@ -826,8 +930,6 @@
const diluteCoolingPump = ref([
{
deviceInfoName: '稀冷泵',
number: '40℃',
lNumber: '139 m3/h',
style: {
mLeft: '30%',
mBottom: '29%',
@ -838,56 +940,40 @@
//
const coldWater = ref([
{
deviceInfoName: '冷热水双蓄储能罐',
number: '40℃',
lNumber: '139 m3/h',
rNumber: '135L',
url: coldWaterSrc,
style: {
mLeft: '36%',
mBottom: '39%',
},
url: coldWaterSrc,
},
]);
//
const userWaterPump = ref([
{
deviceInfoName: '1#用户水泵',
number: '40℃',
user: 1,
url: waterPumpSrc,
style: {
mLeft: '50%',
mBottom: '55.5%',
},
url: waterPumpSrc,
},
{
deviceInfoName: '2#用户水泵',
number: '40℃',
user: 1,
url: waterPumpSrc,
style: {
mLeft: '55%',
mBottom: '51%',
},
url: waterPumpSrc,
},
{
deviceInfoName: '3#用户水泵',
number: '40℃',
user: 1,
url: waterPumpSrc,
style: {
mLeft: '59%',
mBottom: '47%',
},
url: waterPumpSrc,
},
]);
//
const waterPump = ref([
{
deviceInfoName: '1#地源水泵',
number: '40℃',
user: 1,
style: {
mLeft: '65%',
mBottom: '41%',
@ -895,9 +981,6 @@
url: waterPumpSrc,
},
{
deviceInfoName: '2#地源水泵',
number: '40℃',
user: 1,
style: {
mLeft: '70%',
mBottom: '36%',
@ -905,9 +988,6 @@
url: waterPumpSrc,
},
{
deviceInfoName: '3#地源水泵',
number: '40℃',
user: 1,
style: {
mLeft: '75%',
mBottom: '31%',
@ -928,9 +1008,287 @@
url: pressureWaterSrc,
},
]);
const selectAllCheckbox = ref(1);
onMounted(() => {});
onUnmounted(() => {});
// -
const waterSensor = ref([
{
url: sensorSrc,
style: {
mLeft: '9%',
mBottom: '27%',
},
},
]);
//
const manifoldSensor = ref([
{
url: sensorSrc,
style: {
mLeft: '81%',
mBottom: '51%',
},
},
]);
//
const soilCouplerSensor = ref([
{
url: sensorSrc,
style: {
mLeft: '79%',
mBottom: '13%',
},
},
]);
// -
const airSourceSensor = ref([
{
style: {
mLeft: '26.5%',
mBottom: '34%',
},
url: sensorSrc,
},
{
style: {
mLeft: '32.5%',
mBottom: '39%',
},
url: sensorSrc,
},
{
style: {
mLeft: '38.5%',
mBottom: '44.5%',
},
url: sensorSrc,
},
{
style: {
mLeft: '47.5%',
mBottom: '52.5%',
},
url: sensorSrc,
},
]);
//
const arrow = ref([
{
url: arrowSrc,
style: {
mLeft: '6%',
mBottom: '42.5%',
transform: 'rotateZ(36deg)',
},
},
{
url: arrowSrc,
style: {
mLeft: '25%',
mBottom: '18%',
transform: 'rotateZ(36deg)',
},
},
{
url: arrowSrc,
style: {
mLeft: '76%',
mBottom: '13%',
transform: 'rotateZ(37deg)',
},
},
{
url: arrowSrc,
style: {
mLeft: '78%',
mBottom: '72%',
transform: 'rotateZ(149deg)',
},
},
]);
//
const getUserWaterPump = () => {
http
.get(coldAndHeatSourcesApi.getUserWaterPumpState, {
projectId: state.projectId,
siteId: state.siteId,
floor: 1,
})
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any, index: any) => {
userWaterPump.value[index] = {
deviceInfoName: item.deviceInfoName,
...item.record,
url: waterPumpSrc,
style: userWaterPumpPosition[index].style,
};
});
}
});
};
//
const getLandWaterPumpState = () => {
http
.get(coldAndHeatSourcesApi.getLandWaterPumpState, {
projectId: state.projectId,
siteId: state.siteId,
floor: 1,
})
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any, index: any) => {
waterPump.value[index] = {
deviceInfoName: item.deviceInfoName,
...item.record,
url: waterPumpSrc,
style: waterPumpPosition[index].style,
};
});
}
});
};
//
const getAirHeatPumpState = () => {
http
.get(coldAndHeatSourcesApi.getAirHeatPumpState, {
projectId: state.projectId,
siteId: state.siteId,
floor: 1,
})
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any, index: any) => {
airSourceThermalCollapse.value[index] = {
deviceInfoName: item.deviceInfoName,
...item.record,
url: airSourceThermalCollapseSrc,
style: airSourceThermalCollapsePosition[index].style,
};
});
}
});
};
//
const getLandHeatPumpState = () => {
http
.get(coldAndHeatSourcesApi.getLandHeatPumpState, {
projectId: state.projectId,
siteId: state.siteId,
floor: 1,
})
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any, index: any) => {
screwGeothermalHeatPump.value[index] = {
deviceInfoName: item.deviceInfoName,
...item.record,
url: screwGeothermalHeatPumpSrc,
style: screwGeothermalHeatPumpPosition[index].style,
};
});
}
});
};
//
const getEnergyTankState = () => {
http
.get(coldAndHeatSourcesApi.getEnergyTankState, {
projectId: state.projectId,
siteId: state.siteId,
floor: 1,
})
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any) => {
coldWater.value[0] = {
deviceInfoName: item.deviceInfoName,
...item.record,
};
});
}
});
};
//
const getSensorData = () => {
http
.get(ventilating.getSensorData, {
projectId: state.projectId,
siteId: state.siteId,
floor: 1,
})
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any, index: any) => {
if (index < 4) {
airSourceSensor.value[index] = {
deviceInfoName: item.deviceInfoName,
...item.record,
url: sensorSrc,
style: airSourceSensorPosition[index].style,
};
}
});
//
let data = res.data[Math.floor(Math.random() * res.data.length)];
waterSensor.value[0] = {
deviceInfoName: data.deviceInfoName,
...data.record,
url: sensorSrc,
style: {
mLeft: '9%',
mBottom: '27%',
},
};
// -
data = res.data[Math.floor(Math.random() * res.data.length)];
manifoldSensor.value[0] = {
deviceInfoName: data.deviceInfoName,
...data.record,
url: sensorSrc,
style: {
mLeft: '81%',
mBottom: '51%',
},
};
// -
data = res.data[Math.floor(Math.random() * res.data.length)];
soilCouplerSensor.value[0] = {
deviceInfoName: data.deviceInfoName,
...data.record,
url: sensorSrc,
style: {
mLeft: '79%',
mBottom: '13%',
},
};
}
});
};
//
const getData = () => {
//
getUserWaterPump();
//
getLandWaterPumpState();
//
getAirHeatPumpState();
//
getLandHeatPumpState();
//
getEnergyTankState();
//
getSensorData();
};
//
// const intervalId = setInterval(getList, 60000);
onMounted(() => {
getData();
});
onUnmounted(() => {
//
// clearInterval(intervalId);
});
</script>
<style lang="less" scoped>
.box-cold {

111
hx-ai-intelligent/src/view/equipmentControl/coldAndHeatSources/position.ts

@ -0,0 +1,111 @@
//用户水泵位置
export const userWaterPumpPosition = [
{
style: {
mLeft: '50%',
mBottom: '55.5%',
},
},
{
style: {
mLeft: '55%',
mBottom: '51%',
},
},
{
style: {
mLeft: '59%',
mBottom: '47%',
},
},
];
//地源水泵位置
export const waterPumpPosition = [
{
style: {
mLeft: '65%',
mBottom: '41%',
},
},
{
style: {
mLeft: '70%',
mBottom: '36%',
},
},
{
style: {
mLeft: '75%',
mBottom: '31%',
},
},
];
//空气源热泵
export const airSourceThermalCollapsePosition = [
{
style: {
mLeft: '17%',
mBottom: '54%',
},
},
{
style: {
mLeft: '24%',
mBottom: '59%',
},
},
{
style: {
mLeft: '31%',
mBottom: '66%',
},
},
{
style: {
mLeft: '38%',
mBottom: '73%',
},
},
];
//获取螺杆式地源热泵
export const screwGeothermalHeatPumpPosition = [
{
style: {
mLeft: '9.5%',
mBottom: '22.5%',
},
},
{
style: {
mLeft: '18.5%',
mBottom: '31.5%',
},
},
];
// 空气源 - 传感器
export const airSourceSensorPosition = [
{
style: {
mLeft: '26.5%',
mBottom: '34%',
},
},
{
style: {
mLeft: '32.5%',
mBottom: '39%',
},
},
{
style: {
mLeft: '38.5%',
mBottom: '44.5%',
},
},
{
style: {
mLeft: '47.5%',
mBottom: '52.5%',
},
},
];

6
hx-ai-intelligent/src/view/equipmentControl/image/coldAndHeatSources/arrow.svg

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="50px" height="22px" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1 0 0 1 -1191 -569 )">
<path d="M 49.73958333333333 7.581274382314695 C 49.91319444444444 7.752925877763329 50 7.972258344169916 50 8.239271781534459 L 50 13.732119635890768 C 50 13.999133073255308 49.91319444444444 14.2184655396619 49.73958333333333 14.390117035110533 C 49.56597222222222 14.561768530559167 49.34413580246914 14.647594278283485 49.074074074074076 14.647594278283485 L 12.962962962962962 14.647594278283485 L 12.962962962962962 21.055916775032507 C 12.962962962962962 21.456436931079324 12.779706790123457 21.732986562635453 12.413194444444445 21.88556566970091 C 12.046682098765434 22.038144776766362 11.709104938271606 21.99046380580841 11.400462962962964 21.742522756827046 L 0.28935185185185186 11.729518855656698 C 0.09645061728395089 11.538794971824878 0 11.31946250541829 0 11.071521456436932 C 0 10.804508019072387 0.09645061728395089 10.575639358474207 0.28935185185185186 10.384915474642392 L 11.400462962962964 0.2574772431729524 C 11.709104938271606 -0.009536194191596348 12.046682098765434 -0.06675335934114024 12.413194444444445 0.08582574772431828 C 12.779706790123457 0.2574772431729524 12.962962962962962 0.5340268747290853 12.962962962962962 0.9154746423927169 L 12.962962962962962 7.3237971391417425 L 49.074074074074076 7.3237971391417425 C 49.34413580246914 7.3237971391417425 49.56597222222222 7.409622886866061 49.73958333333333 7.581274382314695 Z " fill-rule="nonzero" fill="#ffff80" stroke="none" transform="matrix(1 0 0 1 1191 569 )" />
</g>
</svg>

BIN
hx-ai-intelligent/src/view/equipmentControl/image/coldAndHeatSources/sensor.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

1
hx-ai-intelligent/src/view/equipmentControl/lightingManage/tabs3.vue

@ -30,6 +30,7 @@
v-model:pageSize="pagination.pageSize"
show-size-changer
:total="pagination.total"
show-less-items
@change="getTable(true)" />
<div style="width: 100%; height: 40px"></div>

10
hx-ai-intelligent/src/view/equipmentControl/planToAdd/index.vue

@ -9,10 +9,10 @@
<div
:style="{
color: {
'0': '#ccc',
'1': 'rgba(255, 165, 0, 1)',
'2': 'rgb(57, 215, 187)',
'3': 'rgb(255, 0, 0)',
'4': '#ccc',
}[record.executeStatus.value],
}">
{{ record.executeStatus.label }}</div
@ -36,7 +36,7 @@
import { NsMessage } from '/nerv-lib/component';
import { getEnum } from '/@/api';
import { http } from '/nerv-lib/util';
import dayjs, { Dayjs } from 'dayjs';
//
const orgId = ref('');
const projectId = ref('');
@ -46,6 +46,7 @@
projectId.value = results.projectId;
const mainRef = ref(null);
const modalFormRef = ref(null);
const addPlan = () => {
if (mainRef.value.nsTableRef.treeElRef.selectedRow.node.planLib) {
http
@ -69,6 +70,10 @@
? record.startTime.substring(0, 10) + ' - ' + record.endTime.substring(0, 10)
: '未配置时间';
};
const disabledDate = (current: Dayjs) => {
// Can not select days before today and today
return current && current < dayjs().endOf('day');
};
const nsModalFormConfig = ref({
api: planToAddApi.updPlan,
data: {},
@ -92,6 +97,7 @@
componentProps: {
valueFormat: 'YYYY-MM-DD hh:mm:ss',
placeholder: ['开始日期', '结束日期'],
disabledDate: disabledDate,
},
rules: [
{

4
hx-ai-intelligent/src/view/equipmentControl/ventilationSystem/components/fanControl.vue

@ -85,9 +85,9 @@
:class="{ btn: true, selected: button.selected }"
class="zmhlbtn"
@click="changeLine(button)">
<div v-if="button.lockStatus" class="btn-back">
<!-- <div v-if="button.lockStatus" class="btn-back">
<stop-outlined />
</div>
</div> -->
{{ button.name }}
</button>
<div style="margin-top: 10px">

1
hx-ai-intelligent/src/view/equipmentControl/ventilationSystem/components/fanLog.vue

@ -30,6 +30,7 @@
v-model:pageSize="pagination.pageSize"
show-size-changer
:total="pagination.total"
show-less-items
@change="getTable(true)" />
<div style="width: 100%; height: 40px"></div>

429
hx-ai-intelligent/src/view/equipmentControl/waterSystem/component/logTab.vue

@ -0,0 +1,429 @@
<template>
<table class="custom-table table1">
<thead>
<tr :style="{ background: 'rgba(35,45,69)' }">
<th>序号</th>
<th>执行时间</th>
<th>操作内容</th>
<th>操作人</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr
:style="{ color: row.ctrlResult == 1 ? 'red' : 'white' }"
v-for="(row, index) in dataSource"
:key="index"
@click="handleRowClick(row.id, index)"
:class="index === trIndex ? 'isTrIndex' : ''">
<td>{{ index + 1 }}</td>
<td>{{ row.startTime }}</td>
<td>{{ row.operationContent }}</td>
<td>{{ row.createUser }}</td>
<td>{{ row.ctrlResult ? '失败' : '成功' }}</td>
</tr>
</tbody>
</table>
<a-pagination
style="margin-top: 10px; text-align: right"
v-model:current="pagination.pageNum"
v-model:pageSize="pagination.pageSize"
show-size-changer
:total="pagination.total"
@change="getTable(true)" />
<div style="width: 100%; height: 40px"></div>
<div class="out-dialog" :class="{ showDialog: logModalVisible }" v-if="logModalVisible">
<div class="content">
<div>
<div class="div-operation"></div>
<span class="text-operation">变更内容 </span>
</div>
<div>
<button :class="{ btn: true, selected: activeButton == 1 }" @click="changeBtn(1)"
>阀门</button
>
<button :class="{ btn: true, selected: activeButton == 2 }" @click="changeBtn(2)"
>水泵</button
>
</div>
<div class="device-list" v-if="activeButton == 1">
<div class="device-list-item" v-for="(item, index) in valveLogList" :key="index">
<div class="list-item-title">
<div class="item-title">
<img src="../images/device1.png" alt="" />
<span>{{ item.deviceGroupName }}</span>
</div>
</div>
<div class="list-item-main">
<div>
<div class="info">开度</div>
<div class="text">
<span>{{ item.openPercentBefore + '%' }}</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>{{ item.openPercentAfter + '%' }}</span>
</div>
</div>
<div></div>
</div>
</div>
<a-empty style="margin-top: 100px" v-if="valveLogList.length == 0">
<template #description> <span style="color: white">暂无数据</span></template>
</a-empty>
</div>
<div class="device-list" v-if="activeButton == 2">
<div class="device-list-item" v-for="(item, index) in pumpLogList" :key="index">
<div class="list-item-title">
<div class="item-title">
<img src="../images/device2.png" alt="" />
<span>{{ item.deviceGroupName }}</span>
</div>
</div>
<div class="list-item-main">
<div>
<div class="info">频率</div>
<div class="text">
<span>{{ item.frequencyBefore + 'MHz' }}</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>{{ item.frequencyAfter + 'MHz' }}</span>
</div>
</div>
<div>
<div class="info">开关</div>
<div class="text">
<span>{{ item.switchStatusBefore.label }}</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>{{ item.switchStatusAfter.label }}</span>
</div>
</div>
</div>
</div>
<a-empty style="margin-top: 100px" v-if="pumpLogList.length == 0">
<template #description> <span style="color: white">暂无数据</span></template>
</a-empty>
</div>
</div>
<div style="width: 100%; height: 160px"></div>
<div class="button-box">
<button class="cancel" @click="logModalVisible = false">关闭</button>
</div>
</div>
<div class="div-add">
<button class="add" @click="reset">刷新</button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { Pagination } from 'ant-design-vue';
import { http } from '/nerv-lib/util/http';
import { waterSys } from '/@/api/waterSystem';
//
import { items } from '/@/store/item';
// =======================================================
//
defineOptions({
components: {
'a-pagination': Pagination,
},
});
//
onMounted(() => {
getTable();
});
//
const state = items();
// ======================================================
//
const pagination = ref({
pageSize: 10,
pageNum: 1,
total: 0,
});
//
const dataSource = ref([]);
//
let trIndex = ref(-1);
//
const getTable = (changePage = false) => {
state.setLoading(true);
//
if (changePage) {
trIndex.value = -1;
logModalVisible.value = false;
pumpLogList.value.length = 0;
valveLogList.value.length = 0;
}
http
.get(waterSys.getLog, {
pageSize: pagination.value.pageSize,
pageNum: pagination.value.pageNum,
})
.then((res) => {
state.setLoading(false);
let data = res.data;
dataSource.value = data.records;
pagination.value.total = data.total;
})
.catch(() => {
state.setLoading(false);
});
};
//
const reset = () => {
trIndex.value = -1;
logModalVisible.value = false;
pagination.value = {
pageSize: 10,
pageNum: 1,
total: 0,
};
getTable();
};
//
const handleRowClick = (id: any, index: any) => {
trIndex.value = index;
getLogDetail(id);
};
// ==================================================
//
const logModalVisible = ref(false);
const getLogDetail = (id: any) => {
state.setLoading(true);
http
.get(waterSys.getLogDetail, { logId: id })
.then((res) => {
state.setLoading(false);
const data = res.data;
if (data.pumpLogList.length || data.valveLogList.length) {
//
logModalVisible.value = true;
pumpLogList.value = data.pumpLogList;
valveLogList.value = data.valveLogList;
} else {
return message.info('返回值无效');
}
})
.catch(() => {
state.setLoading(false);
});
};
// =1/=2
const activeButton = ref(1);
//
const changeBtn = (key: number) => {
activeButton.value = key;
};
//
const pumpLogList = ref<any>([]);
//
const valveLogList = ref<any>([]);
//
defineExpose({
reset,
});
</script>
<style lang="less" scoped>
@import '../../style/dialogStyle.less';
//
.div-add {
height: 64px;
display: flex;
justify-content: flex-end;
align-items: center;
position: fixed;
bottom: 0;
right: 0;
margin-right: 20px;
.add {
width: 74px;
height: 40px;
opacity: 1;
border-radius: 4px;
background: rgba(67, 136, 251, 1);
border: rgba(67, 136, 251, 1);
font-size: 14px;
font-weight: 400;
color: rgba(255, 255, 255, 1);
cursor: pointer;
}
}
//
.custom-table {
border-collapse: collapse;
width: 416px;
min-height: 60px;
max-height: 500px;
overflow-y: auto;
cursor: pointer;
color: rgba(255, 255, 255, 1);
}
.custom-table th,
.custom-table td {
border: 1px solid rgba(163, 192, 243, 1);
text-align: left;
padding: 8px;
text-align: center;
}
.table1 {
margin-top: 20px;
width: 100%;
border: 1px solid rgba(255, 255, 255);
border-radius: 5px;
background: rgba(255, 255, 255, 0.1);
.tabReboot,
.tabDelete {
border: none;
background-color: rgba(0, 0, 0, 0);
font-size: 14px;
font-weight: 400;
letter-spacing: 0;
line-height: 20px;
color: rgba(67, 136, 251, 1);
}
.tabReboot {
margin-right: 8px;
}
.isTrIndex {
background: rgba(67, 136, 251, 1);
}
}
::v-deep(.ant-transfer) {
// hover
.ant-transfer-list-content-item:hover {
background: black;
}
}
.btn {
width: 92px;
height: 40px;
border-radius: 4px;
opacity: 1;
margin-top: 10px;
margin-left: 15px;
font-size: 14px;
font-weight: 400;
opacity: 1;
border: 1px solid rgba(207, 212, 219, 1);
line-height: 20.27px;
color: white;
text-align: center;
vertical-align: top;
background-color: rgba(255, 255, 255, 0.1);
}
.selected {
background: linear-gradient(180deg, rgba(201, 245, 255, 1) 0%, rgba(138, 215, 255, 1) 100%);
color: rgba(0, 61, 90, 1);
border: 1px solid white;
}
.btn:hover {
background-color: rgba(207, 212, 219, 1);
}
.btn:active {
background-color: rgba(102, 102, 102, 1);
color: white;
}
.device-list {
margin-left: 15px;
margin-top: 15px;
width: calc(100% - 15px);
font-size: 13px;
height: auto;
display: flex;
gap: 15px;
flex-direction: column;
.device-list-item {
width: calc(100% - 15px);
box-sizing: border-box;
padding: 10px;
border: 2px solid #03407e;
border-radius: 4px;
background: rgba(0, 177, 255, 0.2);
display: flex;
gap: 10px;
flex-direction: column;
.list-item-title {
color: white;
display: flex;
justify-content: space-between;
.item-title {
img {
width: 25px;
}
span {
margin-left: 10px;
font-size: 16px;
}
}
.revoke {
text-align: center;
border: none;
border-radius: 4px;
padding: 5px 15px;
background: linear-gradient(
180deg,
rgba(255, 187, 0, 1) 0%,
rgba(255, 112, 3, 1) 91.21%,
rgba(255, 129, 3, 1) 100%
);
cursor: pointer;
}
}
.list-item-main {
display: flex;
> div {
flex: 1;
display: flex;
gap: 8px;
> .info {
text-align: center;
width: 6em;
height: 2.5em;
line-height: 2.5em;
border-radius: 4px;
color: white;
background: linear-gradient(
180deg,
rgba(86, 221, 253, 1) 0%,
rgba(25, 176, 255, 1) 100%
);
}
> .text {
:first-child {
color: white;
line-height: 2.5em;
}
img {
padding: 0 5px;
}
:last-child {
line-height: 2.5em;
color: red;
}
}
}
}
}
}
</style>

381
hx-ai-intelligent/src/view/equipmentControl/waterSystem/component/planTab.vue

@ -0,0 +1,381 @@
<template>
<div class="div-add">
<button class="add" @click="addModal">添加</button>
<a-popconfirm
title="是否提交以上修改?"
placement="bottomLeft"
ok-text="确定"
cancel-text="取消"
@confirm="sendTable">
<button class="add" style="margin-left: 20px">执行</button>
</a-popconfirm>
</div>
<div class="buttons">
<span style="color: red; padding-top: 20px">*以下修改需执行后生效</span>
<div class="plans">
<button class="plan enabled" style="margin-right: 10px" @click="togglePlan(1)">
计划启用
</button>
<button class="plan disabled" @click="togglePlan(3)"> 计划禁用 </button>
</div>
</div>
<table class="custom-table table1">
<thead>
<tr :style="{ background: 'rgba(35,45,69)' }">
<th>序号</th>
<th>执行时间</th>
<th>计划名称</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in dataSource" v-show="row.executeStatus.value != 0" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ row.startTime }}</td>
<td>{{ row.planName }}</td>
<td>
<button
:style="{
'font-size': '12px',
width: '5em',
background: 'rgb(47, 47, 47)',
color: setStateColor(row.executeStatus.value),
border: '1px solid',
}">
{{ setStateText(row.executeStatus.value) }}
</button>
</td>
<td>
<div class="tabReboot" @click="startPlan(row)">启用</div>
<a-popconfirm
title="此操作将移除该数据"
ok-text="确定"
cancel-text="取消"
placement="topRight"
@confirm="deletePlan(row)">
<div class="tabDelete">删除</div>
</a-popconfirm>
</td>
</tr>
</tbody>
</table>
<div class="out-dialog" v-if="addVisible">
<div class="content">
<div class="div-operation"></div>
<span class="text-operation">计划库</span>
</div>
<div style="margin-top: 20px">
<a-transfer
v-model:target-keys="targetKeys"
:data-source="transferData"
show-search
:filter-option="filterOption"
:render="(item: any) => item.title"
@change="handleChange"
:style="{ color: 'rgba(255,255,255,1)' }"
@search="handleSearch"
:listStyle="{ border: '2px solid rgba(25,74,125,1)', height: 'calc(100vh - 200px)' }" />
</div>
<div style="width: 100%; height: 60px"></div>
<div class="button-box">
<button class="cancel" @click="addVisible = false">取消</button>
<button class="execute" @click="sendPlan">确定</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
//
import { http } from '/nerv-lib/util/http';
import { planManage } from '/@/api/planManage';
import { waterSys } from '/@/api/waterSystem';
import { message } from 'ant-design-vue';
import { getEnum } from '/@/api';
//
import { items } from '/@/store/item';
// ===========================================================
onMounted(() => {
//
getStateEnum();
// 穿
reset();
});
//
const state = items();
//
const getStateEnum = async () => {
let enumData = await getEnum({ params: { enumType: 'PlanExecuteStatus' } });
stateList.value = enumData.data;
};
/**
* @method resetAll 刷新3个tab中的全部数据修改计划会影响tab1数据信息生成tab3日志
*/
const emit = defineEmits(['resetAll']);
// tab ========================================================
//
const stateList = ref([]);
//
const setStateColor = (state: number) => {
if (state == 0) {
return '#ccc';
} else if (state == 1) {
return 'orange';
} else if (state == 2) {
return 'rgb(57, 215, 187)';
} else if (state == 3) {
return 'rgb(255, 0, 0)';
}
};
//
const setStateText = (state: number) => {
const res = stateList.value.find((item) => {
return item.value == state;
});
return res.label;
};
// /
const togglePlan = (state: number) => {
dataSource.value.forEach((item: any) => {
//
if (state == 1) {
if (item.executeStatus.value != 2) {
item.executeStatus.value = state;
}
//
} else {
item.executeStatus.value = state;
}
});
};
//
const dataSource = ref([]);
//
const getTable = () => {
http
.get(planManage.getTableData, {
projectId: state.projectId,
siteId: state.siteId,
// (1,2,3,4,5,6)
ctrlType: 6,
})
.then((res) => {
dataSource.value = res.data;
});
};
// =0
const deletePlan = (row: any) => {
row.executeStatus.value = 0;
};
// = 1
const startPlan = (row: any) => {
if (row.executeStatus.value == 1) {
return message.info('该数据已是待执行状态,无需再次修改');
}
if (row.executeStatus.value == 2) {
return message.info('执行中的状态已被启用,无需修改');
}
row.executeStatus.value = 1;
};
//
const sendTable = () => {
if (!dataSource.value.length) {
return message.info('没有任何数据可以提交');
}
state.setLoading(true);
http
.post(
waterSys.submitTableData +
`?projectId=${state.projectId}${state.siteId ? `&siteId=${state.siteId}` : ''}`,
dataSource.value,
)
.then((res) => {
state.setLoading(false);
if (res.retcode == 0) {
message.success('操作成功');
//
emit('resetAll');
} else {
message.info(res.msg);
}
})
.catch(() => {
state.setLoading(false);
});
};
const reset = () => {
//
getTable();
// 穿
getLeftPlan();
};
// tab ====================================================
//
const addVisible = ref(false);
//
const addModal = () => {
addVisible.value = true;
};
// 穿 =======================================================
// 穿
const transferData = ref([]) as any;
// 穿
const getLeftPlan = () => {
http
.get(planManage.getTransData, {
projectId: state.projectId,
siteId: state.siteId,
// (1,2,3,4,5,6)
ctrlType: 6,
})
.then((res) => {
let arr: Array<Object> = [];
res.data.forEach((item: any) => {
arr.push({
key: item.id,
title: item.planName,
});
});
transferData.value = arr;
});
};
const handleChange = (keys: string[], direction: string, moveKeys: string[]) => {
console.log(keys, direction, moveKeys);
};
const handleSearch = (dir: string, value: string) => {
console.log('search:', dir, value);
};
// 穿
const targetKeys = ref<string[]>([]);
// 穿
const sendPlan = () => {
if (targetKeys.value.length < 1) {
return message.info('没有选择任何计划');
}
http.post(planManage.submitTransData, targetKeys.value).then(() => {
message.success('添加成功');
//
reset();
});
};
const filterOption = (inputValue: string, option: any) => {
return option.description.indexOf(inputValue) > -1;
};
//
defineExpose({
reset,
});
</script>
<style lang="less" scoped>
@import '../../style/dialogStyle.less';
.buttons {
display: flex;
justify-content: space-between;
.plan {
border: none;
font-size: 14px;
font-weight: 400;
border-radius: 5px;
width: 88px;
height: 32px;
color: white;
cursor: pointer;
margin: 15px 0;
vertical-align: middle;
}
.plan.enabled {
background: linear-gradient(180deg, rgba(103, 222, 0, 1) 0%, rgba(0, 181, 6, 1) 100%);
}
.plan.disabled {
background-color: red;
}
.plan:disabled {
cursor: not-allowed;
}
}
//
.div-add {
height: 64px;
display: flex;
justify-content: flex-end;
align-items: center;
position: fixed;
bottom: 0;
right: 0;
margin-right: 10px;
.add {
width: 74px;
height: 40px;
opacity: 1;
border-radius: 4px;
background: rgba(67, 136, 251, 1);
border: rgba(67, 136, 251, 1);
font-size: 14px;
font-weight: 400;
color: rgba(255, 255, 255, 1);
cursor: pointer;
}
}
//
.custom-table {
border-collapse: collapse;
width: 416px;
height: 40px;
color: rgba(255, 255, 255, 1);
}
.custom-table th,
.custom-table td {
border: 1px solid rgba(163, 192, 243, 1);
text-align: left;
padding: 8px;
text-align: center;
}
.table1 {
width: 100%;
border: 1px solid rgba(255, 255, 255);
border-radius: 5px;
background: rgba(255, 255, 255, 0.1);
//
.tabReboot,
.tabDelete {
border: none;
display: inline-block;
background-color: rgba(0, 0, 0, 0);
font-size: 14px;
font-weight: 400;
letter-spacing: 0;
line-height: 20px;
color: rgba(67, 136, 251, 1);
cursor: pointer;
}
.tabReboot {
margin-right: 8px;
}
.tabReboot::active {
color: white !important;
}
.tabDelete::active {
color: white;
}
}
::v-deep(.ant-transfer) {
// hover
.ant-transfer-list-content-item:hover {
background: black;
}
}
</style>

282
hx-ai-intelligent/src/view/equipmentControl/waterSystem/device.ts

@ -0,0 +1,282 @@
import { ref } from 'vue';
// 流动线条样式与定位
export const linePosition = [
// 雨水池 - 控制阀
{
left: '4%',
top: '44%',
transform: 'rotateZ(-30deg)',
transformOrigin: 'left',
zIndex: '6',
width: '6%',
},
{
left: '4%',
top: '84%',
transform: 'rotateZ(-30deg)',
transformOrigin: 'left',
zIndex: '6',
width: '6%',
},
// 控制阀 - 进水阀
{ left: '12%', top: '34%', width: '10%' },
{ left: '12%', top: '74%', width: '10%' },
// 进水阀 - 集水池
{ left: '23%', top: '34%', width: '8%' },
{ left: '23%', top: '74%', width: '8%' },
// 集水池 - 排水泵 - 横线
{ left: '35%', top: '34%', width: '4%' },
{ left: '35%', top: '74%', width: '4%' },
// 上半集水池右侧分线
{ left: '39%', top: '34%', transform: 'rotateZ(90deg)', transformOrigin: 'left', width: '4%' },
{ left: '39%', top: '34%', transform: 'rotateZ(-90deg)', transformOrigin: 'left', width: '4%' },
// 下半集水池右侧分线
{ left: '39%', top: '74%', transform: 'rotateZ(90deg)', transformOrigin: 'left', width: '4%' },
{ left: '39%', top: '74%', transform: 'rotateZ(-90deg)', transformOrigin: 'left', width: '4%' },
// 上半-左侧水泵分线
{ left: '39%', top: '25%', width: '8%' },
{ left: '39%', top: '43%', width: '8%' },
// 下半-左侧水泵分线
{ left: '39%', top: '65%', width: '8%' },
{ left: '39%', top: '83%', width: '8%' },
// 水泵右侧合线 下半
{ left: '47%', top: '83%', transform: 'rotateZ(-90deg)', transformOrigin: 'left', width: '14%' },
// 水泵右侧合线 上半
{ left: '47%', top: '25%', transform: 'rotateZ(90deg)', transformOrigin: 'left', width: '12%' },
// 汇入总闸连线
{ left: '47%', top: '52%', width: '4%' },
// 汇入总集水池
{ left: '51%', top: '52%', transform: 'rotateZ(-25deg)', transformOrigin: 'left', width: '7%' },
// 汇入总排水闸
{ left: '58%', top: '45%', width: '9%' },
// 汇入市政管道 - 途径水泵2
{ left: '68%', top: '45%', width: '28%' },
// 总排水闸 - 总排水泵1 上半
{ left: '75%', top: '45%', transform: 'rotateZ(-90deg)', transformOrigin: 'left', width: '10%' },
{ left: '75%', top: '23%', width: '11%' },
{ left: '86%', top: '22.5%', transform: 'rotateZ(90deg)', transformOrigin: 'left', width: '10%' },
// 总排水闸 - 总排水泵3 下半
{ left: '75%', top: '45%', transform: 'rotateZ(90deg)', transformOrigin: 'left', width: '9%' },
{ left: '75%', top: '65%', width: '11%' },
{
left: '86%',
top: '65%',
transform: 'rotateZ(-90deg)',
transformOrigin: 'left',
width: '9%',
},
];
/**
* 1.
* 2.
* 3.
* @param icon =1/=2/=3//进水阀=4//排水泵=5
* @param type =1/=2/=3/=4
* @param open =true/=false
* @param control false
* @param edited
*/
// 污水池
export const device1 = ref([
{
control: false,
type: 1,
icon: 1,
styleObject: {
left: '1%',
top: '40%',
zIndex: '9',
},
},
{
control: false,
type: 1,
icon: 1,
styleObject: {
left: '1%',
top: '80%',
zIndex: '9',
},
},
]);
// 阀门
export const device2 = ref([
{
control: true,
open: true,
type: 2,
icon: 3,
edited: false,
styleObject: {
left: '8%',
top: '28%',
},
},
{
control: true,
open: false,
type: 2,
icon: 3,
edited: false,
styleObject: {
left: '8%',
top: '68%',
},
},
{
control: true,
open: true,
type: 2,
icon: 4,
edited: false,
styleObject: {
left: '20%',
top: '28%',
},
},
{
control: true,
open: true,
type: 2,
icon: 4,
edited: false,
styleObject: {
left: '20%',
top: '68%',
},
},
{
control: true,
open: true,
type: 2,
icon: 3,
edited: false,
styleObject: {
left: '48%',
top: '46%',
},
},
{
control: true,
open: true,
type: 2,
icon: 3,
edited: false,
styleObject: {
left: '65%',
top: '38%',
},
},
]);
// 集水池
export const device3 = ref([
{
control: false,
type: 3,
icon: 2,
styleObject: {
left: '30%',
top: '68%',
},
},
{
control: false,
type: 3,
icon: 2,
styleObject: {
left: '30%',
top: '28%',
},
},
{
control: false,
type: 3,
icon: 2,
styleObject: {
left: '56%',
top: '40%',
},
},
]);
// 水泵
export const device4 = ref([
{
control: true,
open: false,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '40%',
top: '20%',
},
},
{
control: true,
open: true,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '40%',
top: '40%',
},
},
{
control: true,
open: true,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '40%',
top: '60%',
},
},
{
control: true,
open: true,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '40%',
top: '80%',
},
},
// 右上3水泵
{
control: true,
open: true,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '78%',
top: '20%',
},
},
{
control: true,
open: true,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '78%',
top: '40%',
},
},
{
control: true,
open: true,
type: 4,
icon: 5,
edited: false,
styleObject: {
left: '78%',
top: '60%',
},
},
]);

150
hx-ai-intelligent/src/view/equipmentControl/waterSystem/deviceInfo.vue

@ -0,0 +1,150 @@
<template>
<div class="left-top">
<div class="info">
<div class="title-item">
<div class="title-back">设备状态</div>
</div>
<div class="info-item">
<div v-for="(item, index) in deviceState" :key="index">
<img v-if="item.type == props.state" :src="item.icon" alt="" />
<img v-else :src="item.default" alt="" />
<div class="mode-item-text">{{ item.name }}</div>
</div>
</div>
</div>
<div class="mode">
<div class="title-item">
<div class="title-back">控制模式</div>
<div class="title-button">计划启用</div>
</div>
<div class="mode-item"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
//
import { items } from '/@/store/item';
//
import type1 from './images/type1.png';
import type2 from './images/type2.png';
import type3 from './images/type3.png';
import type1off from './images/type1off.png';
import type2off from './images/type2off.png';
import type3off from './images/type3off.png';
//
const props = defineProps({
//
state: {
type: Number,
},
});
const deviceState = [
{
icon: type1,
default: type1off,
name: '运行',
type: 1,
},
{
icon: type2,
default: type2off,
name: '故障',
type: 2,
},
{
icon: type3,
default: type3off,
name: '强排',
type: 3,
},
];
//
onMounted(() => {});
</script>
<style lang="less" scoped>
.left-top {
--pad: 12px;
position: absolute;
left: var(--pad);
top: var(--pad);
width: auto;
height: 110px;
display: flex;
z-index: 99;
gap: var(--pad);
> div {
background: black;
display: flex;
flex-direction: column;
gap: 3px;
box-sizing: border-box;
padding: 10px;
border-radius: 4px;
.title-item {
padding: 5px;
position: relative;
.title-back {
margin-left: 10px;
padding-left: 5px;
background: linear-gradient(to right, #1aaefb, transparent);
width: 7em;
color: white;
position: relative;
font-size: 14px;
vertical-align: middle;
}
.title-back::before {
position: absolute;
content: '';
display: block;
width: 5px;
height: 100%;
left: -12px;
background: #1aaefb;
}
.title-button {
position: absolute;
right: 2px;
top: 2px;
padding: 3px 8px;
color: white;
border-radius: 4px;
user-select: none;
cursor: pointer;
background: linear-gradient(180deg, rgba(103, 222, 0, 1) 0%, rgba(0, 181, 6, 1) 100%);
}
}
}
//
.info {
width: 230px;
.info-item {
display: flex;
padding-top: 3px;
div {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
align-items: center;
.mode-item-text {
color: white;
user-select: none;
}
img {
width: 23px;
display: block;
user-select: none;
}
}
}
}
//
.mode {
width: 200px;
.mode-item {
}
}
}
</style>

291
hx-ai-intelligent/src/view/equipmentControl/waterSystem/deviceItem.vue

@ -0,0 +1,291 @@
<template>
<div class="deviceItem" :style="info.styleObject">
<!-- 点击编辑弹出框 -->
<a-popover
color="rgba(0, 0, 0, 0.8)"
placement="right"
v-model:visible="visible"
trigger="click">
<template #content>
<div class="item-box">
<div class="item-box-title">
<img v-if="info.type == 2" src="./images/device1.png" alt="" />
<img v-if="info.type == 4" src="./images/device2.png" alt="" />
<span>{{ info.name }}</span>
</div>
<!-- 开关 -->
<div v-if="info.type == 4" class="item-box-switch">
<div>开关</div>
<a-switch style="margin-top: 3px" size="small" v-model:checked="info.open" />
</div>
<div v-if="info.type == 2" class="item-box-range">
<div>开度</div>
<!-- <a-slider v-model:value="info.value" :tooltip-visible="true" :marks="range" :step="1"> -->
<a-slider v-model:value="info.value" :marks="range" :step="1">
<template #mark="{ label, point }">
<template v-if="point === 100">
<strong>{{ label }}</strong>
</template>
<template v-else>{{ label }}</template>
</template>
</a-slider>
</div>
<div v-if="info.type == 4" class="item-box-range">
<div>频率</div>
<a-slider v-model:value="info.value" :marks="range" :step="1">
<template #mark="{ label, point }">
<template v-if="point === 100">
<strong>{{ label }}</strong>
</template>
<template v-else>{{ label }}</template>
</template>
</a-slider>
</div>
<div class="item-box-button">
<a-button class="item-btn" @click="refresh">刷新</a-button>
<a-popconfirm
title="此操作只保存修改,需右下角按钮提交"
ok-text="确定"
cancel-text="取消"
@confirm="editDevice">
<a-button class="item-btn" type="primary">执行</a-button>
</a-popconfirm>
</div>
</div>
</template>
<div v-if="!info.edited && info.control" class="device-button-back">
<img src="./images/edit.png" alt="" />
编辑</div
>
</a-popover>
<a-popconfirm
title="撤销将移除已保存的修改"
ok-text="确定"
cancel-text="取消"
@confirm="backConfirm">
<div v-if="info.edited && info.control" class="device-button">
<img src="./images/back.png" alt="" />
撤销</div
>
</a-popconfirm>
<!-- 设备图标 - 当前共5种单位 -->
<img v-if="info.icon == 1" src="./images/pond1.png" alt="" />
<img v-if="info.icon == 2" src="./images/pond2.png" alt="" />
<img v-if="info.icon == 3" src="./images/valve1.png" alt="" />
<img v-if="info.icon == 4" src="./images/valve2.png" alt="" />
<img v-if="info.icon == 5" style="width: 70px; height: 70px" src="./images/pump.png" alt="" />
<div class="info-name">
<span>{{ info.name }}</span>
<div class="img-box">
<img
v-if="(info.type == 2 || info.type == 4) && info.state == 0"
src="./images/alarm1.png"
alt="" />
<img
v-if="(info.type == 2 || info.type == 4) && info.state > 0"
src="./images/alarm2.png"
alt="" />
</div>
</div>
<!-- 只有水池会显示容量 -->
<div class="info-value" v-if="info.type == 1 || info.type == 3">
容量 : {{ info.value + info.unit }}</div
>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { message } from 'ant-design-vue';
//
// ===============================================================
//
onMounted(() => {});
//
const props = defineProps({
//
info: {
type: Object,
default: () => {},
},
});
//
const info = computed(() => props.info);
// =============================================================
//
const visible = ref(false);
//
const range = ref({
0: {
style: {
color: '#0DFFFF',
},
label: 'min',
},
100: {
style: {
color: '#0DFFFF',
},
label: 'max',
},
});
//
const refresh = () => {
info.value.value = info.value.oldVal;
};
//
const backConfirm = () => {
info.value.value = info.value.oldVal;
info.value.edited = false;
//
if (info.value.type == 4) {
console.log(info.value);
info.value.open = info.value.opened;
}
};
// -
const editDevice = () => {
//
if (info.value.value == info.value.oldVal && info.value.open === info.value.opened) {
return message.info('未产生任何修改');
}
//
visible.value = false;
info.value.edited = true;
message.success('保存成功');
};
</script>
<style lang="less" scoped>
//
.deviceItem {
--size: 90px;
width: var(--size);
height: var(--size);
position: absolute;
top: 20%;
text-align: center;
// 使3
z-index: 3;
> img {
user-select: none;
}
//
.info-name {
color: white;
font-size: 15px;
text-align: center;
position: relative;
> span {
vertical-align: middle;
}
.img-box {
display: inline-block;
position: absolute;
margin-left: 8px;
width: 22px;
> img {
width: 22px;
}
}
}
//
.info-value {
text-align: center;
color: #23fdab;
font-size: 13px;
}
//
.device-button {
position: absolute;
top: -35px;
left: 0;
right: 0;
margin: auto;
width: 5em;
padding: 5px 0;
text-align: center;
border-radius: 4px;
font-weight: 700;
cursor: pointer;
background: linear-gradient(#ffd700, #ffa403);
color: #674330;
user-select: none;
> img {
width: 13px;
}
}
//
.device-button-back {
position: absolute;
top: -35px;
left: 0;
right: 0;
margin: auto;
width: 5em;
padding: 5px 0;
text-align: center;
border-radius: 4px;
font-weight: 700;
cursor: pointer;
background: linear-gradient(#00f92c, #00fe9f);
color: #003d5a;
user-select: none;
> img {
width: 13px;
}
}
> img {
width: 100%;
height: 100%;
}
}
//
.ant-popover-inner {
//
.item-box {
width: 250px;
display: flex;
flex-direction: column;
gap: 15px;
color: white;
img {
width: 20px;
margin-right: 10px;
vertical-align: middle;
}
}
//
.item-box-switch {
padding: 10px 15px;
display: flex;
justify-content: space-between;
border-radius: 4px;
background: rgba(67, 136, 251, 0.3);
.ant-switch-checked {
background: linear-gradient(180deg, rgba(1, 206, 255, 1) 0%, rgba(0, 150, 229, 1) 100%);
}
}
//
.item-box-range {
padding: 10px 15px 10px 15px;
box-sizing: border-box;
border-radius: 4px;
background: rgba(67, 136, 251, 0.3);
.ant-slider-handle {
border: 2px solid red !important;
}
}
//
.item-box-button {
text-align: right;
.item-btn {
text-align: center;
margin-left: 10px;
}
}
}
</style>

30
hx-ai-intelligent/src/view/equipmentControl/waterSystem/deviceLine.vue

@ -0,0 +1,30 @@
<template>
<div class="line-item" :style="position"></div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
//
const props = defineProps({
// 线
position: {
type: Object,
default: () => {},
},
});
const position = computed(() => {
return props.position;
});
</script>
<style lang="less" scoped>
.line-item {
position: absolute;
height: 10px;
width: 250px;
z-index: 1;
background-image: url(./images/back.gif);
background-color: rgba(13, 255, 164, 0.3);
background-size: 200px 10px;
border-radius: 5px;
}
</style>

BIN
hx-ai-intelligent/src/view/equipmentControl/waterSystem/images/open.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

663
hx-ai-intelligent/src/view/equipmentControl/waterSystem/index.vue

@ -0,0 +1,663 @@
<template>
<div class="main">
<!-- 左上角设备信息面板 -->
<deviceInfo :state="deviceState" />
<!-- 示意图 -->
<div class="map">
<!-- 污水池图标 -->
<deviceItem v-for="(item, index) in device1" :key="index" :info="item" />
<!-- 阀门图标 -->
<deviceItem v-for="(item, index) in device2" :key="index" :info="item" />
<!-- 集水池图标 -->
<deviceItem v-for="(item, index) in device3" :key="index" :info="item" />
<!-- 排水泵图标 -->
<deviceItem v-for="(item, index) in device4" :key="index" :info="item" />
<!-- 市政管道图标 -->
<div class="pipe">
<div>市政管道</div>
<img src="./images/pipe.png" alt="" />
</div>
<!-- 设备图标底部连线 -->
<deviceLine v-for="(item, index) in linePosition" :key="index" :position="item" />
</div>
<!-- 右下角按钮 -->
<div class="buttons">
<a-button type="primary" @click="openDrawer1">执行</a-button>
<a-button type="primary" @click="resetAll">全部撤销</a-button>
</div>
<!-- 页面右侧抽屉开关 -->
<div class="right-button">
<div>计划与日志</div>
<img @click="visible = true" src="./images/open.png" alt="" />
</div>
<!-- 右侧 计划日志抽屉 -->
<a-drawer
v-model:visible="visible"
class="drawer-item"
width="496"
placement="right"
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8 }"
:closable="false"
id="drawer"
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="计划列表" force-render>
<planTab ref="tabs1Ref" @reset-all="resetDrawer" />
</a-tab-pane>
<a-tab-pane key="2" tab="日志">
<logTab ref="tabs2Ref" @reset-all="resetDrawer" />
</a-tab-pane>
</a-tabs>
</a-drawer>
<!-- 右侧 操作队列 -->
<a-drawer
v-model:visible="visible1"
class="drawer-item"
width="496"
placement="right"
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8 }"
:closable="false"
id="drawer"
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }">
<a-tabs v-model:activeKey="activeKey1">
<a-tab-pane key="1" tab="操作队列" force-render>
<div>
<a-badge :offset="[-5, 12]" :count="valveList.length">
<button :class="{ btn: true, selected: activeButton == 1 }" @click="changeBtn(1)"
>阀门</button
>
</a-badge>
<a-badge :offset="[-5, 12]" :count="pumpList.length">
<button :class="{ btn: true, selected: activeButton == 2 }" @click="changeBtn(2)"
>水泵</button
>
</a-badge>
</div>
<div class="device-list" v-if="activeButton == 1">
<div class="device-list-item" v-for="(item, index) in valveList" :key="index">
<div class="list-item-title">
<div class="item-title">
<img src="./images/device1.png" alt="" />
<span>{{ item.name }}</span>
</div>
<div class="revoke" @click="revoke(item.id, index, 1)">撤销</div>
</div>
<div class="list-item-main">
<div>
<div class="info">开度</div>
<div class="text">
<span>{{ item.oldVal + item.unit }}</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>{{ item.value + item.unit }}</span>
</div>
</div>
<div></div>
</div>
</div>
<a-empty style="margin-top: 100px" v-if="valveList.length == 0">
<template #description> <span style="color: white">暂无数据</span></template>
</a-empty>
</div>
<div class="device-list" v-if="activeButton == 2">
<div class="device-list-item" v-for="(item, index) in pumpList" :key="index">
<div class="list-item-title">
<div class="item-title">
<img src="./images/device2.png" alt="" />
<span>{{ item.name }}</span>
</div>
<div class="revoke" @click="revoke(item.id, index, 2)">撤销</div>
</div>
<div class="list-item-main">
<div>
<div class="info">频率</div>
<div class="text">
<span>{{ item.oldVal + item.unit }}</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>{{ item.value + item.unit }}</span>
</div>
</div>
<div>
<div class="info">开关</div>
<div class="text">
<span>{{ item.opened ? '开' : '关' }}</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>{{ item.open == 1 ? '开' : '关' }}</span>
</div>
</div>
</div>
</div>
<a-empty style="margin-top: 100px" v-if="pumpList.length == 0">
<template #description> <span style="color: white">暂无数据</span></template>
</a-empty>
</div>
<div style="width: 100%; height: 100px"></div>
<div class="button-box">
<button class="cancel" @click="visible1 = false">取消</button>
<a-popconfirm
title="此操作将提交以上修改内容"
ok-text="确定"
cancel-text="取消"
placement="bottomRight"
@confirm="submitChange">
<button class="execute">执行</button>
</a-popconfirm>
</div>
</a-tab-pane>
</a-tabs>
</a-drawer>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { Modal, message } from 'ant-design-vue';
import deviceInfo from './deviceInfo.vue';
import deviceLine from './deviceLine.vue';
import deviceItem from './deviceItem.vue';
import planTab from './component/planTab.vue';
import logTab from './component/logTab.vue';
import { linePosition, device1, device2, device3, device4 } from './device';
//
import { http } from '/nerv-lib/util/http';
import { waterSys } from '/@/api/waterSystem';
//
import { items } from '/@/store/item';
// ===========================================================================================
const state = items();
onMounted(() => {
//
getAllDevice();
});
const deviceState = 1;
// =========================================================================================
//
const visible = ref(false);
// tab
const activeKey = ref('1');
// =========================================================================================
//
const visible1 = ref(false);
// tab
const activeKey1 = '1';
// =1/=2
const activeButton = ref(1);
//
const changeBtn = (key: number) => {
activeButton.value = key;
};
//
const pumpList = ref<any>([]);
//
const valveList = ref<any>([]);
//
const openDrawer1 = () => {
valveList.value = device2.value.filter((item: any) => {
return item.edited;
});
pumpList.value = device4.value.filter((item: any) => {
return item.edited;
});
if (valveList.value.length || pumpList.value.length) {
visible1.value = true;
} else {
message.info('未产生任何修改');
}
};
// -
const revoke = (id: any, index: number, type: number) => {
if (type == 1) {
valveList.value.splice(index, 1);
device2.value.forEach((item: any) => {
if (item.id == id) {
item.value = item.oldVal;
item.edited = false;
}
});
} else if (type == 2) {
pumpList.value.splice(index, 1);
device4.value.forEach((item: any) => {
if (item.id == id) {
item.value = item.oldVal;
item.open = item.opened;
item.edited = false;
}
});
}
};
const submitChange = () => {
let valveList = [];
device2.value.forEach((item: any) => {
if (item.edited) {
valveList.push({
deviceGroup: item.id,
openPercent: item.value,
});
}
});
let pumpList = [];
device4.value.forEach((item: any) => {
if (item.edited) {
pumpList.push({
deviceGroup: item.id,
frequency: item.value,
switchStatus: +item.open,
});
}
});
state.setLoading(true);
http
.post(waterSys.submitList, {
projectId: state.projectId,
siteId: state.siteId,
valveList,
pumpList,
})
.then((res) => {
let data = res.data;
state.setLoading(false);
//
if (res.retcode != 0) {
//
return message.warning(res.msg);
}
//
if (data.allSucceed) {
message.success('修改完成');
// allSucceedtrue
} else {
message.info(`${data.successList.length}条修改成功,${data.failList.length}条修改失败`);
}
//
resetEdit();
visible1.value = false;
getAllDevice();
});
};
const resetEdit = () => {
device2.value.forEach((item) => {
item.edited = false;
});
device4.value.forEach((item) => {
item.edited = false;
});
};
// ==========================================================================================
// tabtab
const resetDrawer = () => {
try {
// tab1
tabs1Ref.value.reset();
} catch {}
try {
// tab2
tabs2Ref.value.reset();
} catch {}
};
// tab1
const tabs1Ref = ref();
// tab2
const tabs2Ref = ref();
//
const resetAll = () => {
Modal.confirm({
title: '提示信息',
content: '该操作将还原已编辑内容',
onOk() {
//
device2.value.forEach((item: any) => {
if (item.edited) {
item.value = item.oldVal;
item.edited = false;
}
});
//
device4.value.forEach((item: any) => {
if (item.edited) {
item.value = item.oldVal;
item.open = item.opened;
item.edited = false;
}
});
},
onCancel() {},
});
};
//
const getAllDevice = () => {
getDevice(1);
getDevice(2);
getDevice(3);
getDevice(4);
};
/**
* 获取一个设备类型的数据
* @param type 污水池=1/阀门=2/集水池=3/水泵=4
*/
const getDevice = (type: number) => {
//
let url = '';
if (type == 1) {
url = waterSys.getPool1;
} else if (type == 2) {
url = waterSys.getValve;
} else if (type == 3) {
url = waterSys.getPool2;
} else if (type == 4) {
url = waterSys.getPump;
}
http
.get(url, {
projectId: state.projectId,
siteId: state.siteId,
})
.then((res) => {
let data = res.data;
//
if (type == 1) {
device1.value.forEach((item: any, index: number) => {
let result = data[index];
//
item.name = result.deviceInfoName;
//
item.value = result.record.capacity ? result.record.capacity : '--';
//
item.unit = result.record.capacityUnit ? result.record.capacityUnit : '';
});
}
//
if (type == 2) {
device2.value.forEach((item: any, index: number) => {
let result = data[index];
//
item.name = result.deviceGroupName;
// ID
item.id = result.deviceGroup;
//
item.edited = false;
//
item.state = result.record.runStatus.value != null ? result.record.runStatus.value : -1;
// -
item.value = result.record.openPercent ? result.record.openPercent : 0;
// -
item.oldVal = result.record.openPercent ? result.record.openPercent : null;
//
item.unit = result.record.openPercentUnit ? result.record.openPercentUnit : '';
});
}
//
if (type == 3) {
device3.value.forEach((item: any, index: number) => {
let result = data[index];
//
item.name = result.deviceInfoName;
//
item.value = result.record.capacity ? result.record.capacity : '--';
//
item.unit = result.record.capacityUnit ? result.record.capacityUnit : '';
});
}
//
if (type == 4) {
device4.value.forEach((item: any, index: number) => {
let result = data[index];
//
item.name = result.deviceGroupName;
// ID
item.id = result.deviceGroup;
//
item.edited = false;
// -
item.open = result.record.switchStatus.value == 1 ? true : false;
// -
item.opened = result.record.switchStatus.value == 1 ? true : false;
//
item.state = result.record.runStatus.value != null ? result.record.runStatus.value : -1;
// -
item.value = result.record.frequency ? result.record.frequency : 0;
// -
item.oldVal = result.record.frequency ? result.record.frequency : null;
//
item.unit = result.record.frequencyUnit ? result.record.frequencyUnit : '';
});
}
});
};
</script>
<style lang="less" scoped>
.main {
width: 100%;
height: 100%;
position: relative;
background: linear-gradient(to bottom, rgb(35, 102, 165), rgb(1, 19, 81));
//
.map {
width: 85vw;
height: 38vw;
position: relative;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
//
.pipe {
width: 120px;
height: 120px;
position: absolute;
text-align: center;
left: 92%;
top: 40%;
z-index: 3;
transform: translateY(-60px);
img {
height: 100%;
}
div {
width: inherit;
color: white;
position: absolute;
top: -1.5em;
font-size: 16px;
}
}
}
//
.buttons {
position: absolute;
right: 15px;
bottom: 15px;
display: flex;
gap: 15px;
height: 40px;
> button {
height: 40px;
}
}
.right-button {
height: 25px;
position: absolute;
top: 0;
bottom: 0;
right: 10px;
margin: auto;
display: flex;
z-index: 99;
gap: 10px;
color: #0dffff;
> img {
width: 25px;
height: 25px;
border-radius: 2px;
vertical-align: middle;
cursor: pointer;
user-select: none;
}
}
.drawer-item {
width: 100px;
height: 100px;
border: 2px solid red;
}
}
// tab
:deep(.ant-tabs-tab-btn) {
color: white;
}
.btn {
width: 92px;
height: 40px;
border-radius: 4px;
opacity: 1;
margin-top: 10px;
margin-left: 15px;
font-size: 14px;
font-weight: 400;
opacity: 1;
border: 1px solid rgba(207, 212, 219, 1);
line-height: 20.27px;
color: white;
text-align: center;
vertical-align: top;
background-color: rgba(255, 255, 255, 0.1);
}
.selected {
background: linear-gradient(180deg, rgba(201, 245, 255, 1) 0%, rgba(138, 215, 255, 1) 100%);
color: rgba(0, 61, 90, 1);
border: 1px solid white;
}
.btn:hover {
background-color: rgba(207, 212, 219, 1);
}
.btn:active {
background-color: rgba(102, 102, 102, 1);
color: white;
}
.device-list {
margin-left: 15px;
margin-top: 15px;
width: 100%;
height: auto;
display: flex;
gap: 15px;
flex-direction: column;
.device-list-item {
width: calc(100% - 15px);
box-sizing: border-box;
padding: 10px;
border: 2px solid #03407e;
border-radius: 4px;
background: rgba(0, 177, 255, 0.2);
display: flex;
gap: 10px;
flex-direction: column;
.list-item-title {
color: white;
display: flex;
justify-content: space-between;
.item-title {
img {
width: 25px;
}
span {
margin-left: 10px;
font-size: 16px;
}
}
.revoke {
text-align: center;
border: none;
border-radius: 4px;
padding: 5px 15px;
background: linear-gradient(
180deg,
rgba(255, 187, 0, 1) 0%,
rgba(255, 112, 3, 1) 91.21%,
rgba(255, 129, 3, 1) 100%
);
cursor: pointer;
}
}
.list-item-main {
display: flex;
font-size: 13px;
> div {
flex: 1;
display: flex;
gap: 8px;
> .info {
text-align: center;
width: 6em;
height: 2.5em;
line-height: 2.5em;
border-radius: 4px;
color: white;
background: linear-gradient(
180deg,
rgba(86, 221, 253, 1) 0%,
rgba(25, 176, 255, 1) 100%
);
}
> .text {
:first-child {
color: white;
line-height: 2.5em;
}
img {
padding: 0 5px;
}
:last-child {
line-height: 2.5em;
color: red;
}
}
}
}
}
}
.button-box {
width: 100%;
box-sizing: border-box;
padding: 10px;
height: 60px;
position: absolute;
background-color: transparent;
text-align: right;
bottom: 0;
left: 0;
right: 0;
.execute,
.cancel {
margin-right: 10px;
width: 74px;
height: 40px;
opacity: 1;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
border: 0;
margin-left: 10px;
}
.execute {
background: rgb(67, 136, 251);
color: white;
}
.cancel {
background: white;
color: black;
}
}
</style>

4
hx-ai-intelligent/src/view/monitor/deviceMonitor/page.vue

@ -52,6 +52,10 @@
if (graphRef.value) {
graphRef.value.downloadChart();
}
} else {
if (tableRef.value) {
tableRef.value.export1();
}
}
};

5
hx-ai-intelligent/src/view/monitor/deviceMonitor/table/index.vue

@ -16,6 +16,7 @@
</template>
<script lang="ts">
import { exportExcel } from '/@/util/ExcelUtil.js';
import { defineComponent, watch, ref, onMounted } from 'vue';
import type { TableColumnType } from 'ant-design-vue';
import { Pagination } from 'ant-design-vue';
@ -245,6 +246,9 @@
columns.value = columnA;
total.value = dataList.value.length;
onChange(1, 10);
}; // excel
const export1 = () => {
exportExcel(columns.value, dataList.value, '历史数据导出', true, 'deviceName', 1, 4);
};
onMounted(() => {
init();
@ -257,6 +261,7 @@
total,
onChange,
x,
export1,
};
},
});

22
hx-ai-intelligent/src/view/monitor/deviceMonitor/tree/index.vue

@ -15,17 +15,17 @@
:tree-data="treeData1"
@change="changeDeviceType" />
<a-spin :spinning="treeLoading">
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
:show-line="{ showLeafIcon: false }"
checkable
:height="560"
style="width: 100%; overflow-y: auto; margin-bottom: 10px; margin-top: 10px"
:tree-data="treeData2" />
</a-spin>
<!-- <a-spin :spinning="treeLoading"> -->
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
:show-line="{ showLeafIcon: false }"
checkable
:height="560"
style="width: 100%; overflow-y: auto; margin-bottom: 10px; margin-top: 10px"
:tree-data="treeData2" />
<!-- </a-spin> -->
<!-- <div class="fixed-bottom"> -->
<div>

150
hx-ai-intelligent/src/view/monitor/energyMonitor/analysisTable/index.vue

@ -18,6 +18,8 @@
</template>
<script lang="ts">
import ExcelJS from 'exceljs';
import FileSaver from 'file-saver';
import { defineComponent, ref, inject, watch, onMounted } from 'vue';
export default defineComponent({
@ -130,6 +132,153 @@
},
{ deep: true },
);
// excel
//
const export1 = () => {
if (!data.value || data.value.length == 0) {
return;
}
//
for (let i = 0; i < data.value.length; i++) {
data.value[i].index = i + 1;
}
// 簿
const workbook = new ExcelJS.Workbook();
// sheet1
const sheet1 = workbook.addWorksheet('sheet1');
//
let filterVal = [
'name',
'value',
'yoyDiff',
'yoyRate',
'momDiff',
'momRate',
'zongxiangDiff',
'zongxiangRate',
];
// columns
//
sheet1.addRows([
['设备/节点', '统计值', '同比', '', '环比', '', '纵向对比', ''],
['', '', '△差值', '增长率', '△差值', '增长率', '△差值', '增长率'],
]);
//
sheet1.mergeCells('A1:A2'); // '/'
sheet1.mergeCells('B1:B2'); // ''
sheet1.mergeCells('C1:D1'); // ''
sheet1.mergeCells('E1:F1'); // ''
sheet1.mergeCells('G1:H1'); // ''
//
const list = data.value;
//
const datas = formatJson(filterVal, list);
//
datas.forEach((row: any) => {
// const values = Object.values(row);
sheet1.addRow(row);
});
let column = sheet1.columns;
for (let i = 0; i < column.length; i++) {
column[i].width = 20;
}
//
//
sheet1.eachRow((row) => {
//
row.eachCell((cell) => {
//
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
};
//
cell.alignment = {
vertical: 'middle',
horizontal: 'center',
};
});
});
//
const titleCell1 = sheet1.getRow(1);
// 30
titleCell1.height = 30;
//
titleCell1.eachCell((cell) => {
//
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFFF' },
};
//
cell.font = {
// color: { argb: 'FF0000' }, //
bold: true, //
size: 18, // 18
};
});
const titleCell2 = sheet1.getRow(2);
// 30
titleCell2.height = 30;
//
titleCell2.eachCell((cell) => {
//
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFFF' },
};
//
cell.font = {
// color: { argb: 'FF0000' }, //
bold: true, //
size: 18, // 18
};
});
//
const bodyRows = sheet1.getRows(3, sheet1.rowCount);
if (bodyRows) {
//
bodyRows.forEach((bodyRow) => {
// 20
bodyRow.height = 20;
bodyRow.eachCell((cell) => {
cell.font = {
size: 16, // 16
};
});
});
}
//
workbook.xlsx
.writeBuffer()
.then((buffer) => {
let file = new Blob([buffer], { type: 'application/octet-stream' });
FileSaver.saveAs(file, '分析数据导出.xlsx');
})
.catch((error) => console.log('Error writing excel export', error));
};
/**
* 格式化表格数据
* @filterVal 格式头
* @jsonData 用来格式化的表格数据
*/
function formatJson(filterVal: any, jsonData: any) {
return jsonData.map((v: any) => filterVal.map((j: any) => v[j]));
}
onMounted(() => {
//
data.value = JSON.parse(JSON.stringify(pageData.analysisTableList));
@ -165,6 +314,7 @@
rowSelection,
selectedKey,
setStandard,
export1,
};
},
});

5
hx-ai-intelligent/src/view/monitor/energyMonitor/graphTable/index.vue

@ -16,6 +16,7 @@
</template>
<script lang="ts">
import { exportExcel } from '/@/util/ExcelUtil.js';
import { defineComponent, watch, inject, ref, onMounted } from 'vue';
import type { TableColumnType } from 'ant-design-vue';
import { Pagination } from 'ant-design-vue';
@ -152,6 +153,9 @@
columns.value = columnA;
total.value = dataList.value.length;
onChange(1, 10);
}; // excel
const export1 = () => {
exportExcel(columns.value, dataList.value, '图表数据导出');
};
onMounted(() => {
init();
@ -167,6 +171,7 @@
columns,
total,
onChange,
export1,
};
},
});

26
hx-ai-intelligent/src/view/monitor/energyMonitor/page.vue

@ -28,7 +28,7 @@
</template> -->
</a-tabs>
<div class="button">
<ns-icon name="xiazai" size="18" style="margin-right: 10px" @click="downloadChart" />
<ns-icon name="xiazai" size="18" style="margin-right: 10px" @click="download" />
<ns-icon :name="iconName" size="18" style="margin-right: 10px" @click="change" />
</div>
</div>
@ -74,14 +74,26 @@
name: 'EnvironmentMonitorIndex', // name
});
const downloadChart = () => {
if (activeKey.value == '1' && isGraph) {
if (graphRef.value) {
graphRef.value.downloadChart();
const download = () => {
if (activeKey.value == '1') {
if (isGraph.value) {
if (graphRef.value) {
graphRef.value.downloadChart();
}
} else {
if (tableRef.value) {
tableRef.value.export1();
}
}
} else {
if (analysisGraphRef.value) {
analysisGraphRef.value.downloadChart();
if (isGraph.value) {
if (analysisGraphRef.value) {
analysisGraphRef.value.downloadChart();
}
} else {
if (analysisTableRef.value) {
analysisTableRef.value.export1();
}
}
}
};

26
hx-ai-intelligent/src/view/monitor/energyMonitor/tree/index.vue

@ -35,17 +35,17 @@
v-if="mode == '0'"
@change="changeMode" />
<a-input v-model:value="pointName" placeholder="请输入节点名称" v-else @change="changeMode" />
<a-spin :spinning="treeLoading">
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
:show-line="{ showLeafIcon: false }"
checkable
:height="600"
style="width: 100%; overflow-y: auto; margin-bottom: 10px; margin-top: 10px"
:tree-data="treeData2">
<!-- <template #title="{ title }">
<!-- <a-spin :spinning="treeLoading"> -->
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
:show-line="{ showLeafIcon: false }"
checkable
:height="500"
style="width: 100%; overflow-y: auto; margin-bottom: 10px; margin-top: 10px"
:tree-data="treeData2">
<!-- <template #title="{ title }">
<span v-if="title.indexOf(mode == '0' ? deviceName : pointName) > -1">
{{ title.substr(0, title.indexOf(mode == '0' ? deviceName : pointName)) }}
<span style="color: #f50">{{ mode == '0' ? deviceName : pointName }}</span>
@ -58,8 +58,8 @@
</span>
<span v-else>{{ title }}</span>
</template> -->
</a-tree>
</a-spin>
</a-tree>
<!-- </a-spin> -->
<!-- <div class="fixed-bottom"> -->
<div>

5
hx-ai-intelligent/src/view/monitor/environmentMonitor/averageData/index.vue

@ -206,10 +206,7 @@
};
// excel
const export1 = () => {
for (let i = 0; i < data.value.length; i++) {
data.value[i].index = i + 1;
}
exportExcel(tableColumns.value, data.value, '平均数据导出', false);
exportExcel(tableColumns.value, data.value, '平均数据导出');
};
onMounted(async () => {
//

6
hx-ai-intelligent/src/view/monitor/environmentMonitor/historyData/index.vue

@ -288,6 +288,10 @@
//
const getTableList = () => {
loading.value = true;
tableColumns.value = [];
data.value = [];
total.value = 0;
pageData.value = [];
let environmentType = '';
for (let i = 0; i < typeList.value.length; i++) {
if (typeList.value[i].value == typeValue.value) {
@ -353,7 +357,7 @@
};
// excel
const export1 = () => {
exportExcel(tableColumns.value, data.value, '历史数据导出', true, 1, 3);
exportExcel(tableColumns.value, data.value, '历史数据导出', true, 'location', 1, 3);
};
onMounted(async () => {
//

4
lib/component/tree/tree-api.vue

@ -120,9 +120,9 @@
})
.then((res) => {
treeData.value = transform(get(res, resultField));
//
selectedKeys.value = [];
if (formConfig.value.callList && formConfig.value.defaultSelection) {
//
selectedKeys.value = [];
handleSelect([treeData.value[0].id], {
selected: true,
event: 'select',

4
lib/util/xlsx-util.ts

@ -129,11 +129,13 @@ export const importFile = (
headers: { token: Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`) },
})
.then((res) => {
if (res) {
if (res.data.data) {
NsMessage.success('导入成功', 1, () => {
reload && reload();
successBack && successBack(res);
});
} else {
NsMessage.error(res.data.msg || '导入失败');
}
})
.catch((err) => {

Loading…
Cancel
Save