# 用无头 Chrome 截图,做视觉验收 # 用法: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\shots.ps1 -Base http://127.0.0.1:18080 param( [string]$Base = 'http://127.0.0.1:18080', [string]$User = 'admin', [string]$Pass = 'daili123', [string]$OutDir = '' ) $ErrorActionPreference = 'Stop' $root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) if (-not $OutDir) { $OutDir = Join-Path $root 'shots' } New-Item -ItemType Directory -Force -Path $OutDir | Out-Null $chrome = 'C:\Program Files\Google\Chrome\Application\chrome.exe' if (-not (Test-Path $chrome)) { throw '找不到 Chrome' } # 1) 登录拿会话 cookie $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession Invoke-WebRequest -Uri "$Base/login" -WebSession $session -UseBasicParsing | Out-Null Invoke-WebRequest -Uri "$Base/login" -Method POST -WebSession $session -UseBasicParsing -Body @{ username = $User; password = $Pass; next = '/calendar' } | Out-Null $cookie = ($session.Cookies.GetCookies($Base) | Where-Object { $_.Name -eq 'daili_sid' } | Select-Object -First 1) if (-not $cookie) { throw '登录失败,拿不到会话' } $token = $cookie.Value # 2) 拿一个订阅 ID 用于「追加面板」截图 $custID = '' $subID = '' $r = Invoke-WebRequest -Uri "$Base/customers?q=7001" -WebSession $session -UseBasicParsing if ($r.Content -match '/customers/(\d+)') { $custID = $matches[1] $r2 = Invoke-WebRequest -Uri "$Base/customers/$custID" -WebSession $session -UseBasicParsing if ($r2.Content -match 'id="sub-(\d+)"') { $subID = $matches[1] } } # 3) 启动 Chrome $profile = Join-Path $root ('.chrome-profile-' + (Get-Date).ToString('HHmmss')) $chromeArgs = @( '--headless=new', '--disable-gpu', '--hide-scrollbars', '--no-sandbox', '--no-first-run', '--no-default-browser-check', '--disable-extensions', '--disable-crash-reporter', '--disable-breakpad', '--force-device-scale-factor=1', '--remote-debugging-port=9222', "--user-data-dir=$profile", '--window-size=1440,1000', 'about:blank' ) $chromeProc = Start-Process -FilePath $chrome -ArgumentList $chromeArgs -PassThru $targets = $null for ($i = 0; $i -lt 20; $i++) { Start-Sleep -Milliseconds 500 try { $targets = Invoke-RestMethod 'http://127.0.0.1:9222/json' if ($targets) { break } } catch { } } if (-not $targets) { Stop-Process -Id $chromeProc.Id -Force -ErrorAction SilentlyContinue throw 'Chrome 调试端口未就绪' } $page = $targets | Where-Object { $_.type -eq 'page' } | Select-Object -First 1 if (-not $page) { throw '拿不到调试目标' } $script:ws = New-Object System.Net.WebSockets.ClientWebSocket $script:ws.ConnectAsync([Uri]$page.webSocketDebuggerUrl, [Threading.CancellationToken]::None).Wait() $script:msgId = 0 function Send-CDP { param([string]$Method, $Params) $script:msgId++ $payload = @{ id = $script:msgId; method = $Method; params = $Params } | ConvertTo-Json -Depth 12 -Compress $bytes = [Text.Encoding]::UTF8.GetBytes($payload) $seg = New-Object 'System.ArraySegment[byte]' -ArgumentList @(, $bytes) $script:ws.SendAsync($seg, [Net.WebSockets.WebSocketMessageType]::Text, $true, [Threading.CancellationToken]::None).Wait() while ($true) { $ms = New-Object System.IO.MemoryStream do { $buf = New-Object byte[] 131072 $segR = New-Object 'System.ArraySegment[byte]' -ArgumentList @(, $buf) $res = $script:ws.ReceiveAsync($segR, [Threading.CancellationToken]::None).Result $ms.Write($buf, 0, $res.Count) } while (-not $res.EndOfMessage) $text = [Text.Encoding]::UTF8.GetString($ms.ToArray()) $msg = $text | ConvertFrom-Json if ($msg.id -eq $script:msgId) { return $msg } } } function Shoot { param([string]$Name, [string]$Path, [int]$Width = 1440, [int]$Height = 1000, [bool]$SetCookie = $true) Send-CDP 'Emulation.setDeviceMetricsOverride' @{ width = $Width; height = $Height; deviceScaleFactor = 1; mobile = ($Width -lt 600) } | Out-Null if ($SetCookie) { Send-CDP 'Network.setCookie' @{ name = 'daili_sid'; value = $token; domain = '127.0.0.1'; path = '/'; httpOnly = $true } | Out-Null } Send-CDP 'Page.navigate' @{ url = "$Base$Path" } | Out-Null Start-Sleep -Milliseconds 900 $shot = Send-CDP 'Page.captureScreenshot' @{ format = 'png'; captureBeyondViewport = $false } $file = Join-Path $OutDir "$Name.png" [System.IO.File]::WriteAllBytes($file, [Convert]::FromBase64String($shot.result.data)) Write-Host (" shot {0,-16} <- {1}{2}" -f $Name, $Base, $Path) -ForegroundColor DarkGray } Send-CDP 'Network.enable' @{} | Out-Null Send-CDP 'Page.enable' @{} | Out-Null Shoot 'login' '/login' 1440 900 $false Shoot 'calendar' '/calendar' 1440 1000 Shoot 'customers' '/customers' 1440 900 if ($custID) { Shoot 'customer-detail' "/customers/$custID" 1440 1100 } if ($custID -and $subID) { $today = (Get-Date).ToString('yyyy-MM-dd') Shoot 'extend-panel' ("/customers/{0}?extend={1}#extend" -f $custID, $subID) 1440 1200 } Shoot 'subscriptions' '/subscriptions?state=all' 1440 900 Shoot 'packages' '/packages' 1440 800 Shoot 'package-form' '/packages/new' 1200 940 Shoot 'customer-form' '/customers/new' 1000 900 Shoot 'settings' '/settings' 1440 1000 Shoot 'screen' '/screen' 1600 900 Shoot 'calendar-mobile' '/calendar' 390 844 $script:ws.Dispose() Stop-Process -Id $chromeProc.Id -Force -ErrorAction SilentlyContinue Write-Host '截图完成' -ForegroundColor Green