#include "patient_dialog.h" #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/department_service.h" #include "core/patient_case_service.h" #include "models/patient.h" #include "models/ward.h" #include "models/medicine.h" #include "models/doctor.h" #include "models/patient_case.h" #include "utils/input_validator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ========== 工具函数 ========== 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 QString formatDate(time_t t) { if (t == 0) return QObject::tr("-"); struct tm timeinfo; localtime_r(&t, &timeinfo); char buffer[30]; strftime(buffer, 30, "%Y-%m-%d %H:%M", &timeinfo); return QString(buffer); } 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 PatientStatus parsePatientStatus(const QString& s) { if (s.contains("住院", Qt::CaseInsensitive)) return PatientStatus::Inpatient; if (s.contains("出院", Qt::CaseInsensitive)) return PatientStatus::Discharged; if (s.contains("已就诊", Qt::CaseInsensitive)) return PatientStatus::Visited; return PatientStatus::Outpatient; } 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 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 std::vector collectExistingWards(core::HisCore& core) { std::vector wards; core.ctx_.wards.for_each([&wards](const std::string&, const Ward& w) { wards.push_back(w); }); return wards; } static std::vector collectExistingPatientIds(core::HisCore& core) { std::vector ids; core.ctx_.patients.for_each([&ids](const std::string& k, const Patient&) { ids.push_back(k); }); return ids; } static std::vector collectExistingDoctorIds(core::HisCore& core) { std::vector ids; core.ctx_.doctors.for_each([&ids](const std::string& k, const Doctor&) { ids.push_back(k); }); return ids; } static std::vector collectExistingMedicineIds(core::HisCore& core) { std::vector ids; core.ctx_.medicines.for_each([&ids](const std::string& k, const Medicine&) { ids.push_back(k); }); return ids; } static std::vector collectExistingDepartments(core::HisCore& core) { std::set depts; core.ctx_.wards.for_each([&depts](const std::string&, const Ward& w) { depts.insert(w.DepartmentID); }); return std::vector(depts.begin(), depts.end()); } // ========== PatientAddDialog ========== PatientAddDialog::PatientAddDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId), isEditMode_(!patientId.isEmpty()) { setupUI(); if (isEditMode_) { loadPatientData(); } } PatientAddDialog::~PatientAddDialog() {} void PatientAddDialog::setupUI() { setWindowTitle(isEditMode_ ? tr("编辑患者信息") : tr("添加患者")); setModal(true); resize(400, 300); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); formLayout_ = new QFormLayout(); formLayout_->setSpacing(10); formLayout_->setContentsMargins(20, 20, 20, 20); if (isEditMode_) { idLabel_ = new QLabel(patientId_, this); idLabel_->setEnabled(false); formLayout_->addRow(tr("患者ID:"), idLabel_); } else { QString newPatientId = QString::fromStdString(Patient::generateUniqueId()); idLabel_ = new QLabel(newPatientId, this); idLabel_->setEnabled(false); formLayout_->addRow(tr("患者ID (自动生成):"), idLabel_); } nameEdit_ = new QLineEdit(this); nameEdit_->setPlaceholderText(tr("请输入姓名")); input_validator::setMaxLength(nameEdit_); // 限制最大255字符 formLayout_->addRow(tr("姓名:"), nameEdit_); ageSpin_ = new QSpinBox(this); ageSpin_->setRange(0, 200); ageSpin_->setValue(0); formLayout_->addRow(tr("年龄:"), ageSpin_); genderCombo_ = new QComboBox(this); genderCombo_->addItems({tr("男"), tr("女"), tr("其他")}); formLayout_->addRow(tr("性别:"), genderCombo_); contactEdit_ = new QLineEdit(this); contactEdit_->setPlaceholderText(tr("请输入联系方式")); input_validator::setMaxLength(contactEdit_); // 限制最大255字符 formLayout_->addRow(tr("联系方式:"), contactEdit_); if (isEditMode_) { statusCombo_ = new QComboBox(this); statusCombo_->addItems({tr("未挂号"), tr("门诊"), tr("急诊"), tr("已就诊"), tr("住院"), tr("出院")}); formLayout_->addRow(tr("状态:"), statusCombo_); } buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout_->addRow(buttonBox_); mainLayout->addLayout(formLayout_); } void PatientAddDialog::loadPatientData() { auto* p = core_.patientService.findPatient(patientId_.toStdString()); if (!p) return; nameEdit_->setText(QString::fromStdString(p->Name)); ageSpin_->setValue(p->Age); int genderIdx = (p->Gender == QObject::tr("女").toStdString()) ? 1 : (p->Gender == QObject::tr("其他").toStdString()) ? 2 : 0; genderCombo_->setCurrentIndex(genderIdx); contactEdit_->setText(QString::fromStdString(p->Contact)); if (statusCombo_) { int statusIdx = 0; if (p->Status == PatientStatus::Visited) statusIdx = 1; else if (p->Status == PatientStatus::Inpatient) statusIdx = 2; else if (p->Status == PatientStatus::Discharged) statusIdx = 3; statusCombo_->setCurrentIndex(statusIdx); } } QString PatientAddDialog::getName() const { return nameEdit_->text(); } int PatientAddDialog::getAge() const { return ageSpin_->value(); } QString PatientAddDialog::getGender() const { return genderCombo_->currentText(); } QString PatientAddDialog::getContact() const { return contactEdit_->text(); } QString PatientAddDialog::getStatus() const { if (statusCombo_) return statusCombo_->currentText(); return tr("门诊"); } // ========== PatientAdmitDialog ========== PatientAdmitDialog::PatientAdmitDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId) { setupUI(); } PatientAdmitDialog::~PatientAdmitDialog() {} void PatientAdmitDialog::setupUI() { setWindowTitle(tr("入院 - %1").arg(patientId_)); setModal(true); resize(500, 400); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); auto* formLayout = new QFormLayout(); formLayout->setSpacing(10); formLayout->setContentsMargins(20, 20, 20, 20); // 自动显示入院日期 dateLabel_ = new QLabel(formatCurrentDate(), this); dateLabel_->setEnabled(false); formLayout->addRow(tr("入院日期:"), dateLabel_); // 病房选择 wardCombo_ = new QComboBox(this); wardCombo_->addItem(tr("-- 请选择病房 --")); core_.wardService.for_eachWard([this](const std::string&, const Ward& ward) { QString display = QString("%1 (%2) - 空床: %3/%4") .arg(QString::fromStdString(ward.WardID)) .arg(QString::fromStdString(ward.DepartmentID)) .arg(ward.freeBedCount()) .arg(ward.MaxBeds); wardCombo_->addItem(display, QString::fromStdString(ward.WardID)); }); formLayout->addRow(tr("选择病房:"), wardCombo_); // 床位选择(联动病房) bedCombo_ = new QComboBox(this); bedCombo_->addItem(tr("-- 先选择病房 --")); formLayout->addRow(tr("选择床位:"), bedCombo_); // 入院原因 reasonEdit_ = new QTextEdit(this); reasonEdit_->setMaximumHeight(80); reasonEdit_->setPlaceholderText(tr("请输入入院原因")); input_validator::installTextEditValidator(reasonEdit_); // 限制最大255字符 formLayout->addRow(tr("入院原因:"), reasonEdit_); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout->addRow(buttonBox_); mainLayout->addLayout(formLayout); // 联动床位选择 connect(wardCombo_, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { bedCombo_->clear(); if (index == 0) { bedCombo_->addItem(tr("-- 先选择病房 --")); return; } QString wardId = wardCombo_->currentData().toString(); auto* ward = core_.wardService.findWard(wardId.toStdString()); if (!ward) return; bedCombo_->addItem(tr("-- 请选择床位 --")); for (const auto& bed : ward->Beds) { if (bed.Status == BedStatus::Free) { bedCombo_->addItem(QString::fromStdString(bed.BedID), QString::fromStdString(bed.BedID)); } } }); } QString PatientAdmitDialog::getWardId() const { return wardCombo_->currentData().toString(); } QString PatientAdmitDialog::getBedId() const { return bedCombo_->currentData().toString(); } QString PatientAdmitDialog::getReason() const { return reasonEdit_->toPlainText(); } // ========== DiagnosisAddDialog ========== DiagnosisAddDialog::DiagnosisAddDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId) { setupUI(); } DiagnosisAddDialog::~DiagnosisAddDialog() {} void DiagnosisAddDialog::setupUI() { setWindowTitle(tr("添加诊断记录 - %1").arg(patientId_)); setModal(true); resize(500, 450); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); auto* formLayout = new QFormLayout(); formLayout->setSpacing(10); formLayout->setContentsMargins(20, 20, 20, 20); // 医生选择(从已有医生中选择) doctorCombo_ = new QComboBox(this); doctorCombo_->addItem(tr("-- 请选择医生 --")); core_.doctorService.for_eachDoctor([this](const std::string&, const Doctor& d) { QString display = QString("%1 (%2) - %3") .arg(QString::fromStdString(d.DoctorID)) .arg(QString::fromStdString(d.Name)) .arg(doctorTitleToText(d.Title)); doctorCombo_->addItem(display, QString::fromStdString(d.DoctorID)); }); formLayout->addRow(tr("选择医生:"), doctorCombo_); // 自动显示诊断日期 dateLabel_ = new QLabel(formatCurrentDate(), this); dateLabel_->setEnabled(false); formLayout->addRow(tr("诊断日期:"), dateLabel_); diagnosisEdit_ = new QTextEdit(this); diagnosisEdit_->setMinimumHeight(60); diagnosisEdit_->setPlaceholderText(tr("请输入诊断内容")); input_validator::installTextEditValidator(diagnosisEdit_); // 限制最大255字符 formLayout->addRow(tr("诊断:"), diagnosisEdit_); prescriptionEdit_ = new QTextEdit(this); prescriptionEdit_->setMinimumHeight(60); prescriptionEdit_->setPlaceholderText(tr("请输入处方")); input_validator::installTextEditValidator(prescriptionEdit_); // 限制最大255字符 formLayout->addRow(tr("处方:"), prescriptionEdit_); remarksEdit_ = new QLineEdit(this); remarksEdit_->setPlaceholderText(tr("备注(可选)")); input_validator::setMaxLength(remarksEdit_); // 限制最大255字符 formLayout->addRow(tr("备注:"), remarksEdit_); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout->addRow(buttonBox_); mainLayout->addLayout(formLayout); } QString DiagnosisAddDialog::getDoctorId() const { return doctorCombo_->currentData().toString(); } QString DiagnosisAddDialog::getDiagnosis() const { return diagnosisEdit_->toPlainText(); } QString DiagnosisAddDialog::getPrescription() const { return prescriptionEdit_->toPlainText(); } QString DiagnosisAddDialog::getRemarks() const { return remarksEdit_->text(); } // ========== MedicineManageDialog ========== MedicineManageDialog::MedicineManageDialog(core::HisCore& core, const QString& medicineId, QWidget* parent) : QDialog(parent), core_(core), medicineId_(medicineId), isEditMode_(!medicineId.isEmpty()) { setupUI(); if (isEditMode_) { loadMedicineData(); } } MedicineManageDialog::~MedicineManageDialog() {} void MedicineManageDialog::setupUI() { setWindowTitle(isEditMode_ ? tr("更新药品信息") : tr("添加药品")); setModal(true); resize(400, 350); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); auto* formLayout = new QFormLayout(); formLayout->setSpacing(10); formLayout->setContentsMargins(20, 20, 20, 20); if (isEditMode_) { idLabel_ = new QLabel(medicineId_, this); idLabel_->setEnabled(false); formLayout->addRow(tr("药品ID:"), idLabel_); } else { QString newMedId = QString::fromStdString(Medicine::generateUniqueId()); idLabel_ = new QLabel(newMedId, this); idLabel_->setEnabled(false); formLayout->addRow(tr("药品ID (自动生成):"), idLabel_); } genericEdit_ = new QLineEdit(this); genericEdit_->setPlaceholderText(tr("请输入通用名")); input_validator::setMaxLength(genericEdit_); // 限制最大255字符 formLayout->addRow(tr("通用名:"), genericEdit_); brandEdit_ = new QLineEdit(this); brandEdit_->setPlaceholderText(tr("请输入商品名")); input_validator::setMaxLength(brandEdit_); // 限制最大255字符 formLayout->addRow(tr("商品名:"), brandEdit_); deptEdit_ = new QComboBox(this); deptEdit_->addItem(tr("-- 请选择科室 --")); std::set departments; core_.wardService.for_eachWard([&departments](const std::string&, const Ward& w) { departments.insert(w.DepartmentID); }); for (const auto& dept : departments) { deptEdit_->addItem(QString::fromStdString(dept), QString::fromStdString(dept)); } formLayout->addRow(tr("所属科室:"), deptEdit_); stockSpin_ = new QSpinBox(this); stockSpin_->setRange(0, input_validator::MAX_INT_VALUE); // 使用int最大值限制 stockSpin_->setValue(0); formLayout->addRow(tr("库存:"), stockSpin_); priceSpin_ = new QDoubleSpinBox(this); priceSpin_->setRange(0.0, input_validator::MAX_DOUBLE_VALUE); // 使用double最大值 priceSpin_->setDecimals(2); priceSpin_->setValue(0.0); formLayout->addRow(tr("单价:"), priceSpin_); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout->addRow(buttonBox_); mainLayout->addLayout(formLayout); } void MedicineManageDialog::loadMedicineData() { auto* m = core_.medicineService.findMedicine(medicineId_.toStdString()); if (!m) return; genericEdit_->setText(QString::fromStdString(m->GenericName)); brandEdit_->setText(QString::fromStdString(m->BrandName)); int deptIdx = deptEdit_->findData(QString::fromStdString(m->DepartmentID)); if (deptIdx != -1) { deptEdit_->setCurrentIndex(deptIdx); } stockSpin_->setValue(m->StockQuantity); priceSpin_->setValue(m->UnitPrice); } QString MedicineManageDialog::getGenericName() const { return genericEdit_->text(); } QString MedicineManageDialog::getBrandName() const { return brandEdit_->text(); } QString MedicineManageDialog::getDepartment() const { return deptEdit_->currentText(); } int MedicineManageDialog::getStock() const { return stockSpin_->value(); } double MedicineManageDialog::getPrice() const { return priceSpin_->value(); } bool MedicineManageDialog::isEditMode() const { return isEditMode_; } // ========== WardBedManageDialog ========== WardBedManageDialog::WardBedManageDialog(core::HisCore& core, QWidget* parent) : QDialog(parent), core_(core) { setupUI(); refreshWardTable(); } WardBedManageDialog::~WardBedManageDialog() {} void WardBedManageDialog::setupUI() { setWindowTitle(tr("病房床位管理")); setModal(true); resize(1000, 700); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); tabWidget_ = new QTabWidget(this); // ===== Tab 1: 添加病房 ===== auto* addWardTab = new QWidget(); auto* addWardLayout = new QVBoxLayout(addWardTab); auto* addWardForm = new QFormLayout(); QString newWardId = QString::fromStdString(Ward::generateUniqueId()); wardIdDisplay_ = new QLabel(newWardId, addWardTab); wardIdDisplay_->setEnabled(false); addWardForm->addRow(tr("病房ID (自动生成):"), wardIdDisplay_); wardDeptEdit_ = new QComboBox(addWardTab); wardDeptEdit_->addItem(tr("-- 请选择科室 --")); core_.wardService.for_eachWard([this](const std::string&, const Ward& w) { wardDeptEdit_->addItem(QString::fromStdString(w.DepartmentID), QString::fromStdString(w.DepartmentID)); }); addWardForm->addRow(tr("科室:"), wardDeptEdit_); wardTypeCombo_ = new QComboBox(addWardTab); wardTypeCombo_->addItems({tr("普通病房"), tr("特需病房"), tr("ICU")}); addWardForm->addRow(tr("病房类型:"), wardTypeCombo_); wardMaxBedsSpin_ = new QSpinBox(addWardTab); wardMaxBedsSpin_->setRange(1, 1000); wardMaxBedsSpin_->setValue(10); addWardForm->addRow(tr("最大床位数:"), wardMaxBedsSpin_); auto* addWardBtn = new QPushButton(tr("添加病房"), addWardTab); connect(addWardBtn, &QPushButton::clicked, this, &WardBedManageDialog::addWard); addWardForm->addRow(addWardBtn); addWardLayout->addLayout(addWardForm); addWardLayout->addStretch(); tabWidget_->addTab(addWardTab, tr("添加病房")); // ===== Tab 2: 添加床位 ===== auto* addBedTab = new QWidget(); auto* addBedLayout = new QVBoxLayout(addBedTab); auto* addBedForm = new QFormLayout(); bedWardCombo_ = new QComboBox(addBedTab); bedWardCombo_->addItem(tr("-- 请选择病房 --")); core_.wardService.for_eachWard([this](const std::string&, const Ward& ward) { QString display = QString("%1 (%2) - 空床: %3/%4") .arg(QString::fromStdString(ward.WardID)) .arg(QString::fromStdString(ward.DepartmentID)) .arg(ward.freeBedCount()) .arg(ward.MaxBeds); bedWardCombo_->addItem(display, QString::fromStdString(ward.WardID)); }); addBedForm->addRow(tr("选择病房:"), bedWardCombo_); bedIdDisplay_ = new QLabel(tr("请先选择病房"), addBedTab); bedIdDisplay_->setEnabled(false); addBedForm->addRow(tr("床位ID (自动生成):"), bedIdDisplay_); auto* addBedBtn = new QPushButton(tr("添加床位"), addBedTab); connect(addBedBtn, &QPushButton::clicked, this, &WardBedManageDialog::addBed); addBedForm->addRow(addBedBtn); connect(bedWardCombo_, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { if (index == 0) { bedIdDisplay_->setText(tr("请先选择病房")); return; } QString wardId = bedWardCombo_->currentData().toString(); auto* ward = core_.wardService.findWard(wardId.toStdString()); if (!ward) return; QString newBedId = QString::fromStdString(Bed::generateUniqueId()); bedIdDisplay_->setText(newBedId); }); addBedLayout->addLayout(addBedForm); addBedLayout->addStretch(); tabWidget_->addTab(addBedTab, tr("添加床位")); // ===== Tab 3: 删除床位 ===== auto* deleteBedTab = new QWidget(); auto* deleteBedLayout = new QVBoxLayout(deleteBedTab); auto* deleteBedForm = new QFormLayout(); deleteBedWardCombo_ = new QComboBox(deleteBedTab); deleteBedWardCombo_->addItem(tr("-- 请选择病房 --")); core_.wardService.for_eachWard([this](const std::string&, const Ward& ward) { QString display = QString("%1 (%2)") .arg(QString::fromStdString(ward.WardID)) .arg(QString::fromStdString(ward.DepartmentID)); deleteBedWardCombo_->addItem(display, QString::fromStdString(ward.WardID)); }); deleteBedForm->addRow(tr("选择病房:"), deleteBedWardCombo_); deleteBedCombo_ = new QComboBox(deleteBedTab); deleteBedCombo_->addItem(tr("-- 选择床位 --")); deleteBedForm->addRow(tr("选择床位:"), deleteBedCombo_); applyDeleteBedBtn_ = new QPushButton(tr("删除床位"), deleteBedTab); connect(applyDeleteBedBtn_, &QPushButton::clicked, this, &WardBedManageDialog::deleteBed); deleteBedForm->addRow(applyDeleteBedBtn_); connect(deleteBedWardCombo_, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { deleteBedCombo_->clear(); if (index == 0) { deleteBedCombo_->addItem(tr("-- 选择床位 --")); return; } QString wardId = deleteBedWardCombo_->currentData().toString(); auto* ward = core_.wardService.findWard(wardId.toStdString()); if (!ward) return; for (const auto& bed : ward->Beds) { QString status = (bed.Status == BedStatus::Occupied) ? tr("%1 [已占用 - 患者: %2]").arg(QString::fromStdString(bed.BedID)).arg(QString::fromStdString(bed.PatientID)) : tr("%1 [空闲]").arg(QString::fromStdString(bed.BedID)); deleteBedCombo_->addItem(status, QString::fromStdString(bed.BedID)); } }); deleteBedLayout->addLayout(deleteBedForm); deleteBedLayout->addStretch(); tabWidget_->addTab(deleteBedTab, tr("删除床位")); // ===== Tab 4: 病房列表 ===== auto* listTab = new QWidget(); auto* listLayout = new QVBoxLayout(listTab); wardTable_ = new QTableWidget(0, 6, listTab); wardTable_->setHorizontalHeaderLabels({tr("病房ID"), tr("科室"), tr("类型"), tr("最大床位"), tr("空闲床位"), tr("占用率")}); wardTable_->setSelectionBehavior(QAbstractItemView::SelectRows); wardTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); wardTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); listLayout->addWidget(wardTable_); tabWidget_->addTab(listTab, tr("病房列表")); mainLayout->addWidget(tabWidget_); auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); mainLayout->addWidget(buttonBox); } void WardBedManageDialog::refreshWardTable() { wardTable_->setRowCount(0); int row = 0; core_.wardService.for_eachWard([this, &row](const std::string&, const Ward& ward) { wardTable_->insertRow(row); wardTable_->setItem(row, 0, new QTableWidgetItem(QString::fromStdString(ward.WardID))); wardTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(ward.DepartmentID))); wardTable_->setItem(row, 2, new QTableWidgetItem(wardTypeToText(ward.Type))); wardTable_->setItem(row, 3, new QTableWidgetItem(QString::number(ward.MaxBeds))); wardTable_->setItem(row, 4, new QTableWidgetItem(QString::number(ward.freeBedCount()))); wardTable_->setItem(row, 5, new QTableWidgetItem(QString("%1%").arg(int(ward.occupancyRate() * 100)))); row++; }); } void WardBedManageDialog::addWard() { QString newWardId = wardIdDisplay_->text(); QString dept = wardDeptEdit_->currentText(); if (dept.isEmpty() || dept == tr("-- 请选择科室 --")) { QMessageBox::warning(this, tr("错误"), tr("请选择科室")); return; } WardType type = parseWardType(wardTypeCombo_->currentText()); int maxBeds = wardMaxBedsSpin_->value(); Ward ward(newWardId.toStdString(), dept.toStdString(), type, maxBeds); if (!core_.wardService.addWard(ward)) { QMessageBox::warning(this, tr("添加失败"), tr("病房ID已存在")); return; } QMessageBox::information(this, tr("成功"), tr("已添加病房: %1").arg(newWardId)); // 重置表单 QString nextWardId = QString::fromStdString(Ward::generateUniqueId()); wardIdDisplay_->setText(nextWardId); wardDeptEdit_->setCurrentIndex(0); wardTypeCombo_->setCurrentIndex(0); wardMaxBedsSpin_->setValue(10); // 刷新病房下拉列表 bedWardCombo_->clear(); bedWardCombo_->addItem(tr("-- 请选择病房 --")); deleteBedWardCombo_->clear(); deleteBedWardCombo_->addItem(tr("-- 请选择病房 --")); core_.wardService.for_eachWard([this](const std::string&, const Ward& ward) { QString display = QString("%1 (%2) - 空床: %3/%4") .arg(QString::fromStdString(ward.WardID)) .arg(QString::fromStdString(ward.DepartmentID)) .arg(ward.freeBedCount()) .arg(ward.MaxBeds); bedWardCombo_->addItem(display, QString::fromStdString(ward.WardID)); deleteBedWardCombo_->addItem(QString("%1 (%2)").arg(QString::fromStdString(ward.WardID)).arg(QString::fromStdString(ward.DepartmentID)), QString::fromStdString(ward.WardID)); }); refreshWardTable(); } void WardBedManageDialog::addBed() { if (bedWardCombo_->currentIndex() == 0) { QMessageBox::warning(this, tr("错误"), tr("请选择病房")); return; } QString wardId = bedWardCombo_->currentData().toString(); QString bedId = bedIdDisplay_->text(); if (bedId.isEmpty() || bedId == tr("请先选择病房")) { QMessageBox::warning(this, tr("错误"), tr("无法生成床位ID")); return; } if (!core_.wardService.addBed(wardId.toStdString(), bedId.toStdString())) { QMessageBox::warning(this, tr("添加失败"), tr("床位已存在")); return; } QMessageBox::information(this, tr("成功"), tr("已添加床位: %1 到病房: %2").arg(bedId).arg(wardId)); // 更新床位ID auto* ward = core_.wardService.findWard(wardId.toStdString()); if (ward) { QString newBedId = QString::fromStdString(Bed::generateUniqueId()); bedIdDisplay_->setText(newBedId); } refreshWardTable(); } void WardBedManageDialog::deleteBed() { if (deleteBedWardCombo_->currentIndex() == 0 || deleteBedCombo_->currentIndex() == 0) { QMessageBox::warning(this, tr("错误"), tr("请选择病房和床位")); return; } QString wardId = deleteBedWardCombo_->currentData().toString(); QString bedId = deleteBedCombo_->currentData().toString(); // 获取当前床位信息 auto* ward = core_.wardService.findWard(wardId.toStdString()); if (!ward) { QMessageBox::warning(this, tr("错误"), tr("病房不存在")); return; } Bed* bed = nullptr; for (auto& b : ward->Beds) { if (b.BedID == bedId.toStdString()) { bed = &b; break; } } if (!bed) { QMessageBox::warning(this, tr("错误"), tr("床位不存在")); return; } // 如果床位有患者,自动释放 if (bed->Status == BedStatus::Occupied && !bed->PatientID.empty()) { std::string patientId = bed->PatientID; if (!core_.patientService.releaseBed(wardId.toStdString(), bedId.toStdString())) { QMessageBox::warning(this, tr("删除失败"), tr("床位释放失败")); return; } // 更新病例记录 auto* pc = core_.patientCaseService.getCase(patientId); if (pc) { for (auto& ar : pc->AdmissionRecords) { if (ar.isCurrentlyAdmitted() && ar.BedID == bedId.toStdString()) { ar.DischargeTime = time(nullptr); ar.DischargeSummary = "自动释放床位"; break; } } } QMessageBox::information(this, tr("成功"), tr("已删除床位: %1,患者 %2 已自动释放床位").arg(bedId).arg(QString::fromStdString(patientId))); } else { if (!core_.wardService.removeBed(wardId.toStdString(), bedId.toStdString())) { QMessageBox::warning(this, tr("删除失败"), tr("无法删除床位")); return; } QMessageBox::information(this, tr("成功"), tr("已删除床位: %1").arg(bedId)); } // 刷新床位列表 deleteBedCombo_->clear(); deleteBedCombo_->addItem(tr("-- 选择床位 --")); ward = core_.wardService.findWard(wardId.toStdString()); if (ward) { for (const auto& b : ward->Beds) { QString status = (b.Status == BedStatus::Occupied) ? tr("%1 [已占用 - 患者: %2]").arg(QString::fromStdString(b.BedID)).arg(QString::fromStdString(b.PatientID)) : tr("%1 [空闲]").arg(QString::fromStdString(b.BedID)); deleteBedCombo_->addItem(status, QString::fromStdString(b.BedID)); } } refreshWardTable(); } // ========== WardOccupancyDialog ========== WardOccupancyDialog::WardOccupancyDialog(core::HisCore& core, QWidget* parent) : QDialog(parent), core_(core) { setupUI(); } WardOccupancyDialog::~WardOccupancyDialog() {} void WardOccupancyDialog::setupUI() { setWindowTitle(tr("病房使用情况")); setModal(true); resize(900, 600); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); // 筛选条件 auto* filterGroup = new QGroupBox(tr("筛选条件"), this); auto* filterLayout = new QFormLayout(filterGroup); wardTypeFilter_ = new QComboBox(filterGroup); wardTypeFilter_->addItems({tr("全部"), tr("普通病房"), tr("特需病房"), tr("ICU")}); gradeFilter_ = new QComboBox(filterGroup); gradeFilter_->addItems({tr("全部"), tr("高占用率 (>=80%)"), tr("中占用率 (40-80%)"), tr("低占用率 (<40%)")}); auto* applyBtn = new QPushButton(tr("应用筛选"), filterGroup); connect(applyBtn, &QPushButton::clicked, this, &WardOccupancyDialog::refreshData); filterLayout->addRow(tr("病房类型:"), wardTypeFilter_); filterLayout->addRow(tr("占用率筛选:"), gradeFilter_); filterLayout->addWidget(applyBtn); mainLayout->addWidget(filterGroup); // 汇总表 summaryTable_ = new QTableWidget(this); summaryTable_->setColumnCount(7); summaryTable_->setHorizontalHeaderLabels({tr("病房ID"), tr("科室"), tr("类型"), tr("总床位"), tr("已占用"), tr("空闲"), tr("占用率")}); summaryTable_->setSelectionBehavior(QAbstractItemView::SelectRows); summaryTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); summaryTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); mainLayout->addWidget(summaryTable_); // 床位详情 auto* detailGroup = new QGroupBox(tr("床位详情(点击病房查看)"), this); auto* detailLayout = new QVBoxLayout(detailGroup); bedDetailTable_ = new QTableWidget(detailGroup); bedDetailTable_->setColumnCount(4); bedDetailTable_->setHorizontalHeaderLabels({tr("床位ID"), tr("状态"), tr("患者"), tr("备注")}); bedDetailTable_->setSelectionBehavior(QAbstractItemView::SelectRows); bedDetailTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); bedDetailTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); detailLayout->addWidget(bedDetailTable_); mainLayout->addWidget(detailGroup); connect(summaryTable_, &QTableWidget::itemSelectionChanged, this, &WardOccupancyDialog::showBedDetail); refreshData(); auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); mainLayout->addWidget(buttonBox); } void WardOccupancyDialog::refreshData() { wardSummaries_.clear(); QString typeFilter = wardTypeFilter_->currentText(); QString gradeFilter = gradeFilter_->currentText(); core_.wardService.for_eachWard([this, typeFilter, gradeFilter](const std::string&, const Ward& ward) { QString wardType = wardTypeToText(ward.Type); double rate = ward.occupancyRate(); if (typeFilter != tr("全部") && typeFilter != wardType) return; if (gradeFilter == tr("高占用率 (>=80%)") && rate < 0.8) return; if (gradeFilter == tr("中占用率 (40-80%)") && (rate < 0.4 || rate > 0.8)) return; if (gradeFilter == tr("低占用率 (<40%)") && rate >= 0.4) return; WardSummary ws; ws.wardId = QString::fromStdString(ward.WardID); ws.department = QString::fromStdString(ward.DepartmentID); ws.type = wardType; ws.totalBeds = ward.MaxBeds; ws.occupied = ward.occupiedBedCount(); ws.free = ward.freeBedCount(); ws.rate = rate; wardSummaries_.append(ws); }); summaryTable_->setRowCount(wardSummaries_.size()); for (int i = 0; i < wardSummaries_.size(); ++i) { const auto& ws = wardSummaries_[i]; summaryTable_->setItem(i, 0, new QTableWidgetItem(ws.wardId)); summaryTable_->setItem(i, 1, new QTableWidgetItem(ws.department)); summaryTable_->setItem(i, 2, new QTableWidgetItem(ws.type)); summaryTable_->setItem(i, 3, new QTableWidgetItem(QString::number(ws.totalBeds))); summaryTable_->setItem(i, 4, new QTableWidgetItem(QString::number(ws.occupied))); summaryTable_->setItem(i, 5, new QTableWidgetItem(QString::number(ws.free))); summaryTable_->setItem(i, 6, new QTableWidgetItem(QString("%1%").arg(int(ws.rate * 100)))); } } void WardOccupancyDialog::showBedDetail() { int row = summaryTable_->currentRow(); if (row < 0) return; QString wardId = summaryTable_->item(row, 0)->text(); auto* ward = core_.wardService.findWard(wardId.toStdString()); if (!ward) return; bedDetailTable_->setRowCount(static_cast(ward->Beds.size())); for (size_t i = 0; i < ward->Beds.size(); ++i) { const auto& bed = ward->Beds[i]; bedDetailTable_->setItem(static_cast(i), 0, new QTableWidgetItem(QString::fromStdString(bed.BedID))); bedDetailTable_->setItem(static_cast(i), 1, new QTableWidgetItem(bed.Status == BedStatus::Occupied ? tr("已占用") : tr("空闲"))); bedDetailTable_->setItem(static_cast(i), 2, new QTableWidgetItem(bed.PatientID.empty() ? "-" : QString::fromStdString(bed.PatientID))); bedDetailTable_->setItem(static_cast(i), 3, new QTableWidgetItem("")); } } // ========== DoctorAddDialog ========== DoctorAddDialog::DoctorAddDialog(core::HisCore& core, const QString& doctorId, QWidget* parent) : QDialog(parent), core_(core), doctorId_(doctorId), isEditMode_(!doctorId.isEmpty()) { setupUI(); if (isEditMode_) { loadDoctorData(); } } DoctorAddDialog::~DoctorAddDialog() {} void DoctorAddDialog::setupUI() { setWindowTitle(isEditMode_ ? tr("编辑医生信息") : tr("添加医生")); setModal(true); resize(450, 300); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); auto* formLayout = new QFormLayout(); formLayout->setSpacing(10); formLayout->setContentsMargins(20, 20, 20, 20); if (isEditMode_) { idLabel_ = new QLabel(doctorId_, this); idLabel_->setEnabled(false); formLayout->addRow(tr("医生ID:"), idLabel_); } else { QString newDoctorId = QString::fromStdString(Doctor::generateUniqueId()); idLabel_ = new QLabel(newDoctorId, this); idLabel_->setEnabled(false); formLayout->addRow(tr("医生ID (自动生成):"), idLabel_); } nameEdit_ = new QLineEdit(this); nameEdit_->setPlaceholderText(tr("请输入医生姓名")); input_validator::setMaxLength(nameEdit_); // 限制最大255字符 formLayout->addRow(tr("姓名:"), nameEdit_); deptEdit_ = new QComboBox(this); deptEdit_->addItem(tr("-- 请选择科室 --")); core_.departmentService.for_eachDepartment([this](const std::string&, const Department& d) { deptEdit_->addItem(QString::fromStdString(d.Name), QString::fromStdString(d.DepartmentID)); }); formLayout->addRow(tr("科室:"), deptEdit_); titleCombo_ = new QComboBox(this); titleCombo_->addItems({tr("住院医师"), tr("主治医师"), tr("副主任医师"), tr("主任医师")}); formLayout->addRow(tr("职称:"), titleCombo_); scheduleEdit_ = new QLineEdit(this); scheduleEdit_->setPlaceholderText(tr("请输入排班信息")); input_validator::setMaxLength(scheduleEdit_); // 限制最大255字符 formLayout->addRow(tr("排班:"), scheduleEdit_); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout->addRow(buttonBox_); mainLayout->addLayout(formLayout); } void DoctorAddDialog::loadDoctorData() { auto* d = core_.doctorService.findDoctor(doctorId_.toStdString()); if (!d) return; nameEdit_->setText(QString::fromStdString(d->Name)); int deptIdx = deptEdit_->findData(QString::fromStdString(d->DepartmentID)); if (deptIdx != -1) { deptEdit_->setCurrentIndex(deptIdx); } int titleIdx = (d->Title == DoctorTitle::Chief) ? 3 : (d->Title == DoctorTitle::AssociateChief) ? 2 : (d->Title == DoctorTitle::Attending) ? 1 : 0; titleCombo_->setCurrentIndex(titleIdx); scheduleEdit_->setText(QString::fromStdString(d->Schedule)); } QString DoctorAddDialog::getName() const { return nameEdit_->text(); } QString DoctorAddDialog::getDepartment() const { return deptEdit_->currentText(); } QString DoctorAddDialog::getTitle() const { return titleCombo_->currentText(); } QString DoctorAddDialog::getSchedule() const { return scheduleEdit_->text(); } bool DoctorAddDialog::isEditMode() const { return isEditMode_; } // ========== PatientCaseViewDialog ========== PatientCaseViewDialog::PatientCaseViewDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId) { setupUI(); refreshCaseDisplay(); } PatientCaseViewDialog::~PatientCaseViewDialog() {} void PatientCaseViewDialog::setupUI() { setWindowTitle(tr("病例信息 - %1").arg(patientId_)); setModal(true); resize(800, 600); setAttribute(Qt::WA_DeleteOnClose); auto* mainLayout = new QVBoxLayout(this); // 顶部工具栏 auto* toolBar = new QHBoxLayout(); auto* sortLabel = new QLabel(tr("排序方式:"), this); sortComboBox_ = new QComboBox(this); sortComboBox_->addItems({tr("按时间"), tr("按医生"), tr("按类型")}); toolBar->addWidget(sortLabel); toolBar->addWidget(sortComboBox_); toolBar->addStretch(); mainLayout->addLayout(toolBar); // 选项卡 tabWidget_ = new QTabWidget(this); // 预约记录选项卡 auto* appointmentTab = new QWidget(); auto* appointmentLayout = new QVBoxLayout(appointmentTab); appointmentTable_ = new QTableWidget(0, 3, appointmentTab); appointmentTable_->setHorizontalHeaderLabels({tr("预约时间"), tr("医生"), tr("备注")}); appointmentTable_->setSelectionBehavior(QAbstractItemView::SelectRows); appointmentTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); appointmentTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); appointmentLayout->addWidget(appointmentTable_); tabWidget_->addTab(appointmentTab, tr("预约记录")); // 检查记录选项卡 auto* checkTab = new QWidget(); auto* checkLayout = new QVBoxLayout(checkTab); checkTable_ = new QTableWidget(0, 5, checkTab); checkTable_->setHorizontalHeaderLabels({tr("时间"), tr("检查项目"), tr("科室"), tr("价格"), tr("开单医生")}); checkTable_->setSelectionBehavior(QAbstractItemView::SelectRows); checkTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); checkTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); checkLayout->addWidget(checkTable_); tabWidget_->addTab(checkTab, tr("检查记录")); // 诊断记录选项卡 auto* diagnosisTab = new QWidget(); auto* diagnosisLayout = new QVBoxLayout(diagnosisTab); diagnosisTable_ = new QTableWidget(0, 5, diagnosisTab); diagnosisTable_->setHorizontalHeaderLabels({tr("时间"), tr("医生"), tr("诊断"), tr("处方"), tr("备注")}); diagnosisTable_->setSelectionBehavior(QAbstractItemView::SelectRows); diagnosisTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); diagnosisTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); diagnosisLayout->addWidget(diagnosisTable_); tabWidget_->addTab(diagnosisTab, tr("诊断记录")); // 用药记录选项卡 auto* medicineTab = new QWidget(); auto* medicineLayout = new QVBoxLayout(medicineTab); medicineTable_ = new QTableWidget(0, 6, medicineTab); medicineTable_->setHorizontalHeaderLabels({tr("时间"), tr("药品"), tr("数量"), tr("用法"), tr("单价"), tr("总价")}); medicineTable_->setSelectionBehavior(QAbstractItemView::SelectRows); medicineTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); medicineTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); medicineLayout->addWidget(medicineTable_); tabWidget_->addTab(medicineTab, tr("用药记录")); // 住院记录选项卡 auto* admissionTab = new QWidget(); auto* admissionLayout = new QVBoxLayout(admissionTab); admissionTable_ = new QTableWidget(0, 6, admissionTab); admissionTable_->setHorizontalHeaderLabels({tr("入院时间"), tr("出院时间"), tr("病房"), tr("床位"), tr("原因"), tr("出院小结")}); admissionTable_->setSelectionBehavior(QAbstractItemView::SelectRows); admissionTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); admissionTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); admissionLayout->addWidget(admissionTable_); tabWidget_->addTab(admissionTab, tr("住院记录")); mainLayout->addWidget(tabWidget_); // 关闭按钮 auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); mainLayout->addWidget(buttonBox); // 排序变化时刷新 connect(sortComboBox_, QOverload::of(&QComboBox::currentIndexChanged), this, &PatientCaseViewDialog::refreshCaseDisplay); } void PatientCaseViewDialog::refreshCaseDisplay() { PatientCase* pc = core_.patientCaseService.getCase(patientId_.toStdString()); if (!pc) { diagnosisTable_->setRowCount(0); medicineTable_->setRowCount(0); admissionTable_->setRowCount(0); checkTable_->setRowCount(0); return; } // 清空表格 diagnosisTable_->setRowCount(0); medicineTable_->setRowCount(0); admissionTable_->setRowCount(0); checkTable_->setRowCount(0); appointmentTable_->setRowCount(0); // 填充诊断记录 int row = 0; for (const auto& r : pc->DiagnosisRecords) { diagnosisTable_->insertRow(row); diagnosisTable_->setItem(row, 0, new QTableWidgetItem(formatDate(r.Timestamp))); diagnosisTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(r.DoctorID))); diagnosisTable_->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(r.Diagnosis))); diagnosisTable_->setItem(row, 3, new QTableWidgetItem(QString::fromStdString(r.Prescription))); diagnosisTable_->setItem(row, 4, new QTableWidgetItem(QString::fromStdString(r.Remarks))); row++; } // 填充用药记录 row = 0; for (const auto& r : pc->MedicineRecords) { medicineTable_->insertRow(row); medicineTable_->setItem(row, 0, new QTableWidgetItem(formatDate(r.Timestamp))); medicineTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(r.MedicineName))); medicineTable_->setItem(row, 2, new QTableWidgetItem(QString::number(r.Quantity))); medicineTable_->setItem(row, 3, new QTableWidgetItem(QString::fromStdString(r.Usage))); medicineTable_->setItem(row, 4, new QTableWidgetItem(QString::number(r.UnitPrice, 'f', 2))); medicineTable_->setItem(row, 5, new QTableWidgetItem(QString::number(r.getTotalPrice(), 'f', 2))); row++; } // 填充住院记录 row = 0; for (const auto& r : pc->AdmissionRecords) { admissionTable_->insertRow(row); admissionTable_->setItem(row, 0, new QTableWidgetItem(formatDate(r.AdmissionTime))); admissionTable_->setItem(row, 1, new QTableWidgetItem(r.DischargeTime == 0 ? tr("在院") : formatDate(r.DischargeTime))); admissionTable_->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(r.WardID))); admissionTable_->setItem(row, 3, new QTableWidgetItem(QString::fromStdString(r.BedID))); admissionTable_->setItem(row, 4, new QTableWidgetItem(QString::fromStdString(r.Reason))); admissionTable_->setItem(row, 5, new QTableWidgetItem(QString::fromStdString(r.DischargeSummary))); row++; } // 填充检查记录 row = 0; for (const auto& r : pc->CheckRecords) { checkTable_->insertRow(row); checkTable_->setItem(row, 0, new QTableWidgetItem(formatDate(r.Timestamp))); checkTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(r.CheckName))); checkTable_->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(r.DepartmentID))); checkTable_->setItem(row, 3, new QTableWidgetItem(QString::number(r.Price, 'f', 2))); checkTable_->setItem(row, 4, new QTableWidgetItem(QString::fromStdString(r.DoctorID))); row++; } // 填充预约记录 row = 0; for (const auto& r : pc->AppointmentRecords) { appointmentTable_->insertRow(row); appointmentTable_->setItem(row, 0, new QTableWidgetItem(QString::fromStdString(r.AppointmentDate))); appointmentTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(r.DoctorID))); appointmentTable_->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(r.Notes))); row++; } } // ========== MedicineRecordAddDialog ========== MedicineRecordAddDialog::MedicineRecordAddDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId), selectedMedicineId_(QString()), selectedDoctorId_(QString()) { setupUI(); } MedicineRecordAddDialog::~MedicineRecordAddDialog() {} void MedicineRecordAddDialog::setupUI() { setWindowTitle(tr("添加用药记录 - %1").arg(patientId_)); setModal(true); resize(500, 400); auto* mainLayout = new QVBoxLayout(this); auto* formLayout = new QFormLayout(); formLayout->setSpacing(10); formLayout->setContentsMargins(20, 20, 20, 20); // 药品信息显示 - 必须在药品选择之前创建,避免信号触发时访问空指针 stockLabel_ = new QLabel(tr("库存: -"), this); priceLabel_ = new QLabel(tr("单价: -"), this); formLayout->addRow(tr("库存信息:"), stockLabel_); formLayout->addRow(tr("价格信息:"), priceLabel_); // 药品选择 medicineCombo_ = new QComboBox(this); medicineCombo_->addItem(tr("-- 请选择药品 --"), QVariant()); core_.medicineService.for_eachMedicine([this](const std::string&, const Medicine& med) { QString display = QString("%1 (%2) - 库存: %3") .arg(QString::fromStdString(med.GenericName)) .arg(QString::fromStdString(med.BrandName)) .arg(med.StockQuantity); medicineCombo_->addItem(display, QString::fromStdString(med.MedicineID)); }); formLayout->addRow(tr("选择药品:"), medicineCombo_); // 缓存当前选中药品 ID,避免 exec 后直接读取 ComboBox 出现悬指针 connect(medicineCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index <= 0) { selectedMedicineId_.clear(); stockLabel_->setText(tr("库存: -")); priceLabel_->setText(tr("单价: -")); return; } selectedMedicineId_ = medicineCombo_->itemData(index).toString().trimmed(); }); // 数量 quantitySpin_ = new QSpinBox(this); quantitySpin_->setRange(1, 1000); quantitySpin_->setValue(1); formLayout->addRow(tr("数量:"), quantitySpin_); // 用法 usageCombo_ = new QComboBox(this); usageCombo_->addItems({tr("一日三次"), tr("一日两次"), tr("一日一次"), tr("按需服用"), tr("外用")}); formLayout->addRow(tr("用法:"), usageCombo_); // 医生选择 doctorCombo_ = new QComboBox(this); doctorCombo_->addItem(tr("-- 请选择医生 --")); core_.doctorService.for_eachDoctor([this](const std::string&, const Doctor& d) { QString display = QString("%1 (%2)") .arg(QString::fromStdString(d.Name)) .arg(QString::fromStdString(d.DoctorID)); doctorCombo_->addItem(display, QString::fromStdString(d.DoctorID)); }); formLayout->addRow(tr("开药医生:"), doctorCombo_); connect(doctorCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index <= 0) { selectedDoctorId_.clear(); return; } selectedDoctorId_ = doctorCombo_->itemData(index).toString().trimmed(); }); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout->addRow(buttonBox_); mainLayout->addLayout(formLayout); // 药品选择变化时更新信息 // 药品选择变化时更新信息(✅ 用 lambda,彻底避免信号槽坑) connect(medicineCombo_, &QComboBox::currentIndexChanged, this, [this](int index) { if (index <= 0) { stockLabel_->setText(tr("库存: -")); priceLabel_->setText(tr("单价: -")); return; } QString medicineId = medicineCombo_->itemData(index).toString(); if (medicineId.isEmpty()) { stockLabel_->setText(tr("库存: -")); priceLabel_->setText(tr("单价: -")); return; } auto* med = core_.medicineService.findMedicine(medicineId.toStdString()); if (med) { stockLabel_->setText(tr("库存: %1").arg(med->StockQuantity)); priceLabel_->setText(tr("单价: %1").arg(QString::number(med->UnitPrice, 'f', 2))); quantitySpin_->setRange(1, med->StockQuantity); } } ); } QString MedicineRecordAddDialog::getMedicineId() const { // 优先使用缓存值,提高稳定性,避免控件对象在 exec() 后的潜在内存问题 if (!selectedMedicineId_.isEmpty()) { return selectedMedicineId_; } if (!medicineCombo_) { return QString(); } int idx = medicineCombo_->currentIndex(); if (idx <= 0 || idx >= medicineCombo_->count()) { return QString(); } return medicineCombo_->itemData(idx).toString().trimmed(); } int MedicineRecordAddDialog::getQuantity() const { return quantitySpin_->value(); } QString MedicineRecordAddDialog::getUsage() const { return usageCombo_->currentText(); } QString MedicineRecordAddDialog::getDoctorId() const { if (!selectedDoctorId_.isEmpty()) { return selectedDoctorId_; } if (!doctorCombo_) { return QString(); } int idx = doctorCombo_->currentIndex(); if (idx <= 0 || idx >= doctorCombo_->count()) { return QString(); } return doctorCombo_->itemData(idx).toString().trimmed(); } // ========== CheckRecordAddDialog ========== CheckRecordAddDialog::CheckRecordAddDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId), selectedCheckId_(QString()), selectedDoctorId_(QString()) { setupUI(); } CheckRecordAddDialog::~CheckRecordAddDialog() {} void CheckRecordAddDialog::setupUI() { setWindowTitle(tr("添加检查记录 - %1").arg(patientId_)); setModal(true); resize(500, 300); auto* mainLayout = new QVBoxLayout(this); auto* formLayout = new QFormLayout(); formLayout->setSpacing(10); formLayout->setContentsMargins(20, 20, 20, 20); // 价格信息显示 priceLabel_ = new QLabel(tr("价格: -"), this); formLayout->addRow(tr("价格信息:"), priceLabel_); // 检查项目选择 checkCombo_ = new QComboBox(this); checkCombo_->addItem(tr("-- 请选择检查项目 --"), QVariant()); core_.checkService.for_eachCheck([this](const std::string&, const Check& chk) { QString display = QString("%1 - %2") .arg(QString::fromStdString(chk.Name)) .arg(QString::number(chk.Price, 'f', 2)); checkCombo_->addItem(display, QString::fromStdString(chk.CheckID)); }); formLayout->addRow(tr("选择检查项目:"), checkCombo_); // 缓存当前选中检查 ID connect(checkCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index <= 0) { selectedCheckId_.clear(); priceLabel_->setText(tr("价格: -")); return; } selectedCheckId_ = checkCombo_->itemData(index).toString().trimmed(); }); // 医生选择 doctorCombo_ = new QComboBox(this); doctorCombo_->addItem(tr("-- 请选择医生 --")); core_.doctorService.for_eachDoctor([this](const std::string&, const Doctor& d) { QString display = QString("%1 (%2)") .arg(QString::fromStdString(d.Name)) .arg(QString::fromStdString(d.DoctorID)); doctorCombo_->addItem(display, QString::fromStdString(d.DoctorID)); }); formLayout->addRow(tr("开单医生:"), doctorCombo_); connect(doctorCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index <= 0) { selectedDoctorId_.clear(); return; } selectedDoctorId_ = doctorCombo_->itemData(index).toString().trimmed(); }); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox_, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox_, &QDialogButtonBox::rejected, this, &QDialog::reject); formLayout->addRow(buttonBox_); mainLayout->addLayout(formLayout); // 检查项目选择变化时更新信息 connect(checkCombo_, &QComboBox::currentIndexChanged, this, [this](int index) { if (index <= 0) { priceLabel_->setText(tr("价格: -")); return; } QString checkId = checkCombo_->itemData(index).toString(); if (checkId.isEmpty()) { priceLabel_->setText(tr("价格: -")); return; } auto* chk = core_.checkService.findCheck(checkId.toStdString()); if (chk) { priceLabel_->setText(tr("价格: %1").arg(QString::number(chk->Price, 'f', 2))); } } ); } QString CheckRecordAddDialog::getCheckId() const { if (!selectedCheckId_.isEmpty()) { return selectedCheckId_; } if (!checkCombo_) { return QString(); } int idx = checkCombo_->currentIndex(); if (idx <= 0 || idx >= checkCombo_->count()) { return QString(); } return checkCombo_->itemData(idx).toString().trimmed(); } QString CheckRecordAddDialog::getDoctorId() const { if (!selectedDoctorId_.isEmpty()) { return selectedDoctorId_; } if (!doctorCombo_) { return QString(); } int idx = doctorCombo_->currentIndex(); if (idx <= 0 || idx >= doctorCombo_->count()) { return QString(); } return doctorCombo_->itemData(idx).toString().trimmed(); } // ========== RegistrationDialog ========== RegistrationDialog::RegistrationDialog(core::HisCore& core, const QString& patientId, QWidget* parent) : QDialog(parent), core_(core), patientId_(patientId) { setupUI(); loadDepartments(); } RegistrationDialog::~RegistrationDialog() {} void RegistrationDialog::setupUI() { setWindowTitle(tr("挂号 - 患者 %1").arg(patientId_)); setModal(true); resize(700, 500); // 不使用 Qt::WA_DeleteOnClose,改用手动生命周期管理 auto* mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(10); mainLayout->setContentsMargins(20, 20, 20, 20); // 患者信息 auto* patientInfoLayout = new QHBoxLayout(); patientInfoLayout->addWidget(new QLabel(tr("患者ID:"), this)); auto* patientIdLabel = new QLabel(patientId_, this); patientIdLabel->setEnabled(false); patientInfoLayout->addWidget(patientIdLabel); patientInfoLayout->addSpacing(20); patientInfoLayout->addWidget(new QLabel(tr("挂号时间:"), this)); dateLabel_ = new QLabel(formatCurrentDate(), this); dateLabel_->setEnabled(false); patientInfoLayout->addWidget(dateLabel_); patientInfoLayout->addStretch(); mainLayout->addLayout(patientInfoLayout); // 分隔线 auto* separator1 = new QFrame(this); separator1->setFrameShape(QFrame::HLine); separator1->setFrameShadow(QFrame::Sunken); mainLayout->addWidget(separator1); // 挂号类型选择:门诊或急诊 auto* typeLayout = new QHBoxLayout(); typeLayout->addWidget(new QLabel(tr("挂号类型:"), this)); registrationTypeCombo_ = new QComboBox(this); registrationTypeCombo_->addItem(tr("门诊"), QVariant(static_cast(0))); // 0 = 门诊 registrationTypeCombo_->addItem(tr("急诊"), QVariant(static_cast(1))); // 1 = 急诊 typeLayout->addWidget(registrationTypeCombo_); // 挂号费提示 feeLabel_ = new QLabel(tr("挂号费: 10元"), this); feeLabel_->setStyleSheet("color: blue; font-weight: bold;"); typeLayout->addWidget(feeLabel_); typeLayout->addStretch(); mainLayout->addLayout(typeLayout); // 连接挂号类型变化信号 connect(registrationTypeCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, &RegistrationDialog::onRegistrationTypeChanged); // 分隔线 auto* separator2 = new QFrame(this); separator2->setFrameShape(QFrame::HLine); separator2->setFrameShadow(QFrame::Sunken); mainLayout->addWidget(separator2); // 科室选择 auto* deptLayout = new QHBoxLayout(); deptLayout->addWidget(new QLabel(tr("选择科室:"), this)); departmentCombo_ = new QComboBox(this); departmentCombo_->addItem(tr("-- 请选择科室 --")); deptLayout->addWidget(departmentCombo_); deptLayout->addStretch(); mainLayout->addLayout(deptLayout); // 医生列表 mainLayout->addWidget(new QLabel(tr("医生信息:"), this)); doctorTable_ = new QTableWidget(0, 5, this); doctorTable_->setHorizontalHeaderLabels({ tr("医生ID"), tr("姓名"), tr("职称"), tr("所属科室"), tr("出诊时间") }); doctorTable_->setSelectionBehavior(QAbstractItemView::SelectRows); doctorTable_->setSelectionMode(QAbstractItemView::SingleSelection); doctorTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); doctorTable_->setEditTriggers(QAbstractItemView::NoEditTriggers); mainLayout->addWidget(doctorTable_); // 按钮 auto* buttonLayout = new QHBoxLayout(); confirmButton_ = new QPushButton(tr("确认挂号"), this); cancelButton_ = new QPushButton(tr("取消"), this); buttonLayout->addStretch(); buttonLayout->addWidget(confirmButton_); buttonLayout->addWidget(cancelButton_); mainLayout->addLayout(buttonLayout); // 信号与槽 connect(departmentCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, &RegistrationDialog::onDepartmentChanged); connect(doctorTable_, &QTableWidget::cellDoubleClicked, this, &RegistrationDialog::onDoctorSelected); connect(confirmButton_, &QPushButton::clicked, this, &RegistrationDialog::confirmRegistration); connect(cancelButton_, &QPushButton::clicked, this, &QDialog::reject); } void RegistrationDialog::loadDepartments() { if (!departmentCombo_) return; // 临时断开信号以避免重复触发 disconnect(departmentCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, &RegistrationDialog::onDepartmentChanged); // 收集所有科室 std::set deptSet; core_.ctx_.doctors.for_each([&deptSet](const std::string&, const Doctor& d) { if (!d.DepartmentID.empty()) { deptSet.insert(d.DepartmentID); } }); for (const auto& deptId : deptSet) { const auto* dept = core_.departmentService.findDepartment(deptId); if (dept && !dept->Name.empty()) { QString display = QString::fromStdString(dept->Name); departmentCombo_->addItem(display, QString::fromStdString(deptId)); } else if (!deptId.empty()) { // 如果找不到科室信息,就直接显示ID departmentCombo_->addItem(QString::fromStdString(deptId), QString::fromStdString(deptId)); } } // 重新连接信号 connect(departmentCombo_, QOverload::of(&QComboBox::currentIndexChanged), this, &RegistrationDialog::onDepartmentChanged); } void RegistrationDialog::loadDoctorsForDepartment(const QString& deptId) { doctorTable_->setRowCount(0); selectedDoctorId_.clear(); if (deptId.isEmpty()) { return; } // 获取该科室的所有医生 std::vector doctors = core_.departmentService.getDoctorsByDepartment(deptId.toStdString()); for (size_t i = 0; i < doctors.size(); ++i) { int row = static_cast(i); doctorTable_->insertRow(row); // 医生ID auto* idItem = new QTableWidgetItem(QString::fromStdString(doctors[i].DoctorID)); idItem->setFlags(idItem->flags() & ~Qt::ItemIsEditable); doctorTable_->setItem(row, 0, idItem); // 姓名 auto* nameItem = new QTableWidgetItem(QString::fromStdString(doctors[i].Name)); nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable); doctorTable_->setItem(row, 1, nameItem); // 职称 QString titleText; switch (doctors[i].Title) { case DoctorTitle::Chief: titleText = tr("主任医师"); break; case DoctorTitle::AssociateChief: titleText = tr("副主任医师"); break; case DoctorTitle::Attending: titleText = tr("主治医师"); break; case DoctorTitle::Resident: titleText = tr("住院医师"); break; } auto* titleItem = new QTableWidgetItem(titleText); titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable); doctorTable_->setItem(row, 2, titleItem); // 所属科室 QString deptName; const auto* dept = core_.departmentService.findDepartment(doctors[i].DepartmentID); if (dept) { deptName = QString::fromStdString(dept->Name); } else { deptName = QString::fromStdString(doctors[i].DepartmentID); } auto* deptItem = new QTableWidgetItem(deptName); deptItem->setFlags(deptItem->flags() & ~Qt::ItemIsEditable); doctorTable_->setItem(row, 3, deptItem); // 出诊时间 auto* scheduleItem = new QTableWidgetItem(QString::fromStdString(doctors[i].Schedule)); scheduleItem->setFlags(scheduleItem->flags() & ~Qt::ItemIsEditable); doctorTable_->setItem(row, 4, scheduleItem); } } void RegistrationDialog::onDepartmentChanged(int index) { selectedDoctorId_.clear(); if (index <= 0) { doctorTable_->setRowCount(0); selectedDepartmentId_.clear(); return; } QString deptId = departmentCombo_->itemData(index).toString().trimmed(); selectedDepartmentId_ = deptId; loadDoctorsForDepartment(deptId); } void RegistrationDialog::onDoctorSelected(int row, int column) { (void)column; // 不使用 column 参数,避免编译器警告 if (row < 0) return; auto* idItem = doctorTable_->item(row, 0); if (idItem) { selectedDoctorId_ = idItem->text(); // 高亮选中的行 doctorTable_->selectRow(row); } } void RegistrationDialog::confirmRegistration() { if (selectedDepartmentId_.isEmpty() || selectedDoctorId_.isEmpty()) { QMessageBox::warning(this, tr("挂号失败"), tr("请选择科室和医生")); return; } // 验证医生和患者存在 const auto* doctor = core_.doctorService.findDoctor(selectedDoctorId_.toStdString()); const auto* patient = core_.patientService.findPatient(patientId_.toStdString()); if (!doctor || !patient) { QMessageBox::warning(this, tr("挂号失败"), tr("医生或患者不存在")); return; } // 创建和添加挂号记录 time_t now = std::time(nullptr); struct tm timeinfo; localtime_r(&now, &timeinfo); char dateStr[30]; strftime(dateStr, 30, "%Y-%m-%d %H:%M", &timeinfo); std::string notes = "通过GUI挂号"; AppointmentRecord record( selectedDoctorId_.toStdString(), patientId_.toStdString(), std::string(dateStr), notes ); if (core_.patientCaseService.addAppointmentRecord(patientId_.toStdString(), record)) { QMessageBox::information(this, tr("挂号成功"), tr("患者 %1 已成功挂号给医生 %2").arg(patientId_).arg(selectedDoctorId_)); accept(); } else { QMessageBox::warning(this, tr("挂号失败"), tr("添加挂号记录失败")); } } QString RegistrationDialog::getDepartmentId() const { return selectedDepartmentId_; } QString RegistrationDialog::getDoctorId() const { return selectedDoctorId_; } int RegistrationDialog::getRegistrationType() const { return registrationType_; } void RegistrationDialog::onRegistrationTypeChanged(int index) { registrationType_ = registrationTypeCombo_->itemData(index).toInt(); if (registrationType_ == 0) { // 门诊 feeLabel_->setText(tr("挂号费: 10元")); feeLabel_->setStyleSheet("color: blue; font-weight: bold;"); } else { // 急诊 feeLabel_->setText(tr("挂号费: 50元 (急诊加收)")); feeLabel_->setStyleSheet("color: red; font-weight: bold;"); } }