支持全量转码和按需转码
This commit is contained in:
@@ -9,7 +9,9 @@ SOURCES += main.cpp \
|
||||
src/mainwindow/mainwindow.cpp \
|
||||
src/pythonrunner/PythonRunner.cpp \
|
||||
src/pages/about/aboutdialog.cpp \
|
||||
src/pages/businessconvert/businessconvertpage.cpp \
|
||||
src/pages/batchconvert/batchconvertpage.cpp \
|
||||
src/pages/nonlsconvert/nonlsconvertpage.cpp \
|
||||
src/pages/codesearch/codesearchpage.cpp \
|
||||
src/pages/functionsearch/functionsearchpage.cpp \
|
||||
src/pages/functionsearch/uf20functionsearchpage.cpp \
|
||||
@@ -48,7 +50,9 @@ SOURCES += main.cpp \
|
||||
HEADERS += src/mainwindow/mainwindow.h \
|
||||
src/pythonrunner/PythonRunner.h \
|
||||
src/pages/about/aboutdialog.h \
|
||||
src/pages/businessconvert/businessconvertpage.h \
|
||||
src/pages/batchconvert/batchconvertpage.h \
|
||||
src/pages/nonlsconvert/nonlsconvertpage.h \
|
||||
src/pages/codesearch/codesearchpage.h \
|
||||
src/pages/functionsearch/functionsearchpage.h \
|
||||
src/pages/functionsearch/uf20functionsearchpage.h \
|
||||
|
||||
@@ -88,12 +88,12 @@ echo.
|
||||
|
||||
REM Deploy default change_code.ini (only copy when missing, to keep user's existing config)
|
||||
echo Deploying default change_code.ini...
|
||||
if not exist "bin\uf2touft3" mkdir "bin\uf2touft3"
|
||||
if not exist "bin\uf2touft3\change_code.ini" (
|
||||
copy "config\change_code.ini" "bin\uf2touft3\change_code.ini" >nul
|
||||
echo [OK] change_code.ini -> bin\uf2touft3\ (default copied)
|
||||
if not exist "bin\uf2touft3\config" mkdir "bin\uf2touft3\config"
|
||||
if not exist "bin\uf2touft3\config\change_code.ini" (
|
||||
copy "config\change_code.ini" "bin\uf2touft3\config\change_code.ini" >nul
|
||||
echo [OK] change_code.ini -> bin\uf2touft3\config (default copied)
|
||||
) else (
|
||||
echo [SKIP] bin\uf2touft3\change_code.ini already exists, keep user config
|
||||
echo [SKIP] bin\uf2touft3\config\change_code.ini already exists, keep user config
|
||||
)
|
||||
echo.
|
||||
|
||||
|
||||
+100
-35
@@ -40,6 +40,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
, m_iconsSet(false)
|
||||
, m_navigationButton(nullptr)
|
||||
, m_isNavigationExpanded(true)
|
||||
, m_businessConvertPage(nullptr)
|
||||
, m_batchConvertPage(nullptr)
|
||||
, m_nonLsConvertPage(nullptr)
|
||||
, m_activeKind(ConvertKind::LS)
|
||||
, m_totalFunctions(0)
|
||||
, m_currentFunctionIndex(0)
|
||||
, m_lastProgress(0)
|
||||
@@ -136,7 +140,7 @@ void MainWindow::initWindow()
|
||||
void MainWindow::initContent()
|
||||
{
|
||||
addExpanderNode("功能转码", m_funcConvertKey, ElaIconType::FileCode);
|
||||
addPageNode("业务转码", createBatchConvertPage(), m_funcConvertKey, ElaIconType::ArrowsRepeat);
|
||||
addPageNode("业务转码", createBusinessConvertPage(), m_funcConvertKey, ElaIconType::ArrowsRepeat);
|
||||
addPageNode("代码检索", createCodeSearchPage(), m_funcConvertKey, ElaIconType::Cubes);
|
||||
|
||||
addExpanderNode("功能查询", m_funcSearchKey, ElaIconType::MagnifyingGlass);
|
||||
@@ -154,12 +158,21 @@ void MainWindow::initContent()
|
||||
addFooterNode("设置", createSettingsPage(), m_settingKey, 0, ElaIconType::GearComplex);
|
||||
}
|
||||
|
||||
QWidget* MainWindow::createBatchConvertPage()
|
||||
QWidget* MainWindow::createBusinessConvertPage()
|
||||
{
|
||||
m_batchConvertPage = new BatchConvertPage;
|
||||
connect(m_batchConvertPage, &BatchConvertPage::startConvert, this, &MainWindow::onBatchConvertStart);
|
||||
connect(m_batchConvertPage, &BatchConvertPage::stopConvert, this, &MainWindow::onBatchConvertStop);
|
||||
return m_batchConvertPage;
|
||||
m_businessConvertPage = new BusinessConvertPage;
|
||||
m_batchConvertPage = m_businessConvertPage->lsPage();
|
||||
m_nonLsConvertPage = m_businessConvertPage->nonLsPage();
|
||||
|
||||
connect(m_batchConvertPage, &BatchConvertPage::startConvert,
|
||||
this, &MainWindow::onLsConvertStart);
|
||||
connect(m_batchConvertPage, &BatchConvertPage::stopConvert,
|
||||
this, &MainWindow::onConvertStop);
|
||||
connect(m_nonLsConvertPage, &NonLsConvertPage::startConvert,
|
||||
this, &MainWindow::onNonLsConvertStart);
|
||||
connect(m_nonLsConvertPage, &NonLsConvertPage::stopConvert,
|
||||
this, &MainWindow::onConvertStop);
|
||||
return m_businessConvertPage;
|
||||
}
|
||||
|
||||
QWidget* MainWindow::createCodeSearchPage()
|
||||
@@ -176,8 +189,11 @@ QWidget* MainWindow::createFunctionSearchPage()
|
||||
QWidget* MainWindow::createUF20FunctionSearchPage()
|
||||
{
|
||||
UF20FunctionSearchPage *page = new UF20FunctionSearchPage;
|
||||
connect(page, &UF20FunctionSearchPage::addFunctionToConvert,
|
||||
// LS_ 开头功能 → 全量转码工作台;其余功能 → 按需转码工作台
|
||||
connect(page, &UF20FunctionSearchPage::addLsFunctionToConvert,
|
||||
m_batchConvertPage, &BatchConvertPage::addFunction);
|
||||
connect(page, &UF20FunctionSearchPage::addNonLsFunctionToConvert,
|
||||
m_nonLsConvertPage, &NonLsConvertPage::addFunction);
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -206,53 +222,87 @@ QWidget* MainWindow::createFunctionMappingPage()
|
||||
return new FunctionMappingPage();
|
||||
}
|
||||
|
||||
void MainWindow::onBatchConvertStart()
|
||||
void MainWindow::onLsConvertStart()
|
||||
{
|
||||
beginConvert(ConvertKind::LS);
|
||||
}
|
||||
|
||||
void MainWindow::onNonLsConvertStart()
|
||||
{
|
||||
beginConvert(ConvertKind::NonLs);
|
||||
}
|
||||
|
||||
void MainWindow::beginConvert(ConvertKind kind)
|
||||
{
|
||||
if (m_pythonRunner->isRunning()) {
|
||||
LogManager::instance()->logWarning("警告: 上一个任务还在运行中...");
|
||||
QMessageBox::warning(this, "提示", "上一个转换任务正在运行,请等待完成或停止当前任务。");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QString exePath = Constants::toolExe();
|
||||
|
||||
|
||||
if (!m_pythonRunner->canRun()) {
|
||||
LogManager::instance()->logError("错误: 找不到 uf2touft3/uf2touft3.exe!");
|
||||
QMessageBox::critical(this, "错误",
|
||||
QMessageBox::critical(this, "错误",
|
||||
QString("无法启动转换任务!\n\n请确保:\nuf2touft3/uf2touft3.exe 存在,且该目录下包含 config/config.ini\n\n期望路径:\n%1")
|
||||
.arg(exePath));
|
||||
.arg(exePath));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_batchConvertPage) {
|
||||
m_totalFunctions = m_batchConvertPage->getFunctionCount();
|
||||
m_functionList = m_batchConvertPage->getFunctionList();
|
||||
m_currentFunctionIndex = 0;
|
||||
} else {
|
||||
m_totalFunctions = 0;
|
||||
m_currentFunctionIndex = 0;
|
||||
m_functionList.clear();
|
||||
}
|
||||
|
||||
// 进度基准:BatchConvertPage 在开始时已将进度条置为 20(配置加载完成)
|
||||
QStringList engineArgs;
|
||||
if (kind == ConvertKind::NonLs) {
|
||||
// 按需转码:参数由页面在 onStartConvert 中拼装完成(含 -t/-o 与 @列表文件生成)
|
||||
engineArgs = m_nonLsConvertPage ? m_nonLsConvertPage->preparedArguments() : QStringList();
|
||||
m_pythonRunner->setArguments(engineArgs);
|
||||
m_totalFunctions = m_nonLsConvertPage ? m_nonLsConvertPage->getFunctionCount() : 0;
|
||||
m_functionList = m_nonLsConvertPage ? m_nonLsConvertPage->getFunctionList() : QStringList();
|
||||
} else {
|
||||
// 全量转码:沿用原无参调用,显式清空历史参数
|
||||
m_pythonRunner->setArguments(QStringList());
|
||||
m_totalFunctions = m_batchConvertPage ? m_batchConvertPage->getFunctionCount() : 0;
|
||||
m_functionList = m_batchConvertPage ? m_batchConvertPage->getFunctionList() : QStringList();
|
||||
}
|
||||
m_activeKind = kind;
|
||||
m_currentFunctionIndex = 0;
|
||||
|
||||
// 进度基准:页面在开始时已将进度条置为 20(配置加载完成)
|
||||
m_lastProgress = 20;
|
||||
|
||||
|
||||
LogManager::instance()->log("========================================");
|
||||
LogManager::instance()->log("开始执行 UFT2 到 UFT3 的代码转换...");
|
||||
LogManager::instance()->log(QString("开始执行 %1 代码转换...")
|
||||
.arg(kind == ConvertKind::NonLs ? "按需转码" : "全量转码"));
|
||||
LogManager::instance()->log(QString("工具路径: %1").arg(exePath));
|
||||
if (kind == ConvertKind::NonLs && !engineArgs.isEmpty()) {
|
||||
LogManager::instance()->log(QString("引擎参数: %1").arg(engineArgs.join(' ')));
|
||||
}
|
||||
LogManager::instance()->log(QString("待转换函数数量: %1").arg(m_totalFunctions));
|
||||
LogManager::instance()->log("========================================");
|
||||
|
||||
|
||||
if (!m_pythonRunner->start()) {
|
||||
LogManager::instance()->logError("错误: 无法启动转换进程!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 本页进入运行态;另一 Tab 禁止开始,避免并发冲突
|
||||
if (kind == ConvertKind::NonLs) {
|
||||
if (m_nonLsConvertPage) {
|
||||
m_nonLsConvertPage->onConvertStarted();
|
||||
}
|
||||
if (m_batchConvertPage) {
|
||||
m_batchConvertPage->setTaskBusy(true);
|
||||
}
|
||||
} else {
|
||||
if (m_batchConvertPage) {
|
||||
m_batchConvertPage->onConvertStarted();
|
||||
}
|
||||
if (m_nonLsConvertPage) {
|
||||
m_nonLsConvertPage->setTaskBusy(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onBatchConvertStop()
|
||||
void MainWindow::onConvertStop()
|
||||
{
|
||||
if (m_pythonRunner->isRunning()) {
|
||||
LogManager::instance()->log("正在停止转换进程...");
|
||||
@@ -262,8 +312,21 @@ void MainWindow::onBatchConvertStop()
|
||||
|
||||
void MainWindow::onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (m_batchConvertPage) {
|
||||
m_batchConvertPage->onConvertFinished();
|
||||
// 完成态回到发起任务的 Tab,并解除另一 Tab 的互斥限制
|
||||
if (m_activeKind == ConvertKind::NonLs) {
|
||||
if (m_nonLsConvertPage) {
|
||||
m_nonLsConvertPage->onConvertFinished();
|
||||
}
|
||||
if (m_batchConvertPage) {
|
||||
m_batchConvertPage->setTaskBusy(false);
|
||||
}
|
||||
} else {
|
||||
if (m_batchConvertPage) {
|
||||
m_batchConvertPage->onConvertFinished();
|
||||
}
|
||||
if (m_nonLsConvertPage) {
|
||||
m_nonLsConvertPage->setTaskBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
LogManager::instance()->log("========================================");
|
||||
@@ -346,18 +409,20 @@ void MainWindow::onPythonRunnerOutput(const QString &output)
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_batchConvertPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 进度单调递增:只有更大的进度才会推进进度条,避免"一下长一下短"
|
||||
if (targetProgress > m_lastProgress) {
|
||||
m_lastProgress = targetProgress;
|
||||
}
|
||||
|
||||
// 状态文字:仅展示已识别的处理阶段;未识别的日志行不显示(保持上一次状态)
|
||||
// 空 status 表示当前行无有效状态,BatchConvertPage 会保留上一次文字
|
||||
m_batchConvertPage->onProgressUpdate(m_lastProgress, status);
|
||||
// 空 status 表示当前行无有效状态,页面会保留上一次文字
|
||||
if (m_activeKind == ConvertKind::NonLs) {
|
||||
if (m_nonLsConvertPage) {
|
||||
m_nonLsConvertPage->onProgressUpdate(m_lastProgress, status);
|
||||
}
|
||||
} else if (m_batchConvertPage) {
|
||||
m_batchConvertPage->onProgressUpdate(m_lastProgress, status);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onPythonRunnerError(const QString &error)
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "ElaToolButton.h"
|
||||
#include <QWidget>
|
||||
#include "src/pythonrunner/PythonRunner.h"
|
||||
#include "src/pages/businessconvert/businessconvertpage.h"
|
||||
#include "src/pages/batchconvert/batchconvertpage.h"
|
||||
#include "src/pages/nonlsconvert/nonlsconvertpage.h"
|
||||
#include "src/pages/codesearch/codesearchpage.h"
|
||||
#include "src/pages/functionsearch/functionsearchpage.h"
|
||||
#include "src/pages/functionsearch/uf20functionsearchpage.h"
|
||||
@@ -30,7 +32,7 @@ private:
|
||||
void initWindow();
|
||||
void initContent();
|
||||
|
||||
QWidget* createBatchConvertPage();
|
||||
QWidget* createBusinessConvertPage();
|
||||
QWidget* createCodeSearchPage();
|
||||
QWidget* createFunctionSearchPage();
|
||||
QWidget* createUF20FunctionSearchPage();
|
||||
@@ -42,8 +44,13 @@ private:
|
||||
|
||||
void loadConfigCache();
|
||||
|
||||
void onBatchConvertStart();
|
||||
void onBatchConvertStop();
|
||||
// 转码任务类别:全量转码 / 按需转码
|
||||
enum class ConvertKind { LS, NonLs };
|
||||
|
||||
void onLsConvertStart();
|
||||
void onNonLsConvertStart();
|
||||
void onConvertStop();
|
||||
void beginConvert(ConvertKind kind);
|
||||
void onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onPythonRunnerOutput(const QString &output);
|
||||
void onPythonRunnerError(const QString &error);
|
||||
@@ -59,9 +66,13 @@ private:
|
||||
bool m_iconsSet;
|
||||
ElaToolButton* m_navigationButton;
|
||||
bool m_isNavigationExpanded;
|
||||
BusinessConvertPage *m_businessConvertPage;
|
||||
BatchConvertPage *m_batchConvertPage;
|
||||
NonLsConvertPage *m_nonLsConvertPage;
|
||||
CodeSearchPage *_codeSearchPage;
|
||||
|
||||
|
||||
ConvertKind m_activeKind;
|
||||
|
||||
int m_totalFunctions;
|
||||
int m_currentFunctionIndex;
|
||||
int m_lastProgress; // 已上报的最大进度,保证进度单调递增,避免进度条来回跳动
|
||||
|
||||
@@ -45,7 +45,8 @@ bool validateLsFunctionNames(QWidget *parent, const QStringList &functionNames)
|
||||
}
|
||||
|
||||
QMessageBox::warning(parent, "提示",
|
||||
QString("不支持非LS业务转码:\n%1").arg(nonLsNames.join('\n')));
|
||||
QString("\"全量转码\"仅支持 LS_ 开头的功能,以下功能无法加入:\n%1\n\n非LS功能请切到\"按需转码\"工作台添加。")
|
||||
.arg(nonLsNames.join('\n')));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -431,7 +432,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
|
||||
QFile file(jsonPath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("业务转码 - 保存配置失败,无法打开: %1").arg(jsonPath));
|
||||
QString("全量转码 - 保存配置失败,无法打开: %1").arg(jsonPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -442,7 +443,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
|
||||
static QRegularExpression convertRe(R"("convert"\s*:)");
|
||||
auto convertMatch = convertRe.match(content);
|
||||
if (!convertMatch.hasMatch()) {
|
||||
LogManager::instance()->logWarning("业务转码 - 保存配置失败,未找到 convert 节点");
|
||||
LogManager::instance()->logWarning("全量转码 - 保存配置失败,未找到 convert 节点");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -450,7 +451,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
|
||||
static QRegularExpression dirRe(R"("dir"\s*:\s*\[)");
|
||||
auto dirMatch = dirRe.match(content, convertMatch.capturedEnd());
|
||||
if (!dirMatch.hasMatch()) {
|
||||
LogManager::instance()->logWarning("业务转码 - 保存配置失败,未找到 dir 数组");
|
||||
LogManager::instance()->logWarning("全量转码 - 保存配置失败,未找到 dir 数组");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -484,7 +485,7 @@ bool BatchConvertPage::saveToCustJson(const QStringList &funcList)
|
||||
// 写回
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("业务转码 - 保存配置失败,无法写入: %1").arg(jsonPath));
|
||||
QString("全量转码 - 保存配置失败,无法写入: %1").arg(jsonPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -504,7 +505,7 @@ void BatchConvertPage::loadFromCustJson()
|
||||
QFile file(jsonPath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("业务转码 - 加载配置失败,无法打开: %1").arg(jsonPath));
|
||||
QString("全量转码 - 加载配置失败,无法打开: %1").arg(jsonPath));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -515,7 +516,7 @@ void BatchConvertPage::loadFromCustJson()
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("业务转码 - 加载配置 JSON 解析失败: %1").arg(parseError.errorString()));
|
||||
QString("全量转码 - 加载配置 JSON 解析失败: %1").arg(parseError.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -576,6 +577,20 @@ void BatchConvertPage::onConvertFinished()
|
||||
m_startBtn->setStyleSheet("background-color: #1abc9c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
|
||||
}
|
||||
|
||||
void BatchConvertPage::setTaskBusy(bool busy)
|
||||
{
|
||||
if (!m_startBtn) {
|
||||
return;
|
||||
}
|
||||
m_startBtn->setEnabled(!busy);
|
||||
if (busy) {
|
||||
m_progressBar->setValue(0);
|
||||
m_progressLabel->setText("有其他转换任务进行中,请等待完成...");
|
||||
} else {
|
||||
m_progressLabel->setText("等待开始...");
|
||||
}
|
||||
}
|
||||
|
||||
void BatchConvertPage::addFunction(const QString &funcName)
|
||||
{
|
||||
if (funcName.isEmpty()) {
|
||||
@@ -591,9 +606,9 @@ void BatchConvertPage::addFunction(const QString &funcName)
|
||||
updateTable();
|
||||
|
||||
if (saveToCustJson(m_funcList)) {
|
||||
QMessageBox::information(nullptr, "提示", QString("已将功能 [%1] 添加到业务转码").arg(funcName));
|
||||
QMessageBox::information(nullptr, "提示", QString("已将功能 [%1] 添加到\"全量转码\"").arg(funcName));
|
||||
} else {
|
||||
QMessageBox::warning(nullptr, "警告", "添加到业务转码成功,但保存到 cust.json 失败");
|
||||
QMessageBox::warning(nullptr, "警告", "添加到\"全量转码\"成功,但保存到 cust.json 失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ public slots:
|
||||
void onConvertFinished();
|
||||
void onConvertStarted();
|
||||
void addFunction(const QString &funcName);
|
||||
|
||||
// 另一工作台任务运行时由 MainWindow 调用,禁用本页开始按钮
|
||||
void setTaskBusy(bool busy);
|
||||
|
||||
int getFunctionCount() const { return m_funcList.size(); }
|
||||
QStringList getFunctionList() const { return m_funcList; }
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "businessconvertpage.h"
|
||||
#include "ElaPivot.h"
|
||||
#include "src/pages/batchconvert/batchconvertpage.h"
|
||||
#include "src/pages/nonlsconvert/nonlsconvertpage.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QStackedWidget>
|
||||
|
||||
BusinessConvertPage::BusinessConvertPage(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_modePivot(nullptr)
|
||||
, m_stackWidget(nullptr)
|
||||
, m_lsPage(nullptr)
|
||||
, m_nonLsPage(nullptr)
|
||||
{
|
||||
initUI();
|
||||
}
|
||||
|
||||
void BusinessConvertPage::initUI()
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 12, 0, 0);
|
||||
layout->setSpacing(8);
|
||||
|
||||
// 顶部模式切换(ElaPivot),与代码检索页风格一致
|
||||
QHBoxLayout *modeLayout = new QHBoxLayout;
|
||||
modeLayout->setContentsMargins(30, 0, 30, 0);
|
||||
|
||||
m_modePivot = new ElaPivot;
|
||||
m_modePivot->appendPivot("全量转码");
|
||||
m_modePivot->appendPivot("按需转码");
|
||||
m_modePivot->setTextPixelSize(14);
|
||||
m_modePivot->setCurrentIndex(0);
|
||||
connect(m_modePivot, &ElaPivot::pivotClicked, this, &BusinessConvertPage::onModePivotClicked);
|
||||
|
||||
modeLayout->addWidget(m_modePivot);
|
||||
modeLayout->addStretch();
|
||||
layout->addLayout(modeLayout);
|
||||
|
||||
// 两套转码工作台放入堆叠,由 pivot 切换
|
||||
m_lsPage = new BatchConvertPage;
|
||||
m_nonLsPage = new NonLsConvertPage;
|
||||
|
||||
m_stackWidget = new QStackedWidget;
|
||||
m_stackWidget->addWidget(m_lsPage);
|
||||
m_stackWidget->addWidget(m_nonLsPage);
|
||||
layout->addWidget(m_stackWidget, 1);
|
||||
}
|
||||
|
||||
void BusinessConvertPage::onModePivotClicked(int index)
|
||||
{
|
||||
if (m_stackWidget) {
|
||||
m_stackWidget->setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef BUSINESSCONVERTPAGE_H
|
||||
#define BUSINESSCONVERTPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class ElaPivot;
|
||||
class QStackedWidget;
|
||||
class BatchConvertPage;
|
||||
class NonLsConvertPage;
|
||||
|
||||
// 业务转码容器页:用 ElaPivot(类似代码检索的 m_modePivot)切换两种转码工作台
|
||||
// Pivot1 全量转码 = BatchConvertPage(整体复用现有实现)
|
||||
// Pivot2 按需转码 = NonLsConvertPage
|
||||
class BusinessConvertPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BusinessConvertPage(QWidget *parent = nullptr);
|
||||
|
||||
BatchConvertPage *lsPage() const { return m_lsPage; }
|
||||
NonLsConvertPage *nonLsPage() const { return m_nonLsPage; }
|
||||
|
||||
private slots:
|
||||
void onModePivotClicked(int index);
|
||||
|
||||
private:
|
||||
void initUI();
|
||||
|
||||
ElaPivot *m_modePivot;
|
||||
QStackedWidget *m_stackWidget;
|
||||
BatchConvertPage *m_lsPage;
|
||||
NonLsConvertPage *m_nonLsPage;
|
||||
};
|
||||
|
||||
#endif // BUSINESSCONVERTPAGE_H
|
||||
@@ -60,7 +60,7 @@ void UF20FunctionSearchPage::initUI()
|
||||
layout->addWidget(m_resultTable);
|
||||
|
||||
QHBoxLayout *tipLayout = new QHBoxLayout;
|
||||
QLabel *tipLabel = new QLabel("💡 双击选中行添加转码业务,只支持LS功能添加");
|
||||
QLabel *tipLabel = new QLabel("💡 双击选中行加入转码工作台:LS功能 → \"全量转码\";其它功能 → \"按需转码\"");
|
||||
tipLabel->setStyleSheet("color: #666; font-size: 13px;");
|
||||
tipLayout->addWidget(tipLabel);
|
||||
tipLayout->addStretch();
|
||||
@@ -113,11 +113,13 @@ void UF20FunctionSearchPage::onSearch()
|
||||
void UF20FunctionSearchPage::onTableDoubleClicked(const QModelIndex &index)
|
||||
{
|
||||
if (index.isValid()) {
|
||||
QString funcName = m_tableModel->data(m_tableModel->index(index.row(), 0)).toString();
|
||||
if (!funcName.startsWith("LS_")) {
|
||||
QMessageBox::information(this, "提示", "仅支持LS功能添加转码业务");
|
||||
return;
|
||||
const QString funcName =
|
||||
m_tableModel->data(m_tableModel->index(index.row(), 0)).toString();
|
||||
// LS_ 开头 → 全量转码;其余 → 按需转码(由 MainWindow 接到对应工作台)
|
||||
if (funcName.startsWith("LS_")) {
|
||||
emit addLsFunctionToConvert(funcName);
|
||||
} else {
|
||||
emit addNonLsFunctionToConvert(funcName);
|
||||
}
|
||||
emit addFunctionToConvert(funcName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ public:
|
||||
~UF20FunctionSearchPage();
|
||||
|
||||
signals:
|
||||
void addFunctionToConvert(const QString& funcName);
|
||||
// 双击按功能类别分流:LS_ 开头 → 全量转码工作台;其余 → 按需转码工作台
|
||||
void addLsFunctionToConvert(const QString& funcName);
|
||||
void addNonLsFunctionToConvert(const QString& funcName);
|
||||
|
||||
private slots:
|
||||
void onSearch();
|
||||
|
||||
@@ -1,121 +1,776 @@
|
||||
#include "nonlsconvertpage.h"
|
||||
#include "src/utils/Constants.h"
|
||||
#include "src/utils/uf2configreader.h"
|
||||
#include "src/utils/logmanager.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QGroupBox>
|
||||
#include <QComboBox>
|
||||
#include <QFileDialog>
|
||||
#include <QLabel>
|
||||
#include <QSettings>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QHeaderView>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
#include <QDialog>
|
||||
#include <QTextEdit>
|
||||
#include <QStandardItem>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QPainter>
|
||||
#include <QShowEvent>
|
||||
#include <cstring>
|
||||
#include "ElaPushButton.h"
|
||||
#include "ElaComboBox.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// 按需转码不做前缀白名单,仅对 LS_ 开头的函数给出“建议放全量转码 Tab”提示
|
||||
void adviseAboutLsNames(QWidget *parent, const QStringList &names)
|
||||
{
|
||||
QStringList lsNames;
|
||||
for (const QString &name : names) {
|
||||
const QString trimmed = name.trimmed();
|
||||
if (trimmed.startsWith("LS_") && !lsNames.contains(trimmed)) {
|
||||
lsNames.append(trimmed);
|
||||
}
|
||||
}
|
||||
if (!lsNames.isEmpty()) {
|
||||
QMessageBox::information(parent, "提示",
|
||||
QString("以下函数以 LS_ 开头,通常属于\"内存交易\"改造范围,\n建议放入\"全量转码\"工作台转码:\n\n%1")
|
||||
.arg(lsNames.join('\n')));
|
||||
}
|
||||
}
|
||||
|
||||
// 清洗清单项:去首尾空白;若误带 .service_design 后缀则剥离(保持短名形态)
|
||||
QString normalizeNonLsName(const QString &raw)
|
||||
{
|
||||
QString name = raw.trimmed();
|
||||
const QString suffix = ".service_design";
|
||||
if (name.endsWith(suffix)) {
|
||||
name.chop(static_cast<int>(strlen(".service_design")));
|
||||
name = name.trimmed();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// cust_nonls.json convert.dir 值语义:0=否(不递归)、-1=是(递归)
|
||||
constexpr int RecursionNo = 0;
|
||||
constexpr int RecursionYes = -1;
|
||||
// convert.dir 中引擎兜底条目键(非清单功能,加载时跳过、保存时保留),默认值与既有配置样例一致
|
||||
constexpr char WildcardDefaultKey[] = "*";
|
||||
constexpr int WildcardDefaultValue = RecursionYes;
|
||||
|
||||
// 清单表格列定义
|
||||
enum TableCol { ColName = 0, ColExists = 1, ColRecurse = 2, ColAction = 3 };
|
||||
constexpr int ActionColumnWidth = 80;
|
||||
constexpr int RecurseColumnWidth = 100;
|
||||
|
||||
} // namespace
|
||||
|
||||
class NonLsStatusDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit NonLsStatusDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
||||
{
|
||||
QVariant bgColor = index.data(Qt::BackgroundRole);
|
||||
QVariant fgColor = index.data(Qt::ForegroundRole);
|
||||
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
if (bgColor.canConvert<QBrush>()) {
|
||||
painter->fillRect(opt.rect, bgColor.value<QBrush>());
|
||||
}
|
||||
|
||||
if (fgColor.canConvert<QBrush>()) {
|
||||
painter->setPen(fgColor.value<QBrush>().color());
|
||||
}
|
||||
|
||||
painter->drawText(opt.rect, opt.displayAlignment, opt.text);
|
||||
}
|
||||
};
|
||||
|
||||
NonLsConvertPage::NonLsConvertPage(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
initUI();
|
||||
loadFromCustNonLsJson();
|
||||
|
||||
m_reloadTimer = new QTimer(this);
|
||||
m_reloadTimer->setSingleShot(true);
|
||||
m_reloadTimer->setInterval(300);
|
||||
connect(m_reloadTimer, &QTimer::timeout, this, &NonLsConvertPage::onReloadTimeout);
|
||||
|
||||
m_lastFileMtime = currentFileMtime();
|
||||
setupConfigWatcher();
|
||||
}
|
||||
|
||||
NonLsConvertPage::~NonLsConvertPage()
|
||||
{
|
||||
if (m_configWatcher) {
|
||||
m_configWatcher->removePaths(m_configWatcher->files());
|
||||
m_configWatcher->removePaths(m_configWatcher->directories());
|
||||
}
|
||||
}
|
||||
|
||||
void NonLsConvertPage::initUI()
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(30, 30, 30, 30);
|
||||
layout->setSpacing(20);
|
||||
layout->setContentsMargins(30, 20, 30, 30);
|
||||
layout->setSpacing(14);
|
||||
|
||||
QGroupBox *inputBox = new QGroupBox("输入设置");
|
||||
QVBoxLayout *inputLayout = new QVBoxLayout(inputBox);
|
||||
inputLayout->setSpacing(15);
|
||||
// 按需转码说明条:非 LS 功能,支持单文件/递归两种模式,不含核心文件与 module 模块文件
|
||||
QLabel *modeTipLabel = new QLabel(
|
||||
"<b>说明:</b>按需转码针对<b>非 LS 功能</b>转码,支持<b>单文件转码</b>与<b>递归转码</b>两种模式,"
|
||||
"转码范围不包含核心文件、module 模块文件。"
|
||||
"<ul style='margin-top:4px; margin-bottom:0px; padding-left:18px;'>"
|
||||
"<li><b>非递归模式</b>:仅对目标文件本身执行转码,不会处理该文件内调用的函数;</li>"
|
||||
"<li><b>递归模式</b>:转码目标文件的同时,自动对文件中所调用的关联函数一并完成转码。</li>"
|
||||
"</ul>");
|
||||
modeTipLabel->setTextFormat(Qt::RichText);
|
||||
modeTipLabel->setStyleSheet("color: #666; font-size: 12px;");
|
||||
modeTipLabel->setWordWrap(true);
|
||||
layout->addWidget(modeTipLabel);
|
||||
|
||||
QHBoxLayout *srcLayout = new QHBoxLayout;
|
||||
QLabel *srcLabel = new QLabel("源文件:");
|
||||
m_srcFileEdit = new QLineEdit;
|
||||
QPushButton *srcBtn = new QPushButton("浏览");
|
||||
connect(srcBtn, &QPushButton::clicked, this, &NonLsConvertPage::onBrowseSrcFile);
|
||||
srcLayout->addWidget(srcLabel);
|
||||
srcLayout->addWidget(m_srcFileEdit);
|
||||
srcLayout->addWidget(srcBtn);
|
||||
inputLayout->addLayout(srcLayout);
|
||||
QGroupBox *funcBox = new QGroupBox("转码功能");
|
||||
QVBoxLayout *funcLayout = new QVBoxLayout(funcBox);
|
||||
funcLayout->setSpacing(10);
|
||||
|
||||
QHBoxLayout *dstLayout = new QHBoxLayout;
|
||||
QLabel *dstLabel = new QLabel("输出文件:");
|
||||
m_dstFileEdit = new QLineEdit;
|
||||
QPushButton *dstBtn = new QPushButton("浏览");
|
||||
connect(dstBtn, &QPushButton::clicked, this, &NonLsConvertPage::onBrowseDstFile);
|
||||
dstLayout->addWidget(dstLabel);
|
||||
dstLayout->addWidget(m_dstFileEdit);
|
||||
dstLayout->addWidget(dstBtn);
|
||||
inputLayout->addLayout(dstLayout);
|
||||
m_tableModel = new QStandardItemModel(this);
|
||||
m_tableModel->setColumnCount(4);
|
||||
m_tableModel->setHorizontalHeaderLabels({"UF20函数名", "是否存在", "是否递归", "操作"});
|
||||
|
||||
layout->addWidget(inputBox);
|
||||
m_funcTable = new ElaTableView;
|
||||
m_funcTable->setModel(m_tableModel);
|
||||
m_funcTable->setMinimumHeight(180);
|
||||
m_funcTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_funcTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
|
||||
m_funcTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||
m_funcTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
|
||||
m_funcTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
|
||||
m_funcTable->horizontalHeader()->setStretchLastSection(false);
|
||||
m_funcTable->setItemDelegateForColumn(ColExists, new NonLsStatusDelegate(this));
|
||||
funcLayout->addWidget(m_funcTable);
|
||||
|
||||
QGroupBox *optionBox = new QGroupBox("转换选项");
|
||||
QVBoxLayout *optionLayout = new QVBoxLayout(optionBox);
|
||||
optionLayout->setSpacing(15);
|
||||
QHBoxLayout *tableBtnLayout = new QHBoxLayout;
|
||||
ElaPushButton *addBtn = new ElaPushButton("添加");
|
||||
connect(addBtn, &QPushButton::clicked, this, &NonLsConvertPage::onAddFunction);
|
||||
ElaPushButton *removeBtn = new ElaPushButton("删除选中");
|
||||
connect(removeBtn, &QPushButton::clicked, this, &NonLsConvertPage::onRemoveFunction);
|
||||
ElaPushButton *clearBtn = new ElaPushButton("清空列表");
|
||||
connect(clearBtn, &QPushButton::clicked, this, &NonLsConvertPage::onClearTable);
|
||||
tableBtnLayout->addWidget(addBtn);
|
||||
tableBtnLayout->addStretch();
|
||||
tableBtnLayout->addWidget(removeBtn);
|
||||
tableBtnLayout->addWidget(clearBtn);
|
||||
funcLayout->addLayout(tableBtnLayout);
|
||||
|
||||
QHBoxLayout *modeLayout = new QHBoxLayout;
|
||||
QLabel *modeLabel = new QLabel("转换模式:");
|
||||
QComboBox *modeCombo = new QComboBox;
|
||||
modeCombo->addItems({"非LS转LS", "LS转非LS"});
|
||||
modeLayout->addWidget(modeLabel);
|
||||
modeLayout->addWidget(modeCombo);
|
||||
optionLayout->addLayout(modeLayout);
|
||||
layout->addWidget(funcBox);
|
||||
|
||||
QHBoxLayout *encodingLayout = new QHBoxLayout;
|
||||
QLabel *encodingLabel = new QLabel("编码格式:");
|
||||
QComboBox *encodingCombo = new QComboBox;
|
||||
encodingCombo->addItems({"UTF-8", "GBK", "GB2312"});
|
||||
encodingLayout->addWidget(encodingLabel);
|
||||
encodingLayout->addWidget(encodingCombo);
|
||||
optionLayout->addLayout(encodingLayout);
|
||||
QGroupBox *progressBox = new QGroupBox("转换进度");
|
||||
progressBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
QVBoxLayout *progressLayout = new QVBoxLayout(progressBox);
|
||||
progressLayout->setSpacing(10);
|
||||
|
||||
layout->addWidget(optionBox);
|
||||
m_progressLabel = new ElaText("等待开始...");
|
||||
m_progressLabel->setStyleSheet("color: #666; font-size: 12px;");
|
||||
m_progressLabel->setFixedHeight(40);
|
||||
m_progressLabel->setWordWrap(false);
|
||||
m_progressLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
progressLayout->addWidget(m_progressLabel);
|
||||
|
||||
m_progressBar = new ElaProgressBar;
|
||||
m_progressBar->setRange(0, 100);
|
||||
m_progressBar->setValue(0);
|
||||
m_progressBar->setFixedHeight(25);
|
||||
m_progressBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
progressLayout->addWidget(m_progressBar);
|
||||
|
||||
layout->addWidget(progressBox);
|
||||
|
||||
QHBoxLayout *btnLayout = new QHBoxLayout;
|
||||
QPushButton *startBtn = new QPushButton("开始转换");
|
||||
startBtn->setStyleSheet("background-color: #3498db; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
|
||||
connect(startBtn, &QPushButton::clicked, this, &NonLsConvertPage::onStartConvert);
|
||||
QPushButton *previewBtn = new QPushButton("预览");
|
||||
connect(previewBtn, &QPushButton::clicked, this, &NonLsConvertPage::onPreview);
|
||||
m_startBtn = new QPushButton("开始转换");
|
||||
m_startBtn->setStyleSheet("background-color: #1abc9c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
|
||||
connect(m_startBtn, &QPushButton::clicked, this, &NonLsConvertPage::onStartConvert);
|
||||
btnLayout->addStretch();
|
||||
btnLayout->addWidget(startBtn);
|
||||
btnLayout->addWidget(previewBtn);
|
||||
btnLayout->addWidget(m_startBtn);
|
||||
layout->addLayout(btnLayout);
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onProgressUpdate(int progress, const QString &status)
|
||||
{
|
||||
m_progressBar->setValue(progress);
|
||||
|
||||
if (!status.isEmpty()) {
|
||||
QString displayStatus = status;
|
||||
if (displayStatus.length() > 80) {
|
||||
displayStatus = displayStatus.left(80) + "...";
|
||||
}
|
||||
m_progressLabel->setText(displayStatus);
|
||||
}
|
||||
}
|
||||
|
||||
bool NonLsConvertPage::checkFunctionExists(const QString &funcName)
|
||||
{
|
||||
return UF2ConfigReader::checkFuncExistsStatic(funcName);
|
||||
}
|
||||
|
||||
void NonLsConvertPage::updateTable()
|
||||
{
|
||||
// 不使用 blockSignals:QTableView 依赖 rowsRemoved/rowsInserted 信号更新内部布局缓存,
|
||||
// 若屏蔽会导致新增行不可见。m_recursionMap 已预填充,值相等时无需写盘。
|
||||
m_tableModel->removeRows(0, m_tableModel->rowCount());
|
||||
|
||||
for (int i = 0; i < m_funcList.size(); ++i) {
|
||||
const QString funcName = m_funcList[i];
|
||||
|
||||
QStandardItem *nameItem = new QStandardItem(funcName);
|
||||
nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable);
|
||||
// LS_ 开头的函数给出黄色提示(不拦截)
|
||||
if (funcName.startsWith("LS_")) {
|
||||
nameItem->setData(QBrush(QColor(196, 126, 10)), Qt::ForegroundRole);
|
||||
}
|
||||
|
||||
bool exists = checkFunctionExists(funcName);
|
||||
QString statusText = exists ? "是" : "否";
|
||||
|
||||
QStandardItem *statusItem = new QStandardItem(statusText);
|
||||
statusItem->setFlags(statusItem->flags() & ~Qt::ItemIsEditable);
|
||||
statusItem->setTextAlignment(Qt::AlignCenter);
|
||||
|
||||
if (exists) {
|
||||
statusItem->setData(QBrush(QColor(144, 238, 144)), Qt::BackgroundRole);
|
||||
statusItem->setData(QBrush(QColor(0, 100, 0)), Qt::ForegroundRole);
|
||||
} else {
|
||||
statusItem->setData(QBrush(QColor(255, 182, 193)), Qt::BackgroundRole);
|
||||
statusItem->setData(QBrush(QColor(139, 0, 0)), Qt::ForegroundRole);
|
||||
}
|
||||
|
||||
// “是否递归”以行尾下拉呈现:否=0、是=-1(与 cust_nonls.json convert.dir 值一致)
|
||||
QStandardItem *recItem = new QStandardItem("");
|
||||
recItem->setFlags(recItem->flags() & ~Qt::ItemIsEditable);
|
||||
recItem->setTextAlignment(Qt::AlignCenter);
|
||||
|
||||
QStandardItem *actionItem = new QStandardItem("");
|
||||
actionItem->setFlags(actionItem->flags() & ~Qt::ItemIsEditable);
|
||||
|
||||
QList<QStandardItem*> rowItems;
|
||||
rowItems.append(nameItem);
|
||||
rowItems.append(statusItem);
|
||||
rowItems.append(recItem);
|
||||
rowItems.append(actionItem);
|
||||
m_tableModel->appendRow(rowItems);
|
||||
|
||||
// “是否递归”下拉控件:否/是,切换即写回内存映射并持久化
|
||||
ElaComboBox *recCombo = new ElaComboBox;
|
||||
recCombo->addItem("否", RecursionNo);
|
||||
recCombo->addItem("是", RecursionYes);
|
||||
recCombo->setCurrentIndex(
|
||||
(m_recursionMap.value(funcName, RecursionNo) == RecursionYes) ? 1 : 0);
|
||||
connect(recCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, [this, funcName](int index) {
|
||||
const int recursion = (index == 1) ? RecursionYes : RecursionNo;
|
||||
if (m_recursionMap.value(funcName, RecursionNo) == recursion) {
|
||||
return;
|
||||
}
|
||||
m_recursionMap.insert(funcName, recursion);
|
||||
saveToCustNonLsJson(m_funcList);
|
||||
});
|
||||
m_funcTable->setIndexWidget(m_tableModel->index(i, ColRecurse), recCombo);
|
||||
|
||||
QPushButton *editBtn = new QPushButton;
|
||||
editBtn->setIcon(QIcon(":/resources/images/bianji.png"));
|
||||
editBtn->setIconSize(QSize(20, 20));
|
||||
editBtn->setStyleSheet("padding: 2px; border: none; background-color: transparent;");
|
||||
editBtn->setToolTip("修改");
|
||||
connect(editBtn, &QPushButton::clicked, this, [this, i]() {
|
||||
onEditRow(i);
|
||||
});
|
||||
m_funcTable->setIndexWidget(m_tableModel->index(i, ColAction), editBtn);
|
||||
}
|
||||
|
||||
int totalWidth = m_funcTable->viewport()->width();
|
||||
const int fixedWidth = ActionColumnWidth + RecurseColumnWidth;
|
||||
const int rest = totalWidth - fixedWidth;
|
||||
m_funcTable->setColumnWidth(ColName, rest * 8 / 10);
|
||||
m_funcTable->setColumnWidth(ColExists, rest * 2 / 10);
|
||||
m_funcTable->setColumnWidth(ColRecurse, RecurseColumnWidth);
|
||||
m_funcTable->setColumnWidth(ColAction, ActionColumnWidth);
|
||||
|
||||
// 视图刷新保险:强制重绘并滚动到顶部,确保新增行可见
|
||||
m_funcTable->viewport()->update();
|
||||
m_funcTable->resizeRowsToContents();
|
||||
m_funcTable->scrollToTop();
|
||||
m_funcTable->clearSelection();
|
||||
}
|
||||
|
||||
void NonLsConvertPage::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
if (m_funcTable) {
|
||||
int totalWidth = m_funcTable->width();
|
||||
const int fixedWidth = ActionColumnWidth + RecurseColumnWidth;
|
||||
const int rest = totalWidth - fixedWidth;
|
||||
m_funcTable->setColumnWidth(ColName, rest * 8 / 10);
|
||||
m_funcTable->setColumnWidth(ColExists, rest * 2 / 10);
|
||||
m_funcTable->setColumnWidth(ColRecurse, RecurseColumnWidth);
|
||||
m_funcTable->setColumnWidth(ColAction, ActionColumnWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void NonLsConvertPage::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
updateTable();
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onEditRow(int row)
|
||||
{
|
||||
if (row < 0 || row >= m_funcList.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString oldName = m_funcList[row];
|
||||
QString newName = oldName;
|
||||
|
||||
while (true) {
|
||||
QInputDialog dialog(this);
|
||||
dialog.setWindowTitle("修改函数名");
|
||||
dialog.setLabelText("请输入新的函数名:");
|
||||
dialog.setTextValue(oldName);
|
||||
dialog.resize(300, 150);
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
newName = normalizeNonLsName(dialog.textValue());
|
||||
|
||||
if (newName.isEmpty()) {
|
||||
QMessageBox::warning(this, "提示", "函数名不能为空!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newName == oldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_funcList.contains(newName)) {
|
||||
QMessageBox::warning(this, "提示", "该函数名已存在,请重新输入!");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const int oldRecursion = m_recursionMap.value(oldName, RecursionNo);
|
||||
m_recursionMap.remove(oldName);
|
||||
m_recursionMap.insert(newName, oldRecursion);
|
||||
|
||||
m_funcList[row] = newName;
|
||||
updateTable();
|
||||
if (saveToCustNonLsJson(m_funcList)) {
|
||||
loadFromCustNonLsJson();
|
||||
}
|
||||
adviseAboutLsNames(this, {newName});
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onAddFunction()
|
||||
{
|
||||
QDialog dialog(this);
|
||||
dialog.setWindowTitle("添加函数");
|
||||
dialog.resize(400, 250);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(&dialog);
|
||||
|
||||
QTextEdit *textEdit = new QTextEdit;
|
||||
textEdit->setPlaceholderText("每行输入一个函数名(短名,如 AS_xxx)");
|
||||
layout->addWidget(textEdit);
|
||||
|
||||
QHBoxLayout *btnLayout = new QHBoxLayout;
|
||||
QPushButton *okBtn = new QPushButton("确定");
|
||||
QPushButton *cancelBtn = new QPushButton("取消");
|
||||
btnLayout->addStretch();
|
||||
btnLayout->addWidget(okBtn);
|
||||
btnLayout->addWidget(cancelBtn);
|
||||
layout->addLayout(btnLayout);
|
||||
|
||||
m_resultEdit = new QTextEdit;
|
||||
m_resultEdit->setReadOnly(true);
|
||||
m_resultEdit->setStyleSheet("background-color: #2c3e50; color: #ecf0f1;");
|
||||
m_resultEdit->append("非LS转码工具已准备就绪...");
|
||||
layout->addWidget(m_resultEdit);
|
||||
}
|
||||
connect(okBtn, &QPushButton::clicked, &dialog, &QDialog::accept);
|
||||
connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject);
|
||||
|
||||
void NonLsConvertPage::onBrowseSrcFile()
|
||||
{
|
||||
QString file = QFileDialog::getOpenFileName(this, "选择源文件");
|
||||
if (!file.isEmpty()) {
|
||||
m_srcFileEdit->setText(file);
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = textEdit->toPlainText().trimmed();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList funcNames = text.split('\n', Qt::SkipEmptyParts);
|
||||
int addedCount = 0;
|
||||
int existCount = 0;
|
||||
QStringList addedNames;
|
||||
for (const QString &name : funcNames) {
|
||||
const QString trimmedName = normalizeNonLsName(name);
|
||||
if (trimmedName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (m_funcList.contains(trimmedName)) {
|
||||
existCount++;
|
||||
continue;
|
||||
}
|
||||
m_funcList.append(trimmedName);
|
||||
m_recursionMap.insert(trimmedName, RecursionNo); // 新增功能默认“否”
|
||||
addedNames.append(trimmedName);
|
||||
addedCount++;
|
||||
}
|
||||
|
||||
if (existCount > 0) {
|
||||
QMessageBox::information(this, "提示",
|
||||
QString("成功添加 %1 个函数,%2 个函数已存在").arg(addedCount).arg(existCount));
|
||||
} else if (addedCount > 0) {
|
||||
QMessageBox::information(this, "提示",
|
||||
QString("成功添加 %1 个函数").arg(addedCount));
|
||||
}
|
||||
|
||||
adviseAboutLsNames(this, addedNames);
|
||||
|
||||
updateTable();
|
||||
if (addedCount > 0) {
|
||||
saveToCustNonLsJson(m_funcList);
|
||||
}
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onBrowseDstFile()
|
||||
void NonLsConvertPage::onRemoveFunction()
|
||||
{
|
||||
QString file = QFileDialog::getSaveFileName(this, "选择输出文件");
|
||||
if (!file.isEmpty()) {
|
||||
m_dstFileEdit->setText(file);
|
||||
QModelIndexList selectedIndexes = m_funcTable->selectionModel()->selectedRows();
|
||||
if (selectedIndexes.isEmpty()) {
|
||||
QMessageBox::warning(this, "提示", "请先选中要删除的行!");
|
||||
return;
|
||||
}
|
||||
|
||||
QList<int> rowsToRemove;
|
||||
for (const QModelIndex &index : selectedIndexes) {
|
||||
int row = index.row();
|
||||
if (!rowsToRemove.contains(row)) {
|
||||
rowsToRemove.append(row);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(rowsToRemove.begin(), rowsToRemove.end(), std::greater<int>());
|
||||
for (int row : rowsToRemove) {
|
||||
m_recursionMap.remove(m_funcList.at(row));
|
||||
m_funcList.removeAt(row);
|
||||
}
|
||||
|
||||
updateTable();
|
||||
if (saveToCustNonLsJson(m_funcList)) {
|
||||
loadFromCustNonLsJson();
|
||||
}
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onClearTable()
|
||||
{
|
||||
if (m_funcList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton reply;
|
||||
reply = QMessageBox::question(this, "确认清空", "确定要清空所有函数吗?",
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (reply == QMessageBox::Yes) {
|
||||
m_funcList.clear();
|
||||
m_recursionMap.clear();
|
||||
updateTable();
|
||||
if (saveToCustNonLsJson(m_funcList)) {
|
||||
loadFromCustNonLsJson();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NonLsConvertPage::saveToCustNonLsJson(const QStringList &funcList)
|
||||
{
|
||||
const QString jsonPath = Constants::custNonLsJson();
|
||||
|
||||
// 保留既有顶层节点,仅更新 convert 节点
|
||||
QJsonObject root;
|
||||
{
|
||||
QFile file(jsonPath);
|
||||
if (file.exists() && file.open(QIODevice::ReadOnly)) {
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
|
||||
if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
|
||||
root = doc.object();
|
||||
} else {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("按需转码 - 保存前读取既有配置失败(%1),将按当前清单重建: %2")
|
||||
.arg(parseError.errorString()).arg(jsonPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert.dir 键值表:{功能名: 0|-1};0=否(不递归)、-1=是(递归),并保留/兜底引擎默认键 "*"
|
||||
const QJsonObject oldDir = root.value("convert").toObject().value("dir").toObject();
|
||||
QJsonObject dirObj;
|
||||
for (const QString &name : funcList) {
|
||||
dirObj.insert(name, m_recursionMap.value(name, RecursionNo));
|
||||
}
|
||||
if (oldDir.contains(WildcardDefaultKey)) {
|
||||
dirObj.insert(WildcardDefaultKey, oldDir.value(WildcardDefaultKey));
|
||||
} else {
|
||||
dirObj.insert(WildcardDefaultKey, WildcardDefaultValue);
|
||||
}
|
||||
|
||||
QJsonArray extArray;
|
||||
for (const QString &ext : {QStringLiteral(".service_design"),
|
||||
QStringLiteral(".function_design"),
|
||||
QStringLiteral(".aservice_design"),
|
||||
QStringLiteral(".procedure_design"),
|
||||
QStringLiteral(".afunction_design")}) {
|
||||
extArray.append(ext);
|
||||
}
|
||||
QJsonObject convertObj;
|
||||
convertObj["dir"] = dirObj;
|
||||
convertObj["ext"] = extArray;
|
||||
root["convert"] = convertObj;
|
||||
|
||||
QFile file(jsonPath);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("按需转码 - 保存配置失败,无法写入: %1").arg(jsonPath));
|
||||
return false;
|
||||
}
|
||||
QJsonDocument doc(root);
|
||||
file.write(doc.toJson(QJsonDocument::Indented));
|
||||
file.close();
|
||||
|
||||
// 程序自身写入后更新 mtime,避免触发无谓的重载
|
||||
m_lastFileMtime = currentFileMtime();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NonLsConvertPage::loadFromCustNonLsJson()
|
||||
{
|
||||
const QString jsonPath = Constants::custNonLsJson();
|
||||
if (!QFile::exists(jsonPath)) {
|
||||
return; // 文件尚不存在(首次使用),保持空清单,不打扰
|
||||
}
|
||||
|
||||
QFile file(jsonPath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("按需转码 - 加载配置失败,无法打开: %1").arg(jsonPath));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
LogManager::instance()->logWarning(
|
||||
QString("按需转码 - 加载配置 JSON 解析失败: %1").arg(parseError.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject root = doc.object();
|
||||
const QJsonValue dirVal = root["convert"].toObject()["dir"];
|
||||
|
||||
QStringList items;
|
||||
m_recursionMap.clear();
|
||||
if (dirVal.isObject()) {
|
||||
// 新版:dir 为 {功能名: 0|-1},0=否(不递归)、-1=是(递归);"*" 为引擎默认键,不展示
|
||||
const QJsonObject dirObj = dirVal.toObject();
|
||||
for (auto it = dirObj.constBegin(); it != dirObj.constEnd(); ++it) {
|
||||
if (it.key() == WildcardDefaultKey) {
|
||||
continue;
|
||||
}
|
||||
const QString item = normalizeNonLsName(it.key());
|
||||
if (item.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
items.append(item);
|
||||
const int v = it.value().toInt(RecursionNo);
|
||||
m_recursionMap.insert(item, (v == RecursionYes) ? RecursionYes : RecursionNo);
|
||||
}
|
||||
} else {
|
||||
// 兼容旧版:dir 为 ["功能名", ...],无递归标志,一律按“否”加载
|
||||
for (const QJsonValue &val : dirVal.toArray()) {
|
||||
const QString item = normalizeNonLsName(val.toString());
|
||||
if (!item.isEmpty()) {
|
||||
items.append(item);
|
||||
m_recursionMap.insert(item, RecursionNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_funcList = items;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onStartConvert()
|
||||
{
|
||||
m_resultEdit->append("开始转换...");
|
||||
m_resultEdit->append("源文件: " + m_srcFileEdit->text());
|
||||
m_resultEdit->append("输出文件: " + m_dstFileEdit->text());
|
||||
m_resultEdit->append("转换完成!");
|
||||
if (m_startBtn->text() == "停止转换") {
|
||||
m_progressLabel->setText("正在停止转换...");
|
||||
emit stopConvert();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_funcList.isEmpty()) {
|
||||
QMessageBox::warning(this, "提示", "请先添加需要转码的函数!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_progressBar->setValue(0);
|
||||
m_progressLabel->setText("正在准备转换...");
|
||||
|
||||
if (!saveToCustNonLsJson(m_funcList)) {
|
||||
m_progressLabel->setText("配置保存失败");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
m_preparedArgs = buildArguments(&ok);
|
||||
if (!ok) {
|
||||
m_progressLabel->setText("参数生成失败");
|
||||
return;
|
||||
}
|
||||
|
||||
m_progressBar->setValue(20);
|
||||
m_progressLabel->setText("配置加载成功,开始转换...");
|
||||
|
||||
emit startConvert();
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onPreview()
|
||||
void NonLsConvertPage::onConvertStarted()
|
||||
{
|
||||
m_resultEdit->append("预览模式...");
|
||||
m_resultEdit->append("源文件: " + m_srcFileEdit->text());
|
||||
m_resultEdit->append("预览完成!");
|
||||
}
|
||||
m_startBtn->setText("停止转换");
|
||||
m_startBtn->setStyleSheet("background-color: #e74c3c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onConvertFinished()
|
||||
{
|
||||
m_progressBar->setValue(100);
|
||||
m_progressLabel->setText("转换完成");
|
||||
m_startBtn->setText("开始转换");
|
||||
m_startBtn->setStyleSheet("background-color: #1abc9c; color: white; padding: 10px 30px; font-size: 14px; border: none; border-radius: 4px;");
|
||||
}
|
||||
|
||||
void NonLsConvertPage::setTaskBusy(bool busy)
|
||||
{
|
||||
if (!m_startBtn) {
|
||||
return;
|
||||
}
|
||||
m_startBtn->setEnabled(!busy);
|
||||
if (busy) {
|
||||
m_progressBar->setValue(0);
|
||||
m_progressLabel->setText("有其他转换任务进行中,请等待完成...");
|
||||
} else {
|
||||
m_progressLabel->setText("等待开始...");
|
||||
}
|
||||
}
|
||||
|
||||
void NonLsConvertPage::addFunction(const QString &funcName)
|
||||
{
|
||||
if (funcName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString name = normalizeNonLsName(funcName);
|
||||
if (m_funcList.contains(name)) {
|
||||
QMessageBox::information(nullptr, "提示", QString("功能 [%1] 已在列表中").arg(name));
|
||||
return;
|
||||
}
|
||||
|
||||
m_funcList.append(name);
|
||||
m_recursionMap.insert(name, RecursionNo); // 新增功能默认“否”
|
||||
updateTable();
|
||||
|
||||
if (saveToCustNonLsJson(m_funcList)) {
|
||||
QMessageBox::information(nullptr, "提示", QString("已将功能 [%1] 添加到\"按需转码\"").arg(name));
|
||||
} else {
|
||||
QMessageBox::warning(nullptr, "警告", "添加到\"按需转码\"成功,但保存到 cust_nonls.json 失败");
|
||||
}
|
||||
}
|
||||
|
||||
QStringList NonLsConvertPage::buildArguments(bool *ok)
|
||||
{
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
// 引擎契约(新版):仅携带 -m 2 表示“按需转码”模式,
|
||||
// 函数清单与“是否递归”开关由引擎直接读取 config/cust_nonls.json,无需再传 -t/-o 参数
|
||||
return QStringList() << "-m" << "2";
|
||||
}
|
||||
|
||||
void NonLsConvertPage::setupConfigWatcher()
|
||||
{
|
||||
const QString configPath = getConfigFilePath();
|
||||
|
||||
m_configWatcher = new QFileSystemWatcher(this);
|
||||
|
||||
if (QFile::exists(configPath)) {
|
||||
m_configWatcher->addPath(configPath);
|
||||
}
|
||||
|
||||
const QString dir = QFileInfo(configPath).absolutePath();
|
||||
if (QDir(dir).exists()) {
|
||||
m_configWatcher->addPath(dir);
|
||||
}
|
||||
|
||||
connect(m_configWatcher, &QFileSystemWatcher::fileChanged,
|
||||
this, &NonLsConvertPage::onConfigFileChanged);
|
||||
connect(m_configWatcher, &QFileSystemWatcher::directoryChanged,
|
||||
this, &NonLsConvertPage::onConfigDirChanged);
|
||||
}
|
||||
|
||||
QDateTime NonLsConvertPage::currentFileMtime() const
|
||||
{
|
||||
QFileInfo info(getConfigFilePath());
|
||||
return info.exists() ? info.lastModified() : QDateTime();
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onConfigFileChanged(const QString &path)
|
||||
{
|
||||
if (m_configWatcher && !m_configWatcher->files().contains(path)
|
||||
&& QFile::exists(path)) {
|
||||
m_configWatcher->addPath(path);
|
||||
}
|
||||
m_reloadTimer->start(); // 防抖
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onConfigDirChanged(const QString &dirPath)
|
||||
{
|
||||
Q_UNUSED(dirPath);
|
||||
const QString configPath = getConfigFilePath();
|
||||
if (m_configWatcher && QFile::exists(configPath)
|
||||
&& !m_configWatcher->files().contains(configPath)) {
|
||||
m_configWatcher->addPath(configPath);
|
||||
}
|
||||
m_reloadTimer->start(); // 防抖
|
||||
}
|
||||
|
||||
void NonLsConvertPage::onReloadTimeout()
|
||||
{
|
||||
QDateTime now = currentFileMtime();
|
||||
if (now.isValid() && now != m_lastFileMtime) {
|
||||
m_lastFileMtime = now;
|
||||
loadFromCustNonLsJson();
|
||||
}
|
||||
}
|
||||
|
||||
QString NonLsConvertPage::getConfigFilePath() const
|
||||
{
|
||||
return Constants::custNonLsJson();
|
||||
}
|
||||
|
||||
@@ -2,9 +2,23 @@
|
||||
#define NONLSCONVERTPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLineEdit>
|
||||
#include <QTextEdit>
|
||||
#include <QStandardItemModel>
|
||||
#include <QStandardItem>
|
||||
#include <QHash>
|
||||
#include <QPushButton>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
#include "ElaTableView.h"
|
||||
#include "ElaProgressBar.h"
|
||||
#include "ElaText.h"
|
||||
|
||||
// 按需转码工作台(业务转码页 Pivot2)
|
||||
// 交互与全量转码页(BatchConvertPage)保持一致:
|
||||
// 函数清单(短名)与“是否递归”开关 -> 持久化到 config/cust_nonls.json
|
||||
// convert.dir 为 {功能名: 0| -1} 键值表:0=否(不递归)、-1=是(递归),另含引擎默认键 "*"
|
||||
// 调用契约:仅携带 -m 2(按需转码模式);函数清单与递归开关由引擎直接读取 cust_nonls.json,
|
||||
// 不再需要 -t/-o 等命令行参数
|
||||
class NonLsConvertPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -13,18 +27,58 @@ public:
|
||||
explicit NonLsConvertPage(QWidget *parent = nullptr);
|
||||
~NonLsConvertPage();
|
||||
|
||||
signals:
|
||||
void startConvert();
|
||||
void stopConvert();
|
||||
|
||||
public slots:
|
||||
void onProgressUpdate(int progress, const QString &status);
|
||||
void onConvertStarted();
|
||||
void onConvertFinished();
|
||||
void addFunction(const QString &funcName);
|
||||
// 另一工作台任务运行时由 MainWindow 调用,禁用本页开始按钮
|
||||
void setTaskBusy(bool busy);
|
||||
|
||||
int getFunctionCount() const { return m_funcList.size(); }
|
||||
QStringList getFunctionList() const { return m_funcList; }
|
||||
// 本次开始转换前已拼装好的引擎参数(由 onStartConvert 生成)
|
||||
QStringList preparedArguments() const { return m_preparedArgs; }
|
||||
|
||||
private slots:
|
||||
void onBrowseSrcFile();
|
||||
void onBrowseDstFile();
|
||||
void onStartConvert();
|
||||
void onPreview();
|
||||
void onAddFunction();
|
||||
void onRemoveFunction();
|
||||
void onClearTable();
|
||||
void onEditRow(int row);
|
||||
void onConfigFileChanged(const QString &path);
|
||||
void onConfigDirChanged(const QString &path);
|
||||
void onReloadTimeout();
|
||||
|
||||
private:
|
||||
void initUI();
|
||||
bool saveToCustNonLsJson(const QStringList &funcList);
|
||||
void loadFromCustNonLsJson();
|
||||
bool checkFunctionExists(const QString &funcName);
|
||||
void updateTable();
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
QString getConfigFilePath() const;
|
||||
void setupConfigWatcher();
|
||||
QDateTime currentFileMtime() const;
|
||||
QStringList buildArguments(bool *ok);
|
||||
|
||||
QLineEdit *m_srcFileEdit;
|
||||
QLineEdit *m_dstFileEdit;
|
||||
QTextEdit *m_resultEdit;
|
||||
ElaTableView *m_funcTable;
|
||||
QStandardItemModel *m_tableModel;
|
||||
QStringList m_funcList;
|
||||
// 每个清单功能对应的“是否递归”标志:0=否、-1=是(与 cust_nonls.json convert.dir 值一致)
|
||||
QHash<QString, int> m_recursionMap;
|
||||
ElaProgressBar *m_progressBar;
|
||||
ElaText *m_progressLabel;
|
||||
QPushButton *m_startBtn;
|
||||
QFileSystemWatcher *m_configWatcher;
|
||||
QTimer *m_reloadTimer;
|
||||
QDateTime m_lastFileMtime;
|
||||
QStringList m_preparedArgs;
|
||||
};
|
||||
|
||||
#endif // NONLSCONVERTPAGE_H
|
||||
#endif // NONLSCONVERTPAGE_H
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Paths {
|
||||
inline const QString ConfigDir = "config"; // 配置文件目录(toolDir 下)
|
||||
inline const QString ChangeCodeDir = "change_code"; // 代码检索根目录(toolDir 下)
|
||||
inline const QString FunctionOutput = "function_output"; // 调用链/文件导出目录
|
||||
inline const QString TranslateOut = "translate_out"; // 按需转码缺省输出子目录(createPath 下)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -51,6 +52,8 @@ namespace ToolFiles {
|
||||
inline const QString Uft3Trans = "uft3Trans.json";
|
||||
inline const QString CustJson = "config/cust.json";
|
||||
inline const QString Cust2Json = "config/cust2.json";
|
||||
inline const QString CustNonLsJson = "config/cust_nonls.json"; // 按需转码 UI 清单持久化
|
||||
inline const QString NonLsRunList = "config/run_nonls_list.txt"; // 大批量时生成的 @列表文件
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -113,6 +116,8 @@ inline QString toolDir() { return QCoreApplication::applicationDirPath() + "
|
||||
inline QString toolExe() { return toolDir() + "/" + ToolFiles::Exe; }
|
||||
inline QString configIni() { return toolDir() + "/" + ToolFiles::ConfigIni; }
|
||||
inline QString custJson() { return toolDir() + "/" + ToolFiles::CustJson; }
|
||||
inline QString custNonLsJson() { return toolDir() + "/" + ToolFiles::CustNonLsJson; }
|
||||
inline QString nonLsRunListFile() { return toolDir() + "/" + ToolFiles::NonLsRunList; }
|
||||
inline QString logDir() { return QCoreApplication::applicationDirPath() + "/" + Paths::LogDir; }
|
||||
inline QString cacheDir() { return toolDir() + "/" + Paths::CacheDir; } // bin/uf2touft3/cache
|
||||
inline QString uf20CacheDb() { return cacheDir() + "/" + Cache::Uf20DbFile; }
|
||||
|
||||
@@ -68,6 +68,17 @@ bool ConfigReaderBase::doLoadCnameCache(const ConfigReaderTraits& traits,
|
||||
}
|
||||
|
||||
cache = traits.getAllCnames();
|
||||
|
||||
// 接口表为空(首次启动或元数据更新尚未落库完成)时数据尚未就绪:
|
||||
// 此时不标记为“已加载”, 保留 cacheLoaded=false 以便后续查询重新加载,
|
||||
// 避免把这一时刻的空结果长期固化为“功能不存在”。
|
||||
const bool dataReady = traits.hasConfig ? traits.hasConfig(traits.mainFile) : true;
|
||||
if (cache.isEmpty() && !dataReady) {
|
||||
LogManager::instance()->logInfo(
|
||||
QString("%1 Cname缓存暂未加载 - 接口表为空, 数据未就绪, 后续查询将重试").arg(traits.logTag));
|
||||
return false;
|
||||
}
|
||||
|
||||
cacheLoaded = true;
|
||||
return true;
|
||||
}
|
||||
@@ -78,6 +89,11 @@ bool ConfigReaderBase::doCheckFuncExists(const ConfigReaderTraits& traits, const
|
||||
if (!cacheLoaded) {
|
||||
doLoadCnameCache(traits, cache, cacheLoaded);
|
||||
}
|
||||
if (cache.isEmpty()) {
|
||||
// 内存缓存为空(数据尚未就绪/元数据更新进行中): 直接查库兜底,
|
||||
// 避免把“缓存没准备好”误判为“功能不存在”。
|
||||
return traits.checkExists ? traits.checkExists(funcName) : false;
|
||||
}
|
||||
return cache.contains(funcName);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ struct ConfigReaderTraits
|
||||
std::function<bool(const QString&)> hasConfig; // DataCache 是否已缓存该配置
|
||||
std::function<bool(const QJsonObject&)> saveConfig; // DataCache 保存配置
|
||||
std::function<QSet<QString>()> getAllCnames; // DataCache 读取全部 cname
|
||||
// 单行直查(绕过 cname 内存缓存): 用于数据尚未就绪、内存缓存为空时兜底,
|
||||
// 避免把“缓存没准备好”误判为“功能不存在”
|
||||
std::function<bool(const QString&)> checkExists;
|
||||
};
|
||||
|
||||
// 模板方法模式的公共基类:承载 UF2/UFT3 配置读取的共享流程。
|
||||
|
||||
@@ -24,6 +24,9 @@ const ConfigReaderTraits& UF2ConfigReader::traits()
|
||||
traits.getAllCnames = [] {
|
||||
return DataCache::instance()->getAllUF20Cnames();
|
||||
};
|
||||
traits.checkExists = [](const QString& cname) {
|
||||
return DataCache::instance()->checkUF20CnameExists(cname);
|
||||
};
|
||||
return traits;
|
||||
}();
|
||||
return t;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "uft3configreader.h"
|
||||
#include "datacache.h"
|
||||
#include "Constants.h"
|
||||
|
||||
QSet<QString> UFT3ConfigReader::m_cnameCache;
|
||||
bool UFT3ConfigReader::m_cnameCacheLoaded = false;
|
||||
@@ -27,6 +28,13 @@ const ConfigReaderTraits& UFT3ConfigReader::traits()
|
||||
traits.getAllCnames = [] {
|
||||
return DataCache::instance()->getAllUFT3Cnames();
|
||||
};
|
||||
traits.checkExists = [](const QString& cname) {
|
||||
DataCache *cache = DataCache::instance();
|
||||
// 与 getAllUFT3Cnames 覆盖范围保持一致: interface + trans_code 两张表
|
||||
return cache->checkUFT3CnameExists(cname)
|
||||
|| !cache->getRowData(QStringLiteral("uft3"),
|
||||
Constants::Cache::TblUft3TransCode, cname).isEmpty();
|
||||
};
|
||||
return traits;
|
||||
}();
|
||||
return t;
|
||||
|
||||
Reference in New Issue
Block a user