# -*- coding: utf-8 -*- # 启动管理后台(后端 + 前端) # 服务成功启动后自动打开浏览器 $ErrorActionPreference = "Stop" try { # 定位项目根目录:脚本在 scripts/ops/ 下,向上三级 if ($MyInvocation.MyCommand.Path) { $ProjectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)) } else { $ProjectRoot = $PWD.Path } Write-Host "========================================" -ForegroundColor Cyan Write-Host " NeoZQYY 管理后台启动脚本" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "项目根目录: $ProjectRoot" Write-Host "" $backendDir = Join-Path $ProjectRoot "apps\backend" $frontendDir = Join-Path $ProjectRoot "apps\admin-web" if (-not (Test-Path $backendDir)) { throw "后端目录不存在: $backendDir" } if (-not (Test-Path $frontendDir)) { throw "前端目录不存在: $frontendDir" } # 前端日志文件(每次用唯一文件名,避免上次进程锁定旧文件) $ts = Get-Date -Format "yyyyMMdd_HHmmss" $frontendLog = Join-Path $env:TEMP "neozqyy_fe_${ts}.log" # 选择 PowerShell 可执行文件:优先 pwsh (7+),回退 powershell (5.1) $psExe = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" } # ── 生成临时启动脚本 ── $beTmp = Join-Path $env:TEMP "neozqyy_start_be.ps1" $feTmp = Join-Path $env:TEMP "neozqyy_start_fe.ps1" $q = [char]39 @( "Set-Location -LiteralPath ${q}${backendDir}${q}" "Write-Host ${q}=== 后端 FastAPI ===${q} -ForegroundColor Green" "uv run uvicorn app.main:app --reload --port 8000" "Write-Host ${q}后端已退出,按任意键关闭...${q} -ForegroundColor Red" "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})" ) | Set-Content -Path $beTmp -Encoding UTF8 @( "Set-Location -LiteralPath ${q}${frontendDir}${q}" "Write-Host ${q}=== 前端 Vite ===${q} -ForegroundColor Green" "pnpm dev 2>&1 | Tee-Object -FilePath ${q}${frontendLog}${q}" "Write-Host ${q}前端已退出,按任意键关闭...${q} -ForegroundColor Red" "`$null = `$Host.UI.RawUI.ReadKey(${q}NoEcho,IncludeKeyDown${q})" ) | Set-Content -Path $feTmp -Encoding UTF8 # ── 启动后端 ── Write-Host "[1/2] 启动后端 FastAPI (http://localhost:8000) ..." -ForegroundColor Yellow Start-Process $psExe -ArgumentList "-NoExit", "-ExecutionPolicy", "Bypass", "-File", $beTmp Start-Sleep -Seconds 2 # ── 启动前端 ── Write-Host "[2/2] 启动前端 Vite (http://localhost:5173) ..." -ForegroundColor Yellow Start-Process $psExe -ArgumentList "-NoExit", "-ExecutionPolicy", "Bypass", "-File", $feTmp Write-Host "" Write-Host "========================================" -ForegroundColor Green Write-Host " 两个服务已在新窗口中启动" -ForegroundColor Green Write-Host " 后端: http://localhost:8000" -ForegroundColor Green Write-Host " 前端: http://localhost:5173" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "" # ── 检测前端就绪(匹配 Vite 输出中的 localhost:5173,忽略 ANSI 转义码) ── Write-Host "等待前端 Vite 就绪..." -ForegroundColor Yellow $timeout = 45 $elapsed = 0 $ready = $false while ($elapsed -lt $timeout) { Start-Sleep -Seconds 1 $elapsed++ if (Test-Path $frontendLog) { $raw = Get-Content $frontendLog -Raw -ErrorAction SilentlyContinue if ($raw) { # 去掉 ANSI 转义序列后再匹配 $clean = $raw -replace '\x1b\[[0-9;]*m', '' if ($clean -match "localhost:5173" -or $clean -match "ready in") { $ready = $true break } } } } if ($ready) { Write-Host "前端已就绪(${elapsed}s),打开浏览器..." -ForegroundColor Green Start-Process "http://localhost:5173" } else { Write-Host "等待超时(${timeout}s),请手动打开 http://localhost:5173" -ForegroundColor Red } } catch { Write-Host "" Write-Host "启动失败: $_" -ForegroundColor Red Write-Host $_.ScriptStackTrace -ForegroundColor DarkRed } Write-Host "" Write-Host "按任意键关闭此窗口..." -ForegroundColor DarkGray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")