#include "login_page.h" #include #include #include #include #include #include #include #include #include #include namespace vdi { namespace { const char* kResolutionIds[] = {"1920x1440", "2560x1440", "1920x1080", "1280x720"}; QString detectNetworkSummary() { // 取默认路由的出接口与该接口上的 IPv4 地址 QProcess process; process.start("sh", QStringList() << "-c" << "dev=$(ip -4 route show default | awk '{print $NF}' | head -n 1); " "addr=$(ip -4 -o addr show dev \"$dev\" | awk '{print $4}' | head -n 1); " "echo \"$dev $addr\""); process.waitForFinished(2000); const QString out = QString::fromUtf8(process.readAllStandardOutput()).trimmed(); if (out.isEmpty()) { return QStringLiteral("网络:未连接"); } const QStringList parts = out.split(' ', Qt::SkipEmptyParts); QString iface = parts.value(0); QString addr = parts.value(1); if (addr.contains('/')) { addr = addr.split('/').value(0); } if (iface.isEmpty()) { return QStringLiteral("网络:未连接"); } return QStringLiteral("网络:%1 (%2)").arg(iface, addr); } } // namespace LoginPage::LoginPage(const TerminalConfig& initial, QWidget* parent) : QWidget(parent), cfg_(initial) { auto* root = new QVBoxLayout(this); root->setContentsMargins(80, 60, 80, 40); root->setSpacing(18); // ---- 标题区 ---- auto* title = new QLabel(QStringLiteral("VDI 云终端"), this); QFont titleFont = title->font(); titleFont.setPointSize(28); titleFont.setBold(true); title->setFont(titleFont); title->setAlignment(Qt::AlignCenter); root->addWidget(title); auto* subtitle = new QLabel(QStringLiteral("RK3528 远程桌面接入"), this); QFont subFont = subtitle->font(); subFont.setPointSize(13); subtitle->setFont(subFont); subtitle->setAlignment(Qt::AlignCenter); subtitle->setStyleSheet(QStringLiteral("color: #8a94a6;")); root->addWidget(subtitle); // ---- 设备信息条 ---- auto* infoBox = new QHBoxLayout(); labelSn_ = new QLabel(QStringLiteral("设备 SN:%1").arg(QString::fromStdString(cfg_.sn)), this); labelNetwork_ = new QLabel(detectNetworkSummary(), this); labelSn_->setStyleSheet(QStringLiteral("color: #8a94a6;")); labelNetwork_->setStyleSheet(QStringLiteral("color: #8a94a6;")); infoBox->addWidget(labelSn_); infoBox->addStretch(1); infoBox->addWidget(labelNetwork_); root->addLayout(infoBox); // ---- 表单 ---- auto* grid = new QGridLayout(); grid->setHorizontalSpacing(14); grid->setVerticalSpacing(12); grid->setColumnStretch(1, 1); editServer_ = new QLineEdit(QString::fromStdString(cfg_.server), this); editServer_->setPlaceholderText(QStringLiteral("服务器地址,例如 192.168.1.70")); editUser_ = new QLineEdit(QString::fromStdString(cfg_.username), this); editUser_->setPlaceholderText(QStringLiteral("登录账号")); editPassword_ = new QLineEdit(QString::fromStdString(cfg_.password), this); editPassword_->setEchoMode(QLineEdit::Password); editPassword_->setPlaceholderText(QStringLiteral("登录密码(可留空)")); comboResolution_ = new QComboBox(this); for (const char* id : kResolutionIds) { comboResolution_->addItem(QString::fromLatin1(id), QString::fromLatin1(id)); } int resIndex = comboResolution_->findData(QString::fromStdString(cfg_.resolution)); comboResolution_->setCurrentIndex(resIndex >= 0 ? resIndex : 0); checkRemember_ = new QCheckBox(QStringLiteral("记住连接参数"), this); checkRemember_->setChecked(cfg_.remember); struct Row { QString label; QWidget* widget; }; const Row rows[] = { {QStringLiteral("服务器地址"), editServer_}, {QStringLiteral("登录账号"), editUser_}, {QStringLiteral("登录密码"), editPassword_}, {QStringLiteral("显示分辨率"), comboResolution_}, }; int row = 0; for (const Row& r : rows) { auto* label = new QLabel(r.label, this); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); grid->addWidget(label, row, 0); grid->addWidget(r.widget, row, 1); ++row; } grid->addWidget(checkRemember_, row, 1); root->addLayout(grid); // ---- 按钮 ---- auto* buttonRow = new QHBoxLayout(); buttonRow->addStretch(1); buttonConnect_ = new QPushButton(QStringLiteral("连接"), this); buttonConnect_->setMinimumSize(160, 46); buttonConnect_->setStyleSheet( QStringLiteral("QPushButton { background: #2f6feb; color: white; border: none; " "border-radius: 6px; font-weight: bold; }" "QPushButton:disabled { background: #3a4a63; }")); buttonConnect_->setDefault(true); buttonQuit_ = new QPushButton(QStringLiteral("退出"), this); buttonQuit_->setMinimumSize(110, 46); buttonRow->addWidget(buttonConnect_); buttonRow->addWidget(buttonQuit_); buttonRow->addStretch(1); root->addLayout(buttonRow); // ---- 状态条 ---- labelStatus_ = new QLabel(QStringLiteral("就绪"), this); labelStatus_->setAlignment(Qt::AlignCenter); labelStatus_->setStyleSheet(QStringLiteral("color: #8a94a6;")); root->addWidget(labelStatus_); root->addStretch(1); connect(buttonConnect_, &QPushButton::clicked, this, &LoginPage::onConnectClicked); connect(buttonQuit_, &QPushButton::clicked, this, [this]() { emit quitRequested(); }); } void LoginPage::setConnectHandler(ConnectHandler handler) { connectHandler_ = std::move(handler); } void LoginPage::setStatus(const QString& text, bool error) { labelStatus_->setText(text); labelStatus_->setStyleSheet( QStringLiteral("color: %1;").arg(error ? QStringLiteral("#e05561") : QStringLiteral("#8a94a6"))); } void LoginPage::setBusy(bool busy) { buttonConnect_->setEnabled(!busy); buttonConnect_->setText(busy ? QStringLiteral("连接中…") : QStringLiteral("连接")); editServer_->setEnabled(!busy); editUser_->setEnabled(!busy); editPassword_->setEnabled(!busy); comboResolution_->setEnabled(!busy); } TerminalConfig LoginPage::currentConfig() const { TerminalConfig out = cfg_; out.server = editServer_->text().trimmed().toStdString(); out.username = editUser_->text().trimmed().toStdString(); out.password = editPassword_->text().toStdString(); out.resolution = comboResolution_->currentData().toString().toStdString(); out.remember = checkRemember_->isChecked(); return out; } void LoginPage::onConnectClicked() { TerminalConfig cfg = currentConfig(); if (cfg.server.empty()) { setStatus(QStringLiteral("请填写服务器地址"), true); return; } setBusy(true); setStatus(QStringLiteral("正在连接 %1 …").arg(QString::fromStdString(cfg.server))); if (cfg.remember) { saveConfig(cfg); } if (connectHandler_) { connectHandler_(cfg); } emit connectRequested(cfg); } } // namespace vdi