# Story 11.4: Company 管理 Page Object（重点）

Status: done

<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->

## Story

作为测试开发者，
我想要创建公司管理的 Page Object，
以便组织公司管理相关的页面元素和操作，为后续编写公司管理 E2E 测试提供基础。

## Acceptance Criteria

1. **AC1: 创建 CompanyManagementPage 文件**
   - 文件路径: `web/tests/e2e/pages/admin/company-management.page.ts`
   - 使用 TypeScript 编写
   - 导入 Playwright 的 Page、Locator、Response 等类型
   - 定义完整的类结构和方法签名

2. **AC2: 定义页面选择器**
   - 定义页面级选择器：pageTitle, createCompanyButton, searchInput, searchButton, companyTable
   - 定义对话框选择器：createDialogTitle, editDialogTitle
   - 定义表单字段选择器：platformSelector, companyNameInput, contactPersonInput, contactPhoneInput, contactEmailInput, addressInput
   - 定义按钮选择器：createSubmitButton, updateSubmitButton, cancelButton, confirmDeleteButton
   - 所有选择器使用 data-testid 定位（遵循测试标准）

3. **AC3: 实现导航和基础验证方法**
   - 实现 `goto()` 方法：导航到 /admin/companies 页面
   - 实现 `expectToBeVisible()` 方法：验证页面关键元素可见
   - 等待页面标题和表格数据加载完成

4. **AC4: 实现对话框操作方法**
   - 实现 `openCreateDialog()` 方法：打开创建公司对话框
   - 实现 `openEditDialog(companyName)` 方法：打开编辑公司对话框
   - 实现 `openDeleteDialog(companyName)` 方法：打开删除确认对话框
   - 实现 `cancelDialog()` 方法：取消对话框
   - 实现 `waitForDialogClosed()` 方法：等待对话框关闭

5. **AC5: 实现表单操作方法**
   - 实现 `fillCompanyForm(data)` 方法：填写公司表单
   - 实现 `submitForm()` 方法：提交表单并捕获网络响应
   - 表单数据接口包含：platformId?, companyName, contactPerson?, contactPhone?, contactEmail?, address?
   - 支持创建和编辑两种表单模式

6. **AC6: 实现 CRUD 操作方法**
   - 实现 `createCompany(data)` 方法：创建公司（完整流程）
   - 实现 `editCompany(companyName, data)` 方法：编辑公司（完整流程）
   - 实现 `deleteCompany(companyName)` 方法：删除公司（使用 API 直接删除）
   - 删除成功后刷新页面确保列表更新

7. **AC7: 实现搜索和验证方法**
   - 实现 `searchByName(name)` 方法：按公司名称搜索
   - 实现 `companyExists(companyName)` 方法：验证公司是否存在（使用精确匹配）
   - 搜索后验证结果

8. **AC8: 定义数据接口和类型**
   - 定义 CompanyData 接口（包含所有公司字段）
   - 定义 NetworkResponse 接口（捕获网络响应）
   - 定义 FormSubmitResult 接口（表单提交结果）
   - 所有接口使用 JSDoc 注释

9. **AC9: 代码质量标准**
   - TypeScript 类型检查通过（无类型错误）
   - 所有公共方法有完整的 JSDoc 注释
   - 代码结构与 PlatformManagementPage 保持一致

## Tasks / Subtasks

- [x] 任务 1: 创建文件和基础结构 (AC: 1, 8, 9)
  - [x] 创建文件 `web/tests/e2e/pages/admin/company-management.page.ts`
  - [x] 导入 Playwright 类型和依赖
  - [x] 定义类结构：`export class CompanyManagementPage`
  - [x] 定义接口：CompanyData, NetworkResponse, FormSubmitResult

- [x] 任务 2: 定义页面选择器 (AC: 2, 9)
  - [x] 定义页面级选择器（pageTitle, createCompanyButton, searchInput, searchButton, companyTable）
  - [x] 定义对话框选择器（createDialogTitle, editDialogTitle）
  - [x] 定义表单字段选择器（使用 data-testid）
  - [x] 定义按钮选择器（createSubmitButton, updateSubmitButton, cancelButton, confirmDeleteButton）

- [x] 任务 3: 实现导航和基础验证方法 (AC: 3, 9)
  - [x] 实现 `goto()` 方法
  - [x] 实现 `expectToBeVisible()` 方法
  - [x] 添加 JSDoc 注释

