建立简单的数据库备份和恢复机制

This commit is contained in:
tian 2026-02-07 01:56:24 +08:00
parent d9da8e95ce
commit a655e8092c
7 changed files with 853 additions and 0 deletions

View File

@ -64,6 +64,9 @@
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web.Extensions" />
<Reference Include="Newtonsoft.Json">
<HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@ -131,6 +134,7 @@
<!-- Core - Configuration Management -->
<Compile Include="src\Core\Config\SystemConfig.cs" />
<Compile Include="src\Core\Config\ConfigManager.cs" />
<Compile Include="src\Core\Database\BackupManager.cs" />
<!-- Commands - Command Pattern Framework (for testing) -->
<Compile Include="src\Commands\IPathPlanningCommand.cs" />
<Compile Include="src\Commands\CommandBase.cs" />

View File

@ -2,6 +2,12 @@
## 功能点
### [2026/2/6]
1. [x] (功能)在碰撞报告中支持多张截图
2. [x] (功能)增加提取元素的包围盒信息,并一键拷贝到坐标编辑窗口
3. [x] (功能)在状态栏增加路径可视化快捷按钮
### [2026/2/3]
1. [x] (优化)预计算高亮正确,结果高亮错误,高亮了很多不相干的同名物体

View File

@ -6,4 +6,5 @@
<package id="MSTest.TestAdapter" version="3.0.4" targetFramework="net48" />
<package id="System.Data.SQLite.Core" version="1.0.118.0" targetFramework="net48" />
<package id="geometry4Sharp" version="1.0.0" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
</packages>

View File

