Files
HIS-GUI/gui/mainwindow.cpp
2026-04-07 21:30:39 +08:00

317 lines
10 KiB
C++

#include "mainwindow.h"
#include <QAction>
#include <QComboBox>
#include <QCoreApplication>
#include <QDialog>
#include <QDir>
#include <QFileDialog>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMenuBar>
#include <QMessageBox>
#include <QPushButton>
#include <QSpinBox>
#include <QStackedWidget>
#include <QTabWidget>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QTextEdit>
#include <QToolBar>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextCursor>
#include <QFont>
#include <vector>
#include <ctime>
#include <fstream>
#include <sstream>
#include <iomanip>
#include "core/his_core.h"
#include "core/ward_service.h"
#include "core/patient_service.h"
#include "core/doctor_service.h"
#include "core/medicine_service.h"
#include "core/check_service.h"
#include "utils/file_manager.h"
#include "utils/logger.h"
#include "utils/input_validator.h"
#include "dialogs/patient_dialog.h"
#include "dialogs/department_detail_dialog.h"
#include "dialogs/payment_dialog.h"
#include "dialogs/payment_management_dialog.h"
#include "dialogs/settlement_dialog.h"
// ============ 全局常量定义 ============
const int MAX_MEDICINE_STOCK = 10000;
// ============ 工具函数 ============
static QString wardTypeToText(WardType t) {
switch (t) {
case WardType::Normal:
return QObject::tr("普通病房");
case WardType::Special:
return QObject::tr("特需病房");
case WardType::ICU:
return QObject::tr("ICU");
}
return QObject::tr("未知");
}
static WardType parseWardType(const QString& s) {
if (s.contains("ICU", Qt::CaseInsensitive)) return WardType::ICU;
if (s.contains("特需", Qt::CaseInsensitive)) return WardType::Special;
return WardType::Normal;
}
static QString patientStatusToText(PatientStatus st) {
switch (st) {
case PatientStatus::Unregistered:
return QObject::tr("未挂号");
case PatientStatus::Outpatient:
return QObject::tr("门诊");
case PatientStatus::Emergency:
return QObject::tr("急诊");
case PatientStatus::Visited:
return QObject::tr("已就诊");
case PatientStatus::Inpatient:
return QObject::tr("住院");
case PatientStatus::Discharged:
return QObject::tr("出院");
}
return QObject::tr("未挂号");
}
static PatientStatus parsePatientStatus(const QString& s) {
if (s.contains("Unregistered", Qt::CaseInsensitive)) return PatientStatus::Unregistered;
if (s.contains("Emergency", Qt::CaseInsensitive)) return PatientStatus::Emergency;
if (s.contains("Inpatient", Qt::CaseInsensitive)) return PatientStatus::Inpatient;
if (s.contains("Discharged", Qt::CaseInsensitive)) return PatientStatus::Discharged;
if (s.contains("Visited", Qt::CaseInsensitive)) return PatientStatus::Visited;
if (s.contains("Outpatient", Qt::CaseInsensitive)) return PatientStatus::Outpatient;
return PatientStatus::Unregistered;
}
static QString doctorTitleToText(DoctorTitle t) {
switch (t) {
case DoctorTitle::Chief:
return QObject::tr("主任医师");
case DoctorTitle::AssociateChief:
return QObject::tr("副主任医师");
case DoctorTitle::Attending:
return QObject::tr("主治医师");
case DoctorTitle::Resident:
return QObject::tr("住院医师");
}
return QObject::tr("未知");
}
static DoctorTitle parseDoctorTitle(const QString& s) {
if (s.contains("主任", Qt::CaseInsensitive)) return DoctorTitle::Chief;
if (s.contains("副主任", Qt::CaseInsensitive)) return DoctorTitle::AssociateChief;
if (s.contains("主治", Qt::CaseInsensitive)) return DoctorTitle::Attending;
return DoctorTitle::Resident;
}
static QString formatDate(time_t t) {
if (t == 0) return "-";
struct tm timeinfo;
localtime_r(&t, &timeinfo);
char buffer[30];
strftime(buffer, 30, "%Y-%m-%d %H:%M", &timeinfo);
return QString(buffer);
}
static QString formatCurrentDate() {
time_t now = time(nullptr);
struct tm timeinfo;
localtime_r(&now, &timeinfo);
char buffer[30];
strftime(buffer, 30, "%Y-%m-%d %H:%M", &timeinfo);
return QString(buffer);
}
static std::vector<Ward> collectExistingWardsFromCore(core::HisCore& core) {
std::vector<Ward> wards;
core.ctx_.wards.for_each([&wards](const std::string&, const Ward& w) { wards.push_back(w); });
return wards;
}
static std::vector<std::string> collectExistingPatientIdsFromCore(core::HisCore& core) {
std::vector<std::string> ids;
core.ctx_.patients.for_each([&ids](const std::string& k, const Patient&) { ids.push_back(k); });
return ids;
}
static std::vector<std::string> collectExistingDoctorIdsFromCore(core::HisCore& core) {
std::vector<std::string> ids;
core.ctx_.doctors.for_each([&ids](const std::string& k, const Doctor&) { ids.push_back(k); });
return ids;
}
static std::vector<std::string> collectExistingMedicineIdsFromCore(core::HisCore& core) {
std::vector<std::string> ids;
core.ctx_.medicines.for_each([&ids](const std::string& k, const Medicine&) { ids.push_back(k); });
return ids;
}
static QString safeCurrentTableId(QTableWidget* table) {
if (table->currentRow() < 0) return QString();
QTableWidgetItem* item = table->item(table->currentRow(), 0);
return item ? item->text() : QString();
}
// ============ 构造函数 / 析构函数 / UI 初始化 ============
MainWindow::MainWindow(core::HisCore& core, QWidget* parent)
: QMainWindow(parent), core_(core), logger_("", true) {
QDir logsDir(QCoreApplication::applicationDirPath() + "/logs");
if (!logsDir.exists()) {
logsDir.mkpath(".");
}
logger_.setLogFilePath((logsDir.path() + "/his_operation.log").toStdString());
logger_.log(LogEntryType::SYSTEM_EVENT, "SYSTEM_START", "HIS GUI系统启动");
setWindowIcon(QIcon("gui/favicon/favicon.ico"));
setupUI();
if (!loadDataFromFiles()) {
QMessageBox::warning(this, tr("数据加载失败"), tr("请检查 data 目录是否存在并包含 wards.txt/patients.txt/doctors.txt/medicines.txt。"));
}
refreshDashboard();
refreshWardTable();
refreshPatientTable();
refreshDoctorTable();
refreshMedicineTable();
refreshCheckTable();
refreshDepartmentTable();
refreshLogDisplay();
}
MainWindow::~MainWindow() = default;
void MainWindow::closeEvent(QCloseEvent* event) {
QMessageBox::StandardButton reply = QMessageBox::question(
this,
tr("保存数据"),
tr("是否保存当前数据到文件?"),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
QMessageBox::Save
);
if (reply == QMessageBox::Save) {
handleSaveData();
event->accept();
} else if (reply == QMessageBox::Discard) {
event->accept();
} else {
event->ignore();
}
}
void MainWindow::setupUI() {
currentRole_ = ViewRole::Admin;
navList_ = new QListWidget(this);
navList_->setMinimumWidth(180);
navList_->setMaximumWidth(220);
navList_->addItem(tr("仪表盘"));
navList_->addItem(tr("病房"));
navList_->addItem(tr("患者"));
navList_->addItem(tr("医生"));
navList_->addItem(tr("药房"));
navList_->addItem(tr("检查"));
navList_->addItem(tr("科室"));
navList_->addItem(tr("记录"));
navList_->setCurrentRow(0);
navList_->setSpacing(4);
navList_->setStyleSheet("QListWidget { padding: 10px 0; } QListWidget::item { padding: 12px 20px; margin: 2px 8px; }");
contentStack_ = new QStackedWidget(this);
setupDashboardTab();
setupWardsTab();
setupPatientsTab();
setupDoctorsTab();
setupMedicinesTab();
setupChecksTab();
setupDepartmentsTab();
setupLogsTab();
contentStack_->addWidget(dashboardTab_);
contentStack_->addWidget(wardsTab_);
contentStack_->addWidget(patientsTab_);
contentStack_->addWidget(doctorsTab_);
contentStack_->addWidget(medicinesTab_);
contentStack_->addWidget(checksTab_);
contentStack_->addWidget(departmentsTab_);
contentStack_->addWidget(logsTab_);
auto* mainLayout = new QHBoxLayout();
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addWidget(navList_, 1);
mainLayout->addWidget(contentStack_, 5);
auto* central = new QWidget(this);
central->setLayout(mainLayout);
setCentralWidget(central);
connect(navList_, &QListWidget::currentRowChanged, contentStack_, &QStackedWidget::setCurrentIndex);
auto* reloadAction = new QAction(tr("重新载入"), this);
reloadAction->setShortcut(Qt::Key_F5);
connect(reloadAction, &QAction::triggered, this, &MainWindow::handleReloadAction);
menuBar()->addAction(reloadAction);
auto* saveAction = new QAction(tr("保存回文件"), this);
saveAction->setShortcut(Qt::CTRL | Qt::Key_S);
connect(saveAction, &QAction::triggered, this, &MainWindow::handleSaveData);
menuBar()->addAction(saveAction);
auto* toolbar = addToolBar(tr("工具"));
toolbar->addSeparator();
auto* roleLabel = new QLabel(tr(" 视角: "), this);
toolbar->addWidget(roleLabel);
roleComboBox_ = new QComboBox(this);
roleComboBox_->addItem(tr("管理视角"), static_cast<int>(ViewRole::Admin));
roleComboBox_->addItem(tr("病人视角"), static_cast<int>(ViewRole::Patient));
roleComboBox_->addItem(tr("医护视角"), static_cast<int>(ViewRole::Medical));
connect(roleComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onRoleChanged);
toolbar->addWidget(roleComboBox_);
QPushButton* paymentMgmtButton = new QPushButton(tr("支付管理"), this);
connect(paymentMgmtButton, &QPushButton::clicked, [this]() {
PaymentManagementDialog dialog(core_, this);
dialog.exec();
});
toolbar->addWidget(paymentMgmtButton);
setWindowTitle(tr("HIS GUI") + " - " + tr("医院信息系统"));
resize(1200, 720);
}
// ============ 包含各页面实现文件 ============
#include "pages/dashboard_page.cpp"
#include "pages/wards_page.cpp"
#include "pages/patients_page.cpp"
#include "pages/doctors_page.cpp"
#include "pages/medicines_page.cpp"
#include "pages/checks_page.cpp"
#include "pages/departments_page.cpp"
#include "pages/logs_page.cpp"
#include "pages/role_page.cpp"