- [x] 任务 4: 实现对话框操作方法 (AC: 4, 9)
  - [x] 实现 `openCreateDialog()` 方法
  - [x] 实现 `openEditDialog(companyName)` 方法
  - [x] 实现 `openDeleteDialog(companyName)` 方法
  - [x] 实现 `cancelDialog()` 和 `waitForDialogClosed()` 方法
  - [x] 添加 JSDoc 注释

- [x] 任务 5: 实现表单操作方法 (AC: 5, 9)
  - [x] 实现 `fillCompanyForm(data)` 方法
  - [x] 实现 `submitForm()` 方法（包含网络响应捕获）
  - [x] 处理 Toast 消息验证
  - [x] 添加 JSDoc 注释

- [x] 任务 6: 实现 CRUD 操作方法 (AC: 6, 9)
  - [x] 实现 `createCompany(data)` 方法
  - [x] 实现 `editCompany(companyName, data)` 方法
  - [x] 实现 `deleteCompany(companyName)` 方法（API 直接删除）
  - [x] 添加 JSDoc 注释

- [x] 任务 7: 实现搜索和验证方法 (AC: 7, 9)
  - [x] 实现 `searchByName(name)` 方法
  - [x] 实现 `companyExists(companyName)` 方法
  - [x] 添加 JSDoc 注释

- [x] 任务 8: TypeScript 类型检查和代码质量验证 (AC: 9)
  - [x] 运行 `pnpm typecheck` 检查类型
  - [x] 确保无类型错误
  - [x] 验证代码风格符合项目标准

## Dev Notes

### Epic 11 背景和目标

**Epic 11: 基础配置管理测试 (Epic F)**

为平台、公司、渠道配置管理编写 E2E 测试，为后续用户管理和跨端测试提供必要的测试数据。

**实体关系链：**
```
Platform (平台)
  ↓ 1:N
Company (公司) - 必须 platformId
  ↓ 1:N
Order (订单) - 必须 companyId
```

**Story 11.4 在 Epic 中的位置：**
- Story 11.1 (已完成) → Story 11.2 (已完成) → Story 11.3 (已完成) → Story 11.4 (当前)
- Story 11.1 创建了 PlatformManagementPage Page Object
- Story 11.2 编写了平台创建功能的 E2E 测试
- Story 11.3 编写了平台列表显示验证测试
- Story 11.4 将创建 CompanyManagementPage Page Object

**Company 实体关键特点：**
- 必须关联 Platform（platformId 外键，可选但建议填写）
- 公司名称在同一平台下必须唯一（复合唯一索引）
- 包含联系人信息（可选字段）
- 有状态字段（status: 1-启用，0-禁用）
- 删除已禁用的公司才能删除

### 架构模式和约束

**参考 Story 11.1 的 PlatformManagementPage 模式：**

1. **选择器策略：**
   - 优先使用 `data-testid` 定位元素
   - 使用 role + name 组合作为备用（如按钮）
   - 避免使用不稳定的 CSS 类选择器

2. **网络响应捕获：**
   - 使用 `waitForResponse()` 捕获特定 API 响应
   - 监听 `createCompany`、`updateCompany`、`getAllCompanies` 等 API
   - 使用 `Promise.race()` 添加超时保护

3. **Toast 消息验证：**
   - Toast 使用 Sonner UI 库：`[data-sonner-toast][data-type="error"]` 和 `[data-type="success"]`
   - Toast 可能出现得很快，需要主动等待
   - 同时验证 API 响应作为主要验证方式

4. **删除操作策略：**
   - 使用 API 直接删除，绕过 UI 的不可靠性
   - 使用 `page.evaluate()` 在浏览器上下文中执行 fetch
   - 删除成功后刷新页面确保列表更新

### Company 实体结构

**Company Entity (来自 company.entity.ts):**
```typescript
{
  id: number;              // 主键ID
  platformId: number | null;  // 平台ID（外键）
  companyName: string;     // 公司名称（必填，最大100字符）
  contactPerson: string | null;   // 联系人（可选，最大50字符）
  contactPhone: string | null;   // 联系电话（可选，最大20字符）
  contactEmail: string | null;   // 联系邮箱（可选，最大100字符）
  address: string | null;        // 地址（可选，最大200字符）
  status: number;          // 状态：1-正常，0-禁用（默认1）
  createTime: Date;        // 创建时间
  updateTime: Date;        // 更新时间
  platform?: Platform;     // 关联的平台信息（eager加载）
}
```

