20260812:1、给时间范围组件添加更加预设值;2、防止设备管理-历史记录组件页面动态刷新;3、运行状态-新增便捷命令

This commit is contained in:
zhugongping 2026-08-12 07:28:20 +08:00
parent a4996c1c5d
commit f1f9965089
7 changed files with 289 additions and 20 deletions

View File

@ -1,5 +1,6 @@
package com.fastbee.iot.service.impl; package com.fastbee.iot.service.impl;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.enums.DeviceDistributeTypeEnum; import com.fastbee.common.enums.DeviceDistributeTypeEnum;
import com.fastbee.common.enums.DeviceRecordTypeEnum; import com.fastbee.common.enums.DeviceRecordTypeEnum;
import com.fastbee.common.enums.StatusEnum; import com.fastbee.common.enums.StatusEnum;
@ -75,15 +76,20 @@ public class ProjectServiceImpl implements IProjectService {
// project.setTenantId(deptUserId); // project.setTenantId(deptUserId);
// } // }
Long userId = project.getResponsibleUserId(); Long userId = project.getResponsibleUserId();
LoginUser loginUser = getLoginUser();
if (userId == null) { if (userId == null) {
userId = getLoginUser().getUserId(); userId = loginUser.getUserId();
} }
boolean admin = SecurityUtils.isAdmin(userId); boolean admin = SecurityUtils.isAdmin(userId);
if (admin) { if (admin) {
project.setTenantId(userId);
project.setResponsibleUserId(null); project.setResponsibleUserId(null);
} else { } else {
project.setResponsibleUserId(userId); Long deptId = loginUser.getDeptId();
if (deptId == null || deptId == 0L) {
// 无机构用户看不到任何项目,与 selectUserListByScope 的空列表逻辑一致
return new ArrayList<>();
}
project.getParams().put("deptId", deptId);
} }
return projectMapper.selectProjectList(project); return projectMapper.selectProjectList(project);
} }

View File