@ -0,0 +1,276 @@
using System;
using System.Data.SQLite;
using System.IO;
namespace NavisworksTransport.Core.Database
{
/// <summary>
/// 简单数据库备份管理器
/// 直接备份整个 SQLite 数据库文件100% 保留所有数据
/// </summary>
public class BackupManager
{
private readonly PathDatabase _database;
private readonly string _dbPath;
public BackupManager(PathDatabase database)
{
_database = database ?? throw new ArgumentNullException(nameof(database));
_dbPath = GetDatabaseFilePath(database);
}
/// <summary>
/// 获取数据库文件路径
/// </summary>
private string GetDatabaseFilePath(PathDatabase database)
{
// 通过反射获取私有字段 _dbPath
var field = typeof(PathDatabase).GetField("_dbPath", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return field?.GetValue(database) as string ?? "";
}
/// <summary>
/// 获取数据库版本号
/// </summary>
public int GetDatabaseVersion()
{
try
{
using (var cmd = new SQLiteCommand("PRAGMA user_version", _database.Connection))
{
return Convert.ToInt32(cmd.ExecuteScalar());
}
}
catch
{
return 0;
}
}
/// <summary>
/// 格式化版本号显示
/// </summary>
public static string FormatVersion(int version)
{
if (version == 0) return "未知版本";
int major = version / 10000;
int minor = (version % 10000) / 100;
int patch = version % 100;
return $"v{major}.{minor}.{patch}";
}
/// <summary>
/// 检查两个版本是否兼容(主版本必须相同)
/// </summary>
public static bool IsVersionCompatible(int sourceVersion, int targetVersion)
{
if (sourceVersion == 0 || targetVersion == 0) return false;
int sourceMajor = sourceVersion / 10000;
int targetMajor = targetVersion / 10000;
return sourceMajor == targetMajor;
}
/// <summary>
/// 备份数据库(直接复制文件)
/// </summary>
public string BackupDatabase(string exportDirectory = null)
{
try
{
if (string.IsNullOrEmpty(_dbPath) || !File.Exists(_dbPath))
{
throw new InvalidOperationException("数据库文件不存在");
}
// 确保目录存在
if (string.IsNullOrEmpty(exportDirectory))
{
exportDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"NavisworksTransport", "Backups");
}
if (!Directory.Exists(exportDirectory))
Directory.CreateDirectory(exportDirectory);
// 获取当前版本号
int version = GetDatabaseVersion();
string versionStr = FormatVersion(version).Replace(".", "_");
// 生成备份文件名(包含版本号)
string fileName = $"NavisworksTransport_{versionStr}_{DateTime.Now:yyyyMMdd_HHmmss}.db";
string backupPath = Path.Combine(exportDirectory, fileName);
// 关闭数据库连接以释放文件锁
_database.Connection.Close();
try
{
// 复制数据库文件
File.Copy(_dbPath, backupPath, true);
}
finally
{
// 重新打开连接
_database.Connection.Open();
}
LogManager.Info($"数据库备份完成: {backupPath}");
return backupPath;
}
catch (Exception ex)
{
LogManager.Error($"数据库备份失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 获取指定数据库文件的版本号
/// </summary>
private int GetBackupFileVersion(string backupFilePath)
{
try
{
using (var connection = new SQLiteConnection($"Data Source={backupFilePath};Version=3;"))
{
connection.Open();
using (var cmd = new SQLiteCommand("PRAGMA user_version", connection))
{
return Convert.ToInt32(cmd.ExecuteScalar());
}
}
}
catch
{
return 0;
}
}
/// <summary>
/// 恢复数据库(直接替换文件)
/// </summary>
public void RestoreDatabase(string backupFilePath)
{
try
{
if (!File.Exists(backupFilePath))
{
throw new FileNotFoundException("备份文件不存在", backupFilePath);
}
if (string.IsNullOrEmpty(_dbPath))
{
throw new InvalidOperationException("数据库路径未知");
}
// 获取版本信息
int currentVersion = GetDatabaseVersion();
int backupVersion = GetBackupFileVersion(backupFilePath);
LogManager.Info($"数据库版本检查: 当前={FormatVersion(currentVersion)}, 备份={FormatVersion(backupVersion)}");
// 版本兼容性检查
if (!IsVersionCompatible(backupVersion, currentVersion))
{
throw new InvalidOperationException(
$"版本不兼容!\n\n" +
$"备份版本: {FormatVersion(backupVersion)}\n" +
$"当前版本: {FormatVersion(currentVersion)}\n\n" +
$"主版本号必须相同才能恢复。\n" +
$"请使用相同版本的插件进行恢复,或联系管理员进行数据库升级。");
}
// 关闭数据库连接
_database.Connection.Close();
try
{
// 备份当前数据库(以防万一)
if (File.Exists(_dbPath))
{
string safetyBackup = _dbPath + $".backup_{DateTime.Now:yyyyMMdd_HHmmss}";
File.Copy(_dbPath, safetyBackup, true);
LogManager.Info($"已创建安全备份: {safetyBackup}");
}
// 替换数据库文件
File.Copy(backupFilePath, _dbPath, true);
}
finally
{
// 重新打开连接
try { _database.Connection.Open(); } catch { }
}
LogManager.Info($"数据库恢复完成: {backupFilePath}");
}
catch (Exception ex)
{
LogManager.Error($"数据库恢复失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 获取数据库信息
/// </summary>
public DatabaseInfo GetDatabaseInfo()
{
try
{
var info = new DatabaseInfo
{
FilePath = _dbPath,
FileSize = File.Exists(_dbPath) ? new FileInfo(_dbPath).Length : 0,
Version = GetDatabaseVersion()
};
// 获取表行数
info.PathRoutesCount = GetTableCount("PathRoutes");
info.PathPointsCount = GetTableCount("PathPoints");
info.PathEdgesCount = GetTableCount("PathEdges");
info.ClashResultsCount = GetTableCount("ClashDetectiveResults");
info.ScreenshotsCount = GetTableCount("CollisionReportScreenshots");
return info;
}
catch (Exception ex)
{
LogManager.Error($"获取数据库信息失败: {ex.Message}");
return new DatabaseInfo();
}
}
private int GetTableCount(string tableName)
{
try
{
var sql = $"SELECT COUNT(*) FROM {tableName}";
using (var cmd = new SQLiteCommand(sql, _database.Connection))
{
return Convert.ToInt32(cmd.ExecuteScalar());
}
}
catch
{
return 0;
}
}
}
public class DatabaseInfo
{
public string FilePath { get; set; }
public long FileSize { get; set; }
public int Version { get; set; }
public int PathRoutesCount { get; set; }
public int PathPointsCount { get; set; }
public int PathEdgesCount { get; set; }
public int ClashResultsCount { get; set; }
public int ScreenshotsCount { get; set; }
public string FileSizeFormatted => $"{FileSize / 1024} KB";
public string VersionFormatted => BackupManager.FormatVersion(Version);
}
}

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Core.Config;
using NavisworksTransport.Core.Models;
using NavisworksTransport.Core.Database;
namespace NavisworksTransport
{
@ -18,12 +19,18 @@ namespace NavisworksTransport
{
internal SQLiteConnection _connection;
private readonly string _dbPath;
private BackupManager _backupManager;
/// <summary>
/// 数据库连接(供内部使用)
/// </summary>
internal SQLiteConnection Connection => _connection;
/// <summary>
/// 简单数据库备份管理器(直接备份文件,最可靠)
/// </summary>
public BackupManager Backup => _backupManager ?? (_backupManager = new BackupManager(this));
/// <summary>
/// 初始化路径数据库
/// </summary>
@ -217,6 +224,11 @@ namespace NavisworksTransport
)
");
// 9. 设置数据库版本SQLite内置user_version
// 版本号格式:主版本*10000 + 次版本*100 + 修订号
// 例如2.1.3 = 20103
ExecuteNonQuery("PRAGMA user_version = 20100"); // v2.1.0
// 创建索引
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathpoints_route ON PathPoints(RouteId)");
@ -868,6 +880,107 @@ namespace NavisworksTransport
}
}
/// <summary>
/// 获取所有碰撞检测结果
/// </summary>
public List<ClashDetectiveResultRecord> GetAllClashDetectiveResults()
{
var results = new List<ClashDetectiveResultRecord>();
try
{
var sql = @"
SELECT cdr.Id, cdr.TestName, cdr.RouteId, pr.Name AS PathName, cdr.TestTime,
cdr.CollisionCount, cdr.AnimationCollisionCount,
cdr.FrameRate, cdr.Duration, cdr.DetectionGap, cdr.AnimatedObjectName,
cdr.CreatedAt, cdr.ScreenshotPath
FROM ClashDetectiveResults cdr
INNER JOIN PathRoutes pr ON cdr.RouteId = pr.Id
ORDER BY cdr.TestTime DESC
";
using (var cmd = new SQLiteCommand(sql, _connection))
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
results.Add(new ClashDetectiveResultRecord
{
Id = Convert.ToInt32(reader["Id"]),
TestName = reader["TestName"].ToString(),
RouteId = reader["RouteId"].ToString(),
PathName = reader["PathName"].ToString(),
TestTime = Convert.ToDateTime(reader["TestTime"]),
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
FrameRate = Convert.ToInt32(reader["FrameRate"]),
Duration = Convert.ToDouble(reader["Duration"]),
DetectionGap = Convert.ToDouble(reader["DetectionGap"]),
AnimatedObjectName = reader["AnimatedObjectName"].ToString(),
CreatedAt = Convert.ToDateTime(reader["CreatedAt"]),
ScreenshotPath = reader["ScreenshotPath"]?.ToString()
});
}
}
}
catch (Exception ex)
{
LogManager.Error($"获取所有碰撞检测结果失败: {ex.Message}", ex);
}
return results;
}
/// <summary>
/// 清空所有数据
/// </summary>
public void ClearAllData()
{
try
{
// 按依赖关系顺序删除(先删除子表,再删除主表)
var tables = new[]
{
"CollisionReportScreenshots",
"ClashDetectiveCollisionObjects",
"ClashDetectiveResults",
"PathEdges",
"PathPoints",
"AnalysisResults",
"CollisionReports",
"PathRoutes",
"BatchQueueItems"
};
foreach (var table in tables)
{
try
{
ExecuteNonQuery($"DELETE FROM {table}");
LogManager.Debug($"已清空表: {table}");
}
catch (Exception ex)
{
LogManager.Warning($"清空表 {table} 失败(可能表不存在): {ex.Message}");
}
}
// 重置自增ID
try
{
ExecuteNonQuery("DELETE FROM sqlite_sequence");
}
catch { }
LogManager.Info("已清空所有数据表");
}
catch (Exception ex)
{
LogManager.Error($"清空数据失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 根据测试名称获取ClashDetective结果记录
/// </summary>

View File

@ -30,6 +30,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private string _memoryUsage = "0 MB";
private string _runningTime = "00:00:00";
// 数据管理字段
private string _databaseVersion = "--";
private string _pathCount = "--";
private string _collisionRecordCount = "--";
private bool _autoBackupEnabled = true;
private int _selectedBackupKeepCount = 10;
private string _lastBackupInfo = "暂无备份记录";
// 🔧 修复:添加释放状态标志
private bool _disposed = false;
@ -103,6 +111,44 @@ namespace NavisworksTransport.UI.WPF.ViewModels
set => SetProperty(ref _runningTime, value);
}
// 数据管理属性
public string DatabaseVersion
{
get => _databaseVersion;
set => SetProperty(ref _databaseVersion, value);
}
public string PathCount
{
get => _pathCount;
set => SetProperty(ref _pathCount, value);
}
public string CollisionRecordCount
{
get => _collisionRecordCount;
set => SetProperty(ref _collisionRecordCount, value);
}
public bool AutoBackupEnabled
{
get => _autoBackupEnabled;
set => SetProperty(ref _autoBackupEnabled, value);
}
public ObservableCollection<int> BackupKeepCountOptions { get; } = new ObservableCollection<int> { 5, 10, 20, 50 };
public int SelectedBackupKeepCount
{
get => _selectedBackupKeepCount;
set => SetProperty(ref _selectedBackupKeepCount, value);
}
public string LastBackupInfo
{
get => _lastBackupInfo;
set => SetProperty(ref _lastBackupInfo, value);
}
#endregion
@ -114,6 +160,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand GeneratePerformanceReportCommand { get; private set; }
public ICommand DiagnosticCommand { get; private set; }
// 数据管理命令
public ICommand BackupDataCommand { get; private set; }
public ICommand RestoreDataCommand { get; private set; }
public ICommand RepairDatabaseCommand { get; private set; }
public ICommand ClearAllDataCommand { get; private set; }
// 功能测试命令
public ICommand TestVoxelGridSDFCommand { get; private set; }
public ICommand TestVoxelPathFindingCommand { get; private set; }
@ -245,6 +297,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
GeneratePerformanceReportCommand = new RelayCommand(() => ExecuteGeneratePerformanceReport());
DiagnosticCommand = new RelayCommand(() => ExecuteDiagnostic());
// 数据管理命令
BackupDataCommand = new RelayCommand(() => ExecuteBackupData());
RestoreDataCommand = new RelayCommand(() => ExecuteRestoreData());
RepairDatabaseCommand = new RelayCommand(() => ExecuteRepairDatabase());
ClearAllDataCommand = new RelayCommand(() => ExecuteClearAllData());
// 功能测试命令
TestVoxelGridSDFCommand = new RelayCommand(() => ExecuteTestVoxelGridSDF());
TestVoxelPathFindingCommand = new RelayCommand(() => ExecuteTestVoxelPathFinding());
@ -531,6 +589,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 初始化系统信息
PluginVersion = "v1.0";
NavisworksVersion = "2026";
// 初始化数据库状态(如果数据库已连接)
RefreshDatabaseStatus();
UpdateMainStatus("系统管理初始化完成");
});
@ -852,6 +914,323 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
#region
/// <summary>
/// 刷新数据库状态信息
/// </summary>
private void RefreshDatabaseStatus()
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null)
{
// 获取数据库信息
var dbInfo = pathDatabase.Backup.GetDatabaseInfo();
DatabaseVersion = dbInfo.VersionFormatted;
PathCount = dbInfo.PathRoutesCount.ToString();
CollisionRecordCount = dbInfo.ClashResultsCount.ToString();
LogManager.Debug($"数据库状态: 版本{dbInfo.VersionFormatted}, 路径{dbInfo.PathRoutesCount}, 点{dbInfo.PathPointsCount}, 边{dbInfo.PathEdgesCount}, 碰撞{dbInfo.ClashResultsCount}");
}
else
{
DatabaseVersion = "未连接";
PathCount = "--";
CollisionRecordCount = "--";
}
}
catch (Exception ex)
{
LogManager.Error($"刷新数据库状态失败: {ex.Message}");
DatabaseVersion = "错误";
}
}
/// <summary>
/// 执行数据备份
/// </summary>
private void ExecuteBackupData()
{
SafeExecute(() =>
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接,请确保已加载模型",
"备份数据",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
return;
}
UpdateMainStatus("正在备份数据库...");
LogManager.Info("开始数据库文件备份");
string backupPath = pathDatabase.Backup.BackupDatabase();
if (!string.IsNullOrEmpty(backupPath))
{
var info = pathDatabase.Backup.GetDatabaseInfo();
LastBackupInfo = $"上次备份: {DateTime.Now:yyyy-MM-dd HH:mm:ss}, 版本{info.VersionFormatted}, {info.FileSizeFormatted}";
System.Windows.MessageBox.Show(
$"数据库备份完成!\n\n" +
$"版本: {info.VersionFormatted}\n" +
$"文件: {backupPath}\n\n" +
$"包含数据:\n" +
$"- 路径: {info.PathRoutesCount} 条\n" +
$"- 路径点: {info.PathPointsCount} 个\n" +
$"- 碰撞记录: {info.ClashResultsCount} 条\n" +
$"- 截图: {info.ScreenshotsCount} 张",
"备份成功",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
UpdateMainStatus("数据库备份完成");
LogManager.Info($"数据库备份完成: {backupPath}, 版本{info.VersionFormatted}");
}
}
catch (Exception ex)
{
LogManager.Error($"数据备份失败: {ex.Message}");
System.Windows.MessageBox.Show(
$"数据备份失败: {ex.Message}",
"备份失败",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
UpdateMainStatus("数据备份失败");
}
}, "数据备份");
}
/// <summary>
/// 执行数据恢复
/// </summary>
private void ExecuteRestoreData()
{
SafeExecute(() =>
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接,请确保已加载模型",
"恢复数据",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
return;
}
// 选择备份文件
var openFileDialog = new Microsoft.Win32.OpenFileDialog
{
Title = "选择数据库备份文件",
Filter = "数据库备份文件 (*.db)|*.db",
InitialDirectory = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"NavisworksTransport", "Backups")
};
if (openFileDialog.ShowDialog() != true)
{
return;
}
// 确认恢复
var result = System.Windows.MessageBox.Show(
"恢复数据将覆盖现有数据,建议先备份当前数据。\n\n是否继续",
"确认恢复",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Warning);
if (result != System.Windows.MessageBoxResult.Yes)
{
return;
}
UpdateMainStatus("正在恢复数据库...");
LogManager.Info($"开始数据库恢复: {openFileDialog.FileName}");
pathDatabase.Backup.RestoreDatabase(openFileDialog.FileName);
// 恢复成功
bool restoreSuccess = true;
if (restoreSuccess)
{
// 刷新数据库状态
RefreshDatabaseStatus();
var info = pathDatabase.Backup.GetDatabaseInfo();
System.Windows.MessageBox.Show(
$"数据库恢复完成!\n\n" +
$"当前数据:\n" +
$"- 路径: {info.PathRoutesCount} 条\n" +
$"- 路径点: {info.PathPointsCount} 个\n" +
$"- 碰撞记录: {info.ClashResultsCount} 条\n" +
$"- 截图: {info.ScreenshotsCount} 张\n\n" +
$"请重新启动 Navisworks 以加载恢复的数据。",
"恢复成功",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
UpdateMainStatus("数据恢复完成");
LogManager.Info("数据恢复完成");
// 刷新显示
RefreshDatabaseStatus();
}
}
catch (Exception ex)
{
LogManager.Error($"数据恢复失败: {ex.Message}");
System.Windows.MessageBox.Show(
$"数据恢复失败: {ex.Message}",
"恢复失败",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
UpdateMainStatus("数据恢复失败");
}
}, "数据恢复");
}
/// <summary>
/// 执行数据库修复
/// </summary>
private void ExecuteRepairDatabase()
{
SafeExecute(() =>
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接",
"修复数据库",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
return;
}
UpdateMainStatus("正在检查数据库...");
LogManager.Info("开始数据库修复检查");
// 获取数据库信息
var info = pathDatabase.Backup.GetDatabaseInfo();
System.Windows.MessageBox.Show(
$"数据库检查结果:\n\n" +
$"版本: {info.VersionFormatted}\n" +
$"文件大小: {info.FileSizeFormatted}\n" +
$"路径数量: {info.PathRoutesCount} 条\n" +
$"路径点: {info.PathPointsCount} 个\n" +
$"路径边: {info.PathEdgesCount} 条\n" +
$"碰撞记录: {info.ClashResultsCount} 条\n" +
$"截图: {info.ScreenshotsCount} 张",
"检查结果",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
// 刷新状态
RefreshDatabaseStatus();
UpdateMainStatus("数据库检查完成");
}
catch (Exception ex)
{
LogManager.Error($"数据库修复失败: {ex.Message}");
System.Windows.MessageBox.Show(
$"数据库修复失败: {ex.Message}",
"修复失败",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
UpdateMainStatus("数据库修复失败");
}
}, "数据库修复");
}
/// <summary>
/// 执行清空所有数据
/// </summary>
private void ExecuteClearAllData()
{
SafeExecute(() =>
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接",
"清空数据",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
return;
}
// 双重确认
var result1 = System.Windows.MessageBox.Show(
"⚠️ 警告:此操作将永久删除所有路径和碰撞记录!\n\n" +
"建议先备份数据。\n\n" +
"是否继续?",
"确认清空数据",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Warning);
if (result1 != System.Windows.MessageBoxResult.Yes)
{
return;
}
var result2 = System.Windows.MessageBox.Show(
"请再次确认:是否永久删除所有数据?\n\n" +
"此操作不可恢复!",
"最终确认",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Warning);
if (result2 != System.Windows.MessageBoxResult.Yes)
{
return;
}
UpdateMainStatus("正在清空数据...");
LogManager.Info("开始清空所有数据");
// 清空所有表
pathDatabase.ClearAllData();
System.Windows.MessageBox.Show(
"所有数据已清空!",
"清空完成",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
UpdateMainStatus("数据已清空");
LogManager.Info("所有数据已清空");
// 刷新状态
RefreshDatabaseStatus();
}
catch (Exception ex)
{
LogManager.Error($"清空数据失败: {ex.Message}");
System.Windows.MessageBox.Show(
$"清空数据失败: {ex.Message}",
"操作失败",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
UpdateMainStatus("清空数据失败");
}
}, "清空数据");
}
#endregion
/// <summary>
/// 执行体素网格 SDF 测试(使用 MeshSignedDistanceGrid
/// </summary>

