Compare commits

..
25 Commits
Author SHA1 Message Date
taocong 08efc50c3f 优化查询链路的性能等 2026-07-24 15:29:18 +08:00
taocong 9cb7240060 优化非转码业务查询链路性能 2026-07-23 14:25:09 +08:00
taocong 389c6eeecd 新增代码检索功能、提升业务转码效率等 2026-07-21 17:08:23 +08:00
taocong b86fc071c2 帮助文档更新 2026-07-07 10:39:12 +08:00
taocong 9fdd342234 1.修复设置路径不生效;\n2.修复元数据更新后业务转码模块对函数检查状态异常问题; 2026-07-07 10:21:02 +08:00
taocong 38fde4223f 新增update_info.json 2026-07-02 19:57:34 +08:00
taocong 1f33f0fbaa 新增数据映射页面,表映射、函数映射;新增版本更新提示功能 2026-07-02 19:54:22 +08:00
taocong d4d607dc6a 增加2.0账户功能更新与转码 2026-07-02 10:23:06 +08:00
taocong 6dfba8715d 修复元数据页面不显示问题 2026-06-12 10:12:40 +08:00
taocong dbb7059664 增加帮助页面网页代码功能,以及nginx部署 2026-06-10 17:17:07 +08:00
taocong edb693e5af 修复配置文件保存时编码异常问题,修改关于和帮助页面 2026-06-10 17:11:59 +08:00
taocong d891b4f583 帮助页面修改 2026-06-03 09:07:26 +08:00
taocong 5718503f26 增加转码功能函数配置修改自动刷新机制 2026-06-02 10:39:01 +08:00
taocong cc7fc51399 新增自定义控件,修复查询添加问题 2026-06-01 19:32:23 +08:00
taocong fad539e127 修复转码模块缺失问题 2026-06-01 14:50:04 +08:00
taocong 4cb44fc2fa 修复编译警告 2026-06-01 09:41:27 +08:00
taocong 273da28390 完成元数据更新功能 2026-06-01 09:25:20 +08:00
taocong 8ecf44db1a 修复UF20、UFT30接口数据更新数据缺失问题 2026-05-29 09:40:33 +08:00
taocong b55596078d 元数据页面实现更新UF20、UFT30接口数据 2026-05-28 19:44:36 +08:00
taocong 5f3a8cc8dd 元数据页面风格调整 2026-05-28 13:37:53 +08:00
taocong 3e05ecd9d2 页面控件替换,新增元数据查询页面(功能未实现) 2026-05-28 08:57:13 +08:00
taocong aa1bb42467 解决设置界面及保存配置后乱码问题 2026-05-27 09:40:44 +08:00
taocong 2af12b298c 新增功能查询页面,支持按名称和功能号查询 2026-05-26 16:13:51 +08:00
taocong c097120ca3 新增缓存数据库存储、转码进度显示 2026-05-26 14:33:03 +08:00
taocong fd38553be1 折叠按钮修改,转码页面优化 2026-05-26 09:59:17 +08:00
166 changed files with 10946 additions and 533 deletions
+10 -1
View File
@@ -5,9 +5,18 @@
!python_bindings/** !python_bindings/**
!src/ !src/
!src/** !src/**
!help/
!help/**
!resources/
!resources/**
!*.bat !*.bat
!*.rc !*.rc
!*.prp !*.prp
!*.cpp !*.cpp
!*.qrc !*.qrc
!.gitignore !*.h
!*.pro
!*.md
!.gitignore
!.gitkeep
!update_info.json
+24
View File
@@ -0,0 +1,24 @@
# 图标设置说明
## 已完成配置
1. **应用程序窗口图标** - 已设置为 `resources/icons/转码工具.svg`
2. **资源文件** - 图标已添加到 `resources.qrc`
## Windows 可执行程序图标(可选)
如需设置 Windows 可执行文件的图标(任务栏、文件管理器显示的图标),需要:
### 方法 1:使用在线工具转换
1.`resources/images/转码工具.png` 转换为 `.ico` 格式
2. 推荐尺寸:包含 16x16, 32x32, 48x48, 256x256 等多种尺寸
3. 保存为 `app.ico` 并放在项目根目录
4. `app.rc` 已配置引用 `app.ico`
### 方法 2:使用 ImageMagick 转换
```bash
magick convert 转码工具.png -define icon:auto-resize=256,128,96,64,48,32,16 app.ico
```
### 重新编译
设置好图标后重新编译项目即可。
+116
View File
@@ -0,0 +1,116 @@
# Uft30ChangeCodeUFT30 代码转换工具)
一款基于 **Qt 6 + MinGW** 开发的桌面工具,用于将 UF20 业务代码转换为 UFT3 格式,并提供功能查询、代码检索、元数据更新、数据映射等配套能力。UI 采用 [ElaWidgetTools](3rd/ElaWidgetTools) 组件库。
- 当前版本:**v1.4.0**
- 运行平台:Windows
- 界面框架:Qt Widgets + ElaWidgetTools
## 功能特性
| 模块 | 说明 |
| --- | --- |
| 业务转码 | 将 UF20 函数批量转换为 UFT3 函数格式,支持进度显示与中止 |
| 功能查询 | UF20 / UFT3 功能(函数)查找,支持 2.0 账户功能查询 |
| 代码检索 | "转码业务" / "非转码业务" 调用链路分析,支持涉及文件与调用链导出 |
| 元数据更新 | 更新 UF20 / UFT3 标准字段、数据类型、接口数据等,支持增量更新与强制全量更新 |
| 数据映射 | 表映射、函数映射 |
| 设置 | 配置 UFT30 / UF20 项目路径、账户环境路径、转码生成路径等 |
| 其它 | 在线帮助文档、关于页、版本更新提示;全局文字可选中复制、表格 Ctrl+C 导出 |
## 目录结构
```
Uft30ChangeCode/
├─ main.cpp # 程序入口,安装全局文字可复制过滤器
├─ Uft30ChangeCode.pro # qmake 工程文件
├─ build.bat # 一键构建脚本(Release
├─ build_advanced.bat # 进阶构建脚本
├─ config/
│ └─ change_code.ini # 默认配置模板(构建时按需部署到 bin/uf2touft3/
├─ src/
│ ├─ mainwindow/ # 主窗口
│ ├─ pages/ # 各功能页面(batchconvert / codesearch / functionsearch / metadatupdate / datamapping / settings / about
│ ├─ metadataupdate/ # 元数据扫描与处理(UF2 / UFT3 接口、数据类型、字段等)
│ ├─ pythonrunner/ # 调用外部转码工具(uf2touft3.exe
│ ├─ components/ # 自定义控件
│ └─ utils/ # 公共工具(日志、配置读取、调用链分析、版本检查、常量等)
├─ 3rd/ # 第三方库(ElaWidgetTools 等)
├─ resources/ resources.qrc # 图标等资源
├─ help/ # 帮助文档(Writerside
└─ update_info.json # 版本发布说明
```
## 构建
### 环境要求
- Qt 6.6.2mingw_64
- MinGW 11.2.0Qt Tools 自带)
`build.bat` 中默认路径如下,如与本机不一致请自行修改:
```bat
set QT_PATH=D:\01.InstallInfo\InstallPath\QT\6.6.2\mingw_64
set MINGW_PATH=D:\01.InstallInfo\InstallPath\QT\Tools\mingw1120_64
```
### 一键构建
```bat
build.bat
```
脚本会依次执行:关闭正在运行的程序 → 清理旧构建产物 → `qmake``mingw32-make release``windeployqt` 部署 Qt 运行库 → 按需部署默认 `change_code.ini`
构建产物:`bin\Uft30ChangeCode.exe`
> 注意:外部转码工具 `bin\uf2touft3\uf2touft3.exe` 需自行放置,程序运行时依赖它完成实际转码。
### 手动构建
```bat
qmake Uft30ChangeCode.pro -spec win32-g++
mingw32-make release
```
## 配置说明(`config/change_code.ini`
构建时若 `bin/uf2touft3/change_code.ini` 不存在,会从 `config/change_code.ini` 拷贝一份默认模板;已存在则保留用户配置不覆盖。
### `[codeSearch]` 代码检索路径
| 键 | 说明 |
| --- | --- |
| `convertPath` | 转码业务代码检索路径 |
| `searchPath` | 非转码业务代码检索路径 |
### `[log]` 日志配置
| 键 | 默认值 | 说明 |
| --- | --- | --- |
| `level` | `INFO` | 日志等级:`DEBUG` < `INFO` < `WARNING` < `ERROR`,等级越高输出越少 |
| `path` | `./logs` | 日志输出目录(相对路径基于 exe 目录,绝对路径直接生效) |
| `file` | `change_code.log` | 日志文件名 |
| `fileEnabled` | `true` | 是否写入日志文件 |
| `maxSize` | `10MB` | 单个日志文件最大大小(支持 `B`/`KB`/`MB`/`GB``0` 表示不限制) |
| `maxFiles` | `100` | 日志文件轮转保留数量 |
| `maxAge` | `30` | 日志文件保留天数(`0` 表示不限制) |
| `console` | `true` | 是否在控制台打印日志 |
| `caller` | `false` | 是否在日志中附加调用堆栈(仅 Windows 生效) |
| `format` | `text` | 日志格式:`text` / `json` |
| `timeFormat` | `2006-01-02 15:04:05.000` | 时间格式(支持 Go 风格布局或 Qt 格式) |
## 帮助文档
更多使用说明见 `help/` 目录(Writerside 工程),涵盖业务转码、功能查询、代码检索、元数据更新、数据映射、设置等模块的图文操作指引。
## 版本记录
| 版本 | 主要更新 |
| --- | --- |
| v1.4.0 | 提升业务转码性能;元数据更新支持增量/强制全量;新增代码检索与函数调用链路分析;支持全局文字选中复制及表格 Ctrl+C 导出 |
| v1.3.0 | 修复设置路径不生效;修复元数据更新后转码函数检查状态异常 |
| v1.2.0 | 新增数据映射(表映射、函数映射);新增版本更新提示 |
| v1.1.0 | 支持 2.0 账户功能查询及业务转码 |
| v1.0.0 | 首个版本:业务转码、UF20/UFT3 功能查找、帮助文档、接口元数据更新、设置 |
+101
View File
@@ -0,0 +1,101 @@
QT += core gui widgets sql network concurrent
TARGET = Uft30ChangeCode
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += main.cpp \
src/mainwindow/mainwindow.cpp \
src/pythonrunner/PythonRunner.cpp \
src/pages/about/aboutdialog.cpp \
src/pages/batchconvert/batchconvertpage.cpp \
src/pages/codesearch/codesearchpage.cpp \
src/pages/functionsearch/functionsearchpage.cpp \
src/pages/functionsearch/uf20functionsearchpage.cpp \
src/pages/functionsearch/uft3functionsearchpage.cpp \
src/components/elasearchedit.cpp \
src/components/interdialog.cpp \
src/pages/settings/settingspage.cpp \
src/pages/metadatupdate/metadatupdatepage.cpp \
src/pages/datamapping/tablemappingpage.cpp \
src/pages/datamapping/functionmappingpage.cpp \
src/metadataupdate/filedb.cpp \
src/metadataupdate/uf2interface.cpp \
src/metadataupdate/uft3interface.cpp \
src/metadataupdate/metadataprocessor.cpp \
src/metadataupdate/uf2datatype.cpp \
src/metadataupdate/uf2stdfields.cpp \
src/metadataupdate/uf2errnumber.cpp \
src/metadataupdate/uft3datatype.cpp \
src/metadataupdate/uft3stdfields.cpp \
src/metadataupdate/uft3table.cpp \
src/utils/datacache.cpp \
src/utils/business_strategy.cpp \
src/utils/configreaderbase.cpp \
src/utils/uf2configreader.cpp \
src/utils/uft3configreader.cpp \
src/utils/logmanager.cpp \
src/utils/configmanager.cpp \
src/utils/callchainanalyzer.cpp \
src/utils/version.cpp \
src/utils/versionchecker.cpp
HEADERS += src/mainwindow/mainwindow.h \
src/pythonrunner/PythonRunner.h \
src/pages/about/aboutdialog.h \
src/pages/batchconvert/batchconvertpage.h \
src/pages/codesearch/codesearchpage.h \
src/pages/functionsearch/functionsearchpage.h \
src/pages/functionsearch/uf20functionsearchpage.h \
src/pages/functionsearch/uft3functionsearchpage.h \
src/components/elasearchedit.h \
src/components/interdialog.h \
src/pages/settings/settingspage.h \
src/pages/metadatupdate/metadatupdatepage.h \
src/pages/datamapping/tablemappingpage.h \
src/pages/datamapping/functionmappingpage.h \
src/metadataupdate/filedb.h \
src/metadataupdate/uf2interface.h \
src/metadataupdate/uft3interface.h \
src/metadataupdate/metadataprocessor.h \
src/metadataupdate/uf2datatype.h \
src/metadataupdate/uf2stdfields.h \
src/metadataupdate/uf2errnumber.h \
src/metadataupdate/uft3datatype.h \
src/metadataupdate/uft3stdfields.h \
src/metadataupdate/uft3table.h \
src/utils/datacache.h \
src/utils/business_strategy.h \
src/utils/configreaderbase.h \
src/utils/uf2configreader.h \
src/utils/uft3configreader.h \
src/utils/logmanager.h \
src/utils/configmanager.h \
src/utils/callchainanalyzer.h \
src/utils/version.h \
src/utils/versionchecker.h
RESOURCES += resources.qrc
CONFIG += c++17
CONFIG += debug_and_release
QMAKE_CXXFLAGS += -Wall -Wextra
QMAKE_LFLAGS += -fno-use-linker-plugin
INCLUDEPATH += src
INCLUDEPATH += 3rd/ElaWidgetTools/include
win32 {
LIBS += -L$$PWD/3rd/ElaWidgetTools/lib -lElaWidgetTools
LIBS += -ld3d11 -ldxgi
LIBS += -ldbghelp # LogManager stack trace (caller)
RC_FILE = app.rc
}
DESTDIR = $$PWD/bin
OBJECTS_DIR = $$PWD/build
MOC_DIR = $$PWD/build
RCC_DIR = $$PWD/build
+1 -1
View File
@@ -1 +1 @@
IDI_ICON1 ICON DISCARDABLE "app.ico" IDI_ICON1 ICON DISCARDABLE "resources/images/ChangeCode.ico"
+23 -2
View File
@@ -66,8 +66,8 @@ echo [OK] qmake done
echo. echo.
REM Compile REM Compile
echo [4/4] Compiling Debug version... echo [4/4] Compiling Release version...
mingw32-make debug mingw32-make release
if errorlevel 1 ( if errorlevel 1 (
echo. echo.
echo ERROR: Compilation failed echo ERROR: Compilation failed
@@ -76,6 +76,27 @@ if errorlevel 1 (
) )
echo. echo.
REM Deploy Qt runtime dependencies (auto-copy required Qt DLLs/plugins)
echo Deploying Qt runtime dependencies...
windeployqt --release bin\Uft30ChangeCode.exe
if errorlevel 1 (
echo [WARN] windeployqt failed, please deploy Qt DLLs manually
) else (
echo [OK] Qt dependencies deployed
)
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)
) else (
echo [SKIP] bin\uf2touft3\change_code.ini already exists, keep user config
)
echo.
echo ========================================== echo ==========================================
echo Build SUCCESSFUL! echo Build SUCCESSFUL!
echo ========================================== echo ==========================================
+11
View File
@@ -147,4 +147,15 @@ if exist "bin\uf2touft3\uf2touft3.exe" (
echo [WARN] Please make sure bin\uf2touft3\uf2touft3.exe exists echo [WARN] Please make sure bin\uf2touft3\uf2touft3.exe exists
) )
echo. 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)
) else (
echo [SKIP] bin\uf2touft3\change_code.ini already exists, keep user config
)
echo.
pause pause
+3
View File
@@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml
+1
View File
@@ -0,0 +1 @@
Uft30ChangeCodeHelp
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/help.iml" filepath="$PROJECT_DIR$/help.iml" />
</modules>
</component>
</project>
+3
View File
@@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Writerside.iml" filepath="$PROJECT_DIR$/.idea/Writerside.iml" />
</modules>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE categories
SYSTEM "https://resources.jetbrains.com/writerside/1.0/categories.dtd">
<categories>
<category id="wrs" name="Writerside documentation" order="1"/>
</categories>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE buildprofiles SYSTEM "https://resources.jetbrains.com/writerside/1.0/build-profiles.dtd">
<buildprofiles xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/build-profiles.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<variables>
<custom-favicons>ChangeCode.png</custom-favicons>
</variables>
</buildprofiles>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE terms SYSTEM "https://resources.jetbrains.com/writerside/1.0/glossary.dtd">
<terms>
<term name="foo">
Description of what "foo" is.
</term>
</terms>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE instance-profile
SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd">
<instance-profile id="hi"
name="Uft30ChangeCode"
start-page="Uft30ChangeCode.md">
<toc-element topic="Uft30ChangeCode.md">
<toc-element topic="设置.md"/>
</toc-element>
<toc-element topic="功能转码.md">
<toc-element topic="业务转码.md"/>
<toc-element topic="代码检索.md"/>
</toc-element>
<toc-element topic="功能查询.md">
<toc-element topic="UFT3功能查询.md"/>
<toc-element topic="UF20功能查询.md"/>
</toc-element>
<toc-element topic="数据映射.md">
<toc-element topic="表替换.md"/>
<toc-element topic="函数替换.md"/>
</toc-element>
<toc-element topic="元数据更新.md"/>
<toc-element topic="更新记录.md">
<toc-element topic="update-1-4-0.md"/>
<toc-element topic="update-1-3-0.md"/>
<toc-element topic="update-1-2-0.md"/>
<toc-element topic="update-1-1-0.md"/>
<toc-element topic="update-1-0-0.md"/>
</toc-element>
<toc-element topic="下载.md"/>
</instance-profile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rules SYSTEM "https://resources.jetbrains.com/writerside/1.0/redirection-rules.dtd">
<rules>
<!-- format is as follows
<rule id="<unique id>">
<accepts>page.html</accepts>
</rule>
-->
<rule id="42b8a0d">
<description>Created after removal of "Empty MD Topic" from Uft30ChangeCode</description>
<accepts>Empty-MD-Topic.html</accepts>
</rule>
</rules>
@@ -0,0 +1,17 @@
# UF20功能查询
查询UFT3系统中的函数信息。
## 查询条件
从下拉框选择查询(功能名称、功能编号),输入框输入查询关键词,点击"Aa"切换精确/模糊匹配模式,点击放大镜图标或按回车执行查询
- 功能名称
<img src="功能名称查询.png" alt="功能名称查询"/>
- 功能编号
<img src="模糊查询uf20.png" alt="模糊查询uf20"/>
<img src="精准查询uf20.png" alt="精准查询uf20"/>
## 双击添加
双击选中行添加转码业务,只支持LS功能添加
<img src="双击添加.png" alt="双击添加"/>
@@ -0,0 +1,12 @@
# UFT3功能查询
查询UFT3系统中的函数信息。
## 查询条件
从下拉框选择查询(功能名称、功能编号),输入框输入查询关键词,点击"Aa"切换精确/模糊匹配模式,点击放大镜图标或按回车执行查询
- 功能名称
<img src="功能名称查询uft3.png" alt="功能名称查询uft3"/>
- 功能编号
<img src="模糊查询UFT3.png" alt="模糊查询UFT3"/>
<img src="精准查询uft3.png" alt="精准查询uft3"/>
@@ -0,0 +1,3 @@
# Uft30ChangeCode
提供UFT30元数据、UF20公共资源搜索功能,支持业务转码
+11
View File
@@ -0,0 +1,11 @@
# 1.0.0
1.支持业务转码
2.支持代码uf20、uft3功能查找
3.支持帮助菜单和在线帮助文档
4.支持接口元数据更新
5.设置菜单UFT30项目环境路径、UF20项目环境路径、UF20账户环境路径、转码生成路径、支持弹窗选择目录。
+3
View File
@@ -0,0 +1,3 @@
# 1.1.0
1.支持2.0账户功能查询及业务转码
+5
View File
@@ -0,0 +1,5 @@
# 1.2.0
1.新增数据映射页面,表映射、函数映射
2.新增版本更新提示功能
+5
View File
@@ -0,0 +1,5 @@
# 1.3.0
1.修复设置路径不生效
2.修复元数据更新后业务转码模块对函数检查状态异常问题
+11
View File
@@ -0,0 +1,11 @@
# 1.4.0
1.提升业务转码性能;
2.元数据更新支持增量更新和强制全量更新;
3.新增代码检索功能;
4.支持函数调用链路分析;
5.支持全局文字可选中复制及表格Ctrl+C导出;
+22
View File
@@ -0,0 +1,22 @@
# 下载
## 全量更新
下载全量包,解压即用。
> 1.0.0版本是基础版本,包含所需功能,如果是第一次下载,请选择全量包。
{style="note"}
## 增量更新
下载增量包,解压后替换原文件即可
> 1.0.0版本是基础版本,如若要从旧版升级到1.0.0及以上版本,可以选择增量版本下载,并将旧版根目录下的uf2touft3和当前目录替换覆盖到新版根目录。
{style="note"}
## 下载
| 版本 | 更新日期 | 全量包 | 增量包 |
|-------|------------|----------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
| 1.4.0 | 2026-07-21 | [Uft30ChangeCode-1.4.0.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.4.0.zip) | [Uft30ChangeCode-1.4.0-update.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.4.0-update.zip) |
| 1.3.0 | 2026-07-07 | [Uft30ChangeCode-1.3.0.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.3.0.zip) | [Uft30ChangeCode-1.3.0-update.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.3.0-update.zip) |
| 1.2.0 | 2026-07-02 | [Uft30ChangeCode-1.2.0.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.2.0.zip) | [Uft30ChangeCode-1.2.0-update.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.2.0-update.zip) |
| 1.1.0 | 2026-07-01 | [Uft30ChangeCode-1.1.0.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.1.0.zip) | [Uft30ChangeCode-1.1.0-update.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.1.0-update.zip) |
| 1.0.0 | 2026-06-01 | [Uft30ChangeCode-1.0.0.zip](http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.0.0.zip) | |
+27
View File
@@ -0,0 +1,27 @@
# 业务转码
将UF20函数转换为UFT3函数格式的核心功能。
## 功能列表管理
点击"添加"按钮,输入函数名添加到转码列表
<img src="添加函数.png" alt="添加函数"/>
点击表格操作按钮可编辑函数名,如下:
<img src="编辑.png" alt="编辑"/>
选中行后点击"删除"移除函数、点击"清空"清空所有函数,如下:
<img src="删除.png" alt="删除"/>
## 配置文件同步
- 自动读取 uf2touft3/cust.json 配置
- 配置文件外部修改时自动刷新列表
## 转换操作
点击"开始转换"启动转换任务,转换过程中可查看进度条和状态信息,如下:
<img src="转码.png" alt="转码"/>
点击"停止转换"中止当前任务,如下:
<img src="停止转码.png" alt="停止转码"/>
+11
View File
@@ -0,0 +1,11 @@
# 代码检索
支持"转码业务"、"非转码业务"调用链路查询和导出
## 转码业务
支持查询调用链路和导出查询函数所涉及的文件以及调用链路导出
<img src="转码业务.png" alt="转码业务"/>
## 非转码业务
支持查询调用链路和调用链路导出
<img src="非转码业务.png" alt="非转码业务"/>
+25
View File
@@ -0,0 +1,25 @@
# 元数据更新
更新UF20和UFT3系统的元数据配置。
## 项目配置
需要先配置UFT30项目路径和UF20项目路径。
在设置-首选项页面,配置UFT30项目路径,例如:
``在设置-首选项页面,配置UFT30项目路径,例如:``
<img src="UFT3项目环境路径.png" alt="UFT3项目环境路径"/>
在设置-首选项页面,配置UF20项目路径,例如:
``E:\05.code\04.SVN\secu\dep1\hstrade20\trunk\Sources\DevCodes\经纪业务运营平台V21``
<img src="UF20项目环境路径.png" alt="UF20项目环境路径"/>
## UFT3元数据
在 元数据更新 页面,选择更新范围(标准字段、接口数据等)并点击更新UFT3按钮完成元数据更新。
<img src="UFT3元数据.png" alt="UFT3元数据"/>
**备注:接口数据更新比较久**
## 更新UF20元数据
在 元数据更新 页面,选择更新范围(标准字段、标准组件等)并点击更新UF2按钮完成公共资源更新
<img src="UF20元数据.png" alt="UF20元数据"/>
**备注:接口数据更新比较久**
+5
View File
@@ -0,0 +1,5 @@
# 函数替换
uf2函数和uft3函数之间的映射关系,支持增、删、改、查功能
<img src="函数替换.png" alt="函数替换"/>
+6
View File
@@ -0,0 +1,6 @@
# 功能查询
## 快速跳转
[UFT3功能查询](http://10.20.163.105:6045/uft3changecode/UFT3功能查询.html)
[UF20功能查询](http://10.20.163.105:6045/uft3changecode/UF20功能查询.html)
+34
View File
@@ -0,0 +1,34 @@
# 功能转码
提供UFT3业务转码功能,将uf20代码转成UFT3代码
## 项目配置
在使用UFT3功能转码前,需要先配置UFT30项目路径,并更新UFT3、UF20元数据
1.配置项目路径
- UFT3项目环境路径
在设置-首选项页面,配置UFT30项目路径,转码使用,例如:
``E:\05.code\03.GIT\feature_ses``
<img src="UFT3项目环境路径.png" alt="UFT3项目环境路径"/>
- UF20项目环境路径
在设置-首选项页面,UF20项目环境路径, 转码需要读取相关的文件, 例如:
``E:\05.code\04.SVN\secu\dep1\hstrade20\trunk\Sources\DevCodes\经纪业务运营平台V21``
<img src="UF20项目环境路径.png" alt="UF20项目环境路径"/>
- UF20账户环境路径
在设置-首选项页面,配置UF20账户环境路径, 转码需要读取账户相关的文件,例如:
``E:\05.code\04.SVN\secu\dep1\hstrade20\trunk\Sources\DevCodes_Acct1\客户账户管理系统V22``
<img src="UF20账户环境路径.png" alt="UF20账户环境路径"/>
- 转码生成路径
在设置-首选项页面,配置转码生成路径, 用于存放生成的转码文件, 例如:
``F:\07.tools\01.hstools\Uft30ChangeCode\bin\uf2touft3\change_code``
<img src="转码生成路径.png" alt="转码生成路径"/>
## 快速跳转
[业务转码](http://10.20.163.105:6045/uft3changecode/业务转码.html)
[代码检索](http://10.20.163.105:6045/uft3changecode/业务转码.html)
+6
View File
@@ -0,0 +1,6 @@
# 数据映射
## 快速跳转
[表替换](http://10.20.163.105:6045/uft3changecode/表替换.html)
[函数替换](http://10.20.163.105:6045/uft3changecode/函数替换.html)
+3
View File
@@ -0,0 +1,3 @@
# 更新记录
Start typing here...
+6
View File
@@ -0,0 +1,6 @@
# 表替换
uf2表和uft3表之间的映射关系,支持增、删、改、查功能
<img src="表替换.png" alt="表替换"/>
+27
View File
@@ -0,0 +1,27 @@
# 设置
## UFT3项目环境路径
在设置-首选项页面,配置UFT30项目路径,转码使用,例如:
``E:\05.code\03.GIT\feature_ses``
## UF20项目环境路径 {id="uf20_1"}
在设置-首选项页面,UF20项目环境路径, 转码需要读取相关的文件, 例如:
``E:\05.code\04.SVN\secu\dep1\hstrade20\trunk\Sources\DevCodes\经纪业务运营平台V21``
## UF20账户环境路径
在设置-首选项页面,配置UF20账户环境路径, 转码需要读取账户相关的文件,例如:
``E:\05.code\04.SVN\secu\dep1\hstrade20\trunk\Sources\DevCodes_Acct1\客户账户管理系统V22``
## 转码生成路径
在设置-首选项页面,配置转码生成路径, 用于存放生成的转码文件, 例如:
``F:\07.tools\01.hstools\Uft30ChangeCode\bin\uf2touft3\change_code``
## 主题切换
包含日间模式和夜间模式。
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE vars SYSTEM "https://resources.jetbrains.com/writerside/1.0/vars.dtd">
<vars>
<var name="product" value="Writerside"/>
</vars>
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ihp SYSTEM "https://resources.jetbrains.com/writerside/1.0/ihp.dtd">
<ihp version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/writerside-cfg.xsd">
<topics dir="topics"/>
<images dir="images" web-path="images"/>
<categories src="c.list"/>
<vars src="v.list"/>
<instance src="hi.tree"/>
</ihp>
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-07-02T10:44:03.5859325"><title>下载 | Uft30ChangeCode</title><script type="application/json" id="virtual-toc-data">[{"id":"qm9v8k_3","level":0,"title":"全量更新","anchor":"#qm9v8k_3"},{"id":"qm9v8k_4","level":0,"title":"增量更新","anchor":"#qm9v8k_4"},{"id":"qm9v8k_5","level":0,"title":"下载","anchor":"#qm9v8k_5"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.10.0-b419/app.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="16x16" href="images/ChangeCode.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="下载 | Uft30ChangeCode"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Uft30ChangeCode Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/下载.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="下载 | Uft30ChangeCode"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/下载.html#webpage",
"url": "writerside-documentation/下载.html",
"name": "下载 | Uft30ChangeCode",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Uft30ChangeCode Help"
}</script><!-- End Schema.org --></head><body data-id="下载" data-main-title="下载" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Uft30ChangeCode Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="下载" id="下载.md">下载</h1><section class="chapter"><h2 id="qm9v8k_3" data-toc="qm9v8k_3">全量更新</h2><p id="qm9v8k_6">下载全量包,解压即用。</p><aside class="prompt" data-type="note" data-title="" id="qm9v8k_7"><p id="qm9v8k_8">1.0.0版本是基础版本,包含所需功能,如果是第一次下载,请选择全量包。</p></aside></section><section class="chapter"><h2 id="qm9v8k_4" data-toc="qm9v8k_4">增量更新</h2><p id="qm9v8k_9">下载增量包,解压后替换原文件即可</p><aside class="prompt" data-type="note" data-title="" id="qm9v8k_10"><p id="qm9v8k_11">1.0.0版本是基础版本,如若要从旧版升级到1.0.0及以上版本,可以选择增量版本下载,并将旧版根目录下的uf2touft3和当前目录替换覆盖到新版根目录。</p></aside></section><section class="chapter"><h2 id="qm9v8k_5" data-toc="qm9v8k_5">下载</h2><div class="table-wrapper"><table class="wide" id="qm9v8k_12"><thead><tr class="ijRowHead" id="qm9v8k_13"><th id="qm9v8k_16"><p>版本</p></th><th id="qm9v8k_17"><p>更新日期</p></th><th id="qm9v8k_18"><p>全量包</p></th><th id="qm9v8k_19"><p>增量包</p></th></tr></thead><tbody><tr id="qm9v8k_14"><td id="qm9v8k_20"><p>1.1.0</p></td><td id="qm9v8k_21"><p>2026-07-02</p></td><td id="qm9v8k_22"><p><a href="http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.1.0.zip" id="qm9v8k_24" data-external="true" rel="noopener noreferrer">Uft30ChangeCode-1.1.0.zip</a></p></td><td id="qm9v8k_23"><p><a href="http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.1.0-update.zip" id="qm9v8k_25" data-external="true" rel="noopener noreferrer">Uft30ChangeCode-1.1.0-update.zip</a></p></td></tr><tr id="qm9v8k_15"><td id="qm9v8k_26"><p>1.0.0</p></td><td id="qm9v8k_27"><p>2026-06-01</p></td><td id="qm9v8k_28"><p><a href="http://10.20.163.105:6045/download/uft3changecode/Uft30ChangeCode-1.0.0.zip" id="qm9v8k_30" data-external="true" rel="noopener noreferrer">Uft30ChangeCode-1.0.0.zip</a></p></td><td id="qm9v8k_29"></td></tr></tbody></table></div></section><div class="last-modified">Last modified: 02 七月 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="update-1-0-0.html" class="navigation-links__prev">1.0.0</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.10.0-b419/app.js"></script></body></html>
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-07-02T10:44:03.5744178"><title>更新记录 | Uft30ChangeCode</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.10.0-b419/app.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="16x16" href="images/ChangeCode.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="更新记录 | Uft30ChangeCode"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Uft30ChangeCode Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/更新记录.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="更新记录 | Uft30ChangeCode"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/更新记录.html#webpage",
"url": "writerside-documentation/更新记录.html",
"name": "更新记录 | Uft30ChangeCode",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Uft30ChangeCode Help"
}</script><!-- End Schema.org --></head><body data-id="更新记录" data-main-title="更新记录" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Uft30ChangeCode Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="更新记录" id="更新记录.md">更新记录</h1><p id="r4m9bp_3">Start typing here...</p><div class="last-modified">Last modified: 02 七月 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="元数据更新.html" class="navigation-links__prev">元数据更新</a><a href="update-1-1-0.html" class="navigation-links__next">1.1.0</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.10.0-b419/app.js"></script></body></html>
+10
View File
@@ -0,0 +1,10 @@
server {
listen 6045;
server_name _;
location /uft3changecode/ {
alias /home/taoc/uft30help/uft3changecode/;
index index.html;
try_files $uri $uri/ /uft3changecode/index.html;
}
}
+35
View File
@@ -0,0 +1,35 @@
worker_processes 1;
pid /home/taoc/nginx/logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include /home/taoc/nginx/conf/mime.types;
default_type application/octet-stream;
access_log /home/taoc/nginx/logs/help_access.log;
error_log /home/taoc/nginx/logs/help_error.log;
sendfile on;
keepalive_timeout 65;
server {
listen 6045;
server_name _;
root /home/taoc/uft30help/html/;
index index.html index.htm;
location /uft3changecode/ {
index index.html index.htm;
try_files $uri $uri/ /uft3changecode/index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
+89
View File
@@ -0,0 +1,89 @@
#!/bin/bash
echo "=== Force Restart Help Documentation Server ==="
echo ""
echo "1. Killing all nginx processes..."
pkill -f nginx 2>/dev/null || true
sleep 2
echo "2. Checking for remaining nginx processes..."
if pgrep nginx > /dev/null; then
echo " Force killing remaining nginx processes..."
pkill -9 nginx 2>/dev/null || true
sleep 1
fi
echo "3. Checking port 6045..."
lsof -i :6045 2>/dev/null || echo " Port 6045 is free"
echo ""
echo "4. Starting fresh nginx instance..."
NGINX_BIN="/home/taoc/nginx/sbin/nginx"
HELP_DOCS_DIR="/home/taoc/uft30help/"
CONF_FILE="$HELP_DOCS_DIR/workspace/nginx.conf"
if [ ! -f "$CONF_FILE" ]; then
echo " Copying nginx configuration..."
cp "$(dirname "$0")/nginx.conf" "$CONF_FILE"
fi
if [ ! -f "$HELP_DOCS_DIR/workspace/help.conf" ]; then
echo " Copying server configuration..."
cp "$(dirname "$0")/help.conf" "$HELP_DOCS_DIR/workspace/help.conf"
fi
if [ ! -f "$HELP_DOCS_DIR/uft3changecode/index.html" ]; then
echo " Creating default index.html..."
cat > "$HELP_DOCS_DIR/uft3changecode/index.html" << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>UFT30ChangeCode Help</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; background: #f5f5f5; }
.container { background: white; padding: 40px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); display: inline-block; }
.logo { font-size: 64px; margin-bottom: 20px; }
h1 { color: #667eea; margin: 0; }
.subtitle { color: #666; margin-top: 10px; }
.path { background: #f0f0f0; padding: 10px 20px; border-radius: 5px; margin-top: 20px; font-family: monospace; }
</style>
</head>
<body>
<div class="container">
<div class="logo">📖</div>
<h1>UFT30ChangeCode Help</h1>
<p class="subtitle">Help Documentation Server</p>
<div class="path">Path: /uft3changecode/</div>
</div>
</body>
</html>
EOF
fi
echo "5. Testing configuration..."
"$NGINX_BIN" -t -c "$CONF_FILE"
echo "6. Starting nginx..."
"$NGINX_BIN" -c "$CONF_FILE"
sleep 2
echo ""
echo "=== Server Status ==="
if pgrep nginx > /dev/null; then
SERVER_IP=$(ip addr show | grep inet | grep -v '127.0.0.1' | grep -v '::1' | head -1 | awk '{print $2}' | cut -d'/' -f1)
echo "✅ Server started successfully!"
echo ""
echo "Access URLs:"
echo " Local: http://localhost:6045/uft3changecode/"
if [ -n "$SERVER_IP" ]; then
echo " Remote: http://$SERVER_IP:6045/uft3changecode/"
fi
echo ""
echo "Verify with:"
echo " curl http://localhost:6045/uft3changecode/"
else
echo "❌ Failed to start server"
echo "Check error log: /home/taoc/uft30help/workspace/logs/help_error.log"
fi
+56
View File
@@ -0,0 +1,56 @@
#!/bin/bash
NGINX_BIN="/home/taoc/nginx/sbin/nginx"
CONF_DIR="/home/taoc/uft30help/workspace"
HTML_DIR="/home/taoc/uft30help/html/uft3changecode"
CONF_FILE="$CONF_DIR/nginx.conf"
echo "=== Starting Help Documentation Server (Port 6045) ==="
echo ""
echo "1. Checking directories..."
if [ ! -d "$CONF_DIR" ]; then
mkdir -p "$CONF_DIR"
echo " Created config dir: $CONF_DIR"
else
echo " Config dir exists: $CONF_DIR"
fi
if [ ! -d "$HTML_DIR" ]; then
mkdir -p "$HTML_DIR"
echo " Created HTML dir: $HTML_DIR"
else
echo " HTML dir exists: $HTML_DIR"
fi
echo ""
echo "2. Killing all nginx processes..."
pkill -f nginx 2>/dev/null || true
sleep 2
pkill -9 nginx 2>/dev/null || true
sleep 1
echo " Done"
echo ""
echo "3. Starting nginx..."
"$NGINX_BIN" -c "$CONF_FILE"
sleep 2
echo ""
echo "4. Testing..."
echo ""
echo "=== Server Status ==="
if pgrep nginx > /dev/null; then
SERVER_IP=$(ip addr show | grep inet | grep -v '127.0.0.1' | grep -v '::1' | head -1 | awk '{print $2}' | cut -d'/' -f1)
echo "✅ Server running!"
echo ""
echo "=== Access URLs ==="
echo " Local: http://localhost:6045/uft3changecode/"
if [ -n "$SERVER_IP" ]; then
echo " Remote: http://$SERVER_IP:6045/uft3changecode/"
fi
else
echo "❌ Server not running"
echo "Error log:"
tail -20 /home/taoc/uft30help/workspace/logs/help_error.log
fi
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
echo "Checking Help Documentation Server status..."
if pgrep nginx &> /dev/null; then
echo "✅ Help Documentation Server is running"
echo "📄 Access URL: http://localhost:8080"
else
echo "❌ Help Documentation Server is not running"
fi
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
NGINX_DIR="/home/taoc/nginx"
NGINX_BIN="$NGINX_DIR/sbin/nginx"
echo "Stopping Help Documentation Server..."
if [ ! -f "$NGINX_BIN" ]; then
echo "Error: nginx binary not found at $NGINX_BIN"
exit 1
fi
"$NGINX_BIN" -s stop
if [ $? -eq 0 ]; then
echo "✅ Help Documentation Server stopped successfully"
else
echo "❌ Failed to stop nginx"
exit 1
fi
+95 -2
View File
@@ -1,12 +1,105 @@
#include "src/mainwindow/mainwindow.h" #include "src/mainwindow/mainwindow.h"
#include "ElaApplication.h" #include "ElaApplication.h"
#include "src/utils/configmanager.h"
#include <QApplication> #include <QApplication>
#include <QIcon>
#include <QCoreApplication>
#include <QEvent>
#include <QKeyEvent>
#include <QClipboard>
#include <QKeySequence>
#include <QLabel>
#include <QAbstractItemView>
#include <QModelIndex>
#include <algorithm>
// 全局文字可复制过滤器:
// 1) 所有 QLabel(含 ElaText)文字可用鼠标选中并复制;
// 2) 表格/树(QAbstractItemView)选中的单元格支持 Ctrl+C 复制,制表符分隔列、换行分隔行。
class TextSelectableFilter : public QObject
{
public:
using QObject::QObject;
protected:
bool eventFilter(QObject *watched, QEvent *event) override
{
if (event->type() == QEvent::Polish) {
if (QLabel *label = qobject_cast<QLabel *>(watched)) {
// 跳过用作图标按钮的 QLabel(有 pixmap 无文本),
// 否则会被强制设为竖线(IBeamCursor),影响点击类图标控件
if (label->pixmap().isNull()) {
label->setTextInteractionFlags(label->textInteractionFlags()
| Qt::TextSelectableByMouse);
label->setCursor(Qt::IBeamCursor);
}
}
} else if (event->type() == QEvent::KeyPress) {
if (auto *view = qobject_cast<QAbstractItemView *>(watched)) {
auto *ke = static_cast<QKeyEvent *>(event);
if (ke->matches(QKeySequence::Copy)) {
return copySelectedCells(view);
}
}
}
return QObject::eventFilter(watched, event);
}
private:
static bool copySelectedCells(QAbstractItemView *view)
{
if (!view->selectionModel()) {
return false;
}
QModelIndexList idxs = view->selectionModel()->selectedIndexes();
if (idxs.isEmpty()) {
return false;
}
// 按行、列排序,保证复制顺序与显示一致
std::sort(idxs.begin(), idxs.end(),
[](const QModelIndex &a, const QModelIndex &b) {
if (a.row() != b.row()) {
return a.row() < b.row();
}
return a.column() < b.column();
});
QString text;
int lastRow = -1;
for (const QModelIndex &idx : idxs) {
if (idx.row() != lastRow) {
if (lastRow != -1) {
text += QLatin1Char('\n');
}
lastRow = idx.row();
} else {
text += QLatin1Char('\t');
}
text += idx.data(Qt::DisplayRole).toString();
}
if (!text.isEmpty()) {
QApplication::clipboard()->setText(text);
return true; // 已处理,消费该事件
}
return false;
}
};
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication a(argc, argv); QApplication a(argc, argv);
a.setWindowIcon(QIcon(":resources/images/ChangeCode.png"));
// 安装全局过滤器:所有页面文字 / 表格树单元格允许用户复制
TextSelectableFilter textSelectableFilter;
a.installEventFilter(&textSelectableFilter);
eApp->init(); eApp->init();
QString basePath = QCoreApplication::applicationDirPath();
ConfigManager::instance()->loadConfig(basePath);
MainWindow w; MainWindow w;
w.show(); w.showMaximized();
return a.exec(); return a.exec();
} }
+9
View File
@@ -1,4 +1,13 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>resources/images/ChangeCode.png</file>
<file>resources/images/bianji.png</file>
<file>resources/images/zhedie_left.png</file>
<file>resources/images/zhedie_right.png</file>
<file>resources/images/max.png</file>
<file>resources/images/min.png</file>
<file>resources/images/update.png</file>
<file>resources/images/expand_all.png</file>
<file>resources/images/collapse_all.png</file>
</qresource> </qresource>
</RCC> </RCC>
+17
View File
@@ -0,0 +1,17 @@
# 资源目录
此目录用于存放项目使用的图标、图片等资源文件。
## 目录结构
```
resources/
├── icons/ # 图标文件
└── images/ # 图片文件
```
## 添加新资源
1. 将资源文件放入对应目录
2. 更新 resources.qrc 文件添加资源引用
3. 重新编译项目
+164
View File
@@ -0,0 +1,164 @@
# 使用帮助
## 概述
欢迎使用 UFT30 Change Code 工具!这是一款强大的编码转换工具,支持批量转码和非LS格式转换。
### 功能导航
```
UFT30 Change Code
├── 📁 功能转码
│ └── 业务转码
├── 📁 功能查询
│ ├── UF20功能查询
│ └── UFT3功能查询
├── 元数据更新
├── 设置
├── 关于
└── 使用技巧
```
点击左侧导航栏的功能项,查看详细的使用说明。
## 功能转码
### 业务转码
将UF20函数转换为UFT3函数格式的核心功能。
#### 功能列表管理
- 点击"添加"按钮,输入函数名添加到转码列表
- 双击表格行可编辑函数名
- 选中行后点击"删除"移除函数
- 点击"清空"清空所有函数
#### 配置文件同步
- 自动读取 uf2touft3/cust.json 配置
- 配置文件外部修改时自动刷新列表
#### 转换操作
- 点击"开始转换"启动转换任务
- 转换过程中可查看进度条和状态信息
- 点击"停止转换"中止当前任务
## 功能查询
### UF20功能查询
查询UF20系统中的函数信息。
#### 查询条件
- 从下拉框选择查询字段:功能名称、英文名称、功能编号
- 输入框输入查询关键词
- 点击"Aa"切换精确/模糊匹配模式
- 点击放大镜图标或按回车执行查询
#### 结果操作
- 双击查询结果可直接添加到转码列表
- 结果表格显示:功能名称、英文名称、功能编号
### UFT3功能查询
查询UFT3系统中的函数信息。
#### 查询条件
- 从下拉框选择查询字段:功能名称、英文名称、功能编号
- 输入框输入查询关键词
- 点击"Aa"切换精确/模糊匹配模式
- 点击放大镜图标或按回车执行查询
#### 结果显示
- 结果表格显示:功能名称、英文名称、功能编号
## 元数据更新
更新UF20和UFT3系统的元数据配置。
### UF20配置更新
- 勾选需要更新的UF20接口项
- 点击"全选"选中所有UF20项
- 点击"取消"清除UF20选择
- 点击"更新UF20"执行更新
### UFT3配置更新
- 勾选需要更新的UFT3接口项
- 点击"全选"选中所有UFT3项
- 点击"取消"清除UFT3选择
- 点击"更新UFT3"执行更新
### 更新内容
- UF20: 接口、标准字段、数据类型、错误编号
- UFT3: 接口、标准字段、数据类型、表结构
## 设置
配置工具的运行参数和选项。
### 路径配置
- 设置Python脚本路径
- 配置转换工具路径
- 设置配置文件目录
### 显示设置
- 调整界面主题
- 设置字体大小
- 配置语言选项
### 高级设置
- 设置自动保存间隔
- 配置日志级别
- 设置最大并发任务数
## 关于
### UFT30 Change Code
一款强大的编码转换工具,支持批量转码和非LS格式转换。
#### 基本信息
- **版本**: 1.0.0
- **作者**: UFT30 Team
- **版权**: Copyright © 2024
- **联系方式**: support@uft30.com
#### 功能特性
- 支持UF20到UFT3函数格式转换
- 批量转换处理
- 非LS格式转换支持
- 功能查询与快速添加
- 元数据自动更新
## 使用技巧
### 高效使用技巧
- 在功能查询中找到需要转换的函数,双击可快速添加到转码列表
- 精确匹配适合已知完整函数名的查询
- 模糊匹配可以使用部分关键词查找
- 配置文件可以手动编辑,工具会自动检测变化
- 批量转换前建议先进行元数据更新
- 使用Ctrl+F快速搜索帮助内容
### 注意事项
- 首次使用前请先执行"元数据更新"加载最新配置
- 转换前建议备份重要文件
- 确保 uf2touft3/uf2touft3.exe 存在且可执行
- 配置文件 uf2touft3/cust.json 会自动同步转码列表
- 大文件转换可能需要较长时间,请耐心等待
+18
View File
@@ -0,0 +1,18 @@
# 💡 使用技巧
## 高效使用技巧
- 在功能查询中找到需要转换的函数,双击可快速添加到转码列表
- 精确匹配适合已知完整函数名的查询
- 模糊匹配可以使用部分关键词查找
- 配置文件可以手动编辑,工具会自动检测变化
- 批量转换前建议先进行元数据更新
- 使用Ctrl+F快速搜索帮助内容
## 注意事项
- 首次使用前请先执行"元数据更新"加载最新配置
- 转换前建议备份重要文件
- 确保 uf2touft3/uf2touft3.exe 存在且可执行
- 配置文件 uf2touft3/cust.json 会自动同步转码列表
- 大文件转换可能需要较长时间,请耐心等待
+1
View File
@@ -0,0 +1 @@
# 此文件用于保持icons目录在Git仓库中
+1
View File
@@ -0,0 +1 @@
# 此文件用于保持images目录在Git仓库中
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

+116
View File
@@ -0,0 +1,116 @@
#include "elasearchedit.h"
#include <QHBoxLayout>
#include "ElaIcon.h"
ElaSearchEdit::ElaSearchEdit(QWidget* parent)
: QWidget(parent), m_exactMatch(false)
{
initUI();
}
ElaSearchEdit::~ElaSearchEdit()
{
}
void ElaSearchEdit::initUI()
{
QHBoxLayout* mainLayout = new QHBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
m_lineEdit = new ElaLineEdit(this);
m_lineEdit->setFixedHeight(35);
m_lineEdit->setPlaceholderText("输入查询内容");
m_lineEdit->setIsClearButtonEnable(false);
QHBoxLayout* inputLayout = new QHBoxLayout(m_lineEdit);
inputLayout->setContentsMargins(0, 0, 4, 0);
inputLayout->addStretch();
m_exactMatchBtn = addToolButton(m_lineEdit, ElaIconType::FontCase, "区分大小写", QSize(26, 26), true, false);
inputLayout->addWidget(m_exactMatchBtn);
resetInputMargin();
mainLayout->addWidget(m_lineEdit);
m_searchBtn = addToolButton(this, ElaIconType::MagnifyingGlass, "搜索", QSize(35, 35), false, false);
mainLayout->addWidget(m_searchBtn);
connectSignals();
}
void ElaSearchEdit::connectSignals()
{
connect(m_lineEdit, &ElaLineEdit::textChanged, this, &ElaSearchEdit::textChanged);
connect(m_lineEdit, &ElaLineEdit::returnPressed, this, &ElaSearchEdit::returnPressed);
connect(m_searchBtn, &ElaToolButton::clicked, this, &ElaSearchEdit::searchClicked);
connect(m_exactMatchBtn, &ElaToolButton::clicked, this, [this](bool checked) {
m_exactMatch = checked;
emit exactMatchChanged(checked);
});
}
void ElaSearchEdit::resetInputMargin()
{
m_lineEdit->setTextMargins(0, 0, 30, 0);
}
ElaToolButton* ElaSearchEdit::addToolButton(QWidget* parent, ElaIconType::IconName icon, const QString& tooltip, const QSize& size, bool checkable, bool checked)
{
ElaToolButton* btn = new ElaToolButton(parent);
btn->setElaIcon(icon);
btn->setFixedSize(size);
btn->setToolTip(tooltip);
btn->setCheckable(checkable);
btn->setChecked(checked);
btn->setCursor(Qt::PointingHandCursor);
return btn;
}
QString ElaSearchEdit::text() const
{
return m_lineEdit->text();
}
void ElaSearchEdit::setText(const QString& text)
{
m_lineEdit->setText(text);
}
void ElaSearchEdit::setPlaceholderText(const QString& placeholderText)
{
m_lineEdit->setPlaceholderText(placeholderText);
}
void ElaSearchEdit::setFixedSize(const QSize& size)
{
m_lineEdit->setFixedSize(size.width() - 35, size.height());
m_searchBtn->setFixedSize(35, size.height());
QWidget::setFixedSize(size);
}
void ElaSearchEdit::setFixedSize(int w, int h)
{
m_lineEdit->setFixedSize(w - 35, h);
m_searchBtn->setFixedSize(35, h);
QWidget::setFixedSize(w, h);
}
void ElaSearchEdit::setFixedHeight(int h)
{
m_lineEdit->setFixedHeight(h);
m_searchBtn->setFixedSize(35, h);
QWidget::setFixedHeight(h);
}
bool ElaSearchEdit::isExactMatch() const
{
return m_exactMatch;
}
void ElaSearchEdit::setExactMatch(bool exact)
{
m_exactMatch = exact;
m_exactMatchBtn->setChecked(exact);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef ELASEARCHEDIT_H
#define ELASEARCHEDIT_H
#include <QWidget>
#include <QHBoxLayout>
#include <QList>
#include "ElaLineEdit.h"
#include "ElaToolButton.h"
class ElaSearchEdit : public QWidget
{
Q_OBJECT
public:
explicit ElaSearchEdit(QWidget* parent = nullptr);
~ElaSearchEdit() override;
QString text() const;
void setText(const QString& text);
void setPlaceholderText(const QString& placeholderText);
void setFixedSize(const QSize& size);
void setFixedSize(int w, int h);
void setFixedHeight(int h);
bool isExactMatch() const;
void setExactMatch(bool exact);
signals:
void textChanged(const QString& text);
void returnPressed();
void exactMatchChanged(bool exact);
void searchClicked();
private:
void initUI();
void connectSignals();
void resetInputMargin();
ElaToolButton* addToolButton(QWidget* parent, ElaIconType::IconName icon, const QString& tooltip, const QSize& size, bool checkable = false, bool checked = false);
ElaLineEdit* m_lineEdit;
ElaToolButton* m_searchBtn;
ElaToolButton* m_exactMatchBtn;
bool m_exactMatch;
};
#endif // ELASEARCHEDIT_H
+116
View File
@@ -0,0 +1,116 @@
#include "interdialog.h"
#include "ElaText.h"
#include "ElaPushButton.h"
#include <QSpacerItem>
InterDialog::InterDialog(QWidget* parent) : ElaDialog(parent)
{
this->setIsFixedSize(true);
this->setWindowModality(Qt::ApplicationModal);
this->setWindowButtonFlags(ElaAppBarType::CloseButtonHint);
this->setAppBarHeight(30);
_mainLayout = new QVBoxLayout(this);
_mainLayout->setContentsMargins(10, 10, 10, 10);
_centralWidget = new QWidget(this);
auto* centralVLayout = new QVBoxLayout(_centralWidget);
centralVLayout->setContentsMargins(0, 0, 0, 0);
_messageLabel = new ElaText("这是消息", _centralWidget);
_messageLabel->setIsWrapAnywhere(true);
centralVLayout->addWidget(_messageLabel);
_buttonWidget = new QWidget(this);
_btnLayout = new QHBoxLayout(_buttonWidget);
_btnLayout->setContentsMargins(0, 0, 0, 0);
_btnLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Minimum));
auto *defaultButton = new QPushButton(_buttonWidget);
defaultButton->setDefault(true);
defaultButton->hide();
_okButton = new ElaPushButton("确认", _buttonWidget);
connect(_okButton, &ElaPushButton::clicked, this, [=]
{
emit okButtonClicked();
accept();
});
_okButton->setLightDefaultColor(ElaThemeColor(ElaThemeType::Light, PrimaryNormal));
_okButton->setLightHoverColor(ElaThemeColor(ElaThemeType::Light, PrimaryHover));
_okButton->setLightPressColor(ElaThemeColor(ElaThemeType::Light, PrimaryPress));
_okButton->setLightTextColor(ElaThemeColor(ElaThemeType::Dark, BasicText));
_okButton->setDarkDefaultColor(ElaThemeColor(ElaThemeType::Dark, PrimaryNormal));
_okButton->setDarkHoverColor(ElaThemeColor(ElaThemeType::Dark, PrimaryHover));
_okButton->setDarkPressColor(ElaThemeColor(ElaThemeType::Dark, PrimaryPress));
_okButton->setDarkTextColor(ElaThemeColor(ElaThemeType::Light, BasicText));
_okButton->setMinimumSize(0, 0);
_okButton->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
_okButton->setFixedHeight(38);
_btnLayout->addWidget(_okButton);
_cancelButton = new ElaPushButton("取消", _buttonWidget);
connect(_cancelButton, &ElaPushButton::clicked, this, [=]
{
emit cancelButtonClicked();
reject();
});
_cancelButton->setMinimumSize(0, 0);
_cancelButton->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
_cancelButton->setFixedHeight(38);
_btnLayout->addWidget(_cancelButton);
_mainLayout->addWidget(_centralWidget);
_mainLayout->addWidget(_buttonWidget);
setFocus();
}
InterDialog::~InterDialog() = default;
void InterDialog::setMessage(const QString& message)
{
if (_messageLabel)
{
_messageLabel->setText(message);
}
}
void InterDialog::setCentralWidget(QWidget* centralWidget)
{
_mainLayout->removeWidget(_centralWidget);
delete _messageLabel;
_messageLabel = nullptr;
delete _centralWidget;
_centralWidget = centralWidget;
QSizePolicy sizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth());
centralWidget->setSizePolicy(sizePolicy);
_mainLayout->insertWidget(0, _centralWidget);
}
void InterDialog::setStandardButtons(const QDialogButtonBox::StandardButtons buttons) const
{
if (buttons.testFlag(QDialogButtonBox::NoButton))
{
_buttonWidget->setVisible(false);
}
else
{
_okButton->setVisible(buttons.testFlag(QDialogButtonBox::StandardButton::Ok));
_cancelButton->setVisible(buttons.testFlag(QDialogButtonBox::StandardButton::Cancel));
}
}
void InterDialog::addFirstButton(QPushButton* button) const
{
_btnLayout->insertWidget(1, button);
}
void InterDialog::addLastButton(QPushButton* button) const
{
_btnLayout->addWidget(button);
}
+68
View File
@@ -0,0 +1,68 @@
#ifndef METASEARCH_INTERDIALOG_H
#define METASEARCH_INTERDIALOG_H
#include "ElaDialog.h"
#include "ElaTheme.h"
#include "ElaText.h"
#include "ElaPushButton.h"
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
/**
* 带按钮栏的弹窗
*/
class InterDialog : public ElaDialog
{
Q_OBJECT
public:
explicit InterDialog(QWidget* parent = nullptr);
~InterDialog() override;
// 设置消息
virtual void setMessage(const QString& message);
// 设置中心区域
void setCentralWidget(QWidget* centralWidget);
// 设置按钮
void setStandardButtons(QDialogButtonBox::StandardButtons buttons) const;
// 头部添加按钮
void addFirstButton(QPushButton* button) const;
// 尾部添加按钮
void addLastButton(QPushButton* button) const;
signals:
void okButtonClicked();
void cancelButtonClicked();
protected:
// 主题
ElaThemeType::ThemeMode _themeMode;
// 主布局
QVBoxLayout* _mainLayout{nullptr};
// 中心区域
QWidget* _centralWidget{nullptr};
// 中心区域文本
ElaText* _messageLabel{nullptr};
// 按钮区域
QWidget* _buttonWidget{nullptr};
// 按钮区域布局
QHBoxLayout* _btnLayout{nullptr};
// 确认按钮
ElaPushButton* _okButton{nullptr};
// 取消按钮
ElaPushButton* _cancelButton{nullptr};
};
#endif //METASEARCH_INTERDIALOG_H
+380 -56
View File
@@ -2,6 +2,13 @@
#include "ElaIcon.h" #include "ElaIcon.h"
#include "ElaText.h" #include "ElaText.h"
#include "ElaTheme.h" #include "ElaTheme.h"
#include "ElaToolButton.h"
#include "utils/uf2configreader.h"
#include "utils/uft3configreader.h"
#include "utils/configmanager.h"
#include "utils/logmanager.h"
#include "utils/Constants.h"
#include "utils/versionchecker.h"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@@ -16,42 +23,96 @@
#include <QFileInfo> #include <QFileInfo>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QTimer>
#include <QApplication>
#include <QDesktopServices>
#include <QUrl>
namespace {
const QString UPDATE_JSON = Constants::Urls::updateInfo();
const QString DOWNLOAD_PAGE = Constants::Urls::downloadPage();
const QString HELP_PAGE = Constants::Urls::helpPage();
}
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: ElaWindow(parent) : ElaWindow(parent)
, m_pythonRunner(new PythonRunner(this)) , m_pythonRunner(new PythonRunner(this))
, m_iconsSet(false)
, m_navigationButton(nullptr)
, m_isNavigationExpanded(true)
, m_totalFunctions(0)
, m_currentFunctionIndex(0)
, m_lastProgress(0)
{ {
initWindow(); initWindow();
initContent(); initContent();
// 获取应用程序目录 LogManager::instance()->initLogConfig(); // 先读取 change_code.ini 的 [log] 配置,再开始写日志
QString appDir = QCoreApplication::applicationDirPath(); LogManager::instance()->initLogFile();
LogManager::instance()->logInfo("应用程序启动");
// 配置 Python 运行器 - 只使用打包好的 exe,路径是 uf2touft3/uf2touft3.exe loadConfigCache();
m_pythonRunner->setRunMode(PythonRunner::UsePackagedExe); m_pythonRunner->setRunMode(PythonRunner::UsePackagedExe);
// 设置打包好的可执行文件路径和工作目录 QString exePath = Constants::toolExe();
QString exePath = appDir + "/uf2touft3/uf2touft3.exe"; QString workDir = Constants::toolDir();
QString workDir = appDir + "/uf2touft3";
m_pythonRunner->setPackagedExePath(exePath); m_pythonRunner->setPackagedExePath(exePath);
m_pythonRunner->setWorkingDirectory(workDir); m_pythonRunner->setWorkingDirectory(workDir);
// 检查 exe 是否存在
QFileInfo exeInfo(exePath); QFileInfo exeInfo(exePath);
if (!exeInfo.exists() || !exeInfo.isFile()) { if (!exeInfo.exists() || !exeInfo.isFile()) {
m_batchLogEdit->append("⚠️ 警告:未找到 uf2touft3/uf2touft3.exe"); LogManager::instance()->logError(QString("未找到转换工具: %1").arg(exePath));
m_batchLogEdit->append(QString(" 期望位置:%1").arg(exePath));
} else {
m_batchLogEdit->append("✓ Python 工具准备就绪");
} }
connect(m_pythonRunner, &PythonRunner::finished, this, &MainWindow::onPythonRunnerFinished); connect(m_pythonRunner, &PythonRunner::finished, this, &MainWindow::onPythonRunnerFinished);
connect(m_pythonRunner, &PythonRunner::standardOutput, this, &MainWindow::onPythonRunnerOutput); connect(m_pythonRunner, &PythonRunner::standardOutput, this, &MainWindow::onPythonRunnerOutput);
connect(m_pythonRunner, &PythonRunner::standardError, this, &MainWindow::onPythonRunnerError); connect(m_pythonRunner, &PythonRunner::standardError, this, &MainWindow::onPythonRunnerError);
connect(m_pythonRunner, &PythonRunner::started, [this]() {
m_batchLogEdit->append("转换任务已启动..."); connect(this, &ElaWindow::navigationNodeClicked, this, &MainWindow::onNavigationNodeClicked);
});
QTimer::singleShot(100, this, &MainWindow::updateNavigationButtonIcon);
// 启动后延迟检查版本更新
QTimer::singleShot(3000, this, &MainWindow::checkForUpdates);
}
void MainWindow::loadConfigCache()
{
LogManager::instance()->log("正在加载配置缓存...");
// 1. 加载 config.ini 到内存缓存(ConfigManager::loadConfig 之前从未被调用过)
ConfigManager::instance()->loadConfig(QCoreApplication::applicationDirPath());
UF2ConfigReader uf2Reader;
bool uf2Loaded = uf2Reader.loadAllUF20Config();
UFT3ConfigReader uft3Reader;
bool uft3Loaded = uft3Reader.loadAllUFT3Config();
if (uf2Loaded && uft3Loaded) {
LogManager::instance()->log("✓ 配置缓存加载完成");
} else {
LogManager::instance()->logWarning("⚠️ 部分配置加载失败");
QStringList uf2Failed = uf2Reader.getFailedFiles();
QStringList uft3Failed = uft3Reader.getFailedFiles();
if (!uf2Failed.isEmpty()) {
LogManager::instance()->log(" UF20 加载失败:");
foreach (const QString& file, uf2Failed) {
LogManager::instance()->log(QString(" - %1").arg(file));
}
}
if (!uft3Failed.isEmpty()) {
LogManager::instance()->log(" UFT3 加载失败:");
foreach (const QString& file, uft3Failed) {
LogManager::instance()->log(QString(" - %1").arg(file));
}
}
}
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@@ -60,36 +121,51 @@ MainWindow::~MainWindow()
void MainWindow::initWindow() void MainWindow::initWindow()
{ {
setWindowTitle("UFT30 Change Code"); setWindowTitle(QString("UFT30 Change Code %1").arg(Version::getVersionString()));
setWindowIcon(QIcon(":/Resource/Image/Cirno.jpg")); setWindowIcon(QIcon(":/resources/images/ChangeCode.png"));
resize(900, 600); resize(900, 600);
setUserInfoCardTitle("UFT30"); setMinimumSize(800, 500);
setUserInfoCardSubTitle("Change Code Tool"); setUserInfoCardVisible(false);
// 隐藏返回和前进按钮
setWindowButtonFlag(ElaAppBarType::RouteBackButtonHint, false);
setWindowButtonFlag(ElaAppBarType::RouteForwardButtonHint, false);
} }
void MainWindow::initContent() void MainWindow::initContent()
{ {
QString helpKey; addExpanderNode("功能转码", m_funcConvertKey, ElaIconType::FileCode);
QString aboutKey; addPageNode("业务转码", createBatchConvertPage(), m_funcConvertKey, ElaIconType::ArrowsRepeat);
QString settingKey; addPageNode("代码检索", createCodeSearchPage(), m_funcConvertKey, ElaIconType::Cubes);
addPageNode("转码业务", createBatchConvertPage(), ElaIconType::FileCode);
addPageNode("功能搜索", createFunctionSearchPage(), ElaIconType::MagnifyingGlass); addExpanderNode("功能查询", m_funcSearchKey, ElaIconType::MagnifyingGlass);
addFooterNode("帮助", createHelpPage(), helpKey, 0, ElaIconType::CircleQuestion); addPageNode("UF20功能查询", createUF20FunctionSearchPage(), m_funcSearchKey, ElaIconType::MagnifyingGlassPlus);
addFooterNode("关于", createAboutPage(), aboutKey, 0, ElaIconType::User); addPageNode("UFT3功能查询", createUFT3FunctionSearchPage(), m_funcSearchKey, ElaIconType::MagnifyingGlassPlus);
addFooterNode("设置", createSettingsPage(), settingKey, 0, ElaIconType::GearComplex); expandNavigationNode(m_funcConvertKey);
addExpanderNode("数据映射", m_dataMappingKey, ElaIconType::TableList);
addPageNode("表替换", createTableMappingPage(), m_dataMappingKey, ElaIconType::TableColumns);
addPageNode("函数替换", createFunctionMappingPage(), m_dataMappingKey, ElaIconType::Function);
addPageNode("元数据更新", createMetadataUpdatePage(), ElaIconType::ArrowsRotate);
addFooterNode("帮助", m_helpKey, 0, ElaIconType::CircleQuestion);
addFooterNode("关于", m_aboutKey, 0, ElaIconType::Info);
addFooterNode("设置", createSettingsPage(), m_settingKey, 0, ElaIconType::GearComplex);
} }
QWidget* MainWindow::createBatchConvertPage() QWidget* MainWindow::createBatchConvertPage()
{ {
BatchConvertPage *page = new BatchConvertPage; m_batchConvertPage = new BatchConvertPage;
m_batchLogEdit = page->getLogEdit(); connect(m_batchConvertPage, &BatchConvertPage::startConvert, this, &MainWindow::onBatchConvertStart);
connect(page, &BatchConvertPage::startConvert, this, &MainWindow::onBatchConvertStart); connect(m_batchConvertPage, &BatchConvertPage::stopConvert, this, &MainWindow::onBatchConvertStop);
connect(m_pythonRunner, &PythonRunner::started, [this]() { return m_batchConvertPage;
if (m_batchLogEdit) { }
m_batchLogEdit->append("转换任务已启动...");
} QWidget* MainWindow::createCodeSearchPage()
}); {
return page; _codeSearchPage = new CodeSearchPage;
return _codeSearchPage;
} }
QWidget* MainWindow::createFunctionSearchPage() QWidget* MainWindow::createFunctionSearchPage()
@@ -97,14 +173,17 @@ QWidget* MainWindow::createFunctionSearchPage()
return new FunctionSearchPage; return new FunctionSearchPage;
} }
QWidget* MainWindow::createHelpPage() QWidget* MainWindow::createUF20FunctionSearchPage()
{ {
return new HelpPage; UF20FunctionSearchPage *page = new UF20FunctionSearchPage;
connect(page, &UF20FunctionSearchPage::addFunctionToConvert,
m_batchConvertPage, &BatchConvertPage::addFunction);
return page;
} }
QWidget* MainWindow::createAboutPage() QWidget* MainWindow::createUFT3FunctionSearchPage()
{ {
return new AboutPage; return new UFT3FunctionSearchPage;
} }
QWidget* MainWindow::createSettingsPage() QWidget* MainWindow::createSettingsPage()
@@ -112,58 +191,303 @@ QWidget* MainWindow::createSettingsPage()
return new SettingsPage(); return new SettingsPage();
} }
QWidget* MainWindow::createMetadataUpdatePage()
{
return new MetadataUpdatePage();
}
QWidget* MainWindow::createTableMappingPage()
{
return new TableMappingPage();
}
QWidget* MainWindow::createFunctionMappingPage()
{
return new FunctionMappingPage();
}
void MainWindow::onBatchConvertStart() void MainWindow::onBatchConvertStart()
{ {
if (m_pythonRunner->isRunning()) { if (m_pythonRunner->isRunning()) {
m_batchLogEdit->append("警告: 上一个任务还在运行中..."); LogManager::instance()->logWarning("警告: 上一个任务还在运行中...");
QMessageBox::warning(this, "提示", "上一个转换任务正在运行,请等待完成或停止当前任务。"); QMessageBox::warning(this, "提示", "上一个转换任务正在运行,请等待完成或停止当前任务。");
return; return;
} }
QString appDir = QCoreApplication::applicationDirPath(); QString exePath = Constants::toolExe();
QString exePath = appDir + "/uf2touft3/uf2touft3.exe";
if (!m_pythonRunner->canRun()) { if (!m_pythonRunner->canRun()) {
m_batchLogEdit->append("错误: 找不到 uf2touft3/uf2touft3.exe!"); LogManager::instance()->logError("错误: 找不到 uf2touft3/uf2touft3.exe!");
QMessageBox::critical(this, "错误", QMessageBox::critical(this, "错误",
QString("无法启动转换任务!\n\n请确保:\nuf2touft3/uf2touft3.exe 存在,且该目录下包含 config.ini\n\n期望路径:\n%1") QString("无法启动转换任务!\n\n请确保:\nuf2touft3/uf2touft3.exe 存在,且该目录下包含 config.ini\n\n期望路径:\n%1")
.arg(exePath)); .arg(exePath));
return; return;
} }
m_batchLogEdit->append("========================================"); if (m_batchConvertPage) {
m_batchLogEdit->append("开始执行 UFT2 到 UFT3 的代码转换..."); m_totalFunctions = m_batchConvertPage->getFunctionCount();
m_batchLogEdit->append(QString("工具路径: %1").arg(exePath)); m_functionList = m_batchConvertPage->getFunctionList();
m_batchLogEdit->append("========================================"); m_currentFunctionIndex = 0;
} else {
m_totalFunctions = 0;
m_currentFunctionIndex = 0;
m_functionList.clear();
}
// 进度基准:BatchConvertPage 在开始时已将进度条置为 20(配置加载完成)
m_lastProgress = 20;
LogManager::instance()->log("========================================");
LogManager::instance()->log("开始执行 UFT2 到 UFT3 的代码转换...");
LogManager::instance()->log(QString("工具路径: %1").arg(exePath));
LogManager::instance()->log(QString("待转换函数数量: %1").arg(m_totalFunctions));
LogManager::instance()->log("========================================");
if (!m_pythonRunner->start()) { if (!m_pythonRunner->start()) {
m_batchLogEdit->append("错误: 无法启动转换进程!"); LogManager::instance()->logError("错误: 无法启动转换进程!");
} else {
if (m_batchConvertPage) {
m_batchConvertPage->onConvertStarted();
}
}
}
void MainWindow::onBatchConvertStop()
{
if (m_pythonRunner->isRunning()) {
LogManager::instance()->log("正在停止转换进程...");
m_pythonRunner->stop();
} }
} }
void MainWindow::onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus) void MainWindow::onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus)
{ {
m_batchLogEdit->append("========================================"); if (m_batchConvertPage) {
m_batchConvertPage->onConvertFinished();
}
LogManager::instance()->log("========================================");
if (exitStatus == QProcess::NormalExit && exitCode == 0) { if (exitStatus == QProcess::NormalExit && exitCode == 0) {
m_batchLogEdit->append("✓ 代码转换完成!"); LogManager::instance()->log("✓ 代码转换完成!");
if (_codeSearchPage) {
_codeSearchPage->refreshIndexSilent();
}
QMessageBox::information(this, "完成", "代码转换任务已成功完成!"); QMessageBox::information(this, "完成", "代码转换任务已成功完成!");
} else { } else {
m_batchLogEdit->append(QString("✗ 转换失败! 退出码: %1").arg(exitCode)); LogManager::instance()->logError(QString("✗ 转换失败! 退出码: %1").arg(exitCode));
QMessageBox::warning(this, "失败", QString("代码转换任务失败,退出码: %1").arg(exitCode)); QMessageBox::warning(this, "失败", QString("代码转换任务失败,退出码: %1").arg(exitCode));
} }
m_batchLogEdit->append("========================================"); LogManager::instance()->log("========================================");
} }
void MainWindow::onPythonRunnerOutput(const QString &output) void MainWindow::onPythonRunnerOutput(const QString &output)
{ {
if (!output.trimmed().isEmpty()) { if (output.trimmed().isEmpty()) {
m_batchLogEdit->append(output); return;
} }
LogManager::instance()->log(output);
const QString text = output.trimmed();
// 计算本次输出对应的目标进度;-1 表示本次输出不含可识别的进度标记
int targetProgress = -1;
QString status;
// 1) 优先解析工具显式输出的 progress:xx / progress=xx
static const QRegularExpression progressRegExp(
R"(progress\s*[:=]\s*(\d+))", QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch pm = progressRegExp.match(text);
if (pm.hasMatch()) {
targetProgress = pm.captured(1).toInt();
status = QString("转换中 %1%").arg(targetProgress);
} else {
// 2) 解析并行翻译进度 [ n/m](翻译阶段映射到 55~78
// 注意时间戳 [2026-07-17 ...] 不含 '/' 不会误匹配
static const QRegularExpression stepRegExp(R"(\[\s*(\d+)\s*/\s*(\d+)\s*\])");
QRegularExpressionMatch stepMatch;
auto it = stepRegExp.globalMatch(text);
while (it.hasNext()) {
stepMatch = it.next(); // 取本段输出里最后一个 [n/m]
}
if (stepMatch.hasMatch()) {
int cur = stepMatch.captured(1).toInt();
int tot = stepMatch.captured(2).toInt();
if (tot > 0) {
targetProgress = 55 + (cur * 23) / tot;
status = QString("正在翻译文件 %1/%2").arg(cur).arg(tot);
}
}
// 3) 解析流水线阶段标记(关键字命中取最大值,保证进度稳定推进)
if (targetProgress < 0) {
struct Marker { const char* key; int prog; const char* label; };
static const Marker markers[] = {
{"开始加载数据", 25, "正在加载数据..."},
{"配置文件加载完成", 30, "配置文件加载完成"},
{"元数据加载完成", 35, "元数据加载完成"},
{"全部数据加载完成", 42, "数据加载完成"},
{"数据初始化完成", 45, "数据初始化完成"},
{"校验完成", 52, "数据校验完成"},
{"翻译完成", 80, "代码翻译完成"},
{"后处理完成", 90, "后处理完成"},
{"输出完成", 96, "结果输出完成"},
{"转换流水线执行成功", 98, "转换流水线执行成功"},
{"转码完成", 98, "转码完成"},
};
for (const auto &m : markers) {
if (text.contains(QString::fromUtf8(m.key))) {
if (m.prog > targetProgress) {
targetProgress = m.prog;
status = QString::fromUtf8(m.label);
}
}
}
}
}
if (!m_batchConvertPage) {
return;
}
// 进度单调递增:只有更大的进度才会推进进度条,避免"一下长一下短"
if (targetProgress > m_lastProgress) {
m_lastProgress = targetProgress;
}
// 状态文字:仅展示已识别的处理阶段;未识别的日志行不显示(保持上一次状态)
// 空 status 表示当前行无有效状态,BatchConvertPage 会保留上一次文字
m_batchConvertPage->onProgressUpdate(m_lastProgress, status);
} }
void MainWindow::onPythonRunnerError(const QString &error) void MainWindow::onPythonRunnerError(const QString &error)
{ {
if (!error.trimmed().isEmpty()) { if (!error.trimmed().isEmpty()) {
m_batchLogEdit->append(QString("[ERROR] %1").arg(error)); LogManager::instance()->logError(QString("[ERROR] %1").arg(error));
}
}
void MainWindow::updateNavigationButtonIcon()
{
// 使用资源文件中的图标
QIcon leftIcon(":/resources/images/zhedie_left.png");
if (leftIcon.isNull()) {
return;
}
// 查找所有 ElaToolButton
QList<ElaToolButton*> allButtons = this->findChildren<ElaToolButton*>(QString(), Qt::FindChildrenRecursively);
// 尝试查找导航按钮 - 可能在不同位置
for (int i = 0; i < allButtons.size(); i++) {
ElaToolButton* btn = allButtons[i];
QString objName = btn->objectName();
// 检查是否是导航按钮(可能的名称)
if (objName.contains("Nav", Qt::CaseInsensitive) ||
objName.contains("Menu", Qt::CaseInsensitive) ||
objName.contains("Navigation", Qt::CaseInsensitive)) {
m_navigationButton = btn;
break;
}
}
// 如果没有找到有名称的导航按钮,使用第一个可见按钮
if (!m_navigationButton) {
for (ElaToolButton* btn : allButtons) {
if (btn->isVisible()) {
m_navigationButton = btn;
break;
}
}
}
if (m_navigationButton) {
m_navigationButton->setProperty("ElaIconType", "");
m_navigationButton->setIcon(leftIcon);
m_navigationButton->setIconSize(QSize(24, 24));
m_navigationButton->update();
// 先断开可能存在的连接,然后重新连接,避免多次连接
disconnect(m_navigationButton, &ElaToolButton::clicked, this, &MainWindow::onNavigationButtonClicked);
connect(m_navigationButton, &ElaToolButton::clicked, this, &MainWindow::onNavigationButtonClicked);
}
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (!m_iconsSet && event->type() == QEvent::Show) {
// 窗口显示后尝试设置图标
updateNavigationButtonIcon();
m_iconsSet = true;
}
return ElaWindow::eventFilter(obj, event);
}
void MainWindow::onNavigationButtonClicked()
{
if (!m_navigationButton) {
return;
}
QIcon leftIcon(":/resources/images/zhedie_left.png");
QIcon rightIcon(":/resources/images/zhedie_right.png");
// 使用状态变量来判断当前状态,而不是依赖 cacheKey
if (m_isNavigationExpanded) {
// 当前是展开状态,切换到折叠状态
m_navigationButton->setIcon(rightIcon);
m_isNavigationExpanded = false;
} else {
// 当前是折叠状态,切换到展开状态
m_navigationButton->setIcon(leftIcon);
m_isNavigationExpanded = true;
}
m_navigationButton->update();
}
void MainWindow::checkForUpdates()
{
auto *checker = new VersionChecker(this);
connect(checker, &VersionChecker::newVersionAvailable, this, &MainWindow::onNewVersionFound);
connect(checker, &VersionChecker::checkError, this, [](const QString &msg) {
LogManager::instance()->logInfo("版本检查: " + msg);
});
connect(checker, &VersionChecker::noUpdateNeeded, this, []() {
LogManager::instance()->logInfo("版本检查: 已是最新版本");
});
checker->check(UPDATE_JSON, Version::getVersionString());
}
void MainWindow::onNewVersionFound(const QString &latestVersion, const QString &releaseNotes)
{
LogManager::instance()->logInfo(QString("发现新版本: %1").arg(latestVersion));
QString msg = QString("检测到新版本 %1\n当前版本: %2\n")
.arg(latestVersion, Version::getVersionString());
if (!releaseNotes.isEmpty()) {
msg += QString("\n──────── 更新说明 ────────\n%1\n").arg(releaseNotes);
}
msg += "\n是否前往下载最新版本?";
QMessageBox::StandardButton reply = QMessageBox::question(
this, "发现新版本", msg,
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
QDesktopServices::openUrl(QUrl(DOWNLOAD_PAGE));
}
}
void MainWindow::onNavigationNodeClicked(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey)
{
Q_UNUSED(nodeType);
if (nodeKey == m_helpKey) {
QDesktopServices::openUrl(QUrl(HELP_PAGE));
} else if (nodeKey == m_aboutKey) {
std::make_unique<AboutDialog>(this)->exec();
} }
} }
+42 -6
View File
@@ -2,14 +2,21 @@
#define MAINWINDOW_H #define MAINWINDOW_H
#include "ElaWindow.h" #include "ElaWindow.h"
#include "ElaToolButton.h"
#include <QWidget> #include <QWidget>
#include <QTextEdit>
#include "src/pythonrunner/PythonRunner.h" #include "src/pythonrunner/PythonRunner.h"
#include "src/pages/batchconvert/batchconvertpage.h" #include "src/pages/batchconvert/batchconvertpage.h"
#include "src/pages/codesearch/codesearchpage.h"
#include "src/pages/functionsearch/functionsearchpage.h" #include "src/pages/functionsearch/functionsearchpage.h"
#include "src/pages/help/helppage.h" #include "src/pages/functionsearch/uf20functionsearchpage.h"
#include "src/pages/about/aboutpage.h" #include "src/pages/functionsearch/uft3functionsearchpage.h"
#include "src/pages/about/aboutdialog.h"
#include "src/pages/settings/settingspage.h" #include "src/pages/settings/settingspage.h"
#include "src/pages/metadatupdate/metadatupdatepage.h"
#include "src/pages/datamapping/tablemappingpage.h"
#include "src/pages/datamapping/functionmappingpage.h"
#include "src/utils/version.h"
class MainWindow : public ElaWindow class MainWindow : public ElaWindow
{ {
@@ -24,18 +31,47 @@ private:
void initContent(); void initContent();
QWidget* createBatchConvertPage(); QWidget* createBatchConvertPage();
QWidget* createCodeSearchPage();
QWidget* createFunctionSearchPage(); QWidget* createFunctionSearchPage();
QWidget* createHelpPage(); QWidget* createUF20FunctionSearchPage();
QWidget* createAboutPage(); QWidget* createUFT3FunctionSearchPage();
QWidget* createSettingsPage(); QWidget* createSettingsPage();
QWidget* createMetadataUpdatePage();
QWidget* createTableMappingPage();
QWidget* createFunctionMappingPage();
void loadConfigCache();
void onBatchConvertStart(); void onBatchConvertStart();
void onBatchConvertStop();
void onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus); void onPythonRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onPythonRunnerOutput(const QString &output); void onPythonRunnerOutput(const QString &output);
void onPythonRunnerError(const QString &error); void onPythonRunnerError(const QString &error);
void checkForUpdates();
void onNewVersionFound(const QString &latestVersion, const QString &releaseNotes);
void updateNavigationButtonIcon();
bool eventFilter(QObject *obj, QEvent *event) override;
void onNavigationButtonClicked();
void onNavigationNodeClicked(ElaNavigationType::NavigationNodeType nodeType, QString nodeKey);
PythonRunner *m_pythonRunner; PythonRunner *m_pythonRunner;
QTextEdit *m_batchLogEdit; bool m_iconsSet;
ElaToolButton* m_navigationButton;
bool m_isNavigationExpanded;
BatchConvertPage *m_batchConvertPage;
CodeSearchPage *_codeSearchPage;
int m_totalFunctions;
int m_currentFunctionIndex;
int m_lastProgress; // 已上报的最大进度,保证进度单调递增,避免进度条来回跳动
QStringList m_functionList;
QString m_helpKey;
QString m_aboutKey;
QString m_settingKey;
QString m_funcSearchKey;
QString m_funcConvertKey;
QString m_dataMappingKey;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H
+15
View File
@@ -0,0 +1,15 @@
#include "filedb.h"
FileDB* FileDB::m_instance = nullptr;
FileDB::FileDB(QObject *parent) : QObject(parent)
{
}
FileDB* FileDB::instance()
{
if (!m_instance) {
m_instance = new FileDB();
}
return m_instance;
}

Some files were not shown because too many files have changed in this diff Show More