# UI 层诊断 (STA): 反射创建主窗口, 对每个系列执行 LoadSeries, # 校验选项块/下拉的选项数、可选态、以及选中后零件号实时条的输出。 # 用法: powershell -STA -NoProfile -ExecutionPolicy Bypass -File tools\ui-diagnose.ps1 param( [string]$Exe = (Join-Path $PSScriptRoot '..\bin\OnebotCatalog.exe'), [string]$Opc = (Join-Path $PSScriptRoot '..\sample\OnebotCatalog_2026.08.opc'), [switch]$Verbose ) $ErrorActionPreference = 'Stop' Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName PresentationCore Add-Type -AssemblyName WindowsBase $asm = [Reflection.Assembly]::LoadFrom($Exe) $pkgType = $asm.GetType('Ounibo.Catalog.Core.OpcPackage') $pkg = $pkgType.GetMethod('Open').Invoke($null, @($Opc)) $mwType = $asm.GetType('Ounibo.Catalog.App.MainWindow') $mw = [Activator]::CreateInstance($mwType, @($pkg, $false)) $np = [Reflection.BindingFlags]'NonPublic,Instance' $ls = $mwType.GetMethod('LoadSeries', $np) $boxesF = $mwType.GetField('_boxes', $np) # range 参数的可编辑下拉 $tilesF = $mwType.GetField('_tiles', $np) # 枚举参数的选项块 $selF = $mwType.GetField('_sel', $np) $refreshM = $mwType.GetMethod('RefreshOptions', $np) $skuCodeF = $mwType.GetField('_skuCode', $np) $skuHintF = $mwType.GetField('_skuHint', $np) function Get-InlinesText($tb) { if ($tb -eq $null) { return '' } $s = '' foreach ($r in $tb.Inlines) { $s += $r.Text } return $s } $fail = 0 foreach ($s in $pkg.Catalog.series) { try { $ls.Invoke($mw, @($s)) | Out-Null $tiles = $tilesF.GetValue($mw) $boxes = $boxesF.GetValue($mw) # 每个枚举维度: 选项块总数 / 可选数 (与参数定义值域比对) $parts = @() $bad = @() foreach ($p in $s.parameters) { if ($tiles.ContainsKey($p.code)) { $list = $tiles[$p.code] $usable = 0 foreach ($t in $list) { $f = $t.GetType().GetField('_usable', $np) if ($f -ne $null -and $f.GetValue($t)) { $usable++ } } $parts += ($p.code + '=' + $list.Count) # 初始状态: 每个维度至少有一个可选值 (否则用户无从下手) if ($usable -eq 0) { $bad += ($p.code + ':无可选值') } if ($list.Count -eq 0) { $bad += ($p.code + ':选项为空') } } elseif ($boxes.ContainsKey($p.code)) { $cb = $boxes[$p.code] $c = 0 if ($cb.ItemsSource -ne $null) { $c = $cb.Items.Count } $parts += ($p.code + '=range:' + $c) if ($c -eq 0) { $bad += ($p.code + ':range无档位') } } } # 逐个参数按第一个可用值选满, 检查零件号条是否产出非空前缀 $sel = $selF.GetValue($mw) $sel.Clear() $v0 = $s.variants[0] $sku = '' if ($v0 -ne $null) { foreach ($p in $s.parameters) { $val = $null if ($v0.params.ContainsKey($p.code)) { $val = $v0.params[$p.code] } if ($val -ne $null) { $sel[$p.code] = $val } } $refreshM.Invoke($mw, @()) | Out-Null $sku = Get-InlinesText ($skuCodeF.GetValue($mw)) $hint = $skuHintF.GetValue($mw).Text if ([string]::IsNullOrWhiteSpace($sku)) { $bad += '零件号条为空' } } $flag = if ($bad.Count -gt 0) { ' !! ' + ($bad -join '; ') } else { '' } if ($bad.Count -gt 0) { $fail++ } Write-Output ("系列 " + $s.code.PadRight(6) + " 选项块 " + ($parts -join ' ') + $flag) if ($Verbose -and $sku -ne '') { Write-Output (" 零件号: [" + $sku + "] " + $hint) } } catch { $fail++ $m = $_.Exception.Message if ($_.Exception.InnerException -ne $null) { $m += ' / ' + $_.Exception.InnerException.Message } Write-Output ("系列 " + $s.code + " 异常: " + $m) } } Write-Output "" Write-Output ("检查完成: " + $pkg.Catalog.series.Count + " 个系列, 问题 " + $fail + " 个") $pkg.Dispose() if ($fail -gt 0) { exit 1 }