@ -66,6 +66,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="projectCode != null and projectCode != ''"> and p.project_code = #{projectCode}</if> <if test="projectCode != null and projectCode != ''"> and p.project_code = #{projectCode}</if>
<if test="tenantId != null"> and p.tenant_id = #{tenantId}</if> <if test="tenantId != null"> and p.tenant_id = #{tenantId}</if>
<if test="responsibleUserId != null"> and p.responsible_user_id = #{responsibleUserId}</if> <if test="responsibleUserId != null"> and p.responsible_user_id = #{responsibleUserId}</if>
<if test="params.deptId != null and params.deptId != 0">
and p.responsible_user_id in (
select u.user_id
from sys_user u
where u.del_flag = '0'
and (u.dept_id = #{params.deptId}
or u.dept_id in (
select t.dept_id
from sys_dept t
where find_in_set(#{params.deptId}, t.ancestors)
))
)
</if>
<if test="projectStatus != null"> and p.project_status = #{projectStatus}</if> <if test="projectStatus != null"> and p.project_status = #{projectStatus}</if>
</where> </where>
GROUP BY p.project_id GROUP BY p.project_id

View File

@ -41,9 +41,15 @@
"dataCenter.history.384934-17": "data sources ", "dataCenter.history.384934-17": "data sources ",
"dataCenter.history.384934-18": "Please select the data source", "dataCenter.history.384934-18": "Please select the data source",
"dataCenter.history.384934-19": "Last 2 hours", "dataCenter.history.384934-19": "Last 2 hours",
"dataCenter.history.384934-20": "Last day", "dataCenter.history.384934-20": "Last 24 hours",
"dataCenter.history.384934-21": "In the past seven days", "dataCenter.history.384934-21": "In the past seven days",
"dataCenter.history.384934-22": "Last 30 days", "dataCenter.history.384934-22": "Last 30 days",
"dataCenter.history.384934-26": "Last 4 hours",
"dataCenter.history.384934-27": "Last 8 hours",
"dataCenter.history.384934-28": "Last 12 hours",
"dataCenter.history.384934-29": "Last 3 days",
"dataCenter.history.384934-30": "Last 5 days",
"dataCenter.history.384934-31": "Last 15 days",
"dataCenter.history.384934-23": "total", "dataCenter.history.384934-23": "total",
"dataCenter.history.384934-24": "Export Excel", "dataCenter.history.384934-24": "Export Excel",
"dataCenter.history.gwCalculated": "Gateway Calculated", "dataCenter.history.gwCalculated": "Gateway Calculated",

View File

@ -8,7 +8,7 @@
"dataCenter.analysis.349202-6": "重置", "dataCenter.analysis.349202-6": "重置",
"dataCenter.analysis.349202-7": "暂无数据", "dataCenter.analysis.349202-7": "暂无数据",
"dataCenter.analysis.349202-8": "最近2小时", "dataCenter.analysis.349202-8": "最近2小时",
"dataCenter.analysis.384934-25": "最近两天", "dataCenter.analysis.384934-25": "最近2天",
"dataCenter.analysis.349202-9": "最近一天", "dataCenter.analysis.349202-9": "最近一天",
"dataCenter.analysis.349202-10": "最近七天", "dataCenter.analysis.349202-10": "最近七天",
"dataCenter.analysis.349202-11": "最近三十天", "dataCenter.analysis.349202-11": "最近三十天",
@ -41,9 +41,15 @@
"dataCenter.history.384934-17": "数据来源", "dataCenter.history.384934-17": "数据来源",
"dataCenter.history.384934-18": "请选择数据来源", "dataCenter.history.384934-18": "请选择数据来源",
"dataCenter.history.384934-19": "最近2小时", "dataCenter.history.384934-19": "最近2小时",
"dataCenter.history.384934-20": "最近一天", "dataCenter.history.384934-20": "最近24小时",
"dataCenter.history.384934-21": "最近七天", "dataCenter.history.384934-21": "最近7天",
"dataCenter.history.384934-22": "最近三十天", "dataCenter.history.384934-22": "最近30天",
"dataCenter.history.384934-26": "最近4小时",
"dataCenter.history.384934-27": "最近8小时",
"dataCenter.history.384934-28": "最近12小时",
"dataCenter.history.384934-29": "最近3天",
"dataCenter.history.384934-30": "最近5天",
"dataCenter.history.384934-31": "最近15天",
"dataCenter.history.384934-23": "总量", "dataCenter.history.384934-23": "总量",
"dataCenter.history.384934-24": "导出Excel", "dataCenter.history.384934-24": "导出Excel",
"dataCenter.history.gwCalculated": "网关计算量", "dataCenter.history.gwCalculated": "网关计算量",

View File

@ -205,6 +205,30 @@ export default {
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]); picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
}, },
}, },
{
text: this.$t('dataCenter.history.384934-26'),
onClick(picker) {
const end = moment();
const start = moment().subtract(4, 'hours');
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
},
},
{
text: this.$t('dataCenter.history.384934-27'),
onClick(picker) {
const end = moment();
const start = moment().subtract(8, 'hours');
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
},
},
{
text: this.$t('dataCenter.history.384934-28'),
onClick(picker) {
const end = moment();
const start = moment().subtract(12, 'hours');
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
},
},
{ {
text: this.$t('dataCenter.history.384934-20'), text: this.$t('dataCenter.history.384934-20'),
onClick(picker) { onClick(picker) {
@ -221,6 +245,22 @@ export default {
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]); picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
}, },
}, },
{
text: this.$t('dataCenter.history.384934-29'),
onClick(picker) {
const end = moment();
const start = moment().subtract(3, 'days');
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
},
},
{
text: this.$t('dataCenter.history.384934-30'),
onClick(picker) {
const end = moment();
const start = moment().subtract(5, 'days');
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
},
},
{ {
text: this.$t('dataCenter.history.384934-21'), text: this.$t('dataCenter.history.384934-21'),
onClick(picker) { onClick(picker) {
@ -229,6 +269,14 @@ export default {
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]); picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
}, },
}, },
{
text: this.$t('dataCenter.history.384934-31'),
onClick(picker) {
const end = moment();
const start = moment().subtract(15, 'days');
picker.$emit('pick', [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]);
},
},
{ {
text: this.$t('dataCenter.history.384934-22'), text: this.$t('dataCenter.history.384934-22'),
onClick(picker) { onClick(picker) {
@ -392,11 +440,12 @@ export default {
}, },
}, },
watch: { watch: {
device: { // 只监听设备身份变化(deviceId),避免父页面实时更新 form 的其他字段
// (如 MQTT 推送的设备状态 status/isShadow/rssi)时把已查询的历史数据清空
'device.deviceId': {
handler() { handler() {
this.bootstrap(); this.bootstrap();
}, },
deep: true,
immediate: true, immediate: true,
}, },
defaultIdentifiers: { defaultIdentifiers: {

View File

@ -339,6 +339,33 @@ export default {
picker.$emit('pick', [start, end]); picker.$emit('pick', [start, end]);
}, },
}, },
{
text: this.$t('dataCenter.history.384934-26'),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 4);
picker.$emit('pick', [start, end]);
},
},
{
text: this.$t('dataCenter.history.384934-27'),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 8);
picker.$emit('pick', [start, end]);
},
},
{
text: this.$t('dataCenter.history.384934-28'),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 12);
picker.$emit('pick', [start, end]);
},
},
{ {
text: this.$t('dataCenter.history.384934-20'), text: this.$t('dataCenter.history.384934-20'),
onClick(picker) { onClick(picker) {
@ -357,6 +384,24 @@ export default {
picker.$emit('pick', [start, end]); picker.$emit('pick', [start, end]);
}, },
}, },
{
text: this.$t('dataCenter.history.384934-29'),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 3);
picker.$emit('pick', [start, end]);
},
},
{
text: this.$t('dataCenter.history.384934-30'),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 5);
picker.$emit('pick', [start, end]);
},
},
{ {
text: this.$t('dataCenter.history.384934-21'), text: this.$t('dataCenter.history.384934-21'),
onClick(picker) { onClick(picker) {
@ -366,6 +411,15 @@ export default {
picker.$emit('pick', [start, end]); picker.$emit('pick', [start, end]);
}, },
}, },
{
text: this.$t('dataCenter.history.384934-31'),
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 15);
picker.$emit('pick', [start, end]);
},
},
{ {
text: this.$t('dataCenter.history.384934-22'), text: this.$t('dataCenter.history.384934-22'),
onClick(picker) { onClick(picker) {

View File

@ -401,6 +401,43 @@
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
<!-- 便捷指令:一键下发,仅发送、不回显、不保存 -->
<div class="quick-command-panel">
<div class="quick-command-title">
<i class="el-icon-s-promotion"></i>
便捷指令
<span class="quick-command-tip">点击按钮一键下发指令</span>
</div>
<div class="quick-command-list">
<el-button
v-for="item in quickCommands"
:key="item.key"
class="quick-command-btn"
:type="item.danger ? 'danger' : 'primary'"
size="small"
:plain="!item.danger"
:loading="item.loading"
:title="'默认指令:' + item.cmd"
@click="sendQuickCommand(item)"
>
<i :class="item.icon"></i>
{{ item.name }}
</el-button>
</div>
<div class="quick-command-custom">
<span class="quick-command-custom-label">自定义指令</span>
<el-input
v-model="quickCustomCmd"
size="small"
placeholder="如:AT+CSQ\\r\\n 或 Hex:41 54 2b 43 53 51 0d 0a"
style="flex: 1"
clearable
@keyup.enter.native="sendQuickCustom"
/>
<el-button type="primary" size="small" :loading="quickCustomLoading" @click="sendQuickCustom">发送</el-button>
</div>
</div>
<!---设备状态(影子模式,value值不会更新)--> <!---设备状态(影子模式,value值不会更新)-->
<el-descriptions :column="1" border size="mini" v-if="deviceInfo.isShadow == 1 && deviceInfo.status != 3"> <el-descriptions :column="1" border size="mini" v-if="deviceInfo.isShadow == 1 && deviceInfo.status != 3">
<template slot="title"> <template slot="title">
@ -646,6 +683,19 @@ export default {
lbsLoading: false, lbsLoading: false,
saveLoadingImei: false, saveLoadingImei: false,
saveLoadingIccid: false, saveLoadingIccid: false,
// 便捷指令:一键下发,仅发送不回显、不保存
quickCommands: [
{
key: 'reset',
name: '重启设备',
cmd: 'AT+RESET\\r\\n',
icon: 'el-icon-refresh',
danger: true,
loading: false,
},
],
quickCustomCmd: '',
quickCustomLoading: false,
imeiPopoverVisible: false, // 控制IMEI指令编辑弹窗 imeiPopoverVisible: false, // 控制IMEI指令编辑弹窗
iccidPopoverVisible: false, // 控制ICCID指令编辑弹窗 iccidPopoverVisible: false, // 控制ICCID指令编辑弹窗
@ -706,15 +756,47 @@ export default {
}, },
async sendAt(type) { async sendAt(type) {
const cmd = type === 'imei' ? this.imeiCmd : type === 'iccid' ? this.iccidCmd : type === 'lbs' ? this.lbsCmd : '';
const loadingSetter = type === 'imei'
? (v) => (this.imeiLoading = v)
: type === 'iccid'
? (v) => (this.iccidLoading = v)
: type === 'lbs'
? (v) => (this.lbsLoading = v)
: null;
// 读取型指令:下发后轮询刷新设备信息,等待回显
await this.sendRawCommand(cmd, { loadingSetter, refreshAfter: true });
},
// 便捷指令:一键下发,仅发送、不回显、不保存
async sendQuickCommand(item) {
if (!item || !item.cmd) return;
await this.sendRawCommand(item.cmd, {
loadingSetter: (v) => (item.loading = v),
refreshAfter: false,
});
},
// 便捷指令:自定义指令发送(仅下发,不保存)
async sendQuickCustom() {
const cmd = String(this.quickCustomCmd || '').trim();
if (!cmd) {
this.$message({ type: 'warning', message: '请输入要下发的指令' });
return;
}
await this.sendRawCommand(cmd, {
loadingSetter: (v) => (this.quickCustomLoading = v),
refreshAfter: false,
});
},
async sendRawCommand(cmd, { loadingSetter = null, refreshAfter = true } = {}) {
try { try {
const cmd = type === 'imei' ? this.imeiCmd : type === 'iccid' ? this.iccidCmd : type === 'lbs' ? this.lbsCmd : '';
if (!cmd) { if (!cmd) {
this.$message({ type: 'warning', message: '指令不能为空' }); this.$message({ type: 'warning', message: '指令不能为空' });
return; return;
} }
if (type === 'imei') this.imeiLoading = true; if (loadingSetter) loadingSetter(true);
if (type === 'iccid') this.iccidLoading = true;
if (type === 'lbs') this.lbsLoading = true;
// FUNCTION_GET 下发链路最终会走 ByteUtils.convertToHexByteArray(command)。 // FUNCTION_GET 下发链路最终会走 ByteUtils.convertToHexByteArray(command)。
// 因此这里必须传“十六进制字节串”,而不是原始 ASCII。 // 因此这里必须传“十六进制字节串”,而不是原始 ASCII。
@ -737,15 +819,15 @@ export default {
this.$message({ type: 'success', message: '已下发指令,等待设备回复解析…' }); this.$message({ type: 'success', message: '已下发指令,等待设备回复解析…' });
} }
// 后端收到回复后会自动解析并写入数据库,这里做一次轻量轮询刷新3秒、5秒各一次 // 读取型指令:后端收到回复后会自动解析并写入数据库,这里做轻量轮询刷新(3秒、8秒各一次)
setTimeout(() => this.refreshDeviceBaseInfo(), 3000); if (refreshAfter) {
setTimeout(() => this.refreshDeviceBaseInfo(), 8000); setTimeout(() => this.refreshDeviceBaseInfo(), 3000);
setTimeout(() => this.refreshDeviceBaseInfo(), 8000);
}
} catch (e) { } catch (e) {
this.$message({ type: 'error', message: e && e.message ? e.message : '指令下发失败' }); this.$message({ type: 'error', message: e && e.message ? e.message : '指令下发失败' });
} finally { } finally {
this.imeiLoading = false; if (loadingSetter) loadingSetter(false);
this.iccidLoading = false;
this.lbsLoading = false;
} }
}, },
@ -1396,6 +1478,59 @@ export default {
width: 100%; width: 100%;
} }
/* 便捷指令:一键下发,仅发送不回显、不保存 */
.quick-command-panel {
background-color: #f5f7fa;
border: 1px solid #ebeef5;
border-radius: 15px;
padding: 16px 20px;
margin-bottom: 50px;
}
.quick-command-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
}
.quick-command-title i {
font-size: 16px;
color: #409eff;
}
.quick-command-tip {
font-size: 12px;
font-weight: 400;
color: #909399;
}
.quick-command-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 12px;
}
.quick-command-btn i {
margin-right: 4px;
}
.quick-command-custom {
display: flex;
align-items: center;
gap: 10px;
}
.quick-command-custom-label {
font-size: 12px;
color: #909399;
white-space: nowrap;
}
/* 重写滑动块样式 */ /* 重写滑动块样式 */
.el-slider__bar { .el-slider__bar {
height: 18px; height: 18px;