feat:日志报警页面更新
This commit is contained in:
@ -25,8 +25,8 @@
|
|||||||
<view class="table-cell header-cell type-column">种类</view>
|
<view class="table-cell header-cell type-column">种类</view>
|
||||||
<view class="table-cell header-cell time-column">时间</view>
|
<view class="table-cell header-cell time-column">时间</view>
|
||||||
<view class="table-cell header-cell level-column">级别</view>
|
<view class="table-cell header-cell level-column">级别</view>
|
||||||
<view class="table-cell header-cell action-column">处置</view>
|
<!-- <view class="table-cell header-cell action-column">处置</view> -->
|
||||||
<view class="table-cell header-cell action-time-column">时间</view>
|
<!-- <view class="table-cell header-cell action-time-column">时间</view> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 表格内容 -->
|
<!-- 表格内容 -->
|
||||||
@ -55,11 +55,12 @@
|
|||||||
<view class="table-cell content-column">{{ alarm.content }}</view>
|
<view class="table-cell content-column">{{ alarm.content }}</view>
|
||||||
<view class="table-cell type-column">{{ alarm.type }}</view>
|
<view class="table-cell type-column">{{ alarm.type }}</view>
|
||||||
<view class="table-cell time-column">{{ alarm.time }}</view>
|
<view class="table-cell time-column">{{ alarm.time }}</view>
|
||||||
<view class="table-cell level-column" :class="getLevelClass(alarm.level)">
|
<!-- <view class="table-cell level-column" :class="getLevelClass(alarm.level)"> -->
|
||||||
|
<view class="table-cell level-column">
|
||||||
{{ alarm.level }}
|
{{ alarm.level }}
|
||||||
</view>
|
</view>
|
||||||
<view class="table-cell action-column">{{ alarm.action }}</view>
|
<!-- <view class="table-cell action-column">{{ alarm.action }}</view> -->
|
||||||
<view class="table-cell action-time-column">{{ alarm.actionTime }}</view>
|
<!-- <view class="table-cell action-time-column">{{ alarm.actionTime }}</view> -->
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -73,6 +74,17 @@
|
|||||||
<text class="table-empty-text">暂无数据</text>
|
<text class="table-empty-text">暂无数据</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多提示 -->
|
||||||
|
<view class="load-more-container" v-if="isLoadingMore">
|
||||||
|
<view class="load-more-spinner"></view>
|
||||||
|
<text class="load-more-text">正在加载更多...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 没有更多数据提示 -->
|
||||||
|
<!-- <view class="no-more-container" v-if="!hasMoreData && alarmList.length > 0 && !isLoadingMore">
|
||||||
|
<text class="no-more-text">已加载全部数据</text>
|
||||||
|
</view> -->
|
||||||
|
|
||||||
<!-- 底部间距,确保最后一条记录完全显示 -->
|
<!-- 底部间距,确保最后一条记录完全显示 -->
|
||||||
<view class="table-bottom-spacing"></view>
|
<view class="table-bottom-spacing"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
@ -84,6 +96,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
|
import { alertApi, eventApi } from '@/utils/api.js';
|
||||||
|
|
||||||
// 报警数据
|
// 报警数据
|
||||||
const alarmList = ref([]);
|
const alarmList = ref([]);
|
||||||
@ -91,6 +104,12 @@ const isLoading = ref(false);
|
|||||||
const isConnected = ref(false);
|
const isConnected = ref(false);
|
||||||
const hasInitialized = ref(false);
|
const hasInitialized = ref(false);
|
||||||
|
|
||||||
|
// 分页相关
|
||||||
|
const currentPage = ref(0);
|
||||||
|
const pageSize = ref(20);
|
||||||
|
const hasMoreData = ref(true);
|
||||||
|
const isLoadingMore = ref(false);
|
||||||
|
|
||||||
// 滚动相关
|
// 滚动相关
|
||||||
const scrollTop = ref(0);
|
const scrollTop = ref(0);
|
||||||
const isScrolling = ref(false);
|
const isScrolling = ref(false);
|
||||||
@ -100,167 +119,107 @@ const isScrolling = ref(false);
|
|||||||
// MQTT报警服务接口(预留)
|
// MQTT报警服务接口(预留)
|
||||||
const mqttAlarmService = {
|
const mqttAlarmService = {
|
||||||
// 连接MQTT服务器
|
// 连接MQTT服务器
|
||||||
connect: async () => {
|
// connect: async () => {
|
||||||
console.log('MQTT报警服务连接中...');
|
// console.log('MQTT报警服务连接中...');
|
||||||
try {
|
// try {
|
||||||
// 模拟连接延迟
|
// // 模拟连接延迟
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
// await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
isConnected.value = true;
|
// isConnected.value = true;
|
||||||
console.log('MQTT报警服务连接成功');
|
// console.log('MQTT报警服务连接成功');
|
||||||
return Promise.resolve();
|
// return Promise.resolve();
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error('MQTT报警连接失败:', error);
|
// console.error('MQTT报警连接失败:', error);
|
||||||
isConnected.value = false;
|
// isConnected.value = false;
|
||||||
return Promise.reject(error);
|
// return Promise.reject(error);
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
|
|
||||||
// 订阅报警数据
|
// 订阅报警数据
|
||||||
subscribeAlarmData: () => {
|
// subscribeAlarmData: () => {
|
||||||
console.log('订阅系统报警数据');
|
// console.log('订阅系统报警数据');
|
||||||
// 这里后期会实现真实的MQTT报警订阅
|
// // 这里后期会实现真实的MQTT报警订阅
|
||||||
return Promise.resolve();
|
// return Promise.resolve();
|
||||||
},
|
// },
|
||||||
|
|
||||||
// 获取历史报警记录
|
// 获取历史报警记录(分页)
|
||||||
getHistoryAlarms: async (limit = 50) => {
|
getHistoryAlarms: async (page = 0, size = 20, isLoadMore = false) => {
|
||||||
console.log(`获取历史报警记录,限制${limit}条`);
|
console.log(`获取历史报警记录,页码:${page}, 每页:${size}条`);
|
||||||
try {
|
try {
|
||||||
|
if (!isLoadMore) {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
hasInitialized.value = true;
|
hasInitialized.value = true;
|
||||||
// 模拟请求延迟
|
} else {
|
||||||
await new Promise(resolve => setTimeout(resolve, 800));
|
isLoadingMore.value = true;
|
||||||
|
}
|
||||||
// 模拟报警数据
|
|
||||||
const mockAlarms = [
|
// 调用分页获取告警接口
|
||||||
{
|
const response = await alertApi.getList({
|
||||||
content: '湿度45%超标',
|
page: page,
|
||||||
type: '参数超标',
|
size: size
|
||||||
time: '2025-9-3-12:01',
|
});
|
||||||
level: 'A',
|
|
||||||
action: '恢复',
|
console.log('📊 获取告警数据响应:', response);
|
||||||
actionTime: '2025-9-3-13:11'
|
|
||||||
},
|
// 处理响应数据
|
||||||
{
|
if (response && response.data) {
|
||||||
content: '温度28℃过高',
|
const newAlarms = response.data.map(item => ({
|
||||||
type: '参数超标',
|
content: item.content || '未知报警',
|
||||||
time: '2025-9-3-11:45',
|
type: item.category || '未知类型',
|
||||||
level: 'B',
|
time: formatDateTime(item.alertTime) || '未知时间',
|
||||||
action: '调整',
|
level: mapLevelToDisplay(item.level) || 'C',
|
||||||
actionTime: '2025-9-3-12:30'
|
action: item.action || '待处理',
|
||||||
},
|
actionTime: formatDateTime(item.actionTime) || '未知时间'
|
||||||
{
|
}));
|
||||||
content: '洁净度异常',
|
|
||||||
type: '环境异常',
|
if (isLoadMore) {
|
||||||
time: '2025-9-3-11:20',
|
// 加载更多时追加数据
|
||||||
level: 'A',
|
alarmList.value = [...alarmList.value, ...newAlarms];
|
||||||
action: '清理',
|
} else {
|
||||||
actionTime: '2025-9-3-11:50'
|
// 首次加载时替换数据
|
||||||
},
|
alarmList.value = newAlarms;
|
||||||
{
|
}
|
||||||
content: '设备通讯中断',
|
|
||||||
type: '设备故障',
|
// 判断是否还有更多数据
|
||||||
time: '2025-9-3-10:55',
|
hasMoreData.value = newAlarms.length === size;
|
||||||
level: 'C',
|
currentPage.value = page;
|
||||||
action: '重启',
|
|
||||||
actionTime: '2025-9-3-11:05'
|
// 保存查询事件(只在首次加载时保存)
|
||||||
},
|
if (!isLoadMore) {
|
||||||
{
|
await createQueryEvent('success', newAlarms.length);
|
||||||
content: '压力值偏低',
|
}
|
||||||
type: '参数异常',
|
} else {
|
||||||
time: '2025-9-3-10:30',
|
console.warn('⚠️ 响应数据格式异常:', response);
|
||||||
level: 'B',
|
if (!isLoadMore) {
|
||||||
action: '检查',
|
alarmList.value = [];
|
||||||
actionTime: '2025-9-3-10:45'
|
}
|
||||||
},
|
hasMoreData.value = false;
|
||||||
{
|
|
||||||
content: '电源电压不稳',
|
// 保存查询事件(无数据)
|
||||||
type: '电气故障',
|
if (!isLoadMore) {
|
||||||
time: '2025-9-3-10:10',
|
await createQueryEvent('empty', 0);
|
||||||
level: 'A',
|
}
|
||||||
action: '更换',
|
|
||||||
actionTime: '2025-9-3-10:25'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '传感器数据异常',
|
|
||||||
type: '设备异常',
|
|
||||||
time: '2025-9-3-09:50',
|
|
||||||
level: 'B',
|
|
||||||
action: '校准',
|
|
||||||
actionTime: '2025-9-3-10:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '网络连接超时',
|
|
||||||
type: '通讯故障',
|
|
||||||
time: '2025-9-3-09:30',
|
|
||||||
level: 'C',
|
|
||||||
action: '重连',
|
|
||||||
actionTime: '2025-9-3-09:35'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '内存使用率过高',
|
|
||||||
type: '系统异常',
|
|
||||||
time: '2025-9-3-09:15',
|
|
||||||
level: 'B',
|
|
||||||
action: '清理',
|
|
||||||
actionTime: '2025-9-3-09:20'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '磁盘空间不足',
|
|
||||||
type: '存储异常',
|
|
||||||
time: '2025-9-3-09:00',
|
|
||||||
level: 'A',
|
|
||||||
action: '扩容',
|
|
||||||
actionTime: '2025-9-3-09:10'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: 'CPU温度过高',
|
|
||||||
type: '硬件故障',
|
|
||||||
time: '2025-9-3-08:45',
|
|
||||||
level: 'A',
|
|
||||||
action: '散热',
|
|
||||||
actionTime: '2025-9-3-08:50'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '数据库连接失败',
|
|
||||||
type: '数据异常',
|
|
||||||
time: '2025-9-3-08:30',
|
|
||||||
level: 'B',
|
|
||||||
action: '修复',
|
|
||||||
actionTime: '2025-9-3-08:35'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '配置文件损坏',
|
|
||||||
type: '配置异常',
|
|
||||||
time: '2025-9-3-08:15',
|
|
||||||
level: 'C',
|
|
||||||
action: '恢复',
|
|
||||||
actionTime: '2025-9-3-08:20'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '服务进程异常',
|
|
||||||
type: '进程故障',
|
|
||||||
time: '2025-9-3-08:00',
|
|
||||||
level: 'B',
|
|
||||||
action: '重启',
|
|
||||||
actionTime: '2025-9-3-08:05'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
content: '日志文件过大',
|
|
||||||
type: '存储异常',
|
|
||||||
time: '2025-9-3-07:45',
|
|
||||||
level: 'C',
|
|
||||||
action: '压缩',
|
|
||||||
actionTime: '2025-9-3-07:50'
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
|
|
||||||
alarmList.value = mockAlarms;
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
return Promise.resolve(mockAlarms);
|
isLoadingMore.value = false;
|
||||||
|
return Promise.resolve(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取历史报警记录失败:', error);
|
console.error('❌ 获取历史报警记录失败:', error);
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
isLoadingMore.value = false;
|
||||||
|
|
||||||
|
// 保存查询事件(错误)
|
||||||
|
if (!isLoadMore) {
|
||||||
|
await createQueryEvent('error', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示错误提示
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取报警记录失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -367,6 +326,39 @@ const mqttAlarmService = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 映射报警级别到显示格式
|
||||||
|
const mapLevelToDisplay = (level) => {
|
||||||
|
const levelMap = {
|
||||||
|
'高危': 'A',
|
||||||
|
'中危': 'B',
|
||||||
|
'低危': 'C',
|
||||||
|
'high': 'A',
|
||||||
|
'medium': 'B',
|
||||||
|
'low': 'C'
|
||||||
|
};
|
||||||
|
return levelMap[level] || 'C';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
const formatDateTime = (dateString) => {
|
||||||
|
if (!dateString) return '未知时间';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('时间格式化失败:', error);
|
||||||
|
return dateString; // 如果格式化失败,返回原始字符串
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取级别样式类
|
// 获取级别样式类
|
||||||
const getLevelClass = (level) => {
|
const getLevelClass = (level) => {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
@ -400,8 +392,19 @@ const onScroll = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onScrollToLower = () => {
|
const onScrollToLower = () => {
|
||||||
console.log('滚动到底部');
|
console.log('滚动到底部,尝试加载更多数据');
|
||||||
// 可以在这里添加加载更多数据的逻辑
|
|
||||||
|
// 如果正在加载或没有更多数据,则不处理
|
||||||
|
if (isLoadingMore.value || !hasMoreData.value) {
|
||||||
|
console.log('正在加载中或无更多数据,跳过');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载下一页数据
|
||||||
|
const nextPage = currentPage.value + 1;
|
||||||
|
console.log(`📄 加载第${nextPage}页数据`);
|
||||||
|
|
||||||
|
mqttAlarmService.getHistoryAlarms(nextPage, pageSize.value, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 滚动到顶部
|
// 滚动到顶部
|
||||||
@ -442,16 +445,78 @@ const stopRealtimeAlarm = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 创建查询事件
|
||||||
|
const createQueryEvent = async (status, dataCount) => {
|
||||||
|
try {
|
||||||
|
const currentTime = formatDateTime(new Date().toISOString())
|
||||||
|
const queryEvent = {
|
||||||
|
eventType: "报警记录查询",
|
||||||
|
eventTime: currentTime,
|
||||||
|
status: getEventStatus(status),
|
||||||
|
description: getEventDescription(status, dataCount),
|
||||||
|
deviceId: "ALARM_QUERY_001"
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📤 提交报警查询事件:', queryEvent)
|
||||||
|
const response = await eventApi.create(queryEvent)
|
||||||
|
console.log('✅ 报警查询事件创建成功:', response)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 报警查询事件创建失败:', error)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取事件状态
|
||||||
|
const getEventStatus = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'success': '已完成',
|
||||||
|
'empty': '已完成',
|
||||||
|
'error': '失败'
|
||||||
|
};
|
||||||
|
return statusMap[status] || '未知';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取事件描述
|
||||||
|
const getEventDescription = (status, dataCount) => {
|
||||||
|
const descriptions = {
|
||||||
|
'success': `成功查询到${dataCount}条报警记录数据`,
|
||||||
|
'empty': '查询报警记录,但未找到数据',
|
||||||
|
'error': '查询报警记录时发生错误'
|
||||||
|
};
|
||||||
|
return descriptions[status] || '未知查询状态';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新数据方法
|
||||||
|
const refreshData = async () => {
|
||||||
|
console.log('🔄 刷新报警记录数据')
|
||||||
|
try {
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage.value = 0;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
alarmList.value = [];
|
||||||
|
|
||||||
|
// 重新获取第一页数据
|
||||||
|
await mqttAlarmService.getHistoryAlarms(0, pageSize.value, false);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 刷新数据失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
refreshData
|
||||||
|
});
|
||||||
|
|
||||||
// 组件生命周期
|
// 组件生命周期
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
// 连接MQTT并初始化
|
// 连接MQTT并初始化
|
||||||
await mqttAlarmService.connect();
|
// await mqttAlarmService.connect();
|
||||||
await mqttAlarmService.subscribeAlarmData();
|
// await mqttAlarmService.subscribeAlarmData();
|
||||||
await mqttAlarmService.getHistoryAlarms();
|
await mqttAlarmService.getHistoryAlarms(0, pageSize.value, false);
|
||||||
|
|
||||||
// 开始实时报警获取
|
// 开始实时报警获取
|
||||||
startRealtimeAlarm();
|
// startRealtimeAlarm();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('报警系统初始化失败:', error);
|
console.error('报警系统初始化失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -838,4 +903,44 @@ onUnmounted(() => {
|
|||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载更多样式
|
||||||
|
.load-more-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx 20rpx;
|
||||||
|
gap: 15rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-spinner {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border: 3rpx solid #e8eaed;
|
||||||
|
border-top: 3rpx solid #5f6368;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #5f6368;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30rpx 20rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9aa0a6;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -67,6 +67,17 @@
|
|||||||
<text class="table-empty-text">暂无数据</text>
|
<text class="table-empty-text">暂无数据</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多提示 -->
|
||||||
|
<view class="load-more-container" v-if="isLoadingMore">
|
||||||
|
<view class="load-more-spinner"></view>
|
||||||
|
<text class="load-more-text">正在加载更多日志...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 没有更多数据提示 -->
|
||||||
|
<view class="no-more-container" v-if="!hasMoreData && logList.length > 0 && !isLoadingMore">
|
||||||
|
<text class="no-more-text">已加载全部数据</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 底部间距,确保最后一条记录完全显示 -->
|
<!-- 底部间距,确保最后一条记录完全显示 -->
|
||||||
<view class="table-bottom-spacing"></view>
|
<view class="table-bottom-spacing"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
@ -78,6 +89,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
|
import { eventApi } from '@/utils/api.js';
|
||||||
|
|
||||||
// 日志数据
|
// 日志数据
|
||||||
const logList = ref([]);
|
const logList = ref([]);
|
||||||
@ -85,12 +97,50 @@ const isLoading = ref(false);
|
|||||||
const isConnected = ref(false);
|
const isConnected = ref(false);
|
||||||
const hasInitialized = ref(false);
|
const hasInitialized = ref(false);
|
||||||
|
|
||||||
|
// 分页相关
|
||||||
|
const currentPage = ref(0);
|
||||||
|
const pageSize = ref(20);
|
||||||
|
const hasMoreData = ref(true);
|
||||||
|
const isLoadingMore = ref(false);
|
||||||
|
|
||||||
// 滚动相关
|
// 滚动相关
|
||||||
const scrollTop = ref(0);
|
const scrollTop = ref(0);
|
||||||
const isScrolling = ref(false);
|
const isScrolling = ref(false);
|
||||||
|
|
||||||
// 移除空行占位逻辑,没有数据时只显示"暂无数据"
|
// 移除空行占位逻辑,没有数据时只显示"暂无数据"
|
||||||
|
|
||||||
|
// 状态映射函数
|
||||||
|
const mapStatusToDisplay = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'已完成': '正常',
|
||||||
|
'进行中': '进行中',
|
||||||
|
'失败': '异常',
|
||||||
|
'错误': '异常',
|
||||||
|
'成功': '正常',
|
||||||
|
'正常': '正常',
|
||||||
|
'异常': '异常'
|
||||||
|
};
|
||||||
|
return statusMap[status] || status || '未知';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 时间格式化函数
|
||||||
|
const formatDateTime = (dateString) => {
|
||||||
|
if (!dateString) return '未知时间';
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('时间格式化失败:', error);
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// MQTT日志服务接口(预留)
|
// MQTT日志服务接口(预留)
|
||||||
const mqttLogService = {
|
const mqttLogService = {
|
||||||
// 连接MQTT服务器
|
// 连接MQTT服务器
|
||||||
@ -116,191 +166,55 @@ const mqttLogService = {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取历史日志
|
// 获取历史日志(分页)
|
||||||
getHistoryLogs: async (limit = 50) => {
|
getHistoryLogs: async (page = 0, size = 20, isLoadMore = false) => {
|
||||||
console.log(`获取历史日志,限制${limit}条`);
|
console.log(`📄 加载第${page}页数据,每页${size}条`);
|
||||||
try {
|
try {
|
||||||
|
if (!isLoadMore) {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
} else {
|
||||||
|
isLoadingMore.value = true;
|
||||||
|
}
|
||||||
hasInitialized.value = true;
|
hasInitialized.value = true;
|
||||||
// 模拟请求延迟
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 800));
|
|
||||||
|
|
||||||
// 模拟日志数据
|
// 调用事件API获取分页数据
|
||||||
const mockLogs = [
|
const response = await eventApi.getList({ page: page, size: size });
|
||||||
{
|
|
||||||
event: '开机',
|
// 将API数据转换为日志格式
|
||||||
time: '2025-9-3-12:01',
|
const newLogs = response.data.map(item => ({
|
||||||
status: '正常'
|
event: item.eventType || '未知事件',
|
||||||
},
|
time: formatDateTime(item.eventTime) || '未知时间',
|
||||||
{
|
status: mapStatusToDisplay(item.status) || '未知'
|
||||||
event: '报警',
|
}));
|
||||||
time: '2025-9-3-12:01',
|
|
||||||
status: '异常'
|
if (isLoadMore) {
|
||||||
},
|
// 加载更多时追加数据
|
||||||
{
|
logList.value = [...logList.value, ...newLogs];
|
||||||
event: '系统启动',
|
} else {
|
||||||
time: '2025-9-3-11:58',
|
// 首次加载时替换数据
|
||||||
status: '正常'
|
logList.value = newLogs;
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '温度检测',
|
|
||||||
time: '2025-9-3-11:55',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '湿度异常',
|
|
||||||
time: '2025-9-3-11:52',
|
|
||||||
status: '异常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '设备自检',
|
|
||||||
time: '2025-9-3-11:50',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '网络连接',
|
|
||||||
time: '2025-9-3-11:48',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '参数校准',
|
|
||||||
time: '2025-9-3-11:45',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '数据备份',
|
|
||||||
time: '2025-9-3-11:40',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '系统更新',
|
|
||||||
time: '2025-9-3-11:35',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '安全检查',
|
|
||||||
time: '2025-9-3-11:30',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '性能监控',
|
|
||||||
time: '2025-9-3-11:25',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '日志清理',
|
|
||||||
time: '2025-9-3-11:20',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '配置更新',
|
|
||||||
time: '2025-9-3-11:15',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '服务重启',
|
|
||||||
time: '2025-9-3-11:10',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '数据库连接',
|
|
||||||
time: '2025-9-3-11:05',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '缓存清理',
|
|
||||||
time: '2025-9-3-11:00',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '定时任务',
|
|
||||||
time: '2025-9-3-10:55',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '内存检查',
|
|
||||||
time: '2025-9-3-10:50',
|
|
||||||
status: '正常'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: '磁盘检查',
|
|
||||||
time: '2025-9-3-10:45',
|
|
||||||
status: '正常'
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
|
|
||||||
logList.value = mockLogs;
|
// 更新分页状态
|
||||||
isLoading.value = false;
|
currentPage.value = page;
|
||||||
return Promise.resolve(mockLogs);
|
hasMoreData.value = newLogs.length === size; // 如果返回的数据少于请求的数量,说明没有更多数据
|
||||||
|
|
||||||
|
console.log(`✅ 第${page}页数据加载完成,共${newLogs.length}条`);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取历史日志失败:', error);
|
console.error('❌ 获取历史日志失败:', error);
|
||||||
isLoading.value = false;
|
if (!isLoadMore) {
|
||||||
return Promise.reject(error);
|
// 首次加载失败时显示空数据
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取实时日志
|
|
||||||
getRealtimeLogs: async () => {
|
|
||||||
console.log('获取实时日志');
|
|
||||||
try {
|
|
||||||
// 模拟实时日志数据
|
|
||||||
const events = ['温度监测', '湿度检测', '设备巡检', '数据同步', '状态上报'];
|
|
||||||
const statuses = ['正常', '异常', '警告'];
|
|
||||||
|
|
||||||
const newLog = {
|
|
||||||
event: events[Math.floor(Math.random() * events.length)],
|
|
||||||
time: new Date().toLocaleString('zh-CN', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'numeric',
|
|
||||||
day: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
}).replace(/\//g, '-').replace(', ', '-'),
|
|
||||||
status: statuses[Math.floor(Math.random() * statuses.length)]
|
|
||||||
};
|
|
||||||
|
|
||||||
// 添加到日志列表顶部
|
|
||||||
logList.value.unshift(newLog);
|
|
||||||
|
|
||||||
// 限制日志数量,保持最新的50条
|
|
||||||
if (logList.value.length > 50) {
|
|
||||||
logList.value = logList.value.slice(0, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自动滚动到顶部显示最新日志
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollToTop();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
return Promise.resolve(newLog);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取实时日志失败:', error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 清空日志
|
|
||||||
clearLogs: async () => {
|
|
||||||
console.log('清空日志');
|
|
||||||
try {
|
|
||||||
// 模拟清空操作
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
|
||||||
logList.value = [];
|
logList.value = [];
|
||||||
uni.showToast({
|
}
|
||||||
title: '日志已清空',
|
} finally {
|
||||||
icon: 'success'
|
isLoading.value = false;
|
||||||
});
|
isLoadingMore.value = false;
|
||||||
return Promise.resolve();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('清空日志失败:', error);
|
|
||||||
uni.showToast({
|
|
||||||
title: '清空失败',
|
|
||||||
icon: 'error'
|
|
||||||
});
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 断开连接
|
// 断开连接
|
||||||
disconnect: () => {
|
disconnect: () => {
|
||||||
console.log('MQTT日志服务断开连接');
|
console.log('MQTT日志服务断开连接');
|
||||||
@ -342,8 +256,15 @@ const onScroll = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onScrollToLower = () => {
|
const onScrollToLower = () => {
|
||||||
console.log('滚动到底部');
|
console.log('📜 滚动到底部,触发加载更多');
|
||||||
// 可以在这里添加加载更多数据的逻辑
|
if (isLoadingMore.value || !hasMoreData.value) {
|
||||||
|
console.log('⏸️ 正在加载中或没有更多数据,跳过加载');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextPage = currentPage.value + 1;
|
||||||
|
console.log(`📄 开始加载第${nextPage}页数据`);
|
||||||
|
mqttLogService.getHistoryLogs(nextPage, pageSize.value, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 滚动到顶部
|
// 滚动到顶部
|
||||||
@ -351,49 +272,15 @@ const scrollToTop = () => {
|
|||||||
scrollTop.value = scrollTop.value === 0 ? 1 : 0;
|
scrollTop.value = scrollTop.value === 0 ? 1 : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 滚动到底部
|
|
||||||
const scrollToBottom = () => {
|
|
||||||
// 使用nextTick确保DOM更新完成
|
|
||||||
nextTick(() => {
|
|
||||||
// 计算滚动到底部的位置
|
|
||||||
const scrollHeight = logList.value.length * 80; // 假设每行80rpx
|
|
||||||
scrollTop.value = scrollHeight + 100; // 额外100rpx确保完全滚动到底部
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 定时获取实时日志
|
|
||||||
let realtimeTimer = null;
|
|
||||||
|
|
||||||
const startRealtimeLog = () => {
|
|
||||||
if (realtimeTimer) return;
|
|
||||||
|
|
||||||
realtimeTimer = setInterval(() => {
|
|
||||||
if (isConnected.value && !isLoading.value) {
|
|
||||||
// 30%概率生成新日志
|
|
||||||
if (Math.random() < 0.3) {
|
|
||||||
mqttLogService.getRealtimeLogs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 5000); // 每5秒检查一次
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopRealtimeLog = () => {
|
|
||||||
if (realtimeTimer) {
|
|
||||||
clearInterval(realtimeTimer);
|
|
||||||
realtimeTimer = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 组件生命周期
|
// 组件生命周期
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
// 连接MQTT并初始化
|
// 连接MQTT并初始化
|
||||||
await mqttLogService.connect();
|
// await mqttLogService.connect();
|
||||||
await mqttLogService.subscribeLogData();
|
// await mqttLogService.subscribeLogData();
|
||||||
await mqttLogService.getHistoryLogs();
|
await mqttLogService.getHistoryLogs(0, pageSize.value, false);
|
||||||
|
|
||||||
// 开始实时日志获取
|
|
||||||
startRealtimeLog();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('日志系统初始化失败:', error);
|
console.error('日志系统初始化失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -403,8 +290,28 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 刷新数据方法
|
||||||
|
const refreshData = async () => {
|
||||||
|
console.log('🔄 刷新系统日志数据')
|
||||||
|
try {
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage.value = 0;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
logList.value = [];
|
||||||
|
|
||||||
|
// 重新获取第一页数据
|
||||||
|
await mqttLogService.getHistoryLogs(0, pageSize.value, false);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 刷新数据失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
refreshData
|
||||||
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
stopRealtimeLog();
|
|
||||||
mqttLogService.disconnect();
|
mqttLogService.disconnect();
|
||||||
|
|
||||||
// 清理滚动定时器
|
// 清理滚动定时器
|
||||||
@ -759,4 +666,44 @@ onUnmounted(() => {
|
|||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 加载更多样式 */
|
||||||
|
.load-more-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-spinner {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border: 4rpx solid #f3f3f3;
|
||||||
|
border-top: 4rpx solid #3f51b5;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -7,122 +7,7 @@
|
|||||||
|
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<view class="tabbar-content">
|
<view class="tabbar-content">
|
||||||
<AlarmRecord />
|
<AlarmRecord ref="alarmRecord" />
|
||||||
|
|
||||||
<!-- 报警统计 -->
|
|
||||||
<!-- <view class="alarm-statistics">
|
|
||||||
<view class="stat-item">
|
|
||||||
<text class="stat-label">总报警数</text>
|
|
||||||
<text class="stat-value total">{{ totalAlarms }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="stat-item">
|
|
||||||
<text class="stat-label">未处理</text>
|
|
||||||
<text class="stat-value pending">{{ pendingAlarms }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="stat-item">
|
|
||||||
<text class="stat-label">已处理</text>
|
|
||||||
<text class="stat-value resolved">{{ resolvedAlarms }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="stat-item">
|
|
||||||
<text class="stat-label">紧急报警</text>
|
|
||||||
<text class="stat-value urgent">{{ urgentAlarms }}</text>
|
|
||||||
</view>
|
|
||||||
</view> -->
|
|
||||||
|
|
||||||
<!-- 筛选区域 -->
|
|
||||||
<!-- <view class="filter-section">
|
|
||||||
<view class="filter-row">
|
|
||||||
<view class="filter-item">
|
|
||||||
<text class="filter-label">报警级别</text>
|
|
||||||
<picker :value="levelIndex" :range="levelOptions" @change="onLevelChange">
|
|
||||||
<view class="picker-view">
|
|
||||||
<text>{{ levelOptions[levelIndex] }}</text>
|
|
||||||
<text class="picker-arrow">▼</text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="filter-item">
|
|
||||||
<text class="filter-label">处理状态</text>
|
|
||||||
<picker :value="statusIndex" :range="statusOptions" @change="onStatusChange">
|
|
||||||
<view class="picker-view">
|
|
||||||
<text>{{ statusOptions[statusIndex] }}</text>
|
|
||||||
<text class="picker-arrow">▼</text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="filter-row">
|
|
||||||
<view class="filter-item">
|
|
||||||
<text class="filter-label">时间范围</text>
|
|
||||||
<picker mode="date" :value="alarmDate" @change="onDateChange">
|
|
||||||
<view class="picker-view">
|
|
||||||
<text>{{ alarmDate }}</text>
|
|
||||||
<text class="picker-arrow">▼</text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<button class="search-button" @click="searchAlarms">搜索</button>
|
|
||||||
</view>
|
|
||||||
</view> -->
|
|
||||||
|
|
||||||
<!-- 报警列表 -->
|
|
||||||
<!-- <view class="alarms-container">
|
|
||||||
<view class="alarms-header">
|
|
||||||
<text class="alarms-title">报警详情</text>
|
|
||||||
<view class="header-actions">
|
|
||||||
<button class="action-button" @click="refreshAlarms">刷新</button>
|
|
||||||
<button class="action-button" @click="exportAlarms">导出</button>
|
|
||||||
<button class="action-button clear" @click="clearAlarms">清空</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<scroll-view class="alarms-list" scroll-y="true">
|
|
||||||
<view class="alarm-item" v-for="(alarm, index) in alarms" :key="index" :class="alarm.level">
|
|
||||||
<view class="alarm-header">
|
|
||||||
<view class="alarm-level" :class="alarm.level">
|
|
||||||
<text>{{ alarm.levelText }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="alarm-status" :class="alarm.status">
|
|
||||||
<text>{{ alarm.statusText }}</text>
|
|
||||||
</view>
|
|
||||||
<text class="alarm-time">{{ alarm.time }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="alarm-content">
|
|
||||||
<text class="alarm-title">{{ alarm.title }}</text>
|
|
||||||
<text class="alarm-description">{{ alarm.description }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="alarm-details">
|
|
||||||
<view class="detail-item">
|
|
||||||
<text class="detail-label">报警源:</text>
|
|
||||||
<text class="detail-value">{{ alarm.source }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="detail-item">
|
|
||||||
<text class="detail-label">当前值:</text>
|
|
||||||
<text class="detail-value">{{ alarm.currentValue }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="detail-item">
|
|
||||||
<text class="detail-label">阈值:</text>
|
|
||||||
<text class="detail-value">{{ alarm.threshold }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="alarm-actions" v-if="alarm.status === 'pending'">
|
|
||||||
<button class="action-button resolve" @click="resolveAlarm(alarm)">处理</button>
|
|
||||||
<button class="action-button ignore" @click="ignoreAlarm(alarm)">忽略</button>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="alarm-resolution" v-if="alarm.status === 'resolved'">
|
|
||||||
<text class="resolution-text">处理人: {{ alarm.resolver }}</text>
|
|
||||||
<text class="resolution-time">处理时间: {{ alarm.resolveTime }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view> -->
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -133,6 +18,11 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
AlarmRecord
|
AlarmRecord
|
||||||
},
|
},
|
||||||
|
onShow() {
|
||||||
|
console.log('📱 报警记录页面显示,触发页面更新')
|
||||||
|
// 触发子组件重新初始化
|
||||||
|
this.$refs.alarmRecord?.refreshData?.()
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
totalAlarms: 28,
|
totalAlarms: 28,
|
||||||
|
|||||||
@ -52,7 +52,8 @@
|
|||||||
<view class="detail-progress-fill temperature-progress" :style="{ width: temperatureProgress + '%' }"></view>
|
<view class="detail-progress-fill temperature-progress" :style="{ width: temperatureProgress + '%' }"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="detail-range">
|
<view class="detail-range">
|
||||||
<text class="detail-range-text">{{ temperatureRange.min }}°C - {{ temperatureRange.max }}°C</text>
|
<!-- <text class="detail-range-text">{{ temperatureRange.min }}°C - {{ temperatureRange.max }}°C</text> -->
|
||||||
|
<text class="detail-range-text">{{ 0 }}°C - {{ 100 }}°C</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@ -66,7 +67,8 @@
|
|||||||
<view class="detail-progress-fill humidity-progress" :style="{ width: humidityProgress + '%' }"></view>
|
<view class="detail-progress-fill humidity-progress" :style="{ width: humidityProgress + '%' }"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="detail-range">
|
<view class="detail-range">
|
||||||
<text class="detail-range-text">{{ humidityRange.min }}% - {{ humidityRange.max }}%</text>
|
<!-- <text class="detail-range-text">{{ humidityRange.min }}% - {{ humidityRange.max }}%</text> -->
|
||||||
|
<text class="detail-range-text">{{ 0 }}% - {{ 100 }}%</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@ -80,7 +82,8 @@
|
|||||||
<view class="detail-progress-fill cleanliness-progress" :style="{ width: cleanlinessProgress + '%' }"></view>
|
<view class="detail-progress-fill cleanliness-progress" :style="{ width: cleanlinessProgress + '%' }"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="detail-range">
|
<view class="detail-range">
|
||||||
<text class="detail-range-text">暂无数据</text>
|
<!-- <text class="detail-range-text">暂无数据</text> -->
|
||||||
|
<text class="detail-range-text">{{ 0 }}% - {{ 100 }}%</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -121,12 +124,28 @@
|
|||||||
|
|
||||||
<view class="control-ranges">
|
<view class="control-ranges">
|
||||||
<view class="range-item">
|
<view class="range-item">
|
||||||
|
<view class="range-header">
|
||||||
<text class="range-label">温度控制</text>
|
<text class="range-label">温度控制</text>
|
||||||
<text class="range-value">{{ temperatureRange.min }}°C - {{ temperatureRange.max }}°C</text>
|
<view class="range-status" :class="getTemperatureStatus().class">
|
||||||
|
<text class="status-text">{{ getTemperatureStatus().text }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="range-content">
|
||||||
|
<text class="current-value">当前: {{ temperature }}°C</text>
|
||||||
|
<text class="range-value">范围: {{ temperatureRange.min }}°C - {{ temperatureRange.max }}°C</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="range-item">
|
<view class="range-item">
|
||||||
|
<view class="range-header">
|
||||||
<text class="range-label">湿度控制</text>
|
<text class="range-label">湿度控制</text>
|
||||||
<text class="range-value">{{ humidityRange.min }}% - {{ humidityRange.max }}%</text>
|
<view class="range-status" :class="getHumidityStatus().class">
|
||||||
|
<text class="status-text">{{ getHumidityStatus().text }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="range-content">
|
||||||
|
<text class="current-value">当前: {{ humidity }}%</text>
|
||||||
|
<text class="range-value">范围: {{ humidityRange.min }}% - {{ humidityRange.max }}%</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -202,7 +221,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import mqttDataManager from '@/utils/mqttDataManager.js'
|
import mqttDataManager from '@/utils/mqttDataManager.js'
|
||||||
import { manualReconnect, sendMqttData } from '@/utils/sendMqtt.js'
|
import { manualReconnect, sendMqttData } from '@/utils/sendMqtt.js'
|
||||||
import { thDataApi } from '@/utils/api.js'
|
import { thDataApi, alertApi, eventApi } from '@/utils/api.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -215,33 +234,56 @@ export default {
|
|||||||
cleanlinessProgress: 0,
|
cleanlinessProgress: 0,
|
||||||
lastUpdate: '暂无数据',
|
lastUpdate: '暂无数据',
|
||||||
temperatureRange: {
|
temperatureRange: {
|
||||||
min: 0,
|
min: 25,
|
||||||
max: 100
|
max: 35
|
||||||
},
|
},
|
||||||
humidityRange: {
|
humidityRange: {
|
||||||
min: 0,
|
min: 40,
|
||||||
max: 100
|
max: 70
|
||||||
},
|
},
|
||||||
tempSettings: {
|
tempSettings: {
|
||||||
min: 25,
|
min: 25,
|
||||||
max: 35
|
max: 35
|
||||||
},
|
},
|
||||||
humiditySettings: {
|
humiditySettings: {
|
||||||
min: 0,
|
min: 40,
|
||||||
max: 100
|
max: 70
|
||||||
},
|
},
|
||||||
connectionStatus: {
|
connectionStatus: {
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
lastUpdate: null
|
lastUpdate: null
|
||||||
},
|
},
|
||||||
targetTemperature: 30,
|
targetTemperature: 30,
|
||||||
|
targetHumidity: 50, // 空调设定湿度
|
||||||
|
// 报警相关数据
|
||||||
|
acFaultStatus: 0, // 空调故障状态,1表示故障
|
||||||
|
alertHistory: [], // 报警历史记录
|
||||||
|
// 系统启动事件相关
|
||||||
|
hasCreatedStartupEvent: false, // 是否已创建启动事件
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
console.log('环境参数页面加载')
|
console.log('环境参数页面加载')
|
||||||
|
|
||||||
|
// 从本地存储读取是否已创建启动事件的状态
|
||||||
|
const hasCreatedStartupEvent = uni.getStorageSync('hasCreatedStartupEvent')
|
||||||
|
if (hasCreatedStartupEvent) {
|
||||||
|
this.hasCreatedStartupEvent = true
|
||||||
|
console.log('📱 从本地存储读取到启动事件状态: 已创建')
|
||||||
|
}
|
||||||
|
|
||||||
this.initMqttListener()
|
this.initMqttListener()
|
||||||
// 获取最新空调温度
|
// 获取最新空调温度
|
||||||
this.getLatestAirConditionerTemperature()
|
this.getLatestAirConditionerTemperature()
|
||||||
|
// 首次进入系统时创建启动事件
|
||||||
|
this.createStartupEventIfNeeded()
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
console.log('📱 环境参数页面显示,触发页面更新')
|
||||||
|
// 只有在非首次显示时才重新获取最新空调温度
|
||||||
|
if (this.hasCreatedStartupEvent) {
|
||||||
|
this.getLatestAirConditionerTemperature()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onUnload() {
|
onUnload() {
|
||||||
// console.log('🔌 环境参数页面卸载,清理资源...')
|
// console.log('🔌 环境参数页面卸载,清理资源...')
|
||||||
@ -265,6 +307,67 @@ export default {
|
|||||||
// console.log('✅ 环境参数页面资源清理完成')
|
// console.log('✅ 环境参数页面资源清理完成')
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 首次进入系统时创建启动事件
|
||||||
|
async createStartupEventIfNeeded() {
|
||||||
|
// 检查是否已经创建过启动事件
|
||||||
|
if (this.hasCreatedStartupEvent) {
|
||||||
|
console.log('✅ 启动事件已存在,跳过创建')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('🚀 首次进入系统,创建启动事件...')
|
||||||
|
|
||||||
|
const currentTime = this.formatDateTime(new Date())
|
||||||
|
const startupEvent = {
|
||||||
|
eventType: "设备重启",
|
||||||
|
eventTime: currentTime,
|
||||||
|
status: "已完成",
|
||||||
|
description: "设备正常重启维护",
|
||||||
|
deviceId: "AC_001"
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📤 提交启动事件:', startupEvent)
|
||||||
|
const response = await eventApi.create(startupEvent)
|
||||||
|
console.log('✅ 启动事件创建成功:', response)
|
||||||
|
|
||||||
|
// 标记已创建启动事件
|
||||||
|
this.hasCreatedStartupEvent = true
|
||||||
|
|
||||||
|
// 可以保存到本地存储,避免刷新页面后重复创建
|
||||||
|
uni.setStorageSync('hasCreatedStartupEvent', true)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 启动事件创建失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取温度控制状态
|
||||||
|
getTemperatureStatus() {
|
||||||
|
if (this.temperature === 0) {
|
||||||
|
return { text: '无数据', class: 'no-data' }
|
||||||
|
} else if (this.temperature >= this.temperatureRange.min && this.temperature <= this.temperatureRange.max) {
|
||||||
|
return { text: '正常', class: 'normal' }
|
||||||
|
} else if (this.temperature < this.temperatureRange.min) {
|
||||||
|
return { text: '偏低', class: 'low' }
|
||||||
|
} else {
|
||||||
|
return { text: '偏高', class: 'high' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取湿度控制状态
|
||||||
|
getHumidityStatus() {
|
||||||
|
if (this.humidity === 0) {
|
||||||
|
return { text: '无数据', class: 'no-data' }
|
||||||
|
} else if (this.humidity >= this.humidityRange.min && this.humidity <= this.humidityRange.max) {
|
||||||
|
return { text: '正常', class: 'normal' }
|
||||||
|
} else if (this.humidity < this.humidityRange.min) {
|
||||||
|
return { text: '偏低', class: 'low' }
|
||||||
|
} else {
|
||||||
|
return { text: '偏高', class: 'high' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 获取最新空调温度
|
// 获取最新空调温度
|
||||||
async getLatestAirConditionerTemperature() {
|
async getLatestAirConditionerTemperature() {
|
||||||
try {
|
try {
|
||||||
@ -347,6 +450,11 @@ export default {
|
|||||||
updateEnvironmentData(data) {
|
updateEnvironmentData(data) {
|
||||||
// console.log('🌡️ 环境参数页面更新数据:', data)
|
// console.log('🌡️ 环境参数页面更新数据:', data)
|
||||||
|
|
||||||
|
// 处理空调故障状态
|
||||||
|
if (data.deviceType === 'AC' && data.faultStatus !== undefined) {
|
||||||
|
this.acFaultStatus = data.faultStatus
|
||||||
|
}
|
||||||
|
|
||||||
// 只处理WSD设备的数据
|
// 只处理WSD设备的数据
|
||||||
if (data.deviceType === 'WSD') {
|
if (data.deviceType === 'WSD') {
|
||||||
if (data.temperature !== undefined) {
|
if (data.temperature !== undefined) {
|
||||||
@ -363,6 +471,9 @@ export default {
|
|||||||
|
|
||||||
this.lastUpdate = data.time || new Date().toLocaleString('zh-CN')
|
this.lastUpdate = data.time || new Date().toLocaleString('zh-CN')
|
||||||
|
|
||||||
|
// 检查报警条件,传入MQTT原始数据
|
||||||
|
this.checkAlerts(data)
|
||||||
|
|
||||||
// console.log('✅ 环境数据更新完成:', {
|
// console.log('✅ 环境数据更新完成:', {
|
||||||
// temperature: this.temperature,
|
// temperature: this.temperature,
|
||||||
// humidity: this.humidity,
|
// humidity: this.humidity,
|
||||||
@ -373,6 +484,137 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 格式化日期时间为指定格式
|
||||||
|
formatDateTime(date) {
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||||
|
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查报警条件
|
||||||
|
checkAlerts(mqttData) {
|
||||||
|
const currentTime = this.formatDateTime(new Date())
|
||||||
|
|
||||||
|
// 只处理WSD设备的数据
|
||||||
|
if (mqttData.deviceType !== 'WSD') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取MQTT原始数据
|
||||||
|
const mqttTemperature = mqttData.temperature
|
||||||
|
const mqttHumidity = mqttData.humidity
|
||||||
|
|
||||||
|
// 1. 温度报警:使用环境控制设置的区间(使用MQTT原始数据)
|
||||||
|
if (mqttTemperature !== undefined && (mqttTemperature < this.temperatureRange.min || mqttTemperature > this.temperatureRange.max)) {
|
||||||
|
const alert = {
|
||||||
|
// content: `温度传感器异常,读数${mqttTemperature}°C超出控制范围${this.temperatureRange.min}°C-${this.temperatureRange.max}°C`,
|
||||||
|
content: `温度超出控制范围`,
|
||||||
|
category: "传感器故障",
|
||||||
|
alertTime: currentTime,
|
||||||
|
level: "高危",
|
||||||
|
action: "检查温度传感器连接",
|
||||||
|
actionTime: currentTime,
|
||||||
|
deviceId: "WSD_001"
|
||||||
|
}
|
||||||
|
this.logAlert(alert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 湿度报警:使用环境控制设置的区间(使用MQTT原始数据)
|
||||||
|
if (mqttHumidity !== undefined && (mqttHumidity < this.humidityRange.min || mqttHumidity > this.humidityRange.max)) {
|
||||||
|
const alert = {
|
||||||
|
// content: `湿度传感器异常,读数${mqttHumidity}%超出控制范围${this.humidityRange.min}%-${this.humidityRange.max}%`,
|
||||||
|
content: `温度超出控制范围`,
|
||||||
|
category: "传感器故障",
|
||||||
|
alertTime: currentTime,
|
||||||
|
level: "高危",
|
||||||
|
action: "检查湿度传感器连接",
|
||||||
|
actionTime: currentTime,
|
||||||
|
deviceId: "WSD_001"
|
||||||
|
}
|
||||||
|
this.logAlert(alert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 空调故障报警:acFaultStatus为1
|
||||||
|
if (this.acFaultStatus === 1) {
|
||||||
|
const alert = {
|
||||||
|
// content: "空调故障,需要手动设置温度",
|
||||||
|
content: "空调报警",
|
||||||
|
category: "设备故障",
|
||||||
|
alertTime: currentTime,
|
||||||
|
level: "高危",
|
||||||
|
action: "手动调节空调温度",
|
||||||
|
actionTime: currentTime,
|
||||||
|
deviceId: "AC_001"
|
||||||
|
}
|
||||||
|
this.logAlert(alert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 温度偏差报警:|温湿度计温度 - 空调设定温度| / 空调设定温度 > 30%(使用MQTT原始数据)
|
||||||
|
if (mqttTemperature !== undefined && this.targetTemperature > 0) {
|
||||||
|
const temperatureDiff = Math.abs(mqttTemperature - this.targetTemperature)
|
||||||
|
const deviationPercent = (temperatureDiff / this.targetTemperature) * 100
|
||||||
|
|
||||||
|
if (deviationPercent > 30) {
|
||||||
|
const alert = {
|
||||||
|
// content: `温度偏差过大,实际${mqttTemperature}°C与设定${this.targetTemperature}°C偏差${deviationPercent.toFixed(1)}%`,
|
||||||
|
content: `温度偏差过大`,
|
||||||
|
category: "温度控制异常",
|
||||||
|
alertTime: currentTime,
|
||||||
|
level: "中危",
|
||||||
|
action: "调整空调设定温度",
|
||||||
|
actionTime: currentTime,
|
||||||
|
deviceId: "AC_001"
|
||||||
|
}
|
||||||
|
this.logAlert(alert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 湿度偏差报警:|温湿度计湿度 - 空调设定湿度| / 空调设定湿度 > 30%(使用MQTT原始数据)
|
||||||
|
if (mqttHumidity !== undefined && this.targetHumidity > 0) {
|
||||||
|
const humidityDiff = Math.abs(mqttHumidity - this.targetHumidity)
|
||||||
|
const deviationPercent = (humidityDiff / this.targetHumidity) * 100
|
||||||
|
|
||||||
|
if (deviationPercent > 30) {
|
||||||
|
const alert = {
|
||||||
|
// content: `湿度偏差过大,实际${mqttHumidity}%与设定${this.targetHumidity}%偏差${deviationPercent.toFixed(1)}%`,
|
||||||
|
content: `湿度偏差过大`,
|
||||||
|
category: "湿度控制异常",
|
||||||
|
alertTime: currentTime,
|
||||||
|
level: "中危",
|
||||||
|
action: "调整空调设定湿度",
|
||||||
|
actionTime: currentTime,
|
||||||
|
deviceId: "AC_001"
|
||||||
|
}
|
||||||
|
this.logAlert(alert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 记录报警到控制台并调用创建告警接口
|
||||||
|
async logAlert(alert) {
|
||||||
|
console.log('🚨 报警触发:', JSON.stringify(alert, null, 2))
|
||||||
|
this.alertHistory.push(alert)
|
||||||
|
|
||||||
|
// 调用创建告警接口
|
||||||
|
try {
|
||||||
|
console.log('📤 正在创建告警记录...')
|
||||||
|
const response = await alertApi.create(alert)
|
||||||
|
console.log('✅ 告警记录创建成功:', response)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 告警记录创建失败:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制报警历史记录数量,避免内存溢出
|
||||||
|
if (this.alertHistory.length > 100) {
|
||||||
|
this.alertHistory = this.alertHistory.slice(-50)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 降低目标温度
|
// 降低目标温度
|
||||||
decreaseTemperature() {
|
decreaseTemperature() {
|
||||||
if (this.targetTemperature > 16) {
|
if (this.targetTemperature > 16) {
|
||||||
@ -411,6 +653,14 @@ export default {
|
|||||||
|
|
||||||
// 发送空调参数到MQTT
|
// 发送空调参数到MQTT
|
||||||
this.sendAirConditionerParams()
|
this.sendAirConditionerParams()
|
||||||
|
|
||||||
|
// 温度变化后检查报警(使用当前页面数据模拟MQTT数据)
|
||||||
|
const mockMqttData = {
|
||||||
|
deviceType: 'WSD',
|
||||||
|
temperature: this.temperature,
|
||||||
|
humidity: this.humidity
|
||||||
|
}
|
||||||
|
this.checkAlerts(mockMqttData)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 发送空调参数
|
// 发送空调参数
|
||||||
@ -869,25 +1119,68 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.range-item {
|
.range-item {
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #f8fbff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border-left: 4rpx solid #4a90e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 15rpx 20rpx;
|
margin-bottom: 12rpx;
|
||||||
// background: #f8fbff;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
// border-left: 4rpx solid #4a90e2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.range-label {
|
.range-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-status {
|
||||||
|
padding: 6rpx 12rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-status.normal {
|
||||||
|
background-color: #e8f5e8;
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-status.low {
|
||||||
|
background-color: #fff3e0;
|
||||||
|
color: #ff9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-status.high {
|
||||||
|
background-color: #ffebee;
|
||||||
|
color: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-status.no-data {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-value {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #666;
|
color: #4a90e2;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.range-value {
|
.range-value {
|
||||||
font-size: 30rpx;
|
font-size: 24rpx;
|
||||||
color: #4a90e2;
|
color: #666;
|
||||||
font-weight: 500;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-button {
|
.settings-button {
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
</view> -->
|
</view> -->
|
||||||
<SystemLog />
|
<SystemLog ref="systemLog" />
|
||||||
|
|
||||||
<!-- 筛选区域 -->
|
<!-- 筛选区域 -->
|
||||||
<!-- <view class="filter-section">
|
<!-- <view class="filter-section">
|
||||||
@ -167,6 +167,11 @@ export default {
|
|||||||
onLoad() {
|
onLoad() {
|
||||||
console.log('系统日志页面加载')
|
console.log('系统日志页面加载')
|
||||||
},
|
},
|
||||||
|
onShow() {
|
||||||
|
console.log('📱 系统日志页面显示,触发页面更新')
|
||||||
|
// 触发子组件重新初始化
|
||||||
|
this.$refs.systemLog?.refreshData?.()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onLevelChange(e) {
|
onLevelChange(e) {
|
||||||
this.levelIndex = e.detail.value
|
this.levelIndex = e.detail.value
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import mqttDataManager from '@/utils/mqttDataManager.js'
|
import mqttDataManager from '@/utils/mqttDataManager.js'
|
||||||
import { dataHistoryApi } from '@/utils/api.js'
|
import { dataHistoryApi, eventApi } from '@/utils/api.js'
|
||||||
import EChart from '@/uni_modules/e-chart/components/e-chart/e-chart.vue'
|
import EChart from '@/uni_modules/e-chart/components/e-chart/e-chart.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -99,6 +99,8 @@ export default {
|
|||||||
lastUpdateTime: null,
|
lastUpdateTime: null,
|
||||||
dataSource: 'empty' // 'api' | 'empty' | 'error'
|
dataSource: 'empty' // 'api' | 'empty' | 'error'
|
||||||
},
|
},
|
||||||
|
// 页面初始化状态
|
||||||
|
hasInitialized: false,
|
||||||
// ECharts配置选项
|
// ECharts配置选项
|
||||||
temperatureOption: {
|
temperatureOption: {
|
||||||
title: {
|
title: {
|
||||||
@ -312,6 +314,16 @@ export default {
|
|||||||
// 调用历史数据接口
|
// 调用历史数据接口
|
||||||
this.getHistoryData()
|
this.getHistoryData()
|
||||||
},
|
},
|
||||||
|
onShow() {
|
||||||
|
console.log('📱 参数记录页面显示,触发页面更新')
|
||||||
|
// 只有在非首次显示时才重新获取历史数据
|
||||||
|
// 首次显示时已经在onLoad中获取过了
|
||||||
|
if (this.hasInitialized) {
|
||||||
|
this.getHistoryData()
|
||||||
|
} else {
|
||||||
|
this.hasInitialized = true
|
||||||
|
}
|
||||||
|
},
|
||||||
onUnload() {
|
onUnload() {
|
||||||
// 页面卸载时移除监听器
|
// 页面卸载时移除监听器
|
||||||
if (this.dataUpdateHandler) {
|
if (this.dataUpdateHandler) {
|
||||||
@ -467,6 +479,9 @@ export default {
|
|||||||
lastUpdateTime: new Date().toLocaleString(),
|
lastUpdateTime: new Date().toLocaleString(),
|
||||||
dataSource: 'api'
|
dataSource: 'api'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存查询事件
|
||||||
|
await this.createQueryEvent('success', response.length)
|
||||||
} else {
|
} else {
|
||||||
console.log('📊 没有历史数据,显示空状态')
|
console.log('📊 没有历史数据,显示空状态')
|
||||||
// 没有数据时显示空状态
|
// 没有数据时显示空状态
|
||||||
@ -478,6 +493,9 @@ export default {
|
|||||||
lastUpdateTime: new Date().toLocaleString(),
|
lastUpdateTime: new Date().toLocaleString(),
|
||||||
dataSource: 'empty'
|
dataSource: 'empty'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存查询事件(无数据)
|
||||||
|
await this.createQueryEvent('empty', 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
@ -494,6 +512,9 @@ export default {
|
|||||||
dataSource: 'error'
|
dataSource: 'error'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存查询事件(错误)
|
||||||
|
await this.createQueryEvent('error', 0)
|
||||||
|
|
||||||
// 显示错误提示
|
// 显示错误提示
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '数据加载失败',
|
title: '数据加载失败',
|
||||||
@ -680,6 +701,58 @@ export default {
|
|||||||
this.$refs.pm25ChartRef.setOption(this.pm25Option)
|
this.$refs.pm25ChartRef.setOption(this.pm25Option)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建查询事件
|
||||||
|
async createQueryEvent(status, dataCount) {
|
||||||
|
try {
|
||||||
|
const currentTime = this.formatDateTime(new Date())
|
||||||
|
const queryEvent = {
|
||||||
|
eventType: "参数记录查询",
|
||||||
|
eventTime: currentTime,
|
||||||
|
status: this.getEventStatus(status),
|
||||||
|
description: this.getEventDescription(status, dataCount),
|
||||||
|
deviceId: "PARAMETER_QUERY_001"
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📤 提交查询事件:', queryEvent)
|
||||||
|
const response = await eventApi.create(queryEvent)
|
||||||
|
console.log('✅ 查询事件创建成功:', response)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 查询事件创建失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取事件状态
|
||||||
|
getEventStatus(status) {
|
||||||
|
const statusMap = {
|
||||||
|
'success': '已完成',
|
||||||
|
'empty': '已完成',
|
||||||
|
'error': '失败'
|
||||||
|
};
|
||||||
|
return statusMap[status] || '未知';
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取事件描述
|
||||||
|
getEventDescription(status, dataCount) {
|
||||||
|
const descriptions = {
|
||||||
|
'success': `成功查询到${dataCount}条参数记录数据`,
|
||||||
|
'empty': '查询参数记录,但未找到数据',
|
||||||
|
'error': '查询参数记录时发生错误'
|
||||||
|
};
|
||||||
|
return descriptions[status] || '未知查询状态';
|
||||||
|
},
|
||||||
|
|
||||||
|
// 时间格式化函数
|
||||||
|
formatDateTime(date) {
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,10 @@ export default {
|
|||||||
onLoad() {
|
onLoad() {
|
||||||
console.log('视觉监控页面加载')
|
console.log('视觉监控页面加载')
|
||||||
},
|
},
|
||||||
|
onShow() {
|
||||||
|
console.log('📱 视觉监控页面显示,触发页面更新')
|
||||||
|
// 可以在这里添加重新连接摄像头等逻辑
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
connectCamera() {
|
connectCamera() {
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
|
|||||||
Reference in New Issue
Block a user