**唯一约束：**
- 同一平台下公司名称唯一：`idx_company_name_platform (companyName, platformId)`

### CompanyManagement UI 组件分析

**页面路径:** `/admin/companies`

**UI 组件结构（来自 CompanyManagement.tsx）:**

1. **页面结构：**
   - 页面标题："公司管理"
   - 搜索栏：搜索输入框 + 搜索按钮 + 创建公司按钮
   - 公司列表表格（7列）：公司名称、平台、联系人、联系电话、状态、创建时间、操作
   - 分页组件（DataTablePagination）

2. **创建/编辑对话框：**
   - 对话框标题："创建公司" / "编辑公司"
   - 表单字段：
     - platformId: PlatformSelector（使用 Radix UI Select，可选）
     - companyName: Input（必填）
     - contactPerson: Input（可选）
     - contactPhone: Input（可选）
     - contactEmail: Input（可选，email 类型）
     - address: Input（可选）
   - 提交按钮："创建" / "更新"
   - 取消按钮："取消"

3. **删除确认对话框：**
   - 标题："确认删除"
   - 描述："确定要删除这个公司吗？此操作不可恢复。"
   - 确认按钮："确认删除"（红色 destructive 样式）
   - 取消按钮："取消"

4. **data-testid 属性（关键）：**
   ```
   搜索相关：
   - search-company-input (搜索输入框)
   - search-company-button (搜索按钮)
   - create-company-button (创建公司按钮)

   表单字段（创建）：
   - create-company-platform-selector (平台选择器)
   - create-company-name-input (公司名称)
   - create-company-contact-person-input (联系人)
   - create-company-contact-phone-input (联系电话)
   - create-company-contact-email-input (联系邮箱)
   - create-company-address-input (地址)
   - submit-create-company-button (提交按钮)
   - cancel-company-button (取消按钮)

   表单字段（编辑）：
   - edit-company-platform-selector
   - edit-company-name-input
   - edit-company-contact-person-input
   - edit-company-contact-phone-input
   - edit-company-contact-email-input
   - edit-company-address-input
   - submit-edit-company-button (提交按钮)
   - cancel-edit-company-button (取消按钮)

   删除对话框：
   - cancel-delete-company-button (取消)
   - confirm-delete-company-button (确认删除)

   表格操作按钮：
   - edit-company-button-{id} (编辑按钮，动态ID)
   - delete-company-button-{id} (删除按钮，动态ID)

   其他：
   - company-modal-title (对话框标题)
   ```

5. **PlatformSelector 集成：**
   - 使用 Radix UI Select 组件
   - 使用 `@d8d/e2e-test-utils` 的 `selectRadixOption` 或 `selectRadixOptionAsync`
   - 标签："平台" 或 "选择平台"
   - 选项是异步加载的（从平台列表 API）

### API 端点参考

**公司管理 API 端点：**
```typescript
// 获取所有公司
GET /api/v1/company/getAllCompanies?skip=0&take=10

// 搜索公司
GET /api/v1/company/searchCompanies?name=xxx&skip=0&take=10

// 创建公司
POST /api/v1/company/createCompany
Body: { platformId?: number, companyName: string, contactPerson?, contactPhone?, contactEmail?, address? }

// 更新公司
POST /api/v1/company/updateCompany
Body: { id: number, platformId?, companyName?, contactPerson?, contactPhone?, contactEmail?, address? }

// 删除公司
POST /api/v1/company/deleteCompany
Body: { id: number }
```

### Page Object 设计参考

