ux(installer): 双专家审计修复 — P0 取消按钮 + P1 表单优化 + 品牌视觉

基于 UX Auditor + CTO Reviewer 两路并行审计报告的交叉修复:

P0 — 进度窗口逃生通道 (UX F1):
- ControlBox=$true, X 按钮触发 "取消安装?" 确认
- $BWInstallDone 标记: Phase 7 完成后 X 不再弹确认

P1 — 授权码 "跳过" 按钮误导 (UX F6):
- "跳过" → "取消安装", 明确传达后果

P1 — Gitea 空密码静默通过 (UX F10):
- 添加 lblError 红字验证: "用户名和密码不能为空"
- btnOK 改为手动 Click 事件验证, 不再自动 DialogResult

P1 — Gitea 对话框缺少上下文 (UX F11):
- 新增说明: "用于下载 Bookworm 配置文件,由管理员提供"

P2 — 内部术语暴露 (UX F13):
- "crypto-helper.js 缺失" → "Bookworm 启动工具包下载失败" + 3 点排查提示

P2 — 品牌视觉统一 (UX Visual):
- 进度窗口: 白底 + Bookworm 蓝紫(#5865F2) Phase 标签 + 右下角计时器
- TopMost=$false (不遮挡其他窗口)
- ProgressBar Style=Continuous (平滑)
- 全局计时器 $BWStartTime + $BWElapsedLabel "Nm Ns" 实时显示

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
bookworm 2026-04-10 01:03:36 +08:00
parent 7d60911f65
commit 8a6611d96e

View File

@ -61,53 +61,82 @@ $global:BWStatusLabel = $null
$global:BWProgressBar = $null
function Show-ProgressForm {
# 统一品牌色
$brandBlue = [System.Drawing.Color]::FromArgb(88, 101, 242) # Bookworm 蓝紫
$brandDark = [System.Drawing.Color]::FromArgb(30, 31, 46)
$uiFont = "Segoe UI"
$global:BWProgressForm = New-Object System.Windows.Forms.Form
$global:BWProgressForm.Text = "Bookworm Portable Setup"
$global:BWProgressForm.Size = New-Object System.Drawing.Size(520, 200)
$global:BWProgressForm.Text = "Bookworm Portable Setup v1.5.1"
$global:BWProgressForm.Size = New-Object System.Drawing.Size(520, 230)
$global:BWProgressForm.StartPosition = "CenterScreen"
$global:BWProgressForm.FormBorderStyle = "FixedDialog"
$global:BWProgressForm.MaximizeBox = $false
$global:BWProgressForm.MinimizeBox = $false
$global:BWProgressForm.TopMost = $true
$global:BWProgressForm.ControlBox = $false
$global:BWProgressForm.TopMost = $false # P2: 不遮挡其他窗口
$global:BWProgressForm.ControlBox = $true # P0 F1: 允许关闭 (触发确认)
$global:BWProgressForm.BackColor = [System.Drawing.Color]::White
# X 按钮关闭时弹确认
$global:BWProgressForm.Add_FormClosing({
param($s, $e)
if (-not $global:BWInstallDone) {
$r = [System.Windows.Forms.MessageBox]::Show(
"安装尚未完成。`n确定要取消安装吗?",
"取消安装", "YesNo", "Warning")
if ($r -eq "No") { $e.Cancel = $true; return }
Bw-Log "ABORT" "用户手动取消安装"
}
})
$titleLabel = New-Object System.Windows.Forms.Label
$titleLabel.Location = New-Object System.Drawing.Point(20, 18)
$titleLabel.Size = New-Object System.Drawing.Size(480, 24)
$titleLabel.Text = "Bookworm 智能助手 — 自动安装中, 请稍候..."
$titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
$titleLabel.Location = New-Object System.Drawing.Point(20, 16)
$titleLabel.Size = New-Object System.Drawing.Size(480, 26)
$titleLabel.Text = "Bookworm 智能助手 — 自动安装中"
$titleLabel.Font = New-Object System.Drawing.Font($uiFont, 12, [System.Drawing.FontStyle]::Bold)
$titleLabel.ForeColor = $brandDark
$global:BWProgressForm.Controls.Add($titleLabel)
$global:BWPhaseLabel = New-Object System.Windows.Forms.Label
$global:BWPhaseLabel.Location = New-Object System.Drawing.Point(20, 50)
$global:BWPhaseLabel.Size = New-Object System.Drawing.Size(480, 22)
$global:BWPhaseLabel.Text = "[0/$TOTAL_PHASES] 初始化..."
$global:BWPhaseLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$global:BWPhaseLabel.ForeColor = [System.Drawing.Color]::DarkBlue
$global:BWPhaseLabel.Font = New-Object System.Drawing.Font($uiFont, 10)
$global:BWPhaseLabel.ForeColor = $brandBlue
$global:BWProgressForm.Controls.Add($global:BWPhaseLabel)
$global:BWStatusLabel = New-Object System.Windows.Forms.Label
$global:BWStatusLabel.Location = New-Object System.Drawing.Point(20, 78)
$global:BWStatusLabel.Size = New-Object System.Drawing.Size(480, 22)
$global:BWStatusLabel.Text = ""
$global:BWStatusLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$global:BWStatusLabel.ForeColor = [System.Drawing.Color]::DimGray
$global:BWStatusLabel.Font = New-Object System.Drawing.Font($uiFont, 9)
$global:BWStatusLabel.ForeColor = [System.Drawing.Color]::FromArgb(120, 120, 140)
$global:BWProgressForm.Controls.Add($global:BWStatusLabel)
$global:BWProgressBar = New-Object System.Windows.Forms.ProgressBar
$global:BWProgressBar.Location = New-Object System.Drawing.Point(20, 110)
$global:BWProgressBar.Size = New-Object System.Drawing.Size(480, 22)
$global:BWProgressBar.Location = New-Object System.Drawing.Point(20, 112)
$global:BWProgressBar.Size = New-Object System.Drawing.Size(480, 20)
$global:BWProgressBar.Minimum = 0
$global:BWProgressBar.Maximum = $TOTAL_PHASES
$global:BWProgressBar.Value = 0
$global:BWProgressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Continuous # P3: 平滑
$global:BWProgressForm.Controls.Add($global:BWProgressBar)
$global:BWElapsedLabel = New-Object System.Windows.Forms.Label
$global:BWElapsedLabel.Location = New-Object System.Drawing.Point(400, 136)
$global:BWElapsedLabel.Size = New-Object System.Drawing.Size(100, 18)
$global:BWElapsedLabel.Text = ""
$global:BWElapsedLabel.Font = New-Object System.Drawing.Font($uiFont, 8)
$global:BWElapsedLabel.ForeColor = [System.Drawing.Color]::Silver
$global:BWElapsedLabel.TextAlign = [System.Drawing.ContentAlignment]::TopRight
$global:BWProgressForm.Controls.Add($global:BWElapsedLabel)
$hint = New-Object System.Windows.Forms.Label
$hint.Location = New-Object System.Drawing.Point(20, 140)
$hint.Size = New-Object System.Drawing.Size(480, 18)
$hint.Text = "首次安装约 5-10 分钟 (依赖下载) | 日志: $BWLogFile"
$hint.Font = New-Object System.Drawing.Font("Segoe UI", 8)
$hint.ForeColor = [System.Drawing.Color]::Gray
$hint.Location = New-Object System.Drawing.Point(20, 136)
$hint.Size = New-Object System.Drawing.Size(380, 32)
$hint.Text = "首次安装约 5-10 分钟 (依赖下载)`n关闭窗口可取消安装"
$hint.Font = New-Object System.Drawing.Font($uiFont, 8)
$hint.ForeColor = [System.Drawing.Color]::Silver
$global:BWProgressForm.Controls.Add($hint)
$global:BWProgressForm.Show() | Out-Null
@ -115,6 +144,11 @@ function Show-ProgressForm {
[System.Windows.Forms.Application]::DoEvents()
}
# 全局安装完成标记 (Close-ProgressForm 前设为 $true, 避免 X 按钮弹确认)
$global:BWInstallDone = $false
# 全局计时器
$global:BWStartTime = [System.Diagnostics.Stopwatch]::StartNew()
function Update-Progress($phase, $title) {
if ($global:BWProgressForm -and -not $global:BWProgressForm.IsDisposed) {
try {
@ -132,6 +166,11 @@ function Update-Progress-SubStatus($msg) {
try {
$shortMsg = if ($msg.Length -gt 70) { $msg.Substring(0, 67) + "..." } else { $msg }
$global:BWStatusLabel.Text = $shortMsg
# 刷新总耗时
if ($global:BWElapsedLabel -and $global:BWStartTime) {
$sec = [int]$global:BWStartTime.Elapsed.TotalSeconds
$global:BWElapsedLabel.Text = "$([int]($sec / 60))m $($sec % 60)s"
}
$global:BWStatusLabel.Refresh()
[System.Windows.Forms.Application]::DoEvents()
} catch {}
@ -139,6 +178,7 @@ function Update-Progress-SubStatus($msg) {
}
function Close-ProgressForm {
$global:BWInstallDone = $true # 关闭时不再弹 "取消安装?" 确认
if ($global:BWProgressForm -and -not $global:BWProgressForm.IsDisposed) {
try { $global:BWProgressForm.Close(); $global:BWProgressForm.Dispose() } catch {}
}
@ -277,8 +317,8 @@ function Show-AuthCodeDialog($attempt = 1, $maxAttempts = 3) {
$btnCancel = New-Object System.Windows.Forms.Button
$btnCancel.Location = New-Object System.Drawing.Point(350, 145)
$btnCancel.Size = New-Object System.Drawing.Size(80, 35)
$btnCancel.Text = "跳过"
$btnCancel.Size = New-Object System.Drawing.Size(90, 35)
$btnCancel.Text = "取消安装"
$btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $btnCancel
$form.Controls.Add($btnCancel)
@ -295,65 +335,84 @@ function Show-AuthCodeDialog($attempt = 1, $maxAttempts = 3) {
function Show-GiteaCredentialDialog {
$form = New-Object System.Windows.Forms.Form
$form.Text = "Bookworm - Gitea 登录"
$form.Size = New-Object System.Drawing.Size(420, 280)
$form.Size = New-Object System.Drawing.Size(420, 300)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = "FixedDialog"
$form.MaximizeBox = $false
$form.TopMost = $true
$form.BackColor = [System.Drawing.Color]::White
$lblInfo = New-Object System.Windows.Forms.Label
$lblInfo.Location = New-Object System.Drawing.Point(20, 15)
$lblInfo.Size = New-Object System.Drawing.Size(360, 25)
$lblInfo.Text = "输入 Gitea 账号 (code.letcareme.com)"
$lblInfo.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$lblInfo.Size = New-Object System.Drawing.Size(360, 40)
$lblInfo.Text = "输入 Gitea 账号 (code.letcareme.com)`n用于下载 Bookworm 配置文件,由管理员提供。"
$lblInfo.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$form.Controls.Add($lblInfo)
$lblUser = New-Object System.Windows.Forms.Label
$lblUser.Location = New-Object System.Drawing.Point(20, 50)
$lblUser.Location = New-Object System.Drawing.Point(20, 65)
$lblUser.Size = New-Object System.Drawing.Size(80, 25)
$lblUser.Text = "用户名:"
$form.Controls.Add($lblUser)
$txtUser = New-Object System.Windows.Forms.TextBox
$txtUser.Location = New-Object System.Drawing.Point(100, 48)
$txtUser.Location = New-Object System.Drawing.Point(100, 63)
$txtUser.Size = New-Object System.Drawing.Size(280, 25)
$txtUser.Font = New-Object System.Drawing.Font("Consolas", 11)
$form.Controls.Add($txtUser)
$lblPass = New-Object System.Windows.Forms.Label
$lblPass.Location = New-Object System.Drawing.Point(20, 90)
$lblPass.Location = New-Object System.Drawing.Point(20, 105)
$lblPass.Size = New-Object System.Drawing.Size(80, 25)
$lblPass.Text = "密码:"
$form.Controls.Add($lblPass)
$txtPass = New-Object System.Windows.Forms.TextBox
$txtPass.Location = New-Object System.Drawing.Point(100, 88)
$txtPass.Location = New-Object System.Drawing.Point(100, 103)
$txtPass.Size = New-Object System.Drawing.Size(280, 25)
$txtPass.PasswordChar = '*'
$txtPass.Font = New-Object System.Drawing.Font("Consolas", 11)
$form.Controls.Add($txtPass)
# P1 F10: 空值验证提示
$lblError = New-Object System.Windows.Forms.Label
$lblError.Location = New-Object System.Drawing.Point(100, 135)
$lblError.Size = New-Object System.Drawing.Size(280, 20)
$lblError.Text = ""
$lblError.Font = New-Object System.Drawing.Font("Segoe UI", 8)
$lblError.ForeColor = [System.Drawing.Color]::Red
$form.Controls.Add($lblError)
$btnOK = New-Object System.Windows.Forms.Button
$btnOK.Location = New-Object System.Drawing.Point(200, 140)
$btnOK.Location = New-Object System.Drawing.Point(200, 165)
$btnOK.Size = New-Object System.Drawing.Size(90, 35)
$btnOK.Text = "登录"
$btnOK.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $btnOK
$form.Controls.Add($btnOK)
$btnCancel = New-Object System.Windows.Forms.Button
$btnCancel.Location = New-Object System.Drawing.Point(300, 140)
$btnCancel.Location = New-Object System.Drawing.Point(300, 165)
$btnCancel.Size = New-Object System.Drawing.Size(80, 35)
$btnCancel.Text = "取消"
$btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $btnCancel
$form.Controls.Add($btnCancel)
# OK 按钮手动验证 (不用 DialogResult, 防止空值直接关闭)
$btnOK.Add_Click({
if (-not $txtUser.Text.Trim() -or -not $txtPass.Text) {
$lblError.Text = "用户名和密码不能为空"
return
}
$form.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.Close()
})
$form.AcceptButton = $btnOK
$form.Add_Shown({ $txtUser.Focus() })
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK -and $txtUser.Text -and $txtPass.Text) {
return @{ User = $txtUser.Text; Pass = $txtPass.Text }
if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
return @{ User = $txtUser.Text.Trim(); Pass = $txtPass.Text }
}
return $null
}
@ -805,8 +864,8 @@ if (Test-Path (Join-Path $BootDir ".git")) {
$bootCloneUrl = if ($cred) { $BootUrl -replace '://', "://$($cred.User):$($cred.Pass)@" } else { $BootUrl }
$r = Run-CmdWithUI "git" @("clone", "--depth", "1", $bootCloneUrl, $BootDir) "克隆 boot 仓库" 180000
if (-not (Test-Path (Join-Path $BootDir "crypto-helper.js"))) {
Log-Fail "boot 仓库克隆失败 (crypto-helper.js 缺失)"
Show-MsgBox "boot 仓库克隆失败。`n请检查网络和 Gitea 凭证。" "克隆失败" "OK" "Error"
Log-Fail "启动工具包下载失败"
Show-MsgBox "Bookworm 启动工具包下载失败。`n`n请检查:`n1. Gitea 账号和密码是否正确`n2. 网络连接是否正常`n3. 代理软件是否已启动`n`n然后重新运行安装器即可。" "下载失败" "OK" "Error"
exit 1
}
Log-OK "boot 仓库克隆成功 → $BootDir"