feat:订阅Recall、日期查询

This commit is contained in:
吉浩茹
2025-10-11 10:03:58 +08:00
parent 4e66592cf8
commit 8b1a84f739
7 changed files with 777 additions and 349 deletions

View File

@ -15,6 +15,21 @@
</view>
</view> -->
<!-- 日期选择区域 -->
<view class="date-picker-container">
<picker
mode="date"
:value="selectedDate"
@change="onDateChange"
class="date-picker"
>
<view class="picker-input">
<text class="picker-text">{{ selectedDate || '请选择日期' }}</text>
<text class="picker-icon"></text>
</view>
</picker>
</view>
<!-- 报警表格 -->
<view class="page-content">
<view class="alarm-content">
@ -114,6 +129,11 @@ const isLoadingMore = ref(false);
const scrollTop = ref(0);
const isScrolling = ref(false);
// 日期选择相关
const selectedDate = ref('');
const startTime = ref('');
const endTime = ref('');
// 移除空行占位逻辑,没有数据时只显示"暂无数据"
// MQTT报警服务接口预留
@ -151,10 +171,36 @@ const mqttAlarmService = {
isLoadingMore.value = true;
}
// 如果没有选择日期,使用默认日期(当天)
let queryStartTime = startTime.value;
let queryEndTime = endTime.value;
if (!queryStartTime || !queryEndTime) {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const todayStr = `${year}-${month}-${day}`;
queryStartTime = `${todayStr} 00:00:00`;
queryEndTime = `${todayStr} 23:59:59`;
// 更新选择的日期
if (!selectedDate.value) {
selectedDate.value = todayStr;
startTime.value = queryStartTime;
endTime.value = queryEndTime;
}
}
console.log('📅 查询时间范围:', queryStartTime, '至', queryEndTime);
// 调用分页获取告警接口
const response = await alertApi.getList({
const response = await alertApi.getListByCreateTime({
page: page,
size: size
size: size,
startTime: queryStartTime,
endTime: queryEndTime
});
// 处理响应数据
@ -408,6 +454,42 @@ const scrollToTop = () => {
scrollTop.value = scrollTop.value === 0 ? 1 : 0;
};
// 日期选择处理,选择后直接查询
const onDateChange = async (e) => {
selectedDate.value = e.detail.value;
console.log('选择的日期:', selectedDate.value);
// 自动构建开始和结束时间
if (selectedDate.value) {
startTime.value = `${selectedDate.value} 00:00:00`;
endTime.value = `${selectedDate.value} 23:59:59`;
console.log('开始时间:', startTime.value);
console.log('结束时间:', endTime.value);
// 选择日期后直接查询
console.log('🔍 开始查询报警记录');
console.log('查询时间范围:', startTime.value, '至', endTime.value);
try {
// 重置分页状态
currentPage.value = 0;
hasMoreData.value = true;
alarmList.value = [];
// 使用选择的日期范围查询
await mqttAlarmService.getHistoryAlarms(0, pageSize.value, false);
uni.showToast({
title: '查询成功',
icon: 'success',
duration: 1500
});
} catch (error) {
console.error('❌ 查询失败:', error);
}
}
};
// 滚动到底部
const scrollToBottom = () => {
// 使用nextTick确保DOM更新完成
@ -543,6 +625,49 @@ onUnmounted(() => {
height: 100%;
}
// 日期选择器容器样式
.date-picker-container {
display: flex;
align-items: center;
// justify-content: center;
padding: 20rpx 30rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-bottom: 2rpx solid #e8eaed;
}
.date-picker {
width: 100%;
// max-width: 500rpx;
}
.picker-input {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 24rpx;
background: #ffffff;
border: 2rpx solid #dadce0;
border-radius: 8rpx;
transition: all 0.3s ease;
}
.picker-input:active {
border-color: #1a73e8;
background: #f1f5ff;
}
.picker-text {
font-size: 26rpx;
color: #3c4043;
flex: 1;
}
.picker-icon {
font-size: 20rpx;
color: #5f6368;
margin-left: 12rpx;
}
.alarm-content {
// 继承通用内容样式
flex: 1;
@ -863,6 +988,18 @@ onUnmounted(() => {
.empty-text {
font-size: 28rpx;
}
.date-picker-container {
padding: 24rpx 32rpx;
}
.date-picker {
max-width: 600rpx;
}
.picker-text {
font-size: 28rpx;
}
}
// 响应式设计 - 手机设备适配
@ -880,6 +1017,18 @@ onUnmounted(() => {
.empty-text {
font-size: 28rpx;
}
.date-picker-container {
padding: 16rpx 20rpx;
}
.date-picker {
max-width: 100%;
}
.picker-text {
font-size: 24rpx;
}
}
// 响应式设计 - 大屏设备适配

View File

@ -15,6 +15,21 @@
</view>
</view> -->
<!-- 日期选择区域 -->
<view class="date-picker-container">
<picker
mode="date"
:value="selectedDate"
@change="onDateChange"
class="date-picker"
>
<view class="picker-input">
<text class="picker-text">{{ selectedDate || '请选择日期' }}</text>
<text class="picker-icon"></text>
</view>
</picker>
</view>
<!-- 日志表格 -->
<view class="page-content">
<view class="log-content">
@ -91,6 +106,14 @@
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
import { eventApi } from '@/utils/api.js';
// 接收父组件传递的查询日期
const props = defineProps({
queryDate: {
type: String,
default: ''
}
});
// 日志数据
const logList = ref([]);
const isLoading = ref(false);
@ -107,6 +130,11 @@ const isLoadingMore = ref(false);
const scrollTop = ref(0);
const isScrolling = ref(false);
// 日期选择相关
const selectedDate = ref('');
const startTime = ref('');
const endTime = ref('');
// 移除空行占位逻辑,没有数据时只显示"暂无数据"
// 状态映射函数
@ -167,8 +195,8 @@ const mqttLogService = {
},
// 获取历史日志(分页)
getHistoryLogs: async (page = 0, size = 20, isLoadMore = false) => {
console.log(`📄 加载第${page}页数据,每页${size}`);
getHistoryLogs: async (page = 0, size = 20, isLoadMore = false, queryDate = '') => {
console.log(`📄 加载第${page}页数据,每页${size},查询日期:${queryDate}`);
try {
if (!isLoadMore) {
isLoading.value = true;
@ -177,8 +205,40 @@ const mqttLogService = {
}
hasInitialized.value = true;
// 如果没有选择日期,使用默认日期(当天)
let queryStartTime = startTime.value;
let queryEndTime = endTime.value;
if (!queryStartTime || !queryEndTime) {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const todayStr = `${year}-${month}-${day}`;
queryStartTime = `${todayStr} 00:00:00`;
queryEndTime = `${todayStr} 23:59:59`;
// 更新选择的日期
if (!selectedDate.value) {
selectedDate.value = todayStr;
startTime.value = queryStartTime;
endTime.value = queryEndTime;
}
}
console.log('📅 查询时间范围:', queryStartTime, '至', queryEndTime);
// 构建查询参数
const params = {
page: page,
size: size,
startTime: queryStartTime,
endTime: queryEndTime
};
// 调用事件API获取分页数据
const response = await eventApi.getList({ page: page, size: size });
const response = await eventApi.getListByCreateTime(params);
// 将API数据转换为日志格式
const newLogs = response.data.map(item => ({
@ -264,7 +324,7 @@ const onScrollToLower = () => {
const nextPage = currentPage.value + 1;
console.log(`📄 开始加载第${nextPage}页数据`);
mqttLogService.getHistoryLogs(nextPage, pageSize.value, true);
mqttLogService.getHistoryLogs(nextPage, pageSize.value, true, props.queryDate);
};
// 滚动到顶部
@ -272,6 +332,42 @@ const scrollToTop = () => {
scrollTop.value = scrollTop.value === 0 ? 1 : 0;
};
// 日期选择处理,选择后直接查询
const onDateChange = async (e) => {
selectedDate.value = e.detail.value;
console.log('选择的日期:', selectedDate.value);
// 自动构建开始和结束时间
if (selectedDate.value) {
startTime.value = `${selectedDate.value} 00:00:00`;
endTime.value = `${selectedDate.value} 23:59:59`;
console.log('开始时间:', startTime.value);
console.log('结束时间:', endTime.value);
// 选择日期后直接查询
console.log('🔍 开始查询系统日志');
console.log('查询时间范围:', startTime.value, '至', endTime.value);
try {
// 重置分页状态
currentPage.value = 0;
hasMoreData.value = true;
logList.value = [];
// 使用选择的日期范围查询
await mqttLogService.getHistoryLogs(0, pageSize.value, false, selectedDate.value);
uni.showToast({
title: '查询成功',
icon: 'success',
duration: 1500
});
} catch (error) {
console.error('❌ 查询失败:', error);
}
}
};
// 组件生命周期
@ -280,7 +376,7 @@ onMounted(async () => {
// 连接MQTT并初始化
// await mqttLogService.connect();
// await mqttLogService.subscribeLogData();
await mqttLogService.getHistoryLogs(0, pageSize.value, false);
await mqttLogService.getHistoryLogs(0, pageSize.value, false, props.queryDate);
} catch (error) {
console.error('日志系统初始化失败:', error);
uni.showToast({
@ -300,15 +396,32 @@ const refreshData = async () => {
logList.value = [];
// 重新获取第一页数据
await mqttLogService.getHistoryLogs(0, pageSize.value, false);
await mqttLogService.getHistoryLogs(0, pageSize.value, false, props.queryDate);
} catch (error) {
console.error('❌ 刷新数据失败:', error);
}
};
// 按日期查询方法
const queryByDate = async (queryDate) => {
console.log('📅 按日期查询系统日志:', queryDate)
try {
// 重置分页状态
currentPage.value = 0;
hasMoreData.value = true;
logList.value = [];
// 按日期获取第一页数据
await mqttLogService.getHistoryLogs(0, pageSize.value, false, queryDate);
} catch (error) {
console.error('❌ 按日期查询失败:', error);
}
};
// 暴露方法给父组件
defineExpose({
refreshData
refreshData,
queryByDate
});
onUnmounted(() => {
@ -332,6 +445,49 @@ onUnmounted(() => {
height: 100%;
}
// 日期选择器容器样式
.date-picker-container {
display: flex;
align-items: center;
// justify-content: center;
padding: 20rpx 30rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-bottom: 2rpx solid #e8eaed;
}
.date-picker {
width: 100%;
// max-width: 500rpx;
}
.picker-input {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 24rpx;
background: #ffffff;
border: 2rpx solid #dadce0;
border-radius: 8rpx;
transition: all 0.3s ease;
}
.picker-input:active {
border-color: #1a73e8;
background: #f1f5ff;
}
.picker-text {
font-size: 26rpx;
color: #3c4043;
flex: 1;
}
.picker-icon {
font-size: 20rpx;
color: #5f6368;
margin-left: 12rpx;
}
.log-content {
// 继承通用内容样式
flex: 1;
@ -547,74 +703,6 @@ onUnmounted(() => {
font-weight: 400;
}
.table-loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #e9ecef;
border-top: 4rpx solid #6c757d;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.table-loading-text {
font-size: 28rpx;
color: #6c757d;
font-weight: 500;
}
.table-empty-container {
display: flex;
justify-content: center;
align-items: center;
padding: 100rpx 20rpx;
background-color: #ffffff;
}
.table-empty-text {
font-size: 32rpx;
color: #999;
font-weight: 500;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx;
gap: 20rpx;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid rgba(255, 152, 0, 0.3);
border-top: 4rpx solid #ff9800;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 28rpx;
color: #666;
}
.empty-container {
display: flex;
justify-content: center;
align-items: center;
padding: 100rpx;
}
.empty-text {
font-size: 32rpx;
color: #adb5bd;
}
// 响应式设计 - 平板设备适配
@media (min-width: 768px) and (max-width: 1024px) {
@ -631,6 +719,18 @@ onUnmounted(() => {
.empty-text {
font-size: 28rpx;
}
.date-picker-container {
padding: 24rpx 32rpx;
}
.date-picker {
max-width: 600rpx;
}
.picker-text {
font-size: 28rpx;
}
}
// 响应式设计 - 手机设备适配
@ -648,6 +748,18 @@ onUnmounted(() => {
.empty-text {
font-size: 24rpx;
}
.date-picker-container {
padding: 16rpx 20rpx;
}
.date-picker {
max-width: 100%;
}
.picker-text {
font-size: 24rpx;
}
}
// 响应式设计 - 大屏设备适配
@ -667,43 +779,43 @@ onUnmounted(() => {
}
}
/* 加载更多样式 */
// 加载更多样式
.load-more-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30rpx;
gap: 20rpx;
padding: 30rpx 20rpx;
gap: 15rpx;
background-color: #ffffff;
}
.load-more-spinner {
width: 40rpx;
height: 40rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #3f51b5;
border: 3rpx solid #e8eaed;
border-top: 3rpx solid #5f6368;
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;
font-size: 24rpx;
color: #5f6368;
font-weight: 400;
}
.no-more-container {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
align-items: center;
padding: 30rpx 20rpx;
background-color: #ffffff;
}
.no-more-text {
font-size: 28rpx;
color: #999;
font-size: 24rpx;
color: #9aa0a6;
font-weight: 400;
}
</style>

View File

@ -630,11 +630,8 @@ export default {
const currentStatus = mqttDataManager.getConnectionStatus()
}, 10000) // 每10秒检查一次
},
// 更新环境数据仅处理MQTT的空调数据
updateEnvironmentData(data) {
console.log('============MQTT数据', data)
// 只处理空调AC数据温湿度和PM25数据改为从接口获取
if (data.deviceType === 'AC') {
// 处理空调AC数据

View File

@ -7,16 +7,23 @@
<!-- 内容区域 -->
<view class="tabbar-content">
<!-- <view class="filter-item">
<text class="filter-label">时间范围</text>
<picker mode="date" :value="logDate" @change="onDateChange">
<!-- 日期查询区域 -->
<!-- <view class="query-section">
<view class="query-row">
<view class="query-item">
<text class="query-label">查询日期</text>
<picker mode="date" :value="selectedDate" @change="onDateChange">
<view class="picker-view">
<text>{{ logDate }}</text>
<text class="picker-arrow"></text>
<text>{{ selectedDate }}</text>
<text class="picker-arrow">📅</text>
</view>
</picker>
</view>
<button class="query-button" @click="queryLogs">查询</button>
</view>
</view> -->
<SystemLog ref="systemLog" />
<SystemLog ref="systemLog" :query-date="selectedDate" />
<!-- 筛选区域 -->
<!-- <view class="filter-section">
@ -95,73 +102,17 @@ export default {
},
data() {
return {
selectedDate: this.getTodayDate(), // 默认选择今天
levelIndex: 0,
levelOptions: ['全部级别', '错误', '警告', '信息', '调试'],
logDate: '2025-09-29',
searchKeyword: '',
totalLogs: 1248,
errorLogs: 23,
warningLogs: 156,
infoLogs: 1069,
totalLogs: 0,
errorLogs: 0,
warningLogs: 0,
infoLogs: 0,
hasMore: true,
logs: [
{
level: 'error',
levelText: 'ERROR',
time: '2025-09-29 15:45:33',
message: 'MQTT连接失败',
details: 'Connection timeout after 5000ms'
},
{
level: 'warning',
levelText: 'WARN',
time: '2025-09-29 15:44:15',
message: '温度传感器读数异常',
details: 'Temperature reading: 999.9°C (超出正常范围)'
},
{
level: 'info',
levelText: 'INFO',
time: '2025-09-29 15:43:22',
message: '系统启动完成',
details: '所有服务已启动,系统运行正常'
},
{
level: 'error',
levelText: 'ERROR',
time: '2025-09-29 15:42:10',
message: '数据库连接失败',
details: 'Failed to connect to database: Connection refused'
},
{
level: 'info',
levelText: 'INFO',
time: '2025-09-29 15:41:55',
message: '用户登录成功',
details: '用户 admin 登录系统'
},
{
level: 'warning',
levelText: 'WARN',
time: '2025-09-29 15:40:33',
message: '内存使用率过高',
details: 'Memory usage: 85% (建议清理缓存)'
},
{
level: 'info',
levelText: 'INFO',
time: '2025-09-29 15:39:18',
message: '数据同步完成',
details: '同步了 156 条记录到云端'
},
{
level: 'error',
levelText: 'ERROR',
time: '2025-09-29 15:38:45',
message: '文件上传失败',
details: 'File size exceeds limit: 50MB > 10MB'
}
]
logs: [],
}
},
onLoad() {
@ -173,12 +124,50 @@ export default {
this.$refs.systemLog?.refreshData?.()
},
methods: {
// 获取今天的日期
getTodayDate() {
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
// 日期选择变化
onDateChange(e) {
this.selectedDate = e.detail.value
console.log('选择日期:', this.selectedDate)
},
// 查询日志
queryLogs() {
console.log('查询日期:', this.selectedDate)
// 显示加载提示
uni.showLoading({
title: '查询中...'
})
// 调用子组件的查询方法
if (this.$refs.systemLog && this.$refs.systemLog.queryByDate) {
this.$refs.systemLog.queryByDate(this.selectedDate).finally(() => {
uni.hideLoading()
})
} else {
// 如果子组件方法不存在,则刷新数据
if (this.$refs.systemLog && this.$refs.systemLog.refreshData) {
this.$refs.systemLog.refreshData().finally(() => {
uni.hideLoading()
})
} else {
uni.hideLoading()
}
}
},
onLevelChange(e) {
this.levelIndex = e.detail.value
},
onDateChange(e) {
this.logDate = e.detail.value
},
searchLogs() {
console.log('搜索日志', {
level: this.levelOptions[this.levelIndex],
@ -246,6 +235,83 @@ export default {
overflow: hidden;
}
.header-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
text-align: center;
}
/* 查询区域样式 */
.query-section {
background: white;
border-radius: 12rpx;
padding: 25rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
}
.query-row {
display: flex;
align-items: flex-end;
gap: 20rpx;
}
.query-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.query-label {
font-size: 24rpx;
color: #666;
font-weight: 500;
}
.picker-view {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
background-color: #f8f8f8;
border-radius: 10rpx;
border: 2rpx solid #e0e0e0;
transition: all 0.2s ease;
}
.picker-view:active {
background-color: #f0f0f0;
border-color: #3f51b5;
}
.picker-arrow {
color: #3f51b5;
font-size: 24rpx;
}
.query-button {
background: linear-gradient(135deg, #3f51b5 0%, #5c6bc0 100%);
color: white;
padding: 20rpx 30rpx;
border-radius: 10rpx;
font-size: 28rpx;
font-weight: 500;
border: none;
box-shadow: 0 4rpx 12rpx rgba(63, 81, 181, 0.3);
transition: all 0.2s ease;
}
.query-button:active {
transform: translateY(1rpx);
box-shadow: 0 2rpx 8rpx rgba(63, 81, 181, 0.4);
}
.query-button::after {
border: none;
}
.filter-section {
background: white;
border-radius: 12rpx;

View File

@ -7,22 +7,19 @@
<!-- 内容区域 -->
<view class="tabbar-content">
<!-- 日期导航器 -->
<view class="date-selector">
<view class="date-navigation">
<button class="nav-button prev-button" @click="goToPreviousDay">
<!-- <text class="nav-icon"></text> -->
<text class="nav-text">上一天</text>
</button>
<view class="current-date">
<text class="date-text">{{ selectedDate }}</text>
<!-- <text class="date-weekday">{{ getWeekday(selectedDate) }}</text> -->
</view>
<button class="nav-button next-button" @click="goToNextDay">
<text class="nav-text">下一天</text>
<!-- <text class="nav-icon"></text> -->
</button>
<!-- 日期选择区域 -->
<view class="date-picker-container">
<picker
mode="date"
:value="selectedDate"
@change="onDateChange"
class="date-picker"
>
<view class="picker-input">
<text class="picker-text">{{ selectedDate || '请选择日期' }}</text>
<text class="picker-icon"></text>
</view>
</picker>
</view>
<!-- 温度趋势图表 -->
@ -82,10 +79,10 @@ export default {
data() {
return {
selectedDate: this.getTodayDate(), // 默认选择今天
// 24小时数据 (0-23点)
temperatureData: [22, 25, 28, 32, 35, 38, 40, 38, 35, 32, 28, 25, 22, 20, 18, 20, 22, 25, 28, 30, 32, 30, 28, 25],
humidityData: [45, 50, 55, 60, 65, 70, 75, 70, 65, 60, 55, 50, 45, 40, 35, 40, 45, 50, 55, 60, 65, 60, 55, 50],
pm25Data: [15, 18, 22, 25, 28, 32, 35, 32, 28, 25, 22, 18, 15, 12, 10, 12, 15, 18, 22, 25, 28, 25, 22, 18],
// 24小时数据 (0-23点) - 默认显示0
temperatureData: new Array(24).fill(0),
humidityData: new Array(24).fill(0),
pm25Data: new Array(24).fill(0),
connectionStatus: {
isConnected: false,
lastUpdate: null
@ -143,7 +140,7 @@ export default {
type: 'value',
name: '温度(°C)',
min: 0,
max: 100,
max: 80,
axisLabel: {
formatter: '{value}°C',
fontSize: 10
@ -211,7 +208,7 @@ export default {
type: 'value',
name: '湿度(%)',
min: 0,
max: 100,
max: 80,
axisLabel: {
formatter: '{value}%',
fontSize: 10
@ -279,7 +276,7 @@ export default {
type: 'value',
name: 'PM2.5(μg/m³)',
min: 0,
max: 100,
max: 50,
axisLabel: {
formatter: '{value}μg/m³',
fontSize: 10
@ -323,6 +320,10 @@ export default {
},
onShow() {
console.log('📱 参数记录页面显示,触发页面更新')
// 重置时间相关状态
this.resetTimeState()
// 只有在非首次显示时才重新获取历史数据
// 首次显示时已经在onLoad中获取过了
if (this.hasInitialized) {
@ -331,6 +332,11 @@ export default {
this.hasInitialized = true
}
},
onHide() {
console.log('📱 参数记录页面隐藏,清除数据缓存')
// 页面隐藏时清除数据,防止缓存
this.clearPageData()
},
onUnload() {
// 页面卸载时移除监听器
if (this.dataUpdateHandler) {
@ -341,6 +347,57 @@ export default {
}
},
methods: {
// 重置时间相关状态
resetTimeState() {
console.log('🔄 重置时间相关状态')
// 页面切换后重置为默认查询模式过去24小时
this.queryMode = 'default'
this.selectedDate = this.getTodayDate()
console.log('📅 重置为默认查询模式,日期:', this.selectedDate)
// 强制更新X轴标签
this.$nextTick(() => {
this.updateCharts()
})
},
// 清除页面数据缓存
clearPageData() {
console.log('🧹 清除页面数据缓存')
// 清除图表数据
this.temperatureData = new Array(24).fill(0)
this.humidityData = new Array(24).fill(0)
this.pm25Data = new Array(24).fill(0)
// 清除历史数据
this.historyData = []
// 清除处理后的图表数据
this.chartData = {
temperature: [],
humidity: [],
pm: []
}
// 重置数据状态
this.dataStatus = {
isRealData: false,
lastUpdateTime: null,
dataSource: 'empty'
}
// 重置连接状态
this.connectionStatus = {
isConnected: false,
lastUpdate: null
}
console.log('✅ 页面数据缓存已清除')
},
// 生成x轴标签
generateXAxisLabels() {
if (this.queryMode === 'date') {
@ -504,11 +561,10 @@ export default {
this.$refs.pm25ChartRef.init(this.pm25Option)
},
// 上一天
goToPreviousDay() {
const currentDate = new Date(this.selectedDate)
currentDate.setDate(currentDate.getDate() - 1)
this.selectedDate = this.formatDate(currentDate)
// 日期选择处理,选择后直接查询
onDateChange(e) {
this.selectedDate = e.detail.value
console.log('选择的日期:', this.selectedDate)
this.queryMode = 'date' // 切换到按日期查询模式
// 显示加载状态
@ -519,36 +575,12 @@ export default {
// 重新获取历史数据
this.getHistoryDataByDate().finally(() => {
uni.hideLoading()
})
},
// 下一天
goToNextDay() {
const currentDate = new Date(this.selectedDate)
const today = new Date()
// 检查是否已经是今天,如果是则不允许继续往后
if (this.selectedDate >= this.formatDate(today)) {
uni.showToast({
title: '不能查看未来日期',
icon: 'none',
duration: 2000
title: '查询成功',
icon: 'success',
duration: 1500
})
return
}
currentDate.setDate(currentDate.getDate() + 1)
this.selectedDate = this.formatDate(currentDate)
this.queryMode = 'date' // 切换到按日期查询模式
// 显示加载状态
uni.showLoading({
title: '加载数据中...'
})
// 重新获取历史数据
this.getHistoryDataByDate().finally(() => {
uni.hideLoading()
})
},
@ -574,7 +606,7 @@ export default {
},
// 根据选择的日期获取历史数据
async getHistoryDataByDate() {
async getHistoryDataByDate(retryCount = 0) {
try {
// 根据选择的日期构建时间范围
const startTime = `${this.selectedDate} 00:00:00`
@ -585,7 +617,10 @@ export default {
endTime: endTime
}
const response = await dataHistoryApi.getHistory(params)
console.log('📊 请求历史数据(按日期):', params, `${retryCount + 1}次尝试`)
// 增加超时时间到20秒
const response = await dataHistoryApi.getHistory(params, { timeout: 20000 })
// 处理历史数据
if (response && Array.isArray(response) && response.length > 0) {
@ -621,6 +656,21 @@ export default {
} catch (error) {
console.error('❌ 历史数据获取失败:', error)
// 如果是超时错误且重试次数少于2次则重试
if (error.message.includes('timeout') && retryCount < 2) {
console.log(`🔄 网络超时,正在重试... (${retryCount + 1}/2)`)
uni.showToast({
title: `网络超时,重试中...`,
icon: 'loading',
duration: 1500
})
// 等待1秒后重试
await new Promise(resolve => setTimeout(resolve, 1000))
return this.getHistoryDataByDate(retryCount + 1)
}
// 出错时显示空状态
this.showEmptyState()
@ -635,18 +685,20 @@ export default {
await this.createQueryEvent('error', 0)
// 显示错误提示
const errorMessage = error.message.includes('timeout') ? '网络连接超时,请检查网络' : '数据加载失败'
uni.showToast({
title: '数据加载失败',
title: errorMessage,
icon: 'none',
duration: 2000
duration: 3000
})
throw error
// 不再抛出错误避免未捕获的Promise错误
return null
}
},
// 获取历史数据默认过去24小时
async getHistoryData() {
async getHistoryData(retryCount = 0) {
try {
// 构建时间范围从当前时间开始过去24小时
const now = new Date()
@ -661,11 +713,10 @@ export default {
endTime: endTime
}
console.log('📊 请求历史数据过去24小时:', params)
console.log('📊 请求历史数据过去24小时:', params, `${retryCount + 1}次尝试`)
const response = await dataHistoryApi.getHistory(params)
console.log('✅ 历史数据获取成功:', response)
// 增加超时时间到20秒
const response = await dataHistoryApi.getHistory(params, { timeout: 20000 })
// 处理历史数据
if (response && Array.isArray(response) && response.length > 0) {
@ -701,6 +752,21 @@ export default {
} catch (error) {
console.error('❌ 历史数据获取失败:', error)
// 如果是超时错误且重试次数少于2次则重试
if (error.message.includes('timeout') && retryCount < 2) {
console.log(`🔄 网络超时,正在重试... (${retryCount + 1}/2)`)
uni.showToast({
title: `网络超时,重试中...`,
icon: 'loading',
duration: 1500
})
// 等待1秒后重试
await new Promise(resolve => setTimeout(resolve, 1000))
return this.getHistoryData(retryCount + 1)
}
// 出错时显示空状态
this.showEmptyState()
@ -715,13 +781,15 @@ export default {
await this.createQueryEvent('error', 0)
// 显示错误提示
const errorMessage = error.message.includes('timeout') ? '网络连接超时,请检查网络' : '数据加载失败'
uni.showToast({
title: '数据加载失败',
title: errorMessage,
icon: 'none',
duration: 2000
duration: 3000
})
throw error
// 不再抛出错误避免未捕获的Promise错误
return null
}
},
@ -1012,122 +1080,61 @@ export default {
overflow: hidden;
}
.date-selector {
background: white;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.header-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
text-align: center;
}
.time-range-info {
.tabbar-content {
flex: 1;
overflow-y: auto;
}
// 日期选择器容器样式
.date-picker-container {
display: flex;
align-items: center;
// justify-content: center;
padding: 20rpx 30rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-bottom: 2rpx solid #e8eaed;
}
.date-picker {
width: 100%;
// max-width: 500rpx;
}
.picker-input {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 24rpx;
background: #ffffff;
border: 2rpx solid #dadce0;
border-radius: 8rpx;
transition: all 0.3s ease;
}
.picker-input:active {
border-color: #1a73e8;
background: #f1f5ff;
}
.picker-text {
font-size: 26rpx;
color: #3c4043;
flex: 1;
}
.time-range-text {
font-size: 26rpx;
color: #333;
font-weight: 500;
.picker-icon {
font-size: 20rpx;
color: #5f6368;
margin-left: 12rpx;
}
.connection-status {
font-size: 24rpx;
font-weight: bold;
padding: 8rpx 16rpx;
border-radius: 20rpx;
}
.connection-status.connected {
background-color: #e8f5e8;
color: #4caf50;
}
.data-status {
font-size: 24rpx;
font-weight: bold;
padding: 8rpx 16rpx;
border-radius: 20rpx;
}
.data-status.api {
background-color: #e8f5e8;
color: #4caf50;
}
.data-status.default {
background-color: #fff3e0;
color: #ff9800;
}
.data-status .status-text {
font-size: 24rpx;
}
.date-navigation {
display: flex;
align-items: center;
gap: 15rpx;
justify-content: space-between;
}
.nav-button {
// padding: 12rpx 20rpx;
// background: #007aff;
// color: white;
margin: 0;
border: none;
border-radius: 8rpx;
font-size: 24rpx;
// min-width: 120rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 6rpx;
transition: all 0.2s ease;
}
.nav-button:active {
// background: #0056b3;
transform: scale(0.95);
}
.nav-button::after {
border: none;
}
.nav-icon {
font-size: 24rpx;
color: white;
}
.nav-text {
// color: white;
font-size: 24rpx;
font-weight: 500;
}
.current-date {
padding: 16rpx 24rpx;
background: #f8f9fa;
border-radius: 8rpx;
min-width: 180rpx;
text-align: center;
border: 1rpx solid #e9ecef;
}
.date-text {
font-size: 28rpx;
color: #333;
font-weight: 600;
display: block;
margin-bottom: 4rpx;
}
.date-weekday {
font-size: 22rpx;
color: #666;
font-weight: 400;
}
.chart-card {
background: white;
@ -1190,4 +1197,34 @@ export default {
width: 100%;
// padding: 0 20rpx;
}
// 响应式设计 - 平板设备适配
@media (min-width: 768px) and (max-width: 1024px) {
.date-picker-container {
padding: 24rpx 32rpx;
}
.date-picker {
max-width: 600rpx;
}
.picker-text {
font-size: 28rpx;
}
}
// 响应式设计 - 手机设备适配
@media (max-width: 750rpx) {
.date-picker-container {
padding: 16rpx 20rpx;
}
.date-picker {
max-width: 100%;
}
.picker-text {
font-size: 24rpx;
}
}
</style>

View File

@ -36,7 +36,14 @@ export const alertApi = {
// 分页获取告警
getList(params = {}) {
return httpService.get('/api/alerts', params)
}
},
// 根据创建时间获取告警
// http://110.40.171.179:7999/api/alerts/byCreateTime
// startTime endTime page size
getListByCreateTime(params = {}) {
return httpService.get('/api/alerts/byCreateTime', params)
},
}
// 事件相关接口
@ -49,6 +56,11 @@ export const eventApi = {
// 分页获取事件
getList(params = {}) {
return httpService.get('/api/events', params)
},
// 根据创建时间获取事件
getListByCreateTime(params = {}) {
return httpService.get('/api/events/byCreateTime', params)
}
}

View File

@ -122,6 +122,9 @@ const initEventHandleMqtt = (topicUrl) => {
duration: 3000
});
} else {
sendMqttDataRecall({
command: 'Upload immediately',
})
// console.log("✅ MQTT订阅主题成功:", topicUrl);
}
});
@ -299,6 +302,57 @@ const sendMqttData = (data) => {
}
};
// 发送MQTT数据单独订阅HDYDCJ_01_RECALL
const sendMqttDataRecall = (data) => {
try {
if (!client || !client.connected) {
console.error('❌ MQTT客户端未连接无法发送数据');
uni.showToast({
title: 'MQTT未连接',
icon: 'error',
duration: 2000
});
return false;
}
const message = JSON.stringify(data);
// 先订阅HDYDCJ_01_RECALL主题如果还没有订阅
client.subscribe("HDYDCJ_01_RECALL", (err) => {
if (err) {
console.error('❌ 订阅HDYDCJ_01_RECALL失败:', err);
} else {
console.log('✅ 订阅HDYDCJ_01_RECALL成功');
}
});
// 发送数据到HDYDCJ_01_RECALL主题
client.publish("HDYDCJ_01_RECALL", message, (err) => {
if (err) {
uni.showToast({
title: '数据发送失败',
icon: 'error',
duration: 2000
});
} else {
uni.showToast({
title: '数据发送成功',
icon: 'success',
duration: 1500
});
}
});
return true;
} catch (error) {
uni.showToast({
title: '发送数据异常',
icon: 'error',
duration: 2000
});
return false;
}
};
export {
createMqtt,
closeMqtt,
@ -306,5 +360,6 @@ export {
getConnectionStatus,
manualReconnect,
sendMqttData,
sendMqttDataRecall,
client,
}