diff --git a/src/views/ems/record/recordAlarmRule/index.vue b/src/views/ems/record/recordAlarmRule/index.vue
index a21c2f0..62790a2 100644
--- a/src/views/ems/record/recordAlarmRule/index.vue
+++ b/src/views/ems/record/recordAlarmRule/index.vue
@@ -4,7 +4,7 @@
ALARM RULE STUDIO
异常告警规则与处置措施工作台
-
把上位机触发阈值和处置步骤聚合在一个界面中,帮助运维团队更快定位规则、编排措施并完成闭环维护。
+
@@ -106,8 +106,9 @@
-
-
+
+
+
@@ -115,12 +116,12 @@
-
+
-
+
-->
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -289,9 +294,9 @@
-
+
+ <!– 图片上传 –>
-
+ <!– 图片列表 –>
-
+ -->
+
@@ -339,11 +345,11 @@
-
+
diff --git a/src/views/ems/record/recordIotenvInstant/index.vue b/src/views/ems/record/recordIotenvInstant/index.vue
index 13411b2..5c9e9c3 100644
--- a/src/views/ems/record/recordIotenvInstant/index.vue
+++ b/src/views/ems/record/recordIotenvInstant/index.vue
@@ -13,6 +13,10 @@
/>
@@ -128,12 +132,14 @@
+ :default-time="defaultRecordTime"
+ />
搜索
@@ -182,25 +188,30 @@
>导出
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -269,10 +286,24 @@
-
+
-
+
@@ -294,9 +325,60 @@ import {
addRecordIotenvInstant,
updateRecordIotenvInstant
} from '@/api/ems/record/recordIotenvInstant';
-import { parseTime } from '@/utils/ruoyi';
import { getMonitorInfoTree, listBaseMonitorInfo } from '@/api/ems/base/baseMonitorInfo';
+// ---- monitor_type 到允许显示字段的业务规则 ----
+
+const MONITOR_TYPES = [5, 6, 10] as const;
+type MonitorType = (typeof MONITOR_TYPES)[number];
+
+type MonitorMetricProp =
+ | 'temperature'
+ | 'humidity'
+ | 'illuminance'
+ | 'noise'
+ | 'concentration'
+ | 'vibrationSpeed'
+ | 'vibrationDisplacement'
+ | 'vibrationAcceleration'
+ | 'vibrationTemp';
+
+interface TableColumnItem {
+ key: number;
+ label: string;
+ visible: boolean;
+ metricProp?: MonitorMetricProp;
+}
+
+const MONITOR_TYPE_METRIC_MAP: Record = {
+ 5: ['temperature'],
+ 6: ['temperature', 'humidity'],
+ 10: ['vibrationDisplacement']
+};
+
+const getAllowedMetricPropsByMonitorTypes = (monitorTypes: MonitorType[]): MonitorMetricProp[] => {
+ return [...new Set(monitorTypes.flatMap((mt) => MONITOR_TYPE_METRIC_MAP[mt] || []))];
+};
+
+const normalizeMonitorType = (monitorType: unknown): MonitorType | null => {
+ const type = Number(monitorType);
+ if (MONITOR_TYPES.includes(type as MonitorType)) {
+ return type as MonitorType;
+ }
+ return null;
+};
+
+const warnUnknownMonitorType = (monitorType: unknown, node?: any) => {
+ if (!import.meta.env.DEV) return;
+ if (monitorType === undefined || monitorType === null || monitorType === '') return;
+ console.warn('[IotenvInstant] Unknown monitorType ignored:', {
+ monitorType,
+ nodeId: node?.id,
+ nodeCode: node?.code,
+ nodeLabel: node?.label
+ });
+};
+
defineOptions({
name: 'RecordIotenvInstant'
});
@@ -307,6 +389,39 @@ const formRef = ref();
const queryFormRef = ref();
const treeRef = ref();
+// 记录时间时间范围(独立 ref,避免 reactive Proxy 数组与 el-date-picker v-model 的兼容问题)
+const daterangeRecordTime = ref([]);
+
+// 树上已勾选但尚未点击"搜索"的设备(勾选 ≠ 查询,只有点搜索才写入 queryParams)
+const pendingMonitorIds = ref>([]);
+const pendingMonitorTypes = ref([]);
+
+// 日期范围选择器:选日期时默认补 08:00:00
+const defaultRecordTime: [Date, Date] = [new Date(2000, 0, 1, 8, 0, 0), new Date(2000, 0, 1, 8, 0, 0)];
+
+const pad2 = (num: number) => String(num).padStart(2, '0');
+
+const formatDateTime = (date: Date): string => {
+ const y = date.getFullYear();
+ const m = pad2(date.getMonth() + 1);
+ const d = pad2(date.getDate());
+ const h = pad2(date.getHours());
+ const min = pad2(date.getMinutes());
+ const s = pad2(date.getSeconds());
+ return `${y}-${m}-${d} ${h}:${min}:${s}`;
+};
+
+// 默认时间范围:前一天 08:00:00 ~ 当天 08:00:00
+const setDefaultRecordTimeRange = () => {
+ const end = new Date();
+ end.setHours(8, 0, 0, 0);
+
+ const start = new Date(end);
+ start.setDate(start.getDate() - 1);
+
+ daterangeRecordTime.value = [formatDateTime(start), formatDateTime(end)];
+};
+
// 这里统一通过工厂函数重建表单,避免弹窗在新增/编辑切换时残留上一条业务数据。
const createFormData = () => ({
objid: null,
@@ -327,6 +442,8 @@ const createFormData = () => ({
// 这里保留原页面字段结构,优先保证 EMS 业务行为与接口入参不发生漂移。
const state = reactive({
workUnitName: null,
+ // 当前选中设备的 monitorType 集合,用于决定列显隐
+ currentMonitorTypes: [] as number[],
//下拉树List
baseMonitorInfoOptions: [],
//左侧树结构List
@@ -337,10 +454,10 @@ const state = reactive({
children: 'children',
label: 'label'
},
- // 记录时间时间范围
- daterangeRecordTime: [],
- // 遮罩层
- loading: true,
+ // 遮罩层:初始化不显示 loading,只有真正请求接口时才显示
+ loading: false,
+ // 是否已经点击过搜索;初始化未搜索,不显示表格
+ hasSearched: false,
// 选中数组
ids: [],
// 非单个禁用
@@ -394,47 +511,56 @@ const state = reactive({
{
key: 2,
label: `温度`,
- visible: true
+ visible: true,
+ metricProp: 'temperature'
},
{
key: 3,
label: `湿度`,
- visible: true
+ visible: true,
+ metricProp: 'humidity'
},
{
key: 4,
label: `照度`,
- visible: true
+ visible: true,
+ metricProp: 'illuminance'
},
{
key: 5,
label: `噪声`,
- visible: true
+ visible: true,
+ metricProp: 'noise'
},
{
key: 6,
label: `硫化氢浓度`,
- visible: true
+ visible: true,
+ metricProp: 'concentration'
},
{
key: 7,
label: `振动-速度(mm/s)`,
- visible: true
+ visible: true,
+ metricProp: 'vibrationSpeed'
},
{
key: 8,
label: `振动-位移(um)`,
- visible: true
+ visible: true,
+ metricProp: 'vibrationDisplacement'
},
{
key: 9,
label: `振动-加速度(g)`,
- visible: true
+ visible: true,
+ metricProp: 'vibrationAcceleration'
},
{
key: 10,
label: `振动-温度(℃)`,
- visible: true
+ visible: true,
+ metricProp: 'vibrationTemp'
},
{
key: 11,
@@ -446,15 +572,16 @@ const state = reactive({
label: `记录时间`,
visible: true
}
- ]
+ ] as TableColumnItem[]
} as any);
const {
baseMonitorInfoList,
baseMonitorInfoOptions,
columns,
- daterangeRecordTime,
+ currentMonitorTypes,
form,
+ hasSearched,
ids,
loading,
monitorInfoOptions,
@@ -471,6 +598,8 @@ const {
workUnitName
} = toRefs(state);
+const tableColumns = columns as Ref;
+
const normalizer = (node) => {
if (node.children && !node.children.length) {
delete node.children;
@@ -478,13 +607,14 @@ const normalizer = (node) => {
return {
id: node.monitorId,
label: node.monitorName,
+ monitorType: node.monitorType,
children: node.children
};
};
const getTreeMonitorInfo = () => {
listBaseMonitorInfo({
- monitorTypeList: [5, 6, 7, 8, 9]
+ monitorTypeList: [5, 6, 10]
}).then((response) => {
baseMonitorInfoOptions.value = [];
const data = {
@@ -497,19 +627,54 @@ const getTreeMonitorInfo = () => {
});
};
-const getAllMonitorIds = (nodes) => {
- let ids = [];
- if (!nodes || nodes.length === 0) return ids;
+const isLeafDeviceNode = (node) => {
+ return node?.id && (!node.children || node.children.length === 0);
+};
+
+const getLeafDeviceNodes = (nodes: any[] = []) => {
+ let result: any[] = [];
nodes.forEach((node) => {
- if (node.id) {
- ids.push(node.id);
- }
- if (node.children && node.children.length > 0) {
- ids = ids.concat(getAllMonitorIds(node.children));
+ const children = node.children || [];
+ if (children.length > 0) {
+ result = result.concat(getLeafDeviceNodes(children));
+ } else if (node.id) {
+ result.push(node);
}
});
- console.log(ids);
- return ids;
+ return result;
+};
+
+const resolveNodeMonitorCode = (node: any) => {
+ return node?.code ?? node?.monitorCode;
+};
+
+const resolveNodeMonitorType = (node: any) => {
+ return node?.monitorType ?? node?.type;
+};
+
+const isValidMonitorCode = (code: unknown): code is string | number => {
+ return code !== undefined && code !== null && code !== '';
+};
+
+const syncPendingMonitorSelection = (nodes: any[] = []) => {
+ const leafNodes = nodes.filter(isLeafDeviceNode);
+
+ pendingMonitorIds.value = leafNodes
+ .map((node) => resolveNodeMonitorCode(node))
+ .filter(isValidMonitorCode);
+
+ const monitorTypes = leafNodes
+ .map((node) => {
+ const rawType = resolveNodeMonitorType(node);
+ const monitorType = normalizeMonitorType(rawType);
+ if (monitorType === null) {
+ warnUnknownMonitorType(rawType, node);
+ }
+ return monitorType;
+ })
+ .filter((monitorType): monitorType is MonitorType => monitorType !== null);
+
+ pendingMonitorTypes.value = [...new Set(monitorTypes)] as MonitorType[];
};
const filterNode = (value, data) => {
@@ -517,33 +682,99 @@ const filterNode = (value, data) => {
return data.label.indexOf(value) !== -1;
};
-const handleNodeClick = (data) => {
- // 点击节点时清空monitorIds,只使用选中的monitorId
- queryParams.value.monitorIds = [];
- queryParams.value.monitorId = data.code;
- handleQuery();
+const handleCheck = (_data: any, checkedState: any) => {
+ const leafNodes = (checkedState.checkedNodes || []).filter((node: any) => isLeafDeviceNode(node));
+ syncPendingMonitorSelection(leafNodes);
};
+const handleCheckAll = () => {
+ const leafNodes = getLeafDeviceNodes(monitorInfoOptions.value);
+ const allLeafIds = leafNodes.map((node) => node.id);
+ treeRef.value?.setCheckedKeys(allLeafIds, true);
+ syncPendingMonitorSelection(leafNodes);
+};
+
+const handleUncheckAll = () => {
+ treeRef.value?.setCheckedKeys([], true);
+ syncPendingMonitorSelection([]);
+ queryParams.value.monitorId = null;
+ queryParams.value.monitorIds = [];
+ currentMonitorTypes.value = [] as MonitorType[];
+ hasSearched.value = false;
+ loading.value = false;
+ recordIotenvInstantList.value = [];
+ total.value = 0;
+};
+
+// 当前 UI 列过滤实际使用的 monitorType 集合:
+// - 未搜索时跟随勾选态 (pendingMonitorTypes)
+// - 已搜索后跟随查询态 (currentMonitorTypes)
+const effectiveMonitorTypes = computed(() => {
+ if (!hasSearched.value) {
+ return pendingMonitorTypes.value as MonitorType[];
+ }
+ return currentMonitorTypes.value as MonitorType[];
+});
+
+const visibleMetricProps = computed(() => {
+ return getAllowedMetricPropsByMonitorTypes(effectiveMonitorTypes.value);
+});
+
+const columnMap = computed(() => {
+ const map = new Map();
+ tableColumns.value.forEach((column) => {
+ map.set(column.key, column);
+ });
+ return map;
+});
+
+const isMetricColumnAllowed = (column: TableColumnItem) => {
+ if (!column.metricProp) return true;
+
+ // 未搜索前没有类型上下文,保留原始列设置;
+ // 搜索后如果类型解析失败,不再放开所有指标列(暴露问题而非隐藏问题)。
+ if (effectiveMonitorTypes.value.length === 0) return !hasSearched.value;
+
+ return visibleMetricProps.value.includes(column.metricProp);
+};
+
+const isColumnVisible = (columnKey: number) => {
+ const column = columnMap.value.get(columnKey);
+ return !!column?.visible && isMetricColumnAllowed(column);
+};
+
+const displayColumns = computed(() => {
+ return tableColumns.value.filter((column) => isMetricColumnAllowed(column));
+});
+
const getList = () => {
+ if (!hasSearched.value || !queryParams.value.monitorIds || queryParams.value.monitorIds.length === 0) {
+ loading.value = false;
+ recordIotenvInstantList.value = [];
+ total.value = 0;
+ return;
+ }
loading.value = true;
- if (null != daterangeRecordTime.value && '' != daterangeRecordTime.value) {
+ if (daterangeRecordTime.value && daterangeRecordTime.value.length === 2) {
queryParams.value.params['beginCollectTime'] = daterangeRecordTime.value[0];
queryParams.value.params['endCollectTime'] = daterangeRecordTime.value[1];
- }
- if (null != daterangeRecordTime.value && '' != daterangeRecordTime.value) {
queryParams.value.params['beginRecordTime'] = daterangeRecordTime.value[0];
queryParams.value.params['endRecordTime'] = daterangeRecordTime.value[1];
+ } else {
+ delete queryParams.value.params['beginCollectTime'];
+ delete queryParams.value.params['endCollectTime'];
+ delete queryParams.value.params['beginRecordTime'];
+ delete queryParams.value.params['endRecordTime'];
}
- // 如果没有选中节点,则获取所有设备ID
- if (!queryParams.value.monitorId) {
- queryParams.value.monitorIds = getAllMonitorIds(monitorInfoOptions.value);
- }
- listRecordIotenvInstant(queryParams.value).then((response) => {
- recordIotenvInstantList.value = response.rows;
- total.value = response.total;
- loading.value = false;
- });
+ listRecordIotenvInstant(queryParams.value)
+ .then((response) => {
+ recordIotenvInstantList.value = response.rows;
+ total.value = response.total;
+ })
+ .finally(() => {
+ loading.value = false;
+ });
};
const cancel = () => {
@@ -571,15 +802,37 @@ const reset = () => {
};
const handleQuery = () => {
- console.log('查询参数:', daterangeRecordTime.value);
+ if (!pendingMonitorIds.value || pendingMonitorIds.value.length === 0) {
+ proxy?.$modal.msgWarning('请先选择设备后再查询');
+ hasSearched.value = false;
+ loading.value = false;
+ recordIotenvInstantList.value = [];
+ total.value = 0;
+ return;
+ }
+
+ hasSearched.value = true;
queryParams.value.pageNum = 1;
+ queryParams.value.monitorId = null;
+ queryParams.value.monitorIds = [...pendingMonitorIds.value];
+ currentMonitorTypes.value = [...pendingMonitorTypes.value] as MonitorType[];
getList();
};
const resetQuery = () => {
queryFormRef.value?.resetFields();
- daterangeRecordTime.value = [];
- handleQuery();
+ setDefaultRecordTimeRange();
+ treeRef.value?.setCheckedKeys([], true);
+ pendingMonitorIds.value = [];
+ pendingMonitorTypes.value = [];
+ queryParams.value.pageNum = 1;
+ queryParams.value.monitorId = null;
+ queryParams.value.monitorIds = [];
+ currentMonitorTypes.value = [];
+ hasSearched.value = false;
+ loading.value = false;
+ recordIotenvInstantList.value = [];
+ total.value = 0;
};
const handleSelectionChange = (selection) => {
@@ -648,11 +901,43 @@ const handleExport = () => {
);
};
+const MONITOR_TYPE_LABEL_MAP: Record = {
+ 5: '温度',
+ 6: '温湿度',
+ 10: '振动'
+};
+
+// 后端 TreeSelects 返回的字段是 type(不是 monitorType),code 是 monitorCode。
+// 这里统一转成前端使用的字段名,避免后续读取 node.monitorType 时为 undefined。
+const normalizeMonitorTreeNode = (node: any): any => {
+ const children = Array.isArray(node.children) ? node.children.map(normalizeMonitorTreeNode) : [];
+
+ const resolvedMonitorType = node.monitorType ?? node.type;
+ const typeLabel = resolvedMonitorType != null ? MONITOR_TYPE_LABEL_MAP[Number(resolvedMonitorType)] : undefined;
+ const label = typeLabel ? `${node.label ?? node.monitorName ?? node.name} (${typeLabel})` : (node.label ?? node.monitorName ?? node.name);
+
+ const normalized: any = {
+ ...node,
+ id: node.id ?? node.monitorId ?? node.objId,
+ label,
+ code: node.code ?? node.monitorCode,
+ monitorType: resolvedMonitorType
+ };
+
+ if (children.length > 0) {
+ normalized.children = children;
+ } else {
+ delete normalized.children;
+ }
+
+ return normalized;
+};
+
const getTreeselect = () => {
getMonitorInfoTree({
- monitorTypeList: [5, 6, 7, 8, 9]
+ monitorTypeList: [5, 6, 10]
}).then((response) => {
- monitorInfoOptions.value = response.data;
+ monitorInfoOptions.value = (response.data || []).map(normalizeMonitorTreeNode);
});
};
@@ -661,15 +946,8 @@ watch(workUnitName, (val) => {
});
onMounted(() => {
- const nowDate = new Date();
- const today = parseTime(new Date(), '{y}-{m}-{d}');
- const lastDate = new Date();
- lastDate.setDate(nowDate.getDate() - 1);
- const yesterday = parseTime(lastDate, '{y}-{m}-{d}');
- daterangeRecordTime.value[0] = yesterday + ' 08:00:00';
- daterangeRecordTime.value[1] = today + ' 08:00:00';
+ setDefaultRecordTimeRange();
getTreeMonitorInfo();
getTreeselect();
- getList();
});
diff --git a/src/views/index.vue b/src/views/index.vue
index 231911f..60e21eb 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -1,705 +1,1029 @@
-
-
-
-
Hawei Plus Overview
-
海威PLUS 运营概览
-
- 首页直接展示当前系统最新监测快照与报警概况。所有卡片和图表都基于实时接口返回的真实数据生成,方便值班人员进入系统后先看到整体状态,再进入明细页面处理。
-
-