# F9 报表设计器协议宿主 # 注册表 HKCR\f9designer\shell\open\command 指向本脚本: # powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File "<本脚本>" "%1" # 前端唤起:ReportDesign.vue 的 openGridDesigner() # f9designer://open?base=&rid=<报表Id>&token=&code=<报表编号> # # 流程:解析协议参数 -> 下载模板 .grf -> 用锐浪 Grid++Report 7 设计器打开(阻塞等待关闭) # -> 关闭后把模板回传后端 -> 清理临时文件 # 日志:tools/report-designer/logs/designer_yyyyMMdd.log(排查"点了没反应"时看这里) param([string]$Url) $ErrorActionPreference = 'Stop' $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $logDir = Join-Path $scriptDir 'logs' if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } $log = Join-Path $logDir ('designer_{0:yyyyMMdd}.log' -f (Get-Date)) function Write-Log($msg) { $line = '[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $msg try { Add-Content -Path $log -Value $line -Encoding UTF8 } catch { } } # 失败时弹窗提示(注册表里是 -WindowStyle Hidden,不弹窗的话用户只会看到"点了没反应") function Show-Error($msg) { Write-Log "错误: $msg" try { Add-Type -AssemblyName System.Windows.Forms [System.Windows.Forms.MessageBox]::Show( $msg + "`n`n详细日志:" + $log, 'F9 报表设计器', 'OK', 'Error') | Out-Null } catch { } } Write-Log '========== 唤起 ==========' Write-Log "Url: $Url" try { # ---------- 1. 解析协议参数 ---------- # 关键:Windows Shell 会在协议主机后补一个 "/",实际收到的是 # f9designer://open/?base=...&rid=... # 所以不能用固定前缀剥离,直接取第一个 "?" 之后的全部内容。 $qIndex = if ($Url) { $Url.IndexOf('?') } else { -1 } $raw = if ($qIndex -ge 0) { $Url.Substring($qIndex + 1) } else { '' } $qs = @{} foreach ($part in ($raw -split '&')) { if ([string]::IsNullOrWhiteSpace($part)) { continue } $kv = $part -split '=', 2 if ($kv.Length -eq 2) { $key = [Uri]::UnescapeDataString($kv[0]).Trim() $qs[$key] = [Uri]::UnescapeDataString($kv[1]) } } $base = ($qs['base'] + '').TrimEnd('/') $rid = $qs['rid'] $token = $qs['token'] $code = $qs['code'] Write-Log "base=$base rid=$rid code=$code" if (-not $base) { $base = 'http://localhost:5173' Write-Log "未收到 base 参数,回退默认 $base" } if (-not $rid) { throw "缺少 rid 参数(Url=$Url)" } # ---------- 2. 定位锐浪设计器 ---------- # 可用环境变量 F9_GR7_DESIGNER 覆盖,默认 D:\GridReport7\gr7designer64.exe $designer = $env:F9_GR7_DESIGNER if ([string]::IsNullOrWhiteSpace($designer)) { $designer = 'D:\GridReport7\gr7designer64.exe' } if (-not (Test-Path $designer)) { throw "未找到锐浪设计器:$designer (可设置环境变量 F9_GR7_DESIGNER 指定)" } Write-Log "设计器: $designer" # ---------- 3. 下载模板 ---------- $tmp = Join-Path $env:TEMP ('f9_{0}_{1}.grf' -f $rid, [Guid]::NewGuid().ToString('N').Substring(0, 8)) # 下载接口为 [AllowAnonymous],token 走 query 的 access_token $dl = "$base/api/sys/report/$rid/template?access_token=$token" Write-Log "下载: $dl" try { Invoke-WebRequest -Uri $dl -OutFile $tmp -UseBasicParsing Write-Log ('已下载 {0} 字节' -f (Get-Item $tmp).Length) } catch { Write-Log "下载失败(新报表可能尚无模板),改用空白模板继续:$($_.Exception.Message)" Set-Content -Path $tmp -Value '' -Encoding UTF8 } # ---------- 4. 打开设计器并等待关闭 ---------- Write-Log '启动设计器(等待关闭)...' $proc = Start-Process -FilePath $designer -ArgumentList ('"{0}"' -f $tmp) -PassThru $proc.WaitForExit() Write-Log ('设计器已关闭,退出码 {0}' -f $proc.ExitCode) # ---------- 5. 回传模板 ---------- if ((Get-Item $tmp).Length -gt 0) { Write-Log '上传模板...' Add-Type -AssemblyName System.Net.Http $client = New-Object System.Net.Http.HttpClient if ($token) { $client.DefaultRequestHeaders.TryAddWithoutValidation('Authorization', "Bearer $token") | Out-Null } $content = New-Object System.Net.Http.MultipartFormDataContent $fs = [System.IO.File]::OpenRead($tmp) try { $sc = New-Object System.Net.Http.StreamContent($fs) $content.Add($sc, 'file', [System.IO.Path]::GetFileName($tmp)) $resp = $client.PostAsync("$base/api/sys/report/$rid/template", $content).Result Write-Log ('上传结果: {0}' -f $resp.StatusCode) } finally { $fs.Close() $client.Dispose() } } else { Write-Log '模板为空,跳过上传' } Remove-Item $tmp -Force -ErrorAction SilentlyContinue Write-Log '完成' } catch { Show-Error $($_.Exception.Message) Write-Log $_.ScriptStackTrace }