View File

@ -42,6 +42,80 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="10">
<StackPanel>
<!-- 区域0: 数据管理 -->
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,15" Padding="12"
Background="#FFF8FBFF">
<StackPanel>
<Label Content="📦 数据管理" Style="{StaticResource SectionHeaderStyle}"/>
<!-- 数据库状态信息 -->
<Grid Margin="0,5,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="数据库版本:" Style="{StaticResource ParameterLabelStyle}" Width="100"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding DatabaseVersion}" Style="{StaticResource InfoTextStyle}"/>
<Label Grid.Row="1" Grid.Column="0" Content="路径数量:" Style="{StaticResource ParameterLabelStyle}" Width="100"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding PathCount}" Style="{StaticResource InfoTextStyle}"/>
<Label Grid.Row="2" Grid.Column="0" Content="碰撞记录:" Style="{StaticResource ParameterLabelStyle}" Width="100"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding CollisionRecordCount}" Style="{StaticResource InfoTextStyle}"/>
</Grid>
<!-- 数据操作按钮 -->
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Button Content="💾 备份数据"
Command="{Binding BackupDataCommand}"
Style="{StaticResource ActionButtonStyle}"
ToolTip="导出所有路径、碰撞记录和截图到备份文件"/>
<Button Content="📂 恢复数据"
Command="{Binding RestoreDataCommand}"
Style="{StaticResource SecondaryButtonStyle}"
ToolTip="从备份文件恢复数据"/>
<Button Content="🔧 修复数据库"
Command="{Binding RepairDatabaseCommand}"
Style="{StaticResource SecondaryButtonStyle}"
ToolTip="检查并修复数据库完整性"/>
<Button Content="🗑️ 清空数据"
Command="{Binding ClearAllDataCommand}"
Style="{StaticResource SecondaryButtonStyle}"
ToolTip="清空所有路径和碰撞记录(谨慎使用)"/>
</StackPanel>
<!-- 自动备份设置 -->
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<CheckBox Content="启用自动备份"
IsChecked="{Binding AutoBackupEnabled}"
VerticalAlignment="Center"
Margin="0,0,10,0"/>
<TextBlock Text="保留最近" VerticalAlignment="Center" FontSize="10"/>
<ComboBox ItemsSource="{Binding BackupKeepCountOptions}"
SelectedItem="{Binding SelectedBackupKeepCount}"
Width="50"
Margin="5,0"/>
<TextBlock Text="个备份" VerticalAlignment="Center" FontSize="10"/>
</StackPanel>
<!-- 上次备份信息 -->
<TextBlock Text="{Binding LastBackupInfo}"
FontSize="10"
Foreground="{StaticResource NavisworksDarkBrush}"
Margin="0,5,0,0"
TextWrapping="Wrap"/>
</StackPanel>
</Border>
<!-- 区域1: 日志管理 -->
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,15" Padding="12">
<StackPanel>