**CompanyManagementPage 类结构：**
```typescript
import { Page, Locator } from '@playwright/test';

/**
 * 公司数据接口
 */
export interface CompanyData {
  /** 平台ID（可选） */
  platformId?: number;
  /** 公司名称（必填） */
  companyName: string;
  /** 联系人（可选） */
  contactPerson?: string;
  /** 联系电话（可选） */
  contactPhone?: string;
  /** 联系邮箱（可选） */
  contactEmail?: string;
  /** 地址（可选） */
  address?: string;
}

/**
 * 网络响应数据接口
 */
export interface NetworkResponse {
  url: string;
  method: string;
  status: number;
  ok: boolean;
  responseHeaders: Record<string, string>;
  responseBody: unknown;
}

/**
 * 表单提交结果接口
 */
export interface FormSubmitResult {
  success: boolean;
  hasError: boolean;
  hasSuccess: boolean;
  errorMessage?: string;
  successMessage?: string;
  responses?: NetworkResponse[];
}

/**
 * 公司管理 Page Object
 *
 * 用于公司管理功能的 E2E 测试
 * 页面路径: /admin/companies
 */
export class CompanyManagementPage {
  readonly page: Page;

  // ===== 页面级选择器 =====
  readonly pageTitle: Locator;
  readonly createCompanyButton: Locator;
  readonly searchInput: Locator;
  readonly searchButton: Locator;
  readonly companyTable: Locator;

  // ===== 对话框选择器 =====
  readonly createDialogTitle: Locator;
  readonly editDialogTitle: Locator;

  // ===== 表单字段选择器 =====
  readonly platformSelector: Locator;      // PlatformSelector 容器
  readonly companyNameInput: Locator;
  readonly contactPersonInput: Locator;
  readonly contactPhoneInput: Locator;
  readonly contactEmailInput: Locator;
  readonly addressInput: Locator;

  // ===== 按钮选择器 =====
  readonly createSubmitButton: Locator;
  readonly updateSubmitButton: Locator;
  readonly cancelButton: Locator;
  readonly confirmDeleteButton: Locator;

  constructor(page: Page) {
    this.page = page;
    // 初始化所有选择器
  }

  // ===== 导航和基础验证 =====
  async goto(): Promise<void>
  async expectToBeVisible(): Promise<void>

  // ===== 对话框操作 =====
  async openCreateDialog(): Promise<void>
  async openEditDialog(companyName: string): Promise<void>
  async openDeleteDialog(companyName: string): Promise<void>
  async cancelDialog(): Promise<void>
  async waitForDialogClosed(): Promise<void>

  // ===== 表单操作 =====
  async fillCompanyForm(data: CompanyData): Promise<void>
  async submitForm(): Promise<FormSubmitResult>

  // ===== CRUD 操作 =====
  async createCompany(data: CompanyData): Promise<FormSubmitResult>
  async editCompany(companyName: string, data: CompanyData): Promise<FormSubmitResult>
  async deleteCompany(companyName: string): Promise<boolean>

  // ===== 搜索和验证 =====
  async searchByName(name: string): Promise<boolean>
  async companyExists(companyName: string): Promise<boolean>
}
```

### PlatformSelector 选择处理

**关键注意事项：**

1. **使用 `@d8d/e2e-test-utils` 的 Select 工具：**
   ```typescript
   import { selectRadixOption } from '@d8d/e2e-test-utils';

   // 在 fillCompanyForm 中选择平台
   if (data.platformId !== undefined) {
     // 需要知道平台的名称来选择
     // 选项格式：平台名称作为显示值，platformId 作为实际值
     await selectRadixOption(this.page, '平台', platformName);
   }
   ```

2. **测试数据准备：**
   - 测试需要先创建测试用的平台
   - 可以在测试用例中先创建平台，然后获取平台 ID 和名称
   - 或者使用现有平台数据

### 与 PlatformManagementPage 的差异

**相同部分：**
- 整体结构和模式完全一致
- 选择器命名规则一致
- 对话框操作逻辑一致
- 表单提交流程一致
- Toast 验证方式一致
- API 删除策略一致

**差异部分：**
1. **额外的表单字段：**
   - Company 有 `address` 字段（Platform 没有）
   - Platform 有 `platformName`，Company 有 `companyName`

2. **外键关系：**
   - Company 必须选择关联的 Platform（使用 PlatformSelector）
   - Platform 是顶级实体，无外键

3. **表格列差异：**
   - Platform 表格：7列（ID, 平台名称, 联系人, 联系电话, 联系邮箱, 创建时间, 操作）
   - Company 表格：7列（公司名称, 平台, 联系人, 联系电话, 状态, 创建时间, 操作）
   - Company 显示关联的平台名称和状态徽章

4. **删除限制：**
   - 只有禁用状态（status=0）的公司才能删除
   - 测试中需要先禁用公司再删除

### 测试数据唯一性策略

**使用时间戳确保唯一性：**
```typescript
const timestamp = Date.now();
const uniqueId = `company_${timestamp}`;
const companyName = `测试公司_${uniqueId}`;
```

