#include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/his_core.h" #include "models/patient.h" #include "utils/logger.h" #include "utils/input_validator.h" #include "dialogs/patient_dialog.h" #include "dialogs/payment_dialog.h" #include "dialogs/settlement_dialog.h" void MainWindow::setupPatientsTab() { patientsTab_ = new QWidget(this); auto* layout = new QVBoxLayout(patientsTab_); patientSearchBox_ = new QLineEdit(patientsTab_); patientSearchBox_->setPlaceholderText(tr("搜索患者...")); connect(patientSearchBox_, &QLineEdit::textChanged, this, &MainWindow::onPatientSearch); auto* toolBar1 = new QHBoxLayout(); toolBar1->addWidget(patientSearchBox_); patientAddButton_ = new QPushButton(tr("添加患者"), patientsTab_); patientEditButton_ = new QPushButton(tr("编辑患者"), patientsTab_); patientRemoveButton_ = new QPushButton(tr("删除患者"), patientsTab_); patientViewCaseButton_ = new QPushButton(tr("查看病例信息"), patientsTab_); toolBar1->addWidget(patientAddButton_); toolBar1->addWidget(patientEditButton_); toolBar1->addWidget(patientRemoveButton_); toolBar1->addWidget(patientViewCaseButton_); toolBar1->addStretch(1); layout->addLayout(toolBar1); auto* toolBar2 = new QHBoxLayout(); patientRegistrationButton_ = new QPushButton(tr("挂号"), patientsTab_); patientAddCheckRecordButton_ = new QPushButton(tr("添加检查记录"), patientsTab_); patientAddDiagnosisButton_ = new QPushButton(tr("添加诊断记录"), patientsTab_); patientAddMedicineRecordButton_ = new QPushButton(tr("添加用药记录"), patientsTab_); patientAdmitButton_ = new QPushButton(tr("入院"), patientsTab_); patientAddSurgeryRecordButton_ = new QPushButton(tr("添加手术记录"), patientsTab_); patientDischargeButton_ = new QPushButton(tr("出院"), patientsTab_); patientPaymentButton_ = new QPushButton(tr("缴费"), patientsTab_); toolBar2->addWidget(patientRegistrationButton_); toolBar2->addWidget(patientAddCheckRecordButton_); toolBar2->addWidget(patientAddDiagnosisButton_); toolBar2->addWidget(patientAddMedicineRecordButton_); toolBar2->addWidget(patientAdmitButton_); toolBar2->addWidget(patientAddSurgeryRecordButton_); toolBar2->addWidget(patientDischargeButton_); toolBar2->addWidget(patientPaymentButton_); toolBar2->addStretch(1); layout->addLayout(toolBar2); auto* rowLayout = new QHBoxLayout(); patientTable_ = new QTableWidget(0, 6, patientsTab_); patientTable_->setHorizontalHeaderLabels({tr("患者ID"), tr("姓名"), tr("性别"), tr("年龄"), tr("手机号"), tr("状态")}); patientTable_->setSelectionBehavior(QAbstractItemView::SelectRows); patientTable_->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); patientTable_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); patientTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); patientTable_->horizontalHeader()->setStretchLastSection(false); patientTable_->setSortingEnabled(true); patientTable_->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); patientCaseDetail_ = new QTextEdit(patientsTab_); patientCaseDetail_->setReadOnly(true); patientCaseDetail_->setPlaceholderText(tr("选择患者以查看详细病例记录...")); rowLayout->addWidget(patientTable_, 3); rowLayout->addWidget(patientCaseDetail_, 2); layout->addLayout(rowLayout); connect(patientAddButton_, &QPushButton::clicked, this, &MainWindow::handleAddPatient); connect(patientEditButton_, &QPushButton::clicked, this, &MainWindow::handleEditPatient); connect(patientRemoveButton_, &QPushButton::clicked, this, &MainWindow::handleRemovePatient); connect(patientViewCaseButton_, &QPushButton::clicked, this, &MainWindow::handleViewPatientCase); connect(patientAdmitButton_, &QPushButton::clicked, this, &MainWindow::handleAdmitPatient); connect(patientDischargeButton_, &QPushButton::clicked, this, &MainWindow::handleDischargePatient); connect(patientPaymentButton_, &QPushButton::clicked, this, &MainWindow::handlePatientPayment); connect(patientAddDiagnosisButton_, &QPushButton::clicked, this, &MainWindow::handleAddDiagnosisRecord); connect(patientAddSurgeryRecordButton_, &QPushButton::clicked, this, &MainWindow::handleAddSurgeryRecord); connect(patientAddMedicineRecordButton_, &QPushButton::clicked, this, &MainWindow::handleAddMedicineRecord); connect(patientAddCheckRecordButton_, &QPushButton::clicked, this, &MainWindow::handleAddCheckRecord); connect(patientRegistrationButton_, &QPushButton::clicked, this, &MainWindow::handleRegistration); connect(patientTable_, &QTableWidget::itemSelectionChanged, this, &MainWindow::updatePatientCaseDetail); connect(patientTable_, &QTableWidget::cellChanged, this, &MainWindow::onPatientCellChanged); } void MainWindow::onPatientCellChanged(int row, int column) { QTableWidgetItem* idItem = patientTable_->item(row, 0); if (!idItem) return; QString pid = idItem->text(); if (pid.isEmpty()) return; auto* p = core_.patientService.findPatient(pid.toStdString()); if (!p) return; QTableWidgetItem* nameItem = patientTable_->item(row, 1); QTableWidgetItem* genderItem = patientTable_->item(row, 2); QTableWidgetItem* ageItem = patientTable_->item(row, 3); QTableWidgetItem* contactItem = patientTable_->item(row, 4); QTableWidgetItem* statusItem = patientTable_->item(row, 5); std::string modifyDetails; if (nameItem && column == 1) { p->Name = nameItem->text().toStdString(); modifyDetails = "修改项: 姓名 | 新值: " + p->Name; } if (genderItem && column == 2) { p->Gender = genderItem->text().toStdString(); modifyDetails = "修改项: 性别 | 新值: " + p->Gender; } if (ageItem && column == 3) { p->Age = ageItem->text().toInt(); modifyDetails = "修改项: 年龄 | 新值: " + std::to_string(p->Age); } if (contactItem && column == 4) { p->Contact = contactItem->text().toStdString(); modifyDetails = "修改项: 手机号 | 新值: " + p->Contact; } if (statusItem && column == 5) { p->Status = parsePatientStatus(statusItem->text()); modifyDetails = "修改项: 状态 | 新值: " + patientStatusToText(p->Status).toStdString(); } if (!modifyDetails.empty()) { std::string details = "患者ID: " + pid.toStdString() + " | 患者名: " + p->Name + " | " + modifyDetails; logOperation(LogEntryType::PATIENT_OPERATION, "MODIFY_PATIENT", details, pid.toStdString()); } } void MainWindow::refreshPatientTable() { patientTable_->blockSignals(true); patientTable_->setSortingEnabled(false); patientTable_->setRowCount(0); int row = 0; core_.patientService.for_eachPatient([this, &row](const std::string&, const Patient& patient) { patientTable_->insertRow(row); patientTable_->setItem(row, 0, new QTableWidgetItem(QString::fromStdString(patient.PatientID))); patientTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(patient.Name))); patientTable_->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(patient.Gender))); QTableWidgetItem* ageItem = new QTableWidgetItem(); ageItem->setData(Qt::DisplayRole, patient.Age); patientTable_->setItem(row, 3, ageItem); patientTable_->setItem(row, 4, new QTableWidgetItem(QString::fromStdString(patient.Contact))); patientTable_->setItem(row, 5, new QTableWidgetItem(patientStatusToText(patient.Status))); row++; }); patientTable_->setSortingEnabled(true); patientTable_->blockSignals(false); } void MainWindow::updatePatientCaseDetail() { QString patientId = safeCurrentTableId(patientTable_); if (patientId.isEmpty()) { patientCaseDetail_->setText(tr("请选择患者以查看病例。")); return; } PatientCase* pc = core_.patientCaseService.getCase(patientId.toStdString()); if (!pc) { patientCaseDetail_->setText(tr("当前患者没有病例记录。")); return; } QString text; text += tr("PatientID: %1\n").arg(patientId); text += tr("预约记录数量: %1\n").arg((int)pc->getAppointmentRecordCount()); text += tr("检查记录数量: %1\n").arg((int)pc->getCheckRecordCount()); text += tr("诊断记录数量: %1\n").arg((int)pc->getDiagnosisRecordCount()); text += tr("用药记录数量: %1\n").arg((int)pc->getMedicineRecordCount()); text += tr("手术记录数量: %1\n").arg((int)pc->getSurgeryRecordCount()); text += tr("住院记录数量: %1\n\n").arg((int)pc->getAdmissionRecordCount()); text += tr("--- 预约记录 ---\n"); for (const auto& r : pc->AppointmentRecords) { text += tr("医生: %1, 患者: %2, 日期: %3, 备注: %4, 时间: %5\n") .arg(QString::fromStdString(r.DoctorID)) .arg(QString::fromStdString(r.PatientID)) .arg(QString::fromStdString(r.AppointmentDate)) .arg(QString::fromStdString(r.Notes)) .arg(formatDate(r.Timestamp)); } text += tr("\n--- 检查记录 ---\n"); for (const auto& r : pc->CheckRecords) { text += tr("检查: %1, 价格: %2, 医生: %3, 时间: %4\n") .arg(QString::fromStdString(r.CheckName)) .arg(r.Price) .arg(QString::fromStdString(r.DoctorID)) .arg(formatDate(r.Timestamp)); } text += tr("\n--- 诊断记录 ---\n"); for (const auto& r : pc->DiagnosisRecords) { text += tr("医生: %1, 诊断: %2, 处方: %3, 备注: %4, 时间: %5\n") .arg(QString::fromStdString(r.DoctorID)) .arg(QString::fromStdString(r.Diagnosis)) .arg(QString::fromStdString(r.Prescription)) .arg(QString::fromStdString(r.Remarks)) .arg(formatDate(r.Timestamp)); } text += tr("\n--- 用药记录 ---\n"); for (const auto& r : pc->MedicineRecords) { text += tr("药品: %1, 数量: %2, 用法: %3, 单价: %4, 医生: %5, 时间: %6\n") .arg(QString::fromStdString(r.MedicineName)) .arg(r.Quantity) .arg(QString::fromStdString(r.Usage)) .arg(r.UnitPrice) .arg(QString::fromStdString(r.DoctorID)) .arg(formatDate(r.Timestamp)); } text += tr("\n--- 手术记录 ---\n"); for (const auto& r : pc->SurgeryRecords) { text += tr("手术名称: %1, 类型: %2, 主刀医生: %3\n") .arg(QString::fromStdString(r.SurgeryName)) .arg(QString::fromStdString(r.SurgeryType)) .arg(QString::fromStdString(r.SurgeonID)); text += tr(" 麻醉方式: %1, 麻醉医生: %2\n") .arg(QString::fromStdString(r.AnesthesiaType)) .arg(QString::fromStdString(r.AnesthesiologistID)); text += tr(" 术前诊断: %1\n").arg(QString::fromStdString(r.Diagnosis)); text += tr(" 手术时长: %1分钟, 出血量: %2\n").arg(r.Duration).arg(QString::fromStdString(r.BloodLoss)); if (!r.Complications.empty()) { text += tr(" 并发症: %1\n").arg(QString::fromStdString(r.Complications)); } text += tr(" 手术时间: %1\n").arg(formatDate(r.SurgeryTime)); if (!r.Remarks.empty()) { text += tr(" 备注: %1\n").arg(QString::fromStdString(r.Remarks)); } } text += tr("\n--- 住院记录 ---\n"); for (const auto& r : pc->AdmissionRecords) { text += tr("病房: %1, 床位: %2, 原因: %3\n").arg(QString::fromStdString(r.WardID)).arg(QString::fromStdString(r.BedID)).arg(QString::fromStdString(r.Reason)); text += tr(" 入院时间: %1, 出院时间: %2\n").arg(formatDate(r.AdmissionTime)).arg(formatDate(r.DischargeTime)); if (!r.DischargeSummary.empty()) { text += tr(" 出院小结: %1\n").arg(QString::fromStdString(r.DischargeSummary)); } } patientCaseDetail_->setText(text); } void MainWindow::handleAddPatient() { QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("添加患者")); dialog->setModal(true); dialog->resize(450, 300); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); form->setSpacing(10); form->setContentsMargins(20, 20, 20, 20); QString newPatientId = QString::fromStdString(Patient::generateUniqueId()); QLabel* patientIdDisplay = new QLabel(newPatientId, dialog); patientIdDisplay->setEnabled(false); QLineEdit* nameEdit = new QLineEdit(dialog); QSpinBox* ageSpin = new QSpinBox(dialog); ageSpin->setRange(0, 200); ageSpin->setValue(0); QComboBox* genderCombo = new QComboBox(dialog); genderCombo->addItems({tr("Male"), tr("Female")}); QLineEdit* contactEdit = new QLineEdit(dialog); QLabel* infoLabel = new QLabel(tr("患者ID将自动生成"), dialog); infoLabel->setStyleSheet("color: gray; font-size: 11px;"); form->addRow(tr("患者ID (自动生成):"), patientIdDisplay); form->addRow(tr("姓名:"), nameEdit); form->addRow(tr("年龄:"), ageSpin); form->addRow(tr("性别:"), genderCombo); form->addRow(tr("联系方式:"), contactEdit); form->addRow(infoLabel); auto* buttonLayout = new QHBoxLayout(); auto* okButton = new QPushButton(tr("添加"), dialog); auto* cancelButton = new QPushButton(tr("取消"), dialog); buttonLayout->addWidget(okButton); buttonLayout->addWidget(cancelButton); buttonLayout->addStretch(); form->addRow(buttonLayout); connect(okButton, &QPushButton::clicked, [this, dialog, nameEdit, ageSpin, genderCombo, contactEdit, newPatientId]() { if (nameEdit->text().isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请填写姓名")); return; } // 验证手机号格式 QString contact = contactEdit->text().trimmed(); if (!contact.isEmpty() && !input_validator::validatePhoneNumber(contact)) { QMessageBox::warning(this, tr("手机号格式错误"), tr("手机号格式不正确!\n\n要求:\n- 只能包含数字和 \"-\"\n- \"-\" 不能放在开头或结尾\n- 总长度不超过24个字符\n\n例如:010-88889999 或 13812345678")); return; } Patient p(newPatientId.toStdString(), nameEdit->text().toStdString(), ageSpin->value(), genderCombo->currentText().toStdString(), contactEdit->text().toStdString(), PatientStatus::Unregistered); if (!core_.patientService.addPatient(p)) { QMessageBox::warning(this, tr("添加失败"), tr("患者 ID 已存在")); return; } if (!core_.patientCaseService.addPatientCase(newPatientId.toStdString())) { std::string err; core_.patientService.removePatient(newPatientId.toStdString(), err); QMessageBox::warning(this, tr("添加失败"), tr("病例创建失败")); return; } std::string patientDetails = "患者ID: " + newPatientId.toStdString() + " | 姓名: " + nameEdit->text().toStdString() + " | 年龄: " + std::to_string(ageSpin->value()) + " | 性别: " + genderCombo->currentText().toStdString() + " | 联系方式: " + contactEdit->text().toStdString() + " | 状态: 未挂号"; logOperation(LogEntryType::PATIENT_OPERATION, "ADD_PATIENT", patientDetails, newPatientId.toStdString()); QMessageBox::information(this, tr("成功"), tr("已添加患者: %1 (%2)").arg(newPatientId).arg(nameEdit->text())); refreshPatientTable(); refreshDashboard(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::handleEditPatient() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("编辑患者"), tr("请先选择患者")); return; } auto* p = core_.patientService.findPatient(pid.toStdString()); if (!p) return; QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("编辑患者 - %1").arg(pid)); dialog->setModal(true); dialog->resize(450, 300); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); QLabel* patientIdLabel = new QLabel(pid, dialog); patientIdLabel->setEnabled(false); QLineEdit* nameEdit = new QLineEdit(QString::fromStdString(p->Name), dialog); QSpinBox* ageSpin = new QSpinBox(dialog); ageSpin->setRange(0, 200); ageSpin->setValue(p->Age); QComboBox* genderCombo = new QComboBox(dialog); genderCombo->addItems({tr("Male"), tr("Female")}); int genderIdx = (p->Gender == "Female") ? 1 : 0; genderCombo->setCurrentIndex(genderIdx); QLineEdit* contactEdit = new QLineEdit(QString::fromStdString(p->Contact), dialog); QComboBox* statusCombo = new QComboBox(dialog); statusCombo->addItems({tr("Unregistered"), tr("Outpatient"), tr("Emergency"), tr("Visited"), tr("Inpatient"), tr("Discharged")}); int statusIdx = 0; switch (p->Status) { case PatientStatus::Unregistered: statusIdx = 0; break; case PatientStatus::Outpatient: statusIdx = 1; break; case PatientStatus::Emergency: statusIdx = 2; break; case PatientStatus::Visited: statusIdx = 3; break; case PatientStatus::Inpatient: statusIdx = 4; break; case PatientStatus::Discharged: statusIdx = 5; break; } statusCombo->setCurrentIndex(statusIdx); form->addRow(tr("患者ID:"), patientIdLabel); form->addRow(tr("姓名:"), nameEdit); form->addRow(tr("年龄:"), ageSpin); form->addRow(tr("性别:"), genderCombo); form->addRow(tr("联系方式:"), contactEdit); form->addRow(tr("状态:"), statusCombo); auto* buttonLayout = new QHBoxLayout(); auto* okButton = new QPushButton(tr("保存"), dialog); auto* cancelButton = new QPushButton(tr("取消"), dialog); buttonLayout->addWidget(okButton); buttonLayout->addWidget(cancelButton); buttonLayout->addStretch(); form->addRow(buttonLayout); connect(okButton, &QPushButton::clicked, [this, pid, dialog, p, nameEdit, ageSpin, genderCombo, contactEdit, statusCombo]() { // 验证手机号格式 QString contact = contactEdit->text().trimmed(); if (!contact.isEmpty() && !input_validator::validatePhoneNumber(contact)) { QMessageBox::warning(this, tr("手机号格式错误"), tr("手机号格式不正确!\n\n要求:\n- 只能包含数字和 \"-\"\n- \"-\" 不能放在开头或结尾\n- 总长度不超过24个字符\n\n例如:010-88889999 或 13812345678")); return; } std::string oldDetails = "修改前 - 姓名: " + p->Name + " | 年龄: " + std::to_string(p->Age) + " | 性别: " + p->Gender + " | 联系方式: " + p->Contact + " | 状态: " + patientStatusToText(p->Status).toStdString(); if (!core_.patientService.updatePatient(pid.toStdString(), nameEdit->text().toStdString(), ageSpin->value(), genderCombo->currentText().toStdString(), contactEdit->text().toStdString(), parsePatientStatus(statusCombo->currentText()))) { QMessageBox::warning(this, tr("编辑失败"), tr("更新失败")); return; } std::string newDetails = "修改后 - 姓名: " + nameEdit->text().toStdString() + " | 年龄: " + std::to_string(ageSpin->value()) + " | 性别: " + genderCombo->currentText().toStdString() + " | 联系方式: " + contactEdit->text().toStdString() + " | 状态: " + statusCombo->currentText().toStdString(); std::string fullDetails = "患者ID: " + pid.toStdString() + " | " + oldDetails + "\n" + newDetails; logOperation(LogEntryType::PATIENT_OPERATION, "MODIFY_PATIENT", fullDetails, pid.toStdString()); QMessageBox::information(this, tr("成功"), tr("已成功更新患者 %1").arg(pid)); refreshPatientTable(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::handleRemovePatient() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("删除患者"), tr("请先选择患者")); return; } auto* patient = core_.patientService.findPatient(pid.toStdString()); std::string patientDetails; QString patientName = tr("未知"); if (patient) { patientName = QString::fromStdString(patient->Name); patientDetails = "患者ID: " + patient->PatientID + " | 姓名: " + patient->Name + " | 年龄: " + std::to_string(patient->Age) + " | 性别: " + patient->Gender + " | 联系方式: " + patient->Contact + " | 状态: " + patientStatusToText(patient->Status).toStdString(); } QMessageBox::StandardButton reply = QMessageBox::question( this, tr("确认删除"), tr("确定要删除患者 \"%1\" (ID: %2) 吗?\n此操作无法撤销,将同时删除该患者的所有病例记录。").arg(patientName).arg(pid), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if (reply != QMessageBox::Ok) return; std::string err; if (!core_.patientService.removePatient(pid.toStdString(), err)) { QMessageBox::warning(this, tr("删除失败"), QString::fromStdString(err)); return; } core_.patientCaseService.removeCase(pid.toStdString()); if (!patientDetails.empty()) { logOperation(LogEntryType::PATIENT_OPERATION, "REMOVE_PATIENT", patientDetails, pid.toStdString()); } refreshPatientTable(); refreshDashboard(); } void MainWindow::handleViewPatientCase() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("查看病例信息"), tr("请先选择患者")); return; } auto* dialog = new PatientCaseViewDialog(core_, pid, this); dialog->exec(); } void MainWindow::handleAdmitPatient() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("入院"), tr("请先选择患者")); return; } auto* patient = core_.patientService.findPatient(pid.toStdString()); if (patient && patient->Status == PatientStatus::Inpatient) { QMessageBox::warning(this, tr("入院"), tr("该患者 %1 已经入院,不能重复入院。").arg(pid)); return; } QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("入院 - %1").arg(pid)); dialog->setModal(true); dialog->resize(550, 450); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); form->setSpacing(10); form->setContentsMargins(20, 20, 20, 20); QLabel* patientIdLabel = new QLabel(pid, dialog); patientIdLabel->setEnabled(false); QString currentDate = formatCurrentDate(); QLabel* dateLabel = new QLabel(currentDate, dialog); dateLabel->setEnabled(false); QComboBox* wardCombo = new QComboBox(dialog); wardCombo->addItem(tr("-- 选择病房 --")); core_.wardService.for_eachWard([&wardCombo](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)); }); QComboBox* bedCombo = new QComboBox(dialog); bedCombo->addItem(tr("-- 先选择病房 --")); QTextEdit* reasonEdit = new QTextEdit(dialog); reasonEdit->setMaximumHeight(80); reasonEdit->setPlaceholderText(tr("入院原因")); form->addRow(tr("患者ID:"), patientIdLabel); form->addRow(tr("入院日期:"), dateLabel); form->addRow(tr("选择病房:"), wardCombo); form->addRow(tr("选择床位:"), bedCombo); form->addRow(tr("入院原因:"), reasonEdit); auto* buttonLayout = new QHBoxLayout(); auto* okButton = new QPushButton(tr("确认入院"), dialog); auto* cancelButton = new QPushButton(tr("取消"), dialog); buttonLayout->addWidget(okButton); buttonLayout->addWidget(cancelButton); buttonLayout->addStretch(); form->addRow(buttonLayout); connect(wardCombo, QOverload::of(&QComboBox::currentIndexChanged), [wardCombo, bedCombo, &core = core_](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("-- 选择床位 --")); const auto* wardPtr = ward; for (const auto& bed : wardPtr->Beds) { if (bed.Status == BedStatus::Free) { bedCombo->addItem(QString::fromStdString(bed.BedID), QString::fromStdString(bed.BedID)); } } }); connect(okButton, &QPushButton::clicked, [this, pid, wardCombo, bedCombo, dateLabel, reasonEdit, dialog]() { if (wardCombo->currentIndex() == 0 || bedCombo->currentIndex() == 0) { QMessageBox::warning(this, tr("错误"), tr("请选择病房和床位")); return; } QString wardId = wardCombo->currentData().toString(); QString bedId = bedCombo->currentData().toString(); std::string actualBedId; if (!core_.patientService.admitPatient(wardId.toStdString(), pid.toStdString(), actualBedId)) { QMessageBox::warning(this, tr("入院失败"), tr("无法分配床位,请确认病房和床位是否正确")); return; } auto* patient = core_.patientService.findPatient(pid.toStdString()); if (patient) { core_.patientService.updatePatient(pid.toStdString(), patient->Name, patient->Age, patient->Gender, patient->Contact, PatientStatus::Inpatient); } AdmissionRecord ar(wardId.toStdString(), actualBedId, reasonEdit->toPlainText().toStdString()); core_.patientCaseService.addAdmissionRecord(pid.toStdString(), ar); std::string paymentId; double admissionFee = 1000.0; if (core_.paymentService.createPayment(pid.toStdString(), admissionFee, PaymentMethod::Cash, "入院", wardId.toStdString(), "入院押金 - 病房: " + wardId.toStdString() + ", 床位: " + actualBedId, paymentId)) { if (!core_.paymentService.completePayment(paymentId)) { core_.paymentService.removePayment(paymentId); QMessageBox::warning(this, tr("支付失败"), tr("入院押金支付失败,请重试")); return; } } std::string admitDetails = "患者ID: " + pid.toStdString() + " | 患者姓名: " + (patient ? patient->Name : "未知") + " | 病房ID: " + wardId.toStdString() + " | 床位ID: " + actualBedId + " | 入院原因: " + reasonEdit->toPlainText().toStdString() + " | 入院日期: " + dateLabel->text().toStdString() + " | 患者状态: 住院" + " | 入院押金: " + std::to_string(admissionFee) + " | 支付ID: " + paymentId + " | 支付状态: 已完成"; logOperation(LogEntryType::ADMISSION_RECORD, "ADMIT", admitDetails, pid.toStdString()); // 获取患者姓名和科室名称用于显示 auto* patientPtr = core_.patientService.findPatient(pid.toStdString()); QString patientName = patientPtr ? QString::fromStdString(patientPtr->Name) : pid; auto* ward = core_.wardService.findWard(wardId.toStdString()); QString deptName = ward ? QString::fromStdString(ward->DepartmentID) : wardId; if (ward) { auto* dept = core_.departmentService.findDepartment(ward->DepartmentID); if (dept) { deptName = QString::fromStdString(dept->Name); } } QMessageBox::information(this, tr("成功"), tr("%1(%2) 已成功入院到病房 %3(%4)\n床位: %5\n入院押金: %6 元 (已自动完成支付)") .arg(patientName) .arg(pid) .arg(wardId) .arg(deptName) .arg(QString::fromStdString(actualBedId)) .arg(QString::number(admissionFee, 'f', 2))); refreshWardTable(); refreshPatientTable(); refreshDashboard(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::handleDischargePatient() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("出院"), tr("请先选择患者")); return; } auto* p = core_.patientService.findPatient(pid.toStdString()); if (!p || p->Status != PatientStatus::Inpatient) { QMessageBox::warning(this, tr("出院"), tr("该患者不是住院状态")); return; } Ward* patientWard = nullptr; std::string bedName; core_.wardService.for_eachWard([&](const std::string&, const Ward& ward) { for (const auto& bed : ward.Beds) { if (bed.PatientID == pid.toStdString()) { patientWard = const_cast(&ward); bedName = bed.BedID; return; } } }); if (!patientWard) { QMessageBox::warning(this, tr("出院"), tr("未找到该患者的床位信息")); return; } QString currentDate = formatCurrentDate(); if (!core_.patientService.releasePatient(patientWard->WardID, pid.toStdString())) { QMessageBox::warning(this, tr("出院失败"), tr("无法释放床位")); return; } core_.patientService.updatePatient(pid.toStdString(), p->Name, p->Age, p->Gender, p->Contact, PatientStatus::Discharged); auto* pc = core_.patientCaseService.getCase(pid.toStdString()); if (pc) { for (auto& ar : pc->AdmissionRecords) { if (ar.isCurrentlyAdmitted() && ar.BedID == bedName) { ar.DischargeTime = time(nullptr); ar.DischargeSummary = tr("自动出院").toStdString(); break; } } } std::string dischargeDetails = "患者ID: " + pid.toStdString() + " | 患者姓名: " + (p ? p->Name : "未知") + " | 病房ID: " + patientWard->WardID + " | 床位ID: " + bedName + " | 入院日期: " + formatDate(pc && !pc->AdmissionRecords.empty() ? pc->AdmissionRecords.back().AdmissionTime : 0).toStdString() + " | 出院日期: " + currentDate.toStdString() + " | 患者状态: 出院" + " | 出院原因: 自动出院"; logOperation(LogEntryType::DISCHARGE_RECORD, "DISCHARGE", dischargeDetails, pid.toStdString()); QMessageBox::information(this, tr("出院成功"), tr("患者 %1 已出院,入院日期: %2\n请使用\"缴费\"按钮进行结算。") .arg(pid).arg(formatDate(pc && !pc->AdmissionRecords.empty() ? pc->AdmissionRecords.back().AdmissionTime : 0))); refreshWardTable(); refreshPatientTable(); refreshDashboard(); updatePatientCaseDetail(); } void MainWindow::handleAddDiagnosisRecord() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("新增诊断"), tr("请先选择患者")); return; } // 检查患者状态:未挂号或已出院禁止添加诊断记录 auto* patient = core_.patientService.findPatient(pid.toStdString()); if (!patient) { QMessageBox::warning(this, tr("新增诊断"), tr("患者不存在")); return; } if (patient->Status == PatientStatus::Unregistered) { QMessageBox::warning(this, tr("新增诊断"), tr("该患者当前状态为\"未挂号\",禁止添加诊断记录。\n请先为该患者进行挂号操作。")); return; } if (patient->Status == PatientStatus::Discharged) { QMessageBox::warning(this, tr("新增诊断"), tr("该患者当前状态为\"已出院\",禁止添加诊断记录。")); return; } struct DoctorInfo { std::string id; std::string name; std::string dept; }; std::vector doctors; core_.doctorService.for_eachDoctor([&doctors](const std::string&, const Doctor& d) { doctors.push_back({d.DoctorID, d.Name, d.DepartmentID}); }); if (doctors.empty()) { QMessageBox::warning(this, tr("错误"), tr("系统中没有医生,请先添加医生")); return; } QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("添加诊断记录 - %1").arg(pid)); dialog->setModal(true); dialog->resize(500, 400); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); form->setSpacing(10); form->setContentsMargins(20, 20, 20, 20); QComboBox* doctorCombo = new QComboBox(dialog); doctorCombo->addItem(tr("-- 请选择医生 --"), QVariant()); for (const auto& doc : doctors) { QString deptName = QString::fromStdString(doc.dept); auto* dept = core_.departmentService.findDepartment(doc.dept); if (dept) { deptName = QString::fromStdString(dept->Name); } QString display = QString("%1 (%2) - %3").arg(QString::fromStdString(doc.name)).arg(deptName).arg(QString::fromStdString(doc.id)); doctorCombo->addItem(display, QString::fromStdString(doc.id)); } QString currentDate = formatCurrentDate(); QLabel* dateLabel = new QLabel(currentDate, dialog); dateLabel->setEnabled(false); QTextEdit* diagnosisEdit = new QTextEdit(dialog); diagnosisEdit->setMinimumHeight(60); QTextEdit* prescriptionEdit = new QTextEdit(dialog); prescriptionEdit->setMinimumHeight(60); QLineEdit* remarksEdit = new QLineEdit(dialog); form->addRow(tr("选择医生:"), doctorCombo); form->addRow(tr("诊断日期:"), dateLabel); form->addRow(tr("诊断:"), diagnosisEdit); form->addRow(tr("处方:"), prescriptionEdit); form->addRow(tr("备注:"), remarksEdit); auto* buttonLayout = new QHBoxLayout(); auto* okButton = new QPushButton(tr("保存"), dialog); auto* cancelButton = new QPushButton(tr("取消"), dialog); buttonLayout->addWidget(okButton); buttonLayout->addWidget(cancelButton); buttonLayout->addStretch(); form->addRow(buttonLayout); connect(okButton, &QPushButton::clicked, [this, pid, doctorCombo, diagnosisEdit, prescriptionEdit, remarksEdit, dialog]() { if (doctorCombo->currentIndex() <= 0) { QMessageBox::warning(this, tr("错误"), tr("请选择医生")); return; } if (diagnosisEdit->toPlainText().isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请填写诊断内容")); return; } QString doctorId = doctorCombo->itemData(doctorCombo->currentIndex()).toString(); DiagnosisRecord record( doctorId.toStdString(), diagnosisEdit->toPlainText().toStdString(), prescriptionEdit->toPlainText().toStdString(), remarksEdit->text().toStdString() ); if (!core_.patientCaseService.addDiagnosisRecord(pid.toStdString(), record)) { QMessageBox::warning(this, tr("新增失败"), tr("诊断记录添加失败")); return; } std::string diagDetails = "患者ID: " + pid.toStdString() + " | 医生ID: " + doctorId.toStdString() + " | 诊断内容: " + diagnosisEdit->toPlainText().toStdString() + " | 处方: " + prescriptionEdit->toPlainText().toStdString() + " | 备注: " + remarksEdit->text().toStdString(); logOperation(LogEntryType::DIAGNOSIS_RECORD, "ADD_DIAGNOSIS", diagDetails, pid.toStdString()); // 获取患者和医生信息用于显示 auto* patient = core_.patientService.findPatient(pid.toStdString()); QString patientName = patient ? QString::fromStdString(patient->Name) : pid; auto* doctor = core_.doctorService.findDoctor(doctorId.toStdString()); QString doctorName = doctor ? QString::fromStdString(doctor->Name) : doctorId; QMessageBox::information(this, tr("成功"), tr("%1(%2) 已成功诊断\n医生: %3(%4)\n诊断内容: %5") .arg(patientName) .arg(pid) .arg(doctorName) .arg(doctorId) .arg(diagnosisEdit->toPlainText())); if (patient && (patient->Status == PatientStatus::Outpatient || patient->Status == PatientStatus::Emergency || patient->Status == PatientStatus::Visited)) { core_.patientService.updatePatient(pid.toStdString(), patient->Name, patient->Age, patient->Gender, patient->Contact, PatientStatus::Unregistered); refreshPatientTable(); QMessageBox::information(this, tr("诊断完成"), tr("诊断已完成,患者 %1(%2) 已回到\"未挂号\"状态。\n如需继续就诊,请重新挂号。") .arg(patientName) .arg(pid)); } updatePatientCaseDetail(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::handleAddCheckRecord() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("添加检查记录"), tr("请先选择患者")); return; } // 检查患者状态:未挂号或已出院禁止添加检查记录 auto* patient = core_.patientService.findPatient(pid.toStdString()); if (!patient) { QMessageBox::warning(this, tr("添加检查记录"), tr("患者不存在")); return; } if (patient->Status == PatientStatus::Unregistered) { QMessageBox::warning(this, tr("添加检查记录"), tr("该患者当前状态为\"未挂号\",禁止添加检查记录。\n请先为该患者进行挂号操作。")); return; } if (patient->Status == PatientStatus::Discharged) { QMessageBox::warning(this, tr("添加检查记录"), tr("该患者当前状态为\"已出院\",禁止添加检查记录。")); return; } auto* pc = core_.patientCaseService.getCase(pid.toStdString()); if (!pc) { QMessageBox::warning(this, tr("添加检查记录"), tr("该患者没有病例记录。\n请先为该患者创建病例后再添加检查记录。")); return; } auto* dialog = new CheckRecordAddDialog(core_, pid, this); int result = dialog->exec(); QString checkId; QString doctorId; if (result == QDialog::Accepted) { checkId = dialog->getCheckId(); doctorId = dialog->getDoctorId(); } delete dialog; if (result != QDialog::Accepted) { return; } if (checkId.isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请选择检查项目")); return; } if (doctorId.isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请选择医生")); return; } auto* check = core_.checkService.findCheck(checkId.toStdString()); if (!check) { QMessageBox::warning(this, tr("错误"), tr("检查项目不存在")); return; } CheckRecord record( checkId.toStdString(), check->Name, check->DepartmentID, check->Price, doctorId.toStdString() ); if (!core_.patientCaseService.addCheckRecord(pid.toStdString(), record)) { QMessageBox::warning(this, tr("错误"), tr("检查记录添加失败")); return; } std::string paymentId; if (core_.paymentService.createPayment(pid.toStdString(), check->Price, PaymentMethod::Cash, "检查", checkId.toStdString(), "检查: " + check->Name, paymentId)) { if (!core_.paymentService.completePayment(paymentId)) { core_.paymentService.removePayment(paymentId); QMessageBox::warning(this, tr("支付失败"), tr("检查费用支付失败,请重试")); return; } } std::string checkDetails = "患者ID: " + pid.toStdString() + " | 检查ID: " + checkId.toStdString() + " | 检查名: " + check->Name + " | 科室ID: " + check->DepartmentID + " | 价格: " + std::to_string(check->Price) + " | 医生ID: " + doctorId.toStdString() + " | 支付ID: " + paymentId + " | 支付状态: 已完成"; logOperation(LogEntryType::CHECK_RECORD, "ADD_CHECK_RECORD", checkDetails, pid.toStdString()); QMessageBox::information(this, tr("成功"), tr("检查记录已添加\n检查: %1\n科室: %2\n价格: %3\n已自动完成支付") .arg(QString::fromStdString(check->Name)) .arg(QString::fromStdString(check->DepartmentID)) .arg(QString::number(check->Price, 'f', 2))); updatePatientCaseDetail(); } void MainWindow::handleRegistration() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("挂号"), tr("请先选择患者")); return; } const auto* patient = core_.patientService.findPatient(pid.toStdString()); if (!patient) { QMessageBox::warning(this, tr("挂号"), tr("患者不存在")); return; } RegistrationDialog dialog(core_, pid, this); int result = dialog.exec(); if (result == QDialog::Accepted) { QString deptId = dialog.getDepartmentId(); QString doctorId = dialog.getDoctorId(); int regType = dialog.getRegistrationType(); if (!deptId.isEmpty() && !doctorId.isEmpty()) { const auto* doctor = core_.doctorService.findDoctor(doctorId.toStdString()); const auto* dept = core_.departmentService.findDepartment(deptId.toStdString()); if (doctor && dept) { double registrationFee = (regType == 1) ? 50.0 : 10.0; QString regTypeName = (regType == 1) ? tr("急诊") : tr("门诊"); PatientStatus newStatus = (regType == 1) ? PatientStatus::Emergency : PatientStatus::Outpatient; auto* patientPtr = core_.patientService.findPatient(pid.toStdString()); if (patientPtr) { core_.patientService.updatePatient(pid.toStdString(), patientPtr->Name, patientPtr->Age, patientPtr->Gender, patientPtr->Contact, newStatus); } std::string regDetails = "患者ID: " + pid.toStdString() + " | 患者姓名: " + patient->Name + " | 挂号类型: " + regTypeName.toStdString() + " | 科室: " + dept->Name + " | 医生ID: " + doctorId.toStdString() + " | 医生姓名: " + doctor->Name + " | 医生职称: " + (doctor->Title == DoctorTitle::Chief ? std::string("主任医师") : doctor->Title == DoctorTitle::AssociateChief ? std::string("副主任医师") : doctor->Title == DoctorTitle::Attending ? std::string("主治医师") : std::string("住院医师")); std::string paymentId; if (core_.paymentService.createPayment(pid.toStdString(), registrationFee, PaymentMethod::Cash, "挂号", doctorId.toStdString(), "挂号 - 类型: " + regTypeName.toStdString() + ", 科室: " + dept->Name + ", 医生: " + doctor->Name, paymentId)) { if (!core_.paymentService.completePayment(paymentId)) { core_.paymentService.removePayment(paymentId); QMessageBox::warning(this, tr("支付失败"), tr("挂号费支付失败,请重试")); return; } } regDetails += " | 挂号费: " + std::to_string(registrationFee) + " | 支付ID: " + paymentId + " | 支付状态: 已完成"; logOperation(LogEntryType::PATIENT_OPERATION, "REGISTRATION", regDetails, pid.toStdString()); // 获取患者姓名用于显示 QString patientName = patient ? QString::fromStdString(patient->Name) : pid; QMessageBox::information(this, tr("挂号成功"), tr("%1(%2) 已成功挂号\n挂号类型: %3\n医生: %4(%5)\n科室: %6\n挂号费: %7 元 (已自动完成支付)") .arg(patientName) .arg(pid) .arg(regTypeName) .arg(QString::fromStdString(doctor->Name)) .arg(QString::fromStdString(doctorId.toStdString())) .arg(QString::fromStdString(dept->Name)) .arg(QString::number(registrationFee, 'f', 2))); refreshPatientTable(); updatePatientCaseDetail(); } } } } void MainWindow::handleAddMedicineRecord() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("添加用药记录"), tr("请先选择患者")); return; } auto* pc = core_.patientCaseService.getCase(pid.toStdString()); if (!pc || pc->AppointmentRecords.empty()) { QMessageBox::warning(this, tr("添加用药记录"), tr("该患者尚未挂号(没有预约记录)。\n请先为该患者进行挂号操作后再添加用药记录。")); return; } auto* dialog = new MedicineRecordAddDialog(core_, pid, this); int result = dialog->exec(); QString medicineId; int quantity; QString usage; QString doctorId; if (result == QDialog::Accepted) { medicineId = dialog->getMedicineId(); quantity = dialog->getQuantity(); usage = dialog->getUsage(); doctorId = dialog->getDoctorId(); } delete dialog; if (result != QDialog::Accepted) { return; } if (medicineId.isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请选择药品")); return; } if (doctorId.isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请选择医生")); return; } auto* med = core_.medicineService.findMedicine(medicineId.toStdString()); if (!med) { QMessageBox::warning(this, tr("错误"), tr("药品不存在")); return; } if (med->StockQuantity < quantity) { QMessageBox::warning(this, tr("库存不足"), tr("药品 %1 库存不足,当前库存: %2,需要: %3") .arg(QString::fromStdString(med->GenericName)) .arg(med->StockQuantity) .arg(quantity)); return; } MedicineRecord record( medicineId.toStdString(), med->GenericName, quantity, usage.toStdString(), med->UnitPrice, doctorId.toStdString() ); std::string paymentId; double totalPrice = record.getTotalPrice(); if (!core_.paymentService.createPayment(pid.toStdString(), totalPrice, PaymentMethod::Cash, "用药", medicineId.toStdString(), "药品: " + med->GenericName + ", 数量: " + std::to_string(quantity), paymentId)) { QMessageBox::warning(this, tr("支付失败"), tr("创建支付记录失败")); return; } if (!core_.paymentService.completePayment(paymentId)) { core_.paymentService.removePayment(paymentId); QMessageBox::warning(this, tr("支付失败"), tr("药品费用支付失败,请重试")); return; } if (!core_.medicineService.decreaseStock(medicineId.toStdString(), quantity)) { core_.paymentService.refundPayment(paymentId); QMessageBox::warning(this, tr("错误"), tr("库存扣除失败,已退款")); return; } if (!core_.patientCaseService.addMedicineRecord(pid.toStdString(), record)) { core_.medicineService.increaseStock(medicineId.toStdString(), quantity); QMessageBox::warning(this, tr("错误"), tr("用药记录添加失败")); return; } std::string medDetails = "患者ID: " + pid.toStdString() + " | 药品ID: " + medicineId.toStdString() + " | 药品名: " + std::string(med->GenericName.c_str()) + " | 数量: " + std::to_string(quantity) + " | 用法: " + usage.toStdString() + " | 单价: " + std::to_string(med->UnitPrice) + " | 总价: " + std::to_string(totalPrice) + " | 医生ID: " + doctorId.toStdString() + " | 支付ID: " + paymentId + " | 支付状态: 已完成" + " | 变更库存: " + std::to_string(quantity) + "份 (扣除)"; logOperation(LogEntryType::MEDICINE_RECORD, "ADD_MEDICINE", medDetails, pid.toStdString()); QMessageBox::information(this, tr("成功"), tr("用药记录已添加\n药品: %1\n数量: %2\n总价: %3\n已自动完成支付") .arg(QString::fromStdString(med->GenericName)) .arg(quantity) .arg(QString::number(totalPrice, 'f', 2))); if (pc && pc->getDiagnosisRecordCount() > 0 && pc->getMedicineRecordCount() > 0) { auto* patient = core_.patientService.findPatient(pid.toStdString()); if (patient && patient->Status == PatientStatus::Outpatient) { core_.patientService.updatePatient(pid.toStdString(), patient->Name, patient->Age, patient->Gender, patient->Contact, PatientStatus::Visited); refreshPatientTable(); } } refreshMedicineTable(); updatePatientCaseDetail(); } void MainWindow::handlePatientPayment() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("缴费"), tr("请先选择患者")); return; } auto* p = core_.patientService.findPatient(pid.toStdString()); if (!p) { QMessageBox::warning(this, tr("缴费"), tr("患者不存在")); return; } // 检查患者状态:只有出院或门诊状态才能缴费 if (p->Status != PatientStatus::Discharged && p->Status != PatientStatus::Outpatient && p->Status != PatientStatus::Visited && p->Status != PatientStatus::Emergency) { QString statusText = patientStatusToText(p->Status); QString message; if (p->Status == PatientStatus::Inpatient) { message = tr("该患者当前状态为\"%1\",请先办理出院后再缴费。").arg(statusText); } else { message = tr("该患者当前状态为\"%1\",请先进行挂号后再缴费。").arg(statusText); } QMessageBox::warning(this, tr("缴费"), message); return; } auto* pc = core_.patientCaseService.getCase(pid.toStdString()); if (!pc) { QMessageBox::warning(this, tr("缴费"), tr("该患者没有病例记录")); return; } QString currentDate = formatCurrentDate(); QString patientName = p ? QString::fromStdString(p->Name) : QString(); double totalAmount = 0.0; std::vector> feeItems; for (const auto& record : pc->AppointmentRecords) { bool hasPayment = false; core_.paymentManagementService.getAllPayments([&](const Payment& payment) { if (payment.PatientID == pid.toStdString() && payment.OperationID == record.DoctorID && payment.PaymentType == "挂号") { hasPayment = true; } }); if (!hasPayment) { double regFee = 10.0; totalAmount += regFee; feeItems.push_back({tr("挂号费"), regFee}); } } for (const auto& record : pc->CheckRecords) { bool hasPayment = false; core_.paymentManagementService.getAllPayments([&](const Payment& payment) { if (payment.PatientID == pid.toStdString() && payment.OperationID == record.CheckID) { hasPayment = true; } }); if (!hasPayment) { totalAmount += record.Price; feeItems.push_back({QString::fromStdString(record.CheckName), record.Price}); } } for (const auto& record : pc->MedicineRecords) { double itemTotal = record.UnitPrice * record.Quantity; bool hasPayment = false; core_.paymentManagementService.getAllPayments([&](const Payment& payment) { if (payment.PatientID == pid.toStdString() && payment.OperationID == record.MedicineID) { hasPayment = true; } }); if (!hasPayment) { totalAmount += itemTotal; feeItems.push_back({QString::fromStdString(record.MedicineName), itemTotal}); } } std::vector depositPaymentIds; for (const auto& record : pc->AdmissionRecords) { double days = 1.0; if (record.DischargeTime > 0 && record.AdmissionTime > 0) { days = std::max(1.0, std::difftime(record.DischargeTime, record.AdmissionTime) / 86400.0); } double hospitalFee = days * 100.0; double admissionDeposit = 0.0; std::string depositPaymentId; core_.paymentManagementService.getAllPayments([&](const Payment& payment) { if (payment.PatientID == pid.toStdString() && payment.PaymentType == "入院" && payment.OperationID == record.WardID && payment.Status == "Completed") { admissionDeposit = payment.Amount; depositPaymentId = payment.PaymentID; } }); double netFee = hospitalFee - admissionDeposit; totalAmount += netFee; feeItems.push_back({tr("住院费"), hospitalFee}); if (admissionDeposit > 0) { feeItems.push_back({tr("押金退回"), -admissionDeposit}); if (!depositPaymentId.empty()) { depositPaymentIds.push_back(depositPaymentId); } } } // 处理手术费用(从手术记录中读取费用) for (size_t i = 0; i < pc->SurgeryRecords.size(); ++i) { const auto& record = pc->SurgeryRecords[i]; if (record.Fee > 0) { totalAmount += record.Fee; feeItems.push_back({QString::fromStdString(record.SurgeryName), record.Fee}); } } if (feeItems.empty()) { QMessageBox::information(this, tr("缴费"), tr("患者 %1 当前没有需要结算的费用。").arg(pid)); return; } std::string settlementId; if (!core_.settlementService.generateSettlement(pid.toStdString(), patientName.toStdString(), currentDate.toStdString(), settlementId)) { QMessageBox::warning(this, tr("缴费"), tr("结算单生成失败")); return; } for (const auto& item : feeItems) { SettlementItem settlementItem(item.first.toStdString(), "费用", "", 1, item.second, ""); core_.settlementService.addItemToSettlement(settlementId, settlementItem); } core_.settlementService.calculateSettlement(settlementId); const Settlement* finalSettlement = core_.settlementService.findSettlement(settlementId); double finalAmount = finalSettlement ? finalSettlement->TotalAmount : totalAmount; std::string paymentId; if (core_.paymentService.createPayment(pid.toStdString(), finalAmount, PaymentMethod::Cash, "结算", settlementId, "综合结算 - 患者: " + patientName.toStdString(), paymentId)) { if (!core_.paymentService.completePayment(paymentId)) { core_.paymentService.removePayment(paymentId); QMessageBox::warning(this, tr("支付失败"), tr("结算支付失败,请重试")); return; } } core_.settlementService.completeSettlement(settlementId); for (const auto& depositPaymentId : depositPaymentIds) { core_.paymentManagementService.processRefund(depositPaymentId, "入院押金退款-出院结算"); } std::string paymentDetails = "患者ID: " + pid.toStdString() + " | 患者姓名: " + patientName.toStdString() + " | 结算单ID: " + settlementId + " | 支付ID: " + paymentId + " | 总金额: " + std::to_string(totalAmount) + " | 费用项数量: " + std::to_string(feeItems.size()) + " | 押金退款记录数: " + std::to_string(depositPaymentIds.size()) + " | 操作: 缴费结算"; logOperation(LogEntryType::SYSTEM_EVENT, "SETTLEMENT", paymentDetails, pid.toStdString()); QMessageBox::StandardButton reply = QMessageBox::question( this, tr("缴费成功"), tr("患者 %1 缴费成功\n总金额: ¥%2\n费用项: %3项\n是否查看结算单?") .arg(pid) .arg(QString::number(totalAmount, 'f', 2)) .arg(feeItems.size()), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { SettlementDialog dialog(core_, this); dialog.displaySettlement(QString::fromStdString(settlementId)); dialog.exec(); } core_.patientService.updatePatient(pid.toStdString(), p->Name, p->Age, p->Gender, p->Contact, PatientStatus::Unregistered); refreshPatientTable(); refreshDashboard(); } void MainWindow::handleAddAdmissionRecord() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("添加入院记录"), tr("请先选择患者")); return; } QMessageBox::information(this, tr("添加入院记录"), tr("功能开发中...")); } void MainWindow::handleAddSurgeryRecord() { QString pid = safeCurrentTableId(patientTable_); if (pid.isEmpty()) { QMessageBox::warning(this, tr("添加手术记录"), tr("请先选择患者")); return; } // 检查患者状态:只有入院状态才允许添加手术记录 auto* patient = core_.patientService.findPatient(pid.toStdString()); if (!patient) { QMessageBox::warning(this, tr("添加手术记录"), tr("患者不存在")); return; } if (patient->Status != PatientStatus::Inpatient) { QMessageBox::warning(this, tr("添加手术记录"), tr("该患者当前状态为\"%1\",禁止添加手术记录。\n只有入院状态的患者才能添加手术记录。") .arg(patientStatusToText(patient->Status))); return; } struct DoctorInfo { std::string id; std::string name; std::string dept; }; std::vector doctors; core_.doctorService.for_eachDoctor([&doctors](const std::string&, const Doctor& d) { doctors.push_back({d.DoctorID, d.Name, d.DepartmentID}); }); if (doctors.empty()) { QMessageBox::warning(this, tr("错误"), tr("系统中没有医生,请先添加医生")); return; } QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("添加手术记录 - %1").arg(pid)); dialog->setModal(true); dialog->resize(600, 500); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); form->setSpacing(10); form->setContentsMargins(20, 20, 20, 20); // 主刀医生选择 QComboBox* surgeonCombo = new QComboBox(dialog); surgeonCombo->addItem(tr("-- 请选择主刀医生 --"), QVariant()); for (const auto& doc : doctors) { QString deptName = QString::fromStdString(doc.dept); auto* dept = core_.departmentService.findDepartment(doc.dept); if (dept) { deptName = QString::fromStdString(dept->Name); } QString display = QString("%1 (%2) - %3").arg(QString::fromStdString(doc.name)).arg(deptName).arg(QString::fromStdString(doc.id)); surgeonCombo->addItem(display, QString::fromStdString(doc.id)); } // 助手医生选择(可选) QComboBox* assistantCombo = new QComboBox(dialog); assistantCombo->addItem(tr("-- 助手医生(可选) --"), QVariant()); for (const auto& doc : doctors) { QString deptName = QString::fromStdString(doc.dept); auto* dept = core_.departmentService.findDepartment(doc.dept); if (dept) { deptName = QString::fromStdString(dept->Name); } QString display = QString("%1 (%2) - %3").arg(QString::fromStdString(doc.name)).arg(deptName).arg(QString::fromStdString(doc.id)); assistantCombo->addItem(display, QString::fromStdString(doc.id)); } // 麻醉医生选择 QComboBox* anesthesiologistCombo = new QComboBox(dialog); anesthesiologistCombo->addItem(tr("-- 请选择麻醉医生 --"), QVariant()); for (const auto& doc : doctors) { QString deptName = QString::fromStdString(doc.dept); auto* dept = core_.departmentService.findDepartment(doc.dept); if (dept) { deptName = QString::fromStdString(dept->Name); } QString display = QString("%1 (%2) - %3").arg(QString::fromStdString(doc.name)).arg(deptName).arg(QString::fromStdString(doc.id)); anesthesiologistCombo->addItem(display, QString::fromStdString(doc.id)); } QString currentDate = formatCurrentDate(); QLabel* dateLabel = new QLabel(currentDate, dialog); dateLabel->setEnabled(false); // 手术名称 QLineEdit* surgeryNameEdit = new QLineEdit(dialog); surgeryNameEdit->setPlaceholderText(tr("如:阑尾切除术")); // 手术类型 QComboBox* surgeryTypeCombo = new QComboBox(dialog); surgeryTypeCombo->addItems({tr("开放手术"), tr("微创手术"), tr("腔镜手术"), tr("介入手术"), tr("其他")}); // 麻醉方式 QComboBox* anesthesiaTypeCombo = new QComboBox(dialog); anesthesiaTypeCombo->addItems({tr("全身麻醉"), tr("局部麻醉"), tr("椎管内麻醉"), tr("神经阻滞麻醉"), tr("复合麻醉"), tr("其他")}); // 术前诊断 QTextEdit* diagnosisEdit = new QTextEdit(dialog); diagnosisEdit->setMinimumHeight(50); diagnosisEdit->setPlaceholderText(tr("术前诊断")); // 手术过程 QTextEdit* procedureEdit = new QTextEdit(dialog); procedureEdit->setMinimumHeight(80); procedureEdit->setPlaceholderText(tr("详细描述手术过程")); // 术中并发症 QLineEdit* complicationsEdit = new QLineEdit(dialog); complicationsEdit->setPlaceholderText(tr("如无并发症请填写'无'")); // 出血量 QLineEdit* bloodLossEdit = new QLineEdit(dialog); bloodLossEdit->setPlaceholderText(tr("如:200ml")); // 手术时长 QSpinBox* durationSpin = new QSpinBox(dialog); durationSpin->setRange(0, 1440); durationSpin->setSuffix(tr(" 分钟")); durationSpin->setValue(60); // 备注 QLineEdit* remarksEdit = new QLineEdit(dialog); remarksEdit->setPlaceholderText(tr("其他备注")); // 手术金额 QLineEdit* surgeryFeeEdit = new QLineEdit(dialog); surgeryFeeEdit->setPlaceholderText(tr("手术费用(元)")); surgeryFeeEdit->setValidator(new QDoubleValidator(0, 999999, 2, dialog)); surgeryFeeEdit->setText("0.00"); form->addRow(tr("主刀医生:"), surgeonCombo); form->addRow(tr("助手医生:"), assistantCombo); form->addRow(tr("麻醉医生:"), anesthesiologistCombo); form->addRow(tr("手术日期:"), dateLabel); form->addRow(tr("手术名称:"), surgeryNameEdit); form->addRow(tr("手术类型:"), surgeryTypeCombo); form->addRow(tr("麻醉方式:"), anesthesiaTypeCombo); form->addRow(tr("术前诊断:"), diagnosisEdit); form->addRow(tr("手术过程:"), procedureEdit); form->addRow(tr("术中并发症:"), complicationsEdit); form->addRow(tr("出血量:"), bloodLossEdit); form->addRow(tr("手术时长:"), durationSpin); form->addRow(tr("手术金额:"), surgeryFeeEdit); form->addRow(tr("备注:"), remarksEdit); auto* buttonLayout = new QHBoxLayout(); auto* okButton = new QPushButton(tr("保存"), dialog); auto* cancelButton = new QPushButton(tr("取消"), dialog); buttonLayout->addWidget(okButton); buttonLayout->addWidget(cancelButton); buttonLayout->addStretch(); form->addRow(buttonLayout); connect(okButton, &QPushButton::clicked, [this, pid, dialog, surgeonCombo, assistantCombo, anesthesiologistCombo, surgeryNameEdit, surgeryTypeCombo, anesthesiaTypeCombo, diagnosisEdit, procedureEdit, complicationsEdit, bloodLossEdit, durationSpin, remarksEdit, surgeryFeeEdit]() { if (surgeonCombo->currentIndex() <= 0) { QMessageBox::warning(this, tr("错误"), tr("请选择主刀医生")); return; } if (surgeryNameEdit->text().isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请填写手术名称")); return; } if (diagnosisEdit->toPlainText().isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请填写术前诊断")); return; } if (procedureEdit->toPlainText().isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请填写手术过程")); return; } if (anesthesiologistCombo->currentIndex() <= 0) { QMessageBox::warning(this, tr("错误"), tr("请选择麻醉医生")); return; } QString surgeonId = surgeonCombo->itemData(surgeonCombo->currentIndex()).toString(); QString assistantId = assistantCombo->currentIndex() > 0 ? assistantCombo->itemData(assistantCombo->currentIndex()).toString() : ""; QString anesthesiologistId = anesthesiologistCombo->itemData(anesthesiologistCombo->currentIndex()).toString(); // 获取手术费用 double surgeryFee = surgeryFeeEdit->text().toDouble(); SurgeryRecord record; record.SurgeryName = surgeryNameEdit->text().toStdString(); record.SurgeryType = surgeryTypeCombo->currentText().toStdString(); record.SurgeonID = surgeonId.toStdString(); record.AssistantDoctorID = assistantId.toStdString(); record.AnesthesiaType = anesthesiaTypeCombo->currentText().toStdString(); record.AnesthesiologistID = anesthesiologistId.toStdString(); record.Diagnosis = diagnosisEdit->toPlainText().toStdString(); record.Procedure = procedureEdit->toPlainText().toStdString(); record.Complications = complicationsEdit->text().isEmpty() ? "无" : complicationsEdit->text().toStdString(); record.BloodLoss = bloodLossEdit->text().isEmpty() ? "未知" : bloodLossEdit->text().toStdString(); record.Duration = durationSpin->value(); record.Remarks = remarksEdit->text().toStdString(); record.SurgeryTime = std::time(nullptr); record.Fee = surgeryFee; // 保存手术费用到记录中 if (!core_.patientCaseService.addSurgeryRecord(pid.toStdString(), record)) { QMessageBox::warning(this, tr("新增失败"), tr("手术记录添加失败")); return; } std::string surgeryDetails = "患者ID: " + pid.toStdString() + " | 手术名称: " + record.SurgeryName + " | 手术类型: " + record.SurgeryType + " | 主刀医生ID: " + record.SurgeonID + " | 助手医生ID: " + record.AssistantDoctorID + " | 麻醉方式: " + record.AnesthesiaType + " | 麻醉医生ID: " + record.AnesthesiologistID + " | 术前诊断: " + record.Diagnosis + " | 手术时长: " + std::to_string(record.Duration) + "分钟" + " | 出血量: " + record.BloodLoss + " | 并发症: " + record.Complications + " | 手术费用: " + std::to_string(surgeryFee) + " | 备注: " + record.Remarks; logOperation(LogEntryType::SURGERY_RECORD, "ADD_SURGERY", surgeryDetails, pid.toStdString()); QString successMsg = tr("手术记录已添加\n手术名称: %1\n手术时长: %2分钟\n手术费用: ¥%3") .arg(surgeryNameEdit->text()) .arg(durationSpin->value()) .arg(QString::number(surgeryFee, 'f', 2)); if (surgeryFee > 0) { successMsg += tr("\n(手术费用将在\"缴费\"时一并结算)"); } QMessageBox::information(this, tr("成功"), successMsg); updatePatientCaseDetail(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::onPatientSearch(const QString& text) { for (int i = 0; i < patientTable_->rowCount(); ++i) { bool match = false; for (int j = 0; j < patientTable_->columnCount(); ++j) { QTableWidgetItem* item = patientTable_->item(i, j); if (item && item->text().contains(text, Qt::CaseInsensitive)) { match = true; break; } } patientTable_->setRowHidden(i, !match); } }