diff --git a/MainWidget.cpp b/MainWidget.cpp index cccf083..f9f25bd 100644 --- a/MainWidget.cpp +++ b/MainWidget.cpp @@ -3,16 +3,18 @@ #include #include #include +#include +#include #include "src/data_edit.h" #include "./ui_MainWidget.h" constexpr std::size_t g_OnePage = 100; - +namespace fs = std::filesystem; MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget) { ui->setupUi(this); - setWindowTitle(u8"OneLevelXmlOpr v1.2.8"); + setWindowTitle(u8"OneLevelXmlOpr v1.2.9"); setWindowIcon(QIcon("://resource/xml.ico")); setMinimumWidth(900); @@ -45,11 +47,12 @@ MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget read(file); }); connect(ui->btnSearch, &QPushButton::clicked, this, [&]() { search(); }); - connect(ui->btnRead, &QPushButton::clicked, this, - [&]() { read(ui->edStatus->text().trimmed()); }); + connect(ui->btnBackup, &QPushButton::clicked, this, [&]() { backup_file(); }); + connect(ui->btnRead, &QPushButton::clicked, this, [&]() { read(ui->edStatus->text().trimmed()); }); connect(ui->btnSave, &QPushButton::clicked, this, [&]() { save(); }); connect(ui->btnExit, &QPushButton::clicked, this, [&]() { QApplication::exit(0); }); connect(ui->btnReset, &QPushButton::clicked, this, &MainWidget::reset); + connect(ui->btnReplace, &QPushButton::clicked, this, &MainWidget::replace_content); connect(ui->btnExport, &QPushButton::clicked, this, &MainWidget::copy_multi_data); connect(ui->btnPagePre, &QPushButton::clicked, this, [&]() { unsigned int cur = ui->edCurPage->text().toUInt(); @@ -60,6 +63,14 @@ MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget edit.is_import_ = true; edit.set_xml_opr(&xml_); edit.exec(); + + if (!edit.is_import_sucess_) { + return; + } + xml_.get_all_elements(vec_); + current_.clear(); + current_ = vec_; + push_content(current_); }); connect(ui->btnPageNext, &QPushButton::clicked, this, [&]() { unsigned int cur = ui->edCurPage->text().toUInt(); @@ -159,8 +170,7 @@ void MainWidget::generate_table_widget() tab_widget_->setContextMenuPolicy(Qt::CustomContextMenu); connect(tab_widget_, &QTableWidget::itemChanged, this, [&](QTableWidgetItem* item) { item_changed_handle(item); }); - connect(tab_widget_, &QTableWidget::customContextMenuRequested, this, - &MainWidget::show_custom_menu); + connect(tab_widget_, &QTableWidget::customContextMenuRequested, this, &MainWidget::show_custom_menu); auto config = ini_.get_config(); auto keys = splitString(config.purpose, ","); keys_.clear(); @@ -620,3 +630,73 @@ void MainWidget::copy_multi_data() edit.set_data(ret); edit.exec(); } + +void MainWidget::replace_content() +{ + if (tab_widget_ == nullptr) { + return; + } + QString key = ui->edRepPre->text(); + QString after = ui->edRepAfter->text(); + if (key.isEmpty()) { + CUtil::msg(this, u8"替换前数据为空。"); + return; + } + if (ui->cbRepOnlySelect->isChecked()) { + QModelIndexList indexList = tab_widget_->selectionModel()->selectedRows(); + if (indexList.size() < 1) { + CUtil::msg(this, u8"无选择数据"); + return; + } + QString ret; + for (int i = 0; i < indexList.size(); ++i) { + Element_t* e = get_element_by_row(indexList[i].row()); + if (e == nullptr) { + continue; + } + auto* element = e->FirstAttribute(); + while (element) { + QString content(element->Value()); + if (content.contains(key)) { + content.replace(key, after); + e->SetAttribute(element->Name(), content.toStdString().c_str()); + } + element = element->Next(); + } + } + } else { + if (!CUtil::affirm(this, u8"确认", u8"确认进行全局替换吗?")) { + return; + } + + for (auto& data : vec_) { + auto* element = data->FirstAttribute(); + while (element) { + QString content(element->Value()); + content.replace(key, after); + data->SetAttribute(element->Name(), content.toStdString().c_str()); + element = element->Next(); + } + } + } + + xml_.get_all_elements(vec_); + current_.clear(); + current_ = vec_; + push_content(current_); +} + +void MainWidget::backup_file() +{ + if (tab_widget_ == nullptr) { + return; + } + + QString time = QDateTime::currentDateTime().toString("yyyy-MMdd-hhmmss"); + if (!xml_.backup_file(fs::path(exe_path_).parent_path().append("backup").string(), time.toStdString())) { + CUtil::msg(this, u8"备份失败。"); + } + else { + CUtil::msg(this, u8"备份完成。"); + } +} diff --git a/MainWidget.h b/MainWidget.h index 185e4d5..7a90531 100644 --- a/MainWidget.h +++ b/MainWidget.h @@ -46,6 +46,8 @@ private: void show_custom_menu(); void sort_by_repeat(std::vector& vec); void copy_multi_data(); + void replace_content(); + void backup_file(); protected: void closeEvent(QCloseEvent* event); diff --git a/MainWidget.ui b/MainWidget.ui index 0129f40..1ff5256 100644 --- a/MainWidget.ui +++ b/MainWidget.ui @@ -92,7 +92,7 @@ - + 替换为 @@ -261,6 +261,30 @@ + + btnSelectFile + edStatus + btnRead + btnSave + btnExport + btnImport + btnBackup + cbRepOnlySelect + edRepPre + edRepAfter + btnReplace + cbCaseSensitive + edSearchKey + btnSearch + btnReset + btnResort + btnExit + edCurPage + edAllPage + btnJump + btnPagePre + btnPageNext + diff --git a/src/data_edit.cpp b/src/data_edit.cpp index 38332f4..386bad2 100644 --- a/src/data_edit.cpp +++ b/src/data_edit.cpp @@ -39,6 +39,7 @@ void CDataEdit::set_xml_opr(CXmlOpr* xml_opr) void CDataEdit::import_data() { + is_import_sucess_ = false; QString data = ui->plainTextEdit->toPlainText(); if (data.trimmed().isEmpty()) { CUtil::msg(this, u8"内容为空"); @@ -50,6 +51,10 @@ void CDataEdit::import_data() return; } + if (!CUtil::affirm(this, u8"提示", u8"如果出现重复的 key 将跳过,继续吗?")) { + return; + } + std::vector valid_data{}; QStringList list = data.trimmed().split("\n"); for (int i = 0; i < list.size(); ++i) { @@ -72,6 +77,7 @@ void CDataEdit::import_data() QString info = QString(u8"需要导入 %1 条数据,成功导入 %2 条。").arg(task_count).arg(success_count); CUtil::msg(this, info); close(); + is_import_sucess_ = true; } void CDataEdit::showEvent(QShowEvent* event) diff --git a/src/xml_opr.cpp b/src/xml_opr.cpp index e98e763..82194ec 100644 --- a/src/xml_opr.cpp +++ b/src/xml_opr.cpp @@ -1,4 +1,7 @@ #include "xml_opr.h" +#include + +namespace fs = std::filesystem; CXmlOpr::CXmlOpr() = default; CXmlOpr::~CXmlOpr() = default; @@ -12,11 +15,35 @@ bool CXmlOpr::open(const std::string& xml_path) return true; } +bool CXmlOpr::backup_file(const std::string& desti_folder, const std::string& time) +{ + if (!fs::exists(xml_path_)) { + return false; + } + if (!fs::exists(desti_folder)) { + fs::create_directories(desti_folder); + } + fs::path file_path = fs::path(xml_path_); + fs::path des = fs::path(desti_folder).append(file_path.stem().string() + "_" + time + ".xml"); + return fs::copy_file(xml_path_, des); +} + void CXmlOpr::set_baseinfo(const OprBase& base) { opr_base_ = base; } +bool CXmlOpr::get_all_elements(std::vector& vec) +{ + vec.clear(); + Element_t* purpose_node = parent_node_->FirstChildElement(opr_base_.the_node.c_str()); + while (purpose_node) { + vec.push_back(purpose_node); + purpose_node = purpose_node->NextSiblingElement(); + } + return true; +} + bool CXmlOpr::parse_xml(std::vector& vec) { std::string next_node{}; @@ -43,12 +70,7 @@ bool CXmlOpr::parse_xml(std::vector& vec) parent_node_ = parent_node_->FirstChildElement(item.c_str()); } } - vec.clear(); - Element_t* purpose_node = parent_node_->FirstChildElement(opr_base_.the_node.c_str()); - while (purpose_node) { - vec.push_back(purpose_node); - purpose_node = purpose_node->NextSiblingElement(); - } + get_all_elements(vec); return true; } @@ -159,6 +181,7 @@ bool CXmlOpr::import_newer_data(const std::vector& vec, std::size_t if (last_item == nullptr) { return false; } + // 检查重复性 for (const auto& data : vec) { tinyxml2::XMLDocument doc; doc.Parse(data.c_str()); @@ -166,6 +189,10 @@ bool CXmlOpr::import_newer_data(const std::vector& vec, std::size_t if (item == nullptr) { continue; } + const char* key_str = item->Attribute(keys_[0].c_str()); + if (check_key_exists(std::string(key_str))) { + continue; + } ++success_count; auto* nitem = copy_element(item); insert_brother_node(last_item, nitem); @@ -183,10 +210,18 @@ bool CXmlOpr::check_key_exists(const Property_t& property) if (keys_.size() < 1 || property.size() < 1) { return false; } + return check_key_exists(property[0].key); +} + +bool CXmlOpr::check_key_exists(const std::string& key) +{ + if (keys_.size() < 1 || key.empty()) { + return false; + } Element_t* purpose_node = parent_node_->FirstChildElement(opr_base_.the_node.c_str()); while (purpose_node) { const char* value = purpose_node->Attribute(keys_[0].c_str()); - if (property[0].value == std::string(value)) { + if (key == std::string(value)) { return true; } purpose_node = purpose_node->NextSiblingElement(); diff --git a/src/xml_opr.h b/src/xml_opr.h index de09447..08d2526 100644 --- a/src/xml_opr.h +++ b/src/xml_opr.h @@ -22,18 +22,21 @@ public: public: bool open(const std::string& xml_path); + bool backup_file(const std::string& desti_folder, const std::string& time); void set_baseinfo(const OprBase& base); bool parse_xml(std::vector& vec); + bool get_all_elements(std::vector& vec); void copy_and_del(std::vector& vec, std::vector& out); void insert_brother_node(Element_t* brother, Element_t* newer); void del_element(Element_t* ele); bool check_key_exists(const Property_t& property); + bool check_key_exists(const std::string& key); bool save(); public: Element_t* copy_element(Element_t* ele); // 检查 xml 格式合法性 - bool check_valid_xml_data(const std::string& data); + bool check_valid_xml_data(const std::string& data); // 不检查 xml 格式合法性,请自行调用 check_valid_xml_data bool check_same_struct(const std::string& data); // 不检查 xml 格式合法性,请自行调用 check_valid_xml_data