给碰撞报告增加多截图的能力;修复路径宽度在修改路径点坐标后回到默认值的问题

This commit is contained in:
tian 2026-02-07 00:22:07 +08:00
parent 84e5abf592
commit d9da8e95ce
11 changed files with 1192 additions and 204 deletions

View File

@ -282,6 +282,7 @@
<Compile Include="src\UI\WPF\Converters\BatchQueueStatusConverter.cs" />
<Compile Include="src\UI\WPF\Converters\BatchQueueStatusHelper.cs" />
<Compile Include="src\UI\WPF\Converters\NullToDashConverter.cs" />
<Compile Include="src\UI\WPF\Converters\PathToImageConverter.cs" />
<Compile Include="src\UI\WPF\Models\LogisticsModel.cs" />
<Compile Include="src\UI\WPF\Models\PathRouteViewModel.cs" />
<Compile Include="src\UI\WPF\Models\SplitPreviewItem.cs" />

View File

@ -78,6 +78,31 @@ namespace NavisworksTransport.Commands
public string ScreenshotPath { get; set; }
}
/// <summary>
/// 碰撞报告截图项
/// </summary>
public class CollisionReportScreenshot
{
/// <summary>截图唯一标识UI用</summary>
public string Id { get; set; } = Guid.NewGuid().ToString("N");
/// <summary>数据库记录Id用于删除</summary>
public int DatabaseId { get; set; }
/// <summary>截图文件路径</summary>
public string FilePath { get; set; }
/// <summary>图片格式 (PNG/JPG)</summary>
public string Format { get; set; }
/// <summary>图片宽度</summary>
public int Width { get; set; }
/// <summary>图片高度</summary>
public int Height { get; set; }
/// <summary>截图时间</summary>
public DateTime CaptureTime { get; set; } = DateTime.Now;
/// <summary>截图描述/备注</summary>
public string Description { get; set; }
/// <summary>排序顺序</summary>
public int SortOrder { get; set; }
}
/// <summary>
/// 碰撞报告结果类
/// </summary>
@ -96,16 +121,54 @@ namespace NavisworksTransport.Commands
// 新增:动画参数
public string RouteId { get; set; } // 路径ID用于数据库关联
public int ResultId { get; set; } // 数据库记录Id用于关联截图
public string PathName { get; set; }
public int FrameRate { get; set; }
public double Duration { get; set; }
public double DetectionTolerance { get; set; }
// 新增:截图信息
public string ScreenshotPath { get; set; }
public string ScreenshotFormat { get; set; }
public int ScreenshotWidth { get; set; }
public int ScreenshotHeight { get; set; }
// 截图列表 - 支持多张截图
public List<CollisionReportScreenshot> Screenshots { get; set; } = new List<CollisionReportScreenshot>();
// 兼容旧代码:单张截图属性(现在指向第一张截图)
[Obsolete("请使用 Screenshots 列表")]
public string ScreenshotPath
{
get => Screenshots?.FirstOrDefault()?.FilePath;
set
{
if (!string.IsNullOrEmpty(value))
{
if (Screenshots == null) Screenshots = new List<CollisionReportScreenshot>();
if (Screenshots.Count == 0)
{
Screenshots.Add(new CollisionReportScreenshot { FilePath = value });
}
else
{
Screenshots[0].FilePath = value;
}
}
}
}
[Obsolete("请使用 Screenshots 列表")]
public string ScreenshotFormat
{
get => Screenshots?.FirstOrDefault()?.Format;
set { if (Screenshots?.FirstOrDefault() != null) Screenshots[0].Format = value; }
}
[Obsolete("请使用 Screenshots 列表")]
public int ScreenshotWidth
{
get => Screenshots?.FirstOrDefault()?.Width ?? 0;
set { if (Screenshots?.FirstOrDefault() != null) Screenshots[0].Width = value; }
}
[Obsolete("请使用 Screenshots 列表")]
public int ScreenshotHeight
{
get => Screenshots?.FirstOrDefault()?.Height ?? 0;
set { if (Screenshots?.FirstOrDefault() != null) Screenshots[0].Height = value; }
}
}
/// <summary>
@ -184,8 +247,13 @@ namespace NavisworksTransport.Commands
var testInfo = GetTestInfoFromDatabase(targetTestName);
result.PathName = testInfo?.PathName ?? "未知路径";
result.RouteId = testInfo?.RouteId;
result.ResultId = testInfo?.Id ?? 0;
LogManager.Info($"碰撞报告计数 - 检查点: {result.AnimationCollisions}, Clash Detective: {result.TotalCollisions}");
LogManager.Info($"碰撞报告数据 - TestName={targetTestName}, ResultId={result.ResultId}, PathName={result.PathName}");
if (testInfo == null)
{
LogManager.Warning($"未在数据库中找到测试记录: {targetTestName}");
}
UpdateProgress(70, "生成报告内容...");
@ -247,25 +315,70 @@ namespace NavisworksTransport.Commands
// 视角调整失败不影响截图生成
}
UpdateProgress(95, "生成碰撞场景截图...");
UpdateProgress(95, "加载碰撞报告截图...");
// 检查是否已有截图路径(从数据库
// 加载多截图数据(从新的截图表
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
var existingScreenshotPath = pathDatabase != null ?
pathDatabase.GetClashDetectiveResultByTestName(targetTestName)?.ScreenshotPath : null;
if (!string.IsNullOrEmpty(existingScreenshotPath) && System.IO.File.Exists(existingScreenshotPath))
if (pathDatabase != null)
{
// 使用数据库中保存的截图路径
result.ScreenshotPath = existingScreenshotPath;
result.ScreenshotFormat = "JPG";
result.ScreenshotWidth = 1920;
result.ScreenshotHeight = 1080;
LogManager.Info($"使用数据库中保存的截图: {existingScreenshotPath}");
try
{
// 先获取结果记录以获取 ResultId
var testRecord = pathDatabase.GetClashDetectiveResultByTestName(targetTestName);
if (testRecord != null)
{
LogManager.Debug($"加载截图 - 找到测试记录: TestName={targetTestName}, ResultId={testRecord.Id}");
// 从新的截图表加载所有截图
var screenshotRecords = pathDatabase.GetCollisionReportScreenshots(testRecord.Id);
LogManager.Debug($"加载截图 - 查询到 {screenshotRecords?.Count ?? 0} 张截图 (ResultId={testRecord.Id})");
if (screenshotRecords != null && screenshotRecords.Count > 0)
{
result.Screenshots = screenshotRecords.Select(r => new CollisionReportScreenshot
{
DatabaseId = r.Id, // 数据库主键,用于删除
FilePath = r.FilePath,
Format = r.Format,
Width = r.Width,
Height = r.Height,
CaptureTime = r.CaptureTime,
Description = r.Description,
SortOrder = r.SortOrder
}).ToList();
LogManager.Info($"从数据库加载了 {result.Screenshots.Count} 张截图 (ResultId={testRecord.Id})");
}
else
{
// 回退:检查旧表中的单截图
var existingScreenshotPath = testRecord.ScreenshotPath;
if (!string.IsNullOrEmpty(existingScreenshotPath) && System.IO.File.Exists(existingScreenshotPath))
{
result.Screenshots = new List<CollisionReportScreenshot>
{
new CollisionReportScreenshot
{
FilePath = existingScreenshotPath,
Format = "JPG",
Width = 1920,
Height = 1080,
CaptureTime = testRecord.TestTime
}
};
LogManager.Info($"从旧表加载单截图: {existingScreenshotPath}");
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"加载截图数据失败: {ex.Message}");
}
}
else
// 如果没有加载到任何截图,生成一张默认截图
if (result.Screenshots == null || result.Screenshots.Count == 0)
{
// 生成新截图
try
{
string screenshotPath = PathHelper.GenerateSceneScreenshot(
@ -278,13 +391,20 @@ namespace NavisworksTransport.Commands
if (screenshotPath != null)
{
result.ScreenshotPath = screenshotPath;
result.ScreenshotFormat = "JPG";
result.ScreenshotWidth = 1920;
result.ScreenshotHeight = 1080;
LogManager.Info($"碰撞场景截图已自动生成: {screenshotPath}");
result.Screenshots = new List<CollisionReportScreenshot>
{
new CollisionReportScreenshot
{
FilePath = screenshotPath,
Format = "JPG",
Width = 1920,
Height = 1080,
CaptureTime = DateTime.Now
}
};
LogManager.Info($"自动生成默认截图: {screenshotPath}");
// 保存截图路径到数据库
// 保存截图路径到数据库(旧表兼容)
if (pathDatabase != null)
{
try
@ -301,7 +421,6 @@ namespace NavisworksTransport.Commands
catch (Exception dbEx)
{
LogManager.Error($"保存截图路径到数据库失败: {dbEx.Message}");
// 数据库更新失败不影响报告生成
}
}
}
@ -309,7 +428,6 @@ namespace NavisworksTransport.Commands
catch (Exception ex)
{
LogManager.Error($"自动生成碰撞场景截图失败: {ex.Message}");
// 截图失败不影响报告生成
}
}

View File

@ -201,6 +201,22 @@ namespace NavisworksTransport
)
");
// 8. 碰撞报告截图表(支持多张截图)
ExecuteNonQuery(@"
CREATE TABLE IF NOT EXISTS CollisionReportScreenshots (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
ResultId INTEGER NOT NULL,
FilePath TEXT NOT NULL,
Format TEXT,
Width INTEGER,
Height INTEGER,
CaptureTime DATETIME DEFAULT CURRENT_TIMESTAMP,
Description TEXT,
SortOrder INTEGER DEFAULT 0,
FOREIGN KEY(ResultId) REFERENCES ClashDetectiveResults(Id) ON DELETE CASCADE
)
");
// 创建索引
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathpoints_route ON PathPoints(RouteId)");
@ -209,8 +225,10 @@ namespace NavisworksTransport
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_test_time ON ClashDetectiveResults(TestTime DESC)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_objects_result ON ClashDetectiveCollisionObjects(ResultId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_objects_path ON ClashDetectiveCollisionObjects(PathId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_screenshots_result ON CollisionReportScreenshots(ResultId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_screenshots_sort ON CollisionReportScreenshots(ResultId, SortOrder)");
// 8. 批处理队列表简化版只维护FIFO队列
// 9. 批处理队列表简化版只维护FIFO队列
ExecuteNonQuery(@"
CREATE TABLE IF NOT EXISTS BatchQueueItems (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -392,46 +410,193 @@ namespace NavisworksTransport
}
/// <summary>
/// 更新碰撞报告的截图信息更新ClashDetectiveResults表
/// 更新碰撞报告的截图信息更新ClashDetectiveResults表,兼容旧版本
/// </summary>
public void UpdateCollisionReportScreenshot(string routeId, string screenshotPath,
string screenshotFormat, int screenshotWidth, int screenshotHeight)
{
try
{
// 更新 ClashDetectiveResults 表(该表包含 ScreenshotPath 列)
// 更新该 RouteId 的最新记录
var sql = @"
// 获取该 RouteId 的最新记录Id
var getIdSql = @"
SELECT Id FROM ClashDetectiveResults
WHERE RouteId = @routeId
ORDER BY TestTime DESC
LIMIT 1
";
int? resultId = null;
using (var cmd = new SQLiteCommand(getIdSql, _connection))
{
cmd.Parameters.AddWithValue("@routeId", routeId ?? "");
var result = cmd.ExecuteScalar();
if (result != null && result != DBNull.Value)
{
resultId = Convert.ToInt32(result);
}
}
if (!resultId.HasValue)
{
LogManager.Warning($"未找到需要更新的碰撞记录: RouteId={routeId}");
return;
}
// 同时更新旧表的 ScreenshotPath 字段(兼容旧版本)
var updateSql = @"
UPDATE ClashDetectiveResults
SET ScreenshotPath = @screenshotPath
WHERE RouteId = @routeId
AND Id = (
SELECT Id FROM ClashDetectiveResults
WHERE RouteId = @routeId
ORDER BY TestTime DESC
LIMIT 1
)
WHERE Id = @resultId
";
using (var cmd = new SQLiteCommand(updateSql, _connection))
{
cmd.Parameters.AddWithValue("@resultId", resultId.Value);
cmd.Parameters.AddWithValue("@screenshotPath", screenshotPath ?? (object)DBNull.Value);
cmd.ExecuteNonQuery();
}
// 添加/更新到新的截图表
SaveCollisionReportScreenshot(resultId.Value, screenshotPath, screenshotFormat, screenshotWidth, screenshotHeight, 0);
LogManager.Info($"碰撞报告截图已更新: RouteId={routeId}, 截图={screenshotPath}");
}
catch (Exception ex)
{
LogManager.Error($"更新碰撞报告截图失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 保存碰撞报告截图到截图表返回新记录Id
/// </summary>
public int SaveCollisionReportScreenshot(int resultId, string filePath, string format, int width, int height, int sortOrder, string description = null)
{
try
{
var sql = @"
INSERT INTO CollisionReportScreenshots
(ResultId, FilePath, Format, Width, Height, CaptureTime, Description, SortOrder)
VALUES (@resultId, @filePath, @format, @width, @height, @captureTime, @description, @sortOrder)
";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@routeId", routeId ?? "");
cmd.Parameters.AddWithValue("@screenshotPath", screenshotPath ?? (object)DBNull.Value);
var rowsAffected = cmd.ExecuteNonQuery();
if (rowsAffected > 0)
cmd.Parameters.AddWithValue("@resultId", resultId);
cmd.Parameters.AddWithValue("@filePath", filePath ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@format", format ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@width", width);
cmd.Parameters.AddWithValue("@height", height);
cmd.Parameters.AddWithValue("@captureTime", DateTime.Now);
cmd.Parameters.AddWithValue("@description", description ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@sortOrder", sortOrder);
cmd.ExecuteNonQuery();
}
// 获取新插入记录的Id
int newId = (int)_connection.LastInsertRowId;
LogManager.Debug($"碰撞报告截图已保存: Id={newId}, ResultId={resultId}, FilePath={filePath}");
return newId;
}
catch (Exception ex)
{
LogManager.Error($"保存碰撞报告截图失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 获取碰撞报告的所有截图
/// </summary>
public List<CollisionReportScreenshotRecord> GetCollisionReportScreenshots(int resultId)
{
var screenshots = new List<CollisionReportScreenshotRecord>();
try
{
var sql = @"
SELECT Id, ResultId, FilePath, Format, Width, Height, CaptureTime, Description, SortOrder
FROM CollisionReportScreenshots
WHERE ResultId = @resultId
ORDER BY SortOrder, CaptureTime
";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@resultId", resultId);
using (var reader = cmd.ExecuteReader())
{
LogManager.Info($"碰撞报告截图已更新: RouteId={routeId}, 截图={screenshotPath}");
}
else
{
LogManager.Warning($"未找到需要更新的碰撞记录: RouteId={routeId}");
while (reader.Read())
{
screenshots.Add(new CollisionReportScreenshotRecord
{
Id = Convert.ToInt32(reader["Id"]),
ResultId = Convert.ToInt32(reader["ResultId"]),
FilePath = reader["FilePath"]?.ToString(),
Format = reader["Format"]?.ToString(),
Width = reader["Width"] != DBNull.Value ? Convert.ToInt32(reader["Width"]) : 0,
Height = reader["Height"] != DBNull.Value ? Convert.ToInt32(reader["Height"]) : 0,
CaptureTime = reader["CaptureTime"] != DBNull.Value ? Convert.ToDateTime(reader["CaptureTime"]) : DateTime.MinValue,
Description = reader["Description"]?.ToString(),
SortOrder = reader["SortOrder"] != DBNull.Value ? Convert.ToInt32(reader["SortOrder"]) : 0
});
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"更新碰撞报告截图失败: {ex.Message}", ex);
LogManager.Error($"获取碰撞报告截图失败: {ex.Message}", ex);
}
return screenshots;
}
/// <summary>
/// 删除碰撞报告截图
/// </summary>
public void DeleteCollisionReportScreenshot(int screenshotId)
{
try
{
var sql = "DELETE FROM CollisionReportScreenshots WHERE Id = @id";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@id", screenshotId);
cmd.ExecuteNonQuery();
}
LogManager.Debug($"碰撞报告截图已删除: ScreenshotId={screenshotId}");
}
catch (Exception ex)
{
LogManager.Error($"删除碰撞报告截图失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 更新截图排序和描述
/// </summary>
public void UpdateCollisionReportScreenshotMetadata(int screenshotId, int sortOrder, string description)
{
try
{
var sql = @"
UPDATE CollisionReportScreenshots
SET SortOrder = @sortOrder, Description = @description
WHERE Id = @id
";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@id", screenshotId);
cmd.Parameters.AddWithValue("@sortOrder", sortOrder);
cmd.Parameters.AddWithValue("@description", description ?? (object)DBNull.Value);
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogManager.Error($"更新截图元数据失败: {ex.Message}", ex);
throw;
}
}
@ -1877,6 +2042,22 @@ namespace NavisworksTransport
public string ObjectName { get; set; }
}
/// <summary>
/// 碰撞报告截图记录
/// </summary>
public class CollisionReportScreenshotRecord
{
public int Id { get; set; }
public int ResultId { get; set; }
public string FilePath { get; set; }
public string Format { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public DateTime CaptureTime { get; set; }
public string Description { get; set; }
public int SortOrder { get; set; }
}
/// <summary>
/// 路径分析结果数据模型
/// </summary>

View File

@ -2,6 +2,7 @@ using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Linq;
namespace NavisworksTransport.UI.WPF.Converters
{
@ -122,4 +123,24 @@ namespace NavisworksTransport.UI.WPF.Converters
return false;
}
}
/// <summary>
/// 等值转换器 - 用于MultiBinding比较两个值是否相等
/// </summary>
public class EqualityToBooleanConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length < 2)
return false;
// 比较第一个值和第二个值是否相等
return Equals(values[0], values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException("EqualityToBooleanConverter不支持反向转换");
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace NavisworksTransport.UI.WPF.Converters
{
/// <summary>
/// 文件路径转换为BitmapImage的转换器
/// </summary>
public class PathToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string path && !string.IsNullOrEmpty(path) && File.Exists(path))
{
try
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(path);
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
catch (Exception ex)
{
LogManager.Warning($"加载图片失败: {path}, 错误: {ex.Message}");
return null;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -11,6 +11,7 @@ using Microsoft.Win32;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using NavisworksTransport.UI.WPF.Views;
using ImageFormat = System.Drawing.Imaging.ImageFormat;
namespace NavisworksTransport.UI.WPF.ViewModels
{
@ -252,36 +253,80 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
/// <summary>
/// 截图预览图像源
/// 截图预览图像源(兼容旧版本,返回当前选中的截图)
/// </summary>
public BitmapImage ScreenshotPreviewSource
public BitmapImage ScreenshotPreviewSource => GetScreenshotBitmap(SelectedScreenshot);
/// <summary>
/// 截图列表
/// </summary>
private ObservableCollection<CollisionReportScreenshot> _screenshots = new ObservableCollection<CollisionReportScreenshot>();
public ObservableCollection<CollisionReportScreenshot> Screenshots
{
get
get => _screenshots;
set
{
if (string.IsNullOrEmpty(CurrentReport?.ScreenshotPath) ||
!File.Exists(CurrentReport.ScreenshotPath))
if (SetProperty(ref _screenshots, value))
{
return null;
OnPropertyChanged(nameof(HasScreenshots));
OnPropertyChanged(nameof(ScreenshotPreviewSource));
}
try
}
}
/// <summary>
/// 当前选中的截图
/// </summary>
private CollisionReportScreenshot _selectedScreenshot;
public CollisionReportScreenshot SelectedScreenshot
{
get => _selectedScreenshot;
set
{
if (SetProperty(ref _selectedScreenshot, value))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(CurrentReport.ScreenshotPath);
bitmap.EndInit();
bitmap.Freeze(); // 防止跨线程访问问题
return bitmap;
}
catch (Exception ex)
{
LogManager.Error($"加载截图预览失败: {ex.Message}");
return null;
OnPropertyChanged(nameof(ScreenshotPreviewSource));
}
}
}
/// <summary>
/// 是否有截图
/// </summary>
public bool HasScreenshots => Screenshots?.Count > 0;
/// <summary>
/// 截图数量
/// </summary>
public int ScreenshotCount => Screenshots?.Count ?? 0;
/// <summary>
/// 获取截图的BitmapImage
/// </summary>
private BitmapImage GetScreenshotBitmap(CollisionReportScreenshot screenshot)
{
if (screenshot == null || string.IsNullOrEmpty(screenshot.FilePath) || !File.Exists(screenshot.FilePath))
{
return null;
}
try
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(screenshot.FilePath);
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
catch (Exception ex)
{
LogManager.Error($"加载截图预览失败: {ex.Message}");
return null;
}
}
#endregion
#region
@ -297,9 +342,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand CloseCommand { get; }
/// <summary>
/// 重新截图命令
/// 添加截图命令
/// </summary>
public ICommand RetakeScreenshotCommand { get; }
public ICommand AddScreenshotCommand { get; }
/// <summary>
/// 删除截图命令
/// </summary>
public ICommand DeleteScreenshotCommand { get; }
/// <summary>
/// 选择截图命令
/// </summary>
public ICommand SelectScreenshotCommand { get; }
/// <summary>
/// 截图设置命令
/// </summary>
public ICommand ScreenshotSettingsCommand { get; }
/// <summary>
/// 高亮碰撞对象命令
@ -308,6 +368,42 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#endregion
#region
// 默认截图设置
private int _screenshotWidth = 1920;
private int _screenshotHeight = 1080;
private ImageFormat _screenshotFormat = ImageFormat.Png;
/// <summary>
/// 截图宽度
/// </summary>
public int ScreenshotWidthSetting
{
get => _screenshotWidth;
set => SetProperty(ref _screenshotWidth, value);
}
/// <summary>
/// 截图高度
/// </summary>
public int ScreenshotHeightSetting
{
get => _screenshotHeight;
set => SetProperty(ref _screenshotHeight, value);
}
/// <summary>
/// 截图格式
/// </summary>
public ImageFormat ScreenshotFormatSetting
{
get => _screenshotFormat;
set => SetProperty(ref _screenshotFormat, value);
}
#endregion
#region
public CollisionReportViewModel()
@ -322,7 +418,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ExportReportCommand = new RelayCommand(async () => await ExportReportAsync(), () => !IsGenerating && HasCollisions);
CloseCommand = new RelayCommand(CloseWindow);
HighlightCollisionCommand = new RelayCommand<CollisionReportItem>(HighlightCollision, item => item?.CollisionData != null);
RetakeScreenshotCommand = new RelayCommand(ExecuteRetakeScreenshot, CanExecuteRetakeScreenshot);
AddScreenshotCommand = new RelayCommand(ExecuteAddScreenshot, CanExecuteAddScreenshot);
DeleteScreenshotCommand = new RelayCommand<CollisionReportScreenshot>(ExecuteDeleteScreenshot, s => s != null);
SelectScreenshotCommand = new RelayCommand<CollisionReportScreenshot>(ExecuteSelectScreenshot);
ScreenshotSettingsCommand = new RelayCommand(ExecuteScreenshotSettings);
// 初始化状态
ReportStatus = CollisionReportStatus.Generating;
@ -360,6 +459,34 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 清空现有数据
AnimationCollisions.Clear();
CollidedObjectsList.Clear();
Screenshots.Clear();
// 加载截图列表
if (reportResult.Screenshots?.Count > 0)
{
foreach (var screenshot in reportResult.Screenshots.OrderBy(s => s.SortOrder))
{
Screenshots.Add(screenshot);
}
// 默认选中第一张
SelectedScreenshot = Screenshots.FirstOrDefault();
LogManager.Info($"碰撞报告加载了 {Screenshots.Count} 张截图");
}
else if (!string.IsNullOrEmpty(reportResult.ScreenshotPath))
{
// 兼容旧版本:如果只有单张截图路径,也添加到列表
var oldScreenshot = new CollisionReportScreenshot
{
FilePath = reportResult.ScreenshotPath,
Format = reportResult.ScreenshotFormat,
Width = reportResult.ScreenshotWidth,
Height = reportResult.ScreenshotHeight,
SortOrder = 0
};
Screenshots.Add(oldScreenshot);
SelectedScreenshot = oldScreenshot;
LogManager.Info("碰撞报告加载了1张旧版截图");
}
// 设置运动构件信息
MovingObjectInfo = reportResult.MovingObjectInfo;
@ -638,6 +765,35 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 自动导出报告到默认目录(关闭窗口时调用)
/// </summary>
public async Task AutoExportReportAsync()
{
try
{
if (CurrentReport == null || !HasCollisions) return;
// 生成默认文件名和路径
var sanitizedName = PathHelper.SanitizeFileName(CurrentReport.PathName ?? "未知路径");
var fileName = $"NavisworksTransport_CollisionReport_{sanitizedName}_{DateTime.Now:yyyyMMdd_HHmmss}.html";
var reportDir = PathHelper.GetReportDirectory();
var filePath = Path.Combine(reportDir, fileName);
await Task.Run(() =>
{
var content = GenerateHtmlReport(filePath);
File.WriteAllText(filePath, content, Encoding.UTF8);
});
LogManager.Info($"报告已自动导出到: {filePath}");
}
catch (Exception ex)
{
LogManager.Error($"自动导出报告失败: {ex.Message}");
}
}
/// <summary>
/// 生成导出内容
/// </summary>
@ -831,23 +987,56 @@ namespace NavisworksTransport.UI.WPF.ViewModels
html.AppendLine("</div>");
html.AppendLine("</div>");
// 碰撞场景截图
if (!string.IsNullOrEmpty(CurrentReport?.ScreenshotPath) && File.Exists(CurrentReport.ScreenshotPath))
// 碰撞场景截图(支持多张)
var validScreenshots = Screenshots?.Where(s => !string.IsNullOrEmpty(s.FilePath) && File.Exists(s.FilePath)).ToList();
if (validScreenshots?.Count > 0)
{
html.AppendLine("<h2>碰撞场景截图</h2>");
html.AppendLine("<div class='screenshot-section'>");
// 使用 PathHelper 计算相对路径
string relativePath = PathHelper.GetRelativePath(htmlFilePath, CurrentReport.ScreenshotPath);
html.AppendLine("<div class='screenshot-container'>");
html.AppendLine($"<img src=\"{relativePath}\" alt=\"碰撞场景截图\" class=\"screenshot-image\"/>");
html.AppendLine("<div class='screenshot-info'>");
html.AppendLine($"<p>分辨率: {CurrentReport.ScreenshotWidth} x {CurrentReport.ScreenshotHeight}</p>");
html.AppendLine($"<p>格式: {CurrentReport.ScreenshotFormat}</p>");
html.AppendLine("</div>");
html.AppendLine("</div>");
html.AppendLine("</div>");
if (validScreenshots.Count > 1)
{
// 多张截图 - 画廊样式
html.AppendLine("<style>");
html.AppendLine(".screenshot-gallery { display: flex; flex-wrap: wrap; gap: 15px; margin: 15px 0; }");
html.AppendLine(".screenshot-item { flex: 0 0 calc(50% - 10px); background: white; padding: 10px; border-radius: 6px; border: 1px solid #ddd; box-sizing: border-box; }");
html.AppendLine(".screenshot-item:first-child { flex: 0 0 100%; }"); // 第一张图占满宽度
html.AppendLine(".screenshot-thumb { width: 100%; height: auto; border-radius: 4px; }");
html.AppendLine(".screenshot-desc { margin-top: 8px; font-size: 12px; color: #666; text-align: center; }");
html.AppendLine(".screenshot-meta { font-size: 11px; color: #999; text-align: center; margin-top: 4px; }");
html.AppendLine("</style>");
html.AppendLine("<div class='screenshot-gallery'>");
int index = 1;
foreach (var screenshot in validScreenshots.OrderBy(s => s.SortOrder))
{
string relativePath = PathHelper.GetRelativePath(htmlFilePath, screenshot.FilePath);
html.AppendLine("<div class='screenshot-item'>");
html.AppendLine($"<img src=\"{relativePath}\" alt=\"截图 {index}\" class=\"screenshot-thumb\"/>");
if (!string.IsNullOrEmpty(screenshot.Description))
{
html.AppendLine($"<div class='screenshot-desc'>{screenshot.Description}</div>");
}
html.AppendLine($"<div class='screenshot-meta'>截图 {index} • {screenshot.Width}x{screenshot.Height}</div>");
html.AppendLine("</div>");
index++;
}
html.AppendLine("</div>");
}
else
{
// 单张截图
var screenshot = validScreenshots.First();
html.AppendLine("<div class='screenshot-section'>");
string relativePath = PathHelper.GetRelativePath(htmlFilePath, screenshot.FilePath);
html.AppendLine("<div class='screenshot-container'>");
html.AppendLine($"<img src=\"{relativePath}\" alt=\"碰撞场景截图\" class=\"screenshot-image\"/>");
html.AppendLine("<div class='screenshot-info'>");
html.AppendLine($"<p>分辨率: {screenshot.Width} x {screenshot.Height}</p>");
html.AppendLine($"<p>格式: {screenshot.Format}</p>");
html.AppendLine("</div>");
html.AppendLine("</div>");
html.AppendLine("</div>");
}
}
// 运动构件信息
@ -978,17 +1167,105 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
/// <summary>
/// 执行重新截图
/// 执行添加新截图(使用默认设置,不弹出对话框)
/// </summary>
private void ExecuteRetakeScreenshot()
private void ExecuteAddScreenshot()
{
try
{
// 复用现有的截图配置对话框
var dialog = new GenerateNavigationMapDialog();
dialog.Title = "碰撞场景截图配置";
// 使用默认设置直接生成截图,不弹出对话框
string screenshotPath = PathHelper.GenerateSceneScreenshot(
CurrentReport.PathName ?? "collision",
ScreenshotWidthSetting,
ScreenshotHeightSetting,
ScreenshotFormatSetting,
"collision"
);
if (screenshotPath != null)
{
// 创建截图对象
var screenshot = new CollisionReportScreenshot
{
FilePath = screenshotPath,
Format = PathHelper.ImageFormatToExtension(ScreenshotFormatSetting).ToUpper(),
Width = ScreenshotWidthSetting,
Height = ScreenshotHeightSetting,
SortOrder = Screenshots.Count
};
// 添加到列表
Screenshots.Add(screenshot);
SelectedScreenshot = screenshot;
// 同步到报告数据
if (CurrentReport.Screenshots == null)
CurrentReport.Screenshots = new List<CollisionReportScreenshot>();
CurrentReport.Screenshots.Add(screenshot);
// 保存到数据库
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
// 使用 ResultId 精确关联到当前报告记录
if (pathDatabase != null && CurrentReport.ResultId > 0)
{
int dbId = pathDatabase.SaveCollisionReportScreenshot(
CurrentReport.ResultId,
screenshotPath,
screenshot.Format,
screenshot.Width,
screenshot.Height,
screenshot.SortOrder
);
screenshot.DatabaseId = dbId; // 保存数据库Id用于后续删除
LogManager.Info($"新截图已保存到数据库: DatabaseId={dbId}, ResultId={CurrentReport.ResultId}, Path={screenshotPath}");
}
else
{
LogManager.Warning($"无法保存截图到数据库: ResultId={CurrentReport.ResultId}, RouteId={CurrentReport.RouteId}");
}
}
catch (Exception dbEx)
{
LogManager.Error($"保存新截图到数据库失败: {dbEx.Message}");
}
OnPropertyChanged(nameof(HasScreenshots));
OnPropertyChanged(nameof(ScreenshotCount));
LogManager.Info($"已添加新截图,当前共 {Screenshots.Count} 张");
}
}
catch (Exception ex)
{
LogManager.Error($"添加截图失败: {ex.Message}");
System.Windows.MessageBox.Show($"添加截图失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
/// <summary>
/// 检查是否可以添加截图
/// </summary>
private bool CanExecuteAddScreenshot()
{
return CurrentReport != null;
}
/// <summary>
/// 执行截图设置 - 打开对话框修改截图参数
/// </summary>
private void ExecuteScreenshotSettings()
{
try
{
var dialog = new GenerateNavigationMapDialog();
dialog.Title = "截图设置";
// 设置当前值,保留当前报告窗口的设置
dialog.SetInitialValues(ScreenshotWidthSetting, ScreenshotHeightSetting, ScreenshotFormatSetting);
// 尝试设置 Owner如果主窗口已显示
var mainWindow = System.Windows.Application.Current.MainWindow;
if (mainWindow != null && mainWindow.IsLoaded)
{
@ -997,77 +1274,91 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (dialog.ShowDialog() == true)
{
// 调用 PathHelper 生成截图
string screenshotPath = PathHelper.GenerateSceneScreenshot(
CurrentReport.PathName ?? "collision",
dialog.ImageWidth,
dialog.ImageHeight,
dialog.ImageFormat,
"collision"
);
// 更新设置
ScreenshotWidthSetting = dialog.ImageWidth;
ScreenshotHeightSetting = dialog.ImageHeight;
ScreenshotFormatSetting = dialog.ImageFormat;
if (screenshotPath != null)
{
// 更新报告数据
CurrentReport.ScreenshotPath = screenshotPath;
CurrentReport.ScreenshotFormat = PathHelper.ImageFormatToExtension(dialog.ImageFormat).ToUpper();
CurrentReport.ScreenshotWidth = dialog.ImageWidth;
CurrentReport.ScreenshotHeight = dialog.ImageHeight;
// 保存到数据库
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null && !string.IsNullOrEmpty(CurrentReport.RouteId))
{
pathDatabase.UpdateCollisionReportScreenshot(
CurrentReport.RouteId,
screenshotPath,
CurrentReport.ScreenshotFormat,
dialog.ImageWidth,
dialog.ImageHeight
);
LogManager.Info($"碰撞报告截图已保存到数据库: RouteId={CurrentReport.RouteId}");
}
else
{
LogManager.Warning("无法保存截图到数据库PathDatabase不可用或RouteId为空");
}
}
catch (Exception dbEx)
{
LogManager.Error($"保存截图到数据库失败: {dbEx.Message}");
// 继续执行,不影响截图生成
}
// 通知UI更新
OnPropertyChanged(nameof(CurrentReport));
OnPropertyChanged(nameof(ScreenshotPreviewSource));
// 提示用户
System.Windows.MessageBox.Show(
"截图已更新并保存到数据库。",
"截图更新成功",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
LogManager.Info("碰撞场景截图已更新");
}
LogManager.Info($"截图设置已更新: {ScreenshotWidthSetting}x{ScreenshotHeightSetting}, {ScreenshotFormatSetting}");
}
}
catch (Exception ex)
{
LogManager.Error($"重新截图失败: {ex.Message}");
System.Windows.MessageBox.Show($"重新截图失败: {ex.Message}", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
LogManager.Error($"打开截图设置失败: {ex.Message}");
}
}
/// <summary>
/// 检查是否可以执行重新截图
/// 执行删除截图
/// </summary>
private bool CanExecuteRetakeScreenshot()
private void ExecuteDeleteScreenshot(CollisionReportScreenshot screenshot)
{
return CurrentReport != null;
if (screenshot == null) return;
try
{
var result = System.Windows.MessageBox.Show(
"确定要删除这张截图吗?",
"确认删除",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Question);
if (result != System.Windows.MessageBoxResult.Yes) return;
// 从数据库删除如果知道DatabaseId
if (screenshot.DatabaseId > 0)
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
pathDatabase?.DeleteCollisionReportScreenshot(screenshot.DatabaseId);
LogManager.Debug($"从数据库删除截图: DatabaseId={screenshot.DatabaseId}");
}
catch (Exception dbEx)
{
LogManager.Error($"从数据库删除截图失败: {dbEx.Message}");
}
}
// 从列表移除
Screenshots.Remove(screenshot);
// 从报告数据移除
CurrentReport.Screenshots?.Remove(screenshot);
// 更新选中项
if (SelectedScreenshot == screenshot)
{
SelectedScreenshot = Screenshots.FirstOrDefault();
}
// 重新排序并更新数据库
for (int i = 0; i < Screenshots.Count; i++)
{
Screenshots[i].SortOrder = i;
}
OnPropertyChanged(nameof(HasScreenshots));
OnPropertyChanged(nameof(ScreenshotCount));
LogManager.Info($"已删除截图,剩余 {Screenshots.Count} 张");
}
catch (Exception ex)
{
LogManager.Error($"删除截图失败: {ex.Message}");
}
}
/// <summary>
/// 执行选择截图
/// </summary>
private void ExecuteSelectScreenshot(CollisionReportScreenshot screenshot)
{
if (screenshot != null)
{
SelectedScreenshot = screenshot;
LogManager.Debug($"选中截图: {screenshot.FilePath}");
}
}
#endregion

View File

@ -2784,6 +2784,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
if (PathPointRenderPlugin.Instance != null)
{
// 检查检测动画页签是否选择了物体或使用了虚拟车辆,如果有,保持使用对应的尺寸而不是重置为默认车辆参数
var animationVm = AnimationControlViewModel.Instance;
if (animationVm != null && animationVm.HasSelectedAnimatedObject)
{
// 检测动画页签已选择物体(实际物体或虚拟车辆),跳过同步,保留已设置的通行空间
LogManager.Debug("[车辆参数同步] 检测动画页签已选择动画对象,跳过同步以保留当前通行空间尺寸");
return;
}
// 根据路径类型计算通行空间尺寸
double passageAcrossPath, passageNormalToPath, passageAlongPath;
PathType pathType = SelectedPathRoute?.PathType ?? PathType.Ground;

View File

@ -21,8 +21,8 @@ NavisworksTransport 碰撞检测报告对话框 - 采用与主界面一致的Nav
Width="900"
ResizeMode="CanResize"
WindowStartupLocation="CenterOwner"
MinHeight="900"
MinWidth="700">
MinHeight="800"
MinWidth="600">
<Window.Resources>
<!-- 引用共享的Navisworks 2026样式资源 -->
@ -36,6 +36,8 @@ NavisworksTransport 碰撞检测报告对话框 - 采用与主界面一致的Nav
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<local:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<local:CountToVisibilityConverter x:Key="CountToVisibilityConverter"/>
<local:PathToImageConverter x:Key="PathToImageConverter"/>
<local:EqualityToBooleanConverter x:Key="EqualityToBooleanConverter"/>
<!-- 碰撞报告专用样式 -->
<Style x:Key="StatisticItemStyle" TargetType="Border">
@ -270,29 +272,158 @@ NavisworksTransport 碰撞检测报告对话框 - 采用与主界面一致的Nav
</UniformGrid>
<!-- 碰撞场景截图区域 -->
<Label Content="碰撞场景截图" Style="{StaticResource SectionHeaderStyle}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="碰撞场景截图" Style="{StaticResource SectionHeaderStyle}" Grid.Column="0"/>
<TextBlock Grid.Column="1"
Text="{Binding ScreenshotCount, StringFormat='{}{0} 张'}"
VerticalAlignment="Center"
Foreground="#FF666666"
FontSize="11"
Margin="0,0,10,0"/>
</Grid>
<GroupBox Margin="0,0,0,10" BorderBrush="#FFD4E7FF" BorderThickness="1">
<Grid>
<!-- 截图预览 -->
<StackPanel>
<!-- 主图显示区 -->
<Border BorderBrush="Gray"
BorderThickness="1"
Background="Black"
MinHeight="300">
<Image Source="{Binding ScreenshotPreviewSource}"
Stretch="Uniform"
RenderOptions.BitmapScalingMode="HighQuality">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding ScreenshotPreviewSource}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
MinHeight="300"
Margin="0,0,0,10">
<Grid>
<Image Source="{Binding ScreenshotPreviewSource}"
Stretch="Uniform"
RenderOptions.BitmapScalingMode="HighQuality">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding ScreenshotPreviewSource}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<!-- 无截图提示 -->
<TextBlock Text="暂无截图"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
FontSize="14"
Visibility="{Binding HasScreenshots, Converter={StaticResource InverseBooleanToVisibilityConverter}}"/>
</Grid>
</Border>
</Grid>
<!-- 缩略图列表 -->
<Border Background="#FFF5F5F5"
BorderBrush="#FFE0E0E0"
BorderThickness="1"
Padding="5"
Visibility="{Binding HasScreenshots, Converter={StaticResource BooleanToVisibilityConverter}}">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<StackPanel Orientation="Horizontal">
<!-- 缩略图列表 -->
<ItemsControl x:Name="ThumbnailsItemsControl"
ItemsSource="{Binding Screenshots}"
VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="60"
Height="45"
Margin="2"
Cursor="Hand"
MouseLeftButtonDown="OnThumbnailClick">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<!-- 选中时显示1px蓝色边框 -->
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource EqualityToBooleanConverter}">
<Binding />
<Binding ElementName="ThumbnailsItemsControl" Path="DataContext.SelectedScreenshot"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="BorderBrush" Value="#FF2B579A"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Image Source="{Binding FilePath, Converter={StaticResource PathToImageConverter}}"
Stretch="UniformToFill"
RenderOptions.BitmapScalingMode="HighQuality">
<Image.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock Text="{Binding Description, TargetNullValue='无描述'}" FontWeight="SemiBold"/>
<TextBlock Text="{Binding CaptureTime, StringFormat='截图时间: {0:yyyy-MM-dd HH:mm}'}" FontSize="11"/>
<TextBlock FontSize="11">
<TextBlock.Text>
<MultiBinding StringFormat="尺寸: {0} x {1}">
<Binding Path="Width"/>
<Binding Path="Height"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</ToolTip>
</Image.ToolTip>
</Image>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- 加号按钮 -->
<Border Width="45"
Height="45"
Margin="2,2,5,2"
Background="#FFE8F4FF"
BorderBrush="#FF2B579A"
BorderThickness="1"
Cursor="Hand"
MouseLeftButtonDown="OnAddScreenshotClick">
<TextBlock Text="+"
FontSize="24"
Foreground="#FF2B579A"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Light"/>
</Border>
<!-- 减号按钮 - 删除当前选中的截图 -->
<Border Width="45"
Height="45"
Margin="2"
Background="#FFFFEBEE"
BorderBrush="#FFE57373"
BorderThickness="1"
Cursor="Hand"
Visibility="{Binding HasScreenshots, Converter={StaticResource BooleanToVisibilityConverter}}"
MouseLeftButtonDown="OnDeleteScreenshotClick">
<TextBlock Text="-"
FontSize="24"
Foreground="#FFD32F2F"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Light"/>
</Border>
</StackPanel>
</ScrollViewer>
</Border>
</StackPanel>
</GroupBox>
<!-- 运动构件信息 -->
@ -400,17 +531,17 @@ NavisworksTransport 碰撞检测报告对话框 - 采用与主界面一致的Nav
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Content="截图设置"
Command="{Binding ScreenshotSettingsCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Margin="0,0,10,0"/>
<Button Content="导出报告"
Command="{Binding ExportReportCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Margin="0,0,10,0"
Visibility="{Binding HasCollisions, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Content="重新截图"
Command="{Binding RetakeScreenshotCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Margin="0,0,10,0"/>
<Button Content="关闭"
Click="CloseButton_Click"
Style="{StaticResource ActionButtonStyle}"/>

View File

@ -1,7 +1,10 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using NavisworksTransport.UI.WPF.ViewModels;
using NavisworksTransport.Commands;
using CollisionReportScreenshot = NavisworksTransport.Commands.CollisionReportScreenshot;
namespace NavisworksTransport.UI.WPF.Views
{
@ -86,11 +89,15 @@ namespace NavisworksTransport.UI.WPF.Views
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">事件参数</param>
private void CloseButton_Click(object sender, RoutedEventArgs e)
private async void CloseButton_Click(object sender, RoutedEventArgs e)
{
try
{
LogManager.Info("用户关闭碰撞报告对话框");
LogManager.Info("用户关闭碰撞报告对话框,正在自动导出报告...");
// 关闭前自动导出报告
await AutoExportReportAsync();
this.Close();
}
catch (Exception ex)
@ -99,6 +106,24 @@ namespace NavisworksTransport.UI.WPF.Views
}
}
/// <summary>
/// 自动导出报告
/// </summary>
private async System.Threading.Tasks.Task AutoExportReportAsync()
{
try
{
if (_viewModel != null)
{
await _viewModel.AutoExportReportAsync();
}
}
catch (Exception ex)
{
LogManager.Error($"自动导出报告失败: {ex.Message}");
}
}
/// <summary>
/// 窗口关闭事件
/// </summary>
@ -251,6 +276,72 @@ namespace NavisworksTransport.UI.WPF.Views
#endregion
#region
/// <summary>
/// 缩略图点击事件 - 切换主图显示
/// </summary>
private void OnThumbnailClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
try
{
if (sender is Border border && border.DataContext is CollisionReportScreenshot screenshot)
{
_viewModel?.SelectScreenshotCommand?.Execute(screenshot);
LogManager.Debug($"切换截图: {screenshot.FilePath}");
}
}
catch (Exception ex)
{
LogManager.Error($"切换截图失败: {ex.Message}");
}
}
/// <summary>
/// 添加截图按钮点击事件
/// </summary>
private void OnAddScreenshotClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
try
{
_viewModel?.AddScreenshotCommand?.Execute(null);
}
catch (Exception ex)
{
LogManager.Error($"添加截图失败: {ex.Message}");
}
}
/// <summary>
/// 删除截图按钮点击事件 - 删除当前选中的截图
/// </summary>
private void OnDeleteScreenshotClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
try
{
// 删除当前选中的截图
if (_viewModel?.SelectedScreenshot != null)
{
_viewModel?.DeleteScreenshotCommand?.Execute(_viewModel.SelectedScreenshot);
}
else if (_viewModel?.Screenshots?.Count > 0)
{
// 如果没有选中,删除最后一张
var lastScreenshot = _viewModel.Screenshots.LastOrDefault();
if (lastScreenshot != null)
{
_viewModel?.DeleteScreenshotCommand?.Execute(lastScreenshot);
}
}
}
catch (Exception ex)
{
LogManager.Error($"删除截图失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>

View File

@ -44,6 +44,68 @@ namespace NavisworksTransport.UI.WPF.Views
Loaded += (sender, e) => InitializeDefaults();
}
/// <summary>
/// 设置初始值(用于保留当前报告窗口的截图设置)
/// </summary>
public void SetInitialValues(int width, int height, ImageFormat format)
{
try
{
ImageWidth = width;
ImageHeight = height;
ImageFormat = format;
// 更新UI控件
if (WidthTextBox != null)
WidthTextBox.Text = width.ToString();
if (HeightTextBox != null)
HeightTextBox.Text = height.ToString();
// 设置格式下拉框
if (FormatComboBox != null)
{
string formatName = format.ToString().ToUpper();
for (int i = 0; i < FormatComboBox.Items.Count; i++)
{
var item = FormatComboBox.Items[i] as ComboBoxItem;
if (item?.Tag?.ToString()?.ToUpper() == formatName)
{
FormatComboBox.SelectedIndex = i;
break;
}
}
}
// 设置预设尺寸下拉框
if (PresetSizeComboBox != null)
{
string sizeTag = $"{width}x{height}";
bool found = false;
for (int i = 0; i < PresetSizeComboBox.Items.Count; i++)
{
var item = PresetSizeComboBox.Items[i] as ComboBoxItem;
if (item?.Tag?.ToString() == sizeTag)
{
PresetSizeComboBox.SelectedIndex = i;
found = true;
break;
}
}
// 如果没有匹配预设,选择自定义
if (!found && PresetSizeComboBox.Items.Count > 0)
{
PresetSizeComboBox.SelectedIndex = PresetSizeComboBox.Items.Count - 1; // 假设最后是自定义
}
}
LogManager.Debug($"截图对话框初始值已设置: {width}x{height}, {format}");
}
catch (Exception ex)
{
LogManager.Error($"设置截图对话框初始值失败: {ex.Message}", ex);
}
}
/// <summary>
/// 初始化默认值
/// </summary>
@ -51,19 +113,24 @@ namespace NavisworksTransport.UI.WPF.Views
{
try
{
// 设置默认值
ImageWidth = 1920;
ImageHeight = 1080;
ImageFormat = ImageFormat.Png;
// 设置默认值(仅在未通过 SetInitialValues 设置时)
if (ImageWidth == 0) ImageWidth = 1920;
if (ImageHeight == 0) ImageHeight = 1080;
if (ImageFormat == null) ImageFormat = ImageFormat.Png;
RenderStyle = ImageGenerationStyle.ScenePlusOverlay;
IsConfirmed = false;
// 设置默认选择
FormatComboBox.SelectedIndex = 1; // PNG
PresetSizeComboBox.SelectedIndex = 2; // 1920x1080 (因为添加了4K选项索引变为2)
// 设置UI控件值
if (WidthTextBox != null && string.IsNullOrEmpty(WidthTextBox.Text))
WidthTextBox.Text = ImageWidth.ToString();
if (HeightTextBox != null && string.IsNullOrEmpty(HeightTextBox.Text))
HeightTextBox.Text = ImageHeight.ToString();
WidthTextBox.Text = ImageWidth.ToString();
HeightTextBox.Text = ImageHeight.ToString();
// 设置默认选择(仅在未设置时)
if (FormatComboBox != null && FormatComboBox.SelectedIndex < 0)
FormatComboBox.SelectedIndex = 1; // PNG
if (PresetSizeComboBox != null && PresetSizeComboBox.SelectedIndex < 0)
PresetSizeComboBox.SelectedIndex = 2; // 1920x1080
}
catch (Exception ex)
{

View File

@ -99,23 +99,59 @@ namespace NavisworksTransport.Utils
html.AppendLine("</div>");
html.AppendLine("</div>");
// 碰撞场景截图
if (!string.IsNullOrEmpty(report.ScreenshotPath) && File.Exists(report.ScreenshotPath))
// 碰撞场景截图(支持多张)
var validScreenshots = report.Screenshots?.Where(s => !string.IsNullOrEmpty(s.FilePath) && File.Exists(s.FilePath)).ToList();
if (validScreenshots?.Count > 0 || (!string.IsNullOrEmpty(report.ScreenshotPath) && File.Exists(report.ScreenshotPath)))
{
html.AppendLine("<h2>碰撞场景截图</h2>");
html.AppendLine("<div class='screenshot-section'>");
// 使用 PathHelper 计算相对路径
string relativePath = PathHelper.GetRelativePath(htmlFilePath, report.ScreenshotPath);
html.AppendLine("<div class='screenshot-container'>");
html.AppendLine($"<img src=\"{relativePath}\" alt=\"碰撞场景截图\" class=\"screenshot-image\"/>");
html.AppendLine("<div class='screenshot-info'>");
html.AppendLine($"<p>分辨率: {report.ScreenshotWidth} x {report.ScreenshotHeight}</p>");
html.AppendLine($"<p>格式: {report.ScreenshotFormat}</p>");
html.AppendLine("</div>");
html.AppendLine("</div>");
html.AppendLine("</div>");
// 多张截图样式
if (validScreenshots?.Count > 1)
{
html.AppendLine("<style>");
html.AppendLine(".screenshot-gallery { display: flex; flex-wrap: wrap; gap: 15px; margin: 15px 0; }");
html.AppendLine(".screenshot-item { flex: 0 0 calc(50% - 10px); background: white; padding: 10px; border-radius: 6px; border: 1px solid #ddd; box-sizing: border-box; }");
html.AppendLine(".screenshot-item:nth-child(odd) { flex: 0 0 100%; }"); // 第一张图占满宽度
html.AppendLine(".screenshot-thumb { width: 100%; height: auto; border-radius: 4px; cursor: pointer; transition: transform 0.2s; }");
html.AppendLine(".screenshot-thumb:hover { transform: scale(1.02); }");
html.AppendLine(".screenshot-desc { margin-top: 8px; font-size: 12px; color: #666; text-align: center; }");
html.AppendLine(".screenshot-meta { font-size: 11px; color: #999; text-align: center; margin-top: 4px; }");
html.AppendLine("</style>");
html.AppendLine("<div class='screenshot-gallery'>");
int index = 1;
foreach (var screenshot in validScreenshots.OrderBy(s => s.SortOrder))
{
string relativePath = PathHelper.GetRelativePath(htmlFilePath, screenshot.FilePath);
html.AppendLine("<div class='screenshot-item'>");
html.AppendLine($"<img src=\"{relativePath}\" alt=\"截图 {index}\" class=\"screenshot-thumb\" onclick=\"window.open(this.src)\"/>");
if (!string.IsNullOrEmpty(screenshot.Description))
{
html.AppendLine($"<div class='screenshot-desc'>{screenshot.Description}</div>");
}
html.AppendLine($"<div class='screenshot-meta'>截图 {index} • {screenshot.Width}x{screenshot.Height} • {screenshot.CaptureTime:yyyy-MM-dd HH:mm}</div>");
html.AppendLine("</div>");
index++;
}
html.AppendLine("</div>");
}
else
{
// 单张截图样式(兼容旧版本)
string screenshotPath = validScreenshots?.FirstOrDefault()?.FilePath ?? report.ScreenshotPath;
var screenshot = validScreenshots?.FirstOrDefault();
html.AppendLine("<div class='screenshot-section'>");
string relativePath = PathHelper.GetRelativePath(htmlFilePath, screenshotPath);
html.AppendLine("<div class='screenshot-container'>");
html.AppendLine($"<img src=\"{relativePath}\" alt=\"碰撞场景截图\" class=\"screenshot-image\"/>");
html.AppendLine("<div class='screenshot-info'>");
html.AppendLine($"<p>分辨率: {screenshot?.Width ?? report.ScreenshotWidth} x {screenshot?.Height ?? report.ScreenshotHeight}</p>");
html.AppendLine($"<p>格式: {screenshot?.Format ?? report.ScreenshotFormat}</p>");
html.AppendLine("</div>");
html.AppendLine("</div>");
html.AppendLine("</div>");
}
}
// 运动构件信息