**测试数据清理：**
- 每个测试用例结束后清理自己创建的公司数据
- 使用 `deleteCompany()` 方法删除测试数据
- 验证删除成功后再结束测试

### 依赖关系

**Epic 11 内部依赖：**
- Story 11.1: ✅ 已完成（PlatformManagementPage 作为参考）
- Story 11.2: ✅ 已完成（平台创建测试参考）
- Story 11.3: ✅ 已完成（平台列表测试参考）
- Story 11.4: Company 管理 Page Object（当前）

**外部依赖：**
- Epic 1, 2: `@d8d/e2e-test-utils` 包（已存在）
- `web/tests/e2e/utils/test-setup.ts`: 需要添加 companyManagementPage fixture

**测试夹具配置（需要在 test-setup.ts 中添加）：**
```typescript
export const test = test.extend<{
  adminLoginPage: AdminLoginPage;
  companyManagementPage: CompanyManagementPage;
}>({
  adminLoginPage: async ({ page }, use) => {
    await use(new AdminLoginPage(page));
  },
  companyManagementPage: async ({ page }, use) => {
    await use(new CompanyManagementPage(page));
  },
});
```

### 测试标准和规范

遵循项目测试标准：
- `docs/standards/testing-standards.md`
- `docs/standards/web-ui-testing-standards.md`

**关键测试原则：**
1. 测试独立性：每个测试用例独立运行
2. 数据清理：每个测试结束后清理自己创建的数据
3. 清晰断言：使用 expect() 明确断言预期结果
4. 等待策略：使用 Playwright 的 auto-waiting

### 前序 Story (11.1-11.3) 关键经验

从 Story 11.1-11.3 中学到的关键经验：

1. **Toast 检测不可靠：**
   - Toast 消息有时出现得很快
   - 已改用 API 响应验证作为主要验证方式

2. **表格列顺序：**
   - Company 表格列顺序：公司名称(0), 平台(1), 联系人(2), 联系电话(3), 状态(4), 创建时间(5), 操作(6)
   - 使用 `nth(0)` 检查公司名称列

3. **测试数据要求：**
   - 后端 Zod schema 要求 contactEmail 必须是有效邮箱（如果填写）
   - 空字符串会被拒绝，所以测试不填的字段应该设为 undefined

4. **页面刷新：**
   - 创建/删除数据后需要刷新页面或重新导航
   - 使用 `page.reload()` 或 `goto()` 刷新

5. **选择器优先级：**
   - 优先使用 data-testid（最稳定）
   - 其次使用 role + name（较稳定）
   - 避免使用文本选择器（可能变化）

### 已知问题和注意事项

1. **Platform 异步加载：**
   - PlatformSelector 选项是异步加载的
   - 需要等待选项加载完成再选择
   - 使用 `selectRadixOptionAsync` 可能更可靠

2. **公司名称唯一性：**
   - 同一平台下公司名称唯一
   - 测试使用时间戳确保唯一性
   - 如果不选择平台，公司名称全局唯一

3. **删除限制：**
   - 只有禁用状态的公司才能删除
   - 测试删除功能需要先禁用公司
   - 或者使用 API 直接删除

4. **状态徽章显示：**
   - 状态使用徽章显示：启用（绿色）、禁用（灰色）
   - 测试中可以通过颜色或文本验证状态

### 开发顺序建议

1. 创建文件和基础结构
2. 定义所有选择器（参考 CompanyManagement.tsx 的 data-testid）
3. 实现导航和基础验证方法
4. 实现对话框操作方法
5. 实现表单填写方法（包括 PlatformSelector 选择）
6. 实现表单提交方法（网络响应捕获）
7. 实现 CRUD 操作方法
8. 实现搜索和验证方法
9. TypeScript 类型检查
10. 在 test-setup.ts 中添加 fixture

### References

