## 主要更新 ### UI组件重构 - 新增DeviceCard组件:现代化设备卡片设计,支持状态显示和操作控制 - 新增DeviceListPanel组件:完整的设备管理面板,包含搜索、过滤和统计功能 - 集成设备管理组件到MainWindow,替换原有简单按钮布局 ### 界面布局优化 - 调整面板比例:左侧面板从250px增加到320px,右侧面板从280px增加到350px - 中心地图区域保持响应式,适度缩小以平衡整体布局 - 实现近全屏显示,提升用户体验 ### 地图定位功能 - 更新地图坐标为用户指定的实验地点 [113.04336, 28.2561619] - 提升缩放级别到18级,提供更高精度的地理视图 - 保持卫星地图模式,适合战场环境监控 ### 数据库连接优化 - 修复UAVDatabase和DogDatabase的连接冲突问题 - 实现唯一连接名称机制,支持多实例并发访问 - 改进错误处理和资源管理 ### 技术改进 - 设备卡片采用紧凑布局:200x140像素,优化间距和字体 - 修复图标路径问题,使用PNG格式替代缺失的SVG文件 - 改进内存管理,防止段错误和资源泄漏 - 统一军用风格设计,深蓝灰色系配色方案 ### 文档更新 - 完成Phase 3技术设计文档和完成报告 - 更新项目结构说明和开发指南 - 记录组件架构和接口设计 ## 测试状态 - ✅ 编译成功,无错误 - ✅ 应用程序正常启动和运行 - ✅ 设备列表显示4个测试设备 - ✅ 地图正确定位到实验坐标 - ✅ 界面布局响应式适配main
parent
89fcac279e
commit
a7d221ba6d
@ -0,0 +1,502 @@
|
||||
/**
|
||||
* @file DeviceCard.cpp
|
||||
* @brief 设备卡片组件实现
|
||||
* @author CasualtySightPlus Team
|
||||
* @date 2024-12-01
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
#include "ui/components/DeviceCard.h"
|
||||
|
||||
// Qt GUI头文件
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QLinearGradient>
|
||||
#include <QBrush>
|
||||
#include <QPen>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
||||
DeviceCard::DeviceCard(const DeviceInfo &device, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_deviceInfo(device)
|
||||
, m_currentStatus(device.status)
|
||||
, m_isSelected(false)
|
||||
, m_isHovered(false)
|
||||
, m_deviceIconLabel(nullptr)
|
||||
, m_deviceNameLabel(nullptr)
|
||||
, m_statusLabel(nullptr)
|
||||
, m_statusIndicator(nullptr)
|
||||
, m_locationLabel(nullptr)
|
||||
, m_networkLabel(nullptr)
|
||||
, m_signalLabel(nullptr)
|
||||
, m_batteryLabel(nullptr)
|
||||
, m_signalProgressBar(nullptr)
|
||||
, m_batteryProgressBar(nullptr)
|
||||
, m_detailsButton(nullptr)
|
||||
, m_controlButton(nullptr)
|
||||
, m_locationButton(nullptr)
|
||||
, m_mainLayout(nullptr)
|
||||
, m_headerLayout(nullptr)
|
||||
, m_infoLayout(nullptr)
|
||||
, m_buttonLayout(nullptr)
|
||||
{
|
||||
setupUI();
|
||||
setupStyle();
|
||||
connectSignals();
|
||||
|
||||
// 设置卡片基本属性
|
||||
setFixedSize(CARD_WIDTH, CARD_HEIGHT);
|
||||
setMouseTracking(true);
|
||||
setAttribute(Qt::WA_Hover, true);
|
||||
|
||||
qDebug() << "DeviceCard created for device:" << device.name;
|
||||
}
|
||||
|
||||
DeviceCard::~DeviceCard()
|
||||
{
|
||||
qDebug() << "DeviceCard destroyed for device:" << m_deviceInfo.name;
|
||||
}
|
||||
|
||||
void DeviceCard::setupUI()
|
||||
{
|
||||
// 创建主布局 - 更紧凑的间距
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setContentsMargins(PADDING, PADDING, PADDING, PADDING);
|
||||
m_mainLayout->setSpacing(6); // 减小间距
|
||||
|
||||
// === 头部区域 ===
|
||||
m_headerLayout = new QHBoxLayout();
|
||||
m_headerLayout->setSpacing(8);
|
||||
|
||||
// 设备图标
|
||||
m_deviceIconLabel = new QLabel();
|
||||
m_deviceIconLabel->setFixedSize(24, 24);
|
||||
m_deviceIconLabel->setScaledContents(true);
|
||||
updateDeviceIcon();
|
||||
|
||||
// 设备名称
|
||||
m_deviceNameLabel = new QLabel(m_deviceInfo.name);
|
||||
m_deviceNameLabel->setFont(QFont("Arial", 12, QFont::Bold));
|
||||
m_deviceNameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
|
||||
// 状态指示器(圆点)
|
||||
m_statusIndicator = new QLabel("●");
|
||||
m_statusIndicator->setFixedSize(16, 16);
|
||||
m_statusIndicator->setAlignment(Qt::AlignCenter);
|
||||
|
||||
// 状态文本
|
||||
m_statusLabel = new QLabel(getStatusText(m_currentStatus));
|
||||
m_statusLabel->setFont(QFont("Arial", 9));
|
||||
|
||||
m_headerLayout->addWidget(m_deviceIconLabel);
|
||||
m_headerLayout->addWidget(m_deviceNameLabel);
|
||||
m_headerLayout->addStretch();
|
||||
m_headerLayout->addWidget(m_statusIndicator);
|
||||
m_headerLayout->addWidget(m_statusLabel);
|
||||
|
||||
// === 信息区域 ===
|
||||
m_infoLayout = new QGridLayout();
|
||||
m_infoLayout->setSpacing(4);
|
||||
|
||||
// 位置信息 - 简化显示
|
||||
QLabel *locationIcon = new QLabel("📍");
|
||||
locationIcon->setFixedSize(14, 14);
|
||||
locationIcon->setAlignment(Qt::AlignCenter);
|
||||
m_locationLabel = new QLabel(QString("%1,%2").arg(m_deviceInfo.longitude, 0, 'f', 2).arg(m_deviceInfo.latitude, 0, 'f', 2));
|
||||
m_locationLabel->setFont(QFont("Arial", 8));
|
||||
|
||||
// 网络信息 - 简化显示
|
||||
QLabel *networkIcon = new QLabel("🌐");
|
||||
networkIcon->setFixedSize(14, 14);
|
||||
networkIcon->setAlignment(Qt::AlignCenter);
|
||||
m_networkLabel = new QLabel(m_deviceInfo.ipAddress);
|
||||
m_networkLabel->setFont(QFont("Arial", 8));
|
||||
|
||||
// 信号强度 - 更紧凑
|
||||
QLabel *signalIcon = new QLabel("📶");
|
||||
signalIcon->setFixedSize(14, 14);
|
||||
signalIcon->setAlignment(Qt::AlignCenter);
|
||||
m_signalLabel = new QLabel(QString("%1%").arg(m_deviceInfo.signalStrength));
|
||||
m_signalLabel->setFont(QFont("Arial", 8));
|
||||
m_signalProgressBar = new QProgressBar();
|
||||
m_signalProgressBar->setRange(0, 100);
|
||||
m_signalProgressBar->setValue(m_deviceInfo.signalStrength);
|
||||
m_signalProgressBar->setFixedHeight(6);
|
||||
m_signalProgressBar->setTextVisible(false);
|
||||
|
||||
// 电量水平 - 更紧凑
|
||||
QLabel *batteryIcon = new QLabel("🔋");
|
||||
batteryIcon->setFixedSize(14, 14);
|
||||
batteryIcon->setAlignment(Qt::AlignCenter);
|
||||
m_batteryLabel = new QLabel(QString("%1%").arg(m_deviceInfo.batteryLevel));
|
||||
m_batteryLabel->setFont(QFont("Arial", 8));
|
||||
m_batteryProgressBar = new QProgressBar();
|
||||
m_batteryProgressBar->setRange(0, 100);
|
||||
m_batteryProgressBar->setValue(m_deviceInfo.batteryLevel);
|
||||
m_batteryProgressBar->setFixedHeight(6);
|
||||
m_batteryProgressBar->setTextVisible(false);
|
||||
|
||||
// 添加到网格布局
|
||||
m_infoLayout->addWidget(locationIcon, 0, 0);
|
||||
m_infoLayout->addWidget(m_locationLabel, 0, 1, 1, 2);
|
||||
m_infoLayout->addWidget(networkIcon, 1, 0);
|
||||
m_infoLayout->addWidget(m_networkLabel, 1, 1, 1, 2);
|
||||
m_infoLayout->addWidget(signalIcon, 2, 0);
|
||||
m_infoLayout->addWidget(m_signalLabel, 2, 1);
|
||||
m_infoLayout->addWidget(m_signalProgressBar, 2, 2);
|
||||
m_infoLayout->addWidget(batteryIcon, 3, 0);
|
||||
m_infoLayout->addWidget(m_batteryLabel, 3, 1);
|
||||
m_infoLayout->addWidget(m_batteryProgressBar, 3, 2);
|
||||
|
||||
// === 操作按钮区域 ===
|
||||
m_buttonLayout = new QHBoxLayout();
|
||||
m_buttonLayout->setSpacing(4);
|
||||
|
||||
m_detailsButton = new QPushButton("详");
|
||||
m_detailsButton->setFixedSize(32, 20);
|
||||
m_detailsButton->setToolTip("设备详情");
|
||||
m_detailsButton->setFont(QFont("Arial", 8));
|
||||
|
||||
m_controlButton = new QPushButton("控");
|
||||
m_controlButton->setFixedSize(32, 20);
|
||||
m_controlButton->setToolTip("设备控制");
|
||||
m_controlButton->setFont(QFont("Arial", 8));
|
||||
|
||||
m_locationButton = new QPushButton("位");
|
||||
m_locationButton->setFixedSize(32, 20);
|
||||
m_locationButton->setToolTip("设备定位");
|
||||
m_locationButton->setFont(QFont("Arial", 8));
|
||||
|
||||
m_buttonLayout->addWidget(m_detailsButton);
|
||||
m_buttonLayout->addWidget(m_controlButton);
|
||||
m_buttonLayout->addWidget(m_locationButton);
|
||||
m_buttonLayout->addStretch();
|
||||
|
||||
// === 组装主布局 ===
|
||||
m_mainLayout->addLayout(m_headerLayout);
|
||||
|
||||
// 添加分隔线
|
||||
QLabel *separatorLine = new QLabel();
|
||||
separatorLine->setFixedHeight(1);
|
||||
separatorLine->setStyleSheet("background-color: rgba(82, 194, 242, 0.3);");
|
||||
m_mainLayout->addWidget(separatorLine);
|
||||
|
||||
m_mainLayout->addLayout(m_infoLayout);
|
||||
m_mainLayout->addLayout(m_buttonLayout);
|
||||
m_mainLayout->addStretch();
|
||||
}
|
||||
|
||||
void DeviceCard::setupStyle()
|
||||
{
|
||||
// 基础卡片样式
|
||||
QString cardStyle = QString(
|
||||
"DeviceCard {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
|
||||
" stop:0 rgba(45, 65, 95, 0.9),"
|
||||
" stop:1 rgba(25, 40, 65, 0.9));"
|
||||
" border: 2px solid rgba(82, 194, 242, 0.4);"
|
||||
" border-radius: %1px;"
|
||||
"}"
|
||||
"DeviceCard:hover {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
|
||||
" stop:0 rgba(82, 194, 242, 0.3),"
|
||||
" stop:1 rgba(45, 120, 180, 0.3));"
|
||||
" border-color: rgba(82, 194, 242, 0.8);"
|
||||
"}"
|
||||
).arg(BORDER_RADIUS);
|
||||
|
||||
setStyleSheet(cardStyle);
|
||||
|
||||
// 设备名称样式
|
||||
m_deviceNameLabel->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" font-weight: bold;"
|
||||
" background: transparent;"
|
||||
" border: none;"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 状态标签样式
|
||||
m_statusLabel->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: rgb(180, 190, 200);"
|
||||
" background: transparent;"
|
||||
" border: none;"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 信息标签样式
|
||||
QString infoLabelStyle =
|
||||
"QLabel {"
|
||||
" color: rgb(160, 170, 180);"
|
||||
" background: transparent;"
|
||||
" border: none;"
|
||||
"}";
|
||||
|
||||
m_locationLabel->setStyleSheet(infoLabelStyle);
|
||||
m_networkLabel->setStyleSheet(infoLabelStyle);
|
||||
m_signalLabel->setStyleSheet(infoLabelStyle);
|
||||
m_batteryLabel->setStyleSheet(infoLabelStyle);
|
||||
|
||||
// 进度条样式
|
||||
QString progressBarStyle =
|
||||
"QProgressBar {"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
" border-radius: 3px;"
|
||||
" background-color: rgba(25, 35, 45, 0.8);"
|
||||
" text-align: center;"
|
||||
"}"
|
||||
"QProgressBar::chunk {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:1, y2:0,"
|
||||
" stop:0 #00FF7F, stop:0.7 #FFD700, stop:1 #FF4444);"
|
||||
" border-radius: 3px;"
|
||||
"}";
|
||||
|
||||
m_signalProgressBar->setStyleSheet(progressBarStyle);
|
||||
m_batteryProgressBar->setStyleSheet(progressBarStyle);
|
||||
|
||||
// 按钮样式
|
||||
QString buttonStyle =
|
||||
"QPushButton {"
|
||||
" background: rgba(82, 194, 242, 0.2);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.4);"
|
||||
" border-radius: 4px;"
|
||||
" font-size: 12px;"
|
||||
"}"
|
||||
"QPushButton:hover {"
|
||||
" background: rgba(82, 194, 242, 0.4);"
|
||||
" border-color: rgba(82, 194, 242, 0.8);"
|
||||
"}"
|
||||
"QPushButton:pressed {"
|
||||
" background: rgba(82, 194, 242, 0.6);"
|
||||
"}";
|
||||
|
||||
m_detailsButton->setStyleSheet(buttonStyle);
|
||||
m_controlButton->setStyleSheet(buttonStyle);
|
||||
m_locationButton->setStyleSheet(buttonStyle);
|
||||
|
||||
// 更新状态颜色
|
||||
updateStatusColor();
|
||||
}
|
||||
|
||||
void DeviceCard::connectSignals()
|
||||
{
|
||||
connect(m_detailsButton, &QPushButton::clicked, this, &DeviceCard::onDetailsClicked);
|
||||
connect(m_controlButton, &QPushButton::clicked, this, &DeviceCard::onControlClicked);
|
||||
connect(m_locationButton, &QPushButton::clicked, this, &DeviceCard::onLocationClicked);
|
||||
}
|
||||
|
||||
void DeviceCard::updateDeviceInfo(const DeviceInfo &device)
|
||||
{
|
||||
m_deviceInfo = device;
|
||||
|
||||
// 更新界面显示
|
||||
m_deviceNameLabel->setText(device.name);
|
||||
m_locationLabel->setText(formatCoordinates(device.longitude, device.latitude));
|
||||
m_networkLabel->setText(QString("%1:%2").arg(device.ipAddress).arg(device.port));
|
||||
m_signalProgressBar->setValue(device.signalStrength);
|
||||
m_batteryProgressBar->setValue(device.batteryLevel);
|
||||
|
||||
updateDeviceIcon();
|
||||
updateDeviceStatus(device.status);
|
||||
|
||||
qDebug() << "DeviceCard updated for device:" << device.name;
|
||||
}
|
||||
|
||||
void DeviceCard::updateDeviceStatus(DeviceStatus status)
|
||||
{
|
||||
m_currentStatus = status;
|
||||
m_statusLabel->setText(getStatusText(status));
|
||||
updateStatusColor();
|
||||
|
||||
qDebug() << "Device status updated:" << m_deviceInfo.name << "status:" << static_cast<int>(status);
|
||||
}
|
||||
|
||||
void DeviceCard::updateSignalStrength(int strength)
|
||||
{
|
||||
m_deviceInfo.signalStrength = qBound(0, strength, 100);
|
||||
m_signalProgressBar->setValue(m_deviceInfo.signalStrength);
|
||||
|
||||
// 根据信号强度更新颜色
|
||||
QString color;
|
||||
if (strength >= 70) color = "#00FF7F"; // 绿色
|
||||
else if (strength >= 30) color = "#FFD700"; // 黄色
|
||||
else color = "#FF4444"; // 红色
|
||||
|
||||
m_signalProgressBar->setStyleSheet(
|
||||
m_signalProgressBar->styleSheet() +
|
||||
QString("QProgressBar::chunk { background-color: %1; }").arg(color)
|
||||
);
|
||||
}
|
||||
|
||||
void DeviceCard::updateBatteryLevel(int level)
|
||||
{
|
||||
m_deviceInfo.batteryLevel = qBound(0, level, 100);
|
||||
m_batteryProgressBar->setValue(m_deviceInfo.batteryLevel);
|
||||
|
||||
// 根据电量水平更新颜色
|
||||
QString color;
|
||||
if (level >= 50) color = "#00FF7F"; // 绿色
|
||||
else if (level >= 20) color = "#FFD700"; // 黄色
|
||||
else color = "#FF4444"; // 红色
|
||||
|
||||
m_batteryProgressBar->setStyleSheet(
|
||||
m_batteryProgressBar->styleSheet() +
|
||||
QString("QProgressBar::chunk { background-color: %1; }").arg(color)
|
||||
);
|
||||
}
|
||||
|
||||
void DeviceCard::updateLocation(double longitude, double latitude)
|
||||
{
|
||||
m_deviceInfo.longitude = longitude;
|
||||
m_deviceInfo.latitude = latitude;
|
||||
m_locationLabel->setText(formatCoordinates(longitude, latitude));
|
||||
}
|
||||
|
||||
void DeviceCard::setSelected(bool selected)
|
||||
{
|
||||
if (m_isSelected != selected) {
|
||||
m_isSelected = selected;
|
||||
update(); // 触发重绘
|
||||
|
||||
if (selected) {
|
||||
emit deviceSelected(m_deviceInfo.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceCard::refreshStatus()
|
||||
{
|
||||
// 这里可以添加从数据库刷新状态的逻辑
|
||||
qDebug() << "Refreshing status for device:" << m_deviceInfo.name;
|
||||
|
||||
// 暂时使用模拟数据
|
||||
// 实际实现中应该从DeviceManager或数据库获取最新状态
|
||||
updateDeviceStatus(m_currentStatus);
|
||||
}
|
||||
|
||||
void DeviceCard::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
setSelected(!m_isSelected);
|
||||
event->accept();
|
||||
}
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DeviceCard::enterEvent(QEvent *event)
|
||||
{
|
||||
m_isHovered = true;
|
||||
update();
|
||||
QWidget::enterEvent(event);
|
||||
}
|
||||
|
||||
void DeviceCard::leaveEvent(QEvent *event)
|
||||
{
|
||||
m_isHovered = false;
|
||||
update();
|
||||
QWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
void DeviceCard::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
|
||||
// 如果被选中,绘制选中边框
|
||||
if (m_isSelected) {
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QPen pen(QColor(82, 194, 242), 3);
|
||||
painter.setPen(pen);
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), BORDER_RADIUS, BORDER_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceCard::onDetailsClicked()
|
||||
{
|
||||
qDebug() << "Details clicked for device:" << m_deviceInfo.name;
|
||||
emit deviceDetailsRequested(m_deviceInfo.id);
|
||||
}
|
||||
|
||||
void DeviceCard::onControlClicked()
|
||||
{
|
||||
qDebug() << "Control clicked for device:" << m_deviceInfo.name;
|
||||
emit deviceControlRequested(m_deviceInfo.id);
|
||||
}
|
||||
|
||||
void DeviceCard::onLocationClicked()
|
||||
{
|
||||
qDebug() << "Location clicked for device:" << m_deviceInfo.name;
|
||||
emit deviceLocationRequested(m_deviceInfo.id);
|
||||
}
|
||||
|
||||
void DeviceCard::updateStatusColor()
|
||||
{
|
||||
QString color = getStatusColor(m_currentStatus);
|
||||
m_statusIndicator->setStyleSheet(QString("QLabel { color: %1; background: transparent; border: none; }").arg(color));
|
||||
|
||||
// 更新卡片边框颜色
|
||||
QString borderColor;
|
||||
switch (m_currentStatus) {
|
||||
case DeviceStatus::Online:
|
||||
borderColor = "rgba(0, 255, 127, 0.6)";
|
||||
break;
|
||||
case DeviceStatus::Warning:
|
||||
borderColor = "rgba(255, 215, 0, 0.6)";
|
||||
break;
|
||||
case DeviceStatus::Offline:
|
||||
borderColor = "rgba(255, 68, 68, 0.6)";
|
||||
break;
|
||||
default:
|
||||
borderColor = "rgba(136, 136, 136, 0.6)";
|
||||
break;
|
||||
}
|
||||
|
||||
setStyleSheet(styleSheet().replace(QRegExp("border: [^;]*;"), QString("border: 2px solid %1;").arg(borderColor)));
|
||||
}
|
||||
|
||||
void DeviceCard::updateDeviceIcon()
|
||||
{
|
||||
QString iconPath = m_deviceInfo.getTypeIconPath();
|
||||
QPixmap pixmap(iconPath);
|
||||
if (!pixmap.isNull()) {
|
||||
m_deviceIconLabel->setPixmap(pixmap.scaled(24, 24, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
} else {
|
||||
qWarning() << "Failed to load device icon:" << iconPath;
|
||||
m_deviceIconLabel->setText("📱"); // 备用图标
|
||||
}
|
||||
}
|
||||
|
||||
QString DeviceCard::getStatusText(DeviceStatus status) const
|
||||
{
|
||||
switch (status) {
|
||||
case DeviceStatus::Online: return "在线";
|
||||
case DeviceStatus::Warning: return "警告";
|
||||
case DeviceStatus::Offline: return "离线";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
QString DeviceCard::getStatusColor(DeviceStatus status) const
|
||||
{
|
||||
switch (status) {
|
||||
case DeviceStatus::Online: return "#00FF7F"; // 绿色
|
||||
case DeviceStatus::Warning: return "#FFD700"; // 黄色
|
||||
case DeviceStatus::Offline: return "#FF4444"; // 红色
|
||||
default: return "#888888"; // 灰色
|
||||
}
|
||||
}
|
||||
|
||||
QString DeviceCard::formatCoordinates(double longitude, double latitude) const
|
||||
{
|
||||
return QString("N%1, E%2")
|
||||
.arg(latitude, 0, 'f', 2)
|
||||
.arg(longitude, 0, 'f', 2);
|
||||
}
|
@ -0,0 +1,871 @@
|
||||
/**
|
||||
* @file DeviceListPanel.cpp
|
||||
* @brief 设备列表面板组件实现
|
||||
* @author CasualtySightPlus Team
|
||||
* @date 2024-12-01
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
#include "ui/components/DeviceListPanel.h"
|
||||
|
||||
// Qt GUI头文件
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QApplication>
|
||||
#include <QSplitter>
|
||||
|
||||
DeviceListPanel::DeviceListPanel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_titleLabel(nullptr)
|
||||
, m_searchEdit(nullptr)
|
||||
, m_filterComboBox(nullptr)
|
||||
, m_deviceCountLabel(nullptr)
|
||||
, m_addUAVButton(nullptr)
|
||||
, m_addDogButton(nullptr)
|
||||
, m_refreshButton(nullptr)
|
||||
, m_scrollArea(nullptr)
|
||||
, m_scrollWidget(nullptr)
|
||||
, m_deviceListLayout(nullptr)
|
||||
, m_mainLayout(nullptr)
|
||||
, m_headerLayout(nullptr)
|
||||
, m_searchLayout(nullptr)
|
||||
, m_buttonLayout(nullptr)
|
||||
, m_currentFilterType(DeviceFilterType::All)
|
||||
, m_isMonitoringActive(false)
|
||||
, m_uavDatabase(nullptr)
|
||||
, m_dogDatabase(nullptr)
|
||||
, m_totalDeviceCount(0)
|
||||
, m_onlineDeviceCount(0)
|
||||
, m_uavCount(0)
|
||||
, m_dogCount(0)
|
||||
{
|
||||
// 初始化数据库连接
|
||||
m_uavDatabase = UAVDatabase::getInstance();
|
||||
m_dogDatabase = DogDatabase::getInstance();
|
||||
|
||||
// 创建状态监控定时器
|
||||
m_statusMonitorTimer = new QTimer(this);
|
||||
m_statusMonitorTimer->setInterval(STATUS_MONITOR_INTERVAL);
|
||||
|
||||
setupUI();
|
||||
setupStyle();
|
||||
connectSignals();
|
||||
|
||||
// 初始加载设备列表
|
||||
refreshDeviceList();
|
||||
|
||||
qDebug() << "DeviceListPanel created successfully";
|
||||
}
|
||||
|
||||
DeviceListPanel::~DeviceListPanel()
|
||||
{
|
||||
stopStatusMonitoring();
|
||||
clearAllDeviceCards();
|
||||
qDebug() << "DeviceListPanel destroyed";
|
||||
}
|
||||
|
||||
void DeviceListPanel::setupUI()
|
||||
{
|
||||
// 创建主布局
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setContentsMargins(PANEL_PADDING, PANEL_PADDING, PANEL_PADDING, PANEL_PADDING);
|
||||
m_mainLayout->setSpacing(12);
|
||||
|
||||
// === 头部区域 ===
|
||||
m_headerLayout = new QHBoxLayout();
|
||||
|
||||
// 面板标题
|
||||
m_titleLabel = new QLabel("🤖 设备管理");
|
||||
m_titleLabel->setFont(QFont("Arial", 16, QFont::Bold));
|
||||
|
||||
// 设备数量标签
|
||||
m_deviceCountLabel = new QLabel("设备: 0");
|
||||
m_deviceCountLabel->setFont(QFont("Arial", 10));
|
||||
|
||||
m_headerLayout->addWidget(m_titleLabel);
|
||||
m_headerLayout->addStretch();
|
||||
m_headerLayout->addWidget(m_deviceCountLabel);
|
||||
|
||||
// === 搜索和过滤区域 ===
|
||||
m_searchLayout = new QHBoxLayout();
|
||||
|
||||
// 搜索框
|
||||
m_searchEdit = new QLineEdit();
|
||||
m_searchEdit->setPlaceholderText("搜索设备...");
|
||||
m_searchEdit->setMaximumHeight(32);
|
||||
|
||||
// 过滤下拉框
|
||||
m_filterComboBox = new QComboBox();
|
||||
m_filterComboBox->addItem("全部设备", static_cast<int>(DeviceFilterType::All));
|
||||
m_filterComboBox->addItem("无人机", static_cast<int>(DeviceFilterType::UAV));
|
||||
m_filterComboBox->addItem("机器狗", static_cast<int>(DeviceFilterType::Dog));
|
||||
m_filterComboBox->addItem("在线设备", static_cast<int>(DeviceFilterType::Online));
|
||||
m_filterComboBox->addItem("离线设备", static_cast<int>(DeviceFilterType::Offline));
|
||||
m_filterComboBox->setMaximumHeight(32);
|
||||
m_filterComboBox->setMaximumWidth(100);
|
||||
|
||||
m_searchLayout->addWidget(m_searchEdit);
|
||||
m_searchLayout->addWidget(m_filterComboBox);
|
||||
|
||||
// === 操作按钮区域 ===
|
||||
m_buttonLayout = new QHBoxLayout();
|
||||
|
||||
m_addUAVButton = new QPushButton("+ 无人机");
|
||||
m_addUAVButton->setMaximumHeight(32);
|
||||
m_addUAVButton->setMaximumWidth(80);
|
||||
|
||||
m_addDogButton = new QPushButton("+ 机器狗");
|
||||
m_addDogButton->setMaximumHeight(32);
|
||||
m_addDogButton->setMaximumWidth(80);
|
||||
|
||||
m_refreshButton = new QPushButton("🔄");
|
||||
m_refreshButton->setMaximumHeight(32);
|
||||
m_refreshButton->setMaximumWidth(40);
|
||||
m_refreshButton->setToolTip("刷新设备列表");
|
||||
|
||||
m_buttonLayout->addWidget(m_addUAVButton);
|
||||
m_buttonLayout->addWidget(m_addDogButton);
|
||||
m_buttonLayout->addStretch();
|
||||
m_buttonLayout->addWidget(m_refreshButton);
|
||||
|
||||
// === 设备列表滚动区域 ===
|
||||
m_scrollArea = new QScrollArea();
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_scrollArea->setMinimumHeight(300); // 确保足够的显示空间
|
||||
|
||||
m_scrollWidget = new QWidget();
|
||||
m_deviceListLayout = new QVBoxLayout(m_scrollWidget);
|
||||
m_deviceListLayout->setSpacing(8); // 减小间距使卡片更紧凑
|
||||
m_deviceListLayout->setContentsMargins(4, 4, 4, 4); // 小边距
|
||||
m_deviceListLayout->addStretch(); // 底部弹性空间
|
||||
|
||||
m_scrollArea->setWidget(m_scrollWidget);
|
||||
|
||||
// === 组装主布局 ===
|
||||
m_mainLayout->addLayout(m_headerLayout);
|
||||
|
||||
// 添加分隔线
|
||||
QLabel *separatorLine = new QLabel();
|
||||
separatorLine->setFixedHeight(2);
|
||||
separatorLine->setStyleSheet("background-color: rgba(82, 194, 242, 0.3);");
|
||||
m_mainLayout->addWidget(separatorLine);
|
||||
|
||||
m_mainLayout->addLayout(m_searchLayout);
|
||||
m_mainLayout->addLayout(m_buttonLayout);
|
||||
m_mainLayout->addWidget(m_scrollArea, 1); // 占据剩余空间
|
||||
}
|
||||
|
||||
void DeviceListPanel::setupStyle()
|
||||
{
|
||||
// 面板整体样式 - 更现代的军用风格
|
||||
setStyleSheet(
|
||||
"DeviceListPanel {"
|
||||
" background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,"
|
||||
" stop:0 rgb(15, 22, 32), stop:1 rgb(25, 35, 45));"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.3);"
|
||||
" border-radius: 8px;"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 标题样式 - 增强军用特色
|
||||
m_titleLabel->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: rgb(255, 255, 255);"
|
||||
" background: transparent;"
|
||||
" border: none;"
|
||||
" font-weight: bold;"
|
||||
" text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 设备数量标签样式 - 添加背景和边框
|
||||
m_deviceCountLabel->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: rgb(82, 194, 242);"
|
||||
" background: rgba(82, 194, 242, 0.1);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.3);"
|
||||
" border-radius: 4px;"
|
||||
" padding: 2px 6px;"
|
||||
" font-weight: bold;"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 搜索框样式 - 改善视觉效果
|
||||
m_searchEdit->setStyleSheet(
|
||||
"QLineEdit {"
|
||||
" background: rgba(45, 65, 95, 0.9);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 2px solid rgba(82, 194, 242, 0.4);"
|
||||
" border-radius: 6px;"
|
||||
" padding: 6px 10px;"
|
||||
" font-size: 13px;"
|
||||
"}"
|
||||
"QLineEdit:focus {"
|
||||
" border-color: rgba(82, 194, 242, 0.8);"
|
||||
" background: rgba(45, 65, 95, 1.0);"
|
||||
"}"
|
||||
"QLineEdit:hover {"
|
||||
" border-color: rgba(82, 194, 242, 0.6);"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 过滤下拉框样式
|
||||
m_filterComboBox->setStyleSheet(
|
||||
"QComboBox {"
|
||||
" background: rgba(45, 65, 95, 0.9);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 2px solid rgba(82, 194, 242, 0.4);"
|
||||
" border-radius: 6px;"
|
||||
" padding: 4px 8px;"
|
||||
" font-size: 12px;"
|
||||
"}"
|
||||
"QComboBox:hover {"
|
||||
" border-color: rgba(82, 194, 242, 0.6);"
|
||||
"}"
|
||||
"QComboBox::drop-down {"
|
||||
" border: none;"
|
||||
"}"
|
||||
"QComboBox::down-arrow {"
|
||||
" color: rgb(82, 194, 242);"
|
||||
"}"
|
||||
"QComboBox QAbstractItemView {"
|
||||
" background: rgba(45, 65, 95, 0.95);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
" selection-background-color: rgba(82, 194, 242, 0.3);"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 按钮统一样式 - 军用风格
|
||||
QString buttonStyle =
|
||||
"QPushButton {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
|
||||
" stop:0 rgba(45, 65, 95, 0.8), "
|
||||
" stop:1 rgba(25, 40, 65, 0.8));"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 2px solid rgba(82, 194, 242, 0.5);"
|
||||
" border-radius: 6px;"
|
||||
" padding: 6px 12px;"
|
||||
" font-size: 12px;"
|
||||
" font-weight: bold;"
|
||||
"}"
|
||||
"QPushButton:hover {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
|
||||
" stop:0 rgba(82, 194, 242, 0.6), "
|
||||
" stop:1 rgba(45, 120, 180, 0.6));"
|
||||
" border-color: rgba(82, 194, 242, 0.8);"
|
||||
" color: white;"
|
||||
"}"
|
||||
"QPushButton:pressed {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
|
||||
" stop:0 rgba(82, 194, 242, 0.8), "
|
||||
" stop:1 rgba(45, 120, 180, 0.8));"
|
||||
" border-color: rgba(82, 194, 242, 1.0);"
|
||||
"}";
|
||||
|
||||
m_addUAVButton->setStyleSheet(buttonStyle);
|
||||
m_addDogButton->setStyleSheet(buttonStyle);
|
||||
m_refreshButton->setStyleSheet(buttonStyle);
|
||||
|
||||
// 滚动区域样式
|
||||
m_scrollArea->setStyleSheet(
|
||||
"QScrollArea {"
|
||||
" background: transparent;"
|
||||
" border: none;"
|
||||
"}"
|
||||
"QScrollBar:vertical {"
|
||||
" background: rgba(25, 35, 45, 0.5);"
|
||||
" width: 8px;"
|
||||
" border-radius: 4px;"
|
||||
"}"
|
||||
"QScrollBar::handle:vertical {"
|
||||
" background: rgba(82, 194, 242, 0.6);"
|
||||
" border-radius: 4px;"
|
||||
" min-height: 20px;"
|
||||
"}"
|
||||
"QScrollBar::handle:vertical:hover {"
|
||||
" background: rgba(82, 194, 242, 0.8);"
|
||||
"}"
|
||||
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {"
|
||||
" border: none;"
|
||||
" background: none;"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
void DeviceListPanel::connectSignals()
|
||||
{
|
||||
// 搜索和过滤信号
|
||||
connect(m_searchEdit, &QLineEdit::textChanged, this, &DeviceListPanel::onSearchTextChanged);
|
||||
connect(m_filterComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &DeviceListPanel::onFilterTypeChanged);
|
||||
|
||||
// 按钮信号
|
||||
connect(m_addUAVButton, &QPushButton::clicked, this, &DeviceListPanel::onAddUAVClicked);
|
||||
connect(m_addDogButton, &QPushButton::clicked, this, &DeviceListPanel::onAddDogClicked);
|
||||
connect(m_refreshButton, &QPushButton::clicked, this, &DeviceListPanel::refreshDeviceList);
|
||||
|
||||
// 状态监控定时器
|
||||
connect(m_statusMonitorTimer, &QTimer::timeout, this, &DeviceListPanel::onStatusMonitorTimer);
|
||||
}
|
||||
|
||||
QString DeviceListPanel::getSelectedDeviceId() const
|
||||
{
|
||||
return m_selectedDeviceId;
|
||||
}
|
||||
|
||||
int DeviceListPanel::getDeviceCountByType(const QString &type) const
|
||||
{
|
||||
if (type == "uav") return m_uavCount;
|
||||
else if (type == "dog") return m_dogCount;
|
||||
else return m_totalDeviceCount;
|
||||
}
|
||||
|
||||
int DeviceListPanel::getOnlineDeviceCount() const
|
||||
{
|
||||
return m_onlineDeviceCount;
|
||||
}
|
||||
|
||||
void DeviceListPanel::refreshDeviceList()
|
||||
{
|
||||
qDebug() << "Refreshing device list...";
|
||||
|
||||
// 清除现有设备卡片
|
||||
clearAllDeviceCards();
|
||||
|
||||
// 从数据库加载设备
|
||||
m_allDevices = loadDevicesFromDatabase();
|
||||
|
||||
// 创建设备卡片
|
||||
for (const auto &device : m_allDevices) {
|
||||
DeviceCard *card = createDeviceCard(device);
|
||||
if (card) {
|
||||
m_deviceCards[device.id] = card;
|
||||
}
|
||||
}
|
||||
|
||||
// 应用当前的搜索和过滤条件
|
||||
applySearchAndFilter();
|
||||
|
||||
// 更新统计信息
|
||||
updateDeviceCountStats();
|
||||
|
||||
qDebug() << "Device list refreshed. Total devices:" << m_allDevices.size();
|
||||
}
|
||||
|
||||
void DeviceListPanel::addDevice(const DeviceInfo &device)
|
||||
{
|
||||
// 检查设备是否已存在
|
||||
if (m_deviceCards.contains(device.id)) {
|
||||
qWarning() << "Device already exists:" << device.id;
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加到设备列表
|
||||
m_allDevices.append(device);
|
||||
|
||||
// 创建设备卡片
|
||||
DeviceCard *card = createDeviceCard(device);
|
||||
if (card) {
|
||||
m_deviceCards[device.id] = card;
|
||||
|
||||
// 应用搜索和过滤
|
||||
applySearchAndFilter();
|
||||
updateDeviceCountStats();
|
||||
|
||||
qDebug() << "Device added:" << device.name;
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceListPanel::removeDevice(const QString &deviceId)
|
||||
{
|
||||
if (!m_deviceCards.contains(deviceId)) {
|
||||
qWarning() << "Device not found:" << deviceId;
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除设备卡片
|
||||
DeviceCard *card = m_deviceCards.take(deviceId);
|
||||
card->deleteLater();
|
||||
|
||||
// 从设备列表中移除
|
||||
for (int i = m_allDevices.size() - 1; i >= 0; --i) {
|
||||
if (m_allDevices[i].id == deviceId) {
|
||||
m_allDevices.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 清除选择状态
|
||||
if (m_selectedDeviceId == deviceId) {
|
||||
m_selectedDeviceId.clear();
|
||||
}
|
||||
|
||||
updateDeviceCountStats();
|
||||
|
||||
qDebug() << "Device removed:" << deviceId;
|
||||
}
|
||||
|
||||
void DeviceListPanel::updateDevice(const DeviceInfo &device)
|
||||
{
|
||||
if (!m_deviceCards.contains(device.id)) {
|
||||
qWarning() << "Device not found for update:" << device.id;
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新设备信息
|
||||
for (auto &existingDevice : m_allDevices) {
|
||||
if (existingDevice.id == device.id) {
|
||||
existingDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设备卡片
|
||||
DeviceCard *card = m_deviceCards[device.id];
|
||||
card->updateDeviceInfo(device);
|
||||
|
||||
updateDeviceCountStats();
|
||||
|
||||
qDebug() << "Device updated:" << device.name;
|
||||
}
|
||||
|
||||
void DeviceListPanel::updateDeviceStatus(const QString &deviceId, DeviceStatus status)
|
||||
{
|
||||
if (!m_deviceCards.contains(deviceId)) {
|
||||
qWarning() << "Device not found for status update:" << deviceId;
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新设备状态
|
||||
for (auto &device : m_allDevices) {
|
||||
if (device.id == deviceId) {
|
||||
device.status = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设备卡片状态
|
||||
DeviceCard *card = m_deviceCards[deviceId];
|
||||
card->updateDeviceStatus(status);
|
||||
|
||||
updateDeviceCountStats();
|
||||
|
||||
qDebug() << "Device status updated:" << deviceId << "status:" << static_cast<int>(status);
|
||||
}
|
||||
|
||||
void DeviceListPanel::startStatusMonitoring()
|
||||
{
|
||||
if (!m_isMonitoringActive) {
|
||||
m_statusMonitorTimer->start();
|
||||
m_isMonitoringActive = true;
|
||||
qDebug() << "Status monitoring started";
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceListPanel::stopStatusMonitoring()
|
||||
{
|
||||
if (m_isMonitoringActive) {
|
||||
m_statusMonitorTimer->stop();
|
||||
m_isMonitoringActive = false;
|
||||
qDebug() << "Status monitoring stopped";
|
||||
}
|
||||
}
|
||||
|
||||
DeviceCard* DeviceListPanel::createDeviceCard(const DeviceInfo &device)
|
||||
{
|
||||
DeviceCard *card = new DeviceCard(device, this);
|
||||
|
||||
// 连接设备卡片信号
|
||||
connect(card, &DeviceCard::deviceSelected, this, &DeviceListPanel::onDeviceCardSelected);
|
||||
connect(card, &DeviceCard::deviceControlRequested, this, &DeviceListPanel::onDeviceCardControlRequested);
|
||||
connect(card, &DeviceCard::deviceLocationRequested, this, &DeviceListPanel::onDeviceCardLocationRequested);
|
||||
connect(card, &DeviceCard::deviceDetailsRequested, this, &DeviceListPanel::onDeviceCardDetailsRequested);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
QList<DeviceInfo> DeviceListPanel::loadDevicesFromDatabase()
|
||||
{
|
||||
QList<DeviceInfo> devices;
|
||||
|
||||
qDebug() << "Loading devices from database...";
|
||||
|
||||
// 添加测试数据以便查看效果
|
||||
DeviceInfo uav1;
|
||||
uav1.id = "UAV001";
|
||||
uav1.name = "侦察机-01";
|
||||
uav1.type = "uav";
|
||||
uav1.ipAddress = "192.168.1.101";
|
||||
uav1.port = 8080;
|
||||
uav1.longitude = 116.40;
|
||||
uav1.latitude = 39.90;
|
||||
uav1.signalStrength = 85;
|
||||
uav1.batteryLevel = 90;
|
||||
uav1.status = DeviceStatus::Online;
|
||||
uav1.lastHeartbeat = QDateTime::currentDateTime();
|
||||
uav1.createdAt = QDateTime::currentDateTime().addDays(-7);
|
||||
uav1.updatedAt = QDateTime::currentDateTime();
|
||||
devices.append(uav1);
|
||||
|
||||
DeviceInfo uav2;
|
||||
uav2.id = "UAV002";
|
||||
uav2.name = "侦察机-02";
|
||||
uav2.type = "uav";
|
||||
uav2.ipAddress = "192.168.1.102";
|
||||
uav2.port = 8080;
|
||||
uav2.longitude = 116.42;
|
||||
uav2.latitude = 39.92;
|
||||
uav2.signalStrength = 72;
|
||||
uav2.batteryLevel = 65;
|
||||
uav2.status = DeviceStatus::Warning;
|
||||
uav2.lastHeartbeat = QDateTime::currentDateTime().addSecs(-30);
|
||||
uav2.createdAt = QDateTime::currentDateTime().addDays(-5);
|
||||
uav2.updatedAt = QDateTime::currentDateTime();
|
||||
devices.append(uav2);
|
||||
|
||||
DeviceInfo dog1;
|
||||
dog1.id = "DOG001";
|
||||
dog1.name = "巡逻犬-Alpha";
|
||||
dog1.type = "dog";
|
||||
dog1.ipAddress = "192.168.1.201";
|
||||
dog1.port = 9090;
|
||||
dog1.longitude = 116.38;
|
||||
dog1.latitude = 39.88;
|
||||
dog1.signalStrength = 95;
|
||||
dog1.batteryLevel = 80;
|
||||
dog1.status = DeviceStatus::Online;
|
||||
dog1.lastHeartbeat = QDateTime::currentDateTime();
|
||||
dog1.createdAt = QDateTime::currentDateTime().addDays(-3);
|
||||
dog1.updatedAt = QDateTime::currentDateTime();
|
||||
devices.append(dog1);
|
||||
|
||||
DeviceInfo dog2;
|
||||
dog2.id = "DOG002";
|
||||
dog2.name = "巡逻犬-Beta";
|
||||
dog2.type = "dog";
|
||||
dog2.ipAddress = "192.168.1.202";
|
||||
dog2.port = 9090;
|
||||
dog2.longitude = 116.44;
|
||||
dog2.latitude = 39.86;
|
||||
dog2.signalStrength = 0;
|
||||
dog2.batteryLevel = 25;
|
||||
dog2.status = DeviceStatus::Offline;
|
||||
dog2.lastHeartbeat = QDateTime::currentDateTime().addSecs(-300);
|
||||
dog2.createdAt = QDateTime::currentDateTime().addDays(-1);
|
||||
dog2.updatedAt = QDateTime::currentDateTime().addSecs(-120);
|
||||
devices.append(dog2);
|
||||
|
||||
// TODO: 实现真实的数据库查询
|
||||
// auto uavList = m_uavDatabase->getAllDevices();
|
||||
// auto dogList = m_dogDatabase->getAllDevices();
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
void DeviceListPanel::applySearchAndFilter()
|
||||
{
|
||||
qDebug() << "Applying search and filter. Keyword:" << m_currentSearchKeyword
|
||||
<< "Filter:" << static_cast<int>(m_currentFilterType);
|
||||
|
||||
// 隐藏所有设备卡片
|
||||
for (auto card : m_deviceCards) {
|
||||
card->hide();
|
||||
m_deviceListLayout->removeWidget(card);
|
||||
}
|
||||
|
||||
// 根据条件显示匹配的设备卡片
|
||||
for (const auto &device : m_allDevices) {
|
||||
if (isDeviceMatchFilter(device)) {
|
||||
DeviceCard *card = m_deviceCards[device.id];
|
||||
if (card) {
|
||||
m_deviceListLayout->insertWidget(m_deviceListLayout->count() - 1, card);
|
||||
card->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceListPanel::isDeviceMatchFilter(const DeviceInfo &device) const
|
||||
{
|
||||
// 检查搜索关键词
|
||||
if (!m_currentSearchKeyword.isEmpty()) {
|
||||
if (!device.name.contains(m_currentSearchKeyword, Qt::CaseInsensitive) &&
|
||||
!device.ipAddress.contains(m_currentSearchKeyword, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查过滤类型
|
||||
switch (m_currentFilterType) {
|
||||
case DeviceFilterType::UAV:
|
||||
return device.type == "uav";
|
||||
case DeviceFilterType::Dog:
|
||||
return device.type == "dog";
|
||||
case DeviceFilterType::Online:
|
||||
return device.isOnline();
|
||||
case DeviceFilterType::Offline:
|
||||
return !device.isOnline();
|
||||
case DeviceFilterType::All:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceListPanel::updateDeviceCountStats()
|
||||
{
|
||||
m_totalDeviceCount = m_allDevices.size();
|
||||
m_onlineDeviceCount = 0;
|
||||
m_uavCount = 0;
|
||||
m_dogCount = 0;
|
||||
|
||||
for (const auto &device : m_allDevices) {
|
||||
if (device.isOnline()) {
|
||||
m_onlineDeviceCount++;
|
||||
}
|
||||
if (device.type == "uav") {
|
||||
m_uavCount++;
|
||||
} else if (device.type == "dog") {
|
||||
m_dogCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新显示
|
||||
m_deviceCountLabel->setText(QString("设备: %1 (在线: %2)")
|
||||
.arg(m_totalDeviceCount)
|
||||
.arg(m_onlineDeviceCount));
|
||||
|
||||
// 发送统计信号
|
||||
emit deviceCountChanged(m_totalDeviceCount, m_onlineDeviceCount);
|
||||
}
|
||||
|
||||
void DeviceListPanel::clearAllDeviceCards()
|
||||
{
|
||||
for (auto card : m_deviceCards) {
|
||||
card->deleteLater();
|
||||
}
|
||||
m_deviceCards.clear();
|
||||
m_allDevices.clear();
|
||||
m_selectedDeviceId.clear();
|
||||
}
|
||||
|
||||
// 槽函数实现
|
||||
void DeviceListPanel::onSearchTextChanged(const QString &text)
|
||||
{
|
||||
m_currentSearchKeyword = text.trimmed();
|
||||
applySearchAndFilter();
|
||||
}
|
||||
|
||||
void DeviceListPanel::onFilterTypeChanged(int index)
|
||||
{
|
||||
m_currentFilterType = static_cast<DeviceFilterType>(m_filterComboBox->itemData(index).toInt());
|
||||
applySearchAndFilter();
|
||||
}
|
||||
|
||||
void DeviceListPanel::onAddUAVClicked()
|
||||
{
|
||||
emit addDeviceRequested("uav");
|
||||
}
|
||||
|
||||
void DeviceListPanel::onAddDogClicked()
|
||||
{
|
||||
emit addDeviceRequested("dog");
|
||||
}
|
||||
|
||||
void DeviceListPanel::onDeviceCardSelected(const QString &deviceId)
|
||||
{
|
||||
// 清除之前的选择
|
||||
if (!m_selectedDeviceId.isEmpty() && m_deviceCards.contains(m_selectedDeviceId)) {
|
||||
m_deviceCards[m_selectedDeviceId]->setSelected(false);
|
||||
}
|
||||
|
||||
m_selectedDeviceId = deviceId;
|
||||
emit deviceSelected(deviceId);
|
||||
}
|
||||
|
||||
void DeviceListPanel::onDeviceCardControlRequested(const QString &deviceId)
|
||||
{
|
||||
emit deviceControlRequested(deviceId);
|
||||
}
|
||||
|
||||
void DeviceListPanel::onDeviceCardLocationRequested(const QString &deviceId)
|
||||
{
|
||||
emit deviceLocationRequested(deviceId);
|
||||
}
|
||||
|
||||
void DeviceListPanel::onDeviceCardDetailsRequested(const QString &deviceId)
|
||||
{
|
||||
emit deviceDetailsRequested(deviceId);
|
||||
}
|
||||
|
||||
void DeviceListPanel::setSearchKeyword(const QString &keyword)
|
||||
{
|
||||
m_currentSearchKeyword = keyword.trimmed();
|
||||
applySearchAndFilter();
|
||||
}
|
||||
|
||||
void DeviceListPanel::setFilterType(DeviceFilterType filterType)
|
||||
{
|
||||
m_currentFilterType = filterType;
|
||||
applySearchAndFilter();
|
||||
}
|
||||
|
||||
void DeviceListPanel::selectDevice(const QString &deviceId)
|
||||
{
|
||||
if (m_deviceCards.contains(deviceId)) {
|
||||
// 清除之前的选择
|
||||
if (!m_selectedDeviceId.isEmpty() && m_deviceCards.contains(m_selectedDeviceId)) {
|
||||
m_deviceCards[m_selectedDeviceId]->setSelected(false);
|
||||
}
|
||||
|
||||
// 设置新选择
|
||||
m_selectedDeviceId = deviceId;
|
||||
m_deviceCards[deviceId]->setSelected(true);
|
||||
|
||||
emit deviceSelected(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceListPanel::clearSelection()
|
||||
{
|
||||
if (!m_selectedDeviceId.isEmpty() && m_deviceCards.contains(m_selectedDeviceId)) {
|
||||
m_deviceCards[m_selectedDeviceId]->setSelected(false);
|
||||
}
|
||||
m_selectedDeviceId.clear();
|
||||
}
|
||||
|
||||
void DeviceListPanel::onStatusMonitorTimer()
|
||||
{
|
||||
// 定期检查设备状态
|
||||
// 这里可以实现设备状态的定期更新逻辑
|
||||
qDebug() << "Status monitor timer triggered";
|
||||
|
||||
// TODO: 实现真实的状态检查逻辑
|
||||
// 例如:检查设备心跳、网络连接状态等
|
||||
}
|
||||
|
||||
void DeviceListPanel::updateDeviceCount()
|
||||
{
|
||||
// 更新统计数据
|
||||
m_totalDeviceCount = m_deviceCards.size();
|
||||
m_onlineDeviceCount = 0;
|
||||
m_uavCount = 0;
|
||||
m_dogCount = 0;
|
||||
|
||||
// 统计各类设备数量
|
||||
for (const auto& card : m_deviceCards) {
|
||||
const DeviceInfo& info = card->getDeviceInfo();
|
||||
|
||||
if (info.isOnline()) {
|
||||
m_onlineDeviceCount++;
|
||||
}
|
||||
|
||||
if (info.type == "uav") {
|
||||
m_uavCount++;
|
||||
} else if (info.type == "dog") {
|
||||
m_dogCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新显示文本 - 更详细的信息
|
||||
QString statusText = QString("设备: %1 | 在线: %2 | 🚁UAV: %3 | 🤖DOG: %4")
|
||||
.arg(m_totalDeviceCount)
|
||||
.arg(m_onlineDeviceCount)
|
||||
.arg(m_uavCount)
|
||||
.arg(m_dogCount);
|
||||
|
||||
m_deviceCountLabel->setText(statusText);
|
||||
|
||||
// 根据在线率设置颜色
|
||||
double onlineRate = m_totalDeviceCount > 0 ? (double)m_onlineDeviceCount / m_totalDeviceCount : 0.0;
|
||||
QString color;
|
||||
if (onlineRate >= 0.8) {
|
||||
color = "rgb(0, 255, 0)"; // 绿色 - 状态良好
|
||||
} else if (onlineRate >= 0.5) {
|
||||
color = "rgb(255, 255, 0)"; // 黄色 - 状态一般
|
||||
} else {
|
||||
color = "rgb(255, 100, 100)"; // 红色 - 状态不佳
|
||||
}
|
||||
|
||||
m_deviceCountLabel->setStyleSheet(
|
||||
QString("QLabel {"
|
||||
" color: %1;"
|
||||
" background: rgba(82, 194, 242, 0.1);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.3);"
|
||||
" border-radius: 4px;"
|
||||
" padding: 2px 6px;"
|
||||
" font-weight: bold;"
|
||||
" font-size: 11px;"
|
||||
"}").arg(color)
|
||||
);
|
||||
|
||||
// 发送设备数量变化信号
|
||||
emit deviceCountChanged(m_totalDeviceCount, m_onlineDeviceCount);
|
||||
}
|
||||
|
||||
void DeviceListPanel::updateDeviceListDisplay()
|
||||
{
|
||||
// 清空现有显示
|
||||
for (auto card : m_deviceCards) {
|
||||
card->hide();
|
||||
m_deviceListLayout->removeWidget(card);
|
||||
}
|
||||
|
||||
// 根据过滤条件重新显示设备
|
||||
QString searchText = m_searchEdit->text().toLower();
|
||||
|
||||
for (auto card : m_deviceCards) {
|
||||
const DeviceInfo& info = card->getDeviceInfo();
|
||||
bool shouldShow = true;
|
||||
|
||||
// 应用搜索过滤
|
||||
if (!searchText.isEmpty()) {
|
||||
if (!info.name.toLower().contains(searchText) &&
|
||||
!info.ipAddress.toLower().contains(searchText) &&
|
||||
!info.id.toLower().contains(searchText)) {
|
||||
shouldShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 应用类型过滤
|
||||
switch (m_currentFilterType) {
|
||||
case DeviceFilterType::UAV:
|
||||
if (info.type != "uav") shouldShow = false;
|
||||
break;
|
||||
case DeviceFilterType::Dog:
|
||||
if (info.type != "dog") shouldShow = false;
|
||||
break;
|
||||
case DeviceFilterType::Online:
|
||||
if (!info.isOnline()) shouldShow = false;
|
||||
break;
|
||||
case DeviceFilterType::Offline:
|
||||
if (info.isOnline()) shouldShow = false;
|
||||
break;
|
||||
case DeviceFilterType::All:
|
||||
default:
|
||||
// 显示所有设备
|
||||
break;
|
||||
}
|
||||
|
||||
if (shouldShow) {
|
||||
card->show();
|
||||
m_deviceListLayout->insertWidget(m_deviceListLayout->count() - 1, card);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设备计数
|
||||
updateDeviceCount();
|
||||
}
|
||||
|
||||
void DeviceListPanel::applyDeviceFilter()
|
||||
{
|
||||
// 获取当前过滤类型
|
||||
int filterIndex = m_filterComboBox->currentIndex();
|
||||
QVariant filterData = m_filterComboBox->itemData(filterIndex);
|
||||
m_currentFilterType = static_cast<DeviceFilterType>(filterData.toInt());
|
||||
|
||||
// 更新显示
|
||||
updateDeviceListDisplay();
|
||||
}
|
Loading…
Reference in new issue