#include "../mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/his_core.h" #include "core/medicine_service.h" void MainWindow::setupMedicinesTab() { medicinesTab_ = new QWidget(this); auto* layout = new QVBoxLayout(medicinesTab_); layout->setContentsMargins(20, 20, 20, 20); layout->setSpacing(15); auto* searchContainer = new QWidget(medicinesTab_); searchContainer->setStyleSheet("background-color: #1f2937; border-radius: 8px; padding: 10px;"); auto* searchLayout = new QHBoxLayout(searchContainer); searchLayout->setContentsMargins(10, 5, 10, 5); medicineSearchBox_ = new QLineEdit(searchContainer); medicineSearchBox_->setPlaceholderText(tr("🔍 搜索药品...")); medicineSearchBox_->setStyleSheet("padding: 8px 15px; border-radius: 6px; border: 1px solid #374151; background-color: #111827; color: #f3f4f6;"); connect(medicineSearchBox_, &QLineEdit::textChanged, this, &MainWindow::onMedicineSearch); searchLayout->addWidget(medicineSearchBox_); layout->addWidget(searchContainer); auto* buttonContainer = new QWidget(medicinesTab_); buttonContainer->setStyleSheet("background-color: transparent;"); auto* buttonLayout = new QHBoxLayout(buttonContainer); buttonLayout->setContentsMargins(0, 10, 0, 10); medicineAddButton_ = new QPushButton(tr("+ 添加药品"), buttonContainer); medicineUpdateButton_ = new QPushButton(tr("✎ 更新药品"), buttonContainer); medicineRemoveButton_ = new QPushButton(tr("🗑 删除药品"), buttonContainer); medicineIncreaseStockButton_ = new QPushButton(tr("📥 增加库存"), buttonContainer); medicineDecreaseStockButton_ = new QPushButton(tr("📤 减少库存"), buttonContainer); QString buttonStyle = R"( QPushButton { background-color: #374151; color: #f3f4f6; border: 1px solid #4b5563; border-radius: 6px; padding: 8px 16px; font-size: 13px; } QPushButton:hover { background-color: #4b5563; border-color: #6b7280; } )"; medicineAddButton_->setStyleSheet(buttonStyle); medicineUpdateButton_->setStyleSheet(buttonStyle); medicineRemoveButton_->setStyleSheet(buttonStyle); medicineIncreaseStockButton_->setStyleSheet(buttonStyle); medicineDecreaseStockButton_->setStyleSheet(buttonStyle); buttonLayout->addWidget(medicineAddButton_); buttonLayout->addWidget(medicineUpdateButton_); buttonLayout->addWidget(medicineRemoveButton_); buttonLayout->addWidget(medicineIncreaseStockButton_); buttonLayout->addWidget(medicineDecreaseStockButton_); buttonLayout->addStretch(1); layout->addWidget(buttonContainer); medicineTable_ = new QTableWidget(0, 6, medicinesTab_); medicineTable_->setHorizontalHeaderLabels({tr("药品ID"), tr("通用名称"), tr("品牌名称"), tr("归属科室"), tr("存量"), tr("价格")}); medicineTable_->setSelectionBehavior(QAbstractItemView::SelectRows); medicineTable_->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); medicineTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); medicineTable_->setSortingEnabled(true); layout->addWidget(medicineTable_); connect(medicineAddButton_, &QPushButton::clicked, this, &MainWindow::handleAddMedicine); connect(medicineUpdateButton_, &QPushButton::clicked, this, &MainWindow::handleUpdateMedicine); connect(medicineRemoveButton_, &QPushButton::clicked, this, &MainWindow::handleRemoveMedicine); connect(medicineIncreaseStockButton_, &QPushButton::clicked, this, &MainWindow::handleIncreaseMedicineStock); connect(medicineDecreaseStockButton_, &QPushButton::clicked, this, &MainWindow::handleDecreaseMedicineStock); connect(medicineTable_, &QTableWidget::cellChanged, this, &MainWindow::onMedicineCellChanged); } void MainWindow::onMedicineCellChanged(int row, int column) { QTableWidgetItem* idItem = medicineTable_->item(row, 0); if (!idItem) return; QString mid = idItem->text(); if (mid.isEmpty()) return; auto* m = core_.medicineService.findMedicine(mid.toStdString()); if (!m) return; QTableWidgetItem* genericItem = medicineTable_->item(row, 1); QTableWidgetItem* brandItem = medicineTable_->item(row, 2); QTableWidgetItem* deptItem = medicineTable_->item(row, 3); QTableWidgetItem* stockItem = medicineTable_->item(row, 4); QTableWidgetItem* priceItem = medicineTable_->item(row, 5); std::string modifyDetails; if (genericItem && column == 1) { m->GenericName = genericItem->text().toStdString(); modifyDetails = "修改项: 通用名 | 新值: " + m->GenericName; } if (brandItem && column == 2) { m->BrandName = brandItem->text().toStdString(); modifyDetails = "修改项: 商品名 | 新值: " + m->BrandName; } if (deptItem && column == 3) { // 根据科室名称查找科室ID QString deptName = deptItem->text(); std::string deptId = ""; core_.departmentService.for_eachDepartment([&deptName, &deptId](const std::string& id, const Department& d) { if (QString::fromStdString(d.Name) == deptName) { deptId = id; } }); if (!deptId.empty()) { m->DepartmentID = deptId; modifyDetails = "修改项: 科室 | 新值: " + deptName.toStdString(); } else { QMessageBox::warning(this, tr("科室不存在"), tr("未找到科室: ") + deptName + tr(",请使用有效的科室名称")); refreshMedicineTable(); } } if (stockItem && column == 4) { int oldStock = m->StockQuantity; m->StockQuantity = stockItem->text().toInt(); modifyDetails = "修改项: 库存 | 旧值: " + std::to_string(oldStock) + " | 新值: " + std::to_string(m->StockQuantity); } if (priceItem && column == 5) { double oldPrice = m->UnitPrice; m->UnitPrice = priceItem->text().toDouble(); modifyDetails = "修改项: 单价 | 旧值: " + std::to_string(oldPrice) + " | 新值: " + std::to_string(m->UnitPrice); } if (!modifyDetails.empty()) { std::string details = "药品ID: " + mid.toStdString() + " | 药品名: " + m->GenericName + " | " + modifyDetails; logOperation(LogEntryType::MEDICINE_OPERATION, "MODIFY_MEDICINE", details, mid.toStdString()); } } void MainWindow::refreshMedicineTable() { medicineTable_->blockSignals(true); medicineTable_->setSortingEnabled(false); // 【关键修复】 medicineTable_->setRowCount(0); int row = 0; core_.medicineService.for_eachMedicine([this, &row](const std::string&, const Medicine& med) { medicineTable_->insertRow(row); medicineTable_->setItem(row, 0, new QTableWidgetItem(QString::fromStdString(med.MedicineID))); medicineTable_->setItem(row, 1, new QTableWidgetItem(QString::fromStdString(med.GenericName))); medicineTable_->setItem(row, 2, new QTableWidgetItem(QString::fromStdString(med.BrandName))); // 获取科室名称而不是ID QString deptName = QString::fromStdString(med.DepartmentID); auto* dept = core_.departmentService.findDepartment(med.DepartmentID); if (dept) { deptName = QString::fromStdString(dept->Name); } medicineTable_->setItem(row, 3, new QTableWidgetItem(deptName)); QTableWidgetItem* stockItem = new QTableWidgetItem(); stockItem->setData(Qt::DisplayRole, med.StockQuantity); medicineTable_->setItem(row, 4, stockItem); QTableWidgetItem* priceItem = new QTableWidgetItem(); // 修改:使用QString::number设置固定两位小数格式,确保显示后两位小数(如11111.01) priceItem->setText(QString::number(med.UnitPrice, 'f', 2)); // 同时设置DisplayRole以支持按数字排序 priceItem->setData(Qt::DisplayRole, med.UnitPrice); medicineTable_->setItem(row, 5, priceItem); row++; }); medicineTable_->setSortingEnabled(true); // 【关键修复】 medicineTable_->blockSignals(false); } void MainWindow::handleAddMedicine() { QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("添加药品")); dialog->setModal(true); dialog->resize(450, 350); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); form->setSpacing(10); form->setContentsMargins(20, 20, 20, 20); QString newMedId = QString::fromStdString(Medicine::generateUniqueId()); QLabel* medIdDisplay = new QLabel(newMedId, dialog); medIdDisplay->setEnabled(false); QLineEdit* genericEdit = new QLineEdit(dialog); QLineEdit* brandEdit = new QLineEdit(dialog); QComboBox* deptCombo = new QComboBox(dialog); deptCombo->addItem(tr("-- 请选择科室 --")); core_.departmentService.for_eachDepartment([deptCombo](const std::string&, const Department& d) { deptCombo->addItem(QString::fromStdString(d.Name), QString::fromStdString(d.DepartmentID)); }); QSpinBox* stockSpin = new QSpinBox(dialog); stockSpin->setRange(0, MAX_MEDICINE_STOCK); QDoubleSpinBox* priceSpin = new QDoubleSpinBox(dialog); priceSpin->setRange(0.0, 100000.0); priceSpin->setDecimals(2); QLabel* infoLabel = new QLabel(tr("药品ID将自动生成"), dialog); infoLabel->setStyleSheet("color: gray; font-size: 11px;"); form->addRow(tr("药品ID (自动生成):"), medIdDisplay); form->addRow(tr("通用名:"), genericEdit); form->addRow(tr("商品名:"), brandEdit); form->addRow(tr("选择科室:"), deptCombo); form->addRow(tr("库存:"), stockSpin); form->addRow(tr("单价:"), priceSpin); 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, genericEdit, brandEdit, deptCombo, stockSpin, priceSpin, newMedId]() { if (genericEdit->text().isEmpty()) { QMessageBox::warning(this, tr("错误"), tr("请填写通用名")); return; } if (deptCombo->currentIndex() == 0) { QMessageBox::warning(this, tr("错误"), tr("请选择科室")); return; } Medicine m(newMedId.toStdString(), genericEdit->text().toStdString(), brandEdit->text().toStdString(), {}, stockSpin->value(), deptCombo->currentData().toString().toStdString(), priceSpin->value()); if (!core_.medicineService.addOrUpdateMedicine(m)) { QMessageBox::warning(this, tr("添加失败"), tr("药品 ID 已存在")); return; } // 记录详细日志 - 药品的完整信息 std::string medDetails = "药品ID: " + newMedId.toStdString() + " | 通用名: " + genericEdit->text().toStdString() + " | 商品名: " + brandEdit->text().toStdString() + " | 科室: " + deptCombo->currentText().toStdString() + " | 科室ID: " + deptCombo->currentData().toString().toStdString() + " | 初始库存: " + std::to_string(stockSpin->value()) + " | 单价: " + std::to_string(priceSpin->value()); logOperation(LogEntryType::MEDICINE_OPERATION, "ADD_MEDICINE", medDetails, newMedId.toStdString()); QMessageBox::information(this, tr("成功"), tr("已添加药品: %1 (%2)").arg(newMedId).arg(genericEdit->text())); refreshMedicineTable(); refreshDashboard(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::handleUpdateMedicine() { QString mid = safeCurrentTableId(medicineTable_); if (mid.isEmpty()) { QMessageBox::warning(this, tr("更新药品"), tr("请先选择药品")); return; } auto* m = core_.medicineService.findMedicine(mid.toStdString()); if (!m) return; QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("更新药品 - %1").arg(mid)); dialog->setModal(true); dialog->resize(450, 350); dialog->setAttribute(Qt::WA_DeleteOnClose); QFormLayout* form = new QFormLayout(dialog); QLabel* medIdDisplay = new QLabel(mid, dialog); medIdDisplay->setEnabled(false); QLineEdit* genericEdit = new QLineEdit(QString::fromStdString(m->GenericName), dialog); QLineEdit* brandEdit = new QLineEdit(QString::fromStdString(m->BrandName), dialog); QComboBox* deptCombo = new QComboBox(dialog); deptCombo->addItem(tr("-- 请选择科室 --")); core_.departmentService.for_eachDepartment([deptCombo](const std::string&, const Department& d) { deptCombo->addItem(QString::fromStdString(d.Name), QString::fromStdString(d.DepartmentID)); }); // 设置当前选中的科室 for (int i = 1; i < deptCombo->count(); ++i) { if (deptCombo->itemData(i).toString().toStdString() == m->DepartmentID) { deptCombo->setCurrentIndex(i); break; } } QSpinBox* stockSpin = new QSpinBox(dialog); stockSpin->setRange(0, MAX_MEDICINE_STOCK); stockSpin->setValue(m->StockQuantity); QDoubleSpinBox* priceSpin = new QDoubleSpinBox(dialog); priceSpin->setRange(0.0, 100000.0); priceSpin->setDecimals(2); priceSpin->setValue(m->UnitPrice); form->addRow(tr("药品ID:"), medIdDisplay); form->addRow(tr("通用名:"), genericEdit); form->addRow(tr("商品名:"), brandEdit); form->addRow(tr("选择科室:"), deptCombo); form->addRow(tr("库存:"), stockSpin); form->addRow(tr("单价:"), priceSpin); 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, mid, dialog, m, genericEdit, brandEdit, deptCombo, stockSpin, priceSpin]() { // 记录修改前的信息 std::string oldDetails = "修改前 - 通用名: " + m->GenericName + " | 商品名: " + m->BrandName + " | 科室ID: " + m->DepartmentID + " | 库存: " + std::to_string(m->StockQuantity) + " | 单价: " + std::to_string(m->UnitPrice); if (deptCombo->currentIndex() == 0) { QMessageBox::warning(this, tr("错误"), tr("请选择科室")); return; } if (!core_.medicineService.updateMedicine(mid.toStdString(), genericEdit->text().toStdString(), brandEdit->text().toStdString(), {}, stockSpin->value(), deptCombo->currentData().toString().toStdString(), priceSpin->value())) { QMessageBox::warning(this, tr("更新失败"), tr("操作失败")); return; } // 记录详细日志 - 修改后的信息 std::string newDetails = "修改后 - 通用名: " + genericEdit->text().toStdString() + " | 商品名: " + brandEdit->text().toStdString() + " | 科室: " + deptCombo->currentText().toStdString() + " | 库存: " + std::to_string(stockSpin->value()) + " | 单价: " + std::to_string(priceSpin->value()); std::string fullDetails = "药品ID: " + mid.toStdString() + " | " + oldDetails + "\n" + newDetails; logOperation(LogEntryType::MEDICINE_OPERATION, "MODIFY_MEDICINE", fullDetails, mid.toStdString()); QMessageBox::information(this, tr("成功"), tr("药品 %1 已更新").arg(mid)); refreshMedicineTable(); dialog->accept(); }); connect(cancelButton, &QPushButton::clicked, dialog, &QDialog::reject); dialog->exec(); } void MainWindow::handleRemoveMedicine() { QString mid = safeCurrentTableId(medicineTable_); if (mid.isEmpty()) { QMessageBox::warning(this, tr("删除药品"), tr("请先选择药品")); return; } // 获取药品完整信息用于日志记录和确认弹窗 auto* medicine = core_.medicineService.findMedicine(mid.toStdString()); std::string medDetails; QString medName = tr("未知"); if (medicine) { medName = QString::fromStdString(medicine->GenericName); medDetails = "药品ID: " + medicine->MedicineID + " | 通用名: " + medicine->GenericName + " | 商品名: " + medicine->BrandName + " | 科室ID: " + medicine->DepartmentID + " | 当前库存: " + std::to_string(medicine->StockQuantity) + " | 单价: " + std::to_string(medicine->UnitPrice); } // 确认删除弹窗 QMessageBox::StandardButton reply = QMessageBox::question( this, tr("确认删除"), tr("确定要删除药品 \"%1\" (ID: %2) 吗?\n此操作无法撤销。").arg(medName).arg(mid), QMessageBox::Yes | QMessageBox::No); if (reply != QMessageBox::Yes) return; std::string err; if (!core_.medicineService.removeMedicine(mid.toStdString(), err)) { QMessageBox::warning(this, tr("删除失败"), QString::fromStdString(err)); return; } // 记录详细日志 - 包含被删除药品的完整信息 if (!medDetails.empty()) { logOperation(LogEntryType::MEDICINE_OPERATION, "REMOVE_MEDICINE", medDetails, mid.toStdString()); } refreshMedicineTable(); refreshDashboard(); } void MainWindow::handleIncreaseMedicineStock() { QString mid = safeCurrentTableId(medicineTable_); if (mid.isEmpty()) { QMessageBox::warning(this, tr("增加库存"), tr("请先选择药品")); return; } // 获取药品信息用于日志和检查 auto* medicine = core_.medicineService.findMedicine(mid.toStdString()); if (!medicine) { QMessageBox::warning(this, tr("错误"), tr("药品不存在")); return; } int oldStock = medicine->StockQuantity; int maxAddable = MAX_MEDICINE_STOCK - oldStock; if (maxAddable <= 0) { QMessageBox::warning(this, tr("库存已达上限"), tr("药品 %1 的库存已达上限 %2,无法继续增加。") .arg(QString::fromStdString(medicine->GenericName)) .arg(MAX_MEDICINE_STOCK)); return; } int amount = QInputDialog::getInt(this, tr("增加库存"), tr("增加数量 (最大可增加 %1):").arg(maxAddable), 1, 1, maxAddable); // 最终检查防止用户手动输入超过上限的值 if (oldStock + amount > MAX_MEDICINE_STOCK) { QMessageBox::warning(this, tr("库存超限"), tr("库存不能超过上限 %1。当前库存: %2,增加数量: %3。") .arg(MAX_MEDICINE_STOCK) .arg(oldStock) .arg(amount)); return; } if (!core_.medicineService.increaseStock(mid.toStdString(), amount)) { QMessageBox::warning(this, tr("失败"), tr("库存增加失败")); return; } // 记录详细日志 auto* newMedicine = core_.medicineService.findMedicine(mid.toStdString()); int newStock = newMedicine ? newMedicine->StockQuantity : 0; std::string stockDetails = "药品ID: " + mid.toStdString() + " | 药品名: " + (medicine ? medicine->GenericName : "未知") + " | 增加数量: " + std::to_string(amount) + " | 库存变化: " + std::to_string(oldStock) + " -> " + std::to_string(newStock); logOperation(LogEntryType::MEDICINE_OPERATION, "INCREASE_STOCK", stockDetails, mid.toStdString()); refreshMedicineTable(); } void MainWindow::handleDecreaseMedicineStock() { QString mid = safeCurrentTableId(medicineTable_); if (mid.isEmpty()) { QMessageBox::warning(this, tr("减少库存"), tr("请先选择药品")); return; } // 获取药品信息用于日志 auto* medicine = core_.medicineService.findMedicine(mid.toStdString()); int oldStock = medicine ? medicine->StockQuantity : 0; int amount = QInputDialog::getInt(this, tr("减少库存"), tr("减少数量:"), 1, 1, 100000); if (!core_.medicineService.decreaseStock(mid.toStdString(), amount)) { QMessageBox::warning(this, tr("失败"), tr("库存减少失败:可能库存不足")); return; } // 记录详细日志 auto* newMedicine = core_.medicineService.findMedicine(mid.toStdString()); int newStock = newMedicine ? newMedicine->StockQuantity : 0; std::string stockDetails = "药品ID: " + mid.toStdString() + " | 药品名: " + (medicine ? medicine->GenericName : "未知") + " | 减少数量: " + std::to_string(amount) + " | 库存变化: " + std::to_string(oldStock) + " -> " + std::to_string(newStock); logOperation(LogEntryType::MEDICINE_OPERATION, "DECREASE_STOCK", stockDetails, mid.toStdString()); refreshMedicineTable(); } void MainWindow::onMedicineSearch(const QString& text) { for (int i = 0; i < medicineTable_->rowCount(); ++i) { bool match = false; for (int j = 0; j < medicineTable_->columnCount(); ++j) { QTableWidgetItem* item = medicineTable_->item(i, j); if (item && item->text().contains(text, Qt::CaseInsensitive)) { match = true; break; } } medicineTable_->setRowHidden(i, !match); } }