1744 lines
68 KiB
C++
1744 lines
68 KiB
C++
#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 <QMessageBox>
|
||
#include <QTreeWidgetItem>
|
||
#include <QHeaderView>
|
||
#include <QGroupBox>
|
||
#include <QVBoxLayout>
|
||
#include <QRegularExpression>
|
||
#include <QHBoxLayout>
|
||
#include <QFormLayout>
|
||
#include <QLabel>
|
||
#include <QLineEdit>
|
||
#include <QSpinBox>
|
||
#include <QDoubleSpinBox>
|
||
#include <QComboBox>
|
||
#include <QTextEdit>
|
||
#include <QPushButton>
|
||
#include <QTableWidget>
|
||
#include <QTableWidgetItem>
|
||
#include <QTabWidget>
|
||
#include <QDialogButtonBox>
|
||
#include <QDateEdit>
|
||
#include <ctime>
|
||
#include <iomanip>
|
||
#include <sstream>
|
||
|
||
// ========== 工具函数 ==========
|
||
|
||
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<Ward> collectExistingWards(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> collectExistingPatientIds(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> collectExistingDoctorIds(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> collectExistingMedicineIds(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 std::vector<std::string> collectExistingDepartments(core::HisCore& core) {
|
||
std::set<std::string> depts;
|
||
core.ctx_.wards.for_each([&depts](const std::string&, const Ward& w) { depts.insert(w.DepartmentID); });
|
||
return std::vector<std::string>(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<int>::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<std::string> 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<int>::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<int>::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<int>(ward->Beds.size()));
|
||
for (size_t i = 0; i < ward->Beds.size(); ++i) {
|
||
const auto& bed = ward->Beds[i];
|
||
bedDetailTable_->setItem(static_cast<int>(i), 0, new QTableWidgetItem(QString::fromStdString(bed.BedID)));
|
||
bedDetailTable_->setItem(static_cast<int>(i), 1, new QTableWidgetItem(bed.Status == BedStatus::Occupied ? tr("已占用") : tr("空闲")));
|
||
bedDetailTable_->setItem(static_cast<int>(i), 2, new QTableWidgetItem(bed.PatientID.empty() ? "-" : QString::fromStdString(bed.PatientID)));
|
||
bedDetailTable_->setItem(static_cast<int>(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<int>::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<int>::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<int>::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<int>::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<int>::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<int>(0))); // 0 = 门诊
|
||
registrationTypeCombo_->addItem(tr("急诊"), QVariant(static_cast<int>(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<int>::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<int>::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<int>::of(&QComboBox::currentIndexChanged),
|
||
this, &RegistrationDialog::onDepartmentChanged);
|
||
|
||
// 收集所有科室
|
||
std::set<std::string> 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<int>::of(&QComboBox::currentIndexChanged),
|
||
this, &RegistrationDialog::onDepartmentChanged);
|
||
}
|
||
|
||
void RegistrationDialog::loadDoctorsForDepartment(const QString& deptId) {
|
||
doctorTable_->setRowCount(0);
|
||
selectedDoctorId_.clear();
|
||
|
||
if (deptId.isEmpty()) {
|
||
return;
|
||
}
|
||
|
||
// 获取该科室的所有医生
|
||
std::vector<Doctor> doctors = core_.departmentService.getDoctorsByDepartment(deptId.toStdString());
|
||
|
||
for (size_t i = 0; i < doctors.size(); ++i) {
|
||
int row = static_cast<int>(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;");
|
||
}
|
||
}
|