From a30a31e383c700544e28c3dae26382cb8930c764 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 7 Jan 2026 14:06:12 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(ems):=20=E4=BC=98=E5=8C=96=E7=89=A9?= =?UTF-8?q?=E8=81=94=E7=BD=91=E6=95=B0=E6=8D=AE=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E8=AF=B7=E6=B1=82=E5=B7=A5=E5=85=B7=E4=B8=AD?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A0=E9=99=90=E5=88=B6=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将导出功能改为弹窗模式,支持时间范围和设备类型选择 - 新增导出对话框,包含时间范围选择和设备分组选择功能 - 实现按设备类型(温度、温湿度、噪声等)分组显示和选择 - 添加全选设备、清空选择和按类型全选功能 - 优化振动设备导出逻辑,添加时间范围和设备选择验证 - 增加导出文件名的类型信息,便于识别数据类型 - 在请求工具中添加无限制超时设置,确保大文件导出成功 --- src/utils/request.js | 1 + .../ems/record/recordIOTInstant/index.vue | 395 +++++++++++++++++- .../record/recordVibrationInstant/index.vue | 18 +- 3 files changed, 407 insertions(+), 7 deletions(-) diff --git a/src/utils/request.js b/src/utils/request.js index a94a448..3a776dc 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -137,6 +137,7 @@ export function download(url, params, filename, config) { transformRequest: [(params) => { return tansParams(params) }], headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, responseType: 'blob', + timeout: -1, // 导出操作超时时间无限制 ...config }).then(async (data) => { const isBlob = blobValidate(data); diff --git a/src/views/ems/record/recordIOTInstant/index.vue b/src/views/ems/record/recordIOTInstant/index.vue index 2850cc1..102bcaf 100644 --- a/src/views/ems/record/recordIOTInstant/index.vue +++ b/src/views/ems/record/recordIOTInstant/index.vue @@ -180,7 +180,7 @@ plain icon="el-icon-download" size="mini" - @click="handleExport" + @click="openExportDialog" v-hasPermi="['ems/record:recordIotenvInstant:export']" >导出 @@ -295,6 +295,126 @@ 取 消 + + + + + + + + + + + +
+ 全选设备 + 清空选择 + + 已选择 {{ selectedDevicesCount }} 个设备 + +
+ + +
+ + +
+
+ 温度设备 + + {{ isGroupAllSelected('temperature') ? '取消全选' : '全选该类型' }} + +
+ + + + {{ device.monitorName }} + (虚拟) + + + +
+ + +
+
+ 温湿度设备 + + {{ isGroupAllSelected('humidity') ? '取消全选' : '全选该类型' }} + +
+ + + + {{ device.monitorName }} + (虚拟) + + + +
+ + +
+
+ 噪声设备 + + {{ isGroupAllSelected('noise') ? '取消全选' : '全选该类型' }} + +
+ + + + {{ device.monitorName }} + (虚拟) + + + +
+ + +
+
+ 其他设备 + + {{ isGroupAllSelected('other') ? '取消全选' : '全选该类型' }} + +
+ + + + {{ device.monitorName }} + (虚拟) + + + +
+ + +
+ + 暂无可用设备,请先配置物联网采集设备 +
+
+
+
+
+ +
@@ -368,6 +488,24 @@ export default { form: {}, // 表单校验 rules: { + }, + // 导出对话框相关 + exportDialogVisible: false, + exportForm: { + timeRange: [], + selectedDeviceCodes: [] + }, + // 所有设备列表(用于导出选择) + allDevicesList: [], + // 设备列表缓存(避免重复请求) + allDevicesListCache: null, + cacheTimestamp: 0, + // 按类型分组的设备(不含振动设备,振动在专用页面处理) + deviceGroups: { + temperature: [], // 温度设备 (type=5) + humidity: [], // 温湿度设备 (type=6) + noise: [], // 噪声设备 (type=7) + other: [] // 其他设备 (8,9等,不含振动10) } }; }, @@ -375,6 +513,10 @@ export default { // 计算是否已选择设备 hasSelectedDevice() { return this.queryParams.monitorId || (this.queryParams.monitorIds && this.queryParams.monitorIds.length > 0); + }, + // 计算已选择的设备数量 + selectedDevicesCount() { + return this.exportForm.selectedDeviceCodes ? this.exportForm.selectedDeviceCodes.length : 0; } }, created() { @@ -607,13 +749,195 @@ export default { this.$modal.msgSuccess("删除成功"); }).catch(() => {}); }, - /** 导出按钮操作 */ - handleExport() { - this.download('ems/record/recordIotenvInstant/export', { - ...this.queryParams - }, `recordIotenvInstant_${new Date().getTime()}.xlsx`) + + /** ========== 导出相关方法 ========== */ + + /** 打开导出对话框 */ + openExportDialog() { + // 初始化导出表单 + this.exportForm.timeRange = this.daterangeRecordTime ? [...this.daterangeRecordTime] : []; + this.exportForm.selectedDeviceCodes = []; + + // 加载所有设备列表 + this.loadAllDevicesForExport(); + this.exportDialogVisible = true; }, + /** 加载所有设备列表(用于导出,使用缓存优化性能) */ + loadAllDevicesForExport() { + // 缓存5分钟有效(300000毫秒) + const now = Date.now(); + if (this.allDevicesListCache && (now - this.cacheTimestamp) < 300000) { + // 使用缓存数据 + this.allDevicesList = this.allDevicesListCache; + this.groupDevicesByType(); + return; + } + + // 缓存失效,重新加载(不含振动设备type=10) + listBaseMonitorInfo({ monitorTypeList: [5, 6, 7, 8, 9] }).then(response => { + this.allDevicesList = response.data; + this.allDevicesListCache = response.data; + this.cacheTimestamp = now; + this.groupDevicesByType(); + }); + }, + + /** 按设备类型分组(不含振动设备,振动在专用页面处理) */ + groupDevicesByType() { + // 重置分组 + this.deviceGroups = { + temperature: [], // 温度设备 (type=5) + humidity: [], // 温湿度设备 (type=6) + noise: [], // 噪声设备 (type=7) + other: [] // 其他设备 (8,9等,不含振动10) + }; + + this.allDevicesList.forEach(device => { + // 跳过虚拟设备 + if (device.isAmmeter === '0') return; + // 跳过振动设备(振动设备在专用页面处理) + if (device.monitorType === 10) return; + + switch (device.monitorType) { + case 5: + this.deviceGroups.temperature.push(device); + break; + case 6: + this.deviceGroups.humidity.push(device); + break; + case 7: + this.deviceGroups.noise.push(device); + break; + default: + this.deviceGroups.other.push(device); + break; + } + }); + }, + + /** 全选设备(不含振动设备) */ + selectAllDevices() { + // 获取所有非虚拟、非振动设备的设备编号 + const allCodes = this.allDevicesList + .filter(device => device.isAmmeter !== '0' && device.monitorType !== 10) + .map(device => device.monitorCode); + this.exportForm.selectedDeviceCodes = allCodes; + }, + + /** 清空设备选择 */ + clearAllDevices() { + this.exportForm.selectedDeviceCodes = []; + }, + + /** 处理设备选择变化 */ + handleDeviceSelectionChange() { + // 可以在这里添加额外的逻辑,比如更新显示等 + }, + + /** 判断某个类型的设备是否全部选中 */ + isGroupAllSelected(groupName) { + const group = this.deviceGroups[groupName] || []; + // 获取该组非虚拟设备的编码列表 + const validCodes = group + .filter(device => device.isAmmeter !== '0') + .map(device => device.monitorCode); + if (validCodes.length === 0) return false; + // 检查是否全部在已选列表中 + return validCodes.every(code => this.exportForm.selectedDeviceCodes.includes(code)); + }, + + /** 全选/取消全选某个类型的设备 */ + selectGroupDevices(groupName) { + const group = this.deviceGroups[groupName] || []; + // 获取该组非虚拟设备的编码列表 + const validCodes = group + .filter(device => device.isAmmeter !== '0') + .map(device => device.monitorCode); + + if (this.isGroupAllSelected(groupName)) { + // 已全选,则取消该组所有设备 + this.exportForm.selectedDeviceCodes = this.exportForm.selectedDeviceCodes + .filter(code => !validCodes.includes(code)); + } else { + // 未全选,则添加该组所有设备 + const newCodes = validCodes.filter(code => !this.exportForm.selectedDeviceCodes.includes(code)); + this.exportForm.selectedDeviceCodes = [...this.exportForm.selectedDeviceCodes, ...newCodes]; + } + }, + + /** 确认导出 */ + confirmExport() { + // 校验时间范围 + if (!this.exportForm.timeRange || this.exportForm.timeRange.length !== 2) { + this.$modal.msgWarning("请先选择时间范围"); + return; + } + // 校验设备选择 + if (this.exportForm.selectedDeviceCodes.length === 0) { + this.$modal.msgWarning("请至少选择一个设备"); + return; + } + + // 计算选中的设备类型集合(用于后端动态导出列) + const selectedMonitorTypes = this.getSelectedMonitorTypes(); + + // 构建导出参数 + const exportParams = { + params: { + beginRecordTime: this.exportForm.timeRange[0], + endRecordTime: this.exportForm.timeRange[1], + // 传递设备类型,后端根据类型动态选择导出列 + monitorTypes: selectedMonitorTypes.join(',') + }, + monitorIds: this.exportForm.selectedDeviceCodes, + monitorId: null // 清空单个设备ID,使用多设备数组 + }; + + // 生成文件名(简化格式,移除日期中的横杠以缩短文件名) + const beginDate = this.exportForm.timeRange[0].substring(0, 10).replace(/-/g, ''); + const endDate = this.exportForm.timeRange[1].substring(0, 10).replace(/-/g, ''); + const deviceCount = this.exportForm.selectedDeviceCodes.length; + // 文件名包含能源类型信息 + const typeNames = this.getTypeNamesForFileName(selectedMonitorTypes); + const fileName = `IOT数据_${typeNames}_${beginDate}_${endDate}_${deviceCount}设备.xlsx`; + + // 执行导出 + this.download('ems/record/recordIotenvInstant/export', exportParams, fileName); + this.exportDialogVisible = false; + }, + + /** 获取选中设备的类型集合 */ + getSelectedMonitorTypes() { + const selectedCodes = this.exportForm.selectedDeviceCodes; + const types = new Set(); + + // 遍历所有设备列表,找出选中设备的类型 + this.allDevicesList.forEach(device => { + if (selectedCodes.includes(device.monitorCode) && device.monitorType) { + types.add(device.monitorType); + } + }); + + return Array.from(types); + }, + + /** 根据类型生成文件名中的类型描述(不含振动) */ + getTypeNamesForFileName(types) { + if (types.length === 0) return '混合'; + if (types.length > 2) return '混合'; + + const typeMap = { + 5: '温度', + 6: '温湿度', + 7: '噪声' + }; + + return types.map(t => typeMap[t] || '其他').join('_'); + }, + + /** ========== 原有方法 ========== */ + getTreeselect() { getMonitorInfoTree({ monitorTypeList: [5,6,7,8,9]}).then(response => { this.monitorInfoOptions = response.data @@ -636,4 +960,63 @@ export default { .no-device-tip .el-empty { padding: 0; } + +/* 导出对话框样式 */ +.device-select-container { + max-height: 400px; + overflow-y: auto; + border: 1px solid #DCDFE6; + border-radius: 4px; + padding: 10px; +} + +.device-group { + margin-bottom: 15px; + padding-bottom: 15px; + border-bottom: 1px solid #EBEEF5; +} + +.device-group:last-child { + border-bottom: none; + margin-bottom: 0; + padding-bottom: 0; +} + +.device-group-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.device-group-title { + font-weight: bold; + color: #303133; + padding-left: 5px; + border-left: 3px solid #409EFF; +} + +.device-select-container .el-checkbox { + margin-right: 0; + margin-bottom: 8px; + display: block; +} + +/* 空状态样式 */ +.no-devices-empty { + text-align: center; + padding: 40px 20px; + color: #909399; + font-size: 14px; +} + +.no-devices-empty i { + font-size: 24px; + margin-right: 8px; + color: #E6A23C; +} + +.device-select-container .el-col { + margin-bottom: 5px; +} diff --git a/src/views/ems/record/recordVibrationInstant/index.vue b/src/views/ems/record/recordVibrationInstant/index.vue index 43aefa5..4df889e 100644 --- a/src/views/ems/record/recordVibrationInstant/index.vue +++ b/src/views/ems/record/recordVibrationInstant/index.vue @@ -586,9 +586,25 @@ export default { }, /** 导出按钮操作 */ handleExport() { + // 校验时间范围是否已选择 + if (!this.daterangeRecordTime || this.daterangeRecordTime.length !== 2) { + this.$modal.msgWarning("请先选择记录时间范围再导出"); + return; + } + // 校验是否已选择设备 + if (!this.queryParams.monitorId && (!this.queryParams.monitorIds || this.queryParams.monitorIds.length === 0)) { + this.$modal.msgWarning("请先在左侧树中选择设备再导出"); + return; + } + // 设置时间范围参数 + this.queryParams.params['beginRecordTime'] = this.daterangeRecordTime[0]; + this.queryParams.params['endRecordTime'] = this.daterangeRecordTime[1]; + // 振动页面固定传递monitorTypes=10,确保后端只导出振动相关列 + this.queryParams.params['monitorTypes'] = '10'; + this.download('ems/record/recordIotenvInstant/export', { ...this.queryParams - }, `recordIotenvInstant_${new Date().getTime()}.xlsx`) + }, `振动数据_${this.daterangeRecordTime[0].substring(0,10)}_${this.daterangeRecordTime[1].substring(0,10)}.xlsx`) }, getTreeselect() { From a27e1650cb8f0b44d2c64db7b02988ff3c448877 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 7 Jan 2026 14:06:26 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat(tree):=20=E4=BC=98=E5=8C=96=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=95=B0=E6=8D=AE=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 isParentNode 状态标识判断节点是否为父节点 - 重构数据加载逻辑区分父节点和叶子节点的处理方式 - 父节点只获取子节点数据不显示自身数据 - 叶子节点获取当前节点自身数据 - 更新节点选中时的数据加载和日志输出逻辑 - 简化设备数据过滤和去重处理流程 --- src/views/index.vue | 109 +++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 66 deletions(-) diff --git a/src/views/index.vue b/src/views/index.vue index f8a9287..92a6409 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -303,6 +303,7 @@ export default { selectedNodeName: null, // 当前选中的节点名称 selectedNodeType: null, // 当前选中的节点类型 selectedNodeCode: null, // 当前选中的节点Code + isParentNode: false, // 是否为父节点(有子节点) deviceTreeProps: { children: 'children', label: 'label' @@ -362,68 +363,54 @@ export default { this.loading = true try { console.log('=== 开始加载节点数据 ===') - console.log('节点ID:', nodeId) + console.log('节点ID:', nodeId, '是否为父节点:', this.isParentNode) - // 同时获取当前节点和子节点的数据 - const [currentNodeResponse, childNodesResponse] = await Promise.all([ - // 获取当前节点自身的数据(通过获取所有数据然后筛选) - getLatestRecords(), - // 获取子节点的数据 - getLatestRecordsByParentId(nodeId) - ]) + // 判断是否为父节点(有子节点的节点) + const isParentNode = this.isParentNode let allDeviceData = [] - // 处理当前节点自身的数据 - if (currentNodeResponse.code === 200) { - const currentNodeData = currentNodeResponse.data || [] - console.log('所有设备数据总数:', currentNodeData.length) - console.log('当前节点信息 - ID:', this.selectedNodeId, '名称:', this.selectedNodeName, 'Code:', this.selectedNodeCode) + if (isParentNode) { + // 父节点:只获取子节点的数据,不显示父节点自身(父节点通常是虚拟设备) + const childNodesResponse = await getLatestRecordsByParentId(nodeId) + if (childNodesResponse.code === 200) { + const childNodesData = childNodesResponse.data || [] + console.log('子节点原始数据数量:', childNodesData.length) - // 筛选出当前节点对应的设备数据 - // 优先通过节点code匹配设备monitorId(叶子节点的正确匹配方式) - const currentDevices = currentNodeData.filter(device => { - const matchByCode = this.selectedNodeCode && device.monitorId === this.selectedNodeCode - const matchById = device.monitorId === nodeId - const matchByName = device.monitorName === this.selectedNodeName - const isValid = device.monitorName !== '胶东机场' && - device.monitorId && - device.monitorName + const childDevices = childNodesData.filter(device => { + const matches = device.monitorName !== '胶东机场' && + device.monitorId && + device.monitorName + return matches + }) + console.log('子节点有效设备数量:', childDevices.length) + allDeviceData = childDevices + } + } else { + // 叶子节点:获取当前节点自身的数据 + const currentNodeResponse = await getLatestRecords() + if (currentNodeResponse.code === 200) { + const currentNodeData = currentNodeResponse.data || [] + console.log('所有设备数据总数:', currentNodeData.length) + console.log('当前节点信息 - ID:', this.selectedNodeId, '名称:', this.selectedNodeName, 'Code:', this.selectedNodeCode) - const matches = (matchByCode || matchById || matchByName) && isValid + // 筛选出当前节点对应的设备数据 + const currentDevices = currentNodeData.filter(device => { + const matchByCode = this.selectedNodeCode && device.monitorId === this.selectedNodeCode + const matchById = device.monitorId === nodeId + const matchByName = device.monitorName === this.selectedNodeName + const isValid = device.monitorName !== '胶东机场' && + device.monitorId && + device.monitorName - if (matches) { - console.log('找到当前节点设备:', device.monitorName, device.monitorId, - matchByCode ? '(通过Code匹配)' : - matchById ? '(通过ID匹配)' : '(通过名称匹配)') - } - return matches - }) - console.log('当前节点设备数量:', currentDevices.length) - allDeviceData = [...allDeviceData, ...currentDevices] + return (matchByCode || matchById || matchByName) && isValid + }) + console.log('当前节点设备数量:', currentDevices.length) + allDeviceData = currentDevices + } } - // 处理子节点数据 - if (childNodesResponse.code === 200) { - const childNodesData = childNodesResponse.data || [] - console.log('子节点原始数据数量:', childNodesData.length) - - const childDevices = childNodesData.filter(device => { - const matches = device.monitorName !== '胶东机场' && - device.monitorId && - device.monitorName - if (matches) { - console.log('找到子节点设备:', device.monitorName, device.monitorId) - } - return matches - }) - console.log('子节点有效设备数量:', childDevices.length) - allDeviceData = [...allDeviceData, ...childDevices] - } - - console.log('合并后设备数据总数:', allDeviceData.length) - - + console.log('设备数据总数:', allDeviceData.length) // 去重处理(基于monitorId) const uniqueDevices = [] @@ -438,18 +425,6 @@ export default { this.deviceList = uniqueDevices console.log('最终设备列表数量:', this.deviceList.length) - - // 统计当前节点和子节点的设备数量 - const currentNodeDeviceCount = this.deviceList.filter(d => { - return (this.selectedNodeCode && d.monitorId === this.selectedNodeCode) || - d.monitorId === nodeId || - d.monitorName === this.selectedNodeName - }).length - - const childNodeDeviceCount = this.deviceList.length - currentNodeDeviceCount - - console.log('当前节点数据数量:', currentNodeDeviceCount) - console.log('子节点数据数量:', childNodeDeviceCount) console.log('=== 数据加载完成 ===') } catch (error) { @@ -491,8 +466,10 @@ export default { this.selectedNodeName = data.label this.selectedNodeType = data.type this.selectedNodeCode = data.code // 添加节点code字段 + // 判断是否为父节点:有children且children不为空 + this.isParentNode = !!(data.children && data.children.length > 0) - console.log('选中节点ID:', this.selectedNodeId, '节点名称:', this.selectedNodeName, '节点类型:', this.selectedNodeType, '节点Code:', this.selectedNodeCode) + console.log('选中节点ID:', this.selectedNodeId, '节点名称:', this.selectedNodeName, '节点类型:', this.selectedNodeType, '节点Code:', this.selectedNodeCode, '是否父节点:', this.isParentNode) // 根据选中的节点ID加载该节点下的设备数据 this.loadDeviceDataByNode(this.selectedNodeId) From ce9540d0201bb7a9ca5b5a9e2ba005982558993b Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 7 Jan 2026 16:33:49 +0800 Subject: [PATCH 3/5] =?UTF-8?q?style(ems\record\recordIOTInstant\index.vue?= =?UTF-8?q?):=20=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整颜色提升对比度便于识别设备类型标题 - 添加14px字体大小 - 设置2px 6px内边距 - 添加浅色背景突出文字 - 添加3px圆角样式 --- src/views/ems/record/recordIOTInstant/index.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/views/ems/record/recordIOTInstant/index.vue b/src/views/ems/record/recordIOTInstant/index.vue index 102bcaf..8857b27 100644 --- a/src/views/ems/record/recordIOTInstant/index.vue +++ b/src/views/ems/record/recordIOTInstant/index.vue @@ -991,9 +991,12 @@ export default { .device-group-title { font-weight: bold; - color: #303133; - padding-left: 5px; + color: #1f2d3d; /* 提升对比度,便于识别设备类型标题 */ + font-size: 14px; + padding: 2px 6px; border-left: 3px solid #409EFF; + background: #f5f7fa; /* 浅底突出文字 */ + border-radius: 3px; } .device-select-container .el-checkbox { From d6d6aa1bc1ec1704784c2066a57c752ee2a60f44 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 7 Jan 2026 17:32:52 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat(ems):=20=E6=B7=BB=E5=8A=A0=E6=8C=AF?= =?UTF-8?q?=E5=8A=A8=E5=8F=82=E6=95=B0=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在recordVibrationInstant页面添加振动参数下拉选择器 - 根据选中的振动参数动态显示对应的表格列 - 在currentVibrationCurve页面添加振动参数选择功能 - 曲线图表根据选中参数只显示单个图表,移除多图表显示 - 后端查询时传递振动参数进行数据过滤 - 前端对返回数据进行二次过滤,排除无效值 - 导出功能根据选中参数导出对应数据列 - 优化图表样式和坐标轴配置,提升显示效果 --- .../record/recordVibrationInstant/index.vue | 44 +- .../report/currentVibrationCurve/index.vue | 450 +++--------------- 2 files changed, 102 insertions(+), 392 deletions(-) diff --git a/src/views/ems/record/recordVibrationInstant/index.vue b/src/views/ems/record/recordVibrationInstant/index.vue index 4df889e..c30562e 100644 --- a/src/views/ems/record/recordVibrationInstant/index.vue +++ b/src/views/ems/record/recordVibrationInstant/index.vue @@ -135,6 +135,14 @@ end-placeholder="结束时间" > + + + + + + + + 搜索 重置 @@ -193,14 +201,14 @@ - - + + - + - + - + @@ -304,6 +312,8 @@ export default { name: "RecordIotenvInstant", data() { return { + // 选中的振动参数(默认振动速度) + selectedVibrationParam: 'vibrationSpeed', workUnitName:null, //下拉树List @@ -484,9 +494,16 @@ export default { return; } + // 传递选中的振动参数到后端,让SQL根据该参数过滤 + this.queryParams.vibrationParam = this.selectedVibrationParam; listRecordIotenvInstant(this.queryParams).then(response => { - this.recordIotenvInstantList = response.rows; - this.total = response.total; + // 后端已根据vibrationParam过滤,前端再做一次保险过滤 + const filteredRows = response.rows.filter(row => { + const value = parseFloat(row[this.selectedVibrationParam]); + return !isNaN(value) && value > 0 && value < 80; + }); + this.recordIotenvInstantList = filteredRows; + this.total = response.total; // 使用后端返回的总数 this.loading = false; }); }, @@ -601,10 +618,21 @@ export default { this.queryParams.params['endRecordTime'] = this.daterangeRecordTime[1]; // 振动页面固定传递monitorTypes=10,确保后端只导出振动相关列 this.queryParams.params['monitorTypes'] = '10'; + // 传递选中的振动参数,后端只导出该参数列 + this.queryParams.params['vibrationParam'] = this.selectedVibrationParam; + + // 参数中文名映射 + const paramNameMap = { + vibrationSpeed: '振动速度', + vibrationDisplacement: '振动位移', + vibrationAcceleration: '振动加速度', + vibrationTemp: '振动温度' + }; + const paramName = paramNameMap[this.selectedVibrationParam] || '振动数据'; this.download('ems/record/recordIotenvInstant/export', { ...this.queryParams - }, `振动数据_${this.daterangeRecordTime[0].substring(0,10)}_${this.daterangeRecordTime[1].substring(0,10)}.xlsx`) + }, `${paramName}_${this.daterangeRecordTime[0].substring(0,10)}_${this.daterangeRecordTime[1].substring(0,10)}.xlsx`) }, getTreeselect() { diff --git a/src/views/ems/report/currentVibrationCurve/index.vue b/src/views/ems/report/currentVibrationCurve/index.vue index 23ca66c..67b0172 100644 --- a/src/views/ems/report/currentVibrationCurve/index.vue +++ b/src/views/ems/report/currentVibrationCurve/index.vue @@ -59,16 +59,22 @@ > 分钟/点 + + + + + + + + 搜索 重置 - - - - + + @@ -96,6 +102,8 @@ export default { }, data() { return { + // 选中的振动参数(默认振动速度) + selectedVibrationParam: 'vibrationSpeed', //下拉树List baseMonitorInfoOptions: [], //左侧树结构List @@ -288,24 +296,45 @@ export default { this.handleQuery() }, - /** 振动曲线 */ + /** 振动曲线:根据选中参数只显示单个图表,过滤无效值保持曲线光滑 */ async getChart() { if (this.queryParams.monitorId == null) { return } let query = JSON.parse(JSON.stringify(this.queryParams)) - // const {data} = await vibrationInstantList(query) + // 传递选中的振动参数到后端,让SQL根据该参数过滤 + query.vibrationParam = this.selectedVibrationParam const {data} = await getRecordIotenvInstantList(query) - let option1 = { + + // 参数配置映射 + const paramConfig = { + vibrationSpeed: { name: '速度(mm/s)', title: '速度曲线', field: 'vibrationSpeed' }, + vibrationDisplacement: { name: '位移(μm)', title: '位移曲线', field: 'vibrationDisplacement' }, + vibrationAcceleration: { name: '加速度(g)', title: '加速度曲线', field: 'vibrationAcceleration' }, + vibrationTemp: { name: '温度(℃)', title: '温度曲线', field: 'vibrationTemp' } + } + + const config = paramConfig[this.selectedVibrationParam] + + // 过滤无效值:小于等于0或大于等于80的数值不显示 + const validPoints = data.filter(e => { + const value = parseFloat(e[config.field]) + return !isNaN(value) && value > 0 && value < 80 + }) + // 提取有效数据的时间和数值(保持一一对应) + const timeData = validPoints.map(e => e.recodeTime) + const validData = validPoints.map(e => parseFloat(e[config.field])) + + let option = { title: { - text: this.selectMonitorName + ' 速度曲线', + text: this.selectMonitorName + ' ' + config.title, x: 'center' }, grid: { top: '15%', - bottom: '10%', + bottom: '15%', left: '10%', - right: '3%' + right: '5%' }, tooltip: { trigger: 'axis', @@ -323,32 +352,36 @@ export default { right: 0 }, xAxis: { - data: data.map(e => e.recodeTime), + data: timeData, axisLine: { - show: true, //隐藏X轴轴线 + show: true, lineStyle: { color: '#000000' } }, axisTick: { - show: true //隐藏X轴刻度 + show: true }, axisLabel: { show: true, textStyle: { - color: '#000000' //X轴文字颜色 + color: '#000000' } } }, yAxis: [ { type: 'value', - name: '速度(mm/s)', + name: config.name, + min: 0, nameTextStyle: { color: '#000000' }, splitLine: { - show: false + show: true, + lineStyle: { + type: 'dashed' + } }, axisTick: { show: true @@ -363,387 +396,36 @@ export default { show: true, textStyle: { color: '#000000' + }, + formatter: function(value) { + return value.toFixed(2); // 保留2位小数 } } } ], series: [ { - name: '速度(mm/s)', - connectNulls: true, // 关键配置 + name: config.name, + connectNulls: true, // 跳过null值但保持曲线连续光滑 type: 'line', - smooth: true, //平滑曲线显示 - showAllSymbol: true, //显示所有图形。 - symbol: 'circle', //标记的图形为实心圆 - symbolSize: 10, //标记的大小 - // itemStyle: { - // //折线拐点标志的样式 - // color: "#058cff", - // }, - // lineStyle: { - // color: "#058cff", - // }, - // areaStyle: { - // color: "rgba(5,140,255, 0.2)", - // }, - data: data.map(e => e.vibrationSpeed) - }, + smooth: true, // 平滑曲线显示 + showAllSymbol: true, + symbol: 'circle', + symbolSize: 8, + data: validData + } ] } - let option2 = { - title: { - text: this.selectMonitorName + ' 温度曲线', - x: 'center' - }, - grid: { - top: '15%', - bottom: '10%', - left: '10%', - right: '3%' - }, - - dataZoom: [{ - type: 'slider' - }], - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow', - label: { - show: true - } - } - }, - legend: { - right: 0 - }, - xAxis: { - data: data.map(e => e.recodeTime), - axisLine: { - show: true, //隐藏X轴轴线 - lineStyle: { - color: '#000000' - } - }, - axisTick: { - show: true //隐藏X轴刻度 - }, - axisLabel: { - show: true, - textStyle: { - color: '#000000' //X轴文字颜色 - } - } - }, - yAxis: [ - { - type: 'value', - name: '温度(℃)', - nameTextStyle: { - color: '#000000' - }, - splitLine: { - show: false - }, - axisTick: { - show: true - }, - axisLine: { - show: true, - lineStyle: { - color: '#000000' - } - }, - axisLabel: { - show: true, - textStyle: { - color: '#000000' - } - } - } - ], - series: [ - { - name: '温度(℃)', - connectNulls: true, // 关键配置 - type: 'line', - smooth: true, //平滑曲线显示 - showAllSymbol: true, //显示所有图形。 - symbol: 'circle', //标记的图形为实心圆 - symbolSize: 10, //标记的大小 - // itemStyle: { - // //折线拐点标志的样式 - // color: "#058cff", - // }, - // lineStyle: { - // color: "#058cff", - // }, - // areaStyle: { - // color: "rgba(5,140,255, 0.2)", - // }, - data: data.map(e => e.vibrationTemp) - }, - ] - } - let option3 = { - title: { - text: this.selectMonitorName + ' 位移曲线', - x: 'center' - }, - grid: { - top: '15%', - bottom: '10%', - left: '10%', - right: '3%' - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow', - label: { - show: true - } - } - }, - dataZoom: [{ - type: 'slider' - }], - legend: { - right: 0 - }, - xAxis: { - data: data.map(e => e.recodeTime), - axisLine: { - show: true, //隐藏X轴轴线 - lineStyle: { - color: '#000000' - } - }, - axisTick: { - show: true //隐藏X轴刻度 - }, - axisLabel: { - show: true, - textStyle: { - color: '#000000' //X轴文字颜色 - } - } - }, - yAxis: [ - { - type: 'value', - name: '位移(um)', - nameTextStyle: { - color: '#000000' - }, - splitLine: { - show: false - }, - axisTick: { - show: true - }, - axisLine: { - show: true, - lineStyle: { - color: '#000000' - } - }, - axisLabel: { - show: true, - textStyle: { - color: '#000000' - } - } - } - ], - series: [ - { - name: '位移(um)', - connectNulls: true, // 关键配置 - type: 'line', - smooth: true, //平滑曲线显示 - showAllSymbol: true, //显示所有图形。 - symbol: 'circle', //标记的图形为实心圆 - symbolSize: 10, //标记的大小 - // itemStyle: { - // //折线拐点标志的样式 - // color: "#058cff", - // }, - // lineStyle: { - // color: "#058cff", - // }, - // areaStyle: { - // color: "rgba(5,140,255, 0.2)", - // }, - data: data.map(e => e.vibrationDisplacement) - }, - ] - } - let option4 = { - title: { - text: this.selectMonitorName + ' 加速度曲线', - x: 'center' - }, - grid: { - top: '15%', - bottom: '10%', - left: '10%', - right: '3%' - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow', - label: { - show: true - } - } - }, - dataZoom: [{ - type: 'slider' - }], - legend: { - right: 0 - }, - xAxis: { - data: data.map(e => e.recodeTime), - axisLine: { - show: true, //隐藏X轴轴线 - lineStyle: { - color: '#000000' - } - }, - axisTick: { - show: true //隐藏X轴刻度 - }, - axisLabel: { - show: true, - textStyle: { - color: '#000000' //X轴文字颜色 - } - } - }, - yAxis: [ - { - type: 'value', - name: '加速度(g)', - nameTextStyle: { - color: '#000000' - }, - splitLine: { - show: false - }, - axisTick: { - show: true - }, - axisLine: { - show: true, - lineStyle: { - color: '#000000' - } - }, - axisLabel: { - show: true, - textStyle: { - color: '#000000' - } - } - } - ], - series: [ - { - name: '加速度(g)', - connectNulls: true, // 关键配置 - type: 'line', - smooth: true, //平滑曲线显示 - showAllSymbol: true, //显示所有图形。 - symbol: 'circle', //标记的图形为实心圆 - symbolSize: 10, //标记的大小 - // itemStyle: { - // color: "#058cff", - // }, - // lineStyle: { - // color: "#058cff", - // }, - // areaStyle: { - // color: "rgba(5,140,255, 0.2)", - // }, - data: data.map(e => e.vibrationAcceleration) - }, - ] - } - this.$refs.Chart1.setData(option1) - this.$refs.Chart2.setData(option2) - this.$refs.Chart3.setData(option3) - this.$refs.Chart4.setData(option4) - echarts.connect(this.$refs.Chart1.chart, this.$refs.Chart2.chart, this.$refs.Chart3.chart, this.$refs.Chart4.chart) - this.$refs.Chart1.chart.on('datazoom', (e) => { - option2.dataZoom[0].start = e.start - option2.dataZoom[0].end = e.end - this.$refs.Chart2.setData(option2) - option3.dataZoom[0].start = e.start - option3.dataZoom[0].end = e.end - this.$refs.Chart3.setData(option3) - option4.dataZoom[0].start = e.start - option4.dataZoom[0].end = e.end - this.$refs.Chart4.setData(option4) - }) - this.$refs.Chart2.chart.on('datazoom', (e) => { - option1.dataZoom[0].start = e.start - option1.dataZoom[0].end = e.end - this.$refs.Chart1.setData(option1) - option3.dataZoom[0].start = e.start - option3.dataZoom[0].end = e.end - this.$refs.Chart3.setData(option3) - option4.dataZoom[0].start = e.start - option4.dataZoom[0].end = e.end - this.$refs.Chart4.setData(option4) - }) - this.$refs.Chart3.chart.on('datazoom', (e) => { - option2.dataZoom[0].start = e.start - option2.dataZoom[0].end = e.end - this.$refs.Chart2.setData(option2) - option1.dataZoom[0].start = e.start - option1.dataZoom[0].end = e.end - this.$refs.Chart1.setData(option1) - option4.dataZoom[0].start = e.start - option4.dataZoom[0].end = e.end - this.$refs.Chart4.setData(option4) - }) - this.$refs.Chart4.chart.on('datazoom', (e) => { - option2.dataZoom[0].start = e.start - option2.dataZoom[0].end = e.end - this.$refs.Chart2.setData(option2) - option3.dataZoom[0].start = e.start - option3.dataZoom[0].end = e.end - this.$refs.Chart3.setData(option3) - option1.dataZoom[0].start = e.start - option1.dataZoom[0].end = e.end - this.$refs.Chart4.setData(option1) - }) + this.$refs.VibrationChart.setData(option) } } } From 6aed905313cde247ea78fb0579aceb1a315a5c33 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 7 Jan 2026 17:33:25 +0800 Subject: [PATCH 5/5] =?UTF-8?q?perf(request):=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将请求超时间从10秒增加到100秒 --- src/utils/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/request.js b/src/utils/request.js index 3a776dc..7979bb8 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -18,7 +18,7 @@ const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: process.env.VUE_APP_BASE_API, // 超时 - timeout: 10000 + timeout: 100000 }) // request拦截器