diff --git a/TransportPlugin.csproj b/TransportPlugin.csproj
index 4bbb6e9..a41f577 100644
--- a/TransportPlugin.csproj
+++ b/TransportPlugin.csproj
@@ -165,6 +165,7 @@
+
@@ -522,6 +523,18 @@
PreserveNewest
resources\TransportPlugin.name.txt
+
+ PreserveNewest
+ TransportRibbon.xaml
+
+
+ PreserveNewest
+ TransportRibbon_16.png
+
+
+ PreserveNewest
+ TransportRibbon_32.png
+
PreserveNewest
diff --git a/deploy-plugin.bat b/deploy-plugin.bat
index 0c5a3cd..789d687 100644
--- a/deploy-plugin.bat
+++ b/deploy-plugin.bat
@@ -16,6 +16,11 @@ powershell -NoProfile -ExecutionPolicy Bypass -Command ^
" 'System.Data.SQLite.dll'," ^
" 'Tomlyn.dll'" ^
");" ^
+ "$deployRootFiles = @(" ^
+ " 'TransportRibbon.xaml'," ^
+ " 'TransportRibbon_16.png'," ^
+ " 'TransportRibbon_32.png'" ^
+ ");" ^
"$stalePluginFiles = @(" ^
" 'Autodesk.Navisworks.Api.dll'," ^
" 'NavisworksTransport.UnitTests.dll'," ^
@@ -63,6 +68,17 @@ powershell -NoProfile -ExecutionPolicy Bypass -Command ^
" if (-not (Test-Path $sourceDll)) { throw ('Deployment source file missing: ' + $sourceDll) }" ^
" Copy-Item $sourceDll $targetDir -Force;" ^
"}" ^
+ "foreach ($rootFileName in $deployRootFiles) {" ^
+ " $sourceRootFile = Join-Path $buildDir $rootFileName;" ^
+ " if (Test-Path $sourceRootFile) {" ^
+ " Copy-Item $sourceRootFile $targetDir -Force;" ^
+ " foreach ($locale in @('en-US','zh-CN')) {" ^
+ " $localeDir = Join-Path $targetDir $locale;" ^
+ " New-Item -ItemType Directory -Force -Path $localeDir | Out-Null;" ^
+ " Copy-Item $sourceRootFile $localeDir -Force;" ^
+ " }" ^
+ " }" ^
+ "}" ^
"if (Test-Path (Join-Path $buildDir 'resources')) {" ^
" New-Item -ItemType Directory -Force -Path (Join-Path $targetDir 'resources') | Out-Null;" ^
" Copy-Item (Join-Path $buildDir 'resources\\*') (Join-Path $targetDir 'resources') -Recurse -Force;" ^
@@ -70,6 +86,10 @@ powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"" ^
"$sourceFiles = @();" ^
"foreach ($dllName in $deployDllNames) { $sourceFiles += Get-Item (Join-Path $buildDir $dllName) }" ^
+ "foreach ($rootFileName in $deployRootFiles) {" ^
+ " $sourceRootFile = Join-Path $buildDir $rootFileName;" ^
+ " if (Test-Path $sourceRootFile) { $sourceFiles += Get-Item $sourceRootFile }" ^
+ "}" ^
"if (Test-Path (Join-Path $buildDir 'resources')) {" ^
" $sourceFiles += Get-ChildItem (Join-Path $buildDir 'resources') -File;" ^
"}" ^
@@ -82,6 +102,17 @@ powershell -NoProfile -ExecutionPolicy Bypass -Command ^
" throw ('Deployment verification failed for ' + $sourceFile.Name + ': source=' + $sourceFile.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff') + ' (' + $sourceFile.Length + '), target=' + $targetInfo.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff') + ' (' + $targetInfo.Length + ')');" ^
" }" ^
" Write-Host ('Verified ' + $sourceFile.Name + ': ' + $targetInfo.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff'));" ^
+ " if ($deployRootFiles -contains $sourceFile.Name) {" ^
+ " foreach ($locale in @('en-US','zh-CN')) {" ^
+ " $localeTargetFile = Join-Path (Join-Path $targetDir $locale) $sourceFile.Name;" ^
+ " if (-not (Test-Path $localeTargetFile)) { throw ('Deployment verification failed: missing locale target file ' + $localeTargetFile) }" ^
+ " $localeTargetInfo = Get-Item $localeTargetFile;" ^
+ " if ($sourceFile.Length -ne $localeTargetInfo.Length -or $sourceFile.LastWriteTime -ne $localeTargetInfo.LastWriteTime) {" ^
+ " throw ('Deployment verification failed for locale copy ' + $localeTargetFile);" ^
+ " }" ^
+ " Write-Host ('Verified ' + $locale + '\\' + $sourceFile.Name + ': ' + $localeTargetInfo.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff'));" ^
+ " }" ^
+ " }" ^
"}" ^
"" ^
"Write-Host 'Plugin deployed successfully!';"
diff --git a/resources/TransportRibbon.xaml b/resources/TransportRibbon.xaml
new file mode 100644
index 0000000..d0188d7
--- /dev/null
+++ b/resources/TransportRibbon.xaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/TransportRibbon_16.png b/resources/TransportRibbon_16.png
new file mode 100644
index 0000000..7d75f30
Binary files /dev/null and b/resources/TransportRibbon_16.png differ
diff --git a/resources/TransportRibbon_32.png b/resources/TransportRibbon_32.png
new file mode 100644
index 0000000..5118b17
Binary files /dev/null and b/resources/TransportRibbon_32.png differ
diff --git a/src/Commands/TransportRibbonHandler.cs b/src/Commands/TransportRibbonHandler.cs
new file mode 100644
index 0000000..95278f6
--- /dev/null
+++ b/src/Commands/TransportRibbonHandler.cs
@@ -0,0 +1,79 @@
+using System;
+using Autodesk.Navisworks.Api.Plugins;
+using NavisworksTransport.Utils;
+
+namespace NavisworksTransport.Commands
+{
+ [Plugin("TransportRibbonHandler", "NavisworksTransport",
+ DisplayName = "物流路径规划工具栏",
+ ToolTip = "物流路径规划工具栏命令")]
+ [RibbonLayout("TransportRibbon.xaml")]
+ [RibbonTab("ID_TransportTab", DisplayName = "物流路径规划")]
+ [Command("OpenLogisticsPanel",
+ DisplayName = "物流路径规划",
+ ToolTip = "打开物流路径规划面板",
+ LoadForCanExecute = true)]
+ public class TransportRibbonHandler : CommandHandlerPlugin
+ {
+ private const string MainDockPanePluginKey = "NavisworksTransport.Main.Tian";
+ private const string OpenLogisticsPanelCommandId = "OpenLogisticsPanel";
+ private const string TransportRibbonTabId = "ID_TransportTab";
+
+ public override int ExecuteCommand(string commandId, params string[] parameters)
+ {
+ try
+ {
+ if (!string.Equals(commandId, OpenLogisticsPanelCommandId, StringComparison.OrdinalIgnoreCase))
+ {
+ LogManager.Warning($"[Ribbon] 未知命令: {commandId}");
+ return 1;
+ }
+
+ var pluginRecord = Autodesk.Navisworks.Api.Application.Plugins.FindPlugin(MainDockPanePluginKey) as DockPanePluginRecord;
+ if (pluginRecord == null)
+ {
+ throw new InvalidOperationException($"未找到物流路径规划面板插件: {MainDockPanePluginKey}");
+ }
+
+ var dockPane = pluginRecord.LoadPlugin() as DockPanePlugin;
+ if (dockPane == null)
+ {
+ throw new InvalidOperationException("物流路径规划面板加载失败");
+ }
+
+ dockPane.Visible = true;
+ dockPane.ActivatePane();
+ LogManager.Info("[Ribbon] 已通过正式工具栏打开物流路径规划面板");
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[Ribbon] 执行命令失败: {ex.Message}", ex);
+ return 1;
+ }
+ }
+
+ public override CommandState CanExecuteCommand(string commandId)
+ {
+ if (string.Equals(commandId, OpenLogisticsPanelCommandId, StringComparison.OrdinalIgnoreCase))
+ {
+ return new CommandState
+ {
+ IsEnabled = true,
+ IsVisible = true
+ };
+ }
+
+ return new CommandState
+ {
+ IsEnabled = false,
+ IsVisible = false
+ };
+ }
+
+ public override bool CanExecuteRibbonTab(string ribbonTabId)
+ {
+ return string.Equals(ribbonTabId, TransportRibbonTabId, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}