### Task 5: 实现 `pack.sh` PC 构建与复制（GREEN）

**Files:**
- Modify: `deploy/baota/pack.sh`
- Test: `tests/deploy/baota/test-pack-web-hooks.sh`

**Interfaces:**
- Consumes: Task 3 `NUXT_APP_BASE_URL`；`WEB_FRONTEND_DIR` 默认 `$PARENT_DIR/ECShopX_web-frontend`
- Produces: stage `web/.output/`；摘要含 `/web/`

- [ ] **Step 1: 在变量区增加**

```bash
WEB_FRONTEND_DIR="${WEB_FRONTEND_DIR:-$PARENT_DIR/ECShopX_web-frontend}"
```

头注释增加 `WEB_FRONTEND_DIR` 说明；说明打包机需 Node 16（admin/mobile）+ Node 20（PC）。

- [ ] **Step 2: 增加构建/断言/复制函数**（放在 `build_mobile_frontend` 之后）

要求行为：

1. `build_web_frontend`：`cd "$WEB_FRONTEND_DIR"`，确保可用 Node ≥20（可用 `node -v` 解析主版本，失败 `die`）；`corepack enable` 或调用已装 `pnpm`；  
   `NUXT_APP_BASE_URL=/web/ NUXT_PUBLIC_API_BASE=/api/h5app pnpm install && pnpm build`
2. `assert_web_output`：要求 `$WEB_FRONTEND_DIR/.output/server/index.mjs` 存在；若 `.output/public` 下有 HTML/资源清单，优先 `grep -q '/web/'`（有则断言，无则至少 index.mjs 存在）
3. 在 `pack_frontend` 中：与 admin/mobile 相同，`PACK_FRONTEND_BUILD=true` 时调用 `build_web_frontend`；然后  
   `mkdir -p "$STAGE/web"`  
   `rsync -a --delete --quiet "$WEB_FRONTEND_DIR/.output/" "$STAGE/web/.output/"`  
   校验 `"$STAGE/web/.output/server/index.mjs"`
4. `PACK_FRONTEND=false` 时整段跳过（含 PC）
5. 结束 log 增加 `PC SSR → /web/ (web/.output)`

Node 切换：若系统有 nvm/fnm，文档化；实现上优先检测 `node -v` 主版本，若 `<20` 则 `die` 提示改用 Node 20.19 再跑 pack（与 admin 分步执行时由打包者切换版本）。可选：若存在 `WEB_NODE_BIN` 环境变量则用 `$WEB_NODE_BIN` / 其旁路 npm/pnpm。

- [ ] **Step 3: Run** `bash tests/deploy/baota/test-pack-web-hooks.sh` → PASS

- [ ] **Step 4: Commit**（用户要求时）

---