- [Epic 11 基础配置管理测试](../planning-artifacts/epics.md#epic-11-基础配置管理测试-epic-f)
- [Story 11.1 Platform Page Object](./11-1-platform-page-object.story.md)
- [Story 11.2 平台创建测试](./11-2-platform-create-test.story.md)
- [Story 11.3 平台列表测试](./11-3-platform-list-test.story.md)
- [Company Entity](../../allin-packages/company-module/src/entities/company.entity.ts)
- [Company Schema](../../allin-packages/company-module/src/schemas/company.schema.ts)
- [CompanyManagement UI](../../allin-packages/company-management-ui/src/components/CompanyManagement.tsx)
- [PlatformManagementPage](../../web/tests/e2e/pages/admin/platform-management.page.ts) - 参考模式
- [测试标准](../../docs/standards/testing-standards.md)
- [Web UI 测试标准](../../docs/standards/web-ui-testing-standards.md)
- [Architecture Decision - TypeScript + Playwright 陷阱](../planning-artifacts/architecture.md#typescript--playwright-常见陷阱)

### Project Structure Notes

**测试文件存放路径：**
```
web/tests/e2e/
├── pages/admin/
│   ├── platform-management.page.ts   # 平台管理 Page Object（已完成）
│   └── company-management.page.ts    # 公司管理 Page Object（当前）
├── specs/admin/
│   ├── platform-create.spec.ts       # 平台创建测试（已完成）
│   ├── platform-list.spec.ts         # 平台列表测试（已完成）
│   └── company-*.spec.ts             # 公司管理测试（后续 Story）
└── utils/
    └── test-setup.ts                  # 测试夹具配置（需要添加 companyManagementPage）
```

## Dev Agent Record

### Agent Model Used

Claude (d8d-model)

### Completion Notes List

**Story 创建完成：**
1. ✅ 分析 Story 11.4 需求：创建 CompanyManagementPage Page Object
2. ✅ 创建完整的 Story 文档
3. ✅ 包含所有验收标准和任务分解
4. ✅ 提供详细的 Dev Notes 指导开发者
5. ✅ 参考 Story 11.1-11.3 的关键经验和模式
6. ✅ 分析 CompanyManagement.tsx UI 组件结构
7. ✅ 提供完整的 Page Object 类设计参考
8. ✅ 列出所有 data-testid 选择器
9. ✅ 说明 PlatformSelector 集成方法

**Story 实施完成 (2026-01-12)：**
1. ✅ 创建 `web/tests/e2e/pages/admin/company-management.page.ts` 文件
2. ✅ 定义完整的类结构和选择器
3. ✅ 实现所有导航和基础验证方法
4. ✅ 实现所有对话框操作方法
5. ✅ 实现表单填写和提交方法（含网络响应捕获）
6. ✅ 实现 CRUD 操作方法（创建、编辑、删除）
7. ✅ 实现搜索和验证方法
8. ✅ 添加 companyManagementPage fixture 到 test-setup.ts
9. ✅ TypeScript 类型检查通过（无类型错误）

**代码审查完成 (2026-01-12)：**
1. ✅ 集成 `@d8d/e2e-test-utils` 的 `selectRadixOptionAsync` 实现 PlatformSelector 选择
2. ✅ 优化删除方法中的 token 查找策略（仅检查标准键名）
3. ✅ 清理过多的 console.debug 语句（保留关键错误日志）
4. ✅ 提取硬编码的 API URL 为类常量
5. ✅ 更新 `fillCompanyForm`、`createCompany`、`editCompany` 方法签名，添加 `platformName` 参数

**Story 文件位置：**
- `_bmad-output/implementation-artifacts/11-4-company-page-object.story.md`

**实现的关键特性：**
- 遵循 PlatformManagementPage 的设计模式
- 使用 data-testid 优先的选择器策略
- 支持 Toast 消息验证和 API 响应捕获
- API 直接删除策略（绕过 UI 不稳定性）
- 完整的 JSDoc 注释
- 导出 CompanyStatus 常量和类型
- 集成 @d8d/e2e-test-utils 实现 PlatformSelector 选择
- API URL 配置化为类常量

### File List

**新创建的文件（Story 创建）：**
- `_bmad-output/implementation-artifacts/11-4-company-page-object.story.md`

**新创建的文件（Story 实施）：**
- `web/tests/e2e/pages/admin/company-management.page.ts`

**修改的文件（Story 实施）：**
- `web/tests/e2e/utils/test-setup.ts` - 添加 companyManagementPage fixture

**已有的依赖文件：**
- `web/tests/e2e/pages/admin/platform-management.page.ts` - Story 11.1 创建（参考模式）
- `allin-packages/company-management-ui/src/components/CompanyManagement.tsx` - UI 组件参考
- `allin-packages/company-module/src/entities/company.entity.ts` - 实体参考
- `allin-packages/company-module/src/schemas/company.schema.ts` - Schema 参考
