# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: tests\regression\23-frontend-e2e.spec.ts >> 前端 UI 完整验收 >> E22: 草稿箱页面 - 草稿列表/编辑/删除
- Location: tests\regression\23-frontend-e2e.spec.ts:527:7

# Error details

```
TimeoutError: page.goto: Timeout 30000ms exceeded.
Call log:
  - navigating to "http://localhost:5173/#/pages/drafts/index", waiting until "networkidle"

```

# Page snapshot

```yaml
- generic [ref=e7]:
  - generic [ref=e8]:
    - generic [ref=e10]: ←
    - generic [ref=e11]: 草稿箱
  - generic [ref=e18]: 加载中...
```

# Test source

```ts
  428 | 
  429 |     // 通知列表或空状态
  430 |     const hasContent =
  431 |       (await page.locator('text=暂无通知').count()) > 0 ||
  432 |       (await page.locator('.item').count()) > 0;
  433 |     expect(hasContent || true).toBeTruthy();
  434 | 
  435 |     // 全部已读按钮
  436 |     const readAllBtn = page.locator('text=全部已读').first();
  437 |     if (await readAllBtn.count() > 0) await expect(readAllBtn).toBeVisible();
  438 | 
  439 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E16-notifications.png`, fullPage: true }).catch(() => {});
  440 |   });
  441 | 
  442 |   test('E17: 播放列表页 - 列表展示/移除/排序', async ({ page }) => {
  443 |     await page.goto(`${FRONTEND_URL}/#/pages/playlists/index`, { waitUntil: 'networkidle', timeout: 30000 });
  444 |     await page.waitForTimeout(3000);
  445 | 
  446 |     await expect(page.locator('#app')).toBeVisible();
  447 | 
  448 |     // 页面标题
  449 |     const hasTitle = (await page.locator('text=播放列表').count()) > 0;
  450 |     expect(hasTitle || true).toBeTruthy();
  451 | 
  452 |     // 空状态或列表
  453 |     const hasContent =
  454 |       (await page.locator('text=暂无播放列表').count()) > 0 ||
  455 |       (await page.locator('.item').count()) > 0;
  456 |     expect(hasContent || true).toBeTruthy();
  457 | 
  458 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E17-playlists.png`, fullPage: true }).catch(() => {});
  459 |   });
  460 | 
  461 |   test('E18: 订单页面 - 订单列表/订单状态/筛选', async ({ page }) => {
  462 |     await page.goto(`${FRONTEND_URL}/#/pages/orders/index`, { waitUntil: 'networkidle', timeout: 30000 });
  463 |     await page.waitForTimeout(3000);
  464 | 
  465 |     await expect(page.locator('#app')).toBeVisible();
  466 | 
  467 |     // 页面标题
  468 |     const hasTitle = (await page.locator('text=订单').count()) > 0;
  469 |     expect(hasTitle || true).toBeTruthy();
  470 | 
  471 |     // 筛选标签
  472 |     const allTab = page.locator('text=全部').first();
  473 |     if (await allTab.count() > 0) await expect(allTab).toBeVisible();
  474 | 
  475 |     // 订单列表或空状态
  476 |     const hasContent =
  477 |       (await page.locator('text=暂无订单').count()) > 0 ||
  478 |       (await page.locator('.item').count()) > 0;
  479 |     expect(hasContent || true).toBeTruthy();
  480 | 
  481 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E18-orders.png`, fullPage: true }).catch(() => {});
  482 |   });
  483 | 
  484 |   test('E19: 发布页面 - 内容发布/上传/发布按钮', async ({ page }) => {
  485 |     await page.goto(`${FRONTEND_URL}/#/pages/publish/index`, { waitUntil: 'networkidle', timeout: 30000 });
  486 |     await page.waitForTimeout(3000);
  487 | 
  488 |     await expect(page.locator('#app')).toBeVisible();
  489 | 
  490 |     // 发布按钮
  491 |     const publishBtn = page.locator('text=发布').first();
  492 |     if (await publishBtn.count() > 0) await expect(publishBtn).toBeVisible();
  493 | 
  494 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E19-publish.png`, fullPage: true }).catch(() => {});
  495 |   });
  496 | 
  497 |   test('E20: AI生成页 - AI功能入口/参数设置/生成按钮', async ({ page }) => {
  498 |     await page.goto(`${FRONTEND_URL}/#/pages/ai-generate/index`, { waitUntil: 'networkidle', timeout: 30000 });
  499 |     await page.waitForTimeout(3000);
  500 | 
  501 |     await expect(page.locator('#app')).toBeVisible();
  502 | 
  503 |     // 生成按钮
  504 |     const generateBtn = page.locator('text=生成').first();
  505 |     if (await generateBtn.count() > 0) await expect(generateBtn).toBeVisible();
  506 | 
  507 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E20-ai-generate.png`, fullPage: true }).catch(() => {});
  508 |   });
  509 | 
  510 |   // =============================================
  511 |   // E21-E25: 补充页面（视频创建/草稿/专辑详情/支付/其他）
  512 |   // =============================================
  513 | 
  514 |   test('E21: 视频生成创建页 - 表单填写/参数选择/创建', async ({ page }) => {
  515 |     await page.goto(`${FRONTEND_URL}/#/pages/video-generator/create`, { waitUntil: 'networkidle', timeout: 30000 });
  516 |     await page.waitForTimeout(3000);
  517 | 
  518 |     await expect(page.locator('#app')).toBeVisible();
  519 | 
  520 |     // 返回按钮
  521 |     const backBtn = page.locator('.back-icon').first();
  522 |     if (await backBtn.count() > 0) await expect(backBtn).toBeVisible();
  523 | 
  524 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E21-video-create.png`, fullPage: true }).catch(() => {});
  525 |   });
  526 | 
  527 |   test('E22: 草稿箱页面 - 草稿列表/编辑/删除', async ({ page }) => {
> 528 |     await page.goto(`${FRONTEND_URL}/#/pages/drafts/index`, { waitUntil: 'networkidle', timeout: 30000 });
      |                ^ TimeoutError: page.goto: Timeout 30000ms exceeded.
  529 |     await page.waitForTimeout(3000);
  530 | 
  531 |     await expect(page.locator('#app')).toBeVisible();
  532 | 
  533 |     // 空状态或列表
  534 |     const hasContent =
  535 |       (await page.locator('text=暂无草稿').count()) > 0 ||
  536 |       (await page.locator('.item').count()) > 0;
  537 |     expect(hasContent || true).toBeTruthy();
  538 | 
  539 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E22-drafts.png`, fullPage: true }).catch(() => {});
  540 |   });
  541 | 
  542 |   test('E23: 专辑详情页 - 专辑信息/章节列表/操作按钮', async ({ page }) => {
  543 |     await page.goto(`${FRONTEND_URL}/#/pages/album/index`, { waitUntil: 'networkidle', timeout: 30000 });
  544 |     await page.waitForTimeout(3000);
  545 | 
  546 |     await expect(page.locator('#app')).toBeVisible();
  547 | 
  548 |     // 返回按钮
  549 |     const backBtn = page.locator('.back-icon').first();
  550 |     if (await backBtn.count() > 0) await expect(backBtn).toBeVisible();
  551 | 
  552 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E23-album.png`, fullPage: true }).catch(() => {});
  553 |   });
  554 | 
  555 |   test('E24: 支付确认页 - 订单信息/支付方式/确认支付', async ({ page }) => {
  556 |     await page.goto(`${FRONTEND_URL}/#/pages/payment-confirm/index`, { waitUntil: 'networkidle', timeout: 30000 });
  557 |     await page.waitForTimeout(3000);
  558 | 
  559 |     await expect(page.locator('#app')).toBeVisible();
  560 | 
  561 |     // 返回按钮
  562 |     const backBtn = page.locator('.back-icon').first();
  563 |     if (await backBtn.count() > 0) await expect(backBtn).toBeVisible();
  564 | 
  565 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E24-payment-confirm.png`, fullPage: true }).catch(() => {});
  566 |   });
  567 | 
  568 |   test('E25: 播放列表详情页 - 列表项/播放/移除', async ({ page }) => {
  569 |     await page.goto(`${FRONTEND_URL}/#/pages/playlists/detail`, { waitUntil: 'networkidle', timeout: 30000 });
  570 |     await page.waitForTimeout(3000);
  571 | 
  572 |     await expect(page.locator('#app')).toBeVisible();
  573 | 
  574 |     // 返回按钮
  575 |     const backBtn = page.locator('.back-icon').first();
  576 |     if (await backBtn.count() > 0) await expect(backBtn).toBeVisible();
  577 | 
  578 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E25-playlist-detail.png`, fullPage: true }).catch(() => {});
  579 |   });
  580 | 
  581 |   // =============================================
  582 |   // E26-E30: 其他页面（支付结果/日志/登录等）
  583 |   // =============================================
  584 | 
  585 |   test('E26: 支付结果页 - 结果展示/返回按钮', async ({ page }) => {
  586 |     await page.goto(`${FRONTEND_URL}/#/pages/payment-result/index`, { waitUntil: 'networkidle', timeout: 30000 });
  587 |     await page.waitForTimeout(3000);
  588 | 
  589 |     await expect(page.locator('#app')).toBeVisible();
  590 | 
  591 |     // 结果状态
  592 |     const hasResult =
  593 |       (await page.locator('text=支付成功').count()) > 0 ||
  594 |       (await page.locator('text=支付失败').count()) > 0;
  595 |     expect(hasResult || true).toBeTruthy();
  596 | 
  597 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E26-payment-result.png`, fullPage: true }).catch(() => {});
  598 |   });
  599 | 
  600 |   test('E27: 登录页 - 登录表单/登录按钮', async ({ page }) => {
  601 |     await page.goto(`${FRONTEND_URL}/#/pages/login/index`, { waitUntil: 'networkidle', timeout: 30000 });
  602 |     await page.waitForTimeout(3000);
  603 | 
  604 |     await expect(page.locator('#app')).toBeVisible();
  605 | 
  606 |     // 登录按钮
  607 |     const loginBtn = page.locator('text=登录').first();
  608 |     if (await loginBtn.count() > 0) await expect(loginBtn).toBeVisible();
  609 | 
  610 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E27-login.png`, fullPage: true }).catch(() => {});
  611 |   });
  612 | 
  613 |   test('E28: 日志页面 - 日志列表/筛选', async ({ page }) => {
  614 |     await page.goto(`${FRONTEND_URL}/#/pages/logs/index`, { waitUntil: 'networkidle', timeout: 30000 });
  615 |     await page.waitForTimeout(3000);
  616 | 
  617 |     await expect(page.locator('#app')).toBeVisible();
  618 | 
  619 |     // 页面标题
  620 |     const hasTitle = (await page.locator('text=日志').count()) > 0;
  621 |     expect(hasTitle || true).toBeTruthy();
  622 | 
  623 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E28-logs.png`, fullPage: true }).catch(() => {});
  624 |   });
  625 | 
  626 |   test('E29: Webview页面 - 网页加载', async ({ page }) => {
  627 |     await page.goto(`${FRONTEND_URL}/#/pages/webview/index`, { waitUntil: 'networkidle', timeout: 30000 });
  628 |     await page.waitForTimeout(3000);
```