基于 wxWidgets 框架的桌面应用程序-WebView 浏览器控件与Go后端数据交互
项目概述
基于 wxWidgets 框架的桌面应用程序,集成了 WebView 浏览器控件,提供了网页浏览、HTTP 通信、JSON 处理、文件操作和书签管理等功能。项目采用C++作为客户端,Go作为服务端的架构设计。
项目结构
bash
C++ WebView 项目
├── C++ 客户端
│ ├── 主程序入口
│ │ ├── main.h/cpp - 应用程序类定义
│ │ └── MyFrame.h/cpp - 主窗口类及功能实现
│ ├── 日志系统
│ │ ├── LogManager.h/cpp - 日志管理器实现
│ │ └── app.log - 日志文件
│ ├── 资源文件
│ │ ├── resources/ - 静态资源文件(JS/CSS库)
│ │ └── static/ - 静态资源文件(JS/CSS库)
│ ├── 构建系统
│ │ ├── CMakeLists.txt - CMake构建配置
│ │ └── build.bat - Windows构建脚本
│ └── 其他
│ ├── download/ - 下载文件存储目录
│ └── lib/ - 第三方库
├── Go 后端服务
│ └── goServer/
│ ├── main.go - 主服务器程序
│ ├── resources/ - 静态资源文件(JS/CSS库)
│ └── files/ - 上传/下载文件存储目录
└── 文档及其他
└── *.md - Markdown文档文件功能说明书
核心功能模块
1. 浏览器功能模块
- 网页浏览:内嵌基于 Edge 引擎的 WebView 浏览器
- 导航控制:前进、后退、主页、本地服务器页面加载
- URL 输入:通过文本框输入并加载指定 URL
- 新窗口处理:拦截新窗口请求并在当前窗口打开
2. HTTP 通信模块
- POST 请求:向服务器发送 JSON 数据
- GET 请求:从服务器获取 JSON 数据
- 文件上传:通过 multipart/form-data 上传文件到服务器
- 文件下载:从服务器下载文件到本地
3. JSON 处理模块
- JSON 解析:解析服务器返回的 JSON 数据
- JSON 格式化显示:将 JSON 数据格式化为带颜色的 HTML 显示
- 格式化切换:支持紧凑和带缩进两种显示模式
4. 文件管理模块
- 文件选择:本地文件选择对话框
- 文件列表获取:从服务器获取可下载文件列表
- 文件预览:支持多种文件格式的在线预览(HTML、Markdown、XLSX 等)
- XLSX 编辑:在线编辑 Excel 文件
5. 书签管理模块
- 添加书签:保存当前页面 URL 和标题
- 查看书签:以 HTML 页面形式显示所有书签
- 删除书签:通过对话框选择并删除书签
6. 日志系统模块
- 日志记录:记录程序运行日志到文件和窗口
- 日志显示:提供不可关闭的日志窗口
Go 后端服务功能详解
Go 后端服务运行在本地端口 8080,为 C++ 客户端提供 RESTful API 和文件服务支持。
1. HTTP API 接口
POST /post
- 功能:接收客户端发送的 JSON 数据
- 输入:JSON 格式的用户数据
- 输出:包含状态信息和数据列表的 JSON 响应
GET /get
- 功能:向客户端返回示例 JSON 数据
- 输出:包含状态信息和数据列表的 JSON 响应
GET /files
- 功能:返回服务器上可下载的文件列表
- 输出:包含文件名、大小等信息的 JSON 响应
POST /upload
- 功能:接收客户端上传的文件
- 输入:multipart/form-data 格式的文件数据
- 输出:包含上传状态和文件信息的 JSON 响应
GET /download
- 功能:向客户端提供文件下载服务
- 参数:filename - 要下载的文件名
- 输出:指定文件的二进制数据
POST /save-xlsx
- 功能:保存客户端编辑的 XLSX 文件
- 输入:包含文件名、工作表名和更改列表的 JSON 数据
- 输出:保存状态的 JSON 响应
2. 文件服务功能
静态文件服务
- 路径:/static/
- 功能:提供 JS、CSS 等静态资源文件
根路径文件列表
- 路径:/
- 功能:显示服务器上所有可访问文件的列表页面
文件预览服务
- 路径:/preview
- 功能:根据文件类型在线预览文件内容
- 参数:
- filename - 要预览的文件名
- action - 操作类型(edit 表示编辑模式)
3. 特殊文件处理功能
Markdown 文件预览
- 支持 Markdown 文件的在线预览
- 使用 marked.js 和 highlight.js 进行渲染和语法高亮
XLSX 文件处理
- 预览模式:以表格形式展示 Excel 文件内容
- 编辑模式:提供在线编辑 Excel 文件的功能
- 支持多工作表切换
- 支持添加行和列
C++ 客户端与 Go 后端交互关系
1. HTTP 通信交互
graph TD
C++Client[C++ 客户端] -->|POST /post| GoServer[Go 后端服务]
C++Client -->|GET /get| GoServer
C++Client -->|GET /files| GoServer
C++Client -->|POST /upload| GoServer
C++Client -->|GET /download| GoServer
C++Client -->|POST /save-xlsx| GoServer
GoServer -->|JSON 响应| C++Client
2. 文件操作交互
graph TD
C++Client[C++ 客户端] -->|上传文件| GoServer[Go 后端服务]
GoServer -->|保存到files目录| FileStorage[文件存储]
C++Client -->|请求文件列表| GoServer
GoServer -->|扫描files目录| FileStorage
GoServer -->|返回JSON文件列表| C++Client
C++Client -->|下载文件| GoServer
GoServer -->|读取files目录文件| FileStorage
GoServer -->|返回文件数据| C++Client
3. 文件预览交互
graph TD
C++Client[C++ 客户端] -->|请求预览文件| GoServer[Go 后端服务]
GoServer -->|读取文件| FileStorage[文件存储]
GoServer -->|根据文件类型处理| FileTypeHandler{文件类型判断}
FileTypeHandler -->|Markdown| MarkdownRenderer[Markdown渲染]
FileTypeHandler -->|XLSX| XLSXHandler[XLSX处理]
FileTypeHandler -->|其他文本文件| TextRenderer[文本渲染]
GoServer -->|返回HTML页面| C++Client
核心类及方法调用关系
MyFrame 类(主窗口类)
这是应用程序的核心类,包含了所有 UI 元素和功能实现:
graph TD
MyFrame --> HttpClient
MyFrame --> JsonHandler
MyFrame --> BookmarkManager
MyFrame --> LogManager
主要方法及调用关系:
-
构造函数 MyFrame::MyFrame()
- 创建所有 UI 控件
- 设置布局管理器
- 绑定事件处理函数
-
浏览器导航相关方法
- OnSearch() - 处理 URL 搜索
- OnPrevPage() - 处理后退操作
- OnNextPage() - 处理前进操作
- OnHomePage() - 加载主页
- OnLocalServer() - 加载本地服务器
- OnNewWindow() - 处理新窗口事件
- OnWebViewLoaded() - 页面加载完成事件
-
HTTP 操作相关方法
- OnPost() - 发送 POST 请求并处理响应
- OnGet() - 发送 GET 请求并处理响应
- OnFormatJson() - 格式化显示 JSON 数据
-
文件操作相关方法
- OnSelectFile() - 选择本地文件
- OnUploadFile() - 上传文件到服务器
- OnGetFileLists() - 获取服务器文件列表
- OnDownloadFile() - 从服务器下载文件
- OnEditXlsx() - 在线编辑 XLSX 文件
- OnComboBoxSelect() - 处理下拉框选择事件
- ConvertMarkdownToHTML() - 将 Markdown 转换为 HTML
-
书签管理相关方法
- OnAddBookmark() - 添加当前页面到书签
- OnShowBookmarks() - 显示所有书签
- OnDeleteBookmark() - 删除书签
HttpClient 类(HTTP 客户端)
负责所有网络请求操作:
graph TD
HttpClient --> libcurl[curl库]
主要方法:
HttpPost()
- 发送 POST 请求HttpGet()
- 发送 GET 请求HttpUploadFile()
- 上传文件HttpDownloadFile()
- 下载文件URLEncode()
- URL 编码BuildUrl()
- 构建完整 URL
JsonHandler 类(JSON 处理器)
负责 JSON 数据的解析和格式化:
graph TD
JsonHandler --> nlohmann_json[nlohmann::json库]
主要方法:
- ParseJsonResponse() - 解析 JSON 响应
- FormatJsonForHtml() - 格式化 JSON 为 HTML
- FormatJsonValue() - 格式化单个 JSON 值
- GenerateJsonHtml() - 生成完整的 JSON HTML 页面
- SetRawJsonData() - 保存原始 JSON 数据
- GetRawJsonData() - 获取原始 JSON 数据
BookmarkManager 类(书签管理器)
负责书签的增删改查操作:
graph TD
BookmarkManager --> wxFileConfig[wxFileConfig]
主要方法:
- AddBookmark() - 添加书签
- LoadBookmarks() - 加载所有书签
- GenerateBookmarksHtml() - 生成书签 HTML 页面
- ShowDeleteBookmarksDialog() - 显示删除书签对话框
LogManager 类(日志管理器)
负责日志的记录和显示:
graph TD
LogManager --> wxLog[wxLog系统]
主要方法:
技术关键点
1. WebView 集成
- 使用 wxWebView 控件集成 Edge 浏览器引擎
- 处理页面加载事件和错误事件
- 支持 JavaScript 与本地代码交互
2. HTTP 通信
- 基于 libcurl 库实现 HTTP 请求
- 支持 GET/POST 请求和文件上传下载
- 处理各种 HTTP 状态码和错误情况
3. JSON 处理
- 使用 nlohmann::json 库进行 JSON 解析
- 实现 JSON 数据的语法高亮显示
- 支持紧凑和格式化两种显示模式
4. 文件操作
- 支持多种文件格式的上传下载
- 实现 Markdown 文件的在线预览
- 集成 XLSX 文件在线编辑功能
5. Go 后端服务
- 使用 Go 语言实现高性能 HTTP 服务
- 集成 excelize 库处理 Excel 文件
- 支持多种文件格式的在线预览和编辑
- 提供 RESTful API 接口
6. UI 设计
- 使用 wxWidgets 框架构建跨平台界面
- 采用多种布局管理器实现复杂界面布局
- 实现丰富的用户交互功能
类间关系总结
graph TD
MyApp --> MyFrame
MyFrame --> HttpClient
MyFrame --> JsonHandler
MyFrame --> BookmarkManager
MyFrame --> LogManager
HttpClient --> curl[libcurl库]
JsonHandler --> json[nlohmann::json库]
BookmarkManager --> wx[wxWidgets库]
LogManager --> wx
C++Client[C++ 客户端] <-->|HTTP通信| GoServer[Go 后端服务]
GoServer --> Excelize[excelize库]
整个项目采用模块化设计,C++ 客户端负责用户界面和本地功能,Go 后端服务负责文件处理和数据服务。各功能模块相对独立,通过清晰的接口进行交互。MyFrame 作为核心控制器协调各个模块的工作,HttpClient 负责网络通信,JsonHandler 处理数据格式化,BookmarkManager 管理书签数据,LogManager 负责日志记录。这种设计使得代码结构清晰,易于维护和扩展。
功能流程图
- 浏览器导航功能流程图
graph TD
A[用户操作] --> B{操作类型}
B -->|输入URL并搜索| C[OnSearch函数]
B -->|点击后退| D[OnPrevPage函数]
B -->|点击前进| E[OnNextPage函数]
B -->|点击主页| F[OnHomePage函数]
B -->|点击本地服务器| G[OnLocalServer函数]
C --> H[获取输入的URL]
H --> I{URL是否为空?}
I -->|是| J[显示错误提示]
I -->|否| K{URL是否包含协议?}
K -->|否| L[添加http://前缀]
K -->|是| M[保持原URL]
L --> N[WebView加载URL]
M --> N[WebView加载URL]
D --> O{WebView是否可以后退?}
O -->|是| P[执行后退操作]
O -->|否| Q[无操作]
P --> R[更新URL显示框]
E --> S{WebView是否可以前进?}
S -->|是| T[执行前进操作]
S -->|否| U[无操作]
T --> R[更新URL显示框]
F --> V[加载掘金网站]
V --> R[更新URL显示框]
G --> W[加载本地服务器]
W --> R[更新URL显示框]
- HTTP请求功能流程图
graph TD
A[用户操作] --> B{操作类型}
B -->|点击POST JSON| C[OnPost函数]
B -->|点击GET JSON| D[OnGet函数]
B -->|点击格式化JSON| E[OnFormatJson函数]
C --> F[准备JSON数据]
F --> G[调用HttpClient.HttpPost]
G --> H{请求是否成功?}
H -->|否| I[显示错误信息]
H -->|是| J[获取响应数据]
J --> K[在文本框显示响应]
J --> L[保存原始JSON数据]
J --> M[调用JsonHandler.GenerateJsonHtml]
M --> N[在WebView中显示格式化JSON]
D --> O[调用HttpClient.HttpGet]
O --> H
E --> P[获取保存的原始JSON数据]
P --> Q[调用JsonHandler.GenerateJsonHtml
启用格式化模式] Q --> N[在WebView中显示格式化JSON]
启用格式化模式] Q --> N[在WebView中显示格式化JSON]
- 文件操作功能流程图
graph TD
A[用户操作] --> B{操作类型}
B -->|点击选择文件| C[OnSelectFile函数]
B -->|点击上传文件| D[OnUploadFile函数]
B -->|点击获取文件列表| E[OnGetFileLists函数]
B -->|点击下载文件| F[OnDownloadFile函数]
B -->|点击编辑XLSX| G[OnEditXlsx函数]
C --> H[打开文件选择对话框]
H --> I{用户是否选择文件?}
I -->|是| J[获取文件路径]
J --> K[在文本框显示文件路径]
I -->|否| L[无操作]
D --> M[获取文件路径]
M --> N{路径是否为空?}
N -->|是| O[显示错误提示]
N -->|否| P{文件是否存在?}
P -->|否| Q[显示错误提示]
P -->|是| R[调用HttpClient.HttpUploadFile]
R --> S{上传是否成功?}
S -->|是| T[刷新文件列表]
S -->|否| U[显示上传失败信息]
E --> V[调用HttpClient.HttpGet
获取文件列表] V --> W{请求是否成功?} W -->|否| X[显示错误信息] W -->|是| Y[解析JSON响应] Y --> Z[清空下拉框] Y --> AA[提取文件列表] AA --> AB[将文件名添加到下拉框] F --> AC[获取选中的文件名] AC --> AD{文件名是否为空?} AD -->|是| AE[显示错误提示] AD -->|否| AF[构建下载URL] AF --> AG[调用HttpClient.HttpDownloadFile] AG --> AH{下载是否成功?} AH -->|否| AI[显示下载失败信息] AH -->|是| AJ{文件类型判断} AJ -->|Markdown文件| AK[转换为HTML并显示] AJ -->|其他文件| AL[在WebView中加载文件] G --> AM[获取选中的文件名] AM --> AN{文件名是否为空?} AN -->|是| AO[显示提示信息] AN -->|否| AP{是否为XLSX文件?} AP -->|否| AQ[显示提示信息] AP -->|是| AR[构建编辑器URL] AR --> AS[WebView加载编辑器]
获取文件列表] V --> W{请求是否成功?} W -->|否| X[显示错误信息] W -->|是| Y[解析JSON响应] Y --> Z[清空下拉框] Y --> AA[提取文件列表] AA --> AB[将文件名添加到下拉框] F --> AC[获取选中的文件名] AC --> AD{文件名是否为空?} AD -->|是| AE[显示错误提示] AD -->|否| AF[构建下载URL] AF --> AG[调用HttpClient.HttpDownloadFile] AG --> AH{下载是否成功?} AH -->|否| AI[显示下载失败信息] AH -->|是| AJ{文件类型判断} AJ -->|Markdown文件| AK[转换为HTML并显示] AJ -->|其他文件| AL[在WebView中加载文件] G --> AM[获取选中的文件名] AM --> AN{文件名是否为空?} AN -->|是| AO[显示提示信息] AN -->|否| AP{是否为XLSX文件?} AP -->|否| AQ[显示提示信息] AP -->|是| AR[构建编辑器URL] AR --> AS[WebView加载编辑器]
- 书签管理功能流程图
graph TD
A[用户操作] --> B{操作类型}
B -->|点击添加收藏| C[OnAddBookmark函数]
B -->|点击查看收藏| D[OnShowBookmarks函数]
B -->|点击删除收藏| E[OnDeleteBookmark函数]
C --> F[获取当前页面URL]
F --> G[获取当前页面标题]
F --> H{URL是否为空?}
H -->|是| I[显示收藏失败提示]
H -->|否| J[调用BookmarkManager.AddBookmark]
J --> K[显示收藏成功提示]
D --> L[调用BookmarkManager.LoadBookmarks]
L --> M[调用BookmarkManager.GenerateBookmarksHtml]
M --> N[在WebView中显示书签页面]
E --> O[调用BookmarkManager.ShowDeleteBookmarksDialog]
O --> P[显示删除书签对话框]
P --> Q[用户选择要删除的书签]
Q --> R[执行删除操作]
R --> S[刷新书签显示]
- 核心类交互流程图
graph LR
A[MyFrame主窗口] --> B[HttpClient网络请求]
A --> C[JsonHandler JSON处理]
A --> D[BookmarkManager书签管理]
A --> E[LogManager日志管理]
B --> B1[HttpPost - 发送POST请求]
B --> B2[HttpGet - 发送GET请求]
B --> B3[HttpUploadFile - 上传文件]
B --> B4[HttpDownloadFile - 下载文件]
B --> B5[URLEncode - URL编码]
B --> B6[BuildUrl - 构建完整URL]
C --> C1[SetRawJsonData - 保存原始数据]
C --> C2[GetRawJsonData - 获取原始数据]
C --> C3[GenerateJsonHtml - 生成HTML]
C --> C4[FormatJsonForHtml - 格式化JSON]
D --> D1[AddBookmark - 添加书签]
D --> D2[LoadBookmarks - 加载书签]
D --> D3[GenerateBookmarksHtml - 生成书签页面]
D --> D4[ShowDeleteBookmarksDialog - 显示删除对话框]
E --> E1[Init - 初始化日志]
E --> E2[Log - 记录日志信息]
CMakeLists.txt 构建配置说明
cmake
cmake_minimum_required(VERSION 3.10)
project(app)
# 17是指C++17
set(CMAKE_CXX_STANDARD 17)
# 使用MSVC编译器
# set(CMAKE_CXX_COMPILER "cl.exe")
# 使用C盘wxWidgets目录
include_directories("C:/wxWidgets/include")
link_directories("C:/wxWidgets/lib/vc_x64_dll")
# # 设置cURL路径(使用系统环境变量CRUL)
set(CURL_ROOT "C:/MinGW/curl-8.15.0_4-win64-mingw")
include_directories(${CURL_ROOT}/include)
link_directories(${CURL_ROOT}/lib)
#自动添加所有源文件
file(GLOB_RECURSE SRC_FILES "*.cpp")
add_executable(app ${SRC_FILES})
# 仅对 MyApp 这个目标添加编译宏,非常重要,app能显示中文,保证中文不乱码
target_compile_definitions(app PRIVATE
_DEBUG
_WINDOWS
UNICODE
_UNICODE
WXUSINGDLL
__WXMSW__
)
# 设置为 Windows 应用程序
set_target_properties(app PROPERTIES WIN32_EXECUTABLE ON)
# set(wxWidgets_USE_UNICODE ON)
# set(wxUSE_UNICODE_UTF8 1) # 强制使用 UTF-8 模式
# find_package(wxWidgets REQUIRED COMPONENTS core base webview)
# 链接库文件 --Debug版本
target_link_libraries(app
libcurl.lib
wxmsw33ud_core.lib
wxbase33ud.lib
wxmsw33ud_webview.lib
)
# # 链接库文件 --Release版本
# target_link_libraries(app
# wxmsw33u_core.lib
# wxbase33u.lib
# wxmsw33u_webview.lib
# )
build.bat 构建脚本说明
bat
@echo off
chcp 65001
REM 获取当前批处理文件所在目录,并设为工作目录
pushd "%~dp0"
setlocal
:: 设置程序名称
set APP_NAME=app.exe
:: 检查程序是否正在运行
tasklist /FI "IMAGENAME eq %APP_NAME%" 2>NUL | find /I "%APP_NAME%" >NUL
if "%ERRORLEVEL%"=="0" (
echo 正在结束 %APP_NAME%...
taskkill /F /IM "%APP_NAME%" >NUL
ping -n 2 127.0.0.1 >NUL :: 等待1秒确保进程完全结束
)
:: 获取当前时间(开始时间)
for /f "tokens=1-4 delims=:." %%a in ("%time%") do (
set /a "start_hour=%%a"
set /a "start_min=%%b"
set /a "start_sec=%%c"
set /a "start_hundredth=%%d"
)
:: 将小时转换为 24 小时制(如果需要)
if %start_hour% geq 12 (
set /a "start_hour-=12"
)
echo 正在清理构建文件...
if exist "build" (
rmdir /s /q "build"
)
mkdir "build"
cd "build"
if %errorlevel% neq 0 (
echo 清理失败!
pause
exit /b %errorlevel%
)
echo 清理成功!
echo 生成 VS2022 项目...
:: 记录编译开始时间
for /f "delims=" %%a in ('powershell -command "Get-Date -Format 'yyyy-MM-dd HH:mm:ss'"') do set "start_time=%%a"
:: 输出start_time
echo 编译开始时间: %start_time%
cmake .. -G "Visual Studio 17 2022" -A x64
if %errorlevel% neq 0 (
echo 编译失败!
pause
exit /b %errorlevel%
)
if %errorlevel% neq 0 (
echo 构建失败!
pause
exit /b %errorlevel%
)
@REM echo 编译 Release
@REM cmake --build . --config Release --parallel 4
echo 编译 Debug
@REM --parallel 4 参数,使编译过程能够利用4个CPU核心并行处理,从而显著提高编译速度
cmake --build . --config Debug --parallel 4
if %errorlevel% equ 0 (
echo Build succeeded!
) else (
echo Build failed with error code %errorlevel%!
)
echo 编译app.exe完成
:: 记录编译结束时间
for /f "delims=" %%a in ('powershell -command "Get-Date -Format 'yyyy-MM-dd HH:mm:ss'"') do set "end_time=%%a"
echo 编译结束时间: %end_time%
:: 使用 PowerShell 计算时间差
for /f "tokens=*" %%a in ('powershell -command "(Get-Date '%end_time%') - (Get-Date '%start_time%') | Select-Object -ExpandProperty TotalSeconds"') do set "duration_seconds=%%a"
:: 将总时长转换为小时、分钟、秒
set /a "duration_hours=%duration_seconds%/3600"
set /a "duration_minutes=(%duration_seconds% %% 3600)/60"
set /a "duration_seconds=%duration_seconds% %% 60"
:: 格式化输出
echo -------------------------编译总时长: %duration_hours% 小时 %duration_minutes% 分钟 %duration_seconds% 秒
cd ..
echo 复制APP.exe到项目目录
@REM copy "build\Release\APP.exe" "APP.exe"
@REM copy "build\Debug\APP.exe" "APP.exe"
echo 复制lib目录中的debugg下的所有dll到build\Debug中
xcopy "lib\Debug" "build\Debug" /s /e
@REM xcopy "lib\Release" "build\Release" /s /e
echo 在build目录下Debug目录下创建download目录
if not exist "build\Debug\download" (
mkdir "build\Debug\download"
)
echo 在build目录下Debug目录下创建static目录
if not exist "build\Debug\static" (
mkdir "build\Debug\static"
)
echo 复制static目录中下的所有文件到build\Debug\static中
xcopy "static" "build\Debug\static" /s /e
@REM echo 复制static目录中下的所有文件到build\Release\static中
@REM xcopy "static" "build\Release\static" /s /e
echo 启动APP.exe
start "" "build\Debug\app.exe"
::pause
运行说明
- 运行build.bat脚本,编译项目。
- 运行app.exe,启动项目。
- 访问http://localhost:8080/,打开项目。
- 访问http://localhost:8080/static/,查看静态资源。
- 访问http://localhost:8080/files,查看服务器文件列表。
- 访问http://localhost:8080/preview?filename=example.md,预览Markdown文件。
- 访问http://localhost:8080/preview?filename=example.xlsx&action=edit,编辑XLSX文件。
- 使用C++客户端的各种功能按钮,进行网页浏览、HTTP请求、文件操作和书签管理等操作。
依赖环境
- wxWidgets 3.3.0 或更高版本
- CMake 3.10 或更高版本
- Visual Studio 2022 或更高版本(支持C++17)
- libcurl 7.68.0 或更高版本
- nlohmann::json 3.9.1 或更高版本
- Go 1.15 或更高版本(用于后端服务)
- excelize v2.4.0 或更高版本(用于XLSX文件处理)
- Windows 10 或更高版本(推荐使用最新版本以获得最佳兼容性)
许可证
本项目基于MIT许可证开源,您可以在遵守许可证条款的前提下自由使用、修改和分发本项目的代码。
贡献
如果您发现任何问题或有改进建议,请随时提交问题或拉取请求。我们欢迎所有形